diff options
Diffstat (limited to 'contrib/slapd-modules/addpartial')
-rw-r--r-- | contrib/slapd-modules/addpartial/Makefile | 46 | ||||
-rw-r--r-- | contrib/slapd-modules/addpartial/README | 72 | ||||
-rw-r--r-- | contrib/slapd-modules/addpartial/addpartial-overlay.c | 349 |
3 files changed, 467 insertions, 0 deletions
diff --git a/contrib/slapd-modules/addpartial/Makefile b/contrib/slapd-modules/addpartial/Makefile new file mode 100644 index 0000000..ecb7cd0 --- /dev/null +++ b/contrib/slapd-modules/addpartial/Makefile @@ -0,0 +1,46 @@ +# $OpenLDAP$ + +LDAP_SRC = ../../.. +LDAP_BUILD = $(LDAP_SRC) +LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd +LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \ + $(LDAP_BUILD)/libraries/liblber/liblber.la + +LIBTOOL = $(LDAP_BUILD)/libtool +CC = gcc +OPT = -g -O2 +DEFS = +INCS = $(LDAP_INC) +LIBS = $(LDAP_LIB) + +PROGRAMS = addpartial.la +LTVER = 0:0:0 + +prefix=/usr/local +exec_prefix=$(prefix) +ldap_subdir=/openldap + +libdir=$(exec_prefix)/lib +libexecdir=$(exec_prefix)/libexec +moduledir = $(libexecdir)$(ldap_subdir) + +.SUFFIXES: .c .o .lo + +.c.lo: + $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $< + +all: $(PROGRAMS) + +addpartial.la: addpartial-overlay.lo + $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \ + -rpath $(moduledir) -module -o $@ $? $(LIBS) + +clean: + rm -rf *.o *.lo *.la .libs + +install: $(PROGRAMS) + mkdir -p $(DESTDIR)$(moduledir) + for p in $(PROGRAMS) ; do \ + $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \ + done + diff --git a/contrib/slapd-modules/addpartial/README b/contrib/slapd-modules/addpartial/README new file mode 100644 index 0000000..968cdd8 --- /dev/null +++ b/contrib/slapd-modules/addpartial/README @@ -0,0 +1,72 @@ +addpartial Overlay README + +DESCRIPTION + This package contains an OpenLDAP overlay called "addpartial" that + intercepts add requests, determines if the entry exists, determines what + attributes, if any, have changed, and modifies those attributes. If the + entry does not exist, the add request falls through and proceeds normally. + If the entry exists but no changes have been detected, the client receives + LDAP_SUCCESS (I suppose it is debatable what to do in this case, but this is + the most clean for my use. The LDAP_SUCCESS lets me know that the entry I + sent slapd == the entry already in my slapd DB. Perhaps this behavior + should be configurable in the future). + + When a change is found, the addpartial overlay will replace all values for + the attribute (if an attribute does not exist in the new entry but exists + in the entry in the slapd DB, a replace will be done with an empty list of + values). + + Once a modify takes place, the syncprov overlay will properly process the + change, provided that addpartial is the first overlay to run. Please see + the CAVEATS for more specifics about this. + + The addpartial overlay makes it easy to replicate full entries to a slapd + instance without worrying about the differences between entries or even if + the entry exists. Using ldapadd to add entries, the addpartial overlay can + compare about 500 records per second. The intent of the addpartial overlay + is to make it easy to replicate records from a source that is not an LDAP + instance, such as a database. The overlay is also useful in places where it + is easier to create full entries rather than comparing an entry with an + entry that must be retrieved (with ldapsearch or similar) from an existing + slapd DB to find changes. + + The addpartial overlay has been used in production since August 2004 and has + processed millions of records without incident. + +BUILDING + A Makefile is included, please set your LDAP_SRC directory properly. + +INSTALLATION + After compiling the addpartial overlay, add the following to your + slapd.conf: + + ### slapd.conf + ... + moduleload addpartial.so + ... + # after database directive... + # this overlay should be the last overlay in the config file to ensure that + # it properly intercepts the add request + overlay addpartial + ... + ### end slapd.conf + +CAVEATS + - In order to ensure that addpartial does what it needs to do, it should be + the last overlay configured so it will run before the other overlays. + This is especially important if you are using syncrepl, as the modify that + addpartial does will muck with the locking that takes place in the + syncprov overlay. + +--- +Copyright 2004-2022 The OpenLDAP Foundation. +Portions Copyright (C) Virginia Tech, David Hawes. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted only as authorized by the OpenLDAP +Public License. + +A copy of this license is available in file LICENSE in the +top-level directory of the distribution or, alternatively, at +http://www.OpenLDAP.org/license.html. diff --git a/contrib/slapd-modules/addpartial/addpartial-overlay.c b/contrib/slapd-modules/addpartial/addpartial-overlay.c new file mode 100644 index 0000000..b1d637b --- /dev/null +++ b/contrib/slapd-modules/addpartial/addpartial-overlay.c @@ -0,0 +1,349 @@ +/* addpartial-overlay.c */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2004-2022 The OpenLDAP Foundation. + * Portions Copyright (C) 2004 Virginia Tech, David Hawes. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * http://www.OpenLDAP.org/license.html. + */ +/* ACKNOLEDGEDMENTS: + * This work was initially developed by David Hawes of Virginia Tech + * for inclusion in OpenLDAP Software. + */ +/* addpartial-overlay + * + * This is an OpenLDAP overlay that intercepts ADD requests, determines if a + * change has actually taken place for that record, and then performs a modify + * request for those values that have changed (modified, added, deleted). If + * the record has not changed in any way, it is ignored. If the record does not + * exist, the record falls through to the normal add mechanism. This overlay is + * useful for replicating from sources that are not LDAPs where it is easier to + * build entire records than to determine the changes (i.e. a database). + */ + +#include "portable.h" +#include "slap.h" + +static int collect_error_msg_cb( Operation *op, SlapReply *rs); + +static slap_overinst addpartial; + +/** + * The meat of the overlay. Search for the record, determine changes, take + * action or fall through. + */ +static int addpartial_add( Operation *op, SlapReply *rs) +{ + Operation nop = *op; + Entry *toAdd = NULL; + Entry *found = NULL; + slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; + int rc; + + toAdd = op->oq_add.rs_e; + + Debug(LDAP_DEBUG_TRACE, "%s: toAdd->e_nname.bv_val: %s\n", + addpartial.on_bi.bi_type, toAdd->e_nname.bv_val ); + + /* if the user doesn't have access, fall through to the normal ADD */ + if(!access_allowed(op, toAdd, slap_schema.si_ad_entry, + NULL, ACL_WRITE, NULL)) + { + return SLAP_CB_CONTINUE; + } + + rc = overlay_entry_get_ov(&nop, &nop.o_req_ndn, NULL, NULL, 0, &found, on); + + if(rc != LDAP_SUCCESS) + { + Debug(LDAP_DEBUG_TRACE, + "%s: no entry found, falling through to normal add\n", + addpartial.on_bi.bi_type ); + return SLAP_CB_CONTINUE; + } + else + { + Debug(LDAP_DEBUG_TRACE, "%s: found the dn\n", addpartial.on_bi.bi_type ); + + if(found) + { + Attribute *attr = NULL; + Attribute *at = NULL; + int ret; + Modifications *mods = NULL; + Modifications **modtail = &mods; + Modifications *mod = NULL; + + Debug(LDAP_DEBUG_TRACE, "%s: have an entry!\n", + addpartial.on_bi.bi_type ); + + /* determine if the changes are in the found entry */ + for(attr = toAdd->e_attrs; attr; attr = attr->a_next) + { + if(attr->a_desc->ad_type->sat_atype.at_usage != 0) continue; + + at = attr_find(found->e_attrs, attr->a_desc); + if(!at) + { + Debug(LDAP_DEBUG_TRACE, "%s: Attribute %s not found!\n", + addpartial.on_bi.bi_type, + attr->a_desc->ad_cname.bv_val ); + mod = (Modifications *) ch_malloc(sizeof( + Modifications)); + mod->sml_flags = 0; + mod->sml_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES; + mod->sml_op &= LDAP_MOD_OP; + mod->sml_next = NULL; + mod->sml_desc = attr->a_desc; + mod->sml_type = attr->a_desc->ad_cname; + mod->sml_values = attr->a_vals; + mod->sml_nvalues = attr->a_nvals; + mod->sml_numvals = attr->a_numvals; + *modtail = mod; + modtail = &mod->sml_next; + } + else + { + MatchingRule *mr = attr->a_desc->ad_type->sat_equality; + struct berval *bv; + const char *text; + int acount , bcount; + Debug(LDAP_DEBUG_TRACE, "%s: Attribute %s found\n", + addpartial.on_bi.bi_type, + attr->a_desc->ad_cname.bv_val ); + + for(bv = attr->a_vals, acount = 0; bv->bv_val != NULL; + bv++, acount++) + { + /* count num values for attr */ + } + for(bv = at->a_vals, bcount = 0; bv->bv_val != NULL; + bv++, bcount++) + { + /* count num values for attr */ + } + if(acount != bcount) + { + Debug(LDAP_DEBUG_TRACE, "%s: acount != bcount, %s\n", + addpartial.on_bi.bi_type, + "replace all" ); + mod = (Modifications *) ch_malloc(sizeof( + Modifications)); + mod->sml_flags = 0; + mod->sml_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES; + mod->sml_op &= LDAP_MOD_OP; + mod->sml_next = NULL; + mod->sml_desc = attr->a_desc; + mod->sml_type = attr->a_desc->ad_cname; + mod->sml_values = attr->a_vals; + mod->sml_nvalues = attr->a_nvals; + mod->sml_numvals = attr->a_numvals; + *modtail = mod; + modtail = &mod->sml_next; + continue; + } + + for(bv = attr->a_vals; bv->bv_val != NULL; bv++) + { + struct berval *v; + ret = -1; + + for(v = at->a_vals; v->bv_val != NULL; v++) + { + int r; + if(mr && ((r = value_match(&ret, attr->a_desc, mr, + SLAP_MR_VALUE_OF_ASSERTION_SYNTAX, + bv, v, &text)) == 0)) + { + if(ret == 0) + break; + } + else + { + Debug(LDAP_DEBUG_TRACE, + "%s: \tvalue DNE, r: %d \n", + addpartial.on_bi.bi_type, + r ); + ret = strcmp(bv->bv_val, v->bv_val); + if(ret == 0) + break; + } + } + + if(ret == 0) + { + Debug(LDAP_DEBUG_TRACE, + "%s: \tvalue %s exists, ret: %d\n", + addpartial.on_bi.bi_type, bv->bv_val, ret); + } + else + { + Debug(LDAP_DEBUG_TRACE, + "%s: \tvalue %s DNE, ret: %d\n", + addpartial.on_bi.bi_type, bv->bv_val, ret); + mod = (Modifications *) ch_malloc(sizeof( + Modifications)); + mod->sml_flags = 0; + mod->sml_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES; + mod->sml_op &= LDAP_MOD_OP; + mod->sml_next = NULL; + mod->sml_desc = attr->a_desc; + mod->sml_type = attr->a_desc->ad_cname; + mod->sml_values = attr->a_vals; + mod->sml_nvalues = attr->a_nvals; + mod->sml_numvals = attr->a_numvals; + *modtail = mod; + modtail = &mod->sml_next; + break; + } + } + } + } + + /* determine if any attributes were deleted */ + for(attr = found->e_attrs; attr; attr = attr->a_next) + { + if(attr->a_desc->ad_type->sat_atype.at_usage != 0) continue; + + at = NULL; + at = attr_find(toAdd->e_attrs, attr->a_desc); + if(!at) + { + Debug(LDAP_DEBUG_TRACE, + "%s: Attribute %s not found in new entry!!!\n", + addpartial.on_bi.bi_type, + attr->a_desc->ad_cname.bv_val ); + mod = (Modifications *) ch_malloc(sizeof( + Modifications)); + mod->sml_flags = 0; + mod->sml_op = LDAP_MOD_REPLACE; + mod->sml_next = NULL; + mod->sml_desc = attr->a_desc; + mod->sml_type = attr->a_desc->ad_cname; + mod->sml_values = NULL; + mod->sml_nvalues = NULL; + mod->sml_numvals = 0; + *modtail = mod; + modtail = &mod->sml_next; + } + else + { + Debug(LDAP_DEBUG_TRACE, + "%s: Attribute %s found in new entry\n", + addpartial.on_bi.bi_type, + at->a_desc->ad_cname.bv_val ); + } + } + + overlay_entry_release_ov(&nop, found, 0, on); + + if(mods) + { + Modifications *m = NULL; + Modifications *toDel; + int modcount; + slap_callback nullcb = { NULL, collect_error_msg_cb, + NULL, NULL }; + + Debug(LDAP_DEBUG_TRACE, "%s: mods to do...\n", + addpartial.on_bi.bi_type ); + + nop.o_tag = LDAP_REQ_MODIFY; + nop.orm_modlist = mods; + nop.orm_no_opattrs = 0; + nop.o_callback = &nullcb; + nop.o_bd->bd_info = (BackendInfo *) on->on_info; + + for(m = mods, modcount = 0; m; m = m->sml_next, + modcount++) + { + /* count number of mods */ + } + + Debug(LDAP_DEBUG_TRACE, "%s: number of mods: %d\n", + addpartial.on_bi.bi_type, modcount ); + + if(nop.o_bd->be_modify) + { + SlapReply nrs = { REP_RESULT }; + rc = (nop.o_bd->be_modify)(&nop, &nrs); + } + + if(rc == LDAP_SUCCESS) + { + Debug(LDAP_DEBUG_TRACE, + "%s: modify successful\n", + addpartial.on_bi.bi_type ); + } + else + { + Debug(LDAP_DEBUG_TRACE, "%s: modify unsuccessful: %d\n", + addpartial.on_bi.bi_type, rc ); + rs->sr_err = rc; + if(nullcb.sc_private) + { + rs->sr_text = nullcb.sc_private; + } + } + + Debug(LDAP_DEBUG_TRACE, "%s: freeing mods...\n", + addpartial.on_bi.bi_type ); + + for(toDel = mods; toDel; toDel = mods) + { + mods = mods->sml_next; + ch_free(toDel); + } + } + else + { + Debug(LDAP_DEBUG_TRACE, "%s: no mods to process\n", + addpartial.on_bi.bi_type ); + } + } + else + { + Debug(LDAP_DEBUG_TRACE, "%s: no entry!\n", + addpartial.on_bi.bi_type ); + } + + op->o_callback = NULL; + send_ldap_result( op, rs ); + ch_free((void *)rs->sr_text); + rs->sr_text = NULL; + + return LDAP_SUCCESS; + } +} + +static int collect_error_msg_cb( Operation *op, SlapReply *rs) +{ + if(rs->sr_text) + { + op->o_callback->sc_private = (void *) ch_strdup(rs->sr_text); + } + + return LDAP_SUCCESS; +} + +int addpartial_init() +{ + addpartial.on_bi.bi_type = "addpartial"; + addpartial.on_bi.bi_flags = SLAPO_BFLAG_SINGLE; + addpartial.on_bi.bi_op_add = addpartial_add; + + return (overlay_register(&addpartial)); +} + +int init_module(int argc, char *argv[]) +{ + return addpartial_init(); +} |