1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
use std::{
    fs::{read_dir, remove_file},
    os::unix::fs::FileTypeExt,
    sync::OnceLock,
    time::Duration,
};

use anyhow::{anyhow, Result};
use tokio::{io::AsyncWriteExt, net::UnixStream, time};

use crate::ipc::{self, ChannelEndpoint};

pub(crate) static mut ENDPOINT: OnceLock<ChannelEndpoint<(), ()>> =
    OnceLock::new();

/// Removes any sockets in `/tmp/lazybar-ipc/` that can't be connected to.
pub async fn cleanup() -> Result<()> {
    let sockets = read_dir(ipc::IPC_DIR)?
        .filter_map(Result::ok)
        .filter(|f| f.file_type().is_ok_and(|t| t.is_socket()));
    for socket in sockets {
        let path = socket.path();

        let stream = UnixStream::connect(path.as_path()).await;
        match stream {
            Ok(mut stream) => {
                match time::timeout(Duration::from_secs(1), async {
                    let mut buf = [0; 16];
                    stream.writable().await?;
                    stream.try_write(b"ping")?;
                    stream.readable().await?;
                    let bytes = stream.try_read(&mut buf)?;
                    stream.shutdown().await?;

                    match bytes {
                        0 => Err(anyhow!("Failed to read from stream")),
                        _ => Ok(()),
                    }
                })
                .await
                {
                    Ok(Ok(())) => {}
                    Ok(Err(_)) | Err(_) => {
                        let _ = remove_file(path);
                    }
                }
            }
            Err(_) => {
                let _ = remove_file(path);
            }
        }
    }

    Ok(())
}

/// Shutdown the bar as cleanly as possible. Short of SIGKILL, lazybar should
/// never exit without calling this function.
pub async fn exit(
    bar: Option<(&str, bool)>,
    in_runtime: bool,
    exit_code: i32,
) -> ! {
    if let Some((bar, true)) = bar {
        let _ = remove_file(format!("/tmp/lazybar-ipc/{bar}"));
    }
    if in_runtime {
        if let Some(mut endpoint) = unsafe { ENDPOINT.take() } {
            if endpoint.send.send(()).is_ok() {
                let _ =
                    time::timeout(Duration::from_secs(2), endpoint.recv.recv())
                        .await;
            }
        }
    }
    std::process::exit(exit_code);
}