use alloc::ffi::CString;
use alloc::vec::Vec;
use core::ffi::CStr;
#[cfg(all(unix, not(target_os = "redox")))]
use core::ptr::NonNull;
use std::ffi::{OsStr, OsString};
#[cfg(target_os = "linux")]
use std::os::fd::FromRawFd;
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, IntoRawFd, OwnedFd};
use std::path::Path;
pub use super::posix_unix_like::*;
pub use libc::{c_char, pid_t};
pub const EX_OK: i32 = 0;
pub const EX_USAGE: i32 = 64;
pub const EX_DATAERR: i32 = 65;
pub const EX_NOINPUT: i32 = 66;
pub const EX_NOUSER: i32 = 67;
pub const EX_NOHOST: i32 = 68;
pub const EX_UNAVAILABLE: i32 = 69;
pub const EX_SOFTWARE: i32 = 70;
pub const EX_OSERR: i32 = 71;
pub const EX_OSFILE: i32 = 72;
pub const EX_CANTCREAT: i32 = 73;
pub const EX_IOERR: i32 = 74;
pub const EX_TEMPFAIL: i32 = 75;
pub const EX_PROTOCOL: i32 = 76;
pub const EX_NOPERM: i32 = 77;
pub const EX_CONFIG: i32 = 78;
#[cfg(any(target_os = "android", target_os = "redox", unix))]
pub use libc::{PRIO_PGRP, PRIO_PROCESS, PRIO_USER};
#[cfg(target_os = "macos")]
pub use libc::{
COPYFILE_ACL, COPYFILE_DATA, COPYFILE_STAT, COPYFILE_XATTR, PRIO_DARWIN_BG, PRIO_DARWIN_NONUI,
PRIO_DARWIN_PROCESS, PRIO_DARWIN_THREAD, TMP_MAX,
};
#[cfg(target_os = "linux")]
pub use libc::PIDFD_NONBLOCK;
#[cfg(any(target_os = "android", target_os = "linux"))]
pub use libc::{
CLONE_FILES, CLONE_FS, CLONE_NEWCGROUP, CLONE_NEWIPC, CLONE_NEWNET, CLONE_NEWNS, CLONE_NEWPID,
CLONE_NEWUSER, CLONE_NEWUTS, CLONE_SIGHAND, CLONE_SYSVSEM, CLONE_THREAD, CLONE_VM,
MFD_HUGE_SHIFT, P_PIDFD, SCHED_BATCH, SCHED_DEADLINE, SCHED_IDLE, SCHED_NORMAL,
SCHED_RESET_ON_FORK, SPLICE_F_MORE, SPLICE_F_MOVE, SPLICE_F_NONBLOCK,
};
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "netbsd"))]
pub use libc::{XATTR_CREATE, XATTR_REPLACE};
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
pub use libc::{
MFD_ALLOW_SEALING, MFD_CLOEXEC, MFD_HUGE_MASK, MFD_HUGETLB, POSIX_FADV_DONTNEED,
POSIX_FADV_NOREUSE, POSIX_FADV_NORMAL, POSIX_FADV_RANDOM, POSIX_FADV_SEQUENTIAL,
POSIX_FADV_WILLNEED,
};
#[cfg(any(target_os = "android", target_os = "linux", target_os = "redox", unix))]
pub use libc::{RTLD_LAZY, RTLD_NOW, WNOHANG};
#[cfg(any(target_os = "android", target_os = "macos", target_os = "redox", unix))]
pub use libc::RTLD_GLOBAL;
#[cfg(any(
target_os = "android",
target_os = "freebsd",
target_os = "linux",
target_os = "netbsd"
))]
pub use libc::{
EFD_CLOEXEC, EFD_NONBLOCK, EFD_SEMAPHORE, TFD_CLOEXEC, TFD_NONBLOCK, TFD_TIMER_ABSTIME,
TFD_TIMER_CANCEL_ON_SET,
};
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "linux",
target_os = "netbsd"
))]
pub use libc::{GRND_NONBLOCK, GRND_RANDOM};
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "linux",
target_os = "macos",
target_os = "netbsd"
))]
pub use libc::SCHED_OTHER;
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "linux",
target_os = "macos"
))]
pub use libc::{RTLD_NODELETE, SEEK_DATA, SEEK_HOLE};
#[cfg(any(
target_os = "android",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "redox",
unix
))]
pub use libc::RTLD_LOCAL;
#[cfg(any(
target_os = "android",
target_os = "freebsd",
target_os = "linux",
target_os = "netbsd",
target_os = "redox",
unix
))]
pub use libc::WUNTRACED;
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "linux",
target_os = "macos",
target_os = "netbsd"
))]
pub use libc::{
CLD_CONTINUED, CLD_DUMPED, CLD_EXITED, CLD_KILLED, CLD_STOPPED, CLD_TRAPPED, P_ALL, P_PGID,
P_PID, SCHED_FIFO, SCHED_RR,
};
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "redox"
))]
pub use libc::{RTLD_NOLOAD, WEXITED, WNOWAIT, WSTOPPED};
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "redox",
unix
))]
pub use libc::WCONTINUED;
#[cfg(all(target_os = "linux", target_env = "gnu"))]
pub use libc::RTLD_DEEPBIND;
#[cfg(unix)]
pub use libc::{UTIME_NOW, UTIME_OMIT};
#[cfg(target_os = "freebsd")]
pub use libc::{SF_MNOWAIT, SF_NOCACHE, SF_NODISKIO, SF_SYNC};
#[cfg(unix)]
pub use libc::{
_PC_CHOWN_RESTRICTED, _PC_LINK_MAX, _PC_MAX_CANON, _PC_MAX_INPUT, _PC_NAME_MAX, _PC_NO_TRUNC,
_PC_PATH_MAX, _PC_PIPE_BUF, _PC_VDISABLE, _SC_ARG_MAX, _SC_CHILD_MAX, _SC_CLK_TCK,
_SC_LOGIN_NAME_MAX, _SC_NGROUPS_MAX, _SC_OPEN_MAX, _SC_PAGE_SIZE, _SC_RE_DUP_MAX,
_SC_STREAM_MAX, _SC_TTY_NAME_MAX, _SC_TZNAME_MAX, _SC_VERSION,
};
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "linux",
target_os = "netbsd",
target_os = "openbsd",
target_os = "redox"
))]
pub use libc::_PC_FILESIZEBITS;
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "illumos",
target_os = "linux",
target_os = "netbsd",
target_os = "openbsd",
target_os = "redox",
target_os = "solaris"
))]
pub use libc::_PC_2_SYMLINKS;
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "linux",
target_os = "openbsd",
target_os = "redox"
))]
pub use libc::_PC_ALLOC_SIZE_MIN;
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "linux",
target_os = "openbsd"
))]
pub use libc::_PC_REC_INCR_XFER_SIZE;
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "linux",
target_os = "openbsd",
target_os = "redox"
))]
pub use libc::{_PC_REC_MAX_XFER_SIZE, _PC_REC_MIN_XFER_SIZE, _PC_REC_XFER_ALIGN};
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "illumos",
target_os = "linux",
target_os = "netbsd",
target_os = "openbsd",
target_os = "redox",
target_os = "solaris"
))]
pub use libc::_PC_SYMLINK_MAX;
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "illumos",
target_os = "linux",
target_os = "openbsd",
target_os = "redox",
target_os = "solaris"
))]
pub use libc::{_PC_ASYNC_IO, _PC_PRIO_IO};
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "illumos",
target_os = "linux",
target_os = "netbsd",
target_os = "openbsd",
target_os = "redox",
target_os = "solaris"
))]
pub use libc::_PC_SYNC_IO;
#[cfg(any(target_os = "dragonfly", target_os = "openbsd"))]
pub use libc::_PC_TIMESTAMP_RESOLUTION;
#[cfg(all(unix, not(target_os = "redox")))]
pub use libc::{
_SC_2_C_BIND, _SC_2_C_DEV, _SC_2_CHAR_TERM, _SC_2_FORT_DEV, _SC_2_FORT_RUN, _SC_2_LOCALEDEF,
_SC_2_SW_DEV, _SC_2_UPE, _SC_2_VERSION, _SC_AIO_LISTIO_MAX, _SC_AIO_MAX,
_SC_AIO_PRIO_DELTA_MAX, _SC_ASYNCHRONOUS_IO, _SC_ATEXIT_MAX, _SC_BC_BASE_MAX, _SC_BC_DIM_MAX,
_SC_BC_SCALE_MAX, _SC_BC_STRING_MAX, _SC_COLL_WEIGHTS_MAX, _SC_DELAYTIMER_MAX,
_SC_EXPR_NEST_MAX, _SC_FSYNC, _SC_GETGR_R_SIZE_MAX, _SC_GETPW_R_SIZE_MAX, _SC_IOV_MAX,
_SC_JOB_CONTROL, _SC_LINE_MAX, _SC_MAPPED_FILES, _SC_MEMLOCK, _SC_MEMLOCK_RANGE,
_SC_MEMORY_PROTECTION, _SC_MESSAGE_PASSING, _SC_MQ_OPEN_MAX, _SC_MQ_PRIO_MAX,
_SC_NPROCESSORS_CONF, _SC_NPROCESSORS_ONLN, _SC_PHYS_PAGES, _SC_PRIORITIZED_IO,
_SC_PRIORITY_SCHEDULING, _SC_REALTIME_SIGNALS, _SC_RTSIG_MAX, _SC_SAVED_IDS, _SC_SEM_NSEMS_MAX,
_SC_SEM_VALUE_MAX, _SC_SEMAPHORES, _SC_SHARED_MEMORY_OBJECTS, _SC_SIGQUEUE_MAX,
_SC_SYNCHRONIZED_IO, _SC_THREAD_ATTR_STACKADDR, _SC_THREAD_ATTR_STACKSIZE,
_SC_THREAD_DESTRUCTOR_ITERATIONS, _SC_THREAD_KEYS_MAX, _SC_THREAD_PRIO_INHERIT,
_SC_THREAD_PRIO_PROTECT, _SC_THREAD_PRIORITY_SCHEDULING, _SC_THREAD_PROCESS_SHARED,
_SC_THREAD_SAFE_FUNCTIONS, _SC_THREAD_STACK_MIN, _SC_THREAD_THREADS_MAX, _SC_THREADS,
_SC_TIMER_MAX, _SC_TIMERS, _SC_XOPEN_CRYPT, _SC_XOPEN_ENH_I18N, _SC_XOPEN_LEGACY,
_SC_XOPEN_REALTIME, _SC_XOPEN_REALTIME_THREADS, _SC_XOPEN_SHM, _SC_XOPEN_UNIX,
_SC_XOPEN_VERSION, _SC_XOPEN_XCU_VERSION,
};
#[cfg(any(
target_os = "linux",
target_vendor = "apple",
target_os = "netbsd",
target_os = "fuchsia"
))]
pub use libc::{
_SC_PASS_MAX, _SC_XBS5_ILP32_OFF32, _SC_XBS5_ILP32_OFFBIG, _SC_XBS5_LP64_OFF64,
_SC_XBS5_LPBIG_OFFBIG,
};
#[cfg(target_os = "redox")]
pub use libc::{_SC_HOST_NAME_MAX, _SC_SYMLOOP_MAX};
pub struct UnameInfo {
pub sysname: String,
pub nodename: String,
pub release: String,
pub version: String,
pub machine: String,
}
#[derive(Debug)]
pub struct UnameDecodeError {
pub bytes: Vec<u8>,
pub error: core::str::Utf8Error,
}
#[cfg(all(unix, not(target_os = "redox")))]
#[derive(Clone, Copy, Debug)]
pub struct StatVfsInfo {
pub f_bsize: libc::c_ulong,
pub f_frsize: libc::c_ulong,
pub f_blocks: libc::fsblkcnt_t,
pub f_bfree: libc::fsblkcnt_t,
pub f_bavail: libc::fsblkcnt_t,
pub f_files: libc::fsfilcnt_t,
pub f_ffree: libc::fsfilcnt_t,
pub f_favail: libc::fsfilcnt_t,
pub f_flag: libc::c_ulong,
pub f_namemax: libc::c_ulong,
pub f_fsid: libc::c_ulong,
}
#[cfg(all(unix, not(target_os = "redox")))]
#[derive(Clone, Debug)]
pub struct RawDirEntry {
pub name: Vec<u8>,
pub d_type: Option<u8>,
pub ino: u64,
}
#[cfg(all(unix, not(target_os = "redox")))]
pub struct FdDirStream(NonNull<libc::DIR>);
#[cfg(all(target_os = "linux", target_env = "gnu"))]
pub type PriorityWhichType = libc::__priority_which_t;
#[cfg(not(all(target_os = "linux", target_env = "gnu")))]
pub type PriorityWhichType = libc::c_int;
#[cfg(target_os = "freebsd")]
pub type PriorityWhoType = i32;
#[cfg(not(target_os = "freebsd"))]
pub type PriorityWhoType = u32;
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
#[derive(Clone, Debug)]
pub enum PosixSpawnFileAction {
Open {
fd: i32,
path: CString,
oflag: i32,
mode: u32,
},
Close {
fd: i32,
},
Dup2 {
fd: i32,
newfd: i32,
},
}
#[cfg(all(
any(target_os = "linux", target_os = "freebsd", target_os = "android"),
not(target_env = "musl")
))]
pub struct PosixSpawnScheduler {
pub policy: Option<i32>,
pub param: libc::sched_param,
}
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
pub struct PosixSpawnConfig<'a> {
pub path: &'a CStr,
pub args: &'a [CString],
pub env: &'a [CString],
pub file_actions: &'a [PosixSpawnFileAction],
pub setsigdef: Option<&'a [i32]>,
pub setpgroup: Option<libc::pid_t>,
pub resetids: bool,
pub setsid: bool,
pub setsigmask: Option<&'a [i32]>,
pub spawnp: bool,
#[cfg(all(
any(target_os = "linux", target_os = "freebsd", target_os = "android"),
not(target_env = "musl")
))]
pub scheduler: Option<PosixSpawnScheduler>,
}
pub fn set_inheritable(fd: BorrowedFd<'_>, inheritable: bool) -> std::io::Result<()> {
use nix::fcntl;
let flags = fcntl::FdFlag::from_bits_truncate(
fcntl::fcntl(fd, fcntl::FcntlArg::F_GETFD).map_err(std::io::Error::from)?,
);
let mut new_flags = flags;
new_flags.set(fcntl::FdFlag::FD_CLOEXEC, !inheritable);
if flags != new_flags {
fcntl::fcntl(fd, fcntl::FcntlArg::F_SETFD(new_flags)).map_err(std::io::Error::from)?;
}
Ok(())
}
pub fn is_session_leader() -> bool {
unsafe { libc::getsid(0) == libc::getpid() }
}
pub fn getpid() -> libc::pid_t {
unsafe { libc::getpid() }
}
#[cfg(all(unix, not(target_os = "redox")))]
pub fn dup_fd(fd: BorrowedFd<'_>) -> std::io::Result<std::os::fd::OwnedFd> {
nix::unistd::dup(fd).map_err(std::io::Error::from)
}
#[cfg(not(target_os = "redox"))]
pub fn symlinkat(src: &CStr, dir_fd: BorrowedFd<'_>, dst: &CStr) -> std::io::Result<()> {
nix::unistd::symlinkat(src, dir_fd, dst).map_err(std::io::Error::from)
}
#[cfg(target_os = "redox")]
pub fn symlink(src: &CStr, dst: &CStr) -> std::io::Result<()> {
let ret = unsafe { libc::symlink(src.as_ptr(), dst.as_ptr()) };
if ret < 0 {
Err(std::io::Error::last_os_error())
} else {
Ok(())
}
}
#[cfg(not(target_os = "redox"))]
pub fn chroot(path: &Path) -> std::io::Result<()> {
nix::unistd::chroot(path).map_err(std::io::Error::from)
}
#[cfg(all(unix, not(target_os = "redox")))]
pub fn fchmodat(dirfd: i32, path: &CStr, mode: libc::mode_t, flags: i32) -> std::io::Result<()> {
let ret = unsafe { libc::fchmodat(dirfd, path.as_ptr(), mode, flags) };
if ret == 0 {
Ok(())
} else {
Err(std::io::Error::last_os_error())
}
}
#[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "netbsd"))]
pub fn lchmod(path: &CStr, mode: libc::mode_t) -> std::io::Result<()> {
unsafe extern "C" {
fn lchmod(path: *const libc::c_char, mode: libc::mode_t) -> libc::c_int;
}
if unsafe { lchmod(path.as_ptr(), mode) } == 0 {
Ok(())
} else {
Err(std::io::Error::last_os_error())
}
}
#[cfg(target_os = "macos")]
pub fn fcopyfile(in_fd: i32, out_fd: i32, flags: u32) -> std::io::Result<()> {
unsafe extern "C" {
fn fcopyfile(
in_fd: libc::c_int,
out_fd: libc::c_int,
state: *mut libc::c_void,
flags: u32,
) -> libc::c_int;
}
let ret = unsafe { fcopyfile(in_fd, out_fd, core::ptr::null_mut(), flags) };
if ret < 0 {
Err(std::io::Error::last_os_error())
} else {
Ok(())
}
}
#[cfg(unix)]
pub fn link_paths(
src_dir_fd: i32,
src: &CStr,
dst_dir_fd: i32,
dst: &CStr,
follow_symlinks: bool,
) -> std::io::Result<()> {
let flags = if follow_symlinks {
libc::AT_SYMLINK_FOLLOW
} else {
0
};
let ret = unsafe { libc::linkat(src_dir_fd, src.as_ptr(), dst_dir_fd, dst.as_ptr(), flags) };
if ret != 0 {
Err(std::io::Error::last_os_error())
} else {
Ok(())
}
}
#[cfg(all(unix, not(target_os = "redox")))]
fn statvfs_info_from_raw(st: libc::statvfs) -> StatVfsInfo {
let f_fsid = {
let ptr = core::ptr::addr_of!(st.f_fsid) as *const u8;
let size = core::mem::size_of_val(&st.f_fsid);
if size >= 8 {
let bytes = unsafe { core::slice::from_raw_parts(ptr, 8) };
u64::from_ne_bytes([
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
]) as libc::c_ulong
} else if size >= 4 {
let bytes = unsafe { core::slice::from_raw_parts(ptr, 4) };
u32::from_ne_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) as libc::c_ulong
} else {
0
}
};
StatVfsInfo {
f_bsize: st.f_bsize,
f_frsize: st.f_frsize,
f_blocks: st.f_blocks,
f_bfree: st.f_bfree,
f_bavail: st.f_bavail,
f_files: st.f_files,
f_ffree: st.f_ffree,
f_favail: st.f_favail,
f_flag: st.f_flag,
f_namemax: st.f_namemax,
f_fsid,
}
}
#[cfg(all(unix, not(target_os = "redox")))]
pub fn statvfs_path(path: &CStr) -> std::io::Result<StatVfsInfo> {
let mut st: libc::statvfs = unsafe { core::mem::zeroed() };
let ret = unsafe { libc::statvfs(path.as_ptr(), &mut st) };
if ret != 0 {
Err(std::io::Error::last_os_error())
} else {
Ok(statvfs_info_from_raw(st))
}
}
#[cfg(all(unix, not(target_os = "redox")))]
pub fn statvfs_fd(fd: i32) -> std::io::Result<StatVfsInfo> {
let mut st: libc::statvfs = unsafe { core::mem::zeroed() };
let ret = unsafe { libc::fstatvfs(fd, &mut st) };
if ret != 0 {
Err(std::io::Error::last_os_error())
} else {
Ok(statvfs_info_from_raw(st))
}
}
#[cfg(not(target_os = "redox"))]
pub fn mknod(path: &CStr, mode: libc::mode_t, device: libc::dev_t) -> std::io::Result<()> {
let ret = unsafe { libc::mknod(path.as_ptr(), mode, device) };
if ret == 0 {
Ok(())
} else {
Err(std::io::Error::last_os_error())
}
}
#[cfg(all(not(target_os = "redox"), not(target_vendor = "apple")))]
pub fn mknodat(
dir_fd: i32,
path: &CStr,
mode: libc::mode_t,
device: libc::dev_t,
) -> std::io::Result<()> {
let ret = unsafe { libc::mknodat(dir_fd, path.as_ptr(), mode, device) };
if ret == 0 {
Ok(())
} else {
Err(std::io::Error::last_os_error())
}
}
fn uid_from_raw(uid: u32) -> nix::unistd::Uid {
nix::unistd::Uid::from_raw(uid)
}
fn gid_from_raw(gid: u32) -> nix::unistd::Gid {
nix::unistd::Gid::from_raw(gid)
}
pub fn fchown(fd: BorrowedFd<'_>, uid: Option<u32>, gid: Option<u32>) -> std::io::Result<()> {
nix::unistd::fchown(fd, uid.map(uid_from_raw), gid.map(gid_from_raw))
.map_err(std::io::Error::from)
}
#[cfg(not(windows))]
pub fn stat_fd(fd: crate::crt_fd::Borrowed<'_>) -> std::io::Result<crate::fileutils::StatStruct> {
crate::fileutils::fstat(fd)
}
#[cfg(not(target_os = "redox"))]
pub fn fchdir(fd: i32) -> std::io::Result<()> {
let ret = unsafe { libc::fchdir(fd) };
if ret == 0 {
Ok(())
} else {
Err(std::io::Error::last_os_error())
}
}
pub fn fork() -> std::io::Result<libc::pid_t> {
let pid = unsafe { libc::fork() };
if pid == -1 {
Err(std::io::Error::last_os_error())
} else {
Ok(pid)
}
}
pub fn write_fd(fd: BorrowedFd<'_>, buf: &[u8]) -> std::io::Result<usize> {
nix::unistd::write(fd, buf).map_err(std::io::Error::from)
}
pub fn pread(fd: i32, buf: &mut [u8], offset: libc::off_t) -> std::io::Result<usize> {
let ret = unsafe { libc::pread(fd, buf.as_mut_ptr().cast(), buf.len(), offset) };
if ret == -1 {
Err(std::io::Error::last_os_error())
} else {
Ok(ret as usize)
}
}
pub fn pwrite(fd: i32, buf: &[u8], offset: libc::off_t) -> std::io::Result<usize> {
let ret = unsafe { libc::pwrite(fd, buf.as_ptr().cast(), buf.len(), offset) };
if ret == -1 {
Err(std::io::Error::last_os_error())
} else {
Ok(ret as usize)
}
}
pub fn fchownat(
dir_fd: BorrowedFd<'_>,
path: &OsStr,
uid: Option<u32>,
gid: Option<u32>,
follow_symlinks: bool,
) -> std::io::Result<()> {
let flag = if follow_symlinks {
nix::fcntl::AtFlags::empty()
} else {
nix::fcntl::AtFlags::AT_SYMLINK_NOFOLLOW
};
nix::unistd::fchownat(
dir_fd,
path,
uid.map(uid_from_raw),
gid.map(gid_from_raw),
flag,
)
.map_err(std::io::Error::from)
}
pub fn uname_info() -> Result<UnameInfo, UnameDecodeError> {
fn decode(value: &CStr) -> Result<String, UnameDecodeError> {
core::str::from_utf8(value.to_bytes())
.map(str::to_owned)
.map_err(|error| UnameDecodeError {
bytes: value.to_bytes().to_vec(),
error,
})
}
let info = rustix::system::uname();
Ok(UnameInfo {
sysname: decode(info.sysname())?,
nodename: decode(info.nodename())?,
release: decode(info.release())?,
version: decode(info.version())?,
machine: decode(info.machine())?,
})
}
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "linux",
target_os = "android",
target_os = "netbsd",
target_os = "openbsd"
))]
pub fn pipe2(flags: libc::c_int) -> std::io::Result<(std::os::fd::OwnedFd, std::os::fd::OwnedFd)> {
nix::unistd::pipe2(nix::fcntl::OFlag::from_bits_truncate(flags)).map_err(std::io::Error::from)
}
#[cfg(not(target_os = "redox"))]
pub fn pipe() -> std::io::Result<(OwnedFd, OwnedFd)> {
let (rfd, wfd) = nix::unistd::pipe().map_err(std::io::Error::from)?;
set_inheritable(rfd.as_fd(), false)?;
set_inheritable(wfd.as_fd(), false)?;
Ok((rfd, wfd))
}
pub fn sched_yield() -> std::io::Result<()> {
nix::sched::sched_yield().map_err(std::io::Error::from)
}
#[cfg(not(target_os = "redox"))]
pub fn nice(increment: i32) -> std::io::Result<i32> {
crate::os::clear_errno();
let res = unsafe { libc::nice(increment) };
if res == -1 && crate::os::get_errno() != 0 {
Err(std::io::Error::last_os_error())
} else {
Ok(res)
}
}
#[cfg(not(target_os = "redox"))]
pub fn sched_get_priority_max(policy: i32) -> std::io::Result<i32> {
let max = unsafe { libc::sched_get_priority_max(policy) };
if max == -1 {
Err(std::io::Error::last_os_error())
} else {
Ok(max)
}
}
#[cfg(not(target_os = "redox"))]
pub fn sched_get_priority_min(policy: i32) -> std::io::Result<i32> {
let min = unsafe { libc::sched_get_priority_min(policy) };
if min == -1 {
Err(std::io::Error::last_os_error())
} else {
Ok(min)
}
}
#[cfg(not(target_os = "redox"))]
pub fn fchmod(fd: BorrowedFd<'_>, mode: u32) -> std::io::Result<()> {
nix::sys::stat::fchmod(
fd,
nix::sys::stat::Mode::from_bits_truncate(mode as libc::mode_t),
)
.map_err(std::io::Error::from)
}
#[cfg(target_os = "redox")]
pub fn utimes(
path: &Path,
acc: core::time::Duration,
modif: core::time::Duration,
) -> std::io::Result<()> {
let tv = |d: core::time::Duration| libc::timeval {
tv_sec: d.as_secs() as _,
tv_usec: d.subsec_micros() as _,
};
nix::sys::stat::utimes(path, &tv(acc).into(), &tv(modif).into()).map_err(std::io::Error::from)
}
#[cfg(all(any(target_os = "wasi", unix), not(target_os = "redox")))]
pub fn set_file_times_at(
dir_fd: i32,
path: &CStr,
access: core::time::Duration,
modified: core::time::Duration,
follow_symlinks: bool,
) -> std::io::Result<()> {
let ts = |d: core::time::Duration| libc::timespec {
tv_sec: d.as_secs() as _,
tv_nsec: d.subsec_nanos() as _,
};
let times = [ts(access), ts(modified)];
let ret = unsafe {
libc::utimensat(
dir_fd,
path.as_ptr(),
times.as_ptr(),
if follow_symlinks {
0
} else {
libc::AT_SYMLINK_NOFOLLOW
},
)
};
if ret < 0 {
Err(std::io::Error::last_os_error())
} else {
Ok(())
}
}
#[cfg(target_os = "macos")]
#[must_use]
pub fn get_number_of_os_threads() -> isize {
type MachPortT = libc::c_uint;
type KernReturnT = libc::c_int;
type MachMsgTypeNumberT = libc::c_uint;
type ThreadActArrayT = *mut MachPortT;
const KERN_SUCCESS: KernReturnT = 0;
unsafe extern "C" {
fn mach_task_self() -> MachPortT;
fn task_for_pid(
task: MachPortT,
pid: libc::c_int,
target_task: *mut MachPortT,
) -> KernReturnT;
fn task_threads(
target_task: MachPortT,
act_list: *mut ThreadActArrayT,
act_list_cnt: *mut MachMsgTypeNumberT,
) -> KernReturnT;
fn vm_deallocate(
target_task: MachPortT,
address: libc::uintptr_t,
size: libc::uintptr_t,
) -> KernReturnT;
}
let self_task = unsafe { mach_task_self() };
let mut proc_task: MachPortT = 0;
if unsafe { task_for_pid(self_task, libc::getpid(), &mut proc_task) } == KERN_SUCCESS {
let mut threads: ThreadActArrayT = core::ptr::null_mut();
let mut n_threads: MachMsgTypeNumberT = 0;
if unsafe { task_threads(proc_task, &mut threads, &mut n_threads) } == KERN_SUCCESS {
if !threads.is_null() {
let _ = unsafe {
vm_deallocate(
self_task,
threads as libc::uintptr_t,
(n_threads as usize * core::mem::size_of::<MachPortT>()) as libc::uintptr_t,
)
};
}
return n_threads as isize;
}
}
0
}
#[cfg(target_os = "linux")]
#[must_use]
pub fn get_number_of_os_threads() -> isize {
use std::io::Read as _;
let mut file = match crate::fs::open("/proc/self/stat") {
Ok(f) => f,
Err(_) => return 0,
};
let mut buf = [0u8; 160];
let n = match file.read(&mut buf) {
Ok(n) => n,
Err(_) => return 0,
};
let line = match core::str::from_utf8(&buf[..n]) {
Ok(s) => s,
Err(_) => return 0,
};
if let Some(field) = line.split_whitespace().nth(19) {
return field.parse::<isize>().unwrap_or(0);
}
0
}
#[cfg(not(any(target_os = "macos", target_os = "linux")))]
#[must_use]
pub fn get_number_of_os_threads() -> isize {
0
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Permissions {
pub is_readable: bool,
pub is_writable: bool,
pub is_executable: bool,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum AccessError {
InvalidMode,
Os(i32),
}
impl From<std::io::Error> for AccessError {
fn from(value: std::io::Error) -> Self {
Self::Os(value.raw_os_error().unwrap_or(0))
}
}
pub use crate::os::{AccessMode, F_OK, R_OK, W_OK, X_OK};
fn get_permissions(mode: u32) -> Permissions {
Permissions {
is_readable: mode & 4 != 0,
is_writable: mode & 2 != 0,
is_executable: mode & 1 != 0,
}
}
pub fn get_right_permission(
mode: u32,
file_owner: u32,
file_group: u32,
) -> std::io::Result<Permissions> {
let owner_mode = (mode & 0o700) >> 6;
let owner_permissions = get_permissions(owner_mode);
let group_mode = (mode & 0o070) >> 3;
let group_permissions = get_permissions(group_mode);
let others_mode = mode & 0o007;
let others_permissions = get_permissions(others_mode);
let user_id = nix::unistd::getuid().as_raw();
let groups_ids = getgroups()?;
if file_owner == user_id {
Ok(owner_permissions)
} else if groups_ids.contains(&file_group) {
Ok(group_permissions)
} else {
Ok(others_permissions)
}
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn getgroups() -> std::io::Result<Vec<u32>> {
use core::ptr;
use libc::{c_int, gid_t};
use nix::errno::Errno;
let ret = unsafe { libc::getgroups(0, ptr::null_mut()) };
let mut groups =
Vec::<gid_t>::with_capacity(Errno::result(ret).map_err(std::io::Error::from)? as usize);
let ret = unsafe { libc::getgroups(groups.capacity() as c_int, groups.as_mut_ptr()) };
Errno::result(ret).map_err(std::io::Error::from).map(|s| {
unsafe { groups.set_len(s as usize) };
groups.into_iter().collect()
})
}
#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "redox")))]
pub fn getgroups() -> std::io::Result<Vec<u32>> {
nix::unistd::getgroups()
.map(|groups| groups.into_iter().map(|gid| gid.as_raw()).collect())
.map_err(std::io::Error::from)
}
#[cfg(target_os = "redox")]
pub fn getgroups() -> std::io::Result<Vec<u32>> {
Err(std::io::Error::from_raw_os_error(libc::EOPNOTSUPP))
}
pub fn check_access(path: &Path, mode: u8) -> Result<bool, AccessError> {
use std::os::unix::fs::MetadataExt;
let Some(mode) = AccessMode::from_bits(mode) else {
return Err(AccessError::InvalidMode);
};
let metadata = match crate::fs::metadata(path) {
Ok(m) => m,
Err(_) => return Ok(false),
};
if mode.is_empty() {
return Ok(true);
}
let perm = get_right_permission(metadata.mode(), metadata.uid(), metadata.gid())?;
let r_ok = !mode.contains(&crate::os::AccessFlag::R) || perm.is_readable;
let w_ok = !mode.contains(&crate::os::AccessFlag::W) || perm.is_writable;
let x_ok = !mode.contains(&crate::os::AccessFlag::X) || perm.is_executable;
Ok(r_ok && w_ok && x_ok)
}
pub fn close_fds(above: i32, keep: &[BorrowedFd<'_>]) {
#[cfg(not(target_os = "redox"))]
if close_dir_fds(above, keep).is_ok() {
return;
}
#[cfg(target_os = "redox")]
if close_filetable_fds(above, keep).is_ok() {
return;
}
close_fds_brute_force(above, keep)
}
#[allow(clippy::too_many_arguments)]
pub fn setup_child_fds(
fds_to_keep: &[BorrowedFd<'_>],
errpipe_write: BorrowedFd<'_>,
p2cread: i32,
p2cwrite: i32,
c2pread: i32,
c2pwrite: i32,
errread: i32,
errwrite: i32,
errpipe_read: i32,
) -> std::io::Result<()> {
for &fd in fds_to_keep {
if fd.as_raw_fd() != errpipe_write.as_raw_fd() {
set_inheritable(fd, true)?;
}
}
for fd in [p2cwrite, c2pread, errread] {
if fd >= 0 {
nix::unistd::close(fd).map_err(std::io::Error::from)?;
}
}
nix::unistd::close(errpipe_read).map_err(std::io::Error::from)?;
let c2pwrite = if c2pwrite == 0 {
let fd = unsafe { BorrowedFd::borrow_raw(c2pwrite) };
let dup = nix::unistd::dup(fd).map_err(std::io::Error::from)?;
set_inheritable(dup.as_fd(), true)?;
dup.into_raw_fd()
} else {
c2pwrite
};
let mut errwrite = errwrite;
while errwrite == 0 || errwrite == 1 {
let fd = unsafe { BorrowedFd::borrow_raw(errwrite) };
let dup = nix::unistd::dup(fd).map_err(std::io::Error::from)?;
set_inheritable(dup.as_fd(), true)?;
errwrite = dup.into_raw_fd();
}
dup_into_stdio(p2cread, 0)?;
dup_into_stdio(c2pwrite, 1)?;
dup_into_stdio(errwrite, 2)?;
Ok(())
}
fn dup_into_stdio(fd: i32, io_fd: i32) -> std::io::Result<()> {
if fd < 0 {
return Ok(());
}
let fd = unsafe { BorrowedFd::borrow_raw(fd) };
if fd.as_raw_fd() == io_fd {
set_inheritable(fd, true)
} else {
match io_fd {
0 => nix::unistd::dup2_stdin(fd).map_err(std::io::Error::from),
1 => nix::unistd::dup2_stdout(fd).map_err(std::io::Error::from),
2 => nix::unistd::dup2_stderr(fd).map_err(std::io::Error::from),
_ => unreachable!(),
}
}
}
pub fn chdir(cwd: &CStr) -> nix::Result<()> {
nix::unistd::chdir(cwd)
}
pub fn set_umask(child_umask: i32) {
if child_umask >= 0 {
unsafe { libc::umask(child_umask as libc::mode_t) };
}
}
pub fn umask(mask: libc::mode_t) -> libc::mode_t {
unsafe { libc::umask(mask) }
}
#[cfg(not(any(target_os = "redox", target_os = "android")))]
pub fn sync() {
unsafe { libc::sync() };
}
pub fn getlogin() -> Option<CString> {
let ptr = unsafe { libc::getlogin() };
if ptr.is_null() {
None
} else {
Some(unsafe { CStr::from_ptr(ptr) }.to_owned())
}
}
pub fn restore_signals() {
unsafe {
libc::signal(libc::SIGPIPE, libc::SIG_DFL);
libc::signal(libc::SIGXFSZ, libc::SIG_DFL);
}
}
pub fn setsid_if_needed(call_setsid: bool) -> nix::Result<()> {
if call_setsid {
nix::unistd::setsid()?;
}
Ok(())
}
pub fn setpgid_if_needed(pgid_to_set: libc::pid_t) -> nix::Result<()> {
if pgid_to_set > -1 {
nix::unistd::setpgid(
nix::unistd::Pid::from_raw(0),
nix::unistd::Pid::from_raw(pgid_to_set),
)?;
}
Ok(())
}
pub fn setgroups_if_needed(groups: Option<&[u32]>) -> nix::Result<()> {
#[cfg(not(any(target_os = "ios", target_os = "redox")))]
if let Some(groups) = groups {
let ret =
unsafe { libc::setgroups(groups.len() as _, groups.as_ptr().cast::<libc::gid_t>()) };
nix::Error::result(ret)?;
}
Ok(())
}
pub fn setregid_if_needed(gid: Option<u32>) -> nix::Result<()> {
if let Some(gid) = gid.filter(|&x| x != u32::MAX) {
let ret = unsafe { libc::setregid(gid as libc::gid_t, gid as libc::gid_t) };
nix::Error::result(ret)?;
}
Ok(())
}
pub fn setreuid_if_needed(uid: Option<u32>) -> nix::Result<()> {
if let Some(uid) = uid.filter(|&x| x != u32::MAX) {
let ret = unsafe { libc::setreuid(uid as libc::uid_t, uid as libc::uid_t) };
nix::Error::result(ret)?;
}
Ok(())
}
pub fn getppid() -> libc::pid_t {
nix::unistd::getppid().as_raw()
}
pub fn getgid() -> u32 {
nix::unistd::getgid().as_raw()
}
pub fn getegid() -> u32 {
nix::unistd::getegid().as_raw()
}
pub fn getpgid(pid: u32) -> std::io::Result<libc::pid_t> {
nix::unistd::getpgid(Some(nix::unistd::Pid::from_raw(pid as i32)))
.map(nix::unistd::Pid::as_raw)
.map_err(std::io::Error::from)
}
pub fn getpgrp() -> libc::pid_t {
nix::unistd::getpgrp().as_raw()
}
#[cfg(not(target_os = "redox"))]
pub fn getsid(pid: u32) -> std::io::Result<libc::pid_t> {
nix::unistd::getsid(Some(nix::unistd::Pid::from_raw(pid as i32)))
.map(nix::unistd::Pid::as_raw)
.map_err(std::io::Error::from)
}
pub fn getuid() -> u32 {
nix::unistd::getuid().as_raw()
}
pub fn geteuid() -> u32 {
nix::unistd::geteuid().as_raw()
}
#[cfg(not(any(target_os = "wasi", target_os = "android")))]
pub fn setgid(gid: u32) -> std::io::Result<()> {
nix::unistd::setgid(gid_from_raw(gid)).map_err(std::io::Error::from)
}
#[cfg(not(any(target_os = "wasi", target_os = "android", target_os = "redox")))]
pub fn setegid(egid: u32) -> std::io::Result<()> {
nix::unistd::setegid(gid_from_raw(egid)).map_err(std::io::Error::from)
}
pub fn setpgid(pid: u32, pgid: u32) -> std::io::Result<()> {
nix::unistd::setpgid(
nix::unistd::Pid::from_raw(pid as i32),
nix::unistd::Pid::from_raw(pgid as i32),
)
.map_err(std::io::Error::from)
}
pub fn setpgrp() -> std::io::Result<()> {
nix::unistd::setpgid(nix::unistd::Pid::from_raw(0), nix::unistd::Pid::from_raw(0))
.map_err(std::io::Error::from)
}
#[cfg(not(any(target_os = "wasi", target_os = "redox")))]
pub fn setsid() -> std::io::Result<()> {
nix::unistd::setsid()
.map(drop)
.map_err(std::io::Error::from)
}
#[cfg(not(any(target_os = "wasi", target_os = "redox")))]
pub fn tcgetpgrp(fd: BorrowedFd<'_>) -> std::io::Result<libc::pid_t> {
nix::unistd::tcgetpgrp(fd)
.map(nix::unistd::Pid::as_raw)
.map_err(std::io::Error::from)
}
#[cfg(not(any(target_os = "wasi", target_os = "redox")))]
pub fn tcsetpgrp(fd: BorrowedFd<'_>, pgid: libc::pid_t) -> std::io::Result<()> {
nix::unistd::tcsetpgrp(fd, nix::unistd::Pid::from_raw(pgid)).map_err(std::io::Error::from)
}
#[cfg(not(target_os = "redox"))]
pub fn getpriority(which: PriorityWhichType, who: PriorityWhoType) -> std::io::Result<i32> {
crate::os::clear_errno();
let retval = unsafe { libc::getpriority(which, who) };
if crate::os::get_errno() != 0 {
Err(std::io::Error::last_os_error())
} else {
Ok(retval)
}
}
#[cfg(not(target_os = "redox"))]
pub fn setpriority(
which: PriorityWhichType,
who: PriorityWhoType,
priority: i32,
) -> std::io::Result<()> {
let retval = unsafe { libc::setpriority(which, who, priority) };
if retval == -1 {
Err(std::io::Error::last_os_error())
} else {
Ok(())
}
}
pub fn waitpid(pid: libc::pid_t, status: &mut i32, opt: i32) -> std::io::Result<libc::pid_t> {
let res = unsafe { libc::waitpid(pid, status, opt) };
if res == -1 {
Err(std::io::Error::last_os_error())
} else {
Ok(res)
}
}
pub fn wait3(options: i32) -> std::io::Result<(libc::pid_t, i32, crate::resource::RUsage)> {
wait4(-1, options)
}
pub fn wait4(
pid: libc::pid_t,
options: i32,
) -> std::io::Result<(libc::pid_t, i32, crate::resource::RUsage)> {
let mut status = 0;
let mut ru = core::mem::MaybeUninit::<libc::rusage>::zeroed();
let res = unsafe { libc::wait4(pid, &mut status, options, ru.as_mut_ptr()) };
if res == -1 {
return Err(std::io::Error::last_os_error());
}
let ru = if res == 0 {
unsafe { core::mem::zeroed() }
} else {
unsafe { ru.assume_init() }
};
Ok((res, status, ru.into()))
}
pub fn kill(pid: i32, sig: i32) -> std::io::Result<()> {
let ret = unsafe { libc::kill(pid, sig) };
if ret == -1 {
Err(std::io::Error::last_os_error())
} else {
Ok(())
}
}
pub fn killpg(pgid: i32, sig: i32) -> std::io::Result<()> {
let ret = unsafe { libc::killpg(pgid, sig) };
if ret == -1 {
Err(std::io::Error::last_os_error())
} else {
Ok(())
}
}
#[cfg(not(any(target_os = "wasi", target_os = "android")))]
pub fn setuid(uid: u32) -> std::io::Result<()> {
nix::unistd::setuid(uid_from_raw(uid)).map_err(std::io::Error::from)
}
#[cfg(not(any(target_os = "wasi", target_os = "android", target_os = "redox")))]
pub fn seteuid(euid: u32) -> std::io::Result<()> {
nix::unistd::seteuid(uid_from_raw(euid)).map_err(std::io::Error::from)
}
#[cfg(not(any(target_os = "wasi", target_os = "android", target_os = "redox")))]
pub fn setreuid(ruid: u32, euid: u32) -> std::io::Result<()> {
let ret = unsafe { libc::setreuid(ruid as libc::uid_t, euid as libc::uid_t) };
nix::Error::result(ret)
.map(drop)
.map_err(std::io::Error::from)
}
#[cfg(any(
target_os = "android",
target_os = "freebsd",
target_os = "linux",
target_os = "openbsd"
))]
pub fn setresuid(ruid: u32, euid: u32, suid: u32) -> std::io::Result<()> {
let ret = unsafe {
libc::setresuid(
ruid as libc::uid_t,
euid as libc::uid_t,
suid as libc::uid_t,
)
};
nix::Error::result(ret)
.map(drop)
.map_err(std::io::Error::from)
}
#[cfg(not(any(target_os = "wasi", target_os = "solaris", target_os = "illumos")))]
pub fn login_tty(fd: i32) -> std::io::Result<()> {
let ret = unsafe { libc::login_tty(fd) };
if ret < 0 {
Err(std::io::Error::last_os_error())
} else {
Ok(())
}
}
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
pub fn login_tty(fd: i32) -> std::io::Result<()> {
if unsafe { libc::setsid() } < 0 {
let err = std::io::Error::last_os_error();
if err.raw_os_error() != Some(libc::EPERM) {
return Err(err);
}
}
if unsafe { libc::ioctl(fd, libc::TIOCSCTTY, core::ptr::null::<libc::c_char>()) } < 0 {
return Err(std::io::Error::last_os_error());
}
if unsafe { libc::dup2(fd, 0) } < 0
|| unsafe { libc::dup2(fd, 1) } < 0
|| unsafe { libc::dup2(fd, 2) } < 0
{
return Err(std::io::Error::last_os_error());
}
if fd > 2 {
let _ = unsafe { libc::close(fd) };
}
Ok(())
}
#[cfg(not(target_os = "redox"))]
pub fn openpty() -> std::io::Result<(OwnedFd, OwnedFd)> {
let pty = nix::pty::openpty(None, None).map_err(std::io::Error::from)?;
set_inheritable(pty.master.as_fd(), false)?;
set_inheritable(pty.slave.as_fd(), false)?;
Ok((pty.master, pty.slave))
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
pub fn forkpty() -> std::io::Result<(pid_t, i32)> {
let mut master_fd: libc::c_int = -1;
let pid = unsafe {
libc::forkpty(
&mut master_fd,
core::ptr::null_mut(),
core::ptr::null_mut(),
core::ptr::null_mut(),
)
};
if pid < 0 {
return Err(std::io::Error::last_os_error());
}
if pid != 0 && master_fd >= 0 {
let fd = unsafe { BorrowedFd::borrow_raw(master_fd) };
set_inheritable(fd, false)?;
}
Ok((pid, master_fd))
}
pub fn ttyname(fd: BorrowedFd<'_>) -> std::io::Result<OsString> {
nix::unistd::ttyname(fd)
.map(std::path::PathBuf::into_os_string)
.map_err(std::io::Error::from)
}
pub fn execv(path: &CStr, argv: &[&CStr]) -> std::io::Result<()> {
match nix::unistd::execv(path, argv) {
Ok(never) => match never {},
Err(err) => Err(err.into()),
}
}
pub fn execve(path: &CStr, argv: &[&CStr], env: &[&CStr]) -> std::io::Result<()> {
match nix::unistd::execve(path, argv, env) {
Ok(never) => match never {},
Err(err) => Err(err.into()),
}
}
#[cfg(any(target_os = "android", target_os = "linux", target_os = "openbsd"))]
pub fn getresuid() -> std::io::Result<(u32, u32, u32)> {
let ret = nix::unistd::getresuid().map_err(std::io::Error::from)?;
Ok((
ret.real.as_raw(),
ret.effective.as_raw(),
ret.saved.as_raw(),
))
}
#[cfg(any(target_os = "android", target_os = "linux", target_os = "openbsd"))]
pub fn getresgid() -> std::io::Result<(u32, u32, u32)> {
let ret = nix::unistd::getresgid().map_err(std::io::Error::from)?;
Ok((
ret.real.as_raw(),
ret.effective.as_raw(),
ret.saved.as_raw(),
))
}
#[cfg(any(target_os = "freebsd", target_os = "linux", target_os = "openbsd"))]
pub fn setresgid(rgid: u32, egid: u32, sgid: u32) -> std::io::Result<()> {
let ret = unsafe {
libc::setresgid(
rgid as libc::gid_t,
egid as libc::gid_t,
sgid as libc::gid_t,
)
};
nix::Error::result(ret)
.map(drop)
.map_err(std::io::Error::from)
}
#[cfg(not(any(target_os = "wasi", target_os = "android", target_os = "redox")))]
pub fn setregid(rgid: u32, egid: u32) -> std::io::Result<()> {
let ret = unsafe { libc::setregid(rgid as libc::gid_t, egid as libc::gid_t) };
nix::Error::result(ret)
.map(drop)
.map_err(std::io::Error::from)
}
#[cfg(any(target_os = "freebsd", target_os = "linux", target_os = "openbsd"))]
pub fn initgroups(user: &CStr, gid: u32) -> std::io::Result<()> {
nix::unistd::initgroups(user, gid_from_raw(gid)).map_err(std::io::Error::from)
}
#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox")))]
pub fn setgroups_raw(groups: &[u32]) -> std::io::Result<()> {
let gids = groups.iter().copied().map(gid_from_raw).collect::<Vec<_>>();
nix::unistd::setgroups(&gids).map_err(std::io::Error::from)
}
pub fn dup_noninheritable(fd: BorrowedFd<'_>) -> std::io::Result<OwnedFd> {
let fd = nix::unistd::dup(fd).map_err(std::io::Error::from)?;
set_inheritable(fd.as_fd(), false)?;
Ok(fd)
}
pub fn dup2(fd: BorrowedFd<'_>, fd2: OwnedFd, inheritable: bool) -> std::io::Result<OwnedFd> {
let mut fd2 = core::mem::ManuallyDrop::new(fd2);
nix::unistd::dup2(fd, &mut fd2).map_err(std::io::Error::from)?;
let fd2 = core::mem::ManuallyDrop::into_inner(fd2);
if !inheritable {
set_inheritable(fd2.as_fd(), false)?;
}
Ok(fd2)
}
#[cfg(all(unix, not(target_os = "redox")))]
impl FdDirStream {
pub fn from_fd(fd: BorrowedFd<'_>) -> std::io::Result<Self> {
let new_fd = dup_fd(fd)?;
let raw_fd = new_fd.into_raw_fd();
let ptr = unsafe { libc::fdopendir(raw_fd) };
match NonNull::new(ptr) {
Some(ptr) => Ok(Self(ptr)),
None => {
unsafe { libc::close(raw_fd) };
Err(std::io::Error::last_os_error())
}
}
}
pub fn next_entry(&mut self) -> std::io::Result<Option<RawDirEntry>> {
loop {
crate::os::clear_errno();
let ptr = unsafe { libc::readdir(self.0.as_ptr()) };
if ptr.is_null() {
let err = crate::os::get_errno();
return if err == 0 {
Ok(None)
} else {
Err(std::io::Error::from_raw_os_error(err))
};
}
let entry = unsafe { &*ptr };
let name = unsafe { CStr::from_ptr(entry.d_name.as_ptr()) }.to_bytes();
if name == b"." || name == b".." {
continue;
}
#[cfg(target_os = "freebsd")]
let ino = entry.d_fileno as u64;
#[cfg(not(target_os = "freebsd"))]
let ino = entry.d_ino as u64;
return Ok(Some(RawDirEntry {
name: name.to_vec(),
d_type: (entry.d_type != libc::DT_UNKNOWN).then_some(entry.d_type),
ino,
}));
}
}
}
#[cfg(all(unix, not(target_os = "redox")))]
impl Drop for FdDirStream {
fn drop(&mut self) {
unsafe {
libc::rewinddir(self.0.as_ptr());
libc::closedir(self.0.as_ptr());
}
}
}
#[cfg(all(unix, not(target_os = "redox")))]
impl core::fmt::Debug for FdDirStream {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("FdDirStream").field(&self.0).finish()
}
}
#[cfg(all(unix, not(target_os = "redox")))]
unsafe impl Send for FdDirStream {}
#[cfg(all(unix, not(target_os = "redox")))]
unsafe impl Sync for FdDirStream {}
pub fn get_terminal_size(fd: libc::c_int) -> std::io::Result<(u16, u16)> {
let mut w = libc::winsize {
ws_row: 0,
ws_col: 0,
ws_xpixel: 0,
ws_ypixel: 0,
};
let ret = unsafe { libc::ioctl(fd, libc::TIOCGWINSZ, &mut w) };
if ret < 0 {
Err(std::io::Error::last_os_error())
} else {
Ok((w.ws_col, w.ws_row))
}
}
#[cfg(target_os = "macos")]
pub fn full_fsync(fd: BorrowedFd<'_>) -> std::io::Result<()> {
let ret = unsafe { libc::fcntl(fd.as_raw_fd(), libc::F_FULLFSYNC) };
if ret < 0 {
Err(std::io::Error::last_os_error())
} else {
Ok(())
}
}
#[cfg(all(unix, not(target_os = "redox")))]
pub fn madvise(addr: usize, len: usize, advice: i32) -> std::io::Result<()> {
let ret = unsafe { libc::madvise(addr as *mut libc::c_void, len, advice) };
if ret != 0 {
Err(std::io::Error::last_os_error())
} else {
Ok(())
}
}
pub fn pathconf(path: &CStr, name: i32) -> std::io::Result<Option<libc::c_long>> {
crate::os::clear_errno();
debug_assert_eq!(crate::os::get_errno(), 0);
let raw = unsafe { libc::pathconf(path.as_ptr(), name) };
if raw == -1 {
if crate::os::get_errno() == 0 {
Ok(None)
} else {
Err(std::io::Error::last_os_error())
}
} else {
Ok(Some(raw))
}
}
pub fn fpathconf(fd: i32, name: i32) -> std::io::Result<Option<libc::c_long>> {
crate::os::clear_errno();
debug_assert_eq!(crate::os::get_errno(), 0);
let raw = unsafe { libc::fpathconf(fd, name) };
if raw == -1 {
if crate::os::get_errno() == 0 {
Ok(None)
} else {
Err(std::io::Error::last_os_error())
}
} else {
Ok(Some(raw))
}
}
pub fn sysconf(name: i32) -> std::io::Result<libc::c_long> {
crate::os::set_errno(0);
let raw = unsafe { libc::sysconf(name) };
if raw == -1 && crate::os::get_errno() != 0 {
Err(std::io::Error::last_os_error())
} else {
Ok(raw)
}
}
#[cfg(target_os = "linux")]
pub unsafe fn getrandom(
buf: *mut libc::c_void,
buflen: usize,
flags: u32,
) -> std::io::Result<usize> {
let len = unsafe { libc::syscall(libc::SYS_getrandom, buf, buflen, flags as usize) as isize };
if len < 0 {
Err(std::io::Error::last_os_error())
} else {
Ok(len as usize)
}
}
pub fn wcoredump(status: i32) -> bool {
libc::WCOREDUMP(status)
}
pub fn wifcontinued(status: i32) -> bool {
libc::WIFCONTINUED(status)
}
pub fn wifstopped(status: i32) -> bool {
libc::WIFSTOPPED(status)
}
pub fn wifsignaled(status: i32) -> bool {
libc::WIFSIGNALED(status)
}
pub fn wifexited(status: i32) -> bool {
libc::WIFEXITED(status)
}
pub fn wexitstatus(status: i32) -> i32 {
libc::WEXITSTATUS(status)
}
pub fn wstopsig(status: i32) -> i32 {
libc::WSTOPSIG(status)
}
pub fn wtermsig(status: i32) -> i32 {
libc::WTERMSIG(status)
}
#[cfg(target_os = "linux")]
pub fn pidfd_open(pid: libc::pid_t, flags: u32) -> std::io::Result<OwnedFd> {
let fd = unsafe { libc::syscall(libc::SYS_pidfd_open, pid, flags) as libc::c_long };
if fd == -1 {
Err(std::io::Error::last_os_error())
} else {
Ok(unsafe { OwnedFd::from_raw_fd(fd as libc::c_int) })
}
}
#[cfg(any(
target_os = "android",
target_os = "freebsd",
target_os = "linux",
target_os = "openbsd"
))]
pub fn getgrouplist(user: &CStr, gid: u32) -> std::io::Result<Vec<u32>> {
nix::unistd::getgrouplist(user, gid_from_raw(gid))
.map(|groups| groups.into_iter().map(|gid| gid.as_raw()).collect())
.map_err(std::io::Error::from)
}
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
pub fn validate_posix_spawn_signal(sig: i32) -> bool {
nix::sys::signal::Signal::try_from(sig).is_ok()
}
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
pub const fn supports_posix_spawn_setsid() -> bool {
cfg!(any(
target_os = "linux",
target_os = "haiku",
target_os = "solaris",
target_os = "illumos",
target_os = "hurd",
))
}
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
fn build_posix_spawn_file_actions(
actions: &[PosixSpawnFileAction],
) -> std::io::Result<nix::spawn::PosixSpawnFileActions> {
let mut file_actions =
nix::spawn::PosixSpawnFileActions::init().map_err(std::io::Error::from)?;
for action in actions {
match action {
PosixSpawnFileAction::Open {
fd,
path,
oflag,
mode,
} => file_actions
.add_open(
*fd,
path.as_c_str(),
nix::fcntl::OFlag::from_bits_retain(*oflag),
nix::sys::stat::Mode::from_bits_retain(*mode as libc::mode_t),
)
.map_err(std::io::Error::from)?,
PosixSpawnFileAction::Close { fd } => {
file_actions.add_close(*fd).map_err(std::io::Error::from)?
}
PosixSpawnFileAction::Dup2 { fd, newfd } => file_actions
.add_dup2(*fd, *newfd)
.map_err(std::io::Error::from)?,
}
}
Ok(file_actions)
}
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
fn build_sigset(signals: &[i32]) -> nix::sys::signal::SigSet {
let mut set = nix::sys::signal::SigSet::empty();
for &sig in signals {
let sig = nix::sys::signal::Signal::try_from(sig).expect("validated signal");
set.add(sig);
}
set
}
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
fn build_posix_spawn_attrs(
config: &PosixSpawnConfig<'_>,
) -> std::io::Result<nix::spawn::PosixSpawnAttr> {
let mut attrp = nix::spawn::PosixSpawnAttr::init().map_err(std::io::Error::from)?;
let mut flags = nix::spawn::PosixSpawnFlags::empty();
if let Some(sigs) = config.setsigdef {
let set = build_sigset(sigs);
attrp.set_sigdefault(&set).map_err(std::io::Error::from)?;
flags.insert(nix::spawn::PosixSpawnFlags::POSIX_SPAWN_SETSIGDEF);
}
if let Some(pgid) = config.setpgroup {
attrp
.set_pgroup(nix::unistd::Pid::from_raw(pgid))
.map_err(std::io::Error::from)?;
flags.insert(nix::spawn::PosixSpawnFlags::POSIX_SPAWN_SETPGROUP);
}
if config.resetids {
flags.insert(nix::spawn::PosixSpawnFlags::POSIX_SPAWN_RESETIDS);
}
if config.setsid {
#[cfg(any(
target_os = "linux",
target_os = "haiku",
target_os = "solaris",
target_os = "illumos",
target_os = "hurd",
))]
{
#[allow(clippy::useless_conversion)]
flags.insert(nix::spawn::PosixSpawnFlags::from_bits_retain(
libc::POSIX_SPAWN_SETSID.into(),
));
}
#[cfg(not(any(
target_os = "linux",
target_os = "haiku",
target_os = "solaris",
target_os = "illumos",
target_os = "hurd",
)))]
{
return Err(std::io::Error::new(
std::io::ErrorKind::Unsupported,
"setsid parameter is not supported on this platform",
));
}
}
if let Some(sigs) = config.setsigmask {
let set = build_sigset(sigs);
attrp.set_sigmask(&set).map_err(std::io::Error::from)?;
flags.insert(nix::spawn::PosixSpawnFlags::POSIX_SPAWN_SETSIGMASK);
}
#[cfg(all(
any(target_os = "linux", target_os = "freebsd", target_os = "android"),
not(target_env = "musl")
))]
if let Some(scheduler) = &config.scheduler {
let attr_ptr = (&raw mut attrp).cast::<libc::posix_spawnattr_t>();
if let Some(policy) = scheduler.policy {
let err = unsafe { libc::posix_spawnattr_setschedpolicy(attr_ptr, policy) };
if err != 0 {
return Err(std::io::Error::from_raw_os_error(err));
}
flags.insert(nix::spawn::PosixSpawnFlags::from_bits_retain(
libc::POSIX_SPAWN_SETSCHEDULER,
));
}
let err = unsafe { libc::posix_spawnattr_setschedparam(attr_ptr, &scheduler.param) };
if err != 0 {
return Err(std::io::Error::from_raw_os_error(err));
}
flags.insert(nix::spawn::PosixSpawnFlags::from_bits_retain(
libc::POSIX_SPAWN_SETSCHEDPARAM,
));
}
if !flags.is_empty() {
attrp.set_flags(flags).map_err(std::io::Error::from)?;
}
Ok(attrp)
}
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
pub fn posix_spawn(config: PosixSpawnConfig<'_>) -> std::io::Result<libc::pid_t> {
let file_actions = build_posix_spawn_file_actions(config.file_actions)?;
let attrp = build_posix_spawn_attrs(&config)?;
let pid = if config.spawnp {
nix::spawn::posix_spawnp(config.path, &file_actions, &attrp, config.args, config.env)
} else {
nix::spawn::posix_spawn(config.path, &file_actions, &attrp, config.args, config.env)
}
.map_err(std::io::Error::from)?;
Ok(pid.into())
}
#[cfg(target_os = "linux")]
pub fn sendfile(
out_fd: BorrowedFd<'_>,
in_fd: BorrowedFd<'_>,
offset: &mut crate::crt_fd::Offset,
count: usize,
) -> std::io::Result<usize> {
nix::sys::sendfile::sendfile(out_fd, in_fd, Some(offset), count).map_err(std::io::Error::from)
}
#[cfg(target_os = "macos")]
pub fn sendfile(
in_fd: BorrowedFd<'_>,
out_fd: BorrowedFd<'_>,
offset: crate::crt_fd::Offset,
count: i64,
headers: Option<&[&[u8]]>,
trailers: Option<&[&[u8]]>,
) -> (std::io::Result<()>, i64) {
let (res, written) =
nix::sys::sendfile::sendfile(in_fd, out_fd, offset, Some(count), headers, trailers);
(res.map_err(std::io::Error::from), written)
}
#[cfg(any(
target_os = "android",
target_os = "freebsd",
target_os = "linux",
target_os = "netbsd"
))]
pub fn sched_getscheduler(pid: libc::pid_t) -> std::io::Result<i32> {
let policy = unsafe { libc::sched_getscheduler(pid) };
if policy == -1 {
Err(std::io::Error::last_os_error())
} else {
Ok(policy)
}
}
#[cfg(all(
not(target_env = "musl"),
any(
target_os = "android",
target_os = "freebsd",
target_os = "linux",
target_os = "netbsd"
)
))]
pub fn sched_setscheduler(
pid: i32,
policy: i32,
param: &libc::sched_param,
) -> std::io::Result<i32> {
let ret = unsafe { libc::sched_setscheduler(pid, policy, param) };
if ret == -1 {
Err(std::io::Error::last_os_error())
} else {
Ok(ret)
}
}
#[cfg(any(
target_os = "android",
target_os = "freebsd",
target_os = "linux",
target_os = "netbsd"
))]
pub fn sched_getparam(pid: libc::pid_t) -> std::io::Result<libc::sched_param> {
let mut param = core::mem::MaybeUninit::uninit();
let ret = unsafe { libc::sched_getparam(pid, param.as_mut_ptr()) };
if ret == -1 {
Err(std::io::Error::last_os_error())
} else {
Ok(unsafe { param.assume_init() })
}
}
#[cfg(all(
not(target_env = "musl"),
any(
target_os = "android",
target_os = "freebsd",
target_os = "linux",
target_os = "netbsd"
)
))]
pub fn sched_setparam(pid: i32, param: &libc::sched_param) -> std::io::Result<i32> {
let ret = unsafe { libc::sched_setparam(pid, param) };
if ret == -1 {
Err(std::io::Error::last_os_error())
} else {
Ok(ret)
}
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn exec_replace<T: AsRef<CStr>>(
exec_list: &[T],
argv: *const *const libc::c_char,
envp: Option<*const *const libc::c_char>,
) -> nix::errno::Errno {
let mut first_err = None;
for exec in exec_list {
if let Some(envp) = envp {
unsafe { libc::execve(exec.as_ref().as_ptr(), argv, envp) };
} else {
unsafe { libc::execv(exec.as_ref().as_ptr(), argv) };
}
let e = nix::errno::Errno::last();
if e != nix::errno::Errno::ENOENT && e != nix::errno::Errno::ENOTDIR && first_err.is_none()
{
first_err = Some(e);
}
}
first_err.unwrap_or_else(nix::errno::Errno::last)
}
fn should_keep(above: i32, keep: &[BorrowedFd<'_>], fd: i32) -> bool {
fd > above
&& keep
.binary_search_by_key(&fd, BorrowedFd::as_raw_fd)
.is_err()
}
#[cfg(not(target_os = "redox"))]
fn close_dir_fds(above: i32, keep: &[BorrowedFd<'_>]) -> nix::Result<()> {
use nix::{dir::Dir, fcntl::OFlag};
use std::os::fd::AsRawFd;
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_vendor = "apple",
))]
let fd_dir_name = c"/dev/fd";
#[cfg(any(target_os = "linux", target_os = "android"))]
let fd_dir_name = c"/proc/self/fd";
let mut dir = Dir::open(
fd_dir_name,
OFlag::O_RDONLY | OFlag::O_DIRECTORY,
nix::sys::stat::Mode::empty(),
)?;
let dirfd = dir.as_raw_fd();
'outer: for e in dir.iter() {
let e = e?;
let mut parser = IntParser::default();
for &c in e.file_name().to_bytes() {
if parser.feed(c).is_err() {
continue 'outer;
}
}
let fd = parser.num;
if fd != dirfd && should_keep(above, keep, fd) {
let _ = nix::unistd::close(fd);
}
}
Ok(())
}
#[cfg(target_os = "redox")]
fn close_filetable_fds(above: i32, keep: &[BorrowedFd<'_>]) -> nix::Result<()> {
use nix::fcntl;
use std::os::fd::AsRawFd;
let filetable = fcntl::open(
c"/scheme/thisproc/current/filetable",
fcntl::OFlag::O_RDONLY,
nix::sys::stat::Mode::empty(),
)?;
let read_one = || -> nix::Result<_> {
let mut byte = 0;
let n = nix::unistd::read(&filetable, std::slice::from_mut(&mut byte))?;
Ok((n > 0).then_some(byte))
};
while let Some(c) = read_one()? {
let mut parser = IntParser::default();
if parser.feed(c).is_err() {
continue;
}
let done = loop {
let Some(c) = read_one()? else { break true };
if parser.feed(c).is_err() {
break false;
}
};
let fd = parser.num;
if fd != filetable.as_raw_fd() && should_keep(above, keep, fd) {
let _ = nix::unistd::close(fd);
}
if done {
break;
}
}
Ok(())
}
fn close_fds_brute_force(above: i32, keep: &[BorrowedFd<'_>]) {
debug_assert!(
keep.windows(2)
.all(|fds| fds[0].as_raw_fd() <= fds[1].as_raw_fd()),
"close_fds_brute_force requires `keep` to be sorted ascending"
);
let max_fd = nix::unistd::sysconf(nix::unistd::SysconfVar::OPEN_MAX)
.ok()
.flatten()
.unwrap_or(256) as i32;
let mut prev = above;
for fd in keep
.iter()
.map(BorrowedFd::as_raw_fd)
.chain(core::iter::once(max_fd))
{
for candidate in prev + 1..fd {
unsafe { libc::close(candidate) };
}
prev = fd;
}
}
#[derive(Default)]
struct IntParser {
num: i32,
}
struct NonDigit;
impl IntParser {
fn feed(&mut self, c: u8) -> Result<(), NonDigit> {
let digit = (c as char).to_digit(10).ok_or(NonDigit)?;
self.num *= 10;
self.num += digit as i32;
Ok(())
}
}