support_kit/service/
service_control.rs

1use service_manager::*;
2use std::ffi::OsString;
3use std::path::PathBuf;
4
5use crate::{Configuration, ServiceControlError};
6
7use super::{ServiceCommand, ServiceName};
8
9pub struct ServiceControl {
10    name: ServiceName,
11    label: ServiceLabel,
12    manager: Box<dyn ServiceManager>,
13}
14
15impl std::fmt::Debug for ServiceControl {
16    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17        f.debug_struct("ServiceControl")
18            .field("name", &self.name)
19            .field("label", &self.label)
20            .field("manager level", &self.manager.level())
21            .field("manager available", &self.manager.available())
22            .field("program", &self.program())
23            .finish()
24    }
25}
26
27impl ServiceControl {
28    pub fn init(config: &Configuration) -> Result<Self, ServiceControlError> {
29        let mut manager = match config.service.service_manager {
30            Some(manager) => <dyn ServiceManager>::target(manager),
31            None => <dyn ServiceManager>::native()?,
32        };
33
34        if !config.service.system {
35            match manager.set_level(ServiceLevel::User) {
36                Ok(_) => {}
37                Err(_) => {
38                    tracing::warn!(
39                        "attempted to set user level service manager but failed, \
40                        continuing at system level."
41                    );
42                }
43            }
44        }
45
46        Ok(Self {
47            name: config.name(),
48            label: config.name().as_default_label()?,
49            manager,
50        })
51    }
52
53    fn program(&self) -> Result<PathBuf, ServiceControlError> {
54        Ok(std::env::current_exe()?)
55    }
56
57    #[tracing::instrument(level = "trace")]
58    pub fn execute(&self, operation: ServiceCommand) -> Result<(), ServiceControlError> {
59        match operation {
60            ServiceCommand::Install(install) => {
61                tracing::trace!(
62                    install_args = ?install,
63                    "installing with args"
64                );
65
66                self.install(self.program()?, install.args)
67            }
68            ServiceCommand::Start => self.start(),
69            ServiceCommand::Stop => self.stop(),
70            ServiceCommand::Uninstall => self.uninstall(),
71        }?;
72
73        Ok(())
74    }
75
76    #[tracing::instrument(level = "trace")]
77    pub fn install(
78        &self,
79        program: PathBuf,
80        args: Vec<OsString>,
81    ) -> Result<(), ServiceControlError> {
82        self.manager.install(ServiceInstallCtx {
83            label: self.label.clone(),
84            program,
85            args,
86            contents: None,
87            username: None,
88            working_directory: None,
89            environment: None,
90        })?;
91
92        Ok(())
93    }
94
95    #[tracing::instrument(level = "trace")]
96    pub fn start(&self) -> Result<(), ServiceControlError> {
97        self.manager.start(ServiceStartCtx {
98            label: self.label.clone(),
99        })?;
100
101        Ok(())
102    }
103
104    #[tracing::instrument(level = "trace")]
105    pub fn stop(&self) -> Result<(), ServiceControlError> {
106        self.manager.stop(ServiceStopCtx {
107            label: self.label.clone(),
108        })?;
109
110        Ok(())
111    }
112
113    #[tracing::instrument(level = "trace")]
114    pub fn uninstall(&self) -> Result<(), ServiceControlError> {
115        self.manager.uninstall(ServiceUninstallCtx {
116            label: self.label.clone(),
117        })?;
118
119        Ok(())
120    }
121}