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}