1#![crate_name = "nix"]
45#![cfg(unix)]
46#![allow(non_camel_case_types)]
47#![allow(clippy::too_long_first_doc_paragraph)]
50#![recursion_limit = "500"]
51#![deny(unused)]
52#![deny(unexpected_cfgs)]
53#![allow(unused_macros)]
54#![cfg_attr(
55    not(all(
56        feature = "acct",
57        feature = "aio",
58        feature = "dir",
59        feature = "env",
60        feature = "event",
61        feature = "fanotify",
62        feature = "feature",
63        feature = "fs",
64        feature = "hostname",
65        feature = "inotify",
66        feature = "ioctl",
67        feature = "kmod",
68        feature = "mman",
69        feature = "mount",
70        feature = "mqueue",
71        feature = "net",
72        feature = "personality",
73        feature = "poll",
74        feature = "process",
75        feature = "pthread",
76        feature = "ptrace",
77        feature = "quota",
78        feature = "reboot",
79        feature = "resource",
80        feature = "sched",
81        feature = "socket",
82        feature = "signal",
83        feature = "syslog",
84        feature = "term",
85        feature = "time",
86        feature = "ucontext",
87        feature = "uio",
88        feature = "user",
89        feature = "zerocopy",
90    )),
91    allow(unused_imports)
92)]
93#![deny(unstable_features)]
94#![deny(missing_copy_implementations)]
95#![deny(missing_debug_implementations)]
96#![warn(missing_docs)]
97#![cfg_attr(docsrs, feature(doc_cfg))]
98#![deny(clippy::cast_ptr_alignment)]
99#![deny(unsafe_op_in_unsafe_fn)]
100#![allow(clippy::unwrap_or_default)]
105
106pub use libc;
108
109#[macro_use]
111mod macros;
112
113#[cfg(not(target_os = "redox"))]
115feature! {
116    #![feature = "dir"]
117    pub mod dir;
118}
119feature! {
120    #![feature = "env"]
121    pub mod env;
122}
123#[allow(missing_docs)]
124pub mod errno;
125feature! {
126    #![feature = "feature"]
127
128    #[deny(missing_docs)]
129    pub mod features;
130}
131pub mod fcntl;
132feature! {
133    #![feature = "net"]
134
135    #[cfg(any(linux_android,
136              bsd,
137              solarish))]
138    #[deny(missing_docs)]
139    pub mod ifaddrs;
140    #[cfg(not(target_os = "redox"))]
141    #[deny(missing_docs)]
142    pub mod net;
143}
144#[cfg(linux_android)]
145feature! {
146    #![feature = "kmod"]
147    pub mod kmod;
148}
149feature! {
150    #![feature = "mount"]
151    pub mod mount;
152}
153#[cfg(any(
154    freebsdlike,
155    all(target_os = "linux", not(target_env = "ohos")),
156    target_os = "netbsd"
157))]
158feature! {
159    #![feature = "mqueue"]
160    pub mod mqueue;
161}
162feature! {
163    #![feature = "poll"]
164    pub mod poll;
165}
166#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
167feature! {
168    #![feature = "term"]
169    #[deny(missing_docs)]
170    pub mod pty;
171}
172feature! {
173    #![feature = "sched"]
174    pub mod sched;
175}
176pub mod sys;
177feature! {
178    #![feature = "time"]
179    pub mod time;
180}
181#[cfg(all(
184    target_os = "linux",
185    any(
186        target_arch = "aarch64",
187        target_arch = "s390x",
188        target_arch = "x86",
189        target_arch = "x86_64"
190    )
191))]
192feature! {
193    #![feature = "ucontext"]
194    #[allow(missing_docs)]
195    pub mod ucontext;
196}
197pub mod unistd;
198
199#[cfg(any(feature = "poll", feature = "event"))]
200mod poll_timeout;
201
202#[cfg(any(
203    target_os = "freebsd",
204    target_os = "haiku",
205    target_os = "linux",
206    target_os = "netbsd",
207    apple_targets
208))]
209feature! {
210    #![feature = "process"]
211    pub mod spawn;
212}
213
214feature! {
215    #![feature = "syslog"]
216    pub mod syslog;
217}
218
219use std::ffi::{CStr, CString, OsStr};
220use std::mem::MaybeUninit;
221use std::os::unix::ffi::OsStrExt;
222use std::path::{Path, PathBuf};
223use std::{ptr, result, slice};
224
225use errno::Errno;
226
227pub type Result<T> = result::Result<T, Errno>;
229
230pub type Error = Errno;
241
242pub trait NixPath {
244    fn is_empty(&self) -> bool;
246
247    fn len(&self) -> usize;
249
250    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
254    where
255        F: FnOnce(&CStr) -> T;
256}
257
258impl NixPath for str {
259    fn is_empty(&self) -> bool {
260        NixPath::is_empty(OsStr::new(self))
261    }
262
263    fn len(&self) -> usize {
264        NixPath::len(OsStr::new(self))
265    }
266
267    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
268    where
269        F: FnOnce(&CStr) -> T,
270    {
271        OsStr::new(self).with_nix_path(f)
272    }
273}
274
275impl NixPath for OsStr {
276    fn is_empty(&self) -> bool {
277        self.as_bytes().is_empty()
278    }
279
280    fn len(&self) -> usize {
281        self.as_bytes().len()
282    }
283
284    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
285    where
286        F: FnOnce(&CStr) -> T,
287    {
288        self.as_bytes().with_nix_path(f)
289    }
290}
291
292impl NixPath for CStr {
293    fn is_empty(&self) -> bool {
294        self.to_bytes().is_empty()
295    }
296
297    fn len(&self) -> usize {
298        self.to_bytes().len()
299    }
300
301    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
302    where
303        F: FnOnce(&CStr) -> T,
304    {
305        Ok(f(self))
306    }
307}
308
309impl NixPath for [u8] {
310    fn is_empty(&self) -> bool {
311        self.is_empty()
312    }
313
314    fn len(&self) -> usize {
315        self.len()
316    }
317
318    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
319    where
320        F: FnOnce(&CStr) -> T,
321    {
322        const MAX_STACK_ALLOCATION: usize = 1024;
329
330        if self.len() >= MAX_STACK_ALLOCATION {
331            return with_nix_path_allocating(self, f);
332        }
333
334        let mut buf = MaybeUninit::<[u8; MAX_STACK_ALLOCATION]>::uninit();
335        let buf_ptr = buf.as_mut_ptr().cast();
336
337        unsafe {
338            ptr::copy_nonoverlapping(self.as_ptr(), buf_ptr, self.len());
339            buf_ptr.add(self.len()).write(0);
340        }
341
342        match CStr::from_bytes_with_nul(unsafe {
343            slice::from_raw_parts(buf_ptr, self.len() + 1)
344        }) {
345            Ok(s) => Ok(f(s)),
346            Err(_) => Err(Errno::EINVAL),
347        }
348    }
349}
350
351#[cold]
352#[inline(never)]
353fn with_nix_path_allocating<T, F>(from: &[u8], f: F) -> Result<T>
354where
355    F: FnOnce(&CStr) -> T,
356{
357    match CString::new(from) {
358        Ok(s) => Ok(f(&s)),
359        Err(_) => Err(Errno::EINVAL),
360    }
361}
362
363impl NixPath for Path {
364    fn is_empty(&self) -> bool {
365        NixPath::is_empty(self.as_os_str())
366    }
367
368    fn len(&self) -> usize {
369        NixPath::len(self.as_os_str())
370    }
371
372    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
373    where
374        F: FnOnce(&CStr) -> T,
375    {
376        self.as_os_str().with_nix_path(f)
377    }
378}
379
380impl NixPath for PathBuf {
381    fn is_empty(&self) -> bool {
382        NixPath::is_empty(self.as_os_str())
383    }
384
385    fn len(&self) -> usize {
386        NixPath::len(self.as_os_str())
387    }
388
389    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
390    where
391        F: FnOnce(&CStr) -> T,
392    {
393        self.as_os_str().with_nix_path(f)
394    }
395}
396
397#[cfg(any(
401    all(apple_targets, feature = "mount"),
402    all(linux_android, any(feature = "mount", feature = "fanotify"))
403))]
404pub(crate) fn with_opt_nix_path<P, T, F>(path: Option<&P>, f: F) -> Result<T>
405where
406    P: ?Sized + NixPath,
407    F: FnOnce(*const libc::c_char) -> T,
408{
409    match path {
410        Some(path) => path.with_nix_path(|p_str| f(p_str.as_ptr())),
411        None => Ok(f(ptr::null())),
412    }
413}