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