[Fedora-directory-commits] ldapserver/ldap/servers/slapd/tools/rsearch Makefile, NONE, 1.1 addthread.c, NONE, 1.1 addthread.h, NONE, 1.1 infadd.c, NONE, 1.1 infadd.h, NONE, 1.1 main.c, NONE, 1.1 nametable.c, NONE, 1.1 nametable.h, NONE, 1.1 rsearch.c, NONE, 1.1 rsearch.h, NONE, 1.1 sdattable.c, NONE, 1.1 sdattable.h, NONE, 1.1 searchthread.c, NONE, 1.1 searchthread.h, NONE, 1.1

Noriko Hosoi (nhosoi) fedora-directory-commits at redhat.com
Fri Jan 6 00:53:31 UTC 2006


Author: nhosoi

Update of /cvs/dirsec/ldapserver/ldap/servers/slapd/tools/rsearch
In directory cvs-int.fedora.redhat.com:/tmp/cvs-serv12072/rsearch

Added Files:
	Makefile addthread.c addthread.h infadd.c infadd.h main.c 
	nametable.c nametable.h rsearch.c rsearch.h sdattable.c 
	sdattable.h searchthread.c searchthread.h 
Log Message:
[170348] RSEARCH needs to be updated
Integrating rsearch and infadd source codes from DSRK into the Directory Server build tree.



--- NEW FILE Makefile ---
#
# BEGIN COPYRIGHT BLOCK
# This Program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; version 2 of the License.
# 
# This Program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License along with
# this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
# Place, Suite 330, Boston, MA 02111-1307 USA.
# 
# In addition, as a special exception, Red Hat, Inc. gives You the additional
# right to link the code of this Program with code not covered under the GNU
# General Public License ("Non-GPL Code") and to distribute linked combinations
# including the two, subject to the limitations in this paragraph. Non-GPL Code
# permitted under this exception must only link to the code of this Program
# through those well defined interfaces identified in the file named EXCEPTION
# found in the source code files (the "Approved Interfaces"). The files of
# Non-GPL Code may instantiate templates or use macros or inline functions from
# the Approved Interfaces without causing the resulting work to be covered by
# the GNU General Public License. Only Red Hat, Inc. may make changes or
# additions to the list of Approved Interfaces. You must obey the GNU General
# Public License in all respects for all of the Program code and other code used
# in conjunction with the Program except the Non-GPL Code covered by this
# exception. If you modify this file, you may extend this exception to your
# version of the file, but you are not obligated to do so. If you do not wish to
# provide this exception without modification, you must delete this exception
# statement from your version and license this file solely under the GPL without
# exception. 
# 
# 
# Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
# Copyright (C) 2005 Red Hat, Inc.
# All rights reserved.
# END COPYRIGHT BLOCK
#
#
# gnu makefile for LDAP Server tools.
#

LDAP_SRC = ../../../..
BUILD_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)/servers/tools/obj
BINDIR = $(RELDIR)/shared/bin
DATDIR = $(RELDIR)/shared/data

SLAPD_OBJDIR = $(LDAP_OBJDIR)

include $(BUILD_ROOT)/nsdefs.mk
include $(BUILD_ROOT)/nsconfig.mk
include $(LDAP_SRC)/nsldap.mk
INCLUDES+=-I$(DB_INCLUDE)

SLAPDHDIR = ../

ifeq ($(ARCH), OSF1)
PLATFORM_SPECIFIC_EXTRA_LIBRARY = -lcxx
else # OSF1
# oems might need to edit this for their platform
PLATFORM_SPECIFIC_EXTRA_LIBRARY =
endif # OSF1

INCLUDES += $(SSLINCLUDE)
DEFS += $(SSL)

CFLAGS += $(ARCH_CFLAGS)

INCLUDES += -I$(SLAPDHDIR) -I$(LDAP_ADMINCDIR)
INCLUDES += -I$(ACLINC)
INCLUDES +=  -I ../../plugins/rever
LDFLAGS	+= $(EXLDFLAGS) $(SSLLIBFLAG)

DEPLIBS=

EXTRA_LIBS_DEP = $(LDAPSDK_DEP) $(DB_LIB_DEP) $(LDAP_COMMON_LIBS_DEP)

EXTRA_LIBS += $(LDAPLINK) $(DB_LIB) \
	$(PLATFORM_SPECIFIC_EXTRA_LIBRARY) \
	$(ALIBS) $(NSPRLINK) $(SECURITYLINK) \
	$(THREADSLIB) $(LDAP_COMMON_LIBS) 

ifeq ($(ARCH), Linux)
EXTRA_LIBS += -lcrypt
endif

KEYUPG_LIBS_DEP=
KEYUPG_LIBS=$(LDAP_LIBLITEKEY)

# It looks like all of the latest versions of Unix that we ship on
# have a good enough heap implementations that they don't need
# SmartHeap.  We still need it on NT.
ifneq ($(ARCH), WINNT)
LDAP_DONT_USE_SMARTHEAP=1
endif

# Don't use smartheap for debug builds on NT
ifeq ($(ARCH), WINNT)
ifeq ($(DEBUG), full)
LDAP_DONT_USE_SMARTHEAP=1
endif
endif

ifndef LDAP_DONT_USE_SMARTHEAP
include $(BUILD_ROOT)/ns_usesh.mk
_smartheap_depend = $(SH_LIB_DEP)
else
CFLAGS+=-DLDAP_DONT_USE_SMARTHEAP
endif

RSEARCHSRC := nametable.c sdattable.c searchthread.c rsearch.c
	
INFADDSRC := nametable.c addthread.c infadd.c

DATAFILES := scripts/dbgen-GivenNames scripts/dbgen-FamilyNames scripts/dbgen-OrgUnits

DBGEN := scripts/dbgen.pl 

ifeq ($(OS_ARCH), WINNT)
OBJEXT :=.obj
else
OBJEXT :=.o
endif

RSEARCHOBJS	= $(addprefix $(OBJDEST)/, $(RSEARCHSRC:.c=$(OBJEXT)))
INFADDOBJS	= $(addprefix $(OBJDEST)/, $(INFADDSRC:.c=$(OBJEXT)))
HDIR		= $(LDAP_SRC)/include

ALL_OBJS = $(RSEARCHOBJS) $(INFADDOBJS)

RSEARCH = $(addsuffix $(EXE_SUFFIX), $(addprefix $(BINDIR)/, rsearch))
INFADD = $(addsuffix $(EXE_SUFFIX), $(addprefix $(BINDIR)/, infadd))
DBGEN = $(addsuffix $(EXE_SUFFIX), $(addprefix $(BINDIR)/, dbgen.pl))

BINS=	$(RSEARCH) $(INFADD) $(DBGEN)

all:	$(OBJDEST) $(BINDIR) $(BINS) $(DATDIR)

$(RSEARCH):	$(RSEARCHOBJS) $(BINDIR)
	$(LINK_EXE) $(RSEARCHOBJS) $(LDAP_LIBLDIF)
	-chmod 755 $(RSEARCH)

$(INFADD):	$(INFADDOBJS) $(BINDIR)
	$(LINK_EXE) $(INFADDOBJS) $(LDAP_LIBLDIF)
	-chmod 755 $(INFADD)

$(DBGEN): scripts/dbgen.pl $(BINDIR)
	cp scripts/dbgen.pl $(BINDIR)
	-chmod 755 $(DBGEN)

$(OBJDEST):
	if [ ! -d $(OBJDEST) ]; then \
	  $(MKDIR) $(OBJDEST); \
	fi

$(BINDIR):
	if [ ! -d $(BINDIR) ]; then \
	  $(MKDIR) $(BINDIR); \
	fi

$(DATDIR):
	-$(RM) -r $(DATDIR)
	-$(MKDIR) $(DATDIR)
	cp scripts/dbgen-* $(DATDIR)

clean:
	-$(RM) $(ALL_OBJS)
	-$(RM) -r $(BINS) $(DATDIR)



--- NEW FILE addthread.c ---
/** BEGIN COPYRIGHT BLOCK
 * Copyright 2001 Sun Microsystems, Inc.
 * Portions copyright 1999, 2001 Netscape Communications Corporation.
 * All rights reserved.
 * END COPYRIGHT BLOCK **/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef XP_UNIX
#include <unistd.h>
#endif
#include "nspr.h"
#include <netinet/tcp.h>	/* for TCP_NODELAY */
#include "ldap.h"
#include "addthread.h"
#include "infadd.h"


/* local data for a search thread */
struct _addthread {
    PRUint32 addCount;
    PRUint32 addTotal;
    PRUint32 failCount;
    double mintime;
    double maxtime;
    LDAP *ld;
    PRThread *tid;
    PRLock *lock;
    int id;
    int alive;
    char *blob;
	int retry;
};


/*** unique id generator ***/
static unsigned long uniqueid = 0;
void at_initID(unsigned long i)
{
    uniqueid = i;	/* before threading */
}

unsigned long getID(void)
{
    static PRLock *lock = NULL;
    unsigned long ret;

    if (!lock) {
	/* initialize */
	lock = PR_NewLock();
    }
    PR_Lock(lock);
    ret = uniqueid++;
    PR_Unlock(lock);
    return ret;
}

    
/* new addthread */
AddThread *at_new(void)
{
    AddThread *at = (AddThread *)malloc(sizeof(AddThread));

    if (!at) return NULL;
    at->addCount = at->failCount = at->addTotal = 0;
    at->mintime = 10000;
    at->maxtime = 0;
    at->ld = NULL;
    at->tid = NULL;
    at->id = 0;
    at->alive = 1;
    at->retry = 0;
    at->lock = PR_NewLock();
    at->blob = NULL;
    /* make sure the id generator has initialized */
    getID();
    return at;
}

static void at_bail(AddThread *at)
{
    PR_Lock(at->lock);
    at->alive = -10;
    PR_Unlock(at->lock);
}

void at_setThread(AddThread *at, PRThread *tid, int id)
{
    at->tid = tid;
    at->id = id;
}

int at_getThread(AddThread *at, PRThread **tid)
{
    if (tid) *tid = at->tid;
    return at->id;
}


