#[cfg(target_os = "linux")]
use std::path::Path;
use crate::ComposeError;
#[cfg(target_os = "linux")]
pub fn managing_package_manager() -> Option<&'static str> {
let exe = std::env::current_exe().ok()?;
let path = std::fs::canonicalize(&exe).unwrap_or(exe);
dpkg_owns(&path).then_some("apt")
}
#[cfg(target_os = "linux")]
fn dpkg_owns(path: &Path) -> bool {
match std::process::Command::new("dpkg-query")
.arg("-S")
.arg(path)
.output()
{
Ok(output) => output.status.success(),
Err(_) => dpkg_lists_contain(path),
}
}
#[cfg(target_os = "linux")]
fn dpkg_lists_contain(path: &Path) -> bool {
dpkg_lists_contain_in(Path::new("/var/lib/dpkg/info"), path)
}
#[cfg(target_os = "linux")]
fn dpkg_lists_contain_in(info_dir: &Path, path: &Path) -> bool {
let Ok(entries) = std::fs::read_dir(info_dir) else {
return false; };
let needle = path.to_string_lossy();
for entry in entries.flatten() {
let p = entry.path();
if p.extension().and_then(|e| e.to_str()) != Some("list") {
continue;
}
if let Ok(contents) = std::fs::read_to_string(&p) {
if contents.lines().any(|line| line == needle) {
return true;
}
}
}
false
}
#[cfg(not(target_os = "linux"))]
pub fn managing_package_manager() -> Option<&'static str> {
None
}
pub fn package_managed_error(pm: &str) -> ComposeError {
ComposeError::Update(format!(
"this podup was installed by {pm}; update it with your package manager \
(e.g. `apt upgrade podup`) rather than `podup update`, which would break \
the package's record of the file"
))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn package_managed_error_names_the_manager() {
let e = package_managed_error("apt");
match e {
ComposeError::Update(msg) => {
assert!(msg.contains("apt"));
assert!(msg.contains("podup update"));
}
_ => panic!("expected an Update error"),
}
}
#[test]
fn test_binary_is_not_package_managed() {
assert_eq!(managing_package_manager(), None);
}
#[cfg(target_os = "linux")]
#[test]
fn dpkg_lists_fallback_matches_an_owned_path() {
let dir = tempfile::tempdir().unwrap();
let owned = Path::new("/usr/bin/podup");
std::fs::write(
dir.path().join("podup.list"),
"/.\n/usr/bin\n/usr/bin/podup\n",
)
.unwrap();
std::fs::write(dir.path().join("other.md5sums"), "/usr/bin/podup\n").unwrap();
assert!(dpkg_lists_contain_in(dir.path(), owned));
assert!(!dpkg_lists_contain_in(
dir.path(),
Path::new("/usr/bin/somethingelse")
));
}
#[cfg(target_os = "linux")]
#[test]
fn dpkg_lists_fallback_is_false_without_a_database() {
let dir = tempfile::tempdir().unwrap();
let absent = dir.path().join("no-such-info-dir");
assert!(!dpkg_lists_contain_in(&absent, Path::new("/usr/bin/podup")));
}
}