[Date Prev][Date Next] [Chronological] [Thread] [Top]

changelog patch



Attached is the first cut for the changelog implementation.  The patch
and additional file implement the methods required for

http://search.ietf.org/internet-drafts/draft-good-ldap-changelog-00.txt

The patch applies to the "HEAD" CVS branch, let me know if I should use
some other branch when submitting patches.

The changelog is implemented using an independent ldbm back-end
identified with the required "cn=changelog" suffix, eg slapd should
contain something like this

database        ldbm
suffix          "cn=changelog"
directory       /tmp/ldap.data/changelog
index           dn
rootdn          "cn=changelog"
rootpw          "nosecret"

The changelog back-end works like any other.

Changes to slapd.oc.conf are given in the patch, except I missed this

objectclass changelog
        requires
                cn

Required to create the initial db where I used the following ldif

dn: cn=changelog
cn: changelog
objectclass: changelog

The entries can be queried using normal ldap methods rooted to
"cn=changelog".  Some samples from the tests:

changenumber=9,cn=changelog
changenumber=9
targetdn=cn=Merry Brandybuck,o=government of british columbia,c=ca
changetype=delete
objectclass=changelogentry

changenumber=10,cn=changelog
changenumber=10
targetdn=cn=Merry Brandybuck,o=government of british columbia,c=ca
changetype=add
changes=dn: cn=Merry Brandybuck,o=government of british columbia,c=ca
cn: Merry Brandybuck
sn: Brandybuck
objectclass: person

objectclass=changelogentry

changenumber=11,cn=changelog
changenumber=11
targetdn=cn=Merry Brandybuck,o=government of british columbia,c=ca
changetype=modify
changes=delete: title
-
objectclass=changelogentry

changenumber=12,cn=changelog
changenumber=12
targetdn=cn=Merry Brandybuck,o=government of british columbia,c=ca
changetype=modify
changes=add: title
title: grand poo bah
-
objectclass=changelogentry

changenumber=13,cn=changelog
changenumber=13
targetdn=cn=Merry Brandybuck,o=government of british columbia,c=ca
changetype=modrdn
newrdn=cn=Pippin Took
deleteoldrdn=FALSE
objectclass=changelogentry

changenumber=14,cn=changelog
changenumber=14
targetdn=cn=Pippin Took,o=government of british columbia,c=ca
changetype=modrdn
newrdn=cn=Saruman
deleteoldrdn=TRUE
objectclass=changelogentry

There has been minimal testing.  The tests I did run (simple adds, mods,
deletes, modrdns) all worked.  

The usual security caveat applies: make sure you don't make the backend
globally visible as changelog will happily write passwords in the
changes attribute.

Something I was a little unclear on was whether the final newline in the
ldif data contained in the changes attribute should be stripped.  I've
left it in for now.  Also, I have not put in special code for binary
values, just used ldif_type_and_value calls to compose the ldif
content.  I will look at what special handling (if any) is needed for
them as time permits.

Comments, bug fixes/reports, etc welcome.

-- 
Will Ballantyne             GEMS Technical Architect
mailto:Will.Ballantyne@gems1.gov.bc.ca
/* changelog.c
 *
 * Copyright (c) 1998 Will Ballantyne, ITSD, Government of BC
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that this notice is preserved and that due credit is given
 * to ITSD, Government of BC. The name of ITSD
 * may not be used to endorse or promote products derived from this
 * software without specific prior written permission. This software
 * is provided ``as is'' without express or implied warranty.
 */

#include "portable.h"

#include <stdio.h>
#include <string.h>

#include "slap.h"
#include "back-ldbm.h"
#include "proto-back-ldbm.h"

extern char *dn_normalize();

extern Backend *changelog;

/* what is the max string a long can be? What if it changes? */
#define MAX_LONG_STRING	32

/*
 * simplification of commonly used sequence for merge_attr
 */
void changelog_attr (Entry *logEntry, char *attribute, char *attrval) {
	struct 	berval bval;
	struct 	berval *bvals[2];

	bvals[0] = &bval;
	bvals[1] = NULL;

	bval.bv_len = strlen (attrval);
	bval.bv_val = attrval;
	attr_merge (logEntry, attribute, bvals);

	Debug (LDAP_DEBUG_TRACE, "changelog set: %s = %s\n",
		attribute, attrval, 0);
}


/*
 * convert an entry to an ldif buffer.  Return null on err
 */
