net[darwin]: implement NetConnections().

pull/77/head
Shirou WAKAYAMA 10 years ago
parent d6ac361a24
commit c50db4f462

@ -4,6 +4,7 @@ package common
import (
"os/exec"
"strconv"
"strings"
"syscall"
"unsafe"
@ -58,3 +59,27 @@ func CallSyscall(mib []int32) ([]byte, uint64, error) {
return buf, length, nil
}
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/sbin/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
}

@ -3,8 +3,16 @@ package net
import (
"encoding/json"
"net"
"github.com/shirou/gopsutil/common"
)
var invoke common.Invoker
func init() {
invoke = common.Invoke{}
}
type NetIOCountersStat struct {
Name string `json:"name"` // interface name
BytesSent uint64 `json:"bytes_sent"` // number of bytes sent

@ -3,13 +3,23 @@
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 <Link#1> 869107 0 169411755 869107 0 169411755 0 0
@ -90,3 +100,129 @@ func NetIOCounters(pernic bool) ([]NetIOCountersStat, error) {
return ret, nil
}
// Return a list of network connections opened by a process
func NetConnections(kind string) ([]NetConnectionStat, error) {
var ret []NetConnectionStat
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
}
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
}

@ -81,3 +81,9 @@ func NetIOCounters(pernic bool) ([]NetIOCountersStat, error) {
return ret, nil
}
func NetConnections(kind string) ([]NetConnectionStat, error) {
var ret []NetConnectionStat
return ret, common.NotImplementedError
}

@ -89,3 +89,9 @@ func NetIOCounters(pernic bool) ([]NetIOCountersStat, error) {
return ret, nil
}
func NetConnections(kind string) ([]NetConnectionStat, error) {
var ret []NetConnectionStat
return ret, common.NotImplementedError
}

@ -120,3 +120,19 @@ func TestNetInterfaces(t *testing.T) {
}
}
}
func TestNetConnections(t *testing.T) {
v, err := NetConnections("inet")
if err != nil {
t.Errorf("could not get NetConnections: %v", err)
}
if len(v) == 0 {
t.Errorf("could not get NetConnections: %v", v)
}
for _, vv := range v {
if vv.Family == 0 {
t.Errorf("invalid NetConnections: %v", vv)
}
}
}

@ -93,19 +93,23 @@ func (p *Process) Cwd() (string, error) {
return "", common.NotImplementedError
}
func (p *Process) Parent() (*Process, error) {
r, err := callLsof("R", p.Pid)
rr, err := common.CallLsof(invoke, p.Pid, "-FR")
if err != nil {
return nil, err
}
if len(r) != 1 { // TODO: pid 1
return nil, fmt.Errorf("wrong number of parents")
for _, r := range rr {
if strings.HasPrefix(r, "p") { // skip if process
continue
}
v, err := strconv.Atoi(r[0])
l := string(r)
v, err := strconv.Atoi(strings.Replace(l, "R", "", 1))
if err != nil {
return nil, err
}
return NewProcess(int32(v))
}
return nil, fmt.Errorf("could not find parent line")
}
func (p *Process) Status() (string, error) {
r, err := callPs("state", p.Pid, false)
if err != nil {
@ -415,26 +419,3 @@ func callPs(arg string, pid int32, threadOption bool) ([][]string, error) {
return ret, nil
}
func callLsof(arg string, pid int32) ([]string, error) {
var cmd []string
if pid == 0 { // will get from all processes.
cmd = []string{"-F" + arg}
} else {
cmd = []string{"-a", "-F" + arg, "-p", strconv.Itoa(int(pid))}
}
out, err := invoke.Command("/usr/sbin/lsof", cmd...)
if err != nil {
return []string{}, err
}
lines := strings.Split(string(out), "\n")
var ret []string
for _, l := range lines[1:] {
if strings.HasPrefix(l, arg) {
ret = append(ret, l[1:]) // delete first char
}
}
return ret, nil
}

@ -280,12 +280,12 @@ func Test_Parent(t *testing.T) {
c, err := p.Parent()
if err != nil {
t.Errorf("error %v", err)
t.Fatalf("error %v", err)
}
if c == nil {
t.Errorf("could not get parent")
t.Fatalf("could not get parent")
}
if c.Pid == 0 {
t.Errorf("wrong parent pid")
t.Fatalf("wrong parent pid")
}
}

Loading…
Cancel
Save