process_windows.go 32 KB

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