diff options
Diffstat (limited to 'Controller-afb')
-rw-r--r-- | Controller-afb/CMakeLists.txt | 72 | ||||
-rw-r--r-- | Controller-afb/ctl-apidef.h | 103 | ||||
-rw-r--r-- | Controller-afb/ctl-apidef.json | 228 | ||||
-rw-r--r-- | Controller-afb/ctl-binding.c | 67 | ||||
-rw-r--r-- | Controller-afb/ctl-binding.h | 145 | ||||
-rw-r--r-- | Controller-afb/ctl-dispatch.c | 667 | ||||
-rw-r--r-- | Controller-afb/ctl-lua.c | 1068 | ||||
-rw-r--r-- | Controller-afb/ctl-plugin-sample.c | 134 | ||||
-rw-r--r-- | Controller-afb/ctl-policy.c | 221 | ||||
-rw-r--r-- | Controller-afb/ctl-timer.c | 101 | ||||
-rw-r--r-- | Controller-afb/filescan-utils.c | 128 | ||||
-rw-r--r-- | Controller-afb/filescan-utils.h | 41 | ||||
-rw-r--r-- | Controller-afb/wrap-json.c | 939 | ||||
-rw-r--r-- | Controller-afb/wrap-json.h | 46 | ||||
-rw-r--r-- | Controller-afb/wrap-json.md | 305 |
15 files changed, 0 insertions, 4265 deletions
diff --git a/Controller-afb/CMakeLists.txt b/Controller-afb/CMakeLists.txt deleted file mode 100644 index 514c091..0000000 --- a/Controller-afb/CMakeLists.txt +++ /dev/null @@ -1,72 +0,0 @@ -########################################################################### -# 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) - ADD_COMPILE_OPTIONS(-DCONTROL_SUPPORT_LUA) - ADD_COMPILE_OPTIONS(-DCONTROL_LUA_EVENT="luaevt") - ADD_COMPILE_OPTIONS(-DCONTROL_LUA_PATH="${CMAKE_SOURCE_DIR}/conf.d/project/lua.d:${CMAKE_INSTALL_PREFIX}/controller-plugins/ctl-lua.d") -else(CONTROL_SUPPORT_LUA) - message(STATUS "Warning: LUA Without Support ") -endif(CONTROL_SUPPORT_LUA) - -# Add target to project dependency list -PROJECT_TARGET_ADD(control-afb) - - # Define project Targets - ADD_LIBRARY(${TARGET_NAME} MODULE ctl-binding.c ctl-timer.c ctl-dispatch.c ${CTL_LUA_SOURCE}) - - SET_OPENAPI_FILENAME("ctl-apidef") - # Binder exposes a unique public entry point - SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES - PREFIX "afb-" - LABELS "BINDINGV2" - LINK_FLAGS ${BINDINGS_LINK_FLAG} - OUTPUT_NAME ${TARGET_NAME} - ) - - # Library dependencies (include updates automatically) - TARGET_LINK_LIBRARIES(${TARGET_NAME} - afb-utilities - ${link_libraries} - ) - - -PROJECT_TARGET_ADD(audio-plugin-sample) - - # Define targets - ADD_LIBRARY(${TARGET_NAME} MODULE ctl-plugin-sample.c) - - # Alsa Plugin properties - SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES - PREFIX ${CTL_PLUGIN_PRE} - SUFFIX ${CTL_PLUGIN_EXT} - OUTPUT_NAME ${TARGET_NAME} - ) - - # Library dependencies (include updates automatically) - TARGET_LINK_LIBRARIES(${TARGET_NAME} - ${link_libraries} - ) - - # installation directory - INSTALL(TARGETS ${TARGET_NAME} - LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/lib/controller-plugins) - diff --git a/Controller-afb/ctl-apidef.h b/Controller-afb/ctl-apidef.h deleted file mode 100644 index 9ce9e05..0000000 --- a/Controller-afb/ctl-apidef.h +++ /dev/null @@ -1,103 +0,0 @@ - -static const char _afb_description_v2_control[] = - "{\"openapi\":\"3.0.0\",\"$schema\":\"http:iot.bzh/download/openapi/schem" - "a-3.0/default-schema.json\",\"info\":{\"description\":\"\",\"title\":\"c" - "ontroler\",\"version\":\"1.0\",\"x-binding-c-generator\":{\"api\":\"cont" - "rol\",\"version\":2,\"prefix\":\"ctlapi_\",\"postfix\":\"\",\"start\":nu" - "ll,\"onevent\":\"DispatchOneEvent\",\"init\":\"CtlBindingInit\",\"scope\"" - ":\"\",\"private\":false}},\"servers\":[{\"url\":\"ws://{host}:{port}/api" - "/polctl\",\"description\":\"Unicens2 API.\",\"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\":{\"monitor\":{\"permission\":\"urn:AGL:permission:a" - "udio:public:monitor\"},\"multimedia\":{\"permission\":\"urn:AGL:permissi" - "on:audio:public:monitor\"},\"navigation\":{\"permission\":\"urn:AGL:perm" - "ission:audio:public:monitor\"},\"emergency\":{\"permission\":\"urn:AGL:p" - "ermission:audio:public:emergency\"}},\"responses\":{\"200\":{\"descripti" - "on\":\"A complex object array response\",\"content\":{\"application/json" - "\":{\"schema\":{\"$ref\":\"#/components/schemas/afb-reply\"}}}}}},\"path" - "s\":{\"/monitor\":{\"description\":\"Subcribe Audio Agent Policy Control" - " End\",\"get\":{\"x-permissions\":{\"$ref\":\"#/components/x-permissions" - "/monitor\"},\"parameters\":[{\"in\":\"query\",\"name\":\"event_patern\"," - "\"required\":true,\"schema\":{\"type\":\"string\"}}],\"responses\":{\"20" - "0\":{\"$ref\":\"#/components/responses/200\"}}}},\"/dispatch\":{\"descri" - "ption\":\"Request Access to Navigation Audio Channel.\",\"get\":{\"x-per" - "missions\":{\"$ref\":\"#/components/x-permissions/navigation\"},\"parame" - "ters\":[{\"in\":\"query\",\"name\":\"zone\",\"required\":false,\"schema\"" - ":{\"type\":\"string\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components" - "/responses/200\"}}}},\"/request\":{\"description\":\"Execute LUA string " - "script.\",\"get\":{\"x-permissions\":{\"$ref\":\"#/components/x-permissi" - "ons/navigation\"},\"parameters\":[{\"in\":\"query\",\"name\":\"func\",\"" - "required\":true,\"schema\":{\"type\":\"string\"}},{\"in\":\"query\",\"na" - "me\":\"args\",\"required\":false,\"schema\":{\"type\":\"array\"}}],\"res" - "ponses\":{\"200\":{\"$ref\":\"#/components/responses/200\"}}}},\"/debugl" - "ua\":{\"description\":\"Execute LUA string script.\",\"get\":{\"x-permis" - "sions\":{\"$ref\":\"#/components/x-permissions/navigation\"},\"parameter" - "s\":[{\"in\":\"query\",\"name\":\"filename\",\"required\":true,\"schema\"" - ":{\"type\":\"string\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components" - "/responses/200\"}}}}}}" -; - -static const struct afb_auth _afb_auths_v2_control[] = { - { .type = afb_auth_Permission, .text = "urn:AGL:permission:audio:public:monitor" } -}; - - void ctlapi_monitor(struct afb_req req); - void ctlapi_dispatch(struct afb_req req); - void ctlapi_request(struct afb_req req); - void ctlapi_debuglua(struct afb_req req); - -static const struct afb_verb_v2 _afb_verbs_v2_control[] = { - { - .verb = "monitor", - .callback = ctlapi_monitor, - .auth = &_afb_auths_v2_control[0], - .info = NULL, - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = "dispatch", - .callback = ctlapi_dispatch, - .auth = &_afb_auths_v2_control[0], - .info = NULL, - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = "request", - .callback = ctlapi_request, - .auth = &_afb_auths_v2_control[0], - .info = NULL, - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = "debuglua", - .callback = ctlapi_debuglua, - .auth = &_afb_auths_v2_control[0], - .info = NULL, - .session = AFB_SESSION_NONE_V2 - }, - { .verb = NULL } -}; - -const struct afb_binding_v2 afbBindingV2 = { - .api = "control", - .specification = _afb_description_v2_control, - .info = NULL, - .verbs = _afb_verbs_v2_control, - .preinit = NULL, - .init = CtlBindingInit, - .onevent = DispatchOneEvent, - .noconcurrency = 0 -}; - diff --git a/Controller-afb/ctl-apidef.json b/Controller-afb/ctl-apidef.json deleted file mode 100644 index e2207d0..0000000 --- a/Controller-afb/ctl-apidef.json +++ /dev/null @@ -1,228 +0,0 @@ -{ - "openapi": "3.0.0", - "$schema": "http:iot.bzh/download/openapi/schema-3.0/default-schema.json", - "info": { - "description": "", - "title": "controler", - "version": "1.0", - "x-binding-c-generator": { - "api": "control", - "version": 2, - "prefix": "ctlapi_", - "postfix": "", - "start": null, - "onevent": "DispatchOneEvent", - "init": "CtlBindingInit", - "scope": "", - "private": false - } - }, - "servers": [ - { - "url": "ws://{host}:{port}/api/polctl", - "description": "Unicens2 API.", - "variables": { - "host": { - "default": "localhost" - }, - "port": { - "default": "1234" - } - }, - "x-afb-events": [ - { - "$ref": "#/components/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": "Generic response.", - "type": "object", - "required": ["jtype", "request"], - "properties": { - "jtype": { - "type": "string", - "const": "afb-reply" - }, - "request": { - "type": "object", - "required": ["status"], - "properties": { - "status": { - "type": "string" - }, - "info": { - "type": "string" - }, - "token": { - "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": { - "type": "string" - }, - "data": { - "type": "object" - } - } - } - }, - "x-permissions": { - "monitor": { - "permission": "urn:AGL:permission:audio:public:monitor" - }, - "multimedia": { - "permission": "urn:AGL:permission:audio:public:monitor" - }, - "navigation": { - "permission": "urn:AGL:permission:audio:public:monitor" - }, - "emergency": { - "permission": "urn:AGL:permission:audio:public:emergency" - } - }, - "responses": { - "200": { - "description": "A complex object array response", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/afb-reply" - } - } - } - } - } - }, - "paths": { - "/monitor": { - "description": "Subcribe Audio Agent Policy Control End", - "get": { - "x-permissions": { - "$ref": "#/components/x-permissions/monitor" - }, - "parameters": [ - { - "in": "query", - "name": "event_patern", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/200" - } - } - } - }, - "/dispatch": { - "description": "Request Access to Navigation Audio Channel.", - "get": { - "x-permissions": { - "$ref": "#/components/x-permissions/navigation" - }, - "parameters": [ - { - "in": "query", - "name": "zone", - "required": false, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/200" - } - } - } - }, - "/request": { - "description": "Execute LUA string script.", - "get": { - "x-permissions": { - "$ref": "#/components/x-permissions/navigation" - }, - "parameters": [ - { - "in": "query", - "name": "func", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "args", - "required": false, - "schema": { - "type": "array" - } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/200" - } - } - } - }, - "/debuglua": { - "description": "Execute LUA string script.", - "get": { - "x-permissions": { - "$ref": "#/components/x-permissions/navigation" - }, - "parameters": [ - { - "in": "query", - "name": "filename", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/200" - } - } - } - } - } -} diff --git a/Controller-afb/ctl-binding.c b/Controller-afb/ctl-binding.c deleted file mode 100644 index 4f6ecd3..0000000 --- a/Controller-afb/ctl-binding.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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-binding.h" - - - -// Include Binding Stub generated from Json OpenAPI -#include "ctl-apidef.h" - - -PUBLIC void ctlapi_monitor (afb_req request) { - - // subscribe Client to event - int err = afb_req_subscribe(request, TimerEvtGet()); - if (err != 0) { - afb_req_fail_f(request, "register-event", "Fail to subscribe binder event"); - goto OnErrorExit; - } - - afb_req_success(request, NULL, NULL); - - OnErrorExit: - return; -} - -// Create Binding Event at Init -PUBLIC int CtlBindingInit () { - - int errcount=0; - - errcount += TimerEvtInit(); - errcount += DispatchInit(); -#ifdef CONTROL_SUPPORT_LUA - errcount += LuaLibInit(); -#endif - - const char *profile= getenv("CONTROL_ONLOAD_PROFILE"); - if (!profile) profile=CONTROL_ONLOAD_PROFILE; - - // now that everything is initialised execute the onload action - if (!errcount) - errcount += DispatchOnLoad(CONTROL_ONLOAD_PROFILE); - - AFB_DEBUG ("Audio Policy Control Binding Done errcount=%d", errcount); - return errcount; -} - diff --git a/Controller-afb/ctl-binding.h b/Controller-afb/ctl-binding.h deleted file mode 100644 index 0d16e56..0000000 --- a/Controller-afb/ctl-binding.h +++ /dev/null @@ -1,145 +0,0 @@ -/* - * 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 CONTROLLER_BINDING_INCLUDE -#define CONTROLLER_BINDING_INCLUDE - -#define AFB_BINDING_VERSION 2 -#include <afb/afb-binding.h> -#include <json-c/json.h> -#include <systemd/sd-event.h> -#include "filescan-utils.h" -#include "wrap-json.h" - -#ifdef CONTROL_SUPPORT_LUA -#include "lua.h" -#include "lauxlib.h" -#include "lualib.h" -#endif - -#ifndef PUBLIC - #define PUBLIC -#endif -#define STATIC static - -#ifndef UNUSED_ARG -#define UNUSED_ARG(x) UNUSED_ ## x __attribute__((__unused__)) -#define UNUSED_FUNCTION(x) __attribute__((__unused__)) UNUSED_ ## x -#endif - -// polctl-binding.c -PUBLIC int CtlBindingInit (); - -// ctl-timerevt.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); - -// ctl-policy -// ----------- - -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 int (*Lua2cFunctionT)(char *funcname, json_object *argsJ, void*context); - -PUBLIC int DispatchInit(void); -PUBLIC int DispatchOnLoad(const char *onLoadLabel); -PUBLIC void DispatchOneEvent(const char *evtLabel, json_object *eventJ); -PUBLIC int DispatchOneL2c(lua_State* luaState, char *funcname, Lua2cFunctionT callback); -PUBLIC void ctlapi_dispatch (afb_req request); - -#ifdef CONTROL_SUPPORT_LUA -// ctl-lua.c -typedef int (*Lua2cWrapperT) (lua_State* luaState, char *funcname, Lua2cFunctionT callback); - -#define CTLP_LUA2C(FuncName, label,argsJ, context) static int FuncName(char*label,json_object*argsJ, void*context);\ - int lua2c_ ## FuncName(lua_State* luaState){return((*Lua2cWrap)(luaState, MACRO_STR_VALUE(FuncName), FuncName));};\ - static int FuncName(char* label, json_object* argsJ, void* context) - -typedef enum { - LUA_DOCALL, - LUA_DOSTRING, - LUA_DOSCRIPT, -} LuaDoActionT; - -PUBLIC int LuaLibInit (); -PUBLIC void LuaL2cNewLib(const char *label, luaL_Reg *l2cFunc, int count); -PUBLIC int Lua2cWrapper(lua_State* luaState, char *funcname, Lua2cFunctionT callback, void *context); -PUBLIC int LuaCallFunc (DispatchSourceT source, DispatchActionT *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); - -#else - typedef void* Lua2cWrapperT; -#endif // CONTROL_SUPPORT_LUA - - -// sharelib ctl-plugin* -typedef struct { - long magic; - char *label; -} CtlPluginMagicT; - - -#define MACRO_STR_VALUE(arg) #arg -#define CTLP_REGISTER(pluglabel) CtlPluginMagicT CtlPluginMagic={.magic=CTL_PLUGIN_MAGIC,.label=pluglabel}; struct afb_binding_data_v2; Lua2cWrapperT Lua2cWrap; -#define CTLP_ONLOAD(label,version,info) void* CtlPluginOnload(char* label, char* version, char* info) -#define CTLP_CAPI(funcname,source, label,argsJ, queryJ, context) int funcname(DispatchSourceT source, char* label, json_object* argsJ, json_object* queryJ, void* context) - - - -#endif // CONTROLLER_BINDING_INCLUDE diff --git a/Controller-afb/ctl-dispatch.c b/Controller-afb/ctl-dispatch.c deleted file mode 100644 index 0104229..0000000 --- a/Controller-afb/ctl-dispatch.c +++ /dev/null @@ -1,667 +0,0 @@ -/* - * 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 "ctl-binding.h" - -typedef void*(*DispatchPluginInstallCbT)(const char* label, const char*version, const char*info); - - -static afb_req NULL_AFBREQ = {}; - - -typedef struct { - const char* label; - const char *info; - 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 **onloads; - DispatchHandleT **events; - DispatchHandleT **controls; -} DispatchConfigT; - -// global config handle -STATIC DispatchConfigT *configHandle = NULL; - -STATIC int DispatchControlToIndex(DispatchHandleT **controls, const char* controlLabel) { - - for (int idx = 0; controls[idx]; idx++) { - if (!strcasecmp(controlLabel, controls[idx]->label)) return idx; - } - return -1; -} - -STATIC int DispatchOneControl(DispatchSourceT source, DispatchHandleT **controls, const char* controlLabel, json_object *queryJ, afb_req request) { - int err; - - if (!configHandle) { - AFB_ERROR("DISPATCH-CTL-API: (Hoops/Bug!!!) No Config Loaded"); - goto OnErrorExit; - } - - if (!configHandle->controls) { - AFB_ERROR("DISPATCH-CTL-API: No Control Action in Json config label=%s version=%s", configHandle->label, configHandle->version); - goto OnErrorExit; - } - - int index = DispatchControlToIndex(controls, controlLabel); - if (index < 0 || !controls[index]->actions) { - AFB_ERROR("DISPATCH-CTL-API:NotFound/Error label=%s in Json Control Config File", controlLabel); - goto OnErrorExit; - } - - // 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 = controls[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 = "DispatchOneControl(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); - goto OnErrorExit; - } - break; - } - -#ifdef CONTROL_SUPPORT_LUA - case CTL_MODE_LUA: - err = LuaCallFunc(source, &actions[idx], queryJ); - if (err) { - static const char*format = "DispatchOneControl(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)); - goto OnErrorExit; - } - 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 = "DispatchOneControl(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)); - goto OnErrorExit; - } - break; - - default: - { - static const char*format = "DispatchOneControl(unknown) mode control=%s action=%s"; - AFB_ERROR(format, controls[index]->label); - if (afb_req_is_valid(request))afb_req_fail_f(request, "DISPATCH-CTL-MODE:Unknown", format, controls[index]->label); - } - } - } - - // everything when fine - if (afb_req_is_valid(request))afb_req_success(request, NULL, controls[index]->label); - return 0; - -OnErrorExit: - return -1; -} - - -// Event name is mapped on control label and executed as a standard control - -PUBLIC void DispatchOneEvent(const char *evtLabel, json_object *eventJ) { - DispatchHandleT **events = configHandle->events; - - (void) DispatchOneControl(CTL_SOURCE_EVENT, events, evtLabel, eventJ, NULL_AFBREQ); -} - -// Event name is mapped on control label and executed as a standard control - -PUBLIC int DispatchOnLoad(const char *onLoadLabel) { - if (!configHandle) return 1; - - DispatchHandleT **onloads = configHandle->onloads; - - int err = DispatchOneControl(CTL_SOURCE_ONLOAD, onloads, onLoadLabel, NULL, NULL_AFBREQ); - return err; -} - -PUBLIC void ctlapi_dispatch(afb_req request) { - DispatchHandleT **controls = configHandle->controls; - 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)); - goto OnErrorExit; - } - - (void) DispatchOneControl(source, controls, target, argsJ, request); - -OnErrorExit: - return; -} - -// Wrapper to Lua2c plugin command add context dans delegate to LuaWrapper -PUBLIC 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 - -PUBLIC void ctlapi_config(struct afb_req request) { - json_object*tmpJ; - char *dirList; - - - 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 = strdup(CONTROL_CONFIG_PATH); - AFB_NOTICE("CONFIG-MISSING: use default CONTROL_CONFIG_PATH=%s", CONTROL_CONFIG_PATH); - } - - // 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; - int err, modeCount = 0; - - err = wrap_json_unpack(actionJ, "{ss,s?s,s?s,s?s,s?s,s?s,s?o !}" - , "label", &action->label, "info", &action->info, "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)); - 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 && 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); - goto OnErrorExit; - } - } - - // 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)); - goto OnErrorExit; - } - - if (modeCount > 1) { - AFB_ERROR("DISPATCH-LOAD-ACTION:ToMany arguments lua|callback|(api+verb) in %s", json_object_get_string(actionJ)); - goto OnErrorExit; - } - return 0; - -OnErrorExit: - return -1; -}; - -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) goto OnErrorExit; - } - - } else { - actions = calloc(2, sizeof (DispatchActionT)); - err = DispatchLoadOneAction(controlConfig, actionsJ, &actions[0]); - if (err) goto OnErrorExit; - } - - return actions; - -OnErrorExit: - return NULL; - -} - -STATIC DispatchHandleT *DispatchLoadControl(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,s?o, so !}", "label", &dispatchHandle->label, "info", &dispatchHandle->info - , "permissions", &permissionsJ, "actions", &actionsJ); - if (err) { - AFB_ERROR("DISPATCH-LOAD-CONFIG:CONTROL Missing something label|[info]|actions in %s", json_object_get_string(controlJ)); - goto OnErrorExit; - } - - dispatchHandle->actions = DispatchLoadActions(controlConfig, actionsJ); - if (!dispatchHandle->actions) { - AFB_ERROR("DISPATCH-LOAD-CONFIG:CONTROL Error when parsing actions %s", dispatchHandle->label); - goto OnErrorExit; - } - return dispatchHandle; - -OnErrorExit: - return NULL; -} - -STATIC DispatchHandleT *DispatchLoadOnload(DispatchConfigT *controlConfig, json_object *onloadJ) { - json_object *actionsJ = NULL, *requireJ = NULL, *pluginJ = NULL; - int err; - - DispatchHandleT *dispatchHandle = calloc(1, sizeof (DispatchHandleT)); - err = wrap_json_unpack(onloadJ, "{ss,s?s, s?o,s?o,s?o !}", - "label", &dispatchHandle->label, "info", &dispatchHandle->info, "plugin", &pluginJ, "require", &requireJ, "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; - } - - // best effort to initialise everything before starting - if (requireJ) { - - void DispatchRequireOneApi(json_object * bindindJ) { - const char* requireBinding = json_object_get_string(bindindJ); - err = afb_daemon_require_api(requireBinding, 1); - if (err) { - AFB_WARNING("DISPATCH-LOAD-CONFIG:REQUIRE Fail to get=%s", requireBinding); - } - } - - if (json_object_get_type(requireJ) == json_type_array) { - for (int idx = 0; idx < json_object_array_length(requireJ); idx++) { - DispatchRequireOneApi(json_object_array_get_idx(requireJ, idx)); - } - } else { - DispatchRequireOneApi(requireJ); - } - } - - if (pluginJ) { - 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(onloadJ)); - 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, 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); - 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("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", fullpath, filename); - } - - char pluginpath[CONTROL_MAXPATH_LEN]; - strncpy(pluginpath, fullpath, sizeof (pluginpath)); - strncat(pluginpath, "/", sizeof (pluginpath)); - strncat(pluginpath, filename, sizeof (pluginpath)); - dPlugin->dlHandle = dlopen(pluginpath, RTLD_NOW); - if (!dPlugin->dlHandle) { - AFB_ERROR("DISPATCH-LOAD-CONFIG:PLUGIN Fail to load pluginpath=%s err= %s", pluginpath, dlerror()); - goto OnErrorExit; - } - - 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); - goto OnErrorExit; - } 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)); - - 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); - goto OnErrorExit; - } 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); - } - } - - dispatchHandle->actions = DispatchLoadActions(controlConfig, actionsJ); - if (!dispatchHandle->actions) { - AFB_ERROR("DISPATCH-LOAD-CONFIG:ONLOAD Error when parsing actions %s", dispatchHandle->label); - goto OnErrorExit; - } - return dispatchHandle; - -OnErrorExit: - return NULL; -} - -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); - goto OnErrorExit; - } - - AFB_INFO("DISPATCH-LOAD-CONFIG: loading config filepath=%s", filepath); - - json_object *metadataJ = NULL, *onloadsJ = NULL, *controlsJ = NULL, *eventsJ = NULL; - err = wrap_json_unpack(controlConfigJ, "{s?o,so,s?o,s?o,s?o !}", "$schema", &ignoreJ, "metadata", &metadataJ, "onload", &onloadsJ, "controls", &controlsJ, "events", &eventsJ); - if (err) { - AFB_ERROR("DISPATCH-LOAD-CONFIG Missing something metadata|[onload]|[controls]|[events] in %s", json_object_get_string(controlConfigJ)); - goto OnErrorExit; - } - - DispatchConfigT *controlConfig = calloc(1, sizeof (DispatchConfigT)); - if (metadataJ) { - err = wrap_json_unpack(metadataJ, "{ss,s?s,ss !}", "label", &controlConfig->label, "info", &controlConfig->info, "version", &controlConfig->version); - if (err) { - AFB_ERROR("DISPATCH-LOAD-CONFIG:METADATA Missing something label|version|[label] in %s", json_object_get_string(metadataJ)); - goto OnErrorExit; - } - } - - if (onloadsJ) { - DispatchHandleT *dispatchHandle; - - if (json_object_get_type(onloadsJ) != json_type_array) { - controlConfig->onloads = (DispatchHandleT**) calloc(2, sizeof (void*)); - dispatchHandle = DispatchLoadOnload(controlConfig, onloadsJ); - controlConfig->onloads[0] = dispatchHandle; - } else { - int length = json_object_array_length(onloadsJ); - controlConfig->onloads = (DispatchHandleT**) calloc(length + 1, sizeof (void*)); - - for (int jdx = 0; jdx < length; jdx++) { - json_object *onloadJ = json_object_array_get_idx(onloadsJ, jdx); - dispatchHandle = DispatchLoadOnload(controlConfig, onloadJ); - controlConfig->onloads[jdx] = dispatchHandle; - } - } - } - - if (controlsJ) { - DispatchHandleT *dispatchHandle; - - if (json_object_get_type(controlsJ) != json_type_array) { - controlConfig->controls = (DispatchHandleT**) calloc(2, sizeof (void*)); - dispatchHandle = DispatchLoadControl(controlConfig, controlsJ); - controlConfig->controls[0] = dispatchHandle; - } else { - int length = json_object_array_length(controlsJ); - controlConfig->controls = (DispatchHandleT**) calloc(length + 1, sizeof (void*)); - - for (int jdx = 0; jdx < length; jdx++) { - json_object *controlJ = json_object_array_get_idx(controlsJ, jdx); - dispatchHandle = DispatchLoadControl(controlConfig, controlJ); - controlConfig->controls[jdx] = dispatchHandle; - } - } - } - - if (eventsJ) { - DispatchHandleT *dispatchHandle; - - if (json_object_get_type(eventsJ) != json_type_array) { - controlConfig->events = (DispatchHandleT**) calloc(2, sizeof (void*)); - dispatchHandle = DispatchLoadControl(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 = DispatchLoadControl(controlConfig, eventJ); - controlConfig->events[jdx] = dispatchHandle; - } - } - } - - return controlConfig; - -OnErrorExit: - return NULL; -} - - -// Load default config file at init - -PUBLIC int DispatchInit() { - int index, err, luaLoaded = 0; - char controlFile [CONTROL_MAXPATH_LEN]; - - const char *dirList= getenv("CONTROL_CONFIG_PATH"); - if (!dirList) dirList=CONTROL_CONFIG_PATH; - - 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("DISPATCH-INIT HOOPs invalid JSON entry= %s", json_object_get_string(entryJ)); - goto OnErrorExit; - } - - 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)); - configHandle = DispatchLoadConfig(filepath); - if (!configHandle) { - AFB_ERROR("DISPATCH-INIT:ERROR Fail loading [%s]", filepath); - goto OnErrorExit; - } - luaLoaded = 1; - break; - } - } else { - AFB_WARNING("DISPATCH-INIT:WARNING Secondary Control 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: Audio Control Dispatch Init"); - return 0; - -OnErrorExit: - AFB_NOTICE("DISPATCH-INIT:ERROR: Audio Control Dispatch Init"); - return 1; -} - - - diff --git a/Controller-afb/ctl-lua.c b/Controller-afb/ctl-lua.c deleted file mode 100644 index 8d5a87e..0000000 --- a/Controller-afb/ctl-lua.c +++ /dev/null @@ -1,1068 +0,0 @@ -/* - * 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-binding.h" -#include "wrap-json.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, "LuaAfbServiceCall-Syntax is AFB:service_call (api, verb, query, callback, handle ...."); - 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, "LuaAfbServiceSync-Syntax is error,data=AFB:service_call (api, verb, query)"); - 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, void *context) { - - json_object *argsJ= LuaPopArgs(luaState, LUA_FIST_ARG+1); - int response = (*callback) (funcname, argsJ, context); - - // push response to LUA - lua_pushinteger(luaState, response); - return 1; -} - -// Generated some fake event based on watchdog/counter -PUBLIC int LuaCallFunc (DispatchSourceT source, DispatchActionT *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_pushinteger(luaState, source); - - // 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 */ -}; - -// Create Binding Event at Init -PUBLIC int LuaLibInit () { - int err, index; - - // 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; - - json_object *luaScriptPathJ = ScanForConfig(dirList , CTL_SCAN_RECURSIVE, fullprefix, "lua"); - - // open a new LUA interpretor - luaState = luaL_newstate(); - if (!luaState) { - AFB_ERROR ("LUA_INIT: Fail to open lua interpretor"); - goto OnErrorExit; - } - - // load auxiliary libraries - luaL_openlibs(luaState); - - // redirect print to AFB_NOTICE - luaL_newlib(luaState, afbFunction); - lua_setglobal(luaState, "AFB"); - - // 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;; - } - - // 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-afb/ctl-plugin-sample.c b/Controller-afb/ctl-plugin-sample.c deleted file mode 100644 index 1d66802..0000000 --- a/Controller-afb/ctl-plugin-sample.c +++ /dev/null @@ -1,134 +0,0 @@ -/* - * 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. - * - * Sample plugin for Controller - */ - - -#define _GNU_SOURCE -#include <stdio.h> -#include <string.h> - -#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; -} - -// Declare this sharelib as a Controller Plugin -CTLP_REGISTER("MyCtlSamplePlugin"); - -// Call at initialisation time -PUBLIC CTLP_ONLOAD(label, version, info) { - MyPluginCtxT *pluginCtx= (MyPluginCtxT*)calloc (1, sizeof(MyPluginCtxT)); - pluginCtx->magic = MY_PLUGIN_MAGIC; - pluginCtx->count = -1; - - AFB_NOTICE ("CONTROLLER-PLUGIN-SAMPLE:Onload label=%s version=%s info=%s", label, info, version); - return (void*)pluginCtx; -} - -PUBLIC CTLP_CAPI (SamplePolicyInit, source, label, argsJ, queryJ, context) { - MyPluginCtxT *pluginCtx= (MyPluginCtxT*)context; - if (!context || pluginCtx->magic != MY_PLUGIN_MAGIC) { - AFB_ERROR("CONTROLLER-PLUGIN-SAMPLE:SamplePolicyInit (Hoops) Invalid Sample Plugin Context"); - return -1; - }; - - pluginCtx->count = 0; - AFB_NOTICE ("CONTROLLER-PLUGIN-SAMPLE:Init label=%s args=%s\n", label, jsonToString(argsJ)); - return 0; -} - -PUBLIC CTLP_CAPI (sampleControlMultimedia, source, label, argsJ,queryJ,context) { - MyPluginCtxT *pluginCtx= (MyPluginCtxT*)context; - - if (!context || pluginCtx->magic != MY_PLUGIN_MAGIC) { - AFB_ERROR("CONTROLLER-PLUGIN-SAMPLE:sampleControlMultimedia (Hoops) Invalid Sample Plugin Context"); - return -1; - }; - pluginCtx->count++; - AFB_NOTICE ("CONTROLLER-PLUGIN-SAMPLE:sampleControlMultimedia SamplePolicyCount action=%s args=%s query=%s count=%d" - , label, jsonToString(argsJ), jsonToString(queryJ), pluginCtx->count); - return 0; -} - -PUBLIC CTLP_CAPI (sampleControlNavigation, source, label, argsJ, queryJ, context) { - MyPluginCtxT *pluginCtx= (MyPluginCtxT*)context; - - if (!context || pluginCtx->magic != MY_PLUGIN_MAGIC) { - AFB_ERROR("CONTROLLER-PLUGIN-SAMPLE:sampleControlNavigation (Hoops) Invalid Sample Plugin Context"); - return -1; - }; - pluginCtx->count++; - AFB_NOTICE ("CONTROLLER-PLUGIN-SAMPLE:sampleControlNavigation SamplePolicyCount action=%s args=%s query=%s count=%d" - ,label, jsonToString(argsJ), jsonToString(queryJ), pluginCtx->count); - return 0; -} - -PUBLIC CTLP_CAPI (SampleControlEvent, source, label, argsJ, queryJ, context) { - MyPluginCtxT *pluginCtx= (MyPluginCtxT*)context; - - if (!context || pluginCtx->magic != MY_PLUGIN_MAGIC) { - AFB_ERROR("CONTROLLER-PLUGIN-SAMPLE:cousampleControlMultimediant (Hoops) Invalid Sample Plugin Context"); - return -1; - }; - pluginCtx->count++; - AFB_NOTICE ("CONTROLLER-PLUGIN-SAMPLE:sampleControlMultimedia SamplePolicyCount action=%s args=%s query=%s count=%d" - ,label, jsonToString(argsJ), jsonToString(queryJ), pluginCtx->count); - return 0; -} - -// This function is a LUA function. Lua2CHelloWorld label should be declare in the "onload" section of JSON config file -PUBLIC CTLP_LUA2C (Lua2cHelloWorld1, label, argsJ, context) { - MyPluginCtxT *pluginCtx= (MyPluginCtxT*)context; - - if (!context || pluginCtx->magic != MY_PLUGIN_MAGIC) { - AFB_ERROR("CONTROLLER-PLUGIN-SAMPLE:Lua2cHelloWorld1 (Hoops) Invalid Sample Plugin Context"); - return -1; - }; - pluginCtx->count++; - AFB_NOTICE ("CONTROLLER-PLUGIN-SAMPLE:Lua2cHelloWorld1 SamplePolicyCount action=%s args=%s count=%d" - ,label, jsonToString(argsJ), pluginCtx->count); - return 0; -} - -// This function is a LUA function. Lua2CHelloWorld label should be declare in the "onload" section of JSON config file -PUBLIC CTLP_LUA2C (Lua2cHelloWorld2, label, argsJ, context) { - MyPluginCtxT *pluginCtx= (MyPluginCtxT*)context; - - if (!context || pluginCtx->magic != MY_PLUGIN_MAGIC) { - AFB_ERROR("CONTROLLER-PLUGIN-SAMPLE:Lua2cHelloWorld2 (Hoops) Invalid Sample Plugin Context"); - return -1; - }; - pluginCtx->count++; - AFB_NOTICE ("CONTROLLER-PLUGIN-SAMPLE:Lua2cHelloWorld2 SamplePolicyCount action=%s args=%s count=%d" - ,label, jsonToString(argsJ), pluginCtx->count); - return 0; -} diff --git a/Controller-afb/ctl-policy.c b/Controller-afb/ctl-policy.c deleted file mode 100644 index e9798b3..0000000 --- a/Controller-afb/ctl-policy.c +++ /dev/null @@ -1,221 +0,0 @@ -/* - * 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 <dirent.h> -#include <json-c/json_object.h> - -#include "wrap-json.h" -#include "ctl-binding.h" - -STATIC PolicyCtlConfigT *ctlHandle = NULL; - -// List Avaliable Configuration Files -STATIC json_object* ScanForConfig (char* searchPath) { - json_object *responseJ; - DIR *dirHandle; - char *dirPath; - char* dirList= strdup(searchPath); - - responseJ = json_object_new_array(); - for (dirPath= strtok(dirList, ":"); dirPath && *dirPath; dirPath=strtok(NULL,":")) { - struct dirent *dirEnt; - - dirHandle = opendir (dirPath); - if (!dirHandle) { - AFB_NOTICE ("CONFIG-SCANNING dir=%s not readable", dirPath); - continue; - } - - AFB_NOTICE ("CONFIG-SCANNING:ctl_listconfig scanning: %s", dirPath); - while ((dirEnt = readdir(dirHandle)) != NULL) { - // Unknown type is accepted to support dump filesystems - if (dirEnt->d_type == DT_REG || dirEnt->d_type == DT_UNKNOWN) { - struct json_object *pathJ = json_object_new_object(); - json_object_object_add(pathJ, "dirpath", json_object_new_string(dirPath)); - json_object_object_add(pathJ, "filename", json_object_new_string(dirEnt->d_name)); - json_object_array_add(responseJ, pathJ); - } - } - } - - free (dirList); - return (responseJ); -} - -PUBLIC void ctlapi_authorize (PolicyCtlEnumT control, afb_req request) { - json_object*tmpJ; - - json_object* queryJ= afb_req_json(request); - int done=json_object_object_get_ex(queryJ, "closing", &tmpJ); - if (done) return; - -} - - -// List Avaliable Configuration Files -PUBLIC void ctlapi_config (struct afb_req request) { - json_object*tmpJ; - char *dirList; - - 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 = strdup (CONTROL_CONFIG_PATH); - AFB_NOTICE ("CONFIG-MISSING: use default CONTROL_CONFIG_PATH=%s", CONTROL_CONFIG_PATH); - } - - // get list of config file - struct json_object *responseJ = ScanForConfig(dirList); - - 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; -} - -STATIC PolicyActionT **PolicyLoadActions (json_object *actionsJ) { - int err; - PolicyActionT ** actions; - - // unpack individual action object - int actionUnpack (json_object *actionJ, PolicyActionT *action) { - - err= wrap_json_unpack(actionJ, "{ss,s?s,s?s,s?s,s?s,s?s !}" - , "label",&action->label, "info",&action->info, "callback",&action->callback, "query",&queryJ, "api",&action->api, "verb", &action->verb); - if (err) { - AFB_ERROR ("POLICY-LOAD-ACTION Missing something label|info|callback|api|verb|query in %s", json_object_get_string(actionJ)); - return -1; - } - if (!action->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 policyControl; - -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_CONFIG_PATH); - - 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_CONFIG_FILE)) { - char filepath[255]; - strncpy(filepath, dirpath, sizeof(filepath)); - strncat(filepath, "/", sizeof(filepath)); - strncat(filepath, filename, sizeof(filepath)); - ctlHandle = PolicyLoadConfig (filepath); - if (!ctlHandle) goto OnErrorExit; - break; - } - } - - // no policy config found remove control API from binder - if (index == 0) goto OnErrorExit; - - AFB_NOTICE ("SUCCES: Audio Control Policy Init"); - return 0; - -OnErrorExit: - AFB_NOTICE ("ERROR: Audio Control Policy Init"); - return 1; -} - - - diff --git a/Controller-afb/ctl-timer.c b/Controller-afb/ctl-timer.c deleted file mode 100644 index a826f9f..0000000 --- a/Controller-afb/ctl-timer.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * 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 <systemd/sd-event.h> - -#include "ctl-binding.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-afb/filescan-utils.c b/Controller-afb/filescan-utils.c deleted file mode 100644 index dc5d786..0000000 --- a/Controller-afb/filescan-utils.c +++ /dev/null @@ -1,128 +0,0 @@ -/* - * 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 <sys/prctl.h> -#include <dirent.h> -#include <json-c/json.h> - -#include "ctl-binding.h" - -#ifndef PUBLIC - #define PUBLIC -#endif -#define STATIC static - -// List Avaliable Configuration Files -PUBLIC json_object* ScanForConfig (const char* searchPath, CtlScanDirModeT mode, const char *pre, const char *ext) { - json_object *responseJ; - char *dirPath; - char* dirList= strdup(searchPath); - size_t extLen=0; - - void ScanDir (char *searchPath) { - DIR *dirHandle; - struct dirent *dirEnt; - dirHandle = opendir (searchPath); - if (!dirHandle) { - AFB_DEBUG ("CONFIG-SCANNING dir=%s not readable", searchPath); - return; - } - - //AFB_NOTICE ("CONFIG-SCANNING:ctl_listconfig scanning: %s", searchPath); - while ((dirEnt = readdir(dirHandle)) != NULL) { - - // recursively search embedded directories ignoring any directory starting by '.' or '_' - if (dirEnt->d_type == DT_DIR && mode == CTL_SCAN_RECURSIVE) { - char newpath[CONTROL_MAXPATH_LEN]; - if (dirEnt->d_name[0]=='.' || dirEnt->d_name[0]=='_') continue; - - strncpy(newpath, searchPath, sizeof(newpath)); - strncat(newpath, "/", sizeof(newpath)); - strncat(newpath, dirEnt->d_name, sizeof(newpath)); - ScanDir(newpath); - continue; - } - - // Unknown type is accepted to support dump filesystems - if (dirEnt->d_type == DT_REG || dirEnt->d_type == DT_UNKNOWN) { - - // check prefix and extention - size_t extIdx=strlen(dirEnt->d_name)-extLen; - if (extIdx <= 0) continue; - if (pre && !strcasestr (dirEnt->d_name, pre)) continue; - if (ext && strcasecmp (ext, &dirEnt->d_name[extIdx])) continue; - - struct json_object *pathJ = json_object_new_object(); - json_object_object_add(pathJ, "fullpath", json_object_new_string(searchPath)); - json_object_object_add(pathJ, "filename", json_object_new_string(dirEnt->d_name)); - json_object_array_add(responseJ, pathJ); - } - } - closedir(dirHandle); - } - - if (ext) extLen=strlen(ext); - responseJ = json_object_new_array(); - - // loop recursively on dir - for (dirPath= strtok(dirList, ":"); dirPath && *dirPath; dirPath=strtok(NULL,":")) { - ScanDir (dirPath); - } - - free (dirList); - return (responseJ); -} - -PUBLIC const char *GetMidleName(const char*name) { - char *fullname = strdup(name); - - for (int idx = 0; fullname[idx] != '\0'; idx++) { - int start; - if (fullname[idx] == '-') { - start = idx + 1; - for (int jdx = start; ; jdx++) { - if (fullname[jdx] == '-' || fullname[jdx] == '.' || fullname[jdx] == '\0') { - fullname[jdx] = '\0'; - return &fullname[start]; - break; - } - } - break; - } - } - return ""; -} - -PUBLIC const char *GetBinderName() { - char psName[17]; - static char *binderName=NULL; - - if (binderName) return binderName; - - binderName= getenv("AFB_BINDER_NAME"); - if (!binderName) { - // retrieve binder name from process name afb-name-trailer - prctl(PR_GET_NAME, psName,NULL,NULL,NULL); - binderName=(char*)GetMidleName(psName); - } - - return binderName; -} diff --git a/Controller-afb/filescan-utils.h b/Controller-afb/filescan-utils.h deleted file mode 100644 index cbe15de..0000000 --- a/Controller-afb/filescan-utils.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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. - * - * reference: - * amixer contents; amixer controls; - * http://www.tldp.org/HOWTO/Alsa-sound-6.html - */ - -#ifndef FILESCAN_UTILS_H -#define FILESCAN_UTILS_H - -#ifndef PUBLIC - #define PUBLIC -#endif -#define STATIC static - -// ctl-misc.c -typedef enum { - CTL_SCAN_FLAT=0, - CTL_SCAN_RECURSIVE=1, -} CtlScanDirModeT; - -PUBLIC const char *GetMidleName(const char*name); -PUBLIC const char *GetBinderName(); -PUBLIC json_object* ScanForConfig (const char* searchPath, CtlScanDirModeT mode, const char *pre, const char *ext); - -#endif /* FILESCAN_UTILS_H */ - diff --git a/Controller-afb/wrap-json.c b/Controller-afb/wrap-json.c deleted file mode 100644 index 164e127..0000000 --- a/Controller-afb/wrap-json.c +++ /dev/null @@ -1,939 +0,0 @@ -/* - Copyright (C) 2016, 2017 "IoT.bzh" - - author: José Bollo <jose.bollo@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 <string.h> - -#include "wrap-json.h" - -#define STACKCOUNT 32 -#define STRCOUNT 8 - -enum { - wrap_json_error_none, - wrap_json_error_null_object, - wrap_json_error_truncated, - wrap_json_error_internal_error, - wrap_json_error_out_of_memory, - wrap_json_error_invalid_character, - wrap_json_error_too_long, - wrap_json_error_too_deep, - wrap_json_error_null_spec, - wrap_json_error_null_key, - wrap_json_error_null_string, - wrap_json_error_out_of_range, - wrap_json_error_incomplete, - wrap_json_error_missfit_type, - wrap_json_error_key_not_found, - _wrap_json_error_count_ -}; - -static const char ignore_all[] = " \t\n\r,:"; -static const char pack_accept_arr[] = "][{snbiIfoO"; -static const char pack_accept_key[] = "s}"; -#define pack_accept_any (&pack_accept_arr[1]) - -static const char unpack_accept_arr[] = "*!][{snbiIfFoO"; -static const char unpack_accept_key[] = "*!s}"; -#define unpack_accept_any (&unpack_accept_arr[3]) - -static const char *pack_errors[_wrap_json_error_count_] = -{ - [wrap_json_error_none] = "unknown error", - [wrap_json_error_null_object] = "null object", - [wrap_json_error_truncated] = "truncated", - [wrap_json_error_internal_error] = "internal error", - [wrap_json_error_out_of_memory] = "out of memory", - [wrap_json_error_invalid_character] = "invalid character", - [wrap_json_error_too_long] = "too long", - [wrap_json_error_too_deep] = "too deep", - [wrap_json_error_null_spec] = "spec is NULL", - [wrap_json_error_null_key] = "key is NULL", - [wrap_json_error_null_string] = "string is NULL", - [wrap_json_error_out_of_range] = "array too small", - [wrap_json_error_incomplete] = "incomplete container", - [wrap_json_error_missfit_type] = "missfit of type", - [wrap_json_error_key_not_found] = "key not found" -}; - -int wrap_json_get_error_position(int rc) -{ - if (rc < 0) - rc = -rc; - return (rc >> 4) + 1; -} - -int wrap_json_get_error_code(int rc) -{ - if (rc < 0) - rc = -rc; - return rc & 15; -} - -const char *wrap_json_get_error_string(int rc) -{ - rc = wrap_json_get_error_code(rc); - if (rc >= sizeof pack_errors / sizeof *pack_errors) - rc = 0; - return pack_errors[rc]; -} - - - -static inline const char *skip(const char *d) -{ - while (*d && strchr(ignore_all, *d)) - d++; - return d; -} - -int wrap_json_vpack(struct json_object **result, const char *desc, va_list args) -{ - /* TODO: the case of structs with key being single char should be optimized */ - int nstr, notnull, nullable, rc; - size_t sz, dsz, ssz; - char *s; - char c; - const char *d; - char buffer[256]; - struct { const char *str; size_t sz; } strs[STRCOUNT]; - struct { struct json_object *cont, *key; const char *acc; char type; } stack[STACKCOUNT], *top; - struct json_object *obj; - - ssz = sizeof buffer; - s = buffer; - top = stack; - top->key = NULL; - top->cont = NULL; - top->acc = pack_accept_any; - top->type = 0; - d = desc; - if (!d) - goto null_spec; - d = skip(d); - for(;;) { - c = *d; - if (!c) - goto truncated; - if (!strchr(top->acc, c)) - goto invalid_character; - d = skip(++d); - switch(c) { - case 's': - nullable = 0; - notnull = 0; - nstr = 0; - sz = 0; - for (;;) { - strs[nstr].str = va_arg(args, const char*); - if (strs[nstr].str) - notnull = 1; - if (*d == '?') { - d = skip(++d); - nullable = 1; - } - switch(*d) { - case '%': strs[nstr].sz = va_arg(args, size_t); d = skip(++d); break; - case '#': strs[nstr].sz = (size_t)va_arg(args, int); d = skip(++d); break; - default: strs[nstr].sz = strs[nstr].str ? strlen(strs[nstr].str) : 0; break; - } - sz += strs[nstr++].sz; - if (*d == '?') { - d = skip(++d); - nullable = 1; - } - if (*d != '+') - break; - if (nstr >= STRCOUNT) - goto too_long; - d = skip(++d); - } - if (*d == '*') - nullable = 1; - if (notnull) { - if (sz > ssz) { - ssz += ssz; - if (ssz < sz) - ssz = sz; - s = alloca(sz); - } - dsz = sz; - while (nstr) { - nstr--; - dsz -= strs[nstr].sz; - memcpy(&s[dsz], strs[nstr].str, strs[nstr].sz); - } - obj = json_object_new_string_len(s, (int)sz); - if (!obj) - goto out_of_memory; - } else if (nullable) - obj = NULL; - else - goto null_string; - break; - case 'n': - obj = NULL; - break; - case 'b': - obj = json_object_new_boolean(va_arg(args, int)); - if (!obj) - goto out_of_memory; - break; - case 'i': - obj = json_object_new_int(va_arg(args, int)); - if (!obj) - goto out_of_memory; - break; - case 'I': - obj = json_object_new_int64(va_arg(args, int64_t)); - if (!obj) - goto out_of_memory; - break; - case 'f': - obj = json_object_new_double(va_arg(args, double)); - if (!obj) - goto out_of_memory; - break; - case 'o': - case 'O': - obj = va_arg(args, struct json_object*); - if (*d == '?') - d = skip(++d); - else if (*d != '*' && !obj) - goto null_object; - if (c == 'O') - json_object_get(obj); - break; - case '[': - case '{': - if (++top >= &stack[STACKCOUNT]) - goto too_deep; - top->key = NULL; - if (c == '[') { - top->type = ']'; - top->acc = pack_accept_arr; - top->cont = json_object_new_array(); - } else { - top->type = '}'; - top->acc = pack_accept_key; - top->cont = json_object_new_object(); - } - if (!top->cont) - goto out_of_memory; - continue; - case '}': - case ']': - if (c != top->type || top <= stack) - goto internal_error; - obj = (top--)->cont; - if (*d == '*' && !(c == '}' ? json_object_object_length(obj) : json_object_array_length(obj))) { - json_object_put(obj); - obj = NULL; - } - break; - default: - goto internal_error; - } - switch (top->type) { - case 0: - if (top != stack) - goto internal_error; - if (*d) - goto invalid_character; - *result = obj; - return 0; - case ']': - if (obj || *d != '*') - json_object_array_add(top->cont, obj); - if (*d == '*') - d = skip(++d); - break; - case '}': - if (!obj) - goto null_key; - top->key = obj; - top->acc = pack_accept_any; - top->type = ':'; - break; - case ':': - if (obj || *d != '*') - json_object_object_add(top->cont, json_object_get_string(top->key), obj); - if (*d == '*') - d = skip(++d); - json_object_put(top->key); - top->key = NULL; - top->acc = pack_accept_key; - top->type = '}'; - break; - default: - goto internal_error; - } - } - -null_object: - rc = wrap_json_error_null_object; - goto error; -truncated: - rc = wrap_json_error_truncated; - goto error; -internal_error: - rc = wrap_json_error_internal_error; - goto error; -out_of_memory: - rc = wrap_json_error_out_of_memory; - goto error; -invalid_character: - rc = wrap_json_error_invalid_character; - goto error; -too_long: - rc = wrap_json_error_too_long; - goto error; -too_deep: - rc = wrap_json_error_too_deep; - goto error; -null_spec: - rc = wrap_json_error_null_spec; - goto error; -null_key: - rc = wrap_json_error_null_key; - goto error; -null_string: - rc = wrap_json_error_null_string; - goto error; -error: - do { - json_object_put(top->key); - json_object_put(top->cont); - } while (--top >= stack); - *result = NULL; - rc = rc | (int)((d - desc) << 4); - return -rc; -} - -int wrap_json_pack(struct json_object **result, const char *desc, ...) -{ - int rc; - va_list args; - - va_start(args, desc); - rc = wrap_json_vpack(result, desc, args); - va_end(args); - return rc; -} - -static int vunpack(struct json_object *object, const char *desc, va_list args, int store) -{ - int rc = 0, optionnal, ignore; - char c, xacc[2] = { 0, 0 }; - const char *acc; - const char *d, *fit = NULL; - const char *key = NULL; - const char **ps = NULL; - double *pf = NULL; - int *pi = NULL; - int64_t *pI = NULL; - size_t *pz = NULL; - struct { struct json_object *parent; const char *acc; int index, count; char type; } stack[STACKCOUNT], *top; - struct json_object *obj; - struct json_object **po; - - xacc[0] = 0; - ignore = 0; - top = NULL; - acc = unpack_accept_any; - d = desc; - if (!d) - goto null_spec; - d = skip(d); - obj = object; - for(;;) { - fit = d; - c = *d; - if (!c) - goto truncated; - if (!strchr(acc, c)) - goto invalid_character; - d = skip(++d); - switch(c) { - case 's': - if (xacc[0] == '}') { - /* expects a key */ - key = va_arg(args, const char *); - if (!key) - goto null_key; - if (*d != '?') - optionnal = 0; - else { - optionnal = 1; - d = skip(++d); - } - if (ignore) - ignore++; - else { - if (json_object_object_get_ex(top->parent, key, &obj)) { - /* found */ - top->index++; - } else { - /* not found */ - if (!optionnal) - goto key_not_found; - ignore = 1; - obj = NULL; - } - } - xacc[0] = ':'; - acc = unpack_accept_any; - continue; - } - /* get a string */ - if (store) - ps = va_arg(args, const char **); - if (!ignore) { - if (!json_object_is_type(obj, json_type_string)) - goto missfit; - if (store && ps) - *ps = json_object_get_string(obj); - } - if (*d == '%') { - d = skip(++d); - if (store) { - pz = va_arg(args, size_t *); - if (!ignore && pz) - *pz = (size_t)json_object_get_string_len(obj); - } - } - break; - case 'n': - if (!ignore && !json_object_is_type(obj, json_type_null)) - goto missfit; - break; - case 'b': - if (store) - pi = va_arg(args, int *); - - if (!ignore) { - if (!json_object_is_type(obj, json_type_boolean)) - goto missfit; - if (store && pi) - *pi = json_object_get_boolean(obj); - } - break; - case 'i': - if (store) - pi = va_arg(args, int *); - - if (!ignore) { - if (!json_object_is_type(obj, json_type_int)) - goto missfit; - if (store && pi) - *pi = json_object_get_int(obj); - } - break; - case 'I': - if (store) - pI = va_arg(args, int64_t *); - - if (!ignore) { - if (!json_object_is_type(obj, json_type_int)) - goto missfit; - if (store && pI) - *pI = json_object_get_int64(obj); - } - break; - case 'f': - case 'F': - if (store) - pf = va_arg(args, double *); - - if (!ignore) { - if (!(json_object_is_type(obj, json_type_double) || (c == 'F' && json_object_is_type(obj, json_type_int)))) - goto missfit; - if (store && pf) - *pf = json_object_get_double(obj); - } - break; - case 'o': - case 'O': - if (store) { - po = va_arg(args, struct json_object **); - if (!ignore && po) { - if (c == 'O') - obj = json_object_get(obj); - *po = obj; - } - } - break; - - case '[': - case '{': - if (!top) - top = stack; - else if (++top >= &stack[STACKCOUNT]) - goto too_deep; - - top->acc = acc; - top->type = xacc[0]; - top->index = 0; - top->parent = obj; - if (ignore) - ignore++; - if (c == '[') { - if (!ignore) { - if (!json_object_is_type(obj, json_type_array)) - goto missfit; - top->count = json_object_array_length(obj); - } - xacc[0] = ']'; - acc = unpack_accept_arr; - } else { - if (!ignore) { - if (!json_object_is_type(obj, json_type_object)) - goto missfit; - top->count = json_object_object_length(obj); - } - xacc[0] = '}'; - acc = unpack_accept_key; - continue; - } - break; - case '}': - case ']': - if (!top || c != xacc[0]) - goto internal_error; - acc = top->acc; - xacc[0] = top->type; - top = top == stack ? NULL : top - 1; - if (ignore) - ignore--; - break; - case '!': - if (*d != xacc[0]) - goto invalid_character; - if (!ignore && top->index != top->count) - goto incomplete; - /*@fallthrough@*/ - case '*': - acc = xacc; - continue; - default: - goto internal_error; - } - switch (xacc[0]) { - case 0: - if (top) - goto internal_error; - if (*d) - goto invalid_character; - return 0; - case ']': - if (!ignore) { - key = strchr(unpack_accept_arr, *d); - if (key && key >= unpack_accept_any) { - if (top->index >= top->count) - goto out_of_range; - obj = json_object_array_get_idx(top->parent, top->index++); - } - } - break; - case ':': - acc = unpack_accept_key; - xacc[0] = '}'; - if (ignore) - ignore--; - break; - default: - goto internal_error; - } - } -truncated: - rc = wrap_json_error_truncated; - goto error; -internal_error: - rc = wrap_json_error_internal_error; - goto error; -invalid_character: - rc = wrap_json_error_invalid_character; - goto error; -too_deep: - rc = wrap_json_error_too_deep; - goto error; -null_spec: - rc = wrap_json_error_null_spec; - goto error; -null_key: - rc = wrap_json_error_null_key; - goto error; -out_of_range: - rc = wrap_json_error_out_of_range; - goto error; -incomplete: - rc = wrap_json_error_incomplete; - goto error; -missfit: - rc = wrap_json_error_missfit_type; - goto errorfit; -key_not_found: - rc = wrap_json_error_key_not_found; - goto error; -errorfit: - d = fit; -error: - rc = rc | (int)((d - desc) << 4); - return -rc; -} - -int wrap_json_vcheck(struct json_object *object, const char *desc, va_list args) -{ - return vunpack(object, desc, args, 0); -} - -int wrap_json_check(struct json_object *object, const char *desc, ...) -{ - int rc; - va_list args; - - va_start(args, desc); - rc = vunpack(object, desc, args, 0); - va_end(args); - return rc; -} - -int wrap_json_vmatch(struct json_object *object, const char *desc, va_list args) -{ - return !vunpack(object, desc, args, 0); -} - -int wrap_json_match(struct json_object *object, const char *desc, ...) -{ - int rc; - va_list args; - - va_start(args, desc); - rc = vunpack(object, desc, args, 0); - va_end(args); - return !rc; -} - -int wrap_json_vunpack(struct json_object *object, const char *desc, va_list args) -{ - return vunpack(object, desc, args, 1); -} - -int wrap_json_unpack(struct json_object *object, const char *desc, ...) -{ - int rc; - va_list args; - - va_start(args, desc); - rc = vunpack(object, desc, args, 1); - va_end(args); - return rc; -} - -static void object_for_all(struct json_object *object, void (*callback)(void*,struct json_object*,const char*), void *closure) -{ - struct json_object_iterator it = json_object_iter_begin(object); - struct json_object_iterator end = json_object_iter_end(object); - while (!json_object_iter_equal(&it, &end)) { - callback(closure, json_object_iter_peek_value(&it), json_object_iter_peek_name(&it)); - json_object_iter_next(&it); - } -} - -static void array_for_all(struct json_object *object, void (*callback)(void*,struct json_object*), void *closure) -{ - int n = json_object_array_length(object); - int i = 0; - while(i < n) - callback(closure, json_object_array_get_idx(object, i++)); -} - -void wrap_json_optarray_for_all(struct json_object *object, void (*callback)(void*,struct json_object*), void *closure) -{ - if (json_object_is_type(object, json_type_array)) - array_for_all(object, callback, closure); - else - callback(closure, object); -} - -void wrap_json_array_for_all(struct json_object *object, void (*callback)(void*,struct json_object*), void *closure) -{ - if (json_object_is_type(object, json_type_array)) - array_for_all(object, callback, closure); -} - -void wrap_json_object_for_all(struct json_object *object, void (*callback)(void*,struct json_object*,const char*), void *closure) -{ - if (json_object_is_type(object, json_type_object)) - object_for_all(object, callback, closure); -} - -void wrap_json_optobject_for_all(struct json_object *object, void (*callback)(void*,struct json_object*,const char*), void *closure) -{ - if (json_object_is_type(object, json_type_object)) - object_for_all(object, callback, closure); - else - callback(closure, object, NULL); -} - -void wrap_json_for_all(struct json_object *object, void (*callback)(void*,struct json_object*,const char*), void *closure) -{ - if (!object) - /* do nothing */; - else if (json_object_is_type(object, json_type_object)) - object_for_all(object, callback, closure); - else if (!json_object_is_type(object, json_type_array)) - callback(closure, object, NULL); - else { - int n = json_object_array_length(object); - int i = 0; - while(i < n) - callback(closure, json_object_array_get_idx(object, i++), NULL); - } -} - -#if defined(WRAP_JSON_TEST) -#include <stdio.h> - -void p(const char *desc, ...) -{ - int rc; - va_list args; - struct json_object *result; - - va_start(args, desc); - rc = wrap_json_vpack(&result, desc, args); - va_end(args); - if (!rc) - printf(" SUCCESS %s\n\n", json_object_to_json_string(result)); - else - printf(" ERROR[char %d err %d] %s\n\n", wrap_json_get_error_position(rc), wrap_json_get_error_code(rc), wrap_json_get_error_string(rc)); - json_object_put(result); -} - -const char *xs[10]; -int *xi[10]; -int64_t *xI[10]; -double *xf[10]; -struct json_object *xo[10]; -size_t xz[10]; - -void u(const char *value, const char *desc, ...) -{ - unsigned m, k; - int rc; - va_list args; - struct json_object *obj, *o; - - memset(xs, 0, sizeof xs); - memset(xi, 0, sizeof xi); - memset(xI, 0, sizeof xI); - memset(xf, 0, sizeof xf); - memset(xo, 0, sizeof xo); - memset(xz, 0, sizeof xz); - obj = json_tokener_parse(value); - va_start(args, desc); - rc = wrap_json_vunpack(obj, desc, args); - va_end(args); - if (rc) - printf(" ERROR[char %d err %d] %s\n\n", wrap_json_get_error_position(rc), wrap_json_get_error_code(rc), wrap_json_get_error_string(rc)); - else { - value = NULL; - printf(" SUCCESS"); - va_start(args, desc); - k = m = 0; - while(*desc) { - switch(*desc) { - case '{': m = (m << 1) | 1; k = 1; break; - case '}': m = m >> 1; k = m&1; break; - case '[': m = m << 1; k = 0; break; - case ']': m = m >> 1; k = m&1; break; - case 's': printf(" s:%s", k ? va_arg(args, const char*) : *(va_arg(args, const char**)?:&value)); k ^= m&1; break; - case '%': printf(" %%:%zu", *va_arg(args, size_t*)); k = m&1; break; - case 'n': printf(" n"); k = m&1; break; - case 'b': printf(" b:%d", *va_arg(args, int*)); k = m&1; break; - case 'i': printf(" i:%d", *va_arg(args, int*)); k = m&1; break; - case 'I': printf(" I:%lld", *va_arg(args, int64_t*)); k = m&1; break; - case 'f': printf(" f:%f", *va_arg(args, double*)); k = m&1; break; - case 'F': printf(" F:%f", *va_arg(args, double*)); k = m&1; break; - case 'o': printf(" o:%s", json_object_to_json_string(*va_arg(args, struct json_object**))); k = m&1; break; - case 'O': o = *va_arg(args, struct json_object**); printf(" O:%s", json_object_to_json_string(o)); json_object_put(o); k = m&1; break; - default: break; - } - desc++; - } - va_end(args); - printf("\n\n"); - } - json_object_put(obj); -} - -#define P(...) do{ printf("pack(%s)\n",#__VA_ARGS__); p(__VA_ARGS__); } while(0) -#define U(...) do{ printf("unpack(%s)\n",#__VA_ARGS__); u(__VA_ARGS__); } while(0) - -int main() -{ - char buffer[4] = {'t', 'e', 's', 't'}; - - P("n"); - P("b", 1); - P("b", 0); - P("i", 1); - P("I", (uint64_t)0x123456789abcdef); - P("f", 3.14); - P("s", "test"); - P("s?", "test"); - P("s?", NULL); - P("s#", "test asdf", 4); - P("s%", "test asdf", (size_t)4); - P("s#", buffer, 4); - P("s%", buffer, (size_t)4); - P("s++", "te", "st", "ing"); - P("s#+#+", "test", 1, "test", 2, "test"); - P("s%+%+", "test", (size_t)1, "test", (size_t)2, "test"); - P("{}", 1.0); - P("[]", 1.0); - P("o", json_object_new_int(1)); - P("o?", json_object_new_int(1)); - P("o?", NULL); - P("O", json_object_new_int(1)); - P("O?", json_object_new_int(1)); - P("O?", NULL); - P("{s:[]}", "foo"); - P("{s+#+: []}", "foo", "barbar", 3, "baz"); - P("{s:s,s:o,s:O}", "a", NULL, "b", NULL, "c", NULL); - P("{s:**}", "a", NULL); - P("{s:s*,s:o*,s:O*}", "a", NULL, "b", NULL, "c", NULL); - P("[i,i,i]", 0, 1, 2); - P("[s,o,O]", NULL, NULL, NULL); - P("[**]", NULL); - P("[s*,o*,O*]", NULL, NULL, NULL); - P(" s ", "test"); - P("[ ]"); - P("[ i , i, i ] ", 1, 2, 3); - P("{\n\n1"); - P("[}"); - P("{]"); - P("["); - P("{"); - P("[i]a", 42); - P("ia", 42); - P("s", NULL); - P("+", NULL); - P(NULL); - P("{s:i}", NULL, 1); - P("{ {}: s }", "foo"); - P("{ s: {}, s:[ii{} }", "foo", "bar", 12, 13); - P("[[[[[ [[[[[ [[[[ }]]]] ]]]] ]]]]]"); - - U("true", "b", &xi[0]); - U("false", "b", &xi[0]); - U("null", "n"); - U("42", "i", &xi[0]); - U("123456789", "I", &xI[0]); - U("3.14", "f", &xf[0]); - U("12345", "F", &xf[0]); - U("3.14", "F", &xf[0]); - U("\"foo\"", "s", &xs[0]); - U("\"foo\"", "s%", &xs[0], &xz[0]); - U("{}", "{}"); - U("[]", "[]"); - U("{}", "o", &xo[0]); - U("{}", "O", &xo[0]); - U("{\"foo\":42}", "{si}", "foo", &xi[0]); - U("[1,2,3]", "[i,i,i]", &xi[0], &xi[1], &xi[2]); - U("{\"a\":1,\"b\":2,\"c\":3}", "{s:i, s:i, s:i}", "a", &xi[0], "b", &xi[1], "c", &xi[2]); - U("42", "z"); - U("null", "[i]"); - U("[]", "[}"); - U("{}", "{]"); - U("[]", "["); - U("{}", "{"); - U("[42]", "[i]a", &xi[0]); - U("42", "ia", &xi[0]); - U("[]", NULL); - U("\"foo\"", "s", NULL); - U("42", "s", NULL); - U("42", "n"); - U("42", "b", NULL); - U("42", "f", NULL); - U("42", "[i]", NULL); - U("42", "{si}", "foo", NULL); - U("\"foo\"", "n"); - U("\"foo\"", "b", NULL); - U("\"foo\"", "i", NULL); - U("\"foo\"", "I", NULL); - U("\"foo\"", "f", NULL); - U("\"foo\"", "F", NULL); - U("true", "s", NULL); - U("true", "n"); - U("true", "i", NULL); - U("true", "I", NULL); - U("true", "f", NULL); - U("true", "F", NULL); - U("[42]", "[ii]", &xi[0], &xi[1]); - U("{\"foo\":42}", "{si}", NULL, &xi[0]); - U("{\"foo\":42}", "{si}", "baz", &xi[0]); - U("[1,2,3]", "[iii!]", &xi[0], &xi[1], &xi[2]); - U("[1,2,3]", "[ii!]", &xi[0], &xi[1]); - U("[1,2,3]", "[ii]", &xi[0], &xi[1]); - U("[1,2,3]", "[ii*]", &xi[0], &xi[1]); - U("{\"foo\":42,\"baz\":45}", "{sisi}", "baz", &xi[0], "foo", &xi[1]); - U("{\"foo\":42,\"baz\":45}", "{sisi*}", "baz", &xi[0], "foo", &xi[1]); - U("{\"foo\":42,\"baz\":45}", "{sisi!}", "baz", &xi[0], "foo", &xi[1]); - U("{\"foo\":42,\"baz\":45}", "{si}", "baz", &xi[0], "foo", &xi[1]); - U("{\"foo\":42,\"baz\":45}", "{si*}", "baz", &xi[0], "foo", &xi[1]); - U("{\"foo\":42,\"baz\":45}", "{si!}", "baz", &xi[0], "foo", &xi[1]); - U("[1,{\"foo\":2,\"bar\":null},[3,4]]", "[i{sisn}[ii]]", &xi[0], "foo", &xi[1], "bar", &xi[2], &xi[3]); - U("[1,2,3]", "[ii!i]", &xi[0], &xi[1], &xi[2]); - U("[1,2,3]", "[ii*i]", &xi[0], &xi[1], &xi[2]); - U("{\"foo\":1,\"bar\":2}", "{si!si}", "foo", &xi[1], "bar", &xi[2]); - U("{\"foo\":1,\"bar\":2}", "{si*si}", "foo", &xi[1], "bar", &xi[2]); - U("{\"foo\":{\"baz\":null,\"bar\":null}}", "{s{sn!}}", "foo", "bar"); - U("[[1,2,3]]", "[[ii!]]", &xi[0], &xi[1]); - U("{}", "{s?i}", "foo", &xi[0]); - U("{\"foo\":1}", "{s?i}", "foo", &xi[0]); - U("{}", "{s?[ii]s?{s{si!}}}", "foo", &xi[0], &xi[1], "bar", "baz", "quux", &xi[2]); - U("{\"foo\":[1,2]}", "{s?[ii]s?{s{si!}}}", "foo", &xi[0], &xi[1], "bar", "baz", "quux", &xi[2]); - U("{\"bar\":{\"baz\":{\"quux\":15}}}", "{s?[ii]s?{s{si!}}}", "foo", &xi[0], &xi[1], "bar", "baz", "quux", &xi[2]); - U("{\"foo\":{\"bar\":4}}", "{s?{s?i}}", "foo", "bar", &xi[0]); - U("{\"foo\":{}}", "{s?{s?i}}", "foo", "bar", &xi[0]); - U("{}", "{s?{s?i}}", "foo", "bar", &xi[0]); - U("{\"foo\":42,\"baz\":45}", "{s?isi!}", "baz", &xi[0], "foo", &xi[1]); - U("{\"foo\":42}", "{s?isi!}", "baz", &xi[0], "foo", &xi[1]); - return 0; -} - -#endif - -#if 0 - - - /* Unpack the same item twice */ - j = json_pack("{s:s, s:i, s:b}", "foo", "bar", "baz", 42, "quux", 1); - if(!json_unpack_ex(j, &error, 0, "{s:s,s:s!}", "foo", &s, "foo", &s)) - fail("json_unpack object with strict validation failed"); - { - const char *possible_errors[] = { - "2 object item(s) left unpacked: baz, quux", - "2 object item(s) left unpacked: quux, baz" - }; - check_errors(possible_errors, 2, "<validation>", 1, 10, 10); - } - json_decref(j); - -#endif diff --git a/Controller-afb/wrap-json.h b/Controller-afb/wrap-json.h deleted file mode 100644 index cb2d0bf..0000000 --- a/Controller-afb/wrap-json.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - Copyright (C) 2016, 2017 "IoT.bzh" - - author: José Bollo <jose.bollo@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 <stdarg.h> -#include <json-c/json.h> - -extern int wrap_json_get_error_position(int rc); -extern int wrap_json_get_error_code(int rc); -extern const char *wrap_json_get_error_string(int rc); - -extern int wrap_json_vpack(struct json_object **result, const char *desc, va_list args); -extern int wrap_json_pack(struct json_object **result, const char *desc, ...); - -extern int wrap_json_vunpack(struct json_object *object, const char *desc, va_list args); -extern int wrap_json_unpack(struct json_object *object, const char *desc, ...); -extern int wrap_json_vcheck(struct json_object *object, const char *desc, va_list args); -extern int wrap_json_check(struct json_object *object, const char *desc, ...); -extern int wrap_json_vmatch(struct json_object *object, const char *desc, va_list args); -extern int wrap_json_match(struct json_object *object, const char *desc, ...); - -extern void wrap_json_optarray_for_all(struct json_object *object, void (*callback)(void*,struct json_object*), void *closure); -extern void wrap_json_array_for_all(struct json_object *object, void (*callback)(void*,struct json_object*), void *closure); - -extern void wrap_json_optarray_for_all(struct json_object *object, void (*callback)(void*,struct json_object*), void *closure); -extern void wrap_json_array_for_all(struct json_object *object, void (*callback)(void*,struct json_object*), void *closure); -extern void wrap_json_object_for_all(struct json_object *object, void (*callback)(void*,struct json_object*,const char*), void *closure); -extern void wrap_json_optobject_for_all(struct json_object *object, void (*callback)(void*,struct json_object*,const char*), void *closure); -extern void wrap_json_for_all(struct json_object *object, void (*callback)(void*,struct json_object*,const char*), void *closure); - diff --git a/Controller-afb/wrap-json.md b/Controller-afb/wrap-json.md deleted file mode 100644 index 92940f1..0000000 --- a/Controller-afb/wrap-json.md +++ /dev/null @@ -1,305 +0,0 @@ -WRAP-JSON facility -================== - -The facility wrap-json is based on the pack/unpack API on the -libray jansson. The two chapters below are copied from the -documentation of jansson library copyrighted by Petri Lehtinen -(see at end). - -Building Values ---------------- - -This section describes functions that help to create, or *pack*, complex -JSON values, especially nested objects and arrays. Value building is -based on a *format string* that is used to tell the functions about the -expected arguments. - -For example, the format string `"i"` specifies a single integer value, -while the format string `"[ssb]"` or the equivalent `"[s, s, b]"` -specifies an array value with two strings and a boolean as its items: - - /* Create the JSON integer 42 */ - wrap_json_pack(&result, "i", 42); - - /* Create the JSON array ["foo", "bar", true] */ - wrap_json_pack(&result, "[ssb]", "foo", "bar", 1); - -Here's the full list of format specifiers. The type in parentheses -denotes the resulting JSON type, and the type in brackets (if any) -denotes the C type that is expected as the corresponding argument or -arguments. - -`s` (string) \[const char \*\] - -: Convert a null terminated UTF-8 string to a JSON string. - -`s?` (string) \[const char \*\] - -: Like `s`, but if the argument is *NULL*, output a JSON null value. - -`s*` (string) \[const char \*\] - -: Like `s`, but if the argument is *NULL*, do not output any value. - This format can only be used inside an object or an array. If used - inside an object, the corresponding key is additionally suppressed - when the value is omitted. See below for an example. - -`s#` (string) \[const char \*, int\] - -: Convert a UTF-8 buffer of a given length to a JSON string. - -`s%` (string) \[const char \*, size\_t\] - -: Like `s#` but the length argument is of type size\_t. - -`+` \[const char \*\] - -: Like `s`, but concatenate to the previous string. Only valid after - `s`, `s#`, `+` or `+#`. - -`+#` \[const char \*, int\] - -: Like `s#`, but concatenate to the previous string. Only valid after - `s`, `s#`, `+` or `+#`. - -`+%` (string) \[const char \*, size\_t\] - -: Like `+#` but the length argument is of type size\_t. - -`n` (null) - -: Output a JSON null value. No argument is consumed. - -`b` (boolean) \[int\] - -: Convert a C int to JSON boolean value. Zero is converted to `false` - and non-zero to `true`. - -`i` (integer) \[int\] - -: Convert a C int to JSON integer. - -`I` (integer) \[json\_int\_t\] - -: Convert a C json\_int\_t to JSON integer. - -`f` (real) \[double\] - -: Convert a C double to JSON real. - -`o` (any value) \[json\_t \*\] - -: Output any given JSON value as-is. If the value is added to an array - or object, the reference to the value passed to `o` is stolen by the - container. - -`O` (any value) \[json\_t \*\] - -: Like `o`, but the argument's reference count is incremented. This is - useful if you pack into an array or object and want to keep the - reference for the JSON value consumed by `O` to yourself. - -`o?`, `O?` (any value) \[json\_t \*\] - -: Like `o` and `O`, respectively, but if the argument is *NULL*, - output a JSON null value. - -`o*`, `O*` (any value) \[json\_t \*\] - -: Like `o` and `O`, respectively, but if the argument is *NULL*, do - not output any value. This format can only be used inside an object - or an array. If used inside an object, the corresponding key is - additionally suppressed. See below for an example. - -`[fmt]` (array) - -: Build an array with contents from the inner format string. `fmt` may - contain objects and arrays, i.e. recursive value building is - supported. - -`{fmt}` (object) - -: Build an object with contents from the inner format string `fmt`. - The first, third, etc. format specifier represent a key, and must be - a string (see `s`, `s#`, `+` and `+#` above), as object keys are - always strings. The second, fourth, etc. format specifier represent - a value. Any value may be an object or array, i.e. recursive value - building is supported. - -Whitespace, `:` and `,` are ignored. - -More examples: - - /* Build an empty JSON object */ - wrap_json_pack(&result, "{}"); - - /* Build the JSON object {"foo": 42, "bar": 7} */ - wrap_json_pack(&result, "{sisi}", "foo", 42, "bar", 7); - - /* Like above, ':', ',' and whitespace are ignored */ - wrap_json_pack(&result, "{s:i, s:i}", "foo", 42, "bar", 7); - - /* Build the JSON array [[1, 2], {"cool": true}] */ - wrap_json_pack(&result, "[[i,i],{s:b}]", 1, 2, "cool", 1); - - /* Build a string from a non-null terminated buffer */ - char buffer[4] = {'t', 'e', 's', 't'}; - wrap_json_pack(&result, "s#", buffer, 4); - - /* Concatenate strings together to build the JSON string "foobarbaz" */ - wrap_json_pack(&result, "s++", "foo", "bar", "baz"); - - /* Create an empty object or array when optional members are missing */ - wrap_json_pack(&result, "{s:s*,s:o*,s:O*}", "foo", NULL, "bar", NULL, "baz", NULL); - wrap_json_pack(&result, "[s*,o*,O*]", NULL, NULL, NULL); - -Parsing and Validating Values ------------------------------ - -This section describes functions that help to validate complex values -and extract, or *unpack*, data from them. Like building values -<apiref-pack>, this is also based on format strings. - -While a JSON value is unpacked, the type specified in the format string -is checked to match that of the JSON value. This is the validation part -of the process. In addition to this, the unpacking functions can also -check that all items of arrays and objects are unpacked. This check be -enabled with the format specifier `!` or by using the flag -`JSON_STRICT`. See below for details. - -Here's the full list of format specifiers. The type in parentheses -denotes the JSON type, and the type in brackets (if any) denotes the C -type whose address should be passed. - -`s` (string) \[const char \*\] - -: Convert a JSON string to a pointer to a null terminated UTF-8 - string. The resulting string is extracted by using - json\_string\_value() internally, so it exists as long as there are - still references to the corresponding JSON string. - -`s%` (string) \[const char \*, size\_t \*\] - -: Convert a JSON string to a pointer to a null terminated UTF-8 string - and its length. - -`n` (null) - -: Expect a JSON null value. Nothing is extracted. - -`b` (boolean) \[int\] - -: Convert a JSON boolean value to a C int, so that `true` is converted - to 1 and `false` to 0. - -`i` (integer) \[int\] - -: Convert a JSON integer to C int. - -`I` (integer) \[json\_int\_t\] - -: Convert a JSON integer to C json\_int\_t. - -`f` (real) \[double\] - -: Convert a JSON real to C double. - -`F` (integer or real) \[double\] - -: Convert a JSON number (integer or real) to C double. - -`o` (any value) \[json\_t \*\] - -: Store a JSON value with no conversion to a json\_t pointer. - -`O` (any value) \[json\_t \*\] - -: Like `O`, but the JSON value's reference count is incremented. - -`[fmt]` (array) - -: Convert each item in the JSON array according to the inner format - string. `fmt` may contain objects and arrays, i.e. recursive value - extraction is supported. - -`{fmt}` (object) - -: Convert each item in the JSON object according to the inner format - string `fmt`. The first, third, etc. format specifier represent a - key, and must be `s`. The corresponding argument to unpack functions - is read as the object key. The second fourth, etc. format specifier - represent a value and is written to the address given as the - corresponding argument. **Note** that every other argument is read - from and every other is written to. - - `fmt` may contain objects and arrays as values, i.e. recursive value - extraction is supported. - -`!` - -: This special format specifier is used to enable the check that all - object and array items are accessed, on a per-value basis. It must - appear inside an array or object as the last format specifier before - the closing bracket or brace. - -`*` - -: This special format specifier is the opposite of `!`. This is the default. - It must appear inside an array or object as the last format specifier - before the closing bracket or brace. - -Whitespace, `:` and `,` are ignored. - -Examples: - - /* root is the JSON integer 42 */ - int myint; - wrap_json_unpack(root, "i", &myint); - assert(myint == 42); - - /* root is the JSON object {"foo": "bar", "quux": true} */ - const char *str; - int boolean; - wrap_json_unpack(root, "{s:s, s:b}", "foo", &str, "quux", &boolean); - assert(strcmp(str, "bar") == 0 && boolean == 1); - - /* root is the JSON array [[1, 2], {"baz": null} */ - wrap_json_check(root, "[[i,i], {s:n}]", "baz"); - /* returns 0 for validation success, nothing is extracted */ - - /* root is the JSON array [1, 2, 3, 4, 5] */ - int myint1, myint2; - wrap_json_unpack(root, "[ii!]", &myint1, &myint2); - /* returns -1 for failed validation */ - - /* root is an empty JSON object */ - int myint = 0, myint2 = 0, myint3 = 0; - wrap_json_unpack(root, "{s?i, s?[ii]}", - "foo", &myint1, - "bar", &myint2, &myint3); - /* myint1, myint2 or myint3 is no touched as "foo" and "bar" don't exist */ - - -Copyright ---------- - -Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org> - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - |