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(a)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