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::Errno;
51use self::system::Mode;
52use self::system::OfdAccess;
53use self::system::OpenFlag;
54pub use self::system::SharedSystem;
55pub use self::system::System;
56use self::system::SystemEx;
57#[cfg(unix)]
58pub use self::system::real::RealSystem;
59pub use self::system::r#virtual::VirtualSystem;
60use self::trap::TrapSet;
61use self::variable::PPID;
62use self::variable::Scope;
63use self::variable::VariableRefMut;
64use self::variable::VariableSet;
65use std::collections::HashMap;
66use std::fmt::Debug;
67use std::ops::ControlFlow::{self, Break, Continue};
68use std::rc::Rc;
69use std::task::Context;
70use std::task::Poll;
71use std::task::Waker;
72pub use unix_path as path;
73pub use unix_str as str;
74
75#[derive(Clone, Debug)]
89#[non_exhaustive]
90pub struct Env {
91 pub aliases: AliasSet,
93
94 pub arg0: String,
98
99 pub builtins: HashMap<&'static str, Builtin>,
101
102 pub exit_status: ExitStatus,
104
105 pub functions: FunctionSet,
107
108 pub jobs: JobList,
110
111 pub main_pgid: Pid,
113
114 pub main_pid: Pid,
118
119 pub options: OptionSet,
121
122 pub stack: Stack,
124
125 pub traps: TrapSet,
127
128 pub tty: Option<Fd>,
133
134 pub variables: VariableSet,
136
137 pub any: DataSet,
139
140 pub system: SharedSystem,
142}
143
144impl Env {
145 #[must_use]
151 pub fn with_system(system: Box<dyn System>) -> Env {
152 Env {
153 aliases: Default::default(),
154 arg0: Default::default(),
155 builtins: Default::default(),
156 exit_status: Default::default(),
157 functions: Default::default(),
158 jobs: Default::default(),
159 main_pgid: system.getpgrp(),
160 main_pid: system.getpid(),
161 options: Default::default(),
162 stack: Default::default(),
163 traps: Default::default(),
164 tty: Default::default(),
165 variables: Default::default(),
166 any: Default::default(),
167 system: SharedSystem::new(system),
168 }
169 }
170
171 #[must_use]
173 pub fn new_virtual() -> Env {
174 Env::with_system(Box::<VirtualSystem>::default())
175 }
176
177 #[must_use]
183 pub fn clone_with_system(&self, system: Box<dyn System>) -> Env {
184 Env {
185 aliases: self.aliases.clone(),
186 arg0: self.arg0.clone(),
187 builtins: self.builtins.clone(),
188 exit_status: self.exit_status,
189 functions: self.functions.clone(),
190 jobs: self.jobs.clone(),
191 main_pgid: self.main_pgid,
192 main_pid: self.main_pid,
193 options: self.options,
194 stack: self.stack.clone(),
195 traps: self.traps.clone(),
196 tty: self.tty,
197 variables: self.variables.clone(),
198 any: self.any.clone(),
199 system: SharedSystem::new(system),
200 }
201 }
202
203 pub fn init_variables(&mut self) {
219 self.variables.init();
220
221 self.variables
222 .get_or_new(PPID, Scope::Global)
223 .assign(self.system.getppid().to_string(), None)
224 .ok();
225
226 self.prepare_pwd().ok();
227 }
228
229 pub async fn wait_for_signals(&mut self) -> Rc<[signal::Number]> {
238 let result = self.system.wait_for_signals().await;
239 for signal in result.iter().copied() {
240 self.traps.catch_signal(signal);
241 }
242 result
243 }
244
245 pub async fn wait_for_signal(&mut self, signal: signal::Number) {
250 while !self.wait_for_signals().await.contains(&signal) {}
251 }
252
253 pub fn poll_signals(&mut self) -> Option<Rc<[signal::Number]>> {
260 let system = self.system.clone();
261
262 let mut future = std::pin::pin!(self.wait_for_signals());
263
264 let mut context = Context::from_waker(Waker::noop());
265 if let Poll::Ready(signals) = future.as_mut().poll(&mut context) {
266 return Some(signals);
267 }
268
269 system.select(true).ok();
270 if let Poll::Ready(signals) = future.poll(&mut context) {
271 return Some(signals);
272 }
273 None
274 }
275
276 #[must_use]
286 fn should_print_error_in_color(&self) -> bool {
287 self.system.isatty(Fd::STDERR)
290 }
291
292 pub fn get_tty(&mut self) -> Result<Fd, Errno> {
297 if let Some(fd) = self.tty {
298 return Ok(fd);
299 }
300
301 let first_fd = {
302 let mut result = self.system.open(
307 c"/dev/tty",
308 OfdAccess::ReadWrite,
309 OpenFlag::CloseOnExec | OpenFlag::NoCtty,
310 Mode::empty(),
311 );
312 if result == Err(Errno::EINVAL) {
313 result = self.system.open(
316 c"/dev/tty",
317 OfdAccess::ReadWrite,
318 OpenFlag::CloseOnExec.into(),
319 Mode::empty(),
320 );
321 }
322 result?
323 };
324
325 let final_fd = self.system.move_fd_internal(first_fd);
326 self.tty = final_fd.ok();
327 final_fd
328 }
329
330 pub fn ensure_foreground(&mut self) -> Result<(), Errno> {
351 let fd = self.get_tty()?;
352
353 if self.system.getsid(Pid(0)) == Ok(self.main_pgid) {
354 self.system.tcsetpgrp_with_block(fd, self.main_pgid)
355 } else {
356 self.system.tcsetpgrp_without_block(fd, self.main_pgid)
357 }
358 }
359
360 #[must_use]
367 pub fn is_interactive(&self) -> bool {
368 self.options.get(Interactive) == On && !self.stack.contains(&Frame::Subshell)
369 }
370
371 #[must_use]
378 pub fn controls_jobs(&self) -> bool {
379 self.options.get(Monitor) == On && !self.stack.contains(&Frame::Subshell)
380 }
381
382 pub async fn wait_for_subshell(&mut self, target: Pid) -> Result<(Pid, ProcessState), Errno> {
403 self.traps
406 .enable_internal_disposition_for_sigchld(&mut self.system)?;
407
408 let sigchld = self
409 .system
410 .signal_number_from_name(signal::Name::Chld)
411 .unwrap();
412 loop {
413 if let Some((pid, state)) = self.system.wait(target)? {
414 self.jobs.update_status(pid, state);
415 return Ok((pid, state));
416 }
417 self.wait_for_signal(sigchld).await;
418 }
419 }
420
421 pub async fn wait_for_subshell_to_halt(
428 &mut self,
429 target: Pid,
430 ) -> Result<(Pid, ProcessResult), Errno> {
431 loop {
432 let (pid, state) = self.wait_for_subshell(target).await?;
433 if let ProcessState::Halted(result) = state {
434 return Ok((pid, result));
435 }
436 }
437 }
438
439 pub async fn wait_for_subshell_to_finish(
447 &mut self,
448 target: Pid,
449 ) -> Result<(Pid, ExitStatus), Errno> {
450 loop {
451 let (pid, result) = self.wait_for_subshell_to_halt(target).await?;
452 if !result.is_stopped() {
453 return Ok((pid, result.into()));
454 }
455 }
456 }
457
458 pub fn update_all_subshell_statuses(&mut self) {
467 while let Ok(Some((pid, state))) = self.system.wait(Pid::ALL) {
468 self.jobs.update_status(pid, state);
469 }
470 }
471
472 pub fn get_or_create_variable<S>(&mut self, name: S, scope: Scope) -> VariableRefMut<'_>
482 where
483 S: Into<String>,
484 {
485 let mut variable = self.variables.get_or_new(name, scope);
486 if self.options.get(AllExport) == On {
487 variable.export(true);
488 }
489 variable
490 }
491
492 pub fn errexit_is_applicable(&self) -> bool {
500 self.options.get(ErrExit) == On && !self.stack.contains(&Frame::Condition)
501 }
502
503 pub fn apply_errexit(&self) -> ControlFlow<Divert> {
510 if !self.exit_status.is_successful() && self.errexit_is_applicable() {
511 Break(Divert::Exit(None))
512 } else {
513 Continue(())
514 }
515 }
516
517 pub fn apply_result(&mut self, result: crate::semantics::Result) {
522 match result {
523 Continue(_) => {}
524 Break(divert) => {
525 if let Some(exit_status) = divert.exit_status() {
526 self.exit_status = exit_status;
527 }
528 }
529 }
530 }
531}
532
533pub mod alias;
534pub mod any;
535pub mod builtin;
536pub mod decl_util;
537pub mod function;
538pub mod input;
539pub mod io;
540pub mod job;
541pub mod option;
542pub mod parser;
543pub mod prompt;
544pub mod pwd;
545pub mod semantics;
546pub mod signal;
547pub mod source;
548pub mod stack;
549pub mod subshell;
550pub mod system;
551pub mod trap;
552pub mod variable;
553
554#[cfg(test)]
555mod tests {
556 use super::*;
557 use crate::io::MIN_INTERNAL_FD;
558 use crate::job::Job;
559 use crate::source::Location;
560 use crate::subshell::Subshell;
561 use crate::system::r#virtual::FileBody;
562 use crate::system::r#virtual::Inode;
563 use crate::system::r#virtual::SIGCHLD;
564 use crate::system::r#virtual::SystemState;
565 use crate::trap::Action;
566 use assert_matches::assert_matches;
567 use futures_executor::LocalPool;
568 use futures_util::FutureExt as _;
569 use futures_util::task::LocalSpawnExt as _;
570 use std::cell::RefCell;
571 use std::str::from_utf8;
572
573 pub fn in_virtual_system<F, Fut, T>(f: F) -> T
575 where
576 F: FnOnce(Env, Rc<RefCell<SystemState>>) -> Fut,
577 Fut: Future<Output = T> + 'static,
578 T: 'static,
579 {
580 let system = VirtualSystem::new();
581 let state = Rc::clone(&system.state);
582 let mut executor = futures_executor::LocalPool::new();
583 state.borrow_mut().executor = Some(Rc::new(executor.spawner()));
584
585 let env = Env::with_system(Box::new(system));
586 let shared_system = env.system.clone();
587 let task = f(env, Rc::clone(&state));
588 let mut task = executor.spawner().spawn_local_with_handle(task).unwrap();
589 loop {
590 if let Some(result) = (&mut task).now_or_never() {
591 return result;
592 }
593 executor.run_until_stalled();
594 shared_system.select(false).unwrap();
595 SystemState::select_all(&state);
596 }
597 }
598
599 pub fn assert_stderr<F, T>(state: &RefCell<SystemState>, f: F) -> T
601 where
602 F: FnOnce(&str) -> T,
603 {
604 let stderr = state.borrow().file_system.get("/dev/stderr").unwrap();
605 let stderr = stderr.borrow();
606 assert_matches!(&stderr.body, FileBody::Regular { content, .. } => {
607 f(from_utf8(content).unwrap())
608 })
609 }
610
611 #[test]
612 fn wait_for_signal_remembers_signal_in_trap_set() {
613 in_virtual_system(|mut env, state| async move {
614 env.traps
615 .set_action(
616 &mut env.system,
617 SIGCHLD,
618 Action::Command("".into()),
619 Location::dummy(""),
620 false,
621 )
622 .unwrap();
623 {
624 let mut state = state.borrow_mut();
625 let process = state.processes.get_mut(&env.main_pid).unwrap();
626 assert!(process.blocked_signals().contains(&SIGCHLD));
627 let _ = process.raise_signal(SIGCHLD);
628 }
629 env.wait_for_signal(SIGCHLD).await;
630
631 let trap_state = env.traps.get_state(SIGCHLD).0.unwrap();
632 assert!(trap_state.pending);
633 })
634 }
635
636 fn poll_signals_env() -> (Env, VirtualSystem) {
637 let system = VirtualSystem::new();
638 let shared_system = SharedSystem::new(Box::new(system.clone()));
639 let mut env = Env::with_system(Box::new(shared_system));
640 env.traps
641 .set_action(
642 &mut env.system,
643 SIGCHLD,
644 Action::Command("".into()),
645 Location::dummy(""),
646 false,
647 )
648 .unwrap();
649 (env, system)
650 }
651
652 #[test]
653 fn poll_signals_none() {
654 let mut env = poll_signals_env().0;
655 let result = env.poll_signals();
656 assert_eq!(result, None);
657 }
658
659 #[test]
660 fn poll_signals_some() {
661 let (mut env, system) = poll_signals_env();
662 {
663 let mut state = system.state.borrow_mut();
664 let process = state.processes.get_mut(&system.process_id).unwrap();
665 assert!(process.blocked_signals().contains(&SIGCHLD));
666 let _ = process.raise_signal(SIGCHLD);
667 }
668
669 let result = env.poll_signals().unwrap();
670 assert_eq!(*result, [SIGCHLD]);
671 }
672
673 #[test]
674 fn get_tty_opens_tty() {
675 let system = VirtualSystem::new();
676 let tty = Rc::new(RefCell::new(Inode::new([])));
677 system
678 .state
679 .borrow_mut()
680 .file_system
681 .save("/dev/tty", Rc::clone(&tty))
682 .unwrap();
683 let mut env = Env::with_system(Box::new(system.clone()));
684
685 let fd = env.get_tty().unwrap();
686 assert!(
687 fd >= MIN_INTERNAL_FD,
688 "get_tty returned {fd}, which should be >= {MIN_INTERNAL_FD}"
689 );
690 system
691 .with_open_file_description(fd, |ofd| {
692 assert!(Rc::ptr_eq(ofd.inode(), &tty));
693 Ok(())
694 })
695 .unwrap();
696
697 system.state.borrow_mut().file_system = Default::default();
698
699 let fd = env.get_tty().unwrap();
701 system
702 .with_open_file_description(fd, |ofd| {
703 assert!(Rc::ptr_eq(ofd.inode(), &tty));
704 Ok(())
705 })
706 .unwrap();
707 }
708
709 #[test]
710 fn start_and_wait_for_subshell() {
711 in_virtual_system(|mut env, _state| async move {
712 let subshell = Subshell::new(|env, _job_control| {
713 Box::pin(async { env.exit_status = ExitStatus(42) })
714 });
715 let (pid, _) = subshell.start(&mut env).await.unwrap();
716 let result = env.wait_for_subshell(pid).await;
717 assert_eq!(result, Ok((pid, ProcessState::exited(42))));
718 });
719 }
720
721 #[test]
722 fn start_and_wait_for_subshell_with_job_list() {
723 in_virtual_system(|mut env, _state| async move {
724 let subshell = Subshell::new(|env, _job_control| {
725 Box::pin(async { env.exit_status = ExitStatus(42) })
726 });
727 let (pid, _) = subshell.start(&mut env).await.unwrap();
728 let mut job = Job::new(pid);
729 job.name = "my job".to_string();
730 let job_index = env.jobs.add(job.clone());
731 let result = env.wait_for_subshell(pid).await;
732 assert_eq!(result, Ok((pid, ProcessState::exited(42))));
733 job.state = ProcessState::exited(42);
734 assert_eq!(env.jobs[job_index], job);
735 });
736 }
737
738 #[test]
739 fn wait_for_subshell_no_subshell() {
740 let system = VirtualSystem::new();
741 let mut executor = LocalPool::new();
742 system.state.borrow_mut().executor = Some(Rc::new(executor.spawner()));
743 let mut env = Env::with_system(Box::new(system));
744 executor.run_until(async move {
745 let result = env.wait_for_subshell(Pid::ALL).await;
746 assert_eq!(result, Err(Errno::ECHILD));
747 });
748 }
749
750 #[test]
751 fn update_all_subshell_statuses_without_subshells() {
752 let mut env = Env::new_virtual();
753 env.update_all_subshell_statuses();
754 }
755
756 #[test]
757 fn update_all_subshell_statuses_with_subshells() {
758 let system = VirtualSystem::new();
759 let mut executor = futures_executor::LocalPool::new();
760 system.state.borrow_mut().executor = Some(Rc::new(executor.spawner()));
761
762 let mut env = Env::with_system(Box::new(system));
763
764 let [job_1, job_2, job_3] = executor.run_until(async {
765 let subshell_1 = Subshell::new(|env, _job_control| {
767 Box::pin(async { env.exit_status = ExitStatus(12) })
768 });
769 let (pid_1, _) = subshell_1.start(&mut env).await.unwrap();
770
771 let subshell_2 = Subshell::new(|env, _job_control| {
773 Box::pin(async { env.exit_status = ExitStatus(35) })
774 });
775 let (pid_2, _) = subshell_2.start(&mut env).await.unwrap();
776
777 let subshell_3 =
779 Subshell::new(|_env, _job_control| Box::pin(futures_util::future::pending()));
780 let (pid_3, _) = subshell_3.start(&mut env).await.unwrap();
781
782 let subshell_4 = Subshell::new(|env, _job_control| {
784 Box::pin(async { env.exit_status = ExitStatus(100) })
785 });
786 let (_pid_4, _) = subshell_4.start(&mut env).await.unwrap();
787
788 let job_1 = env.jobs.add(Job::new(pid_1));
790 let job_2 = env.jobs.add(Job::new(pid_2));
791 let job_3 = env.jobs.add(Job::new(pid_3));
792 [job_1, job_2, job_3]
793 });
794
795 executor.run_until_stalled();
797
798 assert_eq!(env.jobs[job_1].state, ProcessState::Running);
800 assert_eq!(env.jobs[job_2].state, ProcessState::Running);
801 assert_eq!(env.jobs[job_3].state, ProcessState::Running);
802
803 env.update_all_subshell_statuses();
804
805 assert_eq!(env.jobs[job_3].state, ProcessState::Running);
809 }
810
811 #[test]
812 fn get_or_create_variable_with_all_export_off() {
813 let mut env = Env::new_virtual();
814 let mut a = env.get_or_create_variable("a", Scope::Global);
815 assert!(!a.is_exported);
816 a.export(true);
817 let a = env.get_or_create_variable("a", Scope::Global);
818 assert!(a.is_exported);
819 }
820
821 #[test]
822 fn get_or_create_variable_with_all_export_on() {
823 let mut env = Env::new_virtual();
824 env.options.set(AllExport, On);
825 let mut a = env.get_or_create_variable("a", Scope::Global);
826 assert!(a.is_exported);
827 a.export(false);
828 let a = env.get_or_create_variable("a", Scope::Global);
829 assert!(a.is_exported);
830 }
831
832 #[test]
833 fn errexit_on() {
834 let mut env = Env::new_virtual();
835 env.exit_status = ExitStatus::FAILURE;
836 env.options.set(ErrExit, On);
837 assert_eq!(env.apply_errexit(), Break(Divert::Exit(None)));
838 }
839
840 #[test]
841 fn errexit_with_zero_exit_status() {
842 let mut env = Env::new_virtual();
843 env.options.set(ErrExit, On);
844 assert_eq!(env.apply_errexit(), Continue(()));
845 }
846
847 #[test]
848 fn errexit_in_condition() {
849 let mut env = Env::new_virtual();
850 env.exit_status = ExitStatus::FAILURE;
851 env.options.set(ErrExit, On);
852 let env = env.push_frame(Frame::Condition);
853 assert_eq!(env.apply_errexit(), Continue(()));
854 }
855
856 #[test]
857 fn errexit_off() {
858 let mut env = Env::new_virtual();
859 env.exit_status = ExitStatus::FAILURE;
860 assert_eq!(env.apply_errexit(), Continue(()));
861 }
862
863 #[test]
864 fn apply_result_with_continue() {
865 let mut env = Env::new_virtual();
866 env.apply_result(Continue(()));
867 assert_eq!(env.exit_status, ExitStatus::default());
868 }
869
870 #[test]
871 fn apply_result_with_divert_without_exit_status() {
872 let mut env = Env::new_virtual();
873 env.apply_result(Break(Divert::Exit(None)));
874 assert_eq!(env.exit_status, ExitStatus::default());
875 }
876
877 #[test]
878 fn apply_result_with_divert_with_exit_status() {
879 let mut env = Env::new_virtual();
880 env.apply_result(Break(Divert::Exit(Some(ExitStatus(67)))));
881 assert_eq!(env.exit_status, ExitStatus(67));
882 }
883}