afb-monitor: draft of monitoring
[src/app-framework-binder.git] / src / afb-monitor.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
20 #include <string.h>
21
22 #include <json-c/json.h>
23
24 #include "afb-api.h"
25 #include "afb-apiset.h"
26 #include "afb-ditf.h"
27 #include "afb-xreq.h"
28 #include "verbose.h"
29
30 extern struct afb_apiset *main_apiset;
31
32 /* CAUTION! KEEP VERBS SORTED */
33 #define VERBS  \
34         V(get) \
35         V(hook) \
36         V(set)
37
38 /**
39  * Declare functions of verbs
40  */
41 #define F(x) static void f_##x(struct afb_xreq *xreq)
42 #define V(x) F(x);
43         VERBS
44 #undef V
45
46 /**
47  * Name of the known verbs
48  */
49 static const struct {
50         const char *name;
51         void (*function)(struct afb_xreq*);
52 } verbs[] = {
53 #define V(x) { .name = #x, .function = f_##x },
54         VERBS
55 #undef V
56 };
57
58 /**
59  * get the function of a verb
60  * @param verb the name of the verb
61  * @return the function for the verb or NULL when no verb of name exists
62  */
63 static void (*lookfun(const char *verb))(struct afb_xreq*)
64 {
65         int l, u, i, c;
66
67         l = 0;
68         u = (int)(sizeof verbs / sizeof *verbs);
69         while (l < u) {
70                 i = (l + u) >> 1;
71                 c = strcmp(verb, verbs[i].name);
72                 if (c == 0)
73                         return verbs[i].function;
74                 if (c < 0)
75                         u = i;
76                 else
77                         l = i + 1;
78         }
79         return NULL;
80 }
81
82 static void call_cb(void *closure, struct afb_xreq *xreq)
83 {
84         void (*fun)(struct afb_xreq*);
85
86         fun = lookfun(xreq->verb);
87         if (!fun)
88                 afb_xreq_fail_unknown_verb(xreq);
89         else
90                 fun(xreq);
91 }
92
93 static struct afb_api_itf monitor_api_itf = {
94         .call = call_cb
95 };
96
97 int afb_monitor_init()
98 {
99         struct afb_api api;
100         api.closure = NULL;
101         api.itf = &monitor_api_itf;
102         return afb_apiset_add(main_apiset, "monitor", api);
103 }
104
105 /******************************************************************************
106 **** Monitoring verbosity
107 ******************************************************************************/
108
109 static const char _debug_[] = "debug";
110 static const char _info_[] = "info";
111 static const char _notice_[] = "notice";
112 static const char _warning_[] = "warning";
113 static const char _error_[] = "error";
114
115 /**
116  * Translate verbosity indication to an integer value.
117  * @param v the verbosity indication
118  * @return the verbosity level (0, 1, 2 or 3) or -1 in case of error
119  */
120 static int decode_verbosity(struct json_object *v)
121 {
122         const char *s;
123         int level = -1;
124         if (json_object_is_type(v, json_type_int)) {
125                 level = json_object_get_int(v);
126                 level = level < 0 ? 0 : level > 3 ? 3 : level;
127         } else if (json_object_is_type(v, json_type_string)) {
128                 s = json_object_get_string(v);
129                 switch(*s&~' ') {
130                 case 'D':
131                         if (!strcasecmp(s, _debug_))
132                                 level = 3;
133                         break;
134                 case 'I':
135                         if (!strcasecmp(s, _info_))
136                                 level = 2;
137                         break;
138                 case 'N':
139                         if (!strcasecmp(s, _notice_))
140                                 level = 1;
141                         break;
142                 case 'W':
143                         if (!strcasecmp(s, _warning_))
144                                 level = 1;
145                         break;
146                 case 'E':
147                         if (!strcasecmp(s, _error_))
148                                 level = 0;
149                         break;
150                 }
151         }
152         return level;
153 }
154
155 /**
156  * callback for setting verbosity on all apis
157  * @param set the apiset
158  * @param the name of the api to set
159  * @param closure the verbosity to set as an integer casted to a pointer
160  */
161 static void set_verbosity_to_all_cb(struct afb_apiset *set, const char *name, void *closure)
162 {
163         afb_apiset_set_verbosity(set, name, (int)(intptr_t)closure);
164 }
165
166 /**
167  * set the verbosity 'level' of the api of 'name'
168  * @param name the api name or "*" for any api or NULL or "" for global verbosity
169  * @param level the verbosity level to set
170  */
171 static void set_verbosity_to(const char *name, int level)
172 {
173         if (!name || !name[0])
174                 verbosity = level;
175         else if (name[0] == '*' && !name[1])
176                 afb_apiset_enum(main_apiset, set_verbosity_to_all_cb, (void*)(intptr_t)level);
177         else
178                 afb_apiset_set_verbosity(main_apiset, name, level);
179 }
180
181 /**
182  * Set verbosities accordling to specification in 'spec'
183  * @param spec specification of the verbosity to set
184  */
185 static void set_verbosity(struct json_object *spec)
186 {
187         int l;
188         struct json_object_iterator it, end;
189
190         if (json_object_is_type(spec, json_type_object)) {
191                 it = json_object_iter_begin(spec);
192                 end = json_object_iter_end(spec);
193                 while (!json_object_iter_equal(&it, &end)) {
194                         l = decode_verbosity(json_object_iter_peek_value(&it));
195                         if (l >= 0)
196                                 set_verbosity_to(json_object_iter_peek_name(&it), l);
197                         json_object_iter_next(&it);
198                 }
199         } else {
200                 l = decode_verbosity(spec);
201                 if (l >= 0) {
202                         set_verbosity_to("", l);
203                         set_verbosity_to("*", l);
204                 }
205         }
206 }
207
208 /**
209  * Translate verbosity level to a protocol indication.
210  * @param level the verbosity 
211  * @return the encoded verbosity
212  */
213 static struct json_object *encode_verbosity(int level)
214 {
215         switch(level) {
216         case 0: return json_object_new_string(_error_);
217         case 1: return json_object_new_string(_notice_);
218         case 2: return json_object_new_string(_info_);
219         case 3: return json_object_new_string(_debug_);
220         default: return json_object_new_int(level);
221         }
222 }
223
224 /**
225  * callback for getting verbosity of all apis
226  * @param set the apiset
227  * @param the name of the api to set
228  * @param closure the json object to build
229  */
230 static void get_verbosity_of_all_cb(struct afb_apiset *set, const char *name, void *closure)
231 {
232         struct json_object *resu = closure;
233         int l = afb_apiset_get_verbosity(set, name);
234         if (l >= 0)
235                 json_object_object_add(resu, name, encode_verbosity(l));
236 }
237
238 /**
239  * get in resu the verbosity of the api of 'name'
240  * @param resu the json object to build
241  * @param name the api name or "*" for any api or NULL or "" for global verbosity
242  */
243 static void get_verbosity_of(struct json_object *resu, const char *name)
244 {
245         int l;
246         if (!name || !name[0])
247                 json_object_object_add(resu, "", encode_verbosity(verbosity));
248         else if (name[0] == '*' && !name[1])
249                 afb_apiset_enum(main_apiset, get_verbosity_of_all_cb, resu);
250         else {
251                 l = afb_apiset_get_verbosity(main_apiset, name);
252                 if (l >= 0)
253                         json_object_object_add(resu, name, encode_verbosity(l));
254         }
255 }
256
257 /**
258  * get verbosities accordling to specification in 'spec'
259  * @param resu the json object to build
260  * @param spec specification of the verbosity to set
261  */
262 static void get_verbosity(struct json_object *resu, struct json_object *spec)
263 {
264         int i, n;
265         struct json_object_iterator it, end;
266
267         if (json_object_is_type(spec, json_type_object)) {
268                 it = json_object_iter_begin(spec);
269                 end = json_object_iter_end(spec);
270                 while (!json_object_iter_equal(&it, &end)) {
271                         get_verbosity_of(resu, json_object_iter_peek_name(&it));
272                         json_object_iter_next(&it);
273                 }
274         } else if (json_object_is_type(spec, json_type_array)) {
275                 n = json_object_array_length(spec);
276                 for (i = 0 ; i < n ; i++)
277                         get_verbosity_of(resu, json_object_get_string(json_object_array_get_idx(spec, i)));
278         } else if (json_object_get_boolean(spec)) {
279                 get_verbosity_of(resu, "");
280                 get_verbosity_of(resu, "*");
281         }
282 }
283
284 /******************************************************************************
285 **** Monitoring apis
286 ******************************************************************************/
287
288 /**
289  * get apis accordling to specification in 'spec'
290  * @param resu the json object to build
291  * @param spec specification of the verbosity to set
292  */
293 static void get_one_api(struct json_object *resu, const char *name, struct json_object *spec)
294 {
295         struct json_object *o;
296         struct afb_api api;
297         int rc;
298
299         rc = afb_apiset_lookup(main_apiset, name, &api);
300         if (!rc) {
301                 o = api.itf->describe ? api.itf->describe(api.closure) : NULL;
302                 json_object_object_add(resu, name, o);
303         }
304 }
305
306 /**
307  * callback for getting verbosity of all apis
308  * @param set the apiset
309  * @param the name of the api to set
310  * @param closure the json object to build
311  */
312 static void get_apis_of_all_cb(struct afb_apiset *set, const char *name, void *closure)
313 {
314         struct json_object *resu = closure;
315         get_one_api(resu, name, NULL);
316 }
317
318 /**
319  * get apis accordling to specification in 'spec'
320  * @param resu the json object to build
321  * @param spec specification of the verbosity to set
322  */
323 static void get_apis(struct json_object *resu, struct json_object *spec)
324 {
325         int i, n;
326         struct json_object_iterator it, end;
327
328         if (json_object_is_type(spec, json_type_object)) {
329                 it = json_object_iter_begin(spec);
330                 end = json_object_iter_end(spec);
331                 while (!json_object_iter_equal(&it, &end)) {
332                         get_one_api(resu, json_object_iter_peek_name(&it), json_object_iter_peek_value(&it));
333                         json_object_iter_next(&it);
334                 }
335         } else if (json_object_is_type(spec, json_type_array)) {
336                 n = json_object_array_length(spec);
337                 for (i = 0 ; i < n ; i++)
338                         get_one_api(resu, json_object_get_string(json_object_array_get_idx(spec, i)), NULL);
339         } else if (json_object_get_boolean(spec)) {
340                 afb_apiset_enum(main_apiset, get_apis_of_all_cb, resu);
341         }
342 }
343
344 /******************************************************************************
345 **** Implementation monitoring verbs
346 ******************************************************************************/
347
348 static const char _verbosity_[] = "verbosity";
349 static const char _apis_[] = "apis";
350
351 static void f_get(struct afb_xreq *xreq)
352 {
353         struct json_object *o, *v, *r, *x;
354
355         r = json_object_new_object();
356         o = afb_xreq_json(xreq);
357
358         if (json_object_object_get_ex(o, _verbosity_, &v)) {
359                 x = json_object_new_object();
360                 json_object_object_add(r, _verbosity_, x);
361                 get_verbosity(x, v);
362         }
363
364         if (json_object_object_get_ex(o, _apis_, &v)) {
365                 x = json_object_new_object();
366                 json_object_object_add(r, _apis_, x);
367                 get_apis(x, v);
368         }
369
370         if (!xreq->replied)
371                 afb_xreq_success(xreq, json_object_get(r), NULL);
372         json_object_put(r);
373 }
374
375 static void f_set(struct afb_xreq *xreq)
376 {
377         struct json_object *o, *v;
378
379         o = afb_xreq_json(xreq);
380         if (json_object_object_get_ex(o, _verbosity_, &v)) {
381                 set_verbosity(v);
382         }
383
384         if (!xreq->replied)
385                 afb_xreq_success(xreq, NULL, NULL);
386 }
387
388 static void f_hook(struct afb_xreq *xreq)
389 {
390         struct json_object *o, *v;
391
392         o = afb_xreq_json(xreq);
393         if (json_object_object_get_ex(o, _verbosity_, &v)) {
394                 set_verbosity(v);
395         }
396
397         if (!xreq->replied)
398                 afb_xreq_success(xreq, NULL, NULL);
399 }
400