1use async_stream::stream;
5use futures::stream::{Stream, StreamExt};
6use std::path::Path;
7use std::time::Duration;
8use tokio::time::sleep;
9
10const LISTS_LOCK: &str = "/var/lib/apt/lists/lock";
11const DPKG_LOCK: &str = "/var/lib/dpkg/lock";
12pub enum AptLockEvent {
13 Locked,
14 Unlocked,
15}
16
17pub async fn apt_lock_wait() {
18 let stream = apt_lock_watch();
19 futures::pin_mut!(stream);
20
21 while stream.next().await.is_some() {}
22}
23
24pub fn apt_lock_watch() -> impl Stream<Item = AptLockEvent> {
25 stream! {
26 let paths = &[Path::new(DPKG_LOCK), Path::new(LISTS_LOCK)];
27
28 let mut waiting = apt_lock_found(paths);
29
30 if waiting {
31 yield AptLockEvent::Locked;
32 while waiting {
33 sleep(Duration::from_secs(3)).await;
34 waiting = apt_lock_found(paths);
35 }
36 }
37
38 yield AptLockEvent::Unlocked;
39 }
40}
41
42#[must_use]
43pub fn apt_lock_found(paths: &[&Path]) -> bool {
44 use procfs::process::{all_processes, FDTarget};
45
46 let processes = match all_processes() {
47 Ok(processes) => processes,
48 Err(_) => return false,
49 };
50
51 for proc in processes.filter_map(Result::ok) {
52 let Ok(fdinfos) = proc.fd() else {
53 continue
54 };
55
56 for fdinfo in fdinfos.filter_map(Result::ok) {
57 if let FDTarget::Path(path) = fdinfo.target {
58 if paths.iter().any(|&p| &*path == p) {
59 return true;
60 }
61 }
62 }
63 }
64
65 false
66}