Add process group implementation and support set/getpgid, set/getpgrp
This commit is contained in:
		
							parent
							
								
									81dba866f1
								
							
						
					
					
						commit
						88f04c8df9
					
				| @ -8,7 +8,7 @@ use crate::exception::*; | ||||
| use crate::fs::HostStdioFds; | ||||
| use crate::interrupt; | ||||
| use crate::process::idle_reap_zombie_children; | ||||
| use crate::process::ProcessFilter; | ||||
| use crate::process::{ProcessFilter, SpawnAttr}; | ||||
| use crate::signal::SigNum; | ||||
| use crate::time::up_time::init; | ||||
| use crate::util::log::LevelFilter; | ||||
| @ -270,12 +270,21 @@ fn do_new_process( | ||||
|     let file_actions = Vec::new(); | ||||
|     let current = &process::IDLE; | ||||
|     let program_path_str = program_path.to_str().unwrap(); | ||||
| 
 | ||||
|     // Called from occlum_ecall_new_process, give it an identical process group.
 | ||||
|     // So that "occlum run/exec" process will have its own process group.
 | ||||
|     let spawn_attribute = { | ||||
|         let mut attribute = SpawnAttr::default(); | ||||
|         attribute.process_group = Some(0); | ||||
|         attribute | ||||
|     }; | ||||
| 
 | ||||
|     let new_tid = process::do_spawn_without_exec( | ||||
|         &program_path_str, | ||||
|         argv, | ||||
|         &env_concat, | ||||
|         &file_actions, | ||||
|         None, | ||||
|         Some(spawn_attribute), | ||||
|         host_stdio_fds, | ||||
|         current, | ||||
|     )?; | ||||
|  | ||||
| @ -2,6 +2,7 @@ use crate::signal::constants::*; | ||||
| use std::intrinsics::atomic_store; | ||||
| 
 | ||||
| use super::do_futex::futex_wake; | ||||
| use super::pgrp::clean_pgrp_when_exit; | ||||
| use super::process::{Process, ProcessFilter}; | ||||
| use super::{table, ProcessRef, TermStatus, ThreadRef, ThreadStatus}; | ||||
| use crate::prelude::*; | ||||
| @ -102,6 +103,7 @@ fn exit_process(thread: &ThreadRef, term_status: TermStatus) { | ||||
|         let main_tid = pid; | ||||
|         table::del_thread(main_tid).expect("tid must be in the table"); | ||||
|         table::del_process(pid).expect("pid must be in the table"); | ||||
|         clean_pgrp_when_exit(process); | ||||
| 
 | ||||
|         process_inner.exit(term_status, &idle_ref, &mut idle_inner); | ||||
|         idle_inner.remove_zombie_child(pid); | ||||
|  | ||||
| @ -8,11 +8,6 @@ pub fn do_gettid() -> pid_t { | ||||
|     current!().tid() | ||||
| } | ||||
| 
 | ||||
| pub fn do_getpgid() -> pid_t { | ||||
|     // TODO: implement process groups
 | ||||
|     1 | ||||
| } | ||||
| 
 | ||||
| pub fn do_getppid() -> pid_t { | ||||
|     current!().process().parent().pid() | ||||
| } | ||||
|  | ||||
| @ -13,6 +13,7 @@ use crate::fs::{ | ||||
|     CreationFlags, File, FileDesc, FileMode, FileTable, FsView, HostStdioFds, StdinFile, StdoutFile, | ||||
| }; | ||||
| use crate::prelude::*; | ||||
| use crate::process::pgrp::{get_spawn_attribute_pgrp, update_pgrp_for_new_process}; | ||||
| use crate::vm::ProcessVM; | ||||
| 
 | ||||
| mod aux_vec; | ||||
| @ -269,6 +270,11 @@ fn new_process_common( | ||||
|         } | ||||
|         trace!("new process sig_dispositions = {:?}", sig_dispositions); | ||||
| 
 | ||||
|         // Check for process group spawn attribute. This must be done before building the new process.
 | ||||
|         let new_pgid = get_spawn_attribute_pgrp(spawn_attributes)?; | ||||
|         // Use parent process's process group by default.
 | ||||
|         let pgrp_ref = process_ref.pgrp(); | ||||
| 
 | ||||
|         // Make the default thread name to be the process's corresponding elf file name
 | ||||
|         let elf_name = elf_path.rsplit('/').collect::<Vec<&str>>()[0]; | ||||
|         let thread_name = ThreadName::new(elf_name); | ||||
| @ -282,7 +288,7 @@ fn new_process_common( | ||||
|             parent = process_ref; | ||||
|         } | ||||
| 
 | ||||
|         process_builder | ||||
|         let new_process = process_builder | ||||
|             .vm(vm_ref) | ||||
|             .exec_path(&elf_path) | ||||
|             .umask(parent.umask()) | ||||
| @ -291,11 +297,17 @@ fn new_process_common( | ||||
|             .sched(sched_ref) | ||||
|             .rlimits(rlimit_ref) | ||||
|             .fs(fs_ref) | ||||
|             .pgrp(pgrp_ref) | ||||
|             .files(files_ref) | ||||
|             .sig_mask(sig_mask) | ||||
|             .name(thread_name) | ||||
|             .sig_dispositions(sig_dispositions) | ||||
|             .build()? | ||||
|             .build()?; | ||||
| 
 | ||||
|         // This is done here becuase if we want to create a new process group, we must have a new process first.
 | ||||
|         // So we can't set "pgrp" during the build above.
 | ||||
|         update_pgrp_for_new_process(new_process.clone(), new_pgid)?; | ||||
|         new_process | ||||
|     }; | ||||
| 
 | ||||
|     info!( | ||||
|  | ||||
| @ -1,3 +1,4 @@ | ||||
| use super::pgrp::clean_pgrp_when_exit; | ||||
| use super::process::{ProcessFilter, ProcessInner}; | ||||
| use super::wait::Waiter; | ||||
| use super::{table, ProcessRef, ProcessStatus}; | ||||
| @ -102,6 +103,9 @@ fn free_zombie_child(mut parent_inner: SgxMutexGuard<ProcessInner>, zombie_pid: | ||||
|     let zombie = parent_inner.remove_zombie_child(zombie_pid); | ||||
|     debug_assert!(zombie.status() == ProcessStatus::Zombie); | ||||
| 
 | ||||
|     // This has to be done after removing from process table to make sure process.pgid() can work.
 | ||||
|     clean_pgrp_when_exit(&zombie); | ||||
| 
 | ||||
|     let zombie_inner = zombie.inner(); | ||||
|     zombie_inner.term_status().unwrap().as_u32() as i32 | ||||
| } | ||||
|  | ||||
| @ -16,6 +16,7 @@ use crate::sched::SchedAgent; | ||||
| use crate::signal::{SigDispositions, SigQueues}; | ||||
| use crate::vm::ProcessVM; | ||||
| 
 | ||||
| use self::pgrp::ProcessGrp; | ||||
| use self::process::{ProcessBuilder, ProcessInner}; | ||||
| use self::thread::{ThreadBuilder, ThreadId, ThreadInner}; | ||||
| use self::wait::{WaitQueue, Waiter}; | ||||
| @ -27,6 +28,7 @@ pub use self::do_spawn::do_spawn_without_exec; | ||||
| pub use self::do_wait4::idle_reap_zombie_children; | ||||
| pub use self::process::{Process, ProcessFilter, ProcessStatus, IDLE}; | ||||
| pub use self::spawn_attribute::posix_spawnattr_t; | ||||
| pub use self::spawn_attribute::SpawnAttr; | ||||
| pub use self::syscalls::*; | ||||
| pub use self::task::Task; | ||||
| pub use self::term_status::{ForcedExitStatus, TermStatus}; | ||||
| @ -42,6 +44,7 @@ mod do_robust_list; | ||||
| mod do_set_tid_address; | ||||
| mod do_spawn; | ||||
| mod do_wait4; | ||||
| mod pgrp; | ||||
| mod prctl; | ||||
| mod process; | ||||
| mod spawn_attribute; | ||||
| @ -71,3 +74,4 @@ pub type ProcessVMRef = Arc<ProcessVM>; | ||||
| pub type FsViewRef = Arc<RwLock<FsView>>; | ||||
| pub type SchedAgentRef = Arc<SgxMutex<SchedAgent>>; | ||||
| pub type ResourceLimitsRef = Arc<SgxMutex<ResourceLimits>>; | ||||
| pub type ProcessGrpRef = Arc<ProcessGrp>; | ||||
|  | ||||
							
								
								
									
										223
									
								
								src/libos/src/process/pgrp.rs
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										223
									
								
								src/libos/src/process/pgrp.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,223 @@ | ||||
| use super::*; | ||||
| use crate::process; | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| struct PgrpInner { | ||||
|     pgid: pid_t, | ||||
|     process_group: HashMap<pid_t, ProcessRef>, // process id, process ref
 | ||||
|     leader_process: Option<ProcessRef>, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct ProcessGrp { | ||||
|     inner: RwLock<PgrpInner>, | ||||
| } | ||||
| 
 | ||||
| impl ProcessGrp { | ||||
|     pub fn default() -> Self { | ||||
|         ProcessGrp { | ||||
|             inner: RwLock::new(PgrpInner { | ||||
|                 pgid: 0, | ||||
|                 process_group: HashMap::new(), | ||||
|                 leader_process: None, | ||||
|             }), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn pgid(&self) -> pid_t { | ||||
|         self.inner.read().unwrap().pgid | ||||
|     } | ||||
| 
 | ||||
|     pub fn get_process_number(&self) -> usize { | ||||
|         self.inner.read().unwrap().process_group.len() | ||||
|     } | ||||
| 
 | ||||
|     pub fn set_pgid(&self, pgid: pid_t) { | ||||
|         self.inner.write().unwrap().pgid = pgid; | ||||
|     } | ||||
| 
 | ||||
|     pub fn leader_process_is_set(&self) -> bool { | ||||
|         self.inner.read().unwrap().leader_process.is_some() | ||||
|     } | ||||
| 
 | ||||
|     pub fn get_leader_process(&self) -> Option<ProcessRef> { | ||||
|         self.inner.read().unwrap().leader_process.clone() | ||||
|     } | ||||
| 
 | ||||
|     pub fn set_leader_process(&self, new_leader: ProcessRef) { | ||||
|         self.inner.write().unwrap().leader_process = Some(new_leader); | ||||
|     } | ||||
| 
 | ||||
|     pub fn add_new_process(&self, process: ProcessRef) { | ||||
|         self.inner | ||||
|             .write() | ||||
|             .unwrap() | ||||
|             .process_group | ||||
|             .insert(process.pid(), process); | ||||
|     } | ||||
| 
 | ||||
|     pub fn get_all_processes(&self) -> Vec<ProcessRef> { | ||||
|         self.inner | ||||
|             .read() | ||||
|             .unwrap() | ||||
|             .process_group | ||||
|             .values() | ||||
|             .cloned() | ||||
|             .collect() | ||||
|     } | ||||
| 
 | ||||
|     // Create a new process group
 | ||||
|     pub fn new(process: ProcessRef) -> Result<Self> { | ||||
|         let pgrp = Self::default(); | ||||
|         let pid = process.pid(); | ||||
|         pgrp.set_pgid(pid); | ||||
|         pgrp.set_leader_process(process.clone()); | ||||
|         pgrp.add_new_process(process); | ||||
|         Ok(pgrp) | ||||
|     } | ||||
| 
 | ||||
|     // Create a new process group with given pid
 | ||||
|     pub fn new_with_pid(pid: pid_t) -> Result<Self> { | ||||
|         let leader_process = table::get_process(pid)?; | ||||
|         Self::new(leader_process) | ||||
|     } | ||||
| 
 | ||||
|     // Remove process from process group when process exit
 | ||||
|     pub fn remove_process(&self, process: &ProcessRef) -> Result<isize> { | ||||
|         let pgid = self.pgid(); | ||||
|         let leader_process_is_set = self.leader_process_is_set(); | ||||
|         let pgrp_process_num = self.inner.read().unwrap().process_group.len(); | ||||
|         let process_pid = process.pid(); | ||||
| 
 | ||||
|         if pgrp_process_num < 1 { | ||||
|             return_errno!(EINVAL, "This process group is empty"); | ||||
|         } | ||||
| 
 | ||||
|         let leader_process_pid = if leader_process_is_set { | ||||
|             Some(self.get_leader_process().unwrap().pid()) | ||||
|         } else { | ||||
|             None | ||||
|         }; | ||||
| 
 | ||||
|         if pgrp_process_num == 1 { | ||||
|             table::del_pgrp(pgid); | ||||
|         } | ||||
| 
 | ||||
|         { | ||||
|             // Release lock after removing to avoid deadlock
 | ||||
|             let mut process_group_inner = &mut self.inner.write().unwrap().process_group; | ||||
|             process_group_inner | ||||
|                 .remove(&process_pid) | ||||
|                 .ok_or_else(|| errno!(EINVAL, "This process doesn't belong to this pgrp"))?; | ||||
|         } | ||||
| 
 | ||||
|         if leader_process_pid.is_some() && leader_process_pid.unwrap() == process.pid() { | ||||
|             self.inner.write().unwrap().leader_process = None; | ||||
|         } | ||||
|         return Ok(0); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub fn do_getpgid(pid: pid_t) -> Result<pid_t> { | ||||
|     let process = | ||||
|         table::get_process(pid).map_err(|e| errno!(ESRCH, "pid does not match any process"))?; | ||||
|     Ok(process.pgid()) | ||||
| } | ||||
| 
 | ||||
| // do_setpgid can be called under two cases:
 | ||||
| // 1. a running process is calling for itself
 | ||||
| // 2. a parent process is calling for its children
 | ||||
| // Thus, parent can't setpgid to child if it is executing and only can do that when creating
 | ||||
| // it.
 | ||||
| pub fn do_setpgid(pid: pid_t, pgid: pid_t, is_executing: bool) -> Result<isize> { | ||||
|     // If pid is zero, pid is the calling process's pid.
 | ||||
|     let pid = if pid == 0 { do_getpid()? as pid_t } else { pid }; | ||||
| 
 | ||||
|     // If pgid is zero, pgid is made the same as process ID specified by "pid"
 | ||||
|     let pgid = if pgid == 0 { pid } else { pgid }; | ||||
| 
 | ||||
|     debug!("setpgid: pid: {:?}, pgid: {:?}", pid, pgid); | ||||
| 
 | ||||
|     let process = table::get_process(pid)?; | ||||
|     let current_pid = current!().process().pid(); | ||||
| 
 | ||||
|     // if setpgid to a pgroup other than self, the pgroup must exist
 | ||||
|     if pgid != pid && table::get_pgrp(pgid).is_err() { | ||||
|         return_errno!(EPERM, "process group not exist"); | ||||
|     } | ||||
| 
 | ||||
|     // can't setpgid to a running process other than self
 | ||||
|     if current_pid != pid && is_executing { | ||||
|         return_errno!(EACCES, "can't setpgid to a running child process"); | ||||
|     } | ||||
| 
 | ||||
|     if let Ok(pgrp) = table::get_pgrp(pgid) { | ||||
|         // pgrp exists
 | ||||
|         let pgrp_ref = process.pgrp(); | ||||
|         pgrp_ref.remove_process(&process); | ||||
|         process.update_pgrp(pgrp.clone()); | ||||
|         pgrp.add_new_process(process); | ||||
|     } else { | ||||
|         // pgrp not exist
 | ||||
|         if is_executing { | ||||
|             // First remove process from previous process group. New process
 | ||||
|             // setpgid doesn't need this. This is done for self only.
 | ||||
|             debug_assert!(current_pid == pid); | ||||
|             let pgrp_ref = process.pgrp(); | ||||
|             pgrp_ref.remove_process(&process); | ||||
|         } | ||||
| 
 | ||||
|         let pgrp_ref = Arc::new(ProcessGrp::new_with_pid(pid)?); | ||||
|         process.update_pgrp(pgrp_ref.clone()); | ||||
|         table::add_pgrp(pgrp_ref); | ||||
|     } | ||||
|     Ok(0) | ||||
| } | ||||
| 
 | ||||
| pub fn get_spawn_attribute_pgrp(spawn_attributes: Option<SpawnAttr>) -> Result<Option<pid_t>> { | ||||
|     if spawn_attributes.is_some() && spawn_attributes.unwrap().process_group.is_some() { | ||||
|         let pgid = spawn_attributes.unwrap().process_group.unwrap(); | ||||
|         if pgid != 0 && table::get_pgrp(pgid).is_err() { | ||||
|             return_errno!(EPERM, "process group not exist"); | ||||
|         } else { | ||||
|             return Ok(Some(pgid)); | ||||
|         } | ||||
|     } else { | ||||
|         return Ok(None); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // Check process group attribute here before exec.
 | ||||
| // This must be done after process is ready.
 | ||||
| pub fn update_pgrp_for_new_process(new_process_ref: ProcessRef, pgid: Option<pid_t>) -> Result<()> { | ||||
|     if let Some(pgid) = pgid { | ||||
|         if pgid == 0 { | ||||
|             // create a new process group and add self process
 | ||||
|             let pgrp_ref = Arc::new(ProcessGrp::new(new_process_ref.clone())?); | ||||
|             new_process_ref.update_pgrp(pgrp_ref.clone()); | ||||
|             table::add_pgrp(pgrp_ref); | ||||
|         } else { | ||||
|             // pgrp must exist when updating
 | ||||
|             let pgrp = table::get_pgrp(pgid).unwrap(); | ||||
|             new_process_ref.update_pgrp(pgrp.clone()); | ||||
|             pgrp.add_new_process(new_process_ref.clone()); | ||||
|         } | ||||
|     } else { | ||||
|         // By default, new process's process group is same as its parent.
 | ||||
|         let pgrp_ref = new_process_ref.pgrp(); | ||||
|         pgrp_ref.add_new_process(new_process_ref.clone()); | ||||
|     } | ||||
|     debug!("process group:{:?}", new_process_ref.pgrp()); | ||||
|     debug!("non idle process all pgrp: {:?}", table::get_all_pgrp()); | ||||
| 
 | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| pub fn clean_pgrp_when_exit(process: &ProcessRef) { | ||||
|     let pgrp_ref = process.pgrp(); | ||||
| 
 | ||||
|     // Remove process from pgrp
 | ||||
|     pgrp_ref.remove_process(process); | ||||
|     // Remove pgrp info from process
 | ||||
|     process.remove_pgrp(); | ||||
| } | ||||
| @ -1,8 +1,9 @@ | ||||
| use super::super::table; | ||||
| use super::super::task::Task; | ||||
| use super::super::thread::{ThreadBuilder, ThreadId, ThreadName}; | ||||
| use super::super::{ | ||||
|     FileTableRef, ForcedExitStatus, FsViewRef, ProcessRef, ProcessVMRef, ResourceLimitsRef, | ||||
|     SchedAgentRef, | ||||
|     FileTableRef, ForcedExitStatus, FsViewRef, ProcessGrpRef, ProcessRef, ProcessVMRef, | ||||
|     ResourceLimitsRef, SchedAgentRef, | ||||
| }; | ||||
| use super::{Process, ProcessInner}; | ||||
| use crate::fs::FileMode; | ||||
| @ -15,6 +16,7 @@ pub struct ProcessBuilder { | ||||
|     thread_builder: Option<ThreadBuilder>, | ||||
|     // Mandatory fields
 | ||||
|     vm: Option<ProcessVMRef>, | ||||
|     pgrp: Option<ProcessGrpRef>, | ||||
|     // Optional fields, which have reasonable default values
 | ||||
|     exec_path: Option<String>, | ||||
|     umask: Option<FileMode>, | ||||
| @ -30,6 +32,7 @@ impl ProcessBuilder { | ||||
|             tid: None, | ||||
|             thread_builder: Some(thread_builder), | ||||
|             vm: None, | ||||
|             pgrp: None, | ||||
|             exec_path: None, | ||||
|             umask: None, | ||||
|             parent: None, | ||||
| @ -68,6 +71,11 @@ impl ProcessBuilder { | ||||
|         self | ||||
|     } | ||||
| 
 | ||||
|     pub fn pgrp(mut self, pgrp: ProcessGrpRef) -> Self { | ||||
|         self.pgrp = Some(pgrp); | ||||
|         self | ||||
|     } | ||||
| 
 | ||||
|     pub fn task(mut self, task: Task) -> Self { | ||||
|         self.thread_builder(|tb| tb.task(task)) | ||||
|     } | ||||
| @ -118,6 +126,7 @@ impl ProcessBuilder { | ||||
|             let exec_path = self.exec_path.take().unwrap_or_default(); | ||||
|             let umask = RwLock::new(self.umask.unwrap_or(FileMode::default_umask())); | ||||
|             let parent = self.parent.take().map(|parent| RwLock::new(parent)); | ||||
|             let pgrp = RwLock::new(self.pgrp.clone()); | ||||
|             let inner = SgxMutex::new(ProcessInner::new()); | ||||
|             let sig_dispositions = RwLock::new(self.sig_dispositions.unwrap_or_default()); | ||||
|             let sig_queues = RwLock::new(SigQueues::new()); | ||||
| @ -127,6 +136,7 @@ impl ProcessBuilder { | ||||
|                 exec_path, | ||||
|                 umask, | ||||
|                 parent, | ||||
|                 pgrp, | ||||
|                 inner, | ||||
|                 sig_dispositions, | ||||
|                 sig_queues, | ||||
| @ -148,6 +158,13 @@ impl ProcessBuilder { | ||||
|                 .push(new_process.clone()); | ||||
|         } | ||||
| 
 | ||||
|         // Only set leader process and process group id during process building when idle process first time init
 | ||||
|         let pgrp_ref = new_process.pgrp(); | ||||
|         if !pgrp_ref.leader_process_is_set() && pgrp_ref.pgid() == 0 { | ||||
|             pgrp_ref.set_leader_process(new_process.clone()); | ||||
|             pgrp_ref.set_pgid(pid); | ||||
|         } | ||||
| 
 | ||||
|         Ok(new_process) | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -1,3 +1,5 @@ | ||||
| use super::super::pgrp::ProcessGrp; | ||||
| use super::super::table; | ||||
| use super::super::task::Task; | ||||
| use super::super::thread::ThreadId; | ||||
| use super::{ProcessBuilder, ThreadRef}; | ||||
| @ -19,6 +21,7 @@ fn create_idle_thread() -> Result<ThreadRef> { | ||||
|     let dummy_tid = ThreadId::zero(); | ||||
|     let dummy_vm = Arc::new(ProcessVM::default()); | ||||
|     let dummy_task = Task::default(); | ||||
|     let dummy_pgrp = Arc::new(ProcessGrp::default()); | ||||
| 
 | ||||
|     // rlimit get from Occlum.json
 | ||||
|     let rlimits = Arc::new(SgxMutex::new(ResourceLimits::default())); | ||||
| @ -27,11 +30,13 @@ fn create_idle_thread() -> Result<ThreadRef> { | ||||
|     let idle_process = ProcessBuilder::new() | ||||
|         .tid(dummy_tid) | ||||
|         .vm(dummy_vm) | ||||
|         .pgrp(dummy_pgrp) | ||||
|         .task(dummy_task) | ||||
|         .rlimits(rlimits) | ||||
|         .no_parent(true) | ||||
|         .build()?; | ||||
|     debug_assert!(idle_process.pid() == 0); | ||||
|     debug_assert!(idle_process.pgid() == 0); | ||||
| 
 | ||||
|     let idle_thread = idle_process.main_thread().unwrap(); | ||||
|     debug_assert!(idle_thread.tid() == 0); | ||||
| @ -39,5 +44,8 @@ fn create_idle_thread() -> Result<ThreadRef> { | ||||
|     // We do not add the idle process/thread to the process/thread table.
 | ||||
|     // This ensures that the idle process is not accessible from the user space.
 | ||||
| 
 | ||||
|     // Keep process groud 0 in the table
 | ||||
|     table::add_pgrp(idle_process.pgrp()); | ||||
| 
 | ||||
|     Ok(idle_thread) | ||||
| } | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| use std::fmt; | ||||
| 
 | ||||
| use super::wait::WaitQueue; | ||||
| use super::{ForcedExitStatus, ProcessRef, TermStatus, ThreadRef}; | ||||
| use super::{ForcedExitStatus, ProcessGrpRef, ProcessRef, TermStatus, ThreadRef}; | ||||
| use crate::fs::FileMode; | ||||
| use crate::prelude::*; | ||||
| use crate::signal::{SigDispositions, SigNum, SigQueues}; | ||||
| @ -18,6 +18,7 @@ pub struct Process { | ||||
|     exec_path: String, | ||||
|     // Mutable info
 | ||||
|     parent: Option<RwLock<ProcessRef>>, | ||||
|     pgrp: RwLock<Option<ProcessGrpRef>>, | ||||
|     inner: SgxMutex<ProcessInner>, | ||||
|     umask: RwLock<FileMode>, | ||||
|     // Signal
 | ||||
| @ -40,9 +41,8 @@ impl Process { | ||||
|     } | ||||
| 
 | ||||
|     /// Get process group ID
 | ||||
|     // TODO: implement process group
 | ||||
|     pub fn pgid(&self) -> pid_t { | ||||
|         self.pid | ||||
|         self.pgrp().pgid() | ||||
|     } | ||||
| 
 | ||||
|     /// Get the parent process.
 | ||||
| @ -59,6 +59,29 @@ impl Process { | ||||
|             .clone() | ||||
|     } | ||||
| 
 | ||||
|     /// Get the process group.
 | ||||
|     pub fn pgrp(&self) -> ProcessGrpRef { | ||||
|         self.pgrp | ||||
|             .read() | ||||
|             .unwrap() | ||||
|             .as_ref() | ||||
|             // Process must be assigned a process group
 | ||||
|             .unwrap() | ||||
|             .clone() | ||||
|     } | ||||
| 
 | ||||
|     /// Update process group when setpgid is called
 | ||||
|     pub fn update_pgrp(&self, new_pgrp: ProcessGrpRef) { | ||||
|         let mut pgrp = self.pgrp.write().unwrap(); | ||||
|         *pgrp = Some(new_pgrp); | ||||
|     } | ||||
| 
 | ||||
|     /// Remove process group when process exit
 | ||||
|     pub fn remove_pgrp(&self) { | ||||
|         let mut pgrp = self.pgrp.write().unwrap(); | ||||
|         *pgrp = None; | ||||
|     } | ||||
| 
 | ||||
|     /// Get the main thread.
 | ||||
|     ///
 | ||||
|     /// The main thread is a thread whose tid equals to the process's pid.
 | ||||
| @ -199,6 +222,13 @@ impl ProcessInner { | ||||
|         self.children().map(|children| children.len()).unwrap_or(0) | ||||
|     } | ||||
| 
 | ||||
|     pub fn is_child_of(&self, pid: pid_t) -> bool { | ||||
|         match self.children() { | ||||
|             Some(children) => children.iter().find(|&child| child.pid() == pid).is_some(), | ||||
|             None => false, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn threads(&self) -> Option<&Vec<ThreadRef>> { | ||||
|         match self { | ||||
|             Self::Live { threads, .. } => Some(threads), | ||||
| @ -309,6 +339,7 @@ impl fmt::Debug for Process { | ||||
|             .field("pid", &self.pid()) | ||||
|             .field("exec_path", &self.exec_path()) | ||||
|             .field("ppid", &ppid) | ||||
|             .field("pgid", &self.pgid()) | ||||
|             .field("inner", &self.inner()) | ||||
|             .finish() | ||||
|     } | ||||
|  | ||||
| @ -5,7 +5,7 @@ use crate::util::mem_util::from_user::check_ptr; | ||||
| // Note: This is the Rust representation of `posix_spawnattr_t` defined in libc.
 | ||||
| // The name of the elements follow the glibc style. The comments show the name in musl.
 | ||||
| // Elements other than the listed ones are ignored because we don't care for now because
 | ||||
| // Only POSIX_SPAWN_SETSIGDEF and POSIX_SPAWN_SETSIGMASK are supported now.
 | ||||
| // Only POSIX_SPAWN_SETPGROUP, POSIX_SPAWN_SETSIGDEF and POSIX_SPAWN_SETSIGMASK are supported now.
 | ||||
| #[repr(C)] | ||||
| #[derive(Debug)] | ||||
| pub struct posix_spawnattr_t { | ||||
| @ -32,6 +32,7 @@ bitflags! { | ||||
| impl SpawnAttributeFlags { | ||||
|     fn supported(&self) -> bool { | ||||
|         let unsupported_flags = SpawnAttributeFlags::all() | ||||
|             - SpawnAttributeFlags::POSIX_SPAWN_SETPGROUP | ||||
|             - SpawnAttributeFlags::POSIX_SPAWN_SETSIGDEF | ||||
|             - SpawnAttributeFlags::POSIX_SPAWN_SETSIGMASK; | ||||
|         if self.intersects(unsupported_flags) { | ||||
| @ -44,6 +45,7 @@ impl SpawnAttributeFlags { | ||||
| 
 | ||||
| #[derive(Default, Debug, Copy, Clone)] | ||||
| pub struct SpawnAttr { | ||||
|     pub process_group: Option<pid_t>, | ||||
|     pub sig_mask: Option<SigSet>, | ||||
|     pub sig_default: Option<SigSet>, | ||||
| } | ||||
| @ -70,6 +72,12 @@ pub fn clone_spawn_atrributes_safely( | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     if spawn_attr | ||||
|         .flags | ||||
|         .contains(SpawnAttributeFlags::POSIX_SPAWN_SETPGROUP) | ||||
|     { | ||||
|         safe_attr.process_group = Some(spawn_attr.pgrp as pid_t); | ||||
|     } | ||||
|     if spawn_attr | ||||
|         .flags | ||||
|         .contains(SpawnAttributeFlags::POSIX_SPAWN_SETSIGDEF) | ||||
|  | ||||
| @ -5,6 +5,7 @@ use super::do_futex::{FutexFlags, FutexOp, FutexTimeout}; | ||||
| use super::do_robust_list::RobustListHead; | ||||
| use super::do_spawn::FileAction; | ||||
| use super::do_wait4::WaitOptions; | ||||
| use super::pgrp::*; | ||||
| use super::prctl::PrctlCmd; | ||||
| use super::process::ProcessFilter; | ||||
| use super::spawn_attribute::{clone_spawn_atrributes_safely, posix_spawnattr_t, SpawnAttr}; | ||||
| @ -393,11 +394,44 @@ pub fn do_getppid() -> Result<isize> { | ||||
|     Ok(ppid as isize) | ||||
| } | ||||
| 
 | ||||
| pub fn do_getpgid() -> Result<isize> { | ||||
|     let pgid = super::do_getpid::do_getpgid(); | ||||
| pub fn do_getpgrp() -> Result<isize> { | ||||
|     do_getpgid(0) | ||||
| } | ||||
| 
 | ||||
| pub fn do_getpgid(pid: i32) -> Result<isize> { | ||||
|     if pid < 0 { | ||||
|         return_errno!(ESRCH, "process with negative pid is not found"); | ||||
|     } | ||||
| 
 | ||||
|     let real_pid = if pid == 0 { | ||||
|         do_getpid()? as pid_t | ||||
|     } else { | ||||
|         pid as pid_t | ||||
|     }; | ||||
|     let pgid = super::pgrp::do_getpgid(real_pid)?; | ||||
|     Ok(pgid as isize) | ||||
| } | ||||
| 
 | ||||
| pub fn do_setpgid(pid: i32, pgid: i32) -> Result<isize> { | ||||
|     if pgid < 0 { | ||||
|         return_errno!(EINVAL, "pgid can't be negative"); | ||||
|     } | ||||
| 
 | ||||
|     let pid = pid as pid_t; | ||||
|     let pgid = pgid as pid_t; | ||||
|     // Pid should be the calling process or a child of the calling process.
 | ||||
|     let current_pid = current!().process().pid(); | ||||
|     if pid != 0 && pid != current_pid && current!().process().inner().is_child_of(pid) == false { | ||||
|         return_errno!(ESRCH, "pid not calling process or child processes"); | ||||
|     } | ||||
| 
 | ||||
|     // When this function is calling, the process must be executing.
 | ||||
|     let is_executing = true; | ||||
|     let ret = super::pgrp::do_setpgid(pid, pgid, is_executing)?; | ||||
| 
 | ||||
|     Ok(ret) | ||||
| } | ||||
| 
 | ||||
| // TODO: implement uid, gid, euid, egid
 | ||||
| 
 | ||||
| pub fn do_getuid() -> Result<isize> { | ||||
|  | ||||
| @ -1,6 +1,31 @@ | ||||
| use super::{ProcessRef, ThreadRef}; | ||||
| use super::{ProcessGrpRef, ProcessRef, ThreadRef}; | ||||
| use crate::prelude::*; | ||||
| 
 | ||||
| pub fn get_pgrp(pgid: pid_t) -> Result<ProcessGrpRef> { | ||||
|     PROCESSGRP_TABLE.lock().unwrap().get(pgid) | ||||
| } | ||||
| 
 | ||||
| pub(super) fn add_pgrp(pgrp: ProcessGrpRef) -> Result<()> { | ||||
|     PROCESSGRP_TABLE.lock().unwrap().add(pgrp.pgid(), pgrp) | ||||
| } | ||||
| 
 | ||||
| pub(super) fn del_pgrp(pgid: pid_t) -> Result<ProcessGrpRef> { | ||||
|     PROCESSGRP_TABLE.lock().unwrap().del(pgid) | ||||
| } | ||||
| 
 | ||||
| pub fn get_pgrp_number(pgid: pid_t) -> usize { | ||||
|     PROCESSGRP_TABLE.lock().unwrap().len() | ||||
| } | ||||
| 
 | ||||
| pub fn get_all_pgrp() -> Vec<ProcessGrpRef> { | ||||
|     PROCESSGRP_TABLE | ||||
|         .lock() | ||||
|         .unwrap() | ||||
|         .iter() | ||||
|         .map(|(_, pgrp_ref)| pgrp_ref.clone()) | ||||
|         .collect() | ||||
| } | ||||
| 
 | ||||
| pub fn get_process(pid: pid_t) -> Result<ProcessRef> { | ||||
|     PROCESS_TABLE.lock().unwrap().get(pid) | ||||
| } | ||||
| @ -64,6 +89,8 @@ lazy_static! { | ||||
|         { SgxMutex::new(Table::<ProcessRef>::with_capacity(8)) }; | ||||
|     static ref THREAD_TABLE: SgxMutex<Table<ThreadRef>> = | ||||
|         { SgxMutex::new(Table::<ThreadRef>::with_capacity(8)) }; | ||||
|     static ref PROCESSGRP_TABLE: SgxMutex<Table<ProcessGrpRef>> = | ||||
|         { SgxMutex::new(Table::<ProcessGrpRef>::with_capacity(4)) }; | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone)] | ||||
| @ -78,6 +105,10 @@ impl<I: Debug + Clone + Send + Sync> Table<I> { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn len(&self) -> usize { | ||||
|         self.map.len() | ||||
|     } | ||||
| 
 | ||||
|     pub fn iter(&self) -> std::collections::hash_map::Iter<'_, pid_t, I> { | ||||
|         self.map.iter() | ||||
|     } | ||||
|  | ||||
| @ -56,11 +56,8 @@ fn get_processes(filter: &ProcessFilter) -> Result<Vec<ProcessRef>> { | ||||
|             vec![process] | ||||
|         } | ||||
|         ProcessFilter::WithPgid(pgid) => { | ||||
|             // TODO: implement O(1) lookup for a process group
 | ||||
|             let processes: Vec<ProcessRef> = table::get_all_processes() | ||||
|                 .into_iter() | ||||
|                 .filter(|proc_ref| proc_ref.pgid() == *pgid) | ||||
|                 .collect(); | ||||
|             let pgrp = table::get_pgrp(*pgid)?; | ||||
|             let processes = pgrp.get_all_processes(); | ||||
|             if processes.len() == 0 { | ||||
|                 return_errno!(EINVAL, "invalid pgid"); | ||||
|             } | ||||
|  | ||||
| @ -43,10 +43,10 @@ use crate::net::{ | ||||
| }; | ||||
| use crate::process::{ | ||||
|     do_arch_prctl, do_clone, do_execve, do_exit, do_exit_group, do_futex, do_get_robust_list, | ||||
|     do_getegid, do_geteuid, do_getgid, do_getgroups, do_getpgid, do_getpid, do_getppid, do_gettid, | ||||
|     do_getuid, do_prctl, do_set_robust_list, do_set_tid_address, do_spawn_for_glibc, | ||||
|     do_spawn_for_musl, do_wait4, pid_t, posix_spawnattr_t, FdOp, RobustListHead, SpawnFileActions, | ||||
|     ThreadStatus, | ||||
|     do_getegid, do_geteuid, do_getgid, do_getgroups, do_getpgid, do_getpgrp, do_getpid, do_getppid, | ||||
|     do_gettid, do_getuid, do_prctl, do_set_robust_list, do_set_tid_address, do_setpgid, | ||||
|     do_spawn_for_glibc, do_spawn_for_musl, do_wait4, pid_t, posix_spawnattr_t, FdOp, | ||||
|     RobustListHead, SpawnFileActions, ThreadStatus, | ||||
| }; | ||||
| use crate::sched::{do_getcpu, do_sched_getaffinity, do_sched_setaffinity, do_sched_yield}; | ||||
| use crate::signal::{ | ||||
| @ -197,9 +197,9 @@ macro_rules! process_syscall_table_with_callback { | ||||
|             (Setgid = 106) => handle_unsupported(), | ||||
|             (Geteuid = 107) => do_geteuid(), | ||||
|             (Getegid = 108) => do_getegid(), | ||||
|             (Setpgid = 109) => handle_unsupported(), | ||||
|             (Setpgid = 109) => do_setpgid(pid: i32, pgid: i32), | ||||
|             (Getppid = 110) => do_getppid(), | ||||
|             (Getpgrp = 111) => handle_unsupported(), | ||||
|             (Getpgrp = 111) => do_getpgrp(), | ||||
|             (Setsid = 112) => handle_unsupported(), | ||||
|             (Setreuid = 113) => handle_unsupported(), | ||||
|             (Setregid = 114) => handle_unsupported(), | ||||
| @ -209,7 +209,7 @@ macro_rules! process_syscall_table_with_callback { | ||||
|             (Getresuid = 118) => handle_unsupported(), | ||||
|             (Setresgid = 119) => handle_unsupported(), | ||||
|             (Getresgid = 120) => handle_unsupported(), | ||||
|             (Getpgid = 121) => do_getpgid(), | ||||
|             (Getpgid = 121) => do_getpgid(pid: i32), | ||||
|             (Setfsuid = 122) => handle_unsupported(), | ||||
|             (Setfsgid = 123) => handle_unsupported(), | ||||
|             (Getsid = 124) => handle_unsupported(), | ||||
|  | ||||
| @ -19,7 +19,7 @@ TESTS ?= env empty hello_world malloc mmap file fs_perms getpid spawn sched pipe | ||||
| 	truncate readdir mkdir open stat link symlink chmod chown tls pthread system_info resolv_conf rlimit \
 | ||||
| 	server server_epoll unix_socket cout hostfs cpuid rdtsc device sleep exit_group \
 | ||||
| 	ioctl fcntl eventfd emulate_syscall access signal sysinfo prctl rename procfs wait \
 | ||||
| 	spawn_attribute exec statfs umask | ||||
| 	spawn_attribute exec statfs umask pgrp | ||||
| # Benchmarks: need to be compiled and run by bench-% target
 | ||||
| BENCHES := spawn_and_exit_latency pipe_throughput unix_socket_throughput | ||||
| 
 | ||||
|  | ||||
| @ -2,7 +2,7 @@ | ||||
|     "resource_limits": { | ||||
|         "kernel_space_heap_size": "40MB", | ||||
|         "kernel_space_stack_size": "1MB", | ||||
|         "user_space_size": "420MB", | ||||
|         "user_space_size": "500MB", | ||||
|         "max_num_of_threads": 32 | ||||
|     }, | ||||
|     "process": { | ||||
|  | ||||
| @ -5,7 +5,8 @@ | ||||
| #include <sys/syscall.h> | ||||
| 
 | ||||
| int main(int argc, const char *argv[]) { | ||||
|     printf("Run a new process with pid = %d and ppid = %d\n", getpid(), getppid()); | ||||
|     printf("Run a new process with pid = %d, ppid = %d, pgid = %d\n", getpid(), getppid(), | ||||
|            getpgid(0)); | ||||
|     printf("tid = %ld\n", syscall(SYS_gettid)); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
							
								
								
									
										5
									
								
								test/pgrp/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										5
									
								
								test/pgrp/Makefile
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| include ../test_common.mk | ||||
| 
 | ||||
| EXTRA_C_FLAGS := -g | ||||
| EXTRA_LINK_FLAGS := | ||||
| BIN_ARGS := | ||||
							
								
								
									
										304
									
								
								test/pgrp/main.c
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										304
									
								
								test/pgrp/main.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,304 @@ | ||||
| #include <unistd.h> | ||||
| #include <spawn.h> | ||||
| #include <stdlib.h> | ||||
| #include <errno.h> | ||||
| #include <sys/wait.h> | ||||
| #include <assert.h> | ||||
| 
 | ||||
| #include "test.h" | ||||
| 
 | ||||
| // ============================================================================
 | ||||
| // Helper functions
 | ||||
| // ============================================================================
 | ||||
| 
 | ||||
| static void handle_sigsegv(int num) { | ||||
|     printf("SIGSEGV Caught in child with pid = %d, pgid = %d\n", getpid(), getpgid(0)); | ||||
|     assert(num == SIGSEGV); | ||||
|     exit(0); | ||||
| } | ||||
| 
 | ||||
| // Create a child process with different args which will have the pgid specified by `pgid`
 | ||||
| // and the child will sleep and then abort.
 | ||||
| // This new process should be killed to prevent aborting.
 | ||||
| static int create_process_with_pgid(int pgid) { | ||||
|     int ret = 0; | ||||
|     posix_spawnattr_t attr; | ||||
|     int child_pid = 0; | ||||
| 
 | ||||
|     // set child process spawn attribute
 | ||||
|     ret = posix_spawnattr_init(&attr); | ||||
|     if (ret != 0) { | ||||
|         THROW_ERROR("init spawnattr error"); | ||||
|     } | ||||
|     ret = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETPGROUP); | ||||
|     if (ret != 0) { | ||||
|         THROW_ERROR("set attribute flag error"); | ||||
|     } | ||||
|     // child process will have its own process group
 | ||||
|     ret = posix_spawnattr_setpgroup(&attr, pgid); | ||||
|     if (ret != 0) { | ||||
|         THROW_ERROR("set process group attribute error"); | ||||
|     } | ||||
| 
 | ||||
|     int child_argc = 2; // /bin/pgrp again
 | ||||
|     char **child_argv = calloc(1, sizeof(char *) * (child_argc + 1)); | ||||
|     child_argv[0] = strdup("pgrp"); | ||||
|     child_argv[1] = strdup("again"); | ||||
|     ret = posix_spawn(&child_pid, "/bin/pgrp", NULL, &attr, child_argv, NULL); | ||||
|     if (ret < 0) { | ||||
|         THROW_ERROR("ERROR: failed to spawn a child process\n"); | ||||
|     } | ||||
|     printf("Spawn a new proces successfully pid = %d\n", child_pid); | ||||
|     posix_spawnattr_destroy(&attr); | ||||
|     free(child_argv); | ||||
|     return child_pid; | ||||
| } | ||||
| 
 | ||||
| // ============================================================================
 | ||||
| // Test cases for process group
 | ||||
| // ============================================================================
 | ||||
| int test_child_getpgid() { | ||||
|     int ret, child_pid, status; | ||||
|     int pgid = getpgid(0); | ||||
|     int pgrp_id = getpgrp(); | ||||
|     if (pgid != pgrp_id) { | ||||
|         THROW_ERROR("getpgrp error"); | ||||
|     } | ||||
| 
 | ||||
|     printf("Run a parent process with pid = %d, ppid = %d, pgid = %d\n", getpid(), getppid(), | ||||
|            pgid); | ||||
| 
 | ||||
|     ret = posix_spawn(&child_pid, "/bin/getpid", NULL, NULL, NULL, NULL); | ||||
|     if (ret < 0) { | ||||
|         THROW_ERROR("ERROR: failed to spawn a child process\n"); | ||||
|     } | ||||
|     printf("Spawn a child proces successfully with pid = %d\n", child_pid); | ||||
| 
 | ||||
|     // child process group should have same pgid with parent
 | ||||
|     int child_pgid = getpgid(child_pid); | ||||
|     if (child_pgid != pgid) { | ||||
|         THROW_ERROR("child process group error"); | ||||
|     } | ||||
| 
 | ||||
|     ret = wait4(-1, &status, 0, NULL); | ||||
|     if (ret < 0) { | ||||
|         THROW_ERROR("ERROR: failed to wait4 the child process\n"); | ||||
|     } | ||||
|     printf("Child process exited with status = %d\n", status); | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| int test_child_setpgid() { | ||||
|     int ret, child_pid, status; | ||||
| 
 | ||||
|     printf("Parent process: pid = %d, ppid = %d, pgid = %d\n", getpid(), getppid(), | ||||
|            getpgid(0)); | ||||
| 
 | ||||
|     child_pid = create_process_with_pgid(0); | ||||
|     if (child_pid < 0) { | ||||
|         THROW_ERROR("create child process error"); | ||||
|     } | ||||
| 
 | ||||
|     // child pgid should be same as its pid
 | ||||
|     int child_pgid = getpgid(child_pid); | ||||
|     if (child_pgid != child_pid) { | ||||
|         THROW_ERROR("child process group error"); | ||||
|     } | ||||
| 
 | ||||
|     kill(child_pid, SIGSEGV); | ||||
|     ret = wait4(-1, &status, 0, NULL); | ||||
|     if (ret < 0) { | ||||
|         printf("ERROR: failed to wait4 the child process\n"); | ||||
|         return -1; | ||||
|     } | ||||
|     printf("Child process exited with status = %d\n", status); | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| int test_child_setpgid_to_other_child() { | ||||
|     int ret, first_child_pid, second_child_pid, status; | ||||
| 
 | ||||
|     first_child_pid = create_process_with_pgid(0); | ||||
|     if (first_child_pid < 0) { | ||||
|         THROW_ERROR("failed to create first child"); | ||||
|     } | ||||
| 
 | ||||
|     // child pgid should be same as its pid
 | ||||
|     int child_pgid = getpgid(first_child_pid); | ||||
|     printf("first_child_pgid = %d\n", child_pgid); | ||||
|     if (child_pgid != first_child_pid) { | ||||
|         THROW_ERROR("first child process group error"); | ||||
|     } | ||||
| 
 | ||||
|     // add the second child to the first child's process group
 | ||||
|     second_child_pid = create_process_with_pgid(child_pgid); | ||||
|     if (second_child_pid < 0) { | ||||
|         THROW_ERROR("failed to create first child"); | ||||
|     } | ||||
| 
 | ||||
|     // wait for child to run
 | ||||
|     sleep(1); | ||||
| 
 | ||||
|     // second child pgid should be same as the the first child pgid
 | ||||
|     int second_child_pgid = getpgid(second_child_pid); | ||||
|     if (second_child_pgid != child_pgid) { | ||||
|         THROW_ERROR("second child process group error"); | ||||
|     } | ||||
|     kill(0 - second_child_pid, SIGSEGV); | ||||
| 
 | ||||
|     ret = kill(0 - child_pgid, SIGSEGV); | ||||
|     if (ret < 0) { | ||||
|         THROW_ERROR("ERROR: failed to kill process group 1\n"); | ||||
|     } | ||||
| 
 | ||||
|     // wait for all child process to exit
 | ||||
|     while ((ret = wait(&status)) > 0); | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| int test_setpgid_to_running_child() { | ||||
|     int ret, child_pid, status; | ||||
| 
 | ||||
|     ret = posix_spawn(&child_pid, "/bin/getpid", NULL, NULL, NULL, NULL); | ||||
|     if (ret != 0) { | ||||
|         THROW_ERROR("failed to spawn a child process"); | ||||
|     } | ||||
| 
 | ||||
|     // set child pgrp to itself
 | ||||
|     if (setpgid(child_pid, 0) == 0 || errno != EACCES)  { | ||||
|         THROW_ERROR("set child process group error not catching"); | ||||
|     } | ||||
| 
 | ||||
|     ret = wait4(-1, &status, 0, NULL); | ||||
|     if (ret < 0) { | ||||
|         THROW_ERROR("ERROR: failed to wait4 the child process\n"); | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| int test_setpgid_non_existent_pgrp() { | ||||
|     int ret, child_pid; | ||||
|     posix_spawnattr_t attr; | ||||
|     int non_existent_pgid = 10; | ||||
| 
 | ||||
|     // make self process to join a non-existent process group
 | ||||
|     if (setpgid(0, non_existent_pgid) == 0 || errno != EPERM ) { | ||||
|         THROW_ERROR("set self process group error not catching"); | ||||
|     } | ||||
| 
 | ||||
|     // set child process group to a non-existent pgroup
 | ||||
|     ret = posix_spawnattr_init(&attr); | ||||
|     if (ret != 0) { | ||||
|         THROW_ERROR("init spawnattr error"); | ||||
|     } | ||||
|     ret = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETPGROUP); | ||||
|     if (ret != 0) { | ||||
|         THROW_ERROR("set attribute flag error"); | ||||
|     } | ||||
|     ret = posix_spawnattr_setpgroup(&attr, non_existent_pgid); | ||||
|     if (ret != 0) { | ||||
|         THROW_ERROR("set process group attribute error"); | ||||
|     } | ||||
|     ret = posix_spawn(&child_pid, "/bin/getpid", NULL, &attr, NULL, NULL); | ||||
|     if (ret == 0 || errno != EPERM ) { | ||||
|         THROW_ERROR("child process spawn error not catching\n"); | ||||
|     } | ||||
| 
 | ||||
|     //posix_spawn will fail. No need to wait for child.
 | ||||
|     posix_spawnattr_destroy(&attr); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| int test_signal_a_group_of_process() { | ||||
|     printf("current(parent) pid = %d, pgid = %d\n", getpid(), getpgid(0)); | ||||
|     int process_group_1 = getpid(); | ||||
|     int ret, status; | ||||
| 
 | ||||
|     // spawn self with its own process group
 | ||||
|     int child = create_process_with_pgid(0); | ||||
|     if (child < 0) { | ||||
|         THROW_ERROR("failed to create child"); | ||||
|     } | ||||
|     int process_group_2 = child; | ||||
| 
 | ||||
|     // create 2 other children
 | ||||
|     int other_children[2] = {0}; | ||||
|     int child_argc = 2; // /bin/pgrp again
 | ||||
|     char **child_argv = calloc(1, sizeof(char *) * (child_argc + 1)); | ||||
|     child_argv[0] = strdup("pgrp"); | ||||
|     child_argv[1] = strdup("again"); | ||||
|     for (int i = 0; i < 2; i++) { | ||||
|         ret = posix_spawn(&other_children[i], "/bin/pgrp", NULL, NULL, child_argv, NULL); | ||||
|         if (ret < 0) { | ||||
|             THROW_ERROR("ERROR: failed to spawn a child process\n"); | ||||
|         } | ||||
|         printf("spawn other children pid = %d\n", other_children[i]); | ||||
|     } | ||||
|     free(child_argv); | ||||
|     sleep(1); | ||||
| 
 | ||||
|     // make self process to join child's process group
 | ||||
|     if (setpgid(0, process_group_2) < 0) { | ||||
|         THROW_ERROR("join child process group error"); | ||||
|     } | ||||
| 
 | ||||
|     if (getpgid(0) != process_group_2) { | ||||
|         THROW_ERROR("current pgid should be same as child's"); | ||||
|     } | ||||
| 
 | ||||
|     // other children should be in process group 1
 | ||||
|     ret = kill(0 - process_group_1, SIGSEGV); | ||||
|     if (ret < 0) { | ||||
|         THROW_ERROR("ERROR: failed to kill process group 1\n"); | ||||
|     } | ||||
| 
 | ||||
|     // set a process group for self.
 | ||||
|     // setpgrp() == setpgid(0,0)
 | ||||
|     if (setpgrp() < 0) { | ||||
|         THROW_ERROR("join child process group error"); | ||||
|     } | ||||
| 
 | ||||
|     ret = kill(0 - process_group_2, SIGSEGV); | ||||
|     if (ret < 0) { | ||||
|         THROW_ERROR("ERROR: failed to kill process group 2\n"); | ||||
|     } | ||||
| 
 | ||||
|     // wait for all child process to exit
 | ||||
|     while ((ret = wait(&status)) > 0); | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| // ============================================================================
 | ||||
| // Test suite main
 | ||||
| // ============================================================================
 | ||||
| 
 | ||||
| static test_case_t test_cases[] = { | ||||
|     TEST_CASE(test_child_getpgid), | ||||
|     TEST_CASE(test_child_setpgid), | ||||
|     TEST_CASE(test_child_setpgid_to_other_child), | ||||
|     TEST_CASE(test_setpgid_to_running_child), | ||||
|     TEST_CASE(test_setpgid_non_existent_pgrp), | ||||
|     TEST_CASE(test_signal_a_group_of_process), | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| int main(int argc, char **argv) { | ||||
|     if (argc > 1) { | ||||
|         // Spawn self. Do some extra work here.
 | ||||
|         printf("pgrp run again as child with pid = %d, pgid = %d\n", getpid(), getpgid(0)); | ||||
|         signal(SIGSEGV, handle_sigsegv); | ||||
|         sleep(10); | ||||
|         // This shouldn't be reached.
 | ||||
|         abort(); | ||||
|     } | ||||
| 
 | ||||
|     int ret; | ||||
|     ret = test_suite_run(test_cases, ARRAY_SIZE(test_cases)); | ||||
|     return ret; | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user