git_policy_cli/
service_route.rs1use super::service_provision::{ProvisionHost, SERVICE_PROGRAM};
17use super::service_server::Bind;
18use super::systemd_dbus::session_bus_path;
19use std::path::{Path, PathBuf};
20
21pub const SOCKET_RELATIVE: &str = "cli-git/service.sock";
23
24static OWN_EXECUTABLE: std::sync::OnceLock<PathBuf> = std::sync::OnceLock::new();
25
26pub fn remember_own_executable(path: &Path) {
30 let _first = OWN_EXECUTABLE.set(path.to_path_buf());
31}
32
33pub fn remembered_own_executable() -> Option<&'static Path> {
35 return OWN_EXECUTABLE.get().map(PathBuf::as_path);
36}
37
38#[derive(Clone, Copy, Debug)]
40pub struct RouteInputs<'a> {
41 pub runtime_dir: Option<&'a Path>,
43 pub bus_address: Option<&'a str>,
45 pub config_home: Option<&'a Path>,
47 pub home: Option<&'a Path>,
49 pub own_executable: Option<&'a Path>,
51 pub real_git: &'a Path,
53}
54
55#[derive(Clone, Debug, Eq, PartialEq)]
57pub enum Route {
58 Earlier,
60 Service {
62 address: Bind,
64 provision: Option<ProvisionHost>,
66 },
67}
68
69pub fn user_unit_dir(config_home: Option<&Path>, home: Option<&Path>) -> Option<PathBuf> {
71 return config_home
72 .map(|directory| { return directory.join("systemd").join("user"); })
73 .or_else(|| { return home.map(|directory| { return directory.join(".config").join("systemd").join("user"); }); });
74}
75
76pub fn choose_route(inputs: &RouteInputs<'_>) -> Route {
87 let Some(runtime_dir) = inputs.runtime_dir else { return Route::Earlier };
88 let socket: PathBuf = runtime_dir.join(SOCKET_RELATIVE);
89 if socket.exists() {
90 return Route::Service { address: Bind::Path(socket), provision: None };
91 }
92 let Some(bus) = session_bus_path(inputs.bus_address, Some(runtime_dir)).filter(|path| { return path.exists(); }) else { return Route::Earlier };
93 let Some(service_binary) = inputs
94 .own_executable
95 .and_then(Path::parent)
96 .map(|directory| { return directory.join(SERVICE_PROGRAM); })
97 .filter(|path| { return path.is_file(); })
98 else {
99 return Route::Earlier;
100 };
101 let host: ProvisionHost = ProvisionHost {
102 runtime_dir: runtime_dir.to_path_buf(),
103 user_unit_dir: user_unit_dir(inputs.config_home, inputs.home),
104 bus,
105 service_binary,
106 git: inputs.real_git.to_path_buf(),
107 };
108 return Route::Service { address: Bind::Path(socket), provision: Some(host) };
109}
110
111#[derive(Clone, Debug, Eq, PartialEq)]
113pub struct StatusReport {
114 pub code: i32,
116 pub stdout: String,
118 pub stderr: String,
120}
121
122pub fn service_status(runtime_dir: Option<&Path>) -> StatusReport {
133 let answered: bool = runtime_dir
134 .map(|directory| { return directory.join(SOCKET_RELATIVE); })
135 .is_some_and(|socket| { return std::os::unix::net::UnixStream::connect(socket).is_ok(); });
136 return if answered {
137 StatusReport { code: 0, stdout: String::from("The commit service is running and answers.\n"), stderr: String::new() }
138 } else {
139 StatusReport {
140 code: 1,
141 stdout: String::new(),
142 stderr: String::from(
143 "The commit service does not answer here. It sets itself up the first time a commit needs it, so there is nothing to \
144 install; if a commit fails, its message says what to do.\n",
145 ),
146 }
147 };
148}
149
150#[cfg(test)]
152#[path = "service_route_tests.rs"]
153mod tests;