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