[pbs-devel] applied: [PATCH backup] use new apt/apt-api-types crate

Wolfgang Bumiller w.bumiller at proxmox.com
Tue Jul 9 08:19:27 CEST 2024


From: Dietmar Maurer <dietmar at proxmox.com>

---
 Cargo.toml               |   6 +-
 pbs-api-types/Cargo.toml |   1 +
 pbs-api-types/src/lib.rs |  35 +---
 pbs-buildcfg/src/lib.rs  |   2 +
 src/api2/node/apt.rs     | 402 +++++----------------------------------
 src/bin/pbs2to3.rs       |  26 +--
 src/tools/apt.rs         | 293 ----------------------------
 src/tools/mod.rs         |   1 -
 8 files changed, 72 insertions(+), 694 deletions(-)
 delete mode 100644 src/tools/apt.rs

diff --git a/Cargo.toml b/Cargo.toml
index 17aeeb21..8120526a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -53,7 +53,9 @@ path = "src/lib.rs"
 
 [workspace.dependencies]
 # proxmox workspace
-proxmox-apt = "0.10.5"
+proxmox-config-digest = "0.1.0"
+proxmox-apt = { version = "0.11", features = [ "cache" ] }
+proxmox-apt-api-types = "1.0"
 proxmox-async = "0.4"
 proxmox-auth-api = "0.4"
 proxmox-borrow = "1"
@@ -203,9 +205,11 @@ zstd.workspace = true
 
 # proxmox workspace
 proxmox-apt.workspace = true
+proxmox-apt-api-types.workspace = true
 proxmox-async.workspace = true
 proxmox-auth-api = { workspace = true, features = [ "api", "pam-authenticator" ] }
 proxmox-compression.workspace = true
+proxmox-config-digest.workspace = true
 proxmox-http = { workspace = true, features = [ "client-trait", "proxmox-async", "rate-limited-stream" ] } # pbs-client doesn't use these
 proxmox-human-byte.workspace = true
 proxmox-io.workspace = true
diff --git a/pbs-api-types/Cargo.toml b/pbs-api-types/Cargo.toml
index 94ab583b..808ff514 100644
--- a/pbs-api-types/Cargo.toml
+++ b/pbs-api-types/Cargo.toml
@@ -16,6 +16,7 @@ serde.workspace = true
 serde_plain.workspace = true
 
 proxmox-auth-api = { workspace = true, features = [ "api-types" ] }
+proxmox-apt-api-types.workspace = true
 proxmox-human-byte.workspace = true
 proxmox-lang.workspace=true
 proxmox-schema = { workspace = true, features = [ "api-macro" ] }
diff --git a/pbs-api-types/src/lib.rs b/pbs-api-types/src/lib.rs
index a3ad185b..40bcd8f1 100644
--- a/pbs-api-types/src/lib.rs
+++ b/pbs-api-types/src/lib.rs
@@ -52,6 +52,13 @@ pub use proxmox_schema::api_types::{SYSTEMD_DATETIME_FORMAT, TIME_ZONE_SCHEMA};
 
 use proxmox_schema::api_types::{DNS_NAME_STR, IPRE_BRACKET_STR};
 
+// re-export APT API types
+pub use proxmox_apt_api_types::{
+    APTChangeRepositoryOptions, APTGetChangelogOptions, APTRepositoriesResult, APTRepositoryFile,
+    APTRepositoryFileError, APTRepositoryHandle, APTRepositoryInfo, APTStandardRepository,
+    APTUpdateInfo, APTUpdateOptions,
+};
+
 #[rustfmt::skip]
 pub const BACKUP_ID_RE: &str = r"[A-Za-z0-9_][A-Za-z0-9._\-]*";
 
@@ -249,34 +256,6 @@ pub const PASSWORD_HINT_SCHEMA: Schema = StringSchema::new("Password hint.")
     .max_length(64)
     .schema();
 
-#[api()]
-#[derive(Debug, Clone, Serialize, Deserialize)]
-#[serde(rename_all = "PascalCase")]
-/// Describes a package for which an update is available.
-pub struct APTUpdateInfo {
-    /// Package name
-    pub package: String,
-    /// Package title
-    pub title: String,
-    /// Package architecture
-    pub arch: String,
-    /// Human readable package description
-    pub description: String,
-    /// New version to be updated to
-    pub version: String,
-    /// Old version currently installed
-    pub old_version: String,
-    /// Package origin
-    pub origin: String,
-    /// Package priority in human-readable form
-    pub priority: String,
-    /// Package section
-    pub section: String,
-    /// Custom extra field for additional package information
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub extra_info: Option<String>,
-}
-
 #[api()]
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "lowercase")]
diff --git a/pbs-buildcfg/src/lib.rs b/pbs-buildcfg/src/lib.rs
index 8ce2f7e5..3d087015 100644
--- a/pbs-buildcfg/src/lib.rs
+++ b/pbs-buildcfg/src/lib.rs
@@ -98,6 +98,8 @@ pub const PROXMOX_BACKUP_KERNEL_FN: &str =
 
 pub const PROXMOX_BACKUP_SUBSCRIPTION_FN: &str = configdir!("/subscription");
 
