/*
* Copyright (C) Andrew Clayton
* Copyright (C) F5, Inc.
*/
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdarg.h>
#include <unistd.h>
#include <time.h>
#include <wasm_export.h>
#include "nxt_wasm.h"
typedef struct nxt_wamr_ctx_s nxt_wamr_ctx_t;
struct nxt_wamr_ctx_s {
uint8_t *mod_buf;
wasm_module_t module;
wasm_module_inst_t module_inst;
wasm_exec_env_t exec_env;
};
static nxt_wamr_ctx_t nxt_wamr_ctx;
static void
nxt_wamr_err_msg(const char *fmt, ...)
{
char dstr[20];
time_t t;
va_list args;
struct tm *tmp;
t = time(NULL);
tmp = localtime(&t);
if (tmp == NULL) {
return;
}
strftime(dstr, sizeof(dstr), "%Y/%m/%d %T", tmp);
fprintf(stderr, "%s [WAMR ERROR] %d ", dstr, getpid());
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
fprintf(stderr, "\n");
}
static uint32_t
nxt_wasm_get_init_mem_size(wasm_exec_env_t exec_env)
{
return NXT_WASM_MEM_SIZE;
}
static void
nxt_wasm_response_end(wasm_exec_env_t exec_env)
{
nxt_wasm_ctx_t *ctx = wasm_runtime_get_user_data(exec_env);
nxt_wasm_do_response_end(ctx);
}
static void
nxt_wasm_send_response(wasm_exec_env_t exec_env, uint32_t offset)
{
nxt_wasm_ctx_t *ctx = wasm_runtime_get_user_data(exec_env);
nxt_wasm_do_send_response(ctx, offset);
}
static void
nxt_wasm_send_headers(wasm_exec_env_t exec_env, uint32_t offset)
{
nxt_wasm_ctx_t *ctx = wasm_runtime_get_user_data(exec_env);
nxt_wasm_do_send_headers(ctx, offset);
}
static void
nxt_wasm_set_resp_status(wasm_exec_env_t exec_env, uint16_t status)
{
nxt_wasm_ctx_t *ctx = wasm_runtime_get_user_data(exec_env);
ctx->status = status;
}
static void
nxt_wamr_execute_hook(const nxt_wasm_ctx_t *ctx, nxt_wasm_fh_t hook)
{
bool ok;
const char *name = ctx->fh[hook].func_name;
nxt_wamr_ctx_t *rt_ctx = &nxt_wamr_ctx;
const nxt_wasm_func_t *func = ctx->fh[hook].func;
if (name == NULL) {
return;
}
ok = wasm_runtime_call_wasm_a(rt_ctx->exec_env, (nxt_wasm_func_t)func,
0, NULL, 0, NULL);
if (!ok) {
nxt_wamr_err_msg("failed to call hook function [%s]. (%s)", name,
wasm_runtime_get_exception(rt_ctx->module_inst));
}
}
static int
nxt_wamr_execute_request(const nxt_wasm_ctx_t *ctx)
{
bool ok;
uint32_t i = 0;
wasm_val_t args[1] = { };
wasm_val_t results[1] = { };
nxt_wamr_ctx_t *rt_ctx = &nxt_wamr_ctx;
const nxt_wasm_func_t *func = ctx->fh[NXT_WASM_FH_REQUEST].func;
args[i].kind = WASM_I32;
args[i++].of.i32 = ctx->baddr_off;
ok = wasm_runtime_call_wasm_a(rt_ctx->exec_env, (nxt_wasm_func_t)func,
1, results, i, args);
if (!ok) {
nxt_wamr_err_msg("Failed to call function [->wasm_request_handler]. "
"(%s)",
wasm_runtime_get_exception(rt_ctx->module_inst));
return -1;
}
return results[0].of.i32;
}
static bool
nxt_wamr_set_function_imports(void)
{
int nr_funcs;
bool ok;
static NativeSymbol import_functions[] = {
EXPORT_WASM_API_WITH_SIG(nxt_wasm_get_init_mem_size, "()i"),
EXPORT_WASM_API_WITH_SIG(nxt_wasm_response_end, "()"),
EXPORT_WASM_API_WITH_SIG(nxt_wasm_send_response, "(i)"),
EXPORT_WASM_API_WITH_SIG(nxt_wasm_send_headers, "(i)"),
EXPORT_WASM_API_WITH_SIG(nxt_wasm_set_resp_status, "(i)"),
};
nr_funcs = sizeof(import_functions) / sizeof(NativeSymbol);
ok = wasm_runtime_register_natives("env", import_functions, nr_funcs);
if (!ok) {
return false;
}
return true;
}
static int
nxt_wamr_get_function_exports(nxt_wasm_ctx_t *ctx)
{
int i;
nxt_wamr_ctx_t *rt_ctx = &nxt_wamr_ctx;
for (i = 0; i < NXT_WASM_FH_NR; i++) {
if (ctx->fh[i].func_name == NULL) {
continue;
}
ctx->fh[i].func = wasm_runtime_lookup_function(rt_ctx->module_inst,
ctx->fh[i].func_name,
NULL);
if (ctx->fh[i].func == NULL) {
return -1;
}
}
return 0;
}
static void
nxt_wamr_wasi_init(const nxt_wasm_ctx_t *ctx)
{
nxt_wamr_ctx_t *rt_ctx = &nxt_wamr_ctx;
wasm_runtime_set_wasi_args(rt_ctx->module,
(const char **)ctx->dirs, ctx->nr_dirs,
NULL, 0, NULL, 0, NULL, 0);
}
static int
nxt_wamr_init_memory(nxt_wasm_ctx_t *ctx)
{
bool ok;
uint32_t i = 0;
wasm_val_t args[1] = { };
wasm_val_t results[1] = { };
nxt_wamr_ctx_t *rt_ctx = &nxt_wamr_ctx;
const nxt_wasm_func_t *func = ctx->fh[NXT_WASM_FH_MALLOC].func;
args[i].kind = WASM_I32;
args[i++].of.i32 = NXT_WASM_MEM_SIZE + NXT_WASM_PAGE_SIZE;
ok = wasm_runtime_call_wasm_a(rt_ctx->exec_env, (nxt_wasm_func_t)func,
1, results, i, args);
if (!ok) {
return -1;
}
ctx->baddr_off = results[0].of.i32;
ctx->baddr = wasm_runtime_addr_app_to_native(rt_ctx->module_inst,
ctx->baddr_off);
return 0;
}
static int
nxt_wamr_init(nxt_wasm_ctx_t *ctx)
{
int err;
bool ok;
char error_buf[128];
FILE *fp;
size_t file_size;
uint32_t stack_size = 16384, heap_size = 16384;
nxt_wamr_ctx_t *rt_ctx = &nxt_wamr_ctx;
ok = wasm_runtime_init();
if (!ok) {
nxt_wamr_err_msg("wasm_runtime_init() failed");
return -1;
}
ok = nxt_wamr_set_function_imports();
if (!ok) {
nxt_wamr_err_msg("nxt_wamr_set_function_imports() failed");
return -1;
}
fp = fopen(ctx->module_path, "r");
if (!fp) {
nxt_wamr_err_msg("fopen(\"%s\") failed", ctx->module_path);
return -1;
}
fseek(fp, 0L, SEEK_END);
file_size = ftell(fp);
rt_ctx->mod_buf = malloc(file_size);
fseek(fp, 0L, SEEK_SET);
if (fread(rt_ctx->mod_buf, file_size, 1, fp) != 1) {
nxt_wamr_err_msg("fread(\"%s\") failed", ctx->module_path);
return -1;
}
fclose(fp);
rt_ctx->module = wasm_runtime_load(rt_ctx->mod_buf, file_size, error_buf,
sizeof(error_buf));
if (rt_ctx->module == NULL) {
nxt_wamr_err_msg("wasm_runtime_load() failed");
return -1;
}
nxt_wamr_wasi_init(ctx);
rt_ctx->module_inst = wasm_runtime_instantiate(rt_ctx->module, stack_size,
heap_size, error_buf,
sizeof(error_buf));
if (rt_ctx->module_inst == NULL) {
nxt_wamr_err_msg("wasm_runtime_instantiate() failed");
return -1;
}
rt_ctx->exec_env = wasm_runtime_create_exec_env(rt_ctx->module_inst,
stack_size);
if (rt_ctx->exec_env == NULL) {
nxt_wamr_err_msg("wasm_runtime_create_exec_env() failed");
return -1;
}
wasm_runtime_set_user_data(rt_ctx->exec_env, ctx);
err = nxt_wamr_get_function_exports(ctx);
if (err) {
nxt_wamr_err_msg("nxt_wamr_get_function_exports() failed");
return -1;
}
err = nxt_wamr_init_memory(ctx);
if (err) {
nxt_wamr_err_msg("nxt_wamr_init_memory() failed");
return -1;
}
return 0;
}
static void
nxt_wamr_destroy(const nxt_wasm_ctx_t *ctx)
{
uint32_t i = 0;
wasm_val_t args[1] = { };
nxt_wamr_ctx_t *rt_ctx = &nxt_wamr_ctx;
const nxt_wasm_func_t *func = ctx->fh[NXT_WASM_FH_FREE].func;
args[i].kind = WASM_I32;
args[i++].of.i32 = ctx->baddr_off;
wasm_runtime_call_wasm_a(rt_ctx->exec_env, (nxt_wasm_func_t)func,
0, NULL, i, args);
wasm_runtime_destroy_exec_env(rt_ctx->exec_env);
wasm_runtime_deinstantiate(rt_ctx->module_inst);
wasm_runtime_unload(rt_ctx->module);
free(rt_ctx->mod_buf);
wasm_runtime_destroy();
}
const nxt_wasm_operations_t nxt_wasm_ops = {
.init = nxt_wamr_init,
.destroy = nxt_wamr_destroy,
.exec_request = nxt_wamr_execute_request,
.exec_hook = nxt_wamr_execute_hook,
};