Source code
Revision control
Copy as Markdown
Other Tools
/*
*
* 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 <assert.h>
#include <string.h>
#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 = 0;
int create_assoc = 0;
nr_reg_cb_info *info = 0;
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 = 0;
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 = 0;
nr_reg_cb_info *info;
r_assoc_iterator iter;
char *key = 0;
int keyl;
char *c = 0;
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);
}