diff --git a/common/common_linux.go b/common/common_linux.go new file mode 100644 index 0000000..bec024f --- /dev/null +++ b/common/common_linux.go @@ -0,0 +1,32 @@ +// +build linux + +package common + +import ( + "strconv" + "strings" +) + +func CallLsof(invoke Invoker, pid int32, args ...string) ([]string, error) { + var cmd []string + if pid == 0 { // will get from all processes. + cmd = []string{"-a", "-n", "-P"} + } else { + cmd = []string{"-a", "-n", "-P", "-p", strconv.Itoa(int(pid))} + } + cmd = append(cmd, args...) + out, err := invoke.Command("/usr/bin/lsof", cmd...) + if err != nil { + return []string{}, err + } + lines := strings.Split(string(out), "\n") + + var ret []string + for _, l := range lines[1:] { + if len(l) == 0 { + continue + } + ret = append(ret, l) + } + return ret, nil +} diff --git a/net/net.go b/net/net.go index 339c8c3..506fe57 100644 --- a/net/net.go +++ b/net/net.go @@ -2,7 +2,11 @@ package net import ( "encoding/json" + "fmt" "net" + "strconv" + "strings" + "syscall" "github.com/shirou/gopsutil/common" ) @@ -54,6 +58,13 @@ type NetInterfaceStat struct { Addrs []NetInterfaceAddr `json:"addrs"` } +var constMap = map[string]int{ + "TCP": syscall.SOCK_STREAM, + "UDP": syscall.SOCK_DGRAM, + "IPv4": syscall.AF_INET, + "IPv6": syscall.AF_INET6, +} + func (n NetIOCountersStat) String() string { s, _ := json.Marshal(n) return string(s) @@ -143,3 +154,74 @@ func getNetIOCountersAll(n []NetIOCountersStat) ([]NetIOCountersStat, error) { return []NetIOCountersStat{r}, nil } + +func parseNetLine(line string) (NetConnectionStat, error) { + f := strings.Fields(line) + if len(f) < 9 { + return NetConnectionStat{}, fmt.Errorf("wrong line,%s", line) + } + + pid, err := strconv.Atoi(f[1]) + if err != nil { + return NetConnectionStat{}, err + } + fd, err := strconv.Atoi(strings.Trim(f[3], "u")) + if err != nil { + return NetConnectionStat{}, fmt.Errorf("unknown fd, %s", f[3]) + } + netFamily, ok := constMap[f[4]] + if !ok { + return NetConnectionStat{}, fmt.Errorf("unknown family, %s", f[4]) + } + netType, ok := constMap[f[7]] + if !ok { + return NetConnectionStat{}, fmt.Errorf("unknown type, %s", f[7]) + } + + laddr, raddr, err := parseNetAddr(f[8]) + if err != nil { + return NetConnectionStat{}, fmt.Errorf("failed to parse netaddr, %s", f[8]) + } + + n := NetConnectionStat{ + Fd: uint32(fd), + Family: uint32(netFamily), + Type: uint32(netType), + Laddr: laddr, + Raddr: raddr, + Pid: int32(pid), + } + if len(f) == 10 { + n.Status = strings.Trim(f[9], "()") + } + + return n, nil +} + +func parseNetAddr(line string) (laddr Addr, raddr Addr, err error) { + parse := func(l string) (Addr, error) { + host, port, err := net.SplitHostPort(l) + if err != nil { + return Addr{}, fmt.Errorf("wrong addr, %s", l) + } + lport, err := strconv.Atoi(port) + if err != nil { + return Addr{}, err + } + return Addr{IP: host, Port: uint32(lport)}, nil + } + + addrs := strings.Split(line, "->") + if len(addrs) == 0 { + return laddr, raddr, fmt.Errorf("wrong netaddr, %s", line) + } + laddr, err = parse(addrs[0]) + if len(addrs) == 2 { // remote addr exists + raddr, err = parse(addrs[1]) + if err != nil { + return laddr, raddr, err + } + } + + return laddr, raddr, err +} diff --git a/net/net_darwin.go b/net/net_darwin.go index 618d76e..da722dc 100644 --- a/net/net_darwin.go +++ b/net/net_darwin.go @@ -3,23 +3,13 @@ package net import ( - "fmt" - "net" "os/exec" "strconv" "strings" - "syscall" "github.com/shirou/gopsutil/common" ) -var constMap = map[string]int{ - "TCP": syscall.SOCK_STREAM, - "UDP": syscall.SOCK_DGRAM, - "IPv4": syscall.AF_INET, - "IPv6": syscall.AF_INET6, -} - // example of netstat -idbn output on yosemite // Name Mtu Network Address Ipkts Ierrs Ibytes Opkts Oerrs Obytes Coll Drop // lo0 16384 869107 0 169411755 869107 0 169411755 0 0 @@ -155,74 +145,3 @@ func NetConnections(kind string) ([]NetConnectionStat, error) { return ret, nil } - -func parseNetLine(line string) (NetConnectionStat, error) { - f := strings.Fields(line) - if len(f) < 9 { - return NetConnectionStat{}, fmt.Errorf("wrong line,%s", line) - } - - pid, err := strconv.Atoi(f[1]) - if err != nil { - return NetConnectionStat{}, err - } - fd, err := strconv.Atoi(strings.Trim(f[3], "u")) - if err != nil { - return NetConnectionStat{}, fmt.Errorf("unknown fd, %s", f[3]) - } - netFamily, ok := constMap[f[4]] - if !ok { - return NetConnectionStat{}, fmt.Errorf("unknown family, %s", f[4]) - } - netType, ok := constMap[f[7]] - if !ok { - return NetConnectionStat{}, fmt.Errorf("unknown type, %s", f[7]) - } - - laddr, raddr, err := parseNetAddr(f[8]) - if err != nil { - return NetConnectionStat{}, fmt.Errorf("failed to parse netaddr, %s", f[8]) - } - - n := NetConnectionStat{ - Fd: uint32(fd), - Family: uint32(netFamily), - Type: uint32(netType), - Laddr: laddr, - Raddr: raddr, - Pid: int32(pid), - } - if len(f) == 10 { - n.Status = strings.Trim(f[9], "()") - } - - return n, nil -} - -func parseNetAddr(line string) (laddr Addr, raddr Addr, err error) { - parse := func(l string) (Addr, error) { - host, port, err := net.SplitHostPort(l) - if err != nil { - return Addr{}, fmt.Errorf("wrong addr, %s", l) - } - lport, err := strconv.Atoi(port) - if err != nil { - return Addr{}, err - } - return Addr{IP: host, Port: uint32(lport)}, nil - } - - addrs := strings.Split(line, "->") - if len(addrs) == 0 { - return laddr, raddr, fmt.Errorf("wrong netaddr, %s", line) - } - laddr, err = parse(addrs[0]) - if len(addrs) == 2 { // remote addr exists - raddr, err = parse(addrs[1]) - if err != nil { - return laddr, raddr, err - } - } - - return laddr, raddr, err -} diff --git a/net/net_linux.go b/net/net_linux.go index 82e25a0..de0d65a 100644 --- a/net/net_linux.go +++ b/net/net_linux.go @@ -93,5 +93,53 @@ func NetIOCounters(pernic bool) ([]NetIOCountersStat, error) { func NetConnections(kind string) ([]NetConnectionStat, error) { var ret []NetConnectionStat - return ret, common.NotImplementedError + args := []string{"-i"} + switch strings.ToLower(kind) { + default: + fallthrough + case "": + fallthrough + case "all": + fallthrough + case "inet": + args = append(args, "tcp") + case "inet4": + args = append(args, "4") + case "inet6": + args = append(args, "6") + case "tcp": + args = append(args, "tcp") + case "tcp4": + args = append(args, "4tcp") + case "tcp6": + args = append(args, "6tcp") + case "udp": + args = append(args, "udp") + case "udp4": + args = append(args, "6udp") + case "udp6": + args = append(args, "6udp") + case "unix": + return ret, common.NotImplementedError + } + + // we can not use -F filter to get all of required information at once. + r, err := common.CallLsof(invoke, 0, args...) + if err != nil { + return nil, err + } + for _, rr := range r { + if strings.HasPrefix(rr, "COMMAND") { + continue + } + n, err := parseNetLine(rr) + if err != nil { + // fmt.Println(err) // TODO: should debug print? + continue + } + + ret = append(ret, n) + } + + return ret, nil }