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