summaryrefslogtreecommitdiffstats
path: root/third_party/rust/nix/src/unistd.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/nix/src/unistd.rs
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/nix/src/unistd.rs')
-rw-r--r--third_party/rust/nix/src/unistd.rs3383
1 files changed, 3383 insertions, 0 deletions
diff --git a/third_party/rust/nix/src/unistd.rs b/third_party/rust/nix/src/unistd.rs
new file mode 100644
index 0000000000..ca07b34a2e
--- /dev/null
+++ b/third_party/rust/nix/src/unistd.rs
@@ -0,0 +1,3383 @@
+//! Safe wrappers around functions found in libc "unistd.h" header
+
+use crate::errno::{self, Errno};
+#[cfg(not(target_os = "redox"))]
+#[cfg(feature = "fs")]
+use crate::fcntl::{at_rawfd, AtFlags};
+#[cfg(feature = "fs")]
+use crate::fcntl::{fcntl, FcntlArg::F_SETFD, FdFlag, OFlag};
+#[cfg(all(
+ feature = "fs",
+ any(
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "macos",
+ target_os = "ios"
+ )
+))]
+use crate::sys::stat::FileFlag;
+#[cfg(feature = "fs")]
+use crate::sys::stat::Mode;
+use crate::{Error, NixPath, Result};
+#[cfg(not(target_os = "redox"))]
+use cfg_if::cfg_if;
+use libc::{
+ self, c_char, c_int, c_long, c_uint, c_void, gid_t, mode_t, off_t, pid_t,
+ size_t, uid_t, PATH_MAX,
+};
+use std::convert::Infallible;
+use std::ffi::{CStr, OsString};
+#[cfg(not(target_os = "redox"))]
+use std::ffi::{CString, OsStr};
+#[cfg(not(target_os = "redox"))]
+use std::os::unix::ffi::OsStrExt;
+use std::os::unix::ffi::OsStringExt;
+use std::os::unix::io::RawFd;
+use std::path::PathBuf;
+use std::{fmt, mem, ptr};
+
+feature! {
+ #![feature = "fs"]
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ pub use self::pivot_root::*;
+}
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "openbsd"
+))]
+pub use self::setres::*;
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "openbsd"
+))]
+pub use self::getres::*;
+
+feature! {
+#![feature = "user"]
+
+/// User identifier
+///
+/// Newtype pattern around `uid_t` (which is just alias). It prevents bugs caused by accidentally
+/// passing wrong value.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+pub struct Uid(uid_t);
+
+impl Uid {
+ /// Creates `Uid` from raw `uid_t`.
+ pub const fn from_raw(uid: uid_t) -> Self {
+ Uid(uid)
+ }
+
+ /// Returns Uid of calling process. This is practically a more Rusty alias for `getuid`.
+ #[doc(alias("getuid"))]
+ pub fn current() -> Self {
+ getuid()
+ }
+
+ /// Returns effective Uid of calling process. This is practically a more Rusty alias for `geteuid`.
+ #[doc(alias("geteuid"))]
+ pub fn effective() -> Self {
+ geteuid()
+ }
+
+ /// Returns true if the `Uid` represents privileged user - root. (If it equals zero.)
+ pub const fn is_root(self) -> bool {
+ self.0 == ROOT.0
+ }
+
+ /// Get the raw `uid_t` wrapped by `self`.
+ pub const fn as_raw(self) -> uid_t {
+ self.0
+ }
+}
+
+impl From<Uid> for uid_t {
+ fn from(uid: Uid) -> Self {
+ uid.0
+ }
+}
+
+impl From<uid_t> for Uid {
+ fn from(uid: uid_t) -> Self {
+ Uid(uid)
+ }
+}
+
+impl fmt::Display for Uid {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Display::fmt(&self.0, f)
+ }
+}
+
+/// Constant for UID = 0
+pub const ROOT: Uid = Uid(0);
+
+/// Group identifier
+///
+/// Newtype pattern around `gid_t` (which is just alias). It prevents bugs caused by accidentally
+/// passing wrong value.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+pub struct Gid(gid_t);
+
+impl Gid {
+ /// Creates `Gid` from raw `gid_t`.
+ pub const fn from_raw(gid: gid_t) -> Self {
+ Gid(gid)
+ }
+
+ /// Returns Gid of calling process. This is practically a more Rusty alias for `getgid`.
+ #[doc(alias("getgid"))]
+ pub fn current() -> Self {
+ getgid()
+ }
+
+ /// Returns effective Gid of calling process. This is practically a more Rusty alias for `getegid`.
+ #[doc(alias("getegid"))]
+ pub fn effective() -> Self {
+ getegid()
+ }
+
+ /// Get the raw `gid_t` wrapped by `self`.
+ pub const fn as_raw(self) -> gid_t {
+ self.0
+ }
+}
+
+impl From<Gid> for gid_t {
+ fn from(gid: Gid) -> Self {
+ gid.0
+ }
+}
+
+impl From<gid_t> for Gid {
+ fn from(gid: gid_t) -> Self {
+ Gid(gid)
+ }
+}
+
+impl fmt::Display for Gid {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Display::fmt(&self.0, f)
+ }
+}
+}
+
+feature! {
+#![feature = "process"]
+/// Process identifier
+///
+/// Newtype pattern around `pid_t` (which is just alias). It prevents bugs caused by accidentally
+/// passing wrong value.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+pub struct Pid(pid_t);
+
+impl Pid {
+ /// Creates `Pid` from raw `pid_t`.
+ pub const fn from_raw(pid: pid_t) -> Self {
+ Pid(pid)
+ }
+
+ /// Returns PID of calling process
+ #[doc(alias("getpid"))]
+ pub fn this() -> Self {
+ getpid()
+ }
+
+ /// Returns PID of parent of calling process
+ #[doc(alias("getppid"))]
+ pub fn parent() -> Self {
+ getppid()
+ }
+
+ /// Get the raw `pid_t` wrapped by `self`.
+ pub const fn as_raw(self) -> pid_t {
+ self.0
+ }
+}
+
+impl From<Pid> for pid_t {
+ fn from(pid: Pid) -> Self {
+ pid.0
+ }
+}
+
+impl fmt::Display for Pid {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Display::fmt(&self.0, f)
+ }
+}
+
+
+/// Represents the successful result of calling `fork`
+///
+/// When `fork` is called, the process continues execution in the parent process
+/// and in the new child. This return type can be examined to determine whether
+/// you are now executing in the parent process or in the child.
+#[derive(Clone, Copy, Debug)]
+pub enum ForkResult {
+ Parent { child: Pid },
+ Child,
+}
+
+impl ForkResult {
+
+ /// Return `true` if this is the child process of the `fork()`
+ #[inline]
+ pub fn is_child(self) -> bool {
+ matches!(self, ForkResult::Child)
+ }
+
+ /// Returns `true` if this is the parent process of the `fork()`
+ #[inline]
+ pub fn is_parent(self) -> bool {
+ !self.is_child()
+ }
+}
+
+/// Create a new child process duplicating the parent process ([see
+/// fork(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html)).
+///
+/// After successfully calling the fork system call, a second process will
+/// be created which is identical to the original except for the pid and the
+/// return value of this function. As an example:
+///
+/// ```
+/// use nix::{sys::wait::waitpid,unistd::{fork, ForkResult, write}};
+///
+/// match unsafe{fork()} {
+/// Ok(ForkResult::Parent { child, .. }) => {
+/// println!("Continuing execution in parent process, new child has pid: {}", child);
+/// waitpid(child, None).unwrap();
+/// }
+/// Ok(ForkResult::Child) => {
+/// // Unsafe to use `println!` (or `unwrap`) here. See Safety.
+/// write(libc::STDOUT_FILENO, "I'm a new child process\n".as_bytes()).ok();
+/// unsafe { libc::_exit(0) };
+/// }
+/// Err(_) => println!("Fork failed"),
+/// }
+/// ```
+///
+/// This will print something like the following (order nondeterministic). The
+/// thing to note is that you end up with two processes continuing execution
+/// immediately after the fork call but with different match arms.
+///
+/// ```text
+/// Continuing execution in parent process, new child has pid: 1234
+/// I'm a new child process
+/// ```
+///
+/// # Safety
+///
+/// In a multithreaded program, only [async-signal-safe] functions like `pause`
+/// and `_exit` may be called by the child (the parent isn't restricted). Note
+/// that memory allocation may **not** be async-signal-safe and thus must be
+/// prevented.
+///
+/// Those functions are only a small subset of your operating system's API, so
+/// special care must be taken to only invoke code you can control and audit.
+///
+/// [async-signal-safe]: https://man7.org/linux/man-pages/man7/signal-safety.7.html
+#[inline]
+pub unsafe fn fork() -> Result<ForkResult> {
+ use self::ForkResult::*;
+ let res = libc::fork();
+
+ Errno::result(res).map(|res| match res {
+ 0 => Child,
+ res => Parent { child: Pid(res) },
+ })
+}
+
+/// Get the pid of this process (see
+/// [getpid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpid.html)).
+///
+/// Since you are running code, there is always a pid to return, so there
+/// is no error case that needs to be handled.
+#[inline]
+pub fn getpid() -> Pid {
+ Pid(unsafe { libc::getpid() })
+}
+
+/// Get the pid of this processes' parent (see
+/// [getpid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getppid.html)).
+///
+/// There is always a parent pid to return, so there is no error case that needs
+/// to be handled.
+#[inline]
+pub fn getppid() -> Pid {
+ Pid(unsafe { libc::getppid() }) // no error handling, according to man page: "These functions are always successful."
+}
+
+/// Set a process group ID (see
+/// [setpgid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/setpgid.html)).
+///
+/// Set the process group id (PGID) of a particular process. If a pid of zero
+/// is specified, then the pid of the calling process is used. Process groups
+/// may be used to group together a set of processes in order for the OS to
+/// apply some operations across the group.
+///
+/// `setsid()` may be used to create a new process group.
+#[inline]
+pub fn setpgid(pid: Pid, pgid: Pid) -> Result<()> {
+ let res = unsafe { libc::setpgid(pid.into(), pgid.into()) };
+ Errno::result(res).map(drop)
+}
+#[inline]
+pub fn getpgid(pid: Option<Pid>) -> Result<Pid> {
+ let res = unsafe { libc::getpgid(pid.unwrap_or(Pid(0)).into()) };
+ Errno::result(res).map(Pid)
+}
+
+/// Create new session and set process group id (see
+/// [setsid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/setsid.html)).
+#[inline]
+pub fn setsid() -> Result<Pid> {
+ Errno::result(unsafe { libc::setsid() }).map(Pid)
+}
+
+/// Get the process group ID of a session leader
+/// [getsid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsid.html).
+///
+/// Obtain the process group ID of the process that is the session leader of the process specified
+/// by pid. If pid is zero, it specifies the calling process.
+#[inline]
+#[cfg(not(target_os = "redox"))]
+pub fn getsid(pid: Option<Pid>) -> Result<Pid> {
+ let res = unsafe { libc::getsid(pid.unwrap_or(Pid(0)).into()) };
+ Errno::result(res).map(Pid)
+}
+}
+
+feature! {
+#![all(feature = "process", feature = "term")]
+/// Get the terminal foreground process group (see
+/// [tcgetpgrp(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetpgrp.html)).
+///
+/// Get the group process id (GPID) of the foreground process group on the
+/// terminal associated to file descriptor (FD).
+#[inline]
+pub fn tcgetpgrp(fd: c_int) -> Result<Pid> {
+ let res = unsafe { libc::tcgetpgrp(fd) };
+ Errno::result(res).map(Pid)
+}
+/// Set the terminal foreground process group (see
+/// [tcgetpgrp(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsetpgrp.html)).
+///
+/// Get the group process id (PGID) to the foreground process group on the
+/// terminal associated to file descriptor (FD).
+#[inline]
+pub fn tcsetpgrp(fd: c_int, pgrp: Pid) -> Result<()> {
+ let res = unsafe { libc::tcsetpgrp(fd, pgrp.into()) };
+ Errno::result(res).map(drop)
+}
+}
+
+feature! {
+#![feature = "process"]
+/// Get the group id of the calling process (see
+///[getpgrp(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpgrp.html)).
+///
+/// Get the process group id (PGID) of the calling process.
+/// According to the man page it is always successful.
+#[inline]
+pub fn getpgrp() -> Pid {
+ Pid(unsafe { libc::getpgrp() })
+}
+
+/// Get the caller's thread ID (see
+/// [gettid(2)](https://man7.org/linux/man-pages/man2/gettid.2.html).
+///
+/// This function is only available on Linux based systems. In a single
+/// threaded process, the main thread will have the same ID as the process. In
+/// a multithreaded process, each thread will have a unique thread id but the
+/// same process ID.
+///
+/// No error handling is required as a thread id should always exist for any
+/// process, even if threads are not being used.
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[inline]
+pub fn gettid() -> Pid {
+ Pid(unsafe { libc::syscall(libc::SYS_gettid) as pid_t })
+}
+}
+
+feature! {
+#![feature = "fs"]
+/// Create a copy of the specified file descriptor (see
+/// [dup(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/dup.html)).
+///
+/// The new file descriptor will have a new index but refer to the same
+/// resource as the old file descriptor and the old and new file descriptors may
+/// be used interchangeably. The new and old file descriptor share the same
+/// underlying resource, offset, and file status flags. The actual index used
+/// for the file descriptor will be the lowest fd index that is available.
+///
+/// The two file descriptors do not share file descriptor flags (e.g. `OFlag::FD_CLOEXEC`).
+#[inline]
+pub fn dup(oldfd: RawFd) -> Result<RawFd> {
+ let res = unsafe { libc::dup(oldfd) };
+
+ Errno::result(res)
+}
+
+/// Create a copy of the specified file descriptor using the specified fd (see
+/// [dup(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/dup.html)).
+///
+/// This function behaves similar to `dup()` except that it will try to use the
+/// specified fd instead of allocating a new one. See the man pages for more
+/// detail on the exact behavior of this function.
+#[inline]
+pub fn dup2(oldfd: RawFd, newfd: RawFd) -> Result<RawFd> {
+ let res = unsafe { libc::dup2(oldfd, newfd) };
+
+ Errno::result(res)
+}
+
+/// Create a new copy of the specified file descriptor using the specified fd
+/// and flags (see [dup(2)](https://man7.org/linux/man-pages/man2/dup.2.html)).
+///
+/// This function behaves similar to `dup2()` but allows for flags to be
+/// specified.
+pub fn dup3(oldfd: RawFd, newfd: RawFd, flags: OFlag) -> Result<RawFd> {
+ dup3_polyfill(oldfd, newfd, flags)
+}
+
+#[inline]
+fn dup3_polyfill(oldfd: RawFd, newfd: RawFd, flags: OFlag) -> Result<RawFd> {
+ if oldfd == newfd {
+ return Err(Errno::EINVAL);
+ }
+
+ let fd = dup2(oldfd, newfd)?;
+
+ if flags.contains(OFlag::O_CLOEXEC) {
+ if let Err(e) = fcntl(fd, F_SETFD(FdFlag::FD_CLOEXEC)) {
+ let _ = close(fd);
+ return Err(e);
+ }
+ }
+
+ Ok(fd)
+}
+
+/// Change the current working directory of the calling process (see
+/// [chdir(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/chdir.html)).
+///
+/// This function may fail in a number of different scenarios. See the man
+/// pages for additional details on possible failure cases.
+#[inline]
+pub fn chdir<P: ?Sized + NixPath>(path: &P) -> Result<()> {
+ let res = path.with_nix_path(|cstr| {
+ unsafe { libc::chdir(cstr.as_ptr()) }
+ })?;
+
+ Errno::result(res).map(drop)
+}
+
+/// Change the current working directory of the process to the one
+/// given as an open file descriptor (see
+/// [fchdir(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchdir.html)).
+///
+/// This function may fail in a number of different scenarios. See the man
+/// pages for additional details on possible failure cases.
+#[inline]
+#[cfg(not(target_os = "fuchsia"))]
+pub fn fchdir(dirfd: RawFd) -> Result<()> {
+ let res = unsafe { libc::fchdir(dirfd) };
+
+ Errno::result(res).map(drop)
+}
+
+/// Creates new directory `path` with access rights `mode`. (see [mkdir(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdir.html))
+///
+/// # Errors
+///
+/// There are several situations where mkdir might fail:
+///
+/// - current user has insufficient rights in the parent directory
+/// - the path already exists
+/// - the path name is too long (longer than `PATH_MAX`, usually 4096 on linux, 1024 on OS X)
+///
+/// # Example
+///
+/// ```rust
+/// use nix::unistd;
+/// use nix::sys::stat;
+/// use tempfile::tempdir;
+///
+/// let tmp_dir1 = tempdir().unwrap();
+/// let tmp_dir2 = tmp_dir1.path().join("new_dir");
+///
+/// // create new directory and give read, write and execute rights to the owner
+/// match unistd::mkdir(&tmp_dir2, stat::Mode::S_IRWXU) {
+/// Ok(_) => println!("created {:?}", tmp_dir2),
+/// Err(err) => println!("Error creating directory: {}", err),
+/// }
+/// ```
+#[inline]
+pub fn mkdir<P: ?Sized + NixPath>(path: &P, mode: Mode) -> Result<()> {
+ let res = path.with_nix_path(|cstr| {
+ unsafe { libc::mkdir(cstr.as_ptr(), mode.bits() as mode_t) }
+ })?;
+
+ Errno::result(res).map(drop)
+}
+
+/// Creates new fifo special file (named pipe) with path `path` and access rights `mode`.
+///
+/// # Errors
+///
+/// There are several situations where mkfifo might fail:
+///
+/// - current user has insufficient rights in the parent directory
+/// - the path already exists
+/// - the path name is too long (longer than `PATH_MAX`, usually 4096 on linux, 1024 on OS X)
+///
+/// For a full list consult
+/// [posix specification](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkfifo.html)
+///
+/// # Example
+///
+/// ```rust
+/// use nix::unistd;
+/// use nix::sys::stat;
+/// use tempfile::tempdir;
+///
+/// let tmp_dir = tempdir().unwrap();
+/// let fifo_path = tmp_dir.path().join("foo.pipe");
+///
+/// // create new fifo and give read, write and execute rights to the owner
+/// match unistd::mkfifo(&fifo_path, stat::Mode::S_IRWXU) {
+/// Ok(_) => println!("created {:?}", fifo_path),
+/// Err(err) => println!("Error creating fifo: {}", err),
+/// }
+/// ```
+#[inline]
+#[cfg(not(target_os = "redox"))] // RedoxFS does not support fifo yet
+pub fn mkfifo<P: ?Sized + NixPath>(path: &P, mode: Mode) -> Result<()> {
+ let res = path.with_nix_path(|cstr| {
+ unsafe { libc::mkfifo(cstr.as_ptr(), mode.bits() as mode_t) }
+ })?;
+
+ Errno::result(res).map(drop)
+}
+
+/// Creates new fifo special file (named pipe) with path `path` and access rights `mode`.
+///
+/// If `dirfd` has a value, then `path` is relative to directory associated with the file descriptor.
+///
+/// If `dirfd` is `None`, then `path` is relative to the current working directory.
+///
+/// # References
+///
+/// [mkfifoat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkfifoat.html).
+// mkfifoat is not implemented in OSX or android
+#[inline]
+#[cfg(not(any(
+ target_os = "macos", target_os = "ios", target_os = "haiku",
+ target_os = "android", target_os = "redox")))]
+pub fn mkfifoat<P: ?Sized + NixPath>(dirfd: Option<RawFd>, path: &P, mode: Mode) -> Result<()> {
+ let res = path.with_nix_path(|cstr| unsafe {
+ libc::mkfifoat(at_rawfd(dirfd), cstr.as_ptr(), mode.bits() as mode_t)
+ })?;
+
+ Errno::result(res).map(drop)
+}
+
+/// Creates a symbolic link at `path2` which points to `path1`.
+///
+/// If `dirfd` has a value, then `path2` is relative to directory associated
+/// with the file descriptor.
+///
+/// If `dirfd` is `None`, then `path2` is relative to the current working
+/// directory. This is identical to `libc::symlink(path1, path2)`.
+///
+/// See also [symlinkat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/symlinkat.html).
+#[cfg(not(target_os = "redox"))]
+pub fn symlinkat<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
+ path1: &P1,
+ dirfd: Option<RawFd>,
+ path2: &P2) -> Result<()> {
+ let res =
+ path1.with_nix_path(|path1| {
+ path2.with_nix_path(|path2| {
+ unsafe {
+ libc::symlinkat(
+ path1.as_ptr(),
+ dirfd.unwrap_or(libc::AT_FDCWD),
+ path2.as_ptr()
+ )
+ }
+ })
+ })??;
+ Errno::result(res).map(drop)
+}
+}
+
+// Double the buffer capacity up to limit. In case it already has
+// reached the limit, return Errno::ERANGE.
+#[cfg(any(feature = "fs", feature = "user"))]
+fn reserve_double_buffer_size<T>(buf: &mut Vec<T>, limit: usize) -> Result<()> {
+ use std::cmp::min;
+
+ if buf.capacity() >= limit {
+ return Err(Errno::ERANGE);
+ }
+
+ let capacity = min(buf.capacity() * 2, limit);
+ buf.reserve(capacity);
+
+ Ok(())
+}
+
+feature! {
+#![feature = "fs"]
+
+/// Returns the current directory as a `PathBuf`
+///
+/// Err is returned if the current user doesn't have the permission to read or search a component
+/// of the current path.
+///
+/// # Example
+///
+/// ```rust
+/// use nix::unistd;
+///
+/// // assume that we are allowed to get current directory
+/// let dir = unistd::getcwd().unwrap();
+/// println!("The current directory is {:?}", dir);
+/// ```
+#[inline]
+pub fn getcwd() -> Result<PathBuf> {
+ let mut buf = Vec::with_capacity(512);
+ loop {
+ unsafe {
+ let ptr = buf.as_mut_ptr() as *mut c_char;
+
+ // The buffer must be large enough to store the absolute pathname plus
+ // a terminating null byte, or else null is returned.
+ // To safely handle this we start with a reasonable size (512 bytes)
+ // and double the buffer size upon every error
+ if !libc::getcwd(ptr, buf.capacity()).is_null() {
+ let len = CStr::from_ptr(buf.as_ptr() as *const c_char).to_bytes().len();
+ buf.set_len(len);
+ buf.shrink_to_fit();
+ return Ok(PathBuf::from(OsString::from_vec(buf)));
+ } else {
+ let error = Errno::last();
+ // ERANGE means buffer was too small to store directory name
+ if error != Errno::ERANGE {
+ return Err(error);
+ }
+ }
+
+ // Trigger the internal buffer resizing logic.
+ reserve_double_buffer_size(&mut buf, PATH_MAX as usize)?;
+ }
+ }
+}
+}
+
+feature! {
+#![all(feature = "user", feature = "fs")]
+
+/// Computes the raw UID and GID values to pass to a `*chown` call.
+// The cast is not unnecessary on all platforms.
+#[allow(clippy::unnecessary_cast)]
+fn chown_raw_ids(owner: Option<Uid>, group: Option<Gid>) -> (libc::uid_t, libc::gid_t) {
+ // According to the POSIX specification, -1 is used to indicate that owner and group
+ // are not to be changed. Since uid_t and gid_t are unsigned types, we have to wrap
+ // around to get -1.
+ let uid = owner.map(Into::into)
+ .unwrap_or_else(|| (0 as uid_t).wrapping_sub(1));
+ let gid = group.map(Into::into)
+ .unwrap_or_else(|| (0 as gid_t).wrapping_sub(1));
+ (uid, gid)
+}
+
+/// Change the ownership of the file at `path` to be owned by the specified
+/// `owner` (user) and `group` (see
+/// [chown(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/chown.html)).
+///
+/// The owner/group for the provided path name will not be modified if `None` is
+/// provided for that argument. Ownership change will be attempted for the path
+/// only if `Some` owner/group is provided.
+#[inline]
+pub fn chown<P: ?Sized + NixPath>(path: &P, owner: Option<Uid>, group: Option<Gid>) -> Result<()> {
+ let res = path.with_nix_path(|cstr| {
+ let (uid, gid) = chown_raw_ids(owner, group);
+ unsafe { libc::chown(cstr.as_ptr(), uid, gid) }
+ })?;
+
+ Errno::result(res).map(drop)
+}
+
+/// Change the ownership of the file referred to by the open file descriptor `fd` to be owned by
+/// the specified `owner` (user) and `group` (see
+/// [fchown(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchown.html)).
+///
+/// The owner/group for the provided file will not be modified if `None` is
+/// provided for that argument. Ownership change will be attempted for the path
+/// only if `Some` owner/group is provided.
+#[inline]
+pub fn fchown(fd: RawFd, owner: Option<Uid>, group: Option<Gid>) -> Result<()> {
+ let (uid, gid) = chown_raw_ids(owner, group);
+ let res = unsafe { libc::fchown(fd, uid, gid) };
+ Errno::result(res).map(drop)
+}
+
+/// Flags for `fchownat` function.
+#[derive(Clone, Copy, Debug)]
+pub enum FchownatFlags {
+ FollowSymlink,
+ NoFollowSymlink,
+}
+
+/// Change the ownership of the file at `path` to be owned by the specified
+/// `owner` (user) and `group`.
+///
+/// The owner/group for the provided path name will not be modified if `None` is
+/// provided for that argument. Ownership change will be attempted for the path
+/// only if `Some` owner/group is provided.
+///
+/// The file to be changed is determined relative to the directory associated
+/// with the file descriptor `dirfd` or the current working directory
+/// if `dirfd` is `None`.
+///
+/// If `flag` is `FchownatFlags::NoFollowSymlink` and `path` names a symbolic link,
+/// then the mode of the symbolic link is changed.
+///
+/// `fchownat(None, path, owner, group, FchownatFlags::NoFollowSymlink)` is identical to
+/// a call `libc::lchown(path, owner, group)`. That's why `lchown` is unimplemented in
+/// the `nix` crate.
+///
+/// # References
+///
+/// [fchownat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchownat.html).
+#[cfg(not(target_os = "redox"))]
+pub fn fchownat<P: ?Sized + NixPath>(
+ dirfd: Option<RawFd>,
+ path: &P,
+ owner: Option<Uid>,
+ group: Option<Gid>,
+ flag: FchownatFlags,
+) -> Result<()> {
+ let atflag =
+ match flag {
+ FchownatFlags::FollowSymlink => AtFlags::empty(),
+ FchownatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW,
+ };
+ let res = path.with_nix_path(|cstr| unsafe {
+ let (uid, gid) = chown_raw_ids(owner, group);
+ libc::fchownat(at_rawfd(dirfd), cstr.as_ptr(), uid, gid,
+ atflag.bits() as libc::c_int)
+ })?;
+
+ Errno::result(res).map(drop)
+}
+}
+
+feature! {
+#![feature = "process"]
+fn to_exec_array<S: AsRef<CStr>>(args: &[S]) -> Vec<*const c_char> {
+ use std::iter::once;
+ args.iter()
+ .map(|s| s.as_ref().as_ptr())
+ .chain(once(ptr::null()))
+ .collect()
+}
+
+/// Replace the current process image with a new one (see
+/// [exec(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html)).
+///
+/// See the `::nix::unistd::execve` system call for additional details. `execv`
+/// performs the same action but does not allow for customization of the
+/// environment for the new process.
+#[inline]
+pub fn execv<S: AsRef<CStr>>(path: &CStr, argv: &[S]) -> Result<Infallible> {
+ let args_p = to_exec_array(argv);
+
+ unsafe {
+ libc::execv(path.as_ptr(), args_p.as_ptr())
+ };
+
+ Err(Errno::last())
+}
+
+
+/// Replace the current process image with a new one (see
+/// [execve(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html)).
+///
+/// The execve system call allows for another process to be "called" which will
+/// replace the current process image. That is, this process becomes the new
+/// command that is run. On success, this function will not return. Instead,
+/// the new program will run until it exits.
+///
+/// `::nix::unistd::execv` and `::nix::unistd::execve` take as arguments a slice
+/// of `::std::ffi::CString`s for `args` and `env` (for `execve`). Each element
+/// in the `args` list is an argument to the new process. Each element in the
+/// `env` list should be a string in the form "key=value".
+#[inline]
+pub fn execve<SA: AsRef<CStr>, SE: AsRef<CStr>>(path: &CStr, args: &[SA], env: &[SE]) -> Result<Infallible> {
+ let args_p = to_exec_array(args);
+ let env_p = to_exec_array(env);
+
+ unsafe {
+ libc::execve(path.as_ptr(), args_p.as_ptr(), env_p.as_ptr())
+ };
+
+ Err(Errno::last())
+}
+
+/// Replace the current process image with a new one and replicate shell `PATH`
+/// searching behavior (see
+/// [exec(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html)).
+///
+/// See `::nix::unistd::execve` for additional details. `execvp` behaves the
+/// same as execv except that it will examine the `PATH` environment variables
+/// for file names not specified with a leading slash. For example, `execv`
+/// would not work if "bash" was specified for the path argument, but `execvp`
+/// would assuming that a bash executable was on the system `PATH`.
+#[inline]
+pub fn execvp<S: AsRef<CStr>>(filename: &CStr, args: &[S]) -> Result<Infallible> {
+ let args_p = to_exec_array(args);
+
+ unsafe {
+ libc::execvp(filename.as_ptr(), args_p.as_ptr())
+ };
+
+ Err(Errno::last())
+}
+
+/// Replace the current process image with a new one and replicate shell `PATH`
+/// searching behavior (see
+/// [`execvpe(3)`](https://man7.org/linux/man-pages/man3/exec.3.html)).
+///
+/// This functions like a combination of `execvp(2)` and `execve(2)` to pass an
+/// environment and have a search path. See these two for additional
+/// information.
+#[cfg(any(target_os = "haiku",
+ target_os = "linux",
+ target_os = "openbsd"))]
+pub fn execvpe<SA: AsRef<CStr>, SE: AsRef<CStr>>(filename: &CStr, args: &[SA], env: &[SE]) -> Result<Infallible> {
+ let args_p = to_exec_array(args);
+ let env_p = to_exec_array(env);
+
+ unsafe {
+ libc::execvpe(filename.as_ptr(), args_p.as_ptr(), env_p.as_ptr())
+ };
+
+ Err(Errno::last())
+}
+
+/// Replace the current process image with a new one (see
+/// [fexecve(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fexecve.html)).
+///
+/// The `fexecve` function allows for another process to be "called" which will
+/// replace the current process image. That is, this process becomes the new
+/// command that is run. On success, this function will not return. Instead,
+/// the new program will run until it exits.
+///
+/// This function is similar to `execve`, except that the program to be executed
+/// is referenced as a file descriptor instead of a path.
+#[cfg(any(target_os = "android",
+ target_os = "linux",
+ target_os = "dragonfly",
+ target_os = "freebsd"))]
+#[inline]
+pub fn fexecve<SA: AsRef<CStr> ,SE: AsRef<CStr>>(fd: RawFd, args: &[SA], env: &[SE]) -> Result<Infallible> {
+ let args_p = to_exec_array(args);
+ let env_p = to_exec_array(env);
+
+ unsafe {
+ libc::fexecve(fd, args_p.as_ptr(), env_p.as_ptr())
+ };
+
+ Err(Errno::last())
+}
+
+/// Execute program relative to a directory file descriptor (see
+/// [execveat(2)](https://man7.org/linux/man-pages/man2/execveat.2.html)).
+///
+/// The `execveat` function allows for another process to be "called" which will
+/// replace the current process image. That is, this process becomes the new
+/// command that is run. On success, this function will not return. Instead,
+/// the new program will run until it exits.
+///
+/// This function is similar to `execve`, except that the program to be executed
+/// is referenced as a file descriptor to the base directory plus a path.
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[inline]
+pub fn execveat<SA: AsRef<CStr>,SE: AsRef<CStr>>(dirfd: RawFd, pathname: &CStr, args: &[SA],
+ env: &[SE], flags: super::fcntl::AtFlags) -> Result<Infallible> {
+ let args_p = to_exec_array(args);
+ let env_p = to_exec_array(env);
+
+ unsafe {
+ libc::syscall(libc::SYS_execveat, dirfd, pathname.as_ptr(),
+ args_p.as_ptr(), env_p.as_ptr(), flags);
+ };
+
+ Err(Errno::last())
+}
+
+/// Daemonize this process by detaching from the controlling terminal (see
+/// [daemon(3)](https://man7.org/linux/man-pages/man3/daemon.3.html)).
+///
+/// When a process is launched it is typically associated with a parent and it,
+/// in turn, by its controlling terminal/process. In order for a process to run
+/// in the "background" it must daemonize itself by detaching itself. Under
+/// posix, this is done by doing the following:
+///
+/// 1. Parent process (this one) forks
+/// 2. Parent process exits
+/// 3. Child process continues to run.
+///
+/// `nochdir`:
+///
+/// * `nochdir = true`: The current working directory after daemonizing will
+/// be the current working directory.
+/// * `nochdir = false`: The current working directory after daemonizing will
+/// be the root direcory, `/`.
+///
+/// `noclose`:
+///
+/// * `noclose = true`: The process' current stdin, stdout, and stderr file
+/// descriptors will remain identical after daemonizing.
+/// * `noclose = false`: The process' stdin, stdout, and stderr will point to
+/// `/dev/null` after daemonizing.
+#[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 = "solaris"))]
+pub fn daemon(nochdir: bool, noclose: bool) -> Result<()> {
+ let res = unsafe { libc::daemon(nochdir as c_int, noclose as c_int) };
+ Errno::result(res).map(drop)
+}
+}
+
+feature! {
+#![feature = "hostname"]
+
+/// Set the system host name (see
+/// [sethostname(2)](https://man7.org/linux/man-pages/man2/gethostname.2.html)).
+///
+/// Given a name, attempt to update the system host name to the given string.
+/// On some systems, the host name is limited to as few as 64 bytes. An error
+/// will be returned if the name is not valid or the current process does not
+/// have permissions to update the host name.
+#[cfg(not(target_os = "redox"))]
+pub fn sethostname<S: AsRef<OsStr>>(name: S) -> Result<()> {
+ // Handle some differences in type of the len arg across platforms.
+ cfg_if! {
+ if #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "solaris", ))] {
+ type sethostname_len_t = c_int;
+ } else {
+ type sethostname_len_t = size_t;
+ }
+ }
+ let ptr = name.as_ref().as_bytes().as_ptr() as *const c_char;
+ let len = name.as_ref().len() as sethostname_len_t;
+
+ let res = unsafe { libc::sethostname(ptr, len) };
+ Errno::result(res).map(drop)
+}
+
+/// Get the host name and store it in an internally allocated buffer, returning an
+/// `OsString` on success (see
+/// [gethostname(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/gethostname.html)).
+///
+/// This function call attempts to get the host name for the running system and
+/// store it in an internal buffer, returning it as an `OsString` if successful.
+///
+/// ```no_run
+/// use nix::unistd;
+///
+/// let hostname = unistd::gethostname().expect("Failed getting hostname");
+/// let hostname = hostname.into_string().expect("Hostname wasn't valid UTF-8");
+/// println!("Hostname: {}", hostname);
+/// ```
+pub fn gethostname() -> Result<OsString> {
+ // The capacity is the max length of a hostname plus the NUL terminator.
+ let mut buffer: Vec<u8> = Vec::with_capacity(256);
+ let ptr = buffer.as_mut_ptr() as *mut c_char;
+ let len = buffer.capacity() as size_t;
+
+ let res = unsafe { libc::gethostname(ptr, len) };
+ Errno::result(res).map(|_| {
+ unsafe {
+ buffer.as_mut_ptr().wrapping_add(len - 1).write(0); // ensure always null-terminated
+ let len = CStr::from_ptr(buffer.as_ptr() as *const c_char).len();
+ buffer.set_len(len);
+ }
+ OsString::from_vec(buffer)
+ })
+}
+}
+
+/// Close a raw file descriptor
+///
+/// Be aware that many Rust types implicitly close-on-drop, including
+/// `std::fs::File`. Explicitly closing them with this method too can result in
+/// a double-close condition, which can cause confusing `EBADF` errors in
+/// seemingly unrelated code. Caveat programmer. See also
+/// [close(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html).
+///
+/// # Examples
+///
+/// ```no_run
+/// use std::os::unix::io::AsRawFd;
+/// use nix::unistd::close;
+///
+/// let f = tempfile::tempfile().unwrap();
+/// close(f.as_raw_fd()).unwrap(); // Bad! f will also close on drop!
+/// ```
+///
+/// ```rust
+/// use std::os::unix::io::IntoRawFd;
+/// use nix::unistd::close;
+///
+/// let f = tempfile::tempfile().unwrap();
+/// close(f.into_raw_fd()).unwrap(); // Good. into_raw_fd consumes f
+/// ```
+pub fn close(fd: RawFd) -> Result<()> {
+ let res = unsafe { libc::close(fd) };
+ Errno::result(res).map(drop)
+}
+
+/// Read from a raw file descriptor.
+///
+/// See also [read(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/read.html)
+pub fn read(fd: RawFd, buf: &mut [u8]) -> Result<usize> {
+ let res = unsafe {
+ libc::read(fd, buf.as_mut_ptr() as *mut c_void, buf.len() as size_t)
+ };
+
+ Errno::result(res).map(|r| r as usize)
+}
+
+/// Write to a raw file descriptor.
+///
+/// See also [write(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html)
+pub fn write(fd: RawFd, buf: &[u8]) -> Result<usize> {
+ let res = unsafe {
+ libc::write(fd, buf.as_ptr() as *const c_void, buf.len() as size_t)
+ };
+
+ Errno::result(res).map(|r| r as usize)
+}
+
+feature! {
+#![feature = "fs"]
+
+/// Directive that tells [`lseek`] and [`lseek64`] what the offset is relative to.
+///
+/// [`lseek`]: ./fn.lseek.html
+/// [`lseek64`]: ./fn.lseek64.html
+#[repr(i32)]
+#[derive(Clone, Copy, Debug)]
+pub enum Whence {
+ /// Specify an offset relative to the start of the file.
+ SeekSet = libc::SEEK_SET,
+ /// Specify an offset relative to the current file location.
+ SeekCur = libc::SEEK_CUR,
+ /// Specify an offset relative to the end of the file.
+ SeekEnd = libc::SEEK_END,
+ /// Specify an offset relative to the next location in the file greater than or
+ /// equal to offset that contains some data. If offset points to
+ /// some data, then the file offset is set to offset.
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris"))]
+ SeekData = libc::SEEK_DATA,
+ /// Specify an offset relative to the next hole in the file greater than
+ /// or equal to offset. If offset points into the middle of a hole, then
+ /// the file offset should be set to offset. If there is no hole past offset,
+ /// then the file offset should be adjusted to the end of the file (i.e., there
+ /// is an implicit hole at the end of any file).
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris"))]
+ SeekHole = libc::SEEK_HOLE
+}
+
+/// Move the read/write file offset.
+///
+/// See also [lseek(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/lseek.html)
+pub fn lseek(fd: RawFd, offset: off_t, whence: Whence) -> Result<off_t> {
+ let res = unsafe { libc::lseek(fd, offset, whence as i32) };
+
+ Errno::result(res).map(|r| r as off_t)
+}
+
+#[cfg(any(target_os = "linux", target_os = "android"))]
+pub fn lseek64(fd: RawFd, offset: libc::off64_t, whence: Whence) -> Result<libc::off64_t> {
+ let res = unsafe { libc::lseek64(fd, offset, whence as i32) };
+
+ Errno::result(res).map(|r| r as libc::off64_t)
+}
+}
+
+/// Create an interprocess channel.
+///
+/// See also [pipe(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pipe.html)
+pub fn pipe() -> std::result::Result<(RawFd, RawFd), Error> {
+ let mut fds = mem::MaybeUninit::<[c_int; 2]>::uninit();
+
+ let res = unsafe { libc::pipe(fds.as_mut_ptr() as *mut c_int) };
+
+ Error::result(res)?;
+
+ unsafe { Ok((fds.assume_init()[0], fds.assume_init()[1])) }
+}
+
+feature! {
+#![feature = "fs"]
+/// Like `pipe`, but allows setting certain file descriptor flags.
+///
+/// The following flags are supported, and will be set atomically as the pipe is
+/// created:
+///
+/// - `O_CLOEXEC`: Set the close-on-exec flag for the new file descriptors.
+#[cfg_attr(target_os = "linux", doc = "- `O_DIRECT`: Create a pipe that performs I/O in \"packet\" mode.")]
+#[cfg_attr(target_os = "netbsd", doc = "- `O_NOSIGPIPE`: Return `EPIPE` instead of raising `SIGPIPE`.")]
+/// - `O_NONBLOCK`: Set the non-blocking flag for the ends of the pipe.
+///
+/// See also [pipe(2)](https://man7.org/linux/man-pages/man2/pipe.2.html)
+#[cfg(any(target_os = "android",
+ target_os = "dragonfly",
+ target_os = "emscripten",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "redox",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "solaris"))]
+pub fn pipe2(flags: OFlag) -> Result<(RawFd, RawFd)> {
+ let mut fds = mem::MaybeUninit::<[c_int; 2]>::uninit();
+
+ let res = unsafe {
+ libc::pipe2(fds.as_mut_ptr() as *mut c_int, flags.bits())
+ };
+
+ Errno::result(res)?;
+
+ unsafe { Ok((fds.assume_init()[0], fds.assume_init()[1])) }
+}
+
+/// Truncate a file to a specified length
+///
+/// See also
+/// [truncate(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/truncate.html)
+#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
+pub fn truncate<P: ?Sized + NixPath>(path: &P, len: off_t) -> Result<()> {
+ let res = path.with_nix_path(|cstr| {
+ unsafe {
+ libc::truncate(cstr.as_ptr(), len)
+ }
+ })?;
+
+ Errno::result(res).map(drop)
+}
+
+/// Truncate a file to a specified length
+///
+/// See also
+/// [ftruncate(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html)
+pub fn ftruncate(fd: RawFd, len: off_t) -> Result<()> {
+ Errno::result(unsafe { libc::ftruncate(fd, len) }).map(drop)
+}
+
+pub fn isatty(fd: RawFd) -> Result<bool> {
+ unsafe {
+ // ENOTTY means `fd` is a valid file descriptor, but not a TTY, so
+ // we return `Ok(false)`
+ if libc::isatty(fd) == 1 {
+ Ok(true)
+ } else {
+ match Errno::last() {
+ Errno::ENOTTY => Ok(false),
+ err => Err(err),
+ }
+ }
+ }
+}
+
+/// Flags for `linkat` function.
+#[derive(Clone, Copy, Debug)]
+pub enum LinkatFlags {
+ SymlinkFollow,
+ NoSymlinkFollow,
+}
+
+/// Link one file to another file
+///
+/// Creates a new link (directory entry) at `newpath` for the existing file at `oldpath`. In the
+/// case of a relative `oldpath`, the path is interpreted relative to the directory associated
+/// with file descriptor `olddirfd` instead of the current working directory and similiarly for
+/// `newpath` and file descriptor `newdirfd`. In case `flag` is LinkatFlags::SymlinkFollow and
+/// `oldpath` names a symoblic link, a new link for the target of the symbolic link is created.
+/// If either `olddirfd` or `newdirfd` is `None`, `AT_FDCWD` is used respectively where `oldpath`
+/// and/or `newpath` is then interpreted relative to the current working directory of the calling
+/// process. If either `oldpath` or `newpath` is absolute, then `dirfd` is ignored.
+///
+/// # References
+/// See also [linkat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/linkat.html)
+#[cfg(not(target_os = "redox"))] // RedoxFS does not support symlinks yet
+pub fn linkat<P: ?Sized + NixPath>(
+ olddirfd: Option<RawFd>,
+ oldpath: &P,
+ newdirfd: Option<RawFd>,
+ newpath: &P,
+ flag: LinkatFlags,
+) -> Result<()> {
+
+ let atflag =
+ match flag {
+ LinkatFlags::SymlinkFollow => AtFlags::AT_SYMLINK_FOLLOW,
+ LinkatFlags::NoSymlinkFollow => AtFlags::empty(),
+ };
+
+ let res =
+ oldpath.with_nix_path(|oldcstr| {
+ newpath.with_nix_path(|newcstr| {
+ unsafe {
+ libc::linkat(
+ at_rawfd(olddirfd),
+ oldcstr.as_ptr(),
+ at_rawfd(newdirfd),
+ newcstr.as_ptr(),
+ atflag.bits() as libc::c_int
+ )
+ }
+ })
+ })??;
+ Errno::result(res).map(drop)
+}
+
+
+/// Remove a directory entry
+///
+/// See also [unlink(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlink.html)
+pub fn unlink<P: ?Sized + NixPath>(path: &P) -> Result<()> {
+ let res = path.with_nix_path(|cstr| {
+ unsafe {
+ libc::unlink(cstr.as_ptr())
+ }
+ })?;
+ Errno::result(res).map(drop)
+}
+
+/// Flags for `unlinkat` function.
+#[derive(Clone, Copy, Debug)]
+pub enum UnlinkatFlags {
+ RemoveDir,
+ NoRemoveDir,
+}
+
+/// Remove a directory entry
+///
+/// In the case of a relative path, the directory entry to be removed is determined relative to
+/// the directory associated with the file descriptor `dirfd` or the current working directory
+/// if `dirfd` is `None`. In the case of an absolute `path` `dirfd` is ignored. If `flag` is
+/// `UnlinkatFlags::RemoveDir` then removal of the directory entry specified by `dirfd` and `path`
+/// is performed.
+///
+/// # References
+/// See also [unlinkat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlinkat.html)
+#[cfg(not(target_os = "redox"))]
+pub fn unlinkat<P: ?Sized + NixPath>(
+ dirfd: Option<RawFd>,
+ path: &P,
+ flag: UnlinkatFlags,
+) -> Result<()> {
+ let atflag =
+ match flag {
+ UnlinkatFlags::RemoveDir => AtFlags::AT_REMOVEDIR,
+ UnlinkatFlags::NoRemoveDir => AtFlags::empty(),
+ };
+ let res = path.with_nix_path(|cstr| {
+ unsafe {
+ libc::unlinkat(at_rawfd(dirfd), cstr.as_ptr(), atflag.bits() as libc::c_int)
+ }
+ })?;
+ Errno::result(res).map(drop)
+}
+
+
+#[inline]
+#[cfg(not(target_os = "fuchsia"))]
+pub fn chroot<P: ?Sized + NixPath>(path: &P) -> Result<()> {
+ let res = path.with_nix_path(|cstr| {
+ unsafe { libc::chroot(cstr.as_ptr()) }
+ })?;
+
+ Errno::result(res).map(drop)
+}
+
+/// Commit filesystem caches to disk
+///
+/// See also [sync(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sync.html)
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd"
+))]
+pub fn sync() {
+ unsafe { libc::sync() };
+}
+
+/// Commit filesystem caches containing file referred to by the open file
+/// descriptor `fd` to disk
+///
+/// See also [syncfs(2)](https://man7.org/linux/man-pages/man2/sync.2.html)
+#[cfg(target_os = "linux")]
+pub fn syncfs(fd: RawFd) -> Result<()> {
+ let res = unsafe { libc::syncfs(fd) };
+
+ Errno::result(res).map(drop)
+}
+
+/// Synchronize changes to a file
+///
+/// See also [fsync(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fsync.html)
+#[inline]
+pub fn fsync(fd: RawFd) -> Result<()> {
+ let res = unsafe { libc::fsync(fd) };
+
+ Errno::result(res).map(drop)
+}
+
+/// Synchronize the data of a file
+///
+/// See also
+/// [fdatasync(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fdatasync.html)
+#[cfg(any(target_os = "linux",
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "illumos",
+ target_os = "solaris"))]
+#[inline]
+pub fn fdatasync(fd: RawFd) -> Result<()> {
+ let res = unsafe { libc::fdatasync(fd) };
+
+ Errno::result(res).map(drop)
+}
+}
+
+feature! {
+#![feature = "user"]
+
+/// Get a real user ID
+///
+/// See also [getuid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getuid.html)
+// POSIX requires that getuid is always successful, so no need to check return
+// value or errno.
+#[inline]
+pub fn getuid() -> Uid {
+ Uid(unsafe { libc::getuid() })
+}
+
+/// Get the effective user ID
+///
+/// See also [geteuid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/geteuid.html)
+// POSIX requires that geteuid is always successful, so no need to check return
+// value or errno.
+#[inline]
+pub fn geteuid() -> Uid {
+ Uid(unsafe { libc::geteuid() })
+}
+
+/// Get the real group ID
+///
+/// See also [getgid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getgid.html)
+// POSIX requires that getgid is always successful, so no need to check return
+// value or errno.
+#[inline]
+pub fn getgid() -> Gid {
+ Gid(unsafe { libc::getgid() })
+}
+
+/// Get the effective group ID
+///
+/// See also [getegid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getegid.html)
+// POSIX requires that getegid is always successful, so no need to check return
+// value or errno.
+#[inline]
+pub fn getegid() -> Gid {
+ Gid(unsafe { libc::getegid() })
+}
+
+/// Set the effective user ID
+///
+/// See also [seteuid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/seteuid.html)
+#[inline]
+pub fn seteuid(euid: Uid) -> Result<()> {
+ let res = unsafe { libc::seteuid(euid.into()) };
+
+ Errno::result(res).map(drop)
+}
+
+/// Set the effective group ID
+///
+/// See also [setegid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/setegid.html)
+#[inline]
+pub fn setegid(egid: Gid) -> Result<()> {
+ let res = unsafe { libc::setegid(egid.into()) };
+
+ Errno::result(res).map(drop)
+}
+
+/// Set the user ID
+///
+/// See also [setuid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/setuid.html)
+#[inline]
+pub fn setuid(uid: Uid) -> Result<()> {
+ let res = unsafe { libc::setuid(uid.into()) };
+
+ Errno::result(res).map(drop)
+}
+
+/// Set the group ID
+///
+/// See also [setgid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/setgid.html)
+#[inline]
+pub fn setgid(gid: Gid) -> Result<()> {
+ let res = unsafe { libc::setgid(gid.into()) };
+
+ Errno::result(res).map(drop)
+}
+}
+
+feature! {
+#![all(feature = "fs", feature = "user")]
+/// Set the user identity used for filesystem checks per-thread.
+/// On both success and failure, this call returns the previous filesystem user
+/// ID of the caller.
+///
+/// See also [setfsuid(2)](https://man7.org/linux/man-pages/man2/setfsuid.2.html)
+#[cfg(any(target_os = "linux", target_os = "android"))]
+pub fn setfsuid(uid: Uid) -> Uid {
+ let prev_fsuid = unsafe { libc::setfsuid(uid.into()) };
+ Uid::from_raw(prev_fsuid as uid_t)
+}
+
+/// Set the group identity used for filesystem checks per-thread.
+/// On both success and failure, this call returns the previous filesystem group
+/// ID of the caller.
+///
+/// See also [setfsgid(2)](https://man7.org/linux/man-pages/man2/setfsgid.2.html)
+#[cfg(any(target_os = "linux", target_os = "android"))]
+pub fn setfsgid(gid: Gid) -> Gid {
+ let prev_fsgid = unsafe { libc::setfsgid(gid.into()) };
+ Gid::from_raw(prev_fsgid as gid_t)
+}
+}
+
+feature! {
+#![feature = "user"]
+
+/// Get the list of supplementary group IDs of the calling process.
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/009695399/functions/getgroups.html)
+///
+/// **Note:** This function is not available for Apple platforms. On those
+/// platforms, checking group membership should be achieved via communication
+/// with the `opendirectoryd` service.
+#[cfg(not(any(target_os = "ios", target_os = "macos")))]
+pub fn getgroups() -> Result<Vec<Gid>> {
+ // First get the maximum number of groups. The value returned
+ // shall always be greater than or equal to one and less than or
+ // equal to the value of {NGROUPS_MAX} + 1.
+ let ngroups_max = match sysconf(SysconfVar::NGROUPS_MAX) {
+ Ok(Some(n)) => (n + 1) as usize,
+ Ok(None) | Err(_) => <usize>::max_value(),
+ };
+
+ // Next, get the number of groups so we can size our Vec
+ let ngroups = unsafe { libc::getgroups(0, ptr::null_mut()) };
+
+ // If there are no supplementary groups, return early.
+ // This prevents a potential buffer over-read if the number of groups
+ // increases from zero before the next call. It would return the total
+ // number of groups beyond the capacity of the buffer.
+ if ngroups == 0 {
+ return Ok(Vec::new());
+ }
+
+ // Now actually get the groups. We try multiple times in case the number of
+ // groups has changed since the first call to getgroups() and the buffer is
+ // now too small.
+ let mut groups = Vec::<Gid>::with_capacity(Errno::result(ngroups)? as usize);
+ loop {
+ // FIXME: On the platforms we currently support, the `Gid` struct has
+ // the same representation in memory as a bare `gid_t`. This is not
+ // necessarily the case on all Rust platforms, though. See RFC 1785.
+ let ngroups = unsafe {
+ libc::getgroups(groups.capacity() as c_int, groups.as_mut_ptr() as *mut gid_t)
+ };
+
+ match Errno::result(ngroups) {
+ Ok(s) => {
+ unsafe { groups.set_len(s as usize) };
+ return Ok(groups);
+ },
+ Err(Errno::EINVAL) => {
+ // EINVAL indicates that the buffer size was too
+ // small, resize it up to ngroups_max as limit.
+ reserve_double_buffer_size(&mut groups, ngroups_max)
+ .or(Err(Errno::EINVAL))?;
+ },
+ Err(e) => return Err(e)
+ }
+ }
+}
+
+/// Set the list of supplementary group IDs for the calling process.
+///
+/// [Further reading](https://man7.org/linux/man-pages/man2/getgroups.2.html)
+///
+/// **Note:** This function is not available for Apple platforms. On those
+/// platforms, group membership management should be achieved via communication
+/// with the `opendirectoryd` service.
+///
+/// # Examples
+///
+/// `setgroups` can be used when dropping privileges from the root user to a
+/// specific user and group. For example, given the user `www-data` with UID
+/// `33` and the group `backup` with the GID `34`, one could switch the user as
+/// follows:
+///
+/// ```rust,no_run
+/// # use std::error::Error;
+/// # use nix::unistd::*;
+/// #
+/// # fn try_main() -> Result<(), Box<dyn Error>> {
+/// let uid = Uid::from_raw(33);
+/// let gid = Gid::from_raw(34);
+/// setgroups(&[gid])?;
+/// setgid(gid)?;
+/// setuid(uid)?;
+/// #
+/// # Ok(())
+/// # }
+/// #
+/// # try_main().unwrap();
+/// ```
+#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox", target_os = "haiku")))]
+pub fn setgroups(groups: &[Gid]) -> Result<()> {
+ cfg_if! {
+ if #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "illumos",
+ target_os = "openbsd"))] {
+ type setgroups_ngroups_t = c_int;
+ } else {
+ type setgroups_ngroups_t = size_t;
+ }
+ }
+ // FIXME: On the platforms we currently support, the `Gid` struct has the
+ // same representation in memory as a bare `gid_t`. This is not necessarily
+ // the case on all Rust platforms, though. See RFC 1785.
+ let res = unsafe {
+ libc::setgroups(groups.len() as setgroups_ngroups_t, groups.as_ptr() as *const gid_t)
+ };
+
+ Errno::result(res).map(drop)
+}
+
+/// Calculate the supplementary group access list.
+///
+/// Gets the group IDs of all groups that `user` is a member of. The additional
+/// group `group` is also added to the list.
+///
+/// [Further reading](https://man7.org/linux/man-pages/man3/getgrouplist.3.html)
+///
+/// **Note:** This function is not available for Apple platforms. On those
+/// platforms, checking group membership should be achieved via communication
+/// with the `opendirectoryd` service.
+///
+/// # Errors
+///
+/// Although the `getgrouplist()` call does not return any specific
+/// errors on any known platforms, this implementation will return a system
+/// error of `EINVAL` if the number of groups to be fetched exceeds the
+/// `NGROUPS_MAX` sysconf value. This mimics the behaviour of `getgroups()`
+/// and `setgroups()`. Additionally, while some implementations will return a
+/// partial list of groups when `NGROUPS_MAX` is exceeded, this implementation
+/// will only ever return the complete list or else an error.
+#[cfg(not(any(target_os = "illumos",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "redox")))]
+pub fn getgrouplist(user: &CStr, group: Gid) -> Result<Vec<Gid>> {
+ let ngroups_max = match sysconf(SysconfVar::NGROUPS_MAX) {
+ Ok(Some(n)) => n as c_int,
+ Ok(None) | Err(_) => <c_int>::max_value(),
+ };
+ use std::cmp::min;
+ let mut groups = Vec::<Gid>::with_capacity(min(ngroups_max, 8) as usize);
+ cfg_if! {
+ if #[cfg(any(target_os = "ios", target_os = "macos"))] {
+ type getgrouplist_group_t = c_int;
+ } else {
+ type getgrouplist_group_t = gid_t;
+ }
+ }
+ let gid: gid_t = group.into();
+ loop {
+ let mut ngroups = groups.capacity() as i32;
+ let ret = unsafe {
+ libc::getgrouplist(user.as_ptr(),
+ gid as getgrouplist_group_t,
+ groups.as_mut_ptr() as *mut getgrouplist_group_t,
+ &mut ngroups)
+ };
+
+ // BSD systems only return 0 or -1, Linux returns ngroups on success.
+ if ret >= 0 {
+ unsafe { groups.set_len(ngroups as usize) };
+ return Ok(groups);
+ } else if ret == -1 {
+ // Returns -1 if ngroups is too small, but does not set errno.
+ // BSD systems will still fill the groups buffer with as many
+ // groups as possible, but Linux manpages do not mention this
+ // behavior.
+ reserve_double_buffer_size(&mut groups, ngroups_max as usize)
+ .map_err(|_| Errno::EINVAL)?;
+ }
+ }
+}
+
+/// Initialize the supplementary group access list.
+///
+/// Sets the supplementary group IDs for the calling process using all groups
+/// that `user` is a member of. The additional group `group` is also added to
+/// the list.
+///
+/// [Further reading](https://man7.org/linux/man-pages/man3/initgroups.3.html)
+///
+/// **Note:** This function is not available for Apple platforms. On those
+/// platforms, group membership management should be achieved via communication
+/// with the `opendirectoryd` service.
+///
+/// # Examples
+///
+/// `initgroups` can be used when dropping privileges from the root user to
+/// another user. For example, given the user `www-data`, we could look up the
+/// UID and GID for the user in the system's password database (usually found
+/// in `/etc/passwd`). If the `www-data` user's UID and GID were `33` and `33`,
+/// respectively, one could switch the user as follows:
+///
+/// ```rust,no_run
+/// # use std::error::Error;
+/// # use std::ffi::CString;
+/// # use nix::unistd::*;
+/// #
+/// # fn try_main() -> Result<(), Box<dyn Error>> {
+/// let user = CString::new("www-data").unwrap();
+/// let uid = Uid::from_raw(33);
+/// let gid = Gid::from_raw(33);
+/// initgroups(&user, gid)?;
+/// setgid(gid)?;
+/// setuid(uid)?;
+/// #
+/// # Ok(())
+/// # }
+/// #
+/// # try_main().unwrap();
+/// ```
+#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox", target_os = "haiku")))]
+pub fn initgroups(user: &CStr, group: Gid) -> Result<()> {
+ cfg_if! {
+ if #[cfg(any(target_os = "ios", target_os = "macos"))] {
+ type initgroups_group_t = c_int;
+ } else {
+ type initgroups_group_t = gid_t;
+ }
+ }
+ let gid: gid_t = group.into();
+ let res = unsafe { libc::initgroups(user.as_ptr(), gid as initgroups_group_t) };
+
+ Errno::result(res).map(drop)
+}
+}
+
+feature! {
+#![feature = "signal"]
+
+/// Suspend the thread until a signal is received.
+///
+/// See also [pause(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pause.html).
+#[inline]
+#[cfg(not(target_os = "redox"))]
+pub fn pause() {
+ unsafe { libc::pause() };
+}
+
+pub mod alarm {
+ //! Alarm signal scheduling.
+ //!
+ //! Scheduling an alarm will trigger a `SIGALRM` signal when the time has
+ //! elapsed, which has to be caught, because the default action for the
+ //! signal is to terminate the program. This signal also can't be ignored
+ //! because the system calls like `pause` will not be interrupted, see the
+ //! second example below.
+ //!
+ //! # Examples
+ //!
+ //! Canceling an alarm:
+ //!
+ //! ```
+ //! use nix::unistd::alarm;
+ //!
+ //! // Set an alarm for 60 seconds from now.
+ //! alarm::set(60);
+ //!
+ //! // Cancel the above set alarm, which returns the number of seconds left
+ //! // of the previously set alarm.
+ //! assert_eq!(alarm::cancel(), Some(60));
+ //! ```
+ //!
+ //! Scheduling an alarm and waiting for the signal:
+ //!
+#![cfg_attr(target_os = "redox", doc = " ```rust,ignore")]
+#![cfg_attr(not(target_os = "redox"), doc = " ```rust")]
+ //! use std::time::{Duration, Instant};
+ //!
+ //! use nix::unistd::{alarm, pause};
+ //! use nix::sys::signal::*;
+ //!
+ //! // We need to setup an empty signal handler to catch the alarm signal,
+ //! // otherwise the program will be terminated once the signal is delivered.
+ //! extern fn signal_handler(_: nix::libc::c_int) { }
+ //! let sa = SigAction::new(
+ //! SigHandler::Handler(signal_handler),
+ //! SaFlags::SA_RESTART,
+ //! SigSet::empty()
+ //! );
+ //! unsafe {
+ //! sigaction(Signal::SIGALRM, &sa);
+ //! }
+ //!
+ //! let start = Instant::now();
+ //!
+ //! // Set an alarm for 1 second from now.
+ //! alarm::set(1);
+ //!
+ //! // Pause the process until the alarm signal is received.
+ //! let mut sigset = SigSet::empty();
+ //! sigset.add(Signal::SIGALRM);
+ //! sigset.wait();
+ //!
+ //! assert!(start.elapsed() >= Duration::from_secs(1));
+ //! ```
+ //!
+ //! # References
+ //!
+ //! See also [alarm(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/alarm.html).
+
+ /// Schedule an alarm signal.
+ ///
+ /// This will cause the system to generate a `SIGALRM` signal for the
+ /// process after the specified number of seconds have elapsed.
+ ///
+ /// Returns the leftover time of a previously set alarm if there was one.
+ pub fn set(secs: libc::c_uint) -> Option<libc::c_uint> {
+ assert!(secs != 0, "passing 0 to `alarm::set` is not allowed, to cancel an alarm use `alarm::cancel`");
+ alarm(secs)
+ }
+
+ /// Cancel an previously set alarm signal.
+ ///
+ /// Returns the leftover time of a previously set alarm if there was one.
+ pub fn cancel() -> Option<libc::c_uint> {
+ alarm(0)
+ }
+
+ fn alarm(secs: libc::c_uint) -> Option<libc::c_uint> {
+ match unsafe { libc::alarm(secs) } {
+ 0 => None,
+ secs => Some(secs),
+ }
+ }
+}
+}
+
+/// Suspend execution for an interval of time
+///
+/// See also [sleep(2)](https://pubs.opengroup.org/onlinepubs/009695399/functions/sleep.html#tag_03_705_05)
+// Per POSIX, does not fail
+#[inline]
+pub fn sleep(seconds: c_uint) -> c_uint {
+ unsafe { libc::sleep(seconds) }
+}
+
+feature! {
+#![feature = "acct"]
+
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+pub mod acct {
+ use crate::{Result, NixPath};
+ use crate::errno::Errno;
+ use std::ptr;
+
+ /// Enable process accounting
+ ///
+ /// See also [acct(2)](https://linux.die.net/man/2/acct)
+ pub fn enable<P: ?Sized + NixPath>(filename: &P) -> Result<()> {
+ let res = filename.with_nix_path(|cstr| {
+ unsafe { libc::acct(cstr.as_ptr()) }
+ })?;
+
+ Errno::result(res).map(drop)
+ }
+
+ /// Disable process accounting
+ pub fn disable() -> Result<()> {
+ let res = unsafe { libc::acct(ptr::null()) };
+
+ Errno::result(res).map(drop)
+ }
+}
+}
+
+feature! {
+#![feature = "fs"]
+/// Creates a regular file which persists even after process termination
+///
+/// * `template`: a path whose 6 rightmost characters must be X, e.g. `/tmp/tmpfile_XXXXXX`
+/// * returns: tuple of file descriptor and filename
+///
+/// Err is returned either if no temporary filename could be created or the template doesn't
+/// end with XXXXXX
+///
+/// See also [mkstemp(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkstemp.html)
+///
+/// # Example
+///
+/// ```rust
+/// use nix::unistd;
+///
+/// let _ = match unistd::mkstemp("/tmp/tempfile_XXXXXX") {
+/// Ok((fd, path)) => {
+/// unistd::unlink(path.as_path()).unwrap(); // flag file to be deleted at app termination
+/// fd
+/// }
+/// Err(e) => panic!("mkstemp failed: {}", e)
+/// };
+/// // do something with fd
+/// ```
+#[inline]
+pub fn mkstemp<P: ?Sized + NixPath>(template: &P) -> Result<(RawFd, PathBuf)> {
+ let mut path = template.with_nix_path(|path| {path.to_bytes_with_nul().to_owned()})?;
+ let p = path.as_mut_ptr() as *mut _;
+ let fd = unsafe { libc::mkstemp(p) };
+ let last = path.pop(); // drop the trailing nul
+ debug_assert!(last == Some(b'\0'));
+ let pathname = OsString::from_vec(path);
+ Errno::result(fd)?;
+ Ok((fd, PathBuf::from(pathname)))
+}
+}
+
+feature! {
+#![all(feature = "fs", feature = "feature")]
+
+/// Variable names for `pathconf`
+///
+/// Nix uses the same naming convention for these variables as the
+/// [getconf(1)](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/getconf.html) utility.
+/// That is, `PathconfVar` variables have the same name as the abstract
+/// variables shown in the `pathconf(2)` man page. Usually, it's the same as
+/// the C variable name without the leading `_PC_`.
+///
+/// POSIX 1003.1-2008 standardizes all of these variables, but some OSes choose
+/// not to implement variables that cannot change at runtime.
+///
+/// # References
+///
+/// - [pathconf(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pathconf.html)
+/// - [limits.h](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/limits.h.html)
+/// - [unistd.h](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/unistd.h.html)
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[repr(i32)]
+#[non_exhaustive]
+pub enum PathconfVar {
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "linux",
+ target_os = "netbsd", target_os = "openbsd", target_os = "redox"))]
+ /// Minimum number of bits needed to represent, as a signed integer value,
+ /// the maximum size of a regular file allowed in the specified directory.
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ FILESIZEBITS = libc::_PC_FILESIZEBITS,
+ /// Maximum number of links to a single file.
+ LINK_MAX = libc::_PC_LINK_MAX,
+ /// Maximum number of bytes in a terminal canonical input line.
+ MAX_CANON = libc::_PC_MAX_CANON,
+ /// Minimum number of bytes for which space is available in a terminal input
+ /// queue; therefore, the maximum number of bytes a conforming application
+ /// may require to be typed as input before reading them.
+ MAX_INPUT = libc::_PC_MAX_INPUT,
+ /// Maximum number of bytes in a filename (not including the terminating
+ /// null of a filename string).
+ NAME_MAX = libc::_PC_NAME_MAX,
+ /// Maximum number of bytes the implementation will store as a pathname in a
+ /// user-supplied buffer of unspecified size, including the terminating null
+ /// character. Minimum number the implementation will accept as the maximum
+ /// number of bytes in a pathname.
+ PATH_MAX = libc::_PC_PATH_MAX,
+ /// Maximum number of bytes that is guaranteed to be atomic when writing to
+ /// a pipe.
+ PIPE_BUF = libc::_PC_PIPE_BUF,
+ #[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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// Symbolic links can be created.
+ POSIX2_SYMLINKS = libc::_PC_2_SYMLINKS,
+ #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd",
+ target_os = "linux", target_os = "openbsd", target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// Minimum number of bytes of storage actually allocated for any portion of
+ /// a file.
+ POSIX_ALLOC_SIZE_MIN = libc::_PC_ALLOC_SIZE_MIN,
+ #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd",
+ target_os = "linux", target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// Recommended increment for file transfer sizes between the
+ /// `POSIX_REC_MIN_XFER_SIZE` and `POSIX_REC_MAX_XFER_SIZE` values.
+ POSIX_REC_INCR_XFER_SIZE = 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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// Maximum recommended file transfer size.
+ POSIX_REC_MAX_XFER_SIZE = libc::_PC_REC_MAX_XFER_SIZE,
+ #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd",
+ target_os = "linux", target_os = "openbsd", target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// Minimum recommended file transfer size.
+ POSIX_REC_MIN_XFER_SIZE = libc::_PC_REC_MIN_XFER_SIZE,
+ #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd",
+ target_os = "linux", target_os = "openbsd", target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// Recommended file transfer buffer alignment.
+ POSIX_REC_XFER_ALIGN = libc::_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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// Maximum number of bytes in a symbolic link.
+ SYMLINK_MAX = libc::_PC_SYMLINK_MAX,
+ /// The use of `chown` and `fchown` is restricted to a process with
+ /// appropriate privileges, and to changing the group ID of a file only to
+ /// the effective group ID of the process or to one of its supplementary
+ /// group IDs.
+ _POSIX_CHOWN_RESTRICTED = libc::_PC_CHOWN_RESTRICTED,
+ /// Pathname components longer than {NAME_MAX} generate an error.
+ _POSIX_NO_TRUNC = libc::_PC_NO_TRUNC,
+ /// This symbol shall be defined to be the value of a character that shall
+ /// disable terminal special character handling.
+ _POSIX_VDISABLE = libc::_PC_VDISABLE,
+ #[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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// Asynchronous input or output operations may be performed for the
+ /// associated file.
+ _POSIX_ASYNC_IO = libc::_PC_ASYNC_IO,
+ #[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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// Prioritized input or output operations may be performed for the
+ /// associated file.
+ _POSIX_PRIO_IO = libc::_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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// Synchronized input or output operations may be performed for the
+ /// associated file.
+ _POSIX_SYNC_IO = libc::_PC_SYNC_IO,
+ #[cfg(any(target_os = "dragonfly", target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The resolution in nanoseconds for all file timestamps.
+ _POSIX_TIMESTAMP_RESOLUTION = libc::_PC_TIMESTAMP_RESOLUTION
+}
+
+/// Like `pathconf`, but works with file descriptors instead of paths (see
+/// [fpathconf(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pathconf.html))
+///
+/// # Parameters
+///
+/// - `fd`: The file descriptor whose variable should be interrogated
+/// - `var`: The pathconf variable to lookup
+///
+/// # Returns
+///
+/// - `Ok(Some(x))`: the variable's limit (for limit variables) or its
+/// implementation level (for option variables). Implementation levels are
+/// usually a decimal-coded date, such as 200112 for POSIX 2001.12
+/// - `Ok(None)`: the variable has no limit (for limit variables) or is
+/// unsupported (for option variables)
+/// - `Err(x)`: an error occurred
+pub fn fpathconf(fd: RawFd, var: PathconfVar) -> Result<Option<c_long>> {
+ let raw = unsafe {
+ Errno::clear();
+ libc::fpathconf(fd, var as c_int)
+ };
+ if raw == -1 {
+ if errno::errno() == 0 {
+ Ok(None)
+ } else {
+ Err(Errno::last())
+ }
+ } else {
+ Ok(Some(raw))
+ }
+}
+
+/// Get path-dependent configurable system variables (see
+/// [pathconf(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pathconf.html))
+///
+/// Returns the value of a path-dependent configurable system variable. Most
+/// supported variables also have associated compile-time constants, but POSIX
+/// allows their values to change at runtime. There are generally two types of
+/// `pathconf` variables: options and limits. See [pathconf(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pathconf.html) for more details.
+///
+/// # Parameters
+///
+/// - `path`: Lookup the value of `var` for this file or directory
+/// - `var`: The `pathconf` variable to lookup
+///
+/// # Returns
+///
+/// - `Ok(Some(x))`: the variable's limit (for limit variables) or its
+/// implementation level (for option variables). Implementation levels are
+/// usually a decimal-coded date, such as 200112 for POSIX 2001.12
+/// - `Ok(None)`: the variable has no limit (for limit variables) or is
+/// unsupported (for option variables)
+/// - `Err(x)`: an error occurred
+pub fn pathconf<P: ?Sized + NixPath>(path: &P, var: PathconfVar) -> Result<Option<c_long>> {
+ let raw = path.with_nix_path(|cstr| {
+ unsafe {
+ Errno::clear();
+ libc::pathconf(cstr.as_ptr(), var as c_int)
+ }
+ })?;
+ if raw == -1 {
+ if errno::errno() == 0 {
+ Ok(None)
+ } else {
+ Err(Errno::last())
+ }
+ } else {
+ Ok(Some(raw))
+ }
+}
+}
+
+feature! {
+#![feature = "feature"]
+
+/// Variable names for `sysconf`
+///
+/// Nix uses the same naming convention for these variables as the
+/// [getconf(1)](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/getconf.html) utility.
+/// That is, `SysconfVar` variables have the same name as the abstract variables
+/// shown in the `sysconf(3)` man page. Usually, it's the same as the C
+/// variable name without the leading `_SC_`.
+///
+/// All of these symbols are standardized by POSIX 1003.1-2008, but haven't been
+/// implemented by all platforms.
+///
+/// # References
+///
+/// - [sysconf(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sysconf.html)
+/// - [unistd.h](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/unistd.h.html)
+/// - [limits.h](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/limits.h.html)
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[repr(i32)]
+#[non_exhaustive]
+pub enum SysconfVar {
+ /// Maximum number of I/O operations in a single list I/O call supported by
+ /// the implementation.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ AIO_LISTIO_MAX = libc::_SC_AIO_LISTIO_MAX,
+ /// Maximum number of outstanding asynchronous I/O operations supported by
+ /// the implementation.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ AIO_MAX = libc::_SC_AIO_MAX,
+ #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The maximum amount by which a process can decrease its asynchronous I/O
+ /// priority level from its own scheduling priority.
+ AIO_PRIO_DELTA_MAX = libc::_SC_AIO_PRIO_DELTA_MAX,
+ /// Maximum length of argument to the exec functions including environment data.
+ ARG_MAX = libc::_SC_ARG_MAX,
+ /// Maximum number of functions that may be registered with `atexit`.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ATEXIT_MAX = libc::_SC_ATEXIT_MAX,
+ /// Maximum obase values allowed by the bc utility.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ BC_BASE_MAX = libc::_SC_BC_BASE_MAX,
+ /// Maximum number of elements permitted in an array by the bc utility.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ BC_DIM_MAX = libc::_SC_BC_DIM_MAX,
+ /// Maximum scale value allowed by the bc utility.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ BC_SCALE_MAX = libc::_SC_BC_SCALE_MAX,
+ /// Maximum length of a string constant accepted by the bc utility.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ BC_STRING_MAX = libc::_SC_BC_STRING_MAX,
+ /// Maximum number of simultaneous processes per real user ID.
+ CHILD_MAX = libc::_SC_CHILD_MAX,
+ // The number of clock ticks per second.
+ CLK_TCK = libc::_SC_CLK_TCK,
+ /// Maximum number of weights that can be assigned to an entry of the
+ /// LC_COLLATE order keyword in the locale definition file
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ COLL_WEIGHTS_MAX = libc::_SC_COLL_WEIGHTS_MAX,
+ /// Maximum number of timer expiration overruns.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ DELAYTIMER_MAX = libc::_SC_DELAYTIMER_MAX,
+ /// Maximum number of expressions that can be nested within parentheses by
+ /// the expr utility.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ EXPR_NEST_MAX = libc::_SC_EXPR_NEST_MAX,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="netbsd", target_os="openbsd", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// Maximum length of a host name (not including the terminating null) as
+ /// returned from the `gethostname` function
+ HOST_NAME_MAX = libc::_SC_HOST_NAME_MAX,
+ /// Maximum number of iovec structures that one process has available for
+ /// use with `readv` or `writev`.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IOV_MAX = libc::_SC_IOV_MAX,
+ /// Unless otherwise noted, the maximum length, in bytes, of a utility's
+ /// input line (either standard input or another file), when the utility is
+ /// described as processing text files. The length includes room for the
+ /// trailing newline.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ LINE_MAX = libc::_SC_LINE_MAX,
+ /// Maximum length of a login name.
+ #[cfg(not(target_os = "haiku"))]
+ LOGIN_NAME_MAX = libc::_SC_LOGIN_NAME_MAX,
+ /// Maximum number of simultaneous supplementary group IDs per process.
+ NGROUPS_MAX = libc::_SC_NGROUPS_MAX,
+ /// Initial size of `getgrgid_r` and `getgrnam_r` data buffers
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ GETGR_R_SIZE_MAX = libc::_SC_GETGR_R_SIZE_MAX,
+ /// Initial size of `getpwuid_r` and `getpwnam_r` data buffers
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ GETPW_R_SIZE_MAX = libc::_SC_GETPW_R_SIZE_MAX,
+ /// The maximum number of open message queue descriptors a process may hold.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MQ_OPEN_MAX = libc::_SC_MQ_OPEN_MAX,
+ /// The maximum number of message priorities supported by the implementation.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MQ_PRIO_MAX = libc::_SC_MQ_PRIO_MAX,
+ /// A value one greater than the maximum value that the system may assign to
+ /// a newly-created file descriptor.
+ OPEN_MAX = libc::_SC_OPEN_MAX,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Advisory Information option.
+ _POSIX_ADVISORY_INFO = libc::_SC_ADVISORY_INFO,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="netbsd", target_os="openbsd", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports barriers.
+ _POSIX_BARRIERS = libc::_SC_BARRIERS,
+ /// The implementation supports asynchronous input and output.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_ASYNCHRONOUS_IO = libc::_SC_ASYNCHRONOUS_IO,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="netbsd", target_os="openbsd", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports clock selection.
+ _POSIX_CLOCK_SELECTION = libc::_SC_CLOCK_SELECTION,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="netbsd", target_os="openbsd", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Process CPU-Time Clocks option.
+ _POSIX_CPUTIME = libc::_SC_CPUTIME,
+ /// The implementation supports the File Synchronization option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_FSYNC = libc::_SC_FSYNC,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the IPv6 option.
+ _POSIX_IPV6 = libc::_SC_IPV6,
+ /// The implementation supports job control.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_JOB_CONTROL = libc::_SC_JOB_CONTROL,
+ /// The implementation supports memory mapped Files.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_MAPPED_FILES = libc::_SC_MAPPED_FILES,
+ /// The implementation supports the Process Memory Locking option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_MEMLOCK = libc::_SC_MEMLOCK,
+ /// The implementation supports the Range Memory Locking option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_MEMLOCK_RANGE = libc::_SC_MEMLOCK_RANGE,
+ /// The implementation supports memory protection.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_MEMORY_PROTECTION = libc::_SC_MEMORY_PROTECTION,
+ /// The implementation supports the Message Passing option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_MESSAGE_PASSING = libc::_SC_MESSAGE_PASSING,
+ /// The implementation supports the Monotonic Clock option.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_MONOTONIC_CLOCK = libc::_SC_MONOTONIC_CLOCK,
+ #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
+ target_os = "illumos", target_os = "ios", target_os="linux",
+ target_os = "macos", target_os="openbsd", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Prioritized Input and Output option.
+ _POSIX_PRIORITIZED_IO = libc::_SC_PRIORITIZED_IO,
+ /// The implementation supports the Process Scheduling option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_PRIORITY_SCHEDULING = libc::_SC_PRIORITY_SCHEDULING,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Raw Sockets option.
+ _POSIX_RAW_SOCKETS = libc::_SC_RAW_SOCKETS,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="netbsd", target_os="openbsd", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports read-write locks.
+ _POSIX_READER_WRITER_LOCKS = libc::_SC_READER_WRITER_LOCKS,
+ #[cfg(any(target_os = "android", target_os="dragonfly", target_os="freebsd",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports realtime signals.
+ _POSIX_REALTIME_SIGNALS = libc::_SC_REALTIME_SIGNALS,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="netbsd", target_os="openbsd", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Regular Expression Handling option.
+ _POSIX_REGEXP = libc::_SC_REGEXP,
+ /// Each process has a saved set-user-ID and a saved set-group-ID.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_SAVED_IDS = libc::_SC_SAVED_IDS,
+ /// The implementation supports semaphores.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_SEMAPHORES = libc::_SC_SEMAPHORES,
+ /// The implementation supports the Shared Memory Objects option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_SHARED_MEMORY_OBJECTS = libc::_SC_SHARED_MEMORY_OBJECTS,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the POSIX shell.
+ _POSIX_SHELL = libc::_SC_SHELL,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Spawn option.
+ _POSIX_SPAWN = libc::_SC_SPAWN,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports spin locks.
+ _POSIX_SPIN_LOCKS = libc::_SC_SPIN_LOCKS,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Process Sporadic Server option.
+ _POSIX_SPORADIC_SERVER = libc::_SC_SPORADIC_SERVER,
+ #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_SS_REPL_MAX = libc::_SC_SS_REPL_MAX,
+ /// The implementation supports the Synchronized Input and Output option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_SYNCHRONIZED_IO = libc::_SC_SYNCHRONIZED_IO,
+ /// The implementation supports the Thread Stack Address Attribute option.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_THREAD_ATTR_STACKADDR = libc::_SC_THREAD_ATTR_STACKADDR,
+ /// The implementation supports the Thread Stack Size Attribute option.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_THREAD_ATTR_STACKSIZE = libc::_SC_THREAD_ATTR_STACKSIZE,
+ #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="netbsd", target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Thread CPU-Time Clocks option.
+ _POSIX_THREAD_CPUTIME = libc::_SC_THREAD_CPUTIME,
+ /// The implementation supports the Non-Robust Mutex Priority Inheritance
+ /// option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_THREAD_PRIO_INHERIT = libc::_SC_THREAD_PRIO_INHERIT,
+ /// The implementation supports the Non-Robust Mutex Priority Protection option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_THREAD_PRIO_PROTECT = libc::_SC_THREAD_PRIO_PROTECT,
+ /// The implementation supports the Thread Execution Scheduling option.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_THREAD_PRIORITY_SCHEDULING = libc::_SC_THREAD_PRIORITY_SCHEDULING,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Thread Process-Shared Synchronization
+ /// option.
+ _POSIX_THREAD_PROCESS_SHARED = libc::_SC_THREAD_PROCESS_SHARED,
+ #[cfg(any(target_os="dragonfly", target_os="linux", target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Robust Mutex Priority Inheritance option.
+ _POSIX_THREAD_ROBUST_PRIO_INHERIT = libc::_SC_THREAD_ROBUST_PRIO_INHERIT,
+ #[cfg(any(target_os="dragonfly", target_os="linux", target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Robust Mutex Priority Protection option.
+ _POSIX_THREAD_ROBUST_PRIO_PROTECT = libc::_SC_THREAD_ROBUST_PRIO_PROTECT,
+ /// The implementation supports thread-safe functions.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_THREAD_SAFE_FUNCTIONS = libc::_SC_THREAD_SAFE_FUNCTIONS,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Thread Sporadic Server option.
+ _POSIX_THREAD_SPORADIC_SERVER = libc::_SC_THREAD_SPORADIC_SERVER,
+ /// The implementation supports threads.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_THREADS = libc::_SC_THREADS,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports timeouts.
+ _POSIX_TIMEOUTS = libc::_SC_TIMEOUTS,
+ /// The implementation supports timers.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_TIMERS = libc::_SC_TIMERS,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Trace option.
+ _POSIX_TRACE = libc::_SC_TRACE,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Trace Event Filter option.
+ _POSIX_TRACE_EVENT_FILTER = libc::_SC_TRACE_EVENT_FILTER,
+ #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_TRACE_EVENT_NAME_MAX = libc::_SC_TRACE_EVENT_NAME_MAX,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Trace Inherit option.
+ _POSIX_TRACE_INHERIT = libc::_SC_TRACE_INHERIT,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Trace Log option.
+ _POSIX_TRACE_LOG = libc::_SC_TRACE_LOG,
+ #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_TRACE_NAME_MAX = libc::_SC_TRACE_NAME_MAX,
+ #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_TRACE_SYS_MAX = libc::_SC_TRACE_SYS_MAX,
+ #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_TRACE_USER_EVENT_MAX = libc::_SC_TRACE_USER_EVENT_MAX,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Typed Memory Objects option.
+ _POSIX_TYPED_MEMORY_OBJECTS = libc::_SC_TYPED_MEMORY_OBJECTS,
+ /// Integer value indicating version of this standard (C-language binding)
+ /// to which the implementation conforms. For implementations conforming to
+ /// POSIX.1-2008, the value shall be 200809L.
+ _POSIX_VERSION = libc::_SC_VERSION,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation provides a C-language compilation environment with
+ /// 32-bit `int`, `long`, `pointer`, and `off_t` types.
+ _POSIX_V6_ILP32_OFF32 = libc::_SC_V6_ILP32_OFF32,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation provides a C-language compilation environment with
+ /// 32-bit `int`, `long`, and pointer types and an `off_t` type using at
+ /// least 64 bits.
+ _POSIX_V6_ILP32_OFFBIG = libc::_SC_V6_ILP32_OFFBIG,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation provides a C-language compilation environment with
+ /// 32-bit `int` and 64-bit `long`, `pointer`, and `off_t` types.
+ _POSIX_V6_LP64_OFF64 = libc::_SC_V6_LP64_OFF64,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation provides a C-language compilation environment with an
+ /// `int` type using at least 32 bits and `long`, pointer, and `off_t` types
+ /// using at least 64 bits.
+ _POSIX_V6_LPBIG_OFFBIG = libc::_SC_V6_LPBIG_OFFBIG,
+ /// The implementation supports the C-Language Binding option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX2_C_BIND = libc::_SC_2_C_BIND,
+ /// The implementation supports the C-Language Development Utilities option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX2_C_DEV = libc::_SC_2_C_DEV,
+ /// The implementation supports the Terminal Characteristics option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX2_CHAR_TERM = libc::_SC_2_CHAR_TERM,
+ /// The implementation supports the FORTRAN Development Utilities option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX2_FORT_DEV = libc::_SC_2_FORT_DEV,
+ /// The implementation supports the FORTRAN Runtime Utilities option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX2_FORT_RUN = libc::_SC_2_FORT_RUN,
+ /// The implementation supports the creation of locales by the localedef
+ /// utility.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX2_LOCALEDEF = libc::_SC_2_LOCALEDEF,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Batch Environment Services and Utilities
+ /// option.
+ _POSIX2_PBS = libc::_SC_2_PBS,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Batch Accounting option.
+ _POSIX2_PBS_ACCOUNTING = libc::_SC_2_PBS_ACCOUNTING,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Batch Checkpoint/Restart option.
+ _POSIX2_PBS_CHECKPOINT = libc::_SC_2_PBS_CHECKPOINT,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Locate Batch Job Request option.
+ _POSIX2_PBS_LOCATE = libc::_SC_2_PBS_LOCATE,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Batch Job Message Request option.
+ _POSIX2_PBS_MESSAGE = libc::_SC_2_PBS_MESSAGE,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Track Batch Job Request option.
+ _POSIX2_PBS_TRACK = libc::_SC_2_PBS_TRACK,
+ /// The implementation supports the Software Development Utilities option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX2_SW_DEV = libc::_SC_2_SW_DEV,
+ /// The implementation supports the User Portability Utilities option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX2_UPE = libc::_SC_2_UPE,
+ /// Integer value indicating version of the Shell and Utilities volume of
+ /// POSIX.1 to which the implementation conforms.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX2_VERSION = libc::_SC_2_VERSION,
+ /// The size of a system page in bytes.
+ ///
+ /// POSIX also defines an alias named `PAGESIZE`, but Rust does not allow two
+ /// enum constants to have the same value, so nix omits `PAGESIZE`.
+ PAGE_SIZE = libc::_SC_PAGE_SIZE,
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ PTHREAD_DESTRUCTOR_ITERATIONS = libc::_SC_THREAD_DESTRUCTOR_ITERATIONS,
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ PTHREAD_KEYS_MAX = libc::_SC_THREAD_KEYS_MAX,
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ PTHREAD_STACK_MIN = libc::_SC_THREAD_STACK_MIN,
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ PTHREAD_THREADS_MAX = libc::_SC_THREAD_THREADS_MAX,
+ #[cfg(not(target_os = "haiku"))]
+ RE_DUP_MAX = libc::_SC_RE_DUP_MAX,
+ #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ RTSIG_MAX = libc::_SC_RTSIG_MAX,
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ SEM_NSEMS_MAX = libc::_SC_SEM_NSEMS_MAX,
+ #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ SEM_VALUE_MAX = libc::_SC_SEM_VALUE_MAX,
+ #[cfg(any(target_os = "android", target_os="dragonfly", target_os="freebsd",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ SIGQUEUE_MAX = libc::_SC_SIGQUEUE_MAX,
+ STREAM_MAX = libc::_SC_STREAM_MAX,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ SYMLOOP_MAX = libc::_SC_SYMLOOP_MAX,
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ TIMER_MAX = libc::_SC_TIMER_MAX,
+ TTY_NAME_MAX = libc::_SC_TTY_NAME_MAX,
+ TZNAME_MAX = libc::_SC_TZNAME_MAX,
+ #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the X/Open Encryption Option Group.
+ _XOPEN_CRYPT = libc::_SC_XOPEN_CRYPT,
+ #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Issue 4, Version 2 Enhanced
+ /// Internationalization Option Group.
+ _XOPEN_ENH_I18N = libc::_SC_XOPEN_ENH_I18N,
+ #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _XOPEN_LEGACY = libc::_SC_XOPEN_LEGACY,
+ #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the X/Open Realtime Option Group.
+ _XOPEN_REALTIME = libc::_SC_XOPEN_REALTIME,
+ #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the X/Open Realtime Threads Option Group.
+ _XOPEN_REALTIME_THREADS = libc::_SC_XOPEN_REALTIME_THREADS,
+ /// The implementation supports the Issue 4, Version 2 Shared Memory Option
+ /// Group.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _XOPEN_SHM = libc::_SC_XOPEN_SHM,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the XSI STREAMS Option Group.
+ _XOPEN_STREAMS = libc::_SC_XOPEN_STREAMS,
+ #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the XSI option
+ _XOPEN_UNIX = libc::_SC_XOPEN_UNIX,
+ #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// Integer value indicating version of the X/Open Portability Guide to
+ /// which the implementation conforms.
+ _XOPEN_VERSION = libc::_SC_XOPEN_VERSION,
+ /// The number of pages of physical memory. Note that it is possible for
+ /// the product of this value to overflow.
+ #[cfg(any(target_os="android", target_os="linux"))]
+ _PHYS_PAGES = libc::_SC_PHYS_PAGES,
+ /// The number of currently available pages of physical memory.
+ #[cfg(any(target_os="android", target_os="linux"))]
+ _AVPHYS_PAGES = libc::_SC_AVPHYS_PAGES,
+ /// The number of processors configured.
+ #[cfg(any(target_os="android", target_os="linux"))]
+ _NPROCESSORS_CONF = libc::_SC_NPROCESSORS_CONF,
+ /// The number of processors currently online (available).
+ #[cfg(any(target_os="android", target_os="linux"))]
+ _NPROCESSORS_ONLN = libc::_SC_NPROCESSORS_ONLN,
+}
+
+/// Get configurable system variables (see
+/// [sysconf(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sysconf.html))
+///
+/// Returns the value of a configurable system variable. Most supported
+/// variables also have associated compile-time constants, but POSIX
+/// allows their values to change at runtime. There are generally two types of
+/// sysconf variables: options and limits. See sysconf(3) for more details.
+///
+/// # Returns
+///
+/// - `Ok(Some(x))`: the variable's limit (for limit variables) or its
+/// implementation level (for option variables). Implementation levels are
+/// usually a decimal-coded date, such as 200112 for POSIX 2001.12
+/// - `Ok(None)`: the variable has no limit (for limit variables) or is
+/// unsupported (for option variables)
+/// - `Err(x)`: an error occurred
+pub fn sysconf(var: SysconfVar) -> Result<Option<c_long>> {
+ let raw = unsafe {
+ Errno::clear();
+ libc::sysconf(var as c_int)
+ };
+ if raw == -1 {
+ if errno::errno() == 0 {
+ Ok(None)
+ } else {
+ Err(Errno::last())
+ }
+ } else {
+ Ok(Some(raw))
+ }
+}
+}
+
+feature! {
+#![feature = "fs"]
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+mod pivot_root {
+ use crate::{Result, NixPath};
+ use crate::errno::Errno;
+
+ pub fn pivot_root<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
+ new_root: &P1, put_old: &P2) -> Result<()> {
+ let res = new_root.with_nix_path(|new_root| {
+ put_old.with_nix_path(|put_old| {
+ unsafe {
+ libc::syscall(libc::SYS_pivot_root, new_root.as_ptr(), put_old.as_ptr())
+ }
+ })
+ })??;
+
+ Errno::result(res).map(drop)
+ }
+}
+}
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "openbsd"
+))]
+mod setres {
+ feature! {
+ #![feature = "user"]
+
+ use crate::Result;
+ use crate::errno::Errno;
+ use super::{Uid, Gid};
+
+ /// Sets the real, effective, and saved uid.
+ /// ([see setresuid(2)](https://man7.org/linux/man-pages/man2/setresuid.2.html))
+ ///
+ /// * `ruid`: real user id
+ /// * `euid`: effective user id
+ /// * `suid`: saved user id
+ /// * returns: Ok or libc error code.
+ ///
+ /// Err is returned if the user doesn't have permission to set this UID.
+ #[inline]
+ pub fn setresuid(ruid: Uid, euid: Uid, suid: Uid) -> Result<()> {
+ let res = unsafe { libc::setresuid(ruid.into(), euid.into(), suid.into()) };
+
+ Errno::result(res).map(drop)
+ }
+
+ /// Sets the real, effective, and saved gid.
+ /// ([see setresuid(2)](https://man7.org/linux/man-pages/man2/setresuid.2.html))
+ ///
+ /// * `rgid`: real group id
+ /// * `egid`: effective group id
+ /// * `sgid`: saved group id
+ /// * returns: Ok or libc error code.
+ ///
+ /// Err is returned if the user doesn't have permission to set this GID.
+ #[inline]
+ pub fn setresgid(rgid: Gid, egid: Gid, sgid: Gid) -> Result<()> {
+ let res = unsafe { libc::setresgid(rgid.into(), egid.into(), sgid.into()) };
+
+ Errno::result(res).map(drop)
+ }
+ }
+}
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "openbsd"
+))]
+mod getres {
+ feature! {
+ #![feature = "user"]
+
+ use crate::Result;
+ use crate::errno::Errno;
+ use super::{Uid, Gid};
+
+ /// Real, effective and saved user IDs.
+ #[derive(Debug, Copy, Clone, Eq, PartialEq)]
+ pub struct ResUid {
+ pub real: Uid,
+ pub effective: Uid,
+ pub saved: Uid
+ }
+
+ /// Real, effective and saved group IDs.
+ #[derive(Debug, Copy, Clone, Eq, PartialEq)]
+ pub struct ResGid {
+ pub real: Gid,
+ pub effective: Gid,
+ pub saved: Gid
+ }
+
+ /// Gets the real, effective, and saved user IDs.
+ ///
+ /// ([see getresuid(2)](http://man7.org/linux/man-pages/man2/getresuid.2.html))
+ ///
+ /// #Returns
+ ///
+ /// - `Ok((Uid, Uid, Uid))`: tuple of real, effective and saved uids on success.
+ /// - `Err(x)`: libc error code on failure.
+ ///
+ #[inline]
+ pub fn getresuid() -> Result<ResUid> {
+ let mut ruid = libc::uid_t::max_value();
+ let mut euid = libc::uid_t::max_value();
+ let mut suid = libc::uid_t::max_value();
+ let res = unsafe { libc::getresuid(&mut ruid, &mut euid, &mut suid) };
+
+ Errno::result(res).map(|_| ResUid{ real: Uid(ruid), effective: Uid(euid), saved: Uid(suid) })
+ }
+
+ /// Gets the real, effective, and saved group IDs.
+ ///
+ /// ([see getresgid(2)](http://man7.org/linux/man-pages/man2/getresgid.2.html))
+ ///
+ /// #Returns
+ ///
+ /// - `Ok((Gid, Gid, Gid))`: tuple of real, effective and saved gids on success.
+ /// - `Err(x)`: libc error code on failure.
+ ///
+ #[inline]
+ pub fn getresgid() -> Result<ResGid> {
+ let mut rgid = libc::gid_t::max_value();
+ let mut egid = libc::gid_t::max_value();
+ let mut sgid = libc::gid_t::max_value();
+ let res = unsafe { libc::getresgid(&mut rgid, &mut egid, &mut sgid) };
+
+ Errno::result(res).map(|_| ResGid { real: Gid(rgid), effective: Gid(egid), saved: Gid(sgid) } )
+ }
+ }
+}
+
+#[cfg(feature = "fs")]
+libc_bitflags! {
+ /// Options for access()
+ #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
+ pub struct AccessFlags : c_int {
+ /// Test for existence of file.
+ F_OK;
+ /// Test for read permission.
+ R_OK;
+ /// Test for write permission.
+ W_OK;
+ /// Test for execute (search) permission.
+ X_OK;
+ }
+}
+
+feature! {
+#![feature = "fs"]
+
+/// Checks the file named by `path` for accessibility according to the flags given by `amode`
+/// See [access(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/access.html)
+pub fn access<P: ?Sized + NixPath>(path: &P, amode: AccessFlags) -> Result<()> {
+ let res = path.with_nix_path(|cstr| {
+ unsafe {
+ libc::access(cstr.as_ptr(), amode.bits)
+ }
+ })?;
+ Errno::result(res).map(drop)
+}
+
+/// Checks the file named by `path` for accessibility according to the flags given by `mode`
+///
+/// If `dirfd` has a value, then `path` is relative to directory associated with the file descriptor.
+///
+/// If `dirfd` is `None`, then `path` is relative to the current working directory.
+///
+/// # References
+///
+/// [faccessat(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/faccessat.html)
+// redox: does not appear to support the *at family of syscalls.
+#[cfg(not(target_os = "redox"))]
+pub fn faccessat<P: ?Sized + NixPath>(dirfd: Option<RawFd>, path: &P, mode: AccessFlags, flags: AtFlags) -> Result<()> {
+ let res = path.with_nix_path(|cstr| {
+ unsafe {
+ libc::faccessat(at_rawfd(dirfd), cstr.as_ptr(), mode.bits(), flags.bits())
+ }
+ })?;
+ Errno::result(res).map(drop)
+}
+
+/// Checks the file named by `path` for accessibility according to the flags given
+/// by `mode` using effective UID, effective GID and supplementary group lists.
+///
+/// # References
+///
+/// * [FreeBSD man page](https://www.freebsd.org/cgi/man.cgi?query=eaccess&sektion=2&n=1)
+/// * [Linux man page](https://man7.org/linux/man-pages/man3/euidaccess.3.html)
+#[cfg(any(
+ all(target_os = "linux", not(target_env = "uclibc")),
+ target_os = "freebsd",
+ target_os = "dragonfly"
+))]
+pub fn eaccess<P: ?Sized + NixPath>(path: &P, mode: AccessFlags) -> Result<()> {
+ let res = path.with_nix_path(|cstr| {
+ unsafe {
+ libc::eaccess(cstr.as_ptr(), mode.bits)
+ }
+ })?;
+ Errno::result(res).map(drop)
+}
+}
+
+feature! {
+#![feature = "user"]
+
+/// Representation of a User, based on `libc::passwd`
+///
+/// The reason some fields in this struct are `String` and others are `CString` is because some
+/// fields are based on the user's locale, which could be non-UTF8, while other fields are
+/// guaranteed to conform to [`NAME_REGEX`](https://serverfault.com/a/73101/407341), which only
+/// contains ASCII.
+#[cfg(not(target_os = "redox"))] // RedoxFS does not support passwd
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct User {
+ /// Username
+ pub name: String,
+ /// User password (probably hashed)
+ pub passwd: CString,
+ /// User ID
+ pub uid: Uid,
+ /// Group ID
+ pub gid: Gid,
+ /// User information
+ #[cfg(not(all(target_os = "android", target_pointer_width = "32")))]
+ pub gecos: CString,
+ /// Home directory
+ pub dir: PathBuf,
+ /// Path to shell
+ pub shell: PathBuf,
+ /// Login class
+ #[cfg(not(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub class: CString,
+ /// Last password change
+ #[cfg(not(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub change: libc::time_t,
+ /// Expiration time of account
+ #[cfg(not(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub expire: libc::time_t
+}
+
+#[cfg(not(target_os = "redox"))] //RedoxFS does not support passwd
+impl From<&libc::passwd> for User {
+ fn from(pw: &libc::passwd) -> User {
+ unsafe {
+ User {
+ name: if pw.pw_name.is_null() { Default::default() } else { CStr::from_ptr(pw.pw_name).to_string_lossy().into_owned() },
+ passwd: if pw.pw_passwd.is_null() { Default::default() } else { CString::new(CStr::from_ptr(pw.pw_passwd).to_bytes()).unwrap() },
+ #[cfg(not(all(target_os = "android", target_pointer_width = "32")))]
+ gecos: if pw.pw_gecos.is_null() { Default::default() } else { CString::new(CStr::from_ptr(pw.pw_gecos).to_bytes()).unwrap() },
+ dir: if pw.pw_dir.is_null() { Default::default() } else { PathBuf::from(OsStr::from_bytes(CStr::from_ptr(pw.pw_dir).to_bytes())) },
+ shell: if pw.pw_shell.is_null() { Default::default() } else { PathBuf::from(OsStr::from_bytes(CStr::from_ptr(pw.pw_shell).to_bytes())) },
+ uid: Uid::from_raw(pw.pw_uid),
+ gid: Gid::from_raw(pw.pw_gid),
+ #[cfg(not(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris")))]
+ class: CString::new(CStr::from_ptr(pw.pw_class).to_bytes()).unwrap(),
+ #[cfg(not(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris")))]
+ change: pw.pw_change,
+ #[cfg(not(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris")))]
+ expire: pw.pw_expire
+ }
+ }
+ }
+}
+
+#[cfg(not(target_os = "redox"))] // RedoxFS does not support passwd
+impl From<User> for libc::passwd {
+ fn from(u: User) -> Self {
+ let name = match CString::new(u.name) {
+ Ok(n) => n.into_raw(),
+ Err(_) => CString::new("").unwrap().into_raw(),
+ };
+ let dir = match u.dir.into_os_string().into_string() {
+ Ok(s) => CString::new(s.as_str()).unwrap().into_raw(),
+ Err(_) => CString::new("").unwrap().into_raw(),
+ };
+ let shell = match u.shell.into_os_string().into_string() {
+ Ok(s) => CString::new(s.as_str()).unwrap().into_raw(),
+ Err(_) => CString::new("").unwrap().into_raw(),
+ };
+ Self {
+ pw_name: name,
+ pw_passwd: u.passwd.into_raw(),
+ #[cfg(not(all(target_os = "android", target_pointer_width = "32")))]
+ pw_gecos: u.gecos.into_raw(),
+ pw_dir: dir,
+ pw_shell: shell,
+ pw_uid: u.uid.0,
+ pw_gid: u.gid.0,
+ #[cfg(not(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris")))]
+ pw_class: u.class.into_raw(),
+ #[cfg(not(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris")))]
+ pw_change: u.change,
+ #[cfg(not(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris")))]
+ pw_expire: u.expire,
+ #[cfg(target_os = "illumos")]
+ pw_age: CString::new("").unwrap().into_raw(),
+ #[cfg(target_os = "illumos")]
+ pw_comment: CString::new("").unwrap().into_raw(),
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ pw_fields: 0,
+ }
+ }
+}
+
+#[cfg(not(target_os = "redox"))] // RedoxFS does not support passwd
+impl User {
+ fn from_anything<F>(f: F) -> Result<Option<Self>>
+ where
+ F: Fn(*mut libc::passwd,
+ *mut c_char,
+ libc::size_t,
+ *mut *mut libc::passwd) -> libc::c_int
+ {
+ let buflimit = 1048576;
+ let bufsize = match sysconf(SysconfVar::GETPW_R_SIZE_MAX) {
+ Ok(Some(n)) => n as usize,
+ Ok(None) | Err(_) => 16384,
+ };
+
+ let mut cbuf = Vec::with_capacity(bufsize);
+ let mut pwd = mem::MaybeUninit::<libc::passwd>::uninit();
+ let mut res = ptr::null_mut();
+
+ loop {
+ let error = f(pwd.as_mut_ptr(), cbuf.as_mut_ptr(), cbuf.capacity(), &mut res);
+ if error == 0 {
+ if res.is_null() {
+ return Ok(None);
+ } else {
+ let pwd = unsafe { pwd.assume_init() };
+ return Ok(Some(User::from(&pwd)));
+ }
+ } else if Errno::last() == Errno::ERANGE {
+ // Trigger the internal buffer resizing logic.
+ reserve_double_buffer_size(&mut cbuf, buflimit)?;
+ } else {
+ return Err(Errno::last());
+ }
+ }
+ }
+
+ /// Get a user by UID.
+ ///
+ /// Internally, this function calls
+ /// [getpwuid_r(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwuid_r.html)
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use nix::unistd::{Uid, User};
+ /// // Returns an Result<Option<User>>, thus the double unwrap.
+ /// let res = User::from_uid(Uid::from_raw(0)).unwrap().unwrap();
+ /// assert_eq!(res.name, "root");
+ /// ```
+ pub fn from_uid(uid: Uid) -> Result<Option<Self>> {
+ User::from_anything(|pwd, cbuf, cap, res| {
+ unsafe { libc::getpwuid_r(uid.0, pwd, cbuf, cap, res) }
+ })
+ }
+
+ /// Get a user by name.
+ ///
+ /// Internally, this function calls
+ /// [getpwnam_r(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwuid_r.html)
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use nix::unistd::User;
+ /// // Returns an Result<Option<User>>, thus the double unwrap.
+ /// let res = User::from_name("root").unwrap().unwrap();
+ /// assert_eq!(res.name, "root");
+ /// ```
+ pub fn from_name(name: &str) -> Result<Option<Self>> {
+ let name = match CString::new(name) {
+ Ok(c_str) => c_str,
+ Err(_nul_error) => return Ok(None),
+ };
+ User::from_anything(|pwd, cbuf, cap, res| {
+ unsafe { libc::getpwnam_r(name.as_ptr(), pwd, cbuf, cap, res) }
+ })
+ }
+}
+
+/// Representation of a Group, based on `libc::group`
+#[cfg(not(target_os = "redox"))] // RedoxFS does not support passwd
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct Group {
+ /// Group name
+ pub name: String,
+ /// Group password
+ pub passwd: CString,
+ /// Group ID
+ pub gid: Gid,
+ /// List of Group members
+ pub mem: Vec<String>
+}
+
+#[cfg(not(target_os = "redox"))] // RedoxFS does not support passwd
+impl From<&libc::group> for Group {
+ fn from(gr: &libc::group) -> Group {
+ unsafe {
+ Group {
+ name: CStr::from_ptr(gr.gr_name).to_string_lossy().into_owned(),
+ passwd: CString::new(CStr::from_ptr(gr.gr_passwd).to_bytes()).unwrap(),
+ gid: Gid::from_raw(gr.gr_gid),
+ mem: Group::members(gr.gr_mem)
+ }
+ }
+ }
+}
+
+#[cfg(not(target_os = "redox"))] // RedoxFS does not support passwd
+impl Group {
+ unsafe fn members(mem: *mut *mut c_char) -> Vec<String> {
+ let mut ret = Vec::new();
+
+ for i in 0.. {
+ let u = mem.offset(i);
+ if (*u).is_null() {
+ break;
+ } else {
+ let s = CStr::from_ptr(*u).to_string_lossy().into_owned();
+ ret.push(s);
+ }
+ }
+
+ ret
+ }
+
+ fn from_anything<F>(f: F) -> Result<Option<Self>>
+ where
+ F: Fn(*mut libc::group,
+ *mut c_char,
+ libc::size_t,
+ *mut *mut libc::group) -> libc::c_int
+ {
+ let buflimit = 1048576;
+ let bufsize = match sysconf(SysconfVar::GETGR_R_SIZE_MAX) {
+ Ok(Some(n)) => n as usize,
+ Ok(None) | Err(_) => 16384,
+ };
+
+ let mut cbuf = Vec::with_capacity(bufsize);
+ let mut grp = mem::MaybeUninit::<libc::group>::uninit();
+ let mut res = ptr::null_mut();
+
+ loop {
+ let error = f(grp.as_mut_ptr(), cbuf.as_mut_ptr(), cbuf.capacity(), &mut res);
+ if error == 0 {
+ if res.is_null() {
+ return Ok(None);
+ } else {
+ let grp = unsafe { grp.assume_init() };
+ return Ok(Some(Group::from(&grp)));
+ }
+ } else if Errno::last() == Errno::ERANGE {
+ // Trigger the internal buffer resizing logic.
+ reserve_double_buffer_size(&mut cbuf, buflimit)?;
+ } else {
+ return Err(Errno::last());
+ }
+ }
+ }
+
+ /// Get a group by GID.
+ ///
+ /// Internally, this function calls
+ /// [getgrgid_r(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwuid_r.html)
+ ///
+ /// # Examples
+ ///
+ // Disable this test on all OS except Linux as root group may not exist.
+ #[cfg_attr(not(target_os = "linux"), doc = " ```no_run")]
+ #[cfg_attr(target_os = "linux", doc = " ```")]
+ /// use nix::unistd::{Gid, Group};
+ /// // Returns an Result<Option<Group>>, thus the double unwrap.
+ /// let res = Group::from_gid(Gid::from_raw(0)).unwrap().unwrap();
+ /// assert!(res.name == "root");
+ /// ```
+ pub fn from_gid(gid: Gid) -> Result<Option<Self>> {
+ Group::from_anything(|grp, cbuf, cap, res| {
+ unsafe { libc::getgrgid_r(gid.0, grp, cbuf, cap, res) }
+ })
+ }
+
+ /// Get a group by name.
+ ///
+ /// Internally, this function calls
+ /// [getgrnam_r(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwuid_r.html)
+ ///
+ /// # Examples
+ ///
+ // Disable this test on all OS except Linux as root group may not exist.
+ #[cfg_attr(not(target_os = "linux"), doc = " ```no_run")]
+ #[cfg_attr(target_os = "linux", doc = " ```")]
+ /// use nix::unistd::Group;
+ /// // Returns an Result<Option<Group>>, thus the double unwrap.
+ /// let res = Group::from_name("root").unwrap().unwrap();
+ /// assert!(res.name == "root");
+ /// ```
+ pub fn from_name(name: &str) -> Result<Option<Self>> {
+ let name = match CString::new(name) {
+ Ok(c_str) => c_str,
+ Err(_nul_error) => return Ok(None),
+ };
+ Group::from_anything(|grp, cbuf, cap, res| {
+ unsafe { libc::getgrnam_r(name.as_ptr(), grp, cbuf, cap, res) }
+ })
+ }
+}
+}
+
+feature! {
+#![feature = "term"]
+
+/// Get the name of the terminal device that is open on file descriptor fd
+/// (see [`ttyname(3)`](https://man7.org/linux/man-pages/man3/ttyname.3.html)).
+#[cfg(not(target_os = "fuchsia"))]
+pub fn ttyname(fd: RawFd) -> Result<PathBuf> {
+ const PATH_MAX: usize = libc::PATH_MAX as usize;
+ let mut buf = vec![0_u8; PATH_MAX];
+ let c_buf = buf.as_mut_ptr() as *mut libc::c_char;
+
+ let ret = unsafe { libc::ttyname_r(fd, c_buf, buf.len()) };
+ if ret != 0 {
+ return Err(Errno::from_i32(ret));
+ }
+
+ let nul = buf.iter().position(|c| *c == b'\0').unwrap();
+ buf.truncate(nul);
+ Ok(OsString::from_vec(buf).into())
+}
+}
+
+feature! {
+#![all(feature = "socket", feature = "user")]
+
+/// Get the effective user ID and group ID associated with a Unix domain socket.
+///
+/// See also [getpeereid(3)](https://www.freebsd.org/cgi/man.cgi?query=getpeereid)
+#[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "dragonfly",
+))]
+pub fn getpeereid(fd: RawFd) -> Result<(Uid, Gid)> {
+ let mut uid = 1;
+ let mut gid = 1;
+
+ let ret = unsafe { libc::getpeereid(fd, &mut uid, &mut gid) };
+
+ Errno::result(ret).map(|_| (Uid(uid), Gid(gid)))
+}
+}
+
+feature! {
+#![all(feature = "fs")]
+
+/// Set the file flags.
+///
+/// See also [chflags(2)](https://www.freebsd.org/cgi/man.cgi?query=chflags&sektion=2)
+#[cfg(any(
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "macos",
+ target_os = "ios"
+))]
+pub fn chflags<P: ?Sized + NixPath>(path: &P, flags: FileFlag) -> Result<()> {
+ let res = path.with_nix_path(|cstr| unsafe {
+ libc::chflags(cstr.as_ptr(), flags.bits())
+ })?;
+
+ Errno::result(res).map(drop)
+}
+}