heim_process/process/
mod.rs

1use std::fmt;
2use std::path::PathBuf;
3use std::time::Instant;
4
5use heim_common::prelude::*;
6use heim_common::units::Time;
7
8use crate::{sys, Pid, ProcessResult};
9
10mod command;
11mod cpu_times;
12mod cpu_usage;
13mod memory;
14mod status;
15
16pub use self::command::{Command, CommandIter};
17pub use self::cpu_times::CpuTime;
18pub use self::cpu_usage::CpuUsage;
19pub use self::memory::Memory;
20pub use self::status::Status;
21
22/// System process.
23///
24/// Some extra methods can be found in the [OS extensions](./os/index.html)
25#[derive(Eq, PartialEq, Hash)]
26pub struct Process(sys::Process);
27
28wrap!(Process, sys::Process);
29
30impl Process {
31    /// Returns the process pid.
32    pub fn pid(&self) -> Pid {
33        self.as_ref().pid()
34    }
35
36    /// Returns future which resolves into the process parent pid.
37    pub fn parent_pid(&self) -> impl Future<Output = ProcessResult<Pid>> {
38        self.as_ref().parent_pid()
39    }
40
41    /// Returns future which resolves into the parent [Process].
42    ///
43    /// [Process]: ./struct.Process.html
44    pub fn parent(&self) -> impl Future<Output = ProcessResult<Process>> {
45        self.parent_pid().and_then(get)
46    }
47
48    /// Returns future which resolves into the process name.
49    pub fn name(&self) -> impl Future<Output = ProcessResult<String>> {
50        self.as_ref().name()
51    }
52
53    /// Returns future which resolves into the process executable as an absolute path.
54    pub fn exe(&self) -> impl Future<Output = ProcessResult<PathBuf>> {
55        self.as_ref().exe()
56    }
57
58    /// Returns future which resolves into the process command line.
59    ///
60    /// ## Example
61    ///
62    /// ```rust
63    /// # use heim_process::{self as process, Process, ProcessResult};
64    /// #
65    /// # #[heim_derive::main]
66    /// # async fn main() -> ProcessResult<()> {
67    /// let process = process::current().await?;
68    /// let command = process.command().await?;
69    /// println!("Command line arguments:");
70    /// for arg in &command {
71    ///     println!("{:?}", arg);
72    /// }
73    ///
74    /// # Ok(())
75    /// # }
76    /// ```
77    pub fn command(&self) -> impl Future<Output = ProcessResult<Command>> {
78        self.as_ref().command().map_ok(Into::into)
79    }
80
81    /// Returns future which resolves into the process current working directory.
82    ///
83    /// ## Compatibility
84    ///
85    /// For Windows this method is not implemented yet and will always return an error,
86    /// see [#105](https://github.com/heim-rs/heim/issues/105).
87    pub fn cwd(&self) -> impl Future<Output = ProcessResult<PathBuf>> {
88        self.as_ref().cwd()
89    }
90
91    /// Returns future which resolves into the current process status.
92    pub fn status(&self) -> impl Future<Output = ProcessResult<Status>> {
93        self.as_ref().status()
94    }
95
96    /// Returns future which resolves into the process creation time,
97    /// expressed as a [Time] amount since the UNIX epoch.
98    ///
99    /// [Time]: ../units/type.Time.html
100    pub fn create_time(&self) -> impl Future<Output = ProcessResult<Time>> {
101        self.as_ref().create_time()
102    }
103
104    /// Returns future which resolves into the accumulated process time.
105    pub fn cpu_time(&self) -> impl Future<Output = ProcessResult<CpuTime>> {
106        self.as_ref().cpu_time().map_ok(Into::into)
107    }
108
109    /// Returns future which resolves into the CPU usage measurement.
110    ///
111    /// Returned [`CpuUsage`] struct represents instantaneous CPU usage and does not represent
112    /// any reasonable value by itself.
113    /// It is suggested to wait for a while with help of any async timer
114    /// (for accuracy recommended delay should be at least 100 ms),
115    /// call this method once again and subtract former [`CpuUsage`] from the new one.
116    ///
117    /// Same to any *nix system, calculated CPU usage might exceed 100 %
118    /// if the process is running multiple threads on different CPU cores.
119    ///
120    /// ## Example
121    ///
122    /// ```rust
123    /// # use std::time::Duration;
124    /// # use heim_common::units::ratio;
125    /// # use heim_process::{self as process, Process, ProcessResult};
126    /// #
127    /// # #[heim_derive::main]
128    /// # async fn main() -> ProcessResult<()> {
129    /// let process = process::current().await?;
130    /// let measurement_1 = process.cpu_usage().await?;
131    /// // Or any other async timer at your choice
132    /// futures_timer::Delay::new(Duration::from_millis(100)).await;
133    /// let measurement_2 = process.cpu_usage().await?;
134    ///
135    /// println!("CPU usage: {} %", (measurement_2 - measurement_1).get::<ratio::percent>());
136    /// # Ok(())
137    /// # }
138    /// ```
139    ///
140    /// [`CpuUsage`]: ./struct.CpuUsage.html
141    pub fn cpu_usage(&self) -> impl Future<Output = ProcessResult<CpuUsage>> {
142        self.cpu_time().and_then(|time| {
143            heim_cpu::logical_count()
144                .map_err(Into::into)
145                .map_ok(move |count| CpuUsage {
146                    cpu_count: count,
147                    cpu_time: time,
148                    at: Instant::now(),
149                })
150        })
151    }
152
153    /// Returns future which resolves into the memory information about this process.
154    pub fn memory(&self) -> impl Future<Output = ProcessResult<Memory>> {
155        self.as_ref().memory().map_ok(Into::into)
156    }
157
158    /// Returns future which checks if this `Process` is still running.
159    pub fn is_running(&self) -> impl Future<Output = ProcessResult<bool>> {
160        self.as_ref().is_running()
161    }
162
163    /// Suspend the current process.
164    ///
165    /// Before the signal send, it checks whether process PID has been reused,
166    /// and if it is a case, [`NoSuchProcess`] error will be returned.
167    ///
168    /// ## Compatibility
169    ///
170    /// For *nix systems it sends the `SIGSTOP` signal to process.
171    ///
172    /// [`NoSuchProcess`]: ./enum.ProcessError.html#variant.NoSuchProcess
173    pub fn suspend(&self) -> impl Future<Output = ProcessResult<()>> {
174        self.0.suspend()
175    }
176
177    /// Resume the current process.
178    ///
179    /// Before the signal send, it checks whether process PID has been reused,
180    /// and if it is a case, [`NoSuchProcess`] error will be returned.
181    ///
182    /// ## Compatibility
183    ///
184    /// For *nix systems it sends the `SIGCONT` signal to process.
185    ///
186    /// [`NoSuchProcess`]: ./enum.ProcessError.html#variant.NoSuchProcess
187    pub fn resume(&self) -> impl Future<Output = ProcessResult<()>> {
188        self.0.resume()
189    }
190
191    /// Terminate the current process.
192    ///
193    /// Before the signal send, it checks whether process PID has been reused,
194    /// and if it is a case, [`NoSuchProcess`] error will be returned.
195    ///
196    /// ## Compatibility
197    ///
198    /// For *nix systems it sends the `SIGTERM` signal to process.
199    ///
200    /// For Windows it is an alias for the [`Process::kill`]
201    ///
202    /// [`NoSuchProcess`]: ./enum.ProcessError.html#variant.NoSuchProcess
203    /// [`Process::kill`]: #method.kill
204    pub fn terminate(&self) -> impl Future<Output = ProcessResult<()>> {
205        self.0.terminate()
206    }
207
208    /// Kills the current process.
209    ///
210    /// Before the signal send, it checks whether process PID has been reused,
211    /// and if it is a case, [`NoSuchProcess`] error will be returned.
212    ///
213    /// ## Compatibility
214    ///
215    /// For *nix systems it sends the `SIGKILL` signal to process.
216    ///
217    /// [`TerminateProcess`] function is used for Windows,
218    /// it initiates the termination but does not awaits for completion.
219    ///
220    /// [`NoSuchProcess`]: ./enum.ProcessError.html#variant.NoSuchProcess
221    /// [`TerminateProcess`]: https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-terminateprocess
222    pub fn kill(&self) -> impl Future<Output = ProcessResult<()>> {
223        self.0.kill()
224    }
225}
226
227impl fmt::Debug for Process {
228    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
229        f.debug_struct("Process").field("pid", &self.pid()).finish()
230    }
231}
232
233/// Returns stream which yields currently running processes.
234pub fn processes() -> impl Stream<Item = ProcessResult<Process>> {
235    sys::processes().map_ok(Into::into)
236}
237
238/// Load the process information with `pid` given.
239pub fn get(pid: Pid) -> impl Future<Output = ProcessResult<Process>> {
240    sys::get(pid).map_ok(Into::into)
241}
242
243/// Returns the `Process` matching the currently running program.
244pub fn current() -> impl Future<Output = ProcessResult<Process>> {
245    sys::current().map_ok(Into::into)
246}