2 * Copyright (C) 2015, 2016, 2017 "IoT.bzh"
3 * Author José Bollo <jose.bollo@iot.bzh>
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
19 #define NO_PLUGIN_VERBOSE_MACRO
29 #include <sys/types.h>
30 #include <sys/socket.h>
33 #include <systemd/sd-event.h>
35 #include "afb-apiset.h"
36 #include "afb-common.h"
37 #include "afb-stub-ws.h"
43 char *path; /* path of the object for the API */
44 char *api; /* api name of the interface */
45 int fd; /* file descriptor */
46 sd_event_source *listensrc; /**< systemd source for server socket */
47 struct afb_apiset *apiset;
50 /******************************************************************************/
53 * create a structure api_ws not connected to the 'path'.
55 static struct api_ws *api_ws_make(const char *path)
60 /* allocates the structure */
61 length = strlen(path);
62 api = calloc(1, sizeof *api + 1 + length);
68 /* path is copied after the struct */
69 api->path = (char*)(api+1);
70 memcpy(api->path, path, length + 1);
72 /* api name is at the end of the path */
73 while (length && path[length - 1] != '/' && path[length - 1] != ':')
75 api->api = &api->path[length];
76 if (api->api == NULL || !afb_api_is_valid_name(api->api, 1)) {
90 static int api_ws_socket_unix(const char *path, int server)
93 struct sockaddr_un addr;
96 length = strlen(path);
102 if (server && path[0] != '@')
105 fd = socket(AF_UNIX, SOCK_STREAM, 0);
109 memset(&addr, 0, sizeof addr);
110 addr.sun_family = AF_UNIX;
111 strcpy(addr.sun_path, path);
112 if (addr.sun_path[0] == '@')
113 addr.sun_path[0] = 0; /* implement abstract sockets */
115 rc = bind(fd, (struct sockaddr *) &addr, (socklen_t)(sizeof addr));
117 rc = connect(fd, (struct sockaddr *) &addr, (socklen_t)(sizeof addr));
126 static int api_ws_socket_inet(const char *path, int server)
129 const char *service, *host, *api;
130 struct addrinfo hint, *rai, *iai;
133 api = strrchr(path, '/');
134 service = strrchr(path, ':');
135 if (api == NULL || service == NULL || api < service) {
139 host = strndupa(path, service++ - path);
140 service = strndupa(service, api - service);
143 memset(&hint, 0, sizeof hint);
144 hint.ai_family = AF_INET;
145 hint.ai_socktype = SOCK_STREAM;
146 rc = getaddrinfo(host, service, &hint, &rai);
154 while (iai != NULL) {
155 fd = socket(iai->ai_family, iai->ai_socktype, iai->ai_protocol);
158 rc = bind(fd, iai->ai_addr, iai->ai_addrlen);
160 rc = connect(fd, iai->ai_addr, iai->ai_addrlen);
174 static int api_ws_socket(const char *path, int server)
178 /* check for systemd socket */
179 if (0 == strncmp(path, "sd:", 3))
180 fd = sd_fds_for(path + 3);
182 /* check for unix socket */
183 if (0 == strncmp(path, "unix:", 5))
185 fd = api_ws_socket_unix(path + 5, server);
188 fd = api_ws_socket_inet(path, server);
190 if (fd >= 0 && server) {
192 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &rc, sizeof rc);
196 /* configure the socket */
198 fcntl(fd, F_SETFD, FD_CLOEXEC);
199 fcntl(fd, F_SETFL, O_NONBLOCK);
204 /**********************************************************************************/
206 int afb_api_ws_add_client(const char *path, struct afb_apiset *apiset, int strong)
208 struct api_ws *apiws;
209 struct afb_stub_ws *stubws;
211 /* create the ws client api */
212 apiws = api_ws_make(path);
216 /* connect to the service */
217 apiws->fd = api_ws_socket(apiws->path, 0);
219 ERROR("can't connect to ws service %s", apiws->path);
223 stubws = afb_stub_ws_create_client(apiws->fd, apiws->api, apiset);
225 ERROR("can't setup client ws service to %s", apiws->path);
228 if (afb_stub_ws_client_add(stubws, apiset) < 0) {
229 ERROR("can't add the client to the apiset for service %s", apiws->path);
235 afb_stub_ws_unref(stubws);
244 int afb_api_ws_add_client_strong(const char *path, struct afb_apiset *apiset)
246 return afb_api_ws_add_client(path, apiset, 1);
249 int afb_api_ws_add_client_weak(const char *path, struct afb_apiset *apiset)
251 return afb_api_ws_add_client(path, apiset, 0);
254 static int api_ws_server_accept_client(struct api_ws *apiws, int fd)
256 return -!afb_stub_ws_create_server(fd, apiws->api, apiws->apiset);
259 static void api_ws_server_accept(struct api_ws *apiws)
262 struct sockaddr addr;
265 lenaddr = (socklen_t)sizeof addr;
266 fd = accept(apiws->fd, &addr, &lenaddr);
268 rc = api_ws_server_accept_client(apiws, fd);
275 static int api_ws_server_connect(struct api_ws *apiws);
277 static int api_ws_server_listen_callback(sd_event_source *src, int fd, uint32_t revents, void *closure)
279 struct api_ws *apiws = closure;
281 if ((revents & EPOLLIN) != 0)
282 api_ws_server_accept(apiws);
283 if ((revents & EPOLLHUP) != 0)
284 api_ws_server_connect(apiws);
288 static void api_ws_server_disconnect(struct api_ws *apiws)
290 if (apiws->listensrc != NULL) {
291 sd_event_source_unref(apiws->listensrc);
292 apiws->listensrc = NULL;
294 if (apiws->fd >= 0) {
300 static int api_ws_server_connect(struct api_ws *apiws)
304 /* ensure disconnected */
305 api_ws_server_disconnect(apiws);
307 /* request the service object name */
308 apiws->fd = api_ws_socket(apiws->path, 1);
310 ERROR("can't create socket %s", apiws->path);
312 /* listen for service */
313 rc = sd_event_add_io(afb_common_get_event_loop(),
314 &apiws->listensrc, apiws->fd, EPOLLIN,
315 api_ws_server_listen_callback, apiws);
320 ERROR("can't add ws object %s", apiws->path);
325 /* create the service */
326 int afb_api_ws_add_server(const char *path, struct afb_apiset *apiset)
329 struct api_ws *apiws;
331 /* creates the ws api object */
332 apiws = api_ws_make(path);
337 if (!afb_apiset_lookup(apiset, apiws->api, 1)) {
338 ERROR("Can't provide ws-server for %s: API %s doesn't exist", path, apiws->api);
342 /* connect for serving */
343 rc = api_ws_server_connect(apiws);
347 apiws->apiset = afb_apiset_addref(apiset);