summaryrefslogtreecommitdiffstats
path: root/ldap/servers/plugins/retrocl
diff options
context:
space:
mode:
Diffstat (limited to 'ldap/servers/plugins/retrocl')
-rw-r--r--ldap/servers/plugins/retrocl/Makefile135
-rw-r--r--ldap/servers/plugins/retrocl/dllmain.c90
-rw-r--r--ldap/servers/plugins/retrocl/linktest.c16
-rw-r--r--ldap/servers/plugins/retrocl/retrocl.c341
-rw-r--r--ldap/servers/plugins/retrocl/retrocl.def15
-rw-r--r--ldap/servers/plugins/retrocl/retrocl.h123
-rw-r--r--ldap/servers/plugins/retrocl/retrocl.txt107
-rw-r--r--ldap/servers/plugins/retrocl/retrocl_cn.c391
-rw-r--r--ldap/servers/plugins/retrocl/retrocl_create.c317
-rw-r--r--ldap/servers/plugins/retrocl/retrocl_po.c529
-rw-r--r--ldap/servers/plugins/retrocl/retrocl_rootdse.c64
-rw-r--r--ldap/servers/plugins/retrocl/retrocl_trim.c505
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;
+ }
+}
+