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