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