From e4aea785ee264154f0bb1e11d342e7f08bb95889 Mon Sep 17 00:00:00 2001 From: Niko Marmeladkov Date: Tue, 7 Jul 2026 13:52:13 +0300 Subject: [PATCH] discover daemon state dir from cmdline when environ is unreadable When the daemon runs as a different user (e.g. via systemd), the client cannot read /proc//environ due to permissions. Fall back to parsing the cmdline for -D / --state-dir flags, which are world-readable. Fixes 'error: no logs for ' when client and daemon run as different users. v0.1.1 --- Cargo.toml | 2 +- src/state.rs | 37 +++++++++++++++++++++++++++++++------ 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e3806e1..7449a50 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rss" -version = "0.1.0" +version = "0.1.1" edition = "2021" [dependencies] diff --git a/src/state.rs b/src/state.rs index c7e540f..a391741 100644 --- a/src/state.rs +++ b/src/state.rs @@ -30,16 +30,41 @@ fn discover_daemon_state_dir() -> Option { if !args.contains(&"daemon") { continue; } - let environ = std::fs::read(format!("/proc/{}/environ", pid)).ok()?; - for var in environ.split(|&b| b == 0) { - if let Ok(var_str) = std::str::from_utf8(var) { - if let Some(val) = var_str.strip_prefix("RSS_STATE_DIR=") { - if !val.is_empty() { - return Some(PathBuf::from(val)); + // Try RSS_STATE_DIR from environ first + if let Ok(environ) = std::fs::read(format!("/proc/{}/environ", pid)) { + for var in environ.split(|&b| b == 0) { + if let Ok(var_str) = std::str::from_utf8(var) { + if let Some(val) = var_str.strip_prefix("RSS_STATE_DIR=") { + if !val.is_empty() { + return Some(PathBuf::from(val)); + } } } } } + // Fallback: parse cmdline for -D / --state-dir (works across users) + let mut i = 0; + while i < args.len() { + let next = if args[i] == "-D" || args[i] == "--state-dir" { + args.get(i + 1).map(|s| s.to_string()) + } else if let Some(val) = args[i].strip_prefix("--state-dir=") { + Some(val.to_string()) + } else if let Some(val) = args[i].strip_prefix("-D") { + if val.is_empty() { + args.get(i + 1).map(|s| s.to_string()) + } else { + Some(val.to_string()) + } + } else { + None + }; + if let Some(dir) = next { + if !dir.is_empty() { + return Some(PathBuf::from(dir)); + } + } + i += 1; + } } None }