use std::process;
use nix::sys::signal::Signal;
use nix::sys::signal::{self};
use nix::unistd::Pid;
use ptools::model::stat::ProcState;
fn process_state_str(state: &ProcState) -> &'static str {
match state {
ProcState::Running => "running",
ProcState::Sleeping => "sleeping",
ProcState::Waiting => "uninterruptible sleep",
ProcState::Zombie => "zombie",
ProcState::Stopped => "stopped",
ProcState::Tracing => "tracing stop",
ProcState::Dead => "dead",
ProcState::Wakekill => "wakekill",
ProcState::Waking => "waking",
ProcState::Parked => "parked",
ProcState::Idle => "idle",
ProcState::Other(_) => "unknown state",
}
}
fn run_process(pid: u64) -> bool {
let nix_pid = Pid::from_raw(pid as i32);
let handle = ptools::proc::ProcHandle::from_pid(pid);
match handle.state() {
Err(_) => {
eprintln!("prun: process {pid} does not exist");
return false;
}
Ok(ProcState::Tracing) => {
eprintln!("prun: process {pid} is ptrace-stopped by a debugger; SIGCONT has no effect");
return false;
}
Ok(ProcState::Stopped) => {} Ok(ref state) => {
eprintln!(
"prun: process {} is not stopped ({})",
pid,
process_state_str(state)
);
return false;
}
}
if let Err(e) = signal::kill(nix_pid, Signal::SIGCONT) {
eprintln!("prun: cannot resume {pid}: {e}");
return false;
}
true
}
struct Args {
pid: Vec<u64>,
}
fn print_usage() {
eprintln!("Usage: prun PID...");
eprintln!("Set stopped processes running with SIGCONT. A /proc/pid path may also be used.");
eprintln!();
eprintln!("Options:");
eprintln!(" -h, --help Print help");
eprintln!(" -V, --version Print version");
}
fn parse_args() -> Args {
use lexopt::prelude::*;
let mut args = Args { pid: Vec::new() };
let mut parser = lexopt::Parser::from_env();
while let Some(arg) = parser.next().unwrap_or_else(|e| {
eprintln!("prun: {e}");
process::exit(2);
}) {
match arg {
Short('h') | Long("help") => {
print_usage();
process::exit(0);
}
Short('V') | Long("version") => {
println!("prun {}", env!("CARGO_PKG_VERSION"));
process::exit(0);
}
Value(val) => {
let s = val.to_string_lossy();
match ptools::proc::parse_pid_arg(&s) {
Ok(ptools::proc::PidArg::Pid(pid)) => args.pid.push(pid),
Ok(ptools::proc::PidArg::Skip) => {}
Err(msg) => {
eprintln!("prun: {msg}");
process::exit(2);
}
}
}
_ => {
eprintln!("prun: unexpected argument: {arg:?}");
process::exit(2);
}
}
}
if args.pid.is_empty() {
eprintln!("prun: at least one PID required");
process::exit(2);
}
args
}
fn main() {
ptools::reset_sigpipe();
let args = parse_args();
let mut failed = false;
for &pid in &args.pid {
if !run_process(pid) {
failed = true;
}
}
if failed {
process::exit(1);
}
}