2 * Copyright (C) 2015-2020 "IoT.bzh"
3 * Author José Bollo <jose.bollo@iot.bzh>
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
26 #include <sys/types.h>
27 #include <sys/socket.h>
30 #include <json-c/json.h>
32 #define AFB_BINDING_VERSION 3
33 #define AFB_BINDING_NO_ROOT
34 #include <afb/afb-binding.h>
37 #include "afb-stub-ws.h"
40 #include "afb-api-v3.h"
41 #include "afb-apiset.h"
43 #include "afb-socket.h"
47 #include "wrap-json.h"
49 #include "afs-supervision.h"
50 #include "afs-supervisor.h"
51 #include "afs-discover.h"
53 /* supervised items */
56 /* link to the next supervised */
57 struct supervised *next;
59 /* credentials of the supervised */
60 struct afb_cred *cred;
62 /* connection with the supervised */
63 struct afb_stub_ws *stub;
66 /* api and apiset name */
67 static const char supervision_apiname[] = AFS_SUPERVISION_APINAME;
68 static const char supervisor_apiname[] = AFS_SUPERVISOR_APINAME;
70 /* the empty apiset */
71 static struct afb_apiset *empty_apiset;
73 /* supervision socket path */
74 static const char supervision_socket_path[] = AFS_SUPERVISION_SOCKET;
75 static struct fdev *supervision_fdev;
78 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
80 /* list of supervised daemons */
81 static struct supervised *superviseds;
84 static afb_event_t event_add_pid;
85 static afb_event_t event_del_pid;
87 /*************************************************************************************/
90 /*************************************************************************************/
93 * send on 'fd' an initiator with 'command'
94 * return 0 on success or -1 on failure
96 static int send_initiator(int fd, const char *command)
100 struct afs_supervision_initiator asi;
103 memset(&asi, 0, sizeof asi);
104 strcpy(asi.interface, AFS_SUPERVISION_INTERFACE_1);
106 strncpy(asi.extra, command, sizeof asi.extra - 1);
108 /* send the initiator */
109 swr = write(fd, &asi, sizeof asi);
111 ERROR("Can't send initiator: %m");
113 } else if (swr < sizeof asi) {
114 ERROR("Sending incomplete initiator: %m");
122 * checks whether the incoming supervised represented by its creds
123 * is to be accepted or not.
124 * return 1 if yes or 0 otherwise.
126 static int should_accept(struct afb_cred *cred)
128 return cred && cred->pid != getpid(); /* not me! */
131 static void on_supervised_hangup(struct afb_stub_ws *stub)
133 struct supervised *s, **ps;
135 /* Search the supervised of the ws-stub */
136 pthread_mutex_lock(&mutex);
138 while ((s = *ps) && s->stub != stub)
141 /* unlink the supervised if found */
144 pthread_mutex_unlock(&mutex);
146 /* forgive the ws-stub */
147 afb_stub_ws_unref(stub);
149 /* forgive the supervised */
151 afb_event_push(event_del_pid, json_object_new_int((int)s->cred->pid));
152 afb_cred_unref(s->cred);
158 * create a supervised for socket 'fd' and 'cred'
159 * return 0 in case of success or -1 in case of error
161 static int make_supervised(int fd, struct afb_cred *cred)
163 struct supervised *s;
166 s = malloc(sizeof *s);
170 fdev = afb_fdev_create(fd);
177 s->stub = afb_stub_ws_create_client(fdev, supervision_apiname, empty_apiset);
182 pthread_mutex_lock(&mutex);
183 s->next = superviseds;
185 pthread_mutex_unlock(&mutex);
186 afb_stub_ws_set_on_hangup(s->stub, on_supervised_hangup);
191 * Search the supervised of 'pid', return it or NULL.
193 static struct supervised *supervised_of_pid(pid_t pid)
195 struct supervised *s;
197 pthread_mutex_lock(&mutex);
199 while (s && pid != s->cred->pid)
201 pthread_mutex_unlock(&mutex);
207 * handles incoming connection on 'sock'
209 static void accept_supervision_link(int sock)
212 struct sockaddr addr;
214 struct afb_cred *cred;
216 lenaddr = (socklen_t)sizeof addr;
217 fd = accept(sock, &addr, &lenaddr);
219 cred = afb_cred_create_for_socket(fd);
220 rc = should_accept(cred);
222 rc = send_initiator(fd, NULL);
224 rc = make_supervised(fd, cred);
226 afb_event_push(event_add_pid, json_object_new_int((int)cred->pid));
231 afb_cred_unref(cred);
237 * handle even on server socket
239 static void listening(void *closure, uint32_t revents, struct fdev *fdev)
241 if ((revents & EPOLLHUP) != 0) {
242 ERROR("supervision socket closed");
245 if ((revents & EPOLLIN) != 0)
246 accept_supervision_link((int)(intptr_t)closure);
251 static void discovered_cb(void *closure, pid_t pid)
253 struct supervised *s;
255 s = supervised_of_pid(pid);
262 int afs_supervisor_discover()
265 afs_discover("afb-daemon", discovered_cb, &n);
269 /*************************************************************************************/
271 static void f_subscribe(afb_req_t req)
273 struct json_object *args = afb_req_json(req);
276 revoke = json_object_is_type(args, json_type_boolean)
277 && !json_object_get_boolean(args);
281 ok = !afb_req_subscribe(req, event_add_pid)
282 && !afb_req_subscribe(req, event_del_pid);
285 afb_req_unsubscribe(req, event_add_pid);
286 afb_req_unsubscribe(req, event_del_pid);
288 afb_req_reply(req, NULL, ok ? NULL : "error", NULL);
291 static void f_list(afb_req_t req)
294 struct json_object *resu, *item;
295 struct supervised *s;
297 resu = json_object_new_object();
300 sprintf(pid, "%d", (int)s->cred->pid);
302 wrap_json_pack(&item, "{si si si ss ss ss}",
303 "pid", (int)s->cred->pid,
304 "uid", (int)s->cred->uid,
305 "gid", (int)s->cred->gid,
307 "label", s->cred->label,
308 "user", s->cred->user
310 json_object_object_add(resu, pid, item);
313 afb_req_success(req, resu, NULL);
316 static void f_discover(afb_req_t req)
318 afs_supervisor_discover();
319 afb_req_success(req, NULL, NULL);
322 static void propagate(afb_req_t req, const char *verb)
324 struct afb_xreq *xreq;
325 struct json_object *args, *item;
326 struct supervised *s;
327 struct afb_api_item api;
330 xreq = xreq_from_req_x2(req);
331 args = afb_xreq_json(xreq);
333 /* extract the pid */
334 if (!json_object_object_get_ex(args, "pid", &item)) {
335 afb_xreq_reply(xreq, NULL, "no-pid", NULL);
339 p = json_object_get_int(item);
341 afb_xreq_reply(xreq, NULL, "bad-pid", NULL);
345 /* get supervised of pid */
346 s = supervised_of_pid((pid_t)p);
348 afb_req_reply(req, NULL, "unknown-pid", NULL);
351 json_object_object_del(args, "pid");
353 /* replace the verb to call if needed */
355 xreq->request.called_verb = verb;
358 api = afb_stub_ws_client_api(s->stub);
359 api.itf->call(api.closure, xreq);
362 static void f_do(afb_req_t req)
364 propagate(req, NULL);
367 static void f_config(afb_req_t req)
369 propagate(req, NULL);
372 static void f_trace(afb_req_t req)
374 propagate(req, NULL);
377 static void f_sessions(afb_req_t req)
379 propagate(req, "slist");
382 static void f_session_close(afb_req_t req)
384 propagate(req, "sclose");
387 static void f_exit(afb_req_t req)
389 propagate(req, NULL);
390 afb_req_success(req, NULL, NULL);
393 static void f_debug_wait(afb_req_t req)
395 propagate(req, "wait");
396 afb_req_success(req, NULL, NULL);
399 static void f_debug_break(afb_req_t req)
401 propagate(req, "break");
402 afb_req_success(req, NULL, NULL);
405 /*************************************************************************************/
408 * initialize the supervisor
410 static int init_supervisor(afb_api_t api)
412 event_add_pid = afb_api_make_event(api, "add-pid");
413 if (!afb_event_is_valid(event_add_pid)) {
414 ERROR("Can't create added event");
418 event_del_pid = afb_api_make_event(api, "del-pid");
419 if (!afb_event_is_valid(event_del_pid)) {
420 ERROR("Can't create deleted event");
424 /* create an empty set for superviseds */
425 empty_apiset = afb_apiset_create(supervision_apiname, 0);
427 ERROR("Can't create supervision apiset");
431 /* create the supervision socket */
432 supervision_fdev = afb_socket_open_fdev(supervision_socket_path, 1);
433 if (!supervision_fdev)
436 fdev_set_events(supervision_fdev, EPOLLIN);
437 fdev_set_callback(supervision_fdev, listening,
438 (void*)(intptr_t)fdev_fd(supervision_fdev));
443 /*************************************************************************************/
445 static const struct afb_auth _afb_auths_v2_supervisor[] = {
448 .type = afb_auth_Permission,
449 .text = "urn:AGL:permission:#supervision:platform:access"
453 static const struct afb_verb_v3 _afb_verbs_supervisor[] = {
456 .callback = f_subscribe,
457 .auth = &_afb_auths_v2_supervisor[0],
459 .session = AFB_SESSION_CHECK_X2
464 .auth = &_afb_auths_v2_supervisor[0],
466 .session = AFB_SESSION_CHECK_X2
470 .callback = f_config,
471 .auth = &_afb_auths_v2_supervisor[0],
473 .session = AFB_SESSION_CHECK_X2
478 .auth = &_afb_auths_v2_supervisor[0],
480 .session = AFB_SESSION_CHECK_X2
485 .auth = &_afb_auths_v2_supervisor[0],
487 .session = AFB_SESSION_CHECK_X2
491 .callback = f_sessions,
492 .auth = &_afb_auths_v2_supervisor[0],
494 .session = AFB_SESSION_CHECK_X2
497 .verb = "session-close",
498 .callback = f_session_close,
499 .auth = &_afb_auths_v2_supervisor[0],
501 .session = AFB_SESSION_CHECK_X2
506 .auth = &_afb_auths_v2_supervisor[0],
508 .session = AFB_SESSION_CHECK_X2
511 .verb = "debug-wait",
512 .callback = f_debug_wait,
513 .auth = &_afb_auths_v2_supervisor[0],
515 .session = AFB_SESSION_CHECK_X2
518 .verb = "debug-break",
519 .callback = f_debug_break,
520 .auth = &_afb_auths_v2_supervisor[0],
522 .session = AFB_SESSION_CHECK_X2
526 .callback = f_discover,
527 .auth = &_afb_auths_v2_supervisor[0],
529 .session = AFB_SESSION_CHECK_X2
534 static const struct afb_binding_v3 _afb_binding_supervisor = {
535 .api = supervisor_apiname,
536 .specification = NULL,
538 .verbs = _afb_verbs_supervisor,
540 .init = init_supervisor,
545 int afs_supervisor_add(
546 struct afb_apiset *declare_set,
547 struct afb_apiset * call_set)
549 return -!afb_api_v3_from_binding(&_afb_binding_supervisor, declare_set, call_set);