1package sigar
2
3import (
4	"bytes"
5	"fmt"
6	"os/exec"
7	"strconv"
8	"syscall"
9	"time"
10	"unsafe"
11
12	"github.com/cloudfoundry/gosigar/sys/windows"
13	"github.com/pkg/errors"
14)
15
16var (
17	kernel32DLL = syscall.MustLoadDLL("kernel32")
18
19	procGetDiskFreeSpace     = kernel32DLL.MustFindProc("GetDiskFreeSpaceW")
20	procGetSystemTimes       = kernel32DLL.MustFindProc("GetSystemTimes")
21	procGetTickCount64       = kernel32DLL.MustFindProc("GetTickCount64")
22	procGlobalMemoryStatusEx = kernel32DLL.MustFindProc("GlobalMemoryStatusEx")
23
24	// processQueryLimitedInfoAccess is set to PROCESS_QUERY_INFORMATION for Windows
25	// 2003 and XP where PROCESS_QUERY_LIMITED_INFORMATION is unknown. For all newer
26	// OS versions it is set to PROCESS_QUERY_LIMITED_INFORMATION.
27	processQueryLimitedInfoAccess = windows.PROCESS_QUERY_LIMITED_INFORMATION
28)
29
30func (self *LoadAverage) Get() error {
31	return ErrNotImplemented
32}
33
34func (u *Uptime) Get() error {
35	r1, _, e1 := syscall.Syscall(procGetTickCount64.Addr(), 0, 0, 0, 0)
36	if e1 != 0 {
37		return error(e1)
38	}
39	u.Length = (time.Duration(r1) * time.Millisecond).Seconds()
40	return nil
41}
42
43type memorystatusex struct {
44	Length               uint32
45	MemoryLoad           uint32
46	TotalPhys            uint64
47	AvailPhys            uint64
48	TotalPageFile        uint64
49	AvailPageFile        uint64
50	TotalVirtual         uint64
51	AvailVirtual         uint64
52	AvailExtendedVirtual uint64
53}
54
55func (m *Mem) Get() error {
56	var x memorystatusex
57	x.Length = uint32(unsafe.Sizeof(x))
58	r1, _, e1 := syscall.Syscall(procGlobalMemoryStatusEx.Addr(), 1,
59		uintptr(unsafe.Pointer(&x)),
60		0,
61		0,
62	)
63	if err := checkErrno(r1, e1); err != nil {
64		return fmt.Errorf("GlobalMemoryStatusEx: %s", err)
65	}
66	m.Total = x.TotalPhys
67	m.Free = x.AvailPhys
68	m.ActualFree = m.Free
69	m.Used = m.Total - m.Free
70	m.ActualUsed = m.Used
71	return nil
72}
73
74func (s *Swap) Get() error {
75	const MB = 1024 * 1024
76	out, err := exec.Command("wmic", "pagefile", "list", "full").Output()
77	if err != nil {
78		return err
79	}
80	total, err := parseWmicOutput(out, []byte("AllocatedBaseSize"))
81	if err != nil {
82		return err
83	}
84	used, err := parseWmicOutput(out, []byte("CurrentUsage"))
85	if err != nil {
86		return err
87	}
88	s.Total = total * MB
89	s.Used = used * MB
90	s.Free = s.Total - s.Used
91	return nil
92}
93
94func parseWmicOutput(s, sep []byte) (uint64, error) {
95	bb := bytes.Split(s, []byte("\n"))
96	for i := 0; i < len(bb); i++ {
97		b := bytes.TrimSpace(bb[i])
98		n := bytes.IndexByte(b, '=')
99		if n > 0 && bytes.Equal(sep, b[:n]) {
100			return strconv.ParseUint(string(b[n+1:]), 10, 64)
101		}
102	}
103	return 0, errors.New("parseWmicOutput: missing field: " + string(sep))
104}
105
106func (c *Cpu) Get() error {
107	var (
108		idleTime   syscall.Filetime
109		kernelTime syscall.Filetime // Includes kernel and idle time.
110		userTime   syscall.Filetime
111	)
112	r1, _, e1 := syscall.Syscall(procGetSystemTimes.Addr(), 3,
113		uintptr(unsafe.Pointer(&idleTime)),
114		uintptr(unsafe.Pointer(&kernelTime)),
115		uintptr(unsafe.Pointer(&userTime)),
116	)
117	if err := checkErrno(r1, e1); err != nil {
118		return fmt.Errorf("GetSystemTimes: %s", err)
119	}
120
121	c.Idle = uint64(idleTime.Nanoseconds())
122	c.Sys = uint64(kernelTime.Nanoseconds()) - c.Idle
123	c.User = uint64(userTime.Nanoseconds())
124	return nil
125}
126
127func (self *CpuList) Get() error {
128	return ErrNotImplemented
129}
130
131func (self *FileSystemList) Get() error {
132	return ErrNotImplemented
133}
134
135func (self *ProcList) Get() error {
136	return ErrNotImplemented
137}
138
139func (self *ProcState) Get(pid int) error {
140	return ErrNotImplemented
141}
142
143func (self *ProcMem) Get(pid int) error {
144	handle, err := syscall.OpenProcess(processQueryLimitedInfoAccess|windows.PROCESS_VM_READ, false, uint32(pid))
145	if err != nil {
146		return errors.Wrapf(err, "OpenProcess failed for pid=%v", pid)
147	}
148	defer syscall.CloseHandle(handle)
149
150	counters, err := windows.GetProcessMemoryInfo(handle)
151	if err != nil {
152		return errors.Wrapf(err, "GetProcessMemoryInfo failed for pid=%v", pid)
153	}
154
155	self.Resident = uint64(counters.WorkingSetSize)
156	self.Size = uint64(counters.PrivateUsage)
157	return nil
158}
159
160func (self *ProcTime) Get(pid int) error {
161	handle, err := syscall.OpenProcess(processQueryLimitedInfoAccess, false, uint32(pid))
162	if err != nil {
163		return errors.Wrapf(err, "OpenProcess failed for pid=%v", pid)
164	}
165	defer syscall.CloseHandle(handle)
166
167	var CPU syscall.Rusage
168	if err := syscall.GetProcessTimes(handle, &CPU.CreationTime, &CPU.ExitTime, &CPU.KernelTime, &CPU.UserTime); err != nil {
169		return errors.Wrapf(err, "GetProcessTimes failed for pid=%v", pid)
170	}
171
172	// Windows epoch times are expressed as time elapsed since midnight on
173	// January 1, 1601 at Greenwich, England. This converts the Filetime to
174	// unix epoch in milliseconds.
175	self.StartTime = uint64(CPU.CreationTime.Nanoseconds() / 1e6)
176
177	// Convert to millis.
178	self.User = uint64(windows.FiletimeToDuration(&CPU.UserTime).Nanoseconds() / 1e6)
179	self.Sys = uint64(windows.FiletimeToDuration(&CPU.KernelTime).Nanoseconds() / 1e6)
180	self.Total = self.User + self.Sys
181
182	return nil
183}
184
185func (self *ProcArgs) Get(pid int) error {
186	return ErrNotImplemented
187}
188
189func (self *ProcExe) Get(pid int) error {
190	return ErrNotImplemented
191}
192
193func (fs *FileSystemUsage) Get(path string) error {
194	root, err := syscall.UTF16PtrFromString(path)
195	if err != nil {
196		return fmt.Errorf("FileSystemUsage (%s): %s", path, err)
197	}
198
199	var (
200		SectorsPerCluster uint32
201		BytesPerSector    uint32
202
203		// Free clusters available to the user
204		// associated with the calling thread.
205		NumberOfFreeClusters uint32
206
207		// Total clusters available to the user
208		// associated with the calling thread.
209		TotalNumberOfClusters uint32
210	)
211	r1, _, e1 := syscall.Syscall6(procGetDiskFreeSpace.Addr(), 5,
212		uintptr(unsafe.Pointer(root)),
213		uintptr(unsafe.Pointer(&SectorsPerCluster)),
214		uintptr(unsafe.Pointer(&BytesPerSector)),
215		uintptr(unsafe.Pointer(&NumberOfFreeClusters)),
216		uintptr(unsafe.Pointer(&TotalNumberOfClusters)),
217		0,
218	)
219	if err := checkErrno(r1, e1); err != nil {
220		return fmt.Errorf("FileSystemUsage (%s): %s", path, err)
221	}
222
223	m := uint64(SectorsPerCluster * BytesPerSector / 1024)
224	fs.Total = uint64(TotalNumberOfClusters) * m
225	fs.Free = uint64(NumberOfFreeClusters) * m
226	fs.Avail = fs.Free
227	fs.Used = fs.Total - fs.Free
228
229	return nil
230}
231
232func checkErrno(r1 uintptr, e1 error) error {
233	if r1 == 0 {
234		if e, ok := e1.(syscall.Errno); ok && e != 0 {
235			return e1
236		}
237		return syscall.EINVAL
238	}
239	return nil
240}
241