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