char *changelog_e2ldif (Entry *e) {
	int		sumlen = 0;
	char		*buff = NULL, *ldifline = NULL;
	Attribute 	*a;

	if ((e == NULL) || (e->e_dn == NULL))	{
		return NULL;
	}

	ldifline = ldif_type_and_value ("dn", e->e_dn, strlen (e->e_dn) );
	sumlen += strlen (ldifline);
	buff = (char *) ch_realloc (buff, sumlen + 1);
	strcpy	(buff, ldifline);
	free (ldifline);

	for (a = e->e_attrs; a != NULL; a = a->a_next) {
		int i;
		for (i = 0; a->a_vals[i] != NULL; ++i) {
			ldifline = NULL;
			ldifline = ldif_type_and_value (a->a_type, a->a_vals[i]->bv_val,
							a->a_vals[i]->bv_len);
			if (ldifline != NULL) {
				sumlen += strlen (ldifline);
				buff = (char *) ch_realloc (buff, sumlen + 1);
				strcat (buff, ldifline);
				free (ldifline);
			}
		}
	}

	Debug (LDAP_DEBUG_TRACE, "buff: \"%s\"\n",
		buff, 0, 0);
	return buff;
}

/*
 * log the given change to the directory using the changelog method
 */
void changelog_add (
	Connection 	*conn, 
	Operation 	*op, 
	Entry 		*e, 
	long 		changeType,
	char 		*special,     /* value depends on the log type */
	char 		*newrdn       /* for modrdn */
)
{
	int	idlen;
	char 	*dn = NULL, *pdn = NULL, *matched;
	char	changeId [MAX_LONG_STRING + 1];
	Entry 	*parent = NULL, *logEntry = NULL;
	struct 	ldbminfo *li = (struct ldbminfo *) changelog->be_private;

	if ((changelog == NULL) || (changelog->be_suffix[0] == NULL)) {  
		/* just in case */
		return;
	}

	logEntry =  (Entry *) ch_calloc (1, sizeof(Entry));
	entry_rdwr_init (logEntry);
	logEntry->e_attrs = NULL;

	if ( (logEntry->e_id = next_id (changelog)) == NOID ) {
		Debug (LDAP_DEBUG_ANY, "Unable to obtain id for changelog",
			0,0,0);
		entry_free(logEntry);
		return;
	}

	/*
	 * dn of the form changenumber=nnn,cn=changelog but allow
	 * different suffix to avoid deps.  nnn is a string rep of the
	 * long id.  Make the id the same as the change number
	 */
	sprintf (changeId, "%ld", logEntry->e_id);
	idlen = strlen(changeId);
	logEntry->e_dn = ch_malloc (idlen + 
				 strlen(changelog->be_suffix[0]) +
				 15);
	strcpy (logEntry->e_dn, "changenumber=");
	strcat (logEntry->e_dn, changeId);
	strcat (logEntry->e_dn, ",");
	strcat (logEntry->e_dn, changelog->be_suffix[0]);

	dn = dn_normalize (strdup(logEntry->e_dn));

	Debug (LDAP_DEBUG_TRACE, "determined dn for change: %s\n",
		dn, 0, 0);

	/* fill in the required attributes for changelog spec */
        changelog_attr (logEntry, "changenumber", changeId);

	switch (changeType) {
	case LDAP_REQ_ADD: 
		{
			char *ldifbuffer;
			changelog_attr (logEntry, "targetdn", e->e_dn);
			changelog_attr (logEntry, "changetype", "add");
			ldifbuffer = changelog_e2ldif (e);
			if (ldifbuffer != NULL) {
				changelog_attr (logEntry, "changes", ldifbuffer);
				free (ldifbuffer);
			}
			break;
		}
	case LDAP_REQ_DELETE:
		{
			changelog_attr (logEntry, "targetdn", e->e_dn);
			changelog_attr (logEntry, "changetype", "delete");
			break;
		}
        case LDAP_REQ_MODIFY:
		{
			changelog_attr (logEntry, "targetdn", e->e_dn);
			changelog_attr (logEntry, "changetype", "modify");
			if (special != NULL) {
				changelog_attr (logEntry, "changes", special);
			}
			break;
		}
        case LDAP_REQ_MODRDN:
                {
                        changelog_attr (logEntry, "targetdn", e->e_dn);
                        changelog_attr (logEntry, "changetype", "modrdn");
                        changelog_attr (logEntry, "newrdn", newrdn);
			if (special != NULL) { /* just in case */
                        	changelog_attr (logEntry, "deleteoldrdn", special);
			}
			else {
				Debug (LDAP_DEBUG_ANY, "newrdn not specified for changelog",
				       0, 0, 0);
			}

                        break;
                }

		Debug (LDAP_DEBUG_ANY, "unknown change type: %dl\n",
			changeType, 0, 0);
		free (dn);
		entry_free(logEntry);
		return;
	}

	changelog_attr (logEntry, "objectclass", "changelogentry");

	if ( index_add_entry( changelog, logEntry ) != 0 ) {
		Debug (LDAP_DEBUG_ANY, 
			"changelog: error in index_add_entry for change %s\n",
			changeId, 0, 0);
	}

	if ( dn2id_add( changelog, dn, logEntry->e_id ) != 0 ) {
		Debug (LDAP_DEBUG_ANY, 
			"changelog: error in dn2id_add for change %s\n",
			changeId, 0, 0);
	}
	
	entry_rdwr_lock (logEntry, 1);

	if ( id2entry_add( changelog, logEntry ) != 0 ) {
		Debug (LDAP_DEBUG_ANY, 
			"changelog: error in id2entry_add for change %s\n",
			changeId, 0, 0);
	}

	free (dn);

	cache_set_state (&li->li_cache, logEntry, 0);
	cache_return_entry_w (&li->li_cache, logEntry);
}

