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