188 lines
4.4 KiB
C
188 lines
4.4 KiB
C
#include <errno.h>
|
|
#include <stdatomic.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "hook.h"
|
|
#include "utils.h"
|
|
|
|
int xce_hook_init(hooksys_t* s, int maxtypes)
|
|
{
|
|
int i;
|
|
if ((s == NULL) || (maxtypes <= 0)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
s->maxtypes = 0;
|
|
s->hooks = (xlist_t*)calloc(maxtypes, sizeof(xlist_t));
|
|
if (s->hooks == NULL) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
s->maxtypes = maxtypes;
|
|
for (i = 0; i < s->maxtypes; i++) {
|
|
xlist_init(&(s->hooks[i]), 8);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int xce_add_hook(hooksys_t* s, int typ, hook_fn_t fn, void* data)
|
|
{
|
|
hook_item_t* it;
|
|
|
|
if (s == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (fn == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if ((typ < 0) || (typ >= s->maxtypes)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Single scan of the type's slots:
|
|
* - if (fn,data) is ALREADY registered and active, adding again is a
|
|
* no-op (idempotent). xce_disable_hook removes only ONE matching slot
|
|
* and xce_run_hooks fires ALL slots, so a duplicate (fn,data) would
|
|
* fire twice and survive a single disable — the bug-845 double-append
|
|
* (persist_start + ha_register_persist_hooks both register the same
|
|
* persist_job_hook_fun closure). Dedup is keyed on (fn,data): the same
|
|
* fn with a DISTINCT closure stays a legitimate second registration.
|
|
* - otherwise remember the first disabled (NULL-fn) slot to reuse. */
|
|
{
|
|
xlist_t* l = &s->hooks[typ];
|
|
hook_item_t* reuse = NULL;
|
|
int i;
|
|
for (i = 0; i < l->count; i++) {
|
|
hook_item_t* slot = (hook_item_t*)l->data[i];
|
|
if (!slot) continue;
|
|
hook_fn_t cur =
|
|
atomic_load_explicit(&slot->fn, memory_order_acquire);
|
|
if (cur == NULL) {
|
|
if (!reuse) reuse = slot;
|
|
continue;
|
|
}
|
|
if (cur == fn && slot->data == data)
|
|
return 0; /* already registered — no duplicate */
|
|
}
|
|
if (reuse != NULL) {
|
|
reuse->data = data;
|
|
atomic_store_explicit(&reuse->fn, fn, memory_order_release);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* No reusable slot — allocate new */
|
|
it = (hook_item_t*)calloc(1, sizeof(*it));
|
|
if (it == NULL) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
it->data = data;
|
|
atomic_store_explicit(&it->fn, fn, memory_order_release);
|
|
|
|
xlist_store(&(s->hooks[typ]), it);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int xce_disable_hook(hooksys_t* s, int typ, hook_fn_t fn)
|
|
{
|
|
xlist_t* l;
|
|
int i;
|
|
|
|
if (!s || !fn || typ < 0 || typ >= s->maxtypes) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
l = &s->hooks[typ];
|
|
for (i = 0; i < l->count; i++) {
|
|
hook_item_t* it = (hook_item_t*)l->data[i];
|
|
if (!it) continue;
|
|
hook_fn_t cur = atomic_load_explicit(&it->fn, memory_order_acquire);
|
|
if (cur == fn) {
|
|
atomic_store_explicit(&it->fn, (hook_fn_t)NULL,
|
|
memory_order_release);
|
|
return 0;
|
|
}
|
|
}
|
|
return -ENOENT;
|
|
}
|
|
|
|
/* HOOK_BEFORE_EVENT returns
|
|
* negative value will cause
|
|
* fail
|
|
*/
|
|
int xce_run_hooks(hooksys_t* s, int type, int situation, int detail, ...)
|
|
{
|
|
xlist_t* l;
|
|
int i;
|
|
int rc = 0;
|
|
|
|
if (s == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if ((type < 0) || (type >= s->maxtypes)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
l = &(s->hooks[type]);
|
|
|
|
for (i = 0; i < l->count; i++) {
|
|
hook_item_t* it = l->data[i];
|
|
hook_fn_t local_fn;
|
|
va_list ap;
|
|
|
|
if (it == NULL) {
|
|
continue;
|
|
}
|
|
|
|
local_fn = atomic_load_explicit(&it->fn, memory_order_acquire);
|
|
if (!local_fn) {
|
|
continue; /* disabled slot */
|
|
}
|
|
|
|
va_start(ap, detail);
|
|
rc = local_fn(type, situation, detail, it->data, ap);
|
|
va_end(ap);
|
|
|
|
if ((situation == HOOK_BEFORE_EVENT) && (rc < 0)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int free_hook_item(void* data1, void* data2)
|
|
{
|
|
if (data1 == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
free(data1);
|
|
return 0;
|
|
}
|
|
|
|
void xce_hook_fini(hooksys_t* s)
|
|
{
|
|
int i;
|
|
|
|
if (s == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (s->hooks == NULL) {
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < s->maxtypes; i++) {
|
|
xlist_iter(&(s->hooks[i]), (xlist_iter_fn_t)free_hook_item, NULL);
|
|
xlist_fini(&(s->hooks[i]));
|
|
}
|
|
|
|
FREEUP(s->hooks);
|
|
}
|