mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-30 12:37:49 +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::ffi::OsString;
|
||||||
use std::fmt::Write;
|
use std::fmt::{Display, Formatter, Write};
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::str::Utf8Error;
|
use std::str::Utf8Error;
|
||||||
|
|
||||||
use uucore::display::Quotable;
|
use uucore::display::Quotable;
|
||||||
|
use uucore::error::UError;
|
||||||
|
|
||||||
pub(crate) type Result<T> = std::result::Result<T, Error>;
|
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)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
pub(crate) enum Error {
|
pub(crate) enum Error {
|
||||||
#[error("No command is specified")]
|
#[error("No command is specified")]
|
||||||
|
@ -63,13 +73,44 @@ impl Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn report_full_error(mut err: &dyn std::error::Error) -> String {
|
pub(crate) fn write_full_error<W>(writer: &mut W, err: &dyn std::error::Error) -> std::fmt::Result
|
||||||
let mut desc = String::with_capacity(256);
|
where
|
||||||
write!(&mut desc, "{}", err).unwrap();
|
W: Write,
|
||||||
|
{
|
||||||
|
write!(writer, "{}", err)?;
|
||||||
|
let mut err = err;
|
||||||
while let Some(source) = err.source() {
|
while let Some(source) = err.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
|
// spell-checker:ignore (vars) RFILE
|
||||||
|
|
||||||
use uucore::{show_error, show_usage_error};
|
use uucore::error::{UResult, UUsageError};
|
||||||
|
|
||||||
use clap::{App, Arg};
|
use clap::{App, Arg};
|
||||||
use selinux::{OpaqueSecurityContext, SecurityClass, SecurityContext};
|
use selinux::{OpaqueSecurityContext, SecurityClass, SecurityContext};
|
||||||
|
@ -13,7 +13,8 @@ use std::{io, ptr};
|
||||||
|
|
||||||
mod errors;
|
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 VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
const ABOUT: &str = "Run command with specified security context.";
|
const ABOUT: &str = "Run command with specified security context.";
|
||||||
|
@ -35,16 +36,6 @@ pub mod options {
|
||||||
pub const RANGE: &str = "range";
|
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 {
|
fn get_usage() -> String {
|
||||||
format!(
|
format!(
|
||||||
"{0} [CONTEXT COMMAND [ARG...]]\n \
|
"{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 usage = get_usage();
|
||||||
|
|
||||||
let config = uu_app().usage(usage.as_ref());
|
let config = uu_app().usage(usage.as_ref());
|
||||||
|
@ -65,39 +57,28 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
match r.kind {
|
match r.kind {
|
||||||
clap::ErrorKind::HelpDisplayed | clap::ErrorKind::VersionDisplayed => {
|
clap::ErrorKind::HelpDisplayed | clap::ErrorKind::VersionDisplayed => {
|
||||||
println!("{}", r);
|
println!("{}", r);
|
||||||
return error_exit_status::SUCCESS;
|
return Ok(());
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return Err(UUsageError::new(
|
||||||
show_usage_error!("{}.\n", r);
|
error_exit_status::ANOTHER_ERROR,
|
||||||
return error_exit_status::ANOTHER_ERROR;
|
format!("{}", r),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match &options.mode {
|
match &options.mode {
|
||||||
CommandLineMode::Print => {
|
CommandLineMode::Print => print_current_context().map_err(|e| RunconError::new(e).into()),
|
||||||
if let Err(r) = print_current_context() {
|
|
||||||
show_error!("{}", report_full_error(&r));
|
|
||||||
return error_exit_status::ANOTHER_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CommandLineMode::PlainContext { context, command } => {
|
CommandLineMode::PlainContext { context, command } => {
|
||||||
let (exit_status, err) =
|
get_plain_context(context)
|
||||||
if let Err(err) = get_plain_context(context).and_then(set_next_exec_context) {
|
.and_then(set_next_exec_context)
|
||||||
(error_exit_status::ANOTHER_ERROR, err)
|
.map_err(RunconError::new)?;
|
||||||
} else {
|
// On successful execution, the following call never returns,
|
||||||
// On successful execution, the following call never returns,
|
// and this process image is replaced.
|
||||||
// and this process image is replaced.
|
execute_command(command, &options.arguments)
|
||||||
execute_command(command, &options.arguments)
|
|
||||||
};
|
|
||||||
|
|
||||||
show_error!("{}", report_full_error(&err));
|
|
||||||
return exit_status;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandLineMode::CustomContext {
|
CommandLineMode::CustomContext {
|
||||||
compute_transition_context,
|
compute_transition_context,
|
||||||
user,
|
user,
|
||||||
|
@ -106,34 +87,26 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
range,
|
range,
|
||||||
command,
|
command,
|
||||||
} => {
|
} => {
|
||||||
if let Some(command) = command {
|
match command {
|
||||||
let (exit_status, err) = if let Err(err) = get_custom_context(
|
Some(command) => {
|
||||||
*compute_transition_context,
|
get_custom_context(
|
||||||
user.as_deref(),
|
*compute_transition_context,
|
||||||
role.as_deref(),
|
user.as_deref(),
|
||||||
the_type.as_deref(),
|
role.as_deref(),
|
||||||
range.as_deref(),
|
the_type.as_deref(),
|
||||||
command,
|
range.as_deref(),
|
||||||
)
|
command,
|
||||||
.and_then(set_next_exec_context)
|
)
|
||||||
{
|
.and_then(set_next_exec_context)
|
||||||
(error_exit_status::ANOTHER_ERROR, err)
|
.map_err(RunconError::new)?;
|
||||||
} else {
|
|
||||||
// On successful execution, the following call never returns,
|
// On successful execution, the following call never returns,
|
||||||
// and this process image is replaced.
|
// and this process image is replaced.
|
||||||
execute_command(command, &options.arguments)
|
execute_command(command, &options.arguments)
|
||||||
};
|
}
|
||||||
|
None => print_current_context().map_err(|e| RunconError::new(e).into()),
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
error_exit_status::SUCCESS
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn uu_app() -> App<'static, 'static> {
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
@ -406,25 +379,19 @@ fn get_custom_context(
|
||||||
Ok(osc)
|
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
|
/// 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
|
/// compiler the only valid return type is to say "if this returns, it will
|
||||||
/// always return an error".
|
/// always return an error".
|
||||||
fn execute_command(command: &OsStr, arguments: &[OsString]) -> (i32, Error) {
|
fn execute_command(command: &OsStr, arguments: &[OsString]) -> UResult<()> {
|
||||||
let c_command = match os_str_to_c_string(command) {
|
let c_command = os_str_to_c_string(command).map_err(RunconError::new)?;
|
||||||
Ok(v) => v,
|
|
||||||
Err(r) => return (error_exit_status::ANOTHER_ERROR, r),
|
|
||||||
};
|
|
||||||
|
|
||||||
let argv_storage: Vec<CString> = match arguments
|
let argv_storage: Vec<CString> = arguments
|
||||||
.iter()
|
.iter()
|
||||||
.map(AsRef::as_ref)
|
.map(AsRef::as_ref)
|
||||||
.map(os_str_to_c_string)
|
.map(os_str_to_c_string)
|
||||||
.collect::<Result<_>>()
|
.collect::<Result<_>>()
|
||||||
{
|
.map_err(RunconError::new)?;
|
||||||
Ok(v) => v,
|
|
||||||
Err(r) => return (error_exit_status::ANOTHER_ERROR, r),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut argv: Vec<*const c_char> = Vec::with_capacity(arguments.len().saturating_add(2));
|
let mut argv: Vec<*const c_char> = Vec::with_capacity(arguments.len().saturating_add(2));
|
||||||
argv.push(c_command.as_ptr());
|
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);
|
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> {
|
fn os_str_to_c_string(s: &OsStr) -> Result<CString> {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue