#include <pthread.h>
#include <unistd.h>
#include <fnmatch.h>
+#include <sys/uio.h>
#include <json-c/json.h>
void *closure; /**< closure for callbacks */
};
+/**
+ * Definition of a hook for global
+ */
+struct afb_hook_global {
+ struct afb_hook_global *next; /**< next hook */
+ unsigned refcount; /**< reference count */
+ unsigned flags; /**< hook flags */
+ struct afb_hook_global_itf *itf; /**< interface of hook */
+ void *closure; /**< closure for callbacks */
+};
+
/* synchronisation across threads */
static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
/* list of hooks for evt */
static struct afb_hook_evt *list_of_evt_hooks = NULL;
+/* list of hooks for global */
+static struct afb_hook_global *list_of_global_hooks = NULL;
+
/* hook id */
static unsigned next_hookid = 0;
* section: default callbacks for tracing requests
*****************************************************************************/
-static char *_pbuf_(const char *fmt, va_list args, char **palloc, char *sbuf, size_t szsbuf)
+static char *_pbuf_(const char *fmt, va_list args, char **palloc, char *sbuf, size_t szsbuf, size_t *outlen)
{
int rc;
va_list cp;
sbuf = *palloc;
}
va_end(cp);
+ if (rc >= 0 && outlen)
+ *outlen = (size_t)rc;
return sbuf;
}
+#if 0 /* old behaviour: use NOTICE */
static void _hook_(const char *fmt1, const char *fmt2, va_list arg2, ...)
{
char *tag, *data, *mem1, *mem2, buf1[256], buf2[2000];
va_list arg1;
- data = _pbuf_(fmt2, arg2, &mem2, buf2, sizeof buf2);
+ data = _pbuf_(fmt2, arg2, &mem2, buf2, sizeof buf2, NULL);
va_start(arg1, arg2);
- tag = _pbuf_(fmt1, arg1, &mem1, buf1, sizeof buf1);
+ tag = _pbuf_(fmt1, arg1, &mem1, buf1, sizeof buf1, NULL);
va_end(arg1);
NOTICE("[HOOK %s] %s", tag, data);
free(mem1);
free(mem2);
}
+#else /* new behaviour: emits directly to stderr */
+static void _hook_(const char *fmt1, const char *fmt2, va_list arg2, ...)
+{
+ static const char chars[] = "HOOK: [] \n";
+ char *mem1, *mem2, buf1[256], buf2[2000];
+ struct iovec iov[5];
+ va_list arg1;
+
+ iov[0].iov_base = (void*)&chars[0];
+ iov[0].iov_len = 7;
+
+ va_start(arg1, arg2);
+ iov[1].iov_base = _pbuf_(fmt1, arg1, &mem1, buf1, sizeof buf1, &iov[1].iov_len);
+ va_end(arg1);
+
+ iov[2].iov_base = (void*)&chars[7];
+ iov[2].iov_len = 2;
+
+ iov[3].iov_base = _pbuf_(fmt2, arg2, &mem2, buf2, sizeof buf2, &iov[3].iov_len);
+
+ iov[4].iov_base = (void*)&chars[9];
+ iov[4].iov_len = 1;
+
+ writev(2, iov, 5);
+
+ free(mem1);
+ free(mem2);
+}
+#endif
static void _hook_xreq_(const struct afb_xreq *xreq, const char *format, ...)
{
_hook_xreq_(xreq, " ...subcall_req... -> %d: %s", status, json_object_to_json_string(result));
}
+static void hook_xreq_has_permission_default_cb(void *closure, const struct afb_hookid *hookid, const struct afb_xreq *xreq, const char *permission, int result)
+{
+ _hook_xreq_(xreq, "has_permission(%s) -> %d", permission, result);
+}
+
static struct afb_hook_xreq_itf hook_xreq_default_itf = {
.hook_xreq_begin = hook_xreq_begin_default_cb,
.hook_xreq_end = hook_xreq_end_default_cb,
.hook_xreq_store = hook_xreq_store_default_cb,
.hook_xreq_unstore = hook_xreq_unstore_default_cb,
.hook_xreq_subcall_req = hook_xreq_subcall_req_default_cb,
- .hook_xreq_subcall_req_result = hook_xreq_subcall_req_result_default_cb
+ .hook_xreq_subcall_req_result = hook_xreq_subcall_req_result_default_cb,
+ .hook_xreq_has_permission = hook_xreq_has_permission_default_cb
};
/******************************************************************************
_HOOK_XREQ_(subcall_req_result, xreq, status, result);
}
+int afb_hook_xreq_has_permission(const struct afb_xreq *xreq, const char *permission, int result)
+{
+ _HOOK_XREQ_(has_permission, xreq, permission, result);
+ return result;
+}
+
/******************************************************************************
* section: hooking xreqs
*****************************************************************************/
_hook_ditf_(ditf, "...require_api(%s, %d) -> %d", name, initialized, result);
}
+static void hook_ditf_rename_api_cb(void *closure, const struct afb_hookid *hookid, const struct afb_ditf *ditf, const char *oldname, const char *newname, int result)
+{
+ _hook_ditf_(ditf, "rename_api(%s -> %s) -> %d", oldname, newname, result);
+}
+
static struct afb_hook_ditf_itf hook_ditf_default_itf = {
.hook_ditf_event_broadcast_before = hook_ditf_event_broadcast_before_cb,
.hook_ditf_event_broadcast_after = hook_ditf_event_broadcast_after_cb,
.hook_ditf_queue_job = hook_ditf_queue_job_cb,
.hook_ditf_unstore_req = hook_ditf_unstore_req_cb,
.hook_ditf_require_api = hook_ditf_require_api_cb,
- .hook_ditf_require_api_result = hook_ditf_require_api_result_cb
+ .hook_ditf_require_api_result = hook_ditf_require_api_result_cb,
+ .hook_ditf_rename_api = hook_ditf_rename_api_cb
};
/******************************************************************************
return result;
}
+int afb_hook_ditf_rename_api(const struct afb_ditf *ditf, const char *oldname, const char *newname, int result)
+{
+ _HOOK_DITF_(rename_api, ditf, oldname, newname, result);
+ return result;
+}
+
/******************************************************************************
* section: hooking ditf
*****************************************************************************/
};
/******************************************************************************
- * section: hooks for tracing service interface (evt)
+ * section: hooks for tracing events interface (evt)
*****************************************************************************/
#define _HOOK_EVT_(what,...) \
}
}
}
+
+/******************************************************************************
+ * section: default callbacks for globals (global)
+ *****************************************************************************/
+
+static void _hook_global_(const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ _hook_("global", format, ap);
+ va_end(ap);
+}
+
+static void hook_global_vverbose_default_cb(void *closure, const struct afb_hookid *hookid, int level, const char *file, int line, const char *func, const char *fmt, va_list args)
+{
+ int len;
+ char *msg;
+ va_list ap;
+
+ va_copy(ap, args);
+ len = vasprintf(&msg, fmt, ap);
+ va_end(ap);
+
+ if (len < 0)
+ _hook_global_("vverbose(%d, %s, %d, %s) -> %s ? ? ?", level, file, line, func, fmt);
+ else {
+ _hook_global_("vverbose(%d, %s, %d, %s) -> %s", level, file, line, func, msg);
+ free(msg);
+ }
+}
+
+static struct afb_hook_global_itf hook_global_default_itf = {
+ .hook_global_vverbose = hook_global_vverbose_default_cb
+};
+
+/******************************************************************************
+ * section: hooks for tracing globals (global)
+ *****************************************************************************/
+
+#define _HOOK_GLOBAL_(what,...) \
+ struct afb_hook_global *hook; \
+ struct afb_hookid hookid; \
+ pthread_rwlock_rdlock(&rwlock); \
+ init_hookid(&hookid); \
+ hook = list_of_global_hooks; \
+ while (hook) { \
+ if (hook->itf->hook_global_##what \
+ && (hook->flags & afb_hook_flag_global_##what) != 0) { \
+ hook->itf->hook_global_##what(hook->closure, &hookid, __VA_ARGS__); \
+ } \
+ hook = hook->next; \
+ } \
+ pthread_rwlock_unlock(&rwlock);
+
+static void afb_hook_global_vverbose(int level, const char *file, int line, const char *func, const char *fmt, va_list args)
+{
+ _HOOK_GLOBAL_(vverbose, level, file ?: "?", line, func ?: "?", fmt ?: "", args);
+}
+
+/******************************************************************************
+ * section: hooking globals (global)
+ *****************************************************************************/
+
+static void update_global()
+{
+ struct afb_hook_global *hook;
+ int flags = 0;
+
+ pthread_rwlock_rdlock(&rwlock);
+ hook = list_of_global_hooks;
+ while (hook) {
+ flags = hook->flags;
+ hook = hook->next;
+ }
+ verbose_observer = (flags & afb_hook_flag_global_vverbose) ? afb_hook_global_vverbose : NULL;
+ pthread_rwlock_unlock(&rwlock);
+}
+
+struct afb_hook_global *afb_hook_create_global(int flags, struct afb_hook_global_itf *itf, void *closure)
+{
+ struct afb_hook_global *hook;
+
+ /* alloc the result */
+ hook = calloc(1, sizeof *hook);
+ if (hook == NULL)
+ return NULL;
+
+ /* initialise the rest */
+ hook->refcount = 1;
+ hook->flags = flags;
+ hook->itf = itf ? itf : &hook_global_default_itf;
+ hook->closure = closure;
+
+ /* record the hook */
+ pthread_rwlock_wrlock(&rwlock);
+ hook->next = list_of_global_hooks;
+ list_of_global_hooks = hook;
+ pthread_rwlock_unlock(&rwlock);
+
+ /* update hooking */
+ update_global();
+
+ /* returns it */
+ return hook;
+}
+
+struct afb_hook_global *afb_hook_addref_global(struct afb_hook_global *hook)
+{
+ pthread_rwlock_wrlock(&rwlock);
+ hook->refcount++;
+ pthread_rwlock_unlock(&rwlock);
+ return hook;
+}
+
+void afb_hook_unref_global(struct afb_hook_global *hook)
+{
+ struct afb_hook_global **prv;
+
+ if (hook) {
+ pthread_rwlock_wrlock(&rwlock);
+ if (--hook->refcount)
+ hook = NULL;
+ else {
+ /* unlink */
+ prv = &list_of_global_hooks;
+ while (*prv && *prv != hook)
+ prv = &(*prv)->next;
+ if(*prv)
+ *prv = hook->next;
+ }
+ pthread_rwlock_unlock(&rwlock);
+ if (hook) {
+ /* free */
+ free(hook);
+
+ /* update hooking */
+ update_global();
+ }
+ }
+}
+