Skip to content

Commit 3447599

Browse files
committedApr 12, 2024
Add search for process feature and general maintenance
1 parent 5872a22 commit 3447599

File tree

6 files changed

+107
-18
lines changed

6 files changed

+107
-18
lines changed
 

‎Cargo.toml

+7-3
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,17 @@ path = "./sample/port-binder/main.rs"
1414
name = "proc-runner"
1515
path = "./sample/proc-runner/main.rs"
1616

17+
[[bin]]
18+
name = "waiter"
19+
path = "./sample/waiter/main.rs"
20+
1721
[dependencies]
1822
netstat2 = "0.9.1"
1923
thiserror = "1"
2024
retry = { version = "2.0.0", optional = true }
2125
tokio = { version = "1", features = ["time"], optional = true }
2226
async-recursion = { version = "1", optional = true }
23-
sysinfo = { version = "0.28.4", optional = true }
27+
sysinfo = { version = "0.30.10", optional = true }
2428
once_cell = { version = "1.17.1", optional = true }
2529

2630
[dev-dependencies]
@@ -40,8 +44,8 @@ async = [
4044
"dep:async-recursion"
4145
]
4246

43-
# Included as a default feature but because sysinfo is relatively heavy-weight to initialise, its behind a feature flag
44-
# to allow it to be disabled if desired.
47+
# Included as a default feature but because sysinfo is relatively heavy-weight to initialise, so it's behind a feature
48+
# flag to allow it to be disabled if desired.
4549
proc = [
4650
"dep:sysinfo",
4751
"dep:once_cell"

‎README.md

+21-3
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,32 @@ let query = PortQuery::new()
1616
query.execute().unwrap();
1717
```
1818

19+
### Find processes by name
20+
21+
```rust no_run
22+
use proc_ctl::{ProcCtlResult, ProcQuery};
23+
24+
fn main() -> ProcCtlResult<()> {
25+
26+
let query = ProcQuery::new()
27+
.process_name("firefox");
28+
29+
let processes = query.list_processes()?;
30+
Ok(())
31+
}
32+
```
33+
1934
### Find the children of a given process
2035

2136
```rust no_run
22-
use proc_ctl::ProcQuery;
37+
use proc_ctl::{ProcCtlResult, ProcQuery};
2338

24-
let query = ProcQuery::new()
39+
fn main() -> ProcCtlResult<()> {
40+
let query = ProcQuery::new()
2541
.process_id(55932) // Get a process ID from somewhere
2642
.expect_min_num_children(1);
2743

28-
query.children().unwrap();
44+
query.children()?;
45+
Ok(())
46+
}
2947
```

‎run_tests.sh

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
set -xue
44

55
cargo test
6+
cargo test --no-default-features --test lib_test
67
cargo test --features resilience
78
cargo test --features async
89
cargo test --all-features

‎sample/waiter/main.rs

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
use std::io::stdin;
2+
3+
fn main() {
4+
println!("Waiting");
5+
stdin().read_line(&mut String::new()).unwrap();
6+
}

‎src/proc_query.rs

+48-11
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1+
use crate::common::{resolve_pid, MaybeHasPid};
2+
use crate::{Pid, ProcCtlError, ProcCtlResult};
13
use std::path::PathBuf;
24
use std::process::Child;
35
use std::sync::Mutex;
4-
// Switch to `use std::sync::OnceLock;` on the next Rust release https://github.com/rust-lang/rust/issues/74465
5-
use crate::common::{resolve_pid, MaybeHasPid};
6-
use crate::{Pid, ProcCtlError, ProcCtlResult};
7-
use once_cell::sync::OnceCell;
8-
use sysinfo::{PidExt, Process, ProcessExt, ProcessRefreshKind, RefreshKind, System, SystemExt};
6+
use std::sync::OnceLock;
7+
use sysinfo::{Process, ProcessRefreshKind, RefreshKind, System};
98

109
/// Information about a process
1110
#[derive(Debug, Clone)]
@@ -15,21 +14,22 @@ pub struct ProcInfo {
1514
/// The command used to launch the process
1615
pub cmd: Vec<String>,
1716
/// The path to the executable the process is running
18-
pub exe: PathBuf,
17+
pub exe: Option<PathBuf>,
1918
/// The process ID
2019
pub pid: Pid,
2120
/// Parent process ID if relevant
2221
pub parent: Option<Pid>,
2322
/// Environment variables available to the process
2423
pub env: Vec<String>,
2524
/// The current working directory of the process
26-
pub cwd: PathBuf,
25+
pub cwd: Option<PathBuf>,
2726
}
2827

2928
/// Get information about a process
3029
#[derive(Debug)]
3130
pub struct ProcQuery {
3231
process_id: Option<Pid>,
32+
name: Option<String>,
3333
min_num_children: Option<usize>,
3434
}
3535

@@ -38,18 +38,27 @@ impl ProcQuery {
3838
pub fn new() -> Self {
3939
ProcQuery {
4040
process_id: None,
41+
name: None,
4142
min_num_children: None,
4243
}
4344
}
4445

4546
/// Set the process ID to match
4647
///
47-
/// Either this function or `process_id_from_child` are required to be called before the query is usable.
48+
/// One of this, [ProcQuery::process_name] or [ProcQuery::process_id_from_child] must be called before the query is usable.
4849
pub fn process_id(mut self, pid: Pid) -> Self {
4950
self.process_id = Some(pid);
5051
self
5152
}
5253

54+
/// Set the process name to match
55+
///
56+
/// One of this, [ProcQuery::process_id] or [ProcQuery::process_id_from_child] must be called before the query is usable.
57+
pub fn process_name(mut self, name: impl AsRef<str>) -> Self {
58+
self.name = Some(name.as_ref().to_string());
59+
self
60+
}
61+
5362
/// Get the process ID of a child process
5463
///
5564
/// Either this function or `process_id` are required to be called before the query is usable.
@@ -63,6 +72,34 @@ impl ProcQuery {
6372
self
6473
}
6574

75+
/// List all processes matching the current filters.
76+
pub fn list_processes(&self) -> ProcCtlResult<Vec<ProcInfo>> {
77+
let mut sys_handle = sys_handle().lock().unwrap();
78+
sys_handle.refresh_processes();
79+
let processes = sys_handle.processes();
80+
let infos: Vec<ProcInfo> = processes
81+
.values()
82+
.filter(|p| {
83+
if let Some(pid) = self.process_id {
84+
if p.pid().as_u32() != pid {
85+
return false;
86+
}
87+
}
88+
89+
if let Some(name) = &self.name {
90+
if p.name() != name {
91+
return false;
92+
}
93+
}
94+
95+
true
96+
})
97+
.map(|p| p.into())
98+
.collect();
99+
100+
Ok(infos)
101+
}
102+
66103
/// Find the children of the selected process
67104
pub fn children(&self) -> ProcCtlResult<Vec<ProcInfo>> {
68105
let pid = resolve_pid(self)?;
@@ -121,7 +158,7 @@ impl ProcQuery {
121158
}
122159

123160
fn sys_handle() -> &'static Mutex<System> {
124-
static SYS_HANDLE: OnceCell<Mutex<System>> = OnceCell::new();
161+
static SYS_HANDLE: OnceLock<Mutex<System>> = OnceLock::new();
125162
SYS_HANDLE.get_or_init(|| {
126163
let mut sys = System::new_with_specifics(
127164
RefreshKind::new().with_processes(ProcessRefreshKind::new()),
@@ -137,11 +174,11 @@ impl From<&Process> for ProcInfo {
137174
ProcInfo {
138175
name: value.name().to_owned(),
139176
cmd: value.cmd().to_owned(),
140-
exe: value.exe().to_owned(),
177+
exe: value.exe().map(|p| p.to_owned()),
141178
pid: value.pid().as_u32() as Pid,
142179
parent: value.parent().map(|p| p.as_u32() as Pid),
143180
env: value.environ().to_owned(),
144-
cwd: value.cwd().to_owned(),
181+
cwd: value.cwd().map(|p| p.to_owned()),
145182
}
146183
}
147184
}

‎tests/lib_test.rs

+24-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use assert_cmd::cargo::CommandCargoExt;
2-
use proc_ctl::{PortQuery, ProcQuery};
2+
use proc_ctl::PortQuery;
33
use retry::delay::Fixed;
44
use retry::retry;
55
use std::process::Command;
@@ -88,9 +88,30 @@ async fn port_query_with_async_retry() {
8888
assert_eq!(1, ports.len());
8989
}
9090

91+
#[cfg(feature = "proc")]
92+
#[test]
93+
fn proc_query_by_name() {
94+
use proc_ctl::ProcQuery;
95+
96+
let mut binder = Command::cargo_bin("waiter").unwrap();
97+
let mut handle = binder.spawn().unwrap();
98+
99+
let query = ProcQuery::new().process_name("waiter".to_string());
100+
101+
let processes = query.list_processes().unwrap();
102+
103+
handle.kill().unwrap();
104+
105+
println!("{:?}", processes);
106+
107+
assert_eq!(1, processes.len());
108+
}
109+
91110
#[cfg(feature = "proc")]
92111
#[test]
93112
fn proc_query_for_children() {
113+
use proc_ctl::ProcQuery;
114+
94115
let binder = Command::cargo_bin("port-binder").unwrap();
95116
let port_binder_path = binder.get_program();
96117

@@ -117,6 +138,7 @@ fn proc_query_for_children() {
117138
#[cfg(all(feature = "proc", feature = "resilience"))]
118139
#[test]
119140
fn proc_query_for_children_with_retry() {
141+
use proc_ctl::ProcQuery;
120142
use std::time::Duration;
121143

122144
let binder = Command::cargo_bin("port-binder").unwrap();
@@ -143,6 +165,7 @@ fn proc_query_for_children_with_retry() {
143165
#[cfg(all(feature = "proc", feature = "async"))]
144166
#[tokio::test]
145167
async fn proc_query_for_children_async_with_retry() {
168+
use proc_ctl::ProcQuery;
146169
use std::time::Duration;
147170

148171
let binder = Command::cargo_bin("port-binder").unwrap();

0 commit comments

Comments
 (0)
Please sign in to comment.