static void at_enableTCPnodelay(AddThread *at)
{
    LBER_SOCKET s = 0;
    int val = 1;

    if (ldap_get_option(at->ld, LDAP_OPT_DESC, (void *)&s) != LDAP_SUCCESS) {
	fprintf(stderr, "T%d: failed on ldap_get_option\n", at->id);
	return;
    }
    if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val)))
	fprintf(stderr, "T%d: failed in setsockopt\n", at->id);
}

/* NOTE: currently these are unused */
#if 0
/* abruptly disconnect an LDAP connection without unbinding */
static void at_disconnect(AddThread *at)
{
    LBER_SOCKET s = 0;
    
    if (ldap_get_option(at->ld, LDAP_OPT_DESC, (void *)&s) != LDAP_SUCCESS) {
	fprintf(stderr, "T%d: failed on ldap_get_option\n", at->id);
	return;
    }
#ifdef XP_WIN
    if (closesocket(s))
	fprintf(stderr, "T%d: failed to disconnect\n", at->id);
#else
    if (close(s))
	fprintf(stderr, "T%d: failed to disconnect\n", at->id);
#endif
}
#endif

static void at_bind(AddThread *at)
{
    int ret;
    int retry = 0;

    at->ld = ldap_init(hostname, port);
    if (! at->ld) {
        fprintf(stderr, "T%d: failed to init: %s port %d\n", at->id, hostname, port);
        return;
    }
    while (retry < 10)
    {
        ret = ldap_simple_bind_s(at->ld, strlen(username) ? username : NULL,
                                 strlen(password) ? password : NULL);
        if (LDAP_SUCCESS == ret) {
            return;        /* ok */
        } else if (LDAP_CONNECT_ERROR == ret) {
            retry++;
        } else {
            break;
        }
    }
    fprintf(stderr, "T%d: failed to bind, ldap_simple_bind_s returned %d\n", 
                   at->id,  ret);
}

#if 0
static void at_unbind(AddThread *at)
{
    if (ldap_unbind(at->ld) != LDAP_SUCCESS)
	fprintf(stderr, "T%d: failed to unbind\n", at->id);
}
#endif  /* 0 */

static void at_random_tel_number(char *s)
{
    static char *areaCode[] = { "303", "408", "415", "423", "510",
				"650", "714", "803", "864", "901" };
    int index = rand() % 10;
   
    sprintf(s, "+1 %s %03d %04d", areaCode[index], rand()%1000, rand()%10000);
}

static int at_add(AddThread *at)
{
    LDAPMod *attrs[10];
    LDAPMod attr_cn, attr_sn, attr_givenname,
        attr_objectclass, attr_uid, attr_mail, attr_telephonenumber,
        attr_audio, attr_password;
    struct berval audio_berval;
    struct berval *audio_values[2];
    char dn[100], uid[10], telno[20], *sn, *givenname, cn[50], mail[50];
    char *cn_values[2], *sn_values[2], *givenname_values[2];
    char *uid_values[2], *mail_values[2], *telno_values[2];
#if 1
    char *objectclass_values[] = { "top", "person", "organizationalPerson",
                                   "inetOrgPerson", NULL };
#else
    char *objectclass_values[] = { "inetOrgPerson", NULL };
#endif
    int ret;

    /* make up the strings */
    sprintf(uid, "%lu", getID());
    at_random_tel_number(telno);
    sn = nt_getrand(family_names);
    givenname = nt_getrand(given_names);
    sprintf(cn, "%s %s %s", givenname, sn, uid);
    sprintf(mail, "%s%s at example.com", givenname, uid);
    sprintf(dn, "cn=%s,%s", cn, suffix);

    cn_values[0] = cn;
    cn_values[1] = NULL;
    sn_values[0] = sn;
    sn_values[1] = NULL;
    givenname_values[0] = givenname;
    givenname_values[1] = NULL;
    uid_values[0] = uid;
    uid_values[1] = NULL;
    mail_values[0] = mail;
    mail_values[1] = NULL;
    telno_values[0] = telno;
    telno_values[1] = NULL;
    
    attrs[0] = &attr_objectclass;
    attrs[1] = &attr_cn;
    attrs[2] = &attr_sn;
    attrs[3] = &attr_givenname;
    attrs[4] = &attr_uid;
    attrs[5] = &attr_password;
    attrs[6] = &attr_mail;
    attrs[7] = &attr_telephonenumber;
    if (blobsize > 0) {
        audio_values[0] = &audio_berval;
        audio_values[1] = 0;
        audio_berval.bv_len = (blobsize > 32000) ?
            ((long)rand() * 1039) % blobsize :
            (rand() % blobsize);
        audio_berval.bv_val = at->blob;
        attr_audio.mod_op = LDAP_MOD_BVALUES;
        attr_audio.mod_type = "audio";
        attr_audio.mod_values = (char **)&audio_values;
        attrs[8] = &attr_audio;
        attrs[9] = 0;
    }
    else
        attrs[8] = 0;

    attr_cn.mod_op = LDAP_MOD_ADD;
    attr_cn.mod_type = "cn";
    attr_cn.mod_values = cn_values;
    attr_sn.mod_op = LDAP_MOD_ADD;
    attr_sn.mod_type = "sn";
    attr_sn.mod_values = sn_values;
    attr_givenname.mod_op = LDAP_MOD_ADD;
    attr_givenname.mod_type = "givenname";
    attr_givenname.mod_values = givenname_values;
    attr_objectclass.mod_op = LDAP_MOD_ADD;
    attr_objectclass.mod_type = "objectClass";
    attr_objectclass.mod_values = objectclass_values;
    attr_uid.mod_op = LDAP_MOD_ADD;
    attr_uid.mod_type = "uid";
    attr_uid.mod_values = uid_values;
    attr_password.mod_op = LDAP_MOD_ADD;
    attr_password.mod_type = "userpassword";
    attr_password.mod_values = uid_values;
    attr_mail.mod_op = LDAP_MOD_ADD;
    attr_mail.mod_type = "mail";
    attr_mail.mod_values = mail_values;
    attr_telephonenumber.mod_op = LDAP_MOD_ADD;
    attr_telephonenumber.mod_type = "telephonenumber";
    attr_telephonenumber.mod_values = telno_values;

#if 0
    for (i = 0; attrs[i]; i++) {
        fprintf(stderr, "attr '%s': ", attrs[i]->mod_type);
        if (strcasecmp(attrs[i]->mod_type, "audio") == 0)
            fprintf(stderr, "binary data len=%lu\n", ((struct berval **)(attrs[i]->mod_values))[0]->bv_len);
        else 
            fprintf(stderr, "'%s'\n", attrs[i]->mod_values[0]);
    }
#endif
    ret = ldap_add_s(at->ld, dn, attrs);
	if (ret != LDAP_SUCCESS) {
        fprintf(stderr, "T%d: failed to add, error = %d\n", at->id, ret);
    }
    return ret;
}


/* the main thread */
void infadd_start(void *v)
{
    AddThread *at = (AddThread *)v;
    PRIntervalTime timer;
    PRUint32 span, i;
    int notBound = 1;
    int ret;

    /* make the blob if necessary */
    if (blobsize > 0) {
        at->blob = (char *)malloc(blobsize);
        if (! at->blob) {
            fprintf(stderr, "T%d: can't allocate blob!\n", at->id);
            return;
        }
        for (i = 0; i < blobsize; i++)
            at->blob[i] = (char)(rand() & 0xff);
    }

    at->alive = 1;
    while (1) {
        timer = PR_IntervalNow();
        
        /* bind if we need to */
        if (notBound) {
            at_bind(at);
            if (noDelay)
                at_enableTCPnodelay(at);
            notBound = 0;
        }

        ret = at_add(at);
        if (LDAP_SUCCESS == ret) {
            span = PR_IntervalToMilliseconds(PR_IntervalNow()-timer);
            /* update data */
            PR_Lock(at->lock);
            at->addCount++;
            at->addTotal++;
            if (at->mintime > span)
                at->mintime = span;
            if (at->maxtime < span)
                at->maxtime = span;
            at->alive = 1;
            at->retry = 0;
            PR_Unlock(at->lock);
        } else if (LDAP_CONNECT_ERROR == ret && at->retry < 10) {
            PR_Lock(at->lock);
            at->retry++;
            PR_Unlock(at->lock);
        } else {
            at_bail(at);
            return;
        }
        
    }
}

/* fetches the current min/max times and the search count, and clears them */
void at_getCountMinMax(AddThread *at, PRUint32 *count, PRUint32 *min,
		       PRUint32 *max, PRUint32 *total)
{
    PR_Lock(at->lock);
    if (count) {
        *count = at->addCount;
        at->addCount = 0;
    }
    if (min) {
        *min = at->mintime;
        at->mintime = 10000;
    }
    if (max) {
        *max = at->maxtime;
        at->maxtime = 0;
    }
    if (total)
        *total = at->addTotal;
    at->alive--;
    PR_Unlock(at->lock);
}

int at_alive(AddThread *at)
{
    int alive;

    PR_Lock(at->lock);
    alive = at->alive;
    PR_Unlock(at->lock);
    return alive;
}



--- NEW FILE addthread.h ---
/** BEGIN COPYRIGHT BLOCK
 * Copyright 2001 Sun Microsystems, Inc.
 * Portions copyright 1999, 2001 Netscape Communications Corporation.
 * All rights reserved.
 * END COPYRIGHT BLOCK **/

#ifndef _ADDTHREAD_H
#define _ADDTHREAD_H

typedef struct _addthread AddThread;

AddThread *at_new(void);
void at_setThread(AddThread *at, PRThread *tid, int id);
int at_getThread(AddThread *at, PRThread **tid);
void infadd_start(void *v);
void at_getCountMinMax(AddThread *at, PRUint32 *count, PRUint32 *min,
		       PRUint32 *max, PRUint32 *total);
int at_alive(AddThread *at);
void at_initID(unsigned long i);

#endif


--- NEW FILE infadd.c ---
/** BEGIN COPYRIGHT BLOCK
 * Copyright 2001 Sun Microsystems, Inc.
 * Portions copyright 1999, 2001 Netscape Communications Corporation.
 * All rights reserved.
 * END COPYRIGHT BLOCK **/

