1use self::alias::AliasSet;
44use self::any::DataSet;
45use self::builtin::Builtin;
46use self::function::FunctionSet;
47use self::io::Fd;
48use self::job::JobList;
49use self::job::Pid;
50use self::job::ProcessResult;
51use self::job::ProcessState;
52use self::option::On;
53use self::option::OptionSet;
54use self::option::{AllExport, ErrExit, Interactive, Monitor};
55use self::semantics::Divert;
56use self::semantics::ExitStatus;
57use self::stack::Frame;
58use self::stack::Stack;
59use self::system::CaughtSignals;
60use self::system::Clock;
61use self::system::Close;
62use self::system::Concurrent;
63use self::system::Dup;
64use self::system::Errno;
65use self::system::Fstat;
66use self::system::GetCwd;
67use self::system::GetPid;
68use self::system::Isatty;
69use self::system::Mode;
70use self::system::OfdAccess;
71use self::system::Open;
72use self::system::OpenFlag;
73use self::system::Select;
74#[allow(deprecated)]
75pub use self::system::SharedSystem;
76use self::system::Sigaction;
77use self::system::Sigmask;
78use self::system::SignalList;
79use self::system::Signals;
80#[allow(deprecated)]
81pub use self::system::System;
82use self::system::TcSetPgrp;
83use self::system::Wait;
84#[cfg(unix)]
85pub use self::system::real::RealSystem;
86pub use self::system::r#virtual::VirtualSystem;
87use self::trap::TrapSet;
88use self::variable::PPID;
89use self::variable::Scope;
90use self::variable::VariableRefMut;
91use self::variable::VariableSet;
92use std::collections::HashMap;
93use std::fmt::Debug;
94use std::ops::ControlFlow::{self, Break, Continue};
95use std::rc::Rc;
96use std::task::Context;
97use std::task::Poll;
98use std::task::Waker;
99pub use unix_path as path;
100pub use unix_str as str;
101
102#[derive(Clone, Debug)]
116#[non_exhaustive]
117pub struct Env<S> {
118 pub aliases: AliasSet,
120
121 pub arg0: String,
125
126 pub builtins: HashMap<&'static str, Builtin<S>>,
128
129 pub exit_status: ExitStatus,
131
132 pub functions: FunctionSet<S>,
134
135 pub jobs: JobList,
137
138 pub main_pgid: Pid,
140
141 pub main_pid: Pid,
145
146 pub options: OptionSet,
148
149 pub stack: Stack,
151
152 pub traps: TrapSet,
154
155 pub tty: Option<Fd>,
160
161 pub variables: VariableSet,
163
164 pub any: DataSet,
166
167 pub system: Rc<Concurrent<S>>,
169}
170
171impl<S> Env<S> {
172 #[must_use]
179 pub fn with_system(system: S) -> Self
180 where
181 S: GetPid,
182 {
183 Env {
184 aliases: Default::default(),
185 arg0: Default::default(),
186 builtins: Default::default(),
187 exit_status: Default::default(),
188 functions: Default::default(),
189 jobs: Default::default(),
190 main_pgid: system.getpgrp(),
191 main_pid: system.getpid(),
192 options: Default::default(),
193 stack: Default::default(),
194 traps: Default::default(),
195 tty: Default::default(),
196 variables: Default::default(),
197 any: Default::default(),
198 system: Rc::new(Concurrent::new(system)),
199 }
200 }
201
202 #[must_use]
208 pub fn clone_with_system(&self, system: S) -> Self {
209 Env {
210 aliases: self.aliases.clone(),
211 arg0: self.arg0.clone(),
212 builtins: self.builtins.clone(),
213 exit_status: self.exit_status,
214 functions: self.functions.clone(),
215 jobs: self.jobs.clone(),
216 main_pgid: self.main_pgid,
217 main_pid: self.main_pid,
218 options: self.options,
219 stack: self.stack.clone(),
220 traps: self.traps.clone(),
221 tty: self.tty,
222 variables: self.variables.clone(),
223 any: self.any.clone(),
224 system: Rc::new(Concurrent::new(system)),
225 }
226 }
227}
228
229impl Env<VirtualSystem> {
230 #[must_use]
232 pub fn new_virtual() -> Self {
233 Env::with_system(VirtualSystem::default())
234 }
235}
236
237impl<S> Env<S> {
238 pub fn init_variables(&mut self)
254 where
255 S: Fstat + GetCwd + GetPid,
256 {
257 self.variables.init();
258
259 self.variables
260 .get_or_new(PPID, Scope::Global)
261 .assign(self.system.getppid().to_string(), None)
262 .ok();
263
264 self.prepare_pwd().ok();
265 }
266
267 pub async fn wait_for_signals(&mut self) -> Rc<SignalList> {
276 let result = self.system.wait_for_signals().await;
277 for signal in result.iter().copied() {
278 self.traps.catch_signal(signal);
279 }
280 result
281 }
282
283 pub async fn wait_for_signal(&mut self, signal: signal::Number) {
288 while !self.wait_for_signals().await.contains(&signal) {}
289 }
290
291 pub fn poll_signals(&mut self) -> Option<Rc<SignalList>>
299 where
300 S: Select + CaughtSignals + Clock,
301 {
302 let system = Rc::clone(&self.system);
303
304 let mut future = std::pin::pin!(self.wait_for_signals());
305 let mut context = Context::from_waker(Waker::noop());
306
307 if let Poll::Ready(signals) = future.as_mut().poll(&mut context) {
309 return Some(signals);
310 }
311
312 system.peek();
314
315 if let Poll::Ready(signals) = future.poll(&mut context) {
317 return Some(signals);
318 }
319
320 None
321 }
322
323 #[must_use]
333 fn should_print_error_in_color(&self) -> bool
334 where
335 S: Isatty,
336 {
337 self.system.isatty(Fd::STDERR)
340 }
341
342 pub async fn get_tty(&mut self) -> Result<Fd, Errno>
347 where
348 S: Open + Dup + Close,
349 {
350 if let Some(fd) = self.tty {
351 return Ok(fd);
352 }
353
354 let first_fd = {
355 let mut result = self
360 .system
361 .open(
362 c"/dev/tty",
363 OfdAccess::ReadWrite,
364 OpenFlag::CloseOnExec | OpenFlag::NoCtty,
365 Mode::empty(),
366 )
367 .await;
368 if result == Err(Errno::EINVAL) {
369 result = self
372 .system
373 .open(
374 c"/dev/tty",
375 OfdAccess::ReadWrite,
376 OpenFlag::CloseOnExec.into(),
377 Mode::empty(),
378 )
379 .await;
380 }
381 result?
382 };
383
384 let final_fd = io::move_fd_internal(&self.system, first_fd);
385 self.tty = final_fd.ok();
386 final_fd
387 }
388
389 pub async fn ensure_foreground(&mut self) -> Result<(), Errno>
410 where
411 S: Open + Dup + Close + GetPid + Signals + Sigmask + Sigaction + TcSetPgrp,
412 {
413 let fd = self.get_tty().await?;
414
415 if self.system.getsid(Pid(0)) == Ok(self.main_pgid) {
416 job::tcsetpgrp_with_block(&self.system, fd, self.main_pgid).await
417 } else {
418 job::tcsetpgrp_without_block(&self.system, fd, self.main_pgid).await
419 }
420 }
421
422 pub async fn wait_for_subshell(&mut self, target: Pid) -> Result<(Pid, ProcessState), Errno>
443 where
444 S: Signals + Sigmask + Sigaction + Wait,
445 {
446 self.traps
449 .enable_internal_disposition_for_sigchld(&self.system)
450 .await?;
451
452 loop {
453 if let Some((pid, state)) = self.system.wait(target)? {
454 self.jobs.update_status(pid, state);
455 return Ok((pid, state));
456 }
457 self.wait_for_signal(S::SIGCHLD).await;
458 }
459 }
460
461 pub async fn wait_for_subshell_to_halt(
468 &mut self,
469 target: Pid,
470 ) -> Result<(Pid, ProcessResult), Errno>
471 where
472 S: Signals + Sigmask + Sigaction + Wait,
473 {
474 loop {
475 let (pid, state) = self.wait_for_subshell(target).await?;
476 if let ProcessState::Halted(result) = state {
477 return Ok((pid, result));
478 }
479 }
480 }
481
482 pub async fn wait_for_subshell_to_finish(
490 &mut self,
491 target: Pid,
492 ) -> Result<(Pid, ExitStatus), Errno>
493 where
494 S: Signals + Sigmask + Sigaction + Wait,
495 {
496 loop {
497 let (pid, result) = self.wait_for_subshell_to_halt(target).await?;
498 if !result.is_stopped() {
499 return Ok((pid, result.into()));
500 }
501 }
502 }
503
504 pub fn update_all_subshell_statuses(&mut self)
513 where
514 S: Wait,
515 {
516 while let Ok(Some((pid, state))) = self.system.wait(Pid::ALL) {
517 self.jobs.update_status(pid, state);
518 }
519 }
520}
521
522impl<S> Env<S> {
523 #[must_use]
530 pub fn is_interactive(&self) -> bool {
531 self.options.get(Interactive) == On && !self.stack.contains(&Frame::Subshell)
532 }
533
534 #[must_use]
541 pub fn controls_jobs(&self) -> bool {
542 self.options.get(Monitor) == On && !self.stack.contains(&Frame::Subshell)
543 }
544
545 pub fn get_or_create_variable<N>(&mut self, name: N, scope: Scope) -> VariableRefMut<'_>
555 where
556 N: Into<String>,
557 {
558 let mut variable = self.variables.get_or_new(name, scope);
559 if self.options.get(AllExport) == On {
560 variable.export(true);
561 }
562 variable
563 }
564
565 pub fn errexit_is_applicable(&self) -> bool {
573 self.options.get(ErrExit) == On && !self.stack.contains(&Frame::Condition)
574 }
575
576 pub fn apply_errexit(&self) -> ControlFlow<Divert> {
583 if !self.exit_status.is_successful() && self.errexit_is_applicable() {
584 Break(Divert::Exit(None))
585 } else {
586 Continue(())
587 }
588 }
589
590 pub fn apply_result(&mut self, result: crate::semantics::Result) {
595 match result {
596 Continue(_) => {}
597 Break(divert) => {
598 if let Some(exit_status) = divert.exit_status() {
599 self.exit_status = exit_status;
600 }
601 }
602 }
603 }
604}
605
606pub mod alias;
607pub mod any;
608pub mod builtin;
609pub mod decl_util;
610pub mod function;
611pub mod input;
612pub mod io;
613pub mod job;
614pub mod option;
615pub mod parser;
616pub mod prompt;
617pub mod pwd;
618pub mod semantics;
619pub mod signal;
620pub mod source;
621pub mod stack;
622pub mod subshell;
623pub mod system;
624pub mod trap;
625pub mod variable;
626pub mod waker;
627
628#[cfg(any(test, feature = "yash-executor"))]
629mod executor_helper;
630#[cfg(any(test, feature = "test-helper"))]
631pub mod test_helper;
632
633#[cfg(test)]
634mod tests {
635 use super::*;
636 use crate::io::MIN_INTERNAL_FD;
637 use crate::job::Job;
638 use crate::source::Location;
639 use crate::subshell::Subshell;
640 use crate::system::r#virtual::Inode;
641 use crate::system::r#virtual::SIGCHLD;
642 use crate::test_helper::in_virtual_system;
643 use crate::trap::Action;
644 use futures_executor::LocalPool;
645 use futures_util::FutureExt as _;
646 use std::cell::RefCell;
647
648 #[test]
649 fn wait_for_signal_remembers_signal_in_trap_set() {
650 in_virtual_system(|mut env, state| async move {
651 env.traps
652 .set_action(
653 &env.system,
654 SIGCHLD,
655 Action::Command("".into()),
656 Location::dummy(""),
657 false,
658 )
659 .await
660 .unwrap();
661 {
662 let mut state = state.borrow_mut();
663 let process = state.processes.get_mut(&env.main_pid).unwrap();
664 assert!(process.blocked_signals().contains(&SIGCHLD));
665 let _ = process.raise_signal(SIGCHLD);
666 }
667 env.wait_for_signal(SIGCHLD).await;
668
669 let trap_state = env.traps.get_state(SIGCHLD).0.unwrap();
670 assert!(trap_state.pending);
671 })
672 }
673
674 fn poll_signals_env() -> (Env<VirtualSystem>, VirtualSystem) {
675 let system = VirtualSystem::new();
676 let mut env = Env::with_system(system.clone());
677 env.traps
678 .set_action(
679 &env.system,
680 SIGCHLD,
681 Action::Command("".into()),
682 Location::dummy(""),
683 false,
684 )
685 .now_or_never()
686 .unwrap()
687 .unwrap();
688 (env, system)
689 }
690
691 #[test]
692 fn poll_signals_none() {
693 let mut env = poll_signals_env().0;
694 let result = env.poll_signals();
695 assert_eq!(result, None);
696 }
697
698 #[test]
699 fn poll_signals_some() {
700 let (mut env, system) = poll_signals_env();
701 {
702 let mut state = system.state.borrow_mut();
703 let process = state.processes.get_mut(&system.process_id).unwrap();
704 assert!(process.blocked_signals().contains(&SIGCHLD));
705 let _ = process.raise_signal(SIGCHLD);
706 }
707
708 let result = env.poll_signals().unwrap();
709 assert_eq!(result.as_slice(), [SIGCHLD]);
710 }
711
712 #[test]
713 fn get_tty_opens_tty() {
714 let system = VirtualSystem::new();
715 let tty = Rc::new(RefCell::new(Inode::new([])));
716 system
717 .state
718 .borrow_mut()
719 .file_system
720 .save("/dev/tty", Rc::clone(&tty))
721 .unwrap();
722 let mut env = Env::with_system(system.clone());
723
724 let fd = env.get_tty().now_or_never().unwrap().unwrap();
725 assert!(
726 fd >= MIN_INTERNAL_FD,
727 "get_tty returned {fd}, which should be >= {MIN_INTERNAL_FD}"
728 );
729 system
730 .with_open_file_description(fd, |ofd| {
731 assert!(Rc::ptr_eq(ofd.inode(), &tty));
732 Ok(())
733 })
734 .unwrap();
735
736 system.state.borrow_mut().file_system = Default::default();
737
738 let fd = env.get_tty().now_or_never().unwrap().unwrap();
740 system
741 .with_open_file_description(fd, |ofd| {
742 assert!(Rc::ptr_eq(ofd.inode(), &tty));
743 Ok(())
744 })
745 .unwrap();
746 }
747
748 #[test]
749 fn start_and_wait_for_subshell() {
750 in_virtual_system(|mut env, _state| async move {
751 let subshell = Subshell::new(|env, _job_control| {
752 Box::pin(async { env.exit_status = ExitStatus(42) })
753 });
754 let (pid, _) = subshell.start(&mut env).await.unwrap();
755 let result = env.wait_for_subshell(pid).await;
756 assert_eq!(result, Ok((pid, ProcessState::exited(42))));
757 });
758 }
759
760 #[test]
761 fn start_and_wait_for_subshell_with_job_list() {
762 in_virtual_system(|mut env, _state| async move {
763 let subshell = Subshell::new(|env, _job_control| {
764 Box::pin(async { env.exit_status = ExitStatus(42) })
765 });
766 let (pid, _) = subshell.start(&mut env).await.unwrap();
767 let mut job = Job::new(pid);
768 job.name = "my job".to_string();
769 let job_index = env.jobs.add(job.clone());
770 let result = env.wait_for_subshell(pid).await;
771 assert_eq!(result, Ok((pid, ProcessState::exited(42))));
772 job.state = ProcessState::exited(42);
773 assert_eq!(env.jobs[job_index], job);
774 });
775 }
776
777 #[test]
778 fn wait_for_subshell_no_subshell() {
779 let system = VirtualSystem::new();
780 let mut executor = LocalPool::new();
781 system.state.borrow_mut().executor = Some(Rc::new(executor.spawner()));
782 let mut env = Env::with_system(system);
783 executor.run_until(async move {
784 let result = env.wait_for_subshell(Pid::ALL).await;
785 assert_eq!(result, Err(Errno::ECHILD));
786 });
787 }
788
789 #[test]
790 fn update_all_subshell_statuses_without_subshells() {
791 let mut env = Env::new_virtual();
792 env.update_all_subshell_statuses();
793 }
794
795 #[test]
796 fn update_all_subshell_statuses_with_subshells() {
797 let system = VirtualSystem::new();
798 let mut executor = futures_executor::LocalPool::new();
799 system.state.borrow_mut().executor = Some(Rc::new(executor.spawner()));
800
801 let mut env = Env::with_system(system);
802
803 let [job_1, job_2, job_3] = executor.run_until(async {
804 let subshell_1 = Subshell::new(|env, _job_control| {
806 Box::pin(async { env.exit_status = ExitStatus(12) })
807 });
808 let (pid_1, _) = subshell_1.start(&mut env).await.unwrap();
809
810 let subshell_2 = Subshell::new(|env, _job_control| {
812 Box::pin(async { env.exit_status = ExitStatus(35) })
813 });
814 let (pid_2, _) = subshell_2.start(&mut env).await.unwrap();
815
816 let subshell_3 =
818 Subshell::new(|_env, _job_control| Box::pin(futures_util::future::pending()));
819 let (pid_3, _) = subshell_3.start(&mut env).await.unwrap();
820
821 let subshell_4 = Subshell::new(|env, _job_control| {
823 Box::pin(async { env.exit_status = ExitStatus(100) })
824 });
825 let (_pid_4, _) = subshell_4.start(&mut env).await.unwrap();
826
827 let job_1 = env.jobs.add(Job::new(pid_1));
829 let job_2 = env.jobs.add(Job::new(pid_2));
830 let job_3 = env.jobs.add(Job::new(pid_3));
831 [job_1, job_2, job_3]
832 });
833
834 executor.run_until_stalled();
836
837 assert_eq!(env.jobs[job_1].state, ProcessState::Running);
839 assert_eq!(env.jobs[job_2].state, ProcessState::Running);
840 assert_eq!(env.jobs[job_3].state, ProcessState::Running);
841
842 env.update_all_subshell_statuses();
843
844 assert_eq!(env.jobs[job_3].state, ProcessState::Running);
848 }
849
850 #[test]
851 fn get_or_create_variable_with_all_export_off() {
852 let mut env = Env::new_virtual();
853 let mut a = env.get_or_create_variable("a", Scope::Global);
854 assert!(!a.is_exported);
855 a.export(true);
856 let a = env.get_or_create_variable("a", Scope::Global);
857 assert!(a.is_exported);
858 }
859
860 #[test]
861 fn get_or_create_variable_with_all_export_on() {
862 let mut env = Env::new_virtual();
863 env.options.set(AllExport, On);
864 let mut a = env.get_or_create_variable("a", Scope::Global);
865 assert!(a.is_exported);
866 a.export(false);
867 let a = env.get_or_create_variable("a", Scope::Global);
868 assert!(a.is_exported);
869 }
870
871 #[test]
872 fn errexit_on() {
873 let mut env = Env::new_virtual();
874 env.exit_status = ExitStatus::FAILURE;
875 env.options.set(ErrExit, On);
876 assert_eq!(env.apply_errexit(), Break(Divert::Exit(None)));
877 }
878
879 #[test]
880 fn errexit_with_zero_exit_status() {
881 let mut env = Env::new_virtual();
882 env.options.set(ErrExit, On);
883 assert_eq!(env.apply_errexit(), Continue(()));
884 }
885
886 #[test]
887 fn errexit_in_condition() {
888 let mut env = Env::new_virtual();
889 env.exit_status = ExitStatus::FAILURE;
890 env.options.set(ErrExit, On);
891 let env = env.push_frame(Frame::Condition);
892 assert_eq!(env.apply_errexit(), Continue(()));
893 }
894
895 #[test]
896 fn errexit_off() {
897 let mut env = Env::new_virtual();
898 env.exit_status = ExitStatus::FAILURE;
899 assert_eq!(env.apply_errexit(), Continue(()));
900 }
901
902 #[test]
903 fn apply_result_with_continue() {
904 let mut env = Env::new_virtual();
905 env.apply_result(Continue(()));
906 assert_eq!(env.exit_status, ExitStatus::default());
907 }
908
909 #[test]
910 fn apply_result_with_divert_without_exit_status() {
911 let mut env = Env::new_virtual();
912 env.apply_result(Break(Divert::Exit(None)));
913 assert_eq!(env.exit_status, ExitStatus::default());
914 }
915
916 #[test]
917 fn apply_result_with_divert_with_exit_status() {
918 let mut env = Env::new_virtual();
919 env.apply_result(Break(Divert::Exit(Some(ExitStatus(67)))));
920 assert_eq!(env.exit_status, ExitStatus(67));
921 }
922}