Skip to content

Commit ac405b0

Browse files
committed
feat(Lib/os): getppid; fix(js): getpid() not work on js
1 parent a814951 commit ac405b0

File tree

1 file changed

+126
-4
lines changed

1 file changed

+126
-4
lines changed
Lines changed: 126 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,130 @@
11

22
import std/os
3-
when defined(js):
3+
const JS = defined(js)
4+
when JS:
45
import ../common
5-
proc getpid*(): int{.importDenoOrProcess(pid).}
6+
let pid{.importDenoOrProcess(pid).}: int
7+
8+
proc getpid*(): int =
9+
when JS: pid else: getCurrentProcessId()
10+
11+
when defined(windows):
12+
import std/winlean
13+
14+
type
15+
ULONG = uint32
16+
ULONG_PTR = ULONG
17+
NTSTATUS = uint32 ##\
18+
## https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/87fba13e-bf06-450e-83b1-9241dc81e781
19+
KPRIORITY{.importc, header: "<ks.h>".} = object
20+
PriorityClass, PrioritySubClass: ULONG
21+
type
22+
ProcessBasicInformationFull = object
23+
## `_PROCESS_BASIC_INFORMATION`
24+
## https://learn.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntqueryinformationprocess#process_basic_information
25+
ExitStatus: NTSTATUS
26+
PebBaseAddress: pointer ## `PEB *` in `<winternl.h>`
27+
AffinityMask: ULONG_PTR
28+
BasePriority: KPRIORITY
29+
UniqueProcessId: ULONG_PTR
30+
InheritedFromUniqueProcessId: ULONG_PTR
31+
32+
PROCESSINFOCLASS = enum
33+
ProcessBasicInformation = cint 0
34+
35+
PNT_QUERY_INFORMATION_PROCESS = proc(handle: Handle, infoClass: PROCESSINFOCLASS,
36+
info: pointer, infoLen: ULONG, retLen: ptr ULONG): NTSTATUS {.stdcall.}
37+
HMODULE = Handle
38+
39+
FARPROC = pointer ## XXX: pointer to function in C
40+
41+
proc NT_SUCCESS(status: NTSTATUS): bool{.importc, nodecl.}
42+
43+
proc GetModuleHandleA(lpModuleName: cstring): HMODULE {.importc, header: "<libloaderapi.h>", stdcall.} ## \
44+
## DO NOT pass result to FreeLibrary,
45+
## ref https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandlew
46+
proc GetProcAddress(hModule: HMODULE, lpProcName: cstring): FARPROC {.importc, header: "<libloaderapi.h>", stdcall.}
47+
48+
var cachedPpid: ULONG = 0
49+
proc win32_getppid_fast(): uint =
50+
## This function returns the process ID of the parent process.
51+
## Returns 0 on failure.
52+
if cachedPpid != 0:
53+
return cachedPpid
54+
55+
let ntdll = GetModuleHandleA("ntdll.dll")
56+
if ntdll == 0:
57+
return 0
58+
59+
let pNtQueryInformationProcess = cast[PNT_QUERY_INFORMATION_PROCESS](
60+
GetProcAddress(ntdll, "NtQueryInformationProcess"))
61+
## https://learn.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntqueryinformationprocess
62+
if pNtQueryInformationProcess.isNil:
63+
return 0
64+
65+
var basicInfo: ProcessBasicInformationFull
66+
let status = pNtQueryInformationProcess(getCurrentProcess(), ProcessBasicInformation,
67+
addr basicInfo, sizeof(basicInfo).ULONG, nil)
68+
69+
if not NT_SUCCESS(status):
70+
return 0
71+
# Perform sanity check on the parent process ID we received from NtQueryInformationProcess.
72+
# The check covers values which exceed the 32-bit range (if running on x64) as well as
73+
# zero and (ULONG) -1.
74+
75+
if basicInfo.InheritedFromUniqueProcessId == 0 or
76+
basicInfo.InheritedFromUniqueProcessId >= ULONG.high:
77+
return 0
78+
79+
cachedPpid = ULONG basicInfo.InheritedFromUniqueProcessId
80+
return cachedPpid
81+
82+
type
83+
PSS_CAPTURE_FLAGS = enum
84+
PSS_CAPTURE_NONE = cint 0
85+
PSS_QUERY_INFORMATION_CLASS = enum
86+
PSS_QUERY_PROCESS_INFORMATION = cint 0
87+
const
88+
ERROR_SUCCESS = 0
89+
{.push header: "<processsnapshot.h>".}
90+
{.push stdcall.}
91+
proc PssCaptureSnapshot(process: Handle, flags: PSS_CAPTURE_FLAGS,
92+
snapshotFlags: uint32, snapshot: ptr Handle): uint32 {.importc.}
93+
proc PssQuerySnapshot(snapshot: Handle, infoClass: PSS_QUERY_INFORMATION_CLASS,
94+
info: pointer, infoLen: uint32): uint32 {.importc.}
95+
proc PssFreeSnapshot(process: Handle, snapshot: Handle): uint32 {.importc, discardable.}
96+
{.pop.}
97+
type PSS_PROCESS_INFORMATION{.importc.} = object
98+
ParentProcessId{.importc.}: uint32
99+
{.pop.}
100+
101+
proc win32_getppid(): int =
102+
let pid = win32_getppid_fast()
103+
if pid != 0:
104+
return int(pid)
105+
106+
# Fallback to PSS API if fast method fails
107+
var snapshot: Handle
108+
let process = getCurrentProcess()
109+
let error = PssCaptureSnapshot(process, PSS_CAPTURE_NONE, 0, addr snapshot)
110+
if error != ERROR_SUCCESS:
111+
return 0
112+
113+
var info: PSS_PROCESS_INFORMATION
114+
let queryError = PssQuerySnapshot(snapshot, PSS_QUERY_PROCESS_INFORMATION,
115+
addr info, uint32(sizeof(info)))
116+
result = if queryError == ERROR_SUCCESS: int(info.ParentProcessId)
117+
else: 0
118+
119+
PssFreeSnapshot(process, snapshot)
120+
elif defined(js):
121+
let ppid{.importDenoOrProcess(ppid).}: int
6122
else:
7-
proc getpid*(): int =
8-
getCurrentProcessId()
123+
import std/posix
124+
proc getppid*(): int =
125+
## Returns the parent's process id.
126+
## If the parent process has already exited, Windows machines will still
127+
## return its id; others systems will return the id of the 'init' process (1).
128+
when defined(windows): win32_getppid()
129+
elif JS: ppid
130+
else: int posix.getppid()

0 commit comments

Comments
 (0)