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