Skip to main content

sysd_manager_comcontroler/
data.rs

1use super::UpdatedUnitInfo;
2use crate::{
3    enums::{ActiveState, LoadState, Preset, UnitFileStatus},
4    sysdbus::ListedUnitFile,
5};
6use base::enums::UnitDBusLevel;
7use glib::{self, object::ObjectExt, subclass::types::ObjectSubclassIsExt};
8use serde::Deserialize;
9use std::{cmp::Ordering, fmt::Debug};
10use tracing::warn;
11use zvariant::{OwnedObjectPath, OwnedValue, Value};
12
13pub const SYSD_SOCKET_LISTEN_IDX: &str = "sysdSocketListenIdx";
14pub const PRIMARY: &str = "primary";
15glib::wrapper! {
16    pub struct UnitInfo(ObjectSubclass<imp::UnitInfoImpl>);
17}
18
19impl UnitInfo {
20    pub fn from_listed_unit(listed_unit: ListedLoadedUnit, level: UnitDBusLevel) -> Self {
21        // let this_object: Self = glib::Object::new();
22        let this_object: Self = glib::Object::builder()
23            .property(PRIMARY, &listed_unit.primary_unit_name)
24            .build();
25        this_object.init_from_listed_unit(listed_unit, level);
26        this_object
27    }
28
29    pub fn init_from_listed_unit(&self, listed_unit: ListedLoadedUnit, level: UnitDBusLevel) {
30        self.imp().init_from_listed_unit(listed_unit, level);
31    }
32
33    pub fn from_unit_file(unit_file: ListedUnitFile, level: UnitDBusLevel) -> Self {
34        // let this_object: Self = glib::Object::new();
35        let this_object: Self = glib::Object::builder()
36            .property(PRIMARY, unit_file.unit_primary_name())
37            .build();
38        this_object.imp().init_from_unit_file(unit_file, level);
39        this_object
40    }
41
42    pub fn from_unit_key(name: &str, level: UnitDBusLevel) -> Self {
43        let this_object: Self = glib::Object::builder().property(PRIMARY, name).build();
44        this_object.imp().dbus_level.replace(level);
45        this_object
46    }
47
48    pub fn update_from_loaded_unit(&self, listed_unit: ListedLoadedUnit) {
49        self.imp().update_from_listed_unit(listed_unit);
50    }
51
52    pub fn update_from_unit_info(&self, update: UpdatedUnitInfo) {
53        self.imp().update_from_unit_info(self, update);
54    }
55
56    pub fn update_from_unit_file(&self, unit_file: ListedUnitFile) {
57        self.imp().update_from_unit_file(unit_file);
58    }
59
60    pub fn debug(&self) -> String {
61        format!("{:#?}", *self.imp())
62    }
63
64    pub fn need_to_be_completed(&self) -> bool {
65        self.imp().need_to_be_completed()
66    }
67
68    pub fn insert_socket_listen(&self, quark: glib::Quark, value: OwnedValue) -> usize {
69        let mut alen = 0;
70        if let Value::Array(array) = &value as &Value {
71            let mut new_array = Vec::with_capacity(array.len());
72
73            for s in array.iter() {
74                if let Value::Structure(structure) = s {
75                    let f = structure.fields();
76                    if f.len() == 2 {
77                        let f0: String = f[0]
78                            .clone()
79                            .try_into()
80                            .inspect_err(|err| warn!("socket listen {err:?}"))
81                            .unwrap_or_default();
82                        let f1: String = f[1]
83                            .clone()
84                            .try_into()
85                            .inspect_err(|err| warn!("socket listen {err:?}"))
86                            .unwrap_or_default();
87                        new_array.push((f0, f1));
88                    }
89                }
90            }
91
92            alen = new_array.len();
93            unsafe { self.set_qdata(quark, new_array) };
94        }
95        alen
96    }
97
98    pub fn insert_unit_property_value(&self, quark: glib::Quark, value: OwnedValue) {
99        //let value_ref = &value as &Value;
100        match &value as &Value {
101            Value::Bool(b) => unsafe { self.set_qdata(quark, *b) },
102            Value::U8(i) => unsafe { self.set_qdata(quark, *i) },
103            Value::I16(i) => unsafe { self.set_qdata(quark, *i) },
104            Value::U16(i) => unsafe { self.set_qdata(quark, *i) },
105            Value::I32(i) => unsafe { self.set_qdata(quark, *i) },
106            Value::U32(i) => unsafe { self.set_qdata(quark, *i) },
107            Value::I64(i) => unsafe { self.set_qdata(quark, *i) },
108            Value::U64(i) => unsafe { self.set_qdata(quark, *i) },
109            Value::F64(i) => unsafe { self.set_qdata(quark, *i) },
110            Value::Str(s) => {
111                if s.is_empty() {
112                    unsafe { self.steal_qdata::<String>(quark) };
113                } else {
114                    unsafe { self.set_qdata(quark, s.to_string()) };
115                }
116            }
117            Value::Signature(s) => unsafe { self.set_qdata(quark, s.to_string()) },
118            Value::ObjectPath(op) => unsafe { self.set_qdata(quark, op.to_string()) },
119            Value::Value(val) => unsafe { self.set_qdata(quark, val.to_string()) },
120            Value::Array(array) => {
121                if array.is_empty() {
122                    unsafe { self.steal_qdata::<String>(quark) };
123                } else {
124                    let mut d_str = String::from("");
125
126                    let mut it = array.iter().peekable();
127                    while let Some(mi) = it.next() {
128                        if let Some(str_value) = convert_to_string(mi) {
129                            d_str.push_str(&str_value);
130                        }
131                        if it.peek().is_some() {
132                            d_str.push_str(", ");
133                        }
134                    }
135
136                    unsafe { self.set_qdata(quark, d_str) };
137                }
138            }
139            Value::Dict(d) => {
140                let mut it = d.iter().peekable();
141                if it.peek().is_none() {
142                    unsafe { self.steal_qdata::<String>(quark) };
143                } else {
144                    let mut d_str = String::from("{ ");
145
146                    for (mik, miv) in it {
147                        if let Some(k) = convert_to_string(mik) {
148                            d_str.push_str(&k);
149                        }
150                        d_str.push_str(" : ");
151
152                        if let Some(v) = convert_to_string(miv) {
153                            d_str.push_str(&v);
154                        }
155                    }
156                    d_str.push_str(" }");
157
158                    unsafe { self.set_qdata(quark, d_str) };
159                }
160            }
161            Value::Structure(stc) => {
162                let mut it = stc.fields().iter().peekable();
163
164                if it.peek().is_none() {
165                    unsafe { self.steal_qdata::<String>(quark) };
166                } else {
167                    let v: Vec<String> = it
168                        .filter_map(|v| convert_to_string(v))
169                        .filter(|s| !s.is_empty())
170                        .collect();
171                    let d_str = v.join(", ");
172
173                    unsafe { self.set_qdata(quark, d_str) };
174                }
175            }
176            Value::Fd(fd) => unsafe { self.set_qdata(quark, fd.to_string()) },
177            //Value::Maybe(maybe) => (maybe.to_string(), false),
178        }
179    }
180
181    pub fn get_custom_property_to_string<T>(&self, key: glib::Quark) -> Option<String>
182    where
183        T: ToString + 'static,
184    {
185        unsafe { self.qdata::<T>(key) }
186            .map(|value_ptr| unsafe { value_ptr.as_ref() })
187            .map(|value| value.to_string())
188    }
189
190    pub fn get_custom_property<T: 'static>(&self, key: glib::Quark) -> Option<&T> {
191        unsafe { self.qdata::<T>(key) }.map(|value_ptr| unsafe { value_ptr.as_ref() })
192    }
193
194    pub fn display_custom_property(&self, key: glib::Quark) -> Option<String> {
195        unsafe { self.qdata::<OwnedValue>(key) }
196            .map(|value_ptr| unsafe { value_ptr.as_ref() })
197            .and_then(|value| convert_to_string(value))
198    }
199
200    pub fn is_template_unit_file(&self) -> bool {
201        self.imp().is_template_unit_file()
202    }
203
204    pub fn equals(&self, other: &UnitInfo) -> bool {
205        self.dbus_level() == other.dbus_level()
206            && self.unit_type() == other.unit_type()
207            && self.display_name() == other.display_name()
208    }
209
210    pub fn equals_op(&self, other: Option<&UnitInfo>) -> bool {
211        if let Some(other) = other
212            && self.equals(other)
213        {
214            true
215        } else {
216            false
217        }
218    }
219
220    pub fn is_active(&self) -> bool {
221        self.active_state().is_active()
222    }
223}
224
225pub fn get_custom_property_typed_raw<T, O>(unit: &O, key: glib::Quark) -> Option<T>
226where
227    T: Copy + 'static,
228    O: ObjectExt,
229{
230    unsafe { unit.qdata::<T>(key) }
231        .map(|value_ptr| unsafe { value_ptr.as_ref() })
232        .copied()
233}
234
235mod imp {
236    use std::{
237        cell::{Cell, OnceCell, RefCell},
238        str::FromStr,
239    };
240
241    use base::enums::UnitDBusLevel;
242    use glib::{
243        self,
244        object::ObjectExt,
245        subclass::{object::*, types::ObjectSubclass},
246    };
247
248    use crate::{
249        UpdatedUnitInfo,
250        data::ListedLoadedUnit,
251        enums::{ActiveState, LoadState, Preset, UnitFileStatus, UnitType},
252        sysdbus::ListedUnitFile,
253    };
254
255    #[derive(Debug, glib::Properties, Default)]
256    #[properties(wrapper_type = super::UnitInfo)]
257    pub struct UnitInfoImpl {
258        #[property(get, construct_only, set = Self::set_primary)]
259        pub(super) primary: OnceCell<String>,
260
261        #[property(get = Self::get_display_name, type = String)]
262        display_name: OnceCell<u32>,
263
264        #[property(get, default)]
265        unit_type: Cell<UnitType>,
266
267        #[property(get, set)]
268        pub(super) description: RefCell<Option<String>>,
269
270        #[property(get, set, default)]
271        pub(super) load_state: Cell<LoadState>,
272
273        #[property(get, set, builder(ActiveState::Unknown))]
274        pub(super) active_state: Cell<ActiveState>,
275
276        #[property(get, set)]
277        pub(super) sub_state: RefCell<String>,
278
279        #[property(get)]
280        pub(super) followed_unit: RefCell<String>,
281
282        //#[property(get = Self::has_object_path, name = "pathexists", type = bool)]
283        #[property(get=Self::get_unit_path, type = String)]
284        pub(super) object_path: OnceCell<String>,
285        #[property(get, set, nullable, default = None)]
286        pub(super) file_path: RefCell<Option<String>>,
287        #[property(get, set, default)]
288        pub(super) enable_status: Cell<UnitFileStatus>,
289
290        #[property(get, set, default)]
291        pub(super) dbus_level: Cell<UnitDBusLevel>,
292
293        #[property(get, set, default)]
294        pub(super) preset: Cell<Preset>,
295    }
296
297    #[glib::object_subclass]
298    impl ObjectSubclass for UnitInfoImpl {
299        const NAME: &'static str = "UnitInfo";
300        type Type = super::UnitInfo;
301        type ParentType = glib::Object;
302        fn new() -> Self {
303            Default::default()
304        }
305    }
306
307    #[glib::derived_properties]
308    impl ObjectImpl for UnitInfoImpl {}
309
310    impl UnitInfoImpl {
311        pub(super) fn init_from_listed_unit(
312            &self,
313            listed_unit: super::ListedLoadedUnit,
314            dbus_level: UnitDBusLevel,
315        ) {
316            self.dbus_level.replace(dbus_level);
317            self.update_from_listed_unit(listed_unit);
318        }
319
320        pub(super) fn update_from_listed_unit(&self, listed_unit: ListedLoadedUnit) {
321            let active_state: ActiveState = listed_unit.active_state.as_str().into();
322
323            //self.set_primary(listed_unit.primary_unit_name);
324            self.active_state.replace(active_state);
325
326            let description = if listed_unit.description.is_empty() {
327                None
328            } else {
329                Some(listed_unit.description)
330            };
331
332            self.description.replace(description);
333            let load_state: LoadState = listed_unit.load_state.as_str().into();
334            self.load_state.replace(load_state);
335            self.sub_state.replace(listed_unit.sub_state);
336            self.followed_unit.replace(listed_unit.followed_unit);
337        }
338
339        pub(super) fn init_from_unit_file(&self, unit_file: ListedUnitFile, level: UnitDBusLevel) {
340            self.dbus_level.replace(level);
341            self.update_from_unit_file(unit_file)
342        }
343
344        pub(super) fn update_from_unit_file(&self, unit_file: ListedUnitFile) {
345            self.file_path.replace(Some(unit_file.unit_file_path));
346            let status = UnitFileStatus::from_str(&unit_file.enablement_status)
347                .unwrap_or(UnitFileStatus::default());
348            self.enable_status.replace(status);
349        }
350
351        fn set_primary(&self, primary: String) {
352            let mut split_char_index = primary.len();
353            for (i, c) in primary.chars().rev().enumerate() {
354                if c == '.' {
355                    split_char_index -= i;
356                    break;
357                }
358            }
359
360            // let display_name = primary[..split_char_index - 1].to_owned();
361            self.display_name.set((split_char_index - 1) as u32);
362
363            let unit_type = UnitType::new(&primary[(split_char_index)..]);
364            self.unit_type.set(unit_type);
365
366            self.primary.set(primary);
367        }
368
369        pub fn get_display_name(&self) -> String {
370            let index = *self.display_name.get_or_init(|| unreachable!()) as usize;
371            let s = &self.primary.get().expect("Being set")[..index];
372            s.to_owned()
373        }
374
375        pub fn update_from_unit_info(&self, unit: &super::UnitInfo, update: UpdatedUnitInfo) {
376            // self.object_path.replace(Some(update.object_path));
377
378            self.description.replace(update.description);
379
380            if let Some(sub_state) = update.sub_state {
381                self.sub_state.replace(sub_state);
382            }
383
384            if let Some(active_state) = update.active_state {
385                self.active_state.replace(active_state);
386            }
387
388            if let Some(unit_file_preset) = update.unit_file_preset {
389                let preset: Preset = unit_file_preset.into();
390                unit.set_preset(preset);
391            }
392
393            if let Some(load_state) = update.load_state {
394                unit.set_load_state(load_state);
395            }
396
397            if let Some(fragment_path) = update.fragment_path {
398                self.file_path.replace(Some(fragment_path));
399            }
400
401            if let Some(enablement_status) = update.enablement_status {
402                self.enable_status.replace(enablement_status);
403            }
404        }
405
406        fn get_unit_path(&self) -> String {
407            let object_path = self.object_path.get_or_init(|| {
408                let primary = self.primary.get_or_init(|| unreachable!());
409                crate::sysdbus::unit_dbus_path_from_name(primary)
410            });
411            object_path.clone()
412        }
413
414        pub fn need_to_be_completed(&self) -> bool {
415            self.description.borrow().is_none() || self.preset.get() == Preset::UnSet
416            // || self.load_state.get() == LoadState::Unknown
417        }
418
419        pub(crate) fn is_template_unit_file(&self) -> bool {
420            let index = *self
421                .display_name
422                .get_or_init(|| unreachable!("Has to be set")) as usize;
423            let s = &self.primary.get().expect("Being set")[..index];
424            s.ends_with('@')
425        }
426    }
427}
428
429#[derive(Debug, Eq, PartialEq)]
430pub struct UnitProcess {
431    pub path: String,
432    pub pid: u32,
433    pub name: String,
434    pub(crate) unit_name: usize,
435}
436
437impl UnitProcess {
438    pub fn unit_name(&self) -> &str {
439        &self.path[self.unit_name..]
440    }
441}
442
443impl Ord for UnitProcess {
444    fn cmp(&self, other: &Self) -> Ordering {
445        let cmp: Ordering = self.unit_name().cmp(other.unit_name());
446        if self.unit_name().cmp(other.unit_name()) == Ordering::Equal {
447            self.pid.cmp(&other.pid)
448        } else {
449            cmp
450        }
451    }
452}
453
454impl PartialOrd for UnitProcess {
455    fn partial_cmp(&self, other: &UnitProcess) -> Option<Ordering> {
456        Some(self.cmp(other))
457    }
458}
459
460#[derive(Deserialize, zvariant::Type, PartialEq, Debug)]
461pub struct ListedLoadedUnit {
462    pub primary_unit_name: String,
463    pub description: String,
464    pub load_state: String,
465    pub active_state: String,
466    pub sub_state: String,
467    pub followed_unit: String,
468
469    pub unit_object_path: OwnedObjectPath,
470    ///If there is a job queued for the job unit the numeric job id, 0 otherwise
471    pub numeric_job_id: u32,
472    pub job_type: String,
473    pub job_object_path: OwnedObjectPath,
474}
475
476pub fn convert_to_string(value: &Value) -> Option<String> {
477    match value {
478        Value::Bool(b) => Some(b.to_string()),
479        Value::U8(i) => Some(i.to_string()),
480        Value::I16(i) => Some(i.to_string()),
481        Value::U16(i) => Some(i.to_string()),
482        Value::I32(i) => Some(i.to_string()),
483        Value::U32(i) => Some(i.to_string()),
484        Value::I64(i) => Some(i.to_string()),
485        Value::U64(i) => Some(i.to_string()),
486        Value::F64(i) => Some(i.to_string()),
487        Value::Str(s) => Some(s.to_string()),
488        Value::Signature(s) => Some(s.to_string()),
489        Value::ObjectPath(op) => Some(op.to_string()),
490        Value::Value(v) => Some(v.to_string()),
491        Value::Array(a) => {
492            if a.is_empty() {
493                None
494            } else {
495                let mut d_str = String::from("");
496
497                let mut it = a.iter().peekable();
498                while let Some(mi) = it.next() {
499                    if let Some(v) = convert_to_string(mi) {
500                        d_str.push_str(&v);
501                    }
502                    if it.peek().is_some() {
503                        d_str.push_str(", ");
504                    }
505                }
506
507                Some(d_str)
508            }
509        }
510        Value::Dict(d) => {
511            let mut it = d.iter().peekable();
512            if it.peek().is_none() {
513                None
514            } else {
515                let mut d_str = String::from("{ ");
516
517                for (mik, miv) in it {
518                    if let Some(k) = convert_to_string(mik) {
519                        d_str.push_str(&k);
520                    }
521                    d_str.push_str(" : ");
522
523                    if let Some(v) = convert_to_string(miv) {
524                        d_str.push_str(&v);
525                    }
526                }
527                d_str.push_str(" }");
528                Some(d_str)
529            }
530        }
531        Value::Structure(stc) => {
532            let mut it = stc.fields().iter().peekable();
533
534            if it.peek().is_none() {
535                None
536            } else {
537                let mut d_str = String::from("");
538
539                while let Some(mi) = it.next() {
540                    if let Some(v) = convert_to_string(mi) {
541                        d_str.push_str(&v);
542                    }
543
544                    if it.peek().is_some() {
545                        d_str.push_str(", ");
546                    }
547                }
548
549                Some(d_str)
550            }
551        }
552        Value::Fd(fd) => Some(fd.to_string()),
553        //Value::Maybe(maybe) => (maybe.to_string(), false),
554    }
555}
556
557#[derive(Debug)]
558pub enum UnitPropertySetter {
559    FileState(UnitFileStatus),
560    Description(String),
561    ActiveState(ActiveState),
562    LoadState(LoadState),
563    FragmentPath(String),
564    UnitFilePreset(Preset),
565    SubState(String),
566    Custom(glib::Quark, OwnedValue),
567}