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