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