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