2
2
//!
3
3
//! Command line tool to manage bootable ostree-based containers.
4
4
5
- use std:: ffi:: CString ;
6
- use std:: ffi:: OsString ;
5
+ use std:: ffi:: { CString , OsStr , OsString } ;
7
6
use std:: io:: Seek ;
8
7
use std:: os:: unix:: process:: CommandExt ;
9
8
use std:: process:: Command ;
@@ -879,6 +878,18 @@ where
879
878
run_from_opt ( Opt :: parse_including_static ( args) ) . await
880
879
}
881
880
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
+
882
893
impl Opt {
883
894
/// In some cases (e.g. systemd generator) we dispatch specifically on argv0. This
884
895
/// requires some special handling in clap.
@@ -890,12 +901,19 @@ impl Opt {
890
901
let mut args = args. into_iter ( ) ;
891
902
let first = if let Some ( first) = args. next ( ) {
892
903
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 ) ;
894
905
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) ;
899
917
return Opt :: parse_from ( base_args. chain ( args. map ( |i| i. into ( ) ) ) ) ;
900
918
}
901
919
Some ( first)
@@ -1022,6 +1040,41 @@ async fn run_from_opt(opt: Opt) -> Result<()> {
1022
1040
}
1023
1041
}
1024
1042
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
+
1025
1078
#[ test]
1026
1079
fn test_parse_install_args ( ) {
1027
1080
// Verify we still process the legacy --target-no-signature-verification
@@ -1073,7 +1126,7 @@ fn test_parse_generator() {
1073
1126
"/usr/lib/systemd/system/bootc-systemd-generator" ,
1074
1127
"/run/systemd/system"
1075
1128
] ) ,
1076
- Opt :: Internals ( InternalsOpts :: SystemdGenerator { .. } )
1129
+ Opt :: Internals ( InternalsOpts :: SystemdGenerator { normal_dir , .. } ) if normal_dir == "/run/systemd/system"
1077
1130
) ) ;
1078
1131
}
1079
1132
@@ -1083,4 +1136,31 @@ fn test_parse_ostree_ext() {
1083
1136
Opt :: parse_including_static( [ "bootc" , "internals" , "ostree-container" ] ) ,
1084
1137
Opt :: Internals ( InternalsOpts :: OstreeContainer { .. } )
1085
1138
) ) ;
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" ] ) ;
1086
1166
}
0 commit comments