afb-systemd: Isolate systemd main entries
[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-systemd.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                 (*(int*)closure)++;
282                 kill(pid, SIGHUP);
283         }
284 }
285
286 static int discover_supervised()
287 {
288         int n = 0;
289         afs_discover("afb-daemon", discovered_cb, &n);
290         return n;
291 }
292
293 /**
294  * initalize the supervision
295  */
296 static int init(const char *spec)
297 {
298         int rc, fd;
299
300         /* check argument */
301         if (!spec) {
302                 ERROR("invalid socket spec");
303                 return -1;
304         }
305
306         rc = afb_session_init(100, 600, "");
307         /* TODO check that value */
308
309         /* create the apisets */
310         main_apiset = afb_apiset_create(supervisor_apiname, 0);
311         if (!main_apiset) {
312                 ERROR("Can't create supervisor's apiset");
313                 return -1;
314         }
315         empty_apiset = afb_apiset_create(supervision_apiname, 0);
316         if (!empty_apiset) {
317                 ERROR("Can't create supervision apiset");
318                 return -1;
319         }
320
321
322         /* init the main apiset */
323         rc = afb_init_supervision_api();
324         if (rc < 0) {
325                 ERROR("Can't create supervision's apiset: %m");
326                 return -1;
327         }
328
329         /* create the supervision socket */
330         fd = create_supervision_socket(supervision_socket_path);
331         if (fd < 0)
332                 return fd;
333
334         /* listen the socket */
335         rc = listen(fd, 5);
336         if (rc < 0) {
337                 ERROR("refused to listen on socket");
338                 return rc;
339         }
340
341         /* integrate the socket to the loop */
342         rc = sd_event_add_io(afb_systemd_get_event_loop(),
343                                 NULL, fd, EPOLLIN,
344                                 listening, NULL);
345         if (rc < 0) {
346                 ERROR("handling socket event isn't possible");
347                 return rc;
348         }
349
350         /* adds the server socket */
351         rc = afb_api_ws_add_server(spec, main_apiset);
352         if (rc < 0) {
353                 ERROR("can't start the server socket");
354                 return -1;
355         }
356         return 0;
357 }
358
359 /* start should not be null but */
360 static void start(int signum, void *arg)
361 {
362         char *xpath = arg;
363         int rc;
364
365         if (signum)
366                 exit(1);
367
368         rc = init(xpath);
369         if (rc)
370                 exit(1);
371
372         sd_notify(1, "READY=1");
373
374         discover_supervised();
375 }
376
377 /**
378  * initalize the supervision
379  */
380 int main(int ac, char **av)
381 {
382         verbosity = Verbosity_Level_Debug;
383         /* enter job processing */
384         jobs_start(3, 0, 10, start, av[1]);
385         WARNING("hoops returned from jobs_enter! [report bug]");
386         return 1;
387 }
388
389 /*********************************************************************************************************/
390
391 static struct afb_binding_data_v2 datav2;
392
393 static void f_list(struct afb_req req)
394 {
395         char pid[50];
396         struct json_object *resu, *item;
397         struct supervised *s;
398
399         resu = json_object_new_object();
400         s = superviseds;
401         while (s) {
402                 sprintf(pid, "%d", (int)s->cred->pid);
403                 item = NULL;
404                 wrap_json_pack(&item, "{si si si ss ss ss}",
405                                 "pid", (int)s->cred->pid,
406                                 "uid", (int)s->cred->uid,
407                                 "gid", (int)s->cred->gid,
408                                 "id", s->cred->id,
409                                 "label", s->cred->label,
410                                 "user", s->cred->user
411                                 );
412                 json_object_object_add(resu, pid, item);
413                 s = s->next;
414         }
415         afb_req_success(req, resu, NULL);
416 }
417
418 static void f_discover(struct afb_req req)
419 {
420         discover_supervised();
421         afb_req_success(req, NULL, NULL);
422 }
423
424 static void propagate(struct afb_req req, const char *verb)
425 {
426         struct afb_xreq *xreq;
427         struct json_object *args, *item;
428         struct supervised *s;
429         struct afb_api api;
430         int p;
431
432         xreq = xreq_from_request(req.closure);
433         args = afb_xreq_json(xreq);
434         if (!json_object_object_get_ex(args, "pid", &item)) {
435                 afb_xreq_fail(xreq, "no-pid", NULL);
436                 return;
437         }
438         errno = 0;
439         p = json_object_get_int(item);
440         if (!p && errno) {
441                 afb_xreq_fail(xreq, "bad-pid", NULL);
442                 return;
443         }
444         s = supervised_of_pid((pid_t)p);
445         if (!s) {
446                 afb_req_fail(req, "unknown-pid", NULL);
447                 return;
448         }
449         json_object_object_del(args, "pid");
450         if (verb)
451                 xreq->request.verb = verb;
452         api = afb_stub_ws_client_api(s->stub);
453         api.itf->call(api.closure, xreq);
454 }
455
456 static void f_do(struct afb_req req)
457 {
458         propagate(req, NULL);
459 }
460
461 static void f_config(struct afb_req req)
462 {
463         propagate(req, NULL);
464 }
465
466 static void f_trace(struct afb_req req)
467 {
468         propagate(req, NULL);
469 }
470
471 static void f_sessions(struct afb_req req)
472 {
473         propagate(req, "slist");
474 }
475
476 static void f_session_close(struct afb_req req)
477 {
478         propagate(req, "sclose");
479 }
480
481 static void f_exit(struct afb_req req)
482 {
483         propagate(req, NULL);
484 }
485
486 static void f_debug_wait(struct afb_req req)
487 {
488         propagate(req, "wait");
489 }
490
491 static void f_debug_break(struct afb_req req)
492 {
493         propagate(req, "break");
494 }
495
496 static const struct afb_auth _afb_auths_v2_supervision[] = {
497         /* 0 */
498         {
499                 .type = afb_auth_Permission,
500                 .text = "urn:AGL:permission:#supervision:platform:access"
501         }
502 };
503
504 static const struct afb_verb_v2 _afb_verbs_v2_supervision[] = {
505     {
506         .verb = "list",
507         .callback = f_list,
508         .auth = &_afb_auths_v2_supervision[0],
509         .info = NULL,
510         .session = AFB_SESSION_NONE_V2
511     },
512     {
513         .verb = "config",
514         .callback = f_config,
515         .auth = &_afb_auths_v2_supervision[0],
516         .info = NULL,
517         .session = AFB_SESSION_NONE_V2
518     },
519     {
520         .verb = "do",
521         .callback = f_do,
522         .auth = &_afb_auths_v2_supervision[0],
523         .info = NULL,
524         .session = AFB_SESSION_NONE_V2
525     },
526     {
527         .verb = "trace",
528         .callback = f_trace,
529         .auth = &_afb_auths_v2_supervision[0],
530         .info = NULL,
531         .session = AFB_SESSION_NONE_V2
532     },
533     {
534         .verb = "sessions",
535         .callback = f_sessions,
536         .auth = &_afb_auths_v2_supervision[0],
537         .info = NULL,
538         .session = AFB_SESSION_NONE_V2
539     },
540     {
541         .verb = "session-close",
542         .callback = f_session_close,
543         .auth = &_afb_auths_v2_supervision[0],
544         .info = NULL,
545         .session = AFB_SESSION_NONE_V2
546     },
547     {
548         .verb = "exit",
549         .callback = f_exit,
550         .auth = &_afb_auths_v2_supervision[0],
551         .info = NULL,
552         .session = AFB_SESSION_NONE_V2
553     },
554     {
555         .verb = "debug-wait",
556         .callback = f_debug_wait,
557         .auth = &_afb_auths_v2_supervision[0],
558         .info = NULL,
559         .session = AFB_SESSION_NONE_V2
560     },
561     {
562         .verb = "debug-break",
563         .callback = f_debug_break,
564         .auth = &_afb_auths_v2_supervision[0],
565         .info = NULL,
566         .session = AFB_SESSION_NONE_V2
567     },
568     {
569         .verb = "discover",
570         .callback = f_discover,
571         .auth = &_afb_auths_v2_supervision[0],
572         .info = NULL,
573         .session = AFB_SESSION_NONE_V2
574     },
575     { .verb = NULL }
576 };
577
578 static const struct afb_binding_v2 _afb_binding_v2_supervision = {
579     .api = supervisor_apiname,
580     .specification = NULL,
581     .info = NULL,
582     .verbs = _afb_verbs_v2_supervision,
583     .preinit = NULL,
584     .init = NULL,
585     .onevent = NULL,
586     .noconcurrency = 0
587 };
588
589 static int afb_init_supervision_api()
590 {
591         return afb_api_so_v2_add_binding(&_afb_binding_v2_supervision, NULL, main_apiset, &datav2);
592 }
593