diff options
Diffstat (limited to 'ldap/servers/plugins/statechange/statechange.c')
-rw-r--r-- | ldap/servers/plugins/statechange/statechange.c | 450 |
1 files changed, 450 insertions, 0 deletions
diff --git a/ldap/servers/plugins/statechange/statechange.c b/ldap/servers/plugins/statechange/statechange.c new file mode 100644 index 00000000..c0942b41 --- /dev/null +++ b/ldap/servers/plugins/statechange/statechange.c @@ -0,0 +1,450 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* plugin which provides a callback mechanism for state changes in the DS */ + +#include <stdio.h> +#include <string.h> +#include "portable.h" +#include "slapi-plugin.h" +#include <dirlite_strings.h> /* PLUGIN_MAGIC_VENDOR_STR */ +#include "dirver.h" +#include "statechange.h" + +/* get file mode flags for unix */ +#ifndef _WIN32 +#include <sys/stat.h> +#endif + +/* the circular list of systems to notify */ +typedef struct _statechange_notify +{ + char *caller_id; + char *dn; + char *filter; + Slapi_Filter *realfilter; + notify_callback func; + void *caller_data; + struct _statechange_notify *next; + struct _statechange_notify *prev; +} SCNotify; + +static SCNotify *head; /* a place to start in the list */ + +#define SCN_PLUGIN_SUBSYSTEM "statechange-plugin" /* used for logging */ + +static void *api[5]; +static Slapi_Mutex *buffer_lock = 0; + +/* other function prototypes */ +int statechange_init( Slapi_PBlock *pb ); +static int statechange_start( Slapi_PBlock *pb ); +static int statechange_close( Slapi_PBlock *pb ); +static int statechange_post_op( Slapi_PBlock *pb, int modtype ); +static int statechange_mod_post_op( Slapi_PBlock *pb ); +static int statechange_modrdn_post_op( Slapi_PBlock *pb ); +static int statechange_add_post_op( Slapi_PBlock *pb ); +static int statechange_delete_post_op( Slapi_PBlock *pb ); +static int _statechange_register(char *caller_id, char *dn, char *filter, void *caller_data, notify_callback func); +static void *_statechange_unregister(char *dn, char *filter, notify_callback func); +static void _statechange_unregister_all(char *caller_id, caller_data_free_callback); +static void _statechange_vattr_cache_invalidator_callback(Slapi_Entry *e, char *dn, int modtype, Slapi_PBlock *pb, void *caller_data); +static SCNotify *statechange_find_notify(char *dn, char *filter, notify_callback func); + + +static Slapi_PluginDesc pdesc = { "statechange", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT, + "state change notification service plugin" }; + + +#ifdef _WIN32 +int *module_ldap_debug = 0; + +void plugin_init_debug_level(int *level_ptr) +{ + module_ldap_debug = level_ptr; +} +#endif + + +/* + statechange_init + -------- + adds our callbacks to the list +*/ +int statechange_init( Slapi_PBlock *pb ) +{ + int ret = 0; + + slapi_log_error( SLAPI_LOG_TRACE, SCN_PLUGIN_SUBSYSTEM, "--> statechange_init\n"); + + head = 0; + + if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, + SLAPI_PLUGIN_VERSION_01 ) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN, + (void *) statechange_start ) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODIFY_FN, + (void *) statechange_mod_post_op ) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODRDN_FN, + (void *) statechange_modrdn_post_op ) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_POST_ADD_FN, + (void *) statechange_add_post_op ) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_POST_DELETE_FN, + (void *) statechange_delete_post_op ) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN, + (void *) statechange_close ) != 0 || + slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, + (void *)&pdesc ) != 0 ) + { + slapi_log_error( SLAPI_LOG_FATAL, SCN_PLUGIN_SUBSYSTEM, + "statechange_init: failed to register plugin\n" ); + ret = -1; + } + + slapi_log_error( SLAPI_LOG_TRACE, SCN_PLUGIN_SUBSYSTEM, "<-- statechange_init\n"); + return ret; +} + + +/* + statechange_start + --------- + This function publishes the interface for this plugin +*/ +static int statechange_start( Slapi_PBlock *pb ) +{ + int ret = 0; + + slapi_log_error( SLAPI_LOG_TRACE, SCN_PLUGIN_SUBSYSTEM, "--> statechange_start\n"); + + api[0] = 0; /* reserved for api broker use, must be zero */ + api[1] = (void *)_statechange_register; + api[2] = (void *)_statechange_unregister; + api[3] = (void *)_statechange_unregister_all; + api[4] = (void *)_statechange_vattr_cache_invalidator_callback; + + if(0 == (buffer_lock = slapi_new_mutex())) /* we never free this mutex */ + { + /* badness */ + slapi_log_error( SLAPI_LOG_FATAL, SCN_PLUGIN_SUBSYSTEM, "statechange: failed to create lock\n"); + ret = -1; + } + else + { + if( slapi_apib_register(StateChange_v1_0_GUID, api) ) + { + slapi_log_error( SLAPI_LOG_FATAL, SCN_PLUGIN_SUBSYSTEM, "statechange: failed to publish state change interface\n"); + ret = -1; + } + } + + head = 0; + + slapi_log_error( SLAPI_LOG_TRACE, SCN_PLUGIN_SUBSYSTEM, "<-- statechange_start\n"); + return ret; +} + +/* + statechange_close + --------- + unregisters the interface for this plugin +*/ +static int statechange_close( Slapi_PBlock *pb ) +{ + slapi_log_error( SLAPI_LOG_TRACE, SCN_PLUGIN_SUBSYSTEM, "--> statechange_close\n"); + + slapi_apib_unregister(StateChange_v1_0_GUID); + + slapi_log_error( SLAPI_LOG_TRACE, SCN_PLUGIN_SUBSYSTEM, "<-- statechange_close\n"); + + return 0; +} + + +static int statechange_mod_post_op( Slapi_PBlock *pb ) +{ + return statechange_post_op(pb, LDAP_CHANGETYPE_MODIFY); +} + +static int statechange_modrdn_post_op( Slapi_PBlock *pb ) +{ + return statechange_post_op(pb, LDAP_CHANGETYPE_MODDN); +} + +static int statechange_add_post_op( Slapi_PBlock *pb ) +{ + return statechange_post_op(pb, LDAP_CHANGETYPE_ADD); +} + +static int statechange_delete_post_op( Slapi_PBlock *pb ) +{ + return statechange_post_op(pb, LDAP_CHANGETYPE_DELETE); +} + + +/* + statechange_post_op + ----------- + Catch all for all post operations that change entries + in some way - evaluate the change against the notification + entries and fire off the relevant callbacks - it is called + from the real postop functions which supply it with the + postop type +*/ +static int statechange_post_op( Slapi_PBlock *pb, int modtype ) +{ + SCNotify *notify = head; + int execute; + char *dn = NULL; + struct slapi_entry *e_before = NULL; + struct slapi_entry *e_after = NULL; + + if(head == 0) + return 0; + + slapi_log_error( SLAPI_LOG_TRACE, SCN_PLUGIN_SUBSYSTEM, "--> statechange_post_op\n"); + + /* evaluate this operation against the notification entries */ + + slapi_lock_mutex(buffer_lock); + if(head) + { + if(slapi_pblock_get( pb, SLAPI_TARGET_DN, &dn )) + { + slapi_log_error( SLAPI_LOG_FATAL, SCN_PLUGIN_SUBSYSTEM, "statechange_post_op: failed to get dn of changed entry"); + goto bail; + } + + slapi_dn_normalize( dn ); + + slapi_pblock_get( pb, SLAPI_ENTRY_PRE_OP, &e_before ); + slapi_pblock_get( pb, SLAPI_ENTRY_POST_OP, &e_after ); + + do + { + execute = 0; + + /* first dn */ + if(notify && notify->dn) + { + if(0 != slapi_dn_issuffix(dn, notify->dn)) + execute = 1; + } + else + /* note, if supplied null for everything in the entry *all* ops match */ + if(notify) + execute = 1; + + if(execute && notify->filter) + { + /* next the filter */ + int filter_test = 0; + + /* need to test entry both before and after op */ + if(e_before && !slapi_filter_test_simple( e_before, notify->realfilter)) + filter_test = 1; + + if(!filter_test && e_after && !slapi_filter_test_simple( e_after, notify->realfilter)) + filter_test = 1; + + if(!filter_test) + execute = 0; + } + + if(execute) + { + if(e_after) + (notify->func)(e_after, dn, modtype, pb, notify->caller_data); + else + (notify->func)(e_before, dn, modtype, pb, notify->caller_data); + } + + notify = notify->next; + } + while(notify != head); + } +bail: + slapi_unlock_mutex(buffer_lock); + + slapi_log_error( SLAPI_LOG_TRACE, SCN_PLUGIN_SUBSYSTEM, "<-- statechange_post_op\n"); + return 0; /* always succeed */ +} + + +static int _statechange_register(char *caller_id, char *dn, char *filter, void *caller_data, notify_callback func) +{ + int ret = -1; + SCNotify *item; + + /* simple - we don't check for duplicates */ + + item = (SCNotify*)slapi_ch_malloc(sizeof(SCNotify)); + if(item) + { + char *writable_filter = slapi_ch_strdup(filter); + item->caller_id = slapi_ch_strdup(caller_id); + if(dn) + { + item->dn = slapi_ch_strdup(dn); + slapi_dn_normalize( item->dn ); + } + else + item->dn = 0; + item->filter = slapi_ch_strdup(filter); + item->caller_data = caller_data; + item->realfilter = slapi_str2filter(writable_filter); + item->func = func; + + slapi_lock_mutex(buffer_lock); + if(head == NULL) + { + head = item; + head->next = head; + head->prev = head; + } + else + { + item->next = head; + item->prev = head->prev; + head->prev = item; + item->prev->next = item; + } + slapi_unlock_mutex(buffer_lock); + slapi_ch_free_string(&writable_filter); + + ret = 0; + } + + return ret; +} + +static void *_statechange_unregister(char *dn, char *filter, notify_callback thefunc) +{ + void *ret = NULL; + SCNotify *func = NULL; + + if(buffer_lock == 0) + return ret; + + slapi_lock_mutex(buffer_lock); + + if(func = statechange_find_notify(dn, filter, thefunc)) + { + func->prev->next = func->next; + func->next->prev = func->prev; + + if(func == head) + { + head = func->next; + } + + if(func == head) /* must be the last item, turn off the lights */ + head = 0; + + slapi_ch_free_string(&func->caller_id); + slapi_ch_free_string(&func->dn); + slapi_ch_free_string(&func->filter); + slapi_filter_free( func->realfilter, 1 ); + ret = func->caller_data; + slapi_ch_free((void **)&func); + } + + slapi_unlock_mutex(buffer_lock); + + return ret; +} + +static void _statechange_unregister_all(char *caller_id, caller_data_free_callback callback) +{ + SCNotify *notify = head; + SCNotify *start_notify = head; + + if(buffer_lock == 0) + return; + + slapi_lock_mutex(buffer_lock); + + + if(notify) + { + do + { + SCNotify *notify_next = notify->next; + + if( slapi_utf8casecmp((unsigned char *)caller_id, (unsigned char *)notify->caller_id) ) + { + notify->prev->next = notify->next; + notify->next->prev = notify->prev; + + if(notify == head) + { + head = notify->next; + start_notify = notify->prev; + } + + if(notify == head) /* must be the last item, turn off the lights */ + head = 0; + + if(callback) + callback(notify->caller_data); + slapi_ch_free_string(¬ify->caller_id); + slapi_ch_free_string(¬ify->dn); + slapi_ch_free_string(¬ify->filter); + slapi_filter_free( notify->realfilter, 1 ); + slapi_ch_free((void **)¬ify); + } + + notify = notify_next; + } + while(notify != start_notify && notify != NULL); + } + + slapi_unlock_mutex(buffer_lock); +} + +/* this func needs looking at to make work */ +static SCNotify *statechange_find_notify(char *dn, char *filter, notify_callback func) +{ + SCNotify *notify = head; + SCNotify *start_notify = head; + + if(notify) + { + do + { + if( !slapi_utf8casecmp((unsigned char *)dn, (unsigned char *)notify->dn) && + !slapi_utf8casecmp((unsigned char *)filter, (unsigned char *)notify->filter) && func == notify->func) + { + return notify; + } + + notify = notify->next; + } + while(notify != start_notify); + } + + return 0; +} + +/* intended for use by vattr service providers + * to deal with significant vattr state changes + */ +static void _statechange_vattr_cache_invalidator_callback(Slapi_Entry *e, char *dn, int modtype, Slapi_PBlock *pb, void *caller_data) +{ + /* simply get the significance data and act */ + switch(*(int*)caller_data) + { + case STATECHANGE_VATTR_ENTRY_INVALIDATE: + if(e) + slapi_entry_vattrcache_watermark_invalidate(e); + break; + + case STATECHANGE_VATTR_GLOBAL_INVALIDATE: + default: + slapi_entrycache_vattrcache_watermark_invalidate(); + break; + } +} + |