summaryrefslogtreecommitdiffstats
path: root/contrib/slapd-modules/addpartial
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/slapd-modules/addpartial')
-rw-r--r--contrib/slapd-modules/addpartial/Makefile46
-rw-r--r--contrib/slapd-modules/addpartial/README72
-rw-r--r--contrib/slapd-modules/addpartial/addpartial-overlay.c349
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();
+}