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