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