use crate::ffi::{block_on, clear_last_error, cstr_to_str, set_io_error, set_last_error};
use crate::process::{
DtactChild, DtactChildStderr, DtactChildStdin, DtactChildStdout, DtactCommand,
};
use std::ffi::c_char;
use std::process::Stdio;
pub const DTACT_STDIN_PIPE: u32 = 1;
pub const DTACT_STDOUT_PIPE: u32 = 2;
pub const DTACT_STDERR_PIPE: u32 = 4;
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_process_init(workers: usize) {
clear_last_error();
crate::process::init(workers.max(1));
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_process_spawn(
program: *const c_char,
argv: *const *const c_char,
arg_count: usize,
stdio_flags: u32,
) -> *mut DtactChild {
clear_last_error();
let Some(program) = (unsafe { cstr_to_str(program) }) else {
return std::ptr::null_mut();
};
let mut cmd = DtactCommand::new(program);
if arg_count > 0 {
if argv.is_null() {
set_last_error("null argv with arg_count > 0");
return std::ptr::null_mut();
}
let arg_ptrs = unsafe { std::slice::from_raw_parts(argv, arg_count) };
for &arg in arg_ptrs {
let Some(arg) = (unsafe { cstr_to_str(arg) }) else {
return std::ptr::null_mut();
};
cmd.arg(arg);
}
}
if stdio_flags & DTACT_STDIN_PIPE != 0 {
cmd.stdin(Stdio::piped());
}
if stdio_flags & DTACT_STDOUT_PIPE != 0 {
cmd.stdout(Stdio::piped());
}
if stdio_flags & DTACT_STDERR_PIPE != 0 {
cmd.stderr(Stdio::piped());
}
match cmd.spawn() {
Ok(child) => Box::into_raw(Box::new(child)),
Err(e) => {
set_io_error(&e);
std::ptr::null_mut()
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_process_child_id(child: *mut DtactChild) -> u32 {
clear_last_error();
if child.is_null() {
set_last_error("null child handle");
return 0;
}
unsafe { &*child }.id()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_process_child_kill(child: *mut DtactChild) -> i32 {
clear_last_error();
if child.is_null() {
set_last_error("null child handle");
return -1;
}
match unsafe { &mut *child }.kill() {
Ok(()) => 0,
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_process_child_wait(
child: *mut DtactChild,
out_code: *mut i32,
) -> i32 {
clear_last_error();
if child.is_null() {
set_last_error("null child handle");
return -1;
}
let child = unsafe { Box::from_raw(child) };
match block_on(child.wait()) {
Ok(status) => status.code().map_or_else(
|| {
set_last_error("process terminated without an exit code");
-1
},
|code| {
if !out_code.is_null() {
unsafe { *out_code = code };
}
0
},
),
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_process_child_free(child: *mut DtactChild) {
if !child.is_null() {
drop(unsafe { Box::from_raw(child) });
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_process_child_take_stdin(
child: *mut DtactChild,
) -> *mut DtactChildStdin {
clear_last_error();
if child.is_null() {
set_last_error("null child handle");
return std::ptr::null_mut();
}
unsafe { &mut *child }.take_stdin().map_or_else(
|| {
set_last_error("stdin pipe unavailable or already taken");
std::ptr::null_mut()
},
|h| Box::into_raw(Box::new(h)),
)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_process_child_take_stdout(
child: *mut DtactChild,
) -> *mut DtactChildStdout {
clear_last_error();
if child.is_null() {
set_last_error("null child handle");
return std::ptr::null_mut();
}
unsafe { &mut *child }.take_stdout().map_or_else(
|| {
set_last_error("stdout pipe unavailable or already taken");
std::ptr::null_mut()
},
|h| Box::into_raw(Box::new(h)),
)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_process_child_take_stderr(
child: *mut DtactChild,
) -> *mut DtactChildStderr {
clear_last_error();
if child.is_null() {
set_last_error("null child handle");
return std::ptr::null_mut();
}
unsafe { &mut *child }.take_stderr().map_or_else(
|| {
set_last_error("stderr pipe unavailable or already taken");
std::ptr::null_mut()
},
|h| Box::into_raw(Box::new(h)),
)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_process_stdin_write(
stdin: *mut DtactChildStdin,
buf: *const u8,
len: usize,
) -> isize {
clear_last_error();
if stdin.is_null() || buf.is_null() {
set_last_error("null stdin handle or buffer");
return -1;
}
let stdin = unsafe { &mut *stdin };
let data = unsafe { std::slice::from_raw_parts(buf, len) }.to_vec();
match block_on(stdin.write(data)) {
Ok((n, _)) => n as isize,
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_process_stdin_close(stdin: *mut DtactChildStdin) {
if !stdin.is_null() {
unsafe { Box::from_raw(stdin) }.close();
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_process_stdout_read(
stdout: *mut DtactChildStdout,
buf: *mut u8,
len: usize,
) -> isize {
clear_last_error();
if stdout.is_null() || buf.is_null() {
set_last_error("null stdout handle or buffer");
return -1;
}
let stdout = unsafe { &mut *stdout };
let scratch = vec![0u8; len];
match block_on(stdout.read(scratch)) {
Ok((n, data)) => {
unsafe { std::ptr::copy_nonoverlapping(data.as_ptr(), buf, n) };
n as isize
}
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_process_stdout_free(stdout: *mut DtactChildStdout) {
if !stdout.is_null() {
drop(unsafe { Box::from_raw(stdout) });
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_process_stderr_read(
stderr: *mut DtactChildStderr,
buf: *mut u8,
len: usize,
) -> isize {
clear_last_error();
if stderr.is_null() || buf.is_null() {
set_last_error("null stderr handle or buffer");
return -1;
}
let stderr = unsafe { &mut *stderr };
let scratch = vec![0u8; len];
match block_on(stderr.read(scratch)) {
Ok((n, data)) => {
unsafe { std::ptr::copy_nonoverlapping(data.as_ptr(), buf, n) };
n as isize
}
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_process_stderr_free(stderr: *mut DtactChildStderr) {
if !stderr.is_null() {
drop(unsafe { Box::from_raw(stderr) });
}
}