diff --git a/Cargo.lock b/Cargo.lock index 2beb439..da05f24 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2052,7 +2052,7 @@ checksum = "ba467056f1b547ed52077911161fc86985becbc60e8e1857c8a144dab0def891" [[package]] name = "smart-mount" -version = "2.0.2" +version = "2.0.3" dependencies = [ "aes-gcm 0.11.1", "anyhow", diff --git a/Cargo.toml b/Cargo.toml index ba144d5..39b1bc3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "smart-mount" -version = "2.0.2" +version = "2.0.3" edition = "2024" authors = ['DragonSlayer_14'] readme = "README.md" diff --git a/README.md b/README.md index 48304fa..57b91d5 100644 --- a/README.md +++ b/README.md @@ -162,6 +162,15 @@ sudo systemctl edit smart-mount-watch.timer # OnUnitActiveSec=60s ``` +**Logs:** beide Units schreiben mit festem `SyslogIdentifier=smart-mount` ins systemd-Journal - +`journalctl -u smart-mount-mount` / `-u smart-mount-watch` (oder gebündelt `journalctl -t +smart-mount`) zeigt sie an. Zusätzlich schreibt smart-mount dieselben Meldungen dauerhaft nach +`/var/log/smart-mount/log-.log` (siehe `settings.log_level` für die Ausführlichkeit, +Standard `info`) - unabhängig von der Journal-Rotation. `smart-mount-mount.service` bleibt nach +einem erfolgreichen Lauf als "active (exited)" sichtbar (`RemainAfterExit=yes`), und ein +Fehlschlag beim Mounten mindestens eines konfigurierten Laufwerkspaars lässt den Dienst jetzt +auch tatsächlich als "failed" erscheinen, statt es stillschweigend zu ignorieren. + ### Cron-Fallback (Systeme ohne systemd) Ist beim Installieren/Upgraden des Pakets kein `systemctl` gefunden, aber `/etc/cron.d` diff --git a/packaging/systemd/smart-mount-mount.service b/packaging/systemd/smart-mount-mount.service index 9027017..f02f959 100644 --- a/packaging/systemd/smart-mount-mount.service +++ b/packaging/systemd/smart-mount-mount.service @@ -5,7 +5,18 @@ Wants=network-online.target [Service] Type=oneshot +# Bleibt nach einem erfolgreichen Lauf als "active (exited)" sichtbar (statt sofort wieder +# "inactive") - macht in 'systemctl status' auf einen Blick erkennbar, dass der letzte Boot-Lauf +# tatsächlich durchlief, statt nur "inactive" zu zeigen (das genauso gut "nie gelaufen" heißen +# könnte). +RemainAfterExit=yes ExecStart=/usr/bin/smart-mount mount --all +# Explizit (statt sich auf den systemd-Standard zu verlassen): stdout/stderr gehen ins Journal, +# unter einem festen, von der jeweiligen Unit unabhängigen Tag - 'journalctl -t smart-mount' +# zeigt damit die Ausgaben aller smart-mount-Units gebündelt. +StandardOutput=journal +StandardError=journal +SyslogIdentifier=smart-mount [Install] WantedBy=multi-user.target diff --git a/packaging/systemd/smart-mount-watch.service b/packaging/systemd/smart-mount-watch.service index 144b687..252e684 100644 --- a/packaging/systemd/smart-mount-watch.service +++ b/packaging/systemd/smart-mount-watch.service @@ -4,3 +4,6 @@ Description=smart-mount: check reachability and switch local/cloud if needed [Service] Type=oneshot ExecStart=/usr/bin/smart-mount watch +StandardOutput=journal +StandardError=journal +SyslogIdentifier=smart-mount diff --git a/src/cli/mount_cmd.rs b/src/cli/mount_cmd.rs index d1f36ba..61f311d 100644 --- a/src/cli/mount_cmd.rs +++ b/src/cli/mount_cmd.rs @@ -2,7 +2,7 @@ use crate::config::{self, DrivePair}; use crate::db::credentials::CredentialStore; -use crate::reconcile; +use crate::reconcile::{self, Action}; fn select_pairs( cfg: &crate::config::AppConfig, @@ -32,9 +32,21 @@ pub async fn run_mount(name: Option, all: bool) -> anyhow::Result<()> { ); let creds = CredentialStore::open().await?; + let mut had_failure = false; for pair in &pairs { let outcome = reconcile::reconcile_pair(pair, &cfg.settings, &creds).await; print_outcome(&outcome); + if matches!(outcome.action, Action::Failed(_)) { + had_failure = true; + } + } + + // Ohne dies wäre `smart-mount-mount.service` (siehe packaging/systemd/) beim Booten selbst + // dann "erfolgreich" (Exit-Code 0), wenn tatsächlich kein einziges Laufwerkspaar gemountet + // werden konnte - `systemctl status` würde den Fehlschlag also nie sichtbar machen. Analog + // zu `watch::run` unten. + if had_failure { + anyhow::bail!("at least one drive pair could not be mounted"); } Ok(()) } @@ -52,17 +64,24 @@ pub async fn run_unmount(name: Option, all: bool) -> anyhow::Result<()> &format!("unmount: processing {} drive pair(s)", pairs.len()), ); + let mut had_failure = false; for pair in &pairs { match reconcile::unmount_pair(pair, &cfg.settings).await { Ok(_) => println!("{} ({}): unmounted", pair.name, pair.id), - Err(e) => println!("{} ({}): ERROR: {e}", pair.name, pair.id), + Err(e) => { + println!("{} ({}): ERROR: {e}", pair.name, pair.id); + had_failure = true; + } } } + + if had_failure { + anyhow::bail!("at least one drive pair could not be unmounted"); + } Ok(()) } pub(crate) fn print_outcome(outcome: &reconcile::ReconcileOutcome) { - use reconcile::Action; let action_str = match &outcome.action { Action::NoOp => "no change".to_string(), Action::MountedLocal => "mounted local".to_string(), diff --git a/src/main.rs b/src/main.rs index 323f24a..66a830d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,12 @@ use smart_mount::cli; async fn main() -> ExitCode { smart_mount::config::init(); + // Persistentes, gut auffindbares Log-Verzeichnis statt logger-ctdras Standard-Fallback + // (ein Ordner im System-Temp-Verzeichnis, siehe logger-ctdra-Doku) - smart-mount läuft + // ausschließlich als root/System-Dienst (siehe crate::systemd), daher immer derselbe, + // feste Pfad. Muss vor dem ersten Log-Aufruf stehen (siehe logger_ctdra::set_log_dir). + logger_ctdra::set_log_dir("/var/log/smart-mount"); + let log_level = match smart_mount::config::pairs::load() { Ok(cfg) => cfg.settings.log_level, Err(_) => "info".to_string(),