1mod errno;
25mod file_system;
26mod open_flag;
27mod resource;
28mod signal;
29
30use super::AT_FDCWD;
31use super::CaughtSignals;
32use super::Chdir;
33use super::ChildProcessStarter;
34use super::Clock;
35use super::Close;
36use super::CpuTimes;
37use super::Dir;
38use super::DirEntry;
39use super::Disposition;
40use super::Dup;
41use super::Errno;
42use super::Exec;
43use super::Exit;
44use super::Fcntl;
45use super::FdFlag;
46use super::FileType;
47use super::FlexFuture;
48use super::Fork;
49use super::Fstat;
50use super::GetCwd;
51use super::GetPid;
52use super::GetPw;
53use super::GetRlimit;
54use super::GetUid;
55use super::Gid;
56use super::IsExecutableFile;
57use super::Isatty;
58use super::Mode;
59use super::OfdAccess;
60use super::Open;
61use super::OpenFlag;
62use super::Pipe;
63use super::Read;
64use super::Result;
65use super::Seek;
66use super::Select;
67use super::SendSignal;
68use super::SetPgid;
69use super::SetRlimit;
70use super::ShellPath;
71use super::Sigaction;
72use super::Sigmask;
73use super::SigmaskOp;
74use super::Signals;
75use super::Stat;
76use super::Sysconf;
77#[cfg(doc)]
78use super::System;
79use super::TcGetPgrp;
80use super::TcSetPgrp;
81use super::Times;
82use super::Uid;
83use super::Umask;
84use super::Wait;
85use super::Write;
86use super::resource::LimitPair;
87use super::resource::Resource;
88#[cfg(doc)]
89use crate::Env;
90use crate::io::Fd;
91use crate::job::Pid;
92use crate::job::ProcessResult;
93use crate::job::ProcessState;
94use crate::path::Path;
95use crate::path::PathBuf;
96use crate::semantics::ExitStatus;
97use crate::str::UnixStr;
98use crate::str::UnixString;
99use enumset::EnumSet;
100use libc::DIR;
101use std::convert::Infallible;
102use std::convert::TryInto;
103use std::ffi::CStr;
104use std::ffi::CString;
105use std::ffi::OsStr;
106use std::ffi::c_int;
107use std::io::SeekFrom;
108use std::mem::MaybeUninit;
109use std::num::NonZero;
110use std::os::unix::ffi::OsStrExt as _;
111use std::os::unix::io::IntoRawFd;
112use std::ptr::NonNull;
113use std::sync::atomic::AtomicIsize;
114use std::sync::atomic::Ordering;
115use std::sync::atomic::compiler_fence;
116use std::time::Duration;
117use std::time::Instant;
118use yash_executor::Executor;
119
120trait ErrnoIfM1: PartialEq + Sized {
121 const MINUS_1: Self;
122
123 fn errno_if_m1(self) -> Result<Self> {
131 if self == Self::MINUS_1 {
132 Err(Errno::last())
133 } else {
134 Ok(self)
135 }
136 }
137}
138
139impl ErrnoIfM1 for i8 {
140 const MINUS_1: Self = -1;
141}
142impl ErrnoIfM1 for i16 {
143 const MINUS_1: Self = -1;
144}
145impl ErrnoIfM1 for i32 {
146 const MINUS_1: Self = -1;
147}
148impl ErrnoIfM1 for i64 {
149 const MINUS_1: Self = -1;
150}
151impl ErrnoIfM1 for isize {
152 const MINUS_1: Self = -1;
153}
154
155#[must_use]
160fn to_timespec(duration: Duration) -> MaybeUninit<libc::timespec> {
161 let seconds = duration.as_secs().try_into().unwrap_or(libc::time_t::MAX);
162 let mut timespec = MaybeUninit::<libc::timespec>::uninit();
163 unsafe {
164 (&raw mut (*timespec.as_mut_ptr()).tv_sec).write(seconds);
165 (&raw mut (*timespec.as_mut_ptr()).tv_nsec).write(duration.subsec_nanos() as _);
166 }
167 timespec
168}
169
170static CAUGHT_SIGNALS: [AtomicIsize; 8] = [const { AtomicIsize::new(0) }; 8];
176
177extern "C" fn catch_signal(signal: c_int) {
183 let signal = signal as isize;
191 for slot in &CAUGHT_SIGNALS {
192 match slot.compare_exchange(0, signal, Ordering::Relaxed, Ordering::Relaxed) {
193 Ok(_) => break,
194 Err(slot_value) if slot_value == signal => break,
195 _ => continue,
196 }
197 }
198}
199
200fn sigaction_impl(signal: signal::Number, disposition: Option<Disposition>) -> Result<Disposition> {
201 let new_action = disposition.map(Disposition::to_sigaction);
202 let new_action_ptr = new_action
203 .as_ref()
204 .map_or(std::ptr::null(), |action| action.as_ptr());
205
206 let mut old_action = MaybeUninit::<libc::sigaction>::uninit();
207 let old_mask_ptr = unsafe { &raw mut (*old_action.as_mut_ptr()).sa_mask };
210 unsafe { libc::sigemptyset(old_mask_ptr) }.errno_if_m1()?;
213
214 unsafe { libc::sigaction(signal.as_raw(), new_action_ptr, old_action.as_mut_ptr()) }
215 .errno_if_m1()?;
216
217 let old_disposition = unsafe { Disposition::from_sigaction(&old_action) };
219 Ok(old_disposition)
220}
221
222#[derive(Debug)]
227pub struct RealSystem(());
228
229impl RealSystem {
230 pub unsafe fn new() -> Self {
241 RealSystem(())
242 }
243
244 fn file_has_type(&self, path: &CStr, r#type: FileType) -> bool {
245 self.fstatat(AT_FDCWD, path, true)
246 .is_ok_and(|stat| stat.r#type == r#type)
247 }
248
249 #[cfg(not(target_os = "redox"))]
251 fn has_execute_permission(&self, path: &CStr) -> bool {
252 (unsafe { libc::faccessat(libc::AT_FDCWD, path.as_ptr(), libc::X_OK, libc::AT_EACCESS) })
253 != -1
254 }
255 #[cfg(target_os = "redox")]
256 fn has_execute_permission(&self, path: &CStr) -> bool {
257 (unsafe { libc::access(path.as_ptr(), libc::X_OK) }) != -1
258 }
259}
260
261impl Fstat for RealSystem {
262 fn fstat(&self, fd: Fd) -> Result<Stat> {
263 let mut stat = MaybeUninit::<libc::stat>::uninit();
264 unsafe { libc::fstat(fd.0, stat.as_mut_ptr()) }.errno_if_m1()?;
265 let stat = unsafe { Stat::from_raw(&stat) };
266 Ok(stat)
267 }
268
269 fn fstatat(&self, dir_fd: Fd, path: &CStr, follow_symlinks: bool) -> Result<Stat> {
270 let flags = if follow_symlinks {
271 0
272 } else {
273 libc::AT_SYMLINK_NOFOLLOW
274 };
275
276 let mut stat = MaybeUninit::<libc::stat>::uninit();
277 unsafe { libc::fstatat(dir_fd.0, path.as_ptr(), stat.as_mut_ptr(), flags) }
278 .errno_if_m1()?;
279 let stat = unsafe { Stat::from_raw(&stat) };
280 Ok(stat)
281 }
282}
283
284impl IsExecutableFile for RealSystem {
285 fn is_executable_file(&self, path: &CStr) -> bool {
286 self.file_has_type(path, FileType::Regular) && self.has_execute_permission(path)
287 }
288}
289
290impl Pipe for RealSystem {
291 fn pipe(&self) -> Result<(Fd, Fd)> {
292 let mut fds = MaybeUninit::<[c_int; 2]>::uninit();
293 unsafe { libc::pipe(fds.as_mut_ptr().cast()) }.errno_if_m1()?;
295 let fds = unsafe { fds.assume_init() };
296 Ok((Fd(fds[0]), Fd(fds[1])))
297 }
298}
299
300impl Dup for RealSystem {
301 fn dup(&self, from: Fd, to_min: Fd, flags: EnumSet<FdFlag>) -> Result<Fd> {
302 let command = if flags.contains(FdFlag::CloseOnExec) {
303 libc::F_DUPFD_CLOEXEC
304 } else {
305 libc::F_DUPFD
306 };
307 unsafe { libc::fcntl(from.0, command, to_min.0) }
308 .errno_if_m1()
309 .map(Fd)
310 }
311
312 fn dup2(&self, from: Fd, to: Fd) -> Result<Fd> {
313 loop {
314 let result = unsafe { libc::dup2(from.0, to.0) }.errno_if_m1().map(Fd);
315 if result != Err(Errno::EINTR) {
316 return result;
317 }
318 }
319 }
320}
321
322impl Open for RealSystem {
323 fn open(
324 &self,
325 path: &CStr,
326 access: OfdAccess,
327 flags: EnumSet<OpenFlag>,
328 mode: Mode,
329 ) -> Result<Fd> {
330 let mut raw_flags = access.to_real_flag().ok_or(Errno::EINVAL)?;
331 for flag in flags {
332 raw_flags |= flag.to_real_flag().ok_or(Errno::EINVAL)?;
333 }
334
335 #[cfg(not(target_os = "redox"))]
336 let mode_bits = mode.bits() as std::ffi::c_uint;
337 #[cfg(target_os = "redox")]
338 let mode_bits = mode.bits() as c_int;
339
340 unsafe { libc::open(path.as_ptr(), raw_flags, mode_bits) }
341 .errno_if_m1()
342 .map(Fd)
343 }
344
345 fn open_tmpfile(&self, parent_dir: &Path) -> Result<Fd> {
346 let parent_dir = OsStr::from_bytes(parent_dir.as_unix_str().as_bytes());
347 let file = tempfile::tempfile_in(parent_dir)
348 .map_err(|errno| Errno(errno.raw_os_error().unwrap_or(0)))?;
349 let fd = Fd(file.into_raw_fd());
350
351 _ = self.fcntl_setfd(fd, EnumSet::empty());
353
354 Ok(fd)
355 }
356
357 fn fdopendir(&self, fd: Fd) -> Result<impl Dir + use<>> {
358 let dir = unsafe { libc::fdopendir(fd.0) };
359 let dir = NonNull::new(dir).ok_or_else(Errno::last)?;
360 Ok(RealDir(dir))
361 }
362
363 fn opendir(&self, path: &CStr) -> Result<impl Dir + use<>> {
364 let dir = unsafe { libc::opendir(path.as_ptr()) };
365 let dir = NonNull::new(dir).ok_or_else(Errno::last)?;
366 Ok(RealDir(dir))
367 }
368}
369
370impl Close for RealSystem {
371 fn close(&self, fd: Fd) -> Result<()> {
372 loop {
374 let result = unsafe { libc::close(fd.0) }.errno_if_m1().map(drop);
375 match result {
376 Err(Errno::EBADF) => return Ok(()),
377 Err(Errno::EINTR) => continue,
378 other => return other,
379 }
380 }
381 }
382}
383
384impl Fcntl for RealSystem {
385 fn ofd_access(&self, fd: Fd) -> Result<OfdAccess> {
386 let flags = unsafe { libc::fcntl(fd.0, libc::F_GETFL) }.errno_if_m1()?;
387 Ok(OfdAccess::from_real_flag(flags))
388 }
389
390 fn get_and_set_nonblocking(&self, fd: Fd, nonblocking: bool) -> Result<bool> {
391 let old_flags = unsafe { libc::fcntl(fd.0, libc::F_GETFL) }.errno_if_m1()?;
392 let new_flags = if nonblocking {
393 old_flags | libc::O_NONBLOCK
394 } else {
395 old_flags & !libc::O_NONBLOCK
396 };
397 if new_flags != old_flags {
398 unsafe { libc::fcntl(fd.0, libc::F_SETFL, new_flags) }.errno_if_m1()?;
399 }
400 let was_nonblocking = old_flags & libc::O_NONBLOCK != 0;
401 Ok(was_nonblocking)
402 }
403
404 fn fcntl_getfd(&self, fd: Fd) -> Result<EnumSet<FdFlag>> {
405 let bits = unsafe { libc::fcntl(fd.0, libc::F_GETFD) }.errno_if_m1()?;
406 let mut flags = EnumSet::empty();
407 if bits & libc::FD_CLOEXEC != 0 {
408 flags.insert(FdFlag::CloseOnExec);
409 }
410 Ok(flags)
411 }
412
413 fn fcntl_setfd(&self, fd: Fd, flags: EnumSet<FdFlag>) -> Result<()> {
414 let mut bits = 0 as c_int;
415 if flags.contains(FdFlag::CloseOnExec) {
416 bits |= libc::FD_CLOEXEC;
417 }
418 unsafe { libc::fcntl(fd.0, libc::F_SETFD, bits) }
419 .errno_if_m1()
420 .map(drop)
421 }
422}
423
424impl Read for RealSystem {
425 fn read(&self, fd: Fd, buffer: &mut [u8]) -> Result<usize> {
426 loop {
427 let result =
428 unsafe { libc::read(fd.0, buffer.as_mut_ptr().cast(), buffer.len()) }.errno_if_m1();
429 if result != Err(Errno::EINTR) {
430 return Ok(result?.try_into().unwrap());
431 }
432 }
433 }
434}
435
436impl Write for RealSystem {
437 fn write(&self, fd: Fd, buffer: &[u8]) -> Result<usize> {
438 loop {
439 let result =
440 unsafe { libc::write(fd.0, buffer.as_ptr().cast(), buffer.len()) }.errno_if_m1();
441 if result != Err(Errno::EINTR) {
442 return Ok(result?.try_into().unwrap());
443 }
444 }
445 }
446}
447
448impl Seek for RealSystem {
449 fn lseek(&self, fd: Fd, position: SeekFrom) -> Result<u64> {
450 let (offset, whence) = match position {
451 SeekFrom::Start(offset) => {
452 let offset = offset.try_into().map_err(|_| Errno::EOVERFLOW)?;
453 (offset, libc::SEEK_SET)
454 }
455 SeekFrom::End(offset) => (offset, libc::SEEK_END),
456 SeekFrom::Current(offset) => (offset, libc::SEEK_CUR),
457 };
458 let new_offset = unsafe { libc::lseek(fd.0, offset, whence) }.errno_if_m1()?;
459 Ok(new_offset.try_into().unwrap())
460 }
461}
462
463impl Umask for RealSystem {
464 fn umask(&self, new_mask: Mode) -> Mode {
465 Mode::from_bits_retain(unsafe { libc::umask(new_mask.bits()) })
466 }
467}
468
469impl GetCwd for RealSystem {
470 fn getcwd(&self) -> Result<PathBuf> {
471 let mut buffer = Vec::<u8>::new();
478 for capacity in [1 << 10, 1 << 12, 1 << 14, 1 << 16] {
479 buffer.reserve_exact(capacity);
480
481 let result = unsafe { libc::getcwd(buffer.as_mut_ptr().cast(), capacity) };
482 if !result.is_null() {
483 let len = unsafe { CStr::from_ptr(buffer.as_ptr().cast()) }.count_bytes();
485 unsafe { buffer.set_len(len) }
486 buffer.shrink_to_fit();
487 return Ok(PathBuf::from(UnixString::from_vec(buffer)));
488 }
489 let errno = Errno::last();
490 if errno != Errno::ERANGE {
491 return Err(errno);
492 }
493 }
494 Err(Errno::ERANGE)
495 }
496}
497
498impl Chdir for RealSystem {
499 fn chdir(&self, path: &CStr) -> Result<()> {
500 let result = unsafe { libc::chdir(path.as_ptr()) };
501 result.errno_if_m1().map(drop)
502 }
503}
504
505impl Clock for RealSystem {
506 fn now(&self) -> Instant {
507 Instant::now()
508 }
509}
510
511impl Times for RealSystem {
512 fn times(&self) -> Result<CpuTimes> {
517 let mut usage = MaybeUninit::<libc::rusage>::uninit();
518
519 unsafe { libc::getrusage(libc::RUSAGE_SELF, usage.as_mut_ptr()) }.errno_if_m1()?;
520 let self_user = unsafe {
521 (*usage.as_ptr()).ru_utime.tv_sec as f64
522 + (*usage.as_ptr()).ru_utime.tv_usec as f64 * 1e-6
523 };
524 let self_system = unsafe {
525 (*usage.as_ptr()).ru_stime.tv_sec as f64
526 + (*usage.as_ptr()).ru_stime.tv_usec as f64 * 1e-6
527 };
528
529 unsafe { libc::getrusage(libc::RUSAGE_CHILDREN, usage.as_mut_ptr()) }.errno_if_m1()?;
530 let children_user = unsafe {
531 (*usage.as_ptr()).ru_utime.tv_sec as f64
532 + (*usage.as_ptr()).ru_utime.tv_usec as f64 * 1e-6
533 };
534 let children_system = unsafe {
535 (*usage.as_ptr()).ru_stime.tv_sec as f64
536 + (*usage.as_ptr()).ru_stime.tv_usec as f64 * 1e-6
537 };
538
539 Ok(CpuTimes {
540 self_user,
541 self_system,
542 children_user,
543 children_system,
544 })
545 }
546}
547
548impl GetPid for RealSystem {
549 fn getsid(&self, pid: Pid) -> Result<Pid> {
550 unsafe { libc::getsid(pid.0) }.errno_if_m1().map(Pid)
551 }
552
553 fn getpid(&self) -> Pid {
554 Pid(unsafe { libc::getpid() })
555 }
556
557 fn getppid(&self) -> Pid {
558 Pid(unsafe { libc::getppid() })
559 }
560
561 fn getpgrp(&self) -> Pid {
562 Pid(unsafe { libc::getpgrp() })
563 }
564}
565
566impl SetPgid for RealSystem {
567 fn setpgid(&self, pid: Pid, pgid: Pid) -> Result<()> {
568 let result = unsafe { libc::setpgid(pid.0, pgid.0) };
569 result.errno_if_m1().map(drop)
570 }
571}
572
573impl Signals for RealSystem {
574 fn validate_signal(&self, number: signal::RawNumber) -> Option<(signal::Name, signal::Number)> {
575 let non_zero = NonZero::new(number)?;
576 let name = signal::Name::try_from_raw_real(number)?;
577 Some((name, signal::Number::from_raw_unchecked(non_zero)))
578 }
579
580 #[inline(always)]
581 fn signal_number_from_name(&self, name: signal::Name) -> Option<signal::Number> {
582 name.to_raw_real()
583 }
584}
585
586impl Sigmask for RealSystem {
587 fn sigmask(
588 &self,
589 op: Option<(SigmaskOp, &[signal::Number])>,
590 old_mask: Option<&mut Vec<signal::Number>>,
591 ) -> Result<()> {
592 unsafe {
593 let (how, raw_mask) = match op {
594 None => (libc::SIG_BLOCK, None),
595 Some((op, mask)) => {
596 let how = match op {
597 SigmaskOp::Add => libc::SIG_BLOCK,
598 SigmaskOp::Remove => libc::SIG_UNBLOCK,
599 SigmaskOp::Set => libc::SIG_SETMASK,
600 };
601
602 let mut raw_mask = MaybeUninit::<libc::sigset_t>::uninit();
603 libc::sigemptyset(raw_mask.as_mut_ptr()).errno_if_m1()?;
604 for &signal in mask {
605 libc::sigaddset(raw_mask.as_mut_ptr(), signal.as_raw()).errno_if_m1()?;
606 }
607
608 (how, Some(raw_mask))
609 }
610 };
611 let mut old_mask_pair = match old_mask {
612 None => None,
613 Some(old_mask) => {
614 let mut raw_old_mask = MaybeUninit::<libc::sigset_t>::uninit();
615 libc::sigemptyset(raw_old_mask.as_mut_ptr()).errno_if_m1()?;
617 Some((old_mask, raw_old_mask))
618 }
619 };
620
621 let raw_set_ptr = raw_mask
622 .as_ref()
623 .map_or(std::ptr::null(), |raw_set| raw_set.as_ptr());
624 let raw_old_set_ptr = old_mask_pair
625 .as_mut()
626 .map_or(std::ptr::null_mut(), |(_, raw_old_mask)| {
627 raw_old_mask.as_mut_ptr()
628 });
629 let result = libc::sigprocmask(how, raw_set_ptr, raw_old_set_ptr);
630 result.errno_if_m1().map(drop)?;
631
632 if let Some((old_mask, raw_old_mask)) = old_mask_pair {
633 old_mask.clear();
634 signal::sigset_to_vec(raw_old_mask.as_ptr(), old_mask);
635 }
636
637 Ok(())
638 }
639 }
640}
641
642impl Sigaction for RealSystem {
643 fn get_sigaction(&self, signal: signal::Number) -> Result<Disposition> {
644 sigaction_impl(signal, None)
645 }
646
647 fn sigaction(&self, signal: signal::Number, handling: Disposition) -> Result<Disposition> {
648 sigaction_impl(signal, Some(handling))
649 }
650}
651
652impl CaughtSignals for RealSystem {
653 fn caught_signals(&self) -> Vec<signal::Number> {
654 let mut signals = Vec::new();
655 for slot in &CAUGHT_SIGNALS {
656 compiler_fence(Ordering::Acquire);
658
659 let signal = slot.swap(0, Ordering::Relaxed);
660 if signal == 0 {
661 break;
664 }
665
666 if let Some((_name, number)) = self.validate_signal(signal as signal::RawNumber) {
667 signals.push(number)
668 } else {
669 }
671 }
672 signals
673 }
674}
675
676impl SendSignal for RealSystem {
677 fn kill(&self, target: Pid, signal: Option<signal::Number>) -> FlexFuture<Result<()>> {
678 let raw = signal.map_or(0, signal::Number::as_raw);
679 let result = unsafe { libc::kill(target.0, raw) }.errno_if_m1().map(drop);
680 result.into()
681 }
682
683 fn raise(&self, signal: signal::Number) -> FlexFuture<Result<()>> {
684 let raw = signal.as_raw();
685 unsafe { libc::raise(raw) }.errno_if_m1().map(drop).into()
686 }
687}
688
689impl Select for RealSystem {
690 fn select(
691 &self,
692 readers: &mut Vec<Fd>,
693 writers: &mut Vec<Fd>,
694 timeout: Option<Duration>,
695 signal_mask: Option<&[signal::Number]>,
696 ) -> Result<c_int> {
697 use std::ptr::{null, null_mut};
698
699 let max_fd = readers.iter().chain(writers.iter()).max();
700 let nfds = max_fd
701 .map(|fd| fd.0.checked_add(1).ok_or(Errno::EBADF))
702 .transpose()?
703 .unwrap_or(0);
704
705 fn to_raw_fd_set(fds: &[Fd]) -> MaybeUninit<libc::fd_set> {
706 let mut raw_fds = MaybeUninit::<libc::fd_set>::uninit();
707 unsafe {
708 libc::FD_ZERO(raw_fds.as_mut_ptr());
709 for fd in fds {
710 libc::FD_SET(fd.0, raw_fds.as_mut_ptr());
711 }
712 }
713 raw_fds
714 }
715 let mut raw_readers = to_raw_fd_set(readers);
716 let mut raw_writers = to_raw_fd_set(writers);
717 let readers_ptr = raw_readers.as_mut_ptr();
718 let writers_ptr = raw_writers.as_mut_ptr();
719 let errors = null_mut();
720
721 let timeout_spec = to_timespec(timeout.unwrap_or_default());
722 let timeout_ptr = if timeout.is_some() {
723 timeout_spec.as_ptr()
724 } else {
725 null()
726 };
727
728 let mut raw_mask = MaybeUninit::<libc::sigset_t>::uninit();
729 let raw_mask_ptr = match signal_mask {
730 None => null(),
731 Some(signal_mask) => {
732 unsafe { libc::sigemptyset(raw_mask.as_mut_ptr()) }.errno_if_m1()?;
733 for &signal in signal_mask {
734 unsafe { libc::sigaddset(raw_mask.as_mut_ptr(), signal.as_raw()) }
735 .errno_if_m1()?;
736 }
737 raw_mask.as_ptr()
738 }
739 };
740
741 let count = unsafe {
742 libc::pselect(
743 nfds,
744 readers_ptr,
745 writers_ptr,
746 errors,
747 timeout_ptr,
748 raw_mask_ptr,
749 )
750 }
751 .errno_if_m1()?;
752
753 readers.retain(|fd| unsafe { libc::FD_ISSET(fd.0, readers_ptr) });
754 writers.retain(|fd| unsafe { libc::FD_ISSET(fd.0, writers_ptr) });
755
756 Ok(count)
757 }
758}
759
760impl Isatty for RealSystem {
761 fn isatty(&self, fd: Fd) -> bool {
762 (unsafe { libc::isatty(fd.0) } != 0)
763 }
764}
765
766impl TcGetPgrp for RealSystem {
767 fn tcgetpgrp(&self, fd: Fd) -> Result<Pid> {
768 unsafe { libc::tcgetpgrp(fd.0) }.errno_if_m1().map(Pid)
769 }
770}
771
772impl TcSetPgrp for RealSystem {
773 fn tcsetpgrp(&self, fd: Fd, pgid: Pid) -> FlexFuture<Result<()>> {
774 let result = unsafe { libc::tcsetpgrp(fd.0, pgid.0) };
775 result.errno_if_m1().map(drop).into()
776 }
777}
778
779impl Fork for RealSystem {
780 fn new_child_process(&self) -> Result<ChildProcessStarter<Self>> {
788 let raw_pid = unsafe { libc::fork() }.errno_if_m1()?;
789 if raw_pid != 0 {
790 return Ok(Box::new(move |_env, _task| Pid(raw_pid)));
792 }
793
794 Ok(Box::new(|env, task| {
796 let system = env.system.clone();
797 let executor = Executor::new();
801 let task = Box::pin(async move { match task(env).await {} });
802 unsafe { executor.spawn_pinned(task) }
805 loop {
806 executor.run_until_stalled();
807 system.select(false).ok();
808 }
809 }))
810 }
811}
812
813impl Wait for RealSystem {
814 fn wait(&self, target: Pid) -> Result<Option<(Pid, ProcessState)>> {
815 let mut status = 0;
816 let options = libc::WUNTRACED | libc::WCONTINUED | libc::WNOHANG;
817 match unsafe { libc::waitpid(target.0, &mut status, options) } {
818 -1 => Err(Errno::last()),
819 0 => Ok(None),
820 pid => {
821 let state = if libc::WIFCONTINUED(status) {
822 ProcessState::Running
823 } else if libc::WIFEXITED(status) {
824 let exit_status = libc::WEXITSTATUS(status);
825 ProcessState::exited(exit_status)
826 } else if libc::WIFSIGNALED(status) {
827 let signal = libc::WTERMSIG(status);
828 let core_dump = libc::WCOREDUMP(status);
829 let raw_number = unsafe { NonZero::new_unchecked(signal) };
831 let signal = signal::Number::from_raw_unchecked(raw_number);
832 let process_result = ProcessResult::Signaled { signal, core_dump };
833 process_result.into()
834 } else if libc::WIFSTOPPED(status) {
835 let signal = libc::WSTOPSIG(status);
836 let raw_number = unsafe { NonZero::new_unchecked(signal) };
838 let signal = signal::Number::from_raw_unchecked(raw_number);
839 ProcessState::stopped(signal)
840 } else {
841 unreachable!()
842 };
843 Ok(Some((Pid(pid), state)))
844 }
845 }
846 }
847}
848
849impl Exec for RealSystem {
850 fn execve(
851 &self,
852 path: &CStr,
853 args: &[CString],
854 envs: &[CString],
855 ) -> FlexFuture<Result<Infallible>> {
856 fn to_pointer_array<S: AsRef<CStr>>(strs: &[S]) -> Vec<*const libc::c_char> {
857 strs.iter()
858 .map(|s| s.as_ref().as_ptr())
859 .chain(std::iter::once(std::ptr::null()))
860 .collect()
861 }
862 let args = to_pointer_array(args);
873 let envs = to_pointer_array(envs);
874 loop {
875 let _ = unsafe { libc::execve(path.as_ptr(), args.as_ptr(), envs.as_ptr()) };
876 let errno = Errno::last();
877 if errno != Errno::EINTR {
878 return Err(errno).into();
879 }
880 }
881 }
882}
883
884impl Exit for RealSystem {
885 fn exit(&self, exit_status: ExitStatus) -> FlexFuture<Infallible> {
886 unsafe { libc::_exit(exit_status.0) }
887 }
888}
889
890impl GetUid for RealSystem {
891 fn getuid(&self) -> Uid {
892 Uid(unsafe { libc::getuid() })
893 }
894
895 fn geteuid(&self) -> Uid {
896 Uid(unsafe { libc::geteuid() })
897 }
898
899 fn getgid(&self) -> Gid {
900 Gid(unsafe { libc::getgid() })
901 }
902
903 fn getegid(&self) -> Gid {
904 Gid(unsafe { libc::getegid() })
905 }
906}
907
908impl GetPw for RealSystem {
909 fn getpwnam_dir(&self, name: &CStr) -> Result<Option<PathBuf>> {
910 Errno::clear();
911 let passwd = unsafe { libc::getpwnam(name.as_ptr()) };
912 if passwd.is_null() {
913 let errno = Errno::last();
914 return if errno == Errno::NO_ERROR {
915 Ok(None)
916 } else {
917 Err(errno)
918 };
919 }
920
921 let dir = unsafe { CStr::from_ptr((*passwd).pw_dir) };
922 Ok(Some(UnixString::from_vec(dir.to_bytes().to_vec()).into()))
923 }
924}
925
926impl Sysconf for RealSystem {
927 fn confstr_path(&self) -> Result<UnixString> {
928 #[cfg(any(
930 target_os = "linux",
931 target_os = "macos",
932 target_os = "ios",
933 target_os = "tvos",
934 target_os = "watchos"
935 ))]
936 unsafe {
937 let size = libc::confstr(libc::_CS_PATH, std::ptr::null_mut(), 0);
938 if size == 0 {
939 return Err(Errno::last());
940 }
941 let mut buffer = Vec::<u8>::with_capacity(size);
942 let final_size = libc::confstr(libc::_CS_PATH, buffer.as_mut_ptr() as *mut _, size);
943 if final_size == 0 {
944 return Err(Errno::last());
945 }
946 if final_size > size {
947 return Err(Errno::ERANGE);
948 }
949 buffer.set_len(final_size - 1); return Ok(UnixString::from_vec(buffer));
951 }
952
953 #[allow(unreachable_code)]
954 Err(Errno::ENOSYS)
955 }
956}
957
958impl ShellPath for RealSystem {
959 fn shell_path(&self) -> CString {
965 #[cfg(any(target_os = "linux", target_os = "android"))]
966 if self.is_executable_file(c"/proc/self/exe") {
967 return c"/proc/self/exe".to_owned();
968 }
969 if let Ok(path) = self.confstr_path() {
973 if let Some(full_path) = path
974 .as_bytes()
975 .split(|b| *b == b':')
976 .map(|dir| Path::new(UnixStr::from_bytes(dir)).join("sh"))
977 .filter(|full_path| full_path.is_absolute())
978 .filter_map(|full_path| CString::new(full_path.into_unix_string().into_vec()).ok())
979 .find(|full_path| self.is_executable_file(full_path))
980 {
981 return full_path;
982 }
983 }
984
985 c"/bin/sh".to_owned()
987 }
988}
989
990impl GetRlimit for RealSystem {
991 fn getrlimit(&self, resource: Resource) -> Result<LimitPair> {
992 let raw_resource = resource.as_raw_type().ok_or(Errno::EINVAL)?;
993
994 let mut limits = MaybeUninit::<libc::rlimit>::uninit();
995 unsafe { libc::getrlimit(raw_resource as _, limits.as_mut_ptr()) }.errno_if_m1()?;
996
997 Ok(LimitPair {
1001 soft: unsafe { (*limits.as_ptr()).rlim_cur },
1002 hard: unsafe { (*limits.as_ptr()).rlim_max },
1003 })
1004 }
1005}
1006
1007impl SetRlimit for RealSystem {
1008 fn setrlimit(&self, resource: Resource, limits: LimitPair) -> Result<()> {
1009 let raw_resource = resource.as_raw_type().ok_or(Errno::EINVAL)?;
1010
1011 let mut rlimit = MaybeUninit::<libc::rlimit>::uninit();
1012 unsafe {
1013 (&raw mut (*rlimit.as_mut_ptr()).rlim_cur).write(limits.soft);
1014 (&raw mut (*rlimit.as_mut_ptr()).rlim_max).write(limits.hard);
1015 }
1016
1017 unsafe { libc::setrlimit(raw_resource as _, rlimit.as_ptr()) }.errno_if_m1()?;
1018 Ok(())
1019 }
1020}
1021
1022#[derive(Debug)]
1024struct RealDir(NonNull<DIR>);
1025
1026impl Drop for RealDir {
1027 fn drop(&mut self) {
1028 unsafe {
1029 libc::closedir(self.0.as_ptr());
1030 }
1031 }
1032}
1033
1034impl Dir for RealDir {
1035 fn next(&mut self) -> Result<Option<DirEntry<'_>>> {
1036 Errno::clear();
1037 let entry = unsafe { libc::readdir(self.0.as_ptr()) };
1038 let errno = Errno::last();
1039 if entry.is_null() {
1040 if errno == Errno::NO_ERROR {
1041 Ok(None)
1042 } else {
1043 Err(errno)
1044 }
1045 } else {
1046 let name = unsafe { CStr::from_ptr((&raw const (*entry).d_name).cast()) };
1048 let name = UnixStr::from_bytes(name.to_bytes());
1049 Ok(Some(DirEntry { name }))
1050 }
1051 }
1052}
1053
1054#[cfg(test)]
1055mod tests {
1056 use super::*;
1057
1058 #[test]
1059 fn real_system_directory_entries() {
1060 let system = unsafe { RealSystem::new() };
1061 let mut dir = system.opendir(c".").unwrap();
1062 let mut count = 0;
1063 while dir.next().unwrap().is_some() {
1064 count += 1;
1065 }
1066 assert!(count > 0);
1067 }
1068
1069 #[test]
1071 fn real_system_caught_signals() {
1072 unsafe {
1073 let system = RealSystem::new();
1074 let result = system.caught_signals();
1075 assert_eq!(result, []);
1076
1077 catch_signal(libc::SIGINT);
1078 catch_signal(libc::SIGTERM);
1079 catch_signal(libc::SIGTERM);
1080 catch_signal(libc::SIGCHLD);
1081
1082 let sigint = signal::Number::from_raw_unchecked(NonZero::new(libc::SIGINT).unwrap());
1083 let sigterm = signal::Number::from_raw_unchecked(NonZero::new(libc::SIGTERM).unwrap());
1084 let sigchld = signal::Number::from_raw_unchecked(NonZero::new(libc::SIGCHLD).unwrap());
1085
1086 let result = system.caught_signals();
1087 assert_eq!(result, [sigint, sigterm, sigchld]);
1088 let result = system.caught_signals();
1089 assert_eq!(result, []);
1090 }
1091 }
1092}