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