From 08a382b20717f7047923e1dbb4633831e6d0ec36 Mon Sep 17 00:00:00 2001 From: tycho garen Date: Sun, 5 Nov 2017 20:40:33 -0500 Subject: [PATCH 01/23] make process.processes function public --- process/process_darwin.go | 13 +++---------- process/process_freebsd.go | 8 ++++---- process/process_linux.go | 21 +++++++++++++++++++++ process/process_openbsd.go | 8 ++++---- process/process_windows.go | 14 +++++++------- 5 files changed, 39 insertions(+), 25 deletions(-) diff --git a/process/process_darwin.go b/process/process_darwin.go index aa17d1d8..1e18f1d6 100644 --- a/process/process_darwin.go +++ b/process/process_darwin.go @@ -390,8 +390,8 @@ func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) { return &ret, common.ErrNotImplementedError } -func processes() ([]Process, error) { - results := make([]Process, 0, 50) +func Processes() ([]*Process, error) { + results := make([]*Process) mib := []int32{CTLKern, KernProc, KernProcAll, 0} buf, length, err := common.CallSyscall(mib) @@ -403,13 +403,6 @@ func processes() ([]Process, error) { k := KinfoProc{} procinfoLen := int(unsafe.Sizeof(k)) count := int(length / uint64(procinfoLen)) - /* - fmt.Println(length, procinfoLen, count) - b := buf[0*procinfoLen : 0*procinfoLen+procinfoLen] - fmt.Println(b) - kk, err := parseKinfoProc(b) - fmt.Printf("%#v", kk) - */ // parse buf to procs for i := 0; i < count; i++ { @@ -422,7 +415,7 @@ func processes() ([]Process, error) { if err != nil { continue } - results = append(results, *p) + results = append(results, p) } return results, nil diff --git a/process/process_freebsd.go b/process/process_freebsd.go index aa3e85c2..c8cb1b9d 100644 --- a/process/process_freebsd.go +++ b/process/process_freebsd.go @@ -22,7 +22,7 @@ type MemoryMapsStat struct { func Pids() ([]int32, error) { var ret []int32 - procs, err := processes() + procs, err := Processes() if err != nil { return ret, nil } @@ -278,8 +278,8 @@ func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) { return &ret, common.ErrNotImplementedError } -func processes() ([]Process, error) { - results := make([]Process, 0, 50) +func Processes() ([]*Process, error) { + results := make([]*Process) mib := []int32{CTLKern, KernProc, KernProcProc, 0} buf, length, err := common.CallSyscall(mib) @@ -302,7 +302,7 @@ func processes() ([]Process, error) { continue } - results = append(results, *p) + results = append(results, p) } return results, nil diff --git a/process/process_linux.go b/process/process_linux.go index 1839eb23..9db19972 100644 --- a/process/process_linux.go +++ b/process/process_linux.go @@ -1017,6 +1017,27 @@ func Pids() ([]int32, error) { return readPidsFromDir(common.HostProc()) } +// Process returns a slice of pointers to Process structs for all +// currently running processes. +func Processes() ([]*Process, error) { + out := []*Process{} + + pids, err := Pids() + if err != nil { + return out, err + } + + for _, pid := range pids { + p, err := NewProcess(pid) + if err != nil { + continue + } + out = append(out, p) + } + + return out, nil +} + func readPidsFromDir(path string) ([]int32, error) { var ret []int32 diff --git a/process/process_openbsd.go b/process/process_openbsd.go index fb553a63..6bff0b38 100644 --- a/process/process_openbsd.go +++ b/process/process_openbsd.go @@ -25,7 +25,7 @@ type MemoryMapsStat struct { func Pids() ([]int32, error) { var ret []int32 - procs, err := processes() + procs, err := Processes() if err != nil { return ret, nil } @@ -268,8 +268,8 @@ func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) { return &ret, common.ErrNotImplementedError } -func processes() ([]Process, error) { - results := make([]Process, 0, 50) +func Processes() ([]*Process, error) { + results := make([]*Process) buf, length, err := CallKernProcSyscall(KernProcAll, 0) @@ -292,7 +292,7 @@ func processes() ([]Process, error) { continue } - results = append(results, *p) + results = append(results, p) } return results, nil diff --git a/process/process_windows.go b/process/process_windows.go index c1cf4cb8..02478a36 100644 --- a/process/process_windows.go +++ b/process/process_windows.go @@ -95,7 +95,7 @@ func init() { func Pids() ([]int32, error) { var ret []int32 - procs, err := processes() + procs, err := Processes() if err != nil { return ret, nil } @@ -292,11 +292,11 @@ func (p *Process) Times() (*cpu.TimesStat, error) { // below from psutil's _psutil_windows.c, and in turn from Python's // Modules/posixmodule.c - user := float64(sysTimes.UserTime.HighDateTime) * 429.4967296 + float64(sysTimes.UserTime.LowDateTime) * 1e-7 - kernel := float64(sysTimes.KernelTime.HighDateTime) * 429.4967296 + float64(sysTimes.KernelTime.LowDateTime) * 1e-7 + user := float64(sysTimes.UserTime.HighDateTime)*429.4967296 + float64(sysTimes.UserTime.LowDateTime)*1e-7 + kernel := float64(sysTimes.KernelTime.HighDateTime)*429.4967296 + float64(sysTimes.KernelTime.LowDateTime)*1e-7 return &cpu.TimesStat{ - User: user, + User: user, System: kernel, }, nil } @@ -422,7 +422,7 @@ func (p *Process) getFromSnapProcess(pid int32) (int32, int32, string, error) { } // Get processes -func processes() ([]*Process, error) { +func Processes() ([]*Process, error) { var dst []Win32_Process q := wmi.CreateQuery(&dst, "") err := wmi.Query(q, &dst) @@ -508,9 +508,9 @@ func getProcessMemoryInfo(h windows.Handle, mem *PROCESS_MEMORY_COUNTERS) (err e type SYSTEM_TIMES struct { CreateTime syscall.Filetime - ExitTime syscall.Filetime + ExitTime syscall.Filetime KernelTime syscall.Filetime - UserTime syscall.Filetime + UserTime syscall.Filetime } func getProcessCPUTimes(pid int32) (SYSTEM_TIMES, error) { From befc2c3d92445c733f2b78e326b55b436e4ce7f6 Mon Sep 17 00:00:00 2001 From: shawnps Date: Wed, 8 Nov 2017 13:29:49 +0900 Subject: [PATCH 02/23] call Fatal if length of Users() is 0 --- host/host_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/host/host_test.go b/host/host_test.go index 65f7a751..c420580b 100644 --- a/host/host_test.go +++ b/host/host_test.go @@ -54,7 +54,7 @@ func TestUsers(t *testing.T) { } empty := UserStat{} if len(v) == 0 { - t.Errorf("Users is empty") + t.Fatal("Users is empty") } for _, u := range v { if u == empty { From d62768abc96623b8fd47173205abe129adfe8dc5 Mon Sep 17 00:00:00 2001 From: Marco Pfatschbacher Date: Wed, 8 Nov 2017 20:52:36 +0100 Subject: [PATCH 03/23] Fix IOCounters for OpenBSD Use SysctlRaw instead of Sysctl. The latter assumes NUL terminated strings which returns the lenght off by one. Therefore, only n-1 disks where reported. --- disk/disk_openbsd.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/disk/disk_openbsd.go b/disk/disk_openbsd.go index d1705eaf..348a1f16 100644 --- a/disk/disk_openbsd.go +++ b/disk/disk_openbsd.go @@ -66,7 +66,7 @@ func Partitions(all bool) ([]PartitionStat, error) { func IOCounters(names ...string) (map[string]IOCountersStat, error) { ret := make(map[string]IOCountersStat) - r, err := unix.Sysctl("hw.diskstats") + r, err := unix.SysctlRaw("hw.diskstats") if err != nil { return nil, err } From 6e7aca41d7edca530703196067fea4216d65a563 Mon Sep 17 00:00:00 2001 From: Marco Pfatschbacher Date: Wed, 8 Nov 2017 21:21:11 +0100 Subject: [PATCH 04/23] Implement Connection support for OpenBSD This retrieves open TCP/UDP connections by using netstat(1) File descriptors and pids are not supported. --- net/net_openbsd.go | 143 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 139 insertions(+), 4 deletions(-) diff --git a/net/net_openbsd.go b/net/net_openbsd.go index 85cc70c4..6437e7da 100644 --- a/net/net_openbsd.go +++ b/net/net_openbsd.go @@ -4,13 +4,18 @@ package net import ( "errors" + "fmt" "os/exec" + "regexp" "strconv" "strings" + "syscall" "github.com/shirou/gopsutil/internal/common" ) +var portMatch = regexp.MustCompile(`(.*)\.(\d+)$`) + func ParseNetstat(output string, mode string, iocs map[string]IOCountersStat) error { lines := strings.Split(output, "\n") @@ -146,8 +151,138 @@ func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) { return nil, errors.New("NetProtoCounters not implemented for openbsd") } -// Return a list of network connections opened. -// Not Implemented for OpenBSD -func Connections(kind string) ([]ConnectionStat, error) { - return nil, errors.New("Connections not implemented for openbsd") +func parseNetstatLine(line string) (ConnectionStat, error) { + f := strings.Fields(line) + if len(f) < 5 { + return ConnectionStat{}, fmt.Errorf("wrong line,%s", line) + } + + var netType, netFamily uint32 + switch f[0] { + case "tcp": + netType = syscall.SOCK_STREAM + netFamily = syscall.AF_INET + case "udp": + netType = syscall.SOCK_DGRAM + netFamily = syscall.AF_INET + case "tcp6": + netType = syscall.SOCK_STREAM + netFamily = syscall.AF_INET6 + case "udp6": + netType = syscall.SOCK_DGRAM + netFamily = syscall.AF_INET6 + default: + return ConnectionStat{}, fmt.Errorf("unknown type, %s", f[0]) + } + + laddr, raddr, err := parseNetstatAddr(f[3], f[4], netFamily) + if err != nil { + return ConnectionStat{}, fmt.Errorf("failed to parse netaddr, %s %s", f[3], f[4]) + } + + n := ConnectionStat{ + Fd: uint32(0), // not supported + Family: uint32(netFamily), + Type: uint32(netType), + Laddr: laddr, + Raddr: raddr, + Pid: int32(0), // not supported + } + if len(f) == 6 { + n.Status = f[5] + } + + return n, nil +} + +func parseNetstatAddr(local string, remote string, family uint32) (laddr Addr, raddr Addr, err error) { + parse := func(l string) (Addr, error) { + matches := portMatch.FindStringSubmatch(l) + if matches == nil { + return Addr{}, fmt.Errorf("wrong addr, %s", l) + } + host := matches[1] + port := matches[2] + if host == "*" { + if family == syscall.AF_INET { + host = "0.0.0.0" + } else { + host = "::" + } + } + lport, err := strconv.Atoi(port) + if err != nil { + return Addr{}, err + } + return Addr{IP: host, Port: uint32(lport)}, nil + } + + laddr, err = parse(local) + if remote != "*.*" { // remote addr exists + raddr, err = parse(remote) + if err != nil { + return laddr, raddr, err + } + } + + return laddr, raddr, err +} + +// Return a list of network connections opened. +func Connections(kind string) ([]ConnectionStat, error) { + var ret []ConnectionStat + + args := []string{"-na"} + switch strings.ToLower(kind) { + default: + fallthrough + case "": + fallthrough + case "all": + fallthrough + case "inet": + + case "inet4": + args = append(args, "-finet") + case "inet6": + args = append(args, "-finet6") + case "tcp": + args = append(args, "-ptcp") + case "tcp4": + args = append(args, "-ptcp", "-finet") + case "tcp6": + args = append(args, "-ptcp", "-finet6") + case "udp": + args = append(args, "-pudp") + case "udp4": + args = append(args, "-pudp", "-finet") + case "udp6": + args = append(args, "-pudp", "-finet6") + case "unix": + return ret, common.ErrNotImplementedError + } + + netstat, err := exec.LookPath("/usr/bin/netstat") + if err != nil { + return nil, err + } + out, err := invoke.Command(netstat, args...) + + if err != nil { + return nil, err + } + lines := strings.Split(string(out), "\n") + for _, line := range lines { + if !(strings.HasPrefix(line, "tcp") || strings.HasPrefix(line, "udp")) { + continue + } + n, err := parseNetstatLine(line) + if err != nil { + continue + } + + ret = append(ret, n) + } + + return ret, nil } From 1ca44eace99a80e3293b014c9731c6e4ac7d49cc Mon Sep 17 00:00:00 2001 From: Marco Pfatschbacher Date: Wed, 8 Nov 2017 21:38:07 +0100 Subject: [PATCH 05/23] fix typo --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 7dc29092..139c998f 100644 --- a/README.rst +++ b/README.rst @@ -43,7 +43,7 @@ Available Architectures - Linux i386/amd64/arm(raspberry pi) - Windows/amd64 - Darwin i386/amd64 -- OpenBDS amd64 (Thank you @mpfz0r!) +- OpenBSD amd64 (Thank you @mpfz0r!) - Solaris amd64 (developed and tested on SmartOS/Illumos, Thank you @jen20!) All works are implemented without cgo by porting c struct to golang struct. From 5476f100bc04bc4dffb16c07d79cfa7480f6181f Mon Sep 17 00:00:00 2001 From: Marco Pfatschbacher Date: Wed, 8 Nov 2017 21:41:57 +0100 Subject: [PATCH 06/23] mark net_connections as supported for OpenBSD --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 139c998f..9a707ba5 100644 --- a/README.rst +++ b/README.rst @@ -187,7 +187,7 @@ boot_time x x x x x users x x x x x pids x x x x x pid_exists x x x x x -net_connections x x +net_connections x x x net_protocols x net_if_addrs net_if_stats From df6462b50e5ba633602996ce3530ba8b98e70432 Mon Sep 17 00:00:00 2001 From: Marco Pfatschbacher Date: Thu, 9 Nov 2017 13:14:12 +0100 Subject: [PATCH 07/23] Fix PR comments made by shirou --- net/net_openbsd.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/net/net_openbsd.go b/net/net_openbsd.go index 6437e7da..63caff53 100644 --- a/net/net_openbsd.go +++ b/net/net_openbsd.go @@ -97,7 +97,7 @@ func ParseNetstat(output string, mode string, } func IOCounters(pernic bool) ([]IOCountersStat, error) { - netstat, err := exec.LookPath("/usr/bin/netstat") + netstat, err := exec.LookPath("netstat") if err != nil { return nil, err } @@ -204,10 +204,13 @@ func parseNetstatAddr(local string, remote string, family uint32) (laddr Addr, r host := matches[1] port := matches[2] if host == "*" { - if family == syscall.AF_INET { + switch family { + case syscall.AF_INET: host = "0.0.0.0" - } else { + case syscall.AF_INET6: host = "::" + default: + return Addr{}, fmt.Errorf("unknown family, %d", family) } } lport, err := strconv.Atoi(port) @@ -241,7 +244,7 @@ func Connections(kind string) ([]ConnectionStat, error) { case "all": fallthrough case "inet": - + // nothing to add case "inet4": args = append(args, "-finet") case "inet6": @@ -262,7 +265,7 @@ func Connections(kind string) ([]ConnectionStat, error) { return ret, common.ErrNotImplementedError } - netstat, err := exec.LookPath("/usr/bin/netstat") + netstat, err := exec.LookPath("netstat") if err != nil { return nil, err } From 4d92a03da8b33c171cb3791555b5587f891d3a3d Mon Sep 17 00:00:00 2001 From: Sam Kleinman Date: Thu, 9 Nov 2017 10:31:12 -0500 Subject: [PATCH 08/23] fix cross compiles --- process/process_darwin.go | 2 +- process/process_freebsd.go | 2 +- process/process_openbsd.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/process/process_darwin.go b/process/process_darwin.go index 1e18f1d6..cc289d24 100644 --- a/process/process_darwin.go +++ b/process/process_darwin.go @@ -391,7 +391,7 @@ func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) { } func Processes() ([]*Process, error) { - results := make([]*Process) + results := []*Process{} mib := []int32{CTLKern, KernProc, KernProcAll, 0} buf, length, err := common.CallSyscall(mib) diff --git a/process/process_freebsd.go b/process/process_freebsd.go index c8cb1b9d..9d757176 100644 --- a/process/process_freebsd.go +++ b/process/process_freebsd.go @@ -279,7 +279,7 @@ func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) { } func Processes() ([]*Process, error) { - results := make([]*Process) + results := []*Process{} mib := []int32{CTLKern, KernProc, KernProcProc, 0} buf, length, err := common.CallSyscall(mib) diff --git a/process/process_openbsd.go b/process/process_openbsd.go index 6bff0b38..aca4ffa7 100644 --- a/process/process_openbsd.go +++ b/process/process_openbsd.go @@ -269,7 +269,7 @@ func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) { } func Processes() ([]*Process, error) { - results := make([]*Process) + results := []*Process{} buf, length, err := CallKernProcSyscall(KernProcAll, 0) From 0314bc81f32c2594ac7110f4c7be348c04bed290 Mon Sep 17 00:00:00 2001 From: Lomanic Date: Sun, 12 Nov 2017 03:08:47 +0100 Subject: [PATCH 09/23] Use w32.EnumProcesses to get pids on Windows in process.Pids() --- process/process_windows.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/process/process_windows.go b/process/process_windows.go index 02478a36..320833b5 100644 --- a/process/process_windows.go +++ b/process/process_windows.go @@ -93,17 +93,18 @@ func init() { } func Pids() ([]int32, error) { + // inspired by https://gist.github.com/henkman/3083408 var ret []int32 + ps := make([]uint32, 2048) + var read uint32 = 0 - procs, err := Processes() - if err != nil { - return ret, nil + if !w32.EnumProcesses(ps, uint32(len(ps)), &read) { + return nil, fmt.Errorf("could not get w32.EnumProcesses") } - for _, proc := range procs { - ret = append(ret, proc.Pid) + for _, pid := range ps[:read/4] { + ret = append(ret, int32(pid)) } - return ret, nil } From c9a24cf2d02ba46a400718a3bed60606df8b54b6 Mon Sep 17 00:00:00 2001 From: Lomanic Date: Sun, 12 Nov 2017 16:40:10 +0100 Subject: [PATCH 10/23] Handle case in Windows process.Pids() where buffer is too small to handle that many processes returned by w32.EnumProcesses --- process/process_windows.go | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/process/process_windows.go b/process/process_windows.go index 320833b5..a1c33732 100644 --- a/process/process_windows.go +++ b/process/process_windows.go @@ -94,18 +94,28 @@ func init() { func Pids() ([]int32, error) { // inspired by https://gist.github.com/henkman/3083408 + // and https://github.com/giampaolo/psutil/blob/1c3a15f637521ba5c0031283da39c733fda53e4c/psutil/arch/windows/process_info.c#L315-L329 var ret []int32 - ps := make([]uint32, 2048) var read uint32 = 0 + var psSize uint32 = 1024 + const dwordSize uint32 = 4 + + for { + ps := make([]uint32, psSize) + if !w32.EnumProcesses(ps, uint32(len(ps)), &read) { + return nil, fmt.Errorf("could not get w32.EnumProcesses") + } + if uint32(len(ps)) == read { // ps buffer was too small to host every results, retry with a bigger one + psSize += 1024 + continue + } + for _, pid := range ps[:read/dwordSize] { + ret = append(ret, int32(pid)) + } + return ret, nil - if !w32.EnumProcesses(ps, uint32(len(ps)), &read) { - return nil, fmt.Errorf("could not get w32.EnumProcesses") } - for _, pid := range ps[:read/4] { - ret = append(ret, int32(pid)) - } - return ret, nil } func (p *Process) Ppid() (int32, error) { From da12f10f63237e264d76604ef5e6d181c0d44572 Mon Sep 17 00:00:00 2001 From: Lomanic Date: Sun, 12 Nov 2017 20:24:44 +0100 Subject: [PATCH 11/23] Use w32.EnumerateProcesses instead of slower wmi in windows process.Processes() --- process/process_windows.go | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/process/process_windows.go b/process/process_windows.go index a1c33732..d0230302 100644 --- a/process/process_windows.go +++ b/process/process_windows.go @@ -94,7 +94,7 @@ func init() { func Pids() ([]int32, error) { // inspired by https://gist.github.com/henkman/3083408 - // and https://github.com/giampaolo/psutil/blob/1c3a15f637521ba5c0031283da39c733fda53e4c/psutil/arch/windows/process_info.c#L315-L329 + // and https://github.com/giampaolo/psutil/blob/1c3a15f637521ba5c0031283da39c733fda53e4c/psutil/arch/windows/process_info.c#L315-L329 var ret []int32 var read uint32 = 0 var psSize uint32 = 1024 @@ -434,19 +434,14 @@ func (p *Process) getFromSnapProcess(pid int32) (int32, int32, string, error) { // Get processes func Processes() ([]*Process, error) { - var dst []Win32_Process - q := wmi.CreateQuery(&dst, "") - err := wmi.Query(q, &dst) + pids, err := Pids() if err != nil { - return []*Process{}, err - } - if len(dst) == 0 { - return []*Process{}, fmt.Errorf("could not get Process") + return []*Process{}, fmt.Errorf("could not get Processes %s", err) } results := []*Process{} - for _, proc := range dst { - p, err := NewProcess(int32(proc.ProcessID)) + for _, pid := range pids { + p, err := NewProcess(int32(pid)) if err != nil { continue } From 00bdebfce7bfd653fa38bdbb546c224a5c84c5fd Mon Sep 17 00:00:00 2001 From: Lomanic Date: Sun, 12 Nov 2017 21:59:17 +0100 Subject: [PATCH 12/23] Use getFromSnapProcess to get process Name and Ppid on Windows instead of slow WMI --- process/process_windows.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/process/process_windows.go b/process/process_windows.go index d0230302..bbd77620 100644 --- a/process/process_windows.go +++ b/process/process_windows.go @@ -119,12 +119,11 @@ func Pids() ([]int32, error) { } func (p *Process) Ppid() (int32, error) { - dst, err := GetWin32Proc(p.Pid) + ppid, _, _, err := getFromSnapProcess(p.Pid) if err != nil { return 0, err } - - return int32(dst[0].ParentProcessID), nil + return ppid, nil } func GetWin32Proc(pid int32) ([]Win32_Process, error) { @@ -144,11 +143,11 @@ func GetWin32Proc(pid int32) ([]Win32_Process, error) { } func (p *Process) Name() (string, error) { - dst, err := GetWin32Proc(p.Pid) + _, _, name, err := getFromSnapProcess(p.Pid) if err != nil { return "", fmt.Errorf("could not get Name: %s", err) } - return dst[0].Name, nil + return name, nil } func (p *Process) Exe() (string, error) { @@ -406,7 +405,7 @@ func (p *Process) Kill() error { return common.ErrNotImplementedError } -func (p *Process) getFromSnapProcess(pid int32) (int32, int32, string, error) { +func getFromSnapProcess(pid int32) (int32, int32, string, error) { snap := w32.CreateToolhelp32Snapshot(w32.TH32CS_SNAPPROCESS, uint32(pid)) if snap == 0 { return 0, 0, "", windows.GetLastError() From 6abd227e48f69bd2e910f32f07afc0ba69fb80b8 Mon Sep 17 00:00:00 2001 From: Daniel Nelson Date: Fri, 17 Nov 2017 10:46:16 -0800 Subject: [PATCH 13/23] [net] linux: skip if not exist error on getProcInodesAll --- net/net_linux.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/net_linux.go b/net/net_linux.go index 4fb5939e..6d798fc8 100644 --- a/net/net_linux.go +++ b/net/net_linux.go @@ -541,8 +541,8 @@ func getProcInodesAll(root string, max int) (map[string][]inodeMap, error) { for _, pid := range pids { t, err := getProcInodes(root, pid, max) if err != nil { - // skip if permission error - if os.IsPermission(err) { + // skip if permission error or no longer exists + if os.IsPermission(err) || os.IsNotExist(err) { continue } return ret, err From f5e19d7e16aaf9552a27fb909fc13a8dfcf22068 Mon Sep 17 00:00:00 2001 From: WAKAYAMA shirou Date: Sat, 18 Nov 2017 22:43:54 +0900 Subject: [PATCH 14/23] [process]windows: implements process.Kill using os/exec --- process/process_test.go | 22 +++++++++++++++++++++- process/process_windows.go | 4 +++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/process/process_test.go b/process/process_test.go index 9947dea9..84c24bd6 100644 --- a/process/process_test.go +++ b/process/process_test.go @@ -3,6 +3,7 @@ package process import ( "fmt" "os" + "os/exec" "os/user" "reflect" "runtime" @@ -418,5 +419,24 @@ func Test_OpenFiles(t *testing.T) { for _, vv := range v { assert.NotEqual(t, "", vv.Path) } - +} + +func Test_Kill(t *testing.T) { + var cmd *exec.Cmd + if runtime.GOOS == "windows" { + cmd = exec.Command("choice", "/C", "YN", "/D", "Y", "/t", "3") + } else { + cmd = exec.Command("sleep", "3") + } + var wg sync.WaitGroup + wg.Add(1) + go func() { + assert.NotNil(t, cmd.Run()) + wg.Done() + }() + time.Sleep(100 * time.Millisecond) + p, err := NewProcess(int32(cmd.Process.Pid)) + assert.Nil(t, err) + assert.Nil(t, p.Kill()) + wg.Wait() } diff --git a/process/process_windows.go b/process/process_windows.go index bbd77620..41ceb34f 100644 --- a/process/process_windows.go +++ b/process/process_windows.go @@ -4,6 +4,7 @@ package process import ( "fmt" + "os" "strings" "syscall" "time" @@ -402,7 +403,8 @@ func (p *Process) Terminate() error { } func (p *Process) Kill() error { - return common.ErrNotImplementedError + process := os.Process{Pid: int(p.Pid)} + return process.Kill() } func getFromSnapProcess(pid int32) (int32, int32, string, error) { From 65598d98cc1b8bd8272bf08b73563a06d775e227 Mon Sep 17 00:00:00 2001 From: leaf Date: Thu, 9 Nov 2017 15:44:26 -0800 Subject: [PATCH 15/23] To prevent hang if wmi.Query hangs, add a context-aware wrapper for it. --- cpu/cpu_windows.go | 15 +++++++++++---- disk/disk_windows.go | 6 ++++-- host/host_windows.go | 5 ++++- internal/common/common_windows.go | 19 ++++++++++++++++++- process/process_windows.go | 11 ++++++++--- 5 files changed, 45 insertions(+), 11 deletions(-) diff --git a/cpu/cpu_windows.go b/cpu/cpu_windows.go index b5bf8258..b012fde4 100644 --- a/cpu/cpu_windows.go +++ b/cpu/cpu_windows.go @@ -3,6 +3,7 @@ package cpu import ( + "context" "fmt" "unsafe" @@ -81,8 +82,9 @@ func Info() ([]InfoStat, error) { var ret []InfoStat var dst []Win32_Processor q := wmi.CreateQuery(&dst, "") - err := wmi.Query(q, &dst) - if err != nil { + ctx, cancel := context.WithTimeout(context.Background(), common.Timeout) + defer cancel() + if err := common.WMIQueryWithContext(ctx, q, &dst); err != nil { return ret, err } @@ -113,8 +115,11 @@ func Info() ([]InfoStat, error) { // Name property is the key by which overall, per cpu and per core metric is known. func PerfInfo() ([]Win32_PerfFormattedData_Counters_ProcessorInformation, error) { var ret []Win32_PerfFormattedData_Counters_ProcessorInformation + q := wmi.CreateQuery(&ret, "") - err := wmi.Query(q, &ret) + ctx, cancel := context.WithTimeout(context.Background(), common.Timeout) + defer cancel() + err := common.WMIQueryWithContext(ctx, q, &ret) return ret, err } @@ -123,7 +128,9 @@ func PerfInfo() ([]Win32_PerfFormattedData_Counters_ProcessorInformation, error) func ProcInfo() ([]Win32_PerfFormattedData_PerfOS_System, error) { var ret []Win32_PerfFormattedData_PerfOS_System q := wmi.CreateQuery(&ret, "") - err := wmi.Query(q, &ret) + ctx, cancel := context.WithTimeout(context.Background(), common.Timeout) + defer cancel() + err := common.WMIQueryWithContext(ctx, q, &ret) return ret, err } diff --git a/disk/disk_windows.go b/disk/disk_windows.go index 6cc22a29..9e5f6817 100644 --- a/disk/disk_windows.go +++ b/disk/disk_windows.go @@ -4,9 +4,9 @@ package disk import ( "bytes" + "context" "unsafe" - "github.com/StackExchange/wmi" "github.com/shirou/gopsutil/internal/common" "golang.org/x/sys/windows" ) @@ -132,7 +132,9 @@ func IOCounters(names ...string) (map[string]IOCountersStat, error) { ret := make(map[string]IOCountersStat, 0) var dst []Win32_PerfFormattedData - err := wmi.Query("SELECT * FROM Win32_PerfFormattedData_PerfDisk_LogicalDisk ", &dst) + ctx, cancel := context.WithTimeout(context.Background(), common.Timeout) + defer cancel() + err := common.WMIQueryWithContext(ctx, "SELECT * FROM Win32_PerfFormattedData_PerfDisk_LogicalDisk", &dst) if err != nil { return ret, err } diff --git a/host/host_windows.go b/host/host_windows.go index 98943021..920cec93 100644 --- a/host/host_windows.go +++ b/host/host_windows.go @@ -3,6 +3,7 @@ package host import ( + "context" "fmt" "os" "runtime" @@ -109,7 +110,9 @@ func getMachineGuid() (string, error) { func GetOSInfo() (Win32_OperatingSystem, error) { var dst []Win32_OperatingSystem q := wmi.CreateQuery(&dst, "") - err := wmi.Query(q, &dst) + ctx, cancel := context.WithTimeout(context.Background(), common.Timeout) + defer cancel() + err := common.WMIQueryWithContext(ctx, q, &dst) if err != nil { return Win32_OperatingSystem{}, err } diff --git a/internal/common/common_windows.go b/internal/common/common_windows.go index 8e0177df..1dffe615 100644 --- a/internal/common/common_windows.go +++ b/internal/common/common_windows.go @@ -3,8 +3,10 @@ package common import ( + "context" "unsafe" + "github.com/StackExchange/wmi" "golang.org/x/sys/windows" ) @@ -49,7 +51,7 @@ var ( ModNt = windows.NewLazyDLL("ntdll.dll") ModPdh = windows.NewLazyDLL("pdh.dll") ModPsapi = windows.NewLazyDLL("psapi.dll") - + ProcGetSystemTimes = Modkernel32.NewProc("GetSystemTimes") ProcNtQuerySystemInformation = ModNt.NewProc("NtQuerySystemInformation") PdhOpenQuery = ModPdh.NewProc("PdhOpenQuery") @@ -110,3 +112,18 @@ func CreateCounter(query windows.Handle, pname, cname string) (*CounterInfo, err Counter: counter, }, nil } + +// WMIQueryWithContext - wraps wmi.Query with a timed-out context to avoid hanging +func WMIQueryWithContext(ctx context.Context, query string, dst interface{}, connectServerArgs ...interface{}) error { + errChan := make(chan error, 1) + go func() { + errChan <- wmi.Query(query, dst, connectServerArgs...) + }() + + select { + case <-ctx.Done(): + return ctx.Err() + case err := <-errChan: + return err + } +} diff --git a/process/process_windows.go b/process/process_windows.go index bbd77620..be91f97d 100644 --- a/process/process_windows.go +++ b/process/process_windows.go @@ -3,6 +3,7 @@ package process import ( + "context" "fmt" "strings" "syscall" @@ -130,8 +131,10 @@ func GetWin32Proc(pid int32) ([]Win32_Process, error) { var dst []Win32_Process query := fmt.Sprintf("WHERE ProcessId = %d", pid) q := wmi.CreateQuery(&dst, query) - - if err := wmi.Query(q, &dst); err != nil { + ctx, cancel := context.WithTimeout(context.Background(), common.Timeout) + defer cancel() + err := common.WMIQueryWithContext(ctx, q, &dst) + if err != nil { return []Win32_Process{}, fmt.Errorf("could not get win32Proc: %s", err) } @@ -333,7 +336,9 @@ func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) { func (p *Process) Children() ([]*Process, error) { var dst []Win32_Process query := wmi.CreateQuery(&dst, fmt.Sprintf("Where ParentProcessId = %d", p.Pid)) - err := wmi.Query(query, &dst) + ctx, cancel := context.WithTimeout(context.Background(), common.Timeout) + defer cancel() + err := common.WMIQueryWithContext(ctx, query, &dst) if err != nil { return nil, err } From 22f3299fd74dd1df8b6539bb75f4ca5462b4cc3d Mon Sep 17 00:00:00 2001 From: Adam Medzinski Date: Mon, 27 Nov 2017 12:17:06 +0100 Subject: [PATCH 16/23] Add missing doc for cpu.TimesStat structure --- cpu/cpu.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cpu/cpu.go b/cpu/cpu.go index f5bf315f..f0dccd41 100644 --- a/cpu/cpu.go +++ b/cpu/cpu.go @@ -12,6 +12,9 @@ import ( "github.com/shirou/gopsutil/internal/common" ) +// TimesStat contains the amounts of time the CPU has spent performing different +// kinds of work. Time units are in USER_HZ or Jiffies (typically hundredths of +// a second). It is based on linux /proc/stat file. type TimesStat struct { CPU string `json:"cpu"` User float64 `json:"user"` From 51c7c4013b6b2317265bfeee7c21cf2e9872596e Mon Sep 17 00:00:00 2001 From: Tatiana Borisova Date: Fri, 8 Dec 2017 15:05:00 +0000 Subject: [PATCH 17/23] Fix build with bazel on OSX --- host/host_darwin_cgo.go | 2 +- host/{include => }/smc.c | 0 host/{include => }/smc.h | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename host/{include => }/smc.c (100%) rename host/{include => }/smc.h (100%) diff --git a/host/host_darwin_cgo.go b/host/host_darwin_cgo.go index be5e18b8..9471c3a1 100644 --- a/host/host_darwin_cgo.go +++ b/host/host_darwin_cgo.go @@ -4,7 +4,7 @@ package host // #cgo LDFLAGS: -framework IOKit -// #include "include/smc.c" +// #include "smc.h" import "C" func SensorsTemperatures() ([]TemperatureStat, error) { diff --git a/host/include/smc.c b/host/smc.c similarity index 100% rename from host/include/smc.c rename to host/smc.c diff --git a/host/include/smc.h b/host/smc.h similarity index 100% rename from host/include/smc.h rename to host/smc.h From b8c456e53432efdbba4ee9e55d27f69cb2dcf1ee Mon Sep 17 00:00:00 2001 From: shirou Date: Sat, 9 Dec 2017 16:50:03 +0900 Subject: [PATCH 18/23] Revert "Fix build with bazel on OSX" This reverts commit 51c7c4013b6b2317265bfeee7c21cf2e9872596e. --- host/host_darwin_cgo.go | 2 +- host/{ => include}/smc.c | 0 host/{ => include}/smc.h | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename host/{ => include}/smc.c (100%) rename host/{ => include}/smc.h (100%) diff --git a/host/host_darwin_cgo.go b/host/host_darwin_cgo.go index 9471c3a1..be5e18b8 100644 --- a/host/host_darwin_cgo.go +++ b/host/host_darwin_cgo.go @@ -4,7 +4,7 @@ package host // #cgo LDFLAGS: -framework IOKit -// #include "smc.h" +// #include "include/smc.c" import "C" func SensorsTemperatures() ([]TemperatureStat, error) { diff --git a/host/smc.c b/host/include/smc.c similarity index 100% rename from host/smc.c rename to host/include/smc.c diff --git a/host/smc.h b/host/include/smc.h similarity index 100% rename from host/smc.h rename to host/include/smc.h From e3ae39aa5b8ece9cd69ff0de52471c0a87f966d5 Mon Sep 17 00:00:00 2001 From: Lomanic Date: Tue, 19 Dec 2017 22:52:41 +0100 Subject: [PATCH 19/23] Fix #442, trim sensor names and properly handle CentOS in host.SensorsTemperatures() --- host/host_linux.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/host/host_linux.go b/host/host_linux.go index 14893a43..d5ada589 100644 --- a/host/host_linux.go +++ b/host/host_linux.go @@ -541,7 +541,7 @@ func SensorsTemperatures() ([]TemperatureStat, error) { if len(files) == 0 { // CentOS has an intermediate /device directory: // https://github.com/giampaolo/psutil/issues/971 - files, err = filepath.Glob(common.HostSys("/class/hwmon/hwmon*/temp*_*")) + files, err = filepath.Glob(common.HostSys("/class/hwmon/hwmon*/device/temp*_*")) if err != nil { return temperatures, err } @@ -557,12 +557,12 @@ func SensorsTemperatures() ([]TemperatureStat, error) { if err != nil { return temperatures, err } - temperature, err := strconv.ParseFloat(string(current), 64) + temperature, err := strconv.ParseFloat(strings.TrimSpace(string(current)), 64) if err != nil { continue } temperatures = append(temperatures, TemperatureStat{ - SensorKey: string(name), + SensorKey: strings.TrimSpace(string(name)), Temperature: temperature / 1000.0, }) } From 482ca3af6d842055fe735f0db994896fca1e7505 Mon Sep 17 00:00:00 2001 From: Nick Kirsch Date: Thu, 4 Jan 2018 11:30:39 -0800 Subject: [PATCH 20/23] Parses the tgid field, which is the thread group id (aka user-space process id) on Linux. Returns error on other platforms. --- process/process.go | 2 ++ process/process_fallback.go | 3 +++ process/process_linux.go | 16 ++++++++++++++++ 3 files changed, 21 insertions(+) diff --git a/process/process.go b/process/process.go index df5da2eb..0e089bb9 100644 --- a/process/process.go +++ b/process/process.go @@ -30,6 +30,8 @@ type Process struct { lastCPUTimes *cpu.TimesStat lastCPUTime time.Time + + tgid int32 } type OpenFilesStat struct { diff --git a/process/process_fallback.go b/process/process_fallback.go index 12bd3a64..e8f545be 100644 --- a/process/process_fallback.go +++ b/process/process_fallback.go @@ -41,6 +41,9 @@ func (p *Process) Ppid() (int32, error) { func (p *Process) Name() (string, error) { return "", common.ErrNotImplementedError } +func (p *Process) Tgid() (int32, error) { + return 0, common.ErrNotImplementedError +} func (p *Process) Exe() (string, error) { return "", common.ErrNotImplementedError } diff --git a/process/process_linux.go b/process/process_linux.go index 9db19972..4b1ace1b 100644 --- a/process/process_linux.go +++ b/process/process_linux.go @@ -100,6 +100,16 @@ func (p *Process) Name() (string, error) { return p.name, nil } +// Tgid returns tgid, a Linux-synonym for user-space Pid +func (p *Process) Tgid() (int32, error) { + if p.tgid == 0 { + if err := p.fillFromStatus(); err != nil { + return 0, err + } + } + return p.tgid, nil +} + // Exe returns executable path of the process. func (p *Process) Exe() (string, error) { return p.fillFromExe() @@ -820,6 +830,12 @@ func (p *Process) fillFromStatus() error { return err } p.parent = int32(pval) + case "Tgid": + pval, err := strconv.ParseInt(value, 10, 32) + if err != nil { + return err + } + p.tgid = int32(pval) case "Uid": p.uids = make([]int32, 0, 4) for _, i := range strings.Split(value, "\t") { From 6c35887d024f4257ed09809d3bab3f2fc50fe14f Mon Sep 17 00:00:00 2001 From: Nick Kirsch Date: Thu, 4 Jan 2018 11:51:49 -0800 Subject: [PATCH 21/23] Add ErrNotImplementedError to Darwin, FreeBSD, and OpenBSD. --- process/process_darwin.go | 3 +++ process/process_freebsd.go | 3 +++ process/process_openbsd.go | 3 +++ 3 files changed, 9 insertions(+) diff --git a/process/process_darwin.go b/process/process_darwin.go index cc289d24..2ed7a0b7 100644 --- a/process/process_darwin.go +++ b/process/process_darwin.go @@ -83,6 +83,9 @@ func (p *Process) Name() (string, error) { return common.IntToString(k.Proc.P_comm[:]), nil } +func (p *Process) Tgid() (int32, error) { + return 0, common.ErrNotImplementedError +} func (p *Process) Exe() (string, error) { lsof_bin, err := exec.LookPath("lsof") if err != nil { diff --git a/process/process_freebsd.go b/process/process_freebsd.go index 9d757176..4db021d8 100644 --- a/process/process_freebsd.go +++ b/process/process_freebsd.go @@ -50,6 +50,9 @@ func (p *Process) Name() (string, error) { return common.IntToString(k.Comm[:]), nil } +func (p *Process) Tgid() (int32, error) { + return 0, common.ErrNotImplementedError +} func (p *Process) Exe() (string, error) { return "", common.ErrNotImplementedError } diff --git a/process/process_openbsd.go b/process/process_openbsd.go index aca4ffa7..088abef7 100644 --- a/process/process_openbsd.go +++ b/process/process_openbsd.go @@ -53,6 +53,9 @@ func (p *Process) Name() (string, error) { return common.IntToString(k.Comm[:]), nil } +func (p *Process) Tgid() (int32, error) { + return 0, common.ErrNotImplementedError +} func (p *Process) Exe() (string, error) { return "", common.ErrNotImplementedError } From fb24c70d366b88caa381823e8e7e387784fcda8b Mon Sep 17 00:00:00 2001 From: Nick Kirsch Date: Fri, 5 Jan 2018 11:37:36 -0800 Subject: [PATCH 22/23] Add ErrNotImplementedError for Tgid support. --- process/process_windows.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/process/process_windows.go b/process/process_windows.go index 3ce840b0..14d466e1 100644 --- a/process/process_windows.go +++ b/process/process_windows.go @@ -154,6 +154,10 @@ func (p *Process) Name() (string, error) { return name, nil } +func (p *Process) Tgid() (int32, error) { + return 0, common.ErrNotImplementedError +} + func (p *Process) Exe() (string, error) { dst, err := GetWin32Proc(p.Pid) if err != nil { From 231815dfeab1ddac1419fcaf564eea078fdf9bfc Mon Sep 17 00:00:00 2001 From: shirou Date: Tue, 9 Jan 2018 11:12:00 +0900 Subject: [PATCH 23/23] [docker]: move String() to docker.go for all platforms. --- docker/docker.go | 11 +++++++++++ docker/docker_linux.go | 11 ----------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docker/docker.go b/docker/docker.go index 1d932cfd..83716fdc 100644 --- a/docker/docker.go +++ b/docker/docker.go @@ -1,6 +1,7 @@ package docker import ( + "encoding/json" "errors" "github.com/shirou/gopsutil/internal/common" @@ -50,6 +51,11 @@ type CgroupMemStat struct { MemFailCnt uint64 `json:"memoryFailcnt"` } +func (m CgroupMemStat) String() string { + s, _ := json.Marshal(m) + return string(s) +} + type CgroupDockerStat struct { ContainerID string `json:"containerID"` Name string `json:"name"` @@ -57,3 +63,8 @@ type CgroupDockerStat struct { Status string `json:"status"` Running bool `json:"running"` } + +func (c CgroupDockerStat) String() string { + s, _ := json.Marshal(c) + return string(s) +} diff --git a/docker/docker_linux.go b/docker/docker_linux.go index 000d2f2a..7cc56d85 100644 --- a/docker/docker_linux.go +++ b/docker/docker_linux.go @@ -3,7 +3,6 @@ package docker import ( - "encoding/json" "fmt" "os" "os/exec" @@ -52,11 +51,6 @@ func GetDockerStat() ([]CgroupDockerStat, error) { return ret, nil } -func (c CgroupDockerStat) String() string { - s, _ := json.Marshal(c) - return string(s) -} - // GetDockerIDList returnes a list of DockerID. // This requires certain permission. func GetDockerIDList() ([]string, error) { @@ -220,11 +214,6 @@ func CgroupMemDocker(containerID string) (*CgroupMemStat, error) { return CgroupMem(containerID, common.HostSys("fs/cgroup/memory/docker")) } -func (m CgroupMemStat) String() string { - s, _ := json.Marshal(m) - return string(s) -} - // getCgroupFilePath constructs file path to get targetted stats file. func getCgroupFilePath(containerID, base, target, file string) string { if len(base) == 0 {