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