use rustix::fd::BorrowedFd;
use rustix::fs::AtFlags;
use rustix::mm::{mmap_anonymous, MapFlags, ProtFlags};
use alloc::vec::Vec;
use core::cmp::max;
use core::ffi::CStr;
use core::ptr::{copy_nonoverlapping, null_mut};
use core::slice;
use crate::env::set::load_environ;
use crate::{convert_res, set_errno, Errno};
use libc::{c_char, c_int};
#[no_mangle]
unsafe extern "C" fn execl(path: *const c_char, arg: *const c_char, mut argv: ...) -> c_int {
let mut vec = Vec::new();
vec.push(arg);
loop {
let ptr = argv.arg::<*const c_char>();
vec.push(ptr);
if ptr.is_null() {
break;
}
}
execv(path, vec.as_ptr())
}
#[no_mangle]
unsafe extern "C" fn execle(path: *const c_char, arg: *const c_char, mut argv: ...) -> c_int {
let mut vec = Vec::new();
vec.push(arg);
let envp = loop {
let ptr = argv.arg::<*const c_char>();
vec.push(ptr);
if ptr.is_null() {
break argv.arg::<*const *const c_char>();
}
};
execve(path, vec.as_ptr(), envp)
}
#[no_mangle]
unsafe extern "C" fn execlp(file: *const c_char, arg: *const c_char, mut argv: ...) -> c_int {
let mut vec = Vec::new();
vec.push(arg);
loop {
let ptr = argv.arg::<*const c_char>();
vec.push(ptr);
if ptr.is_null() {
break;
}
}
execvp(file, vec.as_ptr())
}
#[no_mangle]
unsafe extern "C" fn execv(prog: *const c_char, argv: *const *const c_char) -> c_int {
libc!(libc::execv(prog, argv));
let environ = load_environ();
execve(prog, argv, environ as *const _)
}
#[no_mangle]
unsafe extern "C" fn execve(
prog: *const c_char,
argv: *const *const c_char,
envp: *const *const c_char,
) -> c_int {
libc!(libc::execve(prog, argv, envp));
let err = rustix::runtime::execve(
CStr::from_ptr(prog),
argv as *const *const _,
envp as *const *const _,
);
set_errno(Errno(err.raw_os_error()));
-1
}
#[no_mangle]
unsafe extern "C" fn execvp(file: *const c_char, argv: *const *const c_char) -> c_int {
libc!(libc::execvp(file, argv));
let environ = load_environ();
execvpe(file, argv, environ as *const _)
}
#[no_mangle]
unsafe extern "C" fn execvpe(
file: *const c_char,
argv: *const *const c_char,
envp: *const *const c_char,
) -> c_int {
libc!(libc::execvpe(file, argv, envp));
let file = CStr::from_ptr(file);
let file_bytes = file.to_bytes();
if file_bytes.contains(&b'/') {
let err = rustix::runtime::execve(file, argv.cast(), envp.cast());
set_errno(Errno(err.raw_os_error()));
return -1;
}
let path = crate::env::get::_getenv(b"PATH");
let path = if path.is_null() {
c"/bin:/usr/bin"
} else {
CStr::from_ptr(path)
};
let mut longest_length = 0;
for dir in path.to_bytes().split(|byte| *byte == b':') {
longest_length = max(longest_length, dir.len());
}
let buffer = match convert_res(mmap_anonymous(
null_mut(),
longest_length + 1 + file_bytes.len() + 1,
ProtFlags::READ | ProtFlags::WRITE,
MapFlags::PRIVATE,
)) {
Some(buffer) => buffer.cast::<u8>(),
None => return -1,
};
let mut access_error = false;
for dir in path.to_bytes().split(|byte| *byte == b':') {
copy_nonoverlapping(dir.as_ptr(), buffer, dir.len());
buffer.add(dir.len()).write(b'/');
copy_nonoverlapping(
file_bytes.as_ptr(),
buffer.add(dir.len() + 1),
file_bytes.len(),
);
buffer.add(dir.len() + 1 + file_bytes.len()).write(b'\0');
let slice = slice::from_raw_parts(buffer, dir.len() + 1 + file_bytes.len() + 1);
let error = rustix::runtime::execve(
CStr::from_bytes_with_nul(slice).unwrap(),
argv.cast(),
envp.cast(),
);
match error {
rustix::io::Errno::ACCESS => access_error = true,
rustix::io::Errno::NOENT | rustix::io::Errno::NOTDIR => {}
_ => {
set_errno(Errno(error.raw_os_error()));
return -1;
}
}
}
set_errno(Errno(if access_error {
libc::EACCES
} else {
libc::ENOENT
}));
-1
}
#[no_mangle]
unsafe extern "C" fn fexecve(
fd: c_int,
argv: *const *const c_char,
envp: *const *const c_char,
) -> c_int {
libc!(libc::fexecve(fd, argv, envp));
let mut error = rustix::runtime::execveat(
BorrowedFd::borrow_raw(fd),
c"",
argv as *const *const _,
envp as *const *const _,
AtFlags::EMPTY_PATH,
);
#[cfg(any(target_os = "android", target_os = "linux"))]
if let rustix::io::Errno::NOSYS = error {
const PREFIX: &[u8] = b"/proc/self/fd/";
const PREFIX_LEN: usize = PREFIX.len();
let mut buf = [0_u8; PREFIX_LEN + 20 + 1];
buf[..PREFIX_LEN].copy_from_slice(PREFIX);
let fd_dec = rustix::path::DecInt::from_fd(BorrowedFd::borrow_raw(fd));
let fd_bytes = fd_dec.as_c_str().to_bytes_with_nul();
buf[PREFIX_LEN..PREFIX_LEN + fd_bytes.len()].copy_from_slice(fd_bytes);
error = rustix::runtime::execve(
CStr::from_bytes_with_nul_unchecked(&buf),
argv.cast(),
envp.cast(),
);
}
set_errno(Errno(error.raw_os_error()));
-1
}