/*
 * XP port of dboreham's NT tool "infinite_add"
 * robey, june 1998
 *
 * note: i didn't really port this one, i just wrote a quick version
 * from scratch.
 */

#ifdef LINUX
#include <sys/param.h>
#include <sys/sysinfo.h>
#include <getopt.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include "nspr.h"
#include "nametable.h"
#include "addthread.h"

#define DEFAULT_HOSTNAME	"localhost"
#define DEFAULT_PORT		389
#define DEFAULT_THREADS		1
#define DEFAULT_INTERVAL	10000


/* global data for the threads to share */
char *hostname = DEFAULT_HOSTNAME;
PRUint16 port = DEFAULT_PORT;
int thread_count = DEFAULT_THREADS;
char *suffix = NULL;
char *username = NULL;
char *password = NULL;
PRUint32 blobsize = 0;
PRUint32 sampleInterval = DEFAULT_INTERVAL;
int noDelay = 0;
int quiet = 0;
int verbose = 0;
int saveQuit = 0;
int lmtCount = 0;
unsigned long firstUID = 0;
NameTable *given_names = NULL, *family_names = NULL;


void usage()
{
    fprintf(stdout,
	   "Usage: infadd -s suffix -u bindDN -w password [options]\n"
	   "\nOptions:\n"
	   "-h hostname (default: %s)\n"
	   "-p port     (default: %d)\n"
	   "-t threads  -- number of threads to spin (default: %d)\n"
	   "-d          -- use TCP no-delay\n"
	   "-q          -- quiet mode (no status updates)\n"
	   "-v          -- verbose mode (give per-thread statistics)\n"
	   "-I num      -- first uid (default: 0)\n"
	   "-l count    -- limit count; stops when the total count exceeds <count>\n"
	   "-i msec     -- sample interval in milliseconds (default: %u)\n"
	   "-R size     -- generate <size> random names instead of using\n"
	   "               data files\n"
	   "-z size     -- add binary blob of average size of <size> bytes\n"
	   "\n",
	   DEFAULT_HOSTNAME, DEFAULT_PORT, DEFAULT_THREADS,
	   DEFAULT_INTERVAL);
}

/* generate a random name
 * this generates 'names' like "Gxuvbnrc" but hey, there are Bosnian towns
 * with less pronouncable names...
 */
char *randName(void)
{
    char *x;
    int i, len = (rand() % 7) + 5;

    x = (char *)malloc(len+1);
    if (!x) return NULL;
    x[0] = (rand() % 26)+'A';
    for (i = 1; i < len; i++) x[i] = (rand() % 26)+'a';
    x[len] = 0;
    return x;
}

int fill_table(NameTable *nt, PRUint32 size)
{
    PRUint32 i;
    char *x;
    int ret;

    fprintf(stdout, "Generating random names: 0      ");
    for (i = 0; i < size; i++) {
	x = randName();
	/* check for duplicates */
	while (nt_cis_check(nt, x)) {
	    free(x);
	    x = randName();
	}
	ret = nt_push(nt, x);
	if ((i % 100) == 0) {
	    fprintf(stdout, "\b\b\b\b\b\b\b%-7d", i);
	}
    }
    fprintf(stdout, "\b\b\b\b\b\b\b%d.  Done.\n", size);
    return ret;
}

int main(int argc, char **argv)
{
    int ch, index, numThreads, numDead;
    PRUint32 use_random = 0;
    AddThread **threads;
    PRUint32 total = 0, ntotal = 0;
    int counter;
    char familynames[35], givennames[35];

    srand(time(NULL));
    if (argc < 2) {
        usage();
        exit(1);
    }

    while ((ch = getopt(argc, argv, "h:p:s:u:w:z:dR:t:i:I:l:qvS")) != EOF)
        switch (ch) {
        case 'h':
            hostname = optarg;
            break;
        case 'p':
            port = (PRUint16)atoi(optarg);
            break;
        case 's':
            suffix = optarg;
            break;
        case 'u':
            username = optarg;
            break;
        case 'w':
            password = optarg;
            break;
        case 'z':
            blobsize = atoi(optarg);
            break;
        case 'R':
            use_random = (PRUint32)atol(optarg);
            break;
        case 't':
            thread_count = atoi(optarg);
            break;
        case 'i':
            sampleInterval = (PRUint32)atol(optarg);
            break;
        case 'd':
            noDelay = 1;
            break;
        case 'q':
            quiet = 1;
            break;
        case 'v':
            verbose = 1;
            break;
        case 'S':
            saveQuit = 1;
            break;
        case 'l':
            lmtCount = atoi(optarg);
            break;
        case 'I':
            firstUID = atoi(optarg);
            break;
        default:
            usage();
            exit(1);
        }

    if (!suffix || !username || !password) {
        printf("infadd: missing option\n");
        usage();
        exit(1);
    }

    if (use_random < 0 || sampleInterval <= 0 || thread_count <= 0 ||
                lmtCount < 0 || blobsize < 0 || firstUID < 0) {
        printf("infadd: invalid option value\n");
        usage();
        exit(-1);
    }

    argc -= optind;
    argv += optind;

    PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 0);

    given_names = nt_new(0);
    family_names = nt_new(0);
    if (use_random) {
        fill_table(given_names, use_random);
        fill_table(family_names, use_random);
    }
    else {
        if (!access("../data/dbgen-FamilyNames", R_OK)) {
            strcpy(familynames, "../data/dbgen-FamilyNames");
            strcpy(givennames, "../data/dbgen-GivenNames");
        }
        else  {
            strcpy(familynames, "../../data/dbgen-FamilyNames");
            strcpy(givennames, "../../data/dbgen-GivenNames");
        }
        fprintf(stdout, "Loading Given-Names ...\n");
        if (!nt_load(given_names, givennames)) {
            fprintf(stdout, "*** Failed to read name table\n");
            exit(1);
        }

        fprintf(stdout, "Loading Family-Names ...\n");
        if (!nt_load(family_names, familynames)) {
            fprintf(stdout, "*** Failed to read name table\n");
            exit(1);
        }
    }

    if (saveQuit) {
        fprintf(stdout, "Saving Given-Names ...\n");
        nt_save(given_names, givennames);
        fprintf(stdout, "Saving Family-Names ...\n");
        nt_save(family_names, familynames);
        exit(0);
    }

    if (firstUID) {
        at_initID(firstUID);
    }

    /* start up threads */
    threads = (AddThread **)malloc(thread_count * sizeof(AddThread *));

    index = 0;
    while (thread_count--) {
        AddThread *at;
        PRThread *thr;

        at = at_new();
        thr = PR_CreateThread(PR_SYSTEM_THREAD, infadd_start,
                              (void *)at, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
                              PR_JOINABLE_THREAD, 0);
        at_setThread(at, thr, index+1);
        threads[index++] = at;
    }
    numThreads = index;

    fprintf(stdout, "infadd: %d thread%s launched.\n\n",
                    numThreads, numThreads == 1 ? "" : "s");

    numDead = 0;
    counter = 0;
    while (numThreads) {
        int x, alive;
        double tmpv;

        PR_Sleep(PR_MillisecondsToInterval(sampleInterval));

        counter++;

        /* now check for deadies */
        for (x = 0; x < numThreads; x++) {
            alive = at_alive(threads[x]);
            if (alive < 1) {
                int y;
                PRThread *tid;

                fprintf(stdout, "T%d DEAD", at_getThread(threads[x], &tid));
                if (alive <= -4) {
                    fprintf(stdout, " -- Dead thread being reaped.\n");
                    PR_JoinThread(tid);
                    for (y = x+1; y < numThreads; y++)
                        threads[y-1] = threads[y];
                    numThreads--;
                    numDead++;
                    x--;
                }
                else
                    fprintf(stdout, " (waiting)\n");
            }
        }

        /* check the total count */
        ntotal = 0;
        total = 0;
        for (x = 0; x < numThreads; x++) {
            PRUint32 count, min, max, ntot;

            at_getCountMinMax(threads[x], &count, &min, &max, &ntot);
            total += count;
            ntotal += ntot;
            if (!quiet && verbose)
                fprintf(stdout,
                        "T%d min:%5ums, max:%5ums, count: %3u, total: %u\n",
                        at_getThread(threads[x], NULL), min, max, count,
                        ntot);
        }
        if (!quiet && (numThreads > 1 || !verbose)) {
            fprintf(stdout, "Average rate:%7.2f, total: %u\n", 
                       (double)total/(double)numThreads, ntotal);
        }
        if (lmtCount && ntotal >= lmtCount) {
            if (!quiet) {
                fprintf(stdout,
                  "Total added records: %d, Average rate: %7.2f/thrd, "
                  "%6.2f/sec = %6.4fmsec/op\n",
                  ntotal, (double)ntotal/(double)numThreads,
                  (tmpv = (double)ntotal*1000.0/(counter*sampleInterval)),
                  (double)1000.0/tmpv);
            }
            exit(1);
        }
        /* watchdogs were reset when we fetched the min/max counters */
    }

    fprintf(stdout, "All threads died. :(\n");
    exit(1);
}


--- NEW FILE infadd.h ---
/** BEGIN COPYRIGHT BLOCK
 * Copyright 2001 Sun Microsystems, Inc.
 * Portions copyright 1999, 2001 Netscape Communications Corporation.
 * All rights reserved.
 * END COPYRIGHT BLOCK **/

#ifndef _INFADD_H
#define _INFADD_H

#include "nametable.h"

/* global data for the threads to share */
extern char *hostname;
extern PRUint16 port;
extern char *suffix;
extern char *username;
extern char *password;
extern PRUint32 blobsize;
extern NameTable *given_names;
extern NameTable *family_names;
extern int noDelay;

#endif


--- NEW FILE main.c ---
/** BEGIN COPYRIGHT BLOCK
 * Copyright 2001 Sun Microsystems, Inc.
 * Portions copyright 1999, 2001 Netscape Communications Corporation.
 * All rights reserved.
 * END COPYRIGHT BLOCK **/

/*
 * this was just easier to start from scratch.  windows is too different
 * from nspr.
 */

#include <stdio.h>
#include <stdlib.h>
#ifdef XP_UNIX
#include <unistd.h>

