[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