X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?p=src%2Fapp-framework-binder.git;a=blobdiff_plain;f=src%2Fafb-api-ws.c;h=4ea8610e801b19406984e39ada70603e390a6dbf;hp=0022d52668045f0a192b3e8b2fed8c8ce8dec8fa;hb=65353dce81a629e042800bb7b86fcd869a76727e;hpb=6790479e981d885449777ed430f30ad5bf77aa45 diff --git a/src/afb-api-ws.c b/src/afb-api-ws.c index 0022d526..4ea8610e 100644 --- a/src/afb-api-ws.c +++ b/src/afb-api-ws.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015, 2016, 2017 "IoT.bzh" + * Copyright (C) 2015-2020 "IoT.bzh" * Author José Bollo * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,7 +16,6 @@ */ #define _GNU_SOURCE -#define NO_PLUGIN_VERBOSE_MACRO #include #include @@ -30,318 +29,209 @@ #include #include -#include #include "afb-api.h" #include "afb-apiset.h" -#include "afb-common.h" +#include "afb-api-ws.h" +#include "afb-fdev.h" +#include "afb-socket.h" #include "afb-stub-ws.h" #include "verbose.h" -#include "sd-fds.h" +#include "fdev.h" -struct api_ws +struct api_ws_server { - char *path; /* path of the object for the API */ - char *api; /* api name of the interface */ - int fd; /* file descriptor */ - sd_event_source *listensrc; /**< systemd source for server socket */ - struct afb_apiset *apiset; + struct afb_apiset *apiset; /* the apiset for calling */ + struct fdev *fdev; /* fdev handler */ + uint16_t offapi; /* api name of the interface */ + char uri[]; /* the uri of the server socket */ }; +/******************************************************************************/ +/*** C L I E N T ***/ /******************************************************************************/ -/* - * create a structure api_ws not connected to the 'path'. - */ -static struct api_ws *api_ws_make(const char *path) -{ - struct api_ws *api; - size_t length; - - /* allocates the structure */ - length = strlen(path); - api = calloc(1, sizeof *api + 1 + length); - if (api == NULL) { - errno = ENOMEM; - goto error; - } - - /* path is copied after the struct */ - api->path = (char*)(api+1); - memcpy(api->path, path, length + 1); - - /* api name is at the end of the path */ - while (length && path[length - 1] != '/' && path[length - 1] != ':') - length = length - 1; - api->api = &api->path[length]; - if (api->api == NULL || !afb_api_is_valid_name(api->api)) { - errno = EINVAL; - goto error2; - } - - api->fd = -1; - return api; - -error2: - free(api); -error: - return NULL; -} - -static int api_ws_socket_unix(const char *path, int server) +static struct fdev *reopen_client(void *closure) { - int fd, rc; - struct sockaddr_un addr; - size_t length; - - length = strlen(path); - if (length >= 108) { - errno = ENAMETOOLONG; - return -1; - } - - if (server && path[0] != '@') - unlink(path); - - 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, path); - if (addr.sun_path[0] == '@') - addr.sun_path[0] = 0; /* implement abstract sockets */ - if (server) { - rc = bind(fd, (struct sockaddr *) &addr, (socklen_t)(sizeof addr)); - } else { - rc = connect(fd, (struct sockaddr *) &addr, (socklen_t)(sizeof addr)); - } - if (rc < 0) { - close(fd); - return rc; - } - return fd; + const char *uri = closure; + return afb_socket_open_fdev(uri, 0); } -static int api_ws_socket_inet(const char *path, int server) +int afb_api_ws_add_client(const char *uri, struct afb_apiset *declare_set, struct afb_apiset *call_set, int strong) { - int rc, fd; - const char *service, *host, *api; - struct addrinfo hint, *rai, *iai; - - /* scan the uri */ - api = strrchr(path, '/'); - service = strrchr(path, ':'); - if (api == NULL || service == NULL || api < service) { - errno = EINVAL; - return -1; - } - host = strndupa(path, service++ - path); - service = strndupa(service, api - service); + struct afb_stub_ws *stubws; + struct fdev *fdev; + const char *api; - /* 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) { + /* check the api name */ + api = afb_socket_api(uri); + if (api == NULL || !afb_api_is_valid_name(api)) { + ERROR("invalid (too long) ws client uri %s", uri); errno = EINVAL; - return -1; + goto error; } - /* get the socket */ - iai = rai; - while (iai != NULL) { - fd = socket(iai->ai_family, iai->ai_socktype, iai->ai_protocol); - if (fd >= 0) { - if (server) { - rc = bind(fd, iai->ai_addr, iai->ai_addrlen); - } else { - rc = connect(fd, iai->ai_addr, iai->ai_addrlen); - } - if (rc == 0) { - freeaddrinfo(rai); - return fd; + /* open the socket */ + fdev = afb_socket_open_fdev(uri, 0); + if (fdev) { + /* create the client stub */ + stubws = afb_stub_ws_create_client(fdev, api, call_set); + if (!stubws) { + ERROR("can't setup client ws service to %s", uri); + fdev_unref(fdev); + } else { + if (afb_stub_ws_client_add(stubws, declare_set) >= 0) { +#if 1 + /* it is asserted here that uri is never released */ + afb_stub_ws_client_robustify(stubws, reopen_client, (void*)uri, NULL); +#else + /* it is asserted here that uri is released, so use a copy */ + afb_stub_ws_client_robustify(stubws, reopen_client, strdup(uri), free); +#endif + return 0; } - close(fd); + ERROR("can't add the client to the apiset for service %s", uri); + afb_stub_ws_unref(stubws); } - iai = iai->ai_next; } - freeaddrinfo(rai); - return -1; - +error: + return -!!strong; } -static int api_ws_socket(const char *path, int server) +int afb_api_ws_add_client_strong(const char *uri, struct afb_apiset *declare_set, struct afb_apiset *call_set) { - int fd, rc; - - /* check for systemd socket */ - if (0 == strncmp(path, "sd:", 3)) - fd = sd_fds_for(path + 3); - else { - /* check for unix socket */ - if (0 == strncmp(path, "unix:", 5)) - /* unix socket */ - fd = api_ws_socket_unix(path + 5, server); - else - /* inet socket */ - fd = api_ws_socket_inet(path, server); - - if (fd >= 0 && server) { - rc = 1; - setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &rc, sizeof rc); - rc = listen(fd, 5); - } - } - /* configure the socket */ - if (fd >= 0) { - fcntl(fd, F_SETFD, FD_CLOEXEC); - fcntl(fd, F_SETFL, O_NONBLOCK); - } - return fd; + return afb_api_ws_add_client(uri, declare_set, call_set, 1); } -/**********************************************************************************/ - -int afb_api_ws_add_client(const char *path, struct afb_apiset *apiset) +int afb_api_ws_add_client_weak(const char *uri, struct afb_apiset *declare_set, struct afb_apiset *call_set) { - struct api_ws *apiws; - struct afb_stub_ws *stubws; - - /* create the ws client api */ - apiws = api_ws_make(path); - if (apiws == NULL) - goto error; - - /* connect to the service */ - apiws->fd = api_ws_socket(apiws->path, 0); - if (apiws->fd < 0) { - ERROR("can't connect to ws service %s", apiws->path); - goto error2; - } - - stubws = afb_stub_ws_create_client(apiws->fd, apiws->api, apiset); - if (!stubws) { - ERROR("can't setup client ws service to %s", apiws->path); - goto error3; - } - if (afb_stub_ws_client_add(stubws, apiset) < 0) { - ERROR("can't add the client to the apiset for service %s", apiws->path); - goto error4; - } - free(apiws); - return 0; -error4: - afb_stub_ws_unref(stubws); -error3: - close(apiws->fd); -error2: - free(apiws); -error: - return -1; + return afb_api_ws_add_client(uri, declare_set, call_set, 0); } -static int api_ws_server_accept_client(struct api_ws *apiws, int fd) -{ - return -!afb_stub_ws_create_server(fd, apiws->api, apiws->apiset); -} +/*****************************************************************************/ +/*** S E R V E R ***/ +/******************************************************************************/ -static void api_ws_server_accept(struct api_ws *apiws) +static void api_ws_server_accept(struct api_ws_server *apiws) { - int rc, fd; + int fd; struct sockaddr addr; socklen_t lenaddr; + struct fdev *fdev; + struct afb_stub_ws *server; lenaddr = (socklen_t)sizeof addr; - fd = accept(apiws->fd, &addr, &lenaddr); - if (fd >= 0) { - rc = api_ws_server_accept_client(apiws, fd); - if (rc >= 0) - return; - close(fd); + fd = accept(fdev_fd(apiws->fdev), &addr, &lenaddr); + if (fd < 0) { + ERROR("can't accept connection to %s: %m", apiws->uri); + } else { + fdev = afb_fdev_create(fd); + if (!fdev) { + ERROR("can't hold accepted connection to %s: %m", apiws->uri); + close(fd); + } else { + server = afb_stub_ws_create_server(fdev, &apiws->uri[apiws->offapi], apiws->apiset); + if (server) + afb_stub_ws_set_on_hangup(server, afb_stub_ws_unref); + else + ERROR("can't serve accepted connection to %s: %m", apiws->uri); + } } } -static int api_ws_server_connect(struct api_ws *apiws); +static int api_ws_server_connect(struct api_ws_server *apiws); -static int api_ws_server_listen_callback(sd_event_source *src, int fd, uint32_t revents, void *closure) +static void api_ws_server_listen_callback(void *closure, uint32_t revents, struct fdev *fdev) { - struct api_ws *apiws = closure; + struct api_ws_server *apiws = closure; - if ((revents & EPOLLIN) != 0) - api_ws_server_accept(apiws); if ((revents & EPOLLHUP) != 0) api_ws_server_connect(apiws); - return 0; + else if ((revents & EPOLLIN) != 0) + api_ws_server_accept(apiws); } -static void api_ws_server_disconnect(struct api_ws *apiws) +static void api_ws_server_disconnect(struct api_ws_server *apiws) { - if (apiws->listensrc != NULL) { - sd_event_source_unref(apiws->listensrc); - apiws->listensrc = NULL; - } - if (apiws->fd >= 0) { - close(apiws->fd); - apiws->fd = -1; - } + fdev_unref(apiws->fdev); + apiws->fdev = 0; } -static int api_ws_server_connect(struct api_ws *apiws) +static int api_ws_server_connect(struct api_ws_server *apiws) { - int rc; - /* ensure disconnected */ api_ws_server_disconnect(apiws); /* request the service object name */ - apiws->fd = api_ws_socket(apiws->path, 1); - if (apiws->fd < 0) - ERROR("can't create socket %s", apiws->path); + apiws->fdev = afb_socket_open_fdev(apiws->uri, 1); + if (!apiws->fdev) + ERROR("can't create socket %s", apiws->uri); else { /* listen for service */ - rc = sd_event_add_io(afb_common_get_event_loop(), - &apiws->listensrc, apiws->fd, EPOLLIN, - api_ws_server_listen_callback, apiws); - if (rc >= 0) - return 0; - close(apiws->fd); - errno = -rc; - ERROR("can't add ws object %s", apiws->path); + fdev_set_events(apiws->fdev, EPOLLIN); + fdev_set_callback(apiws->fdev, api_ws_server_listen_callback, apiws); + return 0; } return -1; } /* create the service */ -int afb_api_ws_add_server(const char *path, struct afb_apiset *apiset) +int afb_api_ws_add_server(const char *uri, struct afb_apiset *declare_set, struct afb_apiset *call_set) { int rc; - struct api_ws *apiws; + const char *api; + struct api_ws_server *apiws; + size_t luri, lapi, extra; + + /* check the size */ + luri = strlen(uri); + if (luri > 4000) { + ERROR("can't create socket %s", uri); + errno = E2BIG; + return -1; + } - /* creates the ws api object */ - apiws = api_ws_make(path); - if (apiws == NULL) + /* check the api name */ + api = afb_socket_api(uri); + if (api == NULL || !afb_api_is_valid_name(api)) { + ERROR("invalid api name in ws uri %s", uri); + errno = EINVAL; goto error; + } /* check api name */ - if (!afb_apiset_lookup(apiset, apiws->api, 1)) { - ERROR("Can't provide ws-server for %s: API %s doesn't exist", path, apiws->api); - goto error2; + if (!afb_apiset_lookup(call_set, api, 1)) { + ERROR("Can't provide ws-server for URI %s: API %s doesn't exist", uri, api); + errno = ENOENT; + goto error; + } + + /* make the structure */ + lapi = strlen(api); + extra = luri == (api - uri) + lapi ? 0 : lapi + 1; + apiws = malloc(sizeof * apiws + 1 + luri + extra); + if (!apiws) { + ERROR("out of memory"); + errno = ENOMEM; + goto error; + } + + apiws->apiset = afb_apiset_addref(call_set); + apiws->fdev = 0; + strcpy(apiws->uri, uri); + if (!extra) + apiws->offapi = (uint16_t)(api - uri); + else { + apiws->offapi = (uint16_t)(luri + 1); + strcpy(&apiws->uri[apiws->offapi], api); } /* connect for serving */ rc = api_ws_server_connect(apiws); - if (rc < 0) - goto error2; - - apiws->apiset = afb_apiset_addref(apiset); - return 0; + if (rc >= 0) + return 0; -error2: + afb_apiset_unref(apiws->apiset); free(apiws); error: return -1; } - -