From c8aa5a6b578c1149431661236724fb1c1d7b94ef Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen <tuomas.tynkkynen@iki.fi> Date: Sat, 1 Mar 2025 22:38:19 +0200 Subject: [PATCH] mesg: Add tool --- Cargo.lock | 10 ++++ Cargo.toml | 2 + src/uu/mesg/Cargo.toml | 16 ++++++ src/uu/mesg/mesg.md | 7 +++ src/uu/mesg/src/main.rs | 1 + src/uu/mesg/src/mesg.rs | 101 +++++++++++++++++++++++++++++++++++++ tests/by-util/test_mesg.rs | 36 +++++++++++++ tests/tests.rs | 4 ++ 8 files changed, 177 insertions(+) create mode 100644 src/uu/mesg/Cargo.toml create mode 100644 src/uu/mesg/mesg.md create mode 100644 src/uu/mesg/src/main.rs create mode 100644 src/uu/mesg/src/mesg.rs create mode 100644 tests/by-util/test_mesg.rs diff --git a/Cargo.lock b/Cargo.lock index 8ff316a..e447cdc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -976,6 +976,7 @@ dependencies = [ "uu_lscpu", "uu_lslocks", "uu_lsmem", + "uu_mesg", "uu_mountpoint", "uu_rev", "uu_setsid", @@ -1067,6 +1068,15 @@ dependencies = [ "uucore", ] +[[package]] +name = "uu_mesg" +version = "0.0.1" +dependencies = [ + "clap", + "nix", + "uucore", +] + [[package]] name = "uu_mountpoint" version = "0.0.1" diff --git a/Cargo.toml b/Cargo.toml index 2c7adc1..e4242c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ feat_common_core = [ "lscpu", "lslocks", "lsmem", + "mesg", "mountpoint", "rev", "setsid", @@ -79,6 +80,7 @@ last = { optional = true, version = "0.0.1", package = "uu_last", path = "src/uu lscpu = { optional = true, version = "0.0.1", package = "uu_lscpu", path = "src/uu/lscpu" } lslocks = { optional = true, version = "0.0.1", package = "uu_lslocks", path = "src/uu/lslocks" } lsmem = { optional = true, version = "0.0.1", package = "uu_lsmem", path = "src/uu/lsmem" } +mesg = { optional = true, version = "0.0.1", package = "uu_mesg", path = "src/uu/mesg" } mountpoint = { optional = true, version = "0.0.1", package = "uu_mountpoint", path = "src/uu/mountpoint" } rev = { optional = true, version = "0.0.1", package = "uu_rev", path = "src/uu/rev" } setsid = { optional = true, version = "0.0.1", package = "uu_setsid", path ="src/uu/setsid" } diff --git a/src/uu/mesg/Cargo.toml b/src/uu/mesg/Cargo.toml new file mode 100644 index 0000000..c75a4d8 --- /dev/null +++ b/src/uu/mesg/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "uu_mesg" +version = "0.0.1" +edition = "2021" + +[lib] +path = "src/mesg.rs" + +[[bin]] +name = "mesg" +path = "src/main.rs" + +[dependencies] +clap = { workspace = true } +nix = { workspace = true } +uucore = { workspace = true } diff --git a/src/uu/mesg/mesg.md b/src/uu/mesg/mesg.md new file mode 100644 index 0000000..afcb543 --- /dev/null +++ b/src/uu/mesg/mesg.md @@ -0,0 +1,7 @@ +# mesg + +``` +mesg [option] [y|n] +``` + +enables or disables displaying messages from other users diff --git a/src/uu/mesg/src/main.rs b/src/uu/mesg/src/main.rs new file mode 100644 index 0000000..aff7ec1 --- /dev/null +++ b/src/uu/mesg/src/main.rs @@ -0,0 +1 @@ +uucore::bin!(uu_mesg); diff --git a/src/uu/mesg/src/mesg.rs b/src/uu/mesg/src/mesg.rs new file mode 100644 index 0000000..220ed36 --- /dev/null +++ b/src/uu/mesg/src/mesg.rs @@ -0,0 +1,101 @@ +// This file is part of the uutils util-linux package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +use clap::{builder::PossibleValuesParser, crate_version, Arg, ArgAction, ArgMatches, Command}; +#[cfg(target_family = "unix")] +use uucore::error::{set_exit_code, UIoError}; +use uucore::{error::UResult, format_usage, help_about, help_usage}; + +const ABOUT: &str = help_about!("mesg.md"); +const USAGE: &str = help_usage!("mesg.md"); + +#[cfg(target_family = "unix")] +pub fn do_mesg(matches: &ArgMatches) -> UResult<()> { + use nix::sys::stat::{fchmod, fstat, Mode}; + use std::{io, os::fd::AsRawFd}; + use std::{io::IsTerminal, os::fd::AsFd}; + + for fd in &[ + std::io::stdin().as_fd(), + std::io::stdout().as_fd(), + std::io::stderr().as_fd(), + ] { + if fd.is_terminal() { + let st = fstat(fd.as_raw_fd())?; + if let Some(enable) = matches.get_one::<String>("enable") { + // 'mesg y' on the GNU version seems to only modify the group write bit, + // but 'mesg n' modifies both group and others write bits. + let new_mode = if enable == "y" { + st.st_mode | 0o020 + } else { + st.st_mode & !0o022 + }; + fchmod(fd.as_raw_fd(), Mode::from_bits_retain(new_mode))?; + if enable == "n" { + set_exit_code(1); + } + if matches.get_flag("verbose") { + println!( + "write access to your terminal is {}", + if enable == "y" { "allowed" } else { "denied" } + ); + } + } else if st.st_mode & 0o022 != 0 { + println!("is y"); + } else { + set_exit_code(1); + println!("is n"); + } + return Ok(()); + } + } + Err(UIoError::new( + io::ErrorKind::Other, + "stdin/stdout/stderr is not a terminal", + )) +} + +#[cfg(target_family = "unix")] +#[uucore::main] +pub fn uumain(args: impl uucore::Args) -> UResult<()> { + let matches = uu_app().try_get_matches_from(args)?; + if let Err(e) = do_mesg(&matches) { + set_exit_code(2); + uucore::show_error!("{}", e); + }; + Ok(()) +} + +pub fn uu_app() -> Command { + Command::new(uucore::util_name()) + .version(crate_version!()) + .about(ABOUT) + .override_usage(format_usage(USAGE)) + .infer_long_args(true) + .arg( + Arg::new("verbose") + .short('v') + .long("verbose") + .help("Explain what is being done") + .action(ArgAction::SetTrue), + ) + .arg( + Arg::new("enable") + .help("Whether to allow or disallow messages") + .value_parser(PossibleValuesParser::new(["y", "n"])) + .action(ArgAction::Set), + ) +} + +#[cfg(not(target_family = "unix"))] +#[uucore::main] +pub fn uumain(args: impl uucore::Args) -> UResult<()> { + let _matches: ArgMatches = uu_app().try_get_matches_from(args)?; + + Err(uucore::error::USimpleError::new( + 1, + "`mesg` is available only on Unix platforms.", + )) +} diff --git a/tests/by-util/test_mesg.rs b/tests/by-util/test_mesg.rs new file mode 100644 index 0000000..7715a8d --- /dev/null +++ b/tests/by-util/test_mesg.rs @@ -0,0 +1,36 @@ +// This file is part of the uutils util-linux package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +use crate::common::util::TestScenario; + +#[test] +fn test_invalid_verb() { + new_ucmd!().arg("foo").fails().code_is(1); +} + +#[test] +#[cfg(target_family = "unix")] +fn test_no_terminal() { + for args in &[vec![], vec!["y"], vec!["n"]] { + new_ucmd!() + .args(args) + .fails() + .code_is(2) + .stderr_contains("stdin/stdout/stderr is not a terminal"); + } +} + +#[cfg(not(target_family = "unix"))] +mod non_unix { + use crate::common::util::TestScenario; + + #[test] + fn test_fails_on_unsupported_platforms() { + new_ucmd!() + .fails() + .code_is(1) + .stderr_is("mesg: `mesg` is available only on Unix platforms.\n"); + } +} diff --git a/tests/tests.rs b/tests/tests.rs index ae15cde..8d72ee6 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -17,6 +17,10 @@ mod test_lsmem; #[path = "by-util/test_lslocks.rs"] mod test_lslocks; +#[cfg(feature = "mesg")] +#[path = "by-util/test_mesg.rs"] +mod test_mesg; + #[cfg(feature = "mountpoint")] #[path = "by-util/test_mountpoint.rs"] mod test_mountpoint;