>From faf68949bba456988ec4aaa2715c8d9cd664f513 Mon Sep 17 00:00:00 2001
From: Nathan Kinder <nkinder@redhat.com>
Date: Wed, 4 Nov 2009 14:39:44 -0800
Subject: [PATCH] Allow plugin execution order to be defined

This patch adds support for assigning a precedence to each plugin.
This precedence value is used to determine what order the plugins
should be called in within a given plugin type.

The precedence value is an integer between 1 and 99.  The value is
set in the nsslapd-pluginPrecedence attribute in a plugin config
entry in "cn=config".  If a plugin does not have a precedence
value set, the plugin is treated as if it has a precedence of 50.

For more details, see the feature design document at:

    http://directory.fedoraproject.org/wiki/Plugin_Ordering

I also removed an incorrect log message in the linked attrs plugin
that I noticed while debugging this patch.
---
 ldap/servers/plugins/linkedattrs/linked_attrs.c |    4 -
 ldap/servers/slapd/main.c                       |    1 +
 ldap/servers/slapd/plugin.c                     |   92 ++++++++++++++++++++++-
 ldap/servers/slapd/proto-slap.h                 |    1 +
 ldap/servers/slapd/slap.h                       |   15 +++-
 5 files changed, 103 insertions(+), 10 deletions(-)

