diff options
Diffstat (limited to 'vendor/github.com/elastic/gosigar/sigar_windows.go')
-rw-r--r-- | vendor/github.com/elastic/gosigar/sigar_windows.go | 437 |
1 files changed, 437 insertions, 0 deletions
diff --git a/vendor/github.com/elastic/gosigar/sigar_windows.go b/vendor/github.com/elastic/gosigar/sigar_windows.go new file mode 100644 index 000000000..0cdf928d1 --- /dev/null +++ b/vendor/github.com/elastic/gosigar/sigar_windows.go @@ -0,0 +1,437 @@ +// Copyright (c) 2012 VMware, Inc. + +package gosigar + +import ( + "fmt" + "os" + "path/filepath" + "runtime" + "strings" + "sync" + "syscall" + "time" + + "github.com/StackExchange/wmi" + "github.com/elastic/gosigar/sys/windows" + "github.com/pkg/errors" +) + +// Win32_Process represents a process on the Windows operating system. If +// additional fields are added here (that match the Windows struct) they will +// automatically be populated when calling getWin32Process. +// https://msdn.microsoft.com/en-us/library/windows/desktop/aa394372(v=vs.85).aspx +type Win32_Process struct { + CommandLine string +} + +// Win32_OperatingSystem WMI class represents a Windows-based operating system +// installed on a computer. +// https://msdn.microsoft.com/en-us/library/windows/desktop/aa394239(v=vs.85).aspx +type Win32_OperatingSystem struct { + LastBootUpTime time.Time +} + +var ( + // version is Windows version of the host OS. + version = windows.GetWindowsVersion() + + // processQueryLimitedInfoAccess is set to PROCESS_QUERY_INFORMATION for Windows + // 2003 and XP where PROCESS_QUERY_LIMITED_INFORMATION is unknown. For all newer + // OS versions it is set to PROCESS_QUERY_LIMITED_INFORMATION. + processQueryLimitedInfoAccess = windows.PROCESS_QUERY_LIMITED_INFORMATION + + // bootTime is the time when the OS was last booted. This value may be nil + // on operating systems that do not support the WMI query used to obtain it. + bootTime *time.Time + bootTimeLock sync.Mutex +) + +func init() { + if !version.IsWindowsVistaOrGreater() { + // PROCESS_QUERY_LIMITED_INFORMATION cannot be used on 2003 or XP. + processQueryLimitedInfoAccess = syscall.PROCESS_QUERY_INFORMATION + } +} + +func (self *LoadAverage) Get() error { + return ErrNotImplemented{runtime.GOOS} +} + +func (self *FDUsage) Get() error { + return ErrNotImplemented{runtime.GOOS} +} + +func (self *ProcEnv) Get(pid int) error { + return ErrNotImplemented{runtime.GOOS} +} + +func (self *ProcExe) Get(pid int) error { + return ErrNotImplemented{runtime.GOOS} +} + +func (self *ProcFDUsage) Get(pid int) error { + return ErrNotImplemented{runtime.GOOS} +} + +func (self *Uptime) Get() error { + // Minimum supported OS is Windows Vista. + if !version.IsWindowsVistaOrGreater() { + return ErrNotImplemented{runtime.GOOS} + } + + bootTimeLock.Lock() + defer bootTimeLock.Unlock() + if bootTime == nil { + os, err := getWin32OperatingSystem() + if err != nil { + return errors.Wrap(err, "failed to get boot time using WMI") + } + bootTime = &os.LastBootUpTime + } + + self.Length = time.Since(*bootTime).Seconds() + return nil +} + +func (self *Mem) Get() error { + memoryStatusEx, err := windows.GlobalMemoryStatusEx() + if err != nil { + return errors.Wrap(err, "GlobalMemoryStatusEx failed") + } + + self.Total = memoryStatusEx.TotalPhys + self.Free = memoryStatusEx.AvailPhys + self.Used = self.Total - self.Free + self.ActualFree = self.Free + self.ActualUsed = self.Used + return nil +} + +func (self *Swap) Get() error { + memoryStatusEx, err := windows.GlobalMemoryStatusEx() + if err != nil { + return errors.Wrap(err, "GlobalMemoryStatusEx failed") + } + + self.Total = memoryStatusEx.TotalPageFile + self.Free = memoryStatusEx.AvailPageFile + self.Used = self.Total - self.Free + return nil +} + +func (self *Cpu) Get() error { + idle, kernel, user, err := windows.GetSystemTimes() + if err != nil { + return errors.Wrap(err, "GetSystemTimes failed") + } + + // CPU times are reported in milliseconds by gosigar. + self.Idle = uint64(idle / time.Millisecond) + self.Sys = uint64(kernel / time.Millisecond) + self.User = uint64(user / time.Millisecond) + return nil +} + +func (self *CpuList) Get() error { + cpus, err := windows.NtQuerySystemProcessorPerformanceInformation() + if err != nil { + return errors.Wrap(err, "NtQuerySystemProcessorPerformanceInformation failed") + } + + self.List = make([]Cpu, 0, len(cpus)) + for _, cpu := range cpus { + self.List = append(self.List, Cpu{ + Idle: uint64(cpu.IdleTime / time.Millisecond), + Sys: uint64(cpu.KernelTime / time.Millisecond), + User: uint64(cpu.UserTime / time.Millisecond), + }) + } + return nil +} + +func (self *FileSystemList) Get() error { + drives, err := windows.GetLogicalDriveStrings() + if err != nil { + return errors.Wrap(err, "GetLogicalDriveStrings failed") + } + + for _, drive := range drives { + dt, err := windows.GetDriveType(drive) + if err != nil { + return errors.Wrapf(err, "GetDriveType failed") + } + + self.List = append(self.List, FileSystem{ + DirName: drive, + DevName: drive, + TypeName: dt.String(), + }) + } + return nil +} + +// Get retrieves a list of all process identifiers (PIDs) in the system. +func (self *ProcList) Get() error { + pids, err := windows.EnumProcesses() + if err != nil { + return errors.Wrap(err, "EnumProcesses failed") + } + + // Convert uint32 PIDs to int. + self.List = make([]int, 0, len(pids)) + for _, pid := range pids { + self.List = append(self.List, int(pid)) + } + return nil +} + +func (self *ProcState) Get(pid int) error { + var errs []error + + var err error + self.Name, err = getProcName(pid) + if err != nil { + errs = append(errs, errors.Wrap(err, "getProcName failed")) + } + + self.State, err = getProcStatus(pid) + if err != nil { + errs = append(errs, errors.Wrap(err, "getProcStatus failed")) + } + + self.Ppid, err = getParentPid(pid) + if err != nil { + errs = append(errs, errors.Wrap(err, "getParentPid failed")) + } + + self.Username, err = getProcCredName(pid) + if err != nil { + errs = append(errs, errors.Wrap(err, "getProcCredName failed")) + } + + if len(errs) > 0 { + errStrs := make([]string, 0, len(errs)) + for _, e := range errs { + errStrs = append(errStrs, e.Error()) + } + return errors.New(strings.Join(errStrs, "; ")) + } + return nil +} + +// getProcName returns the process name associated with the PID. +func getProcName(pid int) (string, error) { + handle, err := syscall.OpenProcess(processQueryLimitedInfoAccess, false, uint32(pid)) + if err != nil { + return "", errors.Wrapf(err, "OpenProcess failed for pid=%v", pid) + } + defer syscall.CloseHandle(handle) + + filename, err := windows.GetProcessImageFileName(handle) + if err != nil { + return "", errors.Wrapf(err, "GetProcessImageFileName failed for pid=%v", pid) + } + + return filepath.Base(filename), nil +} + +// getProcStatus returns the status of a process. +func getProcStatus(pid int) (RunState, error) { + handle, err := syscall.OpenProcess(processQueryLimitedInfoAccess, false, uint32(pid)) + if err != nil { + return RunStateUnknown, errors.Wrapf(err, "OpenProcess failed for pid=%v", pid) + } + defer syscall.CloseHandle(handle) + + var exitCode uint32 + err = syscall.GetExitCodeProcess(handle, &exitCode) + if err != nil { + return RunStateUnknown, errors.Wrapf(err, "GetExitCodeProcess failed for pid=%v") + } + + if exitCode == 259 { //still active + return RunStateRun, nil + } + return RunStateSleep, nil +} + +// getParentPid returns the parent process ID of a process. +func getParentPid(pid int) (int, error) { + handle, err := syscall.OpenProcess(processQueryLimitedInfoAccess, false, uint32(pid)) + if err != nil { + return RunStateUnknown, errors.Wrapf(err, "OpenProcess failed for pid=%v", pid) + } + defer syscall.CloseHandle(handle) + + procInfo, err := windows.NtQueryProcessBasicInformation(handle) + if err != nil { + return 0, errors.Wrapf(err, "NtQueryProcessBasicInformation failed for pid=%v", pid) + } + + return int(procInfo.InheritedFromUniqueProcessID), nil +} + +func getProcCredName(pid int) (string, error) { + handle, err := syscall.OpenProcess(syscall.PROCESS_QUERY_INFORMATION, false, uint32(pid)) + if err != nil { + return "", errors.Wrapf(err, "OpenProcess failed for pid=%v", pid) + } + defer syscall.CloseHandle(handle) + + // Find process token via win32. + var token syscall.Token + err = syscall.OpenProcessToken(handle, syscall.TOKEN_QUERY, &token) + if err != nil { + return "", errors.Wrapf(err, "OpenProcessToken failed for pid=%v", pid) + } + + // Find the token user. + tokenUser, err := token.GetTokenUser() + if err != nil { + return "", errors.Wrapf(err, "GetTokenInformation failed for pid=%v", pid) + } + + // Close token to prevent handle leaks. + err = token.Close() + if err != nil { + return "", errors.Wrapf(err, "failed while closing process token handle for pid=%v", pid) + } + + // Look up domain account by SID. + account, domain, _, err := tokenUser.User.Sid.LookupAccount("") + if err != nil { + sid, sidErr := tokenUser.User.Sid.String() + if sidErr != nil { + return "", errors.Wrapf(err, "failed while looking up account name for pid=%v", pid) + } + return "", errors.Wrapf(err, "failed while looking up account name for SID=%v of pid=%v", sid, pid) + } + + return fmt.Sprintf(`%s\%s`, domain, account), nil +} + +func (self *ProcMem) Get(pid int) error { + handle, err := syscall.OpenProcess(processQueryLimitedInfoAccess|windows.PROCESS_VM_READ, false, uint32(pid)) + if err != nil { + return errors.Wrapf(err, "OpenProcess failed for pid=%v", pid) + } + defer syscall.CloseHandle(handle) + + counters, err := windows.GetProcessMemoryInfo(handle) + if err != nil { + return errors.Wrapf(err, "GetProcessMemoryInfo failed for pid=%v", pid) + } + + self.Resident = uint64(counters.WorkingSetSize) + self.Size = uint64(counters.PrivateUsage) + return nil +} + +func (self *ProcTime) Get(pid int) error { + cpu, err := getProcTimes(pid) + if err != nil { + return err + } + + // Windows epoch times are expressed as time elapsed since midnight on + // January 1, 1601 at Greenwich, England. This converts the Filetime to + // unix epoch in milliseconds. + self.StartTime = uint64(cpu.CreationTime.Nanoseconds() / 1e6) + + // Convert to millis. + self.User = uint64(windows.FiletimeToDuration(&cpu.UserTime).Nanoseconds() / 1e6) + self.Sys = uint64(windows.FiletimeToDuration(&cpu.KernelTime).Nanoseconds() / 1e6) + self.Total = self.User + self.Sys + + return nil +} + +func getProcTimes(pid int) (*syscall.Rusage, error) { + handle, err := syscall.OpenProcess(processQueryLimitedInfoAccess, false, uint32(pid)) + if err != nil { + return nil, errors.Wrapf(err, "OpenProcess failed for pid=%v", pid) + } + defer syscall.CloseHandle(handle) + + var cpu syscall.Rusage + if err := syscall.GetProcessTimes(handle, &cpu.CreationTime, &cpu.ExitTime, &cpu.KernelTime, &cpu.UserTime); err != nil { + return nil, errors.Wrapf(err, "GetProcessTimes failed for pid=%v", pid) + } + + return &cpu, nil +} + +func (self *ProcArgs) Get(pid int) error { + // The minimum supported client for Win32_Process is Windows Vista. + if !version.IsWindowsVistaOrGreater() { + return ErrNotImplemented{runtime.GOOS} + } + + process, err := getWin32Process(int32(pid)) + if err != nil { + return errors.Wrapf(err, "ProcArgs failed for pid=%v", pid) + } + + self.List = []string{process.CommandLine} + return nil +} + +func (self *FileSystemUsage) Get(path string) error { + freeBytesAvailable, totalNumberOfBytes, totalNumberOfFreeBytes, err := windows.GetDiskFreeSpaceEx(path) + if err != nil { + return errors.Wrap(err, "GetDiskFreeSpaceEx failed") + } + + self.Total = totalNumberOfBytes + self.Free = totalNumberOfFreeBytes + self.Used = self.Total - self.Free + self.Avail = freeBytesAvailable + return nil +} + +// getWin32Process gets information about the process with the given process ID. +// It uses a WMI query to get the information from the local system. +func getWin32Process(pid int32) (Win32_Process, error) { + var dst []Win32_Process + query := fmt.Sprintf("WHERE ProcessId = %d", pid) + q := wmi.CreateQuery(&dst, query) + err := wmi.Query(q, &dst) + if err != nil { + return Win32_Process{}, fmt.Errorf("could not get Win32_Process %s: %v", query, err) + } + if len(dst) < 1 { + return Win32_Process{}, fmt.Errorf("could not get Win32_Process %s: Process not found", query) + } + return dst[0], nil +} + +func getWin32OperatingSystem() (Win32_OperatingSystem, error) { + var dst []Win32_OperatingSystem + q := wmi.CreateQuery(&dst, "") + err := wmi.Query(q, &dst) + if err != nil { + return Win32_OperatingSystem{}, errors.Wrap(err, "wmi query for Win32_OperatingSystem failed") + } + if len(dst) != 1 { + return Win32_OperatingSystem{}, errors.New("wmi query for Win32_OperatingSystem failed") + } + return dst[0], nil +} + +func (self *Rusage) Get(who int) error { + if who != 0 { + return ErrNotImplemented{runtime.GOOS} + } + + pid := os.Getpid() + cpu, err := getProcTimes(pid) + if err != nil { + return err + } + + self.Utime = windows.FiletimeToDuration(&cpu.UserTime) + self.Stime = windows.FiletimeToDuration(&cpu.KernelTime) + + return nil +} |