use std::path::PathBuf;
pub mod cgfs;
pub mod effector;
pub mod history;
pub mod journal;
pub mod lock;
pub mod notify;
pub mod policy;
pub mod report;
pub mod resolve;
pub mod sampler;
pub mod service;
pub mod systemd;
pub mod types;
pub use effector::{Applied, Effector};
pub use journal::Journal;
pub use policy::PolicyEngine;
pub use sampler::Sampler;
pub use systemd::SystemdUser;
pub use types::{Action, Intervention, Level, ProcInfo, PsiSource, Sample, Target};
pub fn guard_file(name: &str) -> Option<PathBuf> {
let state = dirs::state_dir();
let runtime = if state.is_none() {
private_runtime_dir()
} else {
None
};
guard_file_from(state, runtime, name)
}
fn private_runtime_dir() -> Option<PathBuf> {
use std::os::unix::fs::MetadataExt;
static WARNED: std::sync::Once = std::sync::Once::new();
let dir = dirs::runtime_dir()?;
let uid = crate::process::current_uid();
match std::fs::metadata(&dir) {
Ok(m) if runtime_dir_is_private(m.uid(), m.mode(), uid) => Some(dir),
Ok(m) => {
WARNED.call_once(|| {
tracing::warn!(
"ignoring XDG_RUNTIME_DIR {}: owner uid {} mode {:o}, expected owner {} \
and not group or world writable",
dir.display(),
m.uid(),
m.mode() & 0o7777,
uid
)
});
None
}
Err(e) => {
WARNED.call_once(|| tracing::warn!("ignoring XDG_RUNTIME_DIR {}: {e}", dir.display()));
None
}
}
}
fn runtime_dir_is_private(owner: u32, mode: u32, uid: u32) -> bool {
owner == uid && mode & 0o022 == 0
}
fn guard_file_from(
state: Option<PathBuf>,
runtime: Option<PathBuf>,
name: &str,
) -> Option<PathBuf> {
state.or(runtime).map(|d| d.join("rlm").join(name))
}
pub fn try_journal_path() -> Option<PathBuf> {
guard_file("guard-journal.jsonl")
}
pub fn journal_path() -> PathBuf {
try_journal_path().unwrap_or_default()
}
pub fn lock_path() -> Option<PathBuf> {
guard_file("rlm-guard.lock")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn guard_files_prefer_the_state_dir() {
assert_eq!(
guard_file_from(Some("/s".into()), Some("/r".into()), "rlm-guard.lock"),
Some(PathBuf::from("/s/rlm/rlm-guard.lock"))
);
}
#[test]
fn guard_files_fall_back_to_the_runtime_dir_not_tmp() {
assert_eq!(
guard_file_from(None, Some("/run/user/1000".into()), "guard-journal.jsonl"),
Some(PathBuf::from("/run/user/1000/rlm/guard-journal.jsonl"))
);
assert_eq!(guard_file_from(None, None, "guard-journal.jsonl"), None);
}
#[test]
fn runtime_dir_is_used_only_when_private_to_us() {
assert!(runtime_dir_is_private(1000, 0o40700, 1000));
assert!(runtime_dir_is_private(1000, 0o40750, 1000));
assert!(!runtime_dir_is_private(0, 0o40700, 1000));
assert!(!runtime_dir_is_private(1000, 0o40770, 1000));
assert!(!runtime_dir_is_private(1000, 0o40702, 1000));
assert!(!runtime_dir_is_private(1000, 0o41777, 1000));
}
#[test]
fn journal_reader_path_is_empty_without_a_dir() {
let p = try_journal_path().unwrap_or_default();
assert!(!p.starts_with("/tmp"), "{p:?}");
assert!(Journal::read_entries(&PathBuf::new(), "boot").is_empty());
}
}