diff --git a/ldap/servers/plugins/linkedattrs/linked_attrs.c b/ldap/servers/plugins/linkedattrs/linked_attrs.c
index a04fbb0..edaf5db 100644
--- a/ldap/servers/plugins/linkedattrs/linked_attrs.c
+++ b/ldap/servers/plugins/linkedattrs/linked_attrs.c
@@ -1582,10 +1582,6 @@ linked_attrs_mod_post_op(Slapi_PBlock *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 */
diff --git a/ldap/servers/slapd/main.c b/ldap/servers/slapd/main.c
index 1d820f3..3cad84b 100644
--- a/ldap/servers/slapd/main.c
+++ b/ldap/servers/slapd/main.c
@@ -1134,6 +1134,7 @@ main( int argc, char **argv)
 		 */
 		task_cleanup();
 
+		plugin_print_lists();
 		plugin_startall(argc, argv, 1 /* Start Backends */, 1 /* Start Globals */); 
 		if (housekeeping_start((time_t)0, NULL) == NULL) {
 			return_value = 1;
diff --git a/ldap/servers/slapd/plugin.c b/ldap/servers/slapd/plugin.c
index 75ec4f9..1664cc1 100644
--- a/ldap/servers/slapd/plugin.c
+++ b/ldap/servers/slapd/plugin.c
@@ -105,11 +105,43 @@ static void
 add_plugin_to_list(struct slapdplugin **list, struct slapdplugin *plugin)
 {
 	struct slapdplugin **tmp;
+	struct slapdplugin *last = NULL;
+	int plugin_added = 0;
+
+	/* Insert the plugin into list based off of precedence. */
 	for ( tmp = list; *tmp; tmp = &(*tmp)->plg_next )
 	{
-	  ;	/* NULL */
+		if (plugin->plg_precedence < (*tmp)->plg_precedence)
+		{
+			if (last)
+			{
+				/* Insert item between last and tmp. */
+				plugin->plg_next = *tmp;
+				last->plg_next = plugin;
+			} else {
+				/* Add as the first list item. */
+				plugin->plg_next = *tmp;
+				*list = plugin;
+			}
+
+			plugin_added = 1;
+
+			/* We've added the plug-in to the
+ 			 * list, so bail from the loop. */
+			break;
+		}
+
+		/* Save a pointer to this plugin so we can
+		 * refer to it on the next loop iteration. */
+		last = *tmp;
+	}
+
+	/* If we didn't add the plug-in to the list yet,
+	 * it needs to be added to the end of the list. */
+	if (!plugin_added)
+	{
+		*tmp = plugin;
 	}
-	*tmp = plugin;
 }
 
 struct slapdplugin *
@@ -2024,6 +2056,37 @@ plugin_setup(Slapi_Entry *plugin_entry, struct slapi_componentid *group,
 		plugin->plg_name = value; /* plugin owns value's memory now, don't free */
 	}
 
+	if (!(value = slapi_entry_attr_get_charptr(plugin_entry, ATTR_PLUGIN_PRECEDENCE)))
+	{
+		/* A precedence isn't set, so just use the default. */
+		plugin->plg_precedence = PLUGIN_DEFAULT_PRECEDENCE;
+	}
+	else
+	{
+		/* A precedence was set, so let's make sure it's valid. */
+		int precedence = 0;
+		char *endptr = NULL;
+
+		/* Convert the value. */
+		precedence = strtol(value, &endptr, 10);
+
+		/* Make sure the precedence is within our valid
+		 * range and that we had no conversion errors. */
+		if ((*value == '\0') || (*endptr != '\0') ||
+			(precedence < PLUGIN_MIN_PRECEDENCE) || (precedence > PLUGIN_MAX_PRECEDENCE))
+		{
+			LDAPDebug(LDAP_DEBUG_ANY, "Error: value for attribute %s must be "
+				"an integer between %d and %d\n", ATTR_PLUGIN_PRECEDENCE,
+				PLUGIN_MIN_PRECEDENCE, PLUGIN_MAX_PRECEDENCE);
+			status = -1;
+			goto PLUGIN_CLEANUP;
+		}
+		else
+		{
+			plugin->plg_precedence = precedence;
+		}
+	}
+
 	if (!(value = slapi_entry_attr_get_charptr(plugin_entry,
 											   ATTR_PLUGIN_INITFN)))
 	{
@@ -2797,6 +2860,31 @@ void plugin_print_versions(void)
 }
 
 /*
+ * Prints a list of plugins in execution order for each
+ * plug-in type.  This will only be printed at the
+ * SLAPI_LOG_PLUGIN log level.
+ */
+void plugin_print_lists(void)
+{
+	int i;
+	struct slapdplugin *list = NULL;
+	struct slapdplugin *tmp = NULL;
+
+	for (i = 0; i < PLUGIN_LIST_GLOBAL_MAX; i++) {
+		if (list = get_plugin_list(i))
+		{
+			slapi_log_error(SLAPI_LOG_PLUGIN, NULL,
+				"---- Plugin List (type %d) ----\n", i);
+			for ( tmp = list; tmp; tmp = tmp->plg_next )
+			{
+				slapi_log_error(SLAPI_LOG_PLUGIN, NULL, "  %s (precedence: %d)\n",
+					tmp->plg_name, tmp->plg_precedence);
+			}
+		}
+	}
+}
+
+/*
  * check the spedified plugin entry and its nssladp-pluginEnabled value
  * Return Value: 1 if the plugin is on.
  *             : 0 otherwise.
diff --git a/ldap/servers/slapd/proto-slap.h b/ldap/servers/slapd/proto-slap.h
index b220bf0..feca39a 100644
--- a/ldap/servers/slapd/proto-slap.h
+++ b/ldap/servers/slapd/proto-slap.h
@@ -772,6 +772,7 @@ int plugin_add_descriptive_attributes( Slapi_Entry *e,
 void plugin_call_entryfetch_plugins(char **entrystr, uint *size);
 void plugin_call_entrystore_plugins(char **entrystr, uint *size);
 void plugin_print_versions(void);
+void plugin_print_lists(void);
 
 /*
  * plugin_mr.c
diff --git a/ldap/servers/slapd/slap.h b/ldap/servers/slapd/slap.h
index 76c8df2..35fc1f5 100644
--- a/ldap/servers/slapd/slap.h
+++ b/ldap/servers/slapd/slap.h
@@ -691,6 +691,12 @@ struct matchingRuleList {
 #define ATTR_PLUGIN_INVOKE_FOR_REPLOP	"nsslapd-invokeForReplOp"
 #define ATTR_PLUGIN_LOAD_NOW            "nsslapd-pluginLoadNow"
 #define ATTR_PLUGIN_LOAD_GLOBAL         "nsslapd-pluginLoadGlobal"
+#define ATTR_PLUGIN_PRECEDENCE			"nsslapd-pluginPrecedence"
+
+/* plugin precedence defines */
+#define PLUGIN_DEFAULT_PRECEDENCE 50
+#define PLUGIN_MIN_PRECEDENCE 1
+#define PLUGIN_MAX_PRECEDENCE 99
 
 /* plugin action states */
 enum
@@ -738,16 +744,17 @@ struct pluginconfig{
 struct slapdplugin {
 	void				*plg_private;	/* data private to plugin */
 	char				*plg_version;	/* version of this plugin */
-    int                 plg_argc;       /* argc from config file */
+	int                 plg_argc;       /* argc from config file */
 	char				**plg_argv;		/* args from config file */
 	char				*plg_libpath;	/* library path for dll/so */
-    char				*plg_initfunc;  /* init symbol */
+	char				*plg_initfunc;  /* init symbol */
 	IFP					plg_close;		/* close function */
 	Slapi_PluginDesc	plg_desc;		/* vendor's info */
-    char				*plg_name;		/* used for plugin rdn in cn=config */
+	char				*plg_name;		/* used for plugin rdn in cn=config */
 	struct slapdplugin	*plg_next;		/* for plugin lists */
 	int					plg_type;		/* discriminates union */
-    char				*plg_dn;		/* config dn for this plugin */
+	char				*plg_dn;		/* config dn for this plugin */
+	int					plg_precedence;	/* for plugin execution ordering */
 	struct slapdplugin  *plg_group;		/* pointer to the group to which this plugin belongs */
 	struct pluginconfig plg_conf;		/* plugin configuration parameters */
 	IFP					plg_cleanup;	/* cleanup function */
-- 
1.6.2.5

