1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-29 12:07:46 +00:00

Merge pull request #2821 from jfinkels/runcon-uresult

runcon: return UResult from uumain() function
This commit is contained in:
Sylvestre Ledru 2022-01-07 21:47:51 +01:00 committed by GitHub
commit c14ba865ce
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 85 additions and 77 deletions

View file

@ -1,12 +1,22 @@
use std::ffi::OsString;
use std::fmt::Write;
use std::fmt::{Display, Formatter, Write};
use std::io;
use std::str::Utf8Error;
use uucore::display::Quotable;
use uucore::error::UError;
pub(crate) type Result<T> = std::result::Result<T, Error>;
// This list is NOT exhaustive. This command might perform an `execvp()` to run
// a different program. When that happens successfully, the exit status of this
// process will be the exit status of that program.
pub(crate) mod error_exit_status {
pub const NOT_FOUND: i32 = 127;
pub const COULD_NOT_EXECUTE: i32 = 126;
pub const ANOTHER_ERROR: i32 = libc::EXIT_FAILURE;
}
#[derive(thiserror::Error, Debug)]
pub(crate) enum Error {
#[error("No command is specified")]
@ -63,13 +73,44 @@ impl Error {
}
}
pub(crate) fn report_full_error(mut err: &dyn std::error::Error) -> String {
let mut desc = String::with_capacity(256);
write!(&mut desc, "{}", err).unwrap();
pub(crate) fn write_full_error<W>(writer: &mut W, err: &dyn std::error::Error) -> std::fmt::Result
where
W: Write,
{
write!(writer, "{}", err)?;
let mut err = err;
while let Some(source) = err.source() {
err = source;
write!(&mut desc, ": {}", err).unwrap();
write!(writer, ": {}", err)?;
}
write!(writer, ".")?;
Ok(())
}
#[derive(Debug)]
pub(crate) struct RunconError {
inner: Error,
code: i32,
}
impl RunconError {
pub(crate) fn new(e: Error) -> RunconError {
RunconError::with_code(error_exit_status::ANOTHER_ERROR, e)
}
pub(crate) fn with_code(code: i32, e: Error) -> RunconError {
RunconError { inner: e, code }
}
}
impl std::error::Error for RunconError {}
impl UError for RunconError {
fn code(&self) -> i32 {
self.code
}
}
impl Display for RunconError {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
write_full_error(f, &self.inner)
}
desc.push('.');
desc
}

View file

