static char* test_api_assertion() { lsb_lua_sandbox *sb = lsb_create(NULL, "lua/counter.lua", "", NULL); lsb_err_value ret = lsb_init(sb, NULL); mu_assert(!ret, "lsb_init() received: %s", ret); lsb_stop_sandbox(NULL); mu_assert(lsb_destroy(NULL) == NULL, "not null"); mu_assert(lsb_usage(NULL, 0, 0) == 0, "not 0"); mu_assert(lsb_usage(sb, LSB_UT_MAX, 0) == 0, "not 0"); mu_assert(lsb_usage(sb, 0, LSB_US_MAX) == 0, "not 0"); mu_assert(strcmp(lsb_get_error(NULL), "") == 0, "not empty"); lsb_set_error(NULL, "foo"); mu_assert(lsb_get_lua(NULL) == NULL, "not null"); mu_assert(lsb_get_lua_file(NULL) == NULL, "not null"); mu_assert(lsb_get_parent(NULL) == NULL, "not null"); mu_assert(lsb_get_logger(NULL) == NULL, "not null"); mu_assert(lsb_get_state(NULL) == LSB_UNKNOWN, "not unknown"); lsb_add_function(NULL, lsb_test_write_output, "foo"); lsb_add_function(sb, NULL, "foo"); lsb_add_function(sb, lsb_test_write_output, NULL); mu_assert(lsb_pcall_setup(NULL, "foo") == LSB_ERR_UTIL_NULL, "not null"); mu_assert(lsb_pcall_setup(sb, NULL) == LSB_ERR_UTIL_NULL, "not null"); lsb_add_function(NULL, NULL, NULL); lsb_pcall_teardown(NULL); lsb_terminate(NULL, NULL); lsb_terminate(sb, NULL); lsb_add_function(sb, lsb_test_write_output, "write_output"); e = lsb_destroy(sb); mu_assert(!e, "lsb_destroy() received: %s", e); return NULL; }
static char* test_usage_error() { size_t u = lsb_usage(NULL, LSB_UT_MEMORY, LSB_US_CURRENT); mu_assert(u == 0, "NULL sandbox memory usage received: %" PRIuSIZE, u); lsb_lua_sandbox *sb = lsb_create(NULL, "lua/simple.lua", test_cfg, NULL); mu_assert(sb, "lsb_create() received: NULL"); u = lsb_usage(NULL, LSB_UT_MAX + 1, LSB_US_CURRENT); mu_assert(u == 0, "Invalid usage type received: %" PRIuSIZE, u); u = lsb_usage(NULL, LSB_UT_MEMORY, LSB_US_MAX + 1); mu_assert(u == 0, "Invalid usage stat received: %" PRIuSIZE, u); mu_assert(sb, "lsb_create() received: NULL"); lsb_terminate(sb, "forced termination"); lsb_state s = lsb_get_state(sb); mu_assert(s == LSB_TERMINATED, "lsb_get_state() received: %d", s); u = lsb_usage(sb, LSB_UT_MEMORY, LSB_US_CURRENT); mu_assert(u > 0, "Terminated memory usage received: 0"); e = lsb_destroy(sb); mu_assert(!e, "lsb_destroy() received: %s", e); return NULL; }
int lsb_heka_pm_input(lsb_heka_sandbox *hsb, double cp_numeric, const char *cp_string, bool profile) { if (!hsb || hsb->type != 'i') { return 1; } lsb_err_value ret = lsb_pcall_setup(hsb->lsb, pm_func_name); if (ret) { if (ret != LSB_ERR_TERMINATED) { char err[LSB_ERROR_SIZE]; snprintf(err, LSB_ERROR_SIZE, "%s() function was not found", pm_func_name); lsb_terminate(hsb->lsb, err); } return 1; } lua_State *lua = lsb_get_lua(hsb->lsb); if (!lua) return 1; if (!isnan(cp_numeric)) { lua_pushnumber(lua, cp_numeric); } else if (cp_string) { lua_pushstring(lua, cp_string); } else { lua_pushnil(lua); } return process_message(hsb, NULL, lua, 1, profile); }
static char* test_usage_error() { unsigned u = lsb_usage(NULL, LSB_UT_MEMORY, LSB_US_CURRENT); mu_assert(u == 0, "NULL sandbox memory usage received: %u", u); lua_sandbox* sb = lsb_create(NULL, "lua/simple.lua", 65765, 1000, 1024); mu_assert(sb, "lsb_create() received: NULL"); u = lsb_usage(NULL, LSB_UT_MAX + 1, LSB_US_CURRENT); mu_assert(u == 0, "Invalid usage type received: %u", u); u = lsb_usage(NULL, LSB_UT_MEMORY, LSB_US_MAX + 1); mu_assert(u == 0, "Invalid usage stat received: %u", u); mu_assert(sb, "lsb_create() received: NULL"); lsb_terminate(sb, "forced termination"); u = lsb_usage(sb, LSB_UT_MEMORY, LSB_US_CURRENT); mu_assert(u == 0, "Terminated memory usage received: %u", u); e = lsb_destroy(sb, NULL); mu_assert(!e, "lsb_destroy() received: %s", e); return NULL; }
int report(lua_sandbox* lsb, double tc) { static const char* func_name = "report"; lua_State* lua = lsb_get_lua(lsb); if (!lua) return 1; if (lsb_pcall_setup(lsb, func_name)) return 1; lua_pushnumber(lua, tc); if (lua_pcall(lua, 1, 0, 0) != 0) { char err[LSB_ERROR_SIZE]; int len = snprintf(err, LSB_ERROR_SIZE, "%s() %s", func_name, lua_tostring(lua, -1)); if (len >= LSB_ERROR_SIZE || len < 0) { err[LSB_ERROR_SIZE - 1] = 0; } lsb_terminate(lsb, err); return 1; } lsb_pcall_teardown(lsb); lua_gc(lua, LUA_GCCOLLECT, 0); return 0; }
int lsb_heka_timer_event(lsb_heka_sandbox *hsb, time_t t, bool shutdown) { static const char *func_name = "timer_event"; if (!hsb || (hsb->type != 'o' && hsb->type != 'a')) { return 1; } lua_State *lua = lsb_get_lua(hsb->lsb); if (!lua) return 1; if (lsb_pcall_setup(hsb->lsb, func_name)) { char err[LSB_ERROR_SIZE]; snprintf(err, LSB_ERROR_SIZE, "%s() function was not found", func_name); lsb_terminate(hsb->lsb, err); return 1; } // todo change if we need more than 1 sec resolution lua_pushnumber(lua, t * 1e9); lua_pushboolean(lua, shutdown); unsigned long long start, end; start = lsb_get_time(); if (lua_pcall(lua, 2, 0, 0) != 0) { char err[LSB_ERROR_SIZE]; size_t len = snprintf(err, LSB_ERROR_SIZE, "%s() %s", func_name, lua_tostring(lua, -1)); if (len >= LSB_ERROR_SIZE) { err[LSB_ERROR_SIZE - 1] = 0; } lsb_terminate(hsb->lsb, err); return 1; } end = lsb_get_time(); lsb_update_running_stats(&hsb->stats.te, (double)(end - start)); lsb_pcall_teardown(hsb->lsb); lua_gc(lua, LUA_GCCOLLECT, 0); return 0; }
int process(lua_sandbox* lsb, double ts) { static const char* func_name = "process"; lua_State* lua = lsb_get_lua(lsb); if (!lua) return 1; if (lsb_pcall_setup(lsb, func_name)) return 1; lua_pushnumber(lua, ts); if (lua_pcall(lua, 1, 1, 0) != 0) { char err[LSB_ERROR_SIZE]; int len = snprintf(err, LSB_ERROR_SIZE, "%s() %s", func_name, lua_tostring(lua, -1)); if (len >= LSB_ERROR_SIZE || len < 0) { err[LSB_ERROR_SIZE - 1] = 0; } lsb_terminate(lsb, err); return 1; } if (!lua_isnumber(lua, 1)) { char err[LSB_ERROR_SIZE]; int len = snprintf(err, LSB_ERROR_SIZE, "%s() must return a single numeric value", func_name); if (len >= LSB_ERROR_SIZE || len < 0) { err[LSB_ERROR_SIZE - 1] = 0; } lsb_terminate(lsb, err); return 1; } int status = (int)lua_tointeger(lua, 1); lua_pop(lua, 1); lsb_pcall_teardown(lsb); return status; }
int lsb_heka_pm_analysis(lsb_heka_sandbox *hsb, lsb_heka_message *msg, bool profile) { if (!hsb || !msg || hsb->type != 'a') return 1; if (lsb_pcall_setup(hsb->lsb, pm_func_name)) { char err[LSB_ERROR_SIZE]; snprintf(err, LSB_ERROR_SIZE, "%s() function was not found", pm_func_name); lsb_terminate(hsb->lsb, err); return 1; } lua_State *lua = lsb_get_lua(hsb->lsb); if (!lua) return 1; return process_message(hsb, msg, lua, 0, profile); }
lsb_err_value restore_global_data(lsb_lua_sandbox *lsb) { if (!lsb) { return LSB_ERR_UTIL_NULL; } if (!lsb->state_file || !file_exists(lsb->state_file)) { return NULL; } // Clear the sandbox limits during restoration. #ifdef LUA_JIT lua_gc(lsb->lua, LUA_GCSETMEMLIMIT, 0); #else size_t limit = lsb->usage[LSB_UT_MEMORY][LSB_US_LIMIT]; lsb->usage[LSB_UT_MEMORY][LSB_US_LIMIT] = 0; #endif lua_sethook(lsb->lua, NULL, 0, 0); int err = luaL_dofile(lsb->lua, lsb->state_file); if (err) { if (LUA_ERRFILE != err) { int len = snprintf(lsb->error_message, LSB_ERROR_SIZE, "restore_global_data %s", lua_tostring(lsb->lua, -1)); if (len >= LSB_ERROR_SIZE || len < 0) { lsb->error_message[LSB_ERROR_SIZE - 1] = 0; } lsb_terminate(lsb, NULL); return LSB_ERR_LUA; } } #ifdef LUA_JIT lua_gc(lsb->lua, LUA_GCSETMEMLIMIT, lsb->usage[LSB_UT_MEMORY][LSB_US_LIMIT]); // reinstate limit #else lua_gc(lsb->lua, LUA_GCCOLLECT, 0); lsb->usage[LSB_UT_MEMORY][LSB_US_LIMIT] = limit; lsb->usage[LSB_UT_MEMORY][LSB_US_MAXIMUM] = lsb->usage[LSB_UT_MEMORY][LSB_US_CURRENT]; #endif return NULL; }
char* lsb_destroy(lsb_lua_sandbox *lsb) { char *err = NULL; if (!lsb) { return err; } if (preserve_global_data(lsb)) { size_t len = strlen(lsb->error_message); err = malloc(len + 1); if (err != NULL) { strcpy(err, lsb->error_message); } } lsb_terminate(lsb, NULL); lsb_free_output_buffer(&lsb->output); free(lsb->state_file); free(lsb->lua_file); free(lsb); return err; }
int lsb_heka_pm_output(lsb_heka_sandbox *hsb, lsb_heka_message *msg, void *sequence_id, bool profile) { if (!hsb || !msg || hsb->type != 'o') return 1; if (lsb_pcall_setup(hsb->lsb, pm_func_name)) { char err[LSB_ERROR_SIZE]; snprintf(err, LSB_ERROR_SIZE, "%s() function was not found", pm_func_name); lsb_terminate(hsb->lsb, err); return 1; } lua_State *lua = lsb_get_lua(hsb->lsb); if (!lua) return 1; int nargs = 0; if (sequence_id) { nargs = 1; lua_pushlightuserdata(lua, sequence_id); } return process_message(hsb, msg, lua, nargs, profile); }
void lsb_heka_terminate_sandbox(lsb_heka_sandbox *hsb, const char *err) { lsb_terminate(hsb->lsb, err); }
static int process_message(lsb_heka_sandbox *hsb, lsb_heka_message *msg, lua_State *lua, int nargs, bool profile) { unsigned long long start, end; hsb->msg = msg; if (profile) { start = lsb_get_time(); } if (lua_pcall(lua, nargs, 2, 0) != 0) { char err[LSB_ERROR_SIZE]; const char *em = lua_tostring(lua, -1); if (hsb->type == 'i' && strcmp(em, LSB_SHUTTING_DOWN) == 0) { return 0; } size_t len = snprintf(err, LSB_ERROR_SIZE, "%s() %s", pm_func_name, em); if (len >= LSB_ERROR_SIZE) { err[LSB_ERROR_SIZE - 1] = 0; } lsb_terminate(hsb->lsb, err); return 1; } if (profile) { end = lsb_get_time(); lsb_update_running_stats(&hsb->stats.pm, (double)(end - start)); } hsb->msg = NULL; if (lua_type(lua, 1) != LUA_TNUMBER) { char err[LSB_ERROR_SIZE]; size_t len = snprintf(err, LSB_ERROR_SIZE, "%s() must return a numeric status code", pm_func_name); if (len >= LSB_ERROR_SIZE) { err[LSB_ERROR_SIZE - 1] = 0; } lsb_terminate(hsb->lsb, err); return 1; } int status = (int)lua_tointeger(lua, 1); switch (lua_type(lua, 2)) { case LUA_TNIL: lsb_set_error(hsb->lsb, NULL); break; case LUA_TSTRING: lsb_set_error(hsb->lsb, lua_tostring(lua, 2)); break; default: { char err[LSB_ERROR_SIZE]; int len = snprintf(err, LSB_ERROR_SIZE, "%s() must return a nil or string error message", pm_func_name); if (len >= LSB_ERROR_SIZE || len < 0) { err[LSB_ERROR_SIZE - 1] = 0; } lsb_terminate(hsb->lsb, err); return 1; } break; } lua_pop(lua, 2); lsb_pcall_teardown(hsb->lsb); if (status > 0) { char err[LSB_ERROR_SIZE]; size_t len = snprintf(err, LSB_ERROR_SIZE, "%s() received a termination status code", pm_func_name); if (len >= LSB_ERROR_SIZE) { err[LSB_ERROR_SIZE - 1] = 0; } lsb_terminate(hsb->lsb, err); return 1; } else if (status == LSB_HEKA_PM_FAIL) { ++hsb->stats.pm_cnt; ++hsb->stats.pm_failures; } else if (hsb->type != 'o' || status != LSB_HEKA_PM_RETRY) { ++hsb->stats.pm_cnt; } return status; }
static int output_message(hs_output_plugin* p) { int ret = 0, te_ret = 0; bool sample = p->sample; time_t current_t = time(NULL); p->matched = false; struct timespec ts, ts1; double delta = 0; size_t sequence_id = 0; if (p->msg->msg) { // non idle/empty message double mmdelta = 0; if (sample) clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts); p->matched = hs_eval_message_matcher(p->sb->mm, p->msg); if (sample) { clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts1); mmdelta = hs_timespec_delta(&ts, &ts1); } if (p->matched) { if (p->async_len) { sequence_id = p->sb->stats.pm_cnt + 1; int i = sequence_id % p->async_len; p->async_cp[i].input.id = p->cur.input.id; p->async_cp[i].input.offset = p->cur.input.offset; p->async_cp[i].analysis.id = p->cur.analysis.id; p->async_cp[i].analysis.offset = p->cur.analysis.offset; } ret = hs_process_message(p->sb->lsb, (void*)(sequence_id)); if (sample) { clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts); delta = hs_timespec_delta(&ts1, &ts); } if (ret <= 0) { pthread_mutex_lock(&p->cp_lock); switch (ret) { case 0: // sent ++p->sb->stats.pm_cnt; p->batching = false; break; case -1: // failure ++p->sb->stats.pm_cnt; ++p->sb->stats.pm_failures; break; case -2: // skip ++p->sb->stats.pm_cnt; break; case -3: // retry break; case -5: // async update if (!p->async_len) { lsb_terminate(p->sb->lsb, "cannot use async checkpointing without" " a configured buffer"); ret = 1; } // fall thru case -4: // batching ++p->sb->stats.pm_cnt; p->batching = true; break; default: lsb_terminate(p->sb->lsb, "invalid process_message return status"); ret = 1; break; } // update the stats if (sample) { hs_update_running_stats(&p->sb->stats.mm, mmdelta); hs_update_running_stats(&p->sb->stats.pm, delta); p->sample = false; mmdelta = 0; } p->sb->stats.cur_memory = lsb_usage(p->sb->lsb, LSB_UT_MEMORY, LSB_US_CURRENT); p->sb->stats.max_memory = lsb_usage(p->sb->lsb, LSB_UT_MEMORY, LSB_US_MAXIMUM); p->sb->stats.max_output = lsb_usage(p->sb->lsb, LSB_UT_OUTPUT, LSB_US_MAXIMUM); p->sb->stats.max_instructions = lsb_usage(p->sb->lsb, LSB_UT_INSTRUCTION, LSB_US_MAXIMUM); pthread_mutex_unlock(&p->cp_lock); if (ret == -1) { const char* err = lsb_get_error(p->sb->lsb); if (strlen(err) > 0) { hs_log(g_module, 4, "file: %s received: %d %s", p->sb->name, ret, lsb_get_error(p->sb->lsb)); } } } } // advance the checkpoint if not batching/async if (ret <= 0 && !p->batching) { pthread_mutex_lock(&p->cp_lock); p->cp.input.id = p->cur.input.id; p->cp.input.offset = p->cur.input.offset; p->cp.analysis.id = p->cur.analysis.id; p->cp.analysis.offset = p->cur.analysis.offset; pthread_mutex_unlock(&p->cp_lock); } if (mmdelta) { pthread_mutex_lock(&p->cp_lock); hs_update_running_stats(&p->sb->stats.mm, mmdelta); p->sample = false; pthread_mutex_unlock(&p->cp_lock); } } if (ret <= 0 && p->sb->ticker_interval && current_t >= p->sb->next_timer_event) { clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts); te_ret = hs_timer_event(p->sb->lsb, current_t); clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts1); delta = hs_timespec_delta(&ts, &ts1); pthread_mutex_lock(&p->cp_lock); hs_update_running_stats(&p->sb->stats.te, delta); pthread_mutex_unlock(&p->cp_lock); p->sb->next_timer_event = current_t + p->sb->ticker_interval; } if (ret > 0 || te_ret > 0) { hs_log(g_module, 3, "terminated: %s msg: %s", p->sb->name, lsb_get_error(p->sb->lsb)); return 1; } return ret; }
lsb_err_value lsb_init(lsb_lua_sandbox *lsb, const char *state_file) { if (!lsb) { return LSB_ERR_UTIL_NULL; } if (lsb->state != LSB_UNKNOWN) { lsb_terminate(lsb, LSB_ERR_INIT); return LSB_ERR_INIT; } if (state_file && strlen(state_file) > 0) { lsb->state_file = malloc(strlen(state_file) + 1); if (!lsb->state_file) { lsb_terminate(lsb, LSB_ERR_UTIL_OOM); return LSB_ERR_UTIL_OOM; } strcpy(lsb->state_file, state_file); } #ifndef LUA_JIT size_t mem_limit = lsb->usage[LSB_UT_MEMORY][LSB_US_LIMIT]; lsb->usage[LSB_UT_MEMORY][LSB_US_LIMIT] = 0; #endif preload_modules(lsb->lua); // load package module lua_pushcfunction(lsb->lua, luaopen_package); lua_pushstring(lsb->lua, LUA_LOADLIBNAME); lua_call(lsb->lua, 1, 1); lua_newtable(lsb->lua); lua_setmetatable(lsb->lua, -2); lua_pop(lsb->lua, 1); // load base module lua_getglobal(lsb->lua, "require"); if (!lua_iscfunction(lsb->lua, -1)) { snprintf(lsb->error_message, LSB_ERROR_SIZE, "lsb_init() 'require' not found"); lsb_terminate(lsb, NULL); return LSB_ERR_LUA; } lua_pushstring(lsb->lua, LUA_BASELIBNAME); if (lua_pcall(lsb->lua, 1, 0, 0)) { snprintf(lsb->error_message, LSB_ERROR_SIZE, "lsb_init %s", lua_tostring(lsb->lua, -1)); lsb_terminate(lsb, NULL); return LSB_ERR_LUA; } if (lsb->usage[LSB_UT_INSTRUCTION][LSB_US_LIMIT] != 0) { lua_sethook(lsb->lua, instruction_manager, LUA_MASKCOUNT, (int)lsb->usage[LSB_UT_INSTRUCTION][LSB_US_LIMIT]); } else { lua_sethook(lsb->lua, NULL, 0, 0); } #ifdef LUA_JIT // todo limit lua_gc(lsb->lua, LUA_GCSETMEMLIMIT, (int)lsb->usage[LSB_UT_MEMORY][LSB_US_LIMIT]); #else lsb->usage[LSB_UT_MEMORY][LSB_US_LIMIT] = mem_limit; #endif lua_CFunction pf = lua_atpanic(lsb->lua, unprotected_panic); int jump = setjmp(g_jbuf); if (jump || luaL_dofile(lsb->lua, lsb->lua_file) != 0) { int len = snprintf(lsb->error_message, LSB_ERROR_SIZE, "%s", lua_tostring(lsb->lua, -1)); if (len >= LSB_ERROR_SIZE || len < 0) { lsb->error_message[LSB_ERROR_SIZE - 1] = 0; } lsb_terminate(lsb, NULL); return LSB_ERR_LUA; } else { lua_gc(lsb->lua, LUA_GCCOLLECT, 0); lsb->usage[LSB_UT_INSTRUCTION][LSB_US_CURRENT] = instruction_usage(lsb); if (lsb->usage[LSB_UT_INSTRUCTION][LSB_US_CURRENT] > lsb->usage[LSB_UT_INSTRUCTION][LSB_US_MAXIMUM]) { lsb->usage[LSB_UT_INSTRUCTION][LSB_US_MAXIMUM] = lsb->usage[LSB_UT_INSTRUCTION][LSB_US_CURRENT]; } lsb->state = LSB_RUNNING; if (lsb->state_file) { lsb_err_value ret = restore_global_data(lsb); if (ret) return ret; } } lua_atpanic(lsb->lua, pf); return NULL; }