chj_unix_util/
processes.rs1use std::{
2 borrow::Cow,
3 path::{Path, PathBuf},
4 process::Command,
5};
6
7use anyhow::{anyhow, bail, Context, Result};
8use nix::unistd::Pid;
9
10#[derive(Debug, Clone)]
11pub enum ProcessSelection {
12 All,
13 User(Cow<'static, str>),
14}
15
16impl Default for ProcessSelection {
17 fn default() -> Self {
18 ProcessSelection::All
19 }
20}
21
22#[derive(Default)]
23pub struct Processes {
24 pub selection: ProcessSelection,
25 pub ps_path: Option<PathBuf>,
26}
27
28impl Processes {
29 pub fn get_list(&self) -> Result<Vec<Pid>> {
30 let Self { selection, ps_path } = self;
31 let ps: &Path = "ps".as_ref();
32 let ps_path: &Path = ps_path
33 .as_ref()
34 .map(|p| -> &Path { p.as_ref() })
35 .unwrap_or(ps);
36 let mut cmd = Command::new(ps_path);
37 match selection {
38 ProcessSelection::All => {
39 cmd.args(["-e", "-o", "pid="]);
40 }
41 ProcessSelection::User(cow) => {
42 cmd.args(["-u", cow.as_ref(), "-o", "pid="]);
43 }
44 }
45 let output = cmd
46 .output()
47 .with_context(|| anyhow!("running {ps_path:?}"))?;
48
49 if output.status.success() {
50 let s = std::str::from_utf8(&output.stdout)
51 .with_context(|| anyhow!("parsing output from {ps_path:?} as utf8"))?;
52 let mut vec = Vec::new();
53 for line in s.split("\n") {
54 let s = line.trim();
55 if s.is_empty() {
56 continue;
57 }
58 let pid: i32 = s
59 .parse()
60 .with_context(|| anyhow!("parsing {s:?} from {ps_path:?} as an i32"))?;
61 if pid < 0 {
62 bail!("{ps_path:?} gave a negative number: {s:?}")
63 } else {
64 vec.push(Pid::from_raw(pid));
65 }
66 }
67 Ok(vec)
68 } else {
69 bail!("command {ps_path:?} did not exit successfully")
70 }
71 }
72}