summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRomain Forlot <romain.forlot@iot.bzh>2017-09-10 19:46:02 +0200
committerRomain Forlot <romain.forlot@iot.bzh>2017-12-14 11:00:25 +0100
commit75cfbf3d0206f12422091e6479c508c69445bf4a (patch)
tree200085512507e7ee5adc43b377d34ff5953db8de
parentbdf2d19cf8a3d6005456508ca52dba74cd9c103f (diff)
Controller src integration
Change-Id: I0b18cd55057a784d183a5ba02c332810a34d1fca Signed-off-by: Romain Forlot <romain.forlot@iot.bzh>
m---------afb-utilities0
m---------conf.d/app-templates0
-rw-r--r--conf.d/cmake/config.cmake5
-rw-r--r--conf.d/project/etc/sig-sources.json33
-rw-r--r--controller/CMakeLists.txt44
-rw-r--r--controller/ctl-action.c174
-rw-r--r--controller/ctl-config.c176
-rw-r--r--controller/ctl-config.h113
-rw-r--r--controller/ctl-lua.c1081
-rw-r--r--controller/ctl-lua.h77
-rw-r--r--controller/ctl-onload.c57
-rw-r--r--controller/ctl-plugin.c219
-rw-r--r--controller/ctl-plugin.h74
-rw-r--r--controller/ctl-timer.c101
-rw-r--r--controller/ctl-timer.h42
-rw-r--r--signal-composer-binding/CMakeLists.txt2
-rw-r--r--signal-composer-binding/ctl-dispatch.c705
-rw-r--r--signal-composer-binding/ctl-lua.c64
-rw-r--r--signal-composer-binding/ctl-lua.h61
-rw-r--r--signal-composer-binding/signal-composer-apidef.h76
-rw-r--r--signal-composer-binding/signal-composer-apidef.json5
-rw-r--r--signal-composer-binding/signal-composer-binding.cpp30
-rw-r--r--signal-composer-binding/signal-composer-binding.hpp7
-rw-r--r--signal-composer-binding/signal-conf.cpp16
-rw-r--r--signal-composer-binding/signal-conf.hpp27
-rw-r--r--signal-composer-binding/signal.hpp46
26 files changed, 3073 insertions, 162 deletions
diff --git a/afb-utilities b/afb-utilities
-Subproject f352a03a19ebe8a013a96387ec2cd28b98ca069
+Subproject fdf2c27980f756cf63890576d80eef20b78fe98
diff --git a/conf.d/app-templates b/conf.d/app-templates
-Subproject e9a682f1c694eec242c3cebccc09380f621ade0
+Subproject b05e4422c52fe1e80c9552f2057c8c817c4d2fe
diff --git a/conf.d/cmake/config.cmake b/conf.d/cmake/config.cmake
index d57f225..d2f31ae 100644
--- a/conf.d/cmake/config.cmake
+++ b/conf.d/cmake/config.cmake
@@ -77,7 +77,7 @@ set (PKG_REQUIRED_LIST
# Prefix path where will be installed the files
# Default: /usr/local (need root permission to write in)
# ------------------------------------------------------
-#set(CMAKE_INSTALL_PREFIX $ENV{HOME}/opt)
+set(CMAKE_INSTALL_PREFIX $ENV{HOME}/opt)
# Customize link option
# -----------------------------
@@ -125,6 +125,9 @@ set (PKG_REQUIRED_LIST
# -O2
# CACHE STRING "Compilation flags for RELEASE build type.")
+add_definitions("-DCONTROL_CONFIG_PRE=\"init\"")
+add_definitions("-DCTL_PLUGIN_MAGIC=3286576532")
+
# (BUG!!!) as PKG_CONFIG_PATH does not work [should be an env variable]
# ---------------------------------------------------------------------
set(CMAKE_PREFIX_PATH ${CMAKE_INSTALL_PREFIX}/lib64/pkgconfig ${CMAKE_INSTALL_PREFIX}/lib/pkgconfig)
diff --git a/conf.d/project/etc/sig-sources.json b/conf.d/project/etc/sig-sources.json
index 95de2fa..6051e3e 100644
--- a/conf.d/project/etc/sig-sources.json
+++ b/conf.d/project/etc/sig-sources.json
@@ -10,50 +10,51 @@
{
"api": "low-can",
"info": "Low level binding to handle CAN bus communications",
- "init": {
+ "actions": [{
+ "label": "init",
"function": "c/lua (depend on name) function to initialize binding",
"args": {
"arg": "first argument"
}
- },
- "get_signal": {
+ }, {
+ "label": "getSignal",
"function": "c/lua (depend on name) function to get signals",
"args": {
"arg": "first argument"
}
- }
- },
- {
+ }]
+ }, {
"api": "gps",
"info": "Low level binding which retrieve Satellite positionning values",
- "init": {
+ "actions": [{
+ "label": "init",
"function": "c/lua (depend on name) function to initialize binding",
"args": {
"arg": "first argument"
}
- },
- "get_signal": {
+ }, {
+ "label": "getSignal",
"function": "c/lua (depend on name) function to get signals",
"args": {
"arg": "first argument"
}
- }
- },
- {
+ }]
+ }, {
"api": "mraa",
"info": "Low level binding which retrieve different values from several sensors like gyroscope, accelerometer, etc",
- "init": {
+ "actions": [{
+ "label": "init",
"function": "c/lua (depend on name) function to initialize binding",
"args": {
"arg": "first argument"
}
- },
- "get_signal": {
+ }, {
+ "label": "getSignal",
"function": "c/lua (depend on name) function to get signals",
"args": {
"arg": "first argument"
}
- }
+ }]
}
]
}
diff --git a/controller/CMakeLists.txt b/controller/CMakeLists.txt
new file mode 100644
index 0000000..b3d0c32
--- /dev/null
+++ b/controller/CMakeLists.txt
@@ -0,0 +1,44 @@
+###########################################################################
+# Copyright 2015, 2016, 2017 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.
+###########################################################################
+
+# 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)
+ ADD_COMPILE_OPTIONS(-DCONTROL_SUPPORT_LUA)
+else(CONTROL_SUPPORT_LUA)
+ message(STATUS "Warning: LUA Without Support ")
+endif(CONTROL_SUPPORT_LUA)
+
+
+# Add target to project dependency list
+PROJECT_TARGET_ADD(afb-controller)
+
+ # Define project Targets
+ ADD_LIBRARY(${TARGET_NAME} STATIC ctl-action.c ctl-config.c ctl-onload.c ctl-plugin.c ${CTL_LUA_SOURCE})
+
+ # Library dependencies (include updates automatically)
+ TARGET_LINK_LIBRARIES(${TARGET_NAME}
+ afb-utilities
+ )
+
+ # Define target includes for this target client
+ TARGET_INCLUDE_DIRECTORIES(${TARGET_NAME}
+ PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
+ )
+
diff --git a/controller/ctl-action.c b/controller/ctl-action.c
new file mode 100644
index 0000000..b24269c
--- /dev/null
+++ b/controller/ctl-action.c
@@ -0,0 +1,174 @@
+/*
+ * 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, something express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Reference:
+ * Json load using json_unpack https://jansson.readthedocs.io/en/2.9/apiref.html#parsing-and-validating-values
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+
+#include "ctl-config.h"
+
+
+PUBLIC int ActionExecOne(CtlActionT* action, json_object *queryJ) {
+ int err;
+
+
+ switch (action->type) {
+ case CTL_TYPE_API:
+ {
+ json_object *returnJ;
+
+ // if query is empty increment usage count and pass args
+ if (!queryJ || json_object_get_type(queryJ) != json_type_object) {
+ json_object_get(action->argsJ);
+ queryJ = action->argsJ;
+ } else if (action->argsJ) {
+
+ // Merge queryJ and argsJ before sending request
+ if (json_object_get_type(action->argsJ) == json_type_object) {
+
+ json_object_object_foreach(action->argsJ, key, val) {
+ json_object_object_add(queryJ, key, val);
+ }
+ } else {
+ json_object_object_add(queryJ, "args", action->argsJ);
+ }
+ }
+
+ json_object_object_add(queryJ, "label", json_object_new_string(action->source.label));
+
+ int err = afb_service_call_sync(action->api, action->call, queryJ, &returnJ);
+ if (err) {
+ static const char*format = "ActionExecOne(Api) api=%s verb=%s args=%s";
+ AFB_ERROR(format, action->api, action->call, action->source.label);
+ goto OnErrorExit;
+ }
+ break;
+ }
+
+#ifdef CONTROL_SUPPORT_LUA
+ case CTL_TYPE_LUA:
+ err = LuaCallFunc(action, queryJ);
+ if (err) {
+ AFB_ERROR("ActionExecOne(Lua) label=%s func=%s args=%s", action->source.label, action->call, json_object_get_string(action->argsJ));
+ goto OnErrorExit;
+ }
+ break;
+#endif
+
+ case CTL_TYPE_CB:
+ err = (*action->actionCB) (&action->source, action->argsJ, queryJ);
+ if (err) {
+ AFB_ERROR("ActionExecOne(Callback) label%s func=%s args=%s", action->source.label, action->call, json_object_get_string(action->argsJ));
+ goto OnErrorExit;
+ }
+ break;
+
+ default:
+ {
+ AFB_ERROR("ActionExecOne(unknown) API type label=%s", action->source.label);
+ goto OnErrorExit;
+ }
+ }
+
+ return 0;
+
+OnErrorExit:
+ return -1;
+}
+
+
+// unpack individual action object
+
+PUBLIC int ActionLoadOne(CtlActionT *action, json_object *actionJ) {
+ char *api = NULL, *verb = NULL, *lua = NULL;
+ int err, modeCount = 0;
+ json_object *callbackJ=NULL;
+
+ err = wrap_json_unpack(actionJ, "{ss,s?s,s?o,s?s,s?s,s?s,s?o !}"
+ , "label", &action->source.label, "info", &action->source.info, "callback", &callbackJ, "lua", &lua, "api", &api, "verb", &verb, "args", &action->argsJ);
+ if (err) {
+ AFB_ERROR("ACTION-LOAD-ONE Missing something label|info|callback|lua|(api+verb)|args in:\n-- %s", json_object_get_string(actionJ));
+ goto OnErrorExit;
+ }
+
+ if (lua) {
+ action->type = CTL_TYPE_LUA;
+ action->call = lua;
+ modeCount++;
+ }
+
+ if (api && verb) {
+ action->type = CTL_TYPE_API;
+ action->api = api;
+ action->call = verb;
+ modeCount++;
+ }
+
+ if (callbackJ) {
+ action->type = CTL_TYPE_CB;
+ modeCount++;
+ err = PluginGetCB (action, callbackJ);
+ if (err) goto OnErrorExit;
+
+ }
+
+ // make sure at least one mode is selected
+ if (modeCount == 0) {
+ AFB_ERROR("ACTION-LOAD-ONE No Action Selected lua|callback|(api+verb) in %s", json_object_get_string(actionJ));
+ goto OnErrorExit;
+ }
+
+ if (modeCount > 1) {
+ AFB_ERROR("ACTION-LOAD-ONE:ToMany arguments lua|callback|(api+verb) in %s", json_object_get_string(actionJ));
+ goto OnErrorExit;
+ }
+ return 0;
+
+OnErrorExit:
+ return 1;
+};
+
+PUBLIC CtlActionT *ActionLoad(json_object *actionsJ) {
+ int err;
+ CtlActionT *actions;
+
+ // action array is close with a nullvalue;
+ if (json_object_get_type(actionsJ) == json_type_array) {
+ int count = json_object_array_length(actionsJ);
+ actions = calloc(count + 1, sizeof (CtlActionT));
+
+ for (int idx = 0; idx < count; idx++) {
+ json_object *actionJ = json_object_array_get_idx(actionsJ, idx);
+ err = ActionLoadOne(&actions[idx], actionJ);
+ if (err) goto OnErrorExit;
+ }
+
+ } else {
+ actions = calloc(2, sizeof (CtlActionT));
+ err = ActionLoadOne(&actions[0], actionsJ);
+ if (err) goto OnErrorExit;
+ }
+
+ return actions;
+
+OnErrorExit:
+ return NULL;
+
+}
diff --git a/controller/ctl-config.c b/controller/ctl-config.c
new file mode 100644
index 0000000..2e3fc40
--- /dev/null
+++ b/controller/ctl-config.c
@@ -0,0 +1,176 @@
+/*
+ * 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, something express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Reference:
+ * Json load using json_unpack https://jansson.readthedocs.io/en/2.9/apiref.html#parsing-and-validating-values
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+
+#include "filescan-utils.h"
+#include "ctl-config.h"
+
+
+// Load control config file
+
+PUBLIC char* CtlConfigSearch(const char *dirList) {
+ int index, err;
+ char controlFile [CONTROL_MAXPATH_LEN];
+
+ strncpy(controlFile, CONTROL_CONFIG_PRE "-", CONTROL_MAXPATH_LEN);
+ strncat(controlFile, GetBinderName(), CONTROL_MAXPATH_LEN);
+
+ // search for default dispatch config file
+ json_object* responseJ = ScanForConfig(dirList, CTL_SCAN_RECURSIVE, controlFile, ".json");
+
+ // We load 1st file others are just warnings
+ for (index = 0; index < json_object_array_length(responseJ); index++) {
+ json_object *entryJ = json_object_array_get_idx(responseJ, index);
+
+ char *filename;
+ char*fullpath;
+ err = wrap_json_unpack(entryJ, "{s:s, s:s !}", "fullpath", &fullpath, "filename", &filename);
+ if (err) {
+ AFB_ERROR("CTL-INIT HOOPs invalid JSON entry= %s", json_object_get_string(entryJ));
+ return NULL;
+ }
+
+ if (index == 0) {
+ if (strcasestr(filename, controlFile)) {
+ char filepath[CONTROL_MAXPATH_LEN];
+ strncpy(filepath, fullpath, sizeof (filepath));
+ strncat(filepath, "/", sizeof (filepath));
+ strncat(filepath, filename, sizeof (filepath));
+ return (strdup(filepath));
+ }
+ }
+ }
+
+ // no config found
+ return NULL;
+}
+
+PUBLIC int CtlConfigExec(CtlConfigT *ctlConfig) {
+ // best effort to initialise everything before starting
+ if (ctlConfig->requireJ) {
+
+ void DispatchRequireOneApi(json_object * bindindJ) {
+ const char* requireBinding = json_object_get_string(bindindJ);
+ int err = afb_daemon_require_api(requireBinding, 1);
+ if (err) {
+ AFB_WARNING("CTL-LOAD-CONFIG:REQUIRE Fail to get=%s", requireBinding);
+ }
+ }
+
+ if (json_object_get_type(ctlConfig->requireJ) == json_type_array) {
+ for (int idx = 0; idx < json_object_array_length(ctlConfig->requireJ); idx++) {
+ DispatchRequireOneApi(json_object_array_get_idx(ctlConfig->requireJ, idx));
+ }
+ } else {
+ DispatchRequireOneApi(ctlConfig->requireJ);
+ }
+ }
+
+#ifdef CONTROL_SUPPORT_LUA
+ int err= LuaConfigExec();
+ 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++) {
+ errcount += ctlConfig->sections[idx].loadCB(&ctlConfig->sections[idx], NULL);
+ }
+ return errcount;
+
+OnErrorExit:
+ return 1;
+}
+
+PUBLIC CtlConfigT *CtlConfigLoad(const char* filepath, CtlSectionT *sections) {
+ json_object *ctlConfigJ;
+ CtlConfigT *ctlConfig = calloc(1, sizeof (CtlConfigT));
+ int err;
+
+#ifdef CONTROL_SUPPORT_LUA
+ err= LuaConfigLoad();
+ if (err) goto OnErrorExit;
+#endif
+
+ // Search for config in filepath
+ filepath = CtlConfigSearch(filepath);
+
+ if (!filepath) {
+ AFB_ERROR("CTL-LOAD-CONFIG No JSON Config found invalid JSON %s ", filepath);
+ goto OnErrorExit;
+ }
+
+ // Load JSON file
+ ctlConfigJ = json_object_from_file(filepath);
+ if (!ctlConfigJ) {
+ AFB_ERROR("CTL-LOAD-CONFIG Not invalid JSON %s ", filepath);
+ goto OnErrorExit;
+ }
+
+ AFB_INFO("CTL-LOAD-CONFIG: loading config filepath=%s", filepath);
+
+ json_object *metadataJ;
+ int done = json_object_object_get_ex(ctlConfigJ, "metadata", &metadataJ);
+ if (done) {
+ CtlConfigT *ctlConfig = calloc(1, sizeof (CtlConfigT));
+ err = wrap_json_unpack(metadataJ, "{ss,ss,ss,s?s,s?o !}", "label", &ctlConfig->label, "version", &ctlConfig->version
+ , "api", &ctlConfig->api, "info", &ctlConfig->info, "require", &ctlConfig->requireJ);
+ if (err) {
+ AFB_ERROR("CTL-LOAD-CONFIG:METADATA Missing something label|api|version|[info]|[require] in:\n-- %s", json_object_get_string(metadataJ));
+ goto OnErrorExit;
+ }
+
+ // Should replace this with API name change
+ if (ctlConfig->api) {
+ err = afb_daemon_rename_api(ctlConfig->api);
+ if (err) AFB_WARNING("Fail to rename api to:%s", ctlConfig->api);
+ }
+
+ }
+
+ //load config sections
+ err = 0;
+ ctlConfig->sections = sections;
+ for (int idx = 0; sections[idx].key != NULL; idx++) {
+ json_object * sectionJ;
+ int done = json_object_object_get_ex(ctlConfigJ, sections[idx].key, &sectionJ);
+ if (!done) {
+ AFB_ERROR("CtlConfigLoad: fail to find '%s' section in config '%s'", sections[idx].key, filepath);
+ err++;
+ } else {
+ err += sections[idx].loadCB(&sections[idx], sectionJ);
+ }
+
+ }
+ if (err) goto OnErrorExit;
+
+ return (ctlConfig);
+
+OnErrorExit:
+ free(ctlConfig);
+ return NULL;
+}
+
+
+
+
diff --git a/controller/ctl-config.h b/controller/ctl-config.h
new file mode 100644
index 0000000..2e3a16a
--- /dev/null
+++ b/controller/ctl-config.h
@@ -0,0 +1,113 @@
+/*
+ * 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, something express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Reference:
+ * Json load using json_unpack https://jansson.readthedocs.io/en/2.9/apiref.html#parsing-and-validating-values
+ */
+
+#ifndef _CTL_CONFIG_INCLUDE_
+#define _CTL_CONFIG_INCLUDE_
+
+#define _GNU_SOURCE
+#define AFB_BINDING_VERSION 2
+#include <afb/afb-binding.h>
+#include <json-c/json.h>
+#include <filescan-utils.h>
+#include <wrap-json.h>
+
+#include "ctl-plugin.h"
+
+#ifndef CONTROL_MAXPATH_LEN
+ #define CONTROL_MAXPATH_LEN 255
+#endif
+
+#ifndef CONTROL_CONFIG_PRE
+ #define CONTROL_CONFIG_PRE "onload"
+#endif
+
+#ifndef CTL_PLUGIN_EXT
+ #define CTL_PLUGIN_EXT ".ctlso"
+#endif
+
+
+
+typedef enum {
+ CTL_TYPE_NONE=0,
+ CTL_TYPE_API,
+ CTL_TYPE_CB,
+ CTL_TYPE_LUA,
+} CtlActionTypeT;
+
+
+typedef struct {
+ CtlActionTypeT type;
+ const char* api;
+ const char* call;
+ json_object *argsJ;
+ int (*actionCB)(CtlSourceT *source, json_object *argsJ, json_object *queryJ);
+ CtlSourceT source;
+} CtlActionT;
+
+typedef struct {
+ const char* label;
+ const char *info;
+ CtlActionT *actions;
+} DispatchHandleT;
+
+typedef struct ConfigSectionS {
+ const char *key;
+ const char *label;
+ const char *info;
+ int (*loadCB)(struct ConfigSectionS *section, json_object *sectionJ);
+ void *handle;
+} CtlSectionT;
+
+typedef struct {
+ const char* api;
+ const char* label;
+ const char *info;
+ const char *version;
+ json_object *requireJ;
+ CtlSectionT *sections;
+} CtlConfigT;
+
+
+#ifdef CONTROL_SUPPORT_LUA
+ #include "ctl-lua.h"
+#else
+ typedef void* Lua2cWrapperT;
+#endif
+
+
+// ctl-action.c
+PUBLIC CtlActionT *ActionLoad(json_object *actionsJ);
+PUBLIC int ActionExecOne(CtlActionT* action, json_object *queryJ);
+PUBLIC int ActionLoadOne(CtlActionT *action, json_object *actionJ);
+
+// ctl-config.c
+PUBLIC CtlConfigT *CtlConfigLoad(const char* filepath, CtlSectionT *sections);
+PUBLIC int CtlConfigExec(CtlConfigT *ctlConfig);
+
+// ctl-onload.c
+PUBLIC int OnloadConfig(CtlSectionT *section, json_object *actionsJ);
+
+
+// ctl-plugin.c
+PUBLIC int PluginConfig(CtlSectionT *section, json_object *pluginsJ);
+PUBLIC int PluginGetCB (CtlActionT *action , json_object *callbackJ);
+
+
+#endif /* _CTL_CONFIG_INCLUDE_ */ \ No newline at end of file
diff --git a/controller/ctl-lua.c b/controller/ctl-lua.c
new file mode 100644
index 0000000..7b68fe2
--- /dev/null
+++ b/controller/ctl-lua.c
@@ -0,0 +1,1081 @@
+/*
+ * 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
+ *
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+
+#include "ctl-config.h"
+
+#define LUA_FIST_ARG 2 // when using luaL_newlib calllback receive libtable as 1st arg
+#define LUA_MSG_MAX_LENGTH 512
+#define JSON_ERROR (json_object*)-1
+static afb_req NULL_AFBREQ = {};
+
+
+static lua_State* luaState;
+
+#define CTX_MAGIC 123456789
+#define CTX_TOKEN "AFB_ctx"
+
+typedef struct {
+ char *name;
+ int count;
+ afb_event event;
+} LuaAfbEvent;
+static LuaAfbEvent *luaDefaultEvt;
+
+typedef struct {
+ int ctxMagic;
+ afb_req request;
+ void *handle;
+ char *info;
+} LuaAfbContextT;
+
+typedef struct {
+ const char *callback;
+ json_object *context;
+ void *handle;
+} LuaCallServiceT;
+
+typedef enum {
+ AFB_MSG_INFO,
+ AFB_MSG_WARNING,
+ AFB_MSG_NOTICE,
+ AFB_MSG_DEBUG,
+ AFB_MSG_ERROR,
+} LuaAfbMessageT;
+
+/*
+ * Note(Fulup): I fail to use luaL_setmetatable and replaced it with a simple opaque
+ * handle while waiting for someone smarter than me to find a better solution
+ * https://stackoverflow.com/questions/45596493/lua-using-lua-newuserdata-from-lua-pcall
+ */
+
+STATIC LuaAfbContextT *LuaCtxCheck (lua_State *luaState, int index) {
+ LuaAfbContextT *afbContext;
+ //luaL_checktype(luaState, index, LUA_TUSERDATA);
+ //afbContext = (LuaAfbContextT *)luaL_checkudata(luaState, index, CTX_TOKEN);
+ luaL_checktype(luaState, index, LUA_TLIGHTUSERDATA);
+ afbContext = (LuaAfbContextT *) lua_touserdata(luaState, index);
+ 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, void *handle, const char* info) {
+ // LuaAfbContextT *afbContext = (LuaAfbContextT *)lua_newuserdata(luaState, sizeof(LuaAfbContextT));
+ // luaL_setmetatable(luaState, CTX_TOKEN);
+ LuaAfbContextT *afbContext = (LuaAfbContextT *)calloc(1, sizeof(LuaAfbContextT));
+ lua_pushlightuserdata(luaState, afbContext);
+ if (!afbContext) {
+ AFB_ERROR ("LuaCtxPush fail to allocate user data context");
+ return NULL;
+ }
+ afbContext->ctxMagic=CTX_MAGIC;
+ afbContext->info=strdup(info);
+ afbContext->request= request;
+ afbContext->handle= handle;
+ return afbContext;
+}
+
+STATIC void LuaCtxFree (LuaAfbContextT *afbContext) {
+ free(afbContext->info);
+ free(afbContext);
+}
+
+// Push a json structure on the stack as a LUA table
+STATIC int LuaPushArgument (json_object *argsJ) {
+
+ //AFB_NOTICE("LuaPushArgument argsJ=%s", json_object_get_string(argsJ));
+
+ json_type jtype= json_object_get_type(argsJ);
+ switch (jtype) {
+ case json_type_object: {
+ lua_newtable (luaState);
+ json_object_object_foreach (argsJ, key, val) {
+ int done = LuaPushArgument (val);
+ if (done) {
+ lua_setfield(luaState,-2, key);
+ }
+ }
+ break;
+ }
+ case json_type_array: {
+ int length= json_object_array_length(argsJ);
+ lua_newtable (luaState);
+ for (int idx=0; idx < length; idx ++) {
+ json_object *val=json_object_array_get_idx(argsJ, idx);
+ LuaPushArgument (val);
+ lua_seti (luaState,-2, idx);
+ }
+ break;
+ }
+ case json_type_int:
+ lua_pushinteger(luaState, json_object_get_int(argsJ));
+ break;
+ case json_type_string:
+ lua_pushstring(luaState, json_object_get_string(argsJ));
+ break;
+ case json_type_boolean:
+ lua_pushboolean(luaState, json_object_get_boolean(argsJ));
+ break;
+ case json_type_double:
+ lua_pushnumber(luaState, json_object_get_double(argsJ));
+ break;
+ case json_type_null:
+ AFB_WARNING("LuaPushArgument: NULL object type %s", json_object_get_string(argsJ));
+ return 0;
+ break;
+
+ default:
+ AFB_ERROR("LuaPushArgument: unsupported Json object type %s", json_object_get_string(argsJ));
+ return 0;
+ }
+ return 1;
+}
+
+STATIC json_object *LuaPopOneArg (lua_State* luaState, int idx);
+
+// Move a table from Internal Lua representation to Json one
+// Numeric table are transformed in json array, string one in object
+// Mix numeric/string key are not supported
+STATIC json_object *LuaTableToJson (lua_State* luaState, int index) {
+ #define LUA_KEY_INDEX -2
+ #define LUA_VALUE_INDEX -1
+
+ int idx;
+ int tableType;
+ json_object *tableJ= NULL;
+
+ lua_pushnil(luaState); // 1st key
+ if (index < 0) index--;
+ for (idx=1; lua_next(luaState, index) != 0; idx++) {
+
+ // uses 'key' (at index -2) and 'value' (at index -1)
+ if (lua_type(luaState,LUA_KEY_INDEX) == LUA_TSTRING) {
+
+ if (!tableJ) {
+ tableJ= json_object_new_object();
+ tableType=LUA_TSTRING;
+ } else if (tableType != LUA_TSTRING){
+ AFB_ERROR("MIX Lua Table with key string/numeric not supported");
+ return NULL;
+ }
+
+ const char *key= lua_tostring(luaState, LUA_KEY_INDEX);
+ json_object *argJ= LuaPopOneArg(luaState, LUA_VALUE_INDEX);
+ json_object_object_add(tableJ, key, argJ);
+
+ } else {
+ if (!tableJ) {
+ tableJ= json_object_new_array();
+ tableType=LUA_TNUMBER;
+ } else if(tableType != LUA_TNUMBER) {
+ AFB_ERROR("MIX Lua Table with key numeric/string not supported");
+ return NULL;
+ }
+
+ json_object *argJ= LuaPopOneArg(luaState, LUA_VALUE_INDEX);
+ json_object_array_add(tableJ, argJ);
+ }
+
+
+ lua_pop(luaState, 1); // removes 'value'; keeps 'key' for next iteration
+ }
+
+ // Query is empty free empty json object
+ if (idx == 1) {
+ json_object_put(tableJ);
+ return NULL;
+ }
+ return tableJ;
+}
+
+STATIC json_object *LuaPopOneArg (lua_State* luaState, int idx) {
+ json_object *value=NULL;
+
+ int luaType = lua_type(luaState, idx);
+ switch(luaType) {
+ case LUA_TNUMBER: {
+ lua_Number number= lua_tonumber(luaState, idx);;
+ int nombre = (int)number; // evil trick to determine wether n fits in an integer. (stolen from ltcl.c)
+ if (number == nombre) {
+ value= json_object_new_int((int)number);
+ } else {
+ value= json_object_new_double(number);
+ }
+ 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:
+ value= LuaTableToJson(luaState, idx);
+ break;
+ case LUA_TNIL:
+ value=json_object_new_string("nil") ;
+ break;
+ case LUA_TUSERDATA:
+ value=json_object_new_int64((int64_t)lua_touserdata(luaState, idx)); // store userdata as int !!!
+ break;
+
+ default:
+ AFB_NOTICE ("LuaPopOneArg: script returned Unknown/Unsupported idx=%d type:%d/%s", idx, luaType, lua_typename(luaState, luaType));
+ value=NULL;
+ }
+
+ return value;
+}
+
+static json_object *LuaPopArgs (lua_State* luaState, int start) {
+ json_object *responseJ;
+
+ int stop = lua_gettop(luaState);
+ if(stop-start <0) return NULL;
+
+ // start at 2 because we are using a function array lib
+ if (start == stop) {
+ responseJ=LuaPopOneArg (luaState, start);
+ } else {
+ // loop on remaining return arguments
+ responseJ= json_object_new_array();
+ for (int idx=start; idx <= stop; idx++) {
+ json_object *argJ=LuaPopOneArg (luaState, idx);
+ if (!argJ) goto OnErrorExit;
+ json_object_array_add(responseJ, argJ);
+ }
+ }
+
+ return responseJ;
+
+ OnErrorExit:
+ return NULL;
+}
+
+
+STATIC int LuaFormatMessage(lua_State* luaState, LuaAfbMessageT action) {
+ char *message;
+
+ json_object *responseJ= LuaPopArgs(luaState, LUA_FIST_ARG);
+
+ if (!responseJ) {
+ luaL_error(luaState,"LuaFormatMessage empty message");
+ goto OnErrorExit;
+ }
+
+ // if we have only on argument just return the value.
+ if (json_object_get_type(responseJ)!=json_type_array || json_object_array_length(responseJ) <2) {
+ message= (char*)json_object_get_string(responseJ);
+ goto PrintMessage;
+ }
+
+ // extract format and push all other parameter on the stack
+ message = alloca (LUA_MSG_MAX_LENGTH);
+ const char *format = json_object_get_string(json_object_array_get_idx(responseJ, 0));
+
+ int arrayIdx=1;
+ int targetIdx=0;
+
+ for (int idx=0; format[idx] !='\0'; idx++) {
+
+ if (format[idx]=='%' && format[idx] !='\0') {
+ json_object *slotJ= json_object_array_get_idx(responseJ, arrayIdx);
+ //if (slotJ) AFB_NOTICE("**** idx=%d slotJ=%s", arrayIdx, json_object_get_string(slotJ));
+
+
+ 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");
+ 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");
+ arrayIdx++;
+ break;
+
+ case'%':
+ message[targetIdx]='%';
+ targetIdx++;
+ 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");
+ arrayIdx++;
+ }
+
+ } else {
+ if (targetIdx >= LUA_MSG_MAX_LENGTH) {
+ AFB_WARNING ("LuaFormatMessage: message[%s] owerverflow LUA_MSG_MAX_LENGTH=%d", format, LUA_MSG_MAX_LENGTH);
+ targetIdx --; // move backward for EOL
+ break;
+ } else {
+ message[targetIdx++] = format[idx];
+ }
+ }
+ }
+ message[targetIdx]='\0';
+
+PrintMessage:
+ switch (action) {
+ case AFB_MSG_WARNING:
+ AFB_WARNING ("%s", message);
+ break;
+ case AFB_MSG_NOTICE:
+ AFB_NOTICE ("%s", message);
+ break;
+ case AFB_MSG_DEBUG:
+ AFB_DEBUG ("%s", message);
+ break;
+ case AFB_MSG_INFO:
+ AFB_INFO ("%s", message);
+ break;
+ case AFB_MSG_ERROR:
+ default:
+ AFB_ERROR ("%s", message);
+ }
+ return 0; // nothing return to lua
+
+ OnErrorExit: // on argument to return (the error message)
+ return 1;
+}
+
+STATIC int LuaPrintInfo(lua_State* luaState) {
+ int err=LuaFormatMessage (luaState, AFB_MSG_INFO);
+ return err;
+}
+
+STATIC int LuaPrintError(lua_State* luaState) {
+ int err=LuaFormatMessage (luaState, AFB_MSG_ERROR);
+ return err; // no value return
+}
+
+STATIC int LuaPrintWarning(lua_State* luaState) {
+ int err=LuaFormatMessage (luaState, AFB_MSG_WARNING);
+ return err;
+}
+
+STATIC int LuaPrintNotice(lua_State* luaState) {
+ int err=LuaFormatMessage (luaState, AFB_MSG_NOTICE);
+ return err;
+}
+
+STATIC int LuaPrintDebug(lua_State* luaState) {
+ int err=LuaFormatMessage (luaState, AFB_MSG_DEBUG);
+ return err;
+}
+
+STATIC int LuaAfbSuccess(lua_State* luaState) {
+ LuaAfbContextT *afbContext= LuaCtxCheck(luaState, LUA_FIST_ARG);
+ if (!afbContext) goto OnErrorExit;
+
+ // ignore context argument
+ json_object *responseJ= LuaPopArgs(luaState, LUA_FIST_ARG+1);
+ if (responseJ == JSON_ERROR) return 1;
+
+ afb_req_success(afbContext->request, responseJ, NULL);
+
+ LuaCtxFree(afbContext);
+ return 0;
+
+ OnErrorExit:
+ lua_error(luaState);
+ return 1;
+}
+
+STATIC int LuaAfbFail(lua_State* luaState) {
+ LuaAfbContextT *afbContext= LuaCtxCheck(luaState, LUA_FIST_ARG);
+ if (!afbContext) goto OnErrorExit;
+
+ json_object *responseJ= LuaPopArgs(luaState, LUA_FIST_ARG+1);
+ if (responseJ == JSON_ERROR) return 1;
+
+ afb_req_fail(afbContext->request, afbContext->info, json_object_get_string(responseJ));
+
+ 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;
+ int count=1;
+
+ lua_getglobal(luaState, contextCB->callback);
+
+ // push error status & response
+ lua_pushboolean(luaState, iserror);
+ count+= LuaPushArgument(responseJ);
+ count+= LuaPushArgument(contextCB->context);
+
+ int err=lua_pcall(luaState, count, LUA_MULTRET, 0);
+ if (err) {
+ AFB_ERROR ("LUA-SERICE-CB:FAIL response=%s err=%s", json_object_get_string(responseJ), lua_tostring(luaState,-1) );
+ }
+
+ free (contextCB);
+}
+
+
+STATIC int LuaAfbService(lua_State* luaState) {
+ int count = lua_gettop(luaState);
+
+ // note: argument start at 2 because of AFB: table
+ if (count <5 || !lua_isstring(luaState, 2) || !lua_isstring(luaState, 3) || !lua_istable(luaState, 4) || !lua_isstring(luaState, 5)) {
+ lua_pushliteral (luaState, "ERROR: syntax AFB:service(api, verb, {[Lua Table]})");
+ goto OnErrorExit;
+ }
+
+ // get api/verb+query
+ const char *api = lua_tostring(luaState,2);
+ const char *verb= lua_tostring(luaState,3);
+ json_object *queryJ= LuaTableToJson(luaState, 4);
+ if (queryJ == JSON_ERROR) return 1;
+
+ LuaCallServiceT *contextCB = calloc (1, sizeof(LuaCallServiceT));
+ contextCB->callback= lua_tostring(luaState, 5);
+ contextCB->context = LuaPopArgs(luaState, 6);
+
+ afb_service_call(api, verb, queryJ, LuaAfbServiceCB, contextCB);
+
+ return 0; // no value return
+
+ OnErrorExit:
+ lua_error(luaState);
+ return 1;
+}
+
+STATIC int LuaAfbServiceSync(lua_State* luaState) {
+ int count = lua_gettop(luaState);
+ json_object *responseJ;
+
+ // note: argument start at 2 because of AFB: table
+ if (count <3 || !lua_isstring(luaState, 2) || !lua_isstring(luaState, 3) || !lua_istable(luaState, 4)) {
+ lua_pushliteral (luaState, "ERROR: syntax AFB:servsync(api, verb, {[Lua Table]})");
+ goto OnErrorExit;
+ }
+
+ // get api/verb+query
+ const char *api = lua_tostring(luaState,2);
+ const char *verb= lua_tostring(luaState,3);
+ json_object *queryJ= LuaTableToJson(luaState, 4);
+
+ int iserror=afb_service_call_sync (api, verb, queryJ, &responseJ);
+
+ // push error status & response
+ count=1; lua_pushboolean(luaState, iserror);
+ count+= LuaPushArgument(responseJ);
+
+ return count; // return count values
+
+ OnErrorExit:
+ lua_error(luaState);
+ return 1;
+}
+
+STATIC int LuaAfbEventPush(lua_State* luaState) {
+ LuaAfbEvent *afbevt;
+ int index;
+
+ // if no private event handle then use default binding event
+ if (lua_islightuserdata(luaState, LUA_FIST_ARG)) {
+ afbevt = (LuaAfbEvent*) lua_touserdata(luaState, LUA_FIST_ARG);
+ index=LUA_FIST_ARG+1;
+ } else {
+ index=LUA_FIST_ARG;
+ afbevt=luaDefaultEvt;
+ }
+
+ if (!afb_event_is_valid(afbevt->event)) {
+ lua_pushliteral (luaState, "LuaAfbMakePush-Fail invalid event");
+ goto OnErrorExit;
+ }
+
+ json_object *ctlEventJ= LuaTableToJson(luaState, index);
+ if (!ctlEventJ) {
+ lua_pushliteral (luaState, "LuaAfbEventPush-Syntax is AFB:signal ([evtHandle], {lua table})");
+ goto OnErrorExit;
+ }
+
+ int done = afb_event_push(afbevt->event, ctlEventJ);
+ if (!done) {
+ lua_pushliteral (luaState, "LuaAfbEventPush-Fail No Subscriber to event");
+ AFB_ERROR ("LuaAfbEventPush-Fail name subscriber event=%s count=%d", afbevt->name, afbevt->count);
+ goto OnErrorExit;
+ }
+ afbevt->count++;
+ return 0;
+
+ OnErrorExit:
+ lua_error(luaState);
+ return 1;
+}
+
+STATIC int LuaAfbEventSubscribe(lua_State* luaState) {
+ LuaAfbEvent *afbevt;
+
+ LuaAfbContextT *afbContext= LuaCtxCheck(luaState, LUA_FIST_ARG);
+ if (!afbContext) {
+ lua_pushliteral (luaState, "LuaAfbEventSubscribe-Fail Invalid request handle");
+ goto OnErrorExit;
+ }
+
+ // if no private event handle then use default binding event
+ if (lua_islightuserdata(luaState, LUA_FIST_ARG+1)) {
+ afbevt = (LuaAfbEvent*) lua_touserdata(luaState, LUA_FIST_ARG+1);
+ } else {
+ afbevt=luaDefaultEvt;
+ }
+
+ if (!afb_event_is_valid(afbevt->event)) {
+ lua_pushliteral (luaState, "LuaAfbMakePush-Fail invalid event handle");
+ goto OnErrorExit;
+ }
+
+ int err = afb_req_subscribe(afbContext->request, afbevt->event);
+ if (err) {
+ lua_pushliteral (luaState, "LuaAfbEventSubscribe-Fail No Subscriber to event");
+ AFB_ERROR ("LuaAfbEventPush-Fail name subscriber event=%s count=%d", afbevt->name, afbevt->count);
+ goto OnErrorExit;
+ }
+ afbevt->count++;
+ return 0;
+
+ OnErrorExit:
+ lua_error(luaState);
+ return 1;
+}
+
+STATIC int LuaAfbEventMake(lua_State* luaState) {
+ int count = lua_gettop(luaState);
+ LuaAfbEvent *afbevt=calloc(1,sizeof(LuaAfbEvent));
+
+ if (count != LUA_FIST_ARG || !lua_isstring(luaState, LUA_FIST_ARG)) {
+ lua_pushliteral (luaState, "LuaAfbEventMake-Syntax is evtHandle= AFB:event ('myEventName')");
+ goto OnErrorExit;
+ }
+
+ // event name should be the only argument
+ afbevt->name= strdup (lua_tostring(luaState,LUA_FIST_ARG));
+
+ // create a new binder event
+ afbevt->event = afb_daemon_make_event(afbevt->name);
+ if (!afb_event_is_valid(afbevt->event)) {
+ lua_pushliteral (luaState, "LuaAfbEventMake-Fail to Create Binder event");
+ goto OnErrorExit;
+ }
+
+ // push event handler as a LUA opaque handle
+ lua_pushlightuserdata(luaState, afbevt);
+ return 1;
+
+ OnErrorExit:
+ lua_error(luaState);
+ return 1;
+}
+
+// Function call from LUA when lua2c plugin L2C is used
+PUBLIC int Lua2cWrapper(lua_State* luaState, char *funcname, Lua2cFunctionT callback) {
+
+ json_object *argsJ= LuaPopArgs(luaState, LUA_FIST_ARG+1);
+ int response = (*callback) (funcname, argsJ);
+
+ // push response to LUA
+ lua_pushinteger(luaState, response);
+ return 1;
+}
+
+// Call a Lua function from a control action
+PUBLIC int LuaCallFunc (CtlActionT *action, json_object *queryJ) {
+
+ int err, count;
+
+ json_object* argsJ = action->argsJ;
+ const char* func = action->call;
+
+ // load function (should exist in CONTROL_PATH_LUA
+ lua_getglobal(luaState, func);
+
+ // push source on the stack
+ count=1;
+ lua_pushstring(luaState, action->source.label);
+
+ // push argsJ on the stack
+ if (!argsJ) {
+ lua_pushnil(luaState);
+ count++;
+ } else {
+ count+= LuaPushArgument (argsJ);
+ }
+
+ // push queryJ on the stack
+ if (!queryJ) {
+ lua_pushnil(luaState);
+ count++;
+ } else {
+ count+= LuaPushArgument (queryJ);
+ }
+
+ // effectively exec LUA script code
+ err=lua_pcall(luaState, count, 1, 0);
+ if (err) {
+ AFB_ERROR("LuaCallFunc Fail calling %s error=%s", func, lua_tostring(luaState,-1));
+ goto OnErrorExit;
+ }
+
+ // return LUA script value
+ int rc= (int)lua_tointeger(luaState, -1);
+ return rc;
+
+ OnErrorExit:
+ return -1;
+}
+
+
+// Execute LUA code from received API request
+STATIC 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;
+ }
+ // Push AFB client context on the stack
+ LuaAfbContextT *afbContext= LuaCtxPush(luaState, request,NULL,script);
+ if (!afbContext) goto OnErrorExit;
+
+ break;
+ }
+
+ case LUA_DOCALL: {
+ const char *func;
+ json_object *argsJ=NULL;
+
+ err= wrap_json_unpack (queryJ, "{s:s, s?o !}", "target", &func, "args", &argsJ);
+ if (err) {
+ AFB_ERROR ("LUA-DOCALL-SYNTAX missing target|args query=%s", json_object_get_string(queryJ));
+ goto OnErrorExit;
+ }
+
+ // load function (should exist in CONTROL_PATH_LUA
+ lua_getglobal(luaState, func);
+
+ // Push AFB client context on the stack
+ LuaAfbContextT *afbContext= LuaCtxPush(luaState, request, NULL, func);
+ if (!afbContext) goto OnErrorExit;
+
+ // push query on the stack
+ if (!argsJ) {
+ lua_pushnil(luaState);
+ count++;
+ } else {
+ count+= LuaPushArgument (argsJ);
+ }
+
+ break;
+ }
+
+ 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_ERROR ("LUA-DOSCRIPT-SYNTAX:missing target|[path]|[function]|[args] query=%s", json_object_get_string(queryJ));
+ goto OnErrorExit;
+ }
+
+ // search for filename=script in CONTROL_LUA_PATH
+ if (!luaScriptPathJ) {
+ strncpy(luaScriptPath,CONTROL_DOSCRIPT_PRE, sizeof(luaScriptPath));
+ strncat(luaScriptPath,"-", sizeof(luaScriptPath));
+ strncat(luaScriptPath,target, sizeof(luaScriptPath));
+ 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);
+
+ err= wrap_json_unpack (entryJ, "{s:s, s:s !}", "fullpath", &fullpath,"filename", &filename);
+ if (err) {
+ AFB_ERROR ("LUA-DOSCRIPT-SCAN:HOOPs invalid config file path = %s", json_object_get_string(entryJ));
+ goto OnErrorExit;
+ }
+
+ if (index > 0) AFB_WARNING("LUA-DOSCRIPT-SCAN:Ignore second script=%s path=%s", filename, fullpath);
+ else {
+ strncpy (luaScriptPath, fullpath, sizeof(luaScriptPath));
+ strncat (luaScriptPath, "/", sizeof(luaScriptPath));
+ strncat (luaScriptPath, filename, sizeof(luaScriptPath));
+ }
+ }
+
+ err= luaL_loadfile(luaState, luaScriptPath);
+ if (err) {
+ AFB_ERROR ("LUA-DOSCRIPT HOOPs Error in LUA loading scripts=%s err=%s", luaScriptPath, lua_tostring(luaState,-1));
+ goto OnErrorExit;
+ }
+
+ // script was loaded we need to parse to make it executable
+ err=lua_pcall(luaState, 0, 0, 0);
+ if (err) {
+ AFB_ERROR ("LUA-DOSCRIPT:FAIL to load %s", luaScriptPath);
+ goto OnErrorExit;
+ }
+
+ // if no func name given try to deduct from filename
+ if (!func && (func=(char*)GetMidleName(filename))!=NULL) {
+ strncpy(luaScriptPath,"_", sizeof(luaScriptPath));
+ strncat(luaScriptPath,func, sizeof(luaScriptPath));
+ func=luaScriptPath;
+ }
+ if (!func) {
+ AFB_ERROR ("LUA-DOSCRIPT:FAIL to deduct funcname from %s", filename);
+ goto OnErrorExit;
+ }
+
+ // load function (should exist in CONTROL_PATH_LUA
+ lua_getglobal(luaState, func);
+
+ // Push AFB client context on the stack
+ LuaAfbContextT *afbContext= LuaCtxPush(luaState, request, NULL, func);
+ if (!afbContext) goto OnErrorExit;
+
+ // push function arguments
+ if (!argsJ) {
+ lua_pushnil(luaState);
+ count++;
+ } else {
+ count+= LuaPushArgument(argsJ);
+ }
+
+ break;
+ }
+
+ default:
+ AFB_ERROR ("LUA-DOSCRIPT-ACTION unknown query=%s", json_object_get_string(queryJ));
+ goto OnErrorExit;
+ }
+
+ // effectively exec LUA code (afb_reply/fail done later from callback)
+ err=lua_pcall(luaState, count+1, 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;
+ }
+ return;
+
+ OnErrorExit:
+ afb_req_fail(request,"LUA:ERROR", lua_tostring(luaState,-1));
+ return;
+}
+
+PUBLIC void ctlapi_execlua (afb_req request) {
+ LuaDoAction (LUA_DOSTRING, request);
+}
+
+PUBLIC void ctlapi_request (afb_req request) {
+ LuaDoAction (LUA_DOCALL, request);
+}
+
+PUBLIC void ctlapi_debuglua (afb_req request) {
+ LuaDoAction (LUA_DOSCRIPT, request);
+}
+
+STATIC int LuaTimerClear (lua_State* luaState) {
+
+ // Get Timer Handle
+ LuaAfbContextT *afbContext= LuaCtxCheck(luaState, LUA_FIST_ARG);
+ if (!afbContext) goto OnErrorExit;
+
+ // retrieve useful information opaque handle
+ TimerHandleT *timerHandle = (TimerHandleT*)afbContext->handle;
+
+ AFB_NOTICE ("LuaTimerClear timer=%s", timerHandle->label);
+ TimerEvtStop(timerHandle);
+
+ return 0; //happy end
+
+OnErrorExit:
+ return 1;
+}
+STATIC int LuaTimerGet (lua_State* luaState) {
+
+ // Get Timer Handle
+ LuaAfbContextT *afbContext= LuaCtxCheck(luaState, LUA_FIST_ARG);
+ if (!afbContext) goto OnErrorExit;
+
+ // retrieve useful information opaque handle
+ TimerHandleT *timerHandle = (TimerHandleT*)afbContext->handle;
+
+ // create response as a JSON object
+ json_object *responseJ= json_object_new_object();
+ json_object_object_add(responseJ,"label", json_object_new_string(timerHandle->label));
+ json_object_object_add(responseJ,"delay", json_object_new_int(timerHandle->delay));
+ json_object_object_add(responseJ,"count", json_object_new_int(timerHandle->count));
+
+ // return JSON object as Lua table
+ int count=LuaPushArgument(responseJ);
+
+ // free json object
+ json_object_put(responseJ);
+
+ return count; // return argument
+
+OnErrorExit:
+ return 0;
+}
+
+// Timer Callback
+
+// Set timer
+STATIC int LuaTimerSetCB (void *handle) {
+ LuaCallServiceT *contextCB =(LuaCallServiceT*) handle;
+ TimerHandleT *timerHandle = (TimerHandleT*) contextCB->handle;
+ int count;
+
+ // push timer handle and user context on Lua stack
+ lua_getglobal(luaState, contextCB->callback);
+
+ // Push timer handle
+ LuaAfbContextT *afbContext= LuaCtxPush(luaState, NULL_AFBREQ, contextCB->handle, timerHandle->label);
+ if (!afbContext) goto OnErrorExit;
+ count=1;
+
+ // Push user Context
+ count+= LuaPushArgument(contextCB->context);
+
+ int err=lua_pcall(luaState, count, LUA_MULTRET, 0);
+ if (err) {
+ AFB_ERROR ("LUA-TIMER-CB:FAIL response=%s err=%s", json_object_get_string(contextCB->context), lua_tostring(luaState,-1));
+ goto OnErrorExit;
+ }
+
+ // get return parameter
+ if (!lua_isboolean(luaState, -1)) {
+ return (lua_toboolean(luaState, -1));
+ }
+
+ // timer last run free context resource
+ if (timerHandle->count == 1) {
+ LuaCtxFree(afbContext);
+ }
+ return 0; // By default we are happy
+
+ OnErrorExit:
+ return 1; // stop timer
+}
+
+STATIC int LuaTimerSet(lua_State* luaState) {
+ const char *label=NULL, *info=NULL;
+ int delay=0, count=0;
+
+ json_object *timerJ = LuaPopOneArg(luaState, LUA_FIST_ARG);
+ const char *callback = lua_tostring(luaState, LUA_FIST_ARG + 1);
+ json_object *contextJ = LuaPopOneArg(luaState, LUA_FIST_ARG + 2);
+
+ if (lua_gettop(luaState) != LUA_FIST_ARG+2 || !timerJ || !callback || !contextJ) {
+ lua_pushliteral(luaState, "LuaTimerSet-Syntax timerset (timerT, 'callback', contextT)");
+ goto OnErrorExit;
+ }
+
+ int err = wrap_json_unpack(timerJ, "{ss, s?s si, si !}", "label", &label, "info", &info, "delay", &delay, "count", &count);
+ if (err) {
+ lua_pushliteral(luaState, "LuaTimerSet-Syntax timerT={label:xxx delay:ms, count:xx}");
+ goto OnErrorExit;
+ }
+
+ // everything look fine create timer structure
+ TimerHandleT *timerHandle = malloc (sizeof (TimerHandleT));
+ timerHandle->delay=delay;
+ timerHandle->count=count;
+ timerHandle->label=label;
+
+ // Allocate handle to store context and callback
+ LuaCallServiceT *contextCB = calloc (1, sizeof(LuaCallServiceT));
+ contextCB->callback= callback;
+ contextCB->context = contextJ;
+ contextCB->handle = timerHandle;
+
+ // fire timer
+ TimerEvtStart (timerHandle, LuaTimerSetCB, contextCB);
+
+ return 0; // Happy No Return Function
+
+OnErrorExit:
+ lua_error(luaState);
+ return 1; // return error code
+}
+
+// Register a new L2c list of LUA user plugin commands
+PUBLIC void LuaL2cNewLib(const char *label, luaL_Reg *l2cFunc, int count) {
+ // luaL_newlib(luaState, l2cFunc); macro does not work with pointer :(
+ luaL_checkversion(luaState);
+ lua_createtable(luaState, 0, count+1);
+ luaL_setfuncs(luaState,l2cFunc,0);
+ lua_setglobal(luaState, label);
+}
+
+static const luaL_Reg afbFunction[] = {
+ {"timerclear", LuaTimerClear},
+ {"timerget" , LuaTimerGet},
+ {"timerset" , LuaTimerSet},
+ {"notice" , LuaPrintNotice},
+ {"info" , LuaPrintInfo},
+ {"warning" , LuaPrintWarning},
+ {"debug" , LuaPrintDebug},
+ {"error" , LuaPrintError},
+ {"servsync" , LuaAfbServiceSync},
+ {"service" , LuaAfbService},
+ {"success" , LuaAfbSuccess},
+ {"fail" , LuaAfbFail},
+ {"subscribe" , LuaAfbEventSubscribe},
+ {"evtmake" , LuaAfbEventMake},
+ {"evtpush" , LuaAfbEventPush},
+
+ {NULL, NULL} /* sentinel */
+};
+
+// Load Lua Interpreter
+PUBLIC int LuaConfigLoad () {
+
+
+ // 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
+ luaL_newlib(luaState, afbFunction);
+ lua_setglobal(luaState, "AFB");
+
+ return 0;
+
+ OnErrorExit:
+ return 1;
+}
+
+// Create Binding Event at Init Exec Time
+PUBLIC int LuaConfigExec () {
+
+ int err, index;
+ // create default lua event to send test pause/resume
+ luaDefaultEvt=calloc(1,sizeof(LuaAfbEvent));
+ luaDefaultEvt->name=CONTROL_LUA_EVENT;
+ luaDefaultEvt->event = afb_daemon_make_event(CONTROL_LUA_EVENT);
+ if (!afb_event_is_valid(luaDefaultEvt->event)) {
+ AFB_ERROR ("POLCTL_INIT: Cannot register lua-events=%s ", CONTROL_LUA_EVENT);
+ goto OnErrorExit;;
+ }
+
+ // search for default policy config file
+ char fullprefix[CONTROL_MAXPATH_LEN];
+ strncpy (fullprefix, CONTROL_CONFIG_PRE "-", sizeof(fullprefix));
+ strncat (fullprefix, GetBinderName(), sizeof(fullprefix));
+ strncat (fullprefix, "-", sizeof(fullprefix));
+
+ const char *dirList= getenv("CONTROL_LUA_PATH");
+ if (!dirList) dirList=CONTROL_LUA_PATH;
+
+ // special case for no lua even when avaliable
+ if (!strcasecmp ("/dev/null", dirList)) {
+ return 0;
+ }
+
+ json_object *luaScriptPathJ = ScanForConfig(dirList , CTL_SCAN_RECURSIVE, fullprefix, "lua");
+
+ // 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*fullpath;
+ err= wrap_json_unpack (entryJ, "{s:s, s:s !}", "fullpath", &fullpath,"filename", &filename);
+ if (err) {
+ AFB_ERROR ("LUA-INIT HOOPs invalid config file path = %s", json_object_get_string(entryJ));
+ goto OnErrorExit;
+ }
+
+ char filepath[CONTROL_MAXPATH_LEN];
+ strncpy(filepath, fullpath, 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 (setenv CONTROL_LUA_PATH) No LUA '%s*.lua' in '%s'", fullprefix, dirList);
+ }
+
+ AFB_DEBUG ("Audio control-LUA Init Done");
+ return 0;
+
+ OnErrorExit:
+ return 1;
+}
diff --git a/controller/ctl-lua.h b/controller/ctl-lua.h
new file mode 100644
index 0000000..6f2ce7d
--- /dev/null
+++ b/controller/ctl-lua.h
@@ -0,0 +1,77 @@
+/*
+ * 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, something express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Reference:
+ * Json load using json_unpack https://jansson.readthedocs.io/en/2.9/apiref.html#parsing-and-validating-values
+ */
+
+#ifndef _LUA_CTL_INCLUDE_
+#define _LUA_CTL_INCLUDE_
+
+#define _GNU_SOURCE
+
+// prefix start debug script
+#ifndef CONTROL_DOSCRIPT_PRE
+#define CONTROL_DOSCRIPT_PRE "debug"
+#endif
+
+// default event name used by LUA
+#ifndef CONTROL_LUA_EVENT
+#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"
+#include "lauxlib.h"
+#include "lualib.h"
+#endif
+
+#include "ctl-timer.h"
+
+PUBLIC int LuaLibInit ();
+
+typedef int (*Lua2cFunctionT)(char *funcname, json_object *argsJ);
+typedef int (*Lua2cWrapperT) (lua_State* luaState, char *funcname, Lua2cFunctionT callback);
+
+#define CTLP_LUALOAD Lua2cWrapperT Lua2cWrap;
+#define CTLP_LUA2C(FuncName, label,argsJ, context) static int FuncName(char*label,json_object*argsJ);\
+ int lua2c_ ## FuncName(lua_State* luaState){return((*Lua2cWrap)(luaState, MACRO_STR_VALUE(FuncName), FuncName, PLUGIN_NAME));};\
+ static int FuncName(char* label, json_object* argsJ, void* context)
+
+typedef enum {
+ LUA_DOCALL,
+ LUA_DOSTRING,
+ LUA_DOSCRIPT,
+} LuaDoActionT;
+
+
+PUBLIC int LuaConfigLoad();
+PUBLIC int LuaConfigExec();
+PUBLIC void LuaL2cNewLib(const char *label, luaL_Reg *l2cFunc, int count);
+PUBLIC int Lua2cWrapper(lua_State* luaState, char *funcname, Lua2cFunctionT callback);
+PUBLIC int LuaCallFunc (CtlActionT *action, json_object *queryJ) ;
+PUBLIC void ctlapi_lua_docall (afb_req request);
+PUBLIC void ctlapi_lua_dostring (afb_req request);
+PUBLIC void ctlapi_lua_doscript (afb_req request);
+
+
+#endif \ No newline at end of file
diff --git a/controller/ctl-onload.c b/controller/ctl-onload.c
new file mode 100644
index 0000000..97bd109
--- /dev/null
+++ b/controller/ctl-onload.c
@@ -0,0 +1,57 @@
+/*
+ * 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, something express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+
+#include "ctl-config.h"
+
+// onload section receive one action or an array of actions
+PUBLIC int OnloadConfig(CtlSectionT *section, json_object *actionsJ) {
+ CtlActionT *actions;
+
+ // Load time parse actions in config file
+ if (actionsJ != NULL) {
+ actions= ActionLoad(actionsJ);
+ section->handle=actions;
+
+ if (!actions) {
+ AFB_ERROR ("OnloadLoad config fail processing onload actions");
+ goto OnErrorExit;
+ }
+
+ } else {
+ // Exec time process onload action now
+ actions=(CtlActionT*)section->handle;
+ if (!actions) {
+ AFB_ERROR ("OnloadLoad Cannot Exec Non Existing Onload Action");
+ goto OnErrorExit;
+ }
+
+ for (int idx=0; actions[idx].source.label != NULL; idx ++) {
+ ActionExecOne(&actions[idx], NULL);
+ }
+ }
+
+ return 0;
+
+OnErrorExit:
+ return 1;
+
+}
diff --git a/controller/ctl-plugin.c b/controller/ctl-plugin.c
new file mode 100644
index 0000000..f589035
--- /dev/null
+++ b/controller/ctl-plugin.c
@@ -0,0 +1,219 @@
+/*
+ * 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, something express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Reference:
+ * Json load using json_unpack https://jansson.readthedocs.io/en/2.9/apiref.html#parsing-and-validating-values
+ */
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <dlfcn.h>
+
+#include "ctl-config.h"
+
+static CtlPluginT *ctlPlugins=NULL;
+
+PUBLIC int PluginGetCB (CtlActionT *action , json_object *callbackJ) {
+ const char *plugin=NULL, *function=NULL;
+ json_object *argsJ;
+ int idx;
+
+ if (!ctlPlugins) {
+ AFB_ERROR ("PluginGetCB plugin section missing cannot call '%s'", json_object_get_string(callbackJ));
+ goto OnErrorExit;
+ }
+
+
+ int err = wrap_json_unpack(callbackJ, "{ss,ss,s?s,s?o!}", "plugin", &plugin, "function", &function, "args", &argsJ);
+ if (err) {
+ AFB_ERROR("PluginGet missing plugin|function|[args] in %s", json_object_get_string(callbackJ));
+ goto OnErrorExit;
+ }
+
+ for (idx=0; ctlPlugins[idx].label != NULL; idx++) {
+ if (!strcasecmp (ctlPlugins[idx].label, plugin)) break;
+ }
+
+ if (!ctlPlugins[idx].label) {
+ AFB_ERROR ("PluginGetCB no plugin with label=%s", plugin);
+ goto OnErrorExit;
+ }
+
+ action->actionCB = dlsym(ctlPlugins[idx].dlHandle, function);
+ action->source.context = ctlPlugins[idx].context;
+
+ if (!action->actionCB) {
+ AFB_ERROR ("PluginGetCB no plugin=%s no function=%s", plugin, function);
+ goto OnErrorExit;
+ }
+ return 0;
+
+OnErrorExit:
+ return 1;
+
+}
+
+// Wrapper to Lua2c plugin command add context and delegate to LuaWrapper
+STATIC int DispatchOneL2c(lua_State* luaState, char *funcname, Lua2cFunctionT callback) {
+#ifndef CONTROL_SUPPORT_LUA
+ AFB_ERROR("CTL-ONE-L2C: LUA support not selected (cf:CONTROL_SUPPORT_LUA) in config.cmake");
+ return 1;
+#else
+ int err=Lua2cWrapper(luaState, funcname, callback);
+ return err;
+#endif
+}
+
+STATIC int PluginLoadOne (CtlPluginT *ctlPlugin, json_object *pluginJ, void* handle) {
+ json_object *lua2csJ = NULL, *actionsJ = NULL;
+ const char*ldSearchPath = NULL, *basename = NULL;
+ void *dlHandle;
+
+
+ // 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,ss,s?o,s?o!}",
+ "label", &ctlPlugin->label, "version", &ctlPlugin->version, "info", &ctlPlugin->info, "ldpath", &ldSearchPath, "basename", &basename, "lua2c", &lua2csJ, "actions", &actionsJ);
+ if (err) {
+ AFB_ERROR("CTL-PLUGIN-LOADONE Plugin missing label|basename|version|[info]|[ldpath]|[lua2c]|[actions] in:\n-- %s", json_object_get_string(pluginJ));
+ goto OnErrorExit;
+ }
+
+ // if search path not in Json config file, then try default
+ if (!ldSearchPath) ldSearchPath = CONTROL_PLUGIN_PATH;
+
+ // search for default policy config file
+ json_object *pluginPathJ = ScanForConfig(ldSearchPath, CTL_SCAN_RECURSIVE, basename, CTL_PLUGIN_EXT);
+ if (!pluginPathJ || json_object_array_length(pluginPathJ) == 0) {
+ AFB_ERROR("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_ERROR("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_WARNING("CTL-PLUGIN-LOADONE plugin multiple instances in searchpath will use %s/%s", fullpath, filename);
+ }
+
+ char pluginpath[CONTROL_MAXPATH_LEN];
+ strncpy(pluginpath, fullpath, sizeof (pluginpath));
+ strncat(pluginpath, "/", sizeof (pluginpath));
+ strncat(pluginpath, filename, sizeof (pluginpath));
+ dlHandle = dlopen(pluginpath, RTLD_NOW);
+ if (!dlHandle) {
+ AFB_ERROR("CTL-PLUGIN-LOADONE Fail to load pluginpath=%s err= %s", pluginpath, dlerror());
+ goto OnErrorExit;
+ }
+
+ CtlPluginMagicT *ctlPluginMagic = (CtlPluginMagicT*) dlsym(dlHandle, "CtlPluginMagic");
+ if (!ctlPluginMagic || ctlPluginMagic->magic != CTL_PLUGIN_MAGIC) {
+ AFB_ERROR("CTL-PLUGIN-LOADONE symbol'CtlPluginMagic' missing or != CTL_PLUGIN_MAGIC plugin=%s", pluginpath);
+ goto OnErrorExit;
+ } else {
+ AFB_NOTICE("CTL-PLUGIN-LOADONE %s successfully registered", ctlPluginMagic->label);
+ }
+
+ // store dlopen handle to enable onload action at exec time
+ ctlPlugin->dlHandle = dlHandle;
+
+ // Jose hack to make verbosity visible from sharelib
+ struct afb_binding_data_v2 *afbHidenData = dlsym(dlHandle, "afbBindingV2data");
+ if (afbHidenData) *afbHidenData = afbBindingV2data;
+
+ // Push lua2cWrapper @ into plugin
+ Lua2cWrapperT *lua2cInPlug = dlsym(dlHandle, "Lua2cWrap");
+#ifndef CONTROL_SUPPORT_LUA
+ if (lua2cInPlug) *lua2cInPlug = NULL;
+#else
+ // Lua2cWrapper is part of binder and not expose to dynamic link
+ if (lua2csJ && lua2cInPlug) {
+ *lua2cInPlug = DispatchOneL2c;
+
+ int Lua2cAddOne(luaL_Reg *l2cFunc, const char* l2cName, int index) {
+ char funcName[CONTROL_MAXPATH_LEN];
+ strncpy(funcName, "lua2c_", sizeof (funcName));
+ strncat(funcName, l2cName, sizeof (funcName));
+
+ Lua2cFunctionT l2cFunction = (Lua2cFunctionT) dlsym(dlHandle, funcName);
+ if (!l2cFunction) {
+ AFB_ERROR("CTL-PLUGIN-LOADONE symbol'%s' missing err=%s", funcName, dlerror());
+ return 1;
+ }
+ l2cFunc[index].func = (void*) l2cFunction;
+ l2cFunc[index].name = strdup(l2cName);
+
+ return 0;
+ }
+
+ int errCount = 0;
+ luaL_Reg *l2cFunc = NULL;
+
+ // look on l2c command and push them to LUA
+ if (json_object_get_type(lua2csJ) == json_type_array) {
+ int length = json_object_array_length(lua2csJ);
+ l2cFunc = calloc(length + 1, sizeof (luaL_Reg));
+ for (int count = 0; count < length; count++) {
+ int err;
+ const char *l2cName = json_object_get_string(json_object_array_get_idx(lua2csJ, count));
+ err = Lua2cAddOne(l2cFunc, l2cName, count);
+ if (err) errCount++;
+ }
+ } else {
+ l2cFunc = calloc(2, sizeof (luaL_Reg));
+ const char *l2cName = json_object_get_string(lua2csJ);
+ errCount = Lua2cAddOne(l2cFunc, l2cName, 0);
+ }
+ if (errCount) {
+ AFB_ERROR("CTL-PLUGIN-LOADONE %d symbols not found in plugin='%s'", errCount, pluginpath);
+ goto OnErrorExit;
+ }
+ }
+#endif
+ DispatchPluginInstallCbT ctlPluginOnload = dlsym(dlHandle, "CtlPluginOnload");
+ if (ctlPluginOnload) {
+ ctlPlugin->context = (*ctlPluginOnload) (ctlPlugin, handle);
+ }
+ return 0;
+
+OnErrorExit:
+ return 1;
+}
+
+
+PUBLIC int PluginConfig(CtlSectionT *section, json_object *pluginsJ) {
+ int err=0;
+
+ if (json_object_get_type(pluginsJ) == json_type_array) {
+ int length = json_object_array_length(pluginsJ);
+ ctlPlugins = calloc (length+1, sizeof(CtlPluginT));
+ for (int idx=0; idx < length; idx++) {
+ json_object *pluginJ = json_object_array_get_idx(pluginsJ, idx);
+ err += PluginLoadOne(&ctlPlugins[idx], pluginJ, section->handle);
+ }
+ } else {
+ ctlPlugins = calloc (2, sizeof(CtlPluginT));
+ err += PluginLoadOne(&ctlPlugins[0], pluginsJ, section->handle);
+ }
+
+ return err;
+} \ No newline at end of file
diff --git a/controller/ctl-plugin.h b/controller/ctl-plugin.h
new file mode 100644
index 0000000..c0aa2e2
--- /dev/null
+++ b/controller/ctl-plugin.h
@@ -0,0 +1,74 @@
+/*
+ * 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, something express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+
+#ifndef _CTL_PLUGIN_INCLUDE_
+#define _CTL_PLUGIN_INCLUDE_
+
+#define _GNU_SOURCE
+#include <json-c/json.h>
+
+#ifndef CTL_PLUGIN_MAGIC
+ #define CTL_PLUGIN_MAGIC 852369147
+#endif
+
+#ifndef PUBLIC
+ #define PUBLIC
+#endif
+
+#ifndef STATIC
+ #define STATIC static
+#endif
+
+#ifndef UNUSED_ARG
+ #define UNUSED_ARG(x) UNUSED_ ## x __attribute__((__unused__))
+ #define UNUSED_FUNCTION(x) __attribute__((__unused__)) UNUSED_ ## x
+#endif
+
+typedef struct {
+ char *label;
+ char *info;
+ afb_req request;
+ void *context;
+} CtlSourceT;
+
+
+typedef struct {
+ long magic;
+ char *label;
+ void *handle;
+} CtlPluginMagicT;
+
+
+typedef struct {
+ const char *label;
+ const char *info;
+ const char *version;
+ void *context;
+ void *dlHandle;
+} CtlPluginT;
+
+typedef void*(*DispatchPluginInstallCbT)(CtlPluginT *plugin, void* handle);
+
+
+#define MACRO_STR_VALUE(arg) #arg
+#define CTLP_REGISTER(pluglabel) CtlPluginMagicT CtlPluginMagic={.magic=CTL_PLUGIN_MAGIC,.label=pluglabel}; struct afb_binding_data_v2;
+#define CTLP_ONLOAD(plugin, handle) void* CtlPluginOnload(CtlPluginT *plugin, void* handle)
+#define CTLP_CAPI(funcname, source, argsJ, queryJ, context) int funcname(CtlSourceT *source, json_object* argsJ, json_object* queryJ, void* context)
+
+#endif /* _CTL_PLUGIN_INCLUDE_ */ \ No newline at end of file
diff --git a/controller/ctl-timer.c b/controller/ctl-timer.c
new file mode 100644
index 0000000..1de6f99
--- /dev/null
+++ b/controller/ctl-timer.c
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#include "ctl-config.h"
+#include "ctl-timer.h"
+
+#define DEFAULT_PAUSE_DELAY 3000
+#define DEFAULT_TEST_COUNT 1
+typedef struct {
+ int value;
+ const char *label;
+} AutoTestCtxT;
+
+static afb_event afbevt;
+
+STATIC int TimerNext (sd_event_source* source, uint64_t timer, void* handle) {
+ TimerHandleT *timerHandle = (TimerHandleT*) handle;
+ int done;
+ uint64_t usec;
+
+ // Rearm timer if needed
+ timerHandle->count --;
+ if (timerHandle->count == 0) {
+ sd_event_source_unref(source);
+ free (handle);
+ return 0;
+ }
+ else {
+ // otherwise validate timer for a new run
+ sd_event_now(afb_daemon_get_event_loop(), CLOCK_MONOTONIC, &usec);
+ sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
+ sd_event_source_set_time(source, usec + timerHandle->delay*1000);
+ }
+
+ done= timerHandle->callback(timerHandle->context);
+ if (!done) goto OnErrorExit;
+
+ return 0;
+
+OnErrorExit:
+ AFB_WARNING("TimerNext Callback Fail Tag=%s", timerHandle->label);
+ return -1;
+}
+
+PUBLIC void TimerEvtStop(TimerHandleT *timerHandle) {
+
+ sd_event_source_unref(timerHandle->evtSource);
+ free (timerHandle);
+}
+
+
+PUBLIC void TimerEvtStart(TimerHandleT *timerHandle, timerCallbackT callback, void *context) {
+ uint64_t usec;
+
+ // populate CB handle
+ timerHandle->callback=callback;
+ timerHandle->context=context;
+
+ // set a timer with ~250us accuracy
+ sd_event_now(afb_daemon_get_event_loop(), CLOCK_MONOTONIC, &usec);
+ sd_event_add_time(afb_daemon_get_event_loop(), &timerHandle->evtSource, CLOCK_MONOTONIC, usec+timerHandle->delay*1000, 250, TimerNext, timerHandle);
+}
+
+PUBLIC afb_event TimerEvtGet(void) {
+ return afbevt;
+}
+
+
+// Create Binding Event at Init
+PUBLIC int TimerEvtInit () {
+
+ // create binder event to send test pause/resume
+ afbevt = afb_daemon_make_event("control");
+ if (!afb_event_is_valid(afbevt)) {
+ AFB_ERROR ("POLCTL_INIT: Cannot register ctl-events");
+ return 1;
+ }
+
+ AFB_DEBUG ("Audio Control-Events Init Done");
+ return 0;
+}
+
diff --git a/controller/ctl-timer.h b/controller/ctl-timer.h
new file mode 100644
index 0000000..b08299f
--- /dev/null
+++ b/controller/ctl-timer.h
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+
+#ifndef CTL_TIMER_INCLUDE
+#define CTL_TIMER_INCLUDE
+
+#include <systemd/sd-event.h>
+
+// ctl-timer.c
+// ----------------------
+typedef int (*timerCallbackT)(void *context);
+
+typedef struct TimerHandleS {
+ int count;
+ int delay;
+ const char*label;
+ void *context;
+ timerCallbackT callback;
+ sd_event_source *evtSource;
+} TimerHandleT;
+
+PUBLIC int TimerEvtInit (void);
+PUBLIC afb_event TimerEvtGet(void);
+PUBLIC void TimerEvtStart(TimerHandleT *timerHandle, timerCallbackT callback, void *context);
+PUBLIC void TimerEvtStop(TimerHandleT *timerHandle);
+
+#endif // CTL_TIMER_INCLUDE
diff --git a/signal-composer-binding/CMakeLists.txt b/signal-composer-binding/CMakeLists.txt
index 52f8418..78db103 100644
--- a/signal-composer-binding/CMakeLists.txt
+++ b/signal-composer-binding/CMakeLists.txt
@@ -21,7 +21,7 @@
PROJECT_TARGET_ADD(signal-composer)
# Define project Targets
- add_library(${TARGET_NAME} MODULE ${TARGET_NAME}-binding.cpp ${TARGET_NAME}.cpp ctl-lua.c)
+ add_library(${TARGET_NAME} MODULE ${TARGET_NAME}-binding.cpp ${TARGET_NAME}.cpp ctl-lua.c ctl-dispatch.c)
# Binder exposes a unique public entry point
SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES
diff --git a/signal-composer-binding/ctl-dispatch.c b/signal-composer-binding/ctl-dispatch.c
new file mode 100644
index 0000000..b84c86e
--- /dev/null
+++ b/signal-composer-binding/ctl-dispatch.c
@@ -0,0 +1,705 @@
+/*
+ * 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, something express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Reference:
+ * Json load using json_unpack https://jansson.readthedocs.io/en/2.9/apiref.html#parsing-and-validating-values
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <sys/prctl.h>
+#include <filescan-utils.h>
+#include <wrap-json.h>
+
+#include "ctl-lua.h"
+#include "signal-composer-binding.hpp"
+
+typedef void*(*DispatchPluginInstallCbT)(const char* label, const char*version, const char*info);
+
+static afb_req NULL_AFBREQ = {};
+
+typedef struct {
+ const char* id;
+ const char* source;
+ const char* class;
+ DispatchActionT* onReceived;
+} DispatchSignalT;
+
+typedef struct {
+ const char* label;
+ const char* info;
+ const char* ssource;
+ const char* sclass;
+ DispatchActionT* actions;
+} DispatchHandleT;
+
+typedef struct {
+ const char *label;
+ const char *info;
+ void *context;
+ char *sharelib;
+ void *dlHandle;
+ luaL_Reg *l2cFunc;
+ int l2cCount;
+} DispatchPluginT;
+
+typedef struct {
+ const char* label;
+ const char *info;
+ const char *version;
+ DispatchPluginT *plugin;
+ DispatchHandleT **sources;
+ DispatchHandleT **signals;
+} DispatchConfigT;
+
+// global config handle
+static DispatchConfigT *configHandle = NULL;
+
+static int DispatchSignalToIndex(DispatchHandleT **signals, const char* controlLabel) {
+
+ for (int idx = 0; signals[idx]; idx++) {
+ if (!strcasecmp(controlLabel, signals[idx]->label)) return idx;
+ }
+ return -1;
+}
+
+static int DispatchOneSignal(DispatchSourceT source, DispatchHandleT **signals, const char* controlLabel, json_object *queryJ, afb_req request) {
+ int err;
+
+ if (!configHandle) {
+ AFB_ERROR("DISPATCH-CTL-API: (Hoops/Bug!!!) No Config Loaded");
+ return -1;
+ }
+
+ if (!configHandle->signals) {
+ AFB_ERROR("DISPATCH-CTL-API: No Signal Action in Json config label=%s version=%s", configHandle->label, configHandle->version);
+ return -1;
+ }
+
+ int index = DispatchSignalToIndex(signals, controlLabel);
+ if (index < 0 || !signals[index]->actions) {
+ AFB_ERROR("DISPATCH-CTL-API:NotFound/Error label=%s in Json Signal Config File", controlLabel);
+ return -1;
+ }
+
+ // Fulup (Bug/Feature) in current version is unique to every onload profile
+ if (configHandle->plugin && configHandle->plugin->l2cCount) {
+ LuaL2cNewLib (configHandle->plugin->label, configHandle->plugin->l2cFunc, configHandle->plugin->l2cCount);
+ }
+
+ // loop on action for this control
+ DispatchActionT *actions = signals[index]->actions;
+ for (int idx = 0; actions[idx].label; idx++) {
+
+ switch (actions[idx].mode) {
+ case CTL_MODE_API:
+ {
+ json_object *returnJ;
+
+ // if query is empty increment usage count and pass args
+ if (!queryJ || json_object_get_type(queryJ) != json_type_object) {
+ json_object_get(actions[idx].argsJ);
+ queryJ= actions[idx].argsJ;
+ } else if (actions[idx].argsJ) {
+
+ // Merge queryJ and argsJ before sending request
+ if (json_object_get_type(actions[idx].argsJ) == json_type_object) {
+ json_object_object_foreach(actions[idx].argsJ, key, val) {
+ json_object_object_add(queryJ, key, val);
+ }
+ } else {
+ json_object_object_add(queryJ, "args", actions[idx].argsJ);
+ }
+ }
+
+ int err = afb_service_call_sync(actions[idx].api, actions[idx].call, queryJ, &returnJ);
+ if (err) {
+ static const char*format = "DispatchOneSignal(Api) api=%s verb=%s args=%s";
+ if (afb_req_is_valid(request))afb_req_fail_f(request, "DISPATCH-CTL-MODE:API", format, actions[idx].label, actions[idx].api, actions[idx].call);
+ else AFB_ERROR(format, actions[idx].api, actions[idx].call, actions[idx].label);
+ return -1;
+ }
+ break;
+ }
+
+#ifdef CONTROL_SUPPORT_LUA
+ case CTL_MODE_LUA:
+ err = LuaCallFunc(source, &actions[idx], queryJ);
+ if (err) {
+ static const char*format = "DispatchOneSignal(Lua) label=%s func=%s args=%s";
+ if (afb_req_is_valid(request)) afb_req_fail_f(request, "DISPATCH-CTL-MODE:Lua", format, actions[idx].label, actions[idx].call, json_object_get_string(actions[idx].argsJ));
+ else AFB_ERROR(format, actions[idx].label, actions[idx].call, json_object_get_string(actions[idx].argsJ));
+ return -1;
+ }
+ break;
+#endif
+
+ case CTL_MODE_CB:
+ err = (*actions[idx].actionCB) (source, actions[idx].label, actions[idx].argsJ, queryJ, configHandle->plugin->context);
+ if (err) {
+ static const char*format = "DispatchOneSignal(Callback) label%s func=%s args=%s";
+ if (afb_req_is_valid(request)) afb_req_fail_f(request, "DISPATCH-CTL-MODE:Cb", format, actions[idx].label, actions[idx].call, json_object_get_string(actions[idx].argsJ));
+ else AFB_ERROR(format, actions[idx].label, actions[idx].call, json_object_get_string(actions[idx].argsJ));
+ return -1;
+ }
+ break;
+
+ default:
+ {
+ static const char*format = "DispatchOneSignal(unknown) mode control=%s action=%s";
+ AFB_ERROR(format, signals[index]->label);
+ if (afb_req_is_valid(request))afb_req_fail_f(request, "DISPATCH-CTL-MODE:Unknown", format, signals[index]->label);
+ }
+ }
+ }
+
+ // everything when fine
+ if (afb_req_is_valid(request)) afb_req_success(request, NULL, signals[index]->label);
+ return 0;
+}
+
+// Event name is mapped on control label and executed as a standard control
+/*
+void DispatchOneEvent(const char *evtLabel, json_object *eventJ) {
+ DispatchHandleT **events = configHandle->events;
+
+ (void) DispatchOneSignal(CTL_SOURCE_EVENT, events, evtLabel, eventJ, NULL_AFBREQ);
+}
+*/
+// Event name is mapped on control label and executed as a standard control
+
+int DispatchSources() {
+ if (!configHandle) return 1;
+
+ int err = 0;
+ DispatchHandleT **sources = configHandle->sources;
+ ssize_t i = 0, nSources = sizeof(*sources) / sizeof(DispatchHandleT);
+
+ while(i < nSources) {
+ const char* sourceLabel = sources[i++]->label;
+ err = DispatchOneSignal(CTL_SOURCE_ONLOAD, sources, sourceLabel, NULL, NULL_AFBREQ);
+ }
+
+ return err;
+}
+
+void ctlapi_dispatch(afb_req request) {
+ DispatchHandleT **signals = configHandle->signals;
+ json_object *queryJ, *argsJ=NULL;
+ const char *target;
+ DispatchSourceT source= CTL_SOURCE_UNKNOWN;
+
+ queryJ = afb_req_json(request);
+ int err = wrap_json_unpack(queryJ, "{s:s, s?i s?o !}", "target", &target, "source", &source, "args", &argsJ);
+ if (err) {
+ afb_req_fail_f(request, "CTL-DISPTACH-INVALID", "missing target or args not a valid json object query=%s", json_object_get_string(queryJ));
+ return;
+ }
+
+ (void) DispatchOneSignal(source, signals, target, argsJ, request);
+}
+
+// Wrapper to Lua2c plugin command add context dans delegate to LuaWrapper
+int DispatchOneL2c(lua_State* luaState, char *funcname, Lua2cFunctionT callback) {
+#ifndef CONTROL_SUPPORT_LUA
+ AFB_ERROR("DISPATCH-ONE-L2C: LUA support not selected (cf:CONTROL_SUPPORT_LUA) in config.cmake");
+ return 1;
+#else
+ int err=Lua2cWrapper(luaState, funcname, callback, configHandle->plugin->context);
+ return err;
+#endif
+}
+
+
+// List Avaliable Configuration Files
+
+void ctlapi_config(struct afb_req request) {
+ json_object*tmpJ;
+ char *dirList;
+ // Compile some default directories to browse
+ char defaultConfPath[CONTROL_MAXPATH_LEN];
+ strncpy(defaultConfPath, GetBindingDirPath(), sizeof(GetBindingDirPath()));
+ strncat(defaultConfPath, "/etc:", sizeof(defaultConfPath) - strlen(defaultConfPath) - 1);
+ strncat(defaultConfPath, GetBindingDirPath(), sizeof(defaultConfPath) - strlen(defaultConfPath) - 1);
+ strncat(defaultConfPath, "/data", sizeof(defaultConfPath) - strlen(defaultConfPath) - 1);
+
+
+ json_object* queryJ = afb_req_json(request);
+ if (queryJ && json_object_object_get_ex(queryJ, "cfgpath", &tmpJ)) {
+ dirList = strdup(json_object_get_string(tmpJ));
+ } else {
+
+ dirList = getenv("CONTROL_CONFIG_PATH");
+ if (!dirList) dirList = defaultConfPath;
+ AFB_NOTICE("CONFIG-MISSING: use default CONTROL_CONFIG_PATH=%s", defaultConfPath);
+ }
+
+ // get list of config file
+ struct json_object *responseJ = ScanForConfig(dirList, CTL_SCAN_RECURSIVE, "onload", "json");
+
+ if (json_object_array_length(responseJ) == 0) {
+ afb_req_fail(request, "CONFIGPATH:EMPTY", "No Config Found in CONTROL_CONFIG_PATH");
+ } else {
+ afb_req_success(request, responseJ, NULL);
+ }
+
+ return;
+}
+
+// unpack individual action object
+
+static int DispatchLoadOneAction(DispatchConfigT *controlConfig, json_object *actionJ, DispatchActionT *action) {
+ char *api = NULL, *verb = NULL, *callback = NULL, *lua = NULL, *function = NULL;
+ int err, modeCount = 0;
+
+ err = wrap_json_unpack(actionJ, "{s?s,s?s,s?s,s?s,s?s,s?s,s?s,s?o !}"
+ , "label", &action->label
+ , "info", &action->info
+ , "function", &function
+ , "callback", &callback
+ , "lua", &lua
+ , "api", &api, "verb", &verb
+ , "args", &action->argsJ);
+ if (err) {
+ AFB_ERROR("DISPATCH-LOAD-ACTION Missing something label|info|callback|lua|(api+verb)|args in %s", json_object_get_string(actionJ));
+ return -1;
+ }
+
+ // Generic way to specify a C or LUA actions
+ if(function) {
+ if(strcasestr(function, "lua")) {
+ action->mode = CTL_MODE_LUA;
+ action->call = function;
+ modeCount++;
+ }
+ else if(controlConfig->plugin) {
+ action->mode = CTL_MODE_CB;
+ action->call = callback;
+ modeCount++;
+
+ action->actionCB = dlsym(controlConfig->plugin->dlHandle, callback);
+ if (!action->actionCB) {
+ AFB_ERROR("DISPATCH-LOAD-ACTION fail to find calbback=%s in %s", callback, controlConfig->plugin->sharelib);
+ return -1;
+ }
+ }
+ // If label not already set then use function name
+ if(! &action->label) action->label = function;
+ }
+
+ if (lua) {
+ action->mode = CTL_MODE_LUA;
+ action->call = lua;
+ modeCount++;
+ // If label not already set then use function name
+ if(! &action->label) action->label = lua;
+ }
+
+ if (api && verb) {
+ action->mode = CTL_MODE_API;
+ action->api = api;
+ action->call = verb;
+ modeCount++;
+ char* apiVerb = strdup(api);
+ apiVerb = strncat(apiVerb, "/", sizeof(*apiVerb) - strlen(apiVerb) - 1);
+ apiVerb = strncat(apiVerb, verb, sizeof(*apiVerb) - strlen(apiVerb) - 1);
+ // If label not already set then use function name
+ if(! &action->label) action->label = apiVerb;
+ free(apiVerb);
+ }
+
+ if (callback && controlConfig->plugin) {
+ action->mode = CTL_MODE_CB;
+ action->call = callback;
+ modeCount++;
+
+ action->actionCB = dlsym(controlConfig->plugin->dlHandle, callback);
+ if (!action->actionCB) {
+ AFB_ERROR("DISPATCH-LOAD-ACTION fail to find calbback=%s in %s", callback, controlConfig->plugin->sharelib);
+ return -1;
+ }
+ }
+
+ // make sure at least one mode is selected
+ if (modeCount == 0) {
+ AFB_ERROR("DISPATCH-LOAD-ACTION No Action Selected lua|callback|(api+verb) in %s", json_object_get_string(actionJ));
+ return -1;
+ }
+
+ if (modeCount > 1) {
+ AFB_ERROR("DISPATCH-LOAD-ACTION:Too Many arguments lua|callback|(api+verb) in %s", json_object_get_string(actionJ));
+ return -1;
+ }
+ return 0;
+};
+
+static DispatchActionT *DispatchLoadActions(DispatchConfigT *controlConfig, json_object *actionsJ) {
+ int err;
+ DispatchActionT *actions;
+
+ // action array is close with a nullvalue;
+ if (json_object_get_type(actionsJ) == json_type_array) {
+ int count = json_object_array_length(actionsJ);
+ actions = calloc(count + 1, sizeof (DispatchActionT));
+
+ for (int idx = 0; idx < count; idx++) {
+ json_object *actionJ = json_object_array_get_idx(actionsJ, idx);
+ err = DispatchLoadOneAction(controlConfig, actionJ, &actions[idx]);
+ if (err) return NULL;
+ }
+
+ } else {
+ actions = calloc(2, sizeof (DispatchActionT));
+ err = DispatchLoadOneAction(controlConfig, actionsJ, &actions[0]);
+ if (err) return NULL;
+ }
+
+ return actions;
+}
+
+static void DispatchLoadPlugin(DispatchConfigT *controlConfig, json_object* sourcesJ, json_object* pluginJ)
+{
+ int err = 0;
+ json_object *lua2csJ = NULL;
+ DispatchPluginT *dPlugin= calloc(1, sizeof(DispatchPluginT));
+ controlConfig->plugin = dPlugin;
+ const char*ldSearchPath=NULL;
+
+ err = wrap_json_unpack(pluginJ, "{ss,s?s,s?s,ss,s?o!}",
+ "label", &dPlugin->label, "info", &dPlugin->info, "ldpath", &ldSearchPath, "sharelib", &dPlugin->sharelib, "lua2c", &lua2csJ);
+ if (err) {
+ AFB_ERROR("DISPATCH-LOAD-CONFIG:ONLOAD Plugin missing label|[info]|sharelib|[lua2c] in %s", json_object_get_string(sourcesJ));
+ return;
+ }
+
+ // if search path not in Json config file, then try default
+ if (!ldSearchPath) ldSearchPath= strncat(GetBindingDirPath(), "/data", sizeof(GetBindingDirPath()) - strlen(GetBindingDirPath()) - 1);
+
+ // search for default policy config file
+ json_object *pluginPathJ = ScanForConfig(ldSearchPath, CTL_SCAN_RECURSIVE, dPlugin->sharelib, NULL);
+ if (!pluginPathJ || json_object_array_length(pluginPathJ) == 0) {
+ AFB_ERROR("DISPATCH-LOAD-CONFIG:PLUGIN Missing plugin=%s in path=%s", dPlugin->sharelib, ldSearchPath);
+ return;
+ }
+
+ 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_ERROR("DISPATCH-LOAD-CONFIG:PLUGIN HOOPs invalid plugin file path = %s", json_object_get_string(pluginPathJ));
+ return;
+ }
+
+ if (json_object_array_length(pluginPathJ) > 1) {
+ AFB_WARNING("DISPATCH-LOAD-CONFIG:PLUGIN plugin multiple instances in searchpath will use %s/%s", fullpath, filename);
+ }
+
+ char pluginpath[CONTROL_MAXPATH_LEN];
+ strncpy(pluginpath, fullpath, sizeof (pluginpath));
+ strncat(pluginpath, "/", sizeof (pluginpath)-strlen(pluginpath)-1);
+ strncat(pluginpath, filename, sizeof (pluginpath)-strlen(pluginpath)-1);
+ dPlugin->dlHandle = dlopen(pluginpath, RTLD_NOW);
+ if (!dPlugin->dlHandle) {
+ AFB_ERROR("DISPATCH-LOAD-CONFIG:PLUGIN Fail to load pluginpath=%s err= %s", pluginpath, dlerror());
+ return;
+ }
+
+ CtlPluginMagicT *ctlPluginMagic = (CtlPluginMagicT*) dlsym(dPlugin->dlHandle, "CtlPluginMagic");
+ if (!ctlPluginMagic || ctlPluginMagic->magic != CTL_PLUGIN_MAGIC) {
+ AFB_ERROR("DISPATCH-LOAD-CONFIG:Plugin symbol'CtlPluginMagic' missing or != CTL_PLUGIN_MAGIC plugin=%s", pluginpath);
+ return;
+ } else {
+ AFB_NOTICE("DISPATCH-LOAD-CONFIG:Plugin %s successfully registered", ctlPluginMagic->label);
+ }
+
+ // Jose hack to make verbosity visible from sharelib
+ struct afb_binding_data_v2 *afbHidenData = dlsym(dPlugin->dlHandle, "afbBindingV2data");
+ if (afbHidenData) *afbHidenData = afbBindingV2data;
+
+ // Push lua2cWrapper @ into plugin
+ Lua2cWrapperT *lua2cInPlug = dlsym(dPlugin->dlHandle, "Lua2cWrap");
+#ifndef CONTROL_SUPPORT_LUA
+ if (lua2cInPlug) *lua2cInPlug = NULL;
+#else
+ // Lua2cWrapper is part of binder and not expose to dynamic link
+ if (lua2cInPlug) *lua2cInPlug = DispatchOneL2c;
+
+ {
+ int Lua2cAddOne(luaL_Reg *l2cFunc, const char* l2cName, int index) {
+ char funcName[CONTROL_MAXPATH_LEN];
+ strncpy(funcName, "lua2c_", sizeof(funcName));
+ strncat(funcName, l2cName, sizeof(funcName)-strlen(funcName)-1);
+
+ Lua2cFunctionT l2cFunction= (Lua2cFunctionT)dlsym(dPlugin->dlHandle, funcName);
+ if (!l2cFunction) {
+ AFB_ERROR("DISPATCH-LOAD-CONFIG:Plugin symbol'%s' missing err=%s", funcName, dlerror());
+ return 1;
+ }
+ l2cFunc[index].func=(void*)l2cFunction;
+ l2cFunc[index].name=strdup(l2cName);
+
+ return 0;
+ }
+
+ int errCount = 0;
+ luaL_Reg *l2cFunc=NULL;
+ int count=0;
+
+ // look on l2c command and push them to LUA
+ if (json_object_get_type(lua2csJ) == json_type_array) {
+ int length = json_object_array_length(lua2csJ);
+ l2cFunc = calloc(length + 1, sizeof (luaL_Reg));
+ for (count=0; count < length; count++) {
+ int err;
+ const char *l2cName = json_object_get_string(json_object_array_get_idx(lua2csJ, count));
+ err = Lua2cAddOne(l2cFunc, l2cName, count);
+ if (err) errCount++;
+ }
+ } else {
+ l2cFunc = calloc(2, sizeof (luaL_Reg));
+ const char *l2cName = json_object_get_string(lua2csJ);
+ errCount = Lua2cAddOne(l2cFunc, l2cName, 0);
+ count=1;
+ }
+ if (errCount) {
+ AFB_ERROR("DISPATCH-LOAD-CONFIG:Plugin %d symbols not found in plugin='%s'", errCount, pluginpath);
+ return;
+ } else {
+ dPlugin->l2cFunc= l2cFunc;
+ dPlugin->l2cCount= count;
+ }
+ }
+#endif
+ DispatchPluginInstallCbT ctlPluginOnload = dlsym(dPlugin->dlHandle, "CtlPluginOnload");
+ if (ctlPluginOnload) {
+ dPlugin->context = (*ctlPluginOnload) (controlConfig->label, controlConfig->version, controlConfig->info);
+ }
+}
+
+static DispatchHandleT *DispatchLoadSignal(DispatchConfigT *controlConfig, json_object *controlJ) {
+ json_object *actionsJ, *permissionsJ;
+ int err;
+
+ DispatchHandleT *dispatchHandle = calloc(1, sizeof (DispatchHandleT));
+ err = wrap_json_unpack(controlJ, "{ss,s?s,ss,ss,s?o,so !}"
+ , "id", &dispatchHandle->label
+ , "info", &dispatchHandle->info
+ , "source", &dispatchHandle->ssource
+ , "class", &dispatchHandle->sclass
+ , "permissions", &permissionsJ
+ , "onReceived", &actionsJ);
+ if (err) {
+ AFB_ERROR("DISPATCH-LOAD-CONFIG:CONTROL Missing something label|[info]|actions in %s", json_object_get_string(controlJ));
+ return NULL;
+ }
+
+ dispatchHandle->actions = DispatchLoadActions(controlConfig, actionsJ);
+ if (!dispatchHandle->actions) {
+ AFB_ERROR("DISPATCH-LOAD-CONFIG:CONTROL Error when parsing actions %s", dispatchHandle->label);
+ return NULL;
+ }
+ return dispatchHandle;
+}
+
+static DispatchHandleT *DispatchLoadSource(DispatchConfigT *controlConfig, json_object *sourcesJ) {
+ json_object *actionsJ = NULL, *pluginJ = NULL;
+ int err;
+
+ DispatchHandleT *dispatchSources = calloc(1, sizeof (DispatchHandleT));
+ err = wrap_json_unpack(sourcesJ, "{ss,s?s,s?o,s?o,s?o !}",
+ "api", &dispatchSources->label, "info", &dispatchSources->info, "plugin", &pluginJ, "actions", &actionsJ);
+ if (err) {
+ AFB_ERROR("DISPATCH-LOAD-CONFIG:ONLOAD Missing something label|[info]|[plugin]|[actions] in %s", json_object_get_string(sourcesJ));
+ return NULL;
+ }
+
+ // best effort to initialise everything before starting
+ err = afb_daemon_require_api(dispatchSources->label, 1);
+ if (err) {
+ AFB_WARNING("DISPATCH-LOAD-CONFIG:REQUIRE Fail to get=%s", dispatchSources->label);
+ }
+
+ if (pluginJ) {
+ DispatchLoadPlugin(controlConfig, sourcesJ, pluginJ);
+ }
+
+ dispatchSources->actions = DispatchLoadActions(controlConfig, actionsJ);
+ if (!dispatchSources->actions) {
+ AFB_ERROR("DISPATCH-LOAD-CONFIG:ONLOAD Error when parsing actions %s", dispatchSources->label);
+ return NULL;
+ }
+ return dispatchSources;
+}
+
+static DispatchConfigT *DispatchLoadConfig(const char* filepath) {
+ json_object *controlConfigJ, *ignoreJ;
+ int err;
+
+ // Load JSON file
+ controlConfigJ = json_object_from_file(filepath);
+ if (!controlConfigJ) {
+ AFB_ERROR("DISPATCH-LOAD-CONFIG:JsonLoad invalid JSON %s ", filepath);
+ return NULL;
+ }
+
+ AFB_INFO("DISPATCH-LOAD-CONFIG: loading config filepath=%s", filepath);
+
+ json_object *metadataJ = NULL, *sourcesJ = NULL, *signalsJ = NULL;
+ err = wrap_json_unpack(controlConfigJ, "{s?s,s?o,s?o,s?o !}", "$schema", &ignoreJ, "metadata", &metadataJ, "sources", &sourcesJ, "signals", &signalsJ);
+ if (err) {
+ AFB_ERROR("DISPATCH-LOAD-CONFIG Missing something metadata|[sources]|[signals] in %s", json_object_get_string(controlConfigJ));
+ return NULL;
+ }
+
+ DispatchConfigT *controlConfig = calloc(1, sizeof (DispatchConfigT));
+ if (metadataJ) {
+ const char*ctlname=NULL;
+ err = wrap_json_unpack(metadataJ, "{ss,s?s,s?s,ss !}", "label", &controlConfig->label, "version", &controlConfig->version, "name", &ctlname, "info", &controlConfig->info);
+ if (err) {
+ AFB_ERROR("DISPATCH-LOAD-CONFIG:METADATA Missing something label|version|[label] in %s", json_object_get_string(metadataJ));
+ return NULL;
+ }
+
+ // if ctlname is provided change process name now
+ if (ctlname) {
+ err= prctl(PR_SET_NAME, ctlname,NULL,NULL,NULL);
+ if (err) AFB_WARNING("Fail to set Process Name to:%s",ctlname);
+ }
+ }
+
+ if (sourcesJ) {
+ DispatchHandleT *dispatchHandle;
+
+ if (json_object_get_type(sourcesJ) != json_type_array) {
+ controlConfig->sources = (DispatchHandleT**) calloc(2, sizeof (void*));
+ dispatchHandle = DispatchLoadSource(controlConfig, sourcesJ);
+ controlConfig->sources[0] = dispatchHandle;
+ } else {
+ int length = json_object_array_length(sourcesJ);
+ controlConfig->sources = (DispatchHandleT**) calloc(length + 1, sizeof (void*));
+
+ for (int jdx = 0; jdx < length; jdx++) {
+ json_object *sourcesJ = json_object_array_get_idx(sourcesJ, jdx);
+ dispatchHandle = DispatchLoadSource(controlConfig, sourcesJ);
+ controlConfig->sources[jdx] = dispatchHandle;
+ }
+ }
+ }
+
+ if (signalsJ) {
+ DispatchHandleT* dispatchHandle;
+
+ if (json_object_get_type(signalsJ) != json_type_array) {
+ controlConfig->signals = (DispatchHandleT**) calloc(2, sizeof (void*));
+ dispatchHandle = DispatchLoadSignal(controlConfig, signalsJ);
+ controlConfig->signals[0] = dispatchHandle;
+ } else {
+ int length = json_object_array_length(signalsJ);
+ controlConfig->signals = (DispatchHandleT**) calloc(length + 1, sizeof (void*));
+
+ for (int jdx = 0; jdx < length; jdx++) {
+ json_object *controlJ = json_object_array_get_idx(signalsJ, jdx);
+ dispatchHandle = DispatchLoadSignal(controlConfig, controlJ);
+ controlConfig->signals[jdx] = dispatchHandle;
+ }
+ }
+ }
+/*
+ if (eventsJ) {
+ DispatchHandleT *dispatchHandle;
+
+ if (json_object_get_type(eventsJ) != json_type_array) {
+ controlConfig->events = (DispatchHandleT**) calloc(2, sizeof (void*));
+ dispatchHandle = DispatchLoadSignal(controlConfig, eventsJ);
+ controlConfig->events[0] = dispatchHandle;
+ } else {
+ int length = json_object_array_length(eventsJ);
+ controlConfig->events = (DispatchHandleT**) calloc(length + 1, sizeof (void*));
+
+ for (int jdx = 0; jdx < length; jdx++) {
+ json_object *eventJ = json_object_array_get_idx(eventsJ, jdx);
+ dispatchHandle = DispatchLoadSignal(controlConfig, eventJ);
+ controlConfig->events[jdx] = dispatchHandle;
+ }
+ }
+ }
+*/
+ return controlConfig;
+}
+
+
+// Load default config file at init
+
+int DispatchInit() {
+ int index, luaLoaded = 0;
+ char controlFile [CONTROL_MAXPATH_LEN];
+ // Compile some default directories to browse
+ char defaultConfPath[CONTROL_MAXPATH_LEN];
+ strncpy(defaultConfPath, GetBindingDirPath(), sizeof(GetBindingDirPath()));
+ strncat(defaultConfPath, "/etc:", sizeof(defaultConfPath) - strlen(defaultConfPath) - 1);
+ strncat(defaultConfPath, GetBindingDirPath(), sizeof(defaultConfPath) - strlen(defaultConfPath) - 1);
+ strncat(defaultConfPath, "/data", sizeof(defaultConfPath) - strlen(defaultConfPath) - 1);
+
+ const char *dirList = getenv("CONTROL_CONFIG_PATH");
+ if (!dirList) dirList = defaultConfPath;
+
+ strncpy(controlFile, CONTROL_CONFIG_PRE "-", CONTROL_MAXPATH_LEN);
+ strncat(controlFile, GetBinderName(), CONTROL_MAXPATH_LEN-strlen(controlFile)-1);
+
+ // search for default dispatch config file
+ json_object* responseJ = ScanForConfig(dirList, CTL_SCAN_RECURSIVE, controlFile, "json");
+
+ // We load 1st file others are just warnings
+ for (index = 0; index < json_object_array_length(responseJ); index++) {
+ json_object *entryJ = json_object_array_get_idx(responseJ, index);
+
+ char *filename;
+ char*fullpath;
+ int err = wrap_json_unpack(entryJ, "{s:s, s:s !}", "fullpath", &fullpath, "filename", &filename);
+ if (err) {
+ AFB_ERROR("DISPATCH-INIT HOOPs invalid JSON entry= %s", json_object_get_string(entryJ));
+ return -1;
+ }
+
+ if (index == 0) {
+ if (strcasestr(filename, controlFile)) {
+ char filepath[CONTROL_MAXPATH_LEN];
+ strncpy(filepath, fullpath, sizeof (filepath));
+ strncat(filepath, "/", sizeof (filepath)-strlen(filepath)-1);
+ strncat(filepath, filename, sizeof (filepath)-strlen(filepath)-1);
+ configHandle = DispatchLoadConfig(filepath);
+ if (!configHandle) {
+ AFB_ERROR("DISPATCH-INIT:ERROR Fail loading [%s]", filepath);
+ return -1;
+ }
+ luaLoaded = 1;
+ break;
+ }
+ } else {
+ AFB_WARNING("DISPATCH-INIT:WARNING Secondary Signal Config Ignored %s/%s", fullpath, filename);
+ }
+ }
+
+ // no dispatch config found remove control API from binder
+ if (!luaLoaded) {
+ AFB_WARNING("DISPATCH-INIT:WARNING (setenv CONTROL_CONFIG_PATH) No Config '%s-*.json' in '%s'", controlFile, dirList);
+ }
+
+ AFB_NOTICE("DISPATCH-INIT:SUCCES: Signal Dispatch Init");
+ return 0;
+}
diff --git a/signal-composer-binding/ctl-lua.c b/signal-composer-binding/ctl-lua.c
index e8e48fa..2773696 100644
--- a/signal-composer-binding/ctl-lua.c
+++ b/signal-composer-binding/ctl-lua.c
@@ -80,17 +80,17 @@ typedef enum {
*/
static LuaAfbContextT *LuaCtxCheck (lua_State *luaState, int index) {
- LuaAfbContextT *afbContext;
- //luaL_checktype(luaState, index, LUA_TUSERDATA);
- //afbContext = (LuaAfbContextT *)luaL_checkudata(luaState, index, CTX_TOKEN);
- luaL_checktype(luaState, index, LUA_TLIGHTUSERDATA);
- afbContext = (LuaAfbContextT *) lua_touserdata(luaState, index);
- 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;
+ LuaAfbContextT *afbContext;
+ //luaL_checktype(luaState, index, LUA_TUSERDATA);
+ //afbContext = (LuaAfbContextT *)luaL_checkudata(luaState, index, CTX_TOKEN);
+ luaL_checktype(luaState, index, LUA_TLIGHTUSERDATA);
+ afbContext = (LuaAfbContextT *) lua_touserdata(luaState, index);
+ 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, void *handle, const char* info) {
@@ -265,7 +265,7 @@ static json_object *LuaPopArgs (lua_State* luaState, int start) {
json_object *responseJ;
int stop = lua_gettop(luaState);
- if(stop-start <0) return NULL;
+ if(stop-start <0) goto OnErrorExit;
// start at 2 because we are using a function array lib
if (start == stop) {
@@ -277,13 +277,13 @@ static json_object *LuaPopArgs (lua_State* luaState, int start) {
json_object *argJ=LuaPopOneArg (luaState, idx);
if (!argJ) goto OnErrorExit;
json_object_array_add(responseJ, argJ);
- }
+ }
}
return responseJ;
- OnErrorExit:
- return NULL;
+ OnErrorExit:
+ return NULL;
}
@@ -660,18 +660,14 @@ int LuaCallFunc (DispatchSourceT source, DispatchActionT *action, json_object *q
err=lua_pcall(luaState, count, 1, 0);
if (err) {
AFB_ERROR("LuaCallFunc Fail calling %s error=%s", func, lua_tostring(luaState,-1));
- goto OnErrorExit;
+ return -1;
}
// return LUA script value
int rc= (int)lua_tointeger(luaState, -1);
return rc;
-
- OnErrorExit:
- return -1;
}
-
// Execute LUA code from received API request
static void LuaDoAction (LuaDoActionT action, afb_req request) {
@@ -727,7 +723,6 @@ static void LuaDoAction (LuaDoActionT action, afb_req request) {
char *filename; char*fullpath;
char luaScriptPath[CONTROL_MAXPATH_LEN];
int index;
- BPaths BindingPaths = GetBindingDirsPath();
// scan luascript search path once
static json_object *luaScriptPathJ =NULL;
@@ -743,10 +738,10 @@ static void LuaDoAction (LuaDoActionT action, afb_req request) {
// search for filename=script in CONTROL_LUA_PATH
if (!luaScriptPathJ) {
- strncpy(luaScriptPath,CONTROL_DOSCRIPT_PRE, sizeof(luaScriptPath));
+ strncpy(luaScriptPath, CONTROL_DOSCRIPT_PRE, sizeof(luaScriptPath));
strncat(luaScriptPath,"-", sizeof(luaScriptPath)-strlen(luaScriptPath)-1);
strncat(luaScriptPath,target, sizeof(luaScriptPath)-strlen(luaScriptPath)-1);
- luaScriptPathJ= ScanForConfig(BindingPaths.etcdir, CTL_SCAN_RECURSIVE,luaScriptPath,".lua");
+ luaScriptPathJ= ScanForConfig(strncat(GetBindingDirPath(), "/etc", sizeof(GetBindingDirPath()) - strlen(GetBindingDirPath()) - 1), 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);
@@ -841,7 +836,7 @@ static int LuaTimerClear (lua_State* luaState) {
// Get Timer Handle
LuaAfbContextT *afbContext= LuaCtxCheck(luaState, LUA_FIST_ARG);
- if (!afbContext) goto OnErrorExit;
+ if (!afbContext) return -1;
// retrieve useful information opaque handle
TimerHandleT *timerHandle = (TimerHandleT*)afbContext->handle;
@@ -850,15 +845,12 @@ static int LuaTimerClear (lua_State* luaState) {
TimerEvtStop(timerHandle);
return 0; //happy end
-
-OnErrorExit:
- return 1;
}
static int LuaTimerGet (lua_State* luaState) {
// Get Timer Handle
LuaAfbContextT *afbContext= LuaCtxCheck(luaState, LUA_FIST_ARG);
- if (!afbContext) goto OnErrorExit;
+ if (!afbContext) return 0;
// retrieve useful information opaque handle
TimerHandleT *timerHandle = (TimerHandleT*)afbContext->handle;
@@ -876,9 +868,6 @@ static int LuaTimerGet (lua_State* luaState) {
json_object_put(responseJ);
return count; // return argument
-
-OnErrorExit:
- return 0;
}
// Timer Callback
@@ -894,7 +883,7 @@ static int LuaTimerSetCB (void *handle) {
// Push timer handle
LuaAfbContextT *afbContext= LuaCtxPush(luaState, NULL_AFBREQ, contextCB->handle, timerHandle->label);
- if (!afbContext) goto OnErrorExit;
+ if (!afbContext) return 1;
count=1;
// Push user Context
@@ -903,7 +892,7 @@ static int LuaTimerSetCB (void *handle) {
int err=lua_pcall(luaState, count, LUA_MULTRET, 0);
if (err) {
AFB_ERROR ("LUA-TIMER-CB:FAIL response=%s err=%s", json_object_get_string(contextCB->context), lua_tostring(luaState,-1));
- goto OnErrorExit;
+ return 1;
}
// get return parameter
@@ -916,9 +905,6 @@ static int LuaTimerSetCB (void *handle) {
LuaCtxFree(afbContext);
}
return 0; // By default we are happy
-
- OnErrorExit:
- return 1; // stop timer
}
static int LuaTimerSet(lua_State* luaState) {
@@ -997,12 +983,12 @@ int LuaLibInit () {
// search for default policy config file
char fullprefix[CONTROL_MAXPATH_LEN];
- strncpy (fullprefix, CONTROL_CONFIG_PRE "-", sizeof(fullprefix));
+ strncpy (fullprefix, CONTROL_CONFIG_PRE, sizeof(fullprefix));
strncat (fullprefix, GetBinderName(), sizeof(fullprefix)-strlen(fullprefix)-1);
strncat (fullprefix, "-", sizeof(fullprefix)-strlen(fullprefix)-1);
const char *dirList= getenv("CONTROL_LUA_PATH");
- if (!dirList) dirList= GetBindingDirsPath().etcdir;
+ if (!dirList) dirList= strncat(GetBindingDirPath(), "/etc", sizeof(GetBindingDirPath()) - strlen(GetBindingDirPath()) - 1);
json_object *luaScriptPathJ = ScanForConfig(dirList , CTL_SCAN_RECURSIVE, fullprefix, "lua");
@@ -1026,7 +1012,7 @@ int LuaLibInit () {
luaDefaultEvt->event = afb_daemon_make_event(CONTROL_LUA_EVENT);
if (!afb_event_is_valid(luaDefaultEvt->event)) {
AFB_ERROR ("POLCTL_INIT: Cannot register lua-events=%s ", CONTROL_LUA_EVENT);
- goto OnErrorExit;;
+ goto OnErrorExit;
}
// load+exec any file found in LUA search path
diff --git a/signal-composer-binding/ctl-lua.h b/signal-composer-binding/ctl-lua.h
index df2379f..c5f472d 100644
--- a/signal-composer-binding/ctl-lua.h
+++ b/signal-composer-binding/ctl-lua.h
@@ -12,60 +12,6 @@
#include "signal-composer-binding.hpp"
-#ifndef CONTROL_DOSCRIPT_PRE
-#define CONTROL_DOSCRIPT_PRE "doscript"
-#endif
-
-#ifndef CONTROL_CONFIG_PRE
-#define CONTROL_CONFIG_PRE "onload"
-#endif
-
-#ifndef CONTROL_LUA_EVENT
-#define CONTROL_LUA_EVENT "luaevt"
-#endif
-
-typedef int (*timerCallbackT)(void *context);
-
-typedef struct TimerHandleS {
- int count;
- int delay;
- const char*label;
- void *context;
- timerCallbackT callback;
- sd_event_source *evtSource;
-} TimerHandleT;
-
-int TimerEvtInit (void);
-afb_event TimerEvtGet(void);
-void TimerEvtStart(TimerHandleT *timerHandle, timerCallbackT callback, void *context);
-void TimerEvtStop(TimerHandleT *timerHandle);
-
-typedef enum {
- CTL_MODE_NONE=0,
- CTL_MODE_API,
- CTL_MODE_CB,
- CTL_MODE_LUA,
-} CtlRequestModeT;
-
-typedef enum {
- CTL_SOURCE_CLOSE=-1,
- CTL_SOURCE_UNKNOWN=0,
- CTL_SOURCE_ONLOAD=1,
- CTL_SOURCE_OPEN=2,
- CTL_SOURCE_EVENT=3,
-} DispatchSourceT;
-
-typedef struct DispatchActionS{
- const char *info;
- const char* label;
- CtlRequestModeT mode;
- const char* api;
- const char* call;
- json_object *argsJ;
- int timeout;
- int (*actionCB)(DispatchSourceT source, const char*label, json_object *argsJ, json_object *queryJ, void *context);
-} DispatchActionT;
-
typedef enum {
LUA_DOCALL,
LUA_DOSTRING,
@@ -73,3 +19,10 @@ typedef enum {
} LuaDoActionT;
typedef int (*Lua2cFunctionT)(char *funcname, json_object *argsJ, void*context);
+
+typedef int (*Lua2cWrapperT) (lua_State* luaState, char *funcname, Lua2cFunctionT callback);
+
+int LuaLibInit ();
+void LuaL2cNewLib(const char *label, luaL_Reg *l2cFunc, int count);
+int Lua2cWrapper(lua_State* luaState, char *funcname, Lua2cFunctionT callback, void *context);
+int LuaCallFunc (DispatchSourceT source, DispatchActionT *action, json_object *queryJ) ;
diff --git a/signal-composer-binding/signal-composer-apidef.h b/signal-composer-binding/signal-composer-apidef.h
index f986692..a652de5 100644
--- a/signal-composer-binding/signal-composer-apidef.h
+++ b/signal-composer-binding/signal-composer-apidef.h
@@ -4,45 +4,45 @@ static const char _afb_description_v2_signals_composer[] =
"a-3.0/default-schema.json\",\"info\":{\"description\":\"\",\"title\":\"s"
"ignals-composer-service\",\"version\":\"4.0\",\"x-binding-c-generator\":"
"{\"api\":\"signals-composer\",\"version\":2,\"prefix\":\"\",\"postfix\":"
- "\"\",\"start\":null,\"onevent\":\"onEvent\",\"init\":\"init_service\",\""
- "scope\":\"\",\"private\":false}},\"servers\":[{\"url\":\"ws://{host}:{po"
- "rt}/api/monitor\",\"description\":\"Signals composer API connected to lo"
- "w level AGL services\",\"variables\":{\"host\":{\"default\":\"localhost\""
- "},\"port\":{\"default\":\"1234\"}},\"x-afb-events\":[{\"$ref\":\"#/compo"
- "nents/schemas/afb-event\"}]}],\"components\":{\"schemas\":{\"afb-reply\""
- ":{\"$ref\":\"#/components/schemas/afb-reply-v2\"},\"afb-event\":{\"$ref\""
- ":\"#/components/schemas/afb-event-v2\"},\"afb-reply-v2\":{\"title\":\"Ge"
- "neric response.\",\"type\":\"object\",\"required\":[\"jtype\",\"request\""
- "],\"properties\":{\"jtype\":{\"type\":\"string\",\"const\":\"afb-reply\""
- "},\"request\":{\"type\":\"object\",\"required\":[\"status\"],\"propertie"
- "s\":{\"status\":{\"type\":\"string\"},\"info\":{\"type\":\"string\"},\"t"
- "oken\":{\"type\":\"string\"},\"uuid\":{\"type\":\"string\"},\"reqid\":{\""
- "type\":\"string\"}}},\"response\":{\"type\":\"object\"}}},\"afb-event-v2"
- "\":{\"type\":\"object\",\"required\":[\"jtype\",\"event\"],\"properties\""
- ":{\"jtype\":{\"type\":\"string\",\"const\":\"afb-event\"},\"event\":{\"t"
- "ype\":\"string\"},\"data\":{\"type\":\"object\"}}}},\"x-permissions\":{}"
- ",\"responses\":{\"200\":{\"description\":\"A complex object array respon"
- "se\",\"content\":{\"application/json\":{\"schema\":{\"$ref\":\"#/compone"
- "nts/schemas/afb-reply\"}}}}}},\"paths\":{\"/subscribe\":{\"description\""
- ":\"Subscribe to a signal object\",\"parameters\":[{\"in\":\"query\",\"na"
- "me\":\"event\",\"required\":false,\"schema\":{\"type\":\"string\"}}],\"r"
- "esponses\":{\"200\":{\"$ref\":\"#/components/responses/200\"}}},\"/unsub"
- "scribe\":{\"description\":\"Unsubscribe previously suscribed signal obje"
- "cts.\",\"parameters\":[{\"in\":\"query\",\"name\":\"event\",\"required\""
- ":false,\"schema\":{\"type\":\"string\"}}],\"responses\":{\"200\":{\"$ref"
- "\":\"#/components/responses/200\"}}},\"/get\":{\"description\":\"Get inf"
- "ormations about a resource or element\",\"responses\":{\"200\":{\"$ref\""
- ":\"#/components/responses/200\"}}},\"/load\":{\"description\":\"Load con"
- "fig file in directory passed as argument searching for pattern 'sig' in "
- "filename\",\"parameters\":[{\"in\":\"query\",\"name\":\"path\",\"require"
- "d\":true,\"schema\":{\"type\":\"string\"}}],\"responses\":{\"200\":{\"$r"
- "ef\":\"#/components/responses/200\"}}}}}"
+ "\"\",\"start\":null,\"onevent\":\"onEvent\",\"preinit\":\"loadConf\",\"i"
+ "nit\":\"execConf\",\"scope\":\"\",\"private\":false}},\"servers\":[{\"ur"
+ "l\":\"ws://{host}:{port}/api/monitor\",\"description\":\"Signals compose"
+ "r API connected to low level AGL services\",\"variables\":{\"host\":{\"d"
+ "efault\":\"localhost\"},\"port\":{\"default\":\"1234\"}},\"x-afb-events\""
+ ":[{\"$ref\":\"#/components/schemas/afb-event\"}]}],\"components\":{\"sch"
+ "emas\":{\"afb-reply\":{\"$ref\":\"#/components/schemas/afb-reply-v2\"},\""
+ "afb-event\":{\"$ref\":\"#/components/schemas/afb-event-v2\"},\"afb-reply"
+ "-v2\":{\"title\":\"Generic response.\",\"type\":\"object\",\"required\":"
+ "[\"jtype\",\"request\"],\"properties\":{\"jtype\":{\"type\":\"string\",\""
+ "const\":\"afb-reply\"},\"request\":{\"type\":\"object\",\"required\":[\""
+ "status\"],\"properties\":{\"status\":{\"type\":\"string\"},\"info\":{\"t"
+ "ype\":\"string\"},\"token\":{\"type\":\"string\"},\"uuid\":{\"type\":\"s"
+ "tring\"},\"reqid\":{\"type\":\"string\"}}},\"response\":{\"type\":\"obje"
+ "ct\"}}},\"afb-event-v2\":{\"type\":\"object\",\"required\":[\"jtype\",\""
+ "event\"],\"properties\":{\"jtype\":{\"type\":\"string\",\"const\":\"afb-"
+ "event\"},\"event\":{\"type\":\"string\"},\"data\":{\"type\":\"object\"}}"
+ "}},\"x-permissions\":{},\"responses\":{\"200\":{\"description\":\"A comp"
+ "lex object array response\",\"content\":{\"application/json\":{\"schema\""
+ ":{\"$ref\":\"#/components/schemas/afb-reply\"}}}}}},\"paths\":{\"/subscr"
+ "ibe\":{\"description\":\"Subscribe to a signal object\",\"parameters\":["
+ "{\"in\":\"query\",\"name\":\"event\",\"required\":false,\"schema\":{\"ty"
+ "pe\":\"string\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/respo"
+ "nses/200\"}}},\"/unsubscribe\":{\"description\":\"Unsubscribe previously"
+ " suscribed signal objects.\",\"parameters\":[{\"in\":\"query\",\"name\":"
+ "\"event\",\"required\":false,\"schema\":{\"type\":\"string\"}}],\"respon"
+ "ses\":{\"200\":{\"$ref\":\"#/components/responses/200\"}}},\"/get\":{\"d"
+ "escription\":\"Get informations about a resource or element\",\"response"
+ "s\":{\"200\":{\"$ref\":\"#/components/responses/200\"}}},\"/loadConf\":{"
+ "\"description\":\"Load config file in directory passed as argument searc"
+ "hing for pattern 'sig' in filename\",\"parameters\":[{\"in\":\"query\",\""
+ "name\":\"path\",\"required\":true,\"schema\":{\"type\":\"string\"}}],\"r"
+ "esponses\":{\"200\":{\"$ref\":\"#/components/responses/200\"}}}}}"
;
void subscribe(struct afb_req req);
void unsubscribe(struct afb_req req);
void get(struct afb_req req);
- void load(struct afb_req req);
+ void loadConf(struct afb_req req);
static const struct afb_verb_v2 _afb_verbs_v2_signals_composer[] = {
{
@@ -67,8 +67,8 @@ static const struct afb_verb_v2 _afb_verbs_v2_signals_composer[] = {
.session = AFB_SESSION_NONE_V2
},
{
- .verb = "load",
- .callback = load,
+ .verb = "loadConf",
+ .callback = loadConf,
.auth = NULL,
.info = "Load config file in directory passed as argument searching for pattern 'sig' in filename",
.session = AFB_SESSION_NONE_V2
@@ -87,8 +87,8 @@ const struct afb_binding_v2 afbBindingV2 = {
.specification = _afb_description_v2_signals_composer,
.info = "",
.verbs = _afb_verbs_v2_signals_composer,
- .preinit = NULL,
- .init = init_service,
+ .preinit = loadConf,
+ .init = execConf,
.onevent = onEvent,
.noconcurrency = 0
};
diff --git a/signal-composer-binding/signal-composer-apidef.json b/signal-composer-binding/signal-composer-apidef.json
index 78080f1..7085dc2 100644
--- a/signal-composer-binding/signal-composer-apidef.json
+++ b/signal-composer-binding/signal-composer-apidef.json
@@ -12,7 +12,8 @@
"postfix": "",
"start": null ,
"onevent": "onEvent",
- "init": "init_service",
+ "preinit": "loadConf",
+ "init": "execConf",
"scope": "",
"private": false
}
@@ -130,7 +131,7 @@
"200": {"$ref": "#/components/responses/200"}
}
},
- "/load": {
+ "/loadConf": {
"description": "Load config file in directory passed as argument searching for pattern 'sig' in filename",
"parameters": [
{
diff --git a/signal-composer-binding/signal-composer-binding.cpp b/signal-composer-binding/signal-composer-binding.cpp
index 24b81f6..2ca1dec 100644
--- a/signal-composer-binding/signal-composer-binding.cpp
+++ b/signal-composer-binding/signal-composer-binding.cpp
@@ -15,12 +15,15 @@
* limitations under the License.
*/
+#include <ctl-config.h>
+
#include "signal-composer-binding.hpp"
#include "signal-composer-apidef.h"
#include "wrap-json.h"
#include "signal-composer.hpp"
SignalComposer SigComp;
+static CtlConfigT *ctlConfig=NULL;
/// @brief callback for receiving message from low binding. Treatment itself is made in SigComp class.
void onEvent(const char *event, json_object *object)
@@ -46,7 +49,7 @@ void unsubscribe(afb_req request)
}
/// @brief verb that loads JSON configuration (old SigComp.json file now)
-void load(afb_req request)
+void loadConf(afb_req request)
{
json_object* args = afb_req_json(request);
const char* confd;
@@ -79,12 +82,23 @@ int ticked(sd_event_source *source, uint64_t t, void* data)
return 0;
}
-/// @brief Initialize the binding.
-///
-/// @return Exit code, zero if success.
-int init_service()
+int loadConf()
{
- AFB_DEBUG("SigComp level binding is initializing");
- AFB_NOTICE("SigComp level binding is initialized and running");
- return 0;
+ int errcount=0;
+
+ ctlConfig = CtlConfigLoad();
+
+ #ifdef CONTROL_SUPPORT_LUA
+ errcount += LuaLibInit();
+ #endif
+
+ return errcount;
+}
+
+int execConf()
+{
+ int err = CtlConfigExec();
+
+ AFB_DEBUG ("Signal Composer Control configuration Done errcount=%d", errcount);
+ return errcount;
}
diff --git a/signal-composer-binding/signal-composer-binding.hpp b/signal-composer-binding/signal-composer-binding.hpp
index 3b512f3..7bf1df1 100644
--- a/signal-composer-binding/signal-composer-binding.hpp
+++ b/signal-composer-binding/signal-composer-binding.hpp
@@ -13,6 +13,7 @@ extern "C"
};
#endif
- void onEvent(const char *event, struct json_object *object);
- int init_service();
- int ticked(sd_event_source *source, uint64_t t, void *data);
+void onEvent(const char *event, struct json_object *object);
+int loadConf();
+int execConf();
+int ticked(sd_event_source *source, uint64_t t, void *data);
diff --git a/signal-composer-binding/signal-conf.cpp b/signal-composer-binding/signal-conf.cpp
new file mode 100644
index 0000000..73f2450
--- /dev/null
+++ b/signal-composer-binding/signal-conf.cpp
@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) 2015, 2016 "IoT.bzh"
+ * Author "Romain Forlot" <romain.forlot@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.
+ */
diff --git a/signal-composer-binding/signal-conf.hpp b/signal-composer-binding/signal-conf.hpp
new file mode 100644
index 0000000..3698135
--- /dev/null
+++ b/signal-composer-binding/signal-conf.hpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015, 2016 "IoT.bzh"
+ * Author "Romain Forlot" <romain.forlot@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.
+ */
+
+#include <json.hpp>
+
+ class signalConfiguration
+{
+private:
+ nlohmann::json config_;
+
+public:
+ void parseConfig(std::string configPath);
+}
diff --git a/signal-composer-binding/signal.hpp b/signal-composer-binding/signal.hpp
new file mode 100644
index 0000000..94878c9
--- /dev/null
+++ b/signal-composer-binding/signal.hpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2015, 2016 "IoT.bzh"
+ * Author "Romain Forlot" <romain.forlot@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.
+*/
+
+#pragma once
+
+#include <string>
+#include <memory>
+#include <vector>
+
+class Signal;
+
+class Signal
+{
+private:
+ std::string api_;
+ std::string name_;
+ std::vector<> history_;
+ float frequency_;
+ st::string unit_;
+ float min_;
+ float max_;
+ float last_;
+
+ std::vector<std::shared_ptr<Signal>> Observers_;
+
+public:
+ void notify();
+ void infiniteRecursionCheck();
+ void attach();
+ void detach();
+ void notify();
+}