0
# WASI Integration
1
2
Complete WebAssembly System Interface implementation providing filesystem access, environment variables, command-line arguments, I/O redirection, and comprehensive permission controls for secure WebAssembly execution in system environments.
3
4
## Capabilities
5
6
### WASI Configuration
7
8
Configuration system for WebAssembly System Interface providing fine-grained control over system resource access, I/O redirection, filesystem permissions, and process environment setup.
9
10
```python { .api }
11
class WasiConfig:
12
def __init__(self):
13
"""Create a new WASI configuration with default settings"""
14
15
# Command line arguments
16
def argv(self, argv: List[str]) -> None:
17
"""
18
Set command line arguments for the WebAssembly program.
19
20
Parameters:
21
- argv: List of command line arguments (first element is program name)
22
"""
23
24
def inherit_argv(self) -> None:
25
"""Inherit command line arguments from the current Python process"""
26
27
# Environment variables
28
def env(self, env: List[Tuple[str, str]]) -> None:
29
"""
30
Set environment variables for the WebAssembly program.
31
32
Parameters:
33
- env: List of (name, value) tuples for environment variables
34
"""
35
36
def inherit_env(self) -> None:
37
"""Inherit environment variables from the current Python process"""
38
39
# Standard I/O redirection
40
def stdin_file(self, path: str) -> None:
41
"""
42
Redirect stdin from a file.
43
44
Parameters:
45
- path: Path to file for stdin input
46
"""
47
48
def stdout_file(self, path: str) -> None:
49
"""
50
Redirect stdout to a file.
51
52
Parameters:
53
- path: Path to file for stdout output
54
"""
55
56
def stderr_file(self, path: str) -> None:
57
"""
58
Redirect stderr to a file.
59
60
Parameters:
61
- path: Path to file for stderr output
62
"""
63
64
def inherit_stdin(self) -> None:
65
"""Inherit stdin from the current Python process"""
66
67
def inherit_stdout(self) -> None:
68
"""Inherit stdout from the current Python process"""
69
70
def inherit_stderr(self) -> None:
71
"""Inherit stderr from the current Python process"""
72
73
# Filesystem access
74
def preopen_dir(self, path: str, guest_path: str, perms: DirPerms) -> None:
75
"""
76
Preopen a directory for WebAssembly filesystem access.
77
78
Parameters:
79
- path: Host filesystem path to directory
80
- guest_path: Path as seen by WebAssembly program
81
- perms: Directory access permissions
82
"""
83
```
84
85
### Directory Permissions
86
87
Permission enumeration controlling directory access rights for preopened directories, providing read-only, write-only, and read-write access control.
88
89
```python { .api }
90
class DirPerms:
91
READ_ONLY: 'DirPerms' # Read-only directory access
92
WRITE_ONLY: 'DirPerms' # Write-only directory access
93
READ_WRITE: 'DirPerms' # Read and write directory access
94
```
95
96
### File Permissions
97
98
Permission enumeration controlling file access rights within preopened directories, providing granular control over file operations and security boundaries.
99
100
```python { .api }
101
class FilePerms:
102
READ_ONLY: 'FilePerms' # Read-only file access
103
WRITE_ONLY: 'FilePerms' # Write-only file access
104
READ_WRITE: 'FilePerms' # Read and write file access
105
```
106
107
## Usage Examples
108
109
### Basic WASI Setup
110
111
```python
112
import wasmtime
113
import sys
114
import os
115
116
# Create WASI configuration
117
wasi_config = wasmtime.WasiConfig()
118
119
# Configure command line arguments
120
wasi_config.argv(["my_program", "arg1", "arg2", "--flag"])
121
122
# Configure environment variables
123
wasi_config.env([
124
("HOME", "/home/wasm"),
125
("PATH", "/usr/bin:/bin"),
126
("CUSTOM_VAR", "custom_value")
127
])
128
129
# Inherit standard I/O
130
wasi_config.inherit_stdin()
131
wasi_config.inherit_stdout()
132
wasi_config.inherit_stderr()
133
134
# Create engine and linker
135
engine = wasmtime.Engine()
136
linker = wasmtime.Linker(engine)
137
store = wasmtime.Store(engine)
138
139
# Define WASI imports
140
linker.define_wasi(store, wasi_config)
141
142
# Load and instantiate WASI-enabled WebAssembly module
143
module = wasmtime.Module.from_file(engine, "wasi_program.wasm")
144
instance = linker.instantiate(store, module)
145
146
# Call main function if it exists
147
try:
148
start_func = instance.get_export(store, "_start")
149
if start_func:
150
start_func(store)
151
except wasmtime.ExitTrap as exit_trap:
152
print(f"Program exited with code: {exit_trap.code}")
153
```
154
155
### Filesystem Access with Permissions
156
157
```python
158
import wasmtime
159
import tempfile
160
import os
161
162
# Create temporary directories for demo
163
with tempfile.TemporaryDirectory() as temp_dir:
164
# Create some test directories
165
input_dir = os.path.join(temp_dir, "input")
166
output_dir = os.path.join(temp_dir, "output")
167
config_dir = os.path.join(temp_dir, "config")
168
169
os.makedirs(input_dir)
170
os.makedirs(output_dir)
171
os.makedirs(config_dir)
172
173
# Create test files
174
with open(os.path.join(input_dir, "data.txt"), "w") as f:
175
f.write("Input data for processing")
176
177
with open(os.path.join(config_dir, "settings.json"), "w") as f:
178
f.write('{"debug": true, "max_items": 100}')
179
180
# Configure WASI with different directory permissions
181
wasi_config = wasmtime.WasiConfig()
182
183
# Read-only input directory
184
wasi_config.preopen_dir(input_dir, "/input", wasmtime.DirPerms.READ_ONLY)
185
186
# Read-write output directory
187
wasi_config.preopen_dir(output_dir, "/output", wasmtime.DirPerms.READ_WRITE)
188
189
# Read-only config directory
190
wasi_config.preopen_dir(config_dir, "/config", wasmtime.DirPerms.READ_ONLY)
191
192
# Setup WebAssembly execution
193
engine = wasmtime.Engine()
194
store = wasmtime.Store(engine)
195
linker = wasmtime.Linker(engine)
196
linker.define_wasi(store, wasi_config)
197
198
# The WebAssembly program can now:
199
# - Read files from /input and /config
200
# - Write files to /output
201
# - Cannot write to /input or /config
202
203
print("WASI filesystem access configured:")
204
print(f" /input -> {input_dir} (read-only)")
205
print(f" /output -> {output_dir} (read-write)")
206
print(f" /config -> {config_dir} (read-only)")
207
```
208
209
### I/O Redirection
210
211
```python
212
import wasmtime
213
import tempfile
214
import os
215
216
# Create temporary files for I/O redirection
217
with tempfile.NamedTemporaryFile(mode='w', delete=False) as input_file:
218
input_file.write("Hello from input file!\n")
219
input_file.write("Second line of input\n")
220
input_file_path = input_file.name
221
222
with tempfile.NamedTemporaryFile(mode='w', delete=False) as output_file:
223
output_file_path = output_file.name
224
225
with tempfile.NamedTemporaryFile(mode='w', delete=False) as error_file:
226
error_file_path = error_file.name
227
228
try:
229
# Configure WASI with I/O redirection
230
wasi_config = wasmtime.WasiConfig()
231
232
# Redirect stdin from file
233
wasi_config.stdin_file(input_file_path)
234
235
# Redirect stdout to file
236
wasi_config.stdout_file(output_file_path)
237
238
# Redirect stderr to file
239
wasi_config.stderr_file(error_file_path)
240
241
# Set up command line arguments
242
wasi_config.argv(["io_test", "--echo"])
243
244
# Set up environment
245
wasi_config.env([("TEST_MODE", "redirect")])
246
247
# Create WebAssembly execution environment
248
engine = wasmtime.Engine()
249
store = wasmtime.Store(engine)
250
linker = wasmtime.Linker(engine)
251
linker.define_wasi(store, wasi_config)
252
253
# Load and run WebAssembly program
254
# (This would be a WASI program that reads stdin and writes to stdout/stderr)
255
# module = wasmtime.Module.from_file(engine, "io_program.wasm")
256
# instance = linker.instantiate(store, module)
257
# start_func = instance.get_export(store, "_start")
258
# start_func(store)
259
260
print("I/O redirection configured:")
261
print(f" stdin <- {input_file_path}")
262
print(f" stdout -> {output_file_path}")
263
print(f" stderr -> {error_file_path}")
264
265
# After execution, read the output files
266
# with open(output_file_path, 'r') as f:
267
# print(f"Program output: {f.read()}")
268
269
finally:
270
# Clean up temporary files
271
for path in [input_file_path, output_file_path, error_file_path]:
272
try:
273
os.unlink(path)
274
except OSError:
275
pass
276
```
277
278
### Environment Variable Inheritance and Customization
279
280
```python
281
import wasmtime
282
import os
283
284
# Get current environment
285
current_env = dict(os.environ)
286
287
# Create custom environment based on current environment
288
custom_env = []
289
for key, value in current_env.items():
290
# Filter out sensitive variables
291
if not key.startswith(('SECRET_', 'TOKEN_', 'PASSWORD_')):
292
custom_env.append((key, value))
293
294
# Add custom variables
295
custom_env.extend([
296
("WASM_MODE", "production"),
297
("MAX_MEMORY", "1073741824"), # 1GB
298
("DEBUG_LEVEL", "info"),
299
("ALLOWED_HOSTS", "api.example.com,cdn.example.com")
300
])
301
302
# Configure WASI
303
wasi_config = wasmtime.WasiConfig()
304
wasi_config.env(custom_env)
305
306
# Alternative: inherit everything and let WebAssembly filter
307
# wasi_config_simple = wasmtime.WasiConfig()
308
# wasi_config_simple.inherit_env()
309
310
print(f"Configured {len(custom_env)} environment variables for WebAssembly")
311
print("Custom variables:")
312
for key, value in custom_env:
313
if key.startswith(('WASM_', 'MAX_', 'DEBUG_', 'ALLOWED_')):
314
print(f" {key}={value}")
315
```
316
317
### Complete WASI Application Setup
318
319
```python
320
import wasmtime
321
import os
322
import sys
323
import tempfile
324
import shutil
325
326
def setup_wasi_application(wasm_path: str, work_dir: str = None):
327
"""
328
Complete setup for a WASI WebAssembly application.
329
330
Parameters:
331
- wasm_path: Path to the WebAssembly WASI binary
332
- work_dir: Working directory for the application (optional)
333
"""
334
335
# Create working directory if not provided
336
if work_dir is None:
337
work_dir = tempfile.mkdtemp(prefix="wasi_app_")
338
cleanup_work_dir = True
339
else:
340
cleanup_work_dir = False
341
342
try:
343
# Create application directory structure
344
app_dirs = {
345
"data": os.path.join(work_dir, "data"),
346
"config": os.path.join(work_dir, "config"),
347
"logs": os.path.join(work_dir, "logs"),
348
"temp": os.path.join(work_dir, "temp")
349
}
350
351
for dir_path in app_dirs.values():
352
os.makedirs(dir_path, exist_ok=True)
353
354
# Create sample configuration
355
config_content = '''
356
{
357
"app_name": "WASI Application",
358
"version": "1.0.0",
359
"max_memory": "512MB",
360
"log_level": "info"
361
}
362
'''
363
364
with open(os.path.join(app_dirs["config"], "app.json"), "w") as f:
365
f.write(config_content)
366
367
# Configure WASI
368
wasi_config = wasmtime.WasiConfig()
369
370
# Command line arguments
371
wasi_config.argv([
372
"wasi_app",
373
"--config", "/config/app.json",
374
"--data-dir", "/data",
375
"--log-dir", "/logs"
376
])
377
378
# Environment variables
379
wasi_config.env([
380
("HOME", "/app"),
381
("TMPDIR", "/temp"),
382
("LOG_LEVEL", "info"),
383
("APP_VERSION", "1.0.0")
384
])
385
386
# Filesystem access
387
wasi_config.preopen_dir(app_dirs["data"], "/data", wasmtime.DirPerms.READ_WRITE)
388
wasi_config.preopen_dir(app_dirs["config"], "/config", wasmtime.DirPerms.READ_ONLY)
389
wasi_config.preopen_dir(app_dirs["logs"], "/logs", wasmtime.DirPerms.READ_WRITE)
390
wasi_config.preopen_dir(app_dirs["temp"], "/temp", wasmtime.DirPerms.READ_WRITE)
391
392
# I/O configuration
393
wasi_config.inherit_stdin()
394
wasi_config.inherit_stdout()
395
wasi_config.inherit_stderr()
396
397
# Set up WebAssembly runtime
398
engine = wasmtime.Engine()
399
store = wasmtime.Store(engine)
400
linker = wasmtime.Linker(engine)
401
402
# Define WASI imports
403
linker.define_wasi(store, wasi_config)
404
405
# Load the WebAssembly module
406
module = wasmtime.Module.from_file(engine, wasm_path)
407
408
# Instantiate the module
409
instance = linker.instantiate(store, module)
410
411
print(f"WASI application setup complete:")
412
print(f" Work directory: {work_dir}")
413
print(f" WebAssembly module: {wasm_path}")
414
print(f" Directory mappings:")
415
for guest_path, host_path in [("/data", app_dirs["data"]),
416
("/config", app_dirs["config"]),
417
("/logs", app_dirs["logs"]),
418
("/temp", app_dirs["temp"])]:
419
print(f" {guest_path} -> {host_path}")
420
421
# Run the application
422
try:
423
start_func = instance.get_export(store, "_start")
424
if start_func:
425
print("Running WASI application...")
426
start_func(store)
427
print("Application completed successfully")
428
else:
429
print("No _start function found in WebAssembly module")
430
431
except wasmtime.ExitTrap as exit_trap:
432
exit_code = exit_trap.code
433
if exit_code == 0:
434
print("Application completed successfully")
435
else:
436
print(f"Application exited with code: {exit_code}")
437
438
except wasmtime.Trap as trap:
439
print(f"Application trapped: {trap.message}")
440
if trap.frames:
441
for frame in trap.frames:
442
print(f" at {frame.func_name or 'unknown'} in {frame.module_name or 'unknown'}")
443
444
return instance
445
446
finally:
447
if cleanup_work_dir and os.path.exists(work_dir):
448
shutil.rmtree(work_dir)
449
450
# Example usage:
451
# setup_wasi_application("my_wasi_app.wasm", "/path/to/work/directory")
452
```
453
454
### WASI Error Handling
455
456
```python
457
import wasmtime
458
459
def safe_wasi_execution(wasm_path: str):
460
"""Demonstrate proper error handling for WASI applications"""
461
462
try:
463
# Configure WASI
464
wasi_config = wasmtime.WasiConfig()
465
wasi_config.inherit_argv()
466
wasi_config.inherit_env()
467
wasi_config.inherit_stdin()
468
wasi_config.inherit_stdout()
469
wasi_config.inherit_stderr()
470
471
# Set up runtime
472
engine = wasmtime.Engine()
473
store = wasmtime.Store(engine)
474
linker = wasmtime.Linker(engine)
475
linker.define_wasi(store, wasi_config)
476
477
# Load module
478
module = wasmtime.Module.from_file(engine, wasm_path)
479
instance = linker.instantiate(store, module)
480
481
# Execute
482
start_func = instance.get_export(store, "_start")
483
if start_func:
484
start_func(store)
485
486
except FileNotFoundError:
487
print(f"Error: WebAssembly file '{wasm_path}' not found")
488
489
except wasmtime.WasmtimeError as e:
490
print(f"WebAssembly error: {e}")
491
492
except wasmtime.ExitTrap as exit_trap:
493
exit_code = exit_trap.code
494
if exit_code != 0:
495
print(f"Application exited with error code: {exit_code}")
496
return exit_code
497
498
except wasmtime.Trap as trap:
499
print(f"Runtime trap: {trap.message}")
500
print("Stack trace:")
501
for frame in trap.frames:
502
func_name = frame.func_name or f"func[{frame.func_index}]"
503
module_name = frame.module_name or "unknown"
504
print(f" {func_name} in {module_name} at offset {frame.module_offset}")
505
return 1
506
507
except Exception as e:
508
print(f"Unexpected error: {e}")
509
return 1
510
511
return 0
512
```