#endif
#include "nspr.h"
#include "rsearch.h"
#include "nametable.h"
#include "searchthread.h"


void usage()
{
    printf("\nUsage: rsearch -h host -p port -s suffix -D bindDN -w password\n"
	   "-b        -- bind before every operation\n"
	   "-u        -- don't unbind---just close the connection\n"
	   "-f filter -- Filter\n"
	   "-v        -- verbose\n"
	   "-y        -- nodelay\n"
	   "-q        -- quiet\n"
	   "-l        -- logging\n"
	   "-m        -- Operaton: Modify. -i required\n"
	   "-d        -- Operaton: Delete. -i required\n"
	   "-c        -- Operaton: Compare. -i required\n"
	   "-i file   -- name file\n"
	   "-A attrs  -- Attribute List\n"
	   "-n number -- Reserved for future use\n"
	   "-j number -- Sample interval, in seconds\n"
	   "-t number -- Threads\n\n");
}

/*
 * Convert a string of the form "foo bar baz"
 * into an array of strings. Returns a pointer
 * to allocated memory. Array contains pointers 
 * to the string passed in. So the array needs freed,
 * but the pointers don't.
 */
char **string_to_list(char* s)
{
    int string_count = 0;
    int in_space = 1;
    char *p;

    for (p = s; *p != '\0'; p++) {
	if (in_space) {
	    if (' ' != *p) {
		/* We just found the beginning of a string */
		string_count++;
		in_space = 0;
	    }
	}
	else if (' ' == *p) {
	    /* Back in space again */
	    in_space = 1;
	}
    }
    
    /* Now we have the suckers counted */
    if (string_count > 0) {
	char **return_array = (char **)malloc((string_count+1)*sizeof(char *));
	int index = 0;

	in_space = 1;
	for (p = s; *p != '\0'; p++) {
	    if (in_space) {
		if (' ' != *p) {
		    /* We just found the beginning of a string */
		    return_array[index++] = p;
		    in_space = 0;
		}
	    }
	    else if (' ' == *p) {
		/* Back in space again */
		in_space = 1;
		*p = '\0';
	    }
	}
	return_array[index] = 0;
	return return_array;
    }
    else return 0;
}


/* global data for the threads to share */
char *hostname = "localhost";
int port = 389;
int numeric = 0;
int threadCount = 1;
int verbose = 0;
int logging = 0;
int doBind = 0;
int cool = 0;
int quiet = 0;
int noDelay = 0;
int noUnBind = 0;
char *suffix = "o=Ace Industry,c=us";
char *filter = "cn=*jones*";
char *nameFile = 0;
char *bindDN = "cn=Directory Manager";
char *bindPW = "unrestricted";
char **attrToReturn = 0;
char *attrList = 0;
Operation opType = op_search;
NameTable *ntable = NULL;
int sampleInterval = 10000;


void main(int argc, char** argv)
{
    int index = 0, numThreads, numDead = 0;
    int ch;
    SearchThread **threads;

    while ((ch = getopt(argc, argv, "j:i:h:s:f:p:t:D:w:n:A:bvlyqmcdu")) != EOF)
	switch (ch) {
	case 'h':
	    hostname = optarg;
	    break;
	case 's':
	    suffix = optarg;
	    break;
	case 'f':
	    filter = optarg;
	    break;
	case 'i':
	    nameFile = optarg;
	    break;
	case 'D':
	    bindDN = optarg;
	    break;
	case 'w':
	    bindPW = optarg;
	    break;
	case 'A':
	    attrList = optarg;
	    break;
	case 'p':
	    port = atoi(optarg);	
	    break;
	case 'b':			
	    doBind = 1;
	    break;
	case 'u':			
	    noUnBind = 1;
	    break;
	case 'n':			
	    numeric = atoi(optarg);
	    break;
	case 't':		
	    threadCount = atoi(optarg);
	    break;
	case 'j':		
	    sampleInterval = atoi(optarg) * 1000;
	    break;
	case 'v':
	    verbose = 1;
	    break;
	case 'q':
	    quiet = 1;
	    break;
	case 'l':
	    logging = 1;
	    break;
	case 'y':
	    noDelay = 1;
	    break;
	case 'm':
	    opType = op_modify;
	    break;
	case 'd':
	    opType = op_delete;
	    break;
	case 'c':
	    opType = op_compare;
	    break;
	case '?':
	    usage();
	    exit(1);
	    break;
	default:
	    break;
	}
    argc -= optind;
    argv += optind;

    PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 0);

    ntable = nt_new(0);
    if (nameFile) {
	if (!nt_load(ntable, nameFile)) {
	    printf("Failed to read name table\n");
	    exit(1);
	}
    }

    if (attrList)
	attrToReturn = string_to_list(attrList);

    /* a "vector" */
    threads = (SearchThread **)malloc(threadCount * sizeof(SearchThread *));

    while (threadCount--) {
	SearchThread *st;
	PRThread *thr;

	st = st_new();
	thr = PR_CreateThread(PR_SYSTEM_THREAD, search_start,
			      (void *)st, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
			      PR_JOINABLE_THREAD, 0);
	st_setThread(st, thr, index+1);
	threads[index++] = st;
    }
    numThreads = index;

    printf("rsearch: %d threads launched.\n\n", numThreads);

    while (numThreads != numDead) {
	int x;

	PR_Sleep(PR_MillisecondsToInterval(sampleInterval));

	/* now check for deadies */
	for (x = 0; x < numThreads; x++) {
	    if (!st_alive(threads[x])) {
		int y;
		PRThread *tid;

		printf("T%d DEAD.\n", st_getThread(threads[x], &tid));
		PR_JoinThread(tid);
		for (y = x+1; y < numThreads; y++)
		    threads[y-1] = threads[y];
		numThreads--;
		numDead++;
		x--;
	    }
	}

	/* print out stats */
	if (!quiet) {
	    PRUint32 total = 0;

	    for (x = 0; x < numThreads; x++) {
		PRUint32 count, min, max;

		st_getCountMinMax(threads[x], &count, &min, &max);
		total += count;
		printf("T%d min=%4ums, max=%4ums, count = %u\n",
		       st_getThread(threads[x], NULL), min, max, count);
	    }
	    if (numThreads > 1)
		printf("Average rate = %.2f\n", 
		       (double)total/(double)numThreads);
	}
	/* watchdogs were reset when we fetched the min/max counters */
    }

    printf("All threads died. (?)\n");
    exit(1);
}

		



--- NEW FILE nametable.c ---
/** BEGIN COPYRIGHT BLOCK
 * Copyright 2001 Sun Microsystems, Inc.
 * Portions copyright 1999, 2001 Netscape Communications Corporation.
 * All rights reserved.
 * END COPYRIGHT BLOCK **/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "nspr.h"
#include "nametable.h"


struct _nametable {
    char **data;
    PRUint32 capacity;
    PRUint32 size;
};

int get_large_random_number()
{
#ifdef _WIN32
	return rand();
#else
	return random();
#endif
}


/*
 * replacement for fgets
 * This isn't like the real fgets.  It fills in 's' but strips off any
 * trailing linefeed character(s).  The return value is 0 if it went
 * okay.
 */
int PR_GetLine(PRFileDesc *fd, char *s, unsigned int n)
{
    PRInt32 start, newstart;
    int x;
    char *p;

    /* grab current location in file */
    start = PR_Seek(fd, 0, PR_SEEK_CUR);
    x = PR_Read(fd, s, n-1);
    if (x <= 0) return 1;   /* EOF or other error */
    s[x] = 0;
    p = strchr(s, '\n');
    if (p == NULL) p = strchr(s, '\r');
    if (p == NULL) {
        /* assume there was one anyway */
        return 0;
    }
    *p = 0;
    newstart = start+strlen(s)+1;
    if ((p != s) && (*(p-1) == '\r')) *(p-1) = 0;
    PR_Seek(fd, newstart, PR_SEEK_SET);
    return 0;
}

/* new nametable */
NameTable *nt_new(int capacity)
{
    NameTable *nt = (NameTable *)malloc(sizeof(NameTable));
   
    if (!nt) return NULL;
    if (capacity > 0) {
        nt->data = (char **)malloc(sizeof(char *) * capacity);
        if (! nt->data) {
    	    free(nt);
    	    return NULL;
        }
    } else {
        nt->data = NULL;
    }
    nt->capacity = capacity;
    nt->size = 0;
    return nt;
}

/* destroy nametable */
void nt_destroy(NameTable *nt)
{
    int i;

    if (nt->size) {
	for (i = 0; i < nt->size; i++)
	    free(nt->data[i]);
    }
    free(nt->data);
    free(nt);
}

/* push a string into the nametable */
int nt_push(NameTable *nt, char *s)
{
    char **ndata;

    if (nt->size >= nt->capacity) {
	/* expando! */
	nt->capacity += NT_STEP;
	ndata = (char **)realloc(nt->data, sizeof(char *) * nt->capacity);
	if (!ndata) return 0;
	nt->data = ndata;
    }
    nt->data[nt->size++] = s;
    return nt->size;
}

/* push the contents of a file into the nt, one line per entry */
int nt_load(NameTable *nt, const char *filename)
{
    PRFileDesc *fd;

    fd = PR_Open(filename, PR_RDONLY, 0);
    if (!fd) return 0;

    while (PR_Available(fd) > 0) {
	char temp[256], *s;
	if (PR_GetLine(fd, temp, 256)) break;
	s = strdup(temp);
	if (!s) break;
	if (!nt_push(nt, s)) break;
    }
    PR_Close(fd);
    return nt->size;
}

/* write a nametable out into a file */
int nt_save(NameTable *nt, const char *filename)
{
    PRFileDesc *fd;
    int i;

    fd = PR_Open(filename, PR_WRONLY|PR_CREATE_FILE, 0644);
    if (!fd) return 0;

    for (i = 0; i < nt->size; i++) {
	PR_Write(fd, nt->data[i], strlen(nt->data[i]));
	PR_Write(fd, "\n", 1);
    }
    PR_Close(fd);
    return 1;
}

/* painstakingly determine if a given entry is already in the list */
int nt_cis_check(NameTable *nt, const char *name)
{
    int i;
    
    for (i = 0; i < nt->size; i++)
	if (strcasecmp(nt->data[i], name) == 0)
	    return 1;
    return 0;
}

