hook: adding of hook feature for requests
[src/app-framework-binder.git] / src / afb-hook.c
1 /*
2  * Copyright (C) 2016 "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 <stdio.h>
21 #include <stdarg.h>
22 #include <string.h>
23
24 #include <json-c/json.h>
25
26 #include <afb/afb-req-itf.h>
27 #include <afb/afb-event-itf.h>
28
29 #include "afb-context.h"
30 #include "afb-hook.h"
31 #include "session.h"
32 #include "verbose.h"
33
34 /*
35  * Trace 
36  */
37 struct afb_hook {
38         struct afb_hook *next; /* next hook */
39         unsigned refcount; /* reference count */
40         char *api; 
41         char *verb;
42         struct AFB_clientCtx *session;
43         unsigned flags; /* hook flags */
44         struct afb_hook_req_itf *reqitf;
45         void *closure;
46 };
47
48 struct hook_req_observer {
49         struct afb_hook *hook;
50         struct hook_req_observer *next;
51 };
52
53 /*
54  * Structure recording a request to hook
55  */
56 struct afb_hook_req {
57         struct hook_req_observer *observers; /* observers */
58         struct afb_context *context; /* context of the request */
59         struct afb_req req; /* the request hookd */
60         unsigned refcount; /* reference count proxy for request */
61         char name[1]; /* hook info for the request */
62 };
63
64 /*
65  * Structure for handling subcalls callbacks
66  */
67 struct hook_subcall {
68         struct afb_hook_req *tr; /* hookd request */
69         void (*callback)(void*, int, struct json_object*); /* client callback */
70         void *cb_closure; /* cient closure */
71 };
72
73 static unsigned hook_count = 0;
74
75 static struct afb_hook *list_of_hooks = NULL;
76
77 /******************************************************************************
78  * section: default callbacks for tracing requests
79  *****************************************************************************/
80
81 static void _hook_(const struct afb_hook_req *tr, const char *format, ...)
82 {
83         int len;
84         char *buffer;
85         va_list ap;
86
87         va_start(ap, format);
88         len = vasprintf(&buffer, format, ap);
89         va_end(ap);
90
91         if (len < 0)
92                 NOTICE("tracing %s allocation error", tr->name);
93         else {
94                 NOTICE("hook %s %s", tr->name, buffer);
95                 free(buffer);
96         }
97 }
98
99 static void hook_req_begin_default_cb(void * closure, const struct afb_hook_req *tr)
100 {
101         _hook_(tr, "BEGIN");
102 }
103
104 static void hook_req_end_default_cb(void * closure, const struct afb_hook_req *tr)
105 {
106         _hook_(tr, "END");
107 }
108
109 static void hook_req_json_default_cb(void * closure, const struct afb_hook_req *tr, struct json_object *obj)
110 {
111         _hook_(tr, "json() -> %s", json_object_to_json_string(obj));
112 }
113
114 static void hook_req_get_default_cb(void * closure, const struct afb_hook_req *tr, const char *name, struct afb_arg arg)
115 {
116         _hook_(tr, "get(%s) -> { name: %s, value: %s, path: %s }", name, arg.name, arg.value, arg.path);
117 }
118
119 static void hook_req_success_default_cb(void * closure, const struct afb_hook_req *tr, struct json_object *obj, const char *info)
120 {
121         _hook_(tr, "success(%s, %s)", json_object_to_json_string(obj), info);
122 }
123
124 static void hook_req_fail_default_cb(void * closure, const struct afb_hook_req *tr, const char *status, const char *info)
125 {
126         _hook_(tr, "fail(%s, %s)", status, info);
127 }
128
129 static void hook_req_raw_default_cb(void * closure, const struct afb_hook_req *tr, const char *buffer, size_t size)
130 {
131         _hook_(tr, "raw() -> %.*s", (int)size, buffer);
132 }
133
134 static void hook_req_send_default_cb(void * closure, const struct afb_hook_req *tr, const char *buffer, size_t size)
135 {
136         _hook_(tr, "send(%.*s)", (int)size, buffer);
137 }
138
139 static void hook_req_context_get_default_cb(void * closure, const struct afb_hook_req *tr, void *value)
140 {
141         _hook_(tr, "context_get() -> %p", value);
142 }
143
144 static void hook_req_context_set_default_cb(void * closure, const struct afb_hook_req *tr, void *value, void (*free_value)(void*))
145 {
146         _hook_(tr, "context_set(%p, %p)", value, free_value);
147 }
148
149 static void hook_req_addref_default_cb(void * closure, const struct afb_hook_req *tr)
150 {
151         _hook_(tr, "addref()");
152 }
153
154 static void hook_req_unref_default_cb(void * closure, const struct afb_hook_req *tr)
155 {
156         _hook_(tr, "unref()");
157 }
158
159 static void hook_req_session_close_default_cb(void * closure, const struct afb_hook_req *tr)
160 {
161         _hook_(tr, "session_close()");
162 }
163
164 static void hook_req_session_set_LOA_default_cb(void * closure, const struct afb_hook_req *tr, unsigned level, int result)
165 {
166         _hook_(tr, "session_set_LOA(%u) -> %d", level, result);
167 }
168
169 static void hook_req_subscribe_default_cb(void * closure, const struct afb_hook_req *tr, struct afb_event event, int result)
170 {
171         _hook_(tr, "subscribe(%s:%p) -> %d", afb_event_name(event), event.closure, result);
172 }
173
174 static void hook_req_unsubscribe_default_cb(void * closure, const struct afb_hook_req *tr, struct afb_event event, int result)
175 {
176         _hook_(tr, "unsubscribe(%s:%p) -> %d", afb_event_name(event), event.closure, result);
177 }
178
179 static void hook_req_subcall_default_cb(void * closure, const struct afb_hook_req *tr, const char *api, const char *verb, struct json_object *args)
180 {
181         _hook_(tr, "subcall(%s/%s, %s) ...", api, verb, json_object_to_json_string(args));
182 }
183
184 static void hook_req_subcall_result_default_cb(void * closure, const struct afb_hook_req *tr, int status, struct json_object *result)
185 {
186         _hook_(tr, "    ...subcall... -> %d: %s", status, json_object_to_json_string(result));
187 }
188
189 static struct afb_hook_req_itf hook_req_default_itf = {
190         .hook_req_begin = hook_req_begin_default_cb,
191         .hook_req_end = hook_req_end_default_cb,
192         .hook_req_json = hook_req_json_default_cb,
193         .hook_req_get = hook_req_get_default_cb,
194         .hook_req_success = hook_req_success_default_cb,
195         .hook_req_fail = hook_req_fail_default_cb,
196         .hook_req_raw = hook_req_raw_default_cb,
197         .hook_req_send = hook_req_send_default_cb,
198         .hook_req_context_get = hook_req_context_get_default_cb,
199         .hook_req_context_set = hook_req_context_set_default_cb,
200         .hook_req_addref = hook_req_addref_default_cb,
201         .hook_req_unref = hook_req_unref_default_cb,
202         .hook_req_session_close = hook_req_session_close_default_cb,
203         .hook_req_session_set_LOA = hook_req_session_set_LOA_default_cb,
204         .hook_req_subscribe = hook_req_subscribe_default_cb,
205         .hook_req_unsubscribe = hook_req_unsubscribe_default_cb,
206         .hook_req_subcall = hook_req_subcall_default_cb,
207         .hook_req_subcall_result = hook_req_subcall_result_default_cb,
208 };
209
210 /******************************************************************************
211  * section: macro for tracing requests
212  *****************************************************************************/
213
214 #define TRACE_REQX(what,tr) do{\
215                 struct hook_req_observer *observer = tr->observers;\
216                 while (observer != NULL) {\
217                         struct afb_hook *hook = observer->hook;\
218                         observer = observer->next;\
219                         if (hook->reqitf && hook->reqitf->hook_req_##what)\
220                                 hook->reqitf->hook_req_##what(hook->closure, tr);\
221                 }\
222         }while(0)
223
224 #define TRACE_REQ_(what,tr) do{\
225                 struct hook_req_observer *observer = tr->observers;\
226                 while (observer != NULL) {\
227                         struct afb_hook *hook = observer->hook;\
228                         observer = observer->next;\
229                         if ((hook->flags & afb_hook_flag_req_##what) && hook->reqitf && hook->reqitf->hook_req_##what)\
230                                 hook->reqitf->hook_req_##what(hook->closure, tr);\
231                 }\
232         }while(0)
233
234 #define TRACE_REQ(what,tr,...) do{\
235                 struct hook_req_observer *observer = tr->observers;\
236                 while (observer != NULL) {\
237                         struct afb_hook *hook = observer->hook;\
238                         observer = observer->next;\
239                         if ((hook->flags & afb_hook_flag_req_##what) && hook->reqitf && hook->reqitf->hook_req_##what)\
240                                 hook->reqitf->hook_req_##what(hook->closure, tr, __VA_ARGS__);\
241                 }\
242         }while(0)
243
244 /******************************************************************************
245  * section: afb_hook_req handling
246  *****************************************************************************/
247
248 static void hook_req_addref(struct afb_hook_req *tr)
249 {
250         tr->refcount++;
251 }
252
253 static void hook_req_unref(struct afb_hook_req *tr)
254 {
255         struct hook_req_observer *o1, *o2;
256         if (!--tr->refcount) {
257                 TRACE_REQX(end, tr);
258                 afb_req_unref(tr->req);
259                 o1 = tr->observers;
260                 while(o1) {
261                         afb_hook_unref(o1->hook);
262                         o2 = o1->next;
263                         free(o1);
264                         o1 = o2;
265                 }
266                 free(tr);
267         }
268 }
269
270 static struct afb_hook_req *hook_req_create(struct afb_req req, struct afb_context *context, const char *api, size_t lenapi, const char *verb, size_t lenverb)
271 {
272         unsigned id;
273         struct afb_hook_req *tr;
274
275         tr = malloc(sizeof *tr + 8 + lenapi + lenverb);
276         if (tr != NULL) {
277                 /* get the call id */
278                 id = ++hook_count;
279                 if (id == 1000000)
280                         id = hook_count = 1;
281
282                 /* init hook */
283                 tr->observers = NULL;
284                 tr->refcount = 1;
285                 tr->context = context;
286                 tr->req = req;
287                 afb_req_addref(req);
288                 snprintf(tr->name, 9 + lenapi + lenverb, "%06d:%.*s/%.*s", id, (int)lenapi, api, (int)lenverb, verb);
289         }
290         return tr;
291 }
292
293 static void hook_req_add_observer(struct afb_hook_req *tr, struct afb_hook *hook)
294 {
295         struct hook_req_observer *observer;
296
297         observer = malloc(sizeof *observer);
298         if (observer) {
299                 observer->hook = afb_hook_addref(hook);
300                 observer->next = tr->observers;
301                 tr->observers = observer;
302         }
303 }
304
305 /******************************************************************************
306  * section: hooks for tracing requests
307  *****************************************************************************/
308
309 static struct json_object *req_hook_json(void *closure)
310 {
311         struct afb_hook_req *tr = closure;
312         struct json_object *r;
313
314         r = afb_req_json(tr->req);
315         TRACE_REQ(json, tr, r);
316         return r;
317 }
318
319 static struct afb_arg req_hook_get(void *closure, const char *name)
320 {
321         struct afb_hook_req *tr = closure;
322         struct afb_arg a;
323
324         a = afb_req_get(tr->req, name);
325         TRACE_REQ(get, tr, name, a);
326         return a;
327 }
328
329 static void req_hook_success(void *closure, struct json_object *obj, const char *info)
330 {
331         struct afb_hook_req *tr = closure;
332
333         TRACE_REQ(success, tr, obj, info);
334         afb_req_success(tr->req, obj, info);
335         hook_req_unref(tr);
336 }
337
338 static void req_hook_fail(void *closure, const char *status, const char *info)
339 {
340         struct afb_hook_req *tr = closure;
341
342         TRACE_REQ(fail, tr, status, info);
343         afb_req_fail(tr->req, status, info);
344         hook_req_unref(tr);
345 }
346
347 static const char *req_hook_raw(void *closure, size_t *size)
348 {
349         struct afb_hook_req *tr = closure;
350         const char *r;
351         size_t s;
352
353         r = afb_req_raw(tr->req, &s);
354         TRACE_REQ(raw, tr, r, s);
355         if (size)
356                 *size = s;
357         return r;
358 }
359
360 static void req_hook_send(void *closure, const char *buffer, size_t size)
361 {
362         struct afb_hook_req *tr = closure;
363
364         TRACE_REQ(send, tr, buffer, size);
365         afb_req_send(tr->req, buffer, size);
366 }
367
368 static void *req_hook_context_get(void *closure)
369 {
370         struct afb_hook_req *tr = closure;
371         void *r;
372
373         r = afb_req_context_get(tr->req);
374         TRACE_REQ(context_get, tr, r);
375
376         return r;
377 }
378
379 static void req_hook_context_set(void *closure, void *value, void (*free_value)(void*))
380 {
381         struct afb_hook_req *tr = closure;
382
383         TRACE_REQ(context_set, tr, value, free_value);
384         afb_req_context_set(tr->req, value, free_value);
385 }
386
387 static void req_hook_addref(void *closure)
388 {
389         struct afb_hook_req *tr = closure;
390
391         TRACE_REQ_(addref, tr);
392         hook_req_addref(tr);
393 }
394
395 static void req_hook_unref(void *closure)
396 {
397         struct afb_hook_req *tr = closure;
398
399         TRACE_REQ_(unref, tr);
400         hook_req_unref(tr);
401 }
402
403 static void req_hook_session_close(void *closure)
404 {
405         struct afb_hook_req *tr = closure;
406
407         TRACE_REQ_(session_close, tr);
408         afb_req_session_close(tr->req);
409 }
410
411 static int req_hook_session_set_LOA(void *closure, unsigned level)
412 {
413         struct afb_hook_req *tr = closure;
414         int r;
415
416         r = afb_req_session_set_LOA(tr->req, level);
417         TRACE_REQ(session_set_LOA, tr, level, r);
418         return r;
419 }
420
421 static int req_hook_subscribe(void *closure, struct afb_event event)
422 {
423         struct afb_hook_req *tr = closure;
424         int r;
425
426         r = afb_req_subscribe(tr->req, event);
427         TRACE_REQ(subscribe, tr, event, r);
428         return r;
429 }
430
431 static int req_hook_unsubscribe(void *closure, struct afb_event event)
432 {
433         struct afb_hook_req *tr = closure;
434         int r;
435
436         r = afb_req_unsubscribe(tr->req, event);
437         TRACE_REQ(unsubscribe, tr, event, r);
438         return r;
439 }
440
441 static void req_hook_subcall_result(void *closure, int status, struct json_object *result)
442 {
443         struct hook_subcall *sc = closure;
444         struct hook_subcall s = *sc;
445
446         free(sc);
447         TRACE_REQ(subcall_result, s.tr, status, result);
448         hook_req_unref(s.tr);
449         s.callback(s.cb_closure, status, result);
450 }
451
452 static void req_hook_subcall(void *closure, const char *api, const char *verb, struct json_object *args, void (*callback)(void*, int, struct json_object*), void *cb_closure)
453 {
454         struct afb_hook_req *tr = closure;
455         struct hook_subcall *sc;
456
457         TRACE_REQ(subcall, tr, api, verb, args);
458         sc = malloc(sizeof *sc);
459         if (sc) {
460                 sc->tr = tr;
461                 sc->callback = callback;
462                 sc->cb_closure = cb_closure;
463                 hook_req_addref(tr);
464                 cb_closure = sc;
465                 callback = req_hook_subcall_result;
466         }
467         afb_req_subcall(tr->req, api, verb, args, callback, cb_closure);
468 }
469
470 static struct afb_req_itf req_hook_itf = {
471         .json = req_hook_json,
472         .get = req_hook_get,
473         .success = req_hook_success,
474         .fail = req_hook_fail,
475         .raw = req_hook_raw,
476         .send = req_hook_send,
477         .context_get = req_hook_context_get,
478         .context_set = req_hook_context_set,
479         .addref = req_hook_addref,
480         .unref = req_hook_unref,
481         .session_close = req_hook_session_close,
482         .session_set_LOA = req_hook_session_set_LOA,
483         .subscribe = req_hook_subscribe,
484         .unsubscribe = req_hook_unsubscribe,
485         .subcall = req_hook_subcall
486 };
487
488 /******************************************************************************
489  * section: 
490  *****************************************************************************/
491
492 struct afb_req afb_hook_req_call(struct afb_req req, struct afb_context *context, const char *api, size_t lenapi, const char *verb, size_t lenverb)
493 {
494         int add;
495         struct afb_hook_req *tr;
496         struct afb_hook *hook;
497
498         hook = list_of_hooks;
499         if (hook) {
500                 tr = NULL;
501                 do {
502                         add = (hook->flags & afb_hook_flags_req_all) != 0
503                            && (!hook->session || hook->session == context->session)
504                            && (!hook->api || !(memcmp(hook->api, api, lenapi) || hook->api[lenapi]))
505                            && (!hook->verb || !(memcmp(hook->verb, verb, lenverb) || hook->verb[lenverb]));
506                         if (add) {
507                                 if (!tr)
508                                         tr = hook_req_create(req, context, api, lenapi, verb, lenverb);
509                                 if (tr)
510                                         hook_req_add_observer(tr, hook);
511                         }
512                         hook = hook->next;
513                 } while(hook);
514                 if (tr) {
515                         req.closure = tr;
516                         req.itf = &req_hook_itf;
517                         TRACE_REQX(begin, tr);
518                 }
519         }
520
521         return req;
522 }
523
524 struct afb_hook *afb_hook_req_create(const char *api, const char *verb, struct AFB_clientCtx *session, unsigned flags, struct afb_hook_req_itf *itf, void *closure)
525 {
526         struct afb_hook *hook;
527
528         hook = malloc(sizeof *hook);
529         if (hook == NULL)
530                 return NULL;
531
532         hook->api = api ? strdup(api) : NULL;
533         hook->verb = verb ? strdup(verb) : NULL;
534         hook->session = session ? ctxClientAddRef(session) : NULL;
535
536         if ((api && !hook->api) || (verb && !hook->verb)) {
537                 free(hook->api);
538                 free(hook->verb);
539                 if (hook->session)
540                         ctxClientUnref(hook->session);
541                 free(hook);
542                 return NULL;
543         }
544
545         hook->refcount = 1;
546         hook->flags = flags;
547         hook->reqitf = itf ? itf : &hook_req_default_itf;
548         hook->closure = closure;
549
550         hook->next = list_of_hooks;
551         list_of_hooks = hook;
552         return hook;
553 }
554
555 struct afb_hook *afb_hook_addref(struct afb_hook *hook)
556 {
557         hook->refcount++;
558         return hook;
559 }
560
561 void afb_hook_unref(struct afb_hook *hook)
562 {
563         if (!--hook->refcount) {
564                 /* unlink */
565                 struct afb_hook **prv = &list_of_hooks;
566                 while (*prv && *prv != hook)
567                         prv = &(*prv)->next;
568                 if(*prv)
569                         *prv = hook->next;
570
571                 /* free */
572                 free(hook->api);
573                 free(hook->verb);
574                 if (hook->session)
575                         ctxClientUnref(hook->session);
576                 free(hook);
577         }
578 }
579