Only in ldap.new/clients/fax500: fax500
Only in ldap.new/clients/fax500: fax5version.c
Only in ldap.new/clients/fax500: fax5version.o
Only in ldap.new/clients/fax500: faxtotpc.o
Only in ldap.new/clients/fax500: main.o
Only in ldap.new/clients/fax500: rp500
Only in ldap.new/clients/fax500: rp500.o
Only in ldap.new/clients/fax500: rpversion.c
Only in ldap.new/clients/fax500: rpversion.o
Only in ldap.new/clients/finger: in.xfingerd
Only in ldap.new/clients/finger: main.o
Only in ldap.new/clients/finger: version.c
Only in ldap.new/clients/finger: version.o
Only in ldap.new/clients/gopher: detach.o
Only in ldap.new/clients/gopher: go500
Only in ldap.new/clients/gopher: go500.o
Only in ldap.new/clients/gopher: go500gw
Only in ldap.new/clients/gopher: go500gw.o
Only in ldap.new/clients/gopher: goversion.c
Only in ldap.new/clients/gopher: goversion.o
Only in ldap.new/clients/gopher: gwversion.c
Only in ldap.new/clients/gopher: gwversion.o
Only in ldap.new/clients/gopher: setproctitle.o
Only in ldap.new/clients/mail500: mail500
Only in ldap.new/clients/mail500: main.o
Only in ldap.new/clients/mail500: version.c
Only in ldap.new/clients/mail500: version.o
Only in ldap.new/clients/rcpt500: cmds.o
Only in ldap.new/clients/rcpt500: help.o
Only in ldap.new/clients/rcpt500: main.o
Only in ldap.new/clients/rcpt500: query.o
Only in ldap.new/clients/rcpt500: rcpt500
Only in ldap.new/clients/rcpt500: version.c
Only in ldap.new/clients/rcpt500: version.o
Only in ldap.new/clients/tools: ldapadd
Only in ldap.new/clients/tools: ldapdelete
Only in ldap.new/clients/tools: ldapdelete.o
Only in ldap.new/clients/tools: ldapmodify
Only in ldap.new/clients/tools: ldapmodify.o
Only in ldap.new/clients/tools: ldapmodrdn
Only in ldap.new/clients/tools: ldapmodrdn.o
Only in ldap.new/clients/tools: ldapsearch
Only in ldap.new/clients/tools: ldapsearch.o
Only in ldap.new/clients/tools: lddversion.c
Only in ldap.new/clients/tools: lddversion.o
Only in ldap.new/clients/tools: ldmversion.c
Only in ldap.new/clients/tools: ldmversion.o
Only in ldap.new/clients/tools: ldrversion.c
Only in ldap.new/clients/tools: ldrversion.o
Only in ldap.new/clients/tools: ldsversion.c
Only in ldap.new/clients/tools: ldsversion.o
Only in ldap.new/clients/ud: find.o
Only in ldap.new/clients/ud: main.o
Only in ldap.new/clients/ud: mod.o
Only in ldap.new/clients/ud: print.o
Only in ldap.new: config.cache
Only in ldap.new: config.log
Only in ldap.new: config.status
Only in ldap.orig/contrib: php3-tool
Only in ldap.orig/doc/rfc: rfc1274.txt
Only in ldap.orig/doc/rfc: rfc1275.ps
Only in ldap.orig/doc/rfc: rfc1275.txt
Only in ldap.orig/doc/rfc: rfc1279.ps
Only in ldap.orig/doc/rfc: rfc1279.txt
Only in ldap.orig/doc/rfc: rfc1308.txt
Only in ldap.orig/doc/rfc: rfc1309.txt
Only in ldap.orig/doc/rfc: rfc1430.txt
Only in ldap.orig/doc/rfc: rfc1617.txt
Only in ldap.orig/doc/rfc: rfc1781.txt
Only in ldap.orig/doc/rfc: rfc1960.txt
Only in ldap.orig/doc/rfc: rfc2044.txt
Only in ldap.orig/doc/rfc: rfc2164.txt
Only in ldap.orig/doc/rfc: rfc2218.txt
Only in ldap.orig/doc/rfc: rfc2247.txt
Only in ldap.orig/doc/rfc: rfc2251.txt
Only in ldap.orig/doc/rfc: rfc2252.txt
Only in ldap.orig/doc/rfc: rfc2253.txt
Only in ldap.orig/doc/rfc: rfc2254.txt
Only in ldap.orig/doc/rfc: rfc2255.txt
Only in ldap.orig/doc/rfc: rfc2256.txt
Only in ldap.orig/doc/rfc: rfc2293.txt
Only in ldap.orig/doc/rfc: rfc2294.txt
Only in ldap.orig/doc/rfc: rfc2307.txt
Only in ldap.orig/doc/rfc: rfc2377.txt
Only in ldap.new/servers/slapd/back-ldbm: @
diff -rux CVS -x Makefile ldap.orig/servers/slapd/back-ldbm/Makefile.in ldap.new/servers/slapd/back-ldbm/Makefile.in
--- ldap.orig/servers/slapd/back-ldbm/Makefile.in	Wed Oct 28 19:44:48 1998
+++ ldap.new/servers/slapd/back-ldbm/Makefile.in	Tue Oct 27 15:54:13 1998
@@ -1,11 +1,13 @@
 SRCS	= idl.c add.c search.c cache.c dbcache.c dn2id.c id2entry.c \
 		index.c id2children.c nextid.c abandon.c compare.c group.c \
 		modify.c modrdn.c delete.c init.c config.c bind.c attr.c \
