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