Skip to content
This repository was archived by the owner on Nov 10, 2022. It is now read-only.

Bring back "load module from fd" #101

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,9 @@ $ RUST_LOG=enarx_wasmldr=info RUST_BACKTRACE=1 cargo run return_1.wasm
]
```

On Unix platforms, the command can also read the workload from the
file descriptor (3):
On Unix platforms, the command can also read the workload from an open file descriptor:
```console
$ RUST_LOG=enarx_wasmldr=info RUST_BACKTRACE=1 cargo run 3< return_1.wasm
$ RUST_LOG=enarx_wasmldr=info RUST_BACKTRACE=1 cargo run -- --module-on-fd=3 3< return_1.wasm
```


Expand Down
39 changes: 32 additions & 7 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,22 @@

#![allow(missing_docs, unused_variables)] // This is a work-in-progress, so...

use anyhow::{bail, Result};
use structopt::{clap::AppSettings, StructOpt};

use anyhow::{bail, Result};
use std::path::PathBuf;
use std::str::FromStr;

#[cfg(unix)]
use std::os::unix::io::RawFd;

// The main StructOpt for running `wasmldr` directly
// The main StructOpt for CLI options
#[derive(StructOpt, Debug)]
#[structopt(setting=AppSettings::TrailingVarArg)]
#[structopt(
setting = AppSettings::DeriveDisplayOrder,
setting = AppSettings::UnifiedHelpMessage,
)]
/// Enarx Keep Configurator and WebAssembly Loader
pub struct RunOptions {
/// Pass an environment variable to the program
#[structopt(
Expand All @@ -25,15 +33,24 @@ pub struct RunOptions {
#[structopt(long, value_name = "FUNCTION")]
invoke: Option<String>,

/// Load WebAssembly module from the given FD (must be >=3)
#[cfg(unix)]
#[structopt(long, value_name = "FD", parse(try_from_str = parse_module_fd))]
pub module_on_fd: Option<RawFd>,

// TODO: --inherit-env
// TODO: --stdin, --stdout, --stderr
/// Path of the WebAssembly module to run
#[structopt(index = 1, required = true, value_name = "MODULE", parse(from_os_str))]
pub module: PathBuf,
#[structopt(
index = 1,
required_unless = "module-on-fd",
value_name = "MODULE",
parse(from_os_str)
)]
pub module: Option<PathBuf>,

// NOTE: this has to come last for TrailingVarArg
/// Arguments to pass to the WebAssembly module
#[structopt(value_name = "ARGS")]
#[structopt(value_name = "ARGS", last = true)]
pub args: Vec<String>,
}

Expand All @@ -44,3 +61,11 @@ fn parse_env_var(s: &str) -> Result<(String, String)> {
}
Ok((parts[0].to_owned(), parts[1].to_owned()))
}

fn parse_module_fd(s: &str) -> Result<RawFd> {
let fd = RawFd::from_str(s)?;
if fd <= 2 {
bail!("FD must be >= 3");
}
Ok(fd)
}
48 changes: 33 additions & 15 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,9 @@
//! ]
//! ```
//!
//! On Unix platforms, the command can also read the workload from the
//! file descriptor (3):
//! On Unix platforms, the command can also read the workload from an open file descriptor:
//! ```console
//! $ RUST_LOG=enarx_wasmldr=info RUST_BACKTRACE=1 cargo run 3< return_1.wasm
//! $ RUST_LOG=enarx_wasmldr=info RUST_BACKTRACE=1 cargo run -- --module-on-fd=3 3< return_1.wasm
//! ```
//!
#![deny(missing_docs)]
Expand All @@ -31,14 +30,35 @@
mod cli;
mod workload;

use anyhow::{Context, Result};
use cli::RunOptions;
use log::{debug, info};
use structopt::StructOpt;

use std::fs::File;
use std::io::Read;
#[cfg(unix)]
use std::os::unix::io::FromRawFd;

fn main() {
// Initialize the logger, taking settings from the default env vars
// SAFETY: If opts.module_on_fd is Some(fd) we'll use File::from_raw_fd(fd),
// which is unsafe if something else is using that fd already. So this function
// is safe as long as it is called before anything else opens a file/socket/etc.
// (parse_module_fd() enforces fd >= 3, so we can ignore stdin/out/err.)
unsafe fn get_module_reader(opts: &RunOptions) -> Result<File> {
#[cfg(unix)]
if let Some(fd) = opts.module_on_fd {
info!("reading module from fd {:?}", fd);
return Ok(File::from_raw_fd(fd));
};
let path = opts.module.as_ref().expect("missing required arg");
info!("reading module from {:?}", path);
File::open(path).with_context(|| format!("failed opening {:?}", path))
}

fn main() -> Result<()> {
// Initialize the logger, taking filtering and style settings from the
// default env vars (RUST_LOG and RUST_LOG_STYLE).
// The log target is the default target (stderr), so no files get opened.
env_logger::Builder::from_default_env().init();

info!("version {} starting up", env!("CARGO_PKG_VERSION"));
Expand All @@ -47,22 +67,20 @@ fn main() {
let opts = cli::RunOptions::from_args();
info!("opts: {:#?}", opts);

info!("reading {:?}", opts.module);
// TODO: don't just panic here...
let mut reader = File::open(&opts.module).expect("Unable to open file");

// SAFETY: This is safe because we haven't opened anything else yet.
let mut reader = unsafe { get_module_reader(&opts) }?;
let mut bytes = Vec::new();
reader
.read_to_end(&mut bytes)
.expect("Failed to load workload");
reader.read_to_end(&mut bytes).context("loading module")?;

// FUTURE: measure opts.envs, opts.args, opts.wasm_features
// FUTURE: fork() the workload off into a separate memory space
// FUTURE: measure opts.envs, opts.args, opts.wasm_features, etc
// FUTURE: fork() the workload off into a separate memory space?

// TODO: configure wasmtime, stdio, etc.
info!("running workload");
// TODO: pass opts.wasm_features
let result = workload::run(bytes, opts.args, opts.envs).expect("Failed to run workload");
info!("got result: {:#?}", result);
// TODO: exit with the resulting code, if the result is a return code
// FUTURE: produce attestation report here

Ok(())
}