//! Thin wrappers over `systemctl --user` for the auto-mount daemon. //! //! The mount is owned by a systemd *user* service (`proton-drive.service`, see //! `packaging/`). Front-ends never spawn `pdfs mount` directly anymore: logging //! in enables+starts the unit, logging out disables+stops it, and the tray's //! "Disconnect" just stops it. systemd then handles restart-on-failure and a //! clean SIGTERM stop (which the daemon turns into a lazy unmount). //! //! Every call is best-effort: a failure is logged but not fatal, since the user //! can always drive `systemctl --user` by hand. use std::process::{Command, Stdio}; /// The systemd user unit that runs `pdfs daemon`. pub const SERVICE: &str = "proton-drive.service"; /// Run one `systemctl --user proton-drive.service`, returning whether it /// succeeded. Output is suppressed; failures are logged. fn systemctl(args: &[&str]) -> bool { let ok = Command::new("systemctl") .arg("--user") .args(args) .arg(SERVICE) .stdout(Stdio::null()) .stderr(Stdio::null()) .status() .map(|s| s.success()) .unwrap_or(false); if !ok { tracing::warn!(args = ?args, "systemctl --user {SERVICE} failed"); } ok } /// Like [`systemctl`] but treats a non-zero exit as a plain `false` answer /// rather than a failure to log. For query verbs (`is-active`, `is-failed`) /// where "no" is the normal, expected result. fn systemctl_query(args: &[&str]) -> bool { Command::new("systemctl") .arg("--user") .args(args) .arg(SERVICE) .stdout(Stdio::null()) .stderr(Stdio::null()) .status() .map(|s| s.success()) .unwrap_or(false) } /// Enable the service so it survives reboots/logins, and bring it up now. /// Called on successful login. /// /// `restart` rather than `enable --now`: the unit may already be active but /// parked in the daemon's login-wait backoff (`pdfs daemon` sleeps between /// keyring checks). `--now` is a no-op on an active unit, so the daemon would /// keep sleeping and the mount would not appear until the next poll fired. /// Restarting makes the new session take effect immediately, and starts the /// unit when it is not running at all. `reenable` for the reason given on /// [`enable`]. pub fn enable_start() -> bool { let enabled = systemctl(&["reenable"]); systemctl(&["restart"]) && enabled } /// Stop the running mount without disabling it. Called by the tray's /// "Disconnect": the next login (or reboot) brings it back. pub fn stop() -> bool { systemctl(&["stop"]) } /// Restart the unit now. Used by a front-end "Retry" action when the mount has /// failed and the user wants to bring it back without re-logging-in. pub fn restart() -> bool { systemctl(&["restart"]) } /// Whether the unit currently reports `active` (running, or starting up). /// `systemctl is-active` exits 0 only while the unit is active, so a front-end /// can use this to tell "service still coming up" from "service is down". pub fn is_active() -> bool { systemctl_query(&["is-active"]) } /// Whether the unit is in the `failed` state. `systemctl is-failed` exits 0 /// only when the unit has failed, letting a front-end show an actionable error /// (with Retry) rather than an indefinite "connecting…" spinner. pub fn is_failed() -> bool { systemctl_query(&["is-failed"]) } /// Whether the unit is enabled to start on login/boot. `systemctl is-enabled` /// exits 0 only for an enabled unit, so the Settings page can render the /// "Start on login" switch in its true state. pub fn is_enabled() -> bool { systemctl_query(&["is-enabled"]) } /// Enable the service so it auto-starts on future logins, without necessarily /// starting it now beyond what `enable` does. Used by the Settings "Start on /// login" switch; pairs with [`disable`](disable). /// /// `reenable` rather than `enable`: older installs were enabled /// into `default.target.wants`, and a plain `enable` would add the new /// `graphical-session.target.wants` link while leaving the old one to start the /// unit too early. `reenable` drops every existing link and recreates them from /// the current `[Install]` section. pub fn enable() -> bool { systemctl(&["reenable"]) } /// Disable auto-start without stopping the running mount. Used by the Settings /// "Start on login" switch turning off; the current session stays mounted. pub fn disable() -> bool { systemctl(&["disable"]) } /// Disable the service so it no longer auto-starts, and stop it now. Called on /// logout. pub fn disable_stop() -> bool { systemctl(&["disable", "--now"]) }