]> git.sur5r.net Git - openldap/commitdiff
Microsoft-style Update Sequence Numbers
authorHoward Chu <hyc@openldap.org>
Thu, 27 Sep 2007 02:09:38 +0000 (02:09 +0000)
committerHoward Chu <hyc@openldap.org>
Thu, 27 Sep 2007 02:09:38 +0000 (02:09 +0000)
contrib/slapd-modules/usn/README [new file with mode: 0644]
contrib/slapd-modules/usn/usn.c [new file with mode: 0644]

diff --git a/contrib/slapd-modules/usn/README b/contrib/slapd-modules/usn/README
new file mode 100644 (file)
index 0000000..05d1398
--- /dev/null
@@ -0,0 +1,38 @@
+Copyright 2007 Howard Chu, Symas Corp. 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 the file LICENSE in the
+top-level directory of the distribution or, alternatively, at
+<http://www.OpenLDAP.org/license.html>.
+
+This directory contains a slapd overlay, usn, that extends slapd
+to maintain the usnCreated and usnChanged operational attributes
+normally used by Microsoft ActiveDirectory.
+
+To use the overlay, add:
+
+       moduleload <path to>usn.so
+       ...
+
+       database bdb
+       ...
+       overlay usn
+
+to your slapd configuration file. The schema definitions for the
+two USN attributes are hardcoded in this overlay.
+
+No Makefile is provided. Just compile with an invocation like
+       gcc -c -I ../../include/ -I ../../servers/slapd -DSLAPD_OVER_USN=SLAPD_MOD_DYNAMIC usn.c
+       gcc -shared -o usn.so usn.o
+
+This overlay is only set up to be built as a dynamically loaded module.
+On most platforms, in order for the module to be usable, all of the 
+library dependencies must also be available as shared libraries.
+
+If you need to build the overlay statically, you will have to move it into the
+slapd/overlays directory and edit the Makefile and overlays.c to reference
+it. You will also have to define SLAPD_OVER_USN to SLAPD_MOD_STATIC,
+and add the relevant libraries to the main slapd link command.
diff --git a/contrib/slapd-modules/usn/usn.c b/contrib/slapd-modules/usn/usn.c
new file mode 100644 (file)
index 0000000..c102027
--- /dev/null
@@ -0,0 +1,329 @@
+/* usn.c - Maintain Microsoft-style Update Sequence Numbers */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2007 The OpenLDAP Foundation.
+ * 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 the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion in
+ * OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_USN
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "config.h"
+
+/* This overlay intercepts write operations and adds a Microsoft-style
+ * USN to the target entry.
+ */
+
+typedef struct usn_info {
+       int ui_current;
+       ldap_pvt_thread_mutex_t ui_mutex;
+} usn_info_t;
+
+static AttributeDescription *ad_usnCreated, *ad_usnChanged;
+
+static struct {
+       char *desc;
+       AttributeDescription **adp;
+} as[] = {
+       { "( 1.2.840.113556.1.2.19 "
+           "NAME 'uSNCreated' "
+           "SYNTAX '1.2.840.113556.1.4.906' "
+               "SINGLE-VALUE "
+               "NO-USER-MODIFICATION )",
+               &ad_usnCreated },
+       { "( 1.2.840.113556.1.2.120 "
+               "NAME 'uSNChanged' "
+               "SYNTAX '1.2.840.113556.1.4.906' "
+               "SINGLE-VALUE "
+               "NO-USER-MODIFICATION )",
+               &ad_usnChanged },
+       { NULL }
+};
+
+static int
+usn_func( Operation *op, SlapReply *rs )
+{
+       slap_overinst           *on = (slap_overinst *) op->o_bd->bd_info;
+       usn_info_t              *ui = on->on_bi.bi_private;
+       int my_usn;
+       char intbuf[64];
+       struct berval bv[2];
+
+       ldap_pvt_thread_mutex_lock( &ui->ui_mutex );
+       ui->ui_current++;
+       my_usn = ui->ui_current;
+       ldap_pvt_thread_mutex_unlock( &ui->ui_mutex );
+
+       BER_BVZERO(&bv[1]);
+       bv[0].bv_val = intbuf;
+       bv[0].bv_len = snprintf( intbuf, sizeof(intbuf), "%d", my_usn );
+       switch(op->o_tag) {
+       case LDAP_REQ_ADD:
+               attr_merge( op->ora_e, ad_usnCreated, bv, NULL );
+               attr_merge( op->ora_e, ad_usnChanged, bv, NULL );
+               break;
+       case LDAP_REQ_DELETE:
+               /* Probably need to update root usnLastObjRem */
+               break;
+       default: {
+               /* Modify, ModDN */
+               Modifications *ml, *mod = ch_calloc( sizeof( Modifications ), 1 );
+               for ( ml = op->orm_modlist; ml && ml->sml_next; ml = ml->sml_next );
+               ml->sml_next = mod;
+               mod->sml_desc = ad_usnChanged;
+               mod->sml_numvals = 1;
+               value_add_one( &mod->sml_values, &bv[0] );
+               mod->sml_nvalues = NULL;
+               mod->sml_op = LDAP_MOD_REPLACE;
+               mod->sml_flags = 0;
+               mod->sml_next = NULL;
+               break;
+               }
+       }
+       return SLAP_CB_CONTINUE;
+}
+
+static int
+usn_operational(
+       Operation *op,
+       SlapReply *rs )
+{
+       slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
+       usn_info_t              *ui = (usn_info_t *)on->on_bi.bi_private;
+
+       if ( rs->sr_entry &&
+               dn_match( &rs->sr_entry->e_nname, op->o_bd->be_nsuffix )) {
+
+               if ( SLAP_OPATTRS( rs->sr_attr_flags ) ||
+                       ad_inlist( ad_usnChanged, rs->sr_attrs )) {
+                       Attribute *a, **ap = NULL;
+                       char intbuf[64];
+                       struct berval bv;
+                       int my_usn;
+
+                       for ( a=rs->sr_entry->e_attrs; a; a=a->a_next ) {
+                               if ( a->a_desc == ad_usnChanged )
+                                       break;
+                       }
+
+                       if ( !a ) {
+                               for ( ap = &rs->sr_operational_attrs; *ap;
+                                       ap=&(*ap)->a_next );
+
+                                       a = attr_alloc( ad_usnChanged );
+                                       *ap = a;
+                               }
+
+                       if ( !ap ) {
+                               if ( !rs->sr_flags & REP_ENTRY_MODIFIABLE ) {
+                                       rs->sr_entry = entry_dup( rs->sr_entry );
+                                       rs->sr_flags |=
+                                               REP_ENTRY_MODIFIABLE|REP_ENTRY_MUSTBEFREED;
+                                       a = attr_find( rs->sr_entry->e_attrs,
+                                               ad_usnChanged );
+                               }
+                               ber_bvarray_free( a->a_vals );
+                               a->a_vals = NULL;
+                               a->a_numvals = 0;
+                       }
+                       ldap_pvt_thread_mutex_lock( &ui->ui_mutex );
+                       my_usn = ui->ui_current;
+                       ldap_pvt_thread_mutex_unlock( &ui->ui_mutex );
+                       bv.bv_len = snprintf( intbuf, sizeof(intbuf), "%d", my_usn );
+                       bv.bv_val = intbuf;
+                       attr_valadd( a, &bv, NULL, 1 );
+               }
+       }
+       return SLAP_CB_CONTINUE;
+}
+
+/* Read the old USN from the underlying DB. This code is
+ * stolen from the syncprov overlay.
+ */
+static int
+usn_db_open(
+       BackendDB *be,
+       ConfigReply *cr)
+{
+       slap_overinst   *on = (slap_overinst *) be->bd_info;
+       usn_info_t *ui = (usn_info_t *)on->on_bi.bi_private;
+
+       Connection conn = { 0 };
+       OperationBuffer opbuf;
+       Operation *op;
+       Entry *e = NULL;
+       Attribute *a;
+       int rc;
+       void *thrctx = NULL;
+
+       thrctx = ldap_pvt_thread_pool_context();
+       connection_fake_init( &conn, &opbuf, thrctx );
+       op = &opbuf.ob_op;
+       op->o_bd = be;
+       op->o_dn = be->be_rootdn;
+       op->o_ndn = be->be_rootndn;
+
+       rc = overlay_entry_get_ov( op, be->be_nsuffix, NULL,
+               slap_schema.si_ad_contextCSN, 0, &e, on );
+
+       if ( e ) {
+               a = attr_find( e->e_attrs, ad_usnChanged );
+               if ( a ) {
+                       ui->ui_current = atoi( a->a_vals[0].bv_val );
+               }
+               overlay_entry_release_ov( op, e, 0, on );
+       }
+       return 0;
+}
+
+static int
+usn_db_init(
+       BackendDB *be,
+       ConfigReply *cr
+)
+{
+       slap_overinst   *on = (slap_overinst *)be->bd_info;
+       usn_info_t      *ui;
+
+       if ( SLAP_ISGLOBALOVERLAY( be ) ) {
+               Debug( LDAP_DEBUG_ANY,
+                       "usn must be instantiated within a database.\n",
+                       0, 0, 0 );
+               return 1;
+       }
+
+       ui = ch_calloc(1, sizeof(usn_info_t));
+       on->on_bi.bi_private = ui;
+       return 0;
+}
+
+static int
+usn_db_close(
+       BackendDB *be,
+       ConfigReply *cr
+)
+{
+       slap_overinst   *on = (slap_overinst *)be->bd_info;
+       usn_info_t      *ui = on->on_bi.bi_private;
+       Connection conn = {0};
+       OperationBuffer opbuf;
+       Operation *op;
+       SlapReply rs = {REP_RESULT};
+       void *thrctx;
+
+       Modifications mod;
+       SlapReply rsm = { 0 };
+       slap_callback cb = {0};
+       char intbuf[64];
+       struct berval bv[2];
+
+       thrctx = ldap_pvt_thread_pool_context();
+       connection_fake_init( &conn, &opbuf, thrctx );
+       op = &opbuf.ob_op;
+       op->o_bd = be;
+       BER_BVZERO( &bv[1] );
+       bv[0].bv_len = snprintf( intbuf, sizeof(intbuf), "%d", ui->ui_current );
+       bv[0].bv_val = intbuf;
+       mod.sml_numvals = 1;
+       mod.sml_values = bv;
+       mod.sml_nvalues = NULL;
+       mod.sml_desc = ad_usnChanged;
+       mod.sml_op = LDAP_MOD_REPLACE;
+       mod.sml_flags = 0;
+       mod.sml_next = NULL;
+
+       cb.sc_response = slap_null_cb;
+       op->o_tag = LDAP_REQ_MODIFY;
+       op->o_callback = &cb;
+       op->orm_modlist = &mod;
+       op->orm_no_opattrs = 1;
+       op->o_dn = be->be_rootdn;
+       op->o_ndn = be->be_rootndn;
+       op->o_req_dn = op->o_bd->be_suffix[0];
+       op->o_req_ndn = op->o_bd->be_nsuffix[0];
+       op->o_bd->bd_info = on->on_info->oi_orig;
+       op->o_managedsait = SLAP_CONTROL_NONCRITICAL;
+       op->o_no_schema_check = 1;
+       op->o_bd->be_modify( op, &rs );
+       if ( mod.sml_next != NULL ) {
+               slap_mods_free( mod.sml_next, 1 );
+       }
+       return 0;
+}
+
+static int
+usn_db_destroy(
+       BackendDB *be,
+       ConfigReply *cr
+)
+{
+       slap_overinst   *on = (slap_overinst *)be->bd_info;
+       ch_free( on->on_bi.bi_private );
+       return 0;
+}
+
+/* This overlay is set up for dynamic loading via moduleload. For static
+ * configuration, you'll need to arrange for the slap_overinst to be
+ * initialized and registered by some other function inside slapd.
+ */
+
+static slap_overinst usn;
+
+int
+usn_init( void )
+{
+       int i, code;
+
+       memset( &usn, 0, sizeof( slap_overinst ) );
+       usn.on_bi.bi_type = "usn";
+       usn.on_bi.bi_db_init = usn_db_init;
+       usn.on_bi.bi_db_destroy = usn_db_destroy;
+       usn.on_bi.bi_db_open = usn_db_open;
+       usn.on_bi.bi_db_close = usn_db_close;
+
+       usn.on_bi.bi_op_modify = usn_func;
+       usn.on_bi.bi_op_modrdn = usn_func;
+       usn.on_bi.bi_op_add = usn_func;
+       usn.on_bi.bi_op_delete = usn_func;
+       usn.on_bi.bi_operational = usn_operational;
+
+       for ( i = 0; as[i].desc; i++ ) {
+               code = register_at( as[i].desc, as[i].adp, 0 );
+               if ( code ) {
+                       Debug( LDAP_DEBUG_ANY,
+                               "usn_init: register_at #%d failed\n", i, 0, 0 );
+                       return code;
+               }
+       }
+       return overlay_register( &usn );
+}
+
+#if SLAPD_OVER_USN == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+       return usn_init();
+}
+#endif /* SLAPD_OVER_USN == SLAPD_MOD_DYNAMIC */
+
+#endif /* defined(SLAPD_OVER_USN) */