• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
60 pub use libc;
61 
62 // Private internal modules
63 #[macro_use]
64 mod macros;
65 
66 // Public crates
67 #[cfg(not(target_os = "redox"))]
68 feature! {
69     #![feature = "dir"]
70     pub mod dir;
71 }
72 feature! {
73     #![feature = "env"]
74     pub mod env;
75 }
76 #[allow(missing_docs)]
77 pub mod errno;
78 feature! {
79     #![feature = "feature"]
80 
81     #[deny(missing_docs)]
82     pub mod features;
83 }
84 #[allow(missing_docs)]
85 pub mod fcntl;
86 feature! {
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"))]
105 feature! {
106     #![feature = "kmod"]
107     #[allow(missing_docs)]
108     pub mod kmod;
109 }
110 feature! {
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 ))]
120 feature! {
121     #![feature = "mqueue"]
122     pub mod mqueue;
123 }
124 feature! {
125     #![feature = "poll"]
126     pub mod poll;
127 }
128 #[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
129 feature! {
130     #![feature = "term"]
131     #[deny(missing_docs)]
132     pub mod pty;
133 }
134 feature! {
135     #![feature = "sched"]
136     pub mod sched;
137 }
138 pub mod sys;
139 feature! {
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 ))]
150 feature! {
151     #![feature = "ucontext"]
152     #[allow(missing_docs)]
153     pub mod ucontext;
154 }
155 #[allow(missing_docs)]
156 pub mod unistd;
157 
158 use std::ffi::{CStr, CString, OsStr};
159 use std::mem::MaybeUninit;
160 use std::os::unix::ffi::OsStrExt;
161 use std::path::{Path, PathBuf};
162 use std::{ptr, result, slice};
163 
164 use errno::Errno;
165 
166 /// Nix Result Type
167 pub 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.
179 pub type Error = Errno;
180 
181 /// Common trait used to represent file system paths by many Nix functions.
182 pub trait NixPath {
183     /// Is the path empty?
is_empty(&self) -> bool184     fn is_empty(&self) -> bool;
185 
186     /// Length of the path in bytes
len(&self) -> usize187     fn len(&self) -> usize;
188 
189     /// Execute a function with this path as a `CStr`.
190     ///
191     /// Mostly used internally by Nix.
with_nix_path<T, F>(&self, f: F) -> Result<T> where F: FnOnce(&CStr) -> T192     fn with_nix_path<T, F>(&self, f: F) -> Result<T>
193     where
194         F: FnOnce(&CStr) -> T;
195 }
196 
197 impl NixPath for str {
is_empty(&self) -> bool198     fn is_empty(&self) -> bool {
199         NixPath::is_empty(OsStr::new(self))
200     }
201 
len(&self) -> usize202     fn len(&self) -> usize {
203         NixPath::len(OsStr::new(self))
204     }
205 
with_nix_path<T, F>(&self, f: F) -> Result<T> where F: FnOnce(&CStr) -> T,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 
214 impl NixPath for OsStr {
is_empty(&self) -> bool215     fn is_empty(&self) -> bool {
216         self.as_bytes().is_empty()
217     }
218 
len(&self) -> usize219     fn len(&self) -> usize {
220         self.as_bytes().len()
221     }
222 
with_nix_path<T, F>(&self, f: F) -> Result<T> where F: FnOnce(&CStr) -> T,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 
231 impl NixPath for CStr {
is_empty(&self) -> bool232     fn is_empty(&self) -> bool {
233         self.to_bytes().is_empty()
234     }
235 
len(&self) -> usize236     fn len(&self) -> usize {
237         self.to_bytes().len()
238     }
239 
with_nix_path<T, F>(&self, f: F) -> Result<T> where F: FnOnce(&CStr) -> T,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 
248 impl NixPath for [u8] {
is_empty(&self) -> bool249     fn is_empty(&self) -> bool {
250         self.is_empty()
251     }
252 
len(&self) -> usize253     fn len(&self) -> usize {
254         self.len()
255     }
256 
with_nix_path<T, F>(&self, f: F) -> Result<T> where F: FnOnce(&CStr) -> T,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)]
with_nix_path_allocating<T, F>(from: &[u8], f: F) -> Result<T> where F: FnOnce(&CStr) -> T,292 fn with_nix_path_allocating<T, F>(from: &[u8], f: F) -> Result<T>
293 where
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 
302 impl NixPath for Path {
is_empty(&self) -> bool303     fn is_empty(&self) -> bool {
304         NixPath::is_empty(self.as_os_str())
305     }
306 
len(&self) -> usize307     fn len(&self) -> usize {
308         NixPath::len(self.as_os_str())
309     }
310 
with_nix_path<T, F>(&self, f: F) -> Result<T> where F: FnOnce(&CStr) -> T,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 
319 impl NixPath for PathBuf {
is_empty(&self) -> bool320     fn is_empty(&self) -> bool {
321         NixPath::is_empty(self.as_os_str())
322     }
323 
len(&self) -> usize324     fn len(&self) -> usize {
325         NixPath::len(self.as_os_str())
326     }
327 
with_nix_path<T, F>(&self, f: F) -> Result<T> where F: FnOnce(&CStr) -> T,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 }
335