/* select a specific entry */
char *nt_get(NameTable *nt, int entry)
{
    return nt->data[entry];
}

char *nt_getrand(NameTable *nt)
{
    if (! nt->size) return NULL;
    /* FIXME: rand() on NT will never return a number >32k */
    return nt->data[get_large_random_number() % nt->size];
}

/* get all entries */
char **nt_get_all(NameTable *nt )
{
	return nt->data ;
}


--- NEW FILE nametable.h ---
/** BEGIN COPYRIGHT BLOCK
 * Copyright 2001 Sun Microsystems, Inc.
 * Portions copyright 1999, 2001 Netscape Communications Corporation.
 * All rights reserved.
 * END COPYRIGHT BLOCK **/

#ifndef _NAMETABLE_H
#define _NAMETABLE_H

/*
 * a NameTable is a block that just holds an array of (dynamically allocated)
 * strings.  you can read them all in from a file, and then fetch a specific
 * entry, or just a random one.
 */
typedef struct _nametable NameTable;

/* size that the array should grow by when it fills up */
#define NT_STEP		32


NameTable *nt_new(int capacity);
void nt_destroy(NameTable *nt);
int nt_push(NameTable *nt, char *s);
int nt_load(NameTable *nt, const char *filename);
int nt_save(NameTable *nt, const char *filename);
int nt_cis_check(NameTable *nt, const char *name);
char *nt_get(NameTable *nt, int entry);
char **nt_get_all(NameTable *nt );
char *nt_getrand(NameTable *nt);
int PR_GetLine(PRFileDesc *fd, char *s, unsigned int n);
int get_large_random_number();

#endif


--- NEW FILE rsearch.c ---
/** BEGIN COPYRIGHT BLOCK
 * Copyright 2001 Sun Microsystems, Inc.
 * Portions copyright 1999, 2001 Netscape Communications Corporation.
 * All rights reserved.
 * END COPYRIGHT BLOCK **/

/*
 * XP port of dboreham's NT tool "repeated_search"
 * robey, march 1998
 */

#ifdef LINUX
#include <sys/param.h>
#include <sys/sysinfo.h>
#include <getopt.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#ifdef XP_UNIX
#include <unistd.h>
#endif
#include "nspr.h"
#include "rsearch.h"
#include "nametable.h"
#include "searchthread.h"
#include "ldap.h"

#define DEFAULT_HOSTNAME	"localhost"
#define DEFAULT_PORT		389
#define DEFAULT_THREADS		1
#define DEFAULT_INTERVAL	10000

void usage()
{
    printf("\nUsage: rsearch -D binddn -w bindpw -s suffix -f filter [options]\n"
	   "-\\?       -- print Usage (this message)\n"
	   "-H        -- print Usage (this message)\n"
	   "-h host   -- ldap server host  (default: %s)\n"
	   "-p port   -- ldap server port  (default: %d)\n"
	   "-S scope  -- search SCOPE [%d,%d,or %d]  (default: %d)\n"
	   "-b        -- bind before every operation\n"
	   "-u        -- don't unbind -- just close the connection\n"
	   "-L        -- set linger -- connection discarded when closed\n"
	   "-N        -- No operation -- just bind (ignore mdc)\n"
	   "-v        -- verbose\n"
	   "-y        -- nodelay\n"
	   "-q        -- quiet\n"
#ifndef NDSRK
	   "-l        -- logging\n"
#endif  /* NDSRK */
	   "-m        -- operaton: modify non-indexed attr (description). -B required\n"
	   "-M        -- operaton: modify indexed attr (telephonenumber). -B required\n"
	   "-d        -- operaton: delete. -B required\n"
	   "-c        -- operaton: compare. -B required\n"
	   "-i file   -- name file; used for the search filter\n"
	   "-B file   -- [DN and] UID file (use '-B \\?' to see the format)\n"
	   "-A attrs  -- list of attributes for search request\n"
	   "-a file   -- list of attributes for search request in a file\n" 
	   "          -- (use '-a \\?' to see the format ; -a & -A are mutually exclusive)\n"
	   "-n number -- (reserved for future use)\n"
	   "-j number -- sample interval, in seconds  (default: %u)\n"
	   "-t number -- threads  (default: %d)\n"
	   "-T number -- Time limit, in seconds; cmd stops when exceeds <number>\n"
	   "-V        -- show running average\n"
	   "-C num    -- take num samples, then stop\n"
	   "-R num    -- drop connection & reconnect every num searches\n"
	   "-x        -- Use -B file for binding; ignored if -B is not given\n"
	   "\n",
	   DEFAULT_HOSTNAME, DEFAULT_PORT,
	   LDAP_SCOPE_BASE, LDAP_SCOPE_ONELEVEL, LDAP_SCOPE_SUBTREE,
	   LDAP_SCOPE_SUBTREE,
	   (DEFAULT_INTERVAL/1000), DEFAULT_THREADS);
	exit(1);
}

void usage_B()
{
    printf("\nFormat of the file for the '-B <file>' option:\n"
	   "(Assuming each passwd is identical to its corresponding UID.)\n"
	   "\n"
	   "Format 1.\n"
	   "=========\n"
	   "UID: <uid>\n"
	   "...\n"
	   "\n"
	   "Format 2.\n"
	   "=========\n"
	   "DN: <dn>\n"
	   "UID: <uid>\n"
	   "...\n"
	   "\n");
}

void usage_A()
{
	
	printf("\nNote: -A and -a are mutually exclusive options\n");
    printf("\nFormat of the file for the '-a <file>' option:\n"
	   "\n"
	   "Format :\n"
	   "=========\n"
	   "<attr>\n"
	   "<attr>\n"
	   "...\n"
	   "\n");

}

/*
 * Convert a string of the form "foo bar baz"
 * into an array of strings. Returns a pointer
 * to allocated memory. Array contains pointers 
 * to the string passed in. So the array needs freed,
 * but the pointers don't.
 */
char **string_to_list(char* s)
{
    int string_count = 0;
    int in_space = 1;
    char *p;

    for (p = s; *p != '\0'; p++) {
	if (in_space) {
	    if (' ' != *p) {
		/* We just found the beginning of a string */
		string_count++;
		in_space = 0;
	    }
	}
	else if (' ' == *p) {
	    /* Back in space again */
	    in_space = 1;
	}
    }
    
    /* Now we have the suckers counted */
    if (string_count > 0) {
	char **return_array = (char **)malloc((string_count+1)*sizeof(char *));
	int index = 0;

	in_space = 1;
	for (p = s; *p != '\0'; p++) {
	    if (in_space) {
		if (' ' != *p) {
		    /* We just found the beginning of a string */
		    return_array[index++] = p;
		    in_space = 0;
		}
	    }
	    else if (' ' == *p) {
		/* Back in space again */
		in_space = 1;
		*p = '\0';
	    }
	}
	return_array[index] = 0;
	return return_array;
    }
    else return 0;
}


/* global data for the threads to share */
char *hostname = DEFAULT_HOSTNAME;
int port = DEFAULT_PORT;
int numeric = 0;
int threadCount = DEFAULT_THREADS;
int verbose = 0;
int logging = 0;
int doBind = 0;
int cool = 0;
int quiet = 0;
int noDelay = 0;
int noUnBind = 0;
int noOp = 0;
int showRunningAvg = 0;
int countLimit = 0;
int reconnect = 0;
char *suffix = NULL;
char *filter = NULL;
char *nameFile = 0;
char *searchDatFile = 0;
char *attrFile = 0;
char *bindDN = NULL;
char *bindPW = NULL;
char **attrToReturn = 0;
char *attrList = 0;
Operation opType = op_search;
NameTable *ntable = NULL;
NameTable *attrTable = NULL;
SDatTable *sdattable = NULL;
int sampleInterval = DEFAULT_INTERVAL;
int timeLimit = 0;
int setLinger = 0;
int useBFile = 0;
int myScope = LDAP_SCOPE_SUBTREE;


