xref: /wasmtime-44.0.1/src/common.rs (revision dbaaa92f)
1993e26e0STrevor Elliott //! Common functionality shared between command implementations.
2993e26e0STrevor Elliott 
3993e26e0STrevor Elliott use clap::Parser;
482670953SAlex Crichton use std::net::TcpListener;
515b464b6SAdam Bratschi-Kaye use std::{fs::File, path::Path, time::Duration};
694740588SNick Fitzgerald use wasmtime::{
794740588SNick Fitzgerald     Engine, Module, Precompiled, Result, StoreLimits, StoreLimitsBuilder, bail,
894740588SNick Fitzgerald     error::Context as _, format_err,
994740588SNick Fitzgerald };
1090ac295eSAlex Crichton use wasmtime_cli_flags::{CommonOptions, opt::WasmtimeOptionValue};
11f7a5aa34SAlex Crichton use wasmtime_wasi::WasiCtxBuilder;
12993e26e0STrevor Elliott 
13993e26e0STrevor Elliott #[cfg(feature = "component-model")]
14993e26e0STrevor Elliott use wasmtime::component::Component;
15993e26e0STrevor Elliott 
16eced7c7eSAlex Crichton /// Whether or not WASIp3 is enabled by default.
17eced7c7eSAlex Crichton ///
18eced7c7eSAlex Crichton /// Currently this is disabled (the `&& false`), but that'll get removed in the
19eced7c7eSAlex Crichton /// future.
20eced7c7eSAlex Crichton pub const P3_DEFAULT: bool = cfg!(feature = "component-model-async") && false;
21eced7c7eSAlex Crichton 
22133a0ef4SChris Fallin #[derive(Clone)]
23993e26e0STrevor Elliott pub enum RunTarget {
24993e26e0STrevor Elliott     Core(Module),
25993e26e0STrevor Elliott 
26993e26e0STrevor Elliott     #[cfg(feature = "component-model")]
27993e26e0STrevor Elliott     Component(Component),
28993e26e0STrevor Elliott }
29993e26e0STrevor Elliott 
30993e26e0STrevor Elliott impl RunTarget {
unwrap_core(&self) -> &Module31993e26e0STrevor Elliott     pub fn unwrap_core(&self) -> &Module {
32993e26e0STrevor Elliott         match self {
33993e26e0STrevor Elliott             RunTarget::Core(module) => module,
34993e26e0STrevor Elliott             #[cfg(feature = "component-model")]
35993e26e0STrevor Elliott             RunTarget::Component(_) => panic!("expected a core wasm module, not a component"),
36993e26e0STrevor Elliott         }
37993e26e0STrevor Elliott     }
38993e26e0STrevor Elliott 
39993e26e0STrevor Elliott     #[cfg(feature = "component-model")]
unwrap_component(&self) -> &Component40993e26e0STrevor Elliott     pub fn unwrap_component(&self) -> &Component {
41993e26e0STrevor Elliott         match self {
42993e26e0STrevor Elliott             RunTarget::Component(c) => c,
43993e26e0STrevor Elliott             RunTarget::Core(_) => panic!("expected a component, not a core wasm module"),
44993e26e0STrevor Elliott         }
45993e26e0STrevor Elliott     }
46993e26e0STrevor Elliott }
47993e26e0STrevor Elliott 
48993e26e0STrevor Elliott /// Common command line arguments for run commands.
49be12a665SAlex Crichton #[derive(Parser)]
50993e26e0STrevor Elliott pub struct RunCommon {
51f9f8a4dfSXinzhao Xu     #[command(flatten)]
52993e26e0STrevor Elliott     pub common: CommonOptions,
53993e26e0STrevor Elliott 
54993e26e0STrevor Elliott     /// Allow executing precompiled WebAssembly modules as `*.cwasm` files.
55993e26e0STrevor Elliott     ///
56993e26e0STrevor Elliott     /// Note that this option is not safe to pass if the module being passed in
57993e26e0STrevor Elliott     /// is arbitrary user input. Only `wasmtime`-precompiled modules generated
58993e26e0STrevor Elliott     /// via the `wasmtime compile` command or equivalent should be passed as an
59993e26e0STrevor Elliott     /// argument with this option specified.
60f9f8a4dfSXinzhao Xu     #[arg(long = "allow-precompiled")]
61993e26e0STrevor Elliott     pub allow_precompiled: bool,
62993e26e0STrevor Elliott 
63993e26e0STrevor Elliott     /// Profiling strategy (valid options are: perfmap, jitdump, vtune, guest)
64993e26e0STrevor Elliott     ///
65993e26e0STrevor Elliott     /// The perfmap, jitdump, and vtune profiling strategies integrate Wasmtime
66993e26e0STrevor Elliott     /// with external profilers such as `perf`. The guest profiling strategy
67993e26e0STrevor Elliott     /// enables in-process sampling and will write the captured profile to
68993e26e0STrevor Elliott     /// `wasmtime-guest-profile.json` by default which can be viewed at
69993e26e0STrevor Elliott     /// https://profiler.firefox.com/.
70993e26e0STrevor Elliott     ///
71993e26e0STrevor Elliott     /// The `guest` option can be additionally configured as:
72993e26e0STrevor Elliott     ///
73993e26e0STrevor Elliott     ///     --profile=guest[,path[,interval]]
74993e26e0STrevor Elliott     ///
75993e26e0STrevor Elliott     /// where `path` is where to write the profile and `interval` is the
76993e26e0STrevor Elliott     /// duration between samples. When used with `--wasm-timeout` the timeout
77993e26e0STrevor Elliott     /// will be rounded up to the nearest multiple of this interval.
78f9f8a4dfSXinzhao Xu     #[arg(
79993e26e0STrevor Elliott         long,
80993e26e0STrevor Elliott         value_name = "STRATEGY",
81993e26e0STrevor Elliott         value_parser = Profile::parse,
82993e26e0STrevor Elliott     )]
83993e26e0STrevor Elliott     pub profile: Option<Profile>,
8482670953SAlex Crichton 
8582670953SAlex Crichton     /// Grant access of a host directory to a guest.
8682670953SAlex Crichton     ///
8782670953SAlex Crichton     /// If specified as just `HOST_DIR` then the same directory name on the
8882670953SAlex Crichton     /// host is made available within the guest. If specified as `HOST::GUEST`
8982670953SAlex Crichton     /// then the `HOST` directory is opened and made available as the name
9082670953SAlex Crichton     /// `GUEST` in the guest.
9182670953SAlex Crichton     #[arg(long = "dir", value_name = "HOST_DIR[::GUEST_DIR]", value_parser = parse_dirs)]
9282670953SAlex Crichton     pub dirs: Vec<(String, String)>,
9382670953SAlex Crichton 
9482670953SAlex Crichton     /// Pass an environment variable to the program.
9582670953SAlex Crichton     ///
9682670953SAlex Crichton     /// The `--env FOO=BAR` form will set the environment variable named `FOO`
9782670953SAlex Crichton     /// to the value `BAR` for the guest program using WASI. The `--env FOO`
9882670953SAlex Crichton     /// form will set the environment variable named `FOO` to the same value it
9982670953SAlex Crichton     /// has in the calling process for the guest, or in other words it will
10082670953SAlex Crichton     /// cause the environment variable `FOO` to be inherited.
10182670953SAlex Crichton     #[arg(long = "env", number_of_values = 1, value_name = "NAME[=VAL]", value_parser = parse_env_var)]
10282670953SAlex Crichton     pub vars: Vec<(String, Option<String>)>,
103*dbaaa92fSChris Fallin 
104*dbaaa92fSChris Fallin     /// Attach the built-in gdbstub debugger component, listening on
105*dbaaa92fSChris Fallin     /// the given TCP address. Accepts a port number (e.g. `1234`) or
106*dbaaa92fSChris Fallin     /// a full `address:port`. A bare port number will bind on
107*dbaaa92fSChris Fallin     /// localhost only (`127.0.0.1`). A debugger (e.g. LLDB) can then
108*dbaaa92fSChris Fallin     /// connect via `process connect --plugin=wasm
109*dbaaa92fSChris Fallin     /// connect://<ADDR>:<PORT>`.
110*dbaaa92fSChris Fallin     #[cfg(feature = "gdbstub")]
111*dbaaa92fSChris Fallin     #[arg(short = 'g', long = "gdbstub", value_name = "[ADDR:]PORT")]
112*dbaaa92fSChris Fallin     pub gdbstub: Option<String>,
11382670953SAlex Crichton }
11482670953SAlex Crichton 
parse_env_var(s: &str) -> Result<(String, Option<String>)>11582670953SAlex Crichton fn parse_env_var(s: &str) -> Result<(String, Option<String>)> {
11682670953SAlex Crichton     let mut parts = s.splitn(2, '=');
11782670953SAlex Crichton     Ok((
11882670953SAlex Crichton         parts.next().unwrap().to_string(),
11982670953SAlex Crichton         parts.next().map(|s| s.to_string()),
12082670953SAlex Crichton     ))
12182670953SAlex Crichton }
12282670953SAlex Crichton 
parse_dirs(s: &str) -> Result<(String, String)>12382670953SAlex Crichton fn parse_dirs(s: &str) -> Result<(String, String)> {
12482670953SAlex Crichton     let mut parts = s.split("::");
12582670953SAlex Crichton     let host = parts.next().unwrap();
12682670953SAlex Crichton     let guest = match parts.next() {
12782670953SAlex Crichton         Some(guest) => guest,
12882670953SAlex Crichton         None => host,
12982670953SAlex Crichton     };
13082670953SAlex Crichton     Ok((host.into(), guest.into()))
131993e26e0STrevor Elliott }
132993e26e0STrevor Elliott 
133993e26e0STrevor Elliott impl RunCommon {
store_limits(&self) -> StoreLimits134993e26e0STrevor Elliott     pub fn store_limits(&self) -> StoreLimits {
135993e26e0STrevor Elliott         let mut limits = StoreLimitsBuilder::new();
136993e26e0STrevor Elliott         if let Some(max) = self.common.wasm.max_memory_size {
137993e26e0STrevor Elliott             limits = limits.memory_size(max);
138993e26e0STrevor Elliott         }
139993e26e0STrevor Elliott         if let Some(max) = self.common.wasm.max_table_elements {
140993e26e0STrevor Elliott             limits = limits.table_elements(max);
141993e26e0STrevor Elliott         }
142993e26e0STrevor Elliott         if let Some(max) = self.common.wasm.max_instances {
143993e26e0STrevor Elliott             limits = limits.instances(max);
144993e26e0STrevor Elliott         }
145993e26e0STrevor Elliott         if let Some(max) = self.common.wasm.max_tables {
146993e26e0STrevor Elliott             limits = limits.tables(max);
147993e26e0STrevor Elliott         }
148993e26e0STrevor Elliott         if let Some(max) = self.common.wasm.max_memories {
149993e26e0STrevor Elliott             limits = limits.memories(max);
150993e26e0STrevor Elliott         }
151993e26e0STrevor Elliott         if let Some(enable) = self.common.wasm.trap_on_grow_failure {
152993e26e0STrevor Elliott             limits = limits.trap_on_grow_failure(enable);
153993e26e0STrevor Elliott         }
154993e26e0STrevor Elliott 
155993e26e0STrevor Elliott         limits.build()
156993e26e0STrevor Elliott     }
157993e26e0STrevor Elliott 
ensure_allow_precompiled(&self) -> Result<()>158993e26e0STrevor Elliott     pub fn ensure_allow_precompiled(&self) -> Result<()> {
159993e26e0STrevor Elliott         if self.allow_precompiled {
160993e26e0STrevor Elliott             Ok(())
161993e26e0STrevor Elliott         } else {
162993e26e0STrevor Elliott             bail!("running a precompiled module requires the `--allow-precompiled` flag")
163993e26e0STrevor Elliott         }
164993e26e0STrevor Elliott     }
165993e26e0STrevor Elliott 
166993e26e0STrevor Elliott     #[cfg(feature = "component-model")]
ensure_allow_components(&self) -> Result<()>167993e26e0STrevor Elliott     fn ensure_allow_components(&self) -> Result<()> {
16894b3e84eSAlex Crichton         if self.common.wasm.component_model == Some(false) {
169993e26e0STrevor Elliott             bail!("cannot execute a component without `--wasm component-model`");
170993e26e0STrevor Elliott         }
171993e26e0STrevor Elliott 
172993e26e0STrevor Elliott         Ok(())
173993e26e0STrevor Elliott     }
174993e26e0STrevor Elliott 
load_module( &self, engine: &Engine, path: &Path, preloaded_bytes: Option<&[u8]>, ) -> Result<RunTarget>175*dbaaa92fSChris Fallin     pub fn load_module(
176*dbaaa92fSChris Fallin         &self,
177*dbaaa92fSChris Fallin         engine: &Engine,
178*dbaaa92fSChris Fallin         path: &Path,
179*dbaaa92fSChris Fallin         preloaded_bytes: Option<&[u8]>,
180*dbaaa92fSChris Fallin     ) -> Result<RunTarget> {
181993e26e0STrevor Elliott         let path = match path.to_str() {
182993e26e0STrevor Elliott             #[cfg(unix)]
183993e26e0STrevor Elliott             Some("-") => "/dev/stdin".as_ref(),
184993e26e0STrevor Elliott             _ => path,
185993e26e0STrevor Elliott         };
186*dbaaa92fSChris Fallin         if let Some(bytes) = preloaded_bytes {
187*dbaaa92fSChris Fallin             self.load_module_contents(
188*dbaaa92fSChris Fallin                 engine,
189*dbaaa92fSChris Fallin                 path,
190*dbaaa92fSChris Fallin                 &bytes,
191*dbaaa92fSChris Fallin                 || unsafe { Module::deserialize(engine, &bytes) },
192*dbaaa92fSChris Fallin                 #[cfg(feature = "component-model")]
193*dbaaa92fSChris Fallin                 || unsafe { Component::deserialize(engine, &bytes) },
194*dbaaa92fSChris Fallin             )
195*dbaaa92fSChris Fallin         } else {
1962ad3bea6SAlex Crichton             let file =
1972ad3bea6SAlex Crichton                 File::open(path).with_context(|| format!("failed to open wasm module {path:?}"))?;
198993e26e0STrevor Elliott 
199993e26e0STrevor Elliott             // First attempt to load the module as an mmap. If this succeeds then
200993e26e0STrevor Elliott             // detection can be done with the contents of the mmap and if a
201993e26e0STrevor Elliott             // precompiled module is detected then `deserialize_file` can be used
202993e26e0STrevor Elliott             // which is a slightly more optimal version than `deserialize` since we
203993e26e0STrevor Elliott             // can leave most of the bytes on disk until they're referenced.
204993e26e0STrevor Elliott             //
205993e26e0STrevor Elliott             // If the mmap fails, for example if stdin is a pipe, then fall back to
206993e26e0STrevor Elliott             // `std::fs::read` to load the contents. At that point precompiled
207993e26e0STrevor Elliott             // modules must go through the `deserialize` functions.
208993e26e0STrevor Elliott             //
209993e26e0STrevor Elliott             // Note that this has the unfortunate side effect for precompiled
210993e26e0STrevor Elliott             // modules on disk that they're opened once to detect what they are and
211993e26e0STrevor Elliott             // then again internally in Wasmtime as part of the `deserialize_file`
212993e26e0STrevor Elliott             // API. Currently there's no way to pass the `MmapVec` here through to
21372004aadSNick Fitzgerald             // Wasmtime itself (that'd require making `MmapVec` a public type, both
21472004aadSNick Fitzgerald             // which isn't ready to happen at this time). It's hoped though that
21572004aadSNick Fitzgerald             // opening a file twice isn't too bad in the grand scheme of things with
21672004aadSNick Fitzgerald             // respect to the CLI.
21715b464b6SAdam Bratschi-Kaye             match wasmtime::_internal::MmapVec::from_file(file) {
218993e26e0STrevor Elliott                 Ok(map) => self.load_module_contents(
219993e26e0STrevor Elliott                     engine,
220993e26e0STrevor Elliott                     path,
221993e26e0STrevor Elliott                     &map,
222993e26e0STrevor Elliott                     || unsafe { Module::deserialize_file(engine, path) },
223993e26e0STrevor Elliott                     #[cfg(feature = "component-model")]
224993e26e0STrevor Elliott                     || unsafe { Component::deserialize_file(engine, path) },
225993e26e0STrevor Elliott                 ),
226993e26e0STrevor Elliott                 Err(_) => {
227993e26e0STrevor Elliott                     let bytes = std::fs::read(path)
228993e26e0STrevor Elliott                         .with_context(|| format!("failed to read file: {}", path.display()))?;
229993e26e0STrevor Elliott                     self.load_module_contents(
230993e26e0STrevor Elliott                         engine,
231993e26e0STrevor Elliott                         path,
232993e26e0STrevor Elliott                         &bytes,
233993e26e0STrevor Elliott                         || unsafe { Module::deserialize(engine, &bytes) },
234993e26e0STrevor Elliott                         #[cfg(feature = "component-model")]
235993e26e0STrevor Elliott                         || unsafe { Component::deserialize(engine, &bytes) },
236993e26e0STrevor Elliott                     )
237993e26e0STrevor Elliott                 }
238993e26e0STrevor Elliott             }
239993e26e0STrevor Elliott         }
240*dbaaa92fSChris Fallin     }
241993e26e0STrevor Elliott 
load_module_contents( &self, engine: &Engine, path: &Path, bytes: &[u8], deserialize_module: impl FnOnce() -> Result<Module>, #[cfg(feature = "component-model")] deserialize_component: impl FnOnce() -> Result<Component>, ) -> Result<RunTarget>242993e26e0STrevor Elliott     pub fn load_module_contents(
243993e26e0STrevor Elliott         &self,
244993e26e0STrevor Elliott         engine: &Engine,
245993e26e0STrevor Elliott         path: &Path,
246993e26e0STrevor Elliott         bytes: &[u8],
247993e26e0STrevor Elliott         deserialize_module: impl FnOnce() -> Result<Module>,
248993e26e0STrevor Elliott         #[cfg(feature = "component-model")] deserialize_component: impl FnOnce() -> Result<Component>,
249993e26e0STrevor Elliott     ) -> Result<RunTarget> {
2503e406d2eSAlex Crichton         Ok(match Engine::detect_precompiled(bytes) {
251993e26e0STrevor Elliott             Some(Precompiled::Module) => {
252993e26e0STrevor Elliott                 self.ensure_allow_precompiled()?;
253993e26e0STrevor Elliott                 RunTarget::Core(deserialize_module()?)
254993e26e0STrevor Elliott             }
255993e26e0STrevor Elliott             #[cfg(feature = "component-model")]
256993e26e0STrevor Elliott             Some(Precompiled::Component) => {
257993e26e0STrevor Elliott                 self.ensure_allow_precompiled()?;
258993e26e0STrevor Elliott                 self.ensure_allow_components()?;
259993e26e0STrevor Elliott                 RunTarget::Component(deserialize_component()?)
260993e26e0STrevor Elliott             }
261993e26e0STrevor Elliott             #[cfg(not(feature = "component-model"))]
262993e26e0STrevor Elliott             Some(Precompiled::Component) => {
263993e26e0STrevor Elliott                 bail!("support for components was not enabled at compile time");
264993e26e0STrevor Elliott             }
26571951c9cSAlex Crichton             #[cfg(any(feature = "cranelift", feature = "winch"))]
266993e26e0STrevor Elliott             None => {
267f673cde3SAlex Crichton                 let mut code = wasmtime::CodeBuilder::new(engine);
268f673cde3SAlex Crichton                 code.wasm_binary_or_text(bytes, Some(path))?;
269f673cde3SAlex Crichton                 match code.hint() {
270f673cde3SAlex Crichton                     Some(wasmtime::CodeHint::Component) => {
271993e26e0STrevor Elliott                         #[cfg(feature = "component-model")]
272993e26e0STrevor Elliott                         {
273993e26e0STrevor Elliott                             self.ensure_allow_components()?;
274f673cde3SAlex Crichton                             RunTarget::Component(code.compile_component()?)
275993e26e0STrevor Elliott                         }
276993e26e0STrevor Elliott                         #[cfg(not(feature = "component-model"))]
277993e26e0STrevor Elliott                         {
278993e26e0STrevor Elliott                             bail!("support for components was not enabled at compile time");
279993e26e0STrevor Elliott                         }
280f673cde3SAlex Crichton                     }
281f673cde3SAlex Crichton                     Some(wasmtime::CodeHint::Module) | None => {
282f673cde3SAlex Crichton                         RunTarget::Core(code.compile_module()?)
283993e26e0STrevor Elliott                     }
284993e26e0STrevor Elliott                 }
285f673cde3SAlex Crichton             }
286f673cde3SAlex Crichton 
28771951c9cSAlex Crichton             #[cfg(not(any(feature = "cranelift", feature = "winch")))]
28871951c9cSAlex Crichton             None => {
2893e406d2eSAlex Crichton                 let _ = (path, engine);
29071951c9cSAlex Crichton                 bail!("support for compiling modules was disabled at compile time");
29171951c9cSAlex Crichton             }
292993e26e0STrevor Elliott         })
293993e26e0STrevor Elliott     }
29482670953SAlex Crichton 
configure_wasip2(&self, builder: &mut WasiCtxBuilder) -> Result<()>29582670953SAlex Crichton     pub fn configure_wasip2(&self, builder: &mut WasiCtxBuilder) -> Result<()> {
29682670953SAlex Crichton         // It's ok to block the current thread since we're the only thread in
29782670953SAlex Crichton         // the program as the CLI. This helps improve the performance of some
29882670953SAlex Crichton         // blocking operations in WASI, for example, by skipping the
29982670953SAlex Crichton         // back-and-forth between sync and async.
3004005a813SNick Fitzgerald         //
3014005a813SNick Fitzgerald         // However, do not set this if a timeout is configured, as that would
3024005a813SNick Fitzgerald         // cause the timeout to be ignored if the guest does, for example,
3034005a813SNick Fitzgerald         // something like `sleep(FOREVER)`.
3044005a813SNick Fitzgerald         builder.allow_blocking_current_thread(self.common.wasm.timeout.is_none());
30582670953SAlex Crichton 
30682670953SAlex Crichton         if self.common.wasi.inherit_env == Some(true) {
30782670953SAlex Crichton             for (k, v) in std::env::vars() {
30882670953SAlex Crichton                 builder.env(&k, &v);
30982670953SAlex Crichton             }
31082670953SAlex Crichton         }
31182670953SAlex Crichton         for (key, value) in self.vars.iter() {
31282670953SAlex Crichton             let value = match value {
31382670953SAlex Crichton                 Some(value) => value.clone(),
31482670953SAlex Crichton                 None => match std::env::var_os(key) {
31582670953SAlex Crichton                     Some(val) => val
31682670953SAlex Crichton                         .into_string()
31794740588SNick Fitzgerald                         .map_err(|_| format_err!("environment variable `{key}` not valid utf-8"))?,
31882670953SAlex Crichton                     None => {
31982670953SAlex Crichton                         // leave the env var un-set in the guest
32082670953SAlex Crichton                         continue;
32182670953SAlex Crichton                     }
32282670953SAlex Crichton                 },
32382670953SAlex Crichton             };
32482670953SAlex Crichton             builder.env(key, &value);
32582670953SAlex Crichton         }
32682670953SAlex Crichton 
32782670953SAlex Crichton         for (host, guest) in self.dirs.iter() {
32882670953SAlex Crichton             builder.preopened_dir(
32982670953SAlex Crichton                 host,
33082670953SAlex Crichton                 guest,
33182670953SAlex Crichton                 wasmtime_wasi::DirPerms::all(),
33282670953SAlex Crichton                 wasmtime_wasi::FilePerms::all(),
33382670953SAlex Crichton             )?;
33482670953SAlex Crichton         }
33582670953SAlex Crichton 
33682670953SAlex Crichton         if self.common.wasi.listenfd == Some(true) {
33782670953SAlex Crichton             bail!("components do not support --listenfd");
33882670953SAlex Crichton         }
33982670953SAlex Crichton         for _ in self.compute_preopen_sockets()? {
34082670953SAlex Crichton             bail!("components do not support --tcplisten");
34182670953SAlex Crichton         }
34282670953SAlex Crichton 
34382670953SAlex Crichton         if self.common.wasi.inherit_network == Some(true) {
34482670953SAlex Crichton             builder.inherit_network();
34582670953SAlex Crichton         }
34682670953SAlex Crichton         if let Some(enable) = self.common.wasi.allow_ip_name_lookup {
34782670953SAlex Crichton             builder.allow_ip_name_lookup(enable);
34882670953SAlex Crichton         }
34982670953SAlex Crichton         if let Some(enable) = self.common.wasi.tcp {
35082670953SAlex Crichton             builder.allow_tcp(enable);
35182670953SAlex Crichton         }
35282670953SAlex Crichton         if let Some(enable) = self.common.wasi.udp {
35382670953SAlex Crichton             builder.allow_udp(enable);
35482670953SAlex Crichton         }
355301dc716SAlex Crichton         if let Some(max_size) = self.common.wasi.max_random_size {
356301dc716SAlex Crichton             builder.max_random_size(max_size);
357301dc716SAlex Crichton         }
35882670953SAlex Crichton 
35982670953SAlex Crichton         Ok(())
36082670953SAlex Crichton     }
36182670953SAlex Crichton 
362301dc716SAlex Crichton     #[cfg(feature = "wasi-http")]
wasi_http_ctx(&self) -> Result<wasmtime_wasi_http::WasiHttpCtx>363301dc716SAlex Crichton     pub fn wasi_http_ctx(&self) -> Result<wasmtime_wasi_http::WasiHttpCtx> {
364301dc716SAlex Crichton         let mut http = wasmtime_wasi_http::WasiHttpCtx::new();
365301dc716SAlex Crichton         if let Some(limit) = self.common.wasi.max_http_fields_size {
366301dc716SAlex Crichton             http.set_field_size_limit(limit);
367301dc716SAlex Crichton         }
368301dc716SAlex Crichton         Ok(http)
369301dc716SAlex Crichton     }
370301dc716SAlex Crichton 
371365e2d89SAlex Crichton     #[cfg(feature = "wasi-http")]
wasi_http_hooks(&self) -> HttpHooks372365e2d89SAlex Crichton     pub fn wasi_http_hooks(&self) -> HttpHooks {
373365e2d89SAlex Crichton         HttpHooks {
374365e2d89SAlex Crichton             p2_outgoing_body_buffer_chunks: self
375365e2d89SAlex Crichton                 .common
376365e2d89SAlex Crichton                 .wasi
377365e2d89SAlex Crichton                 .http_outgoing_body_buffer_chunks
378365e2d89SAlex Crichton                 .unwrap_or_else(|| wasmtime_wasi_http::p2::DEFAULT_OUTGOING_BODY_BUFFER_CHUNKS),
379365e2d89SAlex Crichton             p2_outgoing_body_chunk_size: self
380365e2d89SAlex Crichton                 .common
381365e2d89SAlex Crichton                 .wasi
382365e2d89SAlex Crichton                 .http_outgoing_body_chunk_size
383365e2d89SAlex Crichton                 .unwrap_or_else(|| wasmtime_wasi_http::p2::DEFAULT_OUTGOING_BODY_CHUNK_SIZE),
384365e2d89SAlex Crichton         }
385365e2d89SAlex Crichton     }
386365e2d89SAlex Crichton 
compute_preopen_sockets(&self) -> Result<Vec<TcpListener>>38782670953SAlex Crichton     pub fn compute_preopen_sockets(&self) -> Result<Vec<TcpListener>> {
38882670953SAlex Crichton         let mut listeners = vec![];
38982670953SAlex Crichton 
39082670953SAlex Crichton         for address in &self.common.wasi.tcplisten {
39182670953SAlex Crichton             let stdlistener = std::net::TcpListener::bind(address)
39246098121SHamir Mahal                 .with_context(|| format!("failed to bind to address '{address}'"))?;
39382670953SAlex Crichton 
39482670953SAlex Crichton             let _ = stdlistener.set_nonblocking(true)?;
39582670953SAlex Crichton 
39682670953SAlex Crichton             listeners.push(stdlistener)
39782670953SAlex Crichton         }
39882670953SAlex Crichton         Ok(listeners)
39982670953SAlex Crichton     }
400c230353dSDave Bakker 
validate_p3_option(&self) -> Result<()>401eced7c7eSAlex Crichton     pub fn validate_p3_option(&self) -> Result<()> {
402eced7c7eSAlex Crichton         let p3 = self.common.wasi.p3.unwrap_or(P3_DEFAULT);
403eced7c7eSAlex Crichton         if p3 && !cfg!(feature = "component-model-async") {
404eced7c7eSAlex Crichton             bail!("support for WASIp3 disabled at compile time");
405eced7c7eSAlex Crichton         }
406eced7c7eSAlex Crichton         Ok(())
407eced7c7eSAlex Crichton     }
408eced7c7eSAlex Crichton 
validate_cli_enabled(&self) -> Result<Option<bool>>409eced7c7eSAlex Crichton     pub fn validate_cli_enabled(&self) -> Result<Option<bool>> {
410eced7c7eSAlex Crichton         let mut cli = self.common.wasi.cli;
411eced7c7eSAlex Crichton 
412eced7c7eSAlex Crichton         // Accept -Scommon as a deprecated alias for -Scli.
413eced7c7eSAlex Crichton         if let Some(common) = self.common.wasi.common {
414eced7c7eSAlex Crichton             if cli.is_some() {
415eced7c7eSAlex Crichton                 bail!(
416eced7c7eSAlex Crichton                     "The -Scommon option should not be use with -Scli as it is a deprecated alias"
417eced7c7eSAlex Crichton                 );
418eced7c7eSAlex Crichton             } else {
419eced7c7eSAlex Crichton                 // In the future, we may add a warning here to tell users to use
420eced7c7eSAlex Crichton                 // `-S cli` instead of `-S common`.
421eced7c7eSAlex Crichton                 cli = Some(common);
422eced7c7eSAlex Crichton             }
423eced7c7eSAlex Crichton         }
424eced7c7eSAlex Crichton 
425eced7c7eSAlex Crichton         Ok(cli)
426eced7c7eSAlex Crichton     }
427eced7c7eSAlex Crichton 
428eced7c7eSAlex Crichton     /// Adds `wasmtime-wasi` interfaces (dubbed "-Scli" in the flags to the
429eced7c7eSAlex Crichton     /// `wasmtime` command) to the `linker` provided.
430eced7c7eSAlex Crichton     ///
431eced7c7eSAlex Crichton     /// This will handle adding various WASI standard versions to the linker
432eced7c7eSAlex Crichton     /// internally.
433eced7c7eSAlex Crichton     #[cfg(feature = "component-model")]
add_wasmtime_wasi_to_linker<T>( &self, linker: &mut wasmtime::component::Linker<T>, ) -> Result<()> where T: wasmtime_wasi::WasiView,434eced7c7eSAlex Crichton     pub fn add_wasmtime_wasi_to_linker<T>(
435eced7c7eSAlex Crichton         &self,
436eced7c7eSAlex Crichton         linker: &mut wasmtime::component::Linker<T>,
437eced7c7eSAlex Crichton     ) -> Result<()>
438eced7c7eSAlex Crichton     where
439eced7c7eSAlex Crichton         T: wasmtime_wasi::WasiView,
440eced7c7eSAlex Crichton     {
441eced7c7eSAlex Crichton         let mut p2_options = wasmtime_wasi::p2::bindings::LinkOptions::default();
442eced7c7eSAlex Crichton         p2_options.cli_exit_with_code(self.common.wasi.cli_exit_with_code.unwrap_or(false));
443eced7c7eSAlex Crichton         p2_options.network_error_code(self.common.wasi.network_error_code.unwrap_or(false));
444eced7c7eSAlex Crichton         wasmtime_wasi::p2::add_to_linker_with_options_async(linker, &p2_options)?;
445eced7c7eSAlex Crichton 
446eced7c7eSAlex Crichton         #[cfg(feature = "component-model-async")]
447eced7c7eSAlex Crichton         if self.common.wasi.p3.unwrap_or(P3_DEFAULT) {
448eced7c7eSAlex Crichton             let mut p3_options = wasmtime_wasi::p3::bindings::LinkOptions::default();
449eced7c7eSAlex Crichton             p3_options.cli_exit_with_code(self.common.wasi.cli_exit_with_code.unwrap_or(false));
450eced7c7eSAlex Crichton             wasmtime_wasi::p3::add_to_linker_with_options(linker, &p3_options)
451eced7c7eSAlex Crichton                 .context("failed to link `wasi:[email protected]`")?;
452eced7c7eSAlex Crichton         }
453eced7c7eSAlex Crichton 
454eced7c7eSAlex Crichton         Ok(())
455c230353dSDave Bakker     }
456993e26e0STrevor Elliott }
457993e26e0STrevor Elliott 
458519808fcSAlex Crichton #[derive(Clone, PartialEq)]
459993e26e0STrevor Elliott pub enum Profile {
460993e26e0STrevor Elliott     Native(wasmtime::ProfilingStrategy),
461993e26e0STrevor Elliott     Guest { path: String, interval: Duration },
462993e26e0STrevor Elliott }
463993e26e0STrevor Elliott 
464993e26e0STrevor Elliott impl Profile {
465993e26e0STrevor Elliott     /// Parse the `profile` argument to either the `run` or `serve` commands.
parse(s: &str) -> Result<Profile>466993e26e0STrevor Elliott     pub fn parse(s: &str) -> Result<Profile> {
467993e26e0STrevor Elliott         let parts = s.split(',').collect::<Vec<_>>();
468993e26e0STrevor Elliott         match &parts[..] {
469993e26e0STrevor Elliott             ["perfmap"] => Ok(Profile::Native(wasmtime::ProfilingStrategy::PerfMap)),
470993e26e0STrevor Elliott             ["jitdump"] => Ok(Profile::Native(wasmtime::ProfilingStrategy::JitDump)),
471993e26e0STrevor Elliott             ["vtune"] => Ok(Profile::Native(wasmtime::ProfilingStrategy::VTune)),
4721d1c06f3SAlex Crichton             ["pulley"] => Ok(Profile::Native(wasmtime::ProfilingStrategy::Pulley)),
473993e26e0STrevor Elliott             ["guest"] => Ok(Profile::Guest {
474993e26e0STrevor Elliott                 path: "wasmtime-guest-profile.json".to_string(),
475993e26e0STrevor Elliott                 interval: Duration::from_millis(10),
476993e26e0STrevor Elliott             }),
477993e26e0STrevor Elliott             ["guest", path] => Ok(Profile::Guest {
478993e26e0STrevor Elliott                 path: path.to_string(),
479993e26e0STrevor Elliott                 interval: Duration::from_millis(10),
480993e26e0STrevor Elliott             }),
481993e26e0STrevor Elliott             ["guest", path, dur] => Ok(Profile::Guest {
482993e26e0STrevor Elliott                 path: path.to_string(),
483993e26e0STrevor Elliott                 interval: WasmtimeOptionValue::parse(Some(dur))?,
484993e26e0STrevor Elliott             }),
485993e26e0STrevor Elliott             _ => bail!("unknown profiling strategy: {s}"),
486993e26e0STrevor Elliott         }
487993e26e0STrevor Elliott     }
488993e26e0STrevor Elliott }
489c90925f6SAlex Crichton 
490365e2d89SAlex Crichton #[derive(Copy, Clone, Debug)]
491365e2d89SAlex Crichton #[cfg(feature = "wasi-http")]
492365e2d89SAlex Crichton pub struct HttpHooks {
493365e2d89SAlex Crichton     p2_outgoing_body_buffer_chunks: usize,
494365e2d89SAlex Crichton     p2_outgoing_body_chunk_size: usize,
495365e2d89SAlex Crichton }
496365e2d89SAlex Crichton 
497365e2d89SAlex Crichton #[cfg(feature = "wasi-http")]
498365e2d89SAlex Crichton impl Default for HttpHooks {
default() -> Self499365e2d89SAlex Crichton     fn default() -> Self {
500365e2d89SAlex Crichton         Self {
501365e2d89SAlex Crichton             p2_outgoing_body_buffer_chunks:
502365e2d89SAlex Crichton                 wasmtime_wasi_http::p2::DEFAULT_OUTGOING_BODY_BUFFER_CHUNKS,
503365e2d89SAlex Crichton             p2_outgoing_body_chunk_size: wasmtime_wasi_http::p2::DEFAULT_OUTGOING_BODY_CHUNK_SIZE,
504365e2d89SAlex Crichton         }
505365e2d89SAlex Crichton     }
506365e2d89SAlex Crichton }
507365e2d89SAlex Crichton 
508365e2d89SAlex Crichton #[cfg(feature = "wasi-http")]
509365e2d89SAlex Crichton impl wasmtime_wasi_http::p2::WasiHttpHooks for HttpHooks {
outgoing_body_buffer_chunks(&mut self) -> usize510365e2d89SAlex Crichton     fn outgoing_body_buffer_chunks(&mut self) -> usize {
511365e2d89SAlex Crichton         self.p2_outgoing_body_buffer_chunks
512365e2d89SAlex Crichton     }
513365e2d89SAlex Crichton 
outgoing_body_chunk_size(&mut self) -> usize514365e2d89SAlex Crichton     fn outgoing_body_chunk_size(&mut self) -> usize {
515365e2d89SAlex Crichton         self.p2_outgoing_body_chunk_size
516365e2d89SAlex Crichton     }
517365e2d89SAlex Crichton }
518365e2d89SAlex Crichton 
519365e2d89SAlex Crichton #[cfg(feature = "wasi-http")]
520365e2d89SAlex Crichton impl wasmtime_wasi_http::p3::WasiHttpHooks for HttpHooks {}
521