2 * Copyright (C) 2015-2018 "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.
28 #include <sys/types.h>
29 #include <sys/socket.h>
33 #include "afb-systemd.h"
35 #include "afb-apiset.h"
36 #include "afb-api-ws.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 struct fdev *fdev; /* fdev handler */
46 struct afb_apiset *apiset;
49 /******************************************************************************/
52 * create a structure api_ws not connected to the 'path'.
54 static struct api_ws *api_ws_make(const char *path)
59 /* allocates the structure */
60 length = strlen(path);
61 api = calloc(1, sizeof *api + 1 + length);
67 /* path is copied after the struct */
68 api->path = (char*)(api+1);
69 memcpy(api->path, path, length + 1);
71 /* api name is at the end of the path */
72 while (length && path[length - 1] != '/' && path[length - 1] != ':')
74 api->api = &api->path[length];
75 if (api->api == NULL || !afb_api_is_valid_name(api->api)) {
88 static int api_ws_socket_unix(const char *path, int server)
91 struct sockaddr_un addr;
94 length = strlen(path);
100 if (server && path[0] != '@')
103 fd = socket(AF_UNIX, SOCK_STREAM, 0);
107 memset(&addr, 0, sizeof addr);
108 addr.sun_family = AF_UNIX;
109 strcpy(addr.sun_path, path);
110 if (addr.sun_path[0] == '@')
111 addr.sun_path[0] = 0; /* implement abstract sockets */
113 rc = bind(fd, (struct sockaddr *) &addr, (socklen_t)(sizeof addr));
115 rc = connect(fd, (struct sockaddr *) &addr, (socklen_t)(sizeof addr));
124 static int api_ws_socket_inet(const char *path, int server)
127 const char *service, *host, *api;
128 struct addrinfo hint, *rai, *iai;
131 api = strrchr(path, '/');
132 service = strrchr(path, ':');
133 if (api == NULL || service == NULL || api < service) {
137 host = strndupa(path, service++ - path);
138 service = strndupa(service, api - service);
141 memset(&hint, 0, sizeof hint);
142 hint.ai_family = AF_INET;
143 hint.ai_socktype = SOCK_STREAM;
144 rc = getaddrinfo(host, service, &hint, &rai);
152 while (iai != NULL) {
153 fd = socket(iai->ai_family, iai->ai_socktype, iai->ai_protocol);
156 rc = bind(fd, iai->ai_addr, iai->ai_addrlen);
158 rc = connect(fd, iai->ai_addr, iai->ai_addrlen);
172 static int api_ws_socket(const char *path, int server)
176 /* check for systemd socket */
177 if (0 == strncmp(path, "sd:", 3))
178 fd = afb_systemd_fds_for(path + 3);
180 /* check for unix socket */
181 if (0 == strncmp(path, "unix:", 5))
183 fd = api_ws_socket_unix(path + 5, server);
186 fd = api_ws_socket_inet(path, server);
188 if (fd >= 0 && server) {
190 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &rc, sizeof rc);
194 /* configure the socket */
196 fcntl(fd, F_SETFD, FD_CLOEXEC);
197 fcntl(fd, F_SETFL, O_NONBLOCK);
202 static struct fdev *api_ws_socket_fdev(const char *path, int server)
207 fd = api_ws_socket(path, server);
211 fdev = afb_fdev_create(fd);
216 ERROR("can't make %s socket for %s", server ? "server" : "client", path);
220 /**********************************************************************************/
222 int afb_api_ws_add_client(const char *path, struct afb_apiset *declare_set, struct afb_apiset *call_set, int strong)
224 struct api_ws *apiws;
225 struct afb_stub_ws *stubws;
227 /* create the ws client api */
228 apiws = api_ws_make(path);
232 /* connect to the service */
233 apiws->fdev = api_ws_socket_fdev(apiws->path, 0);
237 stubws = afb_stub_ws_create_client(apiws->fdev, apiws->api, call_set);
239 ERROR("can't setup client ws service to %s", apiws->path);
242 if (afb_stub_ws_client_add(stubws, declare_set) < 0) {
243 ERROR("can't add the client to the apiset for service %s", apiws->path);
249 afb_stub_ws_unref(stubws);
256 int afb_api_ws_add_client_strong(const char *path, struct afb_apiset *declare_set, struct afb_apiset *call_set)
258 return afb_api_ws_add_client(path, declare_set, call_set, 1);
261 int afb_api_ws_add_client_weak(const char *path, struct afb_apiset *declare_set, struct afb_apiset *call_set)
263 return afb_api_ws_add_client(path, declare_set, call_set, 0);
266 static int api_ws_server_accept_client(struct api_ws *apiws, struct fdev *fdev)
268 return -!afb_stub_ws_create_server(fdev, apiws->api, apiws->apiset);
271 static void api_ws_server_accept(struct api_ws *apiws)
274 struct sockaddr addr;
278 lenaddr = (socklen_t)sizeof addr;
279 fd = accept(fdev_fd(apiws->fdev), &addr, &lenaddr);
281 ERROR("can't accept connection to %s: %m", apiws->path);
283 fdev = afb_fdev_create(fd);
285 ERROR("can't hold accepted connection to %s: %m", apiws->path);
288 rc = api_ws_server_accept_client(apiws, fdev);
290 ERROR("can't serve accepted connection to %s: %m", apiws->path);
295 static int api_ws_server_connect(struct api_ws *apiws);
297 static void api_ws_server_listen_callback(void *closure, uint32_t revents, struct fdev *fdev)
299 struct api_ws *apiws = closure;
301 if ((revents & EPOLLIN) != 0)
302 api_ws_server_accept(apiws);
303 if ((revents & EPOLLHUP) != 0)
304 api_ws_server_connect(apiws);
307 static void api_ws_server_disconnect(struct api_ws *apiws)
309 fdev_unref(apiws->fdev);
313 static int api_ws_server_connect(struct api_ws *apiws)
315 /* ensure disconnected */
316 api_ws_server_disconnect(apiws);
318 /* request the service object name */
319 apiws->fdev = api_ws_socket_fdev(apiws->path, 1);
321 ERROR("can't create socket %s", apiws->path);
323 /* listen for service */
324 fdev_set_events(apiws->fdev, EPOLLIN);
325 fdev_set_callback(apiws->fdev, api_ws_server_listen_callback, apiws);
331 /* create the service */
332 int afb_api_ws_add_server(const char *path, struct afb_apiset *declare_set, struct afb_apiset *call_set)
335 struct api_ws *apiws;
337 /* creates the ws api object */
338 apiws = api_ws_make(path);
343 if (!afb_apiset_lookup(call_set, apiws->api, 1)) {
344 ERROR("Can't provide ws-server for %s: API %s doesn't exist", path, apiws->api);
348 /* connect for serving */
349 rc = api_ws_server_connect(apiws);
353 apiws->apiset = afb_apiset_addref(call_set);