Update date of copyright notices
[src/app-framework-binder.git] / src / afb-api-ws.c
1 /*
2  * Copyright (C) 2015-2018 "IoT.bzh"
3  * Author José Bollo <jose.bollo@iot.bzh>
4  *
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
8  *
9  *   http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  */
17
18 #define _GNU_SOURCE
19 #define NO_PLUGIN_VERBOSE_MACRO
20
21 #include <stdlib.h>
22 #include <string.h>
23 #include <assert.h>
24 #include <fcntl.h>
25 #include <unistd.h>
26 #include <errno.h>
27 #include <endian.h>
28 #include <netdb.h>
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <sys/un.h>
32
33 #include "afb-fdev.h"
34 #include "afb-systemd.h"
35 #include "afb-api.h"
36 #include "afb-apiset.h"
37 #include "afb-stub-ws.h"
38 #include "verbose.h"
39 #include "fdev.h"
40
41 struct api_ws
42 {
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;
47 };
48
49 /******************************************************************************/
50
51 /*
52  * create a structure api_ws not connected to the 'path'.
53  */
54 static struct api_ws *api_ws_make(const char *path)
55 {
56         struct api_ws *api;
57         size_t length;
58
59         /* allocates the structure */
60         length = strlen(path);
61         api = calloc(1, sizeof *api + 1 + length);
62         if (api == NULL) {
63                 errno = ENOMEM;
64                 goto error;
65         }
66
67         /* path is copied after the struct */
68         api->path = (char*)(api+1);
69         memcpy(api->path, path, length + 1);
70
71         /* api name is at the end of the path */
72         while (length && path[length - 1] != '/' && path[length - 1] != ':')
73                 length = length - 1;
74         api->api = &api->path[length];
75         if (api->api == NULL || !afb_api_is_valid_name(api->api, 1)) {
76                 errno = EINVAL;
77                 goto error2;
78         }
79
80         return api;
81
82 error2:
83         free(api);
84 error:
85         return NULL;
86 }
87
88 static int api_ws_socket_unix(const char *path, int server)
89 {
90         int fd, rc;
91         struct sockaddr_un addr;
92         size_t length;
93
94         length = strlen(path);
95         if (length >= 108) {
96                 errno = ENAMETOOLONG;
97                 return -1;
98         }
99
100         if (server && path[0] != '@')
101                 unlink(path);
102
103         fd = socket(AF_UNIX, SOCK_STREAM, 0);
104         if (fd < 0)
105                 return fd;
106
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 */
112         if (server) {
113                 rc = bind(fd, (struct sockaddr *) &addr, (socklen_t)(sizeof addr));
114         } else {
115                 rc = connect(fd, (struct sockaddr *) &addr, (socklen_t)(sizeof addr));
116         }
117         if (rc < 0) {
118                 close(fd);
119                 return rc;
120         }
121         return fd;
122 }
123
124 static int api_ws_socket_inet(const char *path, int server)
125 {
126         int rc, fd;
127         const char *service, *host, *api;
128         struct addrinfo hint, *rai, *iai;
129
130         /* scan the uri */
131         api = strrchr(path, '/');
132         service = strrchr(path, ':');
133         if (api == NULL || service == NULL || api < service) {
134                 errno = EINVAL;
135                 return -1;
136         }
137         host = strndupa(path, service++ - path);
138         service = strndupa(service, api - service);
139
140         /* get addr */
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);
145         if (rc != 0) {
146                 errno = EINVAL;
147                 return -1;
148         }
149
150         /* get the socket */
151         iai = rai;
152         while (iai != NULL) {
153                 fd = socket(iai->ai_family, iai->ai_socktype, iai->ai_protocol);
154                 if (fd >= 0) {
155                         if (server) {
156                                 rc = bind(fd, iai->ai_addr, iai->ai_addrlen);
157                         } else {
158                                 rc = connect(fd, iai->ai_addr, iai->ai_addrlen);
159                         }
160                         if (rc == 0) {
161                                 freeaddrinfo(rai);
162                                 return fd;
163                         }
164                         close(fd);
165                 }
166                 iai = iai->ai_next;
167         }
168         freeaddrinfo(rai);
169         return -1;
170 }
171
172 static int api_ws_socket(const char *path, int server)
173 {
174         int fd, rc;
175
176         /* check for systemd socket */
177         if (0 == strncmp(path, "sd:", 3))
178                 fd = afb_systemd_fds_for(path + 3);
179         else {
180                 /* check for unix socket */
181                 if (0 == strncmp(path, "unix:", 5))
182                         /* unix socket */
183                         fd = api_ws_socket_unix(path + 5, server);
184                 else
185                         /* inet socket */
186                         fd = api_ws_socket_inet(path, server);
187
188                 if (fd >= 0 && server) {
189                         rc = 1;
190                         setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &rc, sizeof rc);
191                         rc = listen(fd, 5);
192                 }
193         }
194         /* configure the socket */
195         if (fd >= 0) {
196                 fcntl(fd, F_SETFD, FD_CLOEXEC);
197                 fcntl(fd, F_SETFL, O_NONBLOCK);
198         }
199         return fd;
200 }
201
202 static struct fdev *api_ws_socket_fdev(const char *path, int server)
203 {
204         int fd;
205         struct fdev *fdev;
206
207         fd = api_ws_socket(path, server);
208         if (fd < 0)
209                 fdev = 0;
210         else {
211                 fdev = afb_fdev_create(fd);
212                 if (!fdev)
213                         close(fd);
214         }
215         if (!fdev)
216                 ERROR("can't make %s socket for %s", server ? "server" : "client", path);
217         return fdev;
218 }
219
220 /**********************************************************************************/
221
222 int afb_api_ws_add_client(const char *path, struct afb_apiset *apiset, int strong)
223 {
224         struct api_ws *apiws;
225         struct afb_stub_ws *stubws;
226
227         /* create the ws client api */
228         apiws = api_ws_make(path);
229         if (apiws == NULL)
230                 goto error;
231
232         /* connect to the service */
233         apiws->fdev = api_ws_socket_fdev(apiws->path, 0);
234         if (!apiws->fdev)
235                 goto error2;
236
237         stubws = afb_stub_ws_create_client(apiws->fdev, apiws->api, apiset);
238         if (!stubws) {
239                 ERROR("can't setup client ws service to %s", apiws->path);
240                 goto error3;
241         }
242         if (afb_stub_ws_client_add(stubws, apiset) < 0) {
243                 ERROR("can't add the client to the apiset for service %s", apiws->path);
244                 goto error3;
245         }
246         free(apiws);
247         return 0;
248 error3:
249         afb_stub_ws_unref(stubws);
250 error2:
251         free(apiws);
252 error:
253         return -!!strong;
254 }
255
256 int afb_api_ws_add_client_strong(const char *path, struct afb_apiset *apiset)
257 {
258         return afb_api_ws_add_client(path, apiset, 1);
259 }
260
261 int afb_api_ws_add_client_weak(const char *path, struct afb_apiset *apiset)
262 {
263         return afb_api_ws_add_client(path, apiset, 0);
264 }
265
266 static int api_ws_server_accept_client(struct api_ws *apiws, struct fdev *fdev)
267 {
268         return -!afb_stub_ws_create_server(fdev, apiws->api, apiws->apiset);
269 }
270
271 static void api_ws_server_accept(struct api_ws *apiws)
272 {
273         int rc, fd;
274         struct sockaddr addr;
275         socklen_t lenaddr;
276         struct fdev *fdev;
277
278         lenaddr = (socklen_t)sizeof addr;
279         fd = accept(fdev_fd(apiws->fdev), &addr, &lenaddr);
280         if (fd < 0) {
281                 ERROR("can't accept connection to %s: %m", apiws->path);
282         } else {
283                 fdev = afb_fdev_create(fd);
284                 if (!fdev) {
285                         ERROR("can't hold accepted connection to %s: %m", apiws->path);
286                         close(fd);
287                 } else {
288                         rc = api_ws_server_accept_client(apiws, fdev);
289                         if (rc < 0)
290                                 ERROR("can't serve accepted connection to %s: %m", apiws->path);
291                 }
292         }
293 }
294
295 static int api_ws_server_connect(struct api_ws *apiws);
296
297 static void api_ws_server_listen_callback(void *closure, uint32_t revents, struct fdev *fdev)
298 {
299         struct api_ws *apiws = closure;
300
301         if ((revents & EPOLLIN) != 0)
302                 api_ws_server_accept(apiws);
303         if ((revents & EPOLLHUP) != 0)
304                 api_ws_server_connect(apiws);
305 }
306
307 static void api_ws_server_disconnect(struct api_ws *apiws)
308 {
309         fdev_unref(apiws->fdev);
310         apiws->fdev = 0;
311 }
312
313 static int api_ws_server_connect(struct api_ws *apiws)
314 {
315         /* ensure disconnected */
316         api_ws_server_disconnect(apiws);
317
318         /* request the service object name */
319         apiws->fdev = api_ws_socket_fdev(apiws->path, 1);
320         if (!apiws->fdev)
321                 ERROR("can't create socket %s", apiws->path);
322         else {
323                 /* listen for service */
324                 fdev_set_events(apiws->fdev, EPOLLIN);
325                 fdev_set_callback(apiws->fdev, api_ws_server_listen_callback, apiws);
326                 return 0;
327         }
328         return -1;
329 }
330
331 /* create the service */
332 int afb_api_ws_add_server(const char *path, struct afb_apiset *apiset)
333 {
334         int rc;
335         struct api_ws *apiws;
336
337         /* creates the ws api object */
338         apiws = api_ws_make(path);
339         if (apiws == NULL)
340                 goto error;
341
342         /* check api name */
343         if (!afb_apiset_lookup(apiset, apiws->api, 1)) {
344                 ERROR("Can't provide ws-server for %s: API %s doesn't exist", path, apiws->api);
345                 goto error2;
346         }
347
348         /* connect for serving */
349         rc = api_ws_server_connect(apiws);
350         if (rc < 0)
351                 goto error2;
352
353         apiws->apiset = afb_apiset_addref(apiset);
354         return 0;
355
356 error2:
357         free(apiws);
358 error:
359         return -1;
360 }
361
362