8000 pmap: implemented rc options by estodi · Pull Request #456 · uutils/procps · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

pmap: implemented rc options #456

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jul 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ clap_complete = "4.5.2"
clap_mangen = "0.2.20"
crossterm = "0.29.0"
ctor = "0.4.1"
dirs = "6.0.0"
libc = "0.2.154"
nix = { version = "0.30", default-features = false, features = ["process"] }
phf = "0.12.1"
Expand Down
1 change: 1 addition & 0 deletions src/uu/pmap/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ version.workspace = true
[dependencies]
uucore = { workspace = true }
clap = { workspace = true }
dirs = { workspace = true }

[lib]
path = "src/pmap.rs"
Expand Down
116 changes: 105 additions & 11 deletions src/uu/pmap/src/pmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

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;
Expand Down Expand Up @@ -39,12 +39,61 @@ 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_conf 10000 ig.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:
Expand Down Expand Up @@ -405,36 +454,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')
Expand Down
95 changes: 95 additions & 0 deletions src/uu/pmap/src/pmap_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -200,6 +204,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);
}
Expand Down Expand Up @@ -244,4 +252,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;
1E0A }
}

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"
}
72 changes: 72 additions & 0 deletions tests/by-util/test_pmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,78 @@ fn test_no_args() {
new_ucmd!().fails().code_is(1);
}

#[test]
#[cfg(target_os = "linux")]
fn test_default_rc() {
if !uutests::util::is_ci() {
return;
}

let pid = process::id();
let ts = TestScenario::new(util_name!());

// Fails to read before creating rc file
for arg in ["-c", "--read-rc"] {
ts.ucmd().arg(arg).arg(pid.to_string()).fails().code_is(1);
}

// Create rc file
ts.ucmd().arg("-n").succeeds();

// Fails to create because rc file already exists
for arg in ["-n", "--create-rc"] {
ts.ucmd().arg(arg).fails().code_is(1);
}

// Succeeds to read now
for arg in ["-c", "--read-rc"] {
ts.ucmd().arg(arg).arg(pid.to_string()).succeeds();
}
}

#[test]
#[cfg(target_os = "linux")]
fn test_create_rc_to() {
let ts = TestScenario::new(util_name!());

ts.ucmd().args(&["-N", "pmap_rc_file_name"]).succeeds();

// Fails to create because rc file already exists
for arg in ["-N", "--create-rc-to"] {
ts.ucmd()
.args(&[arg, "pmap_rc_file_name"])
.fails()
.code_is(1);
}
}

#[test]
#[cfg(target_os = "linux")]
fn test_read_rc_from() {
let pid = process::id();
let ts = TestScenario::new(util_name!());

// Fails to read before creating rc file
for arg in ["-C", "--read-rc-from"] {
ts.ucmd()
.args(&[arg, "pmap_rc_file_name"])
.arg(pid.to_string())
.fails()
.code_is(1);
}

// Create rc file
ts.ucmd().args(&["-N", "pmap_rc_file_name"]).succeeds();

// Succeeds to read now
for arg in ["-C", "--read-rc-from"] {
ts.ucmd()
.args(&[arg, "pmap_rc_file_name"])
.arg(pid.to_string())
.succeeds();
}
}

#[test]
#[cfg(target_os = "linux")]
fn test_existing_pid() {
Expand Down
Loading
0