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