1mod status;
6pub mod units;
7pub use status::UnitStatus;
8pub use units::{
9 daemon_reload, enable_linger, enable_now_socket, is_available, is_unit_enabled, is_unit_failed,
10 reset_failed, restart_unit, start_unit, stop_compositor, stop_socket_and_host, stop_unit,
11};
12
13use std::time::{Duration, Instant};
14
15use anyhow::Result;
16
17use crate::podman::{ContainerState, query_state};
18
19use status::{diagnostic_card, journal_tail, query_unit_status};
20use units::heal_missing_guest_socket;
21
22const POLL_INTERVAL_MS: u64 = 300;
23
24pub fn start_unit_friendly(name: &str, timeout_secs: u64) -> Result<()> {
29 if !is_available() {
30 anyhow::bail!("systemctl not available");
31 }
32
33 match query_unit_status(name) {
35 Ok(status) if status.need_daemon_reload => {
36 tracing::info!("systemd needs reload — running daemon-reload...");
37 daemon_reload()?;
38 }
39 Ok(_) => {}
40 Err(_) => {
41 }
43 }
44
45 reset_failed(name)?;
49
50 let _ = heal_missing_guest_socket(name);
54
55 let attempt = || -> Result<()> {
56 start_unit(name)?;
57 wait_for_running(name, timeout_secs)
58 };
59
60 let mut start_result = attempt();
61
62 if start_result.is_err() {
63 if let Ok(true) = heal_missing_guest_socket(name) {
65 eprintln!("Retrying start after socket rebind...");
66 reset_failed(name)?;
67 start_result = attempt();
68 }
69 }
70
71 match start_result {
72 Ok(()) => Ok(()),
73 Err(_) => {
74 let status = query_unit_status(name).unwrap_or_default();
76 let journal = journal_tail(name, 10).ok();
77 let card = diagnostic_card(name, &status, journal.as_deref());
78 eprintln!("{card}");
79 anyhow::bail!("container '{name}' failed to start");
80 }
81 }
82}
83
84fn wait_for_running(name: &str, timeout_secs: u64) -> Result<()> {
86 let deadline = Instant::now() + Duration::from_secs(timeout_secs);
87 loop {
88 match query_state(name)? {
89 ContainerState::Running => return Ok(()),
90 _ if Instant::now() >= deadline => {
91 let state = query_state(name)?;
92 anyhow::bail!(
93 "container '{name}' did not become ready within {timeout_secs}s (final state: {state:?})",
94 );
95 }
96 _ => {
97 std::thread::sleep(Duration::from_millis(POLL_INTERVAL_MS));
98 }
99 }
100 }
101}