Access operating system resources from WebAssembly

• 3 minutes to read

The WebAssembly VM provides a sandbox to ensure application safety. However, this sandbox is also a very limited “computer” that has no concept of file system, network, or even a clock or timer. That is very limiting for the Rust programs running inside WebAssembly.

If WASM+WASI existed in 2008, we wouldn't have needed to created Docker. That's how important it is. Webassembly on the server is the future of computing. A standardized system interface was the missing link. Let's hope WASI is up to the task! – Solomon Hykes, Co-founder of Docker

The WebAssembly Systems Interface (WASI) is a standard extension for WebAssembly bytecode applications to make operating system calls. It is fully supported in the SSVM. WASI defines a set of function names to perform operating system tasks, such as opening a file. When the WebAssembly VM encounters those function names at runtime, it automatically calls the corresponding operating system standard library function to perform the task and return the result.

In order for WASI to work, we need a compiler toolchain to compile Rust (or other languages) standard library functions, such as opening a file, into bytecode that makes the corresponding WASI calls. The ssvmup tool uses the wasm32-wasi compiler backend for Rust. It supports WASI out of the box.

The example source code for this tutorial is here.

In the getting started with Rust functions in Node.js, we showed you how to compile high performance Rust functions into WebAssembly, and call them from Node.js applications.

Prerequisites

Check out the complete setup instructions for Rust functions in Node.js.

Get random number

The WebAssembly VM is a pure software construct. It does not have a hardware entropy source for random numbers. That's why WASI defines a function for WebAssembly programs to call its host operating system to get a random seed. As a Rust developer, all you need is to use the popular (de facto standard) rand and getrandom crates. Those crates are written in a way that instructs the wasm32-wasi compiler backend to generate the correct WASI calls in the WebAssembly bytecode. The Cargo.toml dependencies are as follows.

[dependencies]
rand = "0.7.3"
getrandom = "0.1.14"
wasm-bindgen = "=0.2.61"

The Rust code to get random number from WebAssembly is this.

use wasm_bindgen::prelude::*;
use rand::prelude::*;

#[wasm_bindgen]
pub fn get_random_i32() -> i32 {
  let x: i32 = random();
  return x;
}

#[wasm_bindgen]
pub fn get_random_bytes() -> Vec<u8> {
  let mut vec: Vec<u8> = vec![0; 128];
  getrandom::getrandom(&mut vec).unwrap();
  return vec;
}

The Javascript code to call the Rust functions from Node.js is as follows.

const { get_random_i32, get_random_bytes } = require('../pkg/wasi_example_lib.js');

console.log( "My random number is: ", get_random_i32() );
console.log( "My random bytes are");
console.hex( get_random_bytes() );

Now, let's run this example in SSVM in Node.js.

$ ssvmup build
$ node node/app.js
... ...

More to come later for WASI functions to access the file system, console / stdout, time / clock, and network requests.

Printing and debugging from Rust

The Rust println! marco just works in WASI. The statements print to the STDOUT of the process that runs the SSVM. In Node.js apps, it is the STDOUT on the Node.js server.

RustWebAssemblyhow-torust-function-in-nodejs
Fast, safe, portable and serverless Rust functions as services