Vendor things
This commit is contained in:
parent
5deceec006
commit
977e3c17e5
19434 changed files with 10682014 additions and 0 deletions
259
third-party/vendor/nix/src/dir.rs
vendored
Normal file
259
third-party/vendor/nix/src/dir.rs
vendored
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
//! List directory contents
|
||||
|
||||
use crate::{Error, NixPath, Result};
|
||||
use crate::errno::Errno;
|
||||
use crate::fcntl::{self, OFlag};
|
||||
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
|
||||
use std::ptr;
|
||||
use std::ffi;
|
||||
use crate::sys;
|
||||
use cfg_if::cfg_if;
|
||||
|
||||
#[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`, perhaps before knowing
|
||||
/// if the path represents a file or directory).
|
||||
/// * implements `AsRawFd`, so it can be passed to `fstat`, `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` (no allocation or conversion beyond whatever libc
|
||||
/// does).
|
||||
#[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<P: ?Sized + NixPath>(dirfd: RawFd, 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.
|
||||
#[inline]
|
||||
pub fn from<F: IntoRawFd>(fd: F) -> Result<Self> {
|
||||
Dir::from_fd(fd.into_raw_fd())
|
||||
}
|
||||
|
||||
/// Converts from a file descriptor, closing it on success or failure.
|
||||
#[cfg_attr(has_doc_alias, doc(alias("fdopendir")))]
|
||||
pub fn from_fd(fd: RawFd) -> Result<Self> {
|
||||
let d = ptr::NonNull::new(unsafe { libc::fdopendir(fd) }).ok_or_else(|| {
|
||||
let e = Error::last();
|
||||
unsafe { libc::close(fd) };
|
||||
e
|
||||
})?;
|
||||
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 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!");
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
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<'d> Iterator for Iter<'d> {
|
||||
type Item = Result<Entry>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
next(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> Drop for Iter<'d> {
|
||||
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 = "android",
|
||||
target_os = "emscripten",
|
||||
target_os = "fuchsia",
|
||||
target_os = "haiku",
|
||||
target_os = "illumos",
|
||||
target_os = "ios",
|
||||
target_os = "l4re",
|
||||
target_os = "linux",
|
||||
target_os = "macos",
|
||||
target_os = "solaris"))] {
|
||||
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 { ::std::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(target_os = "illumos", target_os = "solaris", 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(target_os = "illumos", target_os = "solaris", target_os = "haiku"))]
|
||||
None
|
||||
}
|
||||
}
|
||||
64
third-party/vendor/nix/src/env.rs
vendored
Normal file
64
third-party/vendor/nix/src/env.rs
vendored
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
//! 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(target_os = "fuchsia",
|
||||
target_os = "wasi",
|
||||
target_env = "uclibc",
|
||||
target_os = "linux",
|
||||
target_os = "android",
|
||||
target_os = "emscripten"))] {
|
||||
let ret = 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)
|
||||
}
|
||||
}
|
||||
3286
third-party/vendor/nix/src/errno.rs
vendored
Normal file
3286
third-party/vendor/nix/src/errno.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
882
third-party/vendor/nix/src/fcntl.rs
vendored
Normal file
882
third-party/vendor/nix/src/fcntl.rs
vendored
Normal file
|
|
@ -0,0 +1,882 @@
|
|||
use crate::errno::Errno;
|
||||
use libc::{self, c_char, c_int, c_uint, size_t, ssize_t};
|
||||
use std::ffi::OsString;
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
use std::os::raw;
|
||||
use std::os::unix::ffi::OsStringExt;
|
||||
use std::os::unix::io::RawFd;
|
||||
|
||||
#[cfg(feature = "fs")]
|
||||
use crate::{sys::stat::Mode, NixPath, Result};
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
use std::ptr; // For splice and copy_file_range
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "linux",
|
||||
target_os = "android",
|
||||
target_os = "emscripten",
|
||||
target_os = "fuchsia",
|
||||
target_os = "wasi",
|
||||
target_env = "uclibc",
|
||||
target_os = "freebsd"
|
||||
))]
|
||||
#[cfg(feature = "fs")]
|
||||
pub use self::posix_fadvise::{posix_fadvise, PosixFadviseAdvice};
|
||||
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
#[cfg(any(feature = "fs", feature = "process"))]
|
||||
libc_bitflags! {
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "fs", feature = "process"))))]
|
||||
pub struct AtFlags: c_int {
|
||||
AT_REMOVEDIR;
|
||||
AT_SYMLINK_FOLLOW;
|
||||
AT_SYMLINK_NOFOLLOW;
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
AT_NO_AUTOMOUNT;
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
AT_EMPTY_PATH;
|
||||
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
|
||||
AT_EACCESS;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "fs", feature = "term"))]
|
||||
libc_bitflags!(
|
||||
/// Configuration options for opened files.
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "fs", feature = "term"))))]
|
||||
pub struct OFlag: c_int {
|
||||
/// Mask for the access mode of the file.
|
||||
O_ACCMODE;
|
||||
/// Use alternate I/O semantics.
|
||||
#[cfg(target_os = "netbsd")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
O_ALT_IO;
|
||||
/// Open the file in append-only mode.
|
||||
O_APPEND;
|
||||
/// Generate a signal when input or output becomes possible.
|
||||
#[cfg(not(any(target_os = "illumos", target_os = "solaris", target_os = "haiku")))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
O_ASYNC;
|
||||
/// Closes the file descriptor once an `execve` call is made.
|
||||
///
|
||||
/// Also sets the file offset to the beginning of the file.
|
||||
O_CLOEXEC;
|
||||
/// Create the file if it does not exist.
|
||||
O_CREAT;
|
||||
/// Try to minimize cache effects of the I/O for this file.
|
||||
#[cfg(any(target_os = "android",
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "linux",
|
||||
target_os = "netbsd"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
O_DIRECT;
|
||||
/// If the specified path isn't a directory, fail.
|
||||
#[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
O_DIRECTORY;
|
||||
/// Implicitly follow each `write()` with an `fdatasync()`.
|
||||
#[cfg(any(target_os = "android",
|
||||
target_os = "ios",
|
||||
target_os = "linux",
|
||||
target_os = "macos",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
O_DSYNC;
|
||||
/// Error out if a file was not created.
|
||||
O_EXCL;
|
||||
/// Open for execute only.
|
||||
#[cfg(target_os = "freebsd")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
O_EXEC;
|
||||
/// Open with an exclusive file lock.
|
||||
#[cfg(any(target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "ios",
|
||||
target_os = "macos",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "redox"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
O_EXLOCK;
|
||||
/// Same as `O_SYNC`.
|
||||
#[cfg(any(target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "ios",
|
||||
all(target_os = "linux", not(target_env = "musl")),
|
||||
target_os = "macos",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "redox"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
O_FSYNC;
|
||||
/// Allow files whose sizes can't be represented in an `off_t` to be opened.
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
O_LARGEFILE;
|
||||
/// Do not update the file last access time during `read(2)`s.
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
O_NOATIME;
|
||||
/// Don't attach the device as the process' controlling terminal.
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
O_NOCTTY;
|
||||
/// Same as `O_NONBLOCK`.
|
||||
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
O_NDELAY;
|
||||
/// `open()` will fail if the given path is a symbolic link.
|
||||
O_NOFOLLOW;
|
||||
/// When possible, open the file in nonblocking mode.
|
||||
O_NONBLOCK;
|
||||
/// Don't deliver `SIGPIPE`.
|
||||
#[cfg(target_os = "netbsd")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
O_NOSIGPIPE;
|
||||
/// Obtain a file descriptor for low-level access.
|
||||
///
|
||||
/// The file itself is not opened and other file operations will fail.
|
||||
#[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
O_PATH;
|
||||
/// Only allow reading.
|
||||
///
|
||||
/// This should not be combined with `O_WRONLY` or `O_RDWR`.
|
||||
O_RDONLY;
|
||||
/// Allow both reading and writing.
|
||||
///
|
||||
/// This should not be combined with `O_WRONLY` or `O_RDONLY`.
|
||||
O_RDWR;
|
||||
/// Similar to `O_DSYNC` but applies to `read`s instead.
|
||||
#[cfg(any(target_os = "linux", target_os = "netbsd", target_os = "openbsd"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
O_RSYNC;
|
||||
/// Skip search permission checks.
|
||||
#[cfg(target_os = "netbsd")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
O_SEARCH;
|
||||
/// Open with a shared file lock.
|
||||
#[cfg(any(target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "ios",
|
||||
target_os = "macos",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "redox"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
O_SHLOCK;
|
||||
/// Implicitly follow each `write()` with an `fsync()`.
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
O_SYNC;
|
||||
/// Create an unnamed temporary file.
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
O_TMPFILE;
|
||||
/// Truncate an existing regular file to 0 length if it allows writing.
|
||||
O_TRUNC;
|
||||
/// Restore default TTY attributes.
|
||||
#[cfg(target_os = "freebsd")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
O_TTY_INIT;
|
||||
/// Only allow writing.
|
||||
///
|
||||
/// This should not be combined with `O_RDONLY` or `O_RDWR`.
|
||||
O_WRONLY;
|
||||
}
|
||||
);
|
||||
|
||||
feature! {
|
||||
#![feature = "fs"]
|
||||
|
||||
// The conversion is not identical on all operating systems.
|
||||
#[allow(clippy::useless_conversion)]
|
||||
pub fn open<P: ?Sized + NixPath>(path: &P, oflag: OFlag, mode: Mode) -> Result<RawFd> {
|
||||
let fd = path.with_nix_path(|cstr| {
|
||||
unsafe { libc::open(cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) }
|
||||
})?;
|
||||
|
||||
Errno::result(fd)
|
||||
}
|
||||
|
||||
// The conversion is not identical on all operating systems.
|
||||
#[allow(clippy::useless_conversion)]
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
pub fn openat<P: ?Sized + NixPath>(
|
||||
dirfd: RawFd,
|
||||
path: &P,
|
||||
oflag: OFlag,
|
||||
mode: Mode,
|
||||
) -> Result<RawFd> {
|
||||
let fd = path.with_nix_path(|cstr| {
|
||||
unsafe { libc::openat(dirfd, cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) }
|
||||
})?;
|
||||
Errno::result(fd)
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
pub fn renameat<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
|
||||
old_dirfd: Option<RawFd>,
|
||||
old_path: &P1,
|
||||
new_dirfd: Option<RawFd>,
|
||||
new_path: &P2,
|
||||
) -> Result<()> {
|
||||
let res = old_path.with_nix_path(|old_cstr| {
|
||||
new_path.with_nix_path(|new_cstr| unsafe {
|
||||
libc::renameat(
|
||||
at_rawfd(old_dirfd),
|
||||
old_cstr.as_ptr(),
|
||||
at_rawfd(new_dirfd),
|
||||
new_cstr.as_ptr(),
|
||||
)
|
||||
})
|
||||
})??;
|
||||
Errno::result(res).map(drop)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(target_os = "linux", target_env = "gnu",))]
|
||||
#[cfg(feature = "fs")]
|
||||
libc_bitflags! {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
|
||||
pub struct RenameFlags: u32 {
|
||||
RENAME_EXCHANGE;
|
||||
RENAME_NOREPLACE;
|
||||
RENAME_WHITEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
feature! {
|
||||
#![feature = "fs"]
|
||||
#[cfg(all(
|
||||
target_os = "linux",
|
||||
target_env = "gnu",
|
||||
))]
|
||||
pub fn renameat2<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
|
||||
old_dirfd: Option<RawFd>,
|
||||
old_path: &P1,
|
||||
new_dirfd: Option<RawFd>,
|
||||
new_path: &P2,
|
||||
flags: RenameFlags,
|
||||
) -> Result<()> {
|
||||
let res = old_path.with_nix_path(|old_cstr| {
|
||||
new_path.with_nix_path(|new_cstr| unsafe {
|
||||
libc::renameat2(
|
||||
at_rawfd(old_dirfd),
|
||||
old_cstr.as_ptr(),
|
||||
at_rawfd(new_dirfd),
|
||||
new_cstr.as_ptr(),
|
||||
flags.bits(),
|
||||
)
|
||||
})
|
||||
})??;
|
||||
Errno::result(res).map(drop)
|
||||
}
|
||||
|
||||
fn wrap_readlink_result(mut v: Vec<u8>, len: ssize_t) -> Result<OsString> {
|
||||
unsafe { v.set_len(len as usize) }
|
||||
v.shrink_to_fit();
|
||||
Ok(OsString::from_vec(v.to_vec()))
|
||||
}
|
||||
|
||||
fn readlink_maybe_at<P: ?Sized + NixPath>(
|
||||
dirfd: Option<RawFd>,
|
||||
path: &P,
|
||||
v: &mut Vec<u8>,
|
||||
) -> Result<libc::ssize_t> {
|
||||
path.with_nix_path(|cstr| unsafe {
|
||||
match dirfd {
|
||||
#[cfg(target_os = "redox")]
|
||||
Some(_) => unreachable!(),
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
Some(dirfd) => libc::readlinkat(
|
||||
dirfd,
|
||||
cstr.as_ptr(),
|
||||
v.as_mut_ptr() as *mut c_char,
|
||||
v.capacity() as size_t,
|
||||
),
|
||||
None => libc::readlink(
|
||||
cstr.as_ptr(),
|
||||
v.as_mut_ptr() as *mut c_char,
|
||||
v.capacity() as size_t,
|
||||
),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn inner_readlink<P: ?Sized + NixPath>(dirfd: Option<RawFd>, path: &P) -> Result<OsString> {
|
||||
let mut v = Vec::with_capacity(libc::PATH_MAX as usize);
|
||||
// simple case: result is strictly less than `PATH_MAX`
|
||||
let res = readlink_maybe_at(dirfd, path, &mut v)?;
|
||||
let len = Errno::result(res)?;
|
||||
debug_assert!(len >= 0);
|
||||
if (len as usize) < v.capacity() {
|
||||
return wrap_readlink_result(v, res);
|
||||
}
|
||||
// Uh oh, the result is too long...
|
||||
// Let's try to ask lstat how many bytes to allocate.
|
||||
let reported_size = match dirfd {
|
||||
#[cfg(target_os = "redox")]
|
||||
Some(_) => unreachable!(),
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
Some(dirfd) => {
|
||||
let flags = if path.is_empty() { AtFlags::AT_EMPTY_PATH } else { AtFlags::empty() };
|
||||
super::sys::stat::fstatat(dirfd, path, flags | AtFlags::AT_SYMLINK_NOFOLLOW)
|
||||
},
|
||||
#[cfg(not(any(target_os = "android", target_os = "linux", target_os = "redox")))]
|
||||
Some(dirfd) => super::sys::stat::fstatat(dirfd, path, AtFlags::AT_SYMLINK_NOFOLLOW),
|
||||
None => super::sys::stat::lstat(path)
|
||||
}
|
||||
.map(|x| x.st_size)
|
||||
.unwrap_or(0);
|
||||
let mut try_size = if reported_size > 0 {
|
||||
// Note: even if `lstat`'s apparently valid answer turns out to be
|
||||
// wrong, we will still read the full symlink no matter what.
|
||||
reported_size as usize + 1
|
||||
} else {
|
||||
// If lstat doesn't cooperate, or reports an error, be a little less
|
||||
// precise.
|
||||
(libc::PATH_MAX as usize).max(128) << 1
|
||||
};
|
||||
loop {
|
||||
v.reserve_exact(try_size);
|
||||
let res = readlink_maybe_at(dirfd, path, &mut v)?;
|
||||
let len = Errno::result(res)?;
|
||||
debug_assert!(len >= 0);
|
||||
if (len as usize) < v.capacity() {
|
||||
break wrap_readlink_result(v, res);
|
||||
} else {
|
||||
// Ugh! Still not big enough!
|
||||
match try_size.checked_shl(1) {
|
||||
Some(next_size) => try_size = next_size,
|
||||
// It's absurd that this would happen, but handle it sanely
|
||||
// anyway.
|
||||
None => break Err(Errno::ENAMETOOLONG),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn readlink<P: ?Sized + NixPath>(path: &P) -> Result<OsString> {
|
||||
inner_readlink(None, path)
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
pub fn readlinkat<P: ?Sized + NixPath>(dirfd: RawFd, path: &P) -> Result<OsString> {
|
||||
inner_readlink(Some(dirfd), path)
|
||||
}
|
||||
|
||||
/// Computes the raw fd consumed by a function of the form `*at`.
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
pub(crate) fn at_rawfd(fd: Option<RawFd>) -> raw::c_int {
|
||||
match fd {
|
||||
None => libc::AT_FDCWD,
|
||||
Some(fd) => fd,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
|
||||
#[cfg(feature = "fs")]
|
||||
libc_bitflags!(
|
||||
/// Additional flags for file sealing, which allows for limiting operations on a file.
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
|
||||
pub struct SealFlag: c_int {
|
||||
/// Prevents further calls to `fcntl()` with `F_ADD_SEALS`.
|
||||
F_SEAL_SEAL;
|
||||
/// The file cannot be reduced in size.
|
||||
F_SEAL_SHRINK;
|
||||
/// The size of the file cannot be increased.
|
||||
F_SEAL_GROW;
|
||||
/// The file contents cannot be modified.
|
||||
F_SEAL_WRITE;
|
||||
}
|
||||
);
|
||||
|
||||
#[cfg(feature = "fs")]
|
||||
libc_bitflags!(
|
||||
/// Additional configuration flags for `fcntl`'s `F_SETFD`.
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
|
||||
pub struct FdFlag: c_int {
|
||||
/// The file descriptor will automatically be closed during a successful `execve(2)`.
|
||||
FD_CLOEXEC;
|
||||
}
|
||||
);
|
||||
|
||||
feature! {
|
||||
#![feature = "fs"]
|
||||
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
#[derive(Debug, Eq, Hash, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
pub enum FcntlArg<'a> {
|
||||
F_DUPFD(RawFd),
|
||||
F_DUPFD_CLOEXEC(RawFd),
|
||||
F_GETFD,
|
||||
F_SETFD(FdFlag), // FD_FLAGS
|
||||
F_GETFL,
|
||||
F_SETFL(OFlag), // O_NONBLOCK
|
||||
F_SETLK(&'a libc::flock),
|
||||
F_SETLKW(&'a libc::flock),
|
||||
F_GETLK(&'a mut libc::flock),
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
F_OFD_SETLK(&'a libc::flock),
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
F_OFD_SETLKW(&'a libc::flock),
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
F_OFD_GETLK(&'a mut libc::flock),
|
||||
#[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
|
||||
F_ADD_SEALS(SealFlag),
|
||||
#[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
|
||||
F_GET_SEALS,
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
F_FULLFSYNC,
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
F_GETPIPE_SZ,
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
F_SETPIPE_SZ(c_int),
|
||||
// TODO: Rest of flags
|
||||
}
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
pub enum FcntlArg {
|
||||
F_DUPFD(RawFd),
|
||||
F_DUPFD_CLOEXEC(RawFd),
|
||||
F_GETFD,
|
||||
F_SETFD(FdFlag), // FD_FLAGS
|
||||
F_GETFL,
|
||||
F_SETFL(OFlag), // O_NONBLOCK
|
||||
}
|
||||
pub use self::FcntlArg::*;
|
||||
|
||||
// TODO: Figure out how to handle value fcntl returns
|
||||
pub fn fcntl(fd: RawFd, arg: FcntlArg) -> Result<c_int> {
|
||||
let res = unsafe {
|
||||
match arg {
|
||||
F_DUPFD(rawfd) => libc::fcntl(fd, libc::F_DUPFD, rawfd),
|
||||
F_DUPFD_CLOEXEC(rawfd) => libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, rawfd),
|
||||
F_GETFD => libc::fcntl(fd, libc::F_GETFD),
|
||||
F_SETFD(flag) => libc::fcntl(fd, libc::F_SETFD, flag.bits()),
|
||||
F_GETFL => libc::fcntl(fd, libc::F_GETFL),
|
||||
F_SETFL(flag) => libc::fcntl(fd, libc::F_SETFL, flag.bits()),
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
F_SETLK(flock) => libc::fcntl(fd, libc::F_SETLK, flock),
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
F_SETLKW(flock) => libc::fcntl(fd, libc::F_SETLKW, flock),
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
F_GETLK(flock) => libc::fcntl(fd, libc::F_GETLK, flock),
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
F_OFD_SETLK(flock) => libc::fcntl(fd, libc::F_OFD_SETLK, flock),
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
F_OFD_SETLKW(flock) => libc::fcntl(fd, libc::F_OFD_SETLKW, flock),
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
F_OFD_GETLK(flock) => libc::fcntl(fd, libc::F_OFD_GETLK, flock),
|
||||
#[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
|
||||
F_ADD_SEALS(flag) => libc::fcntl(fd, libc::F_ADD_SEALS, flag.bits()),
|
||||
#[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
|
||||
F_GET_SEALS => libc::fcntl(fd, libc::F_GET_SEALS),
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
F_FULLFSYNC => libc::fcntl(fd, libc::F_FULLFSYNC),
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
F_GETPIPE_SZ => libc::fcntl(fd, libc::F_GETPIPE_SZ),
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
F_SETPIPE_SZ(size) => libc::fcntl(fd, libc::F_SETPIPE_SZ, size),
|
||||
}
|
||||
};
|
||||
|
||||
Errno::result(res)
|
||||
}
|
||||
|
||||
// TODO: convert to libc_enum
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
pub enum FlockArg {
|
||||
LockShared,
|
||||
LockExclusive,
|
||||
Unlock,
|
||||
LockSharedNonblock,
|
||||
LockExclusiveNonblock,
|
||||
UnlockNonblock,
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
pub fn flock(fd: RawFd, arg: FlockArg) -> Result<()> {
|
||||
use self::FlockArg::*;
|
||||
|
||||
let res = unsafe {
|
||||
match arg {
|
||||
LockShared => libc::flock(fd, libc::LOCK_SH),
|
||||
LockExclusive => libc::flock(fd, libc::LOCK_EX),
|
||||
Unlock => libc::flock(fd, libc::LOCK_UN),
|
||||
LockSharedNonblock => libc::flock(fd, libc::LOCK_SH | libc::LOCK_NB),
|
||||
LockExclusiveNonblock => libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB),
|
||||
UnlockNonblock => libc::flock(fd, libc::LOCK_UN | libc::LOCK_NB),
|
||||
}
|
||||
};
|
||||
|
||||
Errno::result(res).map(drop)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg(feature = "zerocopy")]
|
||||
libc_bitflags! {
|
||||
/// Additional flags to `splice` and friends.
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "zerocopy")))]
|
||||
pub struct SpliceFFlags: c_uint {
|
||||
/// Request that pages be moved instead of copied.
|
||||
///
|
||||
/// Not applicable to `vmsplice`.
|
||||
SPLICE_F_MOVE;
|
||||
/// Do not block on I/O.
|
||||
SPLICE_F_NONBLOCK;
|
||||
/// Hint that more data will be coming in a subsequent splice.
|
||||
///
|
||||
/// Not applicable to `vmsplice`.
|
||||
SPLICE_F_MORE;
|
||||
/// Gift the user pages to the kernel.
|
||||
///
|
||||
/// Not applicable to `splice`.
|
||||
SPLICE_F_GIFT;
|
||||
}
|
||||
}
|
||||
|
||||
feature! {
|
||||
#![feature = "zerocopy"]
|
||||
|
||||
/// Copy a range of data from one file to another
|
||||
///
|
||||
/// The `copy_file_range` system call performs an in-kernel copy between
|
||||
/// file descriptors `fd_in` and `fd_out` without the additional cost of
|
||||
/// transferring data from the kernel to user space and then back into the
|
||||
/// kernel. It copies up to `len` bytes of data from file descriptor `fd_in` to
|
||||
/// file descriptor `fd_out`, overwriting any data that exists within the
|
||||
/// requested range of the target file.
|
||||
///
|
||||
/// If the `off_in` and/or `off_out` arguments are used, the values
|
||||
/// will be mutated to reflect the new position within the file after
|
||||
/// copying. If they are not used, the relevant filedescriptors will be seeked
|
||||
/// to the new position.
|
||||
///
|
||||
/// On successful completion the number of bytes actually copied will be
|
||||
/// returned.
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
pub fn copy_file_range(
|
||||
fd_in: RawFd,
|
||||
off_in: Option<&mut libc::loff_t>,
|
||||
fd_out: RawFd,
|
||||
off_out: Option<&mut libc::loff_t>,
|
||||
len: usize,
|
||||
) -> Result<usize> {
|
||||
let off_in = off_in
|
||||
.map(|offset| offset as *mut libc::loff_t)
|
||||
.unwrap_or(ptr::null_mut());
|
||||
let off_out = off_out
|
||||
.map(|offset| offset as *mut libc::loff_t)
|
||||
.unwrap_or(ptr::null_mut());
|
||||
|
||||
let ret = unsafe {
|
||||
libc::syscall(
|
||||
libc::SYS_copy_file_range,
|
||||
fd_in,
|
||||
off_in,
|
||||
fd_out,
|
||||
off_out,
|
||||
len,
|
||||
0,
|
||||
)
|
||||
};
|
||||
Errno::result(ret).map(|r| r as usize)
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
pub fn splice(
|
||||
fd_in: RawFd,
|
||||
off_in: Option<&mut libc::loff_t>,
|
||||
fd_out: RawFd,
|
||||
off_out: Option<&mut libc::loff_t>,
|
||||
len: usize,
|
||||
flags: SpliceFFlags,
|
||||
) -> Result<usize> {
|
||||
let off_in = off_in
|
||||
.map(|offset| offset as *mut libc::loff_t)
|
||||
.unwrap_or(ptr::null_mut());
|
||||
let off_out = off_out
|
||||
.map(|offset| offset as *mut libc::loff_t)
|
||||
.unwrap_or(ptr::null_mut());
|
||||
|
||||
let ret = unsafe { libc::splice(fd_in, off_in, fd_out, off_out, len, flags.bits()) };
|
||||
Errno::result(ret).map(|r| r as usize)
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
pub fn tee(fd_in: RawFd, fd_out: RawFd, len: usize, flags: SpliceFFlags) -> Result<usize> {
|
||||
let ret = unsafe { libc::tee(fd_in, fd_out, len, flags.bits()) };
|
||||
Errno::result(ret).map(|r| r as usize)
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
pub fn vmsplice(
|
||||
fd: RawFd,
|
||||
iov: &[std::io::IoSlice<'_>],
|
||||
flags: SpliceFFlags
|
||||
) -> Result<usize>
|
||||
{
|
||||
let ret = unsafe {
|
||||
libc::vmsplice(
|
||||
fd,
|
||||
iov.as_ptr() as *const libc::iovec,
|
||||
iov.len(),
|
||||
flags.bits(),
|
||||
)
|
||||
};
|
||||
Errno::result(ret).map(|r| r as usize)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux"))]
|
||||
#[cfg(feature = "fs")]
|
||||
libc_bitflags!(
|
||||
/// Mode argument flags for fallocate determining operation performed on a given range.
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
|
||||
pub struct FallocateFlags: c_int {
|
||||
/// File size is not changed.
|
||||
///
|
||||
/// offset + len can be greater than file size.
|
||||
FALLOC_FL_KEEP_SIZE;
|
||||
/// Deallocates space by creating a hole.
|
||||
///
|
||||
/// Must be ORed with FALLOC_FL_KEEP_SIZE. Byte range starts at offset and continues for len bytes.
|
||||
FALLOC_FL_PUNCH_HOLE;
|
||||
/// Removes byte range from a file without leaving a hole.
|
||||
///
|
||||
/// Byte range to collapse starts at offset and continues for len bytes.
|
||||
FALLOC_FL_COLLAPSE_RANGE;
|
||||
/// Zeroes space in specified byte range.
|
||||
///
|
||||
/// Byte range starts at offset and continues for len bytes.
|
||||
FALLOC_FL_ZERO_RANGE;
|
||||
/// Increases file space by inserting a hole within the file size.
|
||||
///
|
||||
/// Does not overwrite existing data. Hole starts at offset and continues for len bytes.
|
||||
FALLOC_FL_INSERT_RANGE;
|
||||
/// Shared file data extants are made private to the file.
|
||||
///
|
||||
/// Gaurantees that a subsequent write will not fail due to lack of space.
|
||||
FALLOC_FL_UNSHARE_RANGE;
|
||||
}
|
||||
);
|
||||
|
||||
feature! {
|
||||
#![feature = "fs"]
|
||||
|
||||
/// Manipulates file space.
|
||||
///
|
||||
/// Allows the caller to directly manipulate the allocated disk space for the
|
||||
/// file referred to by fd.
|
||||
#[cfg(any(target_os = "linux"))]
|
||||
#[cfg(feature = "fs")]
|
||||
pub fn fallocate(
|
||||
fd: RawFd,
|
||||
mode: FallocateFlags,
|
||||
offset: libc::off_t,
|
||||
len: libc::off_t,
|
||||
) -> Result<()> {
|
||||
let res = unsafe { libc::fallocate(fd, mode.bits(), offset, len) };
|
||||
Errno::result(res).map(drop)
|
||||
}
|
||||
|
||||
/// Argument to [`fspacectl`] describing the range to zero. The first member is
|
||||
/// the file offset, and the second is the length of the region.
|
||||
#[cfg(any(target_os = "freebsd"))]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub struct SpacectlRange(pub libc::off_t, pub libc::off_t);
|
||||
|
||||
#[cfg(any(target_os = "freebsd"))]
|
||||
impl SpacectlRange {
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.1 == 0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len(&self) -> libc::off_t {
|
||||
self.1
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn offset(&self) -> libc::off_t {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Punch holes in a file.
|
||||
///
|
||||
/// `fspacectl` instructs the file system to deallocate a portion of a file.
|
||||
/// After a successful operation, this region of the file will return all zeroes
|
||||
/// if read. If the file system supports deallocation, then it may free the
|
||||
/// underlying storage, too.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `fd` - File to operate on
|
||||
/// - `range.0` - File offset at which to begin deallocation
|
||||
/// - `range.1` - Length of the region to deallocate
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// The operation may deallocate less than the entire requested region. On
|
||||
/// success, it returns the region that still remains to be deallocated. The
|
||||
/// caller should loop until the returned region is empty.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
#[cfg_attr(fbsd14, doc = " ```")]
|
||||
#[cfg_attr(not(fbsd14), doc = " ```no_run")]
|
||||
/// # use std::io::Write;
|
||||
/// # use std::os::unix::fs::FileExt;
|
||||
/// # use std::os::unix::io::AsRawFd;
|
||||
/// # use nix::fcntl::*;
|
||||
/// # use tempfile::tempfile;
|
||||
/// const INITIAL: &[u8] = b"0123456789abcdef";
|
||||
/// let mut f = tempfile().unwrap();
|
||||
/// f.write_all(INITIAL).unwrap();
|
||||
/// let mut range = SpacectlRange(3, 6);
|
||||
/// while (!range.is_empty()) {
|
||||
/// range = fspacectl(f.as_raw_fd(), range).unwrap();
|
||||
/// }
|
||||
/// let mut buf = vec![0; INITIAL.len()];
|
||||
/// f.read_exact_at(&mut buf, 0).unwrap();
|
||||
/// assert_eq!(buf, b"012\0\0\0\0\0\09abcdef");
|
||||
/// ```
|
||||
#[cfg(target_os = "freebsd")]
|
||||
pub fn fspacectl(fd: RawFd, range: SpacectlRange) -> Result<SpacectlRange> {
|
||||
let mut rqsr = libc::spacectl_range{r_offset: range.0, r_len: range.1};
|
||||
let res = unsafe { libc::fspacectl(
|
||||
fd,
|
||||
libc::SPACECTL_DEALLOC, // Only one command is supported ATM
|
||||
&rqsr,
|
||||
0, // No flags are currently supported
|
||||
&mut rqsr
|
||||
)};
|
||||
Errno::result(res).map(|_| SpacectlRange(rqsr.r_offset, rqsr.r_len))
|
||||
}
|
||||
|
||||
/// Like [`fspacectl`], but will never return incomplete.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `fd` - File to operate on
|
||||
/// - `offset` - File offset at which to begin deallocation
|
||||
/// - `len` - Length of the region to deallocate
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns `()` on success. On failure, the region may or may not be partially
|
||||
/// deallocated.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
#[cfg_attr(fbsd14, doc = " ```")]
|
||||
#[cfg_attr(not(fbsd14), doc = " ```no_run")]
|
||||
/// # use std::io::Write;
|
||||
/// # use std::os::unix::fs::FileExt;
|
||||
/// # use std::os::unix::io::AsRawFd;
|
||||
/// # use nix::fcntl::*;
|
||||
/// # use tempfile::tempfile;
|
||||
/// const INITIAL: &[u8] = b"0123456789abcdef";
|
||||
/// let mut f = tempfile().unwrap();
|
||||
/// f.write_all(INITIAL).unwrap();
|
||||
/// fspacectl_all(f.as_raw_fd(), 3, 6).unwrap();
|
||||
/// let mut buf = vec![0; INITIAL.len()];
|
||||
/// f.read_exact_at(&mut buf, 0).unwrap();
|
||||
/// assert_eq!(buf, b"012\0\0\0\0\0\09abcdef");
|
||||
/// ```
|
||||
#[cfg(target_os = "freebsd")]
|
||||
pub fn fspacectl_all(fd: RawFd, offset: libc::off_t, len: libc::off_t)
|
||||
-> Result<()>
|
||||
{
|
||||
let mut rqsr = libc::spacectl_range{r_offset: offset, r_len: len};
|
||||
while rqsr.r_len > 0 {
|
||||
let res = unsafe { libc::fspacectl(
|
||||
fd,
|
||||
libc::SPACECTL_DEALLOC, // Only one command is supported ATM
|
||||
&rqsr,
|
||||
0, // No flags are currently supported
|
||||
&mut rqsr
|
||||
)};
|
||||
Errno::result(res)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "linux",
|
||||
target_os = "android",
|
||||
target_os = "emscripten",
|
||||
target_os = "fuchsia",
|
||||
target_os = "wasi",
|
||||
target_env = "uclibc",
|
||||
target_os = "freebsd"
|
||||
))]
|
||||
mod posix_fadvise {
|
||||
use crate::errno::Errno;
|
||||
use std::os::unix::io::RawFd;
|
||||
use crate::Result;
|
||||
|
||||
#[cfg(feature = "fs")]
|
||||
libc_enum! {
|
||||
#[repr(i32)]
|
||||
#[non_exhaustive]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
|
||||
pub enum PosixFadviseAdvice {
|
||||
POSIX_FADV_NORMAL,
|
||||
POSIX_FADV_SEQUENTIAL,
|
||||
POSIX_FADV_RANDOM,
|
||||
POSIX_FADV_NOREUSE,
|
||||
POSIX_FADV_WILLNEED,
|
||||
POSIX_FADV_DONTNEED,
|
||||
}
|
||||
}
|
||||
|
||||
feature! {
|
||||
#![feature = "fs"]
|
||||
pub fn posix_fadvise(
|
||||
fd: RawFd,
|
||||
offset: libc::off_t,
|
||||
len: libc::off_t,
|
||||
advice: PosixFadviseAdvice,
|
||||
) -> Result<()> {
|
||||
let res = unsafe { libc::posix_fadvise(fd, offset, len, advice as libc::c_int) };
|
||||
|
||||
if res == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Errno::from_i32(res))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "linux",
|
||||
target_os = "android",
|
||||
target_os = "dragonfly",
|
||||
target_os = "emscripten",
|
||||
target_os = "fuchsia",
|
||||
target_os = "wasi",
|
||||
target_os = "freebsd"
|
||||
))]
|
||||
pub fn posix_fallocate(fd: RawFd, offset: libc::off_t, len: libc::off_t) -> Result<()> {
|
||||
let res = unsafe { libc::posix_fallocate(fd, offset, len) };
|
||||
match Errno::result(res) {
|
||||
Err(err) => Err(err),
|
||||
Ok(0) => Ok(()),
|
||||
Ok(errno) => Err(Errno::from_i32(errno)),
|
||||
}
|
||||
}
|
||||
}
|
||||
124
third-party/vendor/nix/src/features.rs
vendored
Normal file
124
third-party/vendor/nix/src/features.rs
vendored
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
//! Feature tests for OS functionality
|
||||
pub use self::os::*;
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
mod os {
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use crate::sys::utsname::uname;
|
||||
use crate::Result;
|
||||
|
||||
// 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 mut KERNEL_VERS: usize = 0;
|
||||
|
||||
unsafe {
|
||||
if KERNEL_VERS == 0 {
|
||||
KERNEL_VERS = parse_kernel_version()?;
|
||||
}
|
||||
|
||||
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]
|
||||
pub fn test_parsing_kernel_version() {
|
||||
assert!(kernel_version().unwrap() > 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "dragonfly", // Since ???
|
||||
target_os = "freebsd", // Since 10.0
|
||||
target_os = "illumos", // Since ???
|
||||
target_os = "netbsd", // Since 6.0
|
||||
target_os = "openbsd", // Since 5.7
|
||||
target_os = "redox", // Since 1-july-2020
|
||||
))]
|
||||
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 = "macos",
|
||||
target_os = "ios",
|
||||
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
|
||||
}
|
||||
}
|
||||
213
third-party/vendor/nix/src/ifaddrs.rs
vendored
Normal file
213
third-party/vendor/nix/src/ifaddrs.rs
vendored
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
//! 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(any(target_os = "ios", target_os = "macos"))]
|
||||
use std::convert::TryFrom;
|
||||
use std::ffi;
|
||||
use std::iter::Iterator;
|
||||
use std::mem;
|
||||
use std::option::Option;
|
||||
|
||||
use crate::{Result, Errno};
|
||||
use crate::sys::socket::{SockaddrLike, SockaddrStorage};
|
||||
use crate::net::if_::*;
|
||||
|
||||
/// 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(target_os = "android", target_os = "emscripten", target_os = "fuchsia", target_os = "linux"))] {
|
||||
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(any(target_os = "ios", target_os = "macos"))]
|
||||
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();
|
||||
|
||||
// memcpy only sa_len bytes, assume the rest is zero
|
||||
std::ptr::copy_nonoverlapping(
|
||||
src_sock as *const u8,
|
||||
dst_sock.as_mut_ptr() as *mut u8,
|
||||
(*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();
|
||||
let dst_sock = dst_sock.assume_init();
|
||||
|
||||
let dst_sock_ptr =
|
||||
&dst_sock as *const libc::sockaddr_storage as *const libc::sockaddr;
|
||||
|
||||
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(any(target_os = "ios", target_os = "macos"))]
|
||||
let netmask = unsafe { workaround_xnu_bug(info) };
|
||||
#[cfg(not(any(target_os = "ios", target_os = "macos")))]
|
||||
let netmask =
|
||||
unsafe { SockaddrStorage::from_raw(info.ifa_netmask, None) };
|
||||
let mut addr = InterfaceAddress {
|
||||
interface_name: ifname.to_string_lossy().to_string(),
|
||||
flags: InterfaceFlags::from_bits_truncate(info.ifa_flags as i32),
|
||||
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?");
|
||||
}
|
||||
}
|
||||
122
third-party/vendor/nix/src/kmod.rs
vendored
Normal file
122
third-party/vendor/nix/src/kmod.rs
vendored
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
//! Load and unload kernel modules.
|
||||
//!
|
||||
//! For more details see
|
||||
|
||||
use std::ffi::CStr;
|
||||
use std::os::unix::io::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<T: AsRawFd>(fd: &T, param_values: &CStr, flags: ModuleInitFlags) -> Result<()> {
|
||||
let res = unsafe {
|
||||
libc::syscall(
|
||||
libc::SYS_finit_module,
|
||||
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 {
|
||||
O_NONBLOCK;
|
||||
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)
|
||||
}
|
||||
334
third-party/vendor/nix/src/lib.rs
vendored
Normal file
334
third-party/vendor/nix/src/lib.rs
vendored
Normal file
|
|
@ -0,0 +1,334 @@
|
|||
//! 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`
|
||||
//! * `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 my 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
|
||||
//! * `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)]
|
||||
#![cfg_attr(docsrs, doc(cfg(all())))]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![cfg_attr(test, deny(warnings))]
|
||||
#![recursion_limit = "500"]
|
||||
#![deny(unused)]
|
||||
#![allow(unused_macros)]
|
||||
#![cfg_attr(not(feature = "default"), 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)]
|
||||
|
||||
// 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;
|
||||
}
|
||||
#[allow(missing_docs)]
|
||||
pub mod fcntl;
|
||||
feature! {
|
||||
#![feature = "net"]
|
||||
|
||||
#[cfg(any(target_os = "android",
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "ios",
|
||||
target_os = "linux",
|
||||
target_os = "macos",
|
||||
target_os = "netbsd",
|
||||
target_os = "illumos",
|
||||
target_os = "openbsd"))]
|
||||
#[deny(missing_docs)]
|
||||
pub mod ifaddrs;
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
#[deny(missing_docs)]
|
||||
pub mod net;
|
||||
}
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
feature! {
|
||||
#![feature = "kmod"]
|
||||
#[allow(missing_docs)]
|
||||
pub mod kmod;
|
||||
}
|
||||
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
|
||||
feature! {
|
||||
#![feature = "mount"]
|
||||
pub mod mount;
|
||||
}
|
||||
#[cfg(any(
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "linux",
|
||||
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"]
|
||||
#[allow(missing_docs)]
|
||||
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 = "s390x", target_arch = "x86", target_arch = "x86_64")
|
||||
))]
|
||||
feature! {
|
||||
#![feature = "ucontext"]
|
||||
#[allow(missing_docs)]
|
||||
pub mod ucontext;
|
||||
}
|
||||
#[allow(missing_docs)]
|
||||
pub mod unistd;
|
||||
|
||||
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 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() as *mut u8;
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
328
third-party/vendor/nix/src/macros.rs
vendored
Normal file
328
third-party/vendor/nix/src/macros.rs
vendored
Normal file
|
|
@ -0,0 +1,328 @@
|
|||
// 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(any(target_os = "linux", target_os = "android"))]
|
||||
/// PROT_GROWSDOWN;
|
||||
/// #[cfg(any(target_os = "linux", target_os = "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! {
|
||||
$(#[$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(any(target_os = "linux", target_os = "android"))]
|
||||
/// PROT_GROWSDOWN,
|
||||
/// #[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
/// PROT_GROWSUP,
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
// Some targets don't use all rules.
|
||||
#[allow(unknown_lints)]
|
||||
#[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)]
|
||||
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)*
|
||||
}
|
||||
};
|
||||
}
|
||||
443
third-party/vendor/nix/src/mount/bsd.rs
vendored
Normal file
443
third-party/vendor/nix/src/mount/bsd.rs
vendored
Normal file
|
|
@ -0,0 +1,443 @@
|
|||
use crate::{
|
||||
Error,
|
||||
Errno,
|
||||
NixPath,
|
||||
Result,
|
||||
};
|
||||
use libc::{c_char, c_int, c_uint, c_void};
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
ffi::{CString, CStr},
|
||||
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"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MNT_GJOURNAL;
|
||||
/// MAC support for objects.
|
||||
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MNT_MULTILABEL;
|
||||
/// Disable read clustering.
|
||||
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MNT_NOCLUSTERR;
|
||||
/// Disable write clustering.
|
||||
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MNT_NOCLUSTERW;
|
||||
/// Enable NFS version 4 ACLs.
|
||||
#[cfg(target_os = "freebsd")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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(any(target_os = "dragonfly", target_os = "freebsd"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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(any(target_os = "macos", target_os = "freebsd"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MNT_SNAPSHOT;
|
||||
/// Using soft updates.
|
||||
#[cfg(any(
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MNT_SOFTDEP;
|
||||
/// Directories with the SUID bit set chown new files to their own
|
||||
/// owner.
|
||||
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MNT_SUIDDIR;
|
||||
/// All I/O to the file system should be done synchronously.
|
||||
MNT_SYNCHRONOUS;
|
||||
/// Union with underlying fs.
|
||||
#[cfg(any(
|
||||
target_os = "macos",
|
||||
target_os = "freebsd",
|
||||
target_os = "netbsd"
|
||||
))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MNT_NONBUSY;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
/// The Error type of [`Nmount::nmount`].
|
||||
///
|
||||
/// It wraps an [`Errno`], but also may contain an additional message returned
|
||||
/// by `nmount(2)`.
|
||||
#[derive(Debug)]
|
||||
pub struct NmountError {
|
||||
errno: Error,
|
||||
errmsg: Option<String>
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for NmountError {}
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NmountError> for io::Error {
|
||||
fn from(err: NmountError) -> Self {
|
||||
err.errno.into()
|
||||
}
|
||||
}
|
||||
|
||||
/// Result type of [`Nmount::nmount`].
|
||||
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")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
#[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")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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() as *mut _,
|
||||
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() as *mut c_void,
|
||||
iov_len: errmsg.len(),
|
||||
});
|
||||
|
||||
let niov = self.iov.len() as c_uint;
|
||||
let iovp = self.iov.as_mut_ptr() as *mut libc::iovec;
|
||||
let res = unsafe {
|
||||
libc::nmount(iovp, niov, flags.bits)
|
||||
};
|
||||
match Errno::result(res) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(error) => {
|
||||
let errmsg = match errmsg.iter().position(|&x| x == 0) {
|
||||
None => None,
|
||||
Some(0) => None,
|
||||
Some(n) => {
|
||||
let sl = &errmsg[0..n + 1];
|
||||
Some(CStr::from_bytes_with_nul(sl).unwrap())
|
||||
}
|
||||
};
|
||||
Err(NmountError::new(error, errmsg))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "freebsd")]
|
||||
impl<'a> Drop for Nmount<'a> {
|
||||
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.
|
||||
/// * `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)
|
||||
}
|
||||
112
third-party/vendor/nix/src/mount/linux.rs
vendored
Normal file
112
third-party/vendor/nix/src/mount/linux.rs
vendored
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
#![allow(missing_docs)]
|
||||
use libc::{self, c_ulong, c_int};
|
||||
use crate::{Result, NixPath};
|
||||
use crate::errno::Errno;
|
||||
|
||||
libc_bitflags!(
|
||||
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;
|
||||
MS_MOVE;
|
||||
MS_REC;
|
||||
MS_SILENT;
|
||||
MS_POSIXACL;
|
||||
MS_UNBINDABLE;
|
||||
MS_PRIVATE;
|
||||
MS_SLAVE;
|
||||
MS_SHARED;
|
||||
MS_RELATIME;
|
||||
MS_KERNMOUNT;
|
||||
MS_I_VERSION;
|
||||
MS_STRICTATIME;
|
||||
MS_LAZYTIME;
|
||||
MS_ACTIVE;
|
||||
MS_NOUSER;
|
||||
MS_RMT_MASK;
|
||||
MS_MGC_VAL;
|
||||
MS_MGC_MSK;
|
||||
}
|
||||
);
|
||||
|
||||
libc_bitflags!(
|
||||
pub struct MntFlags: c_int {
|
||||
MNT_FORCE;
|
||||
MNT_DETACH;
|
||||
MNT_EXPIRE;
|
||||
UMOUNT_NOFOLLOW;
|
||||
}
|
||||
);
|
||||
|
||||
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<()> {
|
||||
|
||||
fn with_opt_nix_path<P, T, F>(p: Option<&P>, f: F) -> Result<T>
|
||||
where P: ?Sized + NixPath,
|
||||
F: FnOnce(*const libc::c_char) -> T
|
||||
{
|
||||
match p {
|
||||
Some(path) => path.with_nix_path(|p_str| f(p_str.as_ptr())),
|
||||
None => Ok(f(std::ptr::null()))
|
||||
}
|
||||
}
|
||||
|
||||
let res = with_opt_nix_path(source, |s| {
|
||||
target.with_nix_path(|t| {
|
||||
with_opt_nix_path(fstype, |ty| {
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
23
third-party/vendor/nix/src/mount/mod.rs
vendored
Normal file
23
third-party/vendor/nix/src/mount/mod.rs
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
//! Mount file systems
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
mod linux;
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
pub use self::linux::*;
|
||||
|
||||
#[cfg(any(target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "macos",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
mod bsd;
|
||||
|
||||
#[cfg(any(target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "macos",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
pub use self::bsd::*;
|
||||
248
third-party/vendor/nix/src/mqueue.rs
vendored
Normal file
248
third-party/vendor/nix/src/mqueue.rs
vendored
Normal file
|
|
@ -0,0 +1,248 @@
|
|||
//! 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= CString::new("/a_nix_test_queue").unwrap();
|
||||
//!
|
||||
//! 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::Result;
|
||||
use crate::errno::Errno;
|
||||
|
||||
use libc::{self, c_char, mqd_t, size_t};
|
||||
use std::ffi::CStr;
|
||||
use crate::sys::stat::Mode;
|
||||
use std::mem;
|
||||
|
||||
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"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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")))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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(name: &CStr,
|
||||
oflag: MQ_OFlag,
|
||||
mode: Mode,
|
||||
attr: Option<&MqAttr>)
|
||||
-> Result<MqdT> {
|
||||
let res = match attr {
|
||||
Some(mq_attr) => unsafe {
|
||||
libc::mq_open(name.as_ptr(),
|
||||
oflag.bits(),
|
||||
mode.bits() as libc::c_int,
|
||||
&mq_attr.mq_attr as *const libc::mq_attr)
|
||||
},
|
||||
None => unsafe { libc::mq_open(name.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(name: &CStr) -> Result<()> {
|
||||
let res = unsafe { libc::mq_unlink(name.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() as *mut c_char,
|
||||
len,
|
||||
msg_prio as *mut u32)
|
||||
};
|
||||
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() as *const c_char,
|
||||
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)
|
||||
}
|
||||
468
third-party/vendor/nix/src/net/if_.rs
vendored
Normal file
468
third-party/vendor/nix/src/net/if_.rs
vendored
Normal file
|
|
@ -0,0 +1,468 @@
|
|||
//! Network interface name resolution.
|
||||
//!
|
||||
//! Uses Linux and/or POSIX functions to resolve interface names like "eth0"
|
||||
//! or "socan1" into device numbers.
|
||||
|
||||
use crate::{Error, NixPath, Result};
|
||||
use libc::c_uint;
|
||||
|
||||
/// Resolve an interface into a 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)
|
||||
}
|
||||
}
|
||||
|
||||
libc_bitflags!(
|
||||
/// Standard interface flags, used by `getifaddrs`
|
||||
pub struct InterfaceFlags: libc::c_int {
|
||||
/// Interface is running. (see
|
||||
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
|
||||
IFF_UP;
|
||||
/// Valid broadcast address set. (see
|
||||
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
|
||||
IFF_BROADCAST;
|
||||
/// Internal debugging flag. (see
|
||||
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
|
||||
#[cfg(not(target_os = "haiku"))]
|
||||
IFF_DEBUG;
|
||||
/// Interface is a loopback interface. (see
|
||||
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
|
||||
IFF_LOOPBACK;
|
||||
/// Interface is a point-to-point link. (see
|
||||
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
|
||||
IFF_POINTOPOINT;
|
||||
/// Avoid use of trailers. (see
|
||||
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
|
||||
#[cfg(any(target_os = "android",
|
||||
target_os = "fuchsia",
|
||||
target_os = "ios",
|
||||
target_os = "linux",
|
||||
target_os = "macos",
|
||||
target_os = "netbsd",
|
||||
target_os = "illumos",
|
||||
target_os = "solaris"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_NOTRAILERS;
|
||||
/// Interface manages own routes.
|
||||
#[cfg(any(target_os = "dragonfly"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_SMART;
|
||||
/// Resources allocated. (see
|
||||
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
|
||||
#[cfg(any(target_os = "android",
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "fuchsia",
|
||||
target_os = "illumos",
|
||||
target_os = "ios",
|
||||
target_os = "linux",
|
||||
target_os = "macos",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "solaris"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_RUNNING;
|
||||
/// No arp protocol, L2 destination address not set. (see
|
||||
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
|
||||
IFF_NOARP;
|
||||
/// Interface is in promiscuous mode. (see
|
||||
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
|
||||
IFF_PROMISC;
|
||||
/// Receive all multicast packets. (see
|
||||
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
|
||||
IFF_ALLMULTI;
|
||||
/// Master of a load balancing bundle. (see
|
||||
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
|
||||
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_MASTER;
|
||||
/// transmission in progress, tx hardware queue is full
|
||||
#[cfg(any(target_os = "freebsd",
|
||||
target_os = "macos",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "ios"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_OACTIVE;
|
||||
/// Protocol code on board.
|
||||
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_INTELLIGENT;
|
||||
/// Slave of a load balancing bundle. (see
|
||||
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
|
||||
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_SLAVE;
|
||||
/// Can't hear own transmissions.
|
||||
#[cfg(any(target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "macos",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_SIMPLEX;
|
||||
/// Supports multicast. (see
|
||||
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
|
||||
IFF_MULTICAST;
|
||||
/// Per link layer defined bit.
|
||||
#[cfg(any(target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "macos",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "ios"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_LINK0;
|
||||
/// Multicast using broadcast.
|
||||
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_MULTI_BCAST;
|
||||
/// Is able to select media type via ifmap. (see
|
||||
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
|
||||
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_PORTSEL;
|
||||
/// Per link layer defined bit.
|
||||
#[cfg(any(target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "macos",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "ios"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_LINK1;
|
||||
/// Non-unique address.
|
||||
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_UNNUMBERED;
|
||||
/// Auto media selection active. (see
|
||||
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
|
||||
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_AUTOMEDIA;
|
||||
/// Per link layer defined bit.
|
||||
#[cfg(any(target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "macos",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "ios"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_LINK2;
|
||||
/// Use alternate physical connection.
|
||||
#[cfg(any(target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "macos",
|
||||
target_os = "ios"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_ALTPHYS;
|
||||
/// DHCP controls interface.
|
||||
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_DHCPRUNNING;
|
||||
/// The addresses are lost when the interface goes down. (see
|
||||
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
|
||||
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_DYNAMIC;
|
||||
/// Do not advertise.
|
||||
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_PRIVATE;
|
||||
/// Driver signals L1 up. Volatile.
|
||||
#[cfg(any(target_os = "fuchsia", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_LOWER_UP;
|
||||
/// Interface is in polling mode.
|
||||
#[cfg(any(target_os = "dragonfly"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_POLLING_COMPAT;
|
||||
/// Unconfigurable using ioctl(2).
|
||||
#[cfg(any(target_os = "freebsd"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_CANTCONFIG;
|
||||
/// Do not transmit packets.
|
||||
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_NOXMIT;
|
||||
/// Driver signals dormant. Volatile.
|
||||
#[cfg(any(target_os = "fuchsia", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_DORMANT;
|
||||
/// User-requested promisc mode.
|
||||
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_PPROMISC;
|
||||
/// Just on-link subnet.
|
||||
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_NOLOCAL;
|
||||
/// Echo sent packets. Volatile.
|
||||
#[cfg(any(target_os = "fuchsia", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_ECHO;
|
||||
/// User-requested monitor mode.
|
||||
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_MONITOR;
|
||||
/// Address is deprecated.
|
||||
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_DEPRECATED;
|
||||
/// Static ARP.
|
||||
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_STATICARP;
|
||||
/// Address from stateless addrconf.
|
||||
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_ADDRCONF;
|
||||
/// Interface is in polling mode.
|
||||
#[cfg(any(target_os = "dragonfly"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_NPOLLING;
|
||||
/// Router on interface.
|
||||
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_ROUTER;
|
||||
/// Interface is in polling mode.
|
||||
#[cfg(any(target_os = "dragonfly"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_IDIRECT;
|
||||
/// Interface is winding down
|
||||
#[cfg(any(target_os = "freebsd"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_DYING;
|
||||
/// No NUD on interface.
|
||||
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_NONUD;
|
||||
/// Interface is being renamed
|
||||
#[cfg(any(target_os = "freebsd"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_RENAMING;
|
||||
/// Anycast address.
|
||||
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_ANYCAST;
|
||||
/// Don't exchange routing info.
|
||||
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_NORTEXCH;
|
||||
/// Do not provide packet information
|
||||
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_NO_PI as libc::c_int;
|
||||
/// TUN device (no Ethernet headers)
|
||||
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_TUN as libc::c_int;
|
||||
/// TAP device
|
||||
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_TAP as libc::c_int;
|
||||
/// IPv4 interface.
|
||||
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_IPV4;
|
||||
/// IPv6 interface.
|
||||
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_IPV6;
|
||||
/// in.mpathd test address
|
||||
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_NOFAILOVER;
|
||||
/// Interface has failed
|
||||
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_FAILED;
|
||||
/// Interface is a hot-spare
|
||||
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_STANDBY;
|
||||
/// Functioning but not used
|
||||
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_INACTIVE;
|
||||
/// Interface is offline
|
||||
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_OFFLINE;
|
||||
#[cfg(target_os = "solaris")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_COS_ENABLED;
|
||||
/// Prefer as source addr.
|
||||
#[cfg(target_os = "solaris")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_PREFERRED;
|
||||
/// RFC3041
|
||||
#[cfg(target_os = "solaris")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_TEMPORARY;
|
||||
/// MTU set with SIOCSLIFMTU
|
||||
#[cfg(target_os = "solaris")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_FIXEDMTU;
|
||||
/// Cannot send / receive packets
|
||||
#[cfg(target_os = "solaris")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_VIRTUAL;
|
||||
/// Local address in use
|
||||
#[cfg(target_os = "solaris")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_DUPLICATE;
|
||||
/// IPMP IP interface
|
||||
#[cfg(target_os = "solaris")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
IFF_IPMP;
|
||||
}
|
||||
);
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "fuchsia",
|
||||
target_os = "ios",
|
||||
target_os = "linux",
|
||||
target_os = "macos",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd",
|
||||
))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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()`].
|
||||
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() as *const Interface;
|
||||
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(
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "fuchsia",
|
||||
target_os = "ios",
|
||||
target_os = "linux",
|
||||
target_os = "macos",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd",
|
||||
))]
|
||||
pub use if_nameindex::*;
|
||||
4
third-party/vendor/nix/src/net/mod.rs
vendored
Normal file
4
third-party/vendor/nix/src/net/mod.rs
vendored
Normal 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_;
|
||||
175
third-party/vendor/nix/src/poll.rs
vendored
Normal file
175
third-party/vendor/nix/src/poll.rs
vendored
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
//! Wait for events to trigger on specific file descriptors
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
|
||||
use crate::Result;
|
||||
use crate::errno::Errno;
|
||||
|
||||
/// 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`.
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct PollFd {
|
||||
pollfd: libc::pollfd,
|
||||
}
|
||||
|
||||
impl PollFd {
|
||||
/// Creates a new `PollFd` specifying the events of interest
|
||||
/// for a given file descriptor.
|
||||
pub const fn new(fd: RawFd, events: PollFlags) -> PollFd {
|
||||
PollFd {
|
||||
pollfd: libc::pollfd {
|
||||
fd,
|
||||
events: events.bits(),
|
||||
revents: PollFlags::empty().bits(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
||||
/// 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 AsRawFd for PollFd {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
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"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
POLLRDNORM;
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
/// Equivalent to [`POLLOUT`](constant.POLLOUT.html)
|
||||
POLLWRNORM;
|
||||
/// Priority band data can be read (generally unused on Linux).
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
POLLRDBAND;
|
||||
/// Priority data may be written.
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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 negative value
|
||||
/// in timeout means an infinite timeout. Specifying a timeout of zero
|
||||
/// causes `poll()` to return immediately, even if no file descriptors are
|
||||
/// ready.
|
||||
pub fn poll(fds: &mut [PollFd], timeout: libc::c_int) -> Result<libc::c_int> {
|
||||
let res = unsafe {
|
||||
libc::poll(fds.as_mut_ptr() as *mut libc::pollfd,
|
||||
fds.len() as libc::nfds_t,
|
||||
timeout)
|
||||
};
|
||||
|
||||
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(target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "linux"))]
|
||||
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() as *mut libc::pollfd,
|
||||
fds.len() as libc::nfds_t,
|
||||
timeout,
|
||||
sigmask)
|
||||
};
|
||||
Errno::result(res)
|
||||
}
|
||||
}
|
||||
371
third-party/vendor/nix/src/pty.rs
vendored
Normal file
371
third-party/vendor/nix/src/pty.rs
vendored
Normal file
|
|
@ -0,0 +1,371 @@
|
|||
//! 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;
|
||||
use std::mem;
|
||||
use std::os::unix::prelude::*;
|
||||
|
||||
use crate::sys::termios::Termios;
|
||||
#[cfg(feature = "process")]
|
||||
use crate::unistd::{ForkResult, Pid};
|
||||
use crate::{Result, fcntl, unistd};
|
||||
use crate::errno::Errno;
|
||||
|
||||
/// Representation of a master/slave pty pair
|
||||
///
|
||||
/// This is returned by `openpty`. Note that this type does *not* implement `Drop`, so the user
|
||||
/// must manually close the file descriptors.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct OpenptyResult {
|
||||
/// The master port in a virtual pty pair
|
||||
pub master: RawFd,
|
||||
/// The slave port in a virtual pty pair
|
||||
pub slave: RawFd,
|
||||
}
|
||||
|
||||
feature! {
|
||||
#![feature = "process"]
|
||||
/// Representation of a master with a forked pty
|
||||
///
|
||||
/// This is returned by `forkpty`. Note that this type does *not* implement `Drop`, so the user
|
||||
/// must manually close the file descriptors.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct ForkptyResult {
|
||||
/// The master port in a virtual pty pair
|
||||
pub master: RawFd,
|
||||
/// Metadata about forked process
|
||||
pub fork_result: ForkResult,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Representation of the Master device in a master/slave pty pair
|
||||
///
|
||||
/// While this datatype is a thin wrapper around `RawFd`, it enforces that the available PTY
|
||||
/// functions are given the correct file descriptor. Additionally this type implements `Drop`,
|
||||
/// so that when it's consumed or goes out of scope, it's automatically cleaned-up.
|
||||
#[derive(Debug, Eq, Hash, PartialEq)]
|
||||
pub struct PtyMaster(RawFd);
|
||||
|
||||
impl AsRawFd for PtyMaster {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoRawFd for PtyMaster {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
let fd = self.0;
|
||||
mem::forget(self);
|
||||
fd
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PtyMaster {
|
||||
fn drop(&mut self) {
|
||||
// On drop, we ignore errors like EINTR and EIO because there's no clear
|
||||
// way to handle them, we can't return anything, and (on FreeBSD at
|
||||
// least) the file descriptor is deallocated in these cases. However,
|
||||
// we must panic on EBADF, because it is always an error to close an
|
||||
// invalid file descriptor. That frequently indicates a double-close
|
||||
// condition, which can cause confusing errors for future I/O
|
||||
// operations.
|
||||
let e = unistd::close(self.0);
|
||||
if e == Err(Errno::EBADF) {
|
||||
panic!("Closing an invalid file descriptor!");
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
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(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 = libc::ptsname(fd.as_raw_fd());
|
||||
if name_ptr.is_null() {
|
||||
return Err(Errno::last());
|
||||
}
|
||||
|
||||
let name = 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(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
#[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]
|
||||
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: master.assume_init(),
|
||||
slave: slave.assume_init(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
feature! {
|
||||
#![feature = "process"]
|
||||
/// Create a new pseudoterminal, returning the master file descriptor and forked pid.
|
||||
/// in `ForkptyResult`
|
||||
/// (see [`forkpty`](https://man7.org/linux/man-pages/man3/forkpty.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`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// In a multithreaded program, only [async-signal-safe] functions like `pause`
|
||||
/// and `_exit` may be called by the child (the parent isn't restricted). Note
|
||||
/// that memory allocation may **not** be async-signal-safe and thus must be
|
||||
/// prevented.
|
||||
///
|
||||
/// Those functions are only a small subset of your operating system's API, so
|
||||
/// special care must be taken to only invoke code you can control and audit.
|
||||
///
|
||||
/// [async-signal-safe]: https://man7.org/linux/man-pages/man7/signal-safety.7.html
|
||||
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 = libc::forkpty(master.as_mut_ptr(), ptr::null_mut(), term, win);
|
||||
|
||||
let fork_result = Errno::result(res).map(|res| match res {
|
||||
0 => ForkResult::Child,
|
||||
res => ForkResult::Parent { child: Pid::from_raw(res) },
|
||||
})?;
|
||||
|
||||
Ok(ForkptyResult {
|
||||
master: master.assume_init(),
|
||||
fork_result,
|
||||
})
|
||||
}
|
||||
}
|
||||
294
third-party/vendor/nix/src/sched.rs
vendored
Normal file
294
third-party/vendor/nix/src/sched.rs
vendored
Normal file
|
|
@ -0,0 +1,294 @@
|
|||
//! Execution scheduling
|
||||
//!
|
||||
//! See Also
|
||||
//! [sched.h](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sched.h.html)
|
||||
use crate::{Errno, Result};
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
pub use self::sched_linux_like::*;
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
mod sched_linux_like {
|
||||
use crate::errno::Errno;
|
||||
use libc::{self, c_int, c_void};
|
||||
use std::mem;
|
||||
use std::option::Option;
|
||||
use std::os::unix::io::RawFd;
|
||||
use crate::unistd::Pid;
|
||||
use crate::Result;
|
||||
|
||||
// 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.
|
||||
pub 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 res = unsafe {
|
||||
let combined = flags.bits() | signal.unwrap_or(0);
|
||||
let ptr = stack.as_mut_ptr().add(stack.len());
|
||||
let ptr_aligned = ptr.sub(ptr as usize % 16);
|
||||
libc::clone(
|
||||
mem::transmute(
|
||||
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: RawFd, nstype: CloneFlags) -> Result<()> {
|
||||
let res = unsafe { libc::setns(fd, nstype.bits()) };
|
||||
|
||||
Errno::result(res).map(drop)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "linux"))]
|
||||
pub use self::sched_affinity::*;
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "linux"))]
|
||||
mod sched_affinity {
|
||||
use crate::errno::Errno;
|
||||
use std::mem;
|
||||
use crate::unistd::Pid;
|
||||
use crate::Result;
|
||||
|
||||
/// 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(C)]
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct CpuSet {
|
||||
cpu_set: libc::cpu_set_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 {
|
||||
8 * mem::size_of::<libc::cpu_set_t>()
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
1244
third-party/vendor/nix/src/sys/aio.rs
vendored
Normal file
1244
third-party/vendor/nix/src/sys/aio.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
108
third-party/vendor/nix/src/sys/epoll.rs
vendored
Normal file
108
third-party/vendor/nix/src/sys/epoll.rs
vendored
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
use crate::Result;
|
||||
use crate::errno::Errno;
|
||||
use libc::{self, c_int};
|
||||
use std::os::unix::io::RawFd;
|
||||
use std::ptr;
|
||||
use std::mem;
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn epoll_create() -> Result<RawFd> {
|
||||
let res = unsafe { libc::epoll_create(1024) };
|
||||
|
||||
Errno::result(res)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn epoll_create1(flags: EpollCreateFlags) -> Result<RawFd> {
|
||||
let res = unsafe { libc::epoll_create1(flags.bits()) };
|
||||
|
||||
Errno::result(res)
|
||||
}
|
||||
|
||||
#[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, ptr::null_mut())
|
||||
}
|
||||
};
|
||||
Errno::result(res).map(drop)
|
||||
}
|
||||
}
|
||||
|
||||
#[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() as *mut libc::epoll_event, events.len() as c_int, timeout_ms as c_int)
|
||||
};
|
||||
|
||||
Errno::result(res).map(|r| r as usize)
|
||||
}
|
||||
346
third-party/vendor/nix/src/sys/event.rs
vendored
Normal file
346
third-party/vendor/nix/src/sys/event.rs
vendored
Normal file
|
|
@ -0,0 +1,346 @@
|
|||
/* TOOD: Implement for other kqueue based systems
|
||||
*/
|
||||
|
||||
use crate::{Errno, Result};
|
||||
#[cfg(not(target_os = "netbsd"))]
|
||||
use libc::{timespec, time_t, c_int, c_long, intptr_t, uintptr_t};
|
||||
#[cfg(target_os = "netbsd")]
|
||||
use libc::{timespec, time_t, c_long, intptr_t, uintptr_t, size_t};
|
||||
use std::convert::TryInto;
|
||||
use std::mem;
|
||||
use std::os::unix::io::RawFd;
|
||||
use std::ptr;
|
||||
|
||||
// Redefine kevent in terms of programmer-friendly enums and bitfields.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct KEvent {
|
||||
kevent: libc::kevent,
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "dragonfly", target_os = "freebsd",
|
||||
target_os = "ios", target_os = "macos",
|
||||
target_os = "openbsd"))]
|
||||
type type_of_udata = *mut libc::c_void;
|
||||
#[cfg(any(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]
|
||||
pub enum EventFilter {
|
||||
EVFILT_AIO,
|
||||
/// Returns whenever there is no remaining data in the write buffer
|
||||
#[cfg(target_os = "freebsd")]
|
||||
EVFILT_EMPTY,
|
||||
#[cfg(target_os = "dragonfly")]
|
||||
EVFILT_EXCEPT,
|
||||
#[cfg(any(target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "ios",
|
||||
target_os = "macos"))]
|
||||
EVFILT_FS,
|
||||
#[cfg(target_os = "freebsd")]
|
||||
EVFILT_LIO,
|
||||
#[cfg(any(target_os = "ios", target_os = "macos"))]
|
||||
EVFILT_MACHPORT,
|
||||
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,
|
||||
EVFILT_READ,
|
||||
/// Returns whenever an asynchronous `sendfile()` call completes.
|
||||
#[cfg(target_os = "freebsd")]
|
||||
EVFILT_SENDFILE,
|
||||
EVFILT_SIGNAL,
|
||||
EVFILT_TIMER,
|
||||
#[cfg(any(target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "ios",
|
||||
target_os = "macos"))]
|
||||
EVFILT_USER,
|
||||
#[cfg(any(target_os = "ios", target_os = "macos"))]
|
||||
EVFILT_VM,
|
||||
EVFILT_VNODE,
|
||||
EVFILT_WRITE,
|
||||
}
|
||||
impl TryFrom<type_of_event_filter>
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "dragonfly", target_os = "freebsd",
|
||||
target_os = "ios", target_os = "macos",
|
||||
target_os = "openbsd"))]
|
||||
pub type type_of_event_flag = u16;
|
||||
#[cfg(any(target_os = "netbsd"))]
|
||||
pub type type_of_event_flag = u32;
|
||||
libc_bitflags!{
|
||||
pub struct EventFlag: type_of_event_flag {
|
||||
EV_ADD;
|
||||
EV_CLEAR;
|
||||
EV_DELETE;
|
||||
EV_DISABLE;
|
||||
#[cfg(any(target_os = "dragonfly", target_os = "freebsd",
|
||||
target_os = "ios", target_os = "macos",
|
||||
target_os = "netbsd", target_os = "openbsd"))]
|
||||
EV_DISPATCH;
|
||||
#[cfg(target_os = "freebsd")]
|
||||
EV_DROP;
|
||||
EV_ENABLE;
|
||||
EV_EOF;
|
||||
EV_ERROR;
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
EV_FLAG0;
|
||||
EV_FLAG1;
|
||||
#[cfg(target_os = "dragonfly")]
|
||||
EV_NODATA;
|
||||
EV_ONESHOT;
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
EV_OOBAND;
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
EV_POLL;
|
||||
#[cfg(any(target_os = "dragonfly", target_os = "freebsd",
|
||||
target_os = "ios", target_os = "macos",
|
||||
target_os = "netbsd", target_os = "openbsd"))]
|
||||
EV_RECEIPT;
|
||||
}
|
||||
}
|
||||
|
||||
libc_bitflags!(
|
||||
pub struct FilterFlag: u32 {
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
NOTE_ABSOLUTE;
|
||||
NOTE_ATTRIB;
|
||||
NOTE_CHILD;
|
||||
NOTE_DELETE;
|
||||
#[cfg(target_os = "openbsd")]
|
||||
NOTE_EOF;
|
||||
NOTE_EXEC;
|
||||
NOTE_EXIT;
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
NOTE_EXITSTATUS;
|
||||
NOTE_EXTEND;
|
||||
#[cfg(any(target_os = "macos",
|
||||
target_os = "ios",
|
||||
target_os = "freebsd",
|
||||
target_os = "dragonfly"))]
|
||||
NOTE_FFAND;
|
||||
#[cfg(any(target_os = "macos",
|
||||
target_os = "ios",
|
||||
target_os = "freebsd",
|
||||
target_os = "dragonfly"))]
|
||||
NOTE_FFCOPY;
|
||||
#[cfg(any(target_os = "macos",
|
||||
target_os = "ios",
|
||||
target_os = "freebsd",
|
||||
target_os = "dragonfly"))]
|
||||
NOTE_FFCTRLMASK;
|
||||
#[cfg(any(target_os = "macos",
|
||||
target_os = "ios",
|
||||
target_os = "freebsd",
|
||||
target_os = "dragonfly"))]
|
||||
NOTE_FFLAGSMASK;
|
||||
#[cfg(any(target_os = "macos",
|
||||
target_os = "ios",
|
||||
target_os = "freebsd",
|
||||
target_os = "dragonfly"))]
|
||||
NOTE_FFNOP;
|
||||
#[cfg(any(target_os = "macos",
|
||||
target_os = "ios",
|
||||
target_os = "freebsd",
|
||||
target_os = "dragonfly"))]
|
||||
NOTE_FFOR;
|
||||
NOTE_FORK;
|
||||
NOTE_LINK;
|
||||
NOTE_LOWAT;
|
||||
#[cfg(target_os = "freebsd")]
|
||||
NOTE_MSECONDS;
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
NOTE_NONE;
|
||||
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
|
||||
NOTE_NSECONDS;
|
||||
#[cfg(target_os = "dragonfly")]
|
||||
NOTE_OOB;
|
||||
NOTE_PCTRLMASK;
|
||||
NOTE_PDATAMASK;
|
||||
NOTE_RENAME;
|
||||
NOTE_REVOKE;
|
||||
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
|
||||
NOTE_SECONDS;
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
NOTE_SIGNAL;
|
||||
NOTE_TRACK;
|
||||
NOTE_TRACKERR;
|
||||
#[cfg(any(target_os = "macos",
|
||||
target_os = "ios",
|
||||
target_os = "freebsd",
|
||||
target_os = "dragonfly"))]
|
||||
NOTE_TRIGGER;
|
||||
#[cfg(target_os = "openbsd")]
|
||||
NOTE_TRUNCATE;
|
||||
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
|
||||
NOTE_USECONDS;
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
NOTE_VM_ERROR;
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
NOTE_VM_PRESSURE;
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
NOTE_VM_PRESSURE_SUDDEN_TERMINATE;
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
NOTE_VM_PRESSURE_TERMINATE;
|
||||
NOTE_WRITE;
|
||||
}
|
||||
);
|
||||
|
||||
pub fn kqueue() -> Result<RawFd> {
|
||||
let res = unsafe { libc::kqueue() };
|
||||
|
||||
Errno::result(res)
|
||||
}
|
||||
|
||||
|
||||
// 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.
|
||||
pub fn new(ident: uintptr_t, filter: EventFilter, flags: EventFlag,
|
||||
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() }
|
||||
} }
|
||||
}
|
||||
|
||||
pub fn ident(&self) -> uintptr_t {
|
||||
self.kevent.ident
|
||||
}
|
||||
|
||||
pub fn filter(&self) -> Result<EventFilter> {
|
||||
self.kevent.filter.try_into()
|
||||
}
|
||||
|
||||
pub fn flags(&self) -> EventFlag {
|
||||
EventFlag::from_bits(self.kevent.flags).unwrap()
|
||||
}
|
||||
|
||||
pub fn fflags(&self) -> FilterFlag {
|
||||
FilterFlag::from_bits(self.kevent.fflags).unwrap()
|
||||
}
|
||||
|
||||
pub fn data(&self) -> intptr_t {
|
||||
self.kevent.data as intptr_t
|
||||
}
|
||||
|
||||
pub fn udata(&self) -> intptr_t {
|
||||
self.kevent.udata as intptr_t
|
||||
}
|
||||
}
|
||||
|
||||
pub fn kevent(kq: RawFd,
|
||||
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
|
||||
};
|
||||
|
||||
kevent_ts(kq, changelist, eventlist, Some(timeout))
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "macos",
|
||||
target_os = "ios",
|
||||
target_os = "freebsd",
|
||||
target_os = "dragonfly",
|
||||
target_os = "openbsd"))]
|
||||
type type_of_nchanges = c_int;
|
||||
#[cfg(target_os = "netbsd")]
|
||||
type type_of_nchanges = size_t;
|
||||
|
||||
pub fn kevent_ts(kq: RawFd,
|
||||
changelist: &[KEvent],
|
||||
eventlist: &mut [KEvent],
|
||||
timeout_opt: Option<timespec>) -> Result<usize> {
|
||||
|
||||
let res = unsafe {
|
||||
libc::kevent(
|
||||
kq,
|
||||
changelist.as_ptr() as *const libc::kevent,
|
||||
changelist.len() as type_of_nchanges,
|
||||
eventlist.as_mut_ptr() as *mut libc::kevent,
|
||||
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)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn ev_set(ev: &mut KEvent,
|
||||
ident: usize,
|
||||
filter: EventFilter,
|
||||
flags: EventFlag,
|
||||
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;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_struct_kevent() {
|
||||
use std::mem;
|
||||
|
||||
let udata : intptr_t = 12345;
|
||||
|
||||
let actual = KEvent::new(0xdead_beef,
|
||||
EventFilter::EVFILT_READ,
|
||||
EventFlag::EV_ONESHOT | EventFlag::EV_ADD,
|
||||
FilterFlag::NOTE_CHILD | FilterFlag::NOTE_EXIT,
|
||||
0x1337,
|
||||
udata);
|
||||
assert_eq!(0xdead_beef, actual.ident());
|
||||
let filter = actual.kevent.filter;
|
||||
assert_eq!(libc::EVFILT_READ, filter);
|
||||
assert_eq!(libc::EV_ONESHOT | libc::EV_ADD, actual.flags().bits());
|
||||
assert_eq!(libc::NOTE_CHILD | libc::NOTE_EXIT, actual.fflags().bits());
|
||||
assert_eq!(0x1337, actual.data());
|
||||
assert_eq!(udata as type_of_udata, actual.udata() as type_of_udata);
|
||||
assert_eq!(mem::size_of::<libc::kevent>(), mem::size_of::<KEvent>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_kevent_filter() {
|
||||
let udata : intptr_t = 12345;
|
||||
|
||||
let actual = KEvent::new(0xdead_beef,
|
||||
EventFilter::EVFILT_READ,
|
||||
EventFlag::EV_ONESHOT | EventFlag::EV_ADD,
|
||||
FilterFlag::NOTE_CHILD | FilterFlag::NOTE_EXIT,
|
||||
0x1337,
|
||||
udata);
|
||||
assert_eq!(EventFilter::EVFILT_READ, actual.filter().unwrap());
|
||||
}
|
||||
17
third-party/vendor/nix/src/sys/eventfd.rs
vendored
Normal file
17
third-party/vendor/nix/src/sys/eventfd.rs
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
use std::os::unix::io::RawFd;
|
||||
use crate::Result;
|
||||
use crate::errno::Errno;
|
||||
|
||||
libc_bitflags! {
|
||||
pub struct EfdFlags: libc::c_int {
|
||||
EFD_CLOEXEC; // Since Linux 2.6.27
|
||||
EFD_NONBLOCK; // Since Linux 2.6.27
|
||||
EFD_SEMAPHORE; // Since Linux 2.6.30
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eventfd(initval: libc::c_uint, flags: EfdFlags) -> Result<RawFd> {
|
||||
let res = unsafe { libc::eventfd(initval, flags.bits()) };
|
||||
|
||||
Errno::result(res).map(|r| r as RawFd)
|
||||
}
|
||||
257
third-party/vendor/nix/src/sys/inotify.rs
vendored
Normal file
257
third-party/vendor/nix/src/sys/inotify.rs
vendored
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
//! 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 libc::{
|
||||
c_char,
|
||||
c_int,
|
||||
};
|
||||
use std::ffi::{OsString,OsStr,CStr};
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use std::mem::{MaybeUninit, size_of};
|
||||
use std::os::unix::io::{RawFd,AsRawFd,FromRawFd};
|
||||
use std::ptr;
|
||||
use crate::unistd::read;
|
||||
use crate::Result;
|
||||
use crate::NixPath;
|
||||
use crate::errno::Errno;
|
||||
use cfg_if::cfg_if;
|
||||
|
||||
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, Clone, Copy)]
|
||||
pub struct Inotify {
|
||||
fd: RawFd
|
||||
}
|
||||
|
||||
/// 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 })
|
||||
}
|
||||
|
||||
/// 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, 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, 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() as *mut u8,
|
||||
(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)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for Inotify {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.fd
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRawFd for Inotify {
|
||||
unsafe fn from_raw_fd(fd: RawFd) -> Self {
|
||||
Inotify { fd }
|
||||
}
|
||||
}
|
||||
129
third-party/vendor/nix/src/sys/ioctl/bsd.rs
vendored
Normal file
129
third-party/vendor/nix/src/sys/ioctl/bsd.rs
vendored
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
/// The datatype used for the ioctl number
|
||||
#[doc(hidden)]
|
||||
#[cfg(not(target_os = "illumos"))]
|
||||
pub type ioctl_num_type = ::libc::c_ulong;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[cfg(target_os = "illumos")]
|
||||
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)
|
||||
};
|
||||
}
|
||||
172
third-party/vendor/nix/src/sys/ioctl/linux.rs
vendored
Normal file
172
third-party/vendor/nix/src/sys/ioctl/linux.rs
vendored
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
/// The datatype used for the ioctl number
|
||||
#[cfg(any(target_os = "android", target_env = "musl"))]
|
||||
#[doc(hidden)]
|
||||
pub type ioctl_num_type = ::libc::c_int;
|
||||
#[cfg(not(any(target_os = "android", target_env = "musl")))]
|
||||
#[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(any(
|
||||
target_arch = "mips",
|
||||
target_arch = "mips64",
|
||||
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;
|
||||
}
|
||||
|
||||
// "Generic" ioctl protocol
|
||||
#[cfg(any(
|
||||
target_arch = "x86",
|
||||
target_arch = "arm",
|
||||
target_arch = "s390x",
|
||||
target_arch = "x86_64",
|
||||
target_arch = "aarch64",
|
||||
target_arch = "riscv32",
|
||||
target_arch = "riscv64"
|
||||
))]
|
||||
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
|
||||
)
|
||||
};
|
||||
}
|
||||
786
third-party/vendor/nix/src/sys/ioctl/mod.rs
vendored
Normal file
786
third-party/vendor/nix/src/sys/ioctl/mod.rs
vendored
Normal file
|
|
@ -0,0 +1,786 @@
|
|||
//! 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 = 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(any(target_os = "android", target_os = "linux"))]
|
||||
//! # use nix::libc::TCGETS as TCGETS;
|
||||
//! # #[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
//! # use nix::libc::termios as termios;
|
||||
//! # #[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
//! 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 = 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(target_os = "android", target_os = "linux", target_os = "redox"))]
|
||||
#[macro_use]
|
||||
mod linux;
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "linux",
|
||||
target_os = "redox"
|
||||
))]
|
||||
pub use self::linux::*;
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "illumos",
|
||||
target_os = "ios",
|
||||
target_os = "macos",
|
||||
target_os = "netbsd",
|
||||
target_os = "haiku",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
#[macro_use]
|
||||
mod bsd;
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "illumos",
|
||||
target_os = "ios",
|
||||
target_os = "macos",
|
||||
target_os = "netbsd",
|
||||
target_os = "haiku",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
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> {
|
||||
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> {
|
||||
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> {
|
||||
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(any(target_os = "android", target_os = "linux"))]
|
||||
/// 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> {
|
||||
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> {
|
||||
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(any(target_os = "android", target_os = "linux"))]
|
||||
/// 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> {
|
||||
convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] {
|
||||
/// 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> {
|
||||
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> {
|
||||
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(any(target_os = "android", target_os = "linux"))]
|
||||
/// 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> {
|
||||
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> {
|
||||
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> {
|
||||
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> {
|
||||
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_read!($ioty, $nr, data.len() * ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_write!($ioty, $nr, data.len() * ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_readwrite!($ioty, $nr, data.len() * ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data))
|
||||
}
|
||||
)
|
||||
}
|
||||
47
third-party/vendor/nix/src/sys/memfd.rs
vendored
Normal file
47
third-party/vendor/nix/src/sys/memfd.rs
vendored
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
//! Interfaces for managing memory-backed files.
|
||||
|
||||
use std::os::unix::io::RawFd;
|
||||
use crate::Result;
|
||||
use crate::errno::Errno;
|
||||
use std::ffi::CStr;
|
||||
|
||||
libc_bitflags!(
|
||||
/// Options that change the behavior of [`memfd_create`].
|
||||
pub struct MemFdCreateFlag: 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;
|
||||
}
|
||||
);
|
||||
|
||||
/// 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
|
||||
pub fn memfd_create(name: &CStr, flags: MemFdCreateFlag) -> Result<RawFd> {
|
||||
let res = unsafe {
|
||||
libc::syscall(libc::SYS_memfd_create, name.as_ptr(), flags.bits())
|
||||
};
|
||||
|
||||
Errno::result(res).map(|r| r as RawFd)
|
||||
}
|
||||
569
third-party/vendor/nix/src/sys/mman.rs
vendored
Normal file
569
third-party/vendor/nix/src/sys/mman.rs
vendored
Normal file
|
|
@ -0,0 +1,569 @@
|
|||
//! Memory management declarations.
|
||||
|
||||
use crate::Result;
|
||||
#[cfg(not(target_os = "android"))]
|
||||
use crate::NixPath;
|
||||
use crate::errno::Errno;
|
||||
#[cfg(not(target_os = "android"))]
|
||||
#[cfg(feature = "fs")]
|
||||
use crate::{fcntl::OFlag, sys::stat::Mode};
|
||||
use libc::{self, c_int, c_void, size_t, off_t};
|
||||
use std::os::unix::io::RawFd;
|
||||
|
||||
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(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
PROT_GROWSDOWN;
|
||||
/// Apply protection down to the beginning of a mapping that grows downwards.
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
PROT_GROWSUP;
|
||||
}
|
||||
}
|
||||
|
||||
libc_bitflags!{
|
||||
/// Additional parameters for [`mmap`].
|
||||
pub struct MapFlags: c_int {
|
||||
/// Compatibility flag. Ignored.
|
||||
MAP_FILE;
|
||||
/// Share this mapping. Mutually exclusive with `MAP_PRIVATE`.
|
||||
MAP_SHARED;
|
||||
/// 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")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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(any(target_os = "android", target_os = "linux"),
|
||||
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 = "freebsd", target_pointer_width = "64")))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MAP_32BIT;
|
||||
/// Used for stacks; indicates to the kernel that the mapping should extend downward in memory.
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MAP_GROWSDOWN;
|
||||
/// Compatibility flag. Ignored.
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MAP_DENYWRITE;
|
||||
/// Compatibility flag. Ignored.
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MAP_EXECUTABLE;
|
||||
/// Mark the mmaped region to be locked in the same way as `mlock(2)`.
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MAP_LOCKED;
|
||||
/// Do not reserve swap space for this mapping.
|
||||
///
|
||||
/// This was removed in FreeBSD 11 and is unused in DragonFlyBSD.
|
||||
#[cfg(not(any(target_os = "dragonfly", target_os = "freebsd")))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MAP_NORESERVE;
|
||||
/// Populate page tables for a mapping.
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MAP_POPULATE;
|
||||
/// Only meaningful when used with `MAP_POPULATE`. Don't perform read-ahead.
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MAP_NONBLOCK;
|
||||
/// Allocate the mapping using "huge pages."
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MAP_HUGETLB;
|
||||
/// Make use of 64KB huge page (must be supported by the system)
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MAP_HUGE_64KB;
|
||||
/// Make use of 512KB huge page (must be supported by the system)
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MAP_HUGE_512KB;
|
||||
/// Make use of 1MB huge page (must be supported by the system)
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MAP_HUGE_1MB;
|
||||
/// Make use of 2MB huge page (must be supported by the system)
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MAP_HUGE_2MB;
|
||||
/// Make use of 8MB huge page (must be supported by the system)
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MAP_HUGE_8MB;
|
||||
/// Make use of 16MB huge page (must be supported by the system)
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MAP_HUGE_16MB;
|
||||
/// Make use of 32MB huge page (must be supported by the system)
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MAP_HUGE_32MB;
|
||||
/// Make use of 256MB huge page (must be supported by the system)
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MAP_HUGE_256MB;
|
||||
/// Make use of 512MB huge page (must be supported by the system)
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MAP_HUGE_512MB;
|
||||
/// Make use of 1GB huge page (must be supported by the system)
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MAP_HUGE_1GB;
|
||||
/// Make use of 2GB huge page (must be supported by the system)
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MAP_HUGE_2GB;
|
||||
/// Make use of 16GB huge page (must be supported by the system)
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MAP_HUGE_16GB;
|
||||
|
||||
/// Lock the mapped region into memory as with `mlock(2)`.
|
||||
#[cfg(target_os = "netbsd")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MAP_WIRED;
|
||||
/// Causes dirtied data in the specified range to be flushed to disk only when necessary.
|
||||
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MAP_NOSYNC;
|
||||
/// Rename private pages to a file.
|
||||
///
|
||||
/// This was removed in FreeBSD 11 and is unused in DragonFlyBSD.
|
||||
#[cfg(any(target_os = "netbsd", target_os = "openbsd"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MAP_RENAME;
|
||||
/// Region may contain semaphores.
|
||||
#[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MAP_HASSEMAPHORE;
|
||||
/// Region grows down, like a stack.
|
||||
#[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "linux", target_os = "openbsd"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MAP_STACK;
|
||||
/// Pages in this mapping are not retained in the kernel's memory cache.
|
||||
#[cfg(any(target_os = "ios", target_os = "macos"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MAP_NOCACHE;
|
||||
/// Allows the W/X bit on the page, it's necessary on aarch64 architecture.
|
||||
#[cfg(any(target_os = "ios", target_os = "macos"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MAP_JIT;
|
||||
/// Allows to use large pages, underlying alignment based on size.
|
||||
#[cfg(target_os = "freebsd")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MAP_ALIGNED_SUPER;
|
||||
/// Pages will be discarded in the core dumps.
|
||||
#[cfg(target_os = "openbsd")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MAP_CONCEAL;
|
||||
}
|
||||
}
|
||||
|
||||
#[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")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MREMAP_MAYMOVE;
|
||||
/// Place the mapping at exactly the address specified in `new_address`.
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MREMAP_FIXED;
|
||||
/// Place the mapping at exactly the address specified in `new_address`.
|
||||
#[cfg(target_os = "netbsd")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MAP_FIXED;
|
||||
/// Allows to duplicate the mapping to be able to apply different flags on the copy.
|
||||
#[cfg(target_os = "netbsd")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MADV_REMOVE,
|
||||
/// Do not make pages in this range available to the child after a `fork(2)`.
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MADV_DONTFORK,
|
||||
/// Undo the effect of `MADV_DONTFORK`.
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MADV_DOFORK,
|
||||
/// Poison the given pages.
|
||||
///
|
||||
/// Subsequent references to those pages are treated like hardware memory corruption.
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MADV_HWPOISON,
|
||||
/// Enable Kernel Samepage Merging (KSM) for the given pages.
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MADV_MERGEABLE,
|
||||
/// Undo the effect of `MADV_MERGEABLE`
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MADV_HUGEPAGE,
|
||||
/// Undo the effect of `MADV_HUGEPAGE`.
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MADV_NOHUGEPAGE,
|
||||
/// Exclude the given range from a core dump.
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MADV_DONTDUMP,
|
||||
/// Undo the effect of an earlier `MADV_DONTDUMP`.
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MADV_DODUMP,
|
||||
/// Specify that the application no longer needs the pages in the given range.
|
||||
MADV_FREE,
|
||||
/// Request that the system not flush the current range to disk unless it needs to.
|
||||
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MADV_NOSYNC,
|
||||
/// Undoes the effects of `MADV_NOSYNC` for any future pages dirtied within the given range.
|
||||
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MADV_AUTOSYNC,
|
||||
/// Region is not included in a core file.
|
||||
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MADV_NOCORE,
|
||||
/// Include region in a core file
|
||||
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MADV_CORE,
|
||||
/// This process should not be killed when swap space is exhausted.
|
||||
#[cfg(any(target_os = "freebsd"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MADV_PROTECT,
|
||||
/// Invalidate the hardware page table for the given region.
|
||||
#[cfg(target_os = "dragonfly")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MADV_INVAL,
|
||||
/// Set the offset of the page directory page to `value` for the virtual page table.
|
||||
#[cfg(target_os = "dragonfly")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MADV_SETMAP,
|
||||
/// Indicates that the application will not need the data in the given range.
|
||||
#[cfg(any(target_os = "ios", target_os = "macos"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MADV_ZERO_WIRED_PAGES,
|
||||
/// Pages can be reused (by anyone).
|
||||
#[cfg(any(target_os = "ios", target_os = "macos"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MADV_FREE_REUSABLE,
|
||||
/// Caller wants to reuse those pages.
|
||||
#[cfg(any(target_os = "ios", target_os = "macos"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MADV_FREE_REUSE,
|
||||
// Darwin doesn't document this flag's behavior.
|
||||
#[cfg(any(target_os = "ios", target_os = "macos"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
#[allow(missing_docs)]
|
||||
MADV_CAN_REUSE,
|
||||
}
|
||||
}
|
||||
|
||||
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(any(target_os = "ios", target_os = "macos"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MS_KILLPAGES;
|
||||
/// Deactivate pages, but leave them mapped.
|
||||
#[cfg(any(target_os = "ios", target_os = "macos"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
MS_DEACTIVATE;
|
||||
/// Perform an update and wait for it to complete.
|
||||
MS_SYNC;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "haiku"))]
|
||||
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: *const c_void, length: size_t) -> Result<()> {
|
||||
Errno::result(libc::mlock(addr, 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: *const c_void, length: size_t) -> Result<()> {
|
||||
Errno::result(libc::munlock(addr, 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(target_os = "haiku"))]
|
||||
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(target_os = "haiku"))]
|
||||
pub fn munlockall() -> Result<()> {
|
||||
unsafe { Errno::result(libc::munlockall()) }.map(drop)
|
||||
}
|
||||
|
||||
/// allocate memory, or map files or devices into memory
|
||||
///
|
||||
/// # 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(addr: *mut c_void, length: size_t, prot: ProtFlags, flags: MapFlags, fd: RawFd, offset: off_t) -> Result<*mut c_void> {
|
||||
let ret = libc::mmap(addr, length, prot.bits(), flags.bits(), fd, offset);
|
||||
|
||||
if ret == libc::MAP_FAILED {
|
||||
Err(Errno::last())
|
||||
} else {
|
||||
Ok(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: *mut c_void,
|
||||
old_size: size_t,
|
||||
new_size: size_t,
|
||||
flags: MRemapFlags,
|
||||
new_address: Option<* mut c_void>,
|
||||
) -> Result<*mut c_void> {
|
||||
#[cfg(target_os = "linux")]
|
||||
let ret = libc::mremap(addr, old_size, new_size, flags.bits(), new_address.unwrap_or(std::ptr::null_mut()));
|
||||
#[cfg(target_os = "netbsd")]
|
||||
let ret = libc::mremap(
|
||||
addr,
|
||||
old_size,
|
||||
new_address.unwrap_or(std::ptr::null_mut()),
|
||||
new_size,
|
||||
flags.bits(),
|
||||
);
|
||||
|
||||
if ret == libc::MAP_FAILED {
|
||||
Err(Errno::last())
|
||||
} else {
|
||||
Ok(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: *mut c_void, len: size_t) -> Result<()> {
|
||||
Errno::result(libc::munmap(addr, 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
|
||||
pub unsafe fn madvise(addr: *mut c_void, length: size_t, advise: MmapAdvise) -> Result<()> {
|
||||
Errno::result(libc::madvise(addr, 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, mprotect, MapFlags, ProtFlags};
|
||||
/// # use std::ptr;
|
||||
/// const ONE_K: size_t = 1024;
|
||||
/// let mut slice: &mut [u8] = unsafe {
|
||||
/// let mem = mmap(ptr::null_mut(), ONE_K, ProtFlags::PROT_NONE,
|
||||
/// MapFlags::MAP_ANON | MapFlags::MAP_PRIVATE, -1, 0).unwrap();
|
||||
/// mprotect(mem, ONE_K, ProtFlags::PROT_READ | ProtFlags::PROT_WRITE).unwrap();
|
||||
/// std::slice::from_raw_parts_mut(mem as *mut u8, ONE_K)
|
||||
/// };
|
||||
/// assert_eq!(slice[0], 0x00);
|
||||
/// slice[0] = 0xFF;
|
||||
/// assert_eq!(slice[0], 0xFF);
|
||||
/// ```
|
||||
pub unsafe fn mprotect(addr: *mut c_void, length: size_t, prot: ProtFlags) -> Result<()> {
|
||||
Errno::result(libc::mprotect(addr, 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: *mut c_void, length: size_t, flags: MsFlags) -> Result<()> {
|
||||
Errno::result(libc::msync(addr, 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<RawFd>
|
||||
where P: ?Sized + NixPath
|
||||
{
|
||||
let ret = name.with_nix_path(|cstr| {
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
unsafe {
|
||||
libc::shm_open(cstr.as_ptr(), flag.bits(), mode.bits() as libc::c_uint)
|
||||
}
|
||||
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
|
||||
unsafe {
|
||||
libc::shm_open(cstr.as_ptr(), flag.bits(), mode.bits() as libc::mode_t)
|
||||
}
|
||||
})?;
|
||||
|
||||
Errno::result(ret)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
228
third-party/vendor/nix/src/sys/mod.rs
vendored
Normal file
228
third-party/vendor/nix/src/sys/mod.rs
vendored
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
//! Mostly platform-specific functionality
|
||||
#[cfg(any(
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "ios",
|
||||
all(target_os = "linux", not(target_env = "uclibc")),
|
||||
target_os = "macos",
|
||||
target_os = "netbsd"
|
||||
))]
|
||||
feature! {
|
||||
#![feature = "aio"]
|
||||
pub mod aio;
|
||||
}
|
||||
|
||||
feature! {
|
||||
#![feature = "event"]
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[allow(missing_docs)]
|
||||
pub mod epoll;
|
||||
|
||||
#[cfg(any(target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "ios",
|
||||
target_os = "macos",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"))]
|
||||
#[allow(missing_docs)]
|
||||
pub mod event;
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[allow(missing_docs)]
|
||||
pub mod eventfd;
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "ios",
|
||||
target_os = "linux",
|
||||
target_os = "redox",
|
||||
target_os = "macos",
|
||||
target_os = "netbsd",
|
||||
target_os = "illumos",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
#[cfg(feature = "ioctl")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "ioctl")))]
|
||||
#[macro_use]
|
||||
pub mod ioctl;
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
feature! {
|
||||
#![feature = "fs"]
|
||||
pub mod memfd;
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
feature! {
|
||||
#![feature = "mman"]
|
||||
pub mod mman;
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
feature! {
|
||||
#![feature = "personality"]
|
||||
pub mod personality;
|
||||
}
|
||||
|
||||
feature! {
|
||||
#![feature = "pthread"]
|
||||
pub mod pthread;
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "linux",
|
||||
target_os = "macos",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
feature! {
|
||||
#![feature = "ptrace"]
|
||||
#[allow(missing_docs)]
|
||||
pub mod ptrace;
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
feature! {
|
||||
#![feature = "quota"]
|
||||
pub mod quota;
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
feature! {
|
||||
#![feature = "reboot"]
|
||||
pub mod reboot;
|
||||
}
|
||||
|
||||
#[cfg(not(any(
|
||||
target_os = "redox",
|
||||
target_os = "fuchsia",
|
||||
target_os = "illumos",
|
||||
target_os = "haiku"
|
||||
)))]
|
||||
feature! {
|
||||
#![feature = "resource"]
|
||||
pub mod resource;
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
feature! {
|
||||
#![feature = "poll"]
|
||||
pub mod select;
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "ios",
|
||||
target_os = "linux",
|
||||
target_os = "macos"
|
||||
))]
|
||||
feature! {
|
||||
#![feature = "zerocopy"]
|
||||
pub mod sendfile;
|
||||
}
|
||||
|
||||
pub mod signal;
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
feature! {
|
||||
#![feature = "signal"]
|
||||
#[allow(missing_docs)]
|
||||
pub mod signalfd;
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
feature! {
|
||||
#![feature = "socket"]
|
||||
#[allow(missing_docs)]
|
||||
pub mod socket;
|
||||
}
|
||||
|
||||
feature! {
|
||||
#![feature = "fs"]
|
||||
#[allow(missing_docs)]
|
||||
pub mod stat;
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "ios",
|
||||
target_os = "linux",
|
||||
target_os = "macos",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
feature! {
|
||||
#![feature = "fs"]
|
||||
pub mod statfs;
|
||||
}
|
||||
|
||||
feature! {
|
||||
#![feature = "fs"]
|
||||
pub mod statvfs;
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
#[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(any(target_os = "android", target_os = "linux"))]
|
||||
feature! {
|
||||
#![feature = "inotify"]
|
||||
pub mod inotify;
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
feature! {
|
||||
#![feature = "time"]
|
||||
pub mod timerfd;
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
any(
|
||||
target_os = "freebsd",
|
||||
target_os = "illumos",
|
||||
target_os = "linux",
|
||||
target_os = "netbsd"
|
||||
),
|
||||
feature = "time",
|
||||
feature = "signal"
|
||||
))]
|
||||
feature! {
|
||||
#![feature = "time"]
|
||||
pub mod timer;
|
||||
}
|
||||
97
third-party/vendor/nix/src/sys/personality.rs
vendored
Normal file
97
third-party/vendor/nix/src/sys/personality.rs
vendored
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
//! Process execution domains
|
||||
use crate::Result;
|
||||
use crate::errno::Errno;
|
||||
|
||||
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")))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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")))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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:
|
||||
///
|
||||
/// ```
|
||||
/// # 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)
|
||||
}
|
||||
43
third-party/vendor/nix/src/sys/pthread.rs
vendored
Normal file
43
third-party/vendor/nix/src/sys/pthread.rs
vendored
Normal 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
third-party/vendor/nix/src/sys/ptrace/bsd.rs
vendored
Normal file
187
third-party/vendor/nix/src/sys/ptrace/bsd.rs
vendored
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
use cfg_if::cfg_if;
|
||||
use crate::errno::Errno;
|
||||
use libc::{self, c_int};
|
||||
use std::ptr;
|
||||
use crate::sys::signal::Signal;
|
||||
use crate::unistd::Pid;
|
||||
use crate::Result;
|
||||
|
||||
pub type RequestType = c_int;
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(any(target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "macos",
|
||||
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(target_os = "macos")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
PT_READ_U,
|
||||
PT_WRITE_I,
|
||||
PT_WRITE_D,
|
||||
#[cfg(target_os = "macos")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
PT_WRITE_U,
|
||||
PT_CONTINUE,
|
||||
PT_KILL,
|
||||
#[cfg(any(any(target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "macos"),
|
||||
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(target_os = "macos")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
PT_SIGEXC,
|
||||
#[cfg(target_os = "macos")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
PT_THUPDATE,
|
||||
#[cfg(target_os = "macos")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
PT_ATTACHEXC
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn ptrace_other(
|
||||
request: Request,
|
||||
pid: Pid,
|
||||
addr: AddressType,
|
||||
data: c_int,
|
||||
) -> Result<c_int> {
|
||||
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(target_os = "dragonfly", target_os = "freebsd", target_os = "macos"),
|
||||
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) }
|
||||
}
|
||||
504
third-party/vendor/nix/src/sys/ptrace/linux.rs
vendored
Normal file
504
third-party/vendor/nix/src/sys/ptrace/linux.rs
vendored
Normal file
|
|
@ -0,0 +1,504 @@
|
|||
//! For detailed description of the ptrace requests, consult `man ptrace`.
|
||||
|
||||
use cfg_if::cfg_if;
|
||||
use std::{mem, ptr};
|
||||
use crate::Result;
|
||||
use crate::errno::Errno;
|
||||
use libc::{self, c_void, c_long, siginfo_t};
|
||||
use crate::unistd::Pid;
|
||||
use crate::sys::signal::Signal;
|
||||
|
||||
pub type AddressType = *mut ::libc::c_void;
|
||||
|
||||
#[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"))
|
||||
))]
|
||||
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")), repr(u32))]
|
||||
#[cfg_attr(any(target_env = "musl", target_env = "uclibc", target_os = "android"), 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_arch = "mips",
|
||||
target_arch = "mips64",
|
||||
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_arch = "mips",
|
||||
target_arch = "mips64",
|
||||
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_arch = "mips",
|
||||
target_arch = "mips64",
|
||||
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_arch = "mips",
|
||||
target_arch = "mips64",
|
||||
target_arch = "x86_64",
|
||||
target_pointer_width = "32"))))]
|
||||
PTRACE_SETFPREGS,
|
||||
PTRACE_ATTACH,
|
||||
PTRACE_DETACH,
|
||||
#[cfg(all(target_os = "linux", any(target_env = "musl",
|
||||
target_arch = "mips",
|
||||
target_arch = "mips64",
|
||||
target_arch = "x86",
|
||||
target_arch = "x86_64")))]
|
||||
PTRACE_GETFPXREGS,
|
||||
#[cfg(all(target_os = "linux", any(target_env = "musl",
|
||||
target_arch = "mips",
|
||||
target_arch = "mips64",
|
||||
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 = "mips64"))))]
|
||||
PTRACE_GETREGSET,
|
||||
#[cfg(all(target_os = "linux", not(any(target_arch = "mips",
|
||||
target_arch = "mips64"))))]
|
||||
PTRACE_SETREGSET,
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
PTRACE_SEIZE,
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
PTRACE_INTERRUPT,
|
||||
#[cfg(all(target_os = "linux", not(any(target_arch = "mips",
|
||||
target_arch = "mips64"))))]
|
||||
PTRACE_LISTEN,
|
||||
#[cfg(all(target_os = "linux", not(any(target_arch = "mips",
|
||||
target_arch = "mips64"))))]
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
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, ...)`
|
||||
#[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)
|
||||
}
|
||||
|
||||
/// Set user registers, as with `ptrace(PTRACE_SETREGS, ...)`
|
||||
#[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>(),
|
||||
®s as *const _ as *const c_void)
|
||||
};
|
||||
Errno::result(res).map(drop)
|
||||
}
|
||||
|
||||
/// 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::uninit();
|
||||
let res = unsafe {
|
||||
libc::ptrace(request as RequestType,
|
||||
libc::pid_t::from(pid),
|
||||
ptr::null_mut::<T>(),
|
||||
data.as_mut_ptr() as *const _ as *const c_void)
|
||||
};
|
||||
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> {
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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
|
||||
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
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The `data` argument is passed directly to `ptrace(2)`. Read that man page
|
||||
/// for guidance.
|
||||
pub unsafe fn write(
|
||||
pid: Pid,
|
||||
addr: AddressType,
|
||||
data: *mut c_void) -> Result<()>
|
||||
{
|
||||
ptrace_other(Request::PTRACE_POKEDATA, pid, addr, data).map(drop)
|
||||
}
|
||||
|
||||
/// Reads a word from a user area at `offset`.
|
||||
/// 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`.
|
||||
/// The user struct definition can be found in `/usr/include/sys/user.h`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The `data` argument is passed directly to `ptrace(2)`. Read that man page
|
||||
/// for guidance.
|
||||
pub unsafe fn write_user(
|
||||
pid: Pid,
|
||||
offset: AddressType,
|
||||
data: *mut c_void) -> Result<()>
|
||||
{
|
||||
ptrace_other(Request::PTRACE_POKEUSER, pid, offset, data).map(drop)
|
||||
}
|
||||
22
third-party/vendor/nix/src/sys/ptrace/mod.rs
vendored
Normal file
22
third-party/vendor/nix/src/sys/ptrace/mod.rs
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
///! Provides helpers for making ptrace system calls
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
mod linux;
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
pub use self::linux::*;
|
||||
|
||||
#[cfg(any(target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "macos",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"))]
|
||||
mod bsd;
|
||||
|
||||
#[cfg(any(target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "macos",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
pub use self::bsd::*;
|
||||
277
third-party/vendor/nix/src/sys/quota.rs
vendored
Normal file
277
third-party/vendor/nix/src/sys/quota.rs
vendored
Normal file
|
|
@ -0,0 +1,277 @@
|
|||
//! 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 std::default::Default;
|
||||
use std::{mem, ptr};
|
||||
use libc::{self, c_int, c_char};
|
||||
use crate::{Result, NixPath};
|
||||
use crate::errno::Errno;
|
||||
|
||||
struct QuotaCmd(QuotaSubCmd, QuotaType);
|
||||
|
||||
impl QuotaCmd {
|
||||
#[allow(unused_unsafe)]
|
||||
fn as_int(&self) -> c_int {
|
||||
unsafe { 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() as *mut c_char;
|
||||
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::uninit();
|
||||
quotactl(QuotaCmd(QuotaSubCmd::Q_GETQUOTA, which), Some(special), id, dqblk.as_mut_ptr() as *mut c_char)?;
|
||||
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)
|
||||
}
|
||||
52
third-party/vendor/nix/src/sys/reboot.rs
vendored
Normal file
52
third-party/vendor/nix/src/sys/reboot.rs
vendored
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
//! Reboot/shutdown or enable/disable Ctrl-Alt-Delete.
|
||||
|
||||
use crate::Result;
|
||||
use crate::errno::Errno;
|
||||
use std::convert::Infallible;
|
||||
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)
|
||||
}
|
||||
437
third-party/vendor/nix/src/sys/resource.rs
vendored
Normal file
437
third-party/vendor/nix/src/sys/resource.rs
vendored
Normal file
|
|
@ -0,0 +1,437 @@
|
|||
//! 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;
|
||||
use std::mem;
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")))]{
|
||||
use libc::{__rlimit_resource_t, rlimit};
|
||||
} else if #[cfg(any(
|
||||
target_os = "freebsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "macos",
|
||||
target_os = "ios",
|
||||
target_os = "android",
|
||||
target_os = "dragonfly",
|
||||
all(target_os = "linux", not(target_env = "gnu"))
|
||||
))]{
|
||||
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(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")), repr(u32))]
|
||||
#[cfg_attr(any(
|
||||
target_os = "freebsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "macos",
|
||||
target_os = "ios",
|
||||
target_os = "android",
|
||||
target_os = "dragonfly",
|
||||
all(target_os = "linux", not(any(target_env = "gnu", target_env = "uclibc")))
|
||||
), repr(i32))]
|
||||
#[non_exhaustive]
|
||||
pub enum Resource {
|
||||
#[cfg(not(any(target_os = "freebsd", target_os = "netbsd", target_os = "openbsd")))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
/// 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")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
/// The maximum number of kqueues this user id is allowed to create.
|
||||
RLIMIT_KQUEUES,
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
/// A limit on the combined number of flock locks and fcntl leases that
|
||||
/// this process may establish.
|
||||
RLIMIT_LOCKS,
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "freebsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "linux",
|
||||
target_os = "netbsd"
|
||||
))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
/// The maximum size (in bytes) which a process may lock into memory
|
||||
/// using the mlock(2) system call.
|
||||
RLIMIT_MEMLOCK,
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
/// 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(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
/// A ceiling to which the process's nice value can be raised using
|
||||
/// setpriority or nice.
|
||||
RLIMIT_NICE,
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "freebsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "linux",
|
||||
))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
/// The maximum number of simultaneous processes for this user id.
|
||||
RLIMIT_NPROC,
|
||||
|
||||
#[cfg(target_os = "freebsd")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
/// The maximum number of pseudo-terminals this user id is allowed to
|
||||
/// create.
|
||||
RLIMIT_NPTS,
|
||||
|
||||
#[cfg(any(target_os = "android",
|
||||
target_os = "freebsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "linux",
|
||||
))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
/// When there is memory pressure and swap is available, prioritize
|
||||
/// eviction of a process' resident pages beyond this amount (in bytes).
|
||||
RLIMIT_RSS,
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
/// 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"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
/// 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(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
/// A limit on the number of signals that may be queued for the real
|
||||
/// user ID of the calling process.
|
||||
RLIMIT_SIGPENDING,
|
||||
|
||||
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
/// The maximum size (in bytes) of socket buffer usage for this user.
|
||||
RLIMIT_SBSIZE,
|
||||
|
||||
#[cfg(target_os = "freebsd")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
/// 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")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
/// 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(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")))]{
|
||||
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(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")))]{
|
||||
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"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
/// 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, 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()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::{getrusage, UsageWho};
|
||||
|
||||
#[test]
|
||||
pub fn test_self_cpu_time() {
|
||||
// Make sure some CPU time is used.
|
||||
let mut numbers: Vec<i32> = (1..1_000_000).collect();
|
||||
numbers.iter_mut().for_each(|item| *item *= 2);
|
||||
|
||||
// FIXME: this is here to help ensure the compiler does not optimize the whole
|
||||
// thing away. Replace the assert with test::black_box once stabilized.
|
||||
assert_eq!(numbers[100..200].iter().sum::<i32>(), 30_100);
|
||||
|
||||
let usage = getrusage(UsageWho::RUSAGE_SELF).expect("Failed to call getrusage for SELF");
|
||||
let rusage = usage.as_ref();
|
||||
|
||||
let user = usage.user_time();
|
||||
assert!(user.tv_sec() > 0 || user.tv_usec() > 0);
|
||||
assert_eq!(user.tv_sec(), rusage.ru_utime.tv_sec);
|
||||
assert_eq!(user.tv_usec(), rusage.ru_utime.tv_usec);
|
||||
}
|
||||
}
|
||||
434
third-party/vendor/nix/src/sys/select.rs
vendored
Normal file
434
third-party/vendor/nix/src/sys/select.rs
vendored
Normal file
|
|
@ -0,0 +1,434 @@
|
|||
//! Portably monitor a group of file descriptors for readiness.
|
||||
use std::convert::TryFrom;
|
||||
use std::iter::FusedIterator;
|
||||
use std::mem;
|
||||
use std::ops::Range;
|
||||
use std::os::unix::io::RawFd;
|
||||
use std::ptr::{null, null_mut};
|
||||
use libc::{self, c_int};
|
||||
use crate::Result;
|
||||
use crate::errno::Errno;
|
||||
use crate::sys::time::{TimeSpec, TimeVal};
|
||||
|
||||
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(libc::fd_set);
|
||||
|
||||
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 FdSet {
|
||||
/// Create an empty `FdSet`
|
||||
pub fn new() -> FdSet {
|
||||
let mut fdset = mem::MaybeUninit::uninit();
|
||||
unsafe {
|
||||
libc::FD_ZERO(fdset.as_mut_ptr());
|
||||
FdSet(fdset.assume_init())
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a file descriptor to an `FdSet`
|
||||
pub fn insert(&mut self, fd: RawFd) {
|
||||
assert_fd_valid(fd);
|
||||
unsafe { libc::FD_SET(fd, &mut self.0) };
|
||||
}
|
||||
|
||||
/// Remove a file descriptor from an `FdSet`
|
||||
pub fn remove(&mut self, fd: RawFd) {
|
||||
assert_fd_valid(fd);
|
||||
unsafe { libc::FD_CLR(fd, &mut self.0) };
|
||||
}
|
||||
|
||||
/// Test an `FdSet` for the presence of a certain file descriptor.
|
||||
pub fn contains(&self, fd: RawFd) -> bool {
|
||||
assert_fd_valid(fd);
|
||||
unsafe { libc::FD_ISSET(fd, &self.0) }
|
||||
}
|
||||
|
||||
/// Remove all file descriptors from this `FdSet`.
|
||||
pub fn clear(&mut self) {
|
||||
unsafe { libc::FD_ZERO(&mut self.0) };
|
||||
}
|
||||
|
||||
/// 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 nix::sys::select::FdSet;
|
||||
/// let mut set = FdSet::new();
|
||||
/// set.insert(4);
|
||||
/// set.insert(9);
|
||||
/// assert_eq!(set.highest(), Some(9));
|
||||
/// ```
|
||||
///
|
||||
/// [`select`]: fn.select.html
|
||||
pub fn highest(&self) -> Option<RawFd> {
|
||||
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::RawFd;
|
||||
/// let mut set = FdSet::new();
|
||||
/// set.insert(4);
|
||||
/// set.insert(9);
|
||||
/// let fds: Vec<RawFd> = set.fds(None).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> {
|
||||
set: &'a FdSet,
|
||||
range: Range<usize>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Fds<'a> {
|
||||
type Item = RawFd;
|
||||
|
||||
fn next(&mut self) -> Option<RawFd> {
|
||||
for i in &mut self.range {
|
||||
if self.set.contains(i as RawFd) {
|
||||
return Some(i as RawFd);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let (_, upper) = self.range.size_hint();
|
||||
(0, upper)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DoubleEndedIterator for Fds<'a> {
|
||||
#[inline]
|
||||
fn next_back(&mut self) -> Option<RawFd> {
|
||||
while let Some(i) = self.range.next_back() {
|
||||
if self.set.contains(i as RawFd) {
|
||||
return Some(i as RawFd);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FusedIterator for Fds<'a> {}
|
||||
|
||||
/// 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, N, R, W, E, T>(nfds: N,
|
||||
readfds: R,
|
||||
writefds: W,
|
||||
errorfds: E,
|
||||
timeout: T) -> Result<c_int>
|
||||
where
|
||||
N: Into<Option<c_int>>,
|
||||
R: Into<Option<&'a mut FdSet>>,
|
||||
W: Into<Option<&'a mut FdSet>>,
|
||||
E: Into<Option<&'a mut FdSet>>,
|
||||
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().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, N, R, W, E, T, S>(nfds: N,
|
||||
readfds: R,
|
||||
writefds: W,
|
||||
errorfds: E,
|
||||
timeout: T,
|
||||
sigmask: S) -> Result<c_int>
|
||||
where
|
||||
N: Into<Option<c_int>>,
|
||||
R: Into<Option<&'a mut FdSet>>,
|
||||
W: Into<Option<&'a mut FdSet>>,
|
||||
E: Into<Option<&'a mut FdSet>>,
|
||||
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().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)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::os::unix::io::RawFd;
|
||||
use crate::sys::time::{TimeVal, TimeValLike};
|
||||
use crate::unistd::{write, pipe};
|
||||
|
||||
#[test]
|
||||
fn fdset_insert() {
|
||||
let mut fd_set = FdSet::new();
|
||||
|
||||
for i in 0..FD_SETSIZE {
|
||||
assert!(!fd_set.contains(i as RawFd));
|
||||
}
|
||||
|
||||
fd_set.insert(7);
|
||||
|
||||
assert!(fd_set.contains(7));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fdset_remove() {
|
||||
let mut fd_set = FdSet::new();
|
||||
|
||||
for i in 0..FD_SETSIZE {
|
||||
assert!(!fd_set.contains(i as RawFd));
|
||||
}
|
||||
|
||||
fd_set.insert(7);
|
||||
fd_set.remove(7);
|
||||
|
||||
for i in 0..FD_SETSIZE {
|
||||
assert!(!fd_set.contains(i as RawFd));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fdset_clear() {
|
||||
let mut fd_set = FdSet::new();
|
||||
fd_set.insert(1);
|
||||
fd_set.insert((FD_SETSIZE / 2) as RawFd);
|
||||
fd_set.insert((FD_SETSIZE - 1) as RawFd);
|
||||
|
||||
fd_set.clear();
|
||||
|
||||
for i in 0..FD_SETSIZE {
|
||||
assert!(!fd_set.contains(i as RawFd));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fdset_highest() {
|
||||
let mut set = FdSet::new();
|
||||
assert_eq!(set.highest(), None);
|
||||
set.insert(0);
|
||||
assert_eq!(set.highest(), Some(0));
|
||||
set.insert(90);
|
||||
assert_eq!(set.highest(), Some(90));
|
||||
set.remove(0);
|
||||
assert_eq!(set.highest(), Some(90));
|
||||
set.remove(90);
|
||||
assert_eq!(set.highest(), None);
|
||||
|
||||
set.insert(4);
|
||||
set.insert(5);
|
||||
set.insert(7);
|
||||
assert_eq!(set.highest(), Some(7));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fdset_fds() {
|
||||
let mut set = FdSet::new();
|
||||
assert_eq!(set.fds(None).collect::<Vec<_>>(), vec![]);
|
||||
set.insert(0);
|
||||
assert_eq!(set.fds(None).collect::<Vec<_>>(), vec![0]);
|
||||
set.insert(90);
|
||||
assert_eq!(set.fds(None).collect::<Vec<_>>(), vec![0, 90]);
|
||||
|
||||
// highest limit
|
||||
assert_eq!(set.fds(Some(89)).collect::<Vec<_>>(), vec![0]);
|
||||
assert_eq!(set.fds(Some(90)).collect::<Vec<_>>(), vec![0, 90]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_select() {
|
||||
let (r1, w1) = pipe().unwrap();
|
||||
write(w1, b"hi!").unwrap();
|
||||
let (r2, _w2) = pipe().unwrap();
|
||||
|
||||
let mut fd_set = FdSet::new();
|
||||
fd_set.insert(r1);
|
||||
fd_set.insert(r2);
|
||||
|
||||
let mut timeout = TimeVal::seconds(10);
|
||||
assert_eq!(1, select(None,
|
||||
&mut fd_set,
|
||||
None,
|
||||
None,
|
||||
&mut timeout).unwrap());
|
||||
assert!(fd_set.contains(r1));
|
||||
assert!(!fd_set.contains(r2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_select_nfds() {
|
||||
let (r1, w1) = pipe().unwrap();
|
||||
write(w1, b"hi!").unwrap();
|
||||
let (r2, _w2) = pipe().unwrap();
|
||||
|
||||
let mut fd_set = FdSet::new();
|
||||
fd_set.insert(r1);
|
||||
fd_set.insert(r2);
|
||||
|
||||
let mut timeout = TimeVal::seconds(10);
|
||||
assert_eq!(1, select(Some(fd_set.highest().unwrap() + 1),
|
||||
&mut fd_set,
|
||||
None,
|
||||
None,
|
||||
&mut timeout).unwrap());
|
||||
assert!(fd_set.contains(r1));
|
||||
assert!(!fd_set.contains(r2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_select_nfds2() {
|
||||
let (r1, w1) = pipe().unwrap();
|
||||
write(w1, b"hi!").unwrap();
|
||||
let (r2, _w2) = pipe().unwrap();
|
||||
|
||||
let mut fd_set = FdSet::new();
|
||||
fd_set.insert(r1);
|
||||
fd_set.insert(r2);
|
||||
|
||||
let mut timeout = TimeVal::seconds(10);
|
||||
assert_eq!(1, select(::std::cmp::max(r1, r2) + 1,
|
||||
&mut fd_set,
|
||||
None,
|
||||
None,
|
||||
&mut timeout).unwrap());
|
||||
assert!(fd_set.contains(r1));
|
||||
assert!(!fd_set.contains(r2));
|
||||
}
|
||||
}
|
||||
277
third-party/vendor/nix/src/sys/sendfile.rs
vendored
Normal file
277
third-party/vendor/nix/src/sys/sendfile.rs
vendored
Normal file
|
|
@ -0,0 +1,277 @@
|
|||
//! Send data from a file to a socket, bypassing userland.
|
||||
|
||||
use cfg_if::cfg_if;
|
||||
use std::os::unix::io::RawFd;
|
||||
use std::ptr;
|
||||
|
||||
use libc::{self, off_t};
|
||||
|
||||
use crate::Result;
|
||||
use crate::errno::Errno;
|
||||
|
||||
/// 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(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn sendfile(
|
||||
out_fd: RawFd,
|
||||
in_fd: RawFd,
|
||||
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, in_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")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn sendfile64(
|
||||
out_fd: RawFd,
|
||||
in_fd: RawFd,
|
||||
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, in_fd, offset, count) };
|
||||
Errno::result(ret).map(|r| r as usize)
|
||||
}
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(any(target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "ios",
|
||||
target_os = "macos"))] {
|
||||
use std::io::IoSlice;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct SendfileHeaderTrailer<'a>(
|
||||
libc::sf_hdtr,
|
||||
Option<Vec<IoSlice<'a>>>,
|
||||
Option<Vec<IoSlice<'a>>>,
|
||||
);
|
||||
|
||||
impl<'a> SendfileHeaderTrailer<'a> {
|
||||
fn new(
|
||||
headers: Option<&'a [&'a [u8]]>,
|
||||
trailers: Option<&'a [&'a [u8]]>
|
||||
) -> SendfileHeaderTrailer<'a> {
|
||||
let header_iovecs: Option<Vec<IoSlice<'_>>> =
|
||||
headers.map(|s| s.iter().map(|b| IoSlice::new(b)).collect());
|
||||
let trailer_iovecs: Option<Vec<IoSlice<'_>>> =
|
||||
trailers.map(|s| s.iter().map(|b| IoSlice::new(b)).collect());
|
||||
SendfileHeaderTrailer(
|
||||
libc::sf_hdtr {
|
||||
headers: {
|
||||
header_iovecs
|
||||
.as_ref()
|
||||
.map_or(ptr::null(), |v| v.as_ptr()) as *mut libc::iovec
|
||||
},
|
||||
hdr_cnt: header_iovecs.as_ref().map(|v| v.len()).unwrap_or(0) as i32,
|
||||
trailers: {
|
||||
trailer_iovecs
|
||||
.as_ref()
|
||||
.map_or(ptr::null(), |v| v.as_ptr()) as *mut libc::iovec
|
||||
},
|
||||
trl_cnt: trailer_iovecs.as_ref().map(|v| v.len()).unwrap_or(0) as i32
|
||||
},
|
||||
header_iovecs,
|
||||
trailer_iovecs,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(
|
||||
in_fd: RawFd,
|
||||
out_sock: RawFd,
|
||||
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.0 as *const libc::sf_hdtr);
|
||||
let return_code = unsafe {
|
||||
libc::sendfile(in_fd,
|
||||
out_sock,
|
||||
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§ion=2)
|
||||
pub fn sendfile(
|
||||
in_fd: RawFd,
|
||||
out_sock: RawFd,
|
||||
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.0 as *const libc::sf_hdtr);
|
||||
let return_code = unsafe {
|
||||
libc::sendfile(in_fd,
|
||||
out_sock,
|
||||
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(any(target_os = "ios", target_os = "macos"))] {
|
||||
/// 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(
|
||||
in_fd: RawFd,
|
||||
out_sock: RawFd,
|
||||
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.0 as *const libc::sf_hdtr);
|
||||
let return_code = unsafe {
|
||||
libc::sendfile(in_fd,
|
||||
out_sock,
|
||||
offset,
|
||||
&mut len as *mut off_t,
|
||||
hdtr_ptr as *mut libc::sf_hdtr,
|
||||
0)
|
||||
};
|
||||
(Errno::result(return_code).and(Ok(())), len)
|
||||
}
|
||||
}
|
||||
}
|
||||
1347
third-party/vendor/nix/src/sys/signal.rs
vendored
Normal file
1347
third-party/vendor/nix/src/sys/signal.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
171
third-party/vendor/nix/src/sys/signalfd.rs
vendored
Normal file
171
third-party/vendor/nix/src/sys/signalfd.rs
vendored
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
//! 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::unistd;
|
||||
use crate::Result;
|
||||
use crate::errno::Errno;
|
||||
pub use crate::sys::signal::{self, SigSet};
|
||||
pub use libc::signalfd_siginfo as siginfo;
|
||||
|
||||
use std::os::unix::io::{RawFd, AsRawFd};
|
||||
use std::mem;
|
||||
|
||||
|
||||
libc_bitflags!{
|
||||
pub struct SfdFlags: libc::c_int {
|
||||
SFD_NONBLOCK;
|
||||
SFD_CLOEXEC;
|
||||
}
|
||||
}
|
||||
|
||||
pub const SIGNALFD_NEW: RawFd = -1;
|
||||
#[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)
|
||||
pub fn signalfd(fd: RawFd, mask: &SigSet, flags: SfdFlags) -> Result<RawFd> {
|
||||
unsafe {
|
||||
Errno::result(libc::signalfd(fd as libc::c_int, mask.as_ref(), flags.bits()))
|
||||
}
|
||||
}
|
||||
|
||||
/// 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, Eq, Hash, PartialEq)]
|
||||
pub struct SignalFd(RawFd);
|
||||
|
||||
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(SIGNALFD_NEW, mask, flags)?;
|
||||
|
||||
Ok(SignalFd(fd))
|
||||
}
|
||||
|
||||
pub fn set_mask(&mut self, mask: &SigSet) -> Result<()> {
|
||||
signalfd(self.0, mask, SfdFlags::empty()).map(drop)
|
||||
}
|
||||
|
||||
pub fn read_signal(&mut 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, buffer.as_mut_ptr() as *mut libc::c_void, 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SignalFd {
|
||||
fn drop(&mut self) {
|
||||
let e = unistd::close(self.0);
|
||||
if !std::thread::panicking() && e == Err(Errno::EBADF) {
|
||||
panic!("Closing an invalid file descriptor!");
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for SignalFd {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn create_signalfd() {
|
||||
let mask = SigSet::empty();
|
||||
SignalFd::new(&mask).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_signalfd_with_opts() {
|
||||
let mask = SigSet::empty();
|
||||
SignalFd::with_flags(
|
||||
&mask,
|
||||
SfdFlags::SFD_CLOEXEC | SfdFlags::SFD_NONBLOCK,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_empty_signalfd() {
|
||||
let mask = SigSet::empty();
|
||||
let mut fd = SignalFd::with_flags(&mask, SfdFlags::SFD_NONBLOCK).unwrap();
|
||||
|
||||
let res = fd.read_signal();
|
||||
assert!(res.unwrap().is_none());
|
||||
}
|
||||
}
|
||||
2973
third-party/vendor/nix/src/sys/socket/addr.rs
vendored
Normal file
2973
third-party/vendor/nix/src/sys/socket/addr.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
2254
third-party/vendor/nix/src/sys/socket/mod.rs
vendored
Normal file
2254
third-party/vendor/nix/src/sys/socket/mod.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
1031
third-party/vendor/nix/src/sys/socket/sockopt.rs
vendored
Normal file
1031
third-party/vendor/nix/src/sys/socket/sockopt.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
436
third-party/vendor/nix/src/sys/stat.rs
vendored
Normal file
436
third-party/vendor/nix/src/sys/stat.rs
vendored
Normal file
|
|
@ -0,0 +1,436 @@
|
|||
pub use libc::{dev_t, mode_t};
|
||||
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "openbsd"))]
|
||||
pub use libc::c_uint;
|
||||
#[cfg(any(
|
||||
target_os = "netbsd",
|
||||
target_os = "freebsd",
|
||||
target_os = "dragonfly"
|
||||
))]
|
||||
pub use libc::c_ulong;
|
||||
pub use libc::stat as FileStat;
|
||||
|
||||
use crate::{Result, NixPath, errno::Errno};
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
use crate::fcntl::{AtFlags, at_rawfd};
|
||||
use std::mem;
|
||||
use std::os::unix::io::RawFd;
|
||||
use crate::sys::time::{TimeSpec, TimeVal};
|
||||
|
||||
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 {
|
||||
S_IRWXU;
|
||||
S_IRUSR;
|
||||
S_IWUSR;
|
||||
S_IXUSR;
|
||||
S_IRWXG;
|
||||
S_IRGRP;
|
||||
S_IWGRP;
|
||||
S_IXGRP;
|
||||
S_IRWXO;
|
||||
S_IROTH;
|
||||
S_IWOTH;
|
||||
S_IXOTH;
|
||||
S_ISUID as mode_t;
|
||||
S_ISGID as mode_t;
|
||||
S_ISVTX as mode_t;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "ios", target_os="openbsd"))]
|
||||
pub type type_of_file_flag = c_uint;
|
||||
#[cfg(any(
|
||||
target_os = "netbsd",
|
||||
target_os = "freebsd",
|
||||
target_os = "dragonfly"
|
||||
))]
|
||||
pub type type_of_file_flag = c_ulong;
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "openbsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "freebsd",
|
||||
target_os = "dragonfly",
|
||||
target_os = "macos",
|
||||
target_os = "ios"
|
||||
))]
|
||||
libc_bitflags! {
|
||||
/// File flags.
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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(any(target_os = "freebsd", target_os = "dragonfly"))]
|
||||
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(any(target_os = "macos", target_os = "ios"))]
|
||||
UF_COMPRESSED;
|
||||
/// The file may be hidden from directory listings at the application's
|
||||
/// discretion.
|
||||
#[cfg(any(
|
||||
target_os = "freebsd",
|
||||
target_os = "macos",
|
||||
target_os = "ios",
|
||||
))]
|
||||
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(any(target_os = "freebsd", target_os = "dragonfly"))]
|
||||
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(any(target_os = "macos", target_os = "ios"))]
|
||||
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(target_os = "ios", target_os = "macos", target_os = "redox", target_os = "haiku")))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn mknodat<P: ?Sized + NixPath>(
|
||||
dirfd: RawFd,
|
||||
path: &P,
|
||||
kind: SFlag,
|
||||
perm: Mode,
|
||||
dev: dev_t,
|
||||
) -> Result<()> {
|
||||
let res = path.with_nix_path(|cstr| unsafe {
|
||||
libc::mknodat(dirfd, cstr.as_ptr(), kind.bits | perm.bits() as mode_t, dev)
|
||||
})?;
|
||||
|
||||
Errno::result(res).map(drop)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub const fn major(dev: dev_t) -> u64 {
|
||||
((dev >> 32) & 0xffff_f000) |
|
||||
((dev >> 8) & 0x0000_0fff)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub const fn minor(dev: dev_t) -> u64 {
|
||||
((dev >> 12) & 0xffff_ff00) |
|
||||
((dev ) & 0x0000_00ff)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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: RawFd) -> Result<FileStat> {
|
||||
let mut dst = mem::MaybeUninit::uninit();
|
||||
let res = unsafe { libc::fstat(fd, dst.as_mut_ptr()) };
|
||||
|
||||
Errno::result(res)?;
|
||||
|
||||
Ok(unsafe{dst.assume_init()})
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn fstatat<P: ?Sized + NixPath>(dirfd: RawFd, pathname: &P, f: AtFlags) -> Result<FileStat> {
|
||||
let mut dst = mem::MaybeUninit::uninit();
|
||||
let res = pathname.with_nix_path(|cstr| {
|
||||
unsafe { libc::fstatat(dirfd, 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: RawFd, mode: Mode) -> Result<()> {
|
||||
let res = unsafe { libc::fchmod(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 `None`.
|
||||
///
|
||||
/// If `flag` is `FchmodatFlags::NoFollowSymlink` and `path` names a symbolic link,
|
||||
/// then the mode of the symbolic link is changed.
|
||||
///
|
||||
/// `fchmodat(None, 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"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn fchmodat<P: ?Sized + NixPath>(
|
||||
dirfd: Option<RawFd>,
|
||||
path: &P,
|
||||
mode: Mode,
|
||||
flag: FchmodatFlags,
|
||||
) -> Result<()> {
|
||||
let atflag =
|
||||
match flag {
|
||||
FchmodatFlags::FollowSymlink => AtFlags::empty(),
|
||||
FchmodatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW,
|
||||
};
|
||||
let res = path.with_nix_path(|cstr| unsafe {
|
||||
libc::fchmodat(
|
||||
at_rawfd(dirfd),
|
||||
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(), ×[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",
|
||||
target_os = "ios",
|
||||
target_os = "macos",
|
||||
target_os = "freebsd",
|
||||
target_os = "netbsd"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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(), ×[0])
|
||||
})?;
|
||||
|
||||
Errno::result(res).map(drop)
|
||||
}
|
||||
|
||||
/// Change the access and modification times of the file specified by a file descriptor.
|
||||
///
|
||||
/// # References
|
||||
///
|
||||
/// [futimens(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html).
|
||||
#[inline]
|
||||
pub fn futimens(fd: RawFd, atime: &TimeSpec, mtime: &TimeSpec) -> Result<()> {
|
||||
let times: [libc::timespec; 2] = [*atime.as_ref(), *mtime.as_ref()];
|
||||
let res = unsafe { libc::futimens(fd, ×[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 `None`.
|
||||
///
|
||||
/// If `flag` is `UtimensatFlags::NoFollowSymlink` and `path` names a symbolic link,
|
||||
/// then the mode of the symbolic link is changed.
|
||||
///
|
||||
/// `utimensat(None, 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.
|
||||
///
|
||||
/// # References
|
||||
///
|
||||
/// [utimensat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/utimens.html).
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn utimensat<P: ?Sized + NixPath>(
|
||||
dirfd: Option<RawFd>,
|
||||
path: &P,
|
||||
atime: &TimeSpec,
|
||||
mtime: &TimeSpec,
|
||||
flag: UtimensatFlags
|
||||
) -> Result<()> {
|
||||
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(
|
||||
at_rawfd(dirfd),
|
||||
cstr.as_ptr(),
|
||||
×[0],
|
||||
atflag.bits() as libc::c_int,
|
||||
)
|
||||
})?;
|
||||
|
||||
Errno::result(res).map(drop)
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn mkdirat<P: ?Sized + NixPath>(fd: RawFd, path: &P, mode: Mode) -> Result<()> {
|
||||
let res = path.with_nix_path(|cstr| {
|
||||
unsafe { libc::mkdirat(fd, cstr.as_ptr(), mode.bits() as mode_t) }
|
||||
})?;
|
||||
|
||||
Errno::result(res).map(drop)
|
||||
}
|
||||
764
third-party/vendor/nix/src/sys/statfs.rs
vendored
Normal file
764
third-party/vendor/nix/src/sys/statfs.rs
vendored
Normal file
|
|
@ -0,0 +1,764 @@
|
|||
//! Get filesystem statistics, non-portably
|
||||
//!
|
||||
//! See [`statvfs`](crate::sys::statvfs) for a portable alternative.
|
||||
use std::fmt::{self, Debug};
|
||||
use std::mem;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||
use std::ffi::CStr;
|
||||
|
||||
use crate::{NixPath, Result, errno::Errno};
|
||||
|
||||
/// Identifies a mounted file system
|
||||
#[cfg(target_os = "android")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub type fsid_t = libc::__fsid_t;
|
||||
/// Identifies a mounted file system
|
||||
#[cfg(not(target_os = "android"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub type fsid_t = libc::fsid_t;
|
||||
|
||||
/// Describes a mounted file system
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(transparent)]
|
||||
pub struct Statfs(libc::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"))]
|
||||
type fs_type_t = libc::c_uint;
|
||||
#[cfg(all(target_os = "linux", target_env = "musl"))]
|
||||
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 = "uclibc"))))]
|
||||
type fs_type_t = libc::__fsword_t;
|
||||
|
||||
/// 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", not(any(target_arch = "s390x", target_env = "musl"))),
|
||||
))]
|
||||
#[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(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const ADFS_SUPER_MAGIC: FsType = FsType(libc::ADFS_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const AFFS_SUPER_MAGIC: FsType = FsType(libc::AFFS_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const AFS_SUPER_MAGIC: FsType = FsType(libc::AFS_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const AUTOFS_SUPER_MAGIC: FsType = FsType(libc::AUTOFS_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const BPF_FS_MAGIC: FsType = FsType(libc::BPF_FS_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const BTRFS_SUPER_MAGIC: FsType = FsType(libc::BTRFS_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const CGROUP2_SUPER_MAGIC: FsType = FsType(libc::CGROUP2_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const CGROUP_SUPER_MAGIC: FsType = FsType(libc::CGROUP_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const CODA_SUPER_MAGIC: FsType = FsType(libc::CODA_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const CRAMFS_MAGIC: FsType = FsType(libc::CRAMFS_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const DEBUGFS_MAGIC: FsType = FsType(libc::DEBUGFS_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const DEVPTS_SUPER_MAGIC: FsType = FsType(libc::DEVPTS_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const ECRYPTFS_SUPER_MAGIC: FsType = FsType(libc::ECRYPTFS_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const EFS_SUPER_MAGIC: FsType = FsType(libc::EFS_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const EXT2_SUPER_MAGIC: FsType = FsType(libc::EXT2_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const EXT3_SUPER_MAGIC: FsType = FsType(libc::EXT3_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const EXT4_SUPER_MAGIC: FsType = FsType(libc::EXT4_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const F2FS_SUPER_MAGIC: FsType = FsType(libc::F2FS_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const FUSE_SUPER_MAGIC: FsType = FsType(libc::FUSE_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const FUTEXFS_SUPER_MAGIC: FsType = FsType(libc::FUTEXFS_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const HOSTFS_SUPER_MAGIC: FsType = FsType(libc::HOSTFS_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const HPFS_SUPER_MAGIC: FsType = FsType(libc::HPFS_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const HUGETLBFS_MAGIC: FsType = FsType(libc::HUGETLBFS_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const ISOFS_SUPER_MAGIC: FsType = FsType(libc::ISOFS_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const JFFS2_SUPER_MAGIC: FsType = FsType(libc::JFFS2_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const MINIX2_SUPER_MAGIC2: FsType = FsType(libc::MINIX2_SUPER_MAGIC2 as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const MINIX2_SUPER_MAGIC: FsType = FsType(libc::MINIX2_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const MINIX3_SUPER_MAGIC: FsType = FsType(libc::MINIX3_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const MINIX_SUPER_MAGIC2: FsType = FsType(libc::MINIX_SUPER_MAGIC2 as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const MINIX_SUPER_MAGIC: FsType = FsType(libc::MINIX_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const MSDOS_SUPER_MAGIC: FsType = FsType(libc::MSDOS_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const NCP_SUPER_MAGIC: FsType = FsType(libc::NCP_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const NFS_SUPER_MAGIC: FsType = FsType(libc::NFS_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const NILFS_SUPER_MAGIC: FsType = FsType(libc::NILFS_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const OCFS2_SUPER_MAGIC: FsType = FsType(libc::OCFS2_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const OPENPROM_SUPER_MAGIC: FsType = FsType(libc::OPENPROM_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const OVERLAYFS_SUPER_MAGIC: FsType = FsType(libc::OVERLAYFS_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const PROC_SUPER_MAGIC: FsType = FsType(libc::PROC_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const QNX4_SUPER_MAGIC: FsType = FsType(libc::QNX4_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const QNX6_SUPER_MAGIC: FsType = FsType(libc::QNX6_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const RDTGROUP_SUPER_MAGIC: FsType = FsType(libc::RDTGROUP_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const REISERFS_SUPER_MAGIC: FsType = FsType(libc::REISERFS_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const SECURITYFS_MAGIC: FsType = FsType(libc::SECURITYFS_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const SELINUX_MAGIC: FsType = FsType(libc::SELINUX_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const SMACK_MAGIC: FsType = FsType(libc::SMACK_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const SMB_SUPER_MAGIC: FsType = FsType(libc::SMB_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const SYSFS_MAGIC: FsType = FsType(libc::SYSFS_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const TMPFS_MAGIC: FsType = FsType(libc::TMPFS_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const TRACEFS_MAGIC: FsType = FsType(libc::TRACEFS_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const UDF_SUPER_MAGIC: FsType = FsType(libc::UDF_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const USBDEVICE_SUPER_MAGIC: FsType = FsType(libc::USBDEVICE_SUPER_MAGIC as fs_type_t);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[allow(missing_docs)]
|
||||
pub const XENFS_SUPER_MAGIC: FsType = FsType(libc::XENFS_SUPER_MAGIC as fs_type_t);
|
||||
|
||||
|
||||
impl Statfs {
|
||||
/// Magic code defining system type
|
||||
#[cfg(not(any(
|
||||
target_os = "openbsd",
|
||||
target_os = "dragonfly",
|
||||
target_os = "ios",
|
||||
target_os = "macos"
|
||||
)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn filesystem_type(&self) -> FsType {
|
||||
FsType(self.0.f_type)
|
||||
}
|
||||
|
||||
/// Magic code defining system type
|
||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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(any(target_os = "ios", target_os = "macos"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn optimal_transfer_size(&self) -> i32 {
|
||||
self.0.f_iosize
|
||||
}
|
||||
|
||||
/// Optimal transfer block size
|
||||
#[cfg(target_os = "openbsd")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn optimal_transfer_size(&self) -> u32 {
|
||||
self.0.f_iosize
|
||||
}
|
||||
|
||||
/// Optimal transfer block size
|
||||
#[cfg(all(target_os = "linux", target_arch = "s390x"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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")
|
||||
))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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 = "uclibc"))))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn optimal_transfer_size(&self) -> libc::c_int {
|
||||
self.0.f_bsize
|
||||
}
|
||||
|
||||
/// Optimal transfer block size
|
||||
#[cfg(target_os = "dragonfly")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn optimal_transfer_size(&self) -> libc::c_long {
|
||||
self.0.f_iosize
|
||||
}
|
||||
|
||||
/// Optimal transfer block size
|
||||
#[cfg(target_os = "freebsd")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn optimal_transfer_size(&self) -> u64 {
|
||||
self.0.f_iosize
|
||||
}
|
||||
|
||||
/// Size of a block
|
||||
#[cfg(any(target_os = "ios", target_os = "macos", target_os = "openbsd"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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 = "uclibc"))))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn block_size(&self) -> libc::__fsword_t {
|
||||
self.0.f_bsize
|
||||
}
|
||||
|
||||
/// Size of a block
|
||||
#[cfg(target_os = "freebsd")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn block_size(&self) -> u64 {
|
||||
self.0.f_bsize
|
||||
}
|
||||
|
||||
/// Size of a block
|
||||
#[cfg(target_os = "android")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn block_size(&self) -> libc::c_ulong {
|
||||
self.0.f_bsize
|
||||
}
|
||||
|
||||
/// Size of a block
|
||||
#[cfg(target_os = "dragonfly")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn block_size(&self) -> libc::c_long {
|
||||
self.0.f_bsize
|
||||
}
|
||||
|
||||
/// Maximum length of filenames
|
||||
#[cfg(any(target_os = "freebsd", target_os = "openbsd"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn maximum_name_length(&self) -> u32 {
|
||||
self.0.f_namemax
|
||||
}
|
||||
|
||||
/// Maximum length of filenames
|
||||
#[cfg(all(target_os = "linux", target_arch = "s390x"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn maximum_name_length(&self) -> u32 {
|
||||
self.0.f_namelen
|
||||
}
|
||||
|
||||
/// Maximum length of filenames
|
||||
#[cfg(all(target_os = "linux", target_env = "musl"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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 = "uclibc"))))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn maximum_name_length(&self) -> libc::__fsword_t {
|
||||
self.0.f_namelen
|
||||
}
|
||||
|
||||
/// Maximum length of filenames
|
||||
#[cfg(target_os = "android")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn maximum_name_length(&self) -> libc::c_ulong {
|
||||
self.0.f_namelen
|
||||
}
|
||||
|
||||
/// Total data blocks in filesystem
|
||||
#[cfg(any(
|
||||
target_os = "ios",
|
||||
target_os = "macos",
|
||||
target_os = "android",
|
||||
target_os = "freebsd",
|
||||
target_os = "openbsd",
|
||||
))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn blocks(&self) -> u64 {
|
||||
self.0.f_blocks
|
||||
}
|
||||
|
||||
/// Total data blocks in filesystem
|
||||
#[cfg(target_os = "dragonfly")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn blocks(&self) -> libc::c_long {
|
||||
self.0.f_blocks
|
||||
}
|
||||
|
||||
/// Total data blocks in filesystem
|
||||
#[cfg(all(target_os = "linux", any(target_env = "musl", target_arch = "riscv32", all(target_arch = "x86_64", target_pointer_width = "32"))))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn blocks(&self) -> u64 {
|
||||
self.0.f_blocks
|
||||
}
|
||||
|
||||
/// Total data blocks in filesystem
|
||||
#[cfg(not(any(
|
||||
target_os = "ios",
|
||||
target_os = "macos",
|
||||
target_os = "android",
|
||||
target_os = "freebsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "dragonfly",
|
||||
all(target_os = "linux", any(target_env = "musl", target_arch = "riscv32", all(target_arch = "x86_64", target_pointer_width = "32")))
|
||||
)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn blocks(&self) -> libc::c_ulong {
|
||||
self.0.f_blocks
|
||||
}
|
||||
|
||||
/// Free blocks in filesystem
|
||||
#[cfg(any(
|
||||
target_os = "ios",
|
||||
target_os = "macos",
|
||||
target_os = "android",
|
||||
target_os = "freebsd",
|
||||
target_os = "openbsd",
|
||||
))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn blocks_free(&self) -> u64 {
|
||||
self.0.f_bfree
|
||||
}
|
||||
|
||||
/// Free blocks in filesystem
|
||||
#[cfg(target_os = "dragonfly")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn blocks_free(&self) -> libc::c_long {
|
||||
self.0.f_bfree
|
||||
}
|
||||
|
||||
/// Free blocks in filesystem
|
||||
#[cfg(all(target_os = "linux", any(target_env = "musl", target_arch = "riscv32", all(target_arch = "x86_64", target_pointer_width = "32"))))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn blocks_free(&self) -> u64 {
|
||||
self.0.f_bfree
|
||||
}
|
||||
|
||||
/// Free blocks in filesystem
|
||||
#[cfg(not(any(
|
||||
target_os = "ios",
|
||||
target_os = "macos",
|
||||
target_os = "android",
|
||||
target_os = "freebsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "dragonfly",
|
||||
all(target_os = "linux", any(target_env = "musl", target_arch = "riscv32", all(target_arch = "x86_64", target_pointer_width = "32")))
|
||||
)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn blocks_free(&self) -> libc::c_ulong {
|
||||
self.0.f_bfree
|
||||
}
|
||||
|
||||
/// Free blocks available to unprivileged user
|
||||
#[cfg(any(target_os = "ios", target_os = "macos", target_os = "android"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn blocks_available(&self) -> u64 {
|
||||
self.0.f_bavail
|
||||
}
|
||||
|
||||
/// Free blocks available to unprivileged user
|
||||
#[cfg(target_os = "dragonfly")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn blocks_available(&self) -> i64 {
|
||||
self.0.f_bavail
|
||||
}
|
||||
|
||||
/// Free blocks available to unprivileged user
|
||||
#[cfg(all(target_os = "linux", any(target_env = "musl", target_arch = "riscv32", all(target_arch = "x86_64", target_pointer_width = "32"))))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn blocks_available(&self) -> u64 {
|
||||
self.0.f_bavail
|
||||
}
|
||||
|
||||
/// Free blocks available to unprivileged user
|
||||
#[cfg(not(any(
|
||||
target_os = "ios",
|
||||
target_os = "macos",
|
||||
target_os = "android",
|
||||
target_os = "freebsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "dragonfly",
|
||||
all(target_os = "linux", any(target_env = "musl", target_arch = "riscv32", all(target_arch = "x86_64", target_pointer_width = "32")))
|
||||
)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn blocks_available(&self) -> libc::c_ulong {
|
||||
self.0.f_bavail
|
||||
}
|
||||
|
||||
/// Total file nodes in filesystem
|
||||
#[cfg(any(
|
||||
target_os = "ios",
|
||||
target_os = "macos",
|
||||
target_os = "android",
|
||||
target_os = "freebsd",
|
||||
target_os = "openbsd",
|
||||
))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn files(&self) -> u64 {
|
||||
self.0.f_files
|
||||
}
|
||||
|
||||
/// Total file nodes in filesystem
|
||||
#[cfg(target_os = "dragonfly")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn files(&self) -> libc::c_long {
|
||||
self.0.f_files
|
||||
}
|
||||
|
||||
/// Total file nodes in filesystem
|
||||
#[cfg(all(target_os = "linux", any(target_env = "musl", target_arch = "riscv32", all(target_arch = "x86_64", target_pointer_width = "32"))))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn files(&self) -> libc::fsfilcnt_t {
|
||||
self.0.f_files
|
||||
}
|
||||
|
||||
/// Total file nodes in filesystem
|
||||
#[cfg(not(any(
|
||||
target_os = "ios",
|
||||
target_os = "macos",
|
||||
target_os = "android",
|
||||
target_os = "freebsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "dragonfly",
|
||||
all(target_os = "linux", any(target_env = "musl", target_arch = "riscv32", all(target_arch = "x86_64", target_pointer_width = "32")))
|
||||
)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn files(&self) -> libc::c_ulong {
|
||||
self.0.f_files
|
||||
}
|
||||
|
||||
/// Free file nodes in filesystem
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "ios",
|
||||
target_os = "macos",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn files_free(&self) -> u64 {
|
||||
self.0.f_ffree
|
||||
}
|
||||
|
||||
/// Free file nodes in filesystem
|
||||
#[cfg(target_os = "dragonfly")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn files_free(&self) -> libc::c_long {
|
||||
self.0.f_ffree
|
||||
}
|
||||
|
||||
/// Free file nodes in filesystem
|
||||
#[cfg(target_os = "freebsd")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn files_free(&self) -> i64 {
|
||||
self.0.f_ffree
|
||||
}
|
||||
|
||||
/// Free file nodes in filesystem
|
||||
#[cfg(all(target_os = "linux", any(target_env = "musl", target_arch = "riscv32", all(target_arch = "x86_64", target_pointer_width = "32"))))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn files_free(&self) -> libc::fsfilcnt_t {
|
||||
self.0.f_ffree
|
||||
}
|
||||
|
||||
/// Free file nodes in filesystem
|
||||
#[cfg(not(any(
|
||||
target_os = "ios",
|
||||
target_os = "macos",
|
||||
target_os = "android",
|
||||
target_os = "freebsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "dragonfly",
|
||||
all(target_os = "linux", any(target_env = "musl", target_arch = "riscv32", all(target_arch = "x86_64", target_pointer_width = "32")))
|
||||
)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn files_free(&self) -> libc::c_ulong {
|
||||
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 {
|
||||
f.debug_struct("Statfs")
|
||||
.field("optimal_transfer_size", &self.optimal_transfer_size())
|
||||
.field("block_size", &self.block_size())
|
||||
.field("blocks", &self.blocks())
|
||||
.field("blocks_free", &self.blocks_free())
|
||||
.field("blocks_available", &self.blocks_available())
|
||||
.field("files", &self.files())
|
||||
.field("files_free", &self.files_free())
|
||||
.field("filesystem_id", &self.filesystem_id())
|
||||
.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::<libc::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<T: AsRawFd>(fd: &T) -> Result<Statfs> {
|
||||
unsafe {
|
||||
let mut stat = mem::MaybeUninit::<libc::statfs>::uninit();
|
||||
Errno::result(libc::fstatfs(fd.as_raw_fd(), stat.as_mut_ptr()))
|
||||
.map(|_| Statfs(stat.assume_init()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::fs::File;
|
||||
|
||||
use crate::sys::statfs::*;
|
||||
use crate::sys::statvfs::*;
|
||||
use std::path::Path;
|
||||
|
||||
#[test]
|
||||
fn statfs_call() {
|
||||
check_statfs("/tmp");
|
||||
check_statfs("/dev");
|
||||
check_statfs("/run");
|
||||
check_statfs("/");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fstatfs_call() {
|
||||
check_fstatfs("/tmp");
|
||||
check_fstatfs("/dev");
|
||||
check_fstatfs("/run");
|
||||
check_fstatfs("/");
|
||||
}
|
||||
|
||||
fn check_fstatfs(path: &str) {
|
||||
if !Path::new(path).exists() {
|
||||
return;
|
||||
}
|
||||
let vfs = statvfs(path.as_bytes()).unwrap();
|
||||
let file = File::open(path).unwrap();
|
||||
let fs = fstatfs(&file).unwrap();
|
||||
assert_fs_equals(fs, vfs);
|
||||
}
|
||||
|
||||
fn check_statfs(path: &str) {
|
||||
if !Path::new(path).exists() {
|
||||
return;
|
||||
}
|
||||
let vfs = statvfs(path.as_bytes()).unwrap();
|
||||
let fs = statfs(path.as_bytes()).unwrap();
|
||||
assert_fs_equals(fs, vfs);
|
||||
}
|
||||
|
||||
// The cast is not unnecessary on all platforms.
|
||||
#[allow(clippy::unnecessary_cast)]
|
||||
fn assert_fs_equals(fs: Statfs, vfs: Statvfs) {
|
||||
assert_eq!(fs.files() as u64, vfs.files() as u64);
|
||||
assert_eq!(fs.blocks() as u64, vfs.blocks() as u64);
|
||||
assert_eq!(fs.block_size() as u64, vfs.fragment_size() as u64);
|
||||
}
|
||||
|
||||
// This test is ignored because files_free/blocks_free can change after statvfs call and before
|
||||
// statfs call.
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn statfs_call_strict() {
|
||||
check_statfs_strict("/tmp");
|
||||
check_statfs_strict("/dev");
|
||||
check_statfs_strict("/run");
|
||||
check_statfs_strict("/");
|
||||
}
|
||||
|
||||
// This test is ignored because files_free/blocks_free can change after statvfs call and before
|
||||
// fstatfs call.
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn fstatfs_call_strict() {
|
||||
check_fstatfs_strict("/tmp");
|
||||
check_fstatfs_strict("/dev");
|
||||
check_fstatfs_strict("/run");
|
||||
check_fstatfs_strict("/");
|
||||
}
|
||||
|
||||
fn check_fstatfs_strict(path: &str) {
|
||||
if !Path::new(path).exists() {
|
||||
return;
|
||||
}
|
||||
let vfs = statvfs(path.as_bytes());
|
||||
let file = File::open(path).unwrap();
|
||||
let fs = fstatfs(&file);
|
||||
assert_fs_equals_strict(fs.unwrap(), vfs.unwrap())
|
||||
}
|
||||
|
||||
fn check_statfs_strict(path: &str) {
|
||||
if !Path::new(path).exists() {
|
||||
return;
|
||||
}
|
||||
let vfs = statvfs(path.as_bytes());
|
||||
let fs = statfs(path.as_bytes());
|
||||
assert_fs_equals_strict(fs.unwrap(), vfs.unwrap())
|
||||
}
|
||||
|
||||
// The cast is not unnecessary on all platforms.
|
||||
#[allow(clippy::unnecessary_cast)]
|
||||
fn assert_fs_equals_strict(fs: Statfs, vfs: Statvfs) {
|
||||
assert_eq!(fs.files_free() as u64, vfs.files_free() as u64);
|
||||
assert_eq!(fs.blocks_free() as u64, vfs.blocks_free() as u64);
|
||||
assert_eq!(fs.blocks_available() as u64, vfs.blocks_available() as u64);
|
||||
assert_eq!(fs.files() as u64, vfs.files() as u64);
|
||||
assert_eq!(fs.blocks() as u64, vfs.blocks() as u64);
|
||||
assert_eq!(fs.block_size() as u64, vfs.fragment_size() as u64);
|
||||
}
|
||||
}
|
||||
174
third-party/vendor/nix/src/sys/statvfs.rs
vendored
Normal file
174
third-party/vendor/nix/src/sys/statvfs.rs
vendored
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
//! 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::AsRawFd;
|
||||
|
||||
use libc::{self, c_ulong};
|
||||
|
||||
use crate::{Result, NixPath, errno::Errno};
|
||||
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
libc_bitflags!(
|
||||
/// File system mount Flags
|
||||
#[repr(C)]
|
||||
#[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(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
ST_NODEV;
|
||||
/// Do not allow execution of binaries on the filesystem
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
ST_NOEXEC;
|
||||
/// All IO should be done synchronously
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
ST_SYNCHRONOUS;
|
||||
/// Allow mandatory locks on the filesystem
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
ST_MANDLOCK;
|
||||
/// Write on file/directory/symlink
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
ST_WRITE;
|
||||
/// Append-only file
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
ST_APPEND;
|
||||
/// Immutable file
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
ST_IMMUTABLE;
|
||||
/// Do not update access times on files
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
ST_NOATIME;
|
||||
/// Do not update access times on files
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
ST_NODIRATIME;
|
||||
/// Update access time relative to modify/change time
|
||||
#[cfg(any(target_os = "android", all(target_os = "linux", not(target_env = "musl"))))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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
|
||||
pub fn filesystem_id(&self) -> c_ulong {
|
||||
self.0.f_fsid
|
||||
}
|
||||
|
||||
/// Get the mount flags
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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<T: AsRawFd>(fd: &T) -> Result<Statvfs> {
|
||||
unsafe {
|
||||
Errno::clear();
|
||||
let mut stat = mem::MaybeUninit::<libc::statvfs>::uninit();
|
||||
Errno::result(libc::fstatvfs(fd.as_raw_fd(), stat.as_mut_ptr()))
|
||||
.map(|_| Statvfs(stat.assume_init()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::fs::File;
|
||||
use crate::sys::statvfs::*;
|
||||
|
||||
#[test]
|
||||
fn statvfs_call() {
|
||||
statvfs(&b"/"[..]).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fstatvfs_call() {
|
||||
let root = File::open("/").unwrap();
|
||||
fstatvfs(&root).unwrap();
|
||||
}
|
||||
}
|
||||
83
third-party/vendor/nix/src/sys/sysinfo.rs
vendored
Normal file
83
third-party/vendor/nix/src/sys/sysinfo.rs
vendored
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
use libc::{self, 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()) })
|
||||
}
|
||||
1118
third-party/vendor/nix/src/sys/termios.rs
vendored
Normal file
1118
third-party/vendor/nix/src/sys/termios.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
809
third-party/vendor/nix/src/sys/time.rs
vendored
Normal file
809
third-party/vendor/nix/src/sys/time.rs
vendored
Normal file
|
|
@ -0,0 +1,809 @@
|
|||
#[cfg_attr(target_env = "musl", allow(deprecated))]
|
||||
// https://github.com/rust-lang/libc/issues/1848
|
||||
pub use libc::{suseconds_t, time_t};
|
||||
use libc::{timespec, timeval};
|
||||
use std::convert::From;
|
||||
use std::time::Duration;
|
||||
use std::{cmp, fmt, ops};
|
||||
|
||||
const TIMESPEC_ZERO: libc::timespec = unsafe {
|
||||
std::mem::transmute([0u8; std::mem::size_of::<libc::timespec>()])
|
||||
};
|
||||
|
||||
#[cfg(any(
|
||||
all(feature = "time", any(target_os = "android", target_os = "linux")),
|
||||
all(
|
||||
any(
|
||||
target_os = "freebsd",
|
||||
target_os = "illumos",
|
||||
target_os = "linux",
|
||||
target_os = "netbsd"
|
||||
),
|
||||
feature = "time",
|
||||
feature = "signal"
|
||||
)
|
||||
))]
|
||||
pub(crate) mod timer {
|
||||
use crate::sys::time::{TimeSpec, TIMESPEC_ZERO};
|
||||
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: TIMESPEC_ZERO,
|
||||
it_value: TIMESPEC_ZERO,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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: TIMESPEC_ZERO,
|
||||
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(any(target_os = "android", target_os = "linux"))]
|
||||
bitflags! {
|
||||
/// Flags that are used for arming the timer.
|
||||
pub struct TimerSetTimeFlags: libc::c_int {
|
||||
const TFD_TIMER_ABSTIME = libc::TFD_TIMER_ABSTIME;
|
||||
}
|
||||
}
|
||||
#[cfg(any(
|
||||
target_os = "freebsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "dragonfly",
|
||||
target_os = "illumos"
|
||||
))]
|
||||
bitflags! {
|
||||
/// Flags that are used for arming the timer.
|
||||
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) -> ×pec {
|
||||
&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(target_env = "musl", 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 = TIMESPEC_ZERO;
|
||||
ts.tv_sec = seconds as time_t;
|
||||
ts.tv_nsec = 0;
|
||||
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(target_env = "musl", 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 = TIMESPEC_ZERO;
|
||||
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 {
|
||||
/// Construct a new `TimeSpec` from its components
|
||||
#[cfg_attr(target_env = "musl", 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 = TIMESPEC_ZERO;
|
||||
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(target_env = "musl", 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(target_env = "musl", allow(deprecated))]
|
||||
// https://github.com/rust-lang/libc/issues/1848
|
||||
pub const fn from_duration(duration: Duration) -> Self {
|
||||
let mut ts = TIMESPEC_ZERO;
|
||||
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 abs.tv_sec() == 1 {
|
||||
write!(f, "{} second", sec)?;
|
||||
} else {
|
||||
write!(f, "{} seconds", sec)?;
|
||||
}
|
||||
} else if abs.tv_nsec() % 1_000_000 == 0 {
|
||||
write!(f, "{}.{:03} seconds", sec, abs.tv_nsec() / 1_000_000)?;
|
||||
} else if abs.tv_nsec() % 1_000 == 0 {
|
||||
write!(f, "{}.{:06} seconds", sec, abs.tv_nsec() / 1_000)?;
|
||||
} else {
|
||||
write!(f, "{}.{:09} seconds", sec, 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(target_env = "musl", 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(target_env = "musl", 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(target_env = "musl", 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(target_env = "musl", 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(target_env = "musl", 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 abs.tv_sec() == 1 {
|
||||
write!(f, "{} second", sec)?;
|
||||
} else {
|
||||
write!(f, "{} seconds", sec)?;
|
||||
}
|
||||
} else if abs.tv_usec() % 1000 == 0 {
|
||||
write!(f, "{}.{:03} seconds", sec, abs.tv_usec() / 1000)?;
|
||||
} else {
|
||||
write!(f, "{}.{:06} seconds", sec, 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)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::{TimeSpec, TimeVal, TimeValLike};
|
||||
use std::time::Duration;
|
||||
|
||||
#[test]
|
||||
pub fn test_timespec() {
|
||||
assert_ne!(TimeSpec::seconds(1), TimeSpec::zero());
|
||||
assert_eq!(
|
||||
TimeSpec::seconds(1) + TimeSpec::seconds(2),
|
||||
TimeSpec::seconds(3)
|
||||
);
|
||||
assert_eq!(
|
||||
TimeSpec::minutes(3) + TimeSpec::seconds(2),
|
||||
TimeSpec::seconds(182)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_timespec_from() {
|
||||
let duration = Duration::new(123, 123_456_789);
|
||||
let timespec = TimeSpec::nanoseconds(123_123_456_789);
|
||||
|
||||
assert_eq!(TimeSpec::from(duration), timespec);
|
||||
assert_eq!(Duration::from(timespec), duration);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_timespec_neg() {
|
||||
let a = TimeSpec::seconds(1) + TimeSpec::nanoseconds(123);
|
||||
let b = TimeSpec::seconds(-1) + TimeSpec::nanoseconds(-123);
|
||||
|
||||
assert_eq!(a, -b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_timespec_ord() {
|
||||
assert_eq!(TimeSpec::seconds(1), TimeSpec::nanoseconds(1_000_000_000));
|
||||
assert!(TimeSpec::seconds(1) < TimeSpec::nanoseconds(1_000_000_001));
|
||||
assert!(TimeSpec::seconds(1) > TimeSpec::nanoseconds(999_999_999));
|
||||
assert!(TimeSpec::seconds(-1) < TimeSpec::nanoseconds(-999_999_999));
|
||||
assert!(TimeSpec::seconds(-1) > TimeSpec::nanoseconds(-1_000_000_001));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_timespec_fmt() {
|
||||
assert_eq!(TimeSpec::zero().to_string(), "0 seconds");
|
||||
assert_eq!(TimeSpec::seconds(42).to_string(), "42 seconds");
|
||||
assert_eq!(TimeSpec::milliseconds(42).to_string(), "0.042 seconds");
|
||||
assert_eq!(TimeSpec::microseconds(42).to_string(), "0.000042 seconds");
|
||||
assert_eq!(
|
||||
TimeSpec::nanoseconds(42).to_string(),
|
||||
"0.000000042 seconds"
|
||||
);
|
||||
assert_eq!(TimeSpec::seconds(-86401).to_string(), "-86401 seconds");
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_timeval() {
|
||||
assert_ne!(TimeVal::seconds(1), TimeVal::zero());
|
||||
assert_eq!(
|
||||
TimeVal::seconds(1) + TimeVal::seconds(2),
|
||||
TimeVal::seconds(3)
|
||||
);
|
||||
assert_eq!(
|
||||
TimeVal::minutes(3) + TimeVal::seconds(2),
|
||||
TimeVal::seconds(182)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_timeval_ord() {
|
||||
assert_eq!(TimeVal::seconds(1), TimeVal::microseconds(1_000_000));
|
||||
assert!(TimeVal::seconds(1) < TimeVal::microseconds(1_000_001));
|
||||
assert!(TimeVal::seconds(1) > TimeVal::microseconds(999_999));
|
||||
assert!(TimeVal::seconds(-1) < TimeVal::microseconds(-999_999));
|
||||
assert!(TimeVal::seconds(-1) > TimeVal::microseconds(-1_000_001));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_timeval_neg() {
|
||||
let a = TimeVal::seconds(1) + TimeVal::microseconds(123);
|
||||
let b = TimeVal::seconds(-1) + TimeVal::microseconds(-123);
|
||||
|
||||
assert_eq!(a, -b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_timeval_fmt() {
|
||||
assert_eq!(TimeVal::zero().to_string(), "0 seconds");
|
||||
assert_eq!(TimeVal::seconds(42).to_string(), "42 seconds");
|
||||
assert_eq!(TimeVal::milliseconds(42).to_string(), "0.042 seconds");
|
||||
assert_eq!(TimeVal::microseconds(42).to_string(), "0.000042 seconds");
|
||||
assert_eq!(TimeVal::nanoseconds(1402).to_string(), "0.000001 seconds");
|
||||
assert_eq!(TimeVal::seconds(-86401).to_string(), "-86401 seconds");
|
||||
}
|
||||
}
|
||||
179
third-party/vendor/nix/src/sys/timer.rs
vendored
Normal file
179
third-party/vendor/nix/src/sys/timer.rs
vendored
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
//! 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`.
|
||||
#[cfg_attr(has_doc_alias, 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.
|
||||
#[cfg_attr(has_doc_alias, 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.
|
||||
#[cfg_attr(has_doc_alias, 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.
|
||||
#[cfg_attr(has_doc_alias, 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
205
third-party/vendor/nix/src/sys/timerfd.rs
vendored
Normal file
205
third-party/vendor/nix/src/sys/timerfd.rs
vendored
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
//! 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::{AsRawFd, FromRawFd, RawFd};
|
||||
|
||||
/// A timerfd instance. This is also a file descriptor, you can feed it to
|
||||
/// other interfaces consuming file descriptors, epoll for example.
|
||||
#[derive(Debug)]
|
||||
pub struct TimerFd {
|
||||
fd: RawFd,
|
||||
}
|
||||
|
||||
impl AsRawFd for TimerFd {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.fd
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRawFd for TimerFd {
|
||||
unsafe fn from_raw_fd(fd: RawFd) -> Self {
|
||||
TimerFd { 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.
|
||||
#[cfg_attr(has_doc_alias, 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 })
|
||||
}
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// # 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.
|
||||
#[cfg_attr(has_doc_alias, 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,
|
||||
flags.bits(),
|
||||
timerspec.as_ref(),
|
||||
std::ptr::null_mut(),
|
||||
)
|
||||
})
|
||||
.map(drop)
|
||||
}
|
||||
|
||||
/// Get the parameters for the alarm currently set, if any.
|
||||
#[cfg_attr(has_doc_alias, doc(alias("timerfd_gettime")))]
|
||||
pub fn get(&self) -> Result<Option<Expiration>> {
|
||||
let mut timerspec = TimerSpec::none();
|
||||
Errno::result(unsafe { libc::timerfd_gettime(self.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.
|
||||
#[cfg_attr(has_doc_alias, doc(alias("timerfd_settime")))]
|
||||
pub fn unset(&self) -> Result<()> {
|
||||
Errno::result(unsafe {
|
||||
libc::timerfd_settime(
|
||||
self.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::EINTR {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TimerFd {
|
||||
fn drop(&mut self) {
|
||||
if !std::thread::panicking() {
|
||||
let result = Errno::result(unsafe { libc::close(self.fd) });
|
||||
if let Err(Errno::EBADF) = result {
|
||||
panic!("close of TimerFd encountered EBADF");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
270
third-party/vendor/nix/src/sys/uio.rs
vendored
Normal file
270
third-party/vendor/nix/src/sys/uio.rs
vendored
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
//! Vectored I/O
|
||||
|
||||
use crate::Result;
|
||||
use crate::errno::Errno;
|
||||
use libc::{self, c_int, c_void, size_t, off_t};
|
||||
use std::io::{IoSlice, IoSliceMut};
|
||||
use std::marker::PhantomData;
|
||||
use std::os::unix::io::RawFd;
|
||||
|
||||
/// 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: RawFd, 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, iov.as_ptr() as *const libc::iovec, 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)
|
||||
pub fn readv(fd: RawFd, iov: &mut [IoSliceMut<'_>]) -> Result<usize> {
|
||||
// SAFETY: same as in writev(), IoSliceMut is ABI-compatible with iovec
|
||||
let res = unsafe { libc::readv(fd, iov.as_ptr() as *const libc::iovec, 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")))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn pwritev(fd: RawFd, 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, iov.as_ptr() as *const libc::iovec, 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")))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn preadv(fd: RawFd, 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, iov.as_ptr() as *const libc::iovec, 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: RawFd, buf: &[u8], offset: off_t) -> Result<usize> {
|
||||
let res = unsafe {
|
||||
libc::pwrite(fd, buf.as_ptr() as *const c_void, 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: RawFd, buf: &mut [u8], offset: off_t) -> Result<usize>{
|
||||
let res = unsafe {
|
||||
libc::pread(fd, buf.as_mut_ptr() as *mut c_void, 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(any(target_os = "linux", target_os = "android"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
#[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,
|
||||
}
|
||||
|
||||
/// A vector of buffers.
|
||||
///
|
||||
/// Vectored I/O methods like [`writev`] and [`readv`] use this structure for
|
||||
/// both reading and writing. Each `IoVec` specifies the base address and
|
||||
/// length of an area in memory.
|
||||
#[deprecated(
|
||||
since = "0.24.0",
|
||||
note = "`IoVec` is no longer used in the public interface, use `IoSlice` or `IoSliceMut` instead"
|
||||
)]
|
||||
#[repr(transparent)]
|
||||
#[allow(renamed_and_removed_lints)]
|
||||
#[allow(clippy::unknown_clippy_lints)]
|
||||
// Clippy false positive: https://github.com/rust-lang/rust-clippy/issues/8867
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct IoVec<T>(pub(crate) libc::iovec, PhantomData<T>);
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<T> IoVec<T> {
|
||||
/// View the `IoVec` as a Rust slice.
|
||||
#[deprecated(
|
||||
since = "0.24.0",
|
||||
note = "Use the `Deref` impl of `IoSlice` or `IoSliceMut` instead"
|
||||
)]
|
||||
#[inline]
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
use std::slice;
|
||||
|
||||
unsafe {
|
||||
slice::from_raw_parts(
|
||||
self.0.iov_base as *const u8,
|
||||
self.0.iov_len)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<'a> IoVec<&'a [u8]> {
|
||||
/// Create an `IoVec` from a Rust slice.
|
||||
#[deprecated(
|
||||
since = "0.24.0",
|
||||
note = "Use `IoSlice::new` instead"
|
||||
)]
|
||||
pub fn from_slice(buf: &'a [u8]) -> IoVec<&'a [u8]> {
|
||||
IoVec(libc::iovec {
|
||||
iov_base: buf.as_ptr() as *mut c_void,
|
||||
iov_len: buf.len() as size_t,
|
||||
}, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<'a> IoVec<&'a mut [u8]> {
|
||||
/// Create an `IoVec` from a mutable Rust slice.
|
||||
#[deprecated(
|
||||
since = "0.24.0",
|
||||
note = "Use `IoSliceMut::new` instead"
|
||||
)]
|
||||
pub fn from_mut_slice(buf: &'a mut [u8]) -> IoVec<&'a mut [u8]> {
|
||||
IoVec(libc::iovec {
|
||||
iov_base: buf.as_ptr() as *mut c_void,
|
||||
iov_len: buf.len() as size_t,
|
||||
}, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
// The only reason IoVec isn't automatically Send+Sync is because libc::iovec
|
||||
// contains raw pointers.
|
||||
#[allow(deprecated)]
|
||||
unsafe impl<T> Send for IoVec<T> where T: Send {}
|
||||
#[allow(deprecated)]
|
||||
unsafe impl<T> Sync for IoVec<T> where T: Sync {}
|
||||
|
||||
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(any(target_os = "linux", target_os = "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() as *const libc::iovec, local_iov.len() as libc::c_ulong,
|
||||
remote_iov.as_ptr() as *const libc::iovec, 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(any(target_os = "linux", target_os = "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() as *const libc::iovec, local_iov.len() as libc::c_ulong,
|
||||
remote_iov.as_ptr() as *const libc::iovec, remote_iov.len() as libc::c_ulong, 0)
|
||||
};
|
||||
|
||||
Errno::result(res).map(|r| r as usize)
|
||||
}
|
||||
}
|
||||
77
third-party/vendor/nix/src/sys/utsname.rs
vendored
Normal file
77
third-party/vendor/nix/src/sys/utsname.rs
vendored
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
//! Get system identification
|
||||
use std::mem;
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use std::ffi::OsStr;
|
||||
use libc::c_char;
|
||||
use crate::{Errno, Result};
|
||||
|
||||
/// 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)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
#[cfg(target_os = "linux")]
|
||||
#[test]
|
||||
pub fn test_uname_linux() {
|
||||
assert_eq!(super::uname().unwrap().sysname(), "Linux");
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
#[test]
|
||||
pub fn test_uname_darwin() {
|
||||
assert_eq!(super::uname().unwrap().sysname(), "Darwin");
|
||||
}
|
||||
|
||||
#[cfg(target_os = "freebsd")]
|
||||
#[test]
|
||||
pub fn test_uname_freebsd() {
|
||||
assert_eq!(super::uname().unwrap().sysname(), "FreeBSD");
|
||||
}
|
||||
}
|
||||
381
third-party/vendor/nix/src/sys/wait.rs
vendored
Normal file
381
third-party/vendor/nix/src/sys/wait.rs
vendored
Normal 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::RawFd;
|
||||
|
||||
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(target_os = "android",
|
||||
target_os = "freebsd",
|
||||
target_os = "haiku",
|
||||
target_os = "ios",
|
||||
target_os = "linux",
|
||||
target_os = "redox",
|
||||
target_os = "macos",
|
||||
target_os = "netbsd"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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(target_os = "android",
|
||||
target_os = "freebsd",
|
||||
target_os = "haiku",
|
||||
target_os = "ios",
|
||||
target_os = "linux",
|
||||
target_os = "redox",
|
||||
target_os = "macos",
|
||||
target_os = "netbsd"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
WSTOPPED;
|
||||
/// Don't reap, just poll status.
|
||||
#[cfg(any(target_os = "android",
|
||||
target_os = "freebsd",
|
||||
target_os = "haiku",
|
||||
target_os = "ios",
|
||||
target_os = "linux",
|
||||
target_os = "redox",
|
||||
target_os = "macos",
|
||||
target_os = "netbsd"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
WNOWAIT;
|
||||
/// Don't wait on children of other threads in this group
|
||||
#[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
__WNOTHREAD;
|
||||
/// Wait on all children, regardless of type
|
||||
#[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
__WALL;
|
||||
/// Wait for "clone" children only.
|
||||
#[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
__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(any(target_os = "linux", target_os = "android"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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(any(target_os = "linux", target_os = "android"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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(any(target_os = "android", target_os = "linux"))]
|
||||
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(any(target_os = "android", target_os = "linux"))]
|
||||
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(any(target_os = "android", target_os = "linux"))]
|
||||
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(any(target_os = "android", target_os = "linux"))] {
|
||||
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 = 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 = 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(any(target_os = "android", target_os = "linux"))]
|
||||
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, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum Id {
|
||||
/// 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(any(target_os = "android", target_os = "linux"))]
|
||||
PIDFd(RawFd),
|
||||
}
|
||||
|
||||
/// 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(any(target_os = "android", target_os = "linux"))]
|
||||
Id::PIDFd(fd) => (libc::P_PIDFD, fd as libc::id_t),
|
||||
};
|
||||
|
||||
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) }
|
||||
}
|
||||
269
third-party/vendor/nix/src/time.rs
vendored
Normal file
269
third-party/vendor/nix/src/time.rs
vendored
Normal file
|
|
@ -0,0 +1,269 @@
|
|||
use crate::sys::time::TimeSpec;
|
||||
#[cfg(any(
|
||||
target_os = "freebsd",
|
||||
target_os = "dragonfly",
|
||||
target_os = "linux",
|
||||
target_os = "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 `clockid_t` (which is just alias). It prevents bugs caused by
|
||||
/// accidentally passing wrong value.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct 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(
|
||||
target_os = "freebsd",
|
||||
target_os = "dragonfly",
|
||||
target_os = "linux",
|
||||
target_os = "android",
|
||||
target_os = "emscripten",
|
||||
))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub fn pid_cpu_clock_id(pid: Pid) -> Result<Self> {
|
||||
clock_getcpuclockid(pid)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns resolution of the clock id
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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 = "macos",
|
||||
target_os = "ios",
|
||||
target_os = "redox",
|
||||
target_os = "hermit",
|
||||
)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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(
|
||||
target_os = "android",
|
||||
target_os = "emscripten",
|
||||
target_os = "fuchsia",
|
||||
target_os = "linux"
|
||||
))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub const CLOCK_BOOTTIME: ClockId = ClockId(libc::CLOCK_BOOTTIME);
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "emscripten",
|
||||
target_os = "fuchsia",
|
||||
target_os = "linux"
|
||||
))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub const CLOCK_BOOTTIME_ALARM: ClockId = ClockId(libc::CLOCK_BOOTTIME_ALARM);
|
||||
pub const CLOCK_MONOTONIC: ClockId = ClockId(libc::CLOCK_MONOTONIC);
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "emscripten",
|
||||
target_os = "fuchsia",
|
||||
target_os = "linux"
|
||||
))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub const CLOCK_MONOTONIC_COARSE: ClockId = ClockId(libc::CLOCK_MONOTONIC_COARSE);
|
||||
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub const CLOCK_MONOTONIC_FAST: ClockId = ClockId(libc::CLOCK_MONOTONIC_FAST);
|
||||
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub const CLOCK_MONOTONIC_PRECISE: ClockId = ClockId(libc::CLOCK_MONOTONIC_PRECISE);
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "emscripten",
|
||||
target_os = "fuchsia",
|
||||
target_os = "linux"
|
||||
))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub const CLOCK_MONOTONIC_RAW: ClockId = ClockId(libc::CLOCK_MONOTONIC_RAW);
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "emscripten",
|
||||
target_os = "fuchsia",
|
||||
target_os = "macos",
|
||||
target_os = "ios",
|
||||
target_os = "freebsd",
|
||||
target_os = "dragonfly",
|
||||
target_os = "redox",
|
||||
target_os = "linux"
|
||||
))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub const CLOCK_PROCESS_CPUTIME_ID: ClockId = ClockId(libc::CLOCK_PROCESS_CPUTIME_ID);
|
||||
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub const CLOCK_PROF: ClockId = ClockId(libc::CLOCK_PROF);
|
||||
pub const CLOCK_REALTIME: ClockId = ClockId(libc::CLOCK_REALTIME);
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "emscripten",
|
||||
target_os = "fuchsia",
|
||||
target_os = "linux"
|
||||
))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub const CLOCK_REALTIME_ALARM: ClockId = ClockId(libc::CLOCK_REALTIME_ALARM);
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "emscripten",
|
||||
target_os = "fuchsia",
|
||||
target_os = "linux"
|
||||
))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub const CLOCK_REALTIME_COARSE: ClockId = ClockId(libc::CLOCK_REALTIME_COARSE);
|
||||
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub const CLOCK_REALTIME_FAST: ClockId = ClockId(libc::CLOCK_REALTIME_FAST);
|
||||
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub const CLOCK_REALTIME_PRECISE: ClockId = ClockId(libc::CLOCK_REALTIME_PRECISE);
|
||||
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub const CLOCK_SECOND: ClockId = ClockId(libc::CLOCK_SECOND);
|
||||
#[cfg(any(
|
||||
target_os = "emscripten",
|
||||
target_os = "fuchsia",
|
||||
all(target_os = "linux", target_env = "musl")
|
||||
))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub const CLOCK_SGI_CYCLE: ClockId = ClockId(libc::CLOCK_SGI_CYCLE);
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "emscripten",
|
||||
target_os = "fuchsia",
|
||||
target_os = "linux"
|
||||
))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub const CLOCK_TAI: ClockId = ClockId(libc::CLOCK_TAI);
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "emscripten",
|
||||
target_os = "fuchsia",
|
||||
target_os = "ios",
|
||||
target_os = "macos",
|
||||
target_os = "freebsd",
|
||||
target_os = "dragonfly",
|
||||
target_os = "linux"
|
||||
))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub const CLOCK_THREAD_CPUTIME_ID: ClockId = ClockId(libc::CLOCK_THREAD_CPUTIME_ID);
|
||||
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub const CLOCK_UPTIME: ClockId = ClockId(libc::CLOCK_UPTIME);
|
||||
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub const CLOCK_UPTIME_FAST: ClockId = ClockId(libc::CLOCK_UPTIME_FAST);
|
||||
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
pub const CLOCK_UPTIME_PRECISE: ClockId = ClockId(libc::CLOCK_UPTIME_PRECISE);
|
||||
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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 = "macos",
|
||||
target_os = "ios",
|
||||
target_os = "redox",
|
||||
target_os = "hermit",
|
||||
)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
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(
|
||||
target_os = "freebsd",
|
||||
target_os = "dragonfly",
|
||||
target_os = "linux",
|
||||
target_os = "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_i32(ret))
|
||||
}
|
||||
}
|
||||
43
third-party/vendor/nix/src/ucontext.rs
vendored
Normal file
43
third-party/vendor/nix/src/ucontext.rs
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
#[cfg(not(target_env = "musl"))]
|
||||
use crate::Result;
|
||||
#[cfg(not(target_env = "musl"))]
|
||||
use crate::errno::Errno;
|
||||
#[cfg(not(target_env = "musl"))]
|
||||
use std::mem;
|
||||
use crate::sys::signal::SigSet;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct UContext {
|
||||
context: libc::ucontext_t,
|
||||
}
|
||||
|
||||
impl UContext {
|
||||
#[cfg(not(target_env = "musl"))]
|
||||
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(target_env = "musl"))]
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
3352
third-party/vendor/nix/src/unistd.rs
vendored
Normal file
3352
third-party/vendor/nix/src/unistd.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue