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