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#![allow(clippy::bad_bit_mask)]
58
59// Re-exported external crates
60pub use libc;
61
62// Private internal modules
63#[macro_use]
64mod macros;
65
66// Public crates
67#[cfg(not(target_os = "redox"))]
68feature! {
69    #![feature = "dir"]
70    pub mod dir;
71}
72feature! {
73    #![feature = "env"]
74    pub mod env;
75}
76#[allow(missing_docs)]
77pub mod errno;
78feature! {
79    #![feature = "feature"]
80
81    #[deny(missing_docs)]
82    pub mod features;
83}
84#[allow(missing_docs)]
85pub mod fcntl;
86feature! {
87    #![feature = "net"]
88
89    #[cfg(any(target_os = "android",
90              target_os = "dragonfly",
91              target_os = "freebsd",
92              target_os = "ios",
93              target_os = "linux",
94              target_os = "macos",
95              target_os = "netbsd",
96              target_os = "illumos",
97              target_os = "openbsd"))]
98    #[deny(missing_docs)]
99    pub mod ifaddrs;
100    #[cfg(not(target_os = "redox"))]
101    #[deny(missing_docs)]
102    pub mod net;
103}
104#[cfg(any(target_os = "android", target_os = "linux"))]
105feature! {
106    #![feature = "kmod"]
107    #[allow(missing_docs)]
108    pub mod kmod;
109}
110feature! {
111    #![feature = "mount"]
112    pub mod mount;
113}
114#[cfg(any(
115    target_os = "dragonfly",
116    target_os = "freebsd",
117    target_os = "linux",
118    target_os = "netbsd"
119))]
120feature! {
121    #![feature = "mqueue"]
122    pub mod mqueue;
123}
124feature! {
125    #![feature = "poll"]
126    pub mod poll;
127}
128#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
129feature! {
130    #![feature = "term"]
131    #[deny(missing_docs)]
132    pub mod pty;
133}
134feature! {
135    #![feature = "sched"]
136    pub mod sched;
137}
138pub mod sys;
139feature! {
140    #![feature = "time"]
141    #[allow(missing_docs)]
142    pub mod time;
143}
144// This can be implemented for other platforms as soon as libc
145// provides bindings for them.
146#[cfg(all(
147    target_os = "linux",
148    any(target_arch = "s390x", target_arch = "x86", target_arch = "x86_64")
149))]
150feature! {
151    #![feature = "ucontext"]
152    #[allow(missing_docs)]
153    pub mod ucontext;
154}
155#[allow(missing_docs)]
156pub mod unistd;
157
158use std::ffi::{CStr, CString, OsStr};
159use std::mem::MaybeUninit;
160use std::os::unix::ffi::OsStrExt;
161use std::path::{Path, PathBuf};
162use std::{ptr, result, slice};
163
164use errno::Errno;
165
166/// Nix Result Type
167pub type Result<T> = result::Result<T, Errno>;
168
169/// Nix's main error type.
170///
171/// It's a wrapper around Errno.  As such, it's very interoperable with
172/// [`std::io::Error`], but it has the advantages of:
173/// * `Clone`
174/// * `Copy`
175/// * `Eq`
176/// * Small size
177/// * Represents all of the system's errnos, instead of just the most common
178/// ones.
179pub type Error = Errno;
180
181/// Common trait used to represent file system paths by many Nix functions.
182pub trait NixPath {
183    /// Is the path empty?
184    fn is_empty(&self) -> bool;
185
186    /// Length of the path in bytes
187    fn len(&self) -> usize;
188
189    /// Execute a function with this path as a `CStr`.
190    ///
191    /// Mostly used internally by Nix.
192    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
193    where
194        F: FnOnce(&CStr) -> T;
195}
196
197impl NixPath for str {
198    fn is_empty(&self) -> bool {
199        NixPath::is_empty(OsStr::new(self))
200    }
201
202    fn len(&self) -> usize {
203        NixPath::len(OsStr::new(self))
204    }
205
206    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
207    where
208        F: FnOnce(&CStr) -> T,
209    {
210        OsStr::new(self).with_nix_path(f)
211    }
212}
213
214impl NixPath for OsStr {
215    fn is_empty(&self) -> bool {
216        self.as_bytes().is_empty()
217    }
218
219    fn len(&self) -> usize {
220        self.as_bytes().len()
221    }
222
223    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
224    where
225        F: FnOnce(&CStr) -> T,
226    {
227        self.as_bytes().with_nix_path(f)
228    }
229}
230
231impl NixPath for CStr {
232    fn is_empty(&self) -> bool {
233        self.to_bytes().is_empty()
234    }
235
236    fn len(&self) -> usize {
237        self.to_bytes().len()
238    }
239
240    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
241    where
242        F: FnOnce(&CStr) -> T,
243    {
244        Ok(f(self))
245    }
246}
247
248impl NixPath for [u8] {
249    fn is_empty(&self) -> bool {
250        self.is_empty()
251    }
252
253    fn len(&self) -> usize {
254        self.len()
255    }
256
257    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
258    where
259        F: FnOnce(&CStr) -> T,
260    {
261        // The real PATH_MAX is typically 4096, but it's statistically unlikely to have a path
262        // longer than ~300 bytes. See the the PR description to get stats for your own machine.
263        // https://github.com/nix-rust/nix/pull/1656
264        //
265        // By being smaller than a memory page, we also avoid the compiler inserting a probe frame:
266        // https://docs.rs/compiler_builtins/latest/compiler_builtins/probestack/index.html
267        const MAX_STACK_ALLOCATION: usize = 1024;
268
269        if self.len() >= MAX_STACK_ALLOCATION {
270            return with_nix_path_allocating(self, f);
271        }
272
273        let mut buf = MaybeUninit::<[u8; MAX_STACK_ALLOCATION]>::uninit();
274        let buf_ptr = buf.as_mut_ptr() as *mut u8;
275
276        unsafe {
277            ptr::copy_nonoverlapping(self.as_ptr(), buf_ptr, self.len());
278            buf_ptr.add(self.len()).write(0);
279        }
280
281        match CStr::from_bytes_with_nul(unsafe {
282            slice::from_raw_parts(buf_ptr, self.len() + 1)
283        }) {
284            Ok(s) => Ok(f(s)),
285            Err(_) => Err(Errno::EINVAL),
286        }
287    }
288}
289
290#[cold]
291#[inline(never)]
292fn with_nix_path_allocating<T, F>(from: &[u8], f: F) -> Result<T>
293where
294    F: FnOnce(&CStr) -> T,
295{
296    match CString::new(from) {
297        Ok(s) => Ok(f(&s)),
298        Err(_) => Err(Errno::EINVAL),
299    }
300}
301
302impl NixPath for Path {
303    fn is_empty(&self) -> bool {
304        NixPath::is_empty(self.as_os_str())
305    }
306
307    fn len(&self) -> usize {
308        NixPath::len(self.as_os_str())
309    }
310
311    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
312    where
313        F: FnOnce(&CStr) -> T,
314    {
315        self.as_os_str().with_nix_path(f)
316    }
317}
318
319impl NixPath for PathBuf {
320    fn is_empty(&self) -> bool {
321        NixPath::is_empty(self.as_os_str())
322    }
323
324    fn len(&self) -> usize {
325        NixPath::len(self.as_os_str())
326    }
327
328    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
329    where
330        F: FnOnce(&CStr) -> T,
331    {
332        self.as_os_str().with_nix_path(f)
333    }
334}