From ec938c9ea7f17c5ccfbd9992eed8fe206cd76253 Mon Sep 17 00:00:00 2001 From: fulup Date: Fri, 27 Oct 2017 15:24:39 +0200 Subject: Moved from TCP WS to Unix WebSocket --- hook-plugin/PolicyHookCb.c | 98 +++++++++++++++++++++------------------------- hook-plugin/README.md | 14 +++---- 2 files changed, 51 insertions(+), 61 deletions(-) (limited to 'hook-plugin') diff --git a/hook-plugin/PolicyHookCb.c b/hook-plugin/PolicyHookCb.c index fdec61d..b751ed6 100644 --- a/hook-plugin/PolicyHookCb.c +++ b/hook-plugin/PolicyHookCb.c @@ -39,6 +39,8 @@ #include "afb/afb-wsj1.h" #include "afb/afb-ws-client.h" +#include "afb/afb-proto-ws.h" + #include #include @@ -63,14 +65,14 @@ // Currently not implemented #define UNUSED_ARG(x) UNUSED_ ## x __attribute__((__unused__)) -void OnRequestCB(void* UNUSED_ARG(handle) , const char* UNUSED_ARG(api), const char* UNUSED_ARG(verb), struct afb_wsj1_msg*UNUSED_ARG(msg)) {} +static void OnSuccessCB(void* UNUSED_ARG(handle) , void* UNUSED_ARG(request), struct json_object* UNUSED_ARG(reslt), const char* UNUSED_ARG(info)) {} +static void OnFailureCB(void* UNUSED_ARG(closure), void* UNUSED_ARG(request), const char *UNUSED_ARG(status), const char *UNUSED_ARG(info)) {} typedef struct { - const char *api; const char *openVerb; const char *closeVerb; + json_object *queryJ; long timeout; - char *query; size_t length; sd_event_source *evtSource; @@ -86,7 +88,7 @@ typedef struct { typedef struct { snd_pcm_t *pcm; const char *uri; - struct afb_wsj1 *wsj1; + struct afb_proto_ws *pws; sd_event *sdLoop; int verbose; sem_t semaphore; @@ -97,7 +99,6 @@ typedef struct { } afbClientT; - static void *LoopInThread(void *handle) { afbClientT *afbClient = (afbClientT*) handle; int count=0; @@ -114,7 +115,7 @@ static void *LoopInThread(void *handle) { } // lost connect with the AudioDaemon -static void OnHangupCB(void *handle, struct afb_wsj1 *wsj1) { +static void OnHangupCB(void *handle) { afbClientT *afbClient = (afbClientT*) handle; SNDERR("(Hoops) Lost Connection to %s", afbClient->uri); @@ -129,15 +130,13 @@ typedef enum { HOOK_CLOSE, } hookActionT; - -void OnEventCB(void *handle, const char *event, struct afb_wsj1_msg *msg) { +void OnEventCB(void *handle, const char *event, int evtid, struct json_object *eventJ) { afbClientT *afbClient = (afbClientT*) handle; afbEventT **afbEvent = afbClient->event; - json_object *eventJ, *tmpJ, *dataJ; + json_object *dataJ, *tmpJ; const char *label; int value, done, index; - eventJ = afb_wsj1_msg_object_j(msg); done= json_object_object_get_ex(eventJ,"data", &dataJ); if (!done) { SNDERR ("PCM_HOOK: uri=%s empty event label", afbClient->uri); @@ -166,15 +165,22 @@ void OnEventCB(void *handle, const char *event, struct afb_wsj1_msg *msg) { return; OnErrorExit: - SNDERR("ON-EVENT %s(%s)\n", event, afb_wsj1_msg_object_s(msg)); + SNDERR("ON-EVENT %s(%s)\n", event, json_object_get_string(dataJ)); return; } -// callback interface for wsj1 -static struct afb_wsj1_itf itf = { - .on_hangup = OnHangupCB, - .on_call = OnRequestCB, - .on_event = OnEventCB + +/* the callback interface for pws */ +static struct afb_proto_ws_client_itf itf = { + .on_reply_success = OnSuccessCB, + .on_reply_fail = OnFailureCB, + .on_event_create = NULL, + .on_event_remove = NULL, + .on_event_subscribe = NULL, + .on_event_unsubscribe = NULL, + .on_event_push = OnEventCB, + .on_event_broadcast = NULL, + .on_subcall = NULL, }; void OnResponseCB(void *handle, struct afb_wsj1_msg *msg) { @@ -222,7 +228,7 @@ static int CallWithTimeout(afbClientT *afbClient, afbRequestT *afbRequest, int c int err; // create a unique tag for request - (void) asprintf(&afbRequest->callIdTag, "%d:%s/%s", count, afbRequest->api, afbRequest->openVerb); + (void) asprintf(&afbRequest->callIdTag, "%d:%s", count, afbRequest->openVerb); // create a timer with ~250us accuracy sd_event_now(afbClient->sdLoop, CLOCK_MONOTONIC, &usec); @@ -235,9 +241,9 @@ static int CallWithTimeout(afbClientT *afbClient, afbRequestT *afbRequest, int c // release action is optional if (apiVerb) { - if (afbClient->verbose) printf("CALL-REQUEST api=%s/%s tag=%s\n", afbRequest->api, apiVerb, afbRequest->callIdTag); - err = afb_wsj1_call_s(afbClient->wsj1, afbRequest->api, apiVerb, afbRequest->query, OnResponseCB, afbRequest); - if (err) goto OnErrorExit; + if (afbClient->verbose) printf("CALL-REQUEST verb=%s tag=%s\n", apiVerb, afbRequest->callIdTag); + err = afb_proto_ws_client_call(afbClient->pws, apiVerb, afbRequest->queryJ, "xxxx", afbRequest); + if (err < 0 ) goto OnErrorExit; } // save client handle in request afbRequest->afbClient = afbClient; @@ -246,7 +252,7 @@ static int CallWithTimeout(afbClientT *afbClient, afbRequestT *afbRequest, int c return 0; OnErrorExit: - fprintf(stderr, "LaunchCallRequest: Fail call %s//%s/%s&%s", afbClient->uri, afbRequest->api, apiVerb, afbRequest->query); + fprintf(stderr, "LaunchCallRequest: Fail call %s//%s&%s", afbClient->uri, apiVerb, json_object_get_string(afbRequest->queryJ)); return 1; } @@ -272,13 +278,15 @@ static int LaunchCallRequest(afbClientT *afbClient, hookActionT action) { // start a thread with a mainloop to monitor Audio-Agent err = pthread_create(&tid, NULL, &LoopInThread, afbClient); if (err) goto OnErrorExit; - - // connect the websocket wsj1 to the uri given by the first argument - afbClient->wsj1 = afb_ws_client_connect_wsj1(afbClient->sdLoop, afbClient->uri, &itf, afbClient); - if (afbClient->wsj1 == NULL) { + + afbClient->pws = afb_ws_client_connect_api(afbClient->sdLoop, afbClient->uri, &itf, afbClient); + if (afbClient->pws == NULL) { fprintf(stderr, "LaunchCallRequest: Connection to %s failed\n", afbClient->uri); goto OnErrorExit; - } + } + + // register hanghup callback + afb_proto_ws_on_hangup(afbClient->pws, OnHangupCB); } // send call request to audio-agent asynchronously (respond with thread mainloop context) @@ -345,7 +353,7 @@ int PLUGIN_ENTRY_POINT (snd_pcm_t *pcm, snd_config_t *conf) { if (snd_config_get_id(node, &id) < 0) continue; if (strcmp(id, "comment") == 0 || strcmp(id, "hint") == 0) continue; - if (strcmp(id, "uri") == 0) { + if (strcmp(id, "ws-client") == 0) { const char *uri; if (snd_config_get_string(node, &uri) < 0) { SNDERR("Invalid String for %s", id); @@ -377,8 +385,7 @@ int PLUGIN_ENTRY_POINT (snd_pcm_t *pcm, snd_config_t *conf) { SNDERR("Invalid compound type for %s", callConf); goto OnErrorExit; } - - + // loop on each call snd_config_for_each(currentCall, follow, node) { snd_config_t *ctlconfig = snd_config_iterator_entry(currentCall); @@ -397,17 +404,6 @@ int PLUGIN_ENTRY_POINT (snd_pcm_t *pcm, snd_config_t *conf) { // allocate an empty call request afbRequest[callCount] = calloc(1, sizeof (afbRequestT)); - - err = snd_config_search(ctlconfig, "api", &itemConf); - if (!err) { - const char *api; - if (snd_config_get_string(itemConf, &api) < 0) { - SNDERR("Invalid api string for %s", callLabel); - goto OnErrorExit; - } - afbRequest[callCount]->api=strdup(api); - } - err = snd_config_search(ctlconfig, "request", &itemConf); if (!err) { const char *verb; @@ -438,33 +434,27 @@ int PLUGIN_ENTRY_POINT (snd_pcm_t *pcm, snd_config_t *conf) { err = snd_config_search(ctlconfig, "args", &itemConf); if (!err) { - const char *query; - if (snd_config_get_string(itemConf, &query) < 0) { + char *query; + if (snd_config_get_string(itemConf, (const char**) &query) < 0) { SNDERR("Invalid args string %s", id); goto OnErrorExit; } - // reserve enough space to ad closing message - afbRequest[callCount]->length= strlen(query); - afbRequest[callCount]->query = malloc (afbRequest[callCount]->length+strlen(CLOSING_MSG)+1); - strcpy (afbRequest[callCount]->query, query); // cleanup string for json_tokener for (int idx = 0; query[idx] != '\0'; idx++) { - if (query[idx] == '\'') afbRequest[callCount]->query[idx] = '"'; - else afbRequest[callCount]->query[idx] = query[idx]; + if (query[idx] == '\'') query[idx] = '"'; } - json_object *queryJ = json_tokener_parse(afbRequest[callCount]->query); - if (!queryJ) { - SNDERR("Invalid Json %s args=%s should be args=\"{'tok1':'val1', 'tok2':'val2'}\" ", id, afbRequest[callCount]->query); + afbRequest[callCount]->queryJ = json_tokener_parse(query); + if (!afbRequest[callCount]->queryJ) { + SNDERR("Invalid Json %s args=%s should be args=\"{'tok1':'val1', 'tok2':'val2'}\" ", id, query); goto OnErrorExit; } } // Simple check on call request validity - if (!afbRequest[callCount]->query) afbRequest[callCount]->query= ""; if (!afbRequest[callCount]->timeout) afbRequest[callCount]->timeout=REQUEST_DEFAULT_TIMEOUT ; - if (!afbRequest[callCount]->openVerb || !afbRequest[callCount]->api) { - SNDERR("Missing api/open(verb)/close(verb) %s in asoundrc", callLabel); + if (!afbRequest[callCount]->openVerb || !afbRequest[callCount]->closeVerb) { + SNDERR("Missing open(verb)/close(verb) %s in asoundrc", callLabel); goto OnErrorExit; } diff --git a/hook-plugin/README.md b/hook-plugin/README.md index 0472c7b..748f66f 100644 --- a/hook-plugin/README.md +++ b/hook-plugin/README.md @@ -6,8 +6,8 @@ Author: Fulup Ar Foll fulup@iot.bzh Date : August-2017 Functionalities: - - Execute a set of websocket RPC request again AGL binders to allow/deny access - - Keep websocket open in an idepandant thread on order to monitor event receive from AGL audio agent + - Execute a set of unix/ws RPC request again AGL binders to allow/deny access + - Keep websocket open in an idependant thread in order to monitor event received from AGL audio agent Installation - Alsaplugins are typically search in /usr/share/alsa-lib. Nevertheless a full path might be given @@ -41,12 +41,12 @@ pcm.MyNavigationHook { # Every Call should return OK in order PCM to open (default timeout 100ms) uri "ws://localhost:1234/api?token='audio-agent-token'" + ws-client="unix:/var/tmp/pol4a" request { # Request authorisation to write on navigation navigation-ctl { - api "pol4a" request "navigation-role" - release "navigation-role" + release "release-role" args "{'uid':'alsa-hook-navigation'}" } # subscribe to Audio Agent Event map them to signal @@ -69,9 +69,9 @@ pcm.MyNavigationHook { NOTE: -* Hook plugin is loaded by Alsa libasound within player context. It inherits client process attributes, as UID/GID or -the SMACK label on AGL. This smack label is tested by AGL security framework when requested a call on the audio-agent binder. -As a result the call will only succeed it the permission attached the application in Cynara matches. +* Hook plugin is loaded by Alsa libasound within client context. It inherits client process attributes, as UID/GID and +SMACK label when running on AGL. The smack label is control by AGL security framework. +As a result a control request succeeds only when client application permission match requested audio role inside Cynara security database. * Hook plugin keep a connection with the Audio-Agent until PCM is closed by the application. This connection allow the Audio-Agent to send events. eg: pause, quit, mute, ... \ No newline at end of file -- cgit 1.2.3-korg