]> git.sur5r.net Git - openldap/commitdiff
New module. ITS#3593
authorGavin Henry <ghenry@openldap.org>
Fri, 27 Jul 2007 11:08:09 +0000 (11:08 +0000)
committerGavin Henry <ghenry@openldap.org>
Fri, 27 Jul 2007 11:08:09 +0000 (11:08 +0000)
contrib/slapd-modules/addpartial/COPYRIGHT [new file with mode: 0644]
contrib/slapd-modules/addpartial/LICENSE [new file with mode: 0644]
contrib/slapd-modules/addpartial/Makefile [new file with mode: 0644]
contrib/slapd-modules/addpartial/README [new file with mode: 0644]
contrib/slapd-modules/addpartial/addpartial-overlay.c [new file with mode: 0644]

diff --git a/contrib/slapd-modules/addpartial/COPYRIGHT b/contrib/slapd-modules/addpartial/COPYRIGHT
new file mode 100644 (file)
index 0000000..fda001e
--- /dev/null
@@ -0,0 +1,10 @@
+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/LICENSE b/contrib/slapd-modules/addpartial/LICENSE
new file mode 100644 (file)
index 0000000..83a87ef
--- /dev/null
@@ -0,0 +1,47 @@
+The OpenLDAP Public License
+  Version 2.8, 17 August 2003
+
+  Redistribution and use of this software and associated documentation
+  ("Software"), with or without modification, are permitted provided
+  that the following conditions are met:
+
+  1. Redistributions in source form must retain copyright statements
+     and notices,
+
+  2. Redistributions in binary form must reproduce applicable copyright
+     statements and notices, this list of conditions, and the following
+     disclaimer in the documentation and/or other materials provided
+     with the distribution, and
+
+  3. Redistributions must contain a verbatim copy of this document.
+
+The OpenLDAP Foundation may revise this license from time to time.
+Each revision is distinguished by a version number.  You may use
+this Software under terms of this license revision or under the
+terms of any subsequent revision of the license.
+
+THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND ITS
+CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT
+SHALL THE OPENLDAP FOUNDATION, ITS CONTRIBUTORS, OR THE AUTHOR(S)
+OR OWNER(S) OF THE SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+The names of the authors and copyright holders must not be used in
+advertising or otherwise to promote the sale, use or other dealing
+in this Software without specific, written prior permission.  Title
+to copyright in this Software shall at all times remain with copyright
+holders.
+
+OpenLDAP is a registered trademark of the OpenLDAP Foundation.
+
+Copyright 1999-2003 The OpenLDAP Foundation, Redwood City,
+California, USA.  All Rights Reserved.  Permission to copy and
+distribute verbatim copies of this document is granted.
diff --git a/contrib/slapd-modules/addpartial/Makefile b/contrib/slapd-modules/addpartial/Makefile
new file mode 100644 (file)
index 0000000..60a1b59
--- /dev/null
@@ -0,0 +1,12 @@
+OPENLDAP_SRC=/usr/local/src/openldap-2.3.32
+CPPFLAGS+=-I${OPENLDAP_SRC}/include -I${OPENLDAP_SRC}/servers/slapd
+LDFLAGS+=-L/usr/local/openldap-2.3.32/lib
+CC=gcc
+
+all: addpartial-overlay.so
+
+addpartial-overlay.so: addpartial-overlay.c
+       $(CC) -shared $(CPPFLAGS) $(LDFLAGS) -Wall -o $@ $?
+
+clean:
+       rm addpartial-overlay.so
diff --git a/contrib/slapd-modules/addpartial/README b/contrib/slapd-modules/addpartial/README
new file mode 100644 (file)
index 0000000..dca8b14
--- /dev/null
@@ -0,0 +1,61 @@
+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 addpartial overlay will write changes to the
+    replog (using the slap_replog_cb).  If you are using syncrepl for
+    replication, 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 OPENLDAP_SRC directory properly.
+
+INSTALLATION
+    After compiling the addpartial overlay, add the following to your 
+    slapd.conf:
+
+    ### slapd.conf
+    ...
+    moduleload /path/to/addpartial-overlay.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.
diff --git a/contrib/slapd-modules/addpartial/addpartial-overlay.c b/contrib/slapd-modules/addpartial/addpartial-overlay.c
new file mode 100644 (file)
index 0000000..9210040
--- /dev/null
@@ -0,0 +1,434 @@
+/**
+ * $Id: addpartial-overlay.c 5376 2007-01-26 20:03:13Z dhawes $
+ *
+ * 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.
+ *
+ * SEE LICENSE FOR MORE INFORMATION
+ *
+ * Author:  David H. Hawes, Jr.
+ * Email:   dhawes@vt.edu
+ * Version: $Revision: 5376 $
+ * Updated: $Date: 2007-01-26 15:03:13 -0500 (Fri, 26 Jan 2007) $
+ * 
+ * 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 <stdio.h>
+#include <ac/string.h>
+#include <ac/socket.h>
+#include "slap.h"
+#include <unistd.h>
+#include <lutil.h>
+
+static int addpartial_search_cb( Operation *op, SlapReply *rs);
+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;
+    SlapReply nrs = { REP_RESULT };
+    Filter *filter = NULL;
+    Entry *toAdd = NULL;
+    struct berval fstr = BER_BVNULL;
+    slap_callback cb = { NULL, addpartial_search_cb, NULL, 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,0);
+
+    /* 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;
+    }
+    
+    rs->sr_text = NULL;
+
+    nop.o_callback = &cb;
+    op->o_bd->bd_info = (BackendInfo *) on->on_info;
+    nop.o_tag = LDAP_REQ_SEARCH;
+    nop.o_ctrls = NULL;
+    
+    filter = str2filter("(objectclass=*)");
+    filter2bv(filter, &fstr);
+
+    nop.ors_scope = LDAP_SCOPE_BASE;
+    nop.ors_deref = LDAP_DEREF_NEVER;
+    nop.ors_slimit = -1;//SLAP_NO_LIMIT;
+    nop.ors_tlimit = -1;//SLAP_NO_LIMIT;
+    nop.ors_attrsonly = 0;
+    nop.ors_attrs = slap_anlist_no_attrs;
+    nop.ors_filter = filter;
+    nop.ors_filterstr = fstr;
+
+    memset(&nrs, 0, sizeof(nrs));
+    nrs.sr_type = REP_RESULT;
+    nrs.sr_err = LDAP_SUCCESS;
+    nrs.sr_entry = NULL;
+    nrs.sr_flags |= REP_ENTRY_MUSTBEFREED;
+    nrs.sr_text = NULL;
+
+    Debug(LDAP_DEBUG_TRACE, "%s: performing search\n", addpartial.on_bi.bi_type,
+          0,0);
+
+    if(nop.o_bd->be_search)
+    {
+        rc = nop.o_bd->be_search(&nop, &nrs);
+        Debug(LDAP_DEBUG_TRACE, "%s: search performed\n",
+              addpartial.on_bi.bi_type,0,0);
+    }
+    else
+    {
+        Debug(LDAP_DEBUG_TRACE, "%s: backend missing search function\n",
+              addpartial.on_bi.bi_type,0,0);
+    }
+
+    if(filter)
+        filter_free(filter);
+    if(fstr.bv_val)
+        ch_free(fstr.bv_val);
+
+    if(rc != LDAP_SUCCESS)
+        return SLAP_CB_CONTINUE;
+    else
+    { 
+        Entry *found = NULL;
+        Debug(LDAP_DEBUG_TRACE, "%s: found the dn\n", addpartial.on_bi.bi_type,
+              0,0);
+        found = (Entry *) cb.sc_private;
+
+        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,0,0);
+
+           /* 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,0);
+                    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.bv_val = attr->a_desc->ad_cname.bv_val;
+                    mod->sml_type.bv_len = strlen(mod->sml_type.bv_val);
+                    mod->sml_values = attr->a_vals;
+                    mod->sml_nvalues = attr->a_nvals;
+                    *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,0);
+
+                    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",0);
+                        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.bv_val = attr->a_desc->ad_cname.bv_val;
+                        mod->sml_type.bv_len = strlen(mod->sml_type.bv_val);
+                        mod->sml_values = attr->a_vals;
+                        mod->sml_nvalues = attr->a_nvals;
+                        *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,0);
+                                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.bv_val = 
+                                                  attr->a_desc->ad_cname.bv_val;
+                            mod->sml_type.bv_len = strlen(mod->sml_type.bv_val);
+                            mod->sml_values = attr->a_vals;
+                            mod->sml_nvalues = attr->a_nvals;
+                            *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, 0);
+                    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.bv_val = 
+                                          attr->a_desc->ad_cname.bv_val;
+                    mod->sml_type.bv_len = strlen(mod->sml_type.bv_val);
+                    mod->sml_values = NULL;
+                    mod->sml_nvalues = NULL;
+                    *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, 0);
+                }
+            }
+
+            if(mods)
+            {
+                Debug(LDAP_DEBUG_TRACE, "%s: mods to do...\n",
+                      addpartial.on_bi.bi_type, 0, 0);
+                if(nop.o_bd->be_modify)
+                {
+                    Modifications *m = NULL;
+                    int modcount;
+                    slap_callback cb2 = { NULL, slap_replog_cb, NULL, NULL };
+                    slap_callback nullcb = { NULL, collect_error_msg_cb, 
+                                             NULL, NULL };
+                    char textbuf[SLAP_TEXT_BUFLEN];
+                    size_t textlen = sizeof textbuf;
+
+                    memset(&nrs, 0, sizeof(nrs));
+                    nrs.sr_type = REP_RESULT;
+                    nrs.sr_err = LDAP_SUCCESS;
+                    nrs.sr_entry = NULL;
+                    nrs.sr_text = NULL;
+
+                    nop.o_tag = LDAP_REQ_MODIFY;
+                    nop.orm_modlist = mods;
+                    cb2.sc_next = &nullcb;
+                    nop.o_callback = &cb2;
+                    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, 0);
+
+                    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, 0, 0);
+                    }
+                    else
+                    {
+                        Debug(LDAP_DEBUG_TRACE, "%s: modify unsuccessful: %d\n",
+                              addpartial.on_bi.bi_type, rc, 0);
+                        rs->sr_err = rc;
+                        if(nrs.sr_text)
+                        {
+                            rs->sr_text = nullcb.sc_private;
+                        }
+                    }
+
+                    Debug(LDAP_DEBUG_TRACE, "%s: freeing mods...\n",
+                          addpartial.on_bi.bi_type, 0, 0);
+
+                    if(mods != NULL)
+                    {
+                        Modifications *toDel;
+
+                        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, 0, 0);
+            }
+
+            if(found != NULL) ;
+                entry_free(found);
+        }
+        else
+        {
+            Debug(LDAP_DEBUG_TRACE, "%s: no entry!\n",
+                  addpartial.on_bi.bi_type, 0, 0);
+        }
+
+        op->o_callback = NULL;
+        send_ldap_result( op, rs );
+        ch_free((void *)rs->sr_text);
+        rs->sr_text = NULL;
+
+        return LDAP_SUCCESS;
+    }
+}
+
+static int addpartial_search_cb( Operation *op, SlapReply *rs)
+{
+    Entry *entry = NULL;
+
+    if(rs->sr_type != REP_SEARCH) return 0;
+        
+    Debug(LDAP_DEBUG_TRACE, "%s: addpartial_search_cb\n",
+          addpartial.on_bi.bi_type, 0, 0);
+
+    if(rs->sr_entry)
+    {
+        Debug(LDAP_DEBUG_TRACE, "%s: dn found: %s\n",
+              addpartial.on_bi.bi_type, rs->sr_entry->e_nname.bv_val, 0);
+        entry = rs->sr_entry;
+        op->o_callback->sc_private = (void *) entry_dup(entry);
+    }
+
+    return 0;
+}
+
+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_op_add = addpartial_add;
+
+    return (overlay_register(&addpartial));
+}
+
+int init_module(int argc, char *argv[]) 
+{
+        return addpartial_init();
+}