Skip to main content

systemd_transient/
reusable.rs

1use crate::error::SystemdTransientError;
2use crate::watch::exit_of;
3use crate::{SystemdTransientManager, TransientUnitHandle, UnitExit, UnitProperty, UnitStatus};
4
5#[derive(Debug)]
6pub struct ReusableTransientUnit {
7  pub(crate) name: String,
8  pub(crate) props: Vec<UnitProperty>,
9  pub(crate) unit_handle: Option<TransientUnitHandle>,
10  pub(crate) systemd_manager: SystemdTransientManager,
11}
12
13impl ReusableTransientUnit {
14  /// The daemon decides whether the unit is running, not the cached handle,
15  /// so a unit that exited on its own can be started again.
16  pub async fn start(&mut self) -> Result<(), SystemdTransientError> {
17    if let Some(handle) = &self.unit_handle
18      && exit_of(&handle.unit.active_state().await?).is_none()
19    {
20      return Err(SystemdTransientError::UnitAlreadyRunning {
21        name: handle.name.clone(),
22      });
23    }
24    // The old handle pins the unit; release it before reusing the name.
25    self.release().await?;
26
27    let handle = self
28      .systemd_manager
29      .start(&self.name, self.props.clone())
30      .await?;
31    self.unit_handle = Some(handle);
32
33    Ok(())
34  }
35
36  /// Takes over an already loaded unit of this name instead of starting
37  /// one, see [`SystemdTransientManager::adopt`]. Returns whether there was
38  /// one; afterwards `status`, `wait_for_exit` and `stop` work on it as after
39  /// `start`. A run this value already holds is kept, and `true` is returned.
40  pub async fn adopt(&mut self) -> Result<bool, SystemdTransientError> {
41    if self.unit_handle.is_none() {
42      self.unit_handle = self.systemd_manager.adopt(&self.name).await?;
43    }
44    Ok(self.unit_handle.is_some())
45  }
46
47  pub async fn stop(&mut self) -> Result<UnitExit, SystemdTransientError> {
48    // Drop the handle only once the stop job succeeded.
49    let exit = self.handle()?.stop().await?;
50    self.release().await?;
51
52    Ok(exit)
53  }
54
55  pub async fn wait_for_exit(&self) -> Result<UnitExit, SystemdTransientError> {
56    self.handle()?.wait_for_exit().await
57  }
58
59  /// Read from the exit watch, without a D-Bus round trip.
60  pub fn status(&self) -> UnitStatus {
61    match &self.unit_handle {
62      None => UnitStatus::Idle,
63      Some(h) => h.watch.current().into(),
64    }
65  }
66
67  /// Drops the reference on the last run without stopping it.
68  pub async fn release(&mut self) -> Result<(), SystemdTransientError> {
69    if let Some(h) = self.unit_handle.take() {
70      h.release().await?;
71    }
72    Ok(())
73  }
74
75  fn handle(&self) -> Result<&TransientUnitHandle, SystemdTransientError> {
76    self
77      .unit_handle
78      .as_ref()
79      .ok_or_else(|| SystemdTransientError::UnitNotRunning {
80        name: self.name.clone(),
81      })
82  }
83}