• 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 
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