diff --git a/local/recipes/system/driver-manager/source/src/heartbeat.rs b/local/recipes/system/driver-manager/source/src/heartbeat.rs index f1e59c84cb..1113f03fe0 100644 --- a/local/recipes/system/driver-manager/source/src/heartbeat.rs +++ b/local/recipes/system/driver-manager/source/src/heartbeat.rs @@ -98,14 +98,43 @@ impl Heartbeat { fn write_atomic(path: &Path, bytes: &[u8]) { if let Some(parent) = path.parent() { if !parent.as_os_str().is_empty() { - let _ = fs::create_dir_all(parent); + // N20 S6: log create_dir_all failure (was silently dropped). + if let Err(e) = fs::create_dir_all(parent) { + log::error!( + "heartbeat: create_dir_all({}) failed: {}", + parent.display(), + e + ); + return; + } } } let tmp = path.with_extension("tmp"); - if fs::write(&tmp, bytes).is_err() { + // N20 S6: log write failure (was silently dropped). Includes + // disk-full, read-only-fs, permission-denied. Heartbeat-thread + // continues running (heartbeat is best-effort) but operators now + // see the actual cause in the log. + if let Err(e) = fs::write(&tmp, bytes) { + log::error!( + "heartbeat: write {} ({} bytes) failed: {}", + tmp.display(), + bytes.len(), + e + ); return; } - let _ = fs::rename(&tmp, path); + // N20 S6: log rename failure (was silently dropped). Cross-fs + // rename (e.g. tmpfs → /var/run on different fs) returns EXDEV. + // The temp file is left behind but a subsequent write will overwrite. + if let Err(e) = fs::rename(&tmp, path) { + log::error!( + "heartbeat: rename {} -> {} failed: {}", + tmp.display(), + path.display(), + e + ); + let _ = fs::remove_file(&tmp); + } } /// Cheap cloneable handle that the manager uses to bump counters.