aboutsummaryrefslogtreecommitdiffstats
path: root/Controler-afb
diff options
context:
space:
mode:
Diffstat (limited to 'Controler-afb')
-rw-r--r--Controler-afb/ctl-lua.c354
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++) {