+pub const APT_PKG_STATE_FN: &str = concat!(PROXMOX_BACKUP_STATE_DIR_M!(), "/pkg-state.json");
+
 /// Prepend configuration directory to a file name
 ///
 /// This is a simply way to get the full path for configuration files.
diff --git a/src/api2/node/apt.rs b/src/api2/node/apt.rs
index 0ae353d5..7b604cc0 100644
--- a/src/api2/node/apt.rs
+++ b/src/api2/node/apt.rs
@@ -1,26 +1,21 @@
-use anyhow::{bail, format_err, Error};
-use serde_json::{json, Value};
-use std::os::unix::prelude::OsStrExt;
+use anyhow::{bail, Error};
 
+use proxmox_config_digest::ConfigDigest;
 use proxmox_router::{
     list_subdirs_api_method, Permission, Router, RpcEnvironment, RpcEnvironmentType, SubdirMap,
 };
 use proxmox_schema::api;
 use proxmox_sys::fs::{replace_file, CreateOptions};
 
-use proxmox_apt::repositories::{
-    APTRepositoryFile, APTRepositoryFileError, APTRepositoryHandle, APTRepositoryInfo,
-    APTStandardRepository,
+use proxmox_apt_api_types::{
+    APTChangeRepositoryOptions, APTGetChangelogOptions, APTRepositoriesResult, APTRepositoryHandle,
+    APTUpdateInfo, APTUpdateOptions,
 };
 use proxmox_http::ProxyConfig;
 
-use pbs_api_types::{
-    APTUpdateInfo, NODE_SCHEMA, PRIV_SYS_AUDIT, PRIV_SYS_MODIFY, PROXMOX_CONFIG_DIGEST_SCHEMA,
-    UPID_SCHEMA,
-};
+use pbs_api_types::{NODE_SCHEMA, PRIV_SYS_AUDIT, PRIV_SYS_MODIFY, UPID_SCHEMA};
 
 use crate::config::node;
-use crate::tools::apt;
 use proxmox_rest_server::WorkerTask;
 
 #[api(
@@ -44,16 +39,8 @@ use proxmox_rest_server::WorkerTask;
     },
 )]
 /// List available APT updates
