@@ -13,7 +13,7 @@ use std::{
1313} ;
1414use structopt:: { clap:: AppSettings , StructOpt } ;
1515use wasmtime:: { Engine , Func , Linker , Module , Store , Trap , Val , ValType } ;
16- use wasmtime_wasi:: sync:: { ambient_authority, Dir , WasiCtxBuilder } ;
16+ use wasmtime_wasi:: sync:: { ambient_authority, Dir , TcpListener , WasiCtxBuilder } ;
1717
1818#[ cfg( feature = "wasi-nn" ) ]
1919use wasmtime_wasi_nn:: WasiNnCtx ;
@@ -91,6 +91,19 @@ pub struct RunCommand {
9191 #[ structopt( long = "allow-precompiled" ) ]
9292 allow_precompiled : bool ,
9393
94+ /// Inherit environment variables and file descriptors following the
95+ /// systemd listen fd specification (UNIX only)
96+ #[ structopt( long = "listenfd" ) ]
97+ listenfd : bool ,
98+
99+ /// Grant access to the given TCP listen socket
100+ #[ structopt(
101+ long = "tcplisten" ,
102+ number_of_values = 1 ,
103+ value_name = "SOCKET ADDRESS"
104+ ) ]
105+ tcplisten : Vec < String > ,
106+
94107 /// Grant access to the given host directory
95108 #[ structopt( long = "dir" , number_of_values = 1 , value_name = "DIRECTORY" ) ]
96109 dirs : Vec < String > ,
@@ -165,6 +178,8 @@ impl RunCommand {
165178 & argv,
166179 & self . vars ,
167180 & self . common . wasi_modules . unwrap_or ( WasiModules :: default ( ) ) ,
181+ self . listenfd ,
182+ & self . tcplisten ,
168183 ) ?;
169184
170185 // Load the preload wasm modules.
@@ -415,16 +430,39 @@ fn populate_with_wasi(
415430 argv : & [ String ] ,
416431 vars : & [ ( String , String ) ] ,
417432 wasi_modules : & WasiModules ,
433+ listenfd : bool ,
434+ tcplisten : & Vec < String > ,
418435) -> Result < ( ) > {
419436 if wasi_modules. wasi_common {
420437 wasmtime_wasi:: add_to_linker ( linker, |host| host. wasi . as_mut ( ) . unwrap ( ) ) ?;
421438
422439 let mut builder = WasiCtxBuilder :: new ( ) ;
423440 builder = builder. inherit_stdio ( ) . args ( argv) ?. envs ( vars) ?;
424441
442+ let mut num_fd: usize = 3 ;
443+
444+ if listenfd {
445+ let ( n, b) = ctx_set_listenfd ( num_fd, builder) ?;
446+ num_fd = n;
447+ builder = b;
448+ }
449+
450+ for address in tcplisten. iter ( ) {
451+ let stdlistener = std:: net:: TcpListener :: bind ( address)
452+ . with_context ( || format ! ( "failed to bind to address '{}'" , address) ) ?;
453+
454+ let _ = stdlistener. set_nonblocking ( true ) ?;
455+
456+ let listener = TcpListener :: from_std ( stdlistener) ;
457+
458+ builder = builder. preopened_listener ( num_fd as _ , listener) ?;
459+ num_fd += 1 ;
460+ }
461+
425462 for ( name, dir) in preopen_dirs. into_iter ( ) {
426463 builder = builder. preopened_dir ( dir, name) ?;
427464 }
465+
428466 store. data_mut ( ) . wasi = Some ( builder. build ( ) ) ;
429467 }
430468
@@ -454,3 +492,55 @@ fn populate_with_wasi(
454492
455493 Ok ( ( ) )
456494}
495+
496+ #[ cfg( not( unix) ) ]
497+ fn ctx_set_listenfd ( num_fd : usize , builder : WasiCtxBuilder ) -> Result < ( usize , WasiCtxBuilder ) > {
498+ use listenfd:: ListenFd ;
499+
500+ let mut builder = builder;
501+ let mut num_fd = num_fd;
502+
503+ let mut listenfd = ListenFd :: from_env ( ) ;
504+
505+ for i in 0 ..listenfd. len ( ) {
506+ if let Some ( stdlistener) = listenfd. take_tcp_listener ( i) ? {
507+ let _ = stdlistener. set_nonblocking ( true ) ?;
508+ let listener = TcpListener :: from_std ( stdlistener) ;
509+ builder = builder. preopened_listener ( ( 3 + i) as _ , listener) ?;
510+ num_fd = 3 + i;
511+ }
512+ }
513+
514+ if num_fd > 3 {
515+ builder = builder. env ( "LISTEN_FDS" , & ( num_fd - 3 ) . to_string ( ) ) ?;
516+ }
517+
518+ Ok ( ( num_fd, builder) )
519+ }
520+
521+ #[ cfg( unix) ]
522+ fn ctx_set_listenfd ( num_fd : usize , builder : WasiCtxBuilder ) -> Result < ( usize , WasiCtxBuilder ) > {
523+ use listenfd:: ListenFd ;
524+
525+ let mut builder = builder;
526+ let mut num_fd = num_fd;
527+
528+ for env in [ "LISTEN_FDS" , "LISTEN_FDNAMES" ] {
529+ if let Ok ( val) = std:: env:: var ( env) {
530+ builder = builder. env ( env, & val) ?;
531+ }
532+ }
533+
534+ let mut listenfd = ListenFd :: from_env ( ) ;
535+
536+ for i in 0 ..listenfd. len ( ) {
537+ if let Some ( stdlistener) = listenfd. take_tcp_listener ( i) ? {
538+ let _ = stdlistener. set_nonblocking ( true ) ?;
539+ let listener = TcpListener :: from_std ( stdlistener) ;
540+ builder = builder. preopened_listener ( ( 3 + i) as _ , listener) ?;
541+ num_fd = 3 + i;
542+ }
543+ }
544+
545+ Ok ( ( num_fd, builder) )
546+ }
0 commit comments