afb-socket: Separate socket creation 57/15957/1
authorJosé Bollo <jose.bollo@iot.bzh>
Tue, 31 Jul 2018 17:02:18 +0000 (19:02 +0200)
committerJosé Bollo <jose.bollo@iot.bzh>
Thu, 2 Aug 2018 13:49:43 +0000 (15:49 +0200)
The objective is to have a well identified and
possibly reusable internally.

Change-Id: Icdb6ce7d85ae26373cfe4f0b492fe97b74ed747c
Signed-off-by: José Bollo <jose.bollo@iot.bzh>
src/CMakeLists.txt
src/afb-api-ws.c
src/afb-api-ws.h
src/afb-socket.c [new file with mode: 0644]
src/afb-socket.h [new file with mode: 0644]
src/afb-ws-client.c

index ba6ba77..7038205 100644 (file)
@@ -54,6 +54,7 @@ SET(AFB_LIB_SOURCES
        afb-msg-json.c
        afb-proto-ws.c
        afb-session.c
+       afb-socket.c
        afb-stub-ws.c
        afb-systemd.c
        afb-trace.c
index 4f4ac72..24ffefe 100644 (file)
 #include <sys/socket.h>
 #include <sys/un.h>
 
-#include "afb-fdev.h"
-#include "afb-systemd.h"
 #include "afb-api.h"
 #include "afb-apiset.h"
 #include "afb-api-ws.h"
+#include "afb-fdev.h"
+#include "afb-socket.h"
 #include "afb-stub-ws.h"
 #include "verbose.h"
 #include "fdev.h"
 
 struct api_ws
 {
-       char *path;             /* path of the object for the API */
+       char *uri;              /* uri of the object for the API */
        char *api;              /* api name of the interface */
        struct fdev *fdev;      /* fdev handler */
        struct afb_apiset *apiset;
@@ -49,29 +49,29 @@ struct api_ws
 /******************************************************************************/
 
 /*
- * create a structure api_ws not connected to the 'path'.
+ * create a structure api_ws not connected to the 'uri'.
  */
-static struct api_ws *api_ws_make(const char *path)
+static struct api_ws *api_ws_make(const char *uri)
 {
        struct api_ws *api;
        size_t length;
 
        /* allocates the structure */
-       length = strlen(path);
+       length = strlen(uri);
        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);
+       /* uri is copied after the struct */
+       api->uri = (char*)(api+1);
+       memcpy(api->uri, uri, length + 1);
 
-       /* api name is at the end of the path */
-       while (length && path[length - 1] != '/' && path[length - 1] != ':')
+       /* api name is at the end of the uri */
+       while (length && uri[length - 1] != '/' && uri[length - 1] != ':')
                length = length - 1;
-       api->api = &api->path[length];
+       api->api = &api->uri[length];
        if (api->api == NULL || !afb_api_is_valid_name(api->api)) {
                errno = EINVAL;
                goto error2;
@@ -85,162 +85,30 @@ error:
        return NULL;
 }
 
-static int api_ws_socket_unix(const char *path, int server)
-{
-       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;
-}
-
-static int api_ws_socket_inet(const char *path, int server)
-{
-       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);
-
-       /* 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) {
-                       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;
-                       }
-                       close(fd);
-               }
-               iai = iai->ai_next;
-       }
-       freeaddrinfo(rai);
-       return -1;
-}
-
-static int api_ws_socket(const char *path, int server)
-{
-       int fd, rc;
-
-       /* check for systemd socket */
-       if (0 == strncmp(path, "sd:", 3))
-               fd = afb_systemd_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;
-}
-
-static struct fdev *api_ws_socket_fdev(const char *path, int server)
-{
-       int fd;
-       struct fdev *fdev;
-
-       fd = api_ws_socket(path, server);
-       if (fd < 0)
-               fdev = 0;
-       else {
-               fdev = afb_fdev_create(fd);
-               if (!fdev)
-                       close(fd);
-       }
-       if (!fdev)
-               ERROR("can't make %s socket for %s", server ? "server" : "client", path);
-       return fdev;
-}
-
 /**********************************************************************************/
 
-int afb_api_ws_add_client(const char *path, struct afb_apiset *declare_set, struct afb_apiset *call_set, int strong)
+int afb_api_ws_add_client(const char *uri, struct afb_apiset *declare_set, struct afb_apiset *call_set, int strong)
 {
        struct api_ws *apiws;
        struct afb_stub_ws *stubws;
 
        /* create the ws client api */
-       apiws = api_ws_make(path);
+       apiws = api_ws_make(uri);
        if (apiws == NULL)
                goto error;
 
        /* connect to the service */
-       apiws->fdev = api_ws_socket_fdev(apiws->path, 0);
+       apiws->fdev = afb_socket_open(apiws->uri, 0);
        if (!apiws->fdev)
                goto error2;
 
        stubws = afb_stub_ws_create_client(apiws->fdev, apiws->api, call_set);
        if (!stubws) {
-               ERROR("can't setup client ws service to %s", apiws->path);
+               ERROR("can't setup client ws service to %s", apiws->uri);
                goto error3;
        }
        if (afb_stub_ws_client_add(stubws, declare_set) < 0) {
-               ERROR("can't add the client to the apiset for service %s", apiws->path);
+               ERROR("can't add the client to the apiset for service %s", apiws->uri);
                goto error3;
        }
        free(apiws);
@@ -253,14 +121,14 @@ error:
        return -!!strong;
 }
 
-int afb_api_ws_add_client_strong(const char *path, struct afb_apiset *declare_set, struct afb_apiset *call_set)
+int afb_api_ws_add_client_strong(const char *uri, struct afb_apiset *declare_set, struct afb_apiset *call_set)
 {
-       return afb_api_ws_add_client(path, declare_set, call_set, 1);
+       return afb_api_ws_add_client(uri, declare_set, call_set, 1);
 }
 
-int afb_api_ws_add_client_weak(const char *path, struct afb_apiset *declare_set, struct afb_apiset *call_set)
+int afb_api_ws_add_client_weak(const char *uri, struct afb_apiset *declare_set, struct afb_apiset *call_set)
 {
-       return afb_api_ws_add_client(path, declare_set, call_set, 0);
+       return afb_api_ws_add_client(uri, declare_set, call_set, 0);
 }
 
 static int api_ws_server_accept_client(struct api_ws *apiws, struct fdev *fdev)
@@ -278,16 +146,16 @@ static void api_ws_server_accept(struct api_ws *apiws)
        lenaddr = (socklen_t)sizeof addr;
        fd = accept(fdev_fd(apiws->fdev), &addr, &lenaddr);
        if (fd < 0) {
-               ERROR("can't accept connection to %s: %m", apiws->path);
+               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->path);
+                       ERROR("can't hold accepted connection to %s: %m", apiws->uri);
                        close(fd);
                } else {
                        rc = api_ws_server_accept_client(apiws, fdev);
                        if (rc < 0)
-                               ERROR("can't serve accepted connection to %s: %m", apiws->path);
+                               ERROR("can't serve accepted connection to %s: %m", apiws->uri);
                }
        }
 }
@@ -316,9 +184,9 @@ static int api_ws_server_connect(struct api_ws *apiws)
        api_ws_server_disconnect(apiws);
 
        /* request the service object name */
-       apiws->fdev = api_ws_socket_fdev(apiws->path, 1);
+       apiws->fdev = afb_socket_open(apiws->uri, 1);
        if (!apiws->fdev)
-               ERROR("can't create socket %s", apiws->path);
+               ERROR("can't create socket %s", apiws->uri);
        else {
                /* listen for service */
                fdev_set_events(apiws->fdev, EPOLLIN);
@@ -329,19 +197,19 @@ static int api_ws_server_connect(struct api_ws *apiws)
 }
 
 /* create the service */
-int afb_api_ws_add_server(const char *path, struct afb_apiset *declare_set, struct afb_apiset *call_set)
+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;
 
        /* creates the ws api object */
-       apiws = api_ws_make(path);
+       apiws = api_ws_make(uri);
        if (apiws == NULL)
                goto error;
 
        /* check api name */
        if (!afb_apiset_lookup(call_set, apiws->api, 1)) {
-               ERROR("Can't provide ws-server for %s: API %s doesn't exist", path, apiws->api);
+               ERROR("Can't provide ws-server for %s: API %s doesn't exist", uri, apiws->api);
                goto error2;
        }
 
@@ -358,5 +226,3 @@ error2:
 error:
        return -1;
 }
-
-
index 812cff5..6f77b2b 100644 (file)
 
 struct afb_apiset;
 
-extern int afb_api_ws_add_client(const char *path, struct afb_apiset *declare_set, struct afb_apiset *call_set, int strong);
-extern int afb_api_ws_add_client_strong(const char *path, struct afb_apiset *declare_set, struct afb_apiset *call_set);
-extern int afb_api_ws_add_client_weak(const char *path, struct afb_apiset *declare_set, struct afb_apiset *call_set);
+extern int afb_api_ws_add_client(const char *uri, struct afb_apiset *declare_set, struct afb_apiset *call_set, int strong);
+extern int afb_api_ws_add_client_strong(const char *uri, struct afb_apiset *declare_set, struct afb_apiset *call_set);
+extern int afb_api_ws_add_client_weak(const char *uri, struct afb_apiset *declare_set, struct afb_apiset *call_set);
 
-extern int afb_api_ws_add_server(const char *path, struct afb_apiset *declare_set, struct afb_apiset *call_set);
+extern int afb_api_ws_add_server(const char *uri, struct afb_apiset *declare_set, struct afb_apiset *call_set);
 
 
diff --git a/src/afb-socket.c b/src/afb-socket.c
new file mode 100644 (file)
index 0000000..c6402d4
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2015-2018 "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.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <endian.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "afb-fdev.h"
+#include "afb-socket.h"
+#include "afb-systemd.h"
+#include "fdev.h"
+#include "verbose.h"
+
+#define BACKLOG  5
+
+/******************************************************************************/
+
+static int open_unix(const char *spec, int server)
+{
+       int fd, rc, abstract;
+       struct sockaddr_un addr;
+       size_t length;
+
+       abstract = spec[0] == '@';
+
+       /* check the length */
+       length = strlen(spec);
+       if (length >= 108) {
+               errno = ENAMETOOLONG;
+               return -1;
+       }
+
+       /* remove the file on need */
+       if (server && !abstract)
+               unlink(spec);
+
+       /* create a  socket */
+       fd = socket(AF_UNIX, SOCK_STREAM, 0);
+       if (fd < 0)
+               return fd;
+
+       /* prepare address  */
+       memset(&addr, 0, sizeof addr);
+       addr.sun_family = AF_UNIX;
+       strcpy(addr.sun_path, spec);
+       if (abstract)
+               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;
+}
+
+static int open_inet(const char *spec, int server)
+{
+       int rc, fd;
+       const char *service, *host, *api;
+       struct addrinfo hint, *rai, *iai;
+
+       /* scan the uri */
+       api = strrchr(spec, '/');
+       service = strrchr(spec, ':');
+       if (api == NULL || service == NULL || api < service) {
+               errno = EINVAL;
+               return -1;
+       }
+       host = strndupa(spec, service++ - spec);
+       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) {
+                       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;
+                       }
+                       close(fd);
+               }
+               iai = iai->ai_next;
+       }
+       freeaddrinfo(rai);
+       return -1;
+}
+
+static int open_systemd(const char *spec)
+{
+#if defined(NO_SYSTEMD_ACTIVATION)
+       errno = EAFNOSUPPORT;
+       fd = -1;
+#else
+       return afb_systemd_fds_for(spec);
+#endif
+}
+
+/******************************************************************************/
+
+enum type {
+       Type_Inet,
+       Type_Systemd,
+       Type_Unix
+};
+
+struct entry
+{
+       const char *prefix;
+       unsigned type: 2;
+       unsigned noreuseaddr: 1;
+       unsigned nolisten: 1;
+};
+
+static struct entry entries[] = { /* default at first place */
+       {
+               .prefix = "tcp:",
+               .type = Type_Inet
+       },
+       {
+               .prefix = "sd:",
+               .type = Type_Systemd,
+               .noreuseaddr = 1,
+               .nolisten = 1
+       },
+       {
+               .prefix = "unix:",
+               .type = Type_Unix
+       }
+};
+
+/******************************************************************************/
+
+/* get the entry of the uri by searching to its prefix */
+static struct entry *get_entry(const char *uri, int *offset)
+{
+       int l, search = 1, i = (int)(sizeof entries / sizeof * entries);
+
+       while (search) {
+               if (!i) {
+                       l = 0;
+                       search = 0;
+               } else {
+                       i--;
+                       l = (int)strlen(entries[i].prefix);
+                       search = strncmp(uri, entries[i].prefix, l);
+               }
+       }
+
+       *offset = l;
+       return &entries[i];
+}
+
+static int open_any(const char *uri, int server)
+{
+       int fd, rc, offset;
+       struct entry *e;
+
+       /* search for the entry */
+       e = get_entry(uri, &offset);
+
+       /* get the names */
+
+       uri += offset;
+
+       /* open the socket */
+       switch (e->type) {
+       case Type_Unix:
+               fd = open_unix(uri, server);
+               break;
+       case Type_Inet:
+               fd = open_inet(uri, server);
+               break;
+       case Type_Systemd:
+               fd = open_systemd(uri);
+               break;
+       default:
+               errno = EAFNOSUPPORT;
+               fd = -1;
+               break;
+       }
+       if (fd < 0)
+               return -1;
+
+       /* set it up */
+       fcntl(fd, F_SETFD, FD_CLOEXEC);
+       fcntl(fd, F_SETFL, O_NONBLOCK);
+       if (server) {
+               if (!e->noreuseaddr) {
+                       rc = 1;
+                       setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &rc, sizeof rc);
+               }
+               if (!e->nolisten)
+                       listen(fd, BACKLOG);
+       }
+       return fd;
+}
+
+struct fdev *afb_socket_open(const char *uri, int server)
+{
+       int fd;
+       struct fdev *fdev;
+
+       fd = open_any(uri, server);
+       if (fd < 0)
+               goto error;
+
+       fdev = afb_fdev_create(fd);
+       if (!fdev)
+               goto error2;
+
+       return fdev;
+
+error2:
+       close(fd);
+error:
+       ERROR("can't make %s socket for %s", server ? "server" : "client", uri);
+       return NULL;
+}
+
diff --git a/src/afb-socket.h b/src/afb-socket.h
new file mode 100644 (file)
index 0000000..dff922b
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2018 "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
+
+struct fdev;
+
+extern struct fdev *afb_socket_open(const char *uri, int server);
index a0c37f3..e29eadb 100644 (file)
@@ -499,6 +499,7 @@ static int get_socket(const char *uri)
        }
        return fd;
 }
+
 /*
  * Establish a websocket-like client connection to the API of 'uri' and if successful
  * instantiate a client afb_proto_ws websocket for this API using 'itf' and 'closure'.