diff options
author | fulup <fulup.arfoll@iot.bzh> | 2017-08-08 18:52:13 +0200 |
---|---|---|
committer | fulup <fulup.arfoll@iot.bzh> | 2017-08-08 18:52:13 +0200 |
commit | f817ce67ff6344f181d221c859e1cb2231a3dac4 (patch) | |
tree | ca95512377f7e2105a3a113c31c75abde0dd20ad /Controler-afb/ctl-lua.c | |
parent | 33c2cd0236823d108cbb21af34b8d7843d117ac1 (diff) |
Early Draft of LUA integration
Diffstat (limited to 'Controler-afb/ctl-lua.c')
-rw-r--r-- | Controler-afb/ctl-lua.c | 380 |
1 files changed, 380 insertions, 0 deletions
diff --git a/Controler-afb/ctl-lua.c b/Controler-afb/ctl-lua.c new file mode 100644 index 0000000..0e44bc9 --- /dev/null +++ b/Controler-afb/ctl-lua.c @@ -0,0 +1,380 @@ +/* + * 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: + * 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/ + */ + +#define _GNU_SOURCE +#include <stdio.h> +#include <string.h> +#include <dirent.h> + +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" + +#include "ctl-binding.h" +#include "wrap-json.h" + + +static lua_State* luaState; + +typedef enum { + LUA_DOCALL, + LUA_DOSTRING, + LUA_DOSCRIPT, +} LuaDoActionT; + +// List Avaliable Configuration Files +PUBLIC json_object* ScanForConfig (char* searchPath, char *pre, char *ext) { + json_object *responseJ; + DIR *dirHandle; + char *dirPath; + char* dirList= strdup(searchPath); + size_t extLen = strlen(ext); + + responseJ = json_object_new_array(); + for (dirPath= strtok(dirList, ":"); dirPath && *dirPath; dirPath=strtok(NULL,":")) { + struct dirent *dirEnt; + + dirHandle = opendir (dirPath); + if (!dirHandle) { + AFB_NOTICE ("CONFIG-SCANNING dir=%s not readable", dirPath); + continue; + } + + AFB_NOTICE ("CONFIG-SCANNING:ctl_listconfig scanning: %s", dirPath); + while ((dirEnt = readdir(dirHandle)) != NULL) { + // Unknown type is accepted to support dump filesystems + if (dirEnt->d_type == DT_REG || dirEnt->d_type == DT_UNKNOWN) { + + // check prefix and extention + size_t extIdx=strlen(dirEnt->d_name) - extLen; + if (extIdx <= 0) continue; + if (pre && !strcasestr (dirEnt->d_name, pre)) continue; + if (ext && strcasecmp (ext, &dirEnt->d_name[extIdx])) continue; + + struct json_object *pathJ = json_object_new_object(); + json_object_object_add(pathJ, "dirpath", json_object_new_string(dirPath)); + json_object_object_add(pathJ, "filename", json_object_new_string(dirEnt->d_name)); + json_object_array_add(responseJ, pathJ); + } + } + } + + free (dirList); + 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 + + // Output string. + AFB_NOTICE (str); + } + + return 0; // no value return +} + +STATIC int LuaPushArgument (json_object *arg) { + + switch (json_object_get_type(arg)) { + case json_type_object: + lua_newtable (luaState); + json_object_object_foreach (arg, key, val) { + int done = LuaPushArgument (val); + if (done) { + lua_pushstring(luaState, key); // table.key = val + lua_settable(luaState, -3); + } + } + + break; + case json_type_int: + lua_pushinteger(luaState, json_object_get_int(arg)); + break; + case json_type_string: + lua_pushstring(luaState, json_object_get_string(arg)); + break; + case json_type_boolean: + lua_pushboolean(luaState, json_object_get_boolean(arg)); + break; + case json_type_double: + lua_pushnumber(luaState, json_object_get_double(arg)); + break; + default: + return 0; + } + + 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); + + switch (action) { + + case LUA_DOSTRING: { + const char *script = json_object_get_string(queryJ); + err=luaL_loadstring(luaState, script); + if (err) { + AFB_ERROR ("LUA-DO-COMPILE:FAIL String=%s err=%s", script, lua_tostring(luaState,-1) ); + goto OnErrorExit; + } + break; + } + + case LUA_DOCALL: { + const char *func; + json_object *args; + err= wrap_json_unpack (queryJ, "{s:s, s?o !}", "func", &func,"args", &args); + if (err) { + AFB_ERROR ("LUA-DOCALL-SYNTAX missing func|args args=%s", json_object_get_string(queryJ)); + 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); + } else { + for (int idx=0; idx<json_object_array_length(args); idx++) { + count += LuaPushArgument (json_object_array_get_idx(args, idx)); + if (err) break; + } + } + + break; + } + + case LUA_DOSCRIPT: { + const char *script; + json_object *args; + int index; + + // scan luascript search path once + static json_object *luaScriptPathJ =NULL; + if (!luaScriptPathJ) luaScriptPathJ= ScanForConfig(CONTROL_PATH_LUA , NULL, "lua"); + + err= wrap_json_unpack (queryJ, "{s:s, s?o s?o !}", "script", &script,"args", &args, "arg", &args); + if (err) { + AFB_ERROR ("LUA-DOCALL-SYNTAX missing script|(args,arg) args=%s", json_object_get_string(queryJ)); + goto OnErrorExit; + } + + // push arguments on the stack + if (json_object_get_type(args) != json_type_array) { + lua_createtable(luaState, 1, 0); + count= LuaPushArgument (args); + } else { + int length= json_object_array_length(args); + lua_createtable(luaState, length, 0); + for (int idx=0; idx < length; idx++) { + count += LuaPushArgument (json_object_array_get_idx(args, idx)); + if (err) break; + } + } + + for (index=0; index < json_object_array_length(luaScriptPathJ); index++) { + char *filename; char*dirpath; + json_object *entryJ=json_object_array_get_idx(luaScriptPathJ, index); + + err= wrap_json_unpack (entryJ, "{s:s, s:s !}", "dirpath", &dirpath,"filename", &filename); + if (err) { + AFB_ERROR ("LUA-DOSCRIPT HOOPs invalid LUA script path = %s", json_object_get_string(entryJ)); + goto OnErrorExit; + } + + if (strcmp(filename, script)) continue; + + char filepath[255]; + strncpy(filepath, dirpath, sizeof(filepath)); + strncat(filepath, "/", sizeof(filepath)); + strncat(filepath, filename, sizeof(filepath)); + err= luaL_loadfile(luaState, filepath); + if (err) { + AFB_ERROR ("LUA-DOSCRIPT HOOPs Error in LUA loading scripts=%s err=%s", filepath, lua_tostring(luaState,-1)); + goto OnErrorExit; + } + break; + } + + if (index == json_object_array_length(luaScriptPathJ)) { + AFB_ERROR ("LUA-DOSCRIPT HOOPs script=%s not in path=%s", script, CONTROL_PATH_LUA); + goto OnErrorExit; + + } + + //lua_setglobal(luaState, "args"); + count=1; + + break; + } + + default: + 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); + 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: + afb_req_fail(request,"LUA:ERROR", lua_tostring(luaState,-1)); + return; +} + +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); +} + + +// Create Binding Event at Init +PUBLIC int LuaLibInit () { + int err, index; + + // search for default policy config file + json_object *luaScriptPathJ = ScanForConfig(CONTROL_PATH_LUA , "onload", "lua"); + + // open a new LUA interpretor + luaState = luaL_newstate(); + if (!luaState) { + AFB_ERROR ("LUA_INIT: Fail to open lua interpretor"); + goto OnErrorExit; + } + + // load auxiliary libraries + luaL_openlibs(luaState); + + // redirect print to AFB_NOTICE + lua_register(luaState,"print", LuaPrintNotice ); + //lua_register(lauHandle,"AFB_DEBUG", LuaAFBDebug); + + // load+exec any file found in LUA search path + for (index=0; index < json_object_array_length(luaScriptPathJ); index++) { + json_object *entryJ=json_object_array_get_idx(luaScriptPathJ, index); + + char *filename; char*dirpath; + err= wrap_json_unpack (entryJ, "{s:s, s:s !}", "dirpath", &dirpath,"filename", &filename); + if (err) { + AFB_ERROR ("LUA-INIT HOOPs invalid config file path = %s", json_object_get_string(entryJ)); + goto OnErrorExit; + } + + char filepath[255]; + strncpy(filepath, dirpath, sizeof(filepath)); + strncat(filepath, "/", sizeof(filepath)); + strncat(filepath, filename, sizeof(filepath)); + err= luaL_loadfile(luaState, filepath); + if (err) { + AFB_ERROR ("LUA-LOAD HOOPs Error in LUA loading scripts=%s err=%s", filepath, lua_tostring(luaState,-1)); + goto OnErrorExit; + } + + // exec/compil script + err = lua_pcall(luaState, 0, 0, 0); + if (err) { + AFB_ERROR ("LUA-LOAD HOOPs Error in LUA exec scripts=%s err=%s", filepath, lua_tostring(luaState,-1)); + goto OnErrorExit; + } + + } + + // no policy config found remove control API from binder + if (index == 0) { + AFB_WARNING ("POLICY-INIT:WARNING No Control LUA file in path=[%s]", CONTROL_PATH_LUA); + } + + + AFB_DEBUG ("Audio control-LUA Init Done"); + return 0; + + OnErrorExit: + return -1; +} +
\ No newline at end of file |