...

Source file src/github.com/shirou/gopsutil/process/process_windows.go

Documentation: github.com/shirou/gopsutil/process

     1  // +build windows
     2  
     3  package process
     4  
     5  import (
     6  	"bufio"
     7  	"context"
     8  	"errors"
     9  	"fmt"
    10  	"io"
    11  	"os"
    12  	"reflect"
    13  	"strings"
    14  	"syscall"
    15  	"time"
    16  	"unicode/utf16"
    17  	"unsafe"
    18  
    19  	"github.com/shirou/gopsutil/cpu"
    20  	"github.com/shirou/gopsutil/internal/common"
    21  	"github.com/shirou/gopsutil/net"
    22  	"golang.org/x/sys/windows"
    23  )
    24  
    25  var (
    26  	modntdll             = windows.NewLazySystemDLL("ntdll.dll")
    27  	procNtResumeProcess  = modntdll.NewProc("NtResumeProcess")
    28  	procNtSuspendProcess = modntdll.NewProc("NtSuspendProcess")
    29  
    30  	modpsapi                     = windows.NewLazySystemDLL("psapi.dll")
    31  	procGetProcessMemoryInfo     = modpsapi.NewProc("GetProcessMemoryInfo")
    32  	procGetProcessImageFileNameW = modpsapi.NewProc("GetProcessImageFileNameW")
    33  
    34  	advapi32                  = windows.NewLazySystemDLL("advapi32.dll")
    35  	procLookupPrivilegeValue  = advapi32.NewProc("LookupPrivilegeValueW")
    36  	procAdjustTokenPrivileges = advapi32.NewProc("AdjustTokenPrivileges")
    37  
    38  	procQueryFullProcessImageNameW = common.Modkernel32.NewProc("QueryFullProcessImageNameW")
    39  	procGetPriorityClass           = common.Modkernel32.NewProc("GetPriorityClass")
    40  	procGetProcessIoCounters       = common.Modkernel32.NewProc("GetProcessIoCounters")
    41  	procGetNativeSystemInfo        = common.Modkernel32.NewProc("GetNativeSystemInfo")
    42  
    43  	processorArchitecture uint
    44  )
    45  
    46  const processQueryInformation = windows.PROCESS_QUERY_LIMITED_INFORMATION
    47  
    48  type SystemProcessInformation struct {
    49  	NextEntryOffset   uint64
    50  	NumberOfThreads   uint64
    51  	Reserved1         [48]byte
    52  	Reserved2         [3]byte
    53  	UniqueProcessID   uintptr
    54  	Reserved3         uintptr
    55  	HandleCount       uint64
    56  	Reserved4         [4]byte
    57  	Reserved5         [11]byte
    58  	PeakPagefileUsage uint64
    59  	PrivatePageCount  uint64
    60  	Reserved6         [6]uint64
    61  }
    62  
    63  type systemProcessorInformation struct {
    64  	ProcessorArchitecture uint16
    65  	ProcessorLevel        uint16
    66  	ProcessorRevision     uint16
    67  	Reserved              uint16
    68  	ProcessorFeatureBits  uint16
    69  }
    70  
    71  type systemInfo struct {
    72  	wProcessorArchitecture      uint16
    73  	wReserved                   uint16
    74  	dwPageSize                  uint32
    75  	lpMinimumApplicationAddress uintptr
    76  	lpMaximumApplicationAddress uintptr
    77  	dwActiveProcessorMask       uintptr
    78  	dwNumberOfProcessors        uint32
    79  	dwProcessorType             uint32
    80  	dwAllocationGranularity     uint32
    81  	wProcessorLevel             uint16
    82  	wProcessorRevision          uint16
    83  }
    84  
    85  // Memory_info_ex is different between OSes
    86  type MemoryInfoExStat struct {
    87  }
    88  
    89  type MemoryMapsStat struct {
    90  }
    91  
    92  // ioCounters is an equivalent representation of IO_COUNTERS in the Windows API.
    93  // https://docs.microsoft.com/windows/win32/api/winnt/ns-winnt-io_counters
    94  type ioCounters struct {
    95  	ReadOperationCount  uint64
    96  	WriteOperationCount uint64
    97  	OtherOperationCount uint64
    98  	ReadTransferCount   uint64
    99  	WriteTransferCount  uint64
   100  	OtherTransferCount  uint64
   101  }
   102  
   103  type processBasicInformation32 struct {
   104  	Reserved1       uint32
   105  	PebBaseAddress  uint32
   106  	Reserved2       uint32
   107  	Reserved3       uint32
   108  	UniqueProcessId uint32
   109  	Reserved4       uint32
   110  }
   111  
   112  type processBasicInformation64 struct {
   113  	Reserved1       uint64
   114  	PebBaseAddress  uint64
   115  	Reserved2       uint64
   116  	Reserved3       uint64
   117  	UniqueProcessId uint64
   118  	Reserved4       uint64
   119  }
   120  
   121  type processEnvironmentBlock32 struct {
   122  	Reserved1         [2]uint8
   123  	BeingDebugged     uint8
   124  	Reserved2         uint8
   125  	Reserved3         [2]uint32
   126  	Ldr               uint32
   127  	ProcessParameters uint32
   128  	// More fields which we don't use so far
   129  }
   130  
   131  type processEnvironmentBlock64 struct {
   132  	Reserved1         [2]uint8
   133  	BeingDebugged     uint8
   134  	Reserved2         uint8
   135  	_                 [4]uint8 // padding, since we are 64 bit, the next pointer is 64 bit aligned (when compiling for 32 bit, this is not the case without manual padding)
   136  	Reserved3         [2]uint64
   137  	Ldr               uint64
   138  	ProcessParameters uint64
   139  	// More fields which we don't use so far
   140  }
   141  
   142  type rtlUserProcessParameters32 struct {
   143  	Reserved1 [16]uint8
   144  	ConsoleHandle uint32
   145  	ConsoleFlags uint32
   146  	StdInputHandle uint32
   147  	StdOutputHandle uint32
   148  	StdErrorHandle uint32
   149  	CurrentDirectoryPathNameLength uint16
   150  	_ uint16 // Max Length
   151  	CurrentDirectoryPathAddress uint32
   152  	CurrentDirectoryHandle uint32
   153  	DllPathNameLength uint16
   154  	_ uint16 // Max Length
   155  	DllPathAddress uint32
   156  	ImagePathNameLength uint16
   157  	_ uint16 // Max Length
   158  	ImagePathAddress uint32
   159  	CommandLineLength uint16
   160  	_ uint16 // Max Length
   161  	CommandLineAddress uint32
   162  	EnvironmentAddress uint32
   163  	// More fields which we don't use so far
   164  }
   165  
   166  type rtlUserProcessParameters64 struct {
   167  	Reserved1 [16]uint8
   168  	ConsoleHandle uint64
   169  	ConsoleFlags uint64
   170  	StdInputHandle uint64
   171  	StdOutputHandle uint64
   172  	StdErrorHandle uint64
   173  	CurrentDirectoryPathNameLength uint16
   174  	_ uint16 // Max Length
   175  	_ uint32 // Padding
   176  	CurrentDirectoryPathAddress uint64
   177  	CurrentDirectoryHandle uint64
   178  	DllPathNameLength uint16
   179  	_ uint16 // Max Length
   180  	_ uint32 // Padding
   181  	DllPathAddress uint64
   182  	ImagePathNameLength uint16
   183  	_ uint16 // Max Length
   184  	_ uint32 // Padding
   185  	ImagePathAddress uint64
   186  	CommandLineLength uint16
   187  	_ uint16 // Max Length
   188  	_ uint32 // Padding
   189  	CommandLineAddress uint64
   190  	EnvironmentAddress uint64
   191  	// More fields which we don't use so far
   192  }
   193  
   194  type winLUID struct {
   195  	LowPart  winDWord
   196  	HighPart winLong
   197  }
   198  
   199  // LUID_AND_ATTRIBUTES
   200  type winLUIDAndAttributes struct {
   201  	Luid       winLUID
   202  	Attributes winDWord
   203  }
   204  
   205  // TOKEN_PRIVILEGES
   206  type winTokenPriviledges struct {
   207  	PrivilegeCount winDWord
   208  	Privileges     [1]winLUIDAndAttributes
   209  }
   210  
   211  type winLong int32
   212  type winDWord uint32
   213  
   214  func init() {
   215  	var systemInfo systemInfo
   216  
   217  	procGetNativeSystemInfo.Call(uintptr(unsafe.Pointer(&systemInfo)))
   218  	processorArchitecture = uint(systemInfo.wProcessorArchitecture)
   219  
   220  	// enable SeDebugPrivilege https://github.com/midstar/proci/blob/6ec79f57b90ba3d9efa2a7b16ef9c9369d4be875/proci_windows.go#L80-L119
   221  	handle, err := syscall.GetCurrentProcess()
   222  	if err != nil {
   223  		return
   224  	}
   225  
   226  	var token syscall.Token
   227  	err = syscall.OpenProcessToken(handle, 0x0028, &token)
   228  	if err != nil {
   229  		return
   230  	}
   231  	defer token.Close()
   232  
   233  	tokenPriviledges := winTokenPriviledges{PrivilegeCount: 1}
   234  	lpName := syscall.StringToUTF16("SeDebugPrivilege")
   235  	ret, _, _ := procLookupPrivilegeValue.Call(
   236  		0,
   237  		uintptr(unsafe.Pointer(&lpName[0])),
   238  		uintptr(unsafe.Pointer(&tokenPriviledges.Privileges[0].Luid)))
   239  	if ret == 0 {
   240  		return
   241  	}
   242  
   243  	tokenPriviledges.Privileges[0].Attributes = 0x00000002 // SE_PRIVILEGE_ENABLED
   244  
   245  	procAdjustTokenPrivileges.Call(
   246  		uintptr(token),
   247  		0,
   248  		uintptr(unsafe.Pointer(&tokenPriviledges)),
   249  		uintptr(unsafe.Sizeof(tokenPriviledges)),
   250  		0,
   251  		0)
   252  }
   253  
   254  func pidsWithContext(ctx context.Context) ([]int32, error) {
   255  	// inspired by https://gist.github.com/henkman/3083408
   256  	// and https://github.com/giampaolo/psutil/blob/1c3a15f637521ba5c0031283da39c733fda53e4c/psutil/arch/windows/process_info.c#L315-L329
   257  	var ret []int32
   258  	var read uint32 = 0
   259  	var psSize uint32 = 1024
   260  	const dwordSize uint32 = 4
   261  
   262  	for {
   263  		ps := make([]uint32, psSize)
   264  		if err := windows.EnumProcesses(ps, &read); err != nil {
   265  			return nil, err
   266  		}
   267  		if uint32(len(ps)) == read { // ps buffer was too small to host every results, retry with a bigger one
   268  			psSize += 1024
   269  			continue
   270  		}
   271  		for _, pid := range ps[:read/dwordSize] {
   272  			ret = append(ret, int32(pid))
   273  		}
   274  		return ret, nil
   275  
   276  	}
   277  
   278  }
   279  
   280  func PidExistsWithContext(ctx context.Context, pid int32) (bool, error) {
   281  	if pid == 0 { // special case for pid 0 System Idle Process
   282  		return true, nil
   283  	}
   284  	if pid < 0 {
   285  		return false, fmt.Errorf("invalid pid %v", pid)
   286  	}
   287  	if pid%4 != 0 {
   288  		// OpenProcess will succeed even on non-existing pid here https://devblogs.microsoft.com/oldnewthing/20080606-00/?p=22043
   289  		// so we list every pid just to be sure and be future-proof
   290  		pids, err := PidsWithContext(ctx)
   291  		if err != nil {
   292  			return false, err
   293  		}
   294  		for _, i := range pids {
   295  			if i == pid {
   296  				return true, err
   297  			}
   298  		}
   299  		return false, err
   300  	}
   301  	const STILL_ACTIVE = 259 // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getexitcodeprocess
   302  	h, err := windows.OpenProcess(processQueryInformation, false, uint32(pid))
   303  	if err == windows.ERROR_ACCESS_DENIED {
   304  		return true, nil
   305  	}
   306  	if err == windows.ERROR_INVALID_PARAMETER {
   307  		return false, nil
   308  	}
   309  	if err != nil {
   310  		return false, err
   311  	}
   312  	defer syscall.CloseHandle(syscall.Handle(h))
   313  	var exitCode uint32
   314  	err = windows.GetExitCodeProcess(h, &exitCode)
   315  	return exitCode == STILL_ACTIVE, err
   316  }
   317  
   318  func (p *Process) PpidWithContext(ctx context.Context) (int32, error) {
   319  	// if cached already, return from cache
   320  	cachedPpid := p.getPpid()
   321  	if cachedPpid != 0 {
   322  		return cachedPpid, nil
   323  	}
   324  
   325  	ppid, _, _, err := getFromSnapProcess(p.Pid)
   326  	if err != nil {
   327  		return 0, err
   328  	}
   329  
   330  	// no errors and not cached already, so cache it
   331  	p.setPpid(ppid)
   332  
   333  	return ppid, nil
   334  }
   335  
   336  func (p *Process) NameWithContext(ctx context.Context) (string, error) {
   337  	ppid, _, name, err := getFromSnapProcess(p.Pid)
   338  	if err != nil {
   339  		return "", fmt.Errorf("could not get Name: %s", err)
   340  	}
   341  
   342  	// if no errors and not cached already, cache ppid
   343  	p.parent = ppid
   344  	if 0 == p.getPpid() {
   345  		p.setPpid(ppid)
   346  	}
   347  
   348  	return name, nil
   349  }
   350  
   351  func (p *Process) TgidWithContext(ctx context.Context) (int32, error) {
   352  	return 0, common.ErrNotImplementedError
   353  }
   354  
   355  func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
   356  	c, err := windows.OpenProcess(processQueryInformation, false, uint32(p.Pid))
   357  	if err != nil {
   358  		return "", err
   359  	}
   360  	defer windows.CloseHandle(c)
   361  	buf := make([]uint16, syscall.MAX_LONG_PATH)
   362  	size := uint32(syscall.MAX_LONG_PATH)
   363  	if err := procQueryFullProcessImageNameW.Find(); err == nil { // Vista+
   364  		ret, _, err := procQueryFullProcessImageNameW.Call(
   365  			uintptr(c),
   366  			uintptr(0),
   367  			uintptr(unsafe.Pointer(&buf[0])),
   368  			uintptr(unsafe.Pointer(&size)))
   369  		if ret == 0 {
   370  			return "", err
   371  		}
   372  		return windows.UTF16ToString(buf[:]), nil
   373  	}
   374  	// XP fallback
   375  	ret, _, err := procGetProcessImageFileNameW.Call(uintptr(c), uintptr(unsafe.Pointer(&buf[0])), uintptr(size))
   376  	if ret == 0 {
   377  		return "", err
   378  	}
   379  	return common.ConvertDOSPath(windows.UTF16ToString(buf[:])), nil
   380  }
   381  
   382  func (p *Process) CmdlineWithContext(_ context.Context) (string, error) {
   383  	cmdline, err := getProcessCommandLine(p.Pid)
   384  	if err != nil {
   385  		return "", fmt.Errorf("could not get CommandLine: %s", err)
   386  	}
   387  	return cmdline, nil
   388  }
   389  
   390  func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) {
   391  	cmdline, err := p.CmdlineWithContext(ctx)
   392  	if err != nil {
   393  		return nil, err
   394  	}
   395  	return strings.Split(cmdline, " "), nil
   396  }
   397  
   398  func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) {
   399  	ru, err := getRusage(p.Pid)
   400  	if err != nil {
   401  		return 0, fmt.Errorf("could not get CreationDate: %s", err)
   402  	}
   403  
   404  	return ru.CreationTime.Nanoseconds() / 1000000, nil
   405  }
   406  
   407  func (p *Process) CwdWithContext(_ context.Context) (string, error) {
   408  	h, err := windows.OpenProcess(processQueryInformation|windows.PROCESS_VM_READ, false, uint32(p.Pid))
   409  	if err == windows.ERROR_ACCESS_DENIED || err == windows.ERROR_INVALID_PARAMETER {
   410  		return "", nil
   411  	}
   412  	if err != nil {
   413  		return "", err
   414  	}
   415  	defer syscall.CloseHandle(syscall.Handle(h))
   416  
   417  	procIs32Bits := is32BitProcess(h)
   418  
   419  	if procIs32Bits {
   420  		userProcParams, err := getUserProcessParams32(h)
   421  		if err != nil {
   422  			return "", err
   423  		}
   424  		if userProcParams.CurrentDirectoryPathNameLength > 0 {
   425  			cwd := readProcessMemory(syscall.Handle(h), procIs32Bits, uint64(userProcParams.CurrentDirectoryPathAddress), uint(userProcParams.CurrentDirectoryPathNameLength))
   426  			if len(cwd) != int(userProcParams.CurrentDirectoryPathAddress) {
   427  				return "", errors.New("cannot read current working directory")
   428  			}
   429  
   430  			return convertUTF16ToString(cwd), nil
   431  		}
   432  	} else {
   433  		userProcParams, err := getUserProcessParams64(h)
   434  		if err != nil {
   435  			return "", err
   436  		}
   437  		if userProcParams.CurrentDirectoryPathNameLength > 0 {
   438  			cwd := readProcessMemory(syscall.Handle(h), procIs32Bits, userProcParams.CurrentDirectoryPathAddress, uint(userProcParams.CurrentDirectoryPathNameLength))
   439  			if len(cwd) != int(userProcParams.CurrentDirectoryPathNameLength) {
   440  				return "", errors.New("cannot read current working directory")
   441  			}
   442  
   443  			return convertUTF16ToString(cwd), nil
   444  		}
   445  	}
   446  
   447  	//if we reach here, we have no cwd
   448  	return "", nil
   449  }
   450  
   451  func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) {
   452  	ppid, err := p.PpidWithContext(ctx)
   453  	if err != nil {
   454  		return nil, fmt.Errorf("could not get ParentProcessID: %s", err)
   455  	}
   456  
   457  	return NewProcessWithContext(ctx, ppid)
   458  }
   459  
   460  func (p *Process) StatusWithContext(ctx context.Context) (string, error) {
   461  	return "", common.ErrNotImplementedError
   462  }
   463  
   464  func (p *Process) ForegroundWithContext(ctx context.Context) (bool, error) {
   465  	return false, common.ErrNotImplementedError
   466  }
   467  
   468  func (p *Process) UsernameWithContext(ctx context.Context) (string, error) {
   469  	pid := p.Pid
   470  	c, err := windows.OpenProcess(processQueryInformation, false, uint32(pid))
   471  	if err != nil {
   472  		return "", err
   473  	}
   474  	defer windows.CloseHandle(c)
   475  
   476  	var token syscall.Token
   477  	err = syscall.OpenProcessToken(syscall.Handle(c), syscall.TOKEN_QUERY, &token)
   478  	if err != nil {
   479  		return "", err
   480  	}
   481  	defer token.Close()
   482  	tokenUser, err := token.GetTokenUser()
   483  	if err != nil {
   484  		return "", err
   485  	}
   486  
   487  	user, domain, _, err := tokenUser.User.Sid.LookupAccount("")
   488  	return domain + "\\" + user, err
   489  }
   490  
   491  func (p *Process) UidsWithContext(ctx context.Context) ([]int32, error) {
   492  	return nil, common.ErrNotImplementedError
   493  }
   494  
   495  func (p *Process) GidsWithContext(ctx context.Context) ([]int32, error) {
   496  	return nil, common.ErrNotImplementedError
   497  }
   498  
   499  func (p *Process) GroupsWithContext(ctx context.Context) ([]int32, error) {
   500  	return nil, common.ErrNotImplementedError
   501  }
   502  
   503  func (p *Process) TerminalWithContext(ctx context.Context) (string, error) {
   504  	return "", common.ErrNotImplementedError
   505  }
   506  
   507  // priorityClasses maps a win32 priority class to its WMI equivalent Win32_Process.Priority
   508  // https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-getpriorityclass
   509  // https://docs.microsoft.com/en-us/windows/desktop/cimwin32prov/win32-process
   510  var priorityClasses = map[int]int32{
   511  	0x00008000: 10, // ABOVE_NORMAL_PRIORITY_CLASS
   512  	0x00004000: 6,  // BELOW_NORMAL_PRIORITY_CLASS
   513  	0x00000080: 13, // HIGH_PRIORITY_CLASS
   514  	0x00000040: 4,  // IDLE_PRIORITY_CLASS
   515  	0x00000020: 8,  // NORMAL_PRIORITY_CLASS
   516  	0x00000100: 24, // REALTIME_PRIORITY_CLASS
   517  }
   518  
   519  func (p *Process) NiceWithContext(ctx context.Context) (int32, error) {
   520  	c, err := windows.OpenProcess(processQueryInformation, false, uint32(p.Pid))
   521  	if err != nil {
   522  		return 0, err
   523  	}
   524  	defer windows.CloseHandle(c)
   525  	ret, _, err := procGetPriorityClass.Call(uintptr(c))
   526  	if ret == 0 {
   527  		return 0, err
   528  	}
   529  	priority, ok := priorityClasses[int(ret)]
   530  	if !ok {
   531  		return 0, fmt.Errorf("unknown priority class %v", ret)
   532  	}
   533  	return priority, nil
   534  }
   535  
   536  func (p *Process) IOniceWithContext(ctx context.Context) (int32, error) {
   537  	return 0, common.ErrNotImplementedError
   538  }
   539  
   540  func (p *Process) RlimitWithContext(ctx context.Context) ([]RlimitStat, error) {
   541  	return nil, common.ErrNotImplementedError
   542  }
   543  
   544  func (p *Process) RlimitUsageWithContext(ctx context.Context, gatherUsed bool) ([]RlimitStat, error) {
   545  	return nil, common.ErrNotImplementedError
   546  }
   547  
   548  func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, error) {
   549  	c, err := windows.OpenProcess(processQueryInformation, false, uint32(p.Pid))
   550  	if err != nil {
   551  		return nil, err
   552  	}
   553  	defer windows.CloseHandle(c)
   554  	var ioCounters ioCounters
   555  	ret, _, err := procGetProcessIoCounters.Call(uintptr(c), uintptr(unsafe.Pointer(&ioCounters)))
   556  	if ret == 0 {
   557  		return nil, err
   558  	}
   559  	stats := &IOCountersStat{
   560  		ReadCount:  ioCounters.ReadOperationCount,
   561  		ReadBytes:  ioCounters.ReadTransferCount,
   562  		WriteCount: ioCounters.WriteOperationCount,
   563  		WriteBytes: ioCounters.WriteTransferCount,
   564  	}
   565  
   566  	return stats, nil
   567  }
   568  
   569  func (p *Process) NumCtxSwitchesWithContext(ctx context.Context) (*NumCtxSwitchesStat, error) {
   570  	return nil, common.ErrNotImplementedError
   571  }
   572  
   573  func (p *Process) NumFDsWithContext(ctx context.Context) (int32, error) {
   574  	return 0, common.ErrNotImplementedError
   575  }
   576  
   577  func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) {
   578  	ppid, ret, _, err := getFromSnapProcess(p.Pid)
   579  	if err != nil {
   580  		return 0, err
   581  	}
   582  
   583  	// if no errors and not cached already, cache ppid
   584  	p.parent = ppid
   585  	if 0 == p.getPpid() {
   586  		p.setPpid(ppid)
   587  	}
   588  
   589  	return ret, nil
   590  }
   591  
   592  func (p *Process) ThreadsWithContext(ctx context.Context) (map[int32]*cpu.TimesStat, error) {
   593  	return nil, common.ErrNotImplementedError
   594  }
   595  
   596  func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) {
   597  	sysTimes, err := getProcessCPUTimes(p.Pid)
   598  	if err != nil {
   599  		return nil, err
   600  	}
   601  
   602  	// User and kernel times are represented as a FILETIME structure
   603  	// which contains a 64-bit value representing the number of
   604  	// 100-nanosecond intervals since January 1, 1601 (UTC):
   605  	// http://msdn.microsoft.com/en-us/library/ms724284(VS.85).aspx
   606  	// To convert it into a float representing the seconds that the
   607  	// process has executed in user/kernel mode I borrowed the code
   608  	// below from psutil's _psutil_windows.c, and in turn from Python's
   609  	// Modules/posixmodule.c
   610  
   611  	user := float64(sysTimes.UserTime.HighDateTime)*429.4967296 + float64(sysTimes.UserTime.LowDateTime)*1e-7
   612  	kernel := float64(sysTimes.KernelTime.HighDateTime)*429.4967296 + float64(sysTimes.KernelTime.LowDateTime)*1e-7
   613  
   614  	return &cpu.TimesStat{
   615  		User:   user,
   616  		System: kernel,
   617  	}, nil
   618  }
   619  
   620  func (p *Process) CPUAffinityWithContext(ctx context.Context) ([]int32, error) {
   621  	return nil, common.ErrNotImplementedError
   622  }
   623  
   624  func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) {
   625  	mem, err := getMemoryInfo(p.Pid)
   626  	if err != nil {
   627  		return nil, err
   628  	}
   629  
   630  	ret := &MemoryInfoStat{
   631  		RSS: uint64(mem.WorkingSetSize),
   632  		VMS: uint64(mem.PagefileUsage),
   633  	}
   634  
   635  	return ret, nil
   636  }
   637  
   638  func (p *Process) MemoryInfoExWithContext(ctx context.Context) (*MemoryInfoExStat, error) {
   639  	return nil, common.ErrNotImplementedError
   640  }
   641  
   642  func (p *Process) PageFaultsWithContext(ctx context.Context) (*PageFaultsStat, error) {
   643  	return nil, common.ErrNotImplementedError
   644  }
   645  
   646  func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
   647  	out := []*Process{}
   648  	snap, err := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, uint32(0))
   649  	if err != nil {
   650  		return out, err
   651  	}
   652  	defer windows.CloseHandle(snap)
   653  	var pe32 windows.ProcessEntry32
   654  	pe32.Size = uint32(unsafe.Sizeof(pe32))
   655  	if err := windows.Process32First(snap, &pe32); err != nil {
   656  		return out, err
   657  	}
   658  	for {
   659  		if pe32.ParentProcessID == uint32(p.Pid) {
   660  			p, err := NewProcessWithContext(ctx, int32(pe32.ProcessID))
   661  			if err == nil {
   662  				out = append(out, p)
   663  			}
   664  		}
   665  		if err = windows.Process32Next(snap, &pe32); err != nil {
   666  			break
   667  		}
   668  	}
   669  	return out, nil
   670  }
   671  
   672  func (p *Process) OpenFilesWithContext(ctx context.Context) ([]OpenFilesStat, error) {
   673  	files := make([]OpenFilesStat, 0)
   674  	fileExists := make(map[string]bool)
   675  
   676  	process, err := windows.OpenProcess(common.ProcessQueryInformation, false, uint32(p.Pid))
   677  	if err != nil {
   678  		return nil, err
   679  	}
   680  
   681  	buffer := make([]byte, 1024)
   682  	var size uint32
   683  
   684  	st := common.CallWithExpandingBuffer(
   685  		func() common.NtStatus {
   686  			return common.NtQuerySystemInformation(
   687  				common.SystemExtendedHandleInformationClass,
   688  				&buffer[0],
   689  				uint32(len(buffer)),
   690  				&size,
   691  			)
   692  		},
   693  		&buffer,
   694  		&size,
   695  	)
   696  	if st.IsError() {
   697  		return nil, st.Error()
   698  	}
   699  
   700  	handlesList := (*common.SystemExtendedHandleInformation)(unsafe.Pointer(&buffer[0]))
   701  	handles := make([]common.SystemExtendedHandleTableEntryInformation, int(handlesList.NumberOfHandles))
   702  	hdr := (*reflect.SliceHeader)(unsafe.Pointer(&handles))
   703  	hdr.Data = uintptr(unsafe.Pointer(&handlesList.Handles[0]))
   704  
   705  	currentProcess, err := windows.GetCurrentProcess()
   706  	if err != nil {
   707  		return nil, err
   708  	}
   709  
   710  	for _, handle := range handles {
   711  		var file uintptr
   712  		if int32(handle.UniqueProcessId) != p.Pid {
   713  			continue
   714  		}
   715  		if windows.DuplicateHandle(process, windows.Handle(handle.HandleValue), currentProcess, (*windows.Handle)(&file),
   716  			0, true, windows.DUPLICATE_SAME_ACCESS) != nil {
   717  			continue
   718  		}
   719  		fileType, _ := windows.GetFileType(windows.Handle(file))
   720  		if fileType != windows.FILE_TYPE_DISK {
   721  			continue
   722  		}
   723  
   724  		var fileName string
   725  		ch := make(chan struct{})
   726  
   727  		go func() {
   728  			var buf [syscall.MAX_LONG_PATH]uint16
   729  			n, err := windows.GetFinalPathNameByHandle(windows.Handle(file), &buf[0], syscall.MAX_LONG_PATH, 0)
   730  			if err != nil {
   731  				return
   732  			}
   733  
   734  			fileName = string(utf16.Decode(buf[:n]))
   735  			ch <- struct{}{}
   736  		}()
   737  
   738  		select {
   739  		case <-time.NewTimer(100 * time.Millisecond).C:
   740  			continue
   741  		case <-ch:
   742  			fileInfo, _ := os.Stat(fileName)
   743  			if fileInfo.IsDir() {
   744  				continue
   745  			}
   746  
   747  			if _, exists := fileExists[fileName]; !exists {
   748  				files = append(files, OpenFilesStat{
   749  					Path: fileName,
   750  					Fd:   uint64(file),
   751  				})
   752  				fileExists[fileName] = true
   753  			}
   754  		case <-ctx.Done():
   755  			return files, ctx.Err()
   756  		}
   757  	}
   758  
   759  	return files, nil
   760  }
   761  
   762  func (p *Process) ConnectionsWithContext(ctx context.Context) ([]net.ConnectionStat, error) {
   763  	return net.ConnectionsPidWithContext(ctx, "all", p.Pid)
   764  }
   765  
   766  func (p *Process) ConnectionsMaxWithContext(ctx context.Context, max int) ([]net.ConnectionStat, error) {
   767  	return nil, common.ErrNotImplementedError
   768  }
   769  
   770  func (p *Process) NetIOCountersWithContext(ctx context.Context, pernic bool) ([]net.IOCountersStat, error) {
   771  	return nil, common.ErrNotImplementedError
   772  }
   773  
   774  func (p *Process) MemoryMapsWithContext(ctx context.Context, grouped bool) (*[]MemoryMapsStat, error) {
   775  	return nil, common.ErrNotImplementedError
   776  }
   777  
   778  func (p *Process) SendSignalWithContext(ctx context.Context, sig syscall.Signal) error {
   779  	return common.ErrNotImplementedError
   780  }
   781  
   782  func (p *Process) SuspendWithContext(ctx context.Context) error {
   783  	c, err := windows.OpenProcess(windows.PROCESS_SUSPEND_RESUME, false, uint32(p.Pid))
   784  	if err != nil {
   785  		return err
   786  	}
   787  	defer windows.CloseHandle(c)
   788  
   789  	r1, _, _ := procNtSuspendProcess.Call(uintptr(c))
   790  	if r1 != 0 {
   791  		// See https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55
   792  		return fmt.Errorf("NtStatus='0x%.8X'", r1)
   793  	}
   794  
   795  	return nil
   796  }
   797  
   798  func (p *Process) ResumeWithContext(ctx context.Context) error {
   799  	c, err := windows.OpenProcess(windows.PROCESS_SUSPEND_RESUME, false, uint32(p.Pid))
   800  	if err != nil {
   801  		return err
   802  	}
   803  	defer windows.CloseHandle(c)
   804  
   805  	r1, _, _ := procNtResumeProcess.Call(uintptr(c))
   806  	if r1 != 0 {
   807  		// See https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55
   808  		return fmt.Errorf("NtStatus='0x%.8X'", r1)
   809  	}
   810  
   811  	return nil
   812  }
   813  
   814  func (p *Process) TerminateWithContext(ctx context.Context) error {
   815  	proc, err := windows.OpenProcess(windows.PROCESS_TERMINATE, false, uint32(p.Pid))
   816  	if err != nil {
   817  		return err
   818  	}
   819  	err = windows.TerminateProcess(proc, 0)
   820  	windows.CloseHandle(proc)
   821  	return err
   822  }
   823  
   824  func (p *Process) KillWithContext(ctx context.Context) error {
   825  	process, err := os.FindProcess(int(p.Pid))
   826  	if err != nil {
   827  		return err
   828  	}
   829  	return process.Kill()
   830  }
   831  
   832  func (p *Process) EnvironWithContext(ctx context.Context) ([]string, error) {
   833  	envVars, err := getProcessEnvironmentVariables(p.Pid, ctx)
   834  	if err != nil {
   835  		return nil, fmt.Errorf("could not get environment variables: %s", err)
   836  	}
   837  	return envVars, nil
   838  }
   839  
   840  // retrieve Ppid in a thread-safe manner
   841  func (p *Process) getPpid() int32 {
   842  	p.parentMutex.RLock()
   843  	defer p.parentMutex.RUnlock()
   844  	return p.parent
   845  }
   846  
   847  // cache Ppid in a thread-safe manner (WINDOWS ONLY)
   848  // see https://psutil.readthedocs.io/en/latest/#psutil.Process.ppid
   849  func (p *Process) setPpid(ppid int32) {
   850  	p.parentMutex.Lock()
   851  	defer p.parentMutex.Unlock()
   852  	p.parent = ppid
   853  }
   854  
   855  func getFromSnapProcess(pid int32) (int32, int32, string, error) {
   856  	snap, err := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, uint32(pid))
   857  	if err != nil {
   858  		return 0, 0, "", err
   859  	}
   860  	defer windows.CloseHandle(snap)
   861  	var pe32 windows.ProcessEntry32
   862  	pe32.Size = uint32(unsafe.Sizeof(pe32))
   863  	if err = windows.Process32First(snap, &pe32); err != nil {
   864  		return 0, 0, "", err
   865  	}
   866  	for {
   867  		if pe32.ProcessID == uint32(pid) {
   868  			szexe := windows.UTF16ToString(pe32.ExeFile[:])
   869  			return int32(pe32.ParentProcessID), int32(pe32.Threads), szexe, nil
   870  		}
   871  		if err = windows.Process32Next(snap, &pe32); err != nil {
   872  			break
   873  		}
   874  	}
   875  	return 0, 0, "", fmt.Errorf("couldn't find pid: %d", pid)
   876  }
   877  
   878  func ProcessesWithContext(ctx context.Context) ([]*Process, error) {
   879  	out := []*Process{}
   880  
   881  	pids, err := PidsWithContext(ctx)
   882  	if err != nil {
   883  		return out, fmt.Errorf("could not get Processes %s", err)
   884  	}
   885  
   886  	for _, pid := range pids {
   887  		p, err := NewProcessWithContext(ctx, pid)
   888  		if err != nil {
   889  			continue
   890  		}
   891  		out = append(out, p)
   892  	}
   893  
   894  	return out, nil
   895  }
   896  
   897  func getRusage(pid int32) (*windows.Rusage, error) {
   898  	var CPU windows.Rusage
   899  
   900  	c, err := windows.OpenProcess(processQueryInformation, false, uint32(pid))
   901  	if err != nil {
   902  		return nil, err
   903  	}
   904  	defer windows.CloseHandle(c)
   905  
   906  	if err := windows.GetProcessTimes(c, &CPU.CreationTime, &CPU.ExitTime, &CPU.KernelTime, &CPU.UserTime); err != nil {
   907  		return nil, err
   908  	}
   909  
   910  	return &CPU, nil
   911  }
   912  
   913  func getMemoryInfo(pid int32) (PROCESS_MEMORY_COUNTERS, error) {
   914  	var mem PROCESS_MEMORY_COUNTERS
   915  	c, err := windows.OpenProcess(processQueryInformation, false, uint32(pid))
   916  	if err != nil {
   917  		return mem, err
   918  	}
   919  	defer windows.CloseHandle(c)
   920  	if err := getProcessMemoryInfo(c, &mem); err != nil {
   921  		return mem, err
   922  	}
   923  
   924  	return mem, err
   925  }
   926  
   927  func getProcessMemoryInfo(h windows.Handle, mem *PROCESS_MEMORY_COUNTERS) (err error) {
   928  	r1, _, e1 := syscall.Syscall(procGetProcessMemoryInfo.Addr(), 3, uintptr(h), uintptr(unsafe.Pointer(mem)), uintptr(unsafe.Sizeof(*mem)))
   929  	if r1 == 0 {
   930  		if e1 != 0 {
   931  			err = error(e1)
   932  		} else {
   933  			err = syscall.EINVAL
   934  		}
   935  	}
   936  	return
   937  }
   938  
   939  type SYSTEM_TIMES struct {
   940  	CreateTime syscall.Filetime
   941  	ExitTime   syscall.Filetime
   942  	KernelTime syscall.Filetime
   943  	UserTime   syscall.Filetime
   944  }
   945  
   946  func getProcessCPUTimes(pid int32) (SYSTEM_TIMES, error) {
   947  	var times SYSTEM_TIMES
   948  
   949  	h, err := windows.OpenProcess(processQueryInformation, false, uint32(pid))
   950  	if err != nil {
   951  		return times, err
   952  	}
   953  	defer windows.CloseHandle(h)
   954  
   955  	err = syscall.GetProcessTimes(
   956  		syscall.Handle(h),
   957  		&times.CreateTime,
   958  		&times.ExitTime,
   959  		&times.KernelTime,
   960  		&times.UserTime,
   961  	)
   962  
   963  	return times, err
   964  }
   965  
   966  func getUserProcessParams32(handle windows.Handle) (rtlUserProcessParameters32, error) {
   967  	pebAddress, err := queryPebAddress(syscall.Handle(handle), true)
   968  	if err != nil {
   969  		return rtlUserProcessParameters32{}, fmt.Errorf("cannot locate process PEB: %w", err)
   970  	}
   971  
   972  	buf := readProcessMemory(syscall.Handle(handle), true, pebAddress, uint(unsafe.Sizeof(processEnvironmentBlock32{})))
   973  	if len(buf) != int(unsafe.Sizeof(processEnvironmentBlock32{})) {
   974  		return rtlUserProcessParameters32{}, fmt.Errorf("cannot read process PEB")
   975  	}
   976  	peb := (*processEnvironmentBlock32)(unsafe.Pointer(&buf[0]))
   977  	userProcessAddress := uint64(peb.ProcessParameters)
   978  	buf = readProcessMemory(syscall.Handle(handle), true, userProcessAddress, uint(unsafe.Sizeof(rtlUserProcessParameters32{})))
   979  	if len(buf) != int(unsafe.Sizeof(rtlUserProcessParameters32{})) {
   980  		return rtlUserProcessParameters32{}, fmt.Errorf("cannot read user process parameters")
   981  	}
   982  	return *(*rtlUserProcessParameters32)(unsafe.Pointer(&buf[0])), nil
   983  }
   984  
   985  func getUserProcessParams64(handle windows.Handle) (rtlUserProcessParameters64, error) {
   986  	pebAddress, err := queryPebAddress(syscall.Handle(handle), false)
   987  	if err != nil {
   988  		return rtlUserProcessParameters64{}, fmt.Errorf("cannot locate process PEB: %w", err)
   989  	}
   990  
   991  	buf := readProcessMemory(syscall.Handle(handle), false, pebAddress, uint(unsafe.Sizeof(processEnvironmentBlock64{})))
   992  	if len(buf) != int(unsafe.Sizeof(processEnvironmentBlock64{})) {
   993  		return rtlUserProcessParameters64{}, fmt.Errorf("cannot read process PEB")
   994  	}
   995  	peb := (*processEnvironmentBlock64)(unsafe.Pointer(&buf[0]))
   996  	userProcessAddress := peb.ProcessParameters
   997  	buf = readProcessMemory(syscall.Handle(handle), false, userProcessAddress, uint(unsafe.Sizeof(rtlUserProcessParameters64{})))
   998  	if len(buf) != int(unsafe.Sizeof(rtlUserProcessParameters64{})) {
   999  		return rtlUserProcessParameters64{}, fmt.Errorf("cannot read user process parameters")
  1000  	}
  1001  	return *(*rtlUserProcessParameters64)(unsafe.Pointer(&buf[0])), nil
  1002  }
  1003  
  1004  func is32BitProcess(h windows.Handle) bool {
  1005  	const (
  1006  		PROCESSOR_ARCHITECTURE_INTEL = 0
  1007  		PROCESSOR_ARCHITECTURE_ARM   = 5
  1008  		PROCESSOR_ARCHITECTURE_ARM64 = 12
  1009  		PROCESSOR_ARCHITECTURE_IA64  = 6
  1010  		PROCESSOR_ARCHITECTURE_AMD64 = 9
  1011  	)
  1012  
  1013  	var procIs32Bits bool
  1014  	switch processorArchitecture {
  1015  	case PROCESSOR_ARCHITECTURE_INTEL:
  1016  		fallthrough
  1017  	case PROCESSOR_ARCHITECTURE_ARM:
  1018  		procIs32Bits = true
  1019  	case PROCESSOR_ARCHITECTURE_ARM64:
  1020  		fallthrough
  1021  	case PROCESSOR_ARCHITECTURE_IA64:
  1022  		fallthrough
  1023  	case PROCESSOR_ARCHITECTURE_AMD64:
  1024  		var wow64 uint
  1025  
  1026  		ret, _, _ := common.ProcNtQueryInformationProcess.Call(
  1027  			uintptr(h),
  1028  			uintptr(common.ProcessWow64Information),
  1029  			uintptr(unsafe.Pointer(&wow64)),
  1030  			uintptr(unsafe.Sizeof(wow64)),
  1031  			uintptr(0),
  1032  		)
  1033  		if int(ret) >= 0 {
  1034  			if wow64 != 0 {
  1035  				procIs32Bits = true
  1036  			}
  1037  		} else {
  1038  			//if the OS does not support the call, we fallback into the bitness of the app
  1039  			if unsafe.Sizeof(wow64) == 4 {
  1040  				procIs32Bits = true
  1041  			}
  1042  		}
  1043  
  1044  	default:
  1045  		//for other unknown platforms, we rely on process platform
  1046  		if unsafe.Sizeof(processorArchitecture) == 8 {
  1047  			procIs32Bits = false
  1048  		} else {
  1049  			procIs32Bits = true
  1050  		}
  1051  	}
  1052  	return procIs32Bits
  1053  }
  1054  
  1055  func getProcessEnvironmentVariables(pid int32, ctx context.Context) ([]string, error) {
  1056  	h, err := windows.OpenProcess(processQueryInformation|windows.PROCESS_VM_READ, false, uint32(pid))
  1057  	if err == windows.ERROR_ACCESS_DENIED || err == windows.ERROR_INVALID_PARAMETER {
  1058  		return nil, nil
  1059  	}
  1060  	if err != nil {
  1061  		return nil, err
  1062  	}
  1063  	defer syscall.CloseHandle(syscall.Handle(h))
  1064  
  1065  	procIs32Bits := is32BitProcess(h)
  1066  
  1067  	var processParameterBlockAddress uint64
  1068  
  1069  	if procIs32Bits {
  1070  		peb, err := getUserProcessParams32(h)
  1071  		if err != nil {
  1072  			return nil, err
  1073  		}
  1074  		processParameterBlockAddress = uint64(peb.EnvironmentAddress)
  1075  	} else {
  1076  		peb, err := getUserProcessParams64(h)
  1077  		if err != nil {
  1078  			return nil, err
  1079  		}
  1080  		processParameterBlockAddress = peb.EnvironmentAddress
  1081  	}
  1082  	envvarScanner := bufio.NewScanner(&processReader{
  1083  		processHandle:  h,
  1084  		is32BitProcess: procIs32Bits,
  1085  		offset:         processParameterBlockAddress,
  1086  	})
  1087  	envvarScanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) {
  1088  		if atEOF && len(data) == 0 {
  1089  			return 0, nil, nil
  1090  		}
  1091  		// Check for UTF-16 zero character
  1092  		for i := 0; i < len(data)-1; i += 2 {
  1093  			if data[i] == 0 && data[i+1] == 0 {
  1094  				return i + 2, data[0:i], nil
  1095  			}
  1096  		}
  1097  		if atEOF {
  1098  			return len(data), data, nil
  1099  		}
  1100  		// Request more data
  1101  		return 0, nil, nil
  1102  	})
  1103  	var envVars []string
  1104  	for envvarScanner.Scan() {
  1105  		entry := envvarScanner.Bytes()
  1106  		if len(entry) == 0 {
  1107  			break // Block is finished
  1108  		}
  1109  		envVars = append(envVars, convertUTF16ToString(entry))
  1110  		select {
  1111  		case <-ctx.Done():
  1112  			break
  1113  		default:
  1114  			continue
  1115  		}
  1116  	}
  1117  	if err := envvarScanner.Err(); err != nil {
  1118  		return nil, err
  1119  	}
  1120  	return envVars, nil
  1121  }
  1122  
  1123  type processReader struct {
  1124  	processHandle  windows.Handle
  1125  	is32BitProcess bool
  1126  	offset         uint64
  1127  }
  1128  
  1129  func (p *processReader) Read(buf []byte) (int, error) {
  1130  	processMemory := readProcessMemory(syscall.Handle(p.processHandle), p.is32BitProcess, p.offset, uint(len(buf)))
  1131  	if len(processMemory) == 0 {
  1132  		return 0, io.EOF
  1133  	}
  1134  	copy(buf, processMemory)
  1135  	p.offset += uint64(len(processMemory))
  1136  	return len(processMemory), nil
  1137  }
  1138  
  1139  func getProcessCommandLine(pid int32) (string, error) {
  1140  	h, err := windows.OpenProcess(processQueryInformation|windows.PROCESS_VM_READ, false, uint32(pid))
  1141  	if err == windows.ERROR_ACCESS_DENIED || err == windows.ERROR_INVALID_PARAMETER {
  1142  		return "", nil
  1143  	}
  1144  	if err != nil {
  1145  		return "", err
  1146  	}
  1147  	defer syscall.CloseHandle(syscall.Handle(h))
  1148  
  1149  	procIs32Bits := is32BitProcess(h)
  1150  
  1151  	if procIs32Bits {
  1152  		userProcParams, err := getUserProcessParams32(h)
  1153  		if err != nil {
  1154  			return "", err
  1155  		}
  1156  		if userProcParams.CommandLineLength > 0 {
  1157  			cmdLine := readProcessMemory(syscall.Handle(h), procIs32Bits, uint64(userProcParams.CommandLineAddress), uint(userProcParams.CommandLineLength))
  1158  			if len(cmdLine) != int(userProcParams.CommandLineLength) {
  1159  				return "", errors.New("cannot read cmdline")
  1160  			}
  1161  
  1162  			return convertUTF16ToString(cmdLine), nil
  1163  		}
  1164  	} else {
  1165  		userProcParams, err := getUserProcessParams64(h)
  1166  		if err != nil {
  1167  			return "", err
  1168  		}
  1169  		if userProcParams.CommandLineLength > 0 {
  1170  			cmdLine := readProcessMemory(syscall.Handle(h), procIs32Bits, userProcParams.CommandLineAddress, uint(userProcParams.CommandLineLength))
  1171  			if len(cmdLine) != int(userProcParams.CommandLineLength) {
  1172  				return "", errors.New("cannot read cmdline")
  1173  			}
  1174  
  1175  			return convertUTF16ToString(cmdLine), nil
  1176  		}
  1177  	}
  1178  
  1179  	//if we reach here, we have no command line
  1180  	return "", nil
  1181  }
  1182  
  1183  func convertUTF16ToString(src []byte) string {
  1184  	srcLen := len(src) / 2
  1185  
  1186  	codePoints := make([]uint16, srcLen)
  1187  
  1188  	srcIdx := 0
  1189  	for i := 0; i < srcLen; i++ {
  1190  		codePoints[i] = uint16(src[srcIdx]) | uint16(src[srcIdx+1])<<8
  1191  		srcIdx += 2
  1192  	}
  1193  	return syscall.UTF16ToString(codePoints)
  1194  }
  1195  

View as plain text