1use self::alias::AliasSet;
36use self::any::DataSet;
37use self::builtin::Builtin;
38use self::function::FunctionSet;
39use self::io::Fd;
40use self::job::JobList;
41use self::job::Pid;
42use self::job::ProcessResult;
43use self::job::ProcessState;
44use self::option::On;
45use self::option::OptionSet;
46use self::option::{AllExport, ErrExit, Interactive, Monitor};
47use self::semantics::Divert;
48use self::semantics::ExitStatus;
49use self::stack::Frame;
50use self::stack::Stack;
51use self::system::CaughtSignals;
52use self::system::Clock;
53use self::system::Close;
54use self::system::Dup;
55use self::system::Errno;
56use self::system::Fstat;
57use self::system::GetCwd;
58use self::system::GetPid;
59use self::system::Isatty;
60use self::system::Mode;
61use self::system::OfdAccess;
62use self::system::Open;
63use self::system::OpenFlag;
64use self::system::Select;
65pub use self::system::SharedSystem;
66use self::system::Sigaction;
67use self::system::Sigmask;
68use self::system::Signals;
69#[allow(deprecated)]
70pub use self::system::System;
71use self::system::TcSetPgrp;
72use self::system::Wait;
73#[cfg(unix)]
74pub use self::system::real::RealSystem;
75pub use self::system::r#virtual::VirtualSystem;
76use self::trap::TrapSet;
77use self::variable::PPID;
78use self::variable::Scope;
79use self::variable::VariableRefMut;
80use self::variable::VariableSet;
81use std::collections::HashMap;
82use std::fmt::Debug;
83use std::ops::ControlFlow::{self, Break, Continue};
84use std::rc::Rc;
85use std::task::Context;
86use std::task::Poll;
87use std::task::Waker;
88pub use unix_path as path;
89pub use unix_str as str;
90
91#[derive(Clone, Debug)]
105#[non_exhaustive]
106pub struct Env<S> {
107 pub aliases: AliasSet,
109
110 pub arg0: String,
114
115 pub builtins: HashMap<&'static str, Builtin<S>>,
117
118 pub exit_status: ExitStatus,
120
121 pub functions: FunctionSet<S>,
123
124 pub jobs: JobList,
126
127 pub main_pgid: Pid,
129
130 pub main_pid: Pid,
134
135 pub options: OptionSet,
137
138 pub stack: Stack,
140
141 pub traps: TrapSet,
143
144 pub tty: Option<Fd>,
149
150 pub variables: VariableSet,
152
153 pub any: DataSet,
155
156 pub system: SharedSystem<S>,
158}
159
160impl<S> Env<S> {
161 #[must_use]
167 pub fn with_system(system: S) -> Self
168 where
169 S: GetPid,
170 {
171 Env {
172 aliases: Default::default(),
173 arg0: Default::default(),
174 builtins: Default::default(),
175 exit_status: Default::default(),
176 functions: Default::default(),
177 jobs: Default::default(),
178 main_pgid: system.getpgrp(),
179 main_pid: system.getpid(),
180 options: Default::default(),
181 stack: Default::default(),
182 traps: Default::default(),
183 tty: Default::default(),
184 variables: Default::default(),
185 any: Default::default(),
186 system: SharedSystem::new(system),
187 }
188 }
189
190 #[must_use]
196 pub fn clone_with_system(&self, system: S) -> Self {
197 Env {
198 aliases: self.aliases.clone(),
199 arg0: self.arg0.clone(),
200 builtins: self.builtins.clone(),
201 exit_status: self.exit_status,
202 functions: self.functions.clone(),
203 jobs: self.jobs.clone(),
204 main_pgid: self.main_pgid,
205 main_pid: self.main_pid,
206 options: self.options,
207 stack: self.stack.clone(),
208 traps: self.traps.clone(),
209 tty: self.tty,
210 variables: self.variables.clone(),
211 any: self.any.clone(),
212 system: SharedSystem::new(system),
213 }
214 }
215}
216
217impl Env<VirtualSystem> {
218 #[must_use]
220 pub fn new_virtual() -> Self {
221 Env::with_system(VirtualSystem::default())
222 }
223}
224
225impl<S> Env<S> {
226 pub fn init_variables(&mut self)
242 where
243 S: Fstat + GetCwd + GetPid,
244 {
245 self.variables.init();
246
247 self.variables
248 .get_or_new(PPID, Scope::Global)
249 .assign(self.system.getppid().to_string(), None)
250 .ok();
251
252 self.prepare_pwd().ok();
253 }
254
255 pub async fn wait_for_signals(&mut self) -> Rc<[signal::Number]> {
264 let result = self.system.wait_for_signals().await;
265 for signal in result.iter().copied() {
266 self.traps.catch_signal(signal);
267 }
268 result
269 }
270
271 pub async fn wait_for_signal(&mut self, signal: signal::Number) {
276 while !self.wait_for_signals().await.contains(&signal) {}
277 }
278
279 pub fn poll_signals(&mut self) -> Option<Rc<[signal::Number]>>
286 where
287 S: Select + CaughtSignals + Clock,
288 {
289 let system = self.system.clone();
290
291 let mut future = std::pin::pin!(self.wait_for_signals());
292
293 let mut context = Context::from_waker(Waker::noop());
294 if let Poll::Ready(signals) = future.as_mut().poll(&mut context) {
295 return Some(signals);
296 }
297
298 system.select(true).ok();
299 if let Poll::Ready(signals) = future.poll(&mut context) {
300 return Some(signals);
301 }
302 None
303 }
304
305 #[must_use]
315 fn should_print_error_in_color(&self) -> bool
316 where
317 S: Isatty,
318 {
319 self.system.isatty(Fd::STDERR)
322 }
323
324 pub fn get_tty(&mut self) -> Result<Fd, Errno>
329 where
330 S: Open + Dup + Close,
331 {
332 if let Some(fd) = self.tty {
333 return Ok(fd);
334 }
335
336 let first_fd = {
337 let mut result = self.system.open(
342 c"/dev/tty",
343 OfdAccess::ReadWrite,
344 OpenFlag::CloseOnExec | OpenFlag::NoCtty,
345 Mode::empty(),
346 );
347 if result == Err(Errno::EINVAL) {
348 result = self.system.open(
351 c"/dev/tty",
352 OfdAccess::ReadWrite,
353 OpenFlag::CloseOnExec.into(),
354 Mode::empty(),
355 );
356 }
357 result?
358 };
359
360 let final_fd = io::move_fd_internal(&self.system, first_fd);
361 self.tty = final_fd.ok();
362 final_fd
363 }
364
365 pub async fn ensure_foreground(&mut self) -> Result<(), Errno>
386 where
387 S: Open + Dup + Close + GetPid + Signals + Sigmask + Sigaction + TcSetPgrp,
388 {
389 let fd = self.get_tty()?;
390
391 if self.system.getsid(Pid(0)) == Ok(self.main_pgid) {
392 job::tcsetpgrp_with_block(&self.system, fd, self.main_pgid).await
393 } else {
394 job::tcsetpgrp_without_block(&self.system, fd, self.main_pgid).await
395 }
396 }
397
398 pub async fn wait_for_subshell(&mut self, target: Pid) -> Result<(Pid, ProcessState), Errno>
419 where
420 S: Signals + Sigmask + Sigaction + Wait,
421 {
422 self.traps
425 .enable_internal_disposition_for_sigchld(&mut self.system)?;
426
427 loop {
428 if let Some((pid, state)) = self.system.wait(target)? {
429 self.jobs.update_status(pid, state);
430 return Ok((pid, state));
431 }
432 self.wait_for_signal(S::SIGCHLD).await;
433 }
434 }
435
436 pub async fn wait_for_subshell_to_halt(
443 &mut self,
444 target: Pid,
445 ) -> Result<(Pid, ProcessResult), Errno>
446 where
447 S: Signals + Sigmask + Sigaction + Wait,
448 {
449 loop {
450 let (pid, state) = self.wait_for_subshell(target).await?;
451 if let ProcessState::Halted(result) = state {
452 return Ok((pid, result));
453 }
454 }
455 }
456
457 pub async fn wait_for_subshell_to_finish(
465 &mut self,
466 target: Pid,
467 ) -> Result<(Pid, ExitStatus), Errno>
468 where
469 S: Signals + Sigmask + Sigaction + Wait,
470 {
471 loop {
472 let (pid, result) = self.wait_for_subshell_to_halt(target).await?;
473 if !result.is_stopped() {
474 return Ok((pid, result.into()));
475 }
476 }
477 }
478
479 pub fn update_all_subshell_statuses(&mut self)
488 where
489 S: Wait,
490 {
491 while let Ok(Some((pid, state))) = self.system.wait(Pid::ALL) {
492 self.jobs.update_status(pid, state);
493 }
494 }
495}
496
497impl<S> Env<S> {
498 #[must_use]
505 pub fn is_interactive(&self) -> bool {
506 self.options.get(Interactive) == On && !self.stack.contains(&Frame::Subshell)
507 }
508
509 #[must_use]
516 pub fn controls_jobs(&self) -> bool {
517 self.options.get(Monitor) == On && !self.stack.contains(&Frame::Subshell)
518 }
519
520 pub fn get_or_create_variable<N>(&mut self, name: N, scope: Scope) -> VariableRefMut<'_>
530 where
531 N: Into<String>,
532 {
533 let mut variable = self.variables.get_or_new(name, scope);
534 if self.options.get(AllExport) == On {
535 variable.export(true);
536 }
537 variable
538 }
539
540 pub fn errexit_is_applicable(&self) -> bool {
548 self.options.get(ErrExit) == On && !self.stack.contains(&Frame::Condition)
549 }
550
551 pub fn apply_errexit(&self) -> ControlFlow<Divert> {
558 if !self.exit_status.is_successful() && self.errexit_is_applicable() {
559 Break(Divert::Exit(None))
560 } else {
561 Continue(())
562 }
563 }
564
565 pub fn apply_result(&mut self, result: crate::semantics::Result) {
570 match result {
571 Continue(_) => {}
572 Break(divert) => {
573 if let Some(exit_status) = divert.exit_status() {
574 self.exit_status = exit_status;
575 }
576 }
577 }
578 }
579}
580
581pub mod alias;
582pub mod any;
583pub mod builtin;
584pub mod decl_util;
585pub mod function;
586pub mod input;
587pub mod io;
588pub mod job;
589pub mod option;
590pub mod parser;
591pub mod prompt;
592pub mod pwd;
593pub mod semantics;
594pub mod signal;
595pub mod source;
596pub mod stack;
597pub mod subshell;
598pub mod system;
599pub mod trap;
600pub mod variable;
601
602#[cfg(test)]
603mod tests {
604 use super::*;
605 use crate::io::MIN_INTERNAL_FD;
606 use crate::job::Job;
607 use crate::source::Location;
608 use crate::subshell::Subshell;
609 use crate::system::r#virtual::FileBody;
610 use crate::system::r#virtual::Inode;
611 use crate::system::r#virtual::SIGCHLD;
612 use crate::system::r#virtual::SystemState;
613 use crate::trap::Action;
614 use assert_matches::assert_matches;
615 use futures_executor::LocalPool;
616 use futures_util::FutureExt as _;
617 use futures_util::task::LocalSpawnExt as _;
618 use std::cell::RefCell;
619 use std::str::from_utf8;
620
621 pub fn in_virtual_system<F, Fut, T>(f: F) -> T
623 where
624 F: FnOnce(Env<VirtualSystem>, Rc<RefCell<SystemState>>) -> Fut,
625 Fut: Future<Output = T> + 'static,
626 T: 'static,
627 {
628 let system = VirtualSystem::new();
629 let state = Rc::clone(&system.state);
630 let mut executor = futures_executor::LocalPool::new();
631 state.borrow_mut().executor = Some(Rc::new(executor.spawner()));
632
633 let env = Env::with_system(system);
634 let shared_system = env.system.clone();
635 let task = f(env, Rc::clone(&state));
636 let mut task = executor.spawner().spawn_local_with_handle(task).unwrap();
637 loop {
638 if let Some(result) = (&mut task).now_or_never() {
639 return result;
640 }
641 executor.run_until_stalled();
642 shared_system.select(false).unwrap();
643 SystemState::select_all(&state);
644 }
645 }
646
647 pub fn assert_stderr<F, T>(state: &RefCell<SystemState>, f: F) -> T
649 where
650 F: FnOnce(&str) -> T,
651 {
652 let stderr = state.borrow().file_system.get("/dev/stderr").unwrap();
653 let stderr = stderr.borrow();
654 assert_matches!(&stderr.body, FileBody::Regular { content, .. } => {
655 f(from_utf8(content).unwrap())
656 })
657 }
658
659 #[test]
660 fn wait_for_signal_remembers_signal_in_trap_set() {
661 in_virtual_system(|mut env, state| async move {
662 env.traps
663 .set_action(
664 &mut env.system,
665 SIGCHLD,
666 Action::Command("".into()),
667 Location::dummy(""),
668 false,
669 )
670 .unwrap();
671 {
672 let mut state = state.borrow_mut();
673 let process = state.processes.get_mut(&env.main_pid).unwrap();
674 assert!(process.blocked_signals().contains(&SIGCHLD));
675 let _ = process.raise_signal(SIGCHLD);
676 }
677 env.wait_for_signal(SIGCHLD).await;
678
679 let trap_state = env.traps.get_state(SIGCHLD).0.unwrap();
680 assert!(trap_state.pending);
681 })
682 }
683
684 fn poll_signals_env() -> (Env<VirtualSystem>, VirtualSystem) {
685 let system = VirtualSystem::new();
686 let mut env = Env::with_system(system.clone());
687 env.traps
688 .set_action(
689 &mut env.system,
690 SIGCHLD,
691 Action::Command("".into()),
692 Location::dummy(""),
693 false,
694 )
695 .unwrap();
696 (env, system)
697 }
698
699 #[test]
700 fn poll_signals_none() {
701 let mut env = poll_signals_env().0;
702 let result = env.poll_signals();
703 assert_eq!(result, None);
704 }
705
706 #[test]
707 fn poll_signals_some() {
708 let (mut env, system) = poll_signals_env();
709 {
710 let mut state = system.state.borrow_mut();
711 let process = state.processes.get_mut(&system.process_id).unwrap();
712 assert!(process.blocked_signals().contains(&SIGCHLD));
713 let _ = process.raise_signal(SIGCHLD);
714 }
715
716 let result = env.poll_signals().unwrap();
717 assert_eq!(*result, [SIGCHLD]);
718 }
719
720 #[test]
721 fn get_tty_opens_tty() {
722 let system = VirtualSystem::new();
723 let tty = Rc::new(RefCell::new(Inode::new([])));
724 system
725 .state
726 .borrow_mut()
727 .file_system
728 .save("/dev/tty", Rc::clone(&tty))
729 .unwrap();
730 let mut env = Env::with_system(system.clone());
731
732 let fd = env.get_tty().unwrap();
733 assert!(
734 fd >= MIN_INTERNAL_FD,
735 "get_tty returned {fd}, which should be >= {MIN_INTERNAL_FD}"
736 );
737 system
738 .with_open_file_description(fd, |ofd| {
739 assert!(Rc::ptr_eq(ofd.inode(), &tty));
740 Ok(())
741 })
742 .unwrap();
743
744 system.state.borrow_mut().file_system = Default::default();
745
746 let fd = env.get_tty().unwrap();
748 system
749 .with_open_file_description(fd, |ofd| {
750 assert!(Rc::ptr_eq(ofd.inode(), &tty));
751 Ok(())
752 })
753 .unwrap();
754 }
755
756 #[test]
757 fn start_and_wait_for_subshell() {
758 in_virtual_system(|mut env, _state| async move {
759 let subshell = Subshell::new(|env, _job_control| {
760 Box::pin(async { env.exit_status = ExitStatus(42) })
761 });
762 let (pid, _) = subshell.start(&mut env).await.unwrap();
763 let result = env.wait_for_subshell(pid).await;
764 assert_eq!(result, Ok((pid, ProcessState::exited(42))));
765 });
766 }
767
768 #[test]
769 fn start_and_wait_for_subshell_with_job_list() {
770 in_virtual_system(|mut env, _state| async move {
771 let subshell = Subshell::new(|env, _job_control| {
772 Box::pin(async { env.exit_status = ExitStatus(42) })
773 });
774 let (pid, _) = subshell.start(&mut env).await.unwrap();
775 let mut job = Job::new(pid);
776 job.name = "my job".to_string();
777 let job_index = env.jobs.add(job.clone());
778 let result = env.wait_for_subshell(pid).await;
779 assert_eq!(result, Ok((pid, ProcessState::exited(42))));
780 job.state = ProcessState::exited(42);
781 assert_eq!(env.jobs[job_index], job);
782 });
783 }
784
785 #[test]
786 fn wait_for_subshell_no_subshell() {
787 let system = VirtualSystem::new();
788 let mut executor = LocalPool::new();
789 system.state.borrow_mut().executor = Some(Rc::new(executor.spawner()));
790 let mut env = Env::with_system(system);
791 executor.run_until(async move {
792 let result = env.wait_for_subshell(Pid::ALL).await;
793 assert_eq!(result, Err(Errno::ECHILD));
794 });
795 }
796
797 #[test]
798 fn update_all_subshell_statuses_without_subshells() {
799 let mut env = Env::new_virtual();
800 env.update_all_subshell_statuses();
801 }
802
803 #[test]
804 fn update_all_subshell_statuses_with_subshells() {
805 let system = VirtualSystem::new();
806 let mut executor = futures_executor::LocalPool::new();
807 system.state.borrow_mut().executor = Some(Rc::new(executor.spawner()));
808
809 let mut env = Env::with_system(system);
810
811 let [job_1, job_2, job_3] = executor.run_until(async {
812 let subshell_1 = Subshell::new(|env, _job_control| {
814 Box::pin(async { env.exit_status = ExitStatus(12) })
815 });
816 let (pid_1, _) = subshell_1.start(&mut env).await.unwrap();
817
818 let subshell_2 = Subshell::new(|env, _job_control| {
820 Box::pin(async { env.exit_status = ExitStatus(35) })
821 });
822 let (pid_2, _) = subshell_2.start(&mut env).await.unwrap();
823
824 let subshell_3 =
826 Subshell::new(|_env, _job_control| Box::pin(futures_util::future::pending()));
827 let (pid_3, _) = subshell_3.start(&mut env).await.unwrap();
828
829 let subshell_4 = Subshell::new(|env, _job_control| {
831 Box::pin(async { env.exit_status = ExitStatus(100) })
832 });
833 let (_pid_4, _) = subshell_4.start(&mut env).await.unwrap();
834
835 let job_1 = env.jobs.add(Job::new(pid_1));
837 let job_2 = env.jobs.add(Job::new(pid_2));
838 let job_3 = env.jobs.add(Job::new(pid_3));
839 [job_1, job_2, job_3]
840 });
841
842 executor.run_until_stalled();
844
845 assert_eq!(env.jobs[job_1].state, ProcessState::Running);
847 assert_eq!(env.jobs[job_2].state, ProcessState::Running);
848 assert_eq!(env.jobs[job_3].state, ProcessState::Running);
849
850 env.update_all_subshell_statuses();
851
852 assert_eq!(env.jobs[job_3].state, ProcessState::Running);
856 }
857
858 #[test]
859 fn get_or_create_variable_with_all_export_off() {
860 let mut env = Env::new_virtual();
861 let mut a = env.get_or_create_variable("a", Scope::Global);
862 assert!(!a.is_exported);
863 a.export(true);
864 let a = env.get_or_create_variable("a", Scope::Global);
865 assert!(a.is_exported);
866 }
867
868 #[test]
869 fn get_or_create_variable_with_all_export_on() {
870 let mut env = Env::new_virtual();
871 env.options.set(AllExport, On);
872 let mut a = env.get_or_create_variable("a", Scope::Global);
873 assert!(a.is_exported);
874 a.export(false);
875 let a = env.get_or_create_variable("a", Scope::Global);
876 assert!(a.is_exported);
877 }
878
879 #[test]
880 fn errexit_on() {
881 let mut env = Env::new_virtual();
882 env.exit_status = ExitStatus::FAILURE;
883 env.options.set(ErrExit, On);
884 assert_eq!(env.apply_errexit(), Break(Divert::Exit(None)));
885 }
886
887 #[test]
888 fn errexit_with_zero_exit_status() {
889 let mut env = Env::new_virtual();
890 env.options.set(ErrExit, On);
891 assert_eq!(env.apply_errexit(), Continue(()));
892 }
893
894 #[test]
895 fn errexit_in_condition() {
896 let mut env = Env::new_virtual();
897 env.exit_status = ExitStatus::FAILURE;
898 env.options.set(ErrExit, On);
899 let env = env.push_frame(Frame::Condition);
900 assert_eq!(env.apply_errexit(), Continue(()));
901 }
902
903 #[test]
904 fn errexit_off() {
905 let mut env = Env::new_virtual();
906 env.exit_status = ExitStatus::FAILURE;
907 assert_eq!(env.apply_errexit(), Continue(()));
908 }
909
910 #[test]
911 fn apply_result_with_continue() {
912 let mut env = Env::new_virtual();
913 env.apply_result(Continue(()));
914 assert_eq!(env.exit_status, ExitStatus::default());
915 }
916
917 #[test]
918 fn apply_result_with_divert_without_exit_status() {
919 let mut env = Env::new_virtual();
920 env.apply_result(Break(Divert::Exit(None)));
921 assert_eq!(env.exit_status, ExitStatus::default());
922 }
923
924 #[test]
925 fn apply_result_with_divert_with_exit_status() {
926 let mut env = Env::new_virtual();
927 env.apply_result(Break(Divert::Exit(Some(ExitStatus(67)))));
928 assert_eq!(env.exit_status, ExitStatus(67));
929 }
930}