diff --git a/README.rst b/README.rst index 11f3629f..1ceeb72f 100644 --- a/README.rst +++ b/README.rst @@ -66,8 +66,8 @@ Current Status - disk_usage (linux, freebsd, windows) - boot_time (linux, freebsd, windows(but little broken)) - users (linux, freebsd) - - pids (freebsd) - - pid_exists (freebsd) + - pids (linux, freebsd) + - pid_exists (linux, freebsd) - not yet diff --git a/host_windows.go b/host_windows.go index 45e0d293..92b2abba 100644 --- a/host_windows.go +++ b/host_windows.go @@ -22,15 +22,10 @@ func HostInfo() (HostInfoStat, error) { } ret.Hostname = hostname - - kernel32, err := syscall.LoadLibrary("kernel32.dll") - if err != nil { - return ret, err + uptimemsec, _, err := procGetTickCount.Call() + if uptimemsec == 0 { + return ret, syscall.GetLastError() } - defer syscall.FreeLibrary(kernel32) - GetTickCount, _ := syscall.GetProcAddress(kernel32, "GetTickCount") - - uptimemsec, _, err := syscall.Syscall(uintptr(GetTickCount), 0, 0, 0, 0) ret.Uptime = int64(uptimemsec) / 1000 diff --git a/process.go b/process.go index 0d880d6d..a5ddae48 100644 --- a/process.go +++ b/process.go @@ -6,64 +6,64 @@ type Process struct { Name string `json:"name"` Exe string `json:"exe"` Cmdline string `json:"cmdline"` - Create_time int64 + Create_time int64 `json:"create_time"` // Parent Process // FIXME: recursive - Status string - Cwd string - Username string - Uids []int32 - Gids []int32 - Terminal string - Nice int32 - Ionice int32 - Rlimit []RlimitStat - Io_counters Io_countersStat - Num_ctx_switches int32 - Num_fds int32 - Num_handles int32 - Num_Threads int32 - // Threads map[string]string - Cpu_times CPU_TimesStat - // Cpu_percent - Cpu_affinity []int32 - Memory_info Memory_infoStat - Memory_info_ex map[string]string - Memory_percent float32 - Memory_maps []Memory_mapsStat - // Children []Process // FIXME: recursive - Open_files []Open_filesStat - Connections []Net_connectionStat - Is_running bool + Status string `json:"status"` + Cwd string `json:"cwd"` + Username string `json:"username"` + Uids []int32 `json:"uids"` + Gids []int32 `json:"gids"` + Terminal string `json:"terminal"` + Nice int32 `json:"nice"` + Ionice int32 `json:"ionice"` + Rlimit []RlimitStat `json:"rlimit"` + Io_counters Io_countersStat `json:"io_counter"` + Num_ctx_switches int32 `json:"num_ctx_switches"` + Num_fds int32 `json:"num_fds"` + Num_handles int32 `json:"num_handles"` + Num_Threads int32 `json:"nunm_threads"` + // Threads map[string]string `json:"threads"` + Cpu_times CPU_TimesStat `json:"cpu_times"` + // Cpu_percent `json:"cpu_percent"` + Cpu_affinity []int32 `json:"cpu_affinity"` + Memory_info Memory_infoStat `json:"memory_info"` + Memory_info_ex map[string]string `json:"memori_info_ex"` + Memory_percent float32 `json:"memory_percent"` + Memory_maps []Memory_mapsStat `json:"memory_maps"` + // Children []Process // FIXME: recursive `json:"children"` + Open_files []Open_filesStat `json:"open_files"` + Connections []Net_connectionStat `json:"connections"` + Is_running bool `json:"is_running"` } type Open_filesStat struct { - Path string - Fd uint32 + Path string `json:"path"` + Fd uint32 `json:"fd"` } type Memory_infoStat struct { - RSS int32 // bytes - VMS int32 // bytes + RSS int32 `json:"rss"` // bytes + VMS int32 `json:"vms"` // bytes } type Memory_mapsStat struct { - Path string - RSS int32 - Anonymous int32 - Swap int32 + Path string `json:"path"` + RSS int32 `json:"rss"` + Anonymous int32 `json:"anonymous"` + Swap int32 `json:"swap"` } type RlimitStat struct { - Rresource int32 - Soft int32 - Hard int32 + Resource int32 `json:"resource"` + Soft int32 `json:"soft"` + Hard int32 `json:"hard"` } type Io_countersStat struct { - Read_count int32 - Write_count int32 - Read_bytes int32 - Write_bytes int32 + Read_count int32 `json:"read_count"` + Write_count int32 `json:"write_count"` + Read_bytes int32 `json:"read_bytes"` + Write_bytes int32 `json:"write_bytes"` } func Pid_exists(pid int32) (bool, error) { diff --git a/process_freebsd.go b/process_freebsd.go index d210a22f..ae05af64 100644 --- a/process_freebsd.go +++ b/process_freebsd.go @@ -23,29 +23,6 @@ func Pids() ([]int32, error) { return ret, nil } -// Refresh reloads all the data associated with this process. -func (p *Process) Refresh() error { - - mib := []int32{CTL_KERN, KERN_PROC, KERN_PROC_PID, p.Pid} - - buf, length, err := call_syscall(mib) - if err != nil { - return err - } - proc_k := Kinfo_proc{} - if length != uint64(unsafe.Sizeof(proc_k)) { - return err - } - - k, err := parse_kinfo_proc(buf) - if err != nil { - return err - } - - copy_params(&k, p) - return nil -} - func copy_params(k *Kinfo_proc, p *Process) error { p.Exe = byteToString(k.Ki_comm[:]) p.Ppid = k.Ki_ppid @@ -53,17 +30,6 @@ func copy_params(k *Kinfo_proc, p *Process) error { return nil } -func findProcess(pid int32) (*Process, error) { - mib := []int32{CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, pid} - - _, _, err := call_syscall(mib) - if err != nil { - return &Process{}, err - } - - return newProcess(pid) -} - func processes() ([]Process, error) { results := make([]Process, 0, 50) @@ -85,7 +51,7 @@ func processes() ([]Process, error) { if err != nil { continue } - p, err := newProcess(int32(k.Ki_pid)) + p, err := NewProcess(int32(k.Ki_pid)) if err != nil { continue } @@ -146,7 +112,24 @@ func call_syscall(mib []int32) ([]byte, uint64, error) { return buf, length, nil } -func newProcess(pid int32) (*Process, error) { +func NewProcess(pid int32) (*Process, error) { p := &Process{Pid: pid} - return p, p.Refresh() + mib := []int32{CTL_KERN, KERN_PROC, KERN_PROC_PID, p.Pid} + + buf, length, err := call_syscall(mib) + if err != nil { + return nil, err + } + proc_k := Kinfo_proc{} + if length != uint64(unsafe.Sizeof(proc_k)) { + return nil, err + } + + k, err := parse_kinfo_proc(buf) + if err != nil { + return nil, err + } + + copy_params(&k, p) + return p, nil } diff --git a/process_linux.go b/process_linux.go index b3118898..d8ac6093 100644 --- a/process_linux.go +++ b/process_linux.go @@ -10,11 +10,11 @@ import ( "strings" ) -func NewProcess(pid int32) (Process, error) { - p := Process{ +func NewProcess(pid int32) (*Process, error) { + p := &Process{ Pid: int32(pid), } - go fillFromStat(pid, &p) + go fillFromStat(pid, p) /* // user := parseInt32(fields[13]) @@ -101,8 +101,8 @@ func getState(status uint8) (string, error) { return "running", nil } -func processes() ([]Process, error) { - ret := make([]Process, 0) +func processes() ([]*Process, error) { + ret := make([]*Process, 0) pids, err := Pids() if err != nil { diff --git a/process_test.go b/process_test.go index 250f500c..4589fadd 100644 --- a/process_test.go +++ b/process_test.go @@ -3,6 +3,7 @@ package gopsutil import ( "encoding/json" "fmt" + "runtime" "testing" ) @@ -17,7 +18,12 @@ func Test_Pids(t *testing.T) { } func Test_Pid_exists(t *testing.T) { - ret, err := Pid_exists(1) + check_pid := 1 + if runtime.GOOS == "windows" { + check_pid = 0 + } + + ret, err := Pid_exists(int32(check_pid)) if err != nil { t.Errorf("error %v", err) } @@ -28,7 +34,12 @@ func Test_Pid_exists(t *testing.T) { } func Test_NewProcess(t *testing.T) { - ret, err := NewProcess(1) + check_pid := 1 + if runtime.GOOS == "windows" { + check_pid = 0 + } + + ret, err := NewProcess(int32(check_pid)) if err != nil { t.Errorf("error %v", err) } diff --git a/process_windows.go b/process_windows.go new file mode 100644 index 00000000..a69d1a07 --- /dev/null +++ b/process_windows.go @@ -0,0 +1,117 @@ +// +build windows + +package gopsutil + +import ( + "fmt" + "syscall" + "unsafe" +) + +var ( + procCloseHandle = modKernel32.NewProc("CloseHandle") + procCreateToolhelp32Snapshot = modKernel32.NewProc("CreateToolhelp32Snapshot") + procProcess32First = modKernel32.NewProc("Process32FirstW") + procProcess32Next = modKernel32.NewProc("Process32NextW") +) + +const ( + ERROR_NO_MORE_FILES = 0x12 + MAX_PATH = 260 +) + +type PROCESSENTRY32 struct { + DwSize uint32 + CntUsage uint32 + Th32ProcessID uint32 + Th32DefaultHeapID uintptr + Th32ModuleID uint32 + CntThreads uint32 + Th32ParentProcessID uint32 + PcPriClassBase int32 + DwFlags uint32 + SzExeFile [MAX_PATH]uint16 +} + +/* +type SYSTEM_PROCESS_INFORMATION struct { + ULONG NextEntryOffset; + ULONG NumberOfThreads; + BYTE Reserved1[48]; + PVOID Reserved2[3]; + HANDLE UniqueProcessId; + PVOID Reserved3; + ULONG HandleCount; + BYTE Reserved4[4]; + PVOID Reserved5[11]; + SIZE_T PeakPagefileUsage; + SIZE_T PrivatePageCount; + LARGE_INTEGER Reserved6[6]; +} +*/ + +func Pids() ([]int32, error) { + ret := make([]int32, 0) + + procs, err := processes() + if err != nil { + return ret, nil + } + for _, proc := range procs { + ret = append(ret, proc.Pid) + } + + return ret, nil +} + +func NewProcess(pid int32) (*Process, error) { + p := &Process{Pid: pid} + + if (pid == 0) || (pid == 4) { + p.Cmdline = "" + } + return p, nil +} + +func copy_params(pe32 *PROCESSENTRY32, p *Process) error { + p.Ppid = int32(pe32.Th32ParentProcessID) + + return nil +} + +// Get processes +// This is borrowed from go-ps +func processes() ([]*Process, error) { + handle, _, _ := procCreateToolhelp32Snapshot.Call( + 0x00000002, + 0) + if handle < 0 { + return nil, syscall.GetLastError() + } + defer procCloseHandle.Call(handle) + + var entry PROCESSENTRY32 + entry.DwSize = uint32(unsafe.Sizeof(entry)) + ret, _, _ := procProcess32First.Call(handle, uintptr(unsafe.Pointer(&entry))) + if ret == 0 { + return nil, fmt.Errorf("Error retrieving process info.") + } + + results := make([]*Process, 0) + for { + pid := int32(entry.Th32ProcessID) + p, err := NewProcess(pid) + if err != nil { + break + } + copy_params(&entry, p) + results = append(results, p) + + ret, _, _ := procProcess32Next.Call(handle, uintptr(unsafe.Pointer(&entry))) + if ret == 0 { + break + } + } + + return results, nil +}