diff options
Diffstat (limited to 'ldap/servers/plugins/retrocl')
-rw-r--r-- | ldap/servers/plugins/retrocl/Makefile | 135 | ||||
-rw-r--r-- | ldap/servers/plugins/retrocl/dllmain.c | 90 | ||||
-rw-r--r-- | ldap/servers/plugins/retrocl/linktest.c | 16 | ||||
-rw-r--r-- | ldap/servers/plugins/retrocl/retrocl.c | 341 | ||||
-rw-r--r-- | ldap/servers/plugins/retrocl/retrocl.def | 15 | ||||
-rw-r--r-- | ldap/servers/plugins/retrocl/retrocl.h | 123 | ||||
-rw-r--r-- | ldap/servers/plugins/retrocl/retrocl.txt | 107 | ||||
-rw-r--r-- | ldap/servers/plugins/retrocl/retrocl_cn.c | 391 | ||||
-rw-r--r-- | ldap/servers/plugins/retrocl/retrocl_create.c | 317 | ||||
-rw-r--r-- | ldap/servers/plugins/retrocl/retrocl_po.c | 529 | ||||
-rw-r--r-- | ldap/servers/plugins/retrocl/retrocl_rootdse.c | 64 | ||||
-rw-r--r-- | ldap/servers/plugins/retrocl/retrocl_trim.c | 505 |
12 files changed, 2633 insertions, 0 deletions
diff --git a/ldap/servers/plugins/retrocl/Makefile b/ldap/servers/plugins/retrocl/Makefile new file mode 100644 index 00000000..c81f0dfe --- /dev/null +++ b/ldap/servers/plugins/retrocl/Makefile @@ -0,0 +1,135 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +# +# GNU Makefile for Directory Server "Retrocl" plugin + +LDAP_SRC = ../../.. +MCOM_ROOT = ../../../../.. + +NOSTDCLEAN=true # don't let nsconfig.mk define target clean +NOSTDSTRIP=true # don't let nsconfig.mk define target strip +NSPR20=true # probably should be defined somewhere else (not sure where) + +OBJDEST = $(OBJDIR)/lib/retrocl-plugin +BINDIR = $(LDAP_SERVER_RELDIR) +LIBDIR = $(LIB_RELDIR) + +include $(MCOM_ROOT)/ldapserver/nsdefs.mk +include $(MCOM_ROOT)/ldapserver/nsconfig.mk +include $(LDAP_SRC)/nsldap.mk +include $(MCOM_ROOT)/ldapserver/ns_usedb.mk +INCLUDES+=-I$(DB_INCLUDE) + +ifeq ($(ARCH), WINNT) +DEF_FILE:=./retrocl.def +endif + +CFLAGS += $(SLCFLAGS) -DSLAPD_LOGGING + +ifeq ($(ARCH), WINNT) +CFLAGS += /WX +endif + +ifdef TEST_CL5 +CFLAGS += -DTEST_CL5 +endif + +INCLUDES += -I$(LDAP_SRC)/servers/slapd -I$(DB_INCLUDE) + +ifeq ($(ARCH), WINNT) +SUBSYSTEM=console +endif + +LOCAL_OBJS= \ + retrocl.o \ + retrocl_po.o \ + retrocl_rootdse.o \ + retrocl_cn.o \ + retrocl_trim.o \ + retrocl_create.o \ + + + +LIBRETROCL_OBJS = $(addprefix $(OBJDEST)/, $(LOCAL_OBJS)) + +ifeq ($(ARCH), WINNT) +RETROCL_DLL_OBJ = $(addprefix $(OBJDEST)/, dllmain.o) +endif + +LIBRETROCL= $(addprefix $(LIBDIR)/, $(RETROCL_DLL).$(DLL_SUFFIX)) + +LT_OBJS = $(addprefix $(OBJDEST)/, linktest.o) + +#EXTRA_LIBS_DEP = $(LDAPSDK_DEP) \ +# $(LDAP_LIBLDIF_DEP) \ +# $(LDAP_SLIBLCACHE_DEP) $(DB_LIB_DEP) $(LIBSLAPD_DEP) \ +# $(LDAP_COMMON_LIBS_DEP) + +#EXTRA_LIBS = $(LIBACCESS) $(LDAP_SDK_LIBSSLDAP_LIB) $(ADMINUTIL_LINK) \ +# $(LDAP_SDK_LIBLDAP_DLL) $(LDAP_SLIBLCACHE) $(DB_LIB) \ +# $(PLATFORM_SPECIFIC_EXTRA_LIBRARY) $(LIBSLAPD) $(LDAP_LIBLITEKEY) \ +# $(NLSLINK) $(ALIBS) \ +# $(LDAP_SDK_LIBSSLDAP_LIB) $(LDAP_SDK_LIBLDAP_DLL) \ +# $(LIBSECURITYLINK) $(NSPRLINK) $(DBMLINK) \ +# $(THREADSLIB) $(LDAP_COMMON_LIBS) $(NSPRLINK) $(SVRCORELINK) + +ifeq ($(ARCH), WINNT) +EXTRA_LIBS_DEP += $(LIBSLAPD_DEP) +EXTRA_LIBS_DEP += $(LDAPSDK_DEP) $(DB_LIB_DEP) $(NSPR_DEP) +EXTRA_LIBS += $(LIBSLAPD) $(LDAP_SDK_LIBLDAP_DLL) $(DB_LIB) +endif + +ifeq ($(ARCH), AIX) +EXTRA_LIBS_DEP += $(LIBSLAPD_DEP) +EXTRA_LIBS_DEP += $(LDAPSDK_DEP) $(DB_LIB_DEP) $(NSPR_DEP) +EXTRA_LIBS += $(LIBSLAPD) $(LDAP_SDK_LIBLDAP_DLL) $(DB_LIB) +endif + +ifeq ($(ARCH), HPUX) +EXTRA_LIBS_DEP += $(LIBSLAPD_DEP) $(LDAPSDK_DEP) $(NSPR_DEP) $(SECURITY_DEP) +EXTRA_LIBS += $(DYN_NSHTTPD) $(ADMINUTIL_LINK) $(LDAPLINK) $(SECURITYLINK) $(NSPRLINK) $(ICULINK) +endif + +ifeq ($(ARCH), WINNT) +DLL_LDFLAGS += -def:"./retrocl.def" +endif # WINNT + +ifeq ($(ARCH), AIX) +EXTRA_LIBS += $(DLL_EXTRA_LIBS) +LD=ld +endif + +clientSDK: + +all: $(OBJDEST) $(LIBDIR) $(LIBRETROCL) + +linktest: $(LIBRETROCL) $(LT_OBJS) + $(LINK_EXE_NOLIBSOBJS) -o linktest $(LT_OBJS) $(LIBRETROCL) -Rlib -Rlib/../bin/slapd/lib -Llib -Llib/../bin/slapd/lib -lslapd $(EXTRA_LIBS) $(NSPRLINK) + + +$(LIBRETROCL): $(LIBRETROCL_OBJS) $(RETROCL_DLL_OBJ) $(DEF_FILE) + $(LINK_DLL) $(LIBRETROCL_OBJS) $(RETROCL_DLL_OBJ) $(PLATFORMLIBS) $(EXTRA_LIBS) $(LDAP_LIBLDIF) $(NSPRLINK) + +tests: $(TEST_PROGS) + +veryclean: clean + +clean: + $(RM) $(LIBRETROCL_OBJS) +ifeq ($(ARCH), WINNT) + $(RM) $(RETROCL_DLL_OBJ) +endif + $(RM) $(LIBRETROCL) + +$(OBJDEST): + $(MKDIR) $(OBJDEST) + +# +# header file dependencies (incomplete) +# +$(LIBRETROCL_OBJS): diff --git a/ldap/servers/plugins/retrocl/dllmain.c b/ldap/servers/plugins/retrocl/dllmain.c new file mode 100644 index 00000000..276b1fa9 --- /dev/null +++ b/ldap/servers/plugins/retrocl/dllmain.c @@ -0,0 +1,90 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + /* + * Microsoft Windows specifics for LIBRETROCL DLL + */ +#include "ldap.h" + + +#ifdef _WIN32 +/* Lifted from Q125688 + * How to Port a 16-bit DLL to a Win32 DLL + * on the MSVC 4.0 CD + */ +BOOL WINAPI DllMain (HANDLE hModule, DWORD fdwReason, LPVOID lpReserved) +{ + + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: + /* Code from LibMain inserted here. Return TRUE to keep the + DLL loaded or return FALSE to fail loading the DLL. + + You may have to modify the code in your original LibMain to + account for the fact that it may be called more than once. + You will get one DLL_PROCESS_ATTACH for each process that + loads the DLL. This is different from LibMain which gets + called only once when the DLL is loaded. The only time this + is critical is when you are using shared data sections. + If you are using shared data sections for statically + allocated data, you will need to be careful to initialize it + only once. Check your code carefully. + + Certain one-time initializations may now need to be done for + each process that attaches. You may also not need code from + your original LibMain because the operating system may now + be doing it for you. + */ + /* + * 16 bit code calls UnlockData() + * which is mapped to UnlockSegment in windows.h + * in 32 bit world UnlockData is not defined anywhere + * UnlockSegment is mapped to GlobalUnfix in winbase.h + * and the docs for both UnlockSegment and GlobalUnfix say + * ".. function is oboslete. Segments have no meaning + * in the 32-bit environment". So we do nothing here. + */ + + break; + + case DLL_THREAD_ATTACH: + /* Called each time a thread is created in a process that has + already loaded (attached to) this DLL. Does not get called + for each thread that exists in the process before it loaded + the DLL. + + Do thread-specific initialization here. + */ + break; + + case DLL_THREAD_DETACH: + /* Same as above, but called when a thread in the process + exits. + + Do thread-specific cleanup here. + */ + break; + + case DLL_PROCESS_DETACH: + /* Code from _WEP inserted here. This code may (like the + LibMain) not be necessary. Check to make certain that the + operating system is not doing it for you. + */ + + break; + } + /* The return value is only used for DLL_PROCESS_ATTACH; all other + conditions are ignored. */ + return TRUE; /* successful DLL_PROCESS_ATTACH */ +} +#else +int CALLBACK +LibMain( HINSTANCE hinst, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine ) +{ + /*UnlockData( 0 );*/ + return( 1 ); +} +#endif diff --git a/ldap/servers/plugins/retrocl/linktest.c b/ldap/servers/plugins/retrocl/linktest.c new file mode 100644 index 00000000..22812c57 --- /dev/null +++ b/ldap/servers/plugins/retrocl/linktest.c @@ -0,0 +1,16 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* This is a test program. Not linked into the shared library */ + +#include "retrocl.h" + +int main(int a,char **b) +{ + int r; + + r = retrocl_plugin_init(NULL); +} diff --git a/ldap/servers/plugins/retrocl/retrocl.c b/ldap/servers/plugins/retrocl/retrocl.c new file mode 100644 index 00000000..e0d3e325 --- /dev/null +++ b/ldap/servers/plugins/retrocl/retrocl.c @@ -0,0 +1,341 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Requires that create_instance.c have added a plugin entry similar to: + +dn: cn=Retrocl Plugin,cn=plugins,cn=config +objectclass: top +objectclass: nsSlapdPlugin +objectclass: extensibleObject +cn: RetroCL Plugin +nsslapd-pluginpath: /export2/servers/Hydra-supplier/lib/retrocl-plugin.so +nsslapd-plugininitfunc: retrocl_plugin_init +nsslapd-plugintype: object +nsslapd-pluginenabled: on +nsslapd-plugin-depends-on-type: database +nsslapd-pluginid: retrocl +nsslapd-pluginversion: 5.0b2 +nsslapd-pluginvendor: Sun Microsystems, Inc. +nsslapd-plugindescription: Retrocl Plugin + + * + */ + +#include "retrocl.h" + +#ifdef _WIN32 +int *module_ldap_debug = 0; + +void plugin_init_debug_level(int *level_ptr) +{ + module_ldap_debug = level_ptr; +} +#endif + +void* g_plg_identity [PLUGIN_MAX]; + +Slapi_Backend *retrocl_be_changelog = NULL; + +/* ----------------------------- Retrocl Plugin */ + +static Slapi_PluginDesc retrocldesc = {"retrocl", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT, "Retrocl Plugin"}; +static Slapi_PluginDesc retroclpostopdesc = {"retrocl-postop", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT, "retrocl post-operation plugin"}; +static Slapi_PluginDesc retroclinternalpostopdesc = {"retrocl-internalpostop", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT, "retrocl internal post-operation plugin"}; +static Slapi_PluginDesc retroclbepostopdesc = {"retrocl-bepostop", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT, "Retrocl bepost-operation plugin"}; + + +/* + * Function: retrocl_* + * + * Returns: LDAP_ + * + * Arguments: Pb of operation + * + * Description: wrappers around retrocl_postob registered as callback + * + */ + +int retrocl_postop_add (Slapi_PBlock *pb) { return retrocl_postob(pb,OP_ADD);} +int retrocl_postop_delete (Slapi_PBlock *pb) { return retrocl_postob(pb,OP_DELETE);} +int retrocl_postop_modify (Slapi_PBlock *pb) { return retrocl_postob(pb,OP_MODIFY);} +int retrocl_postop_modrdn (Slapi_PBlock *pb) { return retrocl_postob(pb,OP_MODRDN);} + +/* + * Function: retrocl_postop_init + * + * Returns: 0/-1 + * + * Arguments: Pb + * + * Description: callback function + * + */ + +int +retrocl_postop_init( Slapi_PBlock *pb ) +{ + int rc= 0; /* OK */ + + if( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01 ) != 0 || + slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&retroclpostopdesc ) != 0 || + slapi_pblock_set( pb, SLAPI_PLUGIN_POST_ADD_FN, (void *) retrocl_postop_add ) != 0 || + slapi_pblock_set( pb, SLAPI_PLUGIN_POST_DELETE_FN, (void *) retrocl_postop_delete ) != 0 || + slapi_pblock_set( pb, SLAPI_PLUGIN_POST_MODIFY_FN, (void *) retrocl_postop_modify ) != 0 || + slapi_pblock_set( pb, SLAPI_PLUGIN_POST_MODRDN_FN, (void *) retrocl_postop_modrdn ) != 0 ) + { + slapi_log_error( SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME, "retrocl_postop_init failed\n" ); + rc= -1; + } + + return rc; +} + +/* + * Function: retrocl_internalpostop_init + * + * Returns: 0/-1 + * + * Arguments: Pb + * + * Description: callback function + * + */ + +int +retrocl_internalpostop_init( Slapi_PBlock *pb ) +{ + int rc= 0; /* OK */ + + if( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01 ) != 0 || + slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&retroclinternalpostopdesc ) != 0 || + slapi_pblock_set( pb, SLAPI_PLUGIN_INTERNAL_POST_ADD_FN, (void *) retrocl_postop_add ) != 0 || + slapi_pblock_set( pb, SLAPI_PLUGIN_INTERNAL_POST_DELETE_FN, (void *) retrocl_postop_delete ) != 0 || + slapi_pblock_set( pb, SLAPI_PLUGIN_INTERNAL_POST_MODIFY_FN, (void *) retrocl_postop_modify ) != 0 || + slapi_pblock_set( pb, SLAPI_PLUGIN_INTERNAL_POST_MODRDN_FN, (void *) retrocl_postop_modrdn ) != 0 ) + { + slapi_log_error( SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME, "retrocl_internalpostop_init failed\n" ); + rc= -1; + } + + return rc; +} + +/* + * Function: retrocl_rootdse_init + * + * Returns: LDAP_SUCCESS + * + * Arguments: none + * + * Description: The FE DSE *must* be initialised before we get here. + * + */ +static int retrocl_rootdse_init(void) +{ + + int return_value= LDAP_SUCCESS; + + slapi_config_register_callback(SLAPI_OPERATION_SEARCH,DSE_FLAG_PREOP,"", + LDAP_SCOPE_BASE,"(objectclass=*)", + retrocl_rootdse_search,NULL); + return return_value; +} + +/* + * Function: retrocl_select_backend + * + * Returns: LDAP_ + * + * Arguments: none + * + * Description: simulates an add of the changelog to see if it exists. If not, + * creates it. Then reads the changenumbers. This function should be called + * exactly once at startup. + * + */ + +static int retrocl_select_backend(void) +{ + int err; + Slapi_PBlock *pb; + Slapi_Backend *be = NULL; + Slapi_Entry *referral = NULL; + Slapi_Operation *op = NULL; + char errbuf[1024]; + + pb = slapi_pblock_new(); + + slapi_pblock_set (pb, SLAPI_PLUGIN_IDENTITY, g_plg_identity[PLUGIN_RETROCL]); + + /* This is a simulated operation; no actual add is performed */ + op = operation_new(OP_FLAG_INTERNAL); + operation_set_type(op,SLAPI_OPERATION_ADD); /* Ensure be not readonly */ + + operation_set_target_spec_str(op,RETROCL_CHANGELOG_DN); + + slapi_pblock_set(pb,SLAPI_OPERATION, op); + + err = slapi_mapping_tree_select(pb,&be,&referral,errbuf); + slapi_entry_free(referral); + + operation_free(&op,NULL); + + if (err != LDAP_SUCCESS || be == NULL || be == defbackend_get_backend()) { + LDAPDebug(LDAP_DEBUG_TRACE,"Mapping tree select failed (%d) %s.\n", + err,errbuf,0); + + /* could not find the backend for cn=changelog, either because + * it doesn't exist + * mapping tree not registered. + */ + err = retrocl_create_config(); + + if (err != LDAP_SUCCESS) return err; + } else { + retrocl_be_changelog = be; + } + + retrocl_create_cle(); + + return retrocl_get_changenumbers(); +} + +/* + * Function: retrocl_get_config_str + * + * Returns: malloc'ed string which must be freed. + * + * Arguments: attribute type name + * + * Description: reads a single-valued string attr from the plugins' own DSE. + * This is called twice: to obtain the trim max age during startup, and to + * obtain the change log directory. No callback is registered; you cannot + * change the trim max age without restarting the server. + * + */ + +char *retrocl_get_config_str(const char *attrt) +{ + Slapi_Entry **entries; + Slapi_PBlock *pb = NULL; + char *ma; + int rc = 0; + char *dn; + + dn = RETROCL_PLUGIN_DN; + + pb = slapi_pblock_new(); + + slapi_search_internal_set_pb (pb, dn, LDAP_SCOPE_BASE, "objectclass=*", NULL, 0, NULL, + NULL, g_plg_identity[PLUGIN_RETROCL] , 0); + slapi_search_internal_pb (pb); + slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc); + if (rc != 0) { + slapi_pblock_destroy(pb); + return NULL; + } + slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries); + + ma = slapi_entry_attr_get_charptr(entries[0],attrt); + slapi_free_search_results_internal(pb); + slapi_pblock_destroy(pb); + + return ma; +} + +/* + * Function: retrocl_start + * + * Returns: 0 on success + * + * Arguments: Pb + * + * Description: + * + */ + +static int retrocl_start (Slapi_PBlock *pb) +{ + static int retrocl_started = 0; + int rc = 0; + + if (!retrocl_started) { + retrocl_rootdse_init(); + + rc = retrocl_select_backend(); + + if (rc == 0) { + retrocl_init_trimming(); + } else { + LDAPDebug(LDAP_DEBUG_TRACE,"Couldnt find backend, not trimming retro changelog (%d).\n",rc,0,0); + } + } + + retrocl_started = 1; + return rc; +} + +/* + * Function: retrocl_stop + * + * Returns: 0 + * + * Arguments: Pb + * + * Description: called when the server is shutting down + * + */ + +static int retrocl_stop (Slapi_PBlock *pb) +{ + int rc = 0; + + retrocl_stop_trimming(); + retrocl_be_changelog = NULL; + retrocl_forget_changenumbers(); + + return rc; +} + +/* + * Function: retrocl_plugin_init + * + * Returns: 0 on successs + * + * Arguments: Pb + * + * Description: main entry point for retrocl + * + */ + +int +retrocl_plugin_init(Slapi_PBlock *pb) +{ + static int legacy_initialised= 0; + int rc = 0; + void *identity = NULL; + + slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &identity); + PR_ASSERT (identity); + g_plg_identity[PLUGIN_RETROCL] = identity; + + if (!legacy_initialised) { + rc= slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01 ); + rc= slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&retrocldesc ); + rc= slapi_pblock_set( pb, SLAPI_PLUGIN_START_FN, (void *) retrocl_start ); + rc= slapi_pblock_set( pb, SLAPI_PLUGIN_CLOSE_FN, (void *) retrocl_stop ); + + rc= slapi_register_plugin("postoperation", 1 /* Enabled */, "retrocl_postop_init", retrocl_postop_init, "Retrocl postoperation plugin", NULL, identity); + rc= slapi_register_plugin("internalpostoperation", 1 /* Enabled */, "retrocl_internalpostop_init", retrocl_internalpostop_init, "Retrocl internal postoperation plugin", NULL, identity); + } + + legacy_initialised = 1; + return rc; +} + + + diff --git a/ldap/servers/plugins/retrocl/retrocl.def b/ldap/servers/plugins/retrocl/retrocl.def new file mode 100644 index 00000000..ce730c44 --- /dev/null +++ b/ldap/servers/plugins/retrocl/retrocl.def @@ -0,0 +1,15 @@ +; BEGIN COPYRIGHT BLOCK +; Copyright 2001 Sun Microsystems, Inc. +; Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +; All rights reserved. +; END COPYRIGHT BLOCK +; +; + +DESCRIPTION 'Netscape Directory Server 7.0 Retro-Changelog Plugin' +;CODE SHARED READ EXECUTE +;DATA SHARED READ WRITE +EXPORTS + plugin_init_debug_level @1 + retrocl_plugin_init @2 + diff --git a/ldap/servers/plugins/retrocl/retrocl.h b/ldap/servers/plugins/retrocl/retrocl.h new file mode 100644 index 00000000..f96af64c --- /dev/null +++ b/ldap/servers/plugins/retrocl/retrocl.h @@ -0,0 +1,123 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#ifndef _H_RETROCL +#define _H_RETROCL 1 + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "slapi-private.h" +#include "slapi-plugin.h" +/* #include "portable.h" */ +#include "dirver.h" +#include "ldaplog.h" +#include "ldif.h" +#include "slap.h" +#include <dirlite_strings.h> + +/* max len of a long (2^64), represented as a string, including null byte */ +#define CNUMSTR_LEN 21 +typedef unsigned long changeNumber; + +typedef struct _cnum_result_t { + int crt_nentries; /* number of entries returned from search */ + int crt_err; /* err returned from backend */ + Slapi_Entry *crt_entry; /* The entry returned from the backend */ +} cnum_result_t; + +typedef struct _cnumRet { + changeNumber cr_cnum; + char *cr_time; + int cr_lderr; +} cnumRet; + +/* Operation types */ +#define OP_MODIFY 1 +#define OP_ADD 2 +#define OP_DELETE 3 +#define OP_MODRDN 4 + +/* + * How often the changelog trimming thread runs. This is the minimum trim age. + */ +#define CHANGELOGDB_TRIM_INTERVAL 300*1000 /* 5 minutes */ + +#define RETROCL_DLL_DEFAULT_THREAD_STACKSIZE 131072L +#define RETROCL_BE_CACHEMEMSIZE "2097152" +#define RETROCL_BE_CACHESIZE "-1" +#define RETROCL_PLUGIN_NAME "DSRetroclPlugin" + +/* was originally changelogmaximumage */ +#define CONFIG_CHANGELOG_MAXAGE_ATTRIBUTE "nsslapd-changelogmaxage" +#define CONFIG_CHANGELOG_DIRECTORY_ATTRIBUTE "nsslapd-changelogdir" + +#define RETROCL_CHANGELOG_DN "cn=changelog" +#define RETROCL_MAPPINGTREE_DN "cn=\"cn=changelog\",cn=mapping tree,cn=config" +#define RETROCL_PLUGIN_DN "cn=Retro Changelog Plugin,cn=plugins,cn=config" +#define RETROCL_LDBM_DN "cn=changelog,cn=ldbm database,cn=plugins,cn=config" +#define RETROCL_INDEX_DN "cn=changenumber,cn=index,cn=changelog,cn=ldbm database,cn=plugins,cn=config" + +/* Allow anonymous access to the changelog base only, but not to the + * entries in the changelog. + */ +#define RETROCL_ACL "(target =\"ldap:///cn=changelog\")(targetattr != \"aci\")(version 3.0; acl \"changelog base\"; allow( read,search, compare ) userdn =\"ldap:///anyone\";)" + +enum { + PLUGIN_RETROCL, + PLUGIN_MAX +}; + +extern void* g_plg_identity [PLUGIN_MAX]; +extern Slapi_Backend *retrocl_be_changelog; + +extern const char *attr_changenumber; +extern const char *attr_targetdn; +extern const char *attr_changetype; +extern const char *attr_newrdn; +extern const char *attr_newsuperior; +extern const char *attr_deleteoldrdn; +extern const char *attr_changes; +extern const char *attr_changetime; +extern const char *attr_objectclass; + +extern PRLock *retrocl_internal_lock; + +/* Functions */ + +/* from repl_shared.h: not sure where defined */ +unsigned long strntoul( char *from, size_t len, int base ); + +extern int retrocl_plugin_init(Slapi_PBlock *pb); + +extern int retrocl_bepostop_delete (Slapi_PBlock *pb); +extern int retrocl_postop_add (Slapi_PBlock *pb); +extern int retrocl_postop_delete (Slapi_PBlock *pb); +extern int retrocl_postop_modify (Slapi_PBlock *pb); +extern int retrocl_postop_modrdn (Slapi_PBlock *pb); +extern int retrocl_postob(Slapi_PBlock *,int); + +extern int retrocl_rootdse_search (Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg); + +extern void retrocl_create_cle(void); +extern int retrocl_create_config(void); + +extern changeNumber retrocl_get_first_changenumber(void); +extern void retrocl_set_first_changenumber(changeNumber cn); +extern changeNumber retrocl_get_last_changenumber(void); +extern void retrocl_commit_changenumber(void); +extern void retrocl_release_changenumber(void); +extern changeNumber retrocl_assign_changenumber(void); +extern int retrocl_get_changenumbers(void); +extern void retrocl_forget_changenumbers(void); +extern time_t retrocl_getchangetime( int type, int *err ); + +extern void retrocl_init_trimming(void); +extern void retrocl_stop_trimming(void); +extern char *retrocl_get_config_str(const char *attrt); + +#endif /* _H_RETROCL */ diff --git a/ldap/servers/plugins/retrocl/retrocl.txt b/ldap/servers/plugins/retrocl/retrocl.txt new file mode 100644 index 00000000..e82368e8 --- /dev/null +++ b/ldap/servers/plugins/retrocl/retrocl.txt @@ -0,0 +1,107 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# + +Changelog user documentation +Last Updated October 6, 2000 + +1. Introduction + +This document describes a how DS 6.0 provides a change log broadly +compatible with the Internet Draft draft-good-ldap-changelog-01.txt. + +When enabled, the change log appears in the DIT below cn=changelog. It +consists of a single level of entries, each of class changeLogEntry. This +object class allows the following attributes: + - changeNumber. This attribute is always present and contains a single + value, an integer which is unique for each change. The value for later + changes is larger than those of any change which is already present. + - targetDN. This attribute contains the distinguished name of the entry + which was added, modified or deleted. In the case of a ModifyDN operation, + the targetDN attribute contains the DN of the entry before it was renamed + or moved. + - changeType. This attribute contains one of the following values: "add", + "delete", "modify" or "modrdn". + - changes. This attribute contains the changes made to the entry, in LDIF + format, for a add or modify change. + - newRDN. This attribute contains the new RDN of the entry, for a modifyDN + change. + - deleteOldRDN. This attribute contains whether the old RDN of the entry + was deleted, for a modifyDN change. + - newSuperior. This attribute contains the newSuperior field of the entry, + for a modifyDN change. + +The change log is implemented in an LDBM database. + +2. Configuration + +To enable the change log, the following steps should be performed. First, +change the nsslapd-pluginenabled attribute of the DSE cn=Retrocl Plugin, +cn=plugins,cn=config to "on" instead of "off", Then start or restart the +server. The server will automatically create the change log database. + +3. Trimming + +The entries in the change log may be automatically removed if they are older +than a specified period of time. This is done by setting the +changelogmaximumage attribute in the change log plugin DSE cn=Retrocl Plugin, +cn=plugins,cn=config and restarting the server. If this attribute is not +present, then changed are not trimmed. + +The changelogmaximumage attribute is single-valued, and its value consists of +two parts: a number and a time units code. The time units codes are: + - 's' for seconds, + - 'm' for minutes, + - 'h' for hours, + - 'd' for days, + - 'w' for weeks. + +For example, + +changelogmaximumage: 2d + +The minimum value is 5 minutes. + +4. Access Control + +When the changelog backend is created, the default access control is to allow +anonymous read, search and compare to the changelog base entry, cn=changelog, +by anyone. No access is granted, except implicitly to the Directory Manager, +to any of the entries in the change log. + +Read access to the entries in the change log should not be granted to anonymous +users, as the changes attribute could contain modifications to sensitive +attribute values (such as passwords). Only authenticated services should be +allowed to access this information. + +5. Protocol interaction + +All search and compare operations are supported on the change log database. +Search operations whose filter is of the form +(&(changenumber>=X)(changeNumber<=Y) are optimized. + +Add or modify operations should not be performed on change log entries in the +change log database. Change log entries can be deleted if desired. The +change log base entry, cn=changelog, can be modified if desired, to vary the +access control policy of the change log database. + +6. Caveats + +The change log does not currently record changes which are internally +constructed to resolve conflicts during multi-master replication. As a +result, the change log should not be used in deployments which use multi-master +replication with more than two masters or suppliers for a database. + +== + +root dse firstchangenumber and lastchangenumber + +changelogdir attribute + +test chaining be +if changelog db deleted - what happens? +cannot change trim max age without restarting the server diff --git a/ldap/servers/plugins/retrocl/retrocl_cn.c b/ldap/servers/plugins/retrocl/retrocl_cn.c new file mode 100644 index 00000000..3623f4b3 --- /dev/null +++ b/ldap/servers/plugins/retrocl/retrocl_cn.c @@ -0,0 +1,391 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include "retrocl.h" + +static changeNumber retrocl_internal_cn = 0; +static changeNumber retrocl_first_cn = 0; +PRLock *retrocl_internal_lock = NULL; + +/* + * Function: a2changeNumber + * + * Returns: changeNumber (long) + * + * Arguments: string + * + * Description: parses the string to a changenumber. changenumbers are + * positive integers. + * + */ + +static changeNumber a2changeNumber (const char *p) +{ + changeNumber c; + + c = strntoul((char *)p,strlen(p),10); + return c; +} + +/* + * Function: handle_cnum_entry + * Arguments: op - pointer to Operation struct for this operation + * e - pointer to returned entry. + * Returns: nothing + * Description: Search result handler for retrocl_getchangenum(). Sets the + * op->o_handler_data to point to a structure which contains + * the changenumber retrieved and an error code. + */ +static int +handle_cnum_entry( Slapi_Entry *e, void *callback_data ) +{ + cnumRet *cr = (cnumRet *)callback_data; + Slapi_Value *sval=NULL; + const struct berval *value; + + cr->cr_cnum = 0UL; + cr->cr_time = NULL; + + if ( NULL != e ) { + Slapi_Attr *chattr = NULL; + sval = NULL; + value = NULL; + if ( slapi_entry_attr_find( e, attr_changenumber, &chattr ) == 0 ) { + slapi_attr_first_value( chattr,&sval ); + if ( NULL != sval ) { + value = slapi_value_get_berval ( sval ); + if( NULL != value && NULL != value->bv_val && + '\0' != value->bv_val[0]) { + cr->cr_cnum = a2changeNumber( value->bv_val ); + } + } + } + chattr = NULL; + sval = NULL; + value = NULL; + + chattr = NULL; + sval = NULL; + value = NULL; + if ( slapi_entry_attr_find( e, attr_changetime, &chattr ) == 0 ) { + slapi_attr_first_value( chattr,&sval ); + if ( NULL != sval) { + value = slapi_value_get_berval ( sval ); + if (NULL != value && NULL != value->bv_val && + '\0' != value->bv_val[0]) { + cr->cr_time = slapi_ch_strdup( value->bv_val ); + } + } + } + } + return 0; +} + + +/* + * Function: handle_cnum_result + * Arguments: err - error code returned from search + * callback_data - private data for callback + * Returns: nothing + * Description: result handler for retrocl_getchangenum(). Sets the cr_lderr + * field of the cnumRet struct to the error returned + * from the backend. + */ +static void +handle_cnum_result( int err, void *callback_data ) +{ + cnumRet *cr = (cnumRet *)callback_data; + cr->cr_lderr = err; +} + +/* + * Function: retrocl_get_changenumbers + * + * Returns: 0/-1 + * + * Arguments: none + * + * Description: reads the first and last entry in the changelog to obtain + * the starting and ending change numbers. + * + */ + +int retrocl_get_changenumbers(void) +{ + cnumRet cr; + + if (retrocl_internal_lock == NULL) { + retrocl_internal_lock = PR_NewLock(); + + if (retrocl_internal_lock == NULL) return -1; + } + + if (retrocl_be_changelog == NULL) return -1; + + cr.cr_cnum = 0; + cr.cr_time = 0; + + slapi_seq_callback(RETROCL_CHANGELOG_DN,SLAPI_SEQ_FIRST, + (char *)attr_changenumber, /* cast away const */ + NULL,NULL,0,&cr,NULL,handle_cnum_result, + handle_cnum_entry, NULL); + + retrocl_first_cn = cr.cr_cnum; + + slapi_ch_free(( void **) &cr.cr_time ); + + slapi_seq_callback(RETROCL_CHANGELOG_DN,SLAPI_SEQ_LAST, + (char *)attr_changenumber, /* cast away const */ + NULL,NULL,0,&cr,NULL,handle_cnum_result, + handle_cnum_entry, NULL); + + retrocl_internal_cn = cr.cr_cnum; + + slapi_log_error(SLAPI_LOG_PLUGIN,"retrocl","Got changenumbers %d and %d\n", + retrocl_first_cn, + retrocl_internal_cn); + + slapi_ch_free(( void **) &cr.cr_time ); + + return 0; +} + +/* + * Function: retrocl_getchangetime + * Arguments: type - one of SLAPI_SEQ_FIRST, SLAPI_SEQ_LAST + * Returns: The time of the requested change record. If the return value is + * NO_TIME, the changelog could not be read. + * If err is non-NULL, the memory it points to is set the the + * error code returned from the backend. If "type" is not valid, + * *err is set to -1. + * Description: Get the first or last changenumber stored in the changelog, + * depending on the value of argument "type". + */ +time_t retrocl_getchangetime( int type, int *err ) +{ + cnumRet cr; + time_t ret; + + if ( type != SLAPI_SEQ_FIRST && type != SLAPI_SEQ_LAST ) { + if ( err != NULL ) { + *err = -1; + } + return NO_TIME; + } + memset( &cr, '\0', sizeof( cnumRet )); + slapi_seq_callback( RETROCL_CHANGELOG_DN, type, + (char *)attr_changenumber, /* cast away const */ + NULL, + NULL, 0, &cr, NULL, + handle_cnum_result, handle_cnum_entry, NULL ); + + if ( err != NULL ) { + *err = cr.cr_lderr; + } + + if ( NULL == cr.cr_time ) { + ret = NO_TIME; + } else { + ret = parse_localTime( cr.cr_time ); + } + slapi_ch_free(( void **) &cr.cr_time ); + return ret; +} + +/* + * Function: retrocl_forget_changenumbers + * + * Returns: none + * + * Arguments: none + * + * Description: used only when the server is shutting down + * + */ + +void retrocl_forget_changenumbers(void) +{ + if (retrocl_internal_lock == NULL) return; + + PR_Lock(retrocl_internal_lock); + retrocl_first_cn = 0; + retrocl_internal_cn = 0; + PR_Unlock(retrocl_internal_lock); +} + +/* + * Function: retrocl_get_first_changenumber + * + * Returns: changeNumber + * + * Arguments: none + * + * Description: used in root DSE + * + */ + +changeNumber retrocl_get_first_changenumber(void) +{ + changeNumber cn; + PR_Lock(retrocl_internal_lock); + cn = retrocl_first_cn; + PR_Unlock(retrocl_internal_lock); + return cn; +} + +/* + * Function: retrocl_set_first_changenumber + * + * Returns: none + * + * Arguments: changenumber + * + * Description: used in changelog trimming + * + */ + +void retrocl_set_first_changenumber(changeNumber cn) +{ + PR_Lock(retrocl_internal_lock); + retrocl_first_cn = cn; + PR_Unlock(retrocl_internal_lock); +} + + +/* + * Function: retrocl_get_last_changenumber + * + * Returns: + * + * Arguments: + * + * Description: used in root DSE + * + */ + +changeNumber retrocl_get_last_changenumber(void) +{ + changeNumber cn; + PR_Lock(retrocl_internal_lock); + cn = retrocl_internal_cn; + PR_Unlock(retrocl_internal_lock); + return cn; +} + +/* + * Function: retrocl_commit_changenumber + * + * Returns: none + * + * Arguments: none, lock must be held + * + * Description: NOTE! MUST BE PRECEEDED BY retrocl_assign_changenumber + * + */ + +void retrocl_commit_changenumber(void) +{ + if ( retrocl_first_cn == 0) { + retrocl_first_cn = retrocl_internal_cn; + } +} + +/* + * Function: retrocl_release_changenumber + * + * Returns: none + * + * Arguments: none, lock must be held + * + * Description: NOTE! MUST BE PRECEEDED BY retrocl_assign_changenumber + * + */ + +void retrocl_release_changenumber(void) +{ + retrocl_internal_cn--; +} + +/* + * Function: retrocl_update_lastchangenumber + * + * Returns: 0/-1 + * + * Arguments: none + * + * Description: reads the last entry in the changelog to obtain + * the last change number. + * + */ + +int retrocl_update_lastchangenumber(void) +{ + cnumRet cr; + + if (retrocl_internal_lock == NULL) { + retrocl_internal_lock = PR_NewLock(); + + if (retrocl_internal_lock == NULL) return -1; + } + + if (retrocl_be_changelog == NULL) return -1; + + cr.cr_cnum = 0; + cr.cr_time = 0; + slapi_seq_callback(RETROCL_CHANGELOG_DN,SLAPI_SEQ_LAST, + (char *)attr_changenumber, /* cast away const */ + NULL,NULL,0,&cr,NULL,handle_cnum_result, + handle_cnum_entry, NULL); + + + retrocl_internal_cn = cr.cr_cnum; + slapi_log_error(SLAPI_LOG_PLUGIN,"retrocl","Refetched last changenumber = %d \n", + retrocl_internal_cn); + + slapi_ch_free(( void **) &cr.cr_time ); + + return 0; +} + + + +/* + * Function: retrocl_assign_changenumber + * + * Returns: change number, 0 on error + * + * Arguments: none. Lock must be held. + * + * Description: NOTE! MUST BE FOLLOWED BY retrocl_commit_changenumber or + * retrocl_release_changenumber + * + */ + +changeNumber retrocl_assign_changenumber(void) +{ + changeNumber cn; + + if (retrocl_internal_lock == NULL) return 0; + + /* Before we assign the changenumber; we should check for the + * validity of the internal assignment of retrocl_internal_cn + * we had from the startup */ + + if(retrocl_internal_cn <= retrocl_first_cn){ + /* the numbers have become out of sync - retrocl_get_changenumbers + * gets called only once during startup and it may have had a problem + * getting the last changenumber. + * If there was any problem then update the lastchangenumber from the changelog db. + * This function is being called by only the thread that is actually writing + * to the changelog. + */ + retrocl_update_lastchangenumber(); + } + + retrocl_internal_cn++; + cn = retrocl_internal_cn; + return cn; +} diff --git a/ldap/servers/plugins/retrocl/retrocl_create.c b/ldap/servers/plugins/retrocl/retrocl_create.c new file mode 100644 index 00000000..fbda8e36 --- /dev/null +++ b/ldap/servers/plugins/retrocl/retrocl_create.c @@ -0,0 +1,317 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include "retrocl.h" + +/* +The changelog is created by + - changing the node in the dse tree which represents the changelog plugin + to enabled on, + - shutting down the server, + - starting the server. + */ + +/******************************/ + +/* + * Function: retrocl_create_be + * + * Returns: LDAP_ + * + * Arguments: location in file system to put changelog, or NULL for default + * + * Description: + * add an entry of class nsBackendInstance below cn=ldbm,cn=plugins,cn=config + * + */ + +static int retrocl_create_be(const char *bedir) +{ + Slapi_PBlock *pb = NULL; + Slapi_Entry *e; + struct berval *vals[2]; + struct berval val; + int rc; + + vals[0] = &val; + vals[1] = NULL; + + e = slapi_entry_alloc(); + slapi_entry_set_dn(e,slapi_ch_strdup(RETROCL_LDBM_DN)); + + /* Set the objectclass attribute */ + val.bv_val = "top"; + val.bv_len = 3; + slapi_entry_add_values( e, "objectclass", vals ); + + /* Set the objectclass attribute */ + val.bv_val = "extensibleObject"; + val.bv_len = strlen(val.bv_val); + slapi_entry_add_values( e, "objectclass", vals ); + + /* Set the objectclass attribute */ + val.bv_val = "nsBackendInstance"; + val.bv_len = strlen(val.bv_val); + slapi_entry_add_values( e, "objectclass", vals ); + + val.bv_val = "changelog"; + val.bv_len = strlen(val.bv_val); + slapi_entry_add_values( e, "cn", vals ); + + val.bv_val = RETROCL_BE_CACHESIZE; + val.bv_len = strlen(val.bv_val); + slapi_entry_add_values( e, "nsslapd-cachesize", vals ); + + val.bv_val = RETROCL_CHANGELOG_DN; + val.bv_len = strlen(val.bv_val); + slapi_entry_add_values( e, "nsslapd-suffix", vals ); + + val.bv_val = RETROCL_BE_CACHEMEMSIZE; + val.bv_len = strlen(val.bv_val); + slapi_entry_add_values( e, "nsslapd-cachememsize", vals ); + + val.bv_val = "off"; + val.bv_len = strlen(val.bv_val); + slapi_entry_add_values( e, "nsslapd-readonly", vals ); + + if (bedir) { + val.bv_val = (char *)bedir; /* cast const */ + val.bv_len = strlen(val.bv_val); + slapi_entry_add_values( e, "nsslapd-directory", vals ); + } + + pb = slapi_pblock_new (); + slapi_add_entry_internal_set_pb( pb, e, NULL /* controls */, + g_plg_identity[PLUGIN_RETROCL], + 0 /* actions */ ); + slapi_add_internal_pb (pb); + slapi_pblock_get( pb, SLAPI_PLUGIN_INTOP_RESULT, &rc ); + slapi_pblock_destroy(pb); + + if (rc == 0) { + slapi_log_error (SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME, + "created changelog database node\n"); + } else if (rc == LDAP_ALREADY_EXISTS) { + slapi_log_error (SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME, + "changelog database node already existed\n"); + } else { + slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME, "Changelog LDBM backend could not be created (%d)\n", rc); + return rc; + } + + + /* we need the changenumber indexed */ + e = slapi_entry_alloc(); + slapi_entry_set_dn(e,slapi_ch_strdup(RETROCL_INDEX_DN)); + + /* Set the objectclass attribute */ + val.bv_val = "top"; + val.bv_len = 3; + slapi_entry_add_values( e, "objectclass", vals ); + + /* Set the objectclass attribute */ + val.bv_val = "nsIndex"; + val.bv_len = strlen(val.bv_val); + slapi_entry_add_values( e, "objectclass", vals ); + + val.bv_val = "changenumber"; + val.bv_len = strlen(val.bv_val); + slapi_entry_add_values( e, "cn", vals ); + + val.bv_val = "false"; + val.bv_len = strlen(val.bv_val); + slapi_entry_add_values( e, "nssystemindex", vals ); + + val.bv_val = "eq"; + val.bv_len = strlen(val.bv_val); + slapi_entry_add_values( e, "nsindextype", vals ); + + pb = slapi_pblock_new (); + slapi_add_entry_internal_set_pb( pb, e, NULL /* controls */, + g_plg_identity[PLUGIN_RETROCL], + 0 /* actions */ ); + slapi_add_internal_pb (pb); + slapi_pblock_get( pb, SLAPI_PLUGIN_INTOP_RESULT, &rc ); + slapi_pblock_destroy(pb); + + if (rc == 0) { + slapi_log_error (SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME, + "created changenumber index node\n"); + } else if (rc == LDAP_ALREADY_EXISTS) { + slapi_log_error (SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME, + "changelog index node already existed\n"); + } else { + slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME, "Changelog LDBM backend changenumber index could not be created (%d)\n", rc); + return rc; + } + + return rc; +} + +/* + * Function: retrocl_create_config + * + * Returns: LDAP_ + * + * Arguments: none + * + * Description: + * This function is called if there was no mapping tree node or backend for + * cn=changelog. + */ +int retrocl_create_config(void) +{ + Slapi_PBlock *pb = NULL; + Slapi_Entry *e; + struct berval *vals[2]; + struct berval val; + int rc; + + vals[0] = &val; + vals[1] = NULL; + + /* Assume the mapping tree node is missing. It doesn't hurt to + * attempt to add it if it already exists. You will see a warning + * in the errors file when the referenced backend does not exist. + */ + e = slapi_entry_alloc(); + slapi_entry_set_dn(e,slapi_ch_strdup(RETROCL_MAPPINGTREE_DN)); + + /* Set the objectclass attribute */ + val.bv_val = "top"; + val.bv_len = 3; + slapi_entry_add_values( e, "objectclass", vals ); + + + /* Set the objectclass attribute */ + val.bv_val = "extensibleObject"; + val.bv_len = strlen(val.bv_val); + slapi_entry_add_values( e, "objectclass", vals ); + + /* Set the objectclass attribute */ + val.bv_val = "nsMappingTree"; + val.bv_len = strlen(val.bv_val); + slapi_entry_add_values( e, "objectclass", vals ); + + val.bv_val = "backend"; + val.bv_len = strlen(val.bv_val); + slapi_entry_add_values( e, "nsslapd-state", vals ); + + val.bv_val = RETROCL_CHANGELOG_DN; + val.bv_len = strlen(val.bv_val); + slapi_entry_add_values( e, "cn", vals ); + + val.bv_val = "changelog"; + val.bv_len = strlen(val.bv_val); + slapi_entry_add_values( e, "nsslapd-backend", vals ); + + pb = slapi_pblock_new (); + slapi_add_entry_internal_set_pb( pb, e, NULL /* controls */, + g_plg_identity[PLUGIN_RETROCL], + 0 /* actions */ ); + slapi_add_internal_pb (pb); + slapi_pblock_get( pb, SLAPI_PLUGIN_INTOP_RESULT, &rc ); + slapi_pblock_destroy(pb); + + if (rc == 0) { + slapi_log_error (SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME, + "created changelog mapping tree node\n"); + } else if (rc == LDAP_ALREADY_EXISTS) { + slapi_log_error (SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME, + "changelog mapping tree node already existed\n"); + } else { + slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME, "cn=\"cn=changelog\",cn=mapping tree,cn=config could not be created (%d)\n", rc); + return rc; + } + + retrocl_be_changelog = slapi_be_select_by_instance_name("changelog"); + + if (retrocl_be_changelog == NULL) { + /* This is not the nsslapd-changelogdir from cn=changelog4,cn=config */ + char *bedir; + + bedir = retrocl_get_config_str(CONFIG_CHANGELOG_DIRECTORY_ATTRIBUTE); + + if (bedir == NULL) { + /* none specified */ + } + + rc = retrocl_create_be(bedir); + slapi_ch_free ((void **)&bedir); + if (rc != LDAP_SUCCESS && rc != LDAP_ALREADY_EXISTS) { + return rc; + } + + retrocl_be_changelog = slapi_be_select_by_instance_name("changelog"); + } + + return LDAP_SUCCESS; +} + +/******************************/ + +/* Function: retrocl_create_cle + * + * Arguments: none + * Returns: nothing + * Description: Attempts to create the cn=changelog entry which might already + * exist. + */ + +void retrocl_create_cle (void) +{ + Slapi_PBlock *pb = NULL; + Slapi_Entry *e; + int rc; + struct berval *vals[2]; + struct berval val; + + vals[0] = &val; + vals[1] = NULL; + + e = slapi_entry_alloc(); + slapi_entry_set_dn(e,slapi_ch_strdup(RETROCL_CHANGELOG_DN)); + + /* Set the objectclass attribute */ + val.bv_val = "top"; + val.bv_len = strlen(val.bv_val); + slapi_entry_add_values( e, "objectclass", vals ); + + + /* Set the objectclass attribute */ + val.bv_val = "nsContainer"; + val.bv_len = strlen(val.bv_val); + slapi_entry_add_values( e, "objectclass", vals ); + + + /* Set the objectclass attribute */ + val.bv_val = "changelog"; + val.bv_len = strlen(val.bv_val); + slapi_entry_add_values( e, "cn", vals ); + + val.bv_val = RETROCL_ACL; + val.bv_len = strlen(val.bv_val); + slapi_entry_add_values( e, "aci", vals ); + + pb = slapi_pblock_new (); + slapi_add_entry_internal_set_pb( pb, e, NULL /* controls */, + g_plg_identity[PLUGIN_RETROCL], + 0 /* actions */ ); + slapi_add_internal_pb (pb); + slapi_pblock_get( pb, SLAPI_PLUGIN_INTOP_RESULT, &rc ); + slapi_pblock_destroy(pb); + + if (rc == 0) { + slapi_log_error (SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME, + "created cn=changelog\n"); + } else if (rc == LDAP_ALREADY_EXISTS) { + slapi_log_error (SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME, + "cn=changelog already existed\n"); + } else { + slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME, "cn=changelog could not be created (%d)\n", rc); + } +} + diff --git a/ldap/servers/plugins/retrocl/retrocl_po.c b/ldap/servers/plugins/retrocl/retrocl_po.c new file mode 100644 index 00000000..96512a51 --- /dev/null +++ b/ldap/servers/plugins/retrocl/retrocl_po.c @@ -0,0 +1,529 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include "retrocl.h" + +static int +entry2reple( Slapi_Entry *e, Slapi_Entry *oe ); + +static int +mods2reple( Slapi_Entry *e, LDAPMod **ldm ); + +static int +modrdn2reple( Slapi_Entry *e, const char *newrdn, int deloldrdn, + LDAPMod **ldm, const char *newsup ); + +/******************************/ + +const char *attr_changenumber = "changenumber"; +const char *attr_targetdn = "targetdn"; +const char *attr_changetype = "changetype"; +const char *attr_newrdn = "newrdn"; +const char *attr_deleteoldrdn = "deleteoldrdn"; +const char *attr_changes = "changes"; +const char *attr_newsuperior = "newsuperior"; +const char *attr_changetime = "changetime"; +const char *attr_objectclass = "objectclass"; + +/* + * Function: make_changes_string + * + * Returns: + * + * Arguments: + * + * Description: + * loop through the LDAPMod struct and construct the changes attribute/ + * + */ + +static lenstr *make_changes_string(LDAPMod **ldm, const char **includeattrs) +{ + lenstr *l; + int i, j, len; + int skip; + + l = lenstr_new(); + + for ( i = 0; ldm[ i ] != NULL; i++ ) { + /* If a list of explicit attributes was given, only add those */ + if ( NULL != includeattrs ) { + skip = 1; + for ( j = 0; includeattrs[ j ] != NULL; j++ ) { + if ( strcasecmp( includeattrs[ j ], ldm[ i ]->mod_type ) == 0 ) { + skip = 0; + break; + } + } + if ( skip ) { + continue; + } + } + switch ( ldm[ i ]->mod_op & ~LDAP_MOD_BVALUES ) { + case LDAP_MOD_ADD: + addlenstr( l, "add: " ); + addlenstr( l, ldm[ i ]->mod_type ); + addlenstr( l, "\n" ); + break; + case LDAP_MOD_DELETE: + addlenstr( l, "delete: " ); + addlenstr( l, ldm[ i ]->mod_type ); + addlenstr( l, "\n" ); + break; + case LDAP_MOD_REPLACE: + addlenstr( l, "replace: " ); + addlenstr( l, ldm[ i ]->mod_type ); + addlenstr( l, "\n" ); + break; + } + for ( j = 0; ldm[ i ]->mod_bvalues != NULL && + ldm[ i ]->mod_bvalues[ j ] != NULL; j++ ) { + char *buf = NULL; + char *bufp = NULL; + + len = strlen( ldm[ i ]->mod_type ); + len = LDIF_SIZE_NEEDED( len, + ldm[ i ]->mod_bvalues[ j ]->bv_len ) + 1; + buf = slapi_ch_malloc( len ); + bufp = buf; + ldif_put_type_and_value( &bufp, ldm[ i ]->mod_type, + ldm[ i ]->mod_bvalues[ j ]->bv_val, + ldm[ i ]->mod_bvalues[ j ]->bv_len ); + *bufp = '\0'; + + addlenstr( l, buf ); + + free( buf ); + } + addlenstr( l, "-\n" ); + } + return l; +} + +/* + * Function: write_replog_db + * Arguments: be - backend to which this change is being applied + * optype - type of LDAP operation being logged + * dn - distinguished name of entry being changed + * log_m - pointer to the actual change operation on a modify + * flag - only used by modrdn operations - value of deleteoldrdn + * curtime - the current time + * Returns: nothing + * Description: Given a change, construct an entry which is to be added to the + * changelog database. + */ +static void +write_replog_db( + int optype, + char *dn, + LDAPMod **log_m, + int flag, + time_t curtime, + Slapi_Entry *log_e, + const char *newrdn, + LDAPMod **modrdn_mods, + const char *newsuperior +) +{ + char *pat, *edn; + struct berval *vals[ 2 ]; + struct berval val; + Slapi_Entry *e; + char chnobuf[ 20 ]; + int err; + Slapi_PBlock *pb = NULL; + changeNumber changenum; + + PR_Lock(retrocl_internal_lock); + changenum = retrocl_assign_changenumber(); + + PR_ASSERT( changenum > 0UL ); + slapi_log_error( SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME, + "write_replog_db: write change record %d for dn: \"%s\"\n", + changenum, ( dn == NULL ) ? "NULL" : dn ); + + /* Construct the dn of this change record */ + pat = "%s=%lu,%s"; + edn = slapi_ch_malloc( strlen( pat ) + strlen( RETROCL_CHANGELOG_DN) + 20 ); + sprintf( edn, pat, attr_changenumber, changenum, RETROCL_CHANGELOG_DN); + + /* + * Create the entry struct, and fill in fields common to all types + * of change records. + */ + vals[ 0 ] = &val; + vals[ 1 ] = NULL; + + e = slapi_entry_alloc(); + slapi_entry_set_dn( e, slapi_ch_strdup( edn )); + + /* Set the objectclass attribute */ + val.bv_val = "top"; + val.bv_len = 3; + slapi_entry_add_values( e, "objectclass", vals ); + + val.bv_val = "changelogentry"; + val.bv_len = 14; + slapi_entry_add_values( e, "objectclass", vals ); + + /* Set the changeNumber attribute */ + sprintf( chnobuf, "%lu", changenum ); + val.bv_val = chnobuf; + val.bv_len = strlen( chnobuf ); + slapi_entry_add_values( e, attr_changenumber, vals ); + + /* Set the targetentrydn attribute */ + val.bv_val = dn; + val.bv_len = strlen( dn ); + slapi_entry_add_values( e, attr_targetdn, vals ); + + /* Set the changeTime attribute */ + val.bv_val = format_genTime (curtime); + val.bv_len = strlen( val.bv_val ); + slapi_entry_add_values( e, attr_changetime, vals ); + free( val.bv_val ); + + /* + * Finish constructing the entry. How to do it depends on the type + * of modification being logged. + */ + err = 0; + switch ( optype ) { + case OP_ADD: + if ( entry2reple( e, log_e ) != 0 ) { + err = 1; + } + break; + + case OP_MODIFY: + if ( mods2reple( e, log_m ) != 0 ) { + err = 1; + } + break; + + case OP_MODRDN: + if ( modrdn2reple( e, newrdn, flag, modrdn_mods, newsuperior ) != 0 ) { + err = 1; + } + break; + + case OP_DELETE: + /* Set the changetype attribute */ + val.bv_val = "delete"; + val.bv_len = 6; + slapi_entry_add_values( e, attr_changetype, vals ); + break; + default: + slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME, "replog: Unknown LDAP operation type " + "%d.\n", optype ); + err = 1; + } + + /* Call the repl backend to add this entry */ + if ( 0 == err ) { + int rc; + + pb = slapi_pblock_new (); + slapi_add_entry_internal_set_pb( pb, e, NULL /* controls */, + g_plg_identity[PLUGIN_RETROCL], + 0 /* actions */ ); + slapi_add_internal_pb (pb); + slapi_pblock_get( pb, SLAPI_PLUGIN_INTOP_RESULT, &rc ); + slapi_pblock_destroy(pb); + if ( 0 != rc ) { + slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME, + "replog: an error occured while adding change " + "number %d, dn = %s: %s. \n", + changenum, edn, ldap_err2string( rc )); + retrocl_release_changenumber(); + } else { + /* Tell the change numbering system this one's committed to disk */ + retrocl_commit_changenumber( ); + } + } else { + slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME, + "An error occurred while constructing " + "change record number %ld.\n", changenum ); + retrocl_release_changenumber(); + } + PR_Unlock(retrocl_internal_lock); + if ( NULL != edn ) { + slapi_ch_free((void **) &edn); + } + +} + + +/* + * Function: entry2reple + * Arguments: e - a partially-constructed Slapi_Entry structure + * oe - the original entry (an entry being added by a client). + * Returns: 0 on success. + * Description: Given an Slapi_Entry struct, construct a changelog entry which will be + * added to the replication database. It is assumed that e points + * to an entry obtained from slapi_entry_alloc(). + */ +static int +entry2reple( Slapi_Entry *e, Slapi_Entry *oe ) +{ + char *p, *estr; + struct berval *vals[ 2 ]; + struct berval val; + int len; + + vals[ 0 ] = &val; + vals[ 1 ] = NULL; + + /* Set the changetype attribute */ + val.bv_val = "add"; + val.bv_len = 3; + slapi_entry_add_values( e, attr_changetype, vals ); + + estr = slapi_entry2str( oe, &len ); + p = estr; + /* Skip over the dn: line */ + while (( p = strchr( p, '\n' )) != NULL ) { + p++; + if ( !ldap_utf8isspace( p )) { + break; + } + } + val.bv_val = p; + val.bv_len = len - ( p - estr ); /* length + terminating \0 */ + slapi_entry_add_values( e, attr_changes, vals ); + free( estr ); + return 0; +} + +/* + * Function: mods2reple + * Arguments: e - a partially-constructed Slapi_Entry structure + * ldm - an array of pointers to LDAPMod structures describing the + * change applied. + * Returns: 0 on success. + * Description: Given a pointer to an LDAPMod struct and a dn, construct + * a new entry which will be added to the replication database. + * It is assumed that e points to an entry obtained from + * slapi_entry_alloc(). + */ +static int +mods2reple( Slapi_Entry *e, LDAPMod **ldm ) +{ + struct berval val; + struct berval *vals[ 2 ]; + lenstr *l; + + vals[ 0 ] = &val; + vals[ 1 ] = NULL; + + /* Set the changetype attribute */ + val.bv_val = "modify"; + val.bv_len = 6; + slapi_entry_add_values( e, "changetype", vals ); + + if (NULL != ldm) { + l = make_changes_string( ldm, NULL ); + if ( NULL != l ) { + val.bv_val = l->ls_buf; + val.bv_len = l->ls_len + 1; /* string + terminating \0 */ + slapi_entry_add_values( e, attr_changes, vals ); + lenstr_free( &l ); + } + } + return 0; +} + + +/* + * Function: modrdn2reple + * Arguments: e - a partially-constructed Slapi_Entry structure + * newrdn - the new relative distinguished name for the entry + * deloldrdn - the "deleteoldrdn" flag provided by the client + * ldm - any modifications applied as a side-effect of the modrdn + * Returns: 0 on success + * Description: Given a dn, a new rdn, and a deleteoldrdn flag, construct + * a new entry which will be added to the replication database reflecting a + * completed modrdn operation. The entry has the same form as above. + * It is assumed that e points to an entry obtained from slapi_entry_alloc(). + */ +static int +modrdn2reple( + Slapi_Entry *e, + const char *newrdn, + int deloldrdn, + LDAPMod **ldm, + const char *newsuperior +) +{ + struct berval val; + struct berval *vals[ 2 ]; + lenstr *l; + static const char *lastmodattrs[] = {"modifiersname", "modifytimestamp", + "creatorsname", "createtimestamp", + NULL }; + + vals[ 0 ] = &val; + vals[ 1 ] = NULL; + + val.bv_val = "modrdn"; + val.bv_len = 6; + slapi_entry_add_values( e, attr_changetype, vals ); + + if (newrdn) { + val.bv_val = (char *)newrdn; /* cast away const */ + val.bv_len = strlen( newrdn ); + slapi_entry_add_values( e, attr_newrdn, vals ); + } + + if ( deloldrdn == 0 ) { + val.bv_val = "FALSE"; + val.bv_len = 5; + } else { + val.bv_val = "TRUE"; + val.bv_len = 4; + } + slapi_entry_add_values( e, attr_deleteoldrdn, vals ); + + if (newsuperior) { + val.bv_val = (char *)newsuperior; /* cast away const */ + val.bv_len = strlen(newsuperior); + slapi_entry_add_values(e, attr_newsuperior,vals); + } + + if (NULL != ldm) { + l = make_changes_string( ldm, lastmodattrs ); + if ( NULL != l ) { + val.bv_val = l->ls_buf; + val.bv_len = l->ls_len + 1; /* string + terminating \0 */ + slapi_entry_add_values( e, attr_changes, vals ); + lenstr_free( &l ); + } + } + + return 0; +} + +/* + * Function: retrocl_postob + * + * Returns: 0 on success + * + * Arguments: pblock, optype (add, del, modify etc) + * + * Description: called from retrocl.c op-specific plugins. + * + * Please be aware that operation threads may be scheduled out between their + * being performed inside of the LDBM database and the changelog plugin + * running. For example, suppose MA and MB are two modify operations on the + * same entry. MA may be performed on the LDBM database, and then block + * before the changelog runs. MB then runs against the LDBM database and then + * is written to the changelog. MA starts running. In the changelog, MB will + * appear to have been performed before MA, but in the LDBM database the + * opposite will have occured. + * + * + */ + +int retrocl_postob (Slapi_PBlock *pb,int optype) +{ + char *dn; + LDAPMod **log_m = NULL; + int flag = 0; + Slapi_Entry *te = NULL; + Slapi_Operation *op = NULL; + LDAPMod **modrdn_mods = NULL; + char *newrdn = NULL; + char *newsuperior = NULL; + Slapi_Backend *be = NULL; + time_t curtime; + int rc; + + /* + * Check to see if the change was made to the replication backend db. + * If so, don't try to log it to the db (otherwise, we'd get in a loop). + */ + + (void)slapi_pblock_get( pb, SLAPI_BACKEND, &be ); + + if (slapi_be_logchanges(be) == 0) { + LDAPDebug(LDAP_DEBUG_TRACE,"not applying change if not logging\n", + 0,0,0); + return 0; + } + + if (retrocl_be_changelog == NULL || be == retrocl_be_changelog) { + LDAPDebug(LDAP_DEBUG_TRACE,"not applying change if no/cl be\n",0,0,0); + return 0; + } + + slapi_pblock_get(pb, SLAPI_RESULT_CODE, &rc); + + if (rc != LDAP_SUCCESS) { + LDAPDebug(LDAP_DEBUG_TRACE,"not applying change if op failed %d\n",rc, + 0,0); + return 0; + } + + if (slapi_op_abandoned(pb)) { + LDAPDebug(LDAP_DEBUG_PLUGIN,"not applying change if op abandoned\n", + 0,0,0); + return 0; + } + + curtime = current_time(); + + (void)slapi_pblock_get( pb, SLAPI_ORIGINAL_TARGET_DN, &dn ); + + /* change number could be retrieved from Operation extension stored in + * the pblock, or else it needs to be made dynamically. */ + + /* get the operation extension and retrieve the change number */ + slapi_pblock_get( pb, SLAPI_OPERATION, &op ); + + if (op == NULL) { + LDAPDebug(LDAP_DEBUG_TRACE,"not applying change if no op\n",0,0,0); + return 0; + } + + if (operation_is_flag_set(op, OP_FLAG_TOMBSTONE_ENTRY)){ + LDAPDebug(LDAP_DEBUG_TRACE,"not applying change for nsTombstone entries\n",0,0,0); + return 0; + } + + switch ( optype ) { + case OP_MODIFY: + (void)slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &log_m ); + break; + case OP_ADD: + /* + * For adds, we want the unnormalized dn, so we can preserve + * spacing, case, when replicating it. + */ + (void)slapi_pblock_get( pb, SLAPI_ADD_ENTRY, &te ); + if ( NULL != te ) { + dn = slapi_entry_get_dn( te ); + } + break; + case OP_DELETE: + break; + case OP_MODRDN: + (void)slapi_pblock_get( pb, SLAPI_MODRDN_NEWRDN, &newrdn ); + (void)slapi_pblock_get( pb, SLAPI_MODRDN_DELOLDRDN, &flag ); + (void)slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &modrdn_mods ); + (void)slapi_pblock_get( pb, SLAPI_MODRDN_NEWSUPERIOR, &newsuperior); + break; + } + + + /* check if we should log change to retro changelog, and + * if so, do it here */ + write_replog_db( optype, dn, log_m, flag, curtime, te, + newrdn, modrdn_mods, newsuperior ); + + return 0; +} + + diff --git a/ldap/servers/plugins/retrocl/retrocl_rootdse.c b/ldap/servers/plugins/retrocl/retrocl_rootdse.c new file mode 100644 index 00000000..602858d3 --- /dev/null +++ b/ldap/servers/plugins/retrocl/retrocl_rootdse.c @@ -0,0 +1,64 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include "retrocl.h" + + + +/* + * Function: retrocl_rootdse_search + * + * Returns: SLAPI_DSE_CALLBACK_OK always + * + * Arguments: See plugin API + * + * Description: callback function plugged into base object search of root DSE. + * Adds changelog, firstchangenumber and lastchangenumber attributes. + * + */ + +int +retrocl_rootdse_search(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg) +{ + + struct berval val; + struct berval *vals[2]; + vals[0] = &val; + vals[1] = NULL; + + /* Changelog information */ + if ( retrocl_be_changelog != NULL ) + { + char buf[BUFSIZ]; + changeNumber cnum; + + /* Changelog suffix */ + val.bv_val = RETROCL_CHANGELOG_DN; + if ( val.bv_val != NULL ) + { + val.bv_len = strlen( val.bv_val ); + slapi_entry_attr_replace( e, "changelog", vals ); + } + + /* First change number contained in log */ + cnum = retrocl_get_first_changenumber(); + sprintf( buf, "%lu", cnum ); + val.bv_val = buf; + val.bv_len = strlen( val.bv_val ); + slapi_entry_attr_replace( e, "firstchangenumber", vals ); + + /* Last change number contained in log */ + cnum = retrocl_get_last_changenumber(); + sprintf( buf, "%lu", cnum ); + val.bv_val = buf; + val.bv_len = strlen( val.bv_val ); + slapi_entry_attr_replace( e, "lastchangenumber", vals ); + } + + return SLAPI_DSE_CALLBACK_OK; +} + + diff --git a/ldap/servers/plugins/retrocl/retrocl_trim.c b/ldap/servers/plugins/retrocl/retrocl_trim.c new file mode 100644 index 00000000..5c413097 --- /dev/null +++ b/ldap/servers/plugins/retrocl/retrocl_trim.c @@ -0,0 +1,505 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + + +#include "retrocl.h" + +typedef struct _trim_status { + time_t ts_c_max_age; /* Constraint - max age of a changelog entry */ + time_t ts_s_last_trim; /* Status - last time we trimmed */ + int ts_s_initialized; /* Status - non-zero if initialized */ + int ts_s_trimming; /* non-zero if trimming in progress */ + PRLock *ts_s_trim_mutex; /* protects ts_s_trimming */ +} trim_status; +static trim_status ts = {0L, 0L, 0, 0, NULL}; + +/* + * All standard changeLogEntry attributes (initialized in get_cleattrs) + */ +static const char *cleattrs[ 10 ] = { NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL }; + +static int retrocl_trimming = 0; +static Slapi_Eq_Context retrocl_trim_ctx = NULL; + +/* + * Function: get_cleattrs + * + * Returns: an array of pointers to attribute names. + * + * Arguments: None. + * + * Description: Initializes, if necessary, and returns an array of char *s + * with attribute names used for retrieving changeLogEntry + * entries from the directory. + */ +static const char **get_cleattrs(void) +{ + if ( cleattrs[ 0 ] == NULL ) { + cleattrs[ 0 ] = attr_objectclass; + cleattrs[ 1 ] = attr_changenumber; + cleattrs[ 2 ] = attr_targetdn; + cleattrs[ 3 ] = attr_changetype; + cleattrs[ 4 ] = attr_newrdn; + cleattrs[ 5 ] = attr_deleteoldrdn; + cleattrs[ 6 ] = attr_changes; + cleattrs[ 7 ] = attr_newsuperior; + cleattrs[ 8 ] = attr_changetime; + cleattrs[ 9 ] = NULL; + } + return cleattrs; +} + +/* + * Function: delete_changerecord + * + * Returns: LDAP_ error code + * + * Arguments: the number of the change to delete + * + * Description: + * + */ + +static int +delete_changerecord( changeNumber cnum ) +{ + Slapi_PBlock *pb; + char *dnbuf; + int delrc; + + dnbuf = slapi_ch_malloc( strlen( attr_changenumber ) + 20 + + strlen(RETROCL_CHANGELOG_DN)); + /* Delete the record */ + sprintf( dnbuf, "%s=%ld, %s", attr_changenumber, cnum, + RETROCL_CHANGELOG_DN); + pb = slapi_pblock_new (); + slapi_delete_internal_set_pb ( pb, dnbuf, NULL /*controls*/, NULL /* uniqueid */, + g_plg_identity[PLUGIN_RETROCL], 0 /* actions */ ); + slapi_delete_internal_pb (pb); + slapi_pblock_get( pb, SLAPI_PLUGIN_INTOP_RESULT, &delrc ); + slapi_pblock_destroy( pb ); + + if ( delrc != LDAP_SUCCESS ) { + slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME, "delete_changerecord: could not delete " + "change record %d\n", cnum ); + } else { + slapi_log_error( SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME, + "delete_changerecord: deleted changelog entry \"%s\"\n", dnbuf); + } + slapi_ch_free((void **) &dnbuf ); + return delrc; +} + +/* + * Function: handle_getchangerecord_result + * Arguments: op - pointer to Operation struct for this operation + * err - error code returned from search + * Returns: nothing + * Description: result handler for get_changerecord(). Sets the crt_err + * field of the cnum_result_t struct to the error returned + * from the backend. + */ +static void +handle_getchangerecord_result( int err, void *callback_data ) +{ + cnum_result_t *crt = callback_data; + + if ( crt == NULL ) { + slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME, + "handle_getchangerecord_result: callback_data NULL\n" ); + } else { + crt->crt_err = err; + } +} + +/* + * Function: handle_getchangerecord_search + * Arguments: op - pointer to Operation struct for this operation + * e - entry returned by backend + * Returns: 0 in all cases + * Description: Search result operation handler for get_changerecord(). + * Sets fields in the cnum_result_t struct pointed to by + * op->o_handler_data. + */ +static int +handle_getchangerecord_search( Slapi_Entry *e, void *callback_data) +{ + cnum_result_t *crt = callback_data; + + if ( crt == NULL ) { + slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME, + "handle_getchangerecord_search: op->o_handler_data NULL\n" ); + } else if ( crt->crt_nentries > 0 ) { + /* only return the first entry, I guess */ + slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME, + "handle_getchangerecord_search: multiple entries returned\n" ); + } else { + crt->crt_nentries++; + crt->crt_entry = e; + } + + return 0; +} + + +/* + * Function: get_changerecord + * Arguments: cnum - number of change record to retrieve + * Returns: Pointer to an entry structure. The caller must free the entry. + * If "err" is non-NULL, an error code is returned in the memory + * location it points to. + * Description: Retrieve the change record entry whose number is "cnum". + */ +static Slapi_Entry *get_changerecord( changeNumber cnum, int *err ) +{ + cnum_result_t crt, *crtp = &crt; + char fstr[ 16 + CNUMSTR_LEN + 2 ]; + Slapi_PBlock *pb; + + if ( cnum == 0UL ) { + if ( err != NULL ) { + *err = LDAP_PARAM_ERROR; + } + return NULL; + } + crtp->crt_nentries = crtp->crt_err = 0; crtp->crt_entry = NULL; + sprintf( fstr, "%s=%ld", attr_changenumber, cnum ); + + pb = slapi_pblock_new (); + slapi_search_internal_set_pb (pb, RETROCL_CHANGELOG_DN, + LDAP_SCOPE_SUBTREE, fstr, + (char **)get_cleattrs(), /* cast const */ + 0 /* attrsonly */, + NULL /* controls */, NULL /* uniqueid */, + g_plg_identity[PLUGIN_RETROCL], + 0 /* actions */); + + slapi_search_internal_callback_pb (pb, crtp, + handle_getchangerecord_result, + handle_getchangerecord_search, NULL ); + if ( err != NULL ) { + *err = crtp->crt_err; + } + + slapi_pblock_destroy (pb); + + return( crtp->crt_entry ); +} + +/* + * Function: trim_changelog + * + * Arguments: none + * + * Returns: 0 on success, -1 on failure + * + * Description: Trims the changelog, according to the constraints + * described by the ts structure. + */ +static int trim_changelog(void) +{ + int rc = 0, ldrc, done; + time_t now; + changeNumber first_in_log = 0, last_in_log = 0; + Slapi_Entry *e = NULL; + int num_deleted = 0; + int me,lt; + + + now = current_time(); + + PR_Lock( ts.ts_s_trim_mutex ); + me = ts.ts_c_max_age; + lt = ts.ts_s_last_trim; + PR_Unlock( ts.ts_s_trim_mutex ); + + if ( now - lt >= (CHANGELOGDB_TRIM_INTERVAL / 1000) ) { + + /* + * Trim the changelog. Read sequentially through all the + * entries, deleting any which do not meet the criteria + * described in the ts structure. + */ + done = 0; + + while ( !done && retrocl_trimming == 1 ) { + int did_delete; + Slapi_Attr *attr; + + did_delete = 0; + first_in_log = retrocl_get_first_changenumber(); + if ( 0UL == first_in_log ) { + slapi_log_error( SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME, + "trim_changelog: no changelog records " + "to trim\n" ); + /* Bail out - we can't do any useful work */ + break; + } + + last_in_log = retrocl_get_last_changenumber(); + if ( last_in_log == first_in_log ) { + /* Always leave at least one entry in the change log */ + break; + } + if ( me > 0L ) { + e = get_changerecord( first_in_log, &ldrc ); + if ( NULL != e ) { + Slapi_Value *sval = NULL; + const struct berval *val = NULL; + rc = slapi_entry_attr_find( e, attr_changetime, &attr ); + /* Bug 624442: Logic checking for lack of timestamp was + reversed. */ + if ( 0 != rc || slapi_attr_first_value( attr,&sval ) == -1 || + (val = slapi_value_get_berval ( sval )) == NULL || + NULL == val->bv_val ) { + /* What to do if there's no timestamp? Just delete it. */ + retrocl_set_first_changenumber( first_in_log + 1 ); + ldrc = delete_changerecord( first_in_log ); + num_deleted++; + did_delete = 1; + } else { + time_t change_time = parse_localTime( val->bv_val ); + if ( change_time + me < now ) { + retrocl_set_first_changenumber( first_in_log + 1 ); + ldrc = delete_changerecord( first_in_log ); + num_deleted++; + did_delete = 1; + } + /* slapi_entry_free( e ); */ /* XXXggood should we be freeing this? */ + } + } + } + if ( !did_delete ) { + done = 1; + } + } + } else { + LDAPDebug(LDAP_DEBUG_PLUGIN, "not yet time to trim: %d < (%d+%d)\n", + now,lt,(CHANGELOGDB_TRIM_INTERVAL/1000)); + } + PR_Lock( ts.ts_s_trim_mutex ); + ts.ts_s_trimming = 0; + ts.ts_s_last_trim = now; + PR_Unlock( ts.ts_s_trim_mutex ); + if ( num_deleted > 0 ) { + slapi_log_error( SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME, + "trim_changelog: removed %d change records\n", + num_deleted ); + } + return rc; +} + +static int retrocl_active_threads; + +/* + * Function: changelog_trim_thread_fn + * + * Returns: nothing + * + * Arguments: none + * + * Description: the thread creation callback. retrocl_active_threads is + * provided for debugging purposes. + * + */ + +static void +changelog_trim_thread_fn( void *arg ) +{ + PR_AtomicIncrement(&retrocl_active_threads); + trim_changelog(); + PR_AtomicDecrement(&retrocl_active_threads); +} + + + +/* + * Function: retrocl_housekeeping + * Arguments: cur_time - the current time + * Returns: nothing + * Description: Determines if it is time to trim the changelog database, + * and if so, determines if the changelog database needs to + * be trimmed. If so, a thread is started which will trim + * the database. + */ + +void retrocl_housekeeping ( time_t cur_time, void *noarg ) +{ + static time_t thread_start_time; + int ldrc; + + if (retrocl_be_changelog == NULL) { + LDAPDebug(LDAP_DEBUG_TRACE,"not housekeeping if no cl be\n",0,0,0); + return; + } + + if ( !ts.ts_s_initialized ) { + slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME, "changelog_housekeeping called before " + "trimming constraints set\n" ); + return; + } + + PR_Lock( ts.ts_s_trim_mutex ); + if ( !ts.ts_s_trimming ) { + int must_trim = 0; + /* See if we need to trim */ + /* Has enough time elapsed since our last check? */ + if ( cur_time - ts.ts_s_last_trim >= (ts.ts_c_max_age) ) { + /* Is the first entry too old? */ + time_t first_time; + /* + * good we could avoid going to the database to retrieve + * this time information if we cached the last value we'd read. + * But a client might have deleted it over protocol. + */ + first_time = retrocl_getchangetime( SLAPI_SEQ_FIRST, &ldrc ); + LDAPDebug(LDAP_DEBUG_PLUGIN, + "cltrim: ldrc=%d, first_time=%d, cur_time=%d\n", + ldrc,first_time,cur_time); + if ( LDAP_SUCCESS == ldrc && first_time > (time_t) 0L && + first_time + ts.ts_c_max_age < cur_time ) { + must_trim = 1; + } + } + if ( must_trim ) { + LDAPDebug(LDAP_DEBUG_TRACE,"changelog about to create thread\n",0,0,0); + /* Start a thread to trim the changelog */ + thread_start_time = cur_time; + ts.ts_s_trimming = 1; + if ( PR_CreateThread( PR_USER_THREAD, + changelog_trim_thread_fn, NULL, + PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD, + RETROCL_DLL_DEFAULT_THREAD_STACKSIZE ) == NULL ) { + slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME, "unable to create changelog trimming thread\n" ); + } + } else { + LDAPDebug(LDAP_DEBUG_PLUGIN, + "changelog does not need to be trimmed\n",0,0,0); + } + } + PR_Unlock( ts.ts_s_trim_mutex ); +} + + +/* + * Function: age_str2time + * + * Returns: time_t + * + * Arguments: string representation of age (digits and unit s,m,h,d or w) + * + * Description: + * convert time from string like 1h (1 hour) to corresponding time in seconds + * + */ + +static time_t +age_str2time (const char *age) +{ + char *maxage; + char unit; + time_t ageval; + + if (age == NULL || age[0] == '\0' || strcmp (age, "0") == 0) { + return 0; + } + + maxage = slapi_ch_strdup ( age ); + unit = maxage[ strlen( maxage ) - 1 ]; + maxage[ strlen( maxage ) - 1 ] = '\0'; + ageval = strntoul( maxage, strlen( maxage ), 10 ); + if ( maxage) { + slapi_ch_free ( (void **) &maxage ); + } + switch ( unit ) { + case 's': + break; + case 'm': + ageval *= 60; + break; + case 'h': + ageval *= ( 60 * 60 ); + break; + case 'd': + ageval *= ( 24 * 60 * 60 ); + break; + case 'w': + ageval *= ( 7 * 24 * 60 * 60 ); + break; + default: + slapi_log_error( SLAPI_LOG_PLUGIN, "retrocl", + "age_str2time: unknown unit \"%c\" " + "for maxiumum changelog age\n", unit ); + ageval = -1; + } + + return ageval; +} + +/* + * Function: retrocl_init_trimming + * + * Returns: none, exits on fatal error + * + * Arguments: none + * + * Description: called during startup + * + */ + +void retrocl_init_trimming (void) +{ + const char *cl_maxage; + time_t ageval; + + cl_maxage = retrocl_get_config_str(CONFIG_CHANGELOG_MAXAGE_ATTRIBUTE); + + if (cl_maxage == NULL) { + LDAPDebug(LDAP_DEBUG_TRACE,"No maxage, not trimming retro changelog.\n",0,0,0); + return; + } + ageval = age_str2time (cl_maxage); + slapi_ch_free ((void **)&cl_maxage); + + ts.ts_c_max_age = ageval; + ts.ts_s_last_trim = (time_t) 0L; + ts.ts_s_trimming = 0; + if (( ts.ts_s_trim_mutex = PR_NewLock()) == NULL ) { + slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME, "set_changelog_trim_constraints: " + "cannot create new lock.\n" ); + exit( 1 ); + } + ts.ts_s_initialized = 1; + retrocl_trimming = 1; + + retrocl_trim_ctx = slapi_eq_repeat(retrocl_housekeeping, + NULL,(time_t)0, + CHANGELOGDB_TRIM_INTERVAL); + +} + +/* + * Function: retrocl_stop_trimming + * + * Returns: none + * + * Arguments: none + * + * Description: called when server is shutting down to ensure trimming stops + * eventually. + * + */ + +void retrocl_stop_trimming(void) +{ + retrocl_trimming = 0; + if (retrocl_trim_ctx) { + slapi_eq_cancel(retrocl_trim_ctx); + retrocl_trim_ctx = NULL; + } +} + |