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