diff --git a/src/uu/pmap/src/pmap.rs b/src/uu/pmap/src/pmap.rs index cc5443c8..9617304e 100644 --- a/src/uu/pmap/src/pmap.rs +++ b/src/uu/pmap/src/pmap.rs @@ -81,13 +81,17 @@ fn parse_cmdline(pid: &str) -> Result { Ok(cmdline.into()) } -fn process_maps(pid: &str, mut process_line: F) -> Result<(), Error> +fn process_maps(pid: &str, header: Option<&str>, mut process_line: F) -> Result<(), Error> where F: FnMut(&MapLine), { let path = format!("/proc/{pid}/maps"); let contents = fs::read_to_string(path)?; + if let Some(header) = header { + println!("{header}"); + } + for line in contents.lines() { let map_line = parse_map_line(line)?; process_line(&map_line); @@ -96,7 +100,7 @@ where Ok(()) } -fn process_smaps(pid: &str, mut process_entry: F) -> Result<(), Error> +fn process_smaps(pid: &str, header: Option<&str>, mut process_entry: F) -> Result<(), Error> where F: FnMut(&SmapEntry), { @@ -104,6 +108,10 @@ where let contents = fs::read_to_string(path)?; let smap_entries = parse_smap_entries(&contents)?; + if let Some(header) = header { + println!("{header}"); + } + for entry in smap_entries { process_entry(&entry); } @@ -114,7 +122,7 @@ where fn output_default_format(pid: &str) -> Result<(), Error> { let mut total = 0; - process_maps(pid, |map_line| { + process_maps(pid, None, |map_line| { println!( "{} {:>6}K {} {}", map_line.address, map_line.size_in_kb, map_line.perms, map_line.mapping @@ -132,22 +140,24 @@ fn output_extended_format(pid: &str) -> Result<(), Error> { let mut total_rss = 0; let mut total_dirty = 0; - println!("Address Kbytes RSS Dirty Mode Mapping"); - - process_smaps(pid, |smap_entry| { - 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, - smap_entry.map_line.mapping - ); - total_mapped += smap_entry.map_line.size_in_kb; - total_rss += smap_entry.rss_in_kb; - total_dirty += smap_entry.shared_dirty_in_kb + smap_entry.private_dirty_in_kb; - })?; + process_smaps( + pid, + Some("Address Kbytes RSS Dirty Mode Mapping"), + |smap_entry| { + 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, + smap_entry.map_line.mapping + ); + total_mapped += smap_entry.map_line.size_in_kb; + total_rss += smap_entry.rss_in_kb; + total_dirty += smap_entry.shared_dirty_in_kb + smap_entry.private_dirty_in_kb; + }, + )?; println!("---------------- ------- ------- ------- "); println!("total kB {total_mapped:>7} {total_rss:>7} {total_dirty:>7}"); @@ -160,28 +170,30 @@ fn output_device_format(pid: &str) -> Result<(), Error> { let mut total_writeable_private = 0; let mut total_shared = 0; - println!("Address Kbytes Mode Offset Device Mapping"); - - process_maps(pid, |map_line| { - println!( - "{} {:>7} {} {} {} {}", - map_line.address, - map_line.size_in_kb, - map_line.perms, - map_line.offset, - map_line.device, - map_line.mapping - ); - total_mapped += map_line.size_in_kb; - - if map_line.perms.writable && !map_line.perms.shared { - total_writeable_private += map_line.size_in_kb; - } + process_maps( + pid, + Some("Address Kbytes Mode Offset Device Mapping"), + |map_line| { + println!( + "{} {:>7} {} {} {} {}", + map_line.address, + map_line.size_in_kb, + map_line.perms, + map_line.offset, + map_line.device, + map_line.mapping + ); + 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.shared { - total_shared += map_line.size_in_kb; - } - })?; + if map_line.perms.shared { + total_shared += map_line.size_in_kb; + } + }, + )?; println!( "mapped: {total_mapped}K writeable/private: {total_writeable_private}K shared: {total_shared}K" diff --git a/tests/by-util/test_pmap.rs b/tests/by-util/test_pmap.rs index 5c196a0f..032cb992 100644 --- a/tests/by-util/test_pmap.rs +++ b/tests/by-util/test_pmap.rs @@ -10,6 +10,8 @@ use regex::Regex; use std::process; const NON_EXISTING_PID: &str = "999999"; +#[cfg(target_os = "linux")] +const INIT_PID: &str = "1"; #[test] fn test_no_args() { @@ -73,6 +75,20 @@ fn test_non_existing_pid() { .no_output(); } +#[test] +#[cfg(target_os = "linux")] +fn test_permission_denied() { + let result = new_ucmd!() + .arg(INIT_PID) + .fails() + .code_is(1) + .no_stderr() + .clone() + .stdout_move_str(); + + assert_cmdline_only(INIT_PID, &result); +} + #[test] #[cfg(target_os = "linux")] fn test_extended() { @@ -89,6 +105,23 @@ fn test_extended() { } } +#[test] +#[cfg(target_os = "linux")] +fn test_extended_permission_denied() { + for arg in ["-x", "--extended"] { + let result = new_ucmd!() + .arg(arg) + .arg(INIT_PID) + .fails() + .code_is(1) + .no_stderr() + .clone() + .stdout_move_str(); + + assert_cmdline_only(INIT_PID, &result); + } +} + #[test] #[cfg(target_os = "linux")] fn test_device() { @@ -105,11 +138,38 @@ fn test_device() { } } +#[test] +#[cfg(target_os = "linux")] +fn test_device_permission_denied() { + for arg in ["-d", "--device"] { + let result = new_ucmd!() + .arg(arg) + .arg(INIT_PID) + .fails() + .code_is(1) + .no_stderr() + .clone() + .stdout_move_str(); + + assert_cmdline_only(INIT_PID, &result); + } +} + #[test] fn test_invalid_arg() { new_ucmd!().arg("--definitely-invalid").fails().code_is(1); } +// Ensure `s` has the following format: +// +// 1234: /some/path +#[cfg(target_os = "linux")] +fn assert_cmdline_only(pid: &str, s: &str) { + let s = s.trim_end(); + let re = Regex::new(&format!("^{pid}: .+[^ ]$")).unwrap(); + assert!(re.is_match(s)); +} + // Ensure `s` has the following format: // // 1234: /some/path