-fn apt_update_available(_param: Value) -> Result<Value, Error> {
-    if let Ok(false) = apt::pkg_cache_expired() {
-        if let Ok(Some(cache)) = apt::read_pkg_state() {
-            return Ok(json!(cache.package_status));
-        }
-    }
-
-    let cache = apt::update_cache()?;
-
-    Ok(json!(cache.package_status))
+pub fn apt_update_available() -> Result<Vec<APTUpdateInfo>, Error> {
+    proxmox_apt::list_available_apt_update(pbs_buildcfg::APT_PKG_STATE_FN)
 }
 
 pub fn update_apt_proxy_config(proxy_config: Option<&ProxyConfig>) -> Result<(), Error> {
@@ -83,45 +70,6 @@ fn read_and_update_proxy_config() -> Result<Option<ProxyConfig>, Error> {
     Ok(proxy_config)
 }
 
-fn do_apt_update(worker: &WorkerTask, quiet: bool) -> Result<(), Error> {
-    if !quiet {
-        worker.log_message("starting apt-get update")
-    }
-
-    read_and_update_proxy_config()?;
-
-    let mut command = std::process::Command::new("apt-get");
-    command.arg("update");
-
-    // apt "errors" quite easily, and run_command is a bit rigid, so handle this inline for now.
-    let output = command
-        .output()
-        .map_err(|err| format_err!("failed to execute {:?} - {}", command, err))?;
-
-    if !quiet {
-        worker.log_message(String::from_utf8(output.stdout)?);
-    }
-
-    // TODO: improve run_command to allow outputting both, stderr and stdout
-    if !output.status.success() {
-        if output.status.code().is_some() {
-            let msg = String::from_utf8(output.stderr)
-                .map(|m| {
-                    if m.is_empty() {
-                        String::from("no error message")
-                    } else {
-                        m
-                    }
-                })
-                .unwrap_or_else(|_| String::from("non utf8 error message (suppressed)"));
-            worker.log_warning(msg);
-        } else {
-            bail!("terminated by signal");
-        }
-    }
-    Ok(())
-}
-
 #[api(
     protected: true,
     input: {
@@ -129,19 +77,10 @@ fn do_apt_update(worker: &WorkerTask, quiet: bool) -> Result<(), Error> {
             node: {
                 schema: NODE_SCHEMA,
             },
-            notify: {
-                type: bool,
-                description: r#"Send notification mail about new package updates available to the
-                    email address configured for 'root at pam')."#,
-                default: false,
-                optional: true,
-            },
-            quiet: {
-                description: "Only produces output suitable for logging, omitting progress indicators.",
-                type: bool,
-                default: false,
-                optional: true,
-            },
+            options: {
+                type: APTUpdateOptions,
+                flatten: true,
+            }
         },
     },
     returns: {
@@ -153,40 +92,22 @@ fn do_apt_update(worker: &WorkerTask, quiet: bool) -> Result<(), Error> {
 )]
 /// Update the APT database
 pub fn apt_update_database(
-    notify: bool,
-    quiet: bool,
+    options: APTUpdateOptions,
     rpcenv: &mut dyn RpcEnvironment,
 ) -> Result<String, Error> {
     let auth_id = rpcenv.get_auth_id().unwrap();
     let to_stdout = rpcenv.env_type() == RpcEnvironmentType::CLI;
 
-    let upid_str = WorkerTask::new_thread("aptupdate", None, auth_id, to_stdout, move |worker| {
-        do_apt_update(&worker, quiet)?;
-
-        let mut cache = apt::update_cache()?;
-
-        if notify {
-            let mut notified = cache.notified.unwrap_or_default();
-            let mut to_notify: Vec<&APTUpdateInfo> = Vec::new();
-
-            for pkg in &cache.package_status {
-                match notified.insert(pkg.package.to_owned(), pkg.version.to_owned()) {
-                    Some(notified_version) => {
-                        if notified_version != pkg.version {
-                            to_notify.push(pkg);
-                        }
-                    }
-                    None => to_notify.push(pkg),
-                }
-            }
-            if !to_notify.is_empty() {
-                to_notify.sort_unstable_by_key(|k| &k.package);
-                crate::server::send_updates_available(&to_notify)?;
-            }
-            cache.notified = Some(notified);
-            apt::write_pkg_cache(&cache)?;
-        }
-
+    let upid_str = WorkerTask::new_thread("aptupdate", None, auth_id, to_stdout, move |_worker| {
+        read_and_update_proxy_config()?;
+        proxmox_apt::update_database(
+            pbs_buildcfg::APT_PKG_STATE_FN,
+            &options,
+            |updates: &[&APTUpdateInfo]| {
+                crate::server::send_updates_available(updates)?;
+                Ok(())
+            },
+        )?;
         Ok(())
     })?;
 
@@ -200,14 +121,9 @@ pub fn apt_update_database(
             node: {
                 schema: NODE_SCHEMA,
             },
-            name: {
-                description: "Package name to get changelog of.",
-                type: String,
-            },
-            version: {
-                description: "Package version to get changelog of. Omit to use candidate version.",
-                type: String,
-                optional: true,
+            options: {
+                type: APTGetChangelogOptions,
+                flatten: true,
             },
         },
     },
@@ -219,17 +135,8 @@ pub fn apt_update_database(
     },
 )]
 /// Retrieve the changelog of the specified package.
-fn apt_get_changelog(name: String, version: Option<String>) -> Result<Value, Error> {
-    let mut command = std::process::Command::new("apt-get");
-    command.arg("changelog");
-    command.arg("-qq"); // don't display download progress
-    if let Some(ver) = version {
-        command.arg(format!("{name}={ver}"));
-    } else {
-        command.arg(name);
-    }
-    let output = proxmox_sys::command::run_command(command, None)?;
-    Ok(json!(output))
+fn apt_get_changelog(options: APTGetChangelogOptions) -> Result<String, Error> {
+    proxmox_apt::get_changelog(&options)
 }
 
 #[api(
@@ -256,10 +163,8 @@ pub fn get_versions() -> Result<Vec<APTUpdateInfo>, Error> {
     const PACKAGES: &[&str] = &[
         "ifupdown2",
         "libjs-extjs",
-        "proxmox-backup",
         "proxmox-backup-docs",
         "proxmox-backup-client",
-        "proxmox-backup-server",
         "proxmox-mail-forward",
         "proxmox-mini-journalreader",
         "proxmox-offline-mirror-helper",
@@ -269,96 +174,16 @@ pub fn get_versions() -> Result<Vec<APTUpdateInfo>, Error> {
         "zfsutils-linux",
     ];
 
-    fn unknown_package(package: String, extra_info: Option<String>) -> APTUpdateInfo {
-        APTUpdateInfo {
-            package,
-            title: "unknown".into(),
-            arch: "unknown".into(),
-            description: "unknown".into(),
-            version: "unknown".into(),
-            old_version: "unknown".into(),
-            origin: "unknown".into(),
-            priority: "unknown".into(),
-            section: "unknown".into(),
-            extra_info,
-        }
-    }
-
-    let is_kernel =
-        |name: &str| name.starts_with("pve-kernel-") || name.starts_with("proxmox-kernel");
-
-    let mut packages: Vec<APTUpdateInfo> = Vec::new();
-    let pbs_packages = apt::list_installed_apt_packages(
-        |filter| {
-            filter.installed_version == Some(filter.active_version)
-                && (is_kernel(filter.package) || PACKAGES.contains(&filter.package))
-        },
-        None,
-    );
-
-    let running_kernel = format!(
-        "running kernel: {}",
-        std::str::from_utf8(nix::sys::utsname::uname()?.release().as_bytes())?.to_owned()
-    );
-    if let Some(proxmox_backup) = pbs_packages
-        .iter()
-        .find(|pkg| pkg.package == "proxmox-backup")
-    {
-        let mut proxmox_backup = proxmox_backup.clone();
-        proxmox_backup.extra_info = Some(running_kernel);
-        packages.push(proxmox_backup);
-    } else {
-        packages.push(unknown_package(
-            "proxmox-backup".into(),
-            Some(running_kernel),
-        ));
-    }
-
     let version = pbs_buildcfg::PROXMOX_PKG_VERSION;
     let release = pbs_buildcfg::PROXMOX_PKG_RELEASE;
-    let daemon_version_info = Some(format!("running version: {}.{}", version, release));
-    if let Some(pkg) = pbs_packages
-        .iter()
-        .find(|pkg| pkg.package == "proxmox-backup-server")
-    {
-        let mut pkg = pkg.clone();
-        pkg.extra_info = daemon_version_info;
-        packages.push(pkg);
-    } else {
-        packages.push(unknown_package(
-            "proxmox-backup".into(),
-            daemon_version_info,
-        ));
-    }
-
-    let mut kernel_pkgs: Vec<APTUpdateInfo> = pbs_packages
-        .iter()
-        .filter(|pkg| is_kernel(&pkg.package))
-        .cloned()
-        .collect();
-    // make sure the cache mutex gets dropped before the next call to list_installed_apt_packages
-    {
-        let cache = apt_pkg_native::Cache::get_singleton();
-        kernel_pkgs.sort_by(|left, right| {
-            cache
-                .compare_versions(&left.old_version, &right.old_version)
-                .reverse()
-        });
-    }
-    packages.append(&mut kernel_pkgs);
+    let running_daemon_version = format!("running version: {version}.{release}");
 
-    // add entry for all packages we're interested in, even if not installed
-    for pkg in PACKAGES.iter() {
-        if pkg == &"proxmox-backup" || pkg == &"proxmox-backup-server" {
-            continue;
-        }
-        match pbs_packages.iter().find(|item| &item.package == pkg) {
-            Some(apt_pkg) => packages.push(apt_pkg.to_owned()),
-            None => packages.push(unknown_package(pkg.to_string(), None)),
-        }
-    }
-
-    Ok(packages)
+    proxmox_apt::get_package_versions(
+        "proxmox-backup",
+        "proxmox-backup-server",
+        &running_daemon_version,
+        &PACKAGES,
+    )
 }
 
 #[api(
@@ -370,61 +195,15 @@ pub fn get_versions() -> Result<Vec<APTUpdateInfo>, Error> {
         },
     },
     returns: {
-        type: Object,
-        description: "Result from parsing the APT repository files in /etc/apt/.",
-        properties: {
-            files: {
-                description: "List of parsed repository files.",
-                type: Array,
-                items: {
-                    type: APTRepositoryFile,
-                },
-            },
-            errors: {
-                description: "List of problematic files.",
-                type: Array,
-                items: {
-                    type: APTRepositoryFileError,
-                },
-            },
-            digest: {
-                schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
-            },
-            infos: {
-                description: "List of additional information/warnings about the repositories.",
-                items: {
-                    type: APTRepositoryInfo,
-                },
-            },
-            "standard-repos": {
-                description: "List of standard repositories and their configuration status.",
-                items: {
-                    type: APTStandardRepository,
-                },
-            },
-        },
+        type: APTRepositoriesResult,
     },
     access: {
         permission: &Permission::Privilege(&[], PRIV_SYS_AUDIT, false),
     },
 )]
 /// Get APT repository information.
-pub fn get_repositories() -> Result<Value, Error> {
-    let (files, errors, digest) = proxmox_apt::repositories::repositories()?;
-    let digest = hex::encode(digest);
-
-    let suite = proxmox_apt::repositories::get_current_release_codename()?;
-
-    let infos = proxmox_apt::repositories::check_repositories(&files, suite);
-    let standard_repos = proxmox_apt::repositories::standard_repositories(&files, "pbs", suite);
-
-    Ok(json!({
-        "files": files,
-        "errors": errors,
-        "digest": digest,
-        "infos": infos,
-        "standard-repos": standard_repos,
-    }))
+pub fn get_repositories() -> Result<APTRepositoriesResult, Error> {
+    proxmox_apt::list_repositories("pbs")
 }
 
 #[api(
@@ -437,7 +216,7 @@ pub fn get_repositories() -> Result<Value, Error> {
                 type: APTRepositoryHandle,
             },
             digest: {
-                schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
+                type: ConfigDigest,
                 optional: true,
             },
         },
@@ -451,61 +230,11 @@ pub fn get_repositories() -> Result<Value, Error> {
 /// If the repository is already configured, it will be set to enabled.
 ///
 /// The `digest` parameter asserts that the configuration has not been modified.
-pub fn add_repository(handle: APTRepositoryHandle, digest: Option<String>) -> Result<(), Error> {
-    let (mut files, errors, current_digest) = proxmox_apt::repositories::repositories()?;
-
-    let suite = proxmox_apt::repositories::get_current_release_codename()?;
-
-    if let Some(expected_digest) = digest {
-        let current_digest = hex::encode(current_digest);
-        crate::tools::assert_if_modified(&expected_digest, &current_digest)?;
-    }
-
-    // check if it's already configured first
-    for file in files.iter_mut() {
-        for repo in file.repositories.iter_mut() {
-            if repo.is_referenced_repository(handle, "pbs", &suite.to_string()) {
-                if repo.enabled {
-                    return Ok(());
-                }
-
-                repo.set_enabled(true);
-                file.write()?;
-
-                return Ok(());
-            }
-        }
-    }
-
-    let (repo, path) = proxmox_apt::repositories::get_standard_repository(handle, "pbs", suite);
-
-    if let Some(error) = errors.iter().find(|error| error.path == path) {
-        bail!(
-            "unable to parse existing file {} - {}",
-            error.path,
-            error.error,
-        );
-    }
-
-    if let Some(file) = files
-        .iter_mut()
-        .find(|file| file.path.as_ref() == Some(&path))
-    {
-        file.repositories.push(repo);
-
-        file.write()?;
-    } else {
-        let mut file = match APTRepositoryFile::new(&path)? {
-            Some(file) => file,
-            None => bail!("invalid path - {}", path),
-        };
-
-        file.repositories.push(repo);
-
-        file.write()?;
-    }
-
-    Ok(())
+pub fn add_repository(
+    handle: APTRepositoryHandle,
+    digest: Option<ConfigDigest>,
+) -> Result<(), Error> {
+    proxmox_apt::add_repository_handle("pbs", handle, digest)
 }
 
 #[api(
@@ -522,13 +251,12 @@ pub fn add_repository(handle: APTRepositoryHandle, digest: Option<String>) -> Re
                 description: "Index within the file (starting from 0).",
                 type: usize,
             },
-            enabled: {
-                description: "Whether the repository should be enabled or not.",
-                type: bool,
-                optional: true,
+             options: {
+                type: APTChangeRepositoryOptions,
+                flatten: true,
             },
             digest: {
-                schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
+                type: ConfigDigest,
                 optional: true,
             },
         },
@@ -544,38 +272,10 @@ pub fn add_repository(handle: APTRepositoryHandle, digest: Option<String>) -> Re
 pub fn change_repository(
     path: String,
     index: usize,
-    enabled: Option<bool>,
-    digest: Option<String>,
+    options: APTChangeRepositoryOptions,
+    digest: Option<ConfigDigest>,
 ) -> Result<(), Error> {
-    let (mut files, errors, current_digest) = proxmox_apt::repositories::repositories()?;
-
-    if let Some(expected_digest) = digest {
-        let current_digest = hex::encode(current_digest);
-        crate::tools::assert_if_modified(&expected_digest, &current_digest)?;
-    }
-
-    if let Some(error) = errors.iter().find(|error| error.path == path) {
-        bail!("unable to parse file {} - {}", error.path, error.error);
-    }
-
-    if let Some(file) = files
-        .iter_mut()
-        .find(|file| file.path.as_ref() == Some(&path))
-    {
-        if let Some(repo) = file.repositories.get_mut(index) {
-            if let Some(enabled) = enabled {
-                repo.set_enabled(enabled);
-            }
-
-            file.write()?;
-        } else {
-            bail!("invalid index - {}", index);
-        }
-    } else {
-        bail!("invalid path - {}", path);
-    }
-
-    Ok(())
+    proxmox_apt::change_repository(&path, index, &options, digest)
 }
 
 const SUBDIRS: SubdirMap = &[
diff --git a/src/bin/pbs2to3.rs b/src/bin/pbs2to3.rs
index 3b3714f6..1f895abd 100644
--- a/src/bin/pbs2to3.rs
+++ b/src/bin/pbs2to3.rs
@@ -5,7 +5,8 @@ use anyhow::{format_err, Error};
 use regex::Regex;
 use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
 
-use proxmox_apt::repositories::{self, APTRepositoryFile, APTRepositoryPackageType};
+use proxmox_apt::repositories;
+use proxmox_apt_api_types::{APTRepositoryFile, APTRepositoryPackageType};
 use proxmox_backup::api2::node::apt;
 
 const OLD_SUITE: &str = "bullseye";
@@ -50,19 +51,18 @@ impl Checker {
     fn check_upgradable_packages(&mut self) -> Result<(), Error> {
         self.output.log_info("Checking for package updates..")?;
 
-        let result = Self::get_upgradable_packages();
+        let result = apt::apt_update_available();
         match result {
             Err(err) => {
                 self.output.log_warn(format!("{err}"))?;
                 self.output
                     .log_fail("unable to retrieve list of package updates!")?;
             }
-            Ok(cache) => {
-                if cache.package_status.is_empty() {
+            Ok(package_status) => {
+                if package_status.is_empty() {
                     self.output.log_pass("all packages up-to-date")?;
                 } else {
-                    let pkgs = cache
-                        .package_status
+                    let pkgs = package_status
                         .iter()
                         .map(|pkg| pkg.package.clone())
                         .collect::<Vec<String>>()
@@ -452,20 +452,6 @@ impl Checker {
         }
         Ok(())
     }
-
-    fn get_upgradable_packages() -> Result<proxmox_backup::tools::apt::PkgState, Error> {
-        let cache = if let Ok(false) = proxmox_backup::tools::apt::pkg_cache_expired() {
-            if let Ok(Some(cache)) = proxmox_backup::tools::apt::read_pkg_state() {
-                cache
-            } else {
-                proxmox_backup::tools::apt::update_cache()?
-            }
-        } else {
-            proxmox_backup::tools::apt::update_cache()?
-        };
-
-        Ok(cache)
-    }
 }
 
 #[derive(PartialEq)]
diff --git a/src/tools/apt.rs b/src/tools/apt.rs
deleted file mode 100644
index 900843aa..00000000
--- a/src/tools/apt.rs
+++ /dev/null
@@ -1,293 +0,0 @@
-use std::collections::HashMap;
-use std::collections::HashSet;
-
-use anyhow::{bail, format_err, Error};
-use apt_pkg_native::Cache;
-
-use proxmox_schema::const_regex;
-use proxmox_sys::fs::{file_read_optional_string, replace_file, CreateOptions};
-
-use pbs_api_types::APTUpdateInfo;
-use pbs_buildcfg::PROXMOX_BACKUP_STATE_DIR_M;
-
-const APT_PKG_STATE_FN: &str = concat!(PROXMOX_BACKUP_STATE_DIR_M!(), "/pkg-state.json");
-
-#[derive(Debug, serde::Serialize, serde::Deserialize)]
-/// Some information we cache about the package (update) state, like what pending update version
-/// we already notfied an user about
-pub struct PkgState {
-    /// simple map from package name to most recently notified (emailed) version
-    pub notified: Option<HashMap<String, String>>,
-    /// A list of pending updates
-    pub package_status: Vec<APTUpdateInfo>,
-}
-
-pub fn write_pkg_cache(state: &PkgState) -> Result<(), Error> {
-    let serialized_state = serde_json::to_string(state)?;
-
-    replace_file(
-        APT_PKG_STATE_FN,
-        serialized_state.as_bytes(),
-        CreateOptions::new(),
-        false,
-    )
-    .map_err(|err| format_err!("Error writing package cache - {}", err))?;
-    Ok(())
-}
-
-pub fn read_pkg_state() -> Result<Option<PkgState>, Error> {
-    let serialized_state = match file_read_optional_string(APT_PKG_STATE_FN) {
-        Ok(Some(raw)) => raw,
-        Ok(None) => return Ok(None),
-        Err(err) => bail!("could not read cached package state file - {}", err),
-    };
-
-    serde_json::from_str(&serialized_state)
-        .map(Some)
-        .map_err(|err| format_err!("could not parse cached package status - {}", err))
-}
-
-pub fn pkg_cache_expired() -> Result<bool, Error> {
-    if let Ok(pbs_cache) = std::fs::metadata(APT_PKG_STATE_FN) {
-        let apt_pkgcache = std::fs::metadata("/var/cache/apt/pkgcache.bin")?;
-        let dpkg_status = std::fs::metadata("/var/lib/dpkg/status")?;
-
-        let mtime = pbs_cache.modified()?;
-
-        if apt_pkgcache.modified()? <= mtime && dpkg_status.modified()? <= mtime {
-            return Ok(false);
-        }
-    }
-    Ok(true)
-}
-
-pub fn update_cache() -> Result<PkgState, Error> {
-    // update our cache
-    let all_upgradeable = list_installed_apt_packages(
-        |data| {
-            data.candidate_version == data.active_version
-                && data.installed_version != Some(data.candidate_version)
-        },
-        None,
-    );
-
-    let cache = match read_pkg_state() {
-        Ok(Some(mut cache)) => {
-            cache.package_status = all_upgradeable;
-            cache
-        }
-        _ => PkgState {
-            notified: None,
-            package_status: all_upgradeable,
-        },
-    };
-    write_pkg_cache(&cache)?;
-    Ok(cache)
-}
-
-const_regex! {
-    VERSION_EPOCH_REGEX = r"^\d+:";
-    FILENAME_EXTRACT_REGEX = r"^.*/.*?_(.*)_Packages$";
-}
-
-pub struct FilterData<'a> {
-    /// package name
-    pub package: &'a str,
-    /// this is version info returned by APT
-    pub installed_version: Option<&'a str>,
-    pub candidate_version: &'a str,
-
-    /// this is the version info the filter is supposed to check
-    pub active_version: &'a str,
-}
-
-enum PackagePreSelect {
-    OnlyInstalled,
-    OnlyNew,
-    All,
-}
-
-pub fn list_installed_apt_packages<F: Fn(FilterData) -> bool>(
-    filter: F,
-    only_versions_for: Option<&str>,
-) -> Vec<APTUpdateInfo> {
-    let mut ret = Vec::new();
-    let mut depends = HashSet::new();
-
-    // note: this is not an 'apt update', it just re-reads the cache from disk
-    let mut cache = Cache::get_singleton();
-    cache.reload();
-
-    let mut cache_iter = match only_versions_for {
-        Some(name) => cache.find_by_name(name),
-        None => cache.iter(),
-    };
-
-    loop {
-        match cache_iter.next() {
-            Some(view) => {
-                let di = if only_versions_for.is_some() {
-                    query_detailed_info(PackagePreSelect::All, &filter, view, None)
-                } else {
-                    query_detailed_info(
-                        PackagePreSelect::OnlyInstalled,
-                        &filter,
-                        view,
-                        Some(&mut depends),
-                    )
-                };
-                if let Some(info) = di {
-                    ret.push(info);
-                }
-
-                if only_versions_for.is_some() {
-                    break;
-                }
-            }
-            None => {
-                drop(cache_iter);
-                // also loop through missing dependencies, as they would be installed
-                for pkg in depends.iter() {
-                    let mut iter = cache.find_by_name(pkg);
-                    let view = match iter.next() {
-                        Some(view) => view,
-                        None => continue, // package not found, ignore
-                    };
-
-                    let di = query_detailed_info(PackagePreSelect::OnlyNew, &filter, view, None);
-                    if let Some(info) = di {
-                        ret.push(info);
-                    }
-                }
-                break;
-            }
-        }
-    }
-
-    ret
-}
-
-fn query_detailed_info<'a, F, V>(
-    pre_select: PackagePreSelect,
-    filter: F,
-    view: V,
-    depends: Option<&mut HashSet<String>>,
-) -> Option<APTUpdateInfo>
-where
-    F: Fn(FilterData) -> bool,
-    V: std::ops::Deref<Target = apt_pkg_native::sane::PkgView<'a>>,
-{
-    let current_version = view.current_version();
-    let candidate_version = view.candidate_version();
-
-    let (current_version, candidate_version) = match pre_select {
-        PackagePreSelect::OnlyInstalled => match (current_version, candidate_version) {
-            (Some(cur), Some(can)) => (Some(cur), can), // package installed and there is an update
-            (Some(cur), None) => (Some(cur.clone()), cur), // package installed and up-to-date
-            (None, Some(_)) => return None,             // package could be installed
-            (None, None) => return None,                // broken
-        },
-        PackagePreSelect::OnlyNew => match (current_version, candidate_version) {
-            (Some(_), Some(_)) => return None,
-            (Some(_), None) => return None,
-            (None, Some(can)) => (None, can),
-            (None, None) => return None,
-        },
-        PackagePreSelect::All => match (current_version, candidate_version) {
-            (Some(cur), Some(can)) => (Some(cur), can),
-            (Some(cur), None) => (Some(cur.clone()), cur),
-            (None, Some(can)) => (None, can),
-            (None, None) => return None,
-        },
-    };
-
-    // get additional information via nested APT 'iterators'
-    let mut view_iter = view.versions();
-    while let Some(ver) = view_iter.next() {
-        let package = view.name();
-        let version = ver.version();
-        let mut origin_res = "unknown".to_owned();
-        let mut section_res = "unknown".to_owned();
-        let mut priority_res = "unknown".to_owned();
-        let mut short_desc = package.clone();
-        let mut long_desc = "".to_owned();
-
-        let fd = FilterData {
-            package: package.as_str(),
-            installed_version: current_version.as_deref(),
-            candidate_version: &candidate_version,
-            active_version: &version,
-        };
-
-        if filter(fd) {
-            if let Some(section) = ver.section() {
-                section_res = section;
-            }
-
-            if let Some(prio) = ver.priority_type() {
-                priority_res = prio;
-            }
-
-            // assume every package has only one origin file (not
-            // origin, but origin *file*, for some reason those seem to
-            // be different concepts in APT)
-            let mut origin_iter = ver.origin_iter();
-            let origin = origin_iter.next();
-            if let Some(origin) = origin {
-                if let Some(sd) = origin.short_desc() {
-                    short_desc = sd;
-                }
-
-                if let Some(ld) = origin.long_desc() {
-                    long_desc = ld;
-                }
-
-                // the package files appear in priority order, meaning
-                // the one for the candidate version is first - this is fine
-                // however, as the source package should be the same for all
-                // versions anyway
-                let mut pkg_iter = origin.file();
-                let pkg_file = pkg_iter.next();
-                if let Some(pkg_file) = pkg_file {
-                    if let Some(origin_name) = pkg_file.origin() {
-                        origin_res = origin_name;
-                    }
-                }
-            }
-
-            if let Some(depends) = depends {
-                let mut dep_iter = ver.dep_iter();
-                loop {
-                    let dep = match dep_iter.next() {
-                        Some(dep) if dep.dep_type() != "Depends" => continue,
-                        Some(dep) => dep,
-                        None => break,
-                    };
-
-                    let dep_pkg = dep.target_pkg();
-                    let name = dep_pkg.name();
-
-                    depends.insert(name);
-                }
-            }
-
-            return Some(APTUpdateInfo {
-                package,
-                title: short_desc,
-                arch: view.arch(),
-                description: long_desc,
-                origin: origin_res,
-                version: candidate_version.clone(),
-                old_version: match current_version {
-                    Some(vers) => vers,
-                    None => "".to_owned(),
-                },
-                priority: priority_res,
-                section: section_res,
-                extra_info: None,
-            });
-        }
-    }
-
-    None
-}
diff --git a/src/tools/mod.rs b/src/tools/mod.rs
index f8c4f2d5..322894dd 100644
--- a/src/tools/mod.rs
+++ b/src/tools/mod.rs
@@ -6,7 +6,6 @@ use anyhow::{bail, Error};
 
 use proxmox_http::{client::Client, HttpOptions, ProxyConfig};
 
-pub mod apt;
 pub mod config;
 pub mod disks;
 pub mod fs;
-- 
2.39.2





More information about the pbs-devel mailing list