]> git.sur5r.net Git - openldap/commitdiff
Addition of a new Concurrency Test fro testing slapd performance and
authorKurt Spanier <ksp@openldap.org>
Mon, 15 Feb 1999 10:49:20 +0000 (10:49 +0000)
committerKurt Spanier <ksp@openldap.org>
Mon, 15 Feb 1999 10:49:20 +0000 (10:49 +0000)
  correctness of locking schemas in backends.
In back-bdb2 open NEXTID during startup and close at shutdown.

18 files changed:
doc/devel/todo
servers/slapd/back-bdb2/back-bdb2.h
servers/slapd/back-bdb2/index.c
servers/slapd/back-bdb2/init.c
servers/slapd/back-bdb2/nextid.c
servers/slapd/back-bdb2/startup.c
servers/slapd/back-bdb2/txn.c
tests/Makefile.in
tests/data/do_add.1 [new file with mode: 0644]
tests/data/do_read.0 [new file with mode: 0644]
tests/data/do_search.0 [new file with mode: 0644]
tests/data/slapd-bdb2-master.conf
tests/scripts/defines.sh
tests/scripts/test008-concurrency [new file with mode: 0755]
tests/slapd-addel.c [new file with mode: 0644]
tests/slapd-read.c [new file with mode: 0644]
tests/slapd-search.c [new file with mode: 0644]
tests/slapd-tester.c [new file with mode: 0644]

index c1781181d166b38ae0aacd564cb819a0bfdd8888..3a8e7e057af632e21d648b02a8c716a10b597071 100644 (file)
@@ -32,7 +32,7 @@ Modify -lldap to be session-level multithreaded
 Port slapd (incl back-ldbm & tools) to NT
 MajorLDAP - Design and implement an LDAP-enabled mailing list manager
 Slapd-DB2 - Design and implement a backend to take full advantage
-       of the latest DB2 features. 
+       of the latest DB2 features <ksp@openldap.org>
 
 
 Medium projects
