summaryrefslogtreecommitdiffstats
path: root/ldap/servers/plugins/chainingdb
diff options
context:
space:
mode:
authorcvsadm <cvsadm>2005-01-21 00:44:34 +0000
committercvsadm <cvsadm>2005-01-21 00:44:34 +0000
commitb2093e3016027d6b5cf06b3f91f30769bfc099e2 (patch)
treecf58939393a9032182c4fbc4441164a9456e82f8 /ldap/servers/plugins/chainingdb
downloadds-ldapserver7x.tar.gz
ds-ldapserver7x.tar.xz
ds-ldapserver7x.zip
Moving NSCP Directory Server from DirectoryBranch to TRUNK, initial drop. (foxworth)ldapserver7x
Diffstat (limited to 'ldap/servers/plugins/chainingdb')
-rw-r--r--ldap/servers/plugins/chainingdb/Makefile93
-rw-r--r--ldap/servers/plugins/chainingdb/cb.h461
-rw-r--r--ldap/servers/plugins/chainingdb/cb_abandon.c50
-rw-r--r--ldap/servers/plugins/chainingdb/cb_acl.c60
-rw-r--r--ldap/servers/plugins/chainingdb/cb_add.c227
-rw-r--r--ldap/servers/plugins/chainingdb/cb_bind.c290
-rw-r--r--ldap/servers/plugins/chainingdb/cb_cleanup.c22
-rw-r--r--ldap/servers/plugins/chainingdb/cb_close.c67
-rw-r--r--ldap/servers/plugins/chainingdb/cb_compare.c217
-rw-r--r--ldap/servers/plugins/chainingdb/cb_config.c600
-rw-r--r--ldap/servers/plugins/chainingdb/cb_conn_stateless.c1132
-rw-r--r--ldap/servers/plugins/chainingdb/cb_controls.c289
-rw-r--r--ldap/servers/plugins/chainingdb/cb_debug.c23
-rw-r--r--ldap/servers/plugins/chainingdb/cb_delete.c203
-rw-r--r--ldap/servers/plugins/chainingdb/cb_init.c120
-rw-r--r--ldap/servers/plugins/chainingdb/cb_instance.c1871
-rw-r--r--ldap/servers/plugins/chainingdb/cb_modify.c244
-rw-r--r--ldap/servers/plugins/chainingdb/cb_modrdn.c239
-rw-r--r--ldap/servers/plugins/chainingdb/cb_monitor.c228
-rw-r--r--ldap/servers/plugins/chainingdb/cb_schema.c45
-rw-r--r--ldap/servers/plugins/chainingdb/cb_search.c698
-rw-r--r--ldap/servers/plugins/chainingdb/cb_size.c22
-rw-r--r--ldap/servers/plugins/chainingdb/cb_start.c43
-rw-r--r--ldap/servers/plugins/chainingdb/cb_temp.c15
-rw-r--r--ldap/servers/plugins/chainingdb/cb_test.c76
-rw-r--r--ldap/servers/plugins/chainingdb/cb_unbind.c23
-rw-r--r--ldap/servers/plugins/chainingdb/cb_utils.c375
-rw-r--r--ldap/servers/plugins/chainingdb/cbdllmain.c128
-rw-r--r--ldap/servers/plugins/chainingdb/libcb.def15
29 files changed, 7876 insertions, 0 deletions
diff --git a/ldap/servers/plugins/chainingdb/Makefile b/ldap/servers/plugins/chainingdb/Makefile
new file mode 100644
index 00000000..4f47e480
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/Makefile
@@ -0,0 +1,93 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+#
+# GNU Makefile for Directory Server "Chaining Backend" plugin
+#
+
+LDAP_SRC = ../../..
+
+MCOM_ROOT = ../../../../..
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+NSPR20=true # probably should be defined somewhere else (not sure where)
+
+OBJDEST = $(OBJDIR)/lib/libcb
+LIBDIR = $(LIB_RELDIR)
+
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+
+ifeq ($(ARCH), WINNT)
+DEF_FILE:=./libcb.def
+endif
+
+CFLAGS+=$(SLCFLAGS)
+
+INCLUDES += -I$(LDAP_SRC)/servers/slapd
+
+CB_OBJS= cb_temp.o cb_init.o cb_config.o cb_instance.o cb_start.o cb_search.o cb_utils.o cb_add.o cb_delete.o cb_schema.o \
+cb_acl.o cb_modify.o cb_compare.o cb_modrdn.o cb_abandon.o cb_conn_stateless.o cb_bind.o cb_unbind.o cb_monitor.o \
+cb_controls.o cb_size.o cb_test.o cb_close.o cb_cleanup.o cb_debug.o
+
+OBJS = $(addprefix $(OBJDEST)/, $(CB_OBJS))
+
+ifeq ($(ARCH), WINNT)
+LIBCB_DLL_OBJ = $(addprefix $(OBJDEST)/, cbdllmain.o)
+endif
+
+LIBCB= $(addprefix $(LIBDIR)/, $(CB_DLL).$(DLL_SUFFIX))
+
+ifeq ($(ARCH), WINNT)
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP) $(LDAP_LIBUTIL_DEP) $(LDAP_COMMON_LIBS_DEP)
+EXTRA_LIBS_DEP += $(LDAPSDK_DEP) $(SECURITY_DEP) $(NSPR_DEP)
+EXTRA_LIBS += $(LIBSLAPD) $(LDAP_SDK_LIBLDAP_DLL) $(LDAP_LIBUTIL) $(LDAP_COMMON_LIBS) $(SECURITYLINK) $(NSPRLINK)
+
+endif
+
+
+ifeq ($(ARCH), WINNT)
+DLL_LDFLAGS += -def:"./libcb.def"
+endif # WINNT
+
+ifeq ($(ARCH), AIX)
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP) $(LDAP_LIBUTIL_DEP) $(LDAP_COMMON_LIBS_DEP)
+EXTRA_LIBS_DEP += $(LDAPSDK_DEP) $(SECURITY_DEP) $(NSPR_DEP)
+EXTRA_LIBS += $(LIBSLAPD) $(LDAP_SDK_LIBLDAP_DLL) $(LIBUTIL) $(LDAP_COMMON_LIBS) $(SECURITYLINK) $(NSPRLINK)
+EXTRA_LIBS += $(DLL_EXTRA_LIBS)
+LD=ld
+endif
+
+ifeq ($(ARCH), HPUX)
+EXTRA_LIBS_DEP += $(LDAPSDK_DEP) $(NSPR_DEP) $(SECURITY_DEP)
+EXTRA_LIBS += $(DYN_NSHTTPD) $(ADMINUTIL_LINK) $(LDAPLINK) $(SECURITYLINK) $(NSPRLINK) $(ICULINK)
+endif
+
+clientSDK:
+
+all: $(OBJDEST) $(LIBDIR) $(LIBCB)
+
+$(LIBCB): $(OBJS) $(LIBCB_DLL_OBJ) $(DEF_FILE)
+ $(LINK_DLL) $(LIBCB_DLL_OBJ) $(PLATFORMLIBS) $(EXTRA_LIBS)
+
+veryclean: clean
+
+clean:
+ $(RM) $(OBJS)
+ifeq ($(ARCH), WINNT)
+ $(RM) $(LIBCB_DLL_OBJ)
+endif
+ $(RM) $(LIBCB)
+
+$(OBJDEST):
+ $(MKDIR) $(OBJDEST)
+
+#
+# header file dependencies (incomplete)
+#
+$(OBJS): cb.h
diff --git a/ldap/servers/plugins/chainingdb/cb.h b/ldap/servers/plugins/chainingdb/cb.h
new file mode 100644
index 00000000..8aed412c
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb.h
@@ -0,0 +1,461 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#ifndef CBHFILE
+#define CBHFILE
+
+/*** #define CB_YIELD ***/
+
+#include <stdio.h>
+#include <string.h>
+#include <prlock.h>
+#include <prcvar.h>
+#include "slapi-plugin.h"
+#include "slapi-private.h"
+#include "portable.h"
+#include <dirlite_strings.h> /* PLUGIN_MAGIC_VENDOR_STR */
+#include "dirver.h"
+
+/* Constants */
+
+#define CB_DIRECTORY_MANAGER_DN "cn=directory manager"
+#define CB_CHAINING_BACKEND_TYPE "chaining database"
+#define CB_PLUGIN_NAME "chaining database"
+#define CB_PLUGIN_SUBSYSTEM "chaining database"
+#define CB_PLUGIN_DESCRIPTION "LDAP chaining backend database plugin"
+
+#define CB_LDAP_SECURE_PORT 636
+#define CB_BUFSIZE 2048
+
+
+/* Macros */
+
+#define CB_LDAP_CONN_ERROR( err ) ( (err) == LDAP_SERVER_DOWN || \
+ (err) == LDAP_CONNECT_ERROR )
+#define CB_ASSERT( expr ) PR_ASSERT( expr )
+
+/* Innosoft chaining extension for loop detection */
+
+#define CB_LDAP_CONTROL_CHAIN_SERVER "1.3.6.1.4.1.1466.29539.12"
+
+/* Chaining backend configuration attributes */
+
+/* Monitor entry */
+#define CB_MONITOR_EXTENSIBLEOCL "extensibleObject"
+#define CB_MONITOR_INSTNAME "cn"
+#define CB_MONITOR_ADDCOUNT "nsAddCount"
+#define CB_MONITOR_DELETECOUNT "nsDeleteCount"
+#define CB_MONITOR_MODIFYCOUNT "nsModifyCount"
+#define CB_MONITOR_MODRDNCOUNT "nsRenameCount"
+#define CB_MONITOR_SEARCHBASECOUNT "nsSearchBaseCount"
+#define CB_MONITOR_SEARCHONELEVELCOUNT "nsSearchOneLevelCount"
+#define CB_MONITOR_SEARCHSUBTREECOUNT "nsSearchSubtreeCount"
+#define CB_MONITOR_ABANDONCOUNT "nsAbandonCount"
+#define CB_MONITOR_BINDCOUNT "nsBindCount"
+#define CB_MONITOR_UNBINDCOUNT "nsUnbindCount"
+#define CB_MONITOR_COMPARECOUNT "nsCompareCount"
+#define CB_MONITOR_OUTGOINGCONN "nsOpenOpConnectionCount"
+#define CB_MONITOR_OUTGOINGBINDCOUNT "nsOpenBindConnectionCount"
+
+/* Global configuration */
+#define CB_CONFIG_GLOBAL_FORWARD_CTRLS "nsTransmittedControls"
+#define CB_CONFIG_GLOBAL_CHAINING_COMPONENTS "nsActiveChainingComponents"
+#define CB_CONFIG_GLOBAL_CHAINABLE_COMPONENTS "nsPossibleChainingComponents"
+/* not documented */
+#define CB_CONFIG_GLOBAL_DEBUG "nsDebug"
+
+
+/* Instance-specific configuration */
+#define CB_CONFIG_CHAINING_COMPONENTS CB_CONFIG_GLOBAL_CHAINING_COMPONENTS
+#define CB_CONFIG_EXTENSIBLEOCL "extensibleObject"
+/* XXXSD to be changed */
+#define CB_CONFIG_INSTANCE_FILTER "(objectclass=nsBackendInstance)"
+#define CB_CONFIG_INSTNAME "cn"
+#define CB_CONFIG_SUFFIX "nsslapd-suffix"
+#define CB_CONFIG_SIZELIMIT "nsslapd-sizelimit"
+#define CB_CONFIG_TIMELIMIT "nsslapd-timelimit"
+#define CB_CONFIG_HOSTURL "nsFarmServerURL"
+
+#define CB_CONFIG_BINDUSER "nsMultiplexorBindDn"
+#define CB_CONFIG_USERPASSWORD "nsMultiplexorCredentials"
+#define CB_CONFIG_MAXBINDCONNECTIONS "nsBindConnectionsLimit"
+#define CB_CONFIG_MAXCONNECTIONS "nsOperationConnectionsLimit"
+#define CB_CONFIG_MAXCONCURRENCY "nsConcurrentOperationsLimit"
+#define CB_CONFIG_MAXBINDCONCURRENCY "nsConcurrentBindLimit"
+
+#define CB_CONFIG_IMPERSONATION "nsProxiedAuthorization"
+
+#define CB_CONFIG_BINDTIMEOUT "nsBindTimeout"
+#define CB_CONFIG_TIMEOUT "nsOperationTimeout"
+#define CB_CONFIG_MAX_IDLE_TIME "nsMaxResponseDelay"
+#define CB_CONFIG_MAX_TEST_TIME "nsMaxTestResponseDelay"
+
+#define CB_CONFIG_REFERRAL "nsReferralOnScopedSearch"
+
+#define CB_CONFIG_CONNLIFETIME "nsConnectionLife"
+#define CB_CONFIG_ABANDONTIMEOUT "nsAbandonedSearchCheckInterval "
+#define CB_CONFIG_BINDRETRY "nsBindRetryLimit"
+#define CB_CONFIG_LOCALACL "nsCheckLocalACI"
+#define CB_CONFIG_HOPLIMIT "nsHopLimit"
+
+/* not documented */
+#define CB_CONFIG_ILLEGAL_ATTRS "nsServerDefinedAttributes"
+
+/* Default configuration values (as string) */
+
+/*
+ * CB_DEF_MAXCONNECTIONS and CB_DEF_MAXCONCURRENCY used to be 10.
+ * Reduced CB_DEF_MAXCONCURRENCY to 2 to workaround bug 623793 -
+ * err=1 in accesslogs and ber parsing errors in errors logs.
+ */
+#define CB_DEF_MAXCONNECTIONS "20" /* CB_CONFIG_MAXCONNECTIONS */
+#define CB_DEF_MAXCONCURRENCY "2" /* CB_CONFIG_MAXCONCURRENCY */
+#define CB_DEF_BIND_MAXCONNECTIONS "3" /* CB_CONFIG_MAXBINDCONNECTIONS */
+#define CB_DEF_BIND_MAXCONCURRENCY "10" /* CB_CONFIG_MAXBINDCONCURRENCY */
+#define CB_DEF_BINDTIMEOUT "15" /* CB_CONFIG_BINDTIMEOUT */
+#define CB_DEF_CONNLIFETIME "0" /* CB_CONFIG_CONNLIFETIME */
+#define CB_DEF_IMPERSONATION "on" /* CB_CONFIG_IMPERSONATION */
+#define CB_DEF_SEARCHREFERRAL "off" /* CB_CONFIG_REFERRAL */
+#define CB_DEF_ABANDON_TIMEOUT "1" /* CB_CONFIG_ABANDONTIMEOUT */
+#define CB_DEF_BINDRETRY "3" /* CB_CONFIG_BINDRETRY */
+#define CB_DEF_LOCALACL "off" /* CB_CONFIG_LOCALACL */
+#define CB_DEF_TIMELIMIT "3600"
+#define CB_DEF_SIZELIMIT "2000"
+#define CB_DEF_HOPLIMIT "10" /* CB_CONFIG_HOPLIMIT */
+#define CB_DEF_MAX_IDLE_TIME "60" /* CB_CONFIG_MAX_IDLE_TIME */
+#define CB_DEF_MAX_TEST_TIME "15" /* CB_CONFIG_MAX_TEST_TIME */
+
+typedef void *cb_config_get_fn_t(void *arg);
+typedef int cb_config_set_fn_t(void *arg, void *value, char *errorbuf, int phase, int apply);
+typedef struct _cb_instance_config_info {
+ char *config_name;
+ int config_type;
+ char *config_default_value;
+ cb_config_get_fn_t *config_get_fn;
+ cb_config_set_fn_t *config_set_fn;
+ int config_flags;
+} cb_instance_config_info;
+
+#define CB_CONFIG_TYPE_ONOFF 1 /* val = (int) value */
+#define CB_CONFIG_TYPE_STRING 2 /* val = (char *) value - The get functions
+ * for this type must return alloced memory
+ * that should be freed by the caller. */
+#define CB_CONFIG_TYPE_INT 3 /* val = (int) value */
+#define CB_CONFIG_TYPE_LONG 4 /* val = (long) value */
+#define CB_CONFIG_TYPE_INT_OCTAL 5 /* Same as CONFIG_TYPE_INT, but shown in octal*/
+#define CB_PREVIOUSLY_SET 1
+#define CB_ALWAYS_SHOW 2
+#define CB_CONFIG_PHASE_INITIALIZATION 1
+#define CB_CONFIG_PHASE_STARTUP 2
+#define CB_CONFIG_PHASE_RUNNING 3
+#define CB_CONFIG_PHASE_INTERNAL 4
+
+/*jarnou: default amount of time in seconds during wich the chaining backend will be unavailable */
+#define CB_UNAVAILABLE_PERIOD 30 /* CB_CONFIG_UNAVAILABLE_PERIOD */
+#define CB_INFINITE_TIME 360000 /* must be enough ... */
+/*jarnou: default number of connections failed from which the farm is declared unavailable */
+#define CB_NUM_CONN_BEFORE_UNAVAILABILITY 1
+#define FARMSERVER_UNAVAILABLE 1
+#define FARMSERVER_AVAILABLE 0
+
+/* Internal data structures */
+
+/* cb_backend represents the chaining backend type. */
+/* Only one instance is created when the plugin is */
+/* loaded. Contain global conf */
+typedef struct _cb_backend {
+
+ /*
+ ** keep track of plugin identity.
+ ** Used for internal operations
+ */
+
+ void *identity;
+ char * pluginDN;
+ char * configDN;
+
+ /*
+ ** There are times when we need a pointer to the chaining database
+ ** plugin, so we will store a pointer to it here. Examples of
+ ** when we need it are when we create a new instance and when
+ ** we need the name of the plugin to do internal ops.
+ */
+
+ struct slapdplugin *plugin;
+
+ /*
+ ** Global config. shared by all chaining db instances
+ */
+
+ struct {
+ char ** forward_ctrls; /* List of forwardable controls */
+ char ** chaining_components; /* List of plugins that chains */
+ char ** chainable_components; /* List of plugins allowed to chain*/
+ /* internal operations. */
+ PRRWLock *rwl_config_lock; /* Protect the global config */
+ } config;
+
+ int started; /* TRUE when started */
+
+} cb_backend;
+
+
+/* Connection management */
+
+/* states */
+#define CB_CONNSTATUS_OK 1 /* Open */
+#define CB_CONNSTATUS_DOWN 2 /* Down */
+#define CB_CONNSTATUS_STALE 3
+
+#define ENABLE_MULTITHREAD_PER_CONN 1 /* to allow multiple threads to perform LDAP operations on a connection */
+#define DISABLE_MULTITHREAD_PER_CONN 0 /* to allow only one thread to perform LDAP operations on a connection */
+
+/************** WARNING: Be careful if you want to change this constant. It is used in hexadecimal in cb_conn_stateless.c in the function PR_ThreadSelf() ************/
+#define MAX_CONN_ARRAY 2048 /* we suppose the number of threads in the server not to exceed this limit*/
+/**********************************************************************************************************/
+typedef struct _cb_outgoing_conn{
+ LDAP *ld;
+ unsigned long refcount;
+ struct _cb_outgoing_conn *next;
+ time_t opentime;
+ int status;
+ int ThreadId ; /* usefull to identify the thread when SSL is enabled */
+} cb_outgoing_conn;
+
+typedef struct {
+ char *hostname; /* Farm server name */
+ char *url;
+ unsigned int port;
+ int secure;
+ char *binddn; /* normalized */
+ char *binddn2; /* not normalized, value returned to the client */
+ char *password;
+ int bindit; /* If true, open AND bind */
+ char ** waste_basket; /* stale char * */
+
+ struct {
+ unsigned int maxconnections;
+ unsigned int maxconcurrency;
+ unsigned int connlifetime;
+ struct timeval op_timeout;
+ struct timeval bind_timeout;
+
+ Slapi_Mutex *conn_list_mutex;
+ Slapi_CondVar *conn_list_cv;
+ cb_outgoing_conn *conn_list;
+ unsigned int conn_list_count;
+
+ } conn;
+
+ cb_outgoing_conn *connarray[MAX_CONN_ARRAY]; /* array of secure connections */
+
+ /* To protect the config set by LDAP */
+ PRRWLock * rwl_config_lock;
+} cb_conn_pool;
+
+
+/* _cb_backend_instance represents a instance of the chaining */
+/* backend. */
+
+typedef struct _cb_backend_instance {
+
+ char *inst_name; /* Unique name */
+ Slapi_Backend *inst_be; /* Slapi_Bakedn associated with it */
+ cb_backend *backend_type; /* pointer to the backend type */
+
+ /* configuration */
+
+ PRRWLock *rwl_config_lock; /* protect the config */
+ char *configDn; /* config entry dn */
+ char *monitorDn; /* monitor entry dn */
+ int local_acl; /* True if local acl evaluation */
+ /* sometimes a chaining backend may be associated with a local backend
+ 1) The chaining backend is the backend of a sub suffix, and the
+ parent suffix has a local backend
+ 2) Entry distribution is being used to distribute write operations to
+ a chaining backend and other operations to a local backend
+ (e.g. a replication hub or consumer)
+ If the associated local backend is being initialized (import), it will be
+ disabled, and it will be impossible to evaluate local acls. In this case,
+ we still want to be able to chain operations to a farm server or another
+ database chain. But the current code will not allow cascading without
+ local acl evaluation (cb_controls.c). The following variable allows us to relax that
+ restriction while the associated backend is disabled
+ */
+ int associated_be_is_disabled; /* true if associated backend is disabled */
+ int isconfigured; /* True when valid config entry */
+ int impersonate; /* TRUE to impersonate users */
+ int searchreferral; /* TRUE to return referral for scoped searches */
+ int bind_retry;
+ struct timeval abandon_timeout; /* check for abandoned op periodically */
+ struct timeval op_timeout;
+ char **url_array; /* list of urls to farm servers */
+ char **chaining_components; /* List of plugins using chaining */
+ char **illegal_attributes; /* Attributes not forwarded */
+ char **every_attribute; /* attr list to get every attr, including op attrs */
+ int sizelimit;
+ int timelimit;
+ int hoplimit;
+ int max_idle_time; /* how long we wait before pinging the farm server */
+ int max_test_time; /* how long we wait during ping */
+
+ cb_conn_pool *pool; /* Operation cnx pool */
+ cb_conn_pool *bind_pool; /* Bind cnx pool */
+
+ Slapi_Eq_Context eq_ctx; /* Use to identify the function put in the queue */
+
+ /* Monitoring */
+
+ struct {
+ Slapi_Mutex *mutex;
+ unsigned long addcount;
+ unsigned long deletecount;
+ unsigned long modifycount;
+ unsigned long modrdncount;
+ unsigned long searchbasecount;
+ unsigned long searchonelevelcount;
+ unsigned long searchsubtreecount;
+ unsigned long abandoncount;
+ unsigned long bindcount;
+ unsigned long unbindcount;
+ unsigned long comparecount;
+ } monitor;
+
+ /* Monitoring the chaining BE availability */
+ /* Principle: as soon as we detect an abnormal pb with an ldap operation, and we close the connection
+ or if we can't open a connection, we increment a counter (cpt). This counter represents the number of
+ continuously pbs we can notice. Before forwarding an LDAP operation, wether the farmserver is available or not,
+ through the value of the counter. If the farmserver is not available, we just return an error msg to the client */
+
+ struct {
+ int unavailable_period ; /* how long we wait as soon as the farm is declared unavailable */
+ int max_num_conn_failed ; /* max number of consecutive failed/aborted connections before we declared the farm as unreachable */
+ time_t unavailableTimeLimit ; /* time from which the chaining BE becomes available */
+ int farmserver_state ; /* FARMSERVER_AVAILABLE if the chaining is available, FARMSERVER_UNAVAILABLE else */
+ int cpt ; /* count the number of consecutive failed/aborted connexions */
+ Slapi_Mutex *cpt_lock ; /* lock to protect the counter cpt */
+ Slapi_Mutex *lock_timeLimit ; /* lock to protect the unavailableTimeLimit variable*/
+ } monitor_availability;
+
+
+} cb_backend_instance;
+
+/* Data structure for the search operation to carry candidates */
+
+#define CB_SEARCHCONTEXT_ENTRY 2
+
+typedef struct _cb_searchContext {
+ int type;
+ void *data;
+ int msgid;
+ LDAP *ld;
+ cb_outgoing_conn *cnx;
+ Slapi_Entry *tobefreed;
+ LDAPMessage *pending_result;
+ int pending_result_type;
+} cb_searchContext;
+
+#define CB_REOPEN_CONN -1968 /* Different from any LDAP_XXX errors */
+
+/* Forward declarations */
+
+/* for ctrl_flags on cb_update_controls */
+#define CB_UPDATE_CONTROLS_ADDAUTH 1
+#define CB_UPDATE_CONTROLS_ISABANDON 2
+
+
+int cb_get_connection(cb_conn_pool * pool, LDAP ** ld, cb_outgoing_conn ** cnx, struct timeval * tmax, char **errmsg);
+int cb_config(cb_backend_instance * cb, int argc, char ** argv );
+int cb_update_controls( Slapi_PBlock *pb, LDAP * ld, LDAPControl *** controls, int ctrl_flags);
+int cb_is_control_forwardable(cb_backend * cb, char *controloid);
+int cb_access_allowed (Slapi_PBlock *pb,Slapi_Entry *e,char *type,struct berval * bval, int op, char ** buf);
+int cb_forward_operation(Slapi_PBlock * op);
+int cb_parse_instance_config_entry(cb_backend * cb, Slapi_Entry * e);
+int cb_abandon_connection(cb_backend_instance * cb, Slapi_PBlock * pb, LDAP ** ld);
+int cb_atoi(char *str);
+int cb_check_forward_abandon(cb_backend_instance * cb,Slapi_PBlock * pb, LDAP * ld, int msgid );
+int cb_search_monitor_callback(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *e2, int *ret, char *t,void *a);
+int cb_config_load_dse_info(Slapi_PBlock * pb);
+int cb_config_add_dse_entries(cb_backend *cb, char **entries, char *string1, char *string2, char *string3);
+int cb_add_suffix(cb_backend_instance *inst, struct berval **bvals, int apply_mod, char *returntext);
+int cb_create_default_backend_instance_config(cb_backend * cb);
+int cb_build_backend_instance_config(cb_backend_instance *inst, Slapi_Entry * conf,int apply);
+int cb_instance_delete_config_callback(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* e2,
+ int *returncode, char *returntext, void *arg);
+int cb_instance_search_config_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e,
+ int *returncode, char *returntext, void *arg);
+int cb_instance_add_config_callback(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* e2,
+ int *returncode, char *returntext, void *arg);
+int cb_instance_modify_config_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e,
+ int *returncode, char *returntext, void *arg);
+int cb_dont_allow_that(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e,
+ int *returncode, char *returntext, void *arg);
+int cb_config_search_callback(Slapi_PBlock *pb, Slapi_Entry* e1, Slapi_Entry* e2, int *returncode,
+ char *returntext, void *arg);
+int cb_config_add_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg);
+int cb_config_delete_instance_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg);
+int cb_config_modify_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg);
+int cb_config_add_instance_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg);
+int cb_delete_monitor_callback(Slapi_PBlock * pb, Slapi_Entry * e, Slapi_Entry * entryAfter, int * returnCode, char * returnText, void * arg);
+int cb_config_add_check_callback(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* e2, int *returncode,
+ char *returntext, void *arg);
+int cb_instance_add_config_check_callback(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* e2,
+ int *returncode, char *returntext, void *arg);
+int cb_config_modify_check_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode,
+ char *returntext, void *arg);
+
+void cb_eliminate_illegal_attributes(cb_backend_instance * inst, Slapi_Entry * e);
+void cb_release_op_connection(cb_conn_pool * pool, LDAP *ldd, int dispose);
+void cb_register_supported_control( cb_backend * cb, char *controloid, unsigned long controlops );
+void cb_unregister_all_supported_control( cb_backend * cb );
+void cb_register_supported_control( cb_backend * cb, char *controloid, unsigned long controlops );
+void cb_unregister_supported_control( cb_backend * cb, char *controloid, unsigned long controlops );
+void cb_set_acl_policy(Slapi_PBlock *pb);
+void cb_close_conn_pool(cb_conn_pool * pool);
+void cb_update_monitor_info(Slapi_PBlock * pb, cb_backend_instance * inst,int op);
+void cb_send_ldap_result(Slapi_PBlock *pb, int err, char *m,char *t, int ne, struct berval **urls );
+void cb_stale_all_connections( cb_backend_instance * be);
+int
+cb_config_add_instance_check_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e,
+ int *returncode, char *returntext, void *arg);
+
+
+int chaining_back_add ( Slapi_PBlock *pb );
+int chaining_back_delete ( Slapi_PBlock *pb );
+int chaining_back_compare ( Slapi_PBlock *pb );
+int chaining_back_modify ( Slapi_PBlock *pb );
+int chaining_back_modrdn ( Slapi_PBlock *pb );
+int chaining_back_abandon ( Slapi_PBlock *pb );
+int chaining_back_entry_release ( Slapi_PBlock *pb );
+int chainingdb_next_search_entry( Slapi_PBlock *pb );
+int chainingdb_build_candidate_list ( Slapi_PBlock *pb );
+int chainingdb_start (Slapi_PBlock *pb );
+int chainingdb_bind (Slapi_PBlock *pb );
+int cb_db_size (Slapi_PBlock *pb );
+int cb_back_close (Slapi_PBlock *pb );
+int cb_back_cleanup (Slapi_PBlock *pb );
+
+long cb_atol(char *str);
+
+Slapi_Entry * cb_LDAPMessage2Entry(LDAP * ctx, LDAPMessage * msg, int attrsonly);
+char * cb_urlparse_err2string( int err );
+char * cb_get_rootdn();
+struct berval ** referrals2berval(char ** referrals);
+cb_backend_instance * cb_get_instance(Slapi_Backend * be);
+cb_backend * cb_get_backend_type();
+int cb_debug_on();
+void cb_set_debug(int on);
+int cb_ping_farm(cb_backend_instance *cb,cb_outgoing_conn * cnx,time_t end);
+void cb_update_failed_conn_cpt ( cb_backend_instance *cb ) ;
+void cb_reset_conn_cpt( cb_backend_instance *cb ) ;
+int cb_check_availability( cb_backend_instance *cb, Slapi_PBlock *pb ) ;
+
+time_t current_time();
+char* get_localhost_DNS();
+
+/* this function is called when state of a backend changes */
+void cb_be_state_change (void *handle, char *be_name, int old_be_state, int new_be_state);
+
+#endif
diff --git a/ldap/servers/plugins/chainingdb/cb_abandon.c b/ldap/servers/plugins/chainingdb/cb_abandon.c
new file mode 100644
index 00000000..ca0cfc09
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_abandon.c
@@ -0,0 +1,50 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+/*
+ * Perform an abandon operation
+ *
+ * Returns:
+ * 0 - success
+ * <0 - fail
+ *
+ */
+
+int
+chaining_back_abandon ( Slapi_PBlock *pb )
+{
+ /*
+ * Abandon forwarded to the farm server for scoped
+ * searches only. Done in cb_search.c
+ */
+ return 0;
+}
+
+int cb_check_forward_abandon(cb_backend_instance * cb,Slapi_PBlock * pb, LDAP * ld, int msgid ) {
+
+ int rc;
+ LDAPControl ** ctrls=NULL;
+
+ if (slapi_op_abandoned( pb )) {
+
+ if ((rc=cb_forward_operation(pb)) != LDAP_SUCCESS ) {
+ return 0;
+ }
+
+ if ((rc = cb_update_controls( pb,ld,&ctrls,CB_UPDATE_CONTROLS_ISABANDON )) != LDAP_SUCCESS ) {
+ if ( NULL != ctrls)
+ ldap_controls_free(ctrls);
+ return 0;
+ }
+ rc = ldap_abandon_ext(ld, msgid, ctrls, NULL );
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ if ( NULL != ctrls)
+ ldap_controls_free(ctrls);
+ return 1;
+ }
+ return 0;
+}
diff --git a/ldap/servers/plugins/chainingdb/cb_acl.c b/ldap/servers/plugins/chainingdb/cb_acl.c
new file mode 100644
index 00000000..ce0a6793
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_acl.c
@@ -0,0 +1,60 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+/*
+** generic function to send back results
+** Turn off acl eval on front-end when needed
+*/
+
+void cb_set_acl_policy(Slapi_PBlock *pb) {
+
+ Slapi_Backend *be;
+ cb_backend_instance *cb;
+ int noacl;
+
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+ cb = cb_get_instance(be);
+
+ /* disable acl checking if the local_acl flag is not set
+ or if the associated backend is disabled */
+ noacl=!(cb->local_acl) || cb->associated_be_is_disabled;
+
+ if (noacl) {
+ slapi_pblock_set(pb, SLAPI_PLUGIN_DB_NO_ACL, &noacl);
+ } else {
+ /* Be very conservative about acl evaluation */
+ slapi_pblock_set(pb, SLAPI_PLUGIN_DB_NO_ACL, &noacl);
+ }
+}
+
+int cb_access_allowed(
+ Slapi_PBlock *pb,
+ Slapi_Entry *e, /* The Slapi_Entry */
+ char *attr, /* Attribute of the entry */
+ struct berval *val, /* value of attr. NOT USED */
+ int access, /* access rights */
+ char **errbuf
+ )
+
+{
+
+switch (access) {
+
+ case SLAPI_ACL_ADD:
+ case SLAPI_ACL_DELETE:
+ case SLAPI_ACL_COMPARE:
+ case SLAPI_ACL_WRITE:
+ case SLAPI_ACL_PROXY:
+
+ /* Keep in mind some entries are NOT */
+ /* available for acl evaluation */
+
+ return slapi_access_allowed(pb,e,attr,val,access);
+ default:
+ return LDAP_INSUFFICIENT_ACCESS;
+}
+}
diff --git a/ldap/servers/plugins/chainingdb/cb_add.c b/ldap/servers/plugins/chainingdb/cb_add.c
new file mode 100644
index 00000000..e0b49f7c
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_add.c
@@ -0,0 +1,227 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+/*
+ * Perform an add operation
+ *
+ * Returns:
+ * 0 - success
+ * <0 - fail
+ *
+ */
+
+int
+chaining_back_add ( Slapi_PBlock *pb )
+{
+
+ Slapi_Backend *be;
+ Slapi_Entry *e;
+ cb_backend_instance *cb;
+ LDAPControl **serverctrls=NULL;
+ LDAPControl **ctrls=NULL;
+ int rc,parse_rc,msgid,i;
+ LDAP *ld=NULL;
+ char **referrals=NULL;
+ LDAPMod ** mods;
+ LDAPMessage * res;
+ char *dn,* matched_msg, *error_msg;
+ char *cnxerrbuf=NULL;
+ time_t endtime;
+ cb_outgoing_conn *cnx;
+
+ if ( (rc=cb_forward_operation(pb)) != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, rc, NULL, "Remote data access disabled", 0, NULL );
+ return -1;
+ }
+
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+ cb = cb_get_instance(be);
+
+ /* Update monitor info */
+ cb_update_monitor_info(pb,cb,SLAPI_OPERATION_ADD);
+
+ /* Check wether the chaining BE is available or not */
+ if ( cb_check_availability( cb, pb ) == FARMSERVER_UNAVAILABLE ){
+ return -1;
+ }
+
+
+ slapi_pblock_get( pb, SLAPI_ADD_TARGET, &dn );
+ slapi_pblock_get( pb, SLAPI_ADD_ENTRY, &e );
+
+ /* Check local access controls */
+ if (cb->local_acl && !cb->associated_be_is_disabled) {
+ char * errbuf=NULL;
+ rc = cb_access_allowed (pb, e, NULL, NULL, SLAPI_ACL_ADD, &errbuf);
+ if ( rc != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, rc, NULL, errbuf, 0, NULL );
+ slapi_ch_free((void **)&errbuf);
+ return -1;
+ }
+ }
+
+ /* Build LDAPMod from the SLapi_Entry */
+ cb_eliminate_illegal_attributes(cb,e);
+
+ if ((rc = slapi_entry2mods ((const Slapi_Entry *)e, NULL, &mods)) != LDAP_SUCCESS) {
+ cb_send_ldap_result( pb, rc,NULL,NULL, 0, NULL);
+ return -1;
+ }
+
+ /* Grab a connection handle */
+ if ((rc = cb_get_connection(cb->pool,&ld,&cnx,NULL,&cnxerrbuf)) != LDAP_SUCCESS) {
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR,NULL,cnxerrbuf, 0, NULL);
+ ldap_mods_free(mods,1);
+ slapi_ch_free((void **)&cnxerrbuf);
+ /* ping the farm. If the farm is unreachable, we increment the counter */
+ cb_ping_farm(cb,NULL,0);
+
+ return -1;
+ }
+
+ /* Control management */
+ if ( (rc = cb_update_controls( pb,ld,&ctrls,CB_UPDATE_CONTROLS_ADDAUTH)) != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, rc, NULL,NULL, 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ ldap_mods_free(mods,1);
+ return -1;
+ }
+
+ if ( slapi_op_abandoned( pb )) {
+ cb_release_op_connection(cb->pool,ld,0);
+ ldap_mods_free(mods,1);
+ if ( NULL != ctrls)
+ ldap_controls_free(ctrls);
+ return -1;
+ }
+
+ /* heart-beat management */
+ if (cb->max_idle_time>0)
+ endtime=current_time() + cb->max_idle_time;
+
+ /* Send LDAP operation to the remote host */
+ rc = ldap_add_ext( ld, dn, mods, ctrls, NULL, &msgid );
+
+ if ( NULL != ctrls)
+ ldap_controls_free(ctrls);
+
+ if ( rc != LDAP_SUCCESS ) {
+
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(rc), 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ ldap_mods_free(mods,1);
+ return -1;
+ }
+
+ /*
+ * Poll the server for the results of the add operation.
+ * Check for abandoned operation regularly.
+ */
+
+ while ( 1 ) {
+
+ if (cb_check_forward_abandon(cb,pb,ld,msgid)) {
+ /* connection handle released in cb_check_forward_abandon() */
+ ldap_mods_free(mods,1);
+ return -1;
+ }
+
+ rc = ldap_result( ld, msgid, 0, &cb->abandon_timeout, &res );
+ switch ( rc ) {
+ case -1:
+ cb_send_ldap_result(pb,LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(rc), 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ ldap_mods_free(mods,1);
+ if (res)
+ ldap_msgfree(res);
+ return -1;
+ case 0:
+
+ if ((rc=cb_ping_farm(cb,cnx,endtime)) != LDAP_SUCCESS) {
+
+ /* does not respond. give up and return a*/
+ /* error to the client. */
+
+ /*cb_send_ldap_result(pb,LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(rc), 0, NULL);*/
+ cb_send_ldap_result(pb,LDAP_OPERATIONS_ERROR, NULL, "FARM SERVER TEMPORARY UNAVAILABLE", 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ ldap_mods_free(mods,1);
+ if (res)
+ ldap_msgfree(res);
+ return -1;
+ }
+#ifdef CB_YIELD
+ DS_Sleep(PR_INTERVAL_NO_WAIT);
+#endif
+ break;
+ default:
+ serverctrls=NULL;
+ matched_msg=error_msg=NULL;
+ referrals=NULL;
+
+ parse_rc = ldap_parse_result( ld, res, &rc, &matched_msg,
+ &error_msg, &referrals, &serverctrls, 1 );
+
+ if ( parse_rc != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(parse_rc), 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(parse_rc));
+ ldap_mods_free(mods,1);
+ slapi_ch_free((void **)&matched_msg);
+ slapi_ch_free((void **)&error_msg);
+ if (serverctrls)
+ ldap_controls_free(serverctrls);
+ /* jarnou: free referrals */
+ if (referrals)
+ charray_free(referrals);
+ return -1;
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ struct berval ** refs = referrals2berval(referrals);
+ cb_send_ldap_result( pb, rc, matched_msg, error_msg, 0, refs);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ ldap_mods_free(mods,1);
+ slapi_ch_free((void **)&matched_msg);
+ slapi_ch_free((void **)&error_msg);
+ if (refs)
+ ber_bvecfree(refs);
+ if (referrals)
+ charray_free(referrals);
+ if (serverctrls)
+ ldap_controls_free(serverctrls);
+ return -1;
+ }
+
+ ldap_mods_free(mods,1 );
+ cb_release_op_connection(cb->pool,ld,0);
+
+ /* Add control response sent by the farm server */
+
+ for (i=0; serverctrls && serverctrls[i];i++)
+ slapi_pblock_set( pb, SLAPI_ADD_RESCONTROL, serverctrls[i]);
+ if (serverctrls)
+ ldap_controls_free(serverctrls);
+ /* jarnou: free matched_msg, error_msg, and referrals if necessary */
+ slapi_ch_free((void **)&matched_msg);
+ slapi_ch_free((void **)&error_msg);
+ if (referrals)
+ charray_free(referrals);
+ cb_send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
+
+ slapi_entry_free(e);
+ slapi_pblock_set( pb, SLAPI_ADD_ENTRY, NULL );
+
+ return 0;
+ }
+ }
+
+ /* Never reached */
+}
diff --git a/ldap/servers/plugins/chainingdb/cb_bind.c b/ldap/servers/plugins/chainingdb/cb_bind.c
new file mode 100644
index 00000000..495b672f
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_bind.c
@@ -0,0 +1,290 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+static void
+cb_free_bervals( struct berval **bvs );
+
+
+static int
+cb_sasl_bind_once_s( cb_conn_pool *pool, char *dn, int method, char * mechanism,
+ struct berval *creds, LDAPControl **reqctrls,
+ char **matcheddnp, char **errmsgp, struct berval ***refurlsp,
+ LDAPControl ***resctrlsp , int * status);
+
+/*
+ * Attempt to chain a bind request off to "srvr." We return an LDAP error
+ * code that indicates whether we successfully got a response from the
+ * other server or not. If we succeed, we return LDAP_SUCCESS and *lderrnop
+ * is set to the result code from the remote server.
+ *
+ * Note that in the face of "ldap server down" or "ldap connect failed" errors
+ * we make up to "tries" attempts to bind to the remote server. Since we
+ * are only interested in recovering silently when the remote server is up
+ * but decided to close our connection, we retry without pausing between
+ * attempts.
+ */
+
+static int
+cb_sasl_bind_s(Slapi_PBlock * pb, cb_conn_pool *pool, int tries,
+ char *dn, int method,char * mechanism, struct berval *creds, LDAPControl **reqctrls,
+ char **matcheddnp, char **errmsgp, struct berval ***refurlsp,
+ LDAPControl ***resctrlsp ,int *status) {
+
+ int rc;
+
+ do {
+ /* check to see if operation has been abandoned...*/
+
+ if (LDAP_AUTH_SIMPLE!=method)
+ return LDAP_AUTH_METHOD_NOT_SUPPORTED;
+
+ if ( slapi_op_abandoned( pb )) {
+ rc = LDAP_USER_CANCELLED;
+ } else {
+ rc = cb_sasl_bind_once_s( pool, dn, method,mechanism, creds, reqctrls,
+ matcheddnp, errmsgp, refurlsp, resctrlsp ,status);
+ }
+ } while ( CB_LDAP_CONN_ERROR( rc ) && --tries > 0 );
+
+ return( rc );
+}
+
+static int
+cb_sasl_bind_once_s( cb_conn_pool *pool, char *dn, int method, char * mechanism,
+ struct berval *creds, LDAPControl **reqctrls,
+ char **matcheddnp, char **errmsgp, struct berval ***refurlsp,
+ LDAPControl ***resctrlsp , int * status) {
+
+ int rc, msgid;
+ char **referrals;
+ struct timeval timeout_copy, *timeout;
+ LDAPMessage *result=NULL;
+ LDAP *ld=NULL;
+ char *cnxerrbuf=NULL;
+ cb_outgoing_conn *cnx;
+ int version=LDAP_VERSION3;
+
+ /* Grab an LDAP connection to use for this bind. */
+
+ PR_RWLock_Rlock(pool->rwl_config_lock);
+ timeout_copy.tv_sec = pool->conn.bind_timeout.tv_sec;
+ timeout_copy.tv_usec = pool->conn.bind_timeout.tv_usec;
+ PR_RWLock_Unlock(pool->rwl_config_lock);
+
+ if (( rc = cb_get_connection( pool, &ld ,&cnx, NULL, &cnxerrbuf)) != LDAP_SUCCESS ) {
+ *errmsgp=cnxerrbuf;
+ goto release_and_return;
+ }
+
+ /* Send the bind operation (need to retry on LDAP_SERVER_DOWN) */
+
+ ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
+
+ if (( rc = ldap_sasl_bind( ld, dn, LDAP_SASL_SIMPLE, creds, reqctrls,
+ NULL, &msgid )) != LDAP_SUCCESS ) {
+ goto release_and_return;
+ }
+
+ /* XXXSD what is the exact semantics of bind_to ? it is used to get a
+ connection handle and later to bind ==> bind op may last 2*bind_to
+ from the user point of view
+ confusion comes from teh fact that bind to is used 2for 3 differnt thinks,
+ */
+
+ /*
+ * determine timeout value (how long we will wait for a response)
+ * if timeout is zero'd, we wait indefinitely.
+ */
+ if ( timeout_copy.tv_sec == 0 && timeout_copy.tv_usec == 0 ) {
+ timeout = NULL;
+ } else {
+ timeout = &timeout_copy;
+ }
+
+ /*
+ * Wait for a result.
+ */
+ rc = ldap_result( ld, msgid, 1, timeout, &result );
+
+ /*
+ * Interpret the result.
+ */
+
+ if ( rc == 0 ) { /* timeout */
+ /*
+ * Timed out waiting for a reply from the server.
+ */
+ rc = LDAP_TIMEOUT;
+ } else if ( rc < 0 ) {
+
+ /* Some other error occurred (no result received). */
+ char * matcheddnp2, * errmsgp2;
+ matcheddnp2=errmsgp2=NULL;
+
+ rc = ldap_get_lderrno( ld, &matcheddnp2, &errmsgp2 );
+
+ /* Need to allocate errmsgs */
+ if (matcheddnp2)
+ *matcheddnp=slapi_ch_strdup(matcheddnp2);
+ if (errmsgp2)
+ *errmsgp=slapi_ch_strdup(errmsgp2);
+
+ if ( LDAP_SUCCESS != rc ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "cb_sasl_bind_once_s failed (%s)\n",ldap_err2string(rc));
+ }
+ } else {
+
+ /* Got a result from remote server -- parse it.*/
+
+ char * matcheddnp2, * errmsgp2;
+ matcheddnp2=errmsgp2=NULL;
+ *resctrlsp=NULL;
+ rc = ldap_parse_result( ld, result, status, matcheddnp, errmsgp,
+ &referrals, resctrlsp, 1 );
+ if ( referrals != NULL ) {
+ *refurlsp = referrals2berval( referrals );
+ ldap_value_free( referrals );
+ }
+ /* realloc matcheddn & errmsg because the mem alloc model */
+ /* may differ from malloc */
+ if (matcheddnp2) {
+ *matcheddnp=slapi_ch_strdup(matcheddnp2);
+ ldap_memfree(matcheddnp2);
+ }
+ if (errmsgp2) {
+ *errmsgp=slapi_ch_strdup(errmsgp2);
+ ldap_memfree(errmsgp2);
+ }
+
+ }
+
+release_and_return:
+ if ( ld != NULL ) {
+ cb_release_op_connection( pool, ld, CB_LDAP_CONN_ERROR( rc ));
+ }
+
+ return( rc );
+}
+
+int
+chainingdb_bind( Slapi_PBlock *pb ) {
+
+ int status=LDAP_SUCCESS;
+ int allocated_errmsg;
+ int rc=LDAP_SUCCESS;
+ cb_backend_instance *cb;
+ Slapi_Backend *be;
+ char *dn;
+ int method;
+ struct berval *creds, **urls;
+ char *matcheddn,*errmsg;
+ LDAPControl **reqctrls, **resctrls, **ctrls;
+ char * mechanism;
+ int freectrls=1;
+ int bind_retry;
+
+ if ( LDAP_SUCCESS != (rc = cb_forward_operation(pb) )) {
+ cb_send_ldap_result( pb, rc, NULL, "Chaining forbidden", 0, NULL );
+ return SLAPI_BIND_FAIL;
+ }
+
+ ctrls=NULL;
+ /* don't add proxy auth control. use this call to check for supported */
+ /* controls only. */
+ if ( LDAP_SUCCESS != ( rc = cb_update_controls( pb, NULL, &ctrls, 0 )) ) {
+ cb_send_ldap_result( pb, rc, NULL, NULL, 0, NULL );
+ if (ctrls)
+ ldap_controls_free(ctrls);
+ return SLAPI_BIND_FAIL;
+ }
+ if (ctrls)
+ ldap_controls_free(ctrls);
+
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+ slapi_pblock_get( pb, SLAPI_BIND_TARGET, &dn );
+ slapi_pblock_get( pb, SLAPI_BIND_METHOD, &method );
+ slapi_pblock_get( pb, SLAPI_BIND_SASLMECHANISM, &mechanism);
+ slapi_pblock_get( pb, SLAPI_BIND_CREDENTIALS, &creds );
+ slapi_pblock_get( pb, SLAPI_REQCONTROLS, &reqctrls );
+ cb = cb_get_instance(be);
+
+ if ( NULL == dn )
+ dn="";
+
+ /* always allow noauth simple binds */
+ if (( method == LDAP_AUTH_SIMPLE) && creds->bv_len == 0 ) {
+ return( SLAPI_BIND_ANONYMOUS );
+ }
+
+ cb_update_monitor_info(pb,cb,SLAPI_OPERATION_BIND);
+
+ matcheddn=errmsg=NULL;
+ allocated_errmsg = 0;
+ resctrls=NULL;
+ urls=NULL;
+
+ /* Check wether the chaining BE is available or not */
+ if ( cb_check_availability( cb, pb ) == FARMSERVER_UNAVAILABLE ){
+ return -1;
+ }
+
+ PR_RWLock_Rlock(cb->rwl_config_lock);
+ bind_retry=cb->bind_retry;
+ PR_RWLock_Unlock(cb->rwl_config_lock);
+
+ if ( LDAP_SUCCESS == (rc = cb_sasl_bind_s(pb, cb->bind_pool, bind_retry, dn,method,mechanism,
+ creds,reqctrls,&matcheddn,&errmsg,&urls,&resctrls, &status))) {
+ rc = status;
+ allocated_errmsg = 1;
+ } else
+ if ( LDAP_USER_CANCELLED != rc ) {
+ errmsg = ldap_err2string( rc );
+ if (rc == LDAP_TIMEOUT) {
+ cb_ping_farm(cb,NULL,NULL);
+ }
+ rc = LDAP_OPERATIONS_ERROR;
+ }
+
+ if ( rc != LDAP_USER_CANCELLED ) { /* not abandoned */
+ if ( resctrls != NULL ) {
+ slapi_pblock_set( pb, SLAPI_RESCONTROLS, resctrls );
+ freectrls=0;
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, rc, matcheddn, errmsg, 0, urls );
+ }
+ }
+
+ if ( urls != NULL ) {
+ cb_free_bervals( urls );
+ }
+ if ( freectrls && ( resctrls != NULL )) {
+ ldap_controls_free( resctrls );
+ }
+ slapi_ch_free((void **)& matcheddn );
+ if ( allocated_errmsg && errmsg != NULL ) {
+ slapi_ch_free((void **)& errmsg );
+ }
+
+ return ((rc == LDAP_SUCCESS ) ? SLAPI_BIND_SUCCESS : SLAPI_BIND_FAIL );
+}
+
+static void
+cb_free_bervals( struct berval **bvs )
+{
+ int i;
+
+ if ( bvs != NULL ) {
+ for ( i = 0; bvs[ i ] != NULL; ++i ) {
+ slapi_ch_free( (void **)&bvs[ i ] );
+ }
+ }
+ slapi_ch_free( (void **)&bvs );
+}
+
diff --git a/ldap/servers/plugins/chainingdb/cb_cleanup.c b/ldap/servers/plugins/chainingdb/cb_cleanup.c
new file mode 100644
index 00000000..b034b0a1
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_cleanup.c
@@ -0,0 +1,22 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+/*
+** cLeanup a chaining backend instance
+*/
+
+int cb_back_cleanup( Slapi_PBlock *pb )
+{
+
+ /*
+ ** Connections have been closed in cb_back_close()
+ ** For now, don't do more
+ */
+
+ return 0;
+}
+
diff --git a/ldap/servers/plugins/chainingdb/cb_close.c b/ldap/servers/plugins/chainingdb/cb_close.c
new file mode 100644
index 00000000..3ef52e32
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_close.c
@@ -0,0 +1,67 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+/*
+** Close a chaining backend instance
+** Should be followed by a cleanup
+*/
+
+int cb_back_close( Slapi_PBlock *pb )
+{
+ Slapi_Backend * be;
+ cb_backend_instance * inst;
+ int rc;
+
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+ if (be == NULL) {
+
+ cb_backend * cb = cb_get_backend_type();
+ CB_ASSERT(cb!=NULL);
+
+ slapi_config_remove_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_POSTOP, cb->configDN, LDAP_SCOPE_BASE,
+ "(objectclass=*)",cb_config_modify_callback);
+ slapi_config_remove_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, cb->configDN, LDAP_SCOPE_BASE,
+ "(objectclass=*)",cb_config_modify_check_callback);
+
+ slapi_config_remove_callback(SLAPI_OPERATION_ADD, DSE_FLAG_POSTOP, cb->configDN, LDAP_SCOPE_BASE,
+ "(objectclass=*)",cb_config_add_callback);
+ slapi_config_remove_callback(SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, cb->configDN, LDAP_SCOPE_BASE,
+ "(objectclass=*)",cb_config_add_check_callback);
+
+ slapi_config_remove_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP, cb->configDN, LDAP_SCOPE_BASE,
+ "(objectclass=*)",cb_config_search_callback);
+
+ slapi_config_remove_callback(SLAPI_OPERATION_ADD, DSE_FLAG_POSTOP, cb->pluginDN,
+ LDAP_SCOPE_SUBTREE, CB_CONFIG_INSTANCE_FILTER, cb_config_add_instance_callback);
+
+ return 0;
+ }
+
+ /* XXXSD: temp fix . Sometimes, this functions */
+ /* gets called with a ldbm backend instance... */
+
+ {
+ const char * betype = slapi_be_gettype(be);
+ if (!betype || strcasecmp(betype,CB_CHAINING_BACKEND_TYPE)) {
+
+ slapi_log_error( SLAPI_LOG_FATAL, CB_PLUGIN_SUBSYSTEM,
+ "Wrong database type.\n");
+ return 0;
+ }
+ }
+
+ inst = cb_get_instance(be);
+ CB_ASSERT( inst!=NULL );
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,"Stopping chaining database instance %s\n",
+ inst->configDn);
+ /* emulate a backend instance deletion */
+ /* to clean up everything */
+ cb_instance_delete_config_callback(NULL, NULL,NULL, &rc, NULL, inst);
+
+ return 0;
+}
diff --git a/ldap/servers/plugins/chainingdb/cb_compare.c b/ldap/servers/plugins/chainingdb/cb_compare.c
new file mode 100644
index 00000000..ebce1645
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_compare.c
@@ -0,0 +1,217 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+/*
+ * Perform a compare operation
+ *
+ * Returns:
+ * 0 - success
+ * <0 - fail
+ *
+ */
+
+int
+chaining_back_compare ( Slapi_PBlock *pb )
+{
+
+ Slapi_Backend * be;
+ cb_backend_instance *cb=NULL;
+ struct berval *bval=NULL;
+ LDAPControl **ctrls, **serverctrls;
+ int rc,parse_rc,msgid,i,checkacl;
+ LDAP *ld=NULL;
+ char **referrals=NULL;
+ LDAPMessage * res;
+ char *type,*dn,* matched_msg, *error_msg;
+ char *cnxerrbuf=NULL;
+ time_t endtime;
+ cb_outgoing_conn *cnx;
+
+ if ( LDAP_SUCCESS != (rc=cb_forward_operation(pb) )) {
+ cb_send_ldap_result( pb, rc, NULL, "Chaining forbidden", 0, NULL );
+ return -1;
+ }
+
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+ cb = cb_get_instance(be);
+
+ cb_update_monitor_info(pb,cb,SLAPI_OPERATION_COMPARE);
+
+ /* Check wether the chaining BE is available or not */
+ if ( cb_check_availability( cb, pb ) == FARMSERVER_UNAVAILABLE ){
+ return -1;
+ }
+
+ slapi_pblock_get( pb, SLAPI_COMPARE_TARGET, &dn );
+ slapi_pblock_get( pb, SLAPI_COMPARE_TYPE, &type );
+ slapi_pblock_get( pb, SLAPI_COMPARE_VALUE, &bval );
+
+ /*
+ * Check local acls
+ * No need to lock the config to access cb->local_acl
+ */
+
+ checkacl=cb->local_acl && !cb->associated_be_is_disabled;
+
+ if (checkacl) {
+ char * errbuf=NULL;
+ Slapi_Entry *te = slapi_entry_alloc();
+ slapi_entry_set_dn(te,slapi_ch_strdup(dn));
+ rc = cb_access_allowed (pb, te, type, bval, SLAPI_ACL_COMPARE,&errbuf);
+ slapi_entry_free(te);
+
+ if ( rc != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, rc, NULL, errbuf, 0, NULL );
+ slapi_ch_free((void **) &errbuf);
+ return 1;
+ }
+ }
+
+ /*
+ * Grab a connection handle
+ */
+
+ if ((rc = cb_get_connection(cb->pool,&ld,&cnx,NULL,&cnxerrbuf)) != LDAP_SUCCESS) {
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, cnxerrbuf, 0, NULL);
+ slapi_ch_free((void **)&cnxerrbuf);
+ /* ping the farm. If the farm is unreachable, we increment the counter */
+ cb_ping_farm(cb,NULL,0);
+ return 1;
+ }
+
+ /*
+ * Control management
+ */
+
+ if ( (rc = cb_update_controls( pb,ld,&ctrls,CB_UPDATE_CONTROLS_ADDAUTH )) != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, rc, NULL,NULL, 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ return 1;
+ }
+
+ if ( slapi_op_abandoned( pb )) {
+ cb_release_op_connection(cb->pool,ld,0);
+ if ( NULL != ctrls)
+ ldap_controls_free(ctrls);
+ return -1;
+ }
+
+ /* heart-beat management */
+ if (cb->max_idle_time>0)
+ endtime=current_time() + cb->max_idle_time;
+
+ /*
+ * Send LDAP operation to the remote host
+ */
+
+ rc = ldap_compare_ext( ld, dn, type, bval, ctrls, NULL, &msgid );
+ if ( NULL != ctrls)
+ ldap_controls_free(ctrls);
+
+ if ( rc != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(rc), 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ return 1;
+ }
+
+ while ( 1 ) {
+
+ if (cb_check_forward_abandon(cb,pb,ld,msgid)) {
+ return -1;
+ }
+
+ /* No need to lock the config to access cb->abandon_timeout */
+ rc = ldap_result( ld, msgid, 0, &cb->abandon_timeout, &res );
+ switch ( rc ) {
+ case -1:
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(rc), 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ if (res)
+ ldap_msgfree(res);
+ return 1;
+ case 0:
+ if ((rc=cb_ping_farm(cb,cnx,endtime)) != LDAP_SUCCESS) {
+
+ /* does not respond. give up and return a*/
+ /* error to the client. */
+
+ /*cb_send_ldap_result(pb,LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(rc), 0, NULL);*/
+ cb_send_ldap_result(pb,LDAP_OPERATIONS_ERROR, NULL, "FARM SERVER TEMPORARY UNAVAILABLE", 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ if (res)
+ ldap_msgfree(res);
+ return 1;
+ }
+#ifdef CB_YIELD
+ DS_Sleep(PR_INTERVAL_NO_WAIT);
+#endif
+ break;
+ default:
+ matched_msg=error_msg=NULL;
+ parse_rc = ldap_parse_result( ld, res, &rc, &matched_msg,
+ &error_msg, &referrals, &serverctrls, 1 );
+ if ( parse_rc != LDAP_SUCCESS ) {
+
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(parse_rc), 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(parse_rc));
+ slapi_ch_free((void **)&matched_msg);
+ slapi_ch_free((void **)&error_msg);
+ if (serverctrls)
+ ldap_controls_free(serverctrls);
+ /* jarnou: free referrals */
+ if (referrals)
+ charray_free(referrals);
+ return 1;
+ }
+
+ switch ( rc ) {
+
+ case LDAP_COMPARE_TRUE:
+ case LDAP_COMPARE_FALSE:
+ break;
+ default: {
+ struct berval ** refs = referrals2berval(referrals);
+
+ cb_send_ldap_result( pb, rc, matched_msg, error_msg, 0, refs);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ slapi_ch_free((void **)&matched_msg);
+ slapi_ch_free((void **)&error_msg);
+ if (refs)
+ ber_bvecfree(refs);
+ if (referrals)
+ charray_free(referrals);
+ if (serverctrls)
+ ldap_controls_free(serverctrls);
+ return 1;
+ }
+ }
+
+ /* Add control response sent by the farm server */
+
+ for (i=0; serverctrls && serverctrls[i];i++)
+ slapi_pblock_set( pb, SLAPI_ADD_RESCONTROL, serverctrls[i]);
+ if (serverctrls)
+ ldap_controls_free(serverctrls);
+ /* jarnou: free matched_msg, error_msg, and referrals if necessary */
+ slapi_ch_free((void **)&matched_msg);
+ slapi_ch_free((void **)&error_msg);
+ if (referrals)
+ charray_free(referrals);
+
+ cb_send_ldap_result( pb, rc , NULL, NULL, 0, NULL );
+ cb_release_op_connection(cb->pool,ld,0);
+ return 0;
+ }
+ }
+
+ /* Never reached */
+ /* return 0; */
+}
diff --git a/ldap/servers/plugins/chainingdb/cb_config.c b/ldap/servers/plugins/chainingdb/cb_config.c
new file mode 100644
index 00000000..13dd7b22
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_config.c
@@ -0,0 +1,600 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+#include <errno.h>
+
+/* Forward declarations */
+
+static int cb_parse_config_entry(cb_backend * cb, Slapi_Entry *e);
+
+/* body starts here */
+
+/* Used to add an array of entries, like the one above and
+** cb_instance_skeleton_entries to the dse.
+** Returns 0 on success.
+*/
+
+
+
+int cb_config_add_dse_entries(cb_backend *cb, char **entries, char *string1, char *string2, char *string3)
+{
+ int x;
+ Slapi_Entry *e;
+ Slapi_PBlock *util_pb = NULL;
+ int res, rc = 0;
+ char entry_string[CB_BUFSIZE];
+
+ for(x = 0; strlen(entries[x]) > 0; x++) {
+ util_pb = slapi_pblock_new();
+ sprintf(entry_string, entries[x], string1, string2, string3);
+ e = slapi_str2entry(entry_string, 0);
+ slapi_add_entry_internal_set_pb(util_pb, e, NULL, cb->identity, 0);
+ slapi_add_internal_pb(util_pb);
+ slapi_pblock_get(util_pb, SLAPI_PLUGIN_INTOP_RESULT, &res);
+ if ( LDAP_SUCCESS != res && LDAP_ALREADY_EXISTS != res ) {
+ char ebuf[ BUFSIZ ];
+ slapi_log_error(SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Unable to add config entry (%s) to the DSE: %s\n",
+ escape_string(slapi_entry_get_dn(e), ebuf),
+ ldap_err2string(res));
+ rc = res;
+ slapi_pblock_destroy(util_pb);
+ break;
+ }
+ slapi_pblock_destroy(util_pb);
+ }
+ return rc;
+}
+
+/*
+** Try to read the entry cn=config,cn=chaining database,cn=plugins,cn=config
+** If the entry is there, then process the configuration information it stores.
+** If it is missing, create it with default configuration.
+** The default configuration is taken from the default entry if it exists
+*/
+
+int cb_config_load_dse_info(Slapi_PBlock * pb) {
+
+ Slapi_PBlock *search_pb,*default_pb;
+ Slapi_Entry **entries = NULL;
+ Slapi_Entry *configEntry=NULL;
+ int res,default_res,i;
+ char defaultDn[CB_BUFSIZE];
+ cb_backend *cb;
+
+ slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &cb );
+
+ /* Get global configuration entry */
+ search_pb = slapi_pblock_new();
+ slapi_search_internal_set_pb(search_pb, cb->configDN, LDAP_SCOPE_BASE,
+ "objectclass=*", NULL, 0, NULL, NULL, cb->identity, 0);
+ slapi_search_internal_pb (search_pb);
+ slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &res);
+
+ if ( LDAP_SUCCESS == res ) {
+ slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ if (NULL == entries || entries[0] == NULL) {
+ slapi_log_error(SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Error accessing entry <%s>\n",cb->configDN);
+ slapi_free_search_results_internal(search_pb);
+ slapi_pblock_destroy(search_pb);
+ return 1;
+ }
+ configEntry=entries[0];
+ } else
+ if ( LDAP_NO_SUCH_OBJECT == res ) {
+ /* Don't do anything. The default conf is used */
+ configEntry=NULL;
+ } else {
+ slapi_free_search_results_internal(search_pb);
+ slapi_pblock_destroy(search_pb);
+ slapi_log_error(SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Error accessing entry <%s> (%s)\n",cb->configDN,ldap_err2string(res));
+ return 1;
+ }
+
+ /* Parse the configuration entry */
+ /* Default config if configEntry is NULL*/
+
+ cb_parse_config_entry(cb, configEntry);
+ slapi_free_search_results_internal(search_pb);
+ slapi_pblock_destroy(search_pb);
+
+ /*
+ ** Parse the chaining backend instances
+ ** Immediate subordinates of cn=<plugin name>,cn=plugins,cn=config
+ */
+
+ search_pb = slapi_pblock_new();
+
+ slapi_search_internal_set_pb(search_pb, cb->pluginDN, LDAP_SCOPE_ONELEVEL,
+ CB_CONFIG_INSTANCE_FILTER,NULL,0,NULL,NULL,cb->identity, 0);
+ slapi_search_internal_pb (search_pb);
+ slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &res);
+ if (res != LDAP_SUCCESS) {
+ slapi_log_error(SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Error accessing the config DSE (%s)\n",ldap_err2string(res));
+ slapi_free_search_results_internal(search_pb);
+ slapi_pblock_destroy(search_pb);
+ return 1;
+ }
+
+ /* Get the default instance value entry if it exists */
+ /* else create it */
+
+ sprintf(defaultDn,"cn=default instance config,%s",cb->pluginDN);
+
+ default_pb = slapi_pblock_new();
+ slapi_search_internal_set_pb(default_pb, defaultDn, LDAP_SCOPE_BASE,
+ "objectclass=*", NULL, 0, NULL, NULL, cb->identity, 0);
+ slapi_search_internal_pb (default_pb);
+ slapi_pblock_get(default_pb, SLAPI_PLUGIN_INTOP_RESULT, &default_res);
+ if (LDAP_SUCCESS != default_res) {
+ cb_create_default_backend_instance_config(cb);
+ }
+
+ slapi_free_search_results_internal(default_pb);
+ slapi_pblock_destroy(default_pb);
+
+ slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ for (i=0; entries && entries[i]; i++) {
+ int retcode;
+ char * aDn=slapi_entry_get_dn(entries[i]);
+ slapi_dn_normalize(aDn);
+
+ cb_instance_add_config_callback(pb,entries[i],NULL,&retcode,NULL,cb);
+ }
+
+ slapi_free_search_results_internal(search_pb);
+ slapi_pblock_destroy(search_pb);
+
+
+ /* Add callbacks */
+ slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, cb->configDN,
+ LDAP_SCOPE_BASE, "(objectclass=*)",cb_config_modify_check_callback, (void *) cb);
+ slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_POSTOP, cb->configDN,
+ LDAP_SCOPE_BASE, "(objectclass=*)",cb_config_modify_callback, (void *) cb);
+
+ slapi_config_register_callback(SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, cb->configDN,
+ LDAP_SCOPE_BASE, "(objectclass=*)",cb_config_add_check_callback, (void *) cb);
+ slapi_config_register_callback(SLAPI_OPERATION_ADD, DSE_FLAG_POSTOP, cb->configDN,
+ LDAP_SCOPE_BASE, "(objectclass=*)",cb_config_add_callback, (void *) cb);
+
+ slapi_config_register_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP, cb->configDN,
+ LDAP_SCOPE_BASE, "(objectclass=*)",cb_config_search_callback, (void *) cb);
+
+ /* instance creation */
+ slapi_config_register_callback(SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, cb->pluginDN,
+ LDAP_SCOPE_SUBTREE, CB_CONFIG_INSTANCE_FILTER, cb_config_add_instance_check_callback, (void *) cb);
+
+ slapi_config_register_callback(SLAPI_OPERATION_ADD, DSE_FLAG_POSTOP, cb->pluginDN,
+ LDAP_SCOPE_SUBTREE, CB_CONFIG_INSTANCE_FILTER, cb_config_add_instance_callback, (void *) cb);
+
+ return 0;
+}
+
+/* Check validity of the modification */
+
+int cb_config_add_check_callback(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* e2, int *returncode,
+ char *returntext, void *arg)
+{
+ Slapi_Attr *attr = NULL;
+ Slapi_Value *sval;
+ struct berval * bval;
+ int i;
+ cb_backend *cb = (cb_backend *) arg;
+
+ CB_ASSERT (cb!=NULL);
+
+ for (slapi_entry_first_attr(e, &attr); attr; slapi_entry_next_attr(e, attr, &attr)) {
+ char * attr_name=NULL;
+ slapi_attr_get_type(attr, &attr_name);
+
+ if ( !strcasecmp ( attr_name, CB_CONFIG_GLOBAL_FORWARD_CTRLS )) {
+ /* First, parse the values to make sure they are valid */
+ i = slapi_attr_first_value(attr, &sval);
+ while (i != -1 ) {
+ bval = (struct berval *) slapi_value_get_berval(sval);
+ if (!cb_is_control_forwardable(cb,bval->bv_val)) {
+ slapi_log_error(SLAPI_LOG_PLUGIN,CB_PLUGIN_SUBSYSTEM,
+ "control %s can't be forwarded.\n",bval->bv_val);
+ *returncode=LDAP_CONSTRAINT_VIOLATION;
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+ i = slapi_attr_next_value(attr, i, &sval);
+ }
+ }
+ }
+ *returncode=LDAP_SUCCESS;
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+/*
+** Global config is beeing added
+** Take the new values into account
+*/
+
+int
+cb_config_add_callback(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* e2, int *returncode,
+ char *returntext, void *arg)
+{
+ Slapi_Attr *attr = NULL;
+ Slapi_Value *sval;
+ struct berval * bval;
+ int i;
+ cb_backend *cb = (cb_backend *) arg;
+
+ CB_ASSERT (cb!=NULL);
+
+ for (slapi_entry_first_attr(e, &attr); attr; slapi_entry_next_attr(e, attr, &attr)) {
+ char * attr_name=NULL;
+ slapi_attr_get_type(attr, &attr_name);
+
+ if ( !strcasecmp ( attr_name, CB_CONFIG_GLOBAL_FORWARD_CTRLS )) {
+ /* First, parse the values to make sure they are valid */
+ i = slapi_attr_first_value(attr, &sval);
+ while (i != -1 ) {
+ bval = (struct berval *) slapi_value_get_berval(sval);
+ if (!cb_is_control_forwardable(cb,bval->bv_val)) {
+ slapi_log_error(SLAPI_LOG_PLUGIN,CB_PLUGIN_SUBSYSTEM,
+ "control %s can't be forwarded.\n",bval->bv_val);
+ *returncode=LDAP_CONSTRAINT_VIOLATION;
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+ i = slapi_attr_next_value(attr, i, &sval);
+ }
+ /* second pass. apply changes */
+ cb_unregister_all_supported_control(cb);
+ i = slapi_attr_first_value(attr, &sval);
+ while (i != -1 ) {
+ bval = (struct berval *) slapi_value_get_berval(sval);
+ cb_register_supported_control(cb,bval->bv_val,0);
+ i = slapi_attr_next_value(attr, i, &sval);
+ }
+ }
+ }
+ *returncode=LDAP_SUCCESS;
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+int
+cb_config_search_callback(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* e2, int *returncode,
+ char *returntext, void *arg) {
+
+ cb_backend *cb = (cb_backend *) arg;
+ struct berval val;
+ struct berval *vals[2];
+ int i = 0;
+
+ CB_ASSERT (cb!=NULL);
+
+ vals[0] = &val;
+ vals[1] = NULL;
+
+ /* naming attribute */
+ val.bv_val = "config";
+ val.bv_len = strlen( val.bv_val );
+ slapi_entry_attr_replace( e, "cn", (struct berval **)vals );
+
+ /* objectclass attribute */
+ val.bv_val = "top";
+ val.bv_len = strlen( val.bv_val );
+ slapi_entry_attr_replace( e, "objectclass", (struct berval **)vals );
+ val.bv_val = CB_CONFIG_EXTENSIBLEOCL;
+ val.bv_len = strlen( val.bv_val );
+ slapi_entry_attr_merge( e, "objectclass", (struct berval **)vals );
+
+ /* other attributes */
+
+ PR_RWLock_Rlock(cb->config.rwl_config_lock);
+
+ for (i=0; cb->config.forward_ctrls && cb->config.forward_ctrls[i] ; i++) {
+ val.bv_val=cb->config.forward_ctrls[i];
+ val.bv_len = strlen( val.bv_val );
+ if (i==0)
+ slapi_entry_attr_replace( e, CB_CONFIG_GLOBAL_FORWARD_CTRLS, (struct berval **)vals );
+ else
+ slapi_entry_attr_merge( e, CB_CONFIG_GLOBAL_FORWARD_CTRLS, (struct berval **)vals );
+ }
+
+ for (i=0;cb->config.chaining_components && cb->config.chaining_components[i];i++) {
+ val.bv_val=cb->config.chaining_components[i];
+ val.bv_len = strlen( val.bv_val );
+ if (i==0)
+ slapi_entry_attr_replace( e, CB_CONFIG_GLOBAL_CHAINING_COMPONENTS,
+ (struct berval **)vals );
+ else
+ slapi_entry_attr_merge( e, CB_CONFIG_GLOBAL_CHAINING_COMPONENTS,
+ (struct berval **)vals );
+ }
+
+ for (i=0; cb->config.chainable_components && cb->config.chainable_components[i]; i++) {
+ val.bv_val=cb->config.chainable_components[i];
+ val.bv_len = strlen( val.bv_val );
+ if (i==0)
+ slapi_entry_attr_replace( e, CB_CONFIG_GLOBAL_CHAINABLE_COMPONENTS,
+ (struct berval **)vals );
+ else
+ slapi_entry_attr_merge( e, CB_CONFIG_GLOBAL_CHAINABLE_COMPONENTS,
+ (struct berval **)vals );
+ }
+
+
+ PR_RWLock_Unlock(cb->config.rwl_config_lock);
+
+ *returncode = LDAP_SUCCESS;
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+/* Check validity of the modification */
+
+int
+cb_config_modify_check_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode,
+ char *returntext, void *arg)
+{
+ LDAPMod **mods;
+ char *attr_name;
+ int i,j;
+ cb_backend *cb = (cb_backend *) arg;
+
+ CB_ASSERT (cb!=NULL);
+
+ slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods );
+
+ for (i = 0; mods[i] ; i++) {
+ attr_name = mods[i]->mod_type;
+
+ if ( !strcasecmp ( attr_name, CB_CONFIG_GLOBAL_FORWARD_CTRLS )) {
+ char * config_attr_value;
+ for (j = 0; mods[i]->mod_bvalues && mods[i]->mod_bvalues[j]; j++) {
+ config_attr_value = (char *) mods[i]->mod_bvalues[j]->bv_val;
+ if (!cb_is_control_forwardable(cb,config_attr_value)) {
+ slapi_log_error(SLAPI_LOG_PLUGIN,CB_PLUGIN_SUBSYSTEM,
+ "control %s can't be forwarded.\n",config_attr_value);
+ *returncode=LDAP_CONSTRAINT_VIOLATION;
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+ }
+ }
+ }
+ *returncode=LDAP_SUCCESS;
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+int
+cb_config_modify_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode,
+ char *returntext, void *arg)
+{
+ LDAPMod **mods;
+ char *attr_name;
+ int i,j;
+ cb_backend *cb = (cb_backend *) arg;
+
+ CB_ASSERT (cb!=NULL);
+
+ slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods );
+
+ for (i = 0; mods[i] ; i++) {
+ attr_name = mods[i]->mod_type;
+
+ if ( !strcasecmp ( attr_name, CB_CONFIG_GLOBAL_FORWARD_CTRLS )) {
+ char * config_attr_value;
+ int done=0;
+ for (j = 0; mods[i]->mod_bvalues && mods[i]->mod_bvalues[j]; j++) {
+ config_attr_value = (char *) mods[i]->mod_bvalues[j]->bv_val;
+ if (!cb_is_control_forwardable(cb,config_attr_value)) {
+ slapi_log_error(SLAPI_LOG_PLUGIN,CB_PLUGIN_SUBSYSTEM,
+ "control %s can't be forwarded.\n",config_attr_value);
+ *returncode=LDAP_CONSTRAINT_VIOLATION;
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+ if ( mods[i]->mod_op & LDAP_MOD_REPLACE) {
+ if (!done) {
+ cb_unregister_all_supported_control(cb);
+ done=1;
+ }
+ cb_register_supported_control(cb,config_attr_value,0);
+ } else
+ if ( (mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_ADD) {
+ cb_register_supported_control(cb,config_attr_value,0);
+ } else
+ if ( (mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_DELETE) {
+ cb_unregister_supported_control(cb,config_attr_value,0);
+ }
+ }
+ if (NULL == mods[i]->mod_bvalues)
+ cb_unregister_all_supported_control(cb);
+ } else
+ if ( !strcasecmp ( attr_name, CB_CONFIG_GLOBAL_DEBUG )) {
+ /* assume single-valued */
+ if (mods[i]->mod_op & LDAP_MOD_DELETE)
+ cb_set_debug(0);
+ else if ((mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_ADD)
+ cb_set_debug(1);
+ } else
+ if ( !strcasecmp ( attr_name, CB_CONFIG_GLOBAL_CHAINING_COMPONENTS )) {
+ char * config_attr_value;
+ int done=0;
+
+ PR_RWLock_Wlock(cb->config.rwl_config_lock);
+
+ for (j = 0; mods[i]->mod_bvalues && mods[i]->mod_bvalues[j]; j++) {
+ config_attr_value = (char *) mods[i]->mod_bvalues[j]->bv_val;
+ if ( mods[i]->mod_op & LDAP_MOD_REPLACE) {
+ if (!done) {
+ charray_free(cb->config.chaining_components);
+ cb->config.chaining_components=NULL;
+ done=1;
+ }
+ /* XXXSD assume dn. Normalize it */
+ charray_add(&cb->config.chaining_components,
+ slapi_dn_normalize(slapi_ch_strdup(config_attr_value)));
+ } else
+ if ( (mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_ADD) {
+ charray_add(&cb->config.chaining_components,
+ slapi_dn_normalize(slapi_ch_strdup(config_attr_value)));
+ } else
+ if ( (mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_DELETE) {
+ charray_remove(cb->config.chaining_components,
+ slapi_dn_normalize(slapi_ch_strdup(config_attr_value)));
+ }
+ }
+ if (NULL == mods[i]->mod_bvalues) {
+ charray_free(cb->config.chaining_components);
+ cb->config.chaining_components=NULL;
+ }
+
+ PR_RWLock_Unlock(cb->config.rwl_config_lock);
+ } else
+ if ( !strcasecmp ( attr_name, CB_CONFIG_GLOBAL_CHAINABLE_COMPONENTS )) {
+ char * config_attr_value;
+ int done=0;
+
+ PR_RWLock_Wlock(cb->config.rwl_config_lock);
+
+ for (j = 0; mods[i]->mod_bvalues && mods[i]->mod_bvalues[j]; j++) {
+ config_attr_value = (char *) mods[i]->mod_bvalues[j]->bv_val;
+ if ( mods[i]->mod_op & LDAP_MOD_REPLACE) {
+ if (!done) {
+ charray_free(cb->config.chainable_components);
+ cb->config.chainable_components=NULL;
+ done=1;
+ }
+ charray_add(&cb->config.chainable_components,
+ slapi_dn_normalize(slapi_ch_strdup(config_attr_value)
+));
+ } else
+ if ( (mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_ADD) {
+ charray_add(&cb->config.chainable_components,
+ slapi_dn_normalize(slapi_ch_strdup(config_attr_value)
+));
+ } else
+ if ( (mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_DELETE) {
+ charray_remove(cb->config.chainable_components,
+ slapi_dn_normalize(slapi_ch_strdup(config_attr_value)
+));
+ }
+ }
+ if (NULL == mods[i]->mod_bvalues) {
+ charray_free(cb->config.chainable_components);
+ cb->config.chainable_components=NULL;
+ }
+
+ PR_RWLock_Unlock(cb->config.rwl_config_lock);
+ }
+
+
+ }
+ *returncode=LDAP_SUCCESS;
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+/*
+** Creation of a new backend instance
+*/
+
+int
+cb_config_add_instance_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode,
+ char *returntext, void *arg)
+{
+ cb_backend *cb=(cb_backend *)arg;
+ CB_ASSERT(cb!=NULL);
+ cb_instance_add_config_callback(pb,entryBefore,NULL,returncode,returntext,cb);
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+int
+cb_config_add_instance_check_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode,
+ char *returntext, void *arg)
+{
+ cb_backend *cb=(cb_backend *)arg;
+ CB_ASSERT(cb!=NULL);
+ return cb_instance_add_config_check_callback(pb,entryBefore,NULL,returncode,returntext,cb);
+}
+
+/*
+** Parse the global chaining backend configuration
+*/
+
+static int cb_parse_config_entry(cb_backend * cb, Slapi_Entry *e)
+{
+ Slapi_Attr *attr = NULL;
+ Slapi_Value *sval;
+ struct berval *bval;
+ int i;
+
+ if (e == NULL)
+ return LDAP_SUCCESS;
+
+ cb_set_debug(0);
+
+ for (slapi_entry_first_attr(e, &attr); attr; slapi_entry_next_attr(e, attr, &attr)) {
+ char * attr_name=NULL;
+ slapi_attr_get_type(attr, &attr_name);
+
+ if ( !strcasecmp ( attr_name, CB_CONFIG_GLOBAL_FORWARD_CTRLS )) {
+ i = slapi_attr_first_value(attr, &sval);
+
+ PR_RWLock_Wlock(cb->config.rwl_config_lock);
+ if (cb->config.forward_ctrls) {
+ charray_free(cb->config.forward_ctrls);
+ cb->config.forward_ctrls=NULL;
+ }
+ PR_RWLock_Unlock(cb->config.rwl_config_lock);
+
+ while (i != -1 ) {
+ bval = (struct berval *) slapi_value_get_berval(sval);
+ /* For now, don't support operation type */
+ cb_register_supported_control(cb,bval->bv_val,
+ SLAPI_OPERATION_SEARCH | SLAPI_OPERATION_COMPARE |
+ SLAPI_OPERATION_ADD | SLAPI_OPERATION_DELETE |
+ SLAPI_OPERATION_MODIFY | SLAPI_OPERATION_MODDN);
+ i = slapi_attr_next_value(attr, i, &sval);
+ }
+ } else
+ if ( !strcasecmp ( attr_name, CB_CONFIG_GLOBAL_CHAINING_COMPONENTS )) {
+ i = slapi_attr_first_value(attr, &sval);
+ PR_RWLock_Wlock(cb->config.rwl_config_lock);
+ if (cb->config.chaining_components) {
+ charray_free(cb->config.chaining_components);
+ cb->config.chaining_components=NULL;
+ }
+ while (i != -1 ) {
+ bval = (struct berval *) slapi_value_get_berval(sval);
+ /* XXXSD assume dn. Normalize it */
+ charray_add( &cb->config.chaining_components,
+ slapi_dn_normalize(slapi_ch_strdup(bval->bv_val)));
+ i = slapi_attr_next_value(attr, i, &sval);
+ }
+ PR_RWLock_Unlock(cb->config.rwl_config_lock);
+ } else
+ if ( !strcasecmp ( attr_name, CB_CONFIG_GLOBAL_CHAINABLE_COMPONENTS )) {
+ i = slapi_attr_first_value(attr, &sval);
+ PR_RWLock_Wlock(cb->config.rwl_config_lock);
+ if (cb->config.chainable_components) {
+ charray_free(cb->config.chainable_components);
+ cb->config.chainable_components=NULL;
+ }
+ while (i != -1 ) {
+ bval = (struct berval *) slapi_value_get_berval(sval);
+ charray_add( &cb->config.chainable_components,
+ slapi_dn_normalize(slapi_ch_strdup(bval->bv_val)));
+ i = slapi_attr_next_value(attr, i, &sval);
+ }
+ PR_RWLock_Unlock(cb->config.rwl_config_lock);
+ } else
+ if ( !strcasecmp ( attr_name, CB_CONFIG_GLOBAL_DEBUG )) {
+ i = slapi_attr_first_value(attr, &sval);
+ if (i != -1 ) {
+ bval = (struct berval *) slapi_value_get_berval(sval);
+ /* any value */
+ cb_set_debug(1);
+ }
+ }
+ }
+ return LDAP_SUCCESS;
+}
diff --git a/ldap/servers/plugins/chainingdb/cb_conn_stateless.c b/ldap/servers/plugins/chainingdb/cb_conn_stateless.c
new file mode 100644
index 00000000..d97f947e
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_conn_stateless.c
@@ -0,0 +1,1132 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+/*
+ * Most of the complicated connection-related code lives in this file. Some
+ * general notes about how we manage our connections to "remote" LDAP servers:
+ *
+ * 1) Each farm server we have a relationship with is managed independently.
+ *
+ * 2) We may simultaneously issue multiple requests on a single LDAP
+ * connection. Each server has a "maxconcurrency" configuration
+ * parameter associated with it that caps the number of outstanding operations
+ * per connection. For each connection we maintain a "usecount"
+ * which is used to track the number of threads using the connection.
+ *
+ * 3) IMPORTANT NOTE: This connexion management is stateless i.e there is no garanty that
+ * operation from the same incoming client connections are sent to the same
+ * outgoing connection to the farm server. Today, this is not a problem because
+ * all controls we support are stateless. The implementation of the abandon
+ * operation takes this limitation into account.
+ *
+ * 4) We may open more than one connection to a server. Each farm server
+ * has a "maxconnections" configuration parameter associated with it
+ * that caps the number of connections.
+ *
+ * 5) If no connection is available to service a request , threads
+ * go to sleep on a condition variable and one is woken up each time
+ * a connection's "usecount" is decremented.
+ *
+ * 6) If we see an LDAP_CONNECT_ERROR or LDAP_SERVER_DOWN error on a
+ * session handle, we mark its status as CB_LDAP_STATUS_DOWN and
+ * close it as soon as all threads using it release it. Connections
+ * marked as "down" are not counted against the "maxconnections" limit.
+ *
+ * 7) We close and reopen connections that have been open for more than
+ * the server's configured connection lifetime. This is done to ensure
+ * that we reconnect to a primary server after failover occurs. If no
+ * lifetime is configured or it is set to 0, we never close and reopen
+ * connections.
+ */
+
+static void cb_close_and_dispose_connection ( cb_outgoing_conn * conn );
+static void cb_check_for_stale_connections(cb_conn_pool * pool);
+
+PRUint32 PR_GetThreadID(PRThread *thread);
+
+/* returns the threadId of the current thread modulo MAX_CONN_ARRAY
+=> gives the position of the thread in the array of secure connections */
+static int PR_ThreadSelf() {
+ PRThread *thr = PR_GetCurrentThread();
+ PRUint32 myself = PR_GetThreadID(thr);
+ myself &= 0x000007FF ;
+ return myself;
+}
+
+static int PR_MyThreadId() {
+ PRThread *thr = PR_GetCurrentThread();
+ PRUint32 myself = PR_GetThreadID(thr);
+ return myself;
+}
+
+/*
+** Close outgoing connections
+*/
+
+void cb_close_conn_pool(cb_conn_pool * pool) {
+
+ cb_outgoing_conn *conn, *nextconn;
+ int secure = pool->secure;
+ int i = 0;
+
+ slapi_lock_mutex( pool->conn.conn_list_mutex );
+
+ if (secure) {
+ for (i=0; i< MAX_CONN_ARRAY; i++) {
+ for (conn = pool->connarray[i]; conn != NULL; conn = nextconn) {
+ if ( conn->status != CB_CONNSTATUS_OK ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "cb_close_conn_pool: unexpected connection state (%d)\n",conn->status);
+ }
+ nextconn=conn->next;
+ cb_close_and_dispose_connection(conn);
+ }
+ }
+ }
+ else {
+ for ( conn = pool->conn.conn_list; conn != NULL; conn = nextconn ) {
+ if ( conn->status != CB_CONNSTATUS_OK ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "cb_close_conn_pool: unexpected connection state (%d)\n",conn->status);
+ }
+ nextconn=conn->next;
+ cb_close_and_dispose_connection(conn);
+ }
+ }
+
+ pool->conn.conn_list=NULL;
+ pool->conn.conn_list_count=0;
+
+ slapi_unlock_mutex( pool->conn.conn_list_mutex );
+}
+
+/*
+ * Get an LDAP session handle for communicating with the farm servers.
+ *
+ * Returns an LDAP eror code, typically:
+ * LDAP_SUCCESS
+ * LDAP_TIMELIMIT_EXCEEDED
+ * LDAP_CONNECT_ERROR
+ * NOTE : if maxtime NULL, use operation timeout
+ */
+
+int cb_get_connection(cb_conn_pool * pool, LDAP ** lld, cb_outgoing_conn ** cc,struct timeval * maxtime, char **errmsg) {
+
+ int rc=LDAP_SUCCESS; /* optimistic */
+ cb_outgoing_conn *conn=NULL;
+ cb_outgoing_conn *connprev=NULL;
+ LDAP *ld=NULL;
+ time_t endbefore=0;
+ int checktime=0;
+ struct timeval bind_to, op_to;
+ unsigned int maxconcurrency,maxconnections;
+ char *password,*binddn,*hostname;
+ unsigned int port;
+ int secure;
+ static char *error1="Can't contact remote server : %s";
+ static char *error2="Can't bind to remote server : %s";
+ int isMultiThread = ENABLE_MULTITHREAD_PER_CONN ; /* by default, we enable multiple operations per connection */
+
+ /*
+ ** return an error if we can't get a connection
+ ** before the operation timeout has expired
+ ** bind_timeout: timeout for the bind operation (if bind needed)
+ ** ( checked in ldap_result )
+ ** op_timeout: timeout for the op that needs a connection
+ ** ( checked in the loop )
+ */
+ *cc=NULL;
+
+ PR_RWLock_Rlock(pool->rwl_config_lock);
+ maxconcurrency=pool->conn.maxconcurrency;
+ maxconnections=pool->conn.maxconnections;
+ bind_to.tv_sec = pool->conn.bind_timeout.tv_sec;
+ bind_to.tv_usec = pool->conn.bind_timeout.tv_usec;
+ op_to.tv_sec = pool->conn.op_timeout.tv_sec;
+ op_to.tv_usec = pool->conn.op_timeout.tv_usec;
+
+ /* SD 02/10/2000 temp fix */
+ /* allow dynamic update of the binddn & password */
+ /* host, port and security mode */
+ /* previous values are NOT freed when changed */
+ /* won't likely to be changed often */
+ /* pointers put in the waste basket fields and */
+ /* freed when the backend is stopped. */
+
+ password=pool->password;
+ binddn=pool->binddn;
+ hostname=pool->hostname;
+ port=pool->port;
+ secure=pool->secure;
+
+ PR_RWLock_Unlock(pool->rwl_config_lock);
+
+ if (secure) {
+ isMultiThread = DISABLE_MULTITHREAD_PER_CONN ;
+ }
+
+ /* For stupid admins */
+ if (maxconnections <=0) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "<== cb_get_connection error (no connection available)\n");
+ if ( errmsg ) {
+ *errmsg = slapi_ch_malloc(CB_BUFSIZE);
+ sprintf(*errmsg,error1,"no connection available");
+ }
+ return LDAP_CONNECT_ERROR;
+ }
+
+ if (maxtime) {
+ if (maxtime->tv_sec != 0) {
+ checktime=1;
+ endbefore = current_time() + maxtime->tv_sec;
+
+ /* make sure bind to <= operation timeout */
+ if ((bind_to.tv_sec==0) || (bind_to.tv_sec > maxtime->tv_sec))
+ bind_to.tv_sec=maxtime->tv_sec;
+ }
+ } else {
+ if (op_to.tv_sec != 0) {
+ checktime=1;
+ endbefore = current_time() + op_to.tv_sec;
+
+ /* make sure bind to <= operation timeout */
+ if ((bind_to.tv_sec==0) || (bind_to.tv_sec > op_to.tv_sec))
+ bind_to.tv_sec=op_to.tv_sec;
+ }
+ }
+
+ /*
+ * Close (or mark to be closed) any connections for this farm server that have
+ * exceeded the maximum connection lifetime.
+ */
+
+ cb_check_for_stale_connections(pool);
+
+ /*
+ * Look for an available, already open connection
+ */
+
+ slapi_lock_mutex( pool->conn.conn_list_mutex );
+
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "==> cb_get_connection server %s conns: %d maxconns: %d\n",
+ hostname, pool->conn.conn_list_count, maxconnections );
+ }
+
+ for (;;) {
+
+ /* time limit mgmt */
+ if (checktime) {
+ if (current_time() > endbefore ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "cb_get_connection server %s expired.\n", hostname );
+ if ( errmsg ) {
+ *errmsg = slapi_ch_malloc(CB_BUFSIZE);
+ sprintf(*errmsg,error1,"timelimit exceeded");
+ }
+ rc=LDAP_TIMELIMIT_EXCEEDED;
+ conn=NULL;
+ ld=NULL;
+ goto unlock_and_return;
+ }
+ }
+
+ /*
+ * First, look for an available, already open/bound connection
+ */
+
+ if (secure) {
+ for (conn = pool->connarray[PR_ThreadSelf()]; conn != NULL; conn = conn->next) {
+ if ((conn->ThreadId == PR_MyThreadId()) && (conn->status == CB_CONNSTATUS_OK &&
+ conn->refcount < maxconcurrency)){
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "<= cb_get_connection server found conn 0x%x to use)\n", conn );
+ }
+ goto unlock_and_return; /* found one */
+ }
+ }
+ }
+ else {
+ connprev = NULL;
+ for ( conn = pool->conn.conn_list; conn != NULL; conn = conn->next ) {
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "list: conn 0x%x status %d refcount %d\n", conn,
+ conn->status, conn->refcount );
+ }
+
+ if ( conn->status == CB_CONNSTATUS_OK
+ && conn->refcount < maxconcurrency ) {
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "<= cb_get_connection server found conn 0x%x to use)\n", conn );
+ }
+ goto unlock_and_return; /* found one */
+ }
+ connprev = conn;
+ }
+ }
+
+ if ( secure || pool->conn.conn_list_count <maxconnections) {
+
+ int version=LDAP_VERSION3;
+
+ /* check wether the security libraries are correctly initialized */
+ if (secure && slapd_security_library_is_initialized() != 1) {
+ slapi_log_error(
+ SLAPI_LOG_FATAL, CB_PLUGIN_SUBSYSTEM,
+ "SSL Not Initialized, Chaining Backend over SSL FAILED\n");
+ rc = LDAP_CONNECT_ERROR;
+ goto unlock_and_return;
+ }
+
+ /*
+ * we have not exceeded the maximum number of connections allowed,
+ * so we initialize a new one and add it to the end of our list.
+ */
+
+ /* No need to lock. url can't be changed dynamically */
+ if ((ld=slapi_ldap_init(hostname,port,secure,isMultiThread))== NULL) {
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Can't contact server <%s> port <%d>.\n", hostname, port);
+ }
+ if ( errmsg ) {
+ *errmsg = slapi_ch_malloc(CB_BUFSIZE);
+ sprintf(*errmsg,error1,"unknown reason");
+ }
+ rc = LDAP_CONNECT_ERROR;
+ goto unlock_and_return;
+ }
+
+ ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
+ /* Don't chase referrals */
+ ldap_set_option( ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF );
+
+ /* no controls and simple bind only */
+ /* For now, bind even if no user to detect error */
+ /* earlier */
+ if (pool->bindit) {
+ int msgid;
+ LDAPMessage *res=NULL;
+ int parse_rc;
+ PRErrorCode prerr = 0;
+ LDAPControl **serverctrls=NULL;
+ char **referrals=NULL;
+
+ char *plain = NULL;
+ int ret = -1;
+
+ rc=LDAP_SUCCESS;
+
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Bind to to server <%s> port <%d> as <%s>\n",
+ hostname, port, binddn);
+ }
+
+ ret = pw_rever_decode(password, &plain, CB_CONFIG_USERPASSWORD);
+
+ /* Pb occured in decryption: stop now, binding will fail */
+ if ( ret == -1 )
+ {
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Internal credentials decoding error\n.",
+ 0, 0, 0);
+ }
+ rc = LDAP_LOCAL_ERROR;
+ goto unlock_and_return;
+ }
+
+ /* Password-based client authentication */
+
+ if (( msgid = ldap_simple_bind( ld, binddn, plain)) <0) {
+ rc=ldap_get_lderrno( ld, NULL, NULL );
+ prerr=PR_GetError();
+ }
+ if ( ret == 0 ) free(plain); /* free plain only if it has been duplicated */
+
+ if ( rc != LDAP_SUCCESS ) {
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Can't bind to server <%s> port <%d>. "
+ "(LDAP error %d - %s; "
+ SLAPI_COMPONENT_NAME_NSPR " error %d - %s)\n",
+ hostname, port, rc,
+ ldap_err2string(rc),
+ prerr, slapd_pr_strerror(prerr));
+ }
+ if ( errmsg ) {
+ *errmsg = slapi_ch_malloc(CB_BUFSIZE);
+ sprintf(*errmsg,error2, ldap_err2string(rc));
+ }
+ rc = LDAP_CONNECT_ERROR;
+ goto unlock_and_return;
+ }
+
+ rc = ldap_result( ld, msgid, 0, &bind_to, &res );
+ switch (rc) {
+ case -1:
+ rc = ldap_get_lderrno( ld, NULL, NULL );
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Can't bind to server <%s> port <%d>. "
+ "(LDAP error %d - %s; "
+ SLAPI_COMPONENT_NAME_NSPR " error %d - %s)\n",
+ hostname, port, rc,
+ ldap_err2string(rc),
+ prerr, slapd_pr_strerror(prerr));
+ }
+ if ( errmsg ) {
+ *errmsg = slapi_ch_malloc(CB_BUFSIZE);
+ sprintf(*errmsg,error2,ldap_err2string(rc));
+ }
+ rc = LDAP_CONNECT_ERROR;
+ goto unlock_and_return;
+ case 0:
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Can't bind to server <%s> port <%d>. (%s)\n",
+ hostname, port, "time-out expired");
+ }
+ rc = LDAP_CONNECT_ERROR;
+ goto unlock_and_return;
+ default:
+
+ parse_rc = ldap_parse_result( ld, res, &rc, NULL,
+ NULL, &referrals, &serverctrls, 1 );
+
+ if ( parse_rc != LDAP_SUCCESS ) {
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Can't bind to server <%s> port <%d>. (%s)\n",
+ hostname, port, ldap_err2string(parse_rc));
+ }
+ if ( errmsg ) {
+ *errmsg = slapi_ch_malloc(CB_BUFSIZE);
+ sprintf(*errmsg,error2,ldap_err2string(parse_rc));
+ }
+ rc = parse_rc;
+ goto unlock_and_return;
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Can't bind to server <%s> port <%d>. (%s)\n",
+ hostname, port, ldap_err2string(rc));
+ }
+ if ( errmsg ) {
+ *errmsg = slapi_ch_malloc(CB_BUFSIZE);
+ sprintf(*errmsg,error2, ldap_err2string(rc));
+ }
+ goto unlock_and_return;
+ }
+
+ if ( serverctrls )
+ {
+ int i;
+ for( i = 0; serverctrls[ i ] != NULL; ++i )
+ {
+ if ( !(strcmp( serverctrls[ i ]->ldctl_oid, LDAP_CONTROL_PWEXPIRED)) )
+ {
+ /* Bind is successful but password has expired */
+ slapi_log_error(SLAPI_LOG_FATAL, CB_PLUGIN_SUBSYSTEM,
+ "Succesfully bound as %s to remote server %s:%d, "
+ "but password has expired.\n",
+ binddn, hostname, port);
+ }
+ else if ( !(strcmp( serverctrls[ i ]->ldctl_oid, LDAP_CONTROL_PWEXPIRING)) )
+ {
+ /* The password is expiring in n seconds */
+ if ( (serverctrls[ i ]->ldctl_value.bv_val != NULL) &&
+ (serverctrls[ i ]->ldctl_value.bv_len > 0) )
+ {
+ int password_expiring = atoi( serverctrls[ i ]->ldctl_value.bv_val );
+ slapi_log_error(SLAPI_LOG_FATAL, CB_PLUGIN_SUBSYSTEM,
+ "Succesfully bound as %s to remote server %s:%d, "
+ "but password is expiring in %d seconds.\n",
+ binddn, hostname, port, password_expiring);
+ }
+ }
+ }
+ ldap_controls_free(serverctrls);
+ }
+
+ if (referrals)
+ charray_free(referrals);
+ }
+ }
+
+ conn = (cb_outgoing_conn *) slapi_ch_malloc(sizeof(cb_outgoing_conn));
+ conn->ld=ld;
+ conn->status=CB_CONNSTATUS_OK;
+ conn->refcount=0; /* incremented below */
+ conn->opentime=current_time();
+ conn->ThreadId=PR_MyThreadId(); /* store the thread id */
+ conn->next=NULL;
+ if (secure) {
+ if (pool->connarray[PR_ThreadSelf()] == NULL) {
+ pool->connarray[PR_ThreadSelf()] = conn;
+ }
+ else {
+ conn->next = pool->connarray[PR_ThreadSelf()];
+ pool->connarray[PR_ThreadSelf()] = conn ;
+ }
+ }
+ else {
+ if ( NULL == connprev ) {
+ pool->conn.conn_list = conn;
+ } else {
+ connprev->next=conn;
+ }
+ }
+
+ ++pool->conn.conn_list_count;
+
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "<= cb_get_connection added new conn 0x%x, "
+ "conn count now %d\n", conn->ld, pool->conn.conn_list_count );
+ }
+ goto unlock_and_return; /* got a new one */
+ }
+
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "... cb_get_connection waiting for conn to free up\n" );
+ }
+
+ if (!secure) slapi_wait_condvar( pool->conn.conn_list_cv, NULL );
+
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "... cb_get_connection awake again\n" );
+ }
+ }
+
+unlock_and_return:
+ if ( conn != NULL ) {
+ ++conn->refcount;
+ *lld=conn->ld;
+ *cc=conn;
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "<== cb_get_connection ld=0x%x (concurrency now %d)\n",*lld, conn->refcount );
+ }
+
+ } else {
+ if ( NULL != ld ) {
+ slapi_ldap_unbind( ld );
+ }
+
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "<== cb_get_connection error %d\n", rc );
+ }
+ }
+
+ slapi_unlock_mutex(pool->conn.conn_list_mutex);
+ return( rc );
+}
+
+/*
+ * We are done with the connection handle because the
+ * LDAP operation has completed.
+ */
+
+void cb_release_op_connection(cb_conn_pool* pool, LDAP *lld, int dispose) {
+
+ cb_outgoing_conn *conn;
+ cb_outgoing_conn *connprev = NULL;
+ int secure = pool->secure;
+ int myself = 0;
+
+ slapi_lock_mutex(pool->conn.conn_list_mutex);
+ /*
+ * find the connection structure this ld is part of
+ */
+
+ if (secure) {
+ myself = PR_ThreadSelf();
+ for (conn = pool->connarray[myself]; conn != NULL; conn = conn->next ) {
+ if ( lld == conn->ld )
+ break;
+ connprev = conn;
+ }
+ }
+ else {
+ for ( conn = pool->conn.conn_list; conn != NULL; conn = conn->next ){
+ if ( lld == conn->ld )
+ break;
+ connprev = conn;
+ }
+ }
+
+ if ( conn == NULL ) { /* ld not found -- unexpected */
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "==> cb_release_op_connection ld=0x%x not found\n", lld );
+ } else {
+
+ --conn->refcount;
+
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "release conn 0x%x status %d refcount after release %d\n", conn,
+ conn->status, conn->refcount );
+ }
+
+ if ( dispose ) {
+ conn->status = CB_CONNSTATUS_DOWN;
+ }
+
+ if ( conn->status != CB_CONNSTATUS_OK && conn->refcount == 0 ) {
+
+ /*
+ * remove from server's connection list
+ */
+
+ if (!secure) {
+ if ( connprev == NULL ) {
+ pool->conn.conn_list = conn->next;
+ } else {
+ connprev->next = conn->next;
+ }
+ }
+ else {
+ if ( connprev == NULL ) {
+ pool->connarray[myself] = conn->next;
+ } else {
+ connprev->next = conn->next;
+ }
+ }
+
+ --pool->conn.conn_list_count;
+
+ /*
+ * close connection and free memory
+ */
+ cb_close_and_dispose_connection( conn );
+ }
+ }
+
+ /*
+ * wake up a thread that is waiting for a connection
+ */
+
+ if (!secure) slapi_notify_condvar( pool->conn.conn_list_cv, 0 );
+
+ slapi_unlock_mutex( pool->conn.conn_list_mutex );
+}
+
+
+static void
+cb_close_and_dispose_connection( cb_outgoing_conn *conn )
+{
+ slapi_ldap_unbind( conn->ld );
+ conn->ld = NULL;
+ slapi_ch_free( (void **)&conn );
+}
+
+static void cb_check_for_stale_connections(cb_conn_pool * pool) {
+
+ cb_outgoing_conn * connprev, *conn, *conn_next;
+ time_t curtime;
+ int connlifetime;
+ int myself;
+
+ PR_RWLock_Rlock(pool->rwl_config_lock);
+ connlifetime=pool->conn.connlifetime;
+ PR_RWLock_Unlock(pool->rwl_config_lock);
+
+ connprev = NULL;
+ conn_next = NULL;
+
+ slapi_lock_mutex(pool->conn.conn_list_mutex);
+
+ if (connlifetime > 0)
+ curtime=current_time();
+
+ if (pool->secure) {
+ myself = PR_ThreadSelf();
+ for (conn = pool->connarray[myself]; conn != NULL; conn = conn_next){
+ if ((conn->status == CB_CONNSTATUS_STALE) ||
+ (( connlifetime > 0) && (curtime - conn->opentime > connlifetime))) {
+ if ( conn->refcount == 0 ) {
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "cb_check_for_stale_connections: conn 0x%x idle and stale\n",conn);
+ }
+ --pool->conn.conn_list_count;
+ if (connprev == NULL) {
+ pool->connarray[myself] = conn->next ;
+ }
+ else {
+ connprev->next = conn->next ;
+ }
+ conn_next = conn->next ;
+ cb_close_and_dispose_connection( conn );
+ continue;
+ }
+ /* Connection is stale but in use */
+ /* Mark to be disposed later but let it in the backend list */
+ /* so that it is counted as a valid connection */
+ else {
+ conn->status = CB_CONNSTATUS_STALE;
+ }
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "cb_check_for_stale_connections: conn 0x%x stale\n",conn);
+ }
+ }
+ connprev = conn ;
+ conn_next = conn->next;
+ }
+ slapi_unlock_mutex(pool->conn.conn_list_mutex);
+ return;
+ }
+
+ for ( conn = pool->conn.conn_list; conn != NULL; conn=conn_next ) {
+ if ((conn->status == CB_CONNSTATUS_STALE) ||
+ (( connlifetime > 0) && (curtime - conn->opentime > connlifetime))) {
+ if ( conn->refcount == 0 ) {
+
+ /* Connection idle & stale. Remove and free. */
+
+ if ( NULL == connprev )
+ pool->conn.conn_list = conn->next;
+ else
+ connprev->next=conn->next;
+
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "cb_check_for_stale_connections: conn 0x%x idle and stale\n",conn);
+ }
+ --pool->conn.conn_list_count;
+ conn_next=conn->next;
+ cb_close_and_dispose_connection( conn );
+ continue;
+ }
+
+ /* Connection is stale but in use */
+ /* Mark to be disposed later but let it in the backend list */
+ /* so that it is counted as a valid connection */
+ else {
+ conn->status = CB_CONNSTATUS_STALE;
+ }
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "cb_check_for_stale_connections: conn 0x%x stale\n",conn);
+ }
+ }
+ connprev = conn;
+ conn_next=conn->next;
+ }
+
+ /* Generate an event to wake up threads waiting */
+ /* for a conn to be released. Useful to detect */
+ /* exceeded time limit. May be expensive */
+
+ slapi_notify_condvar( pool->conn.conn_list_cv, 0 );
+
+ slapi_unlock_mutex(pool->conn.conn_list_mutex);
+}
+
+/*
+ * close all open connections in preparation for server shutdown, etc.
+ * WARNING: Don't wait for current operations to complete
+ */
+
+void
+cb_close_all_connections( Slapi_Backend * be )
+{
+
+ cb_outgoing_conn *conn, *next_conn;
+ cb_backend_instance * cb= cb_get_instance(be);
+ int i;
+
+ slapi_lock_mutex(cb->pool->conn.conn_list_mutex);
+ if (cb->pool->secure) {
+ for (i=0; i< MAX_CONN_ARRAY; i++) {
+ for (conn = cb->pool->connarray[i]; conn != NULL; conn = next_conn ){
+ next_conn = conn->next;
+ cb_close_and_dispose_connection(conn);
+ }
+ }
+ } else {
+ for ( conn = cb->pool->conn.conn_list; conn != NULL; conn = next_conn ) {
+ next_conn=conn->next;
+ cb_close_and_dispose_connection(conn);
+ }
+ }
+ slapi_unlock_mutex(cb->pool->conn.conn_list_mutex);
+
+ slapi_lock_mutex(cb->bind_pool->conn.conn_list_mutex);
+ if (cb->bind_pool->secure) {
+ for (i=0; i< MAX_CONN_ARRAY; i++) {
+ for (conn = cb->bind_pool->connarray[i]; conn != NULL; conn = next_conn ){
+ next_conn=conn->next;
+ cb_close_and_dispose_connection(conn);
+ }
+ }
+ } else {
+ for ( conn = cb->bind_pool->conn.conn_list; conn != NULL; conn = next_conn ) {
+ next_conn=conn->next;
+ cb_close_and_dispose_connection(conn);
+ }
+ }
+ slapi_unlock_mutex(cb->bind_pool->conn.conn_list_mutex);
+}
+
+/* Mark used connections as stale and close unsued connections */
+/* Called when the target farm url has changed */
+
+void cb_stale_all_connections( cb_backend_instance * cb)
+{
+ cb_outgoing_conn *conn, *next_conn, *prev_conn;
+ int notify=0;
+ int i, j;
+ cb_conn_pool *pools[3];
+
+ pools[0]=cb->pool;
+ pools[1]=cb->bind_pool;
+ pools[2]=NULL;
+
+ for (i=0; pools[i]; i++) {
+ slapi_lock_mutex(pools[i]->conn.conn_list_mutex);
+ for (j=0; j< MAX_CONN_ARRAY; j++) {
+ prev_conn=NULL;
+ for (conn = pools[i]->connarray[j]; conn != NULL; conn=next_conn) {
+ next_conn=conn->next;
+ if (conn->refcount > 0) {
+ /*
+ ** Connection is stale but in use
+ ** Mark to be disposed later but let it in the backend list
+ ** so that it is counted as a valid connection
+ */
+ conn->status = CB_CONNSTATUS_STALE;
+ prev_conn=conn;
+ } else {
+ if (prev_conn == NULL) {
+ pools[i]->connarray[j]=next_conn;
+ } else {
+ prev_conn->next=next_conn;
+ }
+ cb_close_and_dispose_connection(conn);
+ pools[i]->conn.conn_list_count--;
+ }
+ }
+ }
+ prev_conn = NULL ;
+ for ( conn = pools[i]->conn.conn_list; conn != NULL; conn = next_conn ) {
+ next_conn=conn->next;
+ if (conn->refcount > 0) {
+ /*
+ ** Connection is stale but in use
+ ** Mark to be disposed later but let it in the backend list
+ ** so that it is counted as a valid connection
+ */
+ conn->status = CB_CONNSTATUS_STALE;
+ prev_conn=conn;
+ }
+ else {
+ if (conn==pools[i]->conn.conn_list) {
+ pools[i]->conn.conn_list=next_conn;
+ } else {
+ prev_conn->next=next_conn;
+ }
+ cb_close_and_dispose_connection(conn);
+ pools[i]->conn.conn_list_count--;
+ notify=1;
+ }
+ }
+ if (notify && (! pools[i]->secure)) {
+ slapi_notify_condvar( pools[i]->conn.conn_list_cv, 0 );
+ }
+ slapi_unlock_mutex(pools[i]->conn.conn_list_mutex);
+ }
+}
+
+
+
+/**************************************************************************/
+/* Need to use our own connect function until we've switched to C-SDK 4.1 */
+/* to have a timeout in the connect system call. */
+/**************************************************************************/
+
+static int global_connect_to;
+
+#if 0
+
+/* Taken from C-SDK 4.1 */
+#include <fcntl.h>
+#include <errno.h>
+#define LDAP_X_IO_TIMEOUT_NO_TIMEOUT (-1)
+
+static int
+nsldapi_os_connect_with_to(LBER_SOCKET sockfd, struct sockaddr *saptr,
+ int salen)
+{
+#ifndef _WIN32
+ int flags;
+#endif /* _WIN32 */
+ int n, error;
+ int len;
+ fd_set rset, wset;
+ struct timeval tval;
+#ifdef _WIN32
+ int nonblock = 1;
+ int block = 0;
+ fd_set eset;
+#endif /* _WIN32 */
+
+ int msec=global_connect_to; /* global */
+
+#ifdef _WIN32
+ ioctlsocket(sockfd, FIONBIO, &nonblock);
+#else
+ flags = fcntl(sockfd, F_GETFL, 0);
+ fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
+#endif /* _WIN32 */
+
+ error = 0;
+ if ((n = connect(sockfd, saptr, salen)) < 0)
+#ifdef _WIN32
+ if ((n != SOCKET_ERROR) && (WSAGetLastError() != WSAEWOULDBLOCK)) {
+#else
+ if (errno != EINPROGRESS) {
+#endif /* _WIN32 */
+ return (-1);
+ }
+
+ /* success */
+ if (n == 0)
+ goto done;
+
+ FD_ZERO(&rset);
+ FD_SET(sockfd, &rset);
+ wset = rset;
+
+#ifdef _WIN32
+ eset = rset;
+#endif /* _WIN32 */
+
+ if (msec < 0 && msec != LDAP_X_IO_TIMEOUT_NO_TIMEOUT) {
+ msec = LDAP_X_IO_TIMEOUT_NO_TIMEOUT;
+ } else {
+ if (msec != 0)
+ tval.tv_sec = msec / 1000;
+ else
+ tval.tv_sec = 0;
+ tval.tv_usec = 0;
+ }
+
+ /* if timeval structure == NULL, select will block indefinitely */
+ /* != NULL, and value == 0, select will */
+ /* not block */
+ /* Windows is a bit quirky on how it behaves w.r.t nonblocking */
+ /* connects. If the connect fails, the exception fd, eset, is */
+ /* set to show the failure. The first argument in select is */
+ /* ignored */
+
+#ifdef _WIN32
+ if ((n = select(sockfd +1, &rset, &wset, &eset,
+ (msec != LDAP_X_IO_TIMEOUT_NO_TIMEOUT) ? &tval : NULL)) == 0) {
+ errno = WSAETIMEDOUT;
+ return (-1);
+ }
+ /* if wset is set, the connect worked */
+ if (FD_ISSET(sockfd, &wset) || FD_ISSET(sockfd, &rset)) {
+ len = sizeof(error);
+ if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (char *)&error, &len)
+ < 0)
+ return (-1);
+ goto done;
+ }
+
+ /* if eset is set, the connect failed */
+ if (FD_ISSET(sockfd, &eset)) {
+ return (-1);
+ }
+
+ /* failure on select call */
+ if (n == SOCKET_ERROR) {
+ return (-1);
+ }
+#else
+ if ((n = select(sockfd +1, &rset, &wset, NULL,
+ (msec != LDAP_X_IO_TIMEOUT_NO_TIMEOUT) ? &tval : NULL)) == 0) {
+ errno = ETIMEDOUT;
+ return (-1);
+ }
+ if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) {
+ len = sizeof(error);
+ if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (char *)&error, &len)
+ < 0)
+ return (-1);
+ }
+#endif /* _WIN32 */
+done:
+#ifdef _WIN32
+ ioctlsocket(sockfd, FIONBIO, &block);
+#else
+ fcntl(sockfd, F_SETFL, flags);
+#endif /* _WIN32 */
+
+ if (error) {
+ errno = error;
+ return (-1);
+ }
+
+ return (0);
+}
+
+#endif
+
+/* Try to figure out if a farm server is still alive */
+
+int cb_ping_farm(cb_backend_instance *cb, cb_outgoing_conn * cnx,time_t end_time) {
+
+ char *attrs[] ={"1.1",NULL};
+ int rc;
+ struct timeval timeout;
+ LDAP *ld;
+ LDAPMessage *result;
+#if 0
+ struct ldap_io_fns iof;
+#endif
+ time_t now;
+ if (cb->max_idle_time <=0) /* Heart-beat disabled */
+ return LDAP_SUCCESS;
+
+ if (cnx && (cnx->status != CB_CONNSTATUS_OK )) /* Known problem */
+ return LDAP_SERVER_DOWN;
+
+ now = current_time();
+ if (end_time && ((now <= end_time) || (end_time <0))) return LDAP_SUCCESS;
+
+ ld=slapi_ldap_init(cb->pool->hostname,cb->pool->port,cb->pool->secure,0);
+ if (NULL == ld) {
+ cb_update_failed_conn_cpt( cb );
+ return LDAP_SERVER_DOWN;
+ }
+
+#if 0
+ memset(&iof,0,sizeof(struct ldap_io_fns));
+ if (LDAP_SUCCESS !=ldap_get_option(ld,LDAP_OPT_IO_FN_PTRS,&iof)) {
+ slapi_ldap_unbind( ld );
+ cb_update_failed_conn_cpt( cb );
+ return LDAP_SERVER_DOWN;
+ }
+
+ iof.liof_connect = nsldapi_os_connect_with_to;
+ if (LDAP_SUCCESS !=ldap_set_option(ld,LDAP_OPT_IO_FN_PTRS,&iof)) {
+ slapi_ldap_unbind( ld );
+ cb_update_failed_conn_cpt( cb );
+ return LDAP_SERVER_DOWN;
+ }
+
+#endif
+
+ timeout.tv_sec=cb->max_test_time;
+ timeout.tv_usec=0;
+
+ global_connect_to=cb->max_test_time * 1000; /* Reuse the same for the connect */
+ rc=ldap_search_ext_s(ld ,NULL,LDAP_SCOPE_BASE,"objectclass=*",attrs,1,NULL,
+ NULL, &timeout, 1,&result);
+ if ( LDAP_SUCCESS != rc ) {
+ slapi_ldap_unbind( ld );
+ cb_update_failed_conn_cpt( cb );
+ return LDAP_SERVER_DOWN;
+ }
+
+ ldap_msgfree(result);
+ slapi_ldap_unbind( ld );
+ cb_reset_conn_cpt( cb );
+ return LDAP_SUCCESS;
+}
+
+
+
+void cb_update_failed_conn_cpt ( cb_backend_instance *cb ) {
+ /* if the chaining BE is already unavailable, we do nothing*/
+ time_t now;
+ if (cb->monitor_availability.farmserver_state == FARMSERVER_AVAILABLE) {
+ slapi_lock_mutex(cb->monitor_availability.cpt_lock);
+ cb->monitor_availability.cpt ++;
+ slapi_unlock_mutex(cb->monitor_availability.cpt_lock);
+ if (cb->monitor_availability.cpt >= CB_NUM_CONN_BEFORE_UNAVAILABILITY ) {
+ /* we reach the limit of authorized failed connections => we setup the chaining BE state to unavailable */
+ now = current_time();
+ slapi_lock_mutex(cb->monitor_availability.lock_timeLimit);
+ cb->monitor_availability.unavailableTimeLimit = now + CB_UNAVAILABLE_PERIOD ;
+ slapi_unlock_mutex(cb->monitor_availability.lock_timeLimit);
+ cb->monitor_availability.farmserver_state = FARMSERVER_UNAVAILABLE ;
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "cb_update_failed_conn_cpt: Farm server unavailable");
+ }
+
+ }
+}
+
+void cb_reset_conn_cpt( cb_backend_instance *cb ) {
+ if (cb->monitor_availability.cpt > 0) {
+ slapi_lock_mutex(cb->monitor_availability.cpt_lock);
+ cb->monitor_availability.cpt = 0 ;
+ if (cb->monitor_availability.farmserver_state == FARMSERVER_UNAVAILABLE) {
+ cb->monitor_availability.farmserver_state = FARMSERVER_AVAILABLE ;
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "cb_reset_conn_cpt: Farm server is back");
+ }
+ slapi_unlock_mutex(cb->monitor_availability.cpt_lock);
+ }
+}
+
+int cb_check_availability( cb_backend_instance *cb, Slapi_PBlock *pb ) {
+ /* check wether the farmserver is available or not */
+ int rc ;
+ time_t now ;
+ if ( cb->monitor_availability.farmserver_state == FARMSERVER_UNAVAILABLE ){
+ slapi_lock_mutex(cb->monitor_availability.lock_timeLimit);
+ now = current_time();
+ if (now >= cb->monitor_availability.unavailableTimeLimit) {
+ cb->monitor_availability.unavailableTimeLimit = now + CB_INFINITE_TIME ; /* to be sure only one thread can do the test */
+ slapi_unlock_mutex(cb->monitor_availability.lock_timeLimit);
+ }
+ else {
+ slapi_unlock_mutex(cb->monitor_availability.lock_timeLimit);
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, "FARM SERVER TEMPORARY UNAVAILABLE", 0, NULL) ;
+ return FARMSERVER_UNAVAILABLE ;
+ }
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "cb_check_availability: ping the farm server and check if it's still unavailable");
+ if ((rc = cb_ping_farm(cb, NULL, 0)) != LDAP_SUCCESS) { /* farm still unavailable... Just change the timelimit */
+ slapi_lock_mutex(cb->monitor_availability.lock_timeLimit);
+ now = current_time();
+ cb->monitor_availability.unavailableTimeLimit = now + CB_UNAVAILABLE_PERIOD ;
+ slapi_unlock_mutex(cb->monitor_availability.lock_timeLimit);
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, "FARM SERVER TEMPORARY UNAVAILABLE", 0, NULL) ;
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "cb_check_availability: Farm server still unavailable");
+ return FARMSERVER_UNAVAILABLE ;
+ }
+ else {
+ /* farm is back !*/
+ slapi_lock_mutex(cb->monitor_availability.lock_timeLimit);
+ now = current_time();
+ cb->monitor_availability.unavailableTimeLimit = now ; /* the unavailable period is finished */
+ slapi_unlock_mutex(cb->monitor_availability.lock_timeLimit);
+ /* The farmer server state backs to FARMSERVER_AVAILABLE, but this already done in cb_ping_farm, and also the reset of cpt*/
+ return FARMSERVER_AVAILABLE ;
+ }
+ }
+ return FARMSERVER_AVAILABLE ;
+}
diff --git a/ldap/servers/plugins/chainingdb/cb_controls.c b/ldap/servers/plugins/chainingdb/cb_controls.c
new file mode 100644
index 00000000..ab612099
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_controls.c
@@ -0,0 +1,289 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+/*
+** Controls that can't be forwarded due to the current implementation
+*/
+
+static char * unsupported_ctrls[] = {LDAP_CONTROL_PERSISTENTSEARCH,NULL};
+
+int cb_is_control_forwardable(cb_backend * cb, char *controloid) {
+ return (!(charray_inlist(unsupported_ctrls,controloid)));
+}
+
+void
+cb_register_supported_control( cb_backend * cb, char *controloid, unsigned long controlops )
+{
+ /* For now, ignore controlops */
+ if ( controloid != NULL ) {
+ PR_RWLock_Wlock(cb->config.rwl_config_lock);
+ charray_add( &cb->config.forward_ctrls,slapi_ch_strdup( controloid ));
+ PR_RWLock_Unlock(cb->config.rwl_config_lock);
+ }
+}
+
+
+void
+cb_unregister_all_supported_control( cb_backend * cb ) {
+
+ PR_RWLock_Wlock(cb->config.rwl_config_lock);
+ charray_free(cb->config.forward_ctrls);
+ cb->config.forward_ctrls=NULL;
+ PR_RWLock_Unlock(cb->config.rwl_config_lock);
+}
+
+void
+cb_unregister_supported_control( cb_backend * cb, char *controloid, unsigned long controlops )
+{
+
+ /* For now, ignore controlops */
+ if ( controloid != NULL ) {
+ int i;
+ PR_RWLock_Wlock(cb->config.rwl_config_lock);
+ for ( i = 0; cb->config.forward_ctrls != NULL && cb->config.forward_ctrls[i] != NULL; ++i ) {
+ if ( strcmp( cb->config.forward_ctrls[i], controloid ) == 0 ) {
+ break;
+ }
+ }
+ if ( cb->config.forward_ctrls == NULL || cb->config.forward_ctrls[i] == NULL) {
+ PR_RWLock_Unlock(cb->config.rwl_config_lock);
+ return;
+ }
+ if ( controlops == 0 ) {
+ charray_remove(cb->config.forward_ctrls,controloid);
+ }
+ PR_RWLock_Unlock(cb->config.rwl_config_lock);
+ }
+}
+
+int cb_create_loop_control (
+ const int hops,
+ LDAPControl **ctrlp)
+
+{
+ BerElement *ber;
+ int rc;
+
+ if ((ber = ber_alloc()) == NULL)
+ return -1;
+
+ if ( ber_printf( ber, "i", hops ) < 0) {
+ ber_free(ber,1);
+ return -1;
+ }
+
+ rc = slapi_build_control( CB_LDAP_CONTROL_CHAIN_SERVER, ber, 0, ctrlp);
+
+ ber_free(ber,1);
+
+ return rc;
+}
+
+/*
+** Return the controls to be passed to the remote
+** farm server and the LDAP error to return.
+**
+** Add the Proxied Authorization control when impersonation
+** is enabled. Other controls present in the request are added
+** to the control list
+**
+** #622885 .abandon should not inherit the to-be-abandoned-operation's controls
+** .controls attached to abandon should not be critical
+*/
+
+int cb_update_controls( Slapi_PBlock * pb,
+ LDAP * ld,
+ LDAPControl *** controls,
+ int ctrl_flags
+ )
+{
+
+ int cCount=0;
+ int dCount=0;
+ int i;
+ char * proxyDN=NULL;
+ LDAPControl ** reqControls = NULL;
+ LDAPControl ** ctrls = NULL;
+ cb_backend_instance * cb;
+ cb_backend * cbb;
+ Slapi_Backend * be;
+ int rc=LDAP_SUCCESS;
+ int hops=0;
+ int useloop=0;
+ int addauth = (ctrl_flags & CB_UPDATE_CONTROLS_ADDAUTH);
+ int isabandon = (ctrl_flags & CB_UPDATE_CONTROLS_ISABANDON);
+ int op_type = 0;
+
+ *controls = NULL;
+ slapi_pblock_get(pb, SLAPI_OPERATION_TYPE, &op_type);
+ if (!isabandon || op_type == SLAPI_OPERATION_ABANDON) {
+ /* if not abandon or abandon sent by client */
+ slapi_pblock_get( pb, SLAPI_REQCONTROLS, &reqControls );
+ }
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+ slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &cbb );
+ cb = cb_get_instance(be);
+
+ /*****************************************/
+ /* First, check for unsupported controls */
+ /* Return an error if critical control */
+ /* else remove it from the control list */
+ /*****************************************/
+
+ for ( cCount=0; reqControls && reqControls[cCount]; cCount++ );
+ ctrls = (LDAPControl **)slapi_ch_calloc(1,sizeof(LDAPControl *) * (cCount +3));
+
+ PR_RWLock_Rlock(cbb->config.rwl_config_lock);
+
+ for ( cCount=0; reqControls && reqControls[cCount]; cCount++ ) {
+
+ /* XXXSD CASCADING */
+ /* For now, allow PROXY_AUTH control forwarding only when */
+ /* local acl evaluation to prevent unauthorized access */
+
+ if (!strcmp(reqControls[cCount]->ldctl_oid,LDAP_CONTROL_PROXYAUTH)) {
+
+ /* we have to force remote acl checking if the associated backend to this
+ chaining backend is disabled - disabled == no acl check possible */
+ if (!cb->local_acl && !cb->associated_be_is_disabled) {
+ slapi_log_error( SLAPI_LOG_PLUGIN,CB_PLUGIN_SUBSYSTEM,
+ "local aci check required to handle proxied auth control. Deny access.\n");
+ rc= LDAP_INSUFFICIENT_ACCESS;
+ break;
+ }
+
+ /* XXXSD Not safe to use proxied authorization with Directory Manager */
+ /* checked earlier when impersonation is on */
+
+ if (!cb->impersonate) {
+ char * requestor,*rootdn;
+ char * requestorCopy=NULL;
+
+ rootdn=cb_get_rootdn();
+ slapi_pblock_get( pb, SLAPI_REQUESTOR_DN, &requestor );
+ requestorCopy=slapi_ch_strdup(requestor);
+ slapi_dn_normalize_case(requestorCopy);
+
+ if (!strcmp( requestorCopy, rootdn )) { /* UTF8- aware */
+ slapi_log_error( SLAPI_LOG_PLUGIN,CB_PLUGIN_SUBSYSTEM,
+ "Use of user <%s> incompatible with proxied auth. control\n",rootdn);
+ rc=LDAP_UNAVAILABLE_CRITICAL_EXTENSION;
+ slapi_ch_free((void **)&requestorCopy);
+ break;
+ }
+ slapi_ch_free((void **)&rootdn);
+ slapi_ch_free((void **)&requestorCopy);
+ }
+
+ addauth=0;
+ ctrls[dCount]=slapi_dup_control(reqControls[cCount]);
+ dCount++;
+
+ } else
+ if (!strcmp(reqControls[cCount]->ldctl_oid,CB_LDAP_CONTROL_CHAIN_SERVER)) {
+
+ /* Max hop count reached ? */
+ /* Checked realier by a call to cb_forward_operation() */
+
+ BerElement *ber = NULL;
+
+ ber = ber_init(&(reqControls[cCount]->ldctl_value));
+ ber_scanf(ber,"i",&hops);
+ ber_free(ber,1);
+ useloop=1;
+
+ /* Add to the control list later */
+
+ } else {
+
+ int i;
+ for ( i = 0; cbb->config.forward_ctrls != NULL
+ && cbb->config.forward_ctrls[i] != NULL; ++i ) {
+ if ( strcmp( cbb->config.forward_ctrls[i], reqControls[cCount]->ldctl_oid ) == 0 ) {
+ break;
+ }
+ }
+ /* For now, ignore optype */
+ if ( cbb->config.forward_ctrls == NULL || cbb->config.forward_ctrls[i] == NULL) {
+ if (reqControls[cCount]->ldctl_iscritical) {
+ rc = LDAP_UNAVAILABLE_CRITICAL_EXTENSION;
+ break;
+ }
+ /* Skip it */
+ } else {
+ ctrls[dCount]=slapi_dup_control(reqControls[cCount]);
+ dCount++;
+ }
+ }
+ }
+
+ PR_RWLock_Unlock(cbb->config.rwl_config_lock);
+
+ if (LDAP_SUCCESS != rc) {
+ ldap_controls_free(ctrls);
+ return rc;
+ }
+
+ /***************************************/
+ /* add impersonation control if needed */
+ /***************************************/
+
+ if ( !(cb->impersonate) ) {
+
+ /* don't add proxy control */
+ addauth=0;
+ }
+
+ if (addauth) {
+ slapi_pblock_get( pb, SLAPI_REQUESTOR_DN, &proxyDN );
+
+ if ( ldap_create_proxyauth_control(ld, proxyDN, isabandon?0:1, &ctrls[dCount] )) {
+ ldap_controls_free(ctrls);
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "LDAP_CONTROL_PROXYAUTH control encoding failed.\n");
+ return LDAP_OPERATIONS_ERROR;
+ }
+ dCount++;
+ }
+
+ /***********************************************************/
+ /* add loop control if needed */
+ /* Don't add it if not in the list of forwardable controls */
+ /***********************************************************/
+
+ if (!useloop) {
+ for ( i = 0; cbb->config.forward_ctrls != NULL
+ && cbb->config.forward_ctrls[i] != NULL; ++i ) {
+ if ( strcmp( cbb->config.forward_ctrls[i],
+ CB_LDAP_CONTROL_CHAIN_SERVER) == 0 ) {
+ break;
+ }
+ }
+ }
+ if ( useloop || (cbb->config.forward_ctrls !=NULL && cbb->config.forward_ctrls[i] !=NULL)){
+
+ if (hops > 0) {
+ hops--;
+ } else {
+ hops = cb->hoplimit;
+ }
+
+ /* loop control's critical flag is 0;
+ * no special treatment is needed for abandon */
+ cb_create_loop_control(hops,&ctrls[dCount]);
+ dCount++;
+ }
+
+ if (dCount==0) {
+ ldap_controls_free(ctrls);
+ } else {
+ *controls = ctrls;
+ }
+
+ return LDAP_SUCCESS;
+
+}
diff --git a/ldap/servers/plugins/chainingdb/cb_debug.c b/ldap/servers/plugins/chainingdb/cb_debug.c
new file mode 100644
index 00000000..8a33e440
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_debug.c
@@ -0,0 +1,23 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * cb_debug.c - debugging-related code for Chaining backend
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include "cb.h"
+
+#ifdef _WIN32
+int *module_ldap_debug = 0;
+
+void plugin_init_debug_level(int *level_ptr)
+{
+ module_ldap_debug = level_ptr;
+}
+#endif
+
diff --git a/ldap/servers/plugins/chainingdb/cb_delete.c b/ldap/servers/plugins/chainingdb/cb_delete.c
new file mode 100644
index 00000000..f7eb77f6
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_delete.c
@@ -0,0 +1,203 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+/*
+ * Perform an delete operation
+ *
+ * Returns:
+ * 0 - success
+ * <0 - fail
+ *
+ */
+
+int
+chaining_back_delete ( Slapi_PBlock *pb )
+{
+
+ Slapi_Backend * be;
+ cb_backend_instance *cb;
+ LDAPControl **ctrls, **serverctrls;
+ int rc,parse_rc,msgid,i;
+ LDAP *ld=NULL;
+ char **referrals=NULL;
+ LDAPMessage * res;
+ char *dn,* matched_msg, *error_msg;
+ char *cnxerrbuf=NULL;
+ time_t endtime;
+ cb_outgoing_conn *cnx;
+
+ if ( LDAP_SUCCESS != (rc=cb_forward_operation(pb) )) {
+ cb_send_ldap_result( pb, rc, NULL, "Chaining forbidden", 0, NULL );
+ return -1;
+ }
+
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+ cb = cb_get_instance(be);
+
+ cb_update_monitor_info(pb,cb,SLAPI_OPERATION_DELETE);
+
+ /* Check wether the chaining BE is available or not */
+ if ( cb_check_availability( cb, pb ) == FARMSERVER_UNAVAILABLE ){
+ return -1;
+ }
+
+ slapi_pblock_get( pb, SLAPI_DELETE_TARGET, &dn );
+
+ /*
+ * Check local acls
+ */
+
+ if (cb->local_acl && !cb->associated_be_is_disabled) {
+ char * errbuf=NULL;
+ Slapi_Entry *te = slapi_entry_alloc();
+ slapi_entry_set_dn(te,slapi_ch_strdup(dn));
+ rc = cb_access_allowed (pb, te, NULL, NULL, SLAPI_ACL_DELETE,&errbuf);
+ slapi_entry_free(te);
+
+ if ( rc != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, rc, NULL, errbuf, 0, NULL );
+ slapi_ch_free((void **)&errbuf);
+ return -1;
+ }
+ }
+
+ /*
+ * Grab a connection handle
+ */
+
+ if ((rc = cb_get_connection(cb->pool,&ld,&cnx,NULL,&cnxerrbuf)) != LDAP_SUCCESS) {
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, cnxerrbuf, 0, NULL);
+ slapi_ch_free((void **)&cnxerrbuf);
+ /* ping the farm. If the farm is unreachable, we increment the counter */
+ cb_ping_farm(cb,NULL,0);
+ return -1;
+ }
+
+ /*
+ * Control management
+ */
+
+ if ( (rc = cb_update_controls( pb,ld,&ctrls,CB_UPDATE_CONTROLS_ADDAUTH )) != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, rc, NULL,NULL, 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ return -1;
+ }
+
+ if ( slapi_op_abandoned( pb )) {
+ cb_release_op_connection(cb->pool,ld,0);
+ if ( NULL != ctrls)
+ ldap_controls_free(ctrls);
+ return -1;
+ }
+
+ /* heart-beat management */
+ if (cb->max_idle_time>0)
+ endtime=current_time() + cb->max_idle_time;
+
+ /*
+ * Send LDAP operation to the remote host
+ */
+
+ rc = ldap_delete_ext( ld, dn, ctrls, NULL, &msgid );
+ if ( NULL != ctrls)
+ ldap_controls_free(ctrls);
+ if ( rc != LDAP_SUCCESS ) {
+
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(rc), 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ return -1;
+ }
+
+ while ( 1 ) {
+
+ if (cb_check_forward_abandon(cb,pb,ld,msgid)) {
+ return -1;
+ }
+
+ rc = ldap_result( ld, msgid, 0, &cb->abandon_timeout, &res );
+ switch ( rc ) {
+ case -1:
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(rc), 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ if (res)
+ ldap_msgfree(res);
+ return -1;
+ case 0:
+ if ((rc=cb_ping_farm(cb,cnx,endtime)) != LDAP_SUCCESS) {
+
+ /* does not respond. give up and return a*/
+ /* error to the client. */
+
+ /*cb_send_ldap_result(pb,LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(rc), 0, NULL);*/
+ cb_send_ldap_result(pb,LDAP_OPERATIONS_ERROR, NULL, "FARM SERVER TEMPORARY UNAVAILABLE", 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ if (res)
+ ldap_msgfree(res);
+ return -1;
+ }
+#ifdef CB_YIELD
+ DS_Sleep(PR_INTERVAL_NO_WAIT);
+#endif
+ break;
+ default:
+ matched_msg=error_msg=NULL;
+ parse_rc = ldap_parse_result( ld, res, &rc, &matched_msg,
+ &error_msg, &referrals, &serverctrls, 1 );
+ if ( parse_rc != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(parse_rc), 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(parse_rc));
+ slapi_ch_free((void **)&matched_msg);
+ slapi_ch_free((void **)&error_msg);
+ if (serverctrls)
+ ldap_controls_free(serverctrls);
+ /* jarnou: free referrals */
+ if (referrals)
+ charray_free(referrals);
+ return -1;
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ struct berval ** refs = referrals2berval(referrals);
+
+ cb_send_ldap_result( pb, rc, matched_msg, error_msg, 0, refs);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ slapi_ch_free((void **)&matched_msg);
+ slapi_ch_free((void **)&error_msg);
+ if (refs)
+ ber_bvecfree(refs);
+ if (referrals)
+ charray_free(referrals);
+ if (serverctrls)
+ ldap_controls_free(serverctrls);
+ return -1;
+ }
+
+ cb_release_op_connection(cb->pool,ld,0);
+
+ /* Add control response sent by the farm server */
+
+ for (i=0; serverctrls && serverctrls[i];i++)
+ slapi_pblock_set( pb, SLAPI_ADD_RESCONTROL, serverctrls[i]);
+ if (serverctrls)
+ ldap_controls_free(serverctrls);
+ /* jarnou: free matched_msg, error_msg, and referrals if necessary */
+ slapi_ch_free((void **)&matched_msg);
+ slapi_ch_free((void **)&error_msg);
+ if (referrals)
+ charray_free(referrals);
+ cb_send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
+ return 0;
+ }
+ }
+
+ /* Never reached */
+ /* return 0; */
+}
diff --git a/ldap/servers/plugins/chainingdb/cb_init.c b/ldap/servers/plugins/chainingdb/cb_init.c
new file mode 100644
index 00000000..70af3d42
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_init.c
@@ -0,0 +1,120 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+Slapi_PluginDesc chainingdbdesc = { CB_PLUGIN_NAME,
+ PLUGIN_MAGIC_VENDOR_STR,
+ PRODUCTTEXT,
+ CB_PLUGIN_DESCRIPTION };
+
+
+static cb_backend * cb_backend_type=NULL;
+
+cb_backend * cb_get_backend_type() {
+ return cb_backend_type;
+}
+
+static void cb_set_backend_type(cb_backend * cb) {
+ cb_backend_type=cb;
+}
+
+/* Initialization function */
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+
+int
+chaining_back_init( Slapi_PBlock *pb )
+{
+
+ int rc=0;
+ cb_backend *cb;
+ struct slapdplugin *p;
+
+ cb = (cb_backend *) slapi_ch_calloc( 1, sizeof(cb_backend));
+
+ /* Record the identity of the chaining plugin. used during internal ops.*/
+ slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY, &(cb->identity));
+
+ /* keep a pointer back to the plugin */
+ slapi_pblock_get(pb, SLAPI_PLUGIN, &p);
+ cb->plugin = p;
+
+ /* Initialize misc. fields */
+ cb->config.rwl_config_lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "chaining_db");
+ rc = slapi_pblock_set( pb, SLAPI_PLUGIN_PRIVATE, (void *) cb );
+ cb->pluginDN=slapi_ch_calloc( 1,strlen(PLUGIN_BASE_DN)+strlen(CB_PLUGIN_NAME)+5);
+ sprintf(cb->pluginDN,"cn=%s,%s",CB_PLUGIN_NAME,PLUGIN_BASE_DN);
+
+ cb->configDN=slapi_ch_calloc( 1,strlen(cb->pluginDN)+11);
+ sprintf(cb->configDN,"cn=config,%s",cb->pluginDN);
+
+ /* Set backend callback functions */
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_03 );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&chainingdbdesc );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_SEARCH_FN,
+ (void *) chainingdb_build_candidate_list );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_NEXT_SEARCH_ENTRY_FN,
+ (void *) chainingdb_next_search_entry );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_START_FN,
+ (void *) chainingdb_start ) ;
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_BIND_FN,
+ (void *) chainingdb_bind ) ;
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_ADD_FN,
+ (void *) chaining_back_add );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_DELETE_FN,
+ (void *) chaining_back_delete );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_COMPARE_FN,
+ (void *) chaining_back_compare );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_MODIFY_FN,
+ (void *) chaining_back_modify );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_MODRDN_FN,
+ (void *) chaining_back_modrdn );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_ABANDON_FN,
+ (void *) chaining_back_abandon );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_SIZE_FN,
+ (void *) cb_db_size );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_CLOSE_FN,
+ (void *) cb_back_close );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_CLEANUP_FN,
+ (void *) cb_back_cleanup );
+
+/****
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_ENTRY_RELEASE_FN,
+ (void *) chaining_back_entry_release );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_INIT_INSTANCE_FN,
+ (void *) chaining_back_init);
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_TEST_FN,
+ (void *) cb_back_test );
+****/
+
+ /*
+ ** The following callbacks are not implemented
+ ** by the chaining backend
+ ** - SLAPI_PLUGIN_DB_FLUSH_FN
+ ** - SLAPI_PLUGIN_DB_SEQ_FN
+ ** - SLAPI_PLUGIN_DB_RMDB_FN
+ ** - SLAPI_PLUGIN_DB_DB2INDEX_FN
+ ** - SLAPI_PLUGIN_DB_LDIF2DB_FN
+ ** - SLAPI_PLUGIN_DB_DB2LDIF_FN
+ ** - SLAPI_PLUGIN_DB_ARCHIVE2DB_FN
+ ** - SLAPI_PLUGIN_DB_DB2ARCHIVE_FN
+ ** - SLAPI_PLUGIN_DB_BEGIN_FN
+ ** - SLAPI_PLUGIN_DB_COMMIT_FN
+ ** - SLAPI_PLUGIN_DB_ABORT_FN
+ ** - SLAPI_PLUGIN_DB_NEXT_SEARCH_ENTRY_EXT_FN
+ */
+
+ if ( rc != 0 ) {
+ slapi_log_error( SLAPI_LOG_FATAL, CB_PLUGIN_SUBSYSTEM, "chaining_back_init failed\n");
+ return( -1 );
+ }
+
+ cb_set_backend_type(cb);
+
+ return (0);
+}
+
diff --git a/ldap/servers/plugins/chainingdb/cb_instance.c b/ldap/servers/plugins/chainingdb/cb_instance.c
new file mode 100644
index 00000000..60af726f
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_instance.c
@@ -0,0 +1,1871 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+/*
+** 1 set/get function for each parameter of a backend instance
+** NOTE: Some parameters won't be taken into account until the server has restarted
+** In such cases, the internal conf is not updated but the new value is stored in the
+** dse.ldif file.
+**/
+
+/* Get functions */
+
+static void *cb_instance_hosturl_get(void *arg);
+static void *cb_instance_binduser_get(void *arg);
+static void *cb_instance_userpassword_get(void *arg);
+static void *cb_instance_maxbconn_get(void *arg);
+static void *cb_instance_maxconn_get(void *arg);
+static void *cb_instance_abandonto_get(void *arg);
+static void *cb_instance_maxbconc_get(void *arg);
+static void *cb_instance_maxconc_get(void *arg);
+static void *cb_instance_imperson_get(void *arg);
+static void *cb_instance_connlife_get(void *arg);
+static void *cb_instance_bindto_get(void *arg);
+static void *cb_instance_opto_get(void *arg);
+static void *cb_instance_ref_get(void *arg);
+static void *cb_instance_acl_get(void *arg);
+static void *cb_instance_bindretry_get(void *arg);
+static void *cb_instance_sizelimit_get(void *arg);
+static void *cb_instance_timelimit_get(void *arg);
+static void *cb_instance_hoplimit_get(void *arg);
+static void *cb_instance_max_idle_get(void *arg);
+static void *cb_instance_max_test_get(void *arg);
+
+
+/* Set functions */
+
+static int cb_instance_hosturl_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_binduser_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_userpassword_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_maxbconn_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_maxconn_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_abandonto_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_maxbconc_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_maxconc_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_imperson_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_connlife_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_bindto_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_opto_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_ref_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_acl_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_bindretry_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_sizelimit_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_timelimit_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_hoplimit_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_max_idle_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_max_test_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+
+/* Default hardwired values */
+
+cb_instance_config_info cb_the_instance_config[] = {
+{CB_CONFIG_HOSTURL,CB_CONFIG_TYPE_STRING,"",&cb_instance_hosturl_get,&cb_instance_hosturl_set,CB_ALWAYS_SHOW},
+{CB_CONFIG_BINDUSER, CB_CONFIG_TYPE_STRING, "", &cb_instance_binduser_get, &cb_instance_binduser_set,CB_ALWAYS_SHOW},
+{CB_CONFIG_USERPASSWORD,CB_CONFIG_TYPE_STRING,"",&cb_instance_userpassword_get,&cb_instance_userpassword_set,CB_ALWAYS_SHOW},
+{CB_CONFIG_MAXBINDCONNECTIONS,CB_CONFIG_TYPE_INT,CB_DEF_BIND_MAXCONNECTIONS,&cb_instance_maxbconn_get, &cb_instance_maxbconn_set,CB_ALWAYS_SHOW},
+{CB_CONFIG_MAXCONNECTIONS,CB_CONFIG_TYPE_INT,CB_DEF_MAXCONNECTIONS,&cb_instance_maxconn_get, &cb_instance_maxconn_set,CB_ALWAYS_SHOW},
+{CB_CONFIG_ABANDONTIMEOUT,CB_CONFIG_TYPE_INT,CB_DEF_ABANDON_TIMEOUT,&cb_instance_abandonto_get, &cb_instance_abandonto_set,CB_ALWAYS_SHOW},
+{CB_CONFIG_MAXBINDCONCURRENCY,CB_CONFIG_TYPE_INT,CB_DEF_BIND_MAXCONCURRENCY,&cb_instance_maxbconc_get, &cb_instance_maxbconc_set,CB_ALWAYS_SHOW},
+{CB_CONFIG_MAXCONCURRENCY,CB_CONFIG_TYPE_INT,CB_DEF_MAXCONCURRENCY,&cb_instance_maxconc_get, &cb_instance_maxconc_set,CB_ALWAYS_SHOW},
+{CB_CONFIG_IMPERSONATION,CB_CONFIG_TYPE_ONOFF,CB_DEF_IMPERSONATION,&cb_instance_imperson_get, &cb_instance_imperson_set,CB_ALWAYS_SHOW},
+{CB_CONFIG_CONNLIFETIME,CB_CONFIG_TYPE_INT,CB_DEF_CONNLIFETIME,&cb_instance_connlife_get, &cb_instance_connlife_set,CB_ALWAYS_SHOW},
+{CB_CONFIG_BINDTIMEOUT,CB_CONFIG_TYPE_INT,CB_DEF_BINDTIMEOUT,&cb_instance_bindto_get, &cb_instance_bindto_set,CB_ALWAYS_SHOW},
+{CB_CONFIG_TIMEOUT,CB_CONFIG_TYPE_INT,"0",&cb_instance_opto_get, &cb_instance_opto_set,0},
+{CB_CONFIG_REFERRAL,CB_CONFIG_TYPE_ONOFF,CB_DEF_SEARCHREFERRAL,&cb_instance_ref_get, &cb_instance_ref_set,CB_ALWAYS_SHOW},
+{CB_CONFIG_LOCALACL,CB_CONFIG_TYPE_ONOFF,CB_DEF_LOCALACL,&cb_instance_acl_get, &cb_instance_acl_set,CB_ALWAYS_SHOW},
+{CB_CONFIG_BINDRETRY,CB_CONFIG_TYPE_INT,CB_DEF_BINDRETRY,&cb_instance_bindretry_get, &cb_instance_bindretry_set,CB_ALWAYS_SHOW},
+{CB_CONFIG_SIZELIMIT,CB_CONFIG_TYPE_INT,CB_DEF_SIZELIMIT,&cb_instance_sizelimit_get, &cb_instance_sizelimit_set,CB_ALWAYS_SHOW},
+{CB_CONFIG_TIMELIMIT,CB_CONFIG_TYPE_INT,CB_DEF_TIMELIMIT,&cb_instance_timelimit_get, &cb_instance_timelimit_set,CB_ALWAYS_SHOW},
+{CB_CONFIG_HOPLIMIT,CB_CONFIG_TYPE_INT,CB_DEF_HOPLIMIT,&cb_instance_hoplimit_get, &cb_instance_hoplimit_set,CB_ALWAYS_SHOW},
+{CB_CONFIG_MAX_IDLE_TIME,CB_CONFIG_TYPE_INT,CB_DEF_MAX_IDLE_TIME,&cb_instance_max_idle_get, &cb_instance_max_idle_set,CB_ALWAYS_SHOW},
+{CB_CONFIG_MAX_TEST_TIME,CB_CONFIG_TYPE_INT,CB_DEF_MAX_TEST_TIME,&cb_instance_max_test_get, &cb_instance_max_test_set,CB_ALWAYS_SHOW},
+{NULL, 0, NULL, NULL, NULL, 0}
+};
+
+/* Others forward declarations */
+
+int cb_instance_delete_config_callback(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* e2,
+ int *returncode, char *returntext, void *arg);
+int cb_instance_search_config_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e,
+ int *returncode, char *returntext, void *arg);
+int cb_instance_add_config_callback(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* e2,
+ int *returncode, char *returntext, void *arg);
+static int
+cb_instance_config_set(void *arg, char *attr_name, cb_instance_config_info *config_array,
+struct berval *bval, char *err_buf, int phase, int apply_mod);
+
+
+int
+cb_dont_allow_that(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e,
+ int *returncode, char *returntext, void *arg)
+{
+ *returncode=LDAP_UNWILLING_TO_PERFORM;
+ return SLAPI_DSE_CALLBACK_ERROR;
+}
+
+static char *cb_skeleton_entries[] =
+{
+
+ "dn:cn=monitor, cn=%s, cn=%s, cn=plugins, cn=config\n"
+ "objectclass:top\n"
+ "objectclass:extensibleObject\n"
+ "cn:monitor\n",
+
+ ""
+};
+
+/*
+** Initialize a backend instance with a default configuration
+*/
+
+static void cb_instance_config_set_default(cb_backend_instance *inst)
+{
+ cb_instance_config_info *config;
+ char err_buf[CB_BUFSIZE];
+
+ for (config = cb_the_instance_config; config->config_name != NULL; config++) {
+ cb_instance_config_set((void *)inst,
+ config->config_name, cb_the_instance_config, NULL /* use default */, err_buf,
+ CB_CONFIG_PHASE_INITIALIZATION, 1 /* apply */);
+ }
+
+ /* Set backend instance flags */
+ if (inst->inst_be)
+ slapi_be_set_flag(inst->inst_be,SLAPI_BE_FLAG_REMOTE_DATA);
+}
+
+/*
+** Allocate a new chaining backend instance. Internal structure
+*/
+
+static cb_backend_instance * cb_instance_alloc(cb_backend * cb, char * name, char * basedn) {
+
+ int i;
+
+ cb_backend_instance * inst =
+ (cb_backend_instance *)slapi_ch_calloc(1, sizeof(cb_backend_instance));
+
+ /* associated_be_is_disabled defaults to 0 - this may be a problem if the associated
+ be is disabled at instance creation time
+ */
+ inst->inst_name = slapi_ch_strdup(name);
+ inst->monitor.mutex = slapi_new_mutex();
+ inst->monitor_availability.cpt_lock = slapi_new_mutex();
+ inst->monitor_availability.lock_timeLimit = slapi_new_mutex();
+ inst->pool= (cb_conn_pool *) slapi_ch_calloc(1,sizeof(cb_conn_pool));
+ inst->pool->conn.conn_list_mutex = slapi_new_mutex();
+ inst->pool->conn.conn_list_cv = slapi_new_condvar(inst->pool->conn.conn_list_mutex);
+ inst->pool->bindit=1;
+
+ inst->bind_pool= (cb_conn_pool *) slapi_ch_calloc(1,sizeof(cb_conn_pool));
+ inst->bind_pool->conn.conn_list_mutex = slapi_new_mutex();
+ inst->bind_pool->conn.conn_list_cv = slapi_new_condvar(inst->bind_pool->conn.conn_list_mutex);
+
+ inst->backend_type=cb;
+ /* initialize monitor_availability */
+ inst->monitor_availability.farmserver_state = FARMSERVER_AVAILABLE ; /* we expect the farm to be available */
+ inst->monitor_availability.cpt = 0 ; /* set up the failed conn counter to 0 */
+
+ /* create RW lock to protect the config */
+ inst->rwl_config_lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, slapi_ch_strdup(name));
+
+ /* quick hack */
+ /* put a ref to the config lock in the pool */
+ /* so that the connection mgmt module can */
+ /* access the config safely */
+
+ inst->pool->rwl_config_lock = inst->rwl_config_lock;
+ inst->bind_pool->rwl_config_lock = inst->rwl_config_lock;
+
+ for (i=0; i < MAX_CONN_ARRAY; i++) {
+ inst->pool->connarray[i] = NULL;
+ inst->bind_pool->connarray[i] = NULL;
+ }
+
+ /* Config is now merged with the backend entry */
+ inst->configDn=slapi_ch_strdup(basedn);
+
+ inst->monitorDn=(char *) slapi_ch_calloc(1,strlen(basedn)+15);
+ sprintf(inst->monitorDn,"cn=monitor,%s",basedn);
+
+ inst->eq_ctx = NULL;
+
+ return inst;
+}
+
+void cb_instance_free(cb_backend_instance * inst) {
+
+ if (inst) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+
+ if ( inst->eq_ctx != NULL )
+ {
+ slapi_eq_cancel(inst->eq_ctx);
+ inst->eq_ctx = NULL;
+ }
+
+ if (inst->bind_pool)
+ cb_close_conn_pool(inst->bind_pool);
+ if (inst->pool)
+ cb_close_conn_pool(inst->pool);
+
+ slapi_destroy_condvar(inst->bind_pool->conn.conn_list_cv);
+ slapi_destroy_condvar(inst->pool->conn.conn_list_cv);
+ slapi_destroy_mutex(inst->monitor.mutex);
+ slapi_destroy_mutex(inst->bind_pool->conn.conn_list_mutex);
+ slapi_destroy_mutex(inst->pool->conn.conn_list_mutex);
+ slapi_destroy_mutex(inst->monitor_availability.cpt_lock);
+ slapi_destroy_mutex(inst->monitor_availability.lock_timeLimit);
+ slapi_ch_free((void **) &inst->configDn);
+ slapi_ch_free((void **) &inst->monitorDn);
+ slapi_ch_free((void **) &inst->inst_name);
+ charray_free(inst->every_attribute);
+
+ slapi_ch_free((void **) &inst->bind_pool);
+ slapi_ch_free((void **) &inst->pool);
+
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+
+ PR_DestroyRWLock(inst->rwl_config_lock);
+
+ slapi_ch_free((void **) &inst);
+ }
+/* XXXSD */
+}
+
+/*
+** Check the change the configuration of an existing chaining
+** backend instance.
+*/
+
+int cb_instance_modify_config_check_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e,
+ int *returncode, char *returntext, void *arg) {
+
+ cb_backend_instance * inst = (cb_backend_instance *) arg;
+ LDAPMod **mods;
+ int rc = LDAP_SUCCESS;
+ int i;
+ char * attr_name;
+ returntext[0] = '\0';
+
+ CB_ASSERT(inst!=NULL);
+ slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods );
+
+ /* First pass to validate input */
+ for (i = 0; mods[i] && LDAP_SUCCESS == rc; i++) {
+ attr_name = mods[i]->mod_type;
+
+ /* specific processing for multi-valued attributes */
+ if ( !strcasecmp ( attr_name, CB_CONFIG_SUFFIX )) {
+ sprintf(returntext, "suffix modification not allowed\n");
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ continue;
+ } else
+ if ( !strcasecmp ( attr_name, CB_CONFIG_ILLEGAL_ATTRS )) {
+ continue;
+ } else
+ if ( !strcasecmp ( attr_name, CB_CONFIG_CHAINING_COMPONENTS )) {
+ continue;
+ } else
+
+ /* CB_CONFIG_BINDUSER & CB_CONFIG_USERPASSWORD may be added */
+ /* or deleted */
+
+ if ( !strcasecmp ( attr_name, CB_CONFIG_USERPASSWORD )) {
+ continue;
+ } else
+ if ( !strcasecmp ( attr_name, CB_CONFIG_BINDUSER )) {
+
+ /* Make sure value is not forbidden */
+ if ((mods[i]->mod_op & LDAP_MOD_REPLACE) ||
+ ((mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_ADD)) {
+
+ rc = cb_instance_config_set((void *) inst, attr_name,
+ cb_the_instance_config, mods[i]->mod_bvalues[0], returntext,
+ CB_CONFIG_PHASE_RUNNING, 0);
+ continue;
+ }
+ }
+
+ if ((mods[i]->mod_op & LDAP_MOD_DELETE) ||
+ ((mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_ADD)) {
+ rc= LDAP_UNWILLING_TO_PERFORM;
+ sprintf(returntext, "%s attributes is not allowed",
+ (mods[i]->mod_op & LDAP_MOD_DELETE) ? "Deleting" : "Adding");
+ } else if (mods[i]->mod_op & LDAP_MOD_REPLACE) {
+ /* This assumes there is only one bval for this mod. */
+ rc = cb_instance_config_set((void *) inst, attr_name,
+ cb_the_instance_config, mods[i]->mod_bvalues[0], returntext,
+ CB_CONFIG_PHASE_RUNNING, 0);
+ }
+ }
+ *returncode= rc;
+ return ((LDAP_SUCCESS == rc) ? 1:-1);
+}
+
+
+/*
+** Change the configuration of an existing chaining
+** backend instance.
+*/
+
+int cb_instance_modify_config_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e,
+ int *returncode, char *returntext, void *arg) {
+
+ cb_backend_instance * inst = (cb_backend_instance *) arg;
+ LDAPMod **mods;
+ int rc = LDAP_SUCCESS;
+ int i;
+ int reopen_conn=0;
+ char * attr_name;
+ returntext[0] = '\0';
+
+ CB_ASSERT(inst!=NULL);
+ slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods );
+
+ /* input checked in the preop modify callback */
+
+ for (i = 0; mods[i] && LDAP_SUCCESS == rc; i++) {
+ attr_name = mods[i]->mod_type;
+
+ /* specific processing for multi-valued attributes */
+ if ( !strcasecmp ( attr_name, CB_CONFIG_ILLEGAL_ATTRS )) {
+ char * config_attr_value;
+ int done=0;
+ int j;
+
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ for (j = 0; mods[i]->mod_bvalues && mods[i]->mod_bvalues[j]; j++) {
+ config_attr_value = (char *) mods[i]->mod_bvalues[j]->bv_val;
+ if ( mods[i]->mod_op & LDAP_MOD_REPLACE) {
+ if (!done) {
+ charray_free(inst->illegal_attributes);
+ inst->illegal_attributes=NULL;
+ done=1;
+ }
+ charray_add(&inst->illegal_attributes,
+ slapi_ch_strdup(config_attr_value));
+ } else
+ if ( (mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_ADD) {
+ charray_add(&inst->illegal_attributes,
+ slapi_ch_strdup(config_attr_value));
+ } else
+ if ( (mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_DELETE) {
+ charray_remove(inst->illegal_attributes,
+ slapi_ch_strdup(config_attr_value));
+ }
+ }
+ if (NULL == mods[i]->mod_bvalues) {
+ charray_free(inst->illegal_attributes);
+ inst->illegal_attributes=NULL;
+ }
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ continue;
+ }
+ if ( !strcasecmp ( attr_name, CB_CONFIG_CHAINING_COMPONENTS )) {
+ char * config_attr_value;
+ int done=0;
+ int j;
+
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ for (j = 0; mods[i]->mod_bvalues && mods[i]->mod_bvalues[j]; j++) {
+ config_attr_value = (char *) mods[i]->mod_bvalues[j]->bv_val;
+ if ( mods[i]->mod_op & LDAP_MOD_REPLACE) {
+ if (!done) {
+ charray_free(inst->chaining_components);
+ inst->chaining_components=NULL;
+ done=1;
+ }
+ /* XXXSD assume dns */
+ charray_add(&inst->chaining_components,
+ slapi_dn_normalize(slapi_ch_strdup(config_attr_value)));
+ } else
+ if ( (mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_ADD) {
+ charray_add(&inst->chaining_components,
+ slapi_dn_normalize(slapi_ch_strdup(config_attr_value)));
+ } else
+ if ( (mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_DELETE) {
+ charray_remove(inst->chaining_components,
+ slapi_dn_normalize(slapi_ch_strdup(config_attr_value)));
+ }
+ }
+ if (NULL == mods[i]->mod_bvalues) {
+ charray_free(inst->chaining_components);
+ inst->chaining_components=NULL;
+ }
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ continue;
+ }
+
+
+
+ if ((mods[i]->mod_op & LDAP_MOD_DELETE) ||
+ ((mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_ADD)) {
+
+ /* Special processing for binddn & password */
+ /* because they are optional */
+
+ if (( !strcasecmp ( attr_name, CB_CONFIG_BINDUSER )) ||
+ ( !strcasecmp ( attr_name, CB_CONFIG_USERPASSWORD ))) {
+
+ if (mods[i]->mod_op & LDAP_MOD_DELETE) {
+ rc = cb_instance_config_set((void *) inst, attr_name,
+ cb_the_instance_config, NULL, returntext,
+ CB_CONFIG_PHASE_RUNNING, 1); }
+ else {
+ rc = cb_instance_config_set((void *) inst, attr_name,
+ cb_the_instance_config, mods[i]->mod_bvalues[0], returntext,
+ CB_CONFIG_PHASE_RUNNING, 1);
+ }
+ if (rc==CB_REOPEN_CONN) {
+ reopen_conn=1;
+ rc=LDAP_SUCCESS;
+ }
+ continue;
+ }
+
+ rc= LDAP_UNWILLING_TO_PERFORM;
+ sprintf(returntext, "%s attributes is not allowed",
+ (mods[i]->mod_op & LDAP_MOD_DELETE) ? "Deleting" : "Adding");
+ } else if (mods[i]->mod_op & LDAP_MOD_REPLACE) {
+ /* This assumes there is only one bval for this mod. */
+ rc = cb_instance_config_set((void *) inst, attr_name,
+ cb_the_instance_config, mods[i]->mod_bvalues[0], returntext,
+ CB_CONFIG_PHASE_RUNNING, 1);
+
+ /* Update requires to reopen connections */
+ /* Expensive operation so do it only once */
+ if (rc==CB_REOPEN_CONN) {
+ reopen_conn=1;
+ rc=LDAP_SUCCESS;
+ }
+ }
+ }
+
+ *returncode= rc;
+
+ if (reopen_conn) {
+ cb_stale_all_connections(inst);
+ }
+
+ return ((LDAP_SUCCESS == rc) ? SLAPI_DSE_CALLBACK_OK:SLAPI_DSE_CALLBACK_ERROR);
+}
+
+/*
+** Parse and instantiate backend instances
+*/
+
+int
+cb_parse_instance_config_entry(cb_backend * cb, Slapi_Entry * e) {
+
+ int rc =LDAP_SUCCESS;
+ Slapi_Attr *attr = NULL;
+ Slapi_Value *sval;
+ const struct berval *attrValue;
+ cb_backend_instance *inst=NULL;
+ char *instname;
+ Slapi_PBlock *search_pb=NULL;
+ char retmsg[CB_BUFSIZE];
+
+ CB_ASSERT(e!=NULL);
+
+ /*
+ ** Retrieve the instance name and make sure it is not
+ ** already declared.
+ */
+
+ if ( 0 == slapi_entry_attr_find( e, CB_CONFIG_INSTNAME, &attr )) {
+ slapi_attr_first_value(attr, &sval);
+ attrValue = slapi_value_get_berval(sval);
+ instname=attrValue->bv_val;
+ } else {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Malformed backend instance (<%s> missing)>\n", CB_CONFIG_INSTNAME);
+ return LDAP_LOCAL_ERROR;
+ }
+
+ /* Allocate a new backend internal data structure */
+ inst = cb_instance_alloc(cb,instname,slapi_entry_get_dn(e));
+
+ /* Emulate a add config entry to configure */
+ /* this backend instance. */
+
+ cb_instance_add_config_callback(NULL,e,NULL,&rc,retmsg,inst);
+ if ( rc != LDAP_SUCCESS ) {
+ cb_instance_free(inst);
+ }
+ return rc;
+}
+
+/*
+** Update the instance configuration
+*/
+
+static int
+cb_instance_config_initialize(cb_backend_instance * inst, Slapi_Entry * e , int phase, int apply) {
+
+ int rc =LDAP_SUCCESS;
+ Slapi_Attr *attr = NULL;
+ Slapi_Value *sval;
+ struct berval * bval;
+ int using_def_connlifetime,i;
+ char err_buf[CB_BUFSIZE];
+ int urlfound=0;
+ char *rootdn;
+
+ using_def_connlifetime=1;
+
+ for (slapi_entry_first_attr(e, &attr); attr; slapi_entry_next_attr(e, attr, &attr)) {
+ char * attr_name=NULL;
+ slapi_attr_get_type(attr, &attr_name);
+
+ if ( !strcasecmp ( attr_name, CB_CONFIG_SUFFIX )) {
+ if (apply && ( inst->inst_be != NULL )) {
+ Slapi_DN *suffix;
+ suffix = slapi_sdn_new();
+ i = slapi_attr_first_value(attr, &sval);
+ while (i != -1 ) {
+ bval = (struct berval *) slapi_value_get_berval(sval);
+ slapi_sdn_init_dn_byref(suffix, bval->bv_val);
+
+ if (!slapi_be_issuffix(inst->inst_be, suffix)) {
+ slapi_be_addsuffix(inst->inst_be, suffix);
+ }
+ slapi_sdn_done(suffix);
+ slapi_sdn_free(&suffix);
+ i = slapi_attr_next_value(attr, i, &sval);
+ }
+ }
+ continue;
+ } else
+ if ( !strcasecmp ( attr_name, CB_CONFIG_CHAINING_COMPONENTS )) {
+
+ if (apply) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ i = slapi_attr_first_value(attr, &sval);
+ charray_free(inst->chaining_components);
+ inst->chaining_components=NULL;
+ while (i != -1 ) {
+ bval = (struct berval *) slapi_value_get_berval(sval);
+ charray_add(&inst->chaining_components,
+ slapi_dn_normalize(slapi_ch_strdup(bval->bv_val)));
+ i = slapi_attr_next_value(attr, i, &sval);
+ }
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+ continue;
+ } else
+ if ( !strcasecmp ( attr_name, CB_CONFIG_ILLEGAL_ATTRS )) {
+
+ if (apply) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ i = slapi_attr_first_value(attr, &sval);
+ charray_free(inst->illegal_attributes);
+ inst->illegal_attributes=NULL;
+ while (i != -1 ) {
+ bval = (struct berval *) slapi_value_get_berval(sval);
+ charray_add(&inst->illegal_attributes,
+ slapi_ch_strdup(bval->bv_val));
+ i = slapi_attr_next_value(attr, i, &sval);
+ }
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+ continue;
+ }
+
+
+ if ( !strcasecmp ( attr_name, CB_CONFIG_HOSTURL )) {
+ urlfound=1;
+ }
+
+
+ /* We are assuming that each of these attributes are to have
+ * only one value. If they have more than one value, like
+ * the nsslapd-suffix attribute, then they need to be
+ * handled differently. */
+
+ slapi_attr_first_value(attr, &sval);
+ bval = (struct berval *) slapi_value_get_berval(sval);
+
+ if (cb_instance_config_set((void *) inst, attr_name,
+ cb_the_instance_config, bval, err_buf, phase, apply ) != LDAP_SUCCESS) {
+ slapi_log_error( SLAPI_LOG_FATAL,
+ CB_PLUGIN_SUBSYSTEM,"Error with config attribute %s : %s\n",
+ attr_name, err_buf);
+ rc=LDAP_LOCAL_ERROR;
+ break;
+ }
+ if ( !strcasecmp ( attr_name, CB_CONFIG_CONNLIFETIME )) {
+ using_def_connlifetime=0;
+ }
+ }
+
+
+ /*
+ ** Check for mandatory attributes
+ ** Post-Processing
+ */
+
+ if (LDAP_SUCCESS == rc) {
+ if (!urlfound) {
+ slapi_log_error( SLAPI_LOG_FATAL, CB_PLUGIN_SUBSYSTEM,
+ "Malformed backend instance entry. Mandatory attr <%s> missing\n",
+ CB_CONFIG_HOSTURL);
+ rc= LDAP_LOCAL_ERROR;
+ }
+
+ if (apply ) {
+ if ( using_def_connlifetime &&
+ strchr( inst->pool->hostname, ' ' ) != NULL ) {
+
+ cb_instance_config_set((void *)inst, CB_CONFIG_CONNLIFETIME,
+ cb_the_instance_config, NULL /* use default */, err_buf,
+ CB_CONFIG_PHASE_INITIALIZATION, 1 );
+ }
+ }
+ }
+
+ /*
+ ** Additional checks
+ ** It is forbidden to use directory manager as proxy user
+ ** due to a bug in the acl check
+ */
+
+ rootdn=cb_get_rootdn();
+
+ if (inst->impersonate && inst->pool && inst->pool->binddn &&
+ !strcmp(inst->pool->binddn,rootdn)) { /* UTF8 aware */
+ slapi_log_error( SLAPI_LOG_FATAL,
+ CB_PLUGIN_SUBSYSTEM,"Error with config attribute %s (%s: forbidden value)\n",
+ CB_CONFIG_BINDUSER, rootdn);
+ rc=LDAP_LOCAL_ERROR;
+ }
+ slapi_ch_free((void **)&rootdn);
+
+ return rc;
+}
+
+/********************************************************
+** Get and set functions for chaining backend instances *
+*********************************************************
+*/
+
+static void *cb_instance_hosturl_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ char * data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data = slapi_ch_strdup(inst->pool->url);
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return data;
+}
+
+static int cb_instance_hosturl_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance *inst=(cb_backend_instance *) arg;
+ char *url = (char *) value;
+ LDAPURLDesc *ludp=NULL;
+ int rc=LDAP_SUCCESS;
+
+ if (( rc = ldap_url_parse( url, &ludp )) != 0 ) {
+ strcpy(errorbuf,cb_urlparse_err2string( rc ));
+ if (CB_CONFIG_PHASE_INITIALIZATION == phase)
+ inst->pool->url=slapi_ch_strdup("");
+ return(LDAP_INVALID_SYNTAX);
+ }
+
+ if (apply) {
+
+ char * ptr;
+
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+
+ if (( phase != CB_CONFIG_PHASE_INITIALIZATION ) &&
+ ( phase != CB_CONFIG_PHASE_STARTUP )) {
+
+ /* Dynamic modification */
+ /* Don't free char * pointer now */
+ /* STore them in a waste basket */
+ /* Will be relesase when the backend stops */
+
+ if (inst->pool->hostname)
+ charray_add(&inst->pool->waste_basket,inst->pool->hostname);
+ if (inst->pool->url)
+ charray_add(&inst->pool->waste_basket,inst->pool->url);
+
+ if (inst->bind_pool->hostname)
+ charray_add(&inst->bind_pool->waste_basket,inst->bind_pool->hostname);
+ if (inst->bind_pool->url)
+ charray_add(&inst->bind_pool->waste_basket,inst->bind_pool->url);
+
+ /* Require connection cleanup */
+ rc=CB_REOPEN_CONN;
+ }
+
+ /* Normal case. Extract useful data from */
+ /* the url and update the configuration */
+
+ if ((ludp->lud_host==NULL) || (strlen(ludp->lud_host)==0)) {
+ inst->pool->hostname=(char *)slapi_ch_strdup((char *)get_localhost_DNS());
+ } else {
+ inst->pool->hostname = slapi_ch_strdup( ludp->lud_host );
+ }
+ inst->pool->url = slapi_ch_strdup( url);
+ inst->pool->secure = (( ludp->lud_options & LDAP_URL_OPT_SECURE ) != 0 );
+
+ if ((ludp->lud_port==0) && inst->pool->secure)
+ inst->pool->port=CB_LDAP_SECURE_PORT;
+ else
+ inst->pool->port = ludp->lud_port;
+
+ /* Build a charray of <host>:<port> */
+ /* hostname is of the form <host>[:port] <host>[:port] */
+
+ { char * aBufCopy, * aHostName;
+ char * iter = NULL;
+ aBufCopy= aBufCopy=slapi_ch_strdup(inst->pool->hostname);
+
+ aHostName=ldap_utf8strtok_r(aBufCopy," ", &iter);
+ charray_free(inst->url_array);
+ inst->url_array=NULL;
+ while (aHostName) {
+
+ char * aHostPort = slapi_ch_calloc(1,strlen(aHostName)+30);
+ if ( NULL == ( ptr=strstr(aHostName,":")))
+ sprintf(aHostPort,"%s://%s:%d/",
+ inst->pool->secure ? "ldaps" : "ldap",
+ aHostName,inst->pool->port);
+ else
+ sprintf(aHostPort,"%s://%s/",
+ inst->pool->secure ? "ldaps" : "ldap",
+ aHostName);
+
+ charray_add(&inst->url_array,aHostPort);
+ aHostName=ldap_utf8strtok_r(NULL," ", &iter);
+ }
+
+ slapi_ch_free((void **) &aBufCopy);
+ }
+
+ inst->bind_pool->port=inst->pool->port;
+ inst->bind_pool->secure=inst->pool->secure;
+ inst->bind_pool->hostname=slapi_ch_strdup(inst->pool->hostname);
+
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+
+ if ( ludp != NULL ) {
+ ldap_free_urldesc( ludp );
+ }
+ return rc;
+}
+
+static void *cb_instance_binduser_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ char * data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data = slapi_ch_strdup(inst->pool->binddn2); /* not normalized */
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return data;
+}
+
+static int cb_instance_binduser_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int rc=LDAP_SUCCESS;
+
+ if (apply) {
+
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ if (( phase != CB_CONFIG_PHASE_INITIALIZATION ) &&
+ ( phase != CB_CONFIG_PHASE_STARTUP )) {
+
+ /* Dynamic modif */
+ /* Free user later */
+
+ charray_add(&inst->pool->waste_basket,inst->pool->binddn);
+ charray_add(&inst->pool->waste_basket,inst->pool->binddn2);
+ rc=CB_REOPEN_CONN;
+ }
+
+ inst->pool->binddn=slapi_ch_strdup((char *) value);
+ inst->pool->binddn2=slapi_ch_strdup((char *) value);
+ slapi_dn_normalize_case(inst->pool->binddn);
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ } else {
+
+ /* Security check */
+ /* directory manager of the farm server should not be used as */
+ /* proxing user. This is hard to check, so assume same directory */
+ /* manager across servers. */
+
+ char * rootdn = cb_get_rootdn();
+ char * theValueCopy = NULL;
+
+ if (value) {
+ theValueCopy=slapi_ch_strdup((char *) value);
+ slapi_dn_normalize_case(theValueCopy);
+ }
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ if (inst->impersonate && theValueCopy &&
+ !strcmp(theValueCopy,rootdn)) { /* UTF8-aware. See cb_get_dn() */
+ rc=LDAP_UNWILLING_TO_PERFORM;
+ if (errorbuf) {
+ sprintf(errorbuf,"value %s not allowed",rootdn);
+ }
+ }
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+
+ slapi_ch_free((void **)&theValueCopy);
+ slapi_ch_free((void **)&rootdn);
+ }
+
+ return rc;
+}
+
+
+static void *cb_instance_userpassword_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ char * data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data = slapi_ch_strdup(inst->pool->password);
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return data;
+}
+
+static int cb_instance_userpassword_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int rc=LDAP_SUCCESS;
+
+ if (apply) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ if (( phase != CB_CONFIG_PHASE_INITIALIZATION ) &&
+ ( phase != CB_CONFIG_PHASE_STARTUP )) {
+
+ /* Dynamic modif */
+ charray_add(&inst->pool->waste_basket,inst->pool->password);
+ rc=CB_REOPEN_CONN;
+ }
+
+ inst->pool->password=slapi_ch_strdup((char *) value);
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+ return rc;
+}
+
+static void *cb_instance_sizelimit_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data = inst->sizelimit;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return (void *) data;
+}
+
+static int cb_instance_sizelimit_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ if (apply) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ inst->sizelimit=(int) value;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ if (inst->inst_be)
+ be_set_sizelimit(inst->inst_be, (int) value);
+ }
+ return LDAP_SUCCESS;
+}
+
+static void *cb_instance_timelimit_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data = inst->timelimit;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return (void *) data;
+}
+
+static int cb_instance_timelimit_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ if (apply) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ inst->timelimit=(int) value;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ if (inst->inst_be)
+ be_set_timelimit(inst->inst_be, (int) value);
+ }
+ return LDAP_SUCCESS;
+}
+
+static void *cb_instance_max_test_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data = inst->max_test_time;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return (void *) data;
+}
+
+static int cb_instance_max_test_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ if (apply) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ inst->max_test_time=(int) value;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+ return LDAP_SUCCESS;
+}
+
+static void *cb_instance_max_idle_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data = inst->max_idle_time;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return (void *) data;
+}
+
+static int cb_instance_max_idle_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ if (apply) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ inst->max_idle_time=(int) value;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+ return LDAP_SUCCESS;
+}
+
+
+static void *cb_instance_hoplimit_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data = inst->hoplimit;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return (void *) data;
+}
+
+static int cb_instance_hoplimit_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ if (apply) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ inst->hoplimit=(int) value;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+ return LDAP_SUCCESS;
+}
+
+static void *cb_instance_maxbconn_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data = inst->bind_pool->conn.maxconnections;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return (void *) data;
+}
+
+static int cb_instance_maxbconn_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ if (apply) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ inst->bind_pool->conn.maxconnections=(int) value;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+ return LDAP_SUCCESS;
+}
+
+static void *cb_instance_maxconn_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data = inst->pool->conn.maxconnections;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return (void *) data;
+}
+
+static int cb_instance_maxconn_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ if (apply) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ inst->pool->conn.maxconnections=(int) value;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+ return LDAP_SUCCESS;
+}
+
+static void *cb_instance_abandonto_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data = inst->abandon_timeout.tv_sec;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return (void *) data;
+}
+
+static int cb_instance_abandonto_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+
+ if (apply) {
+ if (( phase != CB_CONFIG_PHASE_INITIALIZATION ) &&
+ ( phase != CB_CONFIG_PHASE_STARTUP )) {
+
+ /* Dynamic modif not supported */
+ /* Stored in ldif only */
+ return LDAP_SUCCESS;
+ }
+
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ inst->abandon_timeout.tv_sec=(int) value;
+ inst->abandon_timeout.tv_usec=0;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+ return LDAP_SUCCESS;
+}
+
+static void *cb_instance_maxbconc_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data = inst->bind_pool->conn.maxconcurrency;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return (void *) data;
+}
+
+static int cb_instance_maxbconc_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ if (apply) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ inst->bind_pool->conn.maxconcurrency=(int) value;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+ return LDAP_SUCCESS;
+}
+
+static void *cb_instance_maxconc_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data = inst->pool->conn.maxconcurrency;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return (void *) data;
+}
+
+static int cb_instance_maxconc_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ if (apply) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ inst->pool->conn.maxconcurrency=(int) value;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+ return LDAP_SUCCESS;
+}
+
+static void *cb_instance_imperson_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data = inst->impersonate;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return (void *) data;
+}
+
+static int cb_instance_imperson_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int rc=LDAP_SUCCESS;
+
+ if (apply) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ inst->impersonate=(int) value;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ } else {
+ /* Security check: Make sure the proxing user is */
+ /* not the directory manager. */
+
+ char * rootdn=cb_get_rootdn();
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ if (((int) value) && inst->pool && inst->pool->binddn &&
+ !strcmp(inst->pool->binddn,rootdn)) { /* UTF-8 aware */
+ rc=LDAP_UNWILLING_TO_PERFORM;
+ if (errorbuf)
+ sprintf(errorbuf,"Proxy mode incompatible with %s value (%s not allowed)",
+ CB_CONFIG_BINDUSER,rootdn);
+ }
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ slapi_ch_free((void **)&rootdn);
+ }
+
+ return rc;
+}
+
+static void *cb_instance_connlife_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data=inst->pool->conn.connlifetime;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return (void *) data;
+}
+
+static int cb_instance_connlife_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ if (apply) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ inst->pool->conn.connlifetime=(int) value;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+ return LDAP_SUCCESS;
+}
+
+static void *cb_instance_bindto_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data=inst->bind_pool->conn.op_timeout.tv_sec;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return (void *) data;
+}
+
+static int cb_instance_bindto_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ if (apply) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ inst->bind_pool->conn.op_timeout.tv_sec=(int) value;
+ inst->bind_pool->conn.op_timeout.tv_usec=0;
+ inst->bind_pool->conn.bind_timeout.tv_sec=(int) value;
+ inst->bind_pool->conn.bind_timeout.tv_usec=0;
+ /* Used to bind to the farm server */
+ inst->pool->conn.bind_timeout.tv_sec=(int) value;
+ inst->pool->conn.bind_timeout.tv_usec=0;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+ return LDAP_SUCCESS;
+}
+
+static void *cb_instance_opto_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data=inst->pool->conn.op_timeout.tv_sec;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return (void *) data;
+}
+
+static int cb_instance_opto_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ if (apply) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ inst->pool->conn.op_timeout.tv_sec=(int) value;
+ inst->pool->conn.op_timeout.tv_usec=0;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+ return LDAP_SUCCESS;
+}
+
+static void *cb_instance_ref_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data=inst->searchreferral;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return (void *) data;
+}
+
+static int cb_instance_ref_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ if (apply) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ inst->searchreferral=(int) value;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+ return LDAP_SUCCESS;
+}
+
+static void *cb_instance_acl_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data=inst->local_acl;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return (void *) data;
+}
+
+static int cb_instance_acl_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+
+ if (apply) {
+ if (( phase != CB_CONFIG_PHASE_INITIALIZATION ) &&
+ ( phase != CB_CONFIG_PHASE_STARTUP )) {
+
+ /* Dynamic modif not supported */
+ /* Stored in ldif only */
+ return LDAP_SUCCESS;
+ }
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ inst->local_acl=(int) value;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+ return LDAP_SUCCESS;
+}
+
+static void *cb_instance_bindretry_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data=inst->bind_retry;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return (void *) data;
+}
+
+static int cb_instance_bindretry_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ if (apply) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ inst->bind_retry=(int) value;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+ return LDAP_SUCCESS;
+}
+
+
+
+
+/* Finds an entry in a config_info array with the given name. Returns
+ * the entry on success and NULL when not found.
+ */
+static cb_instance_config_info *cb_get_config_info(cb_instance_config_info *config_array, char *attr_name)
+{
+ int x;
+
+ for(x = 0; config_array[x].config_name != NULL; x++) {
+ if (!strcasecmp(config_array[x].config_name, attr_name)) {
+ return &(config_array[x]);
+ }
+ }
+ return NULL;
+}
+
+/*
+** Update an attribute value
+** For now, unknown attributes are ignored
+** Return a LDAP error code OR CB_REOPEN_CONN when the
+** update requires to close open connections.
+*/
+
+static int
+cb_instance_config_set(void *arg, char *attr_name, cb_instance_config_info *config_array,
+struct berval *bval, char *err_buf, int phase, int apply_mod)
+{
+ cb_instance_config_info *config;
+ int use_default;
+ int int_val;
+ long long_val;
+ int retval=LDAP_LOCAL_ERROR;
+
+ config = cb_get_config_info(config_array, attr_name);
+ if (NULL == config) {
+ /* Ignore unknown attributes */
+ return LDAP_SUCCESS;
+ }
+
+ /* If the config phase is initialization or if bval is NULL, we will use
+ * the default value for the attribute. */
+ if (CB_CONFIG_PHASE_INITIALIZATION == phase || NULL == bval) {
+ use_default = 1;
+ } else {
+ use_default = 0;
+ /* Since we are setting the value for the config attribute, we
+ * need to turn on the CB_PREVIOUSLY_SET flag to make
+ * sure this attribute is shown. */
+ config->config_flags |= CB_PREVIOUSLY_SET;
+ }
+
+ switch(config->config_type) {
+ case CB_CONFIG_TYPE_INT:
+ if (use_default) {
+ int_val = cb_atoi(config->config_default_value);
+ } else {
+ int_val = cb_atoi((char *)bval->bv_val);
+ }
+ retval = config->config_set_fn(arg, (void *) int_val, err_buf, phase, apply_mod);
+ break;
+ case CB_CONFIG_TYPE_INT_OCTAL:
+ if (use_default) {
+ int_val = (int) strtol(config->config_default_value, NULL, 8);
+ } else {
+ int_val = (int) strtol((char *)bval->bv_val, NULL, 8);
+ }
+ retval = config->config_set_fn(arg, (void *) int_val, err_buf, phase, apply_mod);
+ break;
+ case CB_CONFIG_TYPE_LONG:
+ if (use_default) {
+ long_val = cb_atol(config->config_default_value);
+ } else {
+ long_val = cb_atol((char *)bval->bv_val);
+ }
+ retval = config->config_set_fn(arg, (void *) long_val, err_buf, phase, apply_mod);
+ break;
+ case CB_CONFIG_TYPE_STRING:
+ if (use_default) {
+ retval = config->config_set_fn(arg, config->config_default_value, err_buf, phase, apply_mod);
+ } else {
+ retval = config->config_set_fn(arg, bval->bv_val, err_buf, phase, apply_mod);
+ }
+ break;
+ case CB_CONFIG_TYPE_ONOFF:
+ if (use_default) {
+ int_val = !strcasecmp(config->config_default_value, "on");
+ } else {
+ int_val = !strcasecmp((char *) bval->bv_val, "on");
+ }
+ retval = config->config_set_fn(arg, (void *) int_val, err_buf, phase, apply_mod);
+ break;
+ }
+ return retval;
+}
+
+/* Utility function used in creating config entries. Using the
+ * config_info, this function gets info and formats in the correct
+ * way.
+ */
+void cb_instance_config_get(void *arg, cb_instance_config_info *config, char *buf)
+{
+ char *tmp_string;
+
+ if (config == NULL) {
+ buf[0] = '\0';
+ }
+
+ switch(config->config_type) {
+ case CB_CONFIG_TYPE_INT:
+ sprintf(buf, "%d", (int) config->config_get_fn(arg));
+ break;
+ case CB_CONFIG_TYPE_INT_OCTAL:
+ sprintf(buf, "%o", (int) config->config_get_fn(arg));
+ break;
+ case CB_CONFIG_TYPE_LONG:
+ sprintf(buf, "%d", (long) config->config_get_fn(arg));
+ break;
+ case CB_CONFIG_TYPE_STRING:
+ /* Remember the get function for strings returns memory
+ * that must be freed. */
+ tmp_string = (char *) config->config_get_fn(arg);
+ sprintf(buf, "%s", (char *) tmp_string);
+ slapi_ch_free((void **)&tmp_string);
+ break;
+ case CB_CONFIG_TYPE_ONOFF:
+ if ((int) config->config_get_fn(arg)) {
+ sprintf(buf,"%s","on");
+ } else {
+ sprintf(buf,"%s","off");
+ }
+ break;
+ default:
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Invalid attribute syntax.\n");
+
+ }
+}
+
+/*
+** Search for instance config entry
+** Always return 'active' values because some configuration changes
+** won't be taken into account until the server restarts
+*/
+
+int cb_instance_search_config_callback(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter,
+ int *returncode, char *returntext, void *arg) {
+
+ char buf[CB_BUFSIZE];
+ struct berval val;
+ struct berval *vals[2];
+ int i = 0;
+ cb_backend_instance *inst = (cb_backend_instance *)arg;
+ cb_instance_config_info * config;
+
+ vals[0] = &val;
+ vals[1] = NULL;
+
+ /* suffixes */
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+
+ {
+ const Slapi_DN *aSuffix;
+ i=0;
+ if (inst->inst_be) {
+ while ((aSuffix=slapi_be_getsuffix(inst->inst_be,i))) {
+ val.bv_val = (char *)slapi_sdn_get_dn(aSuffix);
+ val.bv_len = strlen( val.bv_val );
+ if (val.bv_len) {
+ if (i==0)
+ slapi_entry_attr_replace(e,CB_CONFIG_SUFFIX,(struct berval **)vals );
+ else
+ slapi_entry_attr_merge(e,CB_CONFIG_SUFFIX,(struct berval **)vals );
+ }
+ i++;
+ }
+ }
+ }
+
+ for (i=0; inst->chaining_components && inst->chaining_components[i]; i++) {
+ val.bv_val = inst->chaining_components[i];
+ val.bv_len = strlen( val.bv_val );
+ if (val.bv_len) {
+ if (i==0)
+ slapi_entry_attr_replace(e,CB_CONFIG_CHAINING_COMPONENTS,(struct berval **)vals );
+ else
+ slapi_entry_attr_merge(e,CB_CONFIG_CHAINING_COMPONENTS,(struct berval **)vals );
+ }
+ }
+
+ for (i=0; inst->illegal_attributes && inst->illegal_attributes[i]; i++) {
+ val.bv_val = inst->illegal_attributes[i];
+ val.bv_len = strlen( val.bv_val );
+ if (val.bv_len) {
+ if (i==0)
+ slapi_entry_attr_replace(e,CB_CONFIG_ILLEGAL_ATTRS,(struct berval **)vals );
+ else
+ slapi_entry_attr_merge(e,CB_CONFIG_ILLEGAL_ATTRS,(struct berval **)vals );
+ }
+ }
+
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+
+ /* standard attributes */
+ for(config = cb_the_instance_config; config->config_name != NULL; config++) {
+ if (!(config->config_flags & (CB_ALWAYS_SHOW | CB_PREVIOUSLY_SET))) {
+ /* This config option shouldn't be shown */
+ continue;
+ }
+
+ cb_instance_config_get((void *) inst, config, buf);
+
+ val.bv_val = buf;
+ val.bv_len = strlen(buf);
+ if (val.bv_len)
+ slapi_entry_attr_replace(e, config->config_name, vals);
+ }
+
+ *returncode = LDAP_SUCCESS;
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+/*
+** Ooops!!! The backend instance is beeing deleted
+*/
+
+int cb_instance_delete_config_callback(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* e2,
+ int *returncode, char *returntext, void *arg) {
+
+ cb_backend_instance * inst = (cb_backend_instance *) arg;
+ int rc;
+ Slapi_Entry * anEntry=NULL;
+ Slapi_DN * aDn;
+
+ CB_ASSERT( inst!=NULL );
+
+ /* notify the front-end */
+ slapi_mtn_be_stopping(inst->inst_be);
+
+ /* Now it is safe to stop */
+ /* No pending op */
+
+
+ /* unregister callbacks */
+ slapi_config_remove_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP, inst->configDn,
+ LDAP_SCOPE_BASE, "(objectclass=*)", cb_instance_search_config_callback);
+
+ slapi_config_remove_callback(SLAPI_OPERATION_DELETE, DSE_FLAG_POSTOP, inst->configDn,
+ LDAP_SCOPE_BASE, "(objectclass=*)", cb_instance_delete_config_callback);
+
+ slapi_config_remove_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, inst->configDn,
+ LDAP_SCOPE_BASE, "(objectclass=*)", cb_instance_modify_config_check_callback);
+ slapi_config_remove_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_POSTOP, inst->configDn,
+ LDAP_SCOPE_BASE, "(objectclass=*)", cb_instance_modify_config_callback);
+
+ /* At this point, the monitor entry should have been removed */
+ /* If not, manually call delete callback */
+
+ aDn = slapi_sdn_new_dn_byref(inst->monitorDn);
+ if ( LDAP_SUCCESS==(slapi_search_internal_get_entry(aDn,NULL, &anEntry,inst->backend_type->identity))) {
+ cb_delete_monitor_callback( NULL, anEntry, NULL, &rc , NULL, inst );
+ if (anEntry)
+ slapi_entry_free(anEntry);
+ }
+ slapi_sdn_done(aDn);
+ slapi_sdn_free(&aDn);
+
+ /* free resources */
+ cb_close_conn_pool(inst->bind_pool);
+ cb_close_conn_pool(inst->pool);
+ slapi_be_free(&(inst->inst_be));
+ cb_instance_free(inst);
+
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+static void cb_instance_add_monitor_later(time_t when, void *arg) {
+
+ cb_backend_instance * inst = (cb_backend_instance *) arg;
+
+ if ( inst != NULL )
+ {
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+
+ /* create the monitor entry if it is not there yet */
+ if (LDAP_SUCCESS == cb_config_add_dse_entries(inst->backend_type, cb_skeleton_entries,
+ inst->inst_name,CB_PLUGIN_NAME, NULL))
+ {
+
+ /* add monitor callbacks */
+ slapi_config_register_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP, inst->monitorDn, LDAP_SCOPE_BASE,
+ "(objectclass=*)", cb_search_monitor_callback, (void *) inst);
+
+ slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, inst->monitorDn, LDAP_SCOPE_BASE,
+ "(objectclass=*)", cb_dont_allow_that, (void *) NULL);
+
+ slapi_config_register_callback(SLAPI_OPERATION_DELETE, DSE_FLAG_PREOP , inst->monitorDn, LDAP_SCOPE_BASE,
+ "(objectclass=*)", cb_delete_monitor_callback, (void *) inst);
+ }
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+}
+
+
+int cb_instance_add_config_check_callback(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* e2,
+ int *returncode, char *returntext, void *arg) {
+
+ int rc=LDAP_SUCCESS;
+ cb_backend_instance *inst;
+ cb_backend *cb=(cb_backend *) arg;
+ Slapi_Attr *attr = NULL;
+ Slapi_Value *sval;
+ const struct berval *attrValue;
+ char *instname=NULL;
+
+ if (returntext)
+ returntext[0]='\0';
+
+ /* Basic entry check */
+ if ( 0 == slapi_entry_attr_find( e, CB_CONFIG_INSTNAME, &attr )) {
+ slapi_attr_first_value(attr, &sval);
+ attrValue = slapi_value_get_berval(sval);
+ instname=attrValue->bv_val;
+ }
+ if ( instname == NULL ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Malformed backend instance (<%s> missing)>\n", CB_CONFIG_INSTNAME);
+ *returncode = LDAP_LOCAL_ERROR;
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+ /* Allocate a new backend internal data structure */
+ inst = cb_instance_alloc(cb,instname,slapi_entry_get_dn(e));
+
+ /* build the backend instance from the default hardcoded conf, */
+ /* the default instance config and the specific entry specified */
+ if ((rc=cb_build_backend_instance_config(inst,e,0))
+ != LDAP_SUCCESS) {
+ slapi_log_error( SLAPI_LOG_FATAL, CB_PLUGIN_SUBSYSTEM,
+ "Can't instantiate chaining backend instance %s.\n",inst->inst_name);
+ *returncode=rc;
+ cb_instance_free(inst);
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+ /* Free the dummy instance */
+ *returncode=rc;
+ cb_instance_free(inst);
+
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+
+/* Create the default instance config from hard-coded values */
+/*
+** Initialize the backend instance with the config entry
+** passed in arguments.
+** <arg> : (cb_backend *)
+*/
+
+int cb_instance_add_config_callback(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* e2,
+ int *returncode, char *returntext, void *arg) {
+
+ int rc=LDAP_SUCCESS;
+ cb_backend_instance *inst;
+ cb_backend *cb=(cb_backend *) arg;
+ Slapi_Attr *attr = NULL;
+ Slapi_Value *sval;
+ const struct berval *attrValue;
+ char *instname=NULL;
+
+ if (returntext)
+ returntext[0]='\0';
+
+ /* Basic entry check */
+ if ( 0 == slapi_entry_attr_find( e, CB_CONFIG_INSTNAME, &attr )) {
+ slapi_attr_first_value(attr, &sval);
+ attrValue = slapi_value_get_berval(sval);
+ instname=attrValue->bv_val;
+ }
+ if ( instname == NULL ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Malformed backend instance (<%s> missing)>\n", CB_CONFIG_INSTNAME);
+ *returncode = LDAP_LOCAL_ERROR;
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+ /* Allocate a new backend internal data structure */
+ inst = cb_instance_alloc(cb,instname,slapi_entry_get_dn(e));
+
+ /* build the backend instance from the default hardcoded conf, */
+ /* the default instance config and the specific entry specified */
+ if ((rc=cb_build_backend_instance_config(inst,e,0))
+ != LDAP_SUCCESS) {
+ slapi_log_error( SLAPI_LOG_FATAL, CB_PLUGIN_SUBSYSTEM,
+ "Can't instantiate chaining backend instance %s.\n",inst->inst_name);
+ *returncode=rc;
+ cb_instance_free(inst);
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+ /* Instantiate a Slapi_Backend if necessary */
+ if (!inst->isconfigured) {
+
+ Slapi_PBlock *aPb=NULL;
+
+ inst->inst_be = slapi_be_new(CB_CHAINING_BACKEND_TYPE,slapi_ch_strdup(inst->inst_name),0,0);
+ aPb=slapi_pblock_new();
+ slapi_pblock_set(aPb, SLAPI_PLUGIN, inst->backend_type->plugin);
+ slapi_be_setentrypoint(inst->inst_be,0,(void *)NULL,aPb);
+ slapi_be_set_instance_info(inst->inst_be,inst);
+ slapi_pblock_set(aPb, SLAPI_PLUGIN, NULL);
+ slapi_pblock_destroy(aPb);
+ }
+
+ cb_build_backend_instance_config(inst,e,1);
+
+ /* kexcoff: the order of the following calls is very important to prevent the deletion of the
+ instance to happen before the creation of the monitor part of the config.
+ However, not sure it solves all the situations, but at least it is worth to maintain
+ this order. */
+
+ if (!inst->isconfigured)
+ {
+ /* Add monitor entry and callback on it
+ * called from an add...
+ * we can't call recursively into the DSE to do more adds, they'll
+ * silently fail. instead, schedule the adds to happen in 1 second.
+ */
+ inst->eq_ctx = slapi_eq_once(cb_instance_add_monitor_later, (void *)inst, time(NULL)+1);
+ }
+
+ /* Get the list of operational attrs defined in the schema */
+ /* see cb_search file for a reason for that */
+
+ inst->every_attribute=slapi_schema_list_attribute_names(SLAPI_ATTR_FLAG_OPATTR);
+ charray_add(&inst->every_attribute,slapi_ch_strdup(LDAP_ALL_USER_ATTRS));
+
+ if (!inst->isconfigured)
+ {
+ slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, inst->configDn,
+ LDAP_SCOPE_BASE,"(objectclass=*)",cb_instance_modify_config_check_callback, (void *) inst);
+ slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_POSTOP, inst->configDn,
+ LDAP_SCOPE_BASE,"(objectclass=*)",cb_instance_modify_config_callback, (void *) inst);
+
+ slapi_config_register_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP, inst->configDn,
+ LDAP_SCOPE_BASE,"(objectclass=*)", cb_instance_search_config_callback, (void *) inst);
+
+ /* allow deletion otherwise impossible to remote a backend instance */
+ /* dynamically... */
+ slapi_config_register_callback(SLAPI_OPERATION_DELETE, DSE_FLAG_POSTOP, inst->configDn,
+ LDAP_SCOPE_BASE,"(objectclass=*)", cb_instance_delete_config_callback, (void *) inst);
+ }
+
+ /* Notify the front-end */
+ /* After the call below, we can receive ops */
+ slapi_mtn_be_started(inst->inst_be);
+
+ inst->isconfigured=1;
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+
+/* Create the default instance config from hard-coded values */
+
+int cb_create_default_backend_instance_config(cb_backend * cb) {
+
+
+ int rc;
+ cb_backend_instance *dummy;
+ Slapi_Entry *e=slapi_entry_alloc();
+ char defaultDn[CB_BUFSIZE];
+ char *olddn;
+ struct berval val;
+ struct berval *vals[2];
+ Slapi_PBlock *pb;
+
+ dummy = cb_instance_alloc(cb, "dummy", "o=dummy");
+ cb_instance_config_set_default(dummy);
+ cb_instance_search_config_callback(NULL,e,NULL, &rc, NULL,(void *) dummy);
+
+
+ /* set right dn and objectclass */
+
+ sprintf(defaultDn,"cn=default instance config,%s",cb->pluginDN);
+ olddn = slapi_entry_get_dn(e);
+ slapi_ch_free((void **) &olddn);
+
+ slapi_entry_set_dn(e,slapi_ch_strdup(defaultDn));
+
+ vals[0] = &val;
+ vals[1] = NULL;
+
+ val.bv_val = "top";
+ val.bv_len = strlen( val.bv_val );
+ slapi_entry_attr_replace( e, "objectclass", (struct berval **)vals );
+ val.bv_val = CB_CONFIG_EXTENSIBLEOCL;
+ val.bv_len = strlen( val.bv_val );
+ slapi_entry_attr_merge( e, "objectclass", (struct berval **)vals );
+ val.bv_val = "default instance config";
+ val.bv_len = strlen( val.bv_val );
+ slapi_entry_attr_replace( e, "cn", (struct berval **)vals );
+
+ /* create entry */
+ pb = slapi_pblock_new();
+ slapi_add_entry_internal_set_pb(pb, e, NULL, cb->identity, 0);
+ slapi_add_internal_pb(pb);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+ if ( LDAP_SUCCESS != rc ) {
+ slapi_log_error(SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Add %s failed (%s)\n",defaultDn,ldap_err2string(rc));
+ }
+
+ slapi_pblock_destroy(pb);
+ /* cleanup */
+ cb_instance_free(dummy);
+ /* BEWARE: entry is consummed */
+ return rc;
+}
+
+/* Extract backend instance configuration from the LDAP entry */
+
+int cb_build_backend_instance_config(cb_backend_instance *inst, Slapi_Entry * conf, int apply) {
+
+ cb_backend *cb = inst->backend_type;
+ Slapi_PBlock *default_pb;
+ Slapi_Entry **default_entries = NULL;
+ Slapi_Entry *default_conf=NULL;
+ int default_res, rc;
+ char defaultDn[CB_BUFSIZE];
+ cb_backend_instance * current_inst;
+
+ rc=LDAP_SUCCESS;
+
+ if (apply)
+ current_inst=inst;
+ else
+ current_inst=cb_instance_alloc(cb,inst->inst_name,"cn=dummy");
+
+ /* set default configuration */
+ cb_instance_config_set_default(current_inst);
+
+ /* 2: Overwrite values present in the default instance config */
+
+ sprintf(defaultDn,"cn=default instance config,%s",cb->pluginDN);
+
+ default_pb = slapi_pblock_new();
+ slapi_search_internal_set_pb(default_pb, defaultDn, LDAP_SCOPE_BASE,
+ "objectclass=*", NULL, 0, NULL, NULL, cb->identity, 0);
+ slapi_search_internal_pb (default_pb);
+ slapi_pblock_get(default_pb, SLAPI_PLUGIN_INTOP_RESULT, &default_res);
+ if ( LDAP_SUCCESS == default_res ) {
+ slapi_pblock_get(default_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &default_entries);
+ if (default_entries && default_entries[0] ) {
+
+ struct berval val;
+ struct berval *vals[2];
+ vals[0] = &val;
+ vals[1] = NULL;
+ default_conf=default_entries[0];
+
+ /* hack: add a dummy url (mandatory) to avoid error */
+ /* will be overwritten by the one in conf entry */
+ val.bv_val = "ldap://localhost/";
+ val.bv_len = strlen( val.bv_val );
+ slapi_entry_attr_replace( default_conf, CB_CONFIG_HOSTURL, (struct berval **)vals );
+
+ rc=cb_instance_config_initialize(current_inst,default_conf,CB_CONFIG_PHASE_STARTUP,1);
+ }
+ }
+ slapi_free_search_results_internal(default_pb);
+ slapi_pblock_destroy(default_pb);
+
+ if (rc == LDAP_SUCCESS)
+ rc=cb_instance_config_initialize(current_inst,conf,CB_CONFIG_PHASE_STARTUP,1);
+
+ if (!apply)
+ cb_instance_free(current_inst);
+
+ return rc;
+}
diff --git a/ldap/servers/plugins/chainingdb/cb_modify.c b/ldap/servers/plugins/chainingdb/cb_modify.c
new file mode 100644
index 00000000..51aed6a3
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_modify.c
@@ -0,0 +1,244 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+static void cb_remove_illegal_mods(cb_backend_instance * inst, LDAPMod **mods);
+
+/*
+ * Perform a modify operation
+ *
+ * Returns:
+ * 0 - success
+ * <0 - fail
+ *
+ */
+
+int
+chaining_back_modify ( Slapi_PBlock *pb )
+{
+
+ Slapi_Backend *be;
+ cb_backend_instance *cb;
+ LDAPControl **ctrls, **serverctrls;
+ int rc,parse_rc,msgid,i;
+ LDAP *ld=NULL;
+ char **referrals=NULL;
+ LDAPMod ** mods;
+ LDAPMessage * res;
+ char *dn,* matched_msg, *error_msg;
+ char *cnxerrbuf=NULL;
+ time_t endtime;
+ cb_outgoing_conn *cnx;
+
+ if ( LDAP_SUCCESS != (rc=cb_forward_operation(pb) )) {
+ cb_send_ldap_result( pb, rc, NULL, "Chaining forbidden", 0, NULL );
+ return -1;
+ }
+
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+ cb = cb_get_instance(be);
+
+ cb_update_monitor_info(pb,cb,SLAPI_OPERATION_MODIFY);
+
+ /* Check wether the chaining BE is available or not */
+ if ( cb_check_availability( cb, pb ) == FARMSERVER_UNAVAILABLE ){
+ return -1;
+ }
+
+ slapi_pblock_get( pb, SLAPI_MODIFY_TARGET, &dn );
+
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,"modify: target:<%s>\n",dn);
+ }
+
+
+ ctrls=serverctrls=NULL;
+ slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods );
+ slapi_pblock_get( pb, SLAPI_REQCONTROLS, &ctrls );
+
+ /* Check acls */
+
+ if ( cb->local_acl && !cb->associated_be_is_disabled ) {
+ char * errbuf=NULL;
+ Slapi_Entry *te = slapi_entry_alloc();
+ slapi_entry_set_dn(te,slapi_ch_strdup(dn));
+ rc = slapi_acl_check_mods( pb, te, mods, &errbuf);
+ slapi_entry_free(te);
+
+ if ( rc != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, rc, NULL, errbuf, 0, NULL );
+ slapi_ch_free((void **)&errbuf);
+ return -1;
+ }
+ }
+
+
+ /* Grab a connection handle */
+ if ((rc = cb_get_connection(cb->pool,&ld,&cnx,NULL,&cnxerrbuf)) != LDAP_SUCCESS) {
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, cnxerrbuf, 0, NULL);
+ slapi_ch_free((void **)&cnxerrbuf);
+ /* ping the farm. If the farm is unreachable, we increment the counter */
+ cb_ping_farm(cb,NULL,0);
+ return -1;
+ }
+
+ /* Control management */
+ if ( (rc = cb_update_controls( pb,ld,&ctrls,CB_UPDATE_CONTROLS_ADDAUTH )) != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, rc, NULL,NULL, 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ /* Don't free mods here: are freed at the do_modify level */
+ return -1;
+ }
+
+ if ( slapi_op_abandoned( pb )) {
+ cb_release_op_connection(cb->pool,ld,0);
+ /* Don't free mods here: are freed at the do_modify level */
+ if ( NULL != ctrls)
+ ldap_controls_free(ctrls);
+ return -1;
+ }
+
+ /* Remove illegal attributes from the mods */
+ cb_remove_illegal_mods(cb,mods);
+
+ /* heart-beat management */
+ if (cb->max_idle_time>0)
+ endtime=current_time() + cb->max_idle_time;
+
+ /* Send LDAP operation to the remote host */
+ rc = ldap_modify_ext( ld, dn, mods, ctrls, NULL, &msgid );
+ if ( NULL != ctrls)
+ ldap_controls_free(ctrls);
+
+ if ( rc != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, ldap_err2string(rc), 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ return -1;
+ }
+
+ while ( 1 ) {
+
+ if (cb_check_forward_abandon(cb,pb,ld,msgid)) {
+ /* connection handle released */
+ return -1;
+ }
+
+ rc = ldap_result( ld, msgid, 0, &cb->abandon_timeout, &res );
+ switch ( rc ) {
+ case -1:
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(rc), 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ if (res)
+ ldap_msgfree(res);
+ return -1;
+ case 0:
+ if ((rc=cb_ping_farm(cb,cnx,endtime)) != LDAP_SUCCESS) {
+
+ /* does not respond. give up and return a*/
+ /* error to the client. */
+
+ /*cb_send_ldap_result(pb,LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(rc), 0, NULL);*/
+ cb_send_ldap_result(pb,LDAP_OPERATIONS_ERROR, NULL, "FARM SERVER TEMPORARY UNAVAILABLE", 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ if (res)
+ ldap_msgfree(res);
+ return -1;
+ }
+#ifdef CB_YIELD
+ DS_Sleep(PR_INTERVAL_NO_WAIT);
+#endif
+ break;
+ default:
+
+ matched_msg=error_msg=NULL;
+ serverctrls=NULL;
+ parse_rc = ldap_parse_result( ld, res, &rc, &matched_msg,
+ &error_msg, &referrals, &serverctrls, 1 );
+ if ( parse_rc != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(parse_rc), 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(parse_rc));
+ slapi_ch_free((void **)&matched_msg);
+ slapi_ch_free((void **)&error_msg);
+ if (serverctrls)
+ ldap_controls_free(serverctrls);
+ /* jarnou: free referrals */
+ if (referrals)
+ charray_free(referrals);
+ return -1;
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ struct berval ** refs = referrals2berval(referrals);
+ cb_send_ldap_result( pb, rc, matched_msg, error_msg, 0, refs);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ slapi_ch_free((void **)&matched_msg);
+ slapi_ch_free((void **)&error_msg);
+ if (refs)
+ ber_bvecfree(refs);
+ if (referrals)
+ charray_free(referrals);
+ if (serverctrls)
+ ldap_controls_free(serverctrls);
+ return -1;
+ }
+
+ cb_release_op_connection(cb->pool,ld,0);
+
+ /* Add control response sent by the farm server */
+
+ for (i=0; serverctrls && serverctrls[i];i++)
+ slapi_pblock_set( pb, SLAPI_ADD_RESCONTROL, serverctrls[i]);
+ /* SLAPI_ADD_RESCONTROL dups controls */
+ if (serverctrls)
+ ldap_controls_free(serverctrls);
+ /* jarnou: free matched_msg, error_msg, and referrals if necessary */
+ slapi_ch_free((void **)&matched_msg);
+ slapi_ch_free((void **)&error_msg);
+ if (referrals)
+ charray_free(referrals);
+ cb_send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
+ return 0;
+ }
+ }
+
+ /* Never reached */
+ /* return 0; */
+}
+
+/* Function removes mods which are not allowed over-the-wire */
+static void
+cb_remove_illegal_mods(cb_backend_instance *inst, LDAPMod **mods)
+{
+ int i, j;
+ LDAPMod *tmp;
+
+ if ( inst->illegal_attributes != NULL ) { /* Unlikely to happen */
+
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+
+ for (j=0; inst->illegal_attributes[j]; j++) {
+ for ( i = 0; mods[i] != NULL; i++ ) {
+ if (slapi_attr_types_equivalent(inst->illegal_attributes[j],mods[i]->mod_type)) {
+ tmp = mods[i];
+ for ( j = i; mods[j] != NULL; j++ ) {
+ mods[j] = mods[j + 1];
+ }
+ slapi_ch_free( (void**)&(tmp->mod_type) );
+ if ( tmp->mod_bvalues != NULL ) {
+ ber_bvecfree( tmp->mod_bvalues );
+ }
+ slapi_ch_free( (void**)&tmp );
+ i--;
+ }
+ }
+ }
+
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+}
diff --git a/ldap/servers/plugins/chainingdb/cb_modrdn.c b/ldap/servers/plugins/chainingdb/cb_modrdn.c
new file mode 100644
index 00000000..e6b5dadb
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_modrdn.c
@@ -0,0 +1,239 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+/*
+ * Perform a modrdn operation
+ *
+ * Returns:
+ * 0 - success
+ * <0 - fail
+ *
+ */
+
+int
+chaining_back_modrdn ( Slapi_PBlock *pb )
+{
+
+ Slapi_Backend * be;
+ cb_backend_instance *cb;
+ LDAPControl **ctrls, **serverctrls;
+ int rc,parse_rc,msgid,i;
+ LDAP *ld=NULL;
+ char **referrals=NULL;
+ LDAPMessage * res;
+ char * matched_msg, *error_msg,* pdn, *newdn, *dn;
+ int deleteoldrdn=0;
+ char * newsuperior, *newrdn;
+ char * cnxerrbuf=NULL;
+ time_t endtime;
+ cb_outgoing_conn * cnx;
+
+ if ( LDAP_SUCCESS != (rc=cb_forward_operation(pb) )) {
+ cb_send_ldap_result( pb, rc, NULL, "Chaining forbidden", 0, NULL );
+ return -1;
+ }
+
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+ cb = cb_get_instance(be);
+
+ cb_update_monitor_info(pb,cb,SLAPI_OPERATION_MODRDN);
+
+ /* Check wether the chaining BE is available or not */
+ if ( cb_check_availability( cb, pb ) == FARMSERVER_UNAVAILABLE ){
+ return -1;
+ }
+
+ newsuperior=newdn=newrdn=dn=NULL;
+ slapi_pblock_get( pb, SLAPI_MODRDN_TARGET, &dn );
+ slapi_pblock_get( pb, SLAPI_MODRDN_NEWRDN, &newrdn );
+ slapi_pblock_get( pb, SLAPI_MODRDN_NEWSUPERIOR, &newsuperior );
+ slapi_pblock_get( pb, SLAPI_MODRDN_DELOLDRDN, &deleteoldrdn );
+
+ /*
+ * Construct the new dn
+ */
+
+ dn = slapi_dn_normalize_case(dn);
+ if ( (pdn = slapi_dn_parent( dn )) != NULL ) {
+ /* parent + rdn + separator(s) + null */
+ newdn = (char *) slapi_ch_malloc( strlen( pdn ) + strlen( newrdn ) + 3 );
+ strcpy( newdn, newrdn );
+ strcat( newdn, "," );
+ strcat( newdn, pdn );
+
+ slapi_ch_free((void **)&pdn );
+
+ } else {
+ newdn = slapi_ch_strdup( newrdn );
+ }
+
+ /*
+ * Make sure the current backend is managing
+ * the new dn. We won't support moving entries
+ * across backend this way.
+ * Done in the front-end.
+ */
+
+ slapi_ch_free((void **)&newdn);
+
+ if (cb->local_acl && !cb->associated_be_is_disabled) {
+ /*
+ * Check local acls
+ * Keep in mind We don't have the entry for acl evaluation
+ */
+
+ char * errbuf=NULL;
+ Slapi_Entry *te = slapi_entry_alloc();
+ slapi_entry_set_dn(te,slapi_ch_strdup(dn));
+ rc = cb_access_allowed (pb, te, NULL, NULL, SLAPI_ACL_WRITE,&errbuf);
+ slapi_entry_free(te);
+
+ if ( rc != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, rc, NULL, errbuf, 0, NULL );
+ slapi_ch_free((void **)&errbuf);
+ return -1;
+ }
+ }
+
+ /*
+ * Grab a connection handle
+ */
+
+ if ((rc = cb_get_connection(cb->pool,&ld,&cnx,NULL,&cnxerrbuf)) != LDAP_SUCCESS) {
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, cnxerrbuf, 0, NULL);
+ slapi_ch_free((void **)&cnxerrbuf);
+ /* ping the farm. If the farm is unreachable, we increment the counter */
+ cb_ping_farm(cb,NULL,0);
+ return -1;
+ }
+
+ /*
+ * Control management
+ */
+
+ if ( (rc = cb_update_controls( pb,ld,&ctrls,CB_UPDATE_CONTROLS_ADDAUTH )) != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, rc, NULL,NULL, 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ return -1;
+ }
+
+
+ if ( slapi_op_abandoned( pb )) {
+ cb_release_op_connection(cb->pool,ld,0);
+ if ( NULL != ctrls)
+ ldap_controls_free(ctrls);
+ return -1;
+ }
+
+ /* heart-beat management */
+ if (cb->max_idle_time>0)
+ endtime=current_time() + cb->max_idle_time;
+
+ /*
+ * Send LDAP operation to the remote host
+ */
+
+ rc = ldap_rename ( ld,dn,newrdn,newsuperior,deleteoldrdn,ctrls,NULL,&msgid);
+
+ if ( NULL != ctrls)
+ ldap_controls_free(ctrls);
+ if ( rc != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(rc), 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ return -1;
+ }
+
+ while ( 1 ) {
+
+ if (cb_check_forward_abandon(cb,pb,ld,msgid)) {
+ return -1;
+ }
+
+ rc = ldap_result( ld, msgid, 0, &cb->abandon_timeout, &res );
+ switch ( rc ) {
+ case -1:
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(rc), 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ if (res)
+ ldap_msgfree(res);
+ return -1;
+ case 0:
+ if ((rc=cb_ping_farm(cb,cnx,endtime)) != LDAP_SUCCESS) {
+
+ /* does not respond. give up and return a*/
+ /* error to the client. */
+
+ /*cb_send_ldap_result(pb,LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(rc), 0, NULL);*/
+ cb_send_ldap_result(pb,LDAP_OPERATIONS_ERROR, NULL, "FARM SERVER TEMPORARY UNAVAILABLE", 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ if (res)
+ ldap_msgfree(res);
+ return -1;
+ }
+#ifdef CB_YIELD
+ DS_Sleep(PR_INTERVAL_NO_WAIT);
+#endif
+ break;
+ default:
+ matched_msg=error_msg=NULL;
+ parse_rc = ldap_parse_result( ld, res, &rc, &matched_msg,
+ &error_msg, &referrals, &serverctrls, 1 );
+
+ if ( parse_rc != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(parse_rc), 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(parse_rc));
+ slapi_ch_free((void **)&matched_msg);
+ slapi_ch_free((void **)&error_msg);
+ if (serverctrls)
+ ldap_controls_free(serverctrls);
+ /* jarnou: free referrals */
+ if (referrals)
+ charray_free(referrals);
+ return -1;
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ struct berval ** refs = referrals2berval(referrals);
+
+ cb_send_ldap_result( pb, rc, matched_msg, error_msg, 0, refs);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ slapi_ch_free((void **)&matched_msg);
+ slapi_ch_free((void **)&error_msg);
+ if (refs)
+ ber_bvecfree(refs);
+ if (referrals)
+ charray_free(referrals);
+ if (serverctrls)
+ ldap_controls_free(serverctrls);
+ return -1;
+ }
+
+ cb_release_op_connection(cb->pool,ld,0);
+
+ /* Add control response sent by the farm server */
+
+ for (i=0; serverctrls && serverctrls[i];i++)
+ slapi_pblock_set( pb, SLAPI_ADD_RESCONTROL, serverctrls[i]);
+ if (serverctrls)
+ ldap_controls_free(serverctrls);
+ /* jarnou: free matched_msg, error_msg, and referrals if necessary */
+ slapi_ch_free((void **)&matched_msg);
+ slapi_ch_free((void **)&error_msg);
+ if (referrals)
+ charray_free(referrals);
+ cb_send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
+ return 0;
+ }
+ }
+
+ /* Never reached */
+ /* return 0; */
+}
diff --git a/ldap/servers/plugins/chainingdb/cb_monitor.c b/ldap/servers/plugins/chainingdb/cb_monitor.c
new file mode 100644
index 00000000..11ef3bda
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_monitor.c
@@ -0,0 +1,228 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+extern cb_instance_config_info cb_the_instance_config[];
+/*
+** Chaining backend instance monitor function
+** This function wraps up backend specific monitoring information
+** and return it to the client as an LDAP entry.
+** This function is usually called upon receipt of the monitor
+** dn for this backend
+**
+** Monitor information:
+**
+** Database misc
+** database
+**
+** Number of hits for each operation
+** addopcount
+** modifyopcount
+** deleteopcount
+** modrdnopcount
+** compareopcount
+** searchsubtreeopcount
+** searchonelevelopcount
+** searchbaseopcount
+** bindopcount
+** unbindopcount
+** abandonopcount
+**
+** Outgoing connections
+** outgoingopconnections
+** outgoingbindconnections
+**
+*/
+
+int
+cb_search_monitor_callback(Slapi_PBlock * pb, Slapi_Entry * e, Slapi_Entry * entryAfter, int * returnCode, char * returnText, void * arg)
+{
+
+ char buf[CB_BUFSIZE];
+ struct berval val;
+ struct berval *vals[2];
+ int deletecount,addcount,modifycount,modrdncount,searchbasecount,searchonelevelcount;
+ int searchsubtreecount,abandoncount,bindcount,unbindcount,comparecount;
+ int outgoingconn, outgoingbindconn;
+ cb_backend_instance *inst = (cb_backend_instance *)arg;
+
+ /* First make sure the backend instance is configured */
+ /* If not, don't return anything */
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ if (!inst->isconfigured) {
+ *returnCode= LDAP_NO_SUCH_OBJECT;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+
+ vals[0] = &val;
+ vals[1] = NULL;
+
+ slapi_lock_mutex(inst->monitor.mutex);
+
+ addcount =inst->monitor.addcount;
+ deletecount =inst->monitor.deletecount;
+ modifycount =inst->monitor.modifycount;
+ modrdncount =inst->monitor.modrdncount;
+ searchbasecount =inst->monitor.searchbasecount;
+ searchonelevelcount =inst->monitor.searchonelevelcount;
+ searchsubtreecount =inst->monitor.searchsubtreecount;
+ abandoncount =inst->monitor.abandoncount;
+ bindcount =inst->monitor.bindcount;
+ unbindcount =inst->monitor.unbindcount;
+ comparecount =inst->monitor.comparecount;
+
+ slapi_unlock_mutex(inst->monitor.mutex);
+
+ /*
+ ** Get connection information
+ */
+
+ slapi_lock_mutex(inst->pool->conn.conn_list_mutex);
+ outgoingconn= inst->pool->conn.conn_list_count;
+ slapi_unlock_mutex(inst->pool->conn.conn_list_mutex);
+
+ slapi_lock_mutex(inst->bind_pool->conn.conn_list_mutex);
+ outgoingbindconn= inst->bind_pool->conn.conn_list_count;
+ slapi_unlock_mutex(inst->bind_pool->conn.conn_list_mutex);
+
+ sprintf( buf, "%lu", addcount );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ slapi_entry_attr_replace( e, CB_MONITOR_ADDCOUNT, ( struct berval **)vals );
+
+ sprintf( buf, "%lu", deletecount );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ slapi_entry_attr_replace( e, CB_MONITOR_DELETECOUNT, ( struct berval **)vals );
+
+ sprintf( buf, "%lu", modifycount );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ slapi_entry_attr_replace( e, CB_MONITOR_MODIFYCOUNT, ( struct berval **)vals );
+
+ sprintf( buf, "%lu", modrdncount );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ slapi_entry_attr_replace( e, CB_MONITOR_MODRDNCOUNT, ( struct berval **)vals );
+
+ sprintf( buf, "%lu", searchbasecount );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ slapi_entry_attr_replace( e, CB_MONITOR_SEARCHBASECOUNT, ( struct berval **)vals );
+
+ sprintf( buf, "%lu", searchonelevelcount );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ slapi_entry_attr_replace( e, CB_MONITOR_SEARCHONELEVELCOUNT, ( struct berval **)vals );
+
+ sprintf( buf, "%lu", searchsubtreecount );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ slapi_entry_attr_replace( e, CB_MONITOR_SEARCHSUBTREECOUNT, ( struct berval **)vals );
+
+ sprintf( buf, "%lu", abandoncount );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ slapi_entry_attr_replace( e, CB_MONITOR_ABANDONCOUNT, ( struct berval **)vals );
+
+ sprintf( buf, "%lu", bindcount );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ slapi_entry_attr_replace( e, CB_MONITOR_BINDCOUNT, ( struct berval **)vals );
+
+ sprintf( buf, "%lu", unbindcount );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ slapi_entry_attr_replace( e, CB_MONITOR_UNBINDCOUNT, ( struct berval **)vals );
+
+ sprintf( buf, "%lu", comparecount );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ slapi_entry_attr_replace( e, CB_MONITOR_COMPARECOUNT, ( struct berval **)vals );
+
+ sprintf( buf, "%d", outgoingconn );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ slapi_entry_attr_replace( e, CB_MONITOR_OUTGOINGCONN, ( struct berval **)vals );
+
+ sprintf( buf, "%d", outgoingbindconn );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ slapi_entry_attr_replace( e, CB_MONITOR_OUTGOINGBINDCOUNT, ( struct berval **)vals );
+
+ *returnCode= LDAP_SUCCESS;
+ return(SLAPI_DSE_CALLBACK_OK);
+}
+
+void
+cb_update_monitor_info(Slapi_PBlock * pb, cb_backend_instance * inst,int op)
+{
+
+ int scope;
+
+ slapi_lock_mutex(inst->monitor.mutex);
+ switch (op) {
+ case SLAPI_OPERATION_ADD:
+ inst->monitor.addcount++;
+ break;
+ case SLAPI_OPERATION_MODIFY:
+ inst->monitor.modifycount++;
+ break;
+ case SLAPI_OPERATION_DELETE:
+ inst->monitor.deletecount++;
+ break;
+ case SLAPI_OPERATION_MODRDN:
+/** case SLAPI_OPERATION_MODDN: **/
+ inst->monitor.modrdncount++;
+ break;
+ case SLAPI_OPERATION_COMPARE:
+ inst->monitor.comparecount++;
+ break;
+ case SLAPI_OPERATION_ABANDON:
+ inst->monitor.abandoncount++;
+ break;
+ case SLAPI_OPERATION_BIND:
+ inst->monitor.bindcount++;
+ break;
+ case SLAPI_OPERATION_UNBIND:
+ inst->monitor.unbindcount++;
+ break;
+ case SLAPI_OPERATION_SEARCH:
+ slapi_pblock_get( pb, SLAPI_SEARCH_SCOPE, &scope );
+ if ( LDAP_SCOPE_BASE == scope )
+ inst->monitor.searchbasecount++;
+ else
+ if ( LDAP_SCOPE_ONELEVEL == scope )
+ inst->monitor.searchonelevelcount++;
+ else
+ inst->monitor.searchsubtreecount++;
+ break;
+ default:
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,"cb_update_monitor_info: invalid op type <%d>\n",op);
+ }
+ slapi_unlock_mutex(inst->monitor.mutex);
+}
+
+
+int
+cb_delete_monitor_callback(Slapi_PBlock * pb, Slapi_Entry * e, Slapi_Entry * entryAfter, int * returnCode, char * returnText, void * arg)
+{
+
+ cb_backend_instance *inst = (cb_backend_instance *)arg;
+
+ slapi_config_remove_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP, inst->monitorDn, LDAP_SCOPE_BASE,
+ "(objectclass=*)", cb_search_monitor_callback);
+ slapi_config_remove_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, inst->monitorDn, LDAP_SCOPE_BASE,
+ "(objectclass=*)", cb_dont_allow_that);
+ slapi_config_remove_callback(SLAPI_OPERATION_DELETE, DSE_FLAG_PREOP, inst->monitorDn, LDAP_SCOPE_BASE,
+ "(objectclass=*)", cb_delete_monitor_callback);
+
+ *returnCode= LDAP_SUCCESS;
+ return(SLAPI_DSE_CALLBACK_OK);
+}
diff --git a/ldap/servers/plugins/chainingdb/cb_schema.c b/ldap/servers/plugins/chainingdb/cb_schema.c
new file mode 100644
index 00000000..2337b977
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_schema.c
@@ -0,0 +1,45 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+void cb_eliminate_illegal_attributes(cb_backend_instance * inst, Slapi_Entry * e) {
+
+ /* get rid of illegal attributes before sending op to the */
+ /* farm server. (Add) */
+
+ int rc,j;
+ Slapi_Attr *attr=NULL;
+ char *tobefreed=NULL;
+
+ if (inst->illegal_attributes != NULL ) { /* Unlikely to happen */
+
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+
+ for (j=0; inst->illegal_attributes[j]; j++) {
+ char * aType=NULL;
+ rc=slapi_entry_first_attr(e,&attr);
+ while (rc==0) {
+ if (tobefreed) {
+ slapi_entry_attr_delete( e, tobefreed);
+ tobefreed=NULL;
+ }
+ slapi_attr_get_type(attr,&aType);
+ if (aType && slapi_attr_types_equivalent(inst->illegal_attributes[j],aType)) {
+ tobefreed=aType;
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "attribute <%s> not forwarded.\n",aType);
+ }
+ rc = slapi_entry_next_attr(e, attr, &attr);
+ }
+ if (tobefreed) {
+ slapi_entry_attr_delete( e, tobefreed);
+ tobefreed=NULL;
+ }
+ }
+
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+}
diff --git a/ldap/servers/plugins/chainingdb/cb_search.c b/ldap/servers/plugins/chainingdb/cb_search.c
new file mode 100644
index 00000000..d9cf6ef7
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_search.c
@@ -0,0 +1,698 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+/*
+ * Build a candidate list for this backentry and scope.
+ * Could be a BASE, ONELEVEL, or SUBTREE search.
+ *
+ * Returns:
+ * 0 - success
+ * <0 - fail
+ *
+ */
+
+int
+chainingdb_build_candidate_list ( Slapi_PBlock *pb )
+{
+
+ Slapi_Backend * be;
+ Slapi_Operation * op;
+ char *target, *filter;
+ int scope,attrsonly,sizelimit,timelimit,rc,searchreferral;
+ char **attrs=NULL;
+ LDAPControl **controls=NULL;
+ LDAPControl **ctrls=NULL;
+ LDAP *ld=NULL;
+ cb_backend_instance *cb = NULL;
+ cb_searchContext *ctx=NULL;
+ struct timeval timeout;
+ time_t optime;
+ int doit,parse_rc;
+ LDAPMessage *res=NULL;
+ char *matched_msg,*error_msg;
+ LDAPControl **serverctrls=NULL;
+ char **referrals=NULL;
+ char *cnxerrbuf=NULL;
+ time_t endbefore=0;
+ time_t endtime;
+ cb_outgoing_conn *cnx;
+
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+ cb = cb_get_instance(be);
+
+ slapi_pblock_get( pb, SLAPI_OPERATION, &op );
+ slapi_pblock_get( pb, SLAPI_SEARCH_STRFILTER, &filter );
+ slapi_pblock_get( pb, SLAPI_SEARCH_SCOPE, &scope );
+ slapi_pblock_get( pb, SLAPI_OPINITIATED_TIME, &optime );
+ slapi_pblock_get( pb, SLAPI_SEARCH_TARGET, &target );
+
+ if ( LDAP_SUCCESS != (parse_rc=cb_forward_operation(pb) )) {
+
+ /* Don't return errors */
+
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "local search: base:<%s> scope:<%s> filter:<%s>\n",target,
+ scope==LDAP_SCOPE_SUBTREE?"SUBTREE":scope==LDAP_SCOPE_ONELEVEL ? "ONE-LEVEL" : "BASE" , filter);
+ }
+
+ ctx = (cb_searchContext *)slapi_ch_calloc(1,sizeof(cb_searchContext));
+ ctx->type = CB_SEARCHCONTEXT_ENTRY;
+ ctx->data=NULL;
+
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET,ctx);
+ return 0;
+ }
+
+ cb_update_monitor_info(pb,cb,SLAPI_OPERATION_SEARCH);
+
+ /* Check wether the chaining BE is available or not */
+ if ( cb_check_availability( cb, pb ) == FARMSERVER_UNAVAILABLE ){
+ return -1;
+ }
+
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "chained search: base:<%s> scope:<%s> filter:<%s>\n",target,
+ scope==LDAP_SCOPE_SUBTREE?"SUBTREE":scope==LDAP_SCOPE_ONELEVEL ? "ONE-LEVEL" : "BASE" , filter);
+ }
+
+ slapi_pblock_get( pb, SLAPI_SEARCH_ATTRS, &attrs );
+ slapi_pblock_get( pb, SLAPI_SEARCH_ATTRSONLY, &attrsonly );
+ slapi_pblock_get( pb, SLAPI_REQCONTROLS, &controls );
+ slapi_pblock_get( pb, SLAPI_SEARCH_TIMELIMIT, &timelimit );
+ slapi_pblock_get( pb, SLAPI_SEARCH_SIZELIMIT, &sizelimit );
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET,NULL);
+
+
+ if ((scope != LDAP_SCOPE_BASE) && (scope != LDAP_SCOPE_ONELEVEL) && (scope != LDAP_SCOPE_SUBTREE)) {
+ cb_send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL, "Bad scope", 0, NULL );
+ return 1;
+ }
+
+ searchreferral=cb->searchreferral;
+
+ if (( scope != LDAP_SCOPE_BASE ) && ( searchreferral )) {
+
+ int i;
+ struct berval bv,*bvals[2];
+ Slapi_Entry ** aciArray=(Slapi_Entry **) slapi_ch_malloc(2*sizeof(Slapi_Entry *));
+ Slapi_Entry *anEntry = slapi_entry_alloc();
+
+ slapi_entry_set_dn(anEntry,slapi_ch_strdup(target));
+
+ bvals[1]=NULL;
+ bvals[0]=&bv;
+ bv.bv_val="referral";
+ bv.bv_len=strlen(bv.bv_val);
+ slapi_entry_add_values( anEntry, "objectclass", bvals);
+
+ PR_RWLock_Rlock(cb->rwl_config_lock);
+ for (i=0; cb->url_array && cb->url_array[i]; i++) {
+ char * anUrl= slapi_ch_calloc(1,strlen(cb->url_array[i])+strlen(target)+1);
+ sprintf(anUrl,"%s%s",cb->url_array[i],target);
+ bv.bv_val=anUrl;
+ bv.bv_len=strlen(bv.bv_val);
+ slapi_entry_attr_merge( anEntry, "ref", bvals);
+ slapi_ch_free((void **)&anUrl);
+ }
+ PR_RWLock_Unlock(cb->rwl_config_lock);
+
+ aciArray[0]=anEntry;
+ aciArray[1]=NULL;
+
+ ctx = (cb_searchContext *)slapi_ch_calloc(1,sizeof(cb_searchContext));
+ ctx->type = CB_SEARCHCONTEXT_ENTRY;
+ ctx->data=aciArray;
+
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET,ctx);
+ return 0;
+ }
+
+ /*
+ ** Time limit management.
+ ** Make sure the operation has not expired
+ */
+
+ if ( timelimit == -1 ) {
+ timeout.tv_sec = timeout.tv_usec = 0;
+ } else {
+ time_t now=current_time();
+ endbefore=optime + timelimit;
+ if (now >= endbefore) {
+ cb_send_ldap_result( pb, LDAP_TIMELIMIT_EXCEEDED, NULL,NULL, 0, NULL);
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY, NULL );
+ return 1;
+ }
+ timeout.tv_sec=timelimit-(now-optime);
+ timeout.tv_usec=0;
+ }
+
+ /* Operational attribute support for internal searches: */
+ /* The front-end relies on the fact that operational attributes */
+ /* are returned along with standard attrs when the attr list is */
+ /* NULL. To make it work, we need to explicitly request for all*/
+ /* possible operational attrs. Too bad. */
+
+ if ( (attrs == NULL) && operation_is_flag_set(op, OP_FLAG_INTERNAL) ) {
+ attrs = cb->every_attribute;
+
+ }
+ else
+ {
+ int i;
+ if ( attrs != NULL )
+ {
+ for ( i = 0; attrs[i] != NULL; i++ ) {
+ if ( strcasecmp( "nsrole", attrs[i] ) == 0 )
+ {
+ attrs = cb->every_attribute;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Grab a connection handle */
+
+ if ( LDAP_SUCCESS != (rc = cb_get_connection(cb->pool,&ld,&cnx,&timeout,&cnxerrbuf))) {
+ if (rc == LDAP_TIMELIMIT_EXCEEDED)
+ cb_send_ldap_result( pb, rc, NULL,cnxerrbuf, 0, NULL);
+ else
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,cnxerrbuf, 0, NULL);
+
+ slapi_ch_free((void **)&cnxerrbuf);
+ /* ping the farm. If the farm is unreachable, we increment the counter */
+ cb_ping_farm(cb,NULL,0);
+ return 1;
+ }
+
+ /*
+ * Control management
+ */
+
+ if ( LDAP_SUCCESS != (rc = cb_update_controls( pb,ld,&ctrls,CB_UPDATE_CONTROLS_ADDAUTH ))) {
+ cb_send_ldap_result( pb, rc, NULL,NULL, 0, NULL);
+ cb_release_op_connection(cb->pool,ld,0);
+ return 1;
+ }
+
+ if ( slapi_op_abandoned( pb )) {
+ cb_release_op_connection(cb->pool,ld,0);
+ if ( NULL != ctrls)
+ ldap_controls_free(ctrls);
+ return 1;
+ }
+
+ ctx = (cb_searchContext *) slapi_ch_calloc(1,sizeof(cb_searchContext));
+
+ /*
+ ** We need to store the connection handle in the search context
+ ** to make sure we reuse it in the next_entry iteration
+ ** Indeed, if another thread on this connection detects a problem
+ ** on this connection, it may reallocate a new connection and
+ ** a call to get_connection may return a new cnx. Too bad.
+ */
+
+ ctx->ld=ld;
+ ctx->cnx=cnx;
+
+ /* for some reasons, it is an error to pass in a zero'd timeval */
+ /* to ldap_search_ext() */
+ if ((timeout.tv_sec==0) && (timeout.tv_usec==0))
+ timeout.tv_sec=timeout.tv_usec=-1;
+
+ /* heart-beat management */
+ if (cb->max_idle_time>0)
+ endtime=current_time() + cb->max_idle_time;
+
+ rc=ldap_search_ext(ld ,target,scope,filter,attrs,attrsonly,
+ ctrls, NULL, &timeout,sizelimit, &(ctx->msgid) );
+
+ if ( NULL != ctrls)
+ ldap_controls_free(ctrls);
+
+ if ( LDAP_SUCCESS != rc ) {
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, ldap_err2string(rc), 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ slapi_ch_free((void **) &ctx);
+ return 1;
+ }
+
+ /*
+ ** Need to get the very first result to handle
+ ** errors properly, especially no search base.
+ */
+
+ doit=1;
+ while (doit) {
+
+ if (cb_check_forward_abandon(cb,pb,ctx->ld,ctx->msgid)) {
+ slapi_ch_free((void **) &ctx);
+ return 1;
+ }
+
+ rc=ldap_result(ld,ctx->msgid,LDAP_MSG_ONE,&cb->abandon_timeout,&res);
+ switch ( rc ) {
+ case -1:
+ /* An error occurred. return now */
+ rc = ldap_get_lderrno(ld,NULL,NULL);
+ /* tuck away some errors in a OPERATION_ERROR */
+ if (CB_LDAP_CONN_ERROR(rc)) {
+ cb_send_ldap_result(pb,LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string( rc ), 0, NULL);
+ } else {
+ cb_send_ldap_result(pb,rc, NULL, NULL,0,NULL);
+ }
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ if (res)
+ ldap_msgfree(res);
+ slapi_ch_free((void **)&ctx);
+ return 1;
+ case 0:
+
+ /* Local timeout management */
+ if (timelimit != -1) {
+ if (current_time() > endbefore) {
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Local timeout expiration\n");
+
+ cb_send_ldap_result(pb,LDAP_TIMELIMIT_EXCEEDED,
+ NULL,NULL, 0, NULL);
+ /* Force connection close */
+ cb_release_op_connection(cb->pool,ld,1);
+ if (res)
+ ldap_msgfree(res);
+ slapi_ch_free((void **)&ctx);
+ return 1;
+ }
+ }
+ /* heart-beat management */
+ if ((rc=cb_ping_farm(cb,cnx,endtime)) != LDAP_SUCCESS) {
+ cb_send_ldap_result(pb,LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(rc), 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ if (res)
+ ldap_msgfree(res);
+ slapi_ch_free((void **)&ctx);
+ return 1;
+ }
+
+#ifdef CB_YIELD
+ DS_Sleep(PR_INTERVAL_NO_WAIT);
+#endif
+ break;
+ case LDAP_RES_SEARCH_ENTRY:
+ case LDAP_RES_SEARCH_REFERENCE:
+ /* Some results received */
+ /* don't parse result here */
+ ctx->pending_result=res;
+ ctx->pending_result_type=rc;
+ doit=0;
+ break;
+ case LDAP_RES_SEARCH_RESULT:
+ matched_msg=NULL;
+ error_msg=NULL;
+ referrals=NULL;
+ serverctrls=NULL;
+ parse_rc=ldap_parse_result(ld,res,&rc,&matched_msg,
+ &error_msg,&referrals, &serverctrls, 0 );
+ if ( parse_rc != LDAP_SUCCESS ) {
+ cb_send_ldap_result(pb,parse_rc,
+ matched_msg,error_msg,0,NULL);
+ rc=-1;
+ } else
+ if ( rc != LDAP_SUCCESS ) {
+ ldap_get_lderrno( ctx->ld, &matched_msg, &error_msg );
+ cb_send_ldap_result( pb, rc, matched_msg,
+ error_msg,0,NULL);
+ /* BEWARE: matched_msg and error_msg points */
+ /* to ld fields. */
+ matched_msg=NULL;
+ error_msg=NULL;
+ rc=-1;
+ }
+
+ slapi_ch_free((void **)&matched_msg);
+ slapi_ch_free((void **)&error_msg);
+ if (serverctrls)
+ ldap_controls_free(serverctrls);
+ if (referrals)
+ charray_free(referrals);
+
+ if (rc!=LDAP_SUCCESS) {
+ cb_release_op_connection(cb->pool,ld,
+ CB_LDAP_CONN_ERROR(rc));
+ ldap_msgfree(res);
+ slapi_ch_free((void **)&ctx);
+ return -1;
+ }
+
+ /* Store the msg in the ctx */
+ /* Parsed in iterate. */
+
+ ctx->pending_result=res;
+ ctx->pending_result_type=LDAP_RES_SEARCH_RESULT;
+ doit=0;
+ }
+ }
+
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET,ctx);
+ return 0;
+}
+
+/*
+ * Return the next entry in the result set. The entry is returned
+ * in the pblock.
+ * Returns 0 normally. If -1 is returned, it means that some
+ * exceptional condition, e.g. timelimit exceeded has occurred,
+ * and this routine has sent a result to the client. If zero
+ * is returned and no entry is available in the PBlock, then
+ * we've iterated through all the entries.
+ */
+
+int
+chainingdb_next_search_entry ( Slapi_PBlock *pb )
+{
+
+ char *target;
+ int sizelimit,timelimit, rc, parse_rc, optime,i,retcode, attrsonly;
+ LDAPMessage *res=NULL;
+ char *matched_msg,*error_msg;
+ cb_searchContext *ctx=NULL;
+ Slapi_Entry *entry;
+ LDAPControl **serverctrls=NULL;
+ char **referrals=NULL;
+ cb_backend_instance * cb=NULL;
+ Slapi_Backend * be;
+ time_t endtime;
+
+ matched_msg=error_msg=NULL;
+
+ slapi_pblock_get( pb, SLAPI_SEARCH_RESULT_SET, &ctx );
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+ slapi_pblock_get( pb, SLAPI_SEARCH_TIMELIMIT, &timelimit );
+ slapi_pblock_get( pb, SLAPI_SEARCH_SIZELIMIT, &sizelimit );
+ slapi_pblock_get( pb, SLAPI_SEARCH_TARGET, &target );
+ slapi_pblock_get( pb, SLAPI_OPINITIATED_TIME, &optime );
+ slapi_pblock_get( pb, SLAPI_SEARCH_ATTRSONLY, &attrsonly );
+
+ cb = cb_get_instance(be);
+
+ if ( NULL == ctx ) {
+ /* End of local search */
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET,NULL);
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY,NULL);
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Unexpected NULL ctx in chainingdb_next_search_entry\n");
+ return 0;
+ }
+
+ if ( NULL != ctx->tobefreed ) {
+ slapi_entry_free(ctx->tobefreed);
+ ctx->tobefreed=NULL;
+ }
+
+ if ( ctx->type == CB_SEARCHCONTEXT_ENTRY ) {
+
+ int n;
+ Slapi_Entry ** ptr;
+ if ( (timelimit != -1) && (timelimit != 0)) {
+ time_t now=current_time();
+
+ if (now > (optime + timelimit)) {
+ cb_send_ldap_result( pb, LDAP_TIMELIMIT_EXCEEDED, NULL,NULL, 0, NULL);
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET,NULL );
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY,NULL);
+
+ for ( n = 0, ptr=(Slapi_Entry **)ctx->data; ptr != NULL && ptr[n] != NULL; n++ ) {
+ slapi_entry_free(ptr[n]);
+ }
+ if (ctx->data)
+ slapi_ch_free((void **)&ctx->data);
+ slapi_ch_free((void **)&ctx);
+ return -1;
+ }
+ }
+
+ /*
+ ** Return the Slapi_Entry of the result set one
+ ** by one
+ */
+
+ for ( n = 0, ptr=(Slapi_Entry **)ctx->data; ptr != NULL && ptr[n] != NULL; n++ );
+ if ( n != 0) {
+ Slapi_Entry * anEntry=ptr[n-1];
+ ptr[n-1]=NULL;
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY,anEntry);
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET,ctx);
+ cb_set_acl_policy(pb);
+ ctx->tobefreed=anEntry;
+ } else {
+ slapi_ch_free((void **) &ctx);
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET,NULL );
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY,NULL);
+ }
+ return 0;
+ }
+
+ /*
+ * Grab a connection handle. Should be the same as the one
+ * used in the build_candidate list. To be certain of that, grab it from
+ * the context.
+ */
+
+ /* Poll the server for the results of the search operation.
+ * Passing LDAP_MSG_ONE indicates that you want to receive
+ * the entries one at a time, as they come in. If the next
+ * entry that you retrieve is NULL, there are no more entries.
+ */
+
+ /* heart-beat management */
+ if (cb->max_idle_time>0)
+ endtime=current_time() + cb->max_idle_time;
+
+ while (1) {
+
+ if (cb_check_forward_abandon(cb,pb,ctx->ld,ctx->msgid)) {
+ /* cnx handle released */
+ if (ctx->pending_result)
+ ldap_msgfree(ctx->pending_result);
+ slapi_ch_free((void **) &ctx);
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET,NULL );
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY,NULL);
+ return -1;
+ }
+
+ /* Check for time limit done by the remote farm server */
+ /* Check for size limit done by the remote farm server */
+
+ /* Use pending msg if one is available */
+ if (ctx->pending_result) {
+ res=ctx->pending_result;
+ rc=ctx->pending_result_type;
+ ctx->pending_result=NULL;
+ } else {
+
+
+ rc=ldap_result(ctx->ld,ctx->msgid,
+ LDAP_MSG_ONE, &cb->abandon_timeout, &res );
+ }
+
+ /* The server can return three types of results back to the client,
+ * and the return value of ldap_result() indicates the result type:
+ * LDAP_RES_SEARCH_ENTRY identifies an entry found by the search,
+ * LDAP_RES_SEARCH_REFERENCE identifies a search reference returned
+ * by the server, and LDAP_RES_SEARCH_RESULT is the last result
+ * sent from the server to the client after the operation completes.
+ * We need to check for each of these types of results.
+ */
+
+ switch ( rc ) {
+ case -1:
+
+ /* An error occurred. */
+ rc = ldap_get_lderrno( ctx->ld, NULL, NULL );
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET,NULL);
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY,NULL);
+
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, ldap_err2string( rc ), 0, NULL);
+
+ if (res)
+ ldap_msgfree(res);
+ cb_release_op_connection(cb->pool,ctx->ld,CB_LDAP_CONN_ERROR(rc));
+ slapi_ch_free((void **)&ctx);
+ return -1;
+ case 0:
+ /* heart-beat management */
+ if ((rc=cb_ping_farm(cb,ctx->cnx,endtime)) != LDAP_SUCCESS) {
+
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET,NULL);
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY,NULL);
+
+ cb_send_ldap_result(pb,LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(rc), 0, NULL);
+
+ if (res)
+ ldap_msgfree(res);
+ cb_release_op_connection(cb->pool,ctx->ld,CB_LDAP_CONN_ERROR(rc));
+ slapi_ch_free((void **)&ctx);
+ return -1;
+ }
+#ifdef CB_YIELD
+ DS_Sleep(PR_INTERVAL_NO_WAIT);
+#endif
+ break;
+
+ case LDAP_RES_SEARCH_ENTRY:
+
+ /* heart-beat management */
+ if (cb->max_idle_time>0)
+ endtime=current_time() + cb->max_idle_time;
+
+ /* The server sent one of the entries found by the search */
+ if ((entry = cb_LDAPMessage2Entry(ctx->ld,res,attrsonly)) == NULL) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,"Invalid entry received.\n");
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET,NULL);
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY,NULL);
+
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL , 0, NULL);
+
+ ldap_msgfree(res);
+ cb_release_op_connection(cb->pool,ctx->ld,0);
+ slapi_ch_free((void **)&ctx);
+ return -1;
+ }
+
+ ctx->tobefreed=entry;
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET,ctx);
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY,entry);
+ cb_set_acl_policy(pb);
+ ldap_msgfree(res);
+ return 0;
+
+ case LDAP_RES_SEARCH_REFERENCE:
+
+ /* The server sent a search reference encountered during the
+ * search operation.
+ */
+
+ /* heart-beat management */
+ if (cb->max_idle_time>0)
+ endtime=current_time() + cb->max_idle_time;
+
+ parse_rc = ldap_parse_reference( ctx->ld, res, &referrals, NULL, 1 );
+ if ( parse_rc != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string( parse_rc ), 0, NULL);
+ cb_release_op_connection(cb->pool,ctx->ld,CB_LDAP_CONN_ERROR(parse_rc));
+ slapi_ch_free((void **)&ctx);
+
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET,NULL);
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY,NULL);
+ return -1;
+ }
+
+ /*
+ ** build a dummy entry on the fly with a ref attribute
+ */
+
+ {
+
+ struct berval bv;
+ int i;
+ struct berval *bvals[2];
+ Slapi_Entry *anEntry = slapi_entry_alloc();
+ slapi_entry_set_dn(anEntry,slapi_ch_strdup(target));
+
+ bvals[1]=NULL;
+ bvals[0]=&bv;
+
+ bv.bv_val="referral";
+ bv.bv_len=strlen(bv.bv_val);
+ slapi_entry_add_values( anEntry, "objectclass", bvals);
+
+ for (i=0;referrals[i] != NULL; i++) {
+ bv.bv_val=referrals[i];
+ bv.bv_len=strlen(bv.bv_val);
+ slapi_entry_add_values( anEntry, "ref", bvals);
+ }
+
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET,ctx);
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY,anEntry);
+ cb_set_acl_policy(pb);
+ }
+
+ if (referrals != NULL) {
+ ldap_value_free( referrals );
+ }
+
+ return 0;
+
+ case LDAP_RES_SEARCH_RESULT:
+
+ /* Parse the final result received from the server. Note the last
+ * argument is a non-zero value, which indicates that the
+ * LDAPMessage structure will be freed when done.
+ */
+
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET,NULL);
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY,NULL);
+
+ parse_rc = ldap_parse_result( ctx->ld, res,
+ &rc,&matched_msg,&error_msg, &referrals, &serverctrls, 1 );
+ if ( parse_rc != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, matched_msg,
+ ldap_err2string( parse_rc ), 0, NULL);
+
+ retcode=-1;
+ } else
+ if ( rc != LDAP_SUCCESS ) {
+ ldap_get_lderrno( ctx->ld, &matched_msg, &error_msg );
+ cb_send_ldap_result( pb, rc, matched_msg, NULL, 0, NULL);
+
+ /* BEWARE: Don't free matched_msg && error_msg */
+ /* Points to the ld fields */
+ matched_msg=NULL;
+ error_msg=NULL;
+ retcode=-1;
+ } else {
+ /* Add control response sent by the farm server */
+ for (i=0; serverctrls && serverctrls[i];i++)
+ slapi_pblock_set( pb, SLAPI_ADD_RESCONTROL, serverctrls[i]);
+ retcode=0;
+ }
+
+ if (serverctrls)
+ ldap_controls_free(serverctrls);
+ slapi_ch_free((void **)&matched_msg);
+ slapi_ch_free((void **)&error_msg);
+ if (referrals)
+ charray_free(referrals);
+
+ cb_release_op_connection(cb->pool,ctx->ld,0);
+ slapi_ch_free((void **)&ctx);
+ return retcode;
+
+ default:
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "chainingdb_next_search_entry:default case.\n");
+
+ }
+ }
+
+ /* Not reached */
+ /* return 0; */
+}
+
+int
+chaining_back_entry_release ( Slapi_PBlock *pb ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM, "chaining_back_entry_release\n");
+ return 0;
+}
+
diff --git a/ldap/servers/plugins/chainingdb/cb_size.c b/ldap/servers/plugins/chainingdb/cb_size.c
new file mode 100644
index 00000000..a6f1fb20
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_size.c
@@ -0,0 +1,22 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+int
+cb_db_size( Slapi_PBlock *pb )
+{
+
+ /*
+ ** Return the size in byte of the local database storage
+ ** Size is 0 for a chaining backend
+ */
+
+ unsigned int size=0;
+
+ slapi_pblock_set( pb, SLAPI_DBSIZE, &size );
+ return 0;
+}
+
diff --git a/ldap/servers/plugins/chainingdb/cb_start.c b/ldap/servers/plugins/chainingdb/cb_start.c
new file mode 100644
index 00000000..c4a4538b
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_start.c
@@ -0,0 +1,43 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+int
+chainingdb_start ( Slapi_PBlock *pb ) {
+
+ cb_backend * cb;
+
+ slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &cb );
+
+ if (cb->started) {
+ /* We may be called multiple times due to */
+ /* plugin dependency resolution */
+ return 0;
+ }
+
+ /*
+ ** Reads in any configuration information held in the dse for the
+ ** chaining plugin. Create dse entries used to configure the
+ ** chaining plugin if they don't exist. Registers plugins to maintain
+ ** those dse entries.
+ */
+
+ cb_config_load_dse_info(pb);
+
+ /* Register new LDAPv3 controls supported by the chaining backend */
+
+ slapi_register_supported_control( CB_LDAP_CONTROL_CHAIN_SERVER,
+ SLAPI_OPERATION_SEARCH | SLAPI_OPERATION_COMPARE
+ | SLAPI_OPERATION_ADD | SLAPI_OPERATION_DELETE
+ | SLAPI_OPERATION_MODIFY | SLAPI_OPERATION_MODDN );
+
+ /* register to be notified when backend state changes */
+ slapi_register_backend_state_change((void *)cb_be_state_change,
+ cb_be_state_change);
+
+ cb->started=1;
+ return 0;
+}
diff --git a/ldap/servers/plugins/chainingdb/cb_temp.c b/ldap/servers/plugins/chainingdb/cb_temp.c
new file mode 100644
index 00000000..fc5407ff
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_temp.c
@@ -0,0 +1,15 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+/*
+** Temp wrappers until the appropriate functions
+** are implemented in the slapi interface
+*/
+
+cb_backend_instance * cb_get_instance(Slapi_Backend * be) {
+ return (cb_backend_instance *)slapi_be_get_instance_info(be);
+}
diff --git a/ldap/servers/plugins/chainingdb/cb_test.c b/ldap/servers/plugins/chainingdb/cb_test.c
new file mode 100644
index 00000000..cb075664
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_test.c
@@ -0,0 +1,76 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+int cb_back_test( Slapi_PBlock *pb )
+{
+
+ Slapi_Backend * be;
+ cb_backend * cb;
+ cb_backend_instance * inst;
+ Slapi_PBlock * apb;
+ int res;
+ int rc=0;
+ const Slapi_DN *aSuffix=NULL;
+ const char * aSuffixString;
+ char * theTarget;
+
+
+ slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &cb );
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+ inst = cb_get_instance(be);
+ apb = slapi_pblock_new();
+
+ /*
+ ** Try to open a connection to the farm server
+ ** Try to get a dummy entry BELOW the suffix managed
+ ** by the chaining backend, in case the local root is shared
+ ** across different backend
+ */
+
+ printf("Begin test instance %s.\n",inst->inst_name);
+
+ aSuffix = slapi_be_getsuffix(be,0);
+ aSuffixString=slapi_sdn_get_dn(aSuffix);
+ /* Remove leading white spaces */
+ for (aSuffixString; *aSuffixString==' ';aSuffixString++) {}
+ theTarget=slapi_ch_calloc(1,strlen(aSuffixString)+20);
+ sprintf(theTarget,"cn=test,%s",aSuffixString);
+
+ /* XXXSD make sure chaining allowed for this plugin... */
+ slapi_search_internal_set_pb (apb, theTarget, LDAP_SCOPE_BASE, "objectclass=*", NULL, 0, NULL, NULL,
+ cb->identity,0 );
+ slapi_search_internal_pb (apb);
+
+ slapi_ch_free((void **)&theTarget);
+
+ if ( NULL == apb ) {
+ printf("Can't contact farm server. (Internal error).\n");
+ rc=-1;
+ goto the_end;
+ }
+
+ slapi_pblock_get(apb, SLAPI_PLUGIN_INTOP_RESULT, &res);
+ /* OPERATIONS ERRORS also returned when bind failed */
+ if (CB_LDAP_CONN_ERROR(res) || (res==LDAP_OPERATIONS_ERROR ))
+ {
+ printf("Can't contact the remote farm server %s. (%s).\n",inst->pool->hostname,ldap_err2string(res));
+ rc=-1;
+ goto the_end;
+ } else {
+ printf("Connection established with the remote farm server %s.\n",inst->pool->hostname);
+ }
+
+the_end:
+ if (apb)
+ {
+ slapi_free_search_results_internal(apb);
+ slapi_pblock_destroy (apb);
+ }
+
+ return rc;
+}
+
diff --git a/ldap/servers/plugins/chainingdb/cb_unbind.c b/ldap/servers/plugins/chainingdb/cb_unbind.c
new file mode 100644
index 00000000..1400ae1f
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_unbind.c
@@ -0,0 +1,23 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+int
+chainingdb_unbind( Slapi_PBlock *pb ) {
+
+ /* Nothing to do because connection mgmt is stateless*/
+
+ Slapi_Backend * be;
+ cb_backend_instance * cb;
+
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+ cb = cb_get_instance(be);
+
+ cb_update_monitor_info(pb,cb,SLAPI_OPERATION_UNBIND);
+
+ cb_send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
+ return SLAPI_BIND_SUCCESS;
+}
diff --git a/ldap/servers/plugins/chainingdb/cb_utils.c b/ldap/servers/plugins/chainingdb/cb_utils.c
new file mode 100644
index 00000000..b73effd0
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_utils.c
@@ -0,0 +1,375 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+/* return the rootdn configured in the server */
+
+char * cb_get_rootdn() {
+
+ char * ret=slapi_get_rootdn();
+ if (ret == NULL)
+ ret = slapi_ch_strdup(CB_DIRECTORY_MANAGER_DN);
+ if (ret)
+ slapi_dn_normalize_case(ret); /* UTF8-aware */
+ return ret;
+}
+
+void
+cb_send_ldap_result(Slapi_PBlock *pb, int err, char *matched,char *text, int nentries, struct berval **urls )
+{
+ cb_set_acl_policy(pb);
+ slapi_send_ldap_result( pb, err, matched, text, nentries ,urls);
+}
+
+Slapi_Entry * cb_LDAPMessage2Entry(LDAP * ld, LDAPMessage * msg, int attrsonly) {
+
+ Slapi_Entry * e = slapi_entry_alloc();
+ char * a=NULL;
+ BerElement * ber=NULL;
+
+ if ( e == NULL ) return NULL;
+ if (msg == NULL) {
+ slapi_entry_free(e);
+ return NULL;
+ }
+
+ /*
+ * dn not allocated by slapi
+ * attribute type and values ARE allocated
+ */
+
+ slapi_entry_set_dn( e, ldap_get_dn( ld, msg ) );
+
+ for ( a = ldap_first_attribute( ld, msg, &ber ); a!=NULL;
+ a=ldap_next_attribute( ld, msg, ber ) ) {
+ if(attrsonly) {
+ slapi_entry_add_value(e, a, (Slapi_Value *)NULL);
+ ldap_memfree(a);
+ } else {
+ struct berval ** aVal = ldap_get_values_len( ld, msg, a);
+ slapi_entry_add_values( e, a, aVal);
+
+ ldap_memfree(a);
+ ldap_value_free_len(aVal);
+ }
+ }
+ if ( NULL != ber )
+ ldap_ber_free( ber, 0 );
+
+ return e;
+}
+
+struct berval ** referrals2berval(char ** referrals) {
+
+ int i;
+ struct berval ** val=NULL;
+
+ if (referrals == NULL)
+ return NULL;
+
+ for (i=0;referrals[i];i++) {}
+
+ val = (struct berval **) slapi_ch_calloc(1,(i+1)*sizeof(struct berval *));
+
+ for (i=0;referrals[i];i++) {
+
+ val[i]=(struct berval *) slapi_ch_malloc(sizeof(struct berval));
+ val[i]->bv_len= strlen(referrals[i]);
+ val[i]->bv_val = slapi_ch_strdup(referrals[i]);
+ }
+
+ return val;
+}
+
+char *
+cb_urlparse_err2string( int err )
+{
+ char *s="internal error";
+
+ switch( err ) {
+ case 0:
+ s = "no error";
+ break;
+ case LDAP_URL_ERR_NOTLDAP:
+ s = "missing ldap:// or ldaps://";
+ break;
+ case LDAP_URL_ERR_NODN:
+ s = "missing suffix";
+ break;
+ case LDAP_URL_ERR_BADSCOPE:
+ s = "invalid search scope";
+ break;
+ case LDAP_URL_ERR_MEM:
+ s = "unable to allocate memory";
+ break;
+ case LDAP_URL_ERR_PARAM:
+ s = "bad parameter to an LDAP URL function";
+ break;
+ }
+
+ return( s );
+}
+
+/*
+** Return LDAP_SUCCESS if an internal operation needs to be forwarded to
+** the farm server. We check chaining policy for internal operations
+** We also check max hop count for loop detection for both internal
+** and external operations
+*/
+
+int cb_forward_operation(Slapi_PBlock * pb ) {
+
+ Slapi_Operation *op=NULL;
+ Slapi_Backend *be;
+ struct slapi_componentid *cid = NULL;
+ char *pname;
+ cb_backend_instance *cb;
+ int retcode;
+ LDAPControl **ctrls=NULL;
+
+ slapi_pblock_get (pb, SLAPI_OPERATION, &op);
+
+ /* Loop detection */
+ slapi_pblock_get( pb, SLAPI_REQCONTROLS, &ctrls );
+
+ if ( NULL != ctrls ) {
+ struct berval *ctl_value=NULL;
+ int iscritical=0;
+
+ if (slapi_control_present(ctrls,CB_LDAP_CONTROL_CHAIN_SERVER,&ctl_value,&iscritical)) {
+
+ /* Decode control data */
+ /* hop INTEGER (0 .. maxInt) */
+
+ int hops = 0;
+ int rc;
+ BerElement *ber = NULL;
+
+ if ((ber = ber_init(ctl_value)) == NULL) {
+ slapi_log_error(SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "cb_forward_operation: ber_init: Memory allocation failed");
+ return LDAP_NO_MEMORY;
+ }
+ rc = ber_scanf(ber,"i",&hops);
+ if (LBER_ERROR == rc) {
+ slapi_log_error(SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Loop detection control badly encoded.");
+ ber_free(ber,1);
+ return LDAP_LOOP_DETECT;
+ }
+
+ if (hops <=0) {
+ slapi_log_error(SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Max hop count exceeded. Loop detected.\n");
+ ber_free(ber,1);
+ return LDAP_LOOP_DETECT;
+ }
+ ber_free(ber,1);
+ }
+ }
+
+ if ( !operation_is_flag_set(op, OP_FLAG_INTERNAL))
+ return LDAP_SUCCESS;
+
+ slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &cid);
+ if ( cid == NULL ) {
+ /* programming error in the front-end */
+ slapi_log_error(SLAPI_LOG_FATAL, CB_PLUGIN_SUBSYSTEM,
+ "NULL component identity in an internal operation.");
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+ pname=cid->sci_component_name;
+
+ if (cb_debug_on()) {
+ slapi_log_error(SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "internal op received from %s component \n",pname ? pname : "NULL");
+ }
+
+ /* First, make sure chaining is not denied */
+ if (operation_is_flag_set(op, SLAPI_OP_FLAG_NEVER_CHAIN))
+ return LDAP_UNWILLING_TO_PERFORM;
+
+ /* unidentified caller. should not happen */
+ if (pname == NULL)
+ return LDAP_UNWILLING_TO_PERFORM;
+
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+ cb = cb_get_instance(be);
+
+ /* Local policy */
+ PR_RWLock_Rlock(cb->rwl_config_lock);
+ if ( cb->chaining_components != NULL ) {
+ retcode=charray_inlist(cb->chaining_components,pname);
+ PR_RWLock_Unlock(cb->rwl_config_lock);
+ if ( retcode )
+ retcode=LDAP_SUCCESS;
+ else
+ retcode=LDAP_UNWILLING_TO_PERFORM;
+ return retcode;
+ }
+ PR_RWLock_Unlock(cb->rwl_config_lock);
+
+ /* Global policy */
+ PR_RWLock_Rlock(cb->backend_type->config.rwl_config_lock);
+ retcode=charray_inlist(cb->backend_type->config.chaining_components,pname);
+ PR_RWLock_Unlock(cb->backend_type->config.rwl_config_lock);
+
+ if ( retcode )
+ retcode=LDAP_SUCCESS;
+ else
+ retcode=LDAP_UNWILLING_TO_PERFORM;
+ return retcode;
+}
+
+/* better atol -- it understands a trailing multiplier k/m/g
+ * for example, "32k" will be returned as 32768
+ */
+long cb_atol(char *str)
+{
+ long multiplier = 1;
+ char *x = str;
+
+ /* find possible trailing k/m/g */
+ while ((*x >= '0') && (*x <= '9')) x++;
+ switch (*x) {
+ case 'g':
+ case 'G':
+ multiplier *= 1024;
+ case 'm':
+ case 'M':
+ multiplier *= 1024;
+ case 'k':
+ case 'K':
+ multiplier *= 1024;
+ }
+ return (atol(str) * multiplier);
+}
+
+int cb_atoi(char *str)
+{
+ return (int)cb_atol(str);
+}
+
+
+/* This function is used by the instance modify callback to add a new
+ * suffix. It return LDAP_SUCCESS on success.
+ */
+int cb_add_suffix(cb_backend_instance *inst, struct berval **bvals, int apply_mod, char *returntext)
+{
+ Slapi_DN *suffix;
+ int x;
+
+ returntext[0] = '\0';
+ for (x = 0; bvals[x]; x++) {
+ suffix=slapi_sdn_new_dn_byval(bvals[x]->bv_val);
+ if (!slapi_be_issuffix(inst->inst_be, suffix) && apply_mod) {
+ slapi_be_addsuffix(inst->inst_be, suffix);
+ }
+ slapi_sdn_free(&suffix);
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int debug_on=0;
+
+int cb_debug_on()
+{
+ return debug_on;
+}
+
+void cb_set_debug(int on) {
+ debug_on=on;
+}
+
+/* this function is called when state of a backend changes */
+/* The purpose of this function is to handle the associated_be_is_disabled
+ flag in the cb instance structure. The associated database is used to
+ perform local acl evaluations. The associated database can be
+ 1) The chaining backend is the backend of a sub suffix, and the
+ parent suffix has a local backend
+ 2) Entry distribution is being used to distribute write operations to
+ a chaining backend and other operations to a local backend
+ (e.g. a replication hub or consumer)
+ If the associated local backend is being initialized (import), it will be
+ disabled, and it will be impossible to evaluate local acls. In this case,
+ we still want to be able to chain operations to a farm server or another
+ database chain. But the current code will not allow cascading without
+ local acl evaluation (cb_controls.c). associated_be_is_disabled allows
+ us to relax that restriction while the associated backend is disabled
+*/
+/*
+ The first thing we need to do is to determine what our associated backends
+ are. An associated backend is defined as a backend used by the same
+ suffix which uses this cb instance or a backend used by any
+ parent suffix of the suffix which uses this cb instance
+
+ We first see if the be_name is for a local database. If not, then just return.
+ So for the given be_name, we find the suffix which uses it, then the mapping tree
+ entry for that suffix. Then
+ get cb instances used by the suffix and set associated_be_is_disabled
+ get cb instances used by sub suffixes of this suffix and
+ set associated_be_is_disabled
+*/
+void
+cb_be_state_change (void *handle, char *be_name, int old_be_state, int new_be_state)
+{
+ const Slapi_DN *tmpsdn;
+ Slapi_DN *the_be_suffix;
+ char *cookie = NULL;
+ Slapi_Backend *chainbe;
+ Slapi_Backend *the_be = slapi_be_select_by_instance_name(be_name);
+
+ /* no backend? */
+ if (!the_be) {
+ return;
+ }
+
+ /* ignore chaining backends - associated backends must be local */
+ if (slapi_be_is_flag_set(the_be, SLAPI_BE_FLAG_REMOTE_DATA)) {
+ return;
+ }
+
+ /* get the suffix for the local backend */
+ tmpsdn = slapi_be_getsuffix(the_be, 0);
+ if (!tmpsdn) {
+ return;
+ } else {
+ the_be_suffix = slapi_sdn_dup(tmpsdn);
+ }
+
+ /* now, iterate through the chaining backends */
+ for (chainbe = slapi_get_first_backend(&cookie);
+ chainbe; chainbe = slapi_get_next_backend(cookie)) {
+ /* only look at chaining backends */
+ if (slapi_be_is_flag_set(chainbe, SLAPI_BE_FLAG_REMOTE_DATA)) {
+ /* get the suffix */
+ const Slapi_DN *tmpcbsuf = slapi_be_getsuffix(chainbe, 0);
+ if (tmpcbsuf) {
+ /* make a copy - to be safe */
+ Slapi_DN *cbsuffix = slapi_sdn_dup(tmpcbsuf);
+ /* if the suffixes are equal, or the_be_suffix is a suffix
+ of cbsuffix, apply the flag */
+ if (!slapi_sdn_compare(cbsuffix, the_be_suffix) ||
+ slapi_sdn_issuffix(cbsuffix, the_be_suffix)) {
+ cb_backend_instance *cbinst = cb_get_instance(chainbe);
+ if (cbinst) {
+ /* the backend is disabled if the state is not ON */
+ cbinst->associated_be_is_disabled = (new_be_state != SLAPI_BE_STATE_ON);
+ slapi_log_error(SLAPI_LOG_PLUGIN, "chainbe", "cb_be_state_change: set the "
+ "state of chainbe for %s to %d\n",
+ slapi_sdn_get_dn(cbsuffix), (new_be_state != SLAPI_BE_STATE_ON));
+ }
+ }
+ slapi_sdn_free(&cbsuffix);
+ }
+ }
+ }
+
+ /* clean up */
+ slapi_sdn_free(&the_be_suffix);
+ slapi_ch_free_string(&cookie);
+}
diff --git a/ldap/servers/plugins/chainingdb/cbdllmain.c b/ldap/servers/plugins/chainingdb/cbdllmain.c
new file mode 100644
index 00000000..cacf9cb5
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cbdllmain.c
@@ -0,0 +1,128 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+#ifdef _WIN32
+/* Lifted from Q125688
+ * How to Port a 16-bit DLL to a Win32 DLL
+ * on the MSVC 4.0 CD
+ */
+BOOL WINAPI DllMain (HANDLE hModule, DWORD fdwReason, LPVOID lpReserved)
+{
+ WSADATA wsadata;
+
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ /* Code from LibMain inserted here. Return TRUE to keep the
+ DLL loaded or return FALSE to fail loading the DLL.
+
+ You may have to modify the code in your original LibMain to
+ account for the fact that it may be called more than once.
+ You will get one DLL_PROCESS_ATTACH for each process that
+ loads the DLL. This is different from LibMain which gets
+ called only once when the DLL is loaded. The only time this
+ is critical is when you are using shared data sections.
+ If you are using shared data sections for statically
+ allocated data, you will need to be careful to initialize it
+ only once. Check your code carefully.
+
+ Certain one-time initializations may now need to be done for
+ each process that attaches. You may also not need code from
+ your original LibMain because the operating system may now
+ be doing it for you.
+ */
+ /*
+ * 16 bit code calls UnlockData()
+ * which is mapped to UnlockSegment in windows.h
+ * in 32 bit world UnlockData is not defined anywhere
+ * UnlockSegment is mapped to GlobalUnfix in winbase.h
+ * and the docs for both UnlockSegment and GlobalUnfix say
+ * ".. function is oboslete. Segments have no meaning
+ * in the 32-bit environment". So we do nothing here.
+ */
+
+ if( errno = WSAStartup(0x0101, &wsadata ) != 0 )
+ return FALSE;
+
+ break;
+
+ case DLL_THREAD_ATTACH:
+ /* Called each time a thread is created in a process that has
+ already loaded (attached to) this DLL. Does not get called
+ for each thread that exists in the process before it loaded
+ the DLL.
+
+ Do thread-specific initialization here.
+ */
+ break;
+
+ case DLL_THREAD_DETACH:
+ /* Same as above, but called when a thread in the process
+ exits.
+
+ Do thread-specific cleanup here.
+ */
+ break;
+
+ case DLL_PROCESS_DETACH:
+ /* Code from _WEP inserted here. This code may (like the
+ LibMain) not be necessary. Check to make certain that the
+ operating system is not doing it for you.
+ */
+ WSACleanup();
+
+ break;
+ }
+ /* The return value is only used for DLL_PROCESS_ATTACH; all other
+ conditions are ignored. */
+ return TRUE; // successful DLL_PROCESS_ATTACH
+}
+#else
+int CALLBACK
+LibMain( HINSTANCE hinst, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine )
+{
+ /*UnlockData( 0 );*/
+ return( 1 );
+}
+#endif
+
+#ifdef LDAP_DEBUG
+#ifndef _WIN32
+#include <stdarg.h>
+#include <stdio.h>
+
+void LDAPDebug( int level, char* fmt, ... )
+{
+ static char debugBuf[1024];
+
+ if (module_ldap_debug && (*module_ldap_debug & level))
+ {
+ va_list ap;
+ va_start (ap, fmt);
+ _snprintf (debugBuf, sizeof(debugBuf), fmt, ap);
+ va_end (ap);
+
+ OutputDebugString (debugBuf);
+ }
+}
+#endif
+#endif
+
+#ifndef _WIN32
+
+/* The 16-bit version of the RTL does not implement perror() */
+
+#include <stdio.h>
+
+void perror( const char *msg )
+{
+ char buf[128];
+ wsprintf( buf, "%s: error %d\n", msg, WSAGetLastError()) ;
+ OutputDebugString( buf );
+}
+
+#endif
diff --git a/ldap/servers/plugins/chainingdb/libcb.def b/ldap/servers/plugins/chainingdb/libcb.def
new file mode 100644
index 00000000..71fe8482
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/libcb.def
@@ -0,0 +1,15 @@
+; BEGIN COPYRIGHT BLOCK
+; Copyright 2001 Sun Microsystems, Inc.
+; Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+; All rights reserved.
+; END COPYRIGHT BLOCK
+;
+;
+;
+DESCRIPTION 'Netscape Directory Server 7 Chaining Database Plugin'
+;CODE SHARED READ EXECUTE
+;DATA SHARED READ WRITE
+EXPORTS
+ chaining_back_init @1
+ plugin_init_debug_level @2
+ cb_be_state_change @3