diff options
Diffstat (limited to 'Controler-afb')
-rw-r--r-- | Controler-afb/CMakeLists.txt | 60 | ||||
-rw-r--r-- | Controler-afb/ctl-apidef.h | 128 | ||||
-rw-r--r-- | Controler-afb/ctl-apidef.json | 281 | ||||
-rw-r--r-- | Controler-afb/ctl-binding.c | 74 | ||||
-rw-r--r-- | Controler-afb/ctl-binding.h | 96 | ||||
-rw-r--r-- | Controler-afb/ctl-events.c | 139 | ||||
-rw-r--r-- | Controler-afb/ctl-lua.c | 380 | ||||
-rw-r--r-- | Controler-afb/ctl-policy.c | 196 |
8 files changed, 1354 insertions, 0 deletions
diff --git a/Controler-afb/CMakeLists.txt b/Controler-afb/CMakeLists.txt new file mode 100644 index 0000000..afebdf2 --- /dev/null +++ b/Controler-afb/CMakeLists.txt @@ -0,0 +1,60 @@ +########################################################################### +# 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. +########################################################################### + + +# Generate API-v2 hat from OpenAPI json definition +macro(SET_TARGET_GENSKEL TARGET_NAME API_DEF_NAME) + add_custom_command(OUTPUT ${API_DEF_NAME}.h + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + DEPENDS ${API_DEF_NAME}.json + COMMAND afb-genskel ${API_DEF_NAME}.json >${API_DEF_NAME}.h + ) + add_custom_target(${API_DEF_NAME}_OPENAPI DEPENDS ${API_DEF_NAME}.h) + add_dependencies(${TARGET_NAME} ${API_DEF_NAME}_OPENAPI) + +endmacro(SET_TARGET_GENSKEL) + +# Add target to project dependency list +PROJECT_TARGET_ADD(control-afb) + + # Define project Targets + ADD_LIBRARY(${TARGET_NAME} MODULE ctl-binding.c ctl-events.c ctl-policy.c ctl-lua.c +) + + # Generate API-v2 hat from OpenAPI json definition + SET_TARGET_GENSKEL(${TARGET_NAME} ctl-apidef) + + # Binder exposes a unique public entry point + SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES + PREFIX "afb-" + LABELS "BINDING" + LINK_FLAGS ${BINDINGS_LINK_FLAG} + OUTPUT_NAME ${TARGET_NAME} + + ) + + # Library dependencies (include updates automatically) + TARGET_LINK_LIBRARIES(${TARGET_NAME} + audio-common + ${link_libraries} + ) + + # installation directory + INSTALL(TARGETS ${TARGET_NAME} + LIBRARY DESTINATION ${BINDINGS_INSTALL_DIR}) + diff --git a/Controler-afb/ctl-apidef.h b/Controler-afb/ctl-apidef.h new file mode 100644 index 0000000..0ab4cd2 --- /dev/null +++ b/Controler-afb/ctl-apidef.h @@ -0,0 +1,128 @@ + +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\":\"p" + "olctl\",\"version\":\"1.0\",\"x-binding-c-generator\":{\"api\":\"control" + "\",\"version\":2,\"prefix\":\"ctlapi_\",\"postfix\":\"\",\"start\":null," + "\"onevent\":null,\"init\":\"CtlBindingInit\",\"scope\":\"\",\"private\":" + "false}},\"servers\":[{\"url\":\"ws://{host}:{port}/api/polctl\",\"descri" + "ption\":\"Unicens2 API.\",\"variables\":{\"host\":{\"default\":\"localho" + "st\"},\"port\":{\"default\":\"1234\"}},\"x-afb-events\":[{\"$ref\":\"#/c" + "omponents/schemas/afb-event\"}]}],\"components\":{\"schemas\":{\"afb-rep" + "ly\":{\"$ref\":\"#/components/schemas/afb-reply-v2\"},\"afb-event\":{\"$" + "ref\":\"#/components/schemas/afb-event-v2\"},\"afb-reply-v2\":{\"title\"" + ":\"Generic response.\",\"type\":\"object\",\"required\":[\"jtype\",\"req" + "uest\"],\"properties\":{\"jtype\":{\"type\":\"string\",\"const\":\"afb-r" + "eply\"},\"request\":{\"type\":\"object\",\"required\":[\"status\"],\"pro" + "perties\":{\"status\":{\"type\":\"string\"},\"info\":{\"type\":\"string\"" + "},\"token\":{\"type\":\"string\"},\"uuid\":{\"type\":\"string\"},\"reqid" + "\":{\"type\":\"string\"}}},\"response\":{\"type\":\"object\"}}},\"afb-ev" + "ent-v2\":{\"type\":\"object\",\"required\":[\"jtype\",\"event\"],\"prope" + "rties\":{\"jtype\":{\"type\":\"string\",\"const\":\"afb-event\"},\"event" + "\":{\"type\":\"string\"},\"data\":{\"type\":\"object\"}}}},\"x-permissio" + "ns\":{\"monitor\":{\"permission\":\"urn:AGL:permission:audio:public:moni" + "tor\"},\"multimedia\":{\"permission\":\"urn:AGL:permission:audio:public:" + "monitor\"},\"navigation\":{\"permission\":\"urn:AGL:permission:audio:pub" + "lic: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\"},\"para" + "meters\":[{\"in\":\"query\",\"name\":\"event_patern\",\"required\":true," + "\"schema\":{\"type\":\"string\"}}],\"responses\":{\"200\":{\"$ref\":\"#/" + "components/responses/200\"}}}},\"/event_test\":{\"description\":\"Pause " + "Resume Test\",\"get\":{\"x-permissions\":{\"$ref\":\"#/components/x-perm" + "issions/monitor\"},\"parameters\":[{\"in\":\"query\",\"name\":\"delay\"," + "\"required\":false,\"schema\":{\"type\":\"interger\"}},{\"in\":\"query\"" + ",\"name\":\"count\",\"required\":false,\"schema\":{\"type\":\"interger\"" + "}}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\"}}}}," + "\"/navigation\":{\"description\":\"Request Access to Navigation Audio Ch" + "annel.\",\"get\":{\"x-permissions\":{\"$ref\":\"#/components/x-permissio" + "ns/navigation\"},\"parameters\":[{\"in\":\"query\",\"name\":\"zone\",\"r" + "equired\":false,\"schema\":{\"type\":\"string\"}}],\"responses\":{\"200\"" + ":{\"$ref\":\"#/components/responses/200\"}}}},\"/lua_docall\":{\"descrip" + "tion\":\"Execute LUA string script.\",\"get\":{\"x-permissions\":{\"$ref" + "\":\"#/components/x-permissions/navigation\"},\"parameters\":[{\"in\":\"" + "query\",\"name\":\"func\",\"required\":true,\"schema\":{\"type\":\"strin" + "g\"}},{\"in\":\"query\",\"name\":\"args\",\"required\":false,\"schema\":" + "{\"type\":\"array\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/r" + "esponses/200\"}}}},\"/lua_dostring\":{\"description\":\"Execute LUA stri" + "ng script.\",\"get\":{\"x-permissions\":{\"$ref\":\"#/components/x-permi" + "ssions/navigation\"},\"parameters\":[{\"in\":\"query\",\"required\":true" + ",\"schema\":{\"type\":\"string\"}}],\"responses\":{\"200\":{\"$ref\":\"#" + "/components/responses/200\"}}}},\"/lua_doscript\":{\"description\":\"Exe" + "cute LUA string script.\",\"get\":{\"x-permissions\":{\"$ref\":\"#/compo" + "nents/x-permissions/navigation\"},\"parameters\":[{\"in\":\"query\",\"na" + "me\":\"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_event_test(struct afb_req req); + void ctlapi_navigation(struct afb_req req); + void ctlapi_lua_docall(struct afb_req req); + void ctlapi_lua_dostring(struct afb_req req); + void ctlapi_lua_doscript(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 = "event_test", + .callback = ctlapi_event_test, + .auth = &_afb_auths_v2_control[0], + .info = NULL, + .session = AFB_SESSION_NONE_V2 + }, + { + .verb = "navigation", + .callback = ctlapi_navigation, + .auth = &_afb_auths_v2_control[0], + .info = NULL, + .session = AFB_SESSION_NONE_V2 + }, + { + .verb = "lua_docall", + .callback = ctlapi_lua_docall, + .auth = &_afb_auths_v2_control[0], + .info = NULL, + .session = AFB_SESSION_NONE_V2 + }, + { + .verb = "lua_dostring", + .callback = ctlapi_lua_dostring, + .auth = &_afb_auths_v2_control[0], + .info = NULL, + .session = AFB_SESSION_NONE_V2 + }, + { + .verb = "lua_doscript", + .callback = ctlapi_lua_doscript, + .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 = NULL, + .noconcurrency = 0 +}; + diff --git a/Controler-afb/ctl-apidef.json b/Controler-afb/ctl-apidef.json new file mode 100644 index 0000000..fddf301 --- /dev/null +++ b/Controler-afb/ctl-apidef.json @@ -0,0 +1,281 @@ +{ + "openapi": "3.0.0", + "$schema": "http:iot.bzh/download/openapi/schema-3.0/default-schema.json", + "info": { + "description": "", + "title": "polctl", + "version": "1.0", + "x-binding-c-generator": { + "api": "control", + "version": 2, + "prefix": "ctlapi_", + "postfix": "", + "start": null, + "onevent": null, + "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" + } + } + } + }, + "/event_test": { + "description": "Pause Resume Test", + "get": { + "x-permissions": { + "$ref": "#/components/x-permissions/monitor" + }, + "parameters": [ + { + "in": "query", + "name": "delay", + "required": false, + "schema": { + "type": "interger" + } + }, + { + "in": "query", + "name": "count", + "required": false, + "schema": { + "type": "interger" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/200" + } + } + } + }, + "/navigation": { + "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" + } + } + } + }, + "/lua_docall": { + "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" + } + } + } + }, + "/lua_dostring": { + "description": "Execute LUA string script.", + "get": { + "x-permissions": { + "$ref": "#/components/x-permissions/navigation" + }, + "parameters": [ + { + "in": "query", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/200" + } + } + } + }, + "/lua_doscript": { + "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/Controler-afb/ctl-binding.c b/Controler-afb/ctl-binding.c new file mode 100644 index 0000000..45c4e1c --- /dev/null +++ b/Controler-afb/ctl-binding.c @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2016 "IoT.bzh" + * Author Fulup Ar Foll <fulup@iot.bzh> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 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 "audio-common.h" +#include "ctl-binding.h" + + + +// Include Binding Stub generated from Json OpenAPI +#include "ctl-apidef.h" + + +PUBLIC void ctlapi_navigation (afb_req request) { + + ctlapi_authorize (CTLAPI_NAVIGATION, request); +} + +PUBLIC void ctlapi_multimedia (afb_req request) { + + ctlapi_authorize (CTLAPI_MULTIMEDIA, request); +} + +PUBLIC void ctlapi_emergency (afb_req request) { + + ctlapi_authorize (CTLAPI_EMERGENCY, request); +} + +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 += PolicyInit(); + errcount += LuaLibInit(); + + AFB_DEBUG ("Audio Policy Control Binding Done errcount=%d", errcount); + return errcount; +} + diff --git a/Controler-afb/ctl-binding.h b/Controler-afb/ctl-binding.h new file mode 100644 index 0000000..e53d2ca --- /dev/null +++ b/Controler-afb/ctl-binding.h @@ -0,0 +1,96 @@ +/* + * 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. + */ +#include <systemd/sd-event.h> + +#include "audio-common.h" + +#ifndef CONTROLER_BINDING_INCLUDE +#define CONTROLER_BINDING_INCLUDE + +// polctl-binding.c +PUBLIC int CtlBindingInit (); + +// ctl-timerevt.c +// ---------------------- +#define DEFAULT_PAUSE_DELAY 3000 +#define DEFAULT_TEST_COUNT 1 +typedef int (*timerCallbackT)(void *context); +typedef struct { + int value; + const char *label; +} AutoTestCtxT; + +typedef struct TimerHandleS { + int count; + int delay; + AutoTestCtxT *context; + timerCallbackT callback; + sd_event_source *evtSource; +} TimerHandleT; + +PUBLIC int TimerEvtInit (void); +PUBLIC afb_event TimerEvtGet(void); +PUBLIC void ctlapi_event_test (afb_req request); + +// ctl-policy +// ----------- + +typedef struct PolicyActionS{ + const char* label; + const char* api; + const char* verb; + json_object *argsJ; + const char *info; + int timeout; + json_object* (*actionCB)(struct PolicyActionS *action,json_object *response, void *context); +} PolicyActionT; + +typedef struct { + const char* label; + const char *info; + const char *version; + void *context; +} PolicyHandleT; + +typedef struct { + char *sharelib; + void *dlHandle; + PolicyHandleT **handle; + PolicyActionT **onload; + PolicyActionT **events; + PolicyActionT **controls; +} PolicyCtlConfigT; + +typedef enum { + CTLAPI_NAVIGATION, + CTLAPI_MULTIMEDIA, + CTLAPI_EMERGENCY, + + CTL_NONE=-1 +} PolicyCtlEnumT; + +PUBLIC int PolicyInit(void); +PUBLIC json_object* ScanForConfig (char* searchPath, char * pre, char *ext); +PUBLIC void ctlapi_authorize (PolicyCtlEnumT control, afb_req request); + +// ctl-lua.c +PUBLIC int LuaLibInit (); +PUBLIC void ctlapi_lua_docall (afb_req request); +PUBLIC void ctlapi_lua_dostring (afb_req request); +PUBLIC void ctlapi_lua_doscript (afb_req request); + +#endif diff --git a/Controler-afb/ctl-events.c b/Controler-afb/ctl-events.c new file mode 100644 index 0000000..f444848 --- /dev/null +++ b/Controler-afb/ctl-events.c @@ -0,0 +1,139 @@ +/* + * 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" + +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); + 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); + } + + done= timerHandle->callback(timerHandle->context); + if (!done) goto OnErrorExit; + + return 0; + +OnErrorExit: + AFB_WARNING("TimerNext Callback Fail Tag=%s", timerHandle->context->label); + return -1; +} + + +STATIC int DoSendEvent (void *context) { + AutoTestCtxT *ctx= (AutoTestCtxT*)context; + json_object *ctlEventJ; + + if (ctx->value) ctx->value =0; + else ctx->value =1; + + ctlEventJ = json_object_new_object(); + json_object_object_add(ctlEventJ,"action", json_object_new_string(ctx->label)); + json_object_object_add(ctlEventJ,"value" , json_object_new_int(ctx->value)); + int done = afb_event_push(afbevt, ctlEventJ); + + AFB_NOTICE ("DoSendEvent {action: '%s', value:%d} status=%d", ctx->label, ctx->value, done); + + return (done); +} + +STATIC void TimerEvtStart(TimerHandleT *timerHandle, void *context) { + uint64_t usec; + + // populate CB handle + timerHandle->callback=DoSendEvent; + 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, 250, TimerNext, timerHandle); +} + +PUBLIC afb_event TimerEvtGet(void) { + return afbevt; +} + + +// Generated some fake event based on watchdog/counter +PUBLIC void ctlapi_event_test (afb_req request) { + json_object *queryJ, *tmpJ; + TimerHandleT *timerHandle = malloc (sizeof (TimerHandleT)); + AutoTestCtxT *context = calloc (1, sizeof (AutoTestCtxT)); + int done; + + queryJ= afb_req_json(request); + + // Closing call only has one parameter + done=json_object_object_get_ex(queryJ, "closing", &tmpJ); + if (done) return; + + done=json_object_object_get_ex(queryJ, "label", &tmpJ); + if (!done) { + afb_req_fail_f(request, "TEST-LABEL-MISSING", "label is mandatory for event_test"); + goto OnErrorExit; + } + context->label = strdup(json_object_get_string (tmpJ)); + + json_object_object_get_ex(queryJ, "delay", &tmpJ); + timerHandle->delay = json_object_get_int (tmpJ) * 1000; + if (timerHandle->delay == 0) timerHandle->delay=DEFAULT_PAUSE_DELAY * 1000; + + json_object_object_get_ex(queryJ, "count", &tmpJ); + timerHandle->count = json_object_get_int (tmpJ); + if (timerHandle->count == 0) timerHandle->count=DEFAULT_TEST_COUNT; + + // start a lool timer + TimerEvtStart (timerHandle, context); + + afb_req_success(request, NULL, NULL); + return; + + OnErrorExit: + return; +} + +// Create Binding Event at Init +PUBLIC int TimerEvtInit () { + + // create binder event to send test pause/resume + afbevt = afb_daemon_make_event("request"); + 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/Controler-afb/ctl-lua.c b/Controler-afb/ctl-lua.c new file mode 100644 index 0000000..0e44bc9 --- /dev/null +++ b/Controler-afb/ctl-lua.c @@ -0,0 +1,380 @@ +/* + * Copyright (C) 2016 "IoT.bzh" + * Author Fulup Ar Foll <fulup@iot.bzh> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ref: + * http://www.troubleshooters.com/codecorn/lua/lua_c_calls_lua.htm#_Anatomy_of_a_Lua_Call + * http://acamara.es/blog/2012/08/passing-variables-from-lua-5-2-to-c-and-vice-versa/ + */ + +#define _GNU_SOURCE +#include <stdio.h> +#include <string.h> +#include <dirent.h> + +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" + +#include "ctl-binding.h" +#include "wrap-json.h" + + +static lua_State* luaState; + +typedef enum { + LUA_DOCALL, + LUA_DOSTRING, + LUA_DOSCRIPT, +} LuaDoActionT; + +// List Avaliable Configuration Files +PUBLIC json_object* ScanForConfig (char* searchPath, char *pre, char *ext) { + json_object *responseJ; + DIR *dirHandle; + char *dirPath; + char* dirList= strdup(searchPath); + size_t extLen = strlen(ext); + + responseJ = json_object_new_array(); + for (dirPath= strtok(dirList, ":"); dirPath && *dirPath; dirPath=strtok(NULL,":")) { + struct dirent *dirEnt; + + dirHandle = opendir (dirPath); + if (!dirHandle) { + AFB_NOTICE ("CONFIG-SCANNING dir=%s not readable", dirPath); + continue; + } + + AFB_NOTICE ("CONFIG-SCANNING:ctl_listconfig scanning: %s", dirPath); + while ((dirEnt = readdir(dirHandle)) != NULL) { + // Unknown type is accepted to support dump filesystems + if (dirEnt->d_type == DT_REG || dirEnt->d_type == DT_UNKNOWN) { + + // check prefix and extention + size_t extIdx=strlen(dirEnt->d_name) - extLen; + if (extIdx <= 0) continue; + if (pre && !strcasestr (dirEnt->d_name, pre)) continue; + if (ext && strcasecmp (ext, &dirEnt->d_name[extIdx])) continue; + + struct json_object *pathJ = json_object_new_object(); + json_object_object_add(pathJ, "dirpath", json_object_new_string(dirPath)); + json_object_object_add(pathJ, "filename", json_object_new_string(dirEnt->d_name)); + json_object_array_add(responseJ, pathJ); + } + } + } + + free (dirList); + return (responseJ); +} + +STATIC int LuaPrintNotice(lua_State* luaState) { + int count = lua_gettop(luaState); + + for (int idx=1; idx <= count; ++idx) { + const char *str = lua_tostring(luaState, idx); // Get string + + // Output string. + AFB_NOTICE (str); + } + + return 0; // no value return +} + +STATIC int LuaPushArgument (json_object *arg) { + + switch (json_object_get_type(arg)) { + case json_type_object: + lua_newtable (luaState); + json_object_object_foreach (arg, key, val) { + int done = LuaPushArgument (val); + if (done) { + lua_pushstring(luaState, key); // table.key = val + lua_settable(luaState, -3); + } + } + + break; + case json_type_int: + lua_pushinteger(luaState, json_object_get_int(arg)); + break; + case json_type_string: + lua_pushstring(luaState, json_object_get_string(arg)); + break; + case json_type_boolean: + lua_pushboolean(luaState, json_object_get_boolean(arg)); + break; + case json_type_double: + lua_pushnumber(luaState, json_object_get_double(arg)); + break; + default: + return 0; + } + + return 1; +} + +// Generated some fake event based on watchdog/counter +PUBLIC void LuaDoAction (LuaDoActionT action, afb_req request) { + + int err, count=0; + + json_object* queryJ = afb_req_json(request); + + switch (action) { + + case LUA_DOSTRING: { + const char *script = json_object_get_string(queryJ); + err=luaL_loadstring(luaState, script); + if (err) { + AFB_ERROR ("LUA-DO-COMPILE:FAIL String=%s err=%s", script, lua_tostring(luaState,-1) ); + goto OnErrorExit; + } + break; + } + + case LUA_DOCALL: { + const char *func; + json_object *args; + err= wrap_json_unpack (queryJ, "{s:s, s?o !}", "func", &func,"args", &args); + if (err) { + AFB_ERROR ("LUA-DOCALL-SYNTAX missing func|args args=%s", json_object_get_string(queryJ)); + goto OnErrorExit; + } + + // load function (should exist in CONTROL_PATH_LUA + lua_getglobal(luaState, func); + + // push arguments on the stack + if (json_object_get_type(args) != json_type_array) { + count= LuaPushArgument (args); + } else { + for (int idx=0; idx<json_object_array_length(args); idx++) { + count += LuaPushArgument (json_object_array_get_idx(args, idx)); + if (err) break; + } + } + + break; + } + + case LUA_DOSCRIPT: { + const char *script; + json_object *args; + int index; + + // scan luascript search path once + static json_object *luaScriptPathJ =NULL; + if (!luaScriptPathJ) luaScriptPathJ= ScanForConfig(CONTROL_PATH_LUA , NULL, "lua"); + + err= wrap_json_unpack (queryJ, "{s:s, s?o s?o !}", "script", &script,"args", &args, "arg", &args); + if (err) { + AFB_ERROR ("LUA-DOCALL-SYNTAX missing script|(args,arg) args=%s", json_object_get_string(queryJ)); + goto OnErrorExit; + } + + // push arguments on the stack + if (json_object_get_type(args) != json_type_array) { + lua_createtable(luaState, 1, 0); + count= LuaPushArgument (args); + } else { + int length= json_object_array_length(args); + lua_createtable(luaState, length, 0); + for (int idx=0; idx < length; idx++) { + count += LuaPushArgument (json_object_array_get_idx(args, idx)); + if (err) break; + } + } + + for (index=0; index < json_object_array_length(luaScriptPathJ); index++) { + char *filename; char*dirpath; + json_object *entryJ=json_object_array_get_idx(luaScriptPathJ, index); + + err= wrap_json_unpack (entryJ, "{s:s, s:s !}", "dirpath", &dirpath,"filename", &filename); + if (err) { + AFB_ERROR ("LUA-DOSCRIPT HOOPs invalid LUA script path = %s", json_object_get_string(entryJ)); + goto OnErrorExit; + } + + if (strcmp(filename, script)) continue; + + char filepath[255]; + strncpy(filepath, dirpath, sizeof(filepath)); + strncat(filepath, "/", sizeof(filepath)); + strncat(filepath, filename, sizeof(filepath)); + err= luaL_loadfile(luaState, filepath); + if (err) { + AFB_ERROR ("LUA-DOSCRIPT HOOPs Error in LUA loading scripts=%s err=%s", filepath, lua_tostring(luaState,-1)); + goto OnErrorExit; + } + break; + } + + if (index == json_object_array_length(luaScriptPathJ)) { + AFB_ERROR ("LUA-DOSCRIPT HOOPs script=%s not in path=%s", script, CONTROL_PATH_LUA); + goto OnErrorExit; + + } + + //lua_setglobal(luaState, "args"); + count=1; + + break; + } + + default: + AFB_ERROR ("LUA-DOCALL-ACTION unknown query=%s", json_object_get_string(queryJ)); + goto OnErrorExit; + } + + // effectively exec LUA code +// err=lua_pcall(luaState, count, LUA_MULTRET, 0); + err=lua_pcall(luaState, count, LUA_MULTRET, 0); + if (err) { + AFB_ERROR ("LUA-DO-EXEC:FAIL query=%s err=%s", json_object_get_string(queryJ), lua_tostring(luaState,-1) ); + goto OnErrorExit; + } + + // number of return values + count=lua_gettop(luaState); + + // Check status return true=OK false=fail + if (count == 0 || !lua_isboolean(luaState, 1)) { + AFB_ERROR ("LUA-DO-RETURN:INVALID 1st return argument not boolean query=%s", json_object_get_string(queryJ) ); + goto OnErrorExit; + } + + // get return fail/ok status + const int returnStatus=lua_toboolean(luaState, 1); + + // loop on remaining return arguments + json_object *returnArgs = json_object_new_array(); + for (int idx=2; idx <= count; idx++) { + + // push on stack return value (Fulup in LUA-5.3 the two functions are combined) + int valueType=lua_type (luaState, idx); + + switch(valueType) { + case LUA_TNUMBER: + json_object_array_add(returnArgs, json_object_new_double(lua_tonumber(luaState, idx))); + break; + case LUA_TBOOLEAN: + json_object_array_add(returnArgs, json_object_new_boolean(lua_toboolean(luaState, idx))); + break; + case LUA_TSTRING: + json_object_array_add(returnArgs, json_object_new_string(lua_tostring(luaState, idx))); + break; + case LUA_TTABLE: + default: + AFB_NOTICE ("script returned Unknown/Unsupported type: "); + } + + } + + // pop stack including Func/String/Script call + lua_pop(luaState, count+1); + + if(returnStatus) + afb_req_success(request, returnArgs, NULL); + else + afb_req_fail(request, "LUA-DOACTION-FAIL", json_object_get_string(returnArgs)); + + return; + + OnErrorExit: + afb_req_fail(request,"LUA:ERROR", lua_tostring(luaState,-1)); + return; +} + +PUBLIC void ctlapi_lua_dostring (afb_req request) { + + LuaDoAction (LUA_DOSTRING, request); +} + +PUBLIC void ctlapi_lua_docall (afb_req request) { + + LuaDoAction (LUA_DOCALL, request); +} + +PUBLIC void ctlapi_lua_doscript (afb_req request) { + + LuaDoAction (LUA_DOSCRIPT, request); +} + + +// Create Binding Event at Init +PUBLIC int LuaLibInit () { + int err, index; + + // search for default policy config file + json_object *luaScriptPathJ = ScanForConfig(CONTROL_PATH_LUA , "onload", "lua"); + + // open a new LUA interpretor + luaState = luaL_newstate(); + if (!luaState) { + AFB_ERROR ("LUA_INIT: Fail to open lua interpretor"); + goto OnErrorExit; + } + + // load auxiliary libraries + luaL_openlibs(luaState); + + // redirect print to AFB_NOTICE + lua_register(luaState,"print", LuaPrintNotice ); + //lua_register(lauHandle,"AFB_DEBUG", LuaAFBDebug); + + // load+exec any file found in LUA search path + for (index=0; index < json_object_array_length(luaScriptPathJ); index++) { + json_object *entryJ=json_object_array_get_idx(luaScriptPathJ, index); + + char *filename; char*dirpath; + err= wrap_json_unpack (entryJ, "{s:s, s:s !}", "dirpath", &dirpath,"filename", &filename); + if (err) { + AFB_ERROR ("LUA-INIT HOOPs invalid config file path = %s", json_object_get_string(entryJ)); + goto OnErrorExit; + } + + char filepath[255]; + strncpy(filepath, dirpath, sizeof(filepath)); + strncat(filepath, "/", sizeof(filepath)); + strncat(filepath, filename, sizeof(filepath)); + err= luaL_loadfile(luaState, filepath); + if (err) { + AFB_ERROR ("LUA-LOAD HOOPs Error in LUA loading scripts=%s err=%s", filepath, lua_tostring(luaState,-1)); + goto OnErrorExit; + } + + // exec/compil script + err = lua_pcall(luaState, 0, 0, 0); + if (err) { + AFB_ERROR ("LUA-LOAD HOOPs Error in LUA exec scripts=%s err=%s", filepath, lua_tostring(luaState,-1)); + goto OnErrorExit; + } + + } + + // no policy config found remove control API from binder + if (index == 0) { + AFB_WARNING ("POLICY-INIT:WARNING No Control LUA file in path=[%s]", CONTROL_PATH_LUA); + } + + + AFB_DEBUG ("Audio control-LUA Init Done"); + return 0; + + OnErrorExit: + return -1; +} +
\ No newline at end of file diff --git a/Controler-afb/ctl-policy.c b/Controler-afb/ctl-policy.c new file mode 100644 index 0000000..7ad73fa --- /dev/null +++ b/Controler-afb/ctl-policy.c @@ -0,0 +1,196 @@ +/* + * 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 <json-c/json_object.h> + +#include "wrap-json.h" +#include "ctl-binding.h" + +STATIC PolicyCtlConfigT *ctlHandle = NULL; + + +PUBLIC void ctlapi_authorize (PolicyCtlEnumT control, afb_req request) { + json_object*tmpJ; + + json_object* argsJ= afb_req_json(request); + int done=json_object_object_get_ex(argsJ, "closing", &tmpJ); + if (done) return; + +} + + +// List Avaliable Configuration Files +PUBLIC void ctlapi_config (struct afb_req request) { + json_object*tmpJ; + char *dirList; + + json_object* argsJ = afb_req_json(request); + if (argsJ && json_object_object_get_ex (argsJ, "cfgpath" , &tmpJ)) { + dirList = strdup (json_object_get_string(tmpJ)); + } else { + dirList = strdup (CONTROL_PATH_POLICY); + AFB_NOTICE ("CONFIG-MISSING: use default CONTROL_PATH_POLICY=%s", CONTROL_PATH_POLICY); + } + + // get list of config file + struct json_object *responseJ = ScanForConfig(dirList, "onload", "json"); + + if (json_object_array_length(responseJ) == 0) { + afb_req_fail(request, "CONFIGPATH:EMPTY", "No Config Found in CONTROL_PATH_POLICY"); + } else { + afb_req_success(request, responseJ, NULL); + } + + return; +} + +STATIC PolicyActionT *PolicyLoadActions (json_object *actionsJ) { + int err; + PolicyActionT *actions; + char *callback; // need to search in DL lib + + // unpack individual action object + int actionUnpack (json_object *actionJ, PolicyActionT *action) { + + err= wrap_json_unpack(actionJ, "{ss,s?s,s?o,s?s,s?s,s?s !}" + , "label",&action->label, "info",&action->info, "callback",&callback, "args",&action->argsJ, "api",&action->api, "verb", &action->verb); + if (err) { + AFB_ERROR ("POLICY-LOAD-ACTION Missing something label|info|callback|api|verb|args in %s", json_object_get_string(actionJ)); + return -1; + } + if (!callback || !(action->api && action->verb)) { + AFB_ERROR ("POLICY-LOAD-ACTION Missing something callback|(api+verb) in %s", json_object_get_string(actionJ)); + return -1; + } + return 0; + }; + + // action array is close with a nullvalue; + if (json_object_get_type(actionsJ) == json_type_array) { + int count = json_object_array_length(actionsJ); + actions = calloc (count+1, sizeof(PolicyActionT)); + + for (int idx=0; idx < count; idx++) { + json_object *actionJ = json_object_array_get_idx(actionsJ, idx); + err = actionUnpack (actionJ, &actions[idx]); + if (err) goto OnErrorExit; + } + + } else { + actions = calloc (2, sizeof(PolicyActionT)); + err = actionUnpack (actionsJ, &actions[0]); + if (err) goto OnErrorExit; + } + + return actions; + + OnErrorExit: + return NULL; + +} + +// load control policy from file using json_unpack https://jansson.readthedocs.io/en/2.9/apiref.html#parsing-and-validating-values +STATIC PolicyCtlConfigT *PolicyLoadConfig (const char* filepath) { + json_object *policyConfigJ, *ignoreJ, *actionsJ; + PolicyCtlConfigT *policyConfig = calloc (1, sizeof(PolicyCtlConfigT)); + int err; + + // Load JSON file + policyConfigJ= json_object_from_file(filepath); + if (!policyConfigJ) goto OnErrorExit; + + json_object *metadataJ, *onloadJ, *controlsJ, *eventsJ; + err= wrap_json_unpack(policyConfigJ, "{s?o,so,s?o,so,so !}", "$schema", &ignoreJ, "metadata",&metadataJ, "onload",&onloadJ, "controls",&controlsJ, "events",&eventsJ); + if (err) { + AFB_ERROR ("POLICY-LOAD-ERRROR Missing something metadata|onload|controls|events in %s", json_object_get_string(policyConfigJ)); + goto OnErrorExit; + } + + PolicyHandleT *policyHandle = calloc (1, sizeof(PolicyHandleT)); + err= wrap_json_unpack(metadataJ, "{so,s?s,s?s !}", "label", &policyHandle->label, "info",&policyHandle->info, "version",&policyHandle->version); + if (err) { + AFB_ERROR ("POLICY-LOAD-CONFIG Missing something label|info|version in %s", json_object_get_string(metadataJ)); + goto OnErrorExit; + } + + if (onloadJ) { + err= wrap_json_unpack(onloadJ, "{s?o,s?s,s?s !}", "info",&ignoreJ, "label",&ignoreJ, "actions",&actionsJ); + if (err) { + AFB_ERROR ("POLICY-LOAD-CONFIG Missing something label|info|plugin|actions in %s", json_object_get_string(metadataJ)); + goto OnErrorExit; + } + policyConfig->onload= PolicyLoadActions (actionsJ); + } + + return policyConfig; + +OnErrorExit: + return NULL; +} + +// Load default config file at init +PUBLIC int PolicyInit () { + int index, err; + + + // search for default policy config file + json_object* responseJ = ScanForConfig(CONTROL_PATH_POLICY, "onload", "json"); + return 0; + + for (index=0; index < json_object_array_length(responseJ); index++) { + json_object *entryJ=json_object_array_get_idx(responseJ, index); + + char *filename; char*dirpath; + err= wrap_json_unpack (entryJ, "{s:s, s:s !}", "dirpath", &dirpath,"filename", &filename); + if (err) { + AFB_ERROR ("POLICY-INIT HOOPs invalid config file path = %s", json_object_get_string(entryJ)); + goto OnErrorExit; + } + + if (strcasestr(filename, CONTROL_FILE_POLICY)) { + char filepath[255]; + strncpy(filepath, dirpath, sizeof(filepath)); + strncat(filepath, "/", sizeof(filepath)); + strncat(filepath, filename, sizeof(filepath)); + ctlHandle = PolicyLoadConfig (filepath); + if (!ctlHandle) { + AFB_ERROR ("POLICY-INIT:ERROR No File to load [%s]", filepath); + goto OnErrorExit; + } + break; + } + } + + // no policy config found remove control API from binder + if (index == 0) { + AFB_WARNING ("POLICY-INIT:WARNING No Control policy file [%s]", CONTROL_FILE_POLICY); + } + + AFB_NOTICE ("POLICY-INIT:SUCCES: Audio Control Policy Init"); + return 0; + +OnErrorExit: + AFB_NOTICE ("POLICY-INIT:ERROR: Audio Control Policy Init"); + return 1; +} + + + |