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 59pub use libc; 60 61// Private internal modules 62#[macro_use] 63mod macros; 64 65// Public crates 66#[cfg(not(target_os = "redox"))] 67feature! { 68 #![feature = "dir"] 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", target_os = "linux"))] 104feature! { 105 #![feature = "kmod"] 106 #[allow(missing_docs)] 107 pub mod kmod; 108} 109feature! { 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))] 119feature! { 120 #![feature = "mqueue"] 121 pub mod mqueue; 122} 123feature! { 124 #![feature = "poll"] 125 pub mod poll; 126} 127#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))] 128feature! { 129 #![feature = "term"] 130 #[deny(missing_docs)] 131 pub mod pty; 132} 133feature! { 134 #![feature = "sched"] 135 pub mod sched; 136} 137pub mod sys; 138feature! { 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))] 149feature! { 150 #![feature = "ucontext"] 151 #[allow(missing_docs)] 152 pub mod ucontext; 153} 154#[allow(missing_docs)] 155pub mod unistd; 156 157use std::ffi::{CStr, CString, OsStr}; 158use std::mem::MaybeUninit; 159use std::os::unix::ffi::OsStrExt; 160use std::path::{Path, PathBuf}; 161use std::{ptr, result, slice}; 162 163use errno::Errno; 164 165/// Nix Result Type 166pub 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. 178pub type Error = Errno; 179 180/// Common trait used to represent file system paths by many Nix functions. 181pub trait NixPath { 182 /// Is the path empty? 183 fn is_empty(&self) -> bool; 184 185 /// Length of the path in bytes 186 fn len(&self) -> usize; 187 188 /// Execute a function with this path as a `CStr`. 189 /// 190 /// Mostly used internally by Nix. 191 fn with_nix_path<T, F>(&self, f: F) -> Result<T> 192 where 193 F: FnOnce(&CStr) -> T; 194} 195 196impl NixPath for str { 197 fn is_empty(&self) -> bool { 198 NixPath::is_empty(OsStr::new(self)) 199 } 200 201 fn len(&self) -> usize { 202 NixPath::len(OsStr::new(self)) 203 } 204 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 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 224 F: FnOnce(&CStr) -> T, 225 { 226 self.as_bytes().with_nix_path(f) 227 } 228} 229 230impl NixPath for CStr { 231 fn is_empty(&self) -> bool { 232 self.to_bytes().is_empty() 233 } 234 235 fn len(&self) -> usize { 236 self.to_bytes().len() 237 } 238 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 247impl NixPath for [u8] { 248 fn is_empty(&self) -> bool { 249 self.is_empty() 250 } 251 252 fn len(&self) -> usize { 253 self.len() 254 } 255 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)] 291fn with_nix_path_allocating<T, F>(from: &[u8], f: F) -> Result<T> 292where 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 301impl NixPath for Path { 302 fn is_empty(&self) -> bool { 303 NixPath::is_empty(self.as_os_str()) 304 } 305 306 fn len(&self) -> usize { 307 NixPath::len(self.as_os_str()) 308 } 309 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 318impl NixPath for PathBuf { 319 fn is_empty(&self) -> bool { 320 NixPath::is_empty(self.as_os_str()) 321 } 322 323 fn len(&self) -> usize { 324 NixPath::len(self.as_os_str()) 325 } 326 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