mirror of
https://github.com/uutils/coreutils.git
synced 2026-06-10 15:48:22 -07:00
refactor: migrate all utilities from nix to rustix/libc
Migrate 19 utility crates from nix to rustix and direct libc calls: - kill, timeout, env: signal handling via csignal wrappers + libc - sync, cat, tail, dd, tsort: file operations via rustix::fs - touch: futimens via rustix::fs::futimens - stty, tty: terminal ops via rustix::termios + libc ioctl - sort: getrlimit via rustix::process, sysconf via libc - wc, cp, df: stat/fstat via rustix::fs - nice: priority via rustix::process - mkfifo, mknod: via direct libc calls Also add rustix::io::Errno conversions to uucore error.rs.
This commit is contained in:
Generated
+10
-10
@@ -3405,7 +3405,6 @@ dependencies = [
|
||||
"fluent",
|
||||
"indicatif",
|
||||
"libc",
|
||||
"nix",
|
||||
"selinux",
|
||||
"tempfile",
|
||||
"thiserror 2.0.18",
|
||||
@@ -3466,7 +3465,7 @@ dependencies = [
|
||||
"fluent",
|
||||
"gcd",
|
||||
"libc",
|
||||
"nix",
|
||||
"rustix",
|
||||
"tempfile",
|
||||
"thiserror 2.0.18",
|
||||
"uucore",
|
||||
@@ -3543,7 +3542,7 @@ version = "0.8.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"fluent",
|
||||
"nix",
|
||||
"libc",
|
||||
"rust-ini",
|
||||
"thiserror 2.0.18",
|
||||
"uucore",
|
||||
@@ -3708,7 +3707,7 @@ version = "0.8.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"fluent",
|
||||
"nix",
|
||||
"libc",
|
||||
"uucore",
|
||||
]
|
||||
|
||||
@@ -3787,7 +3786,7 @@ version = "0.8.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"fluent",
|
||||
"nix",
|
||||
"libc",
|
||||
"uucore",
|
||||
]
|
||||
|
||||
@@ -3797,7 +3796,7 @@ version = "0.8.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"fluent",
|
||||
"nix",
|
||||
"libc",
|
||||
"uucore",
|
||||
]
|
||||
|
||||
@@ -4156,9 +4155,9 @@ dependencies = [
|
||||
"foldhash 0.2.0",
|
||||
"itertools 0.14.0",
|
||||
"memchr",
|
||||
"nix",
|
||||
"rand 0.10.1",
|
||||
"rayon",
|
||||
"rustix",
|
||||
"self_cell",
|
||||
"tempfile",
|
||||
"thiserror 2.0.18",
|
||||
@@ -4215,7 +4214,8 @@ dependencies = [
|
||||
"cfg_aliases",
|
||||
"clap",
|
||||
"fluent",
|
||||
"nix",
|
||||
"libc",
|
||||
"rustix",
|
||||
"uucore",
|
||||
]
|
||||
|
||||
@@ -4234,7 +4234,8 @@ version = "0.8.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"fluent",
|
||||
"nix",
|
||||
"libc",
|
||||
"rustix",
|
||||
"uucore",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
@@ -4299,7 +4300,6 @@ dependencies = [
|
||||
"codspeed-divan-compat",
|
||||
"fluent",
|
||||
"libc",
|
||||
"nix",
|
||||
"uucore",
|
||||
]
|
||||
|
||||
|
||||
@@ -41,7 +41,6 @@ fluent = { workspace = true }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
exacl = { workspace = true, optional = true }
|
||||
nix = { workspace = true, features = ["fs"] }
|
||||
|
||||
[[bin]]
|
||||
name = "cp"
|
||||
|
||||
+24
-7
@@ -23,7 +23,7 @@ use clap::{Arg, ArgAction, ArgMatches, Command, builder::ValueParser, value_pars
|
||||
use filetime::FileTime;
|
||||
use indicatif::{ProgressBar, ProgressStyle};
|
||||
#[cfg(unix)]
|
||||
use nix::sys::stat::{Mode, SFlag, dev_t, mknod as nix_mknod, mode_t};
|
||||
use libc::{dev_t, mode_t};
|
||||
use thiserror::Error;
|
||||
|
||||
use platform::copy_on_write;
|
||||
@@ -2807,14 +2807,31 @@ fn copy_node(
|
||||
overwrite.verify(dest, debug)?;
|
||||
fs::remove_file(dest)?;
|
||||
}
|
||||
let sflag = if source_metadata.file_type().is_char_device() {
|
||||
SFlag::S_IFCHR
|
||||
let sflag: mode_t = if source_metadata.file_type().is_char_device() {
|
||||
libc::S_IFCHR
|
||||
} else {
|
||||
SFlag::S_IFBLK
|
||||
libc::S_IFBLK
|
||||
};
|
||||
let mode = Mode::from_bits_truncate(source_metadata.mode() as mode_t);
|
||||
nix_mknod(dest, sflag, mode, source_metadata.rdev() as dev_t)
|
||||
.map_err(|e| translate!("cp-error-cannot-create-special-file", "path" => dest.quote(), "error" => e.desc()).into())
|
||||
let mode = (source_metadata.mode() as mode_t) & 0o7777;
|
||||
use std::io;
|
||||
let c_path = std::ffi::CString::new(dest.as_os_str().as_encoded_bytes())
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "path contains null byte"))?;
|
||||
// SAFETY: c_path is a valid null-terminated C string
|
||||
let ret = unsafe {
|
||||
libc::mknod(
|
||||
c_path.as_ptr(),
|
||||
sflag | mode,
|
||||
source_metadata.rdev() as dev_t,
|
||||
)
|
||||
};
|
||||
if ret != 0 {
|
||||
let e = io::Error::last_os_error();
|
||||
return Err(
|
||||
translate!("cp-error-cannot-create-special-file", "path" => dest.quote(), "error" => uucore::error::strip_errno(&e))
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn copy_link(
|
||||
|
||||
@@ -33,7 +33,7 @@ thiserror = { workspace = true }
|
||||
fluent = { workspace = true }
|
||||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies]
|
||||
nix = { workspace = true, features = ["fs", "signal"] }
|
||||
rustix = { workspace = true, features = ["fs"] }
|
||||
|
||||
[[bin]]
|
||||
name = "dd"
|
||||
|
||||
+31
-30
@@ -16,15 +16,13 @@ mod progress;
|
||||
use crate::bufferedoutput::BufferedOutput;
|
||||
use blocks::conv_block_unblock_helper;
|
||||
use datastructures::{ConversionMode, IConvFlags, IFlags, OConvFlags, OFlags, options};
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
use nix::fcntl::FcntlArg;
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
use nix::fcntl::OFlag;
|
||||
use parseargs::Parser;
|
||||
use progress::ProgUpdateType;
|
||||
use progress::{ProgUpdate, ReadStat, StatusLevel, WriteStat, gen_prog_updater};
|
||||
#[cfg(target_os = "linux")]
|
||||
use progress::{check_and_reset_sigusr1, install_sigusr1_handler};
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
use rustix::fs::OFlags as RustixOFlags;
|
||||
use uucore::io::OwnedFileDescriptorOrHandle;
|
||||
use uucore::translate;
|
||||
|
||||
@@ -55,10 +53,7 @@ use std::time::{Duration, Instant};
|
||||
use clap::{Arg, Command};
|
||||
use gcd::Gcd;
|
||||
#[cfg(target_os = "linux")]
|
||||
use nix::{
|
||||
errno::Errno,
|
||||
fcntl::{PosixFadviseAdvice, posix_fadvise},
|
||||
};
|
||||
use rustix::fs::{Advice as PosixFadviseAdvice, fadvise};
|
||||
use uucore::display::Quotable;
|
||||
use uucore::error::{FromIo, UResult};
|
||||
#[cfg(unix)]
|
||||
@@ -316,14 +311,16 @@ impl Source {
|
||||
/// portion of the source is no longer needed. If not possible,
|
||||
/// then this function returns an error.
|
||||
#[cfg(target_os = "linux")]
|
||||
fn discard_cache(&self, offset: libc::off_t, len: libc::off_t) -> nix::Result<()> {
|
||||
fn discard_cache(&self, offset: libc::off_t, len: libc::off_t) -> io::Result<()> {
|
||||
#[allow(clippy::match_wildcard_for_single_variants)]
|
||||
match self {
|
||||
Self::File(f) => {
|
||||
let advice = PosixFadviseAdvice::POSIX_FADV_DONTNEED;
|
||||
posix_fadvise(f.as_fd(), offset, len, advice)
|
||||
let advice = PosixFadviseAdvice::DontNeed;
|
||||
let len = std::num::NonZeroU64::new(len as u64);
|
||||
fadvise(f.as_fd(), offset as u64, len, advice)?;
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(Errno::ESPIPE), // "Illegal seek"
|
||||
_ => Err(io::Error::from_raw_os_error(libc::ESPIPE)), // "Illegal seek"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -700,13 +697,15 @@ impl Dest {
|
||||
/// specified portion of the destination is no longer needed. If
|
||||
/// not possible, then this function returns an error.
|
||||
#[cfg(target_os = "linux")]
|
||||
fn discard_cache(&self, offset: libc::off_t, len: libc::off_t) -> nix::Result<()> {
|
||||
fn discard_cache(&self, offset: libc::off_t, len: libc::off_t) -> io::Result<()> {
|
||||
match self {
|
||||
Self::File(f, _) => {
|
||||
let advice = PosixFadviseAdvice::POSIX_FADV_DONTNEED;
|
||||
posix_fadvise(f.as_fd(), offset, len, advice)
|
||||
let advice = PosixFadviseAdvice::DontNeed;
|
||||
let len = std::num::NonZeroU64::new(len as u64);
|
||||
fadvise(f.as_fd(), offset as u64, len, advice)?;
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(Errno::ESPIPE), // "Illegal seek"
|
||||
_ => Err(io::Error::from_raw_os_error(libc::ESPIPE)), // "Illegal seek"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -720,31 +719,33 @@ fn is_sparse(buf: &[u8]) -> bool {
|
||||
/// This follows GNU dd behavior for partial block writes with O_DIRECT.
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn handle_o_direct_write(f: &mut File, buf: &[u8], original_error: io::Error) -> io::Result<usize> {
|
||||
use nix::fcntl::{FcntlArg, OFlag, fcntl};
|
||||
use rustix::fs::{OFlags, fcntl_getfl, fcntl_setfl};
|
||||
|
||||
// Get current flags using nix
|
||||
let oflags = match fcntl(&mut *f, FcntlArg::F_GETFL) {
|
||||
Ok(flags) => OFlag::from_bits_retain(flags),
|
||||
Err(_) => return Err(original_error),
|
||||
// Get current flags
|
||||
let Ok(oflags) = fcntl_getfl(&*f) else {
|
||||
return Err(original_error);
|
||||
};
|
||||
|
||||
// If O_DIRECT is set, try removing it temporarily
|
||||
if oflags.contains(OFlag::O_DIRECT) {
|
||||
let flags_without_direct = oflags - OFlag::O_DIRECT;
|
||||
if oflags.contains(OFlags::DIRECT) {
|
||||
let flags_without_direct = oflags & !OFlags::DIRECT;
|
||||
|
||||
// Remove O_DIRECT flag using nix
|
||||
if fcntl(&mut *f, FcntlArg::F_SETFL(flags_without_direct)).is_err() {
|
||||
// Remove O_DIRECT flag
|
||||
if fcntl_setfl(&*f, flags_without_direct).is_err() {
|
||||
return Err(original_error);
|
||||
}
|
||||
|
||||
// Retry the write without O_DIRECT
|
||||
let write_result = f.write(buf);
|
||||
|
||||
// Restore O_DIRECT flag using nix (GNU doesn't restore it, but we'll be safer)
|
||||
// Restore O_DIRECT flag (GNU doesn't restore it, but we'll be safer)
|
||||
// Log any restoration errors without failing the operation
|
||||
if let Err(os_err) = fcntl(&mut *f, FcntlArg::F_SETFL(oflags)) {
|
||||
if let Err(os_err) = fcntl_setfl(&*f, oflags) {
|
||||
// Just log the error, don't fail the whole operation
|
||||
show_error!("Failed to restore O_DIRECT flag: {os_err}");
|
||||
show_error!(
|
||||
"Failed to restore O_DIRECT flag: {}",
|
||||
io::Error::from(os_err)
|
||||
);
|
||||
}
|
||||
|
||||
write_result
|
||||
@@ -891,9 +892,9 @@ impl<'a> Output<'a> {
|
||||
let fx = OwnedFileDescriptorOrHandle::from(io::stdout())?;
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
if let Some(libc_flags) = make_linux_oflags(&settings.oflags) {
|
||||
nix::fcntl::fcntl(
|
||||
rustix::fs::fcntl_setfl(
|
||||
fx.as_raw().as_fd(),
|
||||
FcntlArg::F_SETFL(OFlag::from_bits_retain(libc_flags)),
|
||||
RustixOFlags::from_bits_retain(libc_flags as _),
|
||||
)?;
|
||||
}
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -26,7 +26,7 @@ uucore = { workspace = true, features = ["signals"] }
|
||||
fluent = { workspace = true }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
nix = { workspace = true, features = ["signal"] }
|
||||
libc = { workspace = true }
|
||||
|
||||
[[bin]]
|
||||
name = "env"
|
||||
|
||||
Vendored
+44
-38
@@ -17,15 +17,6 @@ use ini::Ini;
|
||||
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,
|
||||
};
|
||||
#[cfg(unix)]
|
||||
use nix::unistd::execvp;
|
||||
use std::borrow::Cow;
|
||||
#[cfg(unix)]
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
@@ -38,6 +29,8 @@ use std::io::Write as _;
|
||||
use std::io::stderr;
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
#[cfg(unix)]
|
||||
use uucore::signals::csignal::{self, SigHandler};
|
||||
|
||||
use uucore::display::{Quotable, print_all_env_vars};
|
||||
use uucore::error::{ExitCode, UError, UResult, USimpleError, UUsageError};
|
||||
@@ -295,16 +288,25 @@ fn build_signal_request(
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn signal_from_value(sig_value: usize) -> UResult<Signal> {
|
||||
Signal::try_from(sig_value as i32).map_err(|_| {
|
||||
USimpleError::new(
|
||||
fn signal_from_value(sig_value: usize) -> UResult<libc::c_int> {
|
||||
let sig = sig_value as libc::c_int;
|
||||
// Validate signal number is in a reasonable range.
|
||||
// On Linux, reject glibc-reserved signals between standard signals and SIGRTMIN
|
||||
// (typically 32 and 33, used internally by NPTL).
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
let reserved = sig > libc::SIGSYS && sig < libc::SIGRTMIN();
|
||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||
let reserved = false;
|
||||
if sig <= 0 || sig >= 128 || reserved {
|
||||
return Err(USimpleError::new(
|
||||
125,
|
||||
translate!(
|
||||
"env-error-invalid-signal",
|
||||
"signal" => sig_value.to_string().quote()
|
||||
),
|
||||
)
|
||||
})
|
||||
));
|
||||
}
|
||||
Ok(sig)
|
||||
}
|
||||
|
||||
fn load_config_file(opts: &mut Options) -> UResult<()> {
|
||||
@@ -836,11 +838,21 @@ impl EnvAppData {
|
||||
}
|
||||
|
||||
// Execute the program using execvp. this replaces the current
|
||||
// process. The execvp function takes care of appending a NULL
|
||||
// argument to the argument list so that we don't have to.
|
||||
match execvp(&prog_cstring, &argv) {
|
||||
Err(nix::errno::Errno::ENOENT) => Err(self.make_error_no_such_file_or_dir(&prog)),
|
||||
Err(nix::errno::Errno::EACCES) => {
|
||||
// process.
|
||||
//
|
||||
// Build a null-terminated array of pointers for execvp.
|
||||
let mut argv_ptrs: Vec<*const libc::c_char> = argv.iter().map(|a| a.as_ptr()).collect();
|
||||
argv_ptrs.push(std::ptr::null());
|
||||
|
||||
// SAFETY: prog_cstring is a valid null-terminated C string,
|
||||
// argv_ptrs is a null-terminated array of valid C string pointers.
|
||||
// execvp only returns on error.
|
||||
unsafe { libc::execvp(prog_cstring.as_ptr(), argv_ptrs.as_ptr()) };
|
||||
|
||||
let err = io::Error::last_os_error();
|
||||
match err.raw_os_error() {
|
||||
Some(libc::ENOENT) => Err(self.make_error_no_such_file_or_dir(&prog)),
|
||||
Some(libc::EACCES) => {
|
||||
uucore::show_error!(
|
||||
"{}",
|
||||
translate!(
|
||||
@@ -850,7 +862,7 @@ impl EnvAppData {
|
||||
);
|
||||
Err(126.into())
|
||||
}
|
||||
Err(_) => {
|
||||
_ => {
|
||||
uucore::show_error!(
|
||||
"{}",
|
||||
translate!(
|
||||
@@ -860,9 +872,6 @@ impl EnvAppData {
|
||||
);
|
||||
Err(126.into())
|
||||
}
|
||||
Ok(_) => {
|
||||
unreachable!("execvp should never return on success")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1072,7 +1081,7 @@ fn apply_signal_action<F>(
|
||||
signal_fn: F,
|
||||
) -> UResult<()>
|
||||
where
|
||||
F: Fn(Signal) -> UResult<()>,
|
||||
F: Fn(libc::c_int) -> UResult<()>,
|
||||
{
|
||||
request.for_each_signal(|sig_value, explicit| {
|
||||
// On some platforms ALL_SIGNALS may contain values that are not valid in libc.
|
||||
@@ -1096,41 +1105,38 @@ where
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn ignore_signal(sig: Signal) -> UResult<()> {
|
||||
// SAFETY: This is safe because we write the handler for each signal only once, and therefore "the current handler is the default", as the documentation requires it.
|
||||
let result = unsafe { signal(sig, SigIgn) };
|
||||
if let Err(err) = result {
|
||||
fn ignore_signal(sig: libc::c_int) -> UResult<()> {
|
||||
if let Err(err) = unsafe { csignal::set_signal_handler(sig, SigHandler::SigIgn) } {
|
||||
return Err(USimpleError::new(
|
||||
125,
|
||||
translate!("env-error-failed-set-signal-action", "signal" => (sig as i32), "error" => err.desc()),
|
||||
translate!("env-error-failed-set-signal-action", "signal" => sig, "error" => err),
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn reset_signal(sig: Signal) -> UResult<()> {
|
||||
let result = unsafe { signal(sig, SigDfl) };
|
||||
if let Err(err) = result {
|
||||
fn reset_signal(sig: libc::c_int) -> UResult<()> {
|
||||
if let Err(err) = unsafe { csignal::set_signal_handler(sig, SigHandler::SigDfl) } {
|
||||
return Err(USimpleError::new(
|
||||
125,
|
||||
translate!("env-error-failed-set-signal-action", "signal" => (sig as i32), "error" => err.desc()),
|
||||
translate!("env-error-failed-set-signal-action", "signal" => sig, "error" => err),
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn block_signal(sig: Signal) -> UResult<()> {
|
||||
let mut set = SigSet::empty();
|
||||
fn block_signal(sig: libc::c_int) -> UResult<()> {
|
||||
let mut set = csignal::SigSet::empty();
|
||||
set.add(sig);
|
||||
if let Err(err) = sigprocmask(SigmaskHow::SIG_BLOCK, Some(&set), None) {
|
||||
if let Err(err) = csignal::sigprocmask(csignal::SigmaskHow::Block, Some(&set), None) {
|
||||
return Err(USimpleError::new(
|
||||
125,
|
||||
translate!(
|
||||
"env-error-failed-set-signal-action",
|
||||
"signal" => (sig as i32),
|
||||
"error" => err.desc()
|
||||
"signal" => sig,
|
||||
"error" => err
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ uucore = { workspace = true, features = ["signals"] }
|
||||
fluent = { workspace = true }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
nix = { workspace = true, features = ["signal"] }
|
||||
libc = { workspace = true }
|
||||
|
||||
[[bin]]
|
||||
name = "kill"
|
||||
|
||||
+10
-14
@@ -6,8 +6,6 @@
|
||||
// spell-checker:ignore (ToDO) signalname pids killpg
|
||||
|
||||
use clap::{Arg, ArgAction, Command};
|
||||
use nix::sys::signal::{self, Signal};
|
||||
use nix::unistd::Pid;
|
||||
use std::io::Error;
|
||||
use uucore::display::Quotable;
|
||||
use uucore::error::{FromIo, UResult, USimpleError};
|
||||
@@ -69,22 +67,18 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
};
|
||||
|
||||
let sig_name = signal_name_by_value(sig);
|
||||
// Signal does not support converting from EXIT
|
||||
// Instead, nix::signal::kill expects Option::None to properly handle EXIT
|
||||
let sig: Option<Signal> = if sig_name.is_some_and(|name| name == "EXIT") {
|
||||
None
|
||||
// Signal 0 (EXIT) means "check if process exists" - pass 0 to kill()
|
||||
let sig_num: i32 = if sig_name.is_some_and(|name| name == "EXIT") {
|
||||
0
|
||||
} else {
|
||||
let sig = (sig as i32)
|
||||
.try_into()
|
||||
.map_err(|e| Error::from_raw_os_error(e as i32))?;
|
||||
Some(sig)
|
||||
sig as i32
|
||||
};
|
||||
|
||||
let pids = parse_pids(&pids_or_signals)?;
|
||||
if pids.is_empty() {
|
||||
Err(USimpleError::new(1, translate!("kill-error-no-process-id")))
|
||||
} else {
|
||||
kill(sig, &pids);
|
||||
kill(sig_num, &pids);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -250,11 +244,13 @@ fn parse_pids(pids: &[String]) -> UResult<Vec<i32>> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn kill(sig: Option<Signal>, pids: &[i32]) {
|
||||
fn kill(sig: i32, pids: &[i32]) {
|
||||
for &pid in pids {
|
||||
if let Err(e) = signal::kill(Pid::from_raw(pid), sig) {
|
||||
// SAFETY: kill() is a standard POSIX function. sig=0 checks process existence.
|
||||
let ret = unsafe { libc::kill(pid, sig) };
|
||||
if ret != 0 {
|
||||
show!(
|
||||
Error::from_raw_os_error(e as i32)
|
||||
Error::last_os_error()
|
||||
.map_err_context(|| { translate!("kill-error-sending-signal", "pid" => pid) })
|
||||
);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ uucore = { workspace = true, features = ["fs", "mode"] }
|
||||
fluent = { workspace = true }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
nix = { workspace = true, features = ["fs"] }
|
||||
libc = { workspace = true }
|
||||
|
||||
[features]
|
||||
selinux = ["uucore/selinux"]
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use clap::{Arg, ArgAction, Command, value_parser};
|
||||
use nix::sys::stat::Mode;
|
||||
use nix::unistd::mkfifo;
|
||||
use std::ffi::CString;
|
||||
use std::fs;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use uucore::display::Quotable;
|
||||
@@ -48,7 +47,9 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
};
|
||||
|
||||
for f in fifos {
|
||||
if mkfifo(f.as_str(), Mode::from_bits_truncate(0o666)).is_err() {
|
||||
let c_path = CString::new(f.as_str()).unwrap();
|
||||
// SAFETY: c_path is a valid null-terminated C string
|
||||
if unsafe { libc::mkfifo(c_path.as_ptr(), 0o666) } != 0 {
|
||||
show!(USimpleError::new(
|
||||
1,
|
||||
translate!("mkfifo-error-cannot-create-fifo", "path" => f.quote()),
|
||||
|
||||
@@ -24,7 +24,7 @@ doctest = false
|
||||
clap = { workspace = true }
|
||||
uucore = { workspace = true, features = ["mode", "fs"] }
|
||||
fluent = { workspace = true }
|
||||
nix = { workspace = true }
|
||||
libc = { workspace = true }
|
||||
|
||||
[features]
|
||||
selinux = ["uucore/selinux"]
|
||||
|
||||
+19
-20
@@ -6,8 +6,8 @@
|
||||
// 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 nix::sys::stat::{Mode, SFlag, mknod as nix_mknod, umask as nix_umask};
|
||||
use libc::{S_IRGRP, S_IROTH, S_IRUSR, S_IWGRP, S_IWOTH, S_IWUSR, mode_t};
|
||||
use std::ffi::CString;
|
||||
|
||||
use uucore::display::Quotable;
|
||||
use uucore::error::{UResult, USimpleError, UUsageError, set_exit_code};
|
||||
@@ -35,11 +35,11 @@ enum FileType {
|
||||
}
|
||||
|
||||
impl FileType {
|
||||
fn as_sflag(&self) -> SFlag {
|
||||
fn as_mode(&self) -> mode_t {
|
||||
match self {
|
||||
Self::Block => SFlag::S_IFBLK,
|
||||
Self::Character => SFlag::S_IFCHR,
|
||||
Self::Fifo => SFlag::S_IFIFO,
|
||||
Self::Block => libc::S_IFBLK,
|
||||
Self::Character => libc::S_IFCHR,
|
||||
Self::Fifo => libc::S_IFIFO,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@ impl FileType {
|
||||
/// Configuration for special inode creation.
|
||||
struct Config {
|
||||
/// Permission bits for the inode
|
||||
mode: Mode,
|
||||
mode: mode_t,
|
||||
|
||||
file_type: FileType,
|
||||
|
||||
@@ -70,28 +70,27 @@ fn mknod(file_name: &str, config: Config) -> i32 {
|
||||
let have_prev_umask = if config.use_umask {
|
||||
None
|
||||
} else {
|
||||
Some(nix_umask(Mode::empty()))
|
||||
// SAFETY: umask is a standard POSIX function, always safe to call
|
||||
Some(unsafe { libc::umask(0) })
|
||||
};
|
||||
|
||||
let mknod_err = nix_mknod(
|
||||
file_name,
|
||||
config.file_type.as_sflag(),
|
||||
config.mode,
|
||||
config.dev as _,
|
||||
)
|
||||
.err();
|
||||
let errno = if mknod_err.is_some() { -1 } else { 0 };
|
||||
let c_path = CString::new(file_name).unwrap();
|
||||
let combined_mode = config.file_type.as_mode() | config.mode;
|
||||
// SAFETY: c_path is a valid null-terminated C string
|
||||
let ret = unsafe { libc::mknod(c_path.as_ptr(), combined_mode, config.dev as _) };
|
||||
let errno = if ret != 0 { -1 } else { 0 };
|
||||
|
||||
// set umask back to original value
|
||||
if let Some(prev_umask) = have_prev_umask {
|
||||
nix_umask(prev_umask);
|
||||
// SAFETY: umask is always safe to call
|
||||
unsafe { libc::umask(prev_umask) };
|
||||
}
|
||||
|
||||
if let Some(err) = mknod_err {
|
||||
if ret != 0 {
|
||||
eprintln!(
|
||||
"{}: {}",
|
||||
uucore::execution_phrase(),
|
||||
std::io::Error::from(err)
|
||||
std::io::Error::last_os_error()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -141,7 +140,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
parse_mode(str_mode).map_err(|e| USimpleError::new(1, e))?
|
||||
}
|
||||
};
|
||||
let mode = Mode::from_bits_truncate(mode_permissions as mode_t);
|
||||
let mode = (mode_permissions as mode_t) & 0o7777;
|
||||
|
||||
let file_name = matches
|
||||
.get_one::<String>("name")
|
||||
|
||||
@@ -51,7 +51,8 @@ foldhash = { workspace = true }
|
||||
ctrlc = { workspace = true }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
nix = { workspace = true, features = ["resource"] }
|
||||
libc = { workspace = true }
|
||||
rustix = { workspace = true, features = ["process", "param"] }
|
||||
|
||||
[dev-dependencies]
|
||||
divan = { workspace = true }
|
||||
|
||||
@@ -110,17 +110,14 @@ fn physical_memory_bytes() -> Option<u128> {
|
||||
any(target_os = "linux", target_os = "android")
|
||||
))]
|
||||
fn physical_memory_bytes_unix() -> Option<u128> {
|
||||
use nix::unistd::{SysconfVar, sysconf};
|
||||
let page_size = u128::try_from(rustix::param::page_size()).ok()?;
|
||||
|
||||
let pages = match sysconf(SysconfVar::_PHYS_PAGES) {
|
||||
Ok(Some(pages)) if pages > 0 => u128::try_from(pages).ok()?,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
let page_size = match sysconf(SysconfVar::PAGE_SIZE) {
|
||||
Ok(Some(page_size)) if page_size > 0 => u128::try_from(page_size).ok()?,
|
||||
_ => return None,
|
||||
};
|
||||
// Use libc::sysconf for _SC_PHYS_PAGES since rustix doesn't expose it
|
||||
let pages = unsafe { libc::sysconf(libc::_SC_PHYS_PAGES) };
|
||||
if pages <= 0 {
|
||||
return None;
|
||||
}
|
||||
let pages = u128::try_from(pages).ok()?;
|
||||
|
||||
Some(pages.saturating_mul(page_size))
|
||||
}
|
||||
|
||||
@@ -1371,14 +1371,15 @@ fn make_sort_mode_arg(mode: &'static str, short: char, help: String) -> Arg {
|
||||
))
|
||||
))]
|
||||
fn get_rlimit() -> UResult<usize> {
|
||||
use nix::sys::resource::{RLIM_INFINITY, Resource, getrlimit};
|
||||
use rustix::process::{Resource, getrlimit};
|
||||
|
||||
let (rlim_cur, _rlim_max) = getrlimit(Resource::RLIMIT_NOFILE)
|
||||
.map_err(|_| UUsageError::new(2, translate!("sort-failed-fetch-rlimit")))?;
|
||||
if rlim_cur == RLIM_INFINITY {
|
||||
let rlimit = getrlimit(Resource::Nofile);
|
||||
let rlim_cur = rlimit.current;
|
||||
if rlim_cur.is_none() {
|
||||
// None means RLIM_INFINITY
|
||||
return Err(UUsageError::new(2, translate!("sort-failed-fetch-rlimit")));
|
||||
}
|
||||
usize::try_from(rlim_cur)
|
||||
usize::try_from(rlim_cur.unwrap())
|
||||
.map_err(|_| UUsageError::new(2, translate!("sort-failed-fetch-rlimit")))
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,8 @@ uucore = { workspace = true, features = ["parser"] }
|
||||
fluent = { workspace = true }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
nix = { workspace = true, features = ["ioctl", "term"] }
|
||||
libc = { workspace = true }
|
||||
rustix = { workspace = true, features = ["termios"] }
|
||||
|
||||
[[bin]]
|
||||
name = "stty"
|
||||
|
||||
+85
-127
@@ -13,11 +13,8 @@
|
||||
|
||||
use crate::Flag;
|
||||
|
||||
#[cfg(not(bsd))]
|
||||
use nix::sys::termios::BaudRate;
|
||||
use nix::sys::termios::{
|
||||
ControlFlags as C, InputFlags as I, LocalFlags as L, OutputFlags as O,
|
||||
SpecialCharacterIndices as S,
|
||||
use rustix::termios::{
|
||||
ControlModes as C, InputModes as I, LocalModes as L, OutputModes as O, SpecialCodeIndex as S,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -31,10 +28,7 @@ pub enum BaudType {
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub enum AllFlags<'a> {
|
||||
#[cfg(bsd)]
|
||||
Baud(u32, BaudType),
|
||||
#[cfg(not(bsd))]
|
||||
Baud(BaudRate, BaudType),
|
||||
ControlFlags((&'a Flag<C>, bool)),
|
||||
InputFlags((&'a Flag<I>, bool)),
|
||||
LocalFlags((&'a Flag<L>, bool)),
|
||||
@@ -94,123 +88,88 @@ pub const OUTPUT_FLAGS: &[Flag<O>] = &[
|
||||
Flag::new("onlcr", O::ONLCR).sane(),
|
||||
Flag::new("onocr", O::ONOCR),
|
||||
Flag::new("onlret", O::ONLRET),
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "haiku",
|
||||
target_os = "linux",
|
||||
target_vendor = "apple"
|
||||
))]
|
||||
// Output delay flags: only available on Linux (glibc) and Android.
|
||||
// Not available on BSDs (macOS/iOS), musl, or other platforms in rustix.
|
||||
#[cfg(any(target_os = "android", target_os = "haiku", target_os = "linux"))]
|
||||
Flag::new("ofdel", O::OFDEL),
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "haiku",
|
||||
target_os = "linux",
|
||||
target_vendor = "apple"
|
||||
#[cfg(all(
|
||||
any(target_os = "android", target_os = "haiku", target_os = "linux"),
|
||||
not(target_env = "musl")
|
||||
))]
|
||||
Flag::new_grouped("nl0", O::NL0, O::NLDLY).sane(),
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "haiku",
|
||||
target_os = "linux",
|
||||
target_vendor = "apple"
|
||||
#[cfg(all(
|
||||
any(target_os = "android", target_os = "haiku", target_os = "linux"),
|
||||
not(target_env = "musl")
|
||||
))]
|
||||
Flag::new_grouped("nl1", O::NL1, O::NLDLY),
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "haiku",
|
||||
target_os = "linux",
|
||||
target_vendor = "apple"
|
||||
#[cfg(all(
|
||||
any(target_os = "android", target_os = "haiku", target_os = "linux"),
|
||||
not(target_env = "musl")
|
||||
))]
|
||||
Flag::new_grouped("cr0", O::CR0, O::CRDLY).sane(),
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "haiku",
|
||||
target_os = "linux",
|
||||
target_vendor = "apple"
|
||||
#[cfg(all(
|
||||
any(target_os = "android", target_os = "haiku", target_os = "linux"),
|
||||
not(target_env = "musl")
|
||||
))]
|
||||
Flag::new_grouped("cr1", O::CR1, O::CRDLY),
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "haiku",
|
||||
target_os = "linux",
|
||||
target_vendor = "apple"
|
||||
#[cfg(all(
|
||||
any(target_os = "android", target_os = "haiku", target_os = "linux"),
|
||||
not(target_env = "musl")
|
||||
))]
|
||||
Flag::new_grouped("cr2", O::CR2, O::CRDLY),
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "haiku",
|
||||
target_os = "linux",
|
||||
target_vendor = "apple"
|
||||
#[cfg(all(
|
||||
any(target_os = "android", target_os = "haiku", target_os = "linux"),
|
||||
not(target_env = "musl")
|
||||
))]
|
||||
Flag::new_grouped("cr3", O::CR3, O::CRDLY),
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "haiku",
|
||||
target_os = "linux",
|
||||
target_vendor = "apple"
|
||||
#[cfg(all(
|
||||
any(target_os = "android", target_os = "haiku", target_os = "linux"),
|
||||
not(target_env = "musl")
|
||||
))]
|
||||
Flag::new_grouped("tab0", O::TAB0, O::TABDLY).sane(),
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "haiku",
|
||||
target_os = "linux",
|
||||
target_vendor = "apple"
|
||||
#[cfg(all(
|
||||
any(target_os = "android", target_os = "haiku", target_os = "linux"),
|
||||
not(target_env = "musl")
|
||||
))]
|
||||
Flag::new_grouped("tab1", O::TAB1, O::TABDLY),
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "haiku",
|
||||
target_os = "linux",
|
||||
target_vendor = "apple"
|
||||
#[cfg(all(
|
||||
any(target_os = "android", target_os = "haiku", target_os = "linux"),
|
||||
not(target_env = "musl")
|
||||
))]
|
||||
Flag::new_grouped("tab2", O::TAB2, O::TABDLY),
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "haiku",
|
||||
target_os = "linux",
|
||||
target_vendor = "apple"
|
||||
#[cfg(all(
|
||||
any(target_os = "android", target_os = "haiku", target_os = "linux"),
|
||||
not(target_env = "musl")
|
||||
))]
|
||||
Flag::new_grouped("tab3", O::TAB3, O::TABDLY),
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "haiku",
|
||||
target_os = "linux",
|
||||
target_vendor = "apple"
|
||||
#[cfg(all(
|
||||
any(target_os = "android", target_os = "haiku", target_os = "linux"),
|
||||
not(target_env = "musl")
|
||||
))]
|
||||
Flag::new_grouped("bs0", O::BS0, O::BSDLY).sane(),
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "haiku",
|
||||
target_os = "linux",
|
||||
target_vendor = "apple"
|
||||
#[cfg(all(
|
||||
any(target_os = "android", target_os = "haiku", target_os = "linux"),
|
||||
not(target_env = "musl")
|
||||
))]
|
||||
Flag::new_grouped("bs1", O::BS1, O::BSDLY),
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "haiku",
|
||||
target_os = "linux",
|
||||
target_vendor = "apple"
|
||||
#[cfg(all(
|
||||
any(target_os = "android", target_os = "haiku", target_os = "linux"),
|
||||
not(target_env = "musl")
|
||||
))]
|
||||
Flag::new_grouped("vt0", O::VT0, O::VTDLY).sane(),
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "haiku",
|
||||
target_os = "linux",
|
||||
target_vendor = "apple"
|
||||
#[cfg(all(
|
||||
any(target_os = "android", target_os = "haiku", target_os = "linux"),
|
||||
not(target_env = "musl")
|
||||
))]
|
||||
Flag::new_grouped("vt1", O::VT1, O::VTDLY),
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "haiku",
|
||||
target_os = "linux",
|
||||
target_vendor = "apple"
|
||||
#[cfg(all(
|
||||
any(target_os = "android", target_os = "haiku", target_os = "linux"),
|
||||
not(target_env = "musl")
|
||||
))]
|
||||
Flag::new_grouped("ff0", O::FF0, O::FFDLY).sane(),
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "haiku",
|
||||
target_os = "linux",
|
||||
target_vendor = "apple"
|
||||
#[cfg(all(
|
||||
any(target_os = "android", target_os = "haiku", target_os = "linux"),
|
||||
not(target_env = "musl")
|
||||
))]
|
||||
Flag::new_grouped("ff1", O::FF1, O::FFDLY),
|
||||
];
|
||||
@@ -241,63 +200,62 @@ pub const LOCAL_FLAGS: &[Flag<L>] = &[
|
||||
Flag::new("extproc", L::EXTPROC),
|
||||
];
|
||||
|
||||
// BSD's use u32 as baud rate, so using the enum is unnecessary.
|
||||
// Baud rate table mapping string names to speed values.
|
||||
// rustix uses actual integer speed values, not encoded B* constants.
|
||||
#[cfg(not(bsd))]
|
||||
pub const BAUD_RATES: &[(&str, BaudRate)] = &[
|
||||
("0", BaudRate::B0),
|
||||
("50", BaudRate::B50),
|
||||
("75", BaudRate::B75),
|
||||
("110", BaudRate::B110),
|
||||
("134", BaudRate::B134),
|
||||
("150", BaudRate::B150),
|
||||
("200", BaudRate::B200),
|
||||
("300", BaudRate::B300),
|
||||
("600", BaudRate::B600),
|
||||
("1200", BaudRate::B1200),
|
||||
("1800", BaudRate::B1800),
|
||||
("2400", BaudRate::B2400),
|
||||
("4800", BaudRate::B4800),
|
||||
("9600", BaudRate::B9600),
|
||||
("19200", BaudRate::B19200),
|
||||
("38400", BaudRate::B38400),
|
||||
("57600", BaudRate::B57600),
|
||||
("115200", BaudRate::B115200),
|
||||
("230400", BaudRate::B230400),
|
||||
("460800", BaudRate::B460800),
|
||||
pub const BAUD_RATES: &[(&str, u32)] = &[
|
||||
("0", 0),
|
||||
("50", 50),
|
||||
("75", 75),
|
||||
("110", 110),
|
||||
("134", 134),
|
||||
("150", 150),
|
||||
("200", 200),
|
||||
("300", 300),
|
||||
("600", 600),
|
||||
("1200", 1200),
|
||||
("1800", 1800),
|
||||
("2400", 2400),
|
||||
("9600", 9600),
|
||||
("19200", 19200),
|
||||
("38400", 38400),
|
||||
("57600", 57600),
|
||||
("115200", 115_200),
|
||||
("230400", 230_400),
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
("500000", BaudRate::B500000),
|
||||
("500000", 500_000),
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
("576000", BaudRate::B576000),
|
||||
("576000", 576_000),
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
("921600", BaudRate::B921600),
|
||||
("921600", 921_600),
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
("1000000", BaudRate::B1000000),
|
||||
("1000000", 1_000_000),
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
("1152000", BaudRate::B1152000),
|
||||
("1152000", 1_152_000),
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
("1500000", BaudRate::B1500000),
|
||||
("1500000", 1_500_000),
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
("2000000", BaudRate::B2000000),
|
||||
("2000000", 2_000_000),
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
all(target_os = "linux", not(target_arch = "sparc64"))
|
||||
))]
|
||||
("2500000", BaudRate::B2500000),
|
||||
("2500000", 2_500_000),
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
all(target_os = "linux", not(target_arch = "sparc64"))
|
||||
))]
|
||||
("3000000", BaudRate::B3000000),
|
||||
("3000000", 3_000_000),
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
all(target_os = "linux", not(target_arch = "sparc64"))
|
||||
))]
|
||||
("3500000", BaudRate::B3500000),
|
||||
("3500000", 3_500_000),
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
all(target_os = "linux", not(target_arch = "sparc64"))
|
||||
))]
|
||||
("4000000", BaudRate::B4000000),
|
||||
("4000000", 4_000_000),
|
||||
];
|
||||
/// Control characters for the stty command.
|
||||
///
|
||||
|
||||
+86
-80
@@ -28,11 +28,11 @@ use nix::libc::{O_NONBLOCK, TIOCGWINSZ, TIOCSWINSZ, c_ushort};
|
||||
))]
|
||||
use nix::libc::{TCGETS2, termios2};
|
||||
|
||||
use nix::sys::termios::{
|
||||
ControlFlags, InputFlags, LocalFlags, OutputFlags, SetArg, SpecialCharacterIndices as S,
|
||||
Termios, cfsetispeed, cfsetospeed, tcgetattr, tcsetattr,
|
||||
use rustix::termios::{
|
||||
ControlModes as ControlFlags, InputModes as InputFlags, LocalModes as LocalFlags,
|
||||
OptionalActions, OutputModes as OutputFlags, SpecialCodeIndex as S, Termios, tcgetattr,
|
||||
tcsetattr,
|
||||
};
|
||||
use nix::{ioctl_read_bad, ioctl_write_ptr_bad};
|
||||
use std::cmp::Ordering;
|
||||
use std::fs::File;
|
||||
use std::io::{self, Stdin, stdin, stdout};
|
||||
@@ -231,19 +231,27 @@ pub struct TermSize {
|
||||
y: c_ushort,
|
||||
}
|
||||
|
||||
ioctl_read_bad!(
|
||||
/// Get terminal window size
|
||||
tiocgwinsz,
|
||||
TIOCGWINSZ,
|
||||
TermSize
|
||||
);
|
||||
/// Get terminal window size via ioctl
|
||||
///
|
||||
/// # Safety
|
||||
/// `size` must point to a valid, writable `TermSize` and `fd` must be a valid terminal fd.
|
||||
unsafe fn tiocgwinsz(fd: RawFd, size: *mut TermSize) -> io::Result<()> {
|
||||
if unsafe { libc::ioctl(fd, TIOCGWINSZ, size) } == -1 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
ioctl_write_ptr_bad!(
|
||||
/// Set terminal window size
|
||||
tiocswinsz,
|
||||
TIOCSWINSZ,
|
||||
TermSize
|
||||
);
|
||||
/// Set terminal window size via ioctl
|
||||
///
|
||||
/// # Safety
|
||||
/// `size` must point to a valid `TermSize` and `fd` must be a valid terminal fd.
|
||||
unsafe fn tiocswinsz(fd: RawFd, size: *mut TermSize) -> io::Result<()> {
|
||||
if unsafe { libc::ioctl(fd, TIOCSWINSZ, size) } == -1 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[uucore::main]
|
||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
@@ -269,7 +277,7 @@ fn stty(opts: &Options) -> UResult<()> {
|
||||
));
|
||||
}
|
||||
|
||||
let mut set_arg = SetArg::TCSADRAIN;
|
||||
let mut set_arg = OptionalActions::Drain;
|
||||
let mut valid_args: Vec<ArgOptions> = Vec::new();
|
||||
|
||||
if let Some(args) = &opts.settings {
|
||||
@@ -352,10 +360,10 @@ fn stty(opts: &Options) -> UResult<()> {
|
||||
}
|
||||
}
|
||||
"drain" => {
|
||||
set_arg = SetArg::TCSADRAIN;
|
||||
set_arg = OptionalActions::Drain;
|
||||
}
|
||||
"-drain" => {
|
||||
set_arg = SetArg::TCSANOW;
|
||||
set_arg = OptionalActions::Now;
|
||||
}
|
||||
"size" => {
|
||||
valid_args.push(ArgOptions::Print(PrintSetting::Size));
|
||||
@@ -550,7 +558,7 @@ fn check_flag_group<T>(flag: &Flag<T>, remove: bool) -> bool {
|
||||
remove && flag.group.is_some()
|
||||
}
|
||||
|
||||
fn print_special_setting(setting: &PrintSetting, fd: i32) -> nix::Result<()> {
|
||||
fn print_special_setting(setting: &PrintSetting, fd: i32) -> io::Result<()> {
|
||||
match setting {
|
||||
PrintSetting::Size => {
|
||||
let mut size = TermSize::default();
|
||||
@@ -626,23 +634,23 @@ fn print_terminal_size(
|
||||
opts: &Options,
|
||||
window_size: Option<&TermSize>,
|
||||
term_size: Option<&TermSize>,
|
||||
) -> nix::Result<()> {
|
||||
) -> io::Result<()> {
|
||||
// GNU linked against glibc 2.42 provides us baudrate 51 which panics cfgetospeed
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
let speed = nix::sys::termios::cfgetospeed(termios);
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg(all(not(target_arch = "powerpc"), not(target_arch = "powerpc64")))]
|
||||
ioctl_read_bad!(tcgets2, TCGETS2, termios2);
|
||||
let speed = termios.output_speed();
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg(all(not(target_arch = "powerpc"), not(target_arch = "powerpc64")))]
|
||||
let speed = {
|
||||
let mut t2 = unsafe { std::mem::zeroed::<termios2>() };
|
||||
unsafe { tcgets2(opts.file.as_raw_fd(), &raw mut t2)? };
|
||||
// SAFETY: TCGETS2 ioctl reads termios2 struct from fd
|
||||
if unsafe { libc::ioctl(opts.file.as_raw_fd(), TCGETS2, &raw mut t2) } == -1 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
t2.c_ospeed
|
||||
};
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))]
|
||||
let speed = nix::sys::termios::cfgetospeed(termios);
|
||||
let speed = termios.output_speed();
|
||||
|
||||
let mut printer = WrappedPrinter::new(window_size);
|
||||
|
||||
@@ -690,10 +698,7 @@ fn print_terminal_size(
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "redox"))]
|
||||
{
|
||||
// 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 line = libc_termios.c_line;
|
||||
let line = termios.line_discipline;
|
||||
printer.print(&translate!("stty-output-line", "line" => line));
|
||||
}
|
||||
printer.flush();
|
||||
@@ -824,7 +829,7 @@ fn string_to_flag(option: &str) -> Option<AllFlags<'_>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn control_char_to_string(cc: nix::libc::cc_t) -> nix::Result<String> {
|
||||
fn control_char_to_string(cc: libc::cc_t) -> io::Result<String> {
|
||||
if cc == 0 {
|
||||
return Ok(translate!("stty-output-undef"));
|
||||
}
|
||||
@@ -844,7 +849,7 @@ fn control_char_to_string(cc: nix::libc::cc_t) -> nix::Result<String> {
|
||||
// DEL character
|
||||
0x7f => Ok(("^", '?')),
|
||||
// Out of range (above 8 bits)
|
||||
_ => Err(nix::errno::Errno::ERANGE),
|
||||
_ => Err(io::Error::from_raw_os_error(libc::ERANGE)),
|
||||
}?;
|
||||
|
||||
Ok(format!("{meta_prefix}{ctrl_prefix}{character}"))
|
||||
@@ -854,12 +859,12 @@ fn print_control_chars(
|
||||
termios: &Termios,
|
||||
opts: &Options,
|
||||
term_size: Option<&TermSize>,
|
||||
) -> nix::Result<()> {
|
||||
) -> io::Result<()> {
|
||||
if !opts.all {
|
||||
// Print only control chars that differ from sane defaults
|
||||
let mut printer = WrappedPrinter::new(term_size);
|
||||
for (text, cc_index) in CONTROL_CHARS {
|
||||
let current_val = termios.control_chars[*cc_index as usize];
|
||||
let current_val = termios.special_codes[*cc_index];
|
||||
let sane_val = get_sane_control_char(*cc_index);
|
||||
|
||||
if current_val != sane_val {
|
||||
@@ -877,12 +882,12 @@ fn print_control_chars(
|
||||
for (text, cc_index) in CONTROL_CHARS {
|
||||
printer.print(&format!(
|
||||
"{text} = {};",
|
||||
control_char_to_string(termios.control_chars[*cc_index as usize])?
|
||||
control_char_to_string(termios.special_codes[*cc_index])?
|
||||
));
|
||||
}
|
||||
printer.print(&translate!("stty-output-min-time",
|
||||
"min" => termios.control_chars[S::VMIN as usize],
|
||||
"time" => termios.control_chars[S::VTIME as usize]
|
||||
"min" => termios.special_codes[S::VMIN],
|
||||
"time" => termios.special_codes[S::VTIME]
|
||||
));
|
||||
printer.flush();
|
||||
Ok(())
|
||||
@@ -891,12 +896,17 @@ fn print_control_chars(
|
||||
fn print_in_save_format(termios: &Termios) {
|
||||
print!(
|
||||
"{:x}:{:x}:{:x}:{:x}",
|
||||
termios.input_flags.bits(),
|
||||
termios.output_flags.bits(),
|
||||
termios.control_flags.bits(),
|
||||
termios.local_flags.bits()
|
||||
termios.input_modes.bits(),
|
||||
termios.output_modes.bits(),
|
||||
termios.control_modes.bits(),
|
||||
termios.local_modes.bits()
|
||||
);
|
||||
for cc in termios.control_chars {
|
||||
// Print all special codes in save format by accessing the raw cc array
|
||||
// via a pointer cast, since rustix doesn't expose the inner array.
|
||||
let cc_ptr = (&raw const termios.special_codes).cast::<[libc::cc_t; libc::NCCS]>();
|
||||
// SAFETY: SpecialCodes is a transparent wrapper around [cc_t; NCCS]
|
||||
let cc_array = unsafe { &*cc_ptr };
|
||||
for cc in cc_array {
|
||||
print!(":{cc:x}");
|
||||
}
|
||||
println!();
|
||||
@@ -904,12 +914,12 @@ fn print_in_save_format(termios: &Termios) {
|
||||
|
||||
/// Gets terminal size using the tiocgwinsz ioctl system call.
|
||||
/// This queries the kernel for the current terminal window dimensions.
|
||||
fn get_terminal_size(fd: RawFd) -> nix::Result<TermSize> {
|
||||
fn get_terminal_size(fd: RawFd) -> io::Result<TermSize> {
|
||||
let mut term_size = TermSize::default();
|
||||
unsafe { tiocgwinsz(fd, &raw mut term_size) }.map(|_| term_size)
|
||||
}
|
||||
|
||||
fn print_settings(termios: &Termios, opts: &Options) -> nix::Result<()> {
|
||||
fn print_settings(termios: &Termios, opts: &Options) -> io::Result<()> {
|
||||
if opts.save {
|
||||
print_in_save_format(termios);
|
||||
} else {
|
||||
@@ -972,7 +982,7 @@ fn print_flags<T: TermiosFlag>(
|
||||
}
|
||||
|
||||
/// Apply a single setting
|
||||
fn apply_setting(termios: &mut Termios, setting: &AllFlags) -> nix::Result<()> {
|
||||
fn apply_setting(termios: &mut Termios, setting: &AllFlags) -> io::Result<()> {
|
||||
match setting {
|
||||
AllFlags::Baud(_, _) => apply_baud_rate_flag(termios, setting)?,
|
||||
AllFlags::ControlFlags((setting, disable)) => {
|
||||
@@ -991,14 +1001,14 @@ fn apply_setting(termios: &mut Termios, setting: &AllFlags) -> nix::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn apply_baud_rate_flag(termios: &mut Termios, input: &AllFlags) -> nix::Result<()> {
|
||||
fn apply_baud_rate_flag(termios: &mut Termios, input: &AllFlags) -> io::Result<()> {
|
||||
if let AllFlags::Baud(rate, baud_type) = input {
|
||||
match baud_type {
|
||||
flags::BaudType::Input => cfsetispeed(termios, *rate)?,
|
||||
flags::BaudType::Output => cfsetospeed(termios, *rate)?,
|
||||
flags::BaudType::Input => termios.set_input_speed(*rate)?,
|
||||
flags::BaudType::Output => termios.set_output_speed(*rate)?,
|
||||
flags::BaudType::Both => {
|
||||
cfsetispeed(termios, *rate)?;
|
||||
cfsetospeed(termios, *rate)?;
|
||||
termios.set_input_speed(*rate)?;
|
||||
termios.set_output_speed(*rate)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1006,7 +1016,7 @@ fn apply_baud_rate_flag(termios: &mut Termios, input: &AllFlags) -> nix::Result<
|
||||
}
|
||||
|
||||
fn apply_char_mapping(termios: &mut Termios, mapping: &(S, u8)) {
|
||||
termios.control_chars[mapping.0 as usize] = mapping.1;
|
||||
termios.special_codes[mapping.0] = mapping.1;
|
||||
}
|
||||
|
||||
/// Apply a saved terminal state to the current termios.
|
||||
@@ -1027,15 +1037,19 @@ fn apply_saved_state(termios: &mut Termios, state: &[u32]) {
|
||||
}
|
||||
|
||||
// Apply the four flag groups, done (as _) for MacOS size compatibility
|
||||
termios.input_flags = InputFlags::from_bits_truncate(state[0] as _);
|
||||
termios.output_flags = OutputFlags::from_bits_truncate(state[1] as _);
|
||||
termios.control_flags = ControlFlags::from_bits_truncate(state[2] as _);
|
||||
termios.local_flags = LocalFlags::from_bits_truncate(state[3] as _);
|
||||
termios.input_modes = InputFlags::from_bits_truncate(state[0] as _);
|
||||
termios.output_modes = OutputFlags::from_bits_truncate(state[1] as _);
|
||||
termios.control_modes = ControlFlags::from_bits_truncate(state[2] as _);
|
||||
termios.local_modes = LocalFlags::from_bits_truncate(state[3] as _);
|
||||
|
||||
// Apply control characters if present (stored as u32 but used as u8)
|
||||
// Access the raw cc array via pointer cast since rustix doesn't expose the inner array.
|
||||
let cc_ptr = (&raw mut termios.special_codes).cast::<[libc::cc_t; libc::NCCS]>();
|
||||
// SAFETY: SpecialCodes is a transparent wrapper around [cc_t; NCCS]
|
||||
let cc_array = unsafe { &mut *cc_ptr };
|
||||
for (i, &cc_val) in state.iter().skip(4).enumerate() {
|
||||
if i < termios.control_chars.len() {
|
||||
termios.control_chars[i] = cc_val as u8;
|
||||
if i < cc_array.len() {
|
||||
cc_array[i] = cc_val as u8;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1044,7 +1058,7 @@ fn apply_special_setting(
|
||||
_termios: &mut Termios,
|
||||
setting: &SpecialSetting,
|
||||
fd: i32,
|
||||
) -> nix::Result<()> {
|
||||
) -> io::Result<()> {
|
||||
let mut size = TermSize::default();
|
||||
unsafe { tiocgwinsz(fd, &raw mut size)? };
|
||||
match setting {
|
||||
@@ -1241,15 +1255,7 @@ fn get_sane_control_char(cc_index: S) -> u8 {
|
||||
}
|
||||
}
|
||||
// Default values for control chars not in the sane list
|
||||
match cc_index {
|
||||
S::VEOL => 0,
|
||||
S::VEOL2 => 0,
|
||||
S::VMIN => 1,
|
||||
S::VTIME => 0,
|
||||
#[cfg(target_os = "linux")]
|
||||
S::VSWTC => 0,
|
||||
_ => 0,
|
||||
}
|
||||
u8::from(cc_index == S::VMIN)
|
||||
}
|
||||
|
||||
pub fn uu_app() -> Command {
|
||||
@@ -1291,45 +1297,45 @@ pub fn uu_app() -> Command {
|
||||
|
||||
impl TermiosFlag for ControlFlags {
|
||||
fn is_in(&self, termios: &Termios, group: Option<Self>) -> bool {
|
||||
termios.control_flags.contains(*self)
|
||||
&& group.is_none_or(|g| !termios.control_flags.intersects(g - *self))
|
||||
termios.control_modes.contains(*self)
|
||||
&& group.is_none_or(|g| !termios.control_modes.intersects(g - *self))
|
||||
}
|
||||
|
||||
fn apply(&self, termios: &mut Termios, val: bool) {
|
||||
termios.control_flags.set(*self, val);
|
||||
termios.control_modes.set(*self, val);
|
||||
}
|
||||
}
|
||||
|
||||
impl TermiosFlag for InputFlags {
|
||||
fn is_in(&self, termios: &Termios, group: Option<Self>) -> bool {
|
||||
termios.input_flags.contains(*self)
|
||||
&& group.is_none_or(|g| !termios.input_flags.intersects(g - *self))
|
||||
termios.input_modes.contains(*self)
|
||||
&& group.is_none_or(|g| !termios.input_modes.intersects(g - *self))
|
||||
}
|
||||
|
||||
fn apply(&self, termios: &mut Termios, val: bool) {
|
||||
termios.input_flags.set(*self, val);
|
||||
termios.input_modes.set(*self, val);
|
||||
}
|
||||
}
|
||||
|
||||
impl TermiosFlag for OutputFlags {
|
||||
fn is_in(&self, termios: &Termios, group: Option<Self>) -> bool {
|
||||
termios.output_flags.contains(*self)
|
||||
&& group.is_none_or(|g| !termios.output_flags.intersects(g - *self))
|
||||
termios.output_modes.contains(*self)
|
||||
&& group.is_none_or(|g| !termios.output_modes.intersects(g - *self))
|
||||
}
|
||||
|
||||
fn apply(&self, termios: &mut Termios, val: bool) {
|
||||
termios.output_flags.set(*self, val);
|
||||
termios.output_modes.set(*self, val);
|
||||
}
|
||||
}
|
||||
|
||||
impl TermiosFlag for LocalFlags {
|
||||
fn is_in(&self, termios: &Termios, group: Option<Self>) -> bool {
|
||||
termios.local_flags.contains(*self)
|
||||
&& group.is_none_or(|g| !termios.local_flags.intersects(g - *self))
|
||||
termios.local_modes.contains(*self)
|
||||
&& group.is_none_or(|g| !termios.local_modes.intersects(g - *self))
|
||||
}
|
||||
|
||||
fn apply(&self, termios: &mut Termios, val: bool) {
|
||||
termios.local_flags.set(*self, val);
|
||||
termios.local_modes.set(*self, val);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,8 @@ uucore = { workspace = true, features = ["wide"] }
|
||||
fluent = { workspace = true }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
nix = { workspace = true }
|
||||
libc = { workspace = true }
|
||||
rustix = { workspace = true, features = ["fs"] }
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
windows-sys = { workspace = true, features = [
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user