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
60mod file_system;
61mod io;
62mod process;
63mod signal;
64
65pub use self::file_system::*;
66pub use self::io::*;
67pub use self::process::*;
68pub use self::signal::*;
69use super::AT_FDCWD;
70use super::CaughtSignals;
71use super::Chdir;
72use super::Clock;
73use super::Close;
74use super::CpuTimes;
75use super::Dir;
76use super::Disposition;
77use super::Dup;
78use super::Errno;
79use super::Exec;
80use super::Exit;
81use super::Fcntl;
82use super::FdFlag;
83use super::Fork;
84use super::Fstat;
85use super::GetCwd;
86use super::GetPid;
87use super::GetPw;
88use super::GetRlimit;
89use super::GetSigaction;
90use super::GetUid;
91use super::Gid;
92use super::IsExecutableFile;
93use super::Isatty;
94use super::OfdAccess;
95use super::Open;
96use super::OpenFlag;
97use super::Pipe;
98use super::Read;
99use super::Result;
100use super::Seek;
101use super::Select;
102use super::SendSignal;
103use super::SetPgid;
104use super::SetRlimit;
105use super::ShellPath;
106use super::Sigaction;
107use super::Sigmask;
108use super::SigmaskOp;
109use super::Signals;
110use super::Stat;
111use super::Sysconf;
112use super::TcGetPgrp;
113use super::TcSetPgrp;
114use super::Times;
115use super::Uid;
116use super::Umask;
117use super::Wait;
118use super::Write;
119use super::resource::INFINITY;
120use super::resource::LimitPair;
121use super::resource::Resource;
122#[cfg(doc)]
123use crate::System;
124use crate::io::Fd;
125use crate::job::Pid;
126use crate::job::ProcessState;
127use crate::path::Path;
128use crate::path::PathBuf;
129use crate::semantics::ExitStatus;
130use crate::str::UnixStr;
131use crate::str::UnixString;
132use crate::system::ChildProcessStarter;
133use enumset::EnumSet;
134use std::borrow::Cow;
135use std::cell::Cell;
136use std::cell::Ref;
137use std::cell::RefCell;
138use std::cell::RefMut;
139use std::collections::BTreeMap;
140use std::collections::HashMap;
141use std::collections::VecDeque;
142use std::convert::Infallible;
143use std::convert::TryInto;
144use std::ffi::CStr;
145use std::ffi::CString;
146use std::ffi::c_int;
147use std::fmt::Debug;
148use std::future::pending;
149use std::future::poll_fn;
150use std::future::ready;
151use std::io::SeekFrom;
152use std::ops::DerefMut as _;
153use std::ops::RangeInclusive;
154use std::pin::Pin;
155use std::rc::Rc;
156use std::task::Context;
157use std::task::Poll;
158use std::task::Waker;
159use std::time::Duration;
160use std::time::Instant;
161
162/// Simulated system
163///
164/// See the [module-level documentation](self) to grasp a basic understanding of
165/// `VirtualSystem`.
166///
167/// A `VirtualSystem` instance has two members: `state` and `process_id`. The
168/// former is a [`SystemState`] that effectively contains the whole state of the
169/// system. The state is contained in `Rc` so that virtual processes can share
170/// the same state. The latter is a process ID that identifies a process calling
171/// the system interfaces.
172///
173/// When you clone a virtual system, the clone will have the same `process_id`
174/// and `state` as the original. To simulate the `fork` system call, you should
175/// call a method of the [`Fork`] trait implemented by `VirtualSystem`.
176#[derive(Clone, Debug)]
177pub struct VirtualSystem {
178    /// State of the system.
179    pub state: Rc<RefCell<SystemState>>,
180
181    /// Process ID of the process that is interacting with the system.
182    pub process_id: Pid,
183}
184
185impl VirtualSystem {
186    /// Creates a virtual system with an almost empty state.
187    ///
188    /// The `process_id` of the returned `VirtualSystem` will be 2.
189    /// (Process ID 1 has special meaning in some system calls, so we don't use
190    /// it as a default value.)
191    ///
192    /// The `state` of the returned `VirtualSystem` will have a [`Process`] with
193    /// process ID 2 in the process set ([`SystemState::processes`]). The file
194    /// system will contain files named `/dev/stdin`, `/dev/stdout`, and
195    /// `/dev/stderr` that are opened in the process with file descriptor 0, 1,
196    /// and 2, respectively. The file system also contains an empty directory
197    /// `/tmp`.
198    pub fn new() -> VirtualSystem {
199        let mut state = SystemState::default();
200        let mut process = Process::with_parent_and_group(Pid(1), Pid(1));
201
202        let mut set_std_fd = |path, fd| {
203            let file = Rc::new(RefCell::new(Inode::new([])));
204            state.file_system.save(path, Rc::clone(&file)).unwrap();
205            let body = FdBody {
206                open_file_description: Rc::new(RefCell::new(OpenFileDescription {
207                    file,
208                    offset: 0,
209                    is_readable: true,
210                    is_writable: true,
211                    is_appending: true,
212                })),
213                flags: EnumSet::empty(),
214            };
215            process.set_fd(fd, body).unwrap();
216        };
217        set_std_fd("/dev/stdin", Fd::STDIN);
218        set_std_fd("/dev/stdout", Fd::STDOUT);
219        set_std_fd("/dev/stderr", Fd::STDERR);
220
221        state
222            .file_system
223            .save(
224                "/tmp",
225                Rc::new(RefCell::new(Inode {
226                    body: FileBody::Directory {
227                        files: Default::default(),
228                    },
229                    permissions: Mode::ALL_9,
230                })),
231            )
232            .unwrap();
233
234        let process_id = Pid(2);
235        state.processes.insert(process_id, process);
236
237        let state = Rc::new(RefCell::new(state));
238        VirtualSystem { state, process_id }
239    }
240
241    /// Finds the current process from the system state.
242    ///
243    /// # Panics
244    ///
245    /// This function will panic if it cannot find a process having
246    /// `self.process_id`.
247    pub fn current_process(&self) -> Ref<'_, Process> {
248        Ref::map(self.state.borrow(), |state| {
249            &state.processes[&self.process_id]
250        })
251    }
252
253    /// Finds the current process from the system state.
254    ///
255    /// # Panics
256    ///
257    /// This function will panic if it cannot find a process having
258    /// `self.process_id`.
259    pub fn current_process_mut(&self) -> RefMut<'_, Process> {
260        RefMut::map(self.state.borrow_mut(), |state| {
261            state.processes.get_mut(&self.process_id).unwrap()
262        })
263    }
264
265    /// Calls the given closure passing the open file description for the FD.
266    ///
267    /// Returns `Err(Errno::EBADF)` if the FD is not open.
268    pub fn with_open_file_description<F, R>(&self, fd: Fd, f: F) -> Result<R>
269    where
270        F: FnOnce(&OpenFileDescription) -> Result<R>,
271    {
272        let process = self.current_process();
273        let body = process.get_fd(fd).ok_or(Errno::EBADF)?;
274        let ofd = body.open_file_description.borrow();
275        f(&ofd)
276    }
277
278    /// Calls the given closure passing the open file description for the FD.
279    ///
280    /// Returns `Err(Errno::EBADF)` if the FD is not open.
281    pub fn with_open_file_description_mut<F, R>(&self, fd: Fd, f: F) -> Result<R>
282    where
283        F: FnOnce(&mut OpenFileDescription) -> Result<R>,
284    {
285        let mut process = self.current_process_mut();
286        let body = process.get_fd_mut(fd).ok_or(Errno::EBADF)?;
287        let mut ofd = body.open_file_description.borrow_mut();
288        f(&mut ofd)
289    }
290
291    fn resolve_relative_path<'a>(&self, path: &'a Path) -> Cow<'a, Path> {
292        if path.is_absolute() {
293            Cow::Borrowed(path)
294        } else {
295            Cow::Owned(self.current_process().cwd.join(path))
296        }
297    }
298
299    fn resolve_existing_file(
300        &self,
301        _dir_fd: Fd,
302        path: &Path,
303        follow_symlinks: bool,
304    ) -> Result<Rc<RefCell<Inode>>> {
305        // TODO Resolve relative to dir_fd
306        // TODO Support AT_FDCWD
307        const _POSIX_SYMLOOP_MAX: i32 = 8;
308
309        let mut path = Cow::Borrowed(path);
310        for _count in 0.._POSIX_SYMLOOP_MAX {
311            let resolved_path = self.resolve_relative_path(&path);
312            let inode = self.state.borrow().file_system.get(&resolved_path)?;
313            if !follow_symlinks {
314                return Ok(inode);
315            }
316
317            let inode_ref = inode.borrow();
318            if let FileBody::Symlink { target } = &inode_ref.body {
319                let mut new_path = resolved_path.into_owned();
320                new_path.pop();
321                new_path.push(target);
322                path = Cow::Owned(new_path);
323            } else {
324                drop(inode_ref);
325                return Ok(inode);
326            }
327        }
328
329        Err(Errno::ELOOP)
330    }
331
332    /// Blocks the calling thread until the current process is running.
333    async fn block_until_running(&self) {
334        let waker = Rc::new(Cell::new(None));
335
336        poll_fn(|cx| {
337            let mut state = self.state.borrow_mut();
338            let Some(process) = state.processes.get_mut(&self.process_id) else {
339                return Poll::Ready(());
340            };
341
342            match process.state {
343                ProcessState::Running => Poll::Ready(()),
344                ProcessState::Halted(result) => {
345                    if result.is_stopped() {
346                        waker.set(Some(cx.waker().clone()));
347                        process.wake_on_resumption(Rc::downgrade(&waker));
348                    }
349                    Poll::Pending
350                }
351            }
352        })
353        .await
354    }
355}
356
357impl Default for VirtualSystem {
358    fn default() -> Self {
359        VirtualSystem::new()
360    }
361}
362
363impl Fstat for VirtualSystem {
364    fn fstat(&self, fd: Fd) -> Result<Stat> {
365        self.with_open_file_description(fd, |ofd| Ok(ofd.file.borrow().stat()))
366    }
367
368    fn fstatat(&self, dir_fd: Fd, path: &CStr, follow_symlinks: bool) -> Result<Stat> {
369        let path = Path::new(UnixStr::from_bytes(path.to_bytes()));
370        let inode = self.resolve_existing_file(dir_fd, path, follow_symlinks)?;
371        Ok(inode.borrow().stat())
372    }
373}
374
375impl IsExecutableFile for VirtualSystem {
376    /// Tests whether the specified file is executable or not.
377    ///
378    /// The current implementation only checks if the file has any executable
379    /// bit in the permissions. The file owner and group are not considered.
380    fn is_executable_file(&self, path: &CStr) -> bool {
381        let path = Path::new(UnixStr::from_bytes(path.to_bytes()));
382        self.resolve_existing_file(AT_FDCWD, path, /* follow symlinks */ true)
383            .is_ok_and(|inode| inode.borrow().permissions.intersects(Mode::ALL_EXEC))
384    }
385}
386
387impl Pipe for VirtualSystem {
388    fn pipe(&self) -> Result<(Fd, Fd)> {
389        let file = Rc::new(RefCell::new(Inode {
390            body: FileBody::Fifo {
391                content: VecDeque::new(),
392                readers: 1,
393                writers: 1,
394            },
395            permissions: Mode::default(),
396        }));
397        let reader = OpenFileDescription {
398            file: Rc::clone(&file),
399            offset: 0,
400            is_readable: true,
401            is_writable: false,
402            is_appending: false,
403        };
404        let writer = OpenFileDescription {
405            file: Rc::clone(&file),
406            offset: 0,
407            is_readable: false,
408            is_writable: true,
409            is_appending: false,
410        };
411
412        let reader = FdBody {
413            open_file_description: Rc::new(RefCell::new(reader)),
414            flags: EnumSet::empty(),
415        };
416        let writer = FdBody {
417            open_file_description: Rc::new(RefCell::new(writer)),
418            flags: EnumSet::empty(),
419        };
420
421        let mut process = self.current_process_mut();
422        let reader = process.open_fd(reader).map_err(|_| Errno::EMFILE)?;
423        let writer = process.open_fd(writer).map_err(|_| {
424            process.close_fd(reader);
425            Errno::EMFILE
426        })?;
427        Ok((reader, writer))
428    }
429}
430
431impl Dup for VirtualSystem {
432    fn dup(&self, from: Fd, to_min: Fd, flags: EnumSet<FdFlag>) -> Result<Fd> {
433        let mut process = self.current_process_mut();
434        let mut body = process.fds.get(&from).ok_or(Errno::EBADF)?.clone();
435        body.flags = flags;
436        process.open_fd_ge(to_min, body).map_err(|_| Errno::EMFILE)
437    }
438
439    fn dup2(&self, from: Fd, to: Fd) -> Result<Fd> {
440        let mut process = self.current_process_mut();
441        let mut body = process.fds.get(&from).ok_or(Errno::EBADF)?.clone();
442        body.flags = EnumSet::empty();
443        process.set_fd(to, body).map_err(|_| Errno::EBADF)?;
444        Ok(to)
445    }
446}
447
448impl Open for VirtualSystem {
449    fn open(
450        &self,
451        path: &CStr,
452        access: OfdAccess,
453        flags: EnumSet<OpenFlag>,
454        mode: Mode,
455    ) -> Result<Fd> {
456        let path = self.resolve_relative_path(Path::new(UnixStr::from_bytes(path.to_bytes())));
457        let umask = self.current_process().umask;
458
459        let mut state = self.state.borrow_mut();
460        let file = match state.file_system.get(&path) {
461            Ok(inode) => {
462                if flags.contains(OpenFlag::Exclusive) {
463                    return Err(Errno::EEXIST);
464                }
465                if flags.contains(OpenFlag::Directory)
466                    && !matches!(inode.borrow().body, FileBody::Directory { .. })
467                {
468                    return Err(Errno::ENOTDIR);
469                }
470                if flags.contains(OpenFlag::Truncate) {
471                    if let FileBody::Regular { content, .. } = &mut inode.borrow_mut().body {
472                        content.clear();
473                    };
474                }
475                inode
476            }
477            Err(Errno::ENOENT) if flags.contains(OpenFlag::Create) => {
478                let mut inode = Inode::new([]);
479                inode.permissions = mode.difference(umask);
480                let inode = Rc::new(RefCell::new(inode));
481                state.file_system.save(&path, Rc::clone(&inode))?;
482                inode
483            }
484            Err(errno) => return Err(errno),
485        };
486
487        let (is_readable, is_writable) = match access {
488            OfdAccess::ReadOnly => (true, false),
489            OfdAccess::WriteOnly => (false, true),
490            OfdAccess::ReadWrite => (true, true),
491            OfdAccess::Exec | OfdAccess::Search => (false, false),
492        };
493
494        if let FileBody::Fifo {
495            readers, writers, ..
496        } = &mut file.borrow_mut().body
497        {
498            if is_readable {
499                *readers += 1;
500            }
501            if is_writable {
502                *writers += 1;
503            }
504        }
505
506        let open_file_description = Rc::new(RefCell::new(OpenFileDescription {
507            file,
508            offset: 0,
509            is_readable,
510            is_writable,
511            is_appending: flags.contains(OpenFlag::Append),
512        }));
513        let body = FdBody {
514            open_file_description,
515            flags: if flags.contains(OpenFlag::CloseOnExec) {
516                EnumSet::only(FdFlag::CloseOnExec)
517            } else {
518                EnumSet::empty()
519            },
520        };
521        let process = state.processes.get_mut(&self.process_id).unwrap();
522        process.open_fd(body).map_err(|_| Errno::EMFILE)
523    }
524
525    fn open_tmpfile(&self, _parent_dir: &Path) -> Result<Fd> {
526        let file = Rc::new(RefCell::new(Inode::new([])));
527        let open_file_description = Rc::new(RefCell::new(OpenFileDescription {
528            file,
529            offset: 0,
530            is_readable: true,
531            is_writable: true,
532            is_appending: false,
533        }));
534        let body = FdBody {
535            open_file_description,
536            flags: EnumSet::empty(),
537        };
538        let mut state = self.state.borrow_mut();
539        let process = state.processes.get_mut(&self.process_id).unwrap();
540        process.open_fd(body).map_err(|_| Errno::EMFILE)
541    }
542
543    fn fdopendir(&self, fd: Fd) -> Result<impl Dir + use<>> {
544        self.with_open_file_description(fd, |ofd| {
545            let inode = ofd.inode();
546            let dir = VirtualDir::try_from(&inode.borrow().body)?;
547            Ok(dir)
548        })
549    }
550
551    fn opendir(&self, path: &CStr) -> Result<impl Dir + use<>> {
552        let fd = self.open(
553            path,
554            OfdAccess::ReadOnly,
555            OpenFlag::Directory.into(),
556            Mode::empty(),
557        )?;
558        self.fdopendir(fd)
559    }
560}
561
562impl Close for VirtualSystem {
563    fn close(&self, fd: Fd) -> Result<()> {
564        self.current_process_mut().close_fd(fd);
565        Ok(())
566    }
567}
568
569impl Fcntl for VirtualSystem {
570    fn ofd_access(&self, fd: Fd) -> Result<OfdAccess> {
571        fn is_directory(file_body: &FileBody) -> bool {
572            matches!(file_body, FileBody::Directory { .. })
573        }
574
575        self.with_open_file_description(fd, |ofd| match (ofd.is_readable, ofd.is_writable) {
576            (true, false) => Ok(OfdAccess::ReadOnly),
577            (false, true) => Ok(OfdAccess::WriteOnly),
578            (true, true) => Ok(OfdAccess::ReadWrite),
579            (false, false) => {
580                if is_directory(&ofd.inode().borrow().body) {
581                    Ok(OfdAccess::Search)
582                } else {
583                    Ok(OfdAccess::Exec)
584                }
585            }
586        })
587    }
588
589    fn get_and_set_nonblocking(&self, fd: Fd, _nonblocking: bool) -> Result<bool> {
590        self.with_open_file_description(fd, |_ofd| {
591            // TODO Implement non-blocking I/O
592            Ok(false)
593        })
594    }
595
596    fn fcntl_getfd(&self, fd: Fd) -> Result<EnumSet<FdFlag>> {
597        let process = self.current_process();
598        let body = process.get_fd(fd).ok_or(Errno::EBADF)?;
599        Ok(body.flags)
600    }
601
602    fn fcntl_setfd(&self, fd: Fd, flags: EnumSet<FdFlag>) -> Result<()> {
603        let mut process = self.current_process_mut();
604        let body = process.get_fd_mut(fd).ok_or(Errno::EBADF)?;
605        body.flags = flags;
606        Ok(())
607    }
608}
609
610impl Read for VirtualSystem {
611    fn read(&self, fd: Fd, buffer: &mut [u8]) -> Result<usize> {
612        self.with_open_file_description_mut(fd, |ofd| ofd.read(buffer))
613    }
614}
615
616impl Write for VirtualSystem {
617    fn write(&self, fd: Fd, buffer: &[u8]) -> Result<usize> {
618        self.with_open_file_description_mut(fd, |ofd| ofd.write(buffer))
619    }
620}
621
622impl Seek for VirtualSystem {
623    fn lseek(&self, fd: Fd, position: SeekFrom) -> Result<u64> {
624        self.with_open_file_description_mut(fd, |ofd| ofd.seek(position))
625            .and_then(|new_offset| new_offset.try_into().map_err(|_| Errno::EOVERFLOW))
626    }
627}
628
629impl Umask for VirtualSystem {
630    fn umask(&self, new_mask: Mode) -> Mode {
631        std::mem::replace(&mut self.current_process_mut().umask, new_mask)
632    }
633}
634
635impl GetCwd for VirtualSystem {
636    fn getcwd(&self) -> Result<PathBuf> {
637        Ok(self.current_process().cwd.clone())
638    }
639}
640
641impl Chdir for VirtualSystem {
642    fn chdir(&self, path: &CStr) -> Result<()> {
643        let path = Path::new(UnixStr::from_bytes(path.to_bytes()));
644        let inode = self.resolve_existing_file(AT_FDCWD, path, /* follow links */ true)?;
645        if matches!(&inode.borrow().body, FileBody::Directory { .. }) {
646            let mut process = self.current_process_mut();
647            let new_path = process.cwd.join(path);
648            process.chdir(new_path);
649            Ok(())
650        } else {
651            Err(Errno::ENOTDIR)
652        }
653    }
654}
655
656impl Clock for VirtualSystem {
657    /// Returns `now` in [`SystemState`].
658    ///
659    /// Panics if it is `None`.
660    fn now(&self) -> Instant {
661        self.state
662            .borrow()
663            .now
664            .expect("SystemState::now not assigned")
665    }
666}
667
668impl Times for VirtualSystem {
669    /// Returns `times` in [`SystemState`].
670    fn times(&self) -> Result<CpuTimes> {
671        Ok(self.state.borrow().times)
672    }
673}
674
675impl Signals for VirtualSystem {
676    const SIGABRT: signal::Number = signal::SIGABRT;
677    const SIGALRM: signal::Number = signal::SIGALRM;
678    const SIGBUS: signal::Number = signal::SIGBUS;
679    const SIGCHLD: signal::Number = signal::SIGCHLD;
680    const SIGCLD: Option<signal::Number> = Some(signal::SIGCLD);
681    const SIGCONT: signal::Number = signal::SIGCONT;
682    const SIGEMT: Option<signal::Number> = Some(signal::SIGEMT);
683    const SIGFPE: signal::Number = signal::SIGFPE;
684    const SIGHUP: signal::Number = signal::SIGHUP;
685    const SIGILL: signal::Number = signal::SIGILL;
686    const SIGINFO: Option<signal::Number> = Some(signal::SIGINFO);
687    const SIGINT: signal::Number = signal::SIGINT;
688    const SIGIO: Option<signal::Number> = Some(signal::SIGIO);
689    const SIGIOT: signal::Number = signal::SIGIOT;
690    const SIGKILL: signal::Number = signal::SIGKILL;
691    const SIGLOST: Option<signal::Number> = Some(signal::SIGLOST);
692    const SIGPIPE: signal::Number = signal::SIGPIPE;
693    const SIGPOLL: Option<signal::Number> = Some(signal::SIGPOLL);
694    const SIGPROF: signal::Number = signal::SIGPROF;
695    const SIGPWR: Option<signal::Number> = Some(signal::SIGPWR);
696    const SIGQUIT: signal::Number = signal::SIGQUIT;
697    const SIGSEGV: signal::Number = signal::SIGSEGV;
698    const SIGSTKFLT: Option<signal::Number> = Some(signal::SIGSTKFLT);
699    const SIGSTOP: signal::Number = signal::SIGSTOP;
700    const SIGSYS: signal::Number = signal::SIGSYS;
701    const SIGTERM: signal::Number = signal::SIGTERM;
702    const SIGTHR: Option<signal::Number> = Some(signal::SIGTHR);
703    const SIGTRAP: signal::Number = signal::SIGTRAP;
704    const SIGTSTP: signal::Number = signal::SIGTSTP;
705    const SIGTTIN: signal::Number = signal::SIGTTIN;
706    const SIGTTOU: signal::Number = signal::SIGTTOU;
707    const SIGURG: signal::Number = signal::SIGURG;
708    const SIGUSR1: signal::Number = signal::SIGUSR1;
709    const SIGUSR2: signal::Number = signal::SIGUSR2;
710    const SIGVTALRM: signal::Number = signal::SIGVTALRM;
711    const SIGWINCH: signal::Number = signal::SIGWINCH;
712    const SIGXCPU: signal::Number = signal::SIGXCPU;
713    const SIGXFSZ: signal::Number = signal::SIGXFSZ;
714
715    fn sigrt_range(&self) -> Option<RangeInclusive<Number>> {
716        Some(signal::SIGRTMIN..=signal::SIGRTMAX)
717    }
718}
719
720impl GetPid for VirtualSystem {
721    /// Currently, this function always returns `Pid(2)` if the process exists.
722    fn getsid(&self, pid: Pid) -> Result<Pid> {
723        self.state
724            .borrow()
725            .processes
726            .get(&pid)
727            .map_or(Err(Errno::ESRCH), |_| Ok(Pid(2)))
728    }
729
730    fn getpid(&self) -> Pid {
731        self.process_id
732    }
733
734    fn getppid(&self) -> Pid {
735        self.current_process().ppid
736    }
737
738    fn getpgrp(&self) -> Pid {
739        self.current_process().pgid
740    }
741}
742
743impl SetPgid for VirtualSystem {
744    /// Modifies the process group ID of a process.
745    ///
746    /// The current implementation does not yet support the concept of sessions.
747    fn setpgid(&self, mut pid: Pid, mut pgid: Pid) -> Result<()> {
748        if pgid.0 < 0 {
749            return Err(Errno::EINVAL);
750        }
751        if pid.0 == 0 {
752            pid = self.process_id;
753        }
754        if pgid.0 == 0 {
755            pgid = pid;
756        }
757
758        let mut state = self.state.borrow_mut();
759        if pgid != pid && !state.processes.values().any(|p| p.pgid == pgid) {
760            return Err(Errno::EPERM);
761        }
762        let process = state.processes.get_mut(&pid).ok_or(Errno::ESRCH)?;
763        if pid != self.process_id && process.ppid != self.process_id {
764            return Err(Errno::ESRCH);
765        }
766        if process.last_exec.is_some() {
767            return Err(Errno::EACCES);
768        }
769
770        process.pgid = pgid;
771        Ok(())
772        // TODO Support sessions
773    }
774}
775
776impl Sigmask for VirtualSystem {
777    fn sigmask(
778        &self,
779        op: Option<(SigmaskOp, &[signal::Number])>,
780        old_mask: Option<&mut Vec<signal::Number>>,
781    ) -> Result<()> {
782        let mut state = self.state.borrow_mut();
783        let process = state
784            .processes
785            .get_mut(&self.process_id)
786            .expect("current process not found");
787
788        if let Some(old_mask) = old_mask {
789            old_mask.clear();
790            old_mask.extend(process.blocked_signals());
791        }
792
793        if let Some((op, mask)) = op {
794            let result = process.block_signals(op, mask);
795            if result.process_state_changed {
796                let parent_pid = process.ppid;
797                raise_sigchld(&mut state, parent_pid);
798            }
799        }
800
801        Ok(())
802    }
803}
804
805impl GetSigaction for VirtualSystem {
806    fn get_sigaction(&self, signal: signal::Number) -> Result<Disposition> {
807        let process = self.current_process();
808        Ok(process.disposition(signal))
809    }
810}
811
812impl Sigaction for VirtualSystem {
813    fn sigaction(&self, signal: signal::Number, disposition: Disposition) -> Result<Disposition> {
814        let mut process = self.current_process_mut();
815        Ok(process.set_disposition(signal, disposition))
816    }
817}
818
819impl CaughtSignals for VirtualSystem {
820    fn caught_signals(&self) -> Vec<signal::Number> {
821        std::mem::take(&mut self.current_process_mut().caught_signals)
822    }
823}
824
825impl SendSignal for VirtualSystem {
826    /// Sends a signal to the target process.
827    ///
828    /// The current implementation accepts any positive signal number and `None`
829    /// (no signal) for `signal`. Negative signal numbers are rejected with
830    /// `Errno::EINVAL`.
831    ///
832    /// This function returns a future that enables the executor to block the
833    /// calling thread until the current process is ready to proceed. If the
834    /// signal is sent to the current process and it causes the process to stop,
835    /// the future will be ready only when the process is resumed. Similarly, if
836    /// the signal causes the current process to terminate, the future will
837    /// never be ready.
838    fn kill(
839        &self,
840        target: Pid,
841        signal: Option<signal::Number>,
842    ) -> impl Future<Output = Result<()>> + use<> {
843        let result = 'result: {
844            if let Some(signal) = signal {
845                // Validate the signal number
846                if signal.as_raw() < 0 {
847                    break 'result Err(Errno::EINVAL);
848                }
849            }
850
851            match target {
852                Pid::MY_PROCESS_GROUP => {
853                    let target_pgid = self.current_process().pgid;
854                    send_signal_to_processes(
855                        &mut self.state.borrow_mut(),
856                        Some(target_pgid),
857                        signal,
858                    )
859                }
860
861                Pid::ALL => send_signal_to_processes(&mut self.state.borrow_mut(), None, signal),
862
863                Pid(raw_pid) if raw_pid >= 0 => {
864                    let mut state = self.state.borrow_mut();
865                    match state.processes.get_mut(&target) {
866                        Some(process) => {
867                            if let Some(signal) = signal {
868                                let result = process.raise_signal(signal);
869                                if result.process_state_changed {
870                                    let parent_pid = process.ppid;
871                                    raise_sigchld(&mut state, parent_pid);
872                                }
873                            }
874                            Ok(())
875                        }
876                        None => Err(Errno::ESRCH),
877                    }
878                }
879
880                Pid(negative_pgid) => {
881                    let target_pgid = Pid(-negative_pgid);
882                    send_signal_to_processes(
883                        &mut self.state.borrow_mut(),
884                        Some(target_pgid),
885                        signal,
886                    )
887                }
888            }
889        };
890
891        let system = self.clone();
892        async move {
893            system.block_until_running().await;
894            result
895        }
896    }
897
898    fn raise(&self, signal: signal::Number) -> impl Future<Output = Result<()>> + use<> {
899        let target = self.process_id;
900        self.kill(target, Some(signal))
901    }
902}
903
904impl Select for VirtualSystem {
905    /// Waits for a next event.
906    ///
907    /// The `VirtualSystem` implementation for this method does not actually
908    /// block the calling thread. The method returns immediately in any case.
909    ///
910    /// The `timeout` is ignored if this function returns because of a ready FD
911    /// or a caught signal. Otherwise, the timeout is added to
912    /// [`SystemState::now`], which must not be `None` then.
913    fn select(
914        &self,
915        readers: &mut Vec<Fd>,
916        writers: &mut Vec<Fd>,
917        timeout: Option<Duration>,
918        signal_mask: Option<&[signal::Number]>,
919    ) -> Result<c_int> {
920        let mut process = self.current_process_mut();
921
922        // Detect invalid FDs first. POSIX requires that the arguments are
923        // not modified if an error occurs.
924        let fds = readers.iter().chain(writers.iter());
925        if { fds }.any(|fd| !process.fds().contains_key(fd)) {
926            return Err(Errno::EBADF);
927        }
928
929        if let Some(signal_mask) = signal_mask {
930            let save_mask = process
931                .blocked_signals()
932                .iter()
933                .copied()
934                .collect::<Vec<signal::Number>>();
935            let result_1 = process.block_signals(SigmaskOp::Set, signal_mask);
936            let result_2 = process.block_signals(SigmaskOp::Set, &save_mask);
937            assert!(!result_2.delivered);
938            if result_1.caught {
939                return Err(Errno::EINTR);
940            }
941        }
942
943        readers.retain(|fd| {
944            // We already checked that the FD is open, so it's safe to access by index.
945            let ofd = process.fds()[fd].open_file_description.borrow();
946            !ofd.is_readable() || ofd.is_ready_for_reading()
947        });
948        writers.retain(|fd| {
949            let ofd = process.fds()[fd].open_file_description.borrow();
950            !ofd.is_writable() || ofd.is_ready_for_writing()
951        });
952
953        drop(process);
954
955        let count = (readers.len() + writers.len()).try_into().unwrap();
956        if count == 0 {
957            if let Some(duration) = timeout {
958                if !duration.is_zero() {
959                    let mut state = self.state.borrow_mut();
960                    let now = state.now.as_mut();
961                    let now = now.expect("now time unspecified; cannot add timeout duration");
962                    *now += duration;
963                }
964            }
965        }
966        Ok(count)
967    }
968}
969
970impl Isatty for VirtualSystem {
971    fn isatty(&self, fd: Fd) -> bool {
972        self.with_open_file_description(fd, |ofd| {
973            Ok(matches!(&ofd.file.borrow().body, FileBody::Terminal { .. }))
974        })
975        .unwrap_or(false)
976    }
977}
978
979impl TcGetPgrp for VirtualSystem {
980    /// Returns the current foreground process group ID.
981    ///
982    /// The current implementation does not yet support the concept of
983    /// controlling terminals and sessions. It accepts any open file descriptor.
984    fn tcgetpgrp(&self, fd: Fd) -> Result<Pid> {
985        // Make sure the FD is open
986        self.with_open_file_description(fd, |_| Ok(()))?;
987
988        self.state.borrow().foreground.ok_or(Errno::ENOTTY)
989    }
990}
991
992impl TcSetPgrp for VirtualSystem {
993    /// Switches the foreground process.
994    ///
995    /// The current implementation does not yet support the concept of
996    /// controlling terminals and sessions. It accepts any open file descriptor.
997    fn tcsetpgrp(&self, fd: Fd, pgid: Pid) -> impl Future<Output = Result<()>> + use<> {
998        fn inner(system: &VirtualSystem, fd: Fd, pgid: Pid) -> Result<()> {
999            // Make sure the FD is open
1000            system.with_open_file_description(fd, |_| Ok(()))?;
1001
1002            // Make sure the process group exists
1003            let mut state = system.state.borrow_mut();
1004            if !state.processes.values().any(|p| p.pgid == pgid) {
1005                return Err(Errno::EPERM);
1006            }
1007
1008            // TODO: Suspend the calling process group if it is in the background
1009            // and not ignoring or blocking SIGTTOU.
1010
1011            state.foreground = Some(pgid);
1012            Ok(())
1013        }
1014
1015        ready(inner(self, fd, pgid))
1016    }
1017}
1018
1019impl Fork for VirtualSystem {
1020    /// Creates a new child process.
1021    ///
1022    /// This implementation does not create any real child process. Instead,
1023    /// it returns a child process starter that runs its task concurrently in
1024    /// the same process.
1025    ///
1026    /// To run the concurrent task, this function needs an executor that has
1027    /// been set in the [`SystemState`]. If the system state does not have an
1028    /// executor, this function fails with `Errno::ENOSYS`.
1029    ///
1030    /// The process ID of the child will be the maximum of existing process IDs
1031    /// plus 1. If there are no other processes, it will be 2.
1032    fn new_child_process(&self) -> Result<ChildProcessStarter<Self>> {
1033        let mut state = self.state.borrow_mut();
1034        let executor = state.executor.clone().ok_or(Errno::ENOSYS)?;
1035        let process_id = state
1036            .processes
1037            .keys()
1038            .max()
1039            .map_or(Pid(2), |pid| Pid(pid.0 + 1));
1040        let parent_process = &state.processes[&self.process_id];
1041        let child_process = Process::fork_from(self.process_id, parent_process);
1042        state.processes.insert(process_id, child_process);
1043        drop(state);
1044
1045        let state = Rc::clone(&self.state);
1046        Ok(Box::new(move |parent_env, task| {
1047            let system = VirtualSystem { state, process_id };
1048            let mut child_env = parent_env.clone_with_system(system.clone());
1049
1050            {
1051                let mut process = system.current_process_mut();
1052                process.selector = Rc::downgrade(&child_env.system.0);
1053            }
1054
1055            let run_task_and_set_exit_status = Box::pin(async move {
1056                let runner = ProcessRunner {
1057                    task: task(&mut child_env),
1058                    system,
1059                    waker: Rc::new(Cell::new(None)),
1060                };
1061                runner.await;
1062            });
1063
1064            executor
1065                .spawn(run_task_and_set_exit_status)
1066                .expect("the executor failed to start the child process task");
1067
1068            process_id
1069        }))
1070    }
1071}
1072
1073impl Wait for VirtualSystem {
1074    /// Waits for a child.
1075    ///
1076    /// TODO: Currently, this function only supports `target == -1 || target > 0`.
1077    fn wait(&self, target: Pid) -> Result<Option<(Pid, ProcessState)>> {
1078        let parent_pid = self.process_id;
1079        let mut state = self.state.borrow_mut();
1080        if let Some((pid, process)) = state.child_to_wait_for(parent_pid, target) {
1081            if process.state_has_changed() {
1082                Ok(Some((pid, process.take_state())))
1083            } else if process.state().is_alive() {
1084                Ok(None)
1085            } else {
1086                Err(Errno::ECHILD)
1087            }
1088        } else {
1089            Err(Errno::ECHILD)
1090        }
1091    }
1092}
1093
1094impl Exec for VirtualSystem {
1095    /// Stub for the `execve` system call.
1096    ///
1097    /// The `execve` system call cannot be simulated in the userland. This
1098    /// function returns `ENOSYS` if the file at `path` is a native executable,
1099    /// `ENOEXEC` if a non-executable file, and `ENOENT` otherwise.
1100    fn execve(
1101        &self,
1102        path: &CStr,
1103        args: &[CString],
1104        envs: &[CString],
1105    ) -> impl Future<Output = Result<Infallible>> + use<> {
1106        let os_path = UnixStr::from_bytes(path.to_bytes());
1107        let mut state = self.state.borrow_mut();
1108        let fs = &state.file_system;
1109        let file = match fs.get(os_path) {
1110            Ok(file) => file,
1111            Err(e) => return ready(Err(e)),
1112        };
1113        // TODO Check file permissions
1114        let is_executable = matches!(
1115            &file.borrow().body,
1116            FileBody::Regular {
1117                is_native_executable: true,
1118                ..
1119            }
1120        );
1121        if is_executable {
1122            // Save arguments in the Process
1123            let process = state.processes.get_mut(&self.process_id).unwrap();
1124            let path = path.to_owned();
1125            let args = args.to_owned();
1126            let envs = envs.to_owned();
1127            process.last_exec = Some((path, args, envs));
1128
1129            // TODO: We should abort the currently running task and start the new one.
1130            // Just returning `pending()` would break existing tests that rely on
1131            // the current behavior.
1132            ready(Err(Errno::ENOSYS))
1133        } else {
1134            ready(Err(Errno::ENOEXEC))
1135        }
1136    }
1137}
1138
1139impl Exit for VirtualSystem {
1140    fn exit(&self, exit_status: ExitStatus) -> impl Future<Output = Infallible> + use<> {
1141        let mut myself = self.current_process_mut();
1142        let parent_pid = myself.ppid;
1143        let exited = myself.set_state(ProcessState::exited(exit_status));
1144        drop(myself);
1145        if exited {
1146            raise_sigchld(&mut self.state.borrow_mut(), parent_pid);
1147        }
1148
1149        pending()
1150    }
1151}
1152
1153impl GetUid for VirtualSystem {
1154    fn getuid(&self) -> Uid {
1155        self.current_process().uid()
1156    }
1157
1158    fn geteuid(&self) -> Uid {
1159        self.current_process().euid()
1160    }
1161
1162    fn getgid(&self) -> Gid {
1163        self.current_process().gid()
1164    }
1165
1166    fn getegid(&self) -> Gid {
1167        self.current_process().egid()
1168    }
1169}
1170
1171impl GetPw for VirtualSystem {
1172    fn getpwnam_dir(&self, name: &CStr) -> Result<Option<PathBuf>> {
1173        let state = self.state.borrow();
1174        let name = match name.to_str() {
1175            Ok(name) => name,
1176            Err(_utf8_error) => return Ok(None),
1177        };
1178        Ok(state.home_dirs.get(name).cloned())
1179    }
1180}
1181
1182impl Sysconf for VirtualSystem {
1183    /// Returns the standard path for the system.
1184    ///
1185    /// This function returns the value of [`SystemState::path`]. If it is empty,
1186    /// it returns the `ENOSYS` error.
1187    fn confstr_path(&self) -> Result<UnixString> {
1188        let path = self.state.borrow().path.clone();
1189        if path.is_empty() {
1190            Err(Errno::ENOSYS)
1191        } else {
1192            Ok(path)
1193        }
1194    }
1195}
1196
1197impl ShellPath for VirtualSystem {
1198    /// Returns the path to the shell.
1199    ///
1200    /// The current implementation returns "/bin/sh".
1201    fn shell_path(&self) -> CString {
1202        c"/bin/sh".to_owned()
1203    }
1204}
1205
1206impl GetRlimit for VirtualSystem {
1207    fn getrlimit(&self, resource: Resource) -> Result<LimitPair> {
1208        Ok(self
1209            .current_process()
1210            .resource_limits
1211            .get(&resource)
1212            .copied()
1213            .unwrap_or(LimitPair {
1214                soft: INFINITY,
1215                hard: INFINITY,
1216            }))
1217    }
1218}
1219
1220impl SetRlimit for VirtualSystem {
1221    fn setrlimit(&self, resource: Resource, limits: LimitPair) -> Result<()> {
1222        if limits.soft_exceeds_hard() {
1223            return Err(Errno::EINVAL);
1224        }
1225
1226        let mut process = self.current_process_mut();
1227        use std::collections::hash_map::Entry::{Occupied, Vacant};
1228        match process.resource_limits.entry(resource) {
1229            Occupied(occupied) => {
1230                let occupied = occupied.into_mut();
1231                if limits.hard > occupied.hard {
1232                    return Err(Errno::EPERM);
1233                }
1234                *occupied = limits;
1235            }
1236            Vacant(vacant) => {
1237                vacant.insert(limits);
1238            }
1239        }
1240        Ok(())
1241    }
1242}
1243
1244fn send_signal_to_processes(
1245    state: &mut SystemState,
1246    target_pgid: Option<Pid>,
1247    signal: Option<signal::Number>,
1248) -> Result<()> {
1249    let mut results = Vec::new();
1250
1251    if let Some(signal) = signal {
1252        for (&_pid, process) in &mut state.processes {
1253            if target_pgid.is_none_or(|target_pgid| process.pgid == target_pgid) {
1254                let result = process.raise_signal(signal);
1255                results.push((result, process.ppid));
1256            }
1257        }
1258    }
1259
1260    if results.is_empty() {
1261        Err(Errno::ESRCH)
1262    } else {
1263        for (result, ppid) in results {
1264            if result.process_state_changed {
1265                raise_sigchld(state, ppid);
1266            }
1267        }
1268        Ok(())
1269    }
1270}
1271
1272fn raise_sigchld(state: &mut SystemState, target_pid: Pid) {
1273    if let Some(target) = state.processes.get_mut(&target_pid) {
1274        let result = target.raise_signal(signal::SIGCHLD);
1275        assert!(!result.process_state_changed);
1276    }
1277}
1278
1279/// State of the virtual system.
1280#[derive(Clone, Debug, Default)]
1281pub struct SystemState {
1282    /// Current time
1283    pub now: Option<Instant>,
1284
1285    /// Consumed CPU time statistics
1286    pub times: CpuTimes,
1287
1288    /// Task manager that can execute asynchronous tasks
1289    ///
1290    /// The virtual system uses this executor to run (virtual) child processes.
1291    /// If `executor` is `None`, [`VirtualSystem::new_child_process`] will fail.
1292    pub executor: Option<Rc<dyn Executor>>,
1293
1294    /// Processes running in the system
1295    pub processes: BTreeMap<Pid, Process>,
1296
1297    /// Process group ID of the foreground process group
1298    ///
1299    /// Note: The current implementation does not support the notion of
1300    /// controlling terminals and sessions. This item may be replaced with a
1301    /// more _correct_ implementation in the future.
1302    pub foreground: Option<Pid>,
1303
1304    /// Collection of files existing in the virtual system
1305    pub file_system: FileSystem,
1306
1307    /// Map from user names to their home directory paths
1308    ///
1309    /// [`VirtualSystem::getpwnam_dir`] looks up its argument in this
1310    /// dictionary.
1311    pub home_dirs: HashMap<String, PathBuf>,
1312
1313    /// Standard path returned by [`VirtualSystem::confstr_path`]
1314    pub path: UnixString,
1315}
1316
1317impl SystemState {
1318    /// Performs [`select`](crate::system::SharedSystem::select) on all
1319    /// processes in the system.
1320    ///
1321    /// Any errors are ignored.
1322    ///
1323    /// The `RefCell` must not have been borrowed, or this function will panic
1324    /// with a double borrow.
1325    pub fn select_all(this: &RefCell<Self>) {
1326        let mut selectors = Vec::new();
1327        for process in this.borrow().processes.values() {
1328            if let Some(selector) = process.selector.upgrade() {
1329                selectors.push(selector);
1330            }
1331        }
1332        // To avoid double borrowing, SelectSystem::select must be called after
1333        // dropping the borrow for `this`
1334        for selector in selectors {
1335            // TODO merge advances of `now` performed by each select
1336            selector.borrow_mut().select(false).ok();
1337        }
1338    }
1339
1340    /// Finds a child process to wait for.
1341    ///
1342    /// This is a helper function for `VirtualSystem::wait`.
1343    fn child_to_wait_for(&mut self, parent_pid: Pid, target: Pid) -> Option<(Pid, &mut Process)> {
1344        match target.0 {
1345            0 => todo!("wait target {}", target),
1346            -1 => {
1347                // any child
1348                let mut result = None;
1349                for (pid, process) in &mut self.processes {
1350                    if process.ppid == parent_pid {
1351                        let changed = process.state_has_changed();
1352                        result = Some((*pid, process));
1353                        if changed {
1354                            break;
1355                        }
1356                    }
1357                }
1358                result
1359            }
1360            raw if raw >= 0 => {
1361                let process = self.processes.get_mut(&target)?;
1362                if process.ppid == parent_pid {
1363                    Some((target, process))
1364                } else {
1365                    None
1366                }
1367            }
1368            _target => todo!("wait target {}", target),
1369        }
1370    }
1371}
1372
1373/// Executor that can start new async tasks.
1374///
1375/// This trait abstracts the executor interface so that [`SystemState`] does not
1376/// depend on a specific executor implementation.
1377///
1378/// Note that [`VirtualSystem`] does not support multi-threading. The executor
1379/// should run concurrent tasks on a single thread.
1380pub trait Executor: Debug {
1381    /// Starts a new async task.
1382    ///
1383    /// Returns `Ok(())` if the task has been started successfully and `Err(_)`
1384    /// otherwise.
1385    fn spawn(
1386        &self,
1387        task: Pin<Box<dyn Future<Output = ()>>>,
1388    ) -> std::result::Result<(), Box<dyn std::error::Error>>;
1389}
1390
1391/// Concurrent task that manages the execution of a process.
1392///
1393/// This struct is a helper for [`VirtualSystem::new_child_process`].
1394/// It basically runs the given task, but pauses or cancels it depending on
1395/// the state of the process.
1396struct ProcessRunner<'a> {
1397    task: Pin<Box<dyn Future<Output = Infallible> + 'a>>,
1398    system: VirtualSystem,
1399
1400    /// Waker that is woken up when the process is resumed.
1401    waker: Rc<Cell<Option<Waker>>>,
1402}
1403
1404impl Future for ProcessRunner<'_> {
1405    type Output = ();
1406
1407    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
1408        let this = self.deref_mut();
1409
1410        let process_state = this.system.current_process().state;
1411        if process_state == ProcessState::Running {
1412            // Let the task make progress
1413            let poll = this.task.as_mut().poll(cx);
1414            match poll {
1415                // unreachable: Poll::Ready(_) => todo!(),
1416                Poll::Pending => (),
1417            }
1418        }
1419
1420        let mut process = this.system.current_process_mut();
1421        match process.state {
1422            ProcessState::Running => Poll::Pending,
1423            ProcessState::Halted(result) => {
1424                if result.is_stopped() {
1425                    this.waker.set(Some(cx.waker().clone()));
1426                    process.wake_on_resumption(Rc::downgrade(&this.waker));
1427                    Poll::Pending
1428                } else {
1429                    Poll::Ready(())
1430                }
1431            }
1432        }
1433    }
1434}
1435
1436#[cfg(test)]
1437mod tests {
1438    use super::*;
1439    use crate::Env;
1440    use crate::job::ProcessResult;
1441    use crate::system::FileType;
1442    use assert_matches::assert_matches;
1443    use futures_executor::LocalPool;
1444    use futures_util::FutureExt as _;
1445    use std::future::pending;
1446
1447    impl Executor for futures_executor::LocalSpawner {
1448        fn spawn(
1449            &self,
1450            task: Pin<Box<dyn Future<Output = ()>>>,
1451        ) -> std::result::Result<(), Box<dyn std::error::Error>> {
1452            use futures_util::task::LocalSpawnExt;
1453            self.spawn_local(task)
1454                .map_err(|e| Box::new(e) as Box<dyn std::error::Error>)
1455        }
1456    }
1457
1458    #[test]
1459    fn fstatat_non_existent_file() {
1460        let system = VirtualSystem::new();
1461        assert_matches!(
1462            system.fstatat(Fd(0), c"/no/such/file", true),
1463            Err(Errno::ENOENT)
1464        );
1465    }
1466
1467    #[test]
1468    fn fstatat_regular_file() {
1469        let system = VirtualSystem::new();
1470        let path = "/some/file";
1471        let content = Rc::new(RefCell::new(Inode::new([1, 2, 3, 42, 100])));
1472        let mut state = system.state.borrow_mut();
1473        state.file_system.save(path, content).unwrap();
1474        drop(state);
1475
1476        let stat = system.fstatat(Fd(0), c"/some/file", true).unwrap();
1477        assert_eq!(stat.mode, Mode::default());
1478        assert_eq!(stat.r#type, FileType::Regular);
1479        assert_eq!(stat.size, 5);
1480        // TODO Other stat properties
1481    }
1482
1483    #[test]
1484    fn fstatat_directory() {
1485        let system = VirtualSystem::new();
1486        let path = "/some/file";
1487        let content = Rc::new(RefCell::new(Inode::new([])));
1488        let mut state = system.state.borrow_mut();
1489        state.file_system.save(path, content).unwrap();
1490        drop(state);
1491
1492        let stat = system.fstatat(Fd(0), c"/some/", true).unwrap();
1493        assert_eq!(stat.mode, Mode::from_bits_retain(0o755));
1494        assert_eq!(stat.r#type, FileType::Directory);
1495        // TODO Other stat properties
1496    }
1497
1498    #[test]
1499    fn fstatat_fifo() {
1500        let system = VirtualSystem::new();
1501        let path = "/some/fifo";
1502        let content = Rc::new(RefCell::new(Inode {
1503            body: FileBody::Fifo {
1504                content: [17; 42].into(),
1505                readers: 0,
1506                writers: 0,
1507            },
1508            permissions: Mode::default(),
1509        }));
1510        let mut state = system.state.borrow_mut();
1511        state.file_system.save(path, content).unwrap();
1512        drop(state);
1513
1514        let stat = system.fstatat(Fd(0), c"/some/fifo", true).unwrap();
1515        assert_eq!(stat.mode, Mode::default());
1516        assert_eq!(stat.r#type, FileType::Fifo);
1517        assert_eq!(stat.size, 42);
1518    }
1519
1520    fn system_with_symlink() -> VirtualSystem {
1521        let system = VirtualSystem::new();
1522        let mut state = system.state.borrow_mut();
1523        state
1524            .file_system
1525            .save("/some/file", Rc::new(RefCell::new(Inode::new([]))))
1526            .unwrap();
1527        state
1528            .file_system
1529            .save(
1530                "/link",
1531                Rc::new(RefCell::new(Inode {
1532                    body: FileBody::Symlink {
1533                        target: "some/file".into(),
1534                    },
1535                    permissions: Mode::default(),
1536                })),
1537            )
1538            .unwrap();
1539        drop(state);
1540        system
1541    }
1542
1543    #[test]
1544    fn fstatat_symlink_to_regular_file() {
1545        let system = system_with_symlink();
1546        let stat = system.fstatat(Fd(0), c"/link", true).unwrap();
1547        assert_eq!(stat.r#type, FileType::Regular);
1548    }
1549
1550    #[test]
1551    fn fstatat_symlink_no_follow() {
1552        let system = system_with_symlink();
1553        let stat = system.fstatat(Fd(0), c"/link", false).unwrap();
1554        assert_eq!(stat.r#type, FileType::Symlink);
1555    }
1556
1557    #[test]
1558    fn is_executable_file_non_existing_file() {
1559        let system = VirtualSystem::new();
1560        assert!(!system.is_executable_file(c"/no/such/file"));
1561    }
1562
1563    #[test]
1564    fn is_executable_file_existing_but_non_executable_file() {
1565        let system = VirtualSystem::new();
1566        let path = "/some/file";
1567        let content = Rc::new(RefCell::new(Inode::default()));
1568        let mut state = system.state.borrow_mut();
1569        state.file_system.save(path, content).unwrap();
1570        drop(state);
1571        assert!(!system.is_executable_file(c"/some/file"));
1572    }
1573
1574    #[test]
1575    fn is_executable_file_with_executable_file() {
1576        let system = VirtualSystem::new();
1577        let path = "/some/file";
1578        let mut content = Inode::default();
1579        content.permissions.set(Mode::USER_EXEC, true);
1580        let content = Rc::new(RefCell::new(content));
1581        let mut state = system.state.borrow_mut();
1582        state.file_system.save(path, content).unwrap();
1583        drop(state);
1584        assert!(system.is_executable_file(c"/some/file"));
1585    }
1586
1587    #[test]
1588    fn pipe_read_write() {
1589        let system = VirtualSystem::new();
1590        let (reader, writer) = system.pipe().unwrap();
1591        let result = system.write(writer, &[5, 42, 29]);
1592        assert_eq!(result, Ok(3));
1593
1594        let mut buffer = [1; 4];
1595        let result = system.read(reader, &mut buffer);
1596        assert_eq!(result, Ok(3));
1597        assert_eq!(buffer, [5, 42, 29, 1]);
1598
1599        let result = system.close(writer);
1600        assert_eq!(result, Ok(()));
1601
1602        let result = system.read(reader, &mut buffer);
1603        assert_eq!(result, Ok(0));
1604    }
1605
1606    #[test]
1607    fn dup_shares_open_file_description() {
1608        let system = VirtualSystem::new();
1609        let result = system.dup(Fd::STDOUT, Fd::STDERR, EnumSet::empty());
1610        assert_eq!(result, Ok(Fd(3)));
1611
1612        let process = system.current_process();
1613        let fd1 = process.fds.get(&Fd(1)).unwrap();
1614        let fd3 = process.fds.get(&Fd(3)).unwrap();
1615        assert_eq!(fd1, fd3);
1616    }
1617
1618    #[test]
1619    fn dup_can_set_cloexec() {
1620        let system = VirtualSystem::new();
1621        let result = system.dup(Fd::STDOUT, Fd::STDERR, FdFlag::CloseOnExec.into());
1622        assert_eq!(result, Ok(Fd(3)));
1623
1624        let process = system.current_process();
1625        let fd3 = process.fds.get(&Fd(3)).unwrap();
1626        assert_eq!(fd3.flags, EnumSet::only(FdFlag::CloseOnExec));
1627    }
1628
1629    #[test]
1630    fn dup2_shares_open_file_description() {
1631        let system = VirtualSystem::new();
1632        let result = system.dup2(Fd::STDOUT, Fd(5));
1633        assert_eq!(result, Ok(Fd(5)));
1634
1635        let process = system.current_process();
1636        let fd1 = process.fds.get(&Fd(1)).unwrap();
1637        let fd5 = process.fds.get(&Fd(5)).unwrap();
1638        assert_eq!(fd1, fd5);
1639    }
1640
1641    #[test]
1642    fn dup2_clears_cloexec() {
1643        let system = VirtualSystem::new();
1644        let mut process = system.current_process_mut();
1645        process.fds.get_mut(&Fd::STDOUT).unwrap().flags = FdFlag::CloseOnExec.into();
1646        drop(process);
1647
1648        let result = system.dup2(Fd::STDOUT, Fd(6));
1649        assert_eq!(result, Ok(Fd(6)));
1650
1651        let process = system.current_process();
1652        let fd6 = process.fds.get(&Fd(6)).unwrap();
1653        assert_eq!(fd6.flags, EnumSet::empty());
1654    }
1655
1656    #[test]
1657    fn open_non_existing_file_no_creation() {
1658        let system = VirtualSystem::new();
1659        let result = system.open(
1660            c"/no/such/file",
1661            OfdAccess::ReadOnly,
1662            EnumSet::empty(),
1663            Mode::empty(),
1664        );
1665        assert_eq!(result, Err(Errno::ENOENT));
1666    }
1667
1668    #[test]
1669    fn open_creating_non_existing_file() {
1670        let system = VirtualSystem::new();
1671        let result = system.open(
1672            c"new_file",
1673            OfdAccess::WriteOnly,
1674            OpenFlag::Create.into(),
1675            Mode::empty(),
1676        );
1677        assert_eq!(result, Ok(Fd(3)));
1678
1679        system.write(Fd(3), &[42, 123]).unwrap();
1680        let file = system.state.borrow().file_system.get("new_file").unwrap();
1681        let file = file.borrow();
1682        assert_eq!(file.permissions, Mode::empty());
1683        assert_matches!(&file.body, FileBody::Regular { content, .. } => {
1684            assert_eq!(content[..], [42, 123]);
1685        });
1686    }
1687
1688    #[test]
1689    fn open_creating_non_existing_file_umask() {
1690        let system = VirtualSystem::new();
1691        system.umask(Mode::from_bits_retain(0o125));
1692        system
1693            .open(
1694                c"file",
1695                OfdAccess::WriteOnly,
1696                OpenFlag::Create.into(),
1697                Mode::ALL_9,
1698            )
1699            .unwrap();
1700
1701        let file = system.state.borrow().file_system.get("file").unwrap();
1702        let file = file.borrow();
1703        assert_eq!(file.permissions, Mode::from_bits_retain(0o652));
1704    }
1705
1706    #[test]
1707    fn open_existing_file() {
1708        let system = VirtualSystem::new();
1709        let fd = system
1710            .open(
1711                c"file",
1712                OfdAccess::WriteOnly,
1713                OpenFlag::Create.into(),
1714                Mode::empty(),
1715            )
1716            .unwrap();
1717        system.write(fd, &[75, 96, 133]).unwrap();
1718
1719        let result = system.open(
1720            c"file",
1721            OfdAccess::ReadOnly,
1722            EnumSet::empty(),
1723            Mode::empty(),
1724        );
1725        assert_eq!(result, Ok(Fd(4)));
1726
1727        let mut buffer = [0; 5];
1728        let count = system.read(Fd(4), &mut buffer).unwrap();
1729        assert_eq!(count, 3);
1730        assert_eq!(buffer, [75, 96, 133, 0, 0]);
1731        let count = system.read(Fd(4), &mut buffer).unwrap();
1732        assert_eq!(count, 0);
1733    }
1734
1735    #[test]
1736    fn open_existing_file_excl() {
1737        let system = VirtualSystem::new();
1738        let first = system.open(
1739            c"my_file",
1740            OfdAccess::WriteOnly,
1741            OpenFlag::Create | OpenFlag::Exclusive,
1742            Mode::empty(),
1743        );
1744        assert_eq!(first, Ok(Fd(3)));
1745
1746        let second = system.open(
1747            c"my_file",
1748            OfdAccess::WriteOnly,
1749            OpenFlag::Create | OpenFlag::Exclusive,
1750            Mode::empty(),
1751        );
1752        assert_eq!(second, Err(Errno::EEXIST));
1753    }
1754
1755    #[test]
1756    fn open_truncating() {
1757        let system = VirtualSystem::new();
1758        let fd = system
1759            .open(
1760                c"file",
1761                OfdAccess::WriteOnly,
1762                OpenFlag::Create.into(),
1763                Mode::ALL_9,
1764            )
1765            .unwrap();
1766        system.write(fd, &[1, 2, 3]).unwrap();
1767
1768        let result = system.open(
1769            c"file",
1770            OfdAccess::WriteOnly,
1771            OpenFlag::Truncate.into(),
1772            Mode::empty(),
1773        );
1774        assert_eq!(result, Ok(Fd(4)));
1775
1776        let reader = system
1777            .open(
1778                c"file",
1779                OfdAccess::ReadOnly,
1780                EnumSet::empty(),
1781                Mode::empty(),
1782            )
1783            .unwrap();
1784        let count = system.read(reader, &mut [0; 1]).unwrap();
1785        assert_eq!(count, 0);
1786    }
1787
1788    #[test]
1789    fn open_appending() {
1790        let system = VirtualSystem::new();
1791        let fd = system
1792            .open(
1793                c"file",
1794                OfdAccess::WriteOnly,
1795                OpenFlag::Create.into(),
1796                Mode::ALL_9,
1797            )
1798            .unwrap();
1799        system.write(fd, &[1, 2, 3]).unwrap();
1800
1801        let result = system.open(
1802            c"file",
1803            OfdAccess::WriteOnly,
1804            OpenFlag::Append.into(),
1805            Mode::empty(),
1806        );
1807        assert_eq!(result, Ok(Fd(4)));
1808        system.write(Fd(4), &[4, 5, 6]).unwrap();
1809
1810        let reader = system
1811            .open(
1812                c"file",
1813                OfdAccess::ReadOnly,
1814                EnumSet::empty(),
1815                Mode::empty(),
1816            )
1817            .unwrap();
1818        let mut buffer = [0; 7];
1819        let count = system.read(reader, &mut buffer).unwrap();
1820        assert_eq!(count, 6);
1821        assert_eq!(buffer, [1, 2, 3, 4, 5, 6, 0]);
1822    }
1823
1824    #[test]
1825    fn open_directory() {
1826        let system = VirtualSystem::new();
1827
1828        // Create a regular file and its parent directory
1829        let _ = system.open(
1830            c"/dir/file",
1831            OfdAccess::WriteOnly,
1832            OpenFlag::Create.into(),
1833            Mode::empty(),
1834        );
1835
1836        let result = system.open(
1837            c"/dir",
1838            OfdAccess::ReadOnly,
1839            OpenFlag::Directory.into(),
1840            Mode::empty(),
1841        );
1842        assert_eq!(result, Ok(Fd(4)));
1843    }
1844
1845    #[test]
1846    fn open_non_directory_path_prefix() {
1847        let system = VirtualSystem::new();
1848
1849        // Create a regular file
1850        let _ = system.open(
1851            c"/file",
1852            OfdAccess::WriteOnly,
1853            OpenFlag::Create.into(),
1854            Mode::empty(),
1855        );
1856
1857        let result = system.open(
1858            c"/file/file",
1859            OfdAccess::WriteOnly,
1860            OpenFlag::Create.into(),
1861            Mode::empty(),
1862        );
1863        assert_eq!(result, Err(Errno::ENOTDIR));
1864    }
1865
1866    #[test]
1867    fn open_non_directory_file() {
1868        let system = VirtualSystem::new();
1869
1870        // Create a regular file
1871        let _ = system.open(
1872            c"/file",
1873            OfdAccess::WriteOnly,
1874            OpenFlag::Create.into(),
1875            Mode::empty(),
1876        );
1877
1878        let result = system.open(
1879            c"/file",
1880            OfdAccess::ReadOnly,
1881            OpenFlag::Directory.into(),
1882            Mode::empty(),
1883        );
1884        assert_eq!(result, Err(Errno::ENOTDIR));
1885    }
1886
1887    #[test]
1888    fn open_default_working_directory() {
1889        // The default working directory is the root directory.
1890        let system = VirtualSystem::new();
1891
1892        let writer = system.open(
1893            c"/dir/file",
1894            OfdAccess::WriteOnly,
1895            OpenFlag::Create.into(),
1896            Mode::ALL_9,
1897        );
1898        system.write(writer.unwrap(), &[1, 2, 3, 42]).unwrap();
1899
1900        let reader = system.open(
1901            c"./dir/file",
1902            OfdAccess::ReadOnly,
1903            EnumSet::empty(),
1904            Mode::empty(),
1905        );
1906        let mut buffer = [0; 10];
1907        let count = system.read(reader.unwrap(), &mut buffer).unwrap();
1908        assert_eq!(count, 4);
1909        assert_eq!(buffer[0..4], [1, 2, 3, 42]);
1910    }
1911
1912    #[test]
1913    fn open_tmpfile() {
1914        let system = VirtualSystem::new();
1915        let fd = system.open_tmpfile(Path::new("")).unwrap();
1916        system.write(fd, &[42, 17, 75]).unwrap();
1917        system.lseek(fd, SeekFrom::Start(0)).unwrap();
1918        let mut buffer = [0; 4];
1919        let count = system.read(fd, &mut buffer).unwrap();
1920        assert_eq!(count, 3);
1921        assert_eq!(buffer[..3], [42, 17, 75]);
1922    }
1923
1924    #[test]
1925    fn close() {
1926        let system = VirtualSystem::new();
1927
1928        let result = system.close(Fd::STDERR);
1929        assert_eq!(result, Ok(()));
1930        assert_eq!(system.current_process().fds.get(&Fd::STDERR), None);
1931
1932        let result = system.close(Fd::STDERR);
1933        assert_eq!(result, Ok(()));
1934    }
1935
1936    #[test]
1937    fn fcntl_getfd_and_setfd() {
1938        let system = VirtualSystem::new();
1939
1940        let flags = system.fcntl_getfd(Fd::STDIN).unwrap();
1941        assert_eq!(flags, EnumSet::empty());
1942
1943        system
1944            .fcntl_setfd(Fd::STDIN, FdFlag::CloseOnExec.into())
1945            .unwrap();
1946
1947        let flags = system.fcntl_getfd(Fd::STDIN).unwrap();
1948        assert_eq!(flags, EnumSet::only(FdFlag::CloseOnExec));
1949
1950        let flags = system.fcntl_getfd(Fd::STDOUT).unwrap();
1951        assert_eq!(flags, EnumSet::empty());
1952
1953        system.fcntl_setfd(Fd::STDIN, EnumSet::empty()).unwrap();
1954
1955        let flags = system.fcntl_getfd(Fd::STDIN).unwrap();
1956        assert_eq!(flags, EnumSet::empty());
1957    }
1958
1959    #[test]
1960    fn opendir_default_working_directory() {
1961        // The default working directory is the root directory.
1962        let system = VirtualSystem::new();
1963
1964        let _ = system.open(
1965            c"/dir/file",
1966            OfdAccess::WriteOnly,
1967            OpenFlag::Create.into(),
1968            Mode::ALL_9,
1969        );
1970
1971        let mut dir = system.opendir(c"./dir").unwrap();
1972        let mut files = Vec::new();
1973        while let Some(entry) = dir.next().unwrap() {
1974            files.push(entry.name.to_unix_string());
1975        }
1976        files.sort_unstable();
1977        assert_eq!(
1978            files[..],
1979            [
1980                UnixString::from("."),
1981                UnixString::from(".."),
1982                UnixString::from("file")
1983            ]
1984        );
1985    }
1986
1987    // TODO Test sigmask
1988
1989    #[test]
1990    fn kill_process() {
1991        let system = VirtualSystem::new();
1992        system
1993            .kill(system.process_id, None)
1994            .now_or_never()
1995            .unwrap()
1996            .unwrap();
1997        assert_eq!(system.current_process().state(), ProcessState::Running);
1998
1999        let result = system.kill(system.process_id, Some(SIGINT)).now_or_never();
2000        // The future should be pending because the current process has been killed
2001        assert_eq!(result, None);
2002        assert_eq!(
2003            system.current_process().state(),
2004            ProcessState::Halted(ProcessResult::Signaled {
2005                signal: SIGINT,
2006                core_dump: false
2007            })
2008        );
2009
2010        let system = VirtualSystem::new();
2011        let state = system.state.borrow();
2012        let max_pid = *state.processes.keys().max().unwrap();
2013        drop(state);
2014        let e = system
2015            .kill(Pid(max_pid.0 + 1), Some(SIGINT))
2016            .now_or_never()
2017            .unwrap()
2018            .unwrap_err();
2019        assert_eq!(e, Errno::ESRCH);
2020    }
2021
2022    #[test]
2023    fn kill_all_processes() {
2024        let system = VirtualSystem::new();
2025        let pgid = system.current_process().pgid;
2026        let mut state = system.state.borrow_mut();
2027        state.processes.insert(
2028            Pid(10),
2029            Process::with_parent_and_group(system.process_id, pgid),
2030        );
2031        state.processes.insert(
2032            Pid(11),
2033            Process::with_parent_and_group(system.process_id, pgid),
2034        );
2035        state
2036            .processes
2037            .insert(Pid(21), Process::with_parent_and_group(Pid(10), Pid(21)));
2038        drop(state);
2039
2040        let result = system.kill(Pid::ALL, Some(SIGTERM)).now_or_never();
2041        // The future should be pending because the current process has been killed
2042        assert_eq!(result, None);
2043        let state = system.state.borrow();
2044        for process in state.processes.values() {
2045            assert_eq!(
2046                process.state,
2047                ProcessState::Halted(ProcessResult::Signaled {
2048                    signal: SIGTERM,
2049                    core_dump: false
2050                })
2051            );
2052        }
2053    }
2054
2055    #[test]
2056    fn kill_processes_in_same_group() {
2057        let system = VirtualSystem::new();
2058        let pgid = system.current_process().pgid;
2059        let mut state = system.state.borrow_mut();
2060        state.processes.insert(
2061            Pid(10),
2062            Process::with_parent_and_group(system.process_id, pgid),
2063        );
2064        state.processes.insert(
2065            Pid(11),
2066            Process::with_parent_and_group(system.process_id, pgid),
2067        );
2068        state
2069            .processes
2070            .insert(Pid(21), Process::with_parent_and_group(Pid(10), Pid(21)));
2071        drop(state);
2072
2073        let result = system
2074            .kill(Pid::MY_PROCESS_GROUP, Some(SIGQUIT))
2075            .now_or_never();
2076        // The future should be pending because the current process has been killed
2077        assert_eq!(result, None);
2078        let state = system.state.borrow();
2079        assert_eq!(
2080            state.processes[&system.process_id].state,
2081            ProcessState::Halted(ProcessResult::Signaled {
2082                signal: SIGQUIT,
2083                core_dump: true
2084            })
2085        );
2086        assert_eq!(
2087            state.processes[&Pid(10)].state,
2088            ProcessState::Halted(ProcessResult::Signaled {
2089                signal: SIGQUIT,
2090                core_dump: true
2091            })
2092        );
2093        assert_eq!(
2094            state.processes[&Pid(11)].state,
2095            ProcessState::Halted(ProcessResult::Signaled {
2096                signal: SIGQUIT,
2097                core_dump: true
2098            })
2099        );
2100        assert_eq!(state.processes[&Pid(21)].state, ProcessState::Running);
2101    }
2102
2103    #[test]
2104    fn kill_process_group() {
2105        let system = VirtualSystem::new();
2106        let pgid = system.current_process().pgid;
2107        let mut state = system.state.borrow_mut();
2108        state.processes.insert(
2109            Pid(10),
2110            Process::with_parent_and_group(system.process_id, pgid),
2111        );
2112        state.processes.insert(
2113            Pid(11),
2114            Process::with_parent_and_group(system.process_id, Pid(21)),
2115        );
2116        state
2117            .processes
2118            .insert(Pid(21), Process::with_parent_and_group(Pid(10), Pid(21)));
2119        drop(state);
2120
2121        system
2122            .kill(Pid(-21), Some(SIGHUP))
2123            .now_or_never()
2124            .unwrap()
2125            .unwrap();
2126        let state = system.state.borrow();
2127        assert_eq!(
2128            state.processes[&system.process_id].state,
2129            ProcessState::Running
2130        );
2131        assert_eq!(state.processes[&Pid(10)].state, ProcessState::Running);
2132        assert_eq!(
2133            state.processes[&Pid(11)].state,
2134            ProcessState::Halted(ProcessResult::Signaled {
2135                signal: SIGHUP,
2136                core_dump: false
2137            })
2138        );
2139        assert_eq!(
2140            state.processes[&Pid(21)].state,
2141            ProcessState::Halted(ProcessResult::Signaled {
2142                signal: SIGHUP,
2143                core_dump: false
2144            })
2145        );
2146    }
2147
2148    #[test]
2149    fn kill_returns_success_even_if_process_state_did_not_change() {
2150        let system = VirtualSystem::new();
2151        let pgid = system.current_process().pgid;
2152        let mut state = system.state.borrow_mut();
2153        state.processes.insert(
2154            Pid(10),
2155            Process::with_parent_and_group(system.process_id, pgid),
2156        );
2157        drop(state);
2158
2159        system
2160            .kill(-pgid, Some(SIGCONT))
2161            .now_or_never()
2162            .unwrap()
2163            .unwrap();
2164        let state = system.state.borrow();
2165        assert_eq!(state.processes[&Pid(10)].state, ProcessState::Running);
2166    }
2167
2168    #[test]
2169    fn select_regular_file_is_always_ready() {
2170        let system = VirtualSystem::new();
2171        let mut readers = vec![Fd::STDIN];
2172        let mut writers = vec![Fd::STDOUT, Fd::STDERR];
2173
2174        let result = system.select(&mut readers, &mut writers, None, None);
2175        assert_eq!(result, Ok(3));
2176        assert_eq!(readers, [Fd::STDIN]);
2177        assert_eq!(writers, [Fd::STDOUT, Fd::STDERR]);
2178    }
2179
2180    #[test]
2181    fn select_pipe_reader_is_ready_if_writer_is_closed() {
2182        let system = VirtualSystem::new();
2183        let (reader, writer) = system.pipe().unwrap();
2184        system.close(writer).unwrap();
2185        let mut readers = vec![reader];
2186        let mut writers = vec![];
2187
2188        let result = system.select(&mut readers, &mut writers, None, None);
2189        assert_eq!(result, Ok(1));
2190        assert_eq!(readers, [reader]);
2191        assert_eq!(writers, []);
2192    }
2193
2194    #[test]
2195    fn select_pipe_reader_is_ready_if_something_has_been_written() {
2196        let system = VirtualSystem::new();
2197        let (reader, writer) = system.pipe().unwrap();
2198        system.write(writer, &[0]).unwrap();
2199        let mut readers = vec![reader];
2200        let mut writers = vec![];
2201
2202        let result = system.select(&mut readers, &mut writers, None, None);
2203        assert_eq!(result, Ok(1));
2204        assert_eq!(readers, [reader]);
2205        assert_eq!(writers, []);
2206    }
2207
2208    #[test]
2209    fn select_pipe_reader_is_not_ready_if_writer_has_written_nothing() {
2210        let system = VirtualSystem::new();
2211        let (reader, _writer) = system.pipe().unwrap();
2212        let mut readers = vec![reader];
2213        let mut writers = vec![];
2214
2215        let result = system.select(&mut readers, &mut writers, None, None);
2216        assert_eq!(result, Ok(0));
2217        assert_eq!(readers, []);
2218        assert_eq!(writers, []);
2219    }
2220
2221    #[test]
2222    fn select_pipe_writer_is_ready_if_pipe_is_not_full() {
2223        let system = VirtualSystem::new();
2224        let (_reader, writer) = system.pipe().unwrap();
2225        let mut readers = vec![];
2226        let mut writers = vec![writer];
2227
2228        let result = system.select(&mut readers, &mut writers, None, None);
2229        assert_eq!(result, Ok(1));
2230        assert_eq!(readers, []);
2231        assert_eq!(writers, [writer]);
2232    }
2233
2234    #[test]
2235    fn select_on_unreadable_fd() {
2236        let system = VirtualSystem::new();
2237        let (_reader, writer) = system.pipe().unwrap();
2238        let mut fds = vec![writer];
2239        let result = system.select(&mut fds, &mut vec![], None, None);
2240        assert_eq!(result, Ok(1));
2241        assert_eq!(fds, [writer]);
2242    }
2243
2244    #[test]
2245    fn select_on_unwritable_fd() {
2246        let system = VirtualSystem::new();
2247        let (reader, _writer) = system.pipe().unwrap();
2248        let mut fds = vec![reader];
2249        let result = system.select(&mut vec![], &mut fds, None, None);
2250        assert_eq!(result, Ok(1));
2251        assert_eq!(fds, [reader]);
2252    }
2253
2254    #[test]
2255    fn select_on_closed_fd() {
2256        let system = VirtualSystem::new();
2257        let result = system.select(&mut vec![Fd(17)], &mut vec![], None, None);
2258        assert_eq!(result, Err(Errno::EBADF));
2259
2260        let result = system.select(&mut vec![], &mut vec![Fd(17)], None, None);
2261        assert_eq!(result, Err(Errno::EBADF));
2262    }
2263
2264    fn system_for_catching_sigchld() -> VirtualSystem {
2265        let system = VirtualSystem::new();
2266        system
2267            .sigmask(Some((SigmaskOp::Add, &[SIGCHLD])), None)
2268            .unwrap();
2269        system.sigaction(SIGCHLD, Disposition::Catch).unwrap();
2270        system
2271    }
2272
2273    #[test]
2274    fn select_on_non_pending_signal() {
2275        let system = system_for_catching_sigchld();
2276        let result = system.select(&mut vec![], &mut vec![], None, Some(&[]));
2277        assert_eq!(result, Ok(0));
2278        assert_eq!(system.caught_signals(), []);
2279    }
2280
2281    #[test]
2282    fn select_on_pending_signal() {
2283        let system = system_for_catching_sigchld();
2284        let _ = system.current_process_mut().raise_signal(SIGCHLD);
2285        let result = system.select(&mut vec![], &mut vec![], None, Some(&[]));
2286        assert_eq!(result, Err(Errno::EINTR));
2287        assert_eq!(system.caught_signals(), [SIGCHLD]);
2288    }
2289
2290    #[test]
2291    fn select_timeout() {
2292        let system = VirtualSystem::new();
2293        let now = Instant::now();
2294        system.state.borrow_mut().now = Some(now);
2295
2296        let (reader, _writer) = system.pipe().unwrap();
2297        let mut readers = vec![reader];
2298        let mut writers = vec![];
2299        let timeout = Duration::new(42, 195);
2300
2301        let result = system.select(&mut readers, &mut writers, Some(timeout), None);
2302        assert_eq!(result, Ok(0));
2303        assert_eq!(readers, []);
2304        assert_eq!(writers, []);
2305        assert_eq!(
2306            system.state.borrow().now,
2307            Some(now + Duration::new(42, 195))
2308        );
2309    }
2310
2311    fn virtual_system_with_executor() -> (VirtualSystem, LocalPool) {
2312        let system = VirtualSystem::new();
2313        let executor = LocalPool::new();
2314        system.state.borrow_mut().executor = Some(Rc::new(executor.spawner()));
2315        (system, executor)
2316    }
2317
2318    #[test]
2319    fn setpgid_creating_new_group_from_parent() {
2320        let (system, _executor) = virtual_system_with_executor();
2321        let state = Rc::clone(&system.state);
2322        let mut env = Env::with_system(system);
2323        let child = env.system.new_child_process().unwrap();
2324        let pid = child(&mut env, Box::new(|_env| Box::pin(pending())));
2325
2326        let result = env.system.setpgid(pid, pid);
2327        assert_eq!(result, Ok(()));
2328
2329        let pgid = state.borrow().processes[&pid].pgid();
2330        assert_eq!(pgid, pid);
2331    }
2332
2333    #[test]
2334    fn setpgid_creating_new_group_from_child() {
2335        let (system, mut executor) = virtual_system_with_executor();
2336        let state = Rc::clone(&system.state);
2337        let mut env = Env::with_system(system);
2338        let child = env.system.new_child_process().unwrap();
2339        let pid = child(
2340            &mut env,
2341            Box::new(|child_env| {
2342                Box::pin(async move {
2343                    let result = child_env.system.setpgid(Pid(0), Pid(0));
2344                    assert_eq!(result, Ok(()));
2345                    child_env.system.exit(child_env.exit_status).await
2346                })
2347            }),
2348        );
2349        executor.run_until_stalled();
2350
2351        let pgid = state.borrow().processes[&pid].pgid();
2352        assert_eq!(pgid, pid);
2353    }
2354
2355    #[test]
2356    fn setpgid_extending_existing_group_from_parent() {
2357        let (system, _executor) = virtual_system_with_executor();
2358        let state = Rc::clone(&system.state);
2359        let mut env = Env::with_system(system);
2360        let child_1 = env.system.new_child_process().unwrap();
2361        let pid_1 = child_1(&mut env, Box::new(|_env| Box::pin(pending())));
2362        env.system.setpgid(pid_1, pid_1).unwrap();
2363        let child_2 = env.system.new_child_process().unwrap();
2364        let pid_2 = child_2(&mut env, Box::new(|_env| Box::pin(pending())));
2365
2366        let result = env.system.setpgid(pid_2, pid_1);
2367        assert_eq!(result, Ok(()));
2368
2369        let pgid = state.borrow().processes[&pid_2].pgid();
2370        assert_eq!(pgid, pid_1);
2371    }
2372
2373    #[test]
2374    fn setpgid_with_nonexisting_pid() {
2375        let (system, _executor) = virtual_system_with_executor();
2376        let state = Rc::clone(&system.state);
2377        let mut env = Env::with_system(system);
2378        let child = env.system.new_child_process().unwrap();
2379        let pid = child(&mut env, Box::new(|_env| Box::pin(pending())));
2380
2381        let dummy_pid = Pid(123);
2382        let result = env.system.setpgid(dummy_pid, dummy_pid);
2383        assert_eq!(result, Err(Errno::ESRCH));
2384
2385        let pgid = state.borrow().processes[&pid].pgid();
2386        assert_eq!(pgid, Pid(1));
2387    }
2388
2389    #[test]
2390    fn setpgid_with_unrelated_pid() {
2391        let (system, mut executor) = virtual_system_with_executor();
2392        let parent_pid = system.process_id;
2393        let state = Rc::clone(&system.state);
2394        let mut env = Env::with_system(system);
2395        let child = env.system.new_child_process().unwrap();
2396        let _pid = child(
2397            &mut env,
2398            Box::new(move |child_env| {
2399                Box::pin(async move {
2400                    let result = child_env.system.setpgid(parent_pid, Pid(0));
2401                    assert_eq!(result, Err(Errno::ESRCH));
2402                    child_env.system.exit(child_env.exit_status).await
2403                })
2404            }),
2405        );
2406        executor.run_until_stalled();
2407
2408        let pgid = state.borrow().processes[&parent_pid].pgid();
2409        assert_eq!(pgid, Pid(1));
2410    }
2411
2412    #[test]
2413    fn setpgid_with_execed_child() {
2414        let (system, mut executor) = virtual_system_with_executor();
2415        let path = "/some/file";
2416        let mut content = Inode::default();
2417        content.body = FileBody::Regular {
2418            content: vec![],
2419            is_native_executable: true,
2420        };
2421        content.permissions.set(Mode::USER_EXEC, true);
2422        let content = Rc::new(RefCell::new(content));
2423        let state = Rc::clone(&system.state);
2424        state.borrow_mut().file_system.save(path, content).unwrap();
2425        let mut env = Env::with_system(system);
2426        let child = env.system.new_child_process().unwrap();
2427        let pid = child(
2428            &mut env,
2429            Box::new(move |child_env| {
2430                Box::pin(async move {
2431                    let path = CString::new(path).unwrap();
2432                    child_env.system.execve(&path, &[], &[]).await.ok();
2433                    child_env.system.exit(child_env.exit_status).await
2434                })
2435            }),
2436        );
2437        executor.run_until_stalled();
2438
2439        let result = env.system.setpgid(pid, pid);
2440        assert_eq!(result, Err(Errno::EACCES));
2441
2442        let pgid = state.borrow().processes[&pid].pgid();
2443        assert_eq!(pgid, Pid(1));
2444    }
2445
2446    #[test]
2447    fn setpgid_with_nonexisting_pgid() {
2448        let (system, mut executor) = virtual_system_with_executor();
2449        let state = Rc::clone(&system.state);
2450        let mut env = Env::with_system(system);
2451        let child_1 = env.system.new_child_process().unwrap();
2452        let pid_1 = child_1(&mut env, Box::new(|_env| Box::pin(pending())));
2453        // env.system.setpgid(pid_1, pid_1).unwrap();
2454        let child_2 = env.system.new_child_process().unwrap();
2455        let pid_2 = child_2(&mut env, Box::new(|_env| Box::pin(pending())));
2456        executor.run_until_stalled();
2457
2458        let result = env.system.setpgid(pid_2, pid_1);
2459        assert_eq!(result, Err(Errno::EPERM));
2460
2461        let pgid = state.borrow().processes[&pid_2].pgid();
2462        assert_eq!(pgid, Pid(1));
2463    }
2464
2465    #[test]
2466    fn tcsetpgrp_success() {
2467        let system = VirtualSystem::new();
2468        let pid = Pid(10);
2469        let ppid = system.process_id;
2470        let pgid = Pid(9);
2471        system
2472            .state
2473            .borrow_mut()
2474            .processes
2475            .insert(pid, Process::with_parent_and_group(ppid, pgid));
2476
2477        system
2478            .tcsetpgrp(Fd::STDIN, pgid)
2479            .now_or_never()
2480            .unwrap()
2481            .unwrap();
2482
2483        let foreground = system.state.borrow().foreground;
2484        assert_eq!(foreground, Some(pgid));
2485    }
2486
2487    #[test]
2488    fn tcsetpgrp_with_invalid_fd() {
2489        let system = VirtualSystem::new();
2490        let result = system.tcsetpgrp(Fd(100), Pid(2)).now_or_never().unwrap();
2491        assert_eq!(result, Err(Errno::EBADF));
2492    }
2493
2494    #[test]
2495    fn tcsetpgrp_with_nonexisting_pgrp() {
2496        let system = VirtualSystem::new();
2497        let result = system
2498            .tcsetpgrp(Fd::STDIN, Pid(100))
2499            .now_or_never()
2500            .unwrap();
2501        assert_eq!(result, Err(Errno::EPERM));
2502    }
2503
2504    #[test]
2505    fn new_child_process_without_executor() {
2506        let system = VirtualSystem::new();
2507        let result = system.new_child_process();
2508        match result {
2509            Ok(_) => panic!("unexpected Ok value"),
2510            Err(e) => assert_eq!(e, Errno::ENOSYS),
2511        }
2512    }
2513
2514    #[test]
2515    fn new_child_process_with_executor() {
2516        let (system, _executor) = virtual_system_with_executor();
2517
2518        let result = system.new_child_process();
2519
2520        let state = system.state.borrow();
2521        assert_eq!(state.processes.len(), 2);
2522        drop(state);
2523
2524        let mut env = Env::with_system(system);
2525        let child_process = result.unwrap();
2526        let pid = child_process(
2527            &mut env,
2528            Box::new(|env| Box::pin(async move { env.system.exit(env.exit_status).await })),
2529        );
2530        assert_eq!(pid, Pid(3));
2531    }
2532
2533    #[test]
2534    fn wait_for_running_child() {
2535        let (system, _executor) = virtual_system_with_executor();
2536
2537        let child_process = system.new_child_process();
2538
2539        let mut env = Env::with_system(system);
2540        let child_process = child_process.unwrap();
2541        let pid = child_process(
2542            &mut env,
2543            Box::new(|_env| {
2544                Box::pin(async {
2545                    unreachable!("child process does not progress unless executor is used")
2546                })
2547            }),
2548        );
2549
2550        let result = env.system.wait(pid);
2551        assert_eq!(result, Ok(None))
2552    }
2553
2554    #[test]
2555    fn wait_for_exited_child() {
2556        let (system, mut executor) = virtual_system_with_executor();
2557
2558        let child_process = system.new_child_process();
2559
2560        let mut env = Env::with_system(system);
2561        let child_process = child_process.unwrap();
2562        let pid = child_process(
2563            &mut env,
2564            Box::new(|env| Box::pin(async move { env.system.exit(ExitStatus(5)).await })),
2565        );
2566        executor.run_until_stalled();
2567
2568        let result = env.system.wait(pid);
2569        assert_eq!(result, Ok(Some((pid, ProcessState::exited(5)))));
2570    }
2571
2572    #[test]
2573    fn wait_for_signaled_child() {
2574        let (system, mut executor) = virtual_system_with_executor();
2575
2576        let child_process = system.new_child_process();
2577
2578        let mut env = Env::with_system(system);
2579        let child_process = child_process.unwrap();
2580        let pid = child_process(
2581            &mut env,
2582            Box::new(|env| {
2583                Box::pin(async move {
2584                    let pid = env.system.getpid();
2585                    let result = env.system.kill(pid, Some(SIGKILL)).await;
2586                    unreachable!("kill returned {result:?}");
2587                })
2588            }),
2589        );
2590        executor.run_until_stalled();
2591
2592        let result = env.system.wait(pid);
2593        assert_eq!(
2594            result,
2595            Ok(Some((
2596                pid,
2597                ProcessState::Halted(ProcessResult::Signaled {
2598                    signal: SIGKILL,
2599                    core_dump: false
2600                })
2601            )))
2602        );
2603    }
2604
2605    #[test]
2606    fn wait_for_stopped_child() {
2607        let (system, mut executor) = virtual_system_with_executor();
2608
2609        let child_process = system.new_child_process();
2610
2611        let mut env = Env::with_system(system);
2612        let child_process = child_process.unwrap();
2613        let pid = child_process(
2614            &mut env,
2615            Box::new(|env| {
2616                Box::pin(async move {
2617                    let pid = env.system.getpid();
2618                    let result = env.system.kill(pid, Some(SIGSTOP)).await;
2619                    unreachable!("kill returned {result:?}");
2620                })
2621            }),
2622        );
2623        executor.run_until_stalled();
2624
2625        let result = env.system.wait(pid);
2626        assert_eq!(result, Ok(Some((pid, ProcessState::stopped(SIGSTOP)))));
2627    }
2628
2629    #[test]
2630    fn wait_for_resumed_child() {
2631        let (system, mut executor) = virtual_system_with_executor();
2632
2633        let child_process = system.new_child_process();
2634
2635        let mut env = Env::with_system(system);
2636        let child_process = child_process.unwrap();
2637        let pid = child_process(
2638            &mut env,
2639            Box::new(|env| {
2640                Box::pin(async move {
2641                    let pid = env.system.getpid();
2642                    let result = env.system.kill(pid, Some(SIGSTOP)).await;
2643                    assert_eq!(result, Ok(()));
2644                    env.system.exit(ExitStatus(123)).await
2645                })
2646            }),
2647        );
2648        executor.run_until_stalled();
2649
2650        env.system
2651            .kill(pid, Some(SIGCONT))
2652            .now_or_never()
2653            .unwrap()
2654            .unwrap();
2655
2656        let result = env.system.wait(pid);
2657        assert_eq!(result, Ok(Some((pid, ProcessState::Running))));
2658
2659        executor.run_until_stalled();
2660
2661        let result = env.system.wait(pid);
2662        assert_eq!(result, Ok(Some((pid, ProcessState::exited(123)))));
2663    }
2664
2665    #[test]
2666    fn wait_without_child() {
2667        let system = VirtualSystem::new();
2668        let result = system.wait(Pid::ALL);
2669        assert_eq!(result, Err(Errno::ECHILD));
2670        // TODO
2671        // let result = system.wait(Pid::MY_PROCESS_GROUP);
2672        // assert_eq!(result, Err(Errno::ECHILD));
2673        let result = system.wait(system.process_id);
2674        assert_eq!(result, Err(Errno::ECHILD));
2675        let result = system.wait(Pid(1234));
2676        assert_eq!(result, Err(Errno::ECHILD));
2677        // TODO
2678        // let result = system.wait(Pid(-1234));
2679        // assert_eq!(result, Err(Errno::ECHILD));
2680    }
2681
2682    #[test]
2683    fn exiting_child_sends_sigchld_to_parent() {
2684        let (system, mut executor) = virtual_system_with_executor();
2685        system.sigaction(SIGCHLD, Disposition::Catch).unwrap();
2686
2687        let child_process = system.new_child_process().unwrap();
2688
2689        let mut env = Env::with_system(system);
2690        let _pid = child_process(
2691            &mut env,
2692            Box::new(|env| Box::pin(async { env.system.exit(ExitStatus(0)).await })),
2693        );
2694        executor.run_until_stalled();
2695
2696        assert_eq!(env.system.caught_signals(), [SIGCHLD]);
2697    }
2698
2699    #[test]
2700    fn execve_returns_enosys_for_executable_file() {
2701        let system = VirtualSystem::new();
2702        let path = "/some/file";
2703        let mut content = Inode::default();
2704        content.body = FileBody::Regular {
2705            content: vec![],
2706            is_native_executable: true,
2707        };
2708        content.permissions.set(Mode::USER_EXEC, true);
2709        let content = Rc::new(RefCell::new(content));
2710        let mut state = system.state.borrow_mut();
2711        state.file_system.save(path, content).unwrap();
2712        drop(state);
2713        let path = CString::new(path).unwrap();
2714        let result = system.execve(&path, &[], &[]).now_or_never().unwrap();
2715        assert_eq!(result, Err(Errno::ENOSYS));
2716    }
2717
2718    #[test]
2719    fn execve_saves_arguments() {
2720        let system = VirtualSystem::new();
2721        let path = "/some/file";
2722        let mut content = Inode::default();
2723        content.body = FileBody::Regular {
2724            content: vec![],
2725            is_native_executable: true,
2726        };
2727        content.permissions.set(Mode::USER_EXEC, true);
2728        let content = Rc::new(RefCell::new(content));
2729        let mut state = system.state.borrow_mut();
2730        state.file_system.save(path, content).unwrap();
2731        drop(state);
2732        let path = CString::new(path).unwrap();
2733        let args = [c"file".to_owned(), c"bar".to_owned()];
2734        let envs = [c"foo=FOO".to_owned(), c"baz".to_owned()];
2735        system.execve(&path, &args, &envs).now_or_never();
2736
2737        let process = system.current_process();
2738        let arguments = process.last_exec.as_ref().unwrap();
2739        assert_eq!(arguments.0, path);
2740        assert_eq!(arguments.1, args);
2741        assert_eq!(arguments.2, envs);
2742    }
2743
2744    #[test]
2745    fn execve_returns_enoexec_for_non_executable_file() {
2746        let system = VirtualSystem::new();
2747        let path = "/some/file";
2748        let mut content = Inode::default();
2749        content.permissions.set(Mode::USER_EXEC, true);
2750        let content = Rc::new(RefCell::new(content));
2751        let mut state = system.state.borrow_mut();
2752        state.file_system.save(path, content).unwrap();
2753        drop(state);
2754        let path = CString::new(path).unwrap();
2755        let result = system.execve(&path, &[], &[]).now_or_never().unwrap();
2756        assert_eq!(result, Err(Errno::ENOEXEC));
2757    }
2758
2759    #[test]
2760    fn execve_returns_enoent_on_file_not_found() {
2761        let system = VirtualSystem::new();
2762        let result = system
2763            .execve(c"/no/such/file", &[], &[])
2764            .now_or_never()
2765            .unwrap();
2766        assert_eq!(result, Err(Errno::ENOENT));
2767    }
2768
2769    #[test]
2770    fn exit_sets_current_process_state_to_exited() {
2771        let system = VirtualSystem::new();
2772        system.exit(ExitStatus(42)).now_or_never();
2773
2774        assert!(system.current_process().state_has_changed());
2775        assert_eq!(
2776            system.current_process().state(),
2777            ProcessState::exited(ExitStatus(42))
2778        );
2779    }
2780
2781    #[test]
2782    fn exit_sends_sigchld_to_parent() {
2783        let (system, mut executor) = virtual_system_with_executor();
2784        system.sigaction(SIGCHLD, Disposition::Catch).unwrap();
2785
2786        let child_process = system.new_child_process().unwrap();
2787
2788        let mut env = Env::with_system(system);
2789        let _pid = child_process(
2790            &mut env,
2791            Box::new(|env| Box::pin(async { env.system.exit(ExitStatus(123)).await })),
2792        );
2793        executor.run_until_stalled();
2794
2795        assert_eq!(env.system.caught_signals(), [SIGCHLD]);
2796    }
2797
2798    #[test]
2799    fn chdir_changes_directory() {
2800        let system = VirtualSystem::new();
2801
2802        // Create a regular file and its parent directory
2803        let _ = system.open(
2804            c"/dir/file",
2805            OfdAccess::WriteOnly,
2806            OpenFlag::Create.into(),
2807            Mode::empty(),
2808        );
2809
2810        let result = system.chdir(c"/dir");
2811        assert_eq!(result, Ok(()));
2812        assert_eq!(system.current_process().cwd, Path::new("/dir"));
2813    }
2814
2815    #[test]
2816    fn chdir_fails_with_non_existing_directory() {
2817        let system = VirtualSystem::new();
2818
2819        let result = system.chdir(c"/no/such/dir");
2820        assert_eq!(result, Err(Errno::ENOENT));
2821    }
2822
2823    #[test]
2824    fn chdir_fails_with_non_directory_file() {
2825        let system = VirtualSystem::new();
2826
2827        // Create a regular file and its parent directory
2828        let _ = system.open(
2829            c"/dir/file",
2830            OfdAccess::WriteOnly,
2831            OpenFlag::Create.into(),
2832            Mode::empty(),
2833        );
2834
2835        let result = system.chdir(c"/dir/file");
2836        assert_eq!(result, Err(Errno::ENOTDIR));
2837    }
2838
2839    #[test]
2840    fn getrlimit_for_unset_resource_returns_infinity() {
2841        let system = VirtualSystem::new();
2842        let result = system.getrlimit(Resource::CPU).unwrap();
2843        assert_eq!(
2844            result,
2845            LimitPair {
2846                soft: INFINITY,
2847                hard: INFINITY,
2848            },
2849        );
2850    }
2851
2852    #[test]
2853    fn setrlimit_and_getrlimit_with_finite_limits() {
2854        let system = VirtualSystem::new();
2855        system
2856            .setrlimit(
2857                Resource::CORE,
2858                LimitPair {
2859                    soft: 4096,
2860                    hard: 8192,
2861                },
2862            )
2863            .unwrap();
2864        system
2865            .setrlimit(Resource::CPU, LimitPair { soft: 10, hard: 30 })
2866            .unwrap();
2867
2868        let result = system.getrlimit(Resource::CORE).unwrap();
2869        assert_eq!(
2870            result,
2871            LimitPair {
2872                soft: 4096,
2873                hard: 8192,
2874            },
2875        );
2876        let result = system.getrlimit(Resource::CPU).unwrap();
2877        assert_eq!(result, LimitPair { soft: 10, hard: 30 },);
2878    }
2879
2880    #[test]
2881    fn setrlimit_rejects_soft_limit_higher_than_hard_limit() {
2882        let system = VirtualSystem::new();
2883        let result = system.setrlimit(Resource::CPU, LimitPair { soft: 2, hard: 1 });
2884        assert_eq!(result, Err(Errno::EINVAL));
2885
2886        // The limits should not have been changed
2887        let result = system.getrlimit(Resource::CPU).unwrap();
2888        assert_eq!(
2889            result,
2890            LimitPair {
2891                soft: INFINITY,
2892                hard: INFINITY,
2893            },
2894        );
2895    }
2896
2897    #[test]
2898    fn setrlimit_refuses_raising_hard_limit() {
2899        let system = VirtualSystem::new();
2900        system
2901            .setrlimit(Resource::CPU, LimitPair { soft: 1, hard: 1 })
2902            .unwrap();
2903        let result = system.setrlimit(Resource::CPU, LimitPair { soft: 1, hard: 2 });
2904        assert_eq!(result, Err(Errno::EPERM));
2905
2906        // The limits should not have been changed
2907        let result = system.getrlimit(Resource::CPU).unwrap();
2908        assert_eq!(result, LimitPair { soft: 1, hard: 1 });
2909    }
2910}