From eb6a78be524aaee70fca55b86a7b065763591178 Mon Sep 17 00:00:00 2001 From: fulup Date: Thu, 10 Aug 2017 17:37:24 +0200 Subject: Work in Progress --- Controler-afb/CMakeLists.txt | 24 ++- Controler-afb/ctl-binding.c | 8 +- Controler-afb/ctl-binding.h | 80 ++++------ Controler-afb/ctl-dispatch.c | 318 ++++++++++++++++++++++++++++++++++++++ Controler-afb/ctl-events.c | 16 ++ Controler-afb/ctl-lua.c | 281 +++++++++++++++++++++------------ Controler-afb/ctl-plugin-sample.c | 64 ++++++++ Controler-afb/ctl-policy.c | 196 ----------------------- 8 files changed, 637 insertions(+), 350 deletions(-) create mode 100644 Controler-afb/ctl-dispatch.c create mode 100644 Controler-afb/ctl-plugin-sample.c delete mode 100644 Controler-afb/ctl-policy.c (limited to 'Controler-afb') diff --git a/Controler-afb/CMakeLists.txt b/Controler-afb/CMakeLists.txt index afebdf2..08bc377 100644 --- a/Controler-afb/CMakeLists.txt +++ b/Controler-afb/CMakeLists.txt @@ -33,7 +33,7 @@ endmacro(SET_TARGET_GENSKEL) PROJECT_TARGET_ADD(control-afb) # Define project Targets - ADD_LIBRARY(${TARGET_NAME} MODULE ctl-binding.c ctl-events.c ctl-policy.c ctl-lua.c + ADD_LIBRARY(${TARGET_NAME} MODULE ctl-binding.c ctl-events.c ctl-dispatch.c ctl-lua.c ) # Generate API-v2 hat from OpenAPI json definition @@ -45,7 +45,6 @@ PROJECT_TARGET_ADD(control-afb) LABELS "BINDING" LINK_FLAGS ${BINDINGS_LINK_FLAG} OUTPUT_NAME ${TARGET_NAME} - ) # Library dependencies (include updates automatically) @@ -56,5 +55,24 @@ PROJECT_TARGET_ADD(control-afb) # installation directory INSTALL(TARGETS ${TARGET_NAME} - LIBRARY DESTINATION ${BINDINGS_INSTALL_DIR}) + LIBRARY DESTINATION CMAKE_INSTALL_PREFIX ) + +PROJECT_TARGET_ADD(ctl-plugin-sample) + + # Define targets + ADD_LIBRARY(${TARGET_NAME} MODULE ctl-plugin-sample.c) + + # Alsa Plugin properties + SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES + PREFIX "audio-" + OUTPUT_NAME ${TARGET_NAME} + ) + # Library dependencies (include updates automatically) + TARGET_LINK_LIBRARIES(${TARGET_NAME} + ${link_libraries} + ) + + # installation directory + INSTALL(TARGETS ${TARGET_NAME} + LIBRARY DESTINATION ${BINDINGS_INSTALL_DIR}/controler) diff --git a/Controler-afb/ctl-binding.c b/Controler-afb/ctl-binding.c index 45c4e1c..fe9388f 100644 --- a/Controler-afb/ctl-binding.c +++ b/Controler-afb/ctl-binding.c @@ -31,17 +31,17 @@ PUBLIC void ctlapi_navigation (afb_req request) { - ctlapi_authorize (CTLAPI_NAVIGATION, request); + ctlapi_dispatch (CTLAPI_NAVIGATION, request); } PUBLIC void ctlapi_multimedia (afb_req request) { - ctlapi_authorize (CTLAPI_MULTIMEDIA, request); + ctlapi_dispatch (CTLAPI_MULTIMEDIA, request); } PUBLIC void ctlapi_emergency (afb_req request) { - ctlapi_authorize (CTLAPI_EMERGENCY, request); + ctlapi_dispatch (CTLAPI_EMERGENCY, request); } PUBLIC void ctlapi_monitor (afb_req request) { @@ -65,7 +65,7 @@ PUBLIC int CtlBindingInit () { int errcount=0; errcount += TimerEvtInit(); - errcount += PolicyInit(); + errcount += DispatchInit(); errcount += LuaLibInit(); AFB_DEBUG ("Audio Policy Control Binding Done errcount=%d", errcount); diff --git a/Controler-afb/ctl-binding.h b/Controler-afb/ctl-binding.h index e53d2ca..77d1b6e 100644 --- a/Controler-afb/ctl-binding.h +++ b/Controler-afb/ctl-binding.h @@ -14,33 +14,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include -#include "audio-common.h" #ifndef CONTROLER_BINDING_INCLUDE #define CONTROLER_BINDING_INCLUDE +#define AFB_BINDING_VERSION 2 +#include +#include + +#ifndef PUBLIC + #define PUBLIC +#endif +#define STATIC static + // polctl-binding.c PUBLIC int CtlBindingInit (); // ctl-timerevt.c // ---------------------- -#define DEFAULT_PAUSE_DELAY 3000 -#define DEFAULT_TEST_COUNT 1 -typedef int (*timerCallbackT)(void *context); -typedef struct { - int value; - const char *label; -} AutoTestCtxT; - -typedef struct TimerHandleS { - int count; - int delay; - AutoTestCtxT *context; - timerCallbackT callback; - sd_event_source *evtSource; -} TimerHandleT; + PUBLIC int TimerEvtInit (void); PUBLIC afb_event TimerEvtGet(void); @@ -49,46 +42,39 @@ PUBLIC void ctlapi_event_test (afb_req request); // ctl-policy // ----------- -typedef struct PolicyActionS{ - const char* label; - const char* api; - const char* verb; - json_object *argsJ; - const char *info; - int timeout; - json_object* (*actionCB)(struct PolicyActionS *action,json_object *response, void *context); -} PolicyActionT; - -typedef struct { - const char* label; - const char *info; - const char *version; - void *context; -} PolicyHandleT; - -typedef struct { - char *sharelib; - void *dlHandle; - PolicyHandleT **handle; - PolicyActionT **onload; - PolicyActionT **events; - PolicyActionT **controls; -} PolicyCtlConfigT; - typedef enum { CTLAPI_NAVIGATION, CTLAPI_MULTIMEDIA, CTLAPI_EMERGENCY, CTL_NONE=-1 -} PolicyCtlEnumT; +} DispatchCtlEnumT; -PUBLIC int PolicyInit(void); -PUBLIC json_object* ScanForConfig (char* searchPath, char * pre, char *ext); -PUBLIC void ctlapi_authorize (PolicyCtlEnumT control, afb_req request); + +typedef enum { + CTL_MODE_NONE=0, + CTL_MODE_API, + CTL_MODE_CB, + CTL_MODE_LUA, +} CtlRequestModeT; + +typedef struct DispatchActionS{ + const char *info; + const char* label; + CtlRequestModeT mode; + const char* api; + const char* call; + json_object *argsJ; + int timeout; + json_object* (*actionCB)(struct DispatchActionS *action,json_object *response, void *context); +} DispatchActionT; + +PUBLIC int DispatchInit(void); +PUBLIC void ctlapi_dispatch (DispatchCtlEnumT control, afb_req request); // ctl-lua.c PUBLIC int LuaLibInit (); +PUBLIC json_object* ScanForConfig (char* searchPath, char * pre, char *ext); PUBLIC void ctlapi_lua_docall (afb_req request); PUBLIC void ctlapi_lua_dostring (afb_req request); PUBLIC void ctlapi_lua_doscript (afb_req request); diff --git a/Controler-afb/ctl-dispatch.c b/Controler-afb/ctl-dispatch.c new file mode 100644 index 0000000..cda3431 --- /dev/null +++ b/Controler-afb/ctl-dispatch.c @@ -0,0 +1,318 @@ +/* + * Copyright (C) 2016 "IoT.bzh" + * Author Fulup Ar Foll + * + * 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 +#include +#include + + +#include + +#include "wrap-json.h" +#include "ctl-binding.h" + + +typedef struct { + const char* label; + const char *info; + const char *version; + DispatchActionT *actions; +} DispatchHandleT; + + +typedef struct { + const char* label; + const char *info; + const char *version; + char *plugin; + void *dlHandle; + DispatchHandleT *onload; + DispatchHandleT *events; + DispatchHandleT *controls; +} DispatchConfigT; + +STATIC DispatchConfigT *ctlHandle = NULL; + +PUBLIC void ctlapi_dispatch (DispatchCtlEnumT control, afb_req request) { + json_object*tmpJ; + + json_object* argsJ= afb_req_json(request); + int done=json_object_object_get_ex(argsJ, "closing", &tmpJ); + if (done) return; + +} + +// List Avaliable Configuration Files +PUBLIC void ctlapi_config (struct afb_req request) { + json_object*tmpJ; + char *dirList; + + json_object* argsJ = afb_req_json(request); + if (argsJ && json_object_object_get_ex (argsJ, "cfgpath" , &tmpJ)) { + dirList = strdup (json_object_get_string(tmpJ)); + } else { + dirList = strdup (CONTROL_DISPATCH_PATH); + AFB_NOTICE ("CONFIG-MISSING: use default CONTROL_DISPATCH_PATH=%s", CONTROL_DISPATCH_PATH); + } + + // get list of config file + struct json_object *responseJ = ScanForConfig(dirList, "onload", "json"); + + if (json_object_array_length(responseJ) == 0) { + afb_req_fail(request, "CONFIGPATH:EMPTY", "No Config Found in CONTROL_DISPATCH_PATH"); + } else { + afb_req_success(request, responseJ, NULL); + } + + return; +} + +// unpack individual action object +STATIC int DispatchLoadOneAction (json_object *actionJ, DispatchActionT *action) { + char *api, *verb, *callback, *lua; + int err, modeCount=0; + + err= wrap_json_unpack(actionJ, "{ss,s?s,s?s,s?o,s?s,s?s,s?s !}" + , "label",&action->label, "info",&action->info, "callback",&callback, "lua", &lua, "args",&action->argsJ, "api",&api, "verb", &verb); + if (err) { + AFB_ERROR ("DISPATCH-LOAD-ACTION Missing something label|info|callback|lua|(api+verb)|args in %s", json_object_get_string(actionJ)); + goto OnErrorExit; + } + + if (lua) { + action->mode = CTL_MODE_LUA; + action->call=lua; + modeCount++; + } + + if (api && verb) { + action->mode = CTL_MODE_API; + action->api=api; + action->call=verb; + modeCount++; + } + + if (callback) { + action->mode = CTL_MODE_CB; + action->call=callback; + modeCount++; + + action->actionCB = dlsym(ctlHandle->dlHandle, callback); + if (!action->actionCB) { + AFB_ERROR ("DISPATCH-LOAD-ACTION fail to find calbback=%s in %s", callback, ctlHandle->plugin); + goto OnErrorExit; + } + } + + // make sure at least one mode is selected + if (modeCount == 0) { + AFB_ERROR ("DISPATCH-LOAD-ACTION Missing something lua|callback|(api+verb) in %s", json_object_get_string(actionJ)); + goto OnErrorExit; + } + + if (modeCount > 1) { + AFB_ERROR ("DISPATCH-LOAD-ACTION ToMany lua|callback|(api+verb) in %s", json_object_get_string(actionJ)); + goto OnErrorExit; + } + return 0; + +OnErrorExit: + return -1; +}; + +STATIC DispatchActionT *DispatchLoadActions (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 (actionJ, &actions[idx]); + if (err) goto OnErrorExit; + } + + } else { + actions = calloc (2, sizeof(DispatchActionT)); + err = DispatchLoadOneAction (actionsJ, &actions[0]); + if (err) goto OnErrorExit; + } + + return actions; + + OnErrorExit: + return NULL; + +} + +STATIC DispatchConfigT *DispatchLoadConfig (const char* filepath) { + json_object *dispatchConfigJ, *ignoreJ; + int err; + + // Load JSON file + dispatchConfigJ= json_object_from_file(filepath); + if (!dispatchConfigJ) goto OnErrorExit; + + AFB_INFO ("DISPATCH-LOAD-CONFIG: loading config filepath=%s", filepath); + + json_object *metadataJ=NULL, *onloadJ=NULL, *controlsJ=NULL, *eventsJ=NULL; + err= wrap_json_unpack(dispatchConfigJ, "{s?o,so,s?o,s?o,s?o !}", "$schema", &ignoreJ, "metadata",&metadataJ, "onload",&onloadJ, "controls",&controlsJ, "events",&eventsJ); + if (err) { + AFB_ERROR ("DISPATCH-LOAD-CONFIG Missing something metadata|[onload]|[controls]|[events] in %s", json_object_get_string(dispatchConfigJ)); + goto OnErrorExit; + } + + DispatchConfigT *dispatchConfig = calloc (1, sizeof(DispatchConfigT)); + if (metadataJ) { + err= wrap_json_unpack(metadataJ, "{so,s?s,ss !}", "label", &dispatchConfig->label, "info",&dispatchConfig->info, "version",&dispatchConfig->version); + if (err) { + AFB_ERROR ("DISPATCH-LOAD-CONFIG:METADATA Missing something label|version|[label] in %s", json_object_get_string(metadataJ)); + goto OnErrorExit; + } + } + + if (onloadJ) { + json_object * actionsJ; + DispatchHandleT *dispatchHandle = calloc (1, sizeof(DispatchHandleT)); + + err= wrap_json_unpack(onloadJ, "{so,s?s,s?s,s?o !}", "label",&dispatchHandle->label, "info",&dispatchHandle->info, "plugin", &dispatchConfig->plugin, "actions",&actionsJ); + if (err) { + AFB_ERROR ("DISPATCH-LOAD-CONFIG:ONLOAD Missing something label|[info]|[plugin]|[actions] in %s", json_object_get_string(onloadJ)); + goto OnErrorExit; + } + + if (dispatchConfig->plugin) { + + // search for default policy config file + json_object *pluginPathJ = ScanForConfig(CONTROL_PLUGIN_PATH , dispatchConfig->plugin, NULL); + if (!pluginPathJ || json_object_array_length(pluginPathJ) == 0) { + AFB_ERROR ("DISPATCH-LOAD-CONFIG:PLUGIN Missing plugin=%s in path=%s", dispatchConfig->plugin, CONTROL_PLUGIN_PATH); + goto OnErrorExit; + } + + char *filename; char*dirpath; + err= wrap_json_unpack (json_object_array_get_idx(pluginPathJ,1), "{s:s, s:s !}", "dirpath", &dirpath,"filename", &filename); + if (err) { + AFB_ERROR ("DISPATCH-LOAD-CONFIG:PLUGIN HOOPs invalid plugin file path = %s", json_object_get_string(pluginPathJ)); + goto OnErrorExit; + } + + if (json_object_array_length(pluginPathJ) > 1) { + AFB_WARNING ("DISPATCH-LOAD-CONFIG:PLUGIN plugin multiple instances in searchpath will use %s/%s", dirpath, filepath); + } + + char filepath[255]; + strncpy(filepath, dirpath, sizeof(filepath)); + strncat(filepath, "/", sizeof(filepath)); + strncat(filepath, filename, sizeof(filepath)); + dispatchConfig->dlHandle = dlopen(filepath, RTLD_NOW); + if (!dispatchConfig->dlHandle) { + AFB_ERROR ("DISPATCH-LOAD-CONFIG:PLIUGIN Fail to load filepath=%s err= %s", filepath, dlerror()); + goto OnErrorExit; + } + } + + dispatchHandle->actions= DispatchLoadActions(actionsJ); + dispatchConfig->onload= dispatchHandle; + } + + if (controlsJ) { + json_object * actionsJ; + DispatchHandleT *dispatchHandle = calloc (1, sizeof(DispatchHandleT)); + + err= wrap_json_unpack(controlsJ, "{so,s?s,so !}", "label",&dispatchHandle->label, "info",&dispatchHandle->info, "actions",&actionsJ); + if (err) { + AFB_ERROR ("DISPATCH-LOAD-CONFIG:CONTROLS Missing something label|[info]|actions in %s", json_object_get_string(controlsJ)); + goto OnErrorExit; + } + dispatchHandle->actions= DispatchLoadActions(actionsJ); + dispatchConfig->onload= dispatchHandle; + } + + if (eventsJ) { + json_object * actionsJ; + DispatchHandleT *dispatchHandle = calloc (1, sizeof(DispatchHandleT)); + + err= wrap_json_unpack(eventsJ, "{so,s?s,so !}", "label",&dispatchHandle->label, "info",&dispatchHandle->info, "actions",&actionsJ); + if (err) { + AFB_ERROR ("DISPATCH-LOAD-CONFIG:EVENTS Missing something label|[info]|actions in %s", json_object_get_string(eventsJ)); + goto OnErrorExit; + } + dispatchHandle->actions= DispatchLoadActions(actionsJ); + dispatchConfig->onload= dispatchHandle; + } + + return dispatchConfig; + +OnErrorExit: + return NULL; +} + +// Load default config file at init +PUBLIC int DispatchInit () { + int index, err; + + + // search for default dispatch config file + json_object* responseJ = ScanForConfig(CONTROL_DISPATCH_PATH, "onload", "json"); + + for (index=0; index < json_object_array_length(responseJ); index++) { + json_object *entryJ=json_object_array_get_idx(responseJ, index); + + char *filename; char*dirpath; + err= wrap_json_unpack (entryJ, "{s:s, s:s !}", "dirpath", &dirpath,"filename", &filename); + if (err) { + AFB_ERROR ("DISPATCH-INIT HOOPs invalid config file path = %s", json_object_get_string(entryJ)); + goto OnErrorExit; + } + + if (strcasestr(filename, CONTROL_DISPATCH_FILE)) { + char filepath[255]; + strncpy(filepath, dirpath, sizeof(filepath)); + strncat(filepath, "/", sizeof(filepath)); + strncat(filepath, filename, sizeof(filepath)); + ctlHandle = DispatchLoadConfig (filepath); + if (!ctlHandle) { + AFB_ERROR ("DISPATCH-INIT:ERROR No File to load [%s]", filepath); + goto OnErrorExit; + } + break; + } + } + + // no dispatch config found remove control API from binder + if (index == 0) { + AFB_WARNING ("DISPATCH-INIT:WARNING No Control dispatch file [%s]", CONTROL_DISPATCH_FILE); + } + + AFB_NOTICE ("DISPATCH-INIT:SUCCES: Audio Control Dispatch Init"); + return 0; + +OnErrorExit: + AFB_NOTICE ("DISPATCH-INIT:ERROR: Audio Control Dispatch Init"); + return 1; +} + + + diff --git a/Controler-afb/ctl-events.c b/Controler-afb/ctl-events.c index f444848..6f710e3 100644 --- a/Controler-afb/ctl-events.c +++ b/Controler-afb/ctl-events.c @@ -23,6 +23,22 @@ #include "ctl-binding.h" +#define DEFAULT_PAUSE_DELAY 3000 +#define DEFAULT_TEST_COUNT 1 +typedef int (*timerCallbackT)(void *context); +typedef struct { + int value; + const char *label; +} AutoTestCtxT; + +typedef struct TimerHandleS { + int count; + int delay; + AutoTestCtxT *context; + timerCallbackT callback; + sd_event_source *evtSource; +} TimerHandleT; + static afb_event afbevt; STATIC int TimerNext (sd_event_source* source, uint64_t timer, void* handle) { diff --git a/Controler-afb/ctl-lua.c b/Controler-afb/ctl-lua.c index 59ffbe8..8b40e38 100644 --- a/Controler-afb/ctl-lua.c +++ b/Controler-afb/ctl-lua.c @@ -31,6 +31,8 @@ #include "ctl-binding.h" #include "wrap-json.h" +#define LUA_FIRST_ARG 2 // when using luaL_newlib calllback receive libtable as 1st arg +#define LUA_MSG_MAX_LENGTH 255 static lua_State* luaState; @@ -55,14 +57,25 @@ typedef struct { } 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_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"); @@ -72,20 +85,21 @@ STATIC LuaAfbContextT *LuaCtxCheck (lua_State *luaState, int index) { } STATIC LuaAfbContextT *LuaCtxPush (lua_State *luaState, afb_req request, const char* info) { - LuaAfbContextT *afbContext = (LuaAfbContextT *)lua_newuserdata(luaState, sizeof(LuaAfbContextT)); + // 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; } - luaL_setmetatable(luaState, CTX_TOKEN); afbContext->ctxMagic=CTX_MAGIC; afbContext->info=strdup(info); afbContext->request= request; return afbContext; } -STATIC void LuaCtxFree (LuaAfbContextT *afbContext) { - +STATIC void LuaCtxFree (LuaAfbContextT *afbContext) { free (afbContext->info); } @@ -95,73 +109,57 @@ PUBLIC json_object* ScanForConfig (char* searchPath, char *pre, char *ext) { DIR *dirHandle; char *dirPath; char* dirList= strdup(searchPath); - size_t extLen = strlen(ext); + size_t extLen=0; - responseJ = json_object_new_array(); - for (dirPath= strtok(dirList, ":"); dirPath && *dirPath; dirPath=strtok(NULL,":")) { - struct dirent *dirEnt; - + void ScanDir (char *dirpath) { + struct dirent *dirEnt; dirHandle = opendir (dirPath); if (!dirHandle) { AFB_NOTICE ("CONFIG-SCANNING dir=%s not readable", dirPath); - continue; + return; } AFB_NOTICE ("CONFIG-SCANNING:ctl_listconfig scanning: %s", dirPath); while ((dirEnt = readdir(dirHandle)) != NULL) { + + // recursively search embedded directories ignoring any directory starting by '.' or '_' + if (dirEnt->d_type == DT_DIR) { + char newpath[CONTROL_MAXPATH_LEN]; + if (dirEnt->d_name[0]=='.' || dirEnt->d_name[0]=='_') continue; + + strncpy(newpath, dirpath, sizeof(newpath)); + strncat(newpath, "/", sizeof(newpath)); + strncat(newpath, dirEnt->d_name, sizeof(newpath)); + } + // Unknown type is accepted to support dump filesystems if (dirEnt->d_type == DT_REG || dirEnt->d_type == DT_UNKNOWN) { - + // check prefix and extention - size_t extIdx=strlen(dirEnt->d_name) - extLen; + size_t extIdx=strlen(dirEnt->d_name)-extLen; if (extIdx <= 0) continue; if (pre && !strcasestr (dirEnt->d_name, pre)) continue; if (ext && strcasecmp (ext, &dirEnt->d_name[extIdx])) continue; - + struct json_object *pathJ = json_object_new_object(); json_object_object_add(pathJ, "dirpath", json_object_new_string(dirPath)); json_object_object_add(pathJ, "filename", json_object_new_string(dirEnt->d_name)); json_object_array_add(responseJ, pathJ); } + closedir(dirHandle); } } - - free (dirList); - return (responseJ); -} -STATIC int LuaPrintMessage(LuaAfbMessageT action, lua_State* luaState) { + if (ext) extLen=strlen(ext); + responseJ = json_object_new_array(); - int count = lua_gettop(luaState); - for (int idx = 1; idx <= count; ++idx) { - const char *str = lua_tostring(luaState, idx); // Get string - - switch (action) { - case AFB_MSG_NOTICE: - AFB_NOTICE(str); - break; - - case AFB_MSG_WARNING: - AFB_WARNING(str); - break; - - default: - AFB_ERROR(str); - } + // loop recursively on dir + for (dirPath= strtok(dirList, ":"); dirPath && *dirPath; dirPath=strtok(NULL,":")) { + ScanDir (dirPath); } - return 0; // no value return -} - -STATIC int LuaPrintWarning(lua_State* luaState) { - - LuaPrintMessage (AFB_MSG_WARNING, luaState); - return 0; // no value return -} - -STATIC int LuaPrintNotice(lua_State* luaState) { - - LuaPrintMessage (AFB_MSG_NOTICE, luaState); - return 0; // no value return + + free (dirList); + return (responseJ); } STATIC int LuaPushArgument (json_object *arg) { @@ -197,9 +195,8 @@ STATIC int LuaPushArgument (json_object *arg) { return 1; } -static json_object *LuaPopArgs (lua_State* luaState, int count); STATIC json_object *PopOneArg (lua_State* luaState, int idx); -static json_object *LuaTableToJson (lua_State* luaState, int index) { +STATIC json_object *LuaTableToJson (lua_State* luaState, int index) { json_object *tableJ= json_object_new_object(); const char *key; @@ -207,9 +204,9 @@ static json_object *LuaTableToJson (lua_State* luaState, int index) { lua_pushnil(luaState); // 1st key for (int jdx=1; lua_next(luaState, index) != 0; jdx++) { - printf("jdx=%d %s - %s\n", jdx, - lua_typename(luaState, lua_type(luaState, -2)), - lua_typename(luaState, lua_type(luaState, -1))); + //printf("jdx=%d %s - %s\n", jdx, + //lua_typename(luaState, lua_type(luaState, -2)), + //lua_typename(luaState, lua_type(luaState, -1))); // uses 'key' (at index -2) and 'value' (at index -1) if (lua_type(luaState,-2) == LUA_TSTRING) key= lua_tostring(luaState, -2); @@ -228,7 +225,8 @@ static json_object *LuaTableToJson (lua_State* luaState, int index) { STATIC json_object *PopOneArg (lua_State* luaState, int idx) { json_object *value=NULL; - switch(lua_type(luaState, idx)) { + int luaType = lua_type(luaState, idx); + switch(luaType) { case LUA_TNUMBER: value= json_object_new_double(lua_tonumber(luaState, idx)); break; @@ -239,33 +237,31 @@ STATIC json_object *PopOneArg (lua_State* luaState, int idx) { value= json_object_new_string(lua_tostring(luaState, idx)); break; case LUA_TTABLE: { - AFB_NOTICE ("-++-- idx=%d ", idx); + AFB_NOTICE ("-++-- luatable idx=%d ", idx); value= LuaTableToJson(luaState, idx); break; } default: - AFB_NOTICE ("script returned Unknown/Unsupported idx=%d type: %s", idx, lua_typename(luaState, idx)); + AFB_NOTICE ("PopOneArg: script returned Unknown/Unsupported idx=%d type:%d/%s", idx, luaType, lua_typename(luaState, luaType)); value=NULL; } - if (value) AFB_NOTICE ("---- idx=%d value=%s", idx, json_object_get_string(value)); return value; } -static json_object *LuaPopArgs (lua_State* luaState, int count) { - - +static json_object *LuaPopArgs (lua_State* luaState, int start) { json_object *responseJ; - if(count <=0) return NULL; + int stop = lua_gettop(luaState); + if(stop-start <=0) return NULL; // start at 2 because we are using a function array lib - if (count == 2) { - responseJ=PopOneArg (luaState, 2); + if (start == stop) { + responseJ=PopOneArg (luaState, start); } else { // loop on remaining return arguments responseJ= json_object_new_array(); - for (int idx=2; idx <= count; idx++) { + for (int idx=start; idx <= stop; idx++) { json_object_array_add(responseJ, PopOneArg (luaState, idx)); } } @@ -274,18 +270,104 @@ static json_object *LuaPopArgs (lua_State* luaState, int count) { } -STATIC int LuaAfbSuccess(lua_State* luaState) { +STATIC void LuaFormatMessage(lua_State* luaState, LuaAfbMessageT action) { + char *message; + json_object *responseJ= LuaPopArgs(luaState, LUA_FIRST_ARG); + if (!responseJ) { + message="--"; + goto PrintMessage; + } - int count = lua_gettop(luaState); + // 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); + + switch (format[++idx]) { + case 'd': + targetIdx += snprintf (&message[targetIdx], LUA_MSG_MAX_LENGTH-targetIdx,"%d", json_object_get_int(slotJ)); + break; + case 'f': + targetIdx += snprintf (&message[targetIdx], LUA_MSG_MAX_LENGTH-targetIdx,"%f", json_object_get_double(slotJ)); + break; + + case 's': + default: + targetIdx += snprintf (&message[targetIdx], LUA_MSG_MAX_LENGTH-targetIdx,"%s", json_object_get_string(slotJ)); + } + + } else { + message[targetIdx++] = format[idx]; + } + } + +PrintMessage: + switch (action) { + case AFB_MSG_WARNING: + AFB_WARNING (message); + break; + case AFB_MSG_NOTICE: + AFB_NOTICE (message); + break; + case AFB_MSG_DEBUG: + AFB_DEBUG (message); + break; + case AFB_MSG_INFO: + AFB_INFO (message); + break; + case AFB_MSG_ERROR: + default: + AFB_ERROR (message); + } +} +STATIC int LuaPrintInfo(lua_State* luaState) { + LuaFormatMessage (luaState, AFB_MSG_INFO); + return 0; // no value return +} - //AFB_NOTICE ("LuaAfbSuccess args=%s",json_object_get_string(LuaPopArgs(luaState, count))); - - LuaAfbContextT *afbContext= LuaCtxCheck(luaState, 1); +STATIC int LuaPrintError(lua_State* luaState) { + LuaFormatMessage (luaState, AFB_MSG_ERROR); + return 0; // no value return +} + +STATIC int LuaPrintWarning(lua_State* luaState) { + LuaFormatMessage (luaState, AFB_MSG_WARNING); + return 0; // no value return +} + +STATIC int LuaPrintNotice(lua_State* luaState) { + LuaFormatMessage (luaState, AFB_MSG_NOTICE); + return 0; // no value return +} + +STATIC int LuaPrintDebug(lua_State* luaState) { + LuaFormatMessage (luaState, AFB_MSG_DEBUG); + return 0; // no value return +} + +STATIC int LuaAfbSuccess(lua_State* luaState) { + LuaAfbContextT *afbContext= LuaCtxCheck(luaState, LUA_FIRST_ARG); if (!afbContext) goto OnErrorExit; + + // ignore context argument + json_object *responseJ= LuaPopArgs(luaState, LUA_FIRST_ARG+1); - afb_req_success(afbContext->request, LuaPopArgs(luaState, count), NULL); + afb_req_success(afbContext->request, responseJ, NULL); LuaCtxFree(afbContext); return 0; @@ -296,13 +378,12 @@ STATIC int LuaAfbSuccess(lua_State* luaState) { } STATIC int LuaAfbFail(lua_State* luaState) { - - LuaAfbContextT *afbContext= LuaCtxCheck(luaState, 1); + LuaAfbContextT *afbContext= LuaCtxCheck(luaState, LUA_FIRST_ARG); if (!afbContext) goto OnErrorExit; - int count = lua_gettop(luaState); + json_object *responseJ= LuaPopArgs(luaState, LUA_FIRST_ARG+1); - afb_req_fail(afbContext->request, afbContext->info, json_object_get_string(LuaPopArgs(luaState, count))); + afb_req_fail(afbContext->request, afbContext->info, json_object_get_string(responseJ)); LuaCtxFree(afbContext); return 0; @@ -402,15 +483,14 @@ PUBLIC void LuaDoAction (LuaDoActionT action, afb_req request) { 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, func); if (!afbContext) goto OnErrorExit; - - // load function (should exist in CONTROL_PATH_LUA - lua_getglobal(luaState, func); - - - + // push arguments on the stack if (json_object_get_type(args) != json_type_array) { count= LuaPushArgument (args); @@ -420,7 +500,7 @@ PUBLIC void LuaDoAction (LuaDoActionT action, afb_req request) { if (err) break; } } - + break; } @@ -431,14 +511,18 @@ PUBLIC void LuaDoAction (LuaDoActionT action, afb_req request) { // scan luascript search path once static json_object *luaScriptPathJ =NULL; - if (!luaScriptPathJ) luaScriptPathJ= ScanForConfig(CONTROL_PATH_LUA , NULL, "lua"); + if (!luaScriptPathJ) luaScriptPathJ= ScanForConfig(CONTROL_LUA_PATH , NULL, "lua"); err= wrap_json_unpack (queryJ, "{s:s, s?o s?o !}", "script", &script,"args", &args, "arg", &args); if (err) { AFB_ERROR ("LUA-DOCALL-SYNTAX missing script|(args,arg) args=%s", json_object_get_string(queryJ)); goto OnErrorExit; } - + + // Push AFB client context on the stack + LuaAfbContextT *afbContext= LuaCtxPush(luaState, request, script); + if (!afbContext) goto OnErrorExit; + // push arguments on the stack if (json_object_get_type(args) != json_type_array) { lua_createtable(luaState, 1, 0); @@ -452,9 +536,6 @@ PUBLIC void LuaDoAction (LuaDoActionT action, afb_req request) { } } - LuaAfbContextT *afbContext= LuaCtxPush(luaState, request, script); - if (!afbContext) goto OnErrorExit; - for (index=0; index < json_object_array_length(luaScriptPathJ); index++) { char *filename; char*dirpath; json_object *entryJ=json_object_array_get_idx(luaScriptPathJ, index); @@ -480,10 +561,10 @@ PUBLIC void LuaDoAction (LuaDoActionT action, afb_req request) { } if (index == json_object_array_length(luaScriptPathJ)) { - AFB_ERROR ("LUA-DOSCRIPT HOOPs script=%s not in path=%s", script, CONTROL_PATH_LUA); + AFB_ERROR ("LUA-DOSCRIPT HOOPs script=%s not in path=%s", script, CONTROL_LUA_PATH); goto OnErrorExit; - - } + } + break; } @@ -491,10 +572,9 @@ PUBLIC void LuaDoAction (LuaDoActionT action, afb_req request) { AFB_ERROR ("LUA-DOCALL-ACTION unknown query=%s", json_object_get_string(queryJ)); goto OnErrorExit; } - - + // effectively exec LUA code (afb_reply/fail done later from callback) - err=lua_pcall(luaState, count, 0, 0); + 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; @@ -521,7 +601,10 @@ PUBLIC void ctlapi_lua_doscript (afb_req request) { static const luaL_Reg afbFunction[] = { {"notice" , LuaPrintNotice}, + {"info" , LuaPrintInfo}, {"warning", LuaPrintWarning}, + {"debug" , LuaPrintDebug}, + {"error" , LuaPrintError}, {"service", LuaAfbService}, {"success", LuaAfbSuccess}, {"fail" , LuaAfbFail}, @@ -533,7 +616,7 @@ PUBLIC int LuaLibInit () { int err, index; // search for default policy config file - json_object *luaScriptPathJ = ScanForConfig(CONTROL_PATH_LUA , "onload", "lua"); + json_object *luaScriptPathJ = ScanForConfig(CONTROL_LUA_PATH , "onload", "lua"); // open a new LUA interpretor luaState = luaL_newstate(); @@ -560,7 +643,7 @@ PUBLIC int LuaLibInit () { goto OnErrorExit; } - char filepath[255]; + char filepath[CONTROL_MAXPATH_LEN]; strncpy(filepath, dirpath, sizeof(filepath)); strncat(filepath, "/", sizeof(filepath)); strncat(filepath, filename, sizeof(filepath)); @@ -575,16 +658,14 @@ PUBLIC int LuaLibInit () { if (err) { AFB_ERROR ("LUA-LOAD HOOPs Error in LUA exec scripts=%s err=%s", filepath, lua_tostring(luaState,-1)); goto OnErrorExit; - } - + } } // no policy config found remove control API from binder if (index == 0) { - AFB_WARNING ("POLICY-INIT:WARNING No Control LUA file in path=[%s]", CONTROL_PATH_LUA); + AFB_WARNING ("POLICY-INIT:WARNING No Control LUA file in path=[%s]", CONTROL_LUA_PATH); } - - + AFB_DEBUG ("Audio control-LUA Init Done"); return 0; diff --git a/Controler-afb/ctl-plugin-sample.c b/Controler-afb/ctl-plugin-sample.c new file mode 100644 index 0000000..f232573 --- /dev/null +++ b/Controler-afb/ctl-plugin-sample.c @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2016 "IoT.bzh" + * Author Fulup Ar Foll + * + * 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. + * + * Sample plugin for Controller + */ + + +#define _GNU_SOURCE +#include +#include + +#include "ctl-binding.h" + +#define MY_PLUGIN_MAGIC 987654321 + +typedef struct { + int magic; + int count; +} MyPluginCtxT; + +STATIC const char* jsonToString (json_object *valueJ) { + const char *value; + if (valueJ) + value=json_object_get_string(valueJ); + else + value="NULL"; + + return value; +} + +PUBLIC int SamplePolicyInstall (DispatchActionT *action, json_object *response, void *context) { + + MyPluginCtxT *pluginCtx= (MyPluginCtxT*)calloc (1, sizeof(MyPluginCtxT)); + pluginCtx->magic = MY_PLUGIN_MAGIC; + pluginCtx->count = 0; + + AFB_INFO ("CONTROLER-PLUGIN-SAMPLE SamplePolicyInstall action=%s args=%s", action->label, jsonToString(action->argsJ)); + + return 0; +} + +PUBLIC int SamplePolicyCount (DispatchActionT *action, json_object *response, void *context) { + + MyPluginCtxT *pluginCtx= (MyPluginCtxT*)context; + //pluginCtx->magic = MY_PLUGIN_MAGIC; + //pluginCtx->count = 0; + + AFB_INFO ("CONTROLER-PLUGIN-SAMPLE SamplePolicyCount action=%s args=%s count=%d", action->label, jsonToString(action->argsJ), pluginCtx->count); + + return 0; +} diff --git a/Controler-afb/ctl-policy.c b/Controler-afb/ctl-policy.c deleted file mode 100644 index 7ad73fa..0000000 --- a/Controler-afb/ctl-policy.c +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright (C) 2016 "IoT.bzh" - * Author Fulup Ar Foll - * - * 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 -#include - -#include - -#include "wrap-json.h" -#include "ctl-binding.h" - -STATIC PolicyCtlConfigT *ctlHandle = NULL; - - -PUBLIC void ctlapi_authorize (PolicyCtlEnumT control, afb_req request) { - json_object*tmpJ; - - json_object* argsJ= afb_req_json(request); - int done=json_object_object_get_ex(argsJ, "closing", &tmpJ); - if (done) return; - -} - - -// List Avaliable Configuration Files -PUBLIC void ctlapi_config (struct afb_req request) { - json_object*tmpJ; - char *dirList; - - json_object* argsJ = afb_req_json(request); - if (argsJ && json_object_object_get_ex (argsJ, "cfgpath" , &tmpJ)) { - dirList = strdup (json_object_get_string(tmpJ)); - } else { - dirList = strdup (CONTROL_PATH_POLICY); - AFB_NOTICE ("CONFIG-MISSING: use default CONTROL_PATH_POLICY=%s", CONTROL_PATH_POLICY); - } - - // get list of config file - struct json_object *responseJ = ScanForConfig(dirList, "onload", "json"); - - if (json_object_array_length(responseJ) == 0) { - afb_req_fail(request, "CONFIGPATH:EMPTY", "No Config Found in CONTROL_PATH_POLICY"); - } else { - afb_req_success(request, responseJ, NULL); - } - - return; -} - -STATIC PolicyActionT *PolicyLoadActions (json_object *actionsJ) { - int err; - PolicyActionT *actions; - char *callback; // need to search in DL lib - - // unpack individual action object - int actionUnpack (json_object *actionJ, PolicyActionT *action) { - - err= wrap_json_unpack(actionJ, "{ss,s?s,s?o,s?s,s?s,s?s !}" - , "label",&action->label, "info",&action->info, "callback",&callback, "args",&action->argsJ, "api",&action->api, "verb", &action->verb); - if (err) { - AFB_ERROR ("POLICY-LOAD-ACTION Missing something label|info|callback|api|verb|args in %s", json_object_get_string(actionJ)); - return -1; - } - if (!callback || !(action->api && action->verb)) { - AFB_ERROR ("POLICY-LOAD-ACTION Missing something callback|(api+verb) in %s", json_object_get_string(actionJ)); - return -1; - } - return 0; - }; - - // 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(PolicyActionT)); - - for (int idx=0; idx < count; idx++) { - json_object *actionJ = json_object_array_get_idx(actionsJ, idx); - err = actionUnpack (actionJ, &actions[idx]); - if (err) goto OnErrorExit; - } - - } else { - actions = calloc (2, sizeof(PolicyActionT)); - err = actionUnpack (actionsJ, &actions[0]); - if (err) goto OnErrorExit; - } - - return actions; - - OnErrorExit: - return NULL; - -} - -// load control policy from file using json_unpack https://jansson.readthedocs.io/en/2.9/apiref.html#parsing-and-validating-values -STATIC PolicyCtlConfigT *PolicyLoadConfig (const char* filepath) { - json_object *policyConfigJ, *ignoreJ, *actionsJ; - PolicyCtlConfigT *policyConfig = calloc (1, sizeof(PolicyCtlConfigT)); - int err; - - // Load JSON file - policyConfigJ= json_object_from_file(filepath); - if (!policyConfigJ) goto OnErrorExit; - - json_object *metadataJ, *onloadJ, *controlsJ, *eventsJ; - err= wrap_json_unpack(policyConfigJ, "{s?o,so,s?o,so,so !}", "$schema", &ignoreJ, "metadata",&metadataJ, "onload",&onloadJ, "controls",&controlsJ, "events",&eventsJ); - if (err) { - AFB_ERROR ("POLICY-LOAD-ERRROR Missing something metadata|onload|controls|events in %s", json_object_get_string(policyConfigJ)); - goto OnErrorExit; - } - - PolicyHandleT *policyHandle = calloc (1, sizeof(PolicyHandleT)); - err= wrap_json_unpack(metadataJ, "{so,s?s,s?s !}", "label", &policyHandle->label, "info",&policyHandle->info, "version",&policyHandle->version); - if (err) { - AFB_ERROR ("POLICY-LOAD-CONFIG Missing something label|info|version in %s", json_object_get_string(metadataJ)); - goto OnErrorExit; - } - - if (onloadJ) { - err= wrap_json_unpack(onloadJ, "{s?o,s?s,s?s !}", "info",&ignoreJ, "label",&ignoreJ, "actions",&actionsJ); - if (err) { - AFB_ERROR ("POLICY-LOAD-CONFIG Missing something label|info|plugin|actions in %s", json_object_get_string(metadataJ)); - goto OnErrorExit; - } - policyConfig->onload= PolicyLoadActions (actionsJ); - } - - return policyConfig; - -OnErrorExit: - return NULL; -} - -// Load default config file at init -PUBLIC int PolicyInit () { - int index, err; - - - // search for default policy config file - json_object* responseJ = ScanForConfig(CONTROL_PATH_POLICY, "onload", "json"); - return 0; - - for (index=0; index < json_object_array_length(responseJ); index++) { - json_object *entryJ=json_object_array_get_idx(responseJ, index); - - char *filename; char*dirpath; - err= wrap_json_unpack (entryJ, "{s:s, s:s !}", "dirpath", &dirpath,"filename", &filename); - if (err) { - AFB_ERROR ("POLICY-INIT HOOPs invalid config file path = %s", json_object_get_string(entryJ)); - goto OnErrorExit; - } - - if (strcasestr(filename, CONTROL_FILE_POLICY)) { - char filepath[255]; - strncpy(filepath, dirpath, sizeof(filepath)); - strncat(filepath, "/", sizeof(filepath)); - strncat(filepath, filename, sizeof(filepath)); - ctlHandle = PolicyLoadConfig (filepath); - if (!ctlHandle) { - AFB_ERROR ("POLICY-INIT:ERROR No File to load [%s]", filepath); - goto OnErrorExit; - } - break; - } - } - - // no policy config found remove control API from binder - if (index == 0) { - AFB_WARNING ("POLICY-INIT:WARNING No Control policy file [%s]", CONTROL_FILE_POLICY); - } - - AFB_NOTICE ("POLICY-INIT:SUCCES: Audio Control Policy Init"); - return 0; - -OnErrorExit: - AFB_NOTICE ("POLICY-INIT:ERROR: Audio Control Policy Init"); - return 1; -} - - - -- cgit 1.2.3-korg