use std::ffi::OsString;
use std::process::exit;
use ptools::proc::ProcHandle;
fn read_cmdline(handle: &ProcHandle) -> std::io::Result<Vec<OsString>> {
let (args, lossy) = handle.read_cmdline().map_err(|e| {
eprintln!("Error reading cmdline for PID {}: {}", handle.pid(), e);
e
})?;
if lossy {
eprintln!(
"warning: cmdline reconstructed from lossy source; \
arguments containing spaces or empty arguments may be wrong",
);
}
Ok(args)
}
fn shell_quote(arg: &str) -> String {
if arg.is_empty() {
return "''".to_string();
}
let is_shell_safe = arg
.bytes()
.all(|b| b.is_ascii_alphanumeric() || b"_.-/@:,".contains(&b));
if is_shell_safe {
arg.to_string()
} else {
format!("'{}'", arg.replace('\'', "'\"'\"'"))
}
}
fn print_args(handle: &ProcHandle) -> std::io::Result<()> {
let args = read_cmdline(handle)?;
ptools::display::print_proc_summary_from(handle);
for (i, arg) in args.iter().enumerate() {
println!("argv[{}]: {}", i, arg.to_string_lossy());
}
Ok(())
}
fn print_cmdline(handle: &ProcHandle) -> std::io::Result<()> {
let args = read_cmdline(handle)?;
let exe = handle.exe().ok();
let mut quoted = Vec::with_capacity(args.len());
for (i, arg) in args.iter().enumerate() {
let display = if i == 0 {
if let Some(ref path) = exe {
path.to_string_lossy().into_owned()
} else {
arg.to_string_lossy().into_owned()
}
} else {
arg.to_string_lossy().into_owned()
};
quoted.push(shell_quote(&display));
}
println!("{}", quoted.join(" "));
Ok(())
}
struct Args {
line: bool,
args: bool,
env: bool,
auxv: bool,
operands: Vec<String>,
}
fn print_usage() {
eprintln!("Usage: pargs [-l] [-a|--args] [-e|--env] [-x|--auxv] [pid | core]...");
eprintln!("Print process arguments, environment variables, or auxiliary vector.");
eprintln!();
eprintln!("Options:");
eprintln!(" -a, --args Print process arguments (default)");
eprintln!(" -e, --env Print process environment variables");
eprintln!(" -l Display arguments as a single command line");
eprintln!(" -x, --auxv Print process auxiliary vector");
eprintln!(" -h, --help Print help");
eprintln!(" -V, --version Print version");
}
fn parse_args() -> Args {
use lexopt::prelude::*;
let mut args = Args {
line: false,
args: false,
env: false,
auxv: false,
operands: Vec::new(),
};
let mut parser = lexopt::Parser::from_env();
while let Some(arg) = parser.next().unwrap_or_else(|e| {
eprintln!("pargs: {e}");
exit(2);
}) {
match arg {
Short('h') | Long("help") => {
print_usage();
exit(0);
}
Short('V') | Long("version") => {
println!("pargs {}", env!("CARGO_PKG_VERSION"));
exit(0);
}
Short('l') => args.line = true,
Short('a') | Long("args") => args.args = true,
Short('e') | Long("env") => args.env = true,
Short('x') | Long("auxv") => args.auxv = true,
Value(val) => {
args.operands.push(val.to_string_lossy().into_owned());
}
_ => {
eprintln!("pargs: unexpected argument: {arg:?}");
exit(2);
}
}
}
if args.line && (args.env || args.auxv) {
eprintln!("pargs: -l is incompatible with -e and -x");
exit(2);
}
if args.operands.is_empty() {
eprintln!("pargs: at least one PID or core required");
exit(2);
}
args
}
fn main() {
ptools::reset_sigpipe();
let args = parse_args();
let want_args = args.args || (!args.env && !args.auxv);
let mut error = false;
let mut first = true;
for operand in &args.operands {
if ptools::proc::is_non_pid_proc_path(operand) {
continue;
}
if !first {
println!();
}
first = false;
let handle = match ptools::proc::resolve_operand(operand) {
Ok(h) => h,
Err(e) => {
eprintln!("pargs: {e}");
error = true;
continue;
}
};
let mut section = false;
if want_args {
if args.line {
if print_cmdline(&handle).is_err() {
error = true;
}
} else if print_args(&handle).is_err() {
error = true;
}
section = true;
}
if args.env {
if section {
println!();
}
if let Err(e) = ptools::display::print_env_from(&handle) {
eprintln!("pargs: {}: {e}", handle.pid());
error = true;
}
section = true;
}
if args.auxv {
if section {
println!();
}
if let Err(e) = ptools::display::print_auxv_from(&handle) {
eprintln!("pargs: {}: {e}", handle.pid());
error = true;
}
}
}
if error {
exit(1);
}
}