/* * * registrycb.c * * $Source: /Users/ekr/tmp/nrappkit-dump/nrappkit/src/registry/registrycb.c,v $ * $Revision: 1.3 $ * $Date: 2007/06/26 22:37:51 $ * * Callback-related functions * * * Copyright (C) 2005, Network Resonance, Inc. * Copyright (C) 2006, Network Resonance, Inc. * All Rights Reserved * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Network Resonance, Inc. nor the name of any * contributors to this software may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * */ #include #include #include "registry.h" #include "registry_int.h" #include "r_assoc.h" #include "r_errors.h" #include "nr_common.h" #include "r_log.h" #include "r_macros.h" static char CB_ACTIONS[] = { NR_REG_CB_ACTION_ADD, NR_REG_CB_ACTION_DELETE, NR_REG_CB_ACTION_CHANGE, NR_REG_CB_ACTION_FINAL }; typedef struct nr_reg_cb_info_ { char action; void (*cb)(void *cb_arg, char action, NR_registry name); void *cb_arg; NR_registry name; } nr_reg_cb_info; /* callbacks that are registered, a mapping from names like "foo.bar.baz" * to an r_assoc which contains possibly several nr_reg_cb_info*'s */ static r_assoc *nr_registry_callbacks = 0; //static size_t SIZEOF_CB_ID = (sizeof(void (*)()) + 1); #define SIZEOF_CB_ID (sizeof(void (*)()) + 1) static int nr_reg_validate_action(char action); static int nr_reg_assoc_destroy(void *ptr); static int compute_cb_id(void *cb, char action, unsigned char cb_id[SIZEOF_CB_ID]); static int nr_reg_info_free(void *ptr); static int nr_reg_raise_event_recurse(char *name, char *tmp, int action); static int nr_reg_register_callback(NR_registry name, char action, void (*cb)(void *cb_arg, char action, NR_registry name), void *cb_arg); static int nr_reg_unregister_callback(char *name, char action, void (*cb)(void *cb_arg, char action, NR_registry name)); int nr_reg_cb_init() { int r, _status; if (nr_registry_callbacks == 0) { if ((r=r_assoc_create(&nr_registry_callbacks, r_assoc_crc32_hash_compute, 12))) ABORT(r); } _status=0; abort: if (_status) { r_log(NR_LOG_REGISTRY, LOG_DEBUG, "Couldn't init notifications: %s", nr_strerror(_status)); } return(_status); } int nr_reg_validate_action(char action) { int _status; size_t i; for (i = 0; i < sizeof(CB_ACTIONS); ++i) { if (action == CB_ACTIONS[i]) return 0; } ABORT(R_BAD_ARGS); _status=0; abort: return(_status); } int nr_reg_register_callback(NR_registry name, char action, void (*cb)(void *cb_arg, char action, NR_registry name), void *cb_arg) { int r, _status; r_assoc *assoc; int create_assoc = 0; nr_reg_cb_info *info; int create_info = 0; unsigned char cb_id[SIZEOF_CB_ID]; if (name == 0 || cb == 0) ABORT(R_BAD_ARGS); if (nr_registry_callbacks == 0) ABORT(R_FAILED); if ((r=nr_reg_is_valid(name))) ABORT(r); if ((r=nr_reg_validate_action(action))) ABORT(r); if ((r=r_assoc_fetch(nr_registry_callbacks, name, strlen(name)+1, (void*)&assoc))) { if (r == R_NOT_FOUND) create_assoc = 1; else ABORT(r); } if (create_assoc) { if ((r=r_assoc_create(&assoc, r_assoc_crc32_hash_compute, 5))) ABORT(r); if ((r=r_assoc_insert(nr_registry_callbacks, name, strlen(name)+1, assoc, 0, nr_reg_assoc_destroy, R_ASSOC_NEW))) ABORT(r); } if ((r=compute_cb_id(cb, action, cb_id))) ABORT(r); if ((r=r_assoc_fetch(assoc, (char*)cb_id, SIZEOF_CB_ID, (void*)&info))) { if (r == R_NOT_FOUND) create_info = 1; else ABORT(r); } if (create_info) { if (!(info=(void*)RCALLOC(sizeof(*info)))) ABORT(R_NO_MEMORY); } strncpy(info->name, name, sizeof(info->name)); info->action = action; info->cb = cb; info->cb_arg = cb_arg; if (create_info) { if ((r=r_assoc_insert(assoc, (char*)cb_id, SIZEOF_CB_ID, info, 0, nr_reg_info_free, R_ASSOC_NEW))) ABORT(r); } _status=0; abort: r_log(NR_LOG_REGISTRY, LOG_DEBUG, "register callback %p on '%s' for '%s' %s", cb, name, nr_reg_action_name(action), (_status ? "FAILED" : "succeeded")); if (_status) { if (create_info && info) RFREE(info); if (create_assoc && assoc) nr_reg_assoc_destroy(&assoc); } return(_status); } int nr_reg_unregister_callback(char *name, char action, void (*cb)(void *cb_arg, char action, NR_registry name)) { int r, _status; r_assoc *assoc; int size; unsigned char cb_id[SIZEOF_CB_ID]; if (name == 0 || cb == 0) ABORT(R_BAD_ARGS); if (nr_registry_callbacks == 0) ABORT(R_FAILED); if ((r=nr_reg_is_valid(name))) ABORT(r); if ((r=nr_reg_validate_action(action))) ABORT(r); if ((r=r_assoc_fetch(nr_registry_callbacks, name, strlen(name)+1, (void*)&assoc))) { if (r != R_NOT_FOUND) ABORT(r); } else { if ((r=compute_cb_id(cb, action, cb_id))) ABORT(r); if ((r=r_assoc_delete(assoc, (char*)cb_id, SIZEOF_CB_ID))) { if (r != R_NOT_FOUND) ABORT(r); } if ((r=r_assoc_num_elements(assoc, &size))) ABORT(r); if (size == 0) { if ((r=r_assoc_delete(nr_registry_callbacks, name, strlen(name)+1))) ABORT(r); } } _status=0; abort: r_log(NR_LOG_REGISTRY, LOG_DEBUG, "unregister callback %p on '%s' for '%s' %s", cb, name, nr_reg_action_name(action), (_status ? "FAILED" : "succeeded")); return(_status); } int compute_cb_id(void *cb, char action, unsigned char cb_id[SIZEOF_CB_ID]) { /* callbacks are identified by the pointer to the cb function plus * the action being watched */ assert(sizeof(cb) == sizeof(void (*)())); assert(sizeof(cb) == (SIZEOF_CB_ID - 1)); memcpy(cb_id, &(cb), sizeof(cb)); cb_id[SIZEOF_CB_ID-1] = action; return 0; } char * nr_reg_action_name(int action) { char *name = "*Unknown*"; switch (action) { case NR_REG_CB_ACTION_ADD: name = "add"; break; case NR_REG_CB_ACTION_DELETE: name = "delete"; break; case NR_REG_CB_ACTION_CHANGE: name = "change"; break; case NR_REG_CB_ACTION_FINAL: name = "final"; break; } return name; } int nr_reg_assoc_destroy(void *ptr) { return r_assoc_destroy((r_assoc**)&ptr); } int nr_reg_info_free(void *ptr) { RFREE(ptr); return 0; } /* call with tmp=0 */ int nr_reg_raise_event_recurse(char *name, char *tmp, int action) { int r, _status; r_assoc *assoc; nr_reg_cb_info *info; r_assoc_iterator iter; char *key; int keyl; char *c; int free_tmp = 0; int count; if (tmp == 0) { if (!(tmp = (char*)r_strdup(name))) ABORT(R_NO_MEMORY); free_tmp = 1; } if ((r=r_assoc_fetch(nr_registry_callbacks, tmp, strlen(tmp)+1, (void*)&assoc))) { if (r != R_NOT_FOUND) ABORT(r); r_log(NR_LOG_REGISTRY, LOG_DEBUG, "No callbacks found on '%s'", tmp); } else { if (!r_assoc_num_elements(assoc, &count)) { r_log(NR_LOG_REGISTRY, LOG_DEBUG, "%d callback%s found on '%s'", count, ((count == 1) ? "" : "s"), tmp); } if ((r=r_assoc_init_iter(assoc, &iter))) ABORT(r); for (;;) { if ((r=r_assoc_iter(&iter, (void*)&key, &keyl, (void*)&info))) { if (r == R_EOD) break; else ABORT(r); } if (info->action == action) { r_log(NR_LOG_REGISTRY, LOG_DEBUG, "Invoking callback %p for '%s'", info->cb, nr_reg_action_name(info->action)); (void)info->cb(info->cb_arg, action, name); } else { r_log(NR_LOG_REGISTRY, LOG_DEBUG, "Skipping callback %p for '%s'", info->cb, nr_reg_action_name(info->action)); } } } if (strlen(tmp) > 0) { c = strrchr(tmp, '.'); if (c != 0) *c = '\0'; else tmp[0] = '\0'; if ((r=nr_reg_raise_event_recurse(name, tmp, action))) ABORT(r); } _status=0; abort: if (free_tmp && tmp != 0) RFREE(tmp); return(_status); } /* NON-STATIC METHODS */ int nr_reg_raise_event(NR_registry name, int action) { int r, _status; int count; char *event = nr_reg_action_name(action); r_log(NR_LOG_REGISTRY, LOG_DEBUG, "raising event '%s' on '%s'", event, name); if (name == 0) ABORT(R_BAD_ARGS); if ((r=nr_reg_validate_action(action))) ABORT(r); if ((r=r_assoc_num_elements(nr_registry_callbacks, &count))) ABORT(r); if (count > 0) { if ((r=nr_reg_raise_event_recurse(name, 0, action))) ABORT(r); } else { r_log(NR_LOG_REGISTRY, LOG_DEBUG, "No callbacks found"); return 0; } _status=0; abort: return(_status); } /* PUBLIC METHODS */ int NR_reg_register_callback(NR_registry name, char action, void (*cb)(void *cb_arg, char action, NR_registry name), void *cb_arg) { int r, _status; size_t i; for (i = 0; i < sizeof(CB_ACTIONS); ++i) { if (action & CB_ACTIONS[i]) { if ((r=nr_reg_register_callback(name, CB_ACTIONS[i], cb, cb_arg))) ABORT(r); action &= ~(CB_ACTIONS[i]); } } if (action) ABORT(R_BAD_ARGS); _status=0; abort: return(_status); } int NR_reg_unregister_callback(NR_registry name, char action, void (*cb)(void *cb_arg, char action, NR_registry name)) { int r, _status; size_t i; for (i = 0; i < sizeof(CB_ACTIONS); ++i) { if (action & CB_ACTIONS[i]) { if ((r=nr_reg_unregister_callback(name, CB_ACTIONS[i], cb))) ABORT(r); action &= ~(CB_ACTIONS[i]); } } if (action) ABORT(R_BAD_ARGS); _status=0; abort: return(_status); }