mirror of
https://github.com/narrrl/proton-drive-linux.git
synced 2026-11-04 16:56:59 +01:00
Merge branch 'fix/mount-failure-surfaced' into fix/db-errors-not-offline
# Conflicts: # docs/BUGS.md
This commit is contained in:
commit
73068f5868
26 files changed
+2305
-1152
No files matched your search
@@ -1980,6 +1980,29 @@ pub struct Diagnostics {
|
||||
pub control_handler_limit: usize,
|
||||
/// Queued mutations not yet drained.
|
||||
pub pending_ops: u64,
|
||||
/// The folder-sync engine. `None` from a daemon that predates the field.
|
||||
#[serde(default)]
|
||||
pub sync: Option<SyncEngineState>,
|
||||
}
|
||||
|
||||
/// What the folder-sync engine is doing, for [`Diagnostics`].
|
||||
///
|
||||
/// The engine is one thread that runs every synced folder's passes in turn.
|
||||
/// Dead or stuck, it looked exactly like an idle one: workers idle, nothing
|
||||
/// queued, nothing in the journal (`docs/BUGS.md` B259).
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
pub struct SyncEngineState {
|
||||
/// Whether the engine thread is running. Once it has ended, nothing syncs
|
||||
/// until the daemon restarts.
|
||||
pub running: bool,
|
||||
/// The synced folder whose pass is running, if one is.
|
||||
pub pass_folder: Option<i64>,
|
||||
/// How long that pass has been running.
|
||||
pub pass_age_secs: u64,
|
||||
/// How long since that pass last reported progress.
|
||||
pub pass_idle_secs: u64,
|
||||
/// Passes that ended in a panic since the daemon started.
|
||||
pub panics: u64,
|
||||
}
|
||||
|
||||
/// The daemon's reply to a [`Request`].
|
||||
|
||||
@@ -15,6 +15,7 @@ pub mod kernelkey;
|
||||
pub mod keycheck;
|
||||
pub mod localindex;
|
||||
pub mod menu;
|
||||
pub mod mountfail;
|
||||
pub mod mounts;
|
||||
pub mod opener;
|
||||
pub mod places;
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
//! Why the daemon's mount keeps failing, for the front ends to read.
|
||||
//!
|
||||
//! The control socket opens only once a mount has succeeded, so a daemon whose
|
||||
//! mount fails on every try had no way to say why. The app and the tray showed
|
||||
//! "Connecting…" for days while the reason sat in the journal (`docs/BUGS.md`
|
||||
//! B263). The daemon now writes its last failure to a small file in the state
|
||||
//! directory, next to the control socket, and removes it once a mount is up.
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// The file's name inside the state directory.
|
||||
const FILE_NAME: &str = "mount-failure.json";
|
||||
|
||||
/// The last failed mount attempt.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct MountFailure {
|
||||
/// The error, in English, as the journal has it.
|
||||
pub error: String,
|
||||
/// Unix seconds of the first failure in this run of failures.
|
||||
pub since: i64,
|
||||
/// Failed attempts in a row.
|
||||
pub attempts: u32,
|
||||
/// Unix seconds of the next attempt.
|
||||
pub next_attempt: i64,
|
||||
}
|
||||
|
||||
/// Where the failure is kept for the state directory `state_dir`.
|
||||
pub fn path(state_dir: &Path) -> PathBuf {
|
||||
state_dir.join(FILE_NAME)
|
||||
}
|
||||
|
||||
/// Record `failure`, replacing the one before it. Written to a temporary file
|
||||
/// and renamed, so a reader never sees half of it.
|
||||
pub fn record(state_dir: &Path, failure: &MountFailure) -> std::io::Result<()> {
|
||||
let json = serde_json::to_vec(failure).map_err(std::io::Error::other)?;
|
||||
let tmp = state_dir.join(format!("{FILE_NAME}.tmp"));
|
||||
std::fs::write(&tmp, json)?;
|
||||
std::fs::rename(&tmp, path(state_dir))
|
||||
}
|
||||
|
||||
/// Forget the failure: a mount is up, or a new daemon has started.
|
||||
pub fn clear(state_dir: &Path) {
|
||||
let _ = std::fs::remove_file(path(state_dir));
|
||||
}
|
||||
|
||||
/// The recorded failure, if there is one and it can be read.
|
||||
pub fn read(state_dir: &Path) -> Option<MountFailure> {
|
||||
let bytes = std::fs::read(path(state_dir)).ok()?;
|
||||
serde_json::from_slice(&bytes).ok()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn a_recorded_failure_reads_back_until_cleared() {
|
||||
let dir = std::env::temp_dir().join(format!("pdfs-mountfail-{}", std::process::id()));
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
assert_eq!(read(&dir), None);
|
||||
let failure = MountFailure {
|
||||
error: "fetch My Files root: missing key".into(),
|
||||
since: 1_700_000_000,
|
||||
attempts: 3,
|
||||
next_attempt: 1_700_000_040,
|
||||
};
|
||||
record(&dir, &failure).unwrap();
|
||||
assert_eq!(read(&dir), Some(failure));
|
||||
clear(&dir);
|
||||
assert_eq!(read(&dir), None);
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user