diff options
-rw-r--r-- | Makefile.am | 83 | ||||
-rw-r--r-- | Makefile.in | 128 | ||||
-rw-r--r-- | ldap/admin/src/scripts/template-fixup-linkedattrs.pl.in | 152 | ||||
-rw-r--r-- | ldap/ldif/template-dse.ldif.in | 12 | ||||
-rw-r--r-- | ldap/servers/plugins/linkedattrs/fixup_task.c | 401 | ||||
-rw-r--r-- | ldap/servers/plugins/linkedattrs/linked_attrs.c | 2024 | ||||
-rw-r--r-- | ldap/servers/plugins/linkedattrs/linked_attrs.h | 141 | ||||
-rw-r--r-- | ldap/servers/slapd/add.c | 4 | ||||
-rw-r--r-- | ldap/servers/slapd/attr.c | 2 | ||||
-rw-r--r-- | ldap/servers/slapd/dse.c | 13 |
10 files changed, 2886 insertions, 74 deletions
diff --git a/Makefile.am b/Makefile.am index 27869a92..4dd8973e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -148,12 +148,13 @@ enable_presence = off endif serverplugin_LTLIBRARIES = libacl-plugin.la libattr-unique-plugin.la \ - libback-ldbm.la libchainingdb-plugin.la libcos-plugin.la libdes-plugin.la \ - libdistrib-plugin.la libhttp-client-plugin.la libcollation-plugin.la \ + libback-ldbm.la libchainingdb-plugin.la libcollation-plugin.la \ + libcos-plugin.la libdes-plugin.la libdistrib-plugin.la \ + libhttp-client-plugin.la liblinkedattrs-plugin.la \ libmemberof-plugin.la libpassthru-plugin.la libpwdstorage-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 $(LIBPAM_PASSTHRU_PLUGIN) \ + libroles-plugin.la libschemareload-plugin.la libstatechange-plugin.la \ + libsyntax-plugin.la libviews-plugin.la $(LIBPAM_PASSTHRU_PLUGIN) \ $(LIBDNA_PLUGIN) $(LIBBITWISE_PLUGIN) $(LIBPRESENCE_PLUGIN) nodist_property_DATA = ns-slapd.properties @@ -299,6 +300,7 @@ task_SCRIPTS = ldap/admin/src/scripts/template-bak2db \ ldap/admin/src/scripts/template-db2bak.pl \ ldap/admin/src/scripts/template-db2index.pl \ ldap/admin/src/scripts/template-db2ldif.pl \ + ldap/admin/src/scripts/template-fixup-linkedattrs.pl \ ldap/admin/src/scripts/template-fixup-memberof.pl \ ldap/admin/src/scripts/template-ldif2db.pl \ ldap/admin/src/scripts/template-ns-accountstatus.pl \ @@ -664,6 +666,14 @@ libattr_unique_plugin_la_CPPFLAGS = -I$(srcdir)/ldap/servers/plugins/shared $(PL libattr_unique_plugin_la_LDFLAGS = -avoid-version #------------------------ +# libbitwise-plugin +#------------------------ +libbitwise_plugin_la_SOURCES = ldap/servers/plugins/bitwise/bitwise.c + +libbitwise_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS) +libbitwise_plugin_la_LDFLAGS = -avoid-version + +#------------------------ # libchainingdb-plugin #------------------------ libchainingdb_plugin_la_SOURCES = ldap/servers/plugins/chainingdb/cb_abandon.c \ @@ -696,6 +706,18 @@ libchainingdb_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS) libchainingdb_plugin_la_LDFLAGS = -avoid-version #------------------------ +# libcollation-plugin +#------------------------ +libcollation_plugin_la_SOURCES = ldap/servers/plugins/collation/collate.c \ + ldap/servers/plugins/collation/config.c \ + ldap/servers/plugins/collation/orfilter.c + +libcollation_plugin_la_CPPFLAGS = @icu_inc@ $(PLUGIN_CPPFLAGS) +libcollation_plugin_la_LIBADD = $(ICU_LINK) $(LIBCSTD) $(LIBCRUN) +libcollation_plugin_la_LDFLAGS = -avoid-version +libcollation_plugin_la_LINK = $(CXXLINK) -avoid-version + +#------------------------ # libcos-plugin #------------------------ libcos_plugin_la_SOURCES = ldap/servers/plugins/cos/cos.c \ @@ -722,6 +744,14 @@ libdistrib_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS) libdistrib_plugin_la_LDFLAGS = -avoid-version #------------------------ +# libdna-plugin +#------------------------ +libdna_plugin_la_SOURCES = ldap/servers/plugins/dna/dna.c + +libdna_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS) +libdna_plugin_la_LDFLAGS = -avoid-version + +#------------------------ # libhttp-client-plugin #------------------------ libhttp_client_plugin_la_SOURCES = ldap/servers/plugins/http/http_client.c \ @@ -731,16 +761,13 @@ libhttp_client_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS) libhttp_client_plugin_la_LDFLAGS = -avoid-version #------------------------ -# libcollation-plugin +# liblinkedattrs-plugin #------------------------ -libcollation_plugin_la_SOURCES = ldap/servers/plugins/collation/collate.c \ - ldap/servers/plugins/collation/config.c \ - ldap/servers/plugins/collation/orfilter.c +liblinkedattrs_plugin_la_SOURCES = ldap/servers/plugins/linkedattrs/fixup_task.c \ + ldap/servers/plugins/linkedattrs/linked_attrs.c -libcollation_plugin_la_CPPFLAGS = @icu_inc@ $(PLUGIN_CPPFLAGS) -libcollation_plugin_la_LIBADD = $(ICU_LINK) $(LIBCSTD) $(LIBCRUN) -libcollation_plugin_la_LDFLAGS = -avoid-version -libcollation_plugin_la_LINK = $(CXXLINK) -avoid-version +liblinkedattrs_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS) +liblinkedattrs_plugin_la_LDFLAGS = -avoid-version #------------------------ # libmemberof-plugin @@ -892,6 +919,14 @@ libroles_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS) libroles_plugin_la_LDFLAGS = -avoid-version #------------------------ +# libschemareload-plugin +#------------------------ +libschemareload_plugin_la_SOURCES = ldap/servers/plugins/schema_reload/schema_reload.c + +libschemareload_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS) +libschemareload_plugin_la_LDFLAGS = -avoid-version + +#------------------------ # libstatechange-plugin #------------------------ libstatechange_plugin_la_SOURCES = ldap/servers/plugins/statechange/statechange.c @@ -928,30 +963,6 @@ libviews_plugin_la_SOURCES = ldap/servers/plugins/views/views.c libviews_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS) libviews_plugin_la_LDFLAGS = -avoid-version -#------------------------ -# libschemareload-plugin -#------------------------ -libschemareload_plugin_la_SOURCES = ldap/servers/plugins/schema_reload/schema_reload.c - -libschemareload_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS) -libschemareload_plugin_la_LDFLAGS = -avoid-version - -#------------------------ -# libdna-plugin -#------------------------ -libdna_plugin_la_SOURCES = ldap/servers/plugins/dna/dna.c - -libdna_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS) -libdna_plugin_la_LDFLAGS = -avoid-version - -#------------------------ -# libbitwise-plugin -#------------------------ -libbitwise_plugin_la_SOURCES = ldap/servers/plugins/bitwise/bitwise.c - -libbitwise_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS) -libbitwise_plugin_la_LDFLAGS = -avoid-version - #//////////////////////////////////////////////////////////////// # diff --git a/Makefile.in b/Makefile.in index 1e423694..e82a1e64 100644 --- a/Makefile.in +++ b/Makefile.in @@ -279,6 +279,14 @@ libhttp_client_plugin_la_OBJECTS = \ libhttp_client_plugin_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(libhttp_client_plugin_la_LDFLAGS) $(LDFLAGS) -o $@ +liblinkedattrs_plugin_la_LIBADD = +am_liblinkedattrs_plugin_la_OBJECTS = ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-fixup_task.lo \ + ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-linked_attrs.lo +liblinkedattrs_plugin_la_OBJECTS = \ + $(am_liblinkedattrs_plugin_la_OBJECTS) +liblinkedattrs_plugin_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(liblinkedattrs_plugin_la_LDFLAGS) $(LDFLAGS) -o $@ libmemberof_plugin_la_LIBADD = am_libmemberof_plugin_la_OBJECTS = ldap/servers/plugins/memberof/libmemberof_plugin_la-memberof.lo \ ldap/servers/plugins/memberof/libmemberof_plugin_la-memberof_config.lo @@ -872,6 +880,7 @@ SOURCES = $(libavl_a_SOURCES) $(libldaputil_a_SOURCES) \ $(libdes_plugin_la_SOURCES) $(libdistrib_plugin_la_SOURCES) \ $(libdna_plugin_la_SOURCES) \ $(libhttp_client_plugin_la_SOURCES) \ + $(liblinkedattrs_plugin_la_SOURCES) \ $(libmemberof_plugin_la_SOURCES) $(libns_dshttpd_la_SOURCES) \ $(libpam_passthru_plugin_la_SOURCES) \ $(libpassthru_plugin_la_SOURCES) \ @@ -898,6 +907,7 @@ DIST_SOURCES = $(libavl_a_SOURCES) $(libldaputil_a_SOURCES) \ $(libdes_plugin_la_SOURCES) $(libdistrib_plugin_la_SOURCES) \ $(libdna_plugin_la_SOURCES) \ $(libhttp_client_plugin_la_SOURCES) \ + $(liblinkedattrs_plugin_la_SOURCES) \ $(libmemberof_plugin_la_SOURCES) $(libns_dshttpd_la_SOURCES) \ $(libpam_passthru_plugin_la_SOURCES) \ $(libpassthru_plugin_la_SOURCES) \ @@ -1222,12 +1232,13 @@ server_LTLIBRARIES = libslapd.la libns-dshttpd.la @enable_presence_FALSE@enable_presence = off @enable_presence_TRUE@enable_presence = on serverplugin_LTLIBRARIES = libacl-plugin.la libattr-unique-plugin.la \ - libback-ldbm.la libchainingdb-plugin.la libcos-plugin.la libdes-plugin.la \ - libdistrib-plugin.la libhttp-client-plugin.la libcollation-plugin.la \ + libback-ldbm.la libchainingdb-plugin.la libcollation-plugin.la \ + libcos-plugin.la libdes-plugin.la libdistrib-plugin.la \ + libhttp-client-plugin.la liblinkedattrs-plugin.la \ libmemberof-plugin.la libpassthru-plugin.la libpwdstorage-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 $(LIBPAM_PASSTHRU_PLUGIN) \ + libroles-plugin.la libschemareload-plugin.la libstatechange-plugin.la \ + libsyntax-plugin.la libviews-plugin.la $(LIBPAM_PASSTHRU_PLUGIN) \ $(LIBDNA_PLUGIN) $(LIBBITWISE_PLUGIN) $(LIBPRESENCE_PLUGIN) nodist_property_DATA = ns-slapd.properties @@ -1372,6 +1383,7 @@ task_SCRIPTS = ldap/admin/src/scripts/template-bak2db \ ldap/admin/src/scripts/template-db2bak.pl \ ldap/admin/src/scripts/template-db2index.pl \ ldap/admin/src/scripts/template-db2ldif.pl \ + ldap/admin/src/scripts/template-fixup-linkedattrs.pl \ ldap/admin/src/scripts/template-fixup-memberof.pl \ ldap/admin/src/scripts/template-ldif2db.pl \ ldap/admin/src/scripts/template-ns-accountstatus.pl \ @@ -1684,6 +1696,13 @@ libattr_unique_plugin_la_CPPFLAGS = -I$(srcdir)/ldap/servers/plugins/shared $(PL libattr_unique_plugin_la_LDFLAGS = -avoid-version #------------------------ +# libbitwise-plugin +#------------------------ +libbitwise_plugin_la_SOURCES = ldap/servers/plugins/bitwise/bitwise.c +libbitwise_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS) +libbitwise_plugin_la_LDFLAGS = -avoid-version + +#------------------------ # libchainingdb-plugin #------------------------ libchainingdb_plugin_la_SOURCES = ldap/servers/plugins/chainingdb/cb_abandon.c \ @@ -1716,6 +1735,18 @@ libchainingdb_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS) libchainingdb_plugin_la_LDFLAGS = -avoid-version #------------------------ +# libcollation-plugin +#------------------------ +libcollation_plugin_la_SOURCES = ldap/servers/plugins/collation/collate.c \ + ldap/servers/plugins/collation/config.c \ + ldap/servers/plugins/collation/orfilter.c + +libcollation_plugin_la_CPPFLAGS = @icu_inc@ $(PLUGIN_CPPFLAGS) +libcollation_plugin_la_LIBADD = $(ICU_LINK) $(LIBCSTD) $(LIBCRUN) +libcollation_plugin_la_LDFLAGS = -avoid-version +libcollation_plugin_la_LINK = $(CXXLINK) -avoid-version + +#------------------------ # libcos-plugin #------------------------ libcos_plugin_la_SOURCES = ldap/servers/plugins/cos/cos.c \ @@ -1741,6 +1772,13 @@ libdistrib_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS) libdistrib_plugin_la_LDFLAGS = -avoid-version #------------------------ +# libdna-plugin +#------------------------ +libdna_plugin_la_SOURCES = ldap/servers/plugins/dna/dna.c +libdna_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS) +libdna_plugin_la_LDFLAGS = -avoid-version + +#------------------------ # libhttp-client-plugin #------------------------ libhttp_client_plugin_la_SOURCES = ldap/servers/plugins/http/http_client.c \ @@ -1750,16 +1788,13 @@ libhttp_client_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS) libhttp_client_plugin_la_LDFLAGS = -avoid-version #------------------------ -# libcollation-plugin +# liblinkedattrs-plugin #------------------------ -libcollation_plugin_la_SOURCES = ldap/servers/plugins/collation/collate.c \ - ldap/servers/plugins/collation/config.c \ - ldap/servers/plugins/collation/orfilter.c +liblinkedattrs_plugin_la_SOURCES = ldap/servers/plugins/linkedattrs/fixup_task.c \ + ldap/servers/plugins/linkedattrs/linked_attrs.c -libcollation_plugin_la_CPPFLAGS = @icu_inc@ $(PLUGIN_CPPFLAGS) -libcollation_plugin_la_LIBADD = $(ICU_LINK) $(LIBCSTD) $(LIBCRUN) -libcollation_plugin_la_LDFLAGS = -avoid-version -libcollation_plugin_la_LINK = $(CXXLINK) -avoid-version +liblinkedattrs_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS) +liblinkedattrs_plugin_la_LDFLAGS = -avoid-version #------------------------ # libmemberof-plugin @@ -1909,6 +1944,13 @@ libroles_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS) libroles_plugin_la_LDFLAGS = -avoid-version #------------------------ +# libschemareload-plugin +#------------------------ +libschemareload_plugin_la_SOURCES = ldap/servers/plugins/schema_reload/schema_reload.c +libschemareload_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS) +libschemareload_plugin_la_LDFLAGS = -avoid-version + +#------------------------ # libstatechange-plugin #------------------------ libstatechange_plugin_la_SOURCES = ldap/servers/plugins/statechange/statechange.c @@ -1943,27 +1985,6 @@ libviews_plugin_la_SOURCES = ldap/servers/plugins/views/views.c libviews_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS) libviews_plugin_la_LDFLAGS = -avoid-version -#------------------------ -# libschemareload-plugin -#------------------------ -libschemareload_plugin_la_SOURCES = ldap/servers/plugins/schema_reload/schema_reload.c -libschemareload_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS) -libschemareload_plugin_la_LDFLAGS = -avoid-version - -#------------------------ -# libdna-plugin -#------------------------ -libdna_plugin_la_SOURCES = ldap/servers/plugins/dna/dna.c -libdna_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS) -libdna_plugin_la_LDFLAGS = -avoid-version - -#------------------------ -# libbitwise-plugin -#------------------------ -libbitwise_plugin_la_SOURCES = ldap/servers/plugins/bitwise/bitwise.c -libbitwise_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS) -libbitwise_plugin_la_LDFLAGS = -avoid-version - #//////////////////////////////////////////////////////////////// # # Programs @@ -2814,6 +2835,20 @@ ldap/servers/plugins/http/libhttp_client_plugin_la-http_impl.lo: \ ldap/servers/plugins/http/$(DEPDIR)/$(am__dirstamp) libhttp-client-plugin.la: $(libhttp_client_plugin_la_OBJECTS) $(libhttp_client_plugin_la_DEPENDENCIES) $(libhttp_client_plugin_la_LINK) -rpath $(serverplugindir) $(libhttp_client_plugin_la_OBJECTS) $(libhttp_client_plugin_la_LIBADD) $(LIBS) +ldap/servers/plugins/linkedattrs/$(am__dirstamp): + @$(MKDIR_P) ldap/servers/plugins/linkedattrs + @: > ldap/servers/plugins/linkedattrs/$(am__dirstamp) +ldap/servers/plugins/linkedattrs/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) ldap/servers/plugins/linkedattrs/$(DEPDIR) + @: > ldap/servers/plugins/linkedattrs/$(DEPDIR)/$(am__dirstamp) +ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-fixup_task.lo: \ + ldap/servers/plugins/linkedattrs/$(am__dirstamp) \ + ldap/servers/plugins/linkedattrs/$(DEPDIR)/$(am__dirstamp) +ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-linked_attrs.lo: \ + ldap/servers/plugins/linkedattrs/$(am__dirstamp) \ + ldap/servers/plugins/linkedattrs/$(DEPDIR)/$(am__dirstamp) +liblinkedattrs-plugin.la: $(liblinkedattrs_plugin_la_OBJECTS) $(liblinkedattrs_plugin_la_DEPENDENCIES) + $(liblinkedattrs_plugin_la_LINK) -rpath $(serverplugindir) $(liblinkedattrs_plugin_la_OBJECTS) $(liblinkedattrs_plugin_la_LIBADD) $(LIBS) ldap/servers/plugins/memberof/$(am__dirstamp): @$(MKDIR_P) ldap/servers/plugins/memberof @: > ldap/servers/plugins/memberof/$(am__dirstamp) @@ -4180,6 +4215,10 @@ mostlyclean-compile: -rm -f ldap/servers/plugins/http/libhttp_client_plugin_la-http_client.lo -rm -f ldap/servers/plugins/http/libhttp_client_plugin_la-http_impl.$(OBJEXT) -rm -f ldap/servers/plugins/http/libhttp_client_plugin_la-http_impl.lo + -rm -f ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-fixup_task.$(OBJEXT) + -rm -f ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-fixup_task.lo + -rm -f ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-linked_attrs.$(OBJEXT) + -rm -f ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-linked_attrs.lo -rm -f ldap/servers/plugins/memberof/libmemberof_plugin_la-memberof.$(OBJEXT) -rm -f ldap/servers/plugins/memberof/libmemberof_plugin_la-memberof.lo -rm -f ldap/servers/plugins/memberof/libmemberof_plugin_la-memberof_config.$(OBJEXT) @@ -4937,6 +4976,8 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@ldap/servers/plugins/dna/$(DEPDIR)/libdna_plugin_la-dna.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ldap/servers/plugins/http/$(DEPDIR)/libhttp_client_plugin_la-http_client.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ldap/servers/plugins/http/$(DEPDIR)/libhttp_client_plugin_la-http_impl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@ldap/servers/plugins/linkedattrs/$(DEPDIR)/liblinkedattrs_plugin_la-fixup_task.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@ldap/servers/plugins/linkedattrs/$(DEPDIR)/liblinkedattrs_plugin_la-linked_attrs.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ldap/servers/plugins/memberof/$(DEPDIR)/libmemberof_plugin_la-memberof.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ldap/servers/plugins/memberof/$(DEPDIR)/libmemberof_plugin_la-memberof_config.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ldap/servers/plugins/pam_passthru/$(DEPDIR)/libpam_passthru_plugin_la-pam_ptconfig.Plo@am__quote@ @@ -6285,6 +6326,20 @@ ldap/servers/plugins/http/libhttp_client_plugin_la-http_impl.lo: ldap/servers/pl @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) $(libhttp_client_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ldap/servers/plugins/http/libhttp_client_plugin_la-http_impl.lo `test -f 'ldap/servers/plugins/http/http_impl.c' || echo '$(srcdir)/'`ldap/servers/plugins/http/http_impl.c +ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-fixup_task.lo: ldap/servers/plugins/linkedattrs/fixup_task.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblinkedattrs_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-fixup_task.lo -MD -MP -MF ldap/servers/plugins/linkedattrs/$(DEPDIR)/liblinkedattrs_plugin_la-fixup_task.Tpo -c -o ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-fixup_task.lo `test -f 'ldap/servers/plugins/linkedattrs/fixup_task.c' || echo '$(srcdir)/'`ldap/servers/plugins/linkedattrs/fixup_task.c +@am__fastdepCC_TRUE@ mv -f ldap/servers/plugins/linkedattrs/$(DEPDIR)/liblinkedattrs_plugin_la-fixup_task.Tpo ldap/servers/plugins/linkedattrs/$(DEPDIR)/liblinkedattrs_plugin_la-fixup_task.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ldap/servers/plugins/linkedattrs/fixup_task.c' object='ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-fixup_task.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) $(liblinkedattrs_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-fixup_task.lo `test -f 'ldap/servers/plugins/linkedattrs/fixup_task.c' || echo '$(srcdir)/'`ldap/servers/plugins/linkedattrs/fixup_task.c + +ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-linked_attrs.lo: ldap/servers/plugins/linkedattrs/linked_attrs.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblinkedattrs_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-linked_attrs.lo -MD -MP -MF ldap/servers/plugins/linkedattrs/$(DEPDIR)/liblinkedattrs_plugin_la-linked_attrs.Tpo -c -o ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-linked_attrs.lo `test -f 'ldap/servers/plugins/linkedattrs/linked_attrs.c' || echo '$(srcdir)/'`ldap/servers/plugins/linkedattrs/linked_attrs.c +@am__fastdepCC_TRUE@ mv -f ldap/servers/plugins/linkedattrs/$(DEPDIR)/liblinkedattrs_plugin_la-linked_attrs.Tpo ldap/servers/plugins/linkedattrs/$(DEPDIR)/liblinkedattrs_plugin_la-linked_attrs.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ldap/servers/plugins/linkedattrs/linked_attrs.c' object='ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-linked_attrs.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) $(liblinkedattrs_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-linked_attrs.lo `test -f 'ldap/servers/plugins/linkedattrs/linked_attrs.c' || echo '$(srcdir)/'`ldap/servers/plugins/linkedattrs/linked_attrs.c + ldap/servers/plugins/memberof/libmemberof_plugin_la-memberof.lo: ldap/servers/plugins/memberof/memberof.c @am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmemberof_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ldap/servers/plugins/memberof/libmemberof_plugin_la-memberof.lo -MD -MP -MF ldap/servers/plugins/memberof/$(DEPDIR)/libmemberof_plugin_la-memberof.Tpo -c -o ldap/servers/plugins/memberof/libmemberof_plugin_la-memberof.lo `test -f 'ldap/servers/plugins/memberof/memberof.c' || echo '$(srcdir)/'`ldap/servers/plugins/memberof/memberof.c @am__fastdepCC_TRUE@ mv -f ldap/servers/plugins/memberof/$(DEPDIR)/libmemberof_plugin_la-memberof.Tpo ldap/servers/plugins/memberof/$(DEPDIR)/libmemberof_plugin_la-memberof.Plo @@ -8962,6 +9017,7 @@ clean-libtool: -rm -rf ldap/servers/plugins/distrib/.libs ldap/servers/plugins/distrib/_libs -rm -rf ldap/servers/plugins/dna/.libs ldap/servers/plugins/dna/_libs -rm -rf ldap/servers/plugins/http/.libs ldap/servers/plugins/http/_libs + -rm -rf ldap/servers/plugins/linkedattrs/.libs ldap/servers/plugins/linkedattrs/_libs -rm -rf ldap/servers/plugins/memberof/.libs ldap/servers/plugins/memberof/_libs -rm -rf ldap/servers/plugins/pam_passthru/.libs ldap/servers/plugins/pam_passthru/_libs -rm -rf ldap/servers/plugins/passthru/.libs ldap/servers/plugins/passthru/_libs @@ -9459,6 +9515,8 @@ distclean-generic: -rm -f ldap/servers/plugins/dna/$(am__dirstamp) -rm -f ldap/servers/plugins/http/$(DEPDIR)/$(am__dirstamp) -rm -f ldap/servers/plugins/http/$(am__dirstamp) + -rm -f ldap/servers/plugins/linkedattrs/$(DEPDIR)/$(am__dirstamp) + -rm -f ldap/servers/plugins/linkedattrs/$(am__dirstamp) -rm -f ldap/servers/plugins/memberof/$(DEPDIR)/$(am__dirstamp) -rm -f ldap/servers/plugins/memberof/$(am__dirstamp) -rm -f ldap/servers/plugins/pam_passthru/$(DEPDIR)/$(am__dirstamp) @@ -9529,7 +9587,7 @@ clean-am: clean-binPROGRAMS clean-generic clean-libtool \ distclean: distclean-am -rm -f $(am__CONFIG_DISTCLEAN_FILES) - -rm -rf ldap/libraries/libavl/$(DEPDIR) ldap/servers/plugins/acl/$(DEPDIR) ldap/servers/plugins/bitwise/$(DEPDIR) ldap/servers/plugins/chainingdb/$(DEPDIR) ldap/servers/plugins/collation/$(DEPDIR) ldap/servers/plugins/cos/$(DEPDIR) ldap/servers/plugins/distrib/$(DEPDIR) ldap/servers/plugins/dna/$(DEPDIR) ldap/servers/plugins/http/$(DEPDIR) ldap/servers/plugins/memberof/$(DEPDIR) ldap/servers/plugins/pam_passthru/$(DEPDIR) ldap/servers/plugins/passthru/$(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/schema_reload/$(DEPDIR) ldap/servers/plugins/shared/$(DEPDIR) ldap/servers/plugins/statechange/$(DEPDIR) ldap/servers/plugins/syntaxes/$(DEPDIR) ldap/servers/plugins/uiduniq/$(DEPDIR) ldap/servers/plugins/views/$(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/acl/$(DEPDIR) ldap/servers/plugins/bitwise/$(DEPDIR) ldap/servers/plugins/chainingdb/$(DEPDIR) ldap/servers/plugins/collation/$(DEPDIR) ldap/servers/plugins/cos/$(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/pam_passthru/$(DEPDIR) ldap/servers/plugins/passthru/$(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/schema_reload/$(DEPDIR) ldap/servers/plugins/shared/$(DEPDIR) ldap/servers/plugins/statechange/$(DEPDIR) ldap/servers/plugins/syntaxes/$(DEPDIR) ldap/servers/plugins/uiduniq/$(DEPDIR) ldap/servers/plugins/views/$(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 @@ -9571,7 +9629,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/acl/$(DEPDIR) ldap/servers/plugins/bitwise/$(DEPDIR) ldap/servers/plugins/chainingdb/$(DEPDIR) ldap/servers/plugins/collation/$(DEPDIR) ldap/servers/plugins/cos/$(DEPDIR) ldap/servers/plugins/distrib/$(DEPDIR) ldap/servers/plugins/dna/$(DEPDIR) ldap/servers/plugins/http/$(DEPDIR) ldap/servers/plugins/memberof/$(DEPDIR) ldap/servers/plugins/pam_passthru/$(DEPDIR) ldap/servers/plugins/passthru/$(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/schema_reload/$(DEPDIR) ldap/servers/plugins/shared/$(DEPDIR) ldap/servers/plugins/statechange/$(DEPDIR) ldap/servers/plugins/syntaxes/$(DEPDIR) ldap/servers/plugins/uiduniq/$(DEPDIR) ldap/servers/plugins/views/$(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/acl/$(DEPDIR) ldap/servers/plugins/bitwise/$(DEPDIR) ldap/servers/plugins/chainingdb/$(DEPDIR) ldap/servers/plugins/collation/$(DEPDIR) ldap/servers/plugins/cos/$(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/pam_passthru/$(DEPDIR) ldap/servers/plugins/passthru/$(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/schema_reload/$(DEPDIR) ldap/servers/plugins/shared/$(DEPDIR) ldap/servers/plugins/statechange/$(DEPDIR) ldap/servers/plugins/syntaxes/$(DEPDIR) ldap/servers/plugins/uiduniq/$(DEPDIR) ldap/servers/plugins/views/$(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/template-fixup-linkedattrs.pl.in b/ldap/admin/src/scripts/template-fixup-linkedattrs.pl.in new file mode 100644 index 00000000..48a89266 --- /dev/null +++ b/ldap/admin/src/scripts/template-fixup-linkedattrs.pl.in @@ -0,0 +1,152 @@ +#{{PERL-EXEC}} +# +# 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) 2001 Sun Microsystems, Inc. Used by permission. +# Copyright (C) 2009 Red Hat, Inc. +# All rights reserved. +# END COPYRIGHT BLOCK +# + +sub usage { + print(STDERR "Usage: $0 [-v] -D rootdn { -w password | -w - | -j filename } \n"); + print(STDERR " [-l linkDN]\n"); + print(STDERR " Opts: -D rootdn - Directory Manager\n"); + print(STDERR " : -w password - Directory Manager's password\n"); + print(STDERR " : -w - - Prompt for Directory Manager's password\n"); + print(STDERR " : -j filename - Read Directory Manager's password from file\n"); + print(STDERR " : -l linkDN - DN of link config entry that you want to fix\n"); + print(STDERR " up the links for. If omitted, all configured\n"); + print(STDERR " linked attributes will be fixed up.\n"); + print(STDERR " : -v - verbose\n"); +} + +$rootdn = ""; +$passwd = ""; +$passwdfile = ""; +$linkdn_arg = ""; +$verbose = 0; + +$prefix = "{{DS-ROOT}}"; + +$ENV{'PATH'} = "$prefix@ldapsdk_bindir@:$prefix/usr/bin:@ldapsdk_bindir@:/usr/bin"; +$ENV{'LD_LIBRARY_PATH'} = "$prefix@nss_libdir@:$prefix/usr/lib:@nss_libdir@:/usr/lib"; +$ENV{'SHLIB_PATH'} = "$prefix@nss_libdir@:$prefix/usr/lib:@nss_libdir@:/usr/lib"; + +$i = 0; +while ($i <= $#ARGV) +{ + if ("$ARGV[$i]" eq "-l") + { + # link DN + $i++; $linkdn_arg = $ARGV[$i]; + } + elsif ("$ARGV[$i]" eq "-D") + { + # Directory Manager + $i++; $rootdn = $ARGV[$i]; + } + elsif ("$ARGV[$i]" eq "-w") + { + # Directory Manager's password + $i++; $passwd = $ARGV[$i]; + } + elsif ("$ARGV[$i]" eq "-j") + { + # Read Directory Manager's password from a file + $i++; $passwdfile = $ARGV[$i]; + } + elsif ("$ARGV[$i]" eq "-v") + { + # verbose + $verbose = 1; + } + else + { + &usage; exit(1); + } + $i++; +} + +if ($passwdfile ne ""){ +# Open file and get the password + unless (open (RPASS, $passwdfile)) { + die "Error, cannot open password file $passwdfile\n"; + } + $passwd = <RPASS>; + chomp($passwd); + close(RPASS); +} elsif ($passwd eq "-"){ +# Read the password from terminal + print "Bind Password: "; + # Disable console echo + system("stty -echo"); + # read the answer + $passwd = <STDIN>; + # Enable console echo + system("stty echo"); + print "\n"; + chop($passwd); # trim trailing newline +} + +if ( $rootdn eq "" || $passwd eq "" ) +{ + &usage; + exit(1); +} + +$vstr = ""; +if ($verbose != 0) +{ + $vstr = "-v"; +} + +# Use a timestamp as part of the task entry name +($s, $m, $h, $dy, $mn, $yr, $wdy, $ydy, $r) = localtime(time); +$mn++; $yr += 1900; +$taskname = "linked_attrs_fixup_${yr}_${mn}_${dy}_${h}_${m}_${s}"; + +# Build the task entry to add +$dn = "dn: cn=$taskname, cn=fixup linked attributes, cn=tasks, cn=config\n"; +$misc = "changetype: add\nobjectclass: top\nobjectclass: extensibleObject\n"; +$cn = "cn: $taskname\n"; +if ($linkdn_arg ne "") +{ + $linkdn = "linkdn: $linkdn_arg\n"; +} + +$entry = "${dn}${misc}${cn}${basedn}${linkdn}"; +open(FOO, "| ldapmodify $vstr -h {{SERVER-NAME}} -p {{SERVER-PORT}} -D \"$rootdn\" -w \"$passwd\" -a" ); +print(FOO "$entry"); +close(FOO); diff --git a/ldap/ldif/template-dse.ldif.in b/ldap/ldif/template-dse.ldif.in index 82326d55..2694f0f1 100644 --- a/ldap/ldif/template-dse.ldif.in +++ b/ldap/ldif/template-dse.ldif.in @@ -554,6 +554,18 @@ nsslapd-plugintype: preoperation nsslapd-pluginenabled: on nsslapd-plugin-depends-on-type: database +dn: cn=Linked Attributes,cn=plugins,cn=config +objectclass: top +objectclass: nsSlapdPlugin +objectclass: extensibleObject +objectclass: nsContainer +cn: Linked Attributes +nsslapd-pluginpath: liblinkedattrs-plugin +nsslapd-plugininitfunc: linked_attrs_init +nsslapd-plugintype: preoperation +nsslapd-pluginenabled: on +nsslapd-plugin-depends-on-type: database + dn: cn=Pass Through Authentication,cn=plugins,cn=config objectclass: top objectclass: nsSlapdPlugin diff --git a/ldap/servers/plugins/linkedattrs/fixup_task.c b/ldap/servers/plugins/linkedattrs/fixup_task.c new file mode 100644 index 00000000..50d35d0e --- /dev/null +++ b/ldap/servers/plugins/linkedattrs/fixup_task.c @@ -0,0 +1,401 @@ +/** 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) 2009 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +/* fixup_task.c - linked attributes fixup task */ + +#include "linked_attrs.h" + +/* + * Function Prototypes + */ +static void linked_attrs_fixup_task_destructor(Slapi_Task *task); +static void linked_attrs_fixup_task_thread(void *arg); +static void linked_attrs_fixup_links(struct configEntry *config); +static int linked_attrs_remove_backlinks_callback(Slapi_Entry *e, void *callback_data); +static int linked_attrs_add_backlinks_callback(Slapi_Entry *e, void *callback_data); +static const char *fetch_attr(Slapi_Entry *e, const char *attrname, + const char *default_val); + +/* + * Function Implementations + */ +int +linked_attrs_fixup_task_add(Slapi_PBlock *pb, Slapi_Entry *e, + Slapi_Entry *eAfter, int *returncode, + char *returntext, void *arg) +{ + PRThread *thread = NULL; + int rv = SLAPI_DSE_CALLBACK_OK; + task_data *mytaskdata = NULL; + Slapi_Task *task = NULL; + const char *linkdn = NULL; + + *returncode = LDAP_SUCCESS; + /* get arg(s) */ + linkdn = fetch_attr(e, "linkdn", 0); + + /* setup our task data */ + mytaskdata = (task_data*)slapi_ch_calloc(1, sizeof(task_data)); + if (mytaskdata == NULL) { + *returncode = LDAP_OPERATIONS_ERROR; + rv = SLAPI_DSE_CALLBACK_ERROR; + goto out; + } + + if (linkdn) { + mytaskdata->linkdn = slapi_dn_normalize(slapi_ch_strdup(linkdn)); + } + + /* allocate new task now */ + task = slapi_new_task(slapi_entry_get_ndn(e)); + + /* register our destructor for cleaning up our private data */ + slapi_task_set_destructor_fn(task, linked_attrs_fixup_task_destructor); + + /* Stash a pointer to our data in the task */ + slapi_task_set_data(task, mytaskdata); + + /* start the sample task as a separate thread */ + thread = PR_CreateThread(PR_USER_THREAD, linked_attrs_fixup_task_thread, + (void *)task, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, + PR_UNJOINABLE_THREAD, SLAPD_DEFAULT_THREAD_STACKSIZE); + if (thread == NULL) { + slapi_log_error( SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM, + "unable to create task thread!\n"); + *returncode = LDAP_OPERATIONS_ERROR; + rv = SLAPI_DSE_CALLBACK_ERROR; + slapi_task_finish(task, *returncode); + } else { + rv = SLAPI_DSE_CALLBACK_OK; + } + +out: + return rv; +} + +static void +linked_attrs_fixup_task_destructor(Slapi_Task *task) +{ + if (task) { + task_data *mydata = (task_data *)slapi_task_get_data(task); + if (mydata) { + slapi_ch_free_string(&mydata->linkdn); + /* Need to cast to avoid a compiler warning */ + slapi_ch_free((void **)&mydata); + } + } +} + +static void +linked_attrs_fixup_task_thread(void *arg) +{ + int rc = 0; + Slapi_Task *task = (Slapi_Task *)arg; + task_data *td = NULL; + PRCList *main_config = NULL; + int found_config = 0; + + /* Fetch our task data from the task */ + td = (task_data *)slapi_task_get_data(task); + + /* Log started message. */ + slapi_task_begin(task, 1); + slapi_task_log_notice(task, "Linked attributes fixup task starting (link dn: \"%s\") ...\n", + td->linkdn ? td->linkdn : ""); + slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM, + "Syntax validate task starting (link dn: \"%s\") ...\n", + td->linkdn ? td->linkdn : ""); + + linked_attrs_read_lock(); + main_config = linked_attrs_get_config(); + if (!PR_CLIST_IS_EMPTY(main_config)) { + struct configEntry *config_entry = NULL; + PRCList *list = PR_LIST_HEAD(main_config); + + while (list != main_config) { + config_entry = (struct configEntry *) list; + + /* See if this is the requested config and fix up if so. */ + if (td->linkdn) { + if (strcasecmp(td->linkdn, config_entry->dn) == 0) { + found_config = 1; + slapi_task_log_notice(task, "Fixing up linked attribute pair (%s)\n", + config_entry->dn); + slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM, + "Fixing up linked attribute pair (%s)\n", + config_entry->dn); + + linked_attrs_fixup_links(config_entry); + break; + } + } else { + /* No config DN was supplied, so fix up all configured links. */ + slapi_task_log_notice(task, "Fixing up linked attribute pair (%s)\n", + config_entry->dn); + slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM, + "Fixing up linked attribute pair (%s)\n", config_entry->dn); + + linked_attrs_fixup_links(config_entry); + } + + list = PR_NEXT_LINK(list); + } + } + + /* Log a message if we didn't find the requested attribute pair. */ + if (td->linkdn && !found_config) { + slapi_task_log_notice(task, "Requested link config DN not found (%s)\n", + td->linkdn); + slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM, + "Requested link config DN not found (%s)\n", td->linkdn); + } + + linked_attrs_unlock(); + + /* Log finished message. */ + slapi_task_log_notice(task, "Linked attributes fixup task complete.\n"); + slapi_task_log_status(task, "Linked attributes fixup task complete.\n"); + slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM, "Linked attributes fixup task complete.\n"); + slapi_task_inc_progress(task); + + /* this will queue the destruction of the task */ + slapi_task_finish(task, rc); +} + +static void +linked_attrs_fixup_links(struct configEntry *config) +{ + Slapi_PBlock *pb = slapi_pblock_new(); + char *del_filter = NULL; + char *add_filter = NULL; + + del_filter = slapi_ch_smprintf("%s=*", config->managedtype); + add_filter = slapi_ch_smprintf("%s=*", config->linktype); + + /* Lock the attribute pair. */ + slapi_lock_mutex(config->lock); + + if (config->scope) { + /* Find all entries with the managed type present + * within the scope and remove the managed type. */ + slapi_search_internal_set_pb(pb, config->scope, LDAP_SCOPE_SUBTREE, + del_filter, 0, 0, 0, 0, linked_attrs_get_plugin_id(), 0); + + slapi_search_internal_callback_pb(pb, config->managedtype, 0, + linked_attrs_remove_backlinks_callback, 0); + + /* Clean out pblock for reuse. */ + slapi_pblock_init(pb); + + /* Find all entries with the link type present within the + * scope and add backlinks to the entries they point to. */ + slapi_search_internal_set_pb(pb, config->scope, LDAP_SCOPE_SUBTREE, + add_filter, 0, 0, 0, 0, linked_attrs_get_plugin_id(), 0); + + slapi_search_internal_callback_pb(pb, config, 0, + linked_attrs_add_backlinks_callback, 0); + } else { + /* Loop through all non-private backend suffixes and + * remove the managed type from any entry that has it. + * We then find any entry with the linktype present and + * generate the proper backlinks. */ + void *node = NULL; + Slapi_DN * suffix = slapi_get_first_suffix (&node, 0); + + while (suffix) { + slapi_search_internal_set_pb(pb, slapi_sdn_get_dn(suffix), + LDAP_SCOPE_SUBTREE, del_filter, + 0, 0, 0, 0, + linked_attrs_get_plugin_id(), 0); + + slapi_search_internal_callback_pb(pb, config->managedtype, 0, + linked_attrs_remove_backlinks_callback, 0); + + /* Clean out pblock for reuse. */ + slapi_pblock_init(pb); + + slapi_search_internal_set_pb(pb, slapi_sdn_get_dn(suffix), + LDAP_SCOPE_SUBTREE, add_filter, + 0, 0, 0, 0, + linked_attrs_get_plugin_id(), 0); + + slapi_search_internal_callback_pb(pb, config, 0, + linked_attrs_add_backlinks_callback, 0); + + /* Clean out pblock for reuse. */ + slapi_pblock_init(pb); + + suffix = slapi_get_next_suffix (&node, 0); + } + } + + /* Unlock the attribute pair. */ + slapi_unlock_mutex(config->lock); + + slapi_ch_free_string(&del_filter); + slapi_ch_free_string(&add_filter); + slapi_pblock_destroy(pb); +} + +static int +linked_attrs_remove_backlinks_callback(Slapi_Entry *e, void *callback_data) +{ + int rc = 0; + char *dn = slapi_entry_get_dn(e); + char *type = (char *)callback_data; + Slapi_PBlock *pb = slapi_pblock_new(); + char *val[1]; + LDAPMod mod; + LDAPMod *mods[2]; + + /* Remove all values of the passed in type. */ + val[0] = 0; + + mod.mod_op = LDAP_MOD_DELETE; + mod.mod_type = type; + mod.mod_values = val; + + mods[0] = &mod; + mods[1] = 0; + + slapi_log_error(SLAPI_LOG_PLUGIN, LINK_PLUGIN_SUBSYSTEM, + "Removing backpointer attribute (%s) from entry (%s)\n", + type, dn); + + /* Perform the operation. */ + slapi_modify_internal_set_pb(pb, dn, mods, 0, 0, + linked_attrs_get_plugin_id(), 0); + slapi_modify_internal_pb(pb); + + slapi_pblock_destroy(pb); + + return rc; +} + +static int +linked_attrs_add_backlinks_callback(Slapi_Entry *e, void *callback_data) +{ + int rc = 0; + char *linkdn = slapi_entry_get_dn(e); + struct configEntry *config = (struct configEntry *)callback_data; + Slapi_PBlock *pb = slapi_pblock_new(); + int i = 0; + char **targets = NULL; + char *val[2]; + LDAPMod mod; + LDAPMod *mods[2]; + + /* Setup the modify operation. Only the target will + * change, so we only need to do this once. */ + val[0] = linkdn; + val[1] = 0; + + mod.mod_op = LDAP_MOD_ADD; + mod.mod_type = config->managedtype; + mod.mod_values = val; + + mods[0] = &mod; + mods[1] = 0; + + targets = slapi_entry_attr_get_charray(e, config->linktype); + for (i = 0; targets && targets[i]; ++i) { + char *targetdn = (char *)targets[i]; + int perform_update = 0; + + if (config->scope) { + /* Check if the target is within the scope. */ + perform_update = slapi_dn_issuffix(targetdn, config->scope); + } else { + /* Find out the root suffix that the linkdn is in + * and see if the target is in the same backend. */ + Slapi_Backend *be = NULL; + Slapi_DN *linksdn = slapi_sdn_new_dn_byref(linkdn); + Slapi_DN *targetsdn = slapi_sdn_new_dn_byref(targetdn); + + if ((be = slapi_be_select(linksdn))) { + perform_update = slapi_sdn_issuffix(targetsdn, slapi_be_getsuffix(be, 0)); + } + + slapi_sdn_free(&linksdn); + slapi_sdn_free(&targetsdn); + } + + if (perform_update) { + slapi_log_error(SLAPI_LOG_PLUGIN, LINK_PLUGIN_SUBSYSTEM, + "Adding backpointer (%s) in entry (%s)\n", + linkdn, targetdn); + + /* Perform the modify operation. */ + slapi_modify_internal_set_pb(pb, targetdn, mods, 0, 0, + linked_attrs_get_plugin_id(), 0); + slapi_modify_internal_pb(pb); + + /* Initialize the pblock so we can reuse it. */ + slapi_pblock_init(pb); + } + } + + slapi_ch_array_free(targets); + slapi_pblock_destroy(pb); + + return rc; +} + +/* extract a single value from the entry (as a string) -- if it's not in the + * entry, the default will be returned (which can be NULL). + * you do not need to free anything returned by this. + */ +static const char * +fetch_attr(Slapi_Entry *e, const char *attrname, + const char *default_val) +{ +Slapi_Attr *attr; +Slapi_Value *val = NULL; + + if (slapi_entry_attr_find(e, attrname, &attr) != 0) { + return default_val; + } + + slapi_attr_first_value(attr, &val); + + return slapi_value_get_string(val); +} + diff --git a/ldap/servers/plugins/linkedattrs/linked_attrs.c b/ldap/servers/plugins/linkedattrs/linked_attrs.c new file mode 100644 index 00000000..5126e35a --- /dev/null +++ b/ldap/servers/plugins/linkedattrs/linked_attrs.c @@ -0,0 +1,2024 @@ +/** 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) 2009 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +/* + * Linked attributes plug-in + */ +#include "linked_attrs.h" + + +/* + * Plug-in globals + */ +static PRCList *g_link_config = NULL; +static PRCList *g_managed_config_index = NULL; +static PRRWLock *g_config_lock; + +static void *_PluginID = NULL; +static char *_PluginDN = NULL; +static int g_plugin_started = 0; + +static Slapi_PluginDesc pdesc = { LINK_FEATURE_DESC, + PLUGIN_MAGIC_VENDOR_STR, + PRODUCTTEXT, + LINK_PLUGIN_DESC }; + +/* + * Plug-in management functions + */ +int linked_attrs_init(Slapi_PBlock * pb); +static int linked_attrs_start(Slapi_PBlock * pb); +static int linked_attrs_close(Slapi_PBlock * pb); +static int linked_attrs_postop_init(Slapi_PBlock * pb); +static int linked_attrs_internal_postop_init(Slapi_PBlock *pb); + +/* + * Operation callbacks (where the real work is done) + */ +static int linked_attrs_mod_post_op(Slapi_PBlock *pb); +static int linked_attrs_add_post_op(Slapi_PBlock *pb); +static int linked_attrs_del_post_op(Slapi_PBlock *pb); +static int linked_attrs_modrdn_post_op(Slapi_PBlock *pb); +static int linked_attrs_pre_op(Slapi_PBlock *pb, int modop); +static int linked_attrs_mod_pre_op(Slapi_PBlock *pb); +static int linked_attrs_add_pre_op(Slapi_PBlock *pb); + +/* + * Config cache management functions + */ +static int linked_attrs_load_config(); +static void linked_attrs_delete_config(); +static int linked_attrs_parse_config_entry(Slapi_Entry * e, int apply); +static void linked_attrs_insert_config_index(struct configEntry *entry); +static void linked_attrs_free_config_entry(struct configEntry ** entry); + +/* + * helpers + */ +static char *linked_attrs_get_dn(Slapi_PBlock * pb); +static int linked_attrs_dn_is_config(char *dn); +static void linked_attrs_find_config(const char *dn, const char *type, + struct configEntry **config); +static void linked_attrs_find_config_reverse(const char *dn, + const char *type, struct configEntry **config); +static int linked_attrs_config_index_has_type(char *type); +static int linked_attrs_config_exists(struct configEntry *entry); +static int linked_attrs_config_exists_reverse(struct configEntry *entry); +static int linked_attrs_oktodo(Slapi_PBlock *pb); +void linked_attrs_load_array(Slapi_Value **array, Slapi_Attr *attr); +int linked_attrs_compare(const void *a, const void *b); +static void linked_attrs_add_backpointers(char *linkdn, struct configEntry *config, + Slapi_Mod *smod); +static void linked_attrs_del_backpointers(Slapi_PBlock *pb, char *linkdn, + struct configEntry *config, Slapi_Mod *smod); +static void linked_attrs_replace_backpointers(Slapi_PBlock *pb, char *linkdn, + struct configEntry *config, Slapi_Mod *smod); +static void linked_attrs_mod_backpointers(char *linkdn, char *type, char *scope, + int modop, Slapi_ValueSet *targetvals); + +/* + * Config cache locking functions + */ +void +linked_attrs_read_lock() +{ + PR_RWLock_Rlock(g_config_lock); +} + +void +linked_attrs_write_lock() +{ + PR_RWLock_Wlock(g_config_lock); +} + +void +linked_attrs_unlock() +{ + PR_RWLock_Unlock(g_config_lock); +} + + +/* + * Plugin identity functions + */ +void +linked_attrs_set_plugin_id(void *pluginID) +{ + _PluginID = pluginID; +} + +void * +linked_attrs_get_plugin_id() +{ + return _PluginID; +} + +void +linked_attrs_set_plugin_dn(char *pluginDN) +{ + _PluginDN = pluginDN; +} + +char * +linked_attrs_get_plugin_dn() +{ + return _PluginDN; +} + + +/* + * Plug-in initialization functions + */ +int +linked_attrs_init(Slapi_PBlock *pb) +{ + int status = 0; + char *plugin_identity = NULL; + + slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM, + "--> linked_attrs_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); + linked_attrs_set_plugin_id(plugin_identity); + + /* Register callbacks */ + if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, + SLAPI_PLUGIN_VERSION_01) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN, + (void *) linked_attrs_start) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN, + (void *) linked_attrs_close) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, + (void *) &pdesc) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_MODIFY_FN, + (void *) linked_attrs_mod_pre_op) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_ADD_FN, + (void *) linked_attrs_add_pre_op) != 0 || + slapi_register_plugin("internalpostoperation", /* op type */ + 1, /* Enabled */ + "linked_attrs_init", /* this function desc */ + linked_attrs_internal_postop_init, /* init func */ + LINK_INT_POSTOP_DESC, /* plugin desc */ + NULL, /* ? */ + plugin_identity /* access control */ + ) || + slapi_register_plugin("postoperation", /* op type */ + 1, /* Enabled */ + "linked_attrs_init", /* this function desc */ + linked_attrs_postop_init, /* init func for post op */ + LINK_POSTOP_DESC, /* plugin desc */ + NULL, /* ? */ + plugin_identity /* access control */ + ) + ) { + slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM, + "linked_attrs_init: failed to register plugin\n"); + status = -1; + } + + slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM, + "<-- linked_attrs_init\n"); + return status; +} + +static int +linked_attrs_internal_postop_init(Slapi_PBlock *pb) +{ + int status = 0; + + if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, + SLAPI_PLUGIN_VERSION_01) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, + (void *) &pdesc) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_ADD_FN, + (void *) linked_attrs_add_post_op) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_DELETE_FN, + (void *) linked_attrs_del_post_op) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_MODIFY_FN, + (void *) linked_attrs_mod_post_op) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_MODRDN_FN, + (void *) linked_attrs_modrdn_post_op) != 0) { + slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM, + "linked_attrs_internal_postop_init: failed to register plugin\n"); + status = -1; + } + + return status; +} + +static int +linked_attrs_postop_init(Slapi_PBlock *pb) +{ + int status = 0; + + if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, + SLAPI_PLUGIN_VERSION_01) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, + (void *) &pdesc) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_POST_ADD_FN, + (void *) linked_attrs_add_post_op) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_POST_DELETE_FN, + (void *) linked_attrs_del_post_op) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODIFY_FN, + (void *) linked_attrs_mod_post_op) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODRDN_FN, + (void *) linked_attrs_modrdn_post_op) != 0) { + slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM, + "linked_attrs_postop_init: failed to register plugin\n"); + status = -1; + } + + return status; +} + + +/* + * linked_attrs_start() + * + * Creates config lock and loads config cache. + */ +static int +linked_attrs_start(Slapi_PBlock * pb) +{ + char *plugindn = NULL; + + slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM, + "--> linked_attrs_start\n"); + + /* Check if we're already started */ + if (g_plugin_started) { + goto done; + } + + g_config_lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "linkedattrs"); + + if (!g_config_lock) { + slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM, + "linked_attrs_start: lock creation failed\n"); + + return -1; + } + + /* + * Get the plug-in target dn from the system + * and store it for future use. */ + slapi_pblock_get(pb, SLAPI_TARGET_DN, &plugindn); + if (NULL == plugindn || 0 == strlen(plugindn)) { + slapi_log_error(SLAPI_LOG_PLUGIN, LINK_PLUGIN_SUBSYSTEM, + "linked_attrs_start: unable to retrieve plugin dn\n"); + return -1; + } + + linked_attrs_set_plugin_dn(plugindn); + + /* + * Load the config cache + */ + g_link_config = (PRCList *)slapi_ch_calloc(1, sizeof(struct configEntry)); + PR_INIT_CLIST(g_link_config); + g_managed_config_index = (PRCList *)slapi_ch_calloc(1, sizeof(struct configIndex)); + PR_INIT_CLIST(g_managed_config_index); + + if (linked_attrs_load_config() != 0) { + slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM, + "linked_attrs_start: unable to load plug-in configuration\n"); + return -1; + } + + /* + * Register our task callback + */ + slapi_task_register_handler("fixup linked attributes", linked_attrs_fixup_task_add); + + g_plugin_started = 1; + slapi_log_error(SLAPI_LOG_PLUGIN, LINK_PLUGIN_SUBSYSTEM, + "linked attributes plug-in: ready for service\n"); + slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM, + "<-- linked_attrs_start\n"); + +done: + return 0; +} + +/* + * linked_attrs_close() + * + * Cleans up the config cache. + */ +static int +linked_attrs_close(Slapi_PBlock * pb) +{ + slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM, + "--> linked_attrs_close\n"); + + linked_attrs_delete_config(); + + slapi_ch_free((void **)&g_link_config); + slapi_ch_free((void **)&g_managed_config_index); + + slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM, + "<-- linked_attrs_close\n"); + + return 0; +} + +PRCList * +linked_attrs_get_config() +{ + return g_link_config; +} + +/* + * config looks like this + * - cn=myplugin + * --- cn=manager link + * --- cn=owner link + * --- cn=etc + */ +static int +linked_attrs_load_config() +{ + int status = 0; + int result; + int i; + Slapi_PBlock *search_pb; + Slapi_Entry **entries = NULL; + + slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM, + "--> linked_attrs_load_config\n"); + + /* Clear out any old config. */ + linked_attrs_write_lock(); + linked_attrs_delete_config(); + + /* Find the config entries beneath our plugin entry. */ + search_pb = slapi_pblock_new(); + slapi_search_internal_set_pb(search_pb, linked_attrs_get_plugin_dn(), + LDAP_SCOPE_SUBTREE, "objectclass=*", + NULL, 0, NULL, NULL, linked_attrs_get_plugin_id(), 0); + slapi_search_internal_pb(search_pb); + slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &result); + + if (LDAP_SUCCESS != result) { + status = -1; + goto cleanup; + } + + slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, + &entries); + if (NULL == entries || NULL == entries[0]) { + /* If there are no config entries, we're done. */ + goto cleanup; + } + + /* Loop through all of the entries we found and parse them. */ + for (i = 0; (entries[i] != NULL); i++) { + /* We don't care about the status here because we may have + * some invalid config entries, but we just want to continue + * looking for valid ones. */ + linked_attrs_parse_config_entry(entries[i], 1); + } + + cleanup: + slapi_free_search_results_internal(search_pb); + slapi_pblock_destroy(search_pb); + linked_attrs_unlock(); + slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM, + "<-- linked_attrs_load_config\n"); + + return status; +} + +/* + * linked_attrs_parse_config_entry() + * + * Parses a single config entry. If apply is non-zero, then + * we will load and start using the new config. You can simply + * validate config without making any changes by setting apply + * to 0. + * + * Returns 0 if the entry is valid and -1 if it is invalid. + */ +static int +linked_attrs_parse_config_entry(Slapi_Entry * e, int apply) +{ + char *value; + struct configEntry *entry = NULL; + struct configEntry *config_entry; + PRCList *list; + int entry_added = 0; + int ret = 0; + + slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM, + "--> linked_attrs_parse_config_entry\n"); + + /* If this is the main plug-in + * config entry, just bail. */ + if (strcasecmp(linked_attrs_get_plugin_dn(), slapi_entry_get_ndn(e)) == 0) { + ret = -1; + goto bail; + } + + entry = (struct configEntry *)slapi_ch_calloc(1, sizeof(struct configEntry)); + if (NULL == entry) { + ret = -1; + goto bail; + } + + value = slapi_entry_get_ndn(e); + if (value) { + entry->dn = slapi_ch_strdup(value); + } else { + slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM, + "linked_attrs_parse_config_entry: Error " + "reading dn from config entry\n"); + ret = -1; + goto bail; + } + + slapi_log_error(SLAPI_LOG_CONFIG, LINK_PLUGIN_SUBSYSTEM, + "----------> dn [%s]\n", entry->dn); + + value = slapi_entry_attr_get_charptr(e, LINK_LINK_TYPE); + if (value) { + int not_dn_syntax = 0; + char *syntaxoid = NULL; + Slapi_Attr *attr = slapi_attr_new(); + + /* Set this first so we free it if we encounter an error */ + entry->linktype = value; + + /* Gather some information about this attribute. */ + slapi_attr_init(attr, value); + slapi_attr_get_syntax_oid_copy(attr, &syntaxoid ); + not_dn_syntax = strcmp(syntaxoid, DN_SYNTAX_OID); + slapi_ch_free_string(&syntaxoid); + slapi_attr_free(&attr); + + /* Check if the link type's syntax is Distinguished Name. + * We only treat this as a warning. */ + if (not_dn_syntax) { + slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM, + "linked_attrs_parse_config_entry: The %s config " + "setting must be set to an attribute with the " + "Distinguished Name syntax for linked attribute " + "pair \"%s\".\n", LINK_LINK_TYPE, entry->dn); + } + } else { + slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM, + "linked_attrs_parse_config_entry: The %s config " + "setting is required for linked attribute pair \"%s\".\n", + LINK_LINK_TYPE, entry->dn); + ret = -1; + goto bail; + } + + slapi_log_error(SLAPI_LOG_CONFIG, LINK_PLUGIN_SUBSYSTEM, + "----------> %s [%s]\n", LINK_LINK_TYPE, entry->linktype); + + value = slapi_entry_attr_get_charptr(e, LINK_MANAGED_TYPE); + if (value) { + int single_valued = 0; + int not_dn_syntax = 0; + char *syntaxoid = NULL; + Slapi_Attr *attr = slapi_attr_new(); + + /* Set this first so we free it if we encounter an error */ + entry->managedtype = value; + + /* Gather some information about this attribute. */ + slapi_attr_init(attr, value); + slapi_attr_get_syntax_oid_copy(attr, &syntaxoid ); + not_dn_syntax = strcmp(syntaxoid, DN_SYNTAX_OID); + single_valued = slapi_attr_flag_is_set(attr, SLAPI_ATTR_FLAG_SINGLE); + slapi_ch_free_string(&syntaxoid); + slapi_attr_free(&attr); + + /* Ensure that the managed type is a multi-valued attribute. */ + if (single_valued) { + slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM, + "linked_attrs_parse_config_entry: The %s config " + "setting must be set to a multi-valued attribute " + "for linked attribute pair \"%s\".\n", + LINK_MANAGED_TYPE, entry->dn); + ret = -1; + goto bail; + /* Check if the link type's syntax is Distinguished Name. + * We only treat this as a warning. */ + } else if (not_dn_syntax) { + slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM, + "linked_attrs_parse_config_entry: The %s config " + "setting must be set to an attribute with the " + "Distinguished Name syntax for linked attribute " + "pair \"%s\".\n", LINK_MANAGED_TYPE, entry->dn); + } + } else { + slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM, + "linked_attrs_parse_config_entry: The %s config " + "setting is required for linked attribute pair \"%s\".\n", + LINK_MANAGED_TYPE, entry->dn); + ret = -1; + goto bail; + } + + slapi_log_error(SLAPI_LOG_CONFIG, LINK_PLUGIN_SUBSYSTEM, + "----------> %s [%s]\n", LINK_MANAGED_TYPE, + entry->managedtype); + + /* A scope is not required. No scope means it + * applies to any part of the DIT. */ + value = slapi_entry_attr_get_charptr(e, LINK_SCOPE); + if (value) { + entry->scope = value; + } + + slapi_log_error(SLAPI_LOG_CONFIG, LINK_PLUGIN_SUBSYSTEM, + "----------> %s [%s]\n", LINK_SCOPE, + entry->scope ? entry->scope : "NULL"); + + /* Check if config already exists for + * the link type at the same scope. */ + if (linked_attrs_config_exists(entry)) { + slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM, + "linked_attrs_parse_config_entry: A config " + "entry for the link attribute %s already " + "exists at a scope of \"%s\".\n", entry->linktype, + entry->scope); + ret = -1; + goto bail; + } + + /* Check if config already exists for + * the managed type at the same scope. */ + if (linked_attrs_config_exists_reverse(entry)) { + slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM, + "linked_attrs_parse_config_entry: A config " + "entry for the managed attribute %s already " + "exists at a scope of \"%s\".\n", entry->managedtype, + entry->scope); + ret = -1; + goto bail; + } + + /* If we were only called to validate config, we can + * just bail out before applying the config changes */ + if (apply == 0) { + goto bail; + } + + /* Create a lock for this attribute pair. */ + entry->lock = slapi_new_mutex(); + if (!entry->lock) { + slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM, + "linked_attrs_parse_config_entry: Unable to create " + "lock for linked attribute pair \"%s\".\n", entry->dn); + ret = -1; + goto bail; + } + + /* Add the entry to the list. We group by link type. We + * also maintain a reverse list grouped by managed type. */ + if (!PR_CLIST_IS_EMPTY(g_link_config)) { + list = PR_LIST_HEAD(g_link_config); + while (list != g_link_config) { + config_entry = (struct configEntry *) list; + + /* See if the types match. We want to group + * entries for the same link type together. */ + if (slapi_attr_type_cmp(config_entry->linktype, entry->linktype, 1) == 0) { + PR_INSERT_BEFORE(&(entry->list), list); + slapi_log_error(SLAPI_LOG_CONFIG, LINK_PLUGIN_SUBSYSTEM, + "store [%s] before [%s] \n", entry->dn, + config_entry->dn); + + /* add to managed type index */ + linked_attrs_insert_config_index(entry); + + entry_added = 1; + break; + } + + list = PR_NEXT_LINK(list); + + if (g_link_config == list) { + /* add to tail */ + PR_INSERT_BEFORE(&(entry->list), list); + slapi_log_error(SLAPI_LOG_CONFIG, LINK_PLUGIN_SUBSYSTEM, + "store [%s] at tail\n", entry->dn); + + /* add to managed type index */ + linked_attrs_insert_config_index(entry); + + entry_added = 1; + break; + } + } + } else { + /* first entry */ + PR_INSERT_LINK(&(entry->list), g_link_config); + slapi_log_error(SLAPI_LOG_CONFIG, LINK_PLUGIN_SUBSYSTEM, + "store [%s] at head \n", entry->dn); + + /* add to managed type index */ + linked_attrs_insert_config_index(entry); + + entry_added = 1; + } + + bail: + if (0 == entry_added) { + /* Don't log error if we weren't asked to apply config */ + if ((apply != 0) && (entry != NULL)) { + slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM, + "linked_attrs_parse_config_entry: Invalid config entry " + "[%s] skipped\n", entry->dn); + } + linked_attrs_free_config_entry(&entry); + } else { + ret = 0; + } + + slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM, + "<-- linked_attrs_parse_config_entry\n"); + + return ret; +} + +/* + * linked_attrs_insert_config_index() + * + * Adds an entry to the ordered config index. We maintain + * an list of pointers to the cached config entries that is + * grouped by managed type. We use this index to find the + * appropriate config entry when given a backpointer. This + * is useful for the case where an entry with backpointers + * is renamed and we need to updated the forward link. + */ +static void +linked_attrs_insert_config_index(struct configEntry *entry) +{ + struct configEntry *config_entry = NULL; + struct configIndex *index_entry = NULL; + PRCList *list = PR_LIST_HEAD(g_managed_config_index); + + index_entry = (struct configIndex *)slapi_ch_calloc(1, sizeof(struct configIndex)); + index_entry->config = entry; + + if (!PR_CLIST_IS_EMPTY(g_managed_config_index)) { + while (list != g_managed_config_index) { + config_entry = ((struct configIndex *)list)->config; + + /* See if the types match. */ + if (slapi_attr_type_cmp(config_entry->managedtype, entry->managedtype, 1) == 0) { + PR_INSERT_BEFORE(&(index_entry->list), list); + slapi_log_error(SLAPI_LOG_CONFIG, LINK_PLUGIN_SUBSYSTEM, + "store [%s] before [%s] \n", entry->dn, + config_entry->dn); + break; + } + + list = PR_NEXT_LINK(list); + + if (g_managed_config_index == list) { + /* add to tail */ + PR_INSERT_BEFORE(&(index_entry->list), list); + slapi_log_error(SLAPI_LOG_CONFIG, LINK_PLUGIN_SUBSYSTEM, + "store [%s] at tail\n", entry->dn); + break; + } + } + } else { + /* first entry */ + slapi_log_error(SLAPI_LOG_CONFIG, LINK_PLUGIN_SUBSYSTEM, + "store [%s] at head \n", entry->dn); + PR_INSERT_LINK(&(index_entry->list), g_managed_config_index); + } +} + +static void +linked_attrs_free_config_entry(struct configEntry ** entry) +{ + struct configEntry *e = *entry; + + if (e == NULL) + return; + + if (e->dn) { + slapi_log_error(SLAPI_LOG_CONFIG, LINK_PLUGIN_SUBSYSTEM, + "freeing config entry [%s]\n", e->dn); + slapi_ch_free_string(&e->dn); + } + + if (e->linktype) + slapi_ch_free_string(&e->linktype); + + if (e->managedtype) + slapi_ch_free_string(&e->managedtype); + + if (e->scope) + slapi_ch_free_string(&e->scope); + + if (e->lock) + slapi_destroy_mutex(e->lock); + + slapi_ch_free((void **) entry); +} + +static void +linked_attrs_delete_configEntry(PRCList *entry) +{ + PR_REMOVE_LINK(entry); + linked_attrs_free_config_entry((struct configEntry **) &entry); +} + +static void +linked_attrs_delete_config() +{ + PRCList *list; + + /* Delete the config cache. */ + while (!PR_CLIST_IS_EMPTY(g_link_config)) { + list = PR_LIST_HEAD(g_link_config); + linked_attrs_delete_configEntry(list); + } + + /* Delete the reverse index. */ + while (!PR_CLIST_IS_EMPTY(g_managed_config_index)) { + list = PR_LIST_HEAD(g_managed_config_index); + PR_REMOVE_LINK(list); + slapi_ch_free((void **)&list); + } + + return; +} + + +/* + * Helper functions + */ +static char * +linked_attrs_get_dn(Slapi_PBlock * pb) +{ + char *dn = 0; + slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM, + "--> linked_attrs_get_dn\n"); + + if (slapi_pblock_get(pb, SLAPI_TARGET_DN, &dn)) { + slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM, + "linked_attrs_get_dn: failed to get dn of changed entry"); + goto bail; + } + + bail: + slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM, + "<-- linked_attrs_get_dn\n"); + + return dn; +} + +/* + * linked_attrs_dn_is_config() + * + * Checks if dn is a linked attribute config entry. + */ +static int +linked_attrs_dn_is_config(char *dn) +{ + int ret = 0; + + slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM, + "--> linked_attrs_dn_is_config\n"); + + /* Return 1 if the passed in dn is a child of the main + * plugin config entry. */ + if (slapi_dn_issuffix(dn, linked_attrs_get_plugin_dn()) && + strcasecmp(dn, linked_attrs_get_plugin_dn())) { + ret = 1; + } + + slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM, + "<-- linked_attrs_dn_is_config\n"); + + return ret; +} + +/* + * linked_attrs_find_config() + * + * Finds the appropriate config entry for a given dn and + * link type. A read lock must be held on the config + * before calling this function. The configEntry that is + * returned is a pointer to the actual config entry in + * the config cache. It should not be modified in any + * way. The read lock should not be released until you + * are finished with the config entry that is returned. + + * Returns NULL if no applicable config entry is found. + */ +static void +linked_attrs_find_config(const char *dn, + const char *type, struct configEntry **config) +{ + int found_type = 0; + PRCList *list = NULL; + + *config = NULL; + + if (!PR_CLIST_IS_EMPTY(g_link_config)) { + list = PR_LIST_HEAD(g_link_config); + while (list != g_link_config) { + if (slapi_attr_type_cmp(((struct configEntry *)list)->linktype, + type, 1) == 0) { + /* Set a flag indicating that we found a config entry + * for this type. We use this flag so we can stop + * processing early if we don't find a matching scope. */ + found_type = 1; + + /* Check if the dn is in the scope of this config + * entry. If the config entry doesn't have a scope + * (global), consider it a match. If we have a match, + * we can stop processing the config. */ + if ((((struct configEntry *)list)->scope == NULL) || + (slapi_dn_issuffix(dn, ((struct configEntry *)list)->scope))) { + *config = (struct configEntry *)list; + break; + } + } else { + /* If flag is set, we're done. We have configured links + * for this type, but none of the scopes match. */ + if (found_type) { + break; + } + } + + list = PR_NEXT_LINK(list); + } + } +} + +/* + * linked_attrs_find_config_reverse() + * + * Finds the appropriate config entry for a given dn and + * managed type. A read lock must be held on the config + * before calling this function. The configEntry that is + * returned is a pointer to the actual config entry in + * the config cache. It should not be modified in any + * way. The read lock should not be released until you + * are finished with the config entry that is returned. + + * Returns NULL if no applicable config entry is found. + */ +static void +linked_attrs_find_config_reverse(const char *dn, + const char *type, struct configEntry **config) +{ + int found_type = 0; + PRCList *list = NULL; + + *config = NULL; + + if (!PR_CLIST_IS_EMPTY(g_managed_config_index)) { + list = PR_LIST_HEAD(g_managed_config_index); + while (list != g_managed_config_index) { + if (slapi_attr_type_cmp(((struct configIndex *)list)->config->managedtype, + type, 1) == 0) { + /* Set a flag indicating that we found a config entry + * for this type. We use this flag so we can stop + * processing early if we don't find a matching scope. */ + found_type = 1; + + /* Check if the dn is in the scope of this config + * entry. If the config entry doesn't have a scope + * (global), consider it a match. If we have a match, + * we can stop processing the config. */ + if ((((struct configIndex *)list)->config->scope == NULL) || + (slapi_dn_issuffix(dn, ((struct configIndex *)list)->config->scope))) { + *config = ((struct configIndex *)list)->config; + break; + } + } else { + /* If flag is set, we're done. We have configured links + * for this type, but none of the scopes match. */ + if (found_type) { + break; + } + } + + list = PR_NEXT_LINK(list); + } + } +} + +/* + * linked_attrs_config_index_has_type() + * + * Returns 1 if a config entry exists with the passed + * in managed type. + * + * A read lock on the config must be held before calling + * this function. + */ +static int +linked_attrs_config_index_has_type(char *type) +{ + int rc = 0; + PRCList *list = NULL; + + if (!PR_CLIST_IS_EMPTY(g_managed_config_index)) { + list = PR_LIST_HEAD(g_managed_config_index); + while (list != g_managed_config_index) { + if (slapi_attr_type_cmp(((struct configIndex *)list)->config->managedtype, + type, 1) == 0) { + rc = 1; + break; + } + + list = PR_NEXT_LINK(list); + } + } + + return rc; +} + +/* + * linked_attrs_config_exists() + * + * Returns 1 if a config entry exists in the cache + * already for the given link type at the given scope. + * This will detect if the cached config entry is really + * the same one as the passed in entry by comparing the + * dn of the config entry. We will still return 0 in + * this case as it's one and the same config entry. We + * really want to use this to prevent multiple config + * entries for the same link type at the same scope. + * + * A read lock on the config must be held before calling + * this function. + */ +static int +linked_attrs_config_exists(struct configEntry *entry) +{ + int rc = 0; + int found_type = 0; + PRCList *list = NULL; + + if (!PR_CLIST_IS_EMPTY(g_link_config)) { + list = PR_LIST_HEAD(g_link_config); + while (list != g_link_config) { + if (slapi_attr_type_cmp(((struct configEntry *)list)->linktype, + entry->linktype, 1) == 0) { + found_type = 1; + /* We don't allow nested config for the same type. We + * need to check for nesting in both directions here. + * If no scope is set, we consider the entry global. */ + if ((((struct configEntry *)list)->scope == NULL) || + slapi_dn_issuffix(entry->scope, ((struct configEntry *)list)->scope) || + slapi_dn_issuffix(((struct configEntry *)list)->scope, entry->scope)) { + /* Make sure that this isn't the same exact entry + * in the list already. This can happen if a config + * entry is being modified. Both of these were already + * normalized when the config struct was filled in. */ + if (strcasecmp(entry->dn, ((struct configEntry *)list)->dn) != 0) { + rc = 1; + break; + } + } + } else { + if (found_type) { + /* Since the list is sorted by link type, we + * are finished if we get here since we found + * the type but didn't match the scope. */ + break; + } + } + + list = PR_NEXT_LINK(list); + } + } + + return rc; +} + +/* + * linked_attrs_config_exists_reverse() + * + * Returns 1 if a config entry exists in the cache + * already for the given managed type at the given scope. + * This will detect if the cached config entry is really + * the same one as the passed in entry by comparing the + * dn of the config entry. We will still return 0 in + * this case as it's one and the same config entry. We + * really want to use this to prevent multiple config + * entries for the same managed type at the same scope. + * + * A read lock on the config must be held before calling + * this function. + */ +static int +linked_attrs_config_exists_reverse(struct configEntry *entry) +{ + int rc = 0; + int found_type = 0; + PRCList *list = NULL; + + if (!PR_CLIST_IS_EMPTY(g_managed_config_index)) { + list = PR_LIST_HEAD(g_managed_config_index); + while (list != g_managed_config_index) { + if (slapi_attr_type_cmp(((struct configIndex *)list)->config->managedtype, + entry->managedtype, 1) == 0) { + found_type = 1; + /* We don't allow nested config for the same type. We + * need to check for nesting in both directions here. */ + if ((((struct configIndex *)list)->config->scope == NULL) || + slapi_dn_issuffix(entry->scope, + ((struct configIndex *)list)->config->scope) || + slapi_dn_issuffix(((struct configIndex *)list)->config->scope, + entry->scope)) { + /* Make sure that this isn't the same exact entry + * in the list already. This can happen if a config + * entry is being modified. Both of these were already + * normalized when the config struct was filled in. */ + if (strcasecmp(entry->dn, ((struct configIndex *)list)->config->dn) != 0) { + rc = 1; + break; + } + } + } else { + if (found_type) { + /* Since the list is sorted by link type, we + * are finished if we get here since we found + * the type but didn't match the scope. */ + break; + } + } + + list = PR_NEXT_LINK(list); + } + } + + return rc; +} + +/* + * linked_attrs_oktodo() + * + * Check if we want to process this operation. We need to be + * sure that the operation succeeded. We also respond to replicated + * ops so we don't test for that. This does require that the managed + * attributes not be replicated. + */ +static int +linked_attrs_oktodo(Slapi_PBlock *pb) +{ + int ret = 1; + int oprc = 0; + + slapi_log_error( SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM, + "--> linked_attrs_oktodo\n" ); + + if(slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &oprc) != 0) + { + slapi_log_error( SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM, + "linked_attrs_oktodo: could not get parameters\n" ); + ret = -1; + } + + /* This plugin should only execute if the operation succeeded. */ + if(oprc != 0) + { + ret = 0; + } + + slapi_log_error( SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM, + "<-- linked_attrs_oktodo\n" ); + + return ret; +} + +/* linked_attrs_load_array() + * + * put attribute values in array structure + */ +void +linked_attrs_load_array(Slapi_Value **array, Slapi_Attr *attr) +{ + Slapi_Value *val = 0; + int hint = slapi_attr_first_value(attr, &val); + + while(val) + { + *array = val; + array++; + hint = slapi_attr_next_value(attr, hint, &val); + } +} + +/* linked_attrs_compare() + * + * Compare two attr values using the DN syntax. + */ +int +linked_attrs_compare(const void *a, const void *b) +{ + int rc = 0; + Slapi_Value *val1 = *((Slapi_Value **)a); + Slapi_Value *val2 = *((Slapi_Value **)b); + Slapi_Attr *linkattr = slapi_attr_new(); + + slapi_attr_init(linkattr, "distinguishedName"); + + rc = slapi_attr_value_cmp(linkattr, + slapi_value_get_berval(val1), + slapi_value_get_berval(val2)); + + slapi_attr_free(&linkattr); + + return rc; +} + +/* + * linked_attrs_add_backpointers() + * + * Adds backpointers pointing to dn to the entries referred to + * by the values in smod. + */ +static void +linked_attrs_add_backpointers(char *linkdn, struct configEntry *config, + Slapi_Mod *smod) +{ + Slapi_ValueSet *vals = slapi_valueset_new(); + + slapi_valueset_set_from_smod(vals, smod); + linked_attrs_mod_backpointers(linkdn, config->managedtype, config->scope, + LDAP_MOD_ADD, vals); + + slapi_valueset_free(vals); +} + +/* + * linked_attrs_del_backpointers() + * + * Remove backpointers pointing to linkdn in the entries referred + * to by the values in smod. + */ +static void +linked_attrs_del_backpointers(Slapi_PBlock *pb, char *linkdn, + struct configEntry *config, Slapi_Mod *smod) +{ + Slapi_ValueSet *vals = NULL; + + /* If no values are listed in the smod, we need to get + * a list of all of the values that were deleted by + * looking at the pre-op copy of the entry. */ + if (slapi_mod_get_num_values(smod) == 0) { + Slapi_Entry *pre_e = NULL; + Slapi_Attr *pre_attr = NULL; + + slapi_pblock_get( pb, SLAPI_ENTRY_PRE_OP, &pre_e ); + slapi_entry_attr_find( pre_e, config->linktype, &pre_attr ); + slapi_attr_get_valueset(pre_attr, &vals); + } else { + vals = slapi_valueset_new(); + slapi_valueset_set_from_smod(vals, smod); + } + + linked_attrs_mod_backpointers(linkdn, config->managedtype, config->scope, + LDAP_MOD_DELETE, vals); + + slapi_valueset_free(vals); +} + +/* + * linked_attrs_replace_backpointers() + * + * Remove backpointers pointing to linkdn from the entries + * whose values were deleted in smod and add backpointers + * for any new values that were added as a part of the + * replace operation. + */ +static void +linked_attrs_replace_backpointers(Slapi_PBlock *pb, char *linkdn, + struct configEntry *config, Slapi_Mod *smod) +{ + Slapi_Entry *pre_e = NULL; + Slapi_Entry *post_e = NULL; + Slapi_Attr *pre_attr = 0; + Slapi_Attr *post_attr = 0; + + /* Get the pre and post copy of the entry to see + * what values have been added and removed. */ + slapi_pblock_get(pb, SLAPI_ENTRY_PRE_OP, &pre_e); + slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &post_e); + + if(pre_e && post_e) { + slapi_entry_attr_find(pre_e, config->linktype, &pre_attr); + slapi_entry_attr_find(post_e, config->linktype, &post_attr); + } + + if(pre_attr || post_attr) { + int pre_total = 0; + int post_total = 0; + Slapi_Value **pre_array = 0; + Slapi_Value **post_array = 0; + int pre_index = 0; + int post_index = 0; + Slapi_ValueSet *addvals = NULL; + Slapi_ValueSet *delvals = NULL; + + /* create arrays of values */ + if(pre_attr) { + slapi_attr_get_numvalues(pre_attr, &pre_total); + } + + if(post_attr) { + slapi_attr_get_numvalues(post_attr, &post_total); + } + + if(pre_total) { + pre_array = (Slapi_Value**) slapi_ch_malloc(sizeof(Slapi_Value*)*pre_total); + linked_attrs_load_array(pre_array, pre_attr); + qsort(pre_array, pre_total, sizeof(Slapi_Value*), linked_attrs_compare); + } + + if(post_total) { + post_array = (Slapi_Value**) slapi_ch_malloc(sizeof(Slapi_Value*)*post_total); + linked_attrs_load_array(post_array, post_attr); + qsort(post_array, post_total, sizeof(Slapi_Value*), linked_attrs_compare); + } + + /* Work through arrays, following these rules: + * - in pre, in post, do nothing + * - in pre, not in post, delete from entry + * - not in pre, in post, add to entry + */ + while(pre_index < pre_total || post_index < post_total) { + if(pre_index == pre_total) { + /* add the rest of post */ + if (addvals == NULL) { + addvals = slapi_valueset_new(); + } + + slapi_valueset_add_value(addvals, post_array[post_index]); + post_index++; + } else if(post_index == post_total) { + /* delete the rest of pre */ + if (delvals == NULL) { + delvals = slapi_valueset_new(); + } + + slapi_valueset_add_value(delvals, pre_array[pre_index]); + pre_index++; + } else { + /* decide what to do */ + int cmp = linked_attrs_compare(&(pre_array[pre_index]), + &(post_array[post_index])); + + if(cmp < 0) { + /* delete pre array */ + if (delvals == NULL) { + delvals = slapi_valueset_new(); + } + + slapi_valueset_add_value(delvals, pre_array[pre_index]); + pre_index++; + } else if(cmp > 0) { + /* add post array */ + if (addvals == NULL) { + addvals = slapi_valueset_new(); + } + + slapi_valueset_add_value(addvals, post_array[post_index]); + post_index++; + } else { + /* do nothing, advance */ + pre_index++; + post_index++; + } + } + } + + /* Perform the actual updates to the target entries. */ + if (delvals) { + linked_attrs_mod_backpointers(linkdn, config->managedtype, + config->scope, LDAP_MOD_DELETE, delvals); + slapi_valueset_free(delvals); + } + + if (addvals) { + linked_attrs_mod_backpointers(linkdn, config->managedtype, + config->scope, LDAP_MOD_ADD, addvals); + slapi_valueset_free(addvals); + } + + slapi_ch_free((void **)&pre_array); + slapi_ch_free((void **)&post_array); + } +} + +/* + * linked_attrs_mod_backpointers() + * + * Performs backpointer management. + */ +static void +linked_attrs_mod_backpointers(char *linkdn, char *type, + char *scope, int modop, Slapi_ValueSet *targetvals) +{ + char *val[2]; + int i = 0; + Slapi_PBlock *mod_pb = slapi_pblock_new(); + LDAPMod mod; + LDAPMod *mods[2]; + Slapi_Value *targetval = NULL; + + /* Setup the modify operation. Only the target will + * change, so we only need to do this once. */ + val[0] = linkdn; + val[1] = 0; + + mod.mod_op = modop; + mod.mod_type = type; + mod.mod_values = val; + + mods[0] = &mod; + mods[1] = 0; + + i = slapi_valueset_first_value(targetvals, &targetval); + while(targetval) + { + int perform_update = 0; + const char *targetdn = slapi_value_get_string(targetval); + + /* If we have a scope, only update the target if it is within + * the scope. If we don't have a scope, only update the target + * if it is in the same backend as the linkdn. */ + if (scope) { + perform_update = slapi_dn_issuffix(targetdn, scope); + } else { + Slapi_Backend *be = NULL; + Slapi_DN *linksdn = slapi_sdn_new_dn_byref(linkdn); + Slapi_DN *targetsdn = slapi_sdn_new_dn_byref(targetdn); + + if ((be = slapi_be_select(linksdn))) { + perform_update = slapi_sdn_issuffix(targetsdn, slapi_be_getsuffix(be, 0)); + } + + slapi_sdn_free(&linksdn); + slapi_sdn_free(&targetsdn); + } + + if (perform_update) { + slapi_log_error(SLAPI_LOG_PLUGIN, LINK_PLUGIN_SUBSYSTEM, + "%s backpointer (%s) in entry (%s)\n", + (modop == LDAP_MOD_ADD) ? "Adding" : "Removing", + linkdn, targetdn); + + /* Perform the modify operation. */ + slapi_modify_internal_set_pb(mod_pb, targetdn, mods, 0, 0, + linked_attrs_get_plugin_id(), 0); + slapi_modify_internal_pb(mod_pb); + + /* Initialize the pblock so we can reuse it. */ + slapi_pblock_init(mod_pb); + } + + i = slapi_valueset_next_value(targetvals, i, &targetval); + } + + slapi_pblock_destroy(mod_pb); +} + + +/* + * Operation callback functions + */ + +/* + * linked_attrs_pre_op() + * + * Checks if an operation is modifying the linked + * attribute config and validates the config changes. + */ +static int +linked_attrs_pre_op(Slapi_PBlock * pb, int modop) +{ + char *dn = 0; + Slapi_Entry *e = 0; + Slapi_Mods *smods = 0; + LDAPMod **mods; + int free_entry = 0; + char *errstr = NULL; + int ret = 0; + + slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM, + "--> linked_attrs_pre_op\n"); + + /* Just bail if we aren't ready to service requests yet. */ + if (!g_plugin_started) + goto bail; + + if (0 == (dn = linked_attrs_get_dn(pb))) + goto bail; + + if (linked_attrs_dn_is_config(dn)) { + /* Validate config changes, but don't apply them. + * This allows us to reject invalid config changes + * here at the pre-op stage. Applying the config + * needs to be done at the post-op stage. */ + + if (LDAP_CHANGETYPE_ADD == modop) { + slapi_pblock_get(pb, SLAPI_ADD_ENTRY, &e); + } else { + /* Fetch the entry being modified so we can + * create the resulting entry for validation. */ + Slapi_DN *tmp_dn = slapi_sdn_new_dn_byref(dn); + if (tmp_dn) { + slapi_search_internal_get_entry(tmp_dn, 0, &e, linked_attrs_get_plugin_id()); + slapi_sdn_free(&tmp_dn); + free_entry = 1; + } + + /* If the entry doesn't exist, just bail and + * let the server handle it. */ + if (e == NULL) { + goto bail; + } + + /* Grab the mods. */ + slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods); + smods = slapi_mods_new(); + slapi_mods_init_passin(smods, mods); + + /* Apply the mods to create the resulting entry. */ + if (mods && (slapi_entry_apply_mods(e, mods) != LDAP_SUCCESS)) { + /* The mods don't apply cleanly, so we just let this op go + * to let the main server handle it. */ + goto bailmod; + } + } + + if (linked_attrs_parse_config_entry(e, 0) != 0) { + /* Refuse the operation if config parsing failed. */ + ret = LDAP_UNWILLING_TO_PERFORM; + if (LDAP_CHANGETYPE_ADD == modop) { + errstr = slapi_ch_smprintf("Not a valid linked attribute configuration entry."); + } else { + errstr = slapi_ch_smprintf("Changes result in an invalid " + "linked attribute configuration."); + } + } + + bailmod: + /* Clean up smods. */ + if (LDAP_CHANGETYPE_MODIFY == modop) { + mods = slapi_mods_get_ldapmods_passout(smods); + slapi_pblock_set(pb, SLAPI_MODIFY_MODS, mods); + slapi_mods_free(&smods); + } + } + + bail: + if (free_entry && e) + slapi_entry_free(e); + + if (ret) { + slapi_log_error(SLAPI_LOG_PLUGIN, LINK_PLUGIN_SUBSYSTEM, + "linked_attrs_pre_op: operation failure [%d]\n", ret); + slapi_send_ldap_result(pb, ret, NULL, errstr, 0, NULL); + slapi_ch_free((void **)&errstr); + ret = -1; + } + + slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM, + "<-- linked_attrs_pre_op\n"); + + return ret; +} + +static int +linked_attrs_add_pre_op(Slapi_PBlock * pb) +{ + return linked_attrs_pre_op(pb, LDAP_CHANGETYPE_ADD); +} + +static int +linked_attrs_mod_pre_op(Slapi_PBlock * pb) +{ + return linked_attrs_pre_op(pb, LDAP_CHANGETYPE_MODIFY); +} + +static int +linked_attrs_mod_post_op(Slapi_PBlock *pb) +{ + Slapi_Mods *smods = NULL; + Slapi_Mod *smod = NULL; + LDAPMod **mods; + Slapi_Mod *next_mod = NULL; + char *dn = NULL; + struct configEntry *config = NULL; + void *caller_id = NULL; + + slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM, + "--> linked_attrs_mod_post_op\n"); + + /* Just bail if we aren't ready to service requests yet. */ + if (!g_plugin_started) + return 0; + + /* We don't want to process internal modify + * operations that originate from this plugin. + * Doing so could cause a deadlock. */ + slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &caller_id); + + if (caller_id == linked_attrs_get_plugin_id()) { + /* Just return without processing */ + return 0; + } + + if (linked_attrs_oktodo(pb) && + (dn = linked_attrs_get_dn(pb))) { + /* First check if the config is being modified. */ + if (linked_attrs_dn_is_config(dn)) { + linked_attrs_load_config(); + } else { + slapi_log_error(SLAPI_LOG_PLUGIN, LINK_PLUGIN_SUBSYSTEM, + "linked_attrs_mod_post_op: Error " + "retrieving dn\n"); + } + + /* get the mod set */ + slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods); + smods = slapi_mods_new(); + slapi_mods_init_byref(smods, mods); + + next_mod = slapi_mod_new(); + smod = slapi_mods_get_first_smod(smods, next_mod); + while(smod) { + char *type = (char *)slapi_mod_get_type(smod); + + /* See if there is an applicable link configured. */ + linked_attrs_read_lock(); + linked_attrs_find_config(dn, type, &config); + + /* If we have a matching config entry, we have + * work to do. If not, we can go to the next smod. */ + if (config) { + int op = slapi_mod_get_operation(smod); + + /* Prevent other threads from managing + * this specific link at the same time. */ + slapi_lock_mutex(config->lock); + + switch(op & ~LDAP_MOD_BVALUES) { + case LDAP_MOD_ADD: + /* Find the entries pointed to by the new + * values and add the backpointers. */ + linked_attrs_add_backpointers(dn, config, smod); + break; + case LDAP_MOD_DELETE: + /* Find the entries pointed to by the deleted + * values and remove the backpointers. */ + linked_attrs_del_backpointers(pb, dn, config, smod); + break; + case LDAP_MOD_REPLACE: + /* Find the entries pointed to by the deleted + * values and remove the backpointers. If + * any new values are being added, find those + * entries and add the backpointers. */ + linked_attrs_replace_backpointers(pb, dn, config, smod); + break; + default: + slapi_log_error(SLAPI_LOG_PLUGIN, LINK_PLUGIN_SUBSYSTEM, + "linked_attrs_mod_post_op: unknown mod type\n" ); + break; + } + + slapi_unlock_mutex(config->lock); + } + + config = NULL; + linked_attrs_unlock(); + slapi_mod_done(next_mod); + smod = slapi_mods_get_next_smod(smods, next_mod); + } + + slapi_mod_free(&next_mod); + slapi_mods_free(&smods); + } + + slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM, + "<-- linked_attrs_mod_post_op\n"); + + return 0; +} + +static int +linked_attrs_add_post_op(Slapi_PBlock *pb) +{ + Slapi_Entry *e = NULL; + char *dn = NULL; + + slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM, + "--> linked_attrs_add_post_op\n"); + + /* Just bail if we aren't ready to service requests yet. */ + if (!g_plugin_started || !linked_attrs_oktodo(pb)) + return 0; + + /* Reload config if a config entry was added. */ + if ((dn = linked_attrs_get_dn(pb))) { + if (linked_attrs_dn_is_config(dn)) + linked_attrs_load_config(); + } else { + slapi_log_error(SLAPI_LOG_PLUGIN, LINK_PLUGIN_SUBSYSTEM, + "linked_attrs_add_post_op: Error " + "retrieving dn\n"); + } + + /* Get the newly added entry. */ + slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &e); + + if (e) { + Slapi_Attr *attr = NULL; + char *type = NULL; + struct configEntry *config = NULL; + + slapi_entry_first_attr(e, &attr); + while (attr) { + slapi_attr_get_type(attr, &type); + + /* See if there is an applicable link configured. */ + linked_attrs_read_lock(); + linked_attrs_find_config(dn, type, &config); + + /* If config was found, add the backpointers to this entry. */ + if (config) { + Slapi_ValueSet *vals = NULL; + + slapi_attr_get_valueset(attr, &vals); + + slapi_lock_mutex(config->lock); + + linked_attrs_mod_backpointers(dn, config->managedtype, + config->scope, LDAP_MOD_ADD, vals); + + slapi_unlock_mutex(config->lock); + + slapi_valueset_free(vals); + } + + config = NULL; + linked_attrs_unlock(); + + slapi_entry_next_attr(e, attr, &attr); + } + } else { + slapi_log_error(SLAPI_LOG_PLUGIN, LINK_PLUGIN_SUBSYSTEM, + "linked_attrs_add_post_op: Error " + "retrieving post-op entry %s\n", dn); + } + + slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM, + "<-- linked_attrs_add_post_op\n"); + + return 0; +} + +static int +linked_attrs_del_post_op(Slapi_PBlock *pb) +{ + char *dn = NULL; + Slapi_Entry *e = NULL; + + slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM, + "--> linked_attrs_del_post_op\n"); + + /* Just bail if we aren't ready to service requests yet. */ + if (!g_plugin_started || !linked_attrs_oktodo(pb)) + return 0; + + /* Reload config if a config entry was deleted. */ + if ((dn = linked_attrs_get_dn(pb))) { + if (linked_attrs_dn_is_config(dn)) + linked_attrs_load_config(); + } else { + slapi_log_error(SLAPI_LOG_PLUGIN, LINK_PLUGIN_SUBSYSTEM, + "linked_attrs_del_post_op: Error " + "retrieving dn\n"); + } + + /* Get deleted entry, then go through types to find config. */ + slapi_pblock_get( pb, SLAPI_ENTRY_PRE_OP, &e ); + + if (e) { + Slapi_Attr *attr = NULL; + char *type = NULL; + struct configEntry *config = NULL; + + slapi_entry_first_attr(e, &attr); + while (attr) { + slapi_attr_get_type(attr, &type); + + /* See if there is an applicable link configured. */ + linked_attrs_read_lock(); + linked_attrs_find_config(dn, type, &config); + + /* If config was found, delete the backpointers to this entry. */ + if (config) { + Slapi_ValueSet *vals = NULL; + + slapi_attr_get_valueset(attr, &vals); + + slapi_lock_mutex(config->lock); + + linked_attrs_mod_backpointers(dn, config->managedtype, + config->scope, LDAP_MOD_DELETE, vals); + + slapi_unlock_mutex(config->lock); + + slapi_valueset_free(vals); + } + + config = NULL; + + /* See if any of the values for this attribute are managed + * backpointers. We need to remove the forward link if so. */ + if (linked_attrs_config_index_has_type(type)) { + int hint = 0; + Slapi_Value *val = NULL; + + /* Loop through values and see if we have matching config */ + hint = slapi_attr_first_value(attr, &val); + while (val) { + linked_attrs_find_config_reverse(slapi_value_get_string(val), + type, &config); + + if (config) { + Slapi_ValueSet *vals = slapi_valueset_new(); + slapi_valueset_add_value(vals, val); + + slapi_lock_mutex(config->lock); + + /* Delete forward link value. */ + linked_attrs_mod_backpointers(dn, config->linktype, + config->scope, LDAP_MOD_DELETE, vals); + + slapi_unlock_mutex(config->lock); + + slapi_valueset_free(vals); + config = NULL; + } + + hint = slapi_attr_next_value(attr, hint, &val); + } + } + + linked_attrs_unlock(); + + slapi_entry_next_attr(e, attr, &attr); + } + } else { + slapi_log_error(SLAPI_LOG_PLUGIN, LINK_PLUGIN_SUBSYSTEM, + "linked_attrs_del_post_op: Error " + "retrieving pre-op entry %s\n", dn); + } + + slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM, + "<-- linked_attrs_del_post_op\n"); + + return 0; +} + +static int +linked_attrs_modrdn_post_op(Slapi_PBlock *pb) +{ + char *old_dn = NULL; + char *new_dn = NULL; + Slapi_Entry *post_e = NULL; + Slapi_Attr *attr = NULL; + char *type = NULL; + struct configEntry *config = NULL; + + slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM, + "--> linked_attrs_modrdn_post_op\n"); + + /* Just bail if we aren't ready to service requests yet. */ + if (!g_plugin_started || !linked_attrs_oktodo(pb)) + return 0;; + + /* Reload config if an existing config entry was renamed, + * or if the new dn brings an entry into the scope of the + * config entries. */ + slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &post_e); + if (post_e) { + new_dn = slapi_entry_get_ndn(post_e); + } else { + slapi_log_error(SLAPI_LOG_PLUGIN, LINK_PLUGIN_SUBSYSTEM, + "linked_attrs_modrdn_post_op: Error " + "retrieving post-op entry\n"); + } + + if ((old_dn = linked_attrs_get_dn(pb))) { + if (linked_attrs_dn_is_config(old_dn) || linked_attrs_dn_is_config(new_dn)) + linked_attrs_load_config(); + } else { + slapi_log_error(SLAPI_LOG_PLUGIN, LINK_PLUGIN_SUBSYSTEM, + "linked_attrs_modrdn_post_op: Error " + "retrieving dn\n"); + } + + /* Check if this operation requires any updates to links. */ + slapi_entry_first_attr(post_e, &attr); + while (attr) { + slapi_attr_get_type(attr, &type); + + /* See if there is an applicable link configured. */ + linked_attrs_read_lock(); + linked_attrs_find_config(old_dn, type, &config); + + /* If config was found for the old dn, delete the backpointers + * to this entry. */ + if (config) { + Slapi_ValueSet *vals = NULL; + + slapi_attr_get_valueset(attr, &vals); + + slapi_lock_mutex(config->lock); + + /* Delete old dn value. */ + linked_attrs_mod_backpointers(old_dn, config->managedtype, + config->scope, LDAP_MOD_DELETE, vals); + + slapi_unlock_mutex(config->lock); + + slapi_valueset_free(vals); + config = NULL; + } + + linked_attrs_find_config(new_dn, type, &config); + + /* If config was found for the new dn, add the backpointers + * to this entry. We do this separate check for both dn's + * to catch an entry that comes into or goes out of scope + * from the MODRDN operation. */ + if (config) { + Slapi_ValueSet *vals = NULL; + + slapi_attr_get_valueset(attr, &vals); + + slapi_lock_mutex(config->lock); + + /* Add new dn value. */ + linked_attrs_mod_backpointers(new_dn, config->managedtype, + config->scope, LDAP_MOD_ADD, vals); + + slapi_unlock_mutex(config->lock); + + slapi_valueset_free(vals); + config = NULL; + } + + /* See if any of the values for this attribute are managed + * backpointers. We need to update the forward link if so. */ + if (linked_attrs_config_index_has_type(type)) { + int hint = 0; + Slapi_Value *val = NULL; + + /* Loop through values and see if we have matching config */ + hint = slapi_attr_first_value(attr, &val); + while (val) { + linked_attrs_find_config_reverse(slapi_value_get_string(val), + type, &config); + + if (config) { + Slapi_ValueSet *vals = slapi_valueset_new(); + slapi_valueset_add_value(vals, val); + + slapi_lock_mutex(config->lock); + + /* Delete old dn value. */ + linked_attrs_mod_backpointers(old_dn, config->linktype, + config->scope, LDAP_MOD_DELETE, vals); + + /* Add new dn value. */ + linked_attrs_mod_backpointers(new_dn, config->linktype, + config->scope, LDAP_MOD_ADD, vals); + + slapi_unlock_mutex(config->lock); + + slapi_valueset_free(vals); + config = NULL; + } + + hint = slapi_attr_next_value(attr, hint, &val); + } + } + + linked_attrs_unlock(); + + slapi_entry_next_attr(post_e, attr, &attr); + } + + slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM, + "<-- linked_attrs_modrdn_post_op\n"); + + return 0; +} + + +/* + * Debug functions to print config + */ +void +linked_attrs_dump_config() +{ + PRCList *list; + + linked_attrs_read_lock(); + + if (!PR_CLIST_IS_EMPTY(g_link_config)) { + list = PR_LIST_HEAD(g_link_config); + while (list != g_link_config) { + linked_attrs_dump_config_entry((struct configEntry *)list); + list = PR_NEXT_LINK(list); + } + } + + linked_attrs_unlock(); +} + +void +linked_attrs_dump_config_index() +{ + PRCList *list; + + linked_attrs_read_lock(); + + if (!PR_CLIST_IS_EMPTY(g_managed_config_index)) { + list = PR_LIST_HEAD(g_managed_config_index); + while (list != g_managed_config_index) { + linked_attrs_dump_config_entry(((struct configIndex *)list)->config); + list = PR_NEXT_LINK(list); + } + } + + linked_attrs_unlock(); +} + + +void +linked_attrs_dump_config_entry(struct configEntry * entry) +{ + slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM, + "<==== Linked Attribute Pair =====>\n"); + slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM, + "<---- config entry dn -----> %s\n", entry->dn); + slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM, + "<---- link type -----------> %s\n", entry->linktype); + slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM, + "<---- managed type --------> %s\n", entry->managedtype); + slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM, + "<---- scope ---------------> %s\n", entry->scope); +} diff --git a/ldap/servers/plugins/linkedattrs/linked_attrs.h b/ldap/servers/plugins/linkedattrs/linked_attrs.h new file mode 100644 index 00000000..8a3b2621 --- /dev/null +++ b/ldap/servers/plugins/linkedattrs/linked_attrs.h @@ -0,0 +1,141 @@ +/** 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) 2009 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +/* + * Linked attributes plug-in header file + */ +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <errno.h> +#include "portable.h" +#include "nspr.h" +#include "slapi-plugin.h" +#include "dirlite_strings.h" +#include "dirver.h" +#include "prclist.h" +#include "ldif.h" + +/* + * Plug-in defines + */ +#define LINK_PLUGIN_SUBSYSTEM "linkedattrs-plugin" +#define LINK_FEATURE_DESC "Linked Attributes" +#define LINK_PLUGIN_DESC "Linked Attributes plugin" +#define LINK_INT_POSTOP_DESC "Linked Attributes internal postop plugin" +#define LINK_POSTOP_DESC "Linked Attributes postop plugin" + +/* + * Config type defines + */ +#define LINK_LINK_TYPE "linkType" +#define LINK_MANAGED_TYPE "managedType" +#define LINK_SCOPE "linkScope" + +/* + * Other defines + */ +#define DN_SYNTAX_OID "1.3.6.1.4.1.1466.115.121.1.12" + +/* + * Linked list of config entries. + */ +struct configEntry { + PRCList list; + char *dn; + char *linktype; + char *managedtype; + char *scope; + Slapi_Mutex *lock; +}; + +/* + * Linked list used for indexing config entries + * by managed type. + */ +struct configIndex { + PRCList list; + struct configEntry *config; +}; + +/* + * Fixup task private data. + */ +typedef struct _task_data +{ + char *linkdn; +} task_data; + + +/* + * Debug functions - global, for the debugger + */ +void linked_attrs_dump_config(); +void linked_attrs_dump_config_index(); +void linked_attrs_dump_config_entry(struct configEntry *); + +/* + * Config fetch function + */ +PRCList *linked_attrs_get_config(); + +/* + * Config cache locking functions + */ +void linked_attrs_read_lock(); +void linked_attrs_write_lock(); +void linked_attrs_unlock(); + +/* + * Plugin identity functions + */ +void linked_attrs_set_plugin_id(void *pluginID); +void *linked_attrs_get_plugin_id(); +void linked_attrs_set_plugin_dn(char *pluginDN); +char *linked_attrs_get_plugin_dn(); + +/* + * Fixup task callback + */ +int linked_attrs_fixup_task_add(Slapi_PBlock *pb, Slapi_Entry *e, + Slapi_Entry *eAfter, int *returncode, + char *returntext, void *arg); + diff --git a/ldap/servers/slapd/add.c b/ldap/servers/slapd/add.c index 6607eff9..1bd2d2f2 100644 --- a/ldap/servers/slapd/add.c +++ b/ldap/servers/slapd/add.c @@ -641,7 +641,9 @@ static void op_shared_add (Slapi_PBlock *pb) slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &pse); do_ps_service(pse, NULL, LDAP_CHANGETYPE_ADD, 0); - e = NULL; /* if be_add succeeded, then e is consumed. Must prevent e from being free'd. */ + /* If be_add succeeded, then e is consumed. We + * set e to NULL to prevent freeing it ourselves. */ + e = NULL; } else { diff --git a/ldap/servers/slapd/attr.c b/ldap/servers/slapd/attr.c index 6c3a6a0f..d78342ee 100644 --- a/ldap/servers/slapd/attr.c +++ b/ldap/servers/slapd/attr.c @@ -835,7 +835,7 @@ attr_add_valuearray(Slapi_Attr *a, Slapi_Value **vals, const char *dn) } } - slapi_log_error( SLAPI_LOG_FATAL, NULL, "add value \"%s\" to " + slapi_log_error( SLAPI_LOG_TRACE, NULL, "add value \"%s\" to " "attribute type \"%s\" in entry \"%s\" failed: %s\n", duplicate_string, a->a_type, diff --git a/ldap/servers/slapd/dse.c b/ldap/servers/slapd/dse.c index c48d1f20..04a1b356 100644 --- a/ldap/servers/slapd/dse.c +++ b/ldap/servers/slapd/dse.c @@ -1065,6 +1065,11 @@ dse_write_entry( caddr_t data, caddr_t arg ) return 0; } +/* + * Adds an entry to the dse backend. The passed in entry will be + * free'd upon success. If we don't return 0, the caller is responsible + * for freeing the entry. + */ static int dse_add_entry_pb(struct dse* pdse, Slapi_Entry *e, Slapi_PBlock *pb) { @@ -1139,7 +1144,13 @@ dse_add_entry_pb(struct dse* pdse, Slapi_Entry *e, Slapi_PBlock *pb) slapi_entry_free(schemacheckentry); } - return rc; + /* Callers expect e (SLAPI_ADD_ENTRY) to be freed or otherwise + * consumed if the add was successful. */ + if (rc == 0) { + slapi_entry_free(e); + } + + return rc; } /* |