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_paths(
259 level: UnitDBusLevel,
260) -> Result<ListUnitResponse, SystemdErrors> {
261 list_loaded_units_by_patterns(level, &["*.path"]).await
262}
263
264pub async fn list_loaded_units_automounts(
265 level: UnitDBusLevel,
266) -> Result<ListUnitResponse, SystemdErrors> {
267 list_loaded_units_by_patterns(level, &["*.automount"]).await
268}
269
270pub async fn list_loaded_units_list(
271 level: UnitDBusLevel,
272 unit_list: Vec<String>,
273) -> Result<ListUnitResponse, SystemdErrors> {
274 let unit_list: Vec<_> = unit_list.iter().map(|s| s.as_ref()).collect();
275 list_loaded_units_by_patterns(level, &unit_list).await
276}
277
278pub async fn list_unit_files(level: UnitDBusLevel) -> Result<ListUnitResponse, SystemdErrors> {
279 let v = systemd_manager_async(level)
280 .await?
281 .list_unit_files()
282 .await?;
283 Ok(ListUnitResponse::File(level, v))
284}
285
286pub async fn list_unit_files_by_patterns(
287 level: UnitDBusLevel,
288 patterns: &[&str],
289) -> Result<ListUnitResponse, SystemdErrors> {
290 let v = systemd_manager_async(level)
291 .await?
292 .list_unit_files_by_patterns(&[], patterns)
293 .await?;
294 Ok(ListUnitResponse::File(level, v))
295}
296
297pub async fn list_unit_files_timers(
298 level: UnitDBusLevel,
299) -> Result<ListUnitResponse, SystemdErrors> {
300 list_unit_files_by_patterns(level, &["*.timer"]).await
301}
302
303pub async fn list_unit_files_sockets(
304 level: UnitDBusLevel,
305) -> Result<ListUnitResponse, SystemdErrors> {
306 list_unit_files_by_patterns(level, &["*.socket"]).await
307}
308
309pub async fn list_unit_files_paths(
310 level: UnitDBusLevel,
311) -> Result<ListUnitResponse, SystemdErrors> {
312 list_unit_files_by_patterns(level, &["*.path"]).await
313}
314
315pub async fn list_unit_files_automounts(
316 level: UnitDBusLevel,
317) -> Result<ListUnitResponse, SystemdErrors> {
318 list_unit_files_by_patterns(level, &["*.automount"]).await
319}
320
321pub async fn list_unit_files_list(
322 level: UnitDBusLevel,
323 unit_list: Vec<String>,
324) -> Result<ListUnitResponse, SystemdErrors> {
325 let unit_list: Vec<_> = unit_list.iter().map(|s| s.as_ref()).collect();
326 list_unit_files_by_patterns(level, &unit_list).await
327}
328
329pub async fn complete_unit_information(
330 units: &[CompleteUnitPropertiesCallParams],
331) -> Result<Vec<UpdatedUnitInfo>, SystemdErrors> {
332 sysdbus::complete_unit_information(units).await
333}
334
335pub async fn complete_single_unit_information(
336 primary_name: String,
337 level: UnitDBusLevel,
338 object_path: String,
339 status: UnitFileStatus,
340) -> Result<Vec<UpdatedUnitInfo>, SystemdErrors> {
341 let units = [CompleteUnitPropertiesCallParams::new_params(
342 level,
343 primary_name,
344 object_path,
345 status,
346 )];
347 sysdbus::complete_unit_information(&units).await
348}
349
350pub fn start_unit(
354 level: UnitDBusLevel,
355 unit_name: &str,
356 mode: StartStopMode,
357) -> Result<String, SystemdErrors> {
358 runtime()
359 .block_on(async move { restartstop_unit(level, unit_name, mode, ReStartStop::Start).await })
360}
361
362pub fn stop_unit(
364 level: UnitDBusLevel,
365 unit_name: &str,
366 mode: StartStopMode,
367) -> Result<String, SystemdErrors> {
368 runtime()
369 .block_on(async move { restartstop_unit(level, unit_name, mode, ReStartStop::Stop).await })
370}
371
372#[derive(Debug)]
373pub enum ReStartStop {
374 Start,
375 Stop,
376 Restart,
377 ReloadUnit,
378}
379
380impl ReStartStop {
381 #[cfg(not(any(feature = "flatpak", feature = "appimage")))]
382 fn use_proxy(&self) -> bool {
383 use crate::proxy_switcher::PROXY_SWITCHER;
384
385 match self {
386 ReStartStop::Start => PROXY_SWITCHER.start(),
387 ReStartStop::Stop => PROXY_SWITCHER.stop(),
388 ReStartStop::Restart => PROXY_SWITCHER.restart(),
389 ReStartStop::ReloadUnit => PROXY_SWITCHER.reload_unit(),
390 }
391 }
392
393 #[cfg(not(any(feature = "flatpak", feature = "appimage")))]
394 async fn action<'a>(
395 &self,
396 proxy: &SysDManagerComLinkProxy<'a>,
397 unit_name: &str,
398 mode: StartStopMode,
399 ) -> Result<String, SystemdErrors> {
400 let path = match self {
401 ReStartStop::Start => proxy.start_unit(unit_name, mode.as_str()).await?,
402 ReStartStop::Stop => proxy.stop_unit(unit_name, mode.as_str()).await?,
403 ReStartStop::Restart => proxy.restart_unit(unit_name, mode.as_str()).await?,
404 ReStartStop::ReloadUnit => proxy.reload_unit(unit_name, mode.as_str()).await?,
405 };
406
407 Ok(path.to_string())
408 }
409
410 async fn systemd_action<'a>(
411 &self,
412 manager: &Systemd1ManagerProxy<'a>,
413 unit_name: &str,
414 mode: StartStopMode,
415 ) -> Result<String, SystemdErrors> {
416 let path = match self {
417 ReStartStop::Start => manager.start_unit(unit_name, mode.as_str()).await?,
418 ReStartStop::Stop => manager.stop_unit(unit_name, mode.as_str()).await?,
419 ReStartStop::Restart => manager.restart_unit(unit_name, mode.as_str()).await?,
420 ReStartStop::ReloadUnit => manager.reload_unit(unit_name, mode.as_str()).await?,
421 };
422
423 Ok(path.to_string())
424 }
425}
426
427pub async fn restartstop_unit(
428 level: UnitDBusLevel,
429 unit_name: &str,
430 mode: StartStopMode,
431 action: ReStartStop,
432) -> Result<String, SystemdErrors> {
433 let watcher = init_signal_watcher(level).await;
434 let job = restartstop_unit_call(level, unit_name, mode, &action).await?;
435 let job_id = job_number(&job).ok_or("Invalid Job Id for job: {job}")?;
436
437 let duration = Duration::from_secs(10);
438 timeout(duration, wait_job_removed(job_id, watcher))
439 .await
440 .map_err(|_err| SystemdErrors::Timeout(duration))
441 .and_then(|res| res.map(|_| job))
442 .inspect(|_job| {
443 #[cfg(not(any(feature = "flatpak", feature = "appimage")))]
444 if matches!(action, ReStartStop::Start | ReStartStop::Restart)
445 && unit_name.starts_with(PROXY_SERVICE)
446 {
447 to_proxy::start_heart_beat()
448 }
449 })
450}
451
452const DONE: &str = "done";
453const SKIPPED: &str = "skipped";
454const CANCELED: &str = "canceled";
455const TIMEOUT: &str = "timeout";
456const FAILED: &str = "failed";
457const DEPENDENCY: &str = "dependency";
458const INVALID: &str = "invalid";
459
460async fn wait_job_removed(
461 job_id: u32,
462 mut watcher: broadcast::Receiver<SystemdSignal>,
463) -> Result<(), SystemdErrors> {
464 loop {
465 match watcher.recv().await {
466 Ok(SystemdSignal::JobRemoved(_level, id, _, _unit, result)) if id == job_id => {
467 match result.as_str() {
468 DONE => {
469 break;
470 }
471 CANCELED => return Err(SystemdErrors::JobRemoved(CANCELED.to_owned())),
472 TIMEOUT => return Err(SystemdErrors::JobRemoved(TIMEOUT.to_owned())),
473 FAILED => return Err(SystemdErrors::JobRemoved(FAILED.to_owned())),
474 DEPENDENCY => return Err(SystemdErrors::JobRemoved(DEPENDENCY.to_owned())),
475 SKIPPED => return Err(SystemdErrors::JobRemoved(SKIPPED.to_owned())),
476 INVALID => return Err(SystemdErrors::JobRemoved(INVALID.to_owned())),
477 unkown_result => {
478 warn!("Unknown JobRemoved result {unkown_result}");
479 }
480 }
481 }
482 Ok(_) => {}
483 Err(RecvError::Lagged(lag)) => info!("Lagged {lag:?}"),
484 Err(err) => {
485 warn!("Recev Err {err:?}");
486 return Err(SystemdErrors::JobRemoved(format!("{err:?}")));
487 }
488 }
489 }
490 Ok(())
491}
492
493fn job_number(job: &str) -> Option<u32> {
494 job.rsplit_once('/').and_then(|(_, job_id)| {
495 job_id
496 .parse::<u32>()
497 .inspect_err(|err| warn!("Job {err:?}"))
498 .ok()
499 })
500}
501
502async fn restartstop_unit_call(
503 level: UnitDBusLevel,
504 unit_name: &str,
505 mode: StartStopMode,
506 action: &ReStartStop,
507) -> Result<String, SystemdErrors> {
508 #[cfg(not(any(feature = "flatpak", feature = "appimage")))]
509 match level {
510 UnitDBusLevel::System | UnitDBusLevel::Both => {
511 use crate::sysdbus::to_proxy::get_proxy_async;
512
513 let proxy = get_proxy_async().await?;
514 if action.use_proxy() && !unit_name.starts_with(PROXY_SERVICE) {
515 match action.action(&proxy, unit_name, mode).await {
518 Ok(ok) => Ok(ok),
519 Err(SystemdErrors::ZFdoServiceUnknowm(msg)) => {
520 warn!("Async ServiceUnkown: {:?} Function: {:?}", msg, action);
521 to_proxy::lazy_start_proxy_async().await;
522 action.action(&proxy, unit_name, mode).await
523 }
524 Err(err) => Err(err),
525 }
526 } else {
527 let manager = sysdbus::dbus_proxies::system_manager_system_async().await?;
528 action.systemd_action(manager, unit_name, mode).await
529 }
530 }
531
532 UnitDBusLevel::UserSession => {
533 let manager = sysdbus::dbus_proxies::system_manager_user_session_async().await?;
534 action.systemd_action(manager, unit_name, mode).await
535 }
536 }
537
538 #[cfg(any(feature = "flatpak", feature = "appimage"))]
539 {
540 let manager = sysdbus::dbus_proxies::systemd_manager_async(level).await?;
541 action.systemd_action(manager, unit_name, mode).await
542 }
543}
544
545pub fn disenable_unit_file(
546 primary_name: &str,
547 level: UnitDBusLevel,
548 enable_status: UnitFileStatus,
549 expected_status: UnitFileStatus,
550) -> Result<DisEnAbleUnitFilesResponse, SystemdErrors> {
551 match expected_status {
552 UnitFileStatus::Enabled | UnitFileStatus::EnabledRuntime => enable_unit_file(
553 level,
554 primary_name,
555 DisEnableFlags::SdSystemdUnitForce.into(),
556 ),
557 _ => {
558 let flags: BitFlags<DisEnableFlags> = if enable_status.is_runtime() {
559 DisEnableFlags::SdSystemdUnitRuntime.into()
560 } else {
561 DisEnableFlags::empty()
562 };
563
564 disable_unit_file(level, primary_name, flags)
565 }
566 }
567}
568
569pub fn enable_unit_file(
570 level: UnitDBusLevel,
571 unit_file: &str,
572 flags: BitFlags<DisEnableFlags>,
573) -> Result<DisEnAbleUnitFilesResponse, SystemdErrors> {
574 #[cfg(not(any(feature = "flatpak", feature = "appimage")))]
575 match level {
576 UnitDBusLevel::System | UnitDBusLevel::Both => {
577 if proxy_switcher::PROXY_SWITCHER.enable_unit_file() {
578 proxy_call_blocking!(
579 enable_unit_files_with_flags,
580 &[unit_file],
581 flags.bits_c() as u64
582 )
583 } else {
584 systemd_manager()
585 .enable_unit_files_with_flags(&[unit_file], flags.bits_c() as u64)
586 .map_err(|err| err.into())
587 }
588 }
589 UnitDBusLevel::UserSession => sysdbus::dbus_proxies::systemd_manager_session()
590 .enable_unit_files_with_flags(&[unit_file], flags.bits_c() as u64)
591 .map_err(|err| err.into()),
592 }
593
594 #[cfg(any(feature = "flatpak", feature = "appimage"))]
595 {
596 use crate::sysdbus::dbus_proxies::systemd_manager_blocking;
597 systemd_manager_blocking(level)
598 .enable_unit_files_with_flags(&[unit_file], flags.bits_c() as u64)
599 .map_err(|err| err.into())
600 }
601}
602
603pub fn disable_unit_file(
604 level: UnitDBusLevel,
605 unit_file: &str,
606 flags: BitFlags<DisEnableFlags>,
607) -> Result<DisEnAbleUnitFilesResponse, SystemdErrors> {
608 info!("{:?} {} {:?}", level, unit_file, flags.bits_c());
609 #[cfg(not(any(feature = "flatpak", feature = "appimage")))]
610 match level {
611 UnitDBusLevel::System | UnitDBusLevel::Both => {
612 if proxy_switcher::PROXY_SWITCHER.disable_unit_file() {
613 proxy_call_blocking!(
614 disable_unit_files_with_flags,
615 &[unit_file],
616 flags.bits_c() as u64
617 )
618 } else {
619 systemd_manager()
620 .disable_unit_files_with_flags_and_install_info(
621 &[unit_file],
622 flags.bits_c() as u64,
623 )
624 .map_err(|err| err.into())
625 }
626 }
627 UnitDBusLevel::UserSession => sysdbus::dbus_proxies::systemd_manager_session()
628 .disable_unit_files_with_flags_and_install_info(&[unit_file], flags.bits_c() as u64)
629 .map_err(|err| err.into()),
630 }
631
632 #[cfg(any(feature = "flatpak", feature = "appimage"))]
633 {
634 use crate::sysdbus::dbus_proxies::systemd_manager_blocking;
635 systemd_manager_blocking(level)
636 .disable_unit_files_with_flags_and_install_info(&[unit_file], flags.bits_c() as u64)
637 .map_err(|err| err.into())
638 }
639}
640
641pub async fn fetch_drop_in_paths(
642 level: UnitDBusLevel,
643 unit_name: &str,
644) -> Result<Vec<String>, SystemdErrors> {
645 sysdbus::fetch_drop_in_paths(level, unit_name).await
646}
647pub fn fetch_unit_file_content(
649 file_path: Option<&str>,
650 unit_primary_name: &str,
651) -> Result<String, SystemdErrors> {
652 let Some(file_path) = file_path else {
653 warn!("No file path for {:?}", unit_primary_name);
654 return Ok(String::new());
655 };
656
657 file_open_get_content(file_path, unit_primary_name)
658}
659
660#[allow(unused)]
661fn flatpak_file_open_get_content(
662 file_path: &str,
663 unit_primary_name: &str,
664) -> Result<String, SystemdErrors> {
665 file_open_get_content(file_path, unit_primary_name).or_else(|e| {
666 info!("Trying to fetch file content through 'cat' command, because {e:?}");
667 file_open_get_content_cat(file_path, unit_primary_name)
668 })
669}
670
671fn file_open_get_content_cat(
672 file_path: &str,
673 unit_primary_name: &str,
674) -> Result<String, SystemdErrors> {
675 info!(
676 "Flatpak Fetching file content Unit: {} File \"{file_path}\"",
677 unit_primary_name
678 );
679 commander_output(&["cat", file_path], None)
681 .map(|cat_output| String::from_utf8_lossy(&cat_output.stdout).to_string())
682 .inspect_err(|e| warn!("Can't open file {file_path:?} with 'cat' command, reason: {e:?}"))
683}
684
685fn file_open_get_content(
686 file_path: &str,
687 unit_primary_name: &str,
688) -> Result<String, SystemdErrors> {
689 let file_path = flatpak_host_file_path(file_path);
691
692 info!(
693 "Fetching file content Unit: {} File: {}",
694 unit_primary_name,
695 file_path.display()
696 );
697
698 let mut file = File::open(&file_path).map_err(|e| {
699 warn!(
700 "Can't open file \"{}\", reason: {e} {:?}",
701 file_path.display(),
702 e.kind()
703 );
704 SystemdErrors::IoError(e)
705 })?;
706
707 let mut output = String::new();
708 let _ = file.read_to_string(&mut output);
709
710 Ok(output)
711}
712
713pub fn get_unit_journal(
715 primary_name: String,
716 level: UnitDBusLevel,
717 boot_filter: BootFilter,
718 range: EventRange,
719 message_max_char: usize,
720 timestamp_style: TimestampStyle,
721) -> Result<JournalEventChunk, SystemdErrors> {
722 journal::get_unit_journal_events(
723 primary_name,
724 level,
725 boot_filter,
726 range,
727 message_max_char,
728 timestamp_style,
729 )
730}
731
732#[allow(clippy::too_many_arguments)]
733pub fn get_unit_journal_continuous(
734 unit_name: String,
735 level: UnitDBusLevel,
736 range: EventRange,
737 journal_continuous_receiver: std::sync::mpsc::Receiver<()>,
738 sender: std::sync::mpsc::Sender<JournalEventChunk>,
739 message_max_char: usize,
740 timestamp_style: TimestampStyle,
741 check_for_new_journal_entry: fn(),
742) {
743 if let Err(err) = journal::get_unit_journal_events_continuous(
744 unit_name,
745 level,
746 range,
747 journal_continuous_receiver,
748 sender,
749 message_max_char,
750 timestamp_style,
751 check_for_new_journal_entry,
752 ) {
753 warn!(
754 "Journal TailError type: {:?} Error: {:?}",
755 err.type_id(),
756 err
757 );
758 } else {
759 warn!("Ok journal tail thread finished");
760 }
761}
762
763pub fn list_boots() -> Result<Vec<Boot>, SystemdErrors> {
764 journal::list_boots()
765}
766
767pub fn fetch_last_time() -> Result<u64, SystemdErrors> {
768 journal::fetch_last_time()
769}
770
771pub fn commander_output(
772 prog_n_args: &[&str],
773 environment_variables: Option<&[(&str, &str)]>,
774) -> Result<std::process::Output, SystemdErrors> {
775 match commander_blocking(prog_n_args, environment_variables).output() {
776 Ok(output) => {
777 if cfg!(feature = "flatpak") {
778 info!("Command Exit status: {}", output.status);
779
780 if !output.status.success() {
781 warn!("Flatpak mode, command line did not succeed, please investigate.");
782 error!("Command exit status: {}", output.status);
783 info!(
784 "{}",
785 String::from_utf8(output.stdout).expect("from_utf8 failed")
786 );
787 error!(
788 "{}",
789 String::from_utf8(output.stderr).expect("from_utf8 failed")
790 );
791 let vec = prog_n_args.iter().map(|s| s.to_string()).collect();
792 return Err(SystemdErrors::CmdNoFreedesktopFlatpakPermission(
793 Some(vec),
794 None,
795 ));
796 }
797 }
798 Ok(output)
799 }
800 Err(err) => {
801 error!("commander_output {err}");
802
803 match test_flatpak_spawn() {
804 Ok(()) => Err(SystemdErrors::IoError(err)),
805 Err(e1) => {
806 error!("commander_output e1 {e1}");
807 Err(SystemdErrors::CmdNoFlatpakSpawn)
808 }
809 }
810 }
811 }
812}
813
814pub fn generate_file_uri(file_path: &str) -> String {
815 let flatpak_host_file_path = flatpak_host_file_path(file_path);
816 format!("file://{}", flatpak_host_file_path.display())
817}
818
819pub fn fetch_system_info() -> Result<Vec<(UnitType, String, String)>, SystemdErrors> {
820 sysdbus::fetch_system_info(UnitDBusLevel::System)
822}
823
824pub fn fetch_system_unit_info_native(
825 unit: &UnitInfo,
826) -> Result<Vec<(UnitType, String, OwnedValue)>, SystemdErrors> {
827 let level = unit.dbus_level();
828 let unit_type: UnitType = unit.unit_type();
829 let object_path = unit.object_path();
830
831 sysdbus::fetch_system_unit_info_native(level, &object_path, unit_type)
832}
833
834pub fn fetch_system_unit_info_native_map(
835 unit: &UnitInfo,
836) -> Result<HashMap<String, OwnedValue>, SystemdErrors> {
837 let level = unit.dbus_level();
838 let unit_type: UnitType = unit.unit_type();
839 let object_path = unit.object_path();
840
841 sysdbus::fetch_system_unit_info_native_map(level, &object_path, unit_type)
842}
843
844pub fn fetch_unit(
856 level: UnitDBusLevel,
857 unit_primary_name: &str,
858) -> Result<UnitInfo, SystemdErrors> {
859 sysdbus::fetch_unit(level, unit_primary_name)
860}
861
862pub fn kill_unit(
863 level: UnitDBusLevel,
864 primary_name: &str,
865 who: KillWho,
866 signal: i32,
867) -> Result<(), SystemdErrors> {
868 sysdbus::kill_unit(level, primary_name, who, signal)
869}
870
871pub fn freeze_unit(params: Option<(UnitDBusLevel, String)>) -> Result<(), SystemdErrors> {
872 if let Some((_level, primary_name)) = params {
873 #[cfg(not(any(feature = "flatpak", feature = "appimage")))]
874 match _level {
875 UnitDBusLevel::System | UnitDBusLevel::Both => {
876 if proxy_switcher::PROXY_SWITCHER.freeze() {
877 proxy_call_blocking!(freeze_unit, &primary_name)
878 } else {
879 let proxy = systemd_manager();
880 proxy.freeze_unit(&primary_name)?;
881 Ok(())
882 }
883 }
884 UnitDBusLevel::UserSession => sysdbus::dbus_proxies::systemd_manager_session()
885 .freeze_unit(&primary_name)
886 .map_err(|err| err.into()),
887 }
888
889 #[cfg(any(feature = "flatpak", feature = "appimage"))]
890 {
891 let proxy = systemd_manager();
892 proxy.freeze_unit(&primary_name)?;
893 Ok(())
894 }
895 } else {
896 Err(SystemdErrors::NoUnit)
897 }
898}
899
900pub fn thaw_unit(params: Option<(UnitDBusLevel, String)>) -> Result<(), SystemdErrors> {
901 let Some((level, primary_name)) = params else {
902 return Err(SystemdErrors::NoUnit);
903 };
904
905 #[cfg(not(any(feature = "flatpak", feature = "appimage")))]
906 match level {
907 UnitDBusLevel::System | UnitDBusLevel::Both => {
908 if proxy_switcher::PROXY_SWITCHER.thaw() {
909 proxy_call_blocking!(thaw_unit, &primary_name)
910 } else {
911 let proxy = systemd_manager();
912 proxy.thaw_unit(&primary_name)?;
913 Ok(())
914 }
915 }
916 UnitDBusLevel::UserSession => sysdbus::dbus_proxies::systemd_manager_session()
917 .thaw_unit(&primary_name)
918 .map_err(|err| err.into()),
919 }
920
921 #[cfg(any(feature = "flatpak", feature = "appimage"))]
922 {
923 use crate::sysdbus::dbus_proxies::systemd_manager_blocking;
924 let proxy = systemd_manager_blocking(level);
925 proxy.thaw_unit(&primary_name)?;
926 Ok(())
927 }
928}
929
930pub fn reload_unit(
931 level: UnitDBusLevel,
932 primary_name: &str,
933 mode: StartStopMode,
934) -> Result<String, SystemdErrors> {
935 sysdbus::reload_unit(level, primary_name, mode.as_str())
936}
937
938pub fn queue_signal_unit(
939 level: UnitDBusLevel,
940 primary_name: &str,
941 who: KillWho,
942 signal: i32,
943 value: i32,
944) -> Result<(), SystemdErrors> {
945 sysdbus::queue_signal_unit(level, primary_name, who, signal, value)
946}
947
948pub fn clean_unit(
949 level: UnitDBusLevel,
950 unit_name: &str,
951 what: &[String],
952) -> Result<(), SystemdErrors> {
953 let mut what_peekable = what
955 .iter()
956 .filter(|c_op| *c_op == CleanOption::All.code())
957 .peekable();
958
959 let clean_what: Vec<&str> = if what_peekable.peek().is_some() {
960 vec![CleanOption::All.code()]
961 } else {
962 what.iter().map(|s| s.as_str()).collect()
963 };
964
965 #[cfg(not(any(feature = "flatpak", feature = "appimage")))]
966 match level {
967 UnitDBusLevel::System | UnitDBusLevel::Both => {
968 if proxy_switcher::PROXY_SWITCHER.clean() {
969 proxy_call_blocking!(clean_unit, unit_name, &clean_what)
970 } else {
971 let proxy = systemd_manager();
972 proxy
973 .clean_unit(unit_name, &clean_what)
974 .map_err(|err| err.into())
975 }
976 }
977 UnitDBusLevel::UserSession => sysdbus::dbus_proxies::systemd_manager_session()
978 .clean_unit(unit_name, &clean_what)
979 .map_err(|err| err.into()),
980 }
981
982 #[cfg(any(feature = "flatpak", feature = "appimage"))]
983 {
984 use crate::sysdbus::dbus_proxies::systemd_manager_blocking;
985
986 systemd_manager_blocking(level)
987 .clean_unit(unit_name, &clean_what)
988 .map_err(|err| err.into())
989 }
990}
991
992pub fn mask_unit_files(
993 level: UnitDBusLevel,
994 primary_name: &str,
995 runtime: bool,
996 force: bool,
997) -> Result<Vec<DisEnAbleUnitFiles>, SystemdErrors> {
998 sysdbus::mask_unit_files(level, &[primary_name], runtime, force)
999}
1000
1001pub fn preset_unit_files(
1002 level: UnitDBusLevel,
1003 primary_name: &str,
1004 runtime: bool,
1005 force: bool,
1006) -> Result<DisEnAbleUnitFilesResponse, SystemdErrors> {
1007 sysdbus::preset_unit_file(level, &[primary_name], runtime, force)
1008}
1009
1010pub fn reenable_unit_file(
1011 level: UnitDBusLevel,
1012 primary_name: &str,
1013 runtime: bool,
1014 force: bool,
1015) -> Result<DisEnAbleUnitFilesResponse, SystemdErrors> {
1016 sysdbus::reenable_unit_file(level, &[primary_name], runtime, force)
1017}
1018
1019pub fn unmask_unit_files(
1020 level: UnitDBusLevel,
1021 primary_name: &str,
1022 runtime: bool,
1023) -> Result<Vec<DisEnAbleUnitFiles>, SystemdErrors> {
1024 sysdbus::unmask_unit_files(level, &[primary_name], runtime)
1025}
1026
1027pub fn link_unit_files(
1028 dbus_level: UnitDBusLevel,
1029 unit_file: &str,
1030 runtime: bool,
1031 force: bool,
1032) -> Result<Vec<DisEnAbleUnitFiles>, SystemdErrors> {
1033 sysdbus::link_unit_files(dbus_level, &[unit_file], runtime, force)
1034}
1035
1036pub async fn daemon_reload(level: UnitDBusLevel) -> Result<(), SystemdErrors> {
1037 let mut watcher = init_signal_watcher(level).await;
1038 daemon_reload_core(level).await?;
1039
1040 let mut wait_reload = async || {
1041 loop {
1042 match watcher.recv().await {
1043 Ok(SystemdSignal::Reloading(_, active)) => {
1044 if active {
1045 info!("Reloading!");
1046 } else {
1047 info!("Reload Finised");
1048 break;
1049 }
1050 }
1051 Ok(_) => {}
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}