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