Skip to main content

sysd_manager_comcontroler/
lib.rs

1#![allow(unused_must_use)]
2pub mod analyze;
3pub mod data;
4pub mod enums;
5pub mod errors;
6mod file;
7mod journal;
8pub mod journal_data;
9#[cfg(not(any(feature = "flatpak", feature = "appimage")))]
10pub mod proxy_switcher;
11pub mod socket_unit;
12pub mod sysdbus;
13pub mod time_handling;
14
15use crate::data::ListedLoadedUnit;
16use crate::{
17    data::UnitPropertySetter,
18    enums::{ActiveState, LoadState, StartStopMode, UnitFileStatus},
19    file::save_text_to_file,
20    journal_data::Boot,
21    sysdbus::{
22        ListedUnitFile,
23        dbus_proxies::{systemd_manager, systemd_manager_async},
24    },
25    time_handling::TimestampStyle,
26};
27use base::file::create_drop_in_path_file;
28use base::{
29    enums::UnitDBusLevel,
30    file::{commander_blocking, flatpak_host_file_path, test_flatpak_spawn},
31    proxy::{DisEnAbleUnitFiles, DisEnAbleUnitFilesResponse},
32};
33use data::{UnitInfo, UnitProcess};
34use enumflags2::{BitFlag, BitFlags};
35use enums::{CleanOption, DependencyType, DisEnableFlags, KillWho, UnitType};
36use errors::SystemdErrors;
37use flagset::{FlagSet, flags};
38use glib::Quark;
39use journal_data::{EventRange, JournalEventChunk};
40use std::{
41    any::Any,
42    collections::{BTreeMap, BTreeSet, HashMap},
43    fs::File,
44    io::Read,
45    sync::OnceLock,
46    time::{SystemTime, UNIX_EPOCH},
47};
48use tokio::{runtime::Runtime, sync::mpsc};
49use tracing::{error, info, warn};
50use zvariant::{OwnedObjectPath, OwnedValue};
51
52#[cfg(not(any(feature = "flatpak", feature = "appimage")))]
53use crate::sysdbus::to_proxy;
54
55#[cfg(not(any(feature = "flatpak", feature = "appimage")))]
56use base::consts::PROXY_SERVICE;
57
58#[derive(Default, Clone, PartialEq, Debug)]
59pub enum BootFilter {
60    #[default]
61    Current,
62    All,
63    Id(String),
64}
65
66#[derive(Clone, Debug)]
67// #[allow(unused)]
68pub struct SystemdUnitFile {
69    pub full_name: String,
70    pub status_code: UnitFileStatus,
71    pub level: UnitDBusLevel,
72    pub file_path: String,
73}
74
75#[derive(Debug, Default)]
76pub struct UpdatedUnitInfo {
77    pub primary: String,
78    // pub object_path: String,
79    pub description: Option<String>,
80    pub load_state: Option<LoadState>,
81    pub sub_state: Option<String>,
82    pub active_state: Option<ActiveState>,
83    pub unit_file_preset: Option<String>,
84    pub valid_unit_name: bool,
85    pub fragment_path: Option<String>,
86    pub enablement_status: Option<UnitFileStatus>,
87    pub level: UnitDBusLevel,
88}
89
90impl UpdatedUnitInfo {
91    fn new(primary: String, level: UnitDBusLevel) -> Self {
92        Self {
93            primary,
94            level,
95            ..Default::default()
96        }
97    }
98}
99
100flags! {
101    pub enum UnitPropertiesFlags : u8 {
102        EnablementStatus,
103        ActiveStatus,
104        Description,
105        LoadState,
106        SubState,
107        UnitFilePreset,
108        FragmentPath,
109    }
110}
111
112#[derive(Debug, Clone, Copy)]
113pub struct UnitProperties(pub FlagSet<UnitPropertiesFlags>);
114
115impl UnitProperties {
116    // fn new(flags: impl Into<FlagSet<UnitPropertiesFlags>>) -> UnitProperties {
117    //     UnitProperties(flags.into())
118    // }
119    //
120}
121
122pub struct CompleteUnitPropertiesCallParams {
123    pub level: UnitDBusLevel,
124    pub unit_name: String,
125    pub object_path: String,
126    pub status: UnitFileStatus,
127}
128
129impl CompleteUnitPropertiesCallParams {
130    pub fn new(unit: &UnitInfo) -> Self {
131        // println!("Has status {:?} {:?}", unit.enable_status(), unit.primary())
132
133        Self::new_params(
134            unit.dbus_level(),
135            unit.primary(),
136            unit.object_path(),
137            unit.enable_status(),
138        )
139    }
140
141    pub fn new_params(
142        level: UnitDBusLevel,
143        unit_name: String,
144        object_path: String,
145        status: UnitFileStatus,
146    ) -> Self {
147        Self {
148            level,
149            unit_name,
150            object_path,
151            status,
152        }
153    }
154}
155
156pub fn runtime() -> &'static Runtime {
157    static RUNTIME: OnceLock<Runtime> = OnceLock::new();
158    RUNTIME.get_or_init(|| Runtime::new().expect("Setting up tokio runtime needs to succeed."))
159}
160
161///Try to Start Proxy
162#[cfg(not(any(feature = "flatpak", feature = "appimage")))]
163pub async fn init_proxy_async(run_mode: base::RunMode) {
164    if let Err(e) = sysdbus::init_proxy_async(run_mode).await {
165        error!("Fail starting Proxy. Error {e:?}");
166    }
167}
168
169#[cfg(not(any(feature = "flatpak", feature = "appimage")))]
170pub fn shut_down() {
171    sysdbus::shut_down_sysd_proxy();
172}
173
174pub fn get_unit_file_state(
175    level: UnitDBusLevel,
176    primary_name: &str,
177) -> Result<UnitFileStatus, SystemdErrors> {
178    sysdbus::get_unit_file_state(level, primary_name)
179}
180
181pub async fn list_units_description_and_state_async(
182    level: UnitDBusLevel,
183) -> Result<(Vec<ListedLoadedUnit>, Vec<SystemdUnitFile>), SystemdErrors> {
184    sysdbus::list_units_description_and_state_async(level).await
185}
186
187#[derive(Debug)]
188pub enum ListUnitResponse {
189    Loaded(UnitDBusLevel, Vec<ListedLoadedUnit>),
190    File(UnitDBusLevel, Vec<ListedUnitFile>),
191}
192
193impl ListUnitResponse {
194    pub fn r_len(&self) -> (usize, usize) {
195        match self {
196            ListUnitResponse::Loaded(_, items) => (items.len(), 0),
197            ListUnitResponse::File(_, items) => (0, items.len()),
198        }
199    }
200
201    pub fn t_len(&self) -> usize {
202        match self {
203            ListUnitResponse::Loaded(_, lunits) => lunits.len(),
204            ListUnitResponse::File(_, items) => items.len(),
205        }
206    }
207    pub fn update_flags(&self) -> FlagSet<UnitPropertiesFlags> {
208        match self {
209            ListUnitResponse::Loaded(_, _) => {
210                UnitPropertiesFlags::EnablementStatus
211                    | UnitPropertiesFlags::Description
212                    | UnitPropertiesFlags::LoadState
213                    | UnitPropertiesFlags::SubState
214                    | UnitPropertiesFlags::UnitFilePreset
215            }
216
217            ListUnitResponse::File(_, _) => {
218                UnitPropertiesFlags::ActiveStatus
219                    | UnitPropertiesFlags::Description
220                    | UnitPropertiesFlags::LoadState
221                    | UnitPropertiesFlags::SubState
222                    | UnitPropertiesFlags::UnitFilePreset
223            }
224        }
225    }
226}
227
228pub async fn list_loaded_units(level: UnitDBusLevel) -> Result<ListUnitResponse, SystemdErrors> {
229    let v = systemd_manager_async(level).await?.list_units().await?;
230    Ok(ListUnitResponse::Loaded(level, v))
231}
232
233pub async fn list_loaded_units_by_patterns(
234    level: UnitDBusLevel,
235    patterns: &[&str],
236) -> Result<ListUnitResponse, SystemdErrors> {
237    let v = systemd_manager_async(level)
238        .await?
239        .list_units_by_patterns(&[], patterns)
240        .await?;
241    Ok(ListUnitResponse::Loaded(level, v))
242}
243
244pub async fn list_loaded_units_timers(
245    level: UnitDBusLevel,
246) -> Result<ListUnitResponse, SystemdErrors> {
247    list_loaded_units_by_patterns(level, &["*.timer"]).await
248}
249
250pub async fn list_loaded_units_sockets(
251    level: UnitDBusLevel,
252) -> Result<ListUnitResponse, SystemdErrors> {
253    list_loaded_units_by_patterns(level, &["*.socket"]).await
254}
255
256pub async fn list_loaded_units_paths(
257    level: UnitDBusLevel,
258) -> Result<ListUnitResponse, SystemdErrors> {
259    list_loaded_units_by_patterns(level, &["*.path"]).await
260}
261
262pub async fn list_loaded_units_automounts(
263    level: UnitDBusLevel,
264) -> Result<ListUnitResponse, SystemdErrors> {
265    list_loaded_units_by_patterns(level, &["*.automount"]).await
266}
267
268pub async fn list_unit_files(level: UnitDBusLevel) -> Result<ListUnitResponse, SystemdErrors> {
269    let v = systemd_manager_async(level)
270        .await?
271        .list_unit_files()
272        .await?;
273    Ok(ListUnitResponse::File(level, v))
274}
275
276pub async fn list_unit_files_by_patterns(
277    level: UnitDBusLevel,
278    patterns: &[&str],
279) -> Result<ListUnitResponse, SystemdErrors> {
280    let v = systemd_manager_async(level)
281        .await?
282        .list_unit_files_by_patterns(&[], patterns)
283        .await?;
284    Ok(ListUnitResponse::File(level, v))
285}
286
287pub async fn list_unit_files_timers(
288    level: UnitDBusLevel,
289) -> Result<ListUnitResponse, SystemdErrors> {
290    list_unit_files_by_patterns(level, &["*.timer"]).await
291}
292
293pub async fn list_unit_files_sockets(
294    level: UnitDBusLevel,
295) -> Result<ListUnitResponse, SystemdErrors> {
296    list_unit_files_by_patterns(level, &["*.socket"]).await
297}
298
299pub async fn list_unit_files_paths(
300    level: UnitDBusLevel,
301) -> Result<ListUnitResponse, SystemdErrors> {
302    list_unit_files_by_patterns(level, &["*.path"]).await
303}
304
305pub async fn list_unit_files_automounts(
306    level: UnitDBusLevel,
307) -> Result<ListUnitResponse, SystemdErrors> {
308    list_unit_files_by_patterns(level, &["*.automount"]).await
309}
310
311pub async fn complete_unit_information(
312    units: &[CompleteUnitPropertiesCallParams],
313) -> Result<Vec<UpdatedUnitInfo>, SystemdErrors> {
314    sysdbus::complete_unit_information(units).await
315}
316
317pub async fn complete_single_unit_information(
318    primary_name: String,
319    level: UnitDBusLevel,
320    object_path: String,
321    status: UnitFileStatus,
322) -> Result<Vec<UpdatedUnitInfo>, SystemdErrors> {
323    let units = [CompleteUnitPropertiesCallParams::new_params(
324        level,
325        primary_name,
326        object_path,
327        status,
328    )];
329    sysdbus::complete_unit_information(&units).await
330}
331
332/// Takes a unit name as input and attempts to start it
333/// # returns
334/// job_path
335pub fn start_unit(
336    level: UnitDBusLevel,
337    unit_name: &str,
338    mode: StartStopMode,
339) -> Result<String, SystemdErrors> {
340    #[cfg(not(any(feature = "flatpak", feature = "appimage")))]
341    match level {
342        UnitDBusLevel::System | UnitDBusLevel::Both => {
343            if proxy_switcher::PROXY_SWITCHER.start() && !unit_name.starts_with(PROXY_SERVICE) {
344                proxy_call_blocking!(start_unit, unit_name, mode.as_str())
345            } else {
346                systemd_manager()
347                    .start_unit(unit_name, mode.as_str())
348                    .map(|o| o.to_string())
349                    .map_err(|err| err.into())
350                // sysdbus::start_unit(level, unit_name, mode)
351            }
352        }
353
354        UnitDBusLevel::UserSession => sysdbus::dbus_proxies::systemd_manager_session()
355            .start_unit(unit_name, mode.as_str())
356            .map(|o| o.to_string())
357            .map_err(|err| err.into()),
358    }
359
360    #[cfg(any(feature = "flatpak", feature = "appimage"))]
361    {
362        use crate::sysdbus::dbus_proxies::systemd_manager_blocking;
363
364        systemd_manager_blocking(level)
365            .start_unit(unit_name, mode.as_str())
366            .map(|o| o.to_string())
367            .map_err(|err| err.into())
368    }
369}
370
371/// Takes a unit name as input and attempts to stop it.
372pub fn stop_unit(
373    level: UnitDBusLevel,
374    unit_name: &str,
375    mode: StartStopMode,
376) -> Result<String, SystemdErrors> {
377    #[cfg(not(any(feature = "flatpak", feature = "appimage")))]
378    match level {
379        UnitDBusLevel::System | UnitDBusLevel::Both => {
380            if proxy_switcher::PROXY_SWITCHER.stop() && !unit_name.starts_with(PROXY_SERVICE) {
381                proxy_call_blocking!(stop_unit, unit_name, mode.as_str())
382            } else {
383                systemd_manager()
384                    .stop_unit(unit_name, mode.as_str())
385                    .map(|o| o.to_string())
386                    .map_err(|err| err.into())
387
388                // sysdbus::stop_unit(level, unit_name, mode)
389            }
390        }
391
392        UnitDBusLevel::UserSession => sysdbus::dbus_proxies::systemd_manager_session()
393            .stop_unit(unit_name, mode.as_str())
394            .map(|o| o.to_string())
395            .map_err(|err| err.into()),
396    }
397
398    #[cfg(any(feature = "flatpak", feature = "appimage"))]
399    {
400        use crate::sysdbus::dbus_proxies::systemd_manager_blocking;
401
402        systemd_manager_blocking(level)
403            .stop_unit(unit_name, mode.as_str())
404            .map(|o| o.to_string())
405            .map_err(|err| err.into())
406    }
407}
408
409pub fn restart_unit(
410    level: UnitDBusLevel,
411    unit_name: &str,
412    mode: StartStopMode,
413) -> Result<String, SystemdErrors> {
414    #[cfg(not(any(feature = "flatpak", feature = "appimage")))]
415    match level {
416        UnitDBusLevel::System | UnitDBusLevel::Both => {
417            if proxy_switcher::PROXY_SWITCHER.restart() && !unit_name.starts_with(PROXY_SERVICE) {
418                proxy_call_blocking!(restart_unit, unit_name, mode.as_str())
419            } else {
420                systemd_manager()
421                    .restart_unit(unit_name, mode.as_str())
422                    .map(|o| o.to_string())
423                    .map_err(|err| err.into())
424                // sysdbus::restart_unit(level, unit_name, mode)
425            }
426        }
427
428        UnitDBusLevel::UserSession => sysdbus::dbus_proxies::systemd_manager_session()
429            .restart_unit(unit_name, mode.as_str())
430            .map(|o| o.to_string())
431            .map_err(|err| err.into()),
432    }
433
434    #[cfg(any(feature = "flatpak", feature = "appimage"))]
435    {
436        use crate::sysdbus::dbus_proxies::systemd_manager_blocking;
437
438        systemd_manager_blocking(level)
439            .restart_unit(unit_name, mode.as_str())
440            .map(|o| o.to_string())
441            .map_err(|err| err.into())
442    }
443}
444
445pub fn disenable_unit_file(
446    primary_name: &str,
447    level: UnitDBusLevel,
448    enable_status: UnitFileStatus,
449    expected_status: UnitFileStatus,
450) -> Result<DisEnAbleUnitFilesResponse, SystemdErrors> {
451    match expected_status {
452        UnitFileStatus::Enabled | UnitFileStatus::EnabledRuntime => enable_unit_file(
453            level,
454            primary_name,
455            DisEnableFlags::SdSystemdUnitForce.into(),
456        ),
457        _ => {
458            let flags: BitFlags<DisEnableFlags> = if enable_status.is_runtime() {
459                DisEnableFlags::SdSystemdUnitRuntime.into()
460            } else {
461                DisEnableFlags::empty()
462            };
463
464            disable_unit_file(level, primary_name, flags)
465        }
466    }
467}
468
469pub fn enable_unit_file(
470    level: UnitDBusLevel,
471    unit_file: &str,
472    flags: BitFlags<DisEnableFlags>,
473) -> Result<DisEnAbleUnitFilesResponse, SystemdErrors> {
474    #[cfg(not(any(feature = "flatpak", feature = "appimage")))]
475    match level {
476        UnitDBusLevel::System | UnitDBusLevel::Both => {
477            if proxy_switcher::PROXY_SWITCHER.enable_unit_file() {
478                proxy_call_blocking!(
479                    enable_unit_files_with_flags,
480                    &[unit_file],
481                    flags.bits_c() as u64
482                )
483            } else {
484                systemd_manager()
485                    .enable_unit_files_with_flags(&[unit_file], flags.bits_c() as u64)
486                    .map_err(|err| err.into())
487            }
488        }
489        UnitDBusLevel::UserSession => sysdbus::dbus_proxies::systemd_manager_session()
490            .enable_unit_files_with_flags(&[unit_file], flags.bits_c() as u64)
491            .map_err(|err| err.into()),
492    }
493
494    #[cfg(any(feature = "flatpak", feature = "appimage"))]
495    {
496        use crate::sysdbus::dbus_proxies::systemd_manager_blocking;
497        systemd_manager_blocking(level)
498            .enable_unit_files_with_flags(&[unit_file], flags.bits_c() as u64)
499            .map_err(|err| err.into())
500    }
501}
502
503pub fn disable_unit_file(
504    level: UnitDBusLevel,
505    unit_file: &str,
506    flags: BitFlags<DisEnableFlags>,
507) -> Result<DisEnAbleUnitFilesResponse, SystemdErrors> {
508    info!("{:?} {} {:?}", level, unit_file, flags.bits_c());
509    #[cfg(not(any(feature = "flatpak", feature = "appimage")))]
510    match level {
511        UnitDBusLevel::System | UnitDBusLevel::Both => {
512            if proxy_switcher::PROXY_SWITCHER.disable_unit_file() {
513                proxy_call_blocking!(
514                    disable_unit_files_with_flags,
515                    &[unit_file],
516                    flags.bits_c() as u64
517                )
518            } else {
519                systemd_manager()
520                    .disable_unit_files_with_flags_and_install_info(
521                        &[unit_file],
522                        flags.bits_c() as u64,
523                    )
524                    .map_err(|err| err.into())
525            }
526        }
527        UnitDBusLevel::UserSession => sysdbus::dbus_proxies::systemd_manager_session()
528            .disable_unit_files_with_flags_and_install_info(&[unit_file], flags.bits_c() as u64)
529            .map_err(|err| err.into()),
530    }
531
532    #[cfg(any(feature = "flatpak", feature = "appimage"))]
533    {
534        use crate::sysdbus::dbus_proxies::systemd_manager_blocking;
535        systemd_manager_blocking(level)
536            .disable_unit_files_with_flags_and_install_info(&[unit_file], flags.bits_c() as u64)
537            .map_err(|err| err.into())
538    }
539}
540
541pub async fn fetch_drop_in_paths(
542    level: UnitDBusLevel,
543    unit_name: &str,
544) -> Result<Vec<String>, SystemdErrors> {
545    sysdbus::fetch_drop_in_paths(level, unit_name).await
546}
547/// Read the unit file and return it's contents so that we can display it
548pub fn fetch_unit_file_content(
549    file_path: Option<&str>,
550    unit_primary_name: &str,
551) -> Result<String, SystemdErrors> {
552    let Some(file_path) = file_path else {
553        warn!("No file path for {:?}", unit_primary_name);
554        return Ok(String::new());
555    };
556
557    file_open_get_content(file_path, unit_primary_name)
558}
559
560#[allow(unused)]
561fn flatpak_file_open_get_content(
562    file_path: &str,
563    unit_primary_name: &str,
564) -> Result<String, SystemdErrors> {
565    file_open_get_content(file_path, unit_primary_name).or_else(|e| {
566        info!("Trying to fetch file content through 'cat' command, because {e:?}");
567        file_open_get_content_cat(file_path, unit_primary_name)
568    })
569}
570
571fn file_open_get_content_cat(
572    file_path: &str,
573    unit_primary_name: &str,
574) -> Result<String, SystemdErrors> {
575    info!(
576        "Flatpak Fetching file content Unit: {} File \"{file_path}\"",
577        unit_primary_name
578    );
579    //Use the REAL path because try to acceess through the 'cat' command
580    commander_output(&["cat", file_path], None)
581        .map(|cat_output| String::from_utf8_lossy(&cat_output.stdout).to_string())
582        .inspect_err(|e| warn!("Can't open file {file_path:?} with 'cat' command, reason: {e:?}"))
583}
584
585fn file_open_get_content(
586    file_path: &str,
587    unit_primary_name: &str,
588) -> Result<String, SystemdErrors> {
589    //To get the relative path from a Flatpak
590    let file_path = flatpak_host_file_path(file_path);
591
592    info!(
593        "Fetching file content Unit: {} File: {}",
594        unit_primary_name,
595        file_path.display()
596    );
597
598    let mut file = File::open(&file_path).map_err(|e| {
599        warn!(
600            "Can't open file \"{}\", reason: {e} {:?}",
601            file_path.display(),
602            e.kind()
603        );
604        SystemdErrors::IoError(e)
605    })?;
606
607    let mut output = String::new();
608    let _ = file.read_to_string(&mut output);
609
610    Ok(output)
611}
612
613/// Obtains the journal log for the given unit.
614pub fn get_unit_journal(
615    primary_name: String,
616    level: UnitDBusLevel,
617    boot_filter: BootFilter,
618    range: EventRange,
619    message_max_char: usize,
620    timestamp_style: TimestampStyle,
621) -> Result<JournalEventChunk, SystemdErrors> {
622    journal::get_unit_journal_events(
623        primary_name,
624        level,
625        boot_filter,
626        range,
627        message_max_char,
628        timestamp_style,
629    )
630}
631
632#[allow(clippy::too_many_arguments)]
633pub fn get_unit_journal_continuous(
634    unit_name: String,
635    level: UnitDBusLevel,
636    range: EventRange,
637    journal_continuous_receiver: std::sync::mpsc::Receiver<()>,
638    sender: std::sync::mpsc::Sender<JournalEventChunk>,
639    message_max_char: usize,
640    timestamp_style: TimestampStyle,
641    check_for_new_journal_entry: fn(),
642) {
643    if let Err(err) = journal::get_unit_journal_events_continuous(
644        unit_name,
645        level,
646        range,
647        journal_continuous_receiver,
648        sender,
649        message_max_char,
650        timestamp_style,
651        check_for_new_journal_entry,
652    ) {
653        warn!(
654            "Journal TailError type: {:?}  Error: {:?}",
655            err.type_id(),
656            err
657        );
658    } else {
659        warn!("Ok journal tail thread finished");
660    }
661}
662
663pub fn list_boots() -> Result<Vec<Boot>, SystemdErrors> {
664    journal::list_boots()
665}
666
667pub fn fetch_last_time() -> Result<u64, SystemdErrors> {
668    journal::fetch_last_time()
669}
670
671pub fn commander_output(
672    prog_n_args: &[&str],
673    environment_variables: Option<&[(&str, &str)]>,
674) -> Result<std::process::Output, SystemdErrors> {
675    match commander_blocking(prog_n_args, environment_variables).output() {
676        Ok(output) => {
677            if cfg!(feature = "flatpak") {
678                info!("Command Exit status: {}", output.status);
679
680                if !output.status.success() {
681                    warn!("Flatpak mode, command line did not succeed, please investigate.");
682                    error!("Command exit status: {}", output.status);
683                    info!(
684                        "{}",
685                        String::from_utf8(output.stdout).expect("from_utf8 failed")
686                    );
687                    error!(
688                        "{}",
689                        String::from_utf8(output.stderr).expect("from_utf8 failed")
690                    );
691                    let vec = prog_n_args.iter().map(|s| s.to_string()).collect();
692                    return Err(SystemdErrors::CmdNoFreedesktopFlatpakPermission(
693                        Some(vec),
694                        None,
695                    ));
696                }
697            }
698            Ok(output)
699        }
700        Err(err) => {
701            error!("commander_output {err}");
702
703            match test_flatpak_spawn() {
704                Ok(()) => Err(SystemdErrors::IoError(err)),
705                Err(e1) => {
706                    error!("commander_output e1 {e1}");
707                    Err(SystemdErrors::CmdNoFlatpakSpawn)
708                }
709            }
710        }
711    }
712}
713
714pub fn generate_file_uri(file_path: &str) -> String {
715    let flatpak_host_file_path = flatpak_host_file_path(file_path);
716    format!("file://{}", flatpak_host_file_path.display())
717}
718
719pub fn fetch_system_info() -> Result<Vec<(UnitType, String, String)>, SystemdErrors> {
720    //TODO check with Session (user)
721    sysdbus::fetch_system_info(UnitDBusLevel::System)
722}
723
724pub fn fetch_system_unit_info_native(
725    unit: &UnitInfo,
726) -> Result<Vec<(UnitType, String, OwnedValue)>, SystemdErrors> {
727    let level = unit.dbus_level();
728    let unit_type: UnitType = unit.unit_type();
729    let object_path = unit.object_path();
730
731    sysdbus::fetch_system_unit_info_native(level, &object_path, unit_type)
732}
733
734pub fn fetch_system_unit_info_native_map(
735    unit: &UnitInfo,
736) -> Result<HashMap<String, OwnedValue>, SystemdErrors> {
737    let level = unit.dbus_level();
738    let unit_type: UnitType = unit.unit_type();
739    let object_path = unit.object_path();
740
741    sysdbus::fetch_system_unit_info_native_map(level, &object_path, unit_type)
742}
743
744/* fn get_unit_path(unit: &UnitInfo) -> String {
745    match unit.object_path() {
746        Some(s) => s,
747        None => {
748            let object_path = sysdbus::unit_dbus_path_from_name(&unit.primary());
749            unit.set_object_path(object_path.clone());
750            object_path
751        }
752    }
753}
754 */
755pub fn fetch_unit(
756    level: UnitDBusLevel,
757    unit_primary_name: &str,
758) -> Result<UnitInfo, SystemdErrors> {
759    sysdbus::fetch_unit(level, unit_primary_name)
760}
761
762pub fn kill_unit(
763    level: UnitDBusLevel,
764    primary_name: &str,
765    who: KillWho,
766    signal: i32,
767) -> Result<(), SystemdErrors> {
768    sysdbus::kill_unit(level, primary_name, who, signal)
769}
770
771pub fn freeze_unit(params: Option<(UnitDBusLevel, String)>) -> Result<(), SystemdErrors> {
772    if let Some((_level, primary_name)) = params {
773        #[cfg(not(any(feature = "flatpak", feature = "appimage")))]
774        match _level {
775            UnitDBusLevel::System | UnitDBusLevel::Both => {
776                if proxy_switcher::PROXY_SWITCHER.freeze() {
777                    proxy_call_blocking!(freeze_unit, &primary_name)
778                } else {
779                    let proxy = systemd_manager();
780                    proxy.freeze_unit(&primary_name)?;
781                    Ok(())
782                }
783            }
784            UnitDBusLevel::UserSession => sysdbus::dbus_proxies::systemd_manager_session()
785                .freeze_unit(&primary_name)
786                .map_err(|err| err.into()),
787        }
788
789        #[cfg(any(feature = "flatpak", feature = "appimage"))]
790        {
791            let proxy = systemd_manager();
792            proxy.freeze_unit(&primary_name)?;
793            Ok(())
794        }
795    } else {
796        Err(SystemdErrors::NoUnit)
797    }
798}
799
800pub fn thaw_unit(params: Option<(UnitDBusLevel, String)>) -> Result<(), SystemdErrors> {
801    let Some((level, primary_name)) = params else {
802        return Err(SystemdErrors::NoUnit);
803    };
804
805    #[cfg(not(any(feature = "flatpak", feature = "appimage")))]
806    match level {
807        UnitDBusLevel::System | UnitDBusLevel::Both => {
808            if proxy_switcher::PROXY_SWITCHER.thaw() {
809                proxy_call_blocking!(thaw_unit, &primary_name)
810            } else {
811                let proxy = systemd_manager();
812                proxy.thaw_unit(&primary_name)?;
813                Ok(())
814            }
815        }
816        UnitDBusLevel::UserSession => sysdbus::dbus_proxies::systemd_manager_session()
817            .thaw_unit(&primary_name)
818            .map_err(|err| err.into()),
819    }
820
821    #[cfg(any(feature = "flatpak", feature = "appimage"))]
822    {
823        use crate::sysdbus::dbus_proxies::systemd_manager_blocking;
824        let proxy = systemd_manager_blocking(level);
825        proxy.thaw_unit(&primary_name)?;
826        Ok(())
827    }
828}
829
830pub fn reload_unit(
831    level: UnitDBusLevel,
832    primary_name: &str,
833    mode: StartStopMode,
834) -> Result<String, SystemdErrors> {
835    sysdbus::reload_unit(level, primary_name, mode.as_str())
836}
837
838pub fn queue_signal_unit(
839    level: UnitDBusLevel,
840    primary_name: &str,
841    who: KillWho,
842    signal: i32,
843    value: i32,
844) -> Result<(), SystemdErrors> {
845    sysdbus::queue_signal_unit(level, primary_name, who, signal, value)
846}
847
848pub fn clean_unit(
849    level: UnitDBusLevel,
850    unit_name: &str,
851    what: &[String],
852) -> Result<(), SystemdErrors> {
853    //just send all if seleted
854    let mut what_peekable = what
855        .iter()
856        .filter(|c_op| *c_op == CleanOption::All.code())
857        .peekable();
858
859    let clean_what: Vec<&str> = if what_peekable.peek().is_some() {
860        vec![CleanOption::All.code()]
861    } else {
862        what.iter().map(|s| s.as_str()).collect()
863    };
864
865    #[cfg(not(any(feature = "flatpak", feature = "appimage")))]
866    match level {
867        UnitDBusLevel::System | UnitDBusLevel::Both => {
868            if proxy_switcher::PROXY_SWITCHER.clean() {
869                proxy_call_blocking!(clean_unit, unit_name, &clean_what)
870            } else {
871                let proxy = systemd_manager();
872                proxy
873                    .clean_unit(unit_name, &clean_what)
874                    .map_err(|err| err.into())
875            }
876        }
877        UnitDBusLevel::UserSession => sysdbus::dbus_proxies::systemd_manager_session()
878            .clean_unit(unit_name, &clean_what)
879            .map_err(|err| err.into()),
880    }
881
882    #[cfg(any(feature = "flatpak", feature = "appimage"))]
883    {
884        use crate::sysdbus::dbus_proxies::systemd_manager_blocking;
885
886        systemd_manager_blocking(level)
887            .clean_unit(unit_name, &clean_what)
888            .map_err(|err| err.into())
889    }
890}
891
892pub fn mask_unit_files(
893    level: UnitDBusLevel,
894    primary_name: &str,
895    runtime: bool,
896    force: bool,
897) -> Result<Vec<DisEnAbleUnitFiles>, SystemdErrors> {
898    sysdbus::mask_unit_files(level, &[primary_name], runtime, force)
899}
900
901pub fn preset_unit_files(
902    level: UnitDBusLevel,
903    primary_name: &str,
904    runtime: bool,
905    force: bool,
906) -> Result<DisEnAbleUnitFilesResponse, SystemdErrors> {
907    sysdbus::preset_unit_file(level, &[primary_name], runtime, force)
908}
909
910pub fn reenable_unit_file(
911    level: UnitDBusLevel,
912    primary_name: &str,
913    runtime: bool,
914    force: bool,
915) -> Result<DisEnAbleUnitFilesResponse, SystemdErrors> {
916    sysdbus::reenable_unit_file(level, &[primary_name], runtime, force)
917}
918
919pub fn unmask_unit_files(
920    level: UnitDBusLevel,
921    primary_name: &str,
922    runtime: bool,
923) -> Result<Vec<DisEnAbleUnitFiles>, SystemdErrors> {
924    sysdbus::unmask_unit_files(level, &[primary_name], runtime)
925}
926
927pub fn link_unit_files(
928    dbus_level: UnitDBusLevel,
929    unit_file: &str,
930    runtime: bool,
931    force: bool,
932) -> Result<Vec<DisEnAbleUnitFiles>, SystemdErrors> {
933    sysdbus::link_unit_files(dbus_level, &[unit_file], runtime, force)
934}
935
936pub async fn daemon_reload(level: UnitDBusLevel) -> Result<(), SystemdErrors> {
937    #[cfg(not(any(feature = "flatpak", feature = "appimage")))]
938    if level.user_session() || !proxy_switcher::PROXY_SWITCHER.reload() {
939        info!("Reloading Daemon - Direct");
940        systemd_manager_async(level)
941            .await?
942            .reload()
943            .await
944            .map_err(|err| err.into())
945    } else {
946        info!("Reloading Daemon - Proxy");
947        println!("proxy");
948        proxy_call_async!(reload)
949    }
950
951    #[cfg(any(feature = "flatpak", feature = "appimage"))]
952    {
953        info!("Reloading Daemon - Direct");
954        systemd_manager_async(level)
955            .await?
956            .reload()
957            .await
958            .map_err(|err| err.into())
959    }
960}
961
962#[derive(Debug, PartialEq, Eq)]
963pub struct Dependency {
964    pub unit_name: String,
965    pub state: ActiveState,
966    pub children: BTreeSet<Dependency>,
967}
968
969impl Dependency {
970    pub fn new(unit_name: &str) -> Self {
971        Self {
972            unit_name: unit_name.to_string(),
973            state: ActiveState::Unknown,
974            children: BTreeSet::new(),
975        }
976    }
977
978    fn partial_clone(&self) -> Dependency {
979        Self {
980            unit_name: self.unit_name.clone(),
981            state: self.state,
982            children: BTreeSet::new(),
983        }
984    }
985}
986
987impl Ord for Dependency {
988    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
989        self.unit_name.cmp(&other.unit_name)
990    }
991}
992
993impl PartialOrd for Dependency {
994    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
995        Some(self.cmp(other))
996    }
997}
998
999pub fn fetch_unit_dependencies(
1000    level: UnitDBusLevel,
1001    primary_name: &str,
1002    object_path: &str,
1003    dependency_type: DependencyType,
1004    plain: bool,
1005) -> Result<Dependency, SystemdErrors> {
1006    sysdbus::unit_get_dependencies(level, primary_name, object_path, dependency_type, plain)
1007}
1008
1009pub fn get_unit_active_state(
1010    level: UnitDBusLevel,
1011    primary_name: &str,
1012) -> Result<ActiveState, SystemdErrors> {
1013    let object_path = sysdbus::unit_dbus_path_from_name(primary_name);
1014
1015    sysdbus::get_unit_active_state(level, &object_path)
1016}
1017
1018pub fn retreive_unit_processes(
1019    unit: &UnitInfo,
1020) -> Result<BTreeMap<String, BTreeSet<UnitProcess>>, SystemdErrors> {
1021    let level = unit.dbus_level();
1022
1023    let unit_processes = sysdbus::retreive_unit_processes(level, &unit.primary())?;
1024
1025    // let mut unit_processes_out = Vec::with_capacity(unit_processes.len());
1026    let mut unit_processes_map: BTreeMap<String, BTreeSet<UnitProcess>> = BTreeMap::new();
1027    for unit_process in unit_processes {
1028        let unit_process = {
1029            let Some(unit_name) = unit_process.path.rsplit_once('/').map(|a| a.1) else {
1030                warn!("No unit name for path {:?}", unit_process.path);
1031                continue;
1032            };
1033
1034            let unit_name_idx = unit_process.path.len() - unit_name.len();
1035
1036            UnitProcess {
1037                path: unit_process.path,
1038                pid: unit_process.pid,
1039                name: unit_process.name,
1040                unit_name: unit_name_idx,
1041            }
1042        };
1043
1044        if let Some(set) = unit_processes_map.get_mut(unit_process.unit_name()) {
1045            set.insert(unit_process);
1046        } else {
1047            let mut set = BTreeSet::new();
1048            let key = unit_process.unit_name().to_string();
1049            set.insert(unit_process);
1050            unit_processes_map.insert(key, set);
1051        }
1052    }
1053
1054    Ok(unit_processes_map)
1055}
1056
1057#[derive(Debug)]
1058pub struct SystemdSignalRow {
1059    pub time_stamp: u64,
1060    pub signal: SystemdSignal,
1061}
1062
1063impl SystemdSignalRow {
1064    pub fn new(signal: SystemdSignal) -> Self {
1065        let current_system_time = SystemTime::now();
1066        let since_the_epoch = current_system_time
1067            .duration_since(UNIX_EPOCH)
1068            .expect("Time went backwards");
1069        let time_stamp =
1070            since_the_epoch.as_secs() * 1_000_000 + since_the_epoch.subsec_nanos() as u64 / 1_000;
1071        SystemdSignalRow { time_stamp, signal }
1072    }
1073
1074    pub fn type_text(&self) -> &str {
1075        self.signal.type_text()
1076    }
1077
1078    pub fn details(&self) -> String {
1079        self.signal.details()
1080    }
1081}
1082
1083#[derive(Debug)]
1084pub enum SystemdSignal {
1085    UnitNew(String, OwnedObjectPath),
1086    UnitRemoved(String, OwnedObjectPath),
1087    JobNew(u32, OwnedObjectPath, String),
1088    JobRemoved(u32, OwnedObjectPath, String, String),
1089    StartupFinished(u64, u64, u64, u64, u64, u64),
1090    UnitFilesChanged,
1091    Reloading(bool),
1092}
1093
1094impl SystemdSignal {
1095    pub fn type_text(&self) -> &str {
1096        match self {
1097            SystemdSignal::UnitNew(_, _) => "UnitNew",
1098            SystemdSignal::UnitRemoved(_, _) => "UnitRemoved",
1099            SystemdSignal::JobNew(_, _, _) => "JobNew",
1100            SystemdSignal::JobRemoved(_, _, _, _) => "JobRemoved",
1101            SystemdSignal::StartupFinished(_, _, _, _, _, _) => "StartupFinished",
1102            SystemdSignal::UnitFilesChanged => "UnitFilesChanged",
1103            SystemdSignal::Reloading(_) => "Reloading",
1104        }
1105    }
1106
1107    pub fn details(&self) -> String {
1108        match self {
1109            SystemdSignal::UnitNew(id, unit) => format!("{id} {unit}"),
1110            SystemdSignal::UnitRemoved(id, unit) => format!("{id} {unit}"),
1111            SystemdSignal::JobNew(id, job, unit) => {
1112                format!("unit={unit} id={id} path={job}")
1113            }
1114            SystemdSignal::JobRemoved(id, job, unit, result) => {
1115                format!("unit={unit} id={id} path={job} result={result}")
1116            }
1117            SystemdSignal::StartupFinished(firmware, loader, kernel, initrd, userspace, total) => {
1118                format!(
1119                    "firmware={firmware} loader={loader} kernel={kernel} initrd={initrd} userspace={userspace} total={total}",
1120                )
1121            }
1122            SystemdSignal::UnitFilesChanged => String::new(),
1123            SystemdSignal::Reloading(active) => format!("firmware={active}"),
1124        }
1125    }
1126}
1127
1128pub async fn watch_systemd_signals(
1129    systemd_signal_sender: mpsc::Sender<SystemdSignalRow>,
1130    cancellation_token: tokio_util::sync::CancellationToken,
1131) {
1132    if let Err(err) =
1133        sysdbus::watcher::watch_systemd_signals(systemd_signal_sender, cancellation_token).await
1134    {
1135        error!("Error listening to jobs {err:?}");
1136    }
1137}
1138
1139pub async fn test(test_name: &str, level: UnitDBusLevel) {
1140    info!("Testing {test_name:?}");
1141
1142    if let Err(error) = sysdbus::test(test_name, level).await {
1143        error!("{error:#?}");
1144    }
1145}
1146
1147#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
1148pub struct UnitPropertyFetch {
1149    pub name: String,
1150    pub signature: String,
1151    pub access: String,
1152}
1153
1154impl UnitPropertyFetch {
1155    fn new(p: &zbus_xml::Property) -> Self {
1156        let access = match p.access() {
1157            zbus_xml::PropertyAccess::Read => "read",
1158            zbus_xml::PropertyAccess::Write => "write",
1159            zbus_xml::PropertyAccess::ReadWrite => "readwrite",
1160        };
1161
1162        UnitPropertyFetch {
1163            name: p.name().to_string(),
1164            signature: p.ty().to_string(),
1165            access: access.to_string(),
1166        }
1167    }
1168}
1169
1170pub async fn fetch_unit_interface_properties()
1171-> Result<BTreeMap<String, Vec<UnitPropertyFetch>>, SystemdErrors> {
1172    sysdbus::fetch_unit_interface_properties().await
1173}
1174
1175pub async fn fetch_unit_properties(
1176    level: UnitDBusLevel,
1177    unit_primary_name: &str,
1178    path: &str,
1179    unit_properties: UnitProperties,
1180    properties: Vec<(UnitType, &str, Quark)>,
1181) -> Result<Vec<UnitPropertySetter>, SystemdErrors> {
1182    sysdbus::fetch_unit_properties(level, unit_primary_name, path, unit_properties, properties)
1183        .await
1184}
1185
1186pub fn fetch_unit_property_blocking(
1187    level: UnitDBusLevel,
1188    unit_primary_name: &str,
1189    unit_type: UnitType,
1190    unit_property: &str,
1191) -> Result<OwnedValue, SystemdErrors> {
1192    sysdbus::fetch_unit_property_blocking(level, unit_primary_name, unit_type, unit_property)
1193}
1194
1195pub async fn create_drop_in(
1196    user_session: bool,
1197    runtime: bool,
1198    unit_name: &str,
1199    file_name: &str,
1200    content: &str,
1201) -> Result<String, SystemdErrors> {
1202    let file_path = create_drop_in_path_file(unit_name, runtime, user_session, file_name)?;
1203
1204    #[cfg(not(any(feature = "flatpak", feature = "appimage")))]
1205    let result = if user_session || !proxy_switcher::PROXY_SWITCHER.create_dropin() {
1206        file::create_drop_in(user_session, &file_path, content).await
1207    } else {
1208        proxy_call_async!(create_drop_in, runtime, unit_name, &file_path, content)
1209    };
1210
1211    #[cfg(any(feature = "flatpak", feature = "appimage"))]
1212    let result = file::create_drop_in(user_session, &file_path, content).await;
1213
1214    result.map(|_| file_path)
1215}
1216
1217pub async fn save_file(
1218    level: UnitDBusLevel,
1219    file_path: &str,
1220    content: &str,
1221) -> Result<u64, SystemdErrors> {
1222    info!("Saving file {file_path:?}");
1223
1224    let user_session = level.user_session();
1225    //TODO check the case of /run
1226
1227    #[cfg(not(any(feature = "flatpak", feature = "appimage")))]
1228    if user_session || !proxy_switcher::PROXY_SWITCHER.save_file() {
1229        save_text_to_file(file_path, content, user_session).await
1230    } else {
1231        proxy_call_async!(save_file, file_path, content)
1232    }
1233
1234    #[cfg(any(feature = "flatpak", feature = "appimage"))]
1235    save_text_to_file(file_path, content, user_session).await
1236}
1237
1238pub async fn revert_unit_file_full(
1239    level: UnitDBusLevel,
1240    unit_name: &str,
1241) -> Result<Vec<DisEnAbleUnitFiles>, SystemdErrors> {
1242    info!("Reverting unit file {unit_name:?}");
1243
1244    #[cfg(not(any(feature = "flatpak", feature = "appimage")))]
1245    if level.user_session() || !proxy_switcher::PROXY_SWITCHER.revert_unit_file() {
1246        systemd_manager_async(level)
1247            .await?
1248            .revert_unit_files(&[unit_name])
1249            .await
1250            .map_err(|err| err.into())
1251    } else {
1252        proxy_call_async!(revert_unit_files, &[unit_name])
1253    }
1254
1255    #[cfg(any(feature = "flatpak", feature = "appimage"))]
1256    {
1257        systemd_manager_async(level)
1258            .await?
1259            .revert_unit_files(&[unit_name])
1260            .await
1261            .map_err(|err| err.into())
1262    }
1263}
1264pub async fn fill_list_unit_files(
1265    level: UnitDBusLevel,
1266) -> Result<Vec<SystemdUnitFile>, SystemdErrors> {
1267    sysdbus::fill_list_unit_files(level).await
1268}