1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
use crate::{host, LunaticError};

/// Process configurations determine permissions of processes.
///
/// The functions `spawn_config` & `spawn_link_config` can be used to create
/// processes with a specific configuration.
pub struct ProcessConfig(ProcessConfigType);

enum ProcessConfigType {
    /// ID of a configuration held by the host as a resource.
    Config(u64),
    /// Indicates that the configuration should be inherited from the parent
    /// process.
    Inherit,
}

impl Drop for ProcessConfigType {
    fn drop(&mut self) {
        match self {
            ProcessConfigType::Config(id) => unsafe { host::api::process::drop_config(*id) },
            ProcessConfigType::Inherit => (),
        }
    }
}

impl std::fmt::Debug for ProcessConfig {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self.0 {
            ProcessConfigType::Config(_) => f
                .debug_struct("ProcessConfig")
                .field("max_memory", &self.get_max_memory())
                .field("max_fuel", &self.get_max_fuel())
                .field("can_compile_modules", &self.can_compile_modules())
                .field("can_create_configs", &self.can_create_configs())
                .field("can_spawn_processes", &self.can_spawn_processes())
                .finish(),
            ProcessConfigType::Inherit => f.debug_struct("ProcessConfig::Inherit").finish(),
        }
    }
}

impl ProcessConfig {
    /// Create a new process configuration with all permissions denied.
    ///
    /// There is no memory or fuel limit set on the newly created configuration,
    /// they are not inherited from parent.
    pub fn new() -> Result<Self, LunaticError> {
        match unsafe { host::api::process::create_config() } {
            -1 => Err(LunaticError::PermissionDenied),
            id => Ok(Self(ProcessConfigType::Config(id as u64))),
        }
    }

    pub(crate) fn inherit() -> Self {
        Self(ProcessConfigType::Inherit)
    }

    /// Returns the id of the configuration resource or -1 in case it's an
    /// inherited configuration.
    pub fn id(&self) -> i64 {
        match self.0 {
            ProcessConfigType::Config(id) => id as i64,
            ProcessConfigType::Inherit => -1,
        }
    }

    /// Sets the maximum amount of memory in bytes that can be used by a
    /// process.
    ///
    /// If a process tries to allocate more memory with `memory.grow`, the
    /// instruction is going to return -1.
    pub fn set_max_memory(&mut self, max_memory: u64) {
        unsafe { host::api::process::config_set_max_memory(self.id() as u64, max_memory) };
    }

    /// Returns the maximum amount of memory in bytes.
    pub fn get_max_memory(&self) -> u64 {
        unsafe { host::api::process::config_get_max_memory(self.id() as u64) }
    }

    /// Sets the maximum amount of fuel available to the process.
    ///
    /// One unit of fuel is approximately 100k wasm instructions. If a process
    /// runs out of fuel it will trap.
    pub fn set_max_fuel(&mut self, max_fuel: u64) {
        unsafe { host::api::process::config_set_max_fuel(self.id() as u64, max_fuel) };
    }

    /// Returns the maximum amount of fuel.
    pub fn get_max_fuel(&self) -> u64 {
        unsafe { host::api::process::config_get_max_fuel(self.id() as u64) }
    }

    /// Sets the ability of a process to compile WebAssembly modules.
    pub fn set_can_compile_modules(&mut self, can: bool) {
        unsafe { host::api::process::config_set_can_compile_modules(self.id() as u64, can as u32) };
    }

    /// Returns true if processes can compile WebAssembly modules.
    pub fn can_compile_modules(&self) -> bool {
        (unsafe { host::api::process::config_can_compile_modules(self.id() as u64) }) > 0
    }

    /// Sets the ability of a process to create their own sub-configuration.
    ///
    /// This setting can be dangerous. If a process is missing a permission, but
    /// has the possibility to create new configurations, it can spawn
    /// sub-processes using a new config that has the permission enabled.
    pub fn set_can_create_configs(&mut self, can: bool) {
        unsafe { host::api::process::config_set_can_create_configs(self.id() as u64, can as u32) };
    }

    /// Returns true if processes can create their own configurations.
    pub fn can_create_configs(&self) -> bool {
        (unsafe { host::api::process::config_can_create_configs(self.id() as u64) }) > 0
    }

    /// Sets the ability of a process to spawn sub-processes.
    pub fn set_can_spawn_processes(&mut self, can: bool) {
        unsafe { host::api::process::config_set_can_spawn_processes(self.id() as u64, can as u32) };
    }

    /// Returns true if processes can spawn sub-processes.
    pub fn can_spawn_processes(&self) -> bool {
        (unsafe { host::api::process::config_can_spawn_processes(self.id() as u64) }) > 0
    }

    /// Adds environment variable.
    pub fn add_environment_variable(&mut self, key: &str, value: &str) {
        unsafe {
            host::api::wasi::config_add_environment_variable(
                self.id() as u64,
                key.as_ptr(),
                key.len(),
                value.as_ptr(),
                value.len(),
            )
        }
    }

    /// Adds command line argument.
    pub fn add_command_line_argument(&mut self, argument: &str) {
        unsafe {
            host::api::wasi::config_add_command_line_argument(
                self.id() as u64,
                argument.as_ptr(),
                argument.len(),
            )
        }
    }

    #[rustversion::before(1.67)]
    /// Mark a directory as pre-opened.
    ///
    /// This API is only available in Rust 1.66 and below. See:
    /// - https://github.com/rust-lang/rust/issues/107635
    /// - https://github.com/rust-lang/rust/pull/108097
    pub fn preopen_dir(&mut self, dir: &str) {
        unsafe { host::api::wasi::config_preopen_dir(self.id() as u64, dir.as_ptr(), dir.len()) }
    }
}