afs-supervisor: Improve name of supervisor api
[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 /* supervised items */
53 struct supervised
54 {
55         /* link to the next supervised */
56         struct supervised *next;
57
58         /* credentials of the supervised */
59         struct afb_cred *cred;
60
61         /* connection with the supervised */
62         struct afb_stub_ws *stub;
63 };
64
65 /* api and apiset name */
66 static const char supervision_apiname[] = AFS_SURPERVISION_APINAME_INTERNAL;
67 static const char supervisor_apiname[] = "supervisor";
68
69 /* the main apiset */
70 struct afb_apiset *main_apiset;
71
72 /* the empty apiset */
73 static struct afb_apiset *empty_apiset;
74
75 /* supervision socket path */
76 static const char supervision_socket_path[] = AFS_SURPERVISION_SOCKET;
77
78 /* global mutex */
79 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
80
81 /* list of supervised daemons */
82 static struct supervised *superviseds;
83
84 /*************************************************************************************/
85
86 static int afb_init_supervision_api();
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_SURPERVISION_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 incomming 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                 afb_stub_ws_unref(stub);
183         }
184         pthread_mutex_unlock(&mutex);
185 }
186
187 /*
188  * create a supervised for socket 'fd' and 'cred'
189  * return 0 in case of success or -1 in case of error
190  */
191 static int make_supervised(int fd, struct afb_cred *cred)
192 {
193         struct supervised *s;
194
195         s = malloc(sizeof *s);
196         if (!s)
197                 return -1;
198
199         s->cred = cred;
200         s->stub = afb_stub_ws_create_client(fd, supervision_apiname, empty_apiset);
201         if (!s->stub) {
202                 free(s);
203                 return -1;
204         }
205         pthread_mutex_lock(&mutex);
206         s->next = superviseds;
207         superviseds = s;
208         pthread_mutex_unlock(&mutex);
209         afb_stub_ws_on_hangup(s->stub, on_supervised_hangup);
210         return 0;
211 }
212
213 /*
214  * handles incoming connection on 'sock'
215  */
216 static void accept_supervision_link(int sock)
217 {
218         int rc, fd;
219         struct sockaddr addr;
220         socklen_t lenaddr;
221         struct afb_cred *cred;
222
223         lenaddr = (socklen_t)sizeof addr;
224         fd = accept(sock, &addr, &lenaddr);
225         if (fd >= 0) {
226                 cred = afb_cred_create_for_socket(fd);
227                 rc = should_accept(cred);
228                 if (rc) {
229                         rc = send_initiator(fd, NULL);
230                         if (!rc) {
231                                 rc = make_supervised(fd, cred);
232                                 if (!rc)
233                                         return;
234                         }
235                 }
236                 afb_cred_unref(cred);
237                 close(fd);
238         }
239 }
240
241 /*
242  * handle even on server socket
243  */
244 static int listening(sd_event_source *src, int fd, uint32_t revents, void *closure)
245 {
246         if ((revents & EPOLLIN) != 0)
247                 accept_supervision_link(fd);
248         if ((revents & EPOLLHUP) != 0) {
249                 ERROR("supervision socket closed");
250                 exit(1);
251         }
252         return 0;
253 }
254
255
256 /**
257  * initalize the supervision
258  */
259 static int init(const char *spec)
260 {
261         int rc, fd;
262
263         /* check argument */
264         if (!spec) {
265                 ERROR("invalid socket spec");
266                 return -1;
267         }
268
269         rc = afb_session_init(100, 600, "");
270         /* TODO check that value */
271
272         /* create the apisets */
273         main_apiset = afb_apiset_create(supervisor_apiname, 0);
274         if (!main_apiset) {
275                 ERROR("Can't create supervisor's apiset");
276                 return -1;
277         }
278         empty_apiset = afb_apiset_create(supervision_apiname, 0);
279         if (!empty_apiset) {
280                 ERROR("Can't create supervision apiset");
281                 return -1;
282         }
283
284
285         /* init the main apiset */
286         rc = afb_init_supervision_api();
287         if (rc < 0) {
288                 ERROR("Can't create supervision's apiset: %m");
289                 return -1;
290         }
291
292         /* create the supervision socket */
293         fd = create_supervision_socket(supervision_socket_path);
294         if (fd < 0)
295                 return fd;
296
297         /* listen the socket */
298         rc = listen(fd, 5);
299         if (rc < 0) {
300                 ERROR("refused to listen on socket");
301                 return rc;
302         }
303
304         /* integrate the socket to the loop */
305         rc = sd_event_add_io(afb_common_get_event_loop(),
306                                 NULL, fd, EPOLLIN,
307                                 listening, NULL);
308         if (rc < 0) {
309                 ERROR("handling socket event isn't possible");
310                 return rc;
311         }
312
313         /* adds the server socket */
314         rc = afb_api_ws_add_server(spec, main_apiset);
315         if (rc < 0) {
316                 ERROR("can't start the server socket");
317                 return -1;
318         }
319         return 0;
320 }
321
322 /* start should not be null but */
323 static void start(int signum, void *arg)
324 {
325         char *xpath = arg;
326         int rc;
327
328         if (signum)
329                 exit(1);
330
331         rc = init(xpath);
332         if (rc)
333                 exit(1);
334
335         sd_notify(1, "READY=1");
336 }
337
338 /**
339  * initalize the supervision
340  */
341 int main(int ac, char **av)
342 {
343         verbosity = Verbosity_Level_Debug;
344         /* enter job processing */
345         jobs_start(3, 0, 10, start, av[1]);
346         WARNING("hoops returned from jobs_enter! [report bug]");
347         return 1;
348 }
349
350 /*********************************************************************************************************/
351
352 static struct afb_binding_data_v2 datav2;
353
354 static void f_list(struct afb_req req)
355 {
356         char pid[50];
357         struct json_object *resu, *item;
358         struct supervised *s;
359
360         resu = json_object_new_object();
361         s = superviseds;
362         while (s) {
363                 sprintf(pid, "%d", (int)s->cred->pid);
364                 item = NULL;
365                 wrap_json_pack(&item, "{si si si ss ss ss}",
366                                 "pid", (int)s->cred->pid,
367                                 "uid", (int)s->cred->uid,
368                                 "gid", (int)s->cred->gid,
369                                 "id", s->cred->id,
370                                 "label", s->cred->label,
371                                 "user", s->cred->user
372                                 );
373                 json_object_object_add(resu, pid, item);
374                 s = s->next;
375         }
376         afb_req_success(req, resu, NULL);
377 }
378
379 static void propagate(struct afb_req req, const char *verb)
380 {
381         struct afb_xreq *xreq;
382         struct json_object *args, *item;
383         struct supervised *s;
384         struct afb_api api;
385         int p;
386
387         xreq = xreq_from_request(req.closure);
388         args = afb_xreq_json(xreq);
389         if (!json_object_object_get_ex(args, "pid", &item)) {
390                 afb_xreq_fail(xreq, "no-pid", NULL);
391                 return;
392         }
393         errno = 0;
394         p = json_object_get_int(item);
395         if (!p && errno) {
396                 afb_xreq_fail(xreq, "bad-pid", NULL);
397                 return;
398         }
399         s = superviseds;
400         while (s && p != (int)s->cred->pid)
401                 s = s->next;
402         if (!s) {
403                 afb_req_fail(req, "unknown-pid", NULL);
404                 return;
405         }
406         json_object_object_del(args, "pid");
407         if (verb)
408                 xreq->request.verb = verb;
409         api = afb_stub_ws_client_api(s->stub);
410         api.itf->call(api.closure, xreq);
411 }
412
413 static void f_do(struct afb_req req)
414 {
415         propagate(req, NULL);
416 }
417
418 static void f_config(struct afb_req req)
419 {
420         propagate(req, NULL);
421 }
422
423 static void f_trace(struct afb_req req)
424 {
425         propagate(req, NULL);
426 }
427
428 static void f_sessions(struct afb_req req)
429 {
430         propagate(req, "slist");
431 }
432
433 static void f_session_close(struct afb_req req)
434 {
435         propagate(req, "sclose");
436 }
437
438 static void f_exit(struct afb_req req)
439 {
440         propagate(req, NULL);
441 }
442
443 static void f_debug_wait(struct afb_req req)
444 {
445         propagate(req, "wait");
446 }
447
448 static void f_debug_break(struct afb_req req)
449 {
450         propagate(req, "break");
451 }
452
453 static const struct afb_auth _afb_auths_v2_supervision[] = {
454         /* 0 */
455         {
456                 .type = afb_auth_Permission,
457                 .text = "urn:AGL:permission:#supervision:platform:access"
458         }
459 };
460
461 static const struct afb_verb_v2 _afb_verbs_v2_supervision[] = {
462     {
463         .verb = "list",
464         .callback = f_list,
465         .auth = &_afb_auths_v2_supervision[0],
466         .info = NULL,
467         .session = AFB_SESSION_NONE_V2
468     },
469     {
470         .verb = "config",
471         .callback = f_config,
472         .auth = &_afb_auths_v2_supervision[0],
473         .info = NULL,
474         .session = AFB_SESSION_NONE_V2
475     },
476     {
477         .verb = "do",
478         .callback = f_do,
479         .auth = &_afb_auths_v2_supervision[0],
480         .info = NULL,
481         .session = AFB_SESSION_NONE_V2
482     },
483     {
484         .verb = "trace",
485         .callback = f_trace,
486         .auth = &_afb_auths_v2_supervision[0],
487         .info = NULL,
488         .session = AFB_SESSION_NONE_V2
489     },
490     {
491         .verb = "sessions",
492         .callback = f_sessions,
493         .auth = &_afb_auths_v2_supervision[0],
494         .info = NULL,
495         .session = AFB_SESSION_NONE_V2
496     },
497     {
498         .verb = "session-close",
499         .callback = f_session_close,
500         .auth = &_afb_auths_v2_supervision[0],
501         .info = NULL,
502         .session = AFB_SESSION_NONE_V2
503     },
504     {
505         .verb = "exit",
506         .callback = f_exit,
507         .auth = &_afb_auths_v2_supervision[0],
508         .info = NULL,
509         .session = AFB_SESSION_NONE_V2
510     },
511     {
512         .verb = "debug-wait",
513         .callback = f_debug_wait,
514         .auth = &_afb_auths_v2_supervision[0],
515         .info = NULL,
516         .session = AFB_SESSION_NONE_V2
517     },
518     {
519         .verb = "debug-break",
520         .callback = f_debug_break,
521         .auth = &_afb_auths_v2_supervision[0],
522         .info = NULL,
523         .session = AFB_SESSION_NONE_V2
524     },
525     { .verb = NULL }
526 };
527
528 static const struct afb_binding_v2 _afb_binding_v2_supervision = {
529     .api = supervisor_apiname,
530     .specification = NULL,
531     .info = NULL,
532     .verbs = _afb_verbs_v2_supervision,
533     .preinit = NULL,
534     .init = NULL,
535     .onevent = NULL,
536     .noconcurrency = 0
537 };
538
539 static int afb_init_supervision_api()
540 {
541         return afb_api_so_v2_add_binding(&_afb_binding_v2_supervision, NULL, main_apiset, &datav2);
542 }
543