mirror of
https://github.com/uutils/procps.git
synced 2026-06-10 16:14:00 -07:00
pmap: implemented rc options (#456)
* pmap: implemented rc options * pmap: added tests for rc options * fixed lint errors * pmap: updated test_default_rc to run only in CI * Update src/uu/pmap/src/pmap.rs Co-authored-by: Daniel Hofstetter <daniel.hofstetter@42dh.com> * Update src/uu/pmap/src/pmap.rs Co-authored-by: Daniel Hofstetter <daniel.hofstetter@42dh.com> --------- Co-authored-by: Daniel Hofstetter <daniel.hofstetter@42dh.com>
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -13,6 +13,7 @@ version.workspace = true
|
||||
[dependencies]
|
||||
uucore = { workspace = true }
|
||||
clap = { workspace = true }
|
||||
dirs = { workspace = true }
|
||||
|
||||
[lib]
|
||||
path = "src/pmap.rs"
|
||||
|
||||
+105
-11
@@ -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;
|
||||
@@ -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_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:
|
||||
@@ -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')
|
||||
|
||||
@@ -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";
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user