Skip to main content

hatchery_server/
subservice.rs

1//! Subservice contract and a supervised-process implementation.
2
3use crate::error::ServerError;
4use serde::Serialize;
5use std::process::Stdio;
6use tokio::process::{Child, Command};
7
8#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
9#[serde(rename_all = "kebab-case")]
10pub enum State {
11    Stopped,
12    Running,
13    Failed,
14}
15
16#[derive(Clone, Debug, Serialize)]
17pub struct Status {
18    pub name: &'static str,
19    pub state: State,
20    /// Loopback port the subservice listens on, when it has one.
21    pub port: Option<u16>,
22    pub detail: Option<String>,
23}
24
25/// A unit the server hosts. `on_demand` subservices are never started by the
26/// server itself; they start only through the authenticated control request.
27pub trait Subservice: Send {
28    fn name(&self) -> &'static str;
29    fn on_demand(&self) -> bool {
30        false
31    }
32    fn start(&mut self) -> Result<(), ServerError>;
33    fn stop(&mut self);
34    fn status(&mut self) -> Status;
35}
36
37/// Child-process hosting shared by the harness and memory subservices.
38pub struct ManagedProcess {
39    child: Option<Child>,
40}
41
42impl ManagedProcess {
43    pub fn new() -> Self {
44        Self { child: None }
45    }
46
47    pub fn spawn(&mut self, mut command: Command) -> Result<(), ServerError> {
48        command.stdin(Stdio::null()).kill_on_drop(true);
49        let child = command.spawn()?;
50        self.child = Some(child);
51        Ok(())
52    }
53
54    /// True while the child is alive; reaps it when it has exited.
55    pub fn alive(&mut self) -> bool {
56        match self.child.as_mut().map(Child::try_wait) {
57            Some(Ok(None)) => true,
58            Some(_) => {
59                self.child = None;
60                false
61            }
62            None => false,
63        }
64    }
65
66    /// Ask the child to die; `kill_on_drop` plus the later reap finish the job.
67    pub fn stop(&mut self) {
68        if let Some(mut child) = self.child.take() {
69            let _ = child.start_kill();
70        }
71    }
72}
73
74impl Default for ManagedProcess {
75    fn default() -> Self {
76        Self::new()
77    }
78}