correctness of locking schemas in backends.
In back-bdb2 open NEXTID during startup and close at shutdown.
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
int dbc_refcnt;
int dbc_maxids;
int dbc_maxindirect;
- time_t dbc_lastref;
long dbc_blksize;
char *dbc_name;
LDBM dbc_db;
#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
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;
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 );
{
int rc;
Datum key;
- ID_BLOCK *idl = NULL;
char *tmpval = NULL;
char *realval = val;
char buf[BUFSIZ];
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;
free( tmpval );
}
- if( idl != NULL ) {
- bdb2i_idl_free( idl );
- }
-
ldap_pvt_thread_yield();
/* Debug( LDAP_DEBUG_TRACE, "<= add_value %d\n", rc, 0, 0 ); */
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 )
#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,
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;
}
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 );
}
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( ¤ttime_mutex );
- curtime = currenttime;
- ldap_pvt_thread_mutex_unlock( ¤ttime_mutex );
-
fileinfo->dbc_refcnt = 1;
- fileinfo->dbc_lastref = curtime;
sprintf( buf, "%s%s%s", li->li_directory, DEFAULT_DIRSEP,
fileinfo->dbc_name );
}
+/* 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,
}
+/* 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];
Debug( LDAP_DEBUG_ANY,
"bdb2i_txn_open_files(): couldn't open file \"%s\" -- FATAL.\n",
dbFile->dbc_name, 0, 0 );
- return( 1 );
+ return( -1 );
}
}
- 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 )
{
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 )
{
## 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..." ; \
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 ; \
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
--- /dev/null
+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
+
--- /dev/null
+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
--- /dev/null
+cn=Barbara Jensen
+cn=Bjorn Jensen
+cn=James A Jones 1
+cn=Bjorn Jensen
+cn=Alumni Assoc Staff
backend bdb2
home ./test-db
+mpoolsize 2100000
database bdb2
cachesize 4
LDAPSEARCH=../clients/tools/ldapsearch
LDAPMODIFY=../clients/tools/ldapmodify
LDAPADD=../clients/tools/ldapadd
+SLAPDTESTER=slapd-tester
LVL=5
PORT=9009
SLAVEPORT=9010
--- /dev/null
+#!/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
--- /dev/null
+#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 );
+}
+
+
--- /dev/null
+#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 );
+}
+
+
--- /dev/null
+#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 );
+}
+
+
--- /dev/null
+#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--;
+ }
+ }
+}