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