diff --git a/Cargo.lock b/Cargo.lock index 3b6c0bfaf62..9a95b8d0dfa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3543,6 +3543,7 @@ version = "0.8.0" dependencies = [ "clap", "fluent", + "libc", "nix", "rust-ini", "thiserror 2.0.18", @@ -3797,6 +3798,7 @@ version = "0.8.0" dependencies = [ "clap", "fluent", + "libc", "nix", "uucore", ] @@ -4155,6 +4157,7 @@ dependencies = [ "fluent", "foldhash 0.2.0", "itertools 0.14.0", + "libc", "memchr", "nix", "rand 0.10.1", @@ -4215,6 +4218,7 @@ dependencies = [ "cfg_aliases", "clap", "fluent", + "libc", "nix", "uucore", ] diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index c1fd0a51d68..139459464e8 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -1928,6 +1928,7 @@ dependencies = [ "jiff-icu", "libc", "parse_datetime", + "regex", "rustix", "uucore", "windows-sys", @@ -1957,7 +1958,7 @@ version = "0.8.0" dependencies = [ "clap", "fluent", - "nix", + "libc", "rust-ini", "thiserror", "uucore", @@ -2010,10 +2011,11 @@ dependencies = [ "fluent", "foldhash 0.2.0", "itertools", + "libc", "memchr", - "nix", - "rand", + "rand 0.10.0", "rayon", + "rustix", "self_cell", "tempfile", "thiserror", @@ -2097,7 +2099,6 @@ dependencies = [ "libc", "md-5", "memchr", - "nix", "num-traits", "os_display", "procfs", diff --git a/src/uu/env/Cargo.toml b/src/uu/env/Cargo.toml index 46aa1be7c99..eaa64eab6fc 100644 --- a/src/uu/env/Cargo.toml +++ b/src/uu/env/Cargo.toml @@ -26,6 +26,7 @@ uucore = { workspace = true, features = ["signals"] } fluent = { workspace = true } [target.'cfg(unix)'.dependencies] +libc = { workspace = true } nix = { workspace = true, features = ["signal"] } [[bin]] diff --git a/src/uu/env/src/env.rs b/src/uu/env/src/env.rs index 3343544253e..1dae336cf67 100644 --- a/src/uu/env/src/env.rs +++ b/src/uu/env/src/env.rs @@ -18,8 +18,6 @@ use native_int_str::{ Convert, NCvt, NativeIntStr, NativeIntString, NativeStr, from_native_int_representation_owned, }; #[cfg(unix)] -use nix::libc; -#[cfg(unix)] use nix::sys::signal::{ SigHandler::{SigDfl, SigIgn}, SigSet, SigmaskHow, Signal, signal, sigprocmask, diff --git a/src/uu/mknod/Cargo.toml b/src/uu/mknod/Cargo.toml index 03ad05b9d50..8583bfb1e58 100644 --- a/src/uu/mknod/Cargo.toml +++ b/src/uu/mknod/Cargo.toml @@ -24,6 +24,7 @@ doctest = false clap = { workspace = true } uucore = { workspace = true, features = ["mode", "fs"] } fluent = { workspace = true } +libc = { workspace = true } nix = { workspace = true } [features] diff --git a/src/uu/mknod/src/mknod.rs b/src/uu/mknod/src/mknod.rs index 39ac62cb62d..c8b3a33e0d3 100644 --- a/src/uu/mknod/src/mknod.rs +++ b/src/uu/mknod/src/mknod.rs @@ -6,7 +6,7 @@ // spell-checker:ignore (ToDO) parsemode makedev sysmacros perror IFBLK IFCHR IFIFO sflag use clap::{Arg, ArgAction, Command, value_parser}; -use nix::libc::{S_IRGRP, S_IROTH, S_IRUSR, S_IWGRP, S_IWOTH, S_IWUSR, mode_t}; +use libc::{S_IRGRP, S_IROTH, S_IRUSR, S_IWGRP, S_IWOTH, S_IWUSR, mode_t}; use nix::sys::stat::{Mode, SFlag, mknod as nix_mknod, umask as nix_umask}; use uucore::display::Quotable; diff --git a/src/uu/sort/Cargo.toml b/src/uu/sort/Cargo.toml index 09763a4f9b0..8deab232eee 100644 --- a/src/uu/sort/Cargo.toml +++ b/src/uu/sort/Cargo.toml @@ -51,6 +51,7 @@ foldhash = { workspace = true } ctrlc = { workspace = true } [target.'cfg(unix)'.dependencies] +libc = { workspace = true } nix = { workspace = true, features = ["resource"] } [dev-dependencies] diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index 8b321b55d53..8f46439d0c2 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -1410,8 +1410,6 @@ pub(crate) fn fd_soft_limit() -> Option { #[cfg(unix)] pub(crate) fn current_open_fd_count() -> Option { - use nix::libc; - fn count_dir(path: &str) -> Option { let entries = std::fs::read_dir(path).ok()?; let mut count = 0usize; @@ -1770,7 +1768,6 @@ fn locale_failed_to_set() -> bool { #[cfg(unix)] fn locale_failed_to_set() -> bool { - use nix::libc; unsafe { libc::setlocale(libc::LC_COLLATE, c"".as_ptr()).is_null() } } diff --git a/src/uu/stty/Cargo.toml b/src/uu/stty/Cargo.toml index e1fb8c5bac3..0f9be60cc67 100644 --- a/src/uu/stty/Cargo.toml +++ b/src/uu/stty/Cargo.toml @@ -24,6 +24,7 @@ uucore = { workspace = true, features = ["parser"] } fluent = { workspace = true } [target.'cfg(unix)'.dependencies] +libc = { workspace = true } nix = { workspace = true, features = ["ioctl", "term"] } [[bin]] diff --git a/src/uu/stty/src/stty.rs b/src/uu/stty/src/stty.rs index 686f00a823e..bde8054e989 100644 --- a/src/uu/stty/src/stty.rs +++ b/src/uu/stty/src/stty.rs @@ -19,14 +19,14 @@ mod flags; use crate::flags::AllFlags; use crate::flags::COMBINATION_SETTINGS; use clap::{Arg, ArgAction, ArgMatches, Command}; -use nix::libc::{O_NONBLOCK, TIOCGWINSZ, TIOCSWINSZ, c_ushort}; +use libc::{O_NONBLOCK, TIOCGWINSZ, TIOCSWINSZ, c_ushort}; #[cfg(all( target_os = "linux", not(target_arch = "powerpc"), not(target_arch = "powerpc64") ))] -use nix::libc::{TCGETS2, termios2}; +use libc::{TCGETS2, termios2}; use nix::sys::termios::{ ControlFlags, InputFlags, LocalFlags, OutputFlags, SetArg, SpecialCharacterIndices as S, @@ -520,7 +520,7 @@ fn parse_rows_cols(arg: &str) -> Option { /// - Returns `None` if format is invalid fn parse_saved_state(arg: &str) -> Option> { let parts: Vec<&str> = arg.split(':').collect(); - let expected_parts = 4 + nix::libc::NCCS; + let expected_parts = 4 + libc::NCCS; // GNU requires exactly the right number of parts for this platform if parts.len() != expected_parts { @@ -692,7 +692,7 @@ fn print_terminal_size( { // For some reason the normal nix Termios struct does not expose the line, // so we get the underlying libc::termios struct to get that information. - let libc_termios: nix::libc::termios = termios.clone().into(); + let libc_termios: libc::termios = termios.clone().into(); let line = libc_termios.c_line; printer.print(&translate!("stty-output-line", "line" => line)); } @@ -824,7 +824,7 @@ fn string_to_flag(option: &str) -> Option> { None } -fn control_char_to_string(cc: nix::libc::cc_t) -> nix::Result { +fn control_char_to_string(cc: libc::cc_t) -> nix::Result { if cc == 0 { return Ok(translate!("stty-output-undef")); } diff --git a/src/uucore/Cargo.toml b/src/uucore/Cargo.toml index 9d0e0d0f38b..3aaa135b691 100644 --- a/src/uucore/Cargo.toml +++ b/src/uucore/Cargo.toml @@ -93,6 +93,7 @@ unic-langid = { workspace = true } thiserror = { workspace = true } [target.'cfg(unix)'.dependencies] +libc = { workspace = true } nix = { workspace = true, features = [ "dir", "fs", diff --git a/src/uucore/src/lib/features/safe_traversal.rs b/src/uucore/src/lib/features/safe_traversal.rs index 98fc81dd928..f8aaa160968 100644 --- a/src/uucore/src/lib/features/safe_traversal.rs +++ b/src/uucore/src/lib/features/safe_traversal.rs @@ -24,7 +24,6 @@ use std::path::{Path, PathBuf}; use nix::dir::Dir; use nix::fcntl::{OFlag, openat}; -use nix::libc; use nix::sys::stat::{FchmodatFlags, FileStat, Mode, fchmodat, fstatat, mkdirat}; use nix::unistd::{Gid, Uid, UnlinkatFlags, fchown, fchownat, unlinkat}; use os_display::Quotable; diff --git a/src/uucore/src/lib/features/signals.rs b/src/uucore/src/lib/features/signals.rs index 275f7acd266..fccba70500c 100644 --- a/src/uucore/src/lib/features/signals.rs +++ b/src/uucore/src/lib/features/signals.rs @@ -12,14 +12,179 @@ #[cfg(unix)] use nix::errno::Errno; -#[cfg(any(target_os = "linux", target_os = "android"))] -use nix::libc; #[cfg(unix)] use nix::sys::signal::{ SaFlags, SigAction, SigHandler, SigHandler::SigDfl, SigHandler::SigIgn, SigSet, Signal, Signal::SIGINT, Signal::SIGPIPE, sigaction, signal, }; +// --------------------------------------------------------------------------- +// Thin libc-based signal wrappers +// +// These replace nix's signal module for operations that rustix does not cover +// (sigaction, signal(), SigSet, SigHandler, sigprocmask). +// --------------------------------------------------------------------------- +#[cfg(unix)] +pub mod csignal { + use std::io; + use std::mem::MaybeUninit; + + /// Signal handler disposition. + #[derive(Debug, Clone, Copy)] + pub enum SigHandler { + /// Default signal action. + SigDfl, + /// Ignore the signal. + SigIgn, + /// Call the given handler function. + Handler(extern "C" fn(libc::c_int)), + } + + impl SigHandler { + fn to_sigaction_handler(self) -> libc::sighandler_t { + match self { + Self::SigDfl => libc::SIG_DFL, + Self::SigIgn => libc::SIG_IGN, + Self::Handler(f) => f as libc::sighandler_t, + } + } + + fn from_sigaction_handler(handler: libc::sighandler_t) -> Self { + if handler == libc::SIG_DFL { + Self::SigDfl + } else if handler == libc::SIG_IGN { + Self::SigIgn + } else { + // SAFETY: the handler is a valid function pointer set by a previous signal() call + Self::Handler(unsafe { + std::mem::transmute::(handler) + }) + } + } + } + + /// Wrapper around `libc::sigset_t`. + #[derive(Clone)] + pub struct SigSet(libc::sigset_t); + + impl SigSet { + /// Creates an empty signal set. + pub fn empty() -> Self { + let mut set = MaybeUninit::::uninit(); + // SAFETY: sigemptyset initializes the sigset_t + unsafe { libc::sigemptyset(set.as_mut_ptr()) }; + Self(unsafe { set.assume_init() }) + } + + /// Creates a signal set with all signals. + pub fn all() -> Self { + let mut set = MaybeUninit::::uninit(); + // SAFETY: sigfillset initializes the sigset_t + unsafe { libc::sigfillset(set.as_mut_ptr()) }; + Self(unsafe { set.assume_init() }) + } + + /// Adds a signal to the set. + pub fn add(&mut self, signum: libc::c_int) { + // SAFETY: sigaddset operates on a valid sigset_t + unsafe { libc::sigaddset(&raw mut self.0, signum) }; + } + + /// Returns a pointer to the inner sigset_t. + pub fn as_ptr(&self) -> *const libc::sigset_t { + &raw const self.0 + } + } + + /// Flags for `sigaction`. + pub mod sa_flags { + pub const SA_RESTART: libc::c_int = libc::SA_RESTART as libc::c_int; + } + + /// How to modify the signal mask in `sigprocmask`. + #[derive(Debug, Clone, Copy)] + pub enum SigmaskHow { + /// Block the signals in the set. + Block, + /// Unblock the signals in the set. + Unblock, + /// Set the signal mask to the given set. + SetMask, + } + + impl SigmaskHow { + fn as_libc(self) -> libc::c_int { + match self { + Self::Block => libc::SIG_BLOCK, + Self::Unblock => libc::SIG_UNBLOCK, + Self::SetMask => libc::SIG_SETMASK, + } + } + } + + fn last_os_error() -> io::Error { + io::Error::last_os_error() + } + + /// Set the disposition of a signal using `libc::signal()`. + /// + /// Returns the previous signal handler. + /// + /// # Safety + /// If `handler` is `SigHandler::Handler(f)`, the function `f` must be + /// async-signal-safe. + pub unsafe fn set_signal_handler( + signum: libc::c_int, + handler: SigHandler, + ) -> io::Result { + let prev = unsafe { libc::signal(signum, handler.to_sigaction_handler()) }; + if prev == libc::SIG_ERR { + Err(last_os_error()) + } else { + Ok(SigHandler::from_sigaction_handler(prev)) + } + } + + /// Install a signal action using `libc::sigaction()`. + /// + /// # Safety + /// If the handler is a function pointer, it must be async-signal-safe. + pub unsafe fn set_signal_action( + signum: libc::c_int, + handler: SigHandler, + flags: libc::c_int, + mask: &SigSet, + ) -> io::Result<()> { + let mut sa: libc::sigaction = unsafe { std::mem::zeroed() }; + sa.sa_sigaction = handler.to_sigaction_handler(); + sa.sa_flags = flags as _; + sa.sa_mask = mask.0; + + let ret = unsafe { libc::sigaction(signum, &raw const sa, std::ptr::null_mut()) }; + if ret == -1 { + Err(last_os_error()) + } else { + Ok(()) + } + } + + /// Block/unblock/set signal mask using `libc::sigprocmask()`. + pub fn sigprocmask( + how: SigmaskHow, + set: Option<&SigSet>, + oldset: Option<&mut SigSet>, + ) -> io::Result<()> { + let set_ptr = set.map_or(std::ptr::null(), SigSet::as_ptr); + let oldset_ptr = oldset.map_or(std::ptr::null_mut(), |s| &raw mut s.0); + let ret = unsafe { libc::sigprocmask(how.as_libc(), set_ptr, oldset_ptr) }; + if ret == -1 { + Err(last_os_error()) + } else { + Ok(()) + } + } +} + /// The default signal value. pub static DEFAULT_SIGNAL: usize = 15; @@ -548,7 +713,6 @@ static STARTUP_STATE_WAS_CAPTURED: AtomicBool = AtomicBool::new(false); #[cfg(unix)] #[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn capture_startup_state() { - use nix::libc; use std::mem::MaybeUninit; use std::ptr; diff --git a/src/uucore/src/lib/mods/panic.rs b/src/uucore/src/lib/mods/panic.rs index 3df3c4f197a..f7f00f0c0fb 100644 --- a/src/uucore/src/lib/mods/panic.rs +++ b/src/uucore/src/lib/mods/panic.rs @@ -49,8 +49,6 @@ pub fn mute_sigpipe_panic() { /// variable. If set to "default", it restores SIGPIPE to SIG_DFL. #[cfg(unix)] pub fn preserve_inherited_sigpipe() { - use nix::libc; - // Check if parent specified that SIGPIPE should be default if std::env::var_os("RUST_SIGPIPE").is_some_and(|v| v == "default") { unsafe { libc::signal(libc::SIGPIPE, libc::SIG_DFL) }; diff --git a/tests/by-util/test_env.rs b/tests/by-util/test_env.rs index 1a1d504a13a..2726120e347 100644 --- a/tests/by-util/test_env.rs +++ b/tests/by-util/test_env.rs @@ -5,8 +5,6 @@ // spell-checker:ignore (words) bamf chdir rlimit prlimit COMSPEC cout cerr FFFD winsize xpixel ypixel Secho sighandler #![allow(clippy::missing_errors_doc)] -#[cfg(unix)] -use nix::libc; #[cfg(unix)] use nix::sys::signal::Signal; #[cfg(feature = "echo")]