apt_cmd/
lock.rs

1// Copyright 2021-2022 System76 <info@system76.com>
2// SPDX-License-Identifier: MPL-2.0
3
4use 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}