Skip to content

Commit

Permalink
Merge pull request #540 from cgwalters/packagesystem-cleanup
Browse files Browse the repository at this point in the history
Packagesystem cleanup
  • Loading branch information
jmarrero committed Oct 1, 2023
2 parents 50c292a + 5b5d0e6 commit 6d46360
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 89 deletions.
10 changes: 2 additions & 8 deletions src/bios.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::process::Command;

use crate::component::*;
use crate::model::*;
use crate::packagesystem;
use anyhow::{bail, Result};

use crate::util;
Expand Down Expand Up @@ -114,14 +115,7 @@ impl Component for Bios {
}

// Query the rpm database and list the package and build times for /usr/sbin/grub2-install
let mut rpmout = util::rpm_query(sysroot_path, &grub_install)?;
let rpmout = rpmout.output()?;
if !rpmout.status.success() {
std::io::stderr().write_all(&rpmout.stderr)?;
bail!("Failed to invoke rpm -qf");
}

let meta = util::parse_rpm_metadata(rpmout.stdout)?;
let meta = packagesystem::query_files(sysroot_path, [&grub_install])?;
write_update_metadata(sysroot_path, self, &meta)?;
Ok(meta)
}
Expand Down
20 changes: 7 additions & 13 deletions src/efi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
*/

use std::cell::RefCell;
use std::io::prelude::*;
use std::os::unix::io::AsRawFd;
use std::path::{Path, PathBuf};
use std::process::Command;
Expand All @@ -14,12 +13,11 @@ use anyhow::{bail, Context, Result};
use openat_ext::OpenatDirExt;
use widestring::U16CString;

use crate::component::*;
use crate::filetree;
use crate::model::*;
use crate::ostreeutil;
use crate::util;
use crate::util::CommandRunExt;
use crate::{component::*, packagesystem};

/// Well-known paths to the ESP that may have been mounted external to us.
pub(crate) const ESP_MOUNTS: &[&str] = &["boot", "boot/efi", "efi"];
Expand Down Expand Up @@ -341,17 +339,13 @@ impl Component for Efi {
Command::new("mv").args([&efisrc, &dest_efidir]).run()?;
}

// Query the rpm database and list the package and build times for all the
// files in the EFI system partition. If any files are not owned it is considered
// and error condition.
let mut rpmout = util::rpm_query(sysroot_path, &dest_efidir)?;
let rpmout = rpmout.output()?;
if !rpmout.status.success() {
std::io::stderr().write_all(&rpmout.stderr)?;
bail!("Failed to invoke rpm -qf");
}
let efidir = openat::Dir::open(&dest_efidir)?;
let files = crate::util::filenames(&efidir)?.into_iter().map(|mut f| {
f.insert_str(0, "/boot/efi/EFI/");
f
});

let meta = util::parse_rpm_metadata(rpmout.stdout)?;
let meta = packagesystem::query_files(sysroot_path, files)?;
write_update_metadata(sysroot_path, self, &meta)?;
Ok(meta)
}
Expand Down
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ mod ipc;
mod model;
mod model_legacy;
mod ostreeutil;
mod packagesystem;
mod sha512string;
mod util;

Expand Down
78 changes: 78 additions & 0 deletions src/packagesystem.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
use std::collections::{BTreeMap, BTreeSet};
use std::io::Write;
use std::path::Path;

use anyhow::{bail, Context, Result};
use chrono::prelude::*;

use crate::model::*;
use crate::ostreeutil;

/// Parse the output of `rpm -q`
fn rpm_parse_metadata(stdout: &[u8]) -> Result<ContentMetadata> {
let pkgs = std::str::from_utf8(stdout)?
.split_whitespace()
.map(|s| -> Result<_> {
let parts: Vec<_> = s.splitn(2, ',').collect();
let name = parts[0];
if let Some(ts) = parts.get(1) {
let nt = DateTime::parse_from_str(ts, "%s")
.context("Failed to parse rpm buildtime")?
.with_timezone(&chrono::Utc);
Ok((name, nt))
} else {
bail!("Failed to parse: {}", s);
}
})
.collect::<Result<BTreeMap<&str, DateTime<Utc>>>>()?;
if pkgs.is_empty() {
bail!("Failed to find any RPM packages matching files in source efidir");
}
let timestamps: BTreeSet<&DateTime<Utc>> = pkgs.values().collect();
// Unwrap safety: We validated pkgs has at least one value above
let largest_timestamp = timestamps.iter().last().unwrap();
let version = pkgs.keys().fold("".to_string(), |mut s, n| {
if !s.is_empty() {
s.push(',');
}
s.push_str(n);
s
});
Ok(ContentMetadata {
timestamp: **largest_timestamp,
version,
})
}

