service_install/install/init/systemd/
unit.rs

1use std::ffi::OsString;
2use std::path::{Path, PathBuf};
3
4use crate::install::init::{extract_path, COMMENT_PREAMBLE, COMMENT_SUFFIX};
5
6#[derive(Debug, Clone, PartialEq, Eq)]
7pub(crate) struct Unit {
8    body: String,
9    pub(crate) path: PathBuf,
10    pub(crate) file_name: String,
11}
12
13/// The executables location could not be found. It is needed to safely
14/// uninstall.
15#[derive(Debug, thiserror::Error)]
16pub enum FindExeError {
17    #[error("Could not read systemd unit file at: {path}")]
18    ReadingUnit {
19        #[source]
20        err: std::io::Error,
21        path: PathBuf,
22    },
23    #[error("ExecStart (use to find binary) is missing from servic unit at: {0}")]
24    ExecLineMissing(PathBuf),
25    #[error("Path to binary extracted from systemd unit does not lead to a file, path: {0}")]
26    ExecPathNotFile(PathBuf),
27    #[error("Could not un-escape/un-quote the Exec line in the unit file")]
28    Unquoting(#[source] extract_path::unsystemd_quote::UnquoteError),
29}
30
31#[derive(Debug, thiserror::Error)]
32pub enum Error {
33    #[error("File has no file name, can not be a systemd unit")]
34    NoName,
35    #[error("Could not read unit's content: {0}")]
36    FailedToRead(
37        #[from]
38        #[source]
39        std::io::Error,
40    ),
41    #[error("File has a non utf8 file name, that is not supported")]
42    NonUtf8,
43}
44
45impl Unit {
46    pub(crate) fn from_path(path: PathBuf) -> Result<Self, Error> {
47        Ok(Self {
48            body: std::fs::read_to_string(&path)?,
49            file_name: path
50                .file_name()
51                .ok_or(Error::NoName)?
52                .to_str()
53                .ok_or(Error::NonUtf8)?
54                .to_string(),
55            path,
56        })
57    }
58
59    pub(crate) fn exe_path(&self) -> Result<PathBuf, FindExeError> {
60        let exe_path = self
61            .body
62            .lines()
63            .map(str::trim)
64            .find_map(|l| l.strip_prefix("ExecStart="))
65            .map(extract_path::unsystemd_quote::first_segement)
66            .transpose()
67            .map_err(FindExeError::Unquoting)?
68            .ok_or(FindExeError::ExecLineMissing(self.path.clone()))?;
69        let exe_path = Path::new(exe_path.as_ref()).to_path_buf();
70        if exe_path.is_file() {
71            Ok(exe_path)
72        } else {
73            Err(FindExeError::ExecPathNotFile(exe_path))
74        }
75    }
76
77    pub(crate) fn our_service(&self) -> bool {
78        self.body.contains(COMMENT_PREAMBLE) && self.body.contains(COMMENT_SUFFIX)
79    }
80
81    pub(crate) fn has_install(&self) -> bool {
82        self.body.contains("[Install]")
83    }
84
85    pub(crate) fn name(&self) -> OsString {
86        self.path
87            .with_extension("")
88            .file_name()
89            .expect("Checked in Unit::from_path")
90            .to_os_string()
91    }
92}