Skip to content

Commit

Permalink
Merge pull request containers#916 from cgwalters/dispatch-ostree-cont…
Browse files Browse the repository at this point in the history
…ainer-argv0

 cli: Add interception for ostree extension verbs
  • Loading branch information
cgwalters authored Nov 27, 2024
2 parents d42710e + 5ae6732 commit fb8c1e9
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 8 deletions.
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ install:
done
install -D -m 0644 -t $(DESTDIR)/$(prefix)/lib/systemd/system systemd/*.service systemd/*.timer

# Run this to also take over the functionality of `ostree container` for example.
# Only needed for OS/distros that have callers invoking `ostree container` and not bootc.
install-ostree-hooks:
install -d $(DESTDIR)$(prefix)/libexec/libostree/ext
for x in ostree-container ostree-ima-sign ostree-provisional-repair; do \
ln -sf ../../../bin/bootc $(DESTDIR)$(prefix)/libexec/libostree/ext/$$x; \
done

install-with-tests: install
install -D -m 0755 target/release/tests-integration $(DESTDIR)$(prefix)/bin/bootc-integration-tests

Expand Down
96 changes: 88 additions & 8 deletions lib/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
//!
//! Command line tool to manage bootable ostree-based containers.
use std::ffi::CString;
use std::ffi::OsString;
use std::ffi::{CString, OsStr, OsString};
use std::io::Seek;
use std::os::unix::process::CommandExt;
use std::process::Command;
Expand Down Expand Up @@ -879,6 +878,18 @@ where
run_from_opt(Opt::parse_including_static(args)).await
}

/// Find the base binary name from argv0 (without a full path). The empty string
/// is never returned; instead a fallback string is used. If the input is not valid
/// UTF-8, a default is used.
fn callname_from_argv0(argv0: &OsStr) -> &str {
let default = "bootc";
std::path::Path::new(argv0)
.file_name()
.and_then(|s| s.to_str())
.filter(|s| !s.is_empty())
.unwrap_or(default)
}

impl Opt {
/// In some cases (e.g. systemd generator) we dispatch specifically on argv0. This
/// requires some special handling in clap.
Expand All @@ -890,12 +901,19 @@ impl Opt {
let mut args = args.into_iter();
let first = if let Some(first) = args.next() {
let first: OsString = first.into();
let argv0 = first.to_str().and_then(|s| s.rsplit_once('/')).map(|s| s.1);
let argv0 = callname_from_argv0(&first);
tracing::debug!("argv0={argv0:?}");
if matches!(argv0, Some(InternalsOpts::GENERATOR_BIN)) {
let base_args = ["bootc", "internals", "systemd-generator"]
.into_iter()
.map(OsString::from);
let mapped = match argv0 {
InternalsOpts::GENERATOR_BIN => {
Some(["bootc", "internals", "systemd-generator"].as_slice())
}
"ostree-container" | "ostree-ima-sign" | "ostree-provisional-repair" => {
Some(["bootc", "internals", "ostree-ext"].as_slice())
}
_ => None,
};
if let Some(base_args) = mapped {
let base_args = base_args.into_iter().map(OsString::from);
return Opt::parse_from(base_args.chain(args.map(|i| i.into())));
}
Some(first)
Expand Down Expand Up @@ -1022,6 +1040,41 @@ async fn run_from_opt(opt: Opt) -> Result<()> {
}
}

#[test]
fn test_callname() {
use std::os::unix::ffi::OsStrExt;

// Cases that change
let mapped_cases = [
("", "bootc"),
("/foo/bar", "bar"),
("/foo/bar/", "bar"),
("foo/bar", "bar"),
("../foo/bar", "bar"),
("usr/bin/ostree-container", "ostree-container"),
];
for (input, output) in mapped_cases {
assert_eq!(
output,
callname_from_argv0(OsStr::new(input)),
"Handling mapped case {input}"
);
}

// Invalid UTF-8
assert_eq!("bootc", callname_from_argv0(OsStr::from_bytes(b"foo\x80")));

// Cases that are identical
let ident_cases = ["foo", "bootc"];
for case in ident_cases {
assert_eq!(
case,
callname_from_argv0(OsStr::new(case)),
"Handling ident case {case}"
);
}
}

#[test]
fn test_parse_install_args() {
// Verify we still process the legacy --target-no-signature-verification
Expand Down Expand Up @@ -1073,7 +1126,7 @@ fn test_parse_generator() {
"/usr/lib/systemd/system/bootc-systemd-generator",
"/run/systemd/system"
]),
Opt::Internals(InternalsOpts::SystemdGenerator { .. })
Opt::Internals(InternalsOpts::SystemdGenerator { normal_dir, .. }) if normal_dir == "/run/systemd/system"
));
}

Expand All @@ -1083,4 +1136,31 @@ fn test_parse_ostree_ext() {
Opt::parse_including_static(["bootc", "internals", "ostree-container"]),
Opt::Internals(InternalsOpts::OstreeContainer { .. })
));

fn peel(o: Opt) -> Vec<OsString> {
match o {
Opt::Internals(InternalsOpts::OstreeExt { args }) => args,
o => panic!("unexpected {o:?}"),
}
}
let args = peel(Opt::parse_including_static([
"/usr/libexec/libostree/ext/ostree-ima-sign",
"ima-sign",
"--repo=foo",
"foo",
"bar",
"baz",
]));
assert_eq!(
args.as_slice(),
["ima-sign", "--repo=foo", "foo", "bar", "baz"]
);

let args = peel(Opt::parse_including_static([
"/usr/libexec/libostree/ext/ostree-container",
"container",
"image",
"pull",
]));
assert_eq!(args.as_slice(), ["container", "image", "pull"]);
}

0 comments on commit fb8c1e9

Please sign in to comment.