int main(int argc, char** argv)
{
    int index = 0, numThreads, numDead = 0;
    int ch;
    int lifeTime;
    SearchThread **threads;
    PRUint32 total;
    double rate, val, cumrate;
    double sumVal;
    int counter;

    if (argc == 1) {
	usage();
	exit(1); 
    }

    while ((ch = getopt(argc, argv, 
			"B:a:j:i:h:s:f:p:t:T:D:w:n:A:S:C:R:bvlyqmMcduNLHx?V"))
	   != EOF)
	switch (ch) {
	case 'h':
	    hostname = optarg;
	    break;
	case 's':
	    suffix = optarg;
	    break;
	case 'f':
	    filter = optarg;
	    break;
	case 'i':
	    nameFile = optarg;
	    break;
	case 'B':
	    if (optarg[0] == '?') {
		usage_B();
		exit(1);
	    }
	    searchDatFile = optarg;
	    break;
	case 'D':
	    bindDN = optarg;
	    break;
	case 'w':
	    bindPW = optarg;
	    break;
	case 'A':
		if (!attrFile)
	    	attrList = optarg;
		else 
			usage();
	    break;
	case 'p':
	    port = atoi(optarg);	
	    break;
	case 'S':
	    myScope = atoi(optarg);
	    if (myScope < LDAP_SCOPE_BASE || myScope > LDAP_SCOPE_SUBTREE)
		myScope = LDAP_SCOPE_SUBTREE;
	    break;
	case 'C':
	    countLimit = atoi(optarg);
	    break;
	case 'b':			
	    doBind = 1;
	    break;
	case 'u':			
	    noUnBind = 1;
	    break;
	case 'L':			
	    setLinger = 1;
	    break;
	case 'n':			
	    numeric = atoi(optarg);
	    break;
	case 't':		
	    threadCount = atoi(optarg);
	    break;
	case 'j':		
	    sampleInterval = atoi(optarg) * 1000;
	    break;
	case 'v':
	    verbose = 1;
	    break;
	case 'q':
	    quiet = 1;
	    break;
	case 'l':
	    logging = 1;
	    break;
	case 'y':
	    noDelay = 1;
	    break;
	case 'm':
	    opType = op_modify;
	    break;
	case 'M':
	    opType = op_idxmodify;
	    break;
	case 'd':
	    opType = op_delete;
	    break;
	case 'c':
	    opType = op_compare;
	    break;
	case 'N':
	    noOp = 1;
	    doBind = 1;	/* no use w/o this */
	    break;
	case 'T':
	    timeLimit = atoi(optarg);
	    break;
	case 'V':
	    showRunningAvg = 1;
	    break;
	case 'R':
	    reconnect = atoi(optarg);
	    break;
	case 'x':
	    useBFile = 1;
	    break;
	case 'a':
	    if (optarg[0] == '?') {
		usage_A();
		exit(1);
	    }
		if (!attrList)
	    	attrFile = optarg;
		else
			usage();
		break;
	case '?':
	case 'H':
	default :
		usage();
	}

    if ( !suffix || !filter || !bindDN || !bindPW ) {
	printf("rsearch: missing option\n");
	usage();
    }

    if ( timeLimit < 0 || threadCount <= 0 || sampleInterval <= 0 ) {
	printf("rsearch: invalid option value\n");
	usage();
    }
    argc -= optind;
    argv += optind;

    PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 0);

    ntable = nt_new(0);
    if (nameFile) {
	if (!nt_load(ntable, nameFile)) {
	    printf("Failed to read name table\n");
	    exit(1);
	}
    }

	attrTable = nt_new(0);
	if (attrFile) 
	{
		if (!nt_load(attrTable , attrFile)) 
		{
			printf ("Failed to read attr name table\n");
			exit(1);
		}
	}

    sdattable = sdt_new(0);
    if (searchDatFile) {
	if (!sdt_load(sdattable, searchDatFile)) {
	    printf("Failed to read search data table: %s\n", searchDatFile);
	    exit(1);
	}
    }

    if (attrList)
	attrToReturn = string_to_list(attrList);
	

    /* a "vector" */
    threads = (SearchThread **)malloc(threadCount * sizeof(SearchThread *));

    while (threadCount--) {
	SearchThread *st;
	PRThread *thr;

	st = st_new();
	thr = PR_CreateThread(PR_SYSTEM_THREAD, search_start,
			      (void *)st, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
			      PR_JOINABLE_THREAD, 0);
	st_setThread(st, thr, index+1);
	threads[index++] = st;
    }
    numThreads = index;

    printf("rsearch: %d threads launched.\n\n", numThreads);

    lifeTime = 0;
    counter = 0;
    sumVal = 0;
    cumrate = 0.0;
    while (numThreads) {
	int x, alive;

	PR_Sleep(PR_MillisecondsToInterval(sampleInterval));

	counter++;
	lifeTime += sampleInterval/1000;
	/* now check for deadies */
	for (x = 0; x < numThreads; x++) {
	    alive = st_alive(threads[x]);
	    if (alive < 1) {
		int y;
		PRThread *tid;

		printf("T%d no heartbeat", st_getThread(threads[x], &tid));
		if (alive <= -4) {
		    printf(" -- Dead thread being reaped.\n");
		    PR_JoinThread(tid);
		    for (y = x+1; y < numThreads; y++)
			threads[y-1] = threads[y];
		    numThreads--;
		    numDead++;
		    x--;
		}
		else
		   printf(" (waiting)\n");
	    }
	}

	/* print out stats */
	total = 0;
	for (x = 0; x < numThreads; x++) {
	    PRUint32 count, min, max;

	    st_getCountMinMax(threads[x], &count, &min, &max);
	    total += count;
	    if (!quiet && verbose)
		printf("T%d min=%4ums, max=%4ums, count = %u\n",
			   st_getThread(threads[x], NULL), min, max, count);
	}
	rate = (double)total / (double)numThreads;
	val = 1000.0 * (double)total / (double)sampleInterval;
	cumrate += rate;
	if ((numThreads > 1) || (!verbose)) {
	    if (!quiet) {
		if (showRunningAvg)
		    printf("Rate: %7.2f/thr (cumul rate: %7.2f/thr)\n",
			   rate, cumrate/(double)counter);
		else
		    printf("Rate: %7.2f/thr (%6.2f/sec =%7.4fms/op), "
			   "total:%6u (%d thr)\n",
			   rate, val, (double)1000.0/val, total, numThreads);
	    }
	}
	if (countLimit && (counter >= countLimit)) {
	    printf("Thank you, and good night.\n");
	    exit(0);
	}
	if (timeLimit && (lifeTime >= timeLimit)) {
	    double tmpv;
	    if (verbose)
		printf("%d sec >= %d\n", lifeTime, timeLimit);
	    printf("Final Average rate: "
		            "%6.2f/sec = %6.4fmsec/op, total:%6u\n",
			    (tmpv = (val + sumVal)/counter),
			    (double)1000.0/tmpv,
			    total);
	    exit(0);
	}
	sumVal += val;
	/* watchdogs were reset when we fetched the min/max counters */
    }

    printf("All threads died. (?)\n");
    exit(1);
}


--- NEW FILE rsearch.h ---
/** BEGIN COPYRIGHT BLOCK
 * Copyright 2001 Sun Microsystems, Inc.
 * Portions copyright 1999, 2001 Netscape Communications Corporation.
 * All rights reserved.
 * END COPYRIGHT BLOCK **/

#ifndef _RSEARCH_H
#define _RSEARCH_H

typedef enum { op_search, op_modify, op_idxmodify, op_add, op_delete, op_compare } Operation;
#include "nametable.h"
#include "sdattable.h"

/* global data for the threads to share */
extern char *hostname;
extern int port;
extern int numeric;
/**/ extern int threadCount;
/**/ extern int verbose;
/**/ extern int logging;
extern int doBind;
extern int setLinger;
/**/ extern int cool;
/**/ extern int quiet;
extern int noDelay;
extern int noUnBind;
extern int noOp;
extern int myScope;
extern char *suffix;
extern char *filter;
/**/ extern char *nameFile;
extern char *bindDN;
extern char *bindPW;
extern char **attrToReturn;
/**/ extern char *attrList;
extern Operation opType;
extern NameTable *ntable;
extern NameTable *attrTable;
extern SDatTable *sdattable;
/**/ extern int sampleInterval;
extern int reconnect;
extern int useBFile;

#endif


--- NEW FILE sdattable.c ---
/** BEGIN COPYRIGHT BLOCK
 * Copyright 2001 Sun Microsystems, Inc.
 * Portions copyright 1999, 2001 Netscape Communications Corporation.
 * All rights reserved.
 * END COPYRIGHT BLOCK **/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "nspr.h"
#include "nametable.h"
#include "sdattable.h"


struct _sdattable {
    char **dns;
    char **uids;
    PRUint32 capacity;
    PRUint32 size;
};

/* new searchdata table */
SDatTable *sdt_new(int capacity)
{
    SDatTable *sdt = (SDatTable *)malloc(sizeof(SDatTable));
   
    if (!sdt) return NULL;
    if (capacity > 0) {
        sdt->dns = (char **)malloc(sizeof(char *) * capacity);
        if (! sdt->dns) {
    	    free(sdt);
    	    return NULL;
        }
        sdt->uids = (char **)malloc(sizeof(char *) * capacity);
        if (! sdt->uids) {
    	    free(sdt->dns);
    	    free(sdt);
    	    return NULL;
        }
    } else {
        sdt->dns = NULL;
        sdt->uids = NULL;
    }
    sdt->capacity = capacity;
    sdt->size = 0;
    return sdt;
}

/* destroy searchdata table */
void sdt_destroy(SDatTable *sdt)
{
    int i;

    if (sdt->size) {
	for (i = 0; i < sdt->size; i++) {
	    if (sdt->dns[i])
	        free(sdt->dns[i]);
	    if (sdt->uids[i])
	        free(sdt->uids[i]);
	}
    }
    if (sdt->dns);
        free(sdt->dns);
    if (sdt->uids);
        free(sdt->uids);
    free(sdt);
}

/* push a string into the searchdata table */
int sdt_push(SDatTable *sdt, char *dn, char *uid)
{
    char **sddns, **sddns0;
    char **sduids;

    if (!dn && !uid)
	return sdt->size;

    if (sdt->size >= sdt->capacity) {
	/* expando! */
	sdt->capacity += SDT_STEP;
	sddns = (char **)realloc(sdt->dns, sizeof(char *) * sdt->capacity);
	if (!sddns) return 0;
	sddns0 = sdt->dns;
	sdt->dns = sddns;
	sduids = (char **)realloc(sdt->uids, sizeof(char *) * sdt->capacity);
	if (!sduids) {
	    sdt->dns = sddns0;	/* restore */
	    return 0;
	}
	sdt->uids = sduids;
    }

    sdt->dns[sdt->size] = dn;	/* might be null */
    sdt->uids[sdt->size] = uid;	/* never be null */
    return ++sdt->size;
}

/* push the contents of a file into the sdt, one line per entry */
int sdt_load(SDatTable *sdt, const char *filename)
{
    PRFileDesc *fd;

    fd = PR_Open(filename, PR_RDONLY, 0);
    if (!fd) return 0;

    while (PR_Available(fd) > 0) {
	int rval;
	char temp[256];
	char *dn = NULL;
	char *uid = NULL;
	while (!(rval = PR_GetLine(fd, temp, 256))) {
	    char *p;
	    if (!strncasecmp(temp, "dn:", 3)) {
		for (p = temp + 4; *p == ' ' || *p == '\t'; p++) ;
	        dn = strdup(p);
	        if (!dn) break;
	    } else if (!strncasecmp(temp, "uid:", 4)) {
		for (p = temp + 5; *p == ' ' || *p == '\t'; p++) ;
	        uid = strdup(p);
	        if (!uid) break;
	    }
	    if (uid) {	/* dn should come earlier than uid */
	        if (!sdt_push(sdt, dn, uid)) goto out;
		break;
	    }
	}
	if (rval) break;	/* PR_GetLine failed */
    }
out:
    PR_Close(fd);
    return sdt->size;
}

