process_windows.odin 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739
  1. //+private file
  2. package os2
  3. import "base:runtime"
  4. import "core:strings"
  5. import win32 "core:sys/windows"
  6. import "core:time"
  7. @(private="package")
  8. _exit :: proc "contextless" (code: int) -> ! {
  9. win32.ExitProcess(u32(code))
  10. }
  11. @(private="package")
  12. _get_uid :: proc() -> int {
  13. return -1
  14. }
  15. @(private="package")
  16. _get_euid :: proc() -> int {
  17. return -1
  18. }
  19. @(private="package")
  20. _get_gid :: proc() -> int {
  21. return -1
  22. }
  23. @(private="package")
  24. _get_egid :: proc() -> int {
  25. return -1
  26. }
  27. @(private="package")
  28. _get_pid :: proc() -> int {
  29. return int(win32.GetCurrentProcessId())
  30. }
  31. @(private="package")
  32. _get_ppid :: proc() -> int {
  33. our_pid := win32.GetCurrentProcessId()
  34. snap := win32.CreateToolhelp32Snapshot(win32.TH32CS_SNAPPROCESS, 0)
  35. if snap == win32.INVALID_HANDLE_VALUE {
  36. return -1
  37. }
  38. defer win32.CloseHandle(snap)
  39. entry := win32.PROCESSENTRY32W { dwSize = size_of(win32.PROCESSENTRY32W) }
  40. for status := win32.Process32FirstW(snap, &entry); status; /**/ {
  41. if entry.th32ProcessID == our_pid {
  42. return int(entry.th32ParentProcessID)
  43. }
  44. status = win32.Process32NextW(snap, &entry)
  45. }
  46. return -1
  47. }
  48. @(private="package")
  49. _process_list :: proc(allocator: runtime.Allocator) -> (list: []int, err: Error) {
  50. snap := win32.CreateToolhelp32Snapshot(win32.TH32CS_SNAPPROCESS, 0)
  51. if snap == win32.INVALID_HANDLE_VALUE {
  52. err = _get_platform_error()
  53. return
  54. }
  55. list_d := make([dynamic]int, allocator) or_return
  56. entry := win32.PROCESSENTRY32W{dwSize = size_of(win32.PROCESSENTRY32W)}
  57. status := win32.Process32FirstW(snap, &entry)
  58. for status {
  59. append(&list_d, int(entry.th32ProcessID))
  60. status = win32.Process32NextW(snap, &entry)
  61. }
  62. list = list_d[:]
  63. return
  64. }
  65. @(require_results)
  66. read_memory_as_struct :: proc(h: win32.HANDLE, addr: rawptr, dest: ^$T) -> (bytes_read: uint, err: Error) {
  67. if !win32.ReadProcessMemory(h, addr, dest, size_of(T), &bytes_read) {
  68. err = _get_platform_error()
  69. }
  70. return
  71. }
  72. @(require_results)
  73. read_memory_as_slice :: proc(h: win32.HANDLE, addr: rawptr, dest: []$T) -> (bytes_read: uint, err: Error) {
  74. if !win32.ReadProcessMemory(h, addr, raw_data(dest), len(dest)*size_of(T), &bytes_read) {
  75. err = _get_platform_error()
  76. }
  77. return
  78. }
  79. @(private="package")
  80. _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
  81. info.pid = pid
  82. // Note(flysand): Open the process handle right away to prevent some race
  83. // conditions. Once the handle is open, the process will be kept alive by
  84. // the OS.
  85. ph := win32.INVALID_HANDLE_VALUE
  86. if selection >= {.Command_Line, .Environment, .Working_Dir, .Username} {
  87. ph = win32.OpenProcess(
  88. win32.PROCESS_QUERY_LIMITED_INFORMATION | win32.PROCESS_VM_READ,
  89. false,
  90. u32(pid),
  91. )
  92. if ph == win32.INVALID_HANDLE_VALUE {
  93. err = _get_platform_error()
  94. return
  95. }
  96. }
  97. defer if ph != win32.INVALID_HANDLE_VALUE {
  98. win32.CloseHandle(ph)
  99. }
  100. snapshot_process: if selection >= {.PPid, .Priority} {
  101. entry, entry_err := _process_entry_by_pid(info.pid)
  102. if entry_err != nil {
  103. err = entry_err
  104. if entry_err == General_Error.Not_Exist {
  105. return
  106. } else {
  107. break snapshot_process
  108. }
  109. }
  110. if .PPid in selection {
  111. info.fields += {.PPid}
  112. info.ppid = int(entry.th32ParentProcessID)
  113. }
  114. if .Priority in selection {
  115. info.fields += {.Priority}
  116. info.priority = int(entry.pcPriClassBase)
  117. }
  118. }
  119. snapshot_modules: if .Executable_Path in selection {
  120. exe_path: string
  121. exe_path, err = _process_exe_by_pid(pid, allocator)
  122. if _, ok := err.(runtime.Allocator_Error); ok {
  123. return
  124. } else if err != nil {
  125. break snapshot_modules
  126. }
  127. info.executable_path = exe_path
  128. info.fields += {.Executable_Path}
  129. }
  130. read_peb: if selection >= {.Command_Line, .Environment, .Working_Dir} {
  131. process_info_size: u32
  132. process_info: win32.PROCESS_BASIC_INFORMATION
  133. status := win32.NtQueryInformationProcess(ph, .ProcessBasicInformation, &process_info, size_of(process_info), &process_info_size)
  134. if status != 0 {
  135. // TODO(flysand): There's probably a mismatch between NTSTATUS and
  136. // windows userland error codes, I haven't checked.
  137. err = Platform_Error(status)
  138. break read_peb
  139. }
  140. assert(process_info.PebBaseAddress != nil)
  141. process_peb: win32.PEB
  142. _, err = read_memory_as_struct(ph, process_info.PebBaseAddress, &process_peb)
  143. if err != nil {
  144. break read_peb
  145. }
  146. process_params: win32.RTL_USER_PROCESS_PARAMETERS
  147. _, err = read_memory_as_struct(ph, process_peb.ProcessParameters, &process_params)
  148. if err != nil {
  149. break read_peb
  150. }
  151. if selection >= {.Command_Line, .Command_Args} {
  152. TEMP_ALLOCATOR_GUARD()
  153. cmdline_w := make([]u16, process_params.CommandLine.Length, temp_allocator()) or_return
  154. _, err = read_memory_as_slice(ph, process_params.CommandLine.Buffer, cmdline_w)
  155. if err != nil {
  156. break read_peb
  157. }
  158. if .Command_Line in selection {
  159. info.command_line = win32_utf16_to_utf8(cmdline_w, allocator) or_return
  160. info.fields += {.Command_Line}
  161. }
  162. if .Command_Args in selection {
  163. info.command_args = _parse_command_line(raw_data(cmdline_w), allocator) or_return
  164. info.fields += {.Command_Args}
  165. }
  166. }
  167. if .Environment in selection {
  168. TEMP_ALLOCATOR_GUARD()
  169. env_len := process_params.EnvironmentSize / 2
  170. envs_w := make([]u16, env_len, temp_allocator()) or_return
  171. _, err = read_memory_as_slice(ph, process_params.Environment, envs_w)
  172. if err != nil {
  173. break read_peb
  174. }
  175. info.environment = _parse_environment_block(raw_data(envs_w), allocator) or_return
  176. info.fields += {.Environment}
  177. }
  178. if .Working_Dir in selection {
  179. TEMP_ALLOCATOR_GUARD()
  180. cwd_w := make([]u16, process_params.CurrentDirectoryPath.Length, temp_allocator()) or_return
  181. _, err = read_memory_as_slice(ph, process_params.CurrentDirectoryPath.Buffer, cwd_w)
  182. if err != nil {
  183. break read_peb
  184. }
  185. info.working_dir = win32_utf16_to_utf8(cwd_w, allocator) or_return
  186. info.fields += {.Working_Dir}
  187. }
  188. }
  189. read_username: if .Username in selection {
  190. username: string
  191. username, err = _get_process_user(ph, allocator)
  192. if _, ok := err.(runtime.Allocator_Error); ok {
  193. return
  194. } else if err != nil {
  195. break read_username
  196. }
  197. info.username = username
  198. info.fields += {.Username}
  199. }
  200. err = nil
  201. return
  202. }
  203. @(private="package")
  204. _process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
  205. pid := process.pid
  206. info.pid = pid
  207. // Data obtained from process snapshots
  208. snapshot_process: if selection >= {.PPid, .Priority} {
  209. entry, entry_err := _process_entry_by_pid(info.pid)
  210. if entry_err != nil {
  211. err = entry_err
  212. if entry_err == General_Error.Not_Exist {
  213. return
  214. } else {
  215. break snapshot_process
  216. }
  217. }
  218. if .PPid in selection {
  219. info.fields += {.PPid}
  220. info.ppid = int(entry.th32ParentProcessID)
  221. }
  222. if .Priority in selection {
  223. info.fields += {.Priority}
  224. info.priority = int(entry.pcPriClassBase)
  225. }
  226. }
  227. snapshot_module: if .Executable_Path in selection {
  228. exe_path: string
  229. exe_path, err = _process_exe_by_pid(pid, allocator)
  230. if _, ok := err.(runtime.Allocator_Error); ok {
  231. return
  232. } else if err != nil {
  233. break snapshot_module
  234. }
  235. info.executable_path = exe_path
  236. info.fields += {.Executable_Path}
  237. }
  238. ph := win32.HANDLE(process.handle)
  239. read_peb: if selection >= {.Command_Line, .Environment, .Working_Dir} {
  240. process_info_size: u32
  241. process_info: win32.PROCESS_BASIC_INFORMATION
  242. status := win32.NtQueryInformationProcess(ph, .ProcessBasicInformation, &process_info, size_of(process_info), &process_info_size)
  243. if status != 0 {
  244. // TODO(flysand): There's probably a mismatch between NTSTATUS and
  245. // windows userland error codes, I haven't checked.
  246. err = Platform_Error(status)
  247. return
  248. }
  249. assert(process_info.PebBaseAddress != nil)
  250. process_peb: win32.PEB
  251. _, err = read_memory_as_struct(ph, process_info.PebBaseAddress, &process_peb)
  252. if err != nil {
  253. break read_peb
  254. }
  255. process_params: win32.RTL_USER_PROCESS_PARAMETERS
  256. _, err = read_memory_as_struct(ph, process_peb.ProcessParameters, &process_params)
  257. if err != nil {
  258. break read_peb
  259. }
  260. if selection >= {.Command_Line, .Command_Args} {
  261. TEMP_ALLOCATOR_GUARD()
  262. cmdline_w := make([]u16, process_params.CommandLine.Length, temp_allocator()) or_return
  263. _, err = read_memory_as_slice(ph, process_params.CommandLine.Buffer, cmdline_w)
  264. if err != nil {
  265. break read_peb
  266. }
  267. if .Command_Line in selection {
  268. info.command_line = win32_utf16_to_utf8(cmdline_w, allocator) or_return
  269. info.fields += {.Command_Line}
  270. }
  271. if .Command_Args in selection {
  272. info.command_args = _parse_command_line(raw_data(cmdline_w), allocator) or_return
  273. info.fields += {.Command_Args}
  274. }
  275. }
  276. if .Environment in selection {
  277. TEMP_ALLOCATOR_GUARD()
  278. env_len := process_params.EnvironmentSize / 2
  279. envs_w := make([]u16, env_len, temp_allocator()) or_return
  280. _, err = read_memory_as_slice(ph, process_params.Environment, envs_w)
  281. if err != nil {
  282. break read_peb
  283. }
  284. info.environment = _parse_environment_block(raw_data(envs_w), allocator) or_return
  285. info.fields += {.Environment}
  286. }
  287. if .Working_Dir in selection {
  288. TEMP_ALLOCATOR_GUARD()
  289. cwd_w := make([]u16, process_params.CurrentDirectoryPath.Length, temp_allocator()) or_return
  290. _, err = read_memory_as_slice(ph, process_params.CurrentDirectoryPath.Buffer, cwd_w)
  291. if err != nil {
  292. break read_peb
  293. }
  294. info.working_dir = win32_utf16_to_utf8(cwd_w, allocator) or_return
  295. info.fields += {.Working_Dir}
  296. }
  297. }
  298. read_username: if .Username in selection {
  299. username: string
  300. username, err = _get_process_user(ph, allocator)
  301. if _, ok := err.(runtime.Allocator_Error); ok {
  302. return
  303. } else if err != nil {
  304. break read_username
  305. }
  306. info.username = username
  307. info.fields += {.Username}
  308. }
  309. err = nil
  310. return
  311. }
  312. @(private="package")
  313. _current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
  314. info.pid = get_pid()
  315. snapshot_process: if selection >= {.PPid, .Priority} {
  316. entry, entry_err := _process_entry_by_pid(info.pid)
  317. if entry_err != nil {
  318. err = entry_err
  319. if entry_err == General_Error.Not_Exist {
  320. return
  321. } else {
  322. break snapshot_process
  323. }
  324. }
  325. if .PPid in selection {
  326. info.fields += {.PPid}
  327. info.ppid = int(entry.th32ProcessID)
  328. }
  329. if .Priority in selection {
  330. info.fields += {.Priority}
  331. info.priority = int(entry.pcPriClassBase)
  332. }
  333. }
  334. module_filename: if .Executable_Path in selection {
  335. exe_filename_w: [256]u16
  336. path_len := win32.GetModuleFileNameW(nil, raw_data(exe_filename_w[:]), len(exe_filename_w))
  337. assert(path_len > 0)
  338. info.executable_path = win32_utf16_to_utf8(exe_filename_w[:path_len], allocator) or_return
  339. info.fields += {.Executable_Path}
  340. }
  341. command_line: if selection >= {.Command_Line, .Command_Args} {
  342. command_line_w := win32.GetCommandLineW()
  343. assert(command_line_w != nil)
  344. if .Command_Line in selection {
  345. info.command_line = win32_wstring_to_utf8(command_line_w, allocator) or_return
  346. info.fields += {.Command_Line}
  347. }
  348. if .Command_Args in selection {
  349. info.command_args = _parse_command_line(command_line_w, allocator) or_return
  350. info.fields += {.Command_Args}
  351. }
  352. }
  353. read_environment: if .Environment in selection {
  354. env_block := win32.GetEnvironmentStringsW()
  355. assert(env_block != nil)
  356. info.environment = _parse_environment_block(env_block, allocator) or_return
  357. info.fields += {.Environment}
  358. }
  359. read_username: if .Username in selection {
  360. process_handle := win32.GetCurrentProcess()
  361. username: string
  362. username, err = _get_process_user(process_handle, allocator)
  363. if _, ok := err.(runtime.Allocator_Error); ok {
  364. return
  365. } else if err != nil {
  366. break read_username
  367. }
  368. info.username = username
  369. info.fields += {.Username}
  370. }
  371. if .Working_Dir in selection {
  372. // TODO(flysand): Implement this by reading PEB
  373. err = .Mode_Not_Implemented
  374. return
  375. }
  376. err = nil
  377. return
  378. }
  379. @(private="package")
  380. _process_open :: proc(pid: int, flags: Process_Open_Flags) -> (process: Process, err: Error) {
  381. // Note(flysand): The handle will be used for querying information so we
  382. // take the necessary permissions right away.
  383. dwDesiredAccess := win32.PROCESS_QUERY_LIMITED_INFORMATION | win32.SYNCHRONIZE
  384. if .Mem_Read in flags {
  385. dwDesiredAccess |= win32.PROCESS_VM_READ
  386. }
  387. if .Mem_Write in flags {
  388. dwDesiredAccess |= win32.PROCESS_VM_WRITE
  389. }
  390. handle := win32.OpenProcess(
  391. dwDesiredAccess,
  392. false,
  393. u32(pid),
  394. )
  395. if handle == win32.INVALID_HANDLE_VALUE {
  396. err = _get_platform_error()
  397. } else {
  398. process = {pid = pid, handle = uintptr(handle)}
  399. }
  400. return
  401. }
  402. @(private="package")
  403. _Sys_Process_Attributes :: struct {}
  404. @(private="package")
  405. _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
  406. TEMP_ALLOCATOR_GUARD()
  407. command_line := _build_command_line(desc.command, temp_allocator())
  408. command_line_w := win32_utf8_to_wstring(command_line, temp_allocator()) or_return
  409. environment := desc.env
  410. if desc.env == nil {
  411. environment = environ(temp_allocator())
  412. }
  413. environment_block := _build_environment_block(environment, temp_allocator())
  414. environment_block_w := win32_utf8_to_utf16(environment_block, temp_allocator()) or_return
  415. stderr_handle := win32.GetStdHandle(win32.STD_ERROR_HANDLE)
  416. stdout_handle := win32.GetStdHandle(win32.STD_OUTPUT_HANDLE)
  417. stdin_handle := win32.GetStdHandle(win32.STD_INPUT_HANDLE)
  418. if desc.stdout != nil {
  419. stdout_handle = win32.HANDLE((^File_Impl)(desc.stdout.impl).fd)
  420. }
  421. if desc.stderr != nil {
  422. stderr_handle = win32.HANDLE((^File_Impl)(desc.stderr.impl).fd)
  423. }
  424. if desc.stdin != nil {
  425. stdin_handle = win32.HANDLE((^File_Impl)(desc.stderr.impl).fd)
  426. }
  427. working_dir_w := (win32_utf8_to_wstring(desc.working_dir, temp_allocator()) or_else nil) if len(desc.working_dir) > 0 else nil
  428. process_info: win32.PROCESS_INFORMATION
  429. ok := win32.CreateProcessW(
  430. nil,
  431. command_line_w,
  432. nil,
  433. nil,
  434. true,
  435. win32.CREATE_UNICODE_ENVIRONMENT|win32.NORMAL_PRIORITY_CLASS,
  436. raw_data(environment_block_w),
  437. working_dir_w,
  438. &win32.STARTUPINFOW{
  439. cb = size_of(win32.STARTUPINFOW),
  440. hStdError = stderr_handle,
  441. hStdOutput = stdout_handle,
  442. hStdInput = stdin_handle,
  443. dwFlags = win32.STARTF_USESTDHANDLES,
  444. },
  445. &process_info,
  446. )
  447. if !ok {
  448. err = _get_platform_error()
  449. return
  450. }
  451. process = {pid = int(process_info.dwProcessId), handle = uintptr(process_info.hProcess)}
  452. return
  453. }
  454. @(private="package")
  455. _process_wait :: proc(process: Process, timeout: time.Duration) -> (process_state: Process_State, err: Error) {
  456. handle := win32.HANDLE(process.handle)
  457. timeout_ms := u32(timeout / time.Millisecond) if timeout >= 0 else win32.INFINITE
  458. switch win32.WaitForSingleObject(handle, timeout_ms) {
  459. case win32.WAIT_OBJECT_0:
  460. exit_code: u32
  461. if !win32.GetExitCodeProcess(handle, &exit_code) {
  462. err =_get_platform_error()
  463. return
  464. }
  465. time_created: win32.FILETIME
  466. time_exited: win32.FILETIME
  467. time_kernel: win32.FILETIME
  468. time_user: win32.FILETIME
  469. if !win32.GetProcessTimes(handle, &time_created, &time_exited, &time_kernel, &time_user) {
  470. err = _get_platform_error()
  471. return
  472. }
  473. process_state = {
  474. exit_code = int(exit_code),
  475. exited = true,
  476. pid = process.pid,
  477. success = true,
  478. system_time = _filetime_to_duration(time_kernel),
  479. user_time = _filetime_to_duration(time_user),
  480. }
  481. return
  482. case win32.WAIT_TIMEOUT:
  483. err = General_Error.Timeout
  484. return
  485. case:
  486. err = _get_platform_error()
  487. return
  488. }
  489. }
  490. @(private="package")
  491. _process_close :: proc(process: Process) -> Error {
  492. if !win32.CloseHandle(win32.HANDLE(process.handle)) {
  493. return _get_platform_error()
  494. }
  495. return nil
  496. }
  497. @(private="package")
  498. _process_kill :: proc(process: Process) -> Error {
  499. // Note(flysand): This is different than what the task manager's "kill process"
  500. // functionality does, as we don't try to send WM_CLOSE message first. This
  501. // is quite a rough way to kill the process, which should be consistent with
  502. // linux. The error code 9 is to mimic SIGKILL event.
  503. if !win32.TerminateProcess(win32.HANDLE(process.handle), 9) {
  504. return _get_platform_error()
  505. }
  506. return nil
  507. }
  508. _filetime_to_duration :: proc(filetime: win32.FILETIME) -> time.Duration {
  509. ticks := u64(filetime.dwHighDateTime)<<32 | u64(filetime.dwLowDateTime)
  510. return time.Duration(ticks * 100)
  511. }
  512. _process_entry_by_pid :: proc(pid: int) -> (entry: win32.PROCESSENTRY32W, err: Error) {
  513. snap := win32.CreateToolhelp32Snapshot(win32.TH32CS_SNAPPROCESS, 0)
  514. if snap == win32.INVALID_HANDLE_VALUE {
  515. err = _get_platform_error()
  516. return
  517. }
  518. defer win32.CloseHandle(snap)
  519. entry = win32.PROCESSENTRY32W{dwSize = size_of(win32.PROCESSENTRY32W)}
  520. status := win32.Process32FirstW(snap, &entry)
  521. for status {
  522. if u32(pid) == entry.th32ProcessID {
  523. return
  524. }
  525. status = win32.Process32NextW(snap, &entry)
  526. }
  527. err = General_Error.Not_Exist
  528. return
  529. }
  530. // Note(flysand): Not sure which way it's better to get the executable path:
  531. // via toolhelp snapshots or by reading other process' PEB memory. I have
  532. // a slight suspicion that if both exe path and command line are desired,
  533. // it's faster to just read both from PEB, but maybe the toolhelp snapshots
  534. // are just better...?
  535. @(private="package")
  536. _process_exe_by_pid :: proc(pid: int, allocator: runtime.Allocator) -> (exe_path: string, err: Error) {
  537. snap := win32.CreateToolhelp32Snapshot(
  538. win32.TH32CS_SNAPMODULE|win32.TH32CS_SNAPMODULE32,
  539. u32(pid),
  540. )
  541. if snap == win32.INVALID_HANDLE_VALUE {
  542. err =_get_platform_error()
  543. return
  544. }
  545. defer win32.CloseHandle(snap)
  546. entry := win32.MODULEENTRY32W { dwSize = size_of(win32.MODULEENTRY32W) }
  547. status := win32.Module32FirstW(snap, &entry)
  548. if !status {
  549. err =_get_platform_error()
  550. return
  551. }
  552. return win32_wstring_to_utf8(raw_data(entry.szExePath[:]), allocator)
  553. }
  554. _get_process_user :: proc(process_handle: win32.HANDLE, allocator: runtime.Allocator) -> (full_username: string, err: Error) {
  555. TEMP_ALLOCATOR_GUARD()
  556. token_handle: win32.HANDLE
  557. if !win32.OpenProcessToken(process_handle, win32.TOKEN_QUERY, &token_handle) {
  558. err = _get_platform_error()
  559. return
  560. }
  561. token_user_size: u32
  562. if !win32.GetTokenInformation(token_handle, .TokenUser, nil, 0, &token_user_size) {
  563. // Note(flysand): Make sure the buffer too small error comes out, and not any other error
  564. err = _get_platform_error()
  565. if v, ok := is_platform_error(err); !ok || v != i32(win32.ERROR_INSUFFICIENT_BUFFER) {
  566. return
  567. }
  568. err = nil
  569. }
  570. token_user := (^win32.TOKEN_USER)(raw_data(make([]u8, token_user_size, temp_allocator()) or_return))
  571. if !win32.GetTokenInformation(token_handle, .TokenUser, token_user, token_user_size, &token_user_size) {
  572. err = _get_platform_error()
  573. return
  574. }
  575. sid_type: win32.SID_NAME_USE
  576. username_w: [256]u16
  577. domain_w: [256]u16
  578. username_chrs := u32(256)
  579. domain_chrs := u32(256)
  580. if !win32.LookupAccountSidW(nil, token_user.User.Sid, &username_w[0], &username_chrs, &domain_w[0], &domain_chrs, &sid_type) {
  581. err = _get_platform_error()
  582. return
  583. }
  584. username := win32_utf16_to_utf8(username_w[:username_chrs], temp_allocator()) or_return
  585. domain := win32_utf16_to_utf8(domain_w[:domain_chrs], temp_allocator()) or_return
  586. return strings.concatenate({domain, "\\", username}, allocator)
  587. }
  588. _parse_command_line :: proc(cmd_line_w: [^]u16, allocator: runtime.Allocator) -> (argv: []string, err: Error) {
  589. argc: i32
  590. argv_w := win32.CommandLineToArgvW(cmd_line_w, &argc)
  591. if argv_w == nil {
  592. return nil, _get_platform_error()
  593. }
  594. argv = make([]string, argc, allocator) or_return
  595. defer if err != nil {
  596. for arg in argv {
  597. delete(arg, allocator)
  598. }
  599. delete(argv, allocator)
  600. }
  601. for arg_w, i in argv_w[:argc] {
  602. argv[i] = win32_wstring_to_utf8(arg_w, allocator) or_return
  603. }
  604. return
  605. }
  606. _build_command_line :: proc(command: []string, allocator: runtime.Allocator) -> string {
  607. _write_byte_n_times :: #force_inline proc(builder: ^strings.Builder, b: byte, n: int) {
  608. for _ in 0 ..< n {
  609. strings.write_byte(builder, b)
  610. }
  611. }
  612. builder := strings.builder_make(allocator)
  613. for arg, i in command {
  614. if i != 0 {
  615. strings.write_byte(&builder, ' ')
  616. }
  617. j := 0
  618. strings.write_byte(&builder, '"')
  619. for j < len(arg) {
  620. backslashes := 0
  621. for j < len(arg) && arg[j] == '\\' {
  622. backslashes += 1
  623. j += 1
  624. }
  625. if j == len(arg) {
  626. _write_byte_n_times(&builder, '\\', 2*backslashes)
  627. break
  628. } else if arg[j] == '"' {
  629. _write_byte_n_times(&builder, '\\', 2*backslashes+1)
  630. strings.write_byte(&builder, '"')
  631. } else {
  632. _write_byte_n_times(&builder, '\\', backslashes)
  633. strings.write_byte(&builder, arg[j])
  634. }
  635. j += 1
  636. }
  637. strings.write_byte(&builder, '"')
  638. }
  639. return strings.to_string(builder)
  640. }
  641. _parse_environment_block :: proc(block: [^]u16, allocator: runtime.Allocator) -> (envs: []string, err: Error) {
  642. zt_count := 0
  643. for idx := 0; true; {
  644. if block[idx] == 0x0000 {
  645. zt_count += 1
  646. if block[idx+1] == 0x0000 {
  647. zt_count += 1
  648. break
  649. }
  650. }
  651. idx += 1
  652. }
  653. // Note(flysand): Each string in the environment block is terminated
  654. // by a NUL character. In addition, the environment block itself is
  655. // terminated by a NUL character. So the number of strings in the
  656. // environment block is the number of NUL character minus the
  657. // block terminator.
  658. env_count := zt_count - 1
  659. envs = make([]string, env_count, allocator) or_return
  660. defer if err != nil {
  661. for env in envs {
  662. delete(env, allocator)
  663. }
  664. delete(envs, allocator)
  665. }
  666. env_idx := 0
  667. last_idx := 0
  668. idx := 0
  669. for block[idx] != 0x0000 {
  670. for block[idx] != 0x0000 {
  671. idx += 1
  672. }
  673. env_w := block[last_idx:idx]
  674. envs[env_idx] = win32_utf16_to_utf8(env_w, allocator) or_return
  675. env_idx += 1
  676. idx += 1
  677. last_idx = idx
  678. }
  679. return
  680. }
  681. _build_environment_block :: proc(environment: []string, allocator: runtime.Allocator) -> string {
  682. builder := strings.builder_make(allocator)
  683. loop: #reverse for kv, cur_idx in environment {
  684. eq_idx := strings.index_byte(kv, '=')
  685. assert(eq_idx >= 0, "Malformed environment string. Expected '=' to separate keys and values")
  686. key := kv[:eq_idx]
  687. for old_kv in environment[cur_idx+1:] {
  688. old_key := old_kv[:strings.index_byte(old_kv, '=')]
  689. if key == old_key {
  690. continue loop
  691. }
  692. }
  693. strings.write_string(&builder, kv)
  694. strings.write_byte(&builder, 0)
  695. }
  696. // Note(flysand): In addition to the NUL-terminator for each string, the
  697. // environment block itself is NUL-terminated.
  698. strings.write_byte(&builder, 0)
  699. return strings.to_string(builder)
  700. }