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