Browse Source

[os2/process]: Implement missing functionality, update docs

flysand7 1 year ago
parent
commit
255f00d971
2 changed files with 40 additions and 17 deletions
  1. 9 6
      core/os/os2/process.odin
  2. 31 11
      core/os/os2/process_windows.odin

+ 9 - 6
core/os/os2/process.odin

@@ -131,7 +131,7 @@ Process_Info_Field :: enum {
 	Command_Args,
 	Command_Args,
 	Environment,
 	Environment,
 	Username,
 	Username,
-	CWD,
+	Working_Dir,
 }
 }
 
 
 /*
 /*
@@ -159,7 +159,7 @@ Process_Info :: struct {
 	// The username of the user who started the process.
 	// The username of the user who started the process.
 	username: string,
 	username: string,
 	// The current working directory of the process.
 	// The current working directory of the process.
-	cwd: string,
+	working_dir: string,
 }
 }
 
 
 /*
 /*
@@ -244,7 +244,7 @@ free_process_info :: proc(pi: Process_Info, allocator: runtime.Allocator) {
 		delete(s, allocator)
 		delete(s, allocator)
 	}
 	}
 	delete(pi.environment, allocator)
 	delete(pi.environment, allocator)
-	delete(pi.cwd, allocator)
+	delete(pi.working_dir, allocator)
 }
 }
 
 
 /*
 /*
@@ -288,8 +288,10 @@ process_open :: proc(pid: int, flags := Process_Open_Flags {}) -> (Process, Erro
 Process_Desc :: struct {
 Process_Desc :: struct {
 	// OS-specific attributes.
 	// OS-specific attributes.
 	sys_attr: _Sys_Process_Attributes,
 	sys_attr: _Sys_Process_Attributes,
-	// The working directory of the process.
-	dir: string,
+	// The working directory of the process. If the string has length 0, the
+	// working directory is assumed to be the current working directory of the
+	// current process.
+	working_dir: string,
 	// The command to run. Each element of the slice is a separate argument to
 	// The command to run. Each element of the slice is a separate argument to
 	// the process. The first element of the slice would be the executable.
 	// the process. The first element of the slice would be the executable.
 	command: []string,
 	command: []string,
@@ -329,7 +331,8 @@ Process_Desc :: struct {
 	the function returns an error, an invalid handle is returned.
 	the function returns an error, an invalid handle is returned.
 
 
 	This procedure is not thread-safe. It may alter the inheritance properties
 	This procedure is not thread-safe. It may alter the inheritance properties
-	of file handles.
+	of file handles in an unpredictable manner. In case multiple threads change
+	handle inheritance properties, make sure to serialize all those calls.
 */
 */
 process_start :: proc(desc := Process_Desc {}) -> (Process, Error) {
 process_start :: proc(desc := Process_Desc {}) -> (Process, Error) {
 	return _process_start(desc)
 	return _process_start(desc)

+ 31 - 11
core/os/os2/process_windows.odin

@@ -79,7 +79,7 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
 	need_peb := \
 	need_peb := \
 		.Command_Line in selection ||
 		.Command_Line in selection ||
 		.Environment in selection ||
 		.Environment in selection ||
-		.CWD in selection
+		.Working_Dir in selection
 	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 {
@@ -122,6 +122,8 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
 		windows.CloseHandle(ph)
 		windows.CloseHandle(ph)
 	}
 	}
 	if need_peb {
 	if need_peb {
+		// TODO(flysand): This was not tested with WOW64 or 32-bit processes,
+		// might need to be revised later when issues occur.
 		ntdll_lib := windows.LoadLibraryW(windows.L("ntdll.dll"))
 		ntdll_lib := windows.LoadLibraryW(windows.L("ntdll.dll"))
 		if ntdll_lib == nil {
 		if ntdll_lib == nil {
 			err = _get_platform_error()
 			err = _get_platform_error()
@@ -206,7 +208,7 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
 			info.fields |= {.Environment}
 			info.fields |= {.Environment}
 			info.environment = envs
 			info.environment = envs
 		}
 		}
-		if .CWD in selection {
+		if .Working_Dir in selection {
 			TEMP_ALLOCATOR_GUARD()
 			TEMP_ALLOCATOR_GUARD()
 			cwd_w := make([]u16, process_params.CurrentDirectoryPath.Length, temp_allocator())
 			cwd_w := make([]u16, process_params.CurrentDirectoryPath.Length, temp_allocator())
 			if !read_slice(ph, process_params.CurrentDirectoryPath.Buffer, cwd_w, &bytes_read) {
 			if !read_slice(ph, process_params.CurrentDirectoryPath.Buffer, cwd_w, &bytes_read) {
@@ -218,8 +220,8 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
 				err = cwd_err
 				err = cwd_err
 				return
 				return
 			}
 			}
-			info.fields |= {.CWD}
-			info.cwd = cwd
+			info.fields |= {.Working_Dir}
+			info.working_dir = cwd
 		}
 		}
 	}
 	}
 	if .Username in selection {
 	if .Username in selection {
@@ -249,7 +251,7 @@ _process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields
 	need_peb := \
 	need_peb := \
 		.Command_Line in selection ||
 		.Command_Line in selection ||
 		.Environment in selection ||
 		.Environment in selection ||
-		.CWD in selection
+		.Working_Dir in selection
 	// Data obtained from process snapshots
 	// Data obtained from process snapshots
 	if need_snapprocess {
 	if need_snapprocess {
 		entry, entry_err := _process_entry_by_pid(info.pid)
 		entry, entry_err := _process_entry_by_pid(info.pid)
@@ -361,7 +363,7 @@ _process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields
 			info.fields |= {.Environment}
 			info.fields |= {.Environment}
 			info.environment = envs
 			info.environment = envs
 		}
 		}
-		if .CWD in selection {
+		if .Working_Dir in selection {
 			TEMP_ALLOCATOR_GUARD()
 			TEMP_ALLOCATOR_GUARD()
 			cwd_w := make([]u16, process_params.CurrentDirectoryPath.Length, temp_allocator())
 			cwd_w := make([]u16, process_params.CurrentDirectoryPath.Length, temp_allocator())
 			if !read_slice(ph, process_params.CurrentDirectoryPath.Buffer, cwd_w, &bytes_read) {
 			if !read_slice(ph, process_params.CurrentDirectoryPath.Buffer, cwd_w, &bytes_read) {
@@ -373,8 +375,8 @@ _process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields
 				err = cwd_err
 				err = cwd_err
 				return
 				return
 			}
 			}
-			info.fields |= {.CWD}
-			info.cwd = cwd
+			info.fields |= {.Working_Dir}
+			info.working_dir = cwd
 		}
 		}
 	}
 	}
 	if .Username in selection {
 	if .Username in selection {
@@ -463,11 +465,18 @@ _current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime
 		info.fields += {.Username}
 		info.fields += {.Username}
 		info.username = username
 		info.username = username
 	}
 	}
+	if .Working_Dir in selection {
+		// TODO(flysand): Implement this by reading PEB
+		err = .Mode_Not_Implemented
+		return
+	}
 	err = nil
 	err = nil
 	return
 	return
 }
 }
 
 
 _process_open :: proc(pid: int, flags: Process_Open_Flags) -> (Process, Error) {
 _process_open :: proc(pid: int, flags: Process_Open_Flags) -> (Process, Error) {
+	// Note(flysand): The handle will be used for querying information so we
+	// take the necessary permissions right away.
 	dwDesiredAccess := windows.PROCESS_QUERY_LIMITED_INFORMATION | windows.SYNCHRONIZE
 	dwDesiredAccess := windows.PROCESS_QUERY_LIMITED_INFORMATION | windows.SYNCHRONIZE
 	if .Mem_Read in flags {
 	if .Mem_Read in flags {
 		dwDesiredAccess |= windows.PROCESS_VM_READ
 		dwDesiredAccess |= windows.PROCESS_VM_READ
@@ -505,10 +514,17 @@ _process_start :: proc(desc: Process_Desc) -> (Process, Error) {
 	stdout_handle := windows.GetStdHandle(windows.STD_OUTPUT_HANDLE)
 	stdout_handle := windows.GetStdHandle(windows.STD_OUTPUT_HANDLE)
 	stdin_handle := windows.GetStdHandle(windows.STD_INPUT_HANDLE)
 	stdin_handle := windows.GetStdHandle(windows.STD_INPUT_HANDLE)
 	if desc.stdout != nil {
 	if desc.stdout != nil {
-		stdout_handle = windows.HANDLE(desc.stdout.impl.fd)
+		stdout_handle = windows.HANDLE((^File_Impl)(desc.stdout.impl).fd)
 	}
 	}
 	if desc.stderr != nil {
 	if desc.stderr != nil {
-		stderr_handle = windows.HANDLE(desc.stderr.impl.fd)
+		stderr_handle = windows.HANDLE((^File_Impl)(desc.stderr.impl).fd)
+	}
+	if desc.stdin != nil {
+		stdin_handle = windows.HANDLE((^File_Impl)(desc.stderr.impl).fd)
+	}
+	working_dir_w := windows.wstring(nil)
+	if len(desc.working_dir) > 0 {
+		working_dir_w = windows.utf8_to_wstring(desc.working_dir, temp_allocator())
 	}
 	}
 	process_info: windows.PROCESS_INFORMATION = ---
 	process_info: windows.PROCESS_INFORMATION = ---
 	process_ok := windows.CreateProcessW(
 	process_ok := windows.CreateProcessW(
@@ -519,7 +535,7 @@ _process_start :: proc(desc: Process_Desc) -> (Process, Error) {
 		true,
 		true,
 		windows.CREATE_UNICODE_ENVIRONMENT|windows.NORMAL_PRIORITY_CLASS,
 		windows.CREATE_UNICODE_ENVIRONMENT|windows.NORMAL_PRIORITY_CLASS,
 		raw_data(environment_block_w),
 		raw_data(environment_block_w),
-		nil,
+		working_dir_w,
 		&windows.STARTUPINFOW {
 		&windows.STARTUPINFOW {
 			cb = size_of(windows.STARTUPINFOW),
 			cb = size_of(windows.STARTUPINFOW),
 			hStdError = stderr_handle,
 			hStdError = stderr_handle,
@@ -578,6 +594,10 @@ _process_close :: proc(process: Process) -> (Error) {
 }
 }
 
 
 _process_kill :: proc(process: Process) -> (Error) {
 _process_kill :: proc(process: Process) -> (Error) {
+	// Note(flysand): This is different than what the task manager's "kill process"
+	// functionality does, as we don't try to send WM_CLOSE message first. This
+	// is quite a rough way to kill the process, which should be consistent with
+	// linux. The error code 9 is to mimic SIGKILL event.
 	if !windows.TerminateProcess(windows.HANDLE(process.handle), 9) {
 	if !windows.TerminateProcess(windows.HANDLE(process.handle), 9) {
 		return _get_platform_error()
 		return _get_platform_error()
 	}
 	}