Remove uses of deprecated macros
[src/app-framework-binder.git] / src / afb-supervision.c
1 /*
2  * Copyright (C) 2016, 2017, 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 #if defined(WITH_SUPERVISION)
19
20 #define _GNU_SOURCE
21
22 #include <string.h>
23 #include <errno.h>
24 #include <signal.h>
25 #include <unistd.h>
26 #include <fcntl.h>
27 #include <pthread.h>
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <sys/un.h>
31
32 #include <json-c/json.h>
33
34 #define AFB_BINDING_VERSION 3
35 #include <afb/afb-binding.h>
36
37 #include "afb-cred.h"
38 #include "afb-api.h"
39 #include "afb-apiset.h"
40 #include "afb-api-so-v2.h"
41 #include "afb-xreq.h"
42 #include "afb-trace.h"
43 #include "afb-session.h"
44 #include "afb-config.h"
45 #include "afb-supervision.h"
46 #include "afs-supervision.h"
47 #include "afb-stub-ws.h"
48 #include "afb-debug.h"
49 #include "afb-fdev.h"
50 #include "verbose.h"
51 #include "wrap-json.h"
52 #include "jobs.h"
53
54 extern struct afb_config *main_config;
55
56 /* api and apiset name */
57 static const char supervision_apiname[] = AFS_SUPERVISION_APINAME;
58 static const char supervisor_apiname[] = AFS_SUPERVISOR_APINAME;
59
60 /* path of the supervision socket */
61 static const char supervisor_socket_path[] = AFS_SUPERVISION_SOCKET;
62
63 /* mutual exclusion */
64 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
65
66 /* the standard apiset */
67 extern struct afb_apiset *main_apiset;
68
69 /* the supervision apiset (not exported) */
70 static struct afb_apiset *supervision_apiset;
71
72 /* local api implementation */
73 static void on_supervision_call(void *closure, struct afb_xreq *xreq);
74 static struct afb_api_itf supervision_api_itf =
75 {
76         .call = on_supervision_call
77 };
78
79 /* the supervisor link */
80 static struct afb_stub_ws *supervisor;
81
82 /* the trace api */
83 static struct afb_trace *trace;
84
85 /* open the socket */
86 static int open_supervisor_socket(const char *path)
87 {
88         int fd, rc;
89         struct sockaddr_un addr;
90         size_t length;
91
92         /* check path length */
93         length = strlen(path);
94         if (length >= 108) {
95                 errno = ENAMETOOLONG;
96                 return -1;
97         }
98
99         /* create the unix socket */
100         fd = socket(AF_UNIX, SOCK_STREAM, 0);
101         if (fd < 0)
102                 return fd;
103
104         /* prepare the connection address */
105         memset(&addr, 0, sizeof addr);
106         addr.sun_family = AF_UNIX;
107         strcpy(addr.sun_path, path);
108         if (addr.sun_path[0] == '@')
109                 addr.sun_path[0] = 0; /* implement abstract sockets */
110
111         /* connect the socket */
112         rc = connect(fd, (struct sockaddr *) &addr, (socklen_t)(sizeof addr));
113         if (rc < 0) {
114                 close(fd);
115                 return rc;
116         }
117         return fd;
118 }
119
120 static void disconnect_supervisor()
121 {
122         struct afb_stub_ws *s;
123         struct afb_trace *t;
124
125         INFO("Disconnecting supervision");
126         s = __atomic_exchange_n(&supervisor, NULL, __ATOMIC_RELAXED);
127         t = __atomic_exchange_n(&trace, NULL, __ATOMIC_RELAXED);
128         if (s)
129                 afb_stub_ws_unref(s);
130         if (t)
131                 afb_trace_unref(t);
132 }
133
134 static void on_supervisor_hangup(struct afb_stub_ws *s)
135 {
136         if (s && s == supervisor)
137                 disconnect_supervisor();
138 }
139
140 /* try to connect to supervisor */
141 static void try_connect_supervisor()
142 {
143         int fd;
144         ssize_t srd;
145         struct afs_supervision_initiator initiator;
146         struct fdev *fdev;
147
148         /* get the mutex */
149         pthread_mutex_lock(&mutex);
150
151         /* needs to connect? */
152         if (supervisor || !supervision_apiset)
153                 goto end;
154
155         /* check existing path */
156         if (supervisor_socket_path[0] != '@' && access(supervisor_socket_path, F_OK)) {
157                 NOTICE("Can't acces socket path %s: %m", supervisor_socket_path);
158                 goto end;
159         }
160
161         /* socket connection */
162         fd = open_supervisor_socket(supervisor_socket_path);
163         if (fd < 0) {
164                 NOTICE("Can't connect supervision socket to %s: %m", supervisor_socket_path);
165                 goto end;
166         }
167
168         /* negotiation */
169         do { srd = read(fd, &initiator, sizeof initiator); } while(srd < 0 && errno == EINTR);
170         if (srd < 0) {
171                 NOTICE("Can't read supervisor %s: %m", supervisor_socket_path);
172                 goto end2;
173         }
174         if ((size_t)srd != sizeof initiator) {
175                 ERROR("When reading supervisor %s: %m", supervisor_socket_path);
176                 goto end2;
177         }
178         if (strnlen(initiator.interface, sizeof initiator.interface) == sizeof initiator.interface) {
179                 ERROR("Bad interface of supervisor %s", supervisor_socket_path);
180                 goto end2;
181         }
182         if (strcmp(initiator.interface, AFS_SUPERVISION_INTERFACE_1)) {
183                 ERROR("Unknown interface %s for supervisor %s", initiator.interface, supervisor_socket_path);
184                 goto end2;
185         }
186         if (strnlen(initiator.extra, sizeof initiator.extra) == sizeof initiator.extra) {
187                 ERROR("Bad extra of supervisor %s", supervisor_socket_path);
188                 goto end2;
189         }
190
191         /* interprets extras */
192         if (!strcmp(initiator.extra, "CLOSE")) {
193                 INFO("Supervisor asks to CLOSE");
194                 goto end2;
195         }
196         if (!strcmp(initiator.extra, "WAIT")) {
197                 afb_debug_wait("supervisor");
198         }
199         if (!strcmp(initiator.extra, "BREAK")) {
200                 afb_debug_break("supervisor");
201         }
202
203         /* make the supervisor link */
204         fdev = afb_fdev_create(fd);
205         if (!fdev) {
206                 ERROR("Creation of fdev failed: %m");
207                 goto end2;
208         }
209         supervisor = afb_stub_ws_create_server(fdev, supervision_apiname, supervision_apiset);
210         if (!supervisor) {
211                 ERROR("Creation of supervisor failed: %m");
212                 goto end;
213         }
214         afb_stub_ws_on_hangup(supervisor, on_supervisor_hangup);
215
216         /* successful termination */
217         goto end;
218
219 end2:
220         close(fd);
221 end:
222         pthread_mutex_unlock(&mutex);
223 }
224
225 static void try_connect_supervisor_job(int signum, void *args)
226 {
227         INFO("Try to connect supervisor after SIGHUP");
228         try_connect_supervisor();
229 }
230
231 static void on_sighup(int signum)
232 {
233         INFO("Supervision received a SIGHUP");
234         jobs_queue(NULL, 0, try_connect_supervisor_job, NULL);
235 }
236
237 /**
238  * initialize the supervision
239  */
240 int afb_supervision_init()
241 {
242         int rc;
243         struct sigaction sa;
244
245         /* don't reinit */
246         if (supervision_apiset)
247                 return 0;
248
249         /* create the apiset */
250         supervision_apiset = afb_apiset_create(supervision_apiname, 0);
251         if (!supervision_apiset) {
252                 ERROR("Can't create supervision's apiset");
253                 return -1;
254         }
255
256         /* init the apiset */
257         rc = afb_apiset_add(supervision_apiset, supervision_apiname,
258                         (struct afb_api_item){ .itf = &supervision_api_itf, .closure = NULL});
259         if (rc < 0) {
260                 ERROR("Can't create supervision's apiset: %m");
261                 afb_apiset_unref(supervision_apiset);
262                 supervision_apiset = NULL;
263                 return rc;
264         }
265
266         /* get SIGHUP */
267         memset(&sa, 0, sizeof sa);
268         sa.sa_handler = on_sighup;
269         rc = sigaction(SIGHUP, &sa, NULL);
270         if (rc < 0)
271                 ERROR("Can't connect supervision to SIGHUP: %m");
272
273         /* connect to supervision */
274         try_connect_supervisor();
275         return 0;
276 }
277
278 /******************************************************************************
279 **** Implementation monitoring verbs
280 ******************************************************************************/
281 static void slist(void *closure, struct afb_session *session)
282 {
283         struct json_object *list = closure;
284         struct json_object *item;
285
286         wrap_json_pack(&item, "{ss}", "token", afb_session_token(session));
287         json_object_object_add(list, afb_session_uuid(session), item);
288 }
289
290 /******************************************************************************
291 **** Implementation monitoring verbs
292 ******************************************************************************/
293
294 static const char *verbs[] = {
295         "break", "config", "do", "exit", "sclose", "slist", "trace", "wait" };
296 enum  {  Break ,  Config ,  Do ,  Exit ,  Sclose ,  Slist ,  Trace ,  Wait  };
297
298 static void on_supervision_call(void *closure, struct afb_xreq *xreq)
299 {
300         int i, rc;
301         struct json_object *args, *drop, *add, *sub, *list;
302         const char *api, *verb, *uuid;
303         struct afb_session *session;
304         const struct afb_api_item *xapi;
305         afb_req_t req;
306
307         /* search the verb */
308         i = (int)(sizeof verbs / sizeof *verbs);
309         while(--i >= 0 && strcasecmp(verbs[i], xreq->request.called_verb));
310         if (i < 0) {
311                 afb_xreq_reply_unknown_verb(xreq);
312                 return;
313         }
314
315         /* process */
316         args = afb_xreq_json(xreq);
317         switch(i) {
318         case Exit:
319                 i = 0;
320                 if (wrap_json_unpack(args, "i", &i))
321                         wrap_json_unpack(args, "{si}", "code", &i);
322                 ERROR("exiting from supervision with code %d -> %d", i, i & 127);
323                 exit(i & 127);
324                 break;
325         case Sclose:
326                 uuid = NULL;
327                 if (wrap_json_unpack(args, "s", &uuid))
328                         wrap_json_unpack(args, "{ss}", "uuid", &uuid);
329                 if (!uuid)
330                         afb_xreq_reply(xreq, NULL, "invalid", NULL);
331                 else {
332                         session = afb_session_search(uuid);
333                         if (!session)
334                                 afb_xreq_reply(xreq, NULL, "not-found", NULL);
335                         else {
336                                 afb_session_close(session);
337                                 afb_session_unref(session);
338                                 afb_session_purge();
339                                 afb_xreq_reply(xreq, NULL, NULL, NULL);
340                         }
341                 }
342                 break;
343         case Slist:
344                 list = json_object_new_object();
345                 afb_session_foreach(slist, list);
346                 afb_xreq_reply(xreq, list, NULL, NULL);
347                 break;
348         case Config:
349                 afb_xreq_reply(xreq, afb_config_json(main_config), NULL, NULL);
350                 break;
351         case Trace:
352                 if (!trace)
353                         trace = afb_trace_create(supervisor_apiname, NULL /* not bound to any session */);
354
355                 req = xreq_to_req_x2(xreq);
356                 add = drop = NULL;
357                 wrap_json_unpack(args, "{s?o s?o}", "add", &add, "drop", &drop);
358                 if (add) {
359                         rc = afb_trace_add(req, add, trace);
360                         if (rc)
361                                 return;
362                 }
363                 if (drop) {
364                         rc = afb_trace_drop(req, drop, trace);
365                         if (rc)
366                                 return;
367                 }
368                 afb_req_success(req, NULL, NULL);
369                 break;
370         case Do:
371                 sub = NULL;
372                 if (wrap_json_unpack(args, "{ss ss s?o*}", "api", &api, "verb", &verb, "args", &sub))
373                         afb_xreq_reply(xreq, NULL, "error", "bad request");
374                 else {
375                         xapi = afb_apiset_lookup_started(main_apiset, api, 1);
376                         if (!xapi)
377                                 afb_xreq_reply_unknown_api(xreq);
378                         else {
379                                 afb_cred_unref(xreq->cred);
380                                 xreq->cred = NULL;
381                                 xreq->request.called_api = api;
382                                 xreq->request.called_verb = verb;
383                                 xreq->json = json_object_get(sub);
384                                 xapi->itf->call(xapi->closure, xreq);
385                                 json_object_put(args);
386                         }
387                 }
388                 break;
389         case Wait:
390                 afb_xreq_reply(xreq, NULL, NULL, NULL);
391                 afb_debug_wait("supervisor");
392                 break;
393         case Break:
394                 afb_xreq_reply(xreq, NULL, NULL, NULL);
395                 afb_debug_break("supervisor");
396                 break;
397         }
398 }
399
400 #endif