Fix: Mount-Unit nutzt pro Scope das richtige WantedBy und network-online.target

mount_service_unit() schrieb bislang unabhaengig vom Installations-Scope
immer "WantedBy=multi-user.target" - fuer eine User-Scope-Installation
(systemctl --user) ist dieses Ziel falsch/wirkungslos, da es dort keine
Entsprechung hat; die Unit haette nie zuverlaessig automatisch gestartet
werden koennen. Nimmt jetzt den Scope entgegen und setzt fuer System
"multi-user.target", fuer User "default.target".

Ebenso wartete die Unit unabhaengig vom Scope auf network-online.target -
auch das ist ein Ziel des System-Managers ohne sinnvolle Entsprechung
unter systemctl --user. Die Ordnungsabhaengigkeit (After=/Wants=) wird
jetzt nur noch im System-Kontext gesetzt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-19 21:45:14 +02:00
co-authored by Claude Sonnet 5
parent 5f8b81ee3e
commit 75a23646dd
+40 -3
View File
@@ -32,9 +32,21 @@ fn binary_path() -> String {
.unwrap_or_else(|| "/usr/bin/smart-mount".to_string())
}
fn mount_service_unit() -> String {
fn mount_service_unit(scope: Scope) -> String {
let wanted_by = match scope {
Scope::System => "multi-user.target",
Scope::User => "default.target",
};
// `network-online.target` ist ein Ziel des System-Managers - unter `systemctl --user` gibt
// es dafür keine sinnvolle Entsprechung (die Unit existiert dort nicht bzw. wird nie
// erreicht), die Ordnungsabhängigkeit wäre also für User-Scope-Units wirkungslos statt
// schlicht harmlos. Nur im System-Kontext gesetzt.
let network_wait = match scope {
Scope::System => "After=network-online.target\nWants=network-online.target\n",
Scope::User => "",
};
format!(
"[Unit]\nDescription=smart-mount: mount configured drive pairs at boot\nAfter=network-online.target\nWants=network-online.target\n\n[Service]\nType=oneshot\nExecStart={} mount --all\n\n[Install]\nWantedBy=multi-user.target\n",
"[Unit]\nDescription=smart-mount: mount configured drive pairs at boot\n{network_wait}\n[Service]\nType=oneshot\nExecStart={} mount --all\n\n[Install]\nWantedBy={wanted_by}\n",
binary_path()
)
}
@@ -93,7 +105,7 @@ pub fn install(scope: Scope, watch_interval_secs: u64) -> Result<()> {
let dir = unit_dir(scope)?;
std::fs::create_dir_all(&dir).map_err(|e| Error::io(&dir, e))?;
std::fs::write(dir.join(MOUNT_SERVICE), mount_service_unit())
std::fs::write(dir.join(MOUNT_SERVICE), mount_service_unit(scope))
.map_err(|e| Error::io(&dir, e))?;
std::fs::write(dir.join(WATCH_SERVICE), watch_service_unit())
.map_err(|e| Error::io(&dir, e))?;
@@ -337,6 +349,31 @@ fn cron_schedule_for_interval(interval_secs: u64) -> String {
mod tests {
use super::*;
#[test]
fn mount_service_unit_system_scope_uses_multi_user_target() {
let unit = mount_service_unit(Scope::System);
assert!(unit.contains("WantedBy=multi-user.target"));
}
#[test]
fn mount_service_unit_user_scope_uses_default_target() {
let unit = mount_service_unit(Scope::User);
assert!(unit.contains("WantedBy=default.target"));
assert!(!unit.contains("WantedBy=multi-user.target"));
}
#[test]
fn mount_service_unit_system_scope_waits_for_network_online() {
let unit = mount_service_unit(Scope::System);
assert!(unit.contains("network-online.target"));
}
#[test]
fn mount_service_unit_user_scope_does_not_reference_network_online_target() {
let unit = mount_service_unit(Scope::User);
assert!(!unit.contains("network-online.target"));
}
#[test]
fn managed_cron_block_for_user_crontab_has_no_user_field() {
let block = managed_cron_block(120, None);