afs-supervisor: Add discovery mechanic
[src/app-framework-binder.git] / src / afs-supervisor.c
1 /*
2  * Copyright (C) 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
20 #include <stdio.h>
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 <systemd/sd-event.h>
32 #include <systemd/sd-daemon.h>
33
34 #include <uuid/uuid.h>
35 #include <json-c/json.h>
36 #include <afb/afb-binding-v2.h>
37
38 #include "afs-supervision.h"
39 #include "afb-common.h"
40 #include "afb-session.h"
41 #include "afb-cred.h"
42 #include "afb-stub-ws.h"
43 #include "afb-api.h"
44 #include "afb-xreq.h"
45 #include "afb-api-so-v2.h"
46 #include "afb-api-ws.h"
47 #include "afb-apiset.h"
48 #include "jobs.h"
49 #include "verbose.h"
50 #include "wrap-json.h"
51
52 extern void afs_discover(const char *pattern, void (*callback)(void *closure, pid_t pid), void *closure);
53
54 /* supervised items */
55 struct supervised
56 {
57         /* link to the next supervised */
58         struct supervised *next;
59
60         /* credentials of the supervised */
61         struct afb_cred *cred;
62
63         /* connection with the supervised */
64         struct afb_stub_ws *stub;
65 };
66
67 /* api and apiset name */
68 static const char supervision_apiname[] = AFS_SURPERVISION_APINAME_INTERNAL;
69 static const char supervisor_apiname[] = "supervisor";
70
71 /* the main apiset */
72 struct afb_apiset *main_apiset;
73
74 /* the empty apiset */
75 static struct afb_apiset *empty_apiset;
76
77 /* supervision socket path */
78 static const char supervision_socket_path[] = AFS_SURPERVISION_SOCKET;
79
80 /* global mutex */
81 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
82
83 /* list of supervised daemons */
84 static struct supervised *superviseds;
85
86 /*************************************************************************************/
87
88 static int afb_init_supervision_api();
89
90 /*************************************************************************************/
91
92 /**
93  * Creates the supervisor socket for 'path' and return it
94  * return -1 in case of failure
95  */
96 static int create_supervision_socket(const char *path)
97 {
98         int fd, rc;
99         struct sockaddr_un addr;
100         size_t length;
101
102         /* check the path's length */
103         length = strlen(path);
104         if (length >= 108) {
105                 ERROR("Path name of supervision socket too long: %d", (int)length);
106                 errno = ENAMETOOLONG;
107                 return -1;
108         }
109
110         /* create a socket */
111         fd = socket(AF_UNIX, SOCK_STREAM, 0);
112         if (fd < 0) {
113                 ERROR("Can't create socket: %m");
114                 return fd;
115         }
116
117         /* setup the bind to a path */
118         memset(&addr, 0, sizeof addr);
119         addr.sun_family = AF_UNIX;
120         strcpy(addr.sun_path, path);
121         if (addr.sun_path[0] == '@')
122                 addr.sun_path[0] = 0; /* abstract sockets */
123         else
124                 unlink(path);
125
126         /* binds the socket to the path */
127         rc = bind(fd, (struct sockaddr *) &addr, (socklen_t)(sizeof addr));
128         if (rc < 0) {
129                 ERROR("can't bind socket to %s", path);
130                 close(fd);
131                 return rc;
132         }
133         return fd;
134 }
135
136 /**
137  * send on 'fd' an initiator with 'command'
138  * return 0 on success or -1 on failure
139  */
140 static int send_initiator(int fd, const char *command)
141 {
142         int rc;
143         ssize_t swr;
144         struct afs_supervision_initiator asi;
145
146         /* set  */
147         memset(&asi, 0, sizeof asi);
148         strcpy(asi.interface, AFS_SURPERVISION_INTERFACE_1);
149         if (command)
150                 strncpy(asi.extra, command, sizeof asi.extra - 1);
151
152         /* send the initiator */
153         swr = write(fd, &asi, sizeof asi);
154         if (swr < 0) {
155                 ERROR("Can't send initiator: %m");
156                 rc = -1;
157         } else if (swr < sizeof asi) {
158                 ERROR("Sending incomplete initiator: %m");
159                 rc = -1;
160         } else
161                 rc = 0;
162         return rc;
163 }
164
165 /*
166  * checks whether the incomming supervised represented by its creds
167  * is to be accepted or not.
168  * return 1 if yes or 0 otherwise.
169  */
170 static int should_accept(struct afb_cred *cred)
171 {
172         return cred && cred->pid != getpid(); /* not me! */
173 }
174
175 static void on_supervised_hangup(struct afb_stub_ws *stub)
176 {
177         struct supervised *s, **ps;
178         pthread_mutex_lock(&mutex);
179         ps = &superviseds;
180         while ((s = *ps) && s->stub != stub)
181                 ps = &s->next;
182         if (s) {
183                 *ps = s->next;
184                 afb_stub_ws_unref(stub);
185         }
186         pthread_mutex_unlock(&mutex);
187 }
188
189 /*
190  * create a supervised for socket 'fd' and 'cred'
191  * return 0 in case of success or -1 in case of error
192  */
193 static int make_supervised(int fd, struct afb_cred *cred)
194 {
195         struct supervised *s;
196
197         s = malloc(sizeof *s);
198         if (!s)
199                 return -1;
200
201         s->cred = cred;
202         s->stub = afb_stub_ws_create_client(fd, supervision_apiname, empty_apiset);
203         if (!s->stub) {
204                 free(s);
205                 return -1;
206         }
207         pthread_mutex_lock(&mutex);
208         s->next = superviseds;
209         superviseds = s;
210         pthread_mutex_unlock(&mutex);
211         afb_stub_ws_on_hangup(s->stub, on_supervised_hangup);
212         return 0;
213 }
214
215 /**
216  * Search the supervised of 'pid', return it or NULL.
217  */
218 static struct supervised *supervised_of_pid(pid_t pid)
219 {
220         struct supervised *s;
221
222         pthread_mutex_lock(&mutex);
223         s = superviseds;
224         while (s && pid != s->cred->pid)
225                 s = s->next;
226         pthread_mutex_unlock(&mutex);
227
228         return s;
229 }
230
231 /*
232  * handles incoming connection on 'sock'
233  */
234 static void accept_supervision_link(int sock)
235 {
236         int rc, fd;
237         struct sockaddr addr;
238         socklen_t lenaddr;
239         struct afb_cred *cred;
240
241         lenaddr = (socklen_t)sizeof addr;
242         fd = accept(sock, &addr, &lenaddr);
243         if (fd >= 0) {
244                 cred = afb_cred_create_for_socket(fd);
245                 rc = should_accept(cred);
246                 if (rc) {
247                         rc = send_initiator(fd, NULL);
248                         if (!rc) {
249                                 rc = make_supervised(fd, cred);
250                                 if (!rc)
251                                         return;
252                         }
253                 }
254                 afb_cred_unref(cred);
255                 close(fd);
256         }
257 }
258
259 /*
260  * handle even on server socket
261  */
262 static int listening(sd_event_source *src, int fd, uint32_t revents, void *closure)
263 {
264         if ((revents & EPOLLIN) != 0)
265                 accept_supervision_link(fd);
266         if ((revents & EPOLLHUP) != 0) {
267                 ERROR("supervision socket closed");
268                 exit(1);
269         }
270         return 0;
271 }
272
273 /*
274  */
275 static void discovered_cb(void *closure, pid_t pid)
276 {
277         struct supervised *s;
278
279         s = supervised_of_pid(pid);
280         if (!s)
281                 kill(pid, SIGHUP);
282 }
283
284 static void discover_supervised()
285 {
286         afs_discover("afb-daemon", discovered_cb, NULL);
287 }
288
289 /**
290  * initalize the supervision
291  */
292 static int init(const char *spec)
293 {
294         int rc, fd;
295
296         /* check argument */
297         if (!spec) {
298                 ERROR("invalid socket spec");
299                 return -1;
300         }
301
302         rc = afb_session_init(100, 600, "");
303         /* TODO check that value */
304
305         /* create the apisets */
306         main_apiset = afb_apiset_create(supervisor_apiname, 0);
307         if (!main_apiset) {
308                 ERROR("Can't create supervisor's apiset");
309                 return -1;
310         }
311         empty_apiset = afb_apiset_create(supervision_apiname, 0);
312         if (!empty_apiset) {
313                 ERROR("Can't create supervision apiset");
314                 return -1;
315         }
316
317
318         /* init the main apiset */
319         rc = afb_init_supervision_api();
320         if (rc < 0) {
321                 ERROR("Can't create supervision's apiset: %m");
322                 return -1;
323         }
324
325         /* create the supervision socket */
326         fd = create_supervision_socket(supervision_socket_path);
327         if (fd < 0)
328                 return fd;
329
330         /* listen the socket */
331         rc = listen(fd, 5);
332         if (rc < 0) {
333                 ERROR("refused to listen on socket");
334                 return rc;
335         }
336
337         /* integrate the socket to the loop */
338         rc = sd_event_add_io(afb_common_get_event_loop(),
339                                 NULL, fd, EPOLLIN,
340                                 listening, NULL);
341         if (rc < 0) {
342                 ERROR("handling socket event isn't possible");
343                 return rc;
344         }
345
346         /* adds the server socket */
347         rc = afb_api_ws_add_server(spec, main_apiset);
348         if (rc < 0) {
349                 ERROR("can't start the server socket");
350                 return -1;
351         }
352         return 0;
353 }
354
355 /* start should not be null but */
356 static void start(int signum, void *arg)
357 {
358         char *xpath = arg;
359         int rc;
360
361         if (signum)
362                 exit(1);
363
364         rc = init(xpath);
365         if (rc)
366                 exit(1);
367
368         sd_notify(1, "READY=1");
369
370         discover_supervised();
371 }
372
373 /**
374  * initalize the supervision
375  */
376 int main(int ac, char **av)
377 {
378         verbosity = Verbosity_Level_Debug;
379         /* enter job processing */
380         jobs_start(3, 0, 10, start, av[1]);
381         WARNING("hoops returned from jobs_enter! [report bug]");
382         return 1;
383 }
384
385 /*********************************************************************************************************/
386
387 static struct afb_binding_data_v2 datav2;
388
389 static void f_list(struct afb_req req)
390 {
391         char pid[50];
392         struct json_object *resu, *item;
393         struct supervised *s;
394
395         resu = json_object_new_object();
396         s = superviseds;
397         while (s) {
398                 sprintf(pid, "%d", (int)s->cred->pid);
399                 item = NULL;
400                 wrap_json_pack(&item, "{si si si ss ss ss}",
401                                 "pid", (int)s->cred->pid,
402                                 "uid", (int)s->cred->uid,
403                                 "gid", (int)s->cred->gid,
404                                 "id", s->cred->id,
405                                 "label", s->cred->label,
406                                 "user", s->cred->user
407                                 );
408                 json_object_object_add(resu, pid, item);
409                 s = s->next;
410         }
411         afb_req_success(req, resu, NULL);
412 }
413
414 static void propagate(struct afb_req req, const char *verb)
415 {
416         struct afb_xreq *xreq;
417         struct json_object *args, *item;
418         struct supervised *s;
419         struct afb_api api;
420         int p;
421
422         xreq = xreq_from_request(req.closure);
423         args = afb_xreq_json(xreq);
424         if (!json_object_object_get_ex(args, "pid", &item)) {
425                 afb_xreq_fail(xreq, "no-pid", NULL);
426                 return;
427         }
428         errno = 0;
429         p = json_object_get_int(item);
430         if (!p && errno) {
431                 afb_xreq_fail(xreq, "bad-pid", NULL);
432                 return;
433         }
434         s = supervised_of_pid((pid_t)p);
435         if (!s) {
436                 afb_req_fail(req, "unknown-pid", NULL);
437                 return;
438         }
439         json_object_object_del(args, "pid");
440         if (verb)
441                 xreq->request.verb = verb;
442         api = afb_stub_ws_client_api(s->stub);
443         api.itf->call(api.closure, xreq);
444 }
445
446 static void f_do(struct afb_req req)
447 {
448         propagate(req, NULL);
449 }
450
451 static void f_config(struct afb_req req)
452 {
453         propagate(req, NULL);
454 }
455
456 static void f_trace(struct afb_req req)
457 {
458         propagate(req, NULL);
459 }
460
461 static void f_sessions(struct afb_req req)
462 {
463         propagate(req, "slist");
464 }
465
466 static void f_session_close(struct afb_req req)
467 {
468         propagate(req, "sclose");
469 }
470
471 static void f_exit(struct afb_req req)
472 {
473         propagate(req, NULL);
474 }
475
476 static void f_debug_wait(struct afb_req req)
477 {
478         propagate(req, "wait");
479 }
480
481 static void f_debug_break(struct afb_req req)
482 {
483         propagate(req, "break");
484 }
485
486 static const struct afb_auth _afb_auths_v2_supervision[] = {
487         /* 0 */
488         {
489                 .type = afb_auth_Permission,
490                 .text = "urn:AGL:permission:#supervision:platform:access"
491         }
492 };
493
494 static const struct afb_verb_v2 _afb_verbs_v2_supervision[] = {
495     {
496         .verb = "list",
497         .callback = f_list,
498         .auth = &_afb_auths_v2_supervision[0],
499         .info = NULL,
500         .session = AFB_SESSION_NONE_V2
501     },
502     {
503         .verb = "config",
504         .callback = f_config,
505         .auth = &_afb_auths_v2_supervision[0],
506         .info = NULL,
507         .session = AFB_SESSION_NONE_V2
508     },
509     {
510         .verb = "do",
511         .callback = f_do,
512         .auth = &_afb_auths_v2_supervision[0],
513         .info = NULL,
514         .session = AFB_SESSION_NONE_V2
515     },
516     {
517         .verb = "trace",
518         .callback = f_trace,
519         .auth = &_afb_auths_v2_supervision[0],
520         .info = NULL,
521         .session = AFB_SESSION_NONE_V2
522     },
523     {
524         .verb = "sessions",
525         .callback = f_sessions,
526         .auth = &_afb_auths_v2_supervision[0],
527         .info = NULL,
528         .session = AFB_SESSION_NONE_V2
529     },
530     {
531         .verb = "session-close",
532         .callback = f_session_close,
533         .auth = &_afb_auths_v2_supervision[0],
534         .info = NULL,
535         .session = AFB_SESSION_NONE_V2
536     },
537     {
538         .verb = "exit",
539         .callback = f_exit,
540         .auth = &_afb_auths_v2_supervision[0],
541         .info = NULL,
542         .session = AFB_SESSION_NONE_V2
543     },
544     {
545         .verb = "debug-wait",
546         .callback = f_debug_wait,
547         .auth = &_afb_auths_v2_supervision[0],
548         .info = NULL,
549         .session = AFB_SESSION_NONE_V2
550     },
551     {
552         .verb = "debug-break",
553         .callback = f_debug_break,
554         .auth = &_afb_auths_v2_supervision[0],
555         .info = NULL,
556         .session = AFB_SESSION_NONE_V2
557     },
558     { .verb = NULL }
559 };
560
561 static const struct afb_binding_v2 _afb_binding_v2_supervision = {
562     .api = supervisor_apiname,
563     .specification = NULL,
564     .info = NULL,
565     .verbs = _afb_verbs_v2_supervision,
566     .preinit = NULL,
567     .init = NULL,
568     .onevent = NULL,
569     .noconcurrency = 0
570 };
571
572 static int afb_init_supervision_api()
573 {
574         return afb_api_so_v2_add_binding(&_afb_binding_v2_supervision, NULL, main_apiset, &datav2);
575 }
576