11//! The module that implements the `wasmtime run` command.
22
33use anyhow:: { anyhow, bail, Context as _, Result } ;
4- use clap:: builder:: { OsStringValueParser , TypedValueParser } ;
54use clap:: Parser ;
65use once_cell:: sync:: Lazy ;
7- use std:: ffi:: OsStr ;
8- use std:: ffi:: OsString ;
96use std:: fs:: File ;
107use std:: io:: Write ;
11- use std:: path:: { Component , Path , PathBuf } ;
8+ use std:: path:: { Path , PathBuf } ;
129use std:: thread;
1310use std:: time:: Duration ;
1411use wasmtime:: {
@@ -39,18 +36,6 @@ use wasmtime_wasi_threads::WasiThreadsCtx;
3936#[ cfg( feature = "wasi-http" ) ]
4037use wasmtime_wasi_http:: WasiHttp ;
4138
42- fn parse_module ( s : OsString ) -> anyhow:: Result < PathBuf > {
43- // Do not accept wasmtime subcommand names as the module name
44- match s. to_str ( ) {
45- Some ( "help" ) | Some ( "config" ) | Some ( "run" ) | Some ( "wast" ) | Some ( "compile" ) => {
46- bail ! ( "module name cannot be the same as a subcommand" )
47- }
48- #[ cfg( unix) ]
49- Some ( "-" ) => Ok ( PathBuf :: from ( "/dev/stdin" ) ) ,
50- _ => Ok ( s. into ( ) ) ,
51- }
52- }
53-
5439fn parse_env_var ( s : & str ) -> Result < ( String , Option < String > ) > {
5540 let mut parts = s. splitn ( 2 , '=' ) ;
5641 Ok ( (
@@ -111,7 +96,7 @@ static AFTER_HELP: Lazy<String> = Lazy::new(|| crate::FLAG_EXPLANATIONS.to_strin
11196
11297/// Runs a WebAssembly module
11398#[ derive( Parser ) ]
114- #[ structopt( name = "run" , trailing_var_arg = true , after_help = AFTER_HELP . as_str( ) ) ]
99+ #[ structopt( name = "run" , after_help = AFTER_HELP . as_str( ) ) ]
115100pub struct RunCommand {
116101 #[ clap( flatten) ]
117102 common : CommonOptions ,
@@ -174,14 +159,6 @@ pub struct RunCommand {
174159 #[ clap( long = "mapdir" , number_of_values = 1 , value_name = "GUEST_DIR::HOST_DIR" , value_parser = parse_map_dirs) ]
175160 map_dirs : Vec < ( String , String ) > ,
176161
177- /// The path of the WebAssembly module to run
178- #[ clap(
179- required = true ,
180- value_name = "MODULE" ,
181- value_parser = OsStringValueParser :: new( ) . try_map( parse_module) ,
182- ) ]
183- module : PathBuf ,
184-
185162 /// Load the given WebAssembly module before the main module
186163 #[ clap(
187164 long = "preload" ,
@@ -225,11 +202,6 @@ pub struct RunCommand {
225202 #[ clap( long = "coredump-on-trap" , value_name = "PATH" ) ]
226203 coredump_on_trap : Option < String > ,
227204
228- // NOTE: this must come last for trailing varargs
229- /// The arguments to pass to the module
230- #[ clap( value_name = "ARGS" ) ]
231- module_args : Vec < String > ,
232-
233205 /// Maximum size, in bytes, that a linear memory is allowed to reach.
234206 ///
235207 /// Growth beyond this limit will cause `memory.grow` instructions in
@@ -261,6 +233,14 @@ pub struct RunCommand {
261233 /// memory, for example.
262234 #[ clap( long) ]
263235 trap_on_grow_failure : bool ,
236+
237+ /// The WebAssembly module to run and arguments to pass to it.
238+ ///
239+ /// Arguments passed to the wasm module will be configured as WASI CLI
240+ /// arguments unless the `--invoke` CLI argument is passed in which case
241+ /// arguments will be interpreted as arguments to the function specified.
242+ #[ clap( value_name = "WASM" , trailing_var_arg = true , required = true ) ]
243+ module_and_args : Vec < PathBuf > ,
264244}
265245
266246#[ derive( Clone ) ]
@@ -303,13 +283,13 @@ impl RunCommand {
303283
304284 // Make wasi available by default.
305285 let preopen_dirs = self . compute_preopen_dirs ( ) ?;
306- let argv = self . compute_argv ( ) ;
286+ let argv = self . compute_argv ( ) ? ;
307287
308288 let mut linker = Linker :: new ( & engine) ;
309289 linker. allow_unknown_exports ( self . allow_unknown_exports ) ;
310290
311291 // Read the wasm module binary either as `*.wat` or a raw binary.
312- let module = self . load_module ( linker. engine ( ) , & self . module ) ?;
292+ let module = self . load_module ( linker. engine ( ) , & self . module_and_args [ 0 ] ) ?;
313293 let mut modules = vec ! [ ( String :: new( ) , module. clone( ) ) ] ;
314294
315295 let host = Host :: default ( ) ;
@@ -370,8 +350,12 @@ impl RunCommand {
370350 // Load the main wasm module.
371351 match self
372352 . load_main_module ( & mut store, & mut linker, module, modules, & argv[ 0 ] )
373- . with_context ( || format ! ( "failed to run main module `{}`" , self . module. display( ) ) )
374- {
353+ . with_context ( || {
354+ format ! (
355+ "failed to run main module `{}`" ,
356+ self . module_and_args[ 0 ] . display( )
357+ )
358+ } ) {
375359 Ok ( ( ) ) => ( ) ,
376360 Err ( e) => {
377361 // Exit the process if Wasmtime understands the error;
@@ -420,27 +404,25 @@ impl RunCommand {
420404 Ok ( listeners)
421405 }
422406
423- fn compute_argv ( & self ) -> Vec < String > {
407+ fn compute_argv ( & self ) -> Result < Vec < String > > {
424408 let mut result = Vec :: new ( ) ;
425409
426- // Add argv[0], which is the program name. Only include the base name of the
427- // main wasm module, to avoid leaking path information.
428- result. push (
429- self . module
430- . components ( )
431- . next_back ( )
432- . map ( Component :: as_os_str)
433- . and_then ( OsStr :: to_str)
434- . unwrap_or ( "" )
435- . to_owned ( ) ,
436- ) ;
437-
438- // Add the remaining arguments.
439- for arg in self . module_args . iter ( ) {
440- result. push ( arg. clone ( ) ) ;
410+ for ( i, arg) in self . module_and_args . iter ( ) . enumerate ( ) {
411+ // For argv[0], which is the program name. Only include the base
412+ // name of the main wasm module, to avoid leaking path information.
413+ let arg = if i == 0 {
414+ arg. components ( ) . next_back ( ) . unwrap ( ) . as_os_str ( )
415+ } else {
416+ arg. as_ref ( )
417+ } ;
418+ result. push (
419+ arg. to_str ( )
420+ . ok_or_else ( || anyhow ! ( "failed to convert {arg:?} to utf-8" ) ) ?
421+ . to_string ( ) ,
422+ ) ;
441423 }
442424
443- result
425+ Ok ( result)
444426 }
445427
446428 fn setup_epoch_handler (
@@ -541,9 +523,10 @@ impl RunCommand {
541523 }
542524
543525 // Use "" as a default module name.
544- linker
545- . module ( & mut * store, "" , & module)
546- . context ( format ! ( "failed to instantiate {:?}" , self . module) ) ?;
526+ linker. module ( & mut * store, "" , & module) . context ( format ! (
527+ "failed to instantiate {:?}" ,
528+ self . module_and_args[ 0 ]
529+ ) ) ?;
547530
548531 // If a function to invoke was given, invoke it.
549532 let func = if let Some ( name) = & self . invoke {
@@ -584,7 +567,7 @@ impl RunCommand {
584567 is experimental and may break in the future"
585568 ) ;
586569 }
587- let mut args = self . module_args . iter ( ) ;
570+ let mut args = self . module_and_args . iter ( ) . skip ( 1 ) ;
588571 let mut values = Vec :: new ( ) ;
589572 for ty in ty. params ( ) {
590573 let val = match args. next ( ) {
@@ -597,6 +580,9 @@ impl RunCommand {
597580 }
598581 }
599582 } ;
583+ let val = val
584+ . to_str ( )
585+ . ok_or_else ( || anyhow ! ( "argument is not valid utf-8: {val:?}" ) ) ?;
600586 values. push ( match ty {
601587 // TODO: integer parsing here should handle hexadecimal notation
602588 // like `0x0...`, but the Rust standard library currently only
@@ -623,7 +609,9 @@ impl RunCommand {
623609 if let Err ( err) = invoke_res {
624610 let err = if err. is :: < wasmtime:: Trap > ( ) {
625611 if let Some ( coredump_path) = self . coredump_on_trap . as_ref ( ) {
626- let source_name = self . module . to_str ( ) . unwrap_or_else ( || "unknown" ) ;
612+ let source_name = self . module_and_args [ 0 ]
613+ . to_str ( )
614+ . unwrap_or_else ( || "unknown" ) ;
627615
628616 if let Err ( coredump_err) = generate_coredump ( & err, & source_name, coredump_path)
629617 {
@@ -664,6 +652,12 @@ impl RunCommand {
664652 }
665653
666654 fn load_module ( & self , engine : & Engine , path : & Path ) -> Result < Module > {
655+ let path = match path. to_str ( ) {
656+ #[ cfg( unix) ]
657+ Some ( "-" ) => "/dev/stdin" . as_ref ( ) ,
658+ _ => path,
659+ } ;
660+
667661 if self . allow_precompiled {
668662 unsafe { Module :: from_trusted_file ( engine, path) }
669663 } else {
0 commit comments