1use alloc::ffi::CString;
4use alloc::vec::Vec;
5use core::ffi::CStr;
6#[cfg(all(unix, not(target_os = "redox")))]
7use core::ptr::NonNull;
8use std::ffi::{OsStr, OsString};
9#[cfg(target_os = "linux")]
10use std::os::fd::FromRawFd;
11use std::os::fd::{AsFd, AsRawFd, BorrowedFd, IntoRawFd, OwnedFd};
12use std::path::Path;
13
14pub use super::posix_unix_like::*;
15
16pub use libc::{c_char, pid_t};
17
18pub const EX_OK: i32 = 0;
20pub const EX_USAGE: i32 = 64;
21pub const EX_DATAERR: i32 = 65;
22pub const EX_NOINPUT: i32 = 66;
23pub const EX_NOUSER: i32 = 67;
24pub const EX_NOHOST: i32 = 68;
25pub const EX_UNAVAILABLE: i32 = 69;
26pub const EX_SOFTWARE: i32 = 70;
27pub const EX_OSERR: i32 = 71;
28pub const EX_OSFILE: i32 = 72;
29pub const EX_CANTCREAT: i32 = 73;
30pub const EX_IOERR: i32 = 74;
31pub const EX_TEMPFAIL: i32 = 75;
32pub const EX_PROTOCOL: i32 = 76;
33pub const EX_NOPERM: i32 = 77;
34pub const EX_CONFIG: i32 = 78;
35
36#[cfg(any(target_os = "android", target_os = "redox", unix))]
38pub use libc::{PRIO_PGRP, PRIO_PROCESS, PRIO_USER};
39
40#[cfg(target_os = "macos")]
41pub use libc::{
42 COPYFILE_ACL, COPYFILE_DATA, COPYFILE_STAT, COPYFILE_XATTR, PRIO_DARWIN_BG, PRIO_DARWIN_NONUI,
43 PRIO_DARWIN_PROCESS, PRIO_DARWIN_THREAD, TMP_MAX,
44};
45
46#[cfg(target_os = "linux")]
47pub use libc::PIDFD_NONBLOCK;
48
49#[cfg(any(target_os = "android", target_os = "linux"))]
50pub use libc::{
51 CLONE_FILES, CLONE_FS, CLONE_NEWCGROUP, CLONE_NEWIPC, CLONE_NEWNET, CLONE_NEWNS, CLONE_NEWPID,
52 CLONE_NEWUSER, CLONE_NEWUTS, CLONE_SIGHAND, CLONE_SYSVSEM, CLONE_THREAD, CLONE_VM,
53 MFD_HUGE_SHIFT, P_PIDFD, SCHED_BATCH, SCHED_DEADLINE, SCHED_IDLE, SCHED_NORMAL,
54 SCHED_RESET_ON_FORK, SPLICE_F_MORE, SPLICE_F_MOVE, SPLICE_F_NONBLOCK,
55};
56
57#[cfg(any(target_os = "linux", target_os = "macos", target_os = "netbsd"))]
58pub use libc::{XATTR_CREATE, XATTR_REPLACE};
59
60#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
61pub use libc::{
62 MFD_ALLOW_SEALING, MFD_CLOEXEC, MFD_HUGE_MASK, MFD_HUGETLB, POSIX_FADV_DONTNEED,
63 POSIX_FADV_NOREUSE, POSIX_FADV_NORMAL, POSIX_FADV_RANDOM, POSIX_FADV_SEQUENTIAL,
64 POSIX_FADV_WILLNEED,
65};
66
67#[cfg(any(target_os = "android", target_os = "linux", target_os = "redox", unix))]
68pub use libc::{RTLD_LAZY, RTLD_NOW, WNOHANG};
69
70#[cfg(any(target_os = "android", target_os = "macos", target_os = "redox", unix))]
71pub use libc::RTLD_GLOBAL;
72
73#[cfg(any(
74 target_os = "android",
75 target_os = "freebsd",
76 target_os = "linux",
77 target_os = "netbsd"
78))]
79pub use libc::{
80 EFD_CLOEXEC, EFD_NONBLOCK, EFD_SEMAPHORE, TFD_CLOEXEC, TFD_NONBLOCK, TFD_TIMER_ABSTIME,
81 TFD_TIMER_CANCEL_ON_SET,
82};
83
84#[cfg(any(
85 target_os = "android",
86 target_os = "dragonfly",
87 target_os = "linux",
88 target_os = "netbsd"
89))]
90pub use libc::{GRND_NONBLOCK, GRND_RANDOM};
91
92#[cfg(any(
93 target_os = "dragonfly",
94 target_os = "freebsd",
95 target_os = "linux",
96 target_os = "macos",
97 target_os = "netbsd"
98))]
99pub use libc::SCHED_OTHER;
100
101#[cfg(any(
102 target_os = "android",
103 target_os = "dragonfly",
104 target_os = "freebsd",
105 target_os = "linux",
106 target_os = "macos"
107))]
108pub use libc::{RTLD_NODELETE, SEEK_DATA, SEEK_HOLE};
109
110#[cfg(any(
111 target_os = "android",
112 target_os = "linux",
113 target_os = "macos",
114 target_os = "netbsd",
115 target_os = "redox",
116 unix
117))]
118pub use libc::RTLD_LOCAL;
119
120#[cfg(any(
121 target_os = "android",
122 target_os = "freebsd",
123 target_os = "linux",
124 target_os = "netbsd",
125 target_os = "redox",
126 unix
127))]
128pub use libc::WUNTRACED;
129
130#[cfg(any(
131 target_os = "android",
132 target_os = "dragonfly",
133 target_os = "freebsd",
134 target_os = "linux",
135 target_os = "macos",
136 target_os = "netbsd"
137))]
138pub use libc::{
139 CLD_CONTINUED, CLD_DUMPED, CLD_EXITED, CLD_KILLED, CLD_STOPPED, CLD_TRAPPED, P_ALL, P_PGID,
140 P_PID, SCHED_FIFO, SCHED_RR,
141};
142
143#[cfg(any(
144 target_os = "android",
145 target_os = "dragonfly",
146 target_os = "freebsd",
147 target_os = "linux",
148 target_os = "macos",
149 target_os = "netbsd",
150 target_os = "redox"
151))]
152pub use libc::{RTLD_NOLOAD, WEXITED, WNOWAIT, WSTOPPED};
153
154#[cfg(any(
155 target_os = "android",
156 target_os = "dragonfly",
157 target_os = "freebsd",
158 target_os = "linux",
159 target_os = "macos",
160 target_os = "netbsd",
161 target_os = "redox",
162 unix
163))]
164pub use libc::WCONTINUED;
165
166#[cfg(all(target_os = "linux", target_env = "gnu"))]
167pub use libc::RTLD_DEEPBIND;
168
169#[cfg(unix)]
170pub use libc::{UTIME_NOW, UTIME_OMIT};
171
172#[cfg(target_os = "freebsd")]
173pub use libc::{SF_MNOWAIT, SF_NOCACHE, SF_NODISKIO, SF_SYNC};
174
175#[cfg(unix)]
178pub use libc::{
179 _PC_CHOWN_RESTRICTED, _PC_LINK_MAX, _PC_MAX_CANON, _PC_MAX_INPUT, _PC_NAME_MAX, _PC_NO_TRUNC,
180 _PC_PATH_MAX, _PC_PIPE_BUF, _PC_VDISABLE, _SC_ARG_MAX, _SC_CHILD_MAX, _SC_CLK_TCK,
181 _SC_LOGIN_NAME_MAX, _SC_NGROUPS_MAX, _SC_OPEN_MAX, _SC_PAGE_SIZE, _SC_RE_DUP_MAX,
182 _SC_STREAM_MAX, _SC_TTY_NAME_MAX, _SC_TZNAME_MAX, _SC_VERSION,
183};
184
185#[cfg(any(
186 target_os = "dragonfly",
187 target_os = "freebsd",
188 target_os = "linux",
189 target_os = "netbsd",
190 target_os = "openbsd",
191 target_os = "redox"
192))]
193pub use libc::_PC_FILESIZEBITS;
194
195#[cfg(any(
196 target_os = "android",
197 target_os = "dragonfly",
198 target_os = "illumos",
199 target_os = "linux",
200 target_os = "netbsd",
201 target_os = "openbsd",
202 target_os = "redox",
203 target_os = "solaris"
204))]
205pub use libc::_PC_2_SYMLINKS;
206
207#[cfg(any(
208 target_os = "android",
209 target_os = "dragonfly",
210 target_os = "freebsd",
211 target_os = "linux",
212 target_os = "openbsd",
213 target_os = "redox"
214))]
215pub use libc::_PC_ALLOC_SIZE_MIN;
216
217#[cfg(any(
218 target_os = "android",
219 target_os = "dragonfly",
220 target_os = "freebsd",
221 target_os = "linux",
222 target_os = "openbsd"
223))]
224pub use libc::_PC_REC_INCR_XFER_SIZE;
225
226#[cfg(any(
227 target_os = "android",
228 target_os = "dragonfly",
229 target_os = "freebsd",
230 target_os = "linux",
231 target_os = "openbsd",
232 target_os = "redox"
233))]
234pub use libc::{_PC_REC_MAX_XFER_SIZE, _PC_REC_MIN_XFER_SIZE, _PC_REC_XFER_ALIGN};
235
236#[cfg(any(
237 target_os = "android",
238 target_os = "dragonfly",
239 target_os = "freebsd",
240 target_os = "illumos",
241 target_os = "linux",
242 target_os = "netbsd",
243 target_os = "openbsd",
244 target_os = "redox",
245 target_os = "solaris"
246))]
247pub use libc::_PC_SYMLINK_MAX;
248
249#[cfg(any(
250 target_os = "android",
251 target_os = "dragonfly",
252 target_os = "freebsd",
253 target_os = "illumos",
254 target_os = "linux",
255 target_os = "openbsd",
256 target_os = "redox",
257 target_os = "solaris"
258))]
259pub use libc::{_PC_ASYNC_IO, _PC_PRIO_IO};
260
261#[cfg(any(
262 target_os = "android",
263 target_os = "dragonfly",
264 target_os = "freebsd",
265 target_os = "illumos",
266 target_os = "linux",
267 target_os = "netbsd",
268 target_os = "openbsd",
269 target_os = "redox",
270 target_os = "solaris"
271))]
272pub use libc::_PC_SYNC_IO;
273
274#[cfg(any(target_os = "dragonfly", target_os = "openbsd"))]
275pub use libc::_PC_TIMESTAMP_RESOLUTION;
276
277#[cfg(all(unix, not(target_os = "redox")))]
278pub use libc::{
279 _SC_2_C_BIND, _SC_2_C_DEV, _SC_2_CHAR_TERM, _SC_2_FORT_DEV, _SC_2_FORT_RUN, _SC_2_LOCALEDEF,
280 _SC_2_SW_DEV, _SC_2_UPE, _SC_2_VERSION, _SC_AIO_LISTIO_MAX, _SC_AIO_MAX,
281 _SC_AIO_PRIO_DELTA_MAX, _SC_ASYNCHRONOUS_IO, _SC_ATEXIT_MAX, _SC_BC_BASE_MAX, _SC_BC_DIM_MAX,
282 _SC_BC_SCALE_MAX, _SC_BC_STRING_MAX, _SC_COLL_WEIGHTS_MAX, _SC_DELAYTIMER_MAX,
283 _SC_EXPR_NEST_MAX, _SC_FSYNC, _SC_GETGR_R_SIZE_MAX, _SC_GETPW_R_SIZE_MAX, _SC_IOV_MAX,
284 _SC_JOB_CONTROL, _SC_LINE_MAX, _SC_MAPPED_FILES, _SC_MEMLOCK, _SC_MEMLOCK_RANGE,
285 _SC_MEMORY_PROTECTION, _SC_MESSAGE_PASSING, _SC_MQ_OPEN_MAX, _SC_MQ_PRIO_MAX,
286 _SC_NPROCESSORS_CONF, _SC_NPROCESSORS_ONLN, _SC_PHYS_PAGES, _SC_PRIORITIZED_IO,
287 _SC_PRIORITY_SCHEDULING, _SC_REALTIME_SIGNALS, _SC_RTSIG_MAX, _SC_SAVED_IDS, _SC_SEM_NSEMS_MAX,
288 _SC_SEM_VALUE_MAX, _SC_SEMAPHORES, _SC_SHARED_MEMORY_OBJECTS, _SC_SIGQUEUE_MAX,
289 _SC_SYNCHRONIZED_IO, _SC_THREAD_ATTR_STACKADDR, _SC_THREAD_ATTR_STACKSIZE,
290 _SC_THREAD_DESTRUCTOR_ITERATIONS, _SC_THREAD_KEYS_MAX, _SC_THREAD_PRIO_INHERIT,
291 _SC_THREAD_PRIO_PROTECT, _SC_THREAD_PRIORITY_SCHEDULING, _SC_THREAD_PROCESS_SHARED,
292 _SC_THREAD_SAFE_FUNCTIONS, _SC_THREAD_STACK_MIN, _SC_THREAD_THREADS_MAX, _SC_THREADS,
293 _SC_TIMER_MAX, _SC_TIMERS, _SC_XOPEN_CRYPT, _SC_XOPEN_ENH_I18N, _SC_XOPEN_LEGACY,
294 _SC_XOPEN_REALTIME, _SC_XOPEN_REALTIME_THREADS, _SC_XOPEN_SHM, _SC_XOPEN_UNIX,
295 _SC_XOPEN_VERSION, _SC_XOPEN_XCU_VERSION,
296};
297
298#[cfg(any(
299 target_os = "linux",
300 target_vendor = "apple",
301 target_os = "netbsd",
302 target_os = "fuchsia"
303))]
304pub use libc::{
305 _SC_PASS_MAX, _SC_XBS5_ILP32_OFF32, _SC_XBS5_ILP32_OFFBIG, _SC_XBS5_LP64_OFF64,
306 _SC_XBS5_LPBIG_OFFBIG,
307};
308
309#[cfg(target_os = "redox")]
310pub use libc::{_SC_HOST_NAME_MAX, _SC_SYMLOOP_MAX};
311
312pub struct UnameInfo {
313 pub sysname: String,
314 pub nodename: String,
315 pub release: String,
316 pub version: String,
317 pub machine: String,
318}
319
320#[derive(Debug)]
321pub struct UnameDecodeError {
322 pub bytes: Vec<u8>,
323 pub error: core::str::Utf8Error,
324}
325
326#[cfg(all(unix, not(target_os = "redox")))]
327#[derive(Clone, Copy, Debug)]
328pub struct StatVfsInfo {
329 pub f_bsize: libc::c_ulong,
330 pub f_frsize: libc::c_ulong,
331 pub f_blocks: libc::fsblkcnt_t,
332 pub f_bfree: libc::fsblkcnt_t,
333 pub f_bavail: libc::fsblkcnt_t,
334 pub f_files: libc::fsfilcnt_t,
335 pub f_ffree: libc::fsfilcnt_t,
336 pub f_favail: libc::fsfilcnt_t,
337 pub f_flag: libc::c_ulong,
338 pub f_namemax: libc::c_ulong,
339 pub f_fsid: libc::c_ulong,
340}
341
342#[cfg(all(unix, not(target_os = "redox")))]
343#[derive(Clone, Debug)]
344pub struct RawDirEntry {
345 pub name: Vec<u8>,
346 pub d_type: Option<u8>,
347 pub ino: u64,
348}
349
350#[cfg(all(unix, not(target_os = "redox")))]
351pub struct FdDirStream(NonNull<libc::DIR>);
352
353#[cfg(all(target_os = "linux", target_env = "gnu"))]
354pub type PriorityWhichType = libc::__priority_which_t;
355#[cfg(not(all(target_os = "linux", target_env = "gnu")))]
356pub type PriorityWhichType = libc::c_int;
357
358#[cfg(target_os = "freebsd")]
359pub type PriorityWhoType = i32;
360#[cfg(not(target_os = "freebsd"))]
361pub type PriorityWhoType = u32;
362
363#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
364#[derive(Clone, Debug)]
365pub enum PosixSpawnFileAction {
366 Open {
367 fd: i32,
368 path: CString,
369 oflag: i32,
370 mode: u32,
371 },
372 Close {
373 fd: i32,
374 },
375 Dup2 {
376 fd: i32,
377 newfd: i32,
378 },
379}
380
381#[cfg(all(
382 any(target_os = "linux", target_os = "freebsd", target_os = "android"),
383 not(target_env = "musl")
384))]
385pub struct PosixSpawnScheduler {
386 pub policy: Option<i32>,
387 pub param: libc::sched_param,
388}
389
390#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
391pub struct PosixSpawnConfig<'a> {
392 pub path: &'a CStr,
393 pub args: &'a [CString],
394 pub env: &'a [CString],
395 pub file_actions: &'a [PosixSpawnFileAction],
396 pub setsigdef: Option<&'a [i32]>,
397 pub setpgroup: Option<libc::pid_t>,
398 pub resetids: bool,
399 pub setsid: bool,
400 pub setsigmask: Option<&'a [i32]>,
401 pub spawnp: bool,
402 #[cfg(all(
403 any(target_os = "linux", target_os = "freebsd", target_os = "android"),
404 not(target_env = "musl")
405 ))]
406 pub scheduler: Option<PosixSpawnScheduler>,
407}
408
409pub fn set_inheritable(fd: BorrowedFd<'_>, inheritable: bool) -> std::io::Result<()> {
410 use nix::fcntl;
411
412 let flags = fcntl::FdFlag::from_bits_truncate(
413 fcntl::fcntl(fd, fcntl::FcntlArg::F_GETFD).map_err(std::io::Error::from)?,
414 );
415 let mut new_flags = flags;
416 new_flags.set(fcntl::FdFlag::FD_CLOEXEC, !inheritable);
417 if flags != new_flags {
418 fcntl::fcntl(fd, fcntl::FcntlArg::F_SETFD(new_flags)).map_err(std::io::Error::from)?;
419 }
420 Ok(())
421}
422
423pub fn is_session_leader() -> bool {
424 unsafe { libc::getsid(0) == libc::getpid() }
425}
426
427pub fn getpid() -> libc::pid_t {
428 unsafe { libc::getpid() }
429}
430
431#[cfg(all(unix, not(target_os = "redox")))]
432pub fn dup_fd(fd: BorrowedFd<'_>) -> std::io::Result<std::os::fd::OwnedFd> {
433 nix::unistd::dup(fd).map_err(std::io::Error::from)
434}
435
436#[cfg(not(target_os = "redox"))]
437pub fn symlinkat(src: &CStr, dir_fd: BorrowedFd<'_>, dst: &CStr) -> std::io::Result<()> {
438 nix::unistd::symlinkat(src, dir_fd, dst).map_err(std::io::Error::from)
439}
440
441#[cfg(target_os = "redox")]
442pub fn symlink(src: &CStr, dst: &CStr) -> std::io::Result<()> {
443 let ret = unsafe { libc::symlink(src.as_ptr(), dst.as_ptr()) };
444 if ret < 0 {
445 Err(std::io::Error::last_os_error())
446 } else {
447 Ok(())
448 }
449}
450
451#[cfg(not(target_os = "redox"))]
452pub fn chroot(path: &Path) -> std::io::Result<()> {
453 nix::unistd::chroot(path).map_err(std::io::Error::from)
454}
455
456#[cfg(all(unix, not(target_os = "redox")))]
457pub fn fchmodat(dirfd: i32, path: &CStr, mode: libc::mode_t, flags: i32) -> std::io::Result<()> {
458 let ret = unsafe { libc::fchmodat(dirfd, path.as_ptr(), mode, flags) };
459 if ret == 0 {
460 Ok(())
461 } else {
462 Err(std::io::Error::last_os_error())
463 }
464}
465
466#[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "netbsd"))]
467pub fn lchmod(path: &CStr, mode: libc::mode_t) -> std::io::Result<()> {
468 unsafe extern "C" {
469 fn lchmod(path: *const libc::c_char, mode: libc::mode_t) -> libc::c_int;
470 }
471 if unsafe { lchmod(path.as_ptr(), mode) } == 0 {
472 Ok(())
473 } else {
474 Err(std::io::Error::last_os_error())
475 }
476}
477
478#[cfg(target_os = "macos")]
479pub fn fcopyfile(in_fd: i32, out_fd: i32, flags: u32) -> std::io::Result<()> {
480 unsafe extern "C" {
481 fn fcopyfile(
482 in_fd: libc::c_int,
483 out_fd: libc::c_int,
484 state: *mut libc::c_void,
485 flags: u32,
486 ) -> libc::c_int;
487 }
488 let ret = unsafe { fcopyfile(in_fd, out_fd, core::ptr::null_mut(), flags) };
489 if ret < 0 {
490 Err(std::io::Error::last_os_error())
491 } else {
492 Ok(())
493 }
494}
495
496#[cfg(unix)]
497pub fn link_paths(
498 src_dir_fd: i32,
499 src: &CStr,
500 dst_dir_fd: i32,
501 dst: &CStr,
502 follow_symlinks: bool,
503) -> std::io::Result<()> {
504 let flags = if follow_symlinks {
505 libc::AT_SYMLINK_FOLLOW
506 } else {
507 0
508 };
509 let ret = unsafe { libc::linkat(src_dir_fd, src.as_ptr(), dst_dir_fd, dst.as_ptr(), flags) };
510 if ret != 0 {
511 Err(std::io::Error::last_os_error())
512 } else {
513 Ok(())
514 }
515}
516
517#[cfg(all(unix, not(target_os = "redox")))]
518fn statvfs_info_from_raw(st: libc::statvfs) -> StatVfsInfo {
519 let f_fsid = {
520 let ptr = core::ptr::addr_of!(st.f_fsid) as *const u8;
521 let size = core::mem::size_of_val(&st.f_fsid);
522 if size >= 8 {
523 let bytes = unsafe { core::slice::from_raw_parts(ptr, 8) };
524 u64::from_ne_bytes([
525 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
526 ]) as libc::c_ulong
527 } else if size >= 4 {
528 let bytes = unsafe { core::slice::from_raw_parts(ptr, 4) };
529 u32::from_ne_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) as libc::c_ulong
530 } else {
531 0
532 }
533 };
534
535 StatVfsInfo {
536 f_bsize: st.f_bsize,
537 f_frsize: st.f_frsize,
538 f_blocks: st.f_blocks,
539 f_bfree: st.f_bfree,
540 f_bavail: st.f_bavail,
541 f_files: st.f_files,
542 f_ffree: st.f_ffree,
543 f_favail: st.f_favail,
544 f_flag: st.f_flag,
545 f_namemax: st.f_namemax,
546 f_fsid,
547 }
548}
549
550#[cfg(all(unix, not(target_os = "redox")))]
551pub fn statvfs_path(path: &CStr) -> std::io::Result<StatVfsInfo> {
552 let mut st: libc::statvfs = unsafe { core::mem::zeroed() };
553 let ret = unsafe { libc::statvfs(path.as_ptr(), &mut st) };
554 if ret != 0 {
555 Err(std::io::Error::last_os_error())
556 } else {
557 Ok(statvfs_info_from_raw(st))
558 }
559}
560
561#[cfg(all(unix, not(target_os = "redox")))]
562pub fn statvfs_fd(fd: i32) -> std::io::Result<StatVfsInfo> {
563 let mut st: libc::statvfs = unsafe { core::mem::zeroed() };
564 let ret = unsafe { libc::fstatvfs(fd, &mut st) };
565 if ret != 0 {
566 Err(std::io::Error::last_os_error())
567 } else {
568 Ok(statvfs_info_from_raw(st))
569 }
570}
571
572#[cfg(not(target_os = "redox"))]
573pub fn mknod(path: &CStr, mode: libc::mode_t, device: libc::dev_t) -> std::io::Result<()> {
574 let ret = unsafe { libc::mknod(path.as_ptr(), mode, device) };
575 if ret == 0 {
576 Ok(())
577 } else {
578 Err(std::io::Error::last_os_error())
579 }
580}
581
582#[cfg(all(not(target_os = "redox"), not(target_vendor = "apple")))]
583pub fn mknodat(
584 dir_fd: i32,
585 path: &CStr,
586 mode: libc::mode_t,
587 device: libc::dev_t,
588) -> std::io::Result<()> {
589 let ret = unsafe { libc::mknodat(dir_fd, path.as_ptr(), mode, device) };
590 if ret == 0 {
591 Ok(())
592 } else {
593 Err(std::io::Error::last_os_error())
594 }
595}
596
597fn uid_from_raw(uid: u32) -> nix::unistd::Uid {
598 nix::unistd::Uid::from_raw(uid)
599}
600
601fn gid_from_raw(gid: u32) -> nix::unistd::Gid {
602 nix::unistd::Gid::from_raw(gid)
603}
604
605pub fn fchown(fd: BorrowedFd<'_>, uid: Option<u32>, gid: Option<u32>) -> std::io::Result<()> {
606 nix::unistd::fchown(fd, uid.map(uid_from_raw), gid.map(gid_from_raw))
607 .map_err(std::io::Error::from)
608}
609
610#[cfg(not(windows))]
611pub fn stat_fd(fd: crate::crt_fd::Borrowed<'_>) -> std::io::Result<crate::fileutils::StatStruct> {
612 crate::fileutils::fstat(fd)
613}
614
615#[cfg(not(target_os = "redox"))]
616pub fn fchdir(fd: i32) -> std::io::Result<()> {
617 let ret = unsafe { libc::fchdir(fd) };
618 if ret == 0 {
619 Ok(())
620 } else {
621 Err(std::io::Error::last_os_error())
622 }
623}
624
625pub fn fork() -> std::io::Result<libc::pid_t> {
626 let pid = unsafe { libc::fork() };
627 if pid == -1 {
628 Err(std::io::Error::last_os_error())
629 } else {
630 Ok(pid)
631 }
632}
633
634pub fn write_fd(fd: BorrowedFd<'_>, buf: &[u8]) -> std::io::Result<usize> {
635 nix::unistd::write(fd, buf).map_err(std::io::Error::from)
636}
637
638pub fn pread(fd: i32, buf: &mut [u8], offset: libc::off_t) -> std::io::Result<usize> {
639 let ret = unsafe { libc::pread(fd, buf.as_mut_ptr().cast(), buf.len(), offset) };
640 if ret == -1 {
641 Err(std::io::Error::last_os_error())
642 } else {
643 Ok(ret as usize)
644 }
645}
646
647pub fn pwrite(fd: i32, buf: &[u8], offset: libc::off_t) -> std::io::Result<usize> {
648 let ret = unsafe { libc::pwrite(fd, buf.as_ptr().cast(), buf.len(), offset) };
649 if ret == -1 {
650 Err(std::io::Error::last_os_error())
651 } else {
652 Ok(ret as usize)
653 }
654}
655
656pub fn fchownat(
657 dir_fd: BorrowedFd<'_>,
658 path: &OsStr,
659 uid: Option<u32>,
660 gid: Option<u32>,
661 follow_symlinks: bool,
662) -> std::io::Result<()> {
663 let flag = if follow_symlinks {
664 nix::fcntl::AtFlags::empty()
665 } else {
666 nix::fcntl::AtFlags::AT_SYMLINK_NOFOLLOW
667 };
668 nix::unistd::fchownat(
669 dir_fd,
670 path,
671 uid.map(uid_from_raw),
672 gid.map(gid_from_raw),
673 flag,
674 )
675 .map_err(std::io::Error::from)
676}
677
678pub fn uname_info() -> Result<UnameInfo, UnameDecodeError> {
679 fn decode(value: &CStr) -> Result<String, UnameDecodeError> {
680 core::str::from_utf8(value.to_bytes())
681 .map(str::to_owned)
682 .map_err(|error| UnameDecodeError {
683 bytes: value.to_bytes().to_vec(),
684 error,
685 })
686 }
687
688 let info = rustix::system::uname();
689 Ok(UnameInfo {
690 sysname: decode(info.sysname())?,
691 nodename: decode(info.nodename())?,
692 release: decode(info.release())?,
693 version: decode(info.version())?,
694 machine: decode(info.machine())?,
695 })
696}
697
698#[cfg(any(
699 target_os = "dragonfly",
700 target_os = "freebsd",
701 target_os = "linux",
702 target_os = "android",
703 target_os = "netbsd",
704 target_os = "openbsd"
705))]
706pub fn pipe2(flags: libc::c_int) -> std::io::Result<(std::os::fd::OwnedFd, std::os::fd::OwnedFd)> {
707 nix::unistd::pipe2(nix::fcntl::OFlag::from_bits_truncate(flags)).map_err(std::io::Error::from)
708}
709
710#[cfg(not(target_os = "redox"))]
711pub fn pipe() -> std::io::Result<(OwnedFd, OwnedFd)> {
712 let (rfd, wfd) = nix::unistd::pipe().map_err(std::io::Error::from)?;
713 set_inheritable(rfd.as_fd(), false)?;
714 set_inheritable(wfd.as_fd(), false)?;
715 Ok((rfd, wfd))
716}
717
718pub fn sched_yield() -> std::io::Result<()> {
719 nix::sched::sched_yield().map_err(std::io::Error::from)
720}
721
722#[cfg(not(target_os = "redox"))]
723pub fn nice(increment: i32) -> std::io::Result<i32> {
724 crate::os::clear_errno();
725 let res = unsafe { libc::nice(increment) };
726 if res == -1 && crate::os::get_errno() != 0 {
727 Err(std::io::Error::last_os_error())
728 } else {
729 Ok(res)
730 }
731}
732
733#[cfg(not(target_os = "redox"))]
734pub fn sched_get_priority_max(policy: i32) -> std::io::Result<i32> {
735 let max = unsafe { libc::sched_get_priority_max(policy) };
736 if max == -1 {
737 Err(std::io::Error::last_os_error())
738 } else {
739 Ok(max)
740 }
741}
742
743#[cfg(not(target_os = "redox"))]
744pub fn sched_get_priority_min(policy: i32) -> std::io::Result<i32> {
745 let min = unsafe { libc::sched_get_priority_min(policy) };
746 if min == -1 {
747 Err(std::io::Error::last_os_error())
748 } else {
749 Ok(min)
750 }
751}
752
753#[cfg(not(target_os = "redox"))]
754pub fn fchmod(fd: BorrowedFd<'_>, mode: u32) -> std::io::Result<()> {
755 nix::sys::stat::fchmod(
756 fd,
757 nix::sys::stat::Mode::from_bits_truncate(mode as libc::mode_t),
758 )
759 .map_err(std::io::Error::from)
760}
761
762#[cfg(target_os = "redox")]
763pub fn utimes(
764 path: &Path,
765 acc: core::time::Duration,
766 modif: core::time::Duration,
767) -> std::io::Result<()> {
768 let tv = |d: core::time::Duration| libc::timeval {
769 tv_sec: d.as_secs() as _,
770 tv_usec: d.subsec_micros() as _,
771 };
772 nix::sys::stat::utimes(path, &tv(acc).into(), &tv(modif).into()).map_err(std::io::Error::from)
773}
774
775#[cfg(all(any(target_os = "wasi", unix), not(target_os = "redox")))]
776pub fn set_file_times_at(
777 dir_fd: i32,
778 path: &CStr,
779 access: core::time::Duration,
780 modified: core::time::Duration,
781 follow_symlinks: bool,
782) -> std::io::Result<()> {
783 let ts = |d: core::time::Duration| libc::timespec {
784 tv_sec: d.as_secs() as _,
785 tv_nsec: d.subsec_nanos() as _,
786 };
787 let times = [ts(access), ts(modified)];
788 let ret = unsafe {
789 libc::utimensat(
790 dir_fd,
791 path.as_ptr(),
792 times.as_ptr(),
793 if follow_symlinks {
794 0
795 } else {
796 libc::AT_SYMLINK_NOFOLLOW
797 },
798 )
799 };
800 if ret < 0 {
801 Err(std::io::Error::last_os_error())
802 } else {
803 Ok(())
804 }
805}
806
807#[cfg(target_os = "macos")]
808#[must_use]
809pub fn get_number_of_os_threads() -> isize {
810 type MachPortT = libc::c_uint;
811 type KernReturnT = libc::c_int;
812 type MachMsgTypeNumberT = libc::c_uint;
813 type ThreadActArrayT = *mut MachPortT;
814 const KERN_SUCCESS: KernReturnT = 0;
815 unsafe extern "C" {
816 fn mach_task_self() -> MachPortT;
817 fn task_for_pid(
818 task: MachPortT,
819 pid: libc::c_int,
820 target_task: *mut MachPortT,
821 ) -> KernReturnT;
822 fn task_threads(
823 target_task: MachPortT,
824 act_list: *mut ThreadActArrayT,
825 act_list_cnt: *mut MachMsgTypeNumberT,
826 ) -> KernReturnT;
827 fn vm_deallocate(
828 target_task: MachPortT,
829 address: libc::uintptr_t,
830 size: libc::uintptr_t,
831 ) -> KernReturnT;
832 }
833
834 let self_task = unsafe { mach_task_self() };
835 let mut proc_task: MachPortT = 0;
836 if unsafe { task_for_pid(self_task, libc::getpid(), &mut proc_task) } == KERN_SUCCESS {
837 let mut threads: ThreadActArrayT = core::ptr::null_mut();
838 let mut n_threads: MachMsgTypeNumberT = 0;
839 if unsafe { task_threads(proc_task, &mut threads, &mut n_threads) } == KERN_SUCCESS {
840 if !threads.is_null() {
841 let _ = unsafe {
842 vm_deallocate(
843 self_task,
844 threads as libc::uintptr_t,
845 (n_threads as usize * core::mem::size_of::<MachPortT>()) as libc::uintptr_t,
846 )
847 };
848 }
849 return n_threads as isize;
850 }
851 }
852 0
853}
854
855#[cfg(target_os = "linux")]
856#[must_use]
857pub fn get_number_of_os_threads() -> isize {
858 use std::io::Read as _;
859
860 let mut file = match crate::fs::open("/proc/self/stat") {
861 Ok(f) => f,
862 Err(_) => return 0,
863 };
864 let mut buf = [0u8; 160];
865 let n = match file.read(&mut buf) {
866 Ok(n) => n,
867 Err(_) => return 0,
868 };
869 let line = match core::str::from_utf8(&buf[..n]) {
870 Ok(s) => s,
871 Err(_) => return 0,
872 };
873 if let Some(field) = line.split_whitespace().nth(19) {
874 return field.parse::<isize>().unwrap_or(0);
875 }
876 0
877}
878
879#[cfg(not(any(target_os = "macos", target_os = "linux")))]
880#[must_use]
881pub fn get_number_of_os_threads() -> isize {
882 0
883}
884
885#[derive(Clone, Copy, Debug, Eq, PartialEq)]
886pub struct Permissions {
887 pub is_readable: bool,
888 pub is_writable: bool,
889 pub is_executable: bool,
890}
891
892#[derive(Clone, Copy, Debug, Eq, PartialEq)]
893pub enum AccessError {
894 InvalidMode,
895 Os(i32),
896}
897
898impl From<std::io::Error> for AccessError {
899 fn from(value: std::io::Error) -> Self {
900 Self::Os(value.raw_os_error().unwrap_or(0))
901 }
902}
903
904pub use crate::os::{AccessMode, F_OK, R_OK, W_OK, X_OK};
905
906fn get_permissions(mode: u32) -> Permissions {
907 Permissions {
908 is_readable: mode & 4 != 0,
909 is_writable: mode & 2 != 0,
910 is_executable: mode & 1 != 0,
911 }
912}
913
914pub fn get_right_permission(
915 mode: u32,
916 file_owner: u32,
917 file_group: u32,
918) -> std::io::Result<Permissions> {
919 let owner_mode = (mode & 0o700) >> 6;
920 let owner_permissions = get_permissions(owner_mode);
921
922 let group_mode = (mode & 0o070) >> 3;
923 let group_permissions = get_permissions(group_mode);
924
925 let others_mode = mode & 0o007;
926 let others_permissions = get_permissions(others_mode);
927
928 let user_id = nix::unistd::getuid().as_raw();
929 let groups_ids = getgroups()?;
930
931 if file_owner == user_id {
932 Ok(owner_permissions)
933 } else if groups_ids.contains(&file_group) {
934 Ok(group_permissions)
935 } else {
936 Ok(others_permissions)
937 }
938}
939
940#[cfg(any(target_os = "macos", target_os = "ios"))]
941pub fn getgroups() -> std::io::Result<Vec<u32>> {
942 use core::ptr;
943 use libc::{c_int, gid_t};
944 use nix::errno::Errno;
945
946 let ret = unsafe { libc::getgroups(0, ptr::null_mut()) };
947 let mut groups =
948 Vec::<gid_t>::with_capacity(Errno::result(ret).map_err(std::io::Error::from)? as usize);
949 let ret = unsafe { libc::getgroups(groups.capacity() as c_int, groups.as_mut_ptr()) };
950
951 Errno::result(ret).map_err(std::io::Error::from).map(|s| {
952 unsafe { groups.set_len(s as usize) };
953 groups.into_iter().collect()
954 })
955}
956
957#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "redox")))]
958pub fn getgroups() -> std::io::Result<Vec<u32>> {
959 nix::unistd::getgroups()
960 .map(|groups| groups.into_iter().map(|gid| gid.as_raw()).collect())
961 .map_err(std::io::Error::from)
962}
963
964#[cfg(target_os = "redox")]
965pub fn getgroups() -> std::io::Result<Vec<u32>> {
966 Err(std::io::Error::from_raw_os_error(libc::EOPNOTSUPP))
967}
968
969pub fn check_access(path: &Path, mode: u8) -> Result<bool, AccessError> {
970 use std::os::unix::fs::MetadataExt;
971
972 let Some(mode) = AccessMode::from_bits(mode) else {
973 return Err(AccessError::InvalidMode);
974 };
975
976 let metadata = match crate::fs::metadata(path) {
977 Ok(m) => m,
978 Err(_) => return Ok(false),
979 };
980
981 if mode.is_empty() {
982 return Ok(true);
983 }
984
985 let perm = get_right_permission(metadata.mode(), metadata.uid(), metadata.gid())?;
986
987 let r_ok = !mode.contains(&crate::os::AccessFlag::R) || perm.is_readable;
988 let w_ok = !mode.contains(&crate::os::AccessFlag::W) || perm.is_writable;
989 let x_ok = !mode.contains(&crate::os::AccessFlag::X) || perm.is_executable;
990
991 Ok(r_ok && w_ok && x_ok)
992}
993
994pub fn close_fds(above: i32, keep: &[BorrowedFd<'_>]) {
995 #[cfg(not(target_os = "redox"))]
996 if close_dir_fds(above, keep).is_ok() {
997 return;
998 }
999 #[cfg(target_os = "redox")]
1000 if close_filetable_fds(above, keep).is_ok() {
1001 return;
1002 }
1003 close_fds_brute_force(above, keep)
1004}
1005
1006#[allow(clippy::too_many_arguments)]
1007pub fn setup_child_fds(
1008 fds_to_keep: &[BorrowedFd<'_>],
1009 errpipe_write: BorrowedFd<'_>,
1010 p2cread: i32,
1011 p2cwrite: i32,
1012 c2pread: i32,
1013 c2pwrite: i32,
1014 errread: i32,
1015 errwrite: i32,
1016 errpipe_read: i32,
1017) -> std::io::Result<()> {
1018 for &fd in fds_to_keep {
1019 if fd.as_raw_fd() != errpipe_write.as_raw_fd() {
1020 set_inheritable(fd, true)?;
1021 }
1022 }
1023
1024 for fd in [p2cwrite, c2pread, errread] {
1025 if fd >= 0 {
1026 nix::unistd::close(fd).map_err(std::io::Error::from)?;
1027 }
1028 }
1029 nix::unistd::close(errpipe_read).map_err(std::io::Error::from)?;
1030
1031 let c2pwrite = if c2pwrite == 0 {
1032 let fd = unsafe { BorrowedFd::borrow_raw(c2pwrite) };
1033 let dup = nix::unistd::dup(fd).map_err(std::io::Error::from)?;
1034 set_inheritable(dup.as_fd(), true)?;
1035 dup.into_raw_fd()
1036 } else {
1037 c2pwrite
1038 };
1039
1040 let mut errwrite = errwrite;
1041 while errwrite == 0 || errwrite == 1 {
1042 let fd = unsafe { BorrowedFd::borrow_raw(errwrite) };
1043 let dup = nix::unistd::dup(fd).map_err(std::io::Error::from)?;
1044 set_inheritable(dup.as_fd(), true)?;
1045 errwrite = dup.into_raw_fd();
1046 }
1047
1048 dup_into_stdio(p2cread, 0)?;
1049 dup_into_stdio(c2pwrite, 1)?;
1050 dup_into_stdio(errwrite, 2)?;
1051 Ok(())
1052}
1053
1054fn dup_into_stdio(fd: i32, io_fd: i32) -> std::io::Result<()> {
1055 if fd < 0 {
1056 return Ok(());
1057 }
1058 let fd = unsafe { BorrowedFd::borrow_raw(fd) };
1059 if fd.as_raw_fd() == io_fd {
1060 set_inheritable(fd, true)
1061 } else {
1062 match io_fd {
1063 0 => nix::unistd::dup2_stdin(fd).map_err(std::io::Error::from),
1064 1 => nix::unistd::dup2_stdout(fd).map_err(std::io::Error::from),
1065 2 => nix::unistd::dup2_stderr(fd).map_err(std::io::Error::from),
1066 _ => unreachable!(),
1067 }
1068 }
1069}
1070
1071pub fn chdir(cwd: &CStr) -> nix::Result<()> {
1072 nix::unistd::chdir(cwd)
1073}
1074
1075pub fn set_umask(child_umask: i32) {
1076 if child_umask >= 0 {
1077 unsafe { libc::umask(child_umask as libc::mode_t) };
1078 }
1079}
1080
1081pub fn umask(mask: libc::mode_t) -> libc::mode_t {
1082 unsafe { libc::umask(mask) }
1083}
1084
1085#[cfg(not(any(target_os = "redox", target_os = "android")))]
1086pub fn sync() {
1087 unsafe { libc::sync() };
1088}
1089
1090pub fn getlogin() -> Option<CString> {
1091 let ptr = unsafe { libc::getlogin() };
1092 if ptr.is_null() {
1093 None
1094 } else {
1095 Some(unsafe { CStr::from_ptr(ptr) }.to_owned())
1096 }
1097}
1098
1099pub fn restore_signals() {
1100 unsafe {
1101 libc::signal(libc::SIGPIPE, libc::SIG_DFL);
1102 libc::signal(libc::SIGXFSZ, libc::SIG_DFL);
1103 }
1104}
1105
1106pub fn setsid_if_needed(call_setsid: bool) -> nix::Result<()> {
1107 if call_setsid {
1108 nix::unistd::setsid()?;
1109 }
1110 Ok(())
1111}
1112
1113pub fn setpgid_if_needed(pgid_to_set: libc::pid_t) -> nix::Result<()> {
1114 if pgid_to_set > -1 {
1115 nix::unistd::setpgid(
1116 nix::unistd::Pid::from_raw(0),
1117 nix::unistd::Pid::from_raw(pgid_to_set),
1118 )?;
1119 }
1120 Ok(())
1121}
1122
1123pub fn setgroups_if_needed(groups: Option<&[u32]>) -> nix::Result<()> {
1124 #[cfg(not(any(target_os = "ios", target_os = "redox")))]
1125 if let Some(groups) = groups {
1126 let ret =
1130 unsafe { libc::setgroups(groups.len() as _, groups.as_ptr().cast::<libc::gid_t>()) };
1131 nix::Error::result(ret)?;
1132 }
1133 Ok(())
1134}
1135
1136pub fn setregid_if_needed(gid: Option<u32>) -> nix::Result<()> {
1137 if let Some(gid) = gid.filter(|&x| x != u32::MAX) {
1138 let ret = unsafe { libc::setregid(gid as libc::gid_t, gid as libc::gid_t) };
1139 nix::Error::result(ret)?;
1140 }
1141 Ok(())
1142}
1143
1144pub fn setreuid_if_needed(uid: Option<u32>) -> nix::Result<()> {
1145 if let Some(uid) = uid.filter(|&x| x != u32::MAX) {
1146 let ret = unsafe { libc::setreuid(uid as libc::uid_t, uid as libc::uid_t) };
1147 nix::Error::result(ret)?;
1148 }
1149 Ok(())
1150}
1151
1152pub fn getppid() -> libc::pid_t {
1153 nix::unistd::getppid().as_raw()
1154}
1155
1156pub fn getgid() -> u32 {
1157 nix::unistd::getgid().as_raw()
1158}
1159
1160pub fn getegid() -> u32 {
1161 nix::unistd::getegid().as_raw()
1162}
1163
1164pub fn getpgid(pid: u32) -> std::io::Result<libc::pid_t> {
1165 nix::unistd::getpgid(Some(nix::unistd::Pid::from_raw(pid as i32)))
1166 .map(nix::unistd::Pid::as_raw)
1167 .map_err(std::io::Error::from)
1168}
1169
1170pub fn getpgrp() -> libc::pid_t {
1171 nix::unistd::getpgrp().as_raw()
1172}
1173
1174#[cfg(not(target_os = "redox"))]
1175pub fn getsid(pid: u32) -> std::io::Result<libc::pid_t> {
1176 nix::unistd::getsid(Some(nix::unistd::Pid::from_raw(pid as i32)))
1177 .map(nix::unistd::Pid::as_raw)
1178 .map_err(std::io::Error::from)
1179}
1180
1181pub fn getuid() -> u32 {
1182 nix::unistd::getuid().as_raw()
1183}
1184
1185pub fn geteuid() -> u32 {
1186 nix::unistd::geteuid().as_raw()
1187}
1188
1189#[cfg(not(any(target_os = "wasi", target_os = "android")))]
1190pub fn setgid(gid: u32) -> std::io::Result<()> {
1191 nix::unistd::setgid(gid_from_raw(gid)).map_err(std::io::Error::from)
1192}
1193
1194#[cfg(not(any(target_os = "wasi", target_os = "android", target_os = "redox")))]
1195pub fn setegid(egid: u32) -> std::io::Result<()> {
1196 nix::unistd::setegid(gid_from_raw(egid)).map_err(std::io::Error::from)
1197}
1198
1199pub fn setpgid(pid: u32, pgid: u32) -> std::io::Result<()> {
1200 nix::unistd::setpgid(
1201 nix::unistd::Pid::from_raw(pid as i32),
1202 nix::unistd::Pid::from_raw(pgid as i32),
1203 )
1204 .map_err(std::io::Error::from)
1205}
1206
1207pub fn setpgrp() -> std::io::Result<()> {
1208 nix::unistd::setpgid(nix::unistd::Pid::from_raw(0), nix::unistd::Pid::from_raw(0))
1209 .map_err(std::io::Error::from)
1210}
1211
1212#[cfg(not(any(target_os = "wasi", target_os = "redox")))]
1213pub fn setsid() -> std::io::Result<()> {
1214 nix::unistd::setsid()
1215 .map(drop)
1216 .map_err(std::io::Error::from)
1217}
1218
1219#[cfg(not(any(target_os = "wasi", target_os = "redox")))]
1220pub fn tcgetpgrp(fd: BorrowedFd<'_>) -> std::io::Result<libc::pid_t> {
1221 nix::unistd::tcgetpgrp(fd)
1222 .map(nix::unistd::Pid::as_raw)
1223 .map_err(std::io::Error::from)
1224}
1225
1226#[cfg(not(any(target_os = "wasi", target_os = "redox")))]
1227pub fn tcsetpgrp(fd: BorrowedFd<'_>, pgid: libc::pid_t) -> std::io::Result<()> {
1228 nix::unistd::tcsetpgrp(fd, nix::unistd::Pid::from_raw(pgid)).map_err(std::io::Error::from)
1229}
1230
1231#[cfg(not(target_os = "redox"))]
1232pub fn getpriority(which: PriorityWhichType, who: PriorityWhoType) -> std::io::Result<i32> {
1233 crate::os::clear_errno();
1234 let retval = unsafe { libc::getpriority(which, who) };
1235 if crate::os::get_errno() != 0 {
1236 Err(std::io::Error::last_os_error())
1237 } else {
1238 Ok(retval)
1239 }
1240}
1241
1242#[cfg(not(target_os = "redox"))]
1243pub fn setpriority(
1244 which: PriorityWhichType,
1245 who: PriorityWhoType,
1246 priority: i32,
1247) -> std::io::Result<()> {
1248 let retval = unsafe { libc::setpriority(which, who, priority) };
1249 if retval == -1 {
1250 Err(std::io::Error::last_os_error())
1251 } else {
1252 Ok(())
1253 }
1254}
1255
1256pub fn waitpid(pid: libc::pid_t, status: &mut i32, opt: i32) -> std::io::Result<libc::pid_t> {
1257 let res = unsafe { libc::waitpid(pid, status, opt) };
1258 if res == -1 {
1259 Err(std::io::Error::last_os_error())
1260 } else {
1261 Ok(res)
1262 }
1263}
1264
1265pub fn wait3(options: i32) -> std::io::Result<(libc::pid_t, i32, crate::resource::RUsage)> {
1267 wait4(-1, options)
1268}
1269
1270pub fn wait4(
1272 pid: libc::pid_t,
1273 options: i32,
1274) -> std::io::Result<(libc::pid_t, i32, crate::resource::RUsage)> {
1275 let mut status = 0;
1276 let mut ru = core::mem::MaybeUninit::<libc::rusage>::zeroed();
1277 let res = unsafe { libc::wait4(pid, &mut status, options, ru.as_mut_ptr()) };
1278 if res == -1 {
1279 return Err(std::io::Error::last_os_error());
1280 }
1281 let ru = if res == 0 {
1282 unsafe { core::mem::zeroed() }
1283 } else {
1284 unsafe { ru.assume_init() }
1285 };
1286 Ok((res, status, ru.into()))
1287}
1288
1289pub fn kill(pid: i32, sig: i32) -> std::io::Result<()> {
1290 let ret = unsafe { libc::kill(pid, sig) };
1291 if ret == -1 {
1292 Err(std::io::Error::last_os_error())
1293 } else {
1294 Ok(())
1295 }
1296}
1297
1298pub fn killpg(pgid: i32, sig: i32) -> std::io::Result<()> {
1304 let ret = unsafe { libc::killpg(pgid, sig) };
1305 if ret == -1 {
1306 Err(std::io::Error::last_os_error())
1307 } else {
1308 Ok(())
1309 }
1310}
1311
1312#[cfg(not(any(target_os = "wasi", target_os = "android")))]
1313pub fn setuid(uid: u32) -> std::io::Result<()> {
1314 nix::unistd::setuid(uid_from_raw(uid)).map_err(std::io::Error::from)
1315}
1316
1317#[cfg(not(any(target_os = "wasi", target_os = "android", target_os = "redox")))]
1318pub fn seteuid(euid: u32) -> std::io::Result<()> {
1319 nix::unistd::seteuid(uid_from_raw(euid)).map_err(std::io::Error::from)
1320}
1321
1322#[cfg(not(any(target_os = "wasi", target_os = "android", target_os = "redox")))]
1323pub fn setreuid(ruid: u32, euid: u32) -> std::io::Result<()> {
1324 let ret = unsafe { libc::setreuid(ruid as libc::uid_t, euid as libc::uid_t) };
1325 nix::Error::result(ret)
1326 .map(drop)
1327 .map_err(std::io::Error::from)
1328}
1329
1330#[cfg(any(
1331 target_os = "android",
1332 target_os = "freebsd",
1333 target_os = "linux",
1334 target_os = "openbsd"
1335))]
1336pub fn setresuid(ruid: u32, euid: u32, suid: u32) -> std::io::Result<()> {
1337 let ret = unsafe {
1338 libc::setresuid(
1339 ruid as libc::uid_t,
1340 euid as libc::uid_t,
1341 suid as libc::uid_t,
1342 )
1343 };
1344 nix::Error::result(ret)
1345 .map(drop)
1346 .map_err(std::io::Error::from)
1347}
1348
1349#[cfg(not(any(target_os = "wasi", target_os = "solaris", target_os = "illumos")))]
1350pub fn login_tty(fd: i32) -> std::io::Result<()> {
1351 let ret = unsafe { libc::login_tty(fd) };
1352 if ret < 0 {
1353 Err(std::io::Error::last_os_error())
1354 } else {
1355 Ok(())
1356 }
1357}
1358
1359#[cfg(any(target_os = "solaris", target_os = "illumos"))]
1360pub fn login_tty(fd: i32) -> std::io::Result<()> {
1361 if unsafe { libc::setsid() } < 0 {
1362 let err = std::io::Error::last_os_error();
1363 if err.raw_os_error() != Some(libc::EPERM) {
1364 return Err(err);
1365 }
1366 }
1367 if unsafe { libc::ioctl(fd, libc::TIOCSCTTY, core::ptr::null::<libc::c_char>()) } < 0 {
1368 return Err(std::io::Error::last_os_error());
1369 }
1370 if unsafe { libc::dup2(fd, 0) } < 0
1371 || unsafe { libc::dup2(fd, 1) } < 0
1372 || unsafe { libc::dup2(fd, 2) } < 0
1373 {
1374 return Err(std::io::Error::last_os_error());
1375 }
1376 if fd > 2 {
1377 let _ = unsafe { libc::close(fd) };
1378 }
1379 Ok(())
1380}
1381
1382#[cfg(not(target_os = "redox"))]
1383pub fn openpty() -> std::io::Result<(OwnedFd, OwnedFd)> {
1384 let pty = nix::pty::openpty(None, None).map_err(std::io::Error::from)?;
1385 set_inheritable(pty.master.as_fd(), false)?;
1386 set_inheritable(pty.slave.as_fd(), false)?;
1387 Ok((pty.master, pty.slave))
1388}
1389
1390#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
1398pub fn forkpty() -> std::io::Result<(pid_t, i32)> {
1399 let mut master_fd: libc::c_int = -1;
1400 let pid = unsafe {
1401 libc::forkpty(
1402 &mut master_fd,
1403 core::ptr::null_mut(),
1404 core::ptr::null_mut(),
1405 core::ptr::null_mut(),
1406 )
1407 };
1408 if pid < 0 {
1409 return Err(std::io::Error::last_os_error());
1410 }
1411 if pid != 0 && master_fd >= 0 {
1412 let fd = unsafe { BorrowedFd::borrow_raw(master_fd) };
1413 set_inheritable(fd, false)?;
1414 }
1415 Ok((pid, master_fd))
1416}
1417
1418pub fn ttyname(fd: BorrowedFd<'_>) -> std::io::Result<OsString> {
1419 nix::unistd::ttyname(fd)
1420 .map(std::path::PathBuf::into_os_string)
1421 .map_err(std::io::Error::from)
1422}
1423
1424pub fn execv(path: &CStr, argv: &[&CStr]) -> std::io::Result<()> {
1425 match nix::unistd::execv(path, argv) {
1426 Ok(never) => match never {},
1427 Err(err) => Err(err.into()),
1428 }
1429}
1430
1431pub fn execve(path: &CStr, argv: &[&CStr], env: &[&CStr]) -> std::io::Result<()> {
1432 match nix::unistd::execve(path, argv, env) {
1433 Ok(never) => match never {},
1434 Err(err) => Err(err.into()),
1435 }
1436}
1437
1438#[cfg(any(target_os = "android", target_os = "linux", target_os = "openbsd"))]
1439pub fn getresuid() -> std::io::Result<(u32, u32, u32)> {
1440 let ret = nix::unistd::getresuid().map_err(std::io::Error::from)?;
1441 Ok((
1442 ret.real.as_raw(),
1443 ret.effective.as_raw(),
1444 ret.saved.as_raw(),
1445 ))
1446}
1447
1448#[cfg(any(target_os = "android", target_os = "linux", target_os = "openbsd"))]
1449pub fn getresgid() -> std::io::Result<(u32, u32, u32)> {
1450 let ret = nix::unistd::getresgid().map_err(std::io::Error::from)?;
1451 Ok((
1452 ret.real.as_raw(),
1453 ret.effective.as_raw(),
1454 ret.saved.as_raw(),
1455 ))
1456}
1457
1458#[cfg(any(target_os = "freebsd", target_os = "linux", target_os = "openbsd"))]
1459pub fn setresgid(rgid: u32, egid: u32, sgid: u32) -> std::io::Result<()> {
1460 let ret = unsafe {
1461 libc::setresgid(
1462 rgid as libc::gid_t,
1463 egid as libc::gid_t,
1464 sgid as libc::gid_t,
1465 )
1466 };
1467 nix::Error::result(ret)
1468 .map(drop)
1469 .map_err(std::io::Error::from)
1470}
1471
1472#[cfg(not(any(target_os = "wasi", target_os = "android", target_os = "redox")))]
1473pub fn setregid(rgid: u32, egid: u32) -> std::io::Result<()> {
1474 let ret = unsafe { libc::setregid(rgid as libc::gid_t, egid as libc::gid_t) };
1475 nix::Error::result(ret)
1476 .map(drop)
1477 .map_err(std::io::Error::from)
1478}
1479
1480#[cfg(any(target_os = "freebsd", target_os = "linux", target_os = "openbsd"))]
1481pub fn initgroups(user: &CStr, gid: u32) -> std::io::Result<()> {
1482 nix::unistd::initgroups(user, gid_from_raw(gid)).map_err(std::io::Error::from)
1483}
1484
1485#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox")))]
1486pub fn setgroups_raw(groups: &[u32]) -> std::io::Result<()> {
1487 let gids = groups.iter().copied().map(gid_from_raw).collect::<Vec<_>>();
1488 nix::unistd::setgroups(&gids).map_err(std::io::Error::from)
1489}
1490
1491pub fn dup_noninheritable(fd: BorrowedFd<'_>) -> std::io::Result<OwnedFd> {
1492 let fd = nix::unistd::dup(fd).map_err(std::io::Error::from)?;
1493 set_inheritable(fd.as_fd(), false)?;
1494 Ok(fd)
1495}
1496
1497pub fn dup2(fd: BorrowedFd<'_>, fd2: OwnedFd, inheritable: bool) -> std::io::Result<OwnedFd> {
1498 let mut fd2 = core::mem::ManuallyDrop::new(fd2);
1499 nix::unistd::dup2(fd, &mut fd2).map_err(std::io::Error::from)?;
1500 let fd2 = core::mem::ManuallyDrop::into_inner(fd2);
1501 if !inheritable {
1502 set_inheritable(fd2.as_fd(), false)?;
1503 }
1504 Ok(fd2)
1505}
1506
1507#[cfg(all(unix, not(target_os = "redox")))]
1508impl FdDirStream {
1509 pub fn from_fd(fd: BorrowedFd<'_>) -> std::io::Result<Self> {
1510 let new_fd = dup_fd(fd)?;
1511 let raw_fd = new_fd.into_raw_fd();
1512 let ptr = unsafe { libc::fdopendir(raw_fd) };
1513 match NonNull::new(ptr) {
1514 Some(ptr) => Ok(Self(ptr)),
1515 None => {
1516 unsafe { libc::close(raw_fd) };
1517 Err(std::io::Error::last_os_error())
1518 }
1519 }
1520 }
1521
1522 pub fn next_entry(&mut self) -> std::io::Result<Option<RawDirEntry>> {
1523 loop {
1524 crate::os::clear_errno();
1525 let ptr = unsafe { libc::readdir(self.0.as_ptr()) };
1526 if ptr.is_null() {
1527 let err = crate::os::get_errno();
1528 return if err == 0 {
1529 Ok(None)
1530 } else {
1531 Err(std::io::Error::from_raw_os_error(err))
1532 };
1533 }
1534
1535 let entry = unsafe { &*ptr };
1536 let name = unsafe { CStr::from_ptr(entry.d_name.as_ptr()) }.to_bytes();
1537 if name == b"." || name == b".." {
1538 continue;
1539 }
1540 #[cfg(target_os = "freebsd")]
1541 let ino = entry.d_fileno as u64;
1542 #[cfg(not(target_os = "freebsd"))]
1543 let ino = entry.d_ino as u64;
1544
1545 return Ok(Some(RawDirEntry {
1546 name: name.to_vec(),
1547 d_type: (entry.d_type != libc::DT_UNKNOWN).then_some(entry.d_type),
1548 ino,
1549 }));
1550 }
1551 }
1552}
1553
1554#[cfg(all(unix, not(target_os = "redox")))]
1555impl Drop for FdDirStream {
1556 fn drop(&mut self) {
1557 unsafe {
1558 libc::rewinddir(self.0.as_ptr());
1559 libc::closedir(self.0.as_ptr());
1560 }
1561 }
1562}
1563
1564#[cfg(all(unix, not(target_os = "redox")))]
1565impl core::fmt::Debug for FdDirStream {
1566 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1567 f.debug_tuple("FdDirStream").field(&self.0).finish()
1568 }
1569}
1570
1571#[cfg(all(unix, not(target_os = "redox")))]
1572unsafe impl Send for FdDirStream {}
1573#[cfg(all(unix, not(target_os = "redox")))]
1574unsafe impl Sync for FdDirStream {}
1575
1576pub fn get_terminal_size(fd: libc::c_int) -> std::io::Result<(u16, u16)> {
1577 let mut w = libc::winsize {
1578 ws_row: 0,
1579 ws_col: 0,
1580 ws_xpixel: 0,
1581 ws_ypixel: 0,
1582 };
1583 let ret = unsafe { libc::ioctl(fd, libc::TIOCGWINSZ, &mut w) };
1584 if ret < 0 {
1585 Err(std::io::Error::last_os_error())
1586 } else {
1587 Ok((w.ws_col, w.ws_row))
1588 }
1589}
1590
1591#[cfg(target_os = "macos")]
1592pub fn full_fsync(fd: BorrowedFd<'_>) -> std::io::Result<()> {
1593 let ret = unsafe { libc::fcntl(fd.as_raw_fd(), libc::F_FULLFSYNC) };
1594 if ret < 0 {
1595 Err(std::io::Error::last_os_error())
1596 } else {
1597 Ok(())
1598 }
1599}
1600
1601#[cfg(all(unix, not(target_os = "redox")))]
1602pub fn madvise(addr: usize, len: usize, advice: i32) -> std::io::Result<()> {
1603 let ret = unsafe { libc::madvise(addr as *mut libc::c_void, len, advice) };
1604 if ret != 0 {
1605 Err(std::io::Error::last_os_error())
1606 } else {
1607 Ok(())
1608 }
1609}
1610
1611pub fn pathconf(path: &CStr, name: i32) -> std::io::Result<Option<libc::c_long>> {
1612 crate::os::clear_errno();
1613 debug_assert_eq!(crate::os::get_errno(), 0);
1614 let raw = unsafe { libc::pathconf(path.as_ptr(), name) };
1615 if raw == -1 {
1616 if crate::os::get_errno() == 0 {
1617 Ok(None)
1618 } else {
1619 Err(std::io::Error::last_os_error())
1620 }
1621 } else {
1622 Ok(Some(raw))
1623 }
1624}
1625
1626pub fn fpathconf(fd: i32, name: i32) -> std::io::Result<Option<libc::c_long>> {
1627 crate::os::clear_errno();
1628 debug_assert_eq!(crate::os::get_errno(), 0);
1629 let raw = unsafe { libc::fpathconf(fd, name) };
1630 if raw == -1 {
1631 if crate::os::get_errno() == 0 {
1632 Ok(None)
1633 } else {
1634 Err(std::io::Error::last_os_error())
1635 }
1636 } else {
1637 Ok(Some(raw))
1638 }
1639}
1640
1641pub fn sysconf(name: i32) -> std::io::Result<libc::c_long> {
1642 crate::os::set_errno(0);
1643 let raw = unsafe { libc::sysconf(name) };
1644 if raw == -1 && crate::os::get_errno() != 0 {
1645 Err(std::io::Error::last_os_error())
1646 } else {
1647 Ok(raw)
1648 }
1649}
1650
1651#[cfg(target_os = "linux")]
1652pub unsafe fn getrandom(
1656 buf: *mut libc::c_void,
1657 buflen: usize,
1658 flags: u32,
1659) -> std::io::Result<usize> {
1660 let len = unsafe { libc::syscall(libc::SYS_getrandom, buf, buflen, flags as usize) as isize };
1661 if len < 0 {
1662 Err(std::io::Error::last_os_error())
1663 } else {
1664 Ok(len as usize)
1665 }
1666}
1667
1668pub fn wcoredump(status: i32) -> bool {
1669 libc::WCOREDUMP(status)
1670}
1671
1672pub fn wifcontinued(status: i32) -> bool {
1673 libc::WIFCONTINUED(status)
1674}
1675
1676pub fn wifstopped(status: i32) -> bool {
1677 libc::WIFSTOPPED(status)
1678}
1679
1680pub fn wifsignaled(status: i32) -> bool {
1681 libc::WIFSIGNALED(status)
1682}
1683
1684pub fn wifexited(status: i32) -> bool {
1685 libc::WIFEXITED(status)
1686}
1687
1688pub fn wexitstatus(status: i32) -> i32 {
1689 libc::WEXITSTATUS(status)
1690}
1691
1692pub fn wstopsig(status: i32) -> i32 {
1693 libc::WSTOPSIG(status)
1694}
1695
1696pub fn wtermsig(status: i32) -> i32 {
1697 libc::WTERMSIG(status)
1698}
1699
1700#[cfg(target_os = "linux")]
1701pub fn pidfd_open(pid: libc::pid_t, flags: u32) -> std::io::Result<OwnedFd> {
1702 let fd = unsafe { libc::syscall(libc::SYS_pidfd_open, pid, flags) as libc::c_long };
1703 if fd == -1 {
1704 Err(std::io::Error::last_os_error())
1705 } else {
1706 Ok(unsafe { OwnedFd::from_raw_fd(fd as libc::c_int) })
1707 }
1708}
1709
1710#[cfg(any(
1711 target_os = "android",
1712 target_os = "freebsd",
1713 target_os = "linux",
1714 target_os = "openbsd"
1715))]
1716pub fn getgrouplist(user: &CStr, gid: u32) -> std::io::Result<Vec<u32>> {
1717 nix::unistd::getgrouplist(user, gid_from_raw(gid))
1718 .map(|groups| groups.into_iter().map(|gid| gid.as_raw()).collect())
1719 .map_err(std::io::Error::from)
1720}
1721
1722#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1723pub fn validate_posix_spawn_signal(sig: i32) -> bool {
1724 nix::sys::signal::Signal::try_from(sig).is_ok()
1725}
1726
1727#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1728pub const fn supports_posix_spawn_setsid() -> bool {
1729 cfg!(any(
1730 target_os = "linux",
1731 target_os = "haiku",
1732 target_os = "solaris",
1733 target_os = "illumos",
1734 target_os = "hurd",
1735 ))
1736}
1737
1738#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1739fn build_posix_spawn_file_actions(
1740 actions: &[PosixSpawnFileAction],
1741) -> std::io::Result<nix::spawn::PosixSpawnFileActions> {
1742 let mut file_actions =
1743 nix::spawn::PosixSpawnFileActions::init().map_err(std::io::Error::from)?;
1744 for action in actions {
1745 match action {
1746 PosixSpawnFileAction::Open {
1747 fd,
1748 path,
1749 oflag,
1750 mode,
1751 } => file_actions
1752 .add_open(
1753 *fd,
1754 path.as_c_str(),
1755 nix::fcntl::OFlag::from_bits_retain(*oflag),
1756 nix::sys::stat::Mode::from_bits_retain(*mode as libc::mode_t),
1757 )
1758 .map_err(std::io::Error::from)?,
1759 PosixSpawnFileAction::Close { fd } => {
1760 file_actions.add_close(*fd).map_err(std::io::Error::from)?
1761 }
1762 PosixSpawnFileAction::Dup2 { fd, newfd } => file_actions
1763 .add_dup2(*fd, *newfd)
1764 .map_err(std::io::Error::from)?,
1765 }
1766 }
1767 Ok(file_actions)
1768}
1769
1770#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1771fn build_sigset(signals: &[i32]) -> nix::sys::signal::SigSet {
1772 let mut set = nix::sys::signal::SigSet::empty();
1773 for &sig in signals {
1774 let sig = nix::sys::signal::Signal::try_from(sig).expect("validated signal");
1775 set.add(sig);
1776 }
1777 set
1778}
1779
1780#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1781fn build_posix_spawn_attrs(
1782 config: &PosixSpawnConfig<'_>,
1783) -> std::io::Result<nix::spawn::PosixSpawnAttr> {
1784 let mut attrp = nix::spawn::PosixSpawnAttr::init().map_err(std::io::Error::from)?;
1785 let mut flags = nix::spawn::PosixSpawnFlags::empty();
1786
1787 if let Some(sigs) = config.setsigdef {
1788 let set = build_sigset(sigs);
1789 attrp.set_sigdefault(&set).map_err(std::io::Error::from)?;
1790 flags.insert(nix::spawn::PosixSpawnFlags::POSIX_SPAWN_SETSIGDEF);
1791 }
1792
1793 if let Some(pgid) = config.setpgroup {
1794 attrp
1795 .set_pgroup(nix::unistd::Pid::from_raw(pgid))
1796 .map_err(std::io::Error::from)?;
1797 flags.insert(nix::spawn::PosixSpawnFlags::POSIX_SPAWN_SETPGROUP);
1798 }
1799
1800 if config.resetids {
1801 flags.insert(nix::spawn::PosixSpawnFlags::POSIX_SPAWN_RESETIDS);
1802 }
1803
1804 if config.setsid {
1805 #[cfg(any(
1806 target_os = "linux",
1807 target_os = "haiku",
1808 target_os = "solaris",
1809 target_os = "illumos",
1810 target_os = "hurd",
1811 ))]
1812 {
1813 #[allow(clippy::useless_conversion)]
1814 flags.insert(nix::spawn::PosixSpawnFlags::from_bits_retain(
1815 libc::POSIX_SPAWN_SETSID.into(),
1816 ));
1817 }
1818 #[cfg(not(any(
1819 target_os = "linux",
1820 target_os = "haiku",
1821 target_os = "solaris",
1822 target_os = "illumos",
1823 target_os = "hurd",
1824 )))]
1825 {
1826 return Err(std::io::Error::new(
1827 std::io::ErrorKind::Unsupported,
1828 "setsid parameter is not supported on this platform",
1829 ));
1830 }
1831 }
1832
1833 if let Some(sigs) = config.setsigmask {
1834 let set = build_sigset(sigs);
1835 attrp.set_sigmask(&set).map_err(std::io::Error::from)?;
1836 flags.insert(nix::spawn::PosixSpawnFlags::POSIX_SPAWN_SETSIGMASK);
1837 }
1838
1839 #[cfg(all(
1840 any(target_os = "linux", target_os = "freebsd", target_os = "android"),
1841 not(target_env = "musl")
1842 ))]
1843 if let Some(scheduler) = &config.scheduler {
1844 let attr_ptr = (&raw mut attrp).cast::<libc::posix_spawnattr_t>();
1847 if let Some(policy) = scheduler.policy {
1848 let err = unsafe { libc::posix_spawnattr_setschedpolicy(attr_ptr, policy) };
1849 if err != 0 {
1850 return Err(std::io::Error::from_raw_os_error(err));
1851 }
1852 flags.insert(nix::spawn::PosixSpawnFlags::from_bits_retain(
1853 libc::POSIX_SPAWN_SETSCHEDULER,
1854 ));
1855 }
1856 let err = unsafe { libc::posix_spawnattr_setschedparam(attr_ptr, &scheduler.param) };
1857 if err != 0 {
1858 return Err(std::io::Error::from_raw_os_error(err));
1859 }
1860 flags.insert(nix::spawn::PosixSpawnFlags::from_bits_retain(
1861 libc::POSIX_SPAWN_SETSCHEDPARAM,
1862 ));
1863 }
1864
1865 if !flags.is_empty() {
1866 attrp.set_flags(flags).map_err(std::io::Error::from)?;
1867 }
1868
1869 Ok(attrp)
1870}
1871
1872#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1873pub fn posix_spawn(config: PosixSpawnConfig<'_>) -> std::io::Result<libc::pid_t> {
1874 let file_actions = build_posix_spawn_file_actions(config.file_actions)?;
1875 let attrp = build_posix_spawn_attrs(&config)?;
1876 let pid = if config.spawnp {
1877 nix::spawn::posix_spawnp(config.path, &file_actions, &attrp, config.args, config.env)
1878 } else {
1879 nix::spawn::posix_spawn(config.path, &file_actions, &attrp, config.args, config.env)
1880 }
1881 .map_err(std::io::Error::from)?;
1882 Ok(pid.into())
1883}
1884
1885#[cfg(target_os = "linux")]
1886pub fn sendfile(
1887 out_fd: BorrowedFd<'_>,
1888 in_fd: BorrowedFd<'_>,
1889 offset: &mut crate::crt_fd::Offset,
1890 count: usize,
1891) -> std::io::Result<usize> {
1892 nix::sys::sendfile::sendfile(out_fd, in_fd, Some(offset), count).map_err(std::io::Error::from)
1893}
1894
1895#[cfg(target_os = "macos")]
1896pub fn sendfile(
1897 in_fd: BorrowedFd<'_>,
1898 out_fd: BorrowedFd<'_>,
1899 offset: crate::crt_fd::Offset,
1900 count: i64,
1901 headers: Option<&[&[u8]]>,
1902 trailers: Option<&[&[u8]]>,
1903) -> (std::io::Result<()>, i64) {
1904 let (res, written) =
1905 nix::sys::sendfile::sendfile(in_fd, out_fd, offset, Some(count), headers, trailers);
1906 (res.map_err(std::io::Error::from), written)
1907}
1908
1909#[cfg(any(
1910 target_os = "android",
1911 target_os = "freebsd",
1912 target_os = "linux",
1913 target_os = "netbsd"
1914))]
1915pub fn sched_getscheduler(pid: libc::pid_t) -> std::io::Result<i32> {
1916 let policy = unsafe { libc::sched_getscheduler(pid) };
1917 if policy == -1 {
1918 Err(std::io::Error::last_os_error())
1919 } else {
1920 Ok(policy)
1921 }
1922}
1923
1924#[cfg(all(
1925 not(target_env = "musl"),
1926 any(
1927 target_os = "android",
1928 target_os = "freebsd",
1929 target_os = "linux",
1930 target_os = "netbsd"
1931 )
1932))]
1933pub fn sched_setscheduler(
1934 pid: i32,
1935 policy: i32,
1936 param: &libc::sched_param,
1937) -> std::io::Result<i32> {
1938 let ret = unsafe { libc::sched_setscheduler(pid, policy, param) };
1939 if ret == -1 {
1940 Err(std::io::Error::last_os_error())
1941 } else {
1942 Ok(ret)
1943 }
1944}
1945
1946#[cfg(any(
1947 target_os = "android",
1948 target_os = "freebsd",
1949 target_os = "linux",
1950 target_os = "netbsd"
1951))]
1952pub fn sched_getparam(pid: libc::pid_t) -> std::io::Result<libc::sched_param> {
1953 let mut param = core::mem::MaybeUninit::uninit();
1954 let ret = unsafe { libc::sched_getparam(pid, param.as_mut_ptr()) };
1955 if ret == -1 {
1956 Err(std::io::Error::last_os_error())
1957 } else {
1958 Ok(unsafe { param.assume_init() })
1959 }
1960}
1961
1962#[cfg(all(
1963 not(target_env = "musl"),
1964 any(
1965 target_os = "android",
1966 target_os = "freebsd",
1967 target_os = "linux",
1968 target_os = "netbsd"
1969 )
1970))]
1971pub fn sched_setparam(pid: i32, param: &libc::sched_param) -> std::io::Result<i32> {
1972 let ret = unsafe { libc::sched_setparam(pid, param) };
1973 if ret == -1 {
1974 Err(std::io::Error::last_os_error())
1975 } else {
1976 Ok(ret)
1977 }
1978}
1979
1980#[allow(clippy::not_unsafe_ptr_arg_deref)]
1981pub fn exec_replace<T: AsRef<CStr>>(
1982 exec_list: &[T],
1983 argv: *const *const libc::c_char,
1984 envp: Option<*const *const libc::c_char>,
1985) -> nix::errno::Errno {
1986 let mut first_err = None;
1987 for exec in exec_list {
1988 if let Some(envp) = envp {
1989 unsafe { libc::execve(exec.as_ref().as_ptr(), argv, envp) };
1990 } else {
1991 unsafe { libc::execv(exec.as_ref().as_ptr(), argv) };
1992 }
1993 let e = nix::errno::Errno::last();
1994 if e != nix::errno::Errno::ENOENT && e != nix::errno::Errno::ENOTDIR && first_err.is_none()
1995 {
1996 first_err = Some(e);
1997 }
1998 }
1999 first_err.unwrap_or_else(nix::errno::Errno::last)
2000}
2001
2002fn should_keep(above: i32, keep: &[BorrowedFd<'_>], fd: i32) -> bool {
2003 fd > above
2004 && keep
2005 .binary_search_by_key(&fd, BorrowedFd::as_raw_fd)
2006 .is_err()
2007}
2008
2009#[cfg(not(target_os = "redox"))]
2010fn close_dir_fds(above: i32, keep: &[BorrowedFd<'_>]) -> nix::Result<()> {
2011 use nix::{dir::Dir, fcntl::OFlag};
2012 use std::os::fd::AsRawFd;
2013
2014 #[cfg(any(
2015 target_os = "dragonfly",
2016 target_os = "freebsd",
2017 target_os = "netbsd",
2018 target_os = "openbsd",
2019 target_vendor = "apple",
2020 ))]
2021 let fd_dir_name = c"/dev/fd";
2022
2023 #[cfg(any(target_os = "linux", target_os = "android"))]
2024 let fd_dir_name = c"/proc/self/fd";
2025
2026 let mut dir = Dir::open(
2027 fd_dir_name,
2028 OFlag::O_RDONLY | OFlag::O_DIRECTORY,
2029 nix::sys::stat::Mode::empty(),
2030 )?;
2031 let dirfd = dir.as_raw_fd();
2032 'outer: for e in dir.iter() {
2033 let e = e?;
2034 let mut parser = IntParser::default();
2035 for &c in e.file_name().to_bytes() {
2036 if parser.feed(c).is_err() {
2037 continue 'outer;
2038 }
2039 }
2040 let fd = parser.num;
2041 if fd != dirfd && should_keep(above, keep, fd) {
2042 let _ = nix::unistd::close(fd);
2043 }
2044 }
2045 Ok(())
2046}
2047
2048#[cfg(target_os = "redox")]
2049fn close_filetable_fds(above: i32, keep: &[BorrowedFd<'_>]) -> nix::Result<()> {
2050 use nix::fcntl;
2051 use std::os::fd::AsRawFd;
2052
2053 let filetable = fcntl::open(
2054 c"/scheme/thisproc/current/filetable",
2055 fcntl::OFlag::O_RDONLY,
2056 nix::sys::stat::Mode::empty(),
2057 )?;
2058 let read_one = || -> nix::Result<_> {
2059 let mut byte = 0;
2060 let n = nix::unistd::read(&filetable, std::slice::from_mut(&mut byte))?;
2061 Ok((n > 0).then_some(byte))
2062 };
2063 while let Some(c) = read_one()? {
2064 let mut parser = IntParser::default();
2065 if parser.feed(c).is_err() {
2066 continue;
2067 }
2068 let done = loop {
2069 let Some(c) = read_one()? else { break true };
2070 if parser.feed(c).is_err() {
2071 break false;
2072 }
2073 };
2074
2075 let fd = parser.num;
2076 if fd != filetable.as_raw_fd() && should_keep(above, keep, fd) {
2077 let _ = nix::unistd::close(fd);
2078 }
2079 if done {
2080 break;
2081 }
2082 }
2083 Ok(())
2084}
2085
2086fn close_fds_brute_force(above: i32, keep: &[BorrowedFd<'_>]) {
2087 debug_assert!(
2088 keep.windows(2)
2089 .all(|fds| fds[0].as_raw_fd() <= fds[1].as_raw_fd()),
2090 "close_fds_brute_force requires `keep` to be sorted ascending"
2091 );
2092
2093 let max_fd = nix::unistd::sysconf(nix::unistd::SysconfVar::OPEN_MAX)
2094 .ok()
2095 .flatten()
2096 .unwrap_or(256) as i32;
2097
2098 let mut prev = above;
2099 for fd in keep
2100 .iter()
2101 .map(BorrowedFd::as_raw_fd)
2102 .chain(core::iter::once(max_fd))
2103 {
2104 for candidate in prev + 1..fd {
2105 unsafe { libc::close(candidate) };
2106 }
2107 prev = fd;
2108 }
2109}
2110
2111#[derive(Default)]
2112struct IntParser {
2113 num: i32,
2114}
2115
2116struct NonDigit;
2117
2118impl IntParser {
2119 fn feed(&mut self, c: u8) -> Result<(), NonDigit> {
2120 let digit = (c as char).to_digit(10).ok_or(NonDigit)?;
2121 self.num *= 10;
2122 self.num += digit as i32;
2123 Ok(())
2124 }
2125}