Improves compatibility with AddressSanitizers
[src/app-framework-binder.git] / src / afb-monitor.c
1 /*
2  * Copyright (C) 2015-2020 "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 <string.h>
21
22 #include <json-c/json.h>
23
24 #define AFB_BINDING_VERSION 3
25 #define AFB_BINDING_NO_ROOT
26 #include <afb/afb-binding.h>
27
28 #include "afb-api.h"
29 #include "afb-apiset.h"
30 #include "afb-api-v3.h"
31 #include "afb-evt.h"
32 #include "afb-xreq.h"
33 #include "afb-trace.h"
34 #include "afb-session.h"
35 #include "afb-error-text.h"
36 #include "verbose.h"
37 #include "wrap-json.h"
38
39 #include "monitor-api.inc"
40
41
42 static const char _verbosity_[] = "verbosity";
43 static const char _apis_[] = "apis";
44
45 static const char _debug_[] = "debug";
46 static const char _info_[] = "info";
47 static const char _notice_[] = "notice";
48 static const char _warning_[] = "warning";
49 static const char _error_[] = "error";
50
51
52 static struct afb_apiset *target_set;
53
54 int afb_monitor_init(struct afb_apiset *declare_set, struct afb_apiset *call_set)
55 {
56         target_set = call_set;
57         return -!afb_api_v3_from_binding(&_afb_binding_monitor, declare_set, call_set);
58 }
59
60 /******************************************************************************
61 **** Monitoring verbosity
62 ******************************************************************************/
63
64 /**
65  * Translate verbosity indication to an integer value.
66  * @param v the verbosity indication
67  * @return the verbosity level (0, 1, 2 or 3) or -1 in case of error
68  */
69 static int decode_verbosity(struct json_object *v)
70 {
71         const char *s;
72         int level = -1;
73
74         if (!wrap_json_unpack(v, "i", &level)) {
75                 level = level < _VERBOSITY_(Log_Level_Error) ? _VERBOSITY_(Log_Level_Error) : level > _VERBOSITY_(Log_Level_Debug) ? _VERBOSITY_(Log_Level_Debug) : level;
76         } else if (!wrap_json_unpack(v, "s", &s)) {
77                 switch(*s&~' ') {
78                 case 'D':
79                         if (!strcasecmp(s, _debug_))
80                                 level = _VERBOSITY_(Log_Level_Debug);
81                         break;
82                 case 'I':
83                         if (!strcasecmp(s, _info_))
84                                 level = _VERBOSITY_(Log_Level_Info);
85                         break;
86                 case 'N':
87                         if (!strcasecmp(s, _notice_))
88                                 level = _VERBOSITY_(Log_Level_Notice);
89                         break;
90                 case 'W':
91                         if (!strcasecmp(s, _warning_))
92                                 level = _VERBOSITY_(Log_Level_Warning);
93                         break;
94                 case 'E':
95                         if (!strcasecmp(s, _error_))
96                                 level = _VERBOSITY_(Log_Level_Error);
97                         break;
98                 }
99         }
100         return level;
101 }
102
103 /**
104  * callback for setting verbosity on all apis
105  * @param set the apiset
106  * @param the name of the api to set
107  * @param closure the verbosity to set as an integer casted to a pointer
108  */
109 static void set_verbosity_to_all_cb(void *closure, struct afb_apiset *set, const char *name, int isalias)
110 {
111         if (!isalias)
112                 afb_apiset_set_logmask(set, name, (int)(intptr_t)closure);
113 }
114
115 /**
116  * set the verbosity 'level' of the api of 'name'
117  * @param name the api name or "*" for any api or NULL or "" for global verbosity
118  * @param level the verbosity level to set
119  */
120 static void set_verbosity_to(const char *name, int level)
121 {
122         int mask = verbosity_to_mask(level);
123         if (!name || !name[0])
124                 verbosity_set(level);
125         else if (name[0] == '*' && !name[1])
126                 afb_apiset_enum(target_set, 1, set_verbosity_to_all_cb, (void*)(intptr_t)mask);
127         else
128                 afb_apiset_set_logmask(target_set, name, mask);
129 }
130
131 /**
132  * Set verbosities accordling to specification in 'spec'
133  * @param spec specification of the verbosity to set
134  */
135 static void set_verbosity(struct json_object *spec)
136 {
137         int l;
138         struct json_object_iterator it, end;
139
140         if (json_object_is_type(spec, json_type_object)) {
141                 it = json_object_iter_begin(spec);
142                 end = json_object_iter_end(spec);
143                 while (!json_object_iter_equal(&it, &end)) {
144                         l = decode_verbosity(json_object_iter_peek_value(&it));
145                         if (l >= 0)
146                                 set_verbosity_to(json_object_iter_peek_name(&it), l);
147                         json_object_iter_next(&it);
148                 }
149         } else {
150                 l = decode_verbosity(spec);
151                 if (l >= 0) {
152                         set_verbosity_to("", l);
153                         set_verbosity_to("*", l);
154                 }
155         }
156 }
157
158 /**
159  * Translate verbosity level to a protocol indication.
160  * @param level the verbosity
161  * @return the encoded verbosity
162  */
163 static struct json_object *encode_verbosity(int level)
164 {
165         switch(_DEVERBOSITY_(level)) {
166         case Log_Level_Error:   return json_object_new_string(_error_);
167         case Log_Level_Warning: return json_object_new_string(_warning_);
168         case Log_Level_Notice:  return json_object_new_string(_notice_);
169         case Log_Level_Info:    return json_object_new_string(_info_);
170         case Log_Level_Debug:   return json_object_new_string(_debug_);
171         default: return json_object_new_int(level);
172         }
173 }
174
175 /**
176  * callback for getting verbosity of all apis
177  * @param set the apiset
178  * @param the name of the api to set
179  * @param closure the json object to build
180  */
181 static void get_verbosity_of_all_cb(void *closure, struct afb_apiset *set, const char *name, int isalias)
182 {
183         struct json_object *resu = closure;
184         int m = afb_apiset_get_logmask(set, name);
185         if (m >= 0)
186                 json_object_object_add(resu, name, encode_verbosity(verbosity_from_mask(m)));
187 }
188
189 /**
190  * get in resu the verbosity of the api of 'name'
191  * @param resu the json object to build
192  * @param name the api name or "*" for any api or NULL or "" for global verbosity
193  */
194 static void get_verbosity_of(struct json_object *resu, const char *name)
195 {
196         int m;
197         if (!name || !name[0])
198                 json_object_object_add(resu, "", encode_verbosity(verbosity_get()));
199         else if (name[0] == '*' && !name[1])
200                 afb_apiset_enum(target_set, 1, get_verbosity_of_all_cb, resu);
201         else {
202                 m = afb_apiset_get_logmask(target_set, name);
203                 if (m >= 0)
204                         json_object_object_add(resu, name, encode_verbosity(verbosity_from_mask(m)));
205         }
206 }
207
208 /**
209  * get verbosities accordling to specification in 'spec'
210  * @param resu the json object to build
211  * @param spec specification of the verbosity to set
212  */
213 static struct json_object *get_verbosity(struct json_object *spec)
214 {
215         int i, n;
216         struct json_object *resu;
217         struct json_object_iterator it, end;
218
219         resu = json_object_new_object();
220         if (json_object_is_type(spec, json_type_object)) {
221                 it = json_object_iter_begin(spec);
222                 end = json_object_iter_end(spec);
223                 while (!json_object_iter_equal(&it, &end)) {
224                         get_verbosity_of(resu, json_object_iter_peek_name(&it));
225                         json_object_iter_next(&it);
226                 }
227         } else if (json_object_is_type(spec, json_type_array)) {
228                 n = (int)json_object_array_length(spec);
229                 for (i = 0 ; i < n ; i++)
230                         get_verbosity_of(resu, json_object_get_string(json_object_array_get_idx(spec, i)));
231         } else if (json_object_is_type(spec, json_type_string)) {
232                 get_verbosity_of(resu, json_object_get_string(spec));
233         } else if (json_object_get_boolean(spec)) {
234                 get_verbosity_of(resu, "");
235                 get_verbosity_of(resu, "*");
236         }
237         return resu;
238 }
239
240 /******************************************************************************
241 **** Manage namelist of api names
242 ******************************************************************************/
243
244 struct namelist {
245         struct namelist *next;
246         json_object *data;
247         char name[];
248 };
249
250 static struct namelist *reverse_namelist(struct namelist *head)
251 {
252         struct namelist *previous, *next;
253
254         previous = NULL;
255         while(head) {
256                 next = head->next;
257                 head->next = previous;
258                 previous = head;
259                 head = next;
260         }
261         return previous;
262 }
263
264 static void add_one_name_to_namelist(struct namelist **head, const char *name, struct json_object *data)
265 {
266         size_t length = strlen(name) + 1;
267         struct namelist *item = malloc(length + sizeof *item);
268         if (!item)
269                 ERROR("out of memory");
270         else {
271                 item->next = *head;
272                 item->data = data;
273                 memcpy(item->name, name, length);
274                 *head = item;
275         }
276 }
277
278 static void get_apis_namelist_of_all_cb(void *closure, struct afb_apiset *set, const char *name, int isalias)
279 {
280         struct namelist **head = closure;
281         add_one_name_to_namelist(head, name, NULL);
282 }
283
284 /**
285  * get apis names as a list accordling to specification in 'spec'
286  * @param spec specification of the apis to get
287  */
288 static struct namelist *get_apis_namelist(struct json_object *spec)
289 {
290         int i, n;
291         struct json_object_iterator it, end;
292         struct namelist *head;
293
294         head = NULL;
295         if (json_object_is_type(spec, json_type_object)) {
296                 it = json_object_iter_begin(spec);
297                 end = json_object_iter_end(spec);
298                 while (!json_object_iter_equal(&it, &end)) {
299                         add_one_name_to_namelist(&head,
300                                                  json_object_iter_peek_name(&it),
301                                                  json_object_iter_peek_value(&it));
302                         json_object_iter_next(&it);
303                 }
304         } else if (json_object_is_type(spec, json_type_array)) {
305                 n = (int)json_object_array_length(spec);
306                 for (i = 0 ; i < n ; i++)
307                         add_one_name_to_namelist(&head,
308                                                  json_object_get_string(
309                                                          json_object_array_get_idx(spec, i)),
310                                                  NULL);
311         } else if (json_object_is_type(spec, json_type_string)) {
312                 add_one_name_to_namelist(&head, json_object_get_string(spec), NULL);
313         } else if (json_object_get_boolean(spec)) {
314                 afb_apiset_enum(target_set, 1, get_apis_namelist_of_all_cb, &head);
315         }
316         return reverse_namelist(head);
317 }
318
319 /******************************************************************************
320 **** Monitoring apis
321 ******************************************************************************/
322
323 struct desc_apis {
324         struct namelist *names;
325         struct json_object *resu;
326         struct json_object *apis;
327         afb_req_t req;
328 };
329
330 static void describe_first_api(struct desc_apis *desc);
331
332 static void on_api_description(void *closure, struct json_object *apidesc)
333 {
334         struct desc_apis *desc = closure;
335         struct namelist *head = desc->names;
336
337         if (apidesc || afb_apiset_lookup(target_set, head->name, 1))
338                 json_object_object_add(desc->apis, head->name, apidesc);
339         desc->names = head->next;
340         free(head);
341         describe_first_api(desc);
342 }
343
344 static void describe_first_api(struct desc_apis *desc)
345 {
346         struct namelist *head = desc->names;
347         if (head)
348                 afb_apiset_describe(target_set, head->name, on_api_description, desc);
349         else {
350                 afb_req_success(desc->req, desc->resu, NULL);
351                 afb_req_unref(desc->req);
352                 free(desc);
353         }
354 }
355
356 static void describe_apis(afb_req_t req, struct json_object *resu, struct json_object *spec)
357 {
358         struct desc_apis *desc;
359
360         desc = malloc(sizeof *desc);
361         if (!desc)
362                 afb_req_fail(req, "out-of-memory", NULL);
363         else {
364                 desc->req = afb_req_addref(req);
365                 desc->resu = resu;
366                 desc->apis = json_object_new_object();
367                 json_object_object_add(desc->resu, _apis_, desc->apis);
368                 desc->names = get_apis_namelist(spec);
369                 describe_first_api(desc);
370         }
371 }
372
373 /******************************************************************************
374 **** Implementation monitoring verbs
375 ******************************************************************************/
376
377 static void f_get(afb_req_t req)
378 {
379         struct json_object *r;
380         struct json_object *apis = NULL;
381         struct json_object *verbosity = NULL;
382
383         wrap_json_unpack(afb_req_json(req), "{s?:o,s?:o}", _verbosity_, &verbosity, _apis_, &apis);
384         if (!verbosity && !apis)
385                 afb_req_success(req, NULL, NULL);
386         else {
387                 r = json_object_new_object();
388                 if (!r)
389                         afb_req_fail(req, "out-of-memory", NULL);
390                 else {
391                         if (verbosity) {
392                                 verbosity = get_verbosity(verbosity);
393                                 json_object_object_add(r, _verbosity_, verbosity);
394                         }
395                         if (!apis)
396                                 afb_req_success(req, r, NULL);
397                         else
398                                 describe_apis(req, r, apis);
399                 }
400         }
401 }
402
403 static void f_set(afb_req_t req)
404 {
405         struct json_object *verbosity = NULL;
406
407         wrap_json_unpack(afb_req_json(req), "{s?:o}", _verbosity_, &verbosity);
408         if (verbosity)
409                 set_verbosity(verbosity);
410
411         afb_req_success(req, NULL, NULL);
412 }
413
414 #if WITH_AFB_TRACE
415 static void *context_create(void *closure)
416 {
417         return afb_trace_create(_afb_binding_monitor.api, NULL);
418 }
419
420 static void context_destroy(void *pointer)
421 {
422         struct afb_trace *trace = pointer;
423         afb_trace_unref(trace);
424 }
425
426 static void f_trace(afb_req_t req)
427 {
428         int rc;
429         struct json_object *add = NULL;
430         struct json_object *drop = NULL;
431         struct afb_trace *trace;
432
433         trace = afb_req_context(req, 0, context_create, context_destroy, NULL);
434         wrap_json_unpack(afb_req_json(req), "{s?o s?o}", "add", &add, "drop", &drop);
435         if (add) {
436                 rc = afb_trace_add(req, add, trace);
437                 if (rc)
438                         goto end;
439         }
440         if (drop) {
441                 rc = afb_trace_drop(req, drop, trace);
442                 if (rc)
443                         goto end;
444         }
445         afb_req_success(req, NULL, NULL);
446 end:
447         afb_apiset_update_hooks(target_set, NULL);
448         afb_evt_update_hooks();
449         return;
450 }
451 #else
452 static void f_trace(afb_req_t req)
453 {
454         afb_req_reply(req, NULL, afb_error_text_not_available, NULL);
455 }
456 #endif
457
458 static void f_session(afb_req_t req)
459 {
460         struct json_object *r = NULL;
461         struct afb_xreq *xreq = xreq_from_req_x2(req);
462
463         /* check right to call it */
464         if (xreq->context.super) {
465                 afb_req_fail(req, "invalid", "reserved to direct clients");
466                 return;
467         }
468
469         /* make the result */
470         wrap_json_pack(&r, "{s:s,s:i,s:i}",
471                         "uuid", afb_session_uuid(xreq->context.session),
472                         "timeout", afb_session_timeout(xreq->context.session),
473                         "remain", afb_session_what_remains(xreq->context.session));
474         afb_req_success(req, r, NULL);
475 }
476