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