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