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)]
79pub 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 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 }
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#[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
361pub 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
373pub 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 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}
658pub 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 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 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
724pub 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 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
855pub 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 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_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 #[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}