-		filterindex.c unbind.c kerberos.c close.c alias.c
+		filterindex.c unbind.c kerberos.c close.c alias.c \
+		changelog.c
 OBJS	= idl.o add.o search.o cache.o dbcache.o dn2id.o id2entry.o \
 		index.o id2children.o nextid.o abandon.o compare.o group.o \
 		modify.o modrdn.o delete.o init.o config.o bind.o attr.o \
-		filterindex.o unbind.o kerberos.o close.o alias.o
+		filterindex.o unbind.o kerberos.o close.o alias.o \
+		changelog.o
 
 LDAP_INCDIR= ../../../include       
 LDAP_LIBDIR= ../../../libraries
diff -rux CVS -x Makefile ldap.orig/servers/slapd/back-ldbm/add.c ldap.new/servers/slapd/back-ldbm/add.c
--- ldap.orig/servers/slapd/back-ldbm/add.c	Wed Oct 28 19:44:48 1998
+++ ldap.new/servers/slapd/back-ldbm/add.c	Wed Oct 28 19:33:31 1998
@@ -14,6 +14,7 @@
 extern int	global_schemacheck;
 extern char	*dn_parent();
 extern char	*dn_normalize();
+extern Backend  *changelog;
 
 int
 ldbm_back_add(
@@ -177,6 +178,11 @@
 
 		rc = -1;
 		goto return_results;
+	}
+
+	/* log the change if the changelog back end is defined */
+	if (changelog != NULL) {
+		changelog_add (conn, op, e, LDAP_REQ_ADD, NULL, NULL);
 	}
 
 	send_ldap_result( conn, op, LDAP_SUCCESS, "", "" );
