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> {
427 let mut usage = MaybeUninit::<libc::rusage>::uninit();
428
429 unsafe { libc::getrusage(libc::RUSAGE_SELF, usage.as_mut_ptr()) }.errno_if_m1()?;
430 let self_user = unsafe {
431 (*usage.as_ptr()).ru_utime.tv_sec as f64
432 + (*usage.as_ptr()).ru_utime.tv_usec as f64 * 1e-6
433 };
434 let self_system = unsafe {
435 (*usage.as_ptr()).ru_stime.tv_sec as f64
436 + (*usage.as_ptr()).ru_stime.tv_usec as f64 * 1e-6
437 };
438
439 unsafe { libc::getrusage(libc::RUSAGE_CHILDREN, usage.as_mut_ptr()) }.errno_if_m1()?;
440 let children_user = unsafe {
441 (*usage.as_ptr()).ru_utime.tv_sec as f64
442 + (*usage.as_ptr()).ru_utime.tv_usec as f64 * 1e-6
443 };
444 let children_system = unsafe {
445 (*usage.as_ptr()).ru_stime.tv_sec as f64
446 + (*usage.as_ptr()).ru_stime.tv_usec as f64 * 1e-6
447 };
448
449 Ok(Times {
450 self_user,
451 self_system,
452 children_user,
453 children_system,
454 })
455 }
456
457 fn validate_signal(&self, number: signal::RawNumber) -> Option<(signal::Name, signal::Number)> {
458 let non_zero = NonZero::new(number)?;
459 let name = signal::Name::try_from_raw_real(number)?;
460 Some((name, signal::Number::from_raw_unchecked(non_zero)))
461 }
462
463 #[inline(always)]
464 fn signal_number_from_name(&self, name: signal::Name) -> Option<signal::Number> {
465 name.to_raw_real()
466 }
467
468 fn sigmask(
469 &mut self,
470 op: Option<(SigmaskOp, &[signal::Number])>,
471 old_mask: Option<&mut Vec<signal::Number>>,
472 ) -> Result<()> {
473 unsafe {
474 let (how, raw_mask) = match op {
475 None => (libc::SIG_BLOCK, None),
476 Some((op, mask)) => {
477 let how = match op {
478 SigmaskOp::Add => libc::SIG_BLOCK,
479 SigmaskOp::Remove => libc::SIG_UNBLOCK,
480 SigmaskOp::Set => libc::SIG_SETMASK,
481 };
482
483 let mut raw_mask = MaybeUninit::<libc::sigset_t>::uninit();
484 libc::sigemptyset(raw_mask.as_mut_ptr()).errno_if_m1()?;
485 for &signal in mask {
486 libc::sigaddset(raw_mask.as_mut_ptr(), signal.as_raw()).errno_if_m1()?;
487 }
488
489 (how, Some(raw_mask))
490 }
491 };
492 let mut old_mask_pair = match old_mask {
493 None => None,
494 Some(old_mask) => {
495 let mut raw_old_mask = MaybeUninit::<libc::sigset_t>::uninit();
496 libc::sigemptyset(raw_old_mask.as_mut_ptr()).errno_if_m1()?;
498 Some((old_mask, raw_old_mask))
499 }
500 };
501
502 let raw_set_ptr = raw_mask
503 .as_ref()
504 .map_or(std::ptr::null(), |raw_set| raw_set.as_ptr());
505 let raw_old_set_ptr = old_mask_pair
506 .as_mut()
507 .map_or(std::ptr::null_mut(), |(_, raw_old_mask)| {
508 raw_old_mask.as_mut_ptr()
509 });
510 let result = libc::sigprocmask(how, raw_set_ptr, raw_old_set_ptr);
511 result.errno_if_m1().map(drop)?;
512
513 if let Some((old_mask, raw_old_mask)) = old_mask_pair {
514 old_mask.clear();
515 signal::sigset_to_vec(raw_old_mask.as_ptr(), old_mask);
516 }
517
518 Ok(())
519 }
520 }
521
522 fn get_sigaction(&self, signal: signal::Number) -> Result<Disposition> {
523 sigaction_impl(signal, None)
524 }
525
526 fn sigaction(&mut self, signal: signal::Number, handling: Disposition) -> Result<Disposition> {
527 sigaction_impl(signal, Some(handling))
528 }
529
530 fn caught_signals(&mut self) -> Vec<signal::Number> {
531 let mut signals = Vec::new();
532 for slot in &CAUGHT_SIGNALS {
533 compiler_fence(Ordering::Acquire);
535
536 let signal = slot.swap(0, Ordering::Relaxed);
537 if signal == 0 {
538 break;
541 }
542
543 if let Some((_name, number)) = self.validate_signal(signal as signal::RawNumber) {
544 signals.push(number)
545 } else {
546 }
548 }
549 signals
550 }
551
552 fn kill(&mut self, target: Pid, signal: Option<signal::Number>) -> FlexFuture<Result<()>> {
553 let raw = signal.map_or(0, signal::Number::as_raw);
554 let result = unsafe { libc::kill(target.0, raw) }.errno_if_m1().map(drop);
555 result.into()
556 }
557
558 fn raise(&mut self, signal: signal::Number) -> FlexFuture<Result<()>> {
559 let raw = signal.as_raw();
560 unsafe { libc::raise(raw) }.errno_if_m1().map(drop).into()
561 }
562
563 fn select(
564 &mut self,
565 readers: &mut Vec<Fd>,
566 writers: &mut Vec<Fd>,
567 timeout: Option<Duration>,
568 signal_mask: Option<&[signal::Number]>,
569 ) -> Result<c_int> {
570 use std::ptr::{null, null_mut};
571
572 let max_fd = readers.iter().chain(writers.iter()).max();
573 let nfds = max_fd
574 .map(|fd| fd.0.checked_add(1).ok_or(Errno::EBADF))
575 .transpose()?
576 .unwrap_or(0);
577
578 fn to_raw_fd_set(fds: &[Fd]) -> MaybeUninit<libc::fd_set> {
579 let mut raw_fds = MaybeUninit::<libc::fd_set>::uninit();
580 unsafe {
581 libc::FD_ZERO(raw_fds.as_mut_ptr());
582 for fd in fds {
583 libc::FD_SET(fd.0, raw_fds.as_mut_ptr());
584 }
585 }
586 raw_fds
587 }
588 let mut raw_readers = to_raw_fd_set(readers);
589 let mut raw_writers = to_raw_fd_set(writers);
590 let readers_ptr = raw_readers.as_mut_ptr();
591 let writers_ptr = raw_writers.as_mut_ptr();
592 let errors = null_mut();
593
594 let timeout_spec = to_timespec(timeout.unwrap_or_default());
595 let timeout_ptr = if timeout.is_some() {
596 timeout_spec.as_ptr()
597 } else {
598 null()
599 };
600
601 let mut raw_mask = MaybeUninit::<libc::sigset_t>::uninit();
602 let raw_mask_ptr = match signal_mask {
603 None => null(),
604 Some(signal_mask) => {
605 unsafe { libc::sigemptyset(raw_mask.as_mut_ptr()) }.errno_if_m1()?;
606 for &signal in signal_mask {
607 unsafe { libc::sigaddset(raw_mask.as_mut_ptr(), signal.as_raw()) }
608 .errno_if_m1()?;
609 }
610 raw_mask.as_ptr()
611 }
612 };
613
614 let count = unsafe {
615 libc::pselect(
616 nfds,
617 readers_ptr,
618 writers_ptr,
619 errors,
620 timeout_ptr,
621 raw_mask_ptr,
622 )
623 }
624 .errno_if_m1()?;
625
626 readers.retain(|fd| unsafe { libc::FD_ISSET(fd.0, readers_ptr) });
627 writers.retain(|fd| unsafe { libc::FD_ISSET(fd.0, writers_ptr) });
628
629 Ok(count)
630 }
631
632 fn getsid(&self, pid: Pid) -> Result<Pid> {
633 unsafe { libc::getsid(pid.0) }.errno_if_m1().map(Pid)
634 }
635
636 fn getpid(&self) -> Pid {
637 Pid(unsafe { libc::getpid() })
638 }
639
640 fn getppid(&self) -> Pid {
641 Pid(unsafe { libc::getppid() })
642 }
643
644 fn getpgrp(&self) -> Pid {
645 Pid(unsafe { libc::getpgrp() })
646 }
647
648 fn setpgid(&mut self, pid: Pid, pgid: Pid) -> Result<()> {
649 let result = unsafe { libc::setpgid(pid.0, pgid.0) };
650 result.errno_if_m1().map(drop)
651 }
652
653 fn tcgetpgrp(&self, fd: Fd) -> Result<Pid> {
654 unsafe { libc::tcgetpgrp(fd.0) }.errno_if_m1().map(Pid)
655 }
656
657 fn tcsetpgrp(&mut self, fd: Fd, pgid: Pid) -> Result<()> {
658 let result = unsafe { libc::tcsetpgrp(fd.0, pgid.0) };
659 result.errno_if_m1().map(drop)
660 }
661
662 fn new_child_process(&mut self) -> Result<ChildProcessStarter> {
670 let raw_pid = unsafe { libc::fork() }.errno_if_m1()?;
671 if raw_pid != 0 {
672 return Ok(Box::new(move |_env, _task| Pid(raw_pid)));
674 }
675
676 Ok(Box::new(|env, task| {
678 let system = env.system.clone();
679 let executor = Executor::new();
683 let task = Box::pin(async move { match task(env).await {} });
684 unsafe { executor.spawn_pinned(task) }
687 loop {
688 executor.run_until_stalled();
689 system.select(false).ok();
690 }
691 }))
692 }
693
694 fn wait(&mut self, target: Pid) -> Result<Option<(Pid, ProcessState)>> {
695 let mut status = 0;
696 let options = libc::WUNTRACED | libc::WCONTINUED | libc::WNOHANG;
697 match unsafe { libc::waitpid(target.0, &mut status, options) } {
698 -1 => Err(Errno::last()),
699 0 => Ok(None),
700 pid => {
701 let state = if libc::WIFCONTINUED(status) {
702 ProcessState::Running
703 } else if libc::WIFEXITED(status) {
704 let exit_status = libc::WEXITSTATUS(status);
705 ProcessState::exited(exit_status)
706 } else if libc::WIFSIGNALED(status) {
707 let signal = libc::WTERMSIG(status);
708 let core_dump = libc::WCOREDUMP(status);
709 let raw_number = unsafe { NonZero::new_unchecked(signal) };
711 let signal = signal::Number::from_raw_unchecked(raw_number);
712 let process_result = ProcessResult::Signaled { signal, core_dump };
713 process_result.into()
714 } else if libc::WIFSTOPPED(status) {
715 let signal = libc::WSTOPSIG(status);
716 let raw_number = unsafe { NonZero::new_unchecked(signal) };
718 let signal = signal::Number::from_raw_unchecked(raw_number);
719 ProcessState::stopped(signal)
720 } else {
721 unreachable!()
722 };
723 Ok(Some((Pid(pid), state)))
724 }
725 }
726 }
727
728 fn execve(
729 &mut self,
730 path: &CStr,
731 args: &[CString],
732 envs: &[CString],
733 ) -> FlexFuture<Result<Infallible>> {
734 fn to_pointer_array<S: AsRef<CStr>>(strs: &[S]) -> Vec<*const libc::c_char> {
735 strs.iter()
736 .map(|s| s.as_ref().as_ptr())
737 .chain(std::iter::once(std::ptr::null()))
738 .collect()
739 }
740 let args = to_pointer_array(args);
751 let envs = to_pointer_array(envs);
752 loop {
753 let _ = unsafe { libc::execve(path.as_ptr(), args.as_ptr(), envs.as_ptr()) };
754 let errno = Errno::last();
755 if errno != Errno::EINTR {
756 return Err(errno).into();
757 }
758 }
759 }
760
761 fn exit(&mut self, exit_status: ExitStatus) -> FlexFuture<Infallible> {
762 unsafe { libc::_exit(exit_status.0) }
763 }
764
765 fn getcwd(&self) -> Result<PathBuf> {
766 let mut buffer = Vec::<u8>::new();
773 for capacity in [1 << 10, 1 << 12, 1 << 14, 1 << 16] {
774 buffer.reserve_exact(capacity);
775
776 let result = unsafe { libc::getcwd(buffer.as_mut_ptr().cast(), capacity) };
777 if !result.is_null() {
778 let len = unsafe { CStr::from_ptr(buffer.as_ptr().cast()) }.count_bytes();
780 unsafe { buffer.set_len(len) }
781 buffer.shrink_to_fit();
782 return Ok(PathBuf::from(UnixString::from_vec(buffer)));
783 }
784 let errno = Errno::last();
785 if errno != Errno::ERANGE {
786 return Err(errno);
787 }
788 }
789 Err(Errno::ERANGE)
790 }
791
792 fn chdir(&mut self, path: &CStr) -> Result<()> {
793 let result = unsafe { libc::chdir(path.as_ptr()) };
794 result.errno_if_m1().map(drop)
795 }
796
797 fn getuid(&self) -> Uid {
798 Uid(unsafe { libc::getuid() })
799 }
800
801 fn geteuid(&self) -> Uid {
802 Uid(unsafe { libc::geteuid() })
803 }
804
805 fn getgid(&self) -> Gid {
806 Gid(unsafe { libc::getgid() })
807 }
808
809 fn getegid(&self) -> Gid {
810 Gid(unsafe { libc::getegid() })
811 }
812
813 fn getpwnam_dir(&self, name: &CStr) -> Result<Option<PathBuf>> {
814 Errno::clear();
815 let passwd = unsafe { libc::getpwnam(name.as_ptr()) };
816 if passwd.is_null() {
817 let errno = Errno::last();
818 return if errno == Errno::NO_ERROR {
819 Ok(None)
820 } else {
821 Err(errno)
822 };
823 }
824
825 let dir = unsafe { CStr::from_ptr((*passwd).pw_dir) };
826 Ok(Some(UnixString::from_vec(dir.to_bytes().to_vec()).into()))
827 }
828
829 fn confstr_path(&self) -> Result<UnixString> {
830 #[cfg(any(
832 target_os = "linux",
833 target_os = "macos",
834 target_os = "ios",
835 target_os = "tvos",
836 target_os = "watchos"
837 ))]
838 unsafe {
839 let size = libc::confstr(libc::_CS_PATH, std::ptr::null_mut(), 0);
840 if size == 0 {
841 return Err(Errno::last());
842 }
843 let mut buffer = Vec::<u8>::with_capacity(size);
844 let final_size = libc::confstr(libc::_CS_PATH, buffer.as_mut_ptr() as *mut _, size);
845 if final_size == 0 {
846 return Err(Errno::last());
847 }
848 if final_size > size {
849 return Err(Errno::ERANGE);
850 }
851 buffer.set_len(final_size - 1); return Ok(UnixString::from_vec(buffer));
853 }
854
855 #[allow(unreachable_code)]
856 Err(Errno::ENOSYS)
857 }
858
859 fn shell_path(&self) -> CString {
865 #[cfg(any(target_os = "linux", target_os = "android"))]
866 if self.is_executable_file(c"/proc/self/exe") {
867 return c"/proc/self/exe".to_owned();
868 }
869 if let Ok(path) = self.confstr_path() {
873 if let Some(full_path) = path
874 .as_bytes()
875 .split(|b| *b == b':')
876 .map(|dir| Path::new(UnixStr::from_bytes(dir)).join("sh"))
877 .filter(|full_path| full_path.is_absolute())
878 .filter_map(|full_path| CString::new(full_path.into_unix_string().into_vec()).ok())
879 .find(|full_path| self.is_executable_file(full_path))
880 {
881 return full_path;
882 }
883 }
884
885 c"/bin/sh".to_owned()
887 }
888
889 fn getrlimit(&self, resource: Resource) -> Result<LimitPair> {
890 let raw_resource = resource.as_raw_type().ok_or(Errno::EINVAL)?;
891
892 let mut limits = MaybeUninit::<libc::rlimit>::uninit();
893 unsafe { libc::getrlimit(raw_resource as _, limits.as_mut_ptr()) }.errno_if_m1()?;
894
895 Ok(LimitPair {
899 soft: unsafe { (*limits.as_ptr()).rlim_cur },
900 hard: unsafe { (*limits.as_ptr()).rlim_max },
901 })
902 }
903
904 fn setrlimit(&mut self, resource: Resource, limits: LimitPair) -> Result<()> {
905 let raw_resource = resource.as_raw_type().ok_or(Errno::EINVAL)?;
906
907 let mut rlimit = MaybeUninit::<libc::rlimit>::uninit();
908 unsafe {
909 (&raw mut (*rlimit.as_mut_ptr()).rlim_cur).write(limits.soft);
910 (&raw mut (*rlimit.as_mut_ptr()).rlim_max).write(limits.hard);
911 }
912
913 unsafe { libc::setrlimit(raw_resource as _, rlimit.as_ptr()) }.errno_if_m1()?;
914 Ok(())
915 }
916}
917
918#[derive(Debug)]
920struct RealDir(NonNull<DIR>);
921
922impl Drop for RealDir {
923 fn drop(&mut self) {
924 unsafe {
925 libc::closedir(self.0.as_ptr());
926 }
927 }
928}
929
930impl Dir for RealDir {
931 fn next(&mut self) -> Result<Option<DirEntry<'_>>> {
932 Errno::clear();
933 let entry = unsafe { libc::readdir(self.0.as_ptr()) };
934 let errno = Errno::last();
935 if entry.is_null() {
936 if errno == Errno::NO_ERROR {
937 Ok(None)
938 } else {
939 Err(errno)
940 }
941 } else {
942 let name = unsafe { CStr::from_ptr((&raw const (*entry).d_name).cast()) };
944 let name = UnixStr::from_bytes(name.to_bytes());
945 Ok(Some(DirEntry { name }))
946 }
947 }
948}
949
950#[cfg(test)]
951mod tests {
952 use super::*;
953
954 #[test]
955 fn real_system_directory_entries() {
956 let mut system = unsafe { RealSystem::new() };
957 let mut dir = system.opendir(c".").unwrap();
958 let mut count = 0;
959 while dir.next().unwrap().is_some() {
960 count += 1;
961 }
962 assert!(count > 0);
963 }
964
965 #[test]
967 fn real_system_caught_signals() {
968 unsafe {
969 let mut system = RealSystem::new();
970 let result = system.caught_signals();
971 assert_eq!(result, []);
972
973 catch_signal(libc::SIGINT);
974 catch_signal(libc::SIGTERM);
975 catch_signal(libc::SIGTERM);
976 catch_signal(libc::SIGCHLD);
977
978 let sigint = signal::Number::from_raw_unchecked(NonZero::new(libc::SIGINT).unwrap());
979 let sigterm = signal::Number::from_raw_unchecked(NonZero::new(libc::SIGTERM).unwrap());
980 let sigchld = signal::Number::from_raw_unchecked(NonZero::new(libc::SIGCHLD).unwrap());
981
982 let result = system.caught_signals();
983 assert_eq!(result, [sigint, sigterm, sigchld]);
984 let result = system.caught_signals();
985 assert_eq!(result, []);
986 }
987 }
988}