1mod errno;
25mod file_system;
26mod open_flag;
27mod resource;
28mod signal;
29
30use super::resource::LimitPair;
31use super::resource::Resource;
32use super::ChildProcessStarter;
33use super::Dir;
34use super::DirEntry;
35use super::Disposition;
36#[cfg(doc)]
37use super::Env;
38use super::Errno;
39use super::FdFlag;
40use super::Gid;
41use super::Mode;
42use super::OfdAccess;
43use super::OpenFlag;
44use super::Result;
45use super::SigmaskOp;
46use super::Stat;
47use super::System;
48use super::Times;
49use super::Uid;
50use crate::io::Fd;
51use crate::job::Pid;
52use crate::job::ProcessResult;
53use crate::job::ProcessState;
54use crate::path::Path;
55use crate::path::PathBuf;
56use crate::str::UnixStr;
57use crate::str::UnixString;
58use enumset::EnumSet;
59use nix::errno::Errno as NixErrno;
60use nix::fcntl::AtFlags;
61use nix::libc::DIR;
62use nix::libc::{S_IFDIR, S_IFMT, S_IFREG};
63use nix::sys::stat::stat;
64use nix::unistd::AccessFlags;
65use std::convert::Infallible;
66use std::convert::TryInto;
67use std::ffi::c_int;
68use std::ffi::CStr;
69use std::ffi::CString;
70use std::ffi::OsStr;
71use std::future::Future;
72use std::io::SeekFrom;
73use std::mem::MaybeUninit;
74use std::num::NonZeroI32;
75use std::os::unix::ffi::OsStrExt as _;
76use std::os::unix::ffi::OsStringExt as _;
77use std::os::unix::io::IntoRawFd;
78use std::pin::Pin;
79use std::ptr::NonNull;
80use std::sync::atomic::compiler_fence;
81use std::sync::atomic::AtomicIsize;
82use std::sync::atomic::Ordering;
83use std::time::Duration;
84use std::time::Instant;
85use yash_executor::Executor;
86
87trait ErrnoIfM1: PartialEq + Sized {
88 const MINUS_1: Self;
89
90 fn errno_if_m1(self) -> Result<Self> {
98 if self == Self::MINUS_1 {
99 Err(Errno::last())
100 } else {
101 Ok(self)
102 }
103 }
104}
105
106impl ErrnoIfM1 for i8 {
107 const MINUS_1: Self = -1;
108}
109impl ErrnoIfM1 for i16 {
110 const MINUS_1: Self = -1;
111}
112impl ErrnoIfM1 for i32 {
113 const MINUS_1: Self = -1;
114}
115impl ErrnoIfM1 for i64 {
116 const MINUS_1: Self = -1;
117}
118impl ErrnoIfM1 for isize {
119 const MINUS_1: Self = -1;
120}
121
122impl Pid {
123 #[inline(always)]
124 const fn to_nix(self) -> nix::unistd::Pid {
125 nix::unistd::Pid::from_raw(self.0)
126 }
127 #[inline(always)]
128 const fn from_nix(pid: nix::unistd::Pid) -> Self {
129 Pid(pid.as_raw())
130 }
131}
132
133#[cfg(not(target_os = "redox"))]
135fn is_executable(path: &CStr) -> bool {
136 nix::unistd::faccessat(None, path, AccessFlags::X_OK, AtFlags::AT_EACCESS).is_ok()
137}
138#[cfg(target_os = "redox")]
139fn is_executable(path: &CStr) -> bool {
140 nix::unistd::access(path, AccessFlags::X_OK).is_ok()
141}
142
143fn is_regular_file(path: &CStr) -> bool {
144 matches!(stat(path), Ok(stat) if stat.st_mode & S_IFMT == S_IFREG)
145}
146
147fn is_directory(path: &CStr) -> bool {
148 matches!(stat(path), Ok(stat) if stat.st_mode & S_IFMT == S_IFDIR)
149}
150
151#[must_use]
156fn to_timespec(duration: Duration) -> MaybeUninit<nix::libc::timespec> {
157 let seconds = duration
158 .as_secs()
159 .try_into()
160 .unwrap_or(nix::libc::time_t::MAX);
161 let mut timespec = MaybeUninit::<nix::libc::timespec>::uninit();
162 unsafe {
163 (&raw mut (*timespec.as_mut_ptr()).tv_sec).write(seconds);
164 (&raw mut (*timespec.as_mut_ptr()).tv_nsec).write(duration.subsec_nanos() as _);
165 }
166 timespec
167}
168
169static CAUGHT_SIGNALS: [AtomicIsize; 8] = [const { AtomicIsize::new(0) }; 8];
175
176extern "C" fn catch_signal(signal: c_int) {
182 let signal = signal as isize;
190 for slot in &CAUGHT_SIGNALS {
191 match slot.compare_exchange(0, signal, Ordering::Relaxed, Ordering::Relaxed) {
192 Ok(_) => break,
193 Err(slot_value) if slot_value == signal => break,
194 _ => continue,
195 }
196 }
197}
198
199#[derive(Debug)]
204pub struct RealSystem(());
205
206impl RealSystem {
207 pub unsafe fn new() -> Self {
218 RealSystem(())
219 }
220}
221
222impl System for RealSystem {
223 fn fstat(&self, fd: Fd) -> Result<Stat> {
224 let mut stat = MaybeUninit::<nix::libc::stat>::uninit();
225 unsafe { nix::libc::fstat(fd.0, stat.as_mut_ptr()) }.errno_if_m1()?;
226 Ok(Stat::from_raw(&stat))
227 }
228
229 fn fstatat(&self, dir_fd: Fd, path: &CStr, follow_symlinks: bool) -> Result<Stat> {
230 let flags = if follow_symlinks {
231 0
232 } else {
233 nix::libc::AT_SYMLINK_NOFOLLOW
234 };
235
236 let mut stat = MaybeUninit::<nix::libc::stat>::uninit();
237 unsafe { nix::libc::fstatat(dir_fd.0, path.as_ptr(), stat.as_mut_ptr(), flags) }
238 .errno_if_m1()?;
239 Ok(Stat::from_raw(&stat))
240 }
241
242 fn is_executable_file(&self, path: &CStr) -> bool {
243 is_regular_file(path) && is_executable(path)
244 }
245
246 fn is_directory(&self, path: &CStr) -> bool {
247 is_directory(path)
248 }
249
250 fn pipe(&mut self) -> Result<(Fd, Fd)> {
251 let mut fds = MaybeUninit::<[c_int; 2]>::uninit();
252 unsafe { nix::libc::pipe(fds.as_mut_ptr().cast()) }.errno_if_m1()?;
254 let fds = unsafe { fds.assume_init() };
255 Ok((Fd(fds[0]), Fd(fds[1])))
256 }
257
258 fn dup(&mut self, from: Fd, to_min: Fd, flags: EnumSet<FdFlag>) -> Result<Fd> {
259 let command = if flags.contains(FdFlag::CloseOnExec) {
260 nix::libc::F_DUPFD_CLOEXEC
261 } else {
262 nix::libc::F_DUPFD
263 };
264 unsafe { nix::libc::fcntl(from.0, command, to_min.0) }
265 .errno_if_m1()
266 .map(Fd)
267 }
268
269 fn dup2(&mut self, from: Fd, to: Fd) -> Result<Fd> {
270 loop {
271 let result = unsafe { nix::libc::dup2(from.0, to.0) }
272 .errno_if_m1()
273 .map(Fd);
274 if result != Err(Errno::EINTR) {
275 return result;
276 }
277 }
278 }
279
280 fn open(
281 &mut self,
282 path: &CStr,
283 access: OfdAccess,
284 flags: EnumSet<OpenFlag>,
285 mode: Mode,
286 ) -> Result<Fd> {
287 let mut raw_flags = access.to_real_flags().ok_or(Errno::EINVAL)?;
288 for flag in flags {
289 raw_flags |= flag.to_real_flags().ok_or(Errno::EINVAL)?;
290 }
291
292 #[cfg(not(target_os = "redox"))]
293 let mode_bits = mode.bits() as std::ffi::c_uint;
294 #[cfg(target_os = "redox")]
295 let mode_bits = mode.bits() as c_int;
296
297 unsafe { nix::libc::open(path.as_ptr(), raw_flags, mode_bits) }
298 .errno_if_m1()
299 .map(Fd)
300 }
301
302 fn open_tmpfile(&mut self, parent_dir: &Path) -> Result<Fd> {
303 let parent_dir = OsStr::from_bytes(parent_dir.as_unix_str().as_bytes());
304 let file = tempfile::tempfile_in(parent_dir)
305 .map_err(|errno| Errno(errno.raw_os_error().unwrap_or(0)))?;
306 let fd = Fd(file.into_raw_fd());
307
308 _ = self.fcntl_setfd(fd, EnumSet::empty());
310
311 Ok(fd)
312 }
313
314 fn close(&mut self, fd: Fd) -> Result<()> {
315 loop {
316 let result = unsafe { nix::libc::close(fd.0) }.errno_if_m1().map(drop);
317 match result {
318 Err(Errno::EBADF) => return Ok(()),
319 Err(Errno::EINTR) => continue,
320 other => return other,
321 }
322 }
323 }
324
325 fn ofd_access(&self, fd: Fd) -> Result<OfdAccess> {
326 let flags = unsafe { nix::libc::fcntl(fd.0, nix::libc::F_GETFL) }.errno_if_m1()?;
327 Ok(OfdAccess::from_real_flags(flags))
328 }
329
330 fn get_and_set_nonblocking(&mut self, fd: Fd, nonblocking: bool) -> Result<bool> {
331 let old_flags = unsafe { nix::libc::fcntl(fd.0, nix::libc::F_GETFL) }.errno_if_m1()?;
332 let new_flags = if nonblocking {
333 old_flags | nix::libc::O_NONBLOCK
334 } else {
335 old_flags & !nix::libc::O_NONBLOCK
336 };
337 if new_flags != old_flags {
338 unsafe { nix::libc::fcntl(fd.0, nix::libc::F_SETFL, new_flags) }.errno_if_m1()?;
339 }
340 let was_nonblocking = old_flags & nix::libc::O_NONBLOCK != 0;
341 Ok(was_nonblocking)
342 }
343
344 fn fcntl_getfd(&self, fd: Fd) -> Result<EnumSet<FdFlag>> {
345 let bits = unsafe { nix::libc::fcntl(fd.0, nix::libc::F_GETFD) }.errno_if_m1()?;
346 let mut flags = EnumSet::empty();
347 if bits & nix::libc::FD_CLOEXEC != 0 {
348 flags.insert(FdFlag::CloseOnExec);
349 }
350 Ok(flags)
351 }
352
353 fn fcntl_setfd(&mut self, fd: Fd, flags: EnumSet<FdFlag>) -> Result<()> {
354 let mut bits = 0 as c_int;
355 if flags.contains(FdFlag::CloseOnExec) {
356 bits |= nix::libc::FD_CLOEXEC;
357 }
358 unsafe { nix::libc::fcntl(fd.0, nix::libc::F_SETFD, bits) }
359 .errno_if_m1()
360 .map(drop)
361 }
362
363 fn isatty(&self, fd: Fd) -> bool {
364 (unsafe { nix::libc::isatty(fd.0) } != 0)
365 }
366
367 fn read(&mut self, fd: Fd, buffer: &mut [u8]) -> Result<usize> {
368 loop {
369 let result = unsafe { nix::libc::read(fd.0, buffer.as_mut_ptr().cast(), buffer.len()) }
370 .errno_if_m1();
371 if result != Err(Errno::EINTR) {
372 return Ok(result?.try_into().unwrap());
373 }
374 }
375 }
376
377 fn write(&mut self, fd: Fd, buffer: &[u8]) -> Result<usize> {
378 loop {
379 let result = unsafe { nix::libc::write(fd.0, buffer.as_ptr().cast(), buffer.len()) }
380 .errno_if_m1();
381 if result != Err(Errno::EINTR) {
382 return Ok(result?.try_into().unwrap());
383 }
384 }
385 }
386
387 fn lseek(&mut self, fd: Fd, position: SeekFrom) -> Result<u64> {
388 let (offset, whence) = match position {
389 SeekFrom::Start(offset) => {
390 let offset = offset.try_into().map_err(|_| Errno::EOVERFLOW)?;
391 (offset, nix::libc::SEEK_SET)
392 }
393 SeekFrom::End(offset) => (offset, nix::libc::SEEK_END),
394 SeekFrom::Current(offset) => (offset, nix::libc::SEEK_CUR),
395 };
396 let new_offset = unsafe { nix::libc::lseek(fd.0, offset, whence) }.errno_if_m1()?;
397 Ok(new_offset.try_into().unwrap())
398 }
399
400 fn fdopendir(&mut self, fd: Fd) -> Result<Box<dyn Dir>> {
401 let dir = unsafe { nix::libc::fdopendir(fd.0) };
402 let dir = NonNull::new(dir).ok_or_else(NixErrno::last)?;
403 Ok(Box::new(RealDir(dir)))
404 }
405
406 fn opendir(&mut self, path: &CStr) -> Result<Box<dyn Dir>> {
407 let dir = unsafe { nix::libc::opendir(path.as_ptr()) };
408 let dir = NonNull::new(dir).ok_or_else(NixErrno::last)?;
409 Ok(Box::new(RealDir(dir)))
410 }
411
412 fn umask(&mut self, new_mask: Mode) -> Mode {
413 Mode::from_bits_retain(unsafe { nix::libc::umask(new_mask.bits()) })
414 }
415
416 fn now(&self) -> Instant {
417 Instant::now()
418 }
419
420 fn times(&self) -> Result<Times> {
421 let mut tms = MaybeUninit::<nix::libc::tms>::uninit();
422 let raw_result = unsafe { nix::libc::times(tms.as_mut_ptr()) };
423 if raw_result == (-1) as _ {
424 return Err(Errno::last());
425 }
426
427 let ticks_per_second = unsafe { nix::libc::sysconf(nix::libc::_SC_CLK_TCK) };
428 if ticks_per_second <= 0 {
429 return Err(Errno::last());
430 }
431
432 let utime = unsafe { (&raw const (*tms.as_ptr()).tms_utime).read() };
436 let stime = unsafe { (&raw const (*tms.as_ptr()).tms_stime).read() };
437 let cutime = unsafe { (&raw const (*tms.as_ptr()).tms_cutime).read() };
438 let cstime = unsafe { (&raw const (*tms.as_ptr()).tms_cstime).read() };
439
440 Ok(Times {
441 self_user: utime as f64 / ticks_per_second as f64,
442 self_system: stime as f64 / ticks_per_second as f64,
443 children_user: cutime as f64 / ticks_per_second as f64,
444 children_system: cstime as f64 / ticks_per_second as f64,
445 })
446 }
447
448 fn validate_signal(&self, number: signal::RawNumber) -> Option<(signal::Name, signal::Number)> {
449 let non_zero = NonZeroI32::new(number)?;
450 let name = signal::Name::try_from_raw_real(number)?;
451 Some((name, signal::Number::from_raw_unchecked(non_zero)))
452 }
453
454 #[inline(always)]
455 fn signal_number_from_name(&self, name: signal::Name) -> Option<signal::Number> {
456 name.to_raw_real()
457 }
458
459 fn sigmask(
460 &mut self,
461 op: Option<(SigmaskOp, &[signal::Number])>,
462 old_mask: Option<&mut Vec<signal::Number>>,
463 ) -> Result<()> {
464 unsafe {
465 let (how, raw_mask) = match op {
466 None => (nix::libc::SIG_BLOCK, None),
467 Some((op, mask)) => {
468 let how = match op {
469 SigmaskOp::Add => nix::libc::SIG_BLOCK,
470 SigmaskOp::Remove => nix::libc::SIG_UNBLOCK,
471 SigmaskOp::Set => nix::libc::SIG_SETMASK,
472 };
473
474 let mut raw_mask = MaybeUninit::<nix::libc::sigset_t>::uninit();
475 nix::libc::sigemptyset(raw_mask.as_mut_ptr()).errno_if_m1()?;
476 for &signal in mask {
477 nix::libc::sigaddset(raw_mask.as_mut_ptr(), signal.as_raw())
478 .errno_if_m1()?;
479 }
480
481 (how, Some(raw_mask))
482 }
483 };
484 let mut old_mask_pair = match old_mask {
485 None => None,
486 Some(old_mask) => {
487 let mut raw_old_mask = MaybeUninit::<nix::libc::sigset_t>::uninit();
488 nix::libc::sigemptyset(raw_old_mask.as_mut_ptr()).errno_if_m1()?;
490 Some((old_mask, raw_old_mask))
491 }
492 };
493
494 let raw_set_ptr = raw_mask
495 .as_ref()
496 .map_or(std::ptr::null(), |raw_set| raw_set.as_ptr());
497 let raw_old_set_ptr = old_mask_pair
498 .as_mut()
499 .map_or(std::ptr::null_mut(), |(_, raw_old_mask)| {
500 raw_old_mask.as_mut_ptr()
501 });
502 let result = nix::libc::sigprocmask(how, raw_set_ptr, raw_old_set_ptr);
503 result.errno_if_m1().map(drop)?;
504
505 if let Some((old_mask, raw_old_mask)) = old_mask_pair {
506 old_mask.clear();
507 signal::sigset_to_vec(raw_old_mask.as_ptr(), old_mask);
508 }
509
510 Ok(())
511 }
512 }
513
514 fn sigaction(&mut self, signal: signal::Number, handling: Disposition) -> Result<Disposition> {
515 unsafe {
516 let new_action = handling.to_sigaction();
517
518 let mut old_action = MaybeUninit::<nix::libc::sigaction>::uninit();
519 let old_mask_ptr = &raw mut (*old_action.as_mut_ptr()).sa_mask;
520 nix::libc::sigemptyset(old_mask_ptr).errno_if_m1()?;
522
523 nix::libc::sigaction(
524 signal.as_raw(),
525 new_action.as_ptr(),
526 old_action.as_mut_ptr(),
527 )
528 .errno_if_m1()?;
529
530 let old_handling = Disposition::from_sigaction(&old_action);
531 Ok(old_handling)
532 }
533 }
534
535 fn caught_signals(&mut self) -> Vec<signal::Number> {
536 let mut signals = Vec::new();
537 for slot in &CAUGHT_SIGNALS {
538 compiler_fence(Ordering::Acquire);
540
541 let signal = slot.swap(0, Ordering::Relaxed);
542 if signal == 0 {
543 break;
546 }
547
548 if let Some((_name, number)) = self.validate_signal(signal as signal::RawNumber) {
549 signals.push(number)
550 } else {
551 }
553 }
554 signals
555 }
556
557 fn kill(
558 &mut self,
559 target: Pid,
560 signal: Option<signal::Number>,
561 ) -> Pin<Box<(dyn Future<Output = Result<()>>)>> {
562 Box::pin(async move {
563 let raw = signal.map_or(0, signal::Number::as_raw);
564 unsafe { nix::libc::kill(target.0, raw) }.errno_if_m1()?;
565 Ok(())
566 })
567 }
568
569 fn select(
570 &mut self,
571 readers: &mut Vec<Fd>,
572 writers: &mut Vec<Fd>,
573 timeout: Option<Duration>,
574 signal_mask: Option<&[signal::Number]>,
575 ) -> Result<c_int> {
576 use std::ptr::{null, null_mut};
577
578 let max_fd = readers.iter().chain(writers.iter()).max();
579 let nfds = max_fd
580 .map(|fd| fd.0.checked_add(1).ok_or(Errno::EBADF))
581 .transpose()?
582 .unwrap_or(0);
583
584 fn to_raw_fd_set(fds: &[Fd]) -> MaybeUninit<nix::libc::fd_set> {
585 let mut raw_fds = MaybeUninit::<nix::libc::fd_set>::uninit();
586 unsafe {
587 nix::libc::FD_ZERO(raw_fds.as_mut_ptr());
588 for fd in fds {
589 nix::libc::FD_SET(fd.0, raw_fds.as_mut_ptr());
590 }
591 }
592 raw_fds
593 }
594 let mut raw_readers = to_raw_fd_set(readers);
595 let mut raw_writers = to_raw_fd_set(writers);
596 let readers_ptr = raw_readers.as_mut_ptr();
597 let writers_ptr = raw_writers.as_mut_ptr();
598 let errors = null_mut();
599
600 let timeout_spec = to_timespec(timeout.unwrap_or_default());
601 let timeout_ptr = if timeout.is_some() {
602 timeout_spec.as_ptr()
603 } else {
604 null()
605 };
606
607 let mut raw_mask = MaybeUninit::<nix::libc::sigset_t>::uninit();
608 let raw_mask_ptr = match signal_mask {
609 None => null(),
610 Some(signal_mask) => {
611 unsafe { nix::libc::sigemptyset(raw_mask.as_mut_ptr()) }.errno_if_m1()?;
612 for &signal in signal_mask {
613 unsafe { nix::libc::sigaddset(raw_mask.as_mut_ptr(), signal.as_raw()) }
614 .errno_if_m1()?;
615 }
616 raw_mask.as_ptr()
617 }
618 };
619
620 let count = unsafe {
621 nix::libc::pselect(
622 nfds,
623 readers_ptr,
624 writers_ptr,
625 errors,
626 timeout_ptr,
627 raw_mask_ptr,
628 )
629 }
630 .errno_if_m1()?;
631
632 readers.retain(|fd| unsafe { nix::libc::FD_ISSET(fd.0, readers_ptr) });
633 writers.retain(|fd| unsafe { nix::libc::FD_ISSET(fd.0, writers_ptr) });
634
635 Ok(count)
636 }
637
638 fn getpid(&self) -> Pid {
639 Pid(unsafe { nix::libc::getpid() })
640 }
641
642 fn getppid(&self) -> Pid {
643 Pid(unsafe { nix::libc::getppid() })
644 }
645
646 fn getpgrp(&self) -> Pid {
647 Pid(unsafe { nix::libc::getpgrp() })
648 }
649
650 fn setpgid(&mut self, pid: Pid, pgid: Pid) -> Result<()> {
651 let result = unsafe { nix::libc::setpgid(pid.0, pgid.0) };
652 result.errno_if_m1().map(drop)
653 }
654
655 fn tcgetpgrp(&self, fd: Fd) -> Result<Pid> {
656 unsafe { nix::libc::tcgetpgrp(fd.0) }.errno_if_m1().map(Pid)
657 }
658
659 fn tcsetpgrp(&mut self, fd: Fd, pgid: Pid) -> Result<()> {
660 let result = unsafe { nix::libc::tcsetpgrp(fd.0, pgid.0) };
661 result.errno_if_m1().map(drop)
662 }
663
664 fn new_child_process(&mut self) -> Result<ChildProcessStarter> {
672 let raw_pid = unsafe { nix::libc::fork() }.errno_if_m1()?;
673 if raw_pid != 0 {
674 return Ok(Box::new(move |_env, _task| Pid(raw_pid)));
676 }
677
678 Ok(Box::new(|env, task| {
680 let system = env.system.clone();
681 let executor = Executor::new();
685 let task = Box::pin(async move {
686 task(env).await;
687 std::process::exit(env.exit_status.0)
688 });
689 unsafe { executor.spawn_pinned(task) }
692 loop {
693 executor.run_until_stalled();
694 system.select(false).ok();
695 }
696 }))
697 }
698
699 fn wait(&mut self, target: Pid) -> Result<Option<(Pid, ProcessState)>> {
700 use nix::sys::wait::{WaitPidFlag, WaitStatus::*};
701 let options = WaitPidFlag::WUNTRACED | WaitPidFlag::WCONTINUED | WaitPidFlag::WNOHANG;
702 let status = nix::sys::wait::waitpid(Some(target.to_nix()), options.into())?;
703 match status {
704 StillAlive => Ok(None),
705 Continued(pid) => Ok(Some((Pid::from_nix(pid), ProcessState::Running))),
706 Exited(pid, exit_status) => Ok(Some((
707 Pid::from_nix(pid),
708 ProcessState::exited(exit_status),
709 ))),
710 Signaled(pid, signal, core_dump) => {
711 let raw_number = unsafe { NonZeroI32::new_unchecked(signal as _) };
713 let signal = signal::Number::from_raw_unchecked(raw_number);
714 let process_result = ProcessResult::Signaled { signal, core_dump };
715 Ok(Some((Pid::from_nix(pid), process_result.into())))
716 }
717 Stopped(pid, signal) => {
718 let raw_number = unsafe { NonZeroI32::new_unchecked(signal as _) };
720 let signal = signal::Number::from_raw_unchecked(raw_number);
721 Ok(Some((Pid::from_nix(pid), ProcessState::stopped(signal))))
722 }
723 #[allow(unreachable_patterns)]
724 _ => unreachable!(),
725 }
726 }
727
728 fn execve(&mut self, path: &CStr, args: &[CString], envs: &[CString]) -> Result<Infallible> {
729 loop {
730 let result = nix::unistd::execve(path, args, envs);
732 if result != Err(NixErrno::EINTR) {
733 return Ok(result?);
734 }
735 }
736 }
737
738 fn getcwd(&self) -> Result<PathBuf> {
739 let path = nix::unistd::getcwd()?;
740 let raw = path.into_os_string().into_vec();
741 Ok(PathBuf::from(UnixString::from_vec(raw)))
742 }
743
744 fn chdir(&mut self, path: &CStr) -> Result<()> {
745 nix::unistd::chdir(path)?;
746 Ok(())
747 }
748
749 fn getuid(&self) -> Uid {
750 Uid(unsafe { nix::libc::getuid() })
751 }
752
753 fn geteuid(&self) -> Uid {
754 Uid(unsafe { nix::libc::geteuid() })
755 }
756
757 fn getgid(&self) -> Gid {
758 Gid(unsafe { nix::libc::getgid() })
759 }
760
761 fn getegid(&self) -> Gid {
762 Gid(unsafe { nix::libc::getegid() })
763 }
764
765 fn getpwnam_dir(&self, name: &str) -> Result<Option<PathBuf>> {
766 let user = nix::unistd::User::from_name(name)?;
767 Ok(user.map(|user| {
768 let dir = user.dir.into_os_string().into_vec();
769 PathBuf::from(UnixString::from_vec(dir))
770 }))
771 }
772
773 fn confstr_path(&self) -> Result<UnixString> {
774 #[cfg(any(
776 target_os = "macos",
777 target_os = "ios",
778 target_os = "tvos",
779 target_os = "watchos"
780 ))]
781 unsafe {
782 let size = nix::libc::confstr(nix::libc::_CS_PATH, std::ptr::null_mut(), 0);
783 if size == 0 {
784 return Err(Errno::last());
785 }
786 let mut buffer = Vec::<u8>::with_capacity(size);
787 let final_size =
788 nix::libc::confstr(nix::libc::_CS_PATH, buffer.as_mut_ptr() as *mut _, size);
789 if final_size == 0 {
790 return Err(Errno::last());
791 }
792 if final_size > size {
793 return Err(Errno::ERANGE);
794 }
795 buffer.set_len(final_size - 1); return Ok(UnixString::from_vec(buffer));
797 }
798
799 #[allow(unreachable_code)]
800 Err(Errno::ENOSYS)
801 }
802
803 fn shell_path(&self) -> CString {
809 #[cfg(any(target_os = "linux", target_os = "android"))]
810 if self.is_executable_file(c"/proc/self/exe") {
811 return c"/proc/self/exe".to_owned();
812 }
813 if let Ok(path) = self.confstr_path() {
817 if let Some(full_path) = path
818 .as_bytes()
819 .split(|b| *b == b':')
820 .map(|dir| Path::new(UnixStr::from_bytes(dir)).join("sh"))
821 .filter(|full_path| full_path.is_absolute())
822 .filter_map(|full_path| CString::new(full_path.into_unix_string().into_vec()).ok())
823 .find(|full_path| self.is_executable_file(full_path))
824 {
825 return full_path;
826 }
827 }
828
829 c"/bin/sh".to_owned()
831 }
832
833 fn getrlimit(&self, resource: Resource) -> Result<LimitPair> {
834 let raw_resource = resource.as_raw_type().ok_or(Errno::EINVAL)?;
835
836 let mut limits = MaybeUninit::<nix::libc::rlimit>::uninit();
837 unsafe { nix::libc::getrlimit(raw_resource as _, limits.as_mut_ptr()) }.errno_if_m1()?;
838 Ok(LimitPair {
839 soft: unsafe { (&raw const (*limits.as_ptr()).rlim_cur).read() },
840 hard: unsafe { (&raw const (*limits.as_ptr()).rlim_max).read() },
841 })
842 }
843
844 fn setrlimit(&mut self, resource: Resource, limits: LimitPair) -> Result<()> {
845 let raw_resource = resource.as_raw_type().ok_or(Errno::EINVAL)?;
846
847 let mut rlimit = MaybeUninit::<nix::libc::rlimit>::uninit();
848 unsafe {
849 (&raw mut (*rlimit.as_mut_ptr()).rlim_cur).write(limits.soft);
850 (&raw mut (*rlimit.as_mut_ptr()).rlim_max).write(limits.hard);
851 }
852
853 unsafe { nix::libc::setrlimit(raw_resource as _, rlimit.as_ptr()) }.errno_if_m1()?;
854 Ok(())
855 }
856}
857
858#[derive(Debug)]
860struct RealDir(NonNull<DIR>);
861
862impl Drop for RealDir {
863 fn drop(&mut self) {
864 unsafe {
865 nix::libc::closedir(self.0.as_ptr());
866 }
867 }
868}
869
870impl Dir for RealDir {
871 fn next(&mut self) -> Result<Option<DirEntry>> {
872 Errno::clear();
873 let entry = unsafe { nix::libc::readdir(self.0.as_ptr()) };
874 let errno = Errno::last();
875 if entry.is_null() {
876 if errno == Errno::NO_ERROR {
877 Ok(None)
878 } else {
879 Err(errno)
880 }
881 } else {
882 let name = unsafe { CStr::from_ptr((&raw const (*entry).d_name).cast()) };
884 let name = UnixStr::from_bytes(name.to_bytes());
885 Ok(Some(DirEntry { name }))
886 }
887 }
888}
889
890#[cfg(test)]
891mod tests {
892 use super::*;
893
894 #[test]
895 fn real_system_directory_entries() {
896 let mut system = unsafe { RealSystem::new() };
897 let mut dir = system.opendir(c".").unwrap();
898 let mut count = 0;
899 while dir.next().unwrap().is_some() {
900 count += 1;
901 }
902 assert!(count > 0);
903 }
904
905 #[test]
907 fn real_system_caught_signals() {
908 unsafe {
909 let mut system = RealSystem::new();
910 let result = system.caught_signals();
911 assert_eq!(result, []);
912
913 catch_signal(nix::libc::SIGINT);
914 catch_signal(nix::libc::SIGTERM);
915 catch_signal(nix::libc::SIGTERM);
916 catch_signal(nix::libc::SIGCHLD);
917
918 let sigint =
919 signal::Number::from_raw_unchecked(NonZeroI32::new(nix::libc::SIGINT).unwrap());
920 let sigterm =
921 signal::Number::from_raw_unchecked(NonZeroI32::new(nix::libc::SIGTERM).unwrap());
922 let sigchld =
923 signal::Number::from_raw_unchecked(NonZeroI32::new(nix::libc::SIGCHLD).unwrap());
924
925 let result = system.caught_signals();
926 assert_eq!(result, [sigint, sigterm, sigchld]);
927 let result = system.caught_signals();
928 assert_eq!(result, []);
929 }
930 }
931}