Makefile.am | 14 Makefile.in | 103 +++- ldap/admin/src/scripts/50contentsync.ldif | 22 ldap/ldif/template-dse.ldif.in | 21 ldap/servers/plugins/sync/sync.h | 196 ++++++++ ldap/servers/plugins/sync/sync_init.c | 174 +++++++ ldap/servers/plugins/sync/sync_persist.c | 693 ++++++++++++++++++++++++++++ ldap/servers/plugins/sync/sync_refresh.c | 732 ++++++++++++++++++++++++++++++ ldap/servers/plugins/sync/sync_util.c | 685 ++++++++++++++++++++++++++++ ldap/servers/slapd/connection.c | 7 ldap/servers/slapd/libslapd.def | 4 ldap/servers/slapd/operation.c | 58 ++ ldap/servers/slapd/plugin.c | 9 ldap/servers/slapd/proto-slap.h | 2 ldap/servers/slapd/result.c | 80 +++ ldap/servers/slapd/slapi-plugin.h | 9 16 files changed, 2799 insertions(+), 10 deletions(-)
New commits: commit 1e7c62deba556af7f6cf30e021dc64be1ad8ccc9 Author: Ludwig Krispenz lkrispen@redhat.com Date: Fri Sep 27 13:58:40 2013 +0200
Ticket 47388 - RFE to implement RFC4533 -ver2
Bug Description: Request to implement RFC4533 in ds 389
Fix Description: This fix impements the content synchronization as a directory server plugin, using the retro changelog to provide synchronization with cookies. The design is presented here: http://port389.org/wiki/Content_synchronization_plugin
https://fedorahosted.org/389/ticket/47388
Reviewed by: nkinder, Thanks
diff --git a/Makefile.am b/Makefile.am index 12d4641..441fe40 100644 --- a/Makefile.am +++ b/Makefile.am @@ -225,6 +225,7 @@ serverplugin_LTLIBRARIES = libacl-plugin.la libattr-unique-plugin.la \ libdes-plugin.la libdistrib-plugin.la libhttp-client-plugin.la \ liblinkedattrs-plugin.la libmanagedentries-plugin.la \ libmemberof-plugin.la libpassthru-plugin.la libpwdstorage-plugin.la \ + libcontentsync-plugin.la \ libreferint-plugin.la libreplication-plugin.la libretrocl-plugin.la \ libroles-plugin.la libstatechange-plugin.la libsyntax-plugin.la \ libviews-plugin.la libschemareload-plugin.la libusn-plugin.la \ @@ -564,6 +565,7 @@ update_DATA = ldap/admin/src/scripts/exampleupdate.pl \ ldap/admin/src/scripts/50refintprecedence.ldif \ ldap/admin/src/scripts/50retroclprecedence.ldif \ ldap/admin/src/scripts/50rootdnaccesscontrolplugin.ldif \ + ldap/admin/src/scripts/50contentsync.ldif \ ldap/admin/src/scripts/60upgradeschemafiles.pl \ ldap/admin/src/scripts/70upgradefromldif.pl \ ldap/admin/src/scripts/80upgradednformat.pl \ @@ -1135,6 +1137,18 @@ libpwdstorage_plugin_la_LIBADD = libslapd.la $(NSS_LINK) $(NSPR_LINK) $(LIBCRYPT libpwdstorage_plugin_la_LDFLAGS = -avoid-version
#------------------------ +# libcontentsync-plugin +#------------------------ +libcontentsync_plugin_la_SOURCES = ldap/servers/plugins/sync/sync_init.c \ + ldap/servers/plugins/sync/sync_util.c \ + ldap/servers/plugins/sync/sync_refresh.c \ + ldap/servers/plugins/sync/sync_persist.c + +libcontentsync_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS) +libcontentsync_plugin_la_LIBADD = libslapd.la $(NSS_LINK) $(NSPR_LINK) $(LIBCRYPT) +libcontentsync_plugin_la_LDFLAGS = -avoid-version + +#------------------------ # libreferint-plugin #------------------------ libreferint_plugin_la_SOURCES = ldap/servers/plugins/referint/referint.c diff --git a/Makefile.in b/Makefile.in index 48444c6..9c9bcd9 100644 --- a/Makefile.in +++ b/Makefile.in @@ -364,6 +364,18 @@ am_libcollation_plugin_la_OBJECTS = ldap/servers/plugins/collation/libcollation_ ldap/servers/plugins/collation/libcollation_plugin_la-config.lo \ ldap/servers/plugins/collation/libcollation_plugin_la-orfilter.lo libcollation_plugin_la_OBJECTS = $(am_libcollation_plugin_la_OBJECTS) +libcontentsync_plugin_la_DEPENDENCIES = libslapd.la \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) +am_libcontentsync_plugin_la_OBJECTS = ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_init.lo \ + ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_util.lo \ + ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_refresh.lo \ + ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_persist.lo +libcontentsync_plugin_la_OBJECTS = \ + $(am_libcontentsync_plugin_la_OBJECTS) +libcontentsync_plugin_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(libcontentsync_plugin_la_LDFLAGS) $(LDFLAGS) -o $@ libcos_plugin_la_DEPENDENCIES = libslapd.la $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) am_libcos_plugin_la_OBJECTS = \ @@ -1137,9 +1149,11 @@ SOURCES = $(libavl_a_SOURCES) $(libldaputil_a_SOURCES) \ $(libautomember_plugin_la_SOURCES) $(libback_ldbm_la_SOURCES) \ $(libbitwise_plugin_la_SOURCES) \ $(libchainingdb_plugin_la_SOURCES) \ - $(libcollation_plugin_la_SOURCES) $(libcos_plugin_la_SOURCES) \ - $(libderef_plugin_la_SOURCES) $(libdes_plugin_la_SOURCES) \ - $(libdistrib_plugin_la_SOURCES) $(libdna_plugin_la_SOURCES) \ + $(libcollation_plugin_la_SOURCES) \ + $(libcontentsync_plugin_la_SOURCES) \ + $(libcos_plugin_la_SOURCES) $(libderef_plugin_la_SOURCES) \ + $(libdes_plugin_la_SOURCES) $(libdistrib_plugin_la_SOURCES) \ + $(libdna_plugin_la_SOURCES) \ $(libhttp_client_plugin_la_SOURCES) \ $(liblinkedattrs_plugin_la_SOURCES) \ $(libmanagedentries_plugin_la_SOURCES) \ @@ -1171,9 +1185,11 @@ DIST_SOURCES = $(libavl_a_SOURCES) $(libldaputil_a_SOURCES) \ $(libautomember_plugin_la_SOURCES) $(libback_ldbm_la_SOURCES) \ $(libbitwise_plugin_la_SOURCES) \ $(libchainingdb_plugin_la_SOURCES) \ - $(libcollation_plugin_la_SOURCES) $(libcos_plugin_la_SOURCES) \ - $(libderef_plugin_la_SOURCES) $(libdes_plugin_la_SOURCES) \ - $(libdistrib_plugin_la_SOURCES) $(libdna_plugin_la_SOURCES) \ + $(libcollation_plugin_la_SOURCES) \ + $(libcontentsync_plugin_la_SOURCES) \ + $(libcos_plugin_la_SOURCES) $(libderef_plugin_la_SOURCES) \ + $(libdes_plugin_la_SOURCES) $(libdistrib_plugin_la_SOURCES) \ + $(libdna_plugin_la_SOURCES) \ $(libhttp_client_plugin_la_SOURCES) \ $(liblinkedattrs_plugin_la_SOURCES) \ $(libmanagedentries_plugin_la_SOURCES) \ @@ -1619,6 +1635,7 @@ serverplugin_LTLIBRARIES = libacl-plugin.la libattr-unique-plugin.la \ libdes-plugin.la libdistrib-plugin.la libhttp-client-plugin.la \ liblinkedattrs-plugin.la libmanagedentries-plugin.la \ libmemberof-plugin.la libpassthru-plugin.la libpwdstorage-plugin.la \ + libcontentsync-plugin.la \ libreferint-plugin.la libreplication-plugin.la libretrocl-plugin.la \ libroles-plugin.la libstatechange-plugin.la libsyntax-plugin.la \ libviews-plugin.la libschemareload-plugin.la libusn-plugin.la \ @@ -1954,6 +1971,7 @@ update_DATA = ldap/admin/src/scripts/exampleupdate.pl \ ldap/admin/src/scripts/50refintprecedence.ldif \ ldap/admin/src/scripts/50retroclprecedence.ldif \ ldap/admin/src/scripts/50rootdnaccesscontrolplugin.ldif \ + ldap/admin/src/scripts/50contentsync.ldif \ ldap/admin/src/scripts/60upgradeschemafiles.pl \ ldap/admin/src/scripts/70upgradefromldif.pl \ ldap/admin/src/scripts/80upgradednformat.pl \ @@ -2461,6 +2479,18 @@ libpwdstorage_plugin_la_LIBADD = libslapd.la $(NSS_LINK) $(NSPR_LINK) $(LIBCRYPT libpwdstorage_plugin_la_LDFLAGS = -avoid-version
#------------------------ +# libcontentsync-plugin +#------------------------ +libcontentsync_plugin_la_SOURCES = ldap/servers/plugins/sync/sync_init.c \ + ldap/servers/plugins/sync/sync_util.c \ + ldap/servers/plugins/sync/sync_refresh.c \ + ldap/servers/plugins/sync/sync_persist.c + +libcontentsync_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS) +libcontentsync_plugin_la_LIBADD = libslapd.la $(NSS_LINK) $(NSPR_LINK) $(LIBCRYPT) +libcontentsync_plugin_la_LDFLAGS = -avoid-version + +#------------------------ # libreferint-plugin #------------------------ libreferint_plugin_la_SOURCES = ldap/servers/plugins/referint/referint.c @@ -3504,6 +3534,26 @@ ldap/servers/plugins/collation/libcollation_plugin_la-orfilter.lo: \
libcollation-plugin.la: $(libcollation_plugin_la_OBJECTS) $(libcollation_plugin_la_DEPENDENCIES) $(EXTRA_libcollation_plugin_la_DEPENDENCIES) $(AM_V_GEN)$(libcollation_plugin_la_LINK) -rpath $(serverplugindir) $(libcollation_plugin_la_OBJECTS) $(libcollation_plugin_la_LIBADD) $(LIBS) +ldap/servers/plugins/sync/$(am__dirstamp): + @$(MKDIR_P) ldap/servers/plugins/sync + @: > ldap/servers/plugins/sync/$(am__dirstamp) +ldap/servers/plugins/sync/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) ldap/servers/plugins/sync/$(DEPDIR) + @: > ldap/servers/plugins/sync/$(DEPDIR)/$(am__dirstamp) +ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_init.lo: \ + ldap/servers/plugins/sync/$(am__dirstamp) \ + ldap/servers/plugins/sync/$(DEPDIR)/$(am__dirstamp) +ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_util.lo: \ + ldap/servers/plugins/sync/$(am__dirstamp) \ + ldap/servers/plugins/sync/$(DEPDIR)/$(am__dirstamp) +ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_refresh.lo: \ + ldap/servers/plugins/sync/$(am__dirstamp) \ + ldap/servers/plugins/sync/$(DEPDIR)/$(am__dirstamp) +ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_persist.lo: \ + ldap/servers/plugins/sync/$(am__dirstamp) \ + ldap/servers/plugins/sync/$(DEPDIR)/$(am__dirstamp) +libcontentsync-plugin.la: $(libcontentsync_plugin_la_OBJECTS) $(libcontentsync_plugin_la_DEPENDENCIES) $(EXTRA_libcontentsync_plugin_la_DEPENDENCIES) + $(libcontentsync_plugin_la_LINK) -rpath $(serverplugindir) $(libcontentsync_plugin_la_OBJECTS) $(libcontentsync_plugin_la_LIBADD) $(LIBS) ldap/servers/plugins/cos/$(am__dirstamp): @$(MKDIR_P) ldap/servers/plugins/cos @: > ldap/servers/plugins/cos/$(am__dirstamp) @@ -5193,6 +5243,8 @@ mostlyclean-compile: -rm -f ldap/servers/plugins/schema_reload/*.lo -rm -f ldap/servers/plugins/statechange/*.$(OBJEXT) -rm -f ldap/servers/plugins/statechange/*.lo + -rm -f ldap/servers/plugins/sync/*.$(OBJEXT) + -rm -f ldap/servers/plugins/sync/*.lo -rm -f ldap/servers/plugins/syntaxes/*.$(OBJEXT) -rm -f ldap/servers/plugins/syntaxes/*.lo -rm -f ldap/servers/plugins/uiduniq/*.$(OBJEXT) @@ -5381,6 +5433,10 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@ldap/servers/plugins/rootdn_access/$(DEPDIR)/librootdn_access_plugin_la-rootdn_access.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ldap/servers/plugins/schema_reload/$(DEPDIR)/libschemareload_plugin_la-schema_reload.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ldap/servers/plugins/statechange/$(DEPDIR)/libstatechange_plugin_la-statechange.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@ldap/servers/plugins/sync/$(DEPDIR)/libcontentsync_plugin_la-sync_init.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@ldap/servers/plugins/sync/$(DEPDIR)/libcontentsync_plugin_la-sync_persist.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@ldap/servers/plugins/sync/$(DEPDIR)/libcontentsync_plugin_la-sync_refresh.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@ldap/servers/plugins/sync/$(DEPDIR)/libcontentsync_plugin_la-sync_util.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ldap/servers/plugins/syntaxes/$(DEPDIR)/libsyntax_plugin_la-bin.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ldap/servers/plugins/syntaxes/$(DEPDIR)/libsyntax_plugin_la-bitstring.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ldap/servers/plugins/syntaxes/$(DEPDIR)/libsyntax_plugin_la-ces.Plo@am__quote@ @@ -6619,6 +6675,34 @@ ldap/servers/plugins/collation/libcollation_plugin_la-orfilter.lo: ldap/servers/ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcollation_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ldap/servers/plugins/collation/libcollation_plugin_la-orfilter.lo `test -f 'ldap/servers/plugins/collation/orfilter.c' || echo '$(srcdir)/'`ldap/servers/plugins/collation/orfilter.c
+ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_init.lo: ldap/servers/plugins/sync/sync_init.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontentsync_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_init.lo -MD -MP -MF ldap/servers/plugins/sync/$(DEPDIR)/libcontentsync_plugin_la-sync_init.Tpo -c -o ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_init.lo `test -f 'ldap/servers/plugins/sync/sync_init.c' || echo '$(srcdir)/'`ldap/servers/plugins/sync/sync_init.c +@am__fastdepCC_TRUE@ $(am__mv) ldap/servers/plugins/sync/$(DEPDIR)/libcontentsync_plugin_la-sync_init.Tpo ldap/servers/plugins/sync/$(DEPDIR)/libcontentsync_plugin_la-sync_init.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ldap/servers/plugins/sync/sync_init.c' object='ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_init.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontentsync_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_init.lo `test -f 'ldap/servers/plugins/sync/sync_init.c' || echo '$(srcdir)/'`ldap/servers/plugins/sync/sync_init.c + +ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_util.lo: ldap/servers/plugins/sync/sync_util.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontentsync_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_util.lo -MD -MP -MF ldap/servers/plugins/sync/$(DEPDIR)/libcontentsync_plugin_la-sync_util.Tpo -c -o ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_util.lo `test -f 'ldap/servers/plugins/sync/sync_util.c' || echo '$(srcdir)/'`ldap/servers/plugins/sync/sync_util.c +@am__fastdepCC_TRUE@ $(am__mv) ldap/servers/plugins/sync/$(DEPDIR)/libcontentsync_plugin_la-sync_util.Tpo ldap/servers/plugins/sync/$(DEPDIR)/libcontentsync_plugin_la-sync_util.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ldap/servers/plugins/sync/sync_util.c' object='ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_util.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontentsync_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_util.lo `test -f 'ldap/servers/plugins/sync/sync_util.c' || echo '$(srcdir)/'`ldap/servers/plugins/sync/sync_util.c + +ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_refresh.lo: ldap/servers/plugins/sync/sync_refresh.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontentsync_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_refresh.lo -MD -MP -MF ldap/servers/plugins/sync/$(DEPDIR)/libcontentsync_plugin_la-sync_refresh.Tpo -c -o ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_refresh.lo `test -f 'ldap/servers/plugins/sync/sync_refresh.c' || echo '$(srcdir)/'`ldap/servers/plugins/sync/sync_refresh.c +@am__fastdepCC_TRUE@ $(am__mv) ldap/servers/plugins/sync/$(DEPDIR)/libcontentsync_plugin_la-sync_refresh.Tpo ldap/servers/plugins/sync/$(DEPDIR)/libcontentsync_plugin_la-sync_refresh.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ldap/servers/plugins/sync/sync_refresh.c' object='ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_refresh.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontentsync_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_refresh.lo `test -f 'ldap/servers/plugins/sync/sync_refresh.c' || echo '$(srcdir)/'`ldap/servers/plugins/sync/sync_refresh.c + +ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_persist.lo: ldap/servers/plugins/sync/sync_persist.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontentsync_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_persist.lo -MD -MP -MF ldap/servers/plugins/sync/$(DEPDIR)/libcontentsync_plugin_la-sync_persist.Tpo -c -o ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_persist.lo `test -f 'ldap/servers/plugins/sync/sync_persist.c' || echo '$(srcdir)/'`ldap/servers/plugins/sync/sync_persist.c +@am__fastdepCC_TRUE@ $(am__mv) ldap/servers/plugins/sync/$(DEPDIR)/libcontentsync_plugin_la-sync_persist.Tpo ldap/servers/plugins/sync/$(DEPDIR)/libcontentsync_plugin_la-sync_persist.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ldap/servers/plugins/sync/sync_persist.c' object='ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_persist.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontentsync_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_persist.lo `test -f 'ldap/servers/plugins/sync/sync_persist.c' || echo '$(srcdir)/'`ldap/servers/plugins/sync/sync_persist.c + ldap/servers/plugins/cos/libcos_plugin_la-cos.lo: ldap/servers/plugins/cos/cos.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcos_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ldap/servers/plugins/cos/libcos_plugin_la-cos.lo -MD -MP -MF ldap/servers/plugins/cos/$(DEPDIR)/libcos_plugin_la-cos.Tpo -c -o ldap/servers/plugins/cos/libcos_plugin_la-cos.lo `test -f 'ldap/servers/plugins/cos/cos.c' || echo '$(srcdir)/'`ldap/servers/plugins/cos/cos.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ldap/servers/plugins/cos/$(DEPDIR)/libcos_plugin_la-cos.Tpo ldap/servers/plugins/cos/$(DEPDIR)/libcos_plugin_la-cos.Plo @@ -9491,6 +9575,7 @@ clean-libtool: -rm -rf ldap/servers/plugins/rootdn_access/.libs ldap/servers/plugins/rootdn_access/_libs -rm -rf ldap/servers/plugins/schema_reload/.libs ldap/servers/plugins/schema_reload/_libs -rm -rf ldap/servers/plugins/statechange/.libs ldap/servers/plugins/statechange/_libs + -rm -rf ldap/servers/plugins/sync/.libs ldap/servers/plugins/sync/_libs -rm -rf ldap/servers/plugins/syntaxes/.libs ldap/servers/plugins/syntaxes/_libs -rm -rf ldap/servers/plugins/uiduniq/.libs ldap/servers/plugins/uiduniq/_libs -rm -rf ldap/servers/plugins/usn/.libs ldap/servers/plugins/usn/_libs @@ -10175,6 +10260,8 @@ distclean-generic: -rm -f ldap/servers/plugins/schema_reload/$(am__dirstamp) -rm -f ldap/servers/plugins/statechange/$(DEPDIR)/$(am__dirstamp) -rm -f ldap/servers/plugins/statechange/$(am__dirstamp) + -rm -f ldap/servers/plugins/sync/$(DEPDIR)/$(am__dirstamp) + -rm -f ldap/servers/plugins/sync/$(am__dirstamp) -rm -f ldap/servers/plugins/syntaxes/$(DEPDIR)/$(am__dirstamp) -rm -f ldap/servers/plugins/syntaxes/$(am__dirstamp) -rm -f ldap/servers/plugins/uiduniq/$(DEPDIR)/$(am__dirstamp) @@ -10223,7 +10310,7 @@ clean-am: clean-binPROGRAMS clean-generic clean-libtool clean-local \
distclean: distclean-am -rm -f $(am__CONFIG_DISTCLEAN_FILES) - -rm -rf ldap/libraries/libavl/$(DEPDIR) ldap/servers/plugins/acct_usability/$(DEPDIR) ldap/servers/plugins/acctpolicy/$(DEPDIR) ldap/servers/plugins/acl/$(DEPDIR) ldap/servers/plugins/automember/$(DEPDIR) ldap/servers/plugins/bitwise/$(DEPDIR) ldap/servers/plugins/chainingdb/$(DEPDIR) ldap/servers/plugins/collation/$(DEPDIR) ldap/servers/plugins/cos/$(DEPDIR) ldap/servers/plugins/deref/$(DEPDIR) ldap/servers/plugins/distrib/$(DEPDIR) ldap/servers/plugins/dna/$(DEPDIR) ldap/servers/plugins/http/$(DEPDIR) ldap/servers/plugins/linkedattrs/$(DEPDIR) ldap/servers/plugins/memberof/$(DEPDIR) ldap/servers/plugins/mep/$(DEPDIR) ldap/servers/plugins/pam_passthru/$(DEPDIR) ldap/servers/plugins/passthru/$(DEPDIR) ldap/servers/plugins/posix-winsync/$(DEPDIR) ldap/servers/plugins/presence/$(DEPDIR) ldap/servers/plugins/pwdstorage/$(DEPDIR) ldap/servers/plugins/referint/$(DEPDIR) ldap/servers/plugins/replication/$(DEPDIR) ldap/servers/plugins/retrocl/$(DEPDIR) ldap/servers/plugins/rever/$ (DEPDIR) ldap/servers/plugins/roles/$(DEPDIR) ldap/servers/plugins/rootdn_access/$(DEPDIR) ldap/servers/plugins/schema_reload/$(DEPDIR) ldap/servers/plugins/statechange/$(DEPDIR) ldap/servers/plugins/syntaxes/$(DEPDIR) ldap/servers/plugins/uiduniq/$(DEPDIR) ldap/servers/plugins/usn/$(DEPDIR) ldap/servers/plugins/views/$(DEPDIR) ldap/servers/plugins/whoami/$(DEPDIR) ldap/servers/slapd/$(DEPDIR) ldap/servers/slapd/back-ldbm/$(DEPDIR) ldap/servers/slapd/tools/$(DEPDIR) ldap/servers/slapd/tools/ldclt/$(DEPDIR) ldap/servers/slapd/tools/rsearch/$(DEPDIR) ldap/servers/snmp/$(DEPDIR) ldap/systools/$(DEPDIR) lib/base/$(DEPDIR) lib/ldaputil/$(DEPDIR) lib/libaccess/$(DEPDIR) lib/libadmin/$(DEPDIR) lib/libsi18n/$(DEPDIR) + -rm -rf ldap/libraries/libavl/$(DEPDIR) ldap/servers/plugins/acct_usability/$(DEPDIR) ldap/servers/plugins/acctpolicy/$(DEPDIR) ldap/servers/plugins/acl/$(DEPDIR) ldap/servers/plugins/automember/$(DEPDIR) ldap/servers/plugins/bitwise/$(DEPDIR) ldap/servers/plugins/chainingdb/$(DEPDIR) ldap/servers/plugins/collation/$(DEPDIR) ldap/servers/plugins/cos/$(DEPDIR) ldap/servers/plugins/deref/$(DEPDIR) ldap/servers/plugins/distrib/$(DEPDIR) ldap/servers/plugins/dna/$(DEPDIR) ldap/servers/plugins/http/$(DEPDIR) ldap/servers/plugins/linkedattrs/$(DEPDIR) ldap/servers/plugins/memberof/$(DEPDIR) ldap/servers/plugins/mep/$(DEPDIR) ldap/servers/plugins/pam_passthru/$(DEPDIR) ldap/servers/plugins/passthru/$(DEPDIR) ldap/servers/plugins/posix-winsync/$(DEPDIR) ldap/servers/plugins/presence/$(DEPDIR) ldap/servers/plugins/pwdstorage/$(DEPDIR) ldap/servers/plugins/referint/$(DEPDIR) ldap/servers/plugins/replication/$(DEPDIR) ldap/servers/plugins/retrocl/$(DEPDIR) ldap/servers/plugins/rever/$ (DEPDIR) ldap/servers/plugins/roles/$(DEPDIR) ldap/servers/plugins/rootdn_access/$(DEPDIR) ldap/servers/plugins/schema_reload/$(DEPDIR) ldap/servers/plugins/statechange/$(DEPDIR) ldap/servers/plugins/sync/$(DEPDIR) ldap/servers/plugins/syntaxes/$(DEPDIR) ldap/servers/plugins/uiduniq/$(DEPDIR) ldap/servers/plugins/usn/$(DEPDIR) ldap/servers/plugins/views/$(DEPDIR) ldap/servers/plugins/whoami/$(DEPDIR) ldap/servers/slapd/$(DEPDIR) ldap/servers/slapd/back-ldbm/$(DEPDIR) ldap/servers/slapd/tools/$(DEPDIR) ldap/servers/slapd/tools/ldclt/$(DEPDIR) ldap/servers/slapd/tools/rsearch/$(DEPDIR) ldap/servers/snmp/$(DEPDIR) ldap/systools/$(DEPDIR) lib/base/$(DEPDIR) lib/ldaputil/$(DEPDIR) lib/libaccess/$(DEPDIR) lib/libadmin/$(DEPDIR) lib/libsi18n/$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-hdr distclean-libtool distclean-tags @@ -10279,7 +10366,7 @@ installcheck-am: maintainer-clean: maintainer-clean-am -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -rf $(top_srcdir)/autom4te.cache - -rm -rf ldap/libraries/libavl/$(DEPDIR) ldap/servers/plugins/acct_usability/$(DEPDIR) ldap/servers/plugins/acctpolicy/$(DEPDIR) ldap/servers/plugins/acl/$(DEPDIR) ldap/servers/plugins/automember/$(DEPDIR) ldap/servers/plugins/bitwise/$(DEPDIR) ldap/servers/plugins/chainingdb/$(DEPDIR) ldap/servers/plugins/collation/$(DEPDIR) ldap/servers/plugins/cos/$(DEPDIR) ldap/servers/plugins/deref/$(DEPDIR) ldap/servers/plugins/distrib/$(DEPDIR) ldap/servers/plugins/dna/$(DEPDIR) ldap/servers/plugins/http/$(DEPDIR) ldap/servers/plugins/linkedattrs/$(DEPDIR) ldap/servers/plugins/memberof/$(DEPDIR) ldap/servers/plugins/mep/$(DEPDIR) ldap/servers/plugins/pam_passthru/$(DEPDIR) ldap/servers/plugins/passthru/$(DEPDIR) ldap/servers/plugins/posix-winsync/$(DEPDIR) ldap/servers/plugins/presence/$(DEPDIR) ldap/servers/plugins/pwdstorage/$(DEPDIR) ldap/servers/plugins/referint/$(DEPDIR) ldap/servers/plugins/replication/$(DEPDIR) ldap/servers/plugins/retrocl/$(DEPDIR) ldap/servers/plugins/rever/$ (DEPDIR) ldap/servers/plugins/roles/$(DEPDIR) ldap/servers/plugins/rootdn_access/$(DEPDIR) ldap/servers/plugins/schema_reload/$(DEPDIR) ldap/servers/plugins/statechange/$(DEPDIR) ldap/servers/plugins/syntaxes/$(DEPDIR) ldap/servers/plugins/uiduniq/$(DEPDIR) ldap/servers/plugins/usn/$(DEPDIR) ldap/servers/plugins/views/$(DEPDIR) ldap/servers/plugins/whoami/$(DEPDIR) ldap/servers/slapd/$(DEPDIR) ldap/servers/slapd/back-ldbm/$(DEPDIR) ldap/servers/slapd/tools/$(DEPDIR) ldap/servers/slapd/tools/ldclt/$(DEPDIR) ldap/servers/slapd/tools/rsearch/$(DEPDIR) ldap/servers/snmp/$(DEPDIR) ldap/systools/$(DEPDIR) lib/base/$(DEPDIR) lib/ldaputil/$(DEPDIR) lib/libaccess/$(DEPDIR) lib/libadmin/$(DEPDIR) lib/libsi18n/$(DEPDIR) + -rm -rf ldap/libraries/libavl/$(DEPDIR) ldap/servers/plugins/acct_usability/$(DEPDIR) ldap/servers/plugins/acctpolicy/$(DEPDIR) ldap/servers/plugins/acl/$(DEPDIR) ldap/servers/plugins/automember/$(DEPDIR) ldap/servers/plugins/bitwise/$(DEPDIR) ldap/servers/plugins/chainingdb/$(DEPDIR) ldap/servers/plugins/collation/$(DEPDIR) ldap/servers/plugins/cos/$(DEPDIR) ldap/servers/plugins/deref/$(DEPDIR) ldap/servers/plugins/distrib/$(DEPDIR) ldap/servers/plugins/dna/$(DEPDIR) ldap/servers/plugins/http/$(DEPDIR) ldap/servers/plugins/linkedattrs/$(DEPDIR) ldap/servers/plugins/memberof/$(DEPDIR) ldap/servers/plugins/mep/$(DEPDIR) ldap/servers/plugins/pam_passthru/$(DEPDIR) ldap/servers/plugins/passthru/$(DEPDIR) ldap/servers/plugins/posix-winsync/$(DEPDIR) ldap/servers/plugins/presence/$(DEPDIR) ldap/servers/plugins/pwdstorage/$(DEPDIR) ldap/servers/plugins/referint/$(DEPDIR) ldap/servers/plugins/replication/$(DEPDIR) ldap/servers/plugins/retrocl/$(DEPDIR) ldap/servers/plugins/rever/$ (DEPDIR) ldap/servers/plugins/roles/$(DEPDIR) ldap/servers/plugins/rootdn_access/$(DEPDIR) ldap/servers/plugins/schema_reload/$(DEPDIR) ldap/servers/plugins/statechange/$(DEPDIR) ldap/servers/plugins/sync/$(DEPDIR) ldap/servers/plugins/syntaxes/$(DEPDIR) ldap/servers/plugins/uiduniq/$(DEPDIR) ldap/servers/plugins/usn/$(DEPDIR) ldap/servers/plugins/views/$(DEPDIR) ldap/servers/plugins/whoami/$(DEPDIR) ldap/servers/slapd/$(DEPDIR) ldap/servers/slapd/back-ldbm/$(DEPDIR) ldap/servers/slapd/tools/$(DEPDIR) ldap/servers/slapd/tools/ldclt/$(DEPDIR) ldap/servers/slapd/tools/rsearch/$(DEPDIR) ldap/servers/snmp/$(DEPDIR) ldap/systools/$(DEPDIR) lib/base/$(DEPDIR) lib/ldaputil/$(DEPDIR) lib/libaccess/$(DEPDIR) lib/libadmin/$(DEPDIR) lib/libsi18n/$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic
diff --git a/ldap/admin/src/scripts/50contentsync.ldif b/ldap/admin/src/scripts/50contentsync.ldif new file mode 100644 index 0000000..0f15bdb --- /dev/null +++ b/ldap/admin/src/scripts/50contentsync.ldif @@ -0,0 +1,22 @@ +dn: cn=Content Synchronization,cn=plugins,cn=config +objectclass: top +objectclass: nsSlapdPlugin +cn: Content Synchronization +nsslapd-pluginpath: libcontentsync-plugin +nsslapd-plugininitfunc: sync_init +nsslapd-plugintype: object +nsslapd-pluginenabled: off +nsslapd-plugin-depends-on-named: Retro Changelog Plugin +# these will be replaced when the server loads the plugin +nsslapd-pluginId: ID +nsslapd-pluginVersion: PACKAGE_VERSION +nsslapd-pluginVendor: VENDOR +nsslapd-pluginDescription: DESC + +dn: oid=1.3.6.1.4.1.4203.1.9.1.1,cn=features,cn=config +objectClass: top +objectClass: directoryServerFeature +oid: 1.3.6.1.4.1.4203.1.9.1.1 +cn: Sync Request Control +aci: (targetattr != "aci")(version 3.0; acl "Sync Request Control"; allow( read + , search ) userdn = "ldap:///all";) diff --git a/ldap/ldif/template-dse.ldif.in b/ldap/ldif/template-dse.ldif.in index 189c4aa..084aacb 100644 --- a/ldap/ldif/template-dse.ldif.in +++ b/ldap/ldif/template-dse.ldif.in @@ -70,6 +70,14 @@ objectClass: directoryServerFeature oid: 1.3.6.1.4.1.42.2.27.9.5.8 cn: Account Usable Request Control
+dn: oid=1.3.6.1.4.1.4203.1.9.1.1,cn=features,cn=config +objectClass: top +objectClass: directoryServerFeature +oid: 1.3.6.1.4.1.4203.1.9.1.1 +cn: Sync Request Control +aci: (targetattr != "aci")(version 3.0; acl "Sync Request Control"; allow( read + , search ) userdn = "ldap:///all";) + dn: cn=plugins,cn=config objectclass: top objectclass: nsContainer @@ -656,6 +664,19 @@ nsslapd-plugintype: betxnpreoperation nsslapd-pluginenabled: on nsslapd-plugin-depends-on-type: database
+dn: cn=Content Synchronization,cn=plugins,cn=config +objectclass: top +objectclass: nsSlapdPlugin +objectclass: extensibleObject +cn: Content Synchronization +nsslapd-pluginpath: libcontentsync-plugin +nsslapd-plugininitfunc: sync_init +nsslapd-plugintype: object +nsslapd-pluginenabled: off +nsslapd-pluginbetxn: on +nsslapd-plugin-depends-on-type: database +nsslapd-plugin-depends-on-named: Retro Changelog Plugin + dn: cn=deref,cn=plugins,cn=config objectclass: top objectclass: nsSlapdPlugin diff --git a/ldap/servers/plugins/sync/sync.h b/ldap/servers/plugins/sync/sync.h new file mode 100644 index 0000000..cf73ce9 --- /dev/null +++ b/ldap/servers/plugins/sync/sync.h @@ -0,0 +1,196 @@ +/** BEGIN COPYRIGHT BLOCK + * This Program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; version 2 of the License. + * + * This Program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA. + * + * In addition, as a special exception, Red Hat, Inc. gives You the additional + * right to link the code of this Program with code not covered under the GNU + * General Public License ("Non-GPL Code") and to distribute linked combinations + * including the two, subject to the limitations in this paragraph. Non-GPL Code + * permitted under this exception must only link to the code of this Program + * through those well defined interfaces identified in the file named EXCEPTION + * found in the source code files (the "Approved Interfaces"). The files of + * Non-GPL Code may instantiate templates or use macros or inline functions from + * the Approved Interfaces without causing the resulting work to be covered by + * the GNU General Public License. Only Red Hat, Inc. may make changes or + * additions to the list of Approved Interfaces. You must obey the GNU General + * Public License in all respects for all of the Program code and other code used + * in conjunction with the Program except the Non-GPL Code covered by this + * exception. If you modify this file, you may extend this exception to your + * version of the file, but you are not obligated to do so. If you do not wish to + * provide this exception without modification, you must delete this exception + * statement from your version and license this file solely under the GPL without + * exception. + * + * + * Copyright (C) 2013 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +/** + * LDAP content synchronization plug-in + */ + +#include <stdio.h> +#include <string.h> +#include "slapi-plugin.h" + +#define PLUGIN_NAME "content-sync-plugin" + +#define SYNC_PLUGIN_SUBSYSTEM "content-sync-plugin" +#define SYNC_PREOP_DESC "content-sync-preop-subplugin" +#define SYNC_POSTOP_DESC "content-sync-postop-subplugin" + +#define OP_FLAG_SYNC_PERSIST 0x01 + +#define E_SYNC_REFRESH_REQUIRED 0x1000 + +#define CL_ATTR_CHANGENUMBER "changenumber" +#define CL_ATTR_ENTRYDN "targetDn" +#define CL_ATTR_UNIQUEID "targetUniqueId" +#define CL_ATTR_CHGTYPE "changetype" +#define CL_ATTR_NEWSUPERIOR "newsuperior" +#define CL_SRCH_BASE "cn=changelog" + +typedef struct sync_cookie { + char *cookie_client_signature; + char *cookie_server_signature; + int cookie_change_info; +} Sync_Cookie; + +typedef struct sync_update { + char *upd_uuid; + int upd_chgtype; + Slapi_Entry *upd_e; +} Sync_UpdateNode; + +typedef struct sync_callback { + Slapi_PBlock *orig_pb; + int changenr; + int change_start; + int cb_err; + Sync_UpdateNode *cb_updates; +} Sync_CallBackData; + +int sync_register_operation_extension(void); + +int sync_srch_refresh_pre_search(Slapi_PBlock *pb); +int sync_srch_refresh_post_search(Slapi_PBlock *pb); +int sync_srch_refresh_pre_entry(Slapi_PBlock *pb); +int sync_srch_refresh_pre_result(Slapi_PBlock *pb); +int sync_del_persist_post_op(Slapi_PBlock *pb); +int sync_mod_persist_post_op(Slapi_PBlock *pb); +int sync_modrdn_persist_post_op(Slapi_PBlock *pb); +int sync_add_persist_post_op(Slapi_PBlock *pb); + +int sync_parse_control_value( struct berval *psbvp, ber_int_t *mode, int *reload, char **cookie ); +int sync_create_state_control( Slapi_Entry *e, LDAPControl **ctrlp, int type, Sync_Cookie *cookie); +int sync_create_sync_done_control( LDAPControl **ctrlp, int refresh, char *cookie); +int sync_intermediate_msg (Slapi_PBlock *pb, int tag, Sync_Cookie *cookie, char **uuids); +int sync_result_msg (Slapi_PBlock *pb, Sync_Cookie *cookie); +int sync_result_err (Slapi_PBlock *pb, int rc, char *msg); + +Sync_Cookie *sync_cookie_create (Slapi_PBlock *pb); +void sync_cookie_update (Sync_Cookie *cookie, Slapi_Entry *ec); +Sync_Cookie *sync_cookie_parse (char *cookie); +int sync_cookie_isvalid (Sync_Cookie *testcookie, Sync_Cookie *refcookie); +void sync_cookie_free (Sync_Cookie **freecookie); +char * sync_cookie2str(Sync_Cookie *cookie); +int sync_number2int(char *nrstr); +char *sync_nsuniqueid2uuid(const char *nsuniqueid); + +int sync_is_active (Slapi_Entry *e, Slapi_PBlock *pb); +int sync_is_active_scope (const Slapi_DN *dn, Slapi_PBlock *pb); + +int sync_refresh_update_content(Slapi_PBlock *pb, Sync_Cookie *client_cookie, Sync_Cookie *session_cookie); +int sync_refresh_initial_content(Slapi_PBlock *pb, int persist, Sync_Cookie *session_cookie); +int sync_read_entry_from_changelog( Slapi_Entry *cl_entry, void *cb_data); +int sync_send_entry_from_changelog( Slapi_PBlock *pb, int chg_req, char *uniqueid); +void sync_send_deleted_entries (Slapi_PBlock *pb, Sync_UpdateNode *upd, int chg_count, Sync_Cookie *session_cookie); +void sync_send_modified_entries (Slapi_PBlock *pb, Sync_UpdateNode *upd, int chg_count); + +int sync_persist_initialize (int argc, char **argv); +PRThread *sync_persist_add (Slapi_PBlock *pb); +int sync_persist_startup (PRThread *tid, Sync_Cookie *session_cookie); +int sync_persist_terminate_all (); +int sync_persist_terminate (PRThread *tid); + +Slapi_PBlock *sync_pblock_copy(Slapi_PBlock *src); + +/* prototype for functions not in slapi-plugin.h */ +Slapi_ComponentId *plugin_get_default_component_id(); + + +/* + * Structures to handle the persitent phase of + * Content Synchronization Requests + * + * A queue of entries being to be sent by a particular persistent + * sync thread + * + * will be created in post op plugins + */ +typedef struct sync_queue_node { + Slapi_Entry *sync_entry; + LDAPControl *pe_ctrls[2]; /* XXX ?? XXX */ + struct sync_queue_node *sync_next; + int sync_chgtype; +} SyncQueueNode; + +/* + * Information about a single sync search + * + * will be created when a content sync control with + * mode == 3 is decoded + */ +typedef struct sync_request { + Slapi_PBlock *req_pblock; + PRLock *req_lock; + PRThread *req_tid; + char *req_orig_base; + Slapi_Filter *req_filter; + PRInt32 req_complete; + Sync_Cookie *req_cookie; + SyncQueueNode *ps_eq_head; + SyncQueueNode *ps_eq_tail; + int req_active; + struct sync_request *req_next; +} SyncRequest; + +/* + * A list of established persistent synchronization searches. + * + * will be initialized at plugin initialization + */ +#define SYNC_MAX_CONCURRENT 10 +typedef struct sync_request_list { + Slapi_RWLock *sync_req_rwlock; /* R/W lock struct to serialize access */ + SyncRequest *sync_req_head; /* Head of list */ + PRLock *sync_req_cvarlock; /* Lock for cvar */ + PRCondVar *sync_req_cvar; /* ps threads sleep on this */ + int sync_req_max_persist; + int sync_req_cur_persist; +} SyncRequestList; + +#define SYNC_FLAG_ADD_STATE_CTRL 0x01 +#define SYNC_FLAG_ADD_DONE_CTRL 0x02 +#define SYNC_FLAG_NO_RESULT 0x04 +#define SYNC_FLAG_SEND_INTERMEDIATE 0x08 + +typedef struct sync_op_info { + int send_flag; /* hint for preop plugins what to send */ + Sync_Cookie *cookie; /* cookie to add in control */ +} SyncOpInfo; + diff --git a/ldap/servers/plugins/sync/sync_init.c b/ldap/servers/plugins/sync/sync_init.c new file mode 100644 index 0000000..d648c79 --- /dev/null +++ b/ldap/servers/plugins/sync/sync_init.c @@ -0,0 +1,174 @@ +/** BEGIN COPYRIGHT BLOCK + * This Program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; version 2 of the License. + * + * This Program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA. + * + * In addition, as a special exception, Red Hat, Inc. gives You the additional + * right to link the code of this Program with code not covered under the GNU + * General Public License ("Non-GPL Code") and to distribute linked combinations + * including the two, subject to the limitations in this paragraph. Non-GPL Code + * permitted under this exception must only link to the code of this Program + * through those well defined interfaces identified in the file named EXCEPTION + * found in the source code files (the "Approved Interfaces"). The files of + * Non-GPL Code may instantiate templates or use macros or inline functions from + * the Approved Interfaces without causing the resulting work to be covered by + * the GNU General Public License. Only Red Hat, Inc. may make changes or + * additions to the list of Approved Interfaces. You must obey the GNU General + * Public License in all respects for all of the Program code and other code used + * in conjunction with the Program except the Non-GPL Code covered by this + * exception. If you modify this file, you may extend this exception to your + * version of the file, but you are not obligated to do so. If you do not wish to + * provide this exception without modification, you must delete this exception + * statement from your version and license this file solely under the GPL without + * exception. + * + * + * Copyright (C) 2013 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include "sync.h" + +static Slapi_PluginDesc pdesc = { PLUGIN_NAME, VENDOR, DS_PACKAGE_VERSION, "Context Synchronization (RFC4533) plugin" }; + +static int sync_start(Slapi_PBlock * pb); +static int sync_close(Slapi_PBlock * pb); +static int sync_preop_init( Slapi_PBlock *pb ); +static int sync_postop_init( Slapi_PBlock *pb ); + +int sync_init( Slapi_PBlock *pb ) +{ + char *plugin_identity = NULL; + int rc = 0; + + slapi_log_error(SLAPI_LOG_TRACE, SYNC_PLUGIN_SUBSYSTEM, + "--> sync_init\n"); + + /** + * Store the plugin identity for later use. + * Used for internal operations + */ + + slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY, &plugin_identity); + PR_ASSERT(plugin_identity); + + if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, + SLAPI_PLUGIN_VERSION_01) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN, + (void *) sync_start) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN, + (void *) sync_close) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, + (void *) &pdesc) != 0 ) { + slapi_log_error(SLAPI_LOG_FATAL, SYNC_PLUGIN_SUBSYSTEM, + "sync_init: failed to register plugin\n"); + rc = 1; + } + + if (rc == 0) { + char *plugin_type = "preoperation"; + /* the config change checking post op */ + if (slapi_register_plugin( + plugin_type, + 1, /* Enabled */ + "sync_init", /* this function desc */ + sync_preop_init,/* init func for post op */ + SYNC_PREOP_DESC,/* plugin desc */ + NULL, + plugin_identity)) { + slapi_log_error(SLAPI_LOG_FATAL, SYNC_PLUGIN_SUBSYSTEM, + "sync_init: failed to register preop plugin\n"); + rc = 1; + } + } + + if (rc == 0) { + char *plugin_type = "postoperation"; + /* the config change checking post op */ + if (slapi_register_plugin(plugin_type, + 1, /* Enabled */ + "sync_init", /* this function desc */ + sync_postop_init, /* init func for post op */ + SYNC_POSTOP_DESC, /* plugin desc */ + NULL, + plugin_identity )) { + slapi_log_error(SLAPI_LOG_FATAL, SYNC_PLUGIN_SUBSYSTEM, + "sync_init: failed to register postop plugin\n"); + rc = 1; + } + } + + return( rc ); +} + +static int +sync_preop_init( Slapi_PBlock *pb ) +{ + int rc; + rc = slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_SEARCH_FN, (void *) sync_srch_refresh_pre_search); + rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_ENTRY_FN, (void *) sync_srch_refresh_pre_entry); + rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_RESULT_FN, (void *) sync_srch_refresh_pre_result); + rc |= sync_register_operation_extension(); + return(rc); + +} + +static int +sync_postop_init( Slapi_PBlock *pb ) +{ + int rc; + rc = slapi_pblock_set(pb, SLAPI_PLUGIN_POST_ADD_FN, (void *) sync_add_persist_post_op); + rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_POST_DELETE_FN, (void *) sync_del_persist_post_op); + rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODIFY_FN, (void *) sync_mod_persist_post_op); + rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODRDN_FN, (void *) sync_modrdn_persist_post_op); + rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_POST_SEARCH_FN, (void *) sync_srch_refresh_post_search); + return(rc); +} + +/* + sync_start + -------------- + Register the Content Synchronization Control. + Initialize locks and queues for the persitent phase. +*/ +static int +sync_start(Slapi_PBlock * pb) +{ + int argc; + char **argv; + + slapi_register_supported_control( LDAP_CONTROL_SYNC, + SLAPI_OPERATION_SEARCH ); + slapi_log_error(SLAPI_LOG_TRACE, SYNC_PLUGIN_SUBSYSTEM, + "--> sync_start\n"); + + if ( slapi_pblock_get( pb, SLAPI_PLUGIN_ARGC, &argc ) != 0 || + slapi_pblock_get( pb, SLAPI_PLUGIN_ARGV, &argv ) != 0 ) { + slapi_log_error( SLAPI_LOG_FATAL, SYNC_PLUGIN_SUBSYSTEM, + "unable to get arguments\n" ); + return( -1 ); + } + sync_persist_initialize(argc, argv); + + return (0); +} + +/* + sync_close + -------------- + Free locks and queues allocated. +*/ +static int +sync_close(Slapi_PBlock * pb) +{ + sync_persist_terminate_all(); + return (0); +} diff --git a/ldap/servers/plugins/sync/sync_persist.c b/ldap/servers/plugins/sync/sync_persist.c new file mode 100644 index 0000000..d35dde9 --- /dev/null +++ b/ldap/servers/plugins/sync/sync_persist.c @@ -0,0 +1,693 @@ +/** BEGIN COPYRIGHT BLOCK +ent sync_srch_refresh_pre_op(Slapi_PBlock *pb); + * This Program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; version 2 of the License. + * + * This Program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA. + * + * In addition, as a special exception, Red Hat, Inc. gives You the additional + * right to link the code of this Program with code not covered under the GNU + * General Public License ("Non-GPL Code") and to distribute linked combinations + * including the two, subject to the limitations in this paragraph. Non-GPL Code + * permitted under this exception must only link to the code of this Program + * through those well defined interfaces identified in the file named EXCEPTION + * found in the source code files (the "Approved Interfaces"). The files of + * Non-GPL Code may instantiate templates or use macros or inline functions from + * the Approved Interfaces without causing the resulting work to be covered by + * the GNU General Public License. Only Red Hat, Inc. may make changes or + * additions to the list of Approved Interfaces. You must obey the GNU General + * Public License in all respects for all of the Program code and other code used + * in conjunction with the Program except the Non-GPL Code covered by this + * exception. If you modify this file, you may extend this exception to your + * version of the file, but you are not obligated to do so. If you do not wish to + * provide this exception without modification, you must delete this exception + * statement from your version and license this file solely under the GPL without + * exception. + * + * + * Copyright (C) 2013 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include "sync.h" + +/* Main list of established persistent synchronizaton searches */ +static SyncRequestList *sync_request_list = NULL; +/* + * Convenience macros for locking the list of persistent searches + */ +#define SYNC_LOCK_READ() slapi_rwlock_rdlock(sync_request_list->sync_req_rwlock) +#define SYNC_UNLOCK_READ() slapi_rwlock_unlock(sync_request_list->sync_req_rwlock) +#define SYNC_LOCK_WRITE() slapi_rwlock_wrlock(sync_request_list->sync_req_rwlock) +#define SYNC_UNLOCK_WRITE() slapi_rwlock_unlock(sync_request_list->sync_req_rwlock) + +/* + * Convenience macro for checking if the Content Synchronization subsystem has + * been initialized. + */ +#define SYNC_IS_INITIALIZED() (sync_request_list != NULL) + + +static int sync_add_request( SyncRequest *req ); +static void sync_remove_request( SyncRequest *req ); +static SyncRequest *sync_request_alloc(); +void sync_queue_change( Slapi_Entry *e, Slapi_Entry *eprev, ber_int_t chgtype ); +static void sync_send_results( void *arg ); +static void sync_request_wakeup_all(); +static void sync_node_free( SyncQueueNode **node ); + +static int sync_acquire_connection (Slapi_Connection *conn); +static int sync_release_connection (Slapi_PBlock *pb, Slapi_Connection *conn, Slapi_Operation *op, int release); + +int sync_add_persist_post_op(Slapi_PBlock *pb) +{ + Slapi_Entry *e; + + if ( !SYNC_IS_INITIALIZED()) { + return(0); + } + slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &e); + sync_queue_change(e, NULL, LDAP_REQ_ADD); + return( 0 ); +} + +int sync_del_persist_post_op(Slapi_PBlock *pb) +{ + Slapi_Entry *e; + + if ( !SYNC_IS_INITIALIZED()) { + return(0); + } + slapi_pblock_get(pb, SLAPI_ENTRY_PRE_OP, &e); + sync_queue_change(e, NULL, LDAP_REQ_DELETE); + return( 0 ); +} + +int sync_mod_persist_post_op(Slapi_PBlock *pb) +{ + Slapi_Entry *e; + + if ( !SYNC_IS_INITIALIZED()) { + return(0); + } + slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &e); + sync_queue_change(e, NULL, LDAP_REQ_MODIFY); + return( 0 ); +} + +int sync_modrdn_persist_post_op(Slapi_PBlock *pb) +{ + Slapi_Entry *e, *e_prev; + + if ( !SYNC_IS_INITIALIZED()) { + return(0); + } + slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &e); + slapi_pblock_get(pb, SLAPI_ENTRY_PRE_OP, &e_prev); + sync_queue_change(e, e_prev, LDAP_REQ_MODRDN); + return( 0 ); +} + +void +sync_queue_change( Slapi_Entry *e, Slapi_Entry *eprev, ber_int_t chgtype ) +{ + SyncRequest *req = NULL; + SyncQueueNode *node = NULL; + int matched = 0; + int prev_match = 0; + int cur_match = 0; + + if ( !SYNC_IS_INITIALIZED()) { + return; + } + + if ( NULL == e ) { + /* For now, some backends such as the chaining backend do not provide a post-op entry */ + return; + } + + SYNC_LOCK_READ(); + + for ( req = sync_request_list->sync_req_head; NULL != req; req = req->req_next ) { + Slapi_DN *base = NULL; + int scope; + Slapi_Operation *op; + + /* Skip the nodes that have no more active operation + */ + slapi_pblock_get( req->req_pblock, SLAPI_OPERATION, &op ); + if ( op == NULL || slapi_op_abandoned( req->req_pblock ) ) { + continue; + } + + slapi_pblock_get( req->req_pblock, SLAPI_SEARCH_TARGET_SDN, &base ); + slapi_pblock_get( req->req_pblock, SLAPI_SEARCH_SCOPE, &scope ); + if (NULL == base) { + base = slapi_sdn_new_dn_byref(req->req_orig_base); + slapi_pblock_set(req->req_pblock, SLAPI_SEARCH_TARGET_SDN, base); + } + + /* + * See if the entry meets the scope and filter criteria. + * We cannot do the acl check here as this thread + * would then potentially clash with the ps_send_results() + * thread on the aclpb in ps->req_pblock. + * By avoiding the acl check in this thread, and leaving all the acl + * checking to the ps_send_results() thread we avoid + * the req_pblock contention problem. + * The lesson here is "Do not give multiple threads arbitary access + * to the same pblock" this kind of muti-threaded access + * to the same pblock must be done carefully--there is currently no + * generic satisfactory way to do this. + */ + + /* if the change is a modrdn then we need to check if the entry was + * moved into scope, out of scope, or stays in scope + */ + if (chgtype == LDAP_REQ_MODRDN) + prev_match = slapi_sdn_scope_test( slapi_entry_get_sdn_const(eprev), base, scope ) && + ( 0 == slapi_vattr_filter_test( req->req_pblock, eprev, req->req_filter, 0 /* verify_access */ )); + + cur_match = slapi_sdn_scope_test( slapi_entry_get_sdn_const(e), base, scope ) && + ( 0 == slapi_vattr_filter_test( req->req_pblock, e, req->req_filter, 0 /* verify_access */ )); + + if (prev_match || cur_match) { + SyncQueueNode *pOldtail; + + /* The scope and the filter match - enqueue it */ + + matched++; + node = (SyncQueueNode *)slapi_ch_calloc( 1, sizeof( SyncQueueNode )); + node->sync_entry = slapi_entry_dup( e ); + + if ( chgtype == LDAP_REQ_MODRDN) { + if (prev_match && cur_match) + node->sync_chgtype = LDAP_REQ_MODIFY; + else if (prev_match) + node->sync_chgtype = LDAP_REQ_DELETE; + else + node->sync_chgtype = LDAP_REQ_ADD; + } else { + node->sync_chgtype = chgtype; + } + /* Put it on the end of the list for this sync search */ + PR_Lock( req->req_lock ); + pOldtail = req->ps_eq_tail; + req->ps_eq_tail = node; + if ( NULL == req->ps_eq_head ) { + req->ps_eq_head = req->ps_eq_tail; + } + else { + pOldtail->sync_next = req->ps_eq_tail; + } + PR_Unlock( req->req_lock ); + } + } + + SYNC_UNLOCK_READ(); + + /* Were there any matches? */ + if ( matched ) { + /* Notify update threads */ + sync_request_wakeup_all(); + slapi_log_error (SLAPI_LOG_TRACE, SYNC_PLUGIN_SUBSYSTEM, "sync search: enqueued entry " + ""%s" on %d request listeners\n", slapi_entry_get_dn_const(e), matched ); + } else { + slapi_log_error (SLAPI_LOG_TRACE, SYNC_PLUGIN_SUBSYSTEM, "sync search: entry " + ""%s" not enqueued on any request search listeners\n", slapi_entry_get_dn_const(e) ); + } + +} +/* + * Initialize the list structure which contains the list + * of established content sync persistent requests + */ +int +sync_persist_initialize (int argc, char **argv) +{ + if ( !SYNC_IS_INITIALIZED()) { + sync_request_list = (SyncRequestList *) slapi_ch_calloc( 1, sizeof( SyncRequestList )); + if (( sync_request_list->sync_req_rwlock = slapi_new_rwlock()) == NULL ) { + slapi_log_error (SLAPI_LOG_FATAL, SYNC_PLUGIN_SUBSYSTEM, "sync_persist_initialize: cannot initialize lock structure(1). "); + return( -1 ); + } + if (( sync_request_list->sync_req_cvarlock = PR_NewLock()) == NULL ) { + slapi_log_error (SLAPI_LOG_FATAL, SYNC_PLUGIN_SUBSYSTEM, "sync_persist_initialize: cannot initialize lock structure(2). "); + return( -1 ); + } + if (( sync_request_list->sync_req_cvar = PR_NewCondVar( sync_request_list->sync_req_cvarlock )) == NULL ) { + slapi_log_error (SLAPI_LOG_FATAL, SYNC_PLUGIN_SUBSYSTEM, "sync_persist_initialize: cannot initialize condition variable. "); + return( -1 ); + } + sync_request_list->sync_req_head = NULL; + sync_request_list->sync_req_cur_persist = 0; + sync_request_list->sync_req_max_persist = SYNC_MAX_CONCURRENT; + if (argc > 0) { + /* for now the only plugin arg is the max concurrent + * persistent sync searches + */ + sync_request_list->sync_req_max_persist = sync_number2int(argv[0]); + if (sync_request_list->sync_req_max_persist == -1) { + sync_request_list->sync_req_max_persist = SYNC_MAX_CONCURRENT; + } + } + } + return (0); +} +/* + * Add the given pblock to the list of established sync searches. + * Then, start a thread to send the results to the client as they + * are dispatched by add, modify, and modrdn operations. + */ +PRThread * +sync_persist_add (Slapi_PBlock *pb) +{ + SyncRequest *req = NULL; + char *base; + Slapi_Filter *filter; + + if ( SYNC_IS_INITIALIZED() && NULL != pb ) { + /* Create the new node */ + req = sync_request_alloc(); + req->req_pblock = sync_pblock_copy(pb); + slapi_pblock_get(pb, SLAPI_ORIGINAL_TARGET_DN, &base); + req->req_orig_base = slapi_ch_strdup(base); + slapi_pblock_get( pb, SLAPI_SEARCH_FILTER, &filter ); + req->req_filter = slapi_filter_dup(filter); + + /* Add it to the head of the list of persistent searches */ + if ( 0 == sync_add_request( req )) { + + /* Start a thread to send the results */ + req->req_tid = PR_CreateThread( PR_USER_THREAD, sync_send_results, + (void *) req, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, + PR_UNJOINABLE_THREAD, SLAPD_DEFAULT_THREAD_STACKSIZE ); + + /* Checking if the thread is succesfully created and + * if the thread is not created succesfully.... we send + * error messages to the Log file + */ + if(NULL == (req->req_tid)){ + int prerr; + prerr = PR_GetError(); + slapi_log_error(SLAPI_LOG_FATAL, "Content Synchronization Search", + "sync_persist_add function: failed to create persitent thread, error %d (%s)\n", + prerr, slapi_pr_strerror(prerr)); + /* Now remove the ps from the list so call the function ps_remove */ + sync_remove_request(req); + PR_DestroyLock ( req->req_lock ); + req->req_lock = NULL; + slapi_ch_free((void **) &req->req_pblock ); + slapi_ch_free((void **) &req ); + } else { + return( req->req_tid); + } + } + } + return( NULL); +} + +int +sync_persist_startup (PRThread *tid, Sync_Cookie *cookie) +{ + SyncRequest *cur; + int rc = 1; + + if ( SYNC_IS_INITIALIZED() && NULL != tid ) { + SYNC_LOCK_READ(); + /* Find and change */ + cur = sync_request_list->sync_req_head; + while ( NULL != cur ) { + if ( cur->req_tid == tid ) { + cur->req_active = PR_TRUE; + cur->req_cookie = cookie; + rc = 0; + break; + } + cur = cur->req_next; + } + SYNC_UNLOCK_READ(); + } + return (rc); +} + + +int +sync_persist_terminate (PRThread *tid) +{ + SyncRequest *cur; + int rc = 1; + + if ( SYNC_IS_INITIALIZED() && NULL != tid ) { + SYNC_LOCK_READ(); + /* Find and change */ + cur = sync_request_list->sync_req_head; + while ( NULL != cur ) { + if ( cur->req_tid == tid ) { + cur->req_active = PR_TRUE; + rc = 0; + break; + } + cur = cur->req_next; + } + SYNC_UNLOCK_READ(); + } + if (rc == 0) { + sync_remove_request(cur); + } + return(rc); +} +int +sync_persist_terminate_all () +{ + SyncRequest *cur; + + if ( SYNC_IS_INITIALIZED() ) { + SYNC_LOCK_READ(); + cur = sync_request_list->sync_req_head; + while ( NULL != cur ) { + cur->req_complete = PR_TRUE; + cur = cur->req_next; + } + SYNC_UNLOCK_READ(); + } + + return (0); +} + +/* + * Allocate and initialize an empty Sync node. + */ +static SyncRequest * +sync_request_alloc() +{ + SyncRequest *req; + + req = (SyncRequest *) slapi_ch_calloc( 1, sizeof( SyncRequest )); + + req->req_pblock = NULL; + if (( req->req_lock = PR_NewLock()) == NULL ) { + slapi_log_error (SLAPI_LOG_FATAL, SYNC_PLUGIN_SUBSYSTEM, "sync_request_alloc: cannot initialize lock structure. "); + slapi_ch_free((void **)&req); + return( NULL ); + } + req->req_tid = (PRThread *) NULL; + req->req_complete = 0; + req->req_cookie = NULL; + req->ps_eq_head = req->ps_eq_tail = (SyncQueueNode *) NULL; + req->req_next = NULL; + req->req_active = PR_FALSE; + return req; +} + + + +/* + * Add the given persistent search to the + * head of the list of persistent searches. + */ +static int +sync_add_request( SyncRequest *req ) +{ + int rc = 0; + if ( SYNC_IS_INITIALIZED() && NULL != req ) { + SYNC_LOCK_WRITE(); + if (sync_request_list->sync_req_cur_persist < sync_request_list->sync_req_max_persist) { + sync_request_list->sync_req_cur_persist++; + req->req_next = sync_request_list->sync_req_head; + sync_request_list->sync_req_head = req; + } else { + rc = 1; + } + SYNC_UNLOCK_WRITE(); + } + return(rc); +} + +static void +sync_remove_request( SyncRequest *req ) +{ + SyncRequest *cur; + int removed = 0; + + if ( SYNC_IS_INITIALIZED() && NULL != req ) { + SYNC_LOCK_WRITE(); + if ( NULL == sync_request_list->sync_req_head ) { + /* should not happen, attempt to remove a request never added */ + } else if ( req == sync_request_list->sync_req_head ) { + /* Remove from head */ + sync_request_list->sync_req_head = sync_request_list->sync_req_head->req_next; + removed = 1; + } else { + /* Find and remove from list */ + cur = sync_request_list->sync_req_head; + while ( NULL != cur->req_next ) { + if ( cur->req_next == req ) { + cur->req_next = cur->req_next->req_next; + removed = 1; + break; + } else { + cur = cur->req_next; + } + } + } + if (removed) { + sync_request_list->sync_req_cur_persist--; + } + SYNC_UNLOCK_WRITE(); + if (!removed) { + slapi_log_error (SLAPI_LOG_PLUGIN, SYNC_PLUGIN_SUBSYSTEM, "attempt to remove nonexistent req"); + } + } +} + +static void +sync_request_wakeup_all() +{ + if ( SYNC_IS_INITIALIZED()) { + PR_Lock( sync_request_list->sync_req_cvarlock ); + PR_NotifyAllCondVar( sync_request_list->sync_req_cvar ); + PR_Unlock( sync_request_list->sync_req_cvarlock ); + } +} +static int +sync_acquire_connection (Slapi_Connection *conn) +{ + int rc; + /* need to acquire a reference to this connection so that it will not + be released or cleaned up out from under us + + in psearch.c it is implemented as: + PR_Lock( ps->req_pblock->pb_conn->c_mutex ); + conn_acq_flag = connection_acquire_nolock(ps->req_pblock->pb_conn); + PR_Unlock( ps->req_pblock->pb_conn->c_mutex ); + + + HOW TO DO FROM A PLUGIN + - either expose the functions from the connection code in the private api + and allow to link them in + - or fake a connection structure + struct fake_conn { + void *needed1 + void *needed2 + void *pad1 + void *pad2 + void *needed3; + } + struct fake_conn *c = (struct fake_conn *) conn; + c->needed3 ++; + this would require knowledge or analysis of the connection structure, + could probably be done for servers with a common history + */ + /* use exposed slapi_connection functions */ + rc = slapi_connection_acquire(conn); + return (rc); +} + +static int +sync_release_connection (Slapi_PBlock *pb, Slapi_Connection *conn, Slapi_Operation *op, int release) +{ + /* see comments in sync_acquire_connection */ + + /* using exposed connection handling functions */ + + slapi_connection_remove_operation(pb, conn, op, release); + + return(0); +} +/* + * Thread routine for sending search results to a client + * which is persistently waiting for them. + * + * This routine will terminate when either (a) the ps_complete + * flag is set, or (b) the associated operation is abandoned. + * In any case, the thread won't notice until it wakes from + * sleeping on the ps_list condition variable, so it needs + * to be awakened. + */ +static void +sync_send_results( void *arg ) +{ + SyncRequest *req = (SyncRequest *)arg; + SyncQueueNode *qnode, *qnodenext; + int conn_acq_flag = 0; + Slapi_Connection *conn = NULL; + Slapi_Operation *op = NULL; + int rc; + PRUint64 connid; + int opid; + + slapi_pblock_get(req->req_pblock, SLAPI_CONN_ID, &connid); + slapi_pblock_get(req->req_pblock, SLAPI_OPERATION_ID, &opid); + slapi_pblock_get(req->req_pblock, SLAPI_CONNECTION, &conn); + slapi_pblock_get(req->req_pblock, SLAPI_OPERATION, &op); + + conn_acq_flag = sync_acquire_connection (conn); + if (conn_acq_flag) { + slapi_log_error(SLAPI_LOG_FATAL, "Content Synchronization Search", + "conn=%" NSPRIu64 " op=%d Could not acquire the connection - aborted\n", + (long long unsigned int)connid, opid); + } + + PR_Lock( sync_request_list->sync_req_cvarlock ); + + while ( (conn_acq_flag == 0) && !req->req_complete ) { + /* Check for an abandoned operation */ + Slapi_Operation *op; + slapi_pblock_get(req->req_pblock, SLAPI_OPERATION, &op); + if ( op == NULL || slapi_op_abandoned( req->req_pblock ) ) { + slapi_log_error(SLAPI_LOG_PLUGIN, "Content Synchronization Search", + "conn=%" NSPRIu64 " op=%d Operation no longer active - terminating\n", + (long long unsigned int)connid, opid); + break; + } + if ( NULL == req->ps_eq_head || !req->req_active) { + /* Nothing to do yet, or the refresh phase is not yet completed */ + /* If an operation is abandoned, we do not get notified by the + * connection code. Wake up every second to check if thread + * should terminate. + */ + PR_WaitCondVar( sync_request_list->sync_req_cvar, PR_SecondsToInterval(1) ); + } else { + /* dequeue the item */ + int attrsonly; + char **attrs; + char **noattrs = NULL; + LDAPControl **ectrls = NULL; + Slapi_Entry *ec; + int chg_type; + + /* deque one element */ + PR_Lock( req->req_lock ); + qnode = req->ps_eq_head; + req->ps_eq_head = qnode->sync_next; + if ( NULL == req->ps_eq_head ) { + req->ps_eq_tail = NULL; + } + PR_Unlock( req->req_lock ); + + /* Get all the information we need to send the result */ + ec = qnode->sync_entry; + slapi_pblock_get( req->req_pblock, SLAPI_SEARCH_ATTRS, &attrs ); + slapi_pblock_get( req->req_pblock, SLAPI_SEARCH_ATTRSONLY, &attrsonly ); + + /* + * Send the result. Since send_ldap_search_entry can block for + * up to 30 minutes, we relinquish all locks before calling it. + */ + PR_Unlock(sync_request_list->sync_req_cvarlock); + + /* + * The entry is in the right scope and matches the filter + * but we need to redo the filter test here to check access + * controls. See the comments at the slapi_filter_test() + * call in sync_persist_add(). + */ + + if ( slapi_vattr_filter_test( req->req_pblock, ec, req->req_filter, + 1 /* verify_access */ ) == 0 ) { + slapi_pblock_set( req->req_pblock, SLAPI_SEARCH_RESULT_ENTRY, ec ); + + /* NEED TO BUILD THE CONTROL */ + switch (qnode->sync_chgtype){ + case LDAP_REQ_ADD: + chg_type = LDAP_SYNC_ADD; + break; + case LDAP_REQ_MODIFY: + chg_type = LDAP_SYNC_MODIFY; + break; + case LDAP_REQ_MODRDN: + chg_type = LDAP_SYNC_MODIFY; + break; + case LDAP_REQ_DELETE: + chg_type = LDAP_SYNC_DELETE; + noattrs = (char **)slapi_ch_calloc(2, sizeof (char *)); + noattrs[0] = slapi_ch_strdup("1.1"); + noattrs[1] = NULL; + break; + } + ectrls = (LDAPControl **)slapi_ch_calloc(2, sizeof (LDAPControl *)); + if (req->req_cookie) + sync_cookie_update(req->req_cookie, ec); + sync_create_state_control(ec, &ectrls[0], chg_type, req->req_cookie); + rc = slapi_send_ldap_search_entry( req->req_pblock, + ec, ectrls, + noattrs?noattrs:attrs, attrsonly ); + if (rc) { + slapi_log_error(SLAPI_LOG_CONNS, SYNC_PLUGIN_SUBSYSTEM, + "Error %d sending entry %s\n", + rc, slapi_entry_get_dn_const(ec)); + } + ldap_controls_free(ectrls); + slapi_ch_array_free(noattrs); + + } + PR_Lock(sync_request_list->sync_req_cvarlock); + + /* Deallocate our wrapper for this entry */ + sync_node_free( &qnode ); + } + } + PR_Unlock( sync_request_list->sync_req_cvarlock ); + sync_remove_request( req ); + + /* indicate the end of search */ + + sync_release_connection(req->req_pblock, conn, op, conn_acq_flag == 0); + + PR_DestroyLock ( req->req_lock ); + req->req_lock = NULL; + + slapi_ch_free((void **) &req->req_pblock ); + slapi_ch_free((void **) &req->req_orig_base ); + slapi_filter_free(req->req_filter, 1); + sync_cookie_free(&req->req_cookie); + for ( qnode = req->ps_eq_head; qnode; qnode = qnodenext) { + qnodenext = qnode->sync_next; + sync_node_free( &qnode ); + } + slapi_ch_free((void **) &req ); +} + + +/* + * Free a sync update node (and everything it holds). + */ +static void +sync_node_free( SyncQueueNode **node ) +{ + if ( node != NULL && *node != NULL ) { + if ( (*node)->sync_entry != NULL ) { + slapi_entry_free( (*node)->sync_entry ); + (*node)->sync_entry = NULL; + } + slapi_ch_free( (void **)node ); + } +} diff --git a/ldap/servers/plugins/sync/sync_refresh.c b/ldap/servers/plugins/sync/sync_refresh.c new file mode 100644 index 0000000..1487289 --- /dev/null +++ b/ldap/servers/plugins/sync/sync_refresh.c @@ -0,0 +1,732 @@ +/** BEGIN COPYRIGHT BLOCK + * This Program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; version 2 of the License. + * + * This Program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA. + * + * In addition, as a special exception, Red Hat, Inc. gives You the additional + * right to link the code of this Program with code not covered under the GNU + * General Public License ("Non-GPL Code") and to distribute linked combinations + * including the two, subject to the limitations in this paragraph. Non-GPL Code + * permitted under this exception must only link to the code of this Program + * through those well defined interfaces identified in the file named EXCEPTION + * found in the source code files (the "Approved Interfaces"). The files of + * Non-GPL Code may instantiate templates or use macros or inline functions from + * the Approved Interfaces without causing the resulting work to be covered by + * the GNU General Public License. Only Red Hat, Inc. may make changes or + * additions to the list of Approved Interfaces. You must obey the GNU General + * Public License in all respects for all of the Program code and other code used + * in conjunction with the Program except the Non-GPL Code covered by this + * exception. If you modify this file, you may extend this exception to your + * version of the file, but you are not obligated to do so. If you do not wish to + * provide this exception without modification, you must delete this exception + * statement from your version and license this file solely under the GPL without + * exception. + * + * + * Copyright (C) 2013 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include "sync.h" + +static SyncOpInfo *new_SyncOpInfo(int flag, Sync_Cookie *cookie); + +static int sync_extension_type; +static int sync_extension_handle; + +static SyncOpInfo *sync_get_operation_extension(Slapi_PBlock *pb); +static void sync_set_operation_extension(Slapi_PBlock *pb, SyncOpInfo *spec); +static int sync_find_ref_by_uuid(Sync_UpdateNode *updates, int stop, char *uniqueid); +static void sync_free_update_nodes (Sync_UpdateNode **updates, int count); +Slapi_Entry *sync_deleted_entry_from_changelog(Slapi_Entry *cl_entry); +static int sync_feature_allowed (Slapi_PBlock *pb); + +static int sync_feature_allowed (Slapi_PBlock *pb) +{ + int isroot = 0; + int ldapcode = LDAP_SUCCESS; + + slapi_pblock_get(pb, SLAPI_REQUESTOR_ISROOT, &isroot); + if ( !isroot) { + char *dn; + Slapi_Entry *feature = NULL; + + /* Fetch the feature entry and see if the requestor is allowed access. */ + dn = slapi_ch_smprintf("dn: oid=%s,cn=features,cn=config", LDAP_CONTROL_SYNC); + if ((feature = slapi_str2entry(dn,0)) != NULL) { + char *dummy_attr = "1.1"; + + ldapcode = slapi_access_allowed(pb, feature, dummy_attr, NULL, SLAPI_ACL_READ); + } + + /* If the feature entry does not exist, deny use of the control. Only + * the root DN will be allowed to use the control in this case. */ + if ((feature == NULL) || (ldapcode != LDAP_SUCCESS)) { + ldapcode = LDAP_INSUFFICIENT_ACCESS; + } + slapi_ch_free((void **)&dn); + slapi_entry_free(feature); + } + return(ldapcode); +} + +int sync_srch_refresh_pre_search(Slapi_PBlock *pb) +{ + + LDAPControl **requestcontrols; + struct berval *psbvp; + Sync_Cookie *client_cookie = NULL; + Sync_Cookie *session_cookie = NULL; + int rc = 0; + int sync_persist = 0; + PRThread *tid = NULL; + int entries_sent = 0; + + slapi_pblock_get (pb, SLAPI_REQCONTROLS, &requestcontrols); + if ( slapi_control_present( requestcontrols, LDAP_CONTROL_SYNC, &psbvp, NULL )){ + char *cookie = NULL; + int mode = 1; + int refresh = 0; + + if ( sync_parse_control_value( psbvp, &mode, + &refresh, &cookie ) != LDAP_SUCCESS ) + { + rc = 1; + goto error_return; + } else { + /* control is valid, check if usere is allowed to perform sync searches */ + rc = sync_feature_allowed(pb); + if (rc) { + sync_result_err(pb,rc,NULL); + goto error_return; + } + } + + if ( mode == 1 || mode == 3 ) + { + + /* we need to return a cookie in the result message + * indicating a state to be used in future sessions + * as starting point - create it now + */ + session_cookie = sync_cookie_create(pb); + /* + * if mode is persist we need to setup the persit handler + * to catch the mods while the refresh is done + */ + if ( mode == 3 ) + { + tid = sync_persist_add(pb); + if ( tid ) + sync_persist = 1; + else { + rc = LDAP_UNWILLING_TO_PERFORM; + sync_result_err(pb,rc,"Too many active synchronization sessions"); + goto error_return; + } + } + /* + * now handl the refresh request + * there are two scenarios + * 1. no cookie is provided this means send all entries matching the search request + * 2. a cookie is provided: send all entries changed since the cookie was issued + * -- return an error if the cookie is invalid + * -- return e-syncRefreshRequired if the data referenced in the cookie are no + * longer in the history + */ + if (cookie && + ( client_cookie = sync_cookie_parse (cookie))) { + if (sync_cookie_isvalid(client_cookie, session_cookie)) { + rc = sync_refresh_update_content(pb, client_cookie, session_cookie); + if (rc == 0) + entries_sent = 1; + if (sync_persist) + rc = sync_intermediate_msg(pb, LDAP_TAG_SYNC_REFRESH_DELETE, session_cookie, NULL); + else + rc = sync_result_msg(pb, session_cookie); + } else { + rc = E_SYNC_REFRESH_REQUIRED; + sync_result_err(pb,rc, "Invalid session cookie"); + } + } else { + rc = sync_refresh_initial_content (pb, sync_persist, session_cookie); + if (rc == 0 && !sync_persist) + /* maintained in postop code */ + session_cookie = NULL; + /* if persis it will be handed over to persist code */ + } + + if ( rc ) { + if (sync_persist) + sync_persist_terminate (tid); + goto error_return; + } else if (sync_persist){ + Slapi_Operation *operation; + + slapi_pblock_get(pb, SLAPI_OPERATION, &operation); + rc = sync_persist_startup(tid, session_cookie); + + if (rc == 0) { + session_cookie = NULL; /* maintained in persist code */ + slapi_operation_set_flag(operation, OP_FLAG_SYNC_PERSIST); + } + } + + + } else { + /* unknown mode, return an error */ + rc = 1; + } +error_return: + sync_cookie_free(&client_cookie); + sync_cookie_free(&session_cookie); + slapi_ch_free((void **)&cookie); + } + + /* if we sent the entries + * return "error" to abort normal search + */ + if ( entries_sent > 0 ) { + return(1); + } else { + return(rc); + } +} + +int sync_srch_refresh_post_search(Slapi_PBlock *pb) +{ + int rc = 0; + SyncOpInfo *info = sync_get_operation_extension(pb); + + if (!info) { + return (0); /* nothing to do */ + } + if (info->send_flag & SYNC_FLAG_SEND_INTERMEDIATE) { + rc = sync_intermediate_msg(pb, LDAP_TAG_SYNC_REFRESH_DELETE, info->cookie, NULL); + /* the refresh phase is over, now the post op + * plugins will create the state control + * depending on the operation type, reset flag + */ + info->send_flag &= ~SYNC_FLAG_ADD_STATE_CTRL; + } + if (info->send_flag & SYNC_FLAG_ADD_DONE_CTRL) { + LDAPControl **ctrl = (LDAPControl **)slapi_ch_calloc(2, sizeof (LDAPControl *)); + char *cookiestr = sync_cookie2str(info->cookie); + sync_create_sync_done_control( &ctrl[0], 0, cookiestr); + slapi_pblock_set(pb, SLAPI_RESCONTROLS, ctrl); + slapi_ch_free((void **)&cookiestr); + } + return(rc); +} + +int sync_srch_refresh_pre_entry(Slapi_PBlock *pb) +{ + int rc = 0; + SyncOpInfo *info = sync_get_operation_extension(pb); + + if (!info) { + rc = 0; /* nothing to do */ + } else if (info->send_flag & SYNC_FLAG_ADD_STATE_CTRL) { + Slapi_Entry *e; + slapi_pblock_get(pb, SLAPI_SEARCH_RESULT_ENTRY, &e); + LDAPControl **ctrl = (LDAPControl **)slapi_ch_calloc(2, sizeof (LDAPControl *)); + sync_create_state_control(e, &ctrl[0], LDAP_SYNC_ADD, NULL); + slapi_pblock_set(pb, SLAPI_SEARCH_CTRLS, ctrl); + } + return(rc); +} + +int sync_srch_refresh_pre_result(Slapi_PBlock *pb) +{ + SyncOpInfo *info = sync_get_operation_extension(pb); + + if (!info) { + return 0; /* nothing to do */ + } + if (info->send_flag & SYNC_FLAG_NO_RESULT ) { + return(1); + } else { + return(0); + } +} + +static void +sync_free_update_nodes (Sync_UpdateNode **updates, int count) +{ + int i; + + for (i=0; i<count;i++) { + if ((*updates)[i].upd_uuid) + slapi_ch_free((void **)&((*updates)[i].upd_uuid)); + if ((*updates)[i].upd_e) + slapi_entry_free((*updates)[i].upd_e); + } + slapi_ch_free((void **)updates); +} + +int +sync_refresh_update_content(Slapi_PBlock *pb, Sync_Cookie *client_cookie, Sync_Cookie *server_cookie) +{ + Slapi_PBlock *seq_pb; + char *filter; + Sync_CallBackData cb_data; + int rc; + int chg_count = server_cookie->cookie_change_info - + client_cookie->cookie_change_info + 1; + + cb_data.cb_updates = (Sync_UpdateNode *)slapi_ch_calloc(chg_count, sizeof(Sync_UpdateNode)); + + seq_pb = slapi_pblock_new(); + slapi_pblock_init(seq_pb); + + cb_data.orig_pb = pb; + cb_data.change_start = client_cookie->cookie_change_info; + + filter = slapi_ch_smprintf("(&(changenumber>=%d)(changenumber<=%d))", + client_cookie->cookie_change_info, + server_cookie->cookie_change_info); + slapi_search_internal_set_pb( + seq_pb, + CL_SRCH_BASE, + LDAP_SCOPE_ONE, + filter, + NULL, + 0, + NULL, NULL, + plugin_get_default_component_id(), + 0); + + rc = slapi_search_internal_callback_pb ( + seq_pb, &cb_data, NULL, sync_read_entry_from_changelog, NULL); + slapi_pblock_destroy(seq_pb); + + /* Now send the deleted entries in a sync info message + * and the modified entries as single entries + */ + sync_send_deleted_entries(pb, cb_data.cb_updates, chg_count, server_cookie); + sync_send_modified_entries(pb, cb_data.cb_updates, chg_count); + + sync_free_update_nodes(&cb_data.cb_updates, chg_count); + slapi_ch_free((void **)&filter); + return (rc); +} + +int +sync_refresh_initial_content(Slapi_PBlock *pb, int sync_persist, Sync_Cookie *sc) +{ + /* the entries will be sent in the normal search process, but + * - a control has to be sent with each entry + * if sync persist: + * - an intermediate response has to be sent + * - no result message must be sent + * + * else + * - a result message with a sync done control has to be sent + * + * setup on operation extension to take care of in + * pre_entry, pre_result and post_search plugins + */ + SyncOpInfo *info; + + if (sync_persist) { + info = new_SyncOpInfo + (SYNC_FLAG_ADD_STATE_CTRL | + SYNC_FLAG_SEND_INTERMEDIATE | + SYNC_FLAG_NO_RESULT, + sc); + } else { + info = new_SyncOpInfo + (SYNC_FLAG_ADD_STATE_CTRL | + SYNC_FLAG_ADD_DONE_CTRL, + sc); + } + sync_set_operation_extension(pb, info); + + return(0); +} + +static int +sync_str2chgreq(char *chgtype) +{ + if (chgtype == NULL) { + return(-1); + } + if (strcasecmp(chgtype,"add") == 0) { + return(LDAP_REQ_ADD); + } else if (strcasecmp(chgtype,"modify") == 0) { + return(LDAP_REQ_MODIFY); + } else if (strcasecmp(chgtype,"modrdn") == 0) { + return(LDAP_REQ_MODRDN); + } else if (strcasecmp(chgtype,"delete") == 0) { + return(LDAP_REQ_DELETE); + } else { + return(-1); + } +} + +static char* +sync_get_attr_value_from_entry( Slapi_Entry *cl_entry, char *attrtype) +{ + Slapi_Value *sval=NULL; + const struct berval *value; + char *strvalue = NULL; + if ( NULL != cl_entry ) { + Slapi_Attr *chattr = NULL; + sval = NULL; + value = NULL; + if ( slapi_entry_attr_find( cl_entry, attrtype, &chattr ) == 0 ) { + slapi_attr_first_value( chattr,&sval ); + if ( NULL != sval ) { + value = slapi_value_get_berval ( sval ); + if( NULL != value && NULL != value->bv_val && + '\0' != value->bv_val[0]) { + strvalue = slapi_ch_strdup( value->bv_val); + } + } + } + } + return (strvalue); +} + +static int +sync_find_ref_by_uuid(Sync_UpdateNode *updates, int stop, char *uniqueid) +{ + int rc = -1; + int i; + for (i=0; i<stop; i++) { + if ( updates[i].upd_uuid && (0 == strcmp(uniqueid, updates[i].upd_uuid))) { + rc = i; + break; + } + } + return (rc); +} + +static int +sync_is_entry_in_scope(Slapi_PBlock *pb, Slapi_Entry *db_entry) +{ + Slapi_Filter *origfilter; + slapi_pblock_get( pb, SLAPI_SEARCH_FILTER, &origfilter ); + if (db_entry && + sync_is_active(db_entry, pb) && + (slapi_vattr_filter_test( pb, db_entry, origfilter, 1) == 0)) { + return(1); + } else { + return(0); + } +} + +Slapi_Entry * +sync_deleted_entry_from_changelog(Slapi_Entry *cl_entry) +{ + Slapi_Entry *db_entry = NULL; + char *entrydn = NULL; + char *uniqueid = NULL; + + entrydn = sync_get_attr_value_from_entry (cl_entry, CL_ATTR_ENTRYDN); + uniqueid = sync_get_attr_value_from_entry (cl_entry, CL_ATTR_UNIQUEID); + + /* when the Retro CL can provide the deleted entry + * the entry will be taken from th RCL. + * For now. just create an entry to holde the nsuniqueid + */ + db_entry = slapi_entry_alloc(); + slapi_entry_init(db_entry, entrydn, NULL); + slapi_entry_add_string(db_entry, "nsuniqueid", uniqueid); + slapi_ch_free((void**)&uniqueid); + + return(db_entry); +} + +int +sync_read_entry_from_changelog( Slapi_Entry *cl_entry, void *cb_data) +{ + char *uniqueid = NULL; + char *chgtype = NULL; + char *chgnr = NULL; + int chg_req; + int prev = 0; + int index = 0; + Sync_CallBackData *cb = (Sync_CallBackData *) cb_data; + + uniqueid = sync_get_attr_value_from_entry (cl_entry, CL_ATTR_UNIQUEID); + chgtype = sync_get_attr_value_from_entry (cl_entry, CL_ATTR_CHGTYPE); + chgnr = sync_get_attr_value_from_entry (cl_entry, CL_ATTR_CHANGENUMBER); + + if (uniqueid == NULL) { + slapi_log_error (SLAPI_LOG_FATAL, SYNC_PLUGIN_SUBSYSTEM, + "Retro Changelog does not provied nsuniquedid." + "Check RCL plugin configuration." ); + } + + + index = sync_number2int(chgnr) - cb->change_start; + chg_req = sync_str2chgreq(chgtype); + switch (chg_req){ + case LDAP_REQ_ADD: + /* nsuniqueid cannot exist, just add reference */ + cb->cb_updates[index].upd_chgtype = LDAP_REQ_ADD; + cb->cb_updates[index].upd_uuid = uniqueid; + break; + case LDAP_REQ_MODIFY: + /* check if we have seen this uuid already */ + prev = sync_find_ref_by_uuid(cb->cb_updates, index, uniqueid); + if (prev == -1) { + cb->cb_updates[index].upd_chgtype = LDAP_REQ_MODIFY; + cb->cb_updates[index].upd_uuid = uniqueid; + } else { + /* was add or mod, keep it */ + cb->cb_updates[index].upd_uuid = 0; + cb->cb_updates[index].upd_chgtype = 0; + slapi_ch_free((void **)&uniqueid); + } + break; + case LDAP_REQ_MODRDN: + { + /* if it is a modrdn, we finally need to decide if this will + * trigger a present or delete state, keep the info that + * the entry was subject to a modrdn + */ + int new_scope = 0; + int old_scope = 0; + Slapi_DN *original_dn; + char *newsuperior = sync_get_attr_value_from_entry (cl_entry, CL_ATTR_NEWSUPERIOR); + char *entrydn = sync_get_attr_value_from_entry (cl_entry, CL_ATTR_ENTRYDN); + /* if newsuperior is set we need to checkif the entry has been moved into + * or moved out of the scope of the synchronization request + */ + original_dn = slapi_sdn_new_dn_byref(entrydn); + old_scope = sync_is_active_scope(original_dn,cb->orig_pb); + slapi_sdn_free(&original_dn); + slapi_ch_free((void **)&entrydn); + if (newsuperior) { + Slapi_DN *newbase; + newbase = slapi_sdn_new_dn_byref(newsuperior); + new_scope = sync_is_active_scope(newbase, cb->orig_pb); + slapi_ch_free((void **)&newsuperior); + slapi_sdn_free(&newbase); + } else { + /* scope didn't change */ + new_scope = old_scope; + } + prev = sync_find_ref_by_uuid(cb->cb_updates, index, uniqueid); + if ( old_scope && new_scope ) { + /* nothing changed, it's just a MOD */ + if (prev == -1) { + cb->cb_updates[index].upd_chgtype = LDAP_REQ_MODIFY; + cb->cb_updates[index].upd_uuid = uniqueid; + } else { + cb->cb_updates[index].upd_uuid = 0; + cb->cb_updates[index].upd_chgtype = 0; + slapi_ch_free((void **)&uniqueid); + } + } else if ( old_scope ) { + /* it was moved out of scope, handle as DEL */ + if (prev == -1) { + cb->cb_updates[index].upd_chgtype = LDAP_REQ_DELETE; + cb->cb_updates[index].upd_uuid = uniqueid; + cb->cb_updates[index].upd_e = sync_deleted_entry_from_changelog(cl_entry); + } else { + cb->cb_updates[prev].upd_chgtype = LDAP_REQ_DELETE; + cb->cb_updates[prev].upd_e = sync_deleted_entry_from_changelog(cl_entry); + slapi_ch_free((void **)&uniqueid); + } + } else if ( new_scope ) { + /* moved into scope, handle as ADD */ + cb->cb_updates[index].upd_chgtype = LDAP_REQ_ADD; + cb->cb_updates[index].upd_uuid = uniqueid; + } else { + /* nothing to do */ + } + slapi_sdn_free(&original_dn); + break; + } + case LDAP_REQ_DELETE: + /* check if we have seen this uuid already */ + prev = sync_find_ref_by_uuid(cb->cb_updates, index, uniqueid); + if (prev == -1) { + cb->cb_updates[index].upd_chgtype = LDAP_REQ_DELETE; + cb->cb_updates[index].upd_uuid = uniqueid; + cb->cb_updates[index].upd_e = sync_deleted_entry_from_changelog(cl_entry); + } else { + /* if it was added since last cookie state, we + * can ignoere it */ + if (cb->cb_updates[prev].upd_chgtype == LDAP_REQ_ADD) { + slapi_ch_free((void **)&(cb->cb_updates[prev].upd_uuid)); + slapi_ch_free((void **)&uniqueid); + cb->cb_updates[prev].upd_uuid = NULL; + cb->cb_updates[index].upd_uuid = NULL; + } else { + /* ignore previous mod */ + cb->cb_updates[index].upd_uuid = NULL; + cb->cb_updates[prev].upd_chgtype = LDAP_REQ_DELETE; + cb->cb_updates[prev].upd_e = sync_deleted_entry_from_changelog(cl_entry); + } + } + break; + } + slapi_ch_free((void **)&chgtype); + slapi_ch_free((void **)&chgnr); + + return (0); +} +#define SYNC_MAX_DELETED_UUID_BATCH 50 + +void +sync_send_deleted_entries(Slapi_PBlock *pb, Sync_UpdateNode *upd, int chg_count, Sync_Cookie *cookie) +{ + char *syncUUIDs[SYNC_MAX_DELETED_UUID_BATCH + 1]; + int uuid_index = 0; + int index, i; + + syncUUIDs[0] = NULL; + for (index=0; index < chg_count; index++) { + if (upd[index].upd_chgtype == LDAP_REQ_DELETE && + upd[index].upd_uuid ) { + if (uuid_index < SYNC_MAX_DELETED_UUID_BATCH) { + syncUUIDs[uuid_index++] = sync_nsuniqueid2uuid(upd[index].upd_uuid); + } else { + /* max number of uuids to be sent in one sync info message */ + syncUUIDs[uuid_index] = NULL; + sync_intermediate_msg (pb, LDAP_TAG_SYNC_ID_SET, cookie, &syncUUIDs[0]); + for (i=0; i<uuid_index;i++) { + slapi_ch_free((void **)&syncUUIDs[i]); + syncUUIDs[i] = NULL; + } + uuid_index = 0; + } + } + } + + if (uuid_index > 0 && syncUUIDs[uuid_index-1]) { + /* more entries to send */ + syncUUIDs[uuid_index] = NULL; + sync_intermediate_msg (pb, LDAP_TAG_SYNC_ID_SET, cookie, &syncUUIDs[0]); + for (i=0; i<uuid_index;i++) { + slapi_ch_free((void **)&syncUUIDs[i]); + syncUUIDs[i] = NULL; + } + } +} + +void +sync_send_modified_entries(Slapi_PBlock *pb, Sync_UpdateNode *upd, int chg_count) +{ + int index; + + for (index=0; index < chg_count; index++) { + if (upd[index].upd_chgtype != LDAP_REQ_DELETE && + upd[index].upd_uuid ) + + sync_send_entry_from_changelog(pb, upd[index].upd_chgtype, upd[index].upd_uuid); + } +} + +int +sync_send_entry_from_changelog(Slapi_PBlock *pb,int chg_req, char *uniqueid) +{ + Slapi_Entry *db_entry = NULL; + int chg_type = LDAP_SYNC_ADD; + int rv; + Slapi_PBlock *search_pb = NULL; + Slapi_Entry **entries = NULL; + char *origbase; + char *filter = slapi_ch_smprintf("(nsuniqueid=%s)",uniqueid); + + slapi_pblock_get( pb, SLAPI_ORIGINAL_TARGET_DN, &origbase ); + search_pb = slapi_pblock_new(); + slapi_search_internal_set_pb(search_pb, origbase, + LDAP_SCOPE_SUBTREE, filter, + NULL, 0, NULL, NULL, plugin_get_default_component_id(), 0); + slapi_search_internal_pb(search_pb); + slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &rv); + if ( rv == LDAP_SUCCESS) { + slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries); + if (entries) + db_entry = *entries; /* there can only be one */ + } + + if (db_entry && sync_is_entry_in_scope(pb, db_entry)) { + LDAPControl **ctrl = (LDAPControl **)slapi_ch_calloc(2, sizeof (LDAPControl *)); + sync_create_state_control(db_entry, &ctrl[0], chg_type, NULL); + slapi_send_ldap_search_entry (pb, db_entry, ctrl, NULL, 0); + ldap_controls_free(ctrl); + } + slapi_free_search_results_internal(search_pb); + slapi_pblock_destroy(search_pb); + slapi_ch_free((void **)&filter); + return (0); +} + +static SyncOpInfo* +new_SyncOpInfo(int flag, Sync_Cookie *cookie) { + SyncOpInfo *spec = (SyncOpInfo *)slapi_ch_calloc(1, sizeof(SyncOpInfo)); + spec->send_flag = flag; + spec->cookie = cookie; + + return spec; +} +/* consumer operation extension constructor */ +static void * +sync_operation_extension_ctor(void *object, void *parent) +{ + /* we only set the extension value explicitly if the + client requested the control - see deref_pre_search */ + return NULL; /* we don't set anything in the ctor */ +} + +/* consumer operation extension destructor */ +static void +sync_delete_SyncOpInfo(SyncOpInfo **info) +{ + if (info && *info) { + sync_cookie_free(&((*info)->cookie)); + slapi_ch_free((void **)info); + } +} + +static void +sync_operation_extension_dtor(void *ext, void *object, void *parent) +{ + SyncOpInfo *spec = (SyncOpInfo *)ext; + sync_delete_SyncOpInfo(&spec); +} + +static SyncOpInfo * +sync_get_operation_extension(Slapi_PBlock *pb) +{ + Slapi_Operation *op; + + slapi_pblock_get(pb, SLAPI_OPERATION, &op); + return (SyncOpInfo *)slapi_get_object_extension(sync_extension_type, + op, sync_extension_handle); +} + +static void +sync_set_operation_extension(Slapi_PBlock *pb, SyncOpInfo *spec) +{ + Slapi_Operation *op; + + slapi_pblock_get(pb, SLAPI_OPERATION, &op); + slapi_set_object_extension(sync_extension_type, op, + sync_extension_handle, (void *)spec); +} + +int +sync_register_operation_extension(void) +{ + return slapi_register_object_extension(SYNC_PLUGIN_SUBSYSTEM, + SLAPI_EXT_OPERATION, + sync_operation_extension_ctor, + sync_operation_extension_dtor, + &sync_extension_type, + &sync_extension_handle); +} diff --git a/ldap/servers/plugins/sync/sync_util.c b/ldap/servers/plugins/sync/sync_util.c new file mode 100644 index 0000000..9e98561 --- /dev/null +++ b/ldap/servers/plugins/sync/sync_util.c @@ -0,0 +1,685 @@ +/** BEGIN COPYRIGHT BLOCK + * This Program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; version 2 of the License. + * + * This Program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA. + * + * In addition, as a special exception, Red Hat, Inc. gives You the additional + * right to link the code of this Program with code not covered under the GNU + * General Public License ("Non-GPL Code") and to distribute linked combinations + * including the two, subject to the limitations in this paragraph. Non-GPL Code + * permitted under this exception must only link to the code of this Program + * through those well defined interfaces identified in the file named EXCEPTION + * found in the source code files (the "Approved Interfaces"). The files of + * Non-GPL Code may instantiate templates or use macros or inline functions from + * the Approved Interfaces without causing the resulting work to be covered by + * the GNU General Public License. Only Red Hat, Inc. may make changes or + * additions to the list of Approved Interfaces. You must obey the GNU General + * Public License in all respects for all of the Program code and other code used + * in conjunction with the Program except the Non-GPL Code covered by this + * exception. If you modify this file, you may extend this exception to your + * version of the file, but you are not obligated to do so. If you do not wish to + * provide this exception without modification, you must delete this exception + * statement from your version and license this file solely under the GPL without + * exception. + * + * + * Copyright (C) 2013 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include "sync.h" + +static struct berval * create_syncinfo_value( int type, const char *cookie, const char **uuids); +static char * sync_cookie_get_server_info(Slapi_PBlock *pb); +static char * sync_cookie_get_client_info(Slapi_PBlock *pb); + +/* + * Parse the value from an LDAPv3 sync request control. They look + * like this: + * + * syncRequestValue ::= SEQUENCE { + * mode ENUMERATED { + * -- 0 unused + * refreshOnly (1), + * -- 2 reserved + * refreshAndPersist (3) + * }, + * cookie syncCookie OPTIONAL, + * reloadHint BOOLEAN DEFAULT FALSE + * } + * + * Return an LDAP error code (LDAP_SUCCESS if all goes well). + * + */ +int +sync_parse_control_value( struct berval *psbvp, ber_int_t *mode, int *reload, char **cookie ) +{ + int rc= LDAP_SUCCESS; + + if ( psbvp->bv_len == 0 || psbvp->bv_val == NULL ) + { + rc= LDAP_PROTOCOL_ERROR; + } + else + { + BerElement *ber= ber_init( psbvp ); + if ( ber == NULL ) + { + rc= LDAP_OPERATIONS_ERROR; + } + else + { + if ( ber_scanf( ber, "{e", mode ) == LBER_ERROR ) + { + rc= LDAP_PROTOCOL_ERROR; + } else if ( ber_scanf( ber, "a", cookie ) != LBER_ERROR ) + ber_scanf( ber, "b}", reload ); + + /* the ber encoding is no longer needed */ + ber_free(ber,1); + } + } + + return( rc ); +} + +char * +sync_nsuniqueid2uuid(const char *nsuniqueid) +{ + char *uuid; + char u[17]; + + u[0] = slapi_str_to_u8(nsuniqueid); + u[1] = slapi_str_to_u8(nsuniqueid+2); + u[2] = slapi_str_to_u8(nsuniqueid+4); + u[3] = slapi_str_to_u8(nsuniqueid+6); + + u[4] = slapi_str_to_u8(nsuniqueid+9); + u[5] = slapi_str_to_u8(nsuniqueid+11); + u[6] = slapi_str_to_u8(nsuniqueid+13); + u[7] = slapi_str_to_u8(nsuniqueid+15); + + u[8] = slapi_str_to_u8(nsuniqueid+18); + u[9] = slapi_str_to_u8(nsuniqueid+20); + u[10] = slapi_str_to_u8(nsuniqueid+22); + u[11] = slapi_str_to_u8(nsuniqueid+24); + + u[12] = slapi_str_to_u8(nsuniqueid+27); + u[13] = slapi_str_to_u8(nsuniqueid+29); + u[14] = slapi_str_to_u8(nsuniqueid+31); + u[15] = slapi_str_to_u8(nsuniqueid+33); + + u[16] = '\0'; + + uuid = slapi_ch_smprintf("%s",(char *)u); + + return(uuid); +} + +/* + * syncStateValue ::= SEQUENCE { + * state ENUMERATED { + * present (0), + * add (1), + * modify (2), + * delete (3) + * }, + * entryUUID syncUUID, + * cookie syncCookie OPTIONAL + * } + * + */ +int +sync_create_state_control( Slapi_Entry *e, LDAPControl **ctrlp, int type, Sync_Cookie *cookie) +{ + int rc; + BerElement *ber; + struct berval *bvp; + char *uuid; + Slapi_Attr *attr; + Slapi_Value *val; + + if ( ctrlp == NULL || ( ber = der_alloc()) == NULL ) { + return( LDAP_OPERATIONS_ERROR ); + } + + *ctrlp = NULL; + + slapi_entry_attr_find(e, SLAPI_ATTR_UNIQUEID, &attr); + slapi_attr_first_value(attr, &val); + uuid = sync_nsuniqueid2uuid(slapi_value_get_string(val)); + if (( rc = ber_printf( ber, "{eo", type , uuid, 16 )) != -1 ) { + if (cookie) { + char *cookiestr = sync_cookie2str(cookie); + rc = ber_printf( ber, "s}", cookiestr); + slapi_ch_free((void **)&cookiestr); + } else { + rc = ber_printf( ber, "}"); + + } + } + if (rc != -1) { + rc = ber_flatten( ber, &bvp ); + } + ber_free( ber, 1 ); + slapi_ch_free((void **)&uuid); + + if ( rc == -1 ) { + return( LDAP_OPERATIONS_ERROR ); + } + + *ctrlp = (LDAPControl *)slapi_ch_malloc( sizeof( LDAPControl )); + (*ctrlp)->ldctl_iscritical = 0; + (*ctrlp)->ldctl_oid = slapi_ch_strdup( LDAP_CONTROL_SYNC_STATE ); + (*ctrlp)->ldctl_value = *bvp; /* struct copy */ + + bvp->bv_val = NULL; + ber_bvfree( bvp ); + + return (LDAP_SUCCESS); +} + +/* + * syncDoneValue ::= SEQUENCE { + * cookie syncCookie OPTIONAL + * refreshDeletes BOOLEAN DEFAULT FALSE + * } + * + */ +int +sync_create_sync_done_control( LDAPControl **ctrlp, int refresh, char *cookie) +{ + int rc; + BerElement *ber; + struct berval *bvp; + + if ( ctrlp == NULL || ( ber = der_alloc()) == NULL ) { + return( LDAP_OPERATIONS_ERROR ); + } + + *ctrlp = NULL; + + if (cookie) { + if (( rc = ber_printf( ber, "{s", cookie)) != -1 ) { + if (refresh) { + rc = ber_printf( ber, "e}", refresh); + } else { + rc = ber_printf( ber, "}"); + } + } + } else { + if (refresh) { + rc = ber_printf( ber, "{e}", refresh); + } else { + rc = ber_printf( ber, "{}"); + } + } + if (rc != -1) { + rc = ber_flatten( ber, &bvp ); + } + ber_free( ber, 1 ); + + if ( rc == -1 ) { + return( LDAP_OPERATIONS_ERROR ); + } + + *ctrlp = (LDAPControl *)slapi_ch_malloc( sizeof( LDAPControl )); + (*ctrlp)->ldctl_iscritical = 0; + (*ctrlp)->ldctl_oid = slapi_ch_strdup( LDAP_CONTROL_SYNC_DONE ); + (*ctrlp)->ldctl_value = *bvp; /* struct copy */ + + bvp->bv_val = NULL; + ber_bvfree( bvp ); + + return (LDAP_SUCCESS); + +} + +char * +sync_cookie2str(Sync_Cookie *cookie) +{ + char *cookiestr = NULL; + + if (cookie) { + cookiestr = slapi_ch_smprintf("%s#%s#%d", + cookie->cookie_server_signature, + cookie->cookie_client_signature, + cookie->cookie_change_info); + } + return(cookiestr); +} + +int +sync_intermediate_msg (Slapi_PBlock *pb, int tag, Sync_Cookie *cookie, char **uuids) +{ + int rc; + struct berval *syncInfo; + LDAPControl *ctrlp = NULL; + char *cookiestr = sync_cookie2str(cookie); + + syncInfo = create_syncinfo_value(tag, cookiestr, (const char **)uuids); + + rc = slapi_send_ldap_intermediate( pb, &ctrlp, LDAP_SYNC_INFO, syncInfo); + slapi_ch_free((void **)&cookiestr); + ber_bvfree(syncInfo); + return (rc); +} + +int sync_result_msg (Slapi_PBlock *pb, Sync_Cookie *cookie) +{ + int rc = 0; + char *cookiestr = sync_cookie2str(cookie); + + LDAPControl **ctrl = (LDAPControl **)slapi_ch_calloc(2, sizeof (LDAPControl *)); + sync_create_sync_done_control( &ctrl[0], 0, cookiestr); + slapi_pblock_set(pb, SLAPI_RESCONTROLS, ctrl); + slapi_send_ldap_result(pb, 0, NULL, NULL, 0, NULL ); + + slapi_ch_free((void **)&cookiestr); + return (rc); +} + +int sync_result_err (Slapi_PBlock *pb, int err, char *msg) +{ + int rc = 0; + + slapi_send_ldap_result(pb, err, NULL, msg, 0, NULL ); + + return (rc); +} + +static struct berval * +create_syncinfo_value( int type, const char *cookie, const char **uuids) +{ + BerElement *ber; + struct berval *bvp = NULL; + + if ( ( ber = der_alloc() ) == NULL ) { + return( NULL ); + } + + switch (type) { + case LDAP_TAG_SYNC_NEW_COOKIE: + ber_printf(ber, "to", type, cookie); + break; + case LDAP_TAG_SYNC_REFRESH_DELETE: + case LDAP_TAG_SYNC_REFRESH_PRESENT: + ber_printf(ber, "t{", type); + if (cookie) + ber_printf(ber, "s", cookie); + /* ber_printf(ber, "b",1); */ + ber_printf(ber, "}"); + break; + case LDAP_TAG_SYNC_ID_SET: + ber_printf(ber, "t{", type); + if (cookie) + ber_printf(ber, "s", cookie); + if (uuids) + ber_printf(ber, "b[v]", 1, uuids); + ber_printf(ber, "}"); + break; + default: + break; + } + ber_flatten( ber, &bvp ); + ber_free( ber, 1 ); + + return( bvp ); +} + +static int +sync_handle_cnum_entry(Slapi_Entry *e, void *cb_data) +{ + int rc = 0; + Sync_CallBackData *cb = (Sync_CallBackData *)cb_data; + Slapi_Value *sval=NULL; + const struct berval *value; + + cb->changenr = 0; + + if ( NULL != e ) { + Slapi_Attr *chattr = NULL; + sval = NULL; + value = NULL; + if ( slapi_entry_attr_find( e, CL_ATTR_CHANGENUMBER, &chattr ) == 0 ) { + slapi_attr_first_value( chattr,&sval ); + if ( NULL != sval ) { + value = slapi_value_get_berval ( sval ); + if( NULL != value && NULL != value->bv_val && + '\0' != value->bv_val[0]) { + cb->changenr = sync_number2int(value->bv_val); + } + } + } + } + return (rc); +} + +/* + * a cookie is used to synchronize client server sessions, + * it consist of three parts + * -- server id, client should only sync with one server + * -- client id, client should use same bind dn, and srch params + * -- change info, kind of state info like csn, ruv, + * in the first implementation use changenumber from retro cl + * + * syntax: <server-id>#<client-id>#change + * + */ +static char * +sync_cookie_get_server_info(Slapi_PBlock *pb) +{ + char *info_enc; + int rc = 0; + Slapi_Entry **entries; + Slapi_PBlock *srch_pb = NULL; + char *host = NULL; + char *port = NULL; + char *server_attrs[] = {"nsslapd-localhost", "nsslapd-port", NULL}; + + srch_pb = slapi_pblock_new (); + slapi_search_internal_set_pb (srch_pb, "cn=config", LDAP_SCOPE_BASE, + "objectclass=*", server_attrs, 0, NULL, NULL, + plugin_get_default_component_id(), 0); + slapi_search_internal_pb (srch_pb); + slapi_pblock_get(srch_pb, SLAPI_PLUGIN_INTOP_RESULT, &rc); + if (rc != 0) + { + slapi_log_error(SLAPI_LOG_FATAL, SYNC_PLUGIN_SUBSYSTEM, "sync_cookie_get_server_info: " + "unable to read server configuration: error %d\n", rc); + } + else + { + slapi_pblock_get(srch_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries); + if (NULL == entries || NULL == entries[0]) + { + slapi_log_error(SLAPI_LOG_FATAL, SYNC_PLUGIN_SUBSYSTEM, "sync_cookie_get_server_info: " + "server configuration missing\n"); + rc = -1; + } + else + { + host = slapi_entry_attr_get_charptr(entries[0], "nsslapd-localhost"); + port = slapi_entry_attr_get_charptr(entries[0], "nsslapd-port"); + } + } + info_enc = slapi_ch_smprintf("%s:%s",host?host:"nohost",port?port:"noport"); + + slapi_ch_free ((void**)&host); + slapi_ch_free ((void**)&port); + slapi_free_search_results_internal(srch_pb); + slapi_pblock_destroy (srch_pb); + return(info_enc); +} + +static char * +sync_cookie_get_client_info(Slapi_PBlock *pb) +{ + char *targetdn; + char *strfilter; + char *clientdn; + char *clientinfo; + + slapi_pblock_get(pb, SLAPI_TARGET_DN, &targetdn); + slapi_pblock_get(pb, SLAPI_SEARCH_STRFILTER, &strfilter); + slapi_pblock_get(pb, SLAPI_REQUESTOR_DN, &clientdn); + clientinfo = slapi_ch_smprintf("%s:%s:%s",clientdn,targetdn,strfilter); + return (clientinfo); +} +static int +sync_cookie_get_change_number(int lastnr, const char *uniqueid) +{ + Slapi_PBlock *srch_pb; + Slapi_Entry **entries; + Slapi_Entry *cl_entry; + int rv; + int newnr = -1; + char *filter = slapi_ch_smprintf("(&(changenumber>=%d)(targetuniqueid=%s))",lastnr,uniqueid); + + srch_pb = slapi_pblock_new(); + slapi_search_internal_set_pb(srch_pb, CL_SRCH_BASE, + LDAP_SCOPE_SUBTREE, filter, + NULL, 0, NULL, NULL, plugin_get_default_component_id(), 0); + slapi_search_internal_pb(srch_pb); + slapi_pblock_get(srch_pb, SLAPI_PLUGIN_INTOP_RESULT, &rv); + if ( rv == LDAP_SUCCESS) { + slapi_pblock_get(srch_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries); + if (entries && *entries) { + Slapi_Attr *attr; + Slapi_Value *val; + cl_entry = *entries; /* only use teh first one */ + slapi_entry_attr_find(cl_entry, CL_ATTR_CHANGENUMBER, &attr); + slapi_attr_first_value(attr, &val); + newnr = sync_number2int((char *)slapi_value_get_string(val)); + } + } + + slapi_free_search_results_internal(srch_pb); + slapi_pblock_destroy(srch_pb); + slapi_ch_free((void **)&filter); + return (newnr); +} + +static int +sync_cookie_get_change_info(Sync_CallBackData *scbd) +{ + Slapi_PBlock *seq_pb; + char *base; + char *attrname; + int rc; + + base = slapi_ch_strdup("cn=changelog"); + attrname = slapi_ch_strdup("changenumber"); + + seq_pb = slapi_pblock_new(); + slapi_pblock_init(seq_pb); + + slapi_seq_internal_set_pb(seq_pb, base, SLAPI_SEQ_LAST, attrname, NULL, NULL, 0, 0, + plugin_get_default_component_id(), 0); + + rc = slapi_seq_internal_callback_pb (seq_pb, scbd, NULL, sync_handle_cnum_entry, NULL); + slapi_pblock_destroy(seq_pb); + + slapi_ch_free((void **)&attrname); + slapi_ch_free((void **)&base); + + return (rc); + +} + +Sync_Cookie * +sync_cookie_create (Slapi_PBlock *pb) +{ + + Sync_CallBackData scbd; + int rc; + Sync_Cookie *sc = (Sync_Cookie *)slapi_ch_malloc(sizeof(Sync_Cookie)); + + + rc = sync_cookie_get_change_info (&scbd); + + if (rc == 0) { + sc->cookie_server_signature = sync_cookie_get_server_info(pb); + sc->cookie_client_signature = sync_cookie_get_client_info(pb); + sc->cookie_change_info = scbd.changenr; + } else { + slapi_ch_free ((void **)&sc); + sc = NULL; + } + + return (sc); +} + +void sync_cookie_update (Sync_Cookie *sc, Slapi_Entry *ec) +{ + const char *uniqueid = NULL; + Slapi_Attr *attr; + Slapi_Value *val; + + slapi_entry_attr_find(ec, SLAPI_ATTR_UNIQUEID, &attr); + slapi_attr_first_value(attr, &val); + uniqueid = slapi_value_get_string(val); + + sc->cookie_change_info = sync_cookie_get_change_number (sc->cookie_change_info, uniqueid); +} + +Sync_Cookie * +sync_cookie_parse (char *cookie) +{ + char *p, *q; + Sync_Cookie *sc; + + if (cookie == NULL || *cookie == '\0' ) { + return NULL; + } + + p = q = cookie; + sc = (Sync_Cookie *)slapi_ch_malloc(sizeof(Sync_Cookie)); + + sc->cookie_client_signature = NULL; + sc->cookie_server_signature = NULL; + sc->cookie_change_info = -1; + p = strchr(q, '#'); + if (p) { + *p = '\0'; + sc->cookie_server_signature = slapi_ch_strdup(q); + q = p + 1; + p = strchr(q, '#'); + if (p) { + *p = '\0'; + sc->cookie_client_signature = slapi_ch_strdup(q); + sc->cookie_change_info = sync_number2int(p+1); + } + } + + return (sc); +} + +int +sync_cookie_isvalid (Sync_Cookie *testcookie, Sync_Cookie *refcookie) +{ + /* client and server info must match */ + if (strcmp(testcookie->cookie_client_signature,refcookie->cookie_client_signature) || + strcmp(testcookie->cookie_server_signature,refcookie->cookie_server_signature) || + testcookie->cookie_change_info == -1 || + testcookie->cookie_change_info > refcookie->cookie_change_info ) + return (0); + /* could add an additional check if the requested state in client cookie is still + * available. Accept any state request for now. + */ + return (1); +} + +void +sync_cookie_free (Sync_Cookie **freecookie) +{ + if (*freecookie) { + slapi_ch_free((void **)&((*freecookie)->cookie_client_signature)); + slapi_ch_free((void **)&((*freecookie)->cookie_server_signature)); + slapi_ch_free((void **)freecookie); + } +} + +int sync_is_active_scope (const Slapi_DN *dn, Slapi_PBlock *pb) +{ + int rc; + char *origbase = NULL; + Slapi_DN *base = NULL; + int scope; + + slapi_pblock_get( pb, SLAPI_ORIGINAL_TARGET_DN, &origbase ); + slapi_pblock_get( pb, SLAPI_SEARCH_TARGET_SDN, &base ); + slapi_pblock_get( pb, SLAPI_SEARCH_SCOPE, &scope ); + if (NULL == base) { + base = slapi_sdn_new_dn_byref(origbase); + slapi_pblock_set(pb, SLAPI_SEARCH_TARGET_SDN, base); + } + if ( slapi_sdn_scope_test(dn, base, scope )) { + rc = 1; + } else { + rc = 0; + } + + return (rc); +} + +int sync_is_active (Slapi_Entry *e, Slapi_PBlock *pb) +{ + if ( pb == NULL ) { + /* not yet initialized */ + return(0); + } else { + /* check id entry is in scope of sync request */ + return(sync_is_active_scope( slapi_entry_get_sdn_const(e),pb)); + } +} + + +Slapi_PBlock * +sync_pblock_copy(Slapi_PBlock *src) +{ + Slapi_Operation *operation; + Slapi_Connection *connection; + int *scope; + int *deref; + int *filter_normalized; + char *fstr; + char **attrs, **attrs_dup; + int *attrsonly; + int *isroot; + int *sizelimit; + int *timelimit; + struct slapdplugin *pi; + + + slapi_pblock_get( src, SLAPI_OPERATION, &operation ); + slapi_pblock_get( src, SLAPI_CONNECTION, &connection ); + slapi_pblock_get( src, SLAPI_SEARCH_SCOPE, &scope ); + slapi_pblock_get( src, SLAPI_SEARCH_DEREF, &deref ); + slapi_pblock_get( src, SLAPI_PLUGIN_SYNTAX_FILTER_NORMALIZED, &filter_normalized ); + slapi_pblock_get( src, SLAPI_SEARCH_STRFILTER, &fstr ); + slapi_pblock_get( src, SLAPI_SEARCH_ATTRS, &attrs ); + slapi_pblock_get( src, SLAPI_SEARCH_ATTRSONLY, &attrsonly ); + slapi_pblock_get( src, SLAPI_REQUESTOR_ISROOT, &isroot ); + slapi_pblock_get( src, SLAPI_SEARCH_SIZELIMIT, &sizelimit ); + slapi_pblock_get( src, SLAPI_SEARCH_TIMELIMIT, &timelimit ); + slapi_pblock_get( src, SLAPI_PLUGIN, &pi); + + Slapi_PBlock *dest = slapi_pblock_new(); + + slapi_pblock_set( dest, SLAPI_OPERATION, operation ); + slapi_pblock_set( dest, SLAPI_CONNECTION, connection ); + slapi_pblock_set( dest, SLAPI_SEARCH_SCOPE, &scope ); + slapi_pblock_set( dest, SLAPI_SEARCH_DEREF, &deref ); + slapi_pblock_set( dest, SLAPI_PLUGIN_SYNTAX_FILTER_NORMALIZED, &filter_normalized ); + slapi_pblock_set( dest, SLAPI_SEARCH_STRFILTER, slapi_ch_strdup(fstr) ); + attrs_dup = slapi_ch_array_dup(attrs); + slapi_pblock_set( dest, SLAPI_SEARCH_ATTRS, attrs_dup ); + slapi_pblock_set( dest, SLAPI_SEARCH_ATTRSONLY, &attrsonly ); + slapi_pblock_set( dest, SLAPI_REQUESTOR_ISROOT, &isroot ); + slapi_pblock_set( dest, SLAPI_SEARCH_SIZELIMIT, &sizelimit ); + slapi_pblock_set( dest, SLAPI_SEARCH_TIMELIMIT, &timelimit ); + slapi_pblock_set( dest, SLAPI_PLUGIN, pi); + + return dest; +} + +int sync_number2int(char *chgnrstr) +{ + char *end; + int nr; + nr = strtoul(chgnrstr, &end, 10); + if ( *end == '\0') { + return (nr); + } else { + return (-1); + } +} diff --git a/ldap/servers/slapd/connection.c b/ldap/servers/slapd/connection.c index f5fa51b..92352e0 100644 --- a/ldap/servers/slapd/connection.c +++ b/ldap/servers/slapd/connection.c @@ -3015,7 +3015,12 @@ connection_abandon_operations( Connection *c ) /* abandon the operation only if it is not yet completed (i.e., no result has been sent yet to the client */ - if ( op->o_status != SLAPI_OP_STATUS_RESULT_SENT ) { + /* sync repl uses the persist mode, and it cannot prevent + * setting o_status, but has to be abandonned + * handle it here until a better solution is found + */ + if ( op->o_status != SLAPI_OP_STATUS_RESULT_SENT || + op->o_flags & OP_FLAG_PS ) { op->o_status = SLAPI_OP_STATUS_ABANDONED; } } diff --git a/ldap/servers/slapd/libslapd.def b/ldap/servers/slapd/libslapd.def index 4b7cd7b..150147b 100644 --- a/ldap/servers/slapd/libslapd.def +++ b/ldap/servers/slapd/libslapd.def @@ -1200,3 +1200,7 @@ EXPORTS config_get_pw_mintokenlength @1207 slapi_check_account_lock @1208 slapi_is_ldapi_conn @1209 +; slapi connection functions + slapi_connection_acquire @1211 + slapi_connection_remove_operation @1212 + diff --git a/ldap/servers/slapd/operation.c b/ldap/servers/slapd/operation.c index 8ac413f..1c5c9b5 100644 --- a/ldap/servers/slapd/operation.c +++ b/ldap/servers/slapd/operation.c @@ -49,6 +49,7 @@ #include <sys/socket.h> #endif #include "slap.h" +#include "fe.h"
int slapi_op_abandoned( Slapi_PBlock *pb ) @@ -550,3 +551,60 @@ void operation_parameters_free(struct slapi_operation_parameters **sop) slapi_ch_free ((void**)sop); } } + +int slapi_connection_acquire(Slapi_Connection *conn) +{ + int rc; + + PR_Lock(conn->c_mutex); + /* rc = connection_acquire_nolock(conn); */ + /* connection in the closing state can't be acquired */ + if (conn->c_flags & CONN_FLAG_CLOSING) + { + /* This may happen while other threads are still working on this connection */ + slapi_log_error(SLAPI_LOG_FATAL, "connection", + "conn=%" NSPRIu64 " fd=%d Attempt to acquire connection in the closing state\n", + (long long unsigned int)conn->c_connid, conn->c_sd); + rc = -1; + } + else + { + conn->c_refcnt++; + rc = 0; + } + PR_Unlock(conn->c_mutex); + return(rc); +} + +int +slapi_connection_remove_operation( Slapi_PBlock *pb, Slapi_Connection *conn, Slapi_Operation *op, int release) +{ + int rc = 0; + Slapi_Operation **olist= &conn->c_ops; + Slapi_Operation **tmp; + PR_Lock( conn->c_mutex ); + /* connection_remove_operation_ext(pb, conn,op); */ + for ( tmp = olist; *tmp != NULL && *tmp != op; tmp = &(*tmp)->o_next ) + ; /* NULL */ + if ( *tmp == NULL ) { + LDAPDebug( LDAP_DEBUG_ANY, "connection_remove_operation: can't find op %d for conn %" NSPRIu64 "\n", + (int)op->o_msgid, conn->c_connid, 0 ); + } else { + *tmp = (*tmp)->o_next; + } + + if (release) { + /* connection_release_nolock(conn); */ + if (conn->c_refcnt <= 0) { + slapi_log_error(SLAPI_LOG_FATAL, "connection", + "conn=%" NSPRIu64 " fd=%d Attempt to release connection that is not acquired\n", + (long long unsigned int)conn->c_connid, conn->c_sd); + rc = -1; + } else { + conn->c_refcnt--; + rc = 0; + } + } + PR_Unlock( conn->c_mutex ); + return (rc); +} diff --git a/ldap/servers/slapd/plugin.c b/ldap/servers/slapd/plugin.c index aa33b02..66b011b 100644 --- a/ldap/servers/slapd/plugin.c +++ b/ldap/servers/slapd/plugin.c @@ -586,6 +586,15 @@ plugin_get_pwd_storage_scheme_list(int index) return( names_list ); }
+int slapi_send_ldap_intermediate( Slapi_PBlock *pb, LDAPControl **ectrls, + char *responseName, struct berval *responseValue) +{ + /* no SLAPI_PLUGIN_DB_INTERMEDIATE_FN defined + * always directly call slapd_ function + */ + return send_ldap_intermediate(pb, ectrls, responseName, responseValue); +} + int slapi_send_ldap_search_entry( Slapi_PBlock *pb, Slapi_Entry *e, LDAPControl **ectrls, char **attrs, int attrsonly ) diff --git a/ldap/servers/slapd/proto-slap.h b/ldap/servers/slapd/proto-slap.h index 95bfafd..e256728 100644 --- a/ldap/servers/slapd/proto-slap.h +++ b/ldap/servers/slapd/proto-slap.h @@ -958,6 +958,8 @@ int send_ldap_search_entry_ext( Slapi_PBlock *pb, Slapi_Entry *e, LDAPControl ** char **attrs, int attrsonly, int send_result, int nentries, struct berval **urls ); void send_ldap_result_ext( Slapi_PBlock *pb, int err, char *matched, char *text, int nentries, struct berval **urls, BerElement *ber ); +int send_ldap_intermediate( Slapi_PBlock *pb, LDAPControl **ectrls, + char *responseName, struct berval *responseValue); void send_nobackend_ldap_result( Slapi_PBlock *pb ); int send_ldap_referral( Slapi_PBlock *pb, Slapi_Entry *e, struct berval **refs, struct berval ***urls ); diff --git a/ldap/servers/slapd/result.c b/ldap/servers/slapd/result.c index f520e6b..655717f 100644 --- a/ldap/servers/slapd/result.c +++ b/ldap/servers/slapd/result.c @@ -82,6 +82,7 @@ static char * op_to_string(int tag); #define _LDAP_SEND_RESULT 0 #define _LDAP_SEND_REFERRAL 1 #define _LDAP_SEND_ENTRY 2 +#define _LDAP_SEND_INTERMED 4
#define SLAPI_SEND_VATTR_FLAG_REALONLY 0x01 #define SLAPI_SEND_VATTR_FLAG_VIRTUALONLY 0x02 @@ -223,6 +224,79 @@ send_ldap_result( send_ldap_result_ext(pb, err, matched, text, nentries, urls, NULL); }
+int send_ldap_intermediate( Slapi_PBlock *pb, LDAPControl **ectrls, + char *responseName, struct berval *responseValue) +{ + ber_tag_t tag; + BerElement *ber; + Slapi_Connection *connection; + Slapi_Operation *operation; + int rc = 0; + int logit = 0; + + LDAPDebug( LDAP_DEBUG_TRACE, "=> send_ldap_intermediate\n", 0, 0, 0 ); + slapi_pblock_get (pb, SLAPI_OPERATION, &operation); + slapi_pblock_get (pb, SLAPI_CONNECTION, &connection); + + if (operation->o_status == SLAPI_OP_STATUS_RESULT_SENT) { + return(rc); /* result already sent */ + } + tag = LDAP_RES_INTERMEDIATE; + if ( (ber = der_alloc()) == NULL ) { + LDAPDebug( LDAP_DEBUG_ANY, "ber_alloc failed\n", 0, 0, 0 ); + goto log_and_return; + } + /* add the intermediate message */ + rc = ber_printf( ber, "{it{", operation->o_msgid, tag); + /* print responsename */ + rc = ber_printf ( ber, "ts", LDAP_TAG_IM_RES_OID, responseName); + /* print responsevalue */ + rc = ber_printf ( ber, "tO", LDAP_TAG_IM_RES_VALUE, responseValue); + + + + if ( rc != LBER_ERROR ) { + rc = ber_printf( ber, "}" ); /* one more } to come */ + } + + if ( ectrls != NULL + && connection->c_ldapversion >= LDAP_VERSION3 + && write_controls( ber, ectrls ) != 0 ) { + rc = (int)LBER_ERROR; + } + + if ( rc != LBER_ERROR ) { /* end the LDAPMessage sequence */ + rc = ber_put_seq( ber ); + } + if ( rc == LBER_ERROR ) { + LDAPDebug( LDAP_DEBUG_ANY, "ber_printf failed\n", 0, 0, 0 ); + ber_free( ber, 1 /* freebuf */ ); + goto log_and_return; + } + + /* write only one pdu at a time - wait til it's our turn */ + if ( flush_ber( pb, connection, operation, ber, _LDAP_SEND_INTERMED ) == 0 ) { + logit = 1; + } +log_and_return: + /* operation->o_status = SLAPI_OP_STATUS_RESULT_SENT; + * there could be multiple intermediate messages on + * the same connection, unlike in send_result do not + * set o_status + */ + + if ( logit && operation_is_flag_set( operation, + OP_FLAG_ACTION_LOG_ACCESS )) { + log_result( pb, operation, rc, tag, 0 ); + } + + LDAPDebug( LDAP_DEBUG_TRACE, "<= send_ldap_intermediate\n", 0, 0, 0 ); + if (rc == LBER_ERROR) { + return(1); + } else { + return(0); + } +}
static int check_and_send_extended_result(Slapi_PBlock *pb, ber_tag_t tag, BerElement *ber) @@ -1555,7 +1629,7 @@ send_ldap_search_entry_ext( } /* if the client explicitly specified a list of attributes look through each attribute requested */ - if( (rc == 0) && (attrs!=NULL)) { + if( (rc == 0) && (attrs!=NULL) && !noattrs) { rc = send_specific_attrs(e,attrs,op,pb,ber,attrsonly,conn->c_ldapversion,dontsendattr,real_attrs_only); }
@@ -1695,6 +1769,8 @@ flush_ber( case _LDAP_SEND_REFERRAL: rc = plugin_call_plugins( pb, SLAPI_PLUGIN_PRE_REFERRAL_FN ); break; + case _LDAP_SEND_INTERMED: + break; /* not a plugin entry point */ }
if ( rc != 0 ) { @@ -1764,6 +1840,8 @@ flush_ber( slapi_counter_increment(g_get_global_snmp_vars()->ops_tbl.dsEntriesReturned); plugin_call_plugins( pb, SLAPI_PLUGIN_POST_ENTRY_FN ); break; + case _LDAP_SEND_INTERMED: + break; /* not a plugin entry point */ }
return( rc ); diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h index e02a125..f904267 100644 --- a/ldap/servers/slapd/slapi-plugin.h +++ b/ldap/servers/slapd/slapi-plugin.h @@ -4966,6 +4966,13 @@ char *slapi_op_type_to_string(unsigned long type); int slapi_op_internal( Slapi_PBlock *pb );
/* + * connection routines + */ + +int slapi_connection_acquire(Slapi_Connection *conn); +int slapi_connection_remove_operation( Slapi_PBlock *pb, Slapi_Connection *conn, Slapi_Operation *op, int release); + +/* * LDAPMod manipulation routines */ Slapi_Mods* slapi_mods_new( void ); @@ -5127,6 +5134,8 @@ void slapi_send_ldap_result( Slapi_PBlock *pb, int err, char *matched, char *text, int nentries, struct berval **urls ); int slapi_send_ldap_referral( Slapi_PBlock *pb, Slapi_Entry *e, struct berval **refs, struct berval ***urls ); +int slapi_send_ldap_intermediate( Slapi_PBlock *pb, LDAPControl **ectrls, + char *responseName, struct berval *responseValue); typedef int (*send_ldap_search_entry_fn_ptr_t)( Slapi_PBlock *pb, Slapi_Entry *e, LDAPControl **ectrls, char **attrs, int attrsonly ); typedef void (*send_ldap_result_fn_ptr_t)( Slapi_PBlock *pb, int err,
389-commits@lists.fedoraproject.org