Only in ldap.new/servers/slapd/back-ldbm: changelog.c
diff -rux CVS -x Makefile ldap.orig/servers/slapd/back-ldbm/delete.c ldap.new/servers/slapd/back-ldbm/delete.c
--- ldap.orig/servers/slapd/back-ldbm/delete.c	Wed Oct 28 19:44:48 1998
+++ ldap.new/servers/slapd/back-ldbm/delete.c	Wed Oct 28 19:34:06 1998
@@ -13,6 +13,8 @@
 
 extern Attribute	*attr_find();
 
+extern Backend		*changelog;
+
 int
 ldbm_back_delete(
     Backend	*be,
@@ -66,6 +68,10 @@
 		e->e_rdwr.readers_reading, e->e_rdwr.writer_writing, 0);
 
 	/* XXX delete from parent's id2children entry XXX */
+
+        if (changelog != NULL) {
+                changelog_add (conn, op, e, LDAP_REQ_DELETE, NULL, NULL);
+	}
 
 	/* delete from dn2id mapping */
 	if ( dn2id_delete( be, e->e_dn ) != 0 ) {
diff -rux CVS -x Makefile ldap.orig/servers/slapd/back-ldbm/modify.c ldap.new/servers/slapd/back-ldbm/modify.c
--- ldap.orig/servers/slapd/back-ldbm/modify.c	Wed Oct 28 19:44:48 1998
+++ ldap.new/servers/slapd/back-ldbm/modify.c	Wed Oct 28 19:33:48 1998
@@ -12,6 +12,7 @@
 #include "proto-back-ldbm.h"
 
 extern int		global_schemacheck;
+extern Backend		*changelog;
 extern Attribute	*attr_find();
 
 static int	add_values();
@@ -28,9 +29,9 @@
 )
 {
 	struct ldbminfo	*li = (struct ldbminfo *) be->be_private;
-	char		*matched = NULL;
+	char		*matched = NULL, *changesldif = NULL, *ldifline = NULL;
 	Entry		*e;
-	int		i, err, modtype;
+	int		i, err, modtype, sumlen = 0;
 	LDAPMod		*mod;
 
 	Debug(LDAP_DEBUG_ARGS, "ldbm_back_modify:\n", 0, 0, 0);
@@ -53,21 +54,68 @@
 		goto error_return;
 	}
 
+	/* set up blank string for realloc and strcat later */
+	if (changelog != NULL) {
+		changesldif = (char *) ch_malloc (1);
+		changesldif[0] = '\0';
+	}
+
 	for ( mod = mods; mod != NULL; mod = mod->mod_next ) {
+		char *thisChange = NULL;
 		switch ( mod->mod_op & ~LDAP_MOD_BVALUES ) {
 		case LDAP_MOD_ADD:
+	                thisChange = "add: ";
 			err = add_values( e, mod, op->o_dn );
 			break;
 
 		case LDAP_MOD_DELETE:
+        	 	thisChange = "delete: ";
 			err = delete_values( e, mod, op->o_dn );
 			break;
 
 		case LDAP_MOD_REPLACE:
+	                thisChange = "replace: ";
 			err = replace_values( e, mod, op->o_dn );
 			break;
 		}
 
+
+		/* build up an ldif line before mod changes for changelog if approp */
+		if ((changelog != NULL) && (thisChange != NULL)) {
+
+			/* format sample add: title\n */
+			sumlen += strlen (thisChange) + strlen (mod->mod_type) + 1;
+			changesldif = (char *) ch_realloc (changesldif, sumlen + 1);
+			strcat (changesldif, thisChange);
+			strcat (changesldif, mod->mod_type);
+			strcat (changesldif, "\n");
+
+			if (mod->mod_bvalues != NULL) {	
+				for ( i = 0; mod->mod_bvalues[i] != NULL; i++ ) {
+					Debug( LDAP_DEBUG_TRACE, "mod[i] \"%s\" \n", 
+					mod->mod_type, 0, 0);
+					ldifline = ldif_type_and_value (mod->mod_type, 
+							mod->mod_bvalues[i]->bv_val,
+							mod->mod_bvalues[i]->bv_len);
+
+					if (ldifline != NULL) {
+						sumlen += strlen (ldifline);
+						changesldif = (char *) ch_realloc (changesldif, 
+										    sumlen + 1);
+						strcat (changesldif, ldifline);
+						free (ldifline);
+						ldifline = NULL;
+					}		
+				}
+			}
+			sumlen += 1; 
+			changesldif = (char *) ch_realloc (changesldif, sumlen + 1);
+			strcat (changesldif, "-");
+
+			Debug( LDAP_DEBUG_TRACE, "built LDIF line %s\n", 
+						changesldif, 0, 0 );
+		}
+
 		if ( err != LDAP_SUCCESS ) {
 			/* unlock entry, delete from cache */
 			send_ldap_result( conn, op, err, NULL, NULL );
@@ -90,6 +138,11 @@
 	}
 	pthread_mutex_unlock( &op->o_abandonmutex );
 
+	/* log it */
+	if (changelog != NULL) {
+		changelog_add (conn, op, e, LDAP_REQ_MODIFY, changesldif, NULL);
+	}
+
 	/* modify indexes */
 	if ( index_add_mods( be, mods, e->e_id ) != 0 ) {
 		send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR, NULL, NULL );
@@ -111,10 +164,18 @@
 	}
 
 	send_ldap_result( conn, op, LDAP_SUCCESS, NULL, NULL );
