Skip to main content

yash_env/system/
virtual.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//! System simulated in Rust
18//!
19//! [`VirtualSystem`] is a pure Rust implementation of system traits that
20//! simulates the behavior of the underlying system without any interaction with
21//! the actual system. `VirtualSystem` is used for testing the behavior of the
22//! shell in unit tests.
23//!
24//! # Features and components
25//!
26//! This module also defines elements that compose a virtual system.
27//!
28//! ## File system
29//!
30//! Basic file operations are supported in the virtual system. Regular files,
31//! directories, named pipes, and symbolic links can be created in the file
32//! system. The file system is shared among all processes in the system.
33//!
34//! ## Processes
35//!
36//! A virtual system initially has one process, but can have more processes as a
37//! result of simulating fork. Each process has its own state.
38//!
39//! ## I/O
40//!
41//! Currently, read and write operations on files and unnamed pipes are
42//! supported.
43//!
44//! ## Signals
45//!
46//! The virtual system can simulate sending signals to processes. Processes can
47//! block, ignore, and catch signals.
48//!
49//! # Concurrency
50//!
51//! `VirtualSystem` is designed to allow multiple virtual processes to run
52//! concurrently. To achieve this, some trait methods return futures that enable
53//! the executor to suspend the calling thread until the process is ready to
54//! proceed. This allows the executor to switch between multiple virtual
55//! processes on a single thread.
56//! See also the [`system` module](super) documentation.
57//!
58//! TBD: Explain how to use `VirtualSystem` with an executor.
59
60pub mod fd_set;
61mod file_body;
62mod file_system;
63mod io;
64mod process;
65mod select;
66mod signal;
67pub mod sigset;
68
69pub use self::file_body::*;
70pub use self::file_system::*;
71pub use self::io::*;
72pub use self::process::*;
73pub use self::signal::*;
74use self::sigset::Sigset;
75use super::AT_FDCWD;
76use super::CaughtSignals;
77use super::Chdir;
78use super::Clock;
79use super::Close;
80use super::CpuTimes;
81use super::Dir;
82use super::Disposition;
83use super::Dup;
84use super::Errno;
85use super::Exec;
86use super::Exit;
87use super::Fcntl;
88use super::FdFlag;
89use super::Fork;
90use super::Fstat;
91use super::GetCwd;
92use super::GetPid;
93use super::GetPw;
94use super::GetRlimit;
95use super::GetSigaction;
96use super::GetUid;
97use super::Gid;
98use super::IsExecutableFile;
99use super::Isatty;
100use super::OfdAccess;
101use super::Open;
102use super::OpenFlag;
103use super::Pipe;
104use super::Read;
105use super::Result;
106use super::Seek;
107use super::Select;
108use super::SendSignal;
109use super::SetPgid;
110use super::SetRlimit;
111use super::ShellPath;
112use super::Sigaction;
113use super::Sigmask;
114use super::SigmaskOp;
115use super::Signals;
116use super::Sysconf;
117use super::TcGetPgrp;
118use super::TcSetPgrp;
119use super::Times;
120use super::Uid;
121use super::Umask;
122use super::Wait;
123use super::Write;
124use super::c_string::{AsCStrArray, IntoCStrArray};
125use super::resource::INFINITY;
126use super::resource::LimitPair;
127use super::resource::Resource;
128use crate::io::Fd;
129use crate::job::Pid;
130use crate::job::ProcessState;
131use crate::path::Path;
132use crate::path::PathBuf;
133use crate::semantics::ExitStatus;
134use crate::str::UnixStr;
135use crate::str::UnixString;
136use crate::waker::ScheduledWakerQueue;
137use crate::waker::WakerSet;
138use enumset::EnumSet;
139use std::borrow::Cow;
140use std::cell::Cell;
141use std::cell::LazyCell;
142use std::cell::Ref;
143use std::cell::RefCell;
144use std::cell::RefMut;
145use std::collections::BTreeMap;
146use std::collections::HashMap;
147use std::collections::VecDeque;
148use std::convert::Infallible;
149use std::convert::TryInto;
150use std::ffi::CStr;
151use std::ffi::CString;
152use std::fmt::Debug;
153use std::future::pending;
154use std::future::poll_fn;
155use std::future::ready;
156use std::io::SeekFrom;
157use std::ops::RangeInclusive;
158use std::pin::Pin;
159use std::rc::Rc;
160use std::task::Poll;
161use std::task::Waker;
162use std::time::Duration;
163use std::time::Instant;
164
165/// Simulated system
166///
167/// See the [module-level documentation](self) to grasp a basic understanding of
168/// `VirtualSystem`.
169///
170/// A `VirtualSystem` instance has two members: `state` and `process_id`. The
171/// former is a [`SystemState`] that effectively contains the whole state of the
172/// system. The state is contained in `Rc` so that virtual processes can share
173/// the same state. The latter is a process ID that identifies a process calling
174/// the system interfaces.
175///
176/// When you clone a virtual system, the clone will have the same `process_id`
177/// and `state` as the original. To simulate the `fork` system call, you should
178/// call a method of the [`Fork`] trait implemented by `VirtualSystem`.
179#[derive(Clone, Debug)]
180pub struct VirtualSystem {
181    /// State of the system.
182    pub state: Rc<RefCell<SystemState>>,
183
184    /// Process ID of the process that is interacting with the system.
185    pub process_id: Pid,
186}
187
188impl VirtualSystem {
189    /// Creates a virtual system with an almost empty state.
190    ///
191    /// The `process_id` of the returned `VirtualSystem` will be 2.
192    /// (Process ID 1 has special meaning in some system calls, so we don't use
193    /// it as a default value.)
194    ///
195    /// The `state` of the returned `VirtualSystem` will have a [`Process`] with
196    /// process ID 2 in the process set ([`SystemState::processes`]). The file
197    /// system will contain files named `/dev/stdin`, `/dev/stdout`, and
198    /// `/dev/stderr` that are opened in the process with file descriptor 0, 1,
199    /// and 2, respectively. The file system also contains an empty directory
200    /// `/tmp`.
201    pub fn new() -> VirtualSystem {
202        let mut state = SystemState::default();
203        let mut process = Process::with_parent_and_group(Pid(1), Pid(1));
204
205        let mut set_std_fd = |path, fd| {
206            let file = Rc::new(RefCell::new(Inode::new([])));
207            state.file_system.save(path, Rc::clone(&file)).unwrap();
208            let body = FdBody {
209                open_file_description: Rc::new(RefCell::new(OpenFileDescription::new(
210                    file, /* offset = */ 0, /* is_readable = */ true,
211                    /* is_writable = */ true, /* is_appending = */ true,
212                    /* is_nonblocking = */ false,
213                ))),
214                flags: EnumSet::empty(),
215            };
216            process.set_fd(fd, body).unwrap();
217        };
218        set_std_fd("/dev/stdin", Fd::STDIN);
219        set_std_fd("/dev/stdout", Fd::STDOUT);
220        set_std_fd("/dev/stderr", Fd::STDERR);
221
222        state
223            .file_system
224            .save(
225                "/tmp",
226                Rc::new(RefCell::new(Inode {
227                    body: FileBody::Directory {
228                        files: Default::default(),
229                    },
230                    permissions: Mode::ALL_9,
231                })),
232            )
233            .unwrap();
234
235        let process_id = Pid(2);
236        state.processes.insert(process_id, process);
237
238        let state = Rc::new(RefCell::new(state));
239        VirtualSystem { state, process_id }
240    }
241
242    /// Finds the current process from the system state.
243    ///
244    /// # Panics
245    ///
246    /// This function will panic if it cannot find a process having
247    /// `self.process_id`.
248    pub fn current_process(&self) -> Ref<'_, Process> {
249        Ref::map(self.state.borrow(), |state| {
250            &state.processes[&self.process_id]
251        })
252    }
253
254    /// Finds the current process from the system state.
255    ///
256    /// # Panics
257    ///
258    /// This function will panic if it cannot find a process having
259    /// `self.process_id`.
260    pub fn current_process_mut(&self) -> RefMut<'_, Process> {
261        RefMut::map(self.state.borrow_mut(), |state| {
262            state.processes.get_mut(&self.process_id).unwrap()
263        })
264    }
265
266    /// Returns the open file description for the given FD.
267    ///
268    /// This is a helper function for obtaining the open file description for a
269    /// given file descriptor in the current process.
270    /// Returns `Err(Errno::EBADF)` if the FD is not open.
271    pub fn get_open_file_description(&self, fd: Fd) -> Result<Rc<RefCell<OpenFileDescription>>> {
272        let process = self.current_process();
273        let body = process.get_fd(fd).ok_or(Errno::EBADF)?;
274        Ok(Rc::clone(&body.open_file_description))
275    }
276
277    /// Calls the given closure passing the open file description for the FD.
278    ///
279    /// This is a helper function for performing an operation on the open file
280    /// description for a given file descriptor in the current process. The
281    /// closure is passed a reference to the open file description, and can
282    /// perform any operation on it, such as checking its state. The return
283    /// value of the closure is returned as the result of this function.
284    ///
285    /// Returns `Err(Errno::EBADF)` if the FD is not open.
286    /// Panics if the open file description is already mutably borrowed.
287    pub fn with_open_file_description<F, R>(&self, fd: Fd, f: F) -> Result<R>
288    where
289        F: FnOnce(&OpenFileDescription) -> Result<R>,
290    {
291        let ofd = self.get_open_file_description(fd)?;
292        f(&ofd.borrow())
293    }
294
295    /// Calls the given closure passing the open file description for the FD.
296    ///
297    /// This is a helper function for performing a mutable operation on the open
298    /// file description for a given file descriptor in the current process. The
299    /// closure is passed a mutable reference to the open file description, and
300    /// can perform any operation on it, such as reading and writing. The return
301    /// value of the closure is returned as the result of this function.
302    ///
303    /// Returns `Err(Errno::EBADF)` if the FD is not open.
304    /// Panics if the open file description is already borrowed.
305    pub fn with_open_file_description_mut<F, R>(&self, fd: Fd, f: F) -> Result<R>
306    where
307        F: FnOnce(&mut OpenFileDescription) -> Result<R>,
308    {
309        let ofd = self.get_open_file_description(fd)?;
310        f(&mut ofd.borrow_mut())
311    }
312
313    fn resolve_relative_path<'a>(&self, path: &'a Path) -> Cow<'a, Path> {
314        if path.is_absolute() {
315            Cow::Borrowed(path)
316        } else {
317            Cow::Owned(self.current_process().cwd.join(path))
318        }
319    }
320
321    fn resolve_existing_file(
322        &self,
323        _dir_fd: Fd,
324        path: &Path,
325        follow_symlinks: bool,
326    ) -> Result<Rc<RefCell<Inode>>> {
327        // TODO Resolve relative to dir_fd
328        // TODO Support AT_FDCWD
329        const _POSIX_SYMLOOP_MAX: i32 = 8;
330
331        let mut path = Cow::Borrowed(path);
332        for _count in 0.._POSIX_SYMLOOP_MAX {
333            let resolved_path = self.resolve_relative_path(&path);
334            let inode = self.state.borrow().file_system.get(&resolved_path)?;
335            if !follow_symlinks {
336                return Ok(inode);
337            }
338
339            let inode_ref = inode.borrow();
340            if let FileBody::Symlink { target } = &inode_ref.body {
341                let mut new_path = resolved_path.into_owned();
342                new_path.pop();
343                new_path.push(target);
344                path = Cow::Owned(new_path);
345            } else {
346                drop(inode_ref);
347                return Ok(inode);
348            }
349        }
350
351        Err(Errno::ELOOP)
352    }
353
354    /// Blocks the calling thread until the current process is running.
355    async fn block_until_running(&self) {
356        let waker = Rc::new(Cell::new(None));
357
358        poll_fn(|cx| {
359            let mut state = self.state.borrow_mut();
360            let Some(process) = state.processes.get_mut(&self.process_id) else {
361                return Poll::Ready(());
362            };
363
364            match process.state {
365                ProcessState::Running => Poll::Ready(()),
366                ProcessState::Halted(result) => {
367                    if result.is_stopped() {
368                        waker.set(Some(cx.waker().clone()));
369                        process.wake_on_resumption(Rc::downgrade(&waker));
370                    }
371                    Poll::Pending
372                }
373            }
374        })
375        .await
376    }
377
378    /// Blocks the calling thread while the current process is stopped.
379    ///
380    /// Returns `true` if the process has terminated, or `false` if it has resumed.
381    /// **Panics** if the process is not found in the system state.
382    pub(crate) async fn block_while_stopped(&self) -> bool {
383        let waker = Rc::new(Cell::new(None));
384
385        poll_fn(|cx| {
386            let mut process = self.current_process_mut();
387            match process.state() {
388                ProcessState::Running => Poll::Ready(false),
389                ProcessState::Halted(process_result) if process_result.is_stopped() => {
390                    waker.set(Some(cx.waker().clone()));
391                    process.wake_on_resumption(Rc::downgrade(&waker));
392                    Poll::Pending
393                }
394                ProcessState::Halted(_) => Poll::Ready(true),
395            }
396        })
397        .await
398    }
399
400    /// Resolves a file for opening.
401    ///
402    /// This is a helper function used internally by [`Self::open`], etc.
403    fn resolve_file(
404        &self,
405        path: &CStr,
406        access: OfdAccess,
407        flags: EnumSet<OpenFlag>,
408        mode: Mode,
409    ) -> Result<(Rc<RefCell<Inode>>, bool, bool)> {
410        let path = self.resolve_relative_path(Path::new(UnixStr::from_bytes(path.to_bytes())));
411        let umask = self.current_process().umask;
412
413        let mut state = self.state.borrow_mut();
414        let file = match state.file_system.get(&path) {
415            Ok(inode) => {
416                if flags.contains(OpenFlag::Exclusive) {
417                    return Err(Errno::EEXIST);
418                }
419                if flags.contains(OpenFlag::Directory)
420                    && !matches!(inode.borrow().body, FileBody::Directory { .. })
421                {
422                    return Err(Errno::ENOTDIR);
423                }
424                if flags.contains(OpenFlag::Truncate)
425                    && let FileBody::Regular { content, .. } = &mut inode.borrow_mut().body
426                {
427                    content.clear();
428                };
429                inode
430            }
431            Err(Errno::ENOENT) if flags.contains(OpenFlag::Create) => {
432                let mut inode = Inode::new([]);
433                inode.permissions = mode.difference(umask);
434                let inode = Rc::new(RefCell::new(inode));
435                state.file_system.save(&path, Rc::clone(&inode))?;
436                inode
437            }
438            Err(errno) => return Err(errno),
439        };
440
441        let (is_readable, is_writable) = match access {
442            OfdAccess::ReadOnly => (true, false),
443            OfdAccess::WriteOnly => (false, true),
444            OfdAccess::ReadWrite => (true, true),
445            OfdAccess::Exec | OfdAccess::Search => (false, false),
446        };
447
448        Ok((file, is_readable, is_writable))
449    }
450
451    /// Creates a new file descriptor in the current process.
452    ///
453    /// This is a helper function used internally by [`Self::open`], etc.
454    fn create_fd(
455        &self,
456        open_file_description: Rc<RefCell<OpenFileDescription>>,
457        flags: EnumSet<OpenFlag>,
458    ) -> std::result::Result<Fd, Errno> {
459        let body = FdBody {
460            open_file_description,
461            flags: if flags.contains(OpenFlag::CloseOnExec) {
462                EnumSet::only(FdFlag::CloseOnExec)
463            } else {
464                EnumSet::empty()
465            },
466        };
467        self.current_process_mut()
468            .open_fd(body)
469            .map_err(|_| Errno::EMFILE)
470    }
471
472    /// Blocks until the FIFO in the given open file description is ready.
473    ///
474    /// This is a helper function used internally by [`Self::open`] when opening
475    /// a FIFO without `OpenFlag::NonBlock`. POSIX requires that opening a FIFO
476    /// for reading blocks until there is a writer, and opening a FIFO for
477    /// writing blocks until there is a reader. This function implements this
478    /// behavior.
479    ///
480    /// This function does nothing if the file is not a FIFO.
481    async fn wait_for_fifo_to_become_ready(open_file_description: &OpenFileDescription) {
482        // If the file is a FIFO, block until the other end is opened.
483        let waker: LazyCell<Rc<Cell<Option<Waker>>>> = LazyCell::default();
484        poll_fn(|context| {
485            let mut file = open_file_description.inode().borrow_mut();
486            let FileBody::Fifo {
487                readers,
488                writers,
489                ref mut pending_open_wakers,
490                ..
491            } = file.body
492            else {
493                return Poll::Ready(());
494            };
495
496            if readers > 0 && writers > 0 {
497                return Poll::Ready(());
498            }
499
500            // Register the current task's waker to be notified when a new
501            // reader or writer is opened on the FIFO.
502            waker.set(Some(context.waker().clone()));
503            pending_open_wakers.insert(Rc::downgrade(&waker));
504            Poll::Pending
505        })
506        .await;
507    }
508}
509
510impl Default for VirtualSystem {
511    fn default() -> Self {
512        VirtualSystem::new()
513    }
514}
515
516impl Fstat for VirtualSystem {
517    type Stat = Stat;
518
519    fn fstat(&self, fd: Fd) -> Result<Stat> {
520        self.with_open_file_description(fd, |ofd| Ok(ofd.inode().borrow().stat()))
521    }
522
523    fn fstatat(&self, dir_fd: Fd, path: &CStr, follow_symlinks: bool) -> Result<Stat> {
524        let path = Path::new(UnixStr::from_bytes(path.to_bytes()));
525        let inode = self.resolve_existing_file(dir_fd, path, follow_symlinks)?;
526        Ok(inode.borrow().stat())
527    }
528}
529
530impl IsExecutableFile for VirtualSystem {
531    /// Tests whether the specified file is executable or not.
532    ///
533    /// The current implementation only checks if the file has any executable
534    /// bit in the permissions. The file owner and group are not considered.
535    fn is_executable_file(&self, path: &CStr) -> bool {
536        let path = Path::new(UnixStr::from_bytes(path.to_bytes()));
537        self.resolve_existing_file(AT_FDCWD, path, /* follow symlinks */ true)
538            .is_ok_and(|inode| inode.borrow().permissions.intersects(Mode::ALL_EXEC))
539    }
540}
541
542impl Pipe for VirtualSystem {
543    fn pipe(&self) -> Result<(Fd, Fd)> {
544        let file = Rc::new(RefCell::new(Inode {
545            body: FileBody::Fifo {
546                content: VecDeque::new(),
547                readers: 0,
548                writers: 0,
549                pending_open_wakers: WakerSet::new(),
550                pending_read_wakers: WakerSet::new(),
551                pending_write_wakers: WakerSet::new(),
552            },
553            permissions: Mode::default(),
554        }));
555        let reader = OpenFileDescription::new(
556            Rc::clone(&file),
557            /* offset = */ 0,
558            /* is_readable = */ true,
559            /* is_writable = */ false,
560            /* is_appending = */ false,
561            /* is_nonblocking = */ false,
562        );
563        let writer = OpenFileDescription::new(
564            Rc::clone(&file),
565            /* offset = */ 0,
566            /* is_readable = */ false,
567            /* is_writable = */ true,
568            /* is_appending = */ false,
569            /* is_nonblocking = */ false,
570        );
571
572        let reader = FdBody {
573            open_file_description: Rc::new(RefCell::new(reader)),
574            flags: EnumSet::empty(),
575        };
576        let writer = FdBody {
577            open_file_description: Rc::new(RefCell::new(writer)),
578            flags: EnumSet::empty(),
579        };
580
581        let mut process = self.current_process_mut();
582        let reader = process.open_fd(reader).map_err(|_| Errno::EMFILE)?;
583        let writer = process.open_fd(writer).map_err(|_| {
584            process.close_fd(reader);
585            Errno::EMFILE
586        })?;
587        Ok((reader, writer))
588    }
589}
590
591impl Dup for VirtualSystem {
592    fn dup(&self, from: Fd, to_min: Fd, flags: EnumSet<FdFlag>) -> Result<Fd> {
593        let mut process = self.current_process_mut();
594        let mut body = process.fds.get(&from).ok_or(Errno::EBADF)?.clone();
595        body.flags = flags;
596        process.open_fd_ge(to_min, body).map_err(|_| Errno::EMFILE)
597    }
598
599    fn dup2(&self, from: Fd, to: Fd) -> Result<Fd> {
600        let mut process = self.current_process_mut();
601        let mut body = process.fds.get(&from).ok_or(Errno::EBADF)?.clone();
602        body.flags = EnumSet::empty();
603        process.set_fd(to, body).map_err(|_| Errno::EBADF)?;
604        Ok(to)
605    }
606}
607
608impl Open for VirtualSystem {
609    /// Opens a file and returns a new file descriptor for it.
610    ///
611    /// The returned future will be pending until the file is ready to be
612    /// opened. For example, if the file is a FIFO, the future will be pending
613    /// until the other end of the FIFO is opened, unless `OpenFlag::NonBlock`
614    /// is specified.
615    fn open(
616        &self,
617        path: &CStr,
618        access: OfdAccess,
619        flags: EnumSet<OpenFlag>,
620        mode: Mode,
621    ) -> impl Future<Output = Result<Fd>> + use<> {
622        let resolution = self.resolve_file(path, access, flags, mode);
623        let system = self.clone();
624
625        async move {
626            let (file, is_readable, is_writable) = resolution?;
627
628            if let FileBody::Fifo { readers, .. } = &mut file.borrow_mut().body {
629                // POSIX: Opening a FIFO with O_NONBLOCK | O_WRONLY should fail with
630                // ENXIO if there are no readers
631                if flags.contains(OpenFlag::NonBlock)
632                    && is_writable
633                    && !is_readable
634                    && *readers == 0
635                {
636                    return Err(Errno::ENXIO);
637                }
638            }
639
640            // If the file is a FIFO, OpenFileDescription::new increments the reader/writer count,
641            // which must be done before `wait_for_fifo_to_become_ready`.
642            let open_file_description = OpenFileDescription::new(
643                file,
644                /* offset = */ 0,
645                is_readable,
646                is_writable,
647                /* is_appending = */ flags.contains(OpenFlag::Append),
648                /* is_nonblocking = */ flags.contains(OpenFlag::NonBlock),
649            );
650
651            if !flags.contains(OpenFlag::NonBlock) {
652                Self::wait_for_fifo_to_become_ready(&open_file_description).await;
653            }
654
655            system.create_fd(Rc::new(RefCell::new(open_file_description)), flags)
656        }
657    }
658
659    fn open_tmpfile(&self, _parent_dir: &Path) -> Result<Fd> {
660        let file = Rc::new(RefCell::new(Inode::new([])));
661        let open_file_description = Rc::new(RefCell::new(OpenFileDescription::new(
662            file, /* offset = */ 0, /* is_readable = */ true,
663            /* is_writable = */ true, /* is_appending = */ false,
664            /* is_nonblocking = */ false,
665        )));
666        let body = FdBody {
667            open_file_description,
668            flags: EnumSet::empty(),
669        };
670        self.current_process_mut()
671            .open_fd(body)
672            .map_err(|_| Errno::EMFILE)
673    }
674
675    fn fdopendir(&self, fd: Fd) -> Result<impl Dir + use<>> {
676        self.with_open_file_description(fd, |ofd| {
677            let inode = ofd.inode();
678            let dir = VirtualDir::try_from(&inode.borrow().body)?;
679            Ok(dir)
680        })
681    }
682
683    fn opendir(&self, path: &CStr) -> Result<impl Dir + use<>> {
684        let (file, is_readable, is_writable) = self.resolve_file(
685            path,
686            OfdAccess::ReadOnly,
687            OpenFlag::Directory.into(),
688            Mode::empty(),
689        )?;
690
691        debug_assert!(
692            matches!(file.borrow().body, FileBody::Directory { .. }),
693            "resolved file is not a directory"
694        );
695
696        let open_file_description = Rc::new(RefCell::new(OpenFileDescription::new(
697            file,
698            /* offset = */ 0,
699            is_readable,
700            is_writable,
701            /* is_appending = */ false,
702            /* is_nonblocking = */ false,
703        )));
704        let fd = self.create_fd(open_file_description, OpenFlag::Directory.into())?;
705        self.fdopendir(fd)
706    }
707}
708
709impl Close for VirtualSystem {
710    fn close(&self, fd: Fd) -> Result<()> {
711        self.current_process_mut().close_fd(fd);
712        Ok(())
713    }
714}
715
716impl Fcntl for VirtualSystem {
717    fn ofd_access(&self, fd: Fd) -> Result<OfdAccess> {
718        fn is_directory(file_body: &FileBody) -> bool {
719            matches!(file_body, FileBody::Directory { .. })
720        }
721
722        self.with_open_file_description(fd, |ofd| match (ofd.is_readable(), ofd.is_writable()) {
723            (true, false) => Ok(OfdAccess::ReadOnly),
724            (false, true) => Ok(OfdAccess::WriteOnly),
725            (true, true) => Ok(OfdAccess::ReadWrite),
726            (false, false) => {
727                if is_directory(&ofd.inode().borrow().body) {
728                    Ok(OfdAccess::Search)
729                } else {
730                    Ok(OfdAccess::Exec)
731                }
732            }
733        })
734    }
735
736    fn get_and_set_nonblocking(&self, fd: Fd, nonblocking: bool) -> Result<bool> {
737        self.with_open_file_description_mut(fd, |ofd| {
738            let was_nonblocking = ofd.is_nonblocking();
739            ofd.set_nonblocking(nonblocking);
740            Ok(was_nonblocking)
741        })
742    }
743
744    fn fcntl_getfd(&self, fd: Fd) -> Result<EnumSet<FdFlag>> {
745        let process = self.current_process();
746        let body = process.get_fd(fd).ok_or(Errno::EBADF)?;
747        Ok(body.flags)
748    }
749
750    fn fcntl_setfd(&self, fd: Fd, flags: EnumSet<FdFlag>) -> Result<()> {
751        let mut process = self.current_process_mut();
752        let body = process.get_fd_mut(fd).ok_or(Errno::EBADF)?;
753        body.flags = flags;
754        Ok(())
755    }
756}
757
758impl VirtualSystem {
759    /// Checks for newly caught signals and registers a signal waker if none found.
760    ///
761    /// Returns `true` if new signals have been caught since `initial_signal_count`,
762    /// leaving the waker unregistered. Returns `false` and registers the waker for
763    /// future signal notifications otherwise.
764    fn signal_interrupted(
765        &self,
766        initial_signal_count: usize,
767        waker: &Rc<Cell<Option<Waker>>>,
768        context: &mut std::task::Context<'_>,
769    ) -> bool {
770        let mut proc = self.current_process_mut();
771        if proc.caught_signals_count != initial_signal_count {
772            return true;
773        }
774        waker.set(Some(context.waker().clone()));
775        proc.register_signal_waker(Rc::downgrade(waker));
776        false
777    }
778}
779
780impl Read for VirtualSystem {
781    /// Reads data from the file descriptor into the buffer.
782    ///
783    /// If a signal is caught while this method is blocking for data, it returns
784    /// [`Errno::EINTR`].
785    fn read<'a>(
786        &self,
787        fd: Fd,
788        buffer: &'a mut [u8],
789    ) -> impl Future<Output = Result<usize>> + use<'a> {
790        let ofd = self.get_open_file_description(fd);
791        let system = self.clone();
792        async move {
793            let ofd = ofd?;
794            let initial_signal_count = system.current_process().caught_signals_count;
795            let waker: LazyCell<Rc<Cell<Option<Waker>>>> = LazyCell::default();
796            poll_fn(|context| {
797                let get_waker = || {
798                    waker.set(Some(context.waker().clone()));
799                    Rc::downgrade(&waker)
800                };
801                let result = ofd.borrow_mut().poll_read(buffer, get_waker);
802                if result.is_pending()
803                    && system.signal_interrupted(initial_signal_count, &waker, context)
804                {
805                    return Poll::Ready(Err(Errno::EINTR));
806                }
807                result
808            })
809            .await
810        }
811    }
812}
813
814impl Write for VirtualSystem {
815    /// Writes data from the buffer to the file descriptor.
816    ///
817    /// For pipes (FIFOs) opened in blocking mode, if the write is at most
818    /// [`PIPE_BUF`] bytes, this method blocks until enough room is available
819    /// to write all bytes atomically. For larger writes, this method writes as
820    /// many bytes as fit on each pass and loops, blocking only when the pipe is
821    /// completely full, until the entire buffer has been written. The decision
822    /// to loop is encapsulated in
823    /// [`OpenFileDescription::poll_write_full`](OpenFileDescription::poll_write_full);
824    /// this method itself contains no file-type branching.
825    ///
826    /// For non-pipe file types or non-blocking mode, this method returns after
827    /// the first successful (possibly partial) write without looping.
828    ///
829    /// If a signal is caught while this method is blocking, it returns
830    /// [`Errno::EINTR`] if no bytes have been written yet, or the count of
831    /// bytes written so far otherwise. The signal-precedence check runs at the
832    /// top of each poll iteration *before* any further write is attempted, so
833    /// when `Err(Errno::EINTR)` is returned no bytes have been transferred in
834    /// this call.
835    fn write<'a>(&self, fd: Fd, buffer: &'a [u8]) -> impl Future<Output = Result<usize>> + use<'a> {
836        let ofd = self.get_open_file_description(fd);
837        let system = self.clone();
838        async move {
839            let ofd = ofd?;
840            let initial_signal_count = system.current_process().caught_signals_count;
841            let mut bytes_written = 0usize;
842            let waker: LazyCell<Rc<Cell<Option<Waker>>>> = LazyCell::default();
843            // Helper that maps the running byte count into the result returned
844            // when a signal interrupts the operation.
845            let interrupted = |bytes_written: usize| -> Poll<Result<usize>> {
846                Poll::Ready(if bytes_written > 0 {
847                    Ok(bytes_written)
848                } else {
849                    Err(Errno::EINTR)
850                })
851            };
852            poll_fn(|context| {
853                if system.current_process().caught_signals_count != initial_signal_count {
854                    return interrupted(bytes_written);
855                }
856                let get_waker = || {
857                    waker.set(Some(context.waker().clone()));
858                    Rc::downgrade(&waker)
859                };
860                let result =
861                    ofd.borrow_mut()
862                        .poll_write_full(buffer, &mut bytes_written, get_waker);
863                if result.is_pending()
864                    && system.signal_interrupted(initial_signal_count, &waker, context)
865                {
866                    return interrupted(bytes_written);
867                }
868                result
869            })
870            .await
871        }
872    }
873}
874
875impl Seek for VirtualSystem {
876    fn lseek(&self, fd: Fd, position: SeekFrom) -> Result<u64> {
877        self.with_open_file_description_mut(fd, |ofd| ofd.seek(position))
878            .and_then(|new_offset| new_offset.try_into().map_err(|_| Errno::EOVERFLOW))
879    }
880}
881
882impl Umask for VirtualSystem {
883    fn umask(&self, new_mask: Mode) -> Mode {
884        std::mem::replace(&mut self.current_process_mut().umask, new_mask)
885    }
886}
887
888impl GetCwd for VirtualSystem {
889    fn getcwd(&self) -> Result<PathBuf> {
890        Ok(self.current_process().cwd.clone())
891    }
892}
893
894impl Chdir for VirtualSystem {
895    fn chdir(&self, path: &CStr) -> Result<()> {
896        let path = Path::new(UnixStr::from_bytes(path.to_bytes()));
897        let inode = self.resolve_existing_file(AT_FDCWD, path, /* follow links */ true)?;
898        if matches!(&inode.borrow().body, FileBody::Directory { .. }) {
899            let mut process = self.current_process_mut();
900            let new_path = process.cwd.join(path);
901            process.chdir(new_path);
902            Ok(())
903        } else {
904            Err(Errno::ENOTDIR)
905        }
906    }
907}
908
909impl Clock for VirtualSystem {
910    /// Returns `now` in [`SystemState`].
911    ///
912    /// Panics if it is `None`.
913    fn now(&self) -> Instant {
914        self.state
915            .borrow()
916            .now
917            .expect("`SystemState::now` should be assigned")
918    }
919}
920
921impl Times for VirtualSystem {
922    /// Returns `times` in [`SystemState`].
923    fn times(&self) -> Result<CpuTimes> {
924        Ok(self.state.borrow().times)
925    }
926}
927
928impl Signals for VirtualSystem {
929    const SIGABRT: signal::Number = signal::SIGABRT;
930    const SIGALRM: signal::Number = signal::SIGALRM;
931    const SIGBUS: signal::Number = signal::SIGBUS;
932    const SIGCHLD: signal::Number = signal::SIGCHLD;
933    const SIGCLD: Option<signal::Number> = Some(signal::SIGCLD);
934    const SIGCONT: signal::Number = signal::SIGCONT;
935    const SIGEMT: Option<signal::Number> = Some(signal::SIGEMT);
936    const SIGFPE: signal::Number = signal::SIGFPE;
937    const SIGHUP: signal::Number = signal::SIGHUP;
938    const SIGILL: signal::Number = signal::SIGILL;
939    const SIGINFO: Option<signal::Number> = Some(signal::SIGINFO);
940    const SIGINT: signal::Number = signal::SIGINT;
941    const SIGIO: Option<signal::Number> = Some(signal::SIGIO);
942    const SIGIOT: signal::Number = signal::SIGIOT;
943    const SIGKILL: signal::Number = signal::SIGKILL;
944    const SIGLOST: Option<signal::Number> = Some(signal::SIGLOST);
945    const SIGPIPE: signal::Number = signal::SIGPIPE;
946    const SIGPOLL: Option<signal::Number> = Some(signal::SIGPOLL);
947    const SIGPROF: signal::Number = signal::SIGPROF;
948    const SIGPWR: Option<signal::Number> = Some(signal::SIGPWR);
949    const SIGQUIT: signal::Number = signal::SIGQUIT;
950    const SIGSEGV: signal::Number = signal::SIGSEGV;
951    const SIGSTKFLT: Option<signal::Number> = Some(signal::SIGSTKFLT);
952    const SIGSTOP: signal::Number = signal::SIGSTOP;
953    const SIGSYS: signal::Number = signal::SIGSYS;
954    const SIGTERM: signal::Number = signal::SIGTERM;
955    const SIGTHR: Option<signal::Number> = Some(signal::SIGTHR);
956    const SIGTRAP: signal::Number = signal::SIGTRAP;
957    const SIGTSTP: signal::Number = signal::SIGTSTP;
958    const SIGTTIN: signal::Number = signal::SIGTTIN;
959    const SIGTTOU: signal::Number = signal::SIGTTOU;
960    const SIGURG: signal::Number = signal::SIGURG;
961    const SIGUSR1: signal::Number = signal::SIGUSR1;
962    const SIGUSR2: signal::Number = signal::SIGUSR2;
963    const SIGVTALRM: signal::Number = signal::SIGVTALRM;
964    const SIGWINCH: signal::Number = signal::SIGWINCH;
965    const SIGXCPU: signal::Number = signal::SIGXCPU;
966    const SIGXFSZ: signal::Number = signal::SIGXFSZ;
967
968    fn sigrt_range(&self) -> Option<RangeInclusive<Number>> {
969        Some(signal::SIGRTMIN..=signal::SIGRTMAX)
970    }
971}
972
973impl GetPid for VirtualSystem {
974    /// Currently, this function always returns `Pid(2)` if the process exists.
975    fn getsid(&self, pid: Pid) -> Result<Pid> {
976        self.state
977            .borrow()
978            .processes
979            .get(&pid)
980            .map_or(Err(Errno::ESRCH), |_| Ok(Pid(2)))
981    }
982
983    fn getpid(&self) -> Pid {
984        self.process_id
985    }
986
987    fn getppid(&self) -> Pid {
988        self.current_process().ppid
989    }
990
991    fn getpgrp(&self) -> Pid {
992        self.current_process().pgid
993    }
994}
995
996impl SetPgid for VirtualSystem {
997    /// Modifies the process group ID of a process.
998    ///
999    /// The current implementation does not yet support the concept of sessions.
1000    fn setpgid(&self, mut pid: Pid, mut pgid: Pid) -> Result<()> {
1001        if pgid.0 < 0 {
1002            return Err(Errno::EINVAL);
1003        }
1004        if pid.0 == 0 {
1005            pid = self.process_id;
1006        }
1007        if pgid.0 == 0 {
1008            pgid = pid;
1009        }
1010
1011        let mut state = self.state.borrow_mut();
1012        if pgid != pid && !state.processes.values().any(|p| p.pgid == pgid) {
1013            return Err(Errno::EPERM);
1014        }
1015        let process = state.processes.get_mut(&pid).ok_or(Errno::ESRCH)?;
1016        if pid != self.process_id && process.ppid != self.process_id {
1017            return Err(Errno::ESRCH);
1018        }
1019        if process.last_exec.is_some() {
1020            return Err(Errno::EACCES);
1021        }
1022
1023        process.pgid = pgid;
1024        Ok(())
1025        // TODO Support sessions
1026    }
1027}
1028
1029impl Sigmask for VirtualSystem {
1030    type Sigset = Sigset;
1031
1032    fn sigmask(
1033        &self,
1034        op: Option<(SigmaskOp, &Sigset)>,
1035        old_mask: Option<&mut Sigset>,
1036    ) -> impl Future<Output = Result<()>> + use<> {
1037        let state_changed = {
1038            let mut state = self.state.borrow_mut();
1039            let process = state
1040                .processes
1041                .get_mut(&self.process_id)
1042                .expect("the current process should be in the system state");
1043
1044            if let Some(old_mask) = old_mask {
1045                old_mask.clone_from(process.blocked_signals());
1046            }
1047
1048            if let Some((op, mask)) = op {
1049                let result = process.block_signals(op, mask.iter().copied());
1050                if result.process_state_changed {
1051                    let parent_pid = process.ppid;
1052                    raise_sigchld(&mut state, parent_pid);
1053                }
1054                result.process_state_changed
1055            } else {
1056                false
1057            }
1058        };
1059
1060        let system = self.clone();
1061        async move {
1062            if state_changed {
1063                system.block_until_running().await;
1064            }
1065            Ok(())
1066        }
1067    }
1068}
1069
1070impl GetSigaction for VirtualSystem {
1071    fn get_sigaction(&self, signal: signal::Number) -> Result<Disposition> {
1072        let process = self.current_process();
1073        Ok(process.disposition(signal))
1074    }
1075}
1076
1077impl Sigaction for VirtualSystem {
1078    fn sigaction(&self, signal: signal::Number, disposition: Disposition) -> Result<Disposition> {
1079        let mut process = self.current_process_mut();
1080        Ok(process.set_disposition(signal, disposition))
1081    }
1082}
1083
1084impl CaughtSignals for VirtualSystem {
1085    fn caught_signals(&self) -> Vec<signal::Number> {
1086        std::mem::take(&mut self.current_process_mut().caught_signals)
1087    }
1088}
1089
1090impl SendSignal for VirtualSystem {
1091    /// Sends a signal to the target process.
1092    ///
1093    /// The current implementation accepts any positive signal number and `None`
1094    /// (no signal) for `signal`. Negative signal numbers are rejected with
1095    /// `Errno::EINVAL`.
1096    ///
1097    /// This function returns a future that enables the executor to block the
1098    /// calling thread until the current process is ready to proceed. If the
1099    /// signal is sent to the current process and it causes the process to stop,
1100    /// the future will be ready only when the process is resumed. Similarly, if
1101    /// the signal causes the current process to terminate, the future will
1102    /// never be ready.
1103    fn kill(
1104        &self,
1105        target: Pid,
1106        signal: Option<signal::Number>,
1107    ) -> impl Future<Output = Result<()>> + use<> {
1108        let result = 'result: {
1109            if let Some(signal) = signal {
1110                // Validate the signal number
1111                if signal.as_raw() < 0 {
1112                    break 'result Err(Errno::EINVAL);
1113                }
1114            }
1115
1116            match target {
1117                Pid::MY_PROCESS_GROUP => {
1118                    let target_pgid = self.current_process().pgid;
1119                    send_signal_to_processes(
1120                        &mut self.state.borrow_mut(),
1121                        Some(target_pgid),
1122                        signal,
1123                    )
1124                }
1125
1126                Pid::ALL => send_signal_to_processes(&mut self.state.borrow_mut(), None, signal),
1127
1128                Pid(raw_pid) if raw_pid >= 0 => {
1129                    let mut state = self.state.borrow_mut();
1130                    match state.processes.get_mut(&target) {
1131                        Some(process) => {
1132                            if let Some(signal) = signal {
1133                                let result = process.raise_signal(signal);
1134                                if result.process_state_changed {
1135                                    let parent_pid = process.ppid;
1136                                    raise_sigchld(&mut state, parent_pid);
1137                                }
1138                            }
1139                            Ok(())
1140                        }
1141                        None => Err(Errno::ESRCH),
1142                    }
1143                }
1144
1145                Pid(negative_pgid) => {
1146                    let target_pgid = Pid(-negative_pgid);
1147                    send_signal_to_processes(
1148                        &mut self.state.borrow_mut(),
1149                        Some(target_pgid),
1150                        signal,
1151                    )
1152                }
1153            }
1154        };
1155
1156        let system = self.clone();
1157        async move {
1158            system.block_until_running().await;
1159            result
1160        }
1161    }
1162
1163    fn raise(&self, signal: signal::Number) -> impl Future<Output = Result<()>> + use<> {
1164        let target = self.process_id;
1165        self.kill(target, Some(signal))
1166    }
1167}
1168
1169impl Isatty for VirtualSystem {
1170    fn isatty(&self, fd: Fd) -> bool {
1171        self.with_open_file_description(fd, |ofd| {
1172            Ok(matches!(
1173                &ofd.inode().borrow().body,
1174                FileBody::Terminal { .. }
1175            ))
1176        })
1177        .unwrap_or(false)
1178    }
1179}
1180
1181impl TcGetPgrp for VirtualSystem {
1182    /// Returns the current foreground process group ID.
1183    ///
1184    /// The current implementation does not yet support the concept of
1185    /// controlling terminals and sessions. It accepts any open file descriptor.
1186    fn tcgetpgrp(&self, fd: Fd) -> Result<Pid> {
1187        // Make sure the FD is open
1188        self.with_open_file_description(fd, |_| Ok(()))?;
1189
1190        self.state.borrow().foreground.ok_or(Errno::ENOTTY)
1191    }
1192}
1193
1194impl TcSetPgrp for VirtualSystem {
1195    /// Switches the foreground process.
1196    ///
1197    /// The current implementation does not yet support the concept of
1198    /// controlling terminals and sessions. It accepts any open file descriptor.
1199    fn tcsetpgrp(&self, fd: Fd, pgid: Pid) -> impl Future<Output = Result<()>> + use<> {
1200        fn inner(system: &VirtualSystem, fd: Fd, pgid: Pid) -> Result<()> {
1201            // Make sure the FD is open
1202            system.with_open_file_description(fd, |_| Ok(()))?;
1203
1204            // Make sure the process group exists
1205            let mut state = system.state.borrow_mut();
1206            if !state.processes.values().any(|p| p.pgid == pgid) {
1207                return Err(Errno::EPERM);
1208            }
1209
1210            // TODO: Suspend the calling process group if it is in the background
1211            // and not ignoring or blocking SIGTTOU.
1212
1213            state.foreground = Some(pgid);
1214            Ok(())
1215        }
1216
1217        ready(inner(self, fd, pgid))
1218    }
1219}
1220
1221impl Fork for VirtualSystem {
1222    /// Runs a task in a new child process.
1223    ///
1224    /// This method implements [`Fork::run_in_child_process`] by adding a new
1225    /// process to the system state and running the given task concurrently in
1226    /// the same real process. It does not create any real child process.
1227    ///
1228    /// To run the concurrent task, this function needs an executor that has
1229    /// been set in the [`SystemState`]. If the system state does not have an
1230    /// executor, this function fails with `Errno::ENOSYS`. If the executor
1231    /// fails to spawn the child task, this function fails with `Errno::EAGAIN`.
1232    ///
1233    /// The child task must terminate itself by calling [`Exit::exit`] or
1234    /// killing itself with a signal. The task returning without doing so will
1235    /// cause a panic.
1236    ///
1237    /// The process ID of the child will be the maximum of existing process IDs
1238    /// plus 1. If there are no other processes, it will be 2.
1239    fn run_in_child_process<D, F>(&self, shared_data: D, child_task: F) -> (Result<Pid>, D)
1240    where
1241        D: Clone + 'static,
1242        F: AsyncFnOnce(Self, D) + 'static,
1243    {
1244        let mut state = self.state.borrow_mut();
1245        let Some(executor) = state.executor.as_ref() else {
1246            return (Err(Errno::ENOSYS), shared_data);
1247        };
1248        let child_process_id = state
1249            .processes
1250            .keys()
1251            .max()
1252            .map_or(Pid(2), |pid| Pid(pid.0 + 1));
1253        let child_system = VirtualSystem {
1254            state: Rc::clone(&self.state),
1255            process_id: child_process_id,
1256        };
1257        let data_clone = shared_data.clone();
1258        let child_task = async move {
1259            child_task(child_system.clone(), data_clone).await;
1260            assert!(
1261                !child_system.current_process().state().is_alive(),
1262                "the child task should have terminated itself by exiting or signaling itself: pid={}",
1263                child_system.process_id
1264            );
1265        };
1266
1267        if executor.spawn(Box::pin(child_task)).is_err() {
1268            return (Err(Errno::EAGAIN), shared_data);
1269        }
1270
1271        let parent_process = &state.processes[&self.process_id];
1272        let child_process = Process::fork_from(self.process_id, parent_process);
1273        state.processes.insert(child_process_id, child_process);
1274
1275        (Ok(child_process_id), shared_data)
1276    }
1277}
1278
1279impl Wait for VirtualSystem {
1280    /// Waits for a child.
1281    ///
1282    /// TODO: Currently, this function only supports `target == -1 || target > 0`.
1283    fn wait(&self, target: Pid) -> Result<Option<(Pid, ProcessState)>> {
1284        let parent_pid = self.process_id;
1285        let mut state = self.state.borrow_mut();
1286        if let Some((pid, process)) = state.child_to_wait_for(parent_pid, target) {
1287            if process.state_has_changed() {
1288                Ok(Some((pid, process.take_state())))
1289            } else if process.state().is_alive() {
1290                Ok(None)
1291            } else {
1292                Err(Errno::ECHILD)
1293            }
1294        } else {
1295            Err(Errno::ECHILD)
1296        }
1297    }
1298}
1299
1300impl Exec for VirtualSystem {
1301    /// Stub for the `execve` system call.
1302    ///
1303    /// The `execve` system call cannot be simulated in the userland. This
1304    /// function returns `ENOSYS` if the file at `path` is a native executable,
1305    /// `ENOEXEC` if a non-executable file, and `ENOENT` otherwise.
1306    fn execve<A, E>(
1307        &self,
1308        path: &CStr,
1309        args: A,
1310        envs: E,
1311    ) -> impl Future<Output = Result<Infallible>> + use<A, E>
1312    where
1313        A: IntoCStrArray,
1314        E: IntoCStrArray,
1315    {
1316        fn inner<A, E>(
1317            this: &VirtualSystem,
1318            path: &CStr,
1319            args: A,
1320            envs: E,
1321        ) -> impl Future<Output = Result<Infallible>> + use<A, E>
1322        where
1323            A: AsCStrArray,
1324            E: AsCStrArray,
1325        {
1326            let os_path = UnixStr::from_bytes(path.to_bytes());
1327            let mut state = this.state.borrow_mut();
1328            let fs = &state.file_system;
1329            let file = match fs.get(os_path) {
1330                Ok(file) => file,
1331                Err(e) => return ready(Err(e)),
1332            };
1333            // TODO Check file permissions
1334            let is_executable = matches!(
1335                &file.borrow().body,
1336                FileBody::Regular {
1337                    is_native_executable: true,
1338                    ..
1339                }
1340            );
1341            if is_executable {
1342                // Save arguments in the Process
1343                let process = state.processes.get_mut(&this.process_id).unwrap();
1344                let path = path.to_owned();
1345                let args = args.into_c_str_array().to_vec();
1346                let envs = envs.into_c_str_array().to_vec();
1347                process.last_exec = Some((path, args, envs));
1348
1349                // TODO: We should abort the currently running task and start the new one.
1350                // Just returning `pending()` would break existing tests that rely on
1351                // the current behavior.
1352                ready(Err(Errno::ENOSYS))
1353            } else {
1354                ready(Err(Errno::ENOEXEC))
1355            }
1356        }
1357
1358        inner(self, path, args.into_c_str_array(), envs.into_c_str_array())
1359    }
1360}
1361
1362impl Exit for VirtualSystem {
1363    fn exit(&self, exit_status: ExitStatus) -> impl Future<Output = Infallible> + use<> {
1364        let mut myself = self.current_process_mut();
1365        let parent_pid = myself.ppid;
1366        let exited = myself.set_state(ProcessState::exited(exit_status));
1367        drop(myself);
1368        if exited {
1369            raise_sigchld(&mut self.state.borrow_mut(), parent_pid);
1370        }
1371
1372        pending()
1373    }
1374}
1375
1376impl GetUid for VirtualSystem {
1377    fn getuid(&self) -> Uid {
1378        self.current_process().uid()
1379    }
1380
1381    fn geteuid(&self) -> Uid {
1382        self.current_process().euid()
1383    }
1384
1385    fn getgid(&self) -> Gid {
1386        self.current_process().gid()
1387    }
1388
1389    fn getegid(&self) -> Gid {
1390        self.current_process().egid()
1391    }
1392}
1393
1394impl GetPw for VirtualSystem {
1395    fn getpwnam_dir(&self, name: &CStr) -> Result<Option<PathBuf>> {
1396        let state = self.state.borrow();
1397        let name = match name.to_str() {
1398            Ok(name) => name,
1399            Err(_utf8_error) => return Ok(None),
1400        };
1401        Ok(state.home_dirs.get(name).cloned())
1402    }
1403}
1404
1405impl Sysconf for VirtualSystem {
1406    /// Returns the standard path for the system.
1407    ///
1408    /// This function returns the value of [`SystemState::path`]. If it is empty,
1409    /// it returns the `ENOSYS` error.
1410    fn confstr_path(&self) -> Result<UnixString> {
1411        let path = self.state.borrow().path.clone();
1412        if path.is_empty() {
1413            Err(Errno::ENOSYS)
1414        } else {
1415            Ok(path)
1416        }
1417    }
1418}
1419
1420impl ShellPath for VirtualSystem {
1421    /// Returns the path to the shell.
1422    ///
1423    /// The current implementation returns "/bin/sh".
1424    fn shell_path(&self) -> CString {
1425        c"/bin/sh".to_owned()
1426    }
1427}
1428
1429impl GetRlimit for VirtualSystem {
1430    fn getrlimit(&self, resource: Resource) -> Result<LimitPair> {
1431        Ok(self
1432            .current_process()
1433            .resource_limits
1434            .get(&resource)
1435            .copied()
1436            .unwrap_or(LimitPair {
1437                soft: INFINITY,
1438                hard: INFINITY,
1439            }))
1440    }
1441}
1442
1443impl SetRlimit for VirtualSystem {
1444    fn setrlimit(&self, resource: Resource, limits: LimitPair) -> Result<()> {
1445        if limits.soft_exceeds_hard() {
1446            return Err(Errno::EINVAL);
1447        }
1448
1449        let mut process = self.current_process_mut();
1450        use std::collections::hash_map::Entry::{Occupied, Vacant};
1451        match process.resource_limits.entry(resource) {
1452            Occupied(occupied) => {
1453                let occupied = occupied.into_mut();
1454                if limits.hard > occupied.hard {
1455                    return Err(Errno::EPERM);
1456                }
1457                *occupied = limits;
1458            }
1459            Vacant(vacant) => {
1460                vacant.insert(limits);
1461            }
1462        }
1463        Ok(())
1464    }
1465}
1466
1467fn send_signal_to_processes(
1468    state: &mut SystemState,
1469    target_pgid: Option<Pid>,
1470    signal: Option<signal::Number>,
1471) -> Result<()> {
1472    let mut results = Vec::new();
1473
1474    for (&_pid, process) in &mut state.processes {
1475        if target_pgid.is_none_or(|target_pgid| process.pgid == target_pgid) {
1476            let result = if let Some(signal) = signal {
1477                process.raise_signal(signal)
1478            } else {
1479                SignalResult::default()
1480            };
1481            results.push((result, process.ppid));
1482        }
1483    }
1484
1485    if results.is_empty() {
1486        Err(Errno::ESRCH)
1487    } else {
1488        for (result, ppid) in results {
1489            if result.process_state_changed {
1490                raise_sigchld(state, ppid);
1491            }
1492        }
1493        Ok(())
1494    }
1495}
1496
1497fn raise_sigchld(state: &mut SystemState, target_pid: Pid) {
1498    if let Some(target) = state.processes.get_mut(&target_pid) {
1499        let result = target.raise_signal(signal::SIGCHLD);
1500        assert!(!result.process_state_changed);
1501    }
1502}
1503
1504/// State of the virtual system
1505#[derive(Clone, Debug, Default)]
1506pub struct SystemState {
1507    /// Current time
1508    ///
1509    /// To make sure scheduled wakers are woken up at the right time, use
1510    /// [`advance_time`](Self::advance_time) to update the current time instead
1511    /// of directly modifying this field.
1512    pub now: Option<Instant>,
1513
1514    /// Priority queue of wakers scheduled to be woken up at specific times
1515    pub scheduled_wakers: ScheduledWakerQueue,
1516
1517    /// Consumed CPU time statistics
1518    pub times: CpuTimes,
1519
1520    /// Task manager that can execute asynchronous tasks
1521    ///
1522    /// The virtual system uses this executor to run (virtual) child processes.
1523    /// If `executor` is `None`, [`VirtualSystem::run_in_child_process`] will
1524    /// fail.
1525    pub executor: Option<Rc<dyn Executor>>,
1526
1527    /// Processes running in the system
1528    pub processes: BTreeMap<Pid, Process>,
1529
1530    /// Process group ID of the foreground process group
1531    ///
1532    /// Note: The current implementation does not support the notion of
1533    /// controlling terminals and sessions. This item may be replaced with a
1534    /// more _correct_ implementation in the future.
1535    pub foreground: Option<Pid>,
1536
1537    /// Collection of files existing in the virtual system
1538    pub file_system: FileSystem,
1539
1540    /// Map from user names to their home directory paths
1541    ///
1542    /// [`VirtualSystem::getpwnam_dir`] looks up its argument in this
1543    /// dictionary.
1544    pub home_dirs: HashMap<String, PathBuf>,
1545
1546    /// Standard path returned by [`VirtualSystem::confstr_path`]
1547    pub path: UnixString,
1548}
1549
1550impl SystemState {
1551    /// Sets the current time to `new_current_time` and wakes up any wakers
1552    /// scheduled to be woken up by that time.
1553    pub fn advance_time(&mut self, new_current_time: Instant) {
1554        self.now = Some(new_current_time);
1555        self.scheduled_wakers.wake(new_current_time);
1556    }
1557
1558    /// Finds a child process to wait for.
1559    ///
1560    /// This is a helper function for `VirtualSystem::wait`.
1561    fn child_to_wait_for(&mut self, parent_pid: Pid, target: Pid) -> Option<(Pid, &mut Process)> {
1562        match target.0 {
1563            0 => todo!("wait target {}", target),
1564            -1 => {
1565                // any child
1566                let mut result = None;
1567                for (pid, process) in &mut self.processes {
1568                    if process.ppid == parent_pid {
1569                        let changed = process.state_has_changed();
1570                        result = Some((*pid, process));
1571                        if changed {
1572                            break;
1573                        }
1574                    }
1575                }
1576                result
1577            }
1578            raw if raw >= 0 => {
1579                let process = self.processes.get_mut(&target)?;
1580                if process.ppid == parent_pid {
1581                    Some((target, process))
1582                } else {
1583                    None
1584                }
1585            }
1586            _target => todo!("wait target {}", target),
1587        }
1588    }
1589}
1590
1591/// Executor that can start new async tasks.
1592///
1593/// This trait abstracts the executor interface so that [`SystemState`] does not
1594/// depend on a specific executor implementation.
1595///
1596/// Note that [`VirtualSystem`] does not support multi-threading. The executor
1597/// should run concurrent tasks on a single thread.
1598pub trait Executor: Debug {
1599    /// Starts a new async task.
1600    ///
1601    /// Returns `Ok(())` if the task has been started successfully and `Err(_)`
1602    /// otherwise.
1603    fn spawn(
1604        &self,
1605        task: Pin<Box<dyn Future<Output = ()>>>,
1606    ) -> std::result::Result<(), Box<dyn std::error::Error>>;
1607}
1608
1609#[cfg(test)]
1610mod tests {
1611    use super::*;
1612    use crate::Env;
1613    use crate::job::ProcessResult;
1614    use crate::system::FileType;
1615    use crate::system::r#virtual::PIPE_SIZE;
1616    use crate::test_helper::WakeFlag;
1617    use assert_matches::assert_matches;
1618    use futures_executor::LocalPool;
1619    use futures_util::FutureExt as _;
1620    use std::future::pending;
1621    use std::pin::pin;
1622    use std::sync::Arc;
1623    use std::task::Context;
1624    use std::task::Poll::{Pending, Ready};
1625
1626    #[test]
1627    fn fstatat_non_existent_file() {
1628        let system = VirtualSystem::new();
1629        assert_matches!(
1630            system.fstatat(Fd(0), c"/no/such/file", true),
1631            Err(Errno::ENOENT)
1632        );
1633    }
1634
1635    #[test]
1636    fn fstatat_regular_file() {
1637        let system = VirtualSystem::new();
1638        let path = "/some/file";
1639        let content = Rc::new(RefCell::new(Inode::new([1, 2, 3, 42, 100])));
1640        let mut state = system.state.borrow_mut();
1641        state.file_system.save(path, content).unwrap();
1642        drop(state);
1643
1644        let stat = system.fstatat(Fd(0), c"/some/file", true).unwrap();
1645        assert_eq!(stat.mode, Mode::default());
1646        assert_eq!(stat.r#type, FileType::Regular);
1647        assert_eq!(stat.size, 5);
1648        // TODO Other stat properties
1649    }
1650
1651    #[test]
1652    fn fstatat_directory() {
1653        let system = VirtualSystem::new();
1654        let path = "/some/file";
1655        let content = Rc::new(RefCell::new(Inode::new([])));
1656        let mut state = system.state.borrow_mut();
1657        state.file_system.save(path, content).unwrap();
1658        drop(state);
1659
1660        let stat = system.fstatat(Fd(0), c"/some/", true).unwrap();
1661        assert_eq!(stat.mode, Mode::from_bits_retain(0o755));
1662        assert_eq!(stat.r#type, FileType::Directory);
1663        // TODO Other stat properties
1664    }
1665
1666    #[test]
1667    fn fstatat_fifo() {
1668        let system = VirtualSystem::new();
1669        let path = "/some/fifo";
1670        let content = Rc::new(RefCell::new(Inode {
1671            body: FileBody::Fifo {
1672                content: [17; 42].into(),
1673                readers: 0,
1674                writers: 0,
1675                pending_open_wakers: WakerSet::new(),
1676                pending_read_wakers: WakerSet::new(),
1677                pending_write_wakers: WakerSet::new(),
1678            },
1679            permissions: Mode::default(),
1680        }));
1681        let mut state = system.state.borrow_mut();
1682        state.file_system.save(path, content).unwrap();
1683        drop(state);
1684
1685        let stat = system.fstatat(Fd(0), c"/some/fifo", true).unwrap();
1686        assert_eq!(stat.mode, Mode::default());
1687        assert_eq!(stat.r#type, FileType::Fifo);
1688        assert_eq!(stat.size, 42);
1689    }
1690
1691    fn system_with_symlink() -> VirtualSystem {
1692        let system = VirtualSystem::new();
1693        let mut state = system.state.borrow_mut();
1694        state
1695            .file_system
1696            .save("/some/file", Rc::new(RefCell::new(Inode::new([]))))
1697            .unwrap();
1698        state
1699            .file_system
1700            .save(
1701                "/link",
1702                Rc::new(RefCell::new(Inode {
1703                    body: FileBody::Symlink {
1704                        target: "some/file".into(),
1705                    },
1706                    permissions: Mode::default(),
1707                })),
1708            )
1709            .unwrap();
1710        drop(state);
1711        system
1712    }
1713
1714    #[test]
1715    fn fstatat_symlink_to_regular_file() {
1716        let system = system_with_symlink();
1717        let stat = system.fstatat(Fd(0), c"/link", true).unwrap();
1718        assert_eq!(stat.r#type, FileType::Regular);
1719    }
1720
1721    #[test]
1722    fn fstatat_symlink_no_follow() {
1723        let system = system_with_symlink();
1724        let stat = system.fstatat(Fd(0), c"/link", false).unwrap();
1725        assert_eq!(stat.r#type, FileType::Symlink);
1726    }
1727
1728    #[test]
1729    fn is_executable_file_non_existing_file() {
1730        let system = VirtualSystem::new();
1731        assert!(!system.is_executable_file(c"/no/such/file"));
1732    }
1733
1734    #[test]
1735    fn is_executable_file_existing_but_non_executable_file() {
1736        let system = VirtualSystem::new();
1737        let path = "/some/file";
1738        let content = Rc::new(RefCell::new(Inode::default()));
1739        let mut state = system.state.borrow_mut();
1740        state.file_system.save(path, content).unwrap();
1741        drop(state);
1742        assert!(!system.is_executable_file(c"/some/file"));
1743    }
1744
1745    #[test]
1746    fn is_executable_file_with_executable_file() {
1747        let system = VirtualSystem::new();
1748        let path = "/some/file";
1749        let mut content = Inode::default();
1750        content.permissions.set(Mode::USER_EXEC, true);
1751        let content = Rc::new(RefCell::new(content));
1752        let mut state = system.state.borrow_mut();
1753        state.file_system.save(path, content).unwrap();
1754        drop(state);
1755        assert!(system.is_executable_file(c"/some/file"));
1756    }
1757
1758    #[test]
1759    fn pipe_read_write() {
1760        let system = VirtualSystem::new();
1761        let (reader, writer) = system.pipe().unwrap();
1762        let result = system.write(writer, &[5, 42, 29]).now_or_never().unwrap();
1763        assert_eq!(result, Ok(3));
1764
1765        let mut buffer = [1; 4];
1766        let result = system.read(reader, &mut buffer).now_or_never().unwrap();
1767        assert_eq!(result, Ok(3));
1768        assert_eq!(buffer, [5, 42, 29, 1]);
1769
1770        let result = system.close(writer);
1771        assert_eq!(result, Ok(()));
1772
1773        let result = system.read(reader, &mut buffer).now_or_never().unwrap();
1774        assert_eq!(result, Ok(0));
1775    }
1776
1777    #[test]
1778    fn read_returns_eintr_when_interrupted_by_signal() {
1779        let system = VirtualSystem::new();
1780        system.sigaction(SIGINT, Disposition::Catch).unwrap();
1781        let (reader, _writer) = system.pipe().unwrap();
1782
1783        let woken = Arc::new(WakeFlag::new());
1784        let waker = std::task::Waker::from(woken.clone());
1785        let mut context = Context::from_waker(&waker);
1786
1787        let mut buffer = [0; 4];
1788        let mut read_fut = pin!(system.read(reader, &mut buffer));
1789
1790        // First poll: no data, should be pending
1791        assert_eq!(read_fut.as_mut().poll(&mut context), Pending);
1792
1793        // Deliver a signal
1794        let _ = system.current_process_mut().raise_signal(SIGINT);
1795        assert!(woken.is_woken(), "signal should have woken the read task");
1796
1797        // Second poll: signal delivered, should return EINTR
1798        let woken = Arc::new(WakeFlag::new());
1799        let waker = std::task::Waker::from(woken.clone());
1800        let mut context = Context::from_waker(&waker);
1801        assert_eq!(read_fut.poll(&mut context), Ready(Err(Errno::EINTR)));
1802    }
1803
1804    #[test]
1805    fn read_does_not_return_eintr_if_data_available_despite_pending_signal() {
1806        let system = VirtualSystem::new();
1807        system.sigaction(SIGINT, Disposition::Catch).unwrap();
1808        let (reader, writer) = system.pipe().unwrap();
1809        // Write data before the signal
1810        system
1811            .write(writer, &[1, 2, 3])
1812            .now_or_never()
1813            .unwrap()
1814            .unwrap();
1815        // Also deliver a signal
1816        let _ = system.current_process_mut().raise_signal(SIGINT);
1817
1818        let woken = Arc::new(WakeFlag::new());
1819        let waker = std::task::Waker::from(woken.clone());
1820        let mut context = Context::from_waker(&waker);
1821
1822        let mut buffer = [0; 4];
1823        let read_fut = pin!(system.read(reader, &mut buffer));
1824        // Data is immediately available; should NOT return EINTR
1825        assert_eq!(read_fut.poll(&mut context), Ready(Ok(3)));
1826    }
1827
1828    #[test]
1829    fn write_returns_eintr_when_interrupted_by_signal() {
1830        let system = VirtualSystem::new();
1831        system.sigaction(SIGINT, Disposition::Catch).unwrap();
1832        let (_reader, writer) = system.pipe().unwrap();
1833        // Fill the pipe to make the next write block
1834        system
1835            .write(writer, &[0u8; PIPE_SIZE])
1836            .now_or_never()
1837            .unwrap()
1838            .unwrap();
1839
1840        let woken = Arc::new(WakeFlag::new());
1841        let waker = std::task::Waker::from(woken.clone());
1842        let mut context = Context::from_waker(&waker);
1843
1844        let mut write_fut = pin!(system.write(writer, &[1]));
1845
1846        // First poll: no space, should be pending
1847        assert_eq!(write_fut.as_mut().poll(&mut context), Pending);
1848
1849        // Deliver a signal
1850        let _ = system.current_process_mut().raise_signal(SIGINT);
1851        assert!(woken.is_woken(), "signal should have woken the write task");
1852
1853        // Second poll: signal delivered, should return EINTR
1854        let woken = Arc::new(WakeFlag::new());
1855        let waker = std::task::Waker::from(woken.clone());
1856        let mut context = Context::from_waker(&waker);
1857        assert_eq!(write_fut.poll(&mut context), Ready(Err(Errno::EINTR)));
1858    }
1859
1860    #[test]
1861    fn write_returns_partial_count_when_interrupted_by_signal_after_partial_write() {
1862        // Write a buffer larger than PIPE_SIZE (= 2 * PIPE_BUF) to a blocking
1863        // pipe.  The pipe can hold PIPE_SIZE bytes; after those are written the
1864        // write blocks waiting for room for the rest.  When a signal arrives
1865        // during that blocking phase, the write must return the count of bytes
1866        // already written rather than Err(EINTR).
1867        let system = VirtualSystem::new();
1868        system.sigaction(SIGINT, Disposition::Catch).unwrap();
1869        let (reader, writer) = system.pipe().unwrap();
1870
1871        // A buffer larger than the pipe can hold: the write will fill the pipe
1872        // and then block waiting for space.
1873        let big_buf: Vec<u8> = (0..2 * PIPE_SIZE).map(|i| i as u8).collect();
1874
1875        let woken = Arc::new(WakeFlag::new());
1876        let waker = std::task::Waker::from(woken.clone());
1877        let mut context = Context::from_waker(&waker);
1878        let mut write_fut = pin!(system.write(writer, &big_buf));
1879
1880        // First poll: writes PIPE_SIZE bytes (filling the pipe), then blocks.
1881        assert_eq!(write_fut.as_mut().poll(&mut context), Pending);
1882
1883        // A signal arrives while the write is blocking.
1884        let _ = system.current_process_mut().raise_signal(SIGINT);
1885        assert!(woken.is_woken(), "signal should have woken the write task");
1886
1887        // Second poll: the write returns the bytes written so far, not EINTR.
1888        let woken = Arc::new(WakeFlag::new());
1889        let waker = std::task::Waker::from(woken.clone());
1890        let mut context = Context::from_waker(&waker);
1891        assert_eq!(
1892            write_fut.poll(&mut context),
1893            Ready(Ok(PIPE_SIZE)),
1894            "expected partial byte count written before the signal"
1895        );
1896
1897        // The bytes already written should be available for reading.
1898        let mut read_buf = [0u8; PIPE_SIZE];
1899        assert_eq!(
1900            system.read(reader, &mut read_buf).now_or_never().unwrap(),
1901            Ok(PIPE_SIZE)
1902        );
1903        assert_eq!(read_buf, big_buf[..PIPE_SIZE]);
1904
1905        // The signal must have been recorded as caught.
1906        assert_eq!(
1907            system.current_process().caught_signals,
1908            [SIGINT],
1909            "SIGINT should be in the caught signals list"
1910        );
1911    }
1912
1913    #[test]
1914    fn write_large_buffer_completes_fully_without_signal() {
1915        // A large write in blocking mode (without a signal) must eventually
1916        // transfer the entire buffer.  The pipe can hold PIPE_SIZE bytes at a
1917        // time; the write fills it and blocks, a reader drains it, and the
1918        // pattern repeats until all bytes are written.
1919        let big_buf: Vec<u8> = (0..2 * PIPE_SIZE).map(|i| i as u8).collect();
1920        let system = VirtualSystem::new();
1921        let (reader, writer) = system.pipe().unwrap();
1922
1923        let woken = Arc::new(WakeFlag::new());
1924        let waker = std::task::Waker::from(woken.clone());
1925        let mut context = Context::from_waker(&waker);
1926        let mut write_fut = pin!(system.write(writer, &big_buf));
1927
1928        // First poll: fills the pipe (PIPE_SIZE bytes), then blocks.
1929        assert_eq!(write_fut.as_mut().poll(&mut context), Pending);
1930
1931        // Drain the pipe so the write can continue.
1932        let mut read_buf = [0u8; PIPE_SIZE];
1933        assert_eq!(
1934            system.read(reader, &mut read_buf).now_or_never().unwrap(),
1935            Ok(PIPE_SIZE)
1936        );
1937        assert_eq!(read_buf, big_buf[..PIPE_SIZE]);
1938        assert!(
1939            woken.is_woken(),
1940            "draining the pipe should wake the write task"
1941        );
1942
1943        // Second poll: writes the remaining bytes.
1944        let woken = Arc::new(WakeFlag::new());
1945        let waker = std::task::Waker::from(woken.clone());
1946        let mut context = Context::from_waker(&waker);
1947        assert_eq!(
1948            write_fut.poll(&mut context),
1949            Ready(Ok(2 * PIPE_SIZE)),
1950            "large write without signal must complete the full buffer"
1951        );
1952
1953        // The second half of the buffer must also be readable from the pipe.
1954        let mut read_buf2 = [0u8; PIPE_SIZE];
1955        assert_eq!(
1956            system.read(reader, &mut read_buf2).now_or_never().unwrap(),
1957            Ok(PIPE_SIZE)
1958        );
1959        assert_eq!(read_buf2, big_buf[PIPE_SIZE..]);
1960    }
1961
1962    #[test]
1963    fn write_returns_eintr_when_space_and_signal_arrive_simultaneously() {
1964        // When a write is blocked on a full pipe, both the availability of
1965        // space in the pipe and the delivery of a signal will wake the write
1966        // task. In the current implementation, if both events occur before the
1967        // next poll, the signal takes precedence and the write returns EINTR
1968        // without writing any more bytes. This test verifies that behavior.
1969        let system = VirtualSystem::new();
1970        system.sigaction(SIGINT, Disposition::Catch).unwrap();
1971        let (reader, writer) = system.pipe().unwrap();
1972        // Fill the pipe so the next write blocks.
1973        system
1974            .write(writer, &[0u8; PIPE_SIZE])
1975            .now_or_never()
1976            .unwrap()
1977            .unwrap();
1978
1979        let woken = Arc::new(WakeFlag::new());
1980        let waker = std::task::Waker::from(woken.clone());
1981        let mut context = Context::from_waker(&waker);
1982        let mut write_fut = pin!(system.write(writer, &[1]));
1983
1984        // First poll: pipe is full, should be pending.
1985        assert_eq!(write_fut.as_mut().poll(&mut context), Pending);
1986
1987        // Drain the pipe (space available, wakes the write task) and deliver a
1988        // signal before the next poll — both events occur simultaneously.
1989        let mut drain_buf = [0u8; PIPE_SIZE];
1990        system
1991            .read(reader, &mut drain_buf)
1992            .now_or_never()
1993            .unwrap()
1994            .unwrap();
1995        let _ = system.current_process_mut().raise_signal(SIGINT);
1996        assert!(woken.is_woken(), "write task should have been woken");
1997
1998        // Second poll: even though space is available, the signal should interrupt.
1999        let woken = Arc::new(WakeFlag::new());
2000        let waker = std::task::Waker::from(woken.clone());
2001        let mut context = Context::from_waker(&waker);
2002        assert_eq!(
2003            write_fut.as_mut().poll(&mut context),
2004            Ready(Err(Errno::EINTR)),
2005            "signal should interrupt write even when space is available"
2006        );
2007
2008        // A fresh read on the now-drained pipe must block (return Pending)
2009        // because the interrupted write left it empty: no bytes were
2010        // transferred when EINTR was returned.
2011        let mut probe_buf = [0u8; 1];
2012        assert_eq!(
2013            system.read(reader, &mut probe_buf).now_or_never(),
2014            None,
2015            "no bytes must be transferred when EINTR is returned"
2016        );
2017    }
2018
2019    #[test]
2020    fn write_does_not_check_signals_before_first_block() {
2021        // A signal caught *before* write is called must not cause the write to
2022        // return EINTR if the operation never had to block. The signal-
2023        // precedence policy only applies once the write has blocked at least
2024        // once.
2025        let system = VirtualSystem::new();
2026        system.sigaction(SIGINT, Disposition::Catch).unwrap();
2027        let (_reader, writer) = system.pipe().unwrap();
2028        let _ = system.current_process_mut().raise_signal(SIGINT);
2029
2030        let result = system.write(writer, &[42]).now_or_never().unwrap();
2031        assert_eq!(
2032            result,
2033            Ok(1),
2034            "write that does not block must succeed even with a pending signal"
2035        );
2036    }
2037
2038    #[test]
2039    fn write_returns_partial_count_when_space_and_signal_arrive_simultaneously() {
2040        // Verifies that when a large write has already written some bytes and then
2041        // blocked, and both space becomes available and a signal is caught before
2042        // the next poll, the write returns the byte count already written rather
2043        // than continuing to write more.
2044        let system = VirtualSystem::new();
2045        system.sigaction(SIGINT, Disposition::Catch).unwrap();
2046        let (reader, writer) = system.pipe().unwrap();
2047
2048        let big_buf: Vec<u8> = (0..2 * PIPE_SIZE).map(|i| i as u8).collect();
2049
2050        let woken = Arc::new(WakeFlag::new());
2051        let waker = std::task::Waker::from(woken.clone());
2052        let mut context = Context::from_waker(&waker);
2053        let mut write_fut = pin!(system.write(writer, &big_buf));
2054
2055        // First poll: writes PIPE_SIZE bytes (filling the pipe), then blocks.
2056        assert_eq!(write_fut.as_mut().poll(&mut context), Pending);
2057
2058        // Drain the pipe AND deliver a signal before the next poll.
2059        let mut drain_buf = [0u8; PIPE_SIZE];
2060        system
2061            .read(reader, &mut drain_buf)
2062            .now_or_never()
2063            .unwrap()
2064            .unwrap();
2065        let _ = system.current_process_mut().raise_signal(SIGINT);
2066        assert!(woken.is_woken(), "write task should have been woken");
2067
2068        // Second poll: the write should return the bytes written so far rather
2069        // than continuing to write more.
2070        let woken = Arc::new(WakeFlag::new());
2071        let waker = std::task::Waker::from(woken.clone());
2072        let mut context = Context::from_waker(&waker);
2073        assert_eq!(
2074            write_fut.poll(&mut context),
2075            Ready(Ok(PIPE_SIZE)),
2076            "signal should interrupt large write and return bytes written so far"
2077        );
2078    }
2079
2080    #[test]
2081    fn write_to_non_blocking_pipe_does_not_block() {
2082        // A write on a non-blocking FD must complete immediately and never return
2083        // Pending, even when the buffer is larger than the pipe capacity.
2084        let system = VirtualSystem::new();
2085        let (_reader, writer) = system.pipe().unwrap();
2086        system.get_and_set_nonblocking(writer, true).unwrap();
2087
2088        // Writing a buffer larger than the pipe capacity should return
2089        // immediately with the number of bytes that fit.
2090        let big_buf = vec![0u8; PIPE_SIZE * 2];
2091        let result = system.write(writer, &big_buf).now_or_never();
2092        assert_eq!(
2093            result,
2094            Some(Ok(PIPE_SIZE)),
2095            "non-blocking write should return immediately with bytes that fit"
2096        );
2097
2098        // A subsequent write to the now-full pipe should return EAGAIN immediately.
2099        let result = system.write(writer, &[1]).now_or_never();
2100        assert_eq!(
2101            result,
2102            Some(Err(Errno::EAGAIN)),
2103            "non-blocking write to full pipe should return EAGAIN"
2104        );
2105    }
2106
2107    #[test]
2108    fn dup_shares_open_file_description() {
2109        let system = VirtualSystem::new();
2110        let result = system.dup(Fd::STDOUT, Fd::STDERR, EnumSet::empty());
2111        assert_eq!(result, Ok(Fd(3)));
2112
2113        let process = system.current_process();
2114        let fd1 = process.fds.get(&Fd(1)).unwrap();
2115        let fd3 = process.fds.get(&Fd(3)).unwrap();
2116        assert_eq!(fd1, fd3);
2117    }
2118
2119    #[test]
2120    fn dup_can_set_cloexec() {
2121        let system = VirtualSystem::new();
2122        let result = system.dup(Fd::STDOUT, Fd::STDERR, FdFlag::CloseOnExec.into());
2123        assert_eq!(result, Ok(Fd(3)));
2124
2125        let process = system.current_process();
2126        let fd3 = process.fds.get(&Fd(3)).unwrap();
2127        assert_eq!(fd3.flags, EnumSet::only(FdFlag::CloseOnExec));
2128    }
2129
2130    #[test]
2131    fn dup2_shares_open_file_description() {
2132        let system = VirtualSystem::new();
2133        let result = system.dup2(Fd::STDOUT, Fd(5));
2134        assert_eq!(result, Ok(Fd(5)));
2135
2136        let process = system.current_process();
2137        let fd1 = process.fds.get(&Fd(1)).unwrap();
2138        let fd5 = process.fds.get(&Fd(5)).unwrap();
2139        assert_eq!(fd1, fd5);
2140    }
2141
2142    #[test]
2143    fn dup2_clears_cloexec() {
2144        let system = VirtualSystem::new();
2145        let mut process = system.current_process_mut();
2146        process.fds.get_mut(&Fd::STDOUT).unwrap().flags = FdFlag::CloseOnExec.into();
2147        drop(process);
2148
2149        let result = system.dup2(Fd::STDOUT, Fd(6));
2150        assert_eq!(result, Ok(Fd(6)));
2151
2152        let process = system.current_process();
2153        let fd6 = process.fds.get(&Fd(6)).unwrap();
2154        assert_eq!(fd6.flags, EnumSet::empty());
2155    }
2156
2157    #[test]
2158    fn open_non_existing_file_no_creation() {
2159        let system = VirtualSystem::new();
2160        let result = system
2161            .open(
2162                c"/no/such/file",
2163                OfdAccess::ReadOnly,
2164                EnumSet::empty(),
2165                Mode::empty(),
2166            )
2167            .now_or_never()
2168            .unwrap();
2169        assert_eq!(result, Err(Errno::ENOENT));
2170    }
2171
2172    #[test]
2173    fn open_creating_non_existing_file() {
2174        let system = VirtualSystem::new();
2175        let result = system
2176            .open(
2177                c"new_file",
2178                OfdAccess::WriteOnly,
2179                OpenFlag::Create.into(),
2180                Mode::empty(),
2181            )
2182            .now_or_never()
2183            .unwrap();
2184        assert_eq!(result, Ok(Fd(3)));
2185
2186        system
2187            .write(Fd(3), &[42, 123])
2188            .now_or_never()
2189            .unwrap()
2190            .unwrap();
2191        let file = system.state.borrow().file_system.get("new_file").unwrap();
2192        let file = file.borrow();
2193        assert_eq!(file.permissions, Mode::empty());
2194        assert_matches!(&file.body, FileBody::Regular { content, .. } => {
2195            assert_eq!(content[..], [42, 123]);
2196        });
2197    }
2198
2199    #[test]
2200    fn open_creating_non_existing_file_umask() {
2201        let system = VirtualSystem::new();
2202        system.umask(Mode::from_bits_retain(0o125));
2203        system
2204            .open(
2205                c"file",
2206                OfdAccess::WriteOnly,
2207                OpenFlag::Create.into(),
2208                Mode::ALL_9,
2209            )
2210            .now_or_never()
2211            .unwrap()
2212            .unwrap();
2213
2214        let file = system.state.borrow().file_system.get("file").unwrap();
2215        let file = file.borrow();
2216        assert_eq!(file.permissions, Mode::from_bits_retain(0o652));
2217    }
2218
2219    #[test]
2220    fn open_existing_file() {
2221        let system = VirtualSystem::new();
2222        let fd = system
2223            .open(
2224                c"file",
2225                OfdAccess::WriteOnly,
2226                OpenFlag::Create.into(),
2227                Mode::empty(),
2228            )
2229            .now_or_never()
2230            .unwrap()
2231            .unwrap();
2232        system
2233            .write(fd, &[75, 96, 133])
2234            .now_or_never()
2235            .unwrap()
2236            .unwrap();
2237
2238        let result = system
2239            .open(
2240                c"file",
2241                OfdAccess::ReadOnly,
2242                EnumSet::empty(),
2243                Mode::empty(),
2244            )
2245            .now_or_never()
2246            .unwrap();
2247        assert_eq!(result, Ok(Fd(4)));
2248
2249        let mut buffer = [0; 5];
2250        let count = system
2251            .read(Fd(4), &mut buffer)
2252            .now_or_never()
2253            .unwrap()
2254            .unwrap();
2255        assert_eq!(count, 3);
2256        assert_eq!(buffer, [75, 96, 133, 0, 0]);
2257        let count = system
2258            .read(Fd(4), &mut buffer)
2259            .now_or_never()
2260            .unwrap()
2261            .unwrap();
2262        assert_eq!(count, 0);
2263    }
2264
2265    #[test]
2266    fn open_existing_file_excl() {
2267        let system = VirtualSystem::new();
2268        let first = system
2269            .open(
2270                c"my_file",
2271                OfdAccess::WriteOnly,
2272                OpenFlag::Create | OpenFlag::Exclusive,
2273                Mode::empty(),
2274            )
2275            .now_or_never()
2276            .unwrap();
2277        assert_eq!(first, Ok(Fd(3)));
2278
2279        let second = system
2280            .open(
2281                c"my_file",
2282                OfdAccess::WriteOnly,
2283                OpenFlag::Create | OpenFlag::Exclusive,
2284                Mode::empty(),
2285            )
2286            .now_or_never()
2287            .unwrap();
2288        assert_eq!(second, Err(Errno::EEXIST));
2289    }
2290
2291    #[test]
2292    fn open_truncating() {
2293        let system = VirtualSystem::new();
2294        let fd = system
2295            .open(
2296                c"file",
2297                OfdAccess::WriteOnly,
2298                OpenFlag::Create.into(),
2299                Mode::ALL_9,
2300            )
2301            .now_or_never()
2302            .unwrap()
2303            .unwrap();
2304        system
2305            .write(fd, &[1, 2, 3])
2306            .now_or_never()
2307            .unwrap()
2308            .unwrap();
2309
2310        let result = system
2311            .open(
2312                c"file",
2313                OfdAccess::WriteOnly,
2314                OpenFlag::Truncate.into(),
2315                Mode::empty(),
2316            )
2317            .now_or_never()
2318            .unwrap();
2319        assert_eq!(result, Ok(Fd(4)));
2320
2321        let reader = system
2322            .open(
2323                c"file",
2324                OfdAccess::ReadOnly,
2325                EnumSet::empty(),
2326                Mode::empty(),
2327            )
2328            .now_or_never()
2329            .unwrap()
2330            .unwrap();
2331        let count = system
2332            .read(reader, &mut [0; 1])
2333            .now_or_never()
2334            .unwrap()
2335            .unwrap();
2336        assert_eq!(count, 0);
2337    }
2338
2339    #[test]
2340    fn open_appending() {
2341        let system = VirtualSystem::new();
2342        let fd = system
2343            .open(
2344                c"file",
2345                OfdAccess::WriteOnly,
2346                OpenFlag::Create.into(),
2347                Mode::ALL_9,
2348            )
2349            .now_or_never()
2350            .unwrap()
2351            .unwrap();
2352        system
2353            .write(fd, &[1, 2, 3])
2354            .now_or_never()
2355            .unwrap()
2356            .unwrap();
2357
2358        let result = system
2359            .open(
2360                c"file",
2361                OfdAccess::WriteOnly,
2362                OpenFlag::Append.into(),
2363                Mode::empty(),
2364            )
2365            .now_or_never()
2366            .unwrap();
2367        assert_eq!(result, Ok(Fd(4)));
2368        system
2369            .write(Fd(4), &[4, 5, 6])
2370            .now_or_never()
2371            .unwrap()
2372            .unwrap();
2373
2374        let reader = system
2375            .open(
2376                c"file",
2377                OfdAccess::ReadOnly,
2378                EnumSet::empty(),
2379                Mode::empty(),
2380            )
2381            .now_or_never()
2382            .unwrap()
2383            .unwrap();
2384        let mut buffer = [0; 7];
2385        let count = system
2386            .read(reader, &mut buffer)
2387            .now_or_never()
2388            .unwrap()
2389            .unwrap();
2390        assert_eq!(count, 6);
2391        assert_eq!(buffer, [1, 2, 3, 4, 5, 6, 0]);
2392    }
2393
2394    #[test]
2395    fn open_directory() {
2396        let system = VirtualSystem::new();
2397
2398        // Create a regular file and its parent directory
2399        let _ = system
2400            .open(
2401                c"/dir/file",
2402                OfdAccess::WriteOnly,
2403                OpenFlag::Create.into(),
2404                Mode::empty(),
2405            )
2406            .now_or_never()
2407            .unwrap();
2408
2409        let result = system
2410            .open(
2411                c"/dir",
2412                OfdAccess::ReadOnly,
2413                OpenFlag::Directory.into(),
2414                Mode::empty(),
2415            )
2416            .now_or_never()
2417            .unwrap();
2418        assert_eq!(result, Ok(Fd(4)));
2419    }
2420
2421    #[test]
2422    fn open_non_directory_path_prefix() {
2423        let system = VirtualSystem::new();
2424
2425        // Create a regular file
2426        let _ = system
2427            .open(
2428                c"/file",
2429                OfdAccess::WriteOnly,
2430                OpenFlag::Create.into(),
2431                Mode::empty(),
2432            )
2433            .now_or_never()
2434            .unwrap();
2435
2436        let result = system
2437            .open(
2438                c"/file/file",
2439                OfdAccess::WriteOnly,
2440                OpenFlag::Create.into(),
2441                Mode::empty(),
2442            )
2443            .now_or_never()
2444            .unwrap();
2445        assert_eq!(result, Err(Errno::ENOTDIR));
2446    }
2447
2448    #[test]
2449    fn open_non_directory_file() {
2450        let system = VirtualSystem::new();
2451
2452        // Create a regular file
2453        let _ = system
2454            .open(
2455                c"/file",
2456                OfdAccess::WriteOnly,
2457                OpenFlag::Create.into(),
2458                Mode::empty(),
2459            )
2460            .now_or_never()
2461            .unwrap();
2462
2463        let result = system
2464            .open(
2465                c"/file",
2466                OfdAccess::ReadOnly,
2467                OpenFlag::Directory.into(),
2468                Mode::empty(),
2469            )
2470            .now_or_never()
2471            .unwrap();
2472        assert_eq!(result, Err(Errno::ENOTDIR));
2473    }
2474
2475    #[test]
2476    fn open_default_working_directory() {
2477        // The default working directory is the root directory.
2478        let system = VirtualSystem::new();
2479
2480        let writer = system
2481            .open(
2482                c"/dir/file",
2483                OfdAccess::WriteOnly,
2484                OpenFlag::Create.into(),
2485                Mode::ALL_9,
2486            )
2487            .now_or_never()
2488            .unwrap();
2489        system
2490            .write(writer.unwrap(), &[1, 2, 3, 42])
2491            .now_or_never()
2492            .unwrap()
2493            .unwrap();
2494
2495        let reader = system
2496            .open(
2497                c"./dir/file",
2498                OfdAccess::ReadOnly,
2499                EnumSet::empty(),
2500                Mode::empty(),
2501            )
2502            .now_or_never()
2503            .unwrap();
2504        let mut buffer = [0; 10];
2505        let count = system
2506            .read(reader.unwrap(), &mut buffer)
2507            .now_or_never()
2508            .unwrap()
2509            .unwrap();
2510        assert_eq!(count, 4);
2511        assert_eq!(buffer[0..4], [1, 2, 3, 42]);
2512    }
2513
2514    #[test]
2515    fn open_tmpfile() {
2516        let system = VirtualSystem::new();
2517        let fd = system.open_tmpfile(Path::new("")).unwrap();
2518        system
2519            .write(fd, &[42, 17, 75])
2520            .now_or_never()
2521            .unwrap()
2522            .unwrap();
2523        system.lseek(fd, SeekFrom::Start(0)).unwrap();
2524        let mut buffer = [0; 4];
2525        let count = system
2526            .read(fd, &mut buffer)
2527            .now_or_never()
2528            .unwrap()
2529            .unwrap();
2530        assert_eq!(count, 3);
2531        assert_eq!(buffer[..3], [42, 17, 75]);
2532    }
2533
2534    #[test]
2535    fn close() {
2536        let system = VirtualSystem::new();
2537
2538        let result = system.close(Fd::STDERR);
2539        assert_eq!(result, Ok(()));
2540        assert_eq!(system.current_process().fds.get(&Fd::STDERR), None);
2541
2542        let result = system.close(Fd::STDERR);
2543        assert_eq!(result, Ok(()));
2544    }
2545
2546    #[test]
2547    fn fcntl_getfd_and_setfd() {
2548        let system = VirtualSystem::new();
2549
2550        let flags = system.fcntl_getfd(Fd::STDIN).unwrap();
2551        assert_eq!(flags, EnumSet::empty());
2552
2553        system
2554            .fcntl_setfd(Fd::STDIN, FdFlag::CloseOnExec.into())
2555            .unwrap();
2556
2557        let flags = system.fcntl_getfd(Fd::STDIN).unwrap();
2558        assert_eq!(flags, EnumSet::only(FdFlag::CloseOnExec));
2559
2560        let flags = system.fcntl_getfd(Fd::STDOUT).unwrap();
2561        assert_eq!(flags, EnumSet::empty());
2562
2563        system.fcntl_setfd(Fd::STDIN, EnumSet::empty()).unwrap();
2564
2565        let flags = system.fcntl_getfd(Fd::STDIN).unwrap();
2566        assert_eq!(flags, EnumSet::empty());
2567    }
2568
2569    #[test]
2570    fn opendir_default_working_directory() {
2571        // The default working directory is the root directory.
2572        let system = VirtualSystem::new();
2573
2574        let _ = system
2575            .open(
2576                c"/dir/file",
2577                OfdAccess::WriteOnly,
2578                OpenFlag::Create.into(),
2579                Mode::ALL_9,
2580            )
2581            .now_or_never()
2582            .unwrap();
2583
2584        let mut dir = system.opendir(c"./dir").unwrap();
2585        let mut files = Vec::new();
2586        while let Some(entry) = dir.next().unwrap() {
2587            files.push(entry.name.to_unix_string());
2588        }
2589        files.sort_unstable();
2590        assert_eq!(
2591            files[..],
2592            [
2593                UnixString::from("."),
2594                UnixString::from(".."),
2595                UnixString::from("file")
2596            ]
2597        );
2598    }
2599
2600    // TODO Test sigmask
2601
2602    #[test]
2603    fn sigmask_suspends_on_pending_stop_signal() {
2604        let system = VirtualSystem::new();
2605        // Block SIGTSTP first
2606        system
2607            .sigmask(Some((SigmaskOp::Add, &Sigset::from(SIGTSTP))), None)
2608            .now_or_never()
2609            .unwrap()
2610            .unwrap();
2611        // Put SIGTSTP in the pending queue
2612        let _ = system.current_process_mut().raise_signal(SIGTSTP);
2613        // Unblocking SIGTSTP should deliver it, stopping the process
2614        let result = system
2615            .sigmask(Some((SigmaskOp::Remove, &Sigset::from(SIGTSTP))), None)
2616            .now_or_never();
2617        // The future should be pending because the process is stopped
2618        assert_eq!(result, None);
2619        assert_eq!(
2620            system.current_process().state(),
2621            ProcessState::stopped(SIGTSTP),
2622        );
2623    }
2624
2625    #[test]
2626    fn sigmask_resumes_after_sigcont() {
2627        let (system, mut executor) = virtual_system_with_executor();
2628        let state = Rc::clone(&system.state);
2629        let mut env = Env::with_system(system);
2630        let pid = env
2631            .run_in_child_process((), |child_env: Env<VirtualSystem>, ()| async move {
2632                // Block SIGTSTP (signal will become pending when raised)
2633                child_env
2634                    .system
2635                    .sigmask(Some((SigmaskOp::Add, &Sigset::from(SIGTSTP))), None)
2636                    .await
2637                    .unwrap();
2638                // Raise SIGTSTP; since it is blocked, it becomes pending
2639                child_env.system.raise(SIGTSTP).await.unwrap();
2640                // Unblocking SIGTSTP delivers it, stopping the process; the
2641                // future suspends until the process is resumed
2642                child_env
2643                    .system
2644                    .sigmask(Some((SigmaskOp::Remove, &Sigset::from(SIGTSTP))), None)
2645                    .await
2646                    .unwrap();
2647                // After being resumed, exit cleanly
2648                child_env.system.exit(ExitStatus(0)).await;
2649            })
2650            .0
2651            .unwrap();
2652        executor.run_until_stalled();
2653
2654        // The child should now be stopped
2655        assert_eq!(
2656            state.borrow().processes[&pid].state,
2657            ProcessState::stopped(SIGTSTP),
2658        );
2659
2660        // Resume the child with SIGCONT
2661        env.system
2662            .kill(pid, Some(SIGCONT))
2663            .now_or_never()
2664            .unwrap()
2665            .unwrap();
2666
2667        executor.run_until_stalled();
2668
2669        // The child should have exited
2670        let result = env.system.wait(pid);
2671        assert_eq!(result, Ok(Some((pid, ProcessState::exited(0)))));
2672    }
2673
2674    #[test]
2675    fn kill_process() {
2676        let system = VirtualSystem::new();
2677        system
2678            .kill(system.process_id, None)
2679            .now_or_never()
2680            .unwrap()
2681            .unwrap();
2682        assert_eq!(system.current_process().state(), ProcessState::Running);
2683
2684        let result = system.kill(system.process_id, Some(SIGINT)).now_or_never();
2685        // The future should be pending because the current process has been killed
2686        assert_eq!(result, None);
2687        assert_eq!(
2688            system.current_process().state(),
2689            ProcessState::Halted(ProcessResult::Signaled {
2690                signal: SIGINT,
2691                core_dump: false
2692            })
2693        );
2694
2695        let system = VirtualSystem::new();
2696        let state = system.state.borrow();
2697        let max_pid = *state.processes.keys().max().unwrap();
2698        drop(state);
2699        let e = system
2700            .kill(Pid(max_pid.0 + 1), Some(SIGINT))
2701            .now_or_never()
2702            .unwrap()
2703            .unwrap_err();
2704        assert_eq!(e, Errno::ESRCH);
2705    }
2706
2707    #[test]
2708    fn kill_all_processes() {
2709        let system = VirtualSystem::new();
2710        let pgid = system.current_process().pgid;
2711        let mut state = system.state.borrow_mut();
2712        state.processes.insert(
2713            Pid(10),
2714            Process::with_parent_and_group(system.process_id, pgid),
2715        );
2716        state.processes.insert(
2717            Pid(11),
2718            Process::with_parent_and_group(system.process_id, pgid),
2719        );
2720        state
2721            .processes
2722            .insert(Pid(21), Process::with_parent_and_group(Pid(10), Pid(21)));
2723        drop(state);
2724
2725        let result = system.kill(Pid::ALL, Some(SIGTERM)).now_or_never();
2726        // The future should be pending because the current process has been killed
2727        assert_eq!(result, None);
2728        let state = system.state.borrow();
2729        for process in state.processes.values() {
2730            assert_eq!(
2731                process.state,
2732                ProcessState::Halted(ProcessResult::Signaled {
2733                    signal: SIGTERM,
2734                    core_dump: false
2735                })
2736            );
2737        }
2738    }
2739
2740    #[test]
2741    fn kill_processes_in_same_group() {
2742        let system = VirtualSystem::new();
2743        let pgid = system.current_process().pgid;
2744        let mut state = system.state.borrow_mut();
2745        state.processes.insert(
2746            Pid(10),
2747            Process::with_parent_and_group(system.process_id, pgid),
2748        );
2749        state.processes.insert(
2750            Pid(11),
2751            Process::with_parent_and_group(system.process_id, pgid),
2752        );
2753        state
2754            .processes
2755            .insert(Pid(21), Process::with_parent_and_group(Pid(10), Pid(21)));
2756        drop(state);
2757
2758        let result = system
2759            .kill(Pid::MY_PROCESS_GROUP, Some(SIGQUIT))
2760            .now_or_never();
2761        // The future should be pending because the current process has been killed
2762        assert_eq!(result, None);
2763        let state = system.state.borrow();
2764        assert_eq!(
2765            state.processes[&system.process_id].state,
2766            ProcessState::Halted(ProcessResult::Signaled {
2767                signal: SIGQUIT,
2768                core_dump: true
2769            })
2770        );
2771        assert_eq!(
2772            state.processes[&Pid(10)].state,
2773            ProcessState::Halted(ProcessResult::Signaled {
2774                signal: SIGQUIT,
2775                core_dump: true
2776            })
2777        );
2778        assert_eq!(
2779            state.processes[&Pid(11)].state,
2780            ProcessState::Halted(ProcessResult::Signaled {
2781                signal: SIGQUIT,
2782                core_dump: true
2783            })
2784        );
2785        assert_eq!(state.processes[&Pid(21)].state, ProcessState::Running);
2786    }
2787
2788    #[test]
2789    fn kill_process_group() {
2790        let system = VirtualSystem::new();
2791        let pgid = system.current_process().pgid;
2792        let mut state = system.state.borrow_mut();
2793        state.processes.insert(
2794            Pid(10),
2795            Process::with_parent_and_group(system.process_id, pgid),
2796        );
2797        state.processes.insert(
2798            Pid(11),
2799            Process::with_parent_and_group(system.process_id, Pid(21)),
2800        );
2801        state
2802            .processes
2803            .insert(Pid(21), Process::with_parent_and_group(Pid(10), Pid(21)));
2804        drop(state);
2805
2806        system
2807            .kill(Pid(-21), Some(SIGHUP))
2808            .now_or_never()
2809            .unwrap()
2810            .unwrap();
2811        let state = system.state.borrow();
2812        assert_eq!(
2813            state.processes[&system.process_id].state,
2814            ProcessState::Running
2815        );
2816        assert_eq!(state.processes[&Pid(10)].state, ProcessState::Running);
2817        assert_eq!(
2818            state.processes[&Pid(11)].state,
2819            ProcessState::Halted(ProcessResult::Signaled {
2820                signal: SIGHUP,
2821                core_dump: false
2822            })
2823        );
2824        assert_eq!(
2825            state.processes[&Pid(21)].state,
2826            ProcessState::Halted(ProcessResult::Signaled {
2827                signal: SIGHUP,
2828                core_dump: false
2829            })
2830        );
2831    }
2832
2833    #[test]
2834    fn kill_returns_success_even_if_process_state_did_not_change() {
2835        let system = VirtualSystem::new();
2836        let pgid = system.current_process().pgid;
2837        let mut state = system.state.borrow_mut();
2838        state.processes.insert(
2839            Pid(10),
2840            Process::with_parent_and_group(system.process_id, pgid),
2841        );
2842        drop(state);
2843
2844        system
2845            .kill(-pgid, Some(SIGCONT))
2846            .now_or_never()
2847            .unwrap()
2848            .unwrap();
2849        let state = system.state.borrow();
2850        assert_eq!(state.processes[&Pid(10)].state, ProcessState::Running);
2851    }
2852
2853    #[test]
2854    fn kill_dummy_signal_to_my_group() {
2855        let system = VirtualSystem::new();
2856
2857        let result = system
2858            .kill(Pid::MY_PROCESS_GROUP, None)
2859            .now_or_never()
2860            .unwrap();
2861
2862        assert_eq!(result, Ok(()));
2863        assert_eq!(system.current_process().state(), ProcessState::Running);
2864    }
2865
2866    #[test]
2867    fn kill_dummy_signal_to_non_existent_group() {
2868        let system = VirtualSystem::new();
2869        let result = system.kill(Pid(-9999), None).now_or_never().unwrap();
2870        assert_eq!(result, Err(Errno::ESRCH));
2871    }
2872
2873    pub(super) fn virtual_system_with_executor() -> (VirtualSystem, LocalPool) {
2874        let system = VirtualSystem::new();
2875        let executor = LocalPool::new();
2876        system.state.borrow_mut().executor = Some(Rc::new(executor.spawner()));
2877        (system, executor)
2878    }
2879
2880    #[test]
2881    fn setpgid_creating_new_group_from_parent() {
2882        let (system, _executor) = virtual_system_with_executor();
2883        let state = Rc::clone(&system.state);
2884        let mut env = Env::with_system(system);
2885        let pid = env.run_in_child_process((), |_, ()| pending()).0.unwrap();
2886
2887        let result = env.system.setpgid(pid, pid);
2888        assert_eq!(result, Ok(()));
2889
2890        let pgid = state.borrow().processes[&pid].pgid();
2891        assert_eq!(pgid, pid);
2892    }
2893
2894    #[test]
2895    fn setpgid_creating_new_group_from_child() {
2896        let (system, mut executor) = virtual_system_with_executor();
2897        let state = Rc::clone(&system.state);
2898        let mut env = Env::with_system(system);
2899        let pid = env
2900            .run_in_child_process((), |child_env: Env<VirtualSystem>, ()| async move {
2901                let result = child_env.system.setpgid(Pid(0), Pid(0));
2902                assert_eq!(result, Ok(()));
2903                child_env.system.exit(child_env.exit_status).await;
2904            })
2905            .0
2906            .unwrap();
2907        executor.run_until_stalled();
2908
2909        let pgid = state.borrow().processes[&pid].pgid();
2910        assert_eq!(pgid, pid);
2911    }
2912
2913    #[test]
2914    fn setpgid_extending_existing_group_from_parent() {
2915        let (system, _executor) = virtual_system_with_executor();
2916        let state = Rc::clone(&system.state);
2917        let mut env = Env::with_system(system);
2918        let pid_1 = env.run_in_child_process((), |_, ()| pending()).0.unwrap();
2919        env.system.setpgid(pid_1, pid_1).unwrap();
2920        let pid_2 = env.run_in_child_process((), |_, ()| pending()).0.unwrap();
2921
2922        let result = env.system.setpgid(pid_2, pid_1);
2923        assert_eq!(result, Ok(()));
2924
2925        let pgid = state.borrow().processes[&pid_2].pgid();
2926        assert_eq!(pgid, pid_1);
2927    }
2928
2929    #[test]
2930    fn setpgid_with_nonexisting_pid() {
2931        let (system, _executor) = virtual_system_with_executor();
2932        let state = Rc::clone(&system.state);
2933        let mut env = Env::with_system(system);
2934        let pid = env.run_in_child_process((), |_, ()| pending()).0.unwrap();
2935
2936        let dummy_pid = Pid(123);
2937        let result = env.system.setpgid(dummy_pid, dummy_pid);
2938        assert_eq!(result, Err(Errno::ESRCH));
2939
2940        let pgid = state.borrow().processes[&pid].pgid();
2941        assert_eq!(pgid, Pid(1));
2942    }
2943
2944    #[test]
2945    fn setpgid_with_unrelated_pid() {
2946        let (system, mut executor) = virtual_system_with_executor();
2947        let parent_pid = system.process_id;
2948        let state = Rc::clone(&system.state);
2949        let mut env = Env::with_system(system);
2950        let _pid = env
2951            .run_in_child_process((), move |child_env: Env<VirtualSystem>, ()| async move {
2952                let result = child_env.system.setpgid(parent_pid, Pid(0));
2953                assert_eq!(result, Err(Errno::ESRCH));
2954                child_env.system.exit(child_env.exit_status).await;
2955            })
2956            .0
2957            .unwrap();
2958        executor.run_until_stalled();
2959
2960        let pgid = state.borrow().processes[&parent_pid].pgid();
2961        assert_eq!(pgid, Pid(1));
2962    }
2963
2964    #[test]
2965    fn setpgid_with_execed_child() {
2966        let (system, mut executor) = virtual_system_with_executor();
2967        let path = "/some/file";
2968        let mut content = Inode::default();
2969        content.body = FileBody::Regular {
2970            content: vec![],
2971            is_native_executable: true,
2972        };
2973        content.permissions.set(Mode::USER_EXEC, true);
2974        let content = Rc::new(RefCell::new(content));
2975        let state = Rc::clone(&system.state);
2976        state.borrow_mut().file_system.save(path, content).unwrap();
2977        let mut env = Env::with_system(system);
2978        let pid = env
2979            .run_in_child_process((), move |child_env: Env<VirtualSystem>, ()| async move {
2980                let path = CString::new(path).unwrap();
2981                child_env
2982                    .system
2983                    .execve(&path, &[] as &[CString], &[] as &[CString])
2984                    .await
2985                    .ok();
2986                child_env.system.exit(child_env.exit_status).await;
2987            })
2988            .0
2989            .unwrap();
2990        executor.run_until_stalled();
2991
2992        let result = env.system.setpgid(pid, pid);
2993        assert_eq!(result, Err(Errno::EACCES));
2994
2995        let pgid = state.borrow().processes[&pid].pgid();
2996        assert_eq!(pgid, Pid(1));
2997    }
2998
2999    #[test]
3000    fn setpgid_with_nonexisting_pgid() {
3001        let (system, mut executor) = virtual_system_with_executor();
3002        let state = Rc::clone(&system.state);
3003        let mut env = Env::with_system(system);
3004        let pid_1 = env.run_in_child_process((), |_, ()| pending()).0.unwrap();
3005        // env.system.setpgid(pid_1, pid_1).unwrap();
3006        let pid_2 = env.run_in_child_process((), |_, ()| pending()).0.unwrap();
3007        executor.run_until_stalled();
3008
3009        let result = env.system.setpgid(pid_2, pid_1);
3010        assert_eq!(result, Err(Errno::EPERM));
3011
3012        let pgid = state.borrow().processes[&pid_2].pgid();
3013        assert_eq!(pgid, Pid(1));
3014    }
3015
3016    #[test]
3017    fn tcsetpgrp_success() {
3018        let system = VirtualSystem::new();
3019        let pid = Pid(10);
3020        let ppid = system.process_id;
3021        let pgid = Pid(9);
3022        system
3023            .state
3024            .borrow_mut()
3025            .processes
3026            .insert(pid, Process::with_parent_and_group(ppid, pgid));
3027
3028        system
3029            .tcsetpgrp(Fd::STDIN, pgid)
3030            .now_or_never()
3031            .unwrap()
3032            .unwrap();
3033
3034        let foreground = system.state.borrow().foreground;
3035        assert_eq!(foreground, Some(pgid));
3036    }
3037
3038    #[test]
3039    fn tcsetpgrp_with_invalid_fd() {
3040        let system = VirtualSystem::new();
3041        let result = system.tcsetpgrp(Fd(100), Pid(2)).now_or_never().unwrap();
3042        assert_eq!(result, Err(Errno::EBADF));
3043    }
3044
3045    #[test]
3046    fn tcsetpgrp_with_nonexisting_pgrp() {
3047        let system = VirtualSystem::new();
3048        let result = system
3049            .tcsetpgrp(Fd::STDIN, Pid(100))
3050            .now_or_never()
3051            .unwrap();
3052        assert_eq!(result, Err(Errno::EPERM));
3053    }
3054
3055    #[test]
3056    fn run_in_child_process_shares_data() {
3057        let (system, mut executor) = virtual_system_with_executor();
3058        let data = "foo".to_string();
3059        let (_pid_or_error, data) =
3060            system.run_in_child_process(data, |child_system: VirtualSystem, data| async move {
3061                assert_eq!(data, "foo");
3062                child_system.exit(ExitStatus(0)).await;
3063            });
3064
3065        assert_eq!(data, "foo");
3066        executor.run_until_stalled();
3067    }
3068
3069    #[test]
3070    fn run_in_child_process_chooses_minimal_unused_pid() {
3071        let (system, _executor) = virtual_system_with_executor();
3072        let dummy_process = Process::with_parent_and_group(system.process_id, Pid(1));
3073        system
3074            .state
3075            .borrow_mut()
3076            .processes
3077            .insert(Pid(42), dummy_process);
3078
3079        let (pid_or_error, ()) = system.run_in_child_process((), |_, ()| pending());
3080        assert_eq!(pid_or_error, Ok(Pid(43)));
3081    }
3082
3083    #[test]
3084    fn run_in_child_process_adds_child_to_process_table() {
3085        let (system, mut executor) = virtual_system_with_executor();
3086        let child_pid = Rc::new(Cell::new(None));
3087
3088        let (pid_or_error, child_pid) = system.run_in_child_process(
3089            child_pid,
3090            |child_system: VirtualSystem, child_pid: Rc<Cell<Option<Pid>>>| async move {
3091                child_pid.set(Some(child_system.getpid()));
3092                child_system.exit(ExitStatus(0)).await;
3093            },
3094        );
3095        executor.run_until_stalled();
3096
3097        let child_pid = child_pid.get().expect("child task should have set pid");
3098        assert_eq!(pid_or_error, Ok(child_pid));
3099        let state = system.state.borrow();
3100        let child_process = state
3101            .processes
3102            .get(&child_pid)
3103            .expect("child process should be in process table");
3104        assert_eq!(child_process.ppid(), system.process_id);
3105    }
3106
3107    #[test]
3108    #[should_panic = "the child task should have terminated itself by exiting or signaling itself: pid=3"]
3109    fn run_in_child_process_panics_on_child_task_not_exiting() {
3110        let (system, mut executor) = virtual_system_with_executor();
3111
3112        system
3113            .run_in_child_process((), |_, ()| async { /* exit() */ })
3114            .0
3115            .unwrap();
3116
3117        executor.run_until_stalled();
3118    }
3119
3120    #[test]
3121    fn run_in_child_process_fails_without_executor() {
3122        let system = VirtualSystem::new();
3123        let result = system
3124            .run_in_child_process((), |_, ()| {
3125                unreachable!("child task should not run without executor")
3126                    as std::future::Pending<()>
3127            })
3128            .0;
3129        assert_eq!(result, Err(Errno::ENOSYS));
3130    }
3131
3132    #[test]
3133    fn wait_for_running_child() {
3134        let (system, _executor) = virtual_system_with_executor();
3135        let mut env = Env::with_system(system);
3136
3137        let pid = env
3138            .run_in_child_process((), |_, ()| async {
3139                unreachable!("child process does not progress unless executor is used")
3140            })
3141            .0
3142            .unwrap();
3143
3144        let result = env.system.wait(pid);
3145        assert_eq!(result, Ok(None))
3146    }
3147
3148    #[test]
3149    fn wait_for_exited_child() {
3150        let (system, mut executor) = virtual_system_with_executor();
3151        let mut env = Env::with_system(system);
3152
3153        let pid = env
3154            .run_in_child_process((), |child_env: Env<VirtualSystem>, ()| async move {
3155                child_env.system.exit(ExitStatus(5)).await;
3156            })
3157            .0
3158            .unwrap();
3159        executor.run_until_stalled();
3160
3161        let result = env.system.wait(pid);
3162        assert_eq!(result, Ok(Some((pid, ProcessState::exited(5)))));
3163    }
3164
3165    #[test]
3166    fn wait_for_signaled_child() {
3167        let (system, mut executor) = virtual_system_with_executor();
3168        let mut env = Env::with_system(system);
3169
3170        let pid = env
3171            .run_in_child_process((), |child_env: Env<VirtualSystem>, ()| async move {
3172                let pid = child_env.system.getpid();
3173                let result = child_env.system.kill(pid, Some(SIGKILL)).await;
3174                unreachable!("kill returned {result:?}");
3175            })
3176            .0
3177            .unwrap();
3178        executor.run_until_stalled();
3179
3180        let result = env.system.wait(pid);
3181        assert_eq!(
3182            result,
3183            Ok(Some((
3184                pid,
3185                ProcessState::Halted(ProcessResult::Signaled {
3186                    signal: SIGKILL,
3187                    core_dump: false
3188                })
3189            )))
3190        );
3191    }
3192
3193    #[test]
3194    fn wait_for_stopped_child() {
3195        let (system, mut executor) = virtual_system_with_executor();
3196        let mut env = Env::with_system(system);
3197
3198        let pid = env
3199            .run_in_child_process((), |child_env: Env<VirtualSystem>, ()| async move {
3200                let pid = child_env.system.getpid();
3201                let result = child_env.system.kill(pid, Some(SIGSTOP)).await;
3202                unreachable!("kill returned {result:?}");
3203            })
3204            .0
3205            .unwrap();
3206        executor.run_until_stalled();
3207
3208        let result = env.system.wait(pid);
3209        assert_eq!(result, Ok(Some((pid, ProcessState::stopped(SIGSTOP)))));
3210    }
3211
3212    #[test]
3213    fn wait_for_resumed_child() {
3214        let (system, mut executor) = virtual_system_with_executor();
3215        let mut env = Env::with_system(system);
3216
3217        let pid = env
3218            .run_in_child_process((), |child_env: Env<VirtualSystem>, ()| async move {
3219                let pid = child_env.system.getpid();
3220                child_env.system.kill(pid, Some(SIGSTOP)).await.unwrap();
3221                child_env.system.exit(ExitStatus(123)).await;
3222            })
3223            .0
3224            .unwrap();
3225        executor.run_until_stalled();
3226
3227        env.system
3228            .kill(pid, Some(SIGCONT))
3229            .now_or_never()
3230            .unwrap()
3231            .unwrap();
3232
3233        let result = env.system.wait(pid);
3234        assert_eq!(result, Ok(Some((pid, ProcessState::Running))));
3235
3236        executor.run_until_stalled();
3237
3238        let result = env.system.wait(pid);
3239        assert_eq!(result, Ok(Some((pid, ProcessState::exited(123)))));
3240    }
3241
3242    #[test]
3243    fn wait_without_child() {
3244        let system = VirtualSystem::new();
3245        let result = system.wait(Pid::ALL);
3246        assert_eq!(result, Err(Errno::ECHILD));
3247        // TODO
3248        // let result = system.wait(Pid::MY_PROCESS_GROUP);
3249        // assert_eq!(result, Err(Errno::ECHILD));
3250        let result = system.wait(system.process_id);
3251        assert_eq!(result, Err(Errno::ECHILD));
3252        let result = system.wait(Pid(1234));
3253        assert_eq!(result, Err(Errno::ECHILD));
3254        // TODO
3255        // let result = system.wait(Pid(-1234));
3256        // assert_eq!(result, Err(Errno::ECHILD));
3257    }
3258
3259    #[test]
3260    fn execve_returns_enosys_for_executable_file() {
3261        let system = VirtualSystem::new();
3262        let path = "/some/file";
3263        let mut content = Inode::default();
3264        content.body = FileBody::Regular {
3265            content: vec![],
3266            is_native_executable: true,
3267        };
3268        content.permissions.set(Mode::USER_EXEC, true);
3269        let content = Rc::new(RefCell::new(content));
3270        let mut state = system.state.borrow_mut();
3271        state.file_system.save(path, content).unwrap();
3272        drop(state);
3273        let path = CString::new(path).unwrap();
3274        let result = system
3275            .execve(&path, &[] as &[CString], &[] as &[CString])
3276            .now_or_never()
3277            .unwrap();
3278        assert_eq!(result, Err(Errno::ENOSYS));
3279    }
3280
3281    #[test]
3282    fn execve_saves_arguments() {
3283        let system = VirtualSystem::new();
3284        let path = "/some/file";
3285        let mut content = Inode::default();
3286        content.body = FileBody::Regular {
3287            content: vec![],
3288            is_native_executable: true,
3289        };
3290        content.permissions.set(Mode::USER_EXEC, true);
3291        let content = Rc::new(RefCell::new(content));
3292        let mut state = system.state.borrow_mut();
3293        state.file_system.save(path, content).unwrap();
3294        drop(state);
3295        let path = CString::new(path).unwrap();
3296        let args = [c"file", c"bar"];
3297        let envs = [c"foo=FOO", c"baz"];
3298        system
3299            .execve(&path, args.as_slice(), envs.as_slice())
3300            .now_or_never();
3301
3302        let process = system.current_process();
3303        let arguments = process.last_exec.as_ref().unwrap();
3304        assert_eq!(arguments.0, path);
3305        assert_eq!(arguments.1, args);
3306        assert_eq!(arguments.2, envs);
3307    }
3308
3309    #[test]
3310    fn execve_returns_enoexec_for_non_executable_file() {
3311        let system = VirtualSystem::new();
3312        let path = "/some/file";
3313        let mut content = Inode::default();
3314        content.permissions.set(Mode::USER_EXEC, true);
3315        let content = Rc::new(RefCell::new(content));
3316        let mut state = system.state.borrow_mut();
3317        state.file_system.save(path, content).unwrap();
3318        drop(state);
3319        let path = CString::new(path).unwrap();
3320        let result = system
3321            .execve(&path, &[] as &[CString], &[] as &[CString])
3322            .now_or_never()
3323            .unwrap();
3324        assert_eq!(result, Err(Errno::ENOEXEC));
3325    }
3326
3327    #[test]
3328    fn execve_returns_enoent_on_file_not_found() {
3329        let system = VirtualSystem::new();
3330        let result = system
3331            .execve(c"/no/such/file", &[] as &[CString], &[] as &[CString])
3332            .now_or_never()
3333            .unwrap();
3334        assert_eq!(result, Err(Errno::ENOENT));
3335    }
3336
3337    #[test]
3338    fn exit_sets_current_process_state_to_exited() {
3339        let system = VirtualSystem::new();
3340        system.exit(ExitStatus(42)).now_or_never();
3341
3342        assert!(system.current_process().state_has_changed());
3343        assert_eq!(
3344            system.current_process().state(),
3345            ProcessState::exited(ExitStatus(42))
3346        );
3347    }
3348
3349    #[test]
3350    fn exit_sends_sigchld_to_parent() {
3351        let (system, mut executor) = virtual_system_with_executor();
3352        system.sigaction(SIGCHLD, Disposition::Catch).unwrap();
3353        let system2 = system.clone();
3354        let mut env = Env::with_system(system);
3355
3356        let _pid = env
3357            .run_in_child_process((), |child_env: Env<VirtualSystem>, ()| async move {
3358                child_env.system.exit(ExitStatus(123)).await;
3359            })
3360            .0
3361            .unwrap();
3362        executor.run_until_stalled();
3363
3364        assert_eq!(system2.current_process().caught_signals, [SIGCHLD]);
3365    }
3366
3367    #[test]
3368    fn chdir_changes_directory() {
3369        let system = VirtualSystem::new();
3370
3371        // Create a regular file and its parent directory
3372        let _ = system
3373            .open(
3374                c"/dir/file",
3375                OfdAccess::WriteOnly,
3376                OpenFlag::Create.into(),
3377                Mode::empty(),
3378            )
3379            .now_or_never()
3380            .unwrap();
3381
3382        let result = system.chdir(c"/dir");
3383        assert_eq!(result, Ok(()));
3384        assert_eq!(system.current_process().cwd, Path::new("/dir"));
3385    }
3386
3387    #[test]
3388    fn chdir_fails_with_non_existing_directory() {
3389        let system = VirtualSystem::new();
3390
3391        let result = system.chdir(c"/no/such/dir");
3392        assert_eq!(result, Err(Errno::ENOENT));
3393    }
3394
3395    #[test]
3396    fn chdir_fails_with_non_directory_file() {
3397        let system = VirtualSystem::new();
3398
3399        // Create a regular file and its parent directory
3400        let _ = system
3401            .open(
3402                c"/dir/file",
3403                OfdAccess::WriteOnly,
3404                OpenFlag::Create.into(),
3405                Mode::empty(),
3406            )
3407            .now_or_never()
3408            .unwrap();
3409
3410        let result = system.chdir(c"/dir/file");
3411        assert_eq!(result, Err(Errno::ENOTDIR));
3412    }
3413
3414    #[test]
3415    fn getrlimit_for_unset_resource_returns_infinity() {
3416        let system = VirtualSystem::new();
3417        let result = system.getrlimit(Resource::CPU).unwrap();
3418        assert_eq!(
3419            result,
3420            LimitPair {
3421                soft: INFINITY,
3422                hard: INFINITY,
3423            },
3424        );
3425    }
3426
3427    #[test]
3428    fn setrlimit_and_getrlimit_with_finite_limits() {
3429        let system = VirtualSystem::new();
3430        system
3431            .setrlimit(
3432                Resource::CORE,
3433                LimitPair {
3434                    soft: 4096,
3435                    hard: 8192,
3436                },
3437            )
3438            .unwrap();
3439        system
3440            .setrlimit(Resource::CPU, LimitPair { soft: 10, hard: 30 })
3441            .unwrap();
3442
3443        let result = system.getrlimit(Resource::CORE).unwrap();
3444        assert_eq!(
3445            result,
3446            LimitPair {
3447                soft: 4096,
3448                hard: 8192,
3449            },
3450        );
3451        let result = system.getrlimit(Resource::CPU).unwrap();
3452        assert_eq!(result, LimitPair { soft: 10, hard: 30 },);
3453    }
3454
3455    #[test]
3456    fn setrlimit_rejects_soft_limit_higher_than_hard_limit() {
3457        let system = VirtualSystem::new();
3458        let result = system.setrlimit(Resource::CPU, LimitPair { soft: 2, hard: 1 });
3459        assert_eq!(result, Err(Errno::EINVAL));
3460
3461        // The limits should not have been changed
3462        let result = system.getrlimit(Resource::CPU).unwrap();
3463        assert_eq!(
3464            result,
3465            LimitPair {
3466                soft: INFINITY,
3467                hard: INFINITY,
3468            },
3469        );
3470    }
3471
3472    #[test]
3473    fn setrlimit_refuses_raising_hard_limit() {
3474        let system = VirtualSystem::new();
3475        system
3476            .setrlimit(Resource::CPU, LimitPair { soft: 1, hard: 1 })
3477            .unwrap();
3478        let result = system.setrlimit(Resource::CPU, LimitPair { soft: 1, hard: 2 });
3479        assert_eq!(result, Err(Errno::EPERM));
3480
3481        // The limits should not have been changed
3482        let result = system.getrlimit(Resource::CPU).unwrap();
3483        assert_eq!(result, LimitPair { soft: 1, hard: 1 });
3484    }
3485}
3486
3487#[cfg(test)]
3488mod fifo_tests;