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:
commit
c14ba865ce
2 changed files with 85 additions and 77 deletions
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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> {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue