Skip to content

Commit fb8c1e9

Browse files
authored
Merge pull request #916 from cgwalters/dispatch-ostree-container-argv0
cli: Add interception for ostree extension verbs
2 parents d42710e + 5ae6732 commit fb8c1e9

File tree

2 files changed

+96
-8
lines changed

2 files changed

+96
-8
lines changed

Makefile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ install:
2626
done
2727
install -D -m 0644 -t $(DESTDIR)/$(prefix)/lib/systemd/system systemd/*.service systemd/*.timer
2828

29+
# Run this to also take over the functionality of `ostree container` for example.
30+
# Only needed for OS/distros that have callers invoking `ostree container` and not bootc.
31+
install-ostree-hooks:
32+
install -d $(DESTDIR)$(prefix)/libexec/libostree/ext
33+
for x in ostree-container ostree-ima-sign ostree-provisional-repair; do \
34+
ln -sf ../../../bin/bootc $(DESTDIR)$(prefix)/libexec/libostree/ext/$$x; \
35+
done
36+
2937
install-with-tests: install
3038
install -D -m 0755 target/release/tests-integration $(DESTDIR)$(prefix)/bin/bootc-integration-tests
3139

lib/src/cli.rs

Lines changed: 88 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22
//!
33
//! Command line tool to manage bootable ostree-based containers.
44
5-
use std::ffi::CString;
6-
use std::ffi::OsString;
5+
use std::ffi::{CString, OsStr, OsString};
76
use std::io::Seek;
87
use std::os::unix::process::CommandExt;
98
use std::process::Command;
@@ -879,6 +878,18 @@ where
879878
run_from_opt(Opt::parse_including_static(args)).await
880879
}
881880

881+
/// Find the base binary name from argv0 (without a full path). The empty string
882+
/// is never returned; instead a fallback string is used. If the input is not valid
883+
/// UTF-8, a default is used.
884+
fn callname_from_argv0(argv0: &OsStr) -> &str {
885+
let default = "bootc";
886+
std::path::Path::new(argv0)
887+
.file_name()
888+
.and_then(|s| s.to_str())
889+
.filter(|s| !s.is_empty())
890+
.unwrap_or(default)
891+
}
892+
882893
impl Opt {
883894
/// In some cases (e.g. systemd generator) we dispatch specifically on argv0. This
884895
/// requires some special handling in clap.
@@ -890,12 +901,19 @@ impl Opt {
890901
let mut args = args.into_iter();
891902
let first = if let Some(first) = args.next() {
892903
let first: OsString = first.into();
893-
let argv0 = first.to_str().and_then(|s| s.rsplit_once('/')).map(|s| s.1);
904+
let argv0 = callname_from_argv0(&first);
894905
tracing::debug!("argv0={argv0:?}");
895-
if matches!(argv0, Some(InternalsOpts::GENERATOR_BIN)) {
896-
let base_args = ["bootc", "internals", "systemd-generator"]
897-
.into_iter()
898-
.map(OsString::from);
906+
let mapped = match argv0 {
907+
InternalsOpts::GENERATOR_BIN => {
908+
Some(["bootc", "internals", "systemd-generator"].as_slice())
909+
}
910+
"ostree-container" | "ostree-ima-sign" | "ostree-provisional-repair" => {
911+
Some(["bootc", "internals", "ostree-ext"].as_slice())
912+
}
913+
_ => None,
914+
};
915+
if let Some(base_args) = mapped {
916+
let base_args = base_args.into_iter().map(OsString::from);
899917
return Opt::parse_from(base_args.chain(args.map(|i| i.into())));
900918
}
901919
Some(first)
@@ -1022,6 +1040,41 @@ async fn run_from_opt(opt: Opt) -> Result<()> {
10221040
}
10231041
}
10241042

1043+
#[test]
1044+
fn test_callname() {
1045+
use std::os::unix::ffi::OsStrExt;
1046+
1047+
// Cases that change
1048+
let mapped_cases = [
1049+
("", "bootc"),
1050+
("/foo/bar", "bar"),
1051+
("/foo/bar/", "bar"),
1052+
("foo/bar", "bar"),
1053+
("../foo/bar", "bar"),
1054+
("usr/bin/ostree-container", "ostree-container"),
1055+
];
1056+
for (input, output) in mapped_cases {
1057+
assert_eq!(
1058+
output,
1059+
callname_from_argv0(OsStr::new(input)),
1060+
"Handling mapped case {input}"
1061+
);
1062+
}
1063+
1064+
// Invalid UTF-8
1065+
assert_eq!("bootc", callname_from_argv0(OsStr::from_bytes(b"foo\x80")));
1066+
1067+
// Cases that are identical
1068+
let ident_cases = ["foo", "bootc"];
1069+
for case in ident_cases {
1070+
assert_eq!(
1071+
case,
1072+
callname_from_argv0(OsStr::new(case)),
1073+
"Handling ident case {case}"
1074+
);
1075+
}
1076+
}
1077+
10251078
#[test]
10261079
fn test_parse_install_args() {
10271080
// Verify we still process the legacy --target-no-signature-verification
@@ -1073,7 +1126,7 @@ fn test_parse_generator() {
10731126
"/usr/lib/systemd/system/bootc-systemd-generator",
10741127
"/run/systemd/system"
10751128
]),
1076-
Opt::Internals(InternalsOpts::SystemdGenerator { .. })
1129+
Opt::Internals(InternalsOpts::SystemdGenerator { normal_dir, .. }) if normal_dir == "/run/systemd/system"
10771130
));
10781131
}
10791132

@@ -1083,4 +1136,31 @@ fn test_parse_ostree_ext() {
10831136
Opt::parse_including_static(["bootc", "internals", "ostree-container"]),
10841137
Opt::Internals(InternalsOpts::OstreeContainer { .. })
10851138
));
1139+
1140+
fn peel(o: Opt) -> Vec<OsString> {
1141+
match o {
1142+
Opt::Internals(InternalsOpts::OstreeExt { args }) => args,
1143+
o => panic!("unexpected {o:?}"),
1144+
}
1145+
}
1146+
let args = peel(Opt::parse_including_static([
1147+
"/usr/libexec/libostree/ext/ostree-ima-sign",
1148+
"ima-sign",
1149+
"--repo=foo",
1150+
"foo",
1151+
"bar",
1152+
"baz",
1153+
]));
1154+
assert_eq!(
1155+
args.as_slice(),
1156+
["ima-sign", "--repo=foo", "foo", "bar", "baz"]
1157+
);
1158+
1159+
let args = peel(Opt::parse_including_static([
1160+
"/usr/libexec/libostree/ext/ostree-container",
1161+
"container",
1162+
"image",
1163+
"pull",
1164+
]));
1165+
assert_eq!(args.as_slice(), ["container", "image", "pull"]);
10861166
}

0 commit comments

Comments
 (0)