Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

328
vendor/nix/src/dir.rs vendored Normal file
View File

@@ -0,0 +1,328 @@
//! List directory contents
use crate::errno::Errno;
use crate::fcntl::{self, OFlag};
use crate::sys;
use crate::{NixPath, Result};
use cfg_if::cfg_if;
use std::ffi;
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
use std::ptr;
#[cfg(target_os = "linux")]
use libc::{dirent64 as dirent, readdir64_r as readdir_r};
#[cfg(not(target_os = "linux"))]
use libc::{dirent, readdir_r};
/// An open directory.
///
/// This is a lower-level interface than [`std::fs::ReadDir`]. Notable differences:
/// * can be opened from a file descriptor (as returned by [`openat`][openat],
/// perhaps before knowing if the path represents a file or directory).
/// * implements [`AsFd`][AsFd], so it can be passed to [`fstat`][fstat],
/// [`openat`][openat], etc. The file descriptor continues to be owned by the
/// `Dir`, so callers must not keep a `RawFd` after the `Dir` is dropped.
/// * can be iterated through multiple times without closing and reopening the file
/// descriptor. Each iteration rewinds when finished.
/// * returns entries for `.` (current directory) and `..` (parent directory).
/// * returns entries' names as a [`CStr`][cstr] (no allocation or conversion beyond whatever libc
/// does).
///
/// [AsFd]: std::os::fd::AsFd
/// [fstat]: crate::sys::stat::fstat
/// [openat]: crate::fcntl::openat
/// [cstr]: std::ffi::CStr
///
/// # Examples
///
/// Traverse the current directory, and print entries' names:
///
/// ```
/// use nix::dir::Dir;
/// use nix::fcntl::OFlag;
/// use nix::sys::stat::Mode;
///
/// let mut cwd = Dir::open(".", OFlag::O_RDONLY | OFlag::O_CLOEXEC, Mode::empty()).unwrap();
/// for res_entry in cwd.iter() {
/// let entry = res_entry.unwrap();
/// println!("File name: {}", entry.file_name().to_string_lossy());
/// }
/// ```
#[derive(Debug, Eq, Hash, PartialEq)]
pub struct Dir(ptr::NonNull<libc::DIR>);
impl Dir {
/// Opens the given path as with `fcntl::open`.
pub fn open<P: ?Sized + NixPath>(
path: &P,
oflag: OFlag,
mode: sys::stat::Mode,
) -> Result<Self> {
let fd = fcntl::open(path, oflag, mode)?;
Dir::from_fd(fd)
}
/// Opens the given path as with `fcntl::openat`.
pub fn openat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
dirfd: Fd,
path: &P,
oflag: OFlag,
mode: sys::stat::Mode,
) -> Result<Self> {
let fd = fcntl::openat(dirfd, path, oflag, mode)?;
Dir::from_fd(fd)
}
/// Converts from a descriptor-based object, closing the descriptor on success or failure.
///
/// # Safety
///
/// It is only safe if `fd` is an owned file descriptor.
#[inline]
#[deprecated(
since = "0.30.0",
note = "Deprecate this since it is not I/O-safe, use from_fd instead."
)]
pub unsafe fn from<F: IntoRawFd>(fd: F) -> Result<Self> {
use std::os::fd::FromRawFd;
use std::os::fd::OwnedFd;
// SAFETY:
//
// This is indeed unsafe is `fd` it not an owned fd.
let owned_fd = unsafe { OwnedFd::from_raw_fd(fd.into_raw_fd()) };
Dir::from_fd(owned_fd)
}
/// Converts from a file descriptor, closing it on failure.
///
/// # Examples
///
/// `ENOTDIR` would be returned if `fd` does not refer to a directory:
///
/// ```should_panic
/// use std::os::fd::OwnedFd;
/// use nix::dir::Dir;
///
/// let temp_file = tempfile::tempfile().unwrap();
/// let temp_file_fd: OwnedFd = temp_file.into();
/// let never = Dir::from_fd(temp_file_fd).unwrap();
/// ```
#[doc(alias("fdopendir"))]
pub fn from_fd(fd: std::os::fd::OwnedFd) -> Result<Self> {
// take the ownership as the constructed `Dir` is now the owner
let raw_fd = fd.into_raw_fd();
let d = ptr::NonNull::new(unsafe { libc::fdopendir(raw_fd) })
.ok_or(Errno::last())?;
Ok(Dir(d))
}
/// Returns an iterator of `Result<Entry>` which rewinds when finished.
pub fn iter(&mut self) -> Iter {
Iter(self)
}
}
// `Dir` is not `Sync`. With the current implementation, it could be, but according to
// https://www.gnu.org/software/libc/manual/html_node/Reading_002fClosing-Directory.html,
// future versions of POSIX are likely to obsolete `readdir_r` and specify that it's unsafe to
// call `readdir` simultaneously from multiple threads.
//
// `Dir` is safe to pass from one thread to another, as it's not reference-counted.
unsafe impl Send for Dir {}
impl std::os::fd::AsFd for Dir {
fn as_fd(&self) -> std::os::fd::BorrowedFd {
let raw_fd = self.as_raw_fd();
// SAFETY:
//
// `raw_fd` should be open and valid for the lifetime of the returned
// `BorrowedFd` as it is extracted from `&self`.
unsafe { std::os::fd::BorrowedFd::borrow_raw(raw_fd) }
}
}
impl AsRawFd for Dir {
fn as_raw_fd(&self) -> RawFd {
unsafe { libc::dirfd(self.0.as_ptr()) }
}
}
impl Drop for Dir {
fn drop(&mut self) {
let e = Errno::result(unsafe { libc::closedir(self.0.as_ptr()) });
if !std::thread::panicking() && e == Err(Errno::EBADF) {
panic!("Closing an invalid file descriptor!");
};
}
}
// The pass by mut is technically needless only because the inner NonNull is
// Copy. But philosophically we're mutating the Dir, so we pass by mut.
#[allow(clippy::needless_pass_by_ref_mut)]
fn next(dir: &mut Dir) -> Option<Result<Entry>> {
unsafe {
// Note: POSIX specifies that portable applications should dynamically allocate a
// buffer with room for a `d_name` field of size `pathconf(..., _PC_NAME_MAX)` plus 1
// for the NUL byte. It doesn't look like the std library does this; it just uses
// fixed-sized buffers (and libc's dirent seems to be sized so this is appropriate).
// Probably fine here too then.
let mut ent = std::mem::MaybeUninit::<dirent>::uninit();
let mut result = ptr::null_mut();
if let Err(e) = Errno::result(readdir_r(
dir.0.as_ptr(),
ent.as_mut_ptr(),
&mut result,
)) {
return Some(Err(e));
}
if result.is_null() {
return None;
}
assert_eq!(result, ent.as_mut_ptr());
Some(Ok(Entry(ent.assume_init())))
}
}
/// Return type of [`Dir::iter`].
#[derive(Debug, Eq, Hash, PartialEq)]
pub struct Iter<'d>(&'d mut Dir);
impl Iterator for Iter<'_> {
type Item = Result<Entry>;
fn next(&mut self) -> Option<Self::Item> {
next(self.0)
}
}
impl Drop for Iter<'_> {
fn drop(&mut self) {
unsafe { libc::rewinddir((self.0).0.as_ptr()) }
}
}
/// The return type of [Dir::into_iter]
#[derive(Debug, Eq, Hash, PartialEq)]
pub struct OwningIter(Dir);
impl Iterator for OwningIter {
type Item = Result<Entry>;
fn next(&mut self) -> Option<Self::Item> {
next(&mut self.0)
}
}
/// The file descriptor continues to be owned by the `OwningIter`,
/// so callers must not keep a `RawFd` after the `OwningIter` is dropped.
impl AsRawFd for OwningIter {
fn as_raw_fd(&self) -> RawFd {
self.0.as_raw_fd()
}
}
impl IntoIterator for Dir {
type Item = Result<Entry>;
type IntoIter = OwningIter;
/// Creates a owning iterator, that is, one that takes ownership of the
/// `Dir`. The `Dir` cannot be used after calling this. This can be useful
/// when you have a function that both creates a `Dir` instance and returns
/// an `Iterator`.
///
/// Example:
///
/// ```
/// use nix::{dir::Dir, fcntl::OFlag, sys::stat::Mode};
/// use std::{iter::Iterator, string::String};
///
/// fn ls_upper(dirname: &str) -> impl Iterator<Item=String> {
/// let d = Dir::open(dirname, OFlag::O_DIRECTORY, Mode::S_IXUSR).unwrap();
/// d.into_iter().map(|x| x.unwrap().file_name().as_ref().to_string_lossy().to_ascii_uppercase())
/// }
/// ```
fn into_iter(self) -> Self::IntoIter {
OwningIter(self)
}
}
/// A directory entry, similar to `std::fs::DirEntry`.
///
/// Note that unlike the std version, this may represent the `.` or `..` entries.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
#[repr(transparent)]
pub struct Entry(dirent);
/// Type of file referenced by a directory entry
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub enum Type {
/// FIFO (Named pipe)
Fifo,
/// Character device
CharacterDevice,
/// Directory
Directory,
/// Block device
BlockDevice,
/// Regular file
File,
/// Symbolic link
Symlink,
/// Unix-domain socket
Socket,
}
impl Entry {
/// Returns the inode number (`d_ino`) of the underlying `dirent`.
#[allow(clippy::useless_conversion)] // Not useless on all OSes
// The cast is not unnecessary on all platforms.
#[allow(clippy::unnecessary_cast)]
pub fn ino(&self) -> u64 {
cfg_if! {
if #[cfg(any(target_os = "aix",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "haiku",
target_os = "hurd",
target_os = "cygwin",
solarish,
linux_android,
apple_targets))] {
self.0.d_ino as u64
} else {
u64::from(self.0.d_fileno)
}
}
}
/// Returns the bare file name of this directory entry without any other leading path component.
pub fn file_name(&self) -> &ffi::CStr {
unsafe { ffi::CStr::from_ptr(self.0.d_name.as_ptr()) }
}
/// Returns the type of this directory entry, if known.
///
/// See platform `readdir(3)` or `dirent(5)` manpage for when the file type is known;
/// notably, some Linux filesystems don't implement this. The caller should use `stat` or
/// `fstat` if this returns `None`.
pub fn file_type(&self) -> Option<Type> {
#[cfg(not(any(solarish, target_os = "aix", target_os = "haiku")))]
match self.0.d_type {
libc::DT_FIFO => Some(Type::Fifo),
libc::DT_CHR => Some(Type::CharacterDevice),
libc::DT_DIR => Some(Type::Directory),
libc::DT_BLK => Some(Type::BlockDevice),
libc::DT_REG => Some(Type::File),
libc::DT_LNK => Some(Type::Symlink),
libc::DT_SOCK => Some(Type::Socket),
/* libc::DT_UNKNOWN | */ _ => None,
}
// illumos, Solaris, and Haiku systems do not have the d_type member at all:
#[cfg(any(solarish, target_os = "aix", target_os = "haiku"))]
None
}
}

63
vendor/nix/src/env.rs vendored Normal file
View File

@@ -0,0 +1,63 @@
//! Environment variables
use cfg_if::cfg_if;
use std::fmt;
/// Indicates that [`clearenv`] failed for some unknown reason
#[derive(Clone, Copy, Debug)]
pub struct ClearEnvError;
impl fmt::Display for ClearEnvError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "clearenv failed")
}
}
impl std::error::Error for ClearEnvError {}
/// Clear the environment of all name-value pairs.
///
/// On platforms where libc provides `clearenv()`, it will be used. libc's
/// `clearenv()` is documented to return an error code but not set errno; if the
/// return value indicates a failure, this function will return
/// [`ClearEnvError`].
///
/// On platforms where libc does not provide `clearenv()`, a fallback
/// implementation will be used that iterates over all environment variables and
/// removes them one-by-one.
///
/// # Safety
///
/// This function is not threadsafe and can cause undefined behavior in
/// combination with `std::env` or other program components that access the
/// environment. See, for example, the discussion on `std::env::remove_var`; this
/// function is a case of an "inherently unsafe non-threadsafe API" dealing with
/// the environment.
///
/// The caller must ensure no other threads access the process environment while
/// this function executes and that no raw pointers to an element of libc's
/// `environ` is currently held. The latter is not an issue if the only other
/// environment access in the program is via `std::env`, but the requirement on
/// thread safety must still be upheld.
pub unsafe fn clearenv() -> std::result::Result<(), ClearEnvError> {
cfg_if! {
if #[cfg(any(linux_android,
target_os = "fuchsia",
target_os = "wasi",
target_env = "uclibc",
target_os = "emscripten"))] {
let ret = unsafe { libc::clearenv() };
} else {
use std::env;
for (name, _) in env::vars_os() {
env::remove_var(name);
}
let ret = 0;
}
}
if ret == 0 {
Ok(())
} else {
Err(ClearEnvError)
}
}

3859
vendor/nix/src/errno.rs vendored Normal file

File diff suppressed because it is too large Load Diff

1619
vendor/nix/src/fcntl.rs vendored Normal file

File diff suppressed because it is too large Load Diff

127
vendor/nix/src/features.rs vendored Normal file
View File

@@ -0,0 +1,127 @@
//! Feature tests for OS functionality
pub use self::os::*;
#[cfg(any(linux_android, target_os = "emscripten"))]
mod os {
use crate::sys::utsname::uname;
use crate::Result;
use std::os::unix::ffi::OsStrExt;
use std::sync::atomic::{AtomicUsize, Ordering};
// Features:
// * atomic cloexec on socket: 2.6.27
// * pipe2: 2.6.27
// * accept4: 2.6.28
static VERS_UNKNOWN: usize = 1;
static VERS_2_6_18: usize = 2;
static VERS_2_6_27: usize = 3;
static VERS_2_6_28: usize = 4;
static VERS_3: usize = 5;
#[inline]
fn digit(dst: &mut usize, b: u8) {
*dst *= 10;
*dst += (b - b'0') as usize;
}
fn parse_kernel_version() -> Result<usize> {
let u = uname()?;
let mut curr: usize = 0;
let mut major: usize = 0;
let mut minor: usize = 0;
let mut patch: usize = 0;
for &b in u.release().as_bytes() {
if curr >= 3 {
break;
}
match b {
b'.' | b'-' => {
curr += 1;
}
b'0'..=b'9' => match curr {
0 => digit(&mut major, b),
1 => digit(&mut minor, b),
_ => digit(&mut patch, b),
},
_ => break,
}
}
Ok(if major >= 3 {
VERS_3
} else if major >= 2 {
if minor >= 7 {
VERS_UNKNOWN
} else if minor >= 6 {
if patch >= 28 {
VERS_2_6_28
} else if patch >= 27 {
VERS_2_6_27
} else {
VERS_2_6_18
}
} else {
VERS_UNKNOWN
}
} else {
VERS_UNKNOWN
})
}
fn kernel_version() -> Result<usize> {
static KERNEL_VERS: AtomicUsize = AtomicUsize::new(0);
let mut kernel_vers = KERNEL_VERS.load(Ordering::Relaxed);
if kernel_vers == 0 {
kernel_vers = parse_kernel_version()?;
KERNEL_VERS.store(kernel_vers, Ordering::Relaxed);
}
Ok(kernel_vers)
}
/// Check if the OS supports atomic close-on-exec for sockets
pub fn socket_atomic_cloexec() -> bool {
kernel_version()
.map(|version| version >= VERS_2_6_27)
.unwrap_or(false)
}
#[test]
fn test_parsing_kernel_version() {
assert!(kernel_version().unwrap() > 0);
}
}
#[cfg(any(
freebsdlike, // FreeBSD since 10.0 DragonFlyBSD since ???
netbsdlike, // NetBSD since 6.0 OpenBSD since 5.7
target_os = "hurd", // Since glibc 2.28
target_os = "illumos", // Since ???
target_os = "redox", // Since 1-july-2020
target_os = "cygwin",
))]
mod os {
/// Check if the OS supports atomic close-on-exec for sockets
pub const fn socket_atomic_cloexec() -> bool {
true
}
}
#[cfg(any(
target_os = "aix",
apple_targets,
target_os = "fuchsia",
target_os = "haiku",
target_os = "solaris"
))]
mod os {
/// Check if the OS supports atomic close-on-exec for sockets
pub const fn socket_atomic_cloexec() -> bool {
false
}
}

217
vendor/nix/src/ifaddrs.rs vendored Normal file
View File

@@ -0,0 +1,217 @@
//! Query network interface addresses
//!
//! Uses the Linux and/or BSD specific function `getifaddrs` to query the list
//! of interfaces and their associated addresses.
use cfg_if::cfg_if;
#[cfg(apple_targets)]
use std::convert::TryFrom;
use std::ffi;
use std::iter::Iterator;
use std::mem;
use std::option::Option;
use crate::net::if_::*;
use crate::sys::socket::{SockaddrLike, SockaddrStorage};
use crate::{Errno, Result};
/// Describes a single address for an interface as returned by `getifaddrs`.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct InterfaceAddress {
/// Name of the network interface
pub interface_name: String,
/// Flags as from `SIOCGIFFLAGS` ioctl
pub flags: InterfaceFlags,
/// Network address of this interface
pub address: Option<SockaddrStorage>,
/// Netmask of this interface
pub netmask: Option<SockaddrStorage>,
/// Broadcast address of this interface, if applicable
pub broadcast: Option<SockaddrStorage>,
/// Point-to-point destination address
pub destination: Option<SockaddrStorage>,
}
cfg_if! {
if #[cfg(any(linux_android, target_os = "emscripten", target_os = "fuchsia"))] {
fn get_ifu_from_sockaddr(info: &libc::ifaddrs) -> *const libc::sockaddr {
info.ifa_ifu
}
} else {
fn get_ifu_from_sockaddr(info: &libc::ifaddrs) -> *const libc::sockaddr {
info.ifa_dstaddr
}
}
}
/// Workaround a bug in XNU where netmasks will always have the wrong size in
/// the sa_len field due to the kernel ignoring trailing zeroes in the structure
/// when setting the field. See https://github.com/nix-rust/nix/issues/1709#issuecomment-1199304470
///
/// To fix this, we stack-allocate a new sockaddr_storage, zero it out, and
/// memcpy sa_len of the netmask to that new storage. Finally, we reset the
/// ss_len field to sizeof(sockaddr_storage). This is supposedly valid as all
/// members of the sockaddr_storage are "ok" with being zeroed out (there are
/// no pointers).
#[cfg(apple_targets)]
unsafe fn workaround_xnu_bug(info: &libc::ifaddrs) -> Option<SockaddrStorage> {
let src_sock = info.ifa_netmask;
if src_sock.is_null() {
return None;
}
let mut dst_sock = mem::MaybeUninit::<libc::sockaddr_storage>::zeroed();
let dst_sock = unsafe {
// memcpy only sa_len bytes, assume the rest is zero
std::ptr::copy_nonoverlapping(
src_sock as *const u8,
dst_sock.as_mut_ptr().cast(),
(*src_sock).sa_len.into(),
);
// Initialize ss_len to sizeof(libc::sockaddr_storage).
(*dst_sock.as_mut_ptr()).ss_len =
u8::try_from(mem::size_of::<libc::sockaddr_storage>()).unwrap();
dst_sock.assume_init()
};
let dst_sock_ptr =
&dst_sock as *const libc::sockaddr_storage as *const libc::sockaddr;
unsafe { SockaddrStorage::from_raw(dst_sock_ptr, None) }
}
impl InterfaceAddress {
/// Create an `InterfaceAddress` from the libc struct.
fn from_libc_ifaddrs(info: &libc::ifaddrs) -> InterfaceAddress {
let ifname = unsafe { ffi::CStr::from_ptr(info.ifa_name) };
let address = unsafe { SockaddrStorage::from_raw(info.ifa_addr, None) };
#[cfg(apple_targets)]
let netmask = unsafe { workaround_xnu_bug(info) };
#[cfg(not(apple_targets))]
let netmask =
unsafe { SockaddrStorage::from_raw(info.ifa_netmask, None) };
let mut addr = InterfaceAddress {
interface_name: ifname.to_string_lossy().into_owned(),
flags: InterfaceFlags::from_bits_truncate(
info.ifa_flags as IflagsType,
),
address,
netmask,
broadcast: None,
destination: None,
};
let ifu = get_ifu_from_sockaddr(info);
if addr.flags.contains(InterfaceFlags::IFF_POINTOPOINT) {
addr.destination = unsafe { SockaddrStorage::from_raw(ifu, None) };
} else if addr.flags.contains(InterfaceFlags::IFF_BROADCAST) {
addr.broadcast = unsafe { SockaddrStorage::from_raw(ifu, None) };
}
addr
}
}
/// Holds the results of `getifaddrs`.
///
/// Use the function `getifaddrs` to create this Iterator. Note that the
/// actual list of interfaces can be iterated once and will be freed as
/// soon as the Iterator goes out of scope.
#[derive(Debug, Eq, Hash, PartialEq)]
pub struct InterfaceAddressIterator {
base: *mut libc::ifaddrs,
next: *mut libc::ifaddrs,
}
impl Drop for InterfaceAddressIterator {
fn drop(&mut self) {
unsafe { libc::freeifaddrs(self.base) };
}
}
impl Iterator for InterfaceAddressIterator {
type Item = InterfaceAddress;
fn next(&mut self) -> Option<<Self as Iterator>::Item> {
match unsafe { self.next.as_ref() } {
Some(ifaddr) => {
self.next = ifaddr.ifa_next;
Some(InterfaceAddress::from_libc_ifaddrs(ifaddr))
}
None => None,
}
}
}
/// Get interface addresses using libc's `getifaddrs`
///
/// Note that the underlying implementation differs between OSes. Only the
/// most common address families are supported by the nix crate (due to
/// lack of time and complexity of testing). The address family is encoded
/// in the specific variant of `SockaddrStorage` returned for the fields
/// `address`, `netmask`, `broadcast`, and `destination`. For any entry not
/// supported, the returned list will contain a `None` entry.
///
/// # Example
/// ```
/// let addrs = nix::ifaddrs::getifaddrs().unwrap();
/// for ifaddr in addrs {
/// match ifaddr.address {
/// Some(address) => {
/// println!("interface {} address {}",
/// ifaddr.interface_name, address);
/// },
/// None => {
/// println!("interface {} with unsupported address family",
/// ifaddr.interface_name);
/// }
/// }
/// }
/// ```
pub fn getifaddrs() -> Result<InterfaceAddressIterator> {
let mut addrs = mem::MaybeUninit::<*mut libc::ifaddrs>::uninit();
unsafe {
Errno::result(libc::getifaddrs(addrs.as_mut_ptr())).map(|_| {
InterfaceAddressIterator {
base: addrs.assume_init(),
next: addrs.assume_init(),
}
})
}
}
#[cfg(test)]
mod tests {
use super::*;
// Only checks if `getifaddrs` can be invoked without panicking.
#[test]
fn test_getifaddrs() {
let _ = getifaddrs();
}
// Ensures getting the netmask works, and in particular that
// `workaround_xnu_bug` works properly.
#[test]
fn test_getifaddrs_netmask_correct() {
let addrs = getifaddrs().unwrap();
for iface in addrs {
let sock = if let Some(sock) = iface.netmask {
sock
} else {
continue;
};
if sock.family() == Some(crate::sys::socket::AddressFamily::Inet) {
let _ = sock.as_sockaddr_in().unwrap();
return;
} else if sock.family()
== Some(crate::sys::socket::AddressFamily::Inet6)
{
let _ = sock.as_sockaddr_in6().unwrap();
return;
}
}
panic!("No address?");
}
}

132
vendor/nix/src/kmod.rs vendored Normal file
View File

@@ -0,0 +1,132 @@
//! Load and unload kernel modules.
//!
//! For more details see
use std::ffi::CStr;
use std::os::unix::io::{AsFd, AsRawFd};
use crate::errno::Errno;
use crate::Result;
/// Loads a kernel module from a buffer.
///
/// It loads an ELF image into kernel space,
/// performs any necessary symbol relocations,
/// initializes module parameters to values provided by the caller,
/// and then runs the module's init function.
///
/// This function requires `CAP_SYS_MODULE` privilege.
///
/// The `module_image` argument points to a buffer containing the binary image
/// to be loaded. The buffer should contain a valid ELF image
/// built for the running kernel.
///
/// The `param_values` argument is a string containing space-delimited specifications
/// of the values for module parameters.
/// Each of the parameter specifications has the form:
///
/// `name[=value[,value...]]`
///
/// # Example
///
/// ```no_run
/// use std::fs::File;
/// use std::io::Read;
/// use std::ffi::CString;
/// use nix::kmod::init_module;
///
/// let mut f = File::open("mykernel.ko").unwrap();
/// let mut contents: Vec<u8> = Vec::new();
/// f.read_to_end(&mut contents).unwrap();
/// init_module(&mut contents, &CString::new("who=Rust when=Now,12").unwrap()).unwrap();
/// ```
///
/// See [`man init_module(2)`](https://man7.org/linux/man-pages/man2/init_module.2.html) for more information.
pub fn init_module(module_image: &[u8], param_values: &CStr) -> Result<()> {
let res = unsafe {
libc::syscall(
libc::SYS_init_module,
module_image.as_ptr(),
module_image.len(),
param_values.as_ptr(),
)
};
Errno::result(res).map(drop)
}
libc_bitflags!(
/// Flags used by the `finit_module` function.
pub struct ModuleInitFlags: libc::c_uint {
/// Ignore symbol version hashes.
MODULE_INIT_IGNORE_MODVERSIONS;
/// Ignore kernel version magic.
MODULE_INIT_IGNORE_VERMAGIC;
}
);
/// Loads a kernel module from a given file descriptor.
///
/// # Example
///
/// ```no_run
/// use std::fs::File;
/// use std::ffi::CString;
/// use nix::kmod::{finit_module, ModuleInitFlags};
///
/// let f = File::open("mymod.ko").unwrap();
/// finit_module(&f, &CString::new("").unwrap(), ModuleInitFlags::empty()).unwrap();
/// ```
///
/// See [`man init_module(2)`](https://man7.org/linux/man-pages/man2/init_module.2.html) for more information.
pub fn finit_module<Fd: AsFd>(
fd: Fd,
param_values: &CStr,
flags: ModuleInitFlags,
) -> Result<()> {
let res = unsafe {
libc::syscall(
libc::SYS_finit_module,
fd.as_fd().as_raw_fd(),
param_values.as_ptr(),
flags.bits(),
)
};
Errno::result(res).map(drop)
}
libc_bitflags!(
/// Flags used by `delete_module`.
///
/// See [`man delete_module(2)`](https://man7.org/linux/man-pages/man2/delete_module.2.html)
/// for a detailed description how these flags work.
pub struct DeleteModuleFlags: libc::c_int {
/// `delete_module` will return immediately, with an error, if the module has a nonzero
/// reference count.
O_NONBLOCK;
/// `delete_module` will unload the module immediately, regardless of whether it has a
/// nonzero reference count.
O_TRUNC;
}
);
/// Unloads the kernel module with the given name.
///
/// # Example
///
/// ```no_run
/// use std::ffi::CString;
/// use nix::kmod::{delete_module, DeleteModuleFlags};
///
/// delete_module(&CString::new("mymod").unwrap(), DeleteModuleFlags::O_NONBLOCK).unwrap();
/// ```
///
/// See [`man delete_module(2)`](https://man7.org/linux/man-pages/man2/delete_module.2.html) for more information.
pub fn delete_module(name: &CStr, flags: DeleteModuleFlags) -> Result<()> {
let res = unsafe {
libc::syscall(libc::SYS_delete_module, name.as_ptr(), flags.bits())
};
Errno::result(res).map(drop)
}

413
vendor/nix/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,413 @@
//! Rust friendly bindings to the various *nix system functions.
//!
//! Modules are structured according to the C header file that they would be
//! defined in.
//!
//! # Features
//!
//! Nix uses the following Cargo features to enable optional functionality.
//! They may be enabled in any combination.
//! * `acct` - Process accounting
//! * `aio` - POSIX AIO
//! * `dir` - Stuff relating to directory iteration
//! * `env` - Manipulate environment variables
//! * `event` - Event-driven APIs, like `kqueue` and `epoll`
//! * `fanotify` - Linux's `fanotify` filesystem events monitoring API
//! * `feature` - Query characteristics of the OS at runtime
//! * `fs` - File system functionality
//! * `hostname` - Get and set the system's hostname
//! * `inotify` - Linux's `inotify` file system notification API
//! * `ioctl` - The `ioctl` syscall, and wrappers for many specific instances
//! * `kmod` - Load and unload kernel modules
//! * `mman` - Stuff relating to memory management
//! * `mount` - Mount and unmount file systems
//! * `mqueue` - POSIX message queues
//! * `net` - Networking-related functionality
//! * `personality` - Set the process execution domain
//! * `poll` - APIs like `poll` and `select`
//! * `process` - Stuff relating to running processes
//! * `pthread` - POSIX threads
//! * `ptrace` - Process tracing and debugging
//! * `quota` - File system quotas
//! * `reboot` - Reboot the system
//! * `resource` - Process resource limits
//! * `sched` - Manipulate process's scheduling
//! * `socket` - Sockets, whether for networking or local use
//! * `signal` - Send and receive signals to processes
//! * `syslog` - System logging
//! * `term` - Terminal control APIs
//! * `time` - Query the operating system's clocks
//! * `ucontext` - User thread context
//! * `uio` - Vectored I/O
//! * `user` - Stuff relating to users and groups
//! * `zerocopy` - APIs like `sendfile` and `copy_file_range`
#![crate_name = "nix"]
#![cfg(unix)]
#![allow(non_camel_case_types)]
// A clear document is a good document no matter if it has a summary in its
// first paragraph or not.
#![allow(clippy::too_long_first_doc_paragraph)]
#![recursion_limit = "500"]
#![deny(unused)]
#![deny(unexpected_cfgs)]
#![allow(unused_macros)]
#![cfg_attr(
not(all(
feature = "acct",
feature = "aio",
feature = "dir",
feature = "env",
feature = "event",
feature = "fanotify",
feature = "feature",
feature = "fs",
feature = "hostname",
feature = "inotify",
feature = "ioctl",
feature = "kmod",
feature = "mman",
feature = "mount",
feature = "mqueue",
feature = "net",
feature = "personality",
feature = "poll",
feature = "process",
feature = "pthread",
feature = "ptrace",
feature = "quota",
feature = "reboot",
feature = "resource",
feature = "sched",
feature = "socket",
feature = "signal",
feature = "syslog",
feature = "term",
feature = "time",
feature = "ucontext",
feature = "uio",
feature = "user",
feature = "zerocopy",
)),
allow(unused_imports)
)]
#![deny(unstable_features)]
#![deny(missing_copy_implementations)]
#![deny(missing_debug_implementations)]
#![warn(missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![deny(clippy::cast_ptr_alignment)]
#![deny(unsafe_op_in_unsafe_fn)]
// I found the change suggested by this rules could hurt code readability. I cannot
// remeber every type's default value, in such cases, it forces me to open
// the std doc to insepct the Default value, which is unnecessary with
// `.unwrap_or(value)`.
#![allow(clippy::unwrap_or_default)]
// Re-exported external crates
pub use libc;
// Private internal modules
#[macro_use]
mod macros;
// Public crates
#[cfg(not(target_os = "redox"))]
feature! {
#![feature = "dir"]
pub mod dir;
}
feature! {
#![feature = "env"]
pub mod env;
}
#[allow(missing_docs)]
pub mod errno;
feature! {
#![feature = "feature"]
#[deny(missing_docs)]
pub mod features;
}
pub mod fcntl;
feature! {
#![feature = "net"]
#[cfg(any(linux_android,
bsd,
solarish))]
#[deny(missing_docs)]
pub mod ifaddrs;
#[cfg(not(target_os = "redox"))]
#[deny(missing_docs)]
pub mod net;
}
#[cfg(linux_android)]
feature! {
#![feature = "kmod"]
pub mod kmod;
}
feature! {
#![feature = "mount"]
pub mod mount;
}
#[cfg(any(
freebsdlike,
all(target_os = "linux", not(target_env = "ohos")),
target_os = "netbsd"
))]
feature! {
#![feature = "mqueue"]
pub mod mqueue;
}
feature! {
#![feature = "poll"]
pub mod poll;
}
#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
feature! {
#![feature = "term"]
#[deny(missing_docs)]
pub mod pty;
}
feature! {
#![feature = "sched"]
pub mod sched;
}
pub mod sys;
feature! {
#![feature = "time"]
pub mod time;
}
// This can be implemented for other platforms as soon as libc
// provides bindings for them.
#[cfg(all(
target_os = "linux",
any(
target_arch = "aarch64",
target_arch = "s390x",
target_arch = "x86",
target_arch = "x86_64"
)
))]
feature! {
#![feature = "ucontext"]
#[allow(missing_docs)]
pub mod ucontext;
}
pub mod unistd;
#[cfg(any(feature = "poll", feature = "event"))]
mod poll_timeout;
#[cfg(any(
target_os = "freebsd",
target_os = "haiku",
target_os = "linux",
target_os = "netbsd",
apple_targets
))]
feature! {
#![feature = "process"]
pub mod spawn;
}
feature! {
#![feature = "syslog"]
pub mod syslog;
}
use std::ffi::{CStr, CString, OsStr};
use std::mem::MaybeUninit;
use std::os::unix::ffi::OsStrExt;
use std::path::{Path, PathBuf};
use std::{ptr, result, slice};
use errno::Errno;
/// Nix Result Type
pub type Result<T> = result::Result<T, Errno>;
/// Nix's main error type.
///
/// It's a wrapper around Errno. As such, it's very interoperable with
/// [`std::io::Error`], but it has the advantages of:
/// * `Clone`
/// * `Copy`
/// * `Eq`
/// * Small size
/// * Represents all of the system's errnos, instead of just the most common
/// ones.
pub type Error = Errno;
/// Common trait used to represent file system paths by many Nix functions.
pub trait NixPath {
/// Is the path empty?
fn is_empty(&self) -> bool;
/// Length of the path in bytes
fn len(&self) -> usize;
/// Execute a function with this path as a `CStr`.
///
/// Mostly used internally by Nix.
fn with_nix_path<T, F>(&self, f: F) -> Result<T>
where
F: FnOnce(&CStr) -> T;
}
impl NixPath for str {
fn is_empty(&self) -> bool {
NixPath::is_empty(OsStr::new(self))
}
fn len(&self) -> usize {
NixPath::len(OsStr::new(self))
}
fn with_nix_path<T, F>(&self, f: F) -> Result<T>
where
F: FnOnce(&CStr) -> T,
{
OsStr::new(self).with_nix_path(f)
}
}
impl NixPath for OsStr {
fn is_empty(&self) -> bool {
self.as_bytes().is_empty()
}
fn len(&self) -> usize {
self.as_bytes().len()
}
fn with_nix_path<T, F>(&self, f: F) -> Result<T>
where
F: FnOnce(&CStr) -> T,
{
self.as_bytes().with_nix_path(f)
}
}
impl NixPath for CStr {
fn is_empty(&self) -> bool {
self.to_bytes().is_empty()
}
fn len(&self) -> usize {
self.to_bytes().len()
}
fn with_nix_path<T, F>(&self, f: F) -> Result<T>
where
F: FnOnce(&CStr) -> T,
{
Ok(f(self))
}
}
impl NixPath for [u8] {
fn is_empty(&self) -> bool {
self.is_empty()
}
fn len(&self) -> usize {
self.len()
}
fn with_nix_path<T, F>(&self, f: F) -> Result<T>
where
F: FnOnce(&CStr) -> T,
{
// The real PATH_MAX is typically 4096, but it's statistically unlikely to have a path
// longer than ~300 bytes. See the PR description to get stats for your own machine.
// https://github.com/nix-rust/nix/pull/1656
//
// By being smaller than a memory page, we also avoid the compiler inserting a probe frame:
// https://docs.rs/compiler_builtins/latest/compiler_builtins/probestack/index.html
const MAX_STACK_ALLOCATION: usize = 1024;
if self.len() >= MAX_STACK_ALLOCATION {
return with_nix_path_allocating(self, f);
}
let mut buf = MaybeUninit::<[u8; MAX_STACK_ALLOCATION]>::uninit();
let buf_ptr = buf.as_mut_ptr().cast();
unsafe {
ptr::copy_nonoverlapping(self.as_ptr(), buf_ptr, self.len());
buf_ptr.add(self.len()).write(0);
}
match CStr::from_bytes_with_nul(unsafe {
slice::from_raw_parts(buf_ptr, self.len() + 1)
}) {
Ok(s) => Ok(f(s)),
Err(_) => Err(Errno::EINVAL),
}
}
}
#[cold]
#[inline(never)]
fn with_nix_path_allocating<T, F>(from: &[u8], f: F) -> Result<T>
where
F: FnOnce(&CStr) -> T,
{
match CString::new(from) {
Ok(s) => Ok(f(&s)),
Err(_) => Err(Errno::EINVAL),
}
}
impl NixPath for Path {
fn is_empty(&self) -> bool {
NixPath::is_empty(self.as_os_str())
}
fn len(&self) -> usize {
NixPath::len(self.as_os_str())
}
fn with_nix_path<T, F>(&self, f: F) -> Result<T>
where
F: FnOnce(&CStr) -> T,
{
self.as_os_str().with_nix_path(f)
}
}
impl NixPath for PathBuf {
fn is_empty(&self) -> bool {
NixPath::is_empty(self.as_os_str())
}
fn len(&self) -> usize {
NixPath::len(self.as_os_str())
}
fn with_nix_path<T, F>(&self, f: F) -> Result<T>
where
F: FnOnce(&CStr) -> T,
{
self.as_os_str().with_nix_path(f)
}
}
/// Like `NixPath::with_nix_path()`, but allow the `path` argument to be optional.
///
/// A NULL pointer will be provided if `path.is_none()`.
#[cfg(any(
all(apple_targets, feature = "mount"),
all(linux_android, any(feature = "mount", feature = "fanotify"))
))]
pub(crate) fn with_opt_nix_path<P, T, F>(path: Option<&P>, f: F) -> Result<T>
where
P: ?Sized + NixPath,
F: FnOnce(*const libc::c_char) -> T,
{
match path {
Some(path) => path.with_nix_path(|p_str| f(p_str.as_ptr())),
None => Ok(f(ptr::null())),
}
}

331
vendor/nix/src/macros.rs vendored Normal file
View File

@@ -0,0 +1,331 @@
// Thanks to Tokio for this macro
macro_rules! feature {
(
#![$meta:meta]
$($item:item)*
) => {
$(
#[cfg($meta)]
#[cfg_attr(docsrs, doc(cfg($meta)))]
$item
)*
}
}
/// The `libc_bitflags!` macro helps with a common use case of defining a public bitflags type
/// with values from the libc crate. It is used the same way as the `bitflags!` macro, except
/// that only the name of the flag value has to be given.
///
/// The `libc` crate must be in scope with the name `libc`.
///
/// # Example
/// ```ignore
/// libc_bitflags!{
/// pub struct ProtFlags: libc::c_int {
/// PROT_NONE;
/// PROT_READ;
/// /// PROT_WRITE enables write protect
/// PROT_WRITE;
/// PROT_EXEC;
/// #[cfg(linux_android)]
/// PROT_GROWSDOWN;
/// #[cfg(linux_android)]
/// PROT_GROWSUP;
/// }
/// }
/// ```
///
/// Example with casting, due to a mistake in libc. In this example, the
/// various flags have different types, so we cast the broken ones to the right
/// type.
///
/// ```ignore
/// libc_bitflags!{
/// pub struct SaFlags: libc::c_ulong {
/// SA_NOCLDSTOP as libc::c_ulong;
/// SA_NOCLDWAIT;
/// SA_NODEFER as libc::c_ulong;
/// SA_ONSTACK;
/// SA_RESETHAND as libc::c_ulong;
/// SA_RESTART as libc::c_ulong;
/// SA_SIGINFO;
/// }
/// }
/// ```
macro_rules! libc_bitflags {
(
$(#[$outer:meta])*
pub struct $BitFlags:ident: $T:ty {
$(
$(#[$inner:ident $($args:tt)*])*
$Flag:ident $(as $cast:ty)*;
)+
}
) => {
::bitflags::bitflags! {
#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[repr(transparent)]
$(#[$outer])*
pub struct $BitFlags: $T {
$(
$(#[$inner $($args)*])*
const $Flag = libc::$Flag $(as $cast)*;
)+
}
}
};
}
/// The `libc_enum!` macro helps with a common use case of defining an enum exclusively using
/// values from the `libc` crate. This macro supports both `pub` and private `enum`s.
///
/// The `libc` crate must be in scope with the name `libc`.
///
/// # Example
/// ```ignore
/// libc_enum!{
/// pub enum ProtFlags {
/// PROT_NONE,
/// PROT_READ,
/// PROT_WRITE,
/// PROT_EXEC,
/// #[cfg(linux_android)]
/// PROT_GROWSDOWN,
/// #[cfg(linux_android)]
/// PROT_GROWSUP,
/// }
/// }
/// ```
// Some targets don't use all rules.
#[allow(unused_macro_rules)]
macro_rules! libc_enum {
// Exit rule.
(@make_enum
name: $BitFlags:ident,
{
$v:vis
attrs: [$($attrs:tt)*],
entries: [$($entries:tt)*],
}
) => {
$($attrs)*
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
$v enum $BitFlags {
$($entries)*
}
};
// Exit rule including TryFrom
(@make_enum
name: $BitFlags:ident,
{
$v:vis
attrs: [$($attrs:tt)*],
entries: [$($entries:tt)*],
from_type: $repr:path,
try_froms: [$($try_froms:tt)*]
}
) => {
$($attrs)*
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
$v enum $BitFlags {
$($entries)*
}
impl ::std::convert::TryFrom<$repr> for $BitFlags {
type Error = $crate::Error;
#[allow(unused_doc_comments)]
#[allow(deprecated)]
#[allow(unused_attributes)]
fn try_from(x: $repr) -> $crate::Result<Self> {
match x {
$($try_froms)*
_ => Err($crate::Error::EINVAL)
}
}
}
};
// Done accumulating.
(@accumulate_entries
name: $BitFlags:ident,
{
$v:vis
attrs: $attrs:tt,
},
$entries:tt,
$try_froms:tt;
) => {
libc_enum! {
@make_enum
name: $BitFlags,
{
$v
attrs: $attrs,
entries: $entries,
}
}
};
// Done accumulating and want TryFrom
(@accumulate_entries
name: $BitFlags:ident,
{
$v:vis
attrs: $attrs:tt,
from_type: $repr:path,
},
$entries:tt,
$try_froms:tt;
) => {
libc_enum! {
@make_enum
name: $BitFlags,
{
$v
attrs: $attrs,
entries: $entries,
from_type: $repr,
try_froms: $try_froms
}
}
};
// Munch an attr.
(@accumulate_entries
name: $BitFlags:ident,
$prefix:tt,
[$($entries:tt)*],
[$($try_froms:tt)*];
#[$attr:meta] $($tail:tt)*
) => {
libc_enum! {
@accumulate_entries
name: $BitFlags,
$prefix,
[
$($entries)*
#[$attr]
],
[
$($try_froms)*
#[$attr]
];
$($tail)*
}
};
// Munch last ident if not followed by a comma.
(@accumulate_entries
name: $BitFlags:ident,
$prefix:tt,
[$($entries:tt)*],
[$($try_froms:tt)*];
$entry:ident
) => {
libc_enum! {
@accumulate_entries
name: $BitFlags,
$prefix,
[
$($entries)*
$entry = libc::$entry,
],
[
$($try_froms)*
libc::$entry => Ok($BitFlags::$entry),
];
}
};
// Munch an ident; covers terminating comma case.
(@accumulate_entries
name: $BitFlags:ident,
$prefix:tt,
[$($entries:tt)*],
[$($try_froms:tt)*];
$entry:ident,
$($tail:tt)*
) => {
libc_enum! {
@accumulate_entries
name: $BitFlags,
$prefix,
[
$($entries)*
$entry = libc::$entry,
],
[
$($try_froms)*
libc::$entry => Ok($BitFlags::$entry),
];
$($tail)*
}
};
// Munch an ident and cast it to the given type; covers terminating comma.
(@accumulate_entries
name: $BitFlags:ident,
$prefix:tt,
[$($entries:tt)*],
[$($try_froms:tt)*];
$entry:ident as $ty:ty,
$($tail:tt)*
) => {
libc_enum! {
@accumulate_entries
name: $BitFlags,
$prefix,
[
$($entries)*
$entry = libc::$entry as $ty,
],
[
$($try_froms)*
libc::$entry as $ty => Ok($BitFlags::$entry),
];
$($tail)*
}
};
// Entry rule.
(
$(#[$attr:meta])*
$v:vis enum $BitFlags:ident {
$($vals:tt)*
}
) => {
libc_enum! {
@accumulate_entries
name: $BitFlags,
{
$v
attrs: [$(#[$attr])*],
},
[],
[];
$($vals)*
}
};
// Entry rule including TryFrom
(
$(#[$attr:meta])*
$v:vis enum $BitFlags:ident {
$($vals:tt)*
}
impl TryFrom<$repr:path>
) => {
libc_enum! {
@accumulate_entries
name: $BitFlags,
{
$v
attrs: [$(#[$attr])*],
from_type: $repr,
},
[],
[];
$($vals)*
}
};
}

111
vendor/nix/src/mount/apple.rs vendored Normal file
View File

@@ -0,0 +1,111 @@
use crate::{Errno, NixPath, Result};
use libc::c_int;
libc_bitflags!(
/// Used with [`mount()`] and [`unmount()`].
pub struct MntFlags: c_int {
/// Do not interpret special files on the filesystem.
MNT_NODEV;
/// Enable data protection on the filesystem if the filesystem is configured for it.
MNT_CPROTECT;
/// file system is quarantined
MNT_QUARANTINE;
/// filesystem is stored locally
MNT_LOCAL;
/// quotas are enabled on filesystem
MNT_QUOTA;
/// identifies the root filesystem
MNT_ROOTFS;
/// file system is not appropriate path to user data
MNT_DONTBROWSE;
/// VFS will ignore ownership information on filesystem objects
MNT_IGNORE_OWNERSHIP;
/// filesystem was mounted by automounter
MNT_AUTOMOUNTED;
/// filesystem is journaled
MNT_JOURNALED;
/// Don't allow user extended attributes
MNT_NOUSERXATTR;
/// filesystem should defer writes
MNT_DEFWRITE;
/// don't block unmount if not responding
MNT_NOBLOCK;
/// file system is exported
MNT_EXPORTED;
/// file system written asynchronously
MNT_ASYNC;
/// Force a read-write mount even if the file system appears to be
/// unclean.
MNT_FORCE;
/// MAC support for objects.
MNT_MULTILABEL;
/// Do not update access times.
MNT_NOATIME;
/// Disallow program execution.
MNT_NOEXEC;
/// Do not honor setuid or setgid bits on files when executing them.
MNT_NOSUID;
/// Mount read-only.
MNT_RDONLY;
/// Causes the vfs subsystem to update its data structures pertaining to
/// the specified already mounted file system.
MNT_RELOAD;
/// Create a snapshot of the file system.
MNT_SNAPSHOT;
/// All I/O to the file system should be done synchronously.
MNT_SYNCHRONOUS;
/// Union with underlying fs.
MNT_UNION;
/// Indicates that the mount command is being applied to an already
/// mounted file system.
MNT_UPDATE;
}
);
/// Mount a file system.
///
/// # Arguments
/// - `source` - Specifies the file system. e.g. `/dev/sd0`.
/// - `target` - Specifies the destination. e.g. `/mnt`.
/// - `flags` - Optional flags controlling the mount.
/// - `data` - Optional file system specific data.
///
/// # see also
/// [`mount`](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/mount.2.html)
pub fn mount<
P1: ?Sized + NixPath,
P2: ?Sized + NixPath,
P3: ?Sized + NixPath,
>(
source: &P1,
target: &P2,
flags: MntFlags,
data: Option<&P3>,
) -> Result<()> {
let res = source.with_nix_path(|s| {
target.with_nix_path(|t| {
crate::with_opt_nix_path(data, |d| unsafe {
libc::mount(
s.as_ptr(),
t.as_ptr(),
flags.bits(),
d.cast_mut().cast(),
)
})
})
})???;
Errno::result(res).map(drop)
}
/// Umount the file system mounted at `target`.
pub fn unmount<P>(target: &P, flags: MntFlags) -> Result<()>
where
P: ?Sized + NixPath,
{
let res = target.with_nix_path(|cstr| unsafe {
libc::unmount(cstr.as_ptr(), flags.bits())
})?;
Errno::result(res).map(drop)
}

View File

@@ -0,0 +1,429 @@
#[cfg(target_os = "freebsd")]
use crate::Error;
use crate::{Errno, NixPath, Result};
use libc::c_int;
#[cfg(target_os = "freebsd")]
use libc::{c_char, c_uint, c_void};
#[cfg(target_os = "freebsd")]
use std::{
borrow::Cow,
ffi::{CStr, CString},
fmt, io,
marker::PhantomData,
};
libc_bitflags!(
/// Used with [`Nmount::nmount`].
pub struct MntFlags: c_int {
/// ACL support enabled.
#[cfg(any(target_os = "netbsd", target_os = "freebsd"))]
MNT_ACLS;
/// All I/O to the file system should be done asynchronously.
MNT_ASYNC;
/// dir should instead be a file system ID encoded as “FSID:val0:val1”.
#[cfg(target_os = "freebsd")]
MNT_BYFSID;
/// Force a read-write mount even if the file system appears to be
/// unclean.
MNT_FORCE;
/// GEOM journal support enabled.
#[cfg(target_os = "freebsd")]
MNT_GJOURNAL;
/// MAC support for objects.
#[cfg(target_os = "freebsd")]
MNT_MULTILABEL;
/// Disable read clustering.
#[cfg(freebsdlike)]
MNT_NOCLUSTERR;
/// Disable write clustering.
#[cfg(freebsdlike)]
MNT_NOCLUSTERW;
/// Enable NFS version 4 ACLs.
#[cfg(target_os = "freebsd")]
MNT_NFS4ACLS;
/// Do not update access times.
MNT_NOATIME;
/// Disallow program execution.
MNT_NOEXEC;
/// Do not honor setuid or setgid bits on files when executing them.
MNT_NOSUID;
/// Do not follow symlinks.
#[cfg(freebsdlike)]
MNT_NOSYMFOLLOW;
/// Mount read-only.
MNT_RDONLY;
/// Causes the vfs subsystem to update its data structures pertaining to
/// the specified already mounted file system.
MNT_RELOAD;
/// Create a snapshot of the file system.
///
/// See [mksnap_ffs(8)](https://www.freebsd.org/cgi/man.cgi?query=mksnap_ffs)
#[cfg(target_os = "freebsd")]
MNT_SNAPSHOT;
/// Using soft updates.
#[cfg(any(freebsdlike, netbsdlike))]
MNT_SOFTDEP;
/// Directories with the SUID bit set chown new files to their own
/// owner.
#[cfg(freebsdlike)]
MNT_SUIDDIR;
/// All I/O to the file system should be done synchronously.
MNT_SYNCHRONOUS;
/// Union with underlying fs.
#[cfg(any(
target_os = "freebsd",
target_os = "netbsd"
))]
MNT_UNION;
/// Indicates that the mount command is being applied to an already
/// mounted file system.
MNT_UPDATE;
/// Check vnode use counts.
#[cfg(target_os = "freebsd")]
MNT_NONBUSY;
}
);
/// The Error type of [`Nmount::nmount`].
///
/// It wraps an [`Errno`], but also may contain an additional message returned
/// by `nmount(2)`.
#[cfg(target_os = "freebsd")]
#[derive(Debug)]
pub struct NmountError {
errno: Error,
errmsg: Option<String>,
}
#[cfg(target_os = "freebsd")]
impl NmountError {
/// Returns the additional error string sometimes generated by `nmount(2)`.
pub fn errmsg(&self) -> Option<&str> {
self.errmsg.as_deref()
}
/// Returns the inner [`Error`]
pub const fn error(&self) -> Error {
self.errno
}
fn new(error: Error, errmsg: Option<&CStr>) -> Self {
Self {
errno: error,
errmsg: errmsg.map(CStr::to_string_lossy).map(Cow::into_owned),
}
}
}
#[cfg(target_os = "freebsd")]
impl std::error::Error for NmountError {}
#[cfg(target_os = "freebsd")]
impl fmt::Display for NmountError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(errmsg) = &self.errmsg {
write!(f, "{:?}: {}: {}", self.errno, errmsg, self.errno.desc())
} else {
write!(f, "{:?}: {}", self.errno, self.errno.desc())
}
}
}
#[cfg(target_os = "freebsd")]
impl From<NmountError> for io::Error {
fn from(err: NmountError) -> Self {
err.errno.into()
}
}
/// Result type of [`Nmount::nmount`].
#[cfg(target_os = "freebsd")]
pub type NmountResult = std::result::Result<(), NmountError>;
/// Mount a FreeBSD file system.
///
/// The `nmount(2)` system call works similarly to the `mount(8)` program; it
/// takes its options as a series of name-value pairs. Most of the values are
/// strings, as are all of the names. The `Nmount` structure builds up an
/// argument list and then executes the syscall.
///
/// # Examples
///
/// To mount `target` onto `mountpoint` with `nullfs`:
/// ```
/// # use nix::unistd::Uid;
/// # use ::sysctl::{CtlValue, Sysctl};
/// # let ctl = ::sysctl::Ctl::new("vfs.usermount").unwrap();
/// # if !Uid::current().is_root() && CtlValue::Int(0) == ctl.value().unwrap() {
/// # return;
/// # };
/// use nix::mount::{MntFlags, Nmount, unmount};
/// use std::ffi::CString;
/// use tempfile::tempdir;
///
/// let mountpoint = tempdir().unwrap();
/// let target = tempdir().unwrap();
///
/// let fstype = CString::new("fstype").unwrap();
/// let nullfs = CString::new("nullfs").unwrap();
/// Nmount::new()
/// .str_opt(&fstype, &nullfs)
/// .str_opt_owned("fspath", mountpoint.path().to_str().unwrap())
/// .str_opt_owned("target", target.path().to_str().unwrap())
/// .nmount(MntFlags::empty()).unwrap();
///
/// unmount(mountpoint.path(), MntFlags::empty()).unwrap();
/// ```
///
/// # See Also
/// * [`nmount(2)`](https://www.freebsd.org/cgi/man.cgi?query=nmount)
/// * [`nullfs(5)`](https://www.freebsd.org/cgi/man.cgi?query=nullfs)
#[cfg(target_os = "freebsd")]
#[derive(Debug, Default)]
pub struct Nmount<'a> {
// n.b. notgull: In reality, this is a list that contains
// both mutable and immutable pointers.
// Be careful using this.
iov: Vec<libc::iovec>,
is_owned: Vec<bool>,
marker: PhantomData<&'a ()>,
}
#[cfg(target_os = "freebsd")]
impl<'a> Nmount<'a> {
/// Helper function to push a slice onto the `iov` array.
fn push_slice(&mut self, val: &'a [u8], is_owned: bool) {
self.iov.push(libc::iovec {
iov_base: val.as_ptr().cast_mut().cast(),
iov_len: val.len(),
});
self.is_owned.push(is_owned);
}
/// Helper function to push a pointer and its length onto the `iov` array.
fn push_pointer_and_length(
&mut self,
val: *const u8,
len: usize,
is_owned: bool,
) {
self.iov.push(libc::iovec {
iov_base: val as *mut _,
iov_len: len,
});
self.is_owned.push(is_owned);
}
/// Helper function to push a `nix` path as owned.
fn push_nix_path<P: ?Sized + NixPath>(&mut self, val: &P) {
val.with_nix_path(|s| {
let len = s.to_bytes_with_nul().len();
let ptr = s.to_owned().into_raw() as *const u8;
self.push_pointer_and_length(ptr, len, true);
})
.unwrap();
}
/// Add an opaque mount option.
///
/// Some file systems take binary-valued mount options. They can be set
/// with this method.
///
/// # Safety
///
/// Unsafe because it will cause `Nmount::nmount` to dereference a raw
/// pointer. The user is responsible for ensuring that `val` is valid and
/// its lifetime outlives `self`! An easy way to do that is to give the
/// value a larger scope than `name`
///
/// # Examples
/// ```
/// use libc::c_void;
/// use nix::mount::Nmount;
/// use std::ffi::CString;
/// use std::mem;
///
/// // Note that flags outlives name
/// let mut flags: u32 = 0xdeadbeef;
/// let name = CString::new("flags").unwrap();
/// let p = &mut flags as *mut u32 as *mut c_void;
/// let len = mem::size_of_val(&flags);
/// let mut nmount = Nmount::new();
/// unsafe { nmount.mut_ptr_opt(&name, p, len) };
/// ```
pub unsafe fn mut_ptr_opt(
&mut self,
name: &'a CStr,
val: *mut c_void,
len: usize,
) -> &mut Self {
self.push_slice(name.to_bytes_with_nul(), false);
self.push_pointer_and_length(val.cast(), len, false);
self
}
/// Add a mount option that does not take a value.
///
/// # Examples
/// ```
/// use nix::mount::Nmount;
/// use std::ffi::CString;
///
/// let read_only = CString::new("ro").unwrap();
/// Nmount::new()
/// .null_opt(&read_only);
/// ```
pub fn null_opt(&mut self, name: &'a CStr) -> &mut Self {
self.push_slice(name.to_bytes_with_nul(), false);
self.push_slice(&[], false);
self
}
/// Add a mount option that does not take a value, but whose name must be
/// owned.
///
///
/// This has higher runtime cost than [`Nmount::null_opt`], but is useful
/// when the name's lifetime doesn't outlive the `Nmount`, or it's a
/// different string type than `CStr`.
///
/// # Examples
/// ```
/// use nix::mount::Nmount;
///
/// let read_only = "ro";
/// let mut nmount: Nmount<'static> = Nmount::new();
/// nmount.null_opt_owned(read_only);
/// ```
pub fn null_opt_owned<P: ?Sized + NixPath>(
&mut self,
name: &P,
) -> &mut Self {
self.push_nix_path(name);
self.push_slice(&[], false);
self
}
/// Add a mount option as a [`CStr`].
///
/// # Examples
/// ```
/// use nix::mount::Nmount;
/// use std::ffi::CString;
///
/// let fstype = CString::new("fstype").unwrap();
/// let nullfs = CString::new("nullfs").unwrap();
/// Nmount::new()
/// .str_opt(&fstype, &nullfs);
/// ```
pub fn str_opt(&mut self, name: &'a CStr, val: &'a CStr) -> &mut Self {
self.push_slice(name.to_bytes_with_nul(), false);
self.push_slice(val.to_bytes_with_nul(), false);
self
}
/// Add a mount option as an owned string.
///
/// This has higher runtime cost than [`Nmount::str_opt`], but is useful
/// when the value's lifetime doesn't outlive the `Nmount`, or it's a
/// different string type than `CStr`.
///
/// # Examples
/// ```
/// use nix::mount::Nmount;
/// use std::path::Path;
///
/// let mountpoint = Path::new("/mnt");
/// Nmount::new()
/// .str_opt_owned("fspath", mountpoint.to_str().unwrap());
/// ```
pub fn str_opt_owned<P1, P2>(&mut self, name: &P1, val: &P2) -> &mut Self
where
P1: ?Sized + NixPath,
P2: ?Sized + NixPath,
{
self.push_nix_path(name);
self.push_nix_path(val);
self
}
/// Create a new `Nmount` struct with no options
pub fn new() -> Self {
Self::default()
}
/// Actually mount the file system.
pub fn nmount(&mut self, flags: MntFlags) -> NmountResult {
const ERRMSG_NAME: &[u8] = b"errmsg\0";
let mut errmsg = vec![0u8; 255];
// nmount can return extra error information via a "errmsg" return
// argument.
self.push_slice(ERRMSG_NAME, false);
// SAFETY: we are pushing a mutable iovec here, so we can't use
// the above method
self.iov.push(libc::iovec {
iov_base: errmsg.as_mut_ptr().cast(),
iov_len: errmsg.len(),
});
let niov = self.iov.len() as c_uint;
let iovp = self.iov.as_mut_ptr();
let res = unsafe { libc::nmount(iovp, niov, flags.bits()) };
match Errno::result(res) {
Ok(_) => Ok(()),
Err(error) => {
let errmsg = if errmsg[0] == 0 {
None
} else {
CStr::from_bytes_until_nul(&errmsg[..]).ok()
};
Err(NmountError::new(error, errmsg))
}
}
}
}
#[cfg(target_os = "freebsd")]
impl Drop for Nmount<'_> {
fn drop(&mut self) {
for (iov, is_owned) in self.iov.iter().zip(self.is_owned.iter()) {
if *is_owned {
// Free the owned string. Safe because we recorded ownership,
// and Nmount does not implement Clone.
unsafe {
drop(CString::from_raw(iov.iov_base as *mut c_char));
}
}
}
}
}
/// Unmount the file system mounted at `mountpoint`.
///
/// Useful flags include
/// * `MNT_FORCE` - Unmount even if still in use.
#[cfg_attr(
target_os = "freebsd",
doc = "
* `MNT_BYFSID` - `mountpoint` is not a path, but a file system ID
encoded as `FSID:val0:val1`, where `val0` and `val1`
are the contents of the `fsid_t val[]` array in decimal.
The file system that has the specified file system ID
will be unmounted. See
[`statfs`](crate::sys::statfs::statfs) to determine the
`fsid`.
"
)]
pub fn unmount<P>(mountpoint: &P, flags: MntFlags) -> Result<()>
where
P: ?Sized + NixPath,
{
let res = mountpoint.with_nix_path(|cstr| unsafe {
libc::unmount(cstr.as_ptr(), flags.bits())
})?;
Errno::result(res).map(drop)
}

152
vendor/nix/src/mount/linux.rs vendored Normal file
View File

@@ -0,0 +1,152 @@
use crate::errno::Errno;
use crate::{NixPath, Result};
use libc::{self, c_int, c_ulong};
libc_bitflags!(
/// Used with [`mount`].
pub struct MsFlags: c_ulong {
/// Mount read-only
MS_RDONLY;
/// Ignore suid and sgid bits
MS_NOSUID;
/// Disallow access to device special files
MS_NODEV;
/// Disallow program execution
MS_NOEXEC;
/// Writes are synced at once
MS_SYNCHRONOUS;
/// Alter flags of a mounted FS
MS_REMOUNT;
/// Allow mandatory locks on a FS
MS_MANDLOCK;
/// Directory modifications are synchronous
MS_DIRSYNC;
/// Do not update access times
MS_NOATIME;
/// Do not update directory access times
MS_NODIRATIME;
/// Linux 2.4.0 - Bind directory at different place
MS_BIND;
/// Move an existing mount to a new location
MS_MOVE;
/// Used to create a recursive bind mount.
MS_REC;
/// Suppress the display of certain kernel warning messages.
MS_SILENT;
/// VFS does not apply the umask
MS_POSIXACL;
/// The resulting mount cannot subsequently be bind mounted.
MS_UNBINDABLE;
/// Make this mount point private.
MS_PRIVATE;
/// If this is a shared mount point that is a member of a peer group
/// that contains other members, convert it to a slave mount.
MS_SLAVE;
/// Make this mount point shared.
MS_SHARED;
/// When a file on this filesystem is accessed, update the file's
/// last access time (atime) only if the current value of atime is
/// less than or equal to the file's last modification time (mtime) or
/// last status change time (ctime).
MS_RELATIME;
/// Mount request came from within the kernel
#[deprecated(since = "0.27.0", note = "Should only be used in-kernel")]
MS_KERNMOUNT;
/// Update inode I_version field
MS_I_VERSION;
/// Always update the last access time (atime) when files on this
/// filesystem are accessed.
MS_STRICTATIME;
/// Reduce on-disk updates of inode timestamps (atime, mtime, ctime) by
/// maintaining these changes only in memory.
MS_LAZYTIME;
#[deprecated(since = "0.27.0", note = "Should only be used in-kernel")]
#[allow(missing_docs)] // Not documented in Linux
MS_ACTIVE;
#[deprecated(since = "0.27.0", note = "Should only be used in-kernel")]
#[allow(missing_docs)] // Not documented in Linux
MS_NOUSER;
#[allow(missing_docs)] // Not documented in Linux; possibly kernel-only
MS_RMT_MASK;
#[allow(missing_docs)] // Not documented in Linux; possibly kernel-only
MS_MGC_VAL;
#[allow(missing_docs)] // Not documented in Linux; possibly kernel-only
MS_MGC_MSK;
}
);
libc_bitflags!(
/// Used with [`umount2].
pub struct MntFlags: c_int {
/// Attempt to unmount even if still in use, aborting pending requests.
MNT_FORCE;
/// Lazy unmount. Disconnect the file system immediately, but don't
/// actually unmount it until it ceases to be busy.
MNT_DETACH;
/// Mark the mount point as expired.
MNT_EXPIRE;
/// Don't dereference `target` if it is a symlink.
UMOUNT_NOFOLLOW;
}
);
/// Mount a file system.
///
/// # Arguments
/// - `source` - Specifies the file system. e.g. `/dev/sd0`.
/// - `target` - Specifies the destination. e.g. `/mnt`.
/// - `fstype` - The file system type, e.g. `ext4`.
/// - `flags` - Optional flags controlling the mount.
/// - `data` - Optional file system specific data.
///
/// # See Also
/// [`mount`](https://man7.org/linux/man-pages/man2/mount.2.html)
pub fn mount<
P1: ?Sized + NixPath,
P2: ?Sized + NixPath,
P3: ?Sized + NixPath,
P4: ?Sized + NixPath,
>(
source: Option<&P1>,
target: &P2,
fstype: Option<&P3>,
flags: MsFlags,
data: Option<&P4>,
) -> Result<()> {
let res = crate::with_opt_nix_path(source, |s| {
target.with_nix_path(|t| {
crate::with_opt_nix_path(fstype, |ty| {
crate::with_opt_nix_path(data, |d| unsafe {
libc::mount(
s,
t.as_ptr(),
ty,
flags.bits(),
d as *const libc::c_void,
)
})
})
})
})????;
Errno::result(res).map(drop)
}
/// Unmount the file system mounted at `target`.
pub fn umount<P: ?Sized + NixPath>(target: &P) -> Result<()> {
let res =
target.with_nix_path(|cstr| unsafe { libc::umount(cstr.as_ptr()) })?;
Errno::result(res).map(drop)
}
/// Unmount the file system mounted at `target`.
///
/// See also [`umount`](https://man7.org/linux/man-pages/man2/umount.2.html)
pub fn umount2<P: ?Sized + NixPath>(target: &P, flags: MntFlags) -> Result<()> {
let res = target.with_nix_path(|cstr| unsafe {
libc::umount2(cstr.as_ptr(), flags.bits())
})?;
Errno::result(res).map(drop)
}

18
vendor/nix/src/mount/mod.rs vendored Normal file
View File

@@ -0,0 +1,18 @@
//! Mount file systems
#[cfg(linux_android)]
mod linux;
#[cfg(linux_android)]
pub use self::linux::*;
#[cfg(bsd_without_apple)]
mod bsd_without_apple;
#[cfg(bsd_without_apple)]
pub use self::bsd_without_apple::*;
#[cfg(apple_targets)]
mod apple;
#[cfg(apple_targets)]
pub use self::apple::*;

353
vendor/nix/src/mqueue.rs vendored Normal file
View File

@@ -0,0 +1,353 @@
//! Posix Message Queue functions
//!
//! # Example
//!
// no_run because a kernel module may be required.
//! ```no_run
//! # use std::ffi::CString;
//! # use nix::mqueue::*;
//! use nix::sys::stat::Mode;
//!
//! const MSG_SIZE: mq_attr_member_t = 32;
//! let mq_name= "/a_nix_test_queue";
//!
//! let oflag0 = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
//! let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
//! let mqd0 = mq_open(mq_name, oflag0, mode, None).unwrap();
//! let msg_to_send = b"msg_1";
//! mq_send(&mqd0, msg_to_send, 1).unwrap();
//!
//! let oflag1 = MQ_OFlag::O_CREAT | MQ_OFlag::O_RDONLY;
//! let mqd1 = mq_open(mq_name, oflag1, mode, None).unwrap();
//! let mut buf = [0u8; 32];
//! let mut prio = 0u32;
//! let len = mq_receive(&mqd1, &mut buf, &mut prio).unwrap();
//! assert_eq!(prio, 1);
//! assert_eq!(msg_to_send, &buf[0..len]);
//!
//! mq_close(mqd1).unwrap();
//! mq_close(mqd0).unwrap();
//! ```
//! [Further reading and details on the C API](https://man7.org/linux/man-pages/man7/mq_overview.7.html)
use crate::errno::Errno;
use crate::NixPath;
use crate::Result;
use crate::sys::stat::Mode;
use libc::{self, mqd_t, size_t};
use std::mem;
#[cfg(any(
target_os = "linux",
target_os = "netbsd",
target_os = "dragonfly"
))]
use std::os::unix::io::{
AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd,
};
libc_bitflags! {
/// Used with [`mq_open`].
pub struct MQ_OFlag: libc::c_int {
/// Open the message queue for receiving messages.
O_RDONLY;
/// Open the queue for sending messages.
O_WRONLY;
/// Open the queue for both receiving and sending messages
O_RDWR;
/// Create a message queue.
O_CREAT;
/// If set along with `O_CREAT`, `mq_open` will fail if the message
/// queue name exists.
O_EXCL;
/// `mq_send` and `mq_receive` should fail with `EAGAIN` rather than
/// wait for resources that are not currently available.
O_NONBLOCK;
/// Set the close-on-exec flag for the message queue descriptor.
O_CLOEXEC;
}
}
/// A message-queue attribute, optionally used with [`mq_setattr`] and
/// [`mq_getattr`] and optionally [`mq_open`],
#[repr(C)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct MqAttr {
mq_attr: libc::mq_attr,
}
/// Identifies an open POSIX Message Queue
// A safer wrapper around libc::mqd_t, which is a pointer on some platforms
// Deliberately is not Clone to prevent use-after-close scenarios
#[repr(transparent)]
#[derive(Debug)]
#[allow(missing_copy_implementations)]
pub struct MqdT(mqd_t);
// x32 compatibility
// See https://sourceware.org/bugzilla/show_bug.cgi?id=21279
/// Size of a message queue attribute member
#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
pub type mq_attr_member_t = i64;
/// Size of a message queue attribute member
#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
pub type mq_attr_member_t = libc::c_long;
impl MqAttr {
/// Create a new message queue attribute
///
/// # Arguments
///
/// - `mq_flags`: Either `0` or `O_NONBLOCK`.
/// - `mq_maxmsg`: Maximum number of messages on the queue.
/// - `mq_msgsize`: Maximum message size in bytes.
/// - `mq_curmsgs`: Number of messages currently in the queue.
pub fn new(
mq_flags: mq_attr_member_t,
mq_maxmsg: mq_attr_member_t,
mq_msgsize: mq_attr_member_t,
mq_curmsgs: mq_attr_member_t,
) -> MqAttr {
let mut attr = mem::MaybeUninit::<libc::mq_attr>::uninit();
unsafe {
let p = attr.as_mut_ptr();
(*p).mq_flags = mq_flags;
(*p).mq_maxmsg = mq_maxmsg;
(*p).mq_msgsize = mq_msgsize;
(*p).mq_curmsgs = mq_curmsgs;
MqAttr {
mq_attr: attr.assume_init(),
}
}
}
/// The current flags, either `0` or `O_NONBLOCK`.
pub const fn flags(&self) -> mq_attr_member_t {
self.mq_attr.mq_flags
}
/// The max number of messages that can be held by the queue
pub const fn maxmsg(&self) -> mq_attr_member_t {
self.mq_attr.mq_maxmsg
}
/// The maximum size of each message (in bytes)
pub const fn msgsize(&self) -> mq_attr_member_t {
self.mq_attr.mq_msgsize
}
/// The number of messages currently held in the queue
pub const fn curmsgs(&self) -> mq_attr_member_t {
self.mq_attr.mq_curmsgs
}
}
/// Open a message queue
///
/// See also [`mq_open(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_open.html)
// The mode.bits() cast is only lossless on some OSes
#[allow(clippy::cast_lossless)]
pub fn mq_open<P>(
name: &P,
oflag: MQ_OFlag,
mode: Mode,
attr: Option<&MqAttr>,
) -> Result<MqdT>
where
P: ?Sized + NixPath,
{
let res = name.with_nix_path(|cstr| match attr {
Some(mq_attr) => unsafe {
libc::mq_open(
cstr.as_ptr(),
oflag.bits(),
mode.bits() as libc::c_int,
&mq_attr.mq_attr as *const libc::mq_attr,
)
},
None => unsafe { libc::mq_open(cstr.as_ptr(), oflag.bits()) },
})?;
Errno::result(res).map(MqdT)
}
/// Remove a message queue
///
/// See also [`mq_unlink(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_unlink.html)
pub fn mq_unlink<P>(name: &P) -> Result<()>
where
P: ?Sized + NixPath,
{
let res =
name.with_nix_path(|cstr| unsafe { libc::mq_unlink(cstr.as_ptr()) })?;
Errno::result(res).map(drop)
}
/// Close a message queue
///
/// See also [`mq_close(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_close.html)
pub fn mq_close(mqdes: MqdT) -> Result<()> {
let res = unsafe { libc::mq_close(mqdes.0) };
Errno::result(res).map(drop)
}
/// Receive a message from a message queue
///
/// See also [`mq_receive(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_receive.html)
pub fn mq_receive(
mqdes: &MqdT,
message: &mut [u8],
msg_prio: &mut u32,
) -> Result<usize> {
let len = message.len() as size_t;
let res = unsafe {
libc::mq_receive(
mqdes.0,
message.as_mut_ptr().cast(),
len,
msg_prio as *mut u32,
)
};
Errno::result(res).map(|r| r as usize)
}
feature! {
#![feature = "time"]
use crate::sys::time::TimeSpec;
/// Receive a message from a message queue with a timeout
///
/// See also ['mq_timedreceive(2)'](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_receive.html)
pub fn mq_timedreceive(
mqdes: &MqdT,
message: &mut [u8],
msg_prio: &mut u32,
abstime: &TimeSpec,
) -> Result<usize> {
let len = message.len() as size_t;
let res = unsafe {
libc::mq_timedreceive(
mqdes.0,
message.as_mut_ptr().cast(),
len,
msg_prio as *mut u32,
abstime.as_ref(),
)
};
Errno::result(res).map(|r| r as usize)
}
}
/// Send a message to a message queue
///
/// See also [`mq_send(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html)
pub fn mq_send(mqdes: &MqdT, message: &[u8], msq_prio: u32) -> Result<()> {
let res = unsafe {
libc::mq_send(mqdes.0, message.as_ptr().cast(), message.len(), msq_prio)
};
Errno::result(res).map(drop)
}
/// Get message queue attributes
///
/// See also [`mq_getattr(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_getattr.html)
pub fn mq_getattr(mqd: &MqdT) -> Result<MqAttr> {
let mut attr = mem::MaybeUninit::<libc::mq_attr>::uninit();
let res = unsafe { libc::mq_getattr(mqd.0, attr.as_mut_ptr()) };
Errno::result(res).map(|_| unsafe {
MqAttr {
mq_attr: attr.assume_init(),
}
})
}
/// Set the attributes of the message queue. Only `O_NONBLOCK` can be set,
/// everything else will be ignored. Returns the old attributes.
///
/// It is recommend to use the `mq_set_nonblock()` and `mq_remove_nonblock()`
/// convenience functions as they are easier to use.
///
/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_setattr.html)
pub fn mq_setattr(mqd: &MqdT, newattr: &MqAttr) -> Result<MqAttr> {
let mut attr = mem::MaybeUninit::<libc::mq_attr>::uninit();
let res = unsafe {
libc::mq_setattr(
mqd.0,
&newattr.mq_attr as *const libc::mq_attr,
attr.as_mut_ptr(),
)
};
Errno::result(res).map(|_| unsafe {
MqAttr {
mq_attr: attr.assume_init(),
}
})
}
/// Convenience function.
/// Sets the `O_NONBLOCK` attribute for a given message queue descriptor
/// Returns the old attributes
#[allow(clippy::useless_conversion)] // Not useless on all OSes
pub fn mq_set_nonblock(mqd: &MqdT) -> Result<MqAttr> {
let oldattr = mq_getattr(mqd)?;
let newattr = MqAttr::new(
mq_attr_member_t::from(MQ_OFlag::O_NONBLOCK.bits()),
oldattr.mq_attr.mq_maxmsg,
oldattr.mq_attr.mq_msgsize,
oldattr.mq_attr.mq_curmsgs,
);
mq_setattr(mqd, &newattr)
}
/// Convenience function.
/// Removes `O_NONBLOCK` attribute for a given message queue descriptor
/// Returns the old attributes
pub fn mq_remove_nonblock(mqd: &MqdT) -> Result<MqAttr> {
let oldattr = mq_getattr(mqd)?;
let newattr = MqAttr::new(
0,
oldattr.mq_attr.mq_maxmsg,
oldattr.mq_attr.mq_msgsize,
oldattr.mq_attr.mq_curmsgs,
);
mq_setattr(mqd, &newattr)
}
#[cfg(any(target_os = "linux", target_os = "netbsd", target_os = "dragonfly"))]
impl AsFd for MqdT {
/// Borrow the underlying message queue descriptor.
fn as_fd(&self) -> BorrowedFd {
// SAFETY: [MqdT] will only contain a valid fd by construction.
unsafe { BorrowedFd::borrow_raw(self.0) }
}
}
#[cfg(any(target_os = "linux", target_os = "netbsd", target_os = "dragonfly"))]
impl AsRawFd for MqdT {
/// Return the underlying message queue descriptor.
///
/// Returned descriptor is a "shallow copy" of the descriptor, so it refers
/// to the same underlying kernel object as `self`.
fn as_raw_fd(&self) -> RawFd {
self.0
}
}
#[cfg(any(target_os = "linux", target_os = "netbsd", target_os = "dragonfly"))]
impl FromRawFd for MqdT {
/// Construct an [MqdT] from [RawFd].
///
/// # Safety
/// The `fd` given must be a valid and open file descriptor for a message
/// queue.
unsafe fn from_raw_fd(fd: RawFd) -> MqdT {
MqdT(fd)
}
}
#[cfg(any(target_os = "linux", target_os = "netbsd", target_os = "dragonfly"))]
impl IntoRawFd for MqdT {
/// Consume this [MqdT] and return a [RawFd].
fn into_raw_fd(self) -> RawFd {
self.0
}
}

404
vendor/nix/src/net/if_.rs vendored Normal file
View File

@@ -0,0 +1,404 @@
//! Network interface name resolution.
//!
//! Uses Linux and/or POSIX functions to resolve interface names like "eth0"
//! or "socan1" into device numbers.
use std::{ffi::{CStr, CString}, fmt};
use crate::{errno::Errno, Error, NixPath, Result};
use libc::{c_uint, IF_NAMESIZE};
#[cfg(not(solarish))]
/// type alias for InterfaceFlags
pub type IflagsType = libc::c_int;
#[cfg(solarish)]
/// type alias for InterfaceFlags
pub type IflagsType = libc::c_longlong;
/// Resolve an interface into an interface number.
pub fn if_nametoindex<P: ?Sized + NixPath>(name: &P) -> Result<c_uint> {
let if_index = name
.with_nix_path(|name| unsafe { libc::if_nametoindex(name.as_ptr()) })?;
if if_index == 0 {
Err(Error::last())
} else {
Ok(if_index)
}
}
/// Resolve an interface number into an interface.
pub fn if_indextoname(index: c_uint) -> Result<CString> {
// We need to allocate this anyway, so doing it directly is faster.
let mut buf = vec![0u8; IF_NAMESIZE];
let return_buf = unsafe {
libc::if_indextoname(index, buf.as_mut_ptr().cast())
};
Errno::result(return_buf.cast())?;
Ok(CStr::from_bytes_until_nul(buf.as_slice()).unwrap().to_owned())
}
libc_bitflags!(
/// Standard interface flags, used by `getifaddrs`
pub struct InterfaceFlags: IflagsType {
/// Interface is running. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
IFF_UP as IflagsType;
/// Valid broadcast address set. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
IFF_BROADCAST as IflagsType;
/// Internal debugging flag. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
#[cfg(not(any(target_os = "haiku", target_os = "cygwin")))]
IFF_DEBUG as IflagsType;
/// Interface is a loopback interface. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
IFF_LOOPBACK as IflagsType;
/// Interface is a point-to-point link. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
IFF_POINTOPOINT as IflagsType;
/// Avoid use of trailers. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
#[cfg(any(
linux_android,
solarish,
apple_targets,
target_os = "fuchsia",
target_os = "netbsd",
target_os = "cygwin"))]
IFF_NOTRAILERS as IflagsType;
/// Interface manages own routes.
#[cfg(any(target_os = "dragonfly"))]
IFF_SMART as IflagsType;
/// Resources allocated. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
#[cfg(any(
linux_android,
bsd,
solarish,
target_os = "fuchsia",
target_os = "cygwin"))]
IFF_RUNNING as IflagsType;
/// No arp protocol, L2 destination address not set. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
IFF_NOARP as IflagsType;
/// Interface is in promiscuous mode. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
IFF_PROMISC as IflagsType;
/// Receive all multicast packets. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
#[cfg(not(target_os = "cygwin"))]
IFF_ALLMULTI as IflagsType;
/// Master of a load balancing bundle. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
#[cfg(any(linux_android, target_os = "fuchsia"))]
IFF_MASTER;
/// transmission in progress, tx hardware queue is full
#[cfg(any(target_os = "freebsd", apple_targets, netbsdlike))]
IFF_OACTIVE;
/// Protocol code on board.
#[cfg(solarish)]
IFF_INTELLIGENT as IflagsType;
/// Slave of a load balancing bundle. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
#[cfg(any(linux_android, target_os = "fuchsia"))]
IFF_SLAVE;
/// Can't hear own transmissions.
#[cfg(bsd)]
IFF_SIMPLEX;
/// Supports multicast. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
IFF_MULTICAST as IflagsType;
/// Per link layer defined bit.
#[cfg(bsd)]
IFF_LINK0;
/// Multicast using broadcast.
#[cfg(solarish)]
IFF_MULTI_BCAST as IflagsType;
/// Is able to select media type via ifmap. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
#[cfg(any(linux_android, target_os = "fuchsia"))]
IFF_PORTSEL;
/// Per link layer defined bit.
#[cfg(bsd)]
IFF_LINK1;
/// Non-unique address.
#[cfg(solarish)]
IFF_UNNUMBERED as IflagsType;
/// Auto media selection active. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
#[cfg(any(linux_android, target_os = "fuchsia"))]
IFF_AUTOMEDIA;
/// Per link layer defined bit.
#[cfg(bsd)]
IFF_LINK2;
/// Use alternate physical connection.
#[cfg(any(freebsdlike, apple_targets))]
IFF_ALTPHYS;
/// DHCP controls interface.
#[cfg(solarish)]
IFF_DHCPRUNNING as IflagsType;
/// The addresses are lost when the interface goes down. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
#[cfg(any(linux_android, target_os = "fuchsia"))]
IFF_DYNAMIC;
/// Do not advertise.
#[cfg(solarish)]
IFF_PRIVATE as IflagsType;
/// Driver signals L1 up. Volatile.
#[cfg(any(target_os = "fuchsia", target_os = "linux", target_os = "cygwin"))]
IFF_LOWER_UP;
/// Interface is in polling mode.
#[cfg(any(target_os = "dragonfly"))]
IFF_POLLING_COMPAT;
/// Unconfigurable using ioctl(2).
#[cfg(any(target_os = "freebsd"))]
IFF_CANTCONFIG;
/// Do not transmit packets.
#[cfg(solarish)]
IFF_NOXMIT as IflagsType;
/// Driver signals dormant. Volatile.
#[cfg(any(target_os = "fuchsia", target_os = "linux", target_os = "cygwin"))]
IFF_DORMANT;
/// User-requested promisc mode.
#[cfg(freebsdlike)]
IFF_PPROMISC;
/// Just on-link subnet.
#[cfg(solarish)]
IFF_NOLOCAL as IflagsType;
/// Echo sent packets. Volatile.
#[cfg(any(target_os = "fuchsia", target_os = "linux"))]
IFF_ECHO;
/// User-requested monitor mode.
#[cfg(freebsdlike)]
IFF_MONITOR;
/// Address is deprecated.
#[cfg(solarish)]
IFF_DEPRECATED as IflagsType;
/// Static ARP.
#[cfg(freebsdlike)]
IFF_STATICARP;
/// Address from stateless addrconf.
#[cfg(solarish)]
IFF_ADDRCONF as IflagsType;
/// Interface is in polling mode.
#[cfg(any(target_os = "dragonfly"))]
IFF_NPOLLING;
/// Router on interface.
#[cfg(solarish)]
IFF_ROUTER as IflagsType;
/// Interface is in polling mode.
#[cfg(any(target_os = "dragonfly"))]
IFF_IDIRECT;
/// Interface is winding down
#[cfg(any(target_os = "freebsd"))]
IFF_DYING;
/// No NUD on interface.
#[cfg(solarish)]
IFF_NONUD as IflagsType;
/// Interface is being renamed
#[cfg(any(target_os = "freebsd"))]
IFF_RENAMING;
/// Anycast address.
#[cfg(solarish)]
IFF_ANYCAST as IflagsType;
/// Don't exchange routing info.
#[cfg(solarish)]
IFF_NORTEXCH as IflagsType;
/// Do not provide packet information
#[cfg(any(linux_android, target_os = "fuchsia"))]
IFF_NO_PI as IflagsType;
/// TUN device (no Ethernet headers)
#[cfg(any(linux_android, target_os = "fuchsia"))]
IFF_TUN as IflagsType;
/// TAP device
#[cfg(any(linux_android, target_os = "fuchsia"))]
IFF_TAP as IflagsType;
/// IPv4 interface.
#[cfg(solarish)]
IFF_IPV4 as IflagsType;
/// IPv6 interface.
#[cfg(solarish)]
IFF_IPV6 as IflagsType;
/// in.mpathd test address
#[cfg(solarish)]
IFF_NOFAILOVER as IflagsType;
/// Interface has failed
#[cfg(solarish)]
IFF_FAILED as IflagsType;
/// Interface is a hot-spare
#[cfg(solarish)]
IFF_STANDBY as IflagsType;
/// Functioning but not used
#[cfg(solarish)]
IFF_INACTIVE as IflagsType;
/// Interface is offline
#[cfg(solarish)]
IFF_OFFLINE as IflagsType;
/// Has CoS marking supported
#[cfg(solarish)]
IFF_COS_ENABLED as IflagsType;
/// Prefer as source addr
#[cfg(solarish)]
IFF_PREFERRED as IflagsType;
/// RFC3041
#[cfg(solarish)]
IFF_TEMPORARY as IflagsType;
/// MTU set
#[cfg(solarish)]
IFF_FIXEDMTU as IflagsType;
/// Cannot send/receive packets
#[cfg(solarish)]
IFF_VIRTUAL as IflagsType;
/// Local address in use
#[cfg(solarish)]
IFF_DUPLICATE as IflagsType;
/// IPMP IP interface
#[cfg(solarish)]
IFF_IPMP as IflagsType;
}
);
impl fmt::Display for InterfaceFlags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
bitflags::parser::to_writer(self, f)
}
}
#[cfg(any(
bsd,
target_os = "fuchsia",
target_os = "linux",
solarish,
))]
mod if_nameindex {
use super::*;
use std::ffi::CStr;
use std::fmt;
use std::marker::PhantomData;
use std::ptr::NonNull;
/// A network interface. Has a name like "eth0" or "wlp4s0" or "wlan0", as well as an index
/// (1, 2, 3, etc) that identifies it in the OS's networking stack.
#[allow(missing_copy_implementations)]
#[repr(transparent)]
pub struct Interface(libc::if_nameindex);
impl Interface {
/// Obtain the index of this interface.
pub fn index(&self) -> c_uint {
self.0.if_index
}
/// Obtain the name of this interface.
pub fn name(&self) -> &CStr {
unsafe { CStr::from_ptr(self.0.if_name) }
}
}
impl fmt::Debug for Interface {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Interface")
.field("index", &self.index())
.field("name", &self.name())
.finish()
}
}
/// A list of the network interfaces available on this system. Obtained from [`if_nameindex()`].
#[repr(transparent)]
pub struct Interfaces {
ptr: NonNull<libc::if_nameindex>,
}
impl Interfaces {
/// Iterate over the interfaces in this list.
#[inline]
pub fn iter(&self) -> InterfacesIter<'_> {
self.into_iter()
}
/// Convert this to a slice of interfaces. Note that the underlying interfaces list is
/// null-terminated, so calling this calculates the length. If random access isn't needed,
/// [`Interfaces::iter()`] should be used instead.
pub fn to_slice(&self) -> &[Interface] {
let ifs = self.ptr.as_ptr().cast();
let len = self.iter().count();
unsafe { std::slice::from_raw_parts(ifs, len) }
}
}
impl Drop for Interfaces {
fn drop(&mut self) {
unsafe { libc::if_freenameindex(self.ptr.as_ptr()) };
}
}
impl fmt::Debug for Interfaces {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.to_slice().fmt(f)
}
}
impl<'a> IntoIterator for &'a Interfaces {
type IntoIter = InterfacesIter<'a>;
type Item = &'a Interface;
#[inline]
fn into_iter(self) -> Self::IntoIter {
InterfacesIter {
ptr: self.ptr.as_ptr(),
_marker: PhantomData,
}
}
}
/// An iterator over the interfaces in an [`Interfaces`].
#[derive(Debug)]
pub struct InterfacesIter<'a> {
ptr: *const libc::if_nameindex,
_marker: PhantomData<&'a Interfaces>,
}
impl<'a> Iterator for InterfacesIter<'a> {
type Item = &'a Interface;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
unsafe {
if (*self.ptr).if_index == 0 {
None
} else {
let ret = &*(self.ptr as *const Interface);
self.ptr = self.ptr.add(1);
Some(ret)
}
}
}
}
/// Retrieve a list of the network interfaces available on the local system.
///
/// ```
/// let interfaces = nix::net::if_::if_nameindex().unwrap();
/// for iface in &interfaces {
/// println!("Interface #{} is called {}", iface.index(), iface.name().to_string_lossy());
/// }
/// ```
pub fn if_nameindex() -> Result<Interfaces> {
unsafe {
let ifs = libc::if_nameindex();
let ptr = NonNull::new(ifs).ok_or_else(Error::last)?;
Ok(Interfaces { ptr })
}
}
}
#[cfg(any(
bsd,
target_os = "fuchsia",
target_os = "linux",
solarish,
))]
pub use if_nameindex::*;

4
vendor/nix/src/net/mod.rs vendored Normal file
View File

@@ -0,0 +1,4 @@
//! Functionality involving network interfaces
// To avoid clashing with the keyword "if", we use "if_" as the module name.
// The original header is called "net/if.h".
pub mod if_;

269
vendor/nix/src/poll.rs vendored Normal file
View File

@@ -0,0 +1,269 @@
//! Wait for events to trigger on specific file descriptors
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd};
use crate::errno::Errno;
pub use crate::poll_timeout::{PollTimeout, PollTimeoutTryFromError};
use crate::Result;
/// This is a wrapper around `libc::pollfd`.
///
/// It's meant to be used as an argument to the [`poll`](fn.poll.html) and
/// [`ppoll`](fn.ppoll.html) functions to specify the events of interest
/// for a specific file descriptor.
///
/// After a call to `poll` or `ppoll`, the events that occurred can be retrieved by calling
/// [`revents()`](#method.revents) on the `PollFd` object from the array passed to `poll`.
#[repr(transparent)]
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct PollFd<'fd> {
pollfd: libc::pollfd,
_fd: std::marker::PhantomData<BorrowedFd<'fd>>,
}
impl<'fd> PollFd<'fd> {
/// Creates a new `PollFd` specifying the events of interest
/// for a given file descriptor.
///
/// # Examples
/// ```no_run
/// # use std::os::unix::io::{AsFd, AsRawFd, FromRawFd};
/// # use nix::{
/// # poll::{PollTimeout, PollFd, PollFlags, poll},
/// # unistd::{pipe, read}
/// # };
/// let (r, w) = pipe().unwrap();
/// let pfd = PollFd::new(r.as_fd(), PollFlags::POLLIN);
/// ```
/// These are placed in an array and passed to [`poll`] or [`ppoll`](fn.ppoll.html).
// Unlike I/O functions, constructors like this must take `BorrowedFd`
// instead of AsFd or &AsFd. Otherwise, an `OwnedFd` argument would be
// dropped at the end of the method, leaving the structure referencing a
// closed file descriptor. For example:
//
// ```rust
// let (r, _) = pipe().unwrap();
// let pollfd = PollFd::new(r, flag); // Drops the OwnedFd
// // Do something with `pollfd`, which uses the CLOSED fd.
// ```
pub fn new(fd: BorrowedFd<'fd>, events: PollFlags) -> PollFd<'fd> {
PollFd {
pollfd: libc::pollfd {
fd: fd.as_raw_fd(),
events: events.bits(),
revents: PollFlags::empty().bits(),
},
_fd: std::marker::PhantomData,
}
}
/// Returns the events that occurred in the last call to `poll` or `ppoll`. Will only return
/// `None` if the kernel provides status flags that Nix does not know about.
pub fn revents(&self) -> Option<PollFlags> {
PollFlags::from_bits(self.pollfd.revents)
}
/// Returns if any of the events of interest occured in the last call to `poll` or `ppoll`. Will
/// only return `None` if the kernel provides status flags that Nix does not know about.
///
/// Equivalent to `x.revents()? != PollFlags::empty()`.
///
/// This is marginally more efficient than [`PollFd::all`].
pub fn any(&self) -> Option<bool> {
Some(self.revents()? != PollFlags::empty())
}
/// Returns if all the events of interest occured in the last call to `poll` or `ppoll`. Will
/// only return `None` if the kernel provides status flags that Nix does not know about.
///
/// Equivalent to `x.revents()? & x.events() == x.events()`.
///
/// This is marginally less efficient than [`PollFd::any`].
pub fn all(&self) -> Option<bool> {
Some(self.revents()? & self.events() == self.events())
}
/// The events of interest for this `PollFd`.
pub fn events(&self) -> PollFlags {
PollFlags::from_bits(self.pollfd.events).unwrap()
}
/// Modify the events of interest for this `PollFd`.
pub fn set_events(&mut self, events: PollFlags) {
self.pollfd.events = events.bits();
}
}
impl AsFd for PollFd<'_> {
fn as_fd(&self) -> BorrowedFd<'_> {
// Safety:
//
// BorrowedFd::borrow_raw(RawFd) requires that the raw fd being passed
// must remain open for the duration of the returned BorrowedFd, this is
// guaranteed as the returned BorrowedFd has the lifetime parameter same
// as `self`:
// "fn as_fd<'self>(&'self self) -> BorrowedFd<'self>"
// which means that `self` (PollFd) is guaranteed to outlive the returned
// BorrowedFd. (Lifetime: PollFd > BorrowedFd)
//
// And the lifetime parameter of PollFd::new(fd, ...) ensures that `fd`
// (an owned file descriptor) must outlive the returned PollFd:
// "pub fn new<Fd: AsFd>(fd: &'fd Fd, events: PollFlags) -> PollFd<'fd>"
// (Lifetime: Owned fd > PollFd)
//
// With two above relationships, we can conclude that the `Owned file
// descriptor` will outlive the returned BorrowedFd,
// (Lifetime: Owned fd > BorrowedFd)
// i.e., the raw fd being passed will remain valid for the lifetime of
// the returned BorrowedFd.
unsafe { BorrowedFd::borrow_raw(self.pollfd.fd) }
}
}
libc_bitflags! {
/// These flags define the different events that can be monitored by `poll` and `ppoll`
pub struct PollFlags: libc::c_short {
/// There is data to read.
POLLIN;
/// There is some exceptional condition on the file descriptor.
///
/// Possibilities include:
///
/// * There is out-of-band data on a TCP socket (see
/// [tcp(7)](https://man7.org/linux/man-pages/man7/tcp.7.html)).
/// * A pseudoterminal master in packet mode has seen a state
/// change on the slave (see
/// [ioctl_tty(2)](https://man7.org/linux/man-pages/man2/ioctl_tty.2.html)).
/// * A cgroup.events file has been modified (see
/// [cgroups(7)](https://man7.org/linux/man-pages/man7/cgroups.7.html)).
POLLPRI;
/// Writing is now possible, though a write larger that the
/// available space in a socket or pipe will still block (unless
/// `O_NONBLOCK` is set).
POLLOUT;
/// Equivalent to [`POLLIN`](constant.POLLIN.html)
#[cfg(not(target_os = "redox"))]
POLLRDNORM;
#[cfg(not(target_os = "redox"))]
/// Equivalent to [`POLLOUT`](constant.POLLOUT.html)
POLLWRNORM;
/// Priority band data can be read (generally unused on Linux).
#[cfg(not(target_os = "redox"))]
POLLRDBAND;
/// Priority data may be written.
#[cfg(not(target_os = "redox"))]
POLLWRBAND;
/// Error condition (only returned in
/// [`PollFd::revents`](struct.PollFd.html#method.revents);
/// ignored in [`PollFd::new`](struct.PollFd.html#method.new)).
/// This bit is also set for a file descriptor referring to the
/// write end of a pipe when the read end has been closed.
POLLERR;
/// Hang up (only returned in [`PollFd::revents`](struct.PollFd.html#method.revents);
/// ignored in [`PollFd::new`](struct.PollFd.html#method.new)).
/// Note that when reading from a channel such as a pipe or a stream
/// socket, this event merely indicates that the peer closed its
/// end of the channel. Subsequent reads from the channel will
/// return 0 (end of file) only after all outstanding data in the
/// channel has been consumed.
POLLHUP;
/// Invalid request: `fd` not open (only returned in
/// [`PollFd::revents`](struct.PollFd.html#method.revents);
/// ignored in [`PollFd::new`](struct.PollFd.html#method.new)).
POLLNVAL;
}
}
/// `poll` waits for one of a set of file descriptors to become ready to perform I/O.
/// ([`poll(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/poll.html))
///
/// `fds` contains all [`PollFd`](struct.PollFd.html) to poll.
/// The function will return as soon as any event occur for any of these `PollFd`s.
///
/// The `timeout` argument specifies the number of milliseconds that `poll()`
/// should block waiting for a file descriptor to become ready. The call
/// will block until either:
///
/// * a file descriptor becomes ready;
/// * the call is interrupted by a signal handler; or
/// * the timeout expires.
///
/// Note that the timeout interval will be rounded up to the system clock
/// granularity, and kernel scheduling delays mean that the blocking
/// interval may overrun by a small amount. Specifying a [`PollTimeout::NONE`]
/// in timeout means an infinite timeout. Specifying a timeout of
/// [`PollTimeout::ZERO`] causes `poll()` to return immediately, even if no file
/// descriptors are ready.
///
/// The return value contains the number of `fds` which have selected events ([`PollFd::revents`]).
///
/// # Examples
/// ```no_run
/// # use std::os::unix::io::{AsFd, AsRawFd, FromRawFd};
/// # use nix::{
/// # poll::{PollTimeout, PollFd, PollFlags, poll},
/// # unistd::{pipe, read}
/// # };
/// let (r0, w0) = pipe().unwrap();
/// let (r1, w1) = pipe().unwrap();
///
/// let mut pollfds = [
/// PollFd::new(r0.as_fd(), PollFlags::POLLIN),
/// PollFd::new(r1.as_fd(), PollFlags::POLLIN),
/// ];
///
/// let nready = poll(&mut pollfds, PollTimeout::NONE).unwrap();
/// assert!(nready >= 1); // Since there is no timeout
///
/// let mut buf = [0u8; 80];
/// if pollfds[0].any().unwrap_or_default() {
/// read(&r0, &mut buf[..]);
/// } else if pollfds[1].any().unwrap_or_default() {
/// read(&r1, &mut buf[..]);
/// }
/// ```
pub fn poll<T: Into<PollTimeout>>(
fds: &mut [PollFd],
timeout: T,
) -> Result<libc::c_int> {
let res = unsafe {
libc::poll(
fds.as_mut_ptr().cast(),
fds.len() as libc::nfds_t,
i32::from(timeout.into()),
)
};
Errno::result(res)
}
feature! {
#![feature = "signal"]
/// `ppoll()` allows an application to safely wait until either a file
/// descriptor becomes ready or until a signal is caught.
/// ([`poll(2)`](https://man7.org/linux/man-pages/man2/poll.2.html))
///
/// `ppoll` behaves like [`poll`], but let you specify what signals may interrupt it
/// with the `sigmask` argument. If you want `ppoll` to block indefinitely,
/// specify `None` as `timeout` (it is like `timeout = -1` for `poll`).
/// If `sigmask` is `None`, then no signal mask manipulation is performed,
/// so in that case `ppoll` differs from `poll` only in the precision of the
/// timeout argument.
///
#[cfg(any(linux_android, freebsdlike))]
pub fn ppoll(
fds: &mut [PollFd],
timeout: Option<crate::sys::time::TimeSpec>,
sigmask: Option<crate::sys::signal::SigSet>
) -> Result<libc::c_int>
{
let timeout = timeout.as_ref().map_or(core::ptr::null(), |r| r.as_ref());
let sigmask = sigmask.as_ref().map_or(core::ptr::null(), |r| r.as_ref());
let res = unsafe {
libc::ppoll(fds.as_mut_ptr().cast(),
fds.len() as libc::nfds_t,
timeout,
sigmask)
};
Errno::result(res)
}
}

224
vendor/nix/src/poll_timeout.rs vendored Normal file
View File

@@ -0,0 +1,224 @@
use std::time::Duration;
/// PollTimeout argument for polling.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
pub struct PollTimeout(i32);
impl PollTimeout {
/// Blocks indefinitely.
///
/// > Specifying a negative value in timeout means an infinite timeout.
pub const NONE: Self = Self(-1);
/// Returns immediately.
///
/// > Specifying a timeout of zero causes poll() to return immediately, even if no file
/// > descriptors are ready.
pub const ZERO: Self = Self(0);
/// Blocks for at most [`i32::MAX`] milliseconds.
pub const MAX: Self = Self(i32::MAX);
/// Returns if `self` equals [`PollTimeout::NONE`].
pub fn is_none(&self) -> bool {
// > Specifying a negative value in timeout means an infinite timeout.
*self <= Self::NONE
}
/// Returns if `self` does not equal [`PollTimeout::NONE`].
pub fn is_some(&self) -> bool {
!self.is_none()
}
/// Returns the timeout in milliseconds if there is some, otherwise returns `None`.
pub fn as_millis(&self) -> Option<u32> {
self.is_some().then_some(u32::try_from(self.0).unwrap())
}
/// Returns the timeout as a `Duration` if there is some, otherwise returns `None`.
pub fn duration(&self) -> Option<Duration> {
self.as_millis()
.map(|x| Duration::from_millis(u64::from(x)))
}
}
/// Error type for integer conversions into `PollTimeout`.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PollTimeoutTryFromError {
/// Passing a value less than -1 is invalid on some systems, see
/// <https://man.freebsd.org/cgi/man.cgi?poll#end>.
TooNegative,
/// Passing a value greater than `i32::MAX` is invalid.
TooPositive,
}
impl std::fmt::Display for PollTimeoutTryFromError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::TooNegative => write!(f, "Passed a negative timeout less than -1."),
Self::TooPositive => write!(f, "Passed a positive timeout greater than `i32::MAX` milliseconds.")
}
}
}
impl std::error::Error for PollTimeoutTryFromError {}
impl<T: Into<PollTimeout>> From<Option<T>> for PollTimeout {
fn from(x: Option<T>) -> Self {
x.map_or(Self::NONE, |x| x.into())
}
}
impl TryFrom<Duration> for PollTimeout {
type Error = PollTimeoutTryFromError;
fn try_from(x: Duration) -> std::result::Result<Self, Self::Error> {
Ok(Self(
i32::try_from(x.as_millis())
.map_err(|_| PollTimeoutTryFromError::TooPositive)?,
))
}
}
impl TryFrom<u128> for PollTimeout {
type Error = PollTimeoutTryFromError;
fn try_from(x: u128) -> std::result::Result<Self, Self::Error> {
Ok(Self(
i32::try_from(x)
.map_err(|_| PollTimeoutTryFromError::TooPositive)?,
))
}
}
impl TryFrom<u64> for PollTimeout {
type Error = PollTimeoutTryFromError;
fn try_from(x: u64) -> std::result::Result<Self, Self::Error> {
Ok(Self(
i32::try_from(x)
.map_err(|_| PollTimeoutTryFromError::TooPositive)?,
))
}
}
impl TryFrom<u32> for PollTimeout {
type Error = PollTimeoutTryFromError;
fn try_from(x: u32) -> std::result::Result<Self, Self::Error> {
Ok(Self(
i32::try_from(x)
.map_err(|_| PollTimeoutTryFromError::TooPositive)?,
))
}
}
impl From<u16> for PollTimeout {
fn from(x: u16) -> Self {
Self(i32::from(x))
}
}
impl From<u8> for PollTimeout {
fn from(x: u8) -> Self {
Self(i32::from(x))
}
}
impl TryFrom<i128> for PollTimeout {
type Error = PollTimeoutTryFromError;
fn try_from(x: i128) -> std::result::Result<Self, Self::Error> {
match x {
..=-2 => Err(PollTimeoutTryFromError::TooNegative),
-1.. => Ok(Self(
i32::try_from(x)
.map_err(|_| PollTimeoutTryFromError::TooPositive)?,
)),
}
}
}
impl TryFrom<i64> for PollTimeout {
type Error = PollTimeoutTryFromError;
fn try_from(x: i64) -> std::result::Result<Self, Self::Error> {
match x {
..=-2 => Err(PollTimeoutTryFromError::TooNegative),
-1.. => Ok(Self(
i32::try_from(x)
.map_err(|_| PollTimeoutTryFromError::TooPositive)?,
)),
}
}
}
impl TryFrom<i32> for PollTimeout {
type Error = PollTimeoutTryFromError;
fn try_from(x: i32) -> std::result::Result<Self, Self::Error> {
match x {
..=-2 => Err(PollTimeoutTryFromError::TooNegative),
-1.. => Ok(Self(x)),
}
}
}
impl TryFrom<i16> for PollTimeout {
type Error = PollTimeoutTryFromError;
fn try_from(x: i16) -> std::result::Result<Self, Self::Error> {
match x {
..=-2 => Err(PollTimeoutTryFromError::TooNegative),
-1.. => Ok(Self(i32::from(x))),
}
}
}
impl TryFrom<i8> for PollTimeout {
type Error = PollTimeoutTryFromError;
fn try_from(x: i8) -> std::result::Result<Self, Self::Error> {
match x {
..=-2 => Err(PollTimeoutTryFromError::TooNegative),
-1.. => Ok(Self(i32::from(x))),
}
}
}
impl TryFrom<PollTimeout> for Duration {
type Error = ();
fn try_from(x: PollTimeout) -> std::result::Result<Self, ()> {
x.duration().ok_or(())
}
}
impl TryFrom<PollTimeout> for u128 {
type Error = <Self as TryFrom<i32>>::Error;
fn try_from(x: PollTimeout) -> std::result::Result<Self, Self::Error> {
Self::try_from(x.0)
}
}
impl TryFrom<PollTimeout> for u64 {
type Error = <Self as TryFrom<i32>>::Error;
fn try_from(x: PollTimeout) -> std::result::Result<Self, Self::Error> {
Self::try_from(x.0)
}
}
impl TryFrom<PollTimeout> for u32 {
type Error = <Self as TryFrom<i32>>::Error;
fn try_from(x: PollTimeout) -> std::result::Result<Self, Self::Error> {
Self::try_from(x.0)
}
}
impl TryFrom<PollTimeout> for u16 {
type Error = <Self as TryFrom<i32>>::Error;
fn try_from(x: PollTimeout) -> std::result::Result<Self, Self::Error> {
Self::try_from(x.0)
}
}
impl TryFrom<PollTimeout> for u8 {
type Error = <Self as TryFrom<i32>>::Error;
fn try_from(x: PollTimeout) -> std::result::Result<Self, Self::Error> {
Self::try_from(x.0)
}
}
impl From<PollTimeout> for i128 {
fn from(x: PollTimeout) -> Self {
Self::from(x.0)
}
}
impl From<PollTimeout> for i64 {
fn from(x: PollTimeout) -> Self {
Self::from(x.0)
}
}
impl From<PollTimeout> for i32 {
fn from(x: PollTimeout) -> Self {
x.0
}
}
impl TryFrom<PollTimeout> for i16 {
type Error = <Self as TryFrom<i32>>::Error;
fn try_from(x: PollTimeout) -> std::result::Result<Self, Self::Error> {
Self::try_from(x.0)
}
}
impl TryFrom<PollTimeout> for i8 {
type Error = <Self as TryFrom<i32>>::Error;
fn try_from(x: PollTimeout) -> std::result::Result<Self, Self::Error> {
Self::try_from(x.0)
}
}

393
vendor/nix/src/pty.rs vendored Normal file
View File

@@ -0,0 +1,393 @@
//! Create master and slave virtual pseudo-terminals (PTYs)
pub use libc::pid_t as SessionId;
pub use libc::winsize as Winsize;
use std::ffi::CStr;
use std::io;
#[cfg(not(target_os = "aix"))]
use std::mem;
use std::os::unix::prelude::*;
use crate::errno::Errno;
#[cfg(not(target_os = "aix"))]
use crate::sys::termios::Termios;
#[cfg(all(feature = "process", not(target_os = "aix")))]
use crate::unistd::Pid;
use crate::{fcntl, unistd, Result};
/// Representation of a master/slave pty pair
///
/// This is returned by [`openpty`].
#[derive(Debug)]
pub struct OpenptyResult {
/// The master port in a virtual pty pair
pub master: OwnedFd,
/// The slave port in a virtual pty pair
pub slave: OwnedFd,
}
feature! {
#![feature = "process"]
/// A successful result of [`forkpty()`].
#[derive(Debug)]
pub enum ForkptyResult {
/// This is the parent process of the underlying fork.
Parent {
/// The PID of the fork's child process
child: Pid,
/// A file descriptor referring to master side of the pseudoterminal of
/// the child process.
master: OwnedFd,
},
/// This is the child process of the underlying fork.
Child,
}
}
/// Representation of the Master device in a master/slave pty pair
///
/// While this datatype is a thin wrapper around `OwnedFd`, it enforces that the available PTY
/// functions are given the correct file descriptor.
#[derive(Debug)]
pub struct PtyMaster(OwnedFd);
impl PtyMaster {
/// Constructs a `PytMaster` wrapping an existing `OwnedFd`.
///
/// # Safety
///
/// `OwnedFd` is a valid `PtyMaster`.
pub unsafe fn from_owned_fd(fd: OwnedFd) -> Self {
Self(fd)
}
}
impl AsRawFd for PtyMaster {
fn as_raw_fd(&self) -> RawFd {
self.0.as_raw_fd()
}
}
impl AsFd for PtyMaster {
fn as_fd(&self) -> BorrowedFd<'_> {
self.0.as_fd()
}
}
impl From<PtyMaster> for OwnedFd {
fn from(value: PtyMaster) -> Self {
value.0
}
}
impl IntoRawFd for PtyMaster {
fn into_raw_fd(self) -> RawFd {
let fd = self.0;
fd.into_raw_fd()
}
}
impl io::Read for PtyMaster {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
unistd::read(&self.0, buf).map_err(io::Error::from)
}
}
impl io::Write for PtyMaster {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
unistd::write(&self.0, buf).map_err(io::Error::from)
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl io::Read for &PtyMaster {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
unistd::read(&self.0, buf).map_err(io::Error::from)
}
}
impl io::Write for &PtyMaster {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
unistd::write(&self.0, buf).map_err(io::Error::from)
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
/// Grant access to a slave pseudoterminal (see
/// [`grantpt(3)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/grantpt.html))
///
/// `grantpt()` changes the mode and owner of the slave pseudoterminal device corresponding to the
/// master pseudoterminal referred to by `fd`. This is a necessary step towards opening the slave.
#[inline]
pub fn grantpt(fd: &PtyMaster) -> Result<()> {
if unsafe { libc::grantpt(fd.as_raw_fd()) } < 0 {
return Err(Errno::last());
}
Ok(())
}
/// Open a pseudoterminal device (see
/// [`posix_openpt(3)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_openpt.html))
///
/// `posix_openpt()` returns a file descriptor to an existing unused pseudoterminal master device.
///
/// # Examples
///
/// A common use case with this function is to open both a master and slave PTY pair. This can be
/// done as follows:
///
/// ```
/// use std::path::Path;
/// use nix::fcntl::{OFlag, open};
/// use nix::pty::{grantpt, posix_openpt, ptsname, unlockpt};
/// use nix::sys::stat::Mode;
///
/// # #[allow(dead_code)]
/// # fn run() -> nix::Result<()> {
/// // Open a new PTY master
/// let master_fd = posix_openpt(OFlag::O_RDWR)?;
///
/// // Allow a slave to be generated for it
/// grantpt(&master_fd)?;
/// unlockpt(&master_fd)?;
///
/// // Get the name of the slave
/// let slave_name = unsafe { ptsname(&master_fd) }?;
///
/// // Try to open the slave
/// let _slave_fd = open(Path::new(&slave_name), OFlag::O_RDWR, Mode::empty())?;
/// # Ok(())
/// # }
/// ```
#[inline]
pub fn posix_openpt(flags: fcntl::OFlag) -> Result<PtyMaster> {
let fd = unsafe { libc::posix_openpt(flags.bits()) };
if fd < 0 {
return Err(Errno::last());
}
Ok(PtyMaster(unsafe { OwnedFd::from_raw_fd(fd) }))
}
/// Get the name of the slave pseudoterminal (see
/// [`ptsname(3)`](https://man7.org/linux/man-pages/man3/ptsname.3.html))
///
/// `ptsname()` returns the name of the slave pseudoterminal device corresponding to the master
/// referred to by `fd`.
///
/// This value is useful for opening the slave pty once the master has already been opened with
/// `posix_openpt()`.
///
/// # Safety
///
/// `ptsname()` mutates global variables and is *not* threadsafe.
/// Mutating global variables is always considered `unsafe` by Rust and this
/// function is marked as `unsafe` to reflect that.
///
/// For a threadsafe and non-`unsafe` alternative on Linux, see `ptsname_r()`.
#[inline]
pub unsafe fn ptsname(fd: &PtyMaster) -> Result<String> {
let name_ptr = unsafe { libc::ptsname(fd.as_raw_fd()) };
if name_ptr.is_null() {
return Err(Errno::last());
}
let name = unsafe { CStr::from_ptr(name_ptr) };
Ok(name.to_string_lossy().into_owned())
}
/// Get the name of the slave pseudoterminal (see
/// [`ptsname(3)`](https://man7.org/linux/man-pages/man3/ptsname.3.html))
///
/// `ptsname_r()` returns the name of the slave pseudoterminal device corresponding to the master
/// referred to by `fd`. This is the threadsafe version of `ptsname()`, but it is not part of the
/// POSIX standard and is instead a Linux-specific extension.
///
/// This value is useful for opening the slave ptty once the master has already been opened with
/// `posix_openpt()`.
#[cfg(linux_android)]
#[inline]
pub fn ptsname_r(fd: &PtyMaster) -> Result<String> {
let mut name_buf = Vec::<libc::c_char>::with_capacity(64);
let name_buf_ptr = name_buf.as_mut_ptr();
let cname = unsafe {
let cap = name_buf.capacity();
if libc::ptsname_r(fd.as_raw_fd(), name_buf_ptr, cap) != 0 {
return Err(crate::Error::last());
}
CStr::from_ptr(name_buf.as_ptr())
};
let name = cname.to_string_lossy().into_owned();
Ok(name)
}
/// Unlock a pseudoterminal master/slave pseudoterminal pair (see
/// [`unlockpt(3)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlockpt.html))
///
/// `unlockpt()` unlocks the slave pseudoterminal device corresponding to the master pseudoterminal
/// referred to by `fd`. This must be called before trying to open the slave side of a
/// pseudoterminal.
#[inline]
pub fn unlockpt(fd: &PtyMaster) -> Result<()> {
if unsafe { libc::unlockpt(fd.as_raw_fd()) } < 0 {
return Err(Errno::last());
}
Ok(())
}
/// Create a new pseudoterminal, returning the slave and master file descriptors
/// in `OpenptyResult`
/// (see [`openpty`](https://man7.org/linux/man-pages/man3/openpty.3.html)).
///
/// If `winsize` is not `None`, the window size of the slave will be set to
/// the values in `winsize`. If `termios` is not `None`, the pseudoterminal's
/// terminal settings of the slave will be set to the values in `termios`.
#[inline]
#[cfg(not(target_os = "aix"))]
pub fn openpty<
'a,
'b,
T: Into<Option<&'a Winsize>>,
U: Into<Option<&'b Termios>>,
>(
winsize: T,
termios: U,
) -> Result<OpenptyResult> {
use std::ptr;
let mut slave = mem::MaybeUninit::<libc::c_int>::uninit();
let mut master = mem::MaybeUninit::<libc::c_int>::uninit();
let ret = {
match (termios.into(), winsize.into()) {
(Some(termios), Some(winsize)) => {
let inner_termios = termios.get_libc_termios();
unsafe {
libc::openpty(
master.as_mut_ptr(),
slave.as_mut_ptr(),
ptr::null_mut(),
&*inner_termios as *const libc::termios as *mut _,
winsize as *const Winsize as *mut _,
)
}
}
(None, Some(winsize)) => unsafe {
libc::openpty(
master.as_mut_ptr(),
slave.as_mut_ptr(),
ptr::null_mut(),
ptr::null_mut(),
winsize as *const Winsize as *mut _,
)
},
(Some(termios), None) => {
let inner_termios = termios.get_libc_termios();
unsafe {
libc::openpty(
master.as_mut_ptr(),
slave.as_mut_ptr(),
ptr::null_mut(),
&*inner_termios as *const libc::termios as *mut _,
ptr::null_mut(),
)
}
}
(None, None) => unsafe {
libc::openpty(
master.as_mut_ptr(),
slave.as_mut_ptr(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
)
},
}
};
Errno::result(ret)?;
unsafe {
Ok(OpenptyResult {
master: OwnedFd::from_raw_fd(master.assume_init()),
slave: OwnedFd::from_raw_fd(slave.assume_init()),
})
}
}
feature! {
#![feature = "process"]
/// Create a new process operating in a pseudoterminal.
///
/// If `winsize` is not `None`, the window size of the slave will be set to
/// the values in `winsize`. If `termios` is not `None`, the pseudoterminal's
/// terminal settings of the slave will be set to the values in `termios`.
///
/// # 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) until
/// a call of `execve(2)`. 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
///
/// # Reference
///
/// * [FreeBSD](https://man.freebsd.org/cgi/man.cgi?query=forkpty)
/// * [Linux](https://man7.org/linux/man-pages/man3/forkpty.3.html)
#[cfg(not(target_os = "aix"))]
pub unsafe fn forkpty<'a, 'b, T: Into<Option<&'a Winsize>>, U: Into<Option<&'b Termios>>>(
winsize: T,
termios: U,
) -> Result<ForkptyResult> {
use std::ptr;
let mut master = mem::MaybeUninit::<libc::c_int>::uninit();
let term = match termios.into() {
Some(termios) => {
let inner_termios = termios.get_libc_termios();
&*inner_termios as *const libc::termios as *mut _
},
None => ptr::null_mut(),
};
let win = winsize
.into()
.map(|ws| ws as *const Winsize as *mut _)
.unwrap_or(ptr::null_mut());
let res = unsafe { libc::forkpty(master.as_mut_ptr(), ptr::null_mut(), term, win) };
let success_ret = Errno::result(res)?;
let forkpty_result = match success_ret {
// In the child process
0 => ForkptyResult::Child,
// In the parent process
child_pid => {
// SAFETY:
// 1. The master buffer is guaranteed to be initialized in the parent process
// 2. OwnedFd::from_raw_fd won't panic as the fd is a valid file descriptor
let master = unsafe { OwnedFd::from_raw_fd( master.assume_init() ) };
ForkptyResult::Parent {
master,
child: Pid::from_raw(child_pid),
}
}
};
Ok(forkpty_result)
}
}

326
vendor/nix/src/sched.rs vendored Normal file
View File

@@ -0,0 +1,326 @@
//! Execution scheduling
//!
//! See Also
//! [sched.h](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sched.h.html)
use crate::{Errno, Result};
#[cfg(linux_android)]
pub use self::sched_linux_like::*;
#[cfg(linux_android)]
mod sched_linux_like {
use crate::errno::Errno;
use crate::unistd::Pid;
use crate::Result;
use libc::{self, c_int, c_void};
use std::mem;
use std::option::Option;
use std::os::unix::io::{AsFd, AsRawFd};
// For some functions taking with a parameter of type CloneFlags,
// only a subset of these flags have an effect.
libc_bitflags! {
/// Options for use with [`clone`]
pub struct CloneFlags: c_int {
/// The calling process and the child process run in the same
/// memory space.
CLONE_VM;
/// The caller and the child process share the same filesystem
/// information.
CLONE_FS;
/// The calling process and the child process share the same file
/// descriptor table.
CLONE_FILES;
/// The calling process and the child process share the same table
/// of signal handlers.
CLONE_SIGHAND;
/// If the calling process is being traced, then trace the child
/// also.
CLONE_PTRACE;
/// The execution of the calling process is suspended until the
/// child releases its virtual memory resources via a call to
/// execve(2) or _exit(2) (as with vfork(2)).
CLONE_VFORK;
/// The parent of the new child (as returned by getppid(2))
/// will be the same as that of the calling process.
CLONE_PARENT;
/// The child is placed in the same thread group as the calling
/// process.
CLONE_THREAD;
/// The cloned child is started in a new mount namespace.
CLONE_NEWNS;
/// The child and the calling process share a single list of System
/// V semaphore adjustment values
CLONE_SYSVSEM;
// Not supported by Nix due to lack of varargs support in Rust FFI
// CLONE_SETTLS;
// Not supported by Nix due to lack of varargs support in Rust FFI
// CLONE_PARENT_SETTID;
// Not supported by Nix due to lack of varargs support in Rust FFI
// CLONE_CHILD_CLEARTID;
/// Unused since Linux 2.6.2
#[deprecated(since = "0.23.0", note = "Deprecated by Linux 2.6.2")]
CLONE_DETACHED;
/// A tracing process cannot force `CLONE_PTRACE` on this child
/// process.
CLONE_UNTRACED;
// Not supported by Nix due to lack of varargs support in Rust FFI
// CLONE_CHILD_SETTID;
/// Create the process in a new cgroup namespace.
CLONE_NEWCGROUP;
/// Create the process in a new UTS namespace.
CLONE_NEWUTS;
/// Create the process in a new IPC namespace.
CLONE_NEWIPC;
/// Create the process in a new user namespace.
CLONE_NEWUSER;
/// Create the process in a new PID namespace.
CLONE_NEWPID;
/// Create the process in a new network namespace.
CLONE_NEWNET;
/// The new process shares an I/O context with the calling process.
CLONE_IO;
}
}
/// Type for the function executed by [`clone`].
pub type CloneCb<'a> = Box<dyn FnMut() -> isize + 'a>;
/// `clone` create a child process
/// ([`clone(2)`](https://man7.org/linux/man-pages/man2/clone.2.html))
///
/// `stack` is a reference to an array which will hold the stack of the new
/// process. Unlike when calling `clone(2)` from C, the provided stack
/// address need not be the highest address of the region. Nix will take
/// care of that requirement. The user only needs to provide a reference to
/// a normally allocated buffer.
///
/// # Safety
///
/// Because `clone` creates a child process with its stack located in
/// `stack` without specifying the size of the stack, special care must be
/// taken to ensure that the child process does not overflow the provided
/// stack space.
///
/// See [`fork`](crate::unistd::fork) for additional safety concerns related
/// to executing child processes.
pub unsafe fn clone(
mut cb: CloneCb,
stack: &mut [u8],
flags: CloneFlags,
signal: Option<c_int>,
) -> Result<Pid> {
extern "C" fn callback(data: *mut CloneCb) -> c_int {
let cb: &mut CloneCb = unsafe { &mut *data };
(*cb)() as c_int
}
let combined = flags.bits() | signal.unwrap_or(0);
let res = unsafe {
let ptr = stack.as_mut_ptr().add(stack.len());
let ptr_aligned = ptr.sub(ptr as usize % 16);
libc::clone(
mem::transmute::<
extern "C" fn(*mut Box<dyn FnMut() -> isize>) -> i32,
extern "C" fn(*mut libc::c_void) -> i32,
>(
callback
as extern "C" fn(*mut Box<dyn FnMut() -> isize>) -> i32,
),
ptr_aligned as *mut c_void,
combined,
&mut cb as *mut _ as *mut c_void,
)
};
Errno::result(res).map(Pid::from_raw)
}
/// disassociate parts of the process execution context
///
/// See also [unshare(2)](https://man7.org/linux/man-pages/man2/unshare.2.html)
pub fn unshare(flags: CloneFlags) -> Result<()> {
let res = unsafe { libc::unshare(flags.bits()) };
Errno::result(res).map(drop)
}
/// reassociate thread with a namespace
///
/// See also [setns(2)](https://man7.org/linux/man-pages/man2/setns.2.html)
pub fn setns<Fd: AsFd>(fd: Fd, nstype: CloneFlags) -> Result<()> {
let res = unsafe { libc::setns(fd.as_fd().as_raw_fd(), nstype.bits()) };
Errno::result(res).map(drop)
}
}
#[cfg(any(linux_android, freebsdlike))]
pub use self::sched_affinity::*;
#[cfg(any(linux_android, freebsdlike))]
mod sched_affinity {
use crate::errno::Errno;
use crate::unistd::Pid;
use crate::Result;
use std::mem;
/// CpuSet represent a bit-mask of CPUs.
/// CpuSets are used by sched_setaffinity and
/// sched_getaffinity for example.
///
/// This is a wrapper around `libc::cpu_set_t`.
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct CpuSet {
#[cfg(not(target_os = "freebsd"))]
cpu_set: libc::cpu_set_t,
#[cfg(target_os = "freebsd")]
cpu_set: libc::cpuset_t,
}
impl CpuSet {
/// Create a new and empty CpuSet.
pub fn new() -> CpuSet {
CpuSet {
cpu_set: unsafe { mem::zeroed() },
}
}
/// Test to see if a CPU is in the CpuSet.
/// `field` is the CPU id to test
pub fn is_set(&self, field: usize) -> Result<bool> {
if field >= CpuSet::count() {
Err(Errno::EINVAL)
} else {
Ok(unsafe { libc::CPU_ISSET(field, &self.cpu_set) })
}
}
/// Add a CPU to CpuSet.
/// `field` is the CPU id to add
pub fn set(&mut self, field: usize) -> Result<()> {
if field >= CpuSet::count() {
Err(Errno::EINVAL)
} else {
unsafe {
libc::CPU_SET(field, &mut self.cpu_set);
}
Ok(())
}
}
/// Remove a CPU from CpuSet.
/// `field` is the CPU id to remove
pub fn unset(&mut self, field: usize) -> Result<()> {
if field >= CpuSet::count() {
Err(Errno::EINVAL)
} else {
unsafe {
libc::CPU_CLR(field, &mut self.cpu_set);
}
Ok(())
}
}
/// Return the maximum number of CPU in CpuSet
pub const fn count() -> usize {
#[cfg(not(target_os = "freebsd"))]
let bytes = mem::size_of::<libc::cpu_set_t>();
#[cfg(target_os = "freebsd")]
let bytes = mem::size_of::<libc::cpuset_t>();
8 * bytes
}
}
impl Default for CpuSet {
fn default() -> Self {
Self::new()
}
}
/// `sched_setaffinity` set a thread's CPU affinity mask
/// ([`sched_setaffinity(2)`](https://man7.org/linux/man-pages/man2/sched_setaffinity.2.html))
///
/// `pid` is the thread ID to update.
/// If pid is zero, then the calling thread is updated.
///
/// The `cpuset` argument specifies the set of CPUs on which the thread
/// will be eligible to run.
///
/// # Example
///
/// Binding the current thread to CPU 0 can be done as follows:
///
/// ```rust,no_run
/// use nix::sched::{CpuSet, sched_setaffinity};
/// use nix::unistd::Pid;
///
/// let mut cpu_set = CpuSet::new();
/// cpu_set.set(0).unwrap();
/// sched_setaffinity(Pid::from_raw(0), &cpu_set).unwrap();
/// ```
pub fn sched_setaffinity(pid: Pid, cpuset: &CpuSet) -> Result<()> {
let res = unsafe {
libc::sched_setaffinity(
pid.into(),
mem::size_of::<CpuSet>() as libc::size_t,
&cpuset.cpu_set,
)
};
Errno::result(res).map(drop)
}
/// `sched_getaffinity` get a thread's CPU affinity mask
/// ([`sched_getaffinity(2)`](https://man7.org/linux/man-pages/man2/sched_getaffinity.2.html))
///
/// `pid` is the thread ID to check.
/// If pid is zero, then the calling thread is checked.
///
/// Returned `cpuset` is the set of CPUs on which the thread
/// is eligible to run.
///
/// # Example
///
/// Checking if the current thread can run on CPU 0 can be done as follows:
///
/// ```rust,no_run
/// use nix::sched::sched_getaffinity;
/// use nix::unistd::Pid;
///
/// let cpu_set = sched_getaffinity(Pid::from_raw(0)).unwrap();
/// if cpu_set.is_set(0).unwrap() {
/// println!("Current thread can run on CPU 0");
/// }
/// ```
pub fn sched_getaffinity(pid: Pid) -> Result<CpuSet> {
let mut cpuset = CpuSet::new();
let res = unsafe {
libc::sched_getaffinity(
pid.into(),
mem::size_of::<CpuSet>() as libc::size_t,
&mut cpuset.cpu_set,
)
};
Errno::result(res).and(Ok(cpuset))
}
/// Determines the CPU on which the calling thread is running.
pub fn sched_getcpu() -> Result<usize> {
let res = unsafe { libc::sched_getcpu() };
Errno::result(res).map(|int| int as usize)
}
}
/// Explicitly yield the processor to other threads.
///
/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sched_yield.html)
pub fn sched_yield() -> Result<()> {
let res = unsafe { libc::sched_yield() };
Errno::result(res).map(drop)
}

431
vendor/nix/src/spawn.rs vendored Normal file
View File

@@ -0,0 +1,431 @@
//! Safe wrappers around posix_spawn* functions found in the libc "spawn.h" header.
use std::{ffi::CStr, mem, os::fd::RawFd};
#[cfg(any(feature = "fs", feature = "term"))]
use crate::fcntl::OFlag;
#[cfg(feature = "signal")]
use crate::sys::signal::SigSet;
#[cfg(feature = "fs")]
use crate::sys::stat::Mode;
use crate::{errno::Errno, unistd::Pid, NixPath, Result};
/// A spawn attributes object. See [posix_spawnattr_t](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_init.html).
#[repr(transparent)]
#[derive(Debug)]
pub struct PosixSpawnAttr {
attr: libc::posix_spawnattr_t,
}
impl PosixSpawnAttr {
/// Initialize the spawn attributes object. See
/// [posix_spawnattr_init](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_init.html).
#[doc(alias("posix_spawnattr_init"))]
pub fn init() -> Result<PosixSpawnAttr> {
let mut attr = mem::MaybeUninit::uninit();
let res = unsafe { libc::posix_spawnattr_init(attr.as_mut_ptr()) };
Errno::result(res)?;
let attr = unsafe { attr.assume_init() };
Ok(PosixSpawnAttr { attr })
}
/// Reinitialize the spawn attributes object.
/// This is a wrapper around
/// [posix_spawnattr_destroy](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_destroy.html)
/// followed by
/// [posix_spawnattr_init](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_init.html).
#[doc(alias("posix_spawnattr_destroy"))]
pub fn reinit(mut self) -> Result<PosixSpawnAttr> {
let res = unsafe {
libc::posix_spawnattr_destroy(
&mut self.attr as *mut libc::posix_spawnattr_t,
)
};
Errno::result(res)?;
let res = unsafe {
libc::posix_spawnattr_init(
&mut self.attr as *mut libc::posix_spawnattr_t,
)
};
Errno::result(res)?;
Ok(self)
}
/// Set spawn flags. See
/// [posix_spawnattr_setflags](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setflags.html).
#[doc(alias("posix_spawnattr_setflags"))]
pub fn set_flags(&mut self, flags: PosixSpawnFlags) -> Result<()> {
let res = unsafe {
libc::posix_spawnattr_setflags(
&mut self.attr as *mut libc::posix_spawnattr_t,
flags.bits() as libc::c_short,
)
};
Errno::result(res)?;
Ok(())
}
/// Get spawn flags. See
/// [posix_spawnattr_getflags](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_getflags.html).
#[doc(alias("posix_spawnattr_getflags"))]
pub fn flags(&self) -> Result<PosixSpawnFlags> {
let mut flags: libc::c_short = 0;
let res = unsafe {
libc::posix_spawnattr_getflags(
&self.attr as *const libc::posix_spawnattr_t,
&mut flags,
)
};
Errno::result(res)?;
Ok(PosixSpawnFlags::from_bits_truncate(flags.into()))
}
/// Set spawn pgroup. See
/// [posix_spawnattr_setpgroup](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setpgroup.html).
#[doc(alias("posix_spawnattr_setpgroup"))]
pub fn set_pgroup(&mut self, pgroup: Pid) -> Result<()> {
let res = unsafe {
libc::posix_spawnattr_setpgroup(
&mut self.attr as *mut libc::posix_spawnattr_t,
pgroup.as_raw(),
)
};
Errno::result(res)?;
Ok(())
}
/// Get spawn pgroup. See
/// [posix_spawnattr_getpgroup](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_getpgroup.html).
#[doc(alias("posix_spawnattr_getpgroup"))]
pub fn pgroup(&self) -> Result<Pid> {
let mut pid: libc::pid_t = 0;
let res = unsafe {
libc::posix_spawnattr_getpgroup(
&self.attr as *const libc::posix_spawnattr_t,
&mut pid,
)
};
Errno::result(res)?;
Ok(Pid::from_raw(pid))
}
feature! {
#![feature = "signal"]
/// Set spawn sigdefault. See
/// [posix_spawnattr_setsigdefault](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setsigdefault.html).
#[doc(alias("posix_spawnattr_setsigdefault"))]
pub fn set_sigdefault(&mut self, sigdefault: &SigSet) -> Result<()> {
let res = unsafe {
libc::posix_spawnattr_setsigdefault(
&mut self.attr as *mut libc::posix_spawnattr_t,
sigdefault.as_ref(),
)
};
Errno::result(res)?;
Ok(())
}
/// Get spawn sigdefault. See
/// [posix_spawnattr_getsigdefault](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_getsigdefault.html).
#[doc(alias("posix_spawnattr_getsigdefault"))]
pub fn sigdefault(&self) -> Result<SigSet> {
let mut sigset = mem::MaybeUninit::uninit();
let res = unsafe {
libc::posix_spawnattr_getsigdefault(
&self.attr as *const libc::posix_spawnattr_t,
sigset.as_mut_ptr(),
)
};
Errno::result(res)?;
let sigdefault =
unsafe { SigSet::from_sigset_t_unchecked(sigset.assume_init()) };
Ok(sigdefault)
}
/// Set spawn sigmask. See
/// [posix_spawnattr_setsigmask](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setsigmask.html).
#[doc(alias("posix_spawnattr_setsigmask"))]
pub fn set_sigmask(&mut self, sigdefault: &SigSet) -> Result<()> {
let res = unsafe {
libc::posix_spawnattr_setsigmask(
&mut self.attr as *mut libc::posix_spawnattr_t,
sigdefault.as_ref(),
)
};
Errno::result(res)?;
Ok(())
}
/// Get spawn sigmask. See
/// [posix_spawnattr_getsigmask](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_getsigmask.html).
#[doc(alias("posix_spawnattr_getsigmask"))]
pub fn sigmask(&self) -> Result<SigSet> {
let mut sigset = mem::MaybeUninit::uninit();
let res = unsafe {
libc::posix_spawnattr_getsigmask(
&self.attr as *const libc::posix_spawnattr_t,
sigset.as_mut_ptr(),
)
};
Errno::result(res)?;
let sigdefault =
unsafe { SigSet::from_sigset_t_unchecked(sigset.assume_init()) };
Ok(sigdefault)
}
}
}
impl Drop for PosixSpawnAttr {
fn drop(&mut self) {
unsafe {
libc::posix_spawnattr_destroy(
&mut self.attr as *mut libc::posix_spawnattr_t,
);
}
}
}
libc_bitflags!(
/// Process attributes to be changed in the new process image when invoking [`posix_spawn`]
/// or [`posix_spawnp`]. See
/// [posix_spawn](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn.html).
pub struct PosixSpawnFlags: libc::c_int {
/// Reset effective user ID of the child process to parent's real user ID.
POSIX_SPAWN_RESETIDS;
/// Put the child in a process group specified by the spawn-pgroup attribute. See
/// [posix_spawnattr_setpgroup](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setpgroup.html).
POSIX_SPAWN_SETPGROUP;
/// Force set signals to default signal handling in child process. See
/// [posix_spawnattr_setsigdefault](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setsigdefault.html).
#[cfg(feature = "signal")]
POSIX_SPAWN_SETSIGDEF;
/// Set signal mask of child process. See
/// [posix_spawnattr_setsigmask](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setsigmask.html).
#[cfg(feature = "signal")]
POSIX_SPAWN_SETSIGMASK;
// TODO: Add support for the following two flags whenever support for
// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sched.h.html
// is added to nix.
// POSIX_SPAWN_SETSCHEDPARAM;
// POSIX_SPAWN_SETSCHEDULER;
}
);
/// A spawn file actions object. See [posix_spawn_file_actions_t](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_addclose.html).
#[repr(transparent)]
#[derive(Debug)]
pub struct PosixSpawnFileActions {
fa: libc::posix_spawn_file_actions_t,
}
impl PosixSpawnFileActions {
/// Initialize the spawn file actions object. See
/// [posix_spawn_file_actions_init](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_init.html).
#[doc(alias("posix_spawn_file_actions_init"))]
pub fn init() -> Result<PosixSpawnFileActions> {
let mut actions = mem::MaybeUninit::uninit();
let res = unsafe {
libc::posix_spawn_file_actions_init(actions.as_mut_ptr())
};
Errno::result(res)?;
Ok(unsafe {
PosixSpawnFileActions {
fa: actions.assume_init(),
}
})
}
/// Reinitialize the spawn file actions object.
/// This is a wrapper around
/// [posix_spawn_file_actions_destroy](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_destroy.html).
/// followed by
/// [posix_spawn_file_actions_init](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_init.html).
#[doc(alias("posix_spawn_file_actions_destroy"))]
pub fn reinit(mut self) -> Result<PosixSpawnFileActions> {
let res = unsafe {
libc::posix_spawn_file_actions_destroy(
&mut self.fa as *mut libc::posix_spawn_file_actions_t,
)
};
Errno::result(res)?;
let res = unsafe {
libc::posix_spawn_file_actions_init(
&mut self.fa as *mut libc::posix_spawn_file_actions_t,
)
};
Errno::result(res)?;
Ok(self)
}
/// Add a [dup2](https://pubs.opengroup.org/onlinepubs/9699919799/functions/dup2.html) action. See
/// [posix_spawn_file_actions_adddup2](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_adddup2.html).
#[doc(alias("posix_spawn_file_actions_adddup2"))]
pub fn add_dup2(&mut self, fd: RawFd, newfd: RawFd) -> Result<()> {
let res = unsafe {
libc::posix_spawn_file_actions_adddup2(
&mut self.fa as *mut libc::posix_spawn_file_actions_t,
fd,
newfd,
)
};
Errno::result(res)?;
Ok(())
}
feature! {
#![all(feature = "fs", feature = "term")]
/// Add an open action. See
/// [posix_spawn_file_actions_addopen](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_addopen.html).
#[doc(alias("posix_spawn_file_actions_addopen"))]
pub fn add_open<P: ?Sized + NixPath>(
&mut self,
fd: RawFd,
path: &P,
oflag: OFlag,
mode: Mode,
) -> Result<()> {
let res = path.with_nix_path(|cstr| unsafe {
libc::posix_spawn_file_actions_addopen(
&mut self.fa as *mut libc::posix_spawn_file_actions_t,
fd,
cstr.as_ptr(),
oflag.bits(),
mode.bits(),
)
})?;
Errno::result(res)?;
Ok(())
}
}
/// Add a close action. See
/// [posix_spawn_file_actions_addclose](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_addclose.html).
#[doc(alias("posix_spawn_file_actions_addclose"))]
pub fn add_close(&mut self, fd: RawFd) -> Result<()> {
let res = unsafe {
libc::posix_spawn_file_actions_addclose(
&mut self.fa as *mut libc::posix_spawn_file_actions_t,
fd,
)
};
Errno::result(res)?;
Ok(())
}
}
impl Drop for PosixSpawnFileActions {
fn drop(&mut self) {
unsafe {
libc::posix_spawn_file_actions_destroy(
&mut self.fa as *mut libc::posix_spawn_file_actions_t,
);
}
}
}
// The POSIX standard requires those `args` and `envp` to be of type `*const *mut [c_char]`,
// but implementations won't modify them, making the `mut` type redundant. Considering this,
// Nix does not expose this mutability, but we have to change the interface when calling the
// underlying libc interfaces , this helper function does the conversion job.
//
// SAFETY:
// It is safe to add the mutability in types as implementations won't mutable them.
unsafe fn to_exec_array<S: AsRef<CStr>>(args: &[S]) -> Vec<*mut libc::c_char> {
let mut v: Vec<*mut libc::c_char> = args
.iter()
.map(|s| s.as_ref().as_ptr().cast_mut())
.collect();
v.push(std::ptr::null_mut());
v
}
/// Create a new child process from the specified process image. See
/// [posix_spawn](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn.html).
pub fn posix_spawn<P, SA, SE>(
path: &P,
file_actions: &PosixSpawnFileActions,
attr: &PosixSpawnAttr,
args: &[SA],
envp: &[SE],
) -> Result<Pid>
where
P: NixPath + ?Sized,
SA: AsRef<CStr>,
SE: AsRef<CStr>,
{
let mut pid = 0;
let ret = unsafe {
let args_p = to_exec_array(args);
let env_p = to_exec_array(envp);
path.with_nix_path(|c_str| {
libc::posix_spawn(
&mut pid as *mut libc::pid_t,
c_str.as_ptr(),
&file_actions.fa as *const libc::posix_spawn_file_actions_t,
&attr.attr as *const libc::posix_spawnattr_t,
args_p.as_ptr(),
env_p.as_ptr(),
)
})?
};
if ret != 0 {
return Err(Errno::from_raw(ret));
}
Ok(Pid::from_raw(pid))
}
/// Create a new child process from the specified process image. See
/// [posix_spawnp](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnp.html).
pub fn posix_spawnp<SA: AsRef<CStr>, SE: AsRef<CStr>>(
path: &CStr,
file_actions: &PosixSpawnFileActions,
attr: &PosixSpawnAttr,
args: &[SA],
envp: &[SE],
) -> Result<Pid> {
let mut pid = 0;
let ret = unsafe {
let args_p = to_exec_array(args);
let env_p = to_exec_array(envp);
libc::posix_spawnp(
&mut pid as *mut libc::pid_t,
path.as_ptr(),
&file_actions.fa as *const libc::posix_spawn_file_actions_t,
&attr.attr as *const libc::posix_spawnattr_t,
args_p.as_ptr(),
env_p.as_ptr(),
)
};
if ret != 0 {
return Err(Errno::from_raw(ret));
}
Ok(Pid::from_raw(pid))
}

1197
vendor/nix/src/sys/aio.rs vendored Normal file

File diff suppressed because it is too large Load Diff

255
vendor/nix/src/sys/epoll.rs vendored Normal file
View File

@@ -0,0 +1,255 @@
use crate::errno::Errno;
pub use crate::poll_timeout::PollTimeout as EpollTimeout;
pub use crate::poll_timeout::PollTimeoutTryFromError as EpollTimeoutTryFromError;
use crate::Result;
use libc::{self, c_int};
use std::mem;
use std::os::unix::io::{AsFd, AsRawFd, FromRawFd, OwnedFd, RawFd};
libc_bitflags!(
pub struct EpollFlags: c_int {
EPOLLIN;
EPOLLPRI;
EPOLLOUT;
EPOLLRDNORM;
EPOLLRDBAND;
EPOLLWRNORM;
EPOLLWRBAND;
EPOLLMSG;
EPOLLERR;
EPOLLHUP;
EPOLLRDHUP;
EPOLLEXCLUSIVE;
#[cfg(not(target_arch = "mips"))]
EPOLLWAKEUP;
EPOLLONESHOT;
EPOLLET;
}
);
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[repr(i32)]
#[non_exhaustive]
pub enum EpollOp {
EpollCtlAdd = libc::EPOLL_CTL_ADD,
EpollCtlDel = libc::EPOLL_CTL_DEL,
EpollCtlMod = libc::EPOLL_CTL_MOD,
}
libc_bitflags! {
pub struct EpollCreateFlags: c_int {
EPOLL_CLOEXEC;
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[repr(transparent)]
pub struct EpollEvent {
event: libc::epoll_event,
}
impl EpollEvent {
pub fn new(events: EpollFlags, data: u64) -> Self {
EpollEvent {
event: libc::epoll_event {
events: events.bits() as u32,
u64: data,
},
}
}
pub fn empty() -> Self {
unsafe { mem::zeroed::<EpollEvent>() }
}
pub fn events(&self) -> EpollFlags {
EpollFlags::from_bits(self.event.events as c_int).unwrap()
}
pub fn data(&self) -> u64 {
self.event.u64
}
}
/// A safe wrapper around [`epoll`](https://man7.org/linux/man-pages/man7/epoll.7.html).
/// ```
/// # use nix::sys::{epoll::{EpollTimeout, Epoll, EpollEvent, EpollFlags, EpollCreateFlags}, eventfd::{EventFd, EfdFlags}};
/// # use nix::unistd::write;
/// # use std::os::unix::io::{OwnedFd, FromRawFd, AsFd};
/// # use std::time::{Instant, Duration};
/// # fn main() -> nix::Result<()> {
/// const DATA: u64 = 17;
/// const MILLIS: u8 = 100;
///
/// // Create epoll
/// let epoll = Epoll::new(EpollCreateFlags::empty())?;
///
/// // Create eventfd & Add event
/// let eventfd = EventFd::new()?;
/// epoll.add(&eventfd, EpollEvent::new(EpollFlags::EPOLLIN,DATA))?;
///
/// // Arm eventfd & Time wait
/// eventfd.write(1)?;
/// let now = Instant::now();
///
/// // Wait on event
/// let mut events = [EpollEvent::empty()];
/// epoll.wait(&mut events, MILLIS)?;
///
/// // Assert data correct & timeout didn't occur
/// assert_eq!(events[0].data(), DATA);
/// assert!(now.elapsed().as_millis() < MILLIS.into());
/// # Ok(())
/// # }
/// ```
#[derive(Debug)]
pub struct Epoll(pub OwnedFd);
impl Epoll {
/// Creates a new epoll instance and returns a file descriptor referring to that instance.
///
/// [`epoll_create1`](https://man7.org/linux/man-pages/man2/epoll_create1.2.html).
pub fn new(flags: EpollCreateFlags) -> Result<Self> {
let res = unsafe { libc::epoll_create1(flags.bits()) };
let fd = Errno::result(res)?;
let owned_fd = unsafe { OwnedFd::from_raw_fd(fd) };
Ok(Self(owned_fd))
}
/// Add an entry to the interest list of the epoll file descriptor for
/// specified in events.
///
/// [`epoll_ctl`](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html) with `EPOLL_CTL_ADD`.
pub fn add<Fd: AsFd>(&self, fd: Fd, mut event: EpollEvent) -> Result<()> {
self.epoll_ctl(EpollOp::EpollCtlAdd, fd, &mut event)
}
/// Remove (deregister) the target file descriptor `fd` from the interest list.
///
/// [`epoll_ctl`](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html) with `EPOLL_CTL_DEL` .
pub fn delete<Fd: AsFd>(&self, fd: Fd) -> Result<()> {
self.epoll_ctl(EpollOp::EpollCtlDel, fd, None)
}
/// Change the settings associated with `fd` in the interest list to the new settings specified
/// in `event`.
///
/// [`epoll_ctl`](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html) with `EPOLL_CTL_MOD`.
pub fn modify<Fd: AsFd>(
&self,
fd: Fd,
event: &mut EpollEvent,
) -> Result<()> {
self.epoll_ctl(EpollOp::EpollCtlMod, fd, event)
}
/// Waits for I/O events, blocking the calling thread if no events are currently available.
/// (This can be thought of as fetching items from the ready list of the epoll instance.)
///
/// [`epoll_wait`](https://man7.org/linux/man-pages/man2/epoll_wait.2.html)
pub fn wait<T: Into<EpollTimeout>>(
&self,
events: &mut [EpollEvent],
timeout: T,
) -> Result<usize> {
let res = unsafe {
libc::epoll_wait(
self.0.as_raw_fd(),
events.as_mut_ptr().cast(),
events.len() as c_int,
timeout.into().into(),
)
};
Errno::result(res).map(|r| r as usize)
}
/// This system call is used to add, modify, or remove entries in the interest list of the epoll
/// instance referred to by `self`. It requests that the operation `op` be performed for the
/// target file descriptor, `fd`.
///
/// When possible prefer [`Epoll::add`], [`Epoll::delete`] and [`Epoll::modify`].
///
/// [`epoll_ctl`](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html)
fn epoll_ctl<'a, Fd: AsFd, T>(
&self,
op: EpollOp,
fd: Fd,
event: T,
) -> Result<()>
where
T: Into<Option<&'a mut EpollEvent>>,
{
let event: Option<&mut EpollEvent> = event.into();
let ptr = event
.map(|x| &mut x.event as *mut libc::epoll_event)
.unwrap_or(std::ptr::null_mut());
unsafe {
Errno::result(libc::epoll_ctl(
self.0.as_raw_fd(),
op as c_int,
fd.as_fd().as_raw_fd(),
ptr,
))
.map(drop)
}
}
}
#[deprecated(since = "0.27.0", note = "Use Epoll::new() instead")]
#[inline]
pub fn epoll_create() -> Result<RawFd> {
let res = unsafe { libc::epoll_create(1024) };
Errno::result(res)
}
#[deprecated(since = "0.27.0", note = "Use Epoll::new() instead")]
#[inline]
pub fn epoll_create1(flags: EpollCreateFlags) -> Result<RawFd> {
let res = unsafe { libc::epoll_create1(flags.bits()) };
Errno::result(res)
}
#[deprecated(
since = "0.27.0",
note = "Use corresponding Epoll methods instead"
)]
#[inline]
pub fn epoll_ctl<'a, T>(
epfd: RawFd,
op: EpollOp,
fd: RawFd,
event: T,
) -> Result<()>
where
T: Into<Option<&'a mut EpollEvent>>,
{
let mut event: Option<&mut EpollEvent> = event.into();
if event.is_none() && op != EpollOp::EpollCtlDel {
Err(Errno::EINVAL)
} else {
let res = unsafe {
if let Some(ref mut event) = event {
libc::epoll_ctl(epfd, op as c_int, fd, &mut event.event)
} else {
libc::epoll_ctl(epfd, op as c_int, fd, std::ptr::null_mut())
}
};
Errno::result(res).map(drop)
}
}
#[deprecated(since = "0.27.0", note = "Use Epoll::wait() instead")]
#[inline]
pub fn epoll_wait(
epfd: RawFd,
events: &mut [EpollEvent],
timeout_ms: isize,
) -> Result<usize> {
let res = unsafe {
libc::epoll_wait(
epfd,
events.as_mut_ptr().cast(),
events.len() as c_int,
timeout_ms as c_int,
)
};
Errno::result(res).map(|r| r as usize)
}

460
vendor/nix/src/sys/event.rs vendored Normal file
View File

@@ -0,0 +1,460 @@
//! Kernel event notification mechanism
//!
//! # See Also
//! [kqueue(2)](https://www.freebsd.org/cgi/man.cgi?query=kqueue)
use crate::{Errno, Result};
#[cfg(not(target_os = "netbsd"))]
use libc::{c_int, c_long, intptr_t, time_t, timespec, uintptr_t};
#[cfg(target_os = "netbsd")]
use libc::{c_long, intptr_t, size_t, time_t, timespec, uintptr_t};
use std::convert::TryInto;
use std::mem;
use std::os::fd::{AsFd, BorrowedFd};
use std::os::unix::io::{AsRawFd, FromRawFd, OwnedFd};
use std::ptr;
/// A kernel event queue. Used to notify a process of various asynchronous
/// events.
#[repr(C)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct KEvent {
kevent: libc::kevent,
}
/// A kernel event queue.
///
/// Used by the kernel to notify the process of various types of asynchronous
/// events.
#[repr(transparent)]
#[derive(Debug)]
pub struct Kqueue(OwnedFd);
impl AsFd for Kqueue {
fn as_fd(&self) -> BorrowedFd<'_> {
self.0.as_fd()
}
}
impl From<Kqueue> for OwnedFd {
fn from(value: Kqueue) -> Self {
value.0
}
}
impl Kqueue {
/// Create a new kernel event queue.
pub fn new() -> Result<Self> {
let res = unsafe { libc::kqueue() };
Errno::result(res).map(|fd| unsafe { Self(OwnedFd::from_raw_fd(fd)) })
}
/// Register new events with the kqueue, and return any pending events to
/// the user.
///
/// This method will block until either the timeout expires, or a registered
/// event triggers a notification.
///
/// # Arguments
/// - `changelist` - Any new kevents to register for notifications.
/// - `eventlist` - Storage space for the kernel to return notifications.
/// - `timeout` - An optional timeout.
///
/// # Returns
/// Returns the number of events placed in the `eventlist`. If an error
/// occurs while processing an element of the `changelist` and there is
/// enough room in the `eventlist`, then the event will be placed in the
/// `eventlist` with `EV_ERROR` set in `flags` and the system error in
/// `data`.
pub fn kevent(
&self,
changelist: &[KEvent],
eventlist: &mut [KEvent],
timeout_opt: Option<timespec>,
) -> Result<usize> {
let res = unsafe {
libc::kevent(
self.0.as_raw_fd(),
changelist.as_ptr().cast(),
changelist.len() as type_of_nchanges,
eventlist.as_mut_ptr().cast(),
eventlist.len() as type_of_nchanges,
if let Some(ref timeout) = timeout_opt {
timeout as *const timespec
} else {
ptr::null()
},
)
};
Errno::result(res).map(|r| r as usize)
}
}
#[cfg(any(freebsdlike, apple_targets, target_os = "openbsd"))]
type type_of_udata = *mut libc::c_void;
#[cfg(target_os = "netbsd")]
type type_of_udata = intptr_t;
#[cfg(target_os = "netbsd")]
type type_of_event_filter = u32;
#[cfg(not(target_os = "netbsd"))]
type type_of_event_filter = i16;
libc_enum! {
#[cfg_attr(target_os = "netbsd", repr(u32))]
#[cfg_attr(not(target_os = "netbsd"), repr(i16))]
#[non_exhaustive]
/// Kqueue filter types. These are all the different types of event that a
/// kqueue can notify for.
pub enum EventFilter {
/// Notifies on the completion of a POSIX AIO operation.
EVFILT_AIO,
#[cfg(target_os = "freebsd")]
/// Returns whenever there is no remaining data in the write buffer
EVFILT_EMPTY,
#[cfg(target_os = "dragonfly")]
/// Takes a descriptor as the identifier, and returns whenever one of
/// the specified exceptional conditions has occurred on the descriptor.
EVFILT_EXCEPT,
#[cfg(any(freebsdlike, apple_targets))]
/// Establishes a file system monitor.
EVFILT_FS,
#[cfg(target_os = "freebsd")]
/// Notify for completion of a list of POSIX AIO operations.
/// # See Also
/// [lio_listio(2)](https://www.freebsd.org/cgi/man.cgi?query=lio_listio)
EVFILT_LIO,
#[cfg(apple_targets)]
/// Mach portsets
EVFILT_MACHPORT,
/// Notifies when a process performs one or more of the requested
/// events.
EVFILT_PROC,
/// Returns events associated with the process referenced by a given
/// process descriptor, created by `pdfork()`. The events to monitor are:
///
/// - NOTE_EXIT: the process has exited. The exit status will be stored in data.
#[cfg(target_os = "freebsd")]
EVFILT_PROCDESC,
/// Takes a file descriptor as the identifier, and notifies whenever
/// there is data available to read.
EVFILT_READ,
#[cfg(target_os = "freebsd")]
#[doc(hidden)]
#[deprecated(since = "0.27.0", note = "Never fully implemented by the OS")]
EVFILT_SENDFILE,
/// Takes a signal number to monitor as the identifier and notifies when
/// the given signal is delivered to the process.
EVFILT_SIGNAL,
/// Establishes a timer and notifies when the timer expires.
EVFILT_TIMER,
#[cfg(any(freebsdlike, apple_targets))]
/// Notifies only when explicitly requested by the user.
EVFILT_USER,
#[cfg(apple_targets)]
/// Virtual memory events
EVFILT_VM,
/// Notifies when a requested event happens on a specified file.
EVFILT_VNODE,
/// Takes a file descriptor as the identifier, and notifies whenever
/// it is possible to write to the file without blocking.
EVFILT_WRITE,
}
impl TryFrom<type_of_event_filter>
}
#[cfg(any(freebsdlike, apple_targets, target_os = "openbsd"))]
#[doc(hidden)]
pub type type_of_event_flag = u16;
#[cfg(target_os = "netbsd")]
#[doc(hidden)]
pub type type_of_event_flag = u32;
libc_bitflags! {
/// Event flags. See the man page for details.
// There's no useful documentation we can write for the individual flags
// that wouldn't simply be repeating the man page.
pub struct EvFlags: type_of_event_flag {
#[allow(missing_docs)]
EV_ADD;
#[allow(missing_docs)]
EV_CLEAR;
#[allow(missing_docs)]
EV_DELETE;
#[allow(missing_docs)]
EV_DISABLE;
#[cfg(bsd)]
#[allow(missing_docs)]
EV_DISPATCH;
#[cfg(target_os = "freebsd")]
#[allow(missing_docs)]
EV_DROP;
#[allow(missing_docs)]
EV_ENABLE;
#[allow(missing_docs)]
EV_EOF;
#[allow(missing_docs)]
EV_ERROR;
#[cfg(apple_targets)]
#[allow(missing_docs)]
EV_FLAG0;
#[allow(missing_docs)]
EV_FLAG1;
#[cfg(target_os = "dragonfly")]
#[allow(missing_docs)]
EV_NODATA;
#[allow(missing_docs)]
EV_ONESHOT;
#[cfg(apple_targets)]
#[allow(missing_docs)]
EV_OOBAND;
#[cfg(apple_targets)]
#[allow(missing_docs)]
EV_POLL;
#[cfg(bsd)]
#[allow(missing_docs)]
EV_RECEIPT;
}
}
#[deprecated(since = "0.30.0", note = "Use `EvFlags instead`")]
/// The deprecated EventFlag type alias
pub type EventFlag = EvFlags;
libc_bitflags!(
/// Filter-specific flags. See the man page for details.
// There's no useful documentation we can write for the individual flags
// that wouldn't simply be repeating the man page.
#[allow(missing_docs)]
pub struct FilterFlag: u32 {
#[cfg(apple_targets)]
#[allow(missing_docs)]
NOTE_ABSOLUTE;
#[allow(missing_docs)]
NOTE_ATTRIB;
#[allow(missing_docs)]
NOTE_CHILD;
#[allow(missing_docs)]
NOTE_DELETE;
#[cfg(target_os = "openbsd")]
#[allow(missing_docs)]
NOTE_EOF;
#[allow(missing_docs)]
NOTE_EXEC;
#[allow(missing_docs)]
NOTE_EXIT;
#[cfg(apple_targets)]
#[allow(missing_docs)]
NOTE_EXITSTATUS;
#[allow(missing_docs)]
NOTE_EXTEND;
#[cfg(any(apple_targets, freebsdlike))]
#[allow(missing_docs)]
NOTE_FFAND;
#[cfg(any(apple_targets, freebsdlike))]
#[allow(missing_docs)]
NOTE_FFCOPY;
#[cfg(any(apple_targets, freebsdlike))]
#[allow(missing_docs)]
NOTE_FFCTRLMASK;
#[cfg(any(apple_targets, freebsdlike))]
#[allow(missing_docs)]
NOTE_FFLAGSMASK;
#[cfg(any(apple_targets, freebsdlike))]
#[allow(missing_docs)]
NOTE_FFNOP;
#[cfg(any(apple_targets, freebsdlike))]
#[allow(missing_docs)]
NOTE_FFOR;
#[allow(missing_docs)]
NOTE_FORK;
#[allow(missing_docs)]
NOTE_LINK;
#[allow(missing_docs)]
NOTE_LOWAT;
#[cfg(target_os = "freebsd")]
#[allow(missing_docs)]
NOTE_MSECONDS;
#[cfg(apple_targets)]
#[allow(missing_docs)]
NOTE_NONE;
#[cfg(any(
apple_targets,
target_os = "freebsd"))]
#[allow(missing_docs)]
NOTE_NSECONDS;
#[cfg(target_os = "dragonfly")]
#[allow(missing_docs)]
NOTE_OOB;
#[allow(missing_docs)]
NOTE_PCTRLMASK;
#[allow(missing_docs)]
NOTE_PDATAMASK;
#[allow(missing_docs)]
NOTE_RENAME;
#[allow(missing_docs)]
NOTE_REVOKE;
#[cfg(any(
apple_targets,
target_os = "freebsd"))]
#[allow(missing_docs)]
NOTE_SECONDS;
#[cfg(apple_targets)]
#[allow(missing_docs)]
NOTE_SIGNAL;
#[allow(missing_docs)]
NOTE_TRACK;
#[allow(missing_docs)]
NOTE_TRACKERR;
#[cfg(any(apple_targets, freebsdlike))]
#[allow(missing_docs)]
NOTE_TRIGGER;
#[cfg(target_os = "openbsd")]
#[allow(missing_docs)]
NOTE_TRUNCATE;
#[cfg(any(
apple_targets,
target_os = "freebsd"))]
#[allow(missing_docs)]
NOTE_USECONDS;
#[cfg(apple_targets)]
#[allow(missing_docs)]
NOTE_VM_ERROR;
#[cfg(apple_targets)]
#[allow(missing_docs)]
NOTE_VM_PRESSURE;
#[cfg(apple_targets)]
#[allow(missing_docs)]
NOTE_VM_PRESSURE_SUDDEN_TERMINATE;
#[cfg(apple_targets)]
#[allow(missing_docs)]
NOTE_VM_PRESSURE_TERMINATE;
#[allow(missing_docs)]
NOTE_WRITE;
}
);
#[allow(missing_docs)]
#[deprecated(since = "0.27.0", note = "Use KEvent::new instead")]
pub fn kqueue() -> Result<Kqueue> {
Kqueue::new()
}
// KEvent can't derive Send because on some operating systems, udata is defined
// as a void*. However, KEvent's public API always treats udata as an intptr_t,
// which is safe to Send.
unsafe impl Send for KEvent {}
impl KEvent {
#[allow(clippy::needless_update)] // Not needless on all platforms.
/// Construct a new `KEvent` suitable for submission to the kernel via the
/// `changelist` argument of [`Kqueue::kevent`].
pub fn new(
ident: uintptr_t,
filter: EventFilter,
flags: EvFlags,
fflags: FilterFlag,
data: intptr_t,
udata: intptr_t,
) -> KEvent {
KEvent {
kevent: libc::kevent {
ident,
filter: filter as type_of_event_filter,
flags: flags.bits(),
fflags: fflags.bits(),
// data can be either i64 or intptr_t, depending on platform
data: data as _,
udata: udata as type_of_udata,
..unsafe { mem::zeroed() }
},
}
}
/// Value used to identify this event. The exact interpretation is
/// determined by the attached filter, but often is a raw file descriptor.
pub fn ident(&self) -> uintptr_t {
self.kevent.ident
}
/// Identifies the kernel filter used to process this event.
///
/// Will only return an error if the kernel reports an event via a filter
/// that is unknown to Nix.
pub fn filter(&self) -> Result<EventFilter> {
self.kevent.filter.try_into()
}
/// Flags control what the kernel will do when this event is added with
/// [`Kqueue::kevent`].
pub fn flags(&self) -> EvFlags {
EvFlags::from_bits(self.kevent.flags).unwrap()
}
/// Filter-specific flags.
pub fn fflags(&self) -> FilterFlag {
FilterFlag::from_bits(self.kevent.fflags).unwrap()
}
/// Filter-specific data value.
pub fn data(&self) -> intptr_t {
self.kevent.data as intptr_t
}
/// Opaque user-defined value passed through the kernel unchanged.
pub fn udata(&self) -> intptr_t {
self.kevent.udata as intptr_t
}
}
#[allow(missing_docs)]
#[deprecated(since = "0.27.0", note = "Use Kqueue::kevent instead")]
pub fn kevent(
kq: &Kqueue,
changelist: &[KEvent],
eventlist: &mut [KEvent],
timeout_ms: usize,
) -> Result<usize> {
// Convert ms to timespec
let timeout = timespec {
tv_sec: (timeout_ms / 1000) as time_t,
tv_nsec: ((timeout_ms % 1000) * 1_000_000) as c_long,
};
kq.kevent(changelist, eventlist, Some(timeout))
}
#[cfg(any(apple_targets, freebsdlike, target_os = "openbsd"))]
type type_of_nchanges = c_int;
#[cfg(target_os = "netbsd")]
type type_of_nchanges = size_t;
#[allow(missing_docs)]
#[deprecated(since = "0.27.0", note = "Use Kqueue::kevent instead")]
pub fn kevent_ts(
kq: &Kqueue,
changelist: &[KEvent],
eventlist: &mut [KEvent],
timeout_opt: Option<timespec>,
) -> Result<usize> {
kq.kevent(changelist, eventlist, timeout_opt)
}
/// Modify an existing [`KEvent`].
// Probably should deprecate. Would anybody ever use it over `KEvent::new`?
#[deprecated(since = "0.27.0", note = "Use Kqueue::kevent instead")]
#[inline]
pub fn ev_set(
ev: &mut KEvent,
ident: usize,
filter: EventFilter,
flags: EvFlags,
fflags: FilterFlag,
udata: intptr_t,
) {
ev.kevent.ident = ident as uintptr_t;
ev.kevent.filter = filter as type_of_event_filter;
ev.kevent.flags = flags.bits();
ev.kevent.fflags = fflags.bits();
ev.kevent.data = 0;
ev.kevent.udata = udata as type_of_udata;
}

118
vendor/nix/src/sys/eventfd.rs vendored Normal file
View File

@@ -0,0 +1,118 @@
use crate::errno::Errno;
use crate::{unistd, Result};
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd};
libc_bitflags! {
/// Eventfd flags.
pub struct EfdFlags: libc::c_int {
/// Set the close-on-exec (`FD_CLOEXEC`) flag on the new event file descriptor.
EFD_CLOEXEC; // Since Linux 2.6.27/FreeBSD 13.0
/// Set the `O_NONBLOCK` file status flag on the new event file description.
EFD_NONBLOCK; // Since Linux 2.6.27/FreeBSD 13.0
/// Provide semaphore-like semantics for reads from the new event file
/// descriptor.
EFD_SEMAPHORE; // Since Linux 2.6.30/FreeBSD 13.0
}
}
#[deprecated(
since = "0.28.0",
note = "Use EventFd::from_value_and_flags() instead"
)]
#[allow(missing_docs)]
pub fn eventfd(initval: libc::c_uint, flags: EfdFlags) -> Result<OwnedFd> {
let res = unsafe { libc::eventfd(initval, flags.bits()) };
Errno::result(res).map(|r| unsafe { OwnedFd::from_raw_fd(r) })
}
/// An eventfd file descriptor.
#[derive(Debug)]
#[repr(transparent)]
pub struct EventFd(OwnedFd);
impl EventFd {
/// [`EventFd::from_value_and_flags`] with `init_val = 0` and `flags = EfdFlags::empty()`.
pub fn new() -> Result<Self> {
Self::from_value_and_flags(0, EfdFlags::empty())
}
/// Constructs [`EventFd`] with the given `init_val` and `flags`.
///
/// Wrapper around [`libc::eventfd`].
pub fn from_value_and_flags(
init_val: u32,
flags: EfdFlags,
) -> Result<Self> {
let res = unsafe { libc::eventfd(init_val, flags.bits()) };
Errno::result(res).map(|r| Self(unsafe { OwnedFd::from_raw_fd(r) }))
}
/// [`EventFd::from_value_and_flags`] with `init_val = 0` and given `flags`.
pub fn from_flags(flags: EfdFlags) -> Result<Self> {
Self::from_value_and_flags(0, flags)
}
/// [`EventFd::from_value_and_flags`] with given `init_val` and `flags = EfdFlags::empty()`.
pub fn from_value(init_val: u32) -> Result<Self> {
Self::from_value_and_flags(init_val, EfdFlags::empty())
}
/// Constructs an `EventFd` wrapping an existing `OwnedFd`.
///
/// # Safety
///
/// `OwnedFd` is a valid eventfd.
pub unsafe fn from_owned_fd(fd: OwnedFd) -> Self {
Self(fd)
}
/// Enqueues `value` triggers, i.e., adds the integer value supplied in `value`
/// to the counter.
///
/// The next `value` calls to `poll`, `select` or `epoll` will return immediately.
///
/// [`EventFd::write`] with `value`.
pub fn write(&self, value: u64) -> Result<usize> {
unistd::write(&self.0, &value.to_ne_bytes())
}
/// Reads the value from the file descriptor.
///
/// * If [`EFD_SEMAPHORE`](EfdFlags::EFD_SEMAPHORE) was not specified and
/// the eventfd counter has a nonzero value, then this function returns
/// an `u64` containing that value, and the counter's value is reset to
/// zero.
///
/// * If [`EFD_SEMAPHORE`](EfdFlags::EFD_SEMAPHORE) was specified and the
/// eventfd counter has a nonzero value, then this function returns an
/// `u64` containing the value 1, and the counter's value is decremented
/// by 1.
///
/// * If the eventfd counter is zero at the time of this call, then the
/// call either blocks until the counter becomes nonzero (at which time,
/// this function proceeds as described above) or fails with the error
/// `EAGAIN` if the file descriptor has been made nonblocking with
/// [`EFD_NONBLOCK`](EfdFlags::EFD_NONBLOCK).
pub fn read(&self) -> Result<u64> {
let mut arr = [0; std::mem::size_of::<u64>()];
unistd::read(&self.0, &mut arr)?;
Ok(u64::from_ne_bytes(arr))
}
}
impl AsFd for EventFd {
fn as_fd(&self) -> BorrowedFd {
self.0.as_fd()
}
}
impl AsRawFd for EventFd {
fn as_raw_fd(&self) -> RawFd {
self.0.as_raw_fd()
}
}
impl From<EventFd> for OwnedFd {
fn from(value: EventFd) -> Self {
value.0
}
}

446
vendor/nix/src/sys/fanotify.rs vendored Normal file
View File

@@ -0,0 +1,446 @@
//! Monitoring API for filesystem events.
//!
//! Fanotify is a Linux-only API to monitor filesystems events.
//!
//! Additional capabilities compared to the `inotify` API include the ability to
//! monitor all of the objects in a mounted filesystem, the ability to make
//! access permission decisions, and the possibility to read or modify files
//! before access by other applications.
//!
//! For more documentation, please read
//! [fanotify(7)](https://man7.org/linux/man-pages/man7/fanotify.7.html).
use crate::errno::Errno;
use crate::fcntl::OFlag;
use crate::unistd::{close, read, write};
use crate::{NixPath, Result};
use std::marker::PhantomData;
use std::mem::{size_of, MaybeUninit};
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd};
use std::ptr;
libc_bitflags! {
/// Mask for defining which events shall be listened with [`Fanotify::mark()`]
/// and for querying notifications.
pub struct MaskFlags: u64 {
/// File was accessed.
FAN_ACCESS;
/// File was modified.
FAN_MODIFY;
/// Metadata has changed. Since Linux 5.1.
FAN_ATTRIB;
/// Writtable file was closed.
FAN_CLOSE_WRITE;
/// Unwrittable file was closed.
FAN_CLOSE_NOWRITE;
/// File was opened.
FAN_OPEN;
/// File was moved from X. Since Linux 5.1.
FAN_MOVED_FROM;
/// File was moved to Y. Since Linux 5.1.
FAN_MOVED_TO;
/// Subfile was created. Since Linux 5.1.
FAN_CREATE;
/// Subfile was deleted. Since Linux 5.1.
FAN_DELETE;
/// Self was deleted. Since Linux 5.1.
FAN_DELETE_SELF;
/// Self was moved. Since Linux 5.1.
FAN_MOVE_SELF;
/// File was opened for execution. Since Linux 5.0.
FAN_OPEN_EXEC;
/// Event queue overflowed.
FAN_Q_OVERFLOW;
/// Filesystem error. Since Linux 5.16.
FAN_FS_ERROR;
/// Permission to open file was requested.
FAN_OPEN_PERM;
/// Permission to access file was requested.
FAN_ACCESS_PERM;
/// Permission to open file for execution was requested. Since Linux
/// 5.0.
FAN_OPEN_EXEC_PERM;
/// Interested in child events.
FAN_EVENT_ON_CHILD;
/// File was renamed. Since Linux 5.17.
FAN_RENAME;
/// Event occurred against dir.
FAN_ONDIR;
/// Combination of `FAN_CLOSE_WRITE` and `FAN_CLOSE_NOWRITE`.
FAN_CLOSE;
/// Combination of `FAN_MOVED_FROM` and `FAN_MOVED_TO`.
FAN_MOVE;
}
}
libc_bitflags! {
/// Configuration options for [`Fanotify::init()`].
pub struct InitFlags: libc::c_uint {
/// Close-on-exec flag set on the file descriptor.
FAN_CLOEXEC;
/// Nonblocking flag set on the file descriptor.
FAN_NONBLOCK;
/// Receipt of events notifications.
FAN_CLASS_NOTIF;
/// Receipt of events for permission decisions, after they contain final
/// data.
FAN_CLASS_CONTENT;
/// Receipt of events for permission decisions, before they contain
/// final data.
FAN_CLASS_PRE_CONTENT;
/// Remove the limit on the number of events in the event queue.
///
/// Prior to Linux kernel 5.13, this limit was hardcoded to 16384. After
/// 5.13, one can change it via file `/proc/sys/fs/fanotify/max_queued_events`.
///
/// See `fanotify(7)` for details about this limit. Use of this flag
/// requires the `CAP_SYS_ADMIN` capability.
FAN_UNLIMITED_QUEUE;
/// Remove the limit on the number of fanotify marks per user.
///
/// Prior to Linux kernel 5.13, this limit was hardcoded to 8192 (per
/// group, not per user). After 5.13, one can change it via file
/// `/proc/sys/fs/fanotify/max_user_marks`.
///
/// See `fanotify(7)` for details about this limit. Use of this flag
/// requires the `CAP_SYS_ADMIN` capability.
FAN_UNLIMITED_MARKS;
/// Make `FanotifyEvent::pid` return pidfd. Since Linux 5.15.
FAN_REPORT_PIDFD;
/// Make `FanotifyEvent::pid` return thread id. Since Linux 4.20.
FAN_REPORT_TID;
}
}
libc_bitflags! {
/// File status flags for fanotify events file descriptors.
pub struct EventFFlags: libc::c_uint {
/// Read only access.
O_RDONLY as libc::c_uint;
/// Write only access.
O_WRONLY as libc::c_uint;
/// Read and write access.
O_RDWR as libc::c_uint;
/// Support for files exceeded 2 GB.
O_LARGEFILE as libc::c_uint;
/// Close-on-exec flag for the file descriptor. Since Linux 3.18.
O_CLOEXEC as libc::c_uint;
/// Append mode for the file descriptor.
O_APPEND as libc::c_uint;
/// Synchronized I/O data integrity completion.
O_DSYNC as libc::c_uint;
/// No file last access time update.
O_NOATIME as libc::c_uint;
/// Nonblocking mode for the file descriptor.
O_NONBLOCK as libc::c_uint;
/// Synchronized I/O file integrity completion.
O_SYNC as libc::c_uint;
}
}
impl TryFrom<OFlag> for EventFFlags {
type Error = Errno;
fn try_from(o_flag: OFlag) -> Result<Self> {
EventFFlags::from_bits(o_flag.bits() as u32).ok_or(Errno::EINVAL)
}
}
impl From<EventFFlags> for OFlag {
fn from(event_f_flags: EventFFlags) -> Self {
OFlag::from_bits_retain(event_f_flags.bits() as i32)
}
}
libc_bitflags! {
/// Configuration options for [`Fanotify::mark()`].
pub struct MarkFlags: libc::c_uint {
/// Add the events to the marks.
FAN_MARK_ADD;
/// Remove the events to the marks.
FAN_MARK_REMOVE;
/// Don't follow symlinks, mark them.
FAN_MARK_DONT_FOLLOW;
/// Raise an error if filesystem to be marked is not a directory.
FAN_MARK_ONLYDIR;
/// Events added to or removed from the marks.
FAN_MARK_IGNORED_MASK;
/// Ignore mask shall survive modify events.
FAN_MARK_IGNORED_SURV_MODIFY;
/// Remove all marks.
FAN_MARK_FLUSH;
/// Do not pin inode object in the inode cache. Since Linux 5.19.
FAN_MARK_EVICTABLE;
/// Events added to or removed from the marks. Since Linux 6.0.
FAN_MARK_IGNORE;
/// Default flag.
FAN_MARK_INODE;
/// Mark the mount specified by pathname.
FAN_MARK_MOUNT;
/// Mark the filesystem specified by pathname. Since Linux 4.20.
FAN_MARK_FILESYSTEM;
/// Combination of `FAN_MARK_IGNORE` and `FAN_MARK_IGNORED_SURV_MODIFY`.
FAN_MARK_IGNORE_SURV;
}
}
/// Compile version number of fanotify API.
pub const FANOTIFY_METADATA_VERSION: u8 = libc::FANOTIFY_METADATA_VERSION;
/// Abstract over [`libc::fanotify_event_metadata`], which represents an event
/// received via [`Fanotify::read_events`].
// Is not Clone due to fd field, to avoid use-after-close scenarios.
#[derive(Debug, Eq, Hash, PartialEq)]
#[repr(transparent)]
#[allow(missing_copy_implementations)]
pub struct FanotifyEvent(libc::fanotify_event_metadata);
impl FanotifyEvent {
/// Version number for the structure. It must be compared to
/// `FANOTIFY_METADATA_VERSION` to verify compile version and runtime
/// version does match. It can be done with the
/// `FanotifyEvent::check_version` method.
pub fn version(&self) -> u8 {
self.0.vers
}
/// Checks that compile fanotify API version is equal to the version of the
/// event.
pub fn check_version(&self) -> bool {
self.version() == FANOTIFY_METADATA_VERSION
}
/// Mask flags of the events.
pub fn mask(&self) -> MaskFlags {
MaskFlags::from_bits_truncate(self.0.mask)
}
/// The file descriptor of the event. If the value is `None` when reading
/// from the fanotify group, this event is to notify that a group queue
/// overflow occured.
pub fn fd(&self) -> Option<BorrowedFd> {
if self.0.fd == libc::FAN_NOFD {
None
} else {
// SAFETY: self.0.fd will be opened for the lifetime of `Self`,
// which is longer than the lifetime of the returned BorrowedFd, so
// it is safe.
Some(unsafe { BorrowedFd::borrow_raw(self.0.fd) })
}
}
/// PID of the process that caused the event. TID in case flag
/// `FAN_REPORT_TID` was set at group initialization.
pub fn pid(&self) -> i32 {
self.0.pid
}
}
impl Drop for FanotifyEvent {
fn drop(&mut self) {
if self.0.fd == libc::FAN_NOFD {
return;
}
let e = close(self.0.fd);
if !std::thread::panicking() && e == Err(Errno::EBADF) {
panic!("Closing an invalid file descriptor!");
};
}
}
/// Abstraction over the structure to be sent to allow or deny a given event.
#[derive(Debug)]
#[repr(transparent)]
pub struct FanotifyResponse<'a> {
inner: libc::fanotify_response,
_borrowed_fd: PhantomData<BorrowedFd<'a>>,
}
impl<'a> FanotifyResponse<'a> {
/// Create a new response.
pub fn new(fd: BorrowedFd<'a>, response: Response) -> Self {
Self {
inner: libc::fanotify_response {
fd: fd.as_raw_fd(),
response: response.bits(),
},
_borrowed_fd: PhantomData,
}
}
}
libc_bitflags! {
/// Response to be wrapped in [`FanotifyResponse`] and sent to the [`Fanotify`]
/// group to allow or deny an event.
pub struct Response: u32 {
/// Allow the event.
FAN_ALLOW;
/// Deny the event.
FAN_DENY;
}
}
/// A fanotify group. This is also a file descriptor that can feed to other
/// interfaces consuming file descriptors.
#[derive(Debug)]
pub struct Fanotify {
fd: OwnedFd,
}
impl Fanotify {
/// Initialize a new fanotify group.
///
/// Returns a Result containing a Fanotify instance.
///
/// For more information, see [fanotify_init(2)](https://man7.org/linux/man-pages/man7/fanotify_init.2.html).
pub fn init(
flags: InitFlags,
event_f_flags: EventFFlags,
) -> Result<Fanotify> {
let res = Errno::result(unsafe {
libc::fanotify_init(flags.bits(), event_f_flags.bits())
});
res.map(|fd| Fanotify {
fd: unsafe { OwnedFd::from_raw_fd(fd) },
})
}
/// Add, remove, or modify an fanotify mark on a filesystem object.
///
/// Returns a Result containing either `()` on success or errno otherwise.
///
/// For more information, see [fanotify_mark(2)](https://man7.org/linux/man-pages/man7/fanotify_mark.2.html).
pub fn mark<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
&self,
flags: MarkFlags,
mask: MaskFlags,
dirfd: Fd,
path: Option<&P>,
) -> Result<()> {
let res = crate::with_opt_nix_path(path, |p| unsafe {
libc::fanotify_mark(
self.fd.as_raw_fd(),
flags.bits(),
mask.bits(),
dirfd.as_fd().as_raw_fd(),
p,
)
})?;
Errno::result(res).map(|_| ())
}
/// Read incoming events from the fanotify group.
///
/// Returns a Result containing either a `Vec` of events on success or errno
/// otherwise.
///
/// # Errors
///
/// Possible errors can be those that are explicitly listed in
/// [fanotify(2)](https://man7.org/linux/man-pages/man7/fanotify.2.html) in
/// addition to the possible errors caused by `read` call.
/// In particular, `EAGAIN` is returned when no event is available on a
/// group that has been initialized with the flag `InitFlags::FAN_NONBLOCK`,
/// thus making this method nonblocking.
pub fn read_events(&self) -> Result<Vec<FanotifyEvent>> {
let metadata_size = size_of::<libc::fanotify_event_metadata>();
const BUFSIZ: usize = 4096;
let mut buffer = [0u8; BUFSIZ];
let mut events = Vec::new();
let mut offset = 0;
let nread = read(&self.fd, &mut buffer)?;
while (nread - offset) >= metadata_size {
let metadata = unsafe {
let mut metadata =
MaybeUninit::<libc::fanotify_event_metadata>::uninit();
ptr::copy_nonoverlapping(
buffer.as_ptr().add(offset),
metadata.as_mut_ptr().cast(),
(BUFSIZ - offset).min(metadata_size),
);
metadata.assume_init()
};
events.push(FanotifyEvent(metadata));
offset += metadata.event_len as usize;
}
Ok(events)
}
/// Write an event response on the fanotify group.
///
/// Returns a Result containing either `()` on success or errno otherwise.
///
/// # Errors
///
/// Possible errors can be those that are explicitly listed in
/// [fanotify(2)](https://man7.org/linux/man-pages/man7/fanotify.2.html) in
/// addition to the possible errors caused by `write` call.
/// In particular, `EAGAIN` or `EWOULDBLOCK` is returned when no event is
/// available on a group that has been initialized with the flag
/// `InitFlags::FAN_NONBLOCK`, thus making this method nonblocking.
pub fn write_response(&self, response: FanotifyResponse) -> Result<()> {
write(self.fd.as_fd(), unsafe {
std::slice::from_raw_parts(
(&response.inner as *const libc::fanotify_response).cast(),
size_of::<libc::fanotify_response>(),
)
})?;
Ok(())
}
}
impl FromRawFd for Fanotify {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
Fanotify {
fd: unsafe { OwnedFd::from_raw_fd(fd) },
}
}
}
impl AsFd for Fanotify {
fn as_fd(&'_ self) -> BorrowedFd<'_> {
self.fd.as_fd()
}
}
impl AsRawFd for Fanotify {
fn as_raw_fd(&self) -> RawFd
{
self.fd.as_raw_fd()
}
}
impl From<Fanotify> for OwnedFd {
fn from(value: Fanotify) -> Self {
value.fd
}
}
impl Fanotify {
/// Constructs a `Fanotify` wrapping an existing `OwnedFd`.
///
/// # Safety
///
/// `OwnedFd` is a valid `Fanotify`.
pub unsafe fn from_owned_fd(fd: OwnedFd) -> Self {
Self {
fd
}
}
}

273
vendor/nix/src/sys/inotify.rs vendored Normal file
View File

@@ -0,0 +1,273 @@
//! Monitoring API for filesystem events.
//!
//! Inotify is a Linux-only API to monitor filesystems events.
//!
//! For more documentation, please read [inotify(7)](https://man7.org/linux/man-pages/man7/inotify.7.html).
//!
//! # Examples
//!
//! Monitor all events happening in directory "test":
//! ```no_run
//! # use nix::sys::inotify::{AddWatchFlags,InitFlags,Inotify};
//! #
//! // We create a new inotify instance.
//! let instance = Inotify::init(InitFlags::empty()).unwrap();
//!
//! // We add a new watch on directory "test" for all events.
//! let wd = instance.add_watch("test", AddWatchFlags::IN_ALL_EVENTS).unwrap();
//!
//! loop {
//! // We read from our inotify instance for events.
//! let events = instance.read_events().unwrap();
//! println!("Events: {:?}", events);
//! }
//! ```
use crate::errno::Errno;
use crate::unistd::read;
use crate::NixPath;
use crate::Result;
use cfg_if::cfg_if;
use libc::{c_char, c_int};
use std::ffi::{CStr, OsStr, OsString};
use std::mem::{size_of, MaybeUninit};
use std::os::unix::ffi::OsStrExt;
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd};
use std::ptr;
libc_bitflags! {
/// Configuration options for [`inotify_add_watch`](fn.inotify_add_watch.html).
pub struct AddWatchFlags: u32 {
/// File was accessed.
IN_ACCESS;
/// File was modified.
IN_MODIFY;
/// Metadata changed.
IN_ATTRIB;
/// Writable file was closed.
IN_CLOSE_WRITE;
/// Nonwritable file was closed.
IN_CLOSE_NOWRITE;
/// File was opened.
IN_OPEN;
/// File was moved from X.
IN_MOVED_FROM;
/// File was moved to Y.
IN_MOVED_TO;
/// Subfile was created.
IN_CREATE;
/// Subfile was deleted.
IN_DELETE;
/// Self was deleted.
IN_DELETE_SELF;
/// Self was moved.
IN_MOVE_SELF;
/// Backing filesystem was unmounted.
IN_UNMOUNT;
/// Event queue overflowed.
IN_Q_OVERFLOW;
/// File was ignored.
IN_IGNORED;
/// Combination of `IN_CLOSE_WRITE` and `IN_CLOSE_NOWRITE`.
IN_CLOSE;
/// Combination of `IN_MOVED_FROM` and `IN_MOVED_TO`.
IN_MOVE;
/// Only watch the path if it is a directory.
IN_ONLYDIR;
/// Don't follow symlinks.
IN_DONT_FOLLOW;
/// Event occurred against directory.
IN_ISDIR;
/// Only send event once.
IN_ONESHOT;
/// All of the events.
IN_ALL_EVENTS;
}
}
libc_bitflags! {
/// Configuration options for [`inotify_init1`](fn.inotify_init1.html).
pub struct InitFlags: c_int {
/// Set the `FD_CLOEXEC` flag on the file descriptor.
IN_CLOEXEC;
/// Set the `O_NONBLOCK` flag on the open file description referred to by the new file descriptor.
IN_NONBLOCK;
}
}
/// An inotify instance. This is also a file descriptor, you can feed it to
/// other interfaces consuming file descriptors, epoll for example.
#[derive(Debug)]
pub struct Inotify {
fd: OwnedFd,
}
/// This object is returned when you create a new watch on an inotify instance.
/// It is then returned as part of an event once triggered. It allows you to
/// know which watch triggered which event.
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct WatchDescriptor {
wd: i32,
}
/// A single inotify event.
///
/// For more documentation see, [inotify(7)](https://man7.org/linux/man-pages/man7/inotify.7.html).
#[derive(Debug)]
pub struct InotifyEvent {
/// Watch descriptor. This field corresponds to the watch descriptor you
/// were issued when calling add_watch. It allows you to know which watch
/// this event comes from.
pub wd: WatchDescriptor,
/// Event mask. This field is a bitfield describing the exact event that
/// occured.
pub mask: AddWatchFlags,
/// This cookie is a number that allows you to connect related events. For
/// now only IN_MOVED_FROM and IN_MOVED_TO can be connected.
pub cookie: u32,
/// Filename. This field exists only if the event was triggered for a file
/// inside the watched directory.
pub name: Option<OsString>,
}
impl Inotify {
/// Initialize a new inotify instance.
///
/// Returns a Result containing an inotify instance.
///
/// For more information see, [inotify_init(2)](https://man7.org/linux/man-pages/man2/inotify_init.2.html).
pub fn init(flags: InitFlags) -> Result<Inotify> {
let res = Errno::result(unsafe { libc::inotify_init1(flags.bits()) });
res.map(|fd| Inotify {
fd: unsafe { OwnedFd::from_raw_fd(fd) },
})
}
/// Adds a new watch on the target file or directory.
///
/// Returns a watch descriptor. This is not a File Descriptor!
///
/// For more information see, [inotify_add_watch(2)](https://man7.org/linux/man-pages/man2/inotify_add_watch.2.html).
pub fn add_watch<P: ?Sized + NixPath>(
&self,
path: &P,
mask: AddWatchFlags,
) -> Result<WatchDescriptor> {
let res = path.with_nix_path(|cstr| unsafe {
libc::inotify_add_watch(
self.fd.as_raw_fd(),
cstr.as_ptr(),
mask.bits(),
)
})?;
Errno::result(res).map(|wd| WatchDescriptor { wd })
}
/// Removes an existing watch using the watch descriptor returned by
/// inotify_add_watch.
///
/// Returns an EINVAL error if the watch descriptor is invalid.
///
/// For more information see, [inotify_rm_watch(2)](https://man7.org/linux/man-pages/man2/inotify_rm_watch.2.html).
pub fn rm_watch(&self, wd: WatchDescriptor) -> Result<()> {
cfg_if! {
if #[cfg(target_os = "linux")] {
let arg = wd.wd;
} else if #[cfg(target_os = "android")] {
let arg = wd.wd as u32;
}
}
let res = unsafe { libc::inotify_rm_watch(self.fd.as_raw_fd(), arg) };
Errno::result(res).map(drop)
}
/// Reads a collection of events from the inotify file descriptor. This call
/// can either be blocking or non blocking depending on whether IN_NONBLOCK
/// was set at initialization.
///
/// Returns as many events as available. If the call was non blocking and no
/// events could be read then the EAGAIN error is returned.
pub fn read_events(&self) -> Result<Vec<InotifyEvent>> {
let header_size = size_of::<libc::inotify_event>();
const BUFSIZ: usize = 4096;
let mut buffer = [0u8; BUFSIZ];
let mut events = Vec::new();
let mut offset = 0;
let nread = read(&self.fd, &mut buffer)?;
while (nread - offset) >= header_size {
let event = unsafe {
let mut event = MaybeUninit::<libc::inotify_event>::uninit();
ptr::copy_nonoverlapping(
buffer.as_ptr().add(offset),
event.as_mut_ptr().cast(),
(BUFSIZ - offset).min(header_size),
);
event.assume_init()
};
let name = match event.len {
0 => None,
_ => {
let ptr = unsafe {
buffer.as_ptr().add(offset + header_size)
as *const c_char
};
let cstr = unsafe { CStr::from_ptr(ptr) };
Some(OsStr::from_bytes(cstr.to_bytes()).to_owned())
}
};
events.push(InotifyEvent {
wd: WatchDescriptor { wd: event.wd },
mask: AddWatchFlags::from_bits_truncate(event.mask),
cookie: event.cookie,
name,
});
offset += header_size + event.len as usize;
}
Ok(events)
}
/// Constructs an `Inotify` wrapping an existing `OwnedFd`.
///
/// # Safety
///
/// `OwnedFd` is a valid `Inotify`.
pub unsafe fn from_owned_fd(fd: OwnedFd) -> Self {
Self {
fd
}
}
}
impl FromRawFd for Inotify {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
Inotify {
fd: unsafe { OwnedFd::from_raw_fd(fd) },
}
}
}
impl AsFd for Inotify {
fn as_fd(&'_ self) -> BorrowedFd<'_> {
self.fd.as_fd()
}
}
impl From<Inotify> for OwnedFd {
fn from(value: Inotify) -> Self {
value.fd
}
}

129
vendor/nix/src/sys/ioctl/bsd.rs vendored Normal file
View File

@@ -0,0 +1,129 @@
/// The datatype used for the ioctl number
#[doc(hidden)]
#[cfg(not(solarish))]
pub type ioctl_num_type = ::libc::c_ulong;
#[doc(hidden)]
#[cfg(solarish)]
pub type ioctl_num_type = ::libc::c_int;
/// The datatype used for the 3rd argument
#[doc(hidden)]
pub type ioctl_param_type = ::libc::c_int;
mod consts {
use crate::sys::ioctl::ioctl_num_type;
#[doc(hidden)]
pub const VOID: ioctl_num_type = 0x2000_0000;
#[doc(hidden)]
pub const OUT: ioctl_num_type = 0x4000_0000;
#[doc(hidden)]
#[allow(overflowing_literals)]
pub const IN: ioctl_num_type = 0x8000_0000;
#[doc(hidden)]
pub const INOUT: ioctl_num_type = IN | OUT;
#[doc(hidden)]
pub const IOCPARM_MASK: ioctl_num_type = 0x1fff;
}
pub use self::consts::*;
#[macro_export]
#[doc(hidden)]
macro_rules! ioc {
($inout:expr, $group:expr, $num:expr, $len:expr) => {
$inout
| (($len as $crate::sys::ioctl::ioctl_num_type
& $crate::sys::ioctl::IOCPARM_MASK)
<< 16)
| (($group as $crate::sys::ioctl::ioctl_num_type) << 8)
| ($num as $crate::sys::ioctl::ioctl_num_type)
};
}
/// Generate an ioctl request code for a command that passes no data.
///
/// This is equivalent to the `_IO()` macro exposed by the C ioctl API.
///
/// You should only use this macro directly if the `ioctl` you're working
/// with is "bad" and you cannot use `ioctl_none!()` directly.
///
/// # Example
///
/// ```
/// # #[macro_use] extern crate nix;
/// const KVMIO: u8 = 0xAE;
/// ioctl_write_int_bad!(kvm_create_vm, request_code_none!(KVMIO, 0x03));
/// # fn main() {}
/// ```
#[macro_export(local_inner_macros)]
macro_rules! request_code_none {
($g:expr, $n:expr) => {
ioc!($crate::sys::ioctl::VOID, $g, $n, 0)
};
}
/// Generate an ioctl request code for a command that passes an integer
///
/// This is equivalent to the `_IOWINT()` macro exposed by the C ioctl API.
///
/// You should only use this macro directly if the `ioctl` you're working
/// with is "bad" and you cannot use `ioctl_write_int!()` directly.
#[macro_export(local_inner_macros)]
macro_rules! request_code_write_int {
($g:expr, $n:expr) => {
ioc!(
$crate::sys::ioctl::VOID,
$g,
$n,
::std::mem::size_of::<$crate::libc::c_int>()
)
};
}
/// Generate an ioctl request code for a command that reads.
///
/// This is equivalent to the `_IOR()` macro exposed by the C ioctl API.
///
/// You should only use this macro directly if the `ioctl` you're working
/// with is "bad" and you cannot use `ioctl_read!()` directly.
///
/// The read/write direction is relative to userland, so this
/// command would be userland is reading and the kernel is
/// writing.
#[macro_export(local_inner_macros)]
macro_rules! request_code_read {
($g:expr, $n:expr, $len:expr) => {
ioc!($crate::sys::ioctl::OUT, $g, $n, $len)
};
}
/// Generate an ioctl request code for a command that writes.
///
/// This is equivalent to the `_IOW()` macro exposed by the C ioctl API.
///
/// You should only use this macro directly if the `ioctl` you're working
/// with is "bad" and you cannot use `ioctl_write!()` directly.
///
/// The read/write direction is relative to userland, so this
/// command would be userland is writing and the kernel is
/// reading.
#[macro_export(local_inner_macros)]
macro_rules! request_code_write {
($g:expr, $n:expr, $len:expr) => {
ioc!($crate::sys::ioctl::IN, $g, $n, $len)
};
}
/// Generate an ioctl request code for a command that reads and writes.
///
/// This is equivalent to the `_IOWR()` macro exposed by the C ioctl API.
///
/// You should only use this macro directly if the `ioctl` you're working
/// with is "bad" and you cannot use `ioctl_readwrite!()` directly.
#[macro_export(local_inner_macros)]
macro_rules! request_code_readwrite {
($g:expr, $n:expr, $len:expr) => {
ioc!($crate::sys::ioctl::INOUT, $g, $n, $len)
};
}

180
vendor/nix/src/sys/ioctl/linux.rs vendored Normal file
View File

@@ -0,0 +1,180 @@
use cfg_if::cfg_if;
/// The datatype used for the ioctl number
#[cfg(any(
target_os = "android",
target_os = "fuchsia",
target_env = "musl",
target_env = "ohos"
))]
#[doc(hidden)]
pub type ioctl_num_type = ::libc::c_int;
#[cfg(not(any(
target_os = "android",
target_os = "fuchsia",
target_env = "musl",
target_env = "ohos"
)))]
#[doc(hidden)]
pub type ioctl_num_type = ::libc::c_ulong;
/// The datatype used for the 3rd argument
#[doc(hidden)]
pub type ioctl_param_type = ::libc::c_ulong;
#[doc(hidden)]
pub const NRBITS: ioctl_num_type = 8;
#[doc(hidden)]
pub const TYPEBITS: ioctl_num_type = 8;
cfg_if! {
if #[cfg(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "sparc64"
))] {
mod consts {
#[doc(hidden)]
pub const NONE: u8 = 1;
#[doc(hidden)]
pub const READ: u8 = 2;
#[doc(hidden)]
pub const WRITE: u8 = 4;
#[doc(hidden)]
pub const SIZEBITS: u8 = 13;
#[doc(hidden)]
pub const DIRBITS: u8 = 3;
}
} else {
// "Generic" ioctl protocol
mod consts {
#[doc(hidden)]
pub const NONE: u8 = 0;
#[doc(hidden)]
pub const READ: u8 = 2;
#[doc(hidden)]
pub const WRITE: u8 = 1;
#[doc(hidden)]
pub const SIZEBITS: u8 = 14;
#[doc(hidden)]
pub const DIRBITS: u8 = 2;
}
}
}
pub use self::consts::*;
#[doc(hidden)]
pub const NRSHIFT: ioctl_num_type = 0;
#[doc(hidden)]
pub const TYPESHIFT: ioctl_num_type = NRSHIFT + NRBITS as ioctl_num_type;
#[doc(hidden)]
pub const SIZESHIFT: ioctl_num_type = TYPESHIFT + TYPEBITS as ioctl_num_type;
#[doc(hidden)]
pub const DIRSHIFT: ioctl_num_type = SIZESHIFT + SIZEBITS as ioctl_num_type;
#[doc(hidden)]
pub const NRMASK: ioctl_num_type = (1 << NRBITS) - 1;
#[doc(hidden)]
pub const TYPEMASK: ioctl_num_type = (1 << TYPEBITS) - 1;
#[doc(hidden)]
pub const SIZEMASK: ioctl_num_type = (1 << SIZEBITS) - 1;
#[doc(hidden)]
pub const DIRMASK: ioctl_num_type = (1 << DIRBITS) - 1;
/// Encode an ioctl command.
#[macro_export]
#[doc(hidden)]
macro_rules! ioc {
($dir:expr, $ty:expr, $nr:expr, $sz:expr) => {
(($dir as $crate::sys::ioctl::ioctl_num_type
& $crate::sys::ioctl::DIRMASK)
<< $crate::sys::ioctl::DIRSHIFT)
| (($ty as $crate::sys::ioctl::ioctl_num_type
& $crate::sys::ioctl::TYPEMASK)
<< $crate::sys::ioctl::TYPESHIFT)
| (($nr as $crate::sys::ioctl::ioctl_num_type
& $crate::sys::ioctl::NRMASK)
<< $crate::sys::ioctl::NRSHIFT)
| (($sz as $crate::sys::ioctl::ioctl_num_type
& $crate::sys::ioctl::SIZEMASK)
<< $crate::sys::ioctl::SIZESHIFT)
};
}
/// Generate an ioctl request code for a command that passes no data.
///
/// This is equivalent to the `_IO()` macro exposed by the C ioctl API.
///
/// You should only use this macro directly if the `ioctl` you're working
/// with is "bad" and you cannot use `ioctl_none!()` directly.
///
/// # Example
///
/// ```
/// # #[macro_use] extern crate nix;
/// const KVMIO: u8 = 0xAE;
/// ioctl_write_int_bad!(kvm_create_vm, request_code_none!(KVMIO, 0x03));
/// # fn main() {}
/// ```
#[macro_export(local_inner_macros)]
macro_rules! request_code_none {
($ty:expr, $nr:expr) => {
ioc!($crate::sys::ioctl::NONE, $ty, $nr, 0)
};
}
/// Generate an ioctl request code for a command that reads.
///
/// This is equivalent to the `_IOR()` macro exposed by the C ioctl API.
///
/// You should only use this macro directly if the `ioctl` you're working
/// with is "bad" and you cannot use `ioctl_read!()` directly.
///
/// The read/write direction is relative to userland, so this
/// command would be userland is reading and the kernel is
/// writing.
#[macro_export(local_inner_macros)]
macro_rules! request_code_read {
($ty:expr, $nr:expr, $sz:expr) => {
ioc!($crate::sys::ioctl::READ, $ty, $nr, $sz)
};
}
/// Generate an ioctl request code for a command that writes.
///
/// This is equivalent to the `_IOW()` macro exposed by the C ioctl API.
///
/// You should only use this macro directly if the `ioctl` you're working
/// with is "bad" and you cannot use `ioctl_write!()` directly.
///
/// The read/write direction is relative to userland, so this
/// command would be userland is writing and the kernel is
/// reading.
#[macro_export(local_inner_macros)]
macro_rules! request_code_write {
($ty:expr, $nr:expr, $sz:expr) => {
ioc!($crate::sys::ioctl::WRITE, $ty, $nr, $sz)
};
}
/// Generate an ioctl request code for a command that reads and writes.
///
/// This is equivalent to the `_IOWR()` macro exposed by the C ioctl API.
///
/// You should only use this macro directly if the `ioctl` you're working
/// with is "bad" and you cannot use `ioctl_readwrite!()` directly.
#[macro_export(local_inner_macros)]
macro_rules! request_code_readwrite {
($ty:expr, $nr:expr, $sz:expr) => {
ioc!(
$crate::sys::ioctl::READ | $crate::sys::ioctl::WRITE,
$ty,
$nr,
$sz
)
};
}

798
vendor/nix/src/sys/ioctl/mod.rs vendored Normal file
View File

@@ -0,0 +1,798 @@
//! Provide helpers for making ioctl system calls.
//!
//! This library is pretty low-level and messy. `ioctl` is not fun.
//!
//! What is an `ioctl`?
//! ===================
//!
//! The `ioctl` syscall is the grab-bag syscall on POSIX systems. Don't want to add a new
//! syscall? Make it an `ioctl`! `ioctl` refers to both the syscall, and the commands that can be
//! sent with it. `ioctl` stands for "IO control", and the commands are always sent to a file
//! descriptor.
//!
//! It is common to see `ioctl`s used for the following purposes:
//!
//! * Provide read/write access to out-of-band data related to a device such as configuration
//! (for instance, setting serial port options)
//! * Provide a mechanism for performing full-duplex data transfers (for instance, xfer on SPI
//! devices).
//! * Provide access to control functions on a device (for example, on Linux you can send
//! commands like pause, resume, and eject to the CDROM device.
//! * Do whatever else the device driver creator thought made most sense.
//!
//! `ioctl`s are synchronous system calls and are similar to read and write calls in that regard.
//! They operate on file descriptors and have an identifier that specifies what the ioctl is.
//! Additionally they may read or write data and therefore need to pass along a data pointer.
//! Besides the semantics of the ioctls being confusing, the generation of this identifer can also
//! be difficult.
//!
//! Historically `ioctl` numbers were arbitrary hard-coded values. In Linux (before 2.6) and some
//! unices this has changed to a more-ordered system where the ioctl numbers are partitioned into
//! subcomponents (For linux this is documented in
//! [`Documentation/ioctl/ioctl-number.rst`](https://elixir.bootlin.com/linux/latest/source/Documentation/userspace-api/ioctl/ioctl-number.rst)):
//!
//! * Number: The actual ioctl ID
//! * Type: A grouping of ioctls for a common purpose or driver
//! * Size: The size in bytes of the data that will be transferred
//! * Direction: Whether there is any data and if it's read, write, or both
//!
//! Newer drivers should not generate complete integer identifiers for their `ioctl`s instead
//! preferring to use the 4 components above to generate the final ioctl identifier. Because of
//! how old `ioctl`s are, however, there are many hard-coded `ioctl` identifiers. These are
//! commonly referred to as "bad" in `ioctl` documentation.
//!
//! Defining `ioctl`s
//! =================
//!
//! This library provides several `ioctl_*!` macros for binding `ioctl`s. These generate public
//! unsafe functions that can then be used for calling the ioctl. This macro has a few different
//! ways it can be used depending on the specific ioctl you're working with.
//!
//! A simple `ioctl` is `SPI_IOC_RD_MODE`. This ioctl works with the SPI interface on Linux. This
//! specific `ioctl` reads the mode of the SPI device as a `u8`. It's declared in
//! `/include/uapi/linux/spi/spidev.h` as `_IOR(SPI_IOC_MAGIC, 1, __u8)`. Since it uses the `_IOR`
//! macro, we know it's a `read` ioctl and can use the `ioctl_read!` macro as follows:
//!
//! ```
//! # #[macro_use] extern crate nix;
//! const SPI_IOC_MAGIC: u8 = b'k'; // Defined in linux/spi/spidev.h
//! const SPI_IOC_TYPE_MODE: u8 = 1;
//! ioctl_read!(spi_read_mode, SPI_IOC_MAGIC, SPI_IOC_TYPE_MODE, u8);
//! # fn main() {}
//! ```
//!
//! This generates the function:
//!
//! ```
//! # #[macro_use] extern crate nix;
//! # use std::mem;
//! # use nix::{libc, Result};
//! # use nix::errno::Errno;
//! # use nix::libc::c_int as c_int;
//! # const SPI_IOC_MAGIC: u8 = b'k'; // Defined in linux/spi/spidev.h
//! # const SPI_IOC_TYPE_MODE: u8 = 1;
//! pub unsafe fn spi_read_mode(fd: c_int, data: *mut u8) -> Result<c_int> {
//! let res = unsafe { libc::ioctl(fd, request_code_read!(SPI_IOC_MAGIC, SPI_IOC_TYPE_MODE, mem::size_of::<u8>()), data) };
//! Errno::result(res)
//! }
//! # fn main() {}
//! ```
//!
//! The return value for the wrapper functions generated by the `ioctl_*!` macros are `nix::Error`s.
//! These are generated by assuming the return value of the ioctl is `-1` on error and everything
//! else is a valid return value. If this is not the case, `Result::map` can be used to map some
//! of the range of "good" values (-Inf..-2, 0..Inf) into a smaller range in a helper function.
//!
//! Writing `ioctl`s generally use pointers as their data source and these should use the
//! `ioctl_write_ptr!`. But in some cases an `int` is passed directly. For these `ioctl`s use the
//! `ioctl_write_int!` macro. This variant does not take a type as the last argument:
//!
//! ```
//! # #[macro_use] extern crate nix;
//! const HCI_IOC_MAGIC: u8 = b'k';
//! const HCI_IOC_HCIDEVUP: u8 = 1;
//! ioctl_write_int!(hci_dev_up, HCI_IOC_MAGIC, HCI_IOC_HCIDEVUP);
//! # fn main() {}
//! ```
//!
//! Some `ioctl`s don't transfer any data, and those should use `ioctl_none!`. This macro
//! doesn't take a type and so it is declared similar to the `write_int` variant shown above.
//!
//! The mode for a given `ioctl` should be clear from the documentation if it has good
//! documentation. Otherwise it will be clear based on the macro used to generate the `ioctl`
//! number where `_IO`, `_IOR`, `_IOW`, and `_IOWR` map to "none", "read", "write_*", and "readwrite"
//! respectively. To determine the specific `write_` variant to use you'll need to find
//! what the argument type is supposed to be. If it's an `int`, then `write_int` should be used,
//! otherwise it should be a pointer and `write_ptr` should be used. On Linux the
//! [`ioctl_list` man page](https://man7.org/linux/man-pages/man2/ioctl_list.2.html) describes a
//! large number of `ioctl`s and describes their argument data type.
//!
//! Using "bad" `ioctl`s
//! --------------------
//!
//! As mentioned earlier, there are many old `ioctl`s that do not use the newer method of
//! generating `ioctl` numbers and instead use hardcoded values. These can be used with the
//! `ioctl_*_bad!` macros. This naming comes from the Linux kernel which refers to these
//! `ioctl`s as "bad". These are a different variant as they bypass calling the macro that generates
//! the ioctl number and instead use the defined value directly.
//!
//! For example the `TCGETS` `ioctl` reads a `termios` data structure for a given file descriptor.
//! It's defined as `0x5401` in `ioctls.h` on Linux and can be implemented as:
//!
//! ```
//! # #[macro_use] extern crate nix;
//! # #[cfg(linux_android)]
//! # use nix::libc::TCGETS as TCGETS;
//! # #[cfg(linux_android)]
//! # use nix::libc::termios as termios;
//! # #[cfg(linux_android)]
//! ioctl_read_bad!(tcgets, TCGETS, termios);
//! # fn main() {}
//! ```
//!
//! The generated function has the same form as that generated by `ioctl_read!`:
//!
//! ```text
//! pub unsafe fn tcgets(fd: c_int, data: *mut termios) -> Result<c_int>;
//! ```
//!
//! Working with Arrays
//! -------------------
//!
//! Some `ioctl`s work with entire arrays of elements. These are supported by the `ioctl_*_buf`
//! family of macros: `ioctl_read_buf`, `ioctl_write_buf`, and `ioctl_readwrite_buf`. Note that
//! there are no "bad" versions for working with buffers. The generated functions include a `len`
//! argument to specify the number of elements (where the type of each element is specified in the
//! macro).
//!
//! Again looking to the SPI `ioctl`s on Linux for an example, there is a `SPI_IOC_MESSAGE` `ioctl`
//! that queues up multiple SPI messages by writing an entire array of `spi_ioc_transfer` structs.
//! `linux/spi/spidev.h` defines a macro to calculate the `ioctl` number like:
//!
//! ```C
//! #define SPI_IOC_MAGIC 'k'
//! #define SPI_MSGSIZE(N) ...
//! #define SPI_IOC_MESSAGE(N) _IOW(SPI_IOC_MAGIC, 0, char[SPI_MSGSIZE(N)])
//! ```
//!
//! The `SPI_MSGSIZE(N)` calculation is already handled by the `ioctl_*!` macros, so all that's
//! needed to define this `ioctl` is:
//!
//! ```
//! # #[macro_use] extern crate nix;
//! const SPI_IOC_MAGIC: u8 = b'k'; // Defined in linux/spi/spidev.h
//! const SPI_IOC_TYPE_MESSAGE: u8 = 0;
//! # pub struct spi_ioc_transfer(u64);
//! ioctl_write_buf!(spi_transfer, SPI_IOC_MAGIC, SPI_IOC_TYPE_MESSAGE, spi_ioc_transfer);
//! # fn main() {}
//! ```
//!
//! This generates a function like:
//!
//! ```
//! # #[macro_use] extern crate nix;
//! # use std::mem;
//! # use nix::{libc, Result};
//! # use nix::errno::Errno;
//! # use nix::libc::c_int as c_int;
//! # const SPI_IOC_MAGIC: u8 = b'k';
//! # const SPI_IOC_TYPE_MESSAGE: u8 = 0;
//! # pub struct spi_ioc_transfer(u64);
//! pub unsafe fn spi_message(fd: c_int, data: &mut [spi_ioc_transfer]) -> Result<c_int> {
//! let res = unsafe {
//! libc::ioctl(
//! fd,
//! request_code_write!(SPI_IOC_MAGIC, SPI_IOC_TYPE_MESSAGE, data.len() * mem::size_of::<spi_ioc_transfer>()),
//! data
//! )
//! };
//! Errno::result(res)
//! }
//! # fn main() {}
//! ```
//!
//! Finding `ioctl` Documentation
//! -----------------------------
//!
//! For Linux, look at your system's headers. For example, `/usr/include/linux/input.h` has a lot
//! of lines defining macros which use `_IO`, `_IOR`, `_IOW`, `_IOC`, and `_IOWR`. Some `ioctl`s are
//! documented directly in the headers defining their constants, but others have more extensive
//! documentation in man pages (like termios' `ioctl`s which are in `tty_ioctl(4)`).
//!
//! Documenting the Generated Functions
//! ===================================
//!
//! In many cases, users will wish for the functions generated by the `ioctl`
//! macro to be public and documented. For this reason, the generated functions
//! are public by default. If you wish to hide the ioctl, you will need to put
//! them in a private module.
//!
//! For documentation, it is possible to use doc comments inside the `ioctl_*!` macros. Here is an
//! example :
//!
//! ```
//! # #[macro_use] extern crate nix;
//! # use nix::libc::c_int;
//! ioctl_read! {
//! /// Make the given terminal the controlling terminal of the calling process. The calling
//! /// process must be a session leader and not have a controlling terminal already. If the
//! /// terminal is already the controlling terminal of a different session group then the
//! /// ioctl will fail with **EPERM**, unless the caller is root (more precisely: has the
//! /// **CAP_SYS_ADMIN** capability) and arg equals 1, in which case the terminal is stolen
//! /// and all processes that had it as controlling terminal lose it.
//! tiocsctty, b't', 19, c_int
//! }
//!
//! # fn main() {}
//! ```
use cfg_if::cfg_if;
#[cfg(any(linux_android, target_os = "fuchsia", target_os = "redox"))]
#[macro_use]
mod linux;
#[cfg(any(linux_android, target_os = "fuchsia", target_os = "redox"))]
pub use self::linux::*;
#[cfg(any(bsd, solarish, target_os = "haiku",))]
#[macro_use]
mod bsd;
#[cfg(any(bsd, solarish, target_os = "haiku",))]
pub use self::bsd::*;
/// Convert raw ioctl return value to a Nix result
#[macro_export]
#[doc(hidden)]
macro_rules! convert_ioctl_res {
($w:expr) => {{
$crate::errno::Errno::result($w)
}};
}
/// Generates a wrapper function for an ioctl that passes no data to the kernel.
///
/// The arguments to this macro are:
///
/// * The function name
/// * The ioctl identifier
/// * The ioctl sequence number
///
/// The generated function has the following signature:
///
/// ```rust,ignore
/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int) -> Result<libc::c_int>
/// ```
///
/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
///
/// # Example
///
/// The `videodev2` driver on Linux defines the `log_status` `ioctl` as:
///
/// ```C
/// #define VIDIOC_LOG_STATUS _IO('V', 70)
/// ```
///
/// This can be implemented in Rust like:
///
/// ```no_run
/// # #[macro_use] extern crate nix;
/// ioctl_none!(log_status, b'V', 70);
/// fn main() {}
/// ```
#[macro_export(local_inner_macros)]
macro_rules! ioctl_none {
($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr) => (
$(#[$attr])*
pub unsafe fn $name(fd: $crate::libc::c_int)
-> $crate::Result<$crate::libc::c_int> {
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_none!($ioty, $nr) as $crate::sys::ioctl::ioctl_num_type))
}
}
)
}
/// Generates a wrapper function for a "bad" ioctl that passes no data to the kernel.
///
/// The arguments to this macro are:
///
/// * The function name
/// * The ioctl request code
///
/// The generated function has the following signature:
///
/// ```rust,ignore
/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int) -> Result<libc::c_int>
/// ```
///
/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
///
/// # Example
///
/// ```no_run
/// # #[macro_use] extern crate nix;
/// # use libc::TIOCNXCL;
/// # use std::fs::File;
/// # use std::os::unix::io::AsRawFd;
/// ioctl_none_bad!(tiocnxcl, TIOCNXCL);
/// fn main() {
/// let file = File::open("/dev/ttyUSB0").unwrap();
/// unsafe { tiocnxcl(file.as_raw_fd()) }.unwrap();
/// }
/// ```
// TODO: add an example using request_code_*!()
#[macro_export(local_inner_macros)]
macro_rules! ioctl_none_bad {
($(#[$attr:meta])* $name:ident, $nr:expr) => (
$(#[$attr])*
pub unsafe fn $name(fd: $crate::libc::c_int)
-> $crate::Result<$crate::libc::c_int> {
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type))
}
}
)
}
/// Generates a wrapper function for an ioctl that reads data from the kernel.
///
/// The arguments to this macro are:
///
/// * The function name
/// * The ioctl identifier
/// * The ioctl sequence number
/// * The data type passed by this ioctl
///
/// The generated function has the following signature:
///
/// ```rust,ignore
/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: *mut DATA_TYPE) -> Result<libc::c_int>
/// ```
///
/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
///
/// # Example
///
/// ```
/// # #[macro_use] extern crate nix;
/// const SPI_IOC_MAGIC: u8 = b'k'; // Defined in linux/spi/spidev.h
/// const SPI_IOC_TYPE_MODE: u8 = 1;
/// ioctl_read!(spi_read_mode, SPI_IOC_MAGIC, SPI_IOC_TYPE_MODE, u8);
/// # fn main() {}
/// ```
#[macro_export(local_inner_macros)]
macro_rules! ioctl_read {
($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => (
$(#[$attr])*
pub unsafe fn $name(fd: $crate::libc::c_int,
data: *mut $ty)
-> $crate::Result<$crate::libc::c_int> {
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_read!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data))
}
}
)
}
/// Generates a wrapper function for a "bad" ioctl that reads data from the kernel.
///
/// The arguments to this macro are:
///
/// * The function name
/// * The ioctl request code
/// * The data type passed by this ioctl
///
/// The generated function has the following signature:
///
/// ```rust,ignore
/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: *mut DATA_TYPE) -> Result<libc::c_int>
/// ```
///
/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
///
/// # Example
///
/// ```
/// # #[macro_use] extern crate nix;
/// # #[cfg(linux_android)]
/// ioctl_read_bad!(tcgets, libc::TCGETS, libc::termios);
/// # fn main() {}
/// ```
#[macro_export(local_inner_macros)]
macro_rules! ioctl_read_bad {
($(#[$attr:meta])* $name:ident, $nr:expr, $ty:ty) => (
$(#[$attr])*
pub unsafe fn $name(fd: $crate::libc::c_int,
data: *mut $ty)
-> $crate::Result<$crate::libc::c_int> {
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data))
}
}
)
}
/// Generates a wrapper function for an ioctl that writes data through a pointer to the kernel.
///
/// The arguments to this macro are:
///
/// * The function name
/// * The ioctl identifier
/// * The ioctl sequence number
/// * The data type passed by this ioctl
///
/// The generated function has the following signature:
///
/// ```rust,ignore
/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: *const DATA_TYPE) -> Result<libc::c_int>
/// ```
///
/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
///
/// # Example
///
/// ```
/// # #[macro_use] extern crate nix;
/// # pub struct v4l2_audio {}
/// ioctl_write_ptr!(s_audio, b'V', 34, v4l2_audio);
/// # fn main() {}
/// ```
#[macro_export(local_inner_macros)]
macro_rules! ioctl_write_ptr {
($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => (
$(#[$attr])*
pub unsafe fn $name(fd: $crate::libc::c_int,
data: *const $ty)
-> $crate::Result<$crate::libc::c_int> {
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_write!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data))
}
}
)
}
/// Generates a wrapper function for a "bad" ioctl that writes data through a pointer to the kernel.
///
/// The arguments to this macro are:
///
/// * The function name
/// * The ioctl request code
/// * The data type passed by this ioctl
///
/// The generated function has the following signature:
///
/// ```rust,ignore
/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: *const DATA_TYPE) -> Result<libc::c_int>
/// ```
///
/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
///
/// # Example
///
/// ```
/// # #[macro_use] extern crate nix;
/// # #[cfg(linux_android)]
/// ioctl_write_ptr_bad!(tcsets, libc::TCSETS, libc::termios);
/// # fn main() {}
/// ```
#[macro_export(local_inner_macros)]
macro_rules! ioctl_write_ptr_bad {
($(#[$attr:meta])* $name:ident, $nr:expr, $ty:ty) => (
$(#[$attr])*
pub unsafe fn $name(fd: $crate::libc::c_int,
data: *const $ty)
-> $crate::Result<$crate::libc::c_int> {
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data))
}
}
)
}
cfg_if! {
if #[cfg(freebsdlike)] {
/// Generates a wrapper function for a ioctl that writes an integer to the kernel.
///
/// The arguments to this macro are:
///
/// * The function name
/// * The ioctl identifier
/// * The ioctl sequence number
///
/// The generated function has the following signature:
///
/// ```rust,ignore
/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: nix::sys::ioctl::ioctl_param_type) -> Result<libc::c_int>
/// ```
///
/// `nix::sys::ioctl::ioctl_param_type` depends on the OS:
/// * BSD - `libc::c_int`
/// * Linux - `libc::c_ulong`
///
/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
///
/// # Example
///
/// ```
/// # #[macro_use] extern crate nix;
/// ioctl_write_int!(vt_activate, b'v', 4);
/// # fn main() {}
/// ```
#[macro_export(local_inner_macros)]
macro_rules! ioctl_write_int {
($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr) => (
$(#[$attr])*
pub unsafe fn $name(fd: $crate::libc::c_int,
data: $crate::sys::ioctl::ioctl_param_type)
-> $crate::Result<$crate::libc::c_int> {
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_write_int!($ioty, $nr) as $crate::sys::ioctl::ioctl_num_type, data))
}
}
)
}
} else {
/// Generates a wrapper function for a ioctl that writes an integer to the kernel.
///
/// The arguments to this macro are:
///
/// * The function name
/// * The ioctl identifier
/// * The ioctl sequence number
///
/// The generated function has the following signature:
///
/// ```rust,ignore
/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: nix::sys::ioctl::ioctl_param_type) -> Result<libc::c_int>
/// ```
///
/// `nix::sys::ioctl::ioctl_param_type` depends on the OS:
/// * BSD - `libc::c_int`
/// * Linux - `libc::c_ulong`
///
/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
///
/// # Example
///
/// ```
/// # #[macro_use] extern crate nix;
/// const HCI_IOC_MAGIC: u8 = b'k';
/// const HCI_IOC_HCIDEVUP: u8 = 1;
/// ioctl_write_int!(hci_dev_up, HCI_IOC_MAGIC, HCI_IOC_HCIDEVUP);
/// # fn main() {}
/// ```
#[macro_export(local_inner_macros)]
macro_rules! ioctl_write_int {
($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr) => (
$(#[$attr])*
pub unsafe fn $name(fd: $crate::libc::c_int,
data: $crate::sys::ioctl::ioctl_param_type)
-> $crate::Result<$crate::libc::c_int> {
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_write!($ioty, $nr, ::std::mem::size_of::<$crate::libc::c_int>()) as $crate::sys::ioctl::ioctl_num_type, data))
}
}
)
}
}
}
/// Generates a wrapper function for a "bad" ioctl that writes an integer to the kernel.
///
/// The arguments to this macro are:
///
/// * The function name
/// * The ioctl request code
///
/// The generated function has the following signature:
///
/// ```rust,ignore
/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: libc::c_int) -> Result<libc::c_int>
/// ```
///
/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
///
/// # Examples
///
/// ```
/// # #[macro_use] extern crate nix;
/// # #[cfg(linux_android)]
/// ioctl_write_int_bad!(tcsbrk, libc::TCSBRK);
/// # fn main() {}
/// ```
///
/// ```rust
/// # #[macro_use] extern crate nix;
/// const KVMIO: u8 = 0xAE;
/// ioctl_write_int_bad!(kvm_create_vm, request_code_none!(KVMIO, 0x03));
/// # fn main() {}
/// ```
#[macro_export(local_inner_macros)]
macro_rules! ioctl_write_int_bad {
($(#[$attr:meta])* $name:ident, $nr:expr) => (
$(#[$attr])*
pub unsafe fn $name(fd: $crate::libc::c_int,
data: $crate::libc::c_int)
-> $crate::Result<$crate::libc::c_int> {
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data))
}
}
)
}
/// Generates a wrapper function for an ioctl that reads and writes data to the kernel.
///
/// The arguments to this macro are:
///
/// * The function name
/// * The ioctl identifier
/// * The ioctl sequence number
/// * The data type passed by this ioctl
///
/// The generated function has the following signature:
///
/// ```rust,ignore
/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: *mut DATA_TYPE) -> Result<libc::c_int>
/// ```
///
/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
///
/// # Example
///
/// ```
/// # #[macro_use] extern crate nix;
/// # pub struct v4l2_audio {}
/// ioctl_readwrite!(enum_audio, b'V', 65, v4l2_audio);
/// # fn main() {}
/// ```
#[macro_export(local_inner_macros)]
macro_rules! ioctl_readwrite {
($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => (
$(#[$attr])*
pub unsafe fn $name(fd: $crate::libc::c_int,
data: *mut $ty)
-> $crate::Result<$crate::libc::c_int> {
let ioty = $ioty;
let nr = $nr;
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_readwrite!(ioty, nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data))
}
}
)
}
/// Generates a wrapper function for a "bad" ioctl that reads and writes data to the kernel.
///
/// The arguments to this macro are:
///
/// * The function name
/// * The ioctl request code
/// * The data type passed by this ioctl
///
/// The generated function has the following signature:
///
/// ```rust,ignore
/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: *mut DATA_TYPE) -> Result<libc::c_int>
/// ```
///
/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
// TODO: Find an example for ioctl_readwrite_bad
#[macro_export(local_inner_macros)]
macro_rules! ioctl_readwrite_bad {
($(#[$attr:meta])* $name:ident, $nr:expr, $ty:ty) => (
$(#[$attr])*
pub unsafe fn $name(fd: $crate::libc::c_int,
data: *mut $ty)
-> $crate::Result<$crate::libc::c_int> {
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data))
}
}
)
}
/// Generates a wrapper function for an ioctl that reads an array of elements from the kernel.
///
/// The arguments to this macro are:
///
/// * The function name
/// * The ioctl identifier
/// * The ioctl sequence number
/// * The data type passed by this ioctl
///
/// The generated function has the following signature:
///
/// ```rust,ignore
/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: &mut [DATA_TYPE]) -> Result<libc::c_int>
/// ```
///
/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
// TODO: Find an example for ioctl_read_buf
#[macro_export(local_inner_macros)]
macro_rules! ioctl_read_buf {
($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => (
$(#[$attr])*
pub unsafe fn $name(fd: $crate::libc::c_int,
data: &mut [$ty])
-> $crate::Result<$crate::libc::c_int> {
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_read!($ioty, $nr, ::std::mem::size_of_val(data)) as $crate::sys::ioctl::ioctl_num_type, data.as_mut_ptr()))
}
}
)
}
/// Generates a wrapper function for an ioctl that writes an array of elements to the kernel.
///
/// The arguments to this macro are:
///
/// * The function name
/// * The ioctl identifier
/// * The ioctl sequence number
/// * The data type passed by this ioctl
///
/// The generated function has the following signature:
///
/// ```rust,ignore
/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: &[DATA_TYPE]) -> Result<libc::c_int>
/// ```
///
/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
///
/// # Examples
///
/// ```
/// # #[macro_use] extern crate nix;
/// const SPI_IOC_MAGIC: u8 = b'k'; // Defined in linux/spi/spidev.h
/// const SPI_IOC_TYPE_MESSAGE: u8 = 0;
/// # pub struct spi_ioc_transfer(u64);
/// ioctl_write_buf!(spi_transfer, SPI_IOC_MAGIC, SPI_IOC_TYPE_MESSAGE, spi_ioc_transfer);
/// # fn main() {}
/// ```
#[macro_export(local_inner_macros)]
macro_rules! ioctl_write_buf {
($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => (
$(#[$attr])*
pub unsafe fn $name(fd: $crate::libc::c_int,
data: &[$ty])
-> $crate::Result<$crate::libc::c_int> {
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_write!($ioty, $nr, ::std::mem::size_of_val(data)) as $crate::sys::ioctl::ioctl_num_type, data.as_ptr()))
}
}
)
}
/// Generates a wrapper function for an ioctl that reads and writes an array of elements to the kernel.
///
/// The arguments to this macro are:
///
/// * The function name
/// * The ioctl identifier
/// * The ioctl sequence number
/// * The data type passed by this ioctl
///
/// The generated function has the following signature:
///
/// ```rust,ignore
/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: &mut [DATA_TYPE]) -> Result<libc::c_int>
/// ```
///
/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
// TODO: Find an example for readwrite_buf
#[macro_export(local_inner_macros)]
macro_rules! ioctl_readwrite_buf {
($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => (
$(#[$attr])*
pub unsafe fn $name(fd: $crate::libc::c_int,
data: &mut [$ty])
-> $crate::Result<$crate::libc::c_int> {
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_readwrite!($ioty, $nr, ::std::mem::size_of_val(data)) as $crate::sys::ioctl::ioctl_num_type, data.as_mut_ptr()))
}
}
)
}

129
vendor/nix/src/sys/memfd.rs vendored Normal file
View File

@@ -0,0 +1,129 @@
//! Interfaces for managing memory-backed files.
use cfg_if::cfg_if;
use std::os::unix::io::{FromRawFd, OwnedFd, RawFd};
use crate::errno::Errno;
use crate::{NixPath, Result};
libc_bitflags!(
/// Options that change the behavior of [`memfd_create`].
pub struct MFdFlags: libc::c_uint {
/// Set the close-on-exec ([`FD_CLOEXEC`]) flag on the new file descriptor.
///
/// By default, the new file descriptor is set to remain open across an [`execve`]
/// (the `FD_CLOEXEC` flag is initially disabled). This flag can be used to change
/// this default. The file offset is set to the beginning of the file (see [`lseek`]).
///
/// See also the description of the `O_CLOEXEC` flag in [`open(2)`].
///
/// [`execve`]: crate::unistd::execve
/// [`lseek`]: crate::unistd::lseek
/// [`FD_CLOEXEC`]: crate::fcntl::FdFlag::FD_CLOEXEC
/// [`open(2)`]: https://man7.org/linux/man-pages/man2/open.2.html
MFD_CLOEXEC;
/// Allow sealing operations on this file.
///
/// See also the file sealing notes given in [`memfd_create(2)`].
///
/// [`memfd_create(2)`]: https://man7.org/linux/man-pages/man2/memfd_create.2.html
MFD_ALLOW_SEALING;
/// Anonymous file will be created using huge pages. It should be safe now to
/// combine with [`MFD_ALLOW_SEALING`] too.
/// However, despite its presence, on FreeBSD it is unimplemented for now (ENOSYS).
///
/// See also the hugetlb filesystem in [`memfd_create(2)`].
///
/// [`memfd_create(2)`]: https://man7.org/linux/man-pages/man2/memfd_create.2.html
#[cfg(linux_android)]
MFD_HUGETLB;
/// Shift to get the huge page size.
#[cfg(target_env = "ohos")]
MFD_HUGE_SHIFT;
/// Mask to get the huge page size.
#[cfg(target_env = "ohos")]
MFD_HUGE_MASK;
/// hugetlb size of 64KB.
#[cfg(target_env = "ohos")]
MFD_HUGE_64KB;
/// hugetlb size of 512KB.
#[cfg(target_env = "ohos")]
MFD_HUGE_512KB;
/// Following are to be used with [`MFD_HUGETLB`], indicating the desired hugetlb size.
///
/// See also the hugetlb filesystem in [`memfd_create(2)`].
///
/// [`memfd_create(2)`]: https://man7.org/linux/man-pages/man2/memfd_create.2.html
#[cfg(linux_android)]
MFD_HUGE_1MB;
/// hugetlb size of 2MB.
#[cfg(linux_android)]
MFD_HUGE_2MB;
/// hugetlb size of 8MB.
#[cfg(linux_android)]
MFD_HUGE_8MB;
/// hugetlb size of 16MB.
#[cfg(linux_android)]
MFD_HUGE_16MB;
/// hugetlb size of 32MB.
#[cfg(linux_android)]
MFD_HUGE_32MB;
/// hugetlb size of 256MB.
#[cfg(linux_android)]
MFD_HUGE_256MB;
/// hugetlb size of 512MB.
#[cfg(linux_android)]
MFD_HUGE_512MB;
/// hugetlb size of 1GB.
#[cfg(linux_android)]
MFD_HUGE_1GB;
/// hugetlb size of 2GB.
#[cfg(linux_android)]
MFD_HUGE_2GB;
/// hugetlb size of 16GB.
#[cfg(linux_android)]
MFD_HUGE_16GB;
}
);
#[deprecated(since = "0.30.0", note = "Use `MFdFlags instead`")]
/// The deprecated MemFdCreateFlag type alias
pub type MemFdCreateFlag = MFdFlags;
/// Creates an anonymous file that lives in memory, and return a file-descriptor to it.
///
/// The file behaves like a regular file, and so can be modified, truncated, memory-mapped, and so on.
/// However, unlike a regular file, it lives in RAM and has a volatile backing storage.
///
/// For more information, see [`memfd_create(2)`].
///
/// [`memfd_create(2)`]: https://man7.org/linux/man-pages/man2/memfd_create.2.html
#[inline] // Delays codegen, preventing linker errors with dylibs and --no-allow-shlib-undefined
pub fn memfd_create<P: NixPath + ?Sized>(
name: &P,
flags: MFdFlags,
) -> Result<OwnedFd> {
let res = name.with_nix_path(|cstr| {
unsafe {
cfg_if! {
if #[cfg(all(
// Android does not have a memfd_create symbol
not(target_os = "android"),
any(
target_os = "freebsd",
// If the OS is Linux, gnu/musl/ohos expose a memfd_create symbol but not uclibc
target_env = "gnu",
target_env = "musl",
target_env = "ohos"
)))]
{
libc::memfd_create(cstr.as_ptr(), flags.bits())
} else {
libc::syscall(libc::SYS_memfd_create, cstr.as_ptr(), flags.bits())
}
}
}
})?;
Errno::result(res).map(|r| unsafe { OwnedFd::from_raw_fd(r as RawFd) })
}

667
vendor/nix/src/sys/mman.rs vendored Normal file
View File

@@ -0,0 +1,667 @@
//! Memory management declarations.
use crate::errno::Errno;
#[cfg(not(target_os = "android"))]
use crate::NixPath;
use crate::Result;
#[cfg(not(target_os = "android"))]
#[cfg(feature = "fs")]
use crate::{fcntl::OFlag, sys::stat::Mode};
use libc::{self, c_int, c_void, off_t, size_t};
use std::ptr::NonNull;
use std::{
num::NonZeroUsize,
os::unix::io::{AsFd, AsRawFd},
};
libc_bitflags! {
/// Desired memory protection of a memory mapping.
pub struct ProtFlags: c_int {
/// Pages cannot be accessed.
PROT_NONE;
/// Pages can be read.
PROT_READ;
/// Pages can be written.
PROT_WRITE;
/// Pages can be executed
PROT_EXEC;
/// Apply protection up to the end of a mapping that grows upwards.
#[cfg(linux_android)]
PROT_GROWSDOWN;
/// Apply protection down to the beginning of a mapping that grows downwards.
#[cfg(linux_android)]
PROT_GROWSUP;
}
}
libc_bitflags! {
/// Additional parameters for [`mmap`].
pub struct MapFlags: c_int {
/// Compatibility flag. Ignored.
#[cfg(not(any(target_os = "solaris", target_os = "redox")))]
MAP_FILE;
/// Share this mapping. Mutually exclusive with `MAP_PRIVATE`.
MAP_SHARED;
/// Force mmap to check and fail on unknown flags. This also enables `MAP_SYNC`.
#[cfg(target_os = "linux")]
MAP_SHARED_VALIDATE;
/// Create a private copy-on-write mapping. Mutually exclusive with `MAP_SHARED`.
MAP_PRIVATE;
/// Place the mapping at exactly the address specified in `addr`.
MAP_FIXED;
/// Place the mapping at exactly the address specified in `addr`, but never clobber an existing range.
#[cfg(target_os = "linux")]
MAP_FIXED_NOREPLACE;
/// To be used with `MAP_FIXED`, to forbid the system
/// to select a different address than the one specified.
#[cfg(target_os = "freebsd")]
MAP_EXCL;
/// Synonym for `MAP_ANONYMOUS`.
MAP_ANON;
/// The mapping is not backed by any file.
MAP_ANONYMOUS;
/// Put the mapping into the first 2GB of the process address space.
#[cfg(any(all(linux_android,
any(target_arch = "x86", target_arch = "x86_64")),
all(target_os = "linux", target_env = "musl", any(target_arch = "x86", target_arch = "x86_64")),
all(target_os = "linux", target_env = "ohos", target_arch = "x86_64"),
all(target_os = "freebsd", target_pointer_width = "64")))]
MAP_32BIT;
/// Used for stacks; indicates to the kernel that the mapping should extend downward in memory.
#[cfg(linux_android)]
MAP_GROWSDOWN;
/// Compatibility flag. Ignored.
#[cfg(linux_android)]
MAP_DENYWRITE;
/// Compatibility flag. Ignored.
#[cfg(linux_android)]
MAP_EXECUTABLE;
/// Mark the mmaped region to be locked in the same way as `mlock(2)`.
#[cfg(linux_android)]
MAP_LOCKED;
/// Do not reserve swap space for this mapping.
///
/// This was removed in FreeBSD 11 and is unused in DragonFlyBSD.
#[cfg(not(any(freebsdlike, target_os = "aix", target_os = "hurd", target_os = "redox")))]
MAP_NORESERVE;
/// Populate page tables for a mapping.
#[cfg(linux_android)]
MAP_POPULATE;
/// Only meaningful when used with `MAP_POPULATE`. Don't perform read-ahead.
#[cfg(linux_android)]
MAP_NONBLOCK;
/// Allocate the mapping using "huge pages."
#[cfg(linux_android)]
MAP_HUGETLB;
/// Make use of 64KB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
MAP_HUGE_64KB;
/// Make use of 512KB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
MAP_HUGE_512KB;
/// Make use of 1MB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
MAP_HUGE_1MB;
/// Make use of 2MB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
MAP_HUGE_2MB;
/// Make use of 8MB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
MAP_HUGE_8MB;
/// Make use of 16MB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
MAP_HUGE_16MB;
/// Make use of 32MB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
MAP_HUGE_32MB;
/// Make use of 256MB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
MAP_HUGE_256MB;
/// Make use of 512MB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
MAP_HUGE_512MB;
/// Make use of 1GB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
MAP_HUGE_1GB;
/// Make use of 2GB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
MAP_HUGE_2GB;
/// Make use of 16GB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
MAP_HUGE_16GB;
/// Lock the mapped region into memory as with `mlock(2)`.
#[cfg(target_os = "netbsd")]
MAP_WIRED;
/// Causes dirtied data in the specified range to be flushed to disk only when necessary.
#[cfg(freebsdlike)]
MAP_NOSYNC;
/// Rename private pages to a file.
///
/// This was removed in FreeBSD 11 and is unused in DragonFlyBSD.
#[cfg(netbsdlike)]
MAP_RENAME;
/// Region may contain semaphores.
#[cfg(any(freebsdlike, netbsdlike))]
MAP_HASSEMAPHORE;
/// Region grows down, like a stack.
#[cfg(any(linux_android, freebsdlike, netbsdlike))]
MAP_STACK;
/// Do not write through the page caches, write directly to the file. Used for Direct Access (DAX) enabled file systems.
// Available on Linux glibc and musl, MIPS* target excluded.
#[cfg(all(target_os = "linux", not(any(target_arch = "mips", target_arch = "mips64", target_arch = "mips32r6", target_arch = "mips64r6")), not(target_env = "uclibc")))]
MAP_SYNC;
/// Pages in this mapping are not retained in the kernel's memory cache.
#[cfg(apple_targets)]
MAP_NOCACHE;
/// Allows the W/X bit on the page, it's necessary on aarch64 architecture.
#[cfg(apple_targets)]
MAP_JIT;
/// Allows to use large pages, underlying alignment based on size.
#[cfg(target_os = "freebsd")]
MAP_ALIGNED_SUPER;
/// Pages will be discarded in the core dumps.
#[cfg(target_os = "openbsd")]
MAP_CONCEAL;
/// Attempt to place the mapping at exactly the address specified in `addr`.
/// it's a default behavior on OpenBSD.
#[cfg(netbsdlike)]
MAP_TRYFIXED;
}
}
impl MapFlags {
/// Create `MAP_HUGETLB` with provided size of huge page.
///
/// Under the hood it computes `MAP_HUGETLB | (huge_page_size_log2 << libc::MAP_HUGE_SHIFT`).
/// `huge_page_size_log2` denotes logarithm of huge page size to use and should be
/// between 16 and 63 (inclusively).
///
/// ```
/// # use nix::sys::mman::MapFlags;
/// let f = MapFlags::map_hugetlb_with_size_log2(30).unwrap();
/// assert_eq!(f, MapFlags::MAP_HUGETLB | MapFlags::MAP_HUGE_1GB);
/// ```
#[cfg(any(linux_android, target_os = "fuchsia"))]
pub fn map_hugetlb_with_size_log2(
huge_page_size_log2: u32,
) -> Option<Self> {
if (16..=63).contains(&huge_page_size_log2) {
let flag = libc::MAP_HUGETLB
| (huge_page_size_log2 << libc::MAP_HUGE_SHIFT) as i32;
Some(Self(flag.into()))
} else {
None
}
}
}
#[cfg(any(target_os = "linux", target_os = "netbsd"))]
libc_bitflags! {
/// Options for [`mremap`].
pub struct MRemapFlags: c_int {
/// Permit the kernel to relocate the mapping to a new virtual address, if necessary.
#[cfg(target_os = "linux")]
MREMAP_MAYMOVE;
/// Place the mapping at exactly the address specified in `new_address`.
#[cfg(target_os = "linux")]
MREMAP_FIXED;
/// Works in conjunction with `MREMAP_MAYMOVE` but does not unmap `old_address`.
/// Note that, in this case, `old_size` and `new_size` must be the same.
#[cfg(target_os = "linux")]
MREMAP_DONTUNMAP;
/// Place the mapping at exactly the address specified in `new_address`.
#[cfg(target_os = "netbsd")]
MAP_FIXED;
/// Allows to duplicate the mapping to be able to apply different flags on the copy.
#[cfg(target_os = "netbsd")]
MAP_REMAPDUP;
}
}
libc_enum! {
/// Usage information for a range of memory to allow for performance optimizations by the kernel.
///
/// Used by [`madvise`].
#[repr(i32)]
#[non_exhaustive]
pub enum MmapAdvise {
/// No further special treatment. This is the default.
MADV_NORMAL,
/// Expect random page references.
MADV_RANDOM,
/// Expect sequential page references.
MADV_SEQUENTIAL,
/// Expect access in the near future.
MADV_WILLNEED,
/// Do not expect access in the near future.
MADV_DONTNEED,
/// Free up a given range of pages and its associated backing store.
#[cfg(linux_android)]
MADV_REMOVE,
/// Do not make pages in this range available to the child after a `fork(2)`.
#[cfg(linux_android)]
MADV_DONTFORK,
/// Undo the effect of `MADV_DONTFORK`.
#[cfg(linux_android)]
MADV_DOFORK,
/// Poison the given pages.
///
/// Subsequent references to those pages are treated like hardware memory corruption.
#[cfg(linux_android)]
MADV_HWPOISON,
/// Enable Kernel Samepage Merging (KSM) for the given pages.
#[cfg(linux_android)]
MADV_MERGEABLE,
/// Undo the effect of `MADV_MERGEABLE`
#[cfg(linux_android)]
MADV_UNMERGEABLE,
/// Preserve the memory of each page but offline the original page.
#[cfg(any(target_os = "android",
all(target_os = "linux", any(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "s390x",
target_arch = "x86",
target_arch = "x86_64",
target_arch = "sparc64"))))]
MADV_SOFT_OFFLINE,
/// Enable Transparent Huge Pages (THP) for pages in the given range.
#[cfg(linux_android)]
MADV_HUGEPAGE,
/// Undo the effect of `MADV_HUGEPAGE`.
#[cfg(linux_android)]
MADV_NOHUGEPAGE,
/// Exclude the given range from a core dump.
#[cfg(linux_android)]
MADV_DONTDUMP,
/// Undo the effect of an earlier `MADV_DONTDUMP`.
#[cfg(linux_android)]
MADV_DODUMP,
/// Specify that the application no longer needs the pages in the given range.
#[cfg(not(any(target_os = "aix", target_os = "hurd", target_os = "cygwin", target_os = "redox")))]
MADV_FREE,
/// Request that the system not flush the current range to disk unless it needs to.
#[cfg(freebsdlike)]
MADV_NOSYNC,
/// Undoes the effects of `MADV_NOSYNC` for any future pages dirtied within the given range.
#[cfg(freebsdlike)]
MADV_AUTOSYNC,
/// Region is not included in a core file.
#[cfg(freebsdlike)]
MADV_NOCORE,
/// Include region in a core file
#[cfg(freebsdlike)]
MADV_CORE,
/// This process should not be killed when swap space is exhausted.
#[cfg(any(target_os = "freebsd"))]
MADV_PROTECT,
/// Invalidate the hardware page table for the given region.
#[cfg(target_os = "dragonfly")]
MADV_INVAL,
/// Set the offset of the page directory page to `value` for the virtual page table.
#[cfg(target_os = "dragonfly")]
MADV_SETMAP,
/// Indicates that the application will not need the data in the given range.
#[cfg(apple_targets)]
MADV_ZERO_WIRED_PAGES,
/// Pages can be reused (by anyone).
#[cfg(apple_targets)]
MADV_FREE_REUSABLE,
/// Caller wants to reuse those pages.
#[cfg(apple_targets)]
MADV_FREE_REUSE,
// Darwin doesn't document this flag's behavior.
#[cfg(apple_targets)]
#[allow(missing_docs)]
MADV_CAN_REUSE,
/// Reclaim the address range when applicable.
#[cfg(linux_android)]
MADV_PAGEOUT,
/// Deactivate the address range when applicable.
#[cfg(linux_android)]
MADV_COLD,
/// After fork, the adress range is zero filled.
#[cfg(linux_android)]
MADV_WIPEONFORK,
/// Undo `MADV_WIPEONFORK` when it applied.
#[cfg(linux_android)]
MADV_KEEPONFORK,
/// Pre-load the address range for reading to reduce page-fault latency.
#[cfg(linux_android)]
MADV_POPULATE_READ,
/// Pre-fault the address range for writing to reduce page-fault
/// latency on subsequent writes.
#[cfg(linux_android)]
MADV_POPULATE_WRITE,
}
}
libc_bitflags! {
/// Configuration flags for [`msync`].
pub struct MsFlags: c_int {
/// Schedule an update but return immediately.
MS_ASYNC;
/// Invalidate all cached data.
MS_INVALIDATE;
/// Invalidate pages, but leave them mapped.
#[cfg(apple_targets)]
MS_KILLPAGES;
/// Deactivate pages, but leave them mapped.
#[cfg(apple_targets)]
MS_DEACTIVATE;
/// Perform an update and wait for it to complete.
MS_SYNC;
}
}
#[cfg(not(any(target_os = "haiku", target_os = "cygwin", target_os = "redox")))]
libc_bitflags! {
/// Flags for [`mlockall`].
pub struct MlockAllFlags: c_int {
/// Lock pages that are currently mapped into the address space of the process.
MCL_CURRENT;
/// Lock pages which will become mapped into the address space of the process in the future.
MCL_FUTURE;
}
}
/// Locks all memory pages that contain part of the address range with `length`
/// bytes starting at `addr`.
///
/// Locked pages never move to the swap area.
///
/// # Safety
///
/// `addr` must meet all the requirements described in the [`mlock(2)`] man page.
///
/// [`mlock(2)`]: https://man7.org/linux/man-pages/man2/mlock.2.html
pub unsafe fn mlock(addr: NonNull<c_void>, length: size_t) -> Result<()> {
unsafe { Errno::result(libc::mlock(addr.as_ptr(), length)).map(drop) }
}
/// Unlocks all memory pages that contain part of the address range with
/// `length` bytes starting at `addr`.
///
/// # Safety
///
/// `addr` must meet all the requirements described in the [`munlock(2)`] man
/// page.
///
/// [`munlock(2)`]: https://man7.org/linux/man-pages/man2/munlock.2.html
pub unsafe fn munlock(addr: NonNull<c_void>, length: size_t) -> Result<()> {
unsafe { Errno::result(libc::munlock(addr.as_ptr(), length)).map(drop) }
}
/// Locks all memory pages mapped into this process' address space.
///
/// Locked pages never move to the swap area. For more information, see [`mlockall(2)`].
///
/// [`mlockall(2)`]: https://man7.org/linux/man-pages/man2/mlockall.2.html
#[cfg(not(any(target_os = "haiku", target_os = "cygwin", target_os = "redox")))]
pub fn mlockall(flags: MlockAllFlags) -> Result<()> {
unsafe { Errno::result(libc::mlockall(flags.bits())) }.map(drop)
}
/// Unlocks all memory pages mapped into this process' address space.
///
/// For more information, see [`munlockall(2)`].
///
/// [`munlockall(2)`]: https://man7.org/linux/man-pages/man2/munlockall.2.html
#[cfg(not(any(target_os = "haiku", target_os = "cygwin", target_os = "redox")))]
pub fn munlockall() -> Result<()> {
unsafe { Errno::result(libc::munlockall()) }.map(drop)
}
/// Allocate memory, or map files or devices into memory
///
/// For anonymous mappings (`MAP_ANON`/`MAP_ANONYMOUS`), see [mmap_anonymous].
///
/// # Safety
///
/// See the [`mmap(2)`] man page for detailed requirements.
///
/// [`mmap(2)`]: https://man7.org/linux/man-pages/man2/mmap.2.html
pub unsafe fn mmap<F: AsFd>(
addr: Option<NonZeroUsize>,
length: NonZeroUsize,
prot: ProtFlags,
flags: MapFlags,
f: F,
offset: off_t,
) -> Result<NonNull<c_void>> {
let ptr = addr.map_or(std::ptr::null_mut(), |a| a.get() as *mut c_void);
let fd = f.as_fd().as_raw_fd();
let ret = unsafe {
libc::mmap(ptr, length.into(), prot.bits(), flags.bits(), fd, offset)
};
if std::ptr::eq(ret, libc::MAP_FAILED) {
Err(Errno::last())
} else {
// SAFETY: `libc::mmap` returns a valid non-null pointer or `libc::MAP_FAILED`, thus `ret`
// will be non-null here.
Ok(unsafe { NonNull::new_unchecked(ret) })
}
}
/// Create an anonymous memory mapping.
///
/// This function is a wrapper around [`mmap`]:
/// `mmap(ptr, len, prot, MAP_ANONYMOUS | flags, -1, 0)`.
///
/// # Safety
///
/// See the [`mmap(2)`] man page for detailed requirements.
///
/// [`mmap(2)`]: https://man7.org/linux/man-pages/man2/mmap.2.html
pub unsafe fn mmap_anonymous(
addr: Option<NonZeroUsize>,
length: NonZeroUsize,
prot: ProtFlags,
flags: MapFlags,
) -> Result<NonNull<c_void>> {
let ptr = addr.map_or(std::ptr::null_mut(), |a| a.get() as *mut c_void);
let flags = MapFlags::MAP_ANONYMOUS | flags;
let ret = unsafe {
libc::mmap(ptr, length.into(), prot.bits(), flags.bits(), -1, 0)
};
if std::ptr::eq(ret, libc::MAP_FAILED) {
Err(Errno::last())
} else {
// SAFETY: `libc::mmap` returns a valid non-null pointer or `libc::MAP_FAILED`, thus `ret`
// will be non-null here.
Ok(unsafe { NonNull::new_unchecked(ret) })
}
}
/// Expands (or shrinks) an existing memory mapping, potentially moving it at
/// the same time.
///
/// # Safety
///
/// See the `mremap(2)` [man page](https://man7.org/linux/man-pages/man2/mremap.2.html) for
/// detailed requirements.
#[cfg(any(target_os = "linux", target_os = "netbsd"))]
pub unsafe fn mremap(
addr: NonNull<c_void>,
old_size: size_t,
new_size: size_t,
flags: MRemapFlags,
new_address: Option<NonNull<c_void>>,
) -> Result<NonNull<c_void>> {
#[cfg(target_os = "linux")]
let ret = unsafe {
libc::mremap(
addr.as_ptr(),
old_size,
new_size,
flags.bits(),
new_address
.map(NonNull::as_ptr)
.unwrap_or(std::ptr::null_mut()),
)
};
#[cfg(target_os = "netbsd")]
let ret = unsafe {
libc::mremap(
addr.as_ptr(),
old_size,
new_address
.map(NonNull::as_ptr)
.unwrap_or(std::ptr::null_mut()),
new_size,
flags.bits(),
)
};
if std::ptr::eq(ret, libc::MAP_FAILED) {
Err(Errno::last())
} else {
// SAFETY: `libc::mremap` returns a valid non-null pointer or `libc::MAP_FAILED`, thus `ret`
// will be non-null here.
Ok(unsafe { NonNull::new_unchecked(ret) })
}
}
/// remove a mapping
///
/// # Safety
///
/// `addr` must meet all the requirements described in the [`munmap(2)`] man
/// page.
///
/// [`munmap(2)`]: https://man7.org/linux/man-pages/man2/munmap.2.html
pub unsafe fn munmap(addr: NonNull<c_void>, len: size_t) -> Result<()> {
unsafe { Errno::result(libc::munmap(addr.as_ptr(), len)).map(drop) }
}
/// give advice about use of memory
///
/// # Safety
///
/// See the [`madvise(2)`] man page. Take special care when using
/// [`MmapAdvise::MADV_FREE`].
///
/// [`madvise(2)`]: https://man7.org/linux/man-pages/man2/madvise.2.html
#[allow(rustdoc::broken_intra_doc_links)] // For Hurd as `MADV_FREE` is not available on it
pub unsafe fn madvise(
addr: NonNull<c_void>,
length: size_t,
advise: MmapAdvise,
) -> Result<()> {
unsafe {
Errno::result(libc::madvise(addr.as_ptr(), length, advise as i32))
.map(drop)
}
}
/// Set protection of memory mapping.
///
/// See [`mprotect(3)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mprotect.html) for
/// details.
///
/// # Safety
///
/// Calls to `mprotect` are inherently unsafe, as changes to memory protections can lead to
/// SIGSEGVs.
///
/// ```
/// # use nix::libc::size_t;
/// # use nix::sys::mman::{mmap_anonymous, mprotect, MapFlags, ProtFlags};
/// # use std::ptr;
/// # use std::os::unix::io::BorrowedFd;
/// const ONE_K: size_t = 1024;
/// let one_k_non_zero = std::num::NonZeroUsize::new(ONE_K).unwrap();
/// let mut slice: &mut [u8] = unsafe {
/// let mem = mmap_anonymous(None, one_k_non_zero, ProtFlags::PROT_NONE, MapFlags::MAP_PRIVATE)
/// .unwrap();
/// mprotect(mem, ONE_K, ProtFlags::PROT_READ | ProtFlags::PROT_WRITE).unwrap();
/// std::slice::from_raw_parts_mut(mem.as_ptr().cast(), ONE_K)
/// };
/// assert_eq!(slice[0], 0x00);
/// slice[0] = 0xFF;
/// assert_eq!(slice[0], 0xFF);
/// ```
pub unsafe fn mprotect(
addr: NonNull<c_void>,
length: size_t,
prot: ProtFlags,
) -> Result<()> {
unsafe {
Errno::result(libc::mprotect(addr.as_ptr(), length, prot.bits()))
.map(drop)
}
}
/// synchronize a mapped region
///
/// # Safety
///
/// `addr` must meet all the requirements described in the [`msync(2)`] man
/// page.
///
/// [`msync(2)`]: https://man7.org/linux/man-pages/man2/msync.2.html
pub unsafe fn msync(
addr: NonNull<c_void>,
length: size_t,
flags: MsFlags,
) -> Result<()> {
unsafe {
Errno::result(libc::msync(addr.as_ptr(), length, flags.bits()))
.map(drop)
}
}
#[cfg(not(target_os = "android"))]
feature! {
#![feature = "fs"]
/// Creates and opens a new, or opens an existing, POSIX shared memory object.
///
/// For more information, see [`shm_open(3)`].
///
/// [`shm_open(3)`]: https://man7.org/linux/man-pages/man3/shm_open.3.html
pub fn shm_open<P>(
name: &P,
flag: OFlag,
mode: Mode
) -> Result<std::os::unix::io::OwnedFd>
where P: ?Sized + NixPath
{
use std::os::unix::io::{FromRawFd, OwnedFd};
let ret = name.with_nix_path(|cstr| {
#[cfg(apple_targets)]
unsafe {
libc::shm_open(cstr.as_ptr(), flag.bits(), mode.bits() as libc::c_uint)
}
#[cfg(not(apple_targets))]
unsafe {
libc::shm_open(cstr.as_ptr(), flag.bits(), mode.bits() as libc::mode_t)
}
})?;
match ret {
-1 => Err(Errno::last()),
fd => Ok(unsafe{ OwnedFd::from_raw_fd(fd) })
}
}
}
/// Performs the converse of [`shm_open`], removing an object previously created.
///
/// For more information, see [`shm_unlink(3)`].
///
/// [`shm_unlink(3)`]: https://man7.org/linux/man-pages/man3/shm_unlink.3.html
#[cfg(not(target_os = "android"))]
pub fn shm_unlink<P: ?Sized + NixPath>(name: &P) -> Result<()> {
let ret =
name.with_nix_path(|cstr| unsafe { libc::shm_unlink(cstr.as_ptr()) })?;
Errno::result(ret).map(drop)
}

209
vendor/nix/src/sys/mod.rs vendored Normal file
View File

@@ -0,0 +1,209 @@
//! Mostly platform-specific functionality
#[cfg(any(
freebsdlike,
all(
target_os = "linux",
not(any(target_env = "uclibc", target_env = "ohos"))
),
apple_targets,
target_os = "netbsd"
))]
feature! {
#![feature = "aio"]
pub mod aio;
}
feature! {
#![feature = "event"]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub mod epoll;
#[cfg(bsd)]
pub mod event;
/// Event file descriptor.
#[cfg(any(linux_android, target_os = "freebsd"))]
pub mod eventfd;
}
#[cfg(target_os = "linux")]
feature! {
#![feature = "fanotify"]
pub mod fanotify;
}
#[cfg(any(
bsd,
linux_android,
solarish,
target_os = "fuchsia",
target_os = "redox",
))]
#[cfg(feature = "ioctl")]
#[cfg_attr(docsrs, doc(cfg(feature = "ioctl")))]
#[macro_use]
pub mod ioctl;
#[cfg(any(linux_android, target_os = "freebsd"))]
feature! {
#![feature = "fs"]
pub mod memfd;
}
feature! {
#![feature = "mman"]
pub mod mman;
}
#[cfg(target_os = "linux")]
feature! {
#![feature = "personality"]
pub mod personality;
}
#[cfg(target_os = "linux")]
feature! {
#![feature = "process"]
pub mod prctl;
}
feature! {
#![feature = "pthread"]
pub mod pthread;
}
#[cfg(any(linux_android, bsd))]
feature! {
#![feature = "ptrace"]
#[allow(missing_docs)]
pub mod ptrace;
}
#[cfg(target_os = "linux")]
feature! {
#![feature = "quota"]
pub mod quota;
}
#[cfg(any(target_os = "linux", netbsdlike))]
feature! {
#![feature = "reboot"]
pub mod reboot;
}
#[cfg(not(any(
target_os = "redox",
target_os = "fuchsia",
solarish,
target_os = "haiku"
)))]
feature! {
#![feature = "resource"]
pub mod resource;
}
feature! {
#![feature = "poll"]
pub mod select;
}
#[cfg(any(linux_android, freebsdlike, apple_targets, solarish))]
feature! {
#![feature = "zerocopy"]
pub mod sendfile;
}
pub mod signal;
#[cfg(linux_android)]
feature! {
#![feature = "signal"]
#[allow(missing_docs)]
pub mod signalfd;
}
feature! {
#![feature = "socket"]
#[allow(missing_docs)]
pub mod socket;
}
feature! {
#![feature = "fs"]
#[allow(missing_docs)]
pub mod stat;
}
#[cfg(any(
linux_android,
freebsdlike,
apple_targets,
target_os = "openbsd",
target_os = "cygwin"
))]
feature! {
#![feature = "fs"]
pub mod statfs;
}
feature! {
#![feature = "fs"]
pub mod statvfs;
}
#[cfg(linux_android)]
#[allow(missing_docs)]
pub mod sysinfo;
feature! {
#![feature = "term"]
#[allow(missing_docs)]
pub mod termios;
}
#[allow(missing_docs)]
pub mod time;
feature! {
#![feature = "uio"]
pub mod uio;
}
feature! {
#![feature = "feature"]
pub mod utsname;
}
feature! {
#![feature = "process"]
pub mod wait;
}
#[cfg(linux_android)]
feature! {
#![feature = "inotify"]
pub mod inotify;
}
#[cfg(linux_android)]
feature! {
#![feature = "time"]
pub mod timerfd;
}
#[cfg(all(
any(
target_os = "freebsd",
solarish,
target_os = "linux",
target_os = "netbsd"
),
feature = "time",
feature = "signal"
))]
feature! {
#![feature = "time"]
pub mod timer;
}

94
vendor/nix/src/sys/personality.rs vendored Normal file
View File

@@ -0,0 +1,94 @@
//! Process execution domains
use crate::errno::Errno;
use crate::Result;
use libc::{self, c_int, c_ulong};
libc_bitflags! {
/// Flags used and returned by [`get()`](fn.get.html) and
/// [`set()`](fn.set.html).
pub struct Persona: c_int {
/// Provide the legacy virtual address space layout.
ADDR_COMPAT_LAYOUT;
/// Disable address-space-layout randomization.
ADDR_NO_RANDOMIZE;
/// Limit the address space to 32 bits.
ADDR_LIMIT_32BIT;
/// Use `0xc0000000` as the offset at which to search a virtual memory
/// chunk on [`mmap(2)`], otherwise use `0xffffe000`.
///
/// [`mmap(2)`]: https://man7.org/linux/man-pages/man2/mmap.2.html
ADDR_LIMIT_3GB;
/// User-space function pointers to signal handlers point to descriptors.
#[cfg(not(any(target_env = "musl", target_env = "uclibc", target_env = "ohos")))]
FDPIC_FUNCPTRS;
/// Map page 0 as read-only.
MMAP_PAGE_ZERO;
/// `PROT_READ` implies `PROT_EXEC` for [`mmap(2)`].
///
/// [`mmap(2)`]: https://man7.org/linux/man-pages/man2/mmap.2.html
READ_IMPLIES_EXEC;
/// No effects.
SHORT_INODE;
/// [`select(2)`], [`pselect(2)`], and [`ppoll(2)`] do not modify the
/// returned timeout argument when interrupted by a signal handler.
///
/// [`select(2)`]: https://man7.org/linux/man-pages/man2/select.2.html
/// [`pselect(2)`]: https://man7.org/linux/man-pages/man2/pselect.2.html
/// [`ppoll(2)`]: https://man7.org/linux/man-pages/man2/ppoll.2.html
STICKY_TIMEOUTS;
/// Have [`uname(2)`] report a 2.6.40+ version number rather than a 3.x
/// version number.
///
/// [`uname(2)`]: https://man7.org/linux/man-pages/man2/uname.2.html
#[cfg(not(any(target_env = "musl", target_env = "uclibc", target_env = "ohos")))]
UNAME26;
/// No effects.
WHOLE_SECONDS;
}
}
/// Retrieve the current process personality.
///
/// Returns a Result containing a Persona instance.
///
/// Example:
///
/// ```
/// # use nix::sys::personality::{self, Persona};
/// let pers = personality::get().unwrap();
/// assert!(!pers.contains(Persona::WHOLE_SECONDS));
/// ```
pub fn get() -> Result<Persona> {
let res = unsafe { libc::personality(0xFFFFFFFF) };
Errno::result(res).map(Persona::from_bits_truncate)
}
/// Set the current process personality.
///
/// Returns a Result containing the *previous* personality for the
/// process, as a Persona.
///
/// For more information, see [personality(2)](https://man7.org/linux/man-pages/man2/personality.2.html)
///
/// **NOTE**: This call **replaces** the current personality entirely.
/// To **update** the personality, first call `get()` and then `set()`
/// with the modified persona.
///
/// Example:
///
// Disable test on aarch64 until we know why it fails.
// https://github.com/nix-rust/nix/issues/2060
#[cfg_attr(target_arch = "aarch64", doc = " ```no_run")]
#[cfg_attr(not(target_arch = "aarch64"), doc = " ```")]
/// # use nix::sys::personality::{self, Persona};
/// let mut pers = personality::get().unwrap();
/// assert!(!pers.contains(Persona::ADDR_NO_RANDOMIZE));
/// personality::set(pers | Persona::ADDR_NO_RANDOMIZE).unwrap();
/// ```
pub fn set(persona: Persona) -> Result<Persona> {
let res = unsafe { libc::personality(persona.bits() as c_ulong) };
Errno::result(res).map(Persona::from_bits_truncate)
}

228
vendor/nix/src/sys/prctl.rs vendored Normal file
View File

@@ -0,0 +1,228 @@
//! prctl is a Linux-only API for performing operations on a process or thread.
//!
//! Note that careless use of some prctl() operations can confuse the user-space run-time
//! environment, so these operations should be used with care.
//!
//! For more documentation, please read [prctl(2)](https://man7.org/linux/man-pages/man2/prctl.2.html).
use crate::errno::Errno;
use crate::sys::signal::Signal;
use crate::Result;
use libc::{c_int, c_ulong, c_void};
use std::convert::TryFrom;
use std::ffi::{CStr, CString};
use std::num::NonZeroUsize;
use std::ptr::NonNull;
libc_enum! {
/// The type of hardware memory corruption kill policy for the thread.
#[repr(i32)]
#[non_exhaustive]
#[allow(non_camel_case_types)]
pub enum PrctlMCEKillPolicy {
/// The thread will receive SIGBUS as soon as a memory corruption is detected.
PR_MCE_KILL_EARLY,
/// The process is killed only when it accesses a corrupted page.
PR_MCE_KILL_LATE,
/// Uses the system-wide default.
PR_MCE_KILL_DEFAULT,
}
impl TryFrom<i32>
}
fn prctl_set_bool(option: c_int, status: bool) -> Result<()> {
let res = unsafe { libc::prctl(option, status as c_ulong, 0, 0, 0) };
Errno::result(res).map(drop)
}
fn prctl_get_bool(option: c_int) -> Result<bool> {
let res = unsafe { libc::prctl(option, 0, 0, 0, 0) };
Errno::result(res).map(|res| res != 0)
}
/// Set the "child subreaper" attribute for this process
pub fn set_child_subreaper(attribute: bool) -> Result<()> {
prctl_set_bool(libc::PR_SET_CHILD_SUBREAPER, attribute)
}
/// Get the "child subreaper" attribute for this process
pub fn get_child_subreaper() -> Result<bool> {
// prctl writes into this var
let mut subreaper: c_int = 0;
let res = unsafe {
libc::prctl(libc::PR_GET_CHILD_SUBREAPER, &mut subreaper, 0, 0, 0)
};
Errno::result(res).map(|_| subreaper != 0)
}
/// Set the dumpable attribute which determines if core dumps are created for this process.
pub fn set_dumpable(attribute: bool) -> Result<()> {
prctl_set_bool(libc::PR_SET_DUMPABLE, attribute)
}
/// Get the dumpable attribute for this process.
pub fn get_dumpable() -> Result<bool> {
prctl_get_bool(libc::PR_GET_DUMPABLE)
}
/// Set the "keep capabilities" attribute for this process. This causes the thread to retain
/// capabilities even if it switches its UID to a nonzero value.
pub fn set_keepcaps(attribute: bool) -> Result<()> {
prctl_set_bool(libc::PR_SET_KEEPCAPS, attribute)
}
/// Get the "keep capabilities" attribute for this process
pub fn get_keepcaps() -> Result<bool> {
prctl_get_bool(libc::PR_GET_KEEPCAPS)
}
/// Clear the thread memory corruption kill policy and use the system-wide default
pub fn clear_mce_kill() -> Result<()> {
let res = unsafe {
libc::prctl(libc::PR_MCE_KILL, libc::PR_MCE_KILL_CLEAR, 0, 0, 0)
};
Errno::result(res).map(drop)
}
/// Set the thread memory corruption kill policy
pub fn set_mce_kill(policy: PrctlMCEKillPolicy) -> Result<()> {
let res = unsafe {
libc::prctl(
libc::PR_MCE_KILL,
libc::PR_MCE_KILL_SET,
policy as c_ulong,
0,
0,
)
};
Errno::result(res).map(drop)
}
/// Get the thread memory corruption kill policy
pub fn get_mce_kill() -> Result<PrctlMCEKillPolicy> {
let res = unsafe { libc::prctl(libc::PR_MCE_KILL_GET, 0, 0, 0, 0) };
match Errno::result(res) {
Ok(val) => Ok(PrctlMCEKillPolicy::try_from(val)?),
Err(e) => Err(e),
}
}
/// Set the parent-death signal of the calling process. This is the signal that the calling process
/// will get when its parent dies.
pub fn set_pdeathsig<T: Into<Option<Signal>>>(signal: T) -> Result<()> {
let sig = match signal.into() {
Some(s) => s as c_int,
None => 0,
};
let res = unsafe { libc::prctl(libc::PR_SET_PDEATHSIG, sig, 0, 0, 0) };
Errno::result(res).map(drop)
}
/// Returns the current parent-death signal
pub fn get_pdeathsig() -> Result<Option<Signal>> {
// prctl writes into this var
let mut sig: c_int = 0;
let res = unsafe { libc::prctl(libc::PR_GET_PDEATHSIG, &mut sig, 0, 0, 0) };
match Errno::result(res) {
Ok(_) => Ok(match sig {
0 => None,
_ => Some(Signal::try_from(sig)?),
}),
Err(e) => Err(e),
}
}
/// Set the name of the calling thread. Strings longer than 15 bytes will be truncated.
pub fn set_name(name: &CStr) -> Result<()> {
let res = unsafe { libc::prctl(libc::PR_SET_NAME, name.as_ptr(), 0, 0, 0) };
Errno::result(res).map(drop)
}
/// Return the name of the calling thread
pub fn get_name() -> Result<CString> {
// Size of buffer determined by linux/sched.h TASK_COMM_LEN
let buf = [0u8; 16];
let res = unsafe { libc::prctl(libc::PR_GET_NAME, &buf, 0, 0, 0) };
Errno::result(res).and_then(|_| {
CStr::from_bytes_until_nul(&buf)
.map(CStr::to_owned)
.map_err(|_| Errno::EINVAL)
})
}
/// Sets the timer slack value for the calling thread. Timer slack is used by the kernel to group
/// timer expirations and make them the supplied amount of nanoseconds late.
pub fn set_timerslack(ns: c_ulong) -> Result<()> {
let res = unsafe { libc::prctl(libc::PR_SET_TIMERSLACK, ns, 0, 0, 0) };
Errno::result(res).map(drop)
}
/// Get the timerslack for the calling thread.
pub fn get_timerslack() -> Result<i32> {
let res = unsafe { libc::prctl(libc::PR_GET_TIMERSLACK, 0, 0, 0, 0) };
Errno::result(res)
}
/// Disable all performance counters attached to the calling process.
pub fn task_perf_events_disable() -> Result<()> {
let res =
unsafe { libc::prctl(libc::PR_TASK_PERF_EVENTS_DISABLE, 0, 0, 0, 0) };
Errno::result(res).map(drop)
}
/// Enable all performance counters attached to the calling process.
pub fn task_perf_events_enable() -> Result<()> {
let res =
unsafe { libc::prctl(libc::PR_TASK_PERF_EVENTS_ENABLE, 0, 0, 0, 0) };
Errno::result(res).map(drop)
}
/// Set the calling threads "no new privs" attribute. Once set this option can not be unset.
pub fn set_no_new_privs() -> Result<()> {
prctl_set_bool(libc::PR_SET_NO_NEW_PRIVS, true) // Cannot be unset
}
/// Get the "no new privs" attribute for the calling thread.
pub fn get_no_new_privs() -> Result<bool> {
prctl_get_bool(libc::PR_GET_NO_NEW_PRIVS)
}
/// Set the state of the "THP disable" flag for the calling thread. Setting this disables
/// transparent huge pages.
pub fn set_thp_disable(flag: bool) -> Result<()> {
prctl_set_bool(libc::PR_SET_THP_DISABLE, flag)
}
/// Get the "THP disable" flag for the calling thread.
pub fn get_thp_disable() -> Result<bool> {
prctl_get_bool(libc::PR_GET_THP_DISABLE)
}
/// Set an identifier (or reset it) to the address memory range.
pub fn set_vma_anon_name(addr: NonNull<c_void>, length: NonZeroUsize, name: Option<&CStr>) -> Result<()> {
let nameref = match name {
Some(n) => n.as_ptr(),
_ => std::ptr::null()
};
let res = unsafe { libc::prctl(libc::PR_SET_VMA, libc::PR_SET_VMA_ANON_NAME, addr.as_ptr(), length, nameref) };
Errno::result(res).map(drop)
}

43
vendor/nix/src/sys/pthread.rs vendored Normal file
View File

@@ -0,0 +1,43 @@
//! Low level threading primitives
#[cfg(not(target_os = "redox"))]
use crate::errno::Errno;
#[cfg(not(target_os = "redox"))]
use crate::Result;
use libc::{self, pthread_t};
/// Identifies an individual thread.
pub type Pthread = pthread_t;
/// Obtain ID of the calling thread (see
/// [`pthread_self(3)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_self.html)
///
/// The thread ID returned by `pthread_self()` is not the same thing as
/// the kernel thread ID returned by a call to `gettid(2)`.
#[inline]
pub fn pthread_self() -> Pthread {
unsafe { libc::pthread_self() }
}
feature! {
#![feature = "signal"]
/// Send a signal to a thread (see [`pthread_kill(3)`]).
///
/// If `signal` is `None`, `pthread_kill` will only preform error checking and
/// won't send any signal.
///
/// [`pthread_kill(3)`]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_kill.html
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[cfg(not(target_os = "redox"))]
pub fn pthread_kill<T>(thread: Pthread, signal: T) -> Result<()>
where T: Into<Option<crate::sys::signal::Signal>>
{
let sig = match signal.into() {
Some(s) => s as libc::c_int,
None => 0,
};
let res = unsafe { libc::pthread_kill(thread, sig) };
Errno::result(res).map(drop)
}
}

187
vendor/nix/src/sys/ptrace/bsd.rs vendored Normal file
View File

@@ -0,0 +1,187 @@
use crate::errno::Errno;
use crate::sys::signal::Signal;
use crate::unistd::Pid;
use crate::Result;
use cfg_if::cfg_if;
use libc::{self, c_int};
use std::ptr;
pub type RequestType = c_int;
cfg_if! {
if #[cfg(any(freebsdlike, apple_targets, target_os = "openbsd"))] {
#[doc(hidden)]
pub type AddressType = *mut ::libc::c_char;
} else {
#[doc(hidden)]
pub type AddressType = *mut ::libc::c_void;
}
}
libc_enum! {
#[repr(i32)]
/// Ptrace Request enum defining the action to be taken.
#[non_exhaustive]
pub enum Request {
PT_TRACE_ME,
PT_READ_I,
PT_READ_D,
#[cfg(apple_targets)]
PT_READ_U,
PT_WRITE_I,
PT_WRITE_D,
#[cfg(apple_targets)]
PT_WRITE_U,
PT_CONTINUE,
PT_KILL,
#[cfg(any(any(freebsdlike, apple_targets),
all(target_os = "openbsd", target_arch = "x86_64"),
all(target_os = "netbsd", any(target_arch = "x86_64",
target_arch = "powerpc"))))]
PT_STEP,
PT_ATTACH,
PT_DETACH,
#[cfg(apple_targets)]
PT_SIGEXC,
#[cfg(apple_targets)]
PT_THUPDATE,
#[cfg(apple_targets)]
PT_ATTACHEXC
}
}
unsafe fn ptrace_other(
request: Request,
pid: Pid,
addr: AddressType,
data: c_int,
) -> Result<c_int> {
unsafe {
Errno::result(libc::ptrace(
request as RequestType,
libc::pid_t::from(pid),
addr,
data,
))
.map(|_| 0)
}
}
/// Sets the process as traceable, as with `ptrace(PT_TRACEME, ...)`
///
/// Indicates that this process is to be traced by its parent.
/// This is the only ptrace request to be issued by the tracee.
pub fn traceme() -> Result<()> {
unsafe {
ptrace_other(Request::PT_TRACE_ME, Pid::from_raw(0), ptr::null_mut(), 0)
.map(drop)
}
}
/// Attach to a running process, as with `ptrace(PT_ATTACH, ...)`
///
/// Attaches to the process specified by `pid`, making it a tracee of the calling process.
pub fn attach(pid: Pid) -> Result<()> {
unsafe {
ptrace_other(Request::PT_ATTACH, pid, ptr::null_mut(), 0).map(drop)
}
}
/// Detaches the current running process, as with `ptrace(PT_DETACH, ...)`
///
/// Detaches from the process specified by `pid` allowing it to run freely, optionally delivering a
/// signal specified by `sig`.
pub fn detach<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
let data = match sig.into() {
Some(s) => s as c_int,
None => 0,
};
unsafe {
ptrace_other(Request::PT_DETACH, pid, ptr::null_mut(), data).map(drop)
}
}
/// Restart the stopped tracee process, as with `ptrace(PTRACE_CONT, ...)`
///
/// Continues the execution of the process with PID `pid`, optionally
/// delivering a signal specified by `sig`.
pub fn cont<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
let data = match sig.into() {
Some(s) => s as c_int,
None => 0,
};
unsafe {
// Ignore the useless return value
ptrace_other(Request::PT_CONTINUE, pid, 1 as AddressType, data)
.map(drop)
}
}
/// Issues a kill request as with `ptrace(PT_KILL, ...)`
///
/// This request is equivalent to `ptrace(PT_CONTINUE, ..., SIGKILL);`
pub fn kill(pid: Pid) -> Result<()> {
unsafe {
ptrace_other(Request::PT_KILL, pid, 0 as AddressType, 0).map(drop)
}
}
/// Move the stopped tracee process forward by a single step as with
/// `ptrace(PT_STEP, ...)`
///
/// Advances the execution of the process with PID `pid` by a single step optionally delivering a
/// signal specified by `sig`.
///
/// # Example
/// ```rust
/// use nix::sys::ptrace::step;
/// use nix::unistd::Pid;
/// use nix::sys::signal::Signal;
/// use nix::sys::wait::*;
/// // If a process changes state to the stopped state because of a SIGUSR1
/// // signal, this will step the process forward and forward the user
/// // signal to the stopped process
/// match waitpid(Pid::from_raw(-1), None) {
/// Ok(WaitStatus::Stopped(pid, Signal::SIGUSR1)) => {
/// let _ = step(pid, Signal::SIGUSR1);
/// }
/// _ => {},
/// }
/// ```
#[cfg(any(
any(freebsdlike, apple_targets),
all(target_os = "openbsd", target_arch = "x86_64"),
all(
target_os = "netbsd",
any(target_arch = "x86_64", target_arch = "powerpc")
)
))]
pub fn step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
let data = match sig.into() {
Some(s) => s as c_int,
None => 0,
};
unsafe {
ptrace_other(Request::PT_STEP, pid, ptr::null_mut(), data).map(drop)
}
}
/// Reads a word from a processes memory at the given address
// Technically, ptrace doesn't dereference the pointer. It passes it directly
// to the kernel.
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn read(pid: Pid, addr: AddressType) -> Result<c_int> {
unsafe {
// Traditionally there was a difference between reading data or
// instruction memory but not in modern systems.
ptrace_other(Request::PT_READ_D, pid, addr, 0)
}
}
/// Writes a word into the processes memory at the given address
// Technically, ptrace doesn't dereference the pointer. It passes it directly
// to the kernel.
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn write(pid: Pid, addr: AddressType, data: c_int) -> Result<()> {
unsafe { ptrace_other(Request::PT_WRITE_D, pid, addr, data).map(drop) }
}

821
vendor/nix/src/sys/ptrace/linux.rs vendored Normal file
View File

@@ -0,0 +1,821 @@
//! For detailed description of the ptrace requests, consult `man ptrace`.
use crate::errno::Errno;
use crate::sys::signal::Signal;
use crate::unistd::Pid;
use crate::Result;
use cfg_if::cfg_if;
use libc::{self, c_long, c_void, siginfo_t};
use std::{mem, ptr};
pub type AddressType = *mut ::libc::c_void;
#[cfg(all(
target_os = "linux",
any(
all(
any(target_arch = "x86_64", target_arch = "aarch64"),
any(target_env = "gnu", target_env = "musl")
),
all(target_arch = "x86", target_env = "gnu"),
all(target_arch = "riscv64", target_env = "gnu"),
),
))]
use libc::user_regs_struct;
cfg_if! {
if #[cfg(any(all(target_os = "linux", target_arch = "s390x"),
all(target_os = "linux", target_env = "gnu"),
target_env = "uclibc"))] {
#[doc(hidden)]
pub type RequestType = ::libc::c_uint;
} else {
#[doc(hidden)]
pub type RequestType = ::libc::c_int;
}
}
libc_enum! {
#[cfg_attr(not(any(target_env = "musl", target_env = "uclibc", target_os = "android", target_env = "ohos")), repr(u32))]
#[cfg_attr(any(target_env = "musl", target_env = "uclibc", target_os = "android", target_env = "ohos"), repr(i32))]
/// Ptrace Request enum defining the action to be taken.
#[non_exhaustive]
pub enum Request {
PTRACE_TRACEME,
PTRACE_PEEKTEXT,
PTRACE_PEEKDATA,
PTRACE_PEEKUSER,
PTRACE_POKETEXT,
PTRACE_POKEDATA,
PTRACE_POKEUSER,
PTRACE_CONT,
PTRACE_KILL,
PTRACE_SINGLESTEP,
#[cfg(any(all(target_os = "android", target_pointer_width = "32"),
all(target_os = "linux", any(target_env = "musl",
target_env = "ohos",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "x86_64",
target_pointer_width = "32"))))]
PTRACE_GETREGS,
#[cfg(any(all(target_os = "android", target_pointer_width = "32"),
all(target_os = "linux", any(target_env = "musl",
target_env = "ohos",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "x86_64",
target_pointer_width = "32"))))]
PTRACE_SETREGS,
#[cfg(any(all(target_os = "android", target_pointer_width = "32"),
all(target_os = "linux", any(target_env = "musl",
target_env = "ohos",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "x86_64",
target_pointer_width = "32"))))]
PTRACE_GETFPREGS,
#[cfg(any(all(target_os = "android", target_pointer_width = "32"),
all(target_os = "linux", any(target_env = "musl",
target_env = "ohos",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "x86_64",
target_pointer_width = "32"))))]
PTRACE_SETFPREGS,
PTRACE_ATTACH,
PTRACE_DETACH,
#[cfg(all(target_os = "linux", any(target_env = "musl",
target_env = "ohos",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "x86",
target_arch = "x86_64")))]
PTRACE_GETFPXREGS,
#[cfg(all(target_os = "linux", any(target_env = "musl",
target_env = "ohos",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "x86",
target_arch = "x86_64")))]
PTRACE_SETFPXREGS,
PTRACE_SYSCALL,
PTRACE_SETOPTIONS,
PTRACE_GETEVENTMSG,
PTRACE_GETSIGINFO,
PTRACE_SETSIGINFO,
#[cfg(all(target_os = "linux", not(any(target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"))))]
PTRACE_GETREGSET,
#[cfg(all(target_os = "linux", not(any(target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"))))]
PTRACE_SETREGSET,
#[cfg(target_os = "linux")]
PTRACE_SEIZE,
#[cfg(target_os = "linux")]
PTRACE_INTERRUPT,
#[cfg(all(target_os = "linux", not(any(target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"))))]
PTRACE_LISTEN,
#[cfg(all(target_os = "linux", not(any(target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"))))]
PTRACE_PEEKSIGINFO,
#[cfg(all(target_os = "linux", target_env = "gnu",
any(target_arch = "x86", target_arch = "x86_64")))]
PTRACE_SYSEMU,
#[cfg(all(target_os = "linux", target_env = "gnu",
any(target_arch = "x86", target_arch = "x86_64")))]
PTRACE_SYSEMU_SINGLESTEP,
#[cfg(all(target_os = "linux", target_env = "gnu"))]
PTRACE_GET_SYSCALL_INFO,
}
}
libc_enum! {
#[repr(i32)]
/// Using the ptrace options the tracer can configure the tracee to stop
/// at certain events. This enum is used to define those events as defined
/// in `man ptrace`.
#[non_exhaustive]
pub enum Event {
/// Event that stops before a return from fork or clone.
PTRACE_EVENT_FORK,
/// Event that stops before a return from vfork or clone.
PTRACE_EVENT_VFORK,
/// Event that stops before a return from clone.
PTRACE_EVENT_CLONE,
/// Event that stops before a return from execve.
PTRACE_EVENT_EXEC,
/// Event for a return from vfork.
PTRACE_EVENT_VFORK_DONE,
/// Event for a stop before an exit. Unlike the waitpid Exit status program.
/// registers can still be examined
PTRACE_EVENT_EXIT,
/// Stop triggered by a seccomp rule on a tracee.
PTRACE_EVENT_SECCOMP,
/// Stop triggered by the `INTERRUPT` syscall, or a group stop,
/// or when a new child is attached.
PTRACE_EVENT_STOP,
}
}
#[cfg(all(
target_os = "linux",
any(
all(
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64",
)
),
all(
target_env = "musl",
target_arch = "aarch64",
)
),
))]
libc_enum! {
#[repr(i32)]
/// Defines a specific register set, as used in `PTRACE_GETREGSET` and `PTRACE_SETREGSET`.
#[non_exhaustive]
pub enum RegisterSetValue {
NT_PRSTATUS,
NT_PRFPREG,
NT_PRPSINFO,
NT_TASKSTRUCT,
NT_AUXV,
}
}
#[cfg(all(
target_os = "linux",
any(
all(
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64",
)
),
all(
target_env = "musl",
target_arch = "aarch64",
)
),
))]
/// Represents register set areas, such as general-purpose registers or
/// floating-point registers.
///
/// # Safety
///
/// This trait is marked unsafe, since implementation of the trait must match
/// ptrace's request `VALUE` and return data type `Regs`.
pub unsafe trait RegisterSet {
/// Corresponding type of registers in the kernel.
const VALUE: RegisterSetValue;
/// Struct representing the register space.
type Regs;
}
#[cfg(all(
target_os = "linux",
any(
all(
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64",
)
),
all(
target_env = "musl",
target_arch = "aarch64",
)
),
))]
/// Register sets used in [`getregset`] and [`setregset`]
pub mod regset {
use super::*;
#[derive(Debug, Clone, Copy)]
/// General-purpose registers.
pub enum NT_PRSTATUS {}
unsafe impl RegisterSet for NT_PRSTATUS {
const VALUE: RegisterSetValue = RegisterSetValue::NT_PRSTATUS;
type Regs = user_regs_struct;
}
#[derive(Debug, Clone, Copy)]
/// Floating-point registers.
pub enum NT_PRFPREG {}
unsafe impl RegisterSet for NT_PRFPREG {
const VALUE: RegisterSetValue = RegisterSetValue::NT_PRFPREG;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
type Regs = libc::user_fpregs_struct;
#[cfg(target_arch = "aarch64")]
type Regs = libc::user_fpsimd_struct;
#[cfg(target_arch = "riscv64")]
type Regs = libc::__riscv_mc_d_ext_state;
}
}
libc_bitflags! {
/// Ptrace options used in conjunction with the PTRACE_SETOPTIONS request.
/// See `man ptrace` for more details.
pub struct Options: libc::c_int {
/// When delivering system call traps set a bit to allow tracer to
/// distinguish between normal stops or syscall stops. May not work on
/// all systems.
PTRACE_O_TRACESYSGOOD;
/// Stop tracee at next fork and start tracing the forked process.
PTRACE_O_TRACEFORK;
/// Stop tracee at next vfork call and trace the vforked process.
PTRACE_O_TRACEVFORK;
/// Stop tracee at next clone call and trace the cloned process.
PTRACE_O_TRACECLONE;
/// Stop tracee at next execve call.
PTRACE_O_TRACEEXEC;
/// Stop tracee at vfork completion.
PTRACE_O_TRACEVFORKDONE;
/// Stop tracee at next exit call. Stops before exit commences allowing
/// tracer to see location of exit and register states.
PTRACE_O_TRACEEXIT;
/// Stop tracee when a SECCOMP_RET_TRACE rule is triggered. See `man seccomp` for more
/// details.
PTRACE_O_TRACESECCOMP;
/// Send a SIGKILL to the tracee if the tracer exits. This is useful
/// for ptrace jailers to prevent tracees from escaping their control.
PTRACE_O_EXITKILL;
}
}
fn ptrace_peek(
request: Request,
pid: Pid,
addr: AddressType,
data: *mut c_void,
) -> Result<c_long> {
let ret = unsafe {
Errno::clear();
libc::ptrace(request as RequestType, libc::pid_t::from(pid), addr, data)
};
match Errno::result(ret) {
Ok(..) | Err(Errno::UnknownErrno) => Ok(ret),
err @ Err(..) => err,
}
}
/// Get user registers, as with `ptrace(PTRACE_GETREGS, ...)`
///
/// Note that since `PTRACE_GETREGS` are not available on all platforms (as in [ptrace(2)]),
/// `ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, ...)` is used instead to achieve the same effect
/// on aarch64 and riscv64.
///
/// [ptrace(2)]: https://www.man7.org/linux/man-pages/man2/ptrace.2.html
#[cfg(all(
target_os = "linux",
any(
all(
target_arch = "x86_64",
any(target_env = "gnu", target_env = "musl")
),
all(target_arch = "x86", target_env = "gnu")
)
))]
pub fn getregs(pid: Pid) -> Result<user_regs_struct> {
ptrace_get_data::<user_regs_struct>(Request::PTRACE_GETREGS, pid)
}
/// Get user registers, as with `ptrace(PTRACE_GETREGS, ...)`
///
/// Note that since `PTRACE_GETREGS` are not available on all platforms (as in [ptrace(2)]),
/// `ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, ...)` is used instead to achieve the same effect
/// on aarch64 and riscv64.
///
/// [ptrace(2)]: https://www.man7.org/linux/man-pages/man2/ptrace.2.html
#[cfg(all(
target_os = "linux",
any(
all(
target_arch = "aarch64",
any(target_env = "gnu", target_env = "musl")
),
all(target_arch = "riscv64", target_env = "gnu")
)
))]
pub fn getregs(pid: Pid) -> Result<user_regs_struct> {
getregset::<regset::NT_PRSTATUS>(pid)
}
/// Get a particular set of user registers, as with `ptrace(PTRACE_GETREGSET, ...)`
#[cfg(all(
target_os = "linux",
any(
all(
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64"
)
),
all(target_env = "musl", target_arch = "aarch64")
)
))]
pub fn getregset<S: RegisterSet>(pid: Pid) -> Result<S::Regs> {
let request = Request::PTRACE_GETREGSET;
let mut data = mem::MaybeUninit::<S::Regs>::uninit();
let mut iov = libc::iovec {
iov_base: data.as_mut_ptr().cast(),
iov_len: mem::size_of::<S::Regs>(),
};
unsafe {
ptrace_other(
request,
pid,
S::VALUE as i32 as AddressType,
(&mut iov as *mut libc::iovec).cast(),
)?;
};
Ok(unsafe { data.assume_init() })
}
/// Set user registers, as with `ptrace(PTRACE_SETREGS, ...)`
///
/// Note that since `PTRACE_SETREGS` are not available on all platforms (as in [ptrace(2)]),
/// `ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, ...)` is used instead to achieve the same effect
/// on aarch64 and riscv64.
///
/// [ptrace(2)]: https://www.man7.org/linux/man-pages/man2/ptrace.2.html
#[cfg(all(
target_os = "linux",
any(
all(
target_arch = "x86_64",
any(target_env = "gnu", target_env = "musl")
),
all(target_arch = "x86", target_env = "gnu")
)
))]
pub fn setregs(pid: Pid, regs: user_regs_struct) -> Result<()> {
let res = unsafe {
libc::ptrace(
Request::PTRACE_SETREGS as RequestType,
libc::pid_t::from(pid),
ptr::null_mut::<c_void>(),
&regs as *const user_regs_struct as *const c_void,
)
};
Errno::result(res).map(drop)
}
/// Set user registers, as with `ptrace(PTRACE_SETREGS, ...)`
///
/// Note that since `PTRACE_SETREGS` are not available on all platforms (as in [ptrace(2)]),
/// `ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, ...)` is used instead to achieve the same effect
/// on aarch64 and riscv64.
///
/// [ptrace(2)]: https://www.man7.org/linux/man-pages/man2/ptrace.2.html
#[cfg(all(
target_os = "linux",
any(
all(
target_env = "gnu",
any(target_arch = "aarch64", target_arch = "riscv64")
),
all(target_env = "musl", target_arch = "aarch64")
)
))]
pub fn setregs(pid: Pid, regs: user_regs_struct) -> Result<()> {
setregset::<regset::NT_PRSTATUS>(pid, regs)
}
/// Set a particular set of user registers, as with `ptrace(PTRACE_SETREGSET, ...)`
#[cfg(all(
target_os = "linux",
any(
all(
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64"
)
),
all(target_env = "musl", target_arch = "aarch64")
)
))]
pub fn setregset<S: RegisterSet>(pid: Pid, mut regs: S::Regs) -> Result<()> {
let mut iov = libc::iovec {
iov_base: (&mut regs as *mut S::Regs).cast(),
iov_len: mem::size_of::<S::Regs>(),
};
unsafe {
ptrace_other(
Request::PTRACE_SETREGSET,
pid,
S::VALUE as i32 as AddressType,
(&mut iov as *mut libc::iovec).cast(),
)?;
}
Ok(())
}
/// Function for ptrace requests that return values from the data field.
/// Some ptrace get requests populate structs or larger elements than `c_long`
/// and therefore use the data field to return values. This function handles these
/// requests.
fn ptrace_get_data<T>(request: Request, pid: Pid) -> Result<T> {
let mut data = mem::MaybeUninit::<T>::uninit();
let res = unsafe {
libc::ptrace(
request as RequestType,
libc::pid_t::from(pid),
ptr::null_mut::<T>(),
data.as_mut_ptr(),
)
};
Errno::result(res)?;
Ok(unsafe { data.assume_init() })
}
unsafe fn ptrace_other(
request: Request,
pid: Pid,
addr: AddressType,
data: *mut c_void,
) -> Result<c_long> {
unsafe {
Errno::result(libc::ptrace(
request as RequestType,
libc::pid_t::from(pid),
addr,
data,
))
.map(|_| 0)
}
}
/// Set options, as with `ptrace(PTRACE_SETOPTIONS, ...)`.
pub fn setoptions(pid: Pid, options: Options) -> Result<()> {
let res = unsafe {
libc::ptrace(
Request::PTRACE_SETOPTIONS as RequestType,
libc::pid_t::from(pid),
ptr::null_mut::<c_void>(),
options.bits() as *mut c_void,
)
};
Errno::result(res).map(drop)
}
/// Gets a ptrace event as described by `ptrace(PTRACE_GETEVENTMSG, ...)`
pub fn getevent(pid: Pid) -> Result<c_long> {
ptrace_get_data::<c_long>(Request::PTRACE_GETEVENTMSG, pid)
}
/// Get siginfo as with `ptrace(PTRACE_GETSIGINFO, ...)`
pub fn getsiginfo(pid: Pid) -> Result<siginfo_t> {
ptrace_get_data::<siginfo_t>(Request::PTRACE_GETSIGINFO, pid)
}
/// Set siginfo as with `ptrace(PTRACE_SETSIGINFO, ...)`
pub fn setsiginfo(pid: Pid, sig: &siginfo_t) -> Result<()> {
let ret = unsafe {
Errno::clear();
libc::ptrace(
Request::PTRACE_SETSIGINFO as RequestType,
libc::pid_t::from(pid),
ptr::null_mut::<c_void>(),
sig as *const _ as *const c_void,
)
};
match Errno::result(ret) {
Ok(_) => Ok(()),
Err(e) => Err(e),
}
}
/// Get the informations of the syscall that caused the stop, as with
/// `ptrace(PTRACE_GET_SYSCALL_INFO, ...`.
#[cfg(all(target_os = "linux", target_env = "gnu"))]
pub fn syscall_info(pid: Pid) -> Result<libc::ptrace_syscall_info> {
ptrace_get_data::<libc::ptrace_syscall_info>(Request::PTRACE_GET_SYSCALL_INFO, pid)
}
/// Sets the process as traceable, as with `ptrace(PTRACE_TRACEME, ...)`
///
/// Indicates that this process is to be traced by its parent.
/// This is the only ptrace request to be issued by the tracee.
pub fn traceme() -> Result<()> {
unsafe {
ptrace_other(
Request::PTRACE_TRACEME,
Pid::from_raw(0),
ptr::null_mut(),
ptr::null_mut(),
)
.map(drop) // ignore the useless return value
}
}
/// Continue execution until the next syscall, as with `ptrace(PTRACE_SYSCALL, ...)`
///
/// Arranges for the tracee to be stopped at the next entry to or exit from a system call,
/// optionally delivering a signal specified by `sig`.
pub fn syscall<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
let data = match sig.into() {
Some(s) => s as i32 as *mut c_void,
None => ptr::null_mut(),
};
unsafe {
ptrace_other(Request::PTRACE_SYSCALL, pid, ptr::null_mut(), data)
.map(drop) // ignore the useless return value
}
}
/// Continue execution until the next syscall, as with `ptrace(PTRACE_SYSEMU, ...)`
///
/// In contrast to the `syscall` function, the syscall stopped at will not be executed.
/// Thus the the tracee will only be stopped once per syscall,
/// optionally delivering a signal specified by `sig`.
#[cfg(all(
target_os = "linux",
target_env = "gnu",
any(target_arch = "x86", target_arch = "x86_64")
))]
pub fn sysemu<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
let data = match sig.into() {
Some(s) => s as i32 as *mut c_void,
None => ptr::null_mut(),
};
unsafe {
ptrace_other(Request::PTRACE_SYSEMU, pid, ptr::null_mut(), data)
.map(drop)
// ignore the useless return value
}
}
/// Attach to a running process, as with `ptrace(PTRACE_ATTACH, ...)`
///
/// Attaches to the process specified by `pid`, making it a tracee of the calling process.
pub fn attach(pid: Pid) -> Result<()> {
unsafe {
ptrace_other(
Request::PTRACE_ATTACH,
pid,
ptr::null_mut(),
ptr::null_mut(),
)
.map(drop) // ignore the useless return value
}
}
/// Attach to a running process, as with `ptrace(PTRACE_SEIZE, ...)`
///
/// Attaches to the process specified in pid, making it a tracee of the calling process.
#[cfg(target_os = "linux")]
pub fn seize(pid: Pid, options: Options) -> Result<()> {
unsafe {
ptrace_other(
Request::PTRACE_SEIZE,
pid,
ptr::null_mut(),
options.bits() as *mut c_void,
)
.map(drop) // ignore the useless return value
}
}
/// Detaches the current running process, as with `ptrace(PTRACE_DETACH, ...)`
///
/// Detaches from the process specified by `pid` allowing it to run freely, optionally delivering a
/// signal specified by `sig`.
pub fn detach<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
let data = match sig.into() {
Some(s) => s as i32 as *mut c_void,
None => ptr::null_mut(),
};
unsafe {
ptrace_other(Request::PTRACE_DETACH, pid, ptr::null_mut(), data)
.map(drop)
}
}
/// Restart the stopped tracee process, as with `ptrace(PTRACE_CONT, ...)`
///
/// Continues the execution of the process with PID `pid`, optionally
/// delivering a signal specified by `sig`.
pub fn cont<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
let data = match sig.into() {
Some(s) => s as i32 as *mut c_void,
None => ptr::null_mut(),
};
unsafe {
ptrace_other(Request::PTRACE_CONT, pid, ptr::null_mut(), data).map(drop)
// ignore the useless return value
}
}
/// Stop a tracee, as with `ptrace(PTRACE_INTERRUPT, ...)`
///
/// This request is equivalent to `ptrace(PTRACE_INTERRUPT, ...)`
#[cfg(target_os = "linux")]
pub fn interrupt(pid: Pid) -> Result<()> {
unsafe {
ptrace_other(
Request::PTRACE_INTERRUPT,
pid,
ptr::null_mut(),
ptr::null_mut(),
)
.map(drop)
}
}
/// Issues a kill request as with `ptrace(PTRACE_KILL, ...)`
///
/// This request is equivalent to `ptrace(PTRACE_CONT, ..., SIGKILL);`
pub fn kill(pid: Pid) -> Result<()> {
unsafe {
ptrace_other(
Request::PTRACE_KILL,
pid,
ptr::null_mut(),
ptr::null_mut(),
)
.map(drop)
}
}
/// Move the stopped tracee process forward by a single step as with
/// `ptrace(PTRACE_SINGLESTEP, ...)`
///
/// Advances the execution of the process with PID `pid` by a single step optionally delivering a
/// signal specified by `sig`.
///
/// # Example
/// ```rust
/// use nix::sys::ptrace::step;
/// use nix::unistd::Pid;
/// use nix::sys::signal::Signal;
/// use nix::sys::wait::*;
///
/// // If a process changes state to the stopped state because of a SIGUSR1
/// // signal, this will step the process forward and forward the user
/// // signal to the stopped process
/// match waitpid(Pid::from_raw(-1), None) {
/// Ok(WaitStatus::Stopped(pid, Signal::SIGUSR1)) => {
/// let _ = step(pid, Signal::SIGUSR1);
/// }
/// _ => {},
/// }
/// ```
pub fn step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
let data = match sig.into() {
Some(s) => s as i32 as *mut c_void,
None => ptr::null_mut(),
};
unsafe {
ptrace_other(Request::PTRACE_SINGLESTEP, pid, ptr::null_mut(), data)
.map(drop)
}
}
/// Move the stopped tracee process forward by a single step or stop at the next syscall
/// as with `ptrace(PTRACE_SYSEMU_SINGLESTEP, ...)`
///
/// Advances the execution by a single step or until the next syscall.
/// In case the tracee is stopped at a syscall, the syscall will not be executed.
/// Optionally, the signal specified by `sig` is delivered to the tracee upon continuation.
#[cfg(all(
target_os = "linux",
target_env = "gnu",
any(target_arch = "x86", target_arch = "x86_64")
))]
pub fn sysemu_step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
let data = match sig.into() {
Some(s) => s as i32 as *mut c_void,
None => ptr::null_mut(),
};
unsafe {
ptrace_other(
Request::PTRACE_SYSEMU_SINGLESTEP,
pid,
ptr::null_mut(),
data,
)
.map(drop) // ignore the useless return value
}
}
/// Reads a word from a processes memory at the given address, as with
/// ptrace(PTRACE_PEEKDATA, ...)
pub fn read(pid: Pid, addr: AddressType) -> Result<c_long> {
ptrace_peek(Request::PTRACE_PEEKDATA, pid, addr, ptr::null_mut())
}
/// Writes a word into the processes memory at the given address, as with
/// ptrace(PTRACE_POKEDATA, ...)
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn write(pid: Pid, addr: AddressType, data: c_long) -> Result<()> {
unsafe {
// Safety(not_unsafe_ptr_arg_deref):
// `ptrace_other` is a common abstract
// but in `PTRACE_POKEDATA` situation, `data` is exactly what will be wtitten
ptrace_other(Request::PTRACE_POKEDATA, pid, addr, data as *mut c_void)
.map(drop)
}
}
/// Reads a word from a user area at `offset`, as with ptrace(PTRACE_PEEKUSER, ...).
/// The user struct definition can be found in `/usr/include/sys/user.h`.
pub fn read_user(pid: Pid, offset: AddressType) -> Result<c_long> {
ptrace_peek(Request::PTRACE_PEEKUSER, pid, offset, ptr::null_mut())
}
/// Writes a word to a user area at `offset`, as with ptrace(PTRACE_POKEUSER, ...).
/// The user struct definition can be found in `/usr/include/sys/user.h`.
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn write_user(pid: Pid, offset: AddressType, data: c_long) -> Result<()> {
unsafe {
// Safety(not_unsafe_ptr_arg_deref):
// `ptrace_other` is a common abstract
// but in `PTRACE_POKEDATA` situation, `data` is exactly what will be wtitten
ptrace_other(Request::PTRACE_POKEUSER, pid, offset, data as *mut c_void)
.map(drop)
}
}

13
vendor/nix/src/sys/ptrace/mod.rs vendored Normal file
View File

@@ -0,0 +1,13 @@
//! Provides helpers for making ptrace system calls
#[cfg(linux_android)]
mod linux;
#[cfg(linux_android)]
pub use self::linux::*;
#[cfg(bsd)]
mod bsd;
#[cfg(bsd)]
pub use self::bsd::*;

337
vendor/nix/src/sys/quota.rs vendored Normal file
View File

@@ -0,0 +1,337 @@
//! Set and configure disk quotas for users, groups, or projects.
//!
//! # Examples
//!
//! Enabling and setting a quota:
//!
//! ```rust,no_run
//! # use nix::sys::quota::{Dqblk, quotactl_on, quotactl_set, QuotaFmt, QuotaType, QuotaValidFlags};
//! quotactl_on(QuotaType::USRQUOTA, "/dev/sda1", QuotaFmt::QFMT_VFS_V1, "aquota.user").unwrap();
//! let mut dqblk: Dqblk = Default::default();
//! dqblk.set_blocks_hard_limit(10000);
//! dqblk.set_blocks_soft_limit(8000);
//! quotactl_set(QuotaType::USRQUOTA, "/dev/sda1", 50, &dqblk, QuotaValidFlags::QIF_BLIMITS).unwrap();
//! ```
use crate::errno::Errno;
use crate::{NixPath, Result};
use libc::{self, c_char, c_int};
use std::default::Default;
use std::{mem, ptr};
struct QuotaCmd(QuotaSubCmd, QuotaType);
impl QuotaCmd {
fn as_int(&self) -> c_int {
libc::QCMD(self.0 as i32, self.1 as i32)
}
}
// linux quota version >= 2
libc_enum! {
#[repr(i32)]
enum QuotaSubCmd {
Q_SYNC,
Q_QUOTAON,
Q_QUOTAOFF,
Q_GETQUOTA,
Q_SETQUOTA,
}
}
libc_enum! {
/// The scope of the quota.
#[repr(i32)]
#[non_exhaustive]
pub enum QuotaType {
/// Specify a user quota
USRQUOTA,
/// Specify a group quota
GRPQUOTA,
}
}
libc_enum! {
/// The type of quota format to use.
#[repr(i32)]
#[non_exhaustive]
pub enum QuotaFmt {
/// Use the original quota format.
QFMT_VFS_OLD,
/// Use the standard VFS v0 quota format.
///
/// Handles 32-bit UIDs/GIDs and quota limits up to 2<sup>32</sup> bytes/2<sup>32</sup> inodes.
QFMT_VFS_V0,
/// Use the VFS v1 quota format.
///
/// Handles 32-bit UIDs/GIDs and quota limits of 2<sup>64</sup> bytes/2<sup>64</sup> inodes.
QFMT_VFS_V1,
}
}
libc_bitflags!(
/// Indicates the quota fields that are valid to read from.
#[derive(Default)]
pub struct QuotaValidFlags: u32 {
/// The block hard & soft limit fields.
QIF_BLIMITS;
/// The current space field.
QIF_SPACE;
/// The inode hard & soft limit fields.
QIF_ILIMITS;
/// The current inodes field.
QIF_INODES;
/// The disk use time limit field.
QIF_BTIME;
/// The file quote time limit field.
QIF_ITIME;
/// All block & inode limits.
QIF_LIMITS;
/// The space & inodes usage fields.
QIF_USAGE;
/// The time limit fields.
QIF_TIMES;
/// All fields.
QIF_ALL;
}
);
/// Wrapper type for `if_dqblk`
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Dqblk(libc::dqblk);
impl Default for Dqblk {
fn default() -> Dqblk {
Dqblk(libc::dqblk {
dqb_bhardlimit: 0,
dqb_bsoftlimit: 0,
dqb_curspace: 0,
dqb_ihardlimit: 0,
dqb_isoftlimit: 0,
dqb_curinodes: 0,
dqb_btime: 0,
dqb_itime: 0,
dqb_valid: 0,
})
}
}
impl Dqblk {
/// The absolute limit on disk quota blocks allocated.
pub fn blocks_hard_limit(&self) -> Option<u64> {
let valid_fields =
QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
if valid_fields.contains(QuotaValidFlags::QIF_BLIMITS) {
Some(self.0.dqb_bhardlimit)
} else {
None
}
}
/// Set the absolute limit on disk quota blocks allocated.
pub fn set_blocks_hard_limit(&mut self, limit: u64) {
self.0.dqb_bhardlimit = limit;
}
/// Preferred limit on disk quota blocks
pub fn blocks_soft_limit(&self) -> Option<u64> {
let valid_fields =
QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
if valid_fields.contains(QuotaValidFlags::QIF_BLIMITS) {
Some(self.0.dqb_bsoftlimit)
} else {
None
}
}
/// Set the preferred limit on disk quota blocks allocated.
pub fn set_blocks_soft_limit(&mut self, limit: u64) {
self.0.dqb_bsoftlimit = limit;
}
/// Current occupied space (bytes).
pub fn occupied_space(&self) -> Option<u64> {
let valid_fields =
QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
if valid_fields.contains(QuotaValidFlags::QIF_SPACE) {
Some(self.0.dqb_curspace)
} else {
None
}
}
/// Maximum number of allocated inodes.
pub fn inodes_hard_limit(&self) -> Option<u64> {
let valid_fields =
QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
if valid_fields.contains(QuotaValidFlags::QIF_ILIMITS) {
Some(self.0.dqb_ihardlimit)
} else {
None
}
}
/// Set the maximum number of allocated inodes.
pub fn set_inodes_hard_limit(&mut self, limit: u64) {
self.0.dqb_ihardlimit = limit;
}
/// Preferred inode limit
pub fn inodes_soft_limit(&self) -> Option<u64> {
let valid_fields =
QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
if valid_fields.contains(QuotaValidFlags::QIF_ILIMITS) {
Some(self.0.dqb_isoftlimit)
} else {
None
}
}
/// Set the preferred limit of allocated inodes.
pub fn set_inodes_soft_limit(&mut self, limit: u64) {
self.0.dqb_isoftlimit = limit;
}
/// Current number of allocated inodes.
pub fn allocated_inodes(&self) -> Option<u64> {
let valid_fields =
QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
if valid_fields.contains(QuotaValidFlags::QIF_INODES) {
Some(self.0.dqb_curinodes)
} else {
None
}
}
/// Time limit for excessive disk use.
pub fn block_time_limit(&self) -> Option<u64> {
let valid_fields =
QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
if valid_fields.contains(QuotaValidFlags::QIF_BTIME) {
Some(self.0.dqb_btime)
} else {
None
}
}
/// Set the time limit for excessive disk use.
pub fn set_block_time_limit(&mut self, limit: u64) {
self.0.dqb_btime = limit;
}
/// Time limit for excessive files.
pub fn inode_time_limit(&self) -> Option<u64> {
let valid_fields =
QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
if valid_fields.contains(QuotaValidFlags::QIF_ITIME) {
Some(self.0.dqb_itime)
} else {
None
}
}
/// Set the time limit for excessive files.
pub fn set_inode_time_limit(&mut self, limit: u64) {
self.0.dqb_itime = limit;
}
}
fn quotactl<P: ?Sized + NixPath>(
cmd: QuotaCmd,
special: Option<&P>,
id: c_int,
addr: *mut c_char,
) -> Result<()> {
unsafe {
Errno::clear();
let res = match special {
Some(dev) => dev.with_nix_path(|path| {
libc::quotactl(cmd.as_int(), path.as_ptr(), id, addr)
}),
None => Ok(libc::quotactl(cmd.as_int(), ptr::null(), id, addr)),
}?;
Errno::result(res).map(drop)
}
}
/// Turn on disk quotas for a block device.
pub fn quotactl_on<P: ?Sized + NixPath>(
which: QuotaType,
special: &P,
format: QuotaFmt,
quota_file: &P,
) -> Result<()> {
quota_file.with_nix_path(|path| {
let mut path_copy = path.to_bytes_with_nul().to_owned();
let p: *mut c_char = path_copy.as_mut_ptr().cast();
quotactl(
QuotaCmd(QuotaSubCmd::Q_QUOTAON, which),
Some(special),
format as c_int,
p,
)
})?
}
/// Disable disk quotas for a block device.
pub fn quotactl_off<P: ?Sized + NixPath>(
which: QuotaType,
special: &P,
) -> Result<()> {
quotactl(
QuotaCmd(QuotaSubCmd::Q_QUOTAOFF, which),
Some(special),
0,
ptr::null_mut(),
)
}
/// Update the on-disk copy of quota usages for a filesystem.
///
/// If `special` is `None`, then all file systems with active quotas are sync'd.
pub fn quotactl_sync<P: ?Sized + NixPath>(
which: QuotaType,
special: Option<&P>,
) -> Result<()> {
quotactl(
QuotaCmd(QuotaSubCmd::Q_SYNC, which),
special,
0,
ptr::null_mut(),
)
}
/// Get disk quota limits and current usage for the given user/group id.
pub fn quotactl_get<P: ?Sized + NixPath>(
which: QuotaType,
special: &P,
id: c_int,
) -> Result<Dqblk> {
let mut dqblk = mem::MaybeUninit::<libc::dqblk>::uninit();
quotactl(
QuotaCmd(QuotaSubCmd::Q_GETQUOTA, which),
Some(special),
id,
dqblk.as_mut_ptr().cast(),
)?;
Ok(unsafe { Dqblk(dqblk.assume_init()) })
}
/// Configure quota values for the specified fields for a given user/group id.
pub fn quotactl_set<P: ?Sized + NixPath>(
which: QuotaType,
special: &P,
id: c_int,
dqblk: &Dqblk,
fields: QuotaValidFlags,
) -> Result<()> {
let mut dqblk_copy = *dqblk;
dqblk_copy.0.dqb_valid = fields.bits();
quotactl(
QuotaCmd(QuotaSubCmd::Q_SETQUOTA, which),
Some(special),
id,
&mut dqblk_copy as *mut _ as *mut c_char,
)
}

141
vendor/nix/src/sys/reboot.rs vendored Normal file
View File

@@ -0,0 +1,141 @@
//! Reboot/shutdown
//!
//! On Linux, This can also be used to enable/disable Ctrl-Alt-Delete.
use crate::errno::Errno;
use crate::Result;
use cfg_if::cfg_if;
use std::convert::Infallible;
cfg_if! {
if #[cfg(target_os = "linux")] {
use std::mem::drop;
libc_enum! {
/// How exactly should the system be rebooted.
///
/// See [`set_cad_enabled()`](fn.set_cad_enabled.html) for
/// enabling/disabling Ctrl-Alt-Delete.
#[repr(i32)]
#[non_exhaustive]
pub enum RebootMode {
/// Halt the system.
RB_HALT_SYSTEM,
/// Execute a kernel that has been loaded earlier with
/// [`kexec_load(2)`](https://man7.org/linux/man-pages/man2/kexec_load.2.html).
RB_KEXEC,
/// Stop the system and switch off power, if possible.
RB_POWER_OFF,
/// Restart the system.
RB_AUTOBOOT,
// we do not support Restart2.
/// Suspend the system using software suspend.
RB_SW_SUSPEND,
}
}
/// Reboots or shuts down the system.
pub fn reboot(how: RebootMode) -> Result<Infallible> {
unsafe { libc::reboot(how as libc::c_int) };
Err(Errno::last())
}
/// Enable or disable the reboot keystroke (Ctrl-Alt-Delete).
///
/// Corresponds to calling `reboot(RB_ENABLE_CAD)` or `reboot(RB_DISABLE_CAD)` in C.
pub fn set_cad_enabled(enable: bool) -> Result<()> {
let cmd = if enable {
libc::RB_ENABLE_CAD
} else {
libc::RB_DISABLE_CAD
};
let res = unsafe { libc::reboot(cmd) };
Errno::result(res).map(drop)
}
} else if #[cfg(netbsdlike)] {
use libc::c_int;
libc_bitflags! {
/// How exactly should the system be rebooted.
pub struct RebootMode: c_int {
/// The default, causing the system to reboot in its usual fashion.
RB_AUTOBOOT;
/// Interpreted by the bootstrap program itself, causing it to
/// prompt on the console as to what file should be booted.
/// Normally, the system is booted from the file “xx(0,0)bsd”,
/// where xx is the default disk name, without prompting for
/// the file name.
RB_ASKNAME;
/// Dump kernel memory before rebooting; see `savecore(8)` for
/// more information.
RB_DUMP;
/// The processor is simply halted; no reboot takes place.
RB_HALT;
/// Power off the system if the system hardware supports the
/// function, otherwise it has no effect.
///
/// Should be used in conjunction with `RB_HALT`.
RB_POWERDOWN;
/// By default, the system will halt if `reboot()` is called during
/// startup (before the system has finished autoconfiguration), even
/// if `RB_HALT` is not specified. This is because `panic(9)`s
/// during startup will probably just repeat on the next boot.
/// Use of this option implies that the user has requested the
/// action specified (for example, using the `ddb(4)` boot reboot
/// command), so the system will reboot if a halt is not explicitly
/// requested.
#[cfg(target_os = "openbsd")]
RB_USERREQ;
/// Load the symbol table and enable a built-in debugger in the
/// system. This option will have no useful function if the kernel
/// is not configured for debugging. Several other options have
/// different meaning if combined with this option, although their
/// use may not be possible via the `reboot()` call. See `ddb(4)` for
/// more information.
RB_KDB;
/// Normally, the disks are sync'd (see `sync(8)`) before the
/// processor is halted or rebooted. This option may be useful
/// if file system changes have been made manually or if the
/// processor is on fire.
RB_NOSYNC;
/// Normally, the reboot procedure involves an automatic disk
/// consistency check and then multi-user operations. `RB_SINGLE`
/// prevents this, booting the system with a single-user shell on
/// the console. `RB_SINGLE` is actually interpreted by the `init(8)`
/// program in the newly booted system.
///
/// When no options are given (i.e., `RB_AUTOBOOT` is used), the
/// system is rebooted from file /bsd in the root file system of
/// unit 0 of a disk chosen in a processor specific way. An automatic
/// consistency check of the disks is normally performed (see `fsck(8)`).
RB_SINGLE;
/// Initially invoke the `userconf(4)` facility when the system
/// starts up again, if it has been compiled into the kernel
/// that is loaded.
#[cfg(target_os = "netbsd")]
RB_USERCONF;
/// Don't update the hardware clock from the system clock, presumably
/// because the system clock is suspect.
#[cfg(target_os = "openbsd")]
RB_TIMEBAD;
}
}
/// Reboot system or halt processor
///
/// For more information, see the man pages:
///
/// * [NetBSD](https://man.netbsd.org/reboot.2)
/// * [OpenBSD](https://man.openbsd.org/reboot.2)
#[cfg(netbsdlike)]
pub fn reboot(how: RebootMode) -> Result<Infallible> {
#[cfg(target_os = "openbsd")]
unsafe { libc::reboot(how.bits()) };
#[cfg(target_os = "netbsd")]
unsafe { libc::reboot(how.bits(), std::ptr::null_mut()) };
Err(Errno::last())
}
}
}

402
vendor/nix/src/sys/resource.rs vendored Normal file
View File

@@ -0,0 +1,402 @@
//! Configure the process resource limits.
use cfg_if::cfg_if;
use libc::{c_int, c_long, rusage};
use crate::errno::Errno;
use crate::sys::time::TimeVal;
use crate::Result;
pub use libc::rlim_t;
pub use libc::RLIM_INFINITY;
use std::mem;
cfg_if! {
if #[cfg(any(
all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")),
target_os = "hurd"
))]{
use libc::{__rlimit_resource_t, rlimit};
} else if #[cfg(any(
bsd,
target_os = "android",
target_os = "aix",
all(target_os = "linux", not(target_env = "gnu")),
target_os = "cygwin"
))]{
use libc::rlimit;
}
}
libc_enum! {
/// Types of process resources.
///
/// The Resource enum is platform dependent. Check different platform
/// manuals for more details. Some platform links have been provided for
/// easier reference (non-exhaustive).
///
/// * [Linux](https://man7.org/linux/man-pages/man2/getrlimit.2.html)
/// * [FreeBSD](https://www.freebsd.org/cgi/man.cgi?query=setrlimit)
/// * [NetBSD](https://man.netbsd.org/setrlimit.2)
// linux-gnu uses u_int as resource enum, which is implemented in libc as
// well.
//
// https://gcc.gnu.org/legacy-ml/gcc/2015-08/msg00441.html
// https://github.com/rust-lang/libc/blob/master/src/unix/linux_like/linux/gnu/mod.rs
#[cfg_attr(any(
all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")),
target_os = "hurd"
), repr(u32))]
#[cfg_attr(any(
bsd,
target_os = "android",
target_os = "aix",
all(target_os = "linux", not(any(target_env = "gnu", target_env = "uclibc"))),
target_os = "cygwin"
), repr(i32))]
#[non_exhaustive]
pub enum Resource {
#[cfg(not(any(target_os = "freebsd", netbsdlike)))]
/// The maximum amount (in bytes) of virtual memory the process is
/// allowed to map.
RLIMIT_AS,
/// The largest size (in bytes) core(5) file that may be created.
RLIMIT_CORE,
/// The maximum amount of cpu time (in seconds) to be used by each
/// process.
RLIMIT_CPU,
/// The maximum size (in bytes) of the data segment for a process
RLIMIT_DATA,
/// The largest size (in bytes) file that may be created.
RLIMIT_FSIZE,
/// The maximum number of open files for this process.
RLIMIT_NOFILE,
/// The maximum size (in bytes) of the stack segment for a process.
RLIMIT_STACK,
#[cfg(target_os = "freebsd")]
/// The maximum number of kqueues this user id is allowed to create.
RLIMIT_KQUEUES,
#[cfg(linux_android)]
/// A limit on the combined number of flock locks and fcntl leases that
/// this process may establish.
RLIMIT_LOCKS,
#[cfg(any(linux_android, target_os = "freebsd", netbsdlike))]
/// The maximum size (in bytes) which a process may lock into memory
/// using the mlock(2) system call.
RLIMIT_MEMLOCK,
#[cfg(linux_android)]
/// A limit on the number of bytes that can be allocated for POSIX
/// message queues for the real user ID of the calling process.
RLIMIT_MSGQUEUE,
#[cfg(linux_android)]
/// A ceiling to which the process's nice value can be raised using
/// setpriority or nice.
RLIMIT_NICE,
#[cfg(any(
linux_android,
target_os = "freebsd",
netbsdlike,
target_os = "aix",
))]
/// The maximum number of simultaneous processes for this user id.
RLIMIT_NPROC,
#[cfg(target_os = "freebsd")]
/// The maximum number of pseudo-terminals this user id is allowed to
/// create.
RLIMIT_NPTS,
#[cfg(any(linux_android,
target_os = "freebsd",
netbsdlike,
target_os = "aix",
))]
/// When there is memory pressure and swap is available, prioritize
/// eviction of a process' resident pages beyond this amount (in bytes).
RLIMIT_RSS,
#[cfg(linux_android)]
/// A ceiling on the real-time priority that may be set for this process
/// using sched_setscheduler and sched_set param.
RLIMIT_RTPRIO,
#[cfg(any(target_os = "linux"))]
/// A limit (in microseconds) on the amount of CPU time that a process
/// scheduled under a real-time scheduling policy may con sume without
/// making a blocking system call.
RLIMIT_RTTIME,
#[cfg(linux_android)]
/// A limit on the number of signals that may be queued for the real
/// user ID of the calling process.
RLIMIT_SIGPENDING,
#[cfg(freebsdlike)]
/// The maximum size (in bytes) of socket buffer usage for this user.
RLIMIT_SBSIZE,
#[cfg(target_os = "freebsd")]
/// The maximum size (in bytes) of the swap space that may be reserved
/// or used by all of this user id's processes.
RLIMIT_SWAP,
#[cfg(target_os = "freebsd")]
/// An alias for RLIMIT_AS.
RLIMIT_VMEM,
}
}
/// Get the current processes resource limits
///
/// The special value [`RLIM_INFINITY`] indicates that no limit will be
/// enforced.
///
/// # Parameters
///
/// * `resource`: The [`Resource`] that we want to get the limits of.
///
/// # Examples
///
/// ```
/// # use nix::sys::resource::{getrlimit, Resource};
///
/// let (soft_limit, hard_limit) = getrlimit(Resource::RLIMIT_NOFILE).unwrap();
/// println!("current soft_limit: {}", soft_limit);
/// println!("current hard_limit: {}", hard_limit);
/// ```
///
/// # References
///
/// [getrlimit(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getrlimit.html#tag_16_215)
///
/// [`Resource`]: enum.Resource.html
pub fn getrlimit(resource: Resource) -> Result<(rlim_t, rlim_t)> {
let mut old_rlim = mem::MaybeUninit::<rlimit>::uninit();
cfg_if! {
if #[cfg(any(
all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")),
target_os = "hurd"
))] {
let res = unsafe { libc::getrlimit(resource as __rlimit_resource_t, old_rlim.as_mut_ptr()) };
} else {
let res = unsafe { libc::getrlimit(resource as c_int, old_rlim.as_mut_ptr()) };
}
}
Errno::result(res).map(|_| {
let rlimit { rlim_cur, rlim_max } = unsafe { old_rlim.assume_init() };
(rlim_cur, rlim_max)
})
}
/// Set the current processes resource limits
///
/// # Parameters
///
/// * `resource`: The [`Resource`] that we want to set the limits of.
/// * `soft_limit`: The value that the kernel enforces for the corresponding
/// resource.
/// * `hard_limit`: The ceiling for the soft limit. Must be lower or equal to
/// the current hard limit for non-root users.
///
/// The special value [`RLIM_INFINITY`] indicates that no limit will be
/// enforced.
///
/// # Examples
///
/// ```
/// # use nix::sys::resource::{setrlimit, Resource};
///
/// let soft_limit = 512;
/// let hard_limit = 1024;
/// setrlimit(Resource::RLIMIT_NOFILE, soft_limit, hard_limit).unwrap();
/// ```
///
/// # References
///
/// [setrlimit(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getrlimit.html#tag_16_215)
///
/// [`Resource`]: enum.Resource.html
///
/// Note: `setrlimit` provides a safe wrapper to libc's `setrlimit`.
pub fn setrlimit(
resource: Resource,
soft_limit: rlim_t,
hard_limit: rlim_t,
) -> Result<()> {
let new_rlim = rlimit {
rlim_cur: soft_limit,
rlim_max: hard_limit,
};
cfg_if! {
if #[cfg(any(
all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")),
target_os = "hurd",
))]{
let res = unsafe { libc::setrlimit(resource as __rlimit_resource_t, &new_rlim as *const rlimit) };
}else{
let res = unsafe { libc::setrlimit(resource as c_int, &new_rlim as *const rlimit) };
}
}
Errno::result(res).map(drop)
}
libc_enum! {
/// Whose resource usage should be returned by [`getrusage`].
#[repr(i32)]
#[non_exhaustive]
pub enum UsageWho {
/// Resource usage for the current process.
RUSAGE_SELF,
/// Resource usage for all the children that have terminated and been waited for.
RUSAGE_CHILDREN,
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))]
/// Resource usage for the calling thread.
RUSAGE_THREAD,
}
}
/// Output of `getrusage` with information about resource usage. Some of the fields
/// may be unused in some platforms, and will be always zeroed out. See their manuals
/// for details.
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Usage(rusage);
impl AsRef<rusage> for Usage {
fn as_ref(&self) -> &rusage {
&self.0
}
}
impl AsMut<rusage> for Usage {
fn as_mut(&mut self) -> &mut rusage {
&mut self.0
}
}
impl Usage {
/// Total amount of time spent executing in user mode.
pub fn user_time(&self) -> TimeVal {
TimeVal::from(self.0.ru_utime)
}
/// Total amount of time spent executing in kernel mode.
pub fn system_time(&self) -> TimeVal {
TimeVal::from(self.0.ru_stime)
}
/// The resident set size at its peak,
#[cfg_attr(apple_targets, doc = " in bytes.")]
#[cfg_attr(not(apple_targets), doc = " in kilobytes.")]
pub fn max_rss(&self) -> c_long {
self.0.ru_maxrss
}
/// Integral value expressed in kilobytes times ticks of execution indicating
/// the amount of text memory shared with other processes.
pub fn shared_integral(&self) -> c_long {
self.0.ru_ixrss
}
/// Integral value expressed in kilobytes times ticks of execution indicating
/// the amount of unshared memory used by data.
pub fn unshared_data_integral(&self) -> c_long {
self.0.ru_idrss
}
/// Integral value expressed in kilobytes times ticks of execution indicating
/// the amount of unshared memory used for stack space.
pub fn unshared_stack_integral(&self) -> c_long {
self.0.ru_isrss
}
/// Number of page faults that were served without resorting to I/O, with pages
/// that have been allocated previously by the kernel.
pub fn minor_page_faults(&self) -> c_long {
self.0.ru_minflt
}
/// Number of page faults that were served through I/O (i.e. swap).
pub fn major_page_faults(&self) -> c_long {
self.0.ru_majflt
}
/// Number of times all of the memory was fully swapped out.
pub fn full_swaps(&self) -> c_long {
self.0.ru_nswap
}
/// Number of times a read was done from a block device.
pub fn block_reads(&self) -> c_long {
self.0.ru_inblock
}
/// Number of times a write was done to a block device.
pub fn block_writes(&self) -> c_long {
self.0.ru_oublock
}
/// Number of IPC messages sent.
pub fn ipc_sends(&self) -> c_long {
self.0.ru_msgsnd
}
/// Number of IPC messages received.
pub fn ipc_receives(&self) -> c_long {
self.0.ru_msgrcv
}
/// Number of signals received.
pub fn signals(&self) -> c_long {
self.0.ru_nsignals
}
/// Number of times a context switch was voluntarily invoked.
pub fn voluntary_context_switches(&self) -> c_long {
self.0.ru_nvcsw
}
/// Number of times a context switch was imposed by the kernel (usually due to
/// time slice expiring or preemption by a higher priority process).
pub fn involuntary_context_switches(&self) -> c_long {
self.0.ru_nivcsw
}
}
/// Get usage information for a process, its children or the current thread
///
/// Real time information can be obtained for either the current process or (in some
/// systems) thread, but information about children processes is only provided for
/// those that have terminated and been waited for (see [`super::wait::wait`]).
///
/// Some information may be missing depending on the platform, and the way information
/// is provided for children may also vary. Check the manuals for details.
///
/// # References
///
/// * [getrusage(2)](https://pubs.opengroup.org/onlinepubs/009696699/functions/getrusage.html)
/// * [Linux](https://man7.org/linux/man-pages/man2/getrusage.2.html)
/// * [FreeBSD](https://www.freebsd.org/cgi/man.cgi?query=getrusage)
/// * [NetBSD](https://man.netbsd.org/getrusage.2)
/// * [MacOS](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/getrusage.2.html)
///
/// [`UsageWho`]: enum.UsageWho.html
///
/// Note: `getrusage` provides a safe wrapper to libc's [`libc::getrusage`].
pub fn getrusage(who: UsageWho) -> Result<Usage> {
unsafe {
let mut rusage = mem::MaybeUninit::<rusage>::uninit();
let res = libc::getrusage(who as c_int, rusage.as_mut_ptr());
Errno::result(res).map(|_| Usage(rusage.assume_init()))
}
}

319
vendor/nix/src/sys/select.rs vendored Normal file
View File

@@ -0,0 +1,319 @@
//! Portably monitor a group of file descriptors for readiness.
use crate::errno::Errno;
use crate::sys::time::{TimeSpec, TimeVal};
use crate::Result;
use libc::{self, c_int};
use std::convert::TryFrom;
use std::iter::FusedIterator;
use std::mem;
use std::ops::Range;
use std::os::unix::io::{AsRawFd, BorrowedFd, RawFd};
use std::ptr::{null, null_mut};
pub use libc::FD_SETSIZE;
/// Contains a set of file descriptors used by [`select`]
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct FdSet<'fd> {
set: libc::fd_set,
_fd: std::marker::PhantomData<BorrowedFd<'fd>>,
}
fn assert_fd_valid(fd: RawFd) {
assert!(
usize::try_from(fd).map_or(false, |fd| fd < FD_SETSIZE),
"fd must be in the range 0..FD_SETSIZE",
);
}
impl<'fd> FdSet<'fd> {
/// Create an empty `FdSet`
pub fn new() -> FdSet<'fd> {
let mut fdset = mem::MaybeUninit::uninit();
unsafe {
libc::FD_ZERO(fdset.as_mut_ptr());
Self {
set: fdset.assume_init(),
_fd: std::marker::PhantomData,
}
}
}
/// Add a file descriptor to an `FdSet`
pub fn insert(&mut self, fd: BorrowedFd<'fd>) {
assert_fd_valid(fd.as_raw_fd());
unsafe { libc::FD_SET(fd.as_raw_fd(), &mut self.set) };
}
/// Remove a file descriptor from an `FdSet`
pub fn remove(&mut self, fd: BorrowedFd<'fd>) {
assert_fd_valid(fd.as_raw_fd());
unsafe { libc::FD_CLR(fd.as_raw_fd(), &mut self.set) };
}
/// Test an `FdSet` for the presence of a certain file descriptor.
pub fn contains(&self, fd: BorrowedFd<'fd>) -> bool {
assert_fd_valid(fd.as_raw_fd());
unsafe { libc::FD_ISSET(fd.as_raw_fd(), &self.set) }
}
/// Remove all file descriptors from this `FdSet`.
pub fn clear(&mut self) {
unsafe { libc::FD_ZERO(&mut self.set) };
}
/// Finds the highest file descriptor in the set.
///
/// Returns `None` if the set is empty.
///
/// This can be used to calculate the `nfds` parameter of the [`select`] function.
///
/// # Example
///
/// ```
/// # use std::os::unix::io::{AsRawFd, BorrowedFd};
/// # use nix::sys::select::FdSet;
/// let fd_four = unsafe {BorrowedFd::borrow_raw(4)};
/// let fd_nine = unsafe {BorrowedFd::borrow_raw(9)};
/// let mut set = FdSet::new();
/// set.insert(fd_four);
/// set.insert(fd_nine);
/// assert_eq!(set.highest().map(|borrowed_fd|borrowed_fd.as_raw_fd()), Some(9));
/// ```
///
/// [`select`]: fn.select.html
pub fn highest(&self) -> Option<BorrowedFd<'_>> {
self.fds(None).next_back()
}
/// Returns an iterator over the file descriptors in the set.
///
/// For performance, it takes an optional higher bound: the iterator will
/// not return any elements of the set greater than the given file
/// descriptor.
///
/// # Examples
///
/// ```
/// # use nix::sys::select::FdSet;
/// # use std::os::unix::io::{AsRawFd, BorrowedFd, RawFd};
/// let mut set = FdSet::new();
/// let fd_four = unsafe {BorrowedFd::borrow_raw(4)};
/// let fd_nine = unsafe {BorrowedFd::borrow_raw(9)};
/// set.insert(fd_four);
/// set.insert(fd_nine);
/// let fds: Vec<RawFd> = set.fds(None).map(|borrowed_fd|borrowed_fd.as_raw_fd()).collect();
/// assert_eq!(fds, vec![4, 9]);
/// ```
#[inline]
pub fn fds(&self, highest: Option<RawFd>) -> Fds {
Fds {
set: self,
range: 0..highest.map(|h| h as usize + 1).unwrap_or(FD_SETSIZE),
}
}
}
impl Default for FdSet<'_> {
fn default() -> Self {
Self::new()
}
}
/// Iterator over `FdSet`.
#[derive(Debug)]
pub struct Fds<'a, 'fd> {
set: &'a FdSet<'fd>,
range: Range<usize>,
}
impl<'fd> Iterator for Fds<'_, 'fd> {
type Item = BorrowedFd<'fd>;
fn next(&mut self) -> Option<Self::Item> {
for i in &mut self.range {
let borrowed_i = unsafe { BorrowedFd::borrow_raw(i as RawFd) };
if self.set.contains(borrowed_i) {
return Some(borrowed_i);
}
}
None
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let (_, upper) = self.range.size_hint();
(0, upper)
}
}
impl<'fd> DoubleEndedIterator for Fds<'_, 'fd> {
#[inline]
fn next_back(&mut self) -> Option<BorrowedFd<'fd>> {
while let Some(i) = self.range.next_back() {
let borrowed_i = unsafe { BorrowedFd::borrow_raw(i as RawFd) };
if self.set.contains(borrowed_i) {
return Some(borrowed_i);
}
}
None
}
}
impl FusedIterator for Fds<'_, '_> {}
/// Monitors file descriptors for readiness
///
/// Returns the total number of ready file descriptors in all sets. The sets are changed so that all
/// file descriptors that are ready for the given operation are set.
///
/// When this function returns, `timeout` has an implementation-defined value.
///
/// # Parameters
///
/// * `nfds`: The highest file descriptor set in any of the passed `FdSet`s, plus 1. If `None`, this
/// is calculated automatically by calling [`FdSet::highest`] on all descriptor sets and adding 1
/// to the maximum of that.
/// * `readfds`: File descriptors to check for being ready to read.
/// * `writefds`: File descriptors to check for being ready to write.
/// * `errorfds`: File descriptors to check for pending error conditions.
/// * `timeout`: Maximum time to wait for descriptors to become ready (`None` to block
/// indefinitely).
///
/// # References
///
/// [select(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/select.html)
///
/// [`FdSet::highest`]: struct.FdSet.html#method.highest
pub fn select<'a, 'fd, N, R, W, E, T>(
nfds: N,
readfds: R,
writefds: W,
errorfds: E,
timeout: T,
) -> Result<c_int>
where
'fd: 'a,
N: Into<Option<c_int>>,
R: Into<Option<&'a mut FdSet<'fd>>>,
W: Into<Option<&'a mut FdSet<'fd>>>,
E: Into<Option<&'a mut FdSet<'fd>>>,
T: Into<Option<&'a mut TimeVal>>,
{
let mut readfds = readfds.into();
let mut writefds = writefds.into();
let mut errorfds = errorfds.into();
let timeout = timeout.into();
let nfds = nfds.into().unwrap_or_else(|| {
readfds
.iter_mut()
.chain(writefds.iter_mut())
.chain(errorfds.iter_mut())
.map(|set| {
set.highest()
.map(|borrowed_fd| borrowed_fd.as_raw_fd())
.unwrap_or(-1)
})
.max()
.unwrap_or(-1)
+ 1
});
let readfds = readfds
.map(|set| set as *mut _ as *mut libc::fd_set)
.unwrap_or(null_mut());
let writefds = writefds
.map(|set| set as *mut _ as *mut libc::fd_set)
.unwrap_or(null_mut());
let errorfds = errorfds
.map(|set| set as *mut _ as *mut libc::fd_set)
.unwrap_or(null_mut());
let timeout = timeout
.map(|tv| tv as *mut _ as *mut libc::timeval)
.unwrap_or(null_mut());
let res =
unsafe { libc::select(nfds, readfds, writefds, errorfds, timeout) };
Errno::result(res)
}
feature! {
#![feature = "signal"]
use crate::sys::signal::SigSet;
/// Monitors file descriptors for readiness with an altered signal mask.
///
/// Returns the total number of ready file descriptors in all sets. The sets are changed so that all
/// file descriptors that are ready for the given operation are set.
///
/// When this function returns, the original signal mask is restored.
///
/// Unlike [`select`](#fn.select), `pselect` does not mutate the `timeout` value.
///
/// # Parameters
///
/// * `nfds`: The highest file descriptor set in any of the passed `FdSet`s, plus 1. If `None`, this
/// is calculated automatically by calling [`FdSet::highest`] on all descriptor sets and adding 1
/// to the maximum of that.
/// * `readfds`: File descriptors to check for read readiness
/// * `writefds`: File descriptors to check for write readiness
/// * `errorfds`: File descriptors to check for pending error conditions.
/// * `timeout`: Maximum time to wait for descriptors to become ready (`None` to block
/// indefinitely).
/// * `sigmask`: Signal mask to activate while waiting for file descriptors to turn
/// ready (`None` to set no alternative signal mask).
///
/// # References
///
/// [pselect(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pselect.html)
///
/// [The new pselect() system call](https://lwn.net/Articles/176911/)
///
/// [`FdSet::highest`]: struct.FdSet.html#method.highest
pub fn pselect<'a, 'fd, N, R, W, E, T, S>(nfds: N,
readfds: R,
writefds: W,
errorfds: E,
timeout: T,
sigmask: S) -> Result<c_int>
where
'fd: 'a,
N: Into<Option<c_int>>,
R: Into<Option<&'a mut FdSet<'fd>>>,
W: Into<Option<&'a mut FdSet<'fd>>>,
E: Into<Option<&'a mut FdSet<'fd>>>,
T: Into<Option<&'a TimeSpec>>,
S: Into<Option<&'a SigSet>>,
{
let mut readfds = readfds.into();
let mut writefds = writefds.into();
let mut errorfds = errorfds.into();
let sigmask = sigmask.into();
let timeout = timeout.into();
let nfds = nfds.into().unwrap_or_else(|| {
readfds.iter_mut()
.chain(writefds.iter_mut())
.chain(errorfds.iter_mut())
.map(|set| set.highest().map(|borrowed_fd|borrowed_fd.as_raw_fd()).unwrap_or(-1))
.max()
.unwrap_or(-1) + 1
});
let readfds = readfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
let writefds = writefds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
let errorfds = errorfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
let timeout = timeout.map(|ts| ts.as_ref() as *const libc::timespec).unwrap_or(null());
let sigmask = sigmask.map(|sm| sm.as_ref() as *const libc::sigset_t).unwrap_or(null());
let res = unsafe {
libc::pselect(nfds, readfds, writefds, errorfds, timeout, sigmask)
};
Errno::result(res)
}
}

351
vendor/nix/src/sys/sendfile.rs vendored Normal file
View File

@@ -0,0 +1,351 @@
//! Send data from a file to a socket, bypassing userland.
use cfg_if::cfg_if;
use std::os::unix::io::{AsFd, AsRawFd};
use std::ptr;
use libc::{self, off_t};
use crate::errno::Errno;
use crate::Result;
/// Copy up to `count` bytes to `out_fd` from `in_fd` starting at `offset`.
///
/// Returns a `Result` with the number of bytes written.
///
/// If `offset` is `None`, `sendfile` will begin reading at the current offset of `in_fd`and will
/// update the offset of `in_fd`. If `offset` is `Some`, `sendfile` will begin at the specified
/// offset and will not update the offset of `in_fd`. Instead, it will mutate `offset` to point to
/// the byte after the last byte copied.
///
/// `in_fd` must support `mmap`-like operations and therefore cannot be a socket.
///
/// For more information, see [the sendfile(2) man page.](https://man7.org/linux/man-pages/man2/sendfile.2.html) for Linux,
/// see [the sendfile(2) man page.](https://docs.oracle.com/cd/E88353_01/html/E37843/sendfile-3c.html) for Solaris.
#[cfg(any(linux_android, solarish))]
pub fn sendfile<F1: AsFd, F2: AsFd>(
out_fd: F1,
in_fd: F2,
offset: Option<&mut off_t>,
count: usize,
) -> Result<usize> {
let offset = offset
.map(|offset| offset as *mut _)
.unwrap_or(ptr::null_mut());
let ret = unsafe {
libc::sendfile(
out_fd.as_fd().as_raw_fd(),
in_fd.as_fd().as_raw_fd(),
offset,
count,
)
};
Errno::result(ret).map(|r| r as usize)
}
/// Copy up to `count` bytes to `out_fd` from `in_fd` starting at `offset`.
///
/// Returns a `Result` with the number of bytes written.
///
/// If `offset` is `None`, `sendfile` will begin reading at the current offset of `in_fd`and will
/// update the offset of `in_fd`. If `offset` is `Some`, `sendfile` will begin at the specified
/// offset and will not update the offset of `in_fd`. Instead, it will mutate `offset` to point to
/// the byte after the last byte copied.
///
/// `in_fd` must support `mmap`-like operations and therefore cannot be a socket.
///
/// For more information, see [the sendfile(2) man page.](https://man7.org/linux/man-pages/man2/sendfile.2.html)
#[cfg(target_os = "linux")]
pub fn sendfile64<F1: AsFd, F2: AsFd>(
out_fd: F1,
in_fd: F2,
offset: Option<&mut libc::off64_t>,
count: usize,
) -> Result<usize> {
let offset = offset
.map(|offset| offset as *mut _)
.unwrap_or(ptr::null_mut());
let ret = unsafe {
libc::sendfile64(
out_fd.as_fd().as_raw_fd(),
in_fd.as_fd().as_raw_fd(),
offset,
count,
)
};
Errno::result(ret).map(|r| r as usize)
}
cfg_if! {
if #[cfg(any(freebsdlike, apple_targets))] {
use std::io::IoSlice;
#[derive(Clone, Debug)]
struct SendfileHeaderTrailer<'a> {
raw: libc::sf_hdtr,
_headers: Option<Vec<IoSlice<'a>>>,
_trailers: Option<Vec<IoSlice<'a>>>,
}
impl<'a> SendfileHeaderTrailer<'a> {
fn new(
headers: Option<&'a [&'a [u8]]>,
trailers: Option<&'a [&'a [u8]]>
) -> SendfileHeaderTrailer<'a> {
let mut header_iovecs: Option<Vec<IoSlice<'_>>> =
headers.map(|s| s.iter().map(|b| IoSlice::new(b)).collect());
let mut trailer_iovecs: Option<Vec<IoSlice<'_>>> =
trailers.map(|s| s.iter().map(|b| IoSlice::new(b)).collect());
SendfileHeaderTrailer {
raw: libc::sf_hdtr {
headers: {
header_iovecs
.as_mut()
.map_or(ptr::null_mut(), |v| v.as_mut_ptr())
.cast()
},
hdr_cnt: header_iovecs.as_ref().map(|v| v.len()).unwrap_or(0) as i32,
trailers: {
trailer_iovecs
.as_mut()
.map_or(ptr::null_mut(), |v| v.as_mut_ptr())
.cast()
},
trl_cnt: trailer_iovecs.as_ref().map(|v| v.len()).unwrap_or(0) as i32
},
_headers: header_iovecs,
_trailers: trailer_iovecs,
}
}
}
} else if #[cfg(solarish)] {
use std::os::unix::io::BorrowedFd;
use std::marker::PhantomData;
#[derive(Debug, Copy, Clone)]
/// Mapping of the raw C sendfilevec_t struct
pub struct SendfileVec<'fd> {
raw: libc::sendfilevec_t,
phantom: PhantomData<BorrowedFd<'fd>>
}
impl<'fd> SendfileVec<'fd> {
/// initialises SendfileVec to send data directly from the process's address space
/// same in C with sfv_fd set to SFV_FD_SELF.
pub fn newself(
off: off_t,
len: usize
) -> Self {
Self{raw: libc::sendfilevec_t{sfv_fd: libc::SFV_FD_SELF, sfv_flag: 0, sfv_off: off, sfv_len: len}, phantom: PhantomData}
}
/// initialises SendfileVec to send data from `fd`.
pub fn new(
fd: BorrowedFd<'fd>,
off: off_t,
len: usize
) -> SendfileVec<'fd> {
Self{raw: libc::sendfilevec_t{sfv_fd: fd.as_raw_fd(), sfv_flag: 0, sfv_off:off, sfv_len: len}, phantom: PhantomData}
}
}
impl From<SendfileVec<'_>> for libc::sendfilevec_t {
fn from<'fd>(vec: SendfileVec) -> libc::sendfilevec_t {
vec.raw
}
}
}
}
cfg_if! {
if #[cfg(target_os = "freebsd")] {
use libc::c_int;
libc_bitflags!{
/// Configuration options for [`sendfile`.](fn.sendfile.html)
pub struct SfFlags: c_int {
/// Causes `sendfile` to return EBUSY instead of blocking when attempting to read a
/// busy page.
SF_NODISKIO;
/// Causes `sendfile` to sleep until the network stack releases its reference to the
/// VM pages read. When `sendfile` returns, the data is not guaranteed to have been
/// sent, but it is safe to modify the file.
SF_SYNC;
/// Causes `sendfile` to cache exactly the number of pages specified in the
/// `readahead` parameter, disabling caching heuristics.
SF_USER_READAHEAD;
/// Causes `sendfile` not to cache the data read.
SF_NOCACHE;
}
}
/// Read up to `count` bytes from `in_fd` starting at `offset` and write to `out_sock`.
///
/// Returns a `Result` and a count of bytes written. Bytes written may be non-zero even if
/// an error occurs.
///
/// `in_fd` must describe a regular file or shared memory object. `out_sock` must describe a
/// stream socket.
///
/// If `offset` falls past the end of the file, the function returns success and zero bytes
/// written.
///
/// If `count` is `None` or 0, bytes will be read from `in_fd` until reaching the end of
/// file (EOF).
///
/// `headers` and `trailers` specify optional slices of byte slices to be sent before and
/// after the data read from `in_fd`, respectively. The length of headers and trailers sent
/// is included in the returned count of bytes written. The values of `offset` and `count`
/// do not apply to headers or trailers.
///
/// `readahead` specifies the minimum number of pages to cache in memory ahead of the page
/// currently being sent.
///
/// For more information, see
/// [the sendfile(2) man page.](https://www.freebsd.org/cgi/man.cgi?query=sendfile&sektion=2)
#[allow(clippy::too_many_arguments)]
pub fn sendfile<F1: AsFd, F2: AsFd>(
in_fd: F1,
out_sock: F2,
offset: off_t,
count: Option<usize>,
headers: Option<&[&[u8]]>,
trailers: Option<&[&[u8]]>,
flags: SfFlags,
readahead: u16
) -> (Result<()>, off_t) {
// Readahead goes in upper 16 bits
// Flags goes in lower 16 bits
// see `man 2 sendfile`
let ra32 = u32::from(readahead);
let flags: u32 = (ra32 << 16) | (flags.bits() as u32);
let mut bytes_sent: off_t = 0;
let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers));
let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.raw as *const libc::sf_hdtr);
let return_code = unsafe {
libc::sendfile(in_fd.as_fd().as_raw_fd(),
out_sock.as_fd().as_raw_fd(),
offset,
count.unwrap_or(0),
hdtr_ptr as *mut libc::sf_hdtr,
&mut bytes_sent as *mut off_t,
flags as c_int)
};
(Errno::result(return_code).and(Ok(())), bytes_sent)
}
} else if #[cfg(target_os = "dragonfly")] {
/// Read up to `count` bytes from `in_fd` starting at `offset` and write to `out_sock`.
///
/// Returns a `Result` and a count of bytes written. Bytes written may be non-zero even if
/// an error occurs.
///
/// `in_fd` must describe a regular file. `out_sock` must describe a stream socket.
///
/// If `offset` falls past the end of the file, the function returns success and zero bytes
/// written.
///
/// If `count` is `None` or 0, bytes will be read from `in_fd` until reaching the end of
/// file (EOF).
///
/// `headers` and `trailers` specify optional slices of byte slices to be sent before and
/// after the data read from `in_fd`, respectively. The length of headers and trailers sent
/// is included in the returned count of bytes written. The values of `offset` and `count`
/// do not apply to headers or trailers.
///
/// For more information, see
/// [the sendfile(2) man page.](https://leaf.dragonflybsd.org/cgi/web-man?command=sendfile&section=2)
pub fn sendfile<F1: AsFd, F2: AsFd>(
in_fd: F1,
out_sock: F2,
offset: off_t,
count: Option<usize>,
headers: Option<&[&[u8]]>,
trailers: Option<&[&[u8]]>,
) -> (Result<()>, off_t) {
let mut bytes_sent: off_t = 0;
let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers));
let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.raw as *const libc::sf_hdtr);
let return_code = unsafe {
libc::sendfile(in_fd.as_fd().as_raw_fd(),
out_sock.as_fd().as_raw_fd(),
offset,
count.unwrap_or(0),
hdtr_ptr as *mut libc::sf_hdtr,
&mut bytes_sent as *mut off_t,
0)
};
(Errno::result(return_code).and(Ok(())), bytes_sent)
}
} else if #[cfg(apple_targets)] {
/// Read bytes from `in_fd` starting at `offset` and write up to `count` bytes to
/// `out_sock`.
///
/// Returns a `Result` and a count of bytes written. Bytes written may be non-zero even if
/// an error occurs.
///
/// `in_fd` must describe a regular file. `out_sock` must describe a stream socket.
///
/// If `offset` falls past the end of the file, the function returns success and zero bytes
/// written.
///
/// If `count` is `None` or 0, bytes will be read from `in_fd` until reaching the end of
/// file (EOF).
///
/// `hdtr` specifies an optional list of headers and trailers to be sent before and after
/// the data read from `in_fd`, respectively. The length of headers and trailers sent is
/// included in the returned count of bytes written. If any headers are specified and
/// `count` is non-zero, the length of the headers will be counted in the limit of total
/// bytes sent. Trailers do not count toward the limit of bytes sent and will always be sent
/// regardless. The value of `offset` does not affect headers or trailers.
///
/// For more information, see
/// [the sendfile(2) man page.](https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man2/sendfile.2.html)
pub fn sendfile<F1: AsFd, F2: AsFd>(
in_fd: F1,
out_sock: F2,
offset: off_t,
count: Option<off_t>,
headers: Option<&[&[u8]]>,
trailers: Option<&[&[u8]]>
) -> (Result<()>, off_t) {
let mut len = count.unwrap_or(0);
let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers));
let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.raw as *const libc::sf_hdtr);
let return_code = unsafe {
libc::sendfile(in_fd.as_fd().as_raw_fd(),
out_sock.as_fd().as_raw_fd(),
offset,
&mut len as *mut off_t,
hdtr_ptr as *mut libc::sf_hdtr,
0)
};
(Errno::result(return_code).and(Ok(())), len)
}
} else if #[cfg(solarish)] {
/// Write data from the vec arrays to `out_sock` and returns a `Result` and a
/// count of bytes written.
///
/// Each `SendfileVec` set needs to be instantiated either with `SendfileVec::new` or
/// `SendfileVec::newself`.
///
/// The former allows to send data from a file descriptor through `fd`,
/// from an offset `off` and for a given amount of data `len`.
///
/// The latter allows to send data from the process's address space, from an offset `off`
/// and for a given amount of data `len`.
///
/// For more information, see
/// [the sendfilev(3) man page.](https://illumos.org/man/3EXT/sendfilev)
pub fn sendfilev<F: AsFd>(
out_sock: F,
vec: &[SendfileVec]
) -> (Result<()>, usize) {
let mut len = 0usize;
let return_code = unsafe {
libc::sendfilev(out_sock.as_fd().as_raw_fd(), vec.as_ptr() as *const libc::sendfilevec_t, vec.len() as i32, &mut len)
};
(Errno::result(return_code).and(Ok(())), len)
}
}
}

1412
vendor/nix/src/sys/signal.rs vendored Normal file

File diff suppressed because it is too large Load Diff

174
vendor/nix/src/sys/signalfd.rs vendored Normal file
View File

@@ -0,0 +1,174 @@
//! Interface for the `signalfd` syscall.
//!
//! # Signal discarding
//! When a signal can't be delivered to a process (or thread), it will become a pending signal.
//! Failure to deliver could happen if the signal is blocked by every thread in the process or if
//! the signal handler is still handling a previous signal.
//!
//! If a signal is sent to a process (or thread) that already has a pending signal of the same
//! type, it will be discarded. This means that if signals of the same type are received faster than
//! they are processed, some of those signals will be dropped. Because of this limitation,
//! `signalfd` in itself cannot be used for reliable communication between processes or threads.
//!
//! Once the signal is unblocked, or the signal handler is finished, and a signal is still pending
//! (ie. not consumed from a signalfd) it will be delivered to the signal handler.
//!
//! Please note that signal discarding is not specific to `signalfd`, but also happens with regular
//! signal handlers.
use crate::errno::Errno;
pub use crate::sys::signal::{self, SigSet};
use crate::Result;
/// Information of a received signal, the return type of [`SignalFd::read_signal()`].
pub use libc::signalfd_siginfo as siginfo;
use std::mem;
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd};
libc_bitflags! {
pub struct SfdFlags: libc::c_int {
SFD_NONBLOCK;
SFD_CLOEXEC;
}
}
#[deprecated(since = "0.23.0", note = "use mem::size_of::<siginfo>() instead")]
pub const SIGNALFD_SIGINFO_SIZE: usize = mem::size_of::<siginfo>();
/// Creates a new file descriptor for reading signals.
///
/// **Important:** please read the module level documentation about signal discarding before using
/// this function!
///
/// The `mask` parameter specifies the set of signals that can be accepted via this file descriptor.
///
/// A signal must be blocked on every thread in a process, otherwise it won't be visible from
/// signalfd (the default handler will be invoked instead).
///
/// See [the signalfd man page for more information](https://man7.org/linux/man-pages/man2/signalfd.2.html)
#[deprecated(since = "0.27.0", note = "Use SignalFd instead")]
pub fn signalfd<F: AsFd>(
fd: Option<F>,
mask: &SigSet,
flags: SfdFlags,
) -> Result<OwnedFd> {
_signalfd(fd, mask, flags)
}
fn _signalfd<F: AsFd>(
fd: Option<F>,
mask: &SigSet,
flags: SfdFlags,
) -> Result<OwnedFd> {
let raw_fd = fd.map_or(-1, |x| x.as_fd().as_raw_fd());
unsafe {
Errno::result(libc::signalfd(raw_fd, mask.as_ref(), flags.bits()))
.map(|raw_fd| FromRawFd::from_raw_fd(raw_fd))
}
}
/// A helper struct for creating, reading and closing a `signalfd` instance.
///
/// **Important:** please read the module level documentation about signal discarding before using
/// this struct!
///
/// # Examples
///
/// ```
/// # use nix::sys::signalfd::*;
/// // Set the thread to block the SIGUSR1 signal, otherwise the default handler will be used
/// let mut mask = SigSet::empty();
/// mask.add(signal::SIGUSR1);
/// mask.thread_block().unwrap();
///
/// // Signals are queued up on the file descriptor
/// let mut sfd = SignalFd::with_flags(&mask, SfdFlags::SFD_NONBLOCK).unwrap();
///
/// match sfd.read_signal() {
/// // we caught a signal
/// Ok(Some(sig)) => (),
/// // there were no signals waiting (only happens when the SFD_NONBLOCK flag is set,
/// // otherwise the read_signal call blocks)
/// Ok(None) => (),
/// Err(err) => (), // some error happend
/// }
/// ```
#[derive(Debug)]
pub struct SignalFd(OwnedFd);
impl SignalFd {
pub fn new(mask: &SigSet) -> Result<SignalFd> {
Self::with_flags(mask, SfdFlags::empty())
}
pub fn with_flags(mask: &SigSet, flags: SfdFlags) -> Result<SignalFd> {
let fd = _signalfd(None::<OwnedFd>, mask, flags)?;
Ok(SignalFd(fd))
}
pub fn set_mask(&self, mask: &SigSet) -> Result<()> {
self.update(mask, SfdFlags::empty())
}
pub fn read_signal(&self) -> Result<Option<siginfo>> {
let mut buffer = mem::MaybeUninit::<siginfo>::uninit();
let size = mem::size_of_val(&buffer);
let res = Errno::result(unsafe {
libc::read(self.0.as_raw_fd(), buffer.as_mut_ptr().cast(), size)
})
.map(|r| r as usize);
match res {
Ok(x) if x == size => Ok(Some(unsafe { buffer.assume_init() })),
Ok(_) => unreachable!("partial read on signalfd"),
Err(Errno::EAGAIN) => Ok(None),
Err(error) => Err(error),
}
}
/// Constructs a `SignalFd` wrapping an existing `OwnedFd`.
///
/// # Safety
///
/// `OwnedFd` is a valid `SignalFd`.
pub unsafe fn from_owned_fd(fd: OwnedFd) -> Self {
Self(fd)
}
fn update(&self, mask: &SigSet, flags: SfdFlags) -> Result<()> {
let raw_fd = self.0.as_raw_fd();
unsafe {
Errno::result(libc::signalfd(raw_fd, mask.as_ref(), flags.bits()))
.map(drop)
}
}
}
impl AsFd for SignalFd {
fn as_fd(&self) -> BorrowedFd {
self.0.as_fd()
}
}
impl AsRawFd for SignalFd {
fn as_raw_fd(&self) -> RawFd {
self.0.as_raw_fd()
}
}
impl From<SignalFd> for OwnedFd {
fn from(value: SignalFd) -> Self {
value.0
}
}
impl Iterator for SignalFd {
type Item = siginfo;
fn next(&mut self) -> Option<Self::Item> {
match self.read_signal() {
Ok(Some(sig)) => Some(sig),
Ok(None) | Err(_) => None,
}
}
}

2447
vendor/nix/src/sys/socket/addr.rs vendored Normal file

File diff suppressed because it is too large Load Diff

2567
vendor/nix/src/sys/socket/mod.rs vendored Normal file

File diff suppressed because it is too large Load Diff

2003
vendor/nix/src/sys/socket/sockopt.rs vendored Normal file

File diff suppressed because it is too large Load Diff

486
vendor/nix/src/sys/stat.rs vendored Normal file
View File

@@ -0,0 +1,486 @@
#[cfg(any(apple_targets, target_os = "openbsd"))]
pub use libc::c_uint;
#[cfg(any(target_os = "netbsd", freebsdlike))]
pub use libc::c_ulong;
pub use libc::stat as FileStat;
pub use libc::{dev_t, mode_t};
#[cfg(not(target_os = "redox"))]
use crate::fcntl::AtFlags;
use crate::sys::time::{TimeSpec, TimeVal};
use crate::{errno::Errno, NixPath, Result};
use std::mem;
libc_bitflags!(
/// "File type" flags for `mknod` and related functions.
pub struct SFlag: mode_t {
S_IFIFO;
S_IFCHR;
S_IFDIR;
S_IFBLK;
S_IFREG;
S_IFLNK;
S_IFSOCK;
S_IFMT;
}
);
libc_bitflags! {
/// "File mode / permissions" flags.
pub struct Mode: mode_t {
/// Read, write and execute for owner.
S_IRWXU;
/// Read for owner.
S_IRUSR;
/// Write for owner.
S_IWUSR;
/// Execute for owner.
S_IXUSR;
/// Read write and execute for group.
S_IRWXG;
/// Read for group.
S_IRGRP;
/// Write for group.
S_IWGRP;
/// Execute for group.
S_IXGRP;
/// Read, write and execute for other.
S_IRWXO;
/// Read for other.
S_IROTH;
/// Write for other.
S_IWOTH;
/// Execute for other.
S_IXOTH;
/// Set user id on execution.
S_ISUID as mode_t;
/// Set group id on execution.
S_ISGID as mode_t;
S_ISVTX as mode_t;
}
}
#[cfg(any(apple_targets, target_os = "openbsd"))]
pub type type_of_file_flag = c_uint;
#[cfg(any(freebsdlike, target_os = "netbsd"))]
pub type type_of_file_flag = c_ulong;
#[cfg(bsd)]
libc_bitflags! {
/// File flags.
pub struct FileFlag: type_of_file_flag {
/// The file may only be appended to.
SF_APPEND;
/// The file has been archived.
SF_ARCHIVED;
#[cfg(any(target_os = "dragonfly"))]
SF_CACHE;
/// The file may not be changed.
SF_IMMUTABLE;
/// Indicates a WAPBL journal file.
#[cfg(any(target_os = "netbsd"))]
SF_LOG;
/// Do not retain history for file
#[cfg(any(target_os = "dragonfly"))]
SF_NOHISTORY;
/// The file may not be renamed or deleted.
#[cfg(freebsdlike)]
SF_NOUNLINK;
/// Mask of superuser changeable flags
SF_SETTABLE;
/// Snapshot is invalid.
#[cfg(any(target_os = "netbsd"))]
SF_SNAPINVAL;
/// The file is a snapshot file.
#[cfg(any(target_os = "netbsd", target_os = "freebsd"))]
SF_SNAPSHOT;
#[cfg(any(target_os = "dragonfly"))]
SF_XLINK;
/// The file may only be appended to.
UF_APPEND;
/// The file needs to be archived.
#[cfg(any(target_os = "freebsd"))]
UF_ARCHIVE;
#[cfg(any(target_os = "dragonfly"))]
UF_CACHE;
/// File is compressed at the file system level.
#[cfg(apple_targets)]
UF_COMPRESSED;
/// The file may be hidden from directory listings at the application's
/// discretion.
#[cfg(any(
target_os = "freebsd",
apple_targets,
))]
UF_HIDDEN;
/// The file may not be changed.
UF_IMMUTABLE;
/// Do not dump the file.
UF_NODUMP;
#[cfg(any(target_os = "dragonfly"))]
UF_NOHISTORY;
/// The file may not be renamed or deleted.
#[cfg(freebsdlike)]
UF_NOUNLINK;
/// The file is offline, or has the Windows and CIFS
/// `FILE_ATTRIBUTE_OFFLINE` attribute.
#[cfg(any(target_os = "freebsd"))]
UF_OFFLINE;
/// The directory is opaque when viewed through a union stack.
UF_OPAQUE;
/// The file is read only, and may not be written or appended.
#[cfg(any(target_os = "freebsd"))]
UF_READONLY;
/// The file contains a Windows reparse point.
#[cfg(any(target_os = "freebsd"))]
UF_REPARSE;
/// Mask of owner changeable flags.
UF_SETTABLE;
/// The file has the Windows `FILE_ATTRIBUTE_SPARSE_FILE` attribute.
#[cfg(any(target_os = "freebsd"))]
UF_SPARSE;
/// The file has the DOS, Windows and CIFS `FILE_ATTRIBUTE_SYSTEM`
/// attribute.
#[cfg(any(target_os = "freebsd"))]
UF_SYSTEM;
/// File renames and deletes are tracked.
#[cfg(apple_targets)]
UF_TRACKED;
#[cfg(any(target_os = "dragonfly"))]
UF_XLINK;
}
}
/// Create a special or ordinary file, by pathname.
pub fn mknod<P: ?Sized + NixPath>(
path: &P,
kind: SFlag,
perm: Mode,
dev: dev_t,
) -> Result<()> {
let res = path.with_nix_path(|cstr| unsafe {
libc::mknod(cstr.as_ptr(), kind.bits() | perm.bits() as mode_t, dev)
})?;
Errno::result(res).map(drop)
}
/// Create a special or ordinary file, relative to a given directory.
#[cfg(not(any(apple_targets, target_os = "redox", target_os = "haiku")))]
pub fn mknodat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
dirfd: Fd,
path: &P,
kind: SFlag,
perm: Mode,
dev: dev_t,
) -> Result<()> {
use std::os::fd::AsRawFd;
let res = path.with_nix_path(|cstr| unsafe {
libc::mknodat(
dirfd.as_fd().as_raw_fd(),
cstr.as_ptr(),
kind.bits() | perm.bits() as mode_t,
dev,
)
})?;
Errno::result(res).map(drop)
}
#[cfg(target_os = "linux")]
pub const fn major(dev: dev_t) -> u64 {
((dev >> 32) & 0xffff_f000) | ((dev >> 8) & 0x0000_0fff)
}
#[cfg(target_os = "linux")]
pub const fn minor(dev: dev_t) -> u64 {
((dev >> 12) & 0xffff_ff00) | ((dev) & 0x0000_00ff)
}
#[cfg(target_os = "linux")]
pub const fn makedev(major: u64, minor: u64) -> dev_t {
((major & 0xffff_f000) << 32)
| ((major & 0x0000_0fff) << 8)
| ((minor & 0xffff_ff00) << 12)
| (minor & 0x0000_00ff)
}
pub fn umask(mode: Mode) -> Mode {
let prev = unsafe { libc::umask(mode.bits() as mode_t) };
Mode::from_bits(prev).expect("[BUG] umask returned invalid Mode")
}
pub fn stat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat> {
let mut dst = mem::MaybeUninit::uninit();
let res = path.with_nix_path(|cstr| unsafe {
libc::stat(cstr.as_ptr(), dst.as_mut_ptr())
})?;
Errno::result(res)?;
Ok(unsafe { dst.assume_init() })
}
pub fn lstat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat> {
let mut dst = mem::MaybeUninit::uninit();
let res = path.with_nix_path(|cstr| unsafe {
libc::lstat(cstr.as_ptr(), dst.as_mut_ptr())
})?;
Errno::result(res)?;
Ok(unsafe { dst.assume_init() })
}
pub fn fstat<Fd: std::os::fd::AsFd>(fd: Fd) -> Result<FileStat> {
use std::os::fd::AsRawFd;
let mut dst = mem::MaybeUninit::uninit();
let res = unsafe { libc::fstat(fd.as_fd().as_raw_fd(), dst.as_mut_ptr()) };
Errno::result(res)?;
Ok(unsafe { dst.assume_init() })
}
#[cfg(not(target_os = "redox"))]
pub fn fstatat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
dirfd: Fd,
pathname: &P,
f: AtFlags,
) -> Result<FileStat> {
use std::os::fd::AsRawFd;
let mut dst = mem::MaybeUninit::uninit();
let res = pathname.with_nix_path(|cstr| unsafe {
libc::fstatat(
dirfd.as_fd().as_raw_fd(),
cstr.as_ptr(),
dst.as_mut_ptr(),
f.bits() as libc::c_int,
)
})?;
Errno::result(res)?;
Ok(unsafe { dst.assume_init() })
}
/// Change the file permission bits of the file specified by a file descriptor.
///
/// # References
///
/// [fchmod(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmod.html).
pub fn fchmod<Fd: std::os::fd::AsFd>(fd: Fd, mode: Mode) -> Result<()> {
use std::os::fd::AsRawFd;
let res =
unsafe { libc::fchmod(fd.as_fd().as_raw_fd(), mode.bits() as mode_t) };
Errno::result(res).map(drop)
}
/// Flags for `fchmodat` function.
#[derive(Clone, Copy, Debug)]
pub enum FchmodatFlags {
FollowSymlink,
NoFollowSymlink,
}
/// Change the file permission bits.
///
/// 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 [`AT_FDCWD`](crate::fcntl::AT_FDCWD).
///
/// If `flag` is `FchmodatFlags::NoFollowSymlink` and `path` names a symbolic link,
/// then the mode of the symbolic link is changed.
///
/// `fchmodat(AT_FDCWD, path, mode, FchmodatFlags::FollowSymlink)` is identical to
/// a call `libc::chmod(path, mode)`. That's why `chmod` is unimplemented
/// in the `nix` crate.
///
/// # References
///
/// [fchmodat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmodat.html).
#[cfg(not(target_os = "redox"))]
pub fn fchmodat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
dirfd: Fd,
path: &P,
mode: Mode,
flag: FchmodatFlags,
) -> Result<()> {
use std::os::fd::AsRawFd;
let atflag = match flag {
FchmodatFlags::FollowSymlink => AtFlags::empty(),
FchmodatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW,
};
let res = path.with_nix_path(|cstr| unsafe {
libc::fchmodat(
dirfd.as_fd().as_raw_fd(),
cstr.as_ptr(),
mode.bits() as mode_t,
atflag.bits() as libc::c_int,
)
})?;
Errno::result(res).map(drop)
}
/// Change the access and modification times of a file.
///
/// `utimes(path, times)` is identical to
/// `utimensat(None, path, times, UtimensatFlags::FollowSymlink)`. The former
/// is a deprecated API so prefer using the latter if the platforms you care
/// about support it.
///
/// # References
///
/// [utimes(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/utimes.html).
pub fn utimes<P: ?Sized + NixPath>(
path: &P,
atime: &TimeVal,
mtime: &TimeVal,
) -> Result<()> {
let times: [libc::timeval; 2] = [*atime.as_ref(), *mtime.as_ref()];
let res = path.with_nix_path(|cstr| unsafe {
libc::utimes(cstr.as_ptr(), &times[0])
})?;
Errno::result(res).map(drop)
}
/// Change the access and modification times of a file without following symlinks.
///
/// `lutimes(path, times)` is identical to
/// `utimensat(None, path, times, UtimensatFlags::NoFollowSymlink)`. The former
/// is a deprecated API so prefer using the latter if the platforms you care
/// about support it.
///
/// # References
///
/// [lutimes(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/lutimes.html).
#[cfg(any(
target_os = "linux",
target_os = "haiku",
apple_targets,
target_os = "freebsd",
target_os = "netbsd"
))]
pub fn lutimes<P: ?Sized + NixPath>(
path: &P,
atime: &TimeVal,
mtime: &TimeVal,
) -> Result<()> {
let times: [libc::timeval; 2] = [*atime.as_ref(), *mtime.as_ref()];
let res = path.with_nix_path(|cstr| unsafe {
libc::lutimes(cstr.as_ptr(), &times[0])
})?;
Errno::result(res).map(drop)
}
/// Change the access and modification times of the file specified by a file descriptor.
///
/// If you want to set the timestamp to now, use `TimeSpec::UTIME_NOW`. Use
/// `TimeSpec::UTIME_OMIT` if you don't want to change it.
///
/// # References
///
/// [futimens(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html).
#[inline]
pub fn futimens<Fd: std::os::fd::AsFd>(
fd: Fd,
atime: &TimeSpec,
mtime: &TimeSpec,
) -> Result<()> {
use std::os::fd::AsRawFd;
let times: [libc::timespec; 2] = [*atime.as_ref(), *mtime.as_ref()];
let res = unsafe { libc::futimens(fd.as_fd().as_raw_fd(), &times[0]) };
Errno::result(res).map(drop)
}
/// Flags for `utimensat` function.
// TODO: replace with fcntl::AtFlags
#[derive(Clone, Copy, Debug)]
pub enum UtimensatFlags {
FollowSymlink,
NoFollowSymlink,
}
/// Change the access and modification times of a file.
///
/// 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 [`AT_FDCWD`](crate::fcntl::AT_FDCWD).
///
/// If `flag` is `UtimensatFlags::NoFollowSymlink` and `path` names a symbolic link,
/// then the mode of the symbolic link is changed.
///
/// `utimensat(AT_FDCWD, path, times, UtimensatFlags::FollowSymlink)` is identical to
/// `utimes(path, times)`. The latter is a deprecated API so prefer using the
/// former if the platforms you care about support it.
///
/// If you want to set the timestamp to now, use `TimeSpec::UTIME_NOW`. Use
/// `TimeSpec::UTIME_OMIT` if you don't want to change it.
///
/// # References
///
/// [utimensat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/utimens.html).
#[cfg(not(target_os = "redox"))]
pub fn utimensat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
dirfd: Fd,
path: &P,
atime: &TimeSpec,
mtime: &TimeSpec,
flag: UtimensatFlags,
) -> Result<()> {
use std::os::fd::AsRawFd;
let atflag = match flag {
UtimensatFlags::FollowSymlink => AtFlags::empty(),
UtimensatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW,
};
let times: [libc::timespec; 2] = [*atime.as_ref(), *mtime.as_ref()];
let res = path.with_nix_path(|cstr| unsafe {
libc::utimensat(
dirfd.as_fd().as_raw_fd(),
cstr.as_ptr(),
&times[0],
atflag.bits() as libc::c_int,
)
})?;
Errno::result(res).map(drop)
}
/// Create a directory at the path specified by `dirfd` and `path`.
///
/// If `path` is a relative path, then it is interpreted relative to the directory
/// referred to by the file descriptor `dirfd`. (One can use [`AT_FDCWD`][link] to
/// specify the current working directory in `dirfd`). If `path` is absolute,
/// then `dirfd` is ignored.
///
/// [link]: crate::fcntl::AT_FDCWD
#[cfg(not(target_os = "redox"))]
pub fn mkdirat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
dirfd: Fd,
path: &P,
mode: Mode,
) -> Result<()> {
use std::os::fd::AsRawFd;
let res = path.with_nix_path(|cstr| unsafe {
libc::mkdirat(
dirfd.as_fd().as_raw_fd(),
cstr.as_ptr(),
mode.bits() as mode_t,
)
})?;
Errno::result(res).map(drop)
}

695
vendor/nix/src/sys/statfs.rs vendored Normal file
View File

@@ -0,0 +1,695 @@
//! Get filesystem statistics, non-portably
//!
//! See [`statvfs`](crate::sys::statvfs) for a portable alternative.
#[cfg(not(any(linux_android, target_os = "cygwin")))]
use std::ffi::CStr;
use std::fmt::{self, Debug};
use std::mem;
use std::os::unix::io::{AsFd, AsRawFd};
use cfg_if::cfg_if;
#[cfg(all(feature = "mount", bsd))]
use crate::mount::MntFlags;
#[cfg(target_os = "linux")]
use crate::sys::statvfs::FsFlags;
use crate::{errno::Errno, NixPath, Result};
/// Identifies a mounted file system
#[cfg(target_os = "android")]
pub type fsid_t = libc::__fsid_t;
/// Identifies a mounted file system
#[cfg(not(any(target_os = "android", target_os = "cygwin")))]
pub type fsid_t = libc::fsid_t;
/// Identifies a mounted file system
#[cfg(target_os = "cygwin")]
pub type fsid_t = libc::c_long;
cfg_if! {
if #[cfg(any(linux_android, target_os = "fuchsia"))] {
type type_of_statfs = libc::statfs64;
const LIBC_FSTATFS: unsafe extern "C" fn
(fd: libc::c_int, buf: *mut type_of_statfs) -> libc::c_int
= libc::fstatfs64;
const LIBC_STATFS: unsafe extern "C" fn
(path: *const libc::c_char, buf: *mut type_of_statfs) -> libc::c_int
= libc::statfs64;
} else {
type type_of_statfs = libc::statfs;
const LIBC_FSTATFS: unsafe extern "C" fn
(fd: libc::c_int, buf: *mut type_of_statfs) -> libc::c_int
= libc::fstatfs;
const LIBC_STATFS: unsafe extern "C" fn
(path: *const libc::c_char, buf: *mut type_of_statfs) -> libc::c_int
= libc::statfs;
}
}
/// Describes a mounted file system
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Statfs(type_of_statfs);
#[cfg(target_os = "freebsd")]
type fs_type_t = u32;
#[cfg(target_os = "android")]
type fs_type_t = libc::c_ulong;
#[cfg(all(
target_os = "linux",
target_arch = "s390x",
not(target_env = "musl")
))]
type fs_type_t = libc::c_uint;
#[cfg(all(target_os = "linux", any(target_env = "musl", target_env = "ohos")))]
type fs_type_t = libc::c_ulong;
#[cfg(all(target_os = "linux", target_env = "uclibc"))]
type fs_type_t = libc::c_int;
#[cfg(all(
target_os = "linux",
not(any(
target_arch = "s390x",
target_env = "musl",
target_env = "ohos",
target_env = "uclibc"
))
))]
type fs_type_t = libc::__fsword_t;
#[cfg(target_os = "cygwin")]
type fs_type_t = libc::c_long;
/// Describes the file system type as known by the operating system.
#[cfg(any(
target_os = "freebsd",
target_os = "android",
all(target_os = "linux", target_arch = "s390x"),
all(target_os = "linux", target_env = "musl"),
all(target_os = "linux", target_env = "ohos"),
all(
target_os = "linux",
not(any(target_arch = "s390x", target_env = "musl"))
),
target_os = "cygwin",
))]
#[derive(Eq, Copy, Clone, PartialEq, Debug)]
pub struct FsType(pub fs_type_t);
// These constants are defined without documentation in the Linux headers, so we
// can't very well document them here.
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const ADFS_SUPER_MAGIC: FsType =
FsType(libc::ADFS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const AFFS_SUPER_MAGIC: FsType =
FsType(libc::AFFS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const AFS_SUPER_MAGIC: FsType = FsType(libc::AFS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const AUTOFS_SUPER_MAGIC: FsType =
FsType(libc::AUTOFS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const BPF_FS_MAGIC: FsType = FsType(libc::BPF_FS_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const BTRFS_SUPER_MAGIC: FsType =
FsType(libc::BTRFS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const CGROUP2_SUPER_MAGIC: FsType =
FsType(libc::CGROUP2_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const CGROUP_SUPER_MAGIC: FsType =
FsType(libc::CGROUP_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const CODA_SUPER_MAGIC: FsType =
FsType(libc::CODA_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const CRAMFS_MAGIC: FsType = FsType(libc::CRAMFS_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const DEBUGFS_MAGIC: FsType = FsType(libc::DEBUGFS_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const DEVPTS_SUPER_MAGIC: FsType =
FsType(libc::DEVPTS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const ECRYPTFS_SUPER_MAGIC: FsType =
FsType(libc::ECRYPTFS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const EFS_SUPER_MAGIC: FsType = FsType(libc::EFS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const EXT2_SUPER_MAGIC: FsType =
FsType(libc::EXT2_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const EXT3_SUPER_MAGIC: FsType =
FsType(libc::EXT3_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const EXT4_SUPER_MAGIC: FsType =
FsType(libc::EXT4_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const F2FS_SUPER_MAGIC: FsType =
FsType(libc::F2FS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const FUSE_SUPER_MAGIC: FsType =
FsType(libc::FUSE_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const FUTEXFS_SUPER_MAGIC: FsType =
FsType(libc::FUTEXFS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const HOSTFS_SUPER_MAGIC: FsType =
FsType(libc::HOSTFS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const HPFS_SUPER_MAGIC: FsType =
FsType(libc::HPFS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const HUGETLBFS_MAGIC: FsType = FsType(libc::HUGETLBFS_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const ISOFS_SUPER_MAGIC: FsType =
FsType(libc::ISOFS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const JFFS2_SUPER_MAGIC: FsType =
FsType(libc::JFFS2_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const MINIX2_SUPER_MAGIC2: FsType =
FsType(libc::MINIX2_SUPER_MAGIC2 as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const MINIX2_SUPER_MAGIC: FsType =
FsType(libc::MINIX2_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const MINIX3_SUPER_MAGIC: FsType =
FsType(libc::MINIX3_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const MINIX_SUPER_MAGIC2: FsType =
FsType(libc::MINIX_SUPER_MAGIC2 as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const MINIX_SUPER_MAGIC: FsType =
FsType(libc::MINIX_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const MSDOS_SUPER_MAGIC: FsType =
FsType(libc::MSDOS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const NCP_SUPER_MAGIC: FsType = FsType(libc::NCP_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const NFS_SUPER_MAGIC: FsType = FsType(libc::NFS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const NILFS_SUPER_MAGIC: FsType =
FsType(libc::NILFS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const OCFS2_SUPER_MAGIC: FsType =
FsType(libc::OCFS2_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const OPENPROM_SUPER_MAGIC: FsType =
FsType(libc::OPENPROM_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const OVERLAYFS_SUPER_MAGIC: FsType =
FsType(libc::OVERLAYFS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const PROC_SUPER_MAGIC: FsType =
FsType(libc::PROC_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const QNX4_SUPER_MAGIC: FsType =
FsType(libc::QNX4_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const QNX6_SUPER_MAGIC: FsType =
FsType(libc::QNX6_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const RDTGROUP_SUPER_MAGIC: FsType =
FsType(libc::RDTGROUP_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const REISERFS_SUPER_MAGIC: FsType =
FsType(libc::REISERFS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const SECURITYFS_MAGIC: FsType =
FsType(libc::SECURITYFS_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const SELINUX_MAGIC: FsType = FsType(libc::SELINUX_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const SMACK_MAGIC: FsType = FsType(libc::SMACK_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const SMB_SUPER_MAGIC: FsType = FsType(libc::SMB_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const SYSFS_MAGIC: FsType = FsType(libc::SYSFS_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const TMPFS_MAGIC: FsType = FsType(libc::TMPFS_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const TRACEFS_MAGIC: FsType = FsType(libc::TRACEFS_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const UDF_SUPER_MAGIC: FsType = FsType(libc::UDF_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const USBDEVICE_SUPER_MAGIC: FsType =
FsType(libc::USBDEVICE_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const XENFS_SUPER_MAGIC: FsType =
FsType(libc::XENFS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const NSFS_MAGIC: FsType = FsType(libc::NSFS_MAGIC as fs_type_t);
#[cfg(all(linux_android, not(target_env = "musl"), not(target_env = "ohos")))]
#[allow(missing_docs)]
pub const XFS_SUPER_MAGIC: FsType = FsType(libc::XFS_SUPER_MAGIC as fs_type_t);
impl Statfs {
/// Magic code defining system type
#[cfg(not(any(
target_os = "openbsd",
target_os = "dragonfly",
apple_targets,
)))]
pub fn filesystem_type(&self) -> FsType {
FsType(self.0.f_type)
}
/// Magic code defining system type
#[cfg(not(any(linux_android, target_os = "cygwin")))]
pub fn filesystem_type_name(&self) -> &str {
let c_str = unsafe { CStr::from_ptr(self.0.f_fstypename.as_ptr()) };
c_str.to_str().unwrap()
}
/// Optimal transfer block size
#[cfg(apple_targets)]
pub fn optimal_transfer_size(&self) -> i32 {
self.0.f_iosize
}
/// Optimal transfer block size
#[cfg(target_os = "openbsd")]
pub fn optimal_transfer_size(&self) -> u32 {
self.0.f_iosize
}
/// Optimal transfer block size
#[cfg(all(
target_os = "linux",
target_arch = "s390x",
not(target_env = "musl")
))]
pub fn optimal_transfer_size(&self) -> u32 {
self.0.f_bsize
}
/// Optimal transfer block size
#[cfg(any(
target_os = "android",
all(target_os = "linux", target_env = "musl"),
all(target_os = "linux", target_env = "ohos")
))]
pub fn optimal_transfer_size(&self) -> libc::c_ulong {
self.0.f_bsize
}
/// Optimal transfer block size
#[cfg(all(
target_os = "linux",
not(any(
target_arch = "s390x",
target_env = "musl",
target_env = "ohos",
target_env = "uclibc"
))
))]
pub fn optimal_transfer_size(&self) -> libc::__fsword_t {
self.0.f_bsize
}
/// Optimal transfer block size
#[cfg(all(target_os = "linux", target_env = "uclibc"))]
pub fn optimal_transfer_size(&self) -> libc::c_int {
self.0.f_bsize
}
/// Optimal transfer block size
#[cfg(target_os = "dragonfly")]
pub fn optimal_transfer_size(&self) -> libc::c_long {
self.0.f_iosize
}
/// Optimal transfer block size
#[cfg(target_os = "freebsd")]
pub fn optimal_transfer_size(&self) -> u64 {
self.0.f_iosize
}
/// Size of a block
#[cfg(any(apple_targets, target_os = "openbsd"))]
pub fn block_size(&self) -> u32 {
self.0.f_bsize
}
/// Size of a block
// f_bsize on linux: https://github.com/torvalds/linux/blob/master/fs/nfs/super.c#L471
#[cfg(all(
target_os = "linux",
target_arch = "s390x",
not(target_env = "musl")
))]
pub fn block_size(&self) -> u32 {
self.0.f_bsize
}
/// Size of a block
// f_bsize on linux: https://github.com/torvalds/linux/blob/master/fs/nfs/super.c#L471
#[cfg(all(target_os = "linux", target_env = "musl"))]
pub fn block_size(&self) -> libc::c_ulong {
self.0.f_bsize
}
/// Size of a block
// f_bsize on linux: https://github.com/torvalds/linux/blob/master/fs/nfs/super.c#L471
#[cfg(all(target_os = "linux", target_env = "ohos"))]
pub fn block_size(&self) -> libc::c_ulong {
self.0.f_bsize
}
/// Size of a block
// f_bsize on linux: https://github.com/torvalds/linux/blob/master/fs/nfs/super.c#L471
#[cfg(all(target_os = "linux", target_env = "uclibc"))]
pub fn block_size(&self) -> libc::c_int {
self.0.f_bsize
}
/// Size of a block
// f_bsize on linux: https://github.com/torvalds/linux/blob/master/fs/nfs/super.c#L471
#[cfg(all(
target_os = "linux",
not(any(
target_arch = "s390x",
target_env = "musl",
target_env = "ohos",
target_env = "uclibc"
))
))]
pub fn block_size(&self) -> libc::__fsword_t {
self.0.f_bsize
}
/// Size of a block
#[cfg(target_os = "freebsd")]
pub fn block_size(&self) -> u64 {
self.0.f_bsize
}
/// Size of a block
#[cfg(target_os = "android")]
pub fn block_size(&self) -> libc::c_ulong {
self.0.f_bsize
}
/// Size of a block
#[cfg(any(target_os = "dragonfly", target_os = "cygwin"))]
pub fn block_size(&self) -> libc::c_long {
self.0.f_bsize
}
/// Get the mount flags
#[cfg(all(feature = "mount", bsd))]
#[allow(clippy::unnecessary_cast)] // Not unnecessary on all arches
pub fn flags(&self) -> MntFlags {
MntFlags::from_bits_truncate(self.0.f_flags as i32)
}
/// Get the mount flags
// The f_flags field exists on Android and Fuchsia too, but without man
// pages I can't tell if it can be cast to FsFlags.
#[cfg(target_os = "linux")]
pub fn flags(&self) -> FsFlags {
FsFlags::from_bits_truncate(self.0.f_flags as libc::c_ulong)
}
/// Maximum length of filenames
#[cfg(any(target_os = "freebsd", target_os = "openbsd"))]
pub fn maximum_name_length(&self) -> u32 {
self.0.f_namemax
}
/// Maximum length of filenames
#[cfg(all(
target_os = "linux",
target_arch = "s390x",
not(target_env = "musl")
))]
pub fn maximum_name_length(&self) -> u32 {
self.0.f_namelen
}
/// Maximum length of filenames
#[cfg(all(target_os = "linux", target_env = "musl"))]
pub fn maximum_name_length(&self) -> libc::c_ulong {
self.0.f_namelen
}
/// Maximum length of filenames
#[cfg(all(target_os = "linux", target_env = "uclibc"))]
pub fn maximum_name_length(&self) -> libc::c_int {
self.0.f_namelen
}
/// Maximum length of filenames
#[cfg(all(
target_os = "linux",
not(any(
target_arch = "s390x",
target_env = "musl",
target_env = "ohos",
target_env = "uclibc"
))
))]
pub fn maximum_name_length(&self) -> libc::__fsword_t {
self.0.f_namelen
}
/// Maximum length of filenames
#[cfg(target_os = "android")]
pub fn maximum_name_length(&self) -> libc::c_ulong {
self.0.f_namelen
}
/// Total data blocks in filesystem
#[cfg(any(
apple_targets,
linux_android,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "openbsd",
))]
pub fn blocks(&self) -> u64 {
self.0.f_blocks
}
/// Total data blocks in filesystem
#[cfg(any(target_os = "dragonfly", target_os = "cygwin"))]
pub fn blocks(&self) -> libc::c_long {
self.0.f_blocks
}
/// Total data blocks in filesystem
#[cfg(target_os = "emscripten")]
pub fn blocks(&self) -> u32 {
self.0.f_blocks
}
/// Free blocks in filesystem
#[cfg(any(
apple_targets,
linux_android,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "openbsd",
))]
pub fn blocks_free(&self) -> u64 {
self.0.f_bfree
}
/// Free blocks in filesystem
#[cfg(any(target_os = "dragonfly", target_os = "cygwin"))]
pub fn blocks_free(&self) -> libc::c_long {
self.0.f_bfree
}
/// Free blocks in filesystem
#[cfg(target_os = "emscripten")]
pub fn blocks_free(&self) -> u32 {
self.0.f_bfree
}
/// Free blocks available to unprivileged user
#[cfg(any(apple_targets, linux_android, target_os = "fuchsia"))]
pub fn blocks_available(&self) -> u64 {
self.0.f_bavail
}
/// Free blocks available to unprivileged user
#[cfg(any(target_os = "dragonfly", target_os = "cygwin"))]
pub fn blocks_available(&self) -> libc::c_long {
self.0.f_bavail
}
/// Free blocks available to unprivileged user
#[cfg(any(target_os = "freebsd", target_os = "openbsd"))]
pub fn blocks_available(&self) -> i64 {
self.0.f_bavail
}
/// Free blocks available to unprivileged user
#[cfg(target_os = "emscripten")]
pub fn blocks_available(&self) -> u32 {
self.0.f_bavail
}
/// Total file nodes in filesystem
#[cfg(any(
apple_targets,
linux_android,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "openbsd",
))]
pub fn files(&self) -> u64 {
self.0.f_files
}
/// Total file nodes in filesystem
#[cfg(any(target_os = "dragonfly", target_os = "cygwin"))]
pub fn files(&self) -> libc::c_long {
self.0.f_files
}
/// Total file nodes in filesystem
#[cfg(target_os = "emscripten")]
pub fn files(&self) -> u32 {
self.0.f_files
}
/// Free file nodes in filesystem
#[cfg(any(
apple_targets,
linux_android,
target_os = "fuchsia",
target_os = "openbsd",
))]
pub fn files_free(&self) -> u64 {
self.0.f_ffree
}
/// Free file nodes in filesystem
#[cfg(any(target_os = "dragonfly", target_os = "cygwin"))]
pub fn files_free(&self) -> libc::c_long {
self.0.f_ffree
}
/// Free file nodes in filesystem
#[cfg(target_os = "freebsd")]
pub fn files_free(&self) -> i64 {
self.0.f_ffree
}
/// Free file nodes in filesystem
#[cfg(target_os = "emscripten")]
pub fn files_free(&self) -> u32 {
self.0.f_ffree
}
/// Filesystem ID
pub fn filesystem_id(&self) -> fsid_t {
self.0.f_fsid
}
}
impl Debug for Statfs {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut ds = f.debug_struct("Statfs");
#[cfg(not(target_os = "cygwin"))]
ds.field("optimal_transfer_size", &self.optimal_transfer_size());
ds.field("block_size", &self.block_size());
ds.field("blocks", &self.blocks());
ds.field("blocks_free", &self.blocks_free());
ds.field("blocks_available", &self.blocks_available());
ds.field("files", &self.files());
ds.field("files_free", &self.files_free());
ds.field("filesystem_id", &self.filesystem_id());
#[cfg(all(feature = "mount", bsd))]
ds.field("flags", &self.flags());
ds.finish()
}
}
/// Describes a mounted file system.
///
/// The result is OS-dependent. For a portable alternative, see
/// [`statvfs`](crate::sys::statvfs::statvfs).
///
/// # Arguments
///
/// `path` - Path to any file within the file system to describe
pub fn statfs<P: ?Sized + NixPath>(path: &P) -> Result<Statfs> {
unsafe {
let mut stat = mem::MaybeUninit::<type_of_statfs>::uninit();
let res = path.with_nix_path(|path| {
LIBC_STATFS(path.as_ptr(), stat.as_mut_ptr())
})?;
Errno::result(res).map(|_| Statfs(stat.assume_init()))
}
}
/// Describes a mounted file system.
///
/// The result is OS-dependent. For a portable alternative, see
/// [`fstatvfs`](crate::sys::statvfs::fstatvfs).
///
/// # Arguments
///
/// `fd` - File descriptor of any open file within the file system to describe
pub fn fstatfs<Fd: AsFd>(fd: Fd) -> Result<Statfs> {
unsafe {
let mut stat = mem::MaybeUninit::<type_of_statfs>::uninit();
Errno::result(LIBC_FSTATFS(fd.as_fd().as_raw_fd(), stat.as_mut_ptr()))
.map(|_| Statfs(stat.assume_init()))
}
}

150
vendor/nix/src/sys/statvfs.rs vendored Normal file
View File

@@ -0,0 +1,150 @@
//! Get filesystem statistics
//!
//! See [the man pages](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fstatvfs.html)
//! for more details.
use std::mem;
use std::os::unix::io::{AsFd, AsRawFd};
use libc::{self, c_ulong};
use crate::{errno::Errno, NixPath, Result};
#[cfg(not(target_os = "redox"))]
libc_bitflags!(
/// File system mount Flags
#[derive(Default)]
pub struct FsFlags: c_ulong {
/// Read Only
#[cfg(not(target_os = "haiku"))]
ST_RDONLY;
/// Do not allow the set-uid bits to have an effect
#[cfg(not(target_os = "haiku"))]
ST_NOSUID;
/// Do not interpret character or block-special devices
#[cfg(linux_android)]
ST_NODEV;
/// Do not allow execution of binaries on the filesystem
#[cfg(linux_android)]
ST_NOEXEC;
/// All IO should be done synchronously
#[cfg(linux_android)]
ST_SYNCHRONOUS;
/// Allow mandatory locks on the filesystem
#[cfg(linux_android)]
ST_MANDLOCK;
/// Write on file/directory/symlink
#[cfg(target_os = "linux")]
ST_WRITE;
/// Append-only file
#[cfg(target_os = "linux")]
ST_APPEND;
/// Immutable file
#[cfg(target_os = "linux")]
ST_IMMUTABLE;
/// Do not update access times on files
#[cfg(linux_android)]
ST_NOATIME;
/// Do not update access times on files
#[cfg(linux_android)]
ST_NODIRATIME;
/// Update access time relative to modify/change time
#[cfg(any(target_os = "android", all(target_os = "linux", not(target_env = "musl"), not(target_env = "ohos"))))]
ST_RELATIME;
}
);
/// Wrapper around the POSIX `statvfs` struct
///
/// For more information see the [`statvfs(3)` man pages](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_statvfs.h.html).
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Statvfs(libc::statvfs);
impl Statvfs {
/// get the file system block size
pub fn block_size(&self) -> c_ulong {
self.0.f_bsize
}
/// Get the fundamental file system block size
pub fn fragment_size(&self) -> c_ulong {
self.0.f_frsize
}
/// Get the number of blocks.
///
/// Units are in units of `fragment_size()`
pub fn blocks(&self) -> libc::fsblkcnt_t {
self.0.f_blocks
}
/// Get the number of free blocks in the file system
pub fn blocks_free(&self) -> libc::fsblkcnt_t {
self.0.f_bfree
}
/// Get the number of free blocks for unprivileged users
pub fn blocks_available(&self) -> libc::fsblkcnt_t {
self.0.f_bavail
}
/// Get the total number of file inodes
pub fn files(&self) -> libc::fsfilcnt_t {
self.0.f_files
}
/// Get the number of free file inodes
pub fn files_free(&self) -> libc::fsfilcnt_t {
self.0.f_ffree
}
/// Get the number of free file inodes for unprivileged users
pub fn files_available(&self) -> libc::fsfilcnt_t {
self.0.f_favail
}
/// Get the file system id
#[cfg(not(target_os = "hurd"))]
pub fn filesystem_id(&self) -> c_ulong {
self.0.f_fsid
}
/// Get the file system id
#[cfg(target_os = "hurd")]
pub fn filesystem_id(&self) -> u64 {
self.0.f_fsid
}
/// Get the mount flags
#[cfg(not(target_os = "redox"))]
pub fn flags(&self) -> FsFlags {
FsFlags::from_bits_truncate(self.0.f_flag)
}
/// Get the maximum filename length
pub fn name_max(&self) -> c_ulong {
self.0.f_namemax
}
}
/// Return a `Statvfs` object with information about the `path`
pub fn statvfs<P: ?Sized + NixPath>(path: &P) -> Result<Statvfs> {
unsafe {
Errno::clear();
let mut stat = mem::MaybeUninit::<libc::statvfs>::uninit();
let res = path.with_nix_path(|path| {
libc::statvfs(path.as_ptr(), stat.as_mut_ptr())
})?;
Errno::result(res).map(|_| Statvfs(stat.assume_init()))
}
}
/// Return a `Statvfs` object with information about `fd`
pub fn fstatvfs<Fd: AsFd>(fd: Fd) -> Result<Statvfs> {
unsafe {
Errno::clear();
let mut stat = mem::MaybeUninit::<libc::statvfs>::uninit();
Errno::result(libc::fstatvfs(fd.as_fd().as_raw_fd(), stat.as_mut_ptr()))
.map(|_| Statvfs(stat.assume_init()))
}
}

83
vendor/nix/src/sys/sysinfo.rs vendored Normal file
View File

@@ -0,0 +1,83 @@
use libc::SI_LOAD_SHIFT;
use std::time::Duration;
use std::{cmp, mem};
use crate::errno::Errno;
use crate::Result;
/// System info structure returned by `sysinfo`.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
#[repr(transparent)]
pub struct SysInfo(libc::sysinfo);
// The fields are c_ulong on 32-bit linux, u64 on 64-bit linux; x32's ulong is u32
#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
type mem_blocks_t = u64;
#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
type mem_blocks_t = libc::c_ulong;
impl SysInfo {
/// Returns the load average tuple.
///
/// The returned values represent the load average over time intervals of
/// 1, 5, and 15 minutes, respectively.
pub fn load_average(&self) -> (f64, f64, f64) {
(
self.0.loads[0] as f64 / (1 << SI_LOAD_SHIFT) as f64,
self.0.loads[1] as f64 / (1 << SI_LOAD_SHIFT) as f64,
self.0.loads[2] as f64 / (1 << SI_LOAD_SHIFT) as f64,
)
}
/// Returns the time since system boot.
// The cast is not unnecessary on all platforms.
#[allow(clippy::unnecessary_cast)]
pub fn uptime(&self) -> Duration {
// Truncate negative values to 0
Duration::from_secs(cmp::max(self.0.uptime, 0) as u64)
}
/// Current number of processes.
pub fn process_count(&self) -> u16 {
self.0.procs
}
/// Returns the amount of swap memory in Bytes.
pub fn swap_total(&self) -> u64 {
self.scale_mem(self.0.totalswap)
}
/// Returns the amount of unused swap memory in Bytes.
pub fn swap_free(&self) -> u64 {
self.scale_mem(self.0.freeswap)
}
/// Returns the total amount of installed RAM in Bytes.
pub fn ram_total(&self) -> u64 {
self.scale_mem(self.0.totalram)
}
/// Returns the amount of completely unused RAM in Bytes.
///
/// "Unused" in this context means that the RAM in neither actively used by
/// programs, nor by the operating system as disk cache or buffer. It is
/// "wasted" RAM since it currently serves no purpose.
pub fn ram_unused(&self) -> u64 {
self.scale_mem(self.0.freeram)
}
// The cast is not unnecessary on all platforms.
#[allow(clippy::unnecessary_cast)]
fn scale_mem(&self, units: mem_blocks_t) -> u64 {
units as u64 * self.0.mem_unit as u64
}
}
/// Returns system information.
///
/// [See `sysinfo(2)`](https://man7.org/linux/man-pages/man2/sysinfo.2.html).
pub fn sysinfo() -> Result<SysInfo> {
let mut info = mem::MaybeUninit::uninit();
let res = unsafe { libc::sysinfo(info.as_mut_ptr()) };
Errno::result(res).map(|_| unsafe { SysInfo(info.assume_init()) })
}

925
vendor/nix/src/sys/termios.rs vendored Normal file
View File

@@ -0,0 +1,925 @@
//! An interface for controlling asynchronous communication ports
//!
//! This interface provides a safe wrapper around the termios subsystem defined by POSIX. The
//! underlying types are all implemented in libc for most platforms and either wrapped in safer
//! types here or exported directly.
//!
//! If you are unfamiliar with the `termios` API, you should first read the
//! [API documentation](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/termios.h.html) and
//! then come back to understand how `nix` safely wraps it.
//!
//! It should be noted that this API incurs some runtime overhead above the base `libc` definitions.
//! As this interface is not used with high-bandwidth information, this should be fine in most
//! cases. The primary cost when using this API is that the `Termios` datatype here duplicates the
//! standard fields of the underlying `termios` struct and uses safe type wrappers for those fields.
//! This means that when crossing the FFI interface to the underlying C library, data is first
//! copied into the underlying `termios` struct, then the operation is done, and the data is copied
//! back (with additional sanity checking) into the safe wrapper types. The `termios` struct is
//! relatively small across all platforms (on the order of 32-64 bytes).
//!
//! The following examples highlight some of the API use cases such that users coming from using C
//! or reading the standard documentation will understand how to use the safe API exposed here.
//!
//! Example disabling processing of the end-of-file control character:
//!
//! ```
//! # use self::nix::sys::termios::SpecialCharacterIndices::VEOF;
//! # use self::nix::sys::termios::{_POSIX_VDISABLE, Termios};
//! # let mut termios: Termios = unsafe { std::mem::zeroed() };
//! termios.control_chars[VEOF as usize] = _POSIX_VDISABLE;
//! ```
//!
//! The flags within `Termios` are defined as bitfields using the `bitflags` crate. This provides
//! an interface for working with bitfields that is similar to working with the raw unsigned
//! integer types but offers type safety because of the internal checking that values will always
//! be a valid combination of the defined flags.
//!
//! An example showing some of the basic operations for interacting with the control flags:
//!
//! ```
//! # use self::nix::sys::termios::{ControlFlags, Termios};
//! # let mut termios: Termios = unsafe { std::mem::zeroed() };
//! termios.control_flags & ControlFlags::CSIZE == ControlFlags::CS5;
//! termios.control_flags |= ControlFlags::CS5;
//! ```
//!
//! # Baud rates
//!
//! This API is not consistent across platforms when it comes to `BaudRate`: Android and Linux both
//! only support the rates specified by the `BaudRate` enum through their termios API while the BSDs
//! support arbitrary baud rates as the values of the `BaudRate` enum constants are the same integer
//! value of the constant (`B9600` == `9600`). Therefore the `nix::termios` API uses the following
//! conventions:
//!
//! * `cfgetispeed()` - Returns `u32` on BSDs, `BaudRate` on Android/Linux
//! * `cfgetospeed()` - Returns `u32` on BSDs, `BaudRate` on Android/Linux
//! * `cfsetispeed()` - Takes `u32` or `BaudRate` on BSDs, `BaudRate` on Android/Linux
//! * `cfsetospeed()` - Takes `u32` or `BaudRate` on BSDs, `BaudRate` on Android/Linux
//! * `cfsetspeed()` - Takes `u32` or `BaudRate` on BSDs, `BaudRate` on Android/Linux
//!
//! The most common use case of specifying a baud rate using the enum will work the same across
//! platforms:
//!
//! ```rust
//! # use nix::sys::termios::{BaudRate, cfsetispeed, cfsetospeed, cfsetspeed, Termios};
//! # fn main() {
//! # let mut t: Termios = unsafe { std::mem::zeroed() };
//! cfsetispeed(&mut t, BaudRate::B9600).unwrap();
//! cfsetospeed(&mut t, BaudRate::B9600).unwrap();
//! cfsetspeed(&mut t, BaudRate::B9600).unwrap();
//! # }
//! ```
//!
//! Additionally round-tripping baud rates is consistent across platforms:
//!
//! ```rust
//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetispeed, cfsetspeed, Termios};
//! # fn main() {
//! # let mut t: Termios = unsafe { std::mem::zeroed() };
//! # cfsetspeed(&mut t, BaudRate::B9600).unwrap();
//! let speed = cfgetispeed(&t);
//! assert_eq!(speed, cfgetospeed(&t));
//! cfsetispeed(&mut t, speed).unwrap();
//! # }
//! ```
//!
//! On non-BSDs, `cfgetispeed()` and `cfgetospeed()` both return a `BaudRate`:
//!
#![cfg_attr(bsd, doc = " ```rust,ignore")]
#![cfg_attr(not(bsd), doc = " ```rust")]
//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetspeed, Termios};
//! # fn main() {
//! # let mut t: Termios = unsafe { std::mem::zeroed() };
//! # cfsetspeed(&mut t, BaudRate::B9600);
//! assert_eq!(cfgetispeed(&t), BaudRate::B9600);
//! assert_eq!(cfgetospeed(&t), BaudRate::B9600);
//! # }
//! ```
//!
//! But on the BSDs, `cfgetispeed()` and `cfgetospeed()` both return `u32`s:
//!
#![cfg_attr(bsd, doc = " ```rust")]
#![cfg_attr(not(bsd), doc = " ```rust,ignore")]
//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetspeed, Termios};
//! # fn main() {
//! # let mut t: Termios = unsafe { std::mem::zeroed() };
//! # cfsetspeed(&mut t, 9600u32);
//! assert_eq!(cfgetispeed(&t), 9600u32);
//! assert_eq!(cfgetospeed(&t), 9600u32);
//! # }
//! ```
//!
//! It's trivial to convert from a `BaudRate` to a `u32` on BSDs:
//!
#![cfg_attr(bsd, doc = " ```rust")]
#![cfg_attr(not(bsd), doc = " ```rust,ignore")]
//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfsetspeed, Termios};
//! # fn main() {
//! # let mut t: Termios = unsafe { std::mem::zeroed() };
//! # cfsetspeed(&mut t, 9600u32);
//! assert_eq!(cfgetispeed(&t), BaudRate::B9600.into());
//! assert_eq!(u32::from(BaudRate::B9600), 9600u32);
//! # }
//! ```
//!
//! And on BSDs you can specify arbitrary baud rates (**note** this depends on hardware support)
//! by specifying baud rates directly using `u32`s:
//!
#![cfg_attr(bsd, doc = " ```rust")]
#![cfg_attr(not(bsd), doc = " ```rust,ignore")]
//! # use nix::sys::termios::{cfsetispeed, cfsetospeed, cfsetspeed, Termios};
//! # fn main() {
//! # let mut t: Termios = unsafe { std::mem::zeroed() };
//! cfsetispeed(&mut t, 9600u32);
//! cfsetospeed(&mut t, 9600u32);
//! cfsetspeed(&mut t, 9600u32);
//! # }
//! ```
use crate::errno::Errno;
use crate::Result;
use cfg_if::cfg_if;
use libc::{self, c_int, tcflag_t};
use std::cell::{Ref, RefCell};
use std::convert::From;
use std::mem;
use std::os::unix::io::{AsFd, AsRawFd};
#[cfg(feature = "process")]
use crate::unistd::Pid;
/// Stores settings for the termios API
///
/// This is a wrapper around the `libc::termios` struct that provides a safe interface for the
/// standard fields. The only safe way to obtain an instance of this struct is to extract it from
/// an open port using `tcgetattr()`.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Termios {
inner: RefCell<libc::termios>,
/// Input mode flags (see `termios.c_iflag` documentation)
pub input_flags: InputFlags,
/// Output mode flags (see `termios.c_oflag` documentation)
pub output_flags: OutputFlags,
/// Control mode flags (see `termios.c_cflag` documentation)
pub control_flags: ControlFlags,
/// Local mode flags (see `termios.c_lflag` documentation)
pub local_flags: LocalFlags,
/// Control characters (see `termios.c_cc` documentation)
pub control_chars: [libc::cc_t; NCCS],
/// Line discipline (see `termios.c_line` documentation)
#[cfg(linux_android)]
pub line_discipline: libc::cc_t,
/// Line discipline (see `termios.c_line` documentation)
#[cfg(target_os = "haiku")]
pub line_discipline: libc::c_char,
}
impl Termios {
/// Exposes an immutable reference to the underlying `libc::termios` data structure.
///
/// This is not part of `nix`'s public API because it requires additional work to maintain type
/// safety.
pub(crate) fn get_libc_termios(&self) -> Ref<libc::termios> {
{
let mut termios = self.inner.borrow_mut();
termios.c_iflag = self.input_flags.bits();
termios.c_oflag = self.output_flags.bits();
termios.c_cflag = self.control_flags.bits();
termios.c_lflag = self.local_flags.bits();
termios.c_cc = self.control_chars;
#[cfg(any(linux_android, target_os = "haiku"))]
{
termios.c_line = self.line_discipline;
}
}
self.inner.borrow()
}
/// Exposes the inner `libc::termios` datastore within `Termios`.
///
/// This is unsafe because if this is used to modify the inner `libc::termios` struct, it will
/// not automatically update the safe wrapper type around it. In this case it should also be
/// paired with a call to `update_wrapper()` so that the wrapper-type and internal
/// representation stay consistent.
pub(crate) unsafe fn get_libc_termios_mut(&mut self) -> *mut libc::termios {
{
let mut termios = self.inner.borrow_mut();
termios.c_iflag = self.input_flags.bits();
termios.c_oflag = self.output_flags.bits();
termios.c_cflag = self.control_flags.bits();
termios.c_lflag = self.local_flags.bits();
termios.c_cc = self.control_chars;
#[cfg(any(linux_android, target_os = "haiku"))]
{
termios.c_line = self.line_discipline;
}
}
self.inner.as_ptr()
}
/// Updates the wrapper values from the internal `libc::termios` data structure.
pub(crate) fn update_wrapper(&mut self) {
let termios = *self.inner.borrow_mut();
self.input_flags = InputFlags::from_bits_truncate(termios.c_iflag);
self.output_flags = OutputFlags::from_bits_truncate(termios.c_oflag);
self.control_flags = ControlFlags::from_bits_retain(termios.c_cflag);
self.local_flags = LocalFlags::from_bits_truncate(termios.c_lflag);
self.control_chars = termios.c_cc;
#[cfg(any(linux_android, target_os = "haiku"))]
{
self.line_discipline = termios.c_line;
}
}
}
impl From<libc::termios> for Termios {
fn from(termios: libc::termios) -> Self {
Termios {
inner: RefCell::new(termios),
input_flags: InputFlags::from_bits_truncate(termios.c_iflag),
output_flags: OutputFlags::from_bits_truncate(termios.c_oflag),
control_flags: ControlFlags::from_bits_truncate(termios.c_cflag),
local_flags: LocalFlags::from_bits_truncate(termios.c_lflag),
control_chars: termios.c_cc,
#[cfg(any(linux_android, target_os = "haiku"))]
line_discipline: termios.c_line,
}
}
}
impl From<Termios> for libc::termios {
fn from(termios: Termios) -> Self {
termios.inner.into_inner()
}
}
libc_enum! {
/// Baud rates supported by the system.
///
/// For the BSDs, arbitrary baud rates can be specified by using `u32`s directly instead of this
/// enum.
///
/// B0 is special and will disable the port.
#[cfg_attr(target_os = "haiku", repr(u8))]
#[cfg_attr(target_os = "hurd", repr(i32))]
#[cfg_attr(all(apple_targets, target_pointer_width = "64"), repr(u64))]
#[cfg_attr(all(
not(all(apple_targets, target_pointer_width = "64")),
not(target_os = "haiku"),
not(target_os = "hurd")
), repr(u32))]
#[non_exhaustive]
pub enum BaudRate {
B0,
B50,
B75,
B110,
B134,
B150,
B200,
B300,
B600,
B1200,
B1800,
B2400,
B4800,
#[cfg(bsd)]
B7200,
B9600,
#[cfg(bsd)]
B14400,
B19200,
#[cfg(bsd)]
B28800,
B38400,
#[cfg(not(target_os = "aix"))]
B57600,
#[cfg(bsd)]
B76800,
#[cfg(not(target_os = "aix"))]
B115200,
#[cfg(solarish)]
B153600,
#[cfg(not(target_os = "aix"))]
B230400,
#[cfg(solarish)]
B307200,
#[cfg(any(linux_android,
solarish,
target_os = "freebsd",
target_os = "netbsd"))]
B460800,
#[cfg(linux_android)]
B500000,
#[cfg(linux_android)]
B576000,
#[cfg(any(linux_android,
solarish,
target_os = "freebsd",
target_os = "netbsd"))]
B921600,
#[cfg(linux_android)]
B1000000,
#[cfg(linux_android)]
B1152000,
#[cfg(linux_android)]
B1500000,
#[cfg(linux_android)]
B2000000,
#[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
B2500000,
#[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
B3000000,
#[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
B3500000,
#[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
B4000000,
}
impl TryFrom<libc::speed_t>
}
#[cfg(bsd)]
impl From<BaudRate> for u32 {
fn from(b: BaudRate) -> u32 {
b as u32
}
}
#[cfg(target_os = "haiku")]
impl From<BaudRate> for u8 {
fn from(b: BaudRate) -> u8 {
b as u8
}
}
// TODO: Add TCSASOFT, which will require treating this as a bitfield.
libc_enum! {
/// Specify when a port configuration change should occur.
///
/// Used as an argument to `tcsetattr()`
#[repr(i32)]
#[non_exhaustive]
pub enum SetArg {
/// The change will occur immediately
TCSANOW,
/// The change occurs after all output has been written
TCSADRAIN,
/// Same as `TCSADRAIN`, but will also flush the input buffer
TCSAFLUSH,
}
}
libc_enum! {
/// Specify a combination of the input and output buffers to flush
///
/// Used as an argument to `tcflush()`.
#[repr(i32)]
#[non_exhaustive]
pub enum FlushArg {
/// Flush data that was received but not read
TCIFLUSH,
/// Flush data written but not transmitted
TCOFLUSH,
/// Flush both received data not read and written data not transmitted
TCIOFLUSH,
}
}
libc_enum! {
/// Specify how transmission flow should be altered
///
/// Used as an argument to `tcflow()`.
#[repr(i32)]
#[non_exhaustive]
pub enum FlowArg {
/// Suspend transmission
TCOOFF,
/// Resume transmission
TCOON,
/// Transmit a STOP character, which should disable a connected terminal device
TCIOFF,
/// Transmit a START character, which should re-enable a connected terminal device
TCION,
}
}
// TODO: Make this usable directly as a slice index.
libc_enum! {
/// Indices into the `termios.c_cc` array for special characters.
#[repr(usize)]
#[non_exhaustive]
pub enum SpecialCharacterIndices {
#[cfg(not(any(target_os = "aix", target_os = "haiku")))]
VDISCARD,
#[cfg(any(bsd,
solarish,
target_os = "aix"))]
VDSUSP,
VEOF,
VEOL,
VEOL2,
VERASE,
#[cfg(any(freebsdlike, target_os = "illumos"))]
VERASE2,
VINTR,
VKILL,
#[cfg(not(target_os = "haiku"))]
VLNEXT,
#[cfg(not(any(all(target_os = "linux", target_arch = "sparc64"),
solarish, target_os = "aix", target_os = "haiku")))]
VMIN,
VQUIT,
#[cfg(not(target_os = "haiku"))]
VREPRINT,
VSTART,
#[cfg(any(bsd, target_os = "illumos"))]
VSTATUS,
VSTOP,
VSUSP,
#[cfg(target_os = "linux")]
VSWTC,
#[cfg(any(solarish, target_os = "haiku"))]
VSWTCH,
#[cfg(not(any(all(target_os = "linux", target_arch = "sparc64"),
solarish, target_os = "aix", target_os = "haiku")))]
VTIME,
#[cfg(not(any(target_os = "aix", target_os = "haiku")))]
VWERASE,
#[cfg(target_os = "dragonfly")]
VCHECKPT,
}
}
#[cfg(any(
all(target_os = "linux", target_arch = "sparc64"),
solarish,
target_os = "aix",
target_os = "haiku",
))]
impl SpecialCharacterIndices {
pub const VMIN: SpecialCharacterIndices = SpecialCharacterIndices::VEOF;
pub const VTIME: SpecialCharacterIndices = SpecialCharacterIndices::VEOL;
}
pub use libc::NCCS;
#[cfg(any(bsd, linux_android, target_os = "aix", target_os = "solaris"))]
pub use libc::_POSIX_VDISABLE;
libc_bitflags! {
/// Flags for configuring the input mode of a terminal
pub struct InputFlags: tcflag_t {
IGNBRK;
BRKINT;
IGNPAR;
PARMRK;
INPCK;
ISTRIP;
INLCR;
IGNCR;
ICRNL;
IXON;
IXOFF;
#[cfg(not(target_os = "redox"))]
IXANY;
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
IMAXBEL;
#[cfg(any(linux_android, apple_targets))]
IUTF8;
}
}
libc_bitflags! {
/// Flags for configuring the output mode of a terminal
pub struct OutputFlags: tcflag_t {
OPOST;
#[cfg(any(linux_android,
target_os = "haiku",
target_os = "openbsd"))]
OLCUC;
ONLCR;
OCRNL as tcflag_t;
ONOCR as tcflag_t;
ONLRET as tcflag_t;
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
OFDEL as tcflag_t;
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
NL0 as tcflag_t;
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
NL1 as tcflag_t;
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
CR0 as tcflag_t;
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
CR1 as tcflag_t;
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
CR2 as tcflag_t;
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
CR3 as tcflag_t;
#[cfg(any(linux_android,
target_os = "freebsd",
target_os = "haiku",
apple_targets))]
TAB0 as tcflag_t;
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
TAB1 as tcflag_t;
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
TAB2 as tcflag_t;
#[cfg(any(linux_android,
target_os = "freebsd",
target_os = "haiku",
apple_targets))]
TAB3 as tcflag_t;
#[cfg(linux_android)]
XTABS;
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
BS0 as tcflag_t;
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
BS1 as tcflag_t;
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
VT0 as tcflag_t;
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
VT1 as tcflag_t;
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
FF0 as tcflag_t;
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
FF1 as tcflag_t;
#[cfg(bsd)]
OXTABS;
#[cfg(bsd)]
ONOEOT as tcflag_t;
// Bitmasks for use with OutputFlags to select specific settings
// These should be moved to be a mask once https://github.com/rust-lang-nursery/bitflags/issues/110
// is resolved.
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
NLDLY as tcflag_t; // FIXME: Datatype needs to be corrected in libc for mac
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
CRDLY as tcflag_t;
#[cfg(any(linux_android,
target_os = "freebsd",
target_os = "haiku",
apple_targets))]
TABDLY as tcflag_t;
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
BSDLY as tcflag_t;
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
VTDLY as tcflag_t;
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
FFDLY as tcflag_t;
}
}
libc_bitflags! {
/// Flags for setting the control mode of a terminal
pub struct ControlFlags: tcflag_t {
#[cfg(bsd)]
CIGNORE;
CS5;
CS6;
CS7;
CS8;
CSTOPB;
CREAD;
PARENB;
PARODD;
HUPCL;
CLOCAL;
#[cfg(not(any(target_os = "redox", target_os = "aix")))]
CRTSCTS;
#[cfg(linux_android)]
CBAUD;
#[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "mips"))))]
CMSPAR;
#[cfg(any(target_os = "android",
all(target_os = "linux",
not(any(target_arch = "powerpc", target_arch = "powerpc64")))))]
CIBAUD;
#[cfg(linux_android)]
CBAUDEX;
#[cfg(bsd)]
MDMBUF;
#[cfg(netbsdlike)]
CHWFLOW;
#[cfg(any(freebsdlike, netbsdlike))]
CCTS_OFLOW;
#[cfg(any(freebsdlike, netbsdlike))]
CRTS_IFLOW;
#[cfg(freebsdlike)]
CDTR_IFLOW;
#[cfg(freebsdlike)]
CDSR_OFLOW;
#[cfg(freebsdlike)]
CCAR_OFLOW;
// Bitmasks for use with ControlFlags to select specific settings
// These should be moved to be a mask once https://github.com/rust-lang-nursery/bitflags/issues/110
// is resolved.
CSIZE;
}
}
libc_bitflags! {
/// Flags for setting any local modes
pub struct LocalFlags: tcflag_t {
#[cfg(not(target_os = "redox"))]
ECHOKE;
ECHOE;
ECHOK;
ECHO;
ECHONL;
#[cfg(not(any(target_os = "redox", target_os = "cygwin")))]
ECHOPRT;
#[cfg(not(target_os = "redox"))]
ECHOCTL;
ISIG;
ICANON;
#[cfg(bsd)]
ALTWERASE;
IEXTEN;
#[cfg(not(any(target_os = "redox", target_os = "haiku", target_os = "aix", target_os = "cygwin")))]
EXTPROC;
TOSTOP;
#[cfg(not(target_os = "redox"))]
FLUSHO;
#[cfg(bsd)]
NOKERNINFO;
#[cfg(not(any(target_os = "redox", target_os = "cygwin")))]
PENDIN;
NOFLSH;
}
}
cfg_if! {
if #[cfg(bsd)] {
/// Get input baud rate (see
/// [cfgetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetispeed.html)).
///
/// `cfgetispeed()` extracts the input baud rate from the given `Termios` structure.
// The cast is not unnecessary on all platforms.
#[allow(clippy::unnecessary_cast)]
pub fn cfgetispeed(termios: &Termios) -> u32 {
let inner_termios = termios.get_libc_termios();
unsafe { libc::cfgetispeed(&*inner_termios) as u32 }
}
/// Get output baud rate (see
/// [cfgetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetospeed.html)).
///
/// `cfgetospeed()` extracts the output baud rate from the given `Termios` structure.
// The cast is not unnecessary on all platforms.
#[allow(clippy::unnecessary_cast)]
pub fn cfgetospeed(termios: &Termios) -> u32 {
let inner_termios = termios.get_libc_termios();
unsafe { libc::cfgetospeed(&*inner_termios) as u32 }
}
/// Set input baud rate (see
/// [cfsetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetispeed.html)).
///
/// `cfsetispeed()` sets the intput baud rate in the given `Termios` structure.
pub fn cfsetispeed<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> {
let inner_termios = unsafe { termios.get_libc_termios_mut() };
let res = unsafe { libc::cfsetispeed(inner_termios, baud.into() as libc::speed_t) };
termios.update_wrapper();
Errno::result(res).map(drop)
}
/// Set output baud rate (see
/// [cfsetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetospeed.html)).
///
/// `cfsetospeed()` sets the output baud rate in the given termios structure.
pub fn cfsetospeed<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> {
let inner_termios = unsafe { termios.get_libc_termios_mut() };
let res = unsafe { libc::cfsetospeed(inner_termios, baud.into() as libc::speed_t) };
termios.update_wrapper();
Errno::result(res).map(drop)
}
/// Set both the input and output baud rates (see
/// [termios(3)](https://www.freebsd.org/cgi/man.cgi?query=cfsetspeed)).
///
/// `cfsetspeed()` sets the input and output baud rate in the given termios structure. Note that
/// this is part of the 4.4BSD standard and not part of POSIX.
pub fn cfsetspeed<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> {
let inner_termios = unsafe { termios.get_libc_termios_mut() };
let res = unsafe { libc::cfsetspeed(inner_termios, baud.into() as libc::speed_t) };
termios.update_wrapper();
Errno::result(res).map(drop)
}
} else {
use std::convert::TryInto;
/// Get input baud rate (see
/// [cfgetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetispeed.html)).
///
/// `cfgetispeed()` extracts the input baud rate from the given `Termios` structure.
pub fn cfgetispeed(termios: &Termios) -> BaudRate {
let inner_termios = termios.get_libc_termios();
unsafe { libc::cfgetispeed(&*inner_termios) }.try_into().unwrap()
}
/// Get output baud rate (see
/// [cfgetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetospeed.html)).
///
/// `cfgetospeed()` extracts the output baud rate from the given `Termios` structure.
pub fn cfgetospeed(termios: &Termios) -> BaudRate {
let inner_termios = termios.get_libc_termios();
unsafe { libc::cfgetospeed(&*inner_termios) }.try_into().unwrap()
}
/// Set input baud rate (see
/// [cfsetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetispeed.html)).
///
/// `cfsetispeed()` sets the intput baud rate in the given `Termios` structure.
pub fn cfsetispeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
let inner_termios = unsafe { termios.get_libc_termios_mut() };
let res = unsafe { libc::cfsetispeed(inner_termios, baud as libc::speed_t) };
termios.update_wrapper();
Errno::result(res).map(drop)
}
/// Set output baud rate (see
/// [cfsetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetospeed.html)).
///
/// `cfsetospeed()` sets the output baud rate in the given `Termios` structure.
pub fn cfsetospeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
let inner_termios = unsafe { termios.get_libc_termios_mut() };
let res = unsafe { libc::cfsetospeed(inner_termios, baud as libc::speed_t) };
termios.update_wrapper();
Errno::result(res).map(drop)
}
/// Set both the input and output baud rates (see
/// [termios(3)](https://www.freebsd.org/cgi/man.cgi?query=cfsetspeed)).
///
/// `cfsetspeed()` sets the input and output baud rate in the given `Termios` structure. Note that
/// this is part of the 4.4BSD standard and not part of POSIX.
#[cfg(not(target_os = "haiku"))]
pub fn cfsetspeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
let inner_termios = unsafe { termios.get_libc_termios_mut() };
let res = unsafe { libc::cfsetspeed(inner_termios, baud as libc::speed_t) };
termios.update_wrapper();
Errno::result(res).map(drop)
}
}
}
/// Configures the port to something like the "raw" mode of the old Version 7 terminal driver (see
/// [termios(3)](https://man7.org/linux/man-pages/man3/termios.3.html)).
///
/// `cfmakeraw()` configures the termios structure such that input is available character-by-
/// character, echoing is disabled, and all special input and output processing is disabled. Note
/// that this is a non-standard function, but is available on Linux and BSDs.
pub fn cfmakeraw(termios: &mut Termios) {
let inner_termios = unsafe { termios.get_libc_termios_mut() };
unsafe {
libc::cfmakeraw(inner_termios);
}
termios.update_wrapper();
}
/// Configures the port to "sane" mode (like the configuration of a newly created terminal) (see
/// [tcsetattr(3)](https://www.freebsd.org/cgi/man.cgi?query=tcsetattr)).
///
/// Note that this is a non-standard function, available on FreeBSD.
#[cfg(target_os = "freebsd")]
pub fn cfmakesane(termios: &mut Termios) {
let inner_termios = unsafe { termios.get_libc_termios_mut() };
unsafe {
libc::cfmakesane(inner_termios);
}
termios.update_wrapper();
}
/// Return the configuration of a port
/// [tcgetattr(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetattr.html)).
///
/// `tcgetattr()` returns a `Termios` structure with the current configuration for a port. Modifying
/// this structure *will not* reconfigure the port, instead the modifications should be done to
/// the `Termios` structure and then the port should be reconfigured using `tcsetattr()`.
pub fn tcgetattr<Fd: AsFd>(fd: Fd) -> Result<Termios> {
let mut termios = mem::MaybeUninit::uninit();
let res = unsafe {
libc::tcgetattr(fd.as_fd().as_raw_fd(), termios.as_mut_ptr())
};
Errno::result(res)?;
unsafe { Ok(termios.assume_init().into()) }
}
/// Set the configuration for a terminal (see
/// [tcsetattr(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsetattr.html)).
///
/// `tcsetattr()` reconfigures the given port based on a given `Termios` structure. This change
/// takes affect at a time specified by `actions`. Note that this function may return success if
/// *any* of the parameters were successfully set, not only if all were set successfully.
pub fn tcsetattr<Fd: AsFd>(
fd: Fd,
actions: SetArg,
termios: &Termios,
) -> Result<()> {
let inner_termios = termios.get_libc_termios();
Errno::result(unsafe {
libc::tcsetattr(
fd.as_fd().as_raw_fd(),
actions as c_int,
&*inner_termios,
)
})
.map(drop)
}
/// Block until all output data is written (see
/// [tcdrain(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcdrain.html)).
pub fn tcdrain<Fd: AsFd>(fd: Fd) -> Result<()> {
Errno::result(unsafe { libc::tcdrain(fd.as_fd().as_raw_fd()) }).map(drop)
}
/// Suspend or resume the transmission or reception of data (see
/// [tcflow(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcflow.html)).
///
/// `tcflow()` suspends of resumes the transmission or reception of data for the given port
/// depending on the value of `action`.
pub fn tcflow<Fd: AsFd>(fd: Fd, action: FlowArg) -> Result<()> {
Errno::result(unsafe {
libc::tcflow(fd.as_fd().as_raw_fd(), action as c_int)
})
.map(drop)
}
/// Discard data in the output or input queue (see
/// [tcflush(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcflush.html)).
///
/// `tcflush()` will discard data for a terminal port in the input queue, output queue, or both
/// depending on the value of `action`.
pub fn tcflush<Fd: AsFd>(fd: Fd, action: FlushArg) -> Result<()> {
Errno::result(unsafe {
libc::tcflush(fd.as_fd().as_raw_fd(), action as c_int)
})
.map(drop)
}
/// Send a break for a specific duration (see
/// [tcsendbreak(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsendbreak.html)).
///
/// When using asynchronous data transmission `tcsendbreak()` will transmit a continuous stream
/// of zero-valued bits for an implementation-defined duration.
pub fn tcsendbreak<Fd: AsFd>(fd: Fd, duration: c_int) -> Result<()> {
Errno::result(unsafe {
libc::tcsendbreak(fd.as_fd().as_raw_fd(), duration)
})
.map(drop)
}
feature! {
#![feature = "process"]
/// Get the session controlled by the given terminal (see
/// [tcgetsid(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetsid.html)).
pub fn tcgetsid<Fd: AsFd>(fd: Fd) -> Result<Pid> {
let res = unsafe { libc::tcgetsid(fd.as_fd().as_raw_fd()) };
Errno::result(res).map(Pid::from_raw)
}
}

752
vendor/nix/src/sys/time.rs vendored Normal file
View File

@@ -0,0 +1,752 @@
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)]
// https://github.com/rust-lang/libc/issues/1848
pub use libc::{suseconds_t, time_t};
use libc::{timespec, timeval};
use std::time::Duration;
use std::{cmp, fmt, ops};
const fn zero_init_timespec() -> timespec {
// `std::mem::MaybeUninit::zeroed()` is not yet a const fn
// (https://github.com/rust-lang/rust/issues/91850) so we will instead initialize an array of
// the appropriate size to zero and then transmute it to a timespec value.
unsafe { std::mem::transmute([0u8; std::mem::size_of::<timespec>()]) }
}
#[cfg(any(
all(feature = "time", any(target_os = "android", target_os = "linux")),
all(
any(
target_os = "freebsd",
solarish,
target_os = "linux",
target_os = "netbsd"
),
feature = "time",
feature = "signal"
)
))]
pub(crate) mod timer {
use crate::sys::time::{zero_init_timespec, TimeSpec};
use bitflags::bitflags;
#[derive(Debug, Clone, Copy)]
pub(crate) struct TimerSpec(libc::itimerspec);
impl TimerSpec {
pub const fn none() -> Self {
Self(libc::itimerspec {
it_interval: zero_init_timespec(),
it_value: zero_init_timespec(),
})
}
}
impl AsMut<libc::itimerspec> for TimerSpec {
fn as_mut(&mut self) -> &mut libc::itimerspec {
&mut self.0
}
}
impl AsRef<libc::itimerspec> for TimerSpec {
fn as_ref(&self) -> &libc::itimerspec {
&self.0
}
}
impl From<Expiration> for TimerSpec {
fn from(expiration: Expiration) -> TimerSpec {
match expiration {
Expiration::OneShot(t) => TimerSpec(libc::itimerspec {
it_interval: zero_init_timespec(),
it_value: *t.as_ref(),
}),
Expiration::IntervalDelayed(start, interval) => {
TimerSpec(libc::itimerspec {
it_interval: *interval.as_ref(),
it_value: *start.as_ref(),
})
}
Expiration::Interval(t) => TimerSpec(libc::itimerspec {
it_interval: *t.as_ref(),
it_value: *t.as_ref(),
}),
}
}
}
/// An enumeration allowing the definition of the expiration time of an alarm,
/// recurring or not.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum Expiration {
/// Alarm will trigger once after the time given in `TimeSpec`
OneShot(TimeSpec),
/// Alarm will trigger after a specified delay and then every interval of
/// time.
IntervalDelayed(TimeSpec, TimeSpec),
/// Alarm will trigger every specified interval of time.
Interval(TimeSpec),
}
#[cfg(linux_android)]
bitflags! {
/// Flags that are used for arming the timer.
#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct TimerSetTimeFlags: libc::c_int {
const TFD_TIMER_ABSTIME = libc::TFD_TIMER_ABSTIME;
const TFD_TIMER_CANCEL_ON_SET = libc::TFD_TIMER_CANCEL_ON_SET;
}
}
#[cfg(any(freebsdlike, target_os = "netbsd", solarish))]
bitflags! {
/// Flags that are used for arming the timer.
#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct TimerSetTimeFlags: libc::c_int {
const TFD_TIMER_ABSTIME = libc::TIMER_ABSTIME;
}
}
impl From<TimerSpec> for Expiration {
fn from(timerspec: TimerSpec) -> Expiration {
match timerspec {
TimerSpec(libc::itimerspec {
it_interval:
libc::timespec {
tv_sec: 0,
tv_nsec: 0,
..
},
it_value: ts,
}) => Expiration::OneShot(ts.into()),
TimerSpec(libc::itimerspec {
it_interval: int_ts,
it_value: val_ts,
}) => {
if (int_ts.tv_sec == val_ts.tv_sec)
&& (int_ts.tv_nsec == val_ts.tv_nsec)
{
Expiration::Interval(int_ts.into())
} else {
Expiration::IntervalDelayed(
val_ts.into(),
int_ts.into(),
)
}
}
}
}
}
}
pub trait TimeValLike: Sized {
#[inline]
fn zero() -> Self {
Self::seconds(0)
}
#[inline]
fn hours(hours: i64) -> Self {
let secs = hours
.checked_mul(SECS_PER_HOUR)
.expect("TimeValLike::hours ouf of bounds");
Self::seconds(secs)
}
#[inline]
fn minutes(minutes: i64) -> Self {
let secs = minutes
.checked_mul(SECS_PER_MINUTE)
.expect("TimeValLike::minutes out of bounds");
Self::seconds(secs)
}
fn seconds(seconds: i64) -> Self;
fn milliseconds(milliseconds: i64) -> Self;
fn microseconds(microseconds: i64) -> Self;
fn nanoseconds(nanoseconds: i64) -> Self;
#[inline]
fn num_hours(&self) -> i64 {
self.num_seconds() / 3600
}
#[inline]
fn num_minutes(&self) -> i64 {
self.num_seconds() / 60
}
fn num_seconds(&self) -> i64;
fn num_milliseconds(&self) -> i64;
fn num_microseconds(&self) -> i64;
fn num_nanoseconds(&self) -> i64;
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct TimeSpec(timespec);
const NANOS_PER_SEC: i64 = 1_000_000_000;
const SECS_PER_MINUTE: i64 = 60;
const SECS_PER_HOUR: i64 = 3600;
#[cfg(target_pointer_width = "64")]
const TS_MAX_SECONDS: i64 = (i64::MAX / NANOS_PER_SEC) - 1;
#[cfg(target_pointer_width = "32")]
const TS_MAX_SECONDS: i64 = isize::MAX as i64;
const TS_MIN_SECONDS: i64 = -TS_MAX_SECONDS;
// x32 compatibility
// See https://sourceware.org/bugzilla/show_bug.cgi?id=16437
#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
type timespec_tv_nsec_t = i64;
#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
type timespec_tv_nsec_t = libc::c_long;
impl From<timespec> for TimeSpec {
fn from(ts: timespec) -> Self {
Self(ts)
}
}
impl From<Duration> for TimeSpec {
fn from(duration: Duration) -> Self {
Self::from_duration(duration)
}
}
impl From<TimeSpec> for Duration {
fn from(timespec: TimeSpec) -> Self {
Duration::new(timespec.0.tv_sec as u64, timespec.0.tv_nsec as u32)
}
}
impl AsRef<timespec> for TimeSpec {
fn as_ref(&self) -> &timespec {
&self.0
}
}
impl AsMut<timespec> for TimeSpec {
fn as_mut(&mut self) -> &mut timespec {
&mut self.0
}
}
impl Ord for TimeSpec {
// The implementation of cmp is simplified by assuming that the struct is
// normalized. That is, tv_nsec must always be within [0, 1_000_000_000)
fn cmp(&self, other: &TimeSpec) -> cmp::Ordering {
if self.tv_sec() == other.tv_sec() {
self.tv_nsec().cmp(&other.tv_nsec())
} else {
self.tv_sec().cmp(&other.tv_sec())
}
}
}
impl PartialOrd for TimeSpec {
fn partial_cmp(&self, other: &TimeSpec) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl TimeValLike for TimeSpec {
#[inline]
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)]
// https://github.com/rust-lang/libc/issues/1848
fn seconds(seconds: i64) -> TimeSpec {
assert!(
(TS_MIN_SECONDS..=TS_MAX_SECONDS).contains(&seconds),
"TimeSpec out of bounds; seconds={seconds}",
);
let mut ts = zero_init_timespec();
ts.tv_sec = seconds as time_t;
TimeSpec(ts)
}
#[inline]
fn milliseconds(milliseconds: i64) -> TimeSpec {
let nanoseconds = milliseconds
.checked_mul(1_000_000)
.expect("TimeSpec::milliseconds out of bounds");
TimeSpec::nanoseconds(nanoseconds)
}
/// Makes a new `TimeSpec` with given number of microseconds.
#[inline]
fn microseconds(microseconds: i64) -> TimeSpec {
let nanoseconds = microseconds
.checked_mul(1_000)
.expect("TimeSpec::milliseconds out of bounds");
TimeSpec::nanoseconds(nanoseconds)
}
/// Makes a new `TimeSpec` with given number of nanoseconds.
#[inline]
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)]
// https://github.com/rust-lang/libc/issues/1848
fn nanoseconds(nanoseconds: i64) -> TimeSpec {
let (secs, nanos) = div_mod_floor_64(nanoseconds, NANOS_PER_SEC);
assert!(
(TS_MIN_SECONDS..=TS_MAX_SECONDS).contains(&secs),
"TimeSpec out of bounds"
);
let mut ts = zero_init_timespec();
ts.tv_sec = secs as time_t;
ts.tv_nsec = nanos as timespec_tv_nsec_t;
TimeSpec(ts)
}
// The cast is not unnecessary on all platforms.
#[allow(clippy::unnecessary_cast)]
fn num_seconds(&self) -> i64 {
if self.tv_sec() < 0 && self.tv_nsec() > 0 {
(self.tv_sec() + 1) as i64
} else {
self.tv_sec() as i64
}
}
fn num_milliseconds(&self) -> i64 {
self.num_nanoseconds() / 1_000_000
}
fn num_microseconds(&self) -> i64 {
self.num_nanoseconds() / 1_000
}
// The cast is not unnecessary on all platforms.
#[allow(clippy::unnecessary_cast)]
fn num_nanoseconds(&self) -> i64 {
let secs = self.num_seconds() * 1_000_000_000;
let nsec = self.nanos_mod_sec();
secs + nsec as i64
}
}
impl TimeSpec {
/// Leave the timestamp unchanged.
#[cfg(not(target_os = "redox"))]
// At the time of writing this PR, redox does not support this feature
pub const UTIME_OMIT: TimeSpec =
TimeSpec::new(0, libc::UTIME_OMIT as timespec_tv_nsec_t);
/// Update the timestamp to `Now`
// At the time of writing this PR, redox does not support this feature
#[cfg(not(target_os = "redox"))]
pub const UTIME_NOW: TimeSpec =
TimeSpec::new(0, libc::UTIME_NOW as timespec_tv_nsec_t);
/// Construct a new `TimeSpec` from its components
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)] // https://github.com/rust-lang/libc/issues/1848
pub const fn new(seconds: time_t, nanoseconds: timespec_tv_nsec_t) -> Self {
let mut ts = zero_init_timespec();
ts.tv_sec = seconds;
ts.tv_nsec = nanoseconds;
Self(ts)
}
fn nanos_mod_sec(&self) -> timespec_tv_nsec_t {
if self.tv_sec() < 0 && self.tv_nsec() > 0 {
self.tv_nsec() - NANOS_PER_SEC as timespec_tv_nsec_t
} else {
self.tv_nsec()
}
}
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)] // https://github.com/rust-lang/libc/issues/1848
pub const fn tv_sec(&self) -> time_t {
self.0.tv_sec
}
pub const fn tv_nsec(&self) -> timespec_tv_nsec_t {
self.0.tv_nsec
}
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)]
// https://github.com/rust-lang/libc/issues/1848
pub const fn from_duration(duration: Duration) -> Self {
let mut ts = zero_init_timespec();
ts.tv_sec = duration.as_secs() as time_t;
ts.tv_nsec = duration.subsec_nanos() as timespec_tv_nsec_t;
TimeSpec(ts)
}
pub const fn from_timespec(timespec: timespec) -> Self {
Self(timespec)
}
}
impl ops::Neg for TimeSpec {
type Output = TimeSpec;
fn neg(self) -> TimeSpec {
TimeSpec::nanoseconds(-self.num_nanoseconds())
}
}
impl ops::Add for TimeSpec {
type Output = TimeSpec;
fn add(self, rhs: TimeSpec) -> TimeSpec {
TimeSpec::nanoseconds(self.num_nanoseconds() + rhs.num_nanoseconds())
}
}
impl ops::Sub for TimeSpec {
type Output = TimeSpec;
fn sub(self, rhs: TimeSpec) -> TimeSpec {
TimeSpec::nanoseconds(self.num_nanoseconds() - rhs.num_nanoseconds())
}
}
impl ops::Mul<i32> for TimeSpec {
type Output = TimeSpec;
fn mul(self, rhs: i32) -> TimeSpec {
let usec = self
.num_nanoseconds()
.checked_mul(i64::from(rhs))
.expect("TimeSpec multiply out of bounds");
TimeSpec::nanoseconds(usec)
}
}
impl ops::Div<i32> for TimeSpec {
type Output = TimeSpec;
fn div(self, rhs: i32) -> TimeSpec {
let usec = self.num_nanoseconds() / i64::from(rhs);
TimeSpec::nanoseconds(usec)
}
}
impl fmt::Display for TimeSpec {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let (abs, sign) = if self.tv_sec() < 0 {
(-*self, "-")
} else {
(*self, "")
};
let sec = abs.tv_sec();
write!(f, "{sign}")?;
if abs.tv_nsec() == 0 {
if sec == 1 {
write!(f, "1 second")?;
} else {
write!(f, "{sec} seconds")?;
}
} else if abs.tv_nsec() % 1_000_000 == 0 {
write!(f, "{sec}.{:03} seconds", abs.tv_nsec() / 1_000_000)?;
} else if abs.tv_nsec() % 1_000 == 0 {
write!(f, "{sec}.{:06} seconds", abs.tv_nsec() / 1_000)?;
} else {
write!(f, "{sec}.{:09} seconds", abs.tv_nsec())?;
}
Ok(())
}
}
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct TimeVal(timeval);
const MICROS_PER_SEC: i64 = 1_000_000;
#[cfg(target_pointer_width = "64")]
const TV_MAX_SECONDS: i64 = (i64::MAX / MICROS_PER_SEC) - 1;
#[cfg(target_pointer_width = "32")]
const TV_MAX_SECONDS: i64 = isize::MAX as i64;
const TV_MIN_SECONDS: i64 = -TV_MAX_SECONDS;
impl AsRef<timeval> for TimeVal {
fn as_ref(&self) -> &timeval {
&self.0
}
}
impl AsMut<timeval> for TimeVal {
fn as_mut(&mut self) -> &mut timeval {
&mut self.0
}
}
impl Ord for TimeVal {
// The implementation of cmp is simplified by assuming that the struct is
// normalized. That is, tv_usec must always be within [0, 1_000_000)
fn cmp(&self, other: &TimeVal) -> cmp::Ordering {
if self.tv_sec() == other.tv_sec() {
self.tv_usec().cmp(&other.tv_usec())
} else {
self.tv_sec().cmp(&other.tv_sec())
}
}
}
impl PartialOrd for TimeVal {
fn partial_cmp(&self, other: &TimeVal) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl TimeValLike for TimeVal {
#[inline]
fn seconds(seconds: i64) -> TimeVal {
assert!(
(TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&seconds),
"TimeVal out of bounds; seconds={seconds}"
);
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)]
// https://github.com/rust-lang/libc/issues/1848
TimeVal(timeval {
tv_sec: seconds as time_t,
tv_usec: 0,
})
}
#[inline]
fn milliseconds(milliseconds: i64) -> TimeVal {
let microseconds = milliseconds
.checked_mul(1_000)
.expect("TimeVal::milliseconds out of bounds");
TimeVal::microseconds(microseconds)
}
/// Makes a new `TimeVal` with given number of microseconds.
#[inline]
fn microseconds(microseconds: i64) -> TimeVal {
let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC);
assert!(
(TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&secs),
"TimeVal out of bounds"
);
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)]
// https://github.com/rust-lang/libc/issues/1848
TimeVal(timeval {
tv_sec: secs as time_t,
tv_usec: micros as suseconds_t,
})
}
/// Makes a new `TimeVal` with given number of nanoseconds. Some precision
/// will be lost
#[inline]
fn nanoseconds(nanoseconds: i64) -> TimeVal {
let microseconds = nanoseconds / 1000;
let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC);
assert!(
(TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&secs),
"TimeVal out of bounds"
);
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)]
// https://github.com/rust-lang/libc/issues/1848
TimeVal(timeval {
tv_sec: secs as time_t,
tv_usec: micros as suseconds_t,
})
}
// The cast is not unnecessary on all platforms.
#[allow(clippy::unnecessary_cast)]
fn num_seconds(&self) -> i64 {
if self.tv_sec() < 0 && self.tv_usec() > 0 {
(self.tv_sec() + 1) as i64
} else {
self.tv_sec() as i64
}
}
fn num_milliseconds(&self) -> i64 {
self.num_microseconds() / 1_000
}
// The cast is not unnecessary on all platforms.
#[allow(clippy::unnecessary_cast)]
fn num_microseconds(&self) -> i64 {
let secs = self.num_seconds() * 1_000_000;
let usec = self.micros_mod_sec();
secs + usec as i64
}
fn num_nanoseconds(&self) -> i64 {
self.num_microseconds() * 1_000
}
}
impl TimeVal {
/// Construct a new `TimeVal` from its components
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)] // https://github.com/rust-lang/libc/issues/1848
pub const fn new(seconds: time_t, microseconds: suseconds_t) -> Self {
Self(timeval {
tv_sec: seconds,
tv_usec: microseconds,
})
}
fn micros_mod_sec(&self) -> suseconds_t {
if self.tv_sec() < 0 && self.tv_usec() > 0 {
self.tv_usec() - MICROS_PER_SEC as suseconds_t
} else {
self.tv_usec()
}
}
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)] // https://github.com/rust-lang/libc/issues/1848
pub const fn tv_sec(&self) -> time_t {
self.0.tv_sec
}
pub const fn tv_usec(&self) -> suseconds_t {
self.0.tv_usec
}
}
impl ops::Neg for TimeVal {
type Output = TimeVal;
fn neg(self) -> TimeVal {
TimeVal::microseconds(-self.num_microseconds())
}
}
impl ops::Add for TimeVal {
type Output = TimeVal;
fn add(self, rhs: TimeVal) -> TimeVal {
TimeVal::microseconds(self.num_microseconds() + rhs.num_microseconds())
}
}
impl ops::Sub for TimeVal {
type Output = TimeVal;
fn sub(self, rhs: TimeVal) -> TimeVal {
TimeVal::microseconds(self.num_microseconds() - rhs.num_microseconds())
}
}
impl ops::Mul<i32> for TimeVal {
type Output = TimeVal;
fn mul(self, rhs: i32) -> TimeVal {
let usec = self
.num_microseconds()
.checked_mul(i64::from(rhs))
.expect("TimeVal multiply out of bounds");
TimeVal::microseconds(usec)
}
}
impl ops::Div<i32> for TimeVal {
type Output = TimeVal;
fn div(self, rhs: i32) -> TimeVal {
let usec = self.num_microseconds() / i64::from(rhs);
TimeVal::microseconds(usec)
}
}
impl fmt::Display for TimeVal {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let (abs, sign) = if self.tv_sec() < 0 {
(-*self, "-")
} else {
(*self, "")
};
let sec = abs.tv_sec();
write!(f, "{sign}")?;
if abs.tv_usec() == 0 {
if sec == 1 {
write!(f, "1 second")?;
} else {
write!(f, "{sec} seconds")?;
}
} else if abs.tv_usec() % 1000 == 0 {
write!(f, "{sec}.{:03} seconds", abs.tv_usec() / 1000)?;
} else {
write!(f, "{sec}.{:06} seconds", abs.tv_usec())?;
}
Ok(())
}
}
impl From<timeval> for TimeVal {
fn from(tv: timeval) -> Self {
TimeVal(tv)
}
}
#[inline]
fn div_mod_floor_64(this: i64, other: i64) -> (i64, i64) {
(div_floor_64(this, other), mod_floor_64(this, other))
}
#[inline]
fn div_floor_64(this: i64, other: i64) -> i64 {
match div_rem_64(this, other) {
(d, r) if (r > 0 && other < 0) || (r < 0 && other > 0) => d - 1,
(d, _) => d,
}
}
#[inline]
fn mod_floor_64(this: i64, other: i64) -> i64 {
match this % other {
r if (r > 0 && other < 0) || (r < 0 && other > 0) => r + other,
r => r,
}
}
#[inline]
fn div_rem_64(this: i64, other: i64) -> (i64, i64) {
(this / other, this % other)
}

187
vendor/nix/src/sys/timer.rs vendored Normal file
View File

@@ -0,0 +1,187 @@
//! Timer API via signals.
//!
//! Timer is a POSIX API to create timers and get expiration notifications
//! through queued Unix signals, for the current process. This is similar to
//! Linux's timerfd mechanism, except that API is specific to Linux and makes
//! use of file polling.
//!
//! For more documentation, please read [timer_create](https://pubs.opengroup.org/onlinepubs/9699919799/functions/timer_create.html).
//!
//! # Examples
//!
//! Create an interval timer that signals SIGALARM every 250 milliseconds.
//!
//! ```no_run
//! use nix::sys::signal::{self, SigEvent, SigHandler, SigevNotify, Signal};
//! use nix::sys::timer::{Expiration, Timer, TimerSetTimeFlags};
//! use nix::time::ClockId;
//! use std::convert::TryFrom;
//! use std::sync::atomic::{AtomicU64, Ordering};
//! use std::thread::yield_now;
//! use std::time::Duration;
//!
//! const SIG: Signal = Signal::SIGALRM;
//! static ALARMS: AtomicU64 = AtomicU64::new(0);
//!
//! extern "C" fn handle_alarm(signal: libc::c_int) {
//! let signal = Signal::try_from(signal).unwrap();
//! if signal == SIG {
//! ALARMS.fetch_add(1, Ordering::Relaxed);
//! }
//! }
//!
//! fn main() {
//! let clockid = ClockId::CLOCK_MONOTONIC;
//! let sigevent = SigEvent::new(SigevNotify::SigevSignal {
//! signal: SIG,
//! si_value: 0,
//! });
//!
//! let mut timer = Timer::new(clockid, sigevent).unwrap();
//! let expiration = Expiration::Interval(Duration::from_millis(250).into());
//! let flags = TimerSetTimeFlags::empty();
//! timer.set(expiration, flags).expect("could not set timer");
//!
//! let handler = SigHandler::Handler(handle_alarm);
//! unsafe { signal::signal(SIG, handler) }.unwrap();
//!
//! loop {
//! let alarms = ALARMS.load(Ordering::Relaxed);
//! if alarms >= 10 {
//! println!("total alarms handled: {}", alarms);
//! break;
//! }
//! yield_now()
//! }
//! }
//! ```
use crate::sys::signal::SigEvent;
use crate::sys::time::timer::TimerSpec;
pub use crate::sys::time::timer::{Expiration, TimerSetTimeFlags};
use crate::time::ClockId;
use crate::{errno::Errno, Result};
use core::mem;
/// A Unix signal per-process timer.
#[derive(Debug)]
#[repr(transparent)]
pub struct Timer(libc::timer_t);
impl Timer {
/// Creates a new timer based on the clock defined by `clockid`. The details
/// of the signal and its handler are defined by the passed `sigevent`.
#[doc(alias("timer_create"))]
pub fn new(clockid: ClockId, mut sigevent: SigEvent) -> Result<Self> {
let mut timer_id: mem::MaybeUninit<libc::timer_t> =
mem::MaybeUninit::uninit();
Errno::result(unsafe {
libc::timer_create(
clockid.as_raw(),
sigevent.as_mut_ptr(),
timer_id.as_mut_ptr(),
)
})
.map(|_| {
// SAFETY: libc::timer_create is responsible for initializing
// timer_id.
unsafe { Self(timer_id.assume_init()) }
})
}
/// Set a new alarm on the timer.
///
/// # Types of alarm
///
/// There are 3 types of alarms you can set:
///
/// - one shot: the alarm will trigger once after the specified amount of
/// time.
/// Example: I want an alarm to go off in 60s and then disable itself.
///
/// - interval: the alarm will trigger every specified interval of time.
/// Example: I want an alarm to go off every 60s. The alarm will first
/// go off 60s after I set it and every 60s after that. The alarm will
/// not disable itself.
///
/// - interval delayed: the alarm will trigger after a certain amount of
/// time and then trigger at a specified interval.
/// Example: I want an alarm to go off every 60s but only start in 1h.
/// The alarm will first trigger 1h after I set it and then every 60s
/// after that. The alarm will not disable itself.
///
/// # Relative vs absolute alarm
///
/// If you do not set any `TimerSetTimeFlags`, then the `TimeSpec` you pass
/// to the `Expiration` you want is relative. If however you want an alarm
/// to go off at a certain point in time, you can set `TFD_TIMER_ABSTIME`.
/// Then the one shot TimeSpec and the delay TimeSpec of the delayed
/// interval are going to be interpreted as absolute.
///
/// # Disabling alarms
///
/// Note: Only one alarm can be set for any given timer. Setting a new alarm
/// actually removes the previous one.
///
/// Note: Setting a one shot alarm with a 0s TimeSpec disable the alarm
/// altogether.
#[doc(alias("timer_settime"))]
pub fn set(
&mut self,
expiration: Expiration,
flags: TimerSetTimeFlags,
) -> Result<()> {
let timerspec: TimerSpec = expiration.into();
Errno::result(unsafe {
libc::timer_settime(
self.0,
flags.bits(),
timerspec.as_ref(),
core::ptr::null_mut(),
)
})
.map(drop)
}
/// Get the parameters for the alarm currently set, if any.
#[doc(alias("timer_gettime"))]
pub fn get(&self) -> Result<Option<Expiration>> {
let mut timerspec = TimerSpec::none();
Errno::result(unsafe {
libc::timer_gettime(self.0, timerspec.as_mut())
})
.map(|_| {
if timerspec.as_ref().it_interval.tv_sec == 0
&& timerspec.as_ref().it_interval.tv_nsec == 0
&& timerspec.as_ref().it_value.tv_sec == 0
&& timerspec.as_ref().it_value.tv_nsec == 0
{
None
} else {
Some(timerspec.into())
}
})
}
/// Return the number of timers that have overrun
///
/// Each timer is able to queue one signal to the process at a time, meaning
/// if the signal is not handled before the next expiration the timer has
/// 'overrun'. This function returns how many times that has happened to
/// this timer, up to `libc::DELAYTIMER_MAX`. If more than the maximum
/// number of overruns have happened the return is capped to the maximum.
#[doc(alias("timer_getoverrun"))]
pub fn overruns(&self) -> i32 {
unsafe { libc::timer_getoverrun(self.0) }
}
}
impl Drop for Timer {
fn drop(&mut self) {
if !std::thread::panicking() {
let result = Errno::result(unsafe { libc::timer_delete(self.0) });
if let Err(Errno::EINVAL) = result {
panic!("close of Timer encountered EINVAL");
}
}
}
}

240
vendor/nix/src/sys/timerfd.rs vendored Normal file
View File

@@ -0,0 +1,240 @@
//! Timer API via file descriptors.
//!
//! Timer FD is a Linux-only API to create timers and get expiration
//! notifications through file descriptors.
//!
//! For more documentation, please read [timerfd_create(2)](https://man7.org/linux/man-pages/man2/timerfd_create.2.html).
//!
//! # Examples
//!
//! Create a new one-shot timer that expires after 1 second.
//! ```
//! # use std::os::unix::io::AsRawFd;
//! # use nix::sys::timerfd::{TimerFd, ClockId, TimerFlags, TimerSetTimeFlags,
//! # Expiration};
//! # use nix::sys::time::{TimeSpec, TimeValLike};
//! # use nix::unistd::read;
//! #
//! // We create a new monotonic timer.
//! let timer = TimerFd::new(ClockId::CLOCK_MONOTONIC, TimerFlags::empty())
//! .unwrap();
//!
//! // We set a new one-shot timer in 1 seconds.
//! timer.set(
//! Expiration::OneShot(TimeSpec::seconds(1)),
//! TimerSetTimeFlags::empty()
//! ).unwrap();
//!
//! // We wait for the timer to expire.
//! timer.wait().unwrap();
//! ```
use crate::sys::time::timer::TimerSpec;
pub use crate::sys::time::timer::{Expiration, TimerSetTimeFlags};
use crate::unistd::read;
use crate::{errno::Errno, Result};
use libc::c_int;
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd};
/// A timerfd instance. This is also a file descriptor, you can feed it to
/// other interfaces taking file descriptors as arguments, [`epoll`] for example.
///
/// [`epoll`]: crate::sys::epoll
#[derive(Debug)]
pub struct TimerFd {
fd: OwnedFd,
}
impl AsFd for TimerFd {
fn as_fd(&self) -> BorrowedFd<'_> {
self.fd.as_fd()
}
}
impl FromRawFd for TimerFd {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
TimerFd {
fd: unsafe { OwnedFd::from_raw_fd(fd) },
}
}
}
impl From<TimerFd> for OwnedFd {
fn from(value: TimerFd) -> Self {
value.fd
}
}
libc_enum! {
/// The type of the clock used to mark the progress of the timer. For more
/// details on each kind of clock, please refer to [timerfd_create(2)](https://man7.org/linux/man-pages/man2/timerfd_create.2.html).
#[repr(i32)]
#[non_exhaustive]
pub enum ClockId {
/// A settable system-wide real-time clock.
CLOCK_REALTIME,
/// A non-settable monotonically increasing clock.
///
/// Does not change after system startup.
/// Does not measure time while the system is suspended.
CLOCK_MONOTONIC,
/// Like `CLOCK_MONOTONIC`, except that `CLOCK_BOOTTIME` includes the time
/// that the system was suspended.
CLOCK_BOOTTIME,
/// Like `CLOCK_REALTIME`, but will wake the system if it is suspended.
CLOCK_REALTIME_ALARM,
/// Like `CLOCK_BOOTTIME`, but will wake the system if it is suspended.
CLOCK_BOOTTIME_ALARM,
}
}
libc_bitflags! {
/// Additional flags to change the behaviour of the file descriptor at the
/// time of creation.
pub struct TimerFlags: c_int {
/// Set the `O_NONBLOCK` flag on the open file description referred to by the new file descriptor.
TFD_NONBLOCK;
/// Set the `FD_CLOEXEC` flag on the file descriptor.
TFD_CLOEXEC;
}
}
impl TimerFd {
/// Creates a new timer based on the clock defined by `clockid`. The
/// underlying fd can be assigned specific flags with `flags` (CLOEXEC,
/// NONBLOCK). The underlying fd will be closed on drop.
#[doc(alias("timerfd_create"))]
pub fn new(clockid: ClockId, flags: TimerFlags) -> Result<Self> {
Errno::result(unsafe {
libc::timerfd_create(clockid as i32, flags.bits())
})
.map(|fd| Self {
fd: unsafe { OwnedFd::from_raw_fd(fd) },
})
}
/// Sets a new alarm on the timer.
///
/// # Types of alarm
///
/// There are 3 types of alarms you can set:
///
/// - one shot: the alarm will trigger once after the specified amount of
/// time.
/// Example: I want an alarm to go off in 60s and then disable itself.
///
/// - interval: the alarm will trigger every specified interval of time.
/// Example: I want an alarm to go off every 60s. The alarm will first
/// go off 60s after I set it and every 60s after that. The alarm will
/// not disable itself.
///
/// - interval delayed: the alarm will trigger after a certain amount of
/// time and then trigger at a specified interval.
/// Example: I want an alarm to go off every 60s but only start in 1h.
/// The alarm will first trigger 1h after I set it and then every 60s
/// after that. The alarm will not disable itself.
///
/// # Relative vs absolute alarm
///
/// If you do not set any `TimerSetTimeFlags`, then the `TimeSpec` you pass
/// to the `Expiration` you want is relative. If however you want an alarm
/// to go off at a certain point in time, you can set `TFD_TIMER_ABSTIME`.
/// Then the one shot TimeSpec and the delay TimeSpec of the delayed
/// interval are going to be interpreted as absolute.
///
/// # Cancel on a clock change
///
/// If you set a `TFD_TIMER_CANCEL_ON_SET` alongside `TFD_TIMER_ABSTIME`
/// and the clock for this timer is `CLOCK_REALTIME` or `CLOCK_REALTIME_ALARM`,
/// then this timer is marked as cancelable if the real-time clock undergoes
/// a discontinuous change.
///
/// # Disabling alarms
///
/// Note: Only one alarm can be set for any given timer. Setting a new alarm
/// actually removes the previous one.
///
/// Note: Setting a one shot alarm with a 0s TimeSpec disables the alarm
/// altogether.
#[doc(alias("timerfd_settime"))]
pub fn set(
&self,
expiration: Expiration,
flags: TimerSetTimeFlags,
) -> Result<()> {
let timerspec: TimerSpec = expiration.into();
Errno::result(unsafe {
libc::timerfd_settime(
self.fd.as_fd().as_raw_fd(),
flags.bits(),
timerspec.as_ref(),
std::ptr::null_mut(),
)
})
.map(drop)
}
/// Get the parameters for the alarm currently set, if any.
#[doc(alias("timerfd_gettime"))]
pub fn get(&self) -> Result<Option<Expiration>> {
let mut timerspec = TimerSpec::none();
Errno::result(unsafe {
libc::timerfd_gettime(
self.fd.as_fd().as_raw_fd(),
timerspec.as_mut(),
)
})
.map(|_| {
if timerspec.as_ref().it_interval.tv_sec == 0
&& timerspec.as_ref().it_interval.tv_nsec == 0
&& timerspec.as_ref().it_value.tv_sec == 0
&& timerspec.as_ref().it_value.tv_nsec == 0
{
None
} else {
Some(timerspec.into())
}
})
}
/// Remove the alarm if any is set.
#[doc(alias("timerfd_settime"))]
pub fn unset(&self) -> Result<()> {
Errno::result(unsafe {
libc::timerfd_settime(
self.fd.as_fd().as_raw_fd(),
TimerSetTimeFlags::empty().bits(),
TimerSpec::none().as_ref(),
std::ptr::null_mut(),
)
})
.map(drop)
}
/// Wait for the configured alarm to expire.
///
/// Note: If the alarm is unset, then you will wait forever.
pub fn wait(&self) -> Result<()> {
while let Err(e) = read(&self.fd, &mut [0u8; 8]) {
if e == Errno::ECANCELED {
break;
}
if e != Errno::EINTR {
return Err(e);
}
}
Ok(())
}
/// Constructs a `TimerFd` wrapping an existing `OwnedFd`.
///
/// # Safety
///
/// `OwnedFd` is a valid `TimerFd`.
pub unsafe fn from_owned_fd(fd: OwnedFd) -> Self {
Self {
fd
}
}
}

234
vendor/nix/src/sys/uio.rs vendored Normal file
View File

@@ -0,0 +1,234 @@
//! Vectored I/O
use crate::errno::Errno;
use crate::Result;
use libc::{self, c_int, off_t, size_t};
use std::io::{IoSlice, IoSliceMut};
use std::os::unix::io::{AsFd, AsRawFd};
/// Low-level vectored write to a raw file descriptor
///
/// See also [writev(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/writev.html)
pub fn writev<Fd: AsFd>(fd: Fd, iov: &[IoSlice<'_>]) -> Result<usize> {
// SAFETY: to quote the documentation for `IoSlice`:
//
// [IoSlice] is semantically a wrapper around a &[u8], but is
// guaranteed to be ABI compatible with the iovec type on Unix
// platforms.
//
// Because it is ABI compatible, a pointer cast here is valid
let res = unsafe {
libc::writev(
fd.as_fd().as_raw_fd(),
iov.as_ptr().cast(),
iov.len() as c_int,
)
};
Errno::result(res).map(|r| r as usize)
}
/// Low-level vectored read from a raw file descriptor
///
/// See also [readv(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/readv.html)
// Clippy doesn't know that we need to pass iov mutably only because the
// mutation happens after converting iov to a pointer
#[allow(clippy::needless_pass_by_ref_mut)]
pub fn readv<Fd: AsFd>(fd: Fd, iov: &mut [IoSliceMut<'_>]) -> Result<usize> {
// SAFETY: same as in writev(), IoSliceMut is ABI-compatible with iovec
let res = unsafe {
libc::readv(
fd.as_fd().as_raw_fd(),
iov.as_ptr().cast(),
iov.len() as c_int,
)
};
Errno::result(res).map(|r| r as usize)
}
/// Write to `fd` at `offset` from buffers in `iov`.
///
/// Buffers in `iov` will be written in order until all buffers have been written
/// or an error occurs. The file offset is not changed.
///
/// See also: [`writev`](fn.writev.html) and [`pwrite`](fn.pwrite.html)
#[cfg(not(any(target_os = "redox", target_os = "haiku", target_os = "solaris", target_os = "cygwin")))]
pub fn pwritev<Fd: AsFd>(
fd: Fd,
iov: &[IoSlice<'_>],
offset: off_t,
) -> Result<usize> {
#[cfg(target_env = "uclibc")]
let offset = offset as libc::off64_t; // uclibc doesn't use off_t
// SAFETY: same as in writev()
let res = unsafe {
libc::pwritev(
fd.as_fd().as_raw_fd(),
iov.as_ptr().cast(),
iov.len() as c_int,
offset,
)
};
Errno::result(res).map(|r| r as usize)
}
/// Read from `fd` at `offset` filling buffers in `iov`.
///
/// Buffers in `iov` will be filled in order until all buffers have been filled,
/// no more bytes are available, or an error occurs. The file offset is not
/// changed.
///
/// See also: [`readv`](fn.readv.html) and [`pread`](fn.pread.html)
#[cfg(not(any(target_os = "redox", target_os = "haiku", target_os = "solaris", target_os = "cygwin")))]
// Clippy doesn't know that we need to pass iov mutably only because the
// mutation happens after converting iov to a pointer
#[allow(clippy::needless_pass_by_ref_mut)]
pub fn preadv<Fd: AsFd>(
fd: Fd,
iov: &mut [IoSliceMut<'_>],
offset: off_t,
) -> Result<usize> {
#[cfg(target_env = "uclibc")]
let offset = offset as libc::off64_t; // uclibc doesn't use off_t
// SAFETY: same as in readv()
let res = unsafe {
libc::preadv(
fd.as_fd().as_raw_fd(),
iov.as_ptr().cast(),
iov.len() as c_int,
offset,
)
};
Errno::result(res).map(|r| r as usize)
}
/// Low-level write to a file, with specified offset.
///
/// See also [pwrite(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pwrite.html)
// TODO: move to unistd
pub fn pwrite<Fd: AsFd>(fd: Fd, buf: &[u8], offset: off_t) -> Result<usize> {
let res = unsafe {
libc::pwrite(
fd.as_fd().as_raw_fd(),
buf.as_ptr().cast(),
buf.len() as size_t,
offset,
)
};
Errno::result(res).map(|r| r as usize)
}
/// Low-level read from a file, with specified offset.
///
/// See also [pread(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pread.html)
// TODO: move to unistd
pub fn pread<Fd: AsFd>(fd: Fd, buf: &mut [u8], offset: off_t) -> Result<usize> {
let res = unsafe {
libc::pread(
fd.as_fd().as_raw_fd(),
buf.as_mut_ptr().cast(),
buf.len() as size_t,
offset,
)
};
Errno::result(res).map(|r| r as usize)
}
/// A slice of memory in a remote process, starting at address `base`
/// and consisting of `len` bytes.
///
/// This is the same underlying C structure as `IoSlice`,
/// except that it refers to memory in some other process, and is
/// therefore not represented in Rust by an actual slice as `IoSlice` is. It
/// is used with [`process_vm_readv`](fn.process_vm_readv.html)
/// and [`process_vm_writev`](fn.process_vm_writev.html).
#[cfg(linux_android)]
#[repr(C)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct RemoteIoVec {
/// The starting address of this slice (`iov_base`).
pub base: usize,
/// The number of bytes in this slice (`iov_len`).
pub len: usize,
}
feature! {
#![feature = "process"]
/// Write data directly to another process's virtual memory
/// (see [`process_vm_writev`(2)]).
///
/// `local_iov` is a list of [`IoSlice`]s containing the data to be written,
/// and `remote_iov` is a list of [`RemoteIoVec`]s identifying where the
/// data should be written in the target process. On success, returns the
/// number of bytes written, which will always be a whole
/// number of `remote_iov` chunks.
///
/// This requires the same permissions as debugging the process using
/// [ptrace]: you must either be a privileged process (with
/// `CAP_SYS_PTRACE`), or you must be running as the same user as the
/// target process and the OS must have unprivileged debugging enabled.
///
/// This function is only available on Linux and Android(SDK23+).
///
/// [`process_vm_writev`(2)]: https://man7.org/linux/man-pages/man2/process_vm_writev.2.html
/// [ptrace]: ../ptrace/index.html
/// [`IoSlice`]: https://doc.rust-lang.org/std/io/struct.IoSlice.html
/// [`RemoteIoVec`]: struct.RemoteIoVec.html
#[cfg(all(linux_android, not(target_env = "uclibc")))]
pub fn process_vm_writev(
pid: crate::unistd::Pid,
local_iov: &[IoSlice<'_>],
remote_iov: &[RemoteIoVec]) -> Result<usize>
{
let res = unsafe {
libc::process_vm_writev(pid.into(),
local_iov.as_ptr().cast(), local_iov.len() as libc::c_ulong,
remote_iov.as_ptr().cast(), remote_iov.len() as libc::c_ulong, 0)
};
Errno::result(res).map(|r| r as usize)
}
/// Read data directly from another process's virtual memory
/// (see [`process_vm_readv`(2)]).
///
/// `local_iov` is a list of [`IoSliceMut`]s containing the buffer to copy
/// data into, and `remote_iov` is a list of [`RemoteIoVec`]s identifying
/// where the source data is in the target process. On success,
/// returns the number of bytes written, which will always be a whole
/// number of `remote_iov` chunks.
///
/// This requires the same permissions as debugging the process using
/// [`ptrace`]: you must either be a privileged process (with
/// `CAP_SYS_PTRACE`), or you must be running as the same user as the
/// target process and the OS must have unprivileged debugging enabled.
///
/// This function is only available on Linux and Android(SDK23+).
///
/// [`process_vm_readv`(2)]: https://man7.org/linux/man-pages/man2/process_vm_readv.2.html
/// [`ptrace`]: ../ptrace/index.html
/// [`IoSliceMut`]: https://doc.rust-lang.org/std/io/struct.IoSliceMut.html
/// [`RemoteIoVec`]: struct.RemoteIoVec.html
#[cfg(all(linux_android, not(target_env = "uclibc")))]
pub fn process_vm_readv(
pid: crate::unistd::Pid,
local_iov: &mut [IoSliceMut<'_>],
remote_iov: &[RemoteIoVec]) -> Result<usize>
{
let res = unsafe {
libc::process_vm_readv(pid.into(),
local_iov.as_ptr().cast(), local_iov.len() as libc::c_ulong,
remote_iov.as_ptr().cast(), remote_iov.len() as libc::c_ulong, 0)
};
Errno::result(res).map(|r| r as usize)
}
}

64
vendor/nix/src/sys/utsname.rs vendored Normal file
View File

@@ -0,0 +1,64 @@
//! Get system identification
use crate::{Errno, Result};
use libc::c_char;
use std::ffi::OsStr;
use std::mem;
use std::os::unix::ffi::OsStrExt;
/// Describes the running system. Return type of [`uname`].
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[repr(transparent)]
pub struct UtsName(libc::utsname);
impl UtsName {
/// Name of the operating system implementation.
pub fn sysname(&self) -> &OsStr {
cast_and_trim(&self.0.sysname)
}
/// Network name of this machine.
pub fn nodename(&self) -> &OsStr {
cast_and_trim(&self.0.nodename)
}
/// Release level of the operating system.
pub fn release(&self) -> &OsStr {
cast_and_trim(&self.0.release)
}
/// Version level of the operating system.
pub fn version(&self) -> &OsStr {
cast_and_trim(&self.0.version)
}
/// Machine hardware platform.
pub fn machine(&self) -> &OsStr {
cast_and_trim(&self.0.machine)
}
/// NIS or YP domain name of this machine.
#[cfg(linux_android)]
pub fn domainname(&self) -> &OsStr {
cast_and_trim(&self.0.domainname)
}
}
/// Get system identification
pub fn uname() -> Result<UtsName> {
unsafe {
let mut ret = mem::MaybeUninit::zeroed();
Errno::result(libc::uname(ret.as_mut_ptr()))?;
Ok(UtsName(ret.assume_init()))
}
}
fn cast_and_trim(slice: &[c_char]) -> &OsStr {
let length = slice
.iter()
.position(|&byte| byte == 0)
.unwrap_or(slice.len());
let bytes =
unsafe { std::slice::from_raw_parts(slice.as_ptr().cast(), length) };
OsStr::from_bytes(bytes)
}

381
vendor/nix/src/sys/wait.rs vendored Normal file
View File

@@ -0,0 +1,381 @@
//! Wait for a process to change status
use crate::errno::Errno;
use crate::sys::signal::Signal;
use crate::unistd::Pid;
use crate::Result;
use cfg_if::cfg_if;
use libc::{self, c_int};
use std::convert::TryFrom;
#[cfg(any(
target_os = "android",
all(target_os = "linux", not(target_env = "uclibc")),
))]
use std::os::unix::io::{AsRawFd, BorrowedFd};
libc_bitflags!(
/// Controls the behavior of [`waitpid`].
pub struct WaitPidFlag: c_int {
/// Do not block when there are no processes wishing to report status.
WNOHANG;
/// Report the status of selected processes which are stopped due to a
/// [`SIGTTIN`](crate::sys::signal::Signal::SIGTTIN),
/// [`SIGTTOU`](crate::sys::signal::Signal::SIGTTOU),
/// [`SIGTSTP`](crate::sys::signal::Signal::SIGTSTP), or
/// [`SIGSTOP`](crate::sys::signal::Signal::SIGSTOP) signal.
WUNTRACED;
/// Report the status of selected processes which have terminated.
#[cfg(any(linux_android,
apple_targets,
target_os = "freebsd",
target_os = "haiku",
target_os = "redox",
target_os = "netbsd"))]
WEXITED;
/// Report the status of selected processes that have continued from a
/// job control stop by receiving a
/// [`SIGCONT`](crate::sys::signal::Signal::SIGCONT) signal.
WCONTINUED;
/// An alias for WUNTRACED.
#[cfg(any(linux_android,
apple_targets,
target_os = "freebsd",
target_os = "haiku",
target_os = "redox",
target_os = "netbsd"))]
WSTOPPED;
/// Don't reap, just poll status.
#[cfg(any(linux_android,
apple_targets,
target_os = "freebsd",
target_os = "haiku",
target_os = "redox",
target_os = "netbsd"))]
WNOWAIT;
/// Don't wait on children of other threads in this group
#[cfg(any(linux_android, target_os = "redox"))]
__WNOTHREAD;
/// Wait on all children, regardless of type
#[cfg(any(linux_android, target_os = "redox"))]
__WALL;
/// Wait for "clone" children only.
#[cfg(any(linux_android, target_os = "redox"))]
__WCLONE;
}
);
/// Possible return values from `wait()` or `waitpid()`.
///
/// Each status (other than `StillAlive`) describes a state transition
/// in a child process `Pid`, such as the process exiting or stopping,
/// plus additional data about the transition if any.
///
/// Note that there are two Linux-specific enum variants, `PtraceEvent`
/// and `PtraceSyscall`. Portable code should avoid exhaustively
/// matching on `WaitStatus`.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum WaitStatus {
/// The process exited normally (as with `exit()` or returning from
/// `main`) with the given exit code. This case matches the C macro
/// `WIFEXITED(status)`; the second field is `WEXITSTATUS(status)`.
Exited(Pid, i32),
/// The process was killed by the given signal. The third field
/// indicates whether the signal generated a core dump. This case
/// matches the C macro `WIFSIGNALED(status)`; the last two fields
/// correspond to `WTERMSIG(status)` and `WCOREDUMP(status)`.
Signaled(Pid, Signal, bool),
/// The process is alive, but was stopped by the given signal. This
/// is only reported if `WaitPidFlag::WUNTRACED` was passed. This
/// case matches the C macro `WIFSTOPPED(status)`; the second field
/// is `WSTOPSIG(status)`.
Stopped(Pid, Signal),
/// The traced process was stopped by a `PTRACE_EVENT_*` event. See
/// [`nix::sys::ptrace`] and [`ptrace`(2)] for more information. All
/// currently-defined events use `SIGTRAP` as the signal; the third
/// field is the `PTRACE_EVENT_*` value of the event.
///
/// [`nix::sys::ptrace`]: ../ptrace/index.html
/// [`ptrace`(2)]: https://man7.org/linux/man-pages/man2/ptrace.2.html
#[cfg(linux_android)]
PtraceEvent(Pid, Signal, c_int),
/// The traced process was stopped by execution of a system call,
/// and `PTRACE_O_TRACESYSGOOD` is in effect. See [`ptrace`(2)] for
/// more information.
///
/// [`ptrace`(2)]: https://man7.org/linux/man-pages/man2/ptrace.2.html
#[cfg(linux_android)]
PtraceSyscall(Pid),
/// The process was previously stopped but has resumed execution
/// after receiving a `SIGCONT` signal. This is only reported if
/// `WaitPidFlag::WCONTINUED` was passed. This case matches the C
/// macro `WIFCONTINUED(status)`.
Continued(Pid),
/// There are currently no state changes to report in any awaited
/// child process. This is only returned if `WaitPidFlag::WNOHANG`
/// was used (otherwise `wait()` or `waitpid()` would block until
/// there was something to report).
StillAlive,
}
impl WaitStatus {
/// Extracts the PID from the WaitStatus unless it equals StillAlive.
pub fn pid(&self) -> Option<Pid> {
use self::WaitStatus::*;
match *self {
Exited(p, _) | Signaled(p, _, _) | Stopped(p, _) | Continued(p) => {
Some(p)
}
StillAlive => None,
#[cfg(linux_android)]
PtraceEvent(p, _, _) | PtraceSyscall(p) => Some(p),
}
}
}
fn exited(status: i32) -> bool {
libc::WIFEXITED(status)
}
fn exit_status(status: i32) -> i32 {
libc::WEXITSTATUS(status)
}
fn signaled(status: i32) -> bool {
libc::WIFSIGNALED(status)
}
fn term_signal(status: i32) -> Result<Signal> {
Signal::try_from(libc::WTERMSIG(status))
}
fn dumped_core(status: i32) -> bool {
libc::WCOREDUMP(status)
}
fn stopped(status: i32) -> bool {
libc::WIFSTOPPED(status)
}
fn stop_signal(status: i32) -> Result<Signal> {
Signal::try_from(libc::WSTOPSIG(status))
}
#[cfg(linux_android)]
fn syscall_stop(status: i32) -> bool {
// From ptrace(2), setting PTRACE_O_TRACESYSGOOD has the effect
// of delivering SIGTRAP | 0x80 as the signal number for syscall
// stops. This allows easily distinguishing syscall stops from
// genuine SIGTRAP signals.
libc::WSTOPSIG(status) == libc::SIGTRAP | 0x80
}
#[cfg(linux_android)]
fn stop_additional(status: i32) -> c_int {
(status >> 16) as c_int
}
fn continued(status: i32) -> bool {
libc::WIFCONTINUED(status)
}
impl WaitStatus {
/// Convert a raw `wstatus` as returned by `waitpid`/`wait` into a `WaitStatus`
///
/// # Errors
///
/// Returns an `Error` corresponding to `EINVAL` for invalid status values.
///
/// # Examples
///
/// Convert a `wstatus` obtained from `libc::waitpid` into a `WaitStatus`:
///
/// ```
/// use nix::sys::wait::WaitStatus;
/// use nix::sys::signal::Signal;
/// let pid = nix::unistd::Pid::from_raw(1);
/// let status = WaitStatus::from_raw(pid, 0x0002);
/// assert_eq!(status, Ok(WaitStatus::Signaled(pid, Signal::SIGINT, false)));
/// ```
pub fn from_raw(pid: Pid, status: i32) -> Result<WaitStatus> {
Ok(if exited(status) {
WaitStatus::Exited(pid, exit_status(status))
} else if signaled(status) {
WaitStatus::Signaled(pid, term_signal(status)?, dumped_core(status))
} else if stopped(status) {
cfg_if! {
if #[cfg(linux_android)] {
fn decode_stopped(pid: Pid, status: i32) -> Result<WaitStatus> {
let status_additional = stop_additional(status);
Ok(if syscall_stop(status) {
WaitStatus::PtraceSyscall(pid)
} else if status_additional == 0 {
WaitStatus::Stopped(pid, stop_signal(status)?)
} else {
WaitStatus::PtraceEvent(pid, stop_signal(status)?,
stop_additional(status))
})
}
} else {
fn decode_stopped(pid: Pid, status: i32) -> Result<WaitStatus> {
Ok(WaitStatus::Stopped(pid, stop_signal(status)?))
}
}
}
return decode_stopped(pid, status);
} else {
assert!(continued(status));
WaitStatus::Continued(pid)
})
}
/// Convert a `siginfo_t` as returned by `waitid` to a `WaitStatus`
///
/// # Errors
///
/// Returns an `Error` corresponding to `EINVAL` for invalid values.
///
/// # Safety
///
/// siginfo_t is actually a union, not all fields may be initialized.
/// The functions si_pid() and si_status() must be valid to call on
/// the passed siginfo_t.
#[cfg(any(
target_os = "android",
target_os = "freebsd",
target_os = "haiku",
all(target_os = "linux", not(target_env = "uclibc")),
))]
unsafe fn from_siginfo(siginfo: &libc::siginfo_t) -> Result<WaitStatus> {
let si_pid = unsafe { siginfo.si_pid() };
if si_pid == 0 {
return Ok(WaitStatus::StillAlive);
}
assert_eq!(siginfo.si_signo, libc::SIGCHLD);
let pid = Pid::from_raw(si_pid);
let si_status = unsafe { siginfo.si_status() };
let status = match siginfo.si_code {
libc::CLD_EXITED => WaitStatus::Exited(pid, si_status),
libc::CLD_KILLED | libc::CLD_DUMPED => WaitStatus::Signaled(
pid,
Signal::try_from(si_status)?,
siginfo.si_code == libc::CLD_DUMPED,
),
libc::CLD_STOPPED => {
WaitStatus::Stopped(pid, Signal::try_from(si_status)?)
}
libc::CLD_CONTINUED => WaitStatus::Continued(pid),
#[cfg(linux_android)]
libc::CLD_TRAPPED => {
if si_status == libc::SIGTRAP | 0x80 {
WaitStatus::PtraceSyscall(pid)
} else {
WaitStatus::PtraceEvent(
pid,
Signal::try_from(si_status & 0xff)?,
(si_status >> 8) as c_int,
)
}
}
_ => return Err(Errno::EINVAL),
};
Ok(status)
}
}
/// Wait for a process to change status
///
/// See also [waitpid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/waitpid.html)
pub fn waitpid<P: Into<Option<Pid>>>(
pid: P,
options: Option<WaitPidFlag>,
) -> Result<WaitStatus> {
use self::WaitStatus::*;
let mut status: i32 = 0;
let option_bits = match options {
Some(bits) => bits.bits(),
None => 0,
};
let res = unsafe {
libc::waitpid(
pid.into().unwrap_or_else(|| Pid::from_raw(-1)).into(),
&mut status as *mut c_int,
option_bits,
)
};
match Errno::result(res)? {
0 => Ok(StillAlive),
res => WaitStatus::from_raw(Pid::from_raw(res), status),
}
}
/// Wait for any child process to change status or a signal is received.
///
/// See also [wait(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html)
pub fn wait() -> Result<WaitStatus> {
waitpid(None, None)
}
/// The ID argument for `waitid`
#[cfg(any(
target_os = "android",
target_os = "freebsd",
target_os = "haiku",
all(target_os = "linux", not(target_env = "uclibc")),
))]
#[derive(Debug)]
pub enum Id<'fd> {
/// Wait for any child
All,
/// Wait for the child whose process ID matches the given PID
Pid(Pid),
/// Wait for the child whose process group ID matches the given PID
///
/// If the PID is zero, the caller's process group is used since Linux 5.4.
PGid(Pid),
/// Wait for the child referred to by the given PID file descriptor
#[cfg(linux_android)]
PIDFd(BorrowedFd<'fd>),
/// A helper variant to resolve the unused parameter (`'fd`) problem on platforms
/// other than Linux and Android.
#[doc(hidden)]
_Unreachable(std::marker::PhantomData<&'fd std::convert::Infallible>),
}
/// Wait for a process to change status
///
/// See also [waitid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/waitid.html)
#[cfg(any(
target_os = "android",
target_os = "freebsd",
target_os = "haiku",
all(target_os = "linux", not(target_env = "uclibc")),
))]
pub fn waitid(id: Id, flags: WaitPidFlag) -> Result<WaitStatus> {
let (idtype, idval) = match id {
Id::All => (libc::P_ALL, 0),
Id::Pid(pid) => (libc::P_PID, pid.as_raw() as libc::id_t),
Id::PGid(pid) => (libc::P_PGID, pid.as_raw() as libc::id_t),
#[cfg(linux_android)]
Id::PIDFd(fd) => (libc::P_PIDFD, fd.as_raw_fd() as libc::id_t),
Id::_Unreachable(_) => {
unreachable!("This variant could never be constructed")
}
};
let siginfo = unsafe {
// Memory is zeroed rather than uninitialized, as not all platforms
// initialize the memory in the StillAlive case
let mut siginfo: libc::siginfo_t = std::mem::zeroed();
Errno::result(libc::waitid(idtype, idval, &mut siginfo, flags.bits()))?;
siginfo
};
unsafe { WaitStatus::from_siginfo(&siginfo) }
}

293
vendor/nix/src/syslog.rs vendored Normal file
View File

@@ -0,0 +1,293 @@
//! Interfaces for controlling system log.
use crate::{NixPath, Result};
use std::ffi::OsStr;
use std::ptr;
/// Logging options of subsequent [`syslog`] calls can be set by calling [`openlog`].
///
/// The parameter `ident` is a string that will be prepended to every message. The `logopt`
/// argument specifies logging options. The `facility` parameter encodes a default facility to be
/// assigned to all messages that do not have an explicit facility encoded.
//
// On Linux, the `ident` argument needs to have static lifetime according to the
// man page:
//
// The argument ident in the call of openlog() is probably stored as-is. Thus,
// if the string it points to is changed, syslog() may start prepending the changed
// string, and if the string it points to ceases to exist, the results are
// undefined. Most portable is to use a string constant.
#[cfg(target_os = "linux")]
pub fn openlog(
ident: Option<&'static std::ffi::CStr>,
logopt: LogFlags,
facility: Facility,
) -> Result<()> {
let logopt = logopt.bits();
let facility = facility as libc::c_int;
match ident {
None => unsafe {
libc::openlog(ptr::null(), logopt, facility);
},
Some(ident) => ident.with_nix_path(|ident| unsafe {
libc::openlog(ident.as_ptr(), logopt, facility);
})?,
}
Ok(())
}
/// Logging options of subsequent [`syslog`] calls can be set by calling [`openlog`].
///
/// The parameter `ident` is a string that will be prepended to every message. The `logopt`
/// argument specifies logging options. The `facility` parameter encodes a default facility to be
/// assigned to all messages that do not have an explicit facility encoded.
#[cfg(not(target_os = "linux"))]
pub fn openlog<S: AsRef<OsStr> + ?Sized>(
ident: Option<&S>,
logopt: LogFlags,
facility: Facility,
) -> Result<()> {
let logopt = logopt.bits();
let facility = facility as libc::c_int;
match ident.map(OsStr::new) {
None => unsafe {
libc::openlog(ptr::null(), logopt, facility);
},
Some(ident) => ident.with_nix_path(|ident| unsafe {
libc::openlog(ident.as_ptr(), logopt, facility);
})?,
}
Ok(())
}
/// Writes message to the system message logger.
///
/// The message is then written to the system console, log files, logged-in users, or forwarded
/// to other machines as appropriate.
///
/// # Examples
///
/// ```rust
/// use nix::syslog::{openlog, syslog, Facility, LogFlags, Severity, Priority};
///
/// let priority = Priority::new(Severity::LOG_EMERG, Facility::LOG_USER);
/// syslog(priority, "Hello, nix!").unwrap();
///
/// // use `format!` to format the message
/// let name = "syslog";
/// syslog(priority, &format!("Hello, {name}!")).unwrap();
/// ```
pub fn syslog<P, S>(priority: P, message: &S) -> Result<()>
where
P: Into<Priority>,
S: AsRef<OsStr> + ?Sized,
{
let priority = priority.into();
let formatter = OsStr::new("%s");
let message = OsStr::new(message);
formatter.with_nix_path(|formatter| {
message.with_nix_path(|message| unsafe {
libc::syslog(priority.0, formatter.as_ptr(), message.as_ptr())
})
})??;
Ok(())
}
/// Set the process-wide priority mask to `mask` and return the previous mask
/// value.
///
/// Calls to `syslog()` with a priority level not set in `mask` are ignored. The
/// default is to log all priorities.
///
/// If the `mask` argument is `None`, the current logmask is not modified, this
/// can be used to query the current log mask.
pub fn setlogmask(mask: Option<LogMask>) -> LogMask {
let mask = match mask {
Some(mask) => mask.0,
None => 0,
};
let prev_mask = unsafe { libc::setlogmask(mask) };
LogMask(prev_mask)
}
/// Closes the log file.
pub fn closelog() {
unsafe { libc::closelog() }
}
/// System log priority mask.
#[derive(Debug, Clone, Copy)]
pub struct LogMask(libc::c_int);
impl LogMask {
/// Creates a mask of all priorities up to and including `priority`.
#[doc(alias("LOG_UPTO"))]
pub fn up_to(priority: Severity) -> Self {
let pri = priority as libc::c_int;
Self((1 << (pri + 1)) - 1)
}
/// Creates a mask for the specified priority.
#[doc(alias("LOG_MASK"))]
pub fn of_priority(priority: Severity) -> Self {
let pri = priority as libc::c_int;
Self(1 << pri)
}
/// Returns if the mask for the specified `priority` is set.
pub fn contains(&self, priority: Severity) -> bool {
let priority = Self::of_priority(priority);
let and_result = *self & priority;
and_result.0 != 0
}
}
impl std::ops::BitOr for LogMask {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
Self(self.0 | rhs.0)
}
}
impl std::ops::BitAnd for LogMask {
type Output = Self;
fn bitand(self, rhs: Self) -> Self::Output {
Self(self.0 & rhs.0)
}
}
impl std::ops::BitOrAssign for LogMask {
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
impl std::ops::BitAndAssign for LogMask {
fn bitand_assign(&mut self, rhs: Self) {
self.0 &= rhs.0;
}
}
impl std::ops::Not for LogMask {
type Output = Self;
fn not(self) -> Self::Output {
Self(!self.0)
}
}
/// The priority for a log message.
#[derive(Debug, Clone, Copy)]
pub struct Priority(libc::c_int);
impl Priority {
/// Create a new priority from a facility and severity level.
pub fn new(severity: Severity, facility: Facility) -> Self {
let priority = (facility as libc::c_int) | (severity as libc::c_int);
Priority(priority)
}
}
impl From<Severity> for Priority {
fn from(severity: Severity) -> Self {
let priority = severity as libc::c_int;
Priority(priority)
}
}
libc_bitflags! {
/// Options for system logging.
pub struct LogFlags: libc::c_int {
/// Log the process id with each message: useful for identifying instantiations of
/// daemons.
LOG_PID;
/// If syslog() cannot pass the message to syslogd(8) it will attempt to write the
/// message to the console ("/dev/console").
LOG_CONS;
/// The converse of [`LOG_NDELAY`][LogFlags::LOG_NDELAY]; opening of the connection is
/// delayed until `syslog` is called.
///
/// This is the default, and need not be specified.
LOG_ODELAY;
/// Open the connection to syslogd(8) immediately. Normally the open is delayed until
/// the first message is logged. Useful for programs that need to manage the order in
/// which file descriptors are allocated.
LOG_NDELAY;
/// Write the message to standard error output as well to the system log.
#[cfg(not(any(solarish, target_os = "redox", target_os = "cygwin")))]
LOG_PERROR;
}
}
libc_enum! {
/// Severity levels for log messages.
#[repr(i32)]
#[non_exhaustive]
pub enum Severity {
/// A panic condition.
///
/// This is normally broadcast to all users.
LOG_EMERG,
/// A condition that should be corrected immediately, such as a corrupted system database.
LOG_ALERT,
/// Critical conditions, e.g., hard device errors.
LOG_CRIT,
/// Errors.
LOG_ERR,
/// Warning messages.
LOG_WARNING,
/// Conditions that are not error conditions, but should possibly be handled specially.
LOG_NOTICE,
/// Informational messages.
LOG_INFO,
/// Messages that contain information normally of use only when debugging a program.
LOG_DEBUG,
}
}
libc_enum! {
/// Facilities for log messages.
#[repr(i32)]
#[non_exhaustive]
pub enum Facility {
/// Messages generated by the kernel.
///
/// These cannot be generated by any user processes.
LOG_KERN,
/// Messages generated by random user processes.
///
/// This is the default facility identifier if none is specified.
LOG_USER,
/// The mail system.
LOG_MAIL,
/// System daemons, such as routed(8), that are not provided for explicitly by other facilities.
LOG_DAEMON,
/// The authorization system: login(1), su(1), getty(8), etc.
LOG_AUTH,
/// Messages generated internally by syslogd(8).
LOG_SYSLOG,
/// The line printer spooling system: cups-lpd(8), cupsd(8), etc.
LOG_LPR,
/// The network news system.
LOG_NEWS,
/// The uucp system.
LOG_UUCP,
/// Reserved for local use.
LOG_LOCAL0,
/// Reserved for local use.
LOG_LOCAL1,
/// Reserved for local use.
LOG_LOCAL2,
/// Reserved for local use.
LOG_LOCAL3,
/// Reserved for local use.
LOG_LOCAL4,
/// Reserved for local use.
LOG_LOCAL5,
/// Reserved for local use.
LOG_LOCAL6,
/// Reserved for local use.
LOG_LOCAL7,
}
}

288
vendor/nix/src/time.rs vendored Normal file
View File

@@ -0,0 +1,288 @@
//! Sleep, query system clocks, and set system clock
use crate::sys::time::TimeSpec;
#[cfg(any(freebsdlike, linux_android, target_os = "emscripten"))]
#[cfg(feature = "process")]
use crate::unistd::Pid;
use crate::{Errno, Result};
use libc::{self, clockid_t};
use std::mem::MaybeUninit;
/// Clock identifier
///
/// Newtype pattern around [`libc::clockid_t`].
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct ClockId(clockid_t);
impl ClockId {
/// Creates `ClockId` from raw `clockid_t`
pub const fn from_raw(clk_id: clockid_t) -> Self {
ClockId(clk_id)
}
feature! {
#![feature = "process"]
/// Returns `ClockId` of a `pid` CPU-time clock
#[cfg(any(freebsdlike, linux_android, target_os = "emscripten"))]
pub fn pid_cpu_clock_id(pid: Pid) -> Result<Self> {
clock_getcpuclockid(pid)
}
}
/// Returns resolution of the clock id
#[cfg(not(target_os = "redox"))]
pub fn res(self) -> Result<TimeSpec> {
clock_getres(self)
}
/// Returns the current time on the clock id
pub fn now(self) -> Result<TimeSpec> {
clock_gettime(self)
}
/// Sets time to `timespec` on the clock id
#[cfg(not(any(
target_os = "ios",
target_os = "tvos",
target_os = "watchos",
target_os = "redox",
target_os = "hermit"
)))]
pub fn set_time(self, timespec: TimeSpec) -> Result<()> {
clock_settime(self, timespec)
}
/// Gets the raw `clockid_t` wrapped by `self`
pub const fn as_raw(self) -> clockid_t {
self.0
}
#[cfg(any(linux_android, target_os = "emscripten", target_os = "fuchsia"))]
/// Starts at zero when the kernel boots and increments monotonically in SI seconds while the
/// machine is running.
pub const CLOCK_BOOTTIME: ClockId = ClockId(libc::CLOCK_BOOTTIME);
/// Like [`CLOCK_BOOTTIME`](ClockId::CLOCK_BOOTTIME), but will wake the system if it is
/// suspended..
#[cfg(any(linux_android, target_os = "emscripten", target_os = "fuchsia"))]
pub const CLOCK_BOOTTIME_ALARM: ClockId =
ClockId(libc::CLOCK_BOOTTIME_ALARM);
/// Increments in SI seconds.
pub const CLOCK_MONOTONIC: ClockId = ClockId(libc::CLOCK_MONOTONIC);
/// Like [`CLOCK_MONOTONIC`](ClockId::CLOCK_MONOTONIC), but optimized for execution time at the expense of accuracy.
#[cfg(any(linux_android, target_os = "emscripten", target_os = "fuchsia"))]
pub const CLOCK_MONOTONIC_COARSE: ClockId =
ClockId(libc::CLOCK_MONOTONIC_COARSE);
#[cfg(freebsdlike)]
/// Like [`CLOCK_MONOTONIC`](ClockId::CLOCK_MONOTONIC), but optimized for execution time at the expense of accuracy.
pub const CLOCK_MONOTONIC_FAST: ClockId =
ClockId(libc::CLOCK_MONOTONIC_FAST);
#[cfg(freebsdlike)]
/// Like [`CLOCK_MONOTONIC`](ClockId::CLOCK_MONOTONIC), but optimized for accuracy at the expense of execution time.
pub const CLOCK_MONOTONIC_PRECISE: ClockId =
ClockId(libc::CLOCK_MONOTONIC_PRECISE);
/// Similar to [`CLOCK_MONOTONIC`](ClockId::CLOCK_MONOTONIC), but provides access to a raw
/// hardware-based time that is not subject to NTP adjustments.
#[cfg(any(linux_android, target_os = "emscripten", target_os = "fuchsia"))]
pub const CLOCK_MONOTONIC_RAW: ClockId = ClockId(libc::CLOCK_MONOTONIC_RAW);
#[cfg(any(
linux_android,
apple_targets,
freebsdlike,
target_os = "emscripten",
target_os = "fuchsia",
target_os = "redox",
))]
/// Returns the execution time of the calling process.
pub const CLOCK_PROCESS_CPUTIME_ID: ClockId =
ClockId(libc::CLOCK_PROCESS_CPUTIME_ID);
#[cfg(freebsdlike)]
/// Increments when the CPU is running in user or kernel mode
pub const CLOCK_PROF: ClockId = ClockId(libc::CLOCK_PROF);
/// Increments as a wall clock should.
pub const CLOCK_REALTIME: ClockId = ClockId(libc::CLOCK_REALTIME);
/// Like [`CLOCK_REALTIME`](ClockId::CLOCK_REALTIME), but not settable.
#[cfg(any(linux_android, target_os = "emscripten", target_os = "fuchsia"))]
pub const CLOCK_REALTIME_ALARM: ClockId =
ClockId(libc::CLOCK_REALTIME_ALARM);
/// Like [`CLOCK_REALTIME`](ClockId::CLOCK_REALTIME), but optimized for execution time at the expense of accuracy.
#[cfg(any(linux_android, target_os = "emscripten", target_os = "fuchsia"))]
pub const CLOCK_REALTIME_COARSE: ClockId =
ClockId(libc::CLOCK_REALTIME_COARSE);
#[cfg(freebsdlike)]
/// Like [`CLOCK_REALTIME`](ClockId::CLOCK_REALTIME), but optimized for execution time at the expense of accuracy.
pub const CLOCK_REALTIME_FAST: ClockId = ClockId(libc::CLOCK_REALTIME_FAST);
#[cfg(freebsdlike)]
/// Like [`CLOCK_REALTIME`](ClockId::CLOCK_REALTIME), but optimized for accuracy at the expense of execution time.
pub const CLOCK_REALTIME_PRECISE: ClockId =
ClockId(libc::CLOCK_REALTIME_PRECISE);
#[cfg(freebsdlike)]
/// Returns the current second without performing a full time counter query, using an in-kernel
/// cached value of the current second.
pub const CLOCK_SECOND: ClockId = ClockId(libc::CLOCK_SECOND);
#[allow(missing_docs)] // Undocumented on Linux!
#[cfg(any(
target_os = "emscripten",
target_os = "fuchsia",
all(
target_os = "linux",
any(target_env = "musl", target_env = "ohos")
)
))]
pub const CLOCK_SGI_CYCLE: ClockId = ClockId(libc::CLOCK_SGI_CYCLE);
/// International Atomic Time.
///
/// A nonsettable system-wide clock derived from wall-clock time but ignoring leap seconds.
#[cfg(any(linux_android, target_os = "emscripten", target_os = "fuchsia"))]
pub const CLOCK_TAI: ClockId = ClockId(libc::CLOCK_TAI);
#[cfg(any(
linux_android,
apple_targets,
freebsdlike,
target_os = "emscripten",
target_os = "fuchsia",
))]
/// Returns the execution time of the calling thread.
pub const CLOCK_THREAD_CPUTIME_ID: ClockId =
ClockId(libc::CLOCK_THREAD_CPUTIME_ID);
#[cfg(freebsdlike)]
/// Starts at zero when the kernel boots and increments monotonically in SI seconds while the
/// machine is running.
pub const CLOCK_UPTIME: ClockId = ClockId(libc::CLOCK_UPTIME);
#[cfg(freebsdlike)]
/// Like [`CLOCK_UPTIME`](ClockId::CLOCK_UPTIME), but optimized for execution time at the expense of accuracy.
pub const CLOCK_UPTIME_FAST: ClockId = ClockId(libc::CLOCK_UPTIME_FAST);
#[cfg(freebsdlike)]
/// Like [`CLOCK_UPTIME`](ClockId::CLOCK_UPTIME), but optimized for accuracy at the expense of execution time.
pub const CLOCK_UPTIME_PRECISE: ClockId =
ClockId(libc::CLOCK_UPTIME_PRECISE);
#[cfg(freebsdlike)]
/// Increments only when the CPU is running in user mode on behalf of the calling process.
pub const CLOCK_VIRTUAL: ClockId = ClockId(libc::CLOCK_VIRTUAL);
}
impl From<ClockId> for clockid_t {
fn from(clock_id: ClockId) -> Self {
clock_id.as_raw()
}
}
impl From<clockid_t> for ClockId {
fn from(clk_id: clockid_t) -> Self {
ClockId::from_raw(clk_id)
}
}
impl std::fmt::Display for ClockId {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Display::fmt(&self.0, f)
}
}
/// Get the resolution of the specified clock, (see
/// [clock_getres(2)](https://pubs.opengroup.org/onlinepubs/7908799/xsh/clock_getres.html)).
#[cfg(not(target_os = "redox"))]
pub fn clock_getres(clock_id: ClockId) -> Result<TimeSpec> {
let mut c_time: MaybeUninit<libc::timespec> = MaybeUninit::uninit();
let ret =
unsafe { libc::clock_getres(clock_id.as_raw(), c_time.as_mut_ptr()) };
Errno::result(ret)?;
let res = unsafe { c_time.assume_init() };
Ok(TimeSpec::from(res))
}
/// Get the time of the specified clock, (see
/// [clock_gettime(2)](https://pubs.opengroup.org/onlinepubs/7908799/xsh/clock_gettime.html)).
pub fn clock_gettime(clock_id: ClockId) -> Result<TimeSpec> {
let mut c_time: MaybeUninit<libc::timespec> = MaybeUninit::uninit();
let ret =
unsafe { libc::clock_gettime(clock_id.as_raw(), c_time.as_mut_ptr()) };
Errno::result(ret)?;
let res = unsafe { c_time.assume_init() };
Ok(TimeSpec::from(res))
}
/// Set the time of the specified clock, (see
/// [clock_settime(2)](https://pubs.opengroup.org/onlinepubs/7908799/xsh/clock_settime.html)).
#[cfg(not(any(
target_os = "ios",
target_os = "tvos",
target_os = "watchos",
target_os = "redox",
target_os = "hermit"
)))]
pub fn clock_settime(clock_id: ClockId, timespec: TimeSpec) -> Result<()> {
let ret =
unsafe { libc::clock_settime(clock_id.as_raw(), timespec.as_ref()) };
Errno::result(ret).map(drop)
}
/// Get the clock id of the specified process id, (see
/// [clock_getcpuclockid(3)](https://pubs.opengroup.org/onlinepubs/009695399/functions/clock_getcpuclockid.html)).
#[cfg(any(freebsdlike, linux_android, target_os = "emscripten"))]
#[cfg(feature = "process")]
#[cfg_attr(docsrs, doc(cfg(feature = "process")))]
pub fn clock_getcpuclockid(pid: Pid) -> Result<ClockId> {
let mut clk_id: MaybeUninit<libc::clockid_t> = MaybeUninit::uninit();
let ret =
unsafe { libc::clock_getcpuclockid(pid.into(), clk_id.as_mut_ptr()) };
if ret == 0 {
let res = unsafe { clk_id.assume_init() };
Ok(ClockId::from(res))
} else {
Err(Errno::from_raw(ret))
}
}
#[cfg(any(
linux_android,
solarish,
freebsdlike,
target_os = "netbsd",
target_os = "hurd",
target_os = "aix"
))]
libc_bitflags! {
/// Flags that are used for arming the timer.
pub struct ClockNanosleepFlags: libc::c_int {
/// Indicates that a requested time value should be treated as absolute instead of
/// relative.
TIMER_ABSTIME;
}
}
/// Suspend execution of this thread for the amount of time specified by `request`
/// and measured against the clock speficied by `clock_id`.
///
/// If `flags` is [`TIMER_ABSTIME`](ClockNanosleepFlags::TIMER_ABSTIME), this function will suspend
/// execution until the time value of clock_id reaches the absolute time specified by `request`. If
/// a signal is caught by a signal-catching function, or a signal causes the process to terminate,
/// this sleep is interrrupted.
///
/// see also [man 3 clock_nanosleep](https://pubs.opengroup.org/onlinepubs/009695399/functions/clock_nanosleep.html)
#[cfg(any(
linux_android,
solarish,
freebsdlike,
target_os = "netbsd",
target_os = "hurd",
target_os = "aix"
))]
pub fn clock_nanosleep(
clock_id: ClockId,
flags: ClockNanosleepFlags,
request: &TimeSpec,
) -> Result<TimeSpec> {
let mut remain = TimeSpec::new(0, 0);
let ret = unsafe {
libc::clock_nanosleep(
clock_id.as_raw(),
flags.bits(),
request.as_ref() as *const _,
remain.as_mut() as *mut _,
)
};
if ret == 0 {
Ok(remain)
} else {
Err(Errno::from_raw(ret))
}
}

47
vendor/nix/src/ucontext.rs vendored Normal file
View File

@@ -0,0 +1,47 @@
#[cfg(not(any(target_env = "musl", target_env = "ohos")))]
use crate::errno::Errno;
use crate::sys::signal::SigSet;
#[cfg(not(any(target_env = "musl", target_env = "ohos")))]
use crate::Result;
#[cfg(not(any(target_env = "musl", target_env = "ohos")))]
use std::mem;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct UContext {
context: libc::ucontext_t,
}
impl UContext {
#[cfg(not(any(target_env = "musl", target_env = "ohos")))]
pub fn get() -> Result<UContext> {
let mut context = mem::MaybeUninit::<libc::ucontext_t>::uninit();
let res = unsafe { libc::getcontext(context.as_mut_ptr()) };
Errno::result(res).map(|_| unsafe {
UContext {
context: context.assume_init(),
}
})
}
#[cfg(not(any(target_env = "musl", target_env = "ohos")))]
pub fn set(&self) -> Result<()> {
let res = unsafe {
libc::setcontext(&self.context as *const libc::ucontext_t)
};
Errno::result(res).map(drop)
}
pub fn sigmask_mut(&mut self) -> &mut SigSet {
unsafe {
&mut *(&mut self.context.uc_sigmask as *mut libc::sigset_t
as *mut SigSet)
}
}
pub fn sigmask(&self) -> &SigSet {
unsafe {
&*(&self.context.uc_sigmask as *const libc::sigset_t
as *const SigSet)
}
}
}

4010
vendor/nix/src/unistd.rs vendored Normal file

File diff suppressed because it is too large Load Diff