nix/
lib.rs

1//! Rust friendly bindings to the various *nix system functions.
2//!
3//! Modules are structured according to the C header file that they would be
4//! defined in.
5//!
6//! # Features
7//!
8//! Nix uses the following Cargo features to enable optional functionality.
9//! They may be enabled in any combination.
10//! * `acct` - Process accounting
11//! * `aio` - POSIX AIO
12//! * `dir` - Stuff relating to directory iteration
13//! * `env` - Manipulate environment variables
14//! * `event` - Event-driven APIs, like `kqueue` and `epoll`
15//! * `fanotify` - Linux's `fanotify` filesystem events monitoring API
16//! * `feature` - Query characteristics of the OS at runtime
17//! * `fs` - File system functionality
18//! * `hostname` - Get and set the system's hostname
19//! * `inotify` - Linux's `inotify` file system notification API
20//! * `ioctl` - The `ioctl` syscall, and wrappers for many specific instances
21//! * `kmod` - Load and unload kernel modules
22//! * `mman` - Stuff relating to memory management
23//! * `mount` - Mount and unmount file systems
24//! * `mqueue` - POSIX message queues
25//! * `net` - Networking-related functionality
26//! * `personality` - Set the process execution domain
27//! * `poll` - APIs like `poll` and `select`
28//! * `process` - Stuff relating to running processes
29//! * `pthread` - POSIX threads
30//! * `ptrace` - Process tracing and debugging
31//! * `quota` - File system quotas
32//! * `reboot` - Reboot the system
33//! * `resource` - Process resource limits
34//! * `sched` - Manipulate process's scheduling
35//! * `socket` - Sockets, whether for networking or local use
36//! * `signal` - Send and receive signals to processes
37//! * `term` - Terminal control APIs
38//! * `time` - Query the operating system's clocks
39//! * `ucontext` - User thread context
40//! * `uio` - Vectored I/O
41//! * `user` - Stuff relating to users and groups
42//! * `zerocopy` - APIs like `sendfile` and `copy_file_range`
43#![crate_name = "nix"]
44#![cfg(unix)]
45#![allow(non_camel_case_types)]
46#![cfg_attr(test, deny(warnings))]
47#![recursion_limit = "500"]
48#![deny(unused)]
49#![allow(unused_macros)]
50#![cfg_attr(
51    not(all(
52        feature = "acct",
53        feature = "aio",
54        feature = "dir",
55        feature = "env",
56        feature = "event",
57        feature = "fanotify",
58        feature = "feature",
59        feature = "fs",
60        feature = "hostname",
61        feature = "inotify",
62        feature = "ioctl",
63        feature = "kmod",
64        feature = "mman",
65        feature = "mount",
66        feature = "mqueue",
67        feature = "net",
68        feature = "personality",
69        feature = "poll",
70        feature = "process",
71        feature = "pthread",
72        feature = "ptrace",
73        feature = "quota",
74        feature = "reboot",
75        feature = "resource",
76        feature = "sched",
77        feature = "socket",
78        feature = "signal",
79        feature = "term",
80        feature = "time",
81        feature = "ucontext",
82        feature = "uio",
83        feature = "user",
84        feature = "zerocopy",
85    )),
86    allow(unused_imports)
87)]
88#![deny(unstable_features)]
89#![deny(missing_copy_implementations)]
90#![deny(missing_debug_implementations)]
91#![warn(missing_docs)]
92#![cfg_attr(docsrs, feature(doc_cfg))]
93#![deny(clippy::cast_ptr_alignment)]
94#![deny(unsafe_op_in_unsafe_fn)]
95
96// Re-exported external crates
97pub use libc;
98
99// Private internal modules
100#[macro_use]
101mod macros;
102
103// Public crates
104#[cfg(not(target_os = "redox"))]
105feature! {
106    #![feature = "dir"]
107    pub mod dir;
108}
109feature! {
110    #![feature = "env"]
111    pub mod env;
112}
113#[allow(missing_docs)]
114pub mod errno;
115feature! {
116    #![feature = "feature"]
117
118    #[deny(missing_docs)]
119    pub mod features;
120}
121pub mod fcntl;
122feature! {
123    #![feature = "net"]
124
125    #[cfg(any(linux_android,
126              bsd,
127              solarish))]
128    #[deny(missing_docs)]
129    pub mod ifaddrs;
130    #[cfg(not(target_os = "redox"))]
131    #[deny(missing_docs)]
132    pub mod net;
133}
134#[cfg(linux_android)]
135feature! {
136    #![feature = "kmod"]
137    pub mod kmod;
138}
139feature! {
140    #![feature = "mount"]
141    pub mod mount;
142}
143#[cfg(any(freebsdlike, target_os = "linux", target_os = "netbsd"))]
144feature! {
145    #![feature = "mqueue"]
146    pub mod mqueue;
147}
148feature! {
149    #![feature = "poll"]
150    pub mod poll;
151}
152#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
153feature! {
154    #![feature = "term"]
155    #[deny(missing_docs)]
156    pub mod pty;
157}
158feature! {
159    #![feature = "sched"]
160    pub mod sched;
161}
162pub mod sys;
163feature! {
164    #![feature = "time"]
165    pub mod time;
166}
167// This can be implemented for other platforms as soon as libc
168// provides bindings for them.
169#[cfg(all(
170    target_os = "linux",
171    any(
172        target_arch = "aarch64",
173        target_arch = "s390x",
174        target_arch = "x86",
175        target_arch = "x86_64"
176    )
177))]
178feature! {
179    #![feature = "ucontext"]
180    #[allow(missing_docs)]
181    pub mod ucontext;
182}
183pub mod unistd;
184
185#[cfg(any(feature = "poll", feature = "event"))]
186mod poll_timeout;
187
188use std::ffi::{CStr, CString, OsStr};
189use std::mem::MaybeUninit;
190use std::os::unix::ffi::OsStrExt;
191use std::path::{Path, PathBuf};
192use std::{ptr, result, slice};
193
194use errno::Errno;
195
196/// Nix Result Type
197pub type Result<T> = result::Result<T, Errno>;
198
199/// Nix's main error type.
200///
201/// It's a wrapper around Errno.  As such, it's very interoperable with
202/// [`std::io::Error`], but it has the advantages of:
203/// * `Clone`
204/// * `Copy`
205/// * `Eq`
206/// * Small size
207/// * Represents all of the system's errnos, instead of just the most common
208/// ones.
209pub type Error = Errno;
210
211/// Common trait used to represent file system paths by many Nix functions.
212pub trait NixPath {
213    /// Is the path empty?
214    fn is_empty(&self) -> bool;
215
216    /// Length of the path in bytes
217    fn len(&self) -> usize;
218
219    /// Execute a function with this path as a `CStr`.
220    ///
221    /// Mostly used internally by Nix.
222    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
223    where
224        F: FnOnce(&CStr) -> T;
225}
226
227impl NixPath for str {
228    fn is_empty(&self) -> bool {
229        NixPath::is_empty(OsStr::new(self))
230    }
231
232    fn len(&self) -> usize {
233        NixPath::len(OsStr::new(self))
234    }
235
236    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
237    where
238        F: FnOnce(&CStr) -> T,
239    {
240        OsStr::new(self).with_nix_path(f)
241    }
242}
243
244impl NixPath for OsStr {
245    fn is_empty(&self) -> bool {
246        self.as_bytes().is_empty()
247    }
248
249    fn len(&self) -> usize {
250        self.as_bytes().len()
251    }
252
253    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
254    where
255        F: FnOnce(&CStr) -> T,
256    {
257        self.as_bytes().with_nix_path(f)
258    }
259}
260
261impl NixPath for CStr {
262    fn is_empty(&self) -> bool {
263        self.to_bytes().is_empty()
264    }
265
266    fn len(&self) -> usize {
267        self.to_bytes().len()
268    }
269
270    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
271    where
272        F: FnOnce(&CStr) -> T,
273    {
274        Ok(f(self))
275    }
276}
277
278impl NixPath for [u8] {
279    fn is_empty(&self) -> bool {
280        self.is_empty()
281    }
282
283    fn len(&self) -> usize {
284        self.len()
285    }
286
287    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
288    where
289        F: FnOnce(&CStr) -> T,
290    {
291        // The real PATH_MAX is typically 4096, but it's statistically unlikely to have a path
292        // longer than ~300 bytes. See the the PR description to get stats for your own machine.
293        // https://github.com/nix-rust/nix/pull/1656
294        //
295        // By being smaller than a memory page, we also avoid the compiler inserting a probe frame:
296        // https://docs.rs/compiler_builtins/latest/compiler_builtins/probestack/index.html
297        const MAX_STACK_ALLOCATION: usize = 1024;
298
299        if self.len() >= MAX_STACK_ALLOCATION {
300            return with_nix_path_allocating(self, f);
301        }
302
303        let mut buf = MaybeUninit::<[u8; MAX_STACK_ALLOCATION]>::uninit();
304        let buf_ptr = buf.as_mut_ptr().cast();
305
306        unsafe {
307            ptr::copy_nonoverlapping(self.as_ptr(), buf_ptr, self.len());
308            buf_ptr.add(self.len()).write(0);
309        }
310
311        match CStr::from_bytes_with_nul(unsafe {
312            slice::from_raw_parts(buf_ptr, self.len() + 1)
313        }) {
314            Ok(s) => Ok(f(s)),
315            Err(_) => Err(Errno::EINVAL),
316        }
317    }
318}
319
320#[cold]
321#[inline(never)]
322fn with_nix_path_allocating<T, F>(from: &[u8], f: F) -> Result<T>
323where
324    F: FnOnce(&CStr) -> T,
325{
326    match CString::new(from) {
327        Ok(s) => Ok(f(&s)),
328        Err(_) => Err(Errno::EINVAL),
329    }
330}
331
332impl NixPath for Path {
333    fn is_empty(&self) -> bool {
334        NixPath::is_empty(self.as_os_str())
335    }
336
337    fn len(&self) -> usize {
338        NixPath::len(self.as_os_str())
339    }
340
341    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
342    where
343        F: FnOnce(&CStr) -> T,
344    {
345        self.as_os_str().with_nix_path(f)
346    }
347}
348
349impl NixPath for PathBuf {
350    fn is_empty(&self) -> bool {
351        NixPath::is_empty(self.as_os_str())
352    }
353
354    fn len(&self) -> usize {
355        NixPath::len(self.as_os_str())
356    }
357
358    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
359    where
360        F: FnOnce(&CStr) -> T,
361    {
362        self.as_os_str().with_nix_path(f)
363    }
364}
365
366/// Like `NixPath::with_nix_path()`, but allow the `path` argument to be optional.
367///
368/// A NULL pointer will be provided if `path.is_none()`.
369#[cfg(any(
370    all(apple_targets, feature = "mount"),
371    all(linux_android, any(feature = "mount", feature = "fanotify"))
372))]
373pub(crate) fn with_opt_nix_path<P, T, F>(path: Option<&P>, f: F) -> Result<T>
374where
375    P: ?Sized + NixPath,
376    F: FnOnce(*const libc::c_char) -> T,
377{
378    match path {
379        Some(path) => path.with_nix_path(|p_str| f(p_str.as_ptr())),
380        None => Ok(f(ptr::null())),
381    }
382}