afs-supervisor: Improve name of supervisor api
[src/app-framework-binder.git] / src / afb-supervision.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 #define AFB_BINDING_PRAGMA_NO_VERBOSE_MACRO
20
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 <json-c/json.h>
32 #include <afb/afb-binding-v2.h>
33
34 #include "afb-cred.h"
35 #include "afb-api.h"
36 #include "afb-apiset.h"
37 #include "afb-api-so-v2.h"
38 #include "afb-xreq.h"
39 #include "afb-trace.h"
40 #include "afb-session.h"
41 #include "afb-config.h"
42 #include "afb-supervision.h"
43 #include "afs-supervision.h"
44 #include "afb-stub-ws.h"
45 #include "afb-debug.h"
46 #include "verbose.h"
47 #include "wrap-json.h"
48
49 extern struct afb_config *main_config;
50
51 /* api and apiset name */
52 static const char supervision_apiname[] = AFS_SURPERVISION_APINAME_INTERNAL;
53
54 /* path of the supervision socket */
55 static const char supervisor_socket_path[] = AFS_SURPERVISION_SOCKET;
56
57 /* mutual exclusion */
58 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
59
60 /* the standard apiset */
61 extern struct afb_apiset *main_apiset;
62
63 /* the supervision apiset (not exported) */
64 static struct afb_apiset *supervision_apiset;
65
66 /* local api implementation */
67 static void on_supervision_call(void *closure, struct afb_xreq *xreq);
68 static struct afb_api_itf supervision_api_itf =
69 {
70         .call = on_supervision_call
71 };
72
73 /* the supervisor link */
74 static struct afb_stub_ws *supervisor;
75
76 /* the trace api */
77 static struct afb_trace *trace;
78
79 /* open the socket */
80 static int open_supervisor_socket(const char *path)
81 {
82         int fd, rc;
83         struct sockaddr_un addr;
84         size_t length;
85
86         /* check path length */
87         length = strlen(path);
88         if (length >= 108) {
89                 errno = ENAMETOOLONG;
90                 return -1;
91         }
92
93         /* create the unix socket */
94         fd = socket(AF_UNIX, SOCK_STREAM, 0);
95         if (fd < 0)
96                 return fd;
97
98         /* prepare the connection address */
99         memset(&addr, 0, sizeof addr);
100         addr.sun_family = AF_UNIX;
101         strcpy(addr.sun_path, path);
102         if (addr.sun_path[0] == '@')
103                 addr.sun_path[0] = 0; /* implement abstract sockets */
104
105         /* connect the socket */
106         rc = connect(fd, (struct sockaddr *) &addr, (socklen_t)(sizeof addr));
107         if (rc < 0) {
108                 close(fd);
109                 return rc;
110         }
111         return fd;
112 }
113
114 static void cleanup_supervisor(void *nada)
115 {
116         struct afb_trace *t = __atomic_exchange_n(&trace, NULL, __ATOMIC_RELAXED);
117         if (t)
118                 afb_trace_unref(t);
119         supervisor = NULL;
120 }
121
122 static void disconnect_supervisor()
123 {
124         struct afb_stub_ws *s = __atomic_exchange_n(&supervisor, NULL, __ATOMIC_RELAXED);
125
126         if (s)
127                 afb_stub_ws_unref(s);
128 }
129
130 /* try to connect to supervisor */
131 static void try_connect_supervisor()
132 {
133         int fd;
134         ssize_t srd;
135         struct afs_supervision_initiator initiator;
136
137         /* get the mutex */
138         pthread_mutex_lock(&mutex);
139
140         /* needs to connect? */
141         if (supervisor || !supervision_apiset)
142                 goto end;
143
144         /* check existing path */
145         if (supervisor_socket_path[0] != '@' && access(supervisor_socket_path, F_OK)) {
146                 NOTICE("Can't acces socket path %s: %m", supervisor_socket_path);
147                 goto end;
148         }
149
150         /* socket connection */
151         fd = open_supervisor_socket(supervisor_socket_path);
152         if (fd < 0) {
153                 NOTICE("Can't connect supervision socket to %s: %m", supervisor_socket_path);
154                 goto end;
155         }
156
157         /* negociation */
158         do { srd = read(fd, &initiator, sizeof initiator); } while(srd < 0 && errno == EINTR);
159         if (srd < 0) {
160                 NOTICE("Can't read supervisor %s: %m", supervisor_socket_path);
161                 goto end2;
162         }
163         if ((size_t)srd != sizeof initiator) {
164                 ERROR("When reading supervisor %s: %m", supervisor_socket_path);
165                 goto end2;
166         }
167         if (strnlen(initiator.interface, sizeof initiator.interface) == sizeof initiator.interface) {
168                 ERROR("Bad interface of supervisor %s", supervisor_socket_path);
169                 goto end2;
170         }
171         if (strcmp(initiator.interface, AFS_SURPERVISION_INTERFACE_1)) {
172                 ERROR("Unknown interface %s for supervisor %s", initiator.interface, supervisor_socket_path);
173                 goto end2;
174         }
175         if (strnlen(initiator.extra, sizeof initiator.extra) == sizeof initiator.extra) {
176                 ERROR("Bad extra of supervisor %s", supervisor_socket_path);
177                 goto end2;
178         }
179
180         /* interprets extras */
181         if (!strcmp(initiator.extra, "CLOSE")) {
182                 INFO("Supervisor asks to CLOSE");
183                 goto end2;
184         }
185         if (!strcmp(initiator.extra, "WAIT")) {
186                 afb_debug_wait("supervisor");
187         }
188         if (!strcmp(initiator.extra, "BREAK")) {
189                 afb_debug_break("supervisor");
190         }
191
192         /* make the supervisor link */
193         supervisor = afb_stub_ws_create_server(fd, supervision_apiname, supervision_apiset);
194         if (!supervisor) {
195                 ERROR("Creation of supervisor failed: %m");
196                 goto end2;
197         }
198
199         /* successful termination */
200         goto end;
201
202 end2:
203         close(fd);
204 end:
205         pthread_mutex_unlock(&mutex);
206 }
207
208 static void on_sighup(int signum)
209 {
210         try_connect_supervisor();
211 }
212
213 /**
214  * initalize the supervision
215  */
216 int afb_supervision_init()
217 {
218         int rc;
219         struct sigaction sa;
220
221         /* don't reinit */
222         if (supervision_apiset)
223                 return 0;
224
225         /* create the apiset */
226         supervision_apiset = afb_apiset_create(supervision_apiname, 0);
227         if (!supervision_apiset) {
228                 ERROR("Can't create supervision's apiset");
229                 return -1;
230         }
231
232         /* init the apiset */
233         rc = afb_apiset_add(supervision_apiset, supervision_apiname,
234                         (struct afb_api){ .itf = &supervision_api_itf, .closure = NULL});
235         if (rc < 0) {
236                 ERROR("Can't create supervision's apiset: %m");
237                 afb_apiset_unref(supervision_apiset);
238                 supervision_apiset = NULL;
239                 return rc;
240         }
241
242         /* get SIGHUP */
243         memset(&sa, 0, sizeof sa);
244         sa.sa_handler = on_sighup;
245         rc = sigaction(SIGHUP, &sa, NULL);
246         if (rc < 0)
247                 ERROR("Can't connect supervision to SIGHUP: %m");
248
249         /* connect to supervision */
250         try_connect_supervisor();
251         return 0;
252 }
253
254 /******************************************************************************
255 **** Implementation monitoring verbs
256 ******************************************************************************/
257 static void slist(void *closure, struct afb_session *session)
258 {
259         struct json_object *list = closure;
260         struct json_object *item;
261
262         wrap_json_pack(&item, "{ss}", "token", afb_session_token(session));
263         json_object_object_add(list, afb_session_uuid(session), item);
264 }
265
266 /******************************************************************************
267 **** Implementation monitoring verbs
268 ******************************************************************************/
269
270 static const char *verbs[] = {
271         "break", "config", "do", "exit", "sclose", "slist", "trace", "wait" };
272 enum  {  Break ,  Config ,  Do ,  Exit ,  Sclose ,  Slist ,  Trace ,  Wait  };
273
274
275 static void on_supervision_call(void *closure, struct afb_xreq *xreq)
276 {
277         int i, rc;
278         struct json_object *args, *drop, *add, *sub, *list;
279         const char *api, *verb, *uuid;
280         struct afb_session *session;
281         const struct afb_api *xapi;
282         struct afb_req req;
283
284         /* search the verb */
285         i = (int)(sizeof verbs / sizeof *verbs);
286         while(--i >= 0 && strcasecmp(verbs[i], xreq->request.verb));
287         if (i < 0) {
288                 afb_xreq_fail_unknown_verb(xreq);
289                 return;
290         }
291
292         /* process */
293         args = afb_xreq_json(xreq);
294         switch(i) {
295         case Exit:
296                 i = 0;
297                 if (wrap_json_unpack(args, "i", &i))
298                         wrap_json_unpack(args, "{si}", "code", &i);
299                 ERROR("existing from supervision with code %d -> %d", i, i & 127);
300                 exit(i & 127);
301                 break;
302         case Sclose:
303                 uuid = NULL;
304                 if (wrap_json_unpack(args, "s", &uuid))
305                         wrap_json_unpack(args, "{ss}", "uuid", &uuid);
306                 if (!uuid)
307                         afb_xreq_fail(xreq, "invalid", NULL);
308                 else {
309                         session = afb_session_search(uuid);
310                         if (!session)
311                                 afb_xreq_fail(xreq, "not-found", NULL);
312                         else {
313                                 afb_session_close(session);
314                                 afb_session_unref(session);
315                                 afb_session_purge();
316                                 afb_xreq_success(xreq, NULL, NULL);
317                         }
318                 }
319                 break;
320         case Slist:
321                 list = json_object_new_object();
322                 afb_session_foreach(slist, list);
323                 afb_xreq_success(xreq, list, NULL);
324                 break;
325         case Config:
326                 afb_xreq_success(xreq, afb_config_json(main_config), NULL);
327                 break;
328         case Trace:
329                 if (!trace)
330                         trace = afb_trace_create(supervision_apiname, NULL /* not bound to any session */);
331
332                 req = afb_xreq_unstore((struct afb_stored_req*)xreq);
333                 wrap_json_unpack(args, "{s?o s?o}", "add", &add, "drop", &drop);
334                 if (add) {
335                         rc = afb_trace_add(req, add, trace);
336                         if (rc)
337                                 return;
338                 }
339                 if (drop) {
340                         rc = afb_trace_drop(req, drop, trace);
341                         if (rc)
342                                 return;
343                 }
344                 afb_req_success(req, NULL, NULL);
345                 break;
346         case Do:
347                 sub = NULL;
348                 if (wrap_json_unpack(args, "{ss ss s?o*}", "api", &api, "verb", &verb, "args", &sub))
349                         afb_xreq_fail(xreq, "error", "bad request");
350                 else {
351                         xapi = afb_apiset_lookup_started(main_apiset, api, 1);
352                         if (!xapi)
353                                 afb_xreq_fail_unknown_api(xreq);
354                         else {
355                                 afb_cred_unref(xreq->cred);
356                                 xreq->cred = NULL;
357                                 xreq->request.api = api;
358                                 xreq->request.verb = verb;
359                                 xreq->json = json_object_get(sub);
360                                 xapi->itf->call(xapi->closure, xreq);
361                                 json_object_put(args);
362                         }
363                 }
364                 break;
365         case Wait:
366                 afb_req_success(req, NULL, NULL);
367                 afb_debug_wait("supervisor");
368                 break;
369         case Break:
370                 afb_req_success(req, NULL, NULL);
371                 afb_debug_break("supervisor");
372                 break;
373         }
374 }
375