index f3c0ec0d688a362219c98e9f32c969e3fe1e3243..126c2fb6ce4256bcb536f67aa43c39f6ec3f2d40 100644 (file)
@@ -90,7 +90,6 @@ struct dbcache {
        int                     dbc_refcnt;
        int                     dbc_maxids;
        int                     dbc_maxindirect;
-       time_t          dbc_lastref;
        long            dbc_blksize;
        char            *dbc_name;
        LDBM            dbc_db;
@@ -146,6 +145,11 @@ typedef  struct _bdb2_txn_head {
 #define  BDB2_DB_ID2CHILDREN_FILE   3
 #define  BDB2_DB_OC_IDX_FILE        4
 
+       /*  a file pointer for the NEXTID file
+               (must be opened appropriately at backend
+               entry and closed on leave  */
+       FILE             *nextidFP;
+
        /*  is the default attribute index set to non-none  */
        int              withDefIDX;
 #define  BDB2_WITH_DEF_IDX          1
@@ -187,9 +191,6 @@ struct ldbminfo {
        Avlnode                 *li_attrs;
        int                     li_dbcachesize;
        int                     li_dbcachewsync;
-       struct dbcache          li_dbcache[MAXDBCACHE];
-       ldap_pvt_thread_mutex_t         li_dbcache_mutex;
-       ldap_pvt_thread_cond_t          li_dbcache_cv;
 
        /*  a list of all files of the database  */
        BDB2_TXN_HEAD           li_txn_head;
index 4344606632c7f27656965e06d32c940bb6526cce..6cd9d31cf7e8d065f6444829cc99c901efcb2423 100644 (file)
@@ -128,26 +128,26 @@ bdb2i_index_read(
        realval = val;
        tmpval = NULL;
        if ( prefix != UNKNOWN_PREFIX ) {
-              unsigned int     len = strlen( val );
+        unsigned int   len = strlen( val );
 
-              if ( (len + 2) < sizeof(buf) ) {
+        if ( (len + 2) < sizeof(buf) ) {
                        realval = buf;
                } else {
                        /* value + prefix + null */
                        tmpval = (char *) ch_malloc( len + 2 );
                        realval = tmpval;
                }
-              realval[0] = prefix;
-              strcpy( &realval[1], val );
+        realval[0] = prefix;
+        strcpy( &realval[1], val );
        }
 
        key.dptr = realval;
        key.dsize = strlen( realval ) + 1;
 
        idl = bdb2i_idl_fetch( be, db, key );
-      if ( tmpval != NULL ) {
-              free( tmpval );
-      }
+    if ( tmpval != NULL ) {
+        free( tmpval );
+    }
 
        bdb2i_cache_close( be, db );
 
@@ -168,7 +168,6 @@ add_value(
 {
        int     rc;
        Datum   key;
-       ID_BLOCK        *idl = NULL;
        char    *tmpval = NULL;
        char    *realval = val;
        char    buf[BUFSIZ];
@@ -180,17 +179,17 @@ add_value(
        Debug( LDAP_DEBUG_TRACE, "=> add_value( \"%c%s\" )\n", prefix, val, 0 );
 
        if ( prefix != UNKNOWN_PREFIX ) {
-              unsigned int     len = strlen( val );
+        unsigned int     len = strlen( val );
 
-              if ( (len + 2) < sizeof(buf) ) {
+        if ( (len + 2) < sizeof(buf) ) {
                        realval = buf;
                } else {
                        /* value + prefix + null */
                        tmpval = (char *) ch_malloc( len + 2 );
                        realval = tmpval;
                }
-              realval[0] = prefix;
-              strcpy( &realval[1], val );
+        realval[0] = prefix;
+        strcpy( &realval[1], val );
        }
 
        key.dptr = realval;
@@ -202,10 +201,6 @@ add_value(
                free( tmpval );
        }
 
-       if( idl != NULL ) {
-               bdb2i_idl_free( idl );
-       }
-
        ldap_pvt_thread_yield();
 
        /* Debug( LDAP_DEBUG_TRACE, "<= add_value %d\n", rc, 0, 0 ); */
index eaa557f9deacde44c7a5c598322b5c3fc89e54f2..5f0c8cd3d1229a3d5af852cc667523883983a50e 100644 (file)
@@ -177,8 +177,6 @@ bdb2i_back_db_init_internal(
        ldap_pvt_thread_mutex_init( &li->li_add_mutex );
        ldap_pvt_thread_mutex_init( &li->li_cache.c_mutex );
        ldap_pvt_thread_mutex_init( &li->li_nextid_mutex );
-       ldap_pvt_thread_mutex_init( &li->li_dbcache_mutex );
-       ldap_pvt_thread_cond_init( &li->li_dbcache_cv );
 
        /*  initialize the TP file head  */
        if ( bdb2i_txn_head_init( &li->li_txn_head ) != 0 )
index 711603a46cbac62b641360da89f2fb10c00d8d7f..129221e31efc9327cd653a0f3de72cf662403e25 100644 (file)
 #include "slap.h"
 #include "back-bdb2.h"
 
+/*  XXX the separate handling of the NEXTID file is in contrast to TP  */
+/*  the NEXTID file is beeing opened during database start-up  */
 static ID
 next_id_read( BackendDB *be )
 {
        struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+       BDB2_TXN_HEAD   *head = &li->li_txn_head;
+       FILE*   fp = head->nextidFP;
        ID      id;
        char    buf[20];
-       char*   file = li->li_nextid_file; 
-       FILE*   fp;
 
-       if ( (fp = fopen( file, "r" )) == NULL ) {
-               Debug( LDAP_DEBUG_ANY,
-                   "next_id_read: could not open \"%s\"\n",
-                   file, 0, 0 );
-               return NOID;
-       }
+       /*  set the file pointer to the beginnig of the file  */
+       rewind( fp );
 
+       /*  read the nextid  */
        if ( fgets( buf, sizeof(buf), fp ) == NULL ) {
                Debug( LDAP_DEBUG_ANY,
                   "next_id_read: could not fgets nextid from \"%s\"\n",
-                   file, 0, 0 );
-               fclose( fp );
+                   li->li_nextid_file, 0, 0 );
                return NOID;
        }
 
        id = atol( buf );
-       fclose( fp );
 
        if(id < 1) {
                Debug( LDAP_DEBUG_ANY,
@@ -50,31 +47,30 @@ next_id_read( BackendDB *be )
        return id;
 }
 
+/*  XXX the separate handling of the NEXTID file is in contrast to TP  */
+/*  the NEXTID file is beeing opened during database start-up  */
 static int
 next_id_write( BackendDB *be, ID id )
 {
        struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+       BDB2_TXN_HEAD   *head = &li->li_txn_head;
+       FILE*   fp = head->nextidFP;
        char    buf[20];
-       char*   file = li->li_nextid_file; 
-       FILE*   fp;
-       int             rc;
-
-       if ( (fp = fopen( file, "w" )) == NULL ) {
-               Debug( LDAP_DEBUG_ANY, "next_id_write(%ld): could not open \"%s\"\n",
-                   id, file, 0 );
-               return -1;
-       } 
+       int             rc = 0;
 
-       rc = 0;
+       /*  set the file pointer to the beginnig of the file  */
+       rewind( fp );
 
+       /*  write the nextid  */
        if ( fprintf( fp, "%ld\n", id ) == EOF ) {
                Debug( LDAP_DEBUG_ANY, "next_id_write(%ld): cannot fprintf\n",
                    id, 0, 0 );
                rc = -1;
        }
 
-       if( fclose( fp ) != 0 ) {
-               Debug( LDAP_DEBUG_ANY, "next_id_write %ld: cannot fclose\n",
+       /*  if forced flushing of files is in effect, do so  */
+       if( li->li_dbcachewsync && ( fflush( fp ) != 0 )) {
+               Debug( LDAP_DEBUG_ANY, "next_id_write %ld: cannot fflush\n",
                    id, 0, 0 );
                rc = -1;
        }
index 07a4b8d7d90eb658b8f8e9f3d76b90e7847363dd..024d3e238c837489213db9afef93233dd8fdcc1f 100644 (file)
@@ -19,7 +19,7 @@ static void remove_old_locks( char *home );
 static void
 bdb2i_db_errcall( char *prefix, char *message )
 {
-       Debug( LDAP_DEBUG_ANY, "dbd2_db_errcall(): %s %s", prefix, message, 0 );
+       Debug( LDAP_DEBUG_ANY, "bdb2_db_errcall(): %s %s", prefix, message, 0 );
 }
 
 
index 493dfa87fc35fe2ebec4570f502d1b386227a4f4..9dc238c4f7a91e77b9bb168d06434b36ffa527ee 100644 (file)
@@ -39,16 +39,10 @@ bdb2i_txn_head_init( BDB2_TXN_HEAD *head )
 static void
 bdb2i_init_db_file_cache( struct ldbminfo *li, BDB2_TXN_FILES *fileinfo )
 {
-       time_t      curtime;
        struct stat st;
        char        buf[MAXPATHLEN];
 
-       ldap_pvt_thread_mutex_lock( &currenttime_mutex );
-       curtime = currenttime;
-       ldap_pvt_thread_mutex_unlock( &currenttime_mutex );
-
        fileinfo->dbc_refcnt = 1;
-       fileinfo->dbc_lastref = curtime;
 
        sprintf( buf, "%s%s%s", li->li_directory, DEFAULT_DIRSEP,
                                        fileinfo->dbc_name );
@@ -66,6 +60,14 @@ bdb2i_init_db_file_cache( struct ldbminfo *li, BDB2_TXN_FILES *fileinfo )
 }
 
 
+/*  create a DB file cache entry for a specified index attribute
+       (if not already done); the function is called during config
+       file read for all index'ed attributes; if "default" index with
+       a non-none selection is given, this is remembered for run-time
+       extension of the list of index files; the function is also
+       called before add or modify operations to check for putative
+       new "default" index files; at that time, files are also opened
+*/
 void
 bdb2i_txn_attr_config(
        struct ldbminfo  *li,
@@ -158,11 +160,45 @@ bdb2i_txn_attr_config(
 }
 
 
+/*  open the NEXTID file for read/write; if it does not exist,
+       create it (access to the file must be preceeded by a rewind)
+*/
+static int
+bdb2i_open_nextid( struct ldbminfo *li )
+{
+       BDB2_TXN_HEAD   *head = &li->li_txn_head;
+       FILE            *fp = NULL;
+       char            *file = li->li_nextid_file;
+
+       /*  try to open the file for read and write  */
+       if ((( fp = fopen( file, "r+" )) == NULL ) &&
+               (( fp = fopen( file, "w+" )) == NULL )) {
+
+                       Debug( LDAP_DEBUG_ANY,
+                               "bdb2i_open_nextid: could not open \"%s\"\n",
+                               file, 0, 0 );
+                       return( -1 );
+
+       }
+
+       /*  the file is open for read/write  */
+       head->nextidFP = fp;
+
+       return( 0 );
+}
+
+
+/*  open all DB during startup of the backend (necessary due to TP)
+       additional files may be opened during slapd life-time due to
+       default indexes (must be configured in slapd.conf;
+       see bdb2i_txn_attr_config)
+*/
 int
 bdb2i_txn_open_files( struct ldbminfo *li )
 {
        BDB2_TXN_HEAD   *head = &li->li_txn_head;
        BDB2_TXN_FILES  *dbFile;
+       int             rc;
 
        for ( dbFile = head->dbFiles; dbFile; dbFile = dbFile->next ) {
                char   fileName[MAXPATHLEN];
@@ -179,7 +215,7 @@ bdb2i_txn_open_files( struct ldbminfo *li )
                        Debug( LDAP_DEBUG_ANY,
                                "bdb2i_txn_open_files(): couldn't open file \"%s\" -- FATAL.\n",
                                dbFile->dbc_name, 0, 0 );
-                       return( 1 );
+                       return( -1 );
 
                }
 
@@ -191,10 +227,22 @@ bdb2i_txn_open_files( struct ldbminfo *li )
 
        }
 
-       return 0;
+       rc = bdb2i_open_nextid( li );
+
+       return rc;
 }
 
 
+/*  close the NEXTID file  */
+static void
+bdb2i_close_nextid( BDB2_TXN_HEAD *head )
+{
+       fclose( head->nextidFP );
+       head->nextidFP = NULL;
+}
+
+
+/*  close all DB files during shutdown of the backend  */
 void
 bdb2i_txn_close_files( BackendDB *be )
 {
@@ -207,9 +255,15 @@ bdb2i_txn_close_files( BackendDB *be )
                ldbm_close( dbFile->dbc_db );
 
        }
+
+       bdb2i_close_nextid( head );
+
 }
 
 
+/*  get the db_cache structure associated with a specified
+       DB file (replaces the on-the-fly opening of files in cache_open()
+*/
 BDB2_TXN_FILES *
 bdb2i_get_db_file_cache( struct ldbminfo *li, char *name )
 {
index aea2c7481b5a1da5b720867213357aea7e88ce0f..b0626ca9d6eb9e4e8f190c01a443c3d27a27b301 100644 (file)
@@ -4,7 +4,32 @@
 ## tests Makefile.in for OpenLDAP
 BUILD_BDB2 = @BUILD_BDB2@
 
-bdb2-local:    FORCE
+SRC      = slapd-tester.c slapd-search.c
+PROGRAMS = slapd-tester slapd-search slapd-read slapd-addel
+
+LDAP_INCDIR= ../include
+LDAP_LIBDIR= ../libraries
+
+XLIBS    = -lldap_r -llber -llutil
+
+build-tools: FORCE
+       $(MAKE) $(MFLAGS) load-tools
+
+load-tools: $(PROGRAMS)
+
+slapd-tester: slapd-tester.o 
+       $(LTLINK) -o $@ slapd-tester.o
+
+slapd-search: slapd-search.o $(LDAP_LIBLBER_DEPEND) $(LDAP_LIBLDAP_DEPEND)
+       $(LTLINK) -o $@ slapd-search.o $(LDAP_LIBPATH) $(XLIBS)
+
+slapd-read: slapd-read.o $(LDAP_LIBLBER_DEPEND) $(LDAP_LIBLDAP_DEPEND)
+       $(LTLINK) -o $@ slapd-read.o $(LDAP_LIBPATH) $(XLIBS)
+
+slapd-addel: slapd-addel.o $(LDAP_LIBLBER_DEPEND) $(LDAP_LIBLDAP_DEPEND)
+       $(LTLINK) -o $@ slapd-addel.o $(LDAP_LIBPATH) $(XLIBS)
+
+bdb2-local:    build-tools FORCE
        @if test "$(BUILD_BDB2)" = "yes" ; then \
                $(LN_S) $(srcdir)/data . ; \
                echo "Initiating LDAP tests..." ; \
@@ -14,7 +39,7 @@ bdb2-local:   FORCE
                echo "run configure with --enable-bdb2" ; \
        fi
 
-all-local:     FORCE
+all-local:     build-tools FORCE
        @-$(LN_S) $(srcdir)/data .
        @echo "Initiating LDAP tests..."; \
        $(MKDIR) test-db test-repl ; \
@@ -22,8 +47,12 @@ all-local:   FORCE
 
 clean-local:   FORCE
        $(RM) test-db/[!C]* test-repl/[!C]* *core
+       $(RM) $(PROGRAMS)
+       $(RM) *.o
 
 veryclean-local: FORCE
        @-$(RM) data
        $(RM) -r test-db test-repl
+       $(RM) $(PROGRAMS)
+       $(RM) *.o
 
diff --git a/tests/data/do_add.1 b/tests/data/do_add.1
new file mode 100644 (file)
index 0000000..8a57d95
--- /dev/null
@@ -0,0 +1,26 @@
+cn=James A Jones 2, ou=Alumni Association, ou=People, o=University of Michigan, c=US
+objectclass: top
+objectclass: person
+objectclass: organizationalPerson
+objectclass: newPilotPerson
+objectclass: umichPerson
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+postaladdress: Alumni Association $ 111 Maple St $ Ann Arbor, MI 48109
+seealso: cn=All Staff, ou=Groups, o=University of Michigan, c=US
+uid: jaj
+krbname: jaj@umich.edu
+userpassword: jaj
+nobatchupdates: TRUE
+onvacation: FALSE
+homepostaladdress: 3882 Beverly Rd. $ Ann Arbor, MI 48105
+homephone: +1 313 555 4772
+multilinedescription: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.umich.edu
+facsimiletelephonenumber: +1 313 555 4332
+telephonenumber: +1 313 555 0895
+
diff --git a/tests/data/do_read.0 b/tests/data/do_read.0
new file mode 100644 (file)
index 0000000..25dc676
--- /dev/null
@@ -0,0 +1,5 @@
+cn=Barbara Jensen, ou=Information Technology Division, ou=People, o=University of Michigan, c=US
+cn=ITD Staff,ou=Groups,o=University of Michigan,c=US
+ou=Groups, o=University of Michigan, c=US
+ou=Alumni Association, ou=People, o=University of Michigan, c=US
+cn=James A Jones 1, ou=Alumni Association, ou=People, o=University of Michigan, c=US
diff --git a/tests/data/do_search.0 b/tests/data/do_search.0
new file mode 100644 (file)
index 0000000..6e9cd1f
--- /dev/null
@@ -0,0 +1,5 @@
+cn=Barbara Jensen
+cn=Bjorn Jensen
+cn=James A Jones 1
+cn=Bjorn Jensen
+cn=Alumni Assoc Staff
index 53e6a91f2d8cd6584392e46b6b8d9829024440cf..2dad68bfc0e294c0a6a380910511bcc015aba24e 100644 (file)
@@ -13,6 +13,7 @@ argsfile    ./test-db/slapd.args
 
 backend                bdb2
 home           ./test-db
+mpoolsize      2100000
 
 database       bdb2
 cachesize      4
index 50c8497c45f3ba73630c3ae430d062d49e8db944..b77c8f47a1ef611e987735b8d27051eb2bcf083a 100755 (executable)
@@ -31,6 +31,7 @@ SLURPD=../servers/slurpd/slurpd
 LDAPSEARCH=../clients/tools/ldapsearch
 LDAPMODIFY=../clients/tools/ldapmodify
 LDAPADD=../clients/tools/ldapadd
+SLAPDTESTER=slapd-tester
 LVL=5
 PORT=9009
 SLAVEPORT=9010
diff --git a/tests/scripts/test008-concurrency b/tests/scripts/test008-concurrency
new file mode 100755 (executable)
index 0000000..815cdaf
--- /dev/null
@@ -0,0 +1,68 @@
+#!/bin/sh
+
+if [ $# -eq 0 ]; then
+       SRCDIR="."
+else
+       SRCDIR=$1; shift
+fi
+if [ $# -eq 1 ]; then
+       BDB2=$1; shift
+fi
+
+echo "running defines.sh $SRCDIR $BDB2"
+
+. $SRCDIR/scripts/defines.sh $SRCDIR $BDB2
+
+echo "Datadir is $DATADIR"
+
+echo "Cleaning up in $DBDIR..."
+
+rm -f $DBDIR/[!C]*
+
+echo "Running ldif2ldbm to build slapd database..."
+$LDIF2LDBM -f $CONF -i $LDIF -e ../servers/slapd/tools
+RC=$?
+if [ $RC != 0 ]; then
+       echo "ldif2ldbm failed!"
+       exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT..."
+$SLAPD -f $CONF -p $PORT -d $LVL $TIMING > $MASTERLOG 2>&1 &
+PID=$!
+
+echo "Waiting 5 seconds for slapd to start..."
+sleep 5
+
+echo "Using tester for concurrent server access..."
+$SLAPDTESTER -b "$BASEDN" -d "$DATADIR" -h localhost -p $PORT -D "$MANAGERDN" -w $PASSWD -l 100
+RC=$?
+
+if [ $RC != 0 ]; then
+       echo "slapd-tester failed!"
+       exit $RC
+fi 
+
+echo "Using ldapsearch to retrieve all the entries..."
+$LDAPSEARCH -L -S "" -b "$BASEDN" -h localhost -p $PORT \
+                       'objectClass=*' > $SEARCHOUT 2>&1
+RC=$?
+
+kill -HUP $PID
+
+if [ $RC != 0 ]; then
+       echo "ldapsearch failed!"
+       exit $RC
+fi
+
+echo "Comparing retrieved entries to LDIF file used to create database"
+cmp $SEARCHOUT $LDIF
+if [ $? != 0 ]; then
+       echo "comparison failed - database was not created correctly"
+       exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+
+exit 0
diff --git a/tests/slapd-addel.c b/tests/slapd-addel.c
new file mode 100644 (file)
index 0000000..a6c7238
--- /dev/null
@@ -0,0 +1,276 @@
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/ctype.h>
+#include <ac/socket.h>
+#include <ac/unistd.h>
+#include <ac/wait.h>
+
+#include <sys/param.h>
+
+#include "lber.h"
+#include "ldap.h"
+
+#define LOOPS  100
+
+static char *
+get_add_entry( char *filename, LDAPMod ***mods );
+
+static void
+do_addel( char *host, int port, char *manager, char *passwd,
+       char *dn, LDAPMod **attrs, int maxloop );
+
+static void
+usage( char *name )
+{
+       fprintf( stderr, "usage: %s [-h <host>] -p port -D <managerDN> -w <passwd> -f <addfile> [-l <loops>]\n",
+                       name );
+       exit( 1 );
+}
+
+int
+main( int argc, char **argv )
+{
+       int             i, j;
+       char        *host = "localhost";
+       int                     port = -1;
+       char            *manager = NULL;
+       char            *passwd = NULL;
+       char            *filename = NULL;
+       char            *entry = NULL;
+       int                     loops = LOOPS;
+       LDAPMod     **attrs = NULL;
+
+       while ( (i = getopt( argc, argv, "h:p:D:w:f:l:" )) != EOF ) {
+               switch( i ) {
+                       case 'h':               /* the servers host */
+                               host = strdup( optarg );
+                       break;
+
+                       case 'p':               /* the servers port */
+                               port = atoi( optarg );
+                               break;
+
+                       case 'D':               /* the servers manager */
+                               manager = strdup( optarg );
+                       break;
+
+                       case 'w':               /* the server managers password */
+                               passwd = strdup( optarg );
+                       break;
+
+                       case 'f':               /* file with entry search request */
+                               filename = strdup( optarg );
+                               break;
+
+                       case 'l':               /* the number of loops */
+                               loops = atoi( optarg );
+                               break;
+
+                       default:
+                               usage( argv[0] );
+                               break;
+               }
+       }
+
+       if (( filename == NULL ) || ( port == -1 ) ||
+                               ( manager == NULL ) || ( passwd == NULL ))
+               usage( argv[0] );
+
+       entry = get_add_entry( filename, &attrs );
+       if (( entry == NULL ) || ( *entry == '\0' )) {
+
+               fprintf( stderr, "%s: invalid entry DN in file \"%s\".\n",
+                               argv[0], filename );
+               exit( 1 );
+
+       }
+
+       if (( attrs == NULL ) || ( *attrs == '\0' )) {
+
+               fprintf( stderr, "%s: invalid attrs in file \"%s\".\n",
+                               argv[0], filename );
+               exit( 1 );
+
+       }
+
+       do_addel( host, port, manager, passwd, entry, attrs, loops );
+
+       exit( 0 );
+}
+
+
+#define safe_realloc( ptr, size )   ( ptr == NULL ? malloc( size ) : \
+                               realloc( ptr, size ))
+
+
+static void
+addmodifyop( LDAPMod ***pmodsp, int modop, char *attr, char *value, int vlen )
+{
+    LDAPMod            **pmods;
+    int                        i, j;
+    struct berval      *bvp;
+
+    pmods = *pmodsp;
+    modop |= LDAP_MOD_BVALUES;
+
+    i = 0;
+    if ( pmods != NULL ) {
+               for ( ; pmods[ i ] != NULL; ++i ) {
+               if ( strcasecmp( pmods[ i ]->mod_type, attr ) == 0 &&
+                       pmods[ i ]->mod_op == modop ) {
+                               break;
+               }
+               }
+    }
+
+    if ( pmods == NULL || pmods[ i ] == NULL ) {
+               if (( pmods = (LDAPMod **)safe_realloc( pmods, (i + 2) *
+                       sizeof( LDAPMod * ))) == NULL ) {
+                       perror( "safe_realloc" );
+                       exit( 1 );
+               }
+               *pmodsp = pmods;
+               pmods[ i + 1 ] = NULL;
+               if (( pmods[ i ] = (LDAPMod *)calloc( 1, sizeof( LDAPMod )))
+                       == NULL ) {
+                       perror( "calloc" );
+                       exit( 1 );
+               }
+               pmods[ i ]->mod_op = modop;
+               if (( pmods[ i ]->mod_type = strdup( attr )) == NULL ) {
+               perror( "strdup" );
+               exit( 1 );
+               }
+    }
+
+    if ( value != NULL ) {
+               j = 0;
+               if ( pmods[ i ]->mod_bvalues != NULL ) {
+               for ( ; pmods[ i ]->mod_bvalues[ j ] != NULL; ++j ) {
+                               ;
+               }
+               }
+               if (( pmods[ i ]->mod_bvalues =
+                       (struct berval **)safe_realloc( pmods[ i ]->mod_bvalues,
+                       (j + 2) * sizeof( struct berval * ))) == NULL ) {
+                       perror( "safe_realloc" );
+                       exit( 1 );
+               }
+               pmods[ i ]->mod_bvalues[ j + 1 ] = NULL;
+               if (( bvp = (struct berval *)malloc( sizeof( struct berval )))
+                       == NULL ) {
+                       perror( "malloc" );
+                       exit( 1 );
+               }
+               pmods[ i ]->mod_bvalues[ j ] = bvp;
+
+           bvp->bv_len = vlen;
+           if (( bvp->bv_val = (char *)malloc( vlen + 1 )) == NULL ) {
+                       perror( "malloc" );
+                       exit( 1 );
+           }
+           SAFEMEMCPY( bvp->bv_val, value, vlen );
+           bvp->bv_val[ vlen ] = '\0';
+    }
+}
+
+
+static char *
+get_add_entry( char *filename, LDAPMod ***mods )
+{
+       FILE    *fp;
+       char    *entry = NULL;
+
+       if ( fp = fopen( filename, "r" )) {
+               char  line[BUFSIZ];
+
+               if ( fgets( line, BUFSIZ, fp )) {
+                       char *nl;
+
+                       if (( nl = strchr( line, '\r' )) || ( nl = strchr( line, '\n' )))
+                               *nl = '\0';
+                       entry = strdup( line );
+
+               }
+
+               while ( fgets( line, BUFSIZ, fp )) {
+                       char    *nl;
+                       char    *value;
+
+                       if (( nl = strchr( line, '\r' )) || ( nl = strchr( line, '\n' )))
+                               *nl = '\0';
+
+                       if ( *line == '\0' ) break;
+                       if ( !( value = strchr( line, ':' ))) break;
+
+                       *value++ = '\0'; 
+                       while ( *value && isblank( *value )) value++;
+
+                       addmodifyop( mods, LDAP_MOD_ADD, line, value, strlen( value ));
+
+               }
+               fclose( fp );
+       }
+
+       return( entry );
+}
+
+
+static void
+do_addel(
+       char *host,
+       int port,
+       char *manager,
+       char *passwd,
+       char *entry,
+       LDAPMod **attrs,
+       int maxloop
+)
+{
+       LDAP    *ld;
+       int     i;
+
+       if (( ld = ldap_init( host, port )) == NULL ) {
+               perror( "ldap_init" );
+               exit( 1 );
+       }
+
+       if ( ldap_bind_s( ld, manager, passwd, LDAP_AUTH_SIMPLE )
+                               != LDAP_SUCCESS ) {
+               ldap_perror( ld, "ldap_bind" );
+                exit( 1 );
+       }
+
+
+       fprintf( stderr, "Add/Delete(%d): entry=\"%s\".\n", maxloop, entry );
+
+       for ( i = 0; i < maxloop; i++ ) {
+
+               /* add the entry */
+               if ( ldap_add_s( ld, entry, attrs ) != LDAP_SUCCESS ) {
+
+                       ldap_perror( ld, "ldap_add" );
+                       break;
+
+               }
+
+               /* wait a second for the add to really complete */
+               sleep( 1 );
+
+               /* now delete the entry again */
+               if ( ldap_delete_s( ld, entry ) != LDAP_SUCCESS ) {
+
+                       ldap_perror( ld, "ldap_delete" );
+                       break;
+
+               }
+
+       }
+
+       ldap_unbind( ld );
+}
+
+
diff --git a/tests/slapd-read.c b/tests/slapd-read.c
new file mode 100644 (file)
index 0000000..da7c9df
--- /dev/null
@@ -0,0 +1,117 @@
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/ctype.h>
+#include <ac/socket.h>
+#include <ac/unistd.h>
+#include <ac/wait.h>
+
+#include <sys/param.h>
+
+#include "lber.h"
+#include "ldap.h"
+
+#define LOOPS  100
+
+static void
+do_read( char *host, int port, char *entry, int maxloop );
+
+static void
+usage( char *name )
+{
+       fprintf( stderr, "usage: %s [-h <host>] -p port -e <entry> [-l <loops>]\n",
+                       name );
+       exit( 1 );
+}
+
+int
+main( int argc, char **argv )
+{
+       int             i, j;
+       char        *host = "localhost";
+       int                     port = -1;
+       char            *entry = NULL;
+       int                     loops = LOOPS;
+
+       while ( (i = getopt( argc, argv, "h:p:e:l:" )) != EOF ) {
+               switch( i ) {
+                       case 'h':               /* the servers host */
+                               host = strdup( optarg );
+                       break;
+
+                       case 'p':               /* the servers port */
+                               port = atoi( optarg );
+                               break;
+
+                       case 'e':               /* file with entry search request */
+                               entry = strdup( optarg );
+                               break;
+
+                       case 'l':               /* the number of loops */
+                               loops = atoi( optarg );
+                               break;
+
+                       default:
+                               usage( argv[0] );
+                               break;
+               }
+       }
+
+       if (( entry == NULL ) || ( port == -1 ))
+               usage( argv[0] );
+
+       if ( *entry == '\0' ) {
+
+               fprintf( stderr, "%s: invalid EMPTY entry DN.\n",
+                               argv[0] );
+               exit( 1 );
+
+       }
+
+       do_read( host, port, entry, loops );
+
+       exit( 0 );
+}
+
+
+static void
+do_read( char *host, int port, char *entry, int maxloop )
+{
+       LDAP    *ld;
+       int     i;
+       char    *attrs[] = { "cn", "sn", NULL };
+       char    *filter = "(objectclass=*)";
+
+       if (( ld = ldap_init( host, port )) == NULL ) {
+               perror( "ldap_init" );
+               exit( 1 );
+       }
+
+       if ( ldap_bind_s( ld, NULL, NULL, LDAP_AUTH_SIMPLE ) != LDAP_SUCCESS ) {
+               ldap_perror( ld, "ldap_bind" );
+                exit( 1 );
+       }
+
+
+       fprintf( stderr, "Read(%d): entry=\"%s\".\n", maxloop, entry );
+
+       for ( i = 0; i < maxloop; i++ ) {
+                LDAPMessage *res;
+
+               if ( ldap_search_s( ld, entry, LDAP_SCOPE_BASE,
+                               filter, attrs, 0, &res ) != LDAP_SUCCESS ) {
+
+                       ldap_perror( ld, "ldap_read" );
+                       break;
+
+               }
+
+               ldap_msgfree( res );
+       }
+
+       ldap_unbind( ld );
+}
+
+
diff --git a/tests/slapd-search.c b/tests/slapd-search.c
new file mode 100644 (file)
index 0000000..559e3bc
--- /dev/null
@@ -0,0 +1,122 @@
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/ctype.h>
+#include <ac/socket.h>
+#include <ac/unistd.h>
+#include <ac/wait.h>
+
+#include <sys/param.h>
+
+#include "lber.h"
+#include "ldap.h"
+
+#define LOOPS  100
+
+static void
+do_search( char *host, int port, char *sbase, char *filter, int maxloop );
+
+static void
+usage( char *name )
+{
+       fprintf( stderr, "usage: %s [-h <host>] -p port -b <searchbase> -f <searchfiter> [-l <loops>]\n",
+                       name );
+       exit( 1 );
+}
+
+int
+main( int argc, char **argv )
+{
+       int             i, j;
+       char        *host = "localhost";
+       int                     port = -1;
+       char        *sbase = NULL;
+       char            *filter  = NULL;
+       int                     loops = LOOPS;
+
+       while ( (i = getopt( argc, argv, "h:p:b:f:l:" )) != EOF ) {
+               switch( i ) {
+                       case 'h':               /* the servers host */
+                               host = strdup( optarg );
+                       break;
+
+                       case 'p':               /* the servers port */
+                               port = atoi( optarg );
+                               break;
+
+                       case 'b':               /* file with search base */
+                               sbase = strdup( optarg );
+                       break;
+
+                       case 'f':               /* the search request */
+                               filter = strdup( optarg );
+                               break;
+
+                       case 'l':               /* number of loops */
+                               loops = atoi( optarg );
+                               break;
+
+                       default:
+                               usage( argv[0] );
+                               break;
+               }
+       }
+
+       if (( sbase == NULL ) || ( filter == NULL ) || ( port == -1 ))
+               usage( argv[0] );
+
+       if ( *filter == '\0' ) {
+
+               fprintf( stderr, "%s: invalid EMPTY search filter.\n",
+                               argv[0] );
+               exit( 1 );
+
+       }
+
+       do_search( host, port, sbase, filter, loops );
+
+       exit( 0 );
+}
+
+
+static void
+do_search( char *host, int port, char *sbase, char *filter, int maxloop )
+{
+       LDAP    *ld;
+       int     i;
+       char    *attrs[] = { "cn", "sn", NULL };
+
+       if (( ld = ldap_init( host, port )) == NULL ) {
+               perror( "ldap_init" );
+               exit( 1 );
+       }
+
+       if ( ldap_bind_s( ld, NULL, NULL, LDAP_AUTH_SIMPLE ) != LDAP_SUCCESS ) {
+               ldap_perror( ld, "ldap_bind" );
+                exit( 1 );
+       }
+
+
+       fprintf( stderr, "Search(%d): base=\"%s\", filter=\"%s\".\n",
+               maxloop, sbase, filter );
+
+       for ( i = 0; i < maxloop; i++ ) {
+                LDAPMessage *res;
+
+               if ( ldap_search_s( ld, sbase, LDAP_SCOPE_SUBTREE,
+                               filter, attrs, 0, &res ) != LDAP_SUCCESS ) {
+
+                       ldap_perror( ld, "ldap_search" );
+                       break;
+
+               }
+
+               ldap_msgfree( res );
+       }
+
+       ldap_unbind( ld );
+}
+
+
diff --git a/tests/slapd-tester.c b/tests/slapd-tester.c
new file mode 100644 (file)
index 0000000..5bf9d4e
--- /dev/null
@@ -0,0 +1,352 @@
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/ctype.h>
+#include <ac/socket.h>
+#include <ac/unistd.h>
+#include <ac/wait.h>
+#include <dirent.h>
+
+#include <sys/param.h>
+
+#include "ldapconfig.h"
+
+
+#define SEARCHCMD              "slapd-search"
+#define READCMD                        "slapd-read"
+#define ADDCMD                 "slapd-addel"
+#define MAXARGS        100
+#define MAXREQS                        20
+#define LOOPS                  "100"
+
+#define TSEARCHFILE            "do_search.0"
+#define TREADFILE              "do_read.0"
+#define TADDFILE               "do_add."
+
+static char *get_file_name( char *dirname, char *filename );
+static int  get_search_filters( char *filename, char *filters[] );
+static int  get_read_entries( char *filename, char *entries[] );
+static void fork_child( char *prog, char *args[] );
+static void    wait4kids( int nkidval );
+
+static int      maxkids = 20;
+static int      nkids;
+
+static void
+usage( char *name )
+{
+       fprintf( stderr, "usage: %s [-h <host>] -p <port> -D <manager> -w <passwd> -d <datadir> -b <baseDN> [-j <maxchild>] [-l <loops>]\n", name );
+       exit( 1 );
+}
+
+int
+main( int argc, char **argv )
+{
+       int             i, j;
+       char            *host = "localhost";
+       char            *port = NULL;
+       char            *manager = NULL;
+       char            *passwd = NULL;
+       char            *dirname = NULL;
+       char        *sbase = NULL;
+       char            *loops = LOOPS;
+       DIR                     *datadir;
+       struct dirent   *file;
+       char            *sfile = NULL;
+       char            *sreqs[MAXREQS];
+       int         snum = 0;
+       char            *rfile = NULL;
+       char            *rreqs[MAXREQS];
+       int         rnum = 0;
+       char            *afiles[MAXREQS];
+       int         anum = 0;
+       char            *sargs[MAXARGS];
+       int                     sanum;
+       char            scmd[MAXPATHLEN];
+       char            *rargs[MAXARGS];
+       int                     ranum;
+       char            rcmd[MAXPATHLEN];
+       char            *aargs[MAXARGS];
+       int                     aanum;
+       char            acmd[MAXPATHLEN];
+
+       while ( (i = getopt( argc, argv, "h:p:D:w:b:d:j:l:" )) != EOF ) {
+               switch( i ) {
+                       case 'h':               /* slapd host */
+                               host = strdup( optarg );
+                       break;
+
+                       case 'p':               /* the servers port number */
+                               port = strdup( optarg );
+                               break;
+
+                       case 'D':               /* slapd manager */
+                               manager = strdup( optarg );
+                       break;
+
+                       case 'w':               /* the managers passwd */
+                               passwd = strdup( optarg );
+                               break;
+
+                       case 'b':               /* the base DN */
+                               sbase = strdup( optarg );
+                               break;
+
+                       case 'd':               /* data directory */
+                               dirname = strdup( optarg );
+                       break;
+
+                       case 'j':               /* the number of parallel clients */
+                               maxkids = atoi( optarg );
+                               break;
+
+                       case 'l':               /* the number of loops per client */
+                               loops = strdup( optarg );
+                               break;
+
+                       default:
+                               usage( argv[0] );
+                               break;
+               }
+       }
+
+       if (( dirname == NULL ) || ( sbase == NULL ) || ( port == NULL ) ||
+                       ( manager == NULL ) || ( passwd == NULL ))
+               usage( argv[0] );
+
+       /* get the file list */
+       if ( ( datadir = opendir( dirname )) == NULL ) {
+
+               fprintf( stderr, "%s: couldn't open data directory \"%s\".\n",
+                                       argv[0], dirname );
+               exit( 1 );
+
+       }
+
+       /*  look for search, read, and add/delete files */
+       for ( file = readdir( datadir ); file; file = readdir( datadir )) {
+
+               if ( !strcasecmp( file->d_name, TSEARCHFILE )) {
+                       sfile = get_file_name( dirname, file->d_name );
+                       continue;
+               } else if ( !strcasecmp( file->d_name, TREADFILE )) {
+                       rfile = get_file_name( dirname, file->d_name );
+                       continue;
+               } else if ( !strncasecmp( file->d_name, TADDFILE, strlen( TADDFILE ))
+                       && ( anum < MAXREQS )) {
+                       afiles[anum++] = get_file_name( dirname, file->d_name );
+                       continue;
+               }
+       }
+
+       /* look for search requests */
+       if ( sfile ) {
+               snum = get_search_filters( sfile, sreqs );
+       }
+
+       /* look for read requests */
+       if ( rfile ) {
+               rnum = get_read_entries( rfile, rreqs );
+       }
+
+       /*
+        * generate the search clients
+        */
+
+       sanum = 0;
+       sprintf( scmd, "%s", SEARCHCMD );
+       sargs[sanum++] = scmd;
+       sargs[sanum++] = "-h";
+       sargs[sanum++] = host;
+       sargs[sanum++] = "-p";
+       sargs[sanum++] = port;
+       sargs[sanum++] = "-b";
+       sargs[sanum++] = sbase;
+       sargs[sanum++] = "-l";
+       sargs[sanum++] = loops;
+       sargs[sanum++] = "-f";
+       sargs[sanum++] = NULL;          /* will hold the search request */
+       sargs[sanum++] = NULL;
+
+       /*
+        * generate the read clients
+        */
+
+       ranum = 0;
+       sprintf( rcmd, "%s", READCMD );
+       rargs[ranum++] = rcmd;
+       rargs[ranum++] = "-h";
+       rargs[ranum++] = host;
+       rargs[ranum++] = "-p";
+       rargs[ranum++] = port;
+       rargs[ranum++] = "-l";
+       rargs[ranum++] = loops;
+       rargs[ranum++] = "-e";
+       rargs[ranum++] = NULL;          /* will hold the read entry */
+       rargs[ranum++] = NULL;
+
+       /*
+        * generate the add/delete clients
+        */
+
+       aanum = 0;
+       sprintf( acmd, "%s", ADDCMD );
+       aargs[aanum++] = acmd;
+       aargs[aanum++] = "-h";
+       aargs[aanum++] = host;
+       aargs[aanum++] = "-p";
+       aargs[aanum++] = port;
+       aargs[aanum++] = "-D";
+       aargs[aanum++] = manager;
+       aargs[aanum++] = "-w";
+       aargs[aanum++] = passwd;
+       aargs[aanum++] = "-l";
+       aargs[aanum++] = loops;
+       aargs[aanum++] = "-f";
+       aargs[aanum++] = NULL;          /* will hold the add data file */
+       aargs[aanum++] = NULL;
+
+       for ( j = 0; j < MAXREQS; j++ ) {
+
+               if ( j < snum ) {
+
+                       sargs[sanum - 2] = sreqs[j];
+                       fork_child( scmd, sargs );
+
+               }
+
+               if ( j < rnum ) {
+
+                       rargs[ranum - 2] = rreqs[j];
+                       fork_child( rcmd, rargs );
+
+               }
+
+               if ( j < anum ) {
+
+                       aargs[aanum - 2] = afiles[j];
+                       fork_child( acmd, aargs );
+
+               }
+
+       }
+
+       wait4kids( -1 );
+
+       exit( 0 );
+}
+
+static char *
+get_file_name( char *dirname, char *filename )
+{
+       char buf[MAXPATHLEN];
+
+       sprintf( buf, "%s%s%s", dirname, DEFAULT_DIRSEP, filename );
+       return( strdup( buf ));
+}
+
+
+static int
+get_search_filters( char *filename, char *filters[] )
+{
+       FILE    *fp;
+       int     filter = 0;
+
+       if ( fp = fopen( filename, "r" )) {
+               char  line[BUFSIZ];
+
+               while (( filter < MAXREQS ) && ( fgets( line, BUFSIZ, fp ))) {
+                       char *nl;
+
+                       if (( nl = strchr( line, '\r' )) || ( nl = strchr( line, '\n' )))
+                               *nl = '\0';
+                       filters[filter++] = strdup( line );
+
+               }
+               fclose( fp );
+       }
+
+       return( filter );
+}
+
+
+static int
+get_read_entries( char *filename, char *entries[] )
+{
+       FILE    *fp;
+       int     entry = 0;
+
+       if ( fp = fopen( filename, "r" )) {
+               char  line[BUFSIZ];
+
+               while (( entry < MAXREQS ) && ( fgets( line, BUFSIZ, fp ))) {
+                       char *nl;
+
+                       if (( nl = strchr( line, '\r' )) || ( nl = strchr( line, '\n' )))
+                               *nl = '\0';
+                       entries[entry++] = strdup( line );
+
+               }
+               fclose( fp );
+       }
+
+       return( entry );
+}
+
+
+static void
+fork_child( char *prog, char *args[] )
+{
+       int     status, pid;
+
+       wait4kids( maxkids );
+
+       switch ( pid = fork() ) {
+       case 0:         /* child */
+               execvp( prog, args );
+               fprintf( stderr, "%s: ", prog );
+               perror( "execv" );
+               exit( -1 );
+               break;
+
+       case -1:        /* trouble */
+               fprintf( stderr, "Could not fork to run %s\n", prog );
+               perror( "fork" );
+               break;
+
+       default:        /* parent */
+               nkids++;
+               break;
+       }
+}
+
+static void
+wait4kids( int nkidval )
+{
+       int             status;
+       unsigned char   *p;
+
+       while ( nkids >= nkidval ) {
+               wait( &status );
+               p = (unsigned char *) &status;
+               if ( p[sizeof(int) - 1] == 0177 ) {
+                       fprintf( stderr,
+                           "stopping: child stopped with signal %d\n",
+                           p[sizeof(int) - 2] );
+               } else if ( p[sizeof(int) - 1] != 0 ) {
+                       fprintf( stderr, 
+                           "stopping: child terminated with signal %d\n",
+                           p[sizeof(int) - 1] );
+                       exit( p[sizeof(int) - 1] );
+               } else if ( p[sizeof(int) - 2] != 0 ) {
+                       fprintf( stderr, 
+                           "stopping: child exited with status %d\n",
+                           p[sizeof(int) - 2] );
+                       exit( p[sizeof(int) - 2] );
+               } else {
+                       nkids--;
+               }
+       }
+}