Merge branch 'main' into ps-fields

This commit is contained in:
Krysztal Huang
2025-08-14 21:10:04 +08:00
committed by GitHub
32 changed files with 1866 additions and 471 deletions
+6 -6
View File
@@ -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
+2 -2
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+5 -2
View File
@@ -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" }
+1 -1
View File
@@ -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
View File
@@ -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(
+1
View File
@@ -13,6 +13,7 @@ version.workspace = true
[dependencies]
uucore = { workspace = true }
clap = { workspace = true }
dirs = { workspace = true }
[lib]
path = "src/pmap.rs"
+194 -26
View File
@@ -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
View File
@@ -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.
)
}
+97
View File
@@ -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"
}
+44 -20
View File
@@ -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"),
],
+26
View File
@@ -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"
+7
View File
@@ -0,0 +1,7 @@
# skill
```
skill [signal] [options] <expression>
```
Report processes matching an expression and send a signal to them.
+1
View File
@@ -0,0 +1 @@
uucore::bin!(uu_skill);
+106
View File
@@ -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
View File
@@ -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 -1
View File
@@ -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!();
+14 -6
View File
@@ -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()
}
+1 -1
View File
@@ -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),
+101
View File
@@ -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