Files
proton-drive-linux/crates/pdfs-core/src/service.rs
T
nvrl 0d4163b9bf fix(service): start with the graphical session, not default.target (#24)
default.target is reached before the desktop exports DISPLAY/WAYLAND_DISPLAY
to the user manager, so After=graphical-session.target never applied. The
daemon's first keyring call D-Bus-activated the Secret Service without a
display; on KDE ksecretd aborted, the first start failed, and every login
left a coredump.

Install into graphical-session.target instead, and use reenable when the
front-ends enable the unit so existing default.target.wants links are
replaced rather than kept alongside the new one.

Fixes #23
2026-09-30 19:01:50 +02:00

122 lines
4.6 KiB
Rust

//! 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 <args> 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"])
}