yash_env/system/virtual/
process.rs

1// This file is part of yash, an extended POSIX shell.
2// Copyright (C) 2021 WATANABE Yuki
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program.  If not, see <https://www.gnu.org/licenses/>.
16
17//! Processes in a virtual system.
18
19use 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/// Process in a virtual system
49#[derive(Clone, Debug)]
50pub struct Process {
51    /// Process ID of the parent process
52    pub(crate) ppid: Pid,
53
54    /// Process group ID of this process
55    pub(crate) pgid: Pid,
56
57    /// Real user ID of this process
58    uid: Uid,
59
60    /// Effective user ID of this process
61    euid: Uid,
62
63    /// Real group ID of this process
64    gid: Gid,
65
66    /// Effective group ID of this process
67    egid: Gid,
68
69    /// Set of file descriptors open in this process
70    pub(crate) fds: BTreeMap<Fd, FdBody>,
71
72    /// File creation mask
73    pub(crate) umask: Mode,
74
75    /// Working directory path
76    pub(crate) cwd: PathBuf,
77
78    /// Execution state of the process
79    pub(crate) state: ProcessState,
80
81    /// True when `state` has changed but not yet reported to the parent
82    /// process.
83    ///
84    /// The change of `state` is reported when the parent `wait`s for this
85    /// process.
86    state_has_changed: bool,
87
88    /// Wakers waiting for the state of this process to change to `Running`
89    resumption_awaiters: Vec<Weak<Cell<Option<Waker>>>>,
90
91    /// Current signal dispositions
92    ///
93    /// For signals not contained in this hash map, the default disposition is
94    /// assumed.
95    dispositions: HashMap<signal::Number, Disposition>,
96
97    /// Set of blocked signals
98    blocked_signals: BTreeSet<signal::Number>,
99
100    /// Set of pending signals
101    pending_signals: BTreeSet<signal::Number>,
102
103    /// List of signals that have been delivered and caught
104    pub(crate) caught_signals: Vec<signal::Number>,
105
106    /// Limits for system resources
107    pub(crate) resource_limits: HashMap<Resource, LimitPair>,
108
109    /// Weak reference to the `SelectSystem` for this process
110    ///
111    /// This weak reference is empty for the initial process of a
112    /// `VirtualSystem`.  When a new child process is created, a weak reference
113    /// to the `SelectSystem` for the child is set.
114    pub(crate) selector: Weak<RefCell<SelectSystem>>,
115
116    /// Copy of arguments passed to [`execve`](crate::System::execve)
117    pub(crate) last_exec: Option<(CString, Vec<CString>, Vec<CString>)>,
118}
119
120/// Finds the minimum available FD.
121///
122/// The returned FD is the minimum that is equal to or greater than `min` and
123/// not included in `existings`. Items of `existings` must be sorted.
124fn 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    /// Creates a new running process.
141    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    /// Creates a new running process as a child of the given parent.
166    ///
167    /// Some part of the parent process state is copied to the new process.
168    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    /// Returns the process ID of the parent process.
182    #[inline(always)]
183    #[must_use]
184    pub fn ppid(&self) -> Pid {
185        self.ppid
186    }
187
188    /// Returns the process group ID of this process.
189    #[inline(always)]
190    #[must_use]
191    pub fn pgid(&self) -> Pid {
192        self.pgid
193    }
194
195    /// Returns the real user ID of this process.
196    #[inline(always)]
197    #[must_use]
198    pub fn uid(&self) -> Uid {
199        self.uid
200    }
201
202    /// Sets the real user ID of this process.
203    #[inline(always)]
204    pub fn set_uid(&mut self, uid: Uid) {
205        self.uid = uid;
206    }
207
208    /// Returns the effective user ID of this process.
209    #[inline(always)]
210    #[must_use]
211    pub fn euid(&self) -> Uid {
212        self.euid
213    }
214
215    /// Sets the effective user ID of this process.
216    #[inline(always)]
217    pub fn set_euid(&mut self, euid: Uid) {
218        self.euid = euid;
219    }
220
221    /// Returns the real group ID of this process.
222    #[inline(always)]
223    #[must_use]
224    pub fn gid(&self) -> Gid {
225        self.gid
226    }
227
228    /// Sets the real group ID of this process.
229    #[inline(always)]
230    pub fn set_gid(&mut self, gid: Gid) {
231        self.gid = gid;
232    }
233
234    /// Returns the effective group ID of this process.
235    #[inline(always)]
236    #[must_use]
237    pub fn egid(&self) -> Gid {
238        self.egid
239    }
240
241    /// Sets the effective group ID of this process.
242    #[inline(always)]
243    pub fn set_egid(&mut self, egid: Gid) {
244        self.egid = egid;
245    }
246
247    /// Returns FDs open in this process.
248    #[inline(always)]
249    #[must_use]
250    pub fn fds(&self) -> &BTreeMap<Fd, FdBody> {
251        &self.fds
252    }
253
254    /// Returns the body for the given FD.
255    #[inline]
256    #[must_use]
257    pub fn get_fd(&self, fd: Fd) -> Option<&FdBody> {
258        self.fds.get(&fd)
259    }
260
261    /// Returns the body for the given FD.
262    #[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    /// Assigns the given FD to the body.
269    ///
270    /// If successful, returns an `Ok` value containing the previous body for
271    /// the FD. If the FD is equal to or greater than the current soft limit for
272    /// `Resource::NOFILE`, returns `Err(body)`.
273    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    /// Assigns a new FD to the given body.
289    ///
290    /// The new FD will be the minimum unused FD equal to or greater than
291    /// `min_fd`.
292    ///
293    /// If successful, the new FD is returned in `Ok`.
294    /// If no more FD can be opened, returns `Err(body)`.
295    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    /// Assigns a new FD to the given body.
303    ///
304    /// The new FD will be the minimum unused FD, which will be returned as an
305    /// `Ok` value.
306    ///
307    /// If no more FD can be opened, returns `Err(body)`.
308    pub fn open_fd(&mut self, body: FdBody) -> Result<Fd, FdBody> {
309        self.open_fd_ge(Fd(0), body)
310    }
311
312    /// Removes the FD body for the given FD.
313    pub fn close_fd(&mut self, fd: Fd) -> Option<FdBody> {
314        self.fds.remove(&fd)
315    }
316
317    /// Removes all FD bodies in this process.
318    pub fn close_fds(&mut self) {
319        self.fds.clear();
320    }
321
322    /// Returns the working directory path.
323    pub fn getcwd(&self) -> &Path {
324        &self.cwd
325    }
326
327    /// Changes the working directory.
328    ///
329    /// This function does not check if the directory exists and is accessible.
330    pub fn chdir(&mut self, path: PathBuf) {
331        self.cwd = path
332    }
333
334    /// Registers a waker that will be woken up when this process resumes.
335    ///
336    /// The given waker will be woken up when this process is resumed by
337    /// [`set_state`](Self::set_state) or [`raise_signal`](Self::raise_signal).
338    /// A strong reference to the waker must be held by the caller until the
339    /// waker is woken up, when the waker is consumed and the `Cell` content is
340    /// set to `None`.
341    ///
342    /// This function does nothing if the process is not stopped.
343    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    /// Returns the process state.
350    #[inline(always)]
351    #[must_use]
352    pub fn state(&self) -> ProcessState {
353        self.state
354    }
355
356    /// Sets the state of this process.
357    ///
358    /// If the new state is `Exited` or `Signaled`, all file descriptors in this
359    /// process are closed.
360    ///
361    /// This function returns whether the state did change. If true, the
362    /// [`state_has_changed`](Self::state_has_changed) flag is set and the
363    /// caller must notify the state change by sending `SIGCHLD` to the parent
364    /// process.
365    #[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    /// Returns true if a new state has been [set](Self::set_state) but not yet
394    /// [taken](Self::take_state).
395    #[must_use]
396    pub fn state_has_changed(&self) -> bool {
397        self.state_has_changed
398    }
399
400    /// Returns the process state and clears the
401    /// [`state_has_changed`](Self::state_has_changed) flag.
402    pub fn take_state(&mut self) -> ProcessState {
403        self.state_has_changed = false;
404        self.state
405    }
406
407    /// Returns the currently blocked signals.
408    pub fn blocked_signals(&self) -> &BTreeSet<signal::Number> {
409        &self.blocked_signals
410    }
411
412    /// Returns the currently pending signals.
413    ///
414    /// A signal is pending when it has been raised but not yet delivered
415    /// because it is being blocked.
416    pub fn pending_signals(&self) -> &BTreeSet<signal::Number> {
417        &self.pending_signals
418    }
419
420    /// Updates the signal blocking mask for this process.
421    ///
422    /// If this function unblocks a signal, any pending signal is delivered.
423    ///
424    /// If the signal changes the execution state of the process, this function
425    /// returns a `SignalResult` with `process_state_changed` being `true`. In
426    /// that case, the caller must send a SIGCHLD to the parent process of this
427    /// process.
428    #[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    /// Returns the current disposition for a signal.
451    ///
452    /// If no disposition is set for the signal, the default disposition is
453    /// returned.
454    pub fn disposition(&self, number: signal::Number) -> Disposition {
455        self.dispositions.get(&number).copied().unwrap_or_default()
456    }
457
458    /// Gets and sets the disposition for a signal.
459    ///
460    /// This function sets the disposition to the given value and returns the
461    /// previous disposition.
462    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    /// Delivers a signal to this process.
472    ///
473    /// The action taken on the delivery depends on the current signal
474    /// disposition for the signal.
475    ///
476    /// If the signal changes the execution state of the process, this function
477    /// returns a `SignalResult` with `process_state_changed` being `true`. In
478    /// that case, the caller must send a SIGCHLD to the parent process of this
479    /// process.
480    #[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    /// Sends a signal to this process.
523    ///
524    /// If the signal is being blocked, it will remain pending. Otherwise, it is
525    /// immediately delivered.
526    ///
527    /// If the signal changes the execution state of the process, this function
528    /// returns a `SignalResult` with `process_state_changed` being `true`. In
529    /// that case, the caller must send a SIGCHLD to the parent process of this
530    /// process.
531    #[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    /// Returns the arguments to the last call to
551    /// [`execve`](crate::System::execve) on this process.
552    #[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/// Result of operations that may deliver a signal to a process.
560#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
561#[non_exhaustive]
562pub struct SignalResult {
563    /// Whether the signal was delivered to the target process.
564    pub delivered: bool,
565
566    /// Whether the delivered signal was caught by a signal handler.
567    pub caught: bool,
568
569    /// Whether the signal changed the execution status of the target process.
570    ///
571    /// This flag is true when the process was terminated, suspended, or resumed.
572    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        // TODO Check if core dump file has been created
855    }
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}