1use std::fmt::Display;
24
25use anyhow::Result;
26use serde::Serialize;
27pub use zbus::{Connection, zvariant::OwnedObjectPath};
28use zbus_systemd::systemd1::ManagerProxy;
29
30use crate::traits::*;
31
32#[derive(Debug, Serialize, Clone)]
34pub struct SystemdServices {
35 pub units: Vec<ServiceInfo>,
36}
37
38impl SystemdServices {
39 pub async fn new_from_connection(conn: &Connection) -> Result<Self> {
40 let mgr = ManagerProxy::new(conn).await?;
41 let mut units = vec![];
42
43 for unit in mgr.list_units().await? {
44 units.push(ServiceInfo::from(unit));
45 }
46
47 Ok(Self { units })
48 }
49}
50
51impl ToJson for SystemdServices {}
52
53impl ToPlainText for SystemdServices {
54 fn to_plain(&self) -> String {
55 let mut s = format!("\nSystemd services list:");
56 for service in &self.units {
57 s += &service.to_plain();
58 }
59
60 s
61 }
62}
63
64fn unescape(s: &str) -> String {
65 s.replace("\\x20", " ")
66 .replace("\\x5c", "\\")
67 .replace("\\x2f", "/")
68 .replace("\\x2d", "-")
69}
70
71type ServiceTuple = (
72 String,
73 String,
74 String,
75 String,
76 String,
77 String,
78 OwnedObjectPath,
79 u32,
80 String,
81 OwnedObjectPath,
82);
83
84#[derive(Debug, Serialize, Clone)]
85pub struct ServiceInfo {
86 pub name: String,
88
89 pub description: String,
91
92 pub load_state: LoadState,
94
95 pub active_state: ActiveState,
97
98 pub work_state: WorkState,
100
101 pub daemon_path: String,
103
104 pub job_id: u32,
106
107 pub unit_type: UnitType,
109}
110
111impl ToPlainText for ServiceInfo {
112 fn to_plain(&self) -> String {
113 let mut s = format!("\nService \"{}\"\n", &self.name);
114 s += &print_val("Description", &self.description);
115 s += &print_val("Load state", &self.load_state);
116 s += &print_val("Active state", &self.active_state);
117 s += &print_val("Work state", &self.work_state);
118 s += &print_val("Daemon path", &self.daemon_path);
119 s += &print_val("Job ID", &self.job_id);
120 s += &print_val("Unit type", &self.unit_type);
121
122 s
123 }
124}
125
126impl ToJson for ServiceInfo {}
127
128impl From<ServiceTuple> for ServiceInfo {
129 fn from(value: ServiceTuple) -> Self {
130 Self {
131 name: unescape(&value.0),
132 description: unescape(&value.1),
133 load_state: LoadState::from(&value.2),
134 active_state: ActiveState::from(&value.3),
135 work_state: WorkState::from(&value.4),
136 daemon_path: unescape(&value.5),
137 job_id: value.7,
138 unit_type: UnitType::from(&value.8),
139 }
140 }
141}
142
143#[derive(Debug, Serialize, Clone)]
144pub enum LoadState {
145 Loaded,
146 Stub,
147 Masked,
148 NotFound,
149 Unknown(String),
150}
151
152impl Display for LoadState {
153 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154 write!(
155 f,
156 "{}",
157 match self {
158 Self::Loaded => "Loaded",
159 Self::Stub => "Stub",
160 Self::Masked => "Masked",
161 Self::NotFound => "Not found",
162 _ => "Unknown",
163 }
164 )
165 }
166}
167
168impl From<&String> for LoadState {
169 fn from(value: &String) -> Self {
170 match value as &str {
171 "loaded" => Self::Loaded,
172 "stub" => Self::Stub,
173 "masked" => Self::Masked,
174 "not-found" => Self::NotFound,
175 _ => Self::Unknown(value.to_string()),
176 }
177 }
178}
179
180#[derive(Debug, Serialize, Clone)]
181pub enum ActiveState {
182 Active,
183 Inactive,
184 Activating,
185 Deactivating,
186 Failed,
187 Unknown(String),
188}
189
190impl Display for ActiveState {
191 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
192 write!(
193 f,
194 "{}",
195 match self {
196 Self::Active => "Active",
197 Self::Inactive => "Inactive",
198 Self::Activating => "Activating",
199 Self::Deactivating => "Deactivating",
200 Self::Failed => "Failed",
201 _ => "Unknown",
202 }
203 )
204 }
205}
206
207impl From<&String> for ActiveState {
208 fn from(value: &String) -> Self {
209 match value as &str {
210 "active" => Self::Active,
211 "inactive" => Self::Inactive,
212 "activating" => Self::Activating,
213 "deactivating" => Self::Deactivating,
214 "failed" => Self::Failed,
215 _ => Self::Unknown(value.to_string()),
216 }
217 }
218}
219
220#[derive(Debug, Serialize, Clone)]
221pub enum WorkState {
222 Active,
223 Running,
224 Exited,
225 Dead,
226 Mounted,
227 Mounting,
228 Plugged,
229 Listening,
230 Waiting,
231 Failed,
232 Unknown(String),
233}
234
235impl Display for WorkState {
236 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
237 write!(
238 f,
239 "{}",
240 match self {
241 Self::Active => "Active",
242 Self::Running => "Running",
243 Self::Exited => "Exited",
244 Self::Dead => "Dead",
245 Self::Mounted => "Mounted",
246 Self::Mounting => "Mounting",
247 Self::Plugged => "Plugged",
248 Self::Listening => "Listening",
249 Self::Waiting => "Waiting",
250 Self::Failed => "Failed",
251 _ => "Unknown",
252 }
253 )
254 }
255}
256
257impl From<&String> for WorkState {
258 fn from(value: &String) -> Self {
259 match value as &str {
260 "active" => Self::Active,
261 "running" => Self::Running,
262 "exited" => Self::Exited,
263 "dead" => Self::Dead,
264 "mounted" => Self::Mounted,
265 "mounting" => Self::Mounting,
266 "plugged" => Self::Plugged,
267 "listening" => Self::Listening,
268 "waiting" => Self::Waiting,
269 "failed" => Self::Failed,
270 _ => Self::Unknown(value.to_string()),
271 }
272 }
273}
274
275#[derive(Debug, Serialize, Clone)]
276pub enum UnitType {
277 Target,
278 Service,
279 Mount,
280 Swap,
281 None,
282 Unknown(String),
283}
284
285impl Display for UnitType {
286 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
287 write!(
288 f,
289 "{}",
290 match self {
291 Self::Target => "Target",
292 Self::Service => "Service",
293 Self::Mount => "Mount",
294 Self::Swap => "Swap",
295 Self::None => "None-type",
296 _ => "Unknown",
297 }
298 )
299 }
300}
301
302impl From<&String> for UnitType {
303 fn from(value: &String) -> Self {
304 match value as &str {
305 "target" => Self::Target,
306 "service" => Self::Service,
307 "mount" => Self::Mount,
308 "swap" => Self::Swap,
309 "" => Self::None,
310 _ => Self::Unknown(value.to_string()),
311 }
312 }
313}