wasmer_wasix/os/command/
mod.rs

1pub mod builtins;
2
3use std::{collections::HashMap, sync::Arc};
4
5use wasmer::FunctionEnvMut;
6use wasmer_wasix_types::wasi::Errno;
7
8use crate::{
9    Runtime, SpawnError, WasiEnv, runtime::task_manager::InlineWaker, syscalls::stderr_write,
10};
11
12use super::task::{OwnedTaskStatus, TaskJoinHandle, TaskStatus};
13
14/// A command available to an OS environment.
15pub trait VirtualCommand
16where
17    Self: std::fmt::Debug,
18{
19    /// Returns the canonical name of the command.
20    fn name(&self) -> &str;
21
22    /// Retrieve the command as a [`std::any::Any`] reference.
23    fn as_any(&self) -> &dyn std::any::Any;
24
25    /// Executes the command.
26    fn exec(
27        &self,
28        parent_ctx: &FunctionEnvMut<'_, WasiEnv>,
29        path: &str,
30        config: &mut Option<WasiEnv>,
31    ) -> Result<TaskJoinHandle, SpawnError>;
32}
33
34#[derive(Debug, Clone)]
35pub struct Commands {
36    commands: HashMap<String, Arc<dyn VirtualCommand + Send + Sync + 'static>>,
37}
38
39impl Commands {
40    fn new() -> Self {
41        Self {
42            commands: HashMap::new(),
43        }
44    }
45
46    // TODO: this method should be somewhere on the runtime, not here.
47    pub fn new_with_builtins(runtime: Arc<dyn Runtime + Send + Sync + 'static>) -> Self {
48        let mut cmd = Self::new();
49        let cmd_wasmer = builtins::cmd_wasmer::CmdWasmer::new(runtime.clone());
50        cmd.register_command(cmd_wasmer);
51
52        cmd
53    }
54
55    /// Register a command.
56    ///
57    /// The command will be available with it's canonical name ([`VirtualCommand::name()`]) at /bin/NAME.
58    pub fn register_command<C: VirtualCommand + Send + Sync + 'static>(&mut self, cmd: C) {
59        let path = format!("/bin/{}", cmd.name());
60        self.register_command_with_path(cmd, path);
61    }
62
63    /// Register a command at a custom path.
64    pub fn register_command_with_path<C: VirtualCommand + Send + Sync + 'static>(
65        &mut self,
66        cmd: C,
67        path: String,
68    ) {
69        self.commands.insert(path, Arc::new(cmd));
70    }
71
72    /// Determine if a command exists at the given path.
73    pub fn exists(&self, path: &str) -> bool {
74        let name = path.to_string();
75        self.commands.contains_key(&name)
76    }
77
78    /// Get a command by its path.
79    pub fn get(&self, path: &str) -> Option<&Arc<dyn VirtualCommand + Send + Sync + 'static>> {
80        self.commands.get(path)
81    }
82
83    /// Execute a command.
84    pub fn exec(
85        &self,
86        parent_ctx: &FunctionEnvMut<'_, WasiEnv>,
87        path: &str,
88        builder: &mut Option<WasiEnv>,
89    ) -> Result<TaskJoinHandle, SpawnError> {
90        let path = path.to_string();
91        if let Some(cmd) = self.commands.get(&path) {
92            cmd.exec(parent_ctx, path.as_str(), builder)
93        } else {
94            unsafe {
95                InlineWaker::block_on(stderr_write(
96                    parent_ctx,
97                    format!("wasm command unknown - {path}\r\n").as_bytes(),
98                ))
99            }
100            .ok();
101
102            let res = OwnedTaskStatus::new(TaskStatus::Finished(Ok(Errno::Noent.into())));
103            Ok(res.handle())
104        }
105    }
106}