// Generic Program Wrapper Template
// This template is used to wrap a generic program in a WebAssembly module
use std::sync::{Arc, Mutex};
use std::process::{Command, Stdio, Child};
use std::io::{Read, Write};
use std::thread;
// Configuration
const EXECUTABLE: &str = "{{executable}}";
// Global state
static mut PROGRAM_INSTANCE: Option<Arc<Mutex<ProgramInstance>>> = None;
struct ProgramInstance {
running: bool,
process: Option<Child>,
exit_code: Option<i32>,
output: Vec<u8>,
}
// Initialize the module
#[no_mangle]
pub extern "C" fn _initialize() {
unsafe {
PROGRAM_INSTANCE = Some(Arc::new(Mutex::new(ProgramInstance {
running: false,
process: None,
exit_code: None,
output: Vec::new(),
})));
}
}
// Run the program with arguments and environment variables
#[no_mangle]
pub extern "C" fn run(
args_ptr: *const u8,
args_len: usize,
env_ptr: *const u8,
env_len: usize,
working_dir_ptr: *const u8,
working_dir_len: usize,
) -> bool {
// Convert args_ptr to a string
let args_bytes = unsafe {
if args_ptr.is_null() {
return false;
}
std::slice::from_raw_parts(args_ptr, args_len)
};
// Parse arguments from JSON
let args: Vec<String> = match serde_json::from_str(std::str::from_utf8(args_bytes).unwrap_or("[]")) {
Ok(a) => a,
Err(_) => Vec::new(),
};
// Convert env_ptr to a string
let env_bytes = unsafe {
if env_ptr.is_null() {
&[] as &[u8]
} else {
std::slice::from_raw_parts(env_ptr, env_len)
}
};
// Parse environment variables from JSON
let env_vars: std::collections::HashMap<String, String> =
match serde_json::from_str(std::str::from_utf8(env_bytes).unwrap_or("{}")) {
Ok(e) => e,
Err(_) => std::collections::HashMap::new(),
};
// Convert working_dir_ptr to a string
let working_dir = if working_dir_ptr.is_null() || working_dir_len == 0 {
None
} else {
let dir_bytes = unsafe {
std::slice::from_raw_parts(working_dir_ptr, working_dir_len)
};
match std::str::from_utf8(dir_bytes) {
Ok(s) if !s.is_empty() => Some(s),
_ => None,
}
};
// Get program instance
let instance = unsafe { PROGRAM_INSTANCE.as_ref().unwrap().clone() };
let mut instance = instance.lock().unwrap();
if instance.running {
// Program is already running
return false;
}
// Build command to start the program
let mut command = Command::new(EXECUTABLE);
// Add arguments
for arg in args {
command.arg(arg);
}
// Add environment variables
for (key, value) in env_vars {
command.env(key, value);
}
// Set working directory
if let Some(dir) = working_dir {
command.current_dir(dir);
}
// Configure stdio
command.stdout(Stdio::piped());
command.stderr(Stdio::piped());
// Start the program
match command.spawn() {
Ok(mut child) => {
instance.running = true;
// Capture output
let stdout = child.stdout.take().expect("Failed to capture stdout");
let stderr = child.stderr.take().expect("Failed to capture stderr");
let instance_clone = unsafe { PROGRAM_INSTANCE.as_ref().unwrap().clone() };
thread::spawn(move || {
let mut buffer = Vec::new();
let mut stdout_reader = std::io::BufReader::new(stdout);
let mut stderr_reader = std::io::BufReader::new(stderr);
// Read stdout
let _ = stdout_reader.read_to_end(&mut buffer);
// Read stderr and append to buffer
let mut stderr_buffer = Vec::new();
let _ = stderr_reader.read_to_end(&mut stderr_buffer);
buffer.extend_from_slice(&stderr_buffer);
let mut instance = instance_clone.lock().unwrap();
instance.output = buffer;
});
instance.process = Some(child);
true
},
Err(_) => false,
}
}
// Wait for the program to exit
#[no_mangle]
pub extern "C" fn wait() -> i32 {
let instance = unsafe { PROGRAM_INSTANCE.as_ref().unwrap().clone() };
let mut instance = instance.lock().unwrap();
if !instance.running || instance.process.is_none() {
return -1;
}
if let Some(exit_code) = instance.exit_code {
instance.running = false;
return exit_code;
}
// Wait for the program to exit
if let Some(child) = &mut instance.process {
match child.wait() {
Ok(status) => {
instance.running = false;
let code = status.code().unwrap_or(-1);
instance.exit_code = Some(code);
code
},
Err(_) => -1,
}
} else {
-1
}
}
// Get output from the program
#[no_mangle]
pub extern "C" fn get_output() -> *const u8 {
let instance = unsafe { PROGRAM_INSTANCE.as_ref().unwrap().clone() };
let instance = instance.lock().unwrap();
if instance.output.is_empty() {
return std::ptr::null();
}
let ptr = instance.output.as_ptr();
std::mem::forget(instance.output.clone());
ptr
}
// Get output length
#[no_mangle]
pub extern "C" fn get_output_len() -> usize {
let instance = unsafe { PROGRAM_INSTANCE.as_ref().unwrap().clone() };
let instance = instance.lock().unwrap();
instance.output.len()
}
// Stop the program
#[no_mangle]
pub extern "C" fn stop() {
let instance = unsafe { PROGRAM_INSTANCE.as_ref().unwrap().clone() };
let mut instance = instance.lock().unwrap();
if !instance.running {
return;
}
// Stop the program
if let Some(mut child) = instance.process.take() {
let _ = child.kill();
let _ = child.wait();
}
instance.running = false;
}
// Check if the program is running
#[no_mangle]
pub extern "C" fn is_running() -> bool {
let instance = unsafe { PROGRAM_INSTANCE.as_ref().unwrap().clone() };
let instance = instance.lock().unwrap();
instance.running
}
// Get exit code
#[no_mangle]
pub extern "C" fn get_exit_code() -> i32 {
let instance = unsafe { PROGRAM_INSTANCE.as_ref().unwrap().clone() };
let instance = instance.lock().unwrap();
instance.exit_code.unwrap_or(-1)
}
// Main function
fn main() {
// Initialize
_initialize();
// The actual logic will be handled by the host through function calls
println!("Program wrapper initialized");
}