1mod add;
3mod app;
4mod auth;
5#[cfg(target_os = "linux")]
6mod binfmt;
7mod cache;
8#[cfg(feature = "compiler")]
9mod compile;
10mod config;
11mod connect;
12mod container;
13#[cfg(any(feature = "static-artifact-create", feature = "wasmer-artifact-create"))]
14mod create_exe;
15#[cfg(feature = "static-artifact-create")]
16mod create_obj;
17pub(crate) mod domain;
18#[cfg(feature = "static-artifact-create")]
19mod gen_c_header;
20mod gen_completions;
21mod gen_manpage;
22mod init;
23mod inspect;
24#[cfg(feature = "journal")]
25mod journal;
26pub(crate) mod namespace;
27mod package;
28mod run;
29mod self_update;
30pub mod ssh;
31mod validate;
32#[cfg(feature = "wast")]
33mod wast;
34use std::ffi::OsString;
35use tokio::task::JoinHandle;
36
37#[cfg(target_os = "linux")]
38pub use binfmt::*;
39use clap::{CommandFactory, Parser};
40#[cfg(feature = "compiler")]
41pub use compile::*;
42#[cfg(any(feature = "static-artifact-create", feature = "wasmer-artifact-create"))]
43pub use create_exe::*;
44#[cfg(feature = "wast")]
45pub use wast::*;
46#[cfg(feature = "static-artifact-create")]
47pub use {create_obj::*, gen_c_header::*};
48
49#[cfg(feature = "journal")]
50pub use self::journal::*;
51pub use self::{
52 add::*, auth::*, cache::*, config::*, container::*, init::*, inspect::*, package::*,
53 publish::*, run::Run, self_update::*, validate::*,
54};
55use crate::error::PrettyError;
56
57pub(crate) trait CliCommand {
59 type Output;
60
61 fn run(self) -> Result<(), anyhow::Error>;
62}
63
64#[async_trait::async_trait]
69pub(crate) trait AsyncCliCommand: Send + Sync {
70 type Output: Send + Sync;
71
72 async fn run_async(self) -> Result<Self::Output, anyhow::Error>;
73
74 fn setup(
75 &self,
76 done: tokio::sync::oneshot::Receiver<()>,
77 ) -> Option<JoinHandle<anyhow::Result<()>>> {
78 if is_terminal::IsTerminal::is_terminal(&std::io::stdin()) {
79 return Some(tokio::task::spawn(async move {
80 tokio::select! {
81 _ = done => {}
82
83 _ = tokio::signal::ctrl_c() => {
84 let term = console::Term::stdout();
85 let _ = term.show_cursor();
86 #[cfg(target_os = "windows")]
88 std::process::exit(3);
89
90 #[cfg(not(target_os = "windows"))]
92 std::process::exit(130);
93 }
94 }
95
96 Ok::<(), anyhow::Error>(())
97 }));
98 }
99
100 None
101 }
102}
103
104impl<O: Send + Sync, C: AsyncCliCommand<Output = O>> CliCommand for C {
105 type Output = O;
106
107 fn run(self) -> Result<(), anyhow::Error> {
108 tokio::runtime::Runtime::new()?.block_on(async {
109 let (snd, rcv) = tokio::sync::oneshot::channel();
110 let handle = self.setup(rcv);
111
112 if let Err(e) = AsyncCliCommand::run_async(self).await {
113 if let Some(handle) = handle {
114 handle.abort();
115 }
116 return Err(e);
117 }
118
119 if let Some(handle) = handle {
120 if snd.send(()).is_err() {
121 tracing::warn!("Failed to send 'done' signal to setup thread!");
122 handle.abort();
123 } else {
124 handle.await??;
125 }
126 }
127
128 Ok::<(), anyhow::Error>(())
129 })?;
130
131 Ok(())
132 }
133}
134
135#[derive(clap::Parser, Debug)]
137#[clap(author, version)]
138#[clap(disable_version_flag = true)] #[cfg_attr(feature = "headless", clap(
140 name = "wasmer-headless",
141 about = concat!("wasmer-headless ", env!("CARGO_PKG_VERSION")),
142))]
143#[cfg_attr(not(feature = "headless"), clap(
144 name = "wasmer",
145 about = concat!("wasmer ", env!("CARGO_PKG_VERSION")),
146))]
147pub struct WasmerCmd {
148 #[clap(short = 'V', long)]
150 version: bool,
151 #[clap(flatten)]
152 output: crate::logging::Output,
153 #[clap(subcommand)]
154 cmd: Option<Cmd>,
155}
156
157impl WasmerCmd {
158 fn execute(self) -> Result<(), anyhow::Error> {
159 let WasmerCmd {
160 cmd,
161 version,
162 output,
163 } = self;
164
165 output.initialize_logging();
166
167 if version {
168 return print_version(output.is_verbose());
169 }
170
171 match cmd {
172 Some(Cmd::GenManPage(cmd)) => cmd.execute(),
173 Some(Cmd::GenCompletions(cmd)) => cmd.execute(),
174 Some(Cmd::Run(options)) => options.execute(output),
175 Some(Cmd::SelfUpdate(options)) => options.execute(),
176 Some(Cmd::Cache(cache)) => cache.execute(),
177 Some(Cmd::Validate(validate)) => validate.execute(),
178 #[cfg(feature = "compiler")]
179 Some(Cmd::Compile(compile)) => compile.execute(),
180 #[cfg(any(feature = "static-artifact-create", feature = "wasmer-artifact-create"))]
181 Some(Cmd::CreateExe(create_exe)) => create_exe.run(),
182 #[cfg(feature = "static-artifact-create")]
183 Some(Cmd::CreateObj(create_obj)) => create_obj.execute(),
184 Some(Cmd::Config(config)) => config.run(),
185 Some(Cmd::Inspect(inspect)) => inspect.execute(),
186 Some(Cmd::Init(init)) => init.run(),
187 Some(Cmd::Login(login)) => login.run(),
188 Some(Cmd::Auth(auth)) => auth.run(),
189 Some(Cmd::Publish(publish)) => publish.run().map(|_| ()),
190 Some(Cmd::Package(cmd)) => match cmd {
191 Package::Download(cmd) => cmd.execute(),
192 Package::Build(cmd) => cmd.execute().map(|_| ()),
193 Package::Tag(cmd) => cmd.run(),
194 Package::Push(cmd) => cmd.run(),
195 Package::Publish(cmd) => cmd.run().map(|_| ()),
196 Package::Unpack(cmd) => cmd.execute(),
197 },
198 Some(Cmd::Container(cmd)) => match cmd {
199 crate::commands::Container::Unpack(cmd) => cmd.execute(),
200 },
201 #[cfg(feature = "static-artifact-create")]
202 Some(Cmd::GenCHeader(gen_heder)) => gen_heder.execute(),
203 #[cfg(feature = "wast")]
204 Some(Cmd::Wast(wast)) => wast.execute(),
205 #[cfg(target_os = "linux")]
206 Some(Cmd::Binfmt(binfmt)) => binfmt.execute(),
207 Some(Cmd::Whoami(whoami)) => whoami.run(),
208 Some(Cmd::Add(add)) => add.run(),
209
210 Some(Cmd::Deploy(c)) => c.run(),
212 Some(Cmd::App(apps)) => apps.run(),
213 #[cfg(feature = "journal")]
214 Some(Cmd::Journal(journal)) => journal.run(),
215 Some(Cmd::Ssh(ssh)) => ssh.run(),
216 Some(Cmd::Namespace(namespace)) => namespace.run(),
217 Some(Cmd::Domain(namespace)) => namespace.run(),
218 None => {
219 WasmerCmd::command().print_long_help()?;
220 std::process::exit(2);
222 }
223 }
224 }
225
226 pub fn run() {
228 #[cfg(windows)]
230 colored::control::set_virtual_terminal(true).unwrap();
231
232 PrettyError::report(Self::run_inner())
233 }
234
235 fn run_inner() -> Result<(), anyhow::Error> {
236 let mut args_os = std::env::args_os();
237
238 let args = args_os.next().into_iter();
239
240 let mut binfmt_args: Vec<OsString> = Vec::new();
241 if is_binfmt_interpreter() {
242 let current_dir = std::env::current_dir()
249 .unwrap()
250 .into_os_string()
251 .into_string()
252 .unwrap();
253 let mut mount_paths = vec!["/home", "/etc", "/tmp", "/var", "/nix", "/opt", "/root"]
254 .into_iter()
255 .filter(|path| {
256 let path = std::path::Path::new(path);
257 if !path.is_dir() {
258 return false;
260 }
261 if std::fs::read_dir(path).is_err() {
262 return false;
264 }
265 true
266 })
267 .collect::<Vec<_>>();
268 if !current_dir.starts_with("/home") {
269 mount_paths.push(current_dir.as_str());
270 }
271
272 binfmt_args.push("run".into());
273 binfmt_args.push("--net".into());
274 binfmt_args.push("--forward-host-env".into());
276 for mount_path in mount_paths {
277 binfmt_args.push(format!("--mapdir={mount_path}:{mount_path}").into());
278 }
279 binfmt_args.push(format!("--cwd={current_dir}").into());
280 binfmt_args.push("--quiet".into());
281 binfmt_args.push("--".into());
282 binfmt_args.push(args_os.next().unwrap());
283 args_os.next().unwrap();
284 };
285 let args_vec = args.chain(binfmt_args).chain(args_os).collect::<Vec<_>>();
286
287 match WasmerCmd::try_parse_from(args_vec.iter()) {
288 Ok(args) => args.execute(),
289 Err(e) => {
290 let first_arg_is_subcommand = if let Some(first_arg) = args_vec.get(1) {
291 let mut ret = false;
292 let cmd = WasmerCmd::command();
293
294 for cmd in cmd.get_subcommands() {
295 if cmd.get_name() == first_arg {
296 ret = true;
297 break;
298 }
299 }
300
301 ret
302 } else {
303 false
304 };
305
306 let might_be_wasmer_run = matches!(
307 e.kind(),
308 clap::error::ErrorKind::InvalidSubcommand
309 | clap::error::ErrorKind::UnknownArgument
310 ) && !first_arg_is_subcommand;
311
312 if might_be_wasmer_run && let Ok(run) = Run::try_parse_from(args_vec.iter()) {
313 let output = crate::logging::Output::default();
318 output.initialize_logging();
319 run.execute(output);
320 }
321
322 e.exit();
323 }
324 }
325 }
326}
327
328#[derive(clap::Parser, Debug)]
329#[allow(clippy::large_enum_variant)]
330enum Cmd {
332 Login(Login),
334
335 #[clap(subcommand)]
336 Auth(CmdAuth),
337
338 #[clap(name = "publish")]
340 Publish(PackagePublish),
341
342 Cache(Cache),
344
345 Validate(Validate),
347
348 #[cfg(feature = "compiler")]
350 Compile(Compile),
351
352 #[cfg(any(feature = "static-artifact-create", feature = "wasmer-artifact-create"))]
383 #[clap(name = "create-exe", verbatim_doc_comment)]
384 CreateExe(CreateExe),
385
386 #[cfg(feature = "static-artifact-create")]
416 #[structopt(name = "create-obj", verbatim_doc_comment)]
417 CreateObj(CreateObj),
418
419 #[cfg(feature = "static-artifact-create")]
421 GenCHeader(GenCHeader),
422
423 Config(Config),
426
427 #[clap(name = "self-update")]
429 SelfUpdate(SelfUpdate),
430
431 Inspect(Inspect),
433
434 #[clap(name = "init")]
436 Init(Init),
437
438 #[cfg(feature = "wast")]
440 Wast(Wast),
441
442 #[cfg(target_os = "linux")]
444 Binfmt(Binfmt),
445
446 Whoami(Whoami),
448
449 Add(CmdAdd),
451
452 #[clap(alias = "run-unstable")]
454 Run(Run),
455
456 #[cfg(feature = "journal")]
458 #[clap(subcommand)]
459 Journal(CmdJournal),
460
461 #[clap(subcommand)]
462 Package(crate::commands::Package),
463
464 #[clap(subcommand)]
465 Container(crate::commands::Container),
466
467 Deploy(crate::commands::app::deploy::CmdAppDeploy),
470
471 #[clap(subcommand, alias = "apps")]
473 App(crate::commands::app::CmdApp),
474
475 Ssh(crate::commands::ssh::CmdSsh),
477
478 #[clap(subcommand, alias = "namespaces")]
480 Namespace(crate::commands::namespace::CmdNamespace),
481
482 #[clap(subcommand, alias = "domains")]
484 Domain(crate::commands::domain::CmdDomain),
485
486 #[clap(name = "gen-completions")]
488 GenCompletions(crate::commands::gen_completions::CmdGenCompletions),
489
490 #[clap(name = "gen-man", hide = true)]
492 GenManPage(crate::commands::gen_manpage::CmdGenManPage),
493}
494
495fn is_binfmt_interpreter() -> bool {
496 cfg_if::cfg_if! {
497 if #[cfg(target_os = "linux")] {
498 let binary_path = match std::env::args_os().next() {
500 Some(path) => std::path::PathBuf::from(path),
501 None => return false,
502 };
503 binary_path.file_name().and_then(|f| f.to_str()) == Some(Binfmt::FILENAME)
504 } else {
505 false
506 }
507 }
508}
509
510fn print_version(verbose: bool) -> Result<(), anyhow::Error> {
511 if !verbose {
512 println!("wasmer {}", env!("CARGO_PKG_VERSION"));
513 return Ok(());
514 }
515
516 println!(
517 "wasmer {} ({} {})",
518 env!("CARGO_PKG_VERSION"),
519 env!("WASMER_BUILD_GIT_HASH_SHORT"),
520 env!("WASMER_BUILD_DATE")
521 );
522 println!("binary: {}", env!("CARGO_PKG_NAME"));
523 println!("commit-hash: {}", env!("WASMER_BUILD_GIT_HASH"));
524 println!("commit-date: {}", env!("WASMER_BUILD_DATE"));
525 println!("host: {}", target_lexicon::HOST);
526
527 let mut runtimes = Vec::<&'static str>::new();
528 if cfg!(feature = "singlepass") {
529 runtimes.push("singlepass");
530 }
531 if cfg!(feature = "cranelift") {
532 runtimes.push("cranelift");
533 }
534 if cfg!(feature = "llvm") {
535 runtimes.push("llvm");
536 }
537
538 if cfg!(feature = "wamr") {
539 runtimes.push("wamr");
540 }
541
542 if cfg!(feature = "wasmi") {
543 runtimes.push("wasmi");
544 }
545
546 if cfg!(feature = "v8") {
547 runtimes.push("v8");
548 }
549
550 println!("runtimes: {}", runtimes.join(", "));
551 Ok(())
552}