From a4671fcc2aa375a260fa0630265948400d0dd3b1 Mon Sep 17 00:00:00 2001 From: Shirou WAKAYAMA Date: Thu, 27 Nov 2014 10:18:15 +0900 Subject: [PATCH] move subdirectories. refer to issue #24 --- common.go | 134 --------- common/common.go | 134 +++++++++ common/common_darwin.go | 20 ++ common/common_freebsd.go | 20 ++ common/common_windows.go | 31 ++ common_darwin.go | 20 -- common_freebsd.go | 20 -- common_windows.go | 31 -- cpu.go | 110 ------- cpu/cpu.go | 110 +++++++ cpu/cpu_darwin.go | 149 ++++++++++ cpu/cpu_freebsd.go | 134 +++++++++ cpu/cpu_linux.go | 181 ++++++++++++ cpu/cpu_test.go | 90 ++++++ cpu/cpu_windows.go | 43 +++ cpu_darwin.go | 147 ---------- cpu_freebsd.go | 132 --------- cpu_linux.go | 181 ------------ cpu_test.go | 90 ------ cpu_windows.go | 43 --- disk.go | 51 ---- disk/disk.go | 51 ++++ disk/disk_darwin.go | 16 ++ disk/disk_freebsd.go | 132 +++++++++ disk/disk_freebsd_amd64.go | 104 +++++++ disk/disk_linux.go | 120 ++++++++ disk/disk_test.go | 93 ++++++ disk/disk_unix.go | 30 ++ disk/disk_windows.go | 119 ++++++++ disk_darwin.go | 12 - disk_freebsd.go | 130 --------- disk_freebsd_amd64.go | 104 ------- disk_linux.go | 120 -------- disk_test.go | 93 ------ disk_unix.go | 30 -- disk_windows.go | 117 -------- docker/docker_linux.go | 184 ++++++++++++ docker/docker_linux_test.go | 46 +++ docker_linux.go | 184 ------------ docker_linux_test.go | 46 --- host.go | 37 --- host/host.go | 37 +++ host/host_darwin.go | 154 ++++++++++ host/host_freebsd.go | 136 +++++++++ host/host_freebsd_amd64.go | 17 ++ host/host_linux.go | 362 ++++++++++++++++++++++++ host/host_linux_amd64.go | 27 ++ host/host_linux_arm.go | 27 ++ host/host_linux_test.go | 61 ++++ host/host_test.go | 67 +++++ host/host_windows.go | 66 +++++ host_darwin.go | 152 ---------- host_freebsd.go | 134 --------- host_freebsd_amd64.go | 17 -- host_linux.go | 360 ----------------------- host_linux_amd64.go | 27 -- host_linux_arm.go | 27 -- host_linux_test.go | 61 ---- host_test.go | 67 ----- host_windows.go | 66 ----- load.go | 16 -- load/load.go | 16 ++ load/load_darwin.go | 37 +++ load/load_freebsd.go | 37 +++ load/load_linux.go | 40 +++ load/load_test.go | 30 ++ load/load_windows.go | 13 + load_darwin.go | 35 --- load_freebsd.go | 35 --- load_linux.go | 40 --- load_test.go | 30 -- load_windows.go | 9 - mem.go | 38 --- mem/mem.go | 38 +++ mem/mem_darwin.go | 106 +++++++ mem/mem_freebsd.go | 131 +++++++++ mem/mem_linux.go | 90 ++++++ mem/mem_test.go | 55 ++++ mem/mem_windows.go | 48 ++++ mem_darwin.go | 104 ------- mem_freebsd.go | 129 --------- mem_linux.go | 90 ------ mem_test.go | 55 ---- mem_windows.go | 48 ---- net.go | 119 -------- net/net.go | 119 ++++++++ net/net_darwin.go | 61 ++++ net/net_freebsd.go | 70 +++++ net/net_linux.go | 74 +++++ net/net_test.go | 69 +++++ net/net_windows.go | 94 ++++++ net_darwin.go | 61 ---- net_freebsd.go | 70 ----- net_linux.go | 74 ----- net_test.go | 69 ----- net_windows.go | 92 ------ process.go | 90 ------ process/process.go | 90 ++++++ process/process_darwin.go | 307 ++++++++++++++++++++ process/process_darwin_amd64.go | 96 +++++++ process/process_freebsd.go | 305 ++++++++++++++++++++ process/process_freebsd_amd64.go | 96 +++++++ process/process_linux.go | 598 +++++++++++++++++++++++++++++++++++++++ process/process_linux_amd64.go | 9 + process/process_linux_arm.go | 9 + process/process_posix.go | 102 +++++++ process/process_posix_test.go | 19 ++ process/process_test.go | 118 ++++++++ process/process_windows.go | 262 +++++++++++++++++ process_darwin.go | 303 -------------------- process_darwin_amd64.go | 96 ------- process_freebsd.go | 301 -------------------- process_freebsd_amd64.go | 96 ------- process_linux.go | 594 -------------------------------------- process_linux_amd64.go | 9 - process_linux_arm.go | 9 - process_posix.go | 102 ------- process_posix_test.go | 19 -- process_test.go | 118 -------- process_windows.go | 260 ----------------- 120 files changed, 5900 insertions(+), 5854 deletions(-) delete mode 100644 common.go create mode 100644 common/common.go create mode 100644 common/common_darwin.go create mode 100644 common/common_freebsd.go create mode 100644 common/common_windows.go delete mode 100644 common_darwin.go delete mode 100644 common_freebsd.go delete mode 100644 common_windows.go delete mode 100644 cpu.go create mode 100644 cpu/cpu.go create mode 100644 cpu/cpu_darwin.go create mode 100644 cpu/cpu_freebsd.go create mode 100644 cpu/cpu_linux.go create mode 100644 cpu/cpu_test.go create mode 100644 cpu/cpu_windows.go delete mode 100644 cpu_darwin.go delete mode 100644 cpu_freebsd.go delete mode 100644 cpu_linux.go delete mode 100644 cpu_test.go delete mode 100644 cpu_windows.go delete mode 100644 disk.go create mode 100644 disk/disk.go create mode 100644 disk/disk_darwin.go create mode 100644 disk/disk_freebsd.go create mode 100644 disk/disk_freebsd_amd64.go create mode 100644 disk/disk_linux.go create mode 100644 disk/disk_test.go create mode 100644 disk/disk_unix.go create mode 100644 disk/disk_windows.go delete mode 100644 disk_darwin.go delete mode 100644 disk_freebsd.go delete mode 100644 disk_freebsd_amd64.go delete mode 100644 disk_linux.go delete mode 100644 disk_test.go delete mode 100644 disk_unix.go delete mode 100644 disk_windows.go create mode 100644 docker/docker_linux.go create mode 100644 docker/docker_linux_test.go delete mode 100644 docker_linux.go delete mode 100644 docker_linux_test.go delete mode 100644 host.go create mode 100644 host/host.go create mode 100644 host/host_darwin.go create mode 100644 host/host_freebsd.go create mode 100644 host/host_freebsd_amd64.go create mode 100644 host/host_linux.go create mode 100644 host/host_linux_amd64.go create mode 100644 host/host_linux_arm.go create mode 100644 host/host_linux_test.go create mode 100644 host/host_test.go create mode 100644 host/host_windows.go delete mode 100644 host_darwin.go delete mode 100644 host_freebsd.go delete mode 100644 host_freebsd_amd64.go delete mode 100644 host_linux.go delete mode 100644 host_linux_amd64.go delete mode 100644 host_linux_arm.go delete mode 100644 host_linux_test.go delete mode 100644 host_test.go delete mode 100644 host_windows.go delete mode 100644 load.go create mode 100644 load/load.go create mode 100644 load/load_darwin.go create mode 100644 load/load_freebsd.go create mode 100644 load/load_linux.go create mode 100644 load/load_test.go create mode 100644 load/load_windows.go delete mode 100644 load_darwin.go delete mode 100644 load_freebsd.go delete mode 100644 load_linux.go delete mode 100644 load_test.go delete mode 100644 load_windows.go delete mode 100644 mem.go create mode 100644 mem/mem.go create mode 100644 mem/mem_darwin.go create mode 100644 mem/mem_freebsd.go create mode 100644 mem/mem_linux.go create mode 100644 mem/mem_test.go create mode 100644 mem/mem_windows.go delete mode 100644 mem_darwin.go delete mode 100644 mem_freebsd.go delete mode 100644 mem_linux.go delete mode 100644 mem_test.go delete mode 100644 mem_windows.go delete mode 100644 net.go create mode 100644 net/net.go create mode 100644 net/net_darwin.go create mode 100644 net/net_freebsd.go create mode 100644 net/net_linux.go create mode 100644 net/net_test.go create mode 100644 net/net_windows.go delete mode 100644 net_darwin.go delete mode 100644 net_freebsd.go delete mode 100644 net_linux.go delete mode 100644 net_test.go delete mode 100644 net_windows.go delete mode 100644 process.go create mode 100644 process/process.go create mode 100644 process/process_darwin.go create mode 100644 process/process_darwin_amd64.go create mode 100644 process/process_freebsd.go create mode 100644 process/process_freebsd_amd64.go create mode 100644 process/process_linux.go create mode 100644 process/process_linux_amd64.go create mode 100644 process/process_linux_arm.go create mode 100644 process/process_posix.go create mode 100644 process/process_posix_test.go create mode 100644 process/process_test.go create mode 100644 process/process_windows.go delete mode 100644 process_darwin.go delete mode 100644 process_darwin_amd64.go delete mode 100644 process_freebsd.go delete mode 100644 process_freebsd_amd64.go delete mode 100644 process_linux.go delete mode 100644 process_linux_amd64.go delete mode 100644 process_linux_arm.go delete mode 100644 process_posix.go delete mode 100644 process_posix_test.go delete mode 100644 process_test.go delete mode 100644 process_windows.go diff --git a/common.go b/common.go deleted file mode 100644 index b4523d0..0000000 --- a/common.go +++ /dev/null @@ -1,134 +0,0 @@ -// -// gopsutil is a port of psutil(http://pythonhosted.org/psutil/). -// This covers these architectures. -// - linux (amd64, arm) -// - freebsd (amd64) -// - windows (amd64) -package gopsutil - -import ( - "bufio" - "errors" - "os" - "reflect" - "strconv" - "strings" -) - -var NotImplementedError = errors.New("not implemented yet") - -// readLines reads contents from file and splits them by new line. -// A convenience wrapper to readLinesOffsetN(filename, 0, -1). -func readLines(filename string) ([]string, error) { - return readLinesOffsetN(filename, 0, -1) -} - -// readLines reads contents from file and splits them by new line. -// The offset tells at which line number to start. -// The count determines the number of lines to read (starting from offset): -// n >= 0: at most n lines -// n < 0: whole file -func readLinesOffsetN(filename string, offset uint, n int) ([]string, error) { - f, err := os.Open(filename) - if err != nil { - return []string{""}, err - } - defer f.Close() - - var ret []string - - r := bufio.NewReader(f) - for i := 0; i < n+int(offset) || n < 0; i++ { - line, err := r.ReadString('\n') - if err != nil { - break - } - if i < int(offset) { - continue - } - ret = append(ret, strings.Trim(line, "\n")) - } - - return ret, nil -} - -func byteToString(orig []byte) string { - n := -1 - l := -1 - for i, b := range orig { - // skip left side null - if l == -1 && b == 0 { - continue - } - if l == -1 { - l = i - } - - if b == 0 { - break - } - n = i + 1 - } - if n == -1 { - return string(orig) - } - return string(orig[l:n]) -} - -// Parse to int32 without error -func mustParseInt32(val string) int32 { - vv, _ := strconv.ParseInt(val, 10, 32) - return int32(vv) -} - -// Parse to uint64 without error -func mustParseUint64(val string) uint64 { - vv, _ := strconv.ParseInt(val, 10, 64) - return uint64(vv) -} - -// Parse to Float64 without error -func mustParseFloat64(val string) float64 { - vv, _ := strconv.ParseFloat(val, 64) - return vv -} - -// Check the target string slice containes src or not -func stringContains(target []string, src string) bool { - for _, t := range target { - if t == src { - return true - } - } - return false -} - -// get struct attributes. -// This method is used only for debugging platform dependent code. -func attributes(m interface{}) map[string]reflect.Type { - typ := reflect.TypeOf(m) - if typ.Kind() == reflect.Ptr { - typ = typ.Elem() - } - - attrs := make(map[string]reflect.Type) - if typ.Kind() != reflect.Struct { - return nil - } - - for i := 0; i < typ.NumField(); i++ { - p := typ.Field(i) - if !p.Anonymous { - attrs[p.Name] = p.Type - } - } - - return attrs -} - -func pathExists(filename string) bool { - if _, err := os.Stat(filename); err == nil { - return true - } - return false -} diff --git a/common/common.go b/common/common.go new file mode 100644 index 0000000..92ec34f --- /dev/null +++ b/common/common.go @@ -0,0 +1,134 @@ +// +// gopsutil is a port of psutil(http://pythonhosted.org/psutil/). +// This covers these architectures. +// - linux (amd64, arm) +// - freebsd (amd64) +// - windows (amd64) +package gopsutil + +import ( + "bufio" + "errors" + "os" + "reflect" + "strconv" + "strings" +) + +var NotImplementedError = errors.New("not implemented yet") + +// readLines reads contents from file and splits them by new line. +// A convenience wrapper to readLinesOffsetN(filename, 0, -1). +func readLines(filename string) ([]string, error) { + return readLinesOffsetN(filename, 0, -1) +} + +// readLines reads contents from file and splits them by new line. +// The offset tells at which line number to start. +// The count determines the number of lines to read (starting from offset): +// n >= 0: at most n lines +// n < 0: whole file +func readLinesOffsetN(filename string, offset uint, n int) ([]string, error) { + f, err := os.Open(filename) + if err != nil { + return []string{""}, err + } + defer f.Close() + + var ret []string + + r := bufio.NewReader(f) + for i := 0; i < n+int(offset) || n < 0; i++ { + line, err := r.ReadString('\n') + if err != nil { + break + } + if i < int(offset) { + continue + } + ret = append(ret, strings.Trim(line, "\n")) + } + + return ret, nil +} + +func ByteToString(orig []byte) string { + n := -1 + l := -1 + for i, b := range orig { + // skip left side null + if l == -1 && b == 0 { + continue + } + if l == -1 { + l = i + } + + if b == 0 { + break + } + n = i + 1 + } + if n == -1 { + return string(orig) + } + return string(orig[l:n]) +} + +// Parse to int32 without error +func mustParseInt32(val string) int32 { + vv, _ := strconv.ParseInt(val, 10, 32) + return int32(vv) +} + +// Parse to uint64 without error +func mustParseUint64(val string) uint64 { + vv, _ := strconv.ParseInt(val, 10, 64) + return uint64(vv) +} + +// Parse to Float64 without error +func mustParseFloat64(val string) float64 { + vv, _ := strconv.ParseFloat(val, 64) + return vv +} + +// Check the target string slice containes src or not +func stringContains(target []string, src string) bool { + for _, t := range target { + if t == src { + return true + } + } + return false +} + +// get struct attributes. +// This method is used only for debugging platform dependent code. +func attributes(m interface{}) map[string]reflect.Type { + typ := reflect.TypeOf(m) + if typ.Kind() == reflect.Ptr { + typ = typ.Elem() + } + + attrs := make(map[string]reflect.Type) + if typ.Kind() != reflect.Struct { + return nil + } + + for i := 0; i < typ.NumField(); i++ { + p := typ.Field(i) + if !p.Anonymous { + attrs[p.Name] = p.Type + } + } + + return attrs +} + +func pathExists(filename string) bool { + if _, err := os.Stat(filename); err == nil { + return true + } + return false +} diff --git a/common/common_darwin.go b/common/common_darwin.go new file mode 100644 index 0000000..7a742bd --- /dev/null +++ b/common/common_darwin.go @@ -0,0 +1,20 @@ +// +build darwin + +package gopsutil + +import ( + "os/exec" + "strings" +) + +func DoSysctrl(mib string) ([]string, error) { + out, err := exec.Command("/usr/sbin/sysctl", "-n", mib).Output() + if err != nil { + return []string{}, err + } + v := strings.Replace(string(out), "{ ", "", 1) + v = strings.Replace(string(v), " }", "", 1) + values := strings.Fields(string(v)) + + return values, nil +} diff --git a/common/common_freebsd.go b/common/common_freebsd.go new file mode 100644 index 0000000..997bb78 --- /dev/null +++ b/common/common_freebsd.go @@ -0,0 +1,20 @@ +// +build freebsd + +package gopsutil + +import ( + "os/exec" + "strings" +) + +func DoSysctrl(mib string) ([]string, error) { + out, err := exec.Command("/sbin/sysctl", "-n", mib).Output() + if err != nil { + return []string{}, err + } + v := strings.Replace(string(out), "{ ", "", 1) + v = strings.Replace(string(v), " }", "", 1) + values := strings.Fields(string(v)) + + return values, nil +} diff --git a/common/common_windows.go b/common/common_windows.go new file mode 100644 index 0000000..af6b2e8 --- /dev/null +++ b/common/common_windows.go @@ -0,0 +1,31 @@ +// +build windows + +package gopsutil + +import ( + "syscall" + "unsafe" +) + +var ( + modkernel32 = syscall.NewLazyDLL("kernel32.dll") + modNt = syscall.NewLazyDLL("ntdll.dll") + + procGetSystemTimes = modkernel32.NewProc("GetSystemTimes") + procNtQuerySystemInformation = modNt.NewProc("NtQuerySystemInformation") +) + +type FILETIME struct { + DwLowDateTime uint32 + DwHighDateTime uint32 +} + +// borrowed from net/interface_windows.go +func BytePtrToString(p *uint8) string { + a := (*[10000]uint8)(unsafe.Pointer(p)) + i := 0 + for a[i] != 0 { + i++ + } + return string(a[:i]) +} diff --git a/common_darwin.go b/common_darwin.go deleted file mode 100644 index 3707636..0000000 --- a/common_darwin.go +++ /dev/null @@ -1,20 +0,0 @@ -// +build darwin - -package gopsutil - -import ( - "os/exec" - "strings" -) - -func doSysctrl(mib string) ([]string, error) { - out, err := exec.Command("/usr/sbin/sysctl", "-n", mib).Output() - if err != nil { - return []string{}, err - } - v := strings.Replace(string(out), "{ ", "", 1) - v = strings.Replace(string(v), " }", "", 1) - values := strings.Fields(string(v)) - - return values, nil -} diff --git a/common_freebsd.go b/common_freebsd.go deleted file mode 100644 index d1e2a6e..0000000 --- a/common_freebsd.go +++ /dev/null @@ -1,20 +0,0 @@ -// +build freebsd - -package gopsutil - -import ( - "os/exec" - "strings" -) - -func doSysctrl(mib string) ([]string, error) { - out, err := exec.Command("/sbin/sysctl", "-n", mib).Output() - if err != nil { - return []string{}, err - } - v := strings.Replace(string(out), "{ ", "", 1) - v = strings.Replace(string(v), " }", "", 1) - values := strings.Fields(string(v)) - - return values, nil -} diff --git a/common_windows.go b/common_windows.go deleted file mode 100644 index e84abc3..0000000 --- a/common_windows.go +++ /dev/null @@ -1,31 +0,0 @@ -// +build windows - -package gopsutil - -import ( - "syscall" - "unsafe" -) - -var ( - modkernel32 = syscall.NewLazyDLL("kernel32.dll") - modNt = syscall.NewLazyDLL("ntdll.dll") - - procGetSystemTimes = modkernel32.NewProc("GetSystemTimes") - procNtQuerySystemInformation = modNt.NewProc("NtQuerySystemInformation") -) - -type FILETIME struct { - DwLowDateTime uint32 - DwHighDateTime uint32 -} - -// borrowed from net/interface_windows.go -func bytePtrToString(p *uint8) string { - a := (*[10000]uint8)(unsafe.Pointer(p)) - i := 0 - for a[i] != 0 { - i++ - } - return string(a[:i]) -} diff --git a/cpu.go b/cpu.go deleted file mode 100644 index c6703e7..0000000 --- a/cpu.go +++ /dev/null @@ -1,110 +0,0 @@ -package gopsutil - -import ( - "encoding/json" - "runtime" - "time" -) - -type CPUTimesStat struct { - CPU string `json:"cpu"` - User float32 `json:"user"` - System float32 `json:"system"` - Idle float32 `json:"idle"` - Nice float32 `json:"nice"` - Iowait float32 `json:"iowait"` - Irq float32 `json:"irq"` - Softirq float32 `json:"softirq"` - Steal float32 `json:"steal"` - Guest float32 `json:"guest"` - GuestNice float32 `json:"guest_nice"` - Stolen float32 `json:"stolen"` -} - -type CPUInfoStat struct { - CPU int32 `json:"cpu"` - VendorID string `json:"vendorId"` - Family string `json:"family"` - Model string `json:"model"` - Stepping int32 `json:"stepping"` - PhysicalID string `json:"physicalId"` - CoreID string `json:"coreId"` - Cores int32 `json:"cores"` - ModelName string `json:"modelName"` - Mhz float64 `json:"mhz"` - CacheSize int32 `json:"cacheSize"` - Flags []string `json:"flags"` -} - -func CPUCounts(logical bool) (int, error) { - return runtime.NumCPU(), nil -} - -var lastCPUTimes []CPUTimesStat -var lastPerCPUTimes []CPUTimesStat - -func CPUPercent(interval time.Duration, percpu bool) ([]float32, error) { - getAllBusy := func(t CPUTimesStat) (float32, float32) { - busy := t.User + t.System + t.Nice + t.Iowait + t.Irq + - t.Softirq + t.Steal + t.Guest + t.GuestNice + t.Stolen - return busy + t.Idle, busy - } - - calculate := func(t1, t2 CPUTimesStat) float32 { - t1All, t1Busy := getAllBusy(t1) - t2All, t2Busy := getAllBusy(t2) - - if t2Busy <= t1Busy { - return 0 - } - if t2All <= t1All { - return 1 - } - return (t2Busy - t1Busy) / (t2All - t1All) * 100 - } - - cpuTimes, err := CPUTimes(percpu) - if err != nil { - return nil, err - } - - if interval > 0 { - if !percpu { - lastCPUTimes = cpuTimes - } else { - lastPerCPUTimes = cpuTimes - } - time.Sleep(interval) - cpuTimes, err = CPUTimes(percpu) - if err != nil { - return nil, err - } - } - - ret := make([]float32, len(cpuTimes)) - if !percpu { - ret[0] = calculate(lastCPUTimes[0], cpuTimes[0]) - lastCPUTimes = cpuTimes - } else { - for i, t := range cpuTimes { - ret[i] = calculate(lastPerCPUTimes[i], t) - } - lastPerCPUTimes = cpuTimes - } - return ret, nil -} - -func (c CPUTimesStat) String() string { - s, _ := json.Marshal(c) - return string(s) -} - -func (c CPUInfoStat) String() string { - s, _ := json.Marshal(c) - return string(s) -} - -func init() { - lastCPUTimes, _ = CPUTimes(false) - lastPerCPUTimes, _ = CPUTimes(true) -} diff --git a/cpu/cpu.go b/cpu/cpu.go new file mode 100644 index 0000000..c6703e7 --- /dev/null +++ b/cpu/cpu.go @@ -0,0 +1,110 @@ +package gopsutil + +import ( + "encoding/json" + "runtime" + "time" +) + +type CPUTimesStat struct { + CPU string `json:"cpu"` + User float32 `json:"user"` + System float32 `json:"system"` + Idle float32 `json:"idle"` + Nice float32 `json:"nice"` + Iowait float32 `json:"iowait"` + Irq float32 `json:"irq"` + Softirq float32 `json:"softirq"` + Steal float32 `json:"steal"` + Guest float32 `json:"guest"` + GuestNice float32 `json:"guest_nice"` + Stolen float32 `json:"stolen"` +} + +type CPUInfoStat struct { + CPU int32 `json:"cpu"` + VendorID string `json:"vendorId"` + Family string `json:"family"` + Model string `json:"model"` + Stepping int32 `json:"stepping"` + PhysicalID string `json:"physicalId"` + CoreID string `json:"coreId"` + Cores int32 `json:"cores"` + ModelName string `json:"modelName"` + Mhz float64 `json:"mhz"` + CacheSize int32 `json:"cacheSize"` + Flags []string `json:"flags"` +} + +func CPUCounts(logical bool) (int, error) { + return runtime.NumCPU(), nil +} + +var lastCPUTimes []CPUTimesStat +var lastPerCPUTimes []CPUTimesStat + +func CPUPercent(interval time.Duration, percpu bool) ([]float32, error) { + getAllBusy := func(t CPUTimesStat) (float32, float32) { + busy := t.User + t.System + t.Nice + t.Iowait + t.Irq + + t.Softirq + t.Steal + t.Guest + t.GuestNice + t.Stolen + return busy + t.Idle, busy + } + + calculate := func(t1, t2 CPUTimesStat) float32 { + t1All, t1Busy := getAllBusy(t1) + t2All, t2Busy := getAllBusy(t2) + + if t2Busy <= t1Busy { + return 0 + } + if t2All <= t1All { + return 1 + } + return (t2Busy - t1Busy) / (t2All - t1All) * 100 + } + + cpuTimes, err := CPUTimes(percpu) + if err != nil { + return nil, err + } + + if interval > 0 { + if !percpu { + lastCPUTimes = cpuTimes + } else { + lastPerCPUTimes = cpuTimes + } + time.Sleep(interval) + cpuTimes, err = CPUTimes(percpu) + if err != nil { + return nil, err + } + } + + ret := make([]float32, len(cpuTimes)) + if !percpu { + ret[0] = calculate(lastCPUTimes[0], cpuTimes[0]) + lastCPUTimes = cpuTimes + } else { + for i, t := range cpuTimes { + ret[i] = calculate(lastPerCPUTimes[i], t) + } + lastPerCPUTimes = cpuTimes + } + return ret, nil +} + +func (c CPUTimesStat) String() string { + s, _ := json.Marshal(c) + return string(s) +} + +func (c CPUInfoStat) String() string { + s, _ := json.Marshal(c) + return string(s) +} + +func init() { + lastCPUTimes, _ = CPUTimes(false) + lastPerCPUTimes, _ = CPUTimes(true) +} diff --git a/cpu/cpu_darwin.go b/cpu/cpu_darwin.go new file mode 100644 index 0000000..cb03fb8 --- /dev/null +++ b/cpu/cpu_darwin.go @@ -0,0 +1,149 @@ +// +build darwin + +package gopsutil + +import ( + "fmt" + "os/exec" + "strconv" + "strings" + + common "github.com/shirou/gopsutil/common" +) + +// sys/resource.h +const ( + CPUser = 0 + CPNice = 1 + CPSys = 2 + CPIntr = 3 + CPIdle = 4 + CPUStates = 5 +) + +// time.h +const ( + ClocksPerSec = 128 +) + +func CPUTimes(percpu bool) ([]CPUTimesStat, error) { + var ret []CPUTimesStat + + var sysctlCall string + var ncpu int + if percpu { + sysctlCall = "kern.cp_times" + ncpu, _ = CPUCounts(true) + } else { + sysctlCall = "kern.cp_time" + ncpu = 1 + } + + cpuTimes, err := common.DoSysctrl(sysctlCall) + if err != nil { + return ret, err + } + + for i := 0; i < ncpu; i++ { + offset := CPUStates * i + user, err := strconv.ParseFloat(cpuTimes[CPUser+offset], 32) + if err != nil { + return ret, err + } + nice, err := strconv.ParseFloat(cpuTimes[CPNice+offset], 32) + if err != nil { + return ret, err + } + sys, err := strconv.ParseFloat(cpuTimes[CPSys+offset], 32) + if err != nil { + return ret, err + } + idle, err := strconv.ParseFloat(cpuTimes[CPIdle+offset], 32) + if err != nil { + return ret, err + } + intr, err := strconv.ParseFloat(cpuTimes[CPIntr+offset], 32) + if err != nil { + return ret, err + } + + c := CPUTimesStat{ + User: float32(user / ClocksPerSec), + Nice: float32(nice / ClocksPerSec), + System: float32(sys / ClocksPerSec), + Idle: float32(idle / ClocksPerSec), + Irq: float32(intr / ClocksPerSec), + } + if !percpu { + c.CPU = "cpu-total" + } else { + c.CPU = fmt.Sprintf("cpu%d", i) + } + + ret = append(ret, c) + } + + return ret, nil +} + +// Returns only one CPUInfoStat on FreeBSD +func CPUInfo() ([]CPUInfoStat, error) { + var ret []CPUInfoStat + + out, err := exec.Command("/usr/sbin/sysctl", "machdep.cpu").Output() + if err != nil { + return ret, err + } + + c := CPUInfoStat{} + for _, line := range strings.Split(string(out), "\n") { + values := strings.Fields(line) + if len(values) < 1 { + continue + } + + t, err := strconv.ParseInt(values[1], 10, 64) + // err is not checked here because some value is string. + if strings.HasPrefix(line, "machdep.cpu.brand_string") { + c.ModelName = strings.Join(values[1:], " ") + } else if strings.HasPrefix(line, "machdep.cpu.family") { + c.Family = values[1] + } else if strings.HasPrefix(line, "machdep.cpu.model") { + c.Model = values[1] + } else if strings.HasPrefix(line, "machdep.cpu.stepping") { + if err != nil { + return ret, err + } + c.Stepping = int32(t) + } else if strings.HasPrefix(line, "machdep.cpu.features") { + for _, v := range values[1:] { + c.Flags = append(c.Flags, strings.ToLower(v)) + } + } else if strings.HasPrefix(line, "machdep.cpu.leaf7_features") { + for _, v := range values[1:] { + c.Flags = append(c.Flags, strings.ToLower(v)) + } + } else if strings.HasPrefix(line, "machdep.cpu.extfeatures") { + for _, v := range values[1:] { + c.Flags = append(c.Flags, strings.ToLower(v)) + } + } else if strings.HasPrefix(line, "machdep.cpu.core_count") { + if err != nil { + return ret, err + } + c.Cores = int32(t) + } else if strings.HasPrefix(line, "machdep.cpu.cache.size") { + if err != nil { + return ret, err + } + c.CacheSize = int32(t) + } else if strings.HasPrefix(line, "machdep.cpu.vendor") { + c.VendorID = values[1] + } + + // TODO: + // c.Mhz = mustParseFloat64(values[1]) + } + + return append(ret, c), nil +} diff --git a/cpu/cpu_freebsd.go b/cpu/cpu_freebsd.go new file mode 100644 index 0000000..f1055bf --- /dev/null +++ b/cpu/cpu_freebsd.go @@ -0,0 +1,134 @@ +// +build freebsd + +package gopsutil + +import ( + "fmt" + "regexp" + "strconv" + "strings" + + common "github.com/shirou/gopsutil/common" +) + +// sys/resource.h +const ( + CPUser = 0 + CPNice = 1 + CPSys = 2 + CPIntr = 3 + CPIdle = 4 + CPUStates = 5 +) + +// time.h +const ( + ClocksPerSec = 128 +) + +func CPUTimes(percpu bool) ([]CPUTimesStat, error) { + var ret []CPUTimesStat + + var sysctlCall string + var ncpu int + if percpu { + sysctlCall = "kern.cp_times" + ncpu, _ = CPUCounts(true) + } else { + sysctlCall = "kern.cp_time" + ncpu = 1 + } + + cpuTimes, err := common.DoSysctrl(sysctlCall) + if err != nil { + return ret, err + } + + for i := 0; i < ncpu; i++ { + offset := CPUStates * i + user, err := strconv.ParseFloat(cpuTimes[CPUser+offset], 32) + if err != nil { + return ret, err + } + nice, err := strconv.ParseFloat(cpuTimes[CPNice+offset], 32) + if err != nil { + return ret, err + } + sys, err := strconv.ParseFloat(cpuTimes[CPSys+offset], 32) + if err != nil { + return ret, err + } + idle, err := strconv.ParseFloat(cpuTimes[CPIdle+offset], 32) + if err != nil { + return ret, err + } + intr, err := strconv.ParseFloat(cpuTimes[CPIntr+offset], 32) + if err != nil { + return ret, err + } + + c := CPUTimesStat{ + User: float32(user / ClocksPerSec), + Nice: float32(nice / ClocksPerSec), + System: float32(sys / ClocksPerSec), + Idle: float32(idle / ClocksPerSec), + Irq: float32(intr / ClocksPerSec), + } + if !percpu { + c.CPU = "cpu-total" + } else { + c.CPU = fmt.Sprintf("cpu%d", i) + } + + ret = append(ret, c) + } + + return ret, nil +} + +// Returns only one CPUInfoStat on FreeBSD +func CPUInfo() ([]CPUInfoStat, error) { + filename := "/var/run/dmesg.boot" + lines, _ := readLines(filename) + + var ret []CPUInfoStat + + c := CPUInfoStat{} + for _, line := range lines { + if matches := regexp.MustCompile(`CPU:\s+(.+) \(([\d.]+).+\)`).FindStringSubmatch(line); matches != nil { + c.ModelName = matches[1] + t, err := strconv.ParseFloat(matches[2], 64) + if err != nil { + return ret, nil + } + c.Mhz = t + } else if matches := regexp.MustCompile(`Origin = "(.+)" Id = (.+) Family = (.+) Model = (.+) Stepping = (.+)`).FindStringSubmatch(line); matches != nil { + c.VendorID = matches[1] + c.Family = matches[3] + c.Model = matches[4] + t, err := strconv.ParseInt(matches[5], 10, 32) + if err != nil { + return ret, nil + } + c.Stepping = int32(t) + } else if matches := regexp.MustCompile(`Features=.+<(.+)>`).FindStringSubmatch(line); matches != nil { + for _, v := range strings.Split(matches[1], ",") { + c.Flags = append(c.Flags, strings.ToLower(v)) + } + } else if matches := regexp.MustCompile(`Features2=[a-f\dx]+<(.+)>`).FindStringSubmatch(line); matches != nil { + for _, v := range strings.Split(matches[1], ",") { + c.Flags = append(c.Flags, strings.ToLower(v)) + } + } else if matches := regexp.MustCompile(`Logical CPUs per core: (\d+)`).FindStringSubmatch(line); matches != nil { + // FIXME: no this line? + t, err := strconv.ParseInt(matches[1], 10, 32) + if err != nil { + return ret, nil + } + c.Cores = int32(t) + } + + } + + return append(ret, c), nil +} diff --git a/cpu/cpu_linux.go b/cpu/cpu_linux.go new file mode 100644 index 0000000..3b2ee71 --- /dev/null +++ b/cpu/cpu_linux.go @@ -0,0 +1,181 @@ +// +build linux + +package gopsutil + +import ( + "errors" + "strconv" + "strings" +) + +func CPUTimes(percpu bool) ([]CPUTimesStat, error) { + filename := "/proc/stat" + var lines []string + if percpu { + ncpu, _ := CPUCounts(true) + lines, _ = readLinesOffsetN(filename, 1, ncpu) + } else { + lines, _ = readLinesOffsetN(filename, 0, 1) + } + + ret := make([]CPUTimesStat, 0, len(lines)) + + for _, line := range lines { + ct, err := parseStatLine(line) + if err != nil { + continue + } + ret = append(ret, *ct) + + } + return ret, nil +} + +func CPUInfo() ([]CPUInfoStat, error) { + filename := "/proc/cpuinfo" + lines, _ := readLines(filename) + + var ret []CPUInfoStat + + var c CPUInfoStat + for _, line := range lines { + fields := strings.Split(line, ":") + if len(fields) < 2 { + if c.VendorID != "" { + ret = append(ret, c) + } + continue + } + key := strings.TrimSpace(fields[0]) + value := strings.TrimSpace(fields[1]) + + switch key { + case "processor": + c = CPUInfoStat{} + t, err := strconv.ParseInt(value, 10, 32) + if err != nil { + return ret, err + } + c.CPU = int32(t) + case "vendor_id": + c.VendorID = value + case "cpu family": + c.Family = value + case "model": + c.Model = value + case "model name": + c.ModelName = value + case "stepping": + t, err := strconv.ParseInt(value, 10, 32) + if err != nil { + return ret, err + } + c.Stepping = int32(t) + case "cpu MHz": + t, err := strconv.ParseFloat(value, 64) + if err != nil { + return ret, err + } + c.Mhz = t + case "cache size": + t, err := strconv.ParseInt(strings.Replace(value, " KB", "", 1), 10, 32) + if err != nil { + return ret, err + } + c.CacheSize = int32(t) + case "physical id": + c.PhysicalID = value + case "core id": + c.CoreID = value + case "cpu cores": + t, err := strconv.ParseInt(value, 10, 32) + if err != nil { + return ret, err + } + c.Cores = int32(t) + case "flags": + c.Flags = strings.Split(value, ",") + } + } + return ret, nil +} + +func parseStatLine(line string) (*CPUTimesStat, error) { + fields := strings.Fields(line) + + if strings.HasPrefix(fields[0], "cpu") == false { + // return CPUTimesStat{}, e + return nil, errors.New("not contain cpu") + } + + cpu := fields[0] + if cpu == "cpu" { + cpu = "cpu-total" + } + user, err := strconv.ParseFloat(fields[1], 32) + if err != nil { + return nil, err + } + nice, err := strconv.ParseFloat(fields[2], 32) + if err != nil { + return nil, err + } + system, err := strconv.ParseFloat(fields[3], 32) + if err != nil { + return nil, err + } + idle, err := strconv.ParseFloat(fields[4], 32) + if err != nil { + return nil, err + } + iowait, err := strconv.ParseFloat(fields[5], 32) + if err != nil { + return nil, err + } + irq, err := strconv.ParseFloat(fields[6], 32) + if err != nil { + return nil, err + } + softirq, err := strconv.ParseFloat(fields[7], 32) + if err != nil { + return nil, err + } + stolen, err := strconv.ParseFloat(fields[8], 32) + if err != nil { + return nil, err + } + ct := &CPUTimesStat{ + CPU: cpu, + User: float32(user), + Nice: float32(nice), + System: float32(system), + Idle: float32(idle), + Iowait: float32(iowait), + Irq: float32(irq), + Softirq: float32(softirq), + Stolen: float32(stolen), + } + if len(fields) > 9 { // Linux >= 2.6.11 + steal, err := strconv.ParseFloat(fields[9], 32) + if err != nil { + return nil, err + } + ct.Steal = float32(steal) + } + if len(fields) > 10 { // Linux >= 2.6.24 + guest, err := strconv.ParseFloat(fields[10], 32) + if err != nil { + return nil, err + } + ct.Guest = float32(guest) + } + if len(fields) > 11 { // Linux >= 3.2.0 + guestNice, err := strconv.ParseFloat(fields[11], 32) + if err != nil { + return nil, err + } + ct.GuestNice = float32(guestNice) + } + + return ct, nil +} diff --git a/cpu/cpu_test.go b/cpu/cpu_test.go new file mode 100644 index 0000000..77aed44 --- /dev/null +++ b/cpu/cpu_test.go @@ -0,0 +1,90 @@ +package gopsutil + +import ( + "fmt" + "runtime" + "testing" + "time" +) + +func TestCpu_times(t *testing.T) { + v, err := CPUTimes(false) + if err != nil { + t.Errorf("error %v", err) + } + if len(v) == 0 { + t.Error("could not get CPUs ", err) + } + empty := CPUTimesStat{} + for _, vv := range v { + if vv == empty { + t.Errorf("could not get CPU User: %v", vv) + } + } +} + +func TestCpu_counts(t *testing.T) { + v, err := CPUCounts(true) + if err != nil { + t.Errorf("error %v", err) + } + if v == 0 { + t.Errorf("could not get CPU counts: %v", v) + } +} + +func TestCPUTimeStat_String(t *testing.T) { + v := CPUTimesStat{ + CPU: "cpu0", + User: 100.1, + System: 200.1, + Idle: 300.1, + } + e := `{"cpu":"cpu0","user":100.1,"system":200.1,"idle":300.1,"nice":0,"iowait":0,"irq":0,"softirq":0,"steal":0,"guest":0,"guest_nice":0,"stolen":0}` + if e != fmt.Sprintf("%v", v) { + t.Errorf("CPUTimesStat string is invalid: %v", v) + } +} + +func TestCpuInfo(t *testing.T) { + v, err := CPUInfo() + if err != nil { + t.Errorf("error %v", err) + } + for _, vv := range v { + if vv.ModelName == "" { + t.Errorf("could not get CPU Info: %v", vv) + } + } +} + +func testCPUPercent(t *testing.T, percpu bool) { + v, err := CPUPercent(time.Millisecond, percpu) + if err != nil { + t.Errorf("error %v", err) + } + numcpu := runtime.NumCPU() + if (percpu && len(v) != numcpu) || (!percpu && len(v) != 1) { + t.Fatalf("wrong number of entries from CPUPercent: %v", v) + } + for i := 0; i < 100; i++ { + duration := time.Duration(10) * time.Microsecond + v, err := CPUPercent(duration, percpu) + if err != nil { + t.Errorf("error %v", err) + } + for _, percent := range v { + if percent < 0.0 || percent > 100.0*float32(numcpu) { + t.Fatalf("CPUPercent value is invalid: %f", percent) + } + } + } +} + +func TestCPUPercent(t *testing.T) { + testCPUPercent(t, false) +} + +func TestCPUPercentPerCpu(t *testing.T) { + testCPUPercent(t, true) +} diff --git a/cpu/cpu_windows.go b/cpu/cpu_windows.go new file mode 100644 index 0000000..1dd8cf6 --- /dev/null +++ b/cpu/cpu_windows.go @@ -0,0 +1,43 @@ +// +build windows + +package gopsutil + +import ( + "syscall" + "unsafe" +) + +// TODO: Get percpu +func CPUTimes(percpu bool) ([]CPUTimesStat, error) { + var ret []CPUTimesStat + + var lpIdleTime FILETIME + var lpKernelTime FILETIME + var lpUserTime FILETIME + r, _, _ := procGetSystemTimes.Call( + uintptr(unsafe.Pointer(&lpIdleTime)), + uintptr(unsafe.Pointer(&lpKernelTime)), + uintptr(unsafe.Pointer(&lpUserTime))) + if r == 0 { + return ret, syscall.GetLastError() + } + + LOT := float64(0.0000001) + HIT := (LOT * 4294967296.0) + idle := ((HIT * float64(lpIdleTime.DwHighDateTime)) + (LOT * float64(lpIdleTime.DwLowDateTime))) + user := ((HIT * float64(lpUserTime.DwHighDateTime)) + (LOT * float64(lpUserTime.DwLowDateTime))) + kernel := ((HIT * float64(lpKernelTime.DwHighDateTime)) + (LOT * float64(lpKernelTime.DwLowDateTime))) + system := (kernel - idle) + + ret = append(ret, CPUTimesStat{ + Idle: float32(idle), + User: float32(user), + System: float32(system), + }) + return ret, nil +} + +func CPUInfo() ([]CPUInfoStat, error) { + var ret []CPUInfoStat + return ret, nil +} diff --git a/cpu_darwin.go b/cpu_darwin.go deleted file mode 100644 index 6d2ba56..0000000 --- a/cpu_darwin.go +++ /dev/null @@ -1,147 +0,0 @@ -// +build darwin - -package gopsutil - -import ( - "fmt" - "os/exec" - "strconv" - "strings" -) - -// sys/resource.h -const ( - CPUser = 0 - CPNice = 1 - CPSys = 2 - CPIntr = 3 - CPIdle = 4 - CPUStates = 5 -) - -// time.h -const ( - ClocksPerSec = 128 -) - -func CPUTimes(percpu bool) ([]CPUTimesStat, error) { - var ret []CPUTimesStat - - var sysctlCall string - var ncpu int - if percpu { - sysctlCall = "kern.cp_times" - ncpu, _ = CPUCounts(true) - } else { - sysctlCall = "kern.cp_time" - ncpu = 1 - } - - cpuTimes, err := doSysctrl(sysctlCall) - if err != nil { - return ret, err - } - - for i := 0; i < ncpu; i++ { - offset := CPUStates * i - user, err := strconv.ParseFloat(cpuTimes[CPUser+offset], 32) - if err != nil { - return ret, err - } - nice, err := strconv.ParseFloat(cpuTimes[CPNice+offset], 32) - if err != nil { - return ret, err - } - sys, err := strconv.ParseFloat(cpuTimes[CPSys+offset], 32) - if err != nil { - return ret, err - } - idle, err := strconv.ParseFloat(cpuTimes[CPIdle+offset], 32) - if err != nil { - return ret, err - } - intr, err := strconv.ParseFloat(cpuTimes[CPIntr+offset], 32) - if err != nil { - return ret, err - } - - c := CPUTimesStat{ - User: float32(user / ClocksPerSec), - Nice: float32(nice / ClocksPerSec), - System: float32(sys / ClocksPerSec), - Idle: float32(idle / ClocksPerSec), - Irq: float32(intr / ClocksPerSec), - } - if !percpu { - c.CPU = "cpu-total" - } else { - c.CPU = fmt.Sprintf("cpu%d", i) - } - - ret = append(ret, c) - } - - return ret, nil -} - -// Returns only one CPUInfoStat on FreeBSD -func CPUInfo() ([]CPUInfoStat, error) { - var ret []CPUInfoStat - - out, err := exec.Command("/usr/sbin/sysctl", "machdep.cpu").Output() - if err != nil { - return ret, err - } - - c := CPUInfoStat{} - for _, line := range strings.Split(string(out), "\n") { - values := strings.Fields(line) - if len(values) < 1 { - continue - } - - t, err := strconv.ParseInt(values[1], 10, 64) - // err is not checked here because some value is string. - if strings.HasPrefix(line, "machdep.cpu.brand_string") { - c.ModelName = strings.Join(values[1:], " ") - } else if strings.HasPrefix(line, "machdep.cpu.family") { - c.Family = values[1] - } else if strings.HasPrefix(line, "machdep.cpu.model") { - c.Model = values[1] - } else if strings.HasPrefix(line, "machdep.cpu.stepping") { - if err != nil { - return ret, err - } - c.Stepping = int32(t) - } else if strings.HasPrefix(line, "machdep.cpu.features") { - for _, v := range values[1:] { - c.Flags = append(c.Flags, strings.ToLower(v)) - } - } else if strings.HasPrefix(line, "machdep.cpu.leaf7_features") { - for _, v := range values[1:] { - c.Flags = append(c.Flags, strings.ToLower(v)) - } - } else if strings.HasPrefix(line, "machdep.cpu.extfeatures") { - for _, v := range values[1:] { - c.Flags = append(c.Flags, strings.ToLower(v)) - } - } else if strings.HasPrefix(line, "machdep.cpu.core_count") { - if err != nil { - return ret, err - } - c.Cores = int32(t) - } else if strings.HasPrefix(line, "machdep.cpu.cache.size") { - if err != nil { - return ret, err - } - c.CacheSize = int32(t) - } else if strings.HasPrefix(line, "machdep.cpu.vendor") { - c.VendorID = values[1] - } - - // TODO: - // c.Mhz = mustParseFloat64(values[1]) - } - - return append(ret, c), nil -} diff --git a/cpu_freebsd.go b/cpu_freebsd.go deleted file mode 100644 index 2d5df5e..0000000 --- a/cpu_freebsd.go +++ /dev/null @@ -1,132 +0,0 @@ -// +build freebsd - -package gopsutil - -import ( - "fmt" - "regexp" - "strconv" - "strings" -) - -// sys/resource.h -const ( - CPUser = 0 - CPNice = 1 - CPSys = 2 - CPIntr = 3 - CPIdle = 4 - CPUStates = 5 -) - -// time.h -const ( - ClocksPerSec = 128 -) - -func CPUTimes(percpu bool) ([]CPUTimesStat, error) { - var ret []CPUTimesStat - - var sysctlCall string - var ncpu int - if percpu { - sysctlCall = "kern.cp_times" - ncpu, _ = CPUCounts(true) - } else { - sysctlCall = "kern.cp_time" - ncpu = 1 - } - - cpuTimes, err := doSysctrl(sysctlCall) - if err != nil { - return ret, err - } - - for i := 0; i < ncpu; i++ { - offset := CPUStates * i - user, err := strconv.ParseFloat(cpuTimes[CPUser+offset], 32) - if err != nil { - return ret, err - } - nice, err := strconv.ParseFloat(cpuTimes[CPNice+offset], 32) - if err != nil { - return ret, err - } - sys, err := strconv.ParseFloat(cpuTimes[CPSys+offset], 32) - if err != nil { - return ret, err - } - idle, err := strconv.ParseFloat(cpuTimes[CPIdle+offset], 32) - if err != nil { - return ret, err - } - intr, err := strconv.ParseFloat(cpuTimes[CPIntr+offset], 32) - if err != nil { - return ret, err - } - - c := CPUTimesStat{ - User: float32(user / ClocksPerSec), - Nice: float32(nice / ClocksPerSec), - System: float32(sys / ClocksPerSec), - Idle: float32(idle / ClocksPerSec), - Irq: float32(intr / ClocksPerSec), - } - if !percpu { - c.CPU = "cpu-total" - } else { - c.CPU = fmt.Sprintf("cpu%d", i) - } - - ret = append(ret, c) - } - - return ret, nil -} - -// Returns only one CPUInfoStat on FreeBSD -func CPUInfo() ([]CPUInfoStat, error) { - filename := "/var/run/dmesg.boot" - lines, _ := readLines(filename) - - var ret []CPUInfoStat - - c := CPUInfoStat{} - for _, line := range lines { - if matches := regexp.MustCompile(`CPU:\s+(.+) \(([\d.]+).+\)`).FindStringSubmatch(line); matches != nil { - c.ModelName = matches[1] - t, err := strconv.ParseFloat(matches[2], 64) - if err != nil { - return ret, nil - } - c.Mhz = t - } else if matches := regexp.MustCompile(`Origin = "(.+)" Id = (.+) Family = (.+) Model = (.+) Stepping = (.+)`).FindStringSubmatch(line); matches != nil { - c.VendorID = matches[1] - c.Family = matches[3] - c.Model = matches[4] - t, err := strconv.ParseInt(matches[5], 10, 32) - if err != nil { - return ret, nil - } - c.Stepping = int32(t) - } else if matches := regexp.MustCompile(`Features=.+<(.+)>`).FindStringSubmatch(line); matches != nil { - for _, v := range strings.Split(matches[1], ",") { - c.Flags = append(c.Flags, strings.ToLower(v)) - } - } else if matches := regexp.MustCompile(`Features2=[a-f\dx]+<(.+)>`).FindStringSubmatch(line); matches != nil { - for _, v := range strings.Split(matches[1], ",") { - c.Flags = append(c.Flags, strings.ToLower(v)) - } - } else if matches := regexp.MustCompile(`Logical CPUs per core: (\d+)`).FindStringSubmatch(line); matches != nil { - // FIXME: no this line? - t, err := strconv.ParseInt(matches[1], 10, 32) - if err != nil { - return ret, nil - } - c.Cores = int32(t) - } - - } - - return append(ret, c), nil -} diff --git a/cpu_linux.go b/cpu_linux.go deleted file mode 100644 index 3b2ee71..0000000 --- a/cpu_linux.go +++ /dev/null @@ -1,181 +0,0 @@ -// +build linux - -package gopsutil - -import ( - "errors" - "strconv" - "strings" -) - -func CPUTimes(percpu bool) ([]CPUTimesStat, error) { - filename := "/proc/stat" - var lines []string - if percpu { - ncpu, _ := CPUCounts(true) - lines, _ = readLinesOffsetN(filename, 1, ncpu) - } else { - lines, _ = readLinesOffsetN(filename, 0, 1) - } - - ret := make([]CPUTimesStat, 0, len(lines)) - - for _, line := range lines { - ct, err := parseStatLine(line) - if err != nil { - continue - } - ret = append(ret, *ct) - - } - return ret, nil -} - -func CPUInfo() ([]CPUInfoStat, error) { - filename := "/proc/cpuinfo" - lines, _ := readLines(filename) - - var ret []CPUInfoStat - - var c CPUInfoStat - for _, line := range lines { - fields := strings.Split(line, ":") - if len(fields) < 2 { - if c.VendorID != "" { - ret = append(ret, c) - } - continue - } - key := strings.TrimSpace(fields[0]) - value := strings.TrimSpace(fields[1]) - - switch key { - case "processor": - c = CPUInfoStat{} - t, err := strconv.ParseInt(value, 10, 32) - if err != nil { - return ret, err - } - c.CPU = int32(t) - case "vendor_id": - c.VendorID = value - case "cpu family": - c.Family = value - case "model": - c.Model = value - case "model name": - c.ModelName = value - case "stepping": - t, err := strconv.ParseInt(value, 10, 32) - if err != nil { - return ret, err - } - c.Stepping = int32(t) - case "cpu MHz": - t, err := strconv.ParseFloat(value, 64) - if err != nil { - return ret, err - } - c.Mhz = t - case "cache size": - t, err := strconv.ParseInt(strings.Replace(value, " KB", "", 1), 10, 32) - if err != nil { - return ret, err - } - c.CacheSize = int32(t) - case "physical id": - c.PhysicalID = value - case "core id": - c.CoreID = value - case "cpu cores": - t, err := strconv.ParseInt(value, 10, 32) - if err != nil { - return ret, err - } - c.Cores = int32(t) - case "flags": - c.Flags = strings.Split(value, ",") - } - } - return ret, nil -} - -func parseStatLine(line string) (*CPUTimesStat, error) { - fields := strings.Fields(line) - - if strings.HasPrefix(fields[0], "cpu") == false { - // return CPUTimesStat{}, e - return nil, errors.New("not contain cpu") - } - - cpu := fields[0] - if cpu == "cpu" { - cpu = "cpu-total" - } - user, err := strconv.ParseFloat(fields[1], 32) - if err != nil { - return nil, err - } - nice, err := strconv.ParseFloat(fields[2], 32) - if err != nil { - return nil, err - } - system, err := strconv.ParseFloat(fields[3], 32) - if err != nil { - return nil, err - } - idle, err := strconv.ParseFloat(fields[4], 32) - if err != nil { - return nil, err - } - iowait, err := strconv.ParseFloat(fields[5], 32) - if err != nil { - return nil, err - } - irq, err := strconv.ParseFloat(fields[6], 32) - if err != nil { - return nil, err - } - softirq, err := strconv.ParseFloat(fields[7], 32) - if err != nil { - return nil, err - } - stolen, err := strconv.ParseFloat(fields[8], 32) - if err != nil { - return nil, err - } - ct := &CPUTimesStat{ - CPU: cpu, - User: float32(user), - Nice: float32(nice), - System: float32(system), - Idle: float32(idle), - Iowait: float32(iowait), - Irq: float32(irq), - Softirq: float32(softirq), - Stolen: float32(stolen), - } - if len(fields) > 9 { // Linux >= 2.6.11 - steal, err := strconv.ParseFloat(fields[9], 32) - if err != nil { - return nil, err - } - ct.Steal = float32(steal) - } - if len(fields) > 10 { // Linux >= 2.6.24 - guest, err := strconv.ParseFloat(fields[10], 32) - if err != nil { - return nil, err - } - ct.Guest = float32(guest) - } - if len(fields) > 11 { // Linux >= 3.2.0 - guestNice, err := strconv.ParseFloat(fields[11], 32) - if err != nil { - return nil, err - } - ct.GuestNice = float32(guestNice) - } - - return ct, nil -} diff --git a/cpu_test.go b/cpu_test.go deleted file mode 100644 index 77aed44..0000000 --- a/cpu_test.go +++ /dev/null @@ -1,90 +0,0 @@ -package gopsutil - -import ( - "fmt" - "runtime" - "testing" - "time" -) - -func TestCpu_times(t *testing.T) { - v, err := CPUTimes(false) - if err != nil { - t.Errorf("error %v", err) - } - if len(v) == 0 { - t.Error("could not get CPUs ", err) - } - empty := CPUTimesStat{} - for _, vv := range v { - if vv == empty { - t.Errorf("could not get CPU User: %v", vv) - } - } -} - -func TestCpu_counts(t *testing.T) { - v, err := CPUCounts(true) - if err != nil { - t.Errorf("error %v", err) - } - if v == 0 { - t.Errorf("could not get CPU counts: %v", v) - } -} - -func TestCPUTimeStat_String(t *testing.T) { - v := CPUTimesStat{ - CPU: "cpu0", - User: 100.1, - System: 200.1, - Idle: 300.1, - } - e := `{"cpu":"cpu0","user":100.1,"system":200.1,"idle":300.1,"nice":0,"iowait":0,"irq":0,"softirq":0,"steal":0,"guest":0,"guest_nice":0,"stolen":0}` - if e != fmt.Sprintf("%v", v) { - t.Errorf("CPUTimesStat string is invalid: %v", v) - } -} - -func TestCpuInfo(t *testing.T) { - v, err := CPUInfo() - if err != nil { - t.Errorf("error %v", err) - } - for _, vv := range v { - if vv.ModelName == "" { - t.Errorf("could not get CPU Info: %v", vv) - } - } -} - -func testCPUPercent(t *testing.T, percpu bool) { - v, err := CPUPercent(time.Millisecond, percpu) - if err != nil { - t.Errorf("error %v", err) - } - numcpu := runtime.NumCPU() - if (percpu && len(v) != numcpu) || (!percpu && len(v) != 1) { - t.Fatalf("wrong number of entries from CPUPercent: %v", v) - } - for i := 0; i < 100; i++ { - duration := time.Duration(10) * time.Microsecond - v, err := CPUPercent(duration, percpu) - if err != nil { - t.Errorf("error %v", err) - } - for _, percent := range v { - if percent < 0.0 || percent > 100.0*float32(numcpu) { - t.Fatalf("CPUPercent value is invalid: %f", percent) - } - } - } -} - -func TestCPUPercent(t *testing.T) { - testCPUPercent(t, false) -} - -func TestCPUPercentPerCpu(t *testing.T) { - testCPUPercent(t, true) -} diff --git a/cpu_windows.go b/cpu_windows.go deleted file mode 100644 index 1dd8cf6..0000000 --- a/cpu_windows.go +++ /dev/null @@ -1,43 +0,0 @@ -// +build windows - -package gopsutil - -import ( - "syscall" - "unsafe" -) - -// TODO: Get percpu -func CPUTimes(percpu bool) ([]CPUTimesStat, error) { - var ret []CPUTimesStat - - var lpIdleTime FILETIME - var lpKernelTime FILETIME - var lpUserTime FILETIME - r, _, _ := procGetSystemTimes.Call( - uintptr(unsafe.Pointer(&lpIdleTime)), - uintptr(unsafe.Pointer(&lpKernelTime)), - uintptr(unsafe.Pointer(&lpUserTime))) - if r == 0 { - return ret, syscall.GetLastError() - } - - LOT := float64(0.0000001) - HIT := (LOT * 4294967296.0) - idle := ((HIT * float64(lpIdleTime.DwHighDateTime)) + (LOT * float64(lpIdleTime.DwLowDateTime))) - user := ((HIT * float64(lpUserTime.DwHighDateTime)) + (LOT * float64(lpUserTime.DwLowDateTime))) - kernel := ((HIT * float64(lpKernelTime.DwHighDateTime)) + (LOT * float64(lpKernelTime.DwLowDateTime))) - system := (kernel - idle) - - ret = append(ret, CPUTimesStat{ - Idle: float32(idle), - User: float32(user), - System: float32(system), - }) - return ret, nil -} - -func CPUInfo() ([]CPUInfoStat, error) { - var ret []CPUInfoStat - return ret, nil -} diff --git a/disk.go b/disk.go deleted file mode 100644 index d94caf7..0000000 --- a/disk.go +++ /dev/null @@ -1,51 +0,0 @@ -package gopsutil - -import ( - "encoding/json" -) - -type DiskUsageStat struct { - Path string `json:"path"` - Total uint64 `json:"total"` - Free uint64 `json:"free"` - Used uint64 `json:"used"` - UsedPercent float64 `json:"usedPercent"` - InodesTotal uint64 `json:"inodesTotal"` - InodesUsed uint64 `json:"inodesUsed"` - InodesFree uint64 `json:"inodesFree"` - InodesUsedPercent float64 `json:"inodesUsedPercent"` -} - -type DiskPartitionStat struct { - Device string `json:"device"` - Mountpoint string `json:"mountpoint"` - Fstype string `json:"fstype"` - Opts string `json:"opts"` -} - -type DiskIOCountersStat struct { - ReadCount uint64 `json:"readCount"` - WriteCount uint64 `json:"writeCount"` - ReadBytes uint64 `json:"readBytes"` - WriteBytes uint64 `json:"writeBytes"` - ReadTime uint64 `json:"readTime"` - WriteTime uint64 `json:"writeTime"` - Name string `json:"name"` - IoTime uint64 `json:"ioTime"` - SerialNumber string `json:"serialNumber"` -} - -func (d DiskUsageStat) String() string { - s, _ := json.Marshal(d) - return string(s) -} - -func (d DiskPartitionStat) String() string { - s, _ := json.Marshal(d) - return string(s) -} - -func (d DiskIOCountersStat) String() string { - s, _ := json.Marshal(d) - return string(s) -} diff --git a/disk/disk.go b/disk/disk.go new file mode 100644 index 0000000..d94caf7 --- /dev/null +++ b/disk/disk.go @@ -0,0 +1,51 @@ +package gopsutil + +import ( + "encoding/json" +) + +type DiskUsageStat struct { + Path string `json:"path"` + Total uint64 `json:"total"` + Free uint64 `json:"free"` + Used uint64 `json:"used"` + UsedPercent float64 `json:"usedPercent"` + InodesTotal uint64 `json:"inodesTotal"` + InodesUsed uint64 `json:"inodesUsed"` + InodesFree uint64 `json:"inodesFree"` + InodesUsedPercent float64 `json:"inodesUsedPercent"` +} + +type DiskPartitionStat struct { + Device string `json:"device"` + Mountpoint string `json:"mountpoint"` + Fstype string `json:"fstype"` + Opts string `json:"opts"` +} + +type DiskIOCountersStat struct { + ReadCount uint64 `json:"readCount"` + WriteCount uint64 `json:"writeCount"` + ReadBytes uint64 `json:"readBytes"` + WriteBytes uint64 `json:"writeBytes"` + ReadTime uint64 `json:"readTime"` + WriteTime uint64 `json:"writeTime"` + Name string `json:"name"` + IoTime uint64 `json:"ioTime"` + SerialNumber string `json:"serialNumber"` +} + +func (d DiskUsageStat) String() string { + s, _ := json.Marshal(d) + return string(s) +} + +func (d DiskPartitionStat) String() string { + s, _ := json.Marshal(d) + return string(s) +} + +func (d DiskIOCountersStat) String() string { + s, _ := json.Marshal(d) + return string(s) +} diff --git a/disk/disk_darwin.go b/disk/disk_darwin.go new file mode 100644 index 0000000..8b8b7e3 --- /dev/null +++ b/disk/disk_darwin.go @@ -0,0 +1,16 @@ +// +build darwin + +package gopsutil + +import ( + common "github.com/shirou/gopsutil/common" +) + +func DiskPartitions(all bool) ([]DiskPartitionStat, error) { + + return nil, common.NotImplementedError +} + +func DiskIOCounters() (map[string]DiskIOCountersStat, error) { + return nil, common.NotImplementedError +} diff --git a/disk/disk_freebsd.go b/disk/disk_freebsd.go new file mode 100644 index 0000000..c415e15 --- /dev/null +++ b/disk/disk_freebsd.go @@ -0,0 +1,132 @@ +// +build freebsd + +package gopsutil + +import ( + "syscall" + "unsafe" + + common "github.com/shirou/gopsutil/common" +) + +func DiskPartitions(all bool) ([]DiskPartitionStat, error) { + var ret []DiskPartitionStat + + // get length + count, err := syscall.Getfsstat(nil, MntWait) + if err != nil { + return ret, err + } + + fs := make([]Statfs, count) + _, err = Getfsstat(fs, MntWait) + + for _, stat := range fs { + opts := "rw" + if stat.FFlags&MntReadOnly != 0 { + opts = "ro" + } + if stat.FFlags&MntSynchronous != 0 { + opts += ",sync" + } + if stat.FFlags&MntNoExec != 0 { + opts += ",noexec" + } + if stat.FFlags&MntNoSuid != 0 { + opts += ",nosuid" + } + if stat.FFlags&MntUnion != 0 { + opts += ",union" + } + if stat.FFlags&MntAsync != 0 { + opts += ",async" + } + if stat.FFlags&MntSuidDir != 0 { + opts += ",suiddir" + } + if stat.FFlags&MntSoftDep != 0 { + opts += ",softdep" + } + if stat.FFlags&MntNoSymFollow != 0 { + opts += ",nosymfollow" + } + if stat.FFlags&MntGEOMJournal != 0 { + opts += ",gjounalc" + } + if stat.FFlags&MntMultilabel != 0 { + opts += ",multilabel" + } + if stat.FFlags&MntACLs != 0 { + opts += ",acls" + } + if stat.FFlags&MntNoATime != 0 { + opts += ",noattime" + } + if stat.FFlags&MntClusterRead != 0 { + opts += ",nocluster" + } + if stat.FFlags&MntClusterWrite != 0 { + opts += ",noclusterw" + } + if stat.FFlags&MntNFS4ACLs != 0 { + opts += ",nfs4acls" + } + + d := DiskPartitionStat{ + Device: common.ByteToString(stat.FMntfromname[:]), + Mountpoint: common.ByteToString(stat.FMntonname[:]), + Fstype: common.ByteToString(stat.FFstypename[:]), + Opts: opts, + } + ret = append(ret, d) + } + + return ret, nil +} + +func DiskIOCounters() (map[string]DiskIOCountersStat, error) { + return nil, common.NotImplementedError + + // statinfo->devinfo->devstat + // /usr/include/devinfo.h + + // get length + count, err := Getfsstat(nil, MntWait) + if err != nil { + return nil, err + } + + fs := make([]Statfs, count) + _, err = Getfsstat(fs, MntWait) + + ret := make(map[string]DiskIOCountersStat, 0) + for _, stat := range fs { + name := ByteToString(stat.FMntonname[:]) + d := DiskIOCountersStat{ + Name: name, + ReadCount: stat.FSyncwrites + stat.FAsyncwrites, + WriteCount: stat.FSyncreads + stat.FAsyncreads, + } + + ret[name] = d + } + + return ret, nil +} + +// Getfsstat is borrowed from pkg/syscall/syscall_freebsd.go +// change Statfs_t to Statfs in order to get more information +func Getfsstat(buf []Statfs, flags int) (n int, err error) { + var _p0 unsafe.Pointer + var bufsize uintptr + if len(buf) > 0 { + _p0 = unsafe.Pointer(&buf[0]) + bufsize = unsafe.Sizeof(Statfs{}) * uintptr(len(buf)) + } + r0, _, e1 := syscall.Syscall(syscall.SYS_GETFSSTAT, uintptr(_p0), bufsize, uintptr(flags)) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} diff --git a/disk/disk_freebsd_amd64.go b/disk/disk_freebsd_amd64.go new file mode 100644 index 0000000..b02aa33 --- /dev/null +++ b/disk/disk_freebsd_amd64.go @@ -0,0 +1,104 @@ +// +build freebsd +// +build amd64 + +package gopsutil + +const ( + MntWait = 1 + MfsNameLen = 16 /* length of type name including null */ + MNameLen = 88 /* size of on/from name bufs */ +) + +// sys/mount.h +const ( + MntReadOnly = 0x00000001 /* read only filesystem */ + MntSynchronous = 0x00000002 /* filesystem written synchronously */ + MntNoExec = 0x00000004 /* can't exec from filesystem */ + MntNoSuid = 0x00000008 /* don't honor setuid bits on fs */ + MntUnion = 0x00000020 /* union with underlying filesystem */ + MntAsync = 0x00000040 /* filesystem written asynchronously */ + MntSuidDir = 0x00100000 /* special handling of SUID on dirs */ + MntSoftDep = 0x00200000 /* soft updates being done */ + MntNoSymFollow = 0x00400000 /* do not follow symlinks */ + MntGEOMJournal = 0x02000000 /* GEOM journal support enabled */ + MntMultilabel = 0x04000000 /* MAC support for individual objects */ + MntACLs = 0x08000000 /* ACL support enabled */ + MntNoATime = 0x10000000 /* disable update of file access time */ + MntClusterRead = 0x40000000 /* disable cluster read */ + MntClusterWrite = 0x80000000 /* disable cluster write */ + MntNFS4ACLs = 0x00000010 +) + +type Statfs struct { + FVersion uint32 /* structure version number */ + FType uint32 /* type of filesystem */ + FFlags uint64 /* copy of mount exported flags */ + FBsize uint64 /* filesystem fragment size */ + FIosize uint64 /* optimal transfer block size */ + FBlocks uint64 /* total data blocks in filesystem */ + FBfree uint64 /* free blocks in filesystem */ + FBavail int64 /* free blocks avail to non-superuser */ + FFiles uint64 /* total file nodes in filesystem */ + FFfree int64 /* free nodes avail to non-superuser */ + FSyncwrites uint64 /* count of sync writes since mount */ + FAsyncwrites uint64 /* count of async writes since mount */ + FSyncreads uint64 /* count of sync reads since mount */ + FAsyncreads uint64 /* count of async reads since mount */ + FSpare [10]uint64 /* unused spare */ + FNamemax uint32 /* maximum filename length */ + FOwner uint32 /* user that mounted the filesystem */ + FFsid int32 /* filesystem id */ + FCharspare [80]byte /* spare string space */ + FFstypename [MfsNameLen]byte /* filesystem type name */ + FMntfromname [MNameLen]byte /* mounted filesystem */ + FMntonname [MNameLen]byte /* directory on which mounted */ +} + +// /usr/include/devstat.h +// devstat_getdevs() +// kern.devstat.all -> devstats list struct + +// struct devinfo { +// struct devstat *devices; +// u_int8_t *mem_ptr; +// long generation; +// int numdevs; +// }; +// +// struct statinfo { +// long cp_time[CPUSTATES]; +// long tk_nin; +// long tk_nout; +// struct devinfo *dinfo; +// long double snap_time; +// }; + +// /usr/include/devinfo.h + +// struct devinfo_dev { +// devinfo_handle_t dd_handle; /* device handle */ +// devinfo_handle_t dd_parent; /* parent handle */ +// char *dd_name; /* name of device */ +// char *dd_desc; /* device description */ +// char *dd_drivername; /* name of attached driver */ +// char *dd_pnpinfo; /* pnp info from parent bus */ +// char *dd_location; /* Where bus thinks dev at */ +// uint32_t dd_devflags; /* API flags */ +// uint16_t dd_flags; /* internal dev flags */ +// device_state_t dd_state; /* attachment state of dev */ +// }; +// +// struct devinfo_rman { +// devinfo_handle_t dm_handle; /* resource manager handle */ +// u_long dm_start; /* resource start */ +// u_long dm_size; /* resource size */ +// char *dm_desc; /* resource description */ +// }; +// +// struct devinfo_res { +// devinfo_handle_t dr_handle; /* resource handle */ +// devinfo_handle_t dr_rman; /* resource manager handle */ +// devinfo_handle_t dr_device; /* owning device */ +// u_long dr_start; /* region start */ +// u_long dr_size; /* region size */ +// }; diff --git a/disk/disk_linux.go b/disk/disk_linux.go new file mode 100644 index 0000000..51e504b --- /dev/null +++ b/disk/disk_linux.go @@ -0,0 +1,120 @@ +// +build linux + +package gopsutil + +import ( + "fmt" + "os/exec" + "strconv" + "strings" +) + +const ( + SectorSize = 512 +) + +// Get disk partitions. +// should use setmntent(3) but this implement use /etc/mtab file +func DiskPartitions(all bool) ([]DiskPartitionStat, error) { + + filename := "/etc/mtab" + lines, err := readLines(filename) + if err != nil { + return nil, err + } + + ret := make([]DiskPartitionStat, 0, len(lines)) + + for _, line := range lines { + fields := strings.Fields(line) + d := DiskPartitionStat{ + Device: fields[0], + Mountpoint: fields[1], + Fstype: fields[2], + Opts: fields[3], + } + ret = append(ret, d) + } + + return ret, nil +} + +func DiskIOCounters() (map[string]DiskIOCountersStat, error) { + filename := "/proc/diskstats" + lines, err := readLines(filename) + if err != nil { + return nil, err + } + ret := make(map[string]DiskIOCountersStat, 0) + empty := DiskIOCountersStat{} + + for _, line := range lines { + fields := strings.Fields(line) + name := fields[2] + reads, err := strconv.ParseUint((fields[3]), 10, 64) + if err != nil { + return ret, err + } + rbytes, err := strconv.ParseUint((fields[5]), 10, 64) + if err != nil { + return ret, err + } + rtime, err := strconv.ParseUint((fields[6]), 10, 64) + if err != nil { + return ret, err + } + writes, err := strconv.ParseUint((fields[7]), 10, 64) + if err != nil { + return ret, err + } + wbytes, err := strconv.ParseUint((fields[9]), 10, 64) + if err != nil { + return ret, err + } + wtime, err := strconv.ParseUint((fields[10]), 10, 64) + if err != nil { + return ret, err + } + iotime, err := strconv.ParseUint((fields[12]), 10, 64) + if err != nil { + return ret, err + } + d := DiskIOCountersStat{ + ReadBytes: rbytes * SectorSize, + WriteBytes: wbytes * SectorSize, + ReadCount: reads, + WriteCount: writes, + ReadTime: rtime, + WriteTime: wtime, + IoTime: iotime, + } + if d == empty { + continue + } + d.Name = name + + d.SerialNumber = GetDiskSerialNumber(name) + ret[name] = d + } + return ret, nil +} + +func GetDiskSerialNumber(name string) string { + n := fmt.Sprintf("--name=%s", name) + out, err := exec.Command("/sbin/udevadm", "info", "--query=property", n).Output() + + // does not return error, just an empty string + if err != nil { + return "" + } + lines := strings.Split(string(out), "\n") + for _, line := range lines { + values := strings.Split(line, "=") + if len(values) < 2 || values[0] != "ID_SERIAL" { + // only get ID_SERIAL, not ID_SERIAL_SHORT + continue + } + return values[1] + } + return "" +} diff --git a/disk/disk_test.go b/disk/disk_test.go new file mode 100644 index 0000000..0f20030 --- /dev/null +++ b/disk/disk_test.go @@ -0,0 +1,93 @@ +package gopsutil + +import ( + "fmt" + "runtime" + "testing" +) + +func TestDisk_usage(t *testing.T) { + path := "/" + if runtime.GOOS == "windows" { + path = "C:" + } + v, err := DiskUsage(path) + if err != nil { + t.Errorf("error %v", err) + } + if v.Path != path { + t.Errorf("error %v", err) + } +} + +func TestDisk_partitions(t *testing.T) { + ret, err := DiskPartitions(false) + if err != nil || len(ret) == 0 { + t.Errorf("error %v", err) + } + empty := DiskPartitionStat{} + for _, disk := range ret { + if disk == empty { + t.Errorf("Could not get device info %v", disk) + } + } +} + +func TestDisk_io_counters(t *testing.T) { + ret, err := DiskIOCounters() + if err != nil || len(ret) == 0 { + t.Errorf("error %v", err) + } + empty := DiskIOCountersStat{} + for part, io := range ret { + if io == empty { + t.Errorf("io_counter error %v, %v", part, io) + } + } +} + +func TestDiskUsageStat_String(t *testing.T) { + v := DiskUsageStat{ + Path: "/", + Total: 1000, + Free: 2000, + Used: 3000, + UsedPercent: 50.1, + InodesTotal: 4000, + InodesUsed: 5000, + InodesFree: 6000, + InodesUsedPercent: 49.1, + } + e := `{"path":"/","total":1000,"free":2000,"used":3000,"usedPercent":50.1,"inodesTotal":4000,"inodesUsed":5000,"inodesFree":6000,"inodesUsedPercent":49.1}` + if e != fmt.Sprintf("%v", v) { + t.Errorf("DiskUsageStat string is invalid: %v", v) + } +} + +func TestDiskPartitionStat_String(t *testing.T) { + v := DiskPartitionStat{ + Device: "sd01", + Mountpoint: "/", + Fstype: "ext4", + Opts: "ro", + } + e := `{"device":"sd01","mountpoint":"/","fstype":"ext4","opts":"ro"}` + if e != fmt.Sprintf("%v", v) { + t.Errorf("DiskUsageStat string is invalid: %v", v) + } +} + +func TestDiskIOCountersStat_String(t *testing.T) { + v := DiskIOCountersStat{ + Name: "sd01", + ReadCount: 100, + WriteCount: 200, + ReadBytes: 300, + WriteBytes: 400, + SerialNumber: "SERIAL", + } + e := `{"readCount":100,"writeCount":200,"readBytes":300,"writeBytes":400,"readTime":0,"writeTime":0,"name":"sd01","ioTime":0,"serialNumber":"SERIAL"}` + if e != fmt.Sprintf("%v", v) { + t.Errorf("DiskUsageStat string is invalid: %v", v) + } +} diff --git a/disk/disk_unix.go b/disk/disk_unix.go new file mode 100644 index 0000000..036e4e0 --- /dev/null +++ b/disk/disk_unix.go @@ -0,0 +1,30 @@ +// +build freebsd linux darwin + +package gopsutil + +import "syscall" + +func DiskUsage(path string) (*DiskUsageStat, error) { + stat := syscall.Statfs_t{} + err := syscall.Statfs(path, &stat) + if err != nil { + return nil, err + } + + bsize := stat.Bsize + + ret := &DiskUsageStat{ + Path: path, + Total: (uint64(stat.Blocks) * uint64(bsize)), + Free: (uint64(stat.Bfree) * uint64(bsize)), + InodesTotal: (uint64(stat.Files)), + InodesFree: (uint64(stat.Ffree)), + } + + ret.InodesUsed = (ret.InodesTotal - ret.InodesFree) + ret.InodesUsedPercent = (float64(ret.InodesUsed) / float64(ret.InodesTotal)) * 100.0 + ret.Used = (ret.Total - ret.Free) + ret.UsedPercent = (float64(ret.Used) / float64(ret.Total)) * 100.0 + + return ret, nil +} diff --git a/disk/disk_windows.go b/disk/disk_windows.go new file mode 100644 index 0000000..cf8b2e2 --- /dev/null +++ b/disk/disk_windows.go @@ -0,0 +1,119 @@ +// +build windows + +package gopsutil + +import ( + "bytes" + "syscall" + "unsafe" + + common "github.com/shirou/gopsutil/common" +) + +var ( + procGetDiskFreeSpaceExW = modkernel32.NewProc("GetDiskFreeSpaceExW") + procGetLogicalDriveStringsW = modkernel32.NewProc("GetLogicalDriveStringsW") + procGetDriveType = modkernel32.NewProc("GetDriveTypeW") + provGetVolumeInformation = modkernel32.NewProc("GetVolumeInformationW") +) + +var ( + FileFileCompression = int64(16) // 0x00000010 + FileReadOnlyVolume = int64(524288) // 0x00080000 +) + +func DiskUsage(path string) (DiskUsageStat, error) { + ret := DiskUsageStat{} + + ret.Path = path + lpFreeBytesAvailable := int64(0) + lpTotalNumberOfBytes := int64(0) + lpTotalNumberOfFreeBytes := int64(0) + diskret, _, err := procGetDiskFreeSpaceExW.Call( + uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(path))), + uintptr(unsafe.Pointer(&lpFreeBytesAvailable)), + uintptr(unsafe.Pointer(&lpTotalNumberOfBytes)), + uintptr(unsafe.Pointer(&lpTotalNumberOfFreeBytes))) + if diskret == 0 { + return ret, err + } + ret.Total = uint64(lpTotalNumberOfBytes) + // ret.Free = uint64(lpFreeBytesAvailable) // python psutil does not use this + ret.Free = uint64(lpTotalNumberOfFreeBytes) + ret.Used = ret.Total - ret.Free + ret.UsedPercent = float64(ret.Used) / float64(ret.Total) * 100.0 + + //TODO: implement inodes stat + ret.InodesTotal = 0 + ret.InodesUsed = 0 + ret.InodesFree = 0 + ret.InodesUsedPercent = 0.0 + return ret, nil +} + +func DiskPartitions(all bool) ([]DiskPartitionStat, error) { + var ret []DiskPartitionStat + lpBuffer := make([]byte, 254) + diskret, _, err := procGetLogicalDriveStringsW.Call( + uintptr(len(lpBuffer)), + uintptr(unsafe.Pointer(&lpBuffer[0]))) + if diskret == 0 { + return ret, err + } + for _, v := range lpBuffer { + if v >= 65 && v <= 90 { + path := string(v) + ":" + if path == "A:" || path == "B:" { // skip floppy drives + continue + } + typepath, _ := syscall.UTF16PtrFromString(path) + typeret, _, _ := procGetDriveType.Call(uintptr(unsafe.Pointer(typepath))) + if typeret == 0 { + return ret, syscall.GetLastError() + } + // 2: DRIVE_REMOVABLE 3: DRIVE_FIXED 5: DRIVE_CDROM + + if typeret == 2 || typeret == 3 || typeret == 5 { + lpVolumeNameBuffer := make([]byte, 256) + lpVolumeSerialNumber := int64(0) + lpMaximumComponentLength := int64(0) + lpFileSystemFlags := int64(0) + lpFileSystemNameBuffer := make([]byte, 256) + volpath, _ := syscall.UTF16PtrFromString(string(v) + ":/") + driveret, _, err := provGetVolumeInformation.Call( + uintptr(unsafe.Pointer(volpath)), + uintptr(unsafe.Pointer(&lpVolumeNameBuffer[0])), + uintptr(len(lpVolumeNameBuffer)), + uintptr(unsafe.Pointer(&lpVolumeSerialNumber)), + uintptr(unsafe.Pointer(&lpMaximumComponentLength)), + uintptr(unsafe.Pointer(&lpFileSystemFlags)), + uintptr(unsafe.Pointer(&lpFileSystemNameBuffer[0])), + uintptr(len(lpFileSystemNameBuffer))) + if driveret == 0 { + return ret, err + } + opts := "rw" + if lpFileSystemFlags&FileReadOnlyVolume != 0 { + opts = "ro" + } + if lpFileSystemFlags&FileFileCompression != 0 { + opts += ".compress" + } + + d := DiskPartitionStat{ + Mountpoint: path, + Device: path, + Fstype: string(bytes.Replace(lpFileSystemNameBuffer, []byte("\x00"), []byte(""), -1)), + Opts: opts, + } + ret = append(ret, d) + } + } + } + return ret, nil +} + +func DiskIOCounters() (map[string]DiskIOCountersStat, error) { + ret := make(map[string]DiskIOCountersStat, 0) + return ret, common.NotImplementedError +} diff --git a/disk_darwin.go b/disk_darwin.go deleted file mode 100644 index 95bda72..0000000 --- a/disk_darwin.go +++ /dev/null @@ -1,12 +0,0 @@ -// +build darwin - -package gopsutil - -func DiskPartitions(all bool) ([]DiskPartitionStat, error) { - - return nil, NotImplementedError -} - -func DiskIOCounters() (map[string]DiskIOCountersStat, error) { - return nil, NotImplementedError -} diff --git a/disk_freebsd.go b/disk_freebsd.go deleted file mode 100644 index be80297..0000000 --- a/disk_freebsd.go +++ /dev/null @@ -1,130 +0,0 @@ -// +build freebsd - -package gopsutil - -import ( - "syscall" - "unsafe" -) - -func DiskPartitions(all bool) ([]DiskPartitionStat, error) { - var ret []DiskPartitionStat - - // get length - count, err := syscall.Getfsstat(nil, MntWait) - if err != nil { - return ret, err - } - - fs := make([]Statfs, count) - _, err = Getfsstat(fs, MntWait) - - for _, stat := range fs { - opts := "rw" - if stat.FFlags&MntReadOnly != 0 { - opts = "ro" - } - if stat.FFlags&MntSynchronous != 0 { - opts += ",sync" - } - if stat.FFlags&MntNoExec != 0 { - opts += ",noexec" - } - if stat.FFlags&MntNoSuid != 0 { - opts += ",nosuid" - } - if stat.FFlags&MntUnion != 0 { - opts += ",union" - } - if stat.FFlags&MntAsync != 0 { - opts += ",async" - } - if stat.FFlags&MntSuidDir != 0 { - opts += ",suiddir" - } - if stat.FFlags&MntSoftDep != 0 { - opts += ",softdep" - } - if stat.FFlags&MntNoSymFollow != 0 { - opts += ",nosymfollow" - } - if stat.FFlags&MntGEOMJournal != 0 { - opts += ",gjounalc" - } - if stat.FFlags&MntMultilabel != 0 { - opts += ",multilabel" - } - if stat.FFlags&MntACLs != 0 { - opts += ",acls" - } - if stat.FFlags&MntNoATime != 0 { - opts += ",noattime" - } - if stat.FFlags&MntClusterRead != 0 { - opts += ",nocluster" - } - if stat.FFlags&MntClusterWrite != 0 { - opts += ",noclusterw" - } - if stat.FFlags&MntNFS4ACLs != 0 { - opts += ",nfs4acls" - } - - d := DiskPartitionStat{ - Device: byteToString(stat.FMntfromname[:]), - Mountpoint: byteToString(stat.FMntonname[:]), - Fstype: byteToString(stat.FFstypename[:]), - Opts: opts, - } - ret = append(ret, d) - } - - return ret, nil -} - -func DiskIOCounters() (map[string]DiskIOCountersStat, error) { - return nil, NotImplementedError - - // statinfo->devinfo->devstat - // /usr/include/devinfo.h - - // get length - count, err := Getfsstat(nil, MntWait) - if err != nil { - return nil, err - } - - fs := make([]Statfs, count) - _, err = Getfsstat(fs, MntWait) - - ret := make(map[string]DiskIOCountersStat, 0) - for _, stat := range fs { - name := byteToString(stat.FMntonname[:]) - d := DiskIOCountersStat{ - Name: name, - ReadCount: stat.FSyncwrites + stat.FAsyncwrites, - WriteCount: stat.FSyncreads + stat.FAsyncreads, - } - - ret[name] = d - } - - return ret, nil -} - -// Getfsstat is borrowed from pkg/syscall/syscall_freebsd.go -// change Statfs_t to Statfs in order to get more information -func Getfsstat(buf []Statfs, flags int) (n int, err error) { - var _p0 unsafe.Pointer - var bufsize uintptr - if len(buf) > 0 { - _p0 = unsafe.Pointer(&buf[0]) - bufsize = unsafe.Sizeof(Statfs{}) * uintptr(len(buf)) - } - r0, _, e1 := syscall.Syscall(syscall.SYS_GETFSSTAT, uintptr(_p0), bufsize, uintptr(flags)) - n = int(r0) - if e1 != 0 { - err = e1 - } - return -} diff --git a/disk_freebsd_amd64.go b/disk_freebsd_amd64.go deleted file mode 100644 index b02aa33..0000000 --- a/disk_freebsd_amd64.go +++ /dev/null @@ -1,104 +0,0 @@ -// +build freebsd -// +build amd64 - -package gopsutil - -const ( - MntWait = 1 - MfsNameLen = 16 /* length of type name including null */ - MNameLen = 88 /* size of on/from name bufs */ -) - -// sys/mount.h -const ( - MntReadOnly = 0x00000001 /* read only filesystem */ - MntSynchronous = 0x00000002 /* filesystem written synchronously */ - MntNoExec = 0x00000004 /* can't exec from filesystem */ - MntNoSuid = 0x00000008 /* don't honor setuid bits on fs */ - MntUnion = 0x00000020 /* union with underlying filesystem */ - MntAsync = 0x00000040 /* filesystem written asynchronously */ - MntSuidDir = 0x00100000 /* special handling of SUID on dirs */ - MntSoftDep = 0x00200000 /* soft updates being done */ - MntNoSymFollow = 0x00400000 /* do not follow symlinks */ - MntGEOMJournal = 0x02000000 /* GEOM journal support enabled */ - MntMultilabel = 0x04000000 /* MAC support for individual objects */ - MntACLs = 0x08000000 /* ACL support enabled */ - MntNoATime = 0x10000000 /* disable update of file access time */ - MntClusterRead = 0x40000000 /* disable cluster read */ - MntClusterWrite = 0x80000000 /* disable cluster write */ - MntNFS4ACLs = 0x00000010 -) - -type Statfs struct { - FVersion uint32 /* structure version number */ - FType uint32 /* type of filesystem */ - FFlags uint64 /* copy of mount exported flags */ - FBsize uint64 /* filesystem fragment size */ - FIosize uint64 /* optimal transfer block size */ - FBlocks uint64 /* total data blocks in filesystem */ - FBfree uint64 /* free blocks in filesystem */ - FBavail int64 /* free blocks avail to non-superuser */ - FFiles uint64 /* total file nodes in filesystem */ - FFfree int64 /* free nodes avail to non-superuser */ - FSyncwrites uint64 /* count of sync writes since mount */ - FAsyncwrites uint64 /* count of async writes since mount */ - FSyncreads uint64 /* count of sync reads since mount */ - FAsyncreads uint64 /* count of async reads since mount */ - FSpare [10]uint64 /* unused spare */ - FNamemax uint32 /* maximum filename length */ - FOwner uint32 /* user that mounted the filesystem */ - FFsid int32 /* filesystem id */ - FCharspare [80]byte /* spare string space */ - FFstypename [MfsNameLen]byte /* filesystem type name */ - FMntfromname [MNameLen]byte /* mounted filesystem */ - FMntonname [MNameLen]byte /* directory on which mounted */ -} - -// /usr/include/devstat.h -// devstat_getdevs() -// kern.devstat.all -> devstats list struct - -// struct devinfo { -// struct devstat *devices; -// u_int8_t *mem_ptr; -// long generation; -// int numdevs; -// }; -// -// struct statinfo { -// long cp_time[CPUSTATES]; -// long tk_nin; -// long tk_nout; -// struct devinfo *dinfo; -// long double snap_time; -// }; - -// /usr/include/devinfo.h - -// struct devinfo_dev { -// devinfo_handle_t dd_handle; /* device handle */ -// devinfo_handle_t dd_parent; /* parent handle */ -// char *dd_name; /* name of device */ -// char *dd_desc; /* device description */ -// char *dd_drivername; /* name of attached driver */ -// char *dd_pnpinfo; /* pnp info from parent bus */ -// char *dd_location; /* Where bus thinks dev at */ -// uint32_t dd_devflags; /* API flags */ -// uint16_t dd_flags; /* internal dev flags */ -// device_state_t dd_state; /* attachment state of dev */ -// }; -// -// struct devinfo_rman { -// devinfo_handle_t dm_handle; /* resource manager handle */ -// u_long dm_start; /* resource start */ -// u_long dm_size; /* resource size */ -// char *dm_desc; /* resource description */ -// }; -// -// struct devinfo_res { -// devinfo_handle_t dr_handle; /* resource handle */ -// devinfo_handle_t dr_rman; /* resource manager handle */ -// devinfo_handle_t dr_device; /* owning device */ -// u_long dr_start; /* region start */ -// u_long dr_size; /* region size */ -// }; diff --git a/disk_linux.go b/disk_linux.go deleted file mode 100644 index 51e504b..0000000 --- a/disk_linux.go +++ /dev/null @@ -1,120 +0,0 @@ -// +build linux - -package gopsutil - -import ( - "fmt" - "os/exec" - "strconv" - "strings" -) - -const ( - SectorSize = 512 -) - -// Get disk partitions. -// should use setmntent(3) but this implement use /etc/mtab file -func DiskPartitions(all bool) ([]DiskPartitionStat, error) { - - filename := "/etc/mtab" - lines, err := readLines(filename) - if err != nil { - return nil, err - } - - ret := make([]DiskPartitionStat, 0, len(lines)) - - for _, line := range lines { - fields := strings.Fields(line) - d := DiskPartitionStat{ - Device: fields[0], - Mountpoint: fields[1], - Fstype: fields[2], - Opts: fields[3], - } - ret = append(ret, d) - } - - return ret, nil -} - -func DiskIOCounters() (map[string]DiskIOCountersStat, error) { - filename := "/proc/diskstats" - lines, err := readLines(filename) - if err != nil { - return nil, err - } - ret := make(map[string]DiskIOCountersStat, 0) - empty := DiskIOCountersStat{} - - for _, line := range lines { - fields := strings.Fields(line) - name := fields[2] - reads, err := strconv.ParseUint((fields[3]), 10, 64) - if err != nil { - return ret, err - } - rbytes, err := strconv.ParseUint((fields[5]), 10, 64) - if err != nil { - return ret, err - } - rtime, err := strconv.ParseUint((fields[6]), 10, 64) - if err != nil { - return ret, err - } - writes, err := strconv.ParseUint((fields[7]), 10, 64) - if err != nil { - return ret, err - } - wbytes, err := strconv.ParseUint((fields[9]), 10, 64) - if err != nil { - return ret, err - } - wtime, err := strconv.ParseUint((fields[10]), 10, 64) - if err != nil { - return ret, err - } - iotime, err := strconv.ParseUint((fields[12]), 10, 64) - if err != nil { - return ret, err - } - d := DiskIOCountersStat{ - ReadBytes: rbytes * SectorSize, - WriteBytes: wbytes * SectorSize, - ReadCount: reads, - WriteCount: writes, - ReadTime: rtime, - WriteTime: wtime, - IoTime: iotime, - } - if d == empty { - continue - } - d.Name = name - - d.SerialNumber = GetDiskSerialNumber(name) - ret[name] = d - } - return ret, nil -} - -func GetDiskSerialNumber(name string) string { - n := fmt.Sprintf("--name=%s", name) - out, err := exec.Command("/sbin/udevadm", "info", "--query=property", n).Output() - - // does not return error, just an empty string - if err != nil { - return "" - } - lines := strings.Split(string(out), "\n") - for _, line := range lines { - values := strings.Split(line, "=") - if len(values) < 2 || values[0] != "ID_SERIAL" { - // only get ID_SERIAL, not ID_SERIAL_SHORT - continue - } - return values[1] - } - return "" -} diff --git a/disk_test.go b/disk_test.go deleted file mode 100644 index 0f20030..0000000 --- a/disk_test.go +++ /dev/null @@ -1,93 +0,0 @@ -package gopsutil - -import ( - "fmt" - "runtime" - "testing" -) - -func TestDisk_usage(t *testing.T) { - path := "/" - if runtime.GOOS == "windows" { - path = "C:" - } - v, err := DiskUsage(path) - if err != nil { - t.Errorf("error %v", err) - } - if v.Path != path { - t.Errorf("error %v", err) - } -} - -func TestDisk_partitions(t *testing.T) { - ret, err := DiskPartitions(false) - if err != nil || len(ret) == 0 { - t.Errorf("error %v", err) - } - empty := DiskPartitionStat{} - for _, disk := range ret { - if disk == empty { - t.Errorf("Could not get device info %v", disk) - } - } -} - -func TestDisk_io_counters(t *testing.T) { - ret, err := DiskIOCounters() - if err != nil || len(ret) == 0 { - t.Errorf("error %v", err) - } - empty := DiskIOCountersStat{} - for part, io := range ret { - if io == empty { - t.Errorf("io_counter error %v, %v", part, io) - } - } -} - -func TestDiskUsageStat_String(t *testing.T) { - v := DiskUsageStat{ - Path: "/", - Total: 1000, - Free: 2000, - Used: 3000, - UsedPercent: 50.1, - InodesTotal: 4000, - InodesUsed: 5000, - InodesFree: 6000, - InodesUsedPercent: 49.1, - } - e := `{"path":"/","total":1000,"free":2000,"used":3000,"usedPercent":50.1,"inodesTotal":4000,"inodesUsed":5000,"inodesFree":6000,"inodesUsedPercent":49.1}` - if e != fmt.Sprintf("%v", v) { - t.Errorf("DiskUsageStat string is invalid: %v", v) - } -} - -func TestDiskPartitionStat_String(t *testing.T) { - v := DiskPartitionStat{ - Device: "sd01", - Mountpoint: "/", - Fstype: "ext4", - Opts: "ro", - } - e := `{"device":"sd01","mountpoint":"/","fstype":"ext4","opts":"ro"}` - if e != fmt.Sprintf("%v", v) { - t.Errorf("DiskUsageStat string is invalid: %v", v) - } -} - -func TestDiskIOCountersStat_String(t *testing.T) { - v := DiskIOCountersStat{ - Name: "sd01", - ReadCount: 100, - WriteCount: 200, - ReadBytes: 300, - WriteBytes: 400, - SerialNumber: "SERIAL", - } - e := `{"readCount":100,"writeCount":200,"readBytes":300,"writeBytes":400,"readTime":0,"writeTime":0,"name":"sd01","ioTime":0,"serialNumber":"SERIAL"}` - if e != fmt.Sprintf("%v", v) { - t.Errorf("DiskUsageStat string is invalid: %v", v) - } -} diff --git a/disk_unix.go b/disk_unix.go deleted file mode 100644 index 036e4e0..0000000 --- a/disk_unix.go +++ /dev/null @@ -1,30 +0,0 @@ -// +build freebsd linux darwin - -package gopsutil - -import "syscall" - -func DiskUsage(path string) (*DiskUsageStat, error) { - stat := syscall.Statfs_t{} - err := syscall.Statfs(path, &stat) - if err != nil { - return nil, err - } - - bsize := stat.Bsize - - ret := &DiskUsageStat{ - Path: path, - Total: (uint64(stat.Blocks) * uint64(bsize)), - Free: (uint64(stat.Bfree) * uint64(bsize)), - InodesTotal: (uint64(stat.Files)), - InodesFree: (uint64(stat.Ffree)), - } - - ret.InodesUsed = (ret.InodesTotal - ret.InodesFree) - ret.InodesUsedPercent = (float64(ret.InodesUsed) / float64(ret.InodesTotal)) * 100.0 - ret.Used = (ret.Total - ret.Free) - ret.UsedPercent = (float64(ret.Used) / float64(ret.Total)) * 100.0 - - return ret, nil -} diff --git a/disk_windows.go b/disk_windows.go deleted file mode 100644 index e8bf318..0000000 --- a/disk_windows.go +++ /dev/null @@ -1,117 +0,0 @@ -// +build windows - -package gopsutil - -import ( - "bytes" - "syscall" - "unsafe" -) - -var ( - procGetDiskFreeSpaceExW = modkernel32.NewProc("GetDiskFreeSpaceExW") - procGetLogicalDriveStringsW = modkernel32.NewProc("GetLogicalDriveStringsW") - procGetDriveType = modkernel32.NewProc("GetDriveTypeW") - provGetVolumeInformation = modkernel32.NewProc("GetVolumeInformationW") -) - -var ( - FileFileCompression = int64(16) // 0x00000010 - FileReadOnlyVolume = int64(524288) // 0x00080000 -) - -func DiskUsage(path string) (DiskUsageStat, error) { - ret := DiskUsageStat{} - - ret.Path = path - lpFreeBytesAvailable := int64(0) - lpTotalNumberOfBytes := int64(0) - lpTotalNumberOfFreeBytes := int64(0) - diskret, _, err := procGetDiskFreeSpaceExW.Call( - uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(path))), - uintptr(unsafe.Pointer(&lpFreeBytesAvailable)), - uintptr(unsafe.Pointer(&lpTotalNumberOfBytes)), - uintptr(unsafe.Pointer(&lpTotalNumberOfFreeBytes))) - if diskret == 0 { - return ret, err - } - ret.Total = uint64(lpTotalNumberOfBytes) - // ret.Free = uint64(lpFreeBytesAvailable) // python psutil does not use this - ret.Free = uint64(lpTotalNumberOfFreeBytes) - ret.Used = ret.Total - ret.Free - ret.UsedPercent = float64(ret.Used) / float64(ret.Total) * 100.0 - - //TODO: implement inodes stat - ret.InodesTotal = 0 - ret.InodesUsed = 0 - ret.InodesFree = 0 - ret.InodesUsedPercent = 0.0 - return ret, nil -} - -func DiskPartitions(all bool) ([]DiskPartitionStat, error) { - var ret []DiskPartitionStat - lpBuffer := make([]byte, 254) - diskret, _, err := procGetLogicalDriveStringsW.Call( - uintptr(len(lpBuffer)), - uintptr(unsafe.Pointer(&lpBuffer[0]))) - if diskret == 0 { - return ret, err - } - for _, v := range lpBuffer { - if v >= 65 && v <= 90 { - path := string(v) + ":" - if path == "A:" || path == "B:" { // skip floppy drives - continue - } - typepath, _ := syscall.UTF16PtrFromString(path) - typeret, _, _ := procGetDriveType.Call(uintptr(unsafe.Pointer(typepath))) - if typeret == 0 { - return ret, syscall.GetLastError() - } - // 2: DRIVE_REMOVABLE 3: DRIVE_FIXED 5: DRIVE_CDROM - - if typeret == 2 || typeret == 3 || typeret == 5 { - lpVolumeNameBuffer := make([]byte, 256) - lpVolumeSerialNumber := int64(0) - lpMaximumComponentLength := int64(0) - lpFileSystemFlags := int64(0) - lpFileSystemNameBuffer := make([]byte, 256) - volpath, _ := syscall.UTF16PtrFromString(string(v) + ":/") - driveret, _, err := provGetVolumeInformation.Call( - uintptr(unsafe.Pointer(volpath)), - uintptr(unsafe.Pointer(&lpVolumeNameBuffer[0])), - uintptr(len(lpVolumeNameBuffer)), - uintptr(unsafe.Pointer(&lpVolumeSerialNumber)), - uintptr(unsafe.Pointer(&lpMaximumComponentLength)), - uintptr(unsafe.Pointer(&lpFileSystemFlags)), - uintptr(unsafe.Pointer(&lpFileSystemNameBuffer[0])), - uintptr(len(lpFileSystemNameBuffer))) - if driveret == 0 { - return ret, err - } - opts := "rw" - if lpFileSystemFlags&FileReadOnlyVolume != 0 { - opts = "ro" - } - if lpFileSystemFlags&FileFileCompression != 0 { - opts += ".compress" - } - - d := DiskPartitionStat{ - Mountpoint: path, - Device: path, - Fstype: string(bytes.Replace(lpFileSystemNameBuffer, []byte("\x00"), []byte(""), -1)), - Opts: opts, - } - ret = append(ret, d) - } - } - } - return ret, nil -} - -func DiskIOCounters() (map[string]DiskIOCountersStat, error) { - ret := make(map[string]DiskIOCountersStat, 0) - return ret, NotImplementedError -} diff --git a/docker/docker_linux.go b/docker/docker_linux.go new file mode 100644 index 0000000..5320c31 --- /dev/null +++ b/docker/docker_linux.go @@ -0,0 +1,184 @@ +// +build linux + +package gopsutil + +import ( + "encoding/json" + "os/exec" + "path" + "strconv" + "strings" +) + +type CgroupMemStat struct { + ContainerID string `json:"containerid"` + Cache uint64 `json:"cache"` + RSS uint64 `json:"rss"` + RSSHuge uint64 `json:"rss_huge"` + MappedFile uint64 `json:"mapped_file"` + Pgpgin uint64 `json:"pgpgin"` + Pgpgout uint64 `json:"pgpgout"` + Pgfault uint64 `json:"pgfault"` + Pgmajfault uint64 `json:"pgmajfault"` + InactiveAnon uint64 `json:"inactive_anon"` + ActiveAnon uint64 `json:"active_anon"` + InctiveFile uint64 `json:"inactive_file"` + ActiveFile uint64 `json:"active_file"` + Unevictable uint64 `json:"unevictable"` + HierarchicalMemoryLimit uint64 `json:"hierarchical_memory_limit"` + TotalCache uint64 `json:"total_cache"` + TotalRSS uint64 `json:"total_rss"` + TotalRSSHuge uint64 `json:"total_rss_huge"` + TotalMappedFile uint64 `json:"total_mapped_file"` + TotalPgpgIn uint64 `json:"total_pgpgin"` + TotalPgpgOut uint64 `json:"total_pgpgout"` + TotalPgFault uint64 `json:"total_pgfault"` + TotalPgMajFault uint64 `json:"total_pgmajfault"` + TotalInactiveAnon uint64 `json:"total_inactive_anon"` + TotalActiveAnon uint64 `json:"total_active_anon"` + TotalInactiveFile uint64 `json:"total_inactive_file"` + TotalActiveFile uint64 `json:"total_active_file"` + TotalUnevictable uint64 `json:"total_unevictable"` +} + +// GetDockerIDList returnes a list of DockerID. +// This requires certain permission. +func GetDockerIDList() ([]string, error) { + out, err := exec.Command("docker", "ps", "-q", "--no-trunc").Output() + if err != nil { + return []string{}, err + } + lines := strings.Split(string(out), "\n") + ret := make([]string, 0, len(lines)) + + for _, l := range lines { + ret = append(ret, l) + } + + return ret, nil +} + +// CgroupCPU returnes specified cgroup id CPU status. +// containerid is same as docker id if you use docker. +// If you use container via systemd.slice, you could use +// containerid = docker-.scope and base=/sys/fs/cgroup/cpuacct/system.slice/ +func CgroupCPU(containerid string, base string) (*CPUTimesStat, error) { + if len(base) == 0 { + base = "/sys/fs/cgroup/cpuacct/docker" + } + path := path.Join(base, containerid, "cpuacct.stat") + + lines, _ := readLines(path) + // empty containerid means all cgroup + if len(containerid) == 0 { + containerid = "all" + } + ret := &CPUTimesStat{CPU: containerid} + for _, line := range lines { + fields := strings.Split(line, " ") + if fields[0] == "user" { + user, err := strconv.ParseFloat(fields[1], 32) + if err == nil { + ret.User = float32(user) + } + } + if fields[0] == "system" { + system, err := strconv.ParseFloat(fields[1], 32) + if err == nil { + ret.System = float32(system) + } + } + } + + return ret, nil +} + +func CgroupCPUDocker(containerid string) (*CPUTimesStat, error) { + return CgroupCPU(containerid, "/sys/fs/cgroup/cpuacct/docker") +} + +func CgroupMem(containerid string, base string) (*CgroupMemStat, error) { + if len(base) == 0 { + base = "/sys/fs/cgroup/memory/docker" + } + path := path.Join(base, containerid, "memory.stat") + // empty containerid means all cgroup + if len(containerid) == 0 { + containerid = "all" + } + lines, _ := readLines(path) + ret := &CgroupMemStat{ContainerID: containerid} + for _, line := range lines { + fields := strings.Split(line, " ") + v, err := strconv.ParseUint(fields[1], 10, 64) + if err != nil { + continue + } + switch fields[0] { + case "cache": + ret.Cache = v + case "rss": + ret.RSS = v + case "rss_huge": + ret.RSSHuge = v + case "mapped_file": + ret.MappedFile = v + case "pgpgin": + ret.Pgpgin = v + case "pgpgout": + ret.Pgpgout = v + case "pgfault": + ret.Pgfault = v + case "pgmajfault": + ret.Pgmajfault = v + case "inactive_anon": + ret.InactiveAnon = v + case "active_anon": + ret.ActiveAnon = v + case "inactive_file": + ret.InctiveFile = v + case "active_file": + ret.ActiveFile = v + case "unevictable": + ret.Unevictable = v + case "hierarchical_memory_limit": + ret.HierarchicalMemoryLimit = v + case "total_cache": + ret.TotalCache = v + case "total_rss": + ret.TotalRSS = v + case "total_rss_huge": + ret.TotalRSSHuge = v + case "total_mapped_file": + ret.TotalMappedFile = v + case "total_pgpgin": + ret.TotalPgpgIn = v + case "total_pgpgout": + ret.TotalPgpgOut = v + case "total_pgfault": + ret.TotalPgFault = v + case "total_pgmajfault": + ret.TotalPgMajFault = v + case "total_inactive_anon": + ret.TotalInactiveAnon = v + case "total_active_anon": + ret.TotalActiveAnon = v + case "total_inactive_file": + ret.TotalInactiveFile = v + case "total_active_file": + ret.TotalActiveFile = v + case "total_unevictable": + ret.TotalUnevictable = v + } + } + return ret, nil +} + +func CgroupMemDocker(containerid string) (*CgroupMemStat, error) { + return CgroupMem(containerid, "/sys/fs/cgroup/memory/docker") +} + +func (m CgroupMemStat) String() string { + s, _ := json.Marshal(m) + return string(s) +} diff --git a/docker/docker_linux_test.go b/docker/docker_linux_test.go new file mode 100644 index 0000000..285861e --- /dev/null +++ b/docker/docker_linux_test.go @@ -0,0 +1,46 @@ +// +build linux + +package gopsutil + +import ( + "testing" +) + +func TestGetDockerIDList(t *testing.T) { + // If there is not docker environment, this test always fail. + // not tested here + /* + _, err := GetDockerIDList() + if err != nil { + t.Errorf("error %v", err) + } + */ +} + +func TestCgroupCPU(t *testing.T) { + v, _ := GetDockerIDList() + for _, id := range v { + v, err := CgroupCPUDocker(id) + if err != nil { + t.Errorf("error %v", err) + } + if v.CPU == "" { + t.Errorf("could not get CgroupCPU %v", v) + } + + } +} + +func TestCgroupMem(t *testing.T) { + v, _ := GetDockerIDList() + for _, id := range v { + v, err := CgroupMemDocker(id) + if err != nil { + t.Errorf("error %v", err) + } + empty := &CgroupMemStat{} + if v == empty { + t.Errorf("Could not CgroupMemStat %v", v) + } + } +} diff --git a/docker_linux.go b/docker_linux.go deleted file mode 100644 index 5320c31..0000000 --- a/docker_linux.go +++ /dev/null @@ -1,184 +0,0 @@ -// +build linux - -package gopsutil - -import ( - "encoding/json" - "os/exec" - "path" - "strconv" - "strings" -) - -type CgroupMemStat struct { - ContainerID string `json:"containerid"` - Cache uint64 `json:"cache"` - RSS uint64 `json:"rss"` - RSSHuge uint64 `json:"rss_huge"` - MappedFile uint64 `json:"mapped_file"` - Pgpgin uint64 `json:"pgpgin"` - Pgpgout uint64 `json:"pgpgout"` - Pgfault uint64 `json:"pgfault"` - Pgmajfault uint64 `json:"pgmajfault"` - InactiveAnon uint64 `json:"inactive_anon"` - ActiveAnon uint64 `json:"active_anon"` - InctiveFile uint64 `json:"inactive_file"` - ActiveFile uint64 `json:"active_file"` - Unevictable uint64 `json:"unevictable"` - HierarchicalMemoryLimit uint64 `json:"hierarchical_memory_limit"` - TotalCache uint64 `json:"total_cache"` - TotalRSS uint64 `json:"total_rss"` - TotalRSSHuge uint64 `json:"total_rss_huge"` - TotalMappedFile uint64 `json:"total_mapped_file"` - TotalPgpgIn uint64 `json:"total_pgpgin"` - TotalPgpgOut uint64 `json:"total_pgpgout"` - TotalPgFault uint64 `json:"total_pgfault"` - TotalPgMajFault uint64 `json:"total_pgmajfault"` - TotalInactiveAnon uint64 `json:"total_inactive_anon"` - TotalActiveAnon uint64 `json:"total_active_anon"` - TotalInactiveFile uint64 `json:"total_inactive_file"` - TotalActiveFile uint64 `json:"total_active_file"` - TotalUnevictable uint64 `json:"total_unevictable"` -} - -// GetDockerIDList returnes a list of DockerID. -// This requires certain permission. -func GetDockerIDList() ([]string, error) { - out, err := exec.Command("docker", "ps", "-q", "--no-trunc").Output() - if err != nil { - return []string{}, err - } - lines := strings.Split(string(out), "\n") - ret := make([]string, 0, len(lines)) - - for _, l := range lines { - ret = append(ret, l) - } - - return ret, nil -} - -// CgroupCPU returnes specified cgroup id CPU status. -// containerid is same as docker id if you use docker. -// If you use container via systemd.slice, you could use -// containerid = docker-.scope and base=/sys/fs/cgroup/cpuacct/system.slice/ -func CgroupCPU(containerid string, base string) (*CPUTimesStat, error) { - if len(base) == 0 { - base = "/sys/fs/cgroup/cpuacct/docker" - } - path := path.Join(base, containerid, "cpuacct.stat") - - lines, _ := readLines(path) - // empty containerid means all cgroup - if len(containerid) == 0 { - containerid = "all" - } - ret := &CPUTimesStat{CPU: containerid} - for _, line := range lines { - fields := strings.Split(line, " ") - if fields[0] == "user" { - user, err := strconv.ParseFloat(fields[1], 32) - if err == nil { - ret.User = float32(user) - } - } - if fields[0] == "system" { - system, err := strconv.ParseFloat(fields[1], 32) - if err == nil { - ret.System = float32(system) - } - } - } - - return ret, nil -} - -func CgroupCPUDocker(containerid string) (*CPUTimesStat, error) { - return CgroupCPU(containerid, "/sys/fs/cgroup/cpuacct/docker") -} - -func CgroupMem(containerid string, base string) (*CgroupMemStat, error) { - if len(base) == 0 { - base = "/sys/fs/cgroup/memory/docker" - } - path := path.Join(base, containerid, "memory.stat") - // empty containerid means all cgroup - if len(containerid) == 0 { - containerid = "all" - } - lines, _ := readLines(path) - ret := &CgroupMemStat{ContainerID: containerid} - for _, line := range lines { - fields := strings.Split(line, " ") - v, err := strconv.ParseUint(fields[1], 10, 64) - if err != nil { - continue - } - switch fields[0] { - case "cache": - ret.Cache = v - case "rss": - ret.RSS = v - case "rss_huge": - ret.RSSHuge = v - case "mapped_file": - ret.MappedFile = v - case "pgpgin": - ret.Pgpgin = v - case "pgpgout": - ret.Pgpgout = v - case "pgfault": - ret.Pgfault = v - case "pgmajfault": - ret.Pgmajfault = v - case "inactive_anon": - ret.InactiveAnon = v - case "active_anon": - ret.ActiveAnon = v - case "inactive_file": - ret.InctiveFile = v - case "active_file": - ret.ActiveFile = v - case "unevictable": - ret.Unevictable = v - case "hierarchical_memory_limit": - ret.HierarchicalMemoryLimit = v - case "total_cache": - ret.TotalCache = v - case "total_rss": - ret.TotalRSS = v - case "total_rss_huge": - ret.TotalRSSHuge = v - case "total_mapped_file": - ret.TotalMappedFile = v - case "total_pgpgin": - ret.TotalPgpgIn = v - case "total_pgpgout": - ret.TotalPgpgOut = v - case "total_pgfault": - ret.TotalPgFault = v - case "total_pgmajfault": - ret.TotalPgMajFault = v - case "total_inactive_anon": - ret.TotalInactiveAnon = v - case "total_active_anon": - ret.TotalActiveAnon = v - case "total_inactive_file": - ret.TotalInactiveFile = v - case "total_active_file": - ret.TotalActiveFile = v - case "total_unevictable": - ret.TotalUnevictable = v - } - } - return ret, nil -} - -func CgroupMemDocker(containerid string) (*CgroupMemStat, error) { - return CgroupMem(containerid, "/sys/fs/cgroup/memory/docker") -} - -func (m CgroupMemStat) String() string { - s, _ := json.Marshal(m) - return string(s) -} diff --git a/docker_linux_test.go b/docker_linux_test.go deleted file mode 100644 index 285861e..0000000 --- a/docker_linux_test.go +++ /dev/null @@ -1,46 +0,0 @@ -// +build linux - -package gopsutil - -import ( - "testing" -) - -func TestGetDockerIDList(t *testing.T) { - // If there is not docker environment, this test always fail. - // not tested here - /* - _, err := GetDockerIDList() - if err != nil { - t.Errorf("error %v", err) - } - */ -} - -func TestCgroupCPU(t *testing.T) { - v, _ := GetDockerIDList() - for _, id := range v { - v, err := CgroupCPUDocker(id) - if err != nil { - t.Errorf("error %v", err) - } - if v.CPU == "" { - t.Errorf("could not get CgroupCPU %v", v) - } - - } -} - -func TestCgroupMem(t *testing.T) { - v, _ := GetDockerIDList() - for _, id := range v { - v, err := CgroupMemDocker(id) - if err != nil { - t.Errorf("error %v", err) - } - empty := &CgroupMemStat{} - if v == empty { - t.Errorf("Could not CgroupMemStat %v", v) - } - } -} diff --git a/host.go b/host.go deleted file mode 100644 index a204231..0000000 --- a/host.go +++ /dev/null @@ -1,37 +0,0 @@ -package gopsutil - -import ( - "encoding/json" -) - -// A HostInfoStat describes the host status. -// This is not in the psutil but it useful. -type HostInfoStat struct { - Hostname string `json:"hostname"` - Uptime uint64 `json:"uptime"` - Procs uint64 `json:"procs"` // number of processes - OS string `json:"os"` // ex: freebsd, linux - Platform string `json:"platform"` // ex: ubuntu, linuxmint - PlatformFamily string `json:"platformFamily"` // ex: debian, rhel - PlatformVersion string `json:"platformVersion"` - VirtualizationSystem string `json:"virtualizationSystem"` - VirtualizationRole string `json:"virtualizationRole"` // guest or host - -} - -type UserStat struct { - User string `json:"user"` - Terminal string `json:"terminal"` - Host string `json:"host"` - Started int `json:"started"` -} - -func (h HostInfoStat) String() string { - s, _ := json.Marshal(h) - return string(s) -} - -func (u UserStat) String() string { - s, _ := json.Marshal(u) - return string(s) -} diff --git a/host/host.go b/host/host.go new file mode 100644 index 0000000..a204231 --- /dev/null +++ b/host/host.go @@ -0,0 +1,37 @@ +package gopsutil + +import ( + "encoding/json" +) + +// A HostInfoStat describes the host status. +// This is not in the psutil but it useful. +type HostInfoStat struct { + Hostname string `json:"hostname"` + Uptime uint64 `json:"uptime"` + Procs uint64 `json:"procs"` // number of processes + OS string `json:"os"` // ex: freebsd, linux + Platform string `json:"platform"` // ex: ubuntu, linuxmint + PlatformFamily string `json:"platformFamily"` // ex: debian, rhel + PlatformVersion string `json:"platformVersion"` + VirtualizationSystem string `json:"virtualizationSystem"` + VirtualizationRole string `json:"virtualizationRole"` // guest or host + +} + +type UserStat struct { + User string `json:"user"` + Terminal string `json:"terminal"` + Host string `json:"host"` + Started int `json:"started"` +} + +func (h HostInfoStat) String() string { + s, _ := json.Marshal(h) + return string(s) +} + +func (u UserStat) String() string { + s, _ := json.Marshal(u) + return string(s) +} diff --git a/host/host_darwin.go b/host/host_darwin.go new file mode 100644 index 0000000..ff9866e --- /dev/null +++ b/host/host_darwin.go @@ -0,0 +1,154 @@ +// +build darwin + +package gopsutil + +import ( + "bytes" + "encoding/binary" + "io/ioutil" + "os" + "os/exec" + "runtime" + "strconv" + "strings" + "unsafe" + + common "github.com/shirou/gopsutil/common" +) + +const ( + UTXUserSize = 256 /* include/NetBSD/utmpx.h */ + UTXIDSize = 4 + UTXLineSize = 32 + UTXHostSize = 256 +) + +type utmpx32 struct { + UtUser [UTXUserSize]byte /* login name */ + UtID [UTXIDSize]byte /* id */ + UtLine [UTXLineSize]byte /* tty name */ + //TODO UtPid pid_t /* process id creating the entry */ + UtType [4]byte /* type of this entry */ + //TODO UtTv timeval32 /* time entry was created */ + UtHost [UTXHostSize]byte /* host name */ + UtPad [16]byte /* reserved for future use */ +} + +func HostInfo() (*HostInfoStat, error) { + ret := &HostInfoStat{ + OS: runtime.GOOS, + PlatformFamily: "darwin", + } + + hostname, err := os.Hostname() + if err != nil { + return ret, err + } + ret.Hostname = hostname + + platform, family, version, err := GetPlatformInformation() + if err == nil { + ret.Platform = platform + ret.PlatformFamily = family + ret.PlatformVersion = version + } + system, role, err := GetVirtualization() + if err == nil { + ret.VirtualizationSystem = system + ret.VirtualizationRole = role + } + + values, err := common.DoSysctrl("kern.boottime") + if err == nil { + // ex: { sec = 1392261637, usec = 627534 } Thu Feb 13 12:20:37 2014 + v := strings.Replace(values[2], ",", "", 1) + t, err := strconv.ParseUint(v, 10, 64) + if err != nil { + return ret, err + } + ret.Uptime = t + } + + return ret, nil +} + +func BootTime() (int64, error) { + values, err := common.DoSysctrl("kern.boottime") + if err != nil { + return 0, err + } + // ex: { sec = 1392261637, usec = 627534 } Thu Feb 13 12:20:37 2014 + v := strings.Replace(values[2], ",", "", 1) + + boottime, err := strconv.ParseInt(v, 10, 64) + if err != nil { + return 0, err + } + + return boottime, nil +} + +func Users() ([]UserStat, error) { + utmpfile := "/var/run/utmpx" + var ret []UserStat + + file, err := os.Open(utmpfile) + if err != nil { + return ret, err + } + + buf, err := ioutil.ReadAll(file) + if err != nil { + return ret, err + } + + u := utmpx32{} + entrySize := int(unsafe.Sizeof(u)) + count := len(buf) / entrySize + + for i := 0; i < count; i++ { + b := buf[i*entrySize : i*entrySize+entrySize] + + var u utmpx32 + br := bytes.NewReader(b) + err := binary.Read(br, binary.LittleEndian, &u) + if err != nil { + continue + } + user := UserStat{ + User: common.ByteToString(u.UtUser[:]), + // Terminal: ByteToString(u.UtLine[:]), + Host: common.ByteToString(u.UtHost[:]), + // Started: int(u.UtTime), + } + ret = append(ret, user) + } + + return ret, nil + +} + +func GetPlatformInformation() (string, string, string, error) { + platform := "" + family := "" + version := "" + + out, err := exec.Command("uname", "-s").Output() + if err == nil { + platform = strings.ToLower(strings.TrimSpace(string(out))) + } + + out, err = exec.Command("uname", "-r").Output() + if err == nil { + version = strings.ToLower(strings.TrimSpace(string(out))) + } + + return platform, family, version, nil +} + +func GetVirtualization() (string, string, error) { + system := "" + role := "" + + return system, role, nil +} diff --git a/host/host_freebsd.go b/host/host_freebsd.go new file mode 100644 index 0000000..6c3697d --- /dev/null +++ b/host/host_freebsd.go @@ -0,0 +1,136 @@ +// +build freebsd + +package gopsutil + +import ( + "bytes" + "encoding/binary" + "io/ioutil" + "os" + "os/exec" + "runtime" + "strconv" + "strings" + "unsafe" + + common "github.com/shirou/gopsutil/common" +) + +func HostInfo() (*HostInfoStat, error) { + ret := &HostInfoStat{ + OS: runtime.GOOS, + PlatformFamily: "freebsd", + } + + hostname, err := os.Hostname() + if err != nil { + return ret, err + } + ret.Hostname = hostname + + platform, family, version, err := GetPlatformInformation() + if err == nil { + ret.Platform = platform + ret.PlatformFamily = family + ret.PlatformVersion = version + } + system, role, err := GetVirtualization() + if err == nil { + ret.VirtualizationSystem = system + ret.VirtualizationRole = role + } + + values, err := common.DoSysctrl("kern.boottime") + if err == nil { + // ex: { sec = 1392261637, usec = 627534 } Thu Feb 13 12:20:37 2014 + v := strings.Replace(values[2], ",", "", 1) + t, err := strconv.ParseUint(v, 10, 64) + if err != nil { + return ret, err + } + ret.Uptime = t + } + + return ret, nil +} + +func BootTime() (int64, error) { + values, err := common.DoSysctrl("kern.boottime") + if err != nil { + return 0, err + } + // ex: { sec = 1392261637, usec = 627534 } Thu Feb 13 12:20:37 2014 + v := strings.Replace(values[2], ",", "", 1) + + boottime, err := strconv.ParseInt(v, 10, 64) + if err != nil { + return 0, err + } + + return boottime, nil +} + +func Users() ([]UserStat, error) { + utmpfile := "/var/run/utmp" + var ret []UserStat + + file, err := os.Open(utmpfile) + if err != nil { + return ret, err + } + + buf, err := ioutil.ReadAll(file) + if err != nil { + return ret, err + } + + u := utmp{} + entrySize := int(unsafe.Sizeof(u)) + count := len(buf) / entrySize + + for i := 0; i < count; i++ { + b := buf[i*entrySize : i*entrySize+entrySize] + + var u utmp + br := bytes.NewReader(b) + err := binary.Read(br, binary.LittleEndian, &u) + if err != nil { + continue + } + user := UserStat{ + User: common.ByteToString(u.UtName[:]), + Terminal: common.ByteToString(u.UtLine[:]), + Host: common.ByteToString(u.UtHost[:]), + Started: int(u.UtTime), + } + ret = append(ret, user) + } + + return ret, nil + +} + +func GetPlatformInformation() (string, string, string, error) { + platform := "" + family := "" + version := "" + + out, err := exec.Command("uname", "-s").Output() + if err == nil { + platform = strings.ToLower(strings.TrimSpace(string(out))) + } + + out, err = exec.Command("uname", "-r").Output() + if err == nil { + version = strings.ToLower(strings.TrimSpace(string(out))) + } + + return platform, family, version, nil +} + +func GetVirtualization() (string, string, error) { + system := "" + role := "" + + return system, role, nil +} diff --git a/host/host_freebsd_amd64.go b/host/host_freebsd_amd64.go new file mode 100644 index 0000000..efd1ade --- /dev/null +++ b/host/host_freebsd_amd64.go @@ -0,0 +1,17 @@ +// +build freebsd +// +build amd64 + +package gopsutil + +const ( + UTNameSize = 16 /* see MAXLOGNAME in */ + UTLineSize = 8 + UTHostSize = 16 +) + +type utmp struct { + UtLine [UTLineSize]byte + UtName [UTNameSize]byte + UtHost [UTHostSize]byte + UtTime int32 +} diff --git a/host/host_linux.go b/host/host_linux.go new file mode 100644 index 0000000..d5a5936 --- /dev/null +++ b/host/host_linux.go @@ -0,0 +1,362 @@ +// +build linux + +package gopsutil + +import ( + "bytes" + "encoding/binary" + "io/ioutil" + "os" + "os/exec" + "regexp" + "runtime" + "strings" + "syscall" + "unsafe" + + common "github.com/shirou/gopsutil/common" +) + +type LSB struct { + ID string + Release string + Codename string + Description string +} + +func HostInfo() (*HostInfoStat, error) { + hostname, err := os.Hostname() + if err != nil { + return nil, err + } + + ret := &HostInfoStat{ + Hostname: hostname, + OS: runtime.GOOS, + } + + platform, family, version, err := GetPlatformInformation() + if err == nil { + ret.Platform = platform + ret.PlatformFamily = family + ret.PlatformVersion = version + } + system, role, err := GetVirtualization() + if err == nil { + ret.VirtualizationSystem = system + ret.VirtualizationRole = role + } + uptime, err := BootTime() + if err == nil { + ret.Uptime = uptime + } + + return ret, nil +} + +func BootTime() (uint64, error) { + sysinfo := &syscall.Sysinfo_t{} + if err := syscall.Sysinfo(sysinfo); err != nil { + return 0, err + } + return uint64(sysinfo.Uptime), nil +} + +func Users() ([]UserStat, error) { + utmpfile := "/var/run/utmp" + + file, err := os.Open(utmpfile) + if err != nil { + return nil, err + } + + buf, err := ioutil.ReadAll(file) + if err != nil { + return nil, err + } + + u := utmp{} + entrySize := int(unsafe.Sizeof(u)) + count := len(buf) / entrySize + + ret := make([]UserStat, 0, count) + + for i := 0; i < count; i++ { + b := buf[i*entrySize : i*entrySize+entrySize] + + var u utmp + br := bytes.NewReader(b) + err := binary.Read(br, binary.LittleEndian, &u) + if err != nil { + continue + } + user := UserStat{ + User: common.ByteToString(u.UtUser[:]), + Terminal: common.ByteToString(u.UtLine[:]), + Host: common.ByteToString(u.UtHost[:]), + Started: int(u.UtTv.TvSec), + } + ret = append(ret, user) + } + + return ret, nil + +} + +func getLSB() (*LSB, error) { + ret := &LSB{} + if pathExists("/etc/lsb-release") { + contents, err := readLines("/etc/lsb-release") + if err != nil { + return ret, err // return empty + } + for _, line := range contents { + field := strings.Split(line, "=") + if len(field) < 2 { + continue + } + switch field[0] { + case "DISTRIB_ID": + ret.ID = field[1] + case "DISTRIB_RELEASE": + ret.Release = field[1] + case "DISTRIB_CODENAME": + ret.Codename = field[1] + case "DISTRIB_DESCRIPTION": + ret.Description = field[1] + } + } + } else if pathExists("/usr/bin/lsb_release") { + out, err := exec.Command("/usr/bin/lsb_release").Output() + if err != nil { + return ret, err + } + for _, line := range strings.Split(string(out), "\n") { + field := strings.Split(line, ":") + if len(field) < 2 { + continue + } + switch field[0] { + case "Distributor ID": + ret.ID = field[1] + case "Release": + ret.Release = field[1] + case "Codename": + ret.Codename = field[1] + case "Description": + ret.Description = field[1] + } + } + + } + + return ret, nil +} + +func GetPlatformInformation() (platform string, family string, version string, err error) { + + lsb, err := getLSB() + if err != nil { + lsb = &LSB{} + } + + if pathExists("/etc/oracle-release") { + platform = "oracle" + contents, err := readLines("/etc/oracle-release") + if err == nil { + version = getRedhatishVersion(contents) + } + } else if pathExists("/etc/enterprise-release") { + platform = "oracle" + contents, err := readLines("/etc/enterprise-release") + if err == nil { + version = getRedhatishVersion(contents) + } + } else if pathExists("/etc/debian_version") { + if lsb.ID == "Ubuntu" { + platform = "ubuntu" + version = lsb.Release + } else if lsb.ID == "LinuxMint" { + platform = "linuxmint" + version = lsb.Release + } else { + if pathExists("/usr/bin/raspi-config") { + platform = "raspbian" + } else { + platform = "debian" + } + contents, err := readLines("/etc/debian_version") + if err == nil { + version = contents[0] + } + } + } else if pathExists("/etc/redhat-release") { + contents, err := readLines("/etc/redhat-release") + if err == nil { + version = getRedhatishVersion(contents) + platform = getRedhatishPlatform(contents) + } + } else if pathExists("/etc/system-release") { + contents, err := readLines("/etc/system-release") + if err == nil { + version = getRedhatishVersion(contents) + platform = getRedhatishPlatform(contents) + } + } else if pathExists("/etc/gentoo-release") { + platform = "gentoo" + contents, err := readLines("/etc/gentoo-release") + if err == nil { + version = getRedhatishVersion(contents) + } + // TODO: suse detection + // TODO: slackware detecion + } else if pathExists("/etc/arch-release") { + platform = "arch" + // TODO: exherbo detection + } else if lsb.ID == "RedHat" { + platform = "redhat" + version = lsb.Release + } else if lsb.ID == "Amazon" { + platform = "amazon" + version = lsb.Release + } else if lsb.ID == "ScientificSL" { + platform = "scientific" + version = lsb.Release + } else if lsb.ID == "XenServer" { + platform = "xenserver" + version = lsb.Release + } else if lsb.ID != "" { + platform = strings.ToLower(lsb.ID) + version = lsb.Release + } + + switch platform { + case "debian", "ubuntu", "linuxmint", "raspbian": + family = "debian" + case "fedora": + family = "fedora" + case "oracle", "centos", "redhat", "scientific", "enterpriseenterprise", "amazon", "xenserver", "cloudlinux", "ibm_powerkvm": + family = "rhel" + case "suse": + family = "suse" + case "gentoo": + family = "gentoo" + case "slackware": + family = "slackware" + case "arch": + family = "arch" + case "exherbo": + family = "exherbo" + } + + return platform, family, version, nil + +} + +func getRedhatishVersion(contents []string) string { + c := strings.ToLower(strings.Join(contents, "")) + + if strings.Contains(c, "rawhide") { + return "rawhide" + } + if matches := regexp.MustCompile(`release (\d[\d.]*)`).FindStringSubmatch(c); matches != nil { + return matches[1] + } + return "" +} + +func getRedhatishPlatform(contents []string) string { + c := strings.ToLower(strings.Join(contents, "")) + + if strings.Contains(c, "red hat") { + return "redhat" + } + f := strings.Split(c, " ") + + return f[0] +} + +func GetVirtualization() (string, string, error) { + var system string + var role string + + if pathExists("/proc/xen") { + system = "xen" + role = "guest" // assume guest + + if pathExists("/proc/xen/capabilities") { + contents, err := readLines("/proc/xen/capabilities") + if err == nil { + if stringContains(contents, "control_d") { + role = "host" + } + } + } + } + if pathExists("/proc/modules") { + contents, err := readLines("/proc/modules") + if err == nil { + if stringContains(contents, "kvm") { + system = "kvm" + role = "host" + } else if stringContains(contents, "vboxdrv") { + system = "vbox" + role = "host" + } else if stringContains(contents, "vboxguest") { + system = "vbox" + role = "guest" + } + } + } + + if pathExists("/proc/cpuinfo") { + contents, err := readLines("/proc/cpuinfo") + if err == nil { + if stringContains(contents, "QEMU Virtual CPU") || + stringContains(contents, "Common KVM processor") || + stringContains(contents, "Common 32-bit KVM processor") { + system = "kvm" + role = "guest" + } + } + } + + if pathExists("/proc/bc/0") { + system = "openvz" + role = "host" + } else if pathExists("/proc/vz") { + system = "openvz" + role = "guest" + } + + // not use dmidecode because it requires root + + if pathExists("/proc/self/status") { + contents, err := readLines("/proc/self/status") + if err == nil { + + if stringContains(contents, "s_context:") || + stringContains(contents, "VxID:") { + system = "linux-vserver" + } + // TODO: guest or host + } + } + + if pathExists("/proc/self/cgroup") { + contents, err := readLines("/proc/self/cgroup") + if err == nil { + + if stringContains(contents, "lxc") || + stringContains(contents, "docker") { + system = "lxc" + role = "guest" + } else if pathExists("/usr/bin/lxc-version") { // TODO: which + system = "lxc" + role = "host" + } + } + } + + return system, role, nil +} diff --git a/host/host_linux_amd64.go b/host/host_linux_amd64.go new file mode 100644 index 0000000..19ee586 --- /dev/null +++ b/host/host_linux_amd64.go @@ -0,0 +1,27 @@ +// +build linux +// +build amd64 + +package gopsutil + +type exitStatus struct { + Etermination int16 // Process termination status. + Eexit int16 // Process exit status. +} +type timeval struct { + TvSec uint32 // Seconds. + TvUsec uint32 // Microseconds. +} + +type utmp struct { + UtType int16 // Type of login. + UtPid int32 // Process ID of login process. + UtLine [32]byte // Devicename. + UtID [4]byte // Inittab ID. + UtUser [32]byte // Username. + UtHost [256]byte // Hostname for remote login. + UtExit exitStatus // Exit status of a process marked + UtSession int32 // Session ID, used for windowing. + UtTv timeval // Time entry was made. + UtAddrV6 [16]byte // Internet address of remote host. + Unused [20]byte // Reserved for future use. // original is 20 +} diff --git a/host/host_linux_arm.go b/host/host_linux_arm.go new file mode 100644 index 0000000..b1371fd --- /dev/null +++ b/host/host_linux_arm.go @@ -0,0 +1,27 @@ +// +build linux +// +build arm + +package gopsutil + +type exitStatus struct { + Etermination int16 // Process termination status. + Eexit int16 // Process exit status. +} +type timeval struct { + TvSec uint32 // Seconds. + TvUsec uint32 // Microseconds. +} + +type utmp struct { + UtType int16 // Type of login. + UtPid int32 // Process ID of login process. + UtLine [32]byte // Devicename. + UtID [4]byte // Inittab ID. + UtUser [32]byte // Username. + UtHost [256]byte // Hostname for remote login. + UtExit exitStatus // Exit status of a process marked + UtSession int32 // Session ID, used for windowing. + UtTv timeval // Time entry was made. + UtAddrV6 [16]byte // Internet address of remote host. + Unused [20]byte // Reserved for future use. // original is 20 +} diff --git a/host/host_linux_test.go b/host/host_linux_test.go new file mode 100644 index 0000000..56343aa --- /dev/null +++ b/host/host_linux_test.go @@ -0,0 +1,61 @@ +// +build linux + +package gopsutil + +import ( + "testing" +) + +func TestGetRedhatishVersion(t *testing.T) { + var ret string + c := []string{"Rawhide"} + ret = getRedhatishVersion(c) + if ret != "rawhide" { + t.Errorf("Could not get version rawhide: %v", ret) + } + + c = []string{"Fedora release 15 (Lovelock)"} + ret = getRedhatishVersion(c) + if ret != "15" { + t.Errorf("Could not get version fedora: %v", ret) + } + + c = []string{"Enterprise Linux Server release 5.5 (Carthage)"} + ret = getRedhatishVersion(c) + if ret != "5.5" { + t.Errorf("Could not get version redhat enterprise: %v", ret) + } + + c = []string{""} + ret = getRedhatishVersion(c) + if ret != "" { + t.Errorf("Could not get version with no value: %v", ret) + } +} + +func TestGetRedhatishPlatform(t *testing.T) { + var ret string + c := []string{"red hat"} + ret = getRedhatishPlatform(c) + if ret != "redhat" { + t.Errorf("Could not get platform redhat: %v", ret) + } + + c = []string{"Fedora release 15 (Lovelock)"} + ret = getRedhatishPlatform(c) + if ret != "fedora" { + t.Errorf("Could not get platform fedora: %v", ret) + } + + c = []string{"Enterprise Linux Server release 5.5 (Carthage)"} + ret = getRedhatishPlatform(c) + if ret != "enterprise" { + t.Errorf("Could not get platform redhat enterprise: %v", ret) + } + + c = []string{""} + ret = getRedhatishPlatform(c) + if ret != "" { + t.Errorf("Could not get platform with no value: %v", ret) + } +} diff --git a/host/host_test.go b/host/host_test.go new file mode 100644 index 0000000..285ae13 --- /dev/null +++ b/host/host_test.go @@ -0,0 +1,67 @@ +package gopsutil + +import ( + "fmt" + "testing" +) + +func TestHostInfo(t *testing.T) { + v, err := HostInfo() + if err != nil { + t.Errorf("error %v", err) + } + empty := &HostInfoStat{} + if v == empty { + t.Errorf("Could not get hostinfo %v", v) + } +} + +func TestBoot_time(t *testing.T) { + v, err := BootTime() + if err != nil { + t.Errorf("error %v", err) + } + if v == 0 { + t.Errorf("Could not boot time %v", v) + } +} + +func TestUsers(t *testing.T) { + v, err := Users() + if err != nil { + t.Errorf("error %v", err) + } + empty := UserStat{} + for _, u := range v { + if u == empty { + t.Errorf("Could not Users %v", v) + } + } +} + +func TestHostInfoStat_String(t *testing.T) { + v := HostInfoStat{ + Hostname: "test", + Uptime: 3000, + Procs: 100, + OS: "linux", + Platform: "ubuntu", + } + e := `{"hostname":"test","uptime":3000,"procs":100,"os":"linux","platform":"ubuntu","platformFamily":"","platformVersion":"","virtualizationSystem":"","virtualizationRole":""}` + if e != fmt.Sprintf("%v", v) { + t.Errorf("HostInfoStat string is invalid: %v", v) + } +} + +func TestUserStat_String(t *testing.T) { + v := UserStat{ + User: "user", + Terminal: "term", + Host: "host", + Started: 100, + } + e := `{"user":"user","terminal":"term","host":"host","started":100}` + if e != fmt.Sprintf("%v", v) { + t.Errorf("UserStat string is invalid: %v", v) + } +} diff --git a/host/host_windows.go b/host/host_windows.go new file mode 100644 index 0000000..8921187 --- /dev/null +++ b/host/host_windows.go @@ -0,0 +1,66 @@ +// +build windows + +package gopsutil + +import ( + "os" + "syscall" + "unsafe" +) + +var ( + procGetSystemTimeAsFileTime = modkernel32.NewProc("GetSystemTimeAsFileTime") + procGetTickCount = modkernel32.NewProc("GetTickCount") +) + +func HostInfo() (*HostInfoStat, error) { + ret := &HostInfoStat{} + hostname, err := os.Hostname() + if err != nil { + return ret, err + } + + ret.Hostname = hostname + uptimemsec, _, err := procGetTickCount.Call() + if uptimemsec == 0 { + return ret, syscall.GetLastError() + } + + ret.Uptime = uint64(uptimemsec) / 1000 + + procs, err := Pids() + if err != nil { + return ret, err + } + + ret.Procs = uint64(len(procs)) + + return ret, nil +} + +func BootTime() (uint64, error) { + var lpSystemTimeAsFileTime FILETIME + + r, _, _ := procGetSystemTimeAsFileTime.Call(uintptr(unsafe.Pointer(&lpSystemTimeAsFileTime))) + if r == 0 { + return 0, syscall.GetLastError() + } + + // TODO: This calc is wrong. + ll := (uint32(lpSystemTimeAsFileTime.DwHighDateTime))<<32 + lpSystemTimeAsFileTime.DwLowDateTime + pt := (uint64(ll) - 116444736000000000) / 10000000 + + u, _, _ := procGetTickCount.Call() + if u == 0 { + return 0, syscall.GetLastError() + } + uptime := uint64(u) / 1000 + + return uint64(pt - uptime), nil +} +func Users() ([]UserStat, error) { + + var ret []UserStat + + return ret, nil +} diff --git a/host_darwin.go b/host_darwin.go deleted file mode 100644 index c666dea..0000000 --- a/host_darwin.go +++ /dev/null @@ -1,152 +0,0 @@ -// +build darwin - -package gopsutil - -import ( - "bytes" - "encoding/binary" - "io/ioutil" - "os" - "os/exec" - "runtime" - "strconv" - "strings" - "unsafe" -) - -const ( - UTXUserSize = 256 /* include/NetBSD/utmpx.h */ - UTXIDSize = 4 - UTXLineSize = 32 - UTXHostSize = 256 -) - -type utmpx32 struct { - UtUser [UTXUserSize]byte /* login name */ - UtID [UTXIDSize]byte /* id */ - UtLine [UTXLineSize]byte /* tty name */ - //TODO UtPid pid_t /* process id creating the entry */ - UtType [4]byte /* type of this entry */ - //TODO UtTv timeval32 /* time entry was created */ - UtHost [UTXHostSize]byte /* host name */ - UtPad [16]byte /* reserved for future use */ -} - -func HostInfo() (*HostInfoStat, error) { - ret := &HostInfoStat{ - OS: runtime.GOOS, - PlatformFamily: "darwin", - } - - hostname, err := os.Hostname() - if err != nil { - return ret, err - } - ret.Hostname = hostname - - platform, family, version, err := GetPlatformInformation() - if err == nil { - ret.Platform = platform - ret.PlatformFamily = family - ret.PlatformVersion = version - } - system, role, err := GetVirtualization() - if err == nil { - ret.VirtualizationSystem = system - ret.VirtualizationRole = role - } - - values, err := doSysctrl("kern.boottime") - if err == nil { - // ex: { sec = 1392261637, usec = 627534 } Thu Feb 13 12:20:37 2014 - v := strings.Replace(values[2], ",", "", 1) - t, err := strconv.ParseUint(v, 10, 64) - if err != nil { - return ret, err - } - ret.Uptime = t - } - - return ret, nil -} - -func BootTime() (int64, error) { - values, err := doSysctrl("kern.boottime") - if err != nil { - return 0, err - } - // ex: { sec = 1392261637, usec = 627534 } Thu Feb 13 12:20:37 2014 - v := strings.Replace(values[2], ",", "", 1) - - boottime, err := strconv.ParseInt(v, 10, 64) - if err != nil { - return 0, err - } - - return boottime, nil -} - -func Users() ([]UserStat, error) { - utmpfile := "/var/run/utmpx" - var ret []UserStat - - file, err := os.Open(utmpfile) - if err != nil { - return ret, err - } - - buf, err := ioutil.ReadAll(file) - if err != nil { - return ret, err - } - - u := utmpx32{} - entrySize := int(unsafe.Sizeof(u)) - count := len(buf) / entrySize - - for i := 0; i < count; i++ { - b := buf[i*entrySize : i*entrySize+entrySize] - - var u utmpx32 - br := bytes.NewReader(b) - err := binary.Read(br, binary.LittleEndian, &u) - if err != nil { - continue - } - user := UserStat{ - User: byteToString(u.UtUser[:]), - // Terminal: byteToString(u.UtLine[:]), - Host: byteToString(u.UtHost[:]), - // Started: int(u.UtTime), - } - ret = append(ret, user) - } - - return ret, nil - -} - -func GetPlatformInformation() (string, string, string, error) { - platform := "" - family := "" - version := "" - - out, err := exec.Command("uname", "-s").Output() - if err == nil { - platform = strings.ToLower(strings.TrimSpace(string(out))) - } - - out, err = exec.Command("uname", "-r").Output() - if err == nil { - version = strings.ToLower(strings.TrimSpace(string(out))) - } - - return platform, family, version, nil -} - -func GetVirtualization() (string, string, error) { - system := "" - role := "" - - return system, role, nil -} diff --git a/host_freebsd.go b/host_freebsd.go deleted file mode 100644 index 71f2d5f..0000000 --- a/host_freebsd.go +++ /dev/null @@ -1,134 +0,0 @@ -// +build freebsd - -package gopsutil - -import ( - "bytes" - "encoding/binary" - "io/ioutil" - "os" - "os/exec" - "runtime" - "strconv" - "strings" - "unsafe" -) - -func HostInfo() (*HostInfoStat, error) { - ret := &HostInfoStat{ - OS: runtime.GOOS, - PlatformFamily: "freebsd", - } - - hostname, err := os.Hostname() - if err != nil { - return ret, err - } - ret.Hostname = hostname - - platform, family, version, err := GetPlatformInformation() - if err == nil { - ret.Platform = platform - ret.PlatformFamily = family - ret.PlatformVersion = version - } - system, role, err := GetVirtualization() - if err == nil { - ret.VirtualizationSystem = system - ret.VirtualizationRole = role - } - - values, err := doSysctrl("kern.boottime") - if err == nil { - // ex: { sec = 1392261637, usec = 627534 } Thu Feb 13 12:20:37 2014 - v := strings.Replace(values[2], ",", "", 1) - t, err := strconv.ParseUint(v, 10, 64) - if err != nil { - return ret, err - } - ret.Uptime = t - } - - return ret, nil -} - -func BootTime() (int64, error) { - values, err := doSysctrl("kern.boottime") - if err != nil { - return 0, err - } - // ex: { sec = 1392261637, usec = 627534 } Thu Feb 13 12:20:37 2014 - v := strings.Replace(values[2], ",", "", 1) - - boottime, err := strconv.ParseInt(v, 10, 64) - if err != nil { - return 0, err - } - - return boottime, nil -} - -func Users() ([]UserStat, error) { - utmpfile := "/var/run/utmp" - var ret []UserStat - - file, err := os.Open(utmpfile) - if err != nil { - return ret, err - } - - buf, err := ioutil.ReadAll(file) - if err != nil { - return ret, err - } - - u := utmp{} - entrySize := int(unsafe.Sizeof(u)) - count := len(buf) / entrySize - - for i := 0; i < count; i++ { - b := buf[i*entrySize : i*entrySize+entrySize] - - var u utmp - br := bytes.NewReader(b) - err := binary.Read(br, binary.LittleEndian, &u) - if err != nil { - continue - } - user := UserStat{ - User: byteToString(u.UtName[:]), - Terminal: byteToString(u.UtLine[:]), - Host: byteToString(u.UtHost[:]), - Started: int(u.UtTime), - } - ret = append(ret, user) - } - - return ret, nil - -} - -func GetPlatformInformation() (string, string, string, error) { - platform := "" - family := "" - version := "" - - out, err := exec.Command("uname", "-s").Output() - if err == nil { - platform = strings.ToLower(strings.TrimSpace(string(out))) - } - - out, err = exec.Command("uname", "-r").Output() - if err == nil { - version = strings.ToLower(strings.TrimSpace(string(out))) - } - - return platform, family, version, nil -} - -func GetVirtualization() (string, string, error) { - system := "" - role := "" - - return system, role, nil -} diff --git a/host_freebsd_amd64.go b/host_freebsd_amd64.go deleted file mode 100644 index efd1ade..0000000 --- a/host_freebsd_amd64.go +++ /dev/null @@ -1,17 +0,0 @@ -// +build freebsd -// +build amd64 - -package gopsutil - -const ( - UTNameSize = 16 /* see MAXLOGNAME in */ - UTLineSize = 8 - UTHostSize = 16 -) - -type utmp struct { - UtLine [UTLineSize]byte - UtName [UTNameSize]byte - UtHost [UTHostSize]byte - UtTime int32 -} diff --git a/host_linux.go b/host_linux.go deleted file mode 100644 index 71d68f2..0000000 --- a/host_linux.go +++ /dev/null @@ -1,360 +0,0 @@ -// +build linux - -package gopsutil - -import ( - "bytes" - "encoding/binary" - "io/ioutil" - "os" - "os/exec" - "regexp" - "runtime" - "strings" - "syscall" - "unsafe" -) - -type LSB struct { - ID string - Release string - Codename string - Description string -} - -func HostInfo() (*HostInfoStat, error) { - hostname, err := os.Hostname() - if err != nil { - return nil, err - } - - ret := &HostInfoStat{ - Hostname: hostname, - OS: runtime.GOOS, - } - - platform, family, version, err := GetPlatformInformation() - if err == nil { - ret.Platform = platform - ret.PlatformFamily = family - ret.PlatformVersion = version - } - system, role, err := GetVirtualization() - if err == nil { - ret.VirtualizationSystem = system - ret.VirtualizationRole = role - } - uptime, err := BootTime() - if err == nil { - ret.Uptime = uptime - } - - return ret, nil -} - -func BootTime() (uint64, error) { - sysinfo := &syscall.Sysinfo_t{} - if err := syscall.Sysinfo(sysinfo); err != nil { - return 0, err - } - return uint64(sysinfo.Uptime), nil -} - -func Users() ([]UserStat, error) { - utmpfile := "/var/run/utmp" - - file, err := os.Open(utmpfile) - if err != nil { - return nil, err - } - - buf, err := ioutil.ReadAll(file) - if err != nil { - return nil, err - } - - u := utmp{} - entrySize := int(unsafe.Sizeof(u)) - count := len(buf) / entrySize - - ret := make([]UserStat, 0, count) - - for i := 0; i < count; i++ { - b := buf[i*entrySize : i*entrySize+entrySize] - - var u utmp - br := bytes.NewReader(b) - err := binary.Read(br, binary.LittleEndian, &u) - if err != nil { - continue - } - user := UserStat{ - User: byteToString(u.UtUser[:]), - Terminal: byteToString(u.UtLine[:]), - Host: byteToString(u.UtHost[:]), - Started: int(u.UtTv.TvSec), - } - ret = append(ret, user) - } - - return ret, nil - -} - -func getLSB() (*LSB, error) { - ret := &LSB{} - if pathExists("/etc/lsb-release") { - contents, err := readLines("/etc/lsb-release") - if err != nil { - return ret, err // return empty - } - for _, line := range contents { - field := strings.Split(line, "=") - if len(field) < 2 { - continue - } - switch field[0] { - case "DISTRIB_ID": - ret.ID = field[1] - case "DISTRIB_RELEASE": - ret.Release = field[1] - case "DISTRIB_CODENAME": - ret.Codename = field[1] - case "DISTRIB_DESCRIPTION": - ret.Description = field[1] - } - } - } else if pathExists("/usr/bin/lsb_release") { - out, err := exec.Command("/usr/bin/lsb_release").Output() - if err != nil { - return ret, err - } - for _, line := range strings.Split(string(out), "\n") { - field := strings.Split(line, ":") - if len(field) < 2 { - continue - } - switch field[0] { - case "Distributor ID": - ret.ID = field[1] - case "Release": - ret.Release = field[1] - case "Codename": - ret.Codename = field[1] - case "Description": - ret.Description = field[1] - } - } - - } - - return ret, nil -} - -func GetPlatformInformation() (platform string, family string, version string, err error) { - - lsb, err := getLSB() - if err != nil { - lsb = &LSB{} - } - - if pathExists("/etc/oracle-release") { - platform = "oracle" - contents, err := readLines("/etc/oracle-release") - if err == nil { - version = getRedhatishVersion(contents) - } - } else if pathExists("/etc/enterprise-release") { - platform = "oracle" - contents, err := readLines("/etc/enterprise-release") - if err == nil { - version = getRedhatishVersion(contents) - } - } else if pathExists("/etc/debian_version") { - if lsb.ID == "Ubuntu" { - platform = "ubuntu" - version = lsb.Release - } else if lsb.ID == "LinuxMint" { - platform = "linuxmint" - version = lsb.Release - } else { - if pathExists("/usr/bin/raspi-config") { - platform = "raspbian" - } else { - platform = "debian" - } - contents, err := readLines("/etc/debian_version") - if err == nil { - version = contents[0] - } - } - } else if pathExists("/etc/redhat-release") { - contents, err := readLines("/etc/redhat-release") - if err == nil { - version = getRedhatishVersion(contents) - platform = getRedhatishPlatform(contents) - } - } else if pathExists("/etc/system-release") { - contents, err := readLines("/etc/system-release") - if err == nil { - version = getRedhatishVersion(contents) - platform = getRedhatishPlatform(contents) - } - } else if pathExists("/etc/gentoo-release") { - platform = "gentoo" - contents, err := readLines("/etc/gentoo-release") - if err == nil { - version = getRedhatishVersion(contents) - } - // TODO: suse detection - // TODO: slackware detecion - } else if pathExists("/etc/arch-release") { - platform = "arch" - // TODO: exherbo detection - } else if lsb.ID == "RedHat" { - platform = "redhat" - version = lsb.Release - } else if lsb.ID == "Amazon" { - platform = "amazon" - version = lsb.Release - } else if lsb.ID == "ScientificSL" { - platform = "scientific" - version = lsb.Release - } else if lsb.ID == "XenServer" { - platform = "xenserver" - version = lsb.Release - } else if lsb.ID != "" { - platform = strings.ToLower(lsb.ID) - version = lsb.Release - } - - switch platform { - case "debian", "ubuntu", "linuxmint", "raspbian": - family = "debian" - case "fedora": - family = "fedora" - case "oracle", "centos", "redhat", "scientific", "enterpriseenterprise", "amazon", "xenserver", "cloudlinux", "ibm_powerkvm": - family = "rhel" - case "suse": - family = "suse" - case "gentoo": - family = "gentoo" - case "slackware": - family = "slackware" - case "arch": - family = "arch" - case "exherbo": - family = "exherbo" - } - - return platform, family, version, nil - -} - -func getRedhatishVersion(contents []string) string { - c := strings.ToLower(strings.Join(contents, "")) - - if strings.Contains(c, "rawhide") { - return "rawhide" - } - if matches := regexp.MustCompile(`release (\d[\d.]*)`).FindStringSubmatch(c); matches != nil { - return matches[1] - } - return "" -} - -func getRedhatishPlatform(contents []string) string { - c := strings.ToLower(strings.Join(contents, "")) - - if strings.Contains(c, "red hat") { - return "redhat" - } - f := strings.Split(c, " ") - - return f[0] -} - -func GetVirtualization() (string, string, error) { - var system string - var role string - - if pathExists("/proc/xen") { - system = "xen" - role = "guest" // assume guest - - if pathExists("/proc/xen/capabilities") { - contents, err := readLines("/proc/xen/capabilities") - if err == nil { - if stringContains(contents, "control_d") { - role = "host" - } - } - } - } - if pathExists("/proc/modules") { - contents, err := readLines("/proc/modules") - if err == nil { - if stringContains(contents, "kvm") { - system = "kvm" - role = "host" - } else if stringContains(contents, "vboxdrv") { - system = "vbox" - role = "host" - } else if stringContains(contents, "vboxguest") { - system = "vbox" - role = "guest" - } - } - } - - if pathExists("/proc/cpuinfo") { - contents, err := readLines("/proc/cpuinfo") - if err == nil { - if stringContains(contents, "QEMU Virtual CPU") || - stringContains(contents, "Common KVM processor") || - stringContains(contents, "Common 32-bit KVM processor") { - system = "kvm" - role = "guest" - } - } - } - - if pathExists("/proc/bc/0") { - system = "openvz" - role = "host" - } else if pathExists("/proc/vz") { - system = "openvz" - role = "guest" - } - - // not use dmidecode because it requires root - - if pathExists("/proc/self/status") { - contents, err := readLines("/proc/self/status") - if err == nil { - - if stringContains(contents, "s_context:") || - stringContains(contents, "VxID:") { - system = "linux-vserver" - } - // TODO: guest or host - } - } - - if pathExists("/proc/self/cgroup") { - contents, err := readLines("/proc/self/cgroup") - if err == nil { - - if stringContains(contents, "lxc") || - stringContains(contents, "docker") { - system = "lxc" - role = "guest" - } else if pathExists("/usr/bin/lxc-version") { // TODO: which - system = "lxc" - role = "host" - } - } - } - - return system, role, nil -} diff --git a/host_linux_amd64.go b/host_linux_amd64.go deleted file mode 100644 index 19ee586..0000000 --- a/host_linux_amd64.go +++ /dev/null @@ -1,27 +0,0 @@ -// +build linux -// +build amd64 - -package gopsutil - -type exitStatus struct { - Etermination int16 // Process termination status. - Eexit int16 // Process exit status. -} -type timeval struct { - TvSec uint32 // Seconds. - TvUsec uint32 // Microseconds. -} - -type utmp struct { - UtType int16 // Type of login. - UtPid int32 // Process ID of login process. - UtLine [32]byte // Devicename. - UtID [4]byte // Inittab ID. - UtUser [32]byte // Username. - UtHost [256]byte // Hostname for remote login. - UtExit exitStatus // Exit status of a process marked - UtSession int32 // Session ID, used for windowing. - UtTv timeval // Time entry was made. - UtAddrV6 [16]byte // Internet address of remote host. - Unused [20]byte // Reserved for future use. // original is 20 -} diff --git a/host_linux_arm.go b/host_linux_arm.go deleted file mode 100644 index b1371fd..0000000 --- a/host_linux_arm.go +++ /dev/null @@ -1,27 +0,0 @@ -// +build linux -// +build arm - -package gopsutil - -type exitStatus struct { - Etermination int16 // Process termination status. - Eexit int16 // Process exit status. -} -type timeval struct { - TvSec uint32 // Seconds. - TvUsec uint32 // Microseconds. -} - -type utmp struct { - UtType int16 // Type of login. - UtPid int32 // Process ID of login process. - UtLine [32]byte // Devicename. - UtID [4]byte // Inittab ID. - UtUser [32]byte // Username. - UtHost [256]byte // Hostname for remote login. - UtExit exitStatus // Exit status of a process marked - UtSession int32 // Session ID, used for windowing. - UtTv timeval // Time entry was made. - UtAddrV6 [16]byte // Internet address of remote host. - Unused [20]byte // Reserved for future use. // original is 20 -} diff --git a/host_linux_test.go b/host_linux_test.go deleted file mode 100644 index 56343aa..0000000 --- a/host_linux_test.go +++ /dev/null @@ -1,61 +0,0 @@ -// +build linux - -package gopsutil - -import ( - "testing" -) - -func TestGetRedhatishVersion(t *testing.T) { - var ret string - c := []string{"Rawhide"} - ret = getRedhatishVersion(c) - if ret != "rawhide" { - t.Errorf("Could not get version rawhide: %v", ret) - } - - c = []string{"Fedora release 15 (Lovelock)"} - ret = getRedhatishVersion(c) - if ret != "15" { - t.Errorf("Could not get version fedora: %v", ret) - } - - c = []string{"Enterprise Linux Server release 5.5 (Carthage)"} - ret = getRedhatishVersion(c) - if ret != "5.5" { - t.Errorf("Could not get version redhat enterprise: %v", ret) - } - - c = []string{""} - ret = getRedhatishVersion(c) - if ret != "" { - t.Errorf("Could not get version with no value: %v", ret) - } -} - -func TestGetRedhatishPlatform(t *testing.T) { - var ret string - c := []string{"red hat"} - ret = getRedhatishPlatform(c) - if ret != "redhat" { - t.Errorf("Could not get platform redhat: %v", ret) - } - - c = []string{"Fedora release 15 (Lovelock)"} - ret = getRedhatishPlatform(c) - if ret != "fedora" { - t.Errorf("Could not get platform fedora: %v", ret) - } - - c = []string{"Enterprise Linux Server release 5.5 (Carthage)"} - ret = getRedhatishPlatform(c) - if ret != "enterprise" { - t.Errorf("Could not get platform redhat enterprise: %v", ret) - } - - c = []string{""} - ret = getRedhatishPlatform(c) - if ret != "" { - t.Errorf("Could not get platform with no value: %v", ret) - } -} diff --git a/host_test.go b/host_test.go deleted file mode 100644 index 285ae13..0000000 --- a/host_test.go +++ /dev/null @@ -1,67 +0,0 @@ -package gopsutil - -import ( - "fmt" - "testing" -) - -func TestHostInfo(t *testing.T) { - v, err := HostInfo() - if err != nil { - t.Errorf("error %v", err) - } - empty := &HostInfoStat{} - if v == empty { - t.Errorf("Could not get hostinfo %v", v) - } -} - -func TestBoot_time(t *testing.T) { - v, err := BootTime() - if err != nil { - t.Errorf("error %v", err) - } - if v == 0 { - t.Errorf("Could not boot time %v", v) - } -} - -func TestUsers(t *testing.T) { - v, err := Users() - if err != nil { - t.Errorf("error %v", err) - } - empty := UserStat{} - for _, u := range v { - if u == empty { - t.Errorf("Could not Users %v", v) - } - } -} - -func TestHostInfoStat_String(t *testing.T) { - v := HostInfoStat{ - Hostname: "test", - Uptime: 3000, - Procs: 100, - OS: "linux", - Platform: "ubuntu", - } - e := `{"hostname":"test","uptime":3000,"procs":100,"os":"linux","platform":"ubuntu","platformFamily":"","platformVersion":"","virtualizationSystem":"","virtualizationRole":""}` - if e != fmt.Sprintf("%v", v) { - t.Errorf("HostInfoStat string is invalid: %v", v) - } -} - -func TestUserStat_String(t *testing.T) { - v := UserStat{ - User: "user", - Terminal: "term", - Host: "host", - Started: 100, - } - e := `{"user":"user","terminal":"term","host":"host","started":100}` - if e != fmt.Sprintf("%v", v) { - t.Errorf("UserStat string is invalid: %v", v) - } -} diff --git a/host_windows.go b/host_windows.go deleted file mode 100644 index 8921187..0000000 --- a/host_windows.go +++ /dev/null @@ -1,66 +0,0 @@ -// +build windows - -package gopsutil - -import ( - "os" - "syscall" - "unsafe" -) - -var ( - procGetSystemTimeAsFileTime = modkernel32.NewProc("GetSystemTimeAsFileTime") - procGetTickCount = modkernel32.NewProc("GetTickCount") -) - -func HostInfo() (*HostInfoStat, error) { - ret := &HostInfoStat{} - hostname, err := os.Hostname() - if err != nil { - return ret, err - } - - ret.Hostname = hostname - uptimemsec, _, err := procGetTickCount.Call() - if uptimemsec == 0 { - return ret, syscall.GetLastError() - } - - ret.Uptime = uint64(uptimemsec) / 1000 - - procs, err := Pids() - if err != nil { - return ret, err - } - - ret.Procs = uint64(len(procs)) - - return ret, nil -} - -func BootTime() (uint64, error) { - var lpSystemTimeAsFileTime FILETIME - - r, _, _ := procGetSystemTimeAsFileTime.Call(uintptr(unsafe.Pointer(&lpSystemTimeAsFileTime))) - if r == 0 { - return 0, syscall.GetLastError() - } - - // TODO: This calc is wrong. - ll := (uint32(lpSystemTimeAsFileTime.DwHighDateTime))<<32 + lpSystemTimeAsFileTime.DwLowDateTime - pt := (uint64(ll) - 116444736000000000) / 10000000 - - u, _, _ := procGetTickCount.Call() - if u == 0 { - return 0, syscall.GetLastError() - } - uptime := uint64(u) / 1000 - - return uint64(pt - uptime), nil -} -func Users() ([]UserStat, error) { - - var ret []UserStat - - return ret, nil -} diff --git a/load.go b/load.go deleted file mode 100644 index 7b124d6..0000000 --- a/load.go +++ /dev/null @@ -1,16 +0,0 @@ -package gopsutil - -import ( - "encoding/json" -) - -type LoadAvgStat struct { - Load1 float64 `json:"load1"` - Load5 float64 `json:"load5"` - Load15 float64 `json:"load15"` -} - -func (l LoadAvgStat) String() string { - s, _ := json.Marshal(l) - return string(s) -} diff --git a/load/load.go b/load/load.go new file mode 100644 index 0000000..7b124d6 --- /dev/null +++ b/load/load.go @@ -0,0 +1,16 @@ +package gopsutil + +import ( + "encoding/json" +) + +type LoadAvgStat struct { + Load1 float64 `json:"load1"` + Load5 float64 `json:"load5"` + Load15 float64 `json:"load15"` +} + +func (l LoadAvgStat) String() string { + s, _ := json.Marshal(l) + return string(s) +} diff --git a/load/load_darwin.go b/load/load_darwin.go new file mode 100644 index 0000000..6954f0d --- /dev/null +++ b/load/load_darwin.go @@ -0,0 +1,37 @@ +// +build darwin + +package gopsutil + +import ( + "strconv" + + common "github.com/shirou/gopsutil/common" +) + +func LoadAvg() (*LoadAvgStat, error) { + values, err := common.DoSysctrl("vm.loadavg") + if err != nil { + return nil, err + } + + load1, err := strconv.ParseFloat(values[0], 64) + if err != nil { + return nil, err + } + load5, err := strconv.ParseFloat(values[1], 64) + if err != nil { + return nil, err + } + load15, err := strconv.ParseFloat(values[2], 64) + if err != nil { + return nil, err + } + + ret := &LoadAvgStat{ + Load1: float64(load1), + Load5: float64(load5), + Load15: float64(load15), + } + + return ret, nil +} diff --git a/load/load_freebsd.go b/load/load_freebsd.go new file mode 100644 index 0000000..50196f6 --- /dev/null +++ b/load/load_freebsd.go @@ -0,0 +1,37 @@ +// +build freebsd + +package gopsutil + +import ( + "strconv" + + common "github.com/shirou/gopsutil/common" +) + +func LoadAvg() (*LoadAvgStat, error) { + values, err := common.DoSysctrl("vm.loadavg") + if err != nil { + return nil, err + } + + load1, err := strconv.ParseFloat(values[0], 64) + if err != nil { + return nil, err + } + load5, err := strconv.ParseFloat(values[1], 64) + if err != nil { + return nil, err + } + load15, err := strconv.ParseFloat(values[2], 64) + if err != nil { + return nil, err + } + + ret := &LoadAvgStat{ + Load1: float64(load1), + Load5: float64(load5), + Load15: float64(load15), + } + + return ret, nil +} diff --git a/load/load_linux.go b/load/load_linux.go new file mode 100644 index 0000000..238ae30 --- /dev/null +++ b/load/load_linux.go @@ -0,0 +1,40 @@ +// +build linux + +package gopsutil + +import ( + "io/ioutil" + "strconv" + "strings" +) + +func LoadAvg() (*LoadAvgStat, error) { + filename := "/proc/loadavg" + line, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + + values := strings.Fields(string(line)) + + load1, err := strconv.ParseFloat(values[0], 64) + if err != nil { + return nil, err + } + load5, err := strconv.ParseFloat(values[1], 64) + if err != nil { + return nil, err + } + load15, err := strconv.ParseFloat(values[2], 64) + if err != nil { + return nil, err + } + + ret := &LoadAvgStat{ + Load1: load1, + Load5: load5, + Load15: load15, + } + + return ret, nil +} diff --git a/load/load_test.go b/load/load_test.go new file mode 100644 index 0000000..660e940 --- /dev/null +++ b/load/load_test.go @@ -0,0 +1,30 @@ +package gopsutil + +import ( + "fmt" + "testing" +) + +func TestLoad(t *testing.T) { + v, err := LoadAvg() + if err != nil { + t.Errorf("error %v", err) + } + + empty := &LoadAvgStat{} + if v == empty { + t.Errorf("error load: %v", v) + } +} + +func TestLoadAvgStat_String(t *testing.T) { + v := LoadAvgStat{ + Load1: 10.1, + Load5: 20.1, + Load15: 30.1, + } + e := `{"load1":10.1,"load5":20.1,"load15":30.1}` + if e != fmt.Sprintf("%v", v) { + t.Errorf("LoadAvgStat string is invalid: %v", v) + } +} diff --git a/load/load_windows.go b/load/load_windows.go new file mode 100644 index 0000000..75f07ac --- /dev/null +++ b/load/load_windows.go @@ -0,0 +1,13 @@ +// +build windows + +package gopsutil + +import ( + common "github.com/shirou/gopsutil/common" +) + +func LoadAvg() (*LoadAvgStat, error) { + ret := LoadAvgStat{} + + return &ret, common.NotImplementedError +} diff --git a/load_darwin.go b/load_darwin.go deleted file mode 100644 index 0d801d5..0000000 --- a/load_darwin.go +++ /dev/null @@ -1,35 +0,0 @@ -// +build darwin - -package gopsutil - -import ( - "strconv" -) - -func LoadAvg() (*LoadAvgStat, error) { - values, err := doSysctrl("vm.loadavg") - if err != nil { - return nil, err - } - - load1, err := strconv.ParseFloat(values[0], 64) - if err != nil { - return nil, err - } - load5, err := strconv.ParseFloat(values[1], 64) - if err != nil { - return nil, err - } - load15, err := strconv.ParseFloat(values[2], 64) - if err != nil { - return nil, err - } - - ret := &LoadAvgStat{ - Load1: float64(load1), - Load5: float64(load5), - Load15: float64(load15), - } - - return ret, nil -} diff --git a/load_freebsd.go b/load_freebsd.go deleted file mode 100644 index c021a30..0000000 --- a/load_freebsd.go +++ /dev/null @@ -1,35 +0,0 @@ -// +build freebsd - -package gopsutil - -import ( - "strconv" -) - -func LoadAvg() (*LoadAvgStat, error) { - values, err := doSysctrl("vm.loadavg") - if err != nil { - return nil, err - } - - load1, err := strconv.ParseFloat(values[0], 64) - if err != nil { - return nil, err - } - load5, err := strconv.ParseFloat(values[1], 64) - if err != nil { - return nil, err - } - load15, err := strconv.ParseFloat(values[2], 64) - if err != nil { - return nil, err - } - - ret := &LoadAvgStat{ - Load1: float64(load1), - Load5: float64(load5), - Load15: float64(load15), - } - - return ret, nil -} diff --git a/load_linux.go b/load_linux.go deleted file mode 100644 index 238ae30..0000000 --- a/load_linux.go +++ /dev/null @@ -1,40 +0,0 @@ -// +build linux - -package gopsutil - -import ( - "io/ioutil" - "strconv" - "strings" -) - -func LoadAvg() (*LoadAvgStat, error) { - filename := "/proc/loadavg" - line, err := ioutil.ReadFile(filename) - if err != nil { - return nil, err - } - - values := strings.Fields(string(line)) - - load1, err := strconv.ParseFloat(values[0], 64) - if err != nil { - return nil, err - } - load5, err := strconv.ParseFloat(values[1], 64) - if err != nil { - return nil, err - } - load15, err := strconv.ParseFloat(values[2], 64) - if err != nil { - return nil, err - } - - ret := &LoadAvgStat{ - Load1: load1, - Load5: load5, - Load15: load15, - } - - return ret, nil -} diff --git a/load_test.go b/load_test.go deleted file mode 100644 index 660e940..0000000 --- a/load_test.go +++ /dev/null @@ -1,30 +0,0 @@ -package gopsutil - -import ( - "fmt" - "testing" -) - -func TestLoad(t *testing.T) { - v, err := LoadAvg() - if err != nil { - t.Errorf("error %v", err) - } - - empty := &LoadAvgStat{} - if v == empty { - t.Errorf("error load: %v", v) - } -} - -func TestLoadAvgStat_String(t *testing.T) { - v := LoadAvgStat{ - Load1: 10.1, - Load5: 20.1, - Load15: 30.1, - } - e := `{"load1":10.1,"load5":20.1,"load15":30.1}` - if e != fmt.Sprintf("%v", v) { - t.Errorf("LoadAvgStat string is invalid: %v", v) - } -} diff --git a/load_windows.go b/load_windows.go deleted file mode 100644 index 4d15220..0000000 --- a/load_windows.go +++ /dev/null @@ -1,9 +0,0 @@ -// +build windows - -package gopsutil - -func LoadAvg() (*LoadAvgStat, error) { - ret := LoadAvgStat{} - - return &ret, NotImplementedError -} diff --git a/mem.go b/mem.go deleted file mode 100644 index 6f2e293..0000000 --- a/mem.go +++ /dev/null @@ -1,38 +0,0 @@ -package gopsutil - -import ( - "encoding/json" -) - -type VirtualMemoryStat struct { - Total uint64 `json:"total"` - Available uint64 `json:"available"` - Used uint64 `json:"used"` - UsedPercent float64 `json:"usedPercent"` - Free uint64 `json:"free"` - Active uint64 `json:"active"` - Inactive uint64 `json:"inactive"` - Buffers uint64 `json:"buffers"` - Cached uint64 `json:"cached"` - Wired uint64 `json:"wired"` - Shared uint64 `json:"shared"` -} - -type SwapMemoryStat struct { - Total uint64 `json:"total"` - Used uint64 `json:"used"` - Free uint64 `json:"free"` - UsedPercent float64 `json:"usedPercent"` - Sin uint64 `json:"sin"` - Sout uint64 `json:"sout"` -} - -func (m VirtualMemoryStat) String() string { - s, _ := json.Marshal(m) - return string(s) -} - -func (m SwapMemoryStat) String() string { - s, _ := json.Marshal(m) - return string(s) -} diff --git a/mem/mem.go b/mem/mem.go new file mode 100644 index 0000000..6f2e293 --- /dev/null +++ b/mem/mem.go @@ -0,0 +1,38 @@ +package gopsutil + +import ( + "encoding/json" +) + +type VirtualMemoryStat struct { + Total uint64 `json:"total"` + Available uint64 `json:"available"` + Used uint64 `json:"used"` + UsedPercent float64 `json:"usedPercent"` + Free uint64 `json:"free"` + Active uint64 `json:"active"` + Inactive uint64 `json:"inactive"` + Buffers uint64 `json:"buffers"` + Cached uint64 `json:"cached"` + Wired uint64 `json:"wired"` + Shared uint64 `json:"shared"` +} + +type SwapMemoryStat struct { + Total uint64 `json:"total"` + Used uint64 `json:"used"` + Free uint64 `json:"free"` + UsedPercent float64 `json:"usedPercent"` + Sin uint64 `json:"sin"` + Sout uint64 `json:"sout"` +} + +func (m VirtualMemoryStat) String() string { + s, _ := json.Marshal(m) + return string(s) +} + +func (m SwapMemoryStat) String() string { + s, _ := json.Marshal(m) + return string(s) +} diff --git a/mem/mem_darwin.go b/mem/mem_darwin.go new file mode 100644 index 0000000..f6282e2 --- /dev/null +++ b/mem/mem_darwin.go @@ -0,0 +1,106 @@ +// +build darwin + +package gopsutil + +import ( + "os/exec" + "strconv" + "strings" + + common "github.com/shirou/gopsutil/common" +) + +func getPageSize() (uint64, error) { + out, err := exec.Command("pagesize").Output() + if err != nil { + return 0, err + } + o := strings.TrimSpace(string(out)) + p, err := strconv.ParseUint(o, 10, 64) + if err != nil { + return 0, err + } + + return p, nil +} + +// VirtualMemory returns VirtualmemoryStat. +func VirtualMemory() (*VirtualMemoryStat, error) { + p, err := getPageSize() + if err != nil { + return nil, err + } + + total, err := common.DoSysctrl("hw.memsize") + if err != nil { + return nil, err + } + free, err := common.DoSysctrl("vm.page_free_count") + if err != nil { + return nil, err + } + parsed := make([]uint64, 0, 7) + vv := []string{ + total[0], + free[0], + } + for _, target := range vv { + t, err := strconv.ParseUint(target, 10, 64) + if err != nil { + return nil, err + } + parsed = append(parsed, t) + } + + ret := &VirtualMemoryStat{ + Total: parsed[0] * p, + Free: parsed[1] * p, + } + + // TODO: platform independent (worked freebsd?) + ret.Available = ret.Free + ret.Buffers + ret.Cached + + ret.Used = ret.Total - ret.Free + ret.UsedPercent = float64(ret.Total-ret.Available) / float64(ret.Total) * 100.0 + + return ret, nil +} + +// SwapMemory returns swapinfo. +func SwapMemory() (*SwapMemoryStat, error) { + var ret *SwapMemoryStat + + swapUsage, err := common.DoSysctrl("vm.swapusage") + if err != nil { + return ret, err + } + + total := strings.Replace(swapUsage[2], "M", "", 1) + used := strings.Replace(swapUsage[5], "M", "", 1) + free := strings.Replace(swapUsage[8], "M", "", 1) + + total_v, err := strconv.ParseFloat(total, 64) + if err != nil { + return nil, err + } + used_v, err := strconv.ParseFloat(used, 64) + if err != nil { + return nil, err + } + free_v, err := strconv.ParseFloat(free, 64) + if err != nil { + return nil, err + } + + u := ((total_v - free_v) / total_v) * 100.0 + + // vm.swapusage shows "M", multiply 1000 + ret = &SwapMemoryStat{ + Total: uint64(total_v * 1000), + Used: uint64(used_v * 1000), + Free: uint64(free_v * 1000), + UsedPercent: u, + } + + return ret, nil +} diff --git a/mem/mem_freebsd.go b/mem/mem_freebsd.go new file mode 100644 index 0000000..97dce78 --- /dev/null +++ b/mem/mem_freebsd.go @@ -0,0 +1,131 @@ +// +build freebsd + +package gopsutil + +import ( + "os/exec" + "strconv" + "strings" + + common "github.com/shirou/gopsutil/common" +) + +func VirtualMemory() (*VirtualMemoryStat, error) { + pageSize, err := common.DoSysctrl("vm.stats.vm.v_page_size") + if err != nil { + return nil, err + } + p, err := strconv.ParseUint(pageSize[0], 10, 64) + if err != nil { + return nil, err + } + + pageCount, err := common.DoSysctrl("vm.stats.vm.v_page_count") + if err != nil { + return nil, err + } + free, err := common.DoSysctrl("vm.stats.vm.v_free_count") + if err != nil { + return nil, err + } + active, err := common.DoSysctrl("vm.stats.vm.v_active_count") + if err != nil { + return nil, err + } + inactive, err := common.DoSysctrl("vm.stats.vm.v_inactive_count") + if err != nil { + return nil, err + } + cache, err := common.DoSysctrl("vm.stats.vm.v_cache_count") + if err != nil { + return nil, err + } + buffer, err := common.DoSysctrl("vfs.bufspace") + if err != nil { + return nil, err + } + wired, err := common.DoSysctrl("vm.stats.vm.v_wire_count") + if err != nil { + return nil, err + } + + parsed := make([]uint64, 0, 7) + vv := []string{ + pageCount[0], + free[0], + active[0], + inactive[0], + cache[0], + buffer[0], + wired[0], + } + for _, target := range vv { + t, err := strconv.ParseUint(target, 10, 64) + if err != nil { + return nil, err + } + parsed = append(parsed, t) + } + + ret := &VirtualMemoryStat{ + Total: parsed[0] * p, + Free: parsed[1] * p, + Active: parsed[2] * p, + Inactive: parsed[3] * p, + Cached: parsed[4] * p, + Buffers: parsed[5], + Wired: parsed[6] * p, + } + + // TODO: platform independent (worked freebsd?) + ret.Available = ret.Free + ret.Buffers + ret.Cached + + ret.Used = ret.Total - ret.Free + ret.UsedPercent = float64(ret.Total-ret.Available) / float64(ret.Total) * 100.0 + + return ret, nil +} + +// Return swapinfo +// FreeBSD can have multiple swap devices. but use only first device +func SwapMemory() (*SwapMemoryStat, error) { + out, err := exec.Command("swapinfo").Output() + if err != nil { + return nil, err + } + var ret *SwapMemoryStat + for _, line := range strings.Split(string(out), "\n") { + values := strings.Fields(line) + // skip title line + if len(values) == 0 || values[0] == "Device" { + continue + } + + u := strings.Replace(values[4], "%", "", 1) + total_v, err := strconv.ParseUint(values[1], 10, 64) + if err != nil { + return nil, err + } + used_v, err := strconv.ParseUint(values[2], 10, 64) + if err != nil { + return nil, err + } + free_v, err := strconv.ParseUint(values[3], 10, 64) + if err != nil { + return nil, err + } + up_v, err := strconv.ParseFloat(u, 64) + if err != nil { + return nil, err + } + + ret = &SwapMemoryStat{ + Total: total_v, + Used: used_v, + Free: free_v, + UsedPercent: up_v, + } + } + + return ret, nil +} diff --git a/mem/mem_linux.go b/mem/mem_linux.go new file mode 100644 index 0000000..fa113ba --- /dev/null +++ b/mem/mem_linux.go @@ -0,0 +1,90 @@ +// +build linux + +package gopsutil + +import ( + "strconv" + "strings" + "syscall" +) + +func VirtualMemory() (*VirtualMemoryStat, error) { + filename := "/proc/meminfo" + lines, _ := readLines(filename) + + ret := &VirtualMemoryStat{} + for _, line := range lines { + fields := strings.Split(line, ":") + if len(fields) != 2 { + continue + } + key := strings.TrimSpace(fields[0]) + value := strings.TrimSpace(fields[1]) + value = strings.Replace(value, " kB", "", -1) + + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, err + } + switch key { + case "MemTotal": + ret.Total = t * 1000 + case "MemFree": + ret.Free = t * 1000 + case "Buffers": + ret.Buffers = t * 1000 + case "Cached": + ret.Cached = t * 1000 + case "Active": + ret.Active = t * 1000 + case "Inactive": + ret.Inactive = t * 1000 + } + } + ret.Available = ret.Free + ret.Buffers + ret.Cached + ret.Used = ret.Total - ret.Free + ret.UsedPercent = float64(ret.Total-ret.Available) / float64(ret.Total) * 100.0 + + return ret, nil +} + +func SwapMemory() (*SwapMemoryStat, error) { + sysinfo := &syscall.Sysinfo_t{} + + if err := syscall.Sysinfo(sysinfo); err != nil { + return nil, err + } + ret := &SwapMemoryStat{ + Total: uint64(sysinfo.Totalswap), + Free: uint64(sysinfo.Freeswap), + } + ret.Used = ret.Total - ret.Free + //check Infinity + if ret.Total != 0 { + ret.UsedPercent = float64(ret.Total-ret.Free) / float64(ret.Total) * 100.0 + } else { + ret.UsedPercent = 0 + } + lines, _ := readLines("/proc/vmstat") + for _, l := range lines { + fields := strings.Fields(l) + if len(fields) < 2 { + continue + } + switch fields[0] { + case "pswpin": + value, err := strconv.ParseUint(fields[1], 10, 64) + if err != nil { + continue + } + ret.Sin = value * 4 * 1024 + case "pswpout": + value, err := strconv.ParseUint(fields[1], 10, 64) + if err != nil { + continue + } + ret.Sout = value * 4 * 1024 + } + } + return ret, nil +} diff --git a/mem/mem_test.go b/mem/mem_test.go new file mode 100644 index 0000000..5631bdf --- /dev/null +++ b/mem/mem_test.go @@ -0,0 +1,55 @@ +package gopsutil + +import ( + "fmt" + "testing" +) + +func TestVirtual_memory(t *testing.T) { + v, err := VirtualMemory() + if err != nil { + t.Errorf("error %v", err) + } + empty := &VirtualMemoryStat{} + if v == empty { + t.Errorf("error %v", v) + } +} + +func TestSwap_memory(t *testing.T) { + v, err := SwapMemory() + if err != nil { + t.Errorf("error %v", err) + } + empty := &SwapMemoryStat{} + if v == empty { + t.Errorf("error %v", v) + } +} + +func TestVirtualMemoryStat_String(t *testing.T) { + v := VirtualMemoryStat{ + Total: 10, + Available: 20, + Used: 30, + UsedPercent: 30.1, + Free: 40, + } + e := `{"total":10,"available":20,"used":30,"usedPercent":30.1,"free":40,"active":0,"inactive":0,"buffers":0,"cached":0,"wired":0,"shared":0}` + if e != fmt.Sprintf("%v", v) { + t.Errorf("VirtualMemoryStat string is invalid: %v", v) + } +} + +func TestSwapMemoryStat_String(t *testing.T) { + v := SwapMemoryStat{ + Total: 10, + Used: 30, + Free: 40, + UsedPercent: 30.1, + } + e := `{"total":10,"used":30,"free":40,"usedPercent":30.1,"sin":0,"sout":0}` + if e != fmt.Sprintf("%v", v) { + t.Errorf("SwapMemoryStat string is invalid: %v", v) + } +} diff --git a/mem/mem_windows.go b/mem/mem_windows.go new file mode 100644 index 0000000..07d16e9 --- /dev/null +++ b/mem/mem_windows.go @@ -0,0 +1,48 @@ +// +build windows + +package gopsutil + +import ( + "syscall" + "unsafe" +) + +var ( + procGlobalMemoryStatusEx = modkernel32.NewProc("GlobalMemoryStatusEx") +) + +type MEMORYSTATUSEX struct { + cbSize uint32 + dwMemoryLoad uint32 + ullTotalPhys uint64 // in bytes + ullAvailPhys uint64 + ullTotalPageFile uint64 + ullAvailPageFile uint64 + ullTotalVirtual uint64 + ullAvailVirtual uint64 + ullAvailExtendedVirtual uint64 +} + +func VirtualMemory() (*VirtualMemoryStat, error) { + var memInfo MEMORYSTATUSEX + memInfo.cbSize = uint32(unsafe.Sizeof(memInfo)) + mem, _, _ := procGlobalMemoryStatusEx.Call(uintptr(unsafe.Pointer(&memInfo))) + if mem == 0 { + return nil, syscall.GetLastError() + } + + ret := &VirtualMemoryStat{ + Total: memInfo.ullTotalPhys, + Available: memInfo.ullAvailPhys, + UsedPercent: float64(memInfo.dwMemoryLoad), + } + + ret.Used = ret.Total - ret.Available + return ret, nil +} + +func SwapMemory() (*SwapMemoryStat, error) { + ret := &SwapMemoryStat{} + + return ret, nil +} diff --git a/mem_darwin.go b/mem_darwin.go deleted file mode 100644 index afeea81..0000000 --- a/mem_darwin.go +++ /dev/null @@ -1,104 +0,0 @@ -// +build darwin - -package gopsutil - -import ( - "os/exec" - "strconv" - "strings" -) - -func getPageSize() (uint64, error) { - out, err := exec.Command("pagesize").Output() - if err != nil { - return 0, err - } - o := strings.TrimSpace(string(out)) - p, err := strconv.ParseUint(o, 10, 64) - if err != nil { - return 0, err - } - - return p, nil -} - -// VirtualMemory returns VirtualmemoryStat. -func VirtualMemory() (*VirtualMemoryStat, error) { - p, err := getPageSize() - if err != nil { - return nil, err - } - - total, err := doSysctrl("hw.memsize") - if err != nil { - return nil, err - } - free, err := doSysctrl("vm.page_free_count") - if err != nil { - return nil, err - } - parsed := make([]uint64, 0, 7) - vv := []string{ - total[0], - free[0], - } - for _, target := range vv { - t, err := strconv.ParseUint(target, 10, 64) - if err != nil { - return nil, err - } - parsed = append(parsed, t) - } - - ret := &VirtualMemoryStat{ - Total: parsed[0] * p, - Free: parsed[1] * p, - } - - // TODO: platform independent (worked freebsd?) - ret.Available = ret.Free + ret.Buffers + ret.Cached - - ret.Used = ret.Total - ret.Free - ret.UsedPercent = float64(ret.Total-ret.Available) / float64(ret.Total) * 100.0 - - return ret, nil -} - -// SwapMemory returns swapinfo. -func SwapMemory() (*SwapMemoryStat, error) { - var ret *SwapMemoryStat - - swapUsage, err := doSysctrl("vm.swapusage") - if err != nil { - return ret, err - } - - total := strings.Replace(swapUsage[2], "M", "", 1) - used := strings.Replace(swapUsage[5], "M", "", 1) - free := strings.Replace(swapUsage[8], "M", "", 1) - - total_v, err := strconv.ParseFloat(total, 64) - if err != nil { - return nil, err - } - used_v, err := strconv.ParseFloat(used, 64) - if err != nil { - return nil, err - } - free_v, err := strconv.ParseFloat(free, 64) - if err != nil { - return nil, err - } - - u := ((total_v - free_v) / total_v) * 100.0 - - // vm.swapusage shows "M", multiply 1000 - ret = &SwapMemoryStat{ - Total: uint64(total_v * 1000), - Used: uint64(used_v * 1000), - Free: uint64(free_v * 1000), - UsedPercent: u, - } - - return ret, nil -} diff --git a/mem_freebsd.go b/mem_freebsd.go deleted file mode 100644 index 4f70ec4..0000000 --- a/mem_freebsd.go +++ /dev/null @@ -1,129 +0,0 @@ -// +build freebsd - -package gopsutil - -import ( - "os/exec" - "strconv" - "strings" -) - -func VirtualMemory() (*VirtualMemoryStat, error) { - pageSize, err := doSysctrl("vm.stats.vm.v_page_size") - if err != nil { - return nil, err - } - p, err := strconv.ParseUint(pageSize[0], 10, 64) - if err != nil { - return nil, err - } - - pageCount, err := doSysctrl("vm.stats.vm.v_page_count") - if err != nil { - return nil, err - } - free, err := doSysctrl("vm.stats.vm.v_free_count") - if err != nil { - return nil, err - } - active, err := doSysctrl("vm.stats.vm.v_active_count") - if err != nil { - return nil, err - } - inactive, err := doSysctrl("vm.stats.vm.v_inactive_count") - if err != nil { - return nil, err - } - cache, err := doSysctrl("vm.stats.vm.v_cache_count") - if err != nil { - return nil, err - } - buffer, err := doSysctrl("vfs.bufspace") - if err != nil { - return nil, err - } - wired, err := doSysctrl("vm.stats.vm.v_wire_count") - if err != nil { - return nil, err - } - - parsed := make([]uint64, 0, 7) - vv := []string{ - pageCount[0], - free[0], - active[0], - inactive[0], - cache[0], - buffer[0], - wired[0], - } - for _, target := range vv { - t, err := strconv.ParseUint(target, 10, 64) - if err != nil { - return nil, err - } - parsed = append(parsed, t) - } - - ret := &VirtualMemoryStat{ - Total: parsed[0] * p, - Free: parsed[1] * p, - Active: parsed[2] * p, - Inactive: parsed[3] * p, - Cached: parsed[4] * p, - Buffers: parsed[5], - Wired: parsed[6] * p, - } - - // TODO: platform independent (worked freebsd?) - ret.Available = ret.Free + ret.Buffers + ret.Cached - - ret.Used = ret.Total - ret.Free - ret.UsedPercent = float64(ret.Total-ret.Available) / float64(ret.Total) * 100.0 - - return ret, nil -} - -// Return swapinfo -// FreeBSD can have multiple swap devices. but use only first device -func SwapMemory() (*SwapMemoryStat, error) { - out, err := exec.Command("swapinfo").Output() - if err != nil { - return nil, err - } - var ret *SwapMemoryStat - for _, line := range strings.Split(string(out), "\n") { - values := strings.Fields(line) - // skip title line - if len(values) == 0 || values[0] == "Device" { - continue - } - - u := strings.Replace(values[4], "%", "", 1) - total_v, err := strconv.ParseUint(values[1], 10, 64) - if err != nil { - return nil, err - } - used_v, err := strconv.ParseUint(values[2], 10, 64) - if err != nil { - return nil, err - } - free_v, err := strconv.ParseUint(values[3], 10, 64) - if err != nil { - return nil, err - } - up_v, err := strconv.ParseFloat(u, 64) - if err != nil { - return nil, err - } - - ret = &SwapMemoryStat{ - Total: total_v, - Used: used_v, - Free: free_v, - UsedPercent: up_v, - } - } - - return ret, nil -} diff --git a/mem_linux.go b/mem_linux.go deleted file mode 100644 index fa113ba..0000000 --- a/mem_linux.go +++ /dev/null @@ -1,90 +0,0 @@ -// +build linux - -package gopsutil - -import ( - "strconv" - "strings" - "syscall" -) - -func VirtualMemory() (*VirtualMemoryStat, error) { - filename := "/proc/meminfo" - lines, _ := readLines(filename) - - ret := &VirtualMemoryStat{} - for _, line := range lines { - fields := strings.Split(line, ":") - if len(fields) != 2 { - continue - } - key := strings.TrimSpace(fields[0]) - value := strings.TrimSpace(fields[1]) - value = strings.Replace(value, " kB", "", -1) - - t, err := strconv.ParseUint(value, 10, 64) - if err != nil { - return ret, err - } - switch key { - case "MemTotal": - ret.Total = t * 1000 - case "MemFree": - ret.Free = t * 1000 - case "Buffers": - ret.Buffers = t * 1000 - case "Cached": - ret.Cached = t * 1000 - case "Active": - ret.Active = t * 1000 - case "Inactive": - ret.Inactive = t * 1000 - } - } - ret.Available = ret.Free + ret.Buffers + ret.Cached - ret.Used = ret.Total - ret.Free - ret.UsedPercent = float64(ret.Total-ret.Available) / float64(ret.Total) * 100.0 - - return ret, nil -} - -func SwapMemory() (*SwapMemoryStat, error) { - sysinfo := &syscall.Sysinfo_t{} - - if err := syscall.Sysinfo(sysinfo); err != nil { - return nil, err - } - ret := &SwapMemoryStat{ - Total: uint64(sysinfo.Totalswap), - Free: uint64(sysinfo.Freeswap), - } - ret.Used = ret.Total - ret.Free - //check Infinity - if ret.Total != 0 { - ret.UsedPercent = float64(ret.Total-ret.Free) / float64(ret.Total) * 100.0 - } else { - ret.UsedPercent = 0 - } - lines, _ := readLines("/proc/vmstat") - for _, l := range lines { - fields := strings.Fields(l) - if len(fields) < 2 { - continue - } - switch fields[0] { - case "pswpin": - value, err := strconv.ParseUint(fields[1], 10, 64) - if err != nil { - continue - } - ret.Sin = value * 4 * 1024 - case "pswpout": - value, err := strconv.ParseUint(fields[1], 10, 64) - if err != nil { - continue - } - ret.Sout = value * 4 * 1024 - } - } - return ret, nil -} diff --git a/mem_test.go b/mem_test.go deleted file mode 100644 index 5631bdf..0000000 --- a/mem_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package gopsutil - -import ( - "fmt" - "testing" -) - -func TestVirtual_memory(t *testing.T) { - v, err := VirtualMemory() - if err != nil { - t.Errorf("error %v", err) - } - empty := &VirtualMemoryStat{} - if v == empty { - t.Errorf("error %v", v) - } -} - -func TestSwap_memory(t *testing.T) { - v, err := SwapMemory() - if err != nil { - t.Errorf("error %v", err) - } - empty := &SwapMemoryStat{} - if v == empty { - t.Errorf("error %v", v) - } -} - -func TestVirtualMemoryStat_String(t *testing.T) { - v := VirtualMemoryStat{ - Total: 10, - Available: 20, - Used: 30, - UsedPercent: 30.1, - Free: 40, - } - e := `{"total":10,"available":20,"used":30,"usedPercent":30.1,"free":40,"active":0,"inactive":0,"buffers":0,"cached":0,"wired":0,"shared":0}` - if e != fmt.Sprintf("%v", v) { - t.Errorf("VirtualMemoryStat string is invalid: %v", v) - } -} - -func TestSwapMemoryStat_String(t *testing.T) { - v := SwapMemoryStat{ - Total: 10, - Used: 30, - Free: 40, - UsedPercent: 30.1, - } - e := `{"total":10,"used":30,"free":40,"usedPercent":30.1,"sin":0,"sout":0}` - if e != fmt.Sprintf("%v", v) { - t.Errorf("SwapMemoryStat string is invalid: %v", v) - } -} diff --git a/mem_windows.go b/mem_windows.go deleted file mode 100644 index 07d16e9..0000000 --- a/mem_windows.go +++ /dev/null @@ -1,48 +0,0 @@ -// +build windows - -package gopsutil - -import ( - "syscall" - "unsafe" -) - -var ( - procGlobalMemoryStatusEx = modkernel32.NewProc("GlobalMemoryStatusEx") -) - -type MEMORYSTATUSEX struct { - cbSize uint32 - dwMemoryLoad uint32 - ullTotalPhys uint64 // in bytes - ullAvailPhys uint64 - ullTotalPageFile uint64 - ullAvailPageFile uint64 - ullTotalVirtual uint64 - ullAvailVirtual uint64 - ullAvailExtendedVirtual uint64 -} - -func VirtualMemory() (*VirtualMemoryStat, error) { - var memInfo MEMORYSTATUSEX - memInfo.cbSize = uint32(unsafe.Sizeof(memInfo)) - mem, _, _ := procGlobalMemoryStatusEx.Call(uintptr(unsafe.Pointer(&memInfo))) - if mem == 0 { - return nil, syscall.GetLastError() - } - - ret := &VirtualMemoryStat{ - Total: memInfo.ullTotalPhys, - Available: memInfo.ullAvailPhys, - UsedPercent: float64(memInfo.dwMemoryLoad), - } - - ret.Used = ret.Total - ret.Available - return ret, nil -} - -func SwapMemory() (*SwapMemoryStat, error) { - ret := &SwapMemoryStat{} - - return ret, nil -} diff --git a/net.go b/net.go deleted file mode 100644 index de4f29f..0000000 --- a/net.go +++ /dev/null @@ -1,119 +0,0 @@ -package gopsutil - -import ( - "encoding/json" - "net" -) - -type NetIOCountersStat struct { - Name string `json:"name"` // interface name - BytesSent uint64 `json:"bytes_sent"` // number of bytes sent - BytesRecv uint64 `json:"bytes_recv"` // number of bytes received - PacketsSent uint64 `json:"packets_sent"` // number of packets sent - PacketsRecv uint64 `json:"packets_recv"` // number of packets received - Errin uint64 `json:"errin"` // total number of errors while receiving - Errout uint64 `json:"errout"` // total number of errors while sending - Dropin uint64 `json:"dropin"` // total number of incoming packets which were dropped - Dropout uint64 `json:"dropout"` // total number of outgoing packets which were dropped (always 0 on OSX and BSD) -} - -// Addr is implemented compatibility to psutil -type Addr struct { - IP string `json:"ip"` - Port uint32 `json:"port"` -} - -type NetConnectionStat struct { - Fd uint32 `json:"fd"` - Family uint32 `json:"family"` - Type uint32 `json:"type"` - Laddr Addr `json:"localaddr"` - Raddr Addr `json:"remoteaddr"` - Status string `json:"status"` - Pid int32 `json:"pid"` -} - -// NetInterfaceAddr is designed for represent interface addresses -type NetInterfaceAddr struct { - Addr string `json:"addr"` -} - -type NetInterfaceStat struct { - MTU int `json:"mtu"` // maximum transmission unit - Name string `json:"name"` // e.g., "en0", "lo0", "eth0.100" - HardwareAddr string `json:"hardwareaddr"` // IEEE MAC-48, EUI-48 and EUI-64 form - Flags []string `json:"flags"` // e.g., FlagUp, FlagLoopback, FlagMulticast - Addrs []NetInterfaceAddr `json:"addrs"` -} - -func (n NetIOCountersStat) String() string { - s, _ := json.Marshal(n) - return string(s) -} - -func (n NetConnectionStat) String() string { - s, _ := json.Marshal(n) - return string(s) -} - -func (a Addr) String() string { - s, _ := json.Marshal(a) - return string(s) -} - -func (n NetInterfaceStat) String() string { - s, _ := json.Marshal(n) - return string(s) -} - -func (n NetInterfaceAddr) String() string { - s, _ := json.Marshal(n) - return string(s) -} - -func NetInterfaces() ([]NetInterfaceStat, error) { - is, err := net.Interfaces() - if err != nil { - return nil, err - } - ret := make([]NetInterfaceStat, 0, len(is)) - for _, ifi := range is { - - var flags []string - if ifi.Flags&net.FlagUp != 0 { - flags = append(flags, "up") - } - if ifi.Flags&net.FlagBroadcast != 0 { - flags = append(flags, "broadcast") - } - if ifi.Flags&net.FlagLoopback != 0 { - flags = append(flags, "loopback") - } - if ifi.Flags&net.FlagPointToPoint != 0 { - flags = append(flags, "pointtopoint") - } - if ifi.Flags&net.FlagMulticast != 0 { - flags = append(flags, "multicast") - } - - r := NetInterfaceStat{ - Name: ifi.Name, - MTU: ifi.MTU, - HardwareAddr: ifi.HardwareAddr.String(), - Flags: flags, - } - addrs, err := ifi.Addrs() - if err == nil { - r.Addrs = make([]NetInterfaceAddr, 0, len(addrs)) - for _, addr := range addrs { - r.Addrs = append(r.Addrs, NetInterfaceAddr{ - Addr: addr.String(), - }) - } - - } - ret = append(ret, r) - } - - return ret, nil -} diff --git a/net/net.go b/net/net.go new file mode 100644 index 0000000..de4f29f --- /dev/null +++ b/net/net.go @@ -0,0 +1,119 @@ +package gopsutil + +import ( + "encoding/json" + "net" +) + +type NetIOCountersStat struct { + Name string `json:"name"` // interface name + BytesSent uint64 `json:"bytes_sent"` // number of bytes sent + BytesRecv uint64 `json:"bytes_recv"` // number of bytes received + PacketsSent uint64 `json:"packets_sent"` // number of packets sent + PacketsRecv uint64 `json:"packets_recv"` // number of packets received + Errin uint64 `json:"errin"` // total number of errors while receiving + Errout uint64 `json:"errout"` // total number of errors while sending + Dropin uint64 `json:"dropin"` // total number of incoming packets which were dropped + Dropout uint64 `json:"dropout"` // total number of outgoing packets which were dropped (always 0 on OSX and BSD) +} + +// Addr is implemented compatibility to psutil +type Addr struct { + IP string `json:"ip"` + Port uint32 `json:"port"` +} + +type NetConnectionStat struct { + Fd uint32 `json:"fd"` + Family uint32 `json:"family"` + Type uint32 `json:"type"` + Laddr Addr `json:"localaddr"` + Raddr Addr `json:"remoteaddr"` + Status string `json:"status"` + Pid int32 `json:"pid"` +} + +// NetInterfaceAddr is designed for represent interface addresses +type NetInterfaceAddr struct { + Addr string `json:"addr"` +} + +type NetInterfaceStat struct { + MTU int `json:"mtu"` // maximum transmission unit + Name string `json:"name"` // e.g., "en0", "lo0", "eth0.100" + HardwareAddr string `json:"hardwareaddr"` // IEEE MAC-48, EUI-48 and EUI-64 form + Flags []string `json:"flags"` // e.g., FlagUp, FlagLoopback, FlagMulticast + Addrs []NetInterfaceAddr `json:"addrs"` +} + +func (n NetIOCountersStat) String() string { + s, _ := json.Marshal(n) + return string(s) +} + +func (n NetConnectionStat) String() string { + s, _ := json.Marshal(n) + return string(s) +} + +func (a Addr) String() string { + s, _ := json.Marshal(a) + return string(s) +} + +func (n NetInterfaceStat) String() string { + s, _ := json.Marshal(n) + return string(s) +} + +func (n NetInterfaceAddr) String() string { + s, _ := json.Marshal(n) + return string(s) +} + +func NetInterfaces() ([]NetInterfaceStat, error) { + is, err := net.Interfaces() + if err != nil { + return nil, err + } + ret := make([]NetInterfaceStat, 0, len(is)) + for _, ifi := range is { + + var flags []string + if ifi.Flags&net.FlagUp != 0 { + flags = append(flags, "up") + } + if ifi.Flags&net.FlagBroadcast != 0 { + flags = append(flags, "broadcast") + } + if ifi.Flags&net.FlagLoopback != 0 { + flags = append(flags, "loopback") + } + if ifi.Flags&net.FlagPointToPoint != 0 { + flags = append(flags, "pointtopoint") + } + if ifi.Flags&net.FlagMulticast != 0 { + flags = append(flags, "multicast") + } + + r := NetInterfaceStat{ + Name: ifi.Name, + MTU: ifi.MTU, + HardwareAddr: ifi.HardwareAddr.String(), + Flags: flags, + } + addrs, err := ifi.Addrs() + if err == nil { + r.Addrs = make([]NetInterfaceAddr, 0, len(addrs)) + for _, addr := range addrs { + r.Addrs = append(r.Addrs, NetInterfaceAddr{ + Addr: addr.String(), + }) + } + + } + ret = append(ret, r) + } + + return ret, nil +} diff --git a/net/net_darwin.go b/net/net_darwin.go new file mode 100644 index 0000000..d65e116 --- /dev/null +++ b/net/net_darwin.go @@ -0,0 +1,61 @@ +// +build darwin + +package gopsutil + +import ( + "os/exec" + "strconv" + "strings" +) + +func NetIOCounters(pernic bool) ([]NetIOCountersStat, error) { + out, err := exec.Command("/usr/sbin/netstat", "-ibdn").Output() + if err != nil { + return nil, err + } + + lines := strings.Split(string(out), "\n") + ret := make([]NetIOCountersStat, 0, len(lines)-1) + + for _, line := range lines { + values := strings.Fields(line) + if len(values) < 1 || values[0] == "Name" { + // skip first line + continue + } + base := 1 + // sometimes Address is ommitted + if len(values) < 11 { + base = 0 + } + + parsed := make([]uint64, 0, 3) + vv := []string{ + values[base+3], // PacketsRecv + values[base+4], // Errin + values[base+5], // Dropin + } + for _, target := range vv { + if target == "-" { + parsed = append(parsed, 0) + continue + } + + t, err := strconv.ParseUint(target, 10, 64) + if err != nil { + return nil, err + } + parsed = append(parsed, t) + } + + n := NetIOCountersStat{ + Name: values[0], + PacketsRecv: parsed[0], + Errin: parsed[1], + Dropin: parsed[2], + } + ret = append(ret, n) + } + + return ret, nil +} diff --git a/net/net_freebsd.go b/net/net_freebsd.go new file mode 100644 index 0000000..bb1bd53 --- /dev/null +++ b/net/net_freebsd.go @@ -0,0 +1,70 @@ +// +build freebsd + +package gopsutil + +import ( + "os/exec" + "strconv" + "strings" +) + +func NetIOCounters(pernic bool) ([]NetIOCountersStat, error) { + out, err := exec.Command("/usr/bin/netstat", "-ibdn").Output() + if err != nil { + return nil, err + } + + lines := strings.Split(string(out), "\n") + ret := make([]NetIOCountersStat, 0, len(lines)-1) + + for _, line := range lines { + values := strings.Fields(line) + if len(values) < 1 || values[0] == "Name" { + continue + } + base := 1 + // sometimes Address is ommitted + if len(values) < 13 { + base = 0 + } + + parsed := make([]uint64, 0, 8) + vv := []string{ + values[base+3], // PacketsRecv + values[base+4], // Errin + values[base+5], // Dropin + values[base+6], // BytesRecvn + values[base+7], // PacketSent + values[base+8], // Errout + values[base+9], // BytesSent + values[base+11], // Dropout + } + for _, target := range vv { + if target == "-" { + parsed = append(parsed, 0) + continue + } + + t, err := strconv.ParseUint(target, 10, 64) + if err != nil { + return nil, err + } + parsed = append(parsed, t) + } + + n := NetIOCountersStat{ + Name: values[0], + PacketsRecv: parsed[0], + Errin: parsed[1], + Dropin: parsed[2], + BytesRecv: parsed[3], + PacketsSent: parsed[4], + Errout: parsed[5], + BytesSent: parsed[6], + Dropout: parsed[7], + } + ret = append(ret, n) + } + + return ret, nil +} diff --git a/net/net_linux.go b/net/net_linux.go new file mode 100644 index 0000000..9cc38ae --- /dev/null +++ b/net/net_linux.go @@ -0,0 +1,74 @@ +// +build linux + +package gopsutil + +import ( + "strconv" + "strings" +) + +func NetIOCounters(pernic bool) ([]NetIOCountersStat, error) { + filename := "/proc/net/dev" + lines, err := readLines(filename) + if err != nil { + return nil, err + } + + statlen := len(lines) - 1 + + ret := make([]NetIOCountersStat, 0, statlen) + + for _, line := range lines[2:] { + fields := strings.Fields(line) + if fields[0] == "" { + continue + } + + bytesRecv, err := strconv.ParseUint(fields[1], 10, 64) + if err != nil { + return ret, err + } + packetsRecv, err := strconv.ParseUint(fields[2], 10, 64) + if err != nil { + return ret, err + } + errIn, err := strconv.ParseUint(fields[3], 10, 64) + if err != nil { + return ret, err + } + dropIn, err := strconv.ParseUint(fields[4], 10, 64) + if err != nil { + return ret, err + } + bytesSent, err := strconv.ParseUint(fields[9], 10, 64) + if err != nil { + return ret, err + } + packetsSent, err := strconv.ParseUint(fields[10], 10, 64) + if err != nil { + return ret, err + } + errOut, err := strconv.ParseUint(fields[11], 10, 64) + if err != nil { + return ret, err + } + dropOut, err := strconv.ParseUint(fields[14], 10, 64) + if err != nil { + return ret, err + } + + nic := NetIOCountersStat{ + Name: strings.Trim(fields[0], ":"), + BytesRecv: bytesRecv, + PacketsRecv: packetsRecv, + Errin: errIn, + Dropin: dropIn, + BytesSent: bytesSent, + PacketsSent: packetsSent, + Errout: errOut, + Dropout: dropOut, + } + ret = append(ret, nic) + } + return ret, nil +} diff --git a/net/net_test.go b/net/net_test.go new file mode 100644 index 0000000..1d3c47a --- /dev/null +++ b/net/net_test.go @@ -0,0 +1,69 @@ +package gopsutil + +import ( + "fmt" + "testing" +) + +func TestAddrString(t *testing.T) { + v := Addr{IP: "192.168.0.1", Port: 8000} + + s := fmt.Sprintf("%v", v) + if s != "{\"ip\":\"192.168.0.1\",\"port\":8000}" { + t.Errorf("Addr string is invalid: %v", v) + } +} + +func TestNetIOCountersStatString(t *testing.T) { + v := NetIOCountersStat{ + Name: "test", + BytesSent: 100, + } + e := `{"name":"test","bytes_sent":100,"bytes_recv":0,"packets_sent":0,"packets_recv":0,"errin":0,"errout":0,"dropin":0,"dropout":0}` + if e != fmt.Sprintf("%v", v) { + t.Errorf("NetIOCountersStat string is invalid: %v", v) + } +} + +func TestNetConnectionStatString(t *testing.T) { + v := NetConnectionStat{ + Fd: 10, + Family: 10, + Type: 10, + } + e := `{"fd":10,"family":10,"type":10,"localaddr":{"ip":"","port":0},"remoteaddr":{"ip":"","port":0},"status":"","pid":0}` + if e != fmt.Sprintf("%v", v) { + t.Errorf("NetConnectionStat string is invalid: %v", v) + } + +} + +func TestNetIOCounters(t *testing.T) { + v, err := NetIOCounters(true) + if err != nil { + t.Errorf("Could not get NetIOCounters: %v", err) + } + if len(v) == 0 { + t.Errorf("Could not get NetIOCounters: %v", v) + } + for _, vv := range v { + if vv.Name == "" { + t.Errorf("Invalid NetIOCounters: %v", vv) + } + } +} + +func TestNetInterfaces(t *testing.T) { + v, err := NetInterfaces() + if err != nil { + t.Errorf("Could not get NetInterfaceStat: %v", err) + } + if len(v) == 0 { + t.Errorf("Could not get NetInterfaceStat: %v", err) + } + for _, vv := range v { + if vv.Name == "" { + t.Errorf("Invalid NetInterface: %v", vv) + } + } +} diff --git a/net/net_windows.go b/net/net_windows.go new file mode 100644 index 0000000..918f4da --- /dev/null +++ b/net/net_windows.go @@ -0,0 +1,94 @@ +// +build windows + +package gopsutil + +import ( + "net" + "os" + "syscall" + "unsafe" + + common "github.com/shirou/gopsutil/common" +) + +var ( + modiphlpapi = syscall.NewLazyDLL("iphlpapi.dll") + procGetExtendedTcpTable = modiphlpapi.NewProc("GetExtendedTcpTable") + procGetExtendedUdpTable = modiphlpapi.NewProc("GetExtendedUdpTable") +) + +const ( + TCPTableBasicListener = iota + TCPTableBasicConnections + TCPTableBasicAll + TCPTableOwnerPIDListener + TCPTableOwnerPIDConnections + TCPTableOwnerPIDAll + TCPTableOwnerModuleListener + TCPTableOwnerModuleConnections + TCPTableOwnerModuleAll +) + +func NetIOCounters(pernic bool) ([]NetIOCountersStat, error) { + ifs, err := net.Interfaces() + if err != nil { + return nil, err + } + + ai, err := getAdapterList() + if err != nil { + return nil, err + } + var ret []NetIOCountersStat + + for _, ifi := range ifs { + name := ifi.Name + for ; ai != nil; ai = ai.Next { + name = bytePtrToString(&ai.Description[0]) + c := NetIOCountersStat{ + Name: name, + } + + row := syscall.MibIfRow{Index: ai.Index} + e := syscall.GetIfEntry(&row) + if e != nil { + return nil, os.NewSyscallError("GetIfEntry", e) + } + c.BytesSent = uint64(row.OutOctets) + c.BytesRecv = uint64(row.InOctets) + c.PacketsSent = uint64(row.OutUcastPkts) + c.PacketsRecv = uint64(row.InUcastPkts) + c.Errin = uint64(row.InErrors) + c.Errout = uint64(row.OutErrors) + c.Dropin = uint64(row.InDiscards) + c.Dropout = uint64(row.OutDiscards) + + ret = append(ret, c) + } + } + return ret, nil +} + +// Return a list of network connections opened by a process +func NetConnections(kind string) ([]NetConnectionStat, error) { + var ret []NetConnectionStat + + return ret, common.NotImplementedError +} + +// borrowed from src/pkg/net/interface_windows.go +func getAdapterList() (*syscall.IpAdapterInfo, error) { + b := make([]byte, 1000) + l := uint32(len(b)) + a := (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0])) + err := syscall.GetAdaptersInfo(a, &l) + if err == syscall.ERROR_BUFFER_OVERFLOW { + b = make([]byte, l) + a = (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0])) + err = syscall.GetAdaptersInfo(a, &l) + } + if err != nil { + return nil, os.NewSyscallError("GetAdaptersInfo", err) + } + return a, nil +} diff --git a/net_darwin.go b/net_darwin.go deleted file mode 100644 index d65e116..0000000 --- a/net_darwin.go +++ /dev/null @@ -1,61 +0,0 @@ -// +build darwin - -package gopsutil - -import ( - "os/exec" - "strconv" - "strings" -) - -func NetIOCounters(pernic bool) ([]NetIOCountersStat, error) { - out, err := exec.Command("/usr/sbin/netstat", "-ibdn").Output() - if err != nil { - return nil, err - } - - lines := strings.Split(string(out), "\n") - ret := make([]NetIOCountersStat, 0, len(lines)-1) - - for _, line := range lines { - values := strings.Fields(line) - if len(values) < 1 || values[0] == "Name" { - // skip first line - continue - } - base := 1 - // sometimes Address is ommitted - if len(values) < 11 { - base = 0 - } - - parsed := make([]uint64, 0, 3) - vv := []string{ - values[base+3], // PacketsRecv - values[base+4], // Errin - values[base+5], // Dropin - } - for _, target := range vv { - if target == "-" { - parsed = append(parsed, 0) - continue - } - - t, err := strconv.ParseUint(target, 10, 64) - if err != nil { - return nil, err - } - parsed = append(parsed, t) - } - - n := NetIOCountersStat{ - Name: values[0], - PacketsRecv: parsed[0], - Errin: parsed[1], - Dropin: parsed[2], - } - ret = append(ret, n) - } - - return ret, nil -} diff --git a/net_freebsd.go b/net_freebsd.go deleted file mode 100644 index bb1bd53..0000000 --- a/net_freebsd.go +++ /dev/null @@ -1,70 +0,0 @@ -// +build freebsd - -package gopsutil - -import ( - "os/exec" - "strconv" - "strings" -) - -func NetIOCounters(pernic bool) ([]NetIOCountersStat, error) { - out, err := exec.Command("/usr/bin/netstat", "-ibdn").Output() - if err != nil { - return nil, err - } - - lines := strings.Split(string(out), "\n") - ret := make([]NetIOCountersStat, 0, len(lines)-1) - - for _, line := range lines { - values := strings.Fields(line) - if len(values) < 1 || values[0] == "Name" { - continue - } - base := 1 - // sometimes Address is ommitted - if len(values) < 13 { - base = 0 - } - - parsed := make([]uint64, 0, 8) - vv := []string{ - values[base+3], // PacketsRecv - values[base+4], // Errin - values[base+5], // Dropin - values[base+6], // BytesRecvn - values[base+7], // PacketSent - values[base+8], // Errout - values[base+9], // BytesSent - values[base+11], // Dropout - } - for _, target := range vv { - if target == "-" { - parsed = append(parsed, 0) - continue - } - - t, err := strconv.ParseUint(target, 10, 64) - if err != nil { - return nil, err - } - parsed = append(parsed, t) - } - - n := NetIOCountersStat{ - Name: values[0], - PacketsRecv: parsed[0], - Errin: parsed[1], - Dropin: parsed[2], - BytesRecv: parsed[3], - PacketsSent: parsed[4], - Errout: parsed[5], - BytesSent: parsed[6], - Dropout: parsed[7], - } - ret = append(ret, n) - } - - return ret, nil -} diff --git a/net_linux.go b/net_linux.go deleted file mode 100644 index 9cc38ae..0000000 --- a/net_linux.go +++ /dev/null @@ -1,74 +0,0 @@ -// +build linux - -package gopsutil - -import ( - "strconv" - "strings" -) - -func NetIOCounters(pernic bool) ([]NetIOCountersStat, error) { - filename := "/proc/net/dev" - lines, err := readLines(filename) - if err != nil { - return nil, err - } - - statlen := len(lines) - 1 - - ret := make([]NetIOCountersStat, 0, statlen) - - for _, line := range lines[2:] { - fields := strings.Fields(line) - if fields[0] == "" { - continue - } - - bytesRecv, err := strconv.ParseUint(fields[1], 10, 64) - if err != nil { - return ret, err - } - packetsRecv, err := strconv.ParseUint(fields[2], 10, 64) - if err != nil { - return ret, err - } - errIn, err := strconv.ParseUint(fields[3], 10, 64) - if err != nil { - return ret, err - } - dropIn, err := strconv.ParseUint(fields[4], 10, 64) - if err != nil { - return ret, err - } - bytesSent, err := strconv.ParseUint(fields[9], 10, 64) - if err != nil { - return ret, err - } - packetsSent, err := strconv.ParseUint(fields[10], 10, 64) - if err != nil { - return ret, err - } - errOut, err := strconv.ParseUint(fields[11], 10, 64) - if err != nil { - return ret, err - } - dropOut, err := strconv.ParseUint(fields[14], 10, 64) - if err != nil { - return ret, err - } - - nic := NetIOCountersStat{ - Name: strings.Trim(fields[0], ":"), - BytesRecv: bytesRecv, - PacketsRecv: packetsRecv, - Errin: errIn, - Dropin: dropIn, - BytesSent: bytesSent, - PacketsSent: packetsSent, - Errout: errOut, - Dropout: dropOut, - } - ret = append(ret, nic) - } - return ret, nil -} diff --git a/net_test.go b/net_test.go deleted file mode 100644 index 1d3c47a..0000000 --- a/net_test.go +++ /dev/null @@ -1,69 +0,0 @@ -package gopsutil - -import ( - "fmt" - "testing" -) - -func TestAddrString(t *testing.T) { - v := Addr{IP: "192.168.0.1", Port: 8000} - - s := fmt.Sprintf("%v", v) - if s != "{\"ip\":\"192.168.0.1\",\"port\":8000}" { - t.Errorf("Addr string is invalid: %v", v) - } -} - -func TestNetIOCountersStatString(t *testing.T) { - v := NetIOCountersStat{ - Name: "test", - BytesSent: 100, - } - e := `{"name":"test","bytes_sent":100,"bytes_recv":0,"packets_sent":0,"packets_recv":0,"errin":0,"errout":0,"dropin":0,"dropout":0}` - if e != fmt.Sprintf("%v", v) { - t.Errorf("NetIOCountersStat string is invalid: %v", v) - } -} - -func TestNetConnectionStatString(t *testing.T) { - v := NetConnectionStat{ - Fd: 10, - Family: 10, - Type: 10, - } - e := `{"fd":10,"family":10,"type":10,"localaddr":{"ip":"","port":0},"remoteaddr":{"ip":"","port":0},"status":"","pid":0}` - if e != fmt.Sprintf("%v", v) { - t.Errorf("NetConnectionStat string is invalid: %v", v) - } - -} - -func TestNetIOCounters(t *testing.T) { - v, err := NetIOCounters(true) - if err != nil { - t.Errorf("Could not get NetIOCounters: %v", err) - } - if len(v) == 0 { - t.Errorf("Could not get NetIOCounters: %v", v) - } - for _, vv := range v { - if vv.Name == "" { - t.Errorf("Invalid NetIOCounters: %v", vv) - } - } -} - -func TestNetInterfaces(t *testing.T) { - v, err := NetInterfaces() - if err != nil { - t.Errorf("Could not get NetInterfaceStat: %v", err) - } - if len(v) == 0 { - t.Errorf("Could not get NetInterfaceStat: %v", err) - } - for _, vv := range v { - if vv.Name == "" { - t.Errorf("Invalid NetInterface: %v", vv) - } - } -} diff --git a/net_windows.go b/net_windows.go deleted file mode 100644 index 7a72713..0000000 --- a/net_windows.go +++ /dev/null @@ -1,92 +0,0 @@ -// +build windows - -package gopsutil - -import ( - "net" - "os" - "syscall" - "unsafe" -) - -var ( - modiphlpapi = syscall.NewLazyDLL("iphlpapi.dll") - procGetExtendedTcpTable = modiphlpapi.NewProc("GetExtendedTcpTable") - procGetExtendedUdpTable = modiphlpapi.NewProc("GetExtendedUdpTable") -) - -const ( - TCPTableBasicListener = iota - TCPTableBasicConnections - TCPTableBasicAll - TCPTableOwnerPIDListener - TCPTableOwnerPIDConnections - TCPTableOwnerPIDAll - TCPTableOwnerModuleListener - TCPTableOwnerModuleConnections - TCPTableOwnerModuleAll -) - -func NetIOCounters(pernic bool) ([]NetIOCountersStat, error) { - ifs, err := net.Interfaces() - if err != nil { - return nil, err - } - - ai, err := getAdapterList() - if err != nil { - return nil, err - } - var ret []NetIOCountersStat - - for _, ifi := range ifs { - name := ifi.Name - for ; ai != nil; ai = ai.Next { - name = bytePtrToString(&ai.Description[0]) - c := NetIOCountersStat{ - Name: name, - } - - row := syscall.MibIfRow{Index: ai.Index} - e := syscall.GetIfEntry(&row) - if e != nil { - return nil, os.NewSyscallError("GetIfEntry", e) - } - c.BytesSent = uint64(row.OutOctets) - c.BytesRecv = uint64(row.InOctets) - c.PacketsSent = uint64(row.OutUcastPkts) - c.PacketsRecv = uint64(row.InUcastPkts) - c.Errin = uint64(row.InErrors) - c.Errout = uint64(row.OutErrors) - c.Dropin = uint64(row.InDiscards) - c.Dropout = uint64(row.OutDiscards) - - ret = append(ret, c) - } - } - return ret, nil -} - -// Return a list of network connections opened by a process -func NetConnections(kind string) ([]NetConnectionStat, error) { - var ret []NetConnectionStat - - return ret, NotImplementedError -} - -// borrowed from src/pkg/net/interface_windows.go -func getAdapterList() (*syscall.IpAdapterInfo, error) { - b := make([]byte, 1000) - l := uint32(len(b)) - a := (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0])) - err := syscall.GetAdaptersInfo(a, &l) - if err == syscall.ERROR_BUFFER_OVERFLOW { - b = make([]byte, l) - a = (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0])) - err = syscall.GetAdaptersInfo(a, &l) - } - if err != nil { - return nil, os.NewSyscallError("GetAdaptersInfo", err) - } - return a, nil -} diff --git a/process.go b/process.go deleted file mode 100644 index d403a58..0000000 --- a/process.go +++ /dev/null @@ -1,90 +0,0 @@ -package gopsutil - -import ( - "encoding/json" -) - -type Process struct { - Pid int32 `json:"pid"` - name string - status string - numCtxSwitches *NumCtxSwitchesStat - uids []int32 - gids []int32 - numThreads int32 - memInfo *MemoryInfoStat -} - -type OpenFilesStat struct { - Path string `json:"path"` - Fd uint64 `json:"fd"` -} - -type MemoryInfoStat struct { - RSS uint64 `json:"rss"` // bytes - VMS uint64 `json:"vms"` // bytes - Swap uint64 `json:"swap"` // bytes -} - -type RlimitStat struct { - Resource int32 `json:"resource"` - Soft int32 `json:"soft"` - Hard int32 `json:"hard"` -} - -type IOCountersStat struct { - ReadCount uint64 `json:"read_count"` - WriteCount uint64 `json:"write_count"` - ReadBytes uint64 `json:"read_bytes"` - WriteBytes uint64 `json:"write_bytes"` -} - -type NumCtxSwitchesStat struct { - Voluntary int64 `json:"voluntary"` - Involuntary int64 `json:"involuntary"` -} - -func (p Process) String() string { - s, _ := json.Marshal(p) - return string(s) -} - -func (o OpenFilesStat) String() string { - s, _ := json.Marshal(o) - return string(s) -} - -func (m MemoryInfoStat) String() string { - s, _ := json.Marshal(m) - return string(s) -} - -func (r RlimitStat) String() string { - s, _ := json.Marshal(r) - return string(s) -} - -func (i IOCountersStat) String() string { - s, _ := json.Marshal(i) - return string(s) -} - -func (p NumCtxSwitchesStat) String() string { - s, _ := json.Marshal(p) - return string(s) -} - -func PidExists(pid int32) (bool, error) { - pids, err := Pids() - if err != nil { - return false, err - } - - for _, i := range pids { - if i == pid { - return true, err - } - } - - return false, err -} diff --git a/process/process.go b/process/process.go new file mode 100644 index 0000000..d403a58 --- /dev/null +++ b/process/process.go @@ -0,0 +1,90 @@ +package gopsutil + +import ( + "encoding/json" +) + +type Process struct { + Pid int32 `json:"pid"` + name string + status string + numCtxSwitches *NumCtxSwitchesStat + uids []int32 + gids []int32 + numThreads int32 + memInfo *MemoryInfoStat +} + +type OpenFilesStat struct { + Path string `json:"path"` + Fd uint64 `json:"fd"` +} + +type MemoryInfoStat struct { + RSS uint64 `json:"rss"` // bytes + VMS uint64 `json:"vms"` // bytes + Swap uint64 `json:"swap"` // bytes +} + +type RlimitStat struct { + Resource int32 `json:"resource"` + Soft int32 `json:"soft"` + Hard int32 `json:"hard"` +} + +type IOCountersStat struct { + ReadCount uint64 `json:"read_count"` + WriteCount uint64 `json:"write_count"` + ReadBytes uint64 `json:"read_bytes"` + WriteBytes uint64 `json:"write_bytes"` +} + +type NumCtxSwitchesStat struct { + Voluntary int64 `json:"voluntary"` + Involuntary int64 `json:"involuntary"` +} + +func (p Process) String() string { + s, _ := json.Marshal(p) + return string(s) +} + +func (o OpenFilesStat) String() string { + s, _ := json.Marshal(o) + return string(s) +} + +func (m MemoryInfoStat) String() string { + s, _ := json.Marshal(m) + return string(s) +} + +func (r RlimitStat) String() string { + s, _ := json.Marshal(r) + return string(s) +} + +func (i IOCountersStat) String() string { + s, _ := json.Marshal(i) + return string(s) +} + +func (p NumCtxSwitchesStat) String() string { + s, _ := json.Marshal(p) + return string(s) +} + +func PidExists(pid int32) (bool, error) { + pids, err := Pids() + if err != nil { + return false, err + } + + for _, i := range pids { + if i == pid { + return true, err + } + } + + return false, err +} diff --git a/process/process_darwin.go b/process/process_darwin.go new file mode 100644 index 0000000..ab64886 --- /dev/null +++ b/process/process_darwin.go @@ -0,0 +1,307 @@ +// +build darwin + +package gopsutil + +import ( + "bytes" + "encoding/binary" + "syscall" + "unsafe" + + common "github.com/shirou/gopsutil/common" + cpu "github.com/shirou/gopsutil/cpu" + net "github.com/shirou/gopsutil/net" +) + +// MemoryInfoExStat is different between OSes +type MemoryInfoExStat struct { +} + +type MemoryMapsStat struct { +} + +func Pids() ([]int32, error) { + var ret []int32 + procs, err := processes() + if err != nil { + return ret, nil + } + + for _, p := range procs { + ret = append(ret, p.Pid) + } + + return ret, nil +} + +func (p *Process) Ppid() (int32, error) { + return 0, common.NotImplementedError + + k, err := p.getKProc() + if err != nil { + return 0, err + } + + return k.KiPpid, nil +} +func (p *Process) Name() (string, error) { + k, err := p.getKProc() + if err != nil { + return "", err + } + + return string(k.KiComm[:]), nil +} +func (p *Process) Exe() (string, error) { + return "", common.NotImplementedError +} +func (p *Process) Cmdline() (string, error) { + return "", common.NotImplementedError +} +func (p *Process) CreateTime() (int64, error) { + return 0, common.NotImplementedError +} +func (p *Process) Cwd() (string, error) { + return "", common.NotImplementedError +} +func (p *Process) Parent() (*Process, error) { + return p, common.NotImplementedError +} +func (p *Process) Status() (string, error) { + k, err := p.getKProc() + if err != nil { + return "", err + } + + return string(k.KiStat[:]), nil +} +func (p *Process) Uids() ([]int32, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + + uids := make([]int32, 0, 3) + + uids = append(uids, int32(k.KiRuid), int32(k.KiUID), int32(k.KiSvuid)) + + return uids, nil +} +func (p *Process) Gids() ([]int32, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + + gids := make([]int32, 0, 3) + gids = append(gids, int32(k.KiRgid), int32(k.KiNgroups[0]), int32(k.KiSvuid)) + + return gids, nil +} +func (p *Process) Terminal() (string, error) { + k, err := p.getKProc() + if err != nil { + return "", err + } + + ttyNr := uint64(k.KiTdev) + + termmap, err := getTerminalMap() + if err != nil { + return "", err + } + + return termmap[ttyNr], nil +} +func (p *Process) Nice() (int32, error) { + return 0, common.NotImplementedError +} +func (p *Process) IOnice() (int32, error) { + return 0, common.NotImplementedError +} +func (p *Process) Rlimit() ([]RlimitStat, error) { + var rlimit []RlimitStat + return rlimit, common.NotImplementedError +} +func (p *Process) IOCounters() (*IOCountersStat, error) { + return nil, common.NotImplementedError +} +func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) { + return nil, common.NotImplementedError +} +func (p *Process) NumFDs() (int32, error) { + return 0, common.NotImplementedError +} +func (p *Process) NumThreads() (int32, error) { + k, err := p.getKProc() + if err != nil { + return 0, err + } + + return k.KiNumthreads, nil +} +func (p *Process) Threads() (map[string]string, error) { + ret := make(map[string]string, 0) + return ret, common.NotImplementedError +} +func (p *Process) CPUTimes() (*cpu.CPUTimesStat, error) { + return nil, common.NotImplementedError +} +func (p *Process) CPUPercent() (int32, error) { + return 0, common.NotImplementedError +} +func (p *Process) CPUAffinity() ([]int32, error) { + return nil, common.NotImplementedError +} +func (p *Process) MemoryInfo() (*MemoryInfoStat, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + + ret := &MemoryInfoStat{ + RSS: uint64(k.KiRssize), + VMS: uint64(k.KiSize), + } + + return ret, nil +} +func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) { + return nil, common.NotImplementedError +} +func (p *Process) MemoryPercent() (float32, error) { + return 0, common.NotImplementedError +} + +func (p *Process) Children() ([]*Process, error) { + return nil, common.NotImplementedError +} + +func (p *Process) OpenFiles() ([]OpenFilesStat, error) { + return nil, common.NotImplementedError +} + +func (p *Process) Connections() ([]net.NetConnectionStat, error) { + return nil, common.NotImplementedError +} + +func (p *Process) IsRunning() (bool, error) { + return true, common.NotImplementedError +} +func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) { + var ret []MemoryMapsStat + return &ret, common.NotImplementedError +} + +func copyParams(k *KinfoProc, p *Process) error { + + return nil +} + +func processes() ([]Process, error) { + results := make([]Process, 0, 50) + + mib := []int32{CTLKern, KernProc, KernProcProc, 0} + buf, length, err := callSyscall(mib) + if err != nil { + return results, err + } + + // get kinfo_proc size + k := KinfoProc{} + procinfoLen := int(unsafe.Sizeof(k)) + count := int(length / uint64(procinfoLen)) + + // parse buf to procs + for i := 0; i < count; i++ { + b := buf[i*procinfoLen : i*procinfoLen+procinfoLen] + k, err := parseKinfoProc(b) + if err != nil { + continue + } + p, err := NewProcess(int32(k.KiPid)) + if err != nil { + continue + } + copyParams(&k, p) + + results = append(results, *p) + } + + return results, nil +} + +func parseKinfoProc(buf []byte) (KinfoProc, error) { + var k KinfoProc + br := bytes.NewReader(buf) + err := binary.Read(br, binary.LittleEndian, &k) + if err != nil { + return k, err + } + + return k, nil +} + +func callSyscall(mib []int32) ([]byte, uint64, error) { + miblen := uint64(len(mib)) + + // get required buffer size + length := uint64(0) + _, _, err := syscall.Syscall6( + syscall.SYS___SYSCTL, + uintptr(unsafe.Pointer(&mib[0])), + uintptr(miblen), + 0, + uintptr(unsafe.Pointer(&length)), + 0, + 0) + if err != 0 { + var b []byte + return b, length, err + } + if length == 0 { + var b []byte + return b, length, err + } + // get proc info itself + buf := make([]byte, length) + _, _, err = syscall.Syscall6( + syscall.SYS___SYSCTL, + uintptr(unsafe.Pointer(&mib[0])), + uintptr(miblen), + uintptr(unsafe.Pointer(&buf[0])), + uintptr(unsafe.Pointer(&length)), + 0, + 0) + if err != 0 { + return buf, length, err + } + + return buf, length, nil +} + +func (p *Process) getKProc() (*KinfoProc, error) { + mib := []int32{CTLKern, KernProc, KernProcPID, p.Pid} + + buf, length, err := callSyscall(mib) + if err != nil { + return nil, err + } + procK := KinfoProc{} + if length != uint64(unsafe.Sizeof(procK)) { + return nil, err + } + + k, err := parseKinfoProc(buf) + if err != nil { + return nil, err + } + + return &k, nil +} + +func NewProcess(pid int32) (*Process, error) { + p := &Process{Pid: pid} + + return p, nil +} diff --git a/process/process_darwin_amd64.go b/process/process_darwin_amd64.go new file mode 100644 index 0000000..8fe4d6f --- /dev/null +++ b/process/process_darwin_amd64.go @@ -0,0 +1,96 @@ +// +build darwin +// +build amd64 + +package gopsutil + +// copied from sys/sysctl.h +const ( + CTLKern = 1 // "high kernel": proc, limits + KernProc = 14 // struct: process entries + KernProcPID = 1 // by process id + KernProcProc = 8 // only return procs + KernProcPathname = 12 // path to executable +) + +// copied from sys/user.h +type KinfoProc struct { + KiStructsize int32 + KiLayout int32 + KiArgs int64 + KiPaddr int64 + KiAddr int64 + KiTracep int64 + KiTextvp int64 + KiFd int64 + KiVmspace int64 + KiWchan int64 + KiPid int32 + KiPpid int32 + KiPgid int32 + KiTpgid int32 + KiSid int32 + KiTsid int32 + KiJobc [2]byte + KiSpareShort1 [2]byte + KiTdev int32 + KiSiglist [16]byte + KiSigmask [16]byte + KiSigignore [16]byte + KiSigcatch [16]byte + KiUID int32 + KiRuid int32 + KiSvuid int32 + KiRgid int32 + KiSvgid int32 + KiNgroups [2]byte + KiSpareShort2 [2]byte + KiGroups [64]byte + KiSize int64 + KiRssize int64 + KiSwrss int64 + KiTsize int64 + KiDsize int64 + KiSsize int64 + KiXstat [2]byte + KiAcflag [2]byte + KiPctcpu int32 + KiEstcpu int32 + KiSlptime int32 + KiSwtime int32 + KiCow int32 + KiRuntime int64 + KiStart [16]byte + KiChildtime [16]byte + KiFlag int64 + KiKflag int64 + KiTraceflag int32 + KiStat [1]byte + KiNice [1]byte + KiLock [1]byte + KiRqindex [1]byte + KiOncpu [1]byte + KiLastcpu [1]byte + KiOcomm [17]byte + KiWmesg [9]byte + KiLogin [18]byte + KiLockname [9]byte + KiComm [20]byte + KiEmul [17]byte + KiSparestrings [68]byte + KiSpareints [36]byte + KiCrFlags int32 + KiJid int32 + KiNumthreads int32 + KiTid int32 + KiPri int32 + KiRusage [144]byte + KiRusageCh [144]byte + KiPcb int64 + KiKstack int64 + KiUdata int64 + KiTdaddr int64 + KiSpareptrs [48]byte + KiSpareint64s [96]byte + KiSflag int64 + KiTdflags int64 +} diff --git a/process/process_freebsd.go b/process/process_freebsd.go new file mode 100644 index 0000000..95f76c7 --- /dev/null +++ b/process/process_freebsd.go @@ -0,0 +1,305 @@ +// +build freebsd + +package gopsutil + +import ( + "bytes" + "encoding/binary" + "syscall" + "unsafe" + + common "github.com/shirou/gopsutil/common" + cpu "github.com/shirou/gopsutil/cpu" + net "github.com/shirou/gopsutil/net" +) + +// MemoryInfoExStat is different between OSes +type MemoryInfoExStat struct { +} + +type MemoryMapsStat struct { +} + +func Pids() ([]int32, error) { + var ret []int32 + procs, err := processes() + if err != nil { + return ret, nil + } + + for _, p := range procs { + ret = append(ret, p.Pid) + } + + return ret, nil +} + +func (p *Process) Ppid() (int32, error) { + k, err := p.getKProc() + if err != nil { + return 0, err + } + + return k.KiPpid, nil +} +func (p *Process) Name() (string, error) { + k, err := p.getKProc() + if err != nil { + return "", err + } + + return string(k.KiComm[:]), nil +} +func (p *Process) Exe() (string, error) { + return "", common.NotImplementedError +} +func (p *Process) Cmdline() (string, error) { + return "", common.NotImplementedError +} +func (p *Process) CreateTime() (int64, error) { + return 0, common.NotImplementedError +} +func (p *Process) Cwd() (string, error) { + return "", common.NotImplementedError +} +func (p *Process) Parent() (*Process, error) { + return p, common.NotImplementedError +} +func (p *Process) Status() (string, error) { + k, err := p.getKProc() + if err != nil { + return "", err + } + + return string(k.KiStat[:]), nil +} +func (p *Process) Uids() ([]int32, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + + uids := make([]int32, 0, 3) + + uids = append(uids, int32(k.KiRuid), int32(k.KiUID), int32(k.KiSvuid)) + + return uids, nil +} +func (p *Process) Gids() ([]int32, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + + gids := make([]int32, 0, 3) + gids = append(gids, int32(k.KiRgid), int32(k.KiNgroups[0]), int32(k.KiSvuid)) + + return gids, nil +} +func (p *Process) Terminal() (string, error) { + k, err := p.getKProc() + if err != nil { + return "", err + } + + ttyNr := uint64(k.KiTdev) + + termmap, err := getTerminalMap() + if err != nil { + return "", err + } + + return termmap[ttyNr], nil +} +func (p *Process) Nice() (int32, error) { + return 0, common.NotImplementedError +} +func (p *Process) IOnice() (int32, error) { + return 0, common.NotImplementedError +} +func (p *Process) Rlimit() ([]RlimitStat, error) { + var rlimit []RlimitStat + return rlimit, common.NotImplementedError +} +func (p *Process) IOCounters() (*IOCountersStat, error) { + return nil, common.NotImplementedError +} +func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) { + return nil, common.NotImplementedError +} +func (p *Process) NumFDs() (int32, error) { + return 0, common.NotImplementedError +} +func (p *Process) NumThreads() (int32, error) { + k, err := p.getKProc() + if err != nil { + return 0, err + } + + return k.KiNumthreads, nil +} +func (p *Process) Threads() (map[string]string, error) { + ret := make(map[string]string, 0) + return ret, common.NotImplementedError +} +func (p *Process) CPUTimes() (*cpu.CPUTimesStat, error) { + return nil, common.NotImplementedError +} +func (p *Process) CPUPercent() (int32, error) { + return 0, common.NotImplementedError +} +func (p *Process) CPUAffinity() ([]int32, error) { + return nil, common.NotImplementedError +} +func (p *Process) MemoryInfo() (*MemoryInfoStat, error) { + k, err := p.getKProc() + if err != nil { + return nil, err + } + + ret := &MemoryInfoStat{ + RSS: uint64(k.KiRssize), + VMS: uint64(k.KiSize), + } + + return ret, nil +} +func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) { + return nil, common.NotImplementedError +} +func (p *Process) MemoryPercent() (float32, error) { + return 0, common.NotImplementedError +} + +func (p *Process) Children() ([]*Process, error) { + return nil, common.NotImplementedError +} + +func (p *Process) OpenFiles() ([]OpenFilesStat, error) { + return nil, common.NotImplementedError +} + +func (p *Process) Connections() ([]net.NetConnectionStat, error) { + return nil, common.NotImplementedError +} + +func (p *Process) IsRunning() (bool, error) { + return true, common.NotImplementedError +} +func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) { + var ret []MemoryMapsStat + return &ret, common.NotImplementedError +} + +func copyParams(k *KinfoProc, p *Process) error { + + return nil +} + +func processes() ([]Process, error) { + results := make([]Process, 0, 50) + + mib := []int32{CTLKern, KernProc, KernProcProc, 0} + buf, length, err := callSyscall(mib) + if err != nil { + return results, err + } + + // get kinfo_proc size + k := KinfoProc{} + procinfoLen := int(unsafe.Sizeof(k)) + count := int(length / uint64(procinfoLen)) + + // parse buf to procs + for i := 0; i < count; i++ { + b := buf[i*procinfoLen : i*procinfoLen+procinfoLen] + k, err := parseKinfoProc(b) + if err != nil { + continue + } + p, err := NewProcess(int32(k.KiPid)) + if err != nil { + continue + } + copyParams(&k, p) + + results = append(results, *p) + } + + return results, nil +} + +func parseKinfoProc(buf []byte) (KinfoProc, error) { + var k KinfoProc + br := bytes.NewReader(buf) + err := binary.Read(br, binary.LittleEndian, &k) + if err != nil { + return k, err + } + + return k, nil +} + +func callSyscall(mib []int32) ([]byte, uint64, error) { + miblen := uint64(len(mib)) + + // get required buffer size + length := uint64(0) + _, _, err := syscall.Syscall6( + syscall.SYS___SYSCTL, + uintptr(unsafe.Pointer(&mib[0])), + uintptr(miblen), + 0, + uintptr(unsafe.Pointer(&length)), + 0, + 0) + if err != 0 { + var b []byte + return b, length, err + } + if length == 0 { + var b []byte + return b, length, err + } + // get proc info itself + buf := make([]byte, length) + _, _, err = syscall.Syscall6( + syscall.SYS___SYSCTL, + uintptr(unsafe.Pointer(&mib[0])), + uintptr(miblen), + uintptr(unsafe.Pointer(&buf[0])), + uintptr(unsafe.Pointer(&length)), + 0, + 0) + if err != 0 { + return buf, length, err + } + + return buf, length, nil +} + +func (p *Process) getKProc() (*KinfoProc, error) { + mib := []int32{CTLKern, KernProc, KernProcPID, p.Pid} + + buf, length, err := callSyscall(mib) + if err != nil { + return nil, err + } + procK := KinfoProc{} + if length != uint64(unsafe.Sizeof(procK)) { + return nil, err + } + + k, err := parseKinfoProc(buf) + if err != nil { + return nil, err + } + + return &k, nil +} + +func NewProcess(pid int32) (*Process, error) { + p := &Process{Pid: pid} + + return p, nil +} diff --git a/process/process_freebsd_amd64.go b/process/process_freebsd_amd64.go new file mode 100644 index 0000000..73bbab4 --- /dev/null +++ b/process/process_freebsd_amd64.go @@ -0,0 +1,96 @@ +// +build freebsd +// +build amd64 + +package gopsutil + +// copied from sys/sysctl.h +const ( + CTLKern = 1 // "high kernel": proc, limits + KernProc = 14 // struct: process entries + KernProcPID = 1 // by process id + KernProcProc = 8 // only return procs + KernProcPathname = 12 // path to executable +) + +// copied from sys/user.h +type KinfoProc struct { + KiStructsize int32 + KiLayout int32 + KiArgs int64 + KiPaddr int64 + KiAddr int64 + KiTracep int64 + KiTextvp int64 + KiFd int64 + KiVmspace int64 + KiWchan int64 + KiPid int32 + KiPpid int32 + KiPgid int32 + KiTpgid int32 + KiSid int32 + KiTsid int32 + KiJobc [2]byte + KiSpareShort1 [2]byte + KiTdev int32 + KiSiglist [16]byte + KiSigmask [16]byte + KiSigignore [16]byte + KiSigcatch [16]byte + KiUID int32 + KiRuid int32 + KiSvuid int32 + KiRgid int32 + KiSvgid int32 + KiNgroups [2]byte + KiSpareShort2 [2]byte + KiGroups [64]byte + KiSize int64 + KiRssize int64 + KiSwrss int64 + KiTsize int64 + KiDsize int64 + KiSsize int64 + KiXstat [2]byte + KiAcflag [2]byte + KiPctcpu int32 + KiEstcpu int32 + KiSlptime int32 + KiSwtime int32 + KiCow int32 + KiRuntime int64 + KiStart [16]byte + KiChildtime [16]byte + KiFlag int64 + KiKflag int64 + KiTraceflag int32 + KiStat [1]byte + KiNice [1]byte + KiLock [1]byte + KiRqindex [1]byte + KiOncpu [1]byte + KiLastcpu [1]byte + KiOcomm [17]byte + KiWmesg [9]byte + KiLogin [18]byte + KiLockname [9]byte + KiComm [20]byte + KiEmul [17]byte + KiSparestrings [68]byte + KiSpareints [36]byte + KiCrFlags int32 + KiJid int32 + KiNumthreads int32 + KiTid int32 + KiPri int32 + KiRusage [144]byte + KiRusageCh [144]byte + KiPcb int64 + KiKstack int64 + KiUdata int64 + KiTdaddr int64 + KiSpareptrs [48]byte + KiSpareint64s [96]byte + KiSflag int64 + KiTdflags int64 +} diff --git a/process/process_linux.go b/process/process_linux.go new file mode 100644 index 0000000..ede8f3f --- /dev/null +++ b/process/process_linux.go @@ -0,0 +1,598 @@ +// +build linux + +package gopsutil + +import ( + "encoding/json" + "io/ioutil" + "os" + "path/filepath" + "strconv" + "strings" + "syscall" + + common "github.com/shirou/gopsutil/common" + cpu "github.com/shirou/gopsutil/cpu" + net "github.com/shirou/gopsutil/net" +) + +const ( + PrioProcess = 0 // linux/resource.h +) + +// MemoryInfoExStat is different between OSes +type MemoryInfoExStat struct { + RSS uint64 `json:"rss"` // bytes + VMS uint64 `json:"vms"` // bytes + Shared uint64 `json:"shared"` // bytes + Text uint64 `json:"text"` // bytes + Lib uint64 `json:"lib"` // bytes + Data uint64 `json:"data"` // bytes + Dirty uint64 `json:"dirty"` // bytes +} + +func (m MemoryInfoExStat) String() string { + s, _ := json.Marshal(m) + return string(s) +} + +type MemoryMapsStat struct { + Path string `json:"path"` + Rss uint64 `json:"rss"` + Size uint64 `json:"size"` + Pss uint64 `json:"pss"` + SharedClean uint64 `json:"shared_clean"` + SharedDirty uint64 `json:"shared_dirty"` + PrivateClean uint64 `json:"private_clean"` + PrivateDirty uint64 `json:"private_dirty"` + Referenced uint64 `json:"referenced"` + Anonymous uint64 `json:"anonymous"` + Swap uint64 `json:"swap"` +} + +func (m MemoryMapsStat) String() string { + s, _ := json.Marshal(m) + return string(s) +} + +// Create new Process instance +// This only stores Pid +func NewProcess(pid int32) (*Process, error) { + p := &Process{ + Pid: int32(pid), + } + err := p.fillFromStatus() + return p, err +} + +func (p *Process) Ppid() (int32, error) { + _, ppid, _, _, _, err := p.fillFromStat() + if err != nil { + return -1, err + } + return ppid, nil +} +func (p *Process) Name() (string, error) { + return p.name, nil +} +func (p *Process) Exe() (string, error) { + return p.fillFromExe() +} +func (p *Process) Cmdline() (string, error) { + return p.fillFromCmdline() +} +func (p *Process) CreateTime() (int64, error) { + _, _, _, createTime, _, err := p.fillFromStat() + if err != nil { + return 0, err + } + return createTime, nil +} + +func (p *Process) Cwd() (string, error) { + return p.fillFromCwd() +} +func (p *Process) Parent() (*Process, error) { + return nil, common.NotImplementedError +} +func (p *Process) Status() (string, error) { + return p.status, nil +} +func (p *Process) Uids() ([]int32, error) { + return p.uids, nil +} +func (p *Process) Gids() ([]int32, error) { + return p.gids, nil +} +func (p *Process) Terminal() (string, error) { + terminal, _, _, _, _, err := p.fillFromStat() + if err != nil { + return "", err + } + return terminal, nil +} +func (p *Process) Nice() (int32, error) { + _, _, _, _, nice, err := p.fillFromStat() + if err != nil { + return 0, err + } + return nice, nil +} +func (p *Process) IOnice() (int32, error) { + return 0, common.NotImplementedError +} +func (p *Process) Rlimit() ([]RlimitStat, error) { + return nil, common.NotImplementedError +} +func (p *Process) IOCounters() (*IOCountersStat, error) { + return p.fillFromIO() +} +func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) { + return p.numCtxSwitches, nil +} +func (p *Process) NumFDs() (int32, error) { + return 0, common.NotImplementedError +} +func (p *Process) NumThreads() (int32, error) { + return p.numThreads, nil +} +func (p *Process) Threads() (map[string]string, error) { + ret := make(map[string]string, 0) + return ret, nil +} +func (p *Process) CPUTimes() (*cpu.CPUTimesStat, error) { + _, _, cpuTimes, _, _, err := p.fillFromStat() + if err != nil { + return nil, err + } + return cpuTimes, nil +} +func (p *Process) CPUPercent() (int32, error) { + return 0, common.NotImplementedError +} +func (p *Process) CPUAffinity() ([]int32, error) { + return nil, common.NotImplementedError +} +func (p *Process) MemoryInfo() (*MemoryInfoStat, error) { + return p.memInfo, nil +} +func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) { + _, memInfoEx, err := p.fillFromStatm() + if err != nil { + return nil, err + } + return memInfoEx, nil +} +func (p *Process) MemoryPercent() (float32, error) { + return 0, common.NotImplementedError +} + +func (p *Process) Children() ([]*Process, error) { + return nil, common.NotImplementedError +} + +func (p *Process) OpenFiles() ([]OpenFilesStat, error) { + return nil, common.NotImplementedError +} + +func (p *Process) Connections() ([]net.NetConnectionStat, error) { + return nil, common.NotImplementedError +} + +func (p *Process) IsRunning() (bool, error) { + return true, common.NotImplementedError +} + +// MemoryMaps get memory maps from /proc/(pid)/smaps +func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) { + pid := p.Pid + var ret []MemoryMapsStat + smapsPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "smaps") + contents, err := ioutil.ReadFile(smapsPath) + if err != nil { + return nil, err + } + lines := strings.Split(string(contents), "\n") + + // function of parsing a block + getBlock := func(first_line []string, block []string) (MemoryMapsStat, error) { + m := MemoryMapsStat{} + m.Path = first_line[len(first_line)-1] + + for _, line := range block { + if strings.Contains(line, "VmFlags") { + continue + } + field := strings.Split(line, ":") + if len(field) < 2 { + continue + } + v := strings.Trim(field[1], " kB") // remove last "kB" + t, err := strconv.ParseUint(v, 10, 64) + if err != nil { + return m, err + } + + switch field[0] { + case "Size": + m.Size = t + case "Rss": + m.Rss = t + case "Pss": + m.Pss = t + case "Shared_Clean": + m.SharedClean = t + case "Shared_Dirty": + m.SharedDirty = t + case "Private_Clean": + m.PrivateClean = t + case "Private_Dirty": + m.PrivateDirty = t + case "Referenced": + m.Referenced = t + case "Anonymous": + m.Anonymous = t + case "Swap": + m.Swap = t + } + } + return m, nil + } + + blocks := make([]string, 16) + for _, line := range lines { + field := strings.Split(line, " ") + if strings.HasSuffix(field[0], ":") == false { + // new block section + if len(blocks) > 0 { + g, err := getBlock(field, blocks) + if err != nil { + return &ret, err + } + ret = append(ret, g) + } + // starts new block + blocks = make([]string, 16) + } else { + blocks = append(blocks, line) + } + } + + return &ret, nil +} + +/** +** Internal functions +**/ + +// Get num_fds from /proc/(pid)/fd +func (p *Process) fillFromfd() (int32, []*OpenFilesStat, error) { + pid := p.Pid + statPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "fd") + d, err := os.Open(statPath) + if err != nil { + return 0, nil, err + } + defer d.Close() + fnames, err := d.Readdirnames(-1) + numFDs := int32(len(fnames)) + + openfiles := make([]*OpenFilesStat, numFDs) + for _, fd := range fnames { + fpath := filepath.Join(statPath, fd) + filepath, err := os.Readlink(fpath) + if err != nil { + continue + } + t, err := strconv.ParseUint(fd, 10, 64) + if err != nil { + return numFDs, openfiles, err + } + o := &OpenFilesStat{ + Path: filepath, + Fd: t, + } + openfiles = append(openfiles, o) + } + + return numFDs, openfiles, nil +} + +// Get cwd from /proc/(pid)/cwd +func (p *Process) fillFromCwd() (string, error) { + pid := p.Pid + cwdPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "cwd") + cwd, err := os.Readlink(cwdPath) + if err != nil { + return "", err + } + return string(cwd), nil +} + +// Get exe from /proc/(pid)/exe +func (p *Process) fillFromExe() (string, error) { + pid := p.Pid + exePath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "exe") + exe, err := os.Readlink(exePath) + if err != nil { + return "", err + } + return string(exe), nil +} + +// Get cmdline from /proc/(pid)/cmdline +func (p *Process) fillFromCmdline() (string, error) { + pid := p.Pid + cmdPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "cmdline") + cmdline, err := ioutil.ReadFile(cmdPath) + if err != nil { + return "", err + } + ret := strings.FieldsFunc(string(cmdline), func(r rune) bool { + if r == '\u0000' { + return true + } + return false + }) + + return strings.Join(ret, " "), nil +} + +// Get IO status from /proc/(pid)/io +func (p *Process) fillFromIO() (*IOCountersStat, error) { + pid := p.Pid + ioPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "io") + ioline, err := ioutil.ReadFile(ioPath) + if err != nil { + return nil, err + } + lines := strings.Split(string(ioline), "\n") + ret := &IOCountersStat{} + + for _, line := range lines { + field := strings.Fields(line) + if len(field) < 2 { + continue + } + t, err := strconv.ParseUint(field[1], 10, 64) + if err != nil { + return nil, err + } + param := field[0] + if strings.HasSuffix(param, ":") { + param = param[:len(param)-1] + } + switch param { + case "syscr": + ret.ReadCount = t + case "syscw": + ret.WriteCount = t + case "read_bytes": + ret.ReadBytes = t + case "write_bytes": + ret.WriteBytes = t + } + } + + return ret, nil +} + +// Get memory info from /proc/(pid)/statm +func (p *Process) fillFromStatm() (*MemoryInfoStat, *MemoryInfoExStat, error) { + pid := p.Pid + memPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "statm") + contents, err := ioutil.ReadFile(memPath) + if err != nil { + return nil, nil, err + } + fields := strings.Split(string(contents), " ") + + vms, err := strconv.ParseUint(fields[0], 10, 64) + if err != nil { + return nil, nil, err + } + rss, err := strconv.ParseUint(fields[1], 10, 64) + if err != nil { + return nil, nil, err + } + memInfo := &MemoryInfoStat{ + RSS: rss * PageSize, + VMS: vms * PageSize, + } + + shared, err := strconv.ParseUint(fields[2], 10, 64) + if err != nil { + return nil, nil, err + } + text, err := strconv.ParseUint(fields[3], 10, 64) + if err != nil { + return nil, nil, err + } + lib, err := strconv.ParseUint(fields[4], 10, 64) + if err != nil { + return nil, nil, err + } + dirty, err := strconv.ParseUint(fields[5], 10, 64) + if err != nil { + return nil, nil, err + } + + memInfoEx := &MemoryInfoExStat{ + RSS: rss * PageSize, + VMS: vms * PageSize, + Shared: shared * PageSize, + Text: text * PageSize, + Lib: lib * PageSize, + Dirty: dirty * PageSize, + } + + return memInfo, memInfoEx, nil +} + +// Get various status from /proc/(pid)/status +func (p *Process) fillFromStatus() error { + pid := p.Pid + statPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "status") + contents, err := ioutil.ReadFile(statPath) + if err != nil { + return err + } + lines := strings.Split(string(contents), "\n") + p.numCtxSwitches = &NumCtxSwitchesStat{} + p.memInfo = &MemoryInfoStat{} + for _, line := range lines { + tabParts := strings.SplitN(line, "\t", 2) + if len(tabParts) < 2 { + continue + } + value := tabParts[1] + switch strings.TrimRight(tabParts[0], ":") { + case "Name": + p.name = strings.Trim(value, " \t") + case "State": + // get between "(" and ")" + s := strings.Index(value, "(") + 1 + e := strings.Index(value, "(") + 1 + p.status = value[s:e] + case "Uid": + p.uids = make([]int32, 0, 4) + for _, i := range strings.Split(value, "\t") { + v, err := strconv.ParseInt(i, 10, 32) + if err != nil { + return err + } + p.uids = append(p.uids, int32(v)) + } + case "Gid": + p.gids = make([]int32, 0, 4) + for _, i := range strings.Split(value, "\t") { + v, err := strconv.ParseInt(i, 10, 32) + if err != nil { + return err + } + p.gids = append(p.gids, int32(v)) + } + case "Threads": + v, err := strconv.ParseInt(value, 10, 32) + if err != nil { + return err + } + p.numThreads = int32(v) + case "voluntary_ctxt_switches": + v, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return err + } + p.numCtxSwitches.Voluntary = v + case "nonvoluntary_ctxt_switches": + v, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return err + } + p.numCtxSwitches.Involuntary = v + case "VmRSS": + value := strings.Trim(value, " kB") // remove last "kB" + v, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return err + } + p.memInfo.RSS = v * 1024 + case "VmSize": + value := strings.Trim(value, " kB") // remove last "kB" + v, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return err + } + p.memInfo.VMS = v * 1024 + case "VmSwap": + value := strings.Trim(value, " kB") // remove last "kB" + v, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return err + } + p.memInfo.Swap = v * 1024 + } + + } + return nil +} + +func (p *Process) fillFromStat() (string, int32, *cpu.CPUTimesStat, int64, int32, error) { + pid := p.Pid + statPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "stat") + contents, err := ioutil.ReadFile(statPath) + if err != nil { + return "", 0, nil, 0, 0, err + } + fields := strings.Fields(string(contents)) + + termmap, err := getTerminalMap() + terminal := "" + if err == nil { + t, err := strconv.ParseUint(fields[6], 10, 64) + if err != nil { + return "", 0, nil, 0, 0, err + } + terminal = termmap[t] + } + + ppid, err := strconv.ParseInt(fields[3], 10, 32) + if err != nil { + return "", 0, nil, 0, 0, err + } + utime, err := strconv.ParseFloat(fields[13], 64) + if err != nil { + return "", 0, nil, 0, 0, err + } + stime, err := strconv.ParseFloat(fields[14], 64) + if err != nil { + return "", 0, nil, 0, 0, err + } + + cpuTimes := &cpu.CPUTimesStat{ + CPU: "cpu", + User: float32(utime * (1000 / ClockTicks)), + System: float32(stime * (1000 / ClockTicks)), + } + + bootTime, _ := BootTime() + t, err := strconv.ParseUint(fields[21], 10, 64) + if err != nil { + return "", 0, nil, 0, 0, err + } + + ctime := ((t / uint64(ClockTicks)) + uint64(bootTime)) * 1000 + createTime := int64(ctime) + + // p.Nice = mustParseInt32(fields[18]) + // use syscall instead of parse Stat file + snice, _ := syscall.Getpriority(PrioProcess, int(pid)) + nice := int32(snice) // FIXME: is this true? + + return terminal, int32(ppid), cpuTimes, createTime, nice, nil +} + +func Pids() ([]int32, error) { + var ret []int32 + + d, err := os.Open("/proc") + if err != nil { + return nil, err + } + defer d.Close() + + fnames, err := d.Readdirnames(-1) + if err != nil { + return nil, err + } + for _, fname := range fnames { + pid, err := strconv.ParseInt(fname, 10, 32) + if err != nil { + // if not numeric name, just skip + continue + } + ret = append(ret, int32(pid)) + } + + return ret, nil +} diff --git a/process/process_linux_amd64.go b/process/process_linux_amd64.go new file mode 100644 index 0000000..29554db --- /dev/null +++ b/process/process_linux_amd64.go @@ -0,0 +1,9 @@ +// +build linux +// +build amd64 + +package gopsutil + +const ( + ClockTicks = 100 // C.sysconf(C._SC_CLK_TCK) + PageSize = 4096 // C.sysconf(C._SC_PAGE_SIZE) +) diff --git a/process/process_linux_arm.go b/process/process_linux_arm.go new file mode 100644 index 0000000..4f915bb --- /dev/null +++ b/process/process_linux_arm.go @@ -0,0 +1,9 @@ +// +build linux +// +build arm + +package gopsutil + +const ( + ClockTicks = 100 // C.sysconf(C._SC_CLK_TCK) + PageSize = 4096 // C.sysconf(C._SC_PAGE_SIZE) +) diff --git a/process/process_posix.go b/process/process_posix.go new file mode 100644 index 0000000..4e2625a --- /dev/null +++ b/process/process_posix.go @@ -0,0 +1,102 @@ +// +build linux freebsd darwin + +package gopsutil + +import ( + "os" + "os/exec" + "os/user" + "strconv" + "strings" + "syscall" +) + +// POSIX +func getTerminalMap() (map[uint64]string, error) { + ret := make(map[uint64]string) + var termfiles []string + + d, err := os.Open("/dev") + if err != nil { + return nil, err + } + defer d.Close() + + devnames, err := d.Readdirnames(-1) + for _, devname := range devnames { + if strings.HasPrefix(devname, "/dev/tty") { + termfiles = append(termfiles, "/dev/tty/"+devname) + } + } + + ptsd, err := os.Open("/dev/pts") + if err != nil { + return nil, err + } + defer ptsd.Close() + + ptsnames, err := ptsd.Readdirnames(-1) + for _, ptsname := range ptsnames { + termfiles = append(termfiles, "/dev/pts/"+ptsname) + } + + for _, name := range termfiles { + stat := syscall.Stat_t{} + if err = syscall.Stat(name, &stat); err != nil { + return nil, err + } + rdev := uint64(stat.Rdev) + ret[rdev] = strings.Replace(name, "/dev", "", -1) + } + return ret, nil +} + +func (p *Process) SendSignal(sig syscall.Signal) error { + sigAsStr := "INT" + switch sig { + case syscall.SIGSTOP: + sigAsStr = "STOP" + case syscall.SIGCONT: + sigAsStr = "CONT" + case syscall.SIGTERM: + sigAsStr = "TERM" + case syscall.SIGKILL: + sigAsStr = "KILL" + } + + cmd := exec.Command("kill", "-s", sigAsStr, strconv.Itoa(int(p.Pid))) + cmd.Stderr = os.Stderr + err := cmd.Run() + if err != nil { + return err + } + + return nil +} + +func (p *Process) Suspend() error { + return p.SendSignal(syscall.SIGSTOP) +} +func (p *Process) Resume() error { + return p.SendSignal(syscall.SIGCONT) +} +func (p *Process) Terminate() error { + return p.SendSignal(syscall.SIGTERM) +} +func (p *Process) Kill() error { + return p.SendSignal(syscall.SIGKILL) +} +func (p *Process) Username() (string, error) { + uids, err := p.Uids() + if err != nil { + return "", err + } + if len(uids) > 0 { + u, err := user.LookupId(strconv.Itoa(int(uids[0]))) + if err != nil { + return "", err + } + return u.Username, nil + } + return "", nil +} diff --git a/process/process_posix_test.go b/process/process_posix_test.go new file mode 100644 index 0000000..b45418a --- /dev/null +++ b/process/process_posix_test.go @@ -0,0 +1,19 @@ +// +build linux freebsd + +package gopsutil + +import ( + "os" + "syscall" + "testing" +) + +func Test_SendSignal(t *testing.T) { + checkPid := os.Getpid() + + p, _ := NewProcess(int32(checkPid)) + err := p.SendSignal(syscall.SIGCONT) + if err != nil { + t.Errorf("send signal %v", err) + } +} diff --git a/process/process_test.go b/process/process_test.go new file mode 100644 index 0000000..2a14d12 --- /dev/null +++ b/process/process_test.go @@ -0,0 +1,118 @@ +package gopsutil + +import ( + "os" + "runtime" + "testing" +) + +func testGetProcess() Process { + checkPid := os.Getpid() + if runtime.GOOS == "windows" { + checkPid = 7960 + } + ret, _ := NewProcess(int32(checkPid)) + return *ret +} + +func Test_Pids(t *testing.T) { + ret, err := Pids() + if err != nil { + t.Errorf("error %v", err) + } + if len(ret) == 0 { + t.Errorf("could not get pids %v", ret) + } +} + +func Test_Pid_exists(t *testing.T) { + checkPid := 1 + if runtime.GOOS == "windows" { + checkPid = 0 + } + + ret, err := PidExists(int32(checkPid)) + if err != nil { + t.Errorf("error %v", err) + } + + if ret == false { + t.Errorf("could not get init process %v", ret) + } +} + +func Test_NewProcess(t *testing.T) { + checkPid := 1 + if runtime.GOOS == "windows" { + checkPid = 0 + } + + ret, err := NewProcess(int32(checkPid)) + if err != nil { + t.Errorf("error %v", err) + } + empty := &Process{} + if runtime.GOOS != "windows" { // Windows pid is 0 + if empty == ret { + t.Errorf("error %v", ret) + } + } + +} + +func Test_Process_memory_maps(t *testing.T) { + checkPid := os.Getpid() + if runtime.GOOS == "windows" { + checkPid = 0 + } + ret, err := NewProcess(int32(checkPid)) + + mmaps, err := ret.MemoryMaps(false) + if err != nil { + t.Errorf("memory map get error %v", err) + } + empty := MemoryMapsStat{} + for _, m := range *mmaps { + if m == empty { + t.Errorf("memory map get error %v", m) + } + } + +} + +func Test_Process_Ppid(t *testing.T) { + p := testGetProcess() + + v, err := p.Ppid() + if err != nil { + t.Errorf("geting ppid error %v", err) + } + if v == 0 { + t.Errorf("return value is 0 %v", v) + } + +} + +func Test_Process_IOCounters(t *testing.T) { + p := testGetProcess() + + v, err := p.IOCounters() + if err != nil { + t.Errorf("geting ppid error %v", err) + return + } + empty := &IOCountersStat{} + if v == empty { + t.Errorf("error %v", v) + } +} + +func Test_Process_NumCtx(t *testing.T) { + p := testGetProcess() + + _, err := p.NumCtxSwitches() + if err != nil { + t.Errorf("geting numctx error %v", err) + return + } +} diff --git a/process/process_windows.go b/process/process_windows.go new file mode 100644 index 0000000..7424f81 --- /dev/null +++ b/process/process_windows.go @@ -0,0 +1,262 @@ +// +build windows + +package gopsutil + +import ( + "errors" + "syscall" + "unsafe" + + "github.com/shirou/w32" + + common "github.com/shirou/gopsutil/common" +) + +const ( + NoMoreFiles = 0x12 + MaxPathLength = 260 +) + +type SystemProcessInformation struct { + NextEntryOffset uint64 + NumberOfThreads uint64 + Reserved1 [48]byte + Reserved2 [3]byte + UniqueProcessID uintptr + Reserved3 uintptr + HandleCount uint64 + Reserved4 [4]byte + Reserved5 [11]byte + PeakPagefileUsage uint64 + PrivatePageCount uint64 + Reserved6 [6]uint64 +} + +// Memory_info_ex is different between OSes +type MemoryInfoExStat struct { +} + +type MemoryMapsStat struct { +} + +func Pids() ([]int32, error) { + + var ret []int32 + + procs, err := processes() + if err != nil { + return ret, nil + } + for _, proc := range procs { + ret = append(ret, proc.Pid) + } + + return ret, nil +} + +func (p *Process) Ppid() (int32, error) { + ret, _, _, err := p.getFromSnapProcess(p.Pid) + if err != nil { + return 0, err + } + return ret, nil +} +func (p *Process) Name() (string, error) { + name := "" + return name, common.NotImplementedError +} +func (p *Process) Exe() (string, error) { + _, _, ret, err := p.getFromSnapProcess(p.Pid) + if err != nil { + return "", err + } + return ret, nil +} +func (p *Process) Cmdline() (string, error) { + return "", common.NotImplementedError +} +func (p *Process) Cwd() (string, error) { + return "", common.NotImplementedError +} +func (p *Process) Parent() (*Process, error) { + return p, common.NotImplementedError +} +func (p *Process) Status() (string, error) { + return "", common.NotImplementedError +} +func (p *Process) Username() (string, error) { + return "", common.NotImplementedError +} +func (p *Process) Uids() ([]int32, error) { + var uids []int32 + + return uids, common.NotImplementedError +} +func (p *Process) Gids() ([]int32, error) { + var gids []int32 + return gids, common.NotImplementedError +} +func (p *Process) Terminal() (string, error) { + return "", common.NotImplementedError +} +func (p *Process) Nice() (int32, error) { + return 0, common.NotImplementedError +} +func (p *Process) IOnice() (int32, error) { + return 0, common.NotImplementedError +} +func (p *Process) Rlimit() ([]RlimitStat, error) { + var rlimit []RlimitStat + + return rlimit, common.NotImplementedError +} +func (p *Process) IOCounters() (*IOCountersStat, error) { + return nil, common.NotImplementedError +} +func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) { + return nil, common.NotImplementedError +} +func (p *Process) NumFDs() (int32, error) { + return 0, common.NotImplementedError +} +func (p *Process) NumThreads() (int32, error) { + _, ret, _, err := p.getFromSnapProcess(p.Pid) + if err != nil { + return 0, err + } + return ret, nil +} +func (p *Process) Threads() (map[string]string, error) { + ret := make(map[string]string, 0) + return ret, common.NotImplementedError +} +func (p *Process) CPUTimes() (*CPUTimesStat, error) { + return nil, common.NotImplementedError +} +func (p *Process) CPUPercent() (int32, error) { + return 0, common.NotImplementedError +} +func (p *Process) CPUAffinity() ([]int32, error) { + return nil, common.NotImplementedError +} +func (p *Process) MemoryInfo() (*MemoryInfoStat, error) { + return nil, common.NotImplementedError +} +func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) { + return nil, common.NotImplementedError +} +func (p *Process) MemoryPercent() (float32, error) { + return 0, common.NotImplementedError +} + +func (p *Process) Children() ([]*Process, error) { + return nil, common.NotImplementedError +} + +func (p *Process) OpenFiles() ([]OpenFilesStat, error) { + return nil, common.NotImplementedError +} + +func (p *Process) Connections() ([]NetConnectionStat, error) { + return nil, common.NotImplementedError +} + +func (p *Process) IsRunning() (bool, error) { + return true, common.NotImplementedError +} + +func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) { + ret := make([]MemoryMapsStat, 0) + return &ret, common.NotImplementedError +} + +func NewProcess(pid int32) (*Process, error) { + p := &Process{Pid: pid} + + return p, nil +} + +func (p *Process) SendSignal(sig syscall.Signal) error { + return common.NotImplementedError +} + +func (p *Process) Suspend() error { + return common.NotImplementedError +} +func (p *Process) Resume() error { + return common.NotImplementedError +} +func (p *Process) Terminate() error { + return common.NotImplementedError +} +func (p *Process) Kill() error { + return common.NotImplementedError +} + +func (p *Process) getFromSnapProcess(pid int32) (int32, int32, string, error) { + snap := w32.CreateToolhelp32Snapshot(w32.TH32CS_SNAPPROCESS, uint32(pid)) + if snap == 0 { + return 0, 0, "", syscall.GetLastError() + } + defer w32.CloseHandle(snap) + var pe32 w32.PROCESSENTRY32 + pe32.DwSize = uint32(unsafe.Sizeof(pe32)) + if w32.Process32First(snap, &pe32) == false { + return 0, 0, "", syscall.GetLastError() + } + + if pe32.Th32ProcessID == uint32(pid) { + szexe := syscall.UTF16ToString(pe32.SzExeFile[:]) + return int32(pe32.Th32ParentProcessID), int32(pe32.CntThreads), szexe, nil + } + + for w32.Process32Next(snap, &pe32) { + if pe32.Th32ProcessID == uint32(pid) { + szexe := syscall.UTF16ToString(pe32.SzExeFile[:]) + return int32(pe32.Th32ParentProcessID), int32(pe32.CntThreads), szexe, nil + } + } + return 0, 0, "", errors.New("Couldn't find pid:" + string(pid)) +} + +// Get processes +func processes() ([]*Process, error) { + ps := make([]uint32, 255) + var read uint32 + if w32.EnumProcesses(ps, uint32(len(ps)), &read) == false { + return nil, syscall.GetLastError() + } + + var results []*Process + dwardSize := uint32(4) + for _, pid := range ps[:read/dwardSize] { + if pid == 0 { + continue + } + p, err := NewProcess(int32(pid)) + if err != nil { + break + } + results = append(results, p) + } + + return results, nil +} + +func getProcInfo(pid int32) (*SystemProcessInformation, error) { + initialBufferSize := uint64(0x4000) + bufferSize := initialBufferSize + buffer := make([]byte, bufferSize) + + var sysProcInfo SystemProcessInformation + ret, _, _ := procNtQuerySystemInformation.Call( + uintptr(unsafe.Pointer(&sysProcInfo)), + uintptr(unsafe.Pointer(&buffer[0])), + uintptr(unsafe.Pointer(&bufferSize)), + uintptr(unsafe.Pointer(&bufferSize))) + if ret != 0 { + return nil, syscall.GetLastError() + } + + return &sysProcInfo, nil +} diff --git a/process_darwin.go b/process_darwin.go deleted file mode 100644 index 5e1299f..0000000 --- a/process_darwin.go +++ /dev/null @@ -1,303 +0,0 @@ -// +build darwin - -package gopsutil - -import ( - "bytes" - "encoding/binary" - "syscall" - "unsafe" -) - -// MemoryInfoExStat is different between OSes -type MemoryInfoExStat struct { -} - -type MemoryMapsStat struct { -} - -func Pids() ([]int32, error) { - var ret []int32 - procs, err := processes() - if err != nil { - return ret, nil - } - - for _, p := range procs { - ret = append(ret, p.Pid) - } - - return ret, nil -} - -func (p *Process) Ppid() (int32, error) { - return 0, NotImplementedError - - k, err := p.getKProc() - if err != nil { - return 0, err - } - - return k.KiPpid, nil -} -func (p *Process) Name() (string, error) { - k, err := p.getKProc() - if err != nil { - return "", err - } - - return string(k.KiComm[:]), nil -} -func (p *Process) Exe() (string, error) { - return "", NotImplementedError -} -func (p *Process) Cmdline() (string, error) { - return "", NotImplementedError -} -func (p *Process) CreateTime() (int64, error) { - return 0, NotImplementedError -} -func (p *Process) Cwd() (string, error) { - return "", NotImplementedError -} -func (p *Process) Parent() (*Process, error) { - return p, NotImplementedError -} -func (p *Process) Status() (string, error) { - k, err := p.getKProc() - if err != nil { - return "", err - } - - return string(k.KiStat[:]), nil -} -func (p *Process) Uids() ([]int32, error) { - k, err := p.getKProc() - if err != nil { - return nil, err - } - - uids := make([]int32, 0, 3) - - uids = append(uids, int32(k.KiRuid), int32(k.KiUID), int32(k.KiSvuid)) - - return uids, nil -} -func (p *Process) Gids() ([]int32, error) { - k, err := p.getKProc() - if err != nil { - return nil, err - } - - gids := make([]int32, 0, 3) - gids = append(gids, int32(k.KiRgid), int32(k.KiNgroups[0]), int32(k.KiSvuid)) - - return gids, nil -} -func (p *Process) Terminal() (string, error) { - k, err := p.getKProc() - if err != nil { - return "", err - } - - ttyNr := uint64(k.KiTdev) - - termmap, err := getTerminalMap() - if err != nil { - return "", err - } - - return termmap[ttyNr], nil -} -func (p *Process) Nice() (int32, error) { - return 0, NotImplementedError -} -func (p *Process) IOnice() (int32, error) { - return 0, NotImplementedError -} -func (p *Process) Rlimit() ([]RlimitStat, error) { - var rlimit []RlimitStat - return rlimit, NotImplementedError -} -func (p *Process) IOCounters() (*IOCountersStat, error) { - return nil, NotImplementedError -} -func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) { - return nil, NotImplementedError -} -func (p *Process) NumFDs() (int32, error) { - return 0, NotImplementedError -} -func (p *Process) NumThreads() (int32, error) { - k, err := p.getKProc() - if err != nil { - return 0, err - } - - return k.KiNumthreads, nil -} -func (p *Process) Threads() (map[string]string, error) { - ret := make(map[string]string, 0) - return ret, NotImplementedError -} -func (p *Process) CPUTimes() (*CPUTimesStat, error) { - return nil, NotImplementedError -} -func (p *Process) CPUPercent() (int32, error) { - return 0, NotImplementedError -} -func (p *Process) CPUAffinity() ([]int32, error) { - return nil, NotImplementedError -} -func (p *Process) MemoryInfo() (*MemoryInfoStat, error) { - k, err := p.getKProc() - if err != nil { - return nil, err - } - - ret := &MemoryInfoStat{ - RSS: uint64(k.KiRssize), - VMS: uint64(k.KiSize), - } - - return ret, nil -} -func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) { - return nil, NotImplementedError -} -func (p *Process) MemoryPercent() (float32, error) { - return 0, NotImplementedError -} - -func (p *Process) Children() ([]*Process, error) { - return nil, NotImplementedError -} - -func (p *Process) OpenFiles() ([]OpenFilesStat, error) { - return nil, NotImplementedError -} - -func (p *Process) Connections() ([]NetConnectionStat, error) { - return nil, NotImplementedError -} - -func (p *Process) IsRunning() (bool, error) { - return true, NotImplementedError -} -func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) { - var ret []MemoryMapsStat - return &ret, NotImplementedError -} - -func copyParams(k *KinfoProc, p *Process) error { - - return nil -} - -func processes() ([]Process, error) { - results := make([]Process, 0, 50) - - mib := []int32{CTLKern, KernProc, KernProcProc, 0} - buf, length, err := callSyscall(mib) - if err != nil { - return results, err - } - - // get kinfo_proc size - k := KinfoProc{} - procinfoLen := int(unsafe.Sizeof(k)) - count := int(length / uint64(procinfoLen)) - - // parse buf to procs - for i := 0; i < count; i++ { - b := buf[i*procinfoLen : i*procinfoLen+procinfoLen] - k, err := parseKinfoProc(b) - if err != nil { - continue - } - p, err := NewProcess(int32(k.KiPid)) - if err != nil { - continue - } - copyParams(&k, p) - - results = append(results, *p) - } - - return results, nil -} - -func parseKinfoProc(buf []byte) (KinfoProc, error) { - var k KinfoProc - br := bytes.NewReader(buf) - err := binary.Read(br, binary.LittleEndian, &k) - if err != nil { - return k, err - } - - return k, nil -} - -func callSyscall(mib []int32) ([]byte, uint64, error) { - miblen := uint64(len(mib)) - - // get required buffer size - length := uint64(0) - _, _, err := syscall.Syscall6( - syscall.SYS___SYSCTL, - uintptr(unsafe.Pointer(&mib[0])), - uintptr(miblen), - 0, - uintptr(unsafe.Pointer(&length)), - 0, - 0) - if err != 0 { - var b []byte - return b, length, err - } - if length == 0 { - var b []byte - return b, length, err - } - // get proc info itself - buf := make([]byte, length) - _, _, err = syscall.Syscall6( - syscall.SYS___SYSCTL, - uintptr(unsafe.Pointer(&mib[0])), - uintptr(miblen), - uintptr(unsafe.Pointer(&buf[0])), - uintptr(unsafe.Pointer(&length)), - 0, - 0) - if err != 0 { - return buf, length, err - } - - return buf, length, nil -} - -func (p *Process) getKProc() (*KinfoProc, error) { - mib := []int32{CTLKern, KernProc, KernProcPID, p.Pid} - - buf, length, err := callSyscall(mib) - if err != nil { - return nil, err - } - procK := KinfoProc{} - if length != uint64(unsafe.Sizeof(procK)) { - return nil, err - } - - k, err := parseKinfoProc(buf) - if err != nil { - return nil, err - } - - return &k, nil -} - -func NewProcess(pid int32) (*Process, error) { - p := &Process{Pid: pid} - - return p, nil -} diff --git a/process_darwin_amd64.go b/process_darwin_amd64.go deleted file mode 100644 index 8fe4d6f..0000000 --- a/process_darwin_amd64.go +++ /dev/null @@ -1,96 +0,0 @@ -// +build darwin -// +build amd64 - -package gopsutil - -// copied from sys/sysctl.h -const ( - CTLKern = 1 // "high kernel": proc, limits - KernProc = 14 // struct: process entries - KernProcPID = 1 // by process id - KernProcProc = 8 // only return procs - KernProcPathname = 12 // path to executable -) - -// copied from sys/user.h -type KinfoProc struct { - KiStructsize int32 - KiLayout int32 - KiArgs int64 - KiPaddr int64 - KiAddr int64 - KiTracep int64 - KiTextvp int64 - KiFd int64 - KiVmspace int64 - KiWchan int64 - KiPid int32 - KiPpid int32 - KiPgid int32 - KiTpgid int32 - KiSid int32 - KiTsid int32 - KiJobc [2]byte - KiSpareShort1 [2]byte - KiTdev int32 - KiSiglist [16]byte - KiSigmask [16]byte - KiSigignore [16]byte - KiSigcatch [16]byte - KiUID int32 - KiRuid int32 - KiSvuid int32 - KiRgid int32 - KiSvgid int32 - KiNgroups [2]byte - KiSpareShort2 [2]byte - KiGroups [64]byte - KiSize int64 - KiRssize int64 - KiSwrss int64 - KiTsize int64 - KiDsize int64 - KiSsize int64 - KiXstat [2]byte - KiAcflag [2]byte - KiPctcpu int32 - KiEstcpu int32 - KiSlptime int32 - KiSwtime int32 - KiCow int32 - KiRuntime int64 - KiStart [16]byte - KiChildtime [16]byte - KiFlag int64 - KiKflag int64 - KiTraceflag int32 - KiStat [1]byte - KiNice [1]byte - KiLock [1]byte - KiRqindex [1]byte - KiOncpu [1]byte - KiLastcpu [1]byte - KiOcomm [17]byte - KiWmesg [9]byte - KiLogin [18]byte - KiLockname [9]byte - KiComm [20]byte - KiEmul [17]byte - KiSparestrings [68]byte - KiSpareints [36]byte - KiCrFlags int32 - KiJid int32 - KiNumthreads int32 - KiTid int32 - KiPri int32 - KiRusage [144]byte - KiRusageCh [144]byte - KiPcb int64 - KiKstack int64 - KiUdata int64 - KiTdaddr int64 - KiSpareptrs [48]byte - KiSpareint64s [96]byte - KiSflag int64 - KiTdflags int64 -} diff --git a/process_freebsd.go b/process_freebsd.go deleted file mode 100644 index 101b3fc..0000000 --- a/process_freebsd.go +++ /dev/null @@ -1,301 +0,0 @@ -// +build freebsd - -package gopsutil - -import ( - "bytes" - "encoding/binary" - "syscall" - "unsafe" -) - -// MemoryInfoExStat is different between OSes -type MemoryInfoExStat struct { -} - -type MemoryMapsStat struct { -} - -func Pids() ([]int32, error) { - var ret []int32 - procs, err := processes() - if err != nil { - return ret, nil - } - - for _, p := range procs { - ret = append(ret, p.Pid) - } - - return ret, nil -} - -func (p *Process) Ppid() (int32, error) { - k, err := p.getKProc() - if err != nil { - return 0, err - } - - return k.KiPpid, nil -} -func (p *Process) Name() (string, error) { - k, err := p.getKProc() - if err != nil { - return "", err - } - - return string(k.KiComm[:]), nil -} -func (p *Process) Exe() (string, error) { - return "", NotImplementedError -} -func (p *Process) Cmdline() (string, error) { - return "", NotImplementedError -} -func (p *Process) CreateTime() (int64, error) { - return 0, NotImplementedError -} -func (p *Process) Cwd() (string, error) { - return "", NotImplementedError -} -func (p *Process) Parent() (*Process, error) { - return p, NotImplementedError -} -func (p *Process) Status() (string, error) { - k, err := p.getKProc() - if err != nil { - return "", err - } - - return string(k.KiStat[:]), nil -} -func (p *Process) Uids() ([]int32, error) { - k, err := p.getKProc() - if err != nil { - return nil, err - } - - uids := make([]int32, 0, 3) - - uids = append(uids, int32(k.KiRuid), int32(k.KiUID), int32(k.KiSvuid)) - - return uids, nil -} -func (p *Process) Gids() ([]int32, error) { - k, err := p.getKProc() - if err != nil { - return nil, err - } - - gids := make([]int32, 0, 3) - gids = append(gids, int32(k.KiRgid), int32(k.KiNgroups[0]), int32(k.KiSvuid)) - - return gids, nil -} -func (p *Process) Terminal() (string, error) { - k, err := p.getKProc() - if err != nil { - return "", err - } - - ttyNr := uint64(k.KiTdev) - - termmap, err := getTerminalMap() - if err != nil { - return "", err - } - - return termmap[ttyNr], nil -} -func (p *Process) Nice() (int32, error) { - return 0, NotImplementedError -} -func (p *Process) IOnice() (int32, error) { - return 0, NotImplementedError -} -func (p *Process) Rlimit() ([]RlimitStat, error) { - var rlimit []RlimitStat - return rlimit, NotImplementedError -} -func (p *Process) IOCounters() (*IOCountersStat, error) { - return nil, NotImplementedError -} -func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) { - return nil, NotImplementedError -} -func (p *Process) NumFDs() (int32, error) { - return 0, NotImplementedError -} -func (p *Process) NumThreads() (int32, error) { - k, err := p.getKProc() - if err != nil { - return 0, err - } - - return k.KiNumthreads, nil -} -func (p *Process) Threads() (map[string]string, error) { - ret := make(map[string]string, 0) - return ret, NotImplementedError -} -func (p *Process) CPUTimes() (*CPUTimesStat, error) { - return nil, NotImplementedError -} -func (p *Process) CPUPercent() (int32, error) { - return 0, NotImplementedError -} -func (p *Process) CPUAffinity() ([]int32, error) { - return nil, NotImplementedError -} -func (p *Process) MemoryInfo() (*MemoryInfoStat, error) { - k, err := p.getKProc() - if err != nil { - return nil, err - } - - ret := &MemoryInfoStat{ - RSS: uint64(k.KiRssize), - VMS: uint64(k.KiSize), - } - - return ret, nil -} -func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) { - return nil, NotImplementedError -} -func (p *Process) MemoryPercent() (float32, error) { - return 0, NotImplementedError -} - -func (p *Process) Children() ([]*Process, error) { - return nil, NotImplementedError -} - -func (p *Process) OpenFiles() ([]OpenFilesStat, error) { - return nil, NotImplementedError -} - -func (p *Process) Connections() ([]NetConnectionStat, error) { - return nil, NotImplementedError -} - -func (p *Process) IsRunning() (bool, error) { - return true, NotImplementedError -} -func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) { - var ret []MemoryMapsStat - return &ret, NotImplementedError -} - -func copyParams(k *KinfoProc, p *Process) error { - - return nil -} - -func processes() ([]Process, error) { - results := make([]Process, 0, 50) - - mib := []int32{CTLKern, KernProc, KernProcProc, 0} - buf, length, err := callSyscall(mib) - if err != nil { - return results, err - } - - // get kinfo_proc size - k := KinfoProc{} - procinfoLen := int(unsafe.Sizeof(k)) - count := int(length / uint64(procinfoLen)) - - // parse buf to procs - for i := 0; i < count; i++ { - b := buf[i*procinfoLen : i*procinfoLen+procinfoLen] - k, err := parseKinfoProc(b) - if err != nil { - continue - } - p, err := NewProcess(int32(k.KiPid)) - if err != nil { - continue - } - copyParams(&k, p) - - results = append(results, *p) - } - - return results, nil -} - -func parseKinfoProc(buf []byte) (KinfoProc, error) { - var k KinfoProc - br := bytes.NewReader(buf) - err := binary.Read(br, binary.LittleEndian, &k) - if err != nil { - return k, err - } - - return k, nil -} - -func callSyscall(mib []int32) ([]byte, uint64, error) { - miblen := uint64(len(mib)) - - // get required buffer size - length := uint64(0) - _, _, err := syscall.Syscall6( - syscall.SYS___SYSCTL, - uintptr(unsafe.Pointer(&mib[0])), - uintptr(miblen), - 0, - uintptr(unsafe.Pointer(&length)), - 0, - 0) - if err != 0 { - var b []byte - return b, length, err - } - if length == 0 { - var b []byte - return b, length, err - } - // get proc info itself - buf := make([]byte, length) - _, _, err = syscall.Syscall6( - syscall.SYS___SYSCTL, - uintptr(unsafe.Pointer(&mib[0])), - uintptr(miblen), - uintptr(unsafe.Pointer(&buf[0])), - uintptr(unsafe.Pointer(&length)), - 0, - 0) - if err != 0 { - return buf, length, err - } - - return buf, length, nil -} - -func (p *Process) getKProc() (*KinfoProc, error) { - mib := []int32{CTLKern, KernProc, KernProcPID, p.Pid} - - buf, length, err := callSyscall(mib) - if err != nil { - return nil, err - } - procK := KinfoProc{} - if length != uint64(unsafe.Sizeof(procK)) { - return nil, err - } - - k, err := parseKinfoProc(buf) - if err != nil { - return nil, err - } - - return &k, nil -} - -func NewProcess(pid int32) (*Process, error) { - p := &Process{Pid: pid} - - return p, nil -} diff --git a/process_freebsd_amd64.go b/process_freebsd_amd64.go deleted file mode 100644 index 73bbab4..0000000 --- a/process_freebsd_amd64.go +++ /dev/null @@ -1,96 +0,0 @@ -// +build freebsd -// +build amd64 - -package gopsutil - -// copied from sys/sysctl.h -const ( - CTLKern = 1 // "high kernel": proc, limits - KernProc = 14 // struct: process entries - KernProcPID = 1 // by process id - KernProcProc = 8 // only return procs - KernProcPathname = 12 // path to executable -) - -// copied from sys/user.h -type KinfoProc struct { - KiStructsize int32 - KiLayout int32 - KiArgs int64 - KiPaddr int64 - KiAddr int64 - KiTracep int64 - KiTextvp int64 - KiFd int64 - KiVmspace int64 - KiWchan int64 - KiPid int32 - KiPpid int32 - KiPgid int32 - KiTpgid int32 - KiSid int32 - KiTsid int32 - KiJobc [2]byte - KiSpareShort1 [2]byte - KiTdev int32 - KiSiglist [16]byte - KiSigmask [16]byte - KiSigignore [16]byte - KiSigcatch [16]byte - KiUID int32 - KiRuid int32 - KiSvuid int32 - KiRgid int32 - KiSvgid int32 - KiNgroups [2]byte - KiSpareShort2 [2]byte - KiGroups [64]byte - KiSize int64 - KiRssize int64 - KiSwrss int64 - KiTsize int64 - KiDsize int64 - KiSsize int64 - KiXstat [2]byte - KiAcflag [2]byte - KiPctcpu int32 - KiEstcpu int32 - KiSlptime int32 - KiSwtime int32 - KiCow int32 - KiRuntime int64 - KiStart [16]byte - KiChildtime [16]byte - KiFlag int64 - KiKflag int64 - KiTraceflag int32 - KiStat [1]byte - KiNice [1]byte - KiLock [1]byte - KiRqindex [1]byte - KiOncpu [1]byte - KiLastcpu [1]byte - KiOcomm [17]byte - KiWmesg [9]byte - KiLogin [18]byte - KiLockname [9]byte - KiComm [20]byte - KiEmul [17]byte - KiSparestrings [68]byte - KiSpareints [36]byte - KiCrFlags int32 - KiJid int32 - KiNumthreads int32 - KiTid int32 - KiPri int32 - KiRusage [144]byte - KiRusageCh [144]byte - KiPcb int64 - KiKstack int64 - KiUdata int64 - KiTdaddr int64 - KiSpareptrs [48]byte - KiSpareint64s [96]byte - KiSflag int64 - KiTdflags int64 -} diff --git a/process_linux.go b/process_linux.go deleted file mode 100644 index 6460ac6..0000000 --- a/process_linux.go +++ /dev/null @@ -1,594 +0,0 @@ -// +build linux - -package gopsutil - -import ( - "encoding/json" - "io/ioutil" - "os" - "path/filepath" - "strconv" - "strings" - "syscall" -) - -const ( - PrioProcess = 0 // linux/resource.h -) - -// MemoryInfoExStat is different between OSes -type MemoryInfoExStat struct { - RSS uint64 `json:"rss"` // bytes - VMS uint64 `json:"vms"` // bytes - Shared uint64 `json:"shared"` // bytes - Text uint64 `json:"text"` // bytes - Lib uint64 `json:"lib"` // bytes - Data uint64 `json:"data"` // bytes - Dirty uint64 `json:"dirty"` // bytes -} - -func (m MemoryInfoExStat) String() string { - s, _ := json.Marshal(m) - return string(s) -} - -type MemoryMapsStat struct { - Path string `json:"path"` - Rss uint64 `json:"rss"` - Size uint64 `json:"size"` - Pss uint64 `json:"pss"` - SharedClean uint64 `json:"shared_clean"` - SharedDirty uint64 `json:"shared_dirty"` - PrivateClean uint64 `json:"private_clean"` - PrivateDirty uint64 `json:"private_dirty"` - Referenced uint64 `json:"referenced"` - Anonymous uint64 `json:"anonymous"` - Swap uint64 `json:"swap"` -} - -func (m MemoryMapsStat) String() string { - s, _ := json.Marshal(m) - return string(s) -} - -// Create new Process instance -// This only stores Pid -func NewProcess(pid int32) (*Process, error) { - p := &Process{ - Pid: int32(pid), - } - err := p.fillFromStatus() - return p, err -} - -func (p *Process) Ppid() (int32, error) { - _, ppid, _, _, _, err := p.fillFromStat() - if err != nil { - return -1, err - } - return ppid, nil -} -func (p *Process) Name() (string, error) { - return p.name, nil -} -func (p *Process) Exe() (string, error) { - return p.fillFromExe() -} -func (p *Process) Cmdline() (string, error) { - return p.fillFromCmdline() -} -func (p *Process) CreateTime() (int64, error) { - _, _, _, createTime, _, err := p.fillFromStat() - if err != nil { - return 0, err - } - return createTime, nil -} - -func (p *Process) Cwd() (string, error) { - return p.fillFromCwd() -} -func (p *Process) Parent() (*Process, error) { - return nil, NotImplementedError -} -func (p *Process) Status() (string, error) { - return p.status, nil -} -func (p *Process) Uids() ([]int32, error) { - return p.uids, nil -} -func (p *Process) Gids() ([]int32, error) { - return p.gids, nil -} -func (p *Process) Terminal() (string, error) { - terminal, _, _, _, _, err := p.fillFromStat() - if err != nil { - return "", err - } - return terminal, nil -} -func (p *Process) Nice() (int32, error) { - _, _, _, _, nice, err := p.fillFromStat() - if err != nil { - return 0, err - } - return nice, nil -} -func (p *Process) IOnice() (int32, error) { - return 0, NotImplementedError -} -func (p *Process) Rlimit() ([]RlimitStat, error) { - return nil, NotImplementedError -} -func (p *Process) IOCounters() (*IOCountersStat, error) { - return p.fillFromIO() -} -func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) { - return p.numCtxSwitches, nil -} -func (p *Process) NumFDs() (int32, error) { - return 0, NotImplementedError -} -func (p *Process) NumThreads() (int32, error) { - return p.numThreads, nil -} -func (p *Process) Threads() (map[string]string, error) { - ret := make(map[string]string, 0) - return ret, nil -} -func (p *Process) CPUTimes() (*CPUTimesStat, error) { - _, _, cpuTimes, _, _, err := p.fillFromStat() - if err != nil { - return nil, err - } - return cpuTimes, nil -} -func (p *Process) CPUPercent() (int32, error) { - return 0, NotImplementedError -} -func (p *Process) CPUAffinity() ([]int32, error) { - return nil, NotImplementedError -} -func (p *Process) MemoryInfo() (*MemoryInfoStat, error) { - return p.memInfo, nil -} -func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) { - _, memInfoEx, err := p.fillFromStatm() - if err != nil { - return nil, err - } - return memInfoEx, nil -} -func (p *Process) MemoryPercent() (float32, error) { - return 0, NotImplementedError -} - -func (p *Process) Children() ([]*Process, error) { - return nil, NotImplementedError -} - -func (p *Process) OpenFiles() ([]OpenFilesStat, error) { - return nil, NotImplementedError -} - -func (p *Process) Connections() ([]NetConnectionStat, error) { - return nil, NotImplementedError -} - -func (p *Process) IsRunning() (bool, error) { - return true, NotImplementedError -} - -// MemoryMaps get memory maps from /proc/(pid)/smaps -func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) { - pid := p.Pid - var ret []MemoryMapsStat - smapsPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "smaps") - contents, err := ioutil.ReadFile(smapsPath) - if err != nil { - return nil, err - } - lines := strings.Split(string(contents), "\n") - - // function of parsing a block - getBlock := func(first_line []string, block []string) (MemoryMapsStat, error) { - m := MemoryMapsStat{} - m.Path = first_line[len(first_line)-1] - - for _, line := range block { - if strings.Contains(line, "VmFlags") { - continue - } - field := strings.Split(line, ":") - if len(field) < 2 { - continue - } - v := strings.Trim(field[1], " kB") // remove last "kB" - t, err := strconv.ParseUint(v, 10, 64) - if err != nil { - return m, err - } - - switch field[0] { - case "Size": - m.Size = t - case "Rss": - m.Rss = t - case "Pss": - m.Pss = t - case "Shared_Clean": - m.SharedClean = t - case "Shared_Dirty": - m.SharedDirty = t - case "Private_Clean": - m.PrivateClean = t - case "Private_Dirty": - m.PrivateDirty = t - case "Referenced": - m.Referenced = t - case "Anonymous": - m.Anonymous = t - case "Swap": - m.Swap = t - } - } - return m, nil - } - - blocks := make([]string, 16) - for _, line := range lines { - field := strings.Split(line, " ") - if strings.HasSuffix(field[0], ":") == false { - // new block section - if len(blocks) > 0 { - g, err := getBlock(field, blocks) - if err != nil { - return &ret, err - } - ret = append(ret, g) - } - // starts new block - blocks = make([]string, 16) - } else { - blocks = append(blocks, line) - } - } - - return &ret, nil -} - -/** -** Internal functions -**/ - -// Get num_fds from /proc/(pid)/fd -func (p *Process) fillFromfd() (int32, []*OpenFilesStat, error) { - pid := p.Pid - statPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "fd") - d, err := os.Open(statPath) - if err != nil { - return 0, nil, err - } - defer d.Close() - fnames, err := d.Readdirnames(-1) - numFDs := int32(len(fnames)) - - openfiles := make([]*OpenFilesStat, numFDs) - for _, fd := range fnames { - fpath := filepath.Join(statPath, fd) - filepath, err := os.Readlink(fpath) - if err != nil { - continue - } - t, err := strconv.ParseUint(fd, 10, 64) - if err != nil { - return numFDs, openfiles, err - } - o := &OpenFilesStat{ - Path: filepath, - Fd: t, - } - openfiles = append(openfiles, o) - } - - return numFDs, openfiles, nil -} - -// Get cwd from /proc/(pid)/cwd -func (p *Process) fillFromCwd() (string, error) { - pid := p.Pid - cwdPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "cwd") - cwd, err := os.Readlink(cwdPath) - if err != nil { - return "", err - } - return string(cwd), nil -} - -// Get exe from /proc/(pid)/exe -func (p *Process) fillFromExe() (string, error) { - pid := p.Pid - exePath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "exe") - exe, err := os.Readlink(exePath) - if err != nil { - return "", err - } - return string(exe), nil -} - -// Get cmdline from /proc/(pid)/cmdline -func (p *Process) fillFromCmdline() (string, error) { - pid := p.Pid - cmdPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "cmdline") - cmdline, err := ioutil.ReadFile(cmdPath) - if err != nil { - return "", err - } - ret := strings.FieldsFunc(string(cmdline), func(r rune) bool { - if r == '\u0000' { - return true - } - return false - }) - - return strings.Join(ret, " "), nil -} - -// Get IO status from /proc/(pid)/io -func (p *Process) fillFromIO() (*IOCountersStat, error) { - pid := p.Pid - ioPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "io") - ioline, err := ioutil.ReadFile(ioPath) - if err != nil { - return nil, err - } - lines := strings.Split(string(ioline), "\n") - ret := &IOCountersStat{} - - for _, line := range lines { - field := strings.Fields(line) - if len(field) < 2 { - continue - } - t, err := strconv.ParseUint(field[1], 10, 64) - if err != nil { - return nil, err - } - param := field[0] - if strings.HasSuffix(param, ":") { - param = param[:len(param)-1] - } - switch param { - case "syscr": - ret.ReadCount = t - case "syscw": - ret.WriteCount = t - case "read_bytes": - ret.ReadBytes = t - case "write_bytes": - ret.WriteBytes = t - } - } - - return ret, nil -} - -// Get memory info from /proc/(pid)/statm -func (p *Process) fillFromStatm() (*MemoryInfoStat, *MemoryInfoExStat, error) { - pid := p.Pid - memPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "statm") - contents, err := ioutil.ReadFile(memPath) - if err != nil { - return nil, nil, err - } - fields := strings.Split(string(contents), " ") - - vms, err := strconv.ParseUint(fields[0], 10, 64) - if err != nil { - return nil, nil, err - } - rss, err := strconv.ParseUint(fields[1], 10, 64) - if err != nil { - return nil, nil, err - } - memInfo := &MemoryInfoStat{ - RSS: rss * PageSize, - VMS: vms * PageSize, - } - - shared, err := strconv.ParseUint(fields[2], 10, 64) - if err != nil { - return nil, nil, err - } - text, err := strconv.ParseUint(fields[3], 10, 64) - if err != nil { - return nil, nil, err - } - lib, err := strconv.ParseUint(fields[4], 10, 64) - if err != nil { - return nil, nil, err - } - dirty, err := strconv.ParseUint(fields[5], 10, 64) - if err != nil { - return nil, nil, err - } - - memInfoEx := &MemoryInfoExStat{ - RSS: rss * PageSize, - VMS: vms * PageSize, - Shared: shared * PageSize, - Text: text * PageSize, - Lib: lib * PageSize, - Dirty: dirty * PageSize, - } - - return memInfo, memInfoEx, nil -} - -// Get various status from /proc/(pid)/status -func (p *Process) fillFromStatus() error { - pid := p.Pid - statPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "status") - contents, err := ioutil.ReadFile(statPath) - if err != nil { - return err - } - lines := strings.Split(string(contents), "\n") - p.numCtxSwitches = &NumCtxSwitchesStat{} - p.memInfo = &MemoryInfoStat{} - for _, line := range lines { - tabParts := strings.SplitN(line, "\t", 2) - if len(tabParts) < 2 { - continue - } - value := tabParts[1] - switch strings.TrimRight(tabParts[0], ":") { - case "Name": - p.name = strings.Trim(value, " \t") - case "State": - // get between "(" and ")" - s := strings.Index(value, "(") + 1 - e := strings.Index(value, "(") + 1 - p.status = value[s:e] - case "Uid": - p.uids = make([]int32, 0, 4) - for _, i := range strings.Split(value, "\t") { - v, err := strconv.ParseInt(i, 10, 32) - if err != nil { - return err - } - p.uids = append(p.uids, int32(v)) - } - case "Gid": - p.gids = make([]int32, 0, 4) - for _, i := range strings.Split(value, "\t") { - v, err := strconv.ParseInt(i, 10, 32) - if err != nil { - return err - } - p.gids = append(p.gids, int32(v)) - } - case "Threads": - v, err := strconv.ParseInt(value, 10, 32) - if err != nil { - return err - } - p.numThreads = int32(v) - case "voluntary_ctxt_switches": - v, err := strconv.ParseInt(value, 10, 64) - if err != nil { - return err - } - p.numCtxSwitches.Voluntary = v - case "nonvoluntary_ctxt_switches": - v, err := strconv.ParseInt(value, 10, 64) - if err != nil { - return err - } - p.numCtxSwitches.Involuntary = v - case "VmRSS": - value := strings.Trim(value, " kB") // remove last "kB" - v, err := strconv.ParseUint(value, 10, 64) - if err != nil { - return err - } - p.memInfo.RSS = v * 1024 - case "VmSize": - value := strings.Trim(value, " kB") // remove last "kB" - v, err := strconv.ParseUint(value, 10, 64) - if err != nil { - return err - } - p.memInfo.VMS = v * 1024 - case "VmSwap": - value := strings.Trim(value, " kB") // remove last "kB" - v, err := strconv.ParseUint(value, 10, 64) - if err != nil { - return err - } - p.memInfo.Swap = v * 1024 - } - - } - return nil -} - -func (p *Process) fillFromStat() (string, int32, *CPUTimesStat, int64, int32, error) { - pid := p.Pid - statPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "stat") - contents, err := ioutil.ReadFile(statPath) - if err != nil { - return "", 0, nil, 0, 0, err - } - fields := strings.Fields(string(contents)) - - termmap, err := getTerminalMap() - terminal := "" - if err == nil { - t, err := strconv.ParseUint(fields[6], 10, 64) - if err != nil { - return "", 0, nil, 0, 0, err - } - terminal = termmap[t] - } - - ppid, err := strconv.ParseInt(fields[3], 10, 32) - if err != nil { - return "", 0, nil, 0, 0, err - } - utime, err := strconv.ParseFloat(fields[13], 64) - if err != nil { - return "", 0, nil, 0, 0, err - } - stime, err := strconv.ParseFloat(fields[14], 64) - if err != nil { - return "", 0, nil, 0, 0, err - } - - cpuTimes := &CPUTimesStat{ - CPU: "cpu", - User: float32(utime * (1000 / ClockTicks)), - System: float32(stime * (1000 / ClockTicks)), - } - - bootTime, _ := BootTime() - t, err := strconv.ParseUint(fields[21], 10, 64) - if err != nil { - return "", 0, nil, 0, 0, err - } - - ctime := ((t / uint64(ClockTicks)) + uint64(bootTime)) * 1000 - createTime := int64(ctime) - - // p.Nice = mustParseInt32(fields[18]) - // use syscall instead of parse Stat file - snice, _ := syscall.Getpriority(PrioProcess, int(pid)) - nice := int32(snice) // FIXME: is this true? - - return terminal, int32(ppid), cpuTimes, createTime, nice, nil -} - -func Pids() ([]int32, error) { - var ret []int32 - - d, err := os.Open("/proc") - if err != nil { - return nil, err - } - defer d.Close() - - fnames, err := d.Readdirnames(-1) - if err != nil { - return nil, err - } - for _, fname := range fnames { - pid, err := strconv.ParseInt(fname, 10, 32) - if err != nil { - // if not numeric name, just skip - continue - } - ret = append(ret, int32(pid)) - } - - return ret, nil -} diff --git a/process_linux_amd64.go b/process_linux_amd64.go deleted file mode 100644 index 29554db..0000000 --- a/process_linux_amd64.go +++ /dev/null @@ -1,9 +0,0 @@ -// +build linux -// +build amd64 - -package gopsutil - -const ( - ClockTicks = 100 // C.sysconf(C._SC_CLK_TCK) - PageSize = 4096 // C.sysconf(C._SC_PAGE_SIZE) -) diff --git a/process_linux_arm.go b/process_linux_arm.go deleted file mode 100644 index 4f915bb..0000000 --- a/process_linux_arm.go +++ /dev/null @@ -1,9 +0,0 @@ -// +build linux -// +build arm - -package gopsutil - -const ( - ClockTicks = 100 // C.sysconf(C._SC_CLK_TCK) - PageSize = 4096 // C.sysconf(C._SC_PAGE_SIZE) -) diff --git a/process_posix.go b/process_posix.go deleted file mode 100644 index 4e2625a..0000000 --- a/process_posix.go +++ /dev/null @@ -1,102 +0,0 @@ -// +build linux freebsd darwin - -package gopsutil - -import ( - "os" - "os/exec" - "os/user" - "strconv" - "strings" - "syscall" -) - -// POSIX -func getTerminalMap() (map[uint64]string, error) { - ret := make(map[uint64]string) - var termfiles []string - - d, err := os.Open("/dev") - if err != nil { - return nil, err - } - defer d.Close() - - devnames, err := d.Readdirnames(-1) - for _, devname := range devnames { - if strings.HasPrefix(devname, "/dev/tty") { - termfiles = append(termfiles, "/dev/tty/"+devname) - } - } - - ptsd, err := os.Open("/dev/pts") - if err != nil { - return nil, err - } - defer ptsd.Close() - - ptsnames, err := ptsd.Readdirnames(-1) - for _, ptsname := range ptsnames { - termfiles = append(termfiles, "/dev/pts/"+ptsname) - } - - for _, name := range termfiles { - stat := syscall.Stat_t{} - if err = syscall.Stat(name, &stat); err != nil { - return nil, err - } - rdev := uint64(stat.Rdev) - ret[rdev] = strings.Replace(name, "/dev", "", -1) - } - return ret, nil -} - -func (p *Process) SendSignal(sig syscall.Signal) error { - sigAsStr := "INT" - switch sig { - case syscall.SIGSTOP: - sigAsStr = "STOP" - case syscall.SIGCONT: - sigAsStr = "CONT" - case syscall.SIGTERM: - sigAsStr = "TERM" - case syscall.SIGKILL: - sigAsStr = "KILL" - } - - cmd := exec.Command("kill", "-s", sigAsStr, strconv.Itoa(int(p.Pid))) - cmd.Stderr = os.Stderr - err := cmd.Run() - if err != nil { - return err - } - - return nil -} - -func (p *Process) Suspend() error { - return p.SendSignal(syscall.SIGSTOP) -} -func (p *Process) Resume() error { - return p.SendSignal(syscall.SIGCONT) -} -func (p *Process) Terminate() error { - return p.SendSignal(syscall.SIGTERM) -} -func (p *Process) Kill() error { - return p.SendSignal(syscall.SIGKILL) -} -func (p *Process) Username() (string, error) { - uids, err := p.Uids() - if err != nil { - return "", err - } - if len(uids) > 0 { - u, err := user.LookupId(strconv.Itoa(int(uids[0]))) - if err != nil { - return "", err - } - return u.Username, nil - } - return "", nil -} diff --git a/process_posix_test.go b/process_posix_test.go deleted file mode 100644 index b45418a..0000000 --- a/process_posix_test.go +++ /dev/null @@ -1,19 +0,0 @@ -// +build linux freebsd - -package gopsutil - -import ( - "os" - "syscall" - "testing" -) - -func Test_SendSignal(t *testing.T) { - checkPid := os.Getpid() - - p, _ := NewProcess(int32(checkPid)) - err := p.SendSignal(syscall.SIGCONT) - if err != nil { - t.Errorf("send signal %v", err) - } -} diff --git a/process_test.go b/process_test.go deleted file mode 100644 index 2a14d12..0000000 --- a/process_test.go +++ /dev/null @@ -1,118 +0,0 @@ -package gopsutil - -import ( - "os" - "runtime" - "testing" -) - -func testGetProcess() Process { - checkPid := os.Getpid() - if runtime.GOOS == "windows" { - checkPid = 7960 - } - ret, _ := NewProcess(int32(checkPid)) - return *ret -} - -func Test_Pids(t *testing.T) { - ret, err := Pids() - if err != nil { - t.Errorf("error %v", err) - } - if len(ret) == 0 { - t.Errorf("could not get pids %v", ret) - } -} - -func Test_Pid_exists(t *testing.T) { - checkPid := 1 - if runtime.GOOS == "windows" { - checkPid = 0 - } - - ret, err := PidExists(int32(checkPid)) - if err != nil { - t.Errorf("error %v", err) - } - - if ret == false { - t.Errorf("could not get init process %v", ret) - } -} - -func Test_NewProcess(t *testing.T) { - checkPid := 1 - if runtime.GOOS == "windows" { - checkPid = 0 - } - - ret, err := NewProcess(int32(checkPid)) - if err != nil { - t.Errorf("error %v", err) - } - empty := &Process{} - if runtime.GOOS != "windows" { // Windows pid is 0 - if empty == ret { - t.Errorf("error %v", ret) - } - } - -} - -func Test_Process_memory_maps(t *testing.T) { - checkPid := os.Getpid() - if runtime.GOOS == "windows" { - checkPid = 0 - } - ret, err := NewProcess(int32(checkPid)) - - mmaps, err := ret.MemoryMaps(false) - if err != nil { - t.Errorf("memory map get error %v", err) - } - empty := MemoryMapsStat{} - for _, m := range *mmaps { - if m == empty { - t.Errorf("memory map get error %v", m) - } - } - -} - -func Test_Process_Ppid(t *testing.T) { - p := testGetProcess() - - v, err := p.Ppid() - if err != nil { - t.Errorf("geting ppid error %v", err) - } - if v == 0 { - t.Errorf("return value is 0 %v", v) - } - -} - -func Test_Process_IOCounters(t *testing.T) { - p := testGetProcess() - - v, err := p.IOCounters() - if err != nil { - t.Errorf("geting ppid error %v", err) - return - } - empty := &IOCountersStat{} - if v == empty { - t.Errorf("error %v", v) - } -} - -func Test_Process_NumCtx(t *testing.T) { - p := testGetProcess() - - _, err := p.NumCtxSwitches() - if err != nil { - t.Errorf("geting numctx error %v", err) - return - } -} diff --git a/process_windows.go b/process_windows.go deleted file mode 100644 index 0f95984..0000000 --- a/process_windows.go +++ /dev/null @@ -1,260 +0,0 @@ -// +build windows - -package gopsutil - -import ( - "errors" - "syscall" - "unsafe" - - "github.com/shirou/w32" -) - -const ( - NoMoreFiles = 0x12 - MaxPathLength = 260 -) - -type SystemProcessInformation struct { - NextEntryOffset uint64 - NumberOfThreads uint64 - Reserved1 [48]byte - Reserved2 [3]byte - UniqueProcessID uintptr - Reserved3 uintptr - HandleCount uint64 - Reserved4 [4]byte - Reserved5 [11]byte - PeakPagefileUsage uint64 - PrivatePageCount uint64 - Reserved6 [6]uint64 -} - -// Memory_info_ex is different between OSes -type MemoryInfoExStat struct { -} - -type MemoryMapsStat struct { -} - -func Pids() ([]int32, error) { - - var ret []int32 - - procs, err := processes() - if err != nil { - return ret, nil - } - for _, proc := range procs { - ret = append(ret, proc.Pid) - } - - return ret, nil -} - -func (p *Process) Ppid() (int32, error) { - ret, _, _, err := p.getFromSnapProcess(p.Pid) - if err != nil { - return 0, err - } - return ret, nil -} -func (p *Process) Name() (string, error) { - name := "" - return name, NotImplementedError -} -func (p *Process) Exe() (string, error) { - _, _, ret, err := p.getFromSnapProcess(p.Pid) - if err != nil { - return "", err - } - return ret, nil -} -func (p *Process) Cmdline() (string, error) { - return "", NotImplementedError -} -func (p *Process) Cwd() (string, error) { - return "", NotImplementedError -} -func (p *Process) Parent() (*Process, error) { - return p, NotImplementedError -} -func (p *Process) Status() (string, error) { - return "", NotImplementedError -} -func (p *Process) Username() (string, error) { - return "", NotImplementedError -} -func (p *Process) Uids() ([]int32, error) { - var uids []int32 - - return uids, NotImplementedError -} -func (p *Process) Gids() ([]int32, error) { - var gids []int32 - return gids, NotImplementedError -} -func (p *Process) Terminal() (string, error) { - return "", NotImplementedError -} -func (p *Process) Nice() (int32, error) { - return 0, NotImplementedError -} -func (p *Process) IOnice() (int32, error) { - return 0, NotImplementedError -} -func (p *Process) Rlimit() ([]RlimitStat, error) { - var rlimit []RlimitStat - - return rlimit, NotImplementedError -} -func (p *Process) IOCounters() (*IOCountersStat, error) { - return nil, NotImplementedError -} -func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) { - return nil, NotImplementedError -} -func (p *Process) NumFDs() (int32, error) { - return 0, NotImplementedError -} -func (p *Process) NumThreads() (int32, error) { - _, ret, _, err := p.getFromSnapProcess(p.Pid) - if err != nil { - return 0, err - } - return ret, nil -} -func (p *Process) Threads() (map[string]string, error) { - ret := make(map[string]string, 0) - return ret, NotImplementedError -} -func (p *Process) CPUTimes() (*CPUTimesStat, error) { - return nil, NotImplementedError -} -func (p *Process) CPUPercent() (int32, error) { - return 0, NotImplementedError -} -func (p *Process) CPUAffinity() ([]int32, error) { - return nil, NotImplementedError -} -func (p *Process) MemoryInfo() (*MemoryInfoStat, error) { - return nil, NotImplementedError -} -func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) { - return nil, NotImplementedError -} -func (p *Process) MemoryPercent() (float32, error) { - return 0, NotImplementedError -} - -func (p *Process) Children() ([]*Process, error) { - return nil, NotImplementedError -} - -func (p *Process) OpenFiles() ([]OpenFilesStat, error) { - return nil, NotImplementedError -} - -func (p *Process) Connections() ([]NetConnectionStat, error) { - return nil, NotImplementedError -} - -func (p *Process) IsRunning() (bool, error) { - return true, NotImplementedError -} - -func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) { - ret := make([]MemoryMapsStat, 0) - return &ret, NotImplementedError -} - -func NewProcess(pid int32) (*Process, error) { - p := &Process{Pid: pid} - - return p, nil -} - -func (p *Process) SendSignal(sig syscall.Signal) error { - return NotImplementedError -} - -func (p *Process) Suspend() error { - return NotImplementedError -} -func (p *Process) Resume() error { - return NotImplementedError -} -func (p *Process) Terminate() error { - return NotImplementedError -} -func (p *Process) Kill() error { - return NotImplementedError -} - -func (p *Process) getFromSnapProcess(pid int32) (int32, int32, string, error) { - snap := w32.CreateToolhelp32Snapshot(w32.TH32CS_SNAPPROCESS, uint32(pid)) - if snap == 0 { - return 0, 0, "", syscall.GetLastError() - } - defer w32.CloseHandle(snap) - var pe32 w32.PROCESSENTRY32 - pe32.DwSize = uint32(unsafe.Sizeof(pe32)) - if w32.Process32First(snap, &pe32) == false { - return 0, 0, "", syscall.GetLastError() - } - - if pe32.Th32ProcessID == uint32(pid) { - szexe := syscall.UTF16ToString(pe32.SzExeFile[:]) - return int32(pe32.Th32ParentProcessID), int32(pe32.CntThreads), szexe, nil - } - - for w32.Process32Next(snap, &pe32) { - if pe32.Th32ProcessID == uint32(pid) { - szexe := syscall.UTF16ToString(pe32.SzExeFile[:]) - return int32(pe32.Th32ParentProcessID), int32(pe32.CntThreads), szexe, nil - } - } - return 0, 0, "", errors.New("Couldn't find pid:" + string(pid)) -} - -// Get processes -func processes() ([]*Process, error) { - ps := make([]uint32, 255) - var read uint32 - if w32.EnumProcesses(ps, uint32(len(ps)), &read) == false { - return nil, syscall.GetLastError() - } - - var results []*Process - dwardSize := uint32(4) - for _, pid := range ps[:read/dwardSize] { - if pid == 0 { - continue - } - p, err := NewProcess(int32(pid)) - if err != nil { - break - } - results = append(results, p) - } - - return results, nil -} - -func getProcInfo(pid int32) (*SystemProcessInformation, error) { - initialBufferSize := uint64(0x4000) - bufferSize := initialBufferSize - buffer := make([]byte, bufferSize) - - var sysProcInfo SystemProcessInformation - ret, _, _ := procNtQuerySystemInformation.Call( - uintptr(unsafe.Pointer(&sysProcInfo)), - uintptr(unsafe.Pointer(&buffer[0])), - uintptr(unsafe.Pointer(&bufferSize)), - uintptr(unsafe.Pointer(&bufferSize))) - if ret != 0 { - return nil, syscall.GetLastError() - } - - return &sysProcInfo, nil -}