use clap::{Arg, ArgAction, Command, value_parser};
use rustix::fs::Mode;
use rustix::process::umask;
use uucore::display::Quotable;
use uucore::error::{UResult, USimpleError, strip_errno};
use uucore::translate;
use uucore::{format_usage, show};
mod options {
pub static MODE: &str = "mode";
pub static SECURITY_CONTEXT: &str = "Z";
pub static CONTEXT: &str = "context";
pub static FIFO: &str = "fifo";
}
#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let matches = uucore::clap_localization::handle_clap_result(uu_app(), args)?;
let mode = calculate_mode(matches.get_one::<String>(options::MODE))
.map_err(|e| USimpleError::new(1, translate!("mkfifo-error-invalid-mode", "error" => e)))?;
let non_file_permission_bits = 0o7000; if mode & non_file_permission_bits != 0 {
return Err(USimpleError::new(
1,
translate!("mkfifo-error-non-file-permission"),
));
}
#[allow(clippy::unwrap_used, reason = "set as required by clap")]
let fifos: Vec<String> = matches
.get_many::<String>(options::FIFO)
.unwrap()
.cloned()
.collect();
for f in fifos {
#[cfg(all(feature = "selinux", any(target_os = "linux", target_os = "android")))]
let _selinux_guard = {
let set_security_context = matches.get_flag(options::SECURITY_CONTEXT);
let context = matches.get_one::<String>(options::CONTEXT);
if set_security_context || context.is_some() {
let mode = uucore::libc::S_IFIFO | mode as uucore::libc::mode_t;
match uucore::selinux::FsCreateContext::new(
std::path::Path::new(&f),
Some(mode),
context,
) {
Ok(guard) => Some(guard),
Err(e) => return Err(USimpleError::new(1, e.to_string())),
}
} else {
None
}
};
let prev_umask = umask(Mode::empty());
let mkfifo_result = create_fifo(f.as_str(), mode);
umask(prev_umask);
if let Err(e) = mkfifo_result {
show!(USimpleError::new(
1,
translate!(
"mkfifo-error-cannot-create-fifo",
"path" => f.quote(),
"error" => strip_errno(&e)
),
));
} else {
#[cfg(all(feature = "smack", target_os = "linux"))]
{
let set_security_context = matches.get_flag(options::SECURITY_CONTEXT);
let context = matches.get_one::<String>(options::CONTEXT);
if set_security_context || context.is_some() {
uucore::smack::set_smack_label_and_cleanup(&f, context, |p| {
std::fs::remove_file(p)
})?;
}
}
}
}
Ok(())
}
pub fn uu_app() -> Command {
Command::new("mkfifo")
.version(uucore::crate_version!())
.help_template(uucore::localized_help_template("mkfifo"))
.override_usage(format_usage(&translate!("mkfifo-usage")))
.about(translate!("mkfifo-about"))
.infer_long_args(true)
.arg(
Arg::new(options::MODE)
.short('m')
.long(options::MODE)
.help(translate!("mkfifo-help-mode"))
.value_name("MODE"),
)
.arg(
Arg::new(options::SECURITY_CONTEXT)
.short('Z')
.help(translate!("mkfifo-help-selinux"))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::CONTEXT)
.long(options::CONTEXT)
.value_name("CTX")
.value_parser(value_parser!(String))
.num_args(0..=1)
.require_equals(true)
.help(translate!("mkfifo-help-context")),
)
.arg(
Arg::new(options::FIFO)
.hide(true)
.action(ArgAction::Append)
.required(true)
.value_hint(clap::ValueHint::AnyPath),
)
}
#[cfg(not(target_vendor = "apple"))]
fn create_fifo(path: &str, mode: u32) -> Result<(), std::io::Error> {
use rustix::fs::{CWD, mkfifoat};
mkfifoat(CWD, path, Mode::from_bits_truncate(mode)).map_err(std::io::Error::from)
}
#[cfg(target_vendor = "apple")]
fn create_fifo(path: &str, mode: u32) -> Result<(), std::io::Error> {
use std::ffi::CString;
let c_path =
CString::new(path).map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))?;
let rc = unsafe { libc::mkfifo(c_path.as_ptr(), mode as libc::mode_t) };
if rc == 0 {
Ok(())
} else {
Err(std::io::Error::last_os_error())
}
}
fn calculate_mode(mode_option: Option<&String>) -> Result<u32, String> {
let umask = uucore::mode::get_umask();
let mode = 0o666;
if let Some(m) = mode_option {
uucore::mode::parse_chmod(mode, m, false, umask)
} else {
Ok(mode & !umask) }
}