Simplify functions for calls
[src/app-framework-binder.git] / src / afb-hook.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 <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 "afb-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_session *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, const char *verb)
276 {
277         int len;
278         char name[257];
279         unsigned id;
280         struct afb_hook_req *tr;
281
282         /* get the call id */
283         id = ++hook_count;
284         if (id == 1000000)
285                 id = hook_count = 1;
286
287         /* creates the name */
288         len = snprintf(name, sizeof name, "%06d:%s/%s", id, api, verb);
289         if (len < 0 || (size_t)len >= sizeof name) {
290                 tr = NULL;
291         } else {
292                 tr = malloc(sizeof *tr + (size_t)len);
293                 if (tr != NULL) {
294                         /* init hook */
295                         tr->observers = NULL;
296                         tr->refcount = 1;
297                         tr->context = context;
298                         tr->req = req;
299                         afb_req_addref(req);
300                         memcpy(tr->name, name, (size_t)(len + 1));
301                 }
302         }
303         return tr;
304 }
305
306 static void hook_req_add_observer(struct afb_hook_req *tr, struct afb_hook *hook)
307 {
308         struct hook_req_observer *observer;
309
310         observer = malloc(sizeof *observer);
311         if (observer) {
312                 observer->hook = afb_hook_addref(hook);
313                 observer->next = tr->observers;
314                 tr->observers = observer;
315         }
316 }
317
318 /******************************************************************************
319  * section: hooks for tracing requests
320  *****************************************************************************/
321
322 static struct json_object *req_hook_json(void *closure)
323 {
324         struct afb_hook_req *tr = closure;
325         struct json_object *r;
326
327         r = afb_req_json(tr->req);
328         TRACE_REQ(json, tr, r);
329         return r;
330 }
331
332 static struct afb_arg req_hook_get(void *closure, const char *name)
333 {
334         struct afb_hook_req *tr = closure;
335         struct afb_arg a;
336
337         a = afb_req_get(tr->req, name);
338         TRACE_REQ(get, tr, name, a);
339         return a;
340 }
341
342 static void req_hook_success(void *closure, struct json_object *obj, const char *info)
343 {
344         struct afb_hook_req *tr = closure;
345
346         TRACE_REQ(success, tr, obj, info);
347         afb_req_success(tr->req, obj, info);
348         hook_req_unref(tr);
349 }
350
351 static void req_hook_fail(void *closure, const char *status, const char *info)
352 {
353         struct afb_hook_req *tr = closure;
354
355         TRACE_REQ(fail, tr, status, info);
356         afb_req_fail(tr->req, status, info);
357         hook_req_unref(tr);
358 }
359
360 static const char *req_hook_raw(void *closure, size_t *size)
361 {
362         struct afb_hook_req *tr = closure;
363         const char *r;
364         size_t s;
365
366         r = afb_req_raw(tr->req, &s);
367         TRACE_REQ(raw, tr, r, s);
368         if (size)
369                 *size = s;
370         return r;
371 }
372
373 static void req_hook_send(void *closure, const char *buffer, size_t size)
374 {
375         struct afb_hook_req *tr = closure;
376
377         TRACE_REQ(send, tr, buffer, size);
378         afb_req_send(tr->req, buffer, size);
379 }
380
381 static void *req_hook_context_get(void *closure)
382 {
383         struct afb_hook_req *tr = closure;
384         void *r;
385
386         r = afb_req_context_get(tr->req);
387         TRACE_REQ(context_get, tr, r);
388
389         return r;
390 }
391
392 static void req_hook_context_set(void *closure, void *value, void (*free_value)(void*))
393 {
394         struct afb_hook_req *tr = closure;
395
396         TRACE_REQ(context_set, tr, value, free_value);
397         afb_req_context_set(tr->req, value, free_value);
398 }
399
400 static void req_hook_addref(void *closure)
401 {
402         struct afb_hook_req *tr = closure;
403
404         TRACE_REQ_(addref, tr);
405         hook_req_addref(tr);
406 }
407
408 static void req_hook_unref(void *closure)
409 {
410         struct afb_hook_req *tr = closure;
411
412         TRACE_REQ_(unref, tr);
413         hook_req_unref(tr);
414 }
415
416 static void req_hook_session_close(void *closure)
417 {
418         struct afb_hook_req *tr = closure;
419
420         TRACE_REQ_(session_close, tr);
421         afb_req_session_close(tr->req);
422 }
423
424 static int req_hook_session_set_LOA(void *closure, unsigned level)
425 {
426         struct afb_hook_req *tr = closure;
427         int r;
428
429         r = afb_req_session_set_LOA(tr->req, level);
430         TRACE_REQ(session_set_LOA, tr, level, r);
431         return r;
432 }
433
434 static int req_hook_subscribe(void *closure, struct afb_event event)
435 {
436         struct afb_hook_req *tr = closure;
437         int r;
438
439         r = afb_req_subscribe(tr->req, event);
440         TRACE_REQ(subscribe, tr, event, r);
441         return r;
442 }
443
444 static int req_hook_unsubscribe(void *closure, struct afb_event event)
445 {
446         struct afb_hook_req *tr = closure;
447         int r;
448
449         r = afb_req_unsubscribe(tr->req, event);
450         TRACE_REQ(unsubscribe, tr, event, r);
451         return r;
452 }
453
454 static void req_hook_subcall_result(void *closure, int status, struct json_object *result)
455 {
456         struct hook_subcall *sc = closure;
457         struct hook_subcall s = *sc;
458
459         free(sc);
460         TRACE_REQ(subcall_result, s.tr, status, result);
461         hook_req_unref(s.tr);
462         s.callback(s.cb_closure, status, result);
463 }
464
465 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)
466 {
467         struct afb_hook_req *tr = closure;
468         struct hook_subcall *sc;
469
470         TRACE_REQ(subcall, tr, api, verb, args);
471         sc = malloc(sizeof *sc);
472         if (sc) {
473                 sc->tr = tr;
474                 sc->callback = callback;
475                 sc->cb_closure = cb_closure;
476                 hook_req_addref(tr);
477                 cb_closure = sc;
478                 callback = req_hook_subcall_result;
479         }
480         afb_req_subcall(tr->req, api, verb, args, callback, cb_closure);
481 }
482
483 static struct afb_req_itf req_hook_itf = {
484         .json = req_hook_json,
485         .get = req_hook_get,
486         .success = req_hook_success,
487         .fail = req_hook_fail,
488         .raw = req_hook_raw,
489         .send = req_hook_send,
490         .context_get = req_hook_context_get,
491         .context_set = req_hook_context_set,
492         .addref = req_hook_addref,
493         .unref = req_hook_unref,
494         .session_close = req_hook_session_close,
495         .session_set_LOA = req_hook_session_set_LOA,
496         .subscribe = req_hook_subscribe,
497         .unsubscribe = req_hook_unsubscribe,
498         .subcall = req_hook_subcall
499 };
500
501 /******************************************************************************
502  * section: 
503  *****************************************************************************/
504
505 struct afb_req afb_hook_req_call(struct afb_req req, struct afb_context *context, const char *api, const char *verb)
506 {
507         int add;
508         struct afb_hook_req *tr;
509         struct afb_hook *hook;
510
511         hook = list_of_hooks;
512         if (hook) {
513                 tr = NULL;
514                 do {
515                         add = (hook->flags & afb_hook_flags_req_all) != 0
516                            && (!hook->session || hook->session == context->session)
517                            && (!hook->api || !strcasecmp(hook->api, api))
518                            && (!hook->verb || !strcasecmp(hook->verb, verb));
519                         if (add) {
520                                 if (!tr)
521                                         tr = hook_req_create(req, context, api, verb);
522                                 if (tr)
523                                         hook_req_add_observer(tr, hook);
524                         }
525                         hook = hook->next;
526                 } while(hook);
527                 if (tr) {
528                         req.closure = tr;
529                         req.itf = &req_hook_itf;
530                         TRACE_REQX(begin, tr);
531                 }
532         }
533
534         return req;
535 }
536
537 struct afb_hook *afb_hook_req_create(const char *api, const char *verb, struct afb_session *session, unsigned flags, struct afb_hook_req_itf *itf, void *closure)
538 {
539         struct afb_hook *hook;
540
541         hook = malloc(sizeof *hook);
542         if (hook == NULL)
543                 return NULL;
544
545         hook->api = api ? strdup(api) : NULL;
546         hook->verb = verb ? strdup(verb) : NULL;
547         hook->session = session ? afb_session_addref(session) : NULL;
548
549         if ((api && !hook->api) || (verb && !hook->verb)) {
550                 free(hook->api);
551                 free(hook->verb);
552                 if (hook->session)
553                         afb_session_unref(hook->session);
554                 free(hook);
555                 return NULL;
556         }
557
558         hook->refcount = 1;
559         hook->flags = flags;
560         hook->reqitf = itf ? itf : &hook_req_default_itf;
561         hook->closure = closure;
562
563         hook->next = list_of_hooks;
564         list_of_hooks = hook;
565         return hook;
566 }
567
568 struct afb_hook *afb_hook_addref(struct afb_hook *hook)
569 {
570         hook->refcount++;
571         return hook;
572 }
573
574 void afb_hook_unref(struct afb_hook *hook)
575 {
576         if (!--hook->refcount) {
577                 /* unlink */
578                 struct afb_hook **prv = &list_of_hooks;
579                 while (*prv && *prv != hook)
580                         prv = &(*prv)->next;
581                 if(*prv)
582                         *prv = hook->next;
583
584                 /* free */
585                 free(hook->api);
586                 free(hook->verb);
587                 if (hook->session)
588                         afb_session_unref(hook->session);
589                 free(hook);
590         }
591 }
592