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