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