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