@@ -34,6 +34,14 @@ use clap::{Parser, Subcommand};
3434use state:: { SandboxState , Status } ;
3535use std:: path:: PathBuf ;
3636
37+ /// Format for the runc-compatible `--log` file. runc defaults to `text`; the
38+ /// containerd shim passes `json`.
39+ #[ derive( Clone , Copy , Debug , PartialEq , Eq , clap:: ValueEnum ) ]
40+ enum LogFormat {
41+ Text ,
42+ Json ,
43+ }
44+
3745#[ derive( Parser ) ]
3846#[ command(
3947 name = "sandlock-oci" ,
@@ -49,6 +57,29 @@ struct Cli {
4957 #[ arg( long, global = true ) ]
5058 root : Option < PathBuf > ,
5159
60+ /// Path to append fatal errors to (runc-compatible; read by the
61+ /// containerd shim to surface the failure reason).
62+ #[ arg( long, global = true ) ]
63+ log : Option < PathBuf > ,
64+
65+ /// Format for the --log file. runc defaults to "text"; the containerd
66+ /// shim passes "json".
67+ #[ arg( long = "log-format" , global = true , value_enum, default_value = "text" ) ]
68+ log_format : LogFormat ,
69+
70+ /// Accepted for runc compatibility; no-op (no logging framework yet).
71+ #[ arg( long, global = true ) ]
72+ debug : bool ,
73+
74+ /// Accepted for runc compatibility; ignored (sandlock is cgroup-less).
75+ #[ arg( long = "systemd-cgroup" , global = true ) ]
76+ systemd_cgroup : bool ,
77+
78+ /// Accepted for runc compatibility; ignored. Both `--rootless` and
79+ /// `--rootless=true|false` are allowed.
80+ #[ arg( long, global = true , num_args = 0 ..=1 , require_equals = true , default_missing_value = "true" ) ]
81+ rootless : Option < bool > ,
82+
5283 #[ command( subcommand) ]
5384 command : Command ,
5485}
@@ -69,6 +100,12 @@ enum Command {
69100 /// Console socket path (ignored — sandlock doesn't use PTYs by default).
70101 #[ arg( long = "console-socket" ) ]
71102 console_socket : Option < PathBuf > ,
103+ /// Accepted for runc compatibility; ignored (sandlock does not pivot_root).
104+ #[ arg( long = "no-pivot" ) ]
105+ no_pivot : bool ,
106+ /// Accepted for runc compatibility; ignored (no session keyring).
107+ #[ arg( long = "no-new-keyring" ) ]
108+ no_new_keyring : bool ,
72109 } ,
73110
74111 /// Start a previously created sandbox.
@@ -167,6 +204,12 @@ enum Command {
167204 /// OCI bundle path (accepted for runc compatibility; may be unused).
168205 #[ arg( long = "bundle" ) ]
169206 bundle : Option < String > ,
207+ /// Accepted for runc compatibility; ignored (sandlock does not pivot_root).
208+ #[ arg( long = "no-pivot" ) ]
209+ no_pivot : bool ,
210+ /// Accepted for runc compatibility; ignored (no session keyring).
211+ #[ arg( long = "no-new-keyring" ) ]
212+ no_new_keyring : bool ,
170213 } ,
171214}
172215
@@ -178,7 +221,7 @@ fn main() -> Result<()> {
178221 state:: init_state_dir ( cli. root . as_deref ( ) . and_then ( |p| p. to_str ( ) ) ) ;
179222
180223 match cli. command {
181- Command :: Create { id, bundle, pid_file, console_socket : _ } => {
224+ Command :: Create { id, bundle, pid_file, console_socket : _, no_pivot : _ , no_new_keyring : _ } => {
182225 cmd_create ( & id, & bundle, pid_file. as_deref ( ) ) ?;
183226 }
184227 Command :: Start { id } => {
@@ -259,7 +302,7 @@ fn main() -> Result<()> {
259302 Command :: Checkpoint { id, image_path } => {
260303 cmd_checkpoint ( & id, & image_path) ?;
261304 }
262- Command :: Restore { id, image_path, bundle : _ } => {
305+ Command :: Restore { id, image_path, bundle : _, no_pivot : _ , no_new_keyring : _ } => {
263306 cmd_restore ( & id, & image_path) ?;
264307 }
265308 }
@@ -868,6 +911,49 @@ fn parse_signal(s: &str) -> Result<i32> {
868911#[ cfg( test) ]
869912mod tests {
870913 use super :: * ;
914+ use clap:: Parser ;
915+
916+ #[ test]
917+ fn accepts_runc_create_globals ( ) {
918+ let cli = Cli :: try_parse_from ( [
919+ "sandlock-oci" , "--root" , "/r" , "--log" , "/l" , "--log-format" , "json" ,
920+ "--systemd-cgroup" , "--debug" ,
921+ "create" , "--bundle" , "/b" , "--pid-file" , "/p" ,
922+ "--no-pivot" , "--no-new-keyring" , "id" ,
923+ ] )
924+ . expect ( "should parse runc-style create invocation" ) ;
925+ assert ! ( matches!( cli. command, Command :: Create { .. } ) ) ;
926+ assert_eq ! ( cli. log. as_deref( ) , Some ( std:: path:: Path :: new( "/l" ) ) ) ;
927+ assert_eq ! ( cli. log_format, LogFormat :: Json ) ;
928+ }
929+
930+ #[ test]
931+ fn accepts_globals_on_other_subcommands ( ) {
932+ let subs: [ & [ & str ] ; 4 ] = [
933+ & [ "state" , "id" ] ,
934+ & [ "start" , "id" ] ,
935+ & [ "kill" , "id" , "SIGKILL" ] ,
936+ & [ "delete" , "--force" , "id" ] ,
937+ ] ;
938+ for sub in subs {
939+ let mut argv = vec ! [
940+ "sandlock-oci" , "--root" , "/r" , "--log" , "/l" ,
941+ "--log-format" , "json" , "--systemd-cgroup" ,
942+ ] ;
943+ argv. extend_from_slice ( sub) ;
944+ Cli :: try_parse_from ( & argv)
945+ . unwrap_or_else ( |e| panic ! ( "failed to parse {:?}: {e}" , argv) ) ;
946+ }
947+ }
948+
949+ #[ test]
950+ fn rootless_both_forms_parse ( ) {
951+ let bare = Cli :: try_parse_from ( [ "sandlock-oci" , "--rootless" , "list" ] ) . unwrap ( ) ;
952+ assert_eq ! ( bare. rootless, Some ( true ) ) ;
953+ let explicit =
954+ Cli :: try_parse_from ( [ "sandlock-oci" , "--rootless=false" , "list" ] ) . unwrap ( ) ;
955+ assert_eq ! ( explicit. rootless, Some ( false ) ) ;
956+ }
871957
872958 #[ test]
873959 fn parse_signal_numeric ( ) {
0 commit comments