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 target_os = "hurd"))]
139 #[deny(missing_docs)]
140 pub mod ifaddrs;
141 #[cfg(not(target_os = "redox"))]
142 #[deny(missing_docs)]
143 pub mod net;
144}
145#[cfg(linux_android)]
146feature! {
147 #![feature = "kmod"]
148 pub mod kmod;
149}
150feature! {
151 #![feature = "mount"]
152 pub mod mount;
153}
154#[cfg(any(
155 freebsdlike,
156 all(target_os = "linux", not(target_env = "ohos")),
157 target_os = "netbsd"
158))]
159feature! {
160 #![feature = "mqueue"]
161 pub mod mqueue;
162}
163feature! {
164 #![feature = "poll"]
165 pub mod poll;
166}
167#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
168feature! {
169 #![feature = "term"]
170 #[deny(missing_docs)]
171 pub mod pty;
172}
173feature! {
174 #![feature = "sched"]
175 pub mod sched;
176}
177pub mod sys;
178feature! {
179 #![feature = "time"]
180 pub mod time;
181}
182#[cfg(all(
185 target_os = "linux",
186 any(
187 target_arch = "aarch64",
188 target_arch = "s390x",
189 target_arch = "x86",
190 target_arch = "x86_64"
191 )
192))]
193feature! {
194 #![feature = "ucontext"]
195 #[allow(missing_docs)]
196 pub mod ucontext;
197}
198pub mod unistd;
199
200#[cfg(any(feature = "poll", feature = "event"))]
201mod poll_timeout;
202
203#[cfg(any(
204 target_os = "freebsd",
205 target_os = "haiku",
206 target_os = "linux",
207 target_os = "netbsd",
208 apple_targets
209))]
210feature! {
211 #![feature = "process"]
212 pub mod spawn;
213}
214
215feature! {
216 #![feature = "syslog"]
217 pub mod syslog;
218}
219
220use std::ffi::{CStr, CString, OsStr};
221use std::mem::MaybeUninit;
222use std::os::unix::ffi::OsStrExt;
223use std::path::{Path, PathBuf};
224use std::{ptr, result, slice};
225
226use errno::Errno;
227
228pub type Result<T> = result::Result<T, Errno>;
230
231pub type Error = Errno;
242
243pub trait NixPath {
245 fn is_empty(&self) -> bool;
247
248 fn len(&self) -> usize;
250
251 fn with_nix_path<T, F>(&self, f: F) -> Result<T>
255 where
256 F: FnOnce(&CStr) -> T;
257}
258
259impl NixPath for str {
260 fn is_empty(&self) -> bool {
261 NixPath::is_empty(OsStr::new(self))
262 }
263
264 fn len(&self) -> usize {
265 NixPath::len(OsStr::new(self))
266 }
267
268 fn with_nix_path<T, F>(&self, f: F) -> Result<T>
269 where
270 F: FnOnce(&CStr) -> T,
271 {
272 OsStr::new(self).with_nix_path(f)
273 }
274}
275
276impl NixPath for OsStr {
277 fn is_empty(&self) -> bool {
278 self.as_bytes().is_empty()
279 }
280
281 fn len(&self) -> usize {
282 self.as_bytes().len()
283 }
284
285 fn with_nix_path<T, F>(&self, f: F) -> Result<T>
286 where
287 F: FnOnce(&CStr) -> T,
288 {
289 self.as_bytes().with_nix_path(f)
290 }
291}
292
293impl NixPath for CStr {
294 fn is_empty(&self) -> bool {
295 self.to_bytes().is_empty()
296 }
297
298 fn len(&self) -> usize {
299 self.to_bytes().len()
300 }
301
302 fn with_nix_path<T, F>(&self, f: F) -> Result<T>
303 where
304 F: FnOnce(&CStr) -> T,
305 {
306 Ok(f(self))
307 }
308}
309
310impl NixPath for [u8] {
311 fn is_empty(&self) -> bool {
312 self.is_empty()
313 }
314
315 fn len(&self) -> usize {
316 self.len()
317 }
318
319 fn with_nix_path<T, F>(&self, f: F) -> Result<T>
320 where
321 F: FnOnce(&CStr) -> T,
322 {
323 const MAX_STACK_ALLOCATION: usize = 1024;
330
331 if self.len() >= MAX_STACK_ALLOCATION {
332 return with_nix_path_allocating(self, f);
333 }
334
335 let mut buf = MaybeUninit::<[u8; MAX_STACK_ALLOCATION]>::uninit();
336 let buf_ptr = buf.as_mut_ptr().cast();
337
338 unsafe {
339 ptr::copy_nonoverlapping(self.as_ptr(), buf_ptr, self.len());
340 buf_ptr.add(self.len()).write(0);
341 }
342
343 match CStr::from_bytes_with_nul(unsafe {
344 slice::from_raw_parts(buf_ptr, self.len() + 1)
345 }) {
346 Ok(s) => Ok(f(s)),
347 Err(_) => Err(Errno::EINVAL),
348 }
349 }
350}
351
352#[cold]
353#[inline(never)]
354fn with_nix_path_allocating<T, F>(from: &[u8], f: F) -> Result<T>
355where
356 F: FnOnce(&CStr) -> T,
357{
358 match CString::new(from) {
359 Ok(s) => Ok(f(&s)),
360 Err(_) => Err(Errno::EINVAL),
361 }
362}
363
364impl NixPath for Path {
365 fn is_empty(&self) -> bool {
366 NixPath::is_empty(self.as_os_str())
367 }
368
369 fn len(&self) -> usize {
370 NixPath::len(self.as_os_str())
371 }
372
373 fn with_nix_path<T, F>(&self, f: F) -> Result<T>
374 where
375 F: FnOnce(&CStr) -> T,
376 {
377 self.as_os_str().with_nix_path(f)
378 }
379}
380
381impl NixPath for PathBuf {
382 fn is_empty(&self) -> bool {
383 NixPath::is_empty(self.as_os_str())
384 }
385
386 fn len(&self) -> usize {
387 NixPath::len(self.as_os_str())
388 }
389
390 fn with_nix_path<T, F>(&self, f: F) -> Result<T>
391 where
392 F: FnOnce(&CStr) -> T,
393 {
394 self.as_os_str().with_nix_path(f)
395 }
396}
397
398#[cfg(any(
402 all(apple_targets, feature = "mount"),
403 all(linux_android, any(feature = "mount", feature = "fanotify"))
404))]
405pub(crate) fn with_opt_nix_path<P, T, F>(path: Option<&P>, f: F) -> Result<T>
406where
407 P: ?Sized + NixPath,
408 F: FnOnce(*const libc::c_char) -> T,
409{
410 match path {
411 Some(path) => path.with_nix_path(|p_str| f(p_str.as_ptr())),
412 None => Ok(f(ptr::null())),
413 }
414}