mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-08-01 21:47:46 +00:00
timeout: Moved argument parsing to clap
Changed from optparse to clap. None of the logic within timeout has been changed, which could use some refactoring, but that's beyond the scope of this commit.
This commit is contained in:
parent
272c5d8516
commit
cccf89a48c
2 changed files with 129 additions and 74 deletions
|
@ -15,11 +15,13 @@ edition = "2018"
|
||||||
path = "src/timeout.rs"
|
path = "src/timeout.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
clap = "2.33"
|
||||||
getopts = "0.2.18"
|
getopts = "0.2.18"
|
||||||
libc = "0.2.42"
|
libc = "0.2.42"
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["parse_time", "process", "signals"] }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["parse_time", "process", "signals"] }
|
||||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||||
|
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "timeout"
|
name = "timeout"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
|
@ -10,99 +10,152 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate uucore;
|
extern crate uucore;
|
||||||
|
|
||||||
|
extern crate clap;
|
||||||
|
|
||||||
|
use clap::{App, Arg, ArgMatches, AppSettings};
|
||||||
use std::io::ErrorKind;
|
use std::io::ErrorKind;
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use uucore::process::ChildExt;
|
use uucore::process::ChildExt;
|
||||||
|
use uucore::signals::{Signal, signal_by_name_or_value};
|
||||||
|
|
||||||
|
|
||||||
static NAME: &str = "timeout";
|
static NAME: &str = "timeout";
|
||||||
static VERSION: &str = env!("CARGO_PKG_VERSION");
|
static VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
const ERR_EXIT_STATUS: i32 = 125;
|
const ERR_EXIT_STATUS: i32 = 125;
|
||||||
|
|
||||||
|
pub mod options {
|
||||||
|
pub static FOREGROUND: &str = "foreground";
|
||||||
|
pub static KILL_AFTER: &str = "kill-after";
|
||||||
|
pub static SIGNAL: &str = "signal";
|
||||||
|
pub static VERSION: &str = "version";
|
||||||
|
pub static PRESERVE_STATUS: &str = "preserve-status";
|
||||||
|
|
||||||
|
// Positional args.
|
||||||
|
pub static DURATION: &str = "duration";
|
||||||
|
pub static COMMAND: &str = "command";
|
||||||
|
pub static ARGS: &str = "args";
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Config {
|
||||||
|
foreground: bool,
|
||||||
|
kill_after: Option<Duration>,
|
||||||
|
signal: Option<Signal>,
|
||||||
|
version: bool,
|
||||||
|
duration: Duration,
|
||||||
|
preserve_status: bool
|
||||||
|
|
||||||
|
command: String,
|
||||||
|
command_args: &[String]
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
fn from(options: Clap::ArgMatches) -> Config {
|
||||||
|
let timeout_signal = match options.value_of(options::SIGNAL) {
|
||||||
|
Some(signal_) =>
|
||||||
|
{
|
||||||
|
let signal_result = signal_by_name_or_value(&signal_);
|
||||||
|
match signal_result{
|
||||||
|
None => {
|
||||||
|
show_error!("invalid signal '{}'", signal_);
|
||||||
|
return ERR_EXIT_STATUS;
|
||||||
|
},
|
||||||
|
_ => Some(signal_result)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => None
|
||||||
|
};
|
||||||
|
|
||||||
|
let kill_after: Option<Duration> =
|
||||||
|
match options.value_of(options::KILL_AFTER) {
|
||||||
|
Some(time) => Some(uucore::parse_time::from_str(&time)),
|
||||||
|
None => None
|
||||||
|
};
|
||||||
|
|
||||||
|
let duration: Duration = uucore::parse_time::from_str(
|
||||||
|
options.value_of(options::DURATION)
|
||||||
|
);
|
||||||
|
|
||||||
|
let preserve_status: bool = options.is_present(options::PRESERVE_STATUS);
|
||||||
|
|
||||||
|
let command: String = options.value_of(options::COMMAND).to_str();
|
||||||
|
let command_args: &[String] = options.values_of(options::ARGS)
|
||||||
|
.map(|x| x.as_str());
|
||||||
|
|
||||||
|
Config {
|
||||||
|
foreground: options.is_present(options::FOREGROUND),
|
||||||
|
kill_after,
|
||||||
|
signal: timeout_signal,
|
||||||
|
duration,
|
||||||
|
preserve_status,
|
||||||
|
command,
|
||||||
|
command_args
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
let args = args.collect_str();
|
let args = args.collect_str();
|
||||||
|
|
||||||
let program = args[0].clone();
|
let program = args[0].clone();
|
||||||
|
|
||||||
let mut opts = getopts::Options::new();
|
let mut opts = getopts::Options::new();
|
||||||
opts.optflag(
|
|
||||||
"",
|
|
||||||
"preserve-status",
|
|
||||||
"exit with the same status as COMMAND, even when the command times out",
|
|
||||||
);
|
|
||||||
opts.optflag("", "foreground", "when not running timeout directly from a shell prompt, allow COMMAND to read from the TTY and get TTY signals; in this mode, children of COMMAND will not be timed out");
|
|
||||||
opts.optopt("k", "kill-after", "also send a KILL signal if COMMAND is still running this long after the initial signal was sent", "DURATION");
|
|
||||||
opts.optflag("s", "signal", "specify the signal to be sent on timeout; SIGNAL may be a name like 'HUP' or a number; see 'kill -l' for a list of signals");
|
|
||||||
opts.optflag("h", "help", "display this help and exit");
|
|
||||||
opts.optflag("V", "version", "output version information and exit");
|
|
||||||
let matches = match opts.parse(&args[1..]) {
|
|
||||||
Ok(m) => m,
|
|
||||||
Err(f) => crash!(ERR_EXIT_STATUS, "{}", f),
|
|
||||||
};
|
|
||||||
if matches.opt_present("help") {
|
|
||||||
print!(
|
|
||||||
"{} {}
|
|
||||||
|
|
||||||
Usage:
|
let mut app = App::new("timeout")
|
||||||
{} [OPTION] DURATION COMMAND [ARG]...
|
.version(VERSION)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::FOREGROUND)
|
||||||
|
.long(options::FOREGROUND)
|
||||||
|
.help("when not running timeout directly from a shell prompt, allow COMMAND to read from the TTY and get TTY signals; in this mode, children of COMMAND will not be timed out")
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::KILL_AFTER)
|
||||||
|
.short("k")
|
||||||
|
.takes_value(true))
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::PRESERVE_STATUS)
|
||||||
|
.long(options::PRESERVE_STATUS)
|
||||||
|
.help("exit with the same status as COMMAND, even when the command times out")
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::SIGNAL)
|
||||||
|
.short("s")
|
||||||
|
.long(options::SIGNAL)
|
||||||
|
.help("specify the signal to be sent on timeout; SIGNAL may be a name like 'HUP' or a number; see 'kill -l' for a list of signals")
|
||||||
|
.takes_value(true)
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::DURATION)
|
||||||
|
.index(1)
|
||||||
|
.required(true)
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::COMMAND)
|
||||||
|
.index(2)
|
||||||
|
.required(true)
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::ARGS).required(true).multiple(true)
|
||||||
|
)
|
||||||
|
.setting(AppSettings::TrailingVarArg);
|
||||||
|
|
||||||
{}",
|
let matches = app.get_matches_from(args);
|
||||||
NAME,
|
|
||||||
VERSION,
|
|
||||||
program,
|
|
||||||
&opts.usage("Start COMMAND, and kill it if still running after DURATION.")
|
|
||||||
);
|
|
||||||
} else if matches.opt_present("version") {
|
|
||||||
println!("{} {}", NAME, VERSION);
|
|
||||||
} else if matches.free.len() < 2 {
|
|
||||||
show_error!("missing an argument");
|
|
||||||
show_error!("for help, try '{0} --help'", program);
|
|
||||||
return ERR_EXIT_STATUS;
|
|
||||||
} else {
|
|
||||||
let status = matches.opt_present("preserve-status");
|
|
||||||
let foreground = matches.opt_present("foreground");
|
|
||||||
let kill_after = match matches.opt_str("kill-after") {
|
|
||||||
Some(tstr) => match uucore::parse_time::from_str(&tstr) {
|
|
||||||
Ok(time) => time,
|
|
||||||
Err(f) => {
|
|
||||||
show_error!("{}", f);
|
|
||||||
return ERR_EXIT_STATUS;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None => Duration::new(0, 0),
|
|
||||||
};
|
|
||||||
let signal = match matches.opt_str("signal") {
|
|
||||||
Some(sigstr) => match uucore::signals::signal_by_name_or_value(&sigstr) {
|
|
||||||
Some(sig) => sig,
|
|
||||||
None => {
|
|
||||||
show_error!("invalid signal '{}'", sigstr);
|
|
||||||
return ERR_EXIT_STATUS;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None => uucore::signals::signal_by_name_or_value("TERM").unwrap(),
|
|
||||||
};
|
|
||||||
let duration = match uucore::parse_time::from_str(&matches.free[0]) {
|
|
||||||
Ok(time) => time,
|
|
||||||
Err(f) => {
|
|
||||||
show_error!("{}", f);
|
|
||||||
return ERR_EXIT_STATUS;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return timeout(
|
|
||||||
&matches.free[1],
|
|
||||||
&matches.free[2..],
|
|
||||||
duration,
|
|
||||||
signal,
|
|
||||||
kill_after,
|
|
||||||
foreground,
|
|
||||||
status,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
0
|
let config = Config::from(matches);
|
||||||
|
timeout(config.command,
|
||||||
|
config.command_args,
|
||||||
|
config.duration,
|
||||||
|
config.signal,
|
||||||
|
config.kill_after,
|
||||||
|
config.foreground,
|
||||||
|
config.preserve_status
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// TODO: Improve exit codes, and make them consistent with the GNU Coreutil
|
||||||
|
/// exit codes.
|
||||||
|
|
||||||
fn timeout(
|
fn timeout(
|
||||||
cmdname: &str,
|
cmdname: &str,
|
||||||
args: &[String],
|
args: &[String],
|
||||||
|
@ -126,10 +179,10 @@ fn timeout(
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
show_error!("failed to execute process: {}", err);
|
show_error!("failed to execute process: {}", err);
|
||||||
if err.kind() == ErrorKind::NotFound {
|
if err.kind() == ErrorKind::NotFound {
|
||||||
// XXX: not sure which to use
|
// FIXME: not sure which to use
|
||||||
return 127;
|
return 127;
|
||||||
} else {
|
} else {
|
||||||
// XXX: this may not be 100% correct...
|
// FIXME: this may not be 100% correct...
|
||||||
return 126;
|
return 126;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue