aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJosé Bollo <jose.bollo@iot.bzh>2018-07-31 19:02:18 +0200
committerJosé Bollo <jose.bollo@iot.bzh>2018-08-02 15:49:43 +0200
commit10aa15afc5bc7321d0049823dd173dda11ff3724 (patch)
tree9d801d6c2336f8e98bf42c0a622b1b510b9429ea
parentdf67c029363f6d42a3e91cd62774c74b3ef66670 (diff)
afb-socket: Separate socket creation
The objective is to have a well identified and possibly reusable internally. Change-Id: Icdb6ce7d85ae26373cfe4f0b492fe97b74ed747c Signed-off-by: José Bollo <jose.bollo@iot.bzh>
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/afb-api-ws.c192
-rw-r--r--src/afb-api-ws.h8
-rw-r--r--src/afb-socket.c264
-rw-r--r--src/afb-socket.h22
-rw-r--r--src/afb-ws-client.c1
6 files changed, 321 insertions, 167 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index ba6ba77e..7038205c 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -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
diff --git a/src/afb-api-ws.c b/src/afb-api-ws.c
index 4f4ac72a..24ffefef 100644
--- a/src/afb-api-ws.c
+++ b/src/afb-api-ws.c
@@ -29,18 +29,18 @@
#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;
}
-
-
diff --git a/src/afb-api-ws.h b/src/afb-api-ws.h
index 812cff59..6f77b2b2 100644
--- a/src/afb-api-ws.h
+++ b/src/afb-api-ws.h
@@ -20,10 +20,10 @@
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
index 00000000..c6402d42
--- /dev/null
+++ b/src/afb-socket.c
@@ -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
index 00000000..dff922b8
--- /dev/null
+++ b/src/afb-socket.h
@@ -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);
diff --git a/src/afb-ws-client.c b/src/afb-ws-client.c
index a0c37f31..e29eadb7 100644
--- a/src/afb-ws-client.c
+++ b/src/afb-ws-client.c
@@ -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'.