From: José Bollo Date: Wed, 11 Oct 2017 11:12:57 +0000 (+0200) Subject: libafbwsc: add function 'afb_ws_client_connect_api' X-Git-Tag: eel_4.99.2~12 X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?p=src%2Fapp-framework-binder.git;a=commitdiff_plain;h=0033c7c16a48819447b3a5273ebb2be99be74352 libafbwsc: add function 'afb_ws_client_connect_api' This function allows to connect to an API exported by the binder. The demo program afb-client-demo is modified to integrate on option that direct API connection. Example: server: afb-daemon --ws-server unix:hello client: afb-client-demo -d -H unix:hello Change-Id: Id9f857a453c406df1c4b36eb25fb5e833a938c3d Signed-off-by: José Bollo --- diff --git a/CMakeLists.txt b/CMakeLists.txt index e1990923..163a74b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,7 +26,7 @@ SET(PROJECT_DESCRIPTION "Secured binder of API for clients of the Application fr SET(PROJECT_VERSION "4.99-EERC1") set(PROJECT_URL "https://gerrit.automotivelinux.org/gerrit/gitweb?p=src/app-framework-binder.git;a=summary") -SET(LIBAFBWSC_VERSION "1.0") +SET(LIBAFBWSC_VERSION "1.1") SET(LIBAFBWSC_SOVERSION "1") INCLUDE(FindPkgConfig) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ade02a11..68f1505a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -89,7 +89,7 @@ INSTALL(TARGETS afb-daemon ########################################### # build and install libafbwsc ########################################### -ADD_LIBRARY(afbwsc SHARED afb-ws.c afb-ws-client.c afb-wsj1.c websock.c) +ADD_LIBRARY(afbwsc SHARED afb-ws.c afb-ws-client.c afb-wsj1.c websock.c afb-proto-ws.c) SET_TARGET_PROPERTIES(afbwsc PROPERTIES VERSION ${LIBAFBWSC_VERSION} SOVERSION ${LIBAFBWSC_SOVERSION}) @@ -101,7 +101,7 @@ TARGET_LINK_LIBRARIES(afbwsc -Wl,--gc-sections ) INSTALL(TARGETS afbwsc LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) -INSTALL(FILES afb-wsj1.h afb-ws-client.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/afb) +INSTALL(FILES afb-wsj1.h afb-ws-client.h afb-proto-ws.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/afb) ########################################### # build and install afb-client-demo diff --git a/src/afb-client-demo.c b/src/afb-client-demo.c index 305e1b34..ae914a46 100644 --- a/src/afb-client-demo.c +++ b/src/afb-client-demo.c @@ -33,28 +33,59 @@ #include "afb-wsj1.h" #include "afb-ws-client.h" +#include "afb-proto-ws.h" /* declaration of functions */ -static void on_hangup(void *closure, struct afb_wsj1 *wsj1); -static void on_call(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg); -static void on_event(void *closure, const char *event, struct afb_wsj1_msg *msg); +static void on_wsj1_hangup(void *closure, struct afb_wsj1 *wsj1); +static void on_wsj1_call(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg); +static void on_wsj1_event(void *closure, const char *event, struct afb_wsj1_msg *msg); + +static void on_pws_hangup(void *closure); +static void on_pws_reply_success(void *closure, void *request, struct json_object *result, const char *info); +static void on_pws_reply_fail(void *closure, void *request, const char *status, const char *info); +static void on_pws_event_create(void *closure, const char *event_name, int event_id); +static void on_pws_event_remove(void *closure, const char *event_name, int event_id); +static void on_pws_event_subscribe(void *closure, void *request, const char *event_name, int event_id); +static void on_pws_event_unsubscribe(void *closure, void *request, const char *event_name, int event_id); +static void on_pws_event_push(void *closure, const char *event_name, int event_id, struct json_object *data); +static void on_pws_event_broadcast(void *closure, const char *event_name, struct json_object *data); +static void on_pws_subcall(void *closure, struct afb_proto_ws_subcall *subcall, void *request, const char *api, const char *verb, struct json_object *args); + static int io_event_callback(sd_event_source *src, int fd, uint32_t revents, void *closure); -static void emit(const char *api, const char *verb, const char *object); + +static void wsj1_emit(const char *api, const char *verb, const char *object); +static void pws_call(const char *verb, const char *object); /* the callback interface for wsj1 */ -static struct afb_wsj1_itf itf = { - .on_hangup = on_hangup, - .on_call = on_call, - .on_event = on_event +static struct afb_wsj1_itf wsj1_itf = { + .on_hangup = on_wsj1_hangup, + .on_call = on_wsj1_call, + .on_event = on_wsj1_event +}; + +/* the callback interface for pws */ +static struct afb_proto_ws_client_itf pws_itf = { + .on_reply_success = on_pws_reply_success, + .on_reply_fail = on_pws_reply_fail, + .on_event_create = on_pws_event_create, + .on_event_remove = on_pws_event_remove, + .on_event_subscribe = on_pws_event_subscribe, + .on_event_unsubscribe = on_pws_event_unsubscribe, + .on_event_push = on_pws_event_push, + .on_event_broadcast = on_pws_event_broadcast, + .on_subcall = on_pws_subcall, }; /* global variables */ static struct afb_wsj1 *wsj1; +static struct afb_proto_ws *pws; static int exonrep; static int callcount; static int human; static int raw; +static int direct; static sd_event_source *evsrc; +static char *sessionid = "afb-client-demo"; /* print usage of the program */ static void usage(int status, char *arg0) @@ -62,6 +93,7 @@ static void usage(int status, char *arg0) char *name = strrchr(arg0, '/'); name = name ? name + 1 : arg0; fprintf(status ? stderr : stdout, "usage: %s [-H [-r]] uri [api verb [data]]\n", name); + fprintf(status ? stderr : stdout, " %s -d [-H [-r]] uri [verb [data]]\n", name); exit(status); } @@ -86,6 +118,9 @@ int main(int ac, char **av, char **env) else if (!strcmp(av[1], "--raw")) /* request for raw output */ raw = 1; + else if (!strcmp(av[1], "--direct")) /* request for direct api */ + direct = 1; + /* emit usage and exit */ else usage(!!strcmp(av[1], "--help"), a0); @@ -95,6 +130,7 @@ int main(int ac, char **av, char **env) switch (av[1][rc]) { case 'H': human = 1; break; case 'r': raw = 1; break; + case 'd': direct = 1; break; default: usage(av[1][rc] != 'h', a0); } } @@ -118,10 +154,19 @@ int main(int ac, char **av, char **env) } /* connect the websocket wsj1 to the uri given by the first argument */ - wsj1 = afb_ws_client_connect_wsj1(loop, av[1], &itf, NULL); - if (wsj1 == NULL) { - fprintf(stderr, "connection to %s failed: %m\n", av[1]); - return 1; + if (direct) { + pws = afb_ws_client_connect_api(loop, av[1], &pws_itf, NULL); + if (pws == NULL) { + fprintf(stderr, "connection to %s failed: %m\n", av[1]); + return 1; + } + afb_proto_ws_on_hangup(pws, on_pws_hangup); + } else { + wsj1 = afb_ws_client_connect_wsj1(loop, av[1], &wsj1_itf, NULL); + if (wsj1 == NULL) { + fprintf(stderr, "connection to %s failed: %m\n", av[1]); + return 1; + } } /* test the behaviour */ @@ -132,7 +177,10 @@ int main(int ac, char **av, char **env) } else { /* the request is defined by the arguments */ exonrep = 1; - emit(av[2], av[3], av[4]); + if (direct) + pws_call(av[2], av[3]); + else + wsj1_emit(av[2], av[3], av[4]); } /* loop until end */ @@ -141,8 +189,16 @@ int main(int ac, char **av, char **env) return 0; } +/* decrement the count of calls */ +static void dec_callcount() +{ + callcount--; + if (exonrep && !callcount) + exit(0); +} + /* called when wsj1 hangsup */ -static void on_hangup(void *closure, struct afb_wsj1 *wsj1) +static void on_wsj1_hangup(void *closure, struct afb_wsj1 *wsj1) { printf("ON-HANGUP\n"); fflush(stdout); @@ -150,7 +206,7 @@ static void on_hangup(void *closure, struct afb_wsj1 *wsj1) } /* called when wsj1 receives a method invocation */ -static void on_call(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg) +static void on_wsj1_call(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg) { int rc; if (raw) @@ -166,7 +222,7 @@ static void on_call(void *closure, const char *api, const char *verb, struct afb } /* called when wsj1 receives an event */ -static void on_event(void *closure, const char *event, struct afb_wsj1_msg *msg) +static void on_wsj1_event(void *closure, const char *event, struct afb_wsj1_msg *msg) { if (raw) printf("ON-EVENT %s(%s)\n", event, afb_wsj1_msg_object_s(msg)); @@ -178,7 +234,7 @@ static void on_event(void *closure, const char *event, struct afb_wsj1_msg *msg) } /* called when wsj1 receives a reply */ -static void on_reply(void *closure, struct afb_wsj1_msg *msg) +static void on_wsj1_reply(void *closure, struct afb_wsj1_msg *msg) { if (raw) printf("ON-REPLY %s: %s\n", (char*)closure, afb_wsj1_msg_object_s(msg)); @@ -189,14 +245,11 @@ static void on_reply(void *closure, struct afb_wsj1_msg *msg) JSON_C_TO_STRING_PRETTY)); fflush(stdout); free(closure); - callcount--; - if (exonrep && !callcount) - //afb_wsj1_hangup(afb_wsj1_msg_wsj1(msg)); - exit(0); + dec_callcount(); } /* makes a call */ -static void call(const char *api, const char *verb, const char *object) +static void wsj1_call(const char *api, const char *verb, const char *object) { static int num = 0; char *key; @@ -207,15 +260,15 @@ static void call(const char *api, const char *verb, const char *object) /* send the request */ callcount++; - rc = afb_wsj1_call_s(wsj1, api, verb, object, on_reply, key); + rc = afb_wsj1_call_s(wsj1, api, verb, object, on_wsj1_reply, key); if (rc < 0) { fprintf(stderr, "calling %s/%s(%s) failed: %m\n", api, verb, object); - callcount--; + dec_callcount(); } } /* sends an event */ -static void event(const char *event, const char *object) +static void wsj1_event(const char *event, const char *object) { int rc; @@ -225,14 +278,14 @@ static void event(const char *event, const char *object) } /* emits either a call (when api!='!') or an event */ -static void emit(const char *api, const char *verb, const char *object) +static void wsj1_emit(const char *api, const char *verb, const char *object) { if (object == NULL || object[0] == 0) object = "null"; if (api[0] == '!' && api[1] == 0) - event(verb, object); + wsj1_event(verb, object); else - call(api, verb, object); + wsj1_call(api, verb, object); } /* called when something happens on stdin */ @@ -271,8 +324,13 @@ static int io_event_callback(sd_event_source *src, int fd, uint32_t revents, voi while(i < count && strchr(sep, line[i])) i++; api[0] = i; while(i < count && !strchr(sepnl, line[i])) i++; api[1] = i; while(i < count && strchr(sep, line[i])) i++; - verb[0] = i; while(i < count && !strchr(sepnl, line[i])) i++; verb[1] = i; - while(i < count && strchr(sep, line[i])) i++; + if (direct) { + verb[0] = api[0]; + verb[1] = api[1]; + } else { + verb[0] = i; while(i < count && !strchr(sepnl, line[i])) i++; verb[1] = i; + while(i < count && strchr(sep, line[i])) i++; + } rest[0] = i; while(i < count && line[i] != '\n') i++; rest[1] = i; if (i == count) break; line[i++] = 0; @@ -284,7 +342,10 @@ static int io_event_callback(sd_event_source *src, int fd, uint32_t revents, voi fprintf(stderr, "verb missing, bad line: %s\n", line+pos); } else { line[api[1]] = line[verb[1]] = 0; - emit(line + api[0], line + verb[0], line + rest[0]); + if (direct) + pws_call(line + verb[0], line + rest[0]); + else + wsj1_emit(line + api[0], line + verb[0], line + rest[0]); } pos = i; } @@ -298,3 +359,110 @@ static int io_event_callback(sd_event_source *src, int fd, uint32_t revents, voi return 1; } +static void on_pws_reply_success(void *closure, void *request, struct json_object *result, const char *info) +{ + if (raw) + printf("ON-REPLY-SUCCESS %s: [%s] %s\n", (char*)request, info?:"", json_object_to_json_string(result)); + if (human) + printf("ON-REPLY-SUCCESS %s: %s\n%s\n", (char*)request, info?:"", json_object_to_json_string_ext(result, JSON_C_TO_STRING_PRETTY)); + fflush(stdout); + free(request); + dec_callcount(); +} + +static void on_pws_reply_fail(void *closure, void *request, const char *status, const char *info) +{ + printf("ON-REPLY-FAIL %s: %s [%s]\n", (char*)request, status?:"?", info?:""); + fflush(stdout); + free(request); + dec_callcount(); +} + +static void on_pws_event_create(void *closure, const char *event_name, int event_id) +{ + printf("ON-EVENT-CREATE: [%d:%s]\n", event_id, event_name); + fflush(stdout); +} + +static void on_pws_event_remove(void *closure, const char *event_name, int event_id) +{ + printf("ON-EVENT-REMOVE: [%d:%s]\n", event_id, event_name); + fflush(stdout); +} + +static void on_pws_event_subscribe(void *closure, void *request, const char *event_name, int event_id) +{ + printf("ON-EVENT-SUBSCRIBE %s: [%d:%s]\n", (char*)request, event_id, event_name); + fflush(stdout); +} + +static void on_pws_event_unsubscribe(void *closure, void *request, const char *event_name, int event_id) +{ + printf("ON-EVENT-UNSUBSCRIBE %s: [%d:%s]\n", (char*)request, event_id, event_name); + fflush(stdout); +} + +static void on_pws_event_push(void *closure, const char *event_name, int event_id, struct json_object *data) +{ + if (raw) + printf("ON-EVENT-PUSH: [%d:%s] %s\n", event_id, event_name, json_object_to_json_string(data)); + if (human) + printf("ON-EVENT-PUSH: [%d:%s]\n%s\n", event_id, event_name, json_object_to_json_string_ext(data, JSON_C_TO_STRING_PRETTY)); + fflush(stdout); +} + +static void on_pws_event_broadcast(void *closure, const char *event_name, struct json_object *data) +{ + if (raw) + printf("ON-EVENT-BROADCAST: [%s] %s\n", event_name, json_object_to_json_string(data)); + if (human) + printf("ON-EVENT-BROADCAST: [%s]\n%s\n", event_name, json_object_to_json_string_ext(data, JSON_C_TO_STRING_PRETTY)); + fflush(stdout); +} + +static void on_pws_subcall(void *closure, struct afb_proto_ws_subcall *subcall, void *request, const char *api, const char *verb, struct json_object *args) +{ + if (raw) + printf("ON-SUBCALL %s: %s/%s %s\n", (char*)request, api, verb, json_object_to_json_string(args)); + if (human) + printf("ON-SUBCALL %s: %s/%s\n%s\n", (char*)request, api, verb, json_object_to_json_string_ext(args, JSON_C_TO_STRING_PRETTY)); + afb_proto_ws_subcall_reply(subcall, 1, NULL); + fflush(stdout); +} + +/* makes a call */ +static void pws_call(const char *verb, const char *object) +{ + static int num = 0; + char *key; + int rc; + struct json_object *o; + + /* allocates an id for the request */ + rc = asprintf(&key, "%d:%s", ++num, verb); + + /* send the request */ + callcount++; + if (object == NULL || object[0] == 0 || !strcmp(object, "null")) + o = NULL; + else { + o = json_tokener_parse(object); + if (!o) + o = json_object_new_string(object); + } + rc = afb_proto_ws_client_call(pws, verb, o, sessionid, key); + if (rc < 0) { + fprintf(stderr, "calling %s(%s) failed: %m\n", verb, object?:""); + dec_callcount(); + } +} + +/* called when pws hangsup */ +static void on_pws_hangup(void *closure) +{ + printf("ON-HANGUP\n"); + fflush(stdout); + exit(0); +} + + diff --git a/src/afb-ws-client.c b/src/afb-ws-client.c index 582212fc..e1d3277e 100644 --- a/src/afb-ws-client.c +++ b/src/afb-ws-client.c @@ -397,4 +397,124 @@ static char *makequery(const char *path, const char *uuid, const char *token) } #endif +/*****************************************************************************************************************************/ + +#include +#include "afb-proto-ws.h" + +static int get_socket_unix(const char *uri) +{ + int fd, rc; + struct sockaddr_un addr; + size_t length; + + length = strlen(uri); + if (length >= 108) { + errno = ENAMETOOLONG; + return -1; + } + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) + return fd; + + memset(&addr, 0, sizeof addr); + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, uri); + if (addr.sun_path[0] == '@') + addr.sun_path[0] = 0; /* implement abstract sockets */ + rc = connect(fd, (struct sockaddr *) &addr, (socklen_t)(sizeof addr)); + if (rc < 0) { + close(fd); + return rc; + } + return fd; +} + +static int get_socket_inet(const char *uri) +{ + int rc, fd; + const char *service, *host, *api; + struct addrinfo hint, *rai, *iai; + + /* scan the uri */ + api = strrchr(uri, '/'); + service = strrchr(uri, ':'); + if (api == NULL || service == NULL || api < service) { + errno = EINVAL; + return -1; + } + host = strndupa(uri, service++ - uri); + service = strndupa(service, api - service); + + /* get addr */ + memset(&hint, 0, sizeof hint); + hint.ai_family = AF_INET; + hint.ai_socktype = SOCK_STREAM; + rc = getaddrinfo(host, service, &hint, &rai); + if (rc != 0) { + errno = EINVAL; + return -1; + } + + /* get the socket */ + iai = rai; + while (iai != NULL) { + fd = socket(iai->ai_family, iai->ai_socktype, iai->ai_protocol); + if (fd >= 0) { + rc = connect(fd, iai->ai_addr, iai->ai_addrlen); + if (rc == 0) { + freeaddrinfo(rai); + return fd; + } + close(fd); + } + iai = iai->ai_next; + } + freeaddrinfo(rai); + return -1; +} + +static int get_socket(const char *uri) +{ + int fd; + + /* check for unix socket */ + if (0 == strncmp(uri, "unix:", 5)) + /* unix socket */ + fd = get_socket_unix(uri + 5); + else + /* inet socket */ + fd = get_socket_inet(uri); + + /* configure the socket */ + if (fd >= 0) { + fcntl(fd, F_SETFD, FD_CLOEXEC); + fcntl(fd, F_SETFL, O_NONBLOCK); + } + return fd; +} +/* + * Establish a websocket-like client connection to the API of 'uri' and if successful + * instanciate a client afb_proto_ws websocket for this API using 'itf' and 'closure'. + * (see afb_proto_ws_create_client). + * The systemd event loop 'eloop' is used to handle the websocket. + * Returns NULL in case of failure with errno set appriately. + */ +struct afb_proto_ws *afb_ws_client_connect_api(struct sd_event *eloop, const char *uri, struct afb_proto_ws_client_itf *itf, void *closure) +{ + int fd; + struct afb_proto_ws *pws; + + fd = get_socket(uri); + if (fd >= 0) { + pws = afb_proto_ws_create_client(eloop, fd, itf, closure); + if (pws) + return pws; + close(fd); + } + return NULL; +} + + diff --git a/src/afb-ws-client.h b/src/afb-ws-client.h index 5bcd31ba..fc9fa06a 100644 --- a/src/afb-ws-client.h +++ b/src/afb-ws-client.h @@ -19,6 +19,8 @@ struct afb_wsj1; struct afb_wsj1_itf; +struct afb_proto_ws; +struct afb_proto_ws_client_itf; struct sd_event; /* @@ -29,3 +31,13 @@ struct sd_event; * Returns NULL in case of failure with errno set appriately. */ extern struct afb_wsj1 *afb_ws_client_connect_wsj1(struct sd_event *eloop, const char *uri, struct afb_wsj1_itf *itf, void *closure); + +/* + * Establish a websocket-like client connection to the API of 'uri' and if successful + * instanciate a client afb_proto_ws websocket for this API using 'itf' and 'closure'. + * (see afb_proto_ws_create_client). + * The systemd event loop 'eloop' is used to handle the websocket. + * Returns NULL in case of failure with errno set appriately. + */ +extern struct afb_proto_ws *afb_ws_client_connect_api(struct sd_event *eloop, const char *uri, struct afb_proto_ws_client_itf *itf, void *closure); + diff --git a/src/export-afbwsc.map b/src/export-afbwsc.map index c775a16f..359315ea 100644 --- a/src/export-afbwsc.map +++ b/src/export-afbwsc.map @@ -1,7 +1,9 @@ { global: afb_ws_client_connect_wsj1; + afb_ws_client_connect_api; afb_wsj1_*; + afb_proto_ws_*; afb_common_*; local: *;