afb-systemd: Isolate systemd main entries
[src/app-framework-binder.git] / src / afb-api-ws.c
1 /*
2  * Copyright (C) 2015, 2016, 2017 "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 <systemd/sd-event.h>
34 #include "afb-api.h"
35 #include "afb-apiset.h"
36 #include "afb-systemd.h"
37 #include "afb-stub-ws.h"
38 #include "verbose.h"
39 #include "sd-fds.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         int fd;                 /* file descriptor */
46         sd_event_source *listensrc; /**< systemd source for server socket */
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, 1)) {
77                 errno = EINVAL;
78                 goto error2;
79         }
80
81         api->fd = -1;
82         return api;
83
84 error2:
85         free(api);
86 error:
87         return NULL;
88 }
89
90 static int api_ws_socket_unix(const char *path, int server)
91 {
92         int fd, rc;
93         struct sockaddr_un addr;
94         size_t length;
95
96         length = strlen(path);
97         if (length >= 108) {
98                 errno = ENAMETOOLONG;
99                 return -1;
100         }
101
102         if (server && path[0] != '@')
103                 unlink(path);
104
105         fd = socket(AF_UNIX, SOCK_STREAM, 0);
106         if (fd < 0)
107                 return fd;
108
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 */
114         if (server) {
115                 rc = bind(fd, (struct sockaddr *) &addr, (socklen_t)(sizeof addr));
116         } else {
117                 rc = connect(fd, (struct sockaddr *) &addr, (socklen_t)(sizeof addr));
118         }
119         if (rc < 0) {
120                 close(fd);
121                 return rc;
122         }
123         return fd;
124 }
125
126 static int api_ws_socket_inet(const char *path, int server)
127 {
128         int rc, fd;
129         const char *service, *host, *api;
130         struct addrinfo hint, *rai, *iai;
131
132         /* scan the uri */
133         api = strrchr(path, '/');
134         service = strrchr(path, ':');
135         if (api == NULL || service == NULL || api < service) {
136                 errno = EINVAL;
137                 return -1;
138         }
139         host = strndupa(path, service++ - path);
140         service = strndupa(service, api - service);
141
142         /* get addr */
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);
147         if (rc != 0) {
148                 errno = EINVAL;
149                 return -1;
150         }
151
152         /* get the socket */
153         iai = rai;
154         while (iai != NULL) {
155                 fd = socket(iai->ai_family, iai->ai_socktype, iai->ai_protocol);
156                 if (fd >= 0) {
157                         if (server) {
158                                 rc = bind(fd, iai->ai_addr, iai->ai_addrlen);
159                         } else {
160                                 rc = connect(fd, iai->ai_addr, iai->ai_addrlen);
161                         }
162                         if (rc == 0) {
163                                 freeaddrinfo(rai);
164                                 return fd;
165                         }
166                         close(fd);
167                 }
168                 iai = iai->ai_next;
169         }
170         freeaddrinfo(rai);
171         return -1;
172 }
173
174 static int api_ws_socket(const char *path, int server)
175 {
176         int fd, rc;
177
178         /* check for systemd socket */
179         if (0 == strncmp(path, "sd:", 3))
180                 fd = sd_fds_for(path + 3);
181         else {
182                 /* check for unix socket */
183                 if (0 == strncmp(path, "unix:", 5))
184                         /* unix socket */
185                         fd = api_ws_socket_unix(path + 5, server);
186                 else
187                         /* inet socket */
188                         fd = api_ws_socket_inet(path, server);
189
190                 if (fd >= 0 && server) {
191                         rc = 1;
192                         setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &rc, sizeof rc);
193                         rc = listen(fd, 5);
194                 }
195         }
196         /* configure the socket */
197         if (fd >= 0) {
198                 fcntl(fd, F_SETFD, FD_CLOEXEC);
199                 fcntl(fd, F_SETFL, O_NONBLOCK);
200         }
201         return fd;
202 }
203
204 /**********************************************************************************/
205
206 int afb_api_ws_add_client(const char *path, struct afb_apiset *apiset, int strong)
207 {
208         struct api_ws *apiws;
209         struct afb_stub_ws *stubws;
210
211         /* create the ws client api */
212         apiws = api_ws_make(path);
213         if (apiws == NULL)
214                 goto error;
215
216         /* connect to the service */
217         apiws->fd = api_ws_socket(apiws->path, 0);
218         if (apiws->fd < 0) {
219                 ERROR("can't connect to ws service %s", apiws->path);
220                 goto error2;
221         }
222
223         stubws = afb_stub_ws_create_client(apiws->fd, apiws->api, apiset);
224         if (!stubws) {
225                 ERROR("can't setup client ws service to %s", apiws->path);
226                 goto error3;
227         }
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);
230                 goto error4;
231         }
232         free(apiws);
233         return 0;
234 error4:
235         afb_stub_ws_unref(stubws);
236 error3:
237         close(apiws->fd);
238 error2:
239         free(apiws);
240 error:
241         return -!!strong;
242 }
243
244 int afb_api_ws_add_client_strong(const char *path, struct afb_apiset *apiset)
245 {
246         return afb_api_ws_add_client(path, apiset, 1);
247 }
248
249 int afb_api_ws_add_client_weak(const char *path, struct afb_apiset *apiset)
250 {
251         return afb_api_ws_add_client(path, apiset, 0);
252 }
253
254 static int api_ws_server_accept_client(struct api_ws *apiws, int fd)
255 {
256         return -!afb_stub_ws_create_server(fd, apiws->api, apiws->apiset);
257 }
258
259 static void api_ws_server_accept(struct api_ws *apiws)
260 {
261         int rc, fd;
262         struct sockaddr addr;
263         socklen_t lenaddr;
264
265         lenaddr = (socklen_t)sizeof addr;
266         fd = accept(apiws->fd, &addr, &lenaddr);
267         if (fd >= 0) {
268                 rc = api_ws_server_accept_client(apiws, fd);
269                 if (rc >= 0)
270                         return;
271                 close(fd);
272         }
273 }
274
275 static int api_ws_server_connect(struct api_ws *apiws);
276
277 static int api_ws_server_listen_callback(sd_event_source *src, int fd, uint32_t revents, void *closure)
278 {
279         struct api_ws *apiws = closure;
280
281         if ((revents & EPOLLIN) != 0)
282                 api_ws_server_accept(apiws);
283         if ((revents & EPOLLHUP) != 0)
284                 api_ws_server_connect(apiws);
285         return 0;
286 }
287
288 static void api_ws_server_disconnect(struct api_ws *apiws)
289 {
290         if (apiws->listensrc != NULL) {
291                 sd_event_source_unref(apiws->listensrc);
292                 apiws->listensrc = NULL;
293         }
294         if (apiws->fd >= 0) {
295                 close(apiws->fd);
296                 apiws->fd = -1;
297         }
298 }
299
300 static int api_ws_server_connect(struct api_ws *apiws)
301 {
302         int rc;
303
304         /* ensure disconnected */
305         api_ws_server_disconnect(apiws);
306
307         /* request the service object name */
308         apiws->fd = api_ws_socket(apiws->path, 1);
309         if (apiws->fd < 0)
310                 ERROR("can't create socket %s", apiws->path);
311         else {
312                 /* listen for service */
313                 rc = sd_event_add_io(afb_systemd_get_event_loop(),
314                                 &apiws->listensrc, apiws->fd, EPOLLIN,
315                                 api_ws_server_listen_callback, apiws);
316                 if (rc >= 0)
317                         return 0;
318                 close(apiws->fd);
319                 errno = -rc;
320                 ERROR("can't add ws object %s", apiws->path);
321         }
322         return -1;
323 }
324
325 /* create the service */
326 int afb_api_ws_add_server(const char *path, struct afb_apiset *apiset)
327 {
328         int rc;
329         struct api_ws *apiws;
330
331         /* creates the ws api object */
332         apiws = api_ws_make(path);
333         if (apiws == NULL)
334                 goto error;
335
336         /* check api name */
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);
339                 goto error2;
340         }
341
342         /* connect for serving */
343         rc = api_ws_server_connect(apiws);
344         if (rc < 0)
345                 goto error2;
346
347         apiws->apiset = afb_apiset_addref(apiset);
348         return 0;
349
350 error2:
351         free(apiws);
352 error:
353         return -1;
354 }
355
356