lazybar_core/
cleanup.rs

1use std::{
2    fs::{read_dir, remove_file},
3    os::unix::fs::FileTypeExt,
4    sync::Arc,
5    time::Duration,
6};
7
8use anyhow::{anyhow, Result};
9use lazy_static::lazy_static;
10use tokio::{io::AsyncWriteExt, net::UnixStream, sync::Mutex, time};
11
12use crate::ipc::{self, ChannelEndpoint};
13
14lazy_static! {
15    pub(crate) static ref ENDPOINT: Arc<Mutex<Option<ChannelEndpoint<(), ()>>>> =
16        Arc::new(Mutex::new(None));
17}
18
19/// Removes any sockets in `/tmp/lazybar-ipc/` that can't be connected to.
20pub async fn cleanup() -> Result<()> {
21    let sockets = read_dir(ipc::IPC_DIR)?
22        .filter_map(Result::ok)
23        .filter(|f| f.file_type().is_ok_and(|t| t.is_socket()));
24    for socket in sockets {
25        let path = socket.path();
26
27        let stream = UnixStream::connect(path.as_path()).await;
28        match stream {
29            Ok(mut stream) => {
30                match time::timeout(Duration::from_secs(1), async {
31                    let mut buf = [0; 16];
32                    stream.writable().await?;
33                    stream.try_write(b"ping")?;
34                    stream.readable().await?;
35                    let bytes = stream.try_read(&mut buf)?;
36                    stream.shutdown().await?;
37
38                    match bytes {
39                        0 => Err(anyhow!("Failed to read from stream")),
40                        _ => Ok(()),
41                    }
42                })
43                .await
44                {
45                    Ok(Ok(())) => {}
46                    Ok(Err(_)) | Err(_) => {
47                        let _ = remove_file(path);
48                    }
49                }
50            }
51            Err(_) => {
52                let _ = remove_file(path);
53            }
54        }
55    }
56
57    Ok(())
58}
59
60/// Shutdown the bar as cleanly as possible. Short of SIGKILL, lazybar should
61/// never exit without calling this function.
62///
63/// - `bar` should be the name of the bar and whether IPC is enabled if
64///   available,
65/// otherwise `None`.
66/// - `in_runtime` should specify whether the function is being called from
67///   within
68/// the Tokio runtime. It's much easier for the caller to determine this..
69/// - `exit_code` will be passed to the operating system by
70///   [std::process::exit].
71pub async fn exit(
72    bar: Option<(&str, bool)>,
73    in_runtime: bool,
74    exit_code: i32,
75) -> ! {
76    if let Some((bar, true)) = bar {
77        let _ = remove_file(format!("/tmp/lazybar-ipc/{bar}"));
78    }
79    if in_runtime {
80        if let Some(ref mut endpoint) = *ENDPOINT.lock().await {
81            if endpoint.send.send(()).is_ok() {
82                let _ =
83                    time::timeout(Duration::from_secs(2), endpoint.recv.recv())
84                        .await;
85            }
86        }
87    }
88    std::process::exit(exit_code);
89}