+
+	if (changesldif != NULL) {
+		free (changesldif);
+	}
+
 	cache_return_entry_w( &li->li_cache, e );
 	return( 0 );
 
 error_return:;
+	if (changesldif != NULL) {
+		free (changesldif);
+	}
 	cache_return_entry_w( &li->li_cache, e );
 	return( -1 );
 }
diff -rux CVS -x Makefile ldap.orig/servers/slapd/back-ldbm/modrdn.c ldap.new/servers/slapd/back-ldbm/modrdn.c
--- ldap.orig/servers/slapd/back-ldbm/modrdn.c	Wed Oct 28 19:44:48 1998
+++ ldap.new/servers/slapd/back-ldbm/modrdn.c	Wed Oct 28 19:31:09 1998
@@ -13,6 +13,8 @@
 
 extern char	*dn_parent();
 
+extern Backend	*changelog;
+
 int
 ldbm_back_modrdn(
     Backend	*be,
@@ -83,6 +85,11 @@
 		goto error_return;
 	}
 	pthread_mutex_unlock( &op->o_abandonmutex );
+
+	/* log it */
+	if (changelog != NULL) {
+		changelog_add (conn, op, e, LDAP_REQ_MODRDN, deleteoldrdn ? "TRUE" : "FALSE" , newrdn);
+	}
 
 	/* add new one */
 	if ( dn2id_add( be, newdn, e->e_id ) != 0 ) {
Only in ldap.new/servers/slapd/back-ldbm: version.c
diff -rux CVS -x Makefile ldap.orig/servers/slapd/config.c ldap.new/servers/slapd/config.c
--- ldap.orig/servers/slapd/config.c	Wed Oct 28 19:44:48 1998
+++ ldap.new/servers/slapd/config.c	Tue Oct 27 15:09:07 1998
@@ -27,6 +27,7 @@
 char		*replogfile;
 int		global_lastmod;
 char		*ldap_srvtab = "";
+Backend		*changelog = NULL;
 
 static char	*fp_getline();
 static void	fp_getline_init();
@@ -135,6 +136,14 @@
 				dn = strdup( cargv[1] );
 				(void) dn_normalize( dn );
 				charray_add( &be->be_suffix, dn );
+
+				/* set the changelog backend global */
+				if (!strcasecmp(dn, "cn=changelog")) {
+					changelog = be;
+					Debug (LDAP_DEBUG_TRACE,
+					       "changelog backend set\n",
+						0, 0, 0);
+				}
 			}
 
                 /* set database suffixAlias */
diff -rux CVS -x Makefile ldap.orig/servers/slapd/slapd.oc.conf ldap.new/servers/slapd/slapd.oc.conf
--- ldap.orig/servers/slapd/slapd.oc.conf	Wed Oct 28 19:44:48 1998
+++ ldap.new/servers/slapd/slapd.oc.conf	Tue Oct 27 14:48:27 1998
@@ -7,6 +7,17 @@
 		aliasedObjectName,
 		objectClass
 
+objectclass changeLogEntry
+	requires
+		changeNumber,
+		targetDN,
+		changeType
+	allows
+		changes,
+		newRDN,
+		deleteOldRDN,
+		nweSuperior
+
 objectclass country
 	requires
 		objectClass,
Only in ldap.new/servers/slapd: version.c
Only in ldap.new/servers/slurpd: version.c
Only in ldap.new: stamp-h