diff --git a/include/AlertCheckLuaEngine.h b/include/AlertCheckLuaEngine.h index f0a3e60748..c453455b3d 100644 --- a/include/AlertCheckLuaEngine.h +++ b/include/AlertCheckLuaEngine.h @@ -27,6 +27,7 @@ class AlertCheckLuaEngine : public LuaEngine { ScriptPeriodicity p; char script_path[MAX_PATH]; u_int num_calls; + bool script_ok; ticks total_ticks, tps /* Ticks per second */; virtual void lua_stats_detail(lua_State *vm) const {}; diff --git a/include/LuaEngine.h b/include/LuaEngine.h index 775239cb5c..712561a894 100644 --- a/include/LuaEngine.h +++ b/include/LuaEngine.h @@ -39,6 +39,7 @@ class LuaEngine { protected: lua_State *L; /**< The LuaEngine state.*/ + char *loaded_script_path; void lua_register_classes(lua_State *L, bool http_mode); @@ -66,17 +67,8 @@ class LuaEngine { inline NetworkInterface* getNetworkInterface() { return(getLuaVMContext(L)->iface); } NetworkStats* getNetwork() { return(getLuaVMContext(L)->network); } - /** - * @brief Run a Lua script. - * @details Run a script from within ntopng. No HTTP GUI. - * - * @param script_path Full path of lua script. - * @param iface Select the specified interface (if not NULL) - * @param load_only Load the Lua script but does not execute it - * - * @return 0 if the script has been executed successfully. - */ - int run_script(char *script_path, NetworkInterface *iface, bool load_only = false, time_t deadline = 0, bool no_pcall = false); + int load_script(char *script_path, NetworkInterface *iface); + int run_loaded_script(); /** * @brief Handling of request info of script. diff --git a/include/LuaReusableEngine.h b/include/LuaReusableEngine.h index 8481e7d77b..6b80f7a586 100644 --- a/include/LuaReusableEngine.h +++ b/include/LuaReusableEngine.h @@ -38,8 +38,8 @@ class LuaReusableEngine { LuaReusableEngine(const char *_script_path, NetworkInterface *_iface, int _reload_interval); ~LuaReusableEngine(); - bool pcall(time_t deadline); inline void setNextVmReload(time_t t) { next_reload = t; } + LuaEngine* getVm(time_t now); }; #endif diff --git a/include/ThreadedActivity.h b/include/ThreadedActivity.h index 823bcd3b2a..2600955b49 100644 --- a/include/ThreadedActivity.h +++ b/include/ThreadedActivity.h @@ -44,8 +44,8 @@ class ThreadedActivity { ThreadedActivityStats **threaded_activity_stats; Mutex vms_mutex; - /* iface -> engine */ - std::map vms; + /* ifid -> engine */ + std::map vms; void periodicActivityBody(); void aperiodicActivityBody(); @@ -55,6 +55,7 @@ class ThreadedActivity { bool isInterfaceTaskRunning(NetworkInterface *iface); void updateThreadedActivityStats(NetworkInterface *iface, u_long latest_duration); void reloadVm(const char *ifname); + LuaEngine* loadVm(char *script_path, NetworkInterface *iface, time_t when); public: ThreadedActivity(const char* _path, diff --git a/src/AlertCheckLuaEngine.cpp b/src/AlertCheckLuaEngine.cpp index 6e15a5cdcf..965115c2bf 100644 --- a/src/AlertCheckLuaEngine.cpp +++ b/src/AlertCheckLuaEngine.cpp @@ -29,6 +29,7 @@ AlertCheckLuaEngine::AlertCheckLuaEngine(AlertEntity alert_entity, ScriptPeriodi const char *lua_file = NULL; iface = _iface; tps = Utils::gettickspersec(); + script_ok = false; p = script_periodicity; @@ -64,14 +65,24 @@ AlertCheckLuaEngine::AlertCheckLuaEngine(AlertEntity alert_entity, ScriptPeriodi lua_file); ntop->fixPath(script_path); - if(run_script(script_path, iface, true /* Load only */) < 0) + if(load_script(script_path, iface) < 0) return; + /* Execute the script so that the "setup" function will be exposed */ + if(lua_pcall(L, 0, 0, 0)) { + ntop->getTrace()->traceEvent(TRACE_WARNING, "Script failure[%s] [%s]", script_path, lua_tostring(L, -1)); + return; + } + lua_getglobal(L, "setup"); /* Called function */ lua_pushstring(L, Utils::periodicityToScriptName(p)); /* push 1st argument */ - if(!pcall(1 /* 1 argument */, 0)) + if(lua_pcall(L, 1 /* 1 argument */, 0, 0)) { + ntop->getTrace()->traceEvent(TRACE_WARNING, "Script failure[%s] [%s]", script_path, lua_tostring(L, -1)); return; + } + + script_ok = true; } else { /* Possibly handle a generic entity */ script_path[0] = '\0'; @@ -87,7 +98,7 @@ AlertCheckLuaEngine::~AlertCheckLuaEngine() { ntop->getTrace()->traceEvent(TRACE_WARNING, "[elapsed time: %.4f sec][num calls: %u][calls/sec: %.4f][%s][clocks/sec: %llu]", elapsed_time, num_calls, num_calls / elapsed_time, script_path, tps); #endif - if(script_path[0] != '\0') { + if(script_ok) { lua_getglobal(L, "teardown"); /* Called function */ if(lua_isfunction(L, -1)) { @@ -139,6 +150,9 @@ const char * AlertCheckLuaEngine::getGranularity() const { bool AlertCheckLuaEngine::pcall(int num_args, int num_results) { ticks t_begin; + if(!script_ok) + return(false); + t_begin = Utils::getticks(); if(lua_pcall(L, num_args, num_results, 0)) { ntop->getTrace()->traceEvent(TRACE_WARNING, "Script failure[%s] [%s]", script_path, lua_tostring(L, -1)); diff --git a/src/LuaEngine.cpp b/src/LuaEngine.cpp index 45a6466984..3e864767e0 100644 --- a/src/LuaEngine.cpp +++ b/src/LuaEngine.cpp @@ -89,6 +89,7 @@ static void stackDump(lua_State *L) { LuaEngine::LuaEngine() { std::bad_alloc bax; void *ctx; + loaded_script_path = NULL; #ifdef HAVE_NEDGE if(!ntop->getPro()->has_valid_license()) { @@ -157,6 +158,8 @@ LuaEngine::~LuaEngine() { lua_close(L); } + + if(loaded_script_path) free(loaded_script_path); } /* ******************************* */ @@ -11667,14 +11670,15 @@ static int post_iterator(void *cls, /* ****************************************** */ -/* - Run a Lua script from within ntopng (no HTTP GUI) -*/ -int LuaEngine::run_script(char *script_path, NetworkInterface *iface, bool load_only, time_t deadline, bool no_pcall) { +/* Loads a script into the engine from within ntopng (no HTTP GUI). */ +int LuaEngine::load_script(char *script_path, NetworkInterface *iface) { int rc = 0; if(!L) return(-1); + if(loaded_script_path) + free(loaded_script_path); + try { luaL_openlibs(L); /* Load base libraries */ lua_register_classes(L, false); /* Load custom classes */ @@ -11684,22 +11688,12 @@ int LuaEngine::run_script(char *script_path, NetworkInterface *iface, bool load_ getLuaVMUservalue(L, iface) = iface; } - /* An optional deadline can be passed to the script so actions - can be taken from lua to stop the execution of the dealine is approaching */ - if(deadline) { - lua_pushinteger(L, deadline); - lua_setglobal(L, "deadline"); - } - #ifdef NTOPNG_PRO if(ntop->getPro()->has_valid_license()) - rc = __ntop_lua_handlefile(L, script_path, !load_only /* Execute? */); + rc = __ntop_lua_handlefile(L, script_path, false /* Do not execute */); else + rc = luaL_loadfile(L, script_path); #endif - rc = !load_only ? luaL_dofile(L, script_path) : luaL_loadfile(L, script_path); - - if(rc == 0 && load_only && !no_pcall) - rc = lua_pcall(L, 0, 0, 0); /* Prevents loaded scripts from failing silently by showing possible syntax errors */ if(rc != 0) { const char *err = lua_tostring(L, -1); @@ -11707,17 +11701,51 @@ int LuaEngine::run_script(char *script_path, NetworkInterface *iface, bool load_ ntop->getTrace()->traceEvent(TRACE_WARNING, "Script failure [%s][%s]", script_path, err ? err : ""); rc = -1; } - } catch(...) { ntop->getTrace()->traceEvent(TRACE_WARNING, "Script failure [%s]", script_path); rc = -2; } + loaded_script_path = strdup(script_path); + return(rc); } /* ****************************************** */ +/* + Run a loaded Lua script (via LuaEngine::load_script) from within ntopng (no HTTP GUI). + The script is invoked without any additional parameters. run_loaded_script can be called + multiple times to run the same script again. +*/ +int LuaEngine::run_loaded_script() { + int top = lua_gettop(L); + int rv = 0; + + if(!loaded_script_path) + return(-1); + + /* Copy the lua_chunk to be able to possibly run it again next time */ + lua_pushvalue(L, -1); + + /* Perform the actual call */ + if(lua_pcall(L, 0, 0, 0) != 0) { + if(lua_type(L, -1) == LUA_TSTRING) { + const char *err = lua_tostring(L, -1); + ntop->getTrace()->traceEvent(TRACE_WARNING, "Script failure [%s][%s]", loaded_script_path, err ? err : ""); + } + + rv = -2; + } + + /* Reset the stack */ + lua_settop(L, top); + + return(rv); +} + +/* ****************************************** */ + /* http://www.geekhideout.com/downloads/urlcode.c */ #if 0 diff --git a/src/LuaReusableEngine.cpp b/src/LuaReusableEngine.cpp index f84c7d1409..3a1ce2a782 100644 --- a/src/LuaReusableEngine.cpp +++ b/src/LuaReusableEngine.cpp @@ -59,7 +59,7 @@ void LuaReusableEngine::reloadVm(time_t now) { return; } - if(vm->run_script(script_path, iface, true /* load only */, 0, true /* no_pcall */) == 0) { + if(vm->load_script(script_path, iface) == 0) { next_reload = now + reload_interval; } else { /* Retry next time */ @@ -70,41 +70,9 @@ void LuaReusableEngine::reloadVm(time_t now) { /* ******************************* */ -bool LuaReusableEngine::pcall(time_t deadline) { - time_t now = time(NULL); - int top; - bool rv = true; - +LuaEngine* LuaReusableEngine::getVm(time_t now) { if((vm == NULL) || (now >= next_reload)) reloadVm(now); - if(vm == NULL) - return(false); - - if(deadline) { - lua_pushinteger(vm->getState(), deadline); - lua_setglobal(vm->getState(), "deadline"); - } - - top = lua_gettop(vm->getState()); - - /* Copy the lua_chunk to be able to run it again next time */ - lua_pushvalue(vm->getState(), -1); - - /* Perform the actual call */ - ntop->getTrace()->traceEvent(TRACE_DEBUG, "%p: pcall(%s, %s)", this, script_path, iface->get_name()); - - if(lua_pcall(vm->getState(), 0, 0, 0) != 0) { - if(lua_type(vm->getState(), -1) == LUA_TSTRING) { - const char *err = lua_tostring(vm->getState(), -1); - ntop->getTrace()->traceEvent(TRACE_WARNING, "Script failure [%s][%s]", script_path, err ? err : ""); - } - - rv = false; - } - - /* Reset the stack */ - lua_settop(vm->getState(), top); - - return(rv); + return(vm); } diff --git a/src/SyslogLuaEngine.cpp b/src/SyslogLuaEngine.cpp index 91a70a2d27..b18f745045 100644 --- a/src/SyslogLuaEngine.cpp +++ b/src/SyslogLuaEngine.cpp @@ -32,7 +32,7 @@ SyslogLuaEngine::SyslogLuaEngine(NetworkInterface *iface) : LuaEngine() { ntop->fixPath(script_path); - if (run_script(script_path, iface, true /* Load only */) < 0) { + if(load_script(script_path, iface) < 0) { ntop->getTrace()->traceEvent(TRACE_ERROR, "Failure loading %s", script_path); return; } diff --git a/src/ThreadedActivity.cpp b/src/ThreadedActivity.cpp index 71acd668df..16dab529c8 100644 --- a/src/ThreadedActivity.cpp +++ b/src/ThreadedActivity.cpp @@ -59,7 +59,7 @@ ThreadedActivity::ThreadedActivity(const char* _path, /* ******************************************* */ ThreadedActivity::~ThreadedActivity() { - std::map::iterator it; + std::map::iterator it; /* NOTE: terminateEnqueueLoop should have already been called by the PeriodicActivities * destructor. */ @@ -211,7 +211,7 @@ void ThreadedActivity::runScript() { /* Run a script - both periodic and one-shot scripts are called here */ void ThreadedActivity::runScript(char *script_path, NetworkInterface *iface, time_t deadline) { - LuaEngine *l = NULL; + LuaEngine *l; u_long max_duration_ms = periodicity * 1e3; u_long msec_diff; struct timeval begin, end; @@ -226,43 +226,17 @@ void ThreadedActivity::runScript(char *script_path, NetworkInterface *iface, tim ntop->getTrace()->traceEvent(TRACE_INFO, "Running %s (iface=%p)", script_path, iface); - if(reuse_vm) { - LuaReusableEngine *engine; - std::map::iterator it; + l = loadVm(script_path, iface, time(NULL)); + if(l == NULL) + return; - /* Reuse an existing engine or allocate a new one */ - vms_mutex.lock(__FILE__, __LINE__); + /* Set the global deadline parameter */ + lua_pushinteger(l->getState(), deadline); + lua_setglobal(l->getState(), "deadline"); - if((it = vms.find(iface->get_name())) != vms.end()) - engine = it->second; - else { - engine = new LuaReusableEngine(script_path, iface, 300 /* Reload every 5 minutes */); - vms[iface->get_name()] = engine; - } - - vms_mutex.unlock(__FILE__, __LINE__); - - gettimeofday(&begin, NULL); - engine->pcall(deadline); - gettimeofday(&end, NULL); - } else { - try { - l = new LuaEngine(); - } catch(std::bad_alloc& ba) { - static bool oom_warning_sent = false; - - if(!oom_warning_sent) { - ntop->getTrace()->traceEvent(TRACE_ERROR, "[ThreadedActivity] Unable to start a Lua interpreter."); - oom_warning_sent = true; - } - - return; - } - - gettimeofday(&begin, NULL); - l->run_script(script_path, iface, false /* Execute */, deadline); - gettimeofday(&end, NULL); - } + gettimeofday(&begin, NULL); + l->run_loaded_script(); + gettimeofday(&end, NULL); msec_diff = (end.tv_sec - begin.tv_sec) * 1000 + (end.tv_usec - begin.tv_usec) / 1000; updateThreadedActivityStats(iface, msec_diff); @@ -286,12 +260,53 @@ void ThreadedActivity::runScript(char *script_path, NetworkInterface *iface, tim else setInterfaceTaskRunning(iface, false); - if(l) + if(!reuse_vm) delete l; } /* ******************************************* */ +LuaEngine* ThreadedActivity::loadVm(char *script_path, NetworkInterface *iface, time_t when) { + LuaEngine *l; + + if(reuse_vm) { + /* Reuse an existing engine or allocate a new one */ + LuaReusableEngine *engine; + std::map::iterator it; + + vms_mutex.lock(__FILE__, __LINE__); + + if((it = vms.find(iface->get_id())) != vms.end()) + engine = it->second; + else { + engine = new LuaReusableEngine(script_path, iface, 300 /* reload interval */); + + /* Save the VM for later use */ + vms[iface->get_id()] = engine; + } + + vms_mutex.unlock(__FILE__, __LINE__); + + l = engine->getVm(when); + } else { + try { + /* NOTE: this needs to be deallocated by the caller */ + l = new LuaEngine(); + + if(l->load_script(script_path, iface) != 0) { + delete l; + l = NULL; + } + } catch(std::bad_alloc& ba) { + l = NULL; + } + } + + return(l); +} + +/* ******************************************* */ + void ThreadedActivity::aperiodicActivityBody() { if(!isTerminating()) runScript(); @@ -432,7 +447,7 @@ void ThreadedActivity::lua(NetworkInterface *iface, lua_State *vm) { /* ******************************************* */ void ThreadedActivity::setNextVmReload(time_t t) { - std::map::iterator it; + std::map::iterator it; vms_mutex.lock(__FILE__, __LINE__);