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