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
59 pub use libc;
60
61 // Private internal modules
62 #[macro_use]
63 mod macros;
64
65 // Public crates
66 #[cfg(not(target_os = "redox"))]
67 feature! {
68 #![feature = "dir"]
69 pub mod dir;
70 }
71 feature! {
72 #![feature = "env"]
73 pub mod env;
74 }
75 #[allow(missing_docs)]
76 pub mod errno;
77 feature! {
78 #![feature = "feature"]
79
80 #[deny(missing_docs)]
81 pub mod features;
82 }
83 #[allow(missing_docs)]
84 pub mod fcntl;
85 feature! {
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"))]
104 feature! {
105 #![feature = "kmod"]
106 #[allow(missing_docs)]
107 pub mod kmod;
108 }
109 feature! {
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 ))]
119 feature! {
120 #![feature = "mqueue"]
121 pub mod mqueue;
122 }
123 feature! {
124 #![feature = "poll"]
125 pub mod poll;
126 }
127 #[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
128 feature! {
129 #![feature = "term"]
130 #[deny(missing_docs)]
131 pub mod pty;
132 }
133 feature! {
134 #![feature = "sched"]
135 pub mod sched;
136 }
137 pub mod sys;
138 feature! {
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 ))]
149 feature! {
150 #![feature = "ucontext"]
151 #[allow(missing_docs)]
152 pub mod ucontext;
153 }
154 #[allow(missing_docs)]
155 pub mod unistd;
156
157 use std::ffi::{CStr, CString, OsStr};
158 use std::mem::MaybeUninit;
159 use std::os::unix::ffi::OsStrExt;
160 use std::path::{Path, PathBuf};
161 use std::{ptr, result, slice};
162
163 use errno::Errno;
164
165 /// Nix Result Type
166 pub 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.
178 pub type Error = Errno;
179
180 /// Common trait used to represent file system paths by many Nix functions.
181 pub trait NixPath {
182 /// Is the path empty?
is_empty(&self) -> bool183 fn is_empty(&self) -> bool;
184
185 /// Length of the path in bytes
len(&self) -> usize186 fn len(&self) -> usize;
187
188 /// Execute a function with this path as a `CStr`.
189 ///
190 /// Mostly used internally by Nix.
with_nix_path<T, F>(&self, f: F) -> Result<T> where F: FnOnce(&CStr) -> T191 fn with_nix_path<T, F>(&self, f: F) -> Result<T>
192 where
193 F: FnOnce(&CStr) -> T;
194 }
195
196 impl NixPath for str {
is_empty(&self) -> bool197 fn is_empty(&self) -> bool {
198 NixPath::is_empty(OsStr::new(self))
199 }
200
len(&self) -> usize201 fn len(&self) -> usize {
202 NixPath::len(OsStr::new(self))
203 }
204
with_nix_path<T, F>(&self, f: F) -> Result<T> where F: FnOnce(&CStr) -> T,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
213 impl NixPath for OsStr {
is_empty(&self) -> bool214 fn is_empty(&self) -> bool {
215 self.as_bytes().is_empty()
216 }
217
len(&self) -> usize218 fn len(&self) -> usize {
219 self.as_bytes().len()
220 }
221
with_nix_path<T, F>(&self, f: F) -> Result<T> where F: FnOnce(&CStr) -> T,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
230 impl NixPath for CStr {
is_empty(&self) -> bool231 fn is_empty(&self) -> bool {
232 self.to_bytes().is_empty()
233 }
234
len(&self) -> usize235 fn len(&self) -> usize {
236 self.to_bytes().len()
237 }
238
with_nix_path<T, F>(&self, f: F) -> Result<T> where F: FnOnce(&CStr) -> T,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
247 impl NixPath for [u8] {
is_empty(&self) -> bool248 fn is_empty(&self) -> bool {
249 self.is_empty()
250 }
251
len(&self) -> usize252 fn len(&self) -> usize {
253 self.len()
254 }
255
with_nix_path<T, F>(&self, f: F) -> Result<T> where F: FnOnce(&CStr) -> T,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)]
with_nix_path_allocating<T, F>(from: &[u8], f: F) -> Result<T> where F: FnOnce(&CStr) -> T,291 fn with_nix_path_allocating<T, F>(from: &[u8], f: F) -> Result<T>
292 where
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
301 impl NixPath for Path {
is_empty(&self) -> bool302 fn is_empty(&self) -> bool {
303 NixPath::is_empty(self.as_os_str())
304 }
305
len(&self) -> usize306 fn len(&self) -> usize {
307 NixPath::len(self.as_os_str())
308 }
309
with_nix_path<T, F>(&self, f: F) -> Result<T> where F: FnOnce(&CStr) -> T,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
318 impl NixPath for PathBuf {
is_empty(&self) -> bool319 fn is_empty(&self) -> bool {
320 NixPath::is_empty(self.as_os_str())
321 }
322
len(&self) -> usize323 fn len(&self) -> usize {
324 NixPath::len(self.as_os_str())
325 }
326
with_nix_path<T, F>(&self, f: F) -> Result<T> where F: FnOnce(&CStr) -> T,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 }
334