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//! * `syslog` - System logging
38//! * `term` - Terminal control APIs
39//! * `time` - Query the operating system's clocks
40//! * `ucontext` - User thread context
41//! * `uio` - Vectored I/O
42//! * `user` - Stuff relating to users and groups
43//! * `zerocopy` - APIs like `sendfile` and `copy_file_range`
44#![crate_name = "nix"]
45#![cfg(unix)]
46#![allow(non_camel_case_types)]
47// A clear document is a good document no matter if it has a summary in its
48// first paragraph or not.
49#![allow(clippy::too_long_first_doc_paragraph)]
50#![recursion_limit = "500"]
51#![deny(unused)]
52#![deny(unexpected_cfgs)]
53#![allow(unused_macros)]
54#![cfg_attr(
55    not(all(
56        feature = "acct",
57        feature = "aio",
58        feature = "dir",
59        feature = "env",
60        feature = "event",
61        feature = "fanotify",
62        feature = "feature",
63        feature = "fs",
64        feature = "hostname",
65        feature = "inotify",
66        feature = "ioctl",
67        feature = "kmod",
68        feature = "mman",
69        feature = "mount",
70        feature = "mqueue",
71        feature = "net",
72        feature = "personality",
73        feature = "poll",
74        feature = "process",
75        feature = "pthread",
76        feature = "ptrace",
77        feature = "quota",
78        feature = "reboot",
79        feature = "resource",
80        feature = "sched",
81        feature = "socket",
82        feature = "signal",
83        feature = "syslog",
84        feature = "term",
85        feature = "time",
86        feature = "ucontext",
87        feature = "uio",
88        feature = "user",
89        feature = "zerocopy",
90    )),
91    allow(unused_imports)
92)]
93#![deny(unstable_features)]
94#![deny(missing_copy_implementations)]
95#![deny(missing_debug_implementations)]
96#![warn(missing_docs)]
97#![cfg_attr(docsrs, feature(doc_cfg))]
98#![deny(clippy::cast_ptr_alignment)]
99#![deny(unsafe_op_in_unsafe_fn)]
100// I found the change suggested by this rules could hurt code readability. I cannot
101// remeber every type's default value, in such cases, it forces me to open
102// the std doc to insepct the Default value, which is unnecessary with
103// `.unwrap_or(value)`.
104#![allow(clippy::unwrap_or_default)]
105
106// Re-exported external crates
107pub use libc;
108
109// Private internal modules
110#[macro_use]
111mod macros;
112
113// Public crates
114#[cfg(not(target_os = "redox"))]
115feature! {
116    #![feature = "dir"]
117    pub mod dir;
118}
119feature! {
120    #![feature = "env"]
121    pub mod env;
122}
123#[allow(missing_docs)]
124pub mod errno;
125feature! {
126    #![feature = "feature"]
127
128    #[deny(missing_docs)]
129    pub mod features;
130}
131pub mod fcntl;
132feature! {
133    #![feature = "net"]
134
135    #[cfg(any(linux_android,
136              bsd,
137              solarish,
138              target_os = "hurd"))]
139    #[deny(missing_docs)]
140    pub mod ifaddrs;
141    #[cfg(not(target_os = "redox"))]
142    #[deny(missing_docs)]
143    pub mod net;
144}
145#[cfg(linux_android)]
146feature! {
147    #![feature = "kmod"]
148    pub mod kmod;
149}
150feature! {
151    #![feature = "mount"]
152    pub mod mount;
153}
154#[cfg(any(
155    freebsdlike,
156    all(target_os = "linux", not(target_env = "ohos")),
157    target_os = "netbsd"
158))]
159feature! {
160    #![feature = "mqueue"]
161    pub mod mqueue;
162}
163feature! {
164    #![feature = "poll"]
165    pub mod poll;
166}
167#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
168feature! {
169    #![feature = "term"]
170    #[deny(missing_docs)]
171    pub mod pty;
172}
173feature! {
174    #![feature = "sched"]
175    pub mod sched;
176}
177pub mod sys;
178feature! {
179    #![feature = "time"]
180    pub mod time;
181}
182// This can be implemented for other platforms as soon as libc
183// provides bindings for them.
184#[cfg(all(
185    target_os = "linux",
186    any(
187        target_arch = "aarch64",
188        target_arch = "s390x",
189        target_arch = "x86",
190        target_arch = "x86_64"
191    )
192))]
193feature! {
194    #![feature = "ucontext"]
195    #[allow(missing_docs)]
196    pub mod ucontext;
197}
198pub mod unistd;
199
200#[cfg(any(feature = "poll", feature = "event"))]
201mod poll_timeout;
202
203#[cfg(any(
204    target_os = "freebsd",
205    target_os = "haiku",
206    target_os = "linux",
207    target_os = "netbsd",
208    apple_targets
209))]
210feature! {
211    #![feature = "process"]
212    pub mod spawn;
213}
214
215feature! {
216    #![feature = "syslog"]
217    pub mod syslog;
218}
219
220use std::ffi::{CStr, CString, OsStr};
221use std::mem::MaybeUninit;
222use std::os::unix::ffi::OsStrExt;
223use std::path::{Path, PathBuf};
224use std::{ptr, result, slice};
225
226use errno::Errno;
227
228/// Nix Result Type
229pub type Result<T> = result::Result<T, Errno>;
230
231/// Nix's main error type.
232///
233/// It's a wrapper around Errno.  As such, it's very interoperable with
234/// [`std::io::Error`], but it has the advantages of:
235/// * `Clone`
236/// * `Copy`
237/// * `Eq`
238/// * Small size
239/// * Represents all of the system's errnos, instead of just the most common
240///   ones.
241pub type Error = Errno;
242
243/// Common trait used to represent file system paths by many Nix functions.
244pub trait NixPath {
245    /// Is the path empty?
246    fn is_empty(&self) -> bool;
247
248    /// Length of the path in bytes
249    fn len(&self) -> usize;
250
251    /// Execute a function with this path as a `CStr`.
252    ///
253    /// Mostly used internally by Nix.
254    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
255    where
256        F: FnOnce(&CStr) -> T;
257}
258
259impl NixPath for str {
260    fn is_empty(&self) -> bool {
261        NixPath::is_empty(OsStr::new(self))
262    }
263
264    fn len(&self) -> usize {
265        NixPath::len(OsStr::new(self))
266    }
267
268    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
269    where
270        F: FnOnce(&CStr) -> T,
271    {
272        OsStr::new(self).with_nix_path(f)
273    }
274}
275
276impl NixPath for OsStr {
277    fn is_empty(&self) -> bool {
278        self.as_bytes().is_empty()
279    }
280
281    fn len(&self) -> usize {
282        self.as_bytes().len()
283    }
284
285    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
286    where
287        F: FnOnce(&CStr) -> T,
288    {
289        self.as_bytes().with_nix_path(f)
290    }
291}
292
293impl NixPath for CStr {
294    fn is_empty(&self) -> bool {
295        self.to_bytes().is_empty()
296    }
297
298    fn len(&self) -> usize {
299        self.to_bytes().len()
300    }
301
302    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
303    where
304        F: FnOnce(&CStr) -> T,
305    {
306        Ok(f(self))
307    }
308}
309
310impl NixPath for [u8] {
311    fn is_empty(&self) -> bool {
312        self.is_empty()
313    }
314
315    fn len(&self) -> usize {
316        self.len()
317    }
318
319    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
320    where
321        F: FnOnce(&CStr) -> T,
322    {
323        // The real PATH_MAX is typically 4096, but it's statistically unlikely to have a path
324        // longer than ~300 bytes. See the PR description to get stats for your own machine.
325        // https://github.com/nix-rust/nix/pull/1656
326        //
327        // By being smaller than a memory page, we also avoid the compiler inserting a probe frame:
328        // https://docs.rs/compiler_builtins/latest/compiler_builtins/probestack/index.html
329        const MAX_STACK_ALLOCATION: usize = 1024;
330
331        if self.len() >= MAX_STACK_ALLOCATION {
332            return with_nix_path_allocating(self, f);
333        }
334
335        let mut buf = MaybeUninit::<[u8; MAX_STACK_ALLOCATION]>::uninit();
336        let buf_ptr = buf.as_mut_ptr().cast();
337
338        unsafe {
339            ptr::copy_nonoverlapping(self.as_ptr(), buf_ptr, self.len());
340            buf_ptr.add(self.len()).write(0);
341        }
342
343        match CStr::from_bytes_with_nul(unsafe {
344            slice::from_raw_parts(buf_ptr, self.len() + 1)
345        }) {
346            Ok(s) => Ok(f(s)),
347            Err(_) => Err(Errno::EINVAL),
348        }
349    }
350}
351
352#[cold]
353#[inline(never)]
354fn with_nix_path_allocating<T, F>(from: &[u8], f: F) -> Result<T>
355where
356    F: FnOnce(&CStr) -> T,
357{
358    match CString::new(from) {
359        Ok(s) => Ok(f(&s)),
360        Err(_) => Err(Errno::EINVAL),
361    }
362}
363
364impl NixPath for Path {
365    fn is_empty(&self) -> bool {
366        NixPath::is_empty(self.as_os_str())
367    }
368
369    fn len(&self) -> usize {
370        NixPath::len(self.as_os_str())
371    }
372
373    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
374    where
375        F: FnOnce(&CStr) -> T,
376    {
377        self.as_os_str().with_nix_path(f)
378    }
379}
380
381impl NixPath for PathBuf {
382    fn is_empty(&self) -> bool {
383        NixPath::is_empty(self.as_os_str())
384    }
385
386    fn len(&self) -> usize {
387        NixPath::len(self.as_os_str())
388    }
389
390    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
391    where
392        F: FnOnce(&CStr) -> T,
393    {
394        self.as_os_str().with_nix_path(f)
395    }
396}
397
398/// Like `NixPath::with_nix_path()`, but allow the `path` argument to be optional.
399///
400/// A NULL pointer will be provided if `path.is_none()`.
401#[cfg(any(
402    all(apple_targets, feature = "mount"),
403    all(linux_android, any(feature = "mount", feature = "fanotify"))
404))]
405pub(crate) fn with_opt_nix_path<P, T, F>(path: Option<&P>, f: F) -> Result<T>
406where
407    P: ?Sized + NixPath,
408    F: FnOnce(*const libc::c_char) -> T,
409{
410    match path {
411        Some(path) => path.with_nix_path(|p_str| f(p_str.as_ptr())),
412        None => Ok(f(ptr::null())),
413    }
414}