support_kit/service/
service_control.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use service_manager::*;
use std::ffi::OsString;
use std::path::PathBuf;

use crate::{Config, ServiceControlError};

use super::{ServiceCommand, ServiceName};

pub struct ServiceControl {
    name: ServiceName,
    label: ServiceLabel,
    manager: Box<dyn ServiceManager>,
}

impl std::fmt::Debug for ServiceControl {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("ServiceControl")
            .field("name", &self.name)
            .field("label", &self.label)
            .field("manager level", &self.manager.level())
            .field("manager available", &self.manager.available())
            .field("program", &self.program())
            .field("args", &self.args())
            .finish()
    }
}

impl ServiceControl {
    pub fn init(config: &Config) -> Result<Self, ServiceControlError> {
        let mut manager = match config.service.service_manager {
            Some(manager) => <dyn ServiceManager>::target(manager),
            None => <dyn ServiceManager>::native()?,
        };

        if !config.service.system {
            match manager.set_level(ServiceLevel::User) {
                Ok(_) => {}
                Err(_) => {
                    tracing::warn!(
                        "attempted to set user level service manager but failed, \
                        continuing at system level."
                    );
                }
            }
        }

        Ok(Self {
            name: config.name(),
            label: config.name().as_default_label()?,
            manager,
        })
    }

    fn program(&self) -> Result<PathBuf, ServiceControlError> {
        Ok(std::env::current_exe()?)
    }

    fn args(&self) -> Vec<OsString> {
        vec![
            "-n".into(),
            self.name.to_string().into(),
            "server".into(),
            "api".into(),
        ]
    }

    #[tracing::instrument(level = "trace")]
    pub fn execute(&self, operation: ServiceCommand) -> Result<(), ServiceControlError> {
        tracing::trace!(operation = ?operation, "executing operation");
        match operation {
            ServiceCommand::Install => self.install(self.program()?, self.args()),
            ServiceCommand::Start => self.start(),
            ServiceCommand::Stop => self.stop(),
            ServiceCommand::Uninstall => self.uninstall(),
        }?;

        Ok(())
    }

    #[tracing::instrument(level = "trace")]
    pub fn install(
        &self,
        program: PathBuf,
        args: Vec<OsString>,
    ) -> Result<(), ServiceControlError> {
        self.manager.install(ServiceInstallCtx {
            label: self.label.clone(),
            program,
            args,
            contents: None,
            username: None,
            working_directory: None,
            environment: None,
        })?;

        Ok(())
    }

    #[tracing::instrument(level = "trace")]
    pub fn start(&self) -> Result<(), ServiceControlError> {
        self.manager.start(ServiceStartCtx {
            label: self.label.clone(),
        })?;

        Ok(())
    }

    #[tracing::instrument(level = "trace")]
    pub fn stop(&self) -> Result<(), ServiceControlError> {
        self.manager.stop(ServiceStopCtx {
            label: self.label.clone(),
        })?;

        Ok(())
    }

    #[tracing::instrument(level = "trace")]
    pub fn uninstall(&self) -> Result<(), ServiceControlError> {
        self.manager.uninstall(ServiceUninstallCtx {
            label: self.label.clone(),
        })?;

        Ok(())
    }
}