diff --git a/README.rst b/README.rst index 7dc2909..9a707ba 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. @@ -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 diff --git a/cpu/cpu.go b/cpu/cpu.go index f5bf315..f0dccd4 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"` diff --git a/cpu/cpu_windows.go b/cpu/cpu_windows.go index b5bf825..b012fde 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_openbsd.go b/disk/disk_openbsd.go index d1705ea..348a1f1 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 } diff --git a/disk/disk_windows.go b/disk/disk_windows.go index 6cc22a2..9e5f681 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/docker/docker.go b/docker/docker.go index 1d932cf..83716fd 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 000d2f2..7cc56d8 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 { diff --git a/host/host_linux.go b/host/host_linux.go index 06d469b..a440f90 100644 --- a/host/host_linux.go +++ b/host/host_linux.go @@ -552,7 +552,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 } @@ -568,12 +568,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, }) } diff --git a/host/host_test.go b/host/host_test.go index d9e6611..87a6bd9 100644 --- a/host/host_test.go +++ b/host/host_test.go @@ -56,7 +56,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 { diff --git a/host/host_windows.go b/host/host_windows.go index 9894302..920cec9 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 8e0177d..1dffe61 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/net/net_linux.go b/net/net_linux.go index 4fb5939..6d798fc 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 diff --git a/net/net_openbsd.go b/net/net_openbsd.go index 85cc70c..63caff5 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") @@ -92,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 } @@ -146,8 +151,141 @@ func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) { return nil, errors.New("NetProtoCounters 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 == "*" { + switch family { + case syscall.AF_INET: + host = "0.0.0.0" + case syscall.AF_INET6: + host = "::" + default: + return Addr{}, fmt.Errorf("unknown family, %d", family) + } + } + 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. -// Not Implemented for OpenBSD func Connections(kind string) ([]ConnectionStat, error) { - return nil, errors.New("Connections not implemented for openbsd") + var ret []ConnectionStat + + args := []string{"-na"} + switch strings.ToLower(kind) { + default: + fallthrough + case "": + fallthrough + case "all": + fallthrough + case "inet": + // nothing to add + 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("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 } diff --git a/process/process.go b/process/process.go index df5da2e..0e089bb 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_darwin.go b/process/process_darwin.go index aa17d1d..2ed7a0b 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 { @@ -390,8 +393,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 := []*Process{} mib := []int32{CTLKern, KernProc, KernProcAll, 0} buf, length, err := common.CallSyscall(mib) @@ -403,13 +406,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 +418,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_fallback.go b/process/process_fallback.go index 12bd3a6..e8f545b 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_freebsd.go b/process/process_freebsd.go index aa3e85c..4db021d 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 } @@ -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 } @@ -278,8 +281,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 := []*Process{} mib := []int32{CTLKern, KernProc, KernProcProc, 0} buf, length, err := common.CallSyscall(mib) @@ -302,7 +305,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 1839eb2..4b1ace1 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") { @@ -1017,6 +1033,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 fb553a6..088abef 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 } @@ -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 } @@ -268,8 +271,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 := []*Process{} buf, length, err := CallKernProcSyscall(KernProcAll, 0) @@ -292,7 +295,7 @@ func processes() ([]Process, error) { continue } - results = append(results, *p) + results = append(results, p) } return results, nil diff --git a/process/process_test.go b/process/process_test.go index 9947dea..84c24bd 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 c1cf4cb..14d466e 100644 --- a/process/process_windows.go +++ b/process/process_windows.go @@ -3,7 +3,9 @@ package process import ( + "context" "fmt" + "os" "strings" "syscall" "time" @@ -93,35 +95,47 @@ 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 - - procs, err := processes() - if err != nil { + 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 - } - for _, proc := range procs { - ret = append(ret, proc.Pid) } - return ret, nil } 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) { 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) } @@ -133,11 +147,15 @@ 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) Tgid() (int32, error) { + return 0, common.ErrNotImplementedError } func (p *Process) Exe() (string, error) { @@ -292,11 +310,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 } @@ -323,7 +341,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 } @@ -392,10 +412,11 @@ func (p *Process) Terminate() error { } func (p *Process) Kill() error { - return common.ErrNotImplementedError + process := os.Process{Pid: int(p.Pid)} + return process.Kill() } -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() @@ -422,20 +443,15 @@ 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) +func Processes() ([]*Process, error) { + 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 } @@ -508,9 +524,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) {