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