mirror of
https://github.com/uutils/procps.git
synced 2026-06-10 16:14:00 -07:00
Merge branch 'main' into ps-fields
This commit is contained in:
@@ -13,7 +13,7 @@ jobs:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macOS-latest, windows-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- if: ${{ contains(matrix.os, 'ubuntu') }}
|
||||
run: |
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macOS-latest, windows-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- if: ${{ contains(matrix.os, 'ubuntu') }}
|
||||
run: |
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macOS-latest, windows-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- if: ${{ contains(matrix.os, 'ubuntu') }}
|
||||
run: |
|
||||
@@ -52,8 +52,8 @@ jobs:
|
||||
- name: build and test all programs separately
|
||||
shell: bash
|
||||
run: |
|
||||
## TODO: add hugetop and skill
|
||||
programs="free pgrep pidof pidwait pkill pmap ps pwdx slabtop snice sysctl tload top vmstat w watch"
|
||||
## TODO: add hugetop
|
||||
programs="free pgrep pidof pidwait pkill pmap ps pwdx skill slabtop snice sysctl tload top vmstat w watch"
|
||||
for program in $programs; do
|
||||
echo "Building and testing $program"
|
||||
cargo test -p "uu_$program" || exit 1
|
||||
@@ -70,7 +70,7 @@ jobs:
|
||||
- { os: macos-latest , features: macos }
|
||||
- { os: windows-latest , features: windows }
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Initialize workflow variables
|
||||
id: vars
|
||||
shell: bash
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
name: cargo fmt --all -- --check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- run: rustup component add rustfmt
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
- { os: macos-latest }
|
||||
- { os: windows-latest }
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
Generated
+128
-125
File diff suppressed because it is too large
Load Diff
+5
-2
@@ -40,6 +40,7 @@ feat_common_core = [
|
||||
"pmap",
|
||||
"ps",
|
||||
"pwdx",
|
||||
"skill",
|
||||
"slabtop",
|
||||
"snice",
|
||||
"sysctl",
|
||||
@@ -57,7 +58,8 @@ clap = { version = "4.5.4", features = ["wrap_help", "cargo", "env"] }
|
||||
clap_complete = "4.5.2"
|
||||
clap_mangen = "0.2.20"
|
||||
crossterm = "0.29.0"
|
||||
ctor = "0.4.1"
|
||||
ctor = "0.5.0"
|
||||
dirs = "6.0.0"
|
||||
libc = "0.2.154"
|
||||
nix = { version = "0.30", default-features = false, features = ["process"] }
|
||||
phf = "0.12.1"
|
||||
@@ -66,7 +68,7 @@ prettytable-rs = "0.10.0"
|
||||
rand = { version = "0.9.0", features = ["small_rng"] }
|
||||
ratatui = "0.29.0"
|
||||
regex = "1.10.4"
|
||||
sysinfo = "0.35.0"
|
||||
sysinfo = "0.37.0"
|
||||
tempfile = "3.10.1"
|
||||
terminal_size = "0.4.2"
|
||||
textwrap = { version = "0.16.1", features = ["terminal_size"] }
|
||||
@@ -97,6 +99,7 @@ pkill = { optional = true, version = "0.0.1", package = "uu_pkill", path = "src/
|
||||
pmap = { optional = true, version = "0.0.1", package = "uu_pmap", path = "src/uu/pmap" }
|
||||
ps = { optional = true, version = "0.0.1", package = "uu_ps", path = "src/uu/ps" }
|
||||
pwdx = { optional = true, version = "0.0.1", package = "uu_pwdx", path = "src/uu/pwdx" }
|
||||
skill = { optional = true, version = "0.0.1", package = "uu_skill", path = "src/uu/skill" }
|
||||
slabtop = { optional = true, version = "0.0.1", package = "uu_slabtop", path = "src/uu/slabtop" }
|
||||
snice = { optional = true, version = "0.0.1", package = "uu_snice", path = "src/uu/snice" }
|
||||
sysctl = { optional = true, version = "0.0.1", package = "uu_sysctl", path = "src/uu/sysctl" }
|
||||
|
||||
@@ -20,6 +20,7 @@ Ongoing:
|
||||
* `pmap`: Displays the memory map of a process.
|
||||
* `ps`: Displays information about active processes.
|
||||
* `pwdx`: Shows the current working directory of a process.
|
||||
* `skill`: Sends a signal to processes based on criteria like user, terminal, etc.
|
||||
* `slabtop`: Displays detailed kernel slab cache information in real time.
|
||||
* `snice`: Changes the scheduling priority of a running process.
|
||||
* `sysctl`: Read or write kernel parameters at run-time.
|
||||
@@ -31,7 +32,6 @@ Ongoing:
|
||||
|
||||
TODO:
|
||||
* `hugetop`: Report hugepage usage of processes and the system as a whole.
|
||||
* `skill`: Sends a signal to processes based on criteria like user, terminal, etc.
|
||||
|
||||
Elsewhere:
|
||||
|
||||
|
||||
+43
-15
@@ -4,7 +4,7 @@
|
||||
// file that was distributed with this source code.
|
||||
|
||||
// Pid utils
|
||||
use clap::{arg, crate_version, Command};
|
||||
use clap::{arg, crate_version, value_parser, Command};
|
||||
#[cfg(unix)]
|
||||
use nix::{
|
||||
sys::signal::{self, Signal},
|
||||
@@ -48,7 +48,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
} else {
|
||||
let sig = (settings.signal as i32)
|
||||
.try_into()
|
||||
.map_err(|e| std::io::Error::from_raw_os_error(e as i32))?;
|
||||
.map_err(|e| Error::from_raw_os_error(e as i32))?;
|
||||
Some(sig)
|
||||
};
|
||||
|
||||
@@ -56,11 +56,13 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let pids = process_matcher::find_matching_pids(&settings)?;
|
||||
|
||||
// Send signal
|
||||
// TODO: Implement -q
|
||||
#[cfg(unix)]
|
||||
let echo = matches.get_flag("echo");
|
||||
#[cfg(unix)]
|
||||
kill(&pids, sig, echo);
|
||||
{
|
||||
let echo = matches.get_flag("echo");
|
||||
let queue = matches.get_one::<u32>("queue").cloned();
|
||||
|
||||
kill(&pids, sig, queue, echo);
|
||||
}
|
||||
|
||||
if matches.get_flag("count") {
|
||||
println!("{}", pids.len());
|
||||
@@ -71,25 +73,50 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
|
||||
#[cfg(unix)]
|
||||
fn handle_obsolete(args: &mut [String]) {
|
||||
// Sanity check
|
||||
if args.len() > 2 {
|
||||
// Old signal can only be in the first argument position
|
||||
let slice = args[1].as_str();
|
||||
if let Some(signal) = slice.strip_prefix('-') {
|
||||
for arg in &mut args[1..] {
|
||||
if let Some(signal) = arg.strip_prefix('-') {
|
||||
// Check if it is a valid signal
|
||||
let opt_signal = signal_by_name_or_value(signal);
|
||||
if opt_signal.is_some() {
|
||||
// Replace with long option that clap can parse
|
||||
args[1] = format!("--signal={signal}");
|
||||
*arg = format!("--signal={signal}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Not contains in libc
|
||||
#[cfg(target_os = "linux")]
|
||||
extern "C" {
|
||||
fn sigqueue(
|
||||
pid: uucore::libc::pid_t,
|
||||
sig: uucore::libc::c_int,
|
||||
val: uucore::libc::sigval,
|
||||
) -> uucore::libc::c_int;
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn kill(pids: &Vec<ProcessInformation>, sig: Option<Signal>, echo: bool) {
|
||||
#[allow(unused_variables)]
|
||||
fn kill(pids: &Vec<ProcessInformation>, sig: Option<Signal>, queue: Option<u32>, echo: bool) {
|
||||
for pid in pids {
|
||||
if let Err(e) = signal::kill(Pid::from_raw(pid.pid as i32), sig) {
|
||||
#[cfg(target_os = "linux")]
|
||||
let result = if let Some(queue) = queue {
|
||||
let v = unsafe {
|
||||
sigqueue(
|
||||
pid.pid as i32,
|
||||
sig.map_or(0, |s| s as uucore::libc::c_int),
|
||||
uucore::libc::sigval {
|
||||
sival_ptr: queue as usize as *mut uucore::libc::c_void,
|
||||
},
|
||||
)
|
||||
};
|
||||
nix::errno::Errno::result(v).map(drop)
|
||||
} else {
|
||||
signal::kill(Pid::from_raw(pid.pid as i32), sig)
|
||||
};
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
let result = signal::kill(Pid::from_raw(pid.pid as i32), sig);
|
||||
if let Err(e) = result {
|
||||
show!(Error::from_raw_os_error(e as i32)
|
||||
.map_err_context(|| format!("killing pid {} failed", pid.pid)));
|
||||
} else if echo {
|
||||
@@ -111,7 +138,8 @@ pub fn uu_app() -> Command {
|
||||
.args_override_self(true)
|
||||
.args([
|
||||
// arg!(-<sig> "signal to send (either number or name)"),
|
||||
// arg!(-q --queue <value> "integer value to be sent with the signal"),
|
||||
arg!(-q --queue <value> "integer value to be sent with the signal")
|
||||
.value_parser(value_parser!(u32)),
|
||||
arg!(-e --echo "display what is killed"),
|
||||
])
|
||||
.args(process_matcher::clap_args(
|
||||
|
||||
@@ -13,6 +13,7 @@ version.workspace = true
|
||||
[dependencies]
|
||||
uucore = { workspace = true }
|
||||
clap = { workspace = true }
|
||||
dirs = { workspace = true }
|
||||
|
||||
[lib]
|
||||
path = "src/pmap.rs"
|
||||
|
||||
@@ -10,15 +10,44 @@ use std::io::{Error, ErrorKind};
|
||||
// Represents a parsed single line from /proc/<PID>/maps.
|
||||
#[derive(Debug, Clone, Default, PartialEq)]
|
||||
pub struct MapLine {
|
||||
pub address: String,
|
||||
pub address: Address,
|
||||
pub size_in_kb: u64,
|
||||
pub perms: Perms,
|
||||
pub offset: String,
|
||||
pub device: String,
|
||||
pub device: Device,
|
||||
pub inode: u64,
|
||||
pub mapping: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq)]
|
||||
pub struct Address {
|
||||
pub start: String,
|
||||
pub low: u64,
|
||||
pub high: u64,
|
||||
}
|
||||
|
||||
impl fmt::Display for Address {
|
||||
// By default, pads with white spaces.
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{: >16}", self.start)
|
||||
}
|
||||
}
|
||||
|
||||
impl Address {
|
||||
// Format for default, extended option, and device option.
|
||||
// Pads the start address with zero.
|
||||
pub fn zero_pad(&self) -> String {
|
||||
format!("{:0>16}", self.start)
|
||||
}
|
||||
|
||||
// Checks whether an entry's address range overlaps the limits specified by the range option.
|
||||
// Note: Even if a reversed range (high < low) is given, an entry still hits
|
||||
// only if the specified range lies entirely within the entry's address range.
|
||||
pub fn is_within_range(&self, pmap_config: &PmapConfig) -> bool {
|
||||
pmap_config.range_low < self.high && self.low <= pmap_config.range_high
|
||||
}
|
||||
}
|
||||
|
||||
// Represents a set of permissions from the "perms" column of /proc/<PID>/maps.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||
pub struct Perms {
|
||||
@@ -69,6 +98,28 @@ impl Perms {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq)]
|
||||
pub struct Device {
|
||||
pub major: String,
|
||||
pub minor: String,
|
||||
pub width: usize,
|
||||
}
|
||||
|
||||
impl fmt::Display for Device {
|
||||
// By default, does not pad.
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}:{}", self.major, self.minor)
|
||||
}
|
||||
}
|
||||
|
||||
impl Device {
|
||||
// Format for device option.
|
||||
// Pads the device info from /proc/<PID>/maps with zeros and turns AB:CD into 0AB:000CD.
|
||||
pub fn device(&self) -> String {
|
||||
format!("{:0>3}:{:0>5}", self.major, self.minor)
|
||||
}
|
||||
}
|
||||
|
||||
// Parses a single line from /proc/<PID>/maps. See
|
||||
// https://www.kernel.org/doc/html/latest/filesystems/proc.html for details about the expected
|
||||
// format.
|
||||
@@ -90,7 +141,7 @@ pub fn parse_map_line(line: &str) -> Result<MapLine, Error> {
|
||||
let (offset, rest) = rest
|
||||
.split_once(' ')
|
||||
.ok_or_else(|| Error::from(ErrorKind::InvalidData))?;
|
||||
let offset = format!("{offset:0>16}");
|
||||
let offset = format!("{offset:0>8}");
|
||||
|
||||
let (device, rest) = rest
|
||||
.split_once(' ')
|
||||
@@ -116,9 +167,8 @@ pub fn parse_map_line(line: &str) -> Result<MapLine, Error> {
|
||||
})
|
||||
}
|
||||
|
||||
// Returns the start address and the size of the provided memory range. The start address is always
|
||||
// 16-digits and padded with 0, if necessary. The size is in KB.
|
||||
fn parse_address(memory_range: &str) -> Result<(String, u64), Error> {
|
||||
// Returns Address instance and the size of the provided memory range. The size is in KB.
|
||||
fn parse_address(memory_range: &str) -> Result<(Address, u64), Error> {
|
||||
let (start, end) = memory_range
|
||||
.split_once('-')
|
||||
.ok_or_else(|| Error::from(ErrorKind::InvalidData))?;
|
||||
@@ -127,15 +177,26 @@ fn parse_address(memory_range: &str) -> Result<(String, u64), Error> {
|
||||
let high = u64::from_str_radix(end, 16).map_err(|_| Error::from(ErrorKind::InvalidData))?;
|
||||
let size_in_kb = (high - low) / 1024;
|
||||
|
||||
Ok((format!("{start:0>16}"), size_in_kb))
|
||||
Ok((
|
||||
Address {
|
||||
start: start.to_string(),
|
||||
low,
|
||||
high,
|
||||
},
|
||||
size_in_kb,
|
||||
))
|
||||
}
|
||||
|
||||
// Pads the device info from /proc/<PID>/maps with zeros and turns AB:CD into 0AB:000CD.
|
||||
fn parse_device(device: &str) -> Result<String, Error> {
|
||||
// Returns Device instance.
|
||||
fn parse_device(device: &str) -> Result<Device, Error> {
|
||||
let (major, minor) = device
|
||||
.split_once(':')
|
||||
.ok_or_else(|| Error::from(ErrorKind::InvalidData))?;
|
||||
Ok(format!("{major:0>3}:{minor:0>5}"))
|
||||
Ok(Device {
|
||||
major: major.to_string(),
|
||||
minor: minor.to_string(),
|
||||
width: device.len(),
|
||||
})
|
||||
}
|
||||
|
||||
impl MapLine {
|
||||
@@ -174,19 +235,31 @@ mod test {
|
||||
|
||||
fn create_map_line(
|
||||
address: &str,
|
||||
low: u64,
|
||||
high: u64,
|
||||
size_in_kb: u64,
|
||||
perms: Perms,
|
||||
offset: &str,
|
||||
device: &str,
|
||||
major: &str,
|
||||
minor: &str,
|
||||
width: usize,
|
||||
inode: u64,
|
||||
mapping: &str,
|
||||
) -> MapLine {
|
||||
MapLine {
|
||||
address: address.to_string(),
|
||||
address: Address {
|
||||
start: address.to_string(),
|
||||
low,
|
||||
high,
|
||||
},
|
||||
size_in_kb,
|
||||
perms,
|
||||
offset: offset.to_string(),
|
||||
device: device.to_string(),
|
||||
device: Device {
|
||||
major: major.to_string(),
|
||||
minor: minor.to_string(),
|
||||
width,
|
||||
},
|
||||
inode,
|
||||
mapping: mapping.to_string(),
|
||||
}
|
||||
@@ -210,31 +283,31 @@ mod test {
|
||||
fn test_parse_map_line() {
|
||||
let data = [
|
||||
(
|
||||
create_map_line("000062442eb9e000", 16, Perms::from("r--p"), "0000000000000000", "008:00008", 10813151, "/usr/bin/konsole"),
|
||||
create_map_line("62442eb9e000", 0x62442eb9e000, 0x62442eba2000, 16, Perms::from("r--p"), "00000000", "08", "08", 5, 10813151, "/usr/bin/konsole"),
|
||||
"62442eb9e000-62442eba2000 r--p 00000000 08:08 10813151 /usr/bin/konsole"
|
||||
),
|
||||
(
|
||||
create_map_line("000071af50000000", 132, Perms::from("rw-p"), "0000000000000000", "000:00000", 0, ""),
|
||||
create_map_line("71af50000000", 0x71af50000000, 0x71af50021000, 132, Perms::from("rw-p"), "00000000", "00", "00", 5, 0, ""),
|
||||
"71af50000000-71af50021000 rw-p 00000000 00:00 0 "
|
||||
),
|
||||
(
|
||||
create_map_line("00007ffc3f8df000", 132, Perms::from("rw-p"), "0000000000000000", "000:00000", 0, "[stack]"),
|
||||
create_map_line("7ffc3f8df000", 0x7ffc3f8df000, 0x7ffc3f900000, 132, Perms::from("rw-p"), "00000000", "00", "00", 5, 0, "[stack]"),
|
||||
"7ffc3f8df000-7ffc3f900000 rw-p 00000000 00:00 0 [stack]"
|
||||
),
|
||||
(
|
||||
create_map_line("000071af8c9e6000", 16, Perms::from("rw-s"), "0000000105830000", "000:00010", 1075, "anon_inode:i915.gem"),
|
||||
create_map_line("71af8c9e6000", 0x71af8c9e6000, 0x71af8c9ea000, 16, Perms::from("rw-s"), "105830000", "00", "10", 5, 1075, "anon_inode:i915.gem"),
|
||||
"71af8c9e6000-71af8c9ea000 rw-s 105830000 00:10 1075 anon_inode:i915.gem"
|
||||
),
|
||||
(
|
||||
create_map_line("000071af6cf0c000", 3560, Perms::from("rw-s"), "0000000000000000", "000:00001", 256481, "/memfd:wayland-shm (deleted)"),
|
||||
create_map_line("71af6cf0c000", 0x71af6cf0c000, 0x71af6d286000, 3560, Perms::from("rw-s"), "00000000", "00", "01", 5, 256481, "/memfd:wayland-shm (deleted)"),
|
||||
"71af6cf0c000-71af6d286000 rw-s 00000000 00:01 256481 /memfd:wayland-shm (deleted)"
|
||||
),
|
||||
(
|
||||
create_map_line("ffffffffff600000", 4, Perms::from("--xp"), "0000000000000000", "000:00000", 0, "[vsyscall]"),
|
||||
create_map_line("ffffffffff600000", 0xffffffffff600000, 0xffffffffff601000, 4, Perms::from("--xp"), "00000000", "00", "00", 5, 0, "[vsyscall]"),
|
||||
"ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]"
|
||||
),
|
||||
(
|
||||
create_map_line("00005e8187da8000", 24, Perms::from("r--p"), "0000000000000000", "008:00008", 9524160, "/usr/bin/hello world"),
|
||||
create_map_line("5e8187da8000", 0x5e8187da8000, 0x5e8187dae000, 24, Perms::from("r--p"), "00000000", "08", "08", 5, 9524160, "/usr/bin/hello world"),
|
||||
"5e8187da8000-5e8187dae000 r--p 00000000 08:08 9524160 /usr/bin/hello world"
|
||||
),
|
||||
];
|
||||
@@ -251,12 +324,16 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_parse_address() {
|
||||
let (start, size) = parse_address("ffffffffff600000-ffffffffff601000").unwrap();
|
||||
assert_eq!(start, "ffffffffff600000");
|
||||
let (address, size) = parse_address("ffffffffff600000-ffffffffff601000").unwrap();
|
||||
assert_eq!(address.start, "ffffffffff600000");
|
||||
assert_eq!(address.low, 0xffffffffff600000);
|
||||
assert_eq!(address.high, 0xffffffffff601000);
|
||||
assert_eq!(size, 4);
|
||||
|
||||
let (start, size) = parse_address("7ffc4f0c2000-7ffc4f0e3000").unwrap();
|
||||
assert_eq!(start, "00007ffc4f0c2000");
|
||||
let (address, size) = parse_address("7ffc4f0c2000-7ffc4f0e3000").unwrap();
|
||||
assert_eq!(address.start, "7ffc4f0c2000");
|
||||
assert_eq!(address.low, 0x7ffc4f0c2000);
|
||||
assert_eq!(address.high, 0x7ffc4f0e3000);
|
||||
assert_eq!(size, 132);
|
||||
}
|
||||
|
||||
@@ -271,10 +348,101 @@ mod test {
|
||||
assert!(parse_address("ffffffffff600000-zfffffffff601000").is_err());
|
||||
}
|
||||
|
||||
fn limit_address_range_and_assert(address: &Address, low: u64, high: u64, expected: bool) {
|
||||
let mut pmap_config = PmapConfig::default();
|
||||
(pmap_config.range_low, pmap_config.range_high) = (low, high);
|
||||
assert_eq!(
|
||||
address.is_within_range(&pmap_config),
|
||||
expected,
|
||||
"`--range 0x{low:x},0x{high:x}` expected to be {expected} for address 0x{:x},0x{:x}",
|
||||
address.low,
|
||||
address.high,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_limit_address_range() {
|
||||
let low: u64 = 0x71af50000000;
|
||||
let high: u64 = 0x71af50021000;
|
||||
let address = Address {
|
||||
start: "0x71af50000000".to_string(),
|
||||
low,
|
||||
high,
|
||||
};
|
||||
|
||||
limit_address_range_and_assert(&address, 0x0, u64::MAX, true);
|
||||
limit_address_range_and_assert(&address, 0x70000000, 0xffffffffffff, true);
|
||||
|
||||
limit_address_range_and_assert(&address, 0x0, 0x0, false);
|
||||
limit_address_range_and_assert(&address, 0x0, 0x70000000, false);
|
||||
limit_address_range_and_assert(&address, 0x0, low - 1, false);
|
||||
limit_address_range_and_assert(&address, low - 1, low - 1, false);
|
||||
|
||||
limit_address_range_and_assert(&address, 0x0, low, true);
|
||||
limit_address_range_and_assert(&address, low - 1, low, true);
|
||||
limit_address_range_and_assert(&address, low, low, true);
|
||||
|
||||
limit_address_range_and_assert(&address, low - 1, high - 1, true);
|
||||
limit_address_range_and_assert(&address, low - 1, high, true);
|
||||
limit_address_range_and_assert(&address, low - 1, high + 1, true);
|
||||
limit_address_range_and_assert(&address, low, high - 1, true);
|
||||
limit_address_range_and_assert(&address, low, high, true);
|
||||
limit_address_range_and_assert(&address, low, high + 1, true);
|
||||
limit_address_range_and_assert(&address, low + 1, high - 1, true);
|
||||
limit_address_range_and_assert(&address, low + 1, high, true);
|
||||
limit_address_range_and_assert(&address, low + 1, high + 1, true);
|
||||
|
||||
limit_address_range_and_assert(&address, high - 1, high - 1, true);
|
||||
limit_address_range_and_assert(&address, high - 1, high, true);
|
||||
limit_address_range_and_assert(&address, high - 1, u64::MAX, true);
|
||||
|
||||
limit_address_range_and_assert(&address, high, high, false);
|
||||
limit_address_range_and_assert(&address, high, high + 1, false);
|
||||
limit_address_range_and_assert(&address, high, u64::MAX, false);
|
||||
limit_address_range_and_assert(&address, 0xffffffffffff, u64::MAX, false);
|
||||
limit_address_range_and_assert(&address, u64::MAX, u64::MAX, false);
|
||||
|
||||
// Reversed range
|
||||
|
||||
limit_address_range_and_assert(&address, u64::MAX, 0, false);
|
||||
limit_address_range_and_assert(&address, 0xffffffffffff, 0x70000000, false);
|
||||
|
||||
limit_address_range_and_assert(&address, 0x70000000, 0x0, false);
|
||||
limit_address_range_and_assert(&address, low - 1, 0x0, false);
|
||||
limit_address_range_and_assert(&address, low - 1, low - 1, false);
|
||||
|
||||
limit_address_range_and_assert(&address, low, 0x0, false);
|
||||
limit_address_range_and_assert(&address, low, low - 1, false);
|
||||
|
||||
limit_address_range_and_assert(&address, high - 1, low - 1, false);
|
||||
limit_address_range_and_assert(&address, high, low - 1, false);
|
||||
limit_address_range_and_assert(&address, high + 1, low - 1, false);
|
||||
limit_address_range_and_assert(&address, high - 1, low, true);
|
||||
limit_address_range_and_assert(&address, high, low, false);
|
||||
limit_address_range_and_assert(&address, high + 1, low, false);
|
||||
limit_address_range_and_assert(&address, high - 1, low + 1, true);
|
||||
limit_address_range_and_assert(&address, high, low + 1, false);
|
||||
limit_address_range_and_assert(&address, high + 1, low + 1, false);
|
||||
|
||||
limit_address_range_and_assert(&address, high, high - 1, false);
|
||||
limit_address_range_and_assert(&address, u64::MAX, high - 1, false);
|
||||
|
||||
limit_address_range_and_assert(&address, high + 1, high, false);
|
||||
limit_address_range_and_assert(&address, u64::MAX, high, false);
|
||||
limit_address_range_and_assert(&address, u64::MAX, 0xffffffffffff, false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_device() {
|
||||
assert_eq!("012:00034", parse_device("12:34").unwrap());
|
||||
assert_eq!("000:00000", parse_device("00:00").unwrap());
|
||||
assert_eq!("12:34", parse_device("12:34").unwrap().to_string());
|
||||
assert_eq!("00:00", parse_device("00:00").unwrap().to_string());
|
||||
assert_eq!("fe:01", parse_device("fe:01").unwrap().to_string());
|
||||
assert_eq!("103:100", parse_device("103:100").unwrap().to_string());
|
||||
|
||||
assert_eq!("012:00034", parse_device("12:34").unwrap().device());
|
||||
assert_eq!("000:00000", parse_device("00:00").unwrap().device());
|
||||
assert_eq!("0fe:00001", parse_device("fe:01").unwrap().device());
|
||||
assert_eq!("103:00100", parse_device("103:100").unwrap().device());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
+196
-53
@@ -5,12 +5,12 @@
|
||||
|
||||
use clap::{crate_version, Arg, ArgAction, Command};
|
||||
use maps_format_parser::{parse_map_line, MapLine};
|
||||
use pmap_config::{pmap_field_name, PmapConfig};
|
||||
use pmap_config::{create_rc, pmap_field_name, PmapConfig};
|
||||
use smaps_format_parser::{parse_smaps, SmapTable};
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::io::Error;
|
||||
use uucore::error::{set_exit_code, UResult};
|
||||
use std::io::{Error, ErrorKind};
|
||||
use uucore::error::{set_exit_code, UResult, USimpleError};
|
||||
use uucore::{format_usage, help_about, help_usage};
|
||||
|
||||
mod maps_format_parser;
|
||||
@@ -39,18 +39,104 @@ mod options {
|
||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let matches = uu_app().try_get_matches_from(args)?;
|
||||
|
||||
if matches.get_flag(options::CREATE_RC) {
|
||||
let path = pmap_config::get_rc_default_path();
|
||||
if std::fs::exists(&path)? {
|
||||
eprintln!("pmap: the file already exists - delete or rename it first");
|
||||
eprintln!(
|
||||
"pmap: couldn't create {}",
|
||||
pmap_config::get_rc_default_path_str()
|
||||
);
|
||||
set_exit_code(1);
|
||||
} else {
|
||||
create_rc(&path)?;
|
||||
eprintln!(
|
||||
"pmap: {} file successfully created, feel free to edit the content",
|
||||
pmap_config::get_rc_default_path_str()
|
||||
);
|
||||
}
|
||||
return Ok(());
|
||||
} else if let Some(path_str) = matches.get_one::<String>(options::CREATE_RC_TO) {
|
||||
let path = std::path::PathBuf::from(path_str);
|
||||
if std::fs::exists(&path)? {
|
||||
eprintln!("pmap: the file already exists - delete or rename it first");
|
||||
eprintln!("pmap: couldn't create the rc file");
|
||||
set_exit_code(1);
|
||||
} else {
|
||||
create_rc(&path)?;
|
||||
eprintln!("pmap: rc file successfully created, feel free to edit the content");
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut pmap_config = PmapConfig::default();
|
||||
|
||||
if matches.get_flag(options::MORE_EXTENDED) {
|
||||
pmap_config.set_more_extended();
|
||||
} else if matches.get_flag(options::MOST_EXTENDED) {
|
||||
pmap_config.set_most_extended();
|
||||
} else if matches.get_flag(options::READ_RC) {
|
||||
let path = pmap_config::get_rc_default_path();
|
||||
if !std::fs::exists(&path)? {
|
||||
eprintln!(
|
||||
"pmap: couldn't read {}",
|
||||
pmap_config::get_rc_default_path_str()
|
||||
);
|
||||
set_exit_code(1);
|
||||
return Ok(());
|
||||
}
|
||||
pmap_config.read_rc(&path)?;
|
||||
} else if let Some(path) = matches.get_one::<String>(options::READ_RC_FROM) {
|
||||
let path = std::fs::canonicalize(path)?;
|
||||
if !std::fs::exists(&path)? {
|
||||
eprintln!("pmap: couldn't read the rc file");
|
||||
set_exit_code(1);
|
||||
return Ok(());
|
||||
}
|
||||
pmap_config.read_rc(&path)?;
|
||||
}
|
||||
|
||||
// Options independent with field selection:
|
||||
pmap_config.quiet = matches.get_flag(options::QUIET);
|
||||
if matches.get_flag(options::SHOW_PATH) {
|
||||
pmap_config.show_path = true;
|
||||
pmap_config.show_path = matches.get_flag(options::SHOW_PATH);
|
||||
|
||||
if let Some(range) = matches.get_one::<String>(options::RANGE) {
|
||||
match range.matches(',').count() {
|
||||
0 => {
|
||||
let address = u64::from_str_radix(range, 16).map_err(|_| {
|
||||
USimpleError::new(1, format!("failed to parse argument: '{range}'"))
|
||||
})?;
|
||||
pmap_config.range_low = address;
|
||||
pmap_config.range_high = address;
|
||||
}
|
||||
1 => {
|
||||
let (low, high) = range
|
||||
.split_once(',')
|
||||
.ok_or_else(|| Error::from(ErrorKind::InvalidData))?;
|
||||
pmap_config.range_low = if low.is_empty() {
|
||||
0
|
||||
} else {
|
||||
u64::from_str_radix(low, 16).map_err(|_| {
|
||||
USimpleError::new(1, format!("failed to parse argument: '{range}'"))
|
||||
})?
|
||||
};
|
||||
pmap_config.range_high = if high.is_empty() {
|
||||
u64::MAX
|
||||
} else {
|
||||
u64::from_str_radix(high, 16).map_err(|_| {
|
||||
USimpleError::new(1, format!("failed to parse argument: '{range}'"))
|
||||
})?
|
||||
};
|
||||
}
|
||||
_ => {
|
||||
eprintln!("pmap: failed to parse argument: '{range}'");
|
||||
set_exit_code(1);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pmap_config.range_low = 0;
|
||||
pmap_config.range_high = u64::MAX;
|
||||
}
|
||||
|
||||
let pids = matches
|
||||
@@ -133,14 +219,16 @@ fn output_default_format(pid: &str, pmap_config: &PmapConfig) -> Result<(), Erro
|
||||
let mut total = 0;
|
||||
|
||||
process_maps(pid, None, |map_line| {
|
||||
println!(
|
||||
"{} {:>6}K {} {}",
|
||||
map_line.address,
|
||||
map_line.size_in_kb,
|
||||
map_line.perms.mode(),
|
||||
map_line.parse_mapping(pmap_config)
|
||||
);
|
||||
total += map_line.size_in_kb;
|
||||
if map_line.address.is_within_range(pmap_config) {
|
||||
println!(
|
||||
"{} {:>6}K {} {}",
|
||||
map_line.address.zero_pad(),
|
||||
map_line.size_in_kb,
|
||||
map_line.perms.mode(),
|
||||
map_line.parse_mapping(pmap_config)
|
||||
);
|
||||
total += map_line.size_in_kb;
|
||||
}
|
||||
})?;
|
||||
|
||||
if !pmap_config.quiet {
|
||||
@@ -157,25 +245,31 @@ fn output_extended_format(pid: &str, pmap_config: &PmapConfig) -> Result<(), Err
|
||||
println!("Address Kbytes RSS Dirty Mode Mapping");
|
||||
}
|
||||
|
||||
let mut total_size_in_kb = 0;
|
||||
let mut total_rss_in_kb = 0;
|
||||
let mut total_dirty_in_kb = 0;
|
||||
|
||||
for smap_entry in smap_table.entries {
|
||||
println!(
|
||||
"{} {:>7} {:>7} {:>7} {} {}",
|
||||
smap_entry.map_line.address,
|
||||
smap_entry.map_line.size_in_kb,
|
||||
smap_entry.rss_in_kb,
|
||||
smap_entry.shared_dirty_in_kb + smap_entry.private_dirty_in_kb,
|
||||
smap_entry.map_line.perms.mode(),
|
||||
smap_entry.map_line.parse_mapping(pmap_config)
|
||||
);
|
||||
if smap_entry.map_line.address.is_within_range(pmap_config) {
|
||||
println!(
|
||||
"{} {:>7} {:>7} {:>7} {} {}",
|
||||
smap_entry.map_line.address.zero_pad(),
|
||||
smap_entry.map_line.size_in_kb,
|
||||
smap_entry.rss_in_kb,
|
||||
smap_entry.shared_dirty_in_kb + smap_entry.private_dirty_in_kb,
|
||||
smap_entry.map_line.perms.mode(),
|
||||
smap_entry.map_line.parse_mapping(pmap_config)
|
||||
);
|
||||
total_size_in_kb += smap_entry.map_line.size_in_kb;
|
||||
total_rss_in_kb += smap_entry.rss_in_kb;
|
||||
total_dirty_in_kb += smap_entry.shared_dirty_in_kb + smap_entry.private_dirty_in_kb;
|
||||
}
|
||||
}
|
||||
|
||||
if !pmap_config.quiet {
|
||||
println!("---------------- ------- ------- ------- ");
|
||||
println!(
|
||||
"total kB {:>7} {:>7} {:>7}",
|
||||
smap_table.info.total_size_in_kb,
|
||||
smap_table.info.total_rss_in_kb,
|
||||
smap_table.info.total_shared_dirty_in_kb + smap_table.info.total_private_dirty_in_kb,
|
||||
"total kB {total_size_in_kb:>7} {total_rss_in_kb:>7} {total_dirty_in_kb:>7}"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -311,23 +405,25 @@ fn output_device_format(pid: &str, pmap_config: &PmapConfig) -> Result<(), Error
|
||||
None
|
||||
},
|
||||
|map_line| {
|
||||
println!(
|
||||
"{} {:>7} {} {} {} {}",
|
||||
map_line.address,
|
||||
map_line.size_in_kb,
|
||||
map_line.perms.mode(),
|
||||
map_line.offset,
|
||||
map_line.device,
|
||||
map_line.parse_mapping(pmap_config)
|
||||
);
|
||||
total_mapped += map_line.size_in_kb;
|
||||
if map_line.address.is_within_range(pmap_config) {
|
||||
println!(
|
||||
"{} {:>7} {} {:0>16} {} {}",
|
||||
map_line.address.zero_pad(),
|
||||
map_line.size_in_kb,
|
||||
map_line.perms.mode(),
|
||||
map_line.offset,
|
||||
map_line.device.device(),
|
||||
map_line.parse_mapping(pmap_config)
|
||||
);
|
||||
total_mapped += map_line.size_in_kb;
|
||||
|
||||
if map_line.perms.writable && !map_line.perms.shared {
|
||||
total_writeable_private += map_line.size_in_kb;
|
||||
}
|
||||
if map_line.perms.writable && !map_line.perms.shared {
|
||||
total_writeable_private += map_line.size_in_kb;
|
||||
}
|
||||
|
||||
if map_line.perms.shared {
|
||||
total_shared += map_line.size_in_kb;
|
||||
if map_line.perms.shared {
|
||||
total_shared += map_line.size_in_kb;
|
||||
}
|
||||
}
|
||||
},
|
||||
)?;
|
||||
@@ -405,36 +501,81 @@ pub fn uu_app() -> Command {
|
||||
.short('c')
|
||||
.long("read-rc")
|
||||
.help("read the default rc")
|
||||
.action(ArgAction::SetTrue),
|
||||
)
|
||||
.action(ArgAction::SetTrue)
|
||||
.conflicts_with_all([
|
||||
"read-rc-from",
|
||||
"device",
|
||||
"create-rc",
|
||||
"create-rc-to",
|
||||
"extended",
|
||||
"more-extended",
|
||||
"most-extended",
|
||||
]),
|
||||
) // pmap: options -c, -C, -d, -n, -N, -x, -X are mutually exclusive
|
||||
.arg(
|
||||
Arg::new(options::READ_RC_FROM)
|
||||
.short('C')
|
||||
.long("read-rc-from")
|
||||
.num_args(1)
|
||||
.help("read the rc from file"),
|
||||
)
|
||||
.help("read the rc from file")
|
||||
.conflicts_with_all([
|
||||
"read-rc",
|
||||
"device",
|
||||
"create-rc",
|
||||
"create-rc-to",
|
||||
"extended",
|
||||
"more-extended",
|
||||
"most-extended",
|
||||
]),
|
||||
) // pmap: options -c, -C, -d, -n, -N, -x, -X are mutually exclusive
|
||||
.arg(
|
||||
Arg::new(options::CREATE_RC)
|
||||
.short('n')
|
||||
.long("create-rc")
|
||||
.help("create new default rc")
|
||||
.action(ArgAction::SetTrue),
|
||||
)
|
||||
.action(ArgAction::SetTrue)
|
||||
.conflicts_with_all([
|
||||
"read-rc",
|
||||
"read-rc-from",
|
||||
"device",
|
||||
"create-rc-to",
|
||||
"extended",
|
||||
"more-extended",
|
||||
"most-extended",
|
||||
]),
|
||||
) // pmap: options -c, -C, -d, -n, -N, -x, -X are mutually exclusive
|
||||
.arg(
|
||||
Arg::new(options::CREATE_RC_TO)
|
||||
.short('N')
|
||||
.long("create-rc-to")
|
||||
.num_args(1)
|
||||
.help("create new rc to file"),
|
||||
)
|
||||
.help("create new rc to file")
|
||||
.conflicts_with_all([
|
||||
"read-rc",
|
||||
"read-rc-from",
|
||||
"device",
|
||||
"create-rc",
|
||||
"extended",
|
||||
"more-extended",
|
||||
"most-extended",
|
||||
]),
|
||||
) // pmap: options -c, -C, -d, -n, -N, -x, -X are mutually exclusive
|
||||
.arg(
|
||||
Arg::new(options::DEVICE)
|
||||
.short('d')
|
||||
.long("device")
|
||||
.help("show the device format")
|
||||
.action(ArgAction::SetTrue),
|
||||
)
|
||||
.action(ArgAction::SetTrue)
|
||||
.conflicts_with_all([
|
||||
"read-rc",
|
||||
"read-rc-from",
|
||||
"create-rc",
|
||||
"create-rc-to",
|
||||
"extended",
|
||||
"more-extended",
|
||||
"most-extended",
|
||||
]),
|
||||
) // pmap: options -c, -C, -d, -n, -N, -x, -X are mutually exclusive
|
||||
.arg(
|
||||
Arg::new(options::QUIET)
|
||||
.short('q')
|
||||
@@ -453,7 +594,9 @@ pub fn uu_app() -> Command {
|
||||
Arg::new(options::RANGE)
|
||||
.short('A')
|
||||
.long("range")
|
||||
.num_args(1..=2)
|
||||
.help("limit results to the given range"),
|
||||
.num_args(1)
|
||||
.help("limit results to the given range <low>[,<high>]"),
|
||||
// This option applies only to the default, extended, or device formats,
|
||||
// yet it will not raise an error in any other case.
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use dirs::home_dir;
|
||||
use std::io::Error;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub mod pmap_field_name {
|
||||
pub const ADDRESS: &str = "Address";
|
||||
pub const PERM: &str = "Perm";
|
||||
@@ -77,6 +81,8 @@ pub struct PmapConfig {
|
||||
// Misc
|
||||
pub quiet: bool,
|
||||
pub custom_format_enabled: bool,
|
||||
pub range_low: u64,
|
||||
pub range_high: u64,
|
||||
}
|
||||
|
||||
impl PmapConfig {
|
||||
@@ -200,6 +206,10 @@ impl PmapConfig {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enable_field(&mut self, field_name: &str) {
|
||||
self.set_field(field_name, true);
|
||||
}
|
||||
|
||||
pub fn disable_field(&mut self, field_name: &str) {
|
||||
self.set_field(field_name, false);
|
||||
}
|
||||
@@ -244,4 +254,91 @@ impl PmapConfig {
|
||||
self.anon_huge_pages = true;
|
||||
self.vmflags = true;
|
||||
}
|
||||
|
||||
pub fn read_rc(&mut self, path: &PathBuf) -> Result<(), Error> {
|
||||
self.custom_format_enabled = true;
|
||||
|
||||
let contents = std::fs::read_to_string(path)?;
|
||||
|
||||
let mut in_field_display = false;
|
||||
let mut in_mapping = false;
|
||||
|
||||
for line in contents.lines() {
|
||||
let line = line.trim_ascii();
|
||||
if line.starts_with("#") || line.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// The leftmost category on the line is recoginized.
|
||||
if line.starts_with("[Fields Display]") {
|
||||
in_field_display = true;
|
||||
in_mapping = false;
|
||||
continue;
|
||||
} else if line.starts_with("[Mapping]") {
|
||||
in_field_display = false;
|
||||
in_mapping = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if in_field_display {
|
||||
self.enable_field(line);
|
||||
} else if in_mapping && line == "ShowPath" {
|
||||
self.show_path = true;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_rc(path: &PathBuf) -> Result<(), Error> {
|
||||
let contents = "# pmap's Config File\n".to_string()
|
||||
+ "\n"
|
||||
+ "# All the entries are case sensitive.\n"
|
||||
+ "# Unsupported entries are ignored!\n"
|
||||
+ "\n"
|
||||
+ "[Fields Display]\n"
|
||||
+ "\n"
|
||||
+ "# To enable a field uncomment its entry\n"
|
||||
+ "\n"
|
||||
+ "#Perm\n"
|
||||
+ "#Offset\n"
|
||||
+ "#Device\n"
|
||||
+ "#Inode\n"
|
||||
+ "#Size\n"
|
||||
+ "#Rss\n"
|
||||
+ "#Pss\n"
|
||||
+ "#Shared_Clean\n"
|
||||
+ "#Shared_Dirty\n"
|
||||
+ "#Private_Clean\n"
|
||||
+ "#Private_Dirty\n"
|
||||
+ "#Referenced\n"
|
||||
+ "#Anonymous\n"
|
||||
+ "#AnonHugePages\n"
|
||||
+ "#Swap\n"
|
||||
+ "#KernelPageSize\n"
|
||||
+ "#MMUPageSize\n"
|
||||
+ "#Locked\n"
|
||||
+ "#VmFlags\n"
|
||||
+ "#Mapping\n"
|
||||
+ "\n"
|
||||
+ "[Mapping]\n"
|
||||
+ "\n"
|
||||
+ "# to show paths in the mapping column uncomment the following line\n"
|
||||
+ "#ShowPath\n"
|
||||
+ "\n";
|
||||
|
||||
std::fs::write(path, contents)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_rc_default_path() -> PathBuf {
|
||||
let mut path = home_dir().expect("home directory should not be None");
|
||||
path.push(".pmaprc");
|
||||
path
|
||||
}
|
||||
|
||||
pub fn get_rc_default_path_str() -> &'static str {
|
||||
"~/.pmaprc"
|
||||
}
|
||||
|
||||
@@ -40,10 +40,10 @@ pub struct SmapEntry {
|
||||
impl SmapEntry {
|
||||
pub fn get_field(&self, field_name: &str) -> String {
|
||||
match field_name {
|
||||
pmap_field_name::ADDRESS => self.map_line.address.clone(),
|
||||
pmap_field_name::ADDRESS => self.map_line.address.to_string(),
|
||||
pmap_field_name::PERM => self.map_line.perms.to_string(),
|
||||
pmap_field_name::OFFSET => self.map_line.offset.clone(),
|
||||
pmap_field_name::DEVICE => self.map_line.device.clone(),
|
||||
pmap_field_name::DEVICE => self.map_line.device.to_string(),
|
||||
pmap_field_name::INODE => self.map_line.inode.to_string(),
|
||||
pmap_field_name::SIZE => self.map_line.size_in_kb.to_string(),
|
||||
pmap_field_name::KERNEL_PAGE_SIZE => self.kernel_page_size_in_kb.to_string(),
|
||||
@@ -107,6 +107,9 @@ pub struct SmapTableInfo {
|
||||
pub total_thp_eligible: u64,
|
||||
pub total_protection_key: u64,
|
||||
// Width
|
||||
pub offset_width: usize,
|
||||
pub device_width: usize,
|
||||
pub inode_width: usize,
|
||||
pub size_in_kb_width: usize,
|
||||
pub kernel_page_size_in_kb_width: usize,
|
||||
pub mmu_page_size_in_kb_width: usize,
|
||||
@@ -165,6 +168,9 @@ impl Default for SmapTableInfo {
|
||||
total_thp_eligible: 0,
|
||||
total_protection_key: 0,
|
||||
|
||||
device_width: pmap_field_name::DEVICE.len(),
|
||||
offset_width: pmap_field_name::OFFSET.len(),
|
||||
inode_width: pmap_field_name::INODE.len(),
|
||||
size_in_kb_width: pmap_field_name::SIZE.len(),
|
||||
kernel_page_size_in_kb_width: pmap_field_name::KERNEL_PAGE_SIZE.len(),
|
||||
mmu_page_size_in_kb_width: pmap_field_name::MMU_PAGE_SIZE.len(),
|
||||
@@ -200,9 +206,9 @@ impl SmapTableInfo {
|
||||
match field_name {
|
||||
pmap_field_name::ADDRESS => 16, // See maps_format_parser.rs
|
||||
pmap_field_name::PERM => 4, // See maps_format_parser.rs
|
||||
pmap_field_name::OFFSET => 16, // See maps_format_parser.rs
|
||||
pmap_field_name::DEVICE => 9, // See maps_format_parser.rs
|
||||
pmap_field_name::INODE => 10, // See maps_format_parser.rs
|
||||
pmap_field_name::OFFSET => self.offset_width,
|
||||
pmap_field_name::DEVICE => self.device_width,
|
||||
pmap_field_name::INODE => self.inode_width,
|
||||
pmap_field_name::SIZE => self.size_in_kb_width,
|
||||
pmap_field_name::KERNEL_PAGE_SIZE => self.kernel_page_size_in_kb_width,
|
||||
pmap_field_name::MMU_PAGE_SIZE => self.mmu_page_size_in_kb_width,
|
||||
@@ -289,6 +295,12 @@ pub fn parse_smaps(contents: &str) -> Result<SmapTable, Error> {
|
||||
smap_entry = SmapEntry::default();
|
||||
}
|
||||
smap_table.info.total_size_in_kb += map_line.size_in_kb;
|
||||
smap_table.info.offset_width = smap_table.info.offset_width.max(map_line.offset.len());
|
||||
smap_table.info.device_width = smap_table.info.device_width.max(map_line.device.width);
|
||||
smap_table.info.inode_width = smap_table
|
||||
.info
|
||||
.inode_width
|
||||
.max(map_line.inode.to_string().len());
|
||||
smap_entry.map_line = map_line;
|
||||
} else {
|
||||
let (key, val) = line
|
||||
@@ -544,9 +556,13 @@ mod test {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn create_smap_entry(
|
||||
address: &str,
|
||||
low: u64,
|
||||
high: u64,
|
||||
perms: Perms,
|
||||
offset: &str,
|
||||
device: &str,
|
||||
major: &str,
|
||||
minor: &str,
|
||||
width: usize,
|
||||
inode: u64,
|
||||
mapping: &str,
|
||||
size_in_kb: u64,
|
||||
@@ -577,11 +593,19 @@ mod test {
|
||||
) -> SmapEntry {
|
||||
SmapEntry {
|
||||
map_line: MapLine {
|
||||
address: address.to_string(),
|
||||
address: crate::maps_format_parser::Address {
|
||||
start: address.to_string(),
|
||||
low,
|
||||
high,
|
||||
},
|
||||
size_in_kb,
|
||||
perms,
|
||||
offset: offset.to_string(),
|
||||
device: device.to_string(),
|
||||
device: crate::maps_format_parser::Device {
|
||||
major: major.to_string(),
|
||||
minor: minor.to_string(),
|
||||
width,
|
||||
},
|
||||
inode,
|
||||
mapping: mapping.to_string(),
|
||||
},
|
||||
@@ -617,7 +641,7 @@ mod test {
|
||||
let data = [
|
||||
(
|
||||
vec![create_smap_entry(
|
||||
"0000560880413000", Perms::from("r--p"), "0000000000000000", "008:00008", 10813151, "/usr/bin/konsole",
|
||||
"560880413000", 0x560880413000, 0x560880440000, Perms::from("r--p"), "00000000", "08", "08", 5, 10813151, "/usr/bin/konsole",
|
||||
180, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
|
||||
22, 0, "rd mr mw me dw sd")],
|
||||
concat!(
|
||||
@@ -651,7 +675,7 @@ mod test {
|
||||
),
|
||||
(
|
||||
vec![create_smap_entry(
|
||||
"000071af50000000", Perms::from("rw-p"), "0000000000000000", "000:00000", 0, "",
|
||||
"71af50000000", 0x71af50000000, 0x71af50021000, Perms::from("rw-p"), "00000000", "00", "00", 5, 0, "",
|
||||
132, 4, 4, 128, 9, 9, 128, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 2, "rd mr mw me sd")],
|
||||
concat!(
|
||||
@@ -684,7 +708,7 @@ mod test {
|
||||
),
|
||||
(
|
||||
vec![create_smap_entry(
|
||||
"00007ffc3f8df000", Perms::from("rw-p"), "0000000000000000", "000:00000", 0, "[stack]",
|
||||
"7ffc3f8df000", 0x7ffc3f8df000, 0x7ffc3f900000, Perms::from("rw-p"), "00000000", "00", "00", 5, 0, "[stack]",
|
||||
132, 4, 4, 108, 108, 108, 0, 0, 0, 108, 108, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 3, "rd wr mr mw me gd ac")],
|
||||
concat!(
|
||||
@@ -717,7 +741,7 @@ mod test {
|
||||
),
|
||||
(
|
||||
vec![create_smap_entry(
|
||||
"000071af8c9e6000", Perms::from("rw-s"), "0000000105830000", "000:00010", 1075, "anon_inode:i915.gem",
|
||||
"71af8c9e6000", 0x71af8c9e6000, 0x71af8c9ea000, Perms::from("rw-s"), "105830000", "00", "10", 5, 1075, "anon_inode:i915.gem",
|
||||
16, 4, 4, 16, 16, 16, 0, 0, 0, 16, 16, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, "rd wr mr mw me ac sd")],
|
||||
concat!(
|
||||
@@ -749,7 +773,7 @@ mod test {
|
||||
),
|
||||
(
|
||||
vec![create_smap_entry(
|
||||
"000071af6cf0c000", Perms::from("rw-s"), "0000000000000000", "000:00001", 256481, "/memfd:wayland-shm (deleted)",
|
||||
"71af6cf0c000", 0x71af6cf0c000, 0x71af6d286000, Perms::from("rw-s"), "00000000", "00", "01", 5, 256481, "/memfd:wayland-shm (deleted)",
|
||||
3560, 4, 4, 532, 108, 0, 524, 0, 8, 0, 532, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, "rd mr mw me sd")],
|
||||
concat!(
|
||||
@@ -781,7 +805,7 @@ mod test {
|
||||
),
|
||||
(
|
||||
vec![create_smap_entry(
|
||||
"ffffffffff600000", Perms::from("--xp"), "0000000000000000", "000:00000", 0, "[vsyscall]",
|
||||
"ffffffffff600000", 0xffffffffff600000, 0xffffffffff601000, Perms::from("--xp"), "00000000", "00", "00", 5, 0, "[vsyscall]",
|
||||
4, 4, 4, 4, 4, 4, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, "rd wr mr mw me ac sd")],
|
||||
concat!(
|
||||
@@ -813,7 +837,7 @@ mod test {
|
||||
),
|
||||
(
|
||||
vec![create_smap_entry(
|
||||
"00005e8187da8000", Perms::from("r--p"), "0000000000000000", "008:00008", 9524160, "/usr/bin/hello world",
|
||||
"5e8187da8000", 0x5e8187da8000, 0x5e8187dae000, Perms::from("r--p"), "00000000", "08", "08", 5, 9524160, "/usr/bin/hello world",
|
||||
24, 4, 4, 24, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, "rd ex mr mw me sd")],
|
||||
concat!(
|
||||
@@ -846,11 +870,11 @@ mod test {
|
||||
(
|
||||
vec![
|
||||
create_smap_entry(
|
||||
"000071af8c9e6000", Perms::from("rw-s"), "0000000105830000", "000:00010", 1075, "anon_inode:i915.gem",
|
||||
"71af8c9e6000", 0x71af8c9e6000, 0x71af8c9ea000, Perms::from("rw-s"), "105830000", "00", "10", 5, 1075, "anon_inode:i915.gem",
|
||||
16, 4, 4, 16, 16, 16, 0, 0, 0, 16, 16, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, "rd wr mr mw me ac sd"),
|
||||
create_smap_entry(
|
||||
"000071af6cf0c000", Perms::from("rw-s"), "0000000000000000", "000:00001", 256481, "/memfd:wayland-shm (deleted)",
|
||||
"71af6cf0c000", 0x71af6cf0c000, 0x71af6d286000, Perms::from("rw-s"), "00000000", "00", "01", 5, 256481, "/memfd:wayland-shm (deleted)",
|
||||
3560, 4, 4, 532, 108, 0, 524, 0, 8, 0, 532, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, "rd mr mw me sd"),
|
||||
],
|
||||
@@ -909,15 +933,15 @@ mod test {
|
||||
(
|
||||
vec![
|
||||
create_smap_entry(
|
||||
"000071af8c9e6000", Perms::from("rw-s"), "0000000105830000", "000:00010", 1075, "anon_inode:i915.gem",
|
||||
"71af8c9e6000", 0x71af8c9e6000, 0x71af8c9ea000, Perms::from("rw-s"), "105830000", "00", "10", 5, 1075, "anon_inode:i915.gem",
|
||||
16, 4, 4, 16, 16, 16, 0, 0, 0, 16, 16, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 3, "rd wr mr mw me ac sd"),
|
||||
create_smap_entry(
|
||||
"0000560880413000", Perms::from("r--p"), "0000000000000000", "008:00008", 10813151, "/usr/bin/konsole",
|
||||
"560880413000", 0x560880413000, 0x560880440000, Perms::from("r--p"), "00000000", "08", "08", 5, 10813151, "/usr/bin/konsole",
|
||||
180, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, ""),
|
||||
create_smap_entry(
|
||||
"000071af6cf0c000", Perms::from("rw-s"), "0000000000000000", "000:00001", 256481, "/memfd:wayland-shm (deleted)",
|
||||
"71af6cf0c000", 0x71af6cf0c000, 0x71af6d286000, Perms::from("rw-s"), "00000000", "00", "01", 5, 256481, "/memfd:wayland-shm (deleted)",
|
||||
3560, 4, 4, 532, 108, 0, 524, 0, 8, 0, 532, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, "rd mr mw me sd"),
|
||||
],
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
[package]
|
||||
name = "uu_skill"
|
||||
description = "skill - (uutils) send a signal or report process status"
|
||||
repository = "https://github.com/uutils/procps/tree/main/src/uu/skill"
|
||||
authors.workspace = true
|
||||
categories.workspace = true
|
||||
edition.workspace = true
|
||||
homepage.workspace = true
|
||||
keywords.workspace = true
|
||||
license.workspace = true
|
||||
version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
uucore = { workspace = true, features = ["signals"] }
|
||||
clap = { workspace = true }
|
||||
nix = { workspace = true }
|
||||
|
||||
uu_snice = { path = "../snice" }
|
||||
|
||||
|
||||
[lib]
|
||||
path = "src/skill.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "skill"
|
||||
path = "src/main.rs"
|
||||
@@ -0,0 +1,7 @@
|
||||
# skill
|
||||
|
||||
```
|
||||
skill [signal] [options] <expression>
|
||||
```
|
||||
|
||||
Report processes matching an expression and send a signal to them.
|
||||
@@ -0,0 +1 @@
|
||||
uucore::bin!(uu_skill);
|
||||
@@ -0,0 +1,106 @@
|
||||
// This file is part of the uutils procps package.
|
||||
//
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use clap::{arg, crate_version, value_parser, Arg, Command};
|
||||
#[cfg(unix)]
|
||||
use nix::{sys::signal, sys::signal::Signal, unistd::Pid};
|
||||
use uu_snice::{
|
||||
collect_pids, construct_verbose_result, print_signals, process_matcher, ActionResult,
|
||||
};
|
||||
use uucore::error::USimpleError;
|
||||
#[cfg(unix)]
|
||||
use uucore::signals::signal_by_name_or_value;
|
||||
use uucore::{error::UResult, format_usage, help_about, help_usage};
|
||||
|
||||
const ABOUT: &str = help_about!("skill.md");
|
||||
const USAGE: &str = help_usage!("skill.md");
|
||||
|
||||
#[uucore::main]
|
||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let matches = uu_app().try_get_matches_from(args)?;
|
||||
let settings = process_matcher::Settings::try_new(&matches)?;
|
||||
|
||||
// Case0: Print SIGNALS
|
||||
if let Some(display) = &settings.display {
|
||||
print_signals(display);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Case1: Send signal
|
||||
if let Some(targets) = settings.expressions {
|
||||
let pids = collect_pids(&targets);
|
||||
|
||||
#[cfg(unix)]
|
||||
let signal_str = matches.get_one::<String>("signal").cloned();
|
||||
|
||||
#[cfg(unix)]
|
||||
let signal = if let Some(sig) = signal_str {
|
||||
(signal_by_name_or_value(sig.strip_prefix('-').unwrap()).unwrap() as i32).try_into()?
|
||||
} else {
|
||||
Signal::SIGTERM
|
||||
};
|
||||
|
||||
#[cfg(unix)]
|
||||
let results = perform_action(&pids, &signal);
|
||||
#[cfg(not(unix))]
|
||||
let results: Vec<Option<ActionResult>> = Vec::new();
|
||||
|
||||
if results.iter().all(|it| it.is_none()) || results.is_empty() {
|
||||
return Err(USimpleError::new(1, "no process selection criteria"));
|
||||
}
|
||||
|
||||
if settings.verbose {
|
||||
let output = construct_verbose_result(&pids, &results).trim().to_owned();
|
||||
println!("{output}");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn perform_action(pids: &[u32], signal: &Signal) -> Vec<Option<ActionResult>> {
|
||||
pids.iter()
|
||||
.map(|pid| {
|
||||
{
|
||||
Some(match signal::kill(Pid::from_raw(*pid as i32), *signal) {
|
||||
Ok(_) => ActionResult::Success,
|
||||
|
||||
Err(_) => ActionResult::PermissionDenied,
|
||||
})
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
pub fn uu_app() -> Command {
|
||||
Command::new(uucore::util_name())
|
||||
.version(crate_version!())
|
||||
.about(ABOUT)
|
||||
.override_usage(format_usage(USAGE))
|
||||
.infer_long_args(true)
|
||||
.arg_required_else_help(true)
|
||||
.arg(Arg::new("signal"))
|
||||
.args([
|
||||
// arg!(-f --fast "fast mode (not implemented)"),
|
||||
// arg!(-i --interactive "interactive"),
|
||||
arg!(-l --list "list all signal names"),
|
||||
arg!(-L --table "list all signal names in a nice table"),
|
||||
// arg!(-n --"no-action" "do not actually kill processes; just print what would happen"),
|
||||
arg!(-v --verbose "explain what is being done"),
|
||||
// arg!(-w --warnings "enable warnings (not implemented)"),
|
||||
// Expressions
|
||||
arg!(-c --command <command> ... "expression is a command name"),
|
||||
arg!(-p --pid <pid> ... "expression is a process id number")
|
||||
.value_parser(value_parser!(u32)),
|
||||
arg!(-t --tty <tty> ... "expression is a terminal"),
|
||||
arg!(-u --user <username> ... "expression is a username"),
|
||||
// arg!(--ns <PID> "match the processes that belong to the same namespace as <pid>"),
|
||||
// arg!(--nslist <ns> "list which namespaces will be considered for the --ns option.")
|
||||
// .value_delimiter(',')
|
||||
// .value_parser(["ipc", "mnt", "net", "pid", "user", "uts"]),
|
||||
])
|
||||
}
|
||||
+21
-11
@@ -3,25 +3,35 @@
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
fs,
|
||||
io::{Error, ErrorKind},
|
||||
};
|
||||
use std::io::Error;
|
||||
use std::{cmp::Ordering, fs, io::ErrorKind};
|
||||
use uucore::error::{UError, UResult, USimpleError};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct SlabInfo {
|
||||
pub(crate) meta: Vec<String>,
|
||||
pub(crate) data: Vec<(String, Vec<u64>)>,
|
||||
pub struct SlabInfo {
|
||||
pub meta: Vec<String>,
|
||||
pub data: Vec<(String, Vec<u64>)>,
|
||||
}
|
||||
|
||||
impl SlabInfo {
|
||||
// parse slabinfo from /proc/slabinfo
|
||||
// need root permission
|
||||
pub fn new() -> Result<SlabInfo, Error> {
|
||||
let content = fs::read_to_string("/proc/slabinfo")?;
|
||||
pub fn new() -> UResult<SlabInfo> {
|
||||
let error_wrapper = |e: Error| {
|
||||
USimpleError::new(
|
||||
1,
|
||||
format!(
|
||||
"Unable to create slabinfo structure: {}",
|
||||
Box::<dyn UError>::from(e) // We need Display impl of UError
|
||||
),
|
||||
)
|
||||
};
|
||||
|
||||
Self::parse(&content).ok_or(ErrorKind::Unsupported.into())
|
||||
let content = fs::read_to_string("/proc/slabinfo").map_err(error_wrapper)?;
|
||||
|
||||
Self::parse(&content)
|
||||
.ok_or(ErrorKind::Unsupported.into())
|
||||
.map_err(error_wrapper)
|
||||
}
|
||||
|
||||
pub fn parse(content: &str) -> Option<SlabInfo> {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use crate::parse::SlabInfo;
|
||||
pub use crate::parse::SlabInfo;
|
||||
use clap::{arg, crate_version, ArgAction, Command};
|
||||
use uucore::{error::UResult, format_usage, help_about, help_section, help_usage};
|
||||
|
||||
@@ -25,6 +25,8 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
|
||||
let slabinfo = SlabInfo::new()?.sort(*sort_flag, false);
|
||||
|
||||
println!("{slabinfo:?}");
|
||||
|
||||
if matches.get_flag("once") {
|
||||
output_header(&slabinfo);
|
||||
println!();
|
||||
|
||||
@@ -24,7 +24,7 @@ pub(crate) fn users() -> &'static Users {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum SelectedTarget {
|
||||
pub enum SelectedTarget {
|
||||
Command(String),
|
||||
Pid(u32),
|
||||
Tty(Teletype),
|
||||
@@ -93,7 +93,7 @@ impl SelectedTarget {
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) enum ActionResult {
|
||||
pub enum ActionResult {
|
||||
PermissionDenied,
|
||||
Success,
|
||||
}
|
||||
@@ -111,7 +111,7 @@ impl Display for ActionResult {
|
||||
///
|
||||
/// But we don't know if the process of pid are exist, if [None], the process doesn't exist
|
||||
#[cfg(target_os = "linux")]
|
||||
fn set_priority(pid: u32, prio: &Priority) -> Option<ActionResult> {
|
||||
fn set_priority(pid: u32, prio: &Priority, take_action: bool) -> Option<ActionResult> {
|
||||
use libc::{getpriority, setpriority, PRIO_PROCESS};
|
||||
use nix::errno::Errno;
|
||||
|
||||
@@ -136,6 +136,10 @@ fn set_priority(pid: u32, prio: &Priority) -> Option<ActionResult> {
|
||||
prio
|
||||
};
|
||||
|
||||
if !take_action {
|
||||
return Some(ActionResult::Success);
|
||||
}
|
||||
|
||||
let prio = match prio {
|
||||
Priority::Increase(prio) => current_priority + *prio as i32,
|
||||
Priority::Decrease(prio) => current_priority - *prio as i32,
|
||||
@@ -159,11 +163,15 @@ fn set_priority(pid: u32, prio: &Priority) -> Option<ActionResult> {
|
||||
|
||||
// TODO: Implemented this on other platform
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
fn set_priority(_pid: u32, _prio: &Priority) -> Option<ActionResult> {
|
||||
fn set_priority(_pid: u32, _prio: &Priority, _take_action: bool) -> Option<ActionResult> {
|
||||
None
|
||||
}
|
||||
|
||||
pub(crate) fn perform_action(pids: &[u32], prio: &Priority) -> Vec<Option<ActionResult>> {
|
||||
let f = |pid: &u32| set_priority(*pid, prio);
|
||||
pub(crate) fn perform_action(
|
||||
pids: &[u32],
|
||||
prio: &Priority,
|
||||
take_action: bool,
|
||||
) -> Vec<Option<ActionResult>> {
|
||||
let f = |pid: &u32| set_priority(*pid, prio, take_action);
|
||||
pids.iter().map(f).collect()
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ pub enum Error {
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub(crate) enum Priority {
|
||||
pub enum Priority {
|
||||
// The default priority is +4. (snice +4 ...)
|
||||
Increase(u32),
|
||||
Decrease(u32),
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
// This file is part of the uutils procps package.
|
||||
//
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use crate::action::SelectedTarget;
|
||||
use crate::SignalDisplay;
|
||||
use clap::{arg, value_parser, Arg, ArgMatches};
|
||||
use uu_pgrep::process::Teletype;
|
||||
use uucore::error::UResult;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Settings {
|
||||
pub display: Option<SignalDisplay>,
|
||||
pub expressions: Option<Vec<SelectedTarget>>,
|
||||
pub verbose: bool,
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
pub fn try_new(matches: &ArgMatches) -> UResult<Self> {
|
||||
let display = if matches.get_flag("table") {
|
||||
Some(SignalDisplay::Table)
|
||||
} else if matches.get_flag("list") {
|
||||
Some(SignalDisplay::List)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
display,
|
||||
expressions: Self::targets(matches),
|
||||
verbose: matches.get_flag("verbose"),
|
||||
})
|
||||
}
|
||||
|
||||
fn targets(matches: &ArgMatches) -> Option<Vec<SelectedTarget>> {
|
||||
let cmd = matches
|
||||
.get_many::<String>("command")
|
||||
.unwrap_or_default()
|
||||
.map(Into::into)
|
||||
.map(SelectedTarget::Command)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let pid = matches
|
||||
.get_many::<u32>("pid")
|
||||
.unwrap_or_default()
|
||||
.map(Clone::clone)
|
||||
.map(SelectedTarget::Pid)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let tty = matches
|
||||
.get_many::<String>("tty")
|
||||
.unwrap_or_default()
|
||||
.flat_map(|it| Teletype::try_from(it.as_str()))
|
||||
.map(SelectedTarget::Tty)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let user = matches
|
||||
.get_many::<String>("user")
|
||||
.unwrap_or_default()
|
||||
.map(Into::into)
|
||||
.map(SelectedTarget::User)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let collected = cmd
|
||||
.into_iter()
|
||||
.chain(pid)
|
||||
.chain(tty)
|
||||
.chain(user)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if collected.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(collected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
pub fn clap_args() -> Vec<Arg> {
|
||||
vec![
|
||||
// arg!(-f --fast "fast mode (not implemented)"),
|
||||
// arg!(-i --interactive "interactive"),
|
||||
arg!(-l --list "list all signal names"),
|
||||
arg!(-L --table "list all signal names in a nice table"),
|
||||
arg!(-n --"no-action" "do not actually kill processes; just print what would happen"),
|
||||
arg!(-v --verbose "explain what is being done"),
|
||||
// arg!(-w --warnings "enable warnings (not implemented)"),
|
||||
// Expressions
|
||||
arg!(-c --command <command> ... "expression is a command name"),
|
||||
arg!(-p --pid <pid> ... "expression is a process id number")
|
||||
.value_parser(value_parser!(u32)),
|
||||
arg!(-t --tty <tty> ... "expression is a terminal"),
|
||||
arg!(-u --user <username> ... "expression is a username"),
|
||||
// arg!(--ns <PID> "match the processes that belong to the same namespace as <pid>"),
|
||||
// arg!(--nslist <ns> "list which namespaces will be considered for the --ns option.")
|
||||
// .value_delimiter(',')
|
||||
// .value_parser(["ipc", "mnt", "net", "pid", "user", "uts"]),
|
||||
]
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user