Skip to main content

oma_utils/
lib.rs

1use std::{os::fd::OwnedFd, path::Path};
2
3use nix::{
4    errno::Errno,
5    fcntl::{
6        FcntlArg::{F_GETLK, F_SETFD, F_SETLK},
7        FdFlag, OFlag, fcntl, open,
8    },
9    libc::{F_WRLCK, SEEK_SET, flock},
10    sys::stat::Mode,
11    unistd::close,
12};
13pub use os_release::OsRelease;
14
15#[cfg(feature = "dbus")]
16pub mod dbus;
17use spdlog::debug;
18use sysinfo::{Pid, System};
19
20#[cfg(feature = "dbus")]
21pub use zbus;
22
23#[cfg(feature = "dpkg")]
24pub mod dpkg;
25#[cfg(feature = "human-bytes")]
26pub mod human_bytes;
27#[cfg(feature = "url-no-escape")]
28pub mod url_no_escape;
29
30#[inline]
31pub fn is_termux() -> bool {
32    std::env::var("TERMUX_VERSION").is_ok_and(|v| !v.is_empty())
33}
34
35#[inline]
36pub fn concat_url(url: &str, path: &str) -> String {
37    format!(
38        "{}/{}",
39        url.trim_end_matches('/'),
40        path.trim_start_matches('/')
41    )
42}
43
44#[derive(thiserror::Error, Debug)]
45pub enum GetLockError {
46    #[error("Set lock failed")]
47    SetLock(Errno),
48    #[error("Set lock failed: process {0} ({1}) is using.")]
49    SetLockWithProcess(String, i32),
50}
51
52/// Create unix file lock
53pub fn get_file_lock(lock_path: &Path) -> Result<OwnedFd, GetLockError> {
54    let fd = open(
55        lock_path,
56        OFlag::O_RDWR | OFlag::O_CREAT | OFlag::O_NOFOLLOW,
57        Mode::from_bits_truncate(0o640),
58    )
59    .map_err(GetLockError::SetLock)?;
60
61    fcntl(&fd, F_SETFD(FdFlag::FD_CLOEXEC)).map_err(GetLockError::SetLock)?;
62
63    // From apt libapt-pkg/fileutil.cc:287
64    let mut fl = flock {
65        l_type: F_WRLCK as i16,
66        l_whence: SEEK_SET as i16,
67        l_start: 0,
68        l_len: 0,
69        l_pid: -1,
70    };
71
72    if let Err(e) = fcntl(&fd, F_SETLK(&fl)) {
73        debug!("{e}");
74
75        if e == Errno::EACCES || e == Errno::EAGAIN {
76            fl.l_type = F_WRLCK as i16;
77            fl.l_whence = SEEK_SET as i16;
78            fl.l_len = 0;
79            fl.l_start = 0;
80            fl.l_pid = -1;
81
82            fcntl(&fd, F_GETLK(&mut fl)).ok();
83        } else {
84            fl.l_pid = -1;
85        }
86
87        close(fd).map_err(GetLockError::SetLock)?;
88
89        if fl.l_pid != -1 {
90            let mut sys = System::new();
91            sys.refresh_processes(sysinfo::ProcessesToUpdate::All, true);
92            let Some(process) = sys.process(Pid::from(fl.l_pid as usize)) else {
93                return Err(GetLockError::SetLock(e));
94            };
95
96            return Err(GetLockError::SetLockWithProcess(
97                process.name().to_string_lossy().into(),
98                fl.l_pid,
99            ));
100        }
101
102        return Err(GetLockError::SetLock(e));
103    }
104
105    Ok(fd)
106}