1pub mod analyze;
2pub mod data;
3pub mod enums;
4pub mod errors;
5mod file;
6mod journal;
7pub mod journal_data;
8pub mod manager;
9pub mod sysdbus;
10pub mod time_handling;
11
12use std::{
13 any::Any,
14 collections::{BTreeMap, BTreeSet, HashMap},
15 fs::File,
16 io::Read,
17 sync::OnceLock,
18 time::{SystemTime, UNIX_EPOCH},
19};
20
21use crate::{
22 enums::{ActiveState, EnablementStatus, LoadState, StartStopMode},
23 file::save_text_to_file,
24 journal_data::Boot,
25 sysdbus::dbus_proxies::systemd_manager,
26 time_handling::TimestampStyle,
27};
28
29#[cfg(not(feature = "flatpak"))]
30use crate::sysdbus::to_proxy;
31use base::{
32 RunMode,
33 enums::UnitDBusLevel,
34 file::{commander, commander_blocking, flatpak_host_file_path, test_flatpak_spawn},
35 proxy::DisEnAbleUnitFiles,
36};
37use data::{UnitInfo, UnitProcess};
38use enumflags2::{BitFlag, BitFlags};
39use enums::{CleanOption, DependencyType, DisEnableFlags, KillWho, UnitType};
40use errors::SystemdErrors;
41
42use journal_data::{EventRange, JournalEventChunk};
43use log::{error, info, warn};
44
45use tokio::{runtime::Runtime, sync::mpsc};
46use zvariant::{OwnedObjectPath, OwnedValue};
47
48use crate::data::{EnableUnitFilesReturn, LUnit};
49
50#[derive(Default, Clone, PartialEq, Debug)]
51pub enum BootFilter {
52 #[default]
53 Current,
54 All,
55 Id(String),
56}
57
58#[derive(Clone, Debug)]
59#[allow(unused)]
60pub struct SystemdUnitFile {
61 pub full_name: String,
62 pub status_code: EnablementStatus,
63 pub level: UnitDBusLevel,
64 pub path: String,
65}
66
67#[derive(Debug, Default)]
68pub struct UpdatedUnitInfo {
69 pub primary: String,
70 pub object_path: String,
71 pub description: Option<String>,
72 pub load_state: Option<LoadState>,
73 pub sub_state: Option<String>,
74 pub active_state: Option<ActiveState>,
75 pub unit_file_preset: Option<String>,
76 pub valid_unit_name: bool,
77 pub fragment_path: Option<String>,
78 pub enablement_status: Option<EnablementStatus>,
79}
80
81impl UpdatedUnitInfo {
82 fn new(primary: String, object_path: String) -> Self {
83 Self {
84 primary,
85 object_path,
86 ..Default::default()
87 }
88 }
89}
90
91pub fn runtime() -> &'static Runtime {
92 static RUNTIME: OnceLock<Runtime> = OnceLock::new();
93 RUNTIME.get_or_init(|| Runtime::new().expect("Setting up tokio runtime needs to succeed."))
94}
95
96pub async fn init_async(run_mode: RunMode) {
101 let _ = sysdbus::init_async(run_mode)
102 .await
103 .inspect_err(|e| error!("Some err {e:?}"));
104}
105
106pub fn shut_down() {
107 sysdbus::shut_down();
108}
109
110pub fn get_unit_file_state(
111 level: UnitDBusLevel,
112 primary_name: &str,
113) -> Result<EnablementStatus, SystemdErrors> {
114 sysdbus::get_unit_file_state(level, primary_name)
115}
116
117pub async fn list_units_description_and_state_async(
135 level: UnitDBusLevel,
136) -> Result<(Vec<LUnit>, Vec<SystemdUnitFile>), SystemdErrors> {
137 sysdbus::list_units_description_and_state_async(level).await
138}
139
140pub async fn complete_unit_information(
141 units: Vec<(UnitDBusLevel, String, String)>,
142) -> Result<Vec<UpdatedUnitInfo>, SystemdErrors> {
143 sysdbus::complete_unit_information(units).await
144}
145
146pub async fn complete_single_unit_information(
147 primary_name: String,
148 level: UnitDBusLevel,
149 object_path: String,
150) -> Result<Vec<UpdatedUnitInfo>, SystemdErrors> {
151 let units = vec![(level, primary_name, object_path)];
152 sysdbus::complete_unit_information(units).await
153}
154
155pub fn start_unit(
159 level: UnitDBusLevel,
160 unit_name: &str,
161 mode: StartStopMode,
162) -> Result<String, SystemdErrors> {
163 start_unit_name(level, unit_name, mode)
164}
165
166pub fn start_unit_name(
170 level: UnitDBusLevel,
171 unit_name: &str,
172 mode: StartStopMode,
173) -> Result<String, SystemdErrors> {
174 sysdbus::start_unit(level, unit_name, mode)
175}
176
177pub fn stop_unit(
179 level: UnitDBusLevel,
180 primary_name: &str,
181 mode: StartStopMode,
182) -> Result<String, SystemdErrors> {
183 sysdbus::stop_unit(level, primary_name, mode)
184}
185
186pub fn restart_unit(
187 level: UnitDBusLevel,
188 primary_name: &str,
189 mode: StartStopMode,
190) -> Result<String, SystemdErrors> {
191 sysdbus::restart_unit(level, primary_name, mode)
192}
193
194#[allow(dead_code)]
195#[derive(Debug)]
196pub enum DisEnableUnitFilesOutput {
197 Enable(EnableUnitFilesReturn),
198 Disable(Vec<DisEnAbleUnitFiles>),
199}
200
201pub fn disenable_unit_file(
202 primary_name: &str,
203 level: UnitDBusLevel,
204 enable_status: EnablementStatus,
205 expected_status: EnablementStatus,
206) -> Result<DisEnableUnitFilesOutput, SystemdErrors> {
207 let msg_return = match expected_status {
208 EnablementStatus::Enabled | EnablementStatus::EnabledRuntime => {
209 let res = sysdbus::enable_unit_files(
210 level,
211 &[primary_name],
212 DisEnableFlags::SdSystemdUnitForce.into(),
213 )?;
214 DisEnableUnitFilesOutput::Enable(res)
215 }
216 _ => {
217 let flags: BitFlags<DisEnableFlags> = if enable_status.is_runtime() {
218 DisEnableFlags::SdSystemdUnitRuntime.into()
219 } else {
220 DisEnableFlags::empty()
221 };
222
223 let out = sysdbus::disable_unit_files(level, &[primary_name], flags)?;
224 DisEnableUnitFilesOutput::Disable(out)
225 }
226 };
227
228 Ok(msg_return)
229}
230
231pub fn enable_unit_file(
232 level: UnitDBusLevel,
233 unit_file: &str,
234 flags: BitFlags<DisEnableFlags>,
235) -> Result<EnableUnitFilesReturn, SystemdErrors> {
236 sysdbus::enable_unit_files(level, &[unit_file], flags)
237}
238
239pub fn disable_unit_files(
240 level: UnitDBusLevel,
241 unit_file: &str,
242 flags: BitFlags<DisEnableFlags>,
243) -> Result<Vec<DisEnAbleUnitFiles>, SystemdErrors> {
244 sysdbus::disable_unit_files(level, &[unit_file], flags)
245}
246
247pub async fn fetch_drop_in_paths(
248 level: UnitDBusLevel,
249 unit_name: &str,
250) -> Result<Vec<String>, SystemdErrors> {
251 sysdbus::fetch_drop_in_paths(level, unit_name).await
252}
253pub fn get_unit_file_info(
255 file_path: Option<&str>,
256 unit_primary_name: &str,
257) -> Result<String, SystemdErrors> {
258 let Some(file_path) = file_path else {
259 warn!("No file path for {:?}", unit_primary_name);
260 return Ok(String::new());
261 };
262
263 file_open_get_content(file_path, unit_primary_name)
264}
265
266#[allow(unused)]
267fn flatpak_file_open_get_content(
268 file_path: &str,
269 unit_primary_name: &str,
270) -> Result<String, SystemdErrors> {
271 file_open_get_content(file_path, unit_primary_name).or_else(|e| {
272 info!("Trying to fetch file content through 'cat' command, because {e:?}");
273 file_open_get_content_cat(file_path, unit_primary_name)
274 })
275}
276
277fn file_open_get_content_cat(
278 file_path: &str,
279 unit_primary_name: &str,
280) -> Result<String, SystemdErrors> {
281 info!(
282 "Flatpack Fetching file content Unit: {} File \"{file_path}\"",
283 unit_primary_name
284 );
285 commander_output(&["cat", file_path], None)
287 .map(|cat_output| String::from_utf8_lossy(&cat_output.stdout).to_string())
288 .inspect_err(|e| warn!("Can't open file {file_path:?} with 'cat' command, reason: {e:?}"))
289}
290
291fn file_open_get_content(
292 file_path: &str,
293 unit_primary_name: &str,
294) -> Result<String, SystemdErrors> {
295 let file_path = flatpak_host_file_path(file_path);
297
298 info!(
299 "Fetching file content Unit: {} File: {}",
300 unit_primary_name,
301 file_path.display()
302 );
303
304 let mut file = File::open(&file_path).map_err(|e| {
305 warn!(
306 "Can't open file \"{}\", reason: {e} {:?}",
307 file_path.display(),
308 e.kind()
309 );
310 SystemdErrors::IoError(e)
311 })?;
312
313 let mut output = String::new();
314 let _ = file.read_to_string(&mut output);
315
316 Ok(output)
317}
318
319pub fn get_unit_journal(
321 primary_name: String,
322 level: UnitDBusLevel,
323 boot_filter: BootFilter,
324 range: EventRange,
325 message_max_char: usize,
326 timestamp_style: TimestampStyle,
327) -> Result<JournalEventChunk, SystemdErrors> {
328 journal::get_unit_journal_events(
329 primary_name,
330 level,
331 boot_filter,
332 range,
333 message_max_char,
334 timestamp_style,
335 )
336}
337
338#[allow(clippy::too_many_arguments)]
339pub fn get_unit_journal_continuous(
340 unit_name: String,
341 level: UnitDBusLevel,
342 range: EventRange,
343 journal_continuous_receiver: std::sync::mpsc::Receiver<()>,
344 sender: std::sync::mpsc::Sender<JournalEventChunk>,
345 message_max_char: usize,
346 timestamp_style: TimestampStyle,
347 check_for_new_journal_entry: fn(),
348) {
349 if let Err(err) = journal::get_unit_journal_events_continuous(
350 unit_name,
351 level,
352 range,
353 journal_continuous_receiver,
354 sender,
355 message_max_char,
356 timestamp_style,
357 check_for_new_journal_entry,
358 ) {
359 warn!(
360 "Journal TailError type: {:?} Error: {:?}",
361 err.type_id(),
362 err
363 );
364 } else {
365 warn!("Ok journal tail thread finished");
366 }
367}
368
369pub fn list_boots() -> Result<Vec<Boot>, SystemdErrors> {
370 journal::list_boots()
371}
372
373pub fn fetch_last_time() -> Result<u64, SystemdErrors> {
374 journal::fetch_last_time()
375}
376
377pub fn commander_output(
378 prog_n_args: &[&str],
379 environment_variables: Option<&[(&str, &str)]>,
380) -> Result<std::process::Output, SystemdErrors> {
381 match commander_blocking(prog_n_args, environment_variables).output() {
382 Ok(output) => {
383 if cfg!(feature = "flatpak") {
384 info!("Command Exit status: {}", output.status);
385
386 if !output.status.success() {
387 warn!("Flatpak mode, command line did not succeed, please investigate.");
388 error!("Command exit status: {}", output.status);
389 info!(
390 "{}",
391 String::from_utf8(output.stdout).expect("from_utf8 failed")
392 );
393 error!(
394 "{}",
395 String::from_utf8(output.stderr).expect("from_utf8 failed")
396 );
397 let vec = prog_n_args.iter().map(|s| s.to_string()).collect();
398 return Err(SystemdErrors::CmdNoFreedesktopFlatpakPermission(
399 Some(vec),
400 None,
401 ));
402 }
403 }
404 Ok(output)
405 }
406 Err(err) => {
407 error!("commander_output {err}");
408
409 match test_flatpak_spawn() {
410 Ok(()) => Err(SystemdErrors::IoError(err)),
411 Err(e1) => {
412 error!("commander_output e1 {e1}");
413 Err(SystemdErrors::CmdNoFlatpakSpawn)
414 }
415 }
416 }
417 }
418}
419
420pub fn generate_file_uri(file_path: &str) -> String {
421 let flatpak_host_file_path = flatpak_host_file_path(file_path);
422 format!("file://{}", flatpak_host_file_path.display())
423}
424
425pub fn fetch_system_info() -> Result<BTreeMap<String, String>, SystemdErrors> {
426 sysdbus::fetch_system_info(UnitDBusLevel::System)
428}
429
430pub fn fetch_system_unit_info_native(
431 unit: &UnitInfo,
432) -> Result<HashMap<String, OwnedValue>, SystemdErrors> {
433 let level = unit.dbus_level();
434 let unit_type: UnitType = unit.unit_type();
435
436 let object_path = unit.object_path();
437
438 sysdbus::fetch_system_unit_info_native(level, &object_path, unit_type)
439}
440
441pub fn fetch_unit(
453 level: UnitDBusLevel,
454 unit_primary_name: &str,
455) -> Result<UnitInfo, SystemdErrors> {
456 sysdbus::fetch_unit(level, unit_primary_name)
457}
458
459pub fn kill_unit(
460 level: UnitDBusLevel,
461 primary_name: &str,
462 who: KillWho,
463 signal: i32,
464) -> Result<(), SystemdErrors> {
465 sysdbus::kill_unit(level, primary_name, who, signal)
466}
467
468pub fn freeze_unit(params: Option<(UnitDBusLevel, String)>) -> Result<(), SystemdErrors> {
469 if let Some((_level, primary_name)) = params {
470 #[cfg(not(feature = "flatpak"))]
471 match _level {
472 UnitDBusLevel::System | UnitDBusLevel::Both => to_proxy::freeze_unit(&primary_name),
473 UnitDBusLevel::UserSession => {
474 let proxy = systemd_manager();
475 proxy.freeze_unit(&primary_name)?;
476 Ok(())
477 }
478 }
479
480 #[cfg(feature = "flatpak")]
481 {
482 let proxy = systemd_manager();
483 proxy.freeze_unit(&primary_name)?;
484 Ok(())
485 }
486 } else {
487 Err(SystemdErrors::NoUnit)
488 }
489}
490
491pub fn thaw_unit(params: Option<(UnitDBusLevel, String)>) -> Result<(), SystemdErrors> {
492 if let Some((_level, primary_name)) = params {
493 #[cfg(not(feature = "flatpak"))]
494 match _level {
495 UnitDBusLevel::System | UnitDBusLevel::Both => to_proxy::thaw_unit(&primary_name),
496 UnitDBusLevel::UserSession => {
497 let proxy = systemd_manager();
498 proxy.thaw_unit(&primary_name)?;
499 Ok(())
500 }
501 }
502
503 #[cfg(feature = "flatpak")]
504 {
505 let proxy = systemd_manager();
506 proxy.thaw_unit(&primary_name)?;
507 Ok(())
508 }
509 } else {
510 Err(SystemdErrors::NoUnit)
511 }
512}
513
514pub fn reload_unit(
515 level: UnitDBusLevel,
516 primary_name: &str,
517 mode: StartStopMode,
518) -> Result<String, SystemdErrors> {
519 sysdbus::reload_unit(level, primary_name, mode.as_str())
520}
521
522pub fn queue_signal_unit(
523 level: UnitDBusLevel,
524 primary_name: &str,
525 who: KillWho,
526 signal: i32,
527 value: i32,
528) -> Result<(), SystemdErrors> {
529 sysdbus::queue_signal_unit(level, primary_name, who, signal, value)
530}
531
532pub fn clean_unit(
533 _level: UnitDBusLevel,
534 unit_name: &str,
535 what: &[String],
536) -> Result<(), SystemdErrors> {
537 let mut what_peekable = what
539 .iter()
540 .filter(|c_op| *c_op == CleanOption::All.code())
541 .peekable();
542
543 let clean_what: Vec<&str> = if what_peekable.peek().is_some() {
544 vec![CleanOption::All.code()]
545 } else {
546 what.iter().map(|s| s.as_str()).collect()
547 };
548
549 #[cfg(not(feature = "flatpak"))]
550 match _level {
551 UnitDBusLevel::System | UnitDBusLevel::Both => to_proxy::clean_unit(unit_name, &clean_what),
552 UnitDBusLevel::UserSession => {
553 let proxy = systemd_manager();
554 proxy.clean_unit(unit_name, &clean_what)?;
555 Ok(())
556 }
557 }
558
559 #[cfg(feature = "flatpak")]
560 {
561 let proxy = systemd_manager();
562 proxy.clean_unit(unit_name, &clean_what)?;
563 Ok(())
564 }
565}
566
567pub fn mask_unit_files(
568 level: UnitDBusLevel,
569 primary_name: &str,
570 runtime: bool,
571 force: bool,
572) -> Result<Vec<DisEnAbleUnitFiles>, SystemdErrors> {
573 sysdbus::mask_unit_files(level, &[primary_name], runtime, force)
574}
575
576pub fn preset_unit_files(
577 level: UnitDBusLevel,
578 primary_name: &str,
579 runtime: bool,
580 force: bool,
581) -> Result<EnableUnitFilesReturn, SystemdErrors> {
582 sysdbus::preset_unit_file(level, &[primary_name], runtime, force)
583}
584
585pub fn reenable_unit_file(
586 level: UnitDBusLevel,
587 primary_name: &str,
588 runtime: bool,
589 force: bool,
590) -> Result<EnableUnitFilesReturn, SystemdErrors> {
591 sysdbus::reenable_unit_file(level, &[primary_name], runtime, force)
592}
593
594pub fn unmask_unit_files(
595 level: UnitDBusLevel,
596 primary_name: &str,
597 runtime: bool,
598) -> Result<Vec<DisEnAbleUnitFiles>, SystemdErrors> {
599 sysdbus::unmask_unit_files(level, &[primary_name], runtime)
600}
601
602pub fn link_unit_files(
603 dbus_level: UnitDBusLevel,
604 unit_file: &str,
605 runtime: bool,
606 force: bool,
607) -> Result<Vec<DisEnAbleUnitFiles>, SystemdErrors> {
608 sysdbus::link_unit_files(dbus_level, &[unit_file], runtime, force)
609}
610
611pub async fn daemon_reload(level: UnitDBusLevel) -> Result<(), SystemdErrors> {
612 sysdbus::daemon_reload(level).await
613}
614
615#[derive(Debug, PartialEq, Eq)]
616pub struct Dependency {
617 pub unit_name: String,
618 pub state: ActiveState,
619 pub children: BTreeSet<Dependency>,
620}
621
622impl Dependency {
623 pub fn new(unit_name: &str) -> Self {
624 Self {
625 unit_name: unit_name.to_string(),
626 state: ActiveState::Unknown,
627 children: BTreeSet::new(),
628 }
629 }
630
631 fn partial_clone(&self) -> Dependency {
632 Self {
633 unit_name: self.unit_name.clone(),
634 state: self.state,
635 children: BTreeSet::new(),
636 }
637 }
638}
639
640impl Ord for Dependency {
641 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
642 self.unit_name.cmp(&other.unit_name)
643 }
644}
645
646impl PartialOrd for Dependency {
647 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
648 Some(self.cmp(other))
649 }
650}
651
652pub fn fetch_unit_dependencies(
653 level: UnitDBusLevel,
654 primary_name: &str,
655 object_path: &str,
656 dependency_type: DependencyType,
657 plain: bool,
658) -> Result<Dependency, SystemdErrors> {
659 sysdbus::unit_get_dependencies(level, primary_name, object_path, dependency_type, plain)
660}
661
662pub fn get_unit_active_state(
663 level: UnitDBusLevel,
664 primary_name: &str,
665) -> Result<ActiveState, SystemdErrors> {
666 let object_path = sysdbus::unit_dbus_path_from_name(primary_name);
667
668 sysdbus::get_unit_active_state(level, &object_path)
669}
670
671pub fn retreive_unit_processes(
672 unit: &UnitInfo,
673) -> Result<BTreeMap<String, BTreeSet<UnitProcess>>, SystemdErrors> {
674 let level = unit.dbus_level();
675
676 let unit_processes = sysdbus::retreive_unit_processes(level, &unit.primary())?;
677
678 let mut unit_processes_map: BTreeMap<String, BTreeSet<UnitProcess>> = BTreeMap::new();
680 for unit_process in unit_processes {
681 let unit_process = {
682 let Some(unit_name) = unit_process.path.rsplit_once('/').map(|a| a.1) else {
683 warn!("No unit name for path {:?}", unit_process.path);
684 continue;
685 };
686
687 let unit_name_idx = unit_process.path.len() - unit_name.len();
688
689 UnitProcess {
690 path: unit_process.path,
691 pid: unit_process.pid,
692 name: unit_process.name,
693 unit_name: unit_name_idx,
694 }
695 };
696
697 if let Some(set) = unit_processes_map.get_mut(unit_process.unit_name()) {
698 set.insert(unit_process);
699 } else {
700 let mut set = BTreeSet::new();
701 let key = unit_process.unit_name().to_string();
702 set.insert(unit_process);
703 unit_processes_map.insert(key, set);
704 }
705 }
706
707 Ok(unit_processes_map)
708}
709
710#[derive(Debug)]
711pub struct SystemdSignalRow {
712 pub time_stamp: u64,
713 pub signal: SystemdSignal,
714}
715
716impl SystemdSignalRow {
717 pub fn new(signal: SystemdSignal) -> Self {
718 let current_system_time = SystemTime::now();
719 let since_the_epoch = current_system_time
720 .duration_since(UNIX_EPOCH)
721 .expect("Time went backwards");
722 let time_stamp =
723 since_the_epoch.as_secs() * 1_000_000 + since_the_epoch.subsec_nanos() as u64 / 1_000;
724 SystemdSignalRow { time_stamp, signal }
725 }
726
727 pub fn type_text(&self) -> &str {
728 self.signal.type_text()
729 }
730
731 pub fn details(&self) -> String {
732 self.signal.details()
733 }
734}
735
736#[derive(Debug)]
737pub enum SystemdSignal {
738 UnitNew(String, OwnedObjectPath),
739 UnitRemoved(String, OwnedObjectPath),
740 JobNew(u32, OwnedObjectPath, String),
741 JobRemoved(u32, OwnedObjectPath, String, String),
742 StartupFinished(u64, u64, u64, u64, u64, u64),
743 UnitFilesChanged,
744 Reloading(bool),
745}
746
747impl SystemdSignal {
748 pub fn type_text(&self) -> &str {
749 match self {
750 SystemdSignal::UnitNew(_, _) => "UnitNew",
751 SystemdSignal::UnitRemoved(_, _) => "UnitRemoved",
752 SystemdSignal::JobNew(_, _, _) => "JobNew",
753 SystemdSignal::JobRemoved(_, _, _, _) => "JobRemoved",
754 SystemdSignal::StartupFinished(_, _, _, _, _, _) => "StartupFinished",
755 SystemdSignal::UnitFilesChanged => "UnitFilesChanged",
756 SystemdSignal::Reloading(_) => "Reloading",
757 }
758 }
759
760 pub fn details(&self) -> String {
761 match self {
762 SystemdSignal::UnitNew(id, unit) => format!("{id} {unit}"),
763 SystemdSignal::UnitRemoved(id, unit) => format!("{id} {unit}"),
764 SystemdSignal::JobNew(id, job, unit) => {
765 format!("unit={unit} id={id} path={job}")
766 }
767 SystemdSignal::JobRemoved(id, job, unit, result) => {
768 format!("unit={unit} id={id} path={job} result={result}")
769 }
770 SystemdSignal::StartupFinished(firmware, loader, kernel, initrd, userspace, total) => {
771 format!(
772 "firmware={firmware} loader={loader} kernel={kernel} initrd={initrd} userspace={userspace} total={total}",
773 )
774 }
775 SystemdSignal::UnitFilesChanged => String::new(),
776 SystemdSignal::Reloading(active) => format!("firmware={active}"),
777 }
778 }
779}
780
781pub async fn watch_systemd_signals(
782 systemd_signal_sender: mpsc::Sender<SystemdSignalRow>,
783 cancellation_token: tokio_util::sync::CancellationToken,
784) {
785 let result: Result<(), SystemdErrors> =
786 sysdbus::watcher::watch_systemd_signals(systemd_signal_sender, cancellation_token).await;
787
788 if let Err(err) = result {
789 log::error!("Error listening to jobs {err:?}");
790 }
791}
792
793pub async fn test(test_name: &str, level: UnitDBusLevel) {
794 info!("Testing {test_name:?}");
795
796 if let Err(error) = sysdbus::test(test_name, level).await {
797 error!("{error:#?}");
798 }
799}
800
801#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
802pub struct UnitPropertyFetch {
803 pub name: String,
804 pub signature: String,
805 pub access: String,
806}
807
808impl UnitPropertyFetch {
809 fn new(p: &zbus_xml::Property) -> Self {
810 let access = match p.access() {
811 zbus_xml::PropertyAccess::Read => "read",
812 zbus_xml::PropertyAccess::Write => "write",
813 zbus_xml::PropertyAccess::ReadWrite => "readwrite",
814 };
815
816 UnitPropertyFetch {
817 name: p.name().to_string(),
818 signature: p.ty().to_string(),
819 access: access.to_string(),
820 }
821 }
822}
823
824pub async fn fetch_unit_interface_properties()
825-> Result<BTreeMap<String, Vec<UnitPropertyFetch>>, SystemdErrors> {
826 sysdbus::fetch_unit_interface_properties().await
827}
828
829pub async fn fetch_unit_properties(
830 level: UnitDBusLevel,
831 path: &str,
832 property_interface: &str,
833 property: &str,
834) -> Result<OwnedValue, SystemdErrors> {
835 sysdbus::fetch_unit_properties(level, path, property_interface, property).await
836}
837
838pub async fn create_drop_in(
839 user_session: bool,
840 runtime: bool,
841 unit_name: &str,
842 file_name: &str,
843 content: &str,
844) -> Result<(), SystemdErrors> {
845 #[cfg(not(feature = "flatpak"))]
846 if user_session {
847 file::create_drop_in(runtime, user_session, unit_name, file_name, content).await
848 } else {
849 to_proxy::create_drop_in(runtime, unit_name, file_name, content).await
850 }
851
852 #[cfg(feature = "flatpak")]
853 {
854 file::create_drop_in(runtime, user_session, unit_name, file_name, content).await
855 }
856}
857
858pub async fn save_file(
859 level: UnitDBusLevel,
860 file_path: &str,
861 content: &str,
862) -> Result<u64, SystemdErrors> {
863 info!("Saving file {file_path:?}");
864
865 let user_session = level.user_session();
866 #[cfg(not(feature = "flatpak"))]
869 if user_session {
870 save_text_to_file(file_path, content, user_session).await
871 } else {
872 to_proxy::save_file(file_path, content).await
873 }
874
875 #[cfg(feature = "flatpak")]
876 save_text_to_file(file_path, content, user_session).await
877}
878
879pub async fn revert_unit_file_full(
880 level: UnitDBusLevel,
881 unit_name: &str,
882) -> Result<Vec<DisEnAbleUnitFiles>, SystemdErrors> {
883 info!("Reverting unit file {unit_name:?}");
884
885 sysdbus::revert_unit_file_full(level, unit_name).await
886}