use alloc::{string::ToString, sync::Arc, vec::Vec};
use core::ffi::c_char;
use ax_errno::{AxError, AxResult};
use ax_fs::FS_CONTEXT;
use ax_hal::uspace::UserContext;
use ax_task::current;
use starry_vm::vm_load_until_nul;
use crate::{
config::USER_HEAP_BASE,
file::FD_TABLE,
mm::{load_user_app, vm_load_string},
task::AsThread,
};
pub fn sys_execve(
uctx: &mut UserContext,
path: *const c_char,
argv: *const *const c_char,
envp: *const *const c_char,
) -> AxResult<isize> {
let path = vm_load_string(path)?;
let args = if argv.is_null() {
Vec::new()
} else {
vm_load_until_nul(argv)?
.into_iter()
.map(vm_load_string)
.collect::<Result<Vec<_>, _>>()?
};
let envs = if envp.is_null() {
Vec::new()
} else {
vm_load_until_nul(envp)?
.into_iter()
.map(vm_load_string)
.collect::<Result<Vec<_>, _>>()?
};
debug!("sys_execve <= path: {path:?}, args: {args:?}, envs: {envs:?}");
let curr = current();
let proc_data = &curr.as_thread().proc_data;
if proc_data.proc.threads().len() > 1 {
error!("sys_execve: multi-thread not supported");
return Err(AxError::WouldBlock);
}
let mut aspace = proc_data.aspace.lock();
let (entry_point, user_stack_base) =
load_user_app(&mut aspace, Some(path.as_str()), &args, &envs)?;
drop(aspace);
let loc = FS_CONTEXT.lock().resolve(&path)?;
curr.set_name(loc.name());
*proc_data.exe_path.write() = loc.absolute_path()?.to_string();
*proc_data.cmdline.write() = Arc::new(args);
proc_data.set_heap_top(USER_HEAP_BASE);
proc_data.signal.reset_actions();
curr.as_thread().set_clear_child_tid(0);
let mut fd_table = FD_TABLE.write();
let cloexec_fds = fd_table
.ids()
.filter(|it| fd_table.get(*it).unwrap().cloexec)
.collect::<Vec<_>>();
for fd in cloexec_fds {
fd_table.remove(fd);
}
drop(fd_table);
uctx.set_ip(entry_point.as_usize());
uctx.set_sp(user_stack_base.as_usize());
Ok(0)
}