1use super::Disposition;
20use super::Gid;
21use super::Mode;
22use super::SigmaskOp;
23use super::Uid;
24use super::io::FdBody;
25use super::signal::{self, SignalEffect};
26use crate::io::Fd;
27use crate::job::Pid;
28use crate::job::ProcessResult;
29use crate::job::ProcessState;
30use crate::path::Path;
31use crate::path::PathBuf;
32use crate::system::SelectSystem;
33use crate::system::resource::INFINITY;
34use crate::system::resource::LimitPair;
35use crate::system::resource::Resource;
36use std::cell::Cell;
37use std::cell::RefCell;
38use std::collections::BTreeMap;
39use std::collections::BTreeSet;
40use std::collections::HashMap;
41use std::ffi::CString;
42use std::fmt::Debug;
43use std::ops::BitOr;
44use std::ops::BitOrAssign;
45use std::rc::Weak;
46use std::task::Waker;
47
48#[derive(Clone, Debug)]
50pub struct Process {
51 pub(crate) ppid: Pid,
53
54 pub(crate) pgid: Pid,
56
57 uid: Uid,
59
60 euid: Uid,
62
63 gid: Gid,
65
66 egid: Gid,
68
69 pub(crate) fds: BTreeMap<Fd, FdBody>,
71
72 pub(crate) umask: Mode,
74
75 pub(crate) cwd: PathBuf,
77
78 pub(crate) state: ProcessState,
80
81 state_has_changed: bool,
87
88 resumption_awaiters: Vec<Weak<Cell<Option<Waker>>>>,
90
91 dispositions: HashMap<signal::Number, Disposition>,
96
97 blocked_signals: BTreeSet<signal::Number>,
99
100 pending_signals: BTreeSet<signal::Number>,
102
103 pub(crate) caught_signals: Vec<signal::Number>,
105
106 pub(crate) resource_limits: HashMap<Resource, LimitPair>,
108
109 pub(crate) selector: Weak<RefCell<SelectSystem>>,
115
116 pub(crate) last_exec: Option<(CString, Vec<CString>, Vec<CString>)>,
118}
119
120fn min_unused_fd<'a, I: IntoIterator<Item = &'a Fd>>(min: Fd, existings: I) -> Fd {
125 let candidates = (min.0..).map(Fd);
126 let rejections = existings
127 .into_iter()
128 .skip_while(|fd| **fd < min)
129 .map(Some)
130 .chain(std::iter::repeat(None));
131 candidates
132 .zip(rejections)
133 .skip_while(|(candidate, rejection)| Some(candidate) == *rejection)
134 .map(|(candidate, _rejection)| candidate)
135 .next()
136 .unwrap()
137}
138
139impl Process {
140 pub fn with_parent_and_group(ppid: Pid, pgid: Pid) -> Process {
142 Process {
143 ppid,
144 pgid,
145 uid: Uid(1),
146 euid: Uid(1),
147 gid: Gid(1),
148 egid: Gid(1),
149 fds: BTreeMap::new(),
150 umask: Mode::default(),
151 cwd: PathBuf::new(),
152 state: ProcessState::Running,
153 state_has_changed: false,
154 resumption_awaiters: Vec::new(),
155 dispositions: HashMap::new(),
156 blocked_signals: BTreeSet::new(),
157 pending_signals: BTreeSet::new(),
158 caught_signals: Vec::new(),
159 resource_limits: HashMap::new(),
160 selector: Weak::new(),
161 last_exec: None,
162 }
163 }
164
165 pub fn fork_from(ppid: Pid, parent: &Process) -> Process {
169 let mut child = Self::with_parent_and_group(ppid, parent.pgid);
170 child.uid = parent.uid;
171 child.euid = parent.euid;
172 child.gid = parent.gid;
173 child.egid = parent.egid;
174 child.fds = parent.fds.clone();
175 child.dispositions.clone_from(&parent.dispositions);
176 child.blocked_signals.clone_from(&parent.blocked_signals);
177 child.pending_signals = BTreeSet::new();
178 child
179 }
180
181 #[inline(always)]
183 #[must_use]
184 pub fn ppid(&self) -> Pid {
185 self.ppid
186 }
187
188 #[inline(always)]
190 #[must_use]
191 pub fn pgid(&self) -> Pid {
192 self.pgid
193 }
194
195 #[inline(always)]
197 #[must_use]
198 pub fn uid(&self) -> Uid {
199 self.uid
200 }
201
202 #[inline(always)]
204 pub fn set_uid(&mut self, uid: Uid) {
205 self.uid = uid;
206 }
207
208 #[inline(always)]
210 #[must_use]
211 pub fn euid(&self) -> Uid {
212 self.euid
213 }
214
215 #[inline(always)]
217 pub fn set_euid(&mut self, euid: Uid) {
218 self.euid = euid;
219 }
220
221 #[inline(always)]
223 #[must_use]
224 pub fn gid(&self) -> Gid {
225 self.gid
226 }
227
228 #[inline(always)]
230 pub fn set_gid(&mut self, gid: Gid) {
231 self.gid = gid;
232 }
233
234 #[inline(always)]
236 #[must_use]
237 pub fn egid(&self) -> Gid {
238 self.egid
239 }
240
241 #[inline(always)]
243 pub fn set_egid(&mut self, egid: Gid) {
244 self.egid = egid;
245 }
246
247 #[inline(always)]
249 #[must_use]
250 pub fn fds(&self) -> &BTreeMap<Fd, FdBody> {
251 &self.fds
252 }
253
254 #[inline]
256 #[must_use]
257 pub fn get_fd(&self, fd: Fd) -> Option<&FdBody> {
258 self.fds.get(&fd)
259 }
260
261 #[inline]
263 #[must_use]
264 pub fn get_fd_mut(&mut self, fd: Fd) -> Option<&mut FdBody> {
265 self.fds.get_mut(&fd)
266 }
267
268 pub fn set_fd(&mut self, fd: Fd, body: FdBody) -> Result<Option<FdBody>, FdBody> {
274 let limit = self
275 .resource_limits
276 .get(&Resource::NOFILE)
277 .map(|l| l.soft)
278 .unwrap_or(INFINITY);
279
280 #[allow(clippy::unnecessary_cast)]
281 if limit == INFINITY || (fd.0 as u64) < limit as u64 {
282 Ok(self.fds.insert(fd, body))
283 } else {
284 Err(body)
285 }
286 }
287
288 pub fn open_fd_ge(&mut self, min_fd: Fd, body: FdBody) -> Result<Fd, FdBody> {
296 let fd = min_unused_fd(min_fd, self.fds.keys());
297 let old_body = self.set_fd(fd, body)?;
298 debug_assert_eq!(old_body, None);
299 Ok(fd)
300 }
301
302 pub fn open_fd(&mut self, body: FdBody) -> Result<Fd, FdBody> {
309 self.open_fd_ge(Fd(0), body)
310 }
311
312 pub fn close_fd(&mut self, fd: Fd) -> Option<FdBody> {
314 self.fds.remove(&fd)
315 }
316
317 pub fn close_fds(&mut self) {
319 self.fds.clear();
320 }
321
322 pub fn getcwd(&self) -> &Path {
324 &self.cwd
325 }
326
327 pub fn chdir(&mut self, path: PathBuf) {
331 self.cwd = path
332 }
333
334 pub fn wake_on_resumption(&mut self, waker: Weak<Cell<Option<Waker>>>) {
344 if self.state.is_stopped() {
345 self.resumption_awaiters.push(waker);
346 }
347 }
348
349 #[inline(always)]
351 #[must_use]
352 pub fn state(&self) -> ProcessState {
353 self.state
354 }
355
356 #[must_use = "You must send SIGCHLD to the parent if set_state returns true"]
366 pub fn set_state(&mut self, state: ProcessState) -> bool {
367 let old_state = std::mem::replace(&mut self.state, state);
368
369 if old_state == state {
370 false
371 } else {
372 match state {
373 ProcessState::Running => {
374 for weak in self.resumption_awaiters.drain(..) {
375 if let Some(strong) = weak.upgrade() {
376 if let Some(waker) = strong.take() {
377 waker.wake();
378 }
379 }
380 }
381 }
382 ProcessState::Halted(result) => {
383 if !result.is_stopped() {
384 self.close_fds()
385 }
386 }
387 }
388 self.state_has_changed = true;
389 true
390 }
391 }
392
393 #[must_use]
396 pub fn state_has_changed(&self) -> bool {
397 self.state_has_changed
398 }
399
400 pub fn take_state(&mut self) -> ProcessState {
403 self.state_has_changed = false;
404 self.state
405 }
406
407 pub fn blocked_signals(&self) -> &BTreeSet<signal::Number> {
409 &self.blocked_signals
410 }
411
412 pub fn pending_signals(&self) -> &BTreeSet<signal::Number> {
417 &self.pending_signals
418 }
419
420 #[must_use = "send SIGCHLD if process state has changed"]
429 pub fn block_signals(&mut self, how: SigmaskOp, signals: &[signal::Number]) -> SignalResult {
430 match how {
431 SigmaskOp::Set => self.blocked_signals = signals.iter().copied().collect(),
432 SigmaskOp::Add => self.blocked_signals.extend(signals),
433 SigmaskOp::Remove => {
434 for signal in signals {
435 self.blocked_signals.remove(signal);
436 }
437 }
438 }
439
440 let signals_to_deliver = self.pending_signals.difference(&self.blocked_signals);
441 let signals_to_deliver = signals_to_deliver.copied().collect::<Vec<signal::Number>>();
442 let mut result = SignalResult::default();
443 for signal in signals_to_deliver {
444 self.pending_signals.remove(&signal);
445 result |= self.deliver_signal(signal);
446 }
447 result
448 }
449
450 pub fn disposition(&self, number: signal::Number) -> Disposition {
455 self.dispositions.get(&number).copied().unwrap_or_default()
456 }
457
458 pub fn set_disposition(
463 &mut self,
464 number: signal::Number,
465 disposition: Disposition,
466 ) -> Disposition {
467 let old_disposition = self.dispositions.insert(number, disposition);
468 old_disposition.unwrap_or_default()
469 }
470
471 #[must_use = "send SIGCHLD if process state has changed"]
481 fn deliver_signal(&mut self, signal: signal::Number) -> SignalResult {
482 let disposition = if signal == signal::SIGKILL || signal == signal::SIGSTOP {
483 Disposition::Default
484 } else {
485 self.disposition(signal)
486 };
487
488 match disposition {
489 Disposition::Default => {
490 let name = signal::Name::try_from_raw_virtual(signal.as_raw())
491 .unwrap_or(signal::Name::Sys);
492 let process_state_changed = match SignalEffect::of(name) {
493 SignalEffect::None | SignalEffect::Resume => false,
494 SignalEffect::Terminate { core_dump } => {
495 let result = ProcessResult::Signaled { signal, core_dump };
496 self.set_state(ProcessState::Halted(result))
497 }
498 SignalEffect::Suspend => self.set_state(ProcessState::stopped(signal)),
499 };
500 SignalResult {
501 delivered: true,
502 caught: false,
503 process_state_changed,
504 }
505 }
506 Disposition::Ignore => SignalResult {
507 delivered: true,
508 caught: false,
509 process_state_changed: false,
510 },
511 Disposition::Catch => {
512 self.caught_signals.push(signal);
513 SignalResult {
514 delivered: true,
515 caught: true,
516 process_state_changed: false,
517 }
518 }
519 }
520 }
521
522 #[must_use = "send SIGCHLD if process state has changed"]
532 pub fn raise_signal(&mut self, signal: signal::Number) -> SignalResult {
533 let process_state_changed =
534 signal == signal::SIGCONT && self.set_state(ProcessState::Running);
535
536 let mut result = if signal != signal::SIGKILL
537 && signal != signal::SIGSTOP
538 && self.blocked_signals().contains(&signal)
539 {
540 self.pending_signals.insert(signal);
541 SignalResult::default()
542 } else {
543 self.deliver_signal(signal)
544 };
545
546 result.process_state_changed |= process_state_changed;
547 result
548 }
549
550 #[inline(always)]
553 #[must_use]
554 pub fn last_exec(&self) -> &Option<(CString, Vec<CString>, Vec<CString>)> {
555 &self.last_exec
556 }
557}
558
559#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
561#[non_exhaustive]
562pub struct SignalResult {
563 pub delivered: bool,
565
566 pub caught: bool,
568
569 pub process_state_changed: bool,
573}
574
575impl BitOr for SignalResult {
576 type Output = Self;
577 fn bitor(self, rhs: Self) -> Self::Output {
578 Self {
579 delivered: self.delivered | rhs.delivered,
580 caught: self.caught | rhs.caught,
581 process_state_changed: self.process_state_changed | rhs.process_state_changed,
582 }
583 }
584}
585
586impl BitOrAssign for SignalResult {
587 fn bitor_assign(&mut self, rhs: Self) {
588 *self = *self | rhs;
589 }
590}
591
592#[cfg(test)]
593mod tests {
594 use super::*;
595 use crate::system::r#virtual::file_system::{FileBody, Inode, Mode};
596 use crate::system::r#virtual::io::OpenFileDescription;
597 use enumset::EnumSet;
598 use futures_util::FutureExt;
599 use futures_util::task::LocalSpawnExt;
600 use std::collections::VecDeque;
601 use std::future::poll_fn;
602 use std::rc::Rc;
603 use std::task::Poll;
604
605 #[test]
606 fn min_unused_fd_for_various_arguments() {
607 assert_eq!(min_unused_fd(Fd(0), []), Fd(0));
608 assert_eq!(min_unused_fd(Fd(0), [&Fd(1)]), Fd(0));
609 assert_eq!(min_unused_fd(Fd(0), [&Fd(3), &Fd(4)]), Fd(0));
610 assert_eq!(min_unused_fd(Fd(0), [&Fd(0)]), Fd(1));
611 assert_eq!(min_unused_fd(Fd(0), [&Fd(0), &Fd(2)]), Fd(1));
612 assert_eq!(min_unused_fd(Fd(0), [&Fd(0), &Fd(1)]), Fd(2));
613 assert_eq!(min_unused_fd(Fd(0), [&Fd(0), &Fd(1), &Fd(4)]), Fd(2));
614 assert_eq!(min_unused_fd(Fd(0), [&Fd(0), &Fd(1), &Fd(2)]), Fd(3));
615
616 assert_eq!(min_unused_fd(Fd(1), []), Fd(1));
617 assert_eq!(min_unused_fd(Fd(1), [&Fd(1)]), Fd(2));
618 assert_eq!(min_unused_fd(Fd(1), [&Fd(2)]), Fd(1));
619
620 assert_eq!(min_unused_fd(Fd(1), [&Fd(1), &Fd(3), &Fd(4)]), Fd(2));
621 assert_eq!(min_unused_fd(Fd(2), [&Fd(1), &Fd(3), &Fd(4)]), Fd(2));
622 assert_eq!(min_unused_fd(Fd(3), [&Fd(1), &Fd(3), &Fd(4)]), Fd(5));
623 assert_eq!(min_unused_fd(Fd(4), [&Fd(1), &Fd(3), &Fd(4)]), Fd(5));
624 assert_eq!(min_unused_fd(Fd(5), [&Fd(1), &Fd(3), &Fd(4)]), Fd(5));
625 assert_eq!(min_unused_fd(Fd(6), [&Fd(1), &Fd(3), &Fd(4)]), Fd(6));
626 }
627
628 fn process_with_pipe() -> (Process, Fd, Fd) {
629 let mut process = Process::with_parent_and_group(Pid(10), Pid(11));
630
631 let file = Rc::new(RefCell::new(Inode {
632 body: FileBody::Fifo {
633 content: VecDeque::new(),
634 readers: 1,
635 writers: 1,
636 },
637 permissions: Mode::default(),
638 }));
639 let reader = OpenFileDescription {
640 file: Rc::clone(&file),
641 offset: 0,
642 is_readable: true,
643 is_writable: false,
644 is_appending: false,
645 };
646 let writer = OpenFileDescription {
647 file: Rc::clone(&file),
648 offset: 0,
649 is_readable: false,
650 is_writable: true,
651 is_appending: false,
652 };
653
654 let reader = FdBody {
655 open_file_description: Rc::new(RefCell::new(reader)),
656 flags: EnumSet::empty(),
657 };
658 let writer = FdBody {
659 open_file_description: Rc::new(RefCell::new(writer)),
660 flags: EnumSet::empty(),
661 };
662
663 let reader = process.open_fd(reader).unwrap();
664 let writer = process.open_fd(writer).unwrap();
665 (process, reader, writer)
666 }
667
668 #[test]
669 fn process_set_state_wakes_on_resumed() {
670 let mut process = Process::with_parent_and_group(Pid(1), Pid(2));
671 process.state = ProcessState::stopped(signal::SIGTSTP);
672 let process = Rc::new(RefCell::new(process));
673 let process2 = Rc::clone(&process);
674 let waker = Rc::new(Cell::new(None));
675 let task = poll_fn(move |cx| {
676 let mut process = process2.borrow_mut();
677 if process.state() == ProcessState::Running {
678 return Poll::Ready(());
679 }
680 waker.set(Some(cx.waker().clone()));
681 process.wake_on_resumption(Rc::downgrade(&waker));
682 Poll::Pending
683 });
684
685 let mut executor = futures_executor::LocalPool::new();
686 let mut handle = executor.spawner().spawn_local_with_handle(task).unwrap();
687 executor.run_until_stalled();
688 assert_eq!((&mut handle).now_or_never(), None);
689
690 _ = process.borrow_mut().set_state(ProcessState::Running);
691 assert!(executor.try_run_one());
692 assert_eq!(handle.now_or_never(), Some(()));
693 }
694
695 #[test]
696 fn process_set_state_closes_all_fds_on_exit() {
697 let (mut process, _reader, _writer) = process_with_pipe();
698 assert!(process.set_state(ProcessState::exited(3)));
699 assert!(process.fds().is_empty(), "{:?}", process.fds());
700 }
701
702 #[test]
703 fn process_set_state_closes_all_fds_on_signaled() {
704 let (mut process, _reader, _writer) = process_with_pipe();
705 assert!(
706 process.set_state(ProcessState::Halted(ProcessResult::Signaled {
707 signal: signal::SIGINT,
708 core_dump: false
709 }))
710 );
711 assert!(process.fds().is_empty(), "{:?}", process.fds());
712 }
713
714 #[test]
715 fn process_default_signal_blocking_mask() {
716 let process = Process::with_parent_and_group(Pid(10), Pid(11));
717 let initial_set = process.blocked_signals();
718 assert!(initial_set.is_empty(), "{initial_set:?}");
719 }
720
721 #[test]
722 fn process_sigmask_setmask() {
723 let mut process = Process::with_parent_and_group(Pid(10), Pid(11));
724 let result = process.block_signals(SigmaskOp::Set, &[signal::SIGINT, signal::SIGCHLD]);
725 assert_eq!(result, SignalResult::default());
726
727 let result_set = process.blocked_signals();
728 assert!(result_set.contains(&signal::SIGINT));
729 assert!(result_set.contains(&signal::SIGCHLD));
730 assert_eq!(result_set.len(), 2);
731
732 let result = process.block_signals(SigmaskOp::Set, &[signal::SIGINT, signal::SIGQUIT]);
733 assert_eq!(result, SignalResult::default());
734
735 let result_set = process.blocked_signals();
736 assert!(result_set.contains(&signal::SIGINT));
737 assert!(result_set.contains(&signal::SIGQUIT));
738 assert_eq!(result_set.len(), 2);
739 }
740
741 #[test]
742 fn process_sigmask_block() {
743 let mut process = Process::with_parent_and_group(Pid(10), Pid(11));
744 let result = process.block_signals(SigmaskOp::Add, &[signal::SIGINT, signal::SIGCHLD]);
745 assert_eq!(result, SignalResult::default());
746
747 let result_set = process.blocked_signals();
748 assert!(result_set.contains(&signal::SIGINT));
749 assert!(result_set.contains(&signal::SIGCHLD));
750 assert_eq!(result_set.len(), 2);
751
752 let result = process.block_signals(SigmaskOp::Add, &[signal::SIGINT, signal::SIGQUIT]);
753 assert_eq!(result, SignalResult::default());
754
755 let result_set = process.blocked_signals();
756 assert!(result_set.contains(&signal::SIGINT));
757 assert!(result_set.contains(&signal::SIGQUIT));
758 assert!(result_set.contains(&signal::SIGCHLD));
759 assert_eq!(result_set.len(), 3);
760 }
761
762 #[test]
763 fn process_sigmask_unblock() {
764 let mut process = Process::with_parent_and_group(Pid(10), Pid(11));
765 let result = process.block_signals(SigmaskOp::Add, &[signal::SIGINT, signal::SIGCHLD]);
766 assert_eq!(result, SignalResult::default());
767
768 let result = process.block_signals(SigmaskOp::Remove, &[signal::SIGINT, signal::SIGQUIT]);
769 assert_eq!(result, SignalResult::default());
770
771 let result_set = process.blocked_signals();
772 assert!(result_set.contains(&signal::SIGCHLD));
773 assert_eq!(result_set.len(), 1);
774 }
775
776 #[test]
777 fn process_set_disposition() {
778 let mut process = Process::with_parent_and_group(Pid(100), Pid(11));
779 let old_disposition = process.set_disposition(signal::SIGINT, Disposition::Ignore);
780 assert_eq!(old_disposition, Disposition::Default);
781 let old_disposition = process.set_disposition(signal::SIGTERM, Disposition::Catch);
782 assert_eq!(old_disposition, Disposition::Default);
783
784 let old_disposition = process.set_disposition(signal::SIGINT, Disposition::Default);
785 assert_eq!(old_disposition, Disposition::Ignore);
786 let old_disposition = process.set_disposition(signal::SIGTERM, Disposition::Ignore);
787 assert_eq!(old_disposition, Disposition::Catch);
788
789 let disposition = process.disposition(signal::SIGINT);
790 assert_eq!(disposition, Disposition::Default);
791 let disposition = process.disposition(signal::SIGTERM);
792 assert_eq!(disposition, Disposition::Ignore);
793 let disposition = process.disposition(signal::SIGQUIT);
794 assert_eq!(disposition, Disposition::Default);
795 }
796
797 #[test]
798 fn process_raise_signal_default_nop() {
799 let mut process = Process::with_parent_and_group(Pid(42), Pid(11));
800 let result = process.raise_signal(signal::SIGCHLD);
801 assert_eq!(
802 result,
803 SignalResult {
804 delivered: true,
805 caught: false,
806 process_state_changed: false,
807 }
808 );
809 assert_eq!(process.state(), ProcessState::Running);
810 }
811
812 #[test]
813 fn process_raise_signal_default_terminating() {
814 let mut process = Process::with_parent_and_group(Pid(42), Pid(11));
815 let result = process.raise_signal(signal::SIGTERM);
816 assert_eq!(
817 result,
818 SignalResult {
819 delivered: true,
820 caught: false,
821 process_state_changed: true,
822 }
823 );
824 assert_eq!(
825 process.state(),
826 ProcessState::Halted(ProcessResult::Signaled {
827 signal: signal::SIGTERM,
828 core_dump: false
829 })
830 );
831 assert_eq!(process.caught_signals, []);
832 }
833
834 #[test]
835 fn process_raise_signal_default_aborting() {
836 let mut process = Process::with_parent_and_group(Pid(42), Pid(11));
837 let result = process.raise_signal(signal::SIGABRT);
838 assert_eq!(
839 result,
840 SignalResult {
841 delivered: true,
842 caught: false,
843 process_state_changed: true,
844 }
845 );
846 assert_eq!(
847 process.state(),
848 ProcessState::Halted(ProcessResult::Signaled {
849 signal: signal::SIGABRT,
850 core_dump: true
851 })
852 );
853 assert_eq!(process.caught_signals, []);
854 }
856
857 #[test]
858 fn process_raise_signal_default_stopping() {
859 let mut process = Process::with_parent_and_group(Pid(42), Pid(11));
860 let result = process.raise_signal(signal::SIGTSTP);
861 assert_eq!(
862 result,
863 SignalResult {
864 delivered: true,
865 caught: false,
866 process_state_changed: true,
867 }
868 );
869 assert_eq!(process.state(), ProcessState::stopped(signal::SIGTSTP));
870 assert_eq!(process.caught_signals, []);
871 }
872
873 #[test]
874 fn process_raise_signal_default_continuing() {
875 let mut process = Process::with_parent_and_group(Pid(42), Pid(11));
876 let _ = process.set_state(ProcessState::stopped(signal::SIGTTOU));
877 let result = process.raise_signal(signal::SIGCONT);
878 assert_eq!(
879 result,
880 SignalResult {
881 delivered: true,
882 caught: false,
883 process_state_changed: true,
884 }
885 );
886 assert_eq!(process.state(), ProcessState::Running);
887 assert_eq!(process.caught_signals, []);
888 }
889
890 #[test]
891 fn process_raise_signal_ignored() {
892 let mut process = Process::with_parent_and_group(Pid(42), Pid(11));
893 process.set_disposition(signal::SIGCHLD, Disposition::Ignore);
894 let result = process.raise_signal(signal::SIGCHLD);
895 assert_eq!(
896 result,
897 SignalResult {
898 delivered: true,
899 caught: false,
900 process_state_changed: false,
901 }
902 );
903 assert_eq!(process.state(), ProcessState::Running);
904 assert_eq!(process.caught_signals, []);
905 }
906
907 #[test]
908 fn process_raise_signal_ignored_and_blocked_sigcont() {
909 let mut process = Process::with_parent_and_group(Pid(42), Pid(11));
910 let _ = process.set_state(ProcessState::stopped(signal::SIGTTOU));
911 let _ = process.set_disposition(signal::SIGCONT, Disposition::Ignore);
912 let _ = process.block_signals(SigmaskOp::Add, &[signal::SIGCONT]);
913 let result = process.raise_signal(signal::SIGCONT);
914 assert_eq!(
915 result,
916 SignalResult {
917 delivered: false,
918 caught: false,
919 process_state_changed: true,
920 }
921 );
922 assert_eq!(process.state(), ProcessState::Running);
923 assert_eq!(process.caught_signals, []);
924 assert!(process.pending_signals.contains(&signal::SIGCONT));
925 }
926
927 #[test]
928 fn process_raise_signal_caught() {
929 let mut process = Process::with_parent_and_group(Pid(42), Pid(11));
930 process.set_disposition(signal::SIGCHLD, Disposition::Catch);
931 let result = process.raise_signal(signal::SIGCHLD);
932 assert_eq!(
933 result,
934 SignalResult {
935 delivered: true,
936 caught: true,
937 process_state_changed: false,
938 }
939 );
940 assert_eq!(process.state(), ProcessState::Running);
941 assert_eq!(process.caught_signals, [signal::SIGCHLD]);
942 }
943
944 #[test]
945 fn process_raise_signal_blocked() {
946 let mut process = Process::with_parent_and_group(Pid(42), Pid(11));
947 process.set_disposition(signal::SIGCHLD, Disposition::Catch);
948 let result = process.block_signals(SigmaskOp::Add, &[signal::SIGCHLD]);
949 assert_eq!(
950 result,
951 SignalResult {
952 delivered: false,
953 caught: false,
954 process_state_changed: false,
955 }
956 );
957
958 let result = process.raise_signal(signal::SIGCHLD);
959 assert_eq!(
960 result,
961 SignalResult {
962 delivered: false,
963 caught: false,
964 process_state_changed: false,
965 }
966 );
967 assert_eq!(process.state(), ProcessState::Running);
968 assert_eq!(process.caught_signals, []);
969
970 let result = process.block_signals(SigmaskOp::Set, &[]);
971 assert_eq!(
972 result,
973 SignalResult {
974 delivered: true,
975 caught: true,
976 process_state_changed: false,
977 }
978 );
979 assert_eq!(process.state(), ProcessState::Running);
980 assert_eq!(process.caught_signals, [signal::SIGCHLD]);
981 }
982}