1use std::fs;
6use std::path::Path;
7
8use anyhow::{bail, Context};
9
10use bark::lock_manager::PidLockError;
11use bark::lock_manager::pid_flock::FlockPidLockManager;
12
13use crate::wallet::AUTH_TOKEN_FILE;
14
15pub const BARKD_LOCK_FILE: &str = "barkd.lock";
17
18pub fn wipe_datadir_except_barkd_files(datadir: &Path) -> anyhow::Result<()> {
21 let barkd_files = [BARKD_LOCK_FILE, AUTH_TOKEN_FILE];
22 for entry in fs::read_dir(datadir).context("failed to list datadir")? {
23 let entry = entry?;
24 if barkd_files.iter().any(|k| entry.file_name() == *k) {
25 continue;
26 }
27 let path = entry.path();
28 if entry.file_type()?.is_dir() {
29 fs::remove_dir_all(&path)
30 } else {
31 fs::remove_file(&path)
32 }.with_context(|| format!("failed to remove {}", path.display()))?;
33 }
34 Ok(())
35}
36
37pub fn acquire_barkd_lock(datadir: &Path) -> anyhow::Result<FlockPidLockManager> {
41 match FlockPidLockManager::new_with_lock_file(datadir, BARKD_LOCK_FILE) {
42 Ok(lock) => Ok(lock),
43 Err(PidLockError::AlreadyHeld { datadir, pid }) => match pid {
44 Some(pid) => bail!(
45 "another barkd is already running on datadir {} (pid {})",
46 datadir.display(), pid,
47 ),
48 None => bail!(
49 "another barkd is already running on datadir {}",
50 datadir.display(),
51 ),
52 },
53 Err(e) => Err(e).context("failed to acquire barkd lock"),
54 }
55}
56
57#[cfg(test)]
58mod test {
59 use super::*;
60
61 fn tmp_dir() -> std::path::PathBuf {
62 let dir = std::env::temp_dir()
63 .join(format!("bark-connection-{}", std::process::id()))
64 .join(format!("{}", std::time::SystemTime::now()
65 .duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos()));
66 fs::create_dir_all(&dir).unwrap();
67 dir
68 }
69
70 #[test]
71 fn wipe_keeps_barkd_files() {
72 let dir = tmp_dir();
73 let _lock = acquire_barkd_lock(&dir).unwrap();
74 fs::write(dir.join(AUTH_TOKEN_FILE), "tok").unwrap();
75 fs::write(dir.join("db.sqlite"), "x").unwrap();
76 fs::create_dir(dir.join("sub")).unwrap();
77 fs::write(dir.join("sub").join("f"), "y").unwrap();
78
79 wipe_datadir_except_barkd_files(&dir).unwrap();
80
81 assert!(dir.join(BARKD_LOCK_FILE).exists());
82 assert!(dir.join(AUTH_TOKEN_FILE).exists());
83 assert!(!dir.join("db.sqlite").exists());
84 assert!(!dir.join("sub").exists());
85
86 let _ = fs::remove_dir_all(&dir);
87 }
88
89 #[test]
90 fn barkd_lock_refuses_second_holder() {
91 let dir = tmp_dir();
92 let held = acquire_barkd_lock(&dir).unwrap();
93
94 let err = acquire_barkd_lock(&dir).unwrap_err();
97 assert!(
98 err.to_string().contains("another barkd is already running"),
99 "unexpected error: {}", err,
100 );
101 assert!(err.to_string().contains(&std::process::id().to_string()));
102
103 drop(held);
104 let _reacquired = acquire_barkd_lock(&dir).unwrap();
105
106 let _ = fs::remove_dir_all(&dir);
107 }
108}