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
52pub 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 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}