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