diff options
Diffstat (limited to 'Controler-afb/ctl-lua.c')
-rw-r--r-- | Controler-afb/ctl-lua.c | 354 |
1 files changed, 284 insertions, 70 deletions
diff --git a/Controler-afb/ctl-lua.c b/Controler-afb/ctl-lua.c index 0e44bc9..59ffbe8 100644 --- a/Controler-afb/ctl-lua.c +++ b/Controler-afb/ctl-lua.c @@ -16,6 +16,7 @@ * ref: * http://www.troubleshooters.com/codecorn/lua/lua_c_calls_lua.htm#_Anatomy_of_a_Lua_Call * http://acamara.es/blog/2012/08/passing-variables-from-lua-5-2-to-c-and-vice-versa/ + * https://john.nachtimwald.com/2014/07/12/wrapping-a-c-library-in-lua/ */ #define _GNU_SOURCE @@ -39,6 +40,55 @@ typedef enum { LUA_DOSCRIPT, } LuaDoActionT; +#define CTX_MAGIC 123456789 +#define CTX_TOKEN "AFB_ctx" + +typedef struct { + int ctxMagic; + afb_req request; + char *info; +} LuaAfbContextT; + +typedef struct { + const char *callback; + const char *handle; +} LuaCallServiceT; + +typedef enum { + AFB_MSG_WARNING, + AFB_MSG_NOTICE, +} LuaAfbMessageT; + +STATIC LuaAfbContextT *LuaCtxCheck (lua_State *luaState, int index) { + LuaAfbContextT *afbContext; + luaL_checktype(luaState, index, LUA_TUSERDATA); + afbContext = (LuaAfbContextT *)luaL_checkudata(luaState, index, CTX_TOKEN); + if (afbContext == NULL && afbContext->ctxMagic != CTX_MAGIC) { + luaL_error(luaState, "Fail to retrieve user data context=%s", CTX_TOKEN); + AFB_ERROR ("afbContextCheck error retrieving afbContext"); + return NULL; + } + return afbContext; +} + +STATIC LuaAfbContextT *LuaCtxPush (lua_State *luaState, afb_req request, const char* info) { + LuaAfbContextT *afbContext = (LuaAfbContextT *)lua_newuserdata(luaState, sizeof(LuaAfbContextT)); + if (!afbContext) { + AFB_ERROR ("LuaCtxPush fail to allocate user data context"); + return NULL; + } + luaL_setmetatable(luaState, CTX_TOKEN); + afbContext->ctxMagic=CTX_MAGIC; + afbContext->info=strdup(info); + afbContext->request= request; + return afbContext; +} + +STATIC void LuaCtxFree (LuaAfbContextT *afbContext) { + + free (afbContext->info); +} + // List Avaliable Configuration Files PUBLIC json_object* ScanForConfig (char* searchPath, char *pre, char *ext) { json_object *responseJ; @@ -80,17 +130,38 @@ PUBLIC json_object* ScanForConfig (char* searchPath, char *pre, char *ext) { return (responseJ); } -STATIC int LuaPrintNotice(lua_State* luaState) { - int count = lua_gettop(luaState); - - for (int idx=1; idx <= count; ++idx) { - const char *str = lua_tostring(luaState, idx); // Get string +STATIC int LuaPrintMessage(LuaAfbMessageT action, lua_State* luaState) { + + int count = lua_gettop(luaState); + for (int idx = 1; idx <= count; ++idx) { + const char *str = lua_tostring(luaState, idx); // Get string + + switch (action) { + case AFB_MSG_NOTICE: + AFB_NOTICE(str); + break; + + case AFB_MSG_WARNING: + AFB_WARNING(str); + break; + + default: + AFB_ERROR(str); + } + } + return 0; // no value return +} - // Output string. - AFB_NOTICE (str); - } +STATIC int LuaPrintWarning(lua_State* luaState) { + + LuaPrintMessage (AFB_MSG_WARNING, luaState); + return 0; // no value return +} - return 0; // no value return +STATIC int LuaPrintNotice(lua_State* luaState) { + + LuaPrintMessage (AFB_MSG_NOTICE, luaState); + return 0; // no value return } STATIC int LuaPushArgument (json_object *arg) { @@ -126,12 +197,185 @@ STATIC int LuaPushArgument (json_object *arg) { return 1; } +static json_object *LuaPopArgs (lua_State* luaState, int count); +STATIC json_object *PopOneArg (lua_State* luaState, int idx); +static json_object *LuaTableToJson (lua_State* luaState, int index) { + + json_object *tableJ= json_object_new_object(); + const char *key; + char number[3]; + lua_pushnil(luaState); // 1st key + for (int jdx=1; lua_next(luaState, index) != 0; jdx++) { + + printf("jdx=%d %s - %s\n", jdx, + lua_typename(luaState, lua_type(luaState, -2)), + lua_typename(luaState, lua_type(luaState, -1))); + + // uses 'key' (at index -2) and 'value' (at index -1) + if (lua_type(luaState,-2) == LUA_TSTRING) key= lua_tostring(luaState, -2); + else { + snprintf(number, sizeof(number),"%d", jdx); + key=number; + } + + json_object_object_add(tableJ, key, PopOneArg(luaState, -1)); + lua_pop(luaState, 1); // removes 'value'; keeps 'key' for next iteration + } + + return tableJ; +} + +STATIC json_object *PopOneArg (lua_State* luaState, int idx) { + json_object *value=NULL; + + switch(lua_type(luaState, idx)) { + case LUA_TNUMBER: + value= json_object_new_double(lua_tonumber(luaState, idx)); + break; + case LUA_TBOOLEAN: + value= json_object_new_boolean(lua_toboolean(luaState, idx)); + break; + case LUA_TSTRING: + value= json_object_new_string(lua_tostring(luaState, idx)); + break; + case LUA_TTABLE: { + AFB_NOTICE ("-++-- idx=%d ", idx); + value= LuaTableToJson(luaState, idx); + break; + } + default: + AFB_NOTICE ("script returned Unknown/Unsupported idx=%d type: %s", idx, lua_typename(luaState, idx)); + value=NULL; + } + if (value) AFB_NOTICE ("---- idx=%d value=%s", idx, json_object_get_string(value)); + + return value; +} + +static json_object *LuaPopArgs (lua_State* luaState, int count) { + + + json_object *responseJ; + + if(count <=0) return NULL; + + // start at 2 because we are using a function array lib + if (count == 2) { + responseJ=PopOneArg (luaState, 2); + } else { + // loop on remaining return arguments + responseJ= json_object_new_array(); + for (int idx=2; idx <= count; idx++) { + json_object_array_add(responseJ, PopOneArg (luaState, idx)); + } + } + + return responseJ; +} + + +STATIC int LuaAfbSuccess(lua_State* luaState) { + + + int count = lua_gettop(luaState); + + + //AFB_NOTICE ("LuaAfbSuccess args=%s",json_object_get_string(LuaPopArgs(luaState, count))); + + LuaAfbContextT *afbContext= LuaCtxCheck(luaState, 1); + if (!afbContext) goto OnErrorExit; + + afb_req_success(afbContext->request, LuaPopArgs(luaState, count), NULL); + + LuaCtxFree(afbContext); + return 0; + + OnErrorExit: + lua_error(luaState); + return 1; +} + +STATIC int LuaAfbFail(lua_State* luaState) { + + LuaAfbContextT *afbContext= LuaCtxCheck(luaState, 1); + if (!afbContext) goto OnErrorExit; + + int count = lua_gettop(luaState); + + afb_req_fail(afbContext->request, afbContext->info, json_object_get_string(LuaPopArgs(luaState, count))); + + LuaCtxFree(afbContext); + return 0; + + OnErrorExit: + lua_error(luaState); + return 1; +} + +STATIC void LuaAfbServiceCB(void *handle, int iserror, struct json_object *responseJ) { + LuaCallServiceT *contextCB= (LuaCallServiceT*)handle; + + // load function (should exist in CONTROL_PATH_LUA + lua_getglobal(luaState, contextCB->callback); + + // push error status & response + lua_pushboolean(luaState, iserror); + (void)LuaPushArgument(responseJ); + + int err=lua_pcall(luaState, 2, LUA_MULTRET, 0); + if (err) { + AFB_ERROR ("LUA-SERICE-CB:FAIL response=%s err=%s", json_object_get_string(responseJ), lua_tostring(luaState,-1) ); + } +} + +STATIC int LuaAfbService(lua_State* luaState) { + int count = lua_gettop(luaState); + + // retrieve userdate context + LuaAfbContextT *afbContext= luaL_checkudata(luaState, 1, "CTX_TOKEN"); + if (!afbContext) { + lua_pushliteral (luaState, "LuaAfbServiceCall-Hoops no CTX_TOKEN"); + goto OnErrorExit; + } + + if (count <5 || !lua_isstring(luaState, 2) || !lua_isstring(luaState, 3) || !lua_istable(luaState, 4) || !lua_isstring(luaState, 5)) { + lua_pushliteral (luaState, "LuaAfbServiceCall-Syntax is AFB_service_call (api, verb, query, callback, handle"); + goto OnErrorExit; + } + + LuaCallServiceT *contextCB = calloc (1, sizeof(LuaCallServiceT)); + + // get api/verb+query + const char *api = lua_tostring(luaState,1); + const char *verb= lua_tostring(luaState,2); + json_object *queryJ= LuaTableToJson(luaState, 3); + if (!queryJ) goto OnErrorExit; + + if (count >= 6) { + if (!lua_istable(luaState, 6)) { + lua_pushliteral (luaState, "LuaAfbServiceCall-Syntax optional handle should be a table"); + goto OnErrorExit; + } + contextCB->handle= lua_tostring(luaState, 5); + } + + afb_service_call(api, verb, queryJ, LuaAfbServiceCB, contextCB); + + return 0; // no value return + + OnErrorExit: + lua_error(luaState); + return 1; + +} + + // Generated some fake event based on watchdog/counter PUBLIC void LuaDoAction (LuaDoActionT action, afb_req request) { int err, count=0; - json_object* queryJ = afb_req_json(request); + json_object* queryJ = afb_req_json(request); switch (action) { @@ -142,6 +386,10 @@ PUBLIC void LuaDoAction (LuaDoActionT action, afb_req request) { AFB_ERROR ("LUA-DO-COMPILE:FAIL String=%s err=%s", script, lua_tostring(luaState,-1) ); goto OnErrorExit; } + // Push AFB client context on the stack + LuaAfbContextT *afbContext= LuaCtxPush(luaState, request, script); + if (!afbContext) goto OnErrorExit; + break; } @@ -154,9 +402,15 @@ PUBLIC void LuaDoAction (LuaDoActionT action, afb_req request) { goto OnErrorExit; } + // Push AFB client context on the stack + LuaAfbContextT *afbContext= LuaCtxPush(luaState, request, func); + if (!afbContext) goto OnErrorExit; + // load function (should exist in CONTROL_PATH_LUA lua_getglobal(luaState, func); - + + + // push arguments on the stack if (json_object_get_type(args) != json_type_array) { count= LuaPushArgument (args); @@ -198,6 +452,9 @@ PUBLIC void LuaDoAction (LuaDoActionT action, afb_req request) { } } + LuaAfbContextT *afbContext= LuaCtxPush(luaState, request, script); + if (!afbContext) goto OnErrorExit; + for (index=0; index < json_object_array_length(luaScriptPathJ); index++) { char *filename; char*dirpath; json_object *entryJ=json_object_array_get_idx(luaScriptPathJ, index); @@ -226,11 +483,7 @@ PUBLIC void LuaDoAction (LuaDoActionT action, afb_req request) { AFB_ERROR ("LUA-DOSCRIPT HOOPs script=%s not in path=%s", script, CONTROL_PATH_LUA); goto OnErrorExit; - } - - //lua_setglobal(luaState, "args"); - count=1; - + } break; } @@ -238,59 +491,15 @@ PUBLIC void LuaDoAction (LuaDoActionT action, afb_req request) { AFB_ERROR ("LUA-DOCALL-ACTION unknown query=%s", json_object_get_string(queryJ)); goto OnErrorExit; } - - // effectively exec LUA code -// err=lua_pcall(luaState, count, LUA_MULTRET, 0); - err=lua_pcall(luaState, count, LUA_MULTRET, 0); + + + // effectively exec LUA code (afb_reply/fail done later from callback) + err=lua_pcall(luaState, count, 0, 0); if (err) { AFB_ERROR ("LUA-DO-EXEC:FAIL query=%s err=%s", json_object_get_string(queryJ), lua_tostring(luaState,-1) ); goto OnErrorExit; } - // number of return values - count=lua_gettop(luaState); - - // Check status return true=OK false=fail - if (count == 0 || !lua_isboolean(luaState, 1)) { - AFB_ERROR ("LUA-DO-RETURN:INVALID 1st return argument not boolean query=%s", json_object_get_string(queryJ) ); - goto OnErrorExit; - } - - // get return fail/ok status - const int returnStatus=lua_toboolean(luaState, 1); - - // loop on remaining return arguments - json_object *returnArgs = json_object_new_array(); - for (int idx=2; idx <= count; idx++) { - - // push on stack return value (Fulup in LUA-5.3 the two functions are combined) - int valueType=lua_type (luaState, idx); - - switch(valueType) { - case LUA_TNUMBER: - json_object_array_add(returnArgs, json_object_new_double(lua_tonumber(luaState, idx))); - break; - case LUA_TBOOLEAN: - json_object_array_add(returnArgs, json_object_new_boolean(lua_toboolean(luaState, idx))); - break; - case LUA_TSTRING: - json_object_array_add(returnArgs, json_object_new_string(lua_tostring(luaState, idx))); - break; - case LUA_TTABLE: - default: - AFB_NOTICE ("script returned Unknown/Unsupported type: "); - } - - } - - // pop stack including Func/String/Script call - lua_pop(luaState, count+1); - - if(returnStatus) - afb_req_success(request, returnArgs, NULL); - else - afb_req_fail(request, "LUA-DOACTION-FAIL", json_object_get_string(returnArgs)); - return; OnErrorExit: @@ -298,21 +507,26 @@ PUBLIC void LuaDoAction (LuaDoActionT action, afb_req request) { return; } -PUBLIC void ctlapi_lua_dostring (afb_req request) { - +PUBLIC void ctlapi_lua_dostring (afb_req request) { LuaDoAction (LUA_DOSTRING, request); } PUBLIC void ctlapi_lua_docall (afb_req request) { - LuaDoAction (LUA_DOCALL, request); } PUBLIC void ctlapi_lua_doscript (afb_req request) { - LuaDoAction (LUA_DOSCRIPT, request); } +static const luaL_Reg afbFunction[] = { + {"notice" , LuaPrintNotice}, + {"warning", LuaPrintWarning}, + {"service", LuaAfbService}, + {"success", LuaAfbSuccess}, + {"fail" , LuaAfbFail}, + {NULL, NULL} /* sentinel */ +}; // Create Binding Event at Init PUBLIC int LuaLibInit () { @@ -332,8 +546,8 @@ PUBLIC int LuaLibInit () { luaL_openlibs(luaState); // redirect print to AFB_NOTICE - lua_register(luaState,"print", LuaPrintNotice ); - //lua_register(lauHandle,"AFB_DEBUG", LuaAFBDebug); + luaL_newlib(luaState, afbFunction); + lua_setglobal(luaState, "AFB"); // load+exec any file found in LUA search path for (index=0; index < json_object_array_length(luaScriptPathJ); index++) { |