summaryrefslogtreecommitdiffstats
path: root/ldap/servers/slapd/conntable.c
diff options
context:
space:
mode:
authorcvsadm <cvsadm>2005-01-21 00:44:34 +0000
committercvsadm <cvsadm>2005-01-21 00:44:34 +0000
commitb2093e3016027d6b5cf06b3f91f30769bfc099e2 (patch)
treecf58939393a9032182c4fbc4441164a9456e82f8 /ldap/servers/slapd/conntable.c
downloadds-b2093e3016027d6b5cf06b3f91f30769bfc099e2.tar.gz
ds-b2093e3016027d6b5cf06b3f91f30769bfc099e2.tar.xz
ds-b2093e3016027d6b5cf06b3f91f30769bfc099e2.zip
Moving NSCP Directory Server from DirectoryBranch to TRUNK, initial drop. (foxworth)ldapserver7x
Diffstat (limited to 'ldap/servers/slapd/conntable.c')
-rw-r--r--ldap/servers/slapd/conntable.c515
1 files changed, 515 insertions, 0 deletions
diff --git a/ldap/servers/slapd/conntable.c b/ldap/servers/slapd/conntable.c
new file mode 100644
index 00000000..8297971c
--- /dev/null
+++ b/ldap/servers/slapd/conntable.c
@@ -0,0 +1,515 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* Connection Table */
+
+#include "fe.h"
+
+Connection_Table *
+connection_table_new(int table_size)
+{
+ Connection_Table *ct;
+ int i = 0;
+
+
+ ct= (Connection_Table*)slapi_ch_calloc( 1, sizeof(Connection_Table) );
+ ct->size= table_size;
+ ct->c = (Connection *)slapi_ch_calloc( 1, table_size * sizeof(Connection) );
+ ct->fd = (struct POLL_STRUCT *)slapi_ch_calloc(1, table_size * sizeof(struct POLL_STRUCT));
+ ct->table_mutex = PR_NewLock();
+
+ /* We rely on the fact that we called calloc, which zeros the block, so we don't
+ * init any structure element unless a zero value is troublesome later
+ */
+ for ( i = 0; i < table_size; i++ )
+ {
+ int invalid_socket;
+ unsigned long maxbersize= config_get_maxbersize();
+ /* DBDB---move this out of here once everything works */
+ ct->c[i].c_sb = ber_sockbuf_alloc();
+ invalid_socket = SLAPD_INVALID_SOCKET;
+ ber_sockbuf_set_option( ct->c[i].c_sb, LBER_SOCKBUF_OPT_DESC, &invalid_socket );
+ ct->c[i].c_sd = SLAPD_INVALID_SOCKET;
+ ber_sockbuf_set_option( ct->c[i].c_sb, LBER_SOCKBUF_OPT_NO_READ_AHEAD, LBER_OPT_ON );
+ ber_sockbuf_set_option( ct->c[i].c_sb, LBER_SOCKBUF_OPT_MAX_INCOMING_SIZE, &maxbersize );
+#ifndef _WIN32
+ /* all connections start out invalid */
+ ct->fd[i].fd = SLAPD_INVALID_SOCKET;
+#endif
+ /* The connection table has a double linked list running through it.
+ * This is used to find out which connections should be looked at
+ * in the poll loop. Slot 0 in the table is always the head of
+ * the linked list. Each slot has a c_next and c_prev which are
+ * pointers back into the array of connection slots. */
+ ct->c[i].c_next = NULL;
+ ct->c[i].c_prev = NULL;
+ ct->c[i].c_ci= i;
+ ct->c[i].c_fdi= SLAPD_INVALID_SOCKET_INDEX;
+ }
+ return ct;
+}
+
+void connection_table_free(Connection_Table *ct)
+{
+ int i;
+ for (i = 0; i < ct->size; i++)
+ {
+ /* Free the contents of the connection structure */
+ Connection *c= &(ct->c[i]);
+ connection_done(c);
+ }
+ slapi_ch_free((void**)&ct->c);
+ slapi_ch_free((void**)&ct->fd);
+ PR_DestroyLock(ct->table_mutex);
+ slapi_ch_free((void**)&ct);
+}
+
+
+#ifdef _WIN32
+/*
+ * This function looks up connection by nspr fd. It is
+ * slow because it iterrates through the entire connection
+ * array. Currently, it is only used on NT in secure_read_function
+ * to handle I/O timeout on SSL socket we should almost never
+ * happen.
+ */
+Connection*
+connection_table_get_connection_from_fd(Connection_Table *ct,PRFileDesc *prfd)
+{
+ int i;
+ for (i = 0; i < ct->size; i++)
+ {
+ if (ct->c[i].c_prfd == prfd)
+ {
+ return (&(ct->c[i]));
+ }
+ }
+
+ return NULL;
+}
+#endif
+
+void
+connection_table_abandon_all_operations(Connection_Table *ct)
+{
+ int i;
+ for ( i = 0; i < ct->size; i++ )
+ {
+ if ( ct->c[i].c_mutex != NULL )
+ {
+ PR_Lock( ct->c[i].c_mutex );
+ connection_abandon_operations( &ct->c[i] );
+ PR_Unlock( ct->c[i].c_mutex );
+ }
+ }
+}
+
+/* Given a file descriptor for a socket, this function will return
+ * a slot in the connection table to use.
+ *
+ * Note: this function is only called from the slapd_daemon (listener)
+ * thread, which means it will never be called by two threads at
+ * the same time.
+ *
+ * Returns a Connection on success
+ * Returns NULL on failure
+ */
+Connection *
+connection_table_get_connection(Connection_Table *ct, int sd)
+{
+ Connection *c= NULL;
+ int index, count;
+
+ index = sd % ct->size;
+ for( count = 0; count < ct->size; count++, index = (index + 1) % ct->size)
+ {
+ /* Do not use slot 0, slot 0 is head of the list of active connections */
+ if ( index == 0 )
+ {
+ continue;
+ }
+ else if( ct->c[index].c_mutex == NULL )
+ {
+ break;
+ }
+
+ if( connection_is_free (&(ct->c[index])))
+ {
+ break;
+ }
+ }
+
+ if ( count < ct->size )
+ {
+ /* Found an available Connection */
+ c= &(ct->c[index]);
+ PR_ASSERT(c->c_next==NULL);
+ PR_ASSERT(c->c_prev==NULL);
+ PR_ASSERT(c->c_extension==NULL);
+ if ( c->c_mutex == NULL )
+ {
+ PR_Lock( ct->table_mutex );
+ c->c_mutex = PR_NewLock();
+ c->c_pdumutex = PR_NewLock();
+ PR_Unlock( ct->table_mutex );
+ if ( c->c_mutex == NULL || c->c_pdumutex == NULL )
+ {
+ c->c_mutex = NULL;
+ c->c_pdumutex = NULL;
+ LDAPDebug( LDAP_DEBUG_ANY,"PR_NewLock failed\n",0, 0, 0 );
+ c= NULL;
+ }
+ }
+ /* Let's make sure there's no cruft left on there from the last time this connection was used. */
+ /* Note: no need to lock c->c_mutex because this function is only
+ * called by one thread (the slapd_daemon thread), and if we got this
+ * far then `c' is not being used by any operation threads, etc.
+ */
+ connection_cleanup(c);
+ }
+ else
+ {
+ /* couldn't find a Connection */
+ LDAPDebug( LDAP_DEBUG_CONNS, "max open connections reached\n", 0, 0, 0);
+ }
+ return c;
+}
+
+/* active connection iteration functions */
+
+Connection*
+connection_table_get_first_active_connection (Connection_Table *ct)
+{
+ return ct->c[0].c_next;
+}
+
+Connection*
+connection_table_get_next_active_connection (Connection_Table *ct, Connection *c)
+{
+ return c->c_next;
+}
+
+int connection_table_iterate_active_connections(Connection_Table *ct, void* arg, Connection_Table_Iterate_Function f)
+{
+ /* Locking in this area seems rather undeveloped, I think because typically only one
+ * thread accesses the connection table (daemon thread). But now we allow other threads
+ * to iterate over the table. So we use the "new mutex mutex" to lock the table.
+ */
+ Connection *current_conn = NULL;
+ int ret = 0;
+ PR_Lock(ct->table_mutex);
+ current_conn = connection_table_get_first_active_connection (ct);
+ while (current_conn) {
+ ret = f(current_conn, arg);
+ if (ret) {
+ break;
+ }
+ current_conn = connection_table_get_next_active_connection (ct, current_conn);
+ }
+ PR_Unlock(ct->table_mutex);
+ return ret;
+}
+
+static void
+connection_table_dump_active_connection (Connection *c)
+{
+ slapi_log_error(SLAPI_LOG_FATAL, "connection", "conn=%p fd=%d refcnt=%d c_flags=%d\n"
+ "mutex=%p next=%p prev=%p\n\n", c, c->c_sd, c->c_refcnt, c->c_flags,
+ c->c_mutex, c->c_next, c->c_prev);
+}
+
+static void
+connection_table_dump_active_connections (Connection_Table *ct)
+{
+ Connection* c;
+
+ PR_Lock(ct->table_mutex);
+ slapi_log_error(SLAPI_LOG_FATAL, "connection", "********** BEGIN DUMP ************\n");
+ c = connection_table_get_first_active_connection (ct);
+ while (c)
+ {
+ connection_table_dump_active_connection (c);
+ c = connection_table_get_next_active_connection (ct, c);
+ }
+
+ slapi_log_error(SLAPI_LOG_FATAL, "connection", "********** END DUMP ************\n");
+ PR_Unlock(ct->table_mutex);
+}
+
+/*
+ * There's a double linked list of active connections running through the array
+ * of connections. This function removes a connection (by index) from that
+ * list. This list is used to find the connections that should be used in the
+ * poll call.
+ */
+void
+connection_table_move_connection_out_of_active_list(Connection_Table *ct,Connection *c)
+{
+ /* we always have previous element because list contains a dummy header */;
+ PR_ASSERT (c->c_prev);
+
+ /* slapi_log_error(SLAPI_LOG_FATAL, "connection", "Moving connection out of active list\n");
+ connection_table_dump_active_connection (c);*/
+
+ /*
+ * Note: the connection will NOT be moved off the active list if any other threads still hold
+ * a reference to the connection (that is, its reference count must be 1 or less).
+ */
+ if(c->c_refcnt > 1) return;
+
+ /* We need to lock here because we're modifying the linked list */
+ PR_Lock(ct->table_mutex);
+
+ c->c_prev->c_next = c->c_next;
+
+ if (c->c_next)
+ {
+ c->c_next->c_prev = c->c_prev;
+ }
+
+ connection_release_nolock (c);
+
+ connection_cleanup (c);
+
+ PR_Unlock(ct->table_mutex);
+
+ /* connection_table_dump_active_connections (ct); */
+}
+
+/*
+ * There's a double linked list of active connections running through the array
+ * of connections. This function adds a connection (by index) to the head of
+ * that list. This list is used to find the connections that should be used in the
+ * poll call.
+ */
+void
+connection_table_move_connection_on_to_active_list(Connection_Table *ct,Connection *c)
+{
+ PR_ASSERT(c->c_next==NULL);
+ PR_ASSERT(c->c_prev==NULL);
+
+ PR_Lock(ct->table_mutex);
+
+ connection_acquire_nolock (c);
+
+ /* slapi_log_error(SLAPI_LOG_FATAL, "connection", "Moving connection into active list\n");
+ connection_table_dump_active_connection (c);*/
+
+ c->c_next = ct->c[0].c_next;
+ if ( c->c_next != NULL )
+ {
+ c->c_next->c_prev = c;
+ }
+ c->c_prev = &(ct->c[0]);
+ ct->c[0].c_next = c;
+
+ PR_Unlock(ct->table_mutex);
+
+ /* connection_table_dump_active_connections (ct); */
+}
+
+/*
+ * Replace the following attributes within the entry 'e' with
+ * information about the connection table:
+ * connection // multivalued; one value for each active connection
+ * currentconnections // single valued; an integer count
+ * totalconnections // single valued; an integer count
+ * dtablesize // single valued; an integer size
+ * readwaiters // single valued; an integer count
+ */
+void
+connection_table_as_entry(Connection_Table *ct, Slapi_Entry *e)
+{
+ char buf[BUFSIZ];
+ struct berval val;
+ struct berval *vals[2];
+ int i, nconns, nreadwaiters;
+ struct tm utm;
+
+ vals[0] = &val;
+ vals[1] = NULL;
+
+ attrlist_delete( &e->e_attrs, "connection");
+ nconns = 0;
+ nreadwaiters = 0;
+ for ( i = 0; i < (ct!=NULL?ct->size:0); i++ )
+ {
+ PR_Lock( ct->table_mutex );
+ if ( (ct->c[i].c_mutex == NULL) || (ct->c[i].c_mutex == (PRLock*)-1) )
+ {
+ PR_Unlock( ct->table_mutex );
+ continue;
+ }
+ /* Can't take c_mutex if holding table_mutex; temporarily unlock */
+ PR_Unlock( ct->table_mutex );
+
+ PR_Lock( ct->c[i].c_mutex );
+ if ( ct->c[i].c_sd != SLAPD_INVALID_SOCKET )
+ {
+ char buf2[20];
+ int lendn = ct->c[i].c_dn ? strlen(ct->c[i].c_dn) : 6; /* "NULLDN" */
+ char *bufptr = &buf[0];
+ char *newbuf = NULL;
+
+ nconns++;
+ if ( ct->c[i].c_gettingber )
+ {
+ nreadwaiters++;
+ }
+
+#ifdef _WIN32
+ {
+ struct tm *pt;
+ pt = gmtime( &ct->c[i].c_starttime );
+ memcpy(&utm, pt, sizeof(struct tm) );
+ }
+#else
+ gmtime_r( &ct->c[i].c_starttime, &utm );
+#endif
+ strftime( buf2, sizeof(buf2), "%Y%m%d%H%M%SZ", &utm );
+
+ if (lendn > (BUFSIZ - 46)) {
+ /*
+ * 46 = 4 for the "i" couter + 20 for buf2 +
+ * 10 for c_opsinitiated + 10 for c_opscompleted +
+ * 1 for c_gettingber + 1
+ */
+ newbuf = (char *) slapi_ch_malloc(lendn + 46);
+ bufptr = newbuf;
+ }
+
+ sprintf( bufptr, "%d:%s:%d:%d:%s%s:%s", i,
+ buf2,
+ ct->c[i].c_opsinitiated,
+ ct->c[i].c_opscompleted,
+ ct->c[i].c_gettingber ? "r" : "",
+ "",
+ ct->c[i].c_dn ? ct->c[i].c_dn : "NULLDN" );
+ val.bv_val = bufptr;
+ val.bv_len = strlen( bufptr );
+ attrlist_merge( &e->e_attrs, "connection", vals );
+ if (newbuf) {
+ slapi_ch_free((void **) &newbuf);
+ }
+ }
+ PR_Unlock( ct->c[i].c_mutex );
+ }
+
+ sprintf( buf, "%d", nconns );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ attrlist_replace( &e->e_attrs, "currentconnections", vals );
+
+ PR_Lock( num_conns_mutex );
+ sprintf( buf, "%d", num_conns );
+ PR_Unlock( num_conns_mutex );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ attrlist_replace( &e->e_attrs, "totalconnections", vals );
+
+ sprintf( buf, "%d", (ct!=NULL?ct->size:0) );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ attrlist_replace( &e->e_attrs, "dtablesize", vals );
+
+ sprintf( buf, "%d", nreadwaiters );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ attrlist_replace( &e->e_attrs, "readwaiters", vals );
+}
+
+void
+connection_table_dump_activity_to_errors_log(Connection_Table *ct)
+{
+ int i;
+ for ( i = 0; i < ct->size; i++ )
+ {
+ Connection *c= &(ct->c[i]);
+ if ( c->c_mutex != NULL )
+ {
+ /* Find the connection we are referring to */
+ int j= c->c_fdi;
+ PR_Lock( c->c_mutex );
+ if ( c->c_sd != SLAPD_INVALID_SOCKET && c->c_prfd == ct->fd[j].fd )
+ {
+ int r = ct->fd[j].out_flags & SLAPD_POLL_FLAGS;
+ if ( r )
+ {
+ LDAPDebug( LDAP_DEBUG_CONNS,"activity on %d%s\n", i, r ? "r" : "",0 );
+ }
+ }
+ PR_Unlock( c->c_mutex );
+ }
+ }
+}
+
+#if 0
+void dump_op_list(FILE *file, Operation *op);
+
+void
+connection_table_dump(Connection_Table *ct)
+{
+ FILE *file;
+
+ file = fopen("/tmp/slapd.conn", "a+");
+ if (file != NULL)
+ {
+ int i;
+ fprintf(file, "=============pid=%d==================\n", getpid());
+ for ( i = 0; i < ct->size; i++ )
+ {
+ if ( (ct->c[i].c_sd == SLAPD_INVALID_SOCKET) && (ct->c[i].c_connid == 0) )
+ {
+ continue;
+ }
+ fprintf(file, "c[%d].c_dn=0x%x\n", i, ct->c[i].c_dn);
+ dump_op_list(file, ct->c[i].c_ops);
+ fprintf(file, "c[%d].c_sb.sb_sd=%d\n", i, ct->c[i].c_sd);
+ fprintf(file, "c[%d].c_connid=%d\n", i, ct->c[i].c_connid);
+ fprintf(file, "c[%d].c_opsinitiated=%d\n", i, ct->c[i].c_opsinitiated);
+ fprintf(file, "c[%d].c_opscompleted=%d\n", i, ct->c[i].c_opscompleted);
+ }
+ fclose(file);
+ }
+}
+
+static const char *
+op_status2str( int o_status )
+{
+ const char *s = "unknown";
+
+ switch( o_status ) {
+ case SLAPI_OP_STATUS_PROCESSING:
+ s = "processing";
+ break;
+ case SLAPI_OP_STATUS_ABANDONED:
+ s = "abandoned";
+ break;
+ case SLAPI_OP_STATUS_WILL_COMPLETE:
+ s = "will_complete";
+ break;
+ case SLAPI_OP_STATUS_RESULT_SENT:
+ s = "result_sent";
+ break;
+ }
+
+ return s;
+}
+
+void
+dump_op_list(FILE *file, Operation *op)
+{
+ Operation *tmp;
+
+ for ( tmp = op; tmp != NULL; tmp = tmp->o_next )
+ {
+ fprintf(file,
+ "(o_msgid=%d, o_tag=%d, o_sdn=0x%x, o_opid=%d, o_connid=%d, o_status=%s)\n",
+ tmp->o_msgid, tmp->o_tag, slapi_sdn_get_dn(&tmp->o_sdn), tmp->o_connid,
+ *tmp->o_tid, op_status2str( tmp->o_status ));
+ }
+}
+#endif /* 0 */
+