/* write a searchdata table out into a file */
int sdt_save(SDatTable *sdt, const char *filename)
{
    PRFileDesc *fd;
    int i;

    fd = PR_Open(filename, PR_WRONLY|PR_CREATE_FILE, 0644);
    if (!fd) return 0;

    for (i = 0; i < sdt->size; i++) {
	if (sdt->dns[i]) {
	    PR_Write(fd, "dn: ", 4);
	    PR_Write(fd, sdt->dns[i], strlen(sdt->dns[i]));
	    PR_Write(fd, "\n", 1);
	}
	if (sdt->dns[i]) {
	    PR_Write(fd, "uid: ", 5);
	    PR_Write(fd, sdt->uids[i], strlen(sdt->uids[i]));
	    PR_Write(fd, "\n", 1);
	}
    }
    PR_Close(fd);
    return 1;
}

/* painstakingly determine if a given entry is already in the list */
int sdt_cis_check(SDatTable *sdt, const char *name)
{
    int i;
    
    for (i = 0; i < sdt->size; i++) {
	if (strcasecmp(sdt->dns[i], name) == 0)
	    return 1;
	if (strcasecmp(sdt->uids[i], name) == 0)
	    return 1;
    }
    return 0;
}

/* select a specific entry */
char *sdt_dn_get(SDatTable *sdt, int entry)
{
    return sdt->dns[entry];
}

void sdt_dn_set(SDatTable *sdt, int entry, char *dn)
{
    sdt->dns[entry] = strdup(dn);
}

char *sdt_uid_get(SDatTable *sdt, int entry)
{
    return sdt->uids[entry];
}

int sdt_getrand(SDatTable *sdt)
{
    if (! sdt->size) return -1;
    /* FIXME: rand() on NT will never return a number >32k */
    return get_large_random_number() % sdt->size;
}

int sdt_getlen(SDatTable *sdt)
{
    return sdt->size;
}


--- NEW FILE sdattable.h ---
/** BEGIN COPYRIGHT BLOCK
 * Copyright 2001 Sun Microsystems, Inc.
 * Portions copyright 1999, 2001 Netscape Communications Corporation.
 * All rights reserved.
 * END COPYRIGHT BLOCK **/

#ifndef _SDATTABLE_H
#define _SDATTABLE_H

/*
 * a SDatTable is a block that just holds an array of (dynamically allocated)
 * dn & uid pair (dn might be empty).  you can read them all in from a file,
 * and then fetch a specific entry, or just a random one.
 */
typedef struct _sdattable SDatTable;

/* size that the array should grow by when it fills up */
#define SDT_STEP		32

SDatTable *sdt_new(int capacity);
void sdt_destroy(SDatTable *sdt);
int sdt_push(SDatTable *sdt, char *dn, char *uid);
int sdt_load(SDatTable *sdt, const char *filename);
int sdt_save(SDatTable *sdt, const char *filename);
int sdt_cis_check(SDatTable *sdt, const char *name);
char *sdt_dn_get(SDatTable *sdt, int entry);
void sdt_dn_set(SDatTable *sdt, int entry, char *dn);
char *sdt_uid_get(SDatTable *sdt, int entry);
int sdt_getrand(SDatTable *sdt);
int sdt_getlen(SDatTable *sdt);

#endif


--- NEW FILE searchthread.c ---
/** BEGIN COPYRIGHT BLOCK
 * Copyright 2001 Sun Microsystems, Inc.
 * Portions copyright 1999, 2001 Netscape Communications Corporation.
 * All rights reserved.
 * END COPYRIGHT BLOCK **/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef XP_UNIX
#include <unistd.h>
#endif
#include <time.h>
#include <errno.h>
#include "nspr.h"
#include <sys/types.h>	
#include <sys/socket.h>	
#include <netinet/tcp.h>	/* for TCP_NODELAY */
#include "ldap.h"
#include "rsearch.h"
#include "searchthread.h"

/* local data for a search thread */
struct _searchthread {
    PRUint32 searchCount;
    PRUint32 failCount;
    double mintime;
    double maxtime;
    LDAP *ld;
    LDAP *ld2;	/* aux LDAP handle */
    LBER_SOCKET soc;
    PRThread *tid;
    PRLock *lock;
    int id;
    int alive;
	int retry;
};

/* new searchthread */
SearchThread *st_new(void)
{
    SearchThread *st = (SearchThread *)malloc(sizeof(SearchThread));

    if (!st) return NULL;
    st->searchCount = st->failCount = 0;
    st->mintime = 10000;
    st->maxtime = 0;
    st->ld = NULL;
    st->ld2 = NULL;
    st->soc = -1;
    st->tid = NULL;
    st->id = 0;
    st->alive = 1;
    st->lock = PR_NewLock();
	st->retry = 0;
    srand(time(0));
    return st;
}

void st_setThread(SearchThread *st, PRThread *tid, int id)
{
    st->tid = tid;
    st->id = id;
}

int st_getThread(SearchThread *st, PRThread **tid)
{
    if (tid) *tid = st->tid;
    return st->id;
}


static void st_enableTCPnodelay(SearchThread *st)
{
    int val = 1;

    if (st->soc < 0) {
        if (ldap_get_option(st->ld, LDAP_OPT_DESC, (void *)&st->soc)
	    != LDAP_SUCCESS) {
	    fprintf(stderr, "T%d: failed on ldap_get_option\n", st->id);
	    return;
        }
    }
    if (setsockopt(st->soc, IPPROTO_TCP, TCP_NODELAY, (char *)&val,
		   sizeof(val)))
	fprintf(stderr, "T%d: failed in setsockopt 1\n", st->id);
}

/* abruptly disconnect an LDAP connection without unbinding */
static void st_disconnect(SearchThread *st)
{
    if (st->soc < 0) {
        if (ldap_get_option(st->ld, LDAP_OPT_DESC, (void *)&st->soc)
	    != LDAP_SUCCESS) {
	    fprintf(stderr, "T%d: failed on ldap_get_option\n", st->id);
	    return;
        }
    }
#ifdef XP_WIN
    if (closesocket(st->soc))
	fprintf(stderr, "T%d: failed to disconnect\n", st->id);
#else
    if (close(st->soc))
	fprintf(stderr, "T%d: failed to disconnect\n", st->id);
#endif
    st->soc = -1;
}

static int st_bind_core(SearchThread *st, LDAP **ld, char *dn, char *uid)
{
	int ret = 0;
	int retry = 0;
    while (1) {
        ret = ldap_simple_bind_s(*ld, dn, uid);
        if (LDAP_SUCCESS == ret) {
            break;
        } else if (LDAP_CONNECT_ERROR == ret && retry < 10) {
			retry++;
		} else {
            fprintf(stderr, "T%d: failed to bind, ldap_simple_bind_s"
                            "(%s, %s) returned 0x%x (errno %d)\n", 
                            st->id, dn, uid, ret, errno);
            *ld = NULL;
            return 0;
        }
    }
	return 1;
}

static int st_bind(SearchThread *st)
{
    if (!st->ld) {
        st->ld = ldap_init(hostname, port);
        if (!st->ld) {
            fprintf(stderr, "T%d: failed to init\n", st->id);
            return 0;
        }
    }
    if (!st->ld2) {        /* aux LDAP handle */
        st->ld2 = ldap_init(hostname, port);
        if (!st->ld2) {
            fprintf(stderr, "T%d: failed to init 2\n", st->id);
            return 0;
        }
        if (0 == st_bind_core(st, &(st->ld2), strlen(bindDN) ? bindDN : NULL,
            strlen(bindPW) ? bindPW : NULL)) {
            return 0;
        }
    }

    if (opType != op_delete && opType != op_modify && opType != op_idxmodify &&
               sdattable && sdt_getlen(sdattable) > 0) {
        int e;
        char *dn, *uid;

        do {
            e = sdt_getrand(sdattable);
        } while (e < 0);
        dn = sdt_dn_get(sdattable, e);
        uid = sdt_uid_get(sdattable, e);

        if (useBFile) {
            /*  in this test, assuming uid == password */
            if (dn) {
                if (0 == st_bind_core(st, &(st->ld), dn, uid)) {
                    return 0;
                }
            } else if (uid) {
                char filterBuffer[100];
                char *pFilter;
                struct timeval timeout;
                int scope = LDAP_SCOPE_SUBTREE, attrsOnly = 0;
                LDAPMessage *result;
                int retry = 0;
    
                pFilter = filterBuffer;
                sprintf(filterBuffer, "(uid=%s)", uid);
                timeout.tv_sec = 3600;
                timeout.tv_usec = 0;
                while (1) {
                    int ret = ldap_search_st(st->ld2, suffix, scope, pFilter,
                                  NULL, attrsOnly, &timeout, &result);
                    if (LDAP_SUCCESS == ret) {
                        break;
                    } else if ((LDAP_CONNECT_ERROR == ret ||
                               (LDAP_TIMEOUT == ret)) && retry < 10) {
                        retry++;
                    } else {
                        fprintf(stderr, "T%d: failed to search 1, error=0x%x\n",
                                st->id, ret);
                        return 0;
                    }
                }
                dn = ldap_get_dn(st->ld2, result);
    
                if (0 == st_bind_core(st, &(st->ld), dn, uid)) {
                    return 0;
                }
            } else {
                fprintf(stderr, "T%d: no data found, dn: %p, uid: %p\n", 
                        st->id, dn, uid);
                return 0;
            }
        } else {
            if (0 == st_bind_core(st, &(st->ld), dn, uid)) {
                return 0;
            }
        }
    } else {
        if (0 == st_bind_core(st, &(st->ld), strlen(bindDN) ? bindDN : NULL,
                              strlen(bindPW) ? bindPW : NULL)) {
            return 0;
        }
    }
    if (st->soc < 0) {
        if (ldap_get_option(st->ld, LDAP_OPT_DESC, (void *)&st->soc)
            != LDAP_SUCCESS) {
            fprintf(stderr, "T%d: failed on ldap_get_option\n", st->id);
            return 0;
        }
    }
    if (setLinger) {
        int val;
        struct linger l;
        val = sizeof(struct linger);
        l.l_onoff = 1;
        l.l_linger = 0;
        if (setsockopt(st->soc, SOL_SOCKET, SO_LINGER, (char *)&l, val) < 0) {
            fprintf(stderr, "T%d: failed in setsockopt 2, errno %d (%d)\n",
                    st->id, errno, st->soc);
            st->soc = -1;
            return 0;
        }
    }
    return 1;
}

