Skip to main content

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::WakerSet;
25use super::io::FdBody;
26use super::signal::{self, SignalEffect};
27use crate::io::Fd;
28use crate::job::Pid;
29use crate::job::ProcessResult;
30use crate::job::ProcessState;
31use crate::path::Path;
32use crate::path::PathBuf;
33use crate::system::resource::INFINITY;
34use crate::system::resource::LimitPair;
35use crate::system::resource::Resource;
36use std::cell::Cell;
37use std::collections::BTreeMap;
38use std::collections::BTreeSet;
39use std::collections::HashMap;
40use std::ffi::CString;
41use std::fmt::Debug;
42use std::ops::BitOr;
43use std::ops::BitOrAssign;
44use std::rc::Weak;
45use std::task::Waker;
46
47/// Process in a virtual system
48#[derive(Clone, derive_more::Debug)]
49pub struct Process {
50    /// Process ID of the parent process
51    pub(crate) ppid: Pid,
52
53    /// Process group ID of this process
54    pub(crate) pgid: Pid,
55
56    /// Real user ID of this process
57    uid: Uid,
58
59    /// Effective user ID of this process
60    euid: Uid,
61
62    /// Real group ID of this process
63    gid: Gid,
64
65    /// Effective group ID of this process
66    egid: Gid,
67
68    /// Set of file descriptors open in this process
69    pub(crate) fds: BTreeMap<Fd, FdBody>,
70
71    /// File creation mask
72    pub(crate) umask: Mode,
73
74    /// Working directory path
75    pub(crate) cwd: PathBuf,
76
77    /// Execution state of the process
78    pub(crate) state: ProcessState,
79
80    /// True when `state` has changed but not yet reported to the parent
81    /// process.
82    ///
83    /// The change of `state` is reported when the parent `wait`s for this
84    /// process.
85    state_has_changed: bool,
86
87    /// Wakers waiting for the state of this process to change to `Running`
88    resumption_awaiters: WakerSet,
89
90    /// Current signal dispositions
91    ///
92    /// For signals not contained in this hash map, the default disposition is
93    /// assumed.
94    dispositions: HashMap<signal::Number, Disposition>,
95
96    /// Set of blocked signals
97    blocked_signals: BTreeSet<signal::Number>,
98
99    /// Set of pending signals
100    pending_signals: BTreeSet<signal::Number>,
101
102    /// List of signals that have been delivered and caught
103    pub(crate) caught_signals: Vec<signal::Number>,
104
105    /// Monotonically increasing count of signals that have been caught
106    ///
107    /// Unlike [`caught_signals`](Self::caught_signals), this counter is never
108    /// reset. It is incremented each time a signal is caught, regardless of
109    /// whether the signal has been consumed from `caught_signals`.
110    pub(crate) caught_signals_count: usize,
111
112    /// Wakers of tasks waiting for signals to be delivered to this process
113    ///
114    /// When a signal is delivered to this process, all wakers in this set are
115    /// woken up and the set is cleared. When a task is waiting for multiple
116    /// signals, only one waker is added to the set and will be woken up when
117    /// any of the signals is delivered.
118    signal_wakers: WakerSet,
119
120    /// Limits for system resources
121    pub(crate) resource_limits: HashMap<Resource, LimitPair>,
122
123    /// Copy of arguments passed to [`execve`](crate::System::execve)
124    pub(crate) last_exec: Option<(CString, Vec<CString>, Vec<CString>)>,
125}
126
127/// Finds the minimum available FD.
128///
129/// The returned FD is the minimum that is equal to or greater than `min` and
130/// not included in `existings`. Items of `existings` must be sorted.
131fn min_unused_fd<'a, I: IntoIterator<Item = &'a Fd>>(min: Fd, existings: I) -> Fd {
132    let candidates = (min.0..).map(Fd);
133    let rejections = existings
134        .into_iter()
135        .skip_while(|fd| **fd < min)
136        .map(Some)
137        .chain(std::iter::repeat(None));
138    candidates
139        .zip(rejections)
140        .skip_while(|(candidate, rejection)| Some(candidate) == *rejection)
141        .map(|(candidate, _rejection)| candidate)
142        .next()
143        .unwrap()
144}
145
146impl Process {
147    /// Creates a new running process.
148    pub fn with_parent_and_group(ppid: Pid, pgid: Pid) -> Process {
149        Process {
150            ppid,
151            pgid,
152            uid: Uid(1),
153            euid: Uid(1),
154            gid: Gid(1),
155            egid: Gid(1),
156            fds: BTreeMap::new(),
157            umask: Mode::default(),
158            cwd: PathBuf::new(),
159            state: ProcessState::Running,
160            state_has_changed: false,
161            resumption_awaiters: WakerSet::new(),
162            dispositions: HashMap::new(),
163            blocked_signals: BTreeSet::new(),
164            pending_signals: BTreeSet::new(),
165            caught_signals: Vec::new(),
166            caught_signals_count: 0,
167            signal_wakers: WakerSet::new(),
168            resource_limits: HashMap::new(),
169            last_exec: None,
170        }
171    }
172
173    /// Creates a new running process as a child of the given parent.
174    ///
175    /// Some part of the parent process state is copied to the new process.
176    pub fn fork_from(ppid: Pid, parent: &Process) -> Process {
177        let mut child = Self::with_parent_and_group(ppid, parent.pgid);
178        child.uid = parent.uid;
179        child.euid = parent.euid;
180        child.gid = parent.gid;
181        child.egid = parent.egid;
182        child.fds = parent.fds.clone();
183        child.dispositions.clone_from(&parent.dispositions);
184        child.blocked_signals.clone_from(&parent.blocked_signals);
185        child
186    }
187
188    /// Returns the process ID of the parent process.
189    #[inline(always)]
190    #[must_use]
191    pub fn ppid(&self) -> Pid {
192        self.ppid
193    }
194
195    /// Returns the process group ID of this process.
196    #[inline(always)]
197    #[must_use]
198    pub fn pgid(&self) -> Pid {
199        self.pgid
200    }
201
202    /// Returns the real user ID of this process.
203    #[inline(always)]
204    #[must_use]
205    pub fn uid(&self) -> Uid {
206        self.uid
207    }
208
209    /// Sets the real user ID of this process.
210    #[inline(always)]
211    pub fn set_uid(&mut self, uid: Uid) {
212        self.uid = uid;
213    }
214
215    /// Returns the effective user ID of this process.
216    #[inline(always)]
217    #[must_use]
218    pub fn euid(&self) -> Uid {
219        self.euid
220    }
221
222    /// Sets the effective user ID of this process.
223    #[inline(always)]
224    pub fn set_euid(&mut self, euid: Uid) {
225        self.euid = euid;
226    }
227
228    /// Returns the real group ID of this process.
229    #[inline(always)]
230    #[must_use]
231    pub fn gid(&self) -> Gid {
232        self.gid
233    }
234
235    /// Sets the real group ID of this process.
236    #[inline(always)]
237    pub fn set_gid(&mut self, gid: Gid) {
238        self.gid = gid;
239    }
240
241    /// Returns the effective group ID of this process.
242    #[inline(always)]
243    #[must_use]
244    pub fn egid(&self) -> Gid {
245        self.egid
246    }
247
248    /// Sets the effective group ID of this process.
249    #[inline(always)]
250    pub fn set_egid(&mut self, egid: Gid) {
251        self.egid = egid;
252    }
253
254    /// Returns FDs open in this process.
255    #[inline(always)]
256    #[must_use]
257    pub fn fds(&self) -> &BTreeMap<Fd, FdBody> {
258        &self.fds
259    }
260
261    /// Returns the body for the given FD.
262    #[inline]
263    #[must_use]
264    pub fn get_fd(&self, fd: Fd) -> Option<&FdBody> {
265        self.fds.get(&fd)
266    }
267
268    /// Returns the body for the given FD.
269    #[inline]
270    #[must_use]
271    pub fn get_fd_mut(&mut self, fd: Fd) -> Option<&mut FdBody> {
272        self.fds.get_mut(&fd)
273    }
274
275    /// Assigns the given FD to the body.
276    ///
277    /// If successful, returns an `Ok` value containing the previous body for
278    /// the FD. If the FD is equal to or greater than the current soft limit for
279    /// `Resource::NOFILE`, returns `Err(body)`.
280    pub fn set_fd(&mut self, fd: Fd, body: FdBody) -> Result<Option<FdBody>, FdBody> {
281        let limit = self
282            .resource_limits
283            .get(&Resource::NOFILE)
284            .map(|l| l.soft)
285            .unwrap_or(INFINITY);
286
287        #[allow(clippy::unnecessary_cast)]
288        if limit == INFINITY || (fd.0 as u64) < limit as u64 {
289            Ok(self.fds.insert(fd, body))
290        } else {
291            Err(body)
292        }
293    }
294
295    /// Assigns a new FD to the given body.
296    ///
297    /// The new FD will be the minimum unused FD equal to or greater than
298    /// `min_fd`.
299    ///
300    /// If successful, the new FD is returned in `Ok`.
301    /// If no more FD can be opened, returns `Err(body)`.
302    pub fn open_fd_ge(&mut self, min_fd: Fd, body: FdBody) -> Result<Fd, FdBody> {
303        let fd = min_unused_fd(min_fd, self.fds.keys());
304        let old_body = self.set_fd(fd, body)?;
305        debug_assert_eq!(old_body, None);
306        Ok(fd)
307    }
308
309    /// Assigns a new FD to the given body.
310    ///
311    /// The new FD will be the minimum unused FD, which will be returned as an
312    /// `Ok` value.
313    ///
314    /// If no more FD can be opened, returns `Err(body)`.
315    pub fn open_fd(&mut self, body: FdBody) -> Result<Fd, FdBody> {
316        self.open_fd_ge(Fd(0), body)
317    }
318
319    /// Removes the FD body for the given FD.
320    pub fn close_fd(&mut self, fd: Fd) -> Option<FdBody> {
321        self.fds.remove(&fd)
322    }
323
324    /// Removes all FD bodies in this process.
325    pub fn close_fds(&mut self) {
326        self.fds.clear();
327    }
328
329    /// Returns the working directory path.
330    pub fn getcwd(&self) -> &Path {
331        &self.cwd
332    }
333
334    /// Changes the working directory.
335    ///
336    /// This function does not check if the directory exists and is accessible.
337    pub fn chdir(&mut self, path: PathBuf) {
338        self.cwd = path
339    }
340
341    /// Registers a waker that will be woken up when this process resumes.
342    ///
343    /// The given waker will be woken up when this process is resumed by
344    /// [`set_state`](Self::set_state) or [`raise_signal`](Self::raise_signal).
345    /// A strong reference to the waker must be held by the caller until the
346    /// waker is woken up, when the waker is consumed and the `Cell` content is
347    /// set to `None`.
348    ///
349    /// This function does nothing if the process is not stopped.
350    pub fn wake_on_resumption(&mut self, waker: Weak<Cell<Option<Waker>>>) {
351        if self.state.is_stopped() {
352            self.resumption_awaiters.insert(waker);
353        }
354    }
355
356    /// Returns the process state.
357    #[inline(always)]
358    #[must_use]
359    pub fn state(&self) -> ProcessState {
360        self.state
361    }
362
363    /// Sets the state of this process.
364    ///
365    /// If the new state is `Exited` or `Signaled`, all file descriptors in this
366    /// process are closed.
367    ///
368    /// This function returns whether the state did change. If true, the
369    /// [`state_has_changed`](Self::state_has_changed) flag is set and the
370    /// caller must notify the state change by sending `SIGCHLD` to the parent
371    /// process.
372    #[must_use = "You must send SIGCHLD to the parent if set_state returns true"]
373    pub fn set_state(&mut self, state: ProcessState) -> bool {
374        let old_state = std::mem::replace(&mut self.state, state);
375
376        if old_state == state {
377            false
378        } else {
379            match state {
380                ProcessState::Running => self.resumption_awaiters.wake_all(),
381                ProcessState::Halted(result) => {
382                    if !result.is_stopped() {
383                        self.close_fds()
384                    }
385                }
386            }
387            self.state_has_changed = true;
388            true
389        }
390    }
391
392    /// Returns true if a new state has been [set](Self::set_state) but not yet
393    /// [taken](Self::take_state).
394    #[must_use]
395    pub fn state_has_changed(&self) -> bool {
396        self.state_has_changed
397    }
398
399    /// Returns the process state and clears the
400    /// [`state_has_changed`](Self::state_has_changed) flag.
401    pub fn take_state(&mut self) -> ProcessState {
402        self.state_has_changed = false;
403        self.state
404    }
405
406    /// Returns the currently blocked signals.
407    pub fn blocked_signals(&self) -> &BTreeSet<signal::Number> {
408        &self.blocked_signals
409    }
410
411    /// Returns the currently pending signals.
412    ///
413    /// A signal is pending when it has been raised but not yet delivered
414    /// because it is being blocked.
415    pub fn pending_signals(&self) -> &BTreeSet<signal::Number> {
416        &self.pending_signals
417    }
418
419    /// Updates the signal blocking mask for this process.
420    ///
421    /// If this function unblocks a signal, any pending signal is delivered.
422    ///
423    /// If the signal changes the execution state of the process, this function
424    /// returns a `SignalResult` with `process_state_changed` being `true`. In
425    /// that case, the caller must send a SIGCHLD to the parent process of this
426    /// process.
427    #[must_use = "send SIGCHLD if process state has changed"]
428    pub fn block_signals(&mut self, how: SigmaskOp, signals: &[signal::Number]) -> SignalResult {
429        match how {
430            SigmaskOp::Set => self.blocked_signals = signals.iter().copied().collect(),
431            SigmaskOp::Add => self.blocked_signals.extend(signals),
432            SigmaskOp::Remove => {
433                for signal in signals {
434                    self.blocked_signals.remove(signal);
435                }
436            }
437        }
438
439        let signals_to_deliver = self.pending_signals.difference(&self.blocked_signals);
440        let signals_to_deliver = signals_to_deliver.copied().collect::<Vec<signal::Number>>();
441        let mut result = SignalResult::default();
442        for signal in signals_to_deliver {
443            self.pending_signals.remove(&signal);
444            result |= self.deliver_signal(signal);
445        }
446        result
447    }
448
449    /// Returns the current disposition for a signal.
450    ///
451    /// If no disposition is set for the signal, the default disposition is
452    /// returned.
453    pub fn disposition(&self, number: signal::Number) -> Disposition {
454        self.dispositions.get(&number).copied().unwrap_or_default()
455    }
456
457    /// Gets and sets the disposition for a signal.
458    ///
459    /// This function sets the disposition to the given value and returns the
460    /// previous disposition.
461    pub fn set_disposition(
462        &mut self,
463        number: signal::Number,
464        disposition: Disposition,
465    ) -> Disposition {
466        let old_disposition = self.dispositions.insert(number, disposition);
467        old_disposition.unwrap_or_default()
468    }
469
470    /// Registers a waker that will be woken up when a signal is delivered to this process.
471    #[inline(always)]
472    pub(crate) fn register_signal_waker(&mut self, waker: Weak<Cell<Option<Waker>>>) {
473        self.signal_wakers.insert(waker);
474    }
475
476    /// Delivers a signal to this process.
477    ///
478    /// The action taken on the delivery depends on the current signal
479    /// disposition for the signal.
480    ///
481    /// If the signal changes the execution state of the process, this function
482    /// returns a `SignalResult` with `process_state_changed` being `true`. In
483    /// that case, the caller must send a SIGCHLD to the parent process of this
484    /// process.
485    #[must_use = "send SIGCHLD if process state has changed"]
486    fn deliver_signal(&mut self, signal: signal::Number) -> SignalResult {
487        let disposition = if signal == signal::SIGKILL || signal == signal::SIGSTOP {
488            Disposition::Default
489        } else {
490            self.disposition(signal)
491        };
492
493        match disposition {
494            Disposition::Default => {
495                let name = signal::Name::try_from_raw_virtual(signal.as_raw())
496                    .unwrap_or(signal::Name::Sys);
497                let process_state_changed = match SignalEffect::of(name) {
498                    SignalEffect::None | SignalEffect::Resume => false,
499                    SignalEffect::Terminate { core_dump } => {
500                        let result = ProcessResult::Signaled { signal, core_dump };
501                        self.set_state(ProcessState::Halted(result))
502                    }
503                    SignalEffect::Suspend => self.set_state(ProcessState::stopped(signal)),
504                };
505                SignalResult {
506                    delivered: true,
507                    caught: false,
508                    process_state_changed,
509                }
510            }
511            Disposition::Ignore => SignalResult {
512                delivered: true,
513                caught: false,
514                process_state_changed: false,
515            },
516            Disposition::Catch => {
517                self.caught_signals.push(signal);
518                self.caught_signals_count += 1;
519                self.signal_wakers.wake_all();
520                SignalResult {
521                    delivered: true,
522                    caught: true,
523                    process_state_changed: false,
524                }
525            }
526        }
527    }
528
529    /// Sends a signal to this process.
530    ///
531    /// If the signal is being blocked, it will remain pending. Otherwise, it is
532    /// immediately delivered.
533    ///
534    /// If the signal changes the execution state of the process, this function
535    /// returns a `SignalResult` with `process_state_changed` being `true`. In
536    /// that case, the caller must send a SIGCHLD to the parent process of this
537    /// process.
538    #[must_use = "send SIGCHLD if process state has changed"]
539    pub fn raise_signal(&mut self, signal: signal::Number) -> SignalResult {
540        let process_state_changed =
541            signal == signal::SIGCONT && self.set_state(ProcessState::Running);
542
543        let mut result = if signal != signal::SIGKILL
544            && signal != signal::SIGSTOP
545            && self.blocked_signals().contains(&signal)
546        {
547            self.pending_signals.insert(signal);
548            SignalResult::default()
549        } else {
550            self.deliver_signal(signal)
551        };
552
553        result.process_state_changed |= process_state_changed;
554        result
555    }
556
557    /// Returns the arguments to the last call to
558    /// [`execve`](crate::system::Exec::execve) on this process.
559    #[inline(always)]
560    #[must_use]
561    pub fn last_exec(&self) -> &Option<(CString, Vec<CString>, Vec<CString>)> {
562        &self.last_exec
563    }
564}
565
566/// Result of operations that may deliver a signal to a process.
567#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
568#[non_exhaustive]
569pub struct SignalResult {
570    /// Whether the signal was delivered to the target process.
571    pub delivered: bool,
572
573    /// Whether the delivered signal was caught by a signal handler.
574    pub caught: bool,
575
576    /// Whether the signal changed the execution status of the target process.
577    ///
578    /// This flag is true when the process was terminated, suspended, or resumed.
579    pub process_state_changed: bool,
580}
581
582impl BitOr for SignalResult {
583    type Output = Self;
584    fn bitor(self, rhs: Self) -> Self::Output {
585        Self {
586            delivered: self.delivered | rhs.delivered,
587            caught: self.caught | rhs.caught,
588            process_state_changed: self.process_state_changed | rhs.process_state_changed,
589        }
590    }
591}
592
593impl BitOrAssign for SignalResult {
594    fn bitor_assign(&mut self, rhs: Self) {
595        *self = *self | rhs;
596    }
597}
598
599#[cfg(test)]
600mod tests {
601    use super::*;
602    use crate::system::file_system::Mode;
603    use crate::system::r#virtual::file_body::FileBody;
604    use crate::system::r#virtual::file_system::Inode;
605    use crate::system::r#virtual::io::OpenFileDescription;
606    use crate::test_helper::WakeFlag;
607    use enumset::EnumSet;
608    use futures_util::FutureExt;
609    use futures_util::task::LocalSpawnExt;
610    use std::cell::RefCell;
611    use std::collections::VecDeque;
612    use std::future::poll_fn;
613    use std::rc::Rc;
614    use std::sync::Arc;
615    use std::task::Poll;
616
617    #[test]
618    fn min_unused_fd_for_various_arguments() {
619        assert_eq!(min_unused_fd(Fd(0), []), Fd(0));
620        assert_eq!(min_unused_fd(Fd(0), [&Fd(1)]), Fd(0));
621        assert_eq!(min_unused_fd(Fd(0), [&Fd(3), &Fd(4)]), Fd(0));
622        assert_eq!(min_unused_fd(Fd(0), [&Fd(0)]), Fd(1));
623        assert_eq!(min_unused_fd(Fd(0), [&Fd(0), &Fd(2)]), Fd(1));
624        assert_eq!(min_unused_fd(Fd(0), [&Fd(0), &Fd(1)]), Fd(2));
625        assert_eq!(min_unused_fd(Fd(0), [&Fd(0), &Fd(1), &Fd(4)]), Fd(2));
626        assert_eq!(min_unused_fd(Fd(0), [&Fd(0), &Fd(1), &Fd(2)]), Fd(3));
627
628        assert_eq!(min_unused_fd(Fd(1), []), Fd(1));
629        assert_eq!(min_unused_fd(Fd(1), [&Fd(1)]), Fd(2));
630        assert_eq!(min_unused_fd(Fd(1), [&Fd(2)]), Fd(1));
631
632        assert_eq!(min_unused_fd(Fd(1), [&Fd(1), &Fd(3), &Fd(4)]), Fd(2));
633        assert_eq!(min_unused_fd(Fd(2), [&Fd(1), &Fd(3), &Fd(4)]), Fd(2));
634        assert_eq!(min_unused_fd(Fd(3), [&Fd(1), &Fd(3), &Fd(4)]), Fd(5));
635        assert_eq!(min_unused_fd(Fd(4), [&Fd(1), &Fd(3), &Fd(4)]), Fd(5));
636        assert_eq!(min_unused_fd(Fd(5), [&Fd(1), &Fd(3), &Fd(4)]), Fd(5));
637        assert_eq!(min_unused_fd(Fd(6), [&Fd(1), &Fd(3), &Fd(4)]), Fd(6));
638    }
639
640    fn process_with_pipe() -> (Process, Fd, Fd) {
641        let mut process = Process::with_parent_and_group(Pid(10), Pid(11));
642
643        let file = Rc::new(RefCell::new(Inode {
644            body: FileBody::Fifo {
645                content: VecDeque::new(),
646                readers: 0,
647                writers: 0,
648                pending_open_wakers: WakerSet::new(),
649                pending_read_wakers: WakerSet::new(),
650                pending_write_wakers: WakerSet::new(),
651            },
652            permissions: Mode::default(),
653        }));
654        let reader = OpenFileDescription::new(
655            Rc::clone(&file),
656            /* offset = */ 0,
657            /* is_readable = */ true,
658            /* is_writable = */ false,
659            /* is_appending = */ false,
660            /* is_nonblocking = */ false,
661        );
662        let writer = OpenFileDescription::new(
663            Rc::clone(&file),
664            /* offset = */ 0,
665            /* is_readable = */ false,
666            /* is_writable = */ true,
667            /* is_appending = */ false,
668            /* is_nonblocking = */ false,
669        );
670
671        let reader = FdBody {
672            open_file_description: Rc::new(RefCell::new(reader)),
673            flags: EnumSet::empty(),
674        };
675        let writer = FdBody {
676            open_file_description: Rc::new(RefCell::new(writer)),
677            flags: EnumSet::empty(),
678        };
679
680        let reader = process.open_fd(reader).unwrap();
681        let writer = process.open_fd(writer).unwrap();
682        (process, reader, writer)
683    }
684
685    #[test]
686    fn process_set_state_wakes_on_resumed() {
687        let mut process = Process::with_parent_and_group(Pid(1), Pid(2));
688        process.state = ProcessState::stopped(signal::SIGTSTP);
689        let process = Rc::new(RefCell::new(process));
690        let process2 = Rc::clone(&process);
691        let waker = Rc::new(Cell::new(None));
692        let task = poll_fn(move |cx| {
693            let mut process = process2.borrow_mut();
694            if process.state() == ProcessState::Running {
695                return Poll::Ready(());
696            }
697            waker.set(Some(cx.waker().clone()));
698            process.wake_on_resumption(Rc::downgrade(&waker));
699            Poll::Pending
700        });
701
702        let mut executor = futures_executor::LocalPool::new();
703        let mut handle = executor.spawner().spawn_local_with_handle(task).unwrap();
704        executor.run_until_stalled();
705        assert_eq!((&mut handle).now_or_never(), None);
706
707        _ = process.borrow_mut().set_state(ProcessState::Running);
708        assert!(executor.try_run_one());
709        assert_eq!(handle.now_or_never(), Some(()));
710    }
711
712    #[test]
713    fn process_set_state_closes_all_fds_on_exit() {
714        let (mut process, _reader, _writer) = process_with_pipe();
715        assert!(process.set_state(ProcessState::exited(3)));
716        assert!(process.fds().is_empty(), "{:?}", process.fds());
717    }
718
719    #[test]
720    fn process_set_state_closes_all_fds_on_signaled() {
721        let (mut process, _reader, _writer) = process_with_pipe();
722        assert!(
723            process.set_state(ProcessState::Halted(ProcessResult::Signaled {
724                signal: signal::SIGINT,
725                core_dump: false
726            }))
727        );
728        assert!(process.fds().is_empty(), "{:?}", process.fds());
729    }
730
731    #[test]
732    fn process_default_signal_blocking_mask() {
733        let process = Process::with_parent_and_group(Pid(10), Pid(11));
734        let initial_set = process.blocked_signals();
735        assert!(initial_set.is_empty(), "{initial_set:?}");
736    }
737
738    #[test]
739    fn process_sigmask_setmask() {
740        let mut process = Process::with_parent_and_group(Pid(10), Pid(11));
741        let result = process.block_signals(SigmaskOp::Set, &[signal::SIGINT, signal::SIGCHLD]);
742        assert_eq!(result, SignalResult::default());
743
744        let result_set = process.blocked_signals();
745        assert!(result_set.contains(&signal::SIGINT));
746        assert!(result_set.contains(&signal::SIGCHLD));
747        assert_eq!(result_set.len(), 2);
748
749        let result = process.block_signals(SigmaskOp::Set, &[signal::SIGINT, signal::SIGQUIT]);
750        assert_eq!(result, SignalResult::default());
751
752        let result_set = process.blocked_signals();
753        assert!(result_set.contains(&signal::SIGINT));
754        assert!(result_set.contains(&signal::SIGQUIT));
755        assert_eq!(result_set.len(), 2);
756    }
757
758    #[test]
759    fn process_sigmask_block() {
760        let mut process = Process::with_parent_and_group(Pid(10), Pid(11));
761        let result = process.block_signals(SigmaskOp::Add, &[signal::SIGINT, signal::SIGCHLD]);
762        assert_eq!(result, SignalResult::default());
763
764        let result_set = process.blocked_signals();
765        assert!(result_set.contains(&signal::SIGINT));
766        assert!(result_set.contains(&signal::SIGCHLD));
767        assert_eq!(result_set.len(), 2);
768
769        let result = process.block_signals(SigmaskOp::Add, &[signal::SIGINT, signal::SIGQUIT]);
770        assert_eq!(result, SignalResult::default());
771
772        let result_set = process.blocked_signals();
773        assert!(result_set.contains(&signal::SIGINT));
774        assert!(result_set.contains(&signal::SIGQUIT));
775        assert!(result_set.contains(&signal::SIGCHLD));
776        assert_eq!(result_set.len(), 3);
777    }
778
779    #[test]
780    fn process_sigmask_unblock() {
781        let mut process = Process::with_parent_and_group(Pid(10), Pid(11));
782        let result = process.block_signals(SigmaskOp::Add, &[signal::SIGINT, signal::SIGCHLD]);
783        assert_eq!(result, SignalResult::default());
784
785        let result = process.block_signals(SigmaskOp::Remove, &[signal::SIGINT, signal::SIGQUIT]);
786        assert_eq!(result, SignalResult::default());
787
788        let result_set = process.blocked_signals();
789        assert!(result_set.contains(&signal::SIGCHLD));
790        assert_eq!(result_set.len(), 1);
791    }
792
793    #[test]
794    fn process_set_disposition() {
795        let mut process = Process::with_parent_and_group(Pid(100), Pid(11));
796        let old_disposition = process.set_disposition(signal::SIGINT, Disposition::Ignore);
797        assert_eq!(old_disposition, Disposition::Default);
798        let old_disposition = process.set_disposition(signal::SIGTERM, Disposition::Catch);
799        assert_eq!(old_disposition, Disposition::Default);
800
801        let old_disposition = process.set_disposition(signal::SIGINT, Disposition::Default);
802        assert_eq!(old_disposition, Disposition::Ignore);
803        let old_disposition = process.set_disposition(signal::SIGTERM, Disposition::Ignore);
804        assert_eq!(old_disposition, Disposition::Catch);
805
806        let disposition = process.disposition(signal::SIGINT);
807        assert_eq!(disposition, Disposition::Default);
808        let disposition = process.disposition(signal::SIGTERM);
809        assert_eq!(disposition, Disposition::Ignore);
810        let disposition = process.disposition(signal::SIGQUIT);
811        assert_eq!(disposition, Disposition::Default);
812    }
813
814    #[test]
815    fn process_raise_signal_default_nop() {
816        let mut process = Process::with_parent_and_group(Pid(42), Pid(11));
817        let result = process.raise_signal(signal::SIGCHLD);
818        assert_eq!(
819            result,
820            SignalResult {
821                delivered: true,
822                caught: false,
823                process_state_changed: false,
824            }
825        );
826        assert_eq!(process.state(), ProcessState::Running);
827    }
828
829    #[test]
830    fn process_raise_signal_default_terminating() {
831        let mut process = Process::with_parent_and_group(Pid(42), Pid(11));
832        let result = process.raise_signal(signal::SIGTERM);
833        assert_eq!(
834            result,
835            SignalResult {
836                delivered: true,
837                caught: false,
838                process_state_changed: true,
839            }
840        );
841        assert_eq!(
842            process.state(),
843            ProcessState::Halted(ProcessResult::Signaled {
844                signal: signal::SIGTERM,
845                core_dump: false
846            })
847        );
848        assert_eq!(process.caught_signals, []);
849    }
850
851    #[test]
852    fn process_raise_signal_default_aborting() {
853        let mut process = Process::with_parent_and_group(Pid(42), Pid(11));
854        let result = process.raise_signal(signal::SIGABRT);
855        assert_eq!(
856            result,
857            SignalResult {
858                delivered: true,
859                caught: false,
860                process_state_changed: true,
861            }
862        );
863        assert_eq!(
864            process.state(),
865            ProcessState::Halted(ProcessResult::Signaled {
866                signal: signal::SIGABRT,
867                core_dump: true
868            })
869        );
870        assert_eq!(process.caught_signals, []);
871        // TODO Check if core dump file has been created
872    }
873
874    #[test]
875    fn process_raise_signal_default_stopping() {
876        let mut process = Process::with_parent_and_group(Pid(42), Pid(11));
877        let result = process.raise_signal(signal::SIGTSTP);
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::stopped(signal::SIGTSTP));
887        assert_eq!(process.caught_signals, []);
888    }
889
890    #[test]
891    fn process_raise_signal_default_continuing() {
892        let mut process = Process::with_parent_and_group(Pid(42), Pid(11));
893        let _ = process.set_state(ProcessState::stopped(signal::SIGTTOU));
894        let result = process.raise_signal(signal::SIGCONT);
895        assert_eq!(
896            result,
897            SignalResult {
898                delivered: true,
899                caught: false,
900                process_state_changed: true,
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() {
909        let mut process = Process::with_parent_and_group(Pid(42), Pid(11));
910        process.set_disposition(signal::SIGCHLD, Disposition::Ignore);
911        let result = process.raise_signal(signal::SIGCHLD);
912        assert_eq!(
913            result,
914            SignalResult {
915                delivered: true,
916                caught: false,
917                process_state_changed: false,
918            }
919        );
920        assert_eq!(process.state(), ProcessState::Running);
921        assert_eq!(process.caught_signals, []);
922    }
923
924    #[test]
925    fn process_raise_signal_ignored_and_blocked_sigcont() {
926        let mut process = Process::with_parent_and_group(Pid(42), Pid(11));
927        let _ = process.set_state(ProcessState::stopped(signal::SIGTTOU));
928        let _ = process.set_disposition(signal::SIGCONT, Disposition::Ignore);
929        let _ = process.block_signals(SigmaskOp::Add, &[signal::SIGCONT]);
930        let result = process.raise_signal(signal::SIGCONT);
931        assert_eq!(
932            result,
933            SignalResult {
934                delivered: false,
935                caught: false,
936                process_state_changed: true,
937            }
938        );
939        assert_eq!(process.state(), ProcessState::Running);
940        assert_eq!(process.caught_signals, []);
941        assert!(process.pending_signals.contains(&signal::SIGCONT));
942    }
943
944    #[test]
945    fn process_raise_signal_caught() {
946        let mut process = Process::with_parent_and_group(Pid(42), Pid(11));
947        process.set_disposition(signal::SIGCHLD, Disposition::Catch);
948        let wake_flag = Arc::new(WakeFlag::new());
949        let waker = Rc::new(Cell::new(Some(Waker::from(wake_flag.clone()))));
950        process.register_signal_waker(Rc::downgrade(&waker));
951
952        let result = process.raise_signal(signal::SIGCHLD);
953        assert_eq!(
954            result,
955            SignalResult {
956                delivered: true,
957                caught: true,
958                process_state_changed: false,
959            }
960        );
961        assert_eq!(process.state(), ProcessState::Running);
962        assert_eq!(process.caught_signals, [signal::SIGCHLD]);
963        assert!(wake_flag.is_woken());
964    }
965
966    #[test]
967    fn process_raise_signal_blocked() {
968        let mut process = Process::with_parent_and_group(Pid(42), Pid(11));
969        process.set_disposition(signal::SIGCHLD, Disposition::Catch);
970        let result = process.block_signals(SigmaskOp::Add, &[signal::SIGCHLD]);
971        assert_eq!(
972            result,
973            SignalResult {
974                delivered: false,
975                caught: false,
976                process_state_changed: false,
977            }
978        );
979
980        let result = process.raise_signal(signal::SIGCHLD);
981        assert_eq!(
982            result,
983            SignalResult {
984                delivered: false,
985                caught: false,
986                process_state_changed: false,
987            }
988        );
989        assert_eq!(process.state(), ProcessState::Running);
990        assert_eq!(process.caught_signals, []);
991
992        let result = process.block_signals(SigmaskOp::Set, &[]);
993        assert_eq!(
994            result,
995            SignalResult {
996                delivered: true,
997                caught: true,
998                process_state_changed: false,
999            }
1000        );
1001        assert_eq!(process.state(), ProcessState::Running);
1002        assert_eq!(process.caught_signals, [signal::SIGCHLD]);
1003    }
1004}