diff --git a/pkgs/additional/bunpen/rt/ext/exec.ha b/pkgs/additional/bunpen/rt/ext/exec.ha index 5503227f2..e4ce344ac 100644 --- a/pkgs/additional/bunpen/rt/ext/exec.ha +++ b/pkgs/additional/bunpen/rt/ext/exec.ha @@ -1,10 +1,13 @@ // vim: set shiftwidth=2 : +use log; +use os; use path; use rt; +use strings; use types::c; -export fn execve(path: str, argv: []str, envp: []str = []) (rt::errno | void) = { +export fn execve(path: str, argv: []str, envp: []str = []) rt::errno = { let path_buf: [path::MAX]c::char = [0...]; syscall( @@ -14,6 +17,21 @@ export fn execve(path: str, argv: []str, envp: []str = []) (rt::errno | void) = to_cstr_array(argv): *[*]nullable *const c::char: uintptr: u64, to_cstr_array(envp): *[*]nullable *const c::char: uintptr: u64, )?; + + log::fatal("[exec] unexpectedly returned without error"); +}; + +// if `name` contains a slash, proceed as per `execve`. +// otherwise, crawl $PATH and try executing until we succeed. +export fn execvpe(name: str, argv: []str, envp: []str = []) rt::errno = { + return if (strings::contains(name, "/")) { + yield execve(name, argv, envp); + } else { + yield match (os::getenv("PATH")) { + case let p: str => yield _execvpe(p, name, argv, envp); + case void => yield execve(name, argv, envp); + }; + }; }; // allocate and return a NULL-terminated array of pointers to c strings. @@ -26,3 +44,19 @@ fn to_cstr_array(strs: []str) []nullable *const c::char = { append(cstrs, null); return cstrs; }; + +// split `path` on `:`, crawl it, and try executing until we succeed. +// note that 1:1 parity with libc's execvpe is probably not what we have here. +fn _execvpe(path: str, name: str, argv: []str, envp: []str = []) rt::errno = { + let status = rt::ENOENT; + let pieces = strings::split(path, ":"); + defer free(pieces); + for (let p .. pieces) { + let this_path = strings::join("/", p, name); + defer free(this_path); + status = execve(this_path, argv, envp); + // try the next item, or return the status if this is the last one. + }; + + return status; +};