diff --git a/README.rst b/README.rst index 4a804b2..67308d9 100644 --- a/README.rst +++ b/README.rst @@ -8,3 +8,5 @@ gopsutil: psutil for golang - dstat: https://github.com/dagwieers/dstat - gosiger: https://github.com/cloudfoundry/gosigar/ - goprocinfo: https://github.com/c9s/goprocinfo +- go-ps: https://github.com/mitchellh/go-ps + diff --git a/common_windows.go b/common_windows.go new file mode 100644 index 0000000..7458d26 --- /dev/null +++ b/common_windows.go @@ -0,0 +1,11 @@ +// +build windows + +package main + +import ( + "syscall" +) + +var ( + modKernel32 = syscall.NewLazyDLL("kernel32.dll") +) diff --git a/cpu_freebsd.go b/cpu_freebsd.go index 33ff0d9..7fc4c7f 100644 --- a/cpu_freebsd.go +++ b/cpu_freebsd.go @@ -6,9 +6,9 @@ import ( "fmt" ) -func (c CPU) Cpu_times() map[string]string { - ret := make(map[string]string) +func (c CPU) Cpu_times() ([]CPU_Times, error) { + ret := make([]CPU_Times, 0) fmt.Println("FreeBSD") - return ret + return ret, nil } diff --git a/cpu_windows.go b/cpu_windows.go new file mode 100644 index 0000000..dbba509 --- /dev/null +++ b/cpu_windows.go @@ -0,0 +1,47 @@ +// +build windows + +package main + +import ( + "syscall" + "unsafe" +) + +var ( + modkernel32 = syscall.NewLazyDLL("kernel32.dll") + procGetSystemTimes = modkernel32.NewProc("GetSystemTimes") +) + +type FILETIME struct { + DwLowDateTime uint32 + DwHighDateTime uint32 +} + +func (c CPU) Cpu_times() ([]CPU_Times, error) { + ret := make([]CPU_Times, 0) + + 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() + } + + LO_T := float64(0.0000001) + HI_T := (LO_T * 4294967296.0) + idle := ((HI_T * float64(lpIdleTime.DwHighDateTime)) + (LO_T * float64(lpIdleTime.DwLowDateTime))) + user := ((HI_T * float64(lpUserTime.DwHighDateTime)) + (LO_T * float64(lpUserTime.DwLowDateTime))) + kernel := ((HI_T * float64(lpKernelTime.DwHighDateTime)) + (LO_T * float64(lpKernelTime.DwLowDateTime))) + system := (kernel - idle) + + ret = append(ret, CPU_Times{ + Idle: uint64(idle), + User: uint64(user), + System: uint64(system), + }) + return ret, nil +} diff --git a/disk.go b/disk.go index 58edb09..7f2e003 100644 --- a/disk.go +++ b/disk.go @@ -3,12 +3,18 @@ package main type Disk struct{} type Disk_usage struct { - Path string `json:"path"` - Total uint64 `json:"total"` - Free uint64 `json:"free"` - Available uint64 `json:"available"` - Used uint64 `json:"used"` - Percent float64 `json:"percent"` + Path string `json:"path"` + Total uint64 `json:"total"` + Free uint64 `json:"free"` + Used uint64 `json:"used"` + UsedPercent float64 `json:"usedPercent"` +} + +type Disk_partition struct { + Device string `json:"device"` + Mountpoint string `json:"mountpoint"` + Fstype string `json:"fstype"` + Opts string `json:"opts"` } type Disk_IO_Counters struct { diff --git a/disk_test.go b/disk_test.go index 5bb3e0b..c76c37c 100644 --- a/disk_test.go +++ b/disk_test.go @@ -3,13 +3,29 @@ package main import ( "encoding/json" "fmt" + "runtime" "testing" ) func TestDisk_usage(t *testing.T) { disk := NewDisk() - v, err := disk.Disk_usage("/") + path := "/" + if runtime.GOOS == "windows" { + path = "C:" + } + v, err := disk.Disk_usage(path) + if err != nil { + t.Errorf("error %v", err) + } + d, _ := json.Marshal(v) + fmt.Printf("%s\n", d) +} + +func TestDisk_partitions(t *testing.T) { + disk := NewDisk() + + v, err := disk.Disk_partitions() if err != nil { t.Errorf("error %v", err) } diff --git a/disk_windows.go b/disk_windows.go new file mode 100644 index 0000000..f271444 --- /dev/null +++ b/disk_windows.go @@ -0,0 +1,107 @@ +// +build windows + +package main + +import ( + "bytes" + "syscall" + "unsafe" +) + +var ( + procGetDiskFreeSpaceExW = modkernel32.NewProc("GetDiskFreeSpaceExW") + procGetLogicalDriveStringsW = modkernel32.NewProc("GetLogicalDriveStringsW") + procGetDriveType = modkernel32.NewProc("GetDriveTypeW") + provGetVolumeInformation = modKernel32.NewProc("GetVolumeInformationW") +) + +var ( + FILE_FILE_COMPRESSION = int64(16) // 0x00000010 + FILE_READ_ONLY_VOLUME = int64(524288) // 0x00080000 +) + +func (d Disk) Disk_usage(path string) (Disk_usage, error) { + ret := Disk_usage{} + + 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 + + return ret, nil +} + +func (d Disk) Disk_partitions() ([]Disk_partition, error) { + ret := make([]Disk_partition, 0) + 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&FILE_READ_ONLY_VOLUME != 0 { + opts = "ro" + } + if lpFileSystemFlags&FILE_FILE_COMPRESSION != 0 { + opts += ".compress" + } + + d := Disk_partition{ + Mountpoint: path, + Device: path, + Fstype: string(bytes.Replace(lpFileSystemNameBuffer, []byte("\x00"), []byte(""), -1)), + Opts: opts, + } + ret = append(ret, d) + } + } + } + return ret, nil +} diff --git a/host.go b/host.go index 9a5f3bd..c5657a7 100644 --- a/host.go +++ b/host.go @@ -1,10 +1,5 @@ package main -import ( - "os" - "syscall" -) - type Host struct{} type HostInfo struct { @@ -17,22 +12,3 @@ func NewHost() Host { h := Host{} return h } - -func (h Host) HostInfo() (HostInfo, error) { - ret := HostInfo{} - sysinfo := &syscall.Sysinfo_t{} - - if err := syscall.Sysinfo(sysinfo); err != nil { - return ret, err - } - - hostname, err := os.Hostname() - if err != nil { - return ret, err - } - ret.Hostname = hostname - ret.Uptime = sysinfo.Uptime - ret.Procs = uint64(sysinfo.Procs) - - return ret, nil -} diff --git a/host_unix.go b/host_unix.go new file mode 100644 index 0000000..4c8ef36 --- /dev/null +++ b/host_unix.go @@ -0,0 +1,17 @@ +// +build linux freebsd + +package main + +import ( + "os" + "syscall" +) + +func (h Host) HostInfo() (HostInfo, error) { + ret := HostInfo{} + hostname, err := os.Hostname() + ret.Hostname = hostname + ret.Uptime = sysinfo.Uptime + + return ret, nil +} diff --git a/host_windows.go b/host_windows.go new file mode 100644 index 0000000..41ff631 --- /dev/null +++ b/host_windows.go @@ -0,0 +1,39 @@ +// +build windows + +package main + +import ( + "github.com/mitchellh/go-ps" + "os" + "syscall" +) + +func (h Host) HostInfo() (HostInfo, error) { + ret := HostInfo{} + hostname, err := os.Hostname() + if err != nil { + return ret, err + } + + ret.Hostname = hostname + + kernel32, err := syscall.LoadLibrary("kernel32.dll") + if err != nil { + return ret, err + } + defer syscall.FreeLibrary(kernel32) + GetTickCount, _ := syscall.GetProcAddress(kernel32, "GetTickCount") + + uptimemsec, _, err := syscall.Syscall(uintptr(GetTickCount), 0, 0, 0, 0) + + ret.Uptime = int64(uptimemsec) / 1000 + + procs, err := ps.Processes() + if err != nil { + return ret, err + } + + ret.Procs = uint64(len(procs)) + + return ret, nil +} diff --git a/load_windows.go b/load_windows.go new file mode 100644 index 0000000..38d6e48 --- /dev/null +++ b/load_windows.go @@ -0,0 +1,9 @@ +// +build windows + +package main + +func (l Load) LoadAvg() (LoadAvg, error) { + ret := LoadAvg{} + + return ret, nil +} diff --git a/mem_windows.go b/mem_windows.go new file mode 100644 index 0000000..a8ea77b --- /dev/null +++ b/mem_windows.go @@ -0,0 +1,47 @@ +// +build windows + +package main + +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 (m Mem) Virtual_memory() (Virtual_memory, error) { + ret := Virtual_memory{} + + var memInfo MEMORYSTATUSEX + memInfo.cbSize = uint32(unsafe.Sizeof(memInfo)) + mem, _, _ := procGlobalMemoryStatusEx.Call(uintptr(unsafe.Pointer(&memInfo))) + if mem == 0 { + return ret, syscall.GetLastError() + } + + ret.Total = memInfo.ullTotalPhys + ret.Available = memInfo.ullAvailPhys + ret.UsedPercent = float64(memInfo.dwMemoryLoad) + ret.Used = ret.Total - ret.Available + return ret, nil +} + +func (m Mem) Swap_memory() (Swap_memory, error) { + ret := Swap_memory{} + + return ret, nil +}