diff --git a/src/uu/ps/src/mapping.rs b/src/uu/ps/src/mapping.rs index 521a0d4f..e71e3fb0 100644 --- a/src/uu/ps/src/mapping.rs +++ b/src/uu/ps/src/mapping.rs @@ -26,17 +26,84 @@ pub(crate) fn default_codes() -> Vec { ["pid", "tname", "time", "ucmd"].map(Into::into).to_vec() } +/// Returns the full format codes (for -f flag). +pub(crate) fn full_format_codes() -> Vec { + [ + "uid_hack", "pid", "ppid", "c", "stime", "tname", "time", "cmd", + ] + .map(Into::into) + .to_vec() +} + +/// Returns the extra full format codes (for -F flag). +pub(crate) fn extra_full_format_codes() -> Vec { + [ + "uid", "pid", "ppid", "c", "sz", "rss", "psr", "stime", "tname", "time", "ucmd", + ] + .map(Into::into) + .to_vec() +} + +/// Returns the job format codes (for -j flag). +pub(crate) fn job_format_codes() -> Vec { + ["pid", "pgid", "sid", "tname", "time", "ucmd"] + .map(Into::into) + .to_vec() +} + +/// Returns the default codes with PSR column (for -P flag). +pub(crate) fn default_with_psr_codes() -> Vec { + ["pid", "psr", "tname", "time", "ucmd"] + .map(Into::into) + .to_vec() +} + +/// Returns the signal format codes (for -s flag). +pub(crate) fn signal_format_codes() -> Vec { + [ + "uid", "pid", "pending", "blocked", "ignored", "caught", "stat", "tname", "time", "command", + ] + .map(Into::into) + .to_vec() +} + +/// Returns the user format codes (for -u flag). +pub(crate) fn user_format_codes() -> Vec { + [ + "user", "pid", "%cpu", "%mem", "vsz", "rss", "tname", "stat", "bsdstart", "time", "command", + ] + .map(Into::into) + .to_vec() +} + +/// Returns the virtual memory format codes (for -v flag). +pub(crate) fn vm_format_codes() -> Vec { + [ + "pid", "tname", "stat", "time", "maj_flt", "trs", "drs", "rss", "%mem", "command", + ] + .map(Into::into) + .to_vec() +} + /// Collect mapping from argument pub(crate) fn default_mapping() -> HashMap { let mut mapping = HashMap::new(); let mut append = |code: &str, header: &str| mapping.insert(code.into(), header.into()); - // Those mapping generated from manpage + // This list is mainly generated from both `ps L` output and manpage, + // but some are also apparently undocumented. append("%cpu", "%CPU"); append("%mem", "%MEM"); + append("_left", "LLLLLLLL"); + append("_left2", "L2L2L2L2"); + append("_right", "RRRRRRRR"); + append("_right2", "R2R2R2R2"); + append("_unlimited", "U"); + append("_unlimited2", "U2"); append("ag_id", "AGID"); append("ag_nice", "AGNI"); append("args", "COMMAND"); + append("atime", "TIME"); append("blocked", "BLOCKED"); append("bsdstart", "START"); append("bsdtime", "TIME"); @@ -50,15 +117,20 @@ pub(crate) fn default_mapping() -> HashMap { append("cmd", "CMD"); append("comm", "COMMAND"); append("command", "COMMAND"); + append("context", "CONTEXT"); append("cp", "CP"); + append("cpuid", "CPUID"); append("cputime", "TIME"); append("cputimes", "TIME"); append("cuc", "%CUC"); append("cuu", "%CUU"); + append("docker", "DOCKER"); append("drs", "DRS"); + append("dsiz", "DSIZ"); append("egid", "EGID"); append("egroup", "EGROUP"); append("eip", "EIP"); + append("environ", "ENVIRONM"); append("esp", "ESP"); append("etime", "ELAPSED"); append("etimes", "ELAPSED"); @@ -66,26 +138,42 @@ pub(crate) fn default_mapping() -> HashMap { append("euser", "EUSER"); append("exe", "EXE"); append("f", "F"); + append("fds", "FDS"); append("fgid", "FGID"); append("fgroup", "FGROUP"); append("flag", "F"); append("flags", "F"); append("fname", "COMMAND"); + append("fsgid", "FSGID"); + append("fsgroup", "FSGROUP"); + append("fsuid", "FSUID"); + append("fsuser", "FSUSER"); append("fuid", "FUID"); append("fuser", "FUSER"); append("gid", "GID"); append("group", "GROUP"); + append("htprv", "HTPRV"); + append("htshr", "HTSHR"); append("ignored", "IGNORED"); + append("intpri", "PRI"); append("ipcns", "IPCNS"); append("label", "LABEL"); - append("lstart", "STARTED"); + append("lastcpu", "C"); + append("lim", "LIM"); + append("longtname", "TTY"); append("lsession", "SESSION"); + append("lstart", "STARTED"); append("luid", "LUID"); append("lwp", "LWP"); append("lxc", "LXC"); + append("m_drs", "DRS"); + append("m_size", "SIZE"); + append("m_trs", "TRS"); append("machine", "MACHINE"); - append("maj_flt", "MAJFLT"); - append("min_flt", "MINFLT"); + append("maj_flt", "MAJFL"); + append("majflt", "MAJFLT"); + append("min_flt", "MINFL"); + append("minflt", "MINFLT"); append("mntns", "MNTNS"); append("netns", "NETNS"); append("ni", "NI"); @@ -95,7 +183,11 @@ pub(crate) fn default_mapping() -> HashMap { append("nwchan", "WCHAN"); append("oom", "OOM"); append("oomadj", "OOMADJ"); + append("opri", "PRI"); append("ouid", "OWNER"); + append("pagein", "PAGEIN"); + append("pcap", "PCAP"); + append("pcaps", "PCAPS"); append("pcpu", "%CPU"); append("pending", "PENDING"); append("pgid", "PGID"); @@ -106,6 +198,11 @@ pub(crate) fn default_mapping() -> HashMap { append("policy", "POL"); append("ppid", "PPID"); append("pri", "PRI"); + append("pri_api", "API"); + append("pri_bar", "BAR"); + append("pri_baz", "BAZ"); + append("pri_foo", "FOO"); + append("priority", "PRI"); append("psr", "PSR"); append("pss", "PSS"); append("rbytes", "RBYTES"); @@ -123,11 +220,17 @@ pub(crate) fn default_mapping() -> HashMap { append("sched", "SCH"); append("seat", "SEAT"); append("sess", "SESS"); + append("session", "SESS"); append("sgi_p", "P"); + append("sgi_rss", "RSS"); append("sgid", "SGID"); append("sgroup", "SGROUP"); append("sid", "SID"); append("sig", "PENDING"); + append("sig_block", "BLOCKED"); + append("sig_catch", "CATCHED"); + append("sig_ignore", "IGNORED"); + append("sig_pend", "SIGNAL"); append("sigcatch", "CAUGHT"); append("sigignore", "IGNORED"); append("sigmask", "BLOCKED"); @@ -136,6 +239,7 @@ pub(crate) fn default_mapping() -> HashMap { append("spid", "SPID"); append("stackp", "STACKP"); append("start", "STARTED"); + append("start_stack", "STACKP"); append("start_time", "START"); append("stat", "STAT"); append("state", "S"); @@ -145,7 +249,9 @@ pub(crate) fn default_mapping() -> HashMap { append("supgrp", "SUPGRP"); append("suser", "SUSER"); append("svgid", "SVGID"); + append("svgroup", "SVGROUP"); append("svuid", "SVUID"); + append("svuser", "SVUSER"); append("sz", "SZ"); append("tgid", "TGID"); append("thcount", "THCNT"); @@ -156,16 +262,23 @@ pub(crate) fn default_mapping() -> HashMap { append("tname", "TTY"); append("tpgid", "TPGID"); append("trs", "TRS"); + append("trss", "TRSS"); + append("tsig", "PENDING"); + append("tsiz", "TSIZ"); append("tt", "TT"); append("tty", "TT"); + append("tty4", "TTY"); + append("tty8", "TTY"); append("ucmd", "CMD"); append("ucomm", "COMMAND"); append("uid", "UID"); + append("uid_hack", "UID"); append("uname", "USER"); append("unit", "UNIT"); append("user", "USER"); append("userns", "USERNS"); append("uss", "USS"); + append("util", "C"); append("utsns", "UTSNS"); append("uunit", "UUNIT"); append("vsize", "VSZ"); @@ -174,7 +287,9 @@ pub(crate) fn default_mapping() -> HashMap { append("wcbytes", "WCBYTES"); append("wchan", "WCHAN"); append("wchars", "WCHARS"); + append("wname", "WCHAN"); append("wops", "WOPS"); + append("zone", "ZONE"); mapping } diff --git a/src/uu/ps/src/picker.rs b/src/uu/ps/src/picker.rs index 553bd27e..cfedcc09 100644 --- a/src/uu/ps/src/picker.rs +++ b/src/uu/ps/src/picker.rs @@ -31,7 +31,7 @@ pub(crate) fn collect_pickers( "uid" | "euid" => pickers.push(helper(euid)), "ruid" => pickers.push(helper(ruid)), "suid" => pickers.push(helper(suid)), - "user" | "euser" => pickers.push(helper(euser)), + "uid_hack" | "user" | "euser" => pickers.push(helper(euser)), "ruser" => pickers.push(helper(ruser)), "suser" => pickers.push(helper(suser)), "pgid" => pickers.push(helper(pgid)), diff --git a/src/uu/ps/src/ps.rs b/src/uu/ps/src/ps.rs index 5e6185b8..db05c519 100644 --- a/src/uu/ps/src/ps.rs +++ b/src/uu/ps/src/ps.rs @@ -11,7 +11,11 @@ mod sorting; use clap::crate_version; use clap::{Arg, ArgAction, ArgMatches, Command}; -use mapping::{collect_code_mapping, default_codes, default_mapping}; +use mapping::{ + collect_code_mapping, default_codes, default_mapping, default_with_psr_codes, + extra_full_format_codes, full_format_codes, job_format_codes, signal_format_codes, + user_format_codes, vm_format_codes, +}; use parser::{parser, OptionalKeyValue}; use prettytable::{format::consts::FORMAT_CLEAN, Row, Table}; use std::{cell::RefCell, rc::Rc}; @@ -47,7 +51,21 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { }; // Collect codes with order - let codes = if arg_formats.is_empty() { + let codes = if matches.get_flag("f") { + full_format_codes() + } else if matches.get_flag("F") { + extra_full_format_codes() + } else if matches.get_flag("j") { + job_format_codes() + } else if matches.get_flag("P") { + default_with_psr_codes() + } else if matches.get_flag("s") { + signal_format_codes() + } else if matches.get_flag("u") { + user_format_codes() + } else if matches.get_flag("v") { + vm_format_codes() + } else if arg_formats.is_empty() { default_codes() } else { arg_formats.iter().map(|it| it.key().to_owned()).collect() @@ -164,6 +182,49 @@ pub fn uu_app() -> Command { // .help("processes without controlling ttys") // .allow_hyphen_values(true), ]) + .arg( + Arg::new("f") + .short('f') + .action(ArgAction::SetTrue) + .help("full format listing"), + ) + .arg( + Arg::new("F") + .short('F') + .action(ArgAction::SetTrue) + .help("extra full format listing"), + ) + .arg( + Arg::new("j") + .short('j') + .action(ArgAction::SetTrue) + .help("job format"), + ) + .arg( + Arg::new("P") + .short('P') + .action(ArgAction::SetTrue) + .help("add psr column"), + ) + .arg( + Arg::new("s") + .short('s') + .action(ArgAction::SetTrue) + .help("signal format"), + ) + // TODO: this can also be used with argument to filter by uid + .arg( + Arg::new("u") + .short('u') + .action(ArgAction::SetTrue) + .help("user format"), + ) + .arg( + Arg::new("v") + .short('v') + .action(ArgAction::SetTrue) + .help("virtual memory format"), + ) .arg( Arg::new("format") .short('o') diff --git a/tests/by-util/test_ps.rs b/tests/by-util/test_ps.rs index 32f07980..8eab0cf9 100644 --- a/tests/by-util/test_ps.rs +++ b/tests/by-util/test_ps.rs @@ -21,6 +21,82 @@ fn test_invalid_arg() { new_ucmd!().arg("--definitely-invalid").fails().code_is(1); } +/// Helper function to check that ps output has the correct headers in the correct order +#[cfg(target_os = "linux")] +fn check_header(flag: &str, expected_headers: &[&str]) { + let result = new_ucmd!().arg(flag).succeeds(); + let lines: Vec<&str> = result.stdout_str().lines().collect(); + let headers: Vec<&str> = lines[0].split_whitespace().collect(); + + assert_eq!(headers, expected_headers); +} + +#[test] +#[cfg(target_os = "linux")] +fn test_full_format_listing() { + check_header( + "-f", + &["UID", "PID", "PPID", "C", "STIME", "TTY", "TIME", "CMD"], + ); +} + +#[test] +#[cfg(target_os = "linux")] +fn test_extra_full_format() { + check_header( + "-F", + &[ + "UID", "PID", "PPID", "C", "SZ", "RSS", "PSR", "STIME", "TTY", "TIME", "CMD", + ], + ); +} + +#[test] +#[cfg(target_os = "linux")] +fn test_job_format() { + check_header("-j", &["PID", "PGID", "SID", "TTY", "TIME", "CMD"]); +} + +#[test] +#[cfg(target_os = "linux")] +fn test_psr_format() { + check_header("-P", &["PID", "PSR", "TTY", "TIME", "CMD"]); +} + +#[test] +#[cfg(target_os = "linux")] +fn test_signal_format() { + check_header( + "-s", + &[ + "UID", "PID", "PENDING", "BLOCKED", "IGNORED", "CAUGHT", "STAT", "TTY", "TIME", + "COMMAND", + ], + ); +} + +#[test] +#[cfg(target_os = "linux")] +fn test_user_format() { + check_header( + "-u", + &[ + "USER", "PID", "%CPU", "%MEM", "VSZ", "RSS", "TTY", "STAT", "START", "TIME", "COMMAND", + ], + ); +} + +#[test] +#[cfg(target_os = "linux")] +fn test_virtual_memory_format() { + check_header( + "-v", + &[ + "PID", "TTY", "STAT", "TIME", "MAJFL", "TRS", "DRS", "RSS", "%MEM", "COMMAND", + ], + ); +} + #[test] #[cfg(target_os = "linux")] fn test_code_mapping() {