diff options
author | Romain Forlot <romain.forlot@iot.bzh> | 2018-05-06 14:24:42 +0200 |
---|---|---|
committer | Romain Forlot <romain.forlot@iot.bzh> | 2018-05-09 15:03:13 +0200 |
commit | 2fa16f981f6e5d86ac5938821e0c533786b60fc7 (patch) | |
tree | ab073906b708cfb2fd7811de5e440e27515087da | |
parent | 59bbc828469169b518eea036f63f60b5ac8f6264 (diff) |
Upgrade config schema
Change the way to load LUA scripts. They are now considerate
as Plugin and loads with them.
This imply rework of how to search and find plugins as
well as the way to load LUA.
Also load an harcoded LUA scripts providing LUA helpers
and managing global variables lock unlock mechanism
Change-Id: I64e38aa27278d0cfdca787155db2d0c89953f905
Signed-off-by: Romain Forlot <romain.forlot@iot.bzh>
-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; |