@ -1,6 +1,6 @@
// spell-checker:ignore (vars) RFILE
use uucore::{show_error, show_usage_error};
use uucore::error::{UResult, UUsageError};
use clap::{App, Arg};
use selinux::{OpaqueSecurityContext, SecurityClass, SecurityContext};
@ -13,7 +13,8 @@ use std::{io, ptr};
mod errors;
use errors::{report_full_error, Error, Result};
use errors::error_exit_status;
use errors::{Error, Result, RunconError};
const VERSION: &str = env!("CARGO_PKG_VERSION");
const ABOUT: &str = "Run command with specified security context.";
@ -35,16 +36,6 @@ pub mod options {
pub const RANGE: &str = "range";
}
// This list is NOT exhaustive. This command might perform an `execvp()` to run
// a different program. When that happens successfully, the exit status of this
// process will be the exit status of that program.
mod error_exit_status {
pub const SUCCESS: i32 = libc::EXIT_SUCCESS;
pub const NOT_FOUND: i32 = 127;
pub const COULD_NOT_EXECUTE: i32 = 126;
pub const ANOTHER_ERROR: i32 = libc::EXIT_FAILURE;
}
fn get_usage() -> String {
format!(
"{0} [CONTEXT COMMAND [ARG...]]\n \
@ -53,7 +44,8 @@ fn get_usage() -> String {
)
}
pub fn uumain(args: impl uucore::Args) -> i32 {
#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let usage = get_usage();
let config = uu_app().usage(usage.as_ref());
@ -65,39 +57,28 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
match r.kind {
clap::ErrorKind::HelpDisplayed | clap::ErrorKind::VersionDisplayed => {
println!("{}", r);
return error_exit_status::SUCCESS;
return Ok(());
}
_ => {}
}
}
show_usage_error!("{}.\n", r);
return error_exit_status::ANOTHER_ERROR;
return Err(UUsageError::new(
error_exit_status::ANOTHER_ERROR,
format!("{}", r),
));
}
};
match &options.mode {
CommandLineMode::Print => {
if let Err(r) = print_current_context() {
show_error!("{}", report_full_error(&r));
return error_exit_status::ANOTHER_ERROR;
}
}
CommandLineMode::Print => print_current_context().map_err(|e| RunconError::new(e).into()),
CommandLineMode::PlainContext { context, command } => {
let (exit_status, err) =
if let Err(err) = get_plain_context(context).and_then(set_next_exec_context) {
(error_exit_status::ANOTHER_ERROR, err)
} else {
// On successful execution, the following call never returns,
// and this process image is replaced.
execute_command(command, &options.arguments)
};
show_error!("{}", report_full_error(&err));
return exit_status;
get_plain_context(context)
.and_then(set_next_exec_context)
.map_err(RunconError::new)?;
// On successful execution, the following call never returns,
// and this process image is replaced.
execute_command(command, &options.arguments)
}
CommandLineMode::CustomContext {
compute_transition_context,
user,
@ -106,34 +87,26 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
range,
command,
} => {
if let Some(command) = command {
let (exit_status, err) = if let Err(err) = get_custom_context(
*compute_transition_context,
user.as_deref(),
role.as_deref(),
the_type.as_deref(),
range.as_deref(),
command,
)
.and_then(set_next_exec_context)
{
(error_exit_status::ANOTHER_ERROR, err)
} else {
match command {
Some(command) => {
get_custom_context(
*compute_transition_context,
user.as_deref(),
role.as_deref(),
the_type.as_deref(),
range.as_deref(),
command,
)
.and_then(set_next_exec_context)
.map_err(RunconError::new)?;
// On successful execution, the following call never returns,
// and this process image is replaced.
execute_command(command, &options.arguments)
};
show_error!("{}", report_full_error(&err));
return exit_status;
} else if let Err(r) = print_current_context() {
show_error!("{}", report_full_error(&r));
return error_exit_status::ANOTHER_ERROR;
}
None => print_current_context().map_err(|e| RunconError::new(e).into()),
}
}
}
error_exit_status::SUCCESS
}
pub fn uu_app() -> App<'static, 'static> {
@ -406,25 +379,19 @@ fn get_custom_context(
Ok(osc)
}
/// The actual return type of this function should be `Result<!, (i32, Error)>`
/// The actual return type of this function should be `UResult<!>`.
/// However, until the *never* type is stabilized, one way to indicate to the
/// compiler the only valid return type is to say "if this returns, it will
/// always return an error".
fn execute_command(command: &OsStr, arguments: &[OsString]) -> (i32, Error) {
let c_command = match os_str_to_c_string(command) {
Ok(v) => v,
Err(r) => return (error_exit_status::ANOTHER_ERROR, r),
};
fn execute_command(command: &OsStr, arguments: &[OsString]) -> UResult<()> {
let c_command = os_str_to_c_string(command).map_err(RunconError::new)?;
let argv_storage: Vec<CString> = match arguments
let argv_storage: Vec<CString> = arguments
.iter()
.map(AsRef::as_ref)
.map(os_str_to_c_string)
.collect::<Result<_>>()
{
Ok(v) => v,
Err(r) => return (error_exit_status::ANOTHER_ERROR, r),
};
.map_err(RunconError::new)?;
let mut argv: Vec<*const c_char> = Vec::with_capacity(arguments.len().saturating_add(2));
argv.push(c_command.as_ptr());
@ -441,7 +408,7 @@ fn execute_command(command: &OsStr, arguments: &[OsString]) -> (i32, Error) {
};
let err = Error::from_io1("Executing command", command, err);
(exit_status, err)
Err(RunconError::with_code(exit_status, err).into())
}
fn os_str_to_c_string(s: &OsStr) -> Result<CString> {