diff options
Diffstat (limited to 'vendor/nix/src/unistd.rs')
-rw-r--r-- | vendor/nix/src/unistd.rs | 3383 |
1 files changed, 0 insertions, 3383 deletions
diff --git a/vendor/nix/src/unistd.rs b/vendor/nix/src/unistd.rs deleted file mode 100644 index ca07b34a2..000000000 --- a/vendor/nix/src/unistd.rs +++ /dev/null @@ -1,3383 +0,0 @@ -//! 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) -} -} |