From 32f3f5c3001e9f13278bb013c72a25b2441f1bdd Mon Sep 17 00:00:00 2001 From: Jianfeng Jiang Date: Thu, 30 Mar 2023 02:53:39 -0400 Subject: [PATCH] Support interrupting foreground job with Ctrl+C --- src/services/libs/jinux-std/src/lib.rs | 2 -- .../libs/jinux-std/src/process/clone.rs | 18 ++++++---- .../libs/jinux-std/src/process/mod.rs | 35 ++++++++++--------- .../process/posix_thread/posix_thread_ext.rs | 2 +- .../src/process/program_loader/mod.rs | 8 +++-- .../libs/jinux-std/src/process/signal/mod.rs | 20 +++++------ .../libs/jinux-std/src/syscall/brk.rs | 2 +- .../libs/jinux-std/src/syscall/execve.rs | 12 +++---- .../libs/jinux-std/src/syscall/readlink.rs | 2 +- .../jinux-std/src/syscall/rt_sigaction.rs | 2 +- .../libs/jinux-std/src/syscall/setpgid.rs | 31 ++++++++++------ .../libs/jinux-std/src/syscall/wait4.rs | 1 + src/services/libs/jinux-std/src/tty/mod.rs | 2 +- src/services/libs/jinux-std/src/tty/termio.rs | 12 +++++-- 14 files changed, 86 insertions(+), 63 deletions(-) diff --git a/src/services/libs/jinux-std/src/lib.rs b/src/services/libs/jinux-std/src/lib.rs index 501cb66b1..a770d4f66 100644 --- a/src/services/libs/jinux-std/src/lib.rs +++ b/src/services/libs/jinux-std/src/lib.rs @@ -91,12 +91,10 @@ fn run_busybox() -> Result> { let argv = ["sh", "-l"]; let envp = [ "SHELL=/bin/sh", - "PWD=/", "LOGNAME=root", "HOME=/", "USER=root", "PATH=/bin", - "OLDPWD=/", ]; let argv = argv .into_iter() diff --git a/src/services/libs/jinux-std/src/process/clone.rs b/src/services/libs/jinux-std/src/process/clone.rs index 693cb83b4..0967bfea5 100644 --- a/src/services/libs/jinux-std/src/process/clone.rs +++ b/src/services/libs/jinux-std/src/process/clone.rs @@ -124,12 +124,14 @@ pub fn clone_child(parent_context: CpuContext, clone_args: CloneArgs) -> Result< let child_thread = clone_child_thread(parent_context, clone_args)?; let child_tid = child_thread.tid(); debug!( - "*********schedule child thread, child pid = {}**********", + "*********schedule child thread, current tid = {}, child pid = {}**********", + current_thread!().tid(), child_tid ); child_thread.run(); debug!( - "*********return to parent thread, child pid = {}*********", + "*********return to parent thread, current tid = {}, child pid = {}*********", + current_thread!().tid(), child_tid ); Ok(child_tid) @@ -137,12 +139,14 @@ pub fn clone_child(parent_context: CpuContext, clone_args: CloneArgs) -> Result< let child_process = clone_child_process(parent_context, clone_args)?; let child_pid = child_process.pid(); debug!( - "*********schedule child process, child pid = {}**********", + "*********schedule child process, current pid = {}, child pid = {}**********", + current!().pid(), child_pid ); child_process.run(); debug!( - "*********return to parent process, child pid = {}*********", + "*********return to parent process, current pid = {}, child pid = {}*********", + current!().pid(), child_pid ); Ok(child_pid) @@ -197,7 +201,7 @@ fn clone_child_process(parent_context: CpuContext, clone_args: CloneArgs) -> Res // clone vm let parent_root_vmar = current.root_vmar(); let child_root_vmar = clone_vm(parent_root_vmar, clone_flags)?; - let child_user_vm = Some(current.user_vm().unwrap().clone()); + let child_user_vm = current.user_vm().clone(); // clone user space let child_cpu_context = clone_cpu_context( @@ -221,7 +225,7 @@ fn clone_child_process(parent_context: CpuContext, clone_args: CloneArgs) -> Res // clone system V semaphore clone_sysvsem(clone_flags)?; - let child_elf_path = current.executable_path().unwrap().clone(); + let child_elf_path = current.executable_path().read().clone(); let child_thread_name = ThreadName::new_from_executable_path(&child_elf_path)?; // inherit parent's sig mask @@ -243,7 +247,7 @@ fn clone_child_process(parent_context: CpuContext, clone_args: CloneArgs) -> Res child_pid, parent, vec![child_thread], - Some(child_elf_path), + child_elf_path, child_user_vm, child_root_vmar.clone(), Weak::new(), diff --git a/src/services/libs/jinux-std/src/process/mod.rs b/src/services/libs/jinux-std/src/process/mod.rs index 8c5829f8d..755cdff43 100644 --- a/src/services/libs/jinux-std/src/process/mod.rs +++ b/src/services/libs/jinux-std/src/process/mod.rs @@ -40,12 +40,11 @@ pub type ExitCode = i32; const INIT_PROCESS_PID: Pid = 1; /// Process stands for a set of threads that shares the same userspace. -/// Currently, we only support one thread inside a process. pub struct Process { // Immutable Part pid: Pid, - executable_path: Option, - user_vm: Option, + + user_vm: UserVm, root_vmar: Arc>, /// wait for child status changed waiting_children: WaitQueue, @@ -53,6 +52,8 @@ pub struct Process { poll_queue: WaitQueue, // Mutable Part + /// The executable path. + executable_path: RwLock, /// The threads threads: Mutex>>, /// The exit code @@ -97,8 +98,8 @@ impl Process { pid: Pid, parent: Weak, threads: Vec>, - executable_path: Option, - user_vm: Option, + executable_path: String, + user_vm: UserVm, root_vmar: Arc>, process_group: Weak, file_table: Arc>, @@ -113,7 +114,7 @@ impl Process { Self { pid, threads: Mutex::new(threads), - executable_path, + executable_path: RwLock::new(executable_path), user_vm, root_vmar, waiting_children, @@ -146,6 +147,8 @@ impl Process { argv: Vec, envp: Vec, ) -> Result> { + // spawn user process should give an absolute path + debug_assert!(executable_path.starts_with('/')); let process = Process::create_user_process(executable_path, argv, envp)?; // FIXME: How to determine the fg process group? let pgid = process.pgid(); @@ -174,8 +177,8 @@ impl Process { pid, parent, vec![], - Some(executable_path.to_string()), - Some(user_vm), + executable_path.to_string(), + user_vm, Arc::new(root_vmar), process_group, Arc::new(Mutex::new(file_table)), @@ -314,8 +317,8 @@ impl Process { } /// returns the user_vm - pub fn user_vm(&self) -> Option<&UserVm> { - self.user_vm.as_ref() + pub fn user_vm(&self) -> &UserVm { + &self.user_vm } /// returns the root vmar @@ -324,11 +327,8 @@ impl Process { } /// returns the user heap if the process does have, otherwise None - pub fn user_heap(&self) -> Option<&UserHeap> { - match self.user_vm { - None => None, - Some(ref user_vm) => Some(user_vm.user_heap()), - } + pub fn user_heap(&self) -> &UserHeap { + self.user_vm.user_heap() } /// free zombie child with pid, returns the exit code of child process. @@ -336,6 +336,7 @@ impl Process { pub fn reap_zombie_child(&self, pid: Pid) -> i32 { let child_process = self.children.lock().remove(&pid).unwrap(); assert!(child_process.status().lock().is_zombie()); + child_process.root_vmar().destroy_all().unwrap(); for thread in &*child_process.threads.lock() { thread_table::remove_thread(thread.tid()); } @@ -359,8 +360,8 @@ impl Process { self.children.lock().len() != 0 } - pub fn executable_path(&self) -> Option<&String> { - self.executable_path.as_ref() + pub fn executable_path(&self) -> &RwLock { + &self.executable_path } pub fn status(&self) -> &Mutex { diff --git a/src/services/libs/jinux-std/src/process/posix_thread/posix_thread_ext.rs b/src/services/libs/jinux-std/src/process/posix_thread/posix_thread_ext.rs index 020dc2153..854071316 100644 --- a/src/services/libs/jinux-std/src/process/posix_thread/posix_thread_ext.rs +++ b/src/services/libs/jinux-std/src/process/posix_thread/posix_thread_ext.rs @@ -34,7 +34,7 @@ impl PosixThreadExt for Thread { argv: Vec, envp: Vec, ) -> Result> { - let elf_load_info = load_program_to_root_vmar( + let (_, elf_load_info) = load_program_to_root_vmar( root_vmar, executable_path.to_string(), argv, diff --git a/src/services/libs/jinux-std/src/process/program_loader/mod.rs b/src/services/libs/jinux-std/src/process/program_loader/mod.rs index 314a18f70..d13dc7a34 100644 --- a/src/services/libs/jinux-std/src/process/program_loader/mod.rs +++ b/src/services/libs/jinux-std/src/process/program_loader/mod.rs @@ -25,15 +25,16 @@ pub fn load_program_to_root_vmar( envp: Vec, fs_resolver: &FsResolver, recursion_limit: usize, -) -> Result { +) -> Result<(String, ElfLoadInfo)> { // Temporary use because fs_resolver cannot deal with procfs now. // FIXME: removes this when procfs is ready. let executable_path = if &executable_path == "/proc/self/exe" { - current!().executable_path().unwrap().clone() + current!().executable_path().read().clone() } else { executable_path }; let fs_path = FsPath::new(AT_FDCWD, &executable_path)?; + let abs_path = fs_resolver.lookup(&fs_path)?.abs_path(); let file = fs_resolver.open(&fs_path, AccessMode::O_RDONLY as u32, 0)?; let file_header = { // read the first page of file header @@ -58,5 +59,6 @@ pub fn load_program_to_root_vmar( } let elf_file = Arc::new(FileHandle::new_inode_handle(file)); - load_elf_to_root_vmar(root_vmar, &file_header, elf_file, argv, envp) + let elf_load_info = load_elf_to_root_vmar(root_vmar, &file_header, elf_file, argv, envp)?; + Ok((abs_path, elf_load_info)) } diff --git a/src/services/libs/jinux-std/src/process/signal/mod.rs b/src/services/libs/jinux-std/src/process/signal/mod.rs index 03d1c53ec..3c2913e44 100644 --- a/src/services/libs/jinux-std/src/process/signal/mod.rs +++ b/src/services/libs/jinux-std/src/process/signal/mod.rs @@ -31,7 +31,6 @@ pub fn handle_pending_signal(context: &mut CpuContext) -> Result<()> { let current_thread = current_thread!(); let posix_thread = current_thread.as_posix_thread().unwrap(); let pid = current.pid(); - let process_name = current.executable_path().unwrap(); let sig_mask = posix_thread.sig_mask().lock().clone(); let mut thread_sig_queues = posix_thread.sig_queues().lock(); let mut proc_sig_queues = current.sig_queues().lock(); @@ -45,12 +44,12 @@ pub fn handle_pending_signal(context: &mut CpuContext) -> Result<()> { }; if let Some(signal) = signal { let sig_num = signal.num(); - debug!("sig_num = {:?}, sig_name = {}", sig_num, sig_num.sig_name()); + trace!("sig_num = {:?}, sig_name = {}", sig_num, sig_num.sig_name()); let sig_action = current.sig_dispositions().lock().get(sig_num); - debug!("sig action: {:x?}", sig_action); + trace!("sig action: {:x?}", sig_action); match sig_action { SigAction::Ign => { - debug!("Ignore signal {:?}", sig_num); + trace!("Ignore signal {:?}", sig_num); } SigAction::User { handler_addr, @@ -68,12 +67,12 @@ pub fn handle_pending_signal(context: &mut CpuContext) -> Result<()> { )?, SigAction::Dfl => { let sig_default_action = SigDefaultAction::from_signum(sig_num); - debug!("sig_default_action: {:?}", sig_default_action); + trace!("sig_default_action: {:?}", sig_default_action); match sig_default_action { SigDefaultAction::Core | SigDefaultAction::Term => { - error!( + warn!( "{:?}: terminating on signal {}", - process_name, + current.executable_path().read(), sig_num.sig_name() ); // FIXME: How to set correct status if process is terminated @@ -116,7 +115,7 @@ pub fn handle_user_signal( context: &mut CpuContext, sig_info: siginfo_t, ) -> Result<()> { - debug!("sig_num = {:?}", sig_num); + debug!("sig_num = {:?}, signame = {}", sig_num, sig_num.sig_name()); debug!("handler_addr = 0x{:x}", handler_addr); debug!("flags = {:?}", flags); debug!("restorer_addr = 0x{:x}", restorer_addr); @@ -134,7 +133,8 @@ pub fn handle_user_signal( // block signals in sigmask when running signal handler posix_thread.sig_mask().lock().block(mask.as_u64()); - // set up signal stack in user stack. // avoid corrupt user stack, we minus 128 first. + // Set up signal stack in user stack, + // to avoid corrupting user stack, we minus 128 first. let mut user_rsp = context.gp_regs.rsp; user_rsp = user_rsp - 128; @@ -167,7 +167,7 @@ pub fn handle_user_signal( // If contains SA_RESTORER flag, trampoline code is provided by libc in restorer_addr. // We just store restorer_addr on user stack to allow user code just to trampoline code. user_rsp = write_u64_to_user_stack(user_rsp, restorer_addr as u64)?; - debug!("After set restorer addr: user_rsp = 0x{:x}", user_rsp); + trace!("After set restorer addr: user_rsp = 0x{:x}", user_rsp); } else { // Otherwise we create a trampoline. // FIXME: This may cause problems if we read old_context from rsp. diff --git a/src/services/libs/jinux-std/src/syscall/brk.rs b/src/services/libs/jinux-std/src/syscall/brk.rs index f28fe5210..528033a25 100644 --- a/src/services/libs/jinux-std/src/syscall/brk.rs +++ b/src/services/libs/jinux-std/src/syscall/brk.rs @@ -14,7 +14,7 @@ pub fn sys_brk(heap_end: u64) -> Result { }; debug!("new heap end = {:x?}", heap_end); let current = current!(); - let user_heap = current.user_heap().unwrap(); + let user_heap = current.user_heap(); let new_heap_end = user_heap.brk(new_heap_end)?; Ok(SyscallReturn::Return(new_heap_end as _)) diff --git a/src/services/libs/jinux-std/src/syscall/execve.rs b/src/services/libs/jinux-std/src/syscall/execve.rs index 5603c868a..37c05992f 100644 --- a/src/services/libs/jinux-std/src/syscall/execve.rs +++ b/src/services/libs/jinux-std/src/syscall/execve.rs @@ -17,6 +17,7 @@ pub fn sys_execve( log_syscall_entry!(SYS_EXECVE); let executable_path = read_cstring_from_user(filename_ptr, MAX_FILENAME_LEN)?; let executable_path = executable_path.into_string().unwrap(); + let argv = read_cstring_vec(argv_ptr_ptr, MAX_ARGV_NUMBER, MAX_ARG_LEN)?; let envp = read_cstring_vec(envp_ptr_ptr, MAX_ENVP_NUMBER, MAX_ENV_LEN)?; debug!( @@ -33,20 +34,19 @@ pub fn sys_execve( // FIXME: should we clear ctid when execve? *posix_thread.clear_child_tid().lock() = 0; - // let elf_file_content = crate::user_apps::read_execve_hello_content(); let current = current!(); // destroy root vmars let root_vmar = current.root_vmar(); root_vmar.clear()?; - let user_vm = current - .user_vm() - .expect("[Internal Error] User process should have user vm"); - user_vm.set_default(); + current.user_vm().set_default(); // load elf content to new vm space let fs_resolver = &*current.fs().read(); - let elf_load_info = + debug!("load program to root vmar"); + let (new_executable_path, elf_load_info) = load_program_to_root_vmar(root_vmar, executable_path, argv, envp, fs_resolver, 1)?; debug!("load elf in execve succeeds"); + // set executable path + *current.executable_path().write() = new_executable_path; // set signal disposition to default current.sig_dispositions().lock().inherit(); // set cpu context to default diff --git a/src/services/libs/jinux-std/src/syscall/readlink.rs b/src/services/libs/jinux-std/src/syscall/readlink.rs index bad28e9c1..ad0a9780c 100644 --- a/src/services/libs/jinux-std/src/syscall/readlink.rs +++ b/src/services/libs/jinux-std/src/syscall/readlink.rs @@ -26,7 +26,7 @@ pub fn sys_readlinkat( let current = current!(); if pathname == CString::new("/proc/self/exe")? { // "proc/self/exe" is used to read the filename of current executable - let process_file_name = current.executable_path().unwrap(); + let process_file_name = current.executable_path().read(); debug!("process exec filename= {:?}", process_file_name); // readlink does not append a terminating null byte to buf let bytes = process_file_name.as_bytes(); diff --git a/src/services/libs/jinux-std/src/syscall/rt_sigaction.rs b/src/services/libs/jinux-std/src/syscall/rt_sigaction.rs index c68ecf379..cab93e291 100644 --- a/src/services/libs/jinux-std/src/syscall/rt_sigaction.rs +++ b/src/services/libs/jinux-std/src/syscall/rt_sigaction.rs @@ -33,7 +33,7 @@ pub fn sys_rt_sigaction( if sig_action_addr != 0 { let sig_action_c = read_val_from_user::(sig_action_addr)?; let sig_action = SigAction::try_from(sig_action_c).unwrap(); - debug!("sig action = {:?}", sig_action); + trace!("sig action = {:?}", sig_action); sig_dispositions.set(sig_num, sig_action); } diff --git a/src/services/libs/jinux-std/src/syscall/setpgid.rs b/src/services/libs/jinux-std/src/syscall/setpgid.rs index c450b4af6..60f2a2b93 100644 --- a/src/services/libs/jinux-std/src/syscall/setpgid.rs +++ b/src/services/libs/jinux-std/src/syscall/setpgid.rs @@ -1,7 +1,11 @@ use crate::{ log_syscall_entry, prelude::*, - process::{process_group::ProcessGroup, process_table, Pgid, Pid}, + process::{ + process_group::ProcessGroup, + process_table::{self, pid_to_process}, + Pgid, Pid, + }, }; use super::{SyscallReturn, SYS_SETPGID}; @@ -15,30 +19,35 @@ pub fn sys_setpgid(pid: Pid, pgid: Pgid) -> Result { let pgid = if pgid == 0 { pid } else { pgid }; debug!("pid = {}, pgid = {}", pid, pgid); - if current.pid() != pid { + if pid != current.pid() && !current.children().lock().contains_key(&pid) { return_errno_with_message!( - Errno::EACCES, - "cannot set pgid for process other than current" + Errno::ESRCH, + "cannot set pgid for process other than current or children of current" ); } + // FIXME: If pid is child process of current and already calls execve, should return error. + // How can we determine a child process has called execve? // only can move process to an existing group or self if pgid != pid && process_table::pgid_to_process_group(pgid).is_none() { return_errno_with_message!(Errno::EPERM, "process group must exist"); } + let process = + pid_to_process(pid).ok_or(Error::with_message(Errno::ESRCH, "process does not exist"))?; + // if the process already belongs to the process group - if current.pgid() == pgid { + if process.pgid() == pgid { return Ok(SyscallReturn::Return(0)); } - if let Some(new_process_group) = process_table::pgid_to_process_group(pgid) { - new_process_group.add_process(current.clone()); - current.set_process_group(Arc::downgrade(&new_process_group)); + if let Some(process_group) = process_table::pgid_to_process_group(pgid) { + process_group.add_process(process.clone()); + process.set_process_group(Arc::downgrade(&process_group)); } else { - let new_process_group = Arc::new(ProcessGroup::new(current.clone())); - new_process_group.add_process(current.clone()); - current.set_process_group(Arc::downgrade(&new_process_group)); + let new_process_group = Arc::new(ProcessGroup::new(process.clone())); + // new_process_group.add_process(process.clone()); + process.set_process_group(Arc::downgrade(&new_process_group)); process_table::add_process_group(new_process_group); } diff --git a/src/services/libs/jinux-std/src/syscall/wait4.rs b/src/services/libs/jinux-std/src/syscall/wait4.rs index a6f751506..ebbfbe7db 100644 --- a/src/services/libs/jinux-std/src/syscall/wait4.rs +++ b/src/services/libs/jinux-std/src/syscall/wait4.rs @@ -17,6 +17,7 @@ pub fn sys_wait4(wait_pid: u64, exit_status_ptr: u64, wait_options: u32) -> Resu "pid = {}, exit_status_ptr = {}, wait_options: {:?}", wait_pid as i32, exit_status_ptr, wait_options ); + debug!("wait4 current pid = {}", current!().pid()); let process_filter = ProcessFilter::from_id(wait_pid as _); let (return_pid, exit_code) = wait_child_exit(process_filter, wait_options)?; if return_pid != 0 && exit_status_ptr != 0 { diff --git a/src/services/libs/jinux-std/src/tty/mod.rs b/src/services/libs/jinux-std/src/tty/mod.rs index 392dac285..9637abc0b 100644 --- a/src/services/libs/jinux-std/src/tty/mod.rs +++ b/src/services/libs/jinux-std/src/tty/mod.rs @@ -85,7 +85,7 @@ impl File for Tty { IoctlCmd::TCGETS => { // Get terminal attributes let termios = self.ldisc.get_termios(); - debug!("get termios = {:?}", termios); + trace!("get termios = {:?}", termios); write_val_to_user(arg, &termios)?; Ok(0) } diff --git a/src/services/libs/jinux-std/src/tty/termio.rs b/src/services/libs/jinux-std/src/tty/termio.rs index 5bdc10d94..60d3e5449 100644 --- a/src/services/libs/jinux-std/src/tty/termio.rs +++ b/src/services/libs/jinux-std/src/tty/termio.rs @@ -242,7 +242,11 @@ impl KernelTermios { } pub fn contains_isig(&self) -> bool { - self.c_lflags.contains(C_LFLAGS::ISIG) + // self.c_lflags.contains(C_LFLAGS::ISIG) + + // FIXME: It seems that ash dose not respect ISIG (not very sure), + // so for ash, if tty is set cannonical mode, then ISIG is also open. + self.is_canonical_mode() } pub fn contain_echo(&self) -> bool { @@ -250,7 +254,11 @@ impl KernelTermios { } pub fn contains_echo_ctl(&self) -> bool { - self.c_lflags.contains(C_LFLAGS::ECHOCTL) + // self.c_lflags.contains(C_LFLAGS::ECHOCTL) + + // FIXME: It seems that ash dose not contains ECHOCTL (not very sure), + // so for ash, it tty contains ECHO, then ECHOCTL is also open. + self.contain_echo() } pub fn contains_iexten(&self) -> bool {