mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-05 23:28:48 +00:00
[os2/process]: Refactor process_info procs, add process_info_by_handle
This commit is contained in:
+39
-10
@@ -168,17 +168,37 @@ Process_Info :: struct {
|
|||||||
This procedure obtains an information, specified by `selection` parameter of
|
This procedure obtains an information, specified by `selection` parameter of
|
||||||
a process given by `pid`.
|
a process given by `pid`.
|
||||||
|
|
||||||
Use `free_process_info` to free the memory allocated by this function. In
|
Use `free_process_info` to free the memory allocated by this procedure. In
|
||||||
case the function returns an error all temporary allocations would be freed
|
case the function returns an error all temporary allocations would be freed
|
||||||
and as such, calling `free_process_info()` is not needed.
|
and as such, calling `free_process_info()` is not needed.
|
||||||
|
|
||||||
**Note**: The resulting information may or may not contain the
|
**Note**: The resulting information may or may contain the fields specified
|
||||||
selected fields. Please check the `fields` field of the `Process_Info`
|
by the `selection` parameter. Always check whether the returned
|
||||||
struct to see if the struct contains the desired fields **before** checking
|
`Process_Info` struct has the required fields before checking the error code
|
||||||
the error code returned by this function.
|
returned by this function.
|
||||||
*/
|
*/
|
||||||
process_info :: proc(pid: int, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) {
|
process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) {
|
||||||
return _process_info(pid, selection, allocator)
|
return _process_info_by_pid(pid, selection, allocator)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Obtain information about a process.
|
||||||
|
|
||||||
|
This procedure obtains information, specified by `selection` parameter
|
||||||
|
about a process that has been opened by the application, specified in
|
||||||
|
the `process` parameter.
|
||||||
|
|
||||||
|
Use `free_process_info` to free the memory allocated by this procedure. In
|
||||||
|
case the function returns an error, all temporary allocations would be freed
|
||||||
|
and as such, calling `free_process_info` is not needed.
|
||||||
|
|
||||||
|
**Note**: The resulting information may or may contain the fields specified
|
||||||
|
by the `selection` parameter. Always check whether the returned
|
||||||
|
`Process_Info` struct has the required fields before checking the error code
|
||||||
|
returned by this function.
|
||||||
|
*/
|
||||||
|
process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) {
|
||||||
|
return _process_info_by_handle(process, selection, allocator)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -191,15 +211,24 @@ process_info :: proc(pid: int, selection: Process_Info_Fields, allocator: runtim
|
|||||||
case this function returns an error, all temporary allocations would be
|
case this function returns an error, all temporary allocations would be
|
||||||
freed and as such calling `free_process_info()` is not needed.
|
freed and as such calling `free_process_info()` is not needed.
|
||||||
|
|
||||||
**Note**: The resulting `Process_Info` may or may not contain the selected
|
**Note**: The resulting information may or may contain the fields specified
|
||||||
fields. Check the `fields` field of the `Process_Info` struct to see, if the
|
by the `selection` parameter. Always check whether the returned
|
||||||
struct contains the selected fields **before** checking the error code
|
`Process_Info` struct has the required fields before checking the error code
|
||||||
returned by this function.
|
returned by this function.
|
||||||
*/
|
*/
|
||||||
current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) {
|
current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) {
|
||||||
return _current_process_info(selection, allocator)
|
return _current_process_info(selection, allocator)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Obtain information about the specified process.
|
||||||
|
*/
|
||||||
|
process_info :: proc {
|
||||||
|
process_info_by_pid,
|
||||||
|
process_info_by_handle,
|
||||||
|
current_process_info,
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Free the information about the process.
|
Free the information about the process.
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ _process_list :: proc(allocator: runtime.Allocator) -> ([]int, Error) {
|
|||||||
return pid_list[:], nil
|
return pid_list[:], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
_process_info :: proc(pid: int, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
|
_process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
|
||||||
info.pid = pid
|
info.pid = pid
|
||||||
defer if err != nil {
|
defer if err != nil {
|
||||||
free_process_info(info, allocator)
|
free_process_info(info, allocator)
|
||||||
@@ -83,22 +83,8 @@ _process_info :: proc(pid: int, selection: Process_Info_Fields, allocator: runti
|
|||||||
need_process_handle := need_peb || .Username in selection
|
need_process_handle := need_peb || .Username in selection
|
||||||
// Data obtained from process snapshots
|
// Data obtained from process snapshots
|
||||||
if need_snapprocess {
|
if need_snapprocess {
|
||||||
snap := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, 0)
|
entry, entry_err := _process_entry_by_pid(info.pid)
|
||||||
if snap == windows.INVALID_HANDLE_VALUE {
|
if entry_err != nil {
|
||||||
return info, _get_platform_error()
|
|
||||||
}
|
|
||||||
defer windows.CloseHandle(snap)
|
|
||||||
entry := windows.PROCESSENTRY32W { dwSize = size_of(windows.PROCESSENTRY32W) }
|
|
||||||
status := windows.Process32FirstW(snap, &entry)
|
|
||||||
found := false
|
|
||||||
for status {
|
|
||||||
if u32(pid) == entry.th32ProcessID {
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
status = windows.Process32NextW(snap, &entry)
|
|
||||||
}
|
|
||||||
if !found {
|
|
||||||
err = General_Error.Not_Exist
|
err = General_Error.Not_Exist
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -111,30 +97,10 @@ _process_info :: proc(pid: int, selection: Process_Info_Fields, allocator: runti
|
|||||||
info.priority = int(entry.pcPriClassBase)
|
info.priority = int(entry.pcPriClassBase)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Note(flysand): Not sure which way it's better to get the executable path:
|
|
||||||
// via toolhelp snapshots or by reading other process' PEB memory. I have
|
|
||||||
// a slight suspicion that if both exe path and command line are desired,
|
|
||||||
// it's faster to just read both from PEB, but maybe the toolhelp snapshots
|
|
||||||
// are just better...?
|
|
||||||
if need_snapmodule {
|
if need_snapmodule {
|
||||||
snap := windows.CreateToolhelp32Snapshot(
|
exe_path, exe_path_err := _process_exe_by_pid(pid, allocator)
|
||||||
windows.TH32CS_SNAPMODULE|windows.TH32CS_SNAPMODULE32,
|
if exe_path_err != nil {
|
||||||
u32(pid),
|
err = exe_path_err
|
||||||
)
|
|
||||||
if snap == windows.INVALID_HANDLE_VALUE {
|
|
||||||
err = _get_platform_error()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer windows.CloseHandle(snap)
|
|
||||||
entry := windows.MODULEENTRY32W { dwSize = size_of(windows.MODULEENTRY32W) }
|
|
||||||
status := windows.Module32FirstW(snap, &entry)
|
|
||||||
if !status {
|
|
||||||
err = _get_platform_error()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
exe_path: string
|
|
||||||
exe_path, err = windows.wstring_to_utf8(raw_data(entry.szExePath[:]), -1, allocator)
|
|
||||||
if err != nil {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
info.fields |= {.Executable_Path}
|
info.fields |= {.Executable_Path}
|
||||||
@@ -269,6 +235,161 @@ _process_info :: proc(pid: int, selection: Process_Info_Fields, allocator: runti
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
|
||||||
|
pid := process.pid
|
||||||
|
info.pid = pid
|
||||||
|
defer if err != nil {
|
||||||
|
free_process_info(info, allocator)
|
||||||
|
}
|
||||||
|
need_snapprocess := \
|
||||||
|
.PPid in selection ||
|
||||||
|
.Priority in selection
|
||||||
|
need_snapmodule := \
|
||||||
|
.Executable_Path in selection
|
||||||
|
need_peb := \
|
||||||
|
.Command_Line in selection ||
|
||||||
|
.Environment in selection ||
|
||||||
|
.CWD in selection
|
||||||
|
// Data obtained from process snapshots
|
||||||
|
if need_snapprocess {
|
||||||
|
entry, entry_err := _process_entry_by_pid(info.pid)
|
||||||
|
if entry_err != nil {
|
||||||
|
err = General_Error.Not_Exist
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if .PPid in selection {
|
||||||
|
info.fields |= {.PPid}
|
||||||
|
info.ppid = int(entry.th32ParentProcessID)
|
||||||
|
}
|
||||||
|
if .Priority in selection {
|
||||||
|
info.fields |= {.Priority}
|
||||||
|
info.priority = int(entry.pcPriClassBase)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if need_snapmodule {
|
||||||
|
exe_path, exe_path_err := _process_exe_by_pid(pid, allocator)
|
||||||
|
if exe_path_err != nil {
|
||||||
|
err = exe_path_err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
info.fields |= {.Executable_Path}
|
||||||
|
info.executable_path = exe_path
|
||||||
|
}
|
||||||
|
ph := cast(windows.HANDLE) process.handle
|
||||||
|
if need_peb {
|
||||||
|
ntdll_lib := windows.LoadLibraryW(windows.L("ntdll.dll"))
|
||||||
|
if ntdll_lib == nil {
|
||||||
|
err = _get_platform_error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer windows.FreeLibrary(ntdll_lib)
|
||||||
|
NtQueryInformationProcess := cast(NtQueryInformationProcess_T) windows.GetProcAddress(ntdll_lib, "NtQueryInformationProcess")
|
||||||
|
if NtQueryInformationProcess == nil {
|
||||||
|
err = _get_platform_error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
process_info_size: u32 = ---
|
||||||
|
process_info: PROCESS_BASIC_INFORMATION = ---
|
||||||
|
status := NtQueryInformationProcess(ph, .ProcessBasicInformation, &process_info, size_of(process_info), &process_info_size)
|
||||||
|
if status != 0 {
|
||||||
|
// TODO(flysand): There's probably a mismatch between NTSTATUS and
|
||||||
|
// windows userland error codes, I haven't checked.
|
||||||
|
err = Platform_Error(status)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if process_info.PebBaseAddress == nil {
|
||||||
|
// Not sure what the error is
|
||||||
|
err = General_Error.Unsupported
|
||||||
|
return
|
||||||
|
}
|
||||||
|
process_peb: PEB = ---
|
||||||
|
bytes_read: uint = ---
|
||||||
|
read_struct :: proc(h: windows.HANDLE, addr: rawptr, dest: ^$T, br: ^uint) -> windows.BOOL {
|
||||||
|
return windows.ReadProcessMemory(h, addr, dest, size_of(T), br)
|
||||||
|
}
|
||||||
|
read_slice :: proc(h: windows.HANDLE, addr: rawptr, dest: []$T, br: ^uint) -> windows.BOOL {
|
||||||
|
return windows.ReadProcessMemory(h, addr, raw_data(dest), len(dest)*size_of(T), br)
|
||||||
|
}
|
||||||
|
if !read_struct(ph, process_info.PebBaseAddress, &process_peb, &bytes_read) {
|
||||||
|
err = _get_platform_error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
process_params: RTL_USER_PROCESS_PARAMETERS = ---
|
||||||
|
if !read_struct(ph, process_peb.ProcessParameters, &process_params, &bytes_read) {
|
||||||
|
err = _get_platform_error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if .Command_Line in selection || .Command_Args in selection {
|
||||||
|
TEMP_ALLOCATOR_GUARD()
|
||||||
|
cmdline_w := make([]u16, process_params.CommandLine.Length, temp_allocator())
|
||||||
|
if !read_slice(ph, process_params.CommandLine.Buffer, cmdline_w, &bytes_read) {
|
||||||
|
err = _get_platform_error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if .Command_Line in selection {
|
||||||
|
cmdline, cmdline_err := windows.utf16_to_utf8(cmdline_w, allocator)
|
||||||
|
if cmdline_err != nil {
|
||||||
|
err = cmdline_err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
info.fields |= {.Command_Line}
|
||||||
|
info.command_line = cmdline
|
||||||
|
}
|
||||||
|
if .Command_Args in selection {
|
||||||
|
args, args_err := _parse_command_line(raw_data(cmdline_w), allocator)
|
||||||
|
if args_err != nil {
|
||||||
|
err = args_err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
info.fields += {.Command_Args}
|
||||||
|
info.command_args = args
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if .Environment in selection {
|
||||||
|
TEMP_ALLOCATOR_GUARD()
|
||||||
|
env_len := process_params.EnvironmentSize / 2
|
||||||
|
envs_w := make([]u16, env_len, temp_allocator())
|
||||||
|
if !read_slice(ph, process_params.Environment, envs_w, &bytes_read) {
|
||||||
|
err = _get_platform_error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
envs, envs_err := _parse_environment_block(raw_data(envs_w), allocator)
|
||||||
|
if envs_err != nil {
|
||||||
|
err = envs_err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
info.fields |= {.Environment}
|
||||||
|
info.environment = envs
|
||||||
|
}
|
||||||
|
if .CWD in selection {
|
||||||
|
TEMP_ALLOCATOR_GUARD()
|
||||||
|
cwd_w := make([]u16, process_params.CurrentDirectoryPath.Length, temp_allocator())
|
||||||
|
if !read_slice(ph, process_params.CurrentDirectoryPath.Buffer, cwd_w, &bytes_read) {
|
||||||
|
err = _get_platform_error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cwd, cwd_err := windows.utf16_to_utf8(cwd_w, allocator)
|
||||||
|
if cwd_err != nil {
|
||||||
|
err = cwd_err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
info.fields |= {.CWD}
|
||||||
|
info.cwd = cwd
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if .Username in selection {
|
||||||
|
username, username_err := _get_process_user(ph, allocator)
|
||||||
|
if username_err != nil {
|
||||||
|
err = username_err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
info.fields |= {.Username}
|
||||||
|
info.username = username
|
||||||
|
}
|
||||||
|
err = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
_current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
|
_current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
|
||||||
info.pid = cast(int) windows.GetCurrentProcessId()
|
info.pid = cast(int) windows.GetCurrentProcessId()
|
||||||
defer if err != nil {
|
defer if err != nil {
|
||||||
@@ -276,20 +397,8 @@ _current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime
|
|||||||
}
|
}
|
||||||
need_snapprocess := .PPid in selection || .Priority in selection
|
need_snapprocess := .PPid in selection || .Priority in selection
|
||||||
if need_snapprocess {
|
if need_snapprocess {
|
||||||
snap := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, 0)
|
entry, entry_err := _process_entry_by_pid(info.pid)
|
||||||
if snap == windows.INVALID_HANDLE_VALUE {
|
if entry_err != nil {
|
||||||
return
|
|
||||||
}
|
|
||||||
defer windows.CloseHandle(snap)
|
|
||||||
entry := windows.PROCESSENTRY32W { dwSize = size_of(windows.PROCESSENTRY32W) }
|
|
||||||
status := windows.Process32FirstW(snap, &entry)
|
|
||||||
for status {
|
|
||||||
if entry.th32ProcessID == u32(info.pid) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
status = windows.Process32NextW(snap, &entry)
|
|
||||||
}
|
|
||||||
if entry.th32ProcessID != u32(info.pid) {
|
|
||||||
err = General_Error.Not_Exist
|
err = General_Error.Not_Exist
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -474,6 +583,56 @@ _filetime_to_duration :: proc(filetime: windows.FILETIME) -> time.Duration {
|
|||||||
return time.Duration(ticks * 100)
|
return time.Duration(ticks * 100)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
_process_entry_by_pid :: proc(pid: int) -> (windows.PROCESSENTRY32W, Error) {
|
||||||
|
snap := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, 0)
|
||||||
|
if snap == windows.INVALID_HANDLE_VALUE {
|
||||||
|
return {}, _get_platform_error()
|
||||||
|
}
|
||||||
|
defer windows.CloseHandle(snap)
|
||||||
|
entry := windows.PROCESSENTRY32W { dwSize = size_of(windows.PROCESSENTRY32W) }
|
||||||
|
status := windows.Process32FirstW(snap, &entry)
|
||||||
|
found := false
|
||||||
|
for status {
|
||||||
|
if u32(pid) == entry.th32ProcessID {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
status = windows.Process32NextW(snap, &entry)
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
return {}, General_Error.Not_Exist
|
||||||
|
}
|
||||||
|
return entry, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note(flysand): Not sure which way it's better to get the executable path:
|
||||||
|
// via toolhelp snapshots or by reading other process' PEB memory. I have
|
||||||
|
// a slight suspicion that if both exe path and command line are desired,
|
||||||
|
// it's faster to just read both from PEB, but maybe the toolhelp snapshots
|
||||||
|
// are just better...?
|
||||||
|
@(private)
|
||||||
|
_process_exe_by_pid :: proc(pid: int, allocator: runtime.Allocator) -> (string, Error) {
|
||||||
|
snap := windows.CreateToolhelp32Snapshot(
|
||||||
|
windows.TH32CS_SNAPMODULE|windows.TH32CS_SNAPMODULE32,
|
||||||
|
u32(pid),
|
||||||
|
)
|
||||||
|
if snap == windows.INVALID_HANDLE_VALUE {
|
||||||
|
return "", _get_platform_error()
|
||||||
|
}
|
||||||
|
defer windows.CloseHandle(snap)
|
||||||
|
entry := windows.MODULEENTRY32W { dwSize = size_of(windows.MODULEENTRY32W) }
|
||||||
|
status := windows.Module32FirstW(snap, &entry)
|
||||||
|
if !status {
|
||||||
|
return "", _get_platform_error()
|
||||||
|
}
|
||||||
|
exe_path, err := windows.wstring_to_utf8(raw_data(entry.szExePath[:]), -1, allocator)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return exe_path, nil
|
||||||
|
}
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
_get_process_user :: proc(process_handle: windows.HANDLE, allocator: runtime.Allocator) -> (full_username: string, err: Error) {
|
_get_process_user :: proc(process_handle: windows.HANDLE, allocator: runtime.Allocator) -> (full_username: string, err: Error) {
|
||||||
TEMP_ALLOCATOR_GUARD()
|
TEMP_ALLOCATOR_GUARD()
|
||||||
|
|||||||
Reference in New Issue
Block a user