Fedora 30 packaging fix issu
[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
20 #include <stdlib.h>
21 #include <string.h>
22 #include <assert.h>
23 #include <fcntl.h>
24 #include <unistd.h>
25 #include <errno.h>
26 #include <endian.h>
27 #include <netdb.h>
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <sys/un.h>
31
32 #include "afb-api.h"
33 #include "afb-apiset.h"
34 #include "afb-api-ws.h"
35 #include "afb-fdev.h"
36 #include "afb-socket.h"
37 #include "afb-stub-ws.h"
38 #include "verbose.h"
39 #include "fdev.h"
40
41 struct api_ws_server
42 {
43         struct afb_apiset *apiset;      /* the apiset for calling */
44         struct fdev *fdev;              /* fdev handler */
45         uint16_t offapi;                /* api name of the interface */
46         char uri[];                     /* the uri of the server socket */
47 };
48
49 /******************************************************************************/
50 /***       C L I E N T                                                      ***/
51 /******************************************************************************/
52
53 static struct fdev *reopen_client(void *closure)
54 {
55         const char *uri = closure;
56         return afb_socket_open_fdev(uri, 0);
57 }
58
59 int afb_api_ws_add_client(const char *uri, struct afb_apiset *declare_set, struct afb_apiset *call_set, int strong)
60 {
61         struct afb_stub_ws *stubws;
62         struct fdev *fdev;
63         const char *api;
64
65         /* check the api name */
66         api = afb_socket_api(uri);
67         if (api == NULL || !afb_api_is_valid_name(api)) {
68                 ERROR("invalid (too long) ws client uri %s", uri);
69                 errno = EINVAL;
70                 goto error;
71         }
72
73         /* open the socket */
74         fdev = afb_socket_open_fdev(uri, 0);
75         if (fdev) {
76                 /* create the client stub */
77                 stubws = afb_stub_ws_create_client(fdev, api, call_set);
78                 if (!stubws) {
79                         ERROR("can't setup client ws service to %s", uri);
80                         fdev_unref(fdev);
81                 } else {
82                         if (afb_stub_ws_client_add(stubws, declare_set) >= 0) {
83 #if 1
84                                 /* it is asserted here that uri is never released */
85                                 afb_stub_ws_client_robustify(stubws, reopen_client, (void*)uri, NULL);
86 #else
87                                 /* it is asserted here that uri is released, so use a copy */
88                                 afb_stub_ws_client_robustify(stubws, reopen_client, strdup(uri), free);
89 #endif
90                                 return 0;
91                         }
92                         ERROR("can't add the client to the apiset for service %s", uri);
93                         afb_stub_ws_unref(stubws);
94                 }
95         }
96 error:
97         return -!!strong;
98 }
99
100 int afb_api_ws_add_client_strong(const char *uri, struct afb_apiset *declare_set, struct afb_apiset *call_set)
101 {
102         return afb_api_ws_add_client(uri, declare_set, call_set, 1);
103 }
104
105 int afb_api_ws_add_client_weak(const char *uri, struct afb_apiset *declare_set, struct afb_apiset *call_set)
106 {
107         return afb_api_ws_add_client(uri, declare_set, call_set, 0);
108 }
109
110 /*****************************************************************************/
111 /***       S E R V E R                                                      ***/
112 /******************************************************************************/
113
114 static void api_ws_server_accept(struct api_ws_server *apiws)
115 {
116         int fd;
117         struct sockaddr addr;
118         socklen_t lenaddr;
119         struct fdev *fdev;
120         struct afb_stub_ws *server;
121
122         lenaddr = (socklen_t)sizeof addr;
123         fd = accept(fdev_fd(apiws->fdev), &addr, &lenaddr);
124         if (fd < 0) {
125                 ERROR("can't accept connection to %s: %m", apiws->uri);
126         } else {
127                 fdev = afb_fdev_create(fd);
128                 if (!fdev) {
129                         ERROR("can't hold accepted connection to %s: %m", apiws->uri);
130                         close(fd);
131                 } else {
132                         server = afb_stub_ws_create_server(fdev, &apiws->uri[apiws->offapi], apiws->apiset);
133                         if (!server)
134                                 ERROR("can't serve accepted connection to %s: %m", apiws->uri);
135                 }
136         }
137 }
138
139 static int api_ws_server_connect(struct api_ws_server *apiws);
140
141 static void api_ws_server_listen_callback(void *closure, uint32_t revents, struct fdev *fdev)
142 {
143         struct api_ws_server *apiws = closure;
144
145         if ((revents & EPOLLIN) != 0)
146                 api_ws_server_accept(apiws);
147         if ((revents & EPOLLHUP) != 0)
148                 api_ws_server_connect(apiws);
149 }
150
151 static void api_ws_server_disconnect(struct api_ws_server *apiws)
152 {
153         fdev_unref(apiws->fdev);
154         apiws->fdev = 0;
155 }
156
157 static int api_ws_server_connect(struct api_ws_server *apiws)
158 {
159         /* ensure disconnected */
160         api_ws_server_disconnect(apiws);
161
162         /* request the service object name */
163         apiws->fdev = afb_socket_open_fdev(apiws->uri, 1);
164         if (!apiws->fdev)
165                 ERROR("can't create socket %s", apiws->uri);
166         else {
167                 /* listen for service */
168                 fdev_set_events(apiws->fdev, EPOLLIN);
169                 fdev_set_callback(apiws->fdev, api_ws_server_listen_callback, apiws);
170                 return 0;
171         }
172         return -1;
173 }
174
175 /* create the service */
176 int afb_api_ws_add_server(const char *uri, struct afb_apiset *declare_set, struct afb_apiset *call_set)
177 {
178         int rc;
179         const char *api;
180         struct api_ws_server *apiws;
181         size_t luri, lapi, extra;
182
183         /* check the size */
184         luri = strlen(uri);
185         if (luri > 4000) {
186                 ERROR("can't create socket %s", uri);
187                 errno = E2BIG;
188                 return -1;
189         }
190
191         /* check the api name */
192         api = afb_socket_api(uri);
193         if (api == NULL || !afb_api_is_valid_name(api)) {
194                 ERROR("invalid api name in ws uri %s", uri);
195                 errno = EINVAL;
196                 goto error;
197         }
198
199         /* check api name */
200         if (!afb_apiset_lookup(call_set, api, 1)) {
201                 ERROR("Can't provide ws-server for URI %s: API %s doesn't exist", uri, api);
202                 errno = ENOENT;
203                 goto error;
204         }
205
206         /* make the structure */
207         lapi = strlen(api);
208         extra = luri == (api - uri) + lapi ? 0 : lapi + 1;
209         apiws = malloc(sizeof * apiws + 1 + luri + extra);
210         if (!apiws) {
211                 ERROR("out of memory");
212                 errno = ENOMEM;
213                 goto error;
214         }
215
216         apiws->apiset = afb_apiset_addref(call_set);
217         apiws->fdev = 0;
218         strcpy(apiws->uri, uri);
219         if (!extra)
220                 apiws->offapi = (uint16_t)(api - uri);
221         else {
222                 apiws->offapi = (uint16_t)(luri + 1);
223                 strcpy(&apiws->uri[apiws->offapi], api);
224         }
225
226         /* connect for serving */
227         rc = api_ws_server_connect(apiws);
228         if (rc >= 0)
229                 return 0;
230
231         afb_apiset_unref(apiws->apiset);
232         free(apiws);
233 error:
234         return -1;
235 }