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 } ;
76use std:: io:: Seek ;
87use std:: os:: unix:: process:: CommandExt ;
98use 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+
882893impl 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]
10261079fn 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