mirror of
https://github.com/odin-lang/Odin.git
synced 2026-02-14 23:33:15 +00:00
[os2/process]: Implement process_exec
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package os2
|
||||
|
||||
import "base:runtime"
|
||||
import "core:strings"
|
||||
import "core:time"
|
||||
|
||||
/*
|
||||
@@ -347,6 +348,95 @@ 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.
|
||||
|
||||
Use this function when the target process doesn't require any input from stdin,
|
||||
in order to complete.
|
||||
|
||||
This procedure does not free `stdout` and `stderr` slices before an error is
|
||||
returned. Make sure to call `delete` on these slices.
|
||||
|
||||
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_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.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user