pipelight_exec/process/
mod.rs

1// Tests
2
3mod finder;
4mod run;
5
6// Re-export
7pub use finder::Finder;
8
9use bon::{bon, builder};
10
11// Unix process manipulation
12use sysinfo::get_current_pid;
13use sysinfo::{ProcessRefreshKind, ProcessesToUpdate, System};
14
15use serde::{Deserialize, Serialize};
16use uuid::Uuid;
17// Struct
18use crate::{Io, State};
19
20/**
21* A struct that stores the process attributes for further access and manipulation.
22*/
23#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
24pub struct Runner {
25    uuid: Uuid,
26    term: bool,
27    background: bool,
28    detach: bool,
29    orphan: bool,
30    fs: bool,
31}
32impl Default for Runner {
33    fn default() -> Self {
34        Runner {
35            uuid: Uuid::new_v4(),
36            term: false,
37            background: false,
38            detach: false,
39            orphan: false,
40            fs: false,
41        }
42    }
43}
44
45impl Runner {
46    fn new(uuid: Uuid) -> Self {
47        Runner {
48            uuid,
49            term: false,
50            background: false,
51            detach: false,
52            orphan: false,
53            fs: false,
54        }
55    }
56}
57/**
58* A struct that stores the process attributes for further access and manipulation.
59*
60* Example:
61*
62* ```rust
63* # use pipelight_exec::Process;
64* # use miette::Report;
65*
66* let proc = Process::new()
67*   .stdin("ls -al")
68*   .fs()
69*   .run()?;
70*
71* let stdout = proc.io.stdout;
72* let stderr = proc.io.stderr;
73*
74* # Ok::<(), Report>(())
75* ```
76*/
77#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
78pub struct Process {
79    pub uuid: Uuid,
80    #[serde(skip_serializing, skip_deserializing)]
81    pub config: Runner,
82    pub pid: Option<i32>,
83    // process parent id
84    pub ppid: Option<i32>,
85    pub gid: Option<i32>,
86    pub sid: Option<i32>,
87    pub state: State,
88    pub io: Io,
89    pub cwd: Option<String>,
90}
91impl Default for Process {
92    /**
93     * Create a process struct without an initial command.
94     */
95    fn default() -> Process {
96        let uuid = Uuid::new_v4();
97        Process {
98            uuid,
99            config: Runner::new(uuid),
100            pid: None,
101            ppid: None,
102            // Add later
103            // pgid: None,
104            gid: None,
105            sid: None,
106            cwd: None,
107            io: Io {
108                uuid,
109                stdin: None,
110                ..Io::default()
111            },
112            state: State::default(),
113        }
114    }
115}
116impl Process {
117    /**
118     * Create a default process struct.
119     */
120    pub fn new() -> Self {
121        Default::default()
122    }
123    pub fn stdin(&mut self, stdin: &str) -> &mut Self {
124        self.io.stdin = Some(stdin.to_owned());
125        self
126    }
127    pub fn term(&mut self) -> &mut Self {
128        self.config.term = true;
129        self
130    }
131    /*
132     * Will spawn the process and not wait for it to return (silent).
133     * However,if the parent is killed, child will be killed.
134     * background != detached
135     *
136     * Usually you want detach AND background.
137     */
138    pub fn background(&mut self) -> &mut Self {
139        self.config.background = true;
140        self
141    }
142
143    pub fn detach(&mut self) -> &mut Self {
144        self.config.detach = true;
145        self
146    }
147    /**
148     * Detach child process from parent but keep in process group.
149     * Killing parent won't kill child but killing parent group will.
150     */
151    pub fn soft_detach(&mut self) -> &mut Self {
152        self.config.detach = true;
153        self
154    }
155    /**
156     * Detach child process from parent and remove from process group
157     * Neither killing parent or parent group will kill the child.
158     *
159     * Must be root to create an orphan process.
160     *
161     */
162    pub fn orphan(&mut self) -> &mut Self {
163        self.config.orphan = true;
164        self
165    }
166    pub fn fs(&mut self) -> &mut Self {
167        self.config.fs = true;
168        self
169    }
170}
171
172impl Process {
173    /**
174     * Get process from Pid
175     */
176    pub fn get_from_pid(pid: &i32) -> Process {
177        let mut s = System::new_all();
178        s.refresh_processes_specifics(
179            ProcessesToUpdate::All,
180            true,
181            ProcessRefreshKind::nothing()
182                .with_cmd(sysinfo::UpdateKind::Always)
183                .with_cwd(sysinfo::UpdateKind::Always)
184                .with_root(sysinfo::UpdateKind::Always)
185                .with_exe(sysinfo::UpdateKind::Always),
186        );
187        let res = s
188            .process(sysinfo::Pid::from_u32(
189                u32::try_from(pid.to_owned()).unwrap(),
190            ))
191            .unwrap()
192            .to_owned();
193
194        res.into()
195    }
196}
197
198/**
199* Convert a rustix::Process struct into a pipelight::Process struct
200*/
201impl From<&sysinfo::Process> for Process {
202    fn from(proc: &sysinfo::Process) -> Process {
203        let mut p = Process {
204            pid: Some(proc.pid().as_u32() as i32),
205            ppid: Some(proc.parent().unwrap().as_u32() as i32),
206            gid: Some(*proc.group_id().unwrap().to_owned() as i32),
207            sid: Some(proc.session_id().unwrap().as_u32() as i32),
208            ..Process::new()
209                .stdin(
210                    &proc
211                        .cmd()
212                        .iter()
213                        .map(|e| e.to_str().unwrap().to_owned())
214                        .collect::<Vec<String>>()
215                        .join(" "),
216                )
217                .to_owned()
218        };
219        if proc.cwd().is_some() {
220            p.cwd = Some(proc.cwd().unwrap().to_str().unwrap().to_owned());
221        }
222        p
223    }
224}