123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499 |
- package os2
- import "base:runtime"
- import "core:strings"
- import "core:time"
- /*
- In procedures that explicitly state this as one of the allowed values,
- specifies an infinite timeout.
- */
- TIMEOUT_INFINITE :: time.MIN_DURATION // Note(flysand): Any negative duration will be treated as infinity
- /*
- Arguments to the current process.
- */
- args := get_args()
- @(private="file")
- get_args :: proc() -> []string {
- result := make([]string, len(runtime.args__), heap_allocator())
- for rt_arg, i in runtime.args__ {
- result[i] = string(rt_arg)
- }
- return result
- }
- /*
- Exit the current process.
- */
- exit :: proc "contextless" (code: int) -> ! {
- _exit(code)
- }
- /*
- Obtain the UID of the current process.
- **Note(windows)**: Windows doesn't follow the posix permissions model, so
- the function simply returns -1.
- */
- @(require_results)
- get_uid :: proc() -> int {
- return _get_uid()
- }
- /*
- Obtain the effective UID of the current process.
- The effective UID is typically the same as the UID of the process. In case
- the process was run by a user with elevated permissions, the process may
- lower the privilege to perform some tasks without privilege. In these cases
- the real UID of the process and the effective UID are different.
- **Note(windows)**: Windows doesn't follow the posix permissions model, so
- the function simply returns -1.
- */
- @(require_results)
- get_euid :: proc() -> int {
- return _get_euid()
- }
- /*
- Obtain the GID of the current process.
- **Note(windows)**: Windows doesn't follow the posix permissions model, so
- the function simply returns -1.
- */
- @(require_results)
- get_gid :: proc() -> int {
- return _get_gid()
- }
- /*
- Obtain the effective GID of the current process.
- The effective GID is typically the same as the GID of the process. In case
- the process was run by a user with elevated permissions, the process may
- lower the privilege to perform some tasks without privilege. In these cases
- the real GID of the process and the effective GID are different.
- **Note(windows)**: Windows doesn't follow the posix permissions model, so
- the function simply returns -1.
- */
- @(require_results)
- get_egid :: proc() -> int {
- return _get_egid()
- }
- /*
- Obtain the ID of the current process.
- */
- @(require_results)
- get_pid :: proc() -> int {
- return _get_pid()
- }
- /*
- Obtain the ID of the parent process.
- **Note(windows)**: Windows does not mantain strong relationships between
- parent and child processes. This function returns the ID of the process
- that has created the current process. In case the parent has died, the ID
- returned by this function can identify a non-existent or a different
- process.
- */
- @(require_results)
- get_ppid :: proc() -> int {
- return _get_ppid()
- }
- /*
- Obtain ID's of all processes running in the system.
- */
- @(require_results)
- process_list :: proc(allocator: runtime.Allocator) -> ([]int, Error) {
- return _process_list(allocator)
- }
- /*
- Bit set specifying which fields of the `Process_Info` struct need to be
- obtained by the `process_info()` procedure. Each bit corresponds to a
- field in the `Process_Info` struct.
- */
- Process_Info_Fields :: bit_set[Process_Info_Field]
- Process_Info_Field :: enum {
- Executable_Path,
- PPid,
- Priority,
- Command_Line,
- Command_Args,
- Environment,
- Username,
- Working_Dir,
- }
- ALL_INFO :: Process_Info_Fields{.Executable_Path, .PPid, .Priority, .Command_Line, .Command_Args, .Environment, .Username, .Working_Dir}
- /*
- Contains information about the process as obtained by the `process_info()`
- procedure.
- */
- Process_Info :: struct {
- // The information about a process the struct contains. `pid` is always
- // stored, no matter what.
- fields: Process_Info_Fields,
- // The ID of the process.
- pid: int,
- // The ID of the parent process.
- ppid: int,
- // The process priority.
- priority: int,
- // The path to the executable, which the process runs.
- executable_path: string,
- // The command line supplied to the process.
- command_line: string,
- // The arguments supplied to the process.
- command_args: []string,
- // The environment of the process.
- environment: []string,
- // The username of the user who started the process.
- username: string,
- // The current working directory of the process.
- working_dir: string,
- }
- /*
- Obtain information about a process.
- This procedure obtains an information, specified by `selection` parameter of
- a process given by `pid`.
- Use `free_process_info` to free the memory allocated by this procedure. The
- `free_process_info` procedure needs to be called, even if this procedure
- returned an error, as some of the fields may have been allocated.
- **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 procedure.
- */
- @(require_results)
- process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) {
- 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. The
- `free_process_info` procedure needs to be called, even if this procedure
- returned an error, as some of the fields may have been allocated.
- **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 procedure.
- */
- @(require_results)
- 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)
- }
- /*
- Obtain information about the current process.
- This procedure obtains the information, specified by `selection` parameter
- about the currently running process.
- Use `free_process_info` to free the memory allocated by this procedure. The
- `free_process_info` procedure needs to be called, even if this procedure
- returned an error, as some of the fields may have been allocated.
- **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 procedure.
- */
- @(require_results)
- current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) {
- 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.
- This procedure frees the memory occupied by process info using the provided
- allocator. The allocator needs to be the same allocator that was supplied
- to the `process_info` function.
- */
- free_process_info :: proc(pi: Process_Info, allocator: runtime.Allocator) {
- delete(pi.executable_path, allocator)
- delete(pi.command_line, allocator)
- for a in pi.command_args {
- delete(a, allocator)
- }
- delete(pi.command_args, allocator)
- for s in pi.environment {
- delete(s, allocator)
- }
- delete(pi.environment, allocator)
- delete(pi.working_dir, allocator)
- delete(pi.username, allocator)
- }
- /*
- Represents a process handle.
- When a process dies, the OS is free to re-use the pid of that process. The
- `Process` struct represents a handle to the process that will refer to a
- specific process, even after it has died.
- **Note(linux)**: The `handle` will be referring to pidfd.
- */
- Process :: struct {
- pid: int,
- handle: uintptr,
- }
- Process_Open_Flags :: bit_set[Process_Open_Flag]
- Process_Open_Flag :: enum {
- // Request for reading from the virtual memory of another process.
- Mem_Read,
- // Request for writing to the virtual memory of another process.
- Mem_Write,
- }
- /*
- Open a process handle using it's pid.
- This procedure obtains a process handle of a process specified by `pid`.
- This procedure can be subject to race conditions. See the description of
- `Process`.
- Use `process_close()` function to close the process handle.
- */
- @(require_results)
- process_open :: proc(pid: int, flags := Process_Open_Flags {}) -> (Process, Error) {
- return _process_open(pid, flags)
- }
- /*
- The description of how a process should be created.
- */
- Process_Desc :: struct {
- // OS-specific attributes.
- sys_attr: _Sys_Process_Attributes,
- // 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 process. The first element of the slice would be the executable.
- command: []string,
- // A slice of strings, each having the format `KEY=VALUE` representing the
- // full environment that the child process will receive.
- // In case this slice is `nil`, the current process' environment is used.
- // NOTE(laytan): maybe should be `Maybe([]string)` so you can do `nil` == current env, empty == empty/no env.
- env: []string,
- // The `stderr` handle to give to the child process. It can be either a file
- // or a writeable end of a pipe. Passing `nil` will shut down the process'
- // stderr output.
- stderr: ^File,
- // The `stdout` handle to give to the child process. It can be either a file
- // or a writeabe end of a pipe. Passing a `nil` will shut down the process'
- // stdout output.
- stdout: ^File,
- // The `stdin` handle to give to the child process. It can either be a file
- // or a readable end of a pipe. Passing a `nil` will shut down the process'
- // input.
- stdin: ^File,
- }
- /*
- Create a new process and obtain its handle.
- This procedure creates a new process, with a given command and environment
- strings as parameters. Use `environ()` to inherit the environment of the
- current process.
- The `desc` parameter specifies the description of how the process should
- be created. It contains information such as the command line, the
- environment of the process, the starting directory and many other options.
- Most of the fields in the struct can be set to `nil` or an empty value.
- Use `process_close` to close the handle to the process. Note, that this
- is not the same as terminating the process. One can terminate the process
- and not close the handle, in which case the handle would be leaked. In case
- the function returns an error, an invalid handle is returned.
- This procedure is not thread-safe. It may alter the inheritance properties
- of file handles in an unpredictable manner. In case multiple threads change
- handle inheritance properties, make sure to serialize all those calls.
- */
- @(require_results)
- process_start :: proc(desc: Process_Desc) -> (Process, Error) {
- return _process_start(desc)
- }
- /*
- Execute the process and capture stdout and stderr streams.
- This procedure creates a new process, with a given command and environment
- strings as parameters, and waits until the process finishes execution. While
- the process is running, this procedure accumulates the output of its stdout
- and stderr streams and returns byte slices containing the captured data from
- the streams.
- This procedure expects that `stdout` and `stderr` fields of the `desc` parameter
- are left at default, i.e. a `nil` value. You can not capture stdout/stderr and
- redirect it to a file at the same time.
- This procedure does not free `stdout` and `stderr` slices before an error is
- returned. Make sure to call `delete` on these slices.
- */
- @(require_results)
- process_exec :: proc(
- desc: Process_Desc,
- allocator: runtime.Allocator,
- loc := #caller_location,
- ) -> (
- state: Process_State,
- stdout: []u8,
- stderr: []u8,
- err: Error,
- ) {
- assert(desc.stdout == nil, "Cannot redirect stdout when it's being captured", loc)
- assert(desc.stderr == nil, "Cannot redirect stderr when it's being captured", loc)
- stdout_r, stdout_w := pipe() or_return
- defer close(stdout_r)
- stderr_r, stderr_w := pipe() or_return
- defer close(stdout_w)
- process: Process
- {
- // NOTE(flysand): Make sure the write-ends are closed, regardless
- // of the outcome. This makes read-ends readable on our side.
- defer close(stdout_w)
- defer close(stderr_w)
- desc := desc
- desc.stdout = stdout_w
- desc.stderr = stderr_w
- process = process_start(desc) or_return
- }
- stdout_builder := strings.builder_make(allocator) or_return
- stderr_builder := strings.builder_make(allocator) or_return
- read_data: for {
- buf: [1024]u8
- n: int
- has_data: bool
- hangup := false
- has_data, err = pipe_has_data(stdout_r)
- if has_data {
- n, err = read(stdout_r, buf[:])
- strings.write_bytes(&stdout_builder, buf[:n])
- }
- switch err {
- case nil: // nothing
- case .Broken_Pipe:
- hangup = true
- case:
- return
- }
- has_data, err = pipe_has_data(stderr_r)
- if has_data {
- n, err = read(stderr_r, buf[:])
- strings.write_bytes(&stderr_builder, buf[:n])
- }
- switch err {
- case nil: // nothing
- case .Broken_Pipe:
- hangup = true
- case:
- return
- }
- if hangup {
- break read_data
- }
- }
- err = nil
- stdout = transmute([]u8) strings.to_string(stdout_builder)
- stderr = transmute([]u8) strings.to_string(stderr_builder)
- state = process_wait(process) or_return
- return
- }
- /*
- The state of the process after it has finished execution.
- */
- Process_State :: struct {
- // The ID of the process.
- pid: int,
- // Specifies whether the process has terminated or is still running.
- exited: bool,
- // The exit code of the process, if it has exited.
- // Will also store the number of the exception or signal that has crashed the
- // process.
- exit_code: int,
- // Specifies whether the termination of the process was successfull or not,
- // i.e. whether it has crashed or not.
- // **Note(windows)**: On windows `true` is always returned, as there is no
- // reliable way to obtain information about whether the process has crashed.
- success: bool,
- // The time the process has spend executing in kernel time.
- system_time: time.Duration,
- // The time the process has spend executing in userspace.
- user_time: time.Duration,
- }
- /*
- Wait for a process event.
- This procedure blocks the execution until the process has exited or the
- timeout (if specified) has reached zero. If the timeout is `TIMEOUT_INFINITE`,
- no timeout restriction is imposed and the procedure can block indefinately.
- If the timeout has expired, the `General_Error.Timeout` is returned as
- the error.
- If an error is returned for any other reason, other than timeout, the
- process state is considered undetermined.
- */
- @(require_results)
- process_wait :: proc(process: Process, timeout := TIMEOUT_INFINITE) -> (Process_State, Error) {
- return _process_wait(process, timeout)
- }
- /*
- Close the handle to a process.
- This procedure closes the handle associated with a process. It **does not**
- terminate a process, in case it was running. In case a termination is
- desired, kill the process first, wait for the process to finish,
- then close the handle.
- */
- @(require_results)
- process_close :: proc(process: Process) -> (Error) {
- return _process_close(process)
- }
- /*
- Terminate a process.
- This procedure terminates a process, specified by it's handle, `process`.
- */
- @(require_results)
- process_kill :: proc(process: Process) -> (Error) {
- return _process_kill(process)
- }
|