Skip to main content

sysd_manager_comcontroler/
data.rs

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