static void st_unbind(SearchThread *st)
{
    if (ldap_unbind(st->ld) != LDAP_SUCCESS)
	fprintf(stderr, "T%d: failed to unbind\n", st->id);
    st->ld = NULL;
    st->soc = -1;
}

static int st_search(SearchThread *st)
{
    char filterBuffer[100];
    char *pFilter;
    struct timeval timeout;
    int scope, attrsOnly = 0;
    LDAPMessage *result;
    int ret;

    scope = myScope;
    if (ntable || numeric) {
        char *s = NULL;
        char num[8];

        if (! numeric) {
            do {
                s = nt_getrand(ntable);
            } while ((s) && (strlen(s) < 1));
        } else {
            sprintf(num, "%d", get_large_random_number() % numeric);
            s = num;
        }
        sprintf(filterBuffer, filter, s);
        pFilter = filterBuffer;
    } else {
        pFilter = filter;
    }

    /* Try to get attributes from the attrNameTable */
    if (!attrToReturn)
        attrToReturn = nt_get_all(attrTable);

    timeout.tv_sec = 30;
    timeout.tv_usec = 0;
    ret = ldap_search_st(st->ld, suffix, scope, pFilter, attrToReturn,
                         attrsOnly, &timeout, &result);
    if (ret != LDAP_SUCCESS) {
        fprintf(stderr, "T%d: failed to search 2, error=0x%02X\n",
                st->id, ret);
    }
    ldap_msgfree(result);
    return ret;
}

static void st_make_random_tel_number(char *pstr)
{
    static char *area_codes[] = {"303", "415", "408", "650", "216", "580", 0};

    int idx = rand() % 6;

    sprintf(pstr, "+1 %s %03d %04d",
	    area_codes[idx], rand() % 1000, rand() % 10000);
}

static int st_modify_nonidx(SearchThread *st)
{
    LDAPMod *attrs[2];
    LDAPMod attr_description;
    int e;
    int rval;
    char *dn = NULL;
    char description[256];
    char *description_values[2];

    /* Decide what entry to modify, for this we need a table */
    if (NULL == sdattable || sdt_getlen(sdattable) == 0) {
        fprintf(stderr, "-m option requires a DN file.  Use -B file.\n");
        return 0;
    }

    /* Get the target dn */
    do {
        e = sdt_getrand(sdattable);
    } while (e < 0);
    dn = sdt_dn_get(sdattable, e);

    sprintf(description, "%s modified at %lu", dn, time(NULL));
    description_values[0] = description;
    description_values[1] = NULL;

    attrs[0] = &attr_description;
    attrs[1] = NULL;

    attr_description.mod_op = LDAP_MOD_REPLACE;
    attr_description.mod_type = "description";
    attr_description.mod_values = description_values;

    rval = ldap_modify_s(st->ld, dn, attrs);
    if (rval != LDAP_SUCCESS) {
        fprintf(stderr, "T%d: Failed to modify error=0x%x\n", st->id, rval);
        fprintf(stderr, "dn: %s\n", dn);
    }
    return rval;
}

static int st_modify_idx(SearchThread *st)
{
    LDAPMod *attrs[2];
    LDAPMod attr_telephonenumber;
    int e;
    int rval;
    char *dn = NULL;
    char telno[32];
    char *telephonenumber_values[2];

    /* Decide what entry to modify, for this we need a table */
    if (NULL == sdattable || sdt_getlen(sdattable) == 0) {
        fprintf(stderr, "-m option requires a DN file.  Use -B file.\n");
        return 0;
    }

    /* Get the target dn */
    do {
        e = sdt_getrand(sdattable);
    } while (e < 0);
    dn = sdt_dn_get(sdattable, e);

    /* Make new mod values */
    st_make_random_tel_number(telno);

    telephonenumber_values[0] = telno;
    telephonenumber_values[1] = NULL;

    attrs[0] = &attr_telephonenumber;
    attrs[1] = NULL;

    attr_telephonenumber.mod_op = LDAP_MOD_REPLACE;
    attr_telephonenumber.mod_type = "telephonenumber";
    attr_telephonenumber.mod_values = telephonenumber_values;

    rval = ldap_modify_s(st->ld, dn, attrs);
    if (rval != LDAP_SUCCESS) {
        fprintf(stderr, "T%d: Failed to modify error=0x%x\n", st->id, rval);
        fprintf(stderr, "dn: %s\n", dn);
    }
    return rval;
}

static int st_compare(SearchThread *st)
{
    int rval;
    int compare_true;
    int correct_answer;
    int e;
    char *dn = NULL;
    char *uid = NULL;
    char uid0[100];

    /* Decide what entry to modify, for this we need a table */
    if (NULL == sdattable || sdt_getlen(sdattable) == 0) {
        fprintf(stderr, "-c option requires a DN file.  Use -B file.\n");
        return 0;
    }

    /* Get the target dn */
    do {
        e = sdt_getrand(sdattable);
    } while (e < 0);
    dn = sdt_dn_get(sdattable, e);
    uid = sdt_uid_get(sdattable, e);

    compare_true = ( (rand() % 5) < 2 );

    if (!compare_true) {
        strcpy(uid0, uid);
        uid0[0] = '@';        /* make it not matched */
        uid = uid0;
    }
    rval = ldap_compare_s(st->ld, dn, "uid", uid);
    correct_answer = compare_true ? LDAP_COMPARE_TRUE : LDAP_COMPARE_FALSE;
    if (rval == correct_answer) {
		rval = LDAP_SUCCESS;
    } else {
        fprintf(stderr, "T%d: Failed to compare error=0x%x (%d)\n",
                        st->id, rval, correct_answer);
        fprintf(stderr, "dn: %s, uid: %s\n", dn, uid);
    }
    return rval;
}

static int st_delete(SearchThread *st)
{
    char *dn = NULL;
    int rval;
    int e;

    /* Decide what entry to modify, for this we need a table */
    if (NULL == sdattable || sdt_getlen(sdattable) == 0) {
        fprintf(stderr, "-d option requires a DN file.  Use -B file.\n");
        return 0;
    }

    /* Get the target dn */
    do {
        e = sdt_getrand(sdattable);
    } while (e < 0);
    dn = sdt_dn_get(sdattable, e);

    rval = ldap_delete_s(st->ld, dn);
    if (rval != LDAP_SUCCESS) {
        if (rval == LDAP_NO_SUCH_OBJECT) {
			rval = LDAP_SUCCESS;
        } else {
            fprintf(stderr, "T%d: Failed to delete error=0x%x\n", st->id, rval);
            fprintf(stderr, "dn: %s\n", dn);
        }
    }
    return rval;
}

/* the main thread */
void search_start(void *v)
{
    SearchThread *st = (SearchThread *)v;
    PRIntervalTime timer;
    int notBound = 1, res, searches = 0;
    PRUint32 span;

    st->alive = 1;
    st->ld = 0;
    while (1) {
        timer = PR_IntervalNow();
        
        /* bind if we need to */
        if (doBind || notBound) {
            res = st_bind(st);
            if (noDelay)
                st_enableTCPnodelay(st);
            if (!res) {
                st_unbind(st);
                continue;        /* error */
            }
            notBound = 0;
        }

        /* do the operation */
        if (!noOp) {
            switch(opType) {
            case op_modify:
                res = st_modify_nonidx(st);
                break;
            case op_idxmodify:
                res = st_modify_idx(st);
                break;
            case op_search:
                res = st_search(st);
                break;
            case op_compare:
                res = st_compare(st);
                break;
            case op_delete:
                res = st_delete(st);
                break;
            default:
                fprintf(stderr, "Illegal operation type specified.\n");
                return;
            }
        }
        if (LDAP_SUCCESS == res) {
            st->retry = 0;
        } else if (LDAP_CONNECT_ERROR == res && st->retry < 10) {
            st->retry++;
        } else {
               break;        /* error */
        }
        if (doBind) {
            if (noUnBind)
                st_disconnect(st);
            st_unbind(st);
        } else if (reconnect) {
            searches++;
            if (searches >= reconnect) {
                /* unceremoniously disconnect, reconnect next cycle */
                st_disconnect(st);
                st_unbind(st);
                notBound = 1;
                searches = 0;
            }
        }
        
        span = PR_IntervalToMilliseconds(PR_IntervalNow()-timer);
        /* update data */
        PR_Lock(st->lock);
        if (0 == st->retry) {    /* only when succeeded */
            st->searchCount++;
            if (st->mintime > span)
                st->mintime = span;
            if (st->maxtime < span)
                st->maxtime = span;
        }
        st->alive = 1;
        PR_Unlock(st->lock);
    }
}

/* fetches the current min/max times and the search count, and clears them */
void st_getCountMinMax(SearchThread *st, PRUint32 *count, PRUint32 *min,
		       PRUint32 *max)
{
    PR_Lock(st->lock);
    if (count) {
	*count = st->searchCount;
	st->searchCount = 0;
    }
    if (min) {
	*min = st->mintime;
	st->mintime = 10000;
    }
    if (max) {
	*max = st->maxtime;
	st->maxtime = 0;
    }
    st->alive--;
    PR_Unlock(st->lock);
}

int st_alive(SearchThread *st)
{
    int alive;

    PR_Lock(st->lock);
    alive = st->alive;
    PR_Unlock(st->lock);
    return alive;
}



--- NEW FILE searchthread.h ---
/** BEGIN COPYRIGHT BLOCK
 * Copyright 2001 Sun Microsystems, Inc.
 * Portions copyright 1999, 2001 Netscape Communications Corporation.
 * All rights reserved.
 * END COPYRIGHT BLOCK **/

#ifndef _SEARCHTHREAD_H
#define _SEARCHTHREAD_H

typedef struct _searchthread SearchThread;

SearchThread *st_new(void);
void st_setThread(SearchThread *st, PRThread *tid, int id);
int st_getThread(SearchThread *st, PRThread **tid);
void search_start(void *v);
void st_getCountMinMax(SearchThread *st, PRUint32 *count, PRUint32 *min,
		       PRUint32 *max);
int st_alive(SearchThread *st);

#endif




More information about the Fedora-directory-commits mailing list