Commit 6c352016 authored by Shirou WAKAYAMA's avatar Shirou WAKAYAMA

[net]linux: start replacing lsof. still work in progress.

parent 3618a777
......@@ -206,6 +206,16 @@ func StringsContains(target []string, src string) bool {
return false
}
// IntContains checks the src in any int of the target int slice.
func IntContains(target []int, src int) bool {
for _, t := range target {
if src == t {
return true
}
}
return false
}
// get struct attributes.
// This method is used only for debugging platform dependent code.
func attributes(m interface{}) map[string]reflect.Type {
......
......@@ -3,9 +3,15 @@
package net
import (
"encoding/hex"
"errors"
"fmt"
"io/ioutil"
"net"
"os"
"strconv"
"strings"
"syscall"
"github.com/shirou/gopsutil/internal/common"
)
......@@ -192,3 +198,291 @@ func NetFilterCounters() ([]NetFilterStat, error) {
stats = append(stats, payload)
return stats, nil
}
type netConnectionKindType struct {
family int
sockType int
}
var KindTCP4 = netConnectionKindType{
family: syscall.AF_INET,
sockType: syscall.SOCK_STREAM,
}
var KindTCP6 = netConnectionKindType{
family: syscall.AF_INET6,
sockType: syscall.SOCK_STREAM,
}
var KindUDP4 = netConnectionKindType{
family: syscall.AF_INET,
sockType: syscall.SOCK_DGRAM,
}
var KindUDP6 = netConnectionKindType{
family: syscall.AF_INET6,
sockType: syscall.SOCK_DGRAM,
}
var KindUNIX = netConnectionKindType{
family: syscall.AF_UNIX,
}
var netConnectionKindMap = map[string][]netConnectionKindType{
"all": []netConnectionKindType{KindTCP4, KindTCP6, KindUDP4, KindUDP6, KindUNIX},
"tcp": []netConnectionKindType{KindTCP4, KindTCP6},
"tcp4": []netConnectionKindType{KindTCP4},
"tcp6": []netConnectionKindType{KindTCP6},
"udp": []netConnectionKindType{KindUDP4, KindUDP6},
"udp4": []netConnectionKindType{KindUDP4},
"udp6": []netConnectionKindType{KindUDP6},
"unix": []netConnectionKindType{KindUNIX},
"inet": []netConnectionKindType{KindTCP4, KindTCP6, KindUDP4, KindUDP6},
"inet4": []netConnectionKindType{KindTCP4, KindUDP4},
"inet6": []netConnectionKindType{KindTCP6, KindUDP6},
}
type inodeMap struct {
pid int32
fd string
}
type bb struct {
fd int
family int
sockType int
laddr string
raddr string
status string
bound_pid int32
}
// Return a list of network connections opened.
func NetConnections(kind string) ([]NetConnectionStat, error) {
return NetConnectionsPid(kind, 0)
}
// Return a list of network connections opened by a process.
func NetConnectionsPid(kind string, pid int32) ([]NetConnectionStat, error) {
tmap, ok := netConnectionKindMap[kind]
if !ok {
return nil, fmt.Errorf("invalid kind, %s", kind)
}
root := common.HostProc()
var err error
var inodes map[string][]inodeMap
if pid == 0 {
inodes, err = getProcInodesAll(root)
} else {
inodes, err = getProcInodes(root, pid)
}
if err != nil {
return nil, fmt.Errorf("cound not get pid(s), %d", pid)
}
for _, t := range tmap {
fmt.Println(t)
fmt.Println(inodes)
var path string
var ls []string
switch t.family {
case syscall.AF_INET:
path = fmt.Sprintf("%d/net/%s", pid, "tcp")
ls, err = processInet(path, t, inodes, pid)
case syscall.AF_INET6:
path = fmt.Sprintf("%d/net/%s", pid, "tcp6")
ls, err = processInet(path, t, inodes, pid)
case syscall.AF_UNIX:
path = fmt.Sprintf("%d/net/%s", pid, "unix")
ls, err = processInet(path, t, inodes, pid)
}
if err != nil {
return nil, err
}
for _, sss := range ls {
fmt.Println(sss)
}
}
return []NetConnectionStat{}, nil
}
// getProcInodes returnes fd of the pid.
func getProcInodes(root string, pid int32) (map[string][]inodeMap, error) {
ret := make(map[string][]inodeMap)
dir := fmt.Sprintf("%s/%d/fd", root, pid)
files, err := ioutil.ReadDir(dir)
if err != nil {
return ret, nil
}
for _, fd := range files {
inodePath := fmt.Sprintf("%s/%d/fd/%s", root, pid, fd.Name())
inode, err := os.Readlink(inodePath)
if err != nil {
continue
}
fmt.Println(inodePath)
if strings.HasPrefix(inode, "socket:[") {
// the process is using a socket
l := len(inode)
inode = inode[8 : l-1]
}
_, ok := ret[inode]
if !ok {
ret[inode] = make([]inodeMap, 0)
}
i := inodeMap{
pid: pid,
fd: fd.Name(),
}
ret[inode] = append(ret[inode], i)
}
return ret, nil
}
// Pids retunres all pids.
// Note: this is a copy of process_linux.Pids()
// FIXME: Import process occures import cycle.
// move to common made other platform breaking. Need consider.
func Pids() ([]int32, error) {
var ret []int32
d, err := os.Open(common.HostProc())
if err != nil {
return nil, err
}
defer d.Close()
fnames, err := d.Readdirnames(-1)
if err != nil {
return nil, err
}
for _, fname := range fnames {
pid, err := strconv.ParseInt(fname, 10, 32)
if err != nil {
// if not numeric name, just skip
continue
}
ret = append(ret, int32(pid))
}
return ret, nil
}
func getProcInodesAll(root string) (map[string][]inodeMap, error) {
pids, err := Pids()
if err != nil {
return nil, err
}
ret := make(map[string][]inodeMap)
for _, pid := range pids {
t, err := getProcInodes(root, pid)
if err != nil {
return ret, err
}
if len(t) == 0 {
continue
}
// TODO: update ret.
fmt.Println(t)
}
return ret, nil
}
// decodeAddress decode addresse represents addr in proc/net/*
// ex:
// "0500000A:0016" -> "10.0.0.5", 22
// "0085002452100113070057A13F025401:0035" -> "2400:8500:1301:1052:a157:7:154:23f", 53
func decodeAddress(family int, src string) (net.IP, int, error) {
t := strings.Split(src, ":")
if len(t) != 2 {
return nil, 0, fmt.Errorf("does not contain port, %s", src)
}
addr := t[0]
port, err := strconv.ParseInt("0x"+t[1], 0, 64)
if err != nil {
return nil, 0, fmt.Errorf("invalid port, %s", src)
}
decoded, err := hex.DecodeString(addr)
if err != nil {
return nil, 0, fmt.Errorf("decode error:", err)
}
var ip net.IP
// Assumes this is little_endian
if family == syscall.AF_INET {
ip = net.IP(Reverse(decoded))
} else { // IPv6
ip, err = parseIPv6HexString(decoded)
if err != nil {
return nil, 0, err
}
}
return ip, int(port), nil
}
// Reverse reverses array of bytes.
func Reverse(s []byte) []byte {
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
return s
}
// parseIPv6HexString parse array of bytes to IPv6 string
func parseIPv6HexString(src []byte) (net.IP, error) {
if len(src) != 16 {
return nil, fmt.Errorf("invalid IPv6 string")
}
buf := make([]byte, 0, 16)
for i := 0; i < len(src); i += 4 {
r := Reverse(src[i : i+4])
buf = append(buf, r...)
}
return net.IP(buf), nil
}
func processInet(file string, kind netConnectionKindType, inodes map[string][]inodeMap, filterPid int32) ([]string, error) {
if strings.HasSuffix(file, "6") && !common.PathExists(file) {
// IPv6 not supported, return empty.
return []string{}, nil
}
lines, err := common.ReadLines(file)
if err != nil {
return nil, err
}
// skip first line
for _, line := range lines[1:] {
l := strings.Fields(line)
if len(l) < 10 {
continue
}
laddr := l[1]
raddr := l[2]
status := l[3]
inode, err := strconv.Atoi(l[9])
if err != nil {
continue
}
fmt.Println(laddr)
fmt.Println(raddr)
fmt.Println(status)
fmt.Println(inode)
}
return nil, nil
}
func processUnix(file string, kind netConnectionKindType, inodes map[string][]inodeMap, filterPid int32) ([]string, error) {
return nil, nil
}
package net
import (
"fmt"
"syscall"
"testing"
"github.com/shirou/gopsutil/internal/common"
"github.com/stretchr/testify/assert"
)
func TestGetProcInodes(t *testing.T) {
root := common.HostProc("")
// /proc/19957/fd
v, err := getProcInodes(root, 19957)
if err != nil {
t.Fatal(err)
}
fmt.Println(v)
}
type AddrTest struct {
IP string
Port int
Error bool
}
func TestDecodeAddress(t *testing.T) {
assert := assert.New(t)
addr := map[string]AddrTest{
"0500000A:0016": AddrTest{
IP: "10.0.0.5",
Port: 22,
},
"0100007F:D1C2": AddrTest{
IP: "127.0.0.1",
Port: 53698,
},
"11111:0035": AddrTest{
Error: true,
},
"0100007F:BLAH": AddrTest{
Error: true,
},
"0085002452100113070057A13F025401:0035": AddrTest{
IP: "2400:8500:1301:1052:a157:7:154:23f",
Port: 53,
},
"00855210011307F025401:0035": AddrTest{
Error: true,
},
}
for src, dst := range addr {
family := syscall.AF_INET
if len(src) > 13 {
family = syscall.AF_INET6
}
ip, port, err := decodeAddress(family, src)
if dst.Error {
assert.NotNil(err, src)
} else {
assert.Nil(err, src)
assert.Equal(dst.IP, ip.String(), src)
assert.Equal(dst.Port, port, src)
}
}
}
// +build linux freebsd darwin
// +build freebsd darwin
package net
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment