diff options
Diffstat (limited to 'ctl-lib')
-rw-r--r-- | ctl-lib/CMakeLists.txt | 2 | ||||
-rw-r--r-- | ctl-lib/ctl-action.c | 6 | ||||
-rw-r--r-- | ctl-lib/ctl-config.c | 5 | ||||
-rw-r--r-- | ctl-lib/ctl-lua-utils.c | 87 | ||||
-rw-r--r-- | ctl-lib/ctl-lua.c | 319 | ||||
-rw-r--r-- | ctl-lib/ctl-lua.h | 8 | ||||
-rw-r--r-- | ctl-lib/ctl-plugin.c | 234 | ||||
-rw-r--r-- | ctl-lib/ctl-plugin.h | 5 |
8 files changed, 440 insertions, 226 deletions
diff --git a/ctl-lib/CMakeLists.txt b/ctl-lib/CMakeLists.txt index 4f29014..3ab0774 100644 --- a/ctl-lib/CMakeLists.txt +++ b/ctl-lib/CMakeLists.txt @@ -19,7 +19,7 @@ # Include LUA only when requested if(CONTROL_SUPPORT_LUA) message(STATUS "Notice: LUA Controler Support Selected") - set(CTL_LUA_SOURCE ctl-lua.c ctl-timer.c) + set(CTL_LUA_SOURCE ctl-lua.c ctl-timer.c ctl-lua-utils.c) ADD_COMPILE_OPTIONS(-DCONTROL_SUPPORT_LUA) else(CONTROL_SUPPORT_LUA) message(STATUS "Warning: LUA Without Support ") diff --git a/ctl-lib/ctl-action.c b/ctl-lib/ctl-action.c index 277591d..da2eef1 100644 --- a/ctl-lib/ctl-action.c +++ b/ctl-lib/ctl-action.c @@ -24,8 +24,6 @@ #include "ctl-config.h" -extern CtlLua2cFuncT *ctlLua2cFunc; - PUBLIC int ActionLabelToIndex(CtlActionT*actions, const char* actionLabel) { for (int idx = 0; actions[idx].uid; idx++) { if (!strcasecmp(actionLabel, actions[idx].uid)) return idx; @@ -58,10 +56,6 @@ PUBLIC void ActionExecUID(AFB_ReqT request, CtlConfigT *ctlConfig, const char *u PUBLIC void ActionExecOne(CtlSourceT *source, CtlActionT* action, json_object *queryJ) { int err = 0; - if(action->type == CTL_TYPE_LUA && ctlLua2cFunc && ctlLua2cFunc->l2cCount) { - LuaL2cNewLib (ctlLua2cFunc->l2cFunc, ctlLua2cFunc->l2cCount); - } - switch (action->type) { case CTL_TYPE_API: { diff --git a/ctl-lib/ctl-config.c b/ctl-lib/ctl-config.c index 6294f63..b811754 100644 --- a/ctl-lib/ctl-config.c +++ b/ctl-lib/ctl-config.c @@ -109,11 +109,6 @@ PUBLIC int CtlConfigExec(AFB_ApiT apiHandle, CtlConfigT *ctlConfig) { } } -#ifdef CONTROL_SUPPORT_LUA - int err = LuaConfigExec(apiHandle, ctlConfig->api); - if (err) goto OnErrorExit; -#endif - // Loop on every section and process config int errcount=0; for (int idx = 0; ctlConfig->sections[idx].key != NULL; idx++) { diff --git a/ctl-lib/ctl-lua-utils.c b/ctl-lib/ctl-lua-utils.c new file mode 100644 index 0000000..da22b7a --- /dev/null +++ b/ctl-lib/ctl-lua-utils.c @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2016 \"IoT.bzh\" + * Author Fulup Ar Foll <fulup@iot.bzh> + * + Licensed under the Apache License, Version 2.0 (the \"License\"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an \"AS IS\" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ref: + * (manual) https://www.lua.org/manual/5.3/manual.html + * (lua->C) http://www.troubleshooters.com/codecorn/lua/lua_c_calls_lua.htm#_Anatomy_of_a_Lua_Call + * (lua/C Var) http://acamara.es/blog/2012/08/passing-variables-from-lua-5-2-to-c-and-vice-versa/ + * (Lua/C Lib)https://john.nachtimwald.com/2014/07/12/wrapping-a-c-library-in-lua/ + * (Lua/C Table) https://gist.github.com/SONIC3D/10388137 + * (Lua/C Nested table) https://stackoverflow.com/questions/45699144/lua-nested-table-from-lua-to-c + * (Lua/C Wrapper) https://stackoverflow.com/questions/45699950/lua-passing-handle-to-function-created-with-newlib + * + */ + +const char *lua_utils = "--===================================================\n\ +--= Niklas Frykholm\n\ +-- basically if user tries to create global variable\n\ +-- the system will not let them!!\n\ +-- call GLOBAL_lock(_G)\n\ +-- \n\ +--===================================================\n\ +function GLOBAL_lock(t)\n\ + local mt = getmetatable(t) or {}\n\ + mt.__newindex = lock_new_index\n\ + setmetatable(t, mt)\n\ +end\n\ +\n\ +--===================================================\n\ +-- call GLOBAL_unlock(_G)\n\ +-- to change things back to normal.\n\ +--===================================================\n\ +function GLOBAL_unlock(t)\n\ + local mt = getmetatable(t) or {}\n\ + mt.__newindex = unlock_new_index\n\ + setmetatable(t, mt)\n\ +end\n\ +\n\ +function lock_new_index(t, k, v)\n\ + if (string.sub(k,1,1) ~= \"_\") then\n\ + GLOBAL_unlock(_G)\n\ + error(\"GLOBALS are locked -- \" .. k ..\n\ + \" must be declared local or prefix with '_' for globals.\", 2)\n\ + else\n\ + rawset(t, k, v)\n\ + end\n\ +end\n\ +\n\ +function unlock_new_index(t, k, v)\n\ + rawset(t, k, v)\n\ +end\n\ +\n\ +-- return serialised version of printable table\n\ +function Dump_Table(o)\n\ + if type(o) == 'table' then\n\ + local s = '{ '\n\ + for k,v in pairs(o) do\n\ + if type(k) ~= 'number' then k = '\"'..k..'\"' end\n\ + s = s .. '['..k..'] = ' .. Dump_Table(v) .. ','\ + end\n\ + return s .. '} '\n\ + else\n\ + return tostring(o)\n\ + end\n\ +end\n\ +\n\ +-- simulate C prinf function\n\ +printf = function(s,...)\n\ + io.write(s:format(...))\n\ + io.write(\"\")\n\ + return\n\ +end\n\ +\n\ +-- lock global variable\n\ +GLOBAL_lock(_G)\n\ +"; diff --git a/ctl-lib/ctl-lua.c b/ctl-lib/ctl-lua.c index 757956e..d5c2975 100644 --- a/ctl-lib/ctl-lua.c +++ b/ctl-lib/ctl-lua.c @@ -36,6 +36,7 @@ #define JSON_ERROR (json_object*)-1 +extern CtlLua2cFuncT *ctlLua2cFunc; static lua_State* luaState; #ifndef CTX_MAGIC @@ -299,7 +300,7 @@ STATIC int LuaFormatMessage(lua_State* luaState, int verbosity, int level) { const char *format = json_object_get_string(json_object_array_get_idx(responseJ, 0)); int arrayIdx=1; - int targetIdx=0; + int uidIdx=0; for (int idx=0; format[idx] !='\0'; idx++) { @@ -310,43 +311,43 @@ STATIC int LuaFormatMessage(lua_State* luaState, int verbosity, int level) { switch (format[++idx]) { case 'd': - if (slotJ) targetIdx += snprintf (&message[targetIdx], LUA_MSG_MAX_LENGTH-targetIdx,"%d", json_object_get_int(slotJ)); - else targetIdx += snprintf (&message[targetIdx], LUA_MSG_MAX_LENGTH-targetIdx,"nil"); + if (slotJ) uidIdx += snprintf (&message[uidIdx], LUA_MSG_MAX_LENGTH-uidIdx,"%d", json_object_get_int(slotJ)); + else uidIdx += snprintf (&message[uidIdx], LUA_MSG_MAX_LENGTH-uidIdx,"nil"); arrayIdx++; break; case 'f': - if (slotJ) targetIdx += snprintf (&message[targetIdx], LUA_MSG_MAX_LENGTH-targetIdx,"%f", json_object_get_double(slotJ)); - else targetIdx += snprintf (&message[targetIdx], LUA_MSG_MAX_LENGTH-targetIdx,"nil"); + if (slotJ) uidIdx += snprintf (&message[uidIdx], LUA_MSG_MAX_LENGTH-uidIdx,"%f", json_object_get_double(slotJ)); + else uidIdx += snprintf (&message[uidIdx], LUA_MSG_MAX_LENGTH-uidIdx,"nil"); arrayIdx++; break; case'%': - message[targetIdx]='%'; - targetIdx++; + message[uidIdx]='%'; + uidIdx++; break; case 'A': - targetIdx += snprintf (&message[targetIdx], LUA_MSG_MAX_LENGTH-targetIdx,"level: %s", source->uid); + uidIdx += snprintf (&message[uidIdx], LUA_MSG_MAX_LENGTH-uidIdx,"level: %s", source->uid); break; case 's': default: - if (slotJ) targetIdx += snprintf (&message[targetIdx], LUA_MSG_MAX_LENGTH-targetIdx,"%s", json_object_get_string(slotJ)); - else targetIdx += snprintf (&message[targetIdx], LUA_MSG_MAX_LENGTH-targetIdx,"nil"); + if (slotJ) uidIdx += snprintf (&message[uidIdx], LUA_MSG_MAX_LENGTH-uidIdx,"%s", json_object_get_string(slotJ)); + else uidIdx += snprintf (&message[uidIdx], LUA_MSG_MAX_LENGTH-uidIdx,"nil"); arrayIdx++; } } else { - if (targetIdx >= LUA_MSG_MAX_LENGTH) { + if (uidIdx >= LUA_MSG_MAX_LENGTH) { AFB_ApiWarning (source->api, "LuaFormatMessage: message[%s] owerverflow LUA_MSG_MAX_LENGTH=%d", format, LUA_MSG_MAX_LENGTH); - targetIdx --; // move backward for EOL + uidIdx --; // move backward for EOL break; } else { - message[targetIdx++] = format[idx]; + message[uidIdx++] = format[idx]; } } } - message[targetIdx]='\0'; + message[uidIdx]='\0'; PrintMessage: // TBD: __file__ and __line__ should match LUA source code @@ -695,10 +696,10 @@ PUBLIC int LuaCallFunc (CtlSourceT *source, CtlActionT *action, json_object *que int err, count; - json_object* argsJ = action->argsJ; - const char* func = action->exec.lua.funcname; + json_object* argsJ = action->argsJ; + const char* func = action->exec.lua.funcname; - // load function (should exist in CONTROL_PATH_LUA + // load function (should exist in CONTROL_PATH_LUA) lua_getglobal(luaState, func); // push source on the stack @@ -737,145 +738,178 @@ PUBLIC int LuaCallFunc (CtlSourceT *source, CtlActionT *action, json_object *que return -1; } +PUBLIC int luaLoadScript(const char *luaScriptPath) +{ + int err = luaL_loadfile(luaState, luaScriptPath); + if (err) { + AFB_ApiError(source->api, "LUA-DOSCRIPT HOOPs Error in LUA loading scripts=%s err=%s", luaScriptPath, lua_tostring(luaState,-1)); + return err; + } -// Execute LUA code from received API request -STATIC void LuaDoAction (LuaDoActionT action, AFB_ReqT request) { + // Script was loaded we need to parse to make it executable + err = lua_pcall(luaState, 0, 0, 0); + if (err) { + AFB_ApiError(source->api, "LUA-DOSCRIPT:FAIL to load %s", luaScriptPath); + return err; + } - int err, count=0; - CtlSourceT *source = alloca(sizeof(CtlSourceT)); - source->request = request; + return err; +} - json_object* queryJ = AFB_ReqJson(request); +STATIC int LuaDoScript(json_object *queryJ, CtlSourceT *source) +{ + const char *uid = NULL, *func = NULL; + char luaScriptPath[CONTROL_MAXPATH_LEN]; + char *filename, *fullpath; + int index, err = 0; + json_object *argsJ=NULL; + static json_object *luaScriptPathJ = NULL; + if (!queryJ) { + return -1; + } - switch (action) { + err= wrap_json_unpack (queryJ, "{s:s,s?s,s?s,s?o !}", + "uid", &uid, + "spath",&luaScriptPathJ, + "function",&func, + "args",&argsJ); - case LUA_DOSTRING: { - const char *script = json_object_get_string(queryJ); - err=luaL_loadstring(luaState, script); - if (err) { - AFB_ApiError(source->api, "LUA-DO-COMPILE:FAIL String=%s err=%s", script, lua_tostring(luaState,-1) ); - goto OnErrorExit; - } - // Push AFB client context on the stack - LuaAfbSourceT *afbSource= LuaSourcePush(luaState, source); - if (!afbSource) goto OnErrorExit; + if (err) { + return -1; + } - break; + // search for filename=script in CONTROL_LUA_PATH + if (!luaScriptPathJ) { + strncpy(luaScriptPath,CONTROL_DOSCRIPT_PRE, strlen(CONTROL_DOSCRIPT_PRE)+1); + strncat(luaScriptPath,"-", strlen("-")); + strncat(luaScriptPath,uid, strlen(uid)); + luaScriptPathJ= ScanForConfig(luaScriptPath , CTL_SCAN_RECURSIVE, luaScriptPath, ".lua"); + } + + for (index=0; index < json_object_array_length(luaScriptPathJ); index++) { + json_object *entryJ=json_object_array_get_idx(luaScriptPathJ, index); + + err= wrap_json_unpack (entryJ, "{s:s, s:s !}", "fullpath", &fullpath,"filename", &filename); + if (err) { + AFB_ApiError(source->api, "LUA-DOSCRIPT-SCAN:HOOPs invalid config file path = %s", json_object_get_string(entryJ)); + return -2; } - case LUA_DOCALL: { - const char *func; - json_object *argsJ=NULL; + // Ignoring other found script. Only take the first one. + if (!index) { + strncpy (luaScriptPath, fullpath, strlen(fullpath)+1); + strncat (luaScriptPath, "/", strlen("/")); + strncat (luaScriptPath, filename, strlen(filename)); + } + } - err= wrap_json_unpack (queryJ, "{s:s, s?o !}", "target", &func, "args", &argsJ); - if (err) { - AFB_ApiError(source->api, "LUA-DOCALL-SYNTAX missing target|args query=%s", json_object_get_string(queryJ)); - goto OnErrorExit; - } + err = luaLoadScript(luaScriptPath); + if(err) + return err; - // load function (should exist in CONTROL_PATH_LUA - lua_getglobal(luaState, func); + // if no func name given try to deduct from filename + if (!func && (func=(char*)GetMidleName(filename))!=NULL) { + strncpy(luaScriptPath,"_", strlen("_")+1); + strncat(luaScriptPath,func, strlen(func)); + func=luaScriptPath; + } + if (!func) { + AFB_ApiError(source->api, "LUA-DOSCRIPT:FAIL to deduct funcname from %s", filename); + return -5; + } - // Push AFB client context on the stack - LuaAfbSourceT *afbSource= LuaSourcePush(luaState, source); - if (!afbSource) goto OnErrorExit; + // load function (should exist in CONTROL_PATH_LUA + lua_getglobal(luaState, func); - // push query on the stack - if (!argsJ) { - lua_pushnil(luaState); - count++; - } else { - count+= LuaPushArgument (source, argsJ); - } + // Push AFB client context on the stack + LuaAfbSourceT *afbSource = LuaSourcePush(luaState, source); + if (!afbSource) + return -6; - break; - } + return 0; +} - case LUA_DOSCRIPT: { // Fulup need to fix argument passing - char *filename; char*fullpath; - char luaScriptPath[CONTROL_MAXPATH_LEN]; - int index; - - // scan luascript search path once - static json_object *luaScriptPathJ =NULL; - - // extract value from query - const char *target=NULL,*func=NULL; - json_object *argsJ=NULL; - err= wrap_json_unpack (queryJ, "{s:s,s?s,s?s,s?o !}", - "target", &target, - "path",&luaScriptPathJ, - "function",&func, - "args",&argsJ); - if (err) { - AFB_ApiError(source->api, "LUA-DOSCRIPT-SYNTAX:missing target|[path]|[function]|[args] query=%s", json_object_get_string(queryJ)); - goto OnErrorExit; - } +STATIC int LuaDoCall(json_object *queryJ, CtlSourceT *source) +{ + int err = 0; + int count = 0; + const char *func; + json_object *argsJ = NULL; - // search for filename=script in CONTROL_LUA_PATH - if (!luaScriptPathJ) { - strncpy(luaScriptPath,CONTROL_DOSCRIPT_PRE, strlen(CONTROL_DOSCRIPT_PRE)+1); - strncat(luaScriptPath,"-", strlen("-")); - strncat(luaScriptPath,target, strlen(target)); - luaScriptPathJ= ScanForConfig(CONTROL_LUA_PATH , CTL_SCAN_RECURSIVE,luaScriptPath,".lua"); - } - for (index=0; index < json_object_array_length(luaScriptPathJ); index++) { - json_object *entryJ=json_object_array_get_idx(luaScriptPathJ, index); + if(!queryJ) + return -1; - err= wrap_json_unpack (entryJ, "{s:s, s:s !}", "fullpath", &fullpath,"filename", &filename); - if (err) { - AFB_ApiError(source->api, "LUA-DOSCRIPT-SCAN:HOOPs invalid config file path = %s", json_object_get_string(entryJ)); - goto OnErrorExit; - } + err = wrap_json_unpack(queryJ, "{s:s, s?o !}", "uid", &func, "args", &argsJ); + if (err) + return -2; - if (index > 0) AFB_ApiWarning(source->api, "LUA-DOSCRIPT-SCAN:Ignore second script=%s path=%s", filename, fullpath); - else { - strncpy (luaScriptPath, fullpath, strlen(fullpath)+1); - strncat (luaScriptPath, "/", strlen("/")); - strncat (luaScriptPath, filename, strlen(filename)); - } - } + // load function (should exist in CONTROL_PATH_LUA + lua_getglobal(luaState, func); - err= luaL_loadfile(luaState, luaScriptPath); - if (err) { - AFB_ApiError(source->api, "LUA-DOSCRIPT HOOPs Error in LUA loading scripts=%s err=%s", luaScriptPath, lua_tostring(luaState,-1)); - goto OnErrorExit; - } + // Push AFB client context on the stack + LuaAfbSourceT *afbSource= LuaSourcePush(luaState, source); + if (!afbSource) + return -3; - // script was loaded we need to parse to make it executable - err=lua_pcall(luaState, 0, 0, 0); - if (err) { - AFB_ApiError(source->api, "LUA-DOSCRIPT:FAIL to load %s", luaScriptPath); - goto OnErrorExit; - } + // push query on the stack + if (!argsJ) { + lua_pushnil(luaState); + count++; + } else { + count+= LuaPushArgument (source, argsJ); + } - // if no func name given try to deduct from filename - if (!func && (func=(char*)GetMidleName(filename))!=NULL) { - strncpy(luaScriptPath,"_", strlen("_")+1); - strncat(luaScriptPath,func, strlen(func)); - func=luaScriptPath; - } - if (!func) { - AFB_ApiError(source->api, "LUA-DOSCRIPT:FAIL to deduct funcname from %s", filename); - goto OnErrorExit; - } + return count; +} - // load function (should exist in CONTROL_PATH_LUA - lua_getglobal(luaState, func); +STATIC int LuaDoString(const char *script, CtlSourceT *source) +{ + int err = 0; - // Push AFB client context on the stack - LuaAfbSourceT *afbSource= LuaSourcePush(luaState, source); - if (!afbSource) goto OnErrorExit; + err = luaL_loadstring(luaState, script); + if (err) + return -1; - // push function arguments - if (!argsJ) { - lua_pushnil(luaState); - count++; - } else { - count+= LuaPushArgument(source, argsJ); - } + // Push AFB client context on the stack + if(source) { + LuaAfbSourceT *afbSource= LuaSourcePush(luaState, source); + if (!afbSource) + return -2; + } + return 0; +} + +// Execute LUA code from received API request +STATIC void LuaDoAction (LuaDoActionT action, AFB_ReqT request) { + int err, count=0; + CtlSourceT *source = alloca(sizeof(CtlSourceT)); + source->request = request; + + json_object* queryJ = AFB_ReqJson(request); + + switch (action) { + + case LUA_DOSTRING: { + const char *script = json_object_get_string(queryJ); + count = LuaDoString(script, source); + if(count) + AFB_ApiError(source->api, "DOSTRING goes wrong err=%d, String=%s ", count, script); + break; + } + + case LUA_DOCALL: { + count = LuaDoCall(queryJ, source); + if(count) + AFB_ApiError(source->api, "DOCALL goes wrong, error = %d, query=%s", count, json_object_get_string(queryJ)); + break; + } + + case LUA_DOSCRIPT: { // Fulup need to fix argument passing + count = LuaDoScript(queryJ, source); + if(count) + AFB_ApiError(source->api, "DOSCRIPT goes wrong error=%d query=%s", count, json_object_get_string(queryJ)); break; } @@ -884,8 +918,8 @@ STATIC void LuaDoAction (LuaDoActionT action, AFB_ReqT request) { goto OnErrorExit; } - // effectively exec LUA code (afb_reply/fail done later from callback) - err=lua_pcall(luaState, count+1, 0, 0); + if(count >= 0) + err=lua_pcall(luaState, count+1, 0, 0); if (err) { AFB_ApiError(source->api, "LUA-DO-EXEC:FAIL query=%s err=%s", json_object_get_string(queryJ), lua_tostring(luaState,-1)); goto OnErrorExit; @@ -1214,7 +1248,7 @@ PUBLIC void LuaL2cNewLib(luaL_Reg *l2cFunc, int count) { luaL_checkversion(luaState); lua_createtable(luaState, 0, count+1); luaL_setfuncs(luaState,l2cFunc,0); - lua_setglobal(luaState, "_lua2c"); + lua_setglobal(luaState, "L2C"); } static const luaL_Reg afbFunction[] = { @@ -1242,7 +1276,8 @@ static const luaL_Reg afbFunction[] = { // Load Lua Interpreter PUBLIC int LuaConfigLoad (AFB_ApiT apiHandle) { - static int luaLoaded=0; + static int luaLoaded=0, err = 0; + //int err = 0; // Lua loads only once if (luaLoaded) return 0; @@ -1271,6 +1306,13 @@ PUBLIC int LuaConfigLoad (AFB_ApiT apiHandle) { TIMER_MAGIC=CtlConfigMagicNew(); #endif + // Load LUA utils functions. + err = LuaDoString(lua_utils, NULL); + if(err) { + AFB_ApiError(apiHandle, "Error loading lua_utils default functions.%s, %d", lua_utils, err); + return -1; + } + return 0; OnErrorExit: @@ -1283,6 +1325,11 @@ PUBLIC int LuaConfigExec (AFB_ApiT apiHandle, const char* prefix) { int err, index; + // create L2C mapping before any LUA script is loaded + if (ctlLua2cFunc && ctlLua2cFunc->l2cCount) { + LuaL2cNewLib (ctlLua2cFunc->l2cFunc, ctlLua2cFunc->l2cCount); + } + // search for default policy config files char fullprefix[CONTROL_MAXPATH_LEN] = ""; if(prefix) @@ -1293,7 +1340,7 @@ PUBLIC int LuaConfigExec (AFB_ApiT apiHandle, const char* prefix) { strncat (fullprefix, "-", strlen("-")); const char *dirList= getenv("CONTROL_LUA_PATH"); - if (!dirList) dirList=CONTROL_LUA_PATH; + //if (!dirList) dirList=CONTROL_LUA_PATH; // special case for no lua even when avaliable if (!strcasecmp ("/dev/null", dirList)) { diff --git a/ctl-lib/ctl-lua.h b/ctl-lib/ctl-lua.h index b3aea54..cf28dcb 100644 --- a/ctl-lib/ctl-lua.h +++ b/ctl-lib/ctl-lua.h @@ -39,11 +39,6 @@ extern "C" { #define CONTROL_LUA_EVENT "luaevt" #endif -// default use same search path for config.json and script.lua -#ifndef CONTROL_LUA_PATH -#define CONTROL_LUA_PATH CONTROL_CONFIG_PATH -#endif - // standard lua include file #ifdef CONTROL_SUPPORT_LUA #include "lua.h" @@ -62,7 +57,8 @@ typedef enum { LUA_DOSCRIPT, } LuaDoActionT; - +extern const char *lua_utils; +PUBLIC int luaLoadScript(const char *luaScriptPath); PUBLIC int LuaConfigLoad (AFB_ApiT apiHandle); PUBLIC int LuaConfigExec(AFB_ApiT apiHandle, const char * prefix); PUBLIC void LuaL2cNewLib(luaL_Reg *l2cFunc, int count); diff --git a/ctl-lib/ctl-plugin.c b/ctl-lib/ctl-plugin.c index 2520d77..3e3e16e 100644 --- a/ctl-lib/ctl-plugin.c +++ b/ctl-lib/ctl-plugin.c @@ -84,77 +84,19 @@ STATIC int DispatchOneL2c(void* luaState, char *funcname, Lua2cFunctionT callbac #endif } -STATIC int PluginLoadOne (AFB_ApiT apiHandle, CtlPluginT *ctlPlugin, json_object *pluginJ, void* handle) { - json_object *lua2csJ = NULL, *pluginPathJ = NULL; - const char*ldSearchPath = NULL, *basename = NULL; - void *dlHandle; +STATIC int PluginLoadCOne(AFB_ApiT apiHandle, const char *pluginpath, json_object *lua2csJ, void * handle, CtlPluginT *ctlPlugin) +{ + void *dlHandle = dlopen(pluginpath, RTLD_NOW); - - // plugin initialises at 1st load further init actions should be place into onload section - if (!pluginJ) return 0; - - int err = wrap_json_unpack(pluginJ, "{ss,s?s,s?s,s?s,s?o !}", - "uid", &ctlPlugin->uid, - "info", &ctlPlugin->info, - "ldpath", &ldSearchPath, - "basename", &basename, - "lua2c", &lua2csJ); - if (err) { - AFB_ApiError(apiHandle, "CTL-PLUGIN-LOADONE Plugin missing uid|[info]|basename|[ldpath]|[lua2c] in:\n-- %s", json_object_get_string(pluginJ)); - goto OnErrorExit; - } - - // default basename equal uid - if (!basename) basename=ctlPlugin->uid; - - // if search path not in Json config file, then try default - if (!ldSearchPath) - { - char path[CONTROL_MAXPATH_LEN]; - memset(path, 0, sizeof(path)); - const char *envpath = getenv("CONTROL_PLUGIN_PATH"); - envpath ? - strncat(path, envpath, strlen(envpath)): - strncat(path, CONTROL_PLUGIN_PATH, strlen(CONTROL_PLUGIN_PATH)); - const char *bPath = GetBindingDirPath(); - strncat(path, ":", strlen(":")); - strncat(path, bPath, strlen(bPath)); - ldSearchPath = path; - } - - // search for default policy config file - pluginPathJ = ScanForConfig(ldSearchPath, CTL_SCAN_RECURSIVE, basename, CTL_PLUGIN_EXT); - if (!pluginPathJ || json_object_array_length(pluginPathJ) == 0) { - AFB_ApiError(apiHandle, "CTL-PLUGIN-LOADONE Missing plugin=%s*%s (config ldpath?) search=\n-- %s", basename, CTL_PLUGIN_EXT, ldSearchPath); - goto OnErrorExit; - } - - char *filename; - char*fullpath; - err = wrap_json_unpack(json_object_array_get_idx(pluginPathJ, 0), "{s:s, s:s !}", "fullpath", &fullpath, "filename", &filename); - if (err) { - AFB_ApiError(apiHandle, "CTL-PLUGIN-LOADONE HOOPs invalid plugin file path=\n-- %s", json_object_get_string(pluginPathJ)); - goto OnErrorExit; - } - - if (json_object_array_length(pluginPathJ) > 1) { - AFB_ApiWarning(apiHandle, "CTL-PLUGIN-LOADONE plugin multiple instances in searchpath will use %s/%s", fullpath, filename); - } - - char pluginpath[CONTROL_MAXPATH_LEN]; - strncpy(pluginpath, fullpath, strlen (fullpath)+1); - strncat(pluginpath, "/", strlen ("/")); - strncat(pluginpath, filename, strlen (filename)); - dlHandle = dlopen(pluginpath, RTLD_NOW); if (!dlHandle) { AFB_ApiError(apiHandle, "CTL-PLUGIN-LOADONE Fail to load pluginpath=%s err= %s", pluginpath, dlerror()); - goto OnErrorExit; + return -1; } CtlPluginMagicT *ctlPluginMagic = (CtlPluginMagicT*) dlsym(dlHandle, "CtlPluginMagic"); if (!ctlPluginMagic || ctlPluginMagic->magic != CTL_PLUGIN_MAGIC) { AFB_ApiError(apiHandle, "CTL-PLUGIN-LOADONE symbol'CtlPluginMagic' missing or != CTL_PLUGIN_MAGIC plugin=%s", pluginpath); - goto OnErrorExit; + return -1; } else { AFB_ApiNotice(apiHandle, "CTL-PLUGIN-LOADONE %s successfully registered", ctlPluginMagic->uid); } @@ -175,7 +117,7 @@ STATIC int PluginLoadOne (AFB_ApiT apiHandle, CtlPluginT *ctlPlugin, json_object #else // Lua2cWrapper is part of binder and not expose to dynamic link if (lua2csJ && lua2cInPlug) { - *lua2cInPlug = DispatchOneL2c; + *lua2cInPlug = (Lua2cWrapperT)DispatchOneL2c; int Lua2cAddOne(luaL_Reg *l2cFunc, const char* l2cName, int index) { if(ctlLua2cFunc->l2cCount) @@ -219,7 +161,7 @@ STATIC int PluginLoadOne (AFB_ApiT apiHandle, CtlPluginT *ctlPlugin, json_object } if (errCount) { AFB_ApiError(apiHandle, "CTL-PLUGIN-LOADONE %d symbols not found in plugin='%s'", errCount, pluginpath); - goto OnErrorExit; + return -1; } int total = ctlLua2cFunc->l2cCount + count; if(ctlLua2cFunc->l2cCount) { @@ -240,21 +182,171 @@ STATIC int PluginLoadOne (AFB_ApiT apiHandle, CtlPluginT *ctlPlugin, json_object ctlPlugin->context = (*ctlPluginOnload) (ctlPlugin, handle); } + return 0; +} + +STATIC int LoadFoundPlugins(AFB_ApiT apiHandle, json_object *scanResult, json_object *lua2csJ, void *handle, CtlPluginT *ctlPlugin) +{ + char pluginpath[CONTROL_MAXPATH_LEN]; + char *filename; + char *fullpath; + char *ext; + int i; + size_t len; + json_object *object = NULL; + + if (!json_object_is_type(scanResult, json_type_array)) + return -1; + + len = json_object_array_length(scanResult); + + for (i = 0; i < len; ++i) { + object = json_object_array_get_idx(scanResult, i); + int err = wrap_json_unpack(object, "{s:s, s:s !}", + "fullpath", &fullpath, + "filename", &filename); + + if (err) { + AFB_ApiError(apiHandle, "HOOPs invalid plugin file path=\n-- %s", json_object_get_string(scanResult)); + return -1; + } + + /* Make sure you don't load two found libraries */ + ext = strrchr(filename, '.'); + strncpy(pluginpath, fullpath, strlen (fullpath)+1); + strncat(pluginpath, "/", strlen ("/")); + strncat(pluginpath, filename, strlen (filename)); + + if(!strcasecmp(ext, CONTROL_PLUGIN_EXT)) { + if(ext && !strcasecmp(ext, CONTROL_PLUGIN_EXT) && i > 0) { + AFB_ApiWarning(apiHandle, "Plugin multiple instances in searchpath will use %s/%s", fullpath, filename); + return 0; + } + PluginLoadCOne(apiHandle, pluginpath, lua2csJ, handle, ctlPlugin); + } + else if(!strcasecmp(ext, CONTROL_SCRIPT_EXT)) { + ctlPlugin->api = apiHandle; + ctlPlugin->context = handle; + luaLoadScript(pluginpath); + } + } + + return 0; +} + +STATIC char *GetDefaultSearchPath() +{ + char *searchPath; + const char *bPath; + const char *envPath; + size_t bPath_len, envPath_len, CTL_PLGN_len; + + bPath = GetBindingDirPath(); + envPath = getenv("CONTROL_PLUGIN_PATH"); + bPath_len = strlen(bPath); + envPath_len = envPath ? strlen(envPath) : 0; + CTL_PLGN_len = envPath_len ? 0 : strlen(CONTROL_PLUGIN_PATH); + + /* Allocating with the size of binding root dir path + environment if found + * + 2 for the NULL terminating character and the additionnal separator + * between bPath and envPath concatenation. + */ + if(envPath) { + searchPath = calloc(1, sizeof(char) *((bPath_len + envPath_len) + 2)); + strncat(searchPath, envPath, envPath_len); + } + else { + searchPath = calloc(1, sizeof(char) * ((bPath_len + CTL_PLGN_len) + 2)); + strncat(searchPath, CONTROL_PLUGIN_PATH, CTL_PLGN_len); + } + + strncat(searchPath, ":", 1); + strncat(searchPath, bPath, bPath_len); + + return searchPath; +} + +STATIC int FindPlugins(AFB_ApiT apiHandle, const char *searchPath, const char *file, json_object **pluginPathJ) +{ + *pluginPathJ = ScanForConfig(searchPath, CTL_SCAN_RECURSIVE, file, NULL); + if (!*pluginPathJ || json_object_array_length(*pluginPathJ) == 0) { + AFB_ApiError(apiHandle, "CTL-PLUGIN-LOADONE Missing plugin=%s* (config ldpath?) search=\n-- %s", file, searchPath); + return -1; + } + + return 0; +} + +STATIC int PluginLoad (AFB_ApiT apiHandle, CtlPluginT *ctlPlugin, json_object *pluginJ, void *handle) +{ + int err = 0, i = 0; + char *searchPath; + const char *sPath = NULL, *file = NULL; + json_object *lua2csJ = NULL, *fileJ = NULL, *pluginPathJ = NULL; + + // plugin initialises at 1st load further init actions should be place into onload section + if (!pluginJ) return 0; + + err = wrap_json_unpack(pluginJ, "{ss,s?s,s?s,s?o,s?o !}", + "uid", &ctlPlugin->uid, + "info", &ctlPlugin->info, + "spath", &sPath, + "file", &fileJ, + "lua2c", &lua2csJ); + if (err) { + AFB_ApiError(apiHandle, "CTL-PLUGIN-LOADONE Plugin missing uid|[info]|file|[ldpath]|[lua2c] in:\n-- %s", json_object_get_string(pluginJ)); + goto OnErrorExit; + } + + // if search path not in Json config file, then try default + if(sPath) + searchPath = strdup(sPath); + else + searchPath = GetDefaultSearchPath(); + + // default file equal uid + if (!fileJ) { + file = ctlPlugin->uid; + if(FindPlugins(apiHandle, searchPath, file, &pluginPathJ)) + goto OnErrorExit; + LoadFoundPlugins(apiHandle, pluginPathJ, lua2csJ, handle, ctlPlugin); + } + else if(json_object_is_type(fileJ, json_type_string)) { + file = json_object_get_string(fileJ); + if(FindPlugins(apiHandle, searchPath, file, &pluginPathJ)) + goto OnErrorExit; + LoadFoundPlugins(apiHandle, pluginPathJ, lua2csJ, handle, ctlPlugin); + } + else if(json_object_is_type(fileJ, json_type_array)) { + for(i = 0; i < json_object_array_length(fileJ);++i) { + file = json_object_get_string(json_object_array_get_idx(fileJ, i)); + if(FindPlugins(apiHandle, searchPath, file, &pluginPathJ)) + goto OnErrorExit; + LoadFoundPlugins(apiHandle, pluginPathJ, lua2csJ, handle, ctlPlugin); + } + } + + if(err) + goto OnErrorExit; + + free(searchPath); json_object_put(pluginPathJ); // No more needs for that json_object. return 0; OnErrorExit: + free(searchPath); json_object_put(pluginPathJ); // No more needs for that json_object. return 1; } PUBLIC int PluginConfig(AFB_ApiT apiHandle, CtlSectionT *section, json_object *pluginsJ) { - int err=0; + int err = 0; + int idx = 0; + size_t length = 0; if (ctlPlugins) { - int idx = 0; while(ctlPlugins[idx].uid != NULL) { // Jose hack to make verbosity visible from sharedlib and @@ -267,15 +359,15 @@ PUBLIC int PluginConfig(AFB_ApiT apiHandle, CtlSectionT *section, json_object *p else { if (json_object_get_type(pluginsJ) == json_type_array) { - size_t length = json_object_array_length(pluginsJ); + length = json_object_array_length(pluginsJ); ctlPlugins = calloc (length+1, sizeof(CtlPluginT)); - for (int idx=0; idx < length; idx++) { + for (idx=0; idx < length; idx++) { json_object *pluginJ = json_object_array_get_idx(pluginsJ, idx); - err += PluginLoadOne(apiHandle, &ctlPlugins[idx], pluginJ, section->handle); + err += PluginLoad(apiHandle, &ctlPlugins[idx], pluginJ, section->handle); } } else { ctlPlugins = calloc (2, sizeof(CtlPluginT)); - err += PluginLoadOne(apiHandle, &ctlPlugins[0], pluginsJ, section->handle); + err += PluginLoad(apiHandle, &ctlPlugins[0], pluginsJ, section->handle); } } diff --git a/ctl-lib/ctl-plugin.h b/ctl-lib/ctl-plugin.h index eaceea5..5d505ac 100644 --- a/ctl-lib/ctl-plugin.h +++ b/ctl-lib/ctl-plugin.h @@ -30,6 +30,9 @@ extern "C" { #include <json-c/json.h> +#define CONTROL_PLUGIN_EXT ".ctlso" +#define CONTROL_SCRIPT_EXT ".lua" + // Waiting for a clean AppFW-V3 API #ifdef USE_API_DYN #define AFB_BINDING_VERSION dyn @@ -212,7 +215,7 @@ typedef struct { } subcall; struct { - const char* load; + const char* plugin; const char* funcname; } lua; |