/// Query the rpm database and list the package and build times.
pub(crate) fn query_files<T>(
sysroot_path: &str,
paths: impl IntoIterator<Item = T>,
) -> Result<ContentMetadata>
where
T: AsRef<Path>,
{
let mut c = ostreeutil::rpm_cmd(sysroot_path);
c.args(["-q", "--queryformat", "%{nevra},%{buildtime} ", "-f"]);
for arg in paths {
c.arg(arg.as_ref());
}

let rpmout = c.output()?;
if !rpmout.status.success() {
std::io::stderr().write_all(&rpmout.stderr)?;
bail!("Failed to invoke rpm -qf");
}

rpm_parse_metadata(&rpmout.stdout)
}

#[test]
fn test_parse_rpmout() {
let testdata = "grub2-efi-x64-1:2.06-95.fc38.x86_64,1681321788 grub2-efi-x64-1:2.06-95.fc38.x86_64,1681321788 shim-x64-15.6-2.x86_64,1657222566 shim-x64-15.6-2.x86_64,1657222566 shim-x64-15.6-2.x86_64,1657222566";
let parsed = rpm_parse_metadata(testdata.as_bytes()).unwrap();
assert_eq!(
parsed.version,
"grub2-efi-x64-1:2.06-95.fc38.x86_64,shim-x64-15.6-2.x86_64"
);
}
70 changes: 2 additions & 68 deletions src/util.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
use std::collections::HashSet;

use anyhow::{bail, Context, Result};
use openat_ext::OpenatDirExt;

use std::path::Path;
use std::process::Command;

use crate::model::*;
use crate::ostreeutil;
use chrono::prelude::*;
use std::collections::{BTreeMap, BTreeSet};
use anyhow::{bail, Context, Result};
use openat_ext::OpenatDirExt;

pub(crate) trait CommandRunExt {
fn run(&mut self) -> Result<()>;
Expand Down Expand Up @@ -89,66 +83,6 @@ pub(crate) fn ensure_writable_mount<P: AsRef<Path>>(p: P) -> Result<()> {
Ok(())
}

/// Parse the output of `rpm -q`
pub(crate) fn parse_rpm_metadata(stdout: Vec<u8>) -> Result<ContentMetadata> {
let pkgs = std::str::from_utf8(&stdout)?
.split_whitespace()
.map(|s| -> Result<_> {
let parts: Vec<_> = s.splitn(2, ',').collect();
let name = parts[0];
if let Some(ts) = parts.get(1) {
let nt = DateTime::parse_from_str(ts, "%s")
.context("Failed to parse rpm buildtime")?
.with_timezone(&chrono::Utc);
Ok((name, nt))
} else {
bail!("Failed to parse: {}", s);
}
})
.collect::<Result<BTreeMap<&str, DateTime<Utc>>>>()?;
if pkgs.is_empty() {
bail!("Failed to find any RPM packages matching files in source efidir");
}
let timestamps: BTreeSet<&DateTime<Utc>> = pkgs.values().collect();
// Unwrap safety: We validated pkgs has at least one value above
let largest_timestamp = timestamps.iter().last().unwrap();
let version = pkgs.keys().fold("".to_string(), |mut s, n| {
if !s.is_empty() {
s.push(',');
}
s.push_str(n);
s
});
Ok(ContentMetadata {
timestamp: **largest_timestamp,
version,
})
}

/// Query the rpm database and list the package and build times, for all the
/// files in the EFI system partition, or for grub2-install file
pub(crate) fn rpm_query(sysroot_path: &str, path: &Path) -> Result<Command> {
let mut c = ostreeutil::rpm_cmd(sysroot_path);
c.args(["-q", "--queryformat", "%{nevra},%{buildtime} ", "-f"]);

match path.file_name().expect("filename").to_str() {
Some("EFI") => {
let efidir = openat::Dir::open(path)?;
c.args(filenames(&efidir)?.drain().map(|mut f| {
f.insert_str(0, "/boot/efi/EFI/");
f
}));
}
Some("grub2-install") => {
c.arg(path);
}
_ => {
bail!("Unsupported file/directory {:?}", path)
}
}
Ok(c)
}

/// Runs the provided Command object, captures its stdout, and swallows its stderr except on
/// failure. Returns a Result<String> describing whether the command failed, and if not, its
/// standard output. Output is assumed to be UTF-8. Errors are adequately prefixed with the full
Expand Down

0 comments on commit 6d46360

Please sign in to comment.