diff options
author | cvsadm <cvsadm> | 2005-01-21 00:44:34 +0000 |
---|---|---|
committer | cvsadm <cvsadm> | 2005-01-21 00:44:34 +0000 |
commit | b2093e3016027d6b5cf06b3f91f30769bfc099e2 (patch) | |
tree | cf58939393a9032182c4fbc4441164a9456e82f8 /ldap/servers/plugins/chainingdb | |
download | ds-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')
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 |