diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:35:32 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:35:32 +0000 |
commit | 5ea77a75dd2d2158401331879f3c8f47940a732c (patch) | |
tree | d89dc06e9f4850a900f161e25f84e922c4f86cc8 /tests/progs | |
parent | Initial commit. (diff) | |
download | openldap-upstream.tar.xz openldap-upstream.zip |
Adding upstream version 2.5.13+dfsg.upstream/2.5.13+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | tests/progs/Makefile.in | 66 | ||||
-rw-r--r-- | tests/progs/ldif-filter.c | 256 | ||||
-rw-r--r-- | tests/progs/slapd-addel.c | 302 | ||||
-rw-r--r-- | tests/progs/slapd-auth.c | 335 | ||||
-rw-r--r-- | tests/progs/slapd-bind.c | 551 | ||||
-rw-r--r-- | tests/progs/slapd-common.c | 550 | ||||
-rw-r--r-- | tests/progs/slapd-common.h | 92 | ||||
-rw-r--r-- | tests/progs/slapd-modify.c | 225 | ||||
-rw-r--r-- | tests/progs/slapd-modrdn.c | 229 | ||||
-rw-r--r-- | tests/progs/slapd-mtread.c | 722 | ||||
-rw-r--r-- | tests/progs/slapd-read.c | 445 | ||||
-rw-r--r-- | tests/progs/slapd-search.c | 491 | ||||
-rw-r--r-- | tests/progs/slapd-tester.c | 1143 | ||||
-rw-r--r-- | tests/progs/slapd-watcher.c | 823 |
14 files changed, 6230 insertions, 0 deletions
diff --git a/tests/progs/Makefile.in b/tests/progs/Makefile.in new file mode 100644 index 0000000..5e7a2a2 --- /dev/null +++ b/tests/progs/Makefile.in @@ -0,0 +1,66 @@ +## Makefile.in for test programs +# $OpenLDAP$ +## This work is part of OpenLDAP Software <http://www.openldap.org/>. +## +## Copyright 1998-2022 The OpenLDAP Foundation. +## All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted only as authorized by the OpenLDAP +## Public License. +## +## A copy of this license is available in the file LICENSE in the +## top-level directory of the distribution or, alternatively, at +## <http://www.OpenLDAP.org/license.html>. + +PROGRAMS = slapd-tester slapd-search slapd-read slapd-addel slapd-modrdn \ + slapd-modify slapd-bind slapd-mtread ldif-filter slapd-watcher + +SRCS = slapd-common.c \ + slapd-tester.c slapd-search.c slapd-read.c slapd-addel.c \ + slapd-modrdn.c slapd-modify.c slapd-bind.c slapd-mtread.c \ + ldif-filter.c slapd-watcher.c + +LDAP_INCDIR= ../../include +LDAP_LIBDIR= ../../libraries + +XLIBS = $(LDAP_LIBLDAP_LA) $(LDAP_LIBLUTIL_A) $(LDAP_LIBLDAP_LA) $(LDAP_LIBLBER_LA) +XXLIBS = $(SECURITY_LIBS) $(LUTIL_LIBS) +XXXLIBS = $(LTHREAD_LIBS) + +OBJS = slapd-common.o + +# build-tools: FORCE +# $(MAKE) $(MFLAGS) load-tools + +# load-tools: $(PROGRAMS) + +slapd-tester: slapd-tester.o $(OBJS) $(XLIBS) + $(LTLINK) -o $@ slapd-tester.o $(OBJS) $(LIBS) + +slapd-search: slapd-search.o $(OBJS) $(XLIBS) + $(LTLINK) -o $@ slapd-search.o $(OBJS) $(LIBS) + +slapd-read: slapd-read.o $(OBJS) $(XLIBS) + $(LTLINK) -o $@ slapd-read.o $(OBJS) $(LIBS) + +slapd-addel: slapd-addel.o $(OBJS) $(XLIBS) + $(LTLINK) -o $@ slapd-addel.o $(OBJS) $(LIBS) + +slapd-modrdn: slapd-modrdn.o $(OBJS) $(XLIBS) + $(LTLINK) -o $@ slapd-modrdn.o $(OBJS) $(LIBS) + +slapd-modify: slapd-modify.o $(OBJS) $(XLIBS) + $(LTLINK) -o $@ slapd-modify.o $(OBJS) $(LIBS) + +slapd-bind: slapd-bind.o $(OBJS) $(XLIBS) + $(LTLINK) -o $@ slapd-bind.o $(OBJS) $(LIBS) + +ldif-filter: ldif-filter.o $(OBJS) $(XLIBS) + $(LTLINK) -o $@ ldif-filter.o $(OBJS) $(LIBS) + +slapd-mtread: slapd-mtread.o $(OBJS) $(XLIBS) + $(LTLINK) -o $@ slapd-mtread.o $(OBJS) $(LIBS) + +slapd-watcher: slapd-watcher.o $(OBJS) $(XLIBS) + $(LTLINK) -o $@ slapd-watcher.o $(OBJS) $(LIBS) diff --git a/tests/progs/ldif-filter.c b/tests/progs/ldif-filter.c new file mode 100644 index 0000000..355b716 --- /dev/null +++ b/tests/progs/ldif-filter.c @@ -0,0 +1,256 @@ +/* ldif-filter -- clean up LDIF testdata from stdin */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2009-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/ctype.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/unistd.h> +#ifdef _WIN32 +#include <fcntl.h> +#endif + +#define DEFAULT_SPECS "ndb=a,null=n" + +typedef struct { char *val; size_t len, alloc; } String; +typedef struct { String *val; size_t len, alloc; } Strings; + +/* Flags and corresponding program options */ +enum { SORT_ATTRS = 1, SORT_ENTRIES = 2, NO_OUTPUT = 4, DUMMY_FLAG = 8 }; +static const char spec_options[] = "aen"; /* option index = log2(enum flag) */ + +static const char *progname = "ldif-filter"; +static const String null_string = { NULL, 0, 0 }; + +static void +usage( void ) +{ + fprintf( stderr, "\ +Usage: %s [-b backend] [-s spec[,spec]...]\n\ +Filter standard input by first <spec> matching '[<backend>]=[a][e][n]':\n\ + - Remove LDIF comments.\n\ + - 'a': Sort attributes in entries.\n\ + - 'e': Sort any entries separated by just one empty line.\n\ + - 'n': Output nothing.\n\ +<backend> defaults to the $BACKEND environment variable.\n\ +Use specs '%s' if no spec on the command line applies.\n", + progname, DEFAULT_SPECS ); + exit( EXIT_FAILURE ); +} + +/* Return flags from "backend=flags" in spec; nonzero if backend found */ +static unsigned +get_flags( const char *backend, const char *spec ) +{ + size_t len = strlen( backend ); + unsigned flags = DUMMY_FLAG; + const char *end, *tmp; + + for ( ;; spec = end + ( *end != '\0' )) { + if ( !*spec ) + return 0; + end = spec + strcspn( spec, "," ); + if ( !(tmp = memchr( spec, '=', end-spec ))) + break; + if ( tmp-spec == len && !memcmp( spec, backend, len )) { + spec = tmp+1; + break; + } + } + + for ( ; spec < end; spec++ ) { + if ( (tmp = strchr( spec_options, *spec )) == NULL ) { + usage(); + } + flags |= 1U << (tmp - spec_options); + } + return flags; +} + +#define APPEND(s /* String or Strings */, data, count, isString) do { \ + size_t slen = (s)->len, salloc = (s)->alloc, sz = sizeof *(s)->val; \ + if ( salloc <= slen + (count) ) { \ + (s)->alloc = salloc += salloc + ((count)|7) + 1; \ + (s)->val = xrealloc( (s)->val, sz * salloc ); \ + } \ + memcpy( (s)->val + slen, data, sz * ((count) + !!(isString)) ); \ + (s)->len = slen + (count); \ +} while (0) + +static void * +xrealloc( void *ptr, size_t len ) +{ + if ( (ptr = realloc( ptr, len )) == NULL ) { + perror( progname ); + exit( EXIT_FAILURE ); + } + return ptr; +} + +static int +cmp( const void *s, const void *t ) +{ + return strcmp( ((const String *) s)->val, ((const String *) t)->val ); +} + +static void +sort_strings( Strings *ss, size_t offset ) +{ + qsort( ss->val + offset, ss->len - offset, sizeof(*ss->val), cmp ); +} + +/* Build entry ss[n] from attrs ss[n...], and free the attrs */ +static void +build_entry( Strings *ss, size_t n, unsigned flags, size_t new_len ) +{ + String *vals = ss->val, *e = &vals[n]; + size_t end = ss->len; + char *ptr; + + if ( flags & SORT_ATTRS ) { + sort_strings( ss, n + 1 ); + } + e->val = xrealloc( e->val, e->alloc = new_len + 1 ); + ptr = e->val + e->len; + e->len = new_len; + ss->len = ++n; + for ( ; n < end; free( vals[n++].val )) { + ptr = strcpy( ptr, vals[n].val ) + vals[n].len; + } + assert( ptr == e->val + new_len ); +} + +/* Flush entries to stdout and free them */ +static void +flush_entries( Strings *ss, const char *sep, unsigned flags ) +{ + size_t i, end = ss->len; + const char *prefix = ""; + + if ( flags & SORT_ENTRIES ) { + sort_strings( ss, 0 ); + } + for ( i = 0; i < end; i++, prefix = sep ) { + if ( printf( "%s%s", prefix, ss->val[i].val ) < 0 ) { + perror( progname ); + exit( EXIT_FAILURE ); + } + free( ss->val[i].val ); + } + ss->len = 0; +} + +static void +filter_stdin( unsigned flags ) +{ + char line[256]; + Strings ss = { NULL, 0, 0 }; /* entries + attrs of partial entry */ + size_t entries = 0, attrs_totlen = 0, line_len; + const char *entry_sep = "\n", *sep = ""; + int comment = 0, eof = 0, eol, prev_eol = 1; /* flags */ + String *s; + + /* LDIF = Entries ss[..entries-1] + sep + attrs ss[entries..] + line */ + for ( ; !eof || ss.len || *sep; prev_eol = eol ) { + if ( eof || (eof = !fgets( line, sizeof(line), stdin ))) { + strcpy( line, prev_eol ? "" : *sep ? sep : "\n" ); + } + line_len = strlen( line ); + eol = (line_len == 0 || line[line_len - 1] == '\n'); + + if ( *line == ' ' ) { /* continuation line? */ + prev_eol = 0; + } else if ( prev_eol ) { /* start of logical line? */ + comment = (*line == '#'); + } + if ( comment || (flags & NO_OUTPUT) ) { + continue; + } + + /* Collect attrs for partial entry in ss[entries...] */ + if ( !prev_eol && attrs_totlen != 0 ) { + goto grow_attr; + } else if ( line_len > (*line == '\r' ? 2 : 1) ) { + APPEND( &ss, &null_string, 1, 0 ); /* new attr */ + grow_attr: + s = &ss.val[ss.len - 1]; + APPEND( s, line, line_len, 1 ); /* strcat to attr */ + attrs_totlen += line_len; + continue; + } + + /* Empty line - consume sep+attrs or entries+sep */ + if ( attrs_totlen != 0 ) { + entry_sep = sep; + if ( entries == 0 ) + fputs( sep, stdout ); + build_entry( &ss, entries++, flags, attrs_totlen ); + attrs_totlen = 0; + } else { + flush_entries( &ss, entry_sep, flags ); + fputs( sep, stdout ); + entries = 0; + } + sep = "\r\n" + 2 - line_len; /* sep = copy(line) */ + } + + free( ss.val ); +} + +int +main( int argc, char **argv ) +{ + const char *backend = getenv( "BACKEND" ), *specs = "", *tmp; + unsigned flags; + int i; + + if ( argc > 0 ) { + progname = (tmp = strrchr( argv[0], '/' )) ? tmp+1 : argv[0]; + } + + while ( (i = getopt( argc, argv, "b:s:" )) != EOF ) { + switch ( i ) { + case 'b': + backend = optarg; + break; + case 's': + specs = optarg; + break; + default: + usage(); + } + } + if ( optind < argc ) { + usage(); + } + if ( backend == NULL ) { + backend = ""; + } + +#ifdef _WIN32 + _setmode(1, _O_BINARY); /* don't convert \n to \r\n on stdout */ +#endif + flags = get_flags( backend, specs ); + filter_stdin( flags ? flags : get_flags( backend, DEFAULT_SPECS )); + if ( fclose( stdout ) == EOF ) { + perror( progname ); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/tests/progs/slapd-addel.c b/tests/progs/slapd-addel.c new file mode 100644 index 0000000..ca007ce --- /dev/null +++ b/tests/progs/slapd-addel.c @@ -0,0 +1,302 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1999-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This work was initially developed by Kurt Spanier for inclusion + * in OpenLDAP Software. + */ + +#include "portable.h" + +#include <stdio.h> + +#include "ac/stdlib.h" + +#include "ac/ctype.h" +#include "ac/param.h" +#include "ac/socket.h" +#include "ac/string.h" +#include "ac/unistd.h" +#include "ac/wait.h" + +#include "ldap.h" +#include "lutil.h" +#include "ldif.h" + +#include "slapd-common.h" + +static LDIFRecord * +get_add_entry( char *filename ); + +static void +do_addel( struct tester_conn_args *config, + LDIFRecord *record, int friendly ); + +static void +usage( char *name, char opt ) +{ + if ( opt ) { + fprintf( stderr, "%s: unable to handle option \'%c\'\n\n", + name, opt ); + } + + fprintf( stderr, "usage: %s " TESTER_COMMON_HELP + "-f <addfile> " + "[-F]\n", + name ); + exit( EXIT_FAILURE ); +} + +int +main( int argc, char **argv ) +{ + int i; + char *filename = NULL, *buf = NULL; + int friendly = 0; + struct LDIFFP *fp; + LDIFRecord record = {}; + struct tester_conn_args *config; + struct berval bv = {}; + unsigned long lineno = 0; + + config = tester_init( "slapd-addel", TESTER_ADDEL ); + + while ( ( i = getopt( argc, argv, TESTER_COMMON_OPTS "Ff:" ) ) != EOF ) + { + switch ( i ) { + case 'F': + friendly++; + break; + + case 'i': + /* ignored (!) by now */ + break; + + case 'f': /* file with entry search request */ + filename = optarg; + break; + + default: + if ( tester_config_opt( config, i, optarg ) == LDAP_SUCCESS ) { + break; + } + usage( argv[0], i ); + break; + } + } + + if ( filename == NULL ) + usage( argv[0], 0 ); + + if ( (fp = ldif_open( filename, "r" )) == NULL ) { + tester_perror( filename, "while reading ldif file" ); + exit( EXIT_FAILURE ); + } + + i = 0; + if ( ldif_read_record( fp, &lineno, &buf, &i ) < 0 ) { + tester_error( "ldif_read_record failed" ); + exit( EXIT_FAILURE ); + } + bv.bv_val = buf; + bv.bv_len = i; + + if ( ldap_parse_ldif_record( &bv, lineno, &record, "slapd-addel", + LDIF_DEFAULT_ADD | LDIF_ENTRIES_ONLY ) ) { + tester_error( "ldif_read_record failed" ); + exit( EXIT_FAILURE ); + } + ldif_close( fp ); + + if ( ( record.lr_op != LDAP_REQ_ADD ) || ( !record.lrop_mods ) ) { + + fprintf( stderr, "%s: invalid entry DN in file \"%s\".\n", + argv[0], filename ); + exit( EXIT_FAILURE ); + + } + + tester_config_finish( config ); + + for ( i = 0; i < config->outerloops; i++ ) { + do_addel( config, &record, friendly ); + } + + free( buf ); + exit( EXIT_SUCCESS ); +} + + +static void +addmodifyop( LDAPMod ***pmodsp, int modop, char *attr, char *value, int vlen ) +{ + LDAPMod **pmods; + int i, j; + struct berval *bvp; + + pmods = *pmodsp; + modop |= LDAP_MOD_BVALUES; + + i = 0; + if ( pmods != NULL ) { + for ( ; pmods[ i ] != NULL; ++i ) { + if ( strcasecmp( pmods[ i ]->mod_type, attr ) == 0 && + pmods[ i ]->mod_op == modop ) { + break; + } + } + } + + if ( pmods == NULL || pmods[ i ] == NULL ) { + if (( pmods = (LDAPMod **)realloc( pmods, (i + 2) * + sizeof( LDAPMod * ))) == NULL ) { + tester_perror( "realloc", NULL ); + exit( EXIT_FAILURE ); + } + *pmodsp = pmods; + pmods[ i + 1 ] = NULL; + if (( pmods[ i ] = (LDAPMod *)calloc( 1, sizeof( LDAPMod ))) + == NULL ) { + tester_perror( "calloc", NULL ); + exit( EXIT_FAILURE ); + } + pmods[ i ]->mod_op = modop; + if (( pmods[ i ]->mod_type = strdup( attr )) == NULL ) { + tester_perror( "strdup", NULL ); + exit( EXIT_FAILURE ); + } + } + + if ( value != NULL ) { + j = 0; + if ( pmods[ i ]->mod_bvalues != NULL ) { + for ( ; pmods[ i ]->mod_bvalues[ j ] != NULL; ++j ) { + ; + } + } + if (( pmods[ i ]->mod_bvalues = + (struct berval **)ber_memrealloc( pmods[ i ]->mod_bvalues, + (j + 2) * sizeof( struct berval * ))) == NULL ) { + tester_perror( "ber_memrealloc", NULL ); + exit( EXIT_FAILURE ); + } + pmods[ i ]->mod_bvalues[ j + 1 ] = NULL; + if (( bvp = (struct berval *)ber_memalloc( sizeof( struct berval ))) + == NULL ) { + tester_perror( "ber_memalloc", NULL ); + exit( EXIT_FAILURE ); + } + pmods[ i ]->mod_bvalues[ j ] = bvp; + + bvp->bv_len = vlen; + if (( bvp->bv_val = (char *)malloc( vlen + 1 )) == NULL ) { + tester_perror( "malloc", NULL ); + exit( EXIT_FAILURE ); + } + AC_MEMCPY( bvp->bv_val, value, vlen ); + bvp->bv_val[ vlen ] = '\0'; + } +} + + +static void +do_addel( + struct tester_conn_args *config, + LDIFRecord *record, + int friendly ) +{ + LDAP *ld = NULL; + int i = 0, do_retry = config->retries; + int rc = LDAP_SUCCESS; + +retry:; + if ( ld == NULL ) { + tester_init_ld( &ld, config, 0 ); + } + + if ( do_retry == config->retries ) { + fprintf( stderr, "PID=%ld - Add/Delete(%d): entry=\"%s\".\n", + (long) pid, config->loops, record->lr_dn.bv_val ); + } + + for ( ; i < config->loops; i++ ) { + + /* add the entry */ + rc = ldap_add_ext_s( ld, record->lr_dn.bv_val, record->lrop_mods, NULL, NULL ); + if ( rc != LDAP_SUCCESS ) { + tester_ldap_error( ld, "ldap_add_ext_s", NULL ); + switch ( rc ) { + case LDAP_ALREADY_EXISTS: + /* NOTE: this likely means + * the delete failed + * during the previous round... */ + if ( !friendly ) { + goto done; + } + break; + + case LDAP_BUSY: + case LDAP_UNAVAILABLE: + if ( do_retry > 0 ) { + do_retry--; + goto retry; + } + /* fall thru */ + + default: + goto done; + } + } + +#if 0 + /* wait a second for the add to really complete */ + /* This masks some race conditions though. */ + sleep( 1 ); +#endif + + /* now delete the entry again */ + rc = ldap_delete_ext_s( ld, record->lr_dn.bv_val, NULL, NULL ); + if ( rc != LDAP_SUCCESS ) { + tester_ldap_error( ld, "ldap_delete_ext_s", NULL ); + switch ( rc ) { + case LDAP_NO_SUCH_OBJECT: + /* NOTE: this likely means + * the add failed + * during the previous round... */ + if ( !friendly ) { + goto done; + } + break; + + case LDAP_BUSY: + case LDAP_UNAVAILABLE: + if ( do_retry > 0 ) { + do_retry--; + goto retry; + } + /* fall thru */ + + default: + goto done; + } + } + } + +done:; + fprintf( stderr, " PID=%ld - Add/Delete done (%d).\n", (long) pid, rc ); + + ldap_unbind_ext( ld, NULL, NULL ); +} + + diff --git a/tests/progs/slapd-auth.c b/tests/progs/slapd-auth.c new file mode 100644 index 0000000..dcb4690 --- /dev/null +++ b/tests/progs/slapd-auth.c @@ -0,0 +1,335 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2006-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This work was initially developed by Howard Chu for inclusion + * in OpenLDAP Software. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/ctype.h> +#include <ac/param.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/unistd.h> +#include <ac/wait.h> +#include <ac/time.h> +#include <ac/signal.h> + +#include <ldap.h> +#include <ldap_pvt_thread.h> +#include <lutil.h> + +static int +do_time( ); + +/* This program is a simplified version of SLAMD's WeightedAuthRate jobclass. + * It doesn't offer as much configurability, but it's a good starting point. + * When run without the -R option it will behave as a Standard AuthRate job. + * Eventually this will grow into a set of C-based load generators for the SLAMD + * framework. This code is anywhere from 2 to 10 times more efficient than the + * original Java code, allowing servers to be fully loaded without requiring + * anywhere near as much load-generation hardware. + */ +static void +usage( char *name ) +{ + fprintf( stderr, "usage: %s -H <uri> -b <baseDN> -w <passwd> -t <seconds> -r lo:hi\n\t" + "[-R %:lo:hi] [-f <filter-template>] [-n <threads>] [-D <bindDN>] [-i <seconds>]\n", + name ); + exit( EXIT_FAILURE ); +} + +static char *filter = "(uid=user.%d)"; + +static char hname[1024]; +static char *uri = "ldap:///"; +static char *base; +static char *pass; +static char *binder; + +static int tdur, r1per, r1lo, r1hi, r2per, r2lo, r2hi; +static int threads = 1; + +static int interval = 30; + +static volatile int *r1binds, *r2binds; +static int *r1old, *r2old; +static volatile int finish; + +int +main( int argc, char **argv ) +{ + int i; + + while ( (i = getopt( argc, argv, "b:D:H:w:f:n:i:t:r:R:" )) != EOF ) { + switch( i ) { + case 'b': /* base DN of a tree of user DNs */ + base = optarg; + break; + + case 'D': + binder = optarg; + break; + + case 'H': /* the server uri */ + uri = optarg; + break; + + case 'w': + pass = strdup( optarg ); + break; + + case 't': /* the duration to run */ + if ( lutil_atoi( &tdur, optarg ) != 0 ) { + usage( argv[0] ); + } + break; + + case 'i': /* the time interval */ + if ( lutil_atoi( &interval, optarg ) != 0 ) { + usage( argv[0] ); + } + break; + + case 'r': /* the uid range */ + if ( sscanf(optarg, "%d:%d", &r1lo, &r1hi) != 2 ) { + usage( argv[0] ); + } + break; + + case 'R': /* percentage:2nd uid range */ + if ( sscanf(optarg, "%d:%d:%d", &r2per, &r2lo, &r2hi) != 3 ) { + usage( argv[0] ); + } + break; + + case 'f': + filter = optarg; + break; + + case 'n': + if ( lutil_atoi( &threads, optarg ) != 0 || threads < 1 ) { + usage( argv[0] ); + } + break; + + default: + usage( argv[0] ); + break; + } + } + + if ( tdur == 0 || r1hi <= r1lo ) + usage( argv[0] ); + + r1per = 100 - r2per; + if ( r1per < 1 ) + usage( argv[0] ); + + r1binds = calloc( threads*4, sizeof( int )); + r2binds = r1binds + threads; + r1old = (int *)r2binds + threads; + r2old = r1old + threads; + + do_time( ); + + exit( EXIT_SUCCESS ); +} + +static void * +my_task( void *my_num ) +{ + LDAP *ld = NULL, *sld = NULL; + ber_int_t msgid; + LDAPMessage *res, *msg; + char *attrs[] = { "1.1", NULL }; + int rc = LDAP_SUCCESS; + int tid = *(int *)my_num; + + ldap_initialize( &ld, uri ); + if ( ld == NULL ) { + perror( "ldap_initialize" ); + return NULL; + } + + { + int version = LDAP_VERSION3; + (void) ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, + &version ); + } + (void) ldap_set_option( ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF ); + + ldap_initialize( &sld, uri ); + if ( sld == NULL ) { + perror( "ldap_initialize" ); + return NULL; + } + + { + int version = LDAP_VERSION3; + (void) ldap_set_option( sld, LDAP_OPT_PROTOCOL_VERSION, + &version ); + } + (void) ldap_set_option( sld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF ); + if ( binder ) { + rc = ldap_bind_s( sld, binder, pass, LDAP_AUTH_SIMPLE ); + if ( rc != LDAP_SUCCESS ) { + ldap_perror( sld, "ldap_bind" ); + } + } + + r1binds[tid] = 0; + + for (;;) { + char dn[BUFSIZ], *ptr, fstr[256]; + int j, isr1; + + if ( finish ) + break; + + j = rand() % 100; + if ( j < r1per ) { + j = rand() % r1hi; + isr1 = 1; + } else { + j = rand() % (r2hi - r2lo + 1 ); + j += r2lo; + isr1 = 0; + } + sprintf(fstr, filter, j); + + rc = ldap_search_ext( sld, base, LDAP_SCOPE_SUB, + fstr, attrs, 0, NULL, NULL, 0, 0, &msgid ); + if ( rc != LDAP_SUCCESS ) { + ldap_perror( sld, "ldap_search_ex" ); + return NULL; + } + + while (( rc=ldap_result( sld, LDAP_RES_ANY, LDAP_MSG_ONE, NULL, &res )) >0){ + BerElement *ber; + struct berval bv; + char *ptr; + int done = 0; + + for (msg = ldap_first_message( sld, res ); msg; + msg = ldap_next_message( sld, msg )) { + switch ( ldap_msgtype( msg )) { + case LDAP_RES_SEARCH_ENTRY: + rc = ldap_get_dn_ber( sld, msg, &ber, &bv ); + strcpy(dn, bv.bv_val ); + ber_free( ber, 0 ); + break; + case LDAP_RES_SEARCH_RESULT: + done = 1; + break; + } + if ( done ) + break; + } + ldap_msgfree( res ); + if ( done ) break; + } + + rc = ldap_bind_s( ld, dn, pass, LDAP_AUTH_SIMPLE ); + if ( rc != LDAP_SUCCESS ) { + ldap_perror( ld, "ldap_bind" ); + } + if ( isr1 ) + r1binds[tid]++; + else + r2binds[tid]++; + } + + ldap_unbind( sld ); + ldap_unbind( ld ); + + return NULL; +} + +static int +do_time( ) +{ + struct timeval tv; + time_t now, prevt, start; + + int r1new, r2new; + int dt, dr1, dr2, rr1, rr2; + int dr10, dr20; + int i; + + gethostname(hname, sizeof(hname)); + printf("%s(tid)\tdeltaT\tauth1\tauth2\trate1\trate2\tRate1+2\n", hname); + srand(getpid()); + + prevt = start = time(0L); + + for ( i = 0; i<threads; i++ ) { + ldap_pvt_thread_t thr; + r1binds[i] = i; + ldap_pvt_thread_create( &thr, 1, my_task, (void *)&r1binds[i] ); + } + + for (;;) { + tv.tv_sec = interval; + tv.tv_usec = 0; + + select(0, NULL, NULL, NULL, &tv); + + now = time(0L); + + dt = now - prevt; + prevt = now; + + dr10 = 0; + dr20 = 0; + + for ( i = 0; i < threads; i++ ) { + r1new = r1binds[i]; + r2new = r2binds[i]; + + dr1 = r1new - r1old[i]; + dr2 = r2new - r2old[i]; + rr1 = dr1 / dt; + rr2 = dr2 / dt; + + printf("%s(%d)\t%d\t%d\t%d\t%d\t%d\t%d\n", + hname, i, dt, dr1, dr2, rr1, rr2, rr1 + rr2); + + dr10 += dr1; + dr20 += dr2; + + r1old[i] = r1new; + r2old[i] = r2new; + } + if ( i > 1 ) { + rr1 = dr10 / dt; + rr2 = dr20 / dt; + + printf("%s(sum)\t%d\t%d\t%d\t%d\t%d\t%d\n", + hname, 0, dr10, dr20, rr1, rr2, rr1 + rr2); + } + + if ( now - start >= tdur ) { + finish = 1; + break; + } + } + return 0; +} diff --git a/tests/progs/slapd-bind.c b/tests/progs/slapd-bind.c new file mode 100644 index 0000000..dad0dcb --- /dev/null +++ b/tests/progs/slapd-bind.c @@ -0,0 +1,551 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1999-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This work was initially developed by Howard Chu for inclusion + * in OpenLDAP Software. + */ + +#include "portable.h" + +#include <stdio.h> + +#include "ac/stdlib.h" +#include "ac/time.h" + +#include "ac/ctype.h" +#include "ac/param.h" +#include "ac/socket.h" +#include "ac/string.h" +#include "ac/unistd.h" +#include "ac/wait.h" +#include "ac/time.h" + +#include "ldap.h" +#include "lutil.h" +#include "lutil_ldap.h" +#include "lber_pvt.h" +#include "ldap_pvt.h" + +#include "slapd-common.h" + +static int +do_bind( struct tester_conn_args *config, char *dn, int maxloop, int force, + int noinit, LDAP **ldp, struct berval *pass, int action_type, void *action ); + +static int +do_base( struct tester_conn_args *config, char *dn, char *base, char *filter, char *pwattr, + int force, int noinit, int action_type, void *action ); + +/* This program can be invoked two ways: if -D is used to specify a Bind DN, + * that DN will be used repeatedly for all of the Binds. If instead -b is used + * to specify a base DN, a search will be done for all "person" objects under + * that base DN. Then DNs from this list will be randomly selected for each + * Bind request. All of the users must have identical passwords. Also it is + * assumed that the users are all onelevel children of the base. + */ +static void +usage( char *name, char opt ) +{ + if ( opt ) { + fprintf( stderr, "%s: unable to handle option \'%c\'\n\n", + name, opt ); + } + + fprintf( stderr, "usage: %s " TESTER_COMMON_HELP + "[-b <baseDN> [-f <searchfilter>] [-a pwattr]] " + "[-B <extra>[,...]] " + "[-F] " + "[-I]\n", + name ); + exit( EXIT_FAILURE ); +} + +int +main( int argc, char **argv ) +{ + int i; + char *base = NULL; + char *filter = "(objectClass=person)"; + char *pwattr = NULL; + int force = 0; + int noinit = 1; + struct tester_conn_args *config; + + /* extra action to do after bind... */ + struct berval type[] = { + BER_BVC( "tester=" ), + BER_BVC( "add=" ), + BER_BVC( "bind=" ), + BER_BVC( "modify=" ), + BER_BVC( "modrdn=" ), + BER_BVC( "read=" ), + BER_BVC( "search=" ), + BER_BVNULL + }; + + LDAPURLDesc *extra_ludp = NULL; + + config = tester_init( "slapd-bind", TESTER_BIND ); + + /* by default, tolerate invalid credentials */ + tester_ignore_str2errlist( "*INVALID_CREDENTIALS" ); + + while ( ( i = getopt( argc, argv, TESTER_COMMON_OPTS "a:B:b:Ff:I" ) ) != EOF ) + { + switch ( i ) { + case 'a': + pwattr = optarg; + break; + + case 'b': /* base DN of a tree of user DNs */ + base = optarg; + break; + + case 'B': + { + int c; + + for ( c = 0; type[c].bv_val; c++ ) { + if ( strncasecmp( optarg, type[c].bv_val, type[c].bv_len ) == 0 ) + { + break; + } + } + + if ( type[c].bv_val == NULL ) { + usage( argv[0], 'B' ); + } + + switch ( c ) { + case TESTER_TESTER: + case TESTER_BIND: + /* invalid */ + usage( argv[0], 'B' ); + + case TESTER_SEARCH: + { + if ( ldap_url_parse( &optarg[type[c].bv_len], &extra_ludp ) != LDAP_URL_SUCCESS ) + { + usage( argv[0], 'B' ); + } + } break; + + case TESTER_ADDEL: + case TESTER_MODIFY: + case TESTER_MODRDN: + case TESTER_READ: + /* nothing to do */ + break; + + default: + assert( 0 ); + } + + } break; + + case 'f': + filter = optarg; + break; + + case 'F': + force++; + break; + + case 'I': + /* reuse connection */ + noinit = 0; + break; + + default: + if ( tester_config_opt( config, i, optarg ) == LDAP_SUCCESS ) { + break; + } + usage( argv[0], i ); + break; + } + } + + tester_config_finish( config ); + + for ( i = 0; i < config->outerloops; i++ ) { + int rc; + + if ( base != NULL ) { + rc = do_base( config, config->binddn, base, + filter, pwattr, force, noinit, -1, NULL ); + } else { + rc = do_bind( config, config->binddn, + config->loops, force, noinit, NULL, &config->pass, -1, NULL ); + } + if ( rc == LDAP_SERVER_DOWN ) + break; + } + + exit( EXIT_SUCCESS ); +} + + +static int +do_bind( struct tester_conn_args *config, char *dn, int maxloop, int force, + int noinit, LDAP **ldp, struct berval *pass, int action_type, void *action ) +{ + LDAP *ld = ldp ? *ldp : NULL; + char *bindfunc = "ldap_sasl_bind_s"; + int i, rc = -1; + + /* for internal search */ + int timelimit = 0; + int sizelimit = 0; + + switch ( action_type ) { + case -1: + break; + + case TESTER_SEARCH: + { + LDAPURLDesc *ludp = (LDAPURLDesc *)action; + + assert( action != NULL ); + + if ( ludp->lud_exts != NULL ) { + for ( i = 0; ludp->lud_exts[ i ] != NULL; i++ ) { + char *ext = ludp->lud_exts[ i ]; + int crit = 0; + + if (ext[0] == '!') { + crit++; + ext++; + } + + if ( strncasecmp( ext, "x-timelimit=", STRLENOF( "x-timelimit=" ) ) == 0 ) { + if ( lutil_atoi( &timelimit, &ext[ STRLENOF( "x-timelimit=" ) ] ) && crit ) { + tester_error( "unable to parse critical extension x-timelimit" ); + } + + } else if ( strncasecmp( ext, "x-sizelimit=", STRLENOF( "x-sizelimit=" ) ) == 0 ) { + if ( lutil_atoi( &sizelimit, &ext[ STRLENOF( "x-sizelimit=" ) ] ) && crit ) { + tester_error( "unable to parse critical extension x-sizelimit" ); + } + + } else if ( crit ) { + tester_error( "unknown critical extension" ); + } + } + } + } break; + + default: + /* nothing to do yet */ + break; + } + + if ( maxloop > 1 ) { + fprintf( stderr, "PID=%ld - Bind(%d): dn=\"%s\".\n", + (long) pid, maxloop, dn ); + } + + for ( i = 0; i < maxloop; i++ ) { + if ( !noinit || ld == NULL ) { + tester_init_ld( &ld, config, TESTER_INIT_ONLY ); + +#ifdef HAVE_CYRUS_SASL + if ( config->secprops != NULL ) { + rc = ldap_set_option( ld, + LDAP_OPT_X_SASL_SECPROPS, config->secprops ); + + if( rc != LDAP_OPT_SUCCESS ) { + tester_ldap_error( ld, "ldap_set_option(SECPROPS)", NULL ); + exit( EXIT_FAILURE ); + } + } +#endif + } + + if ( config->authmethod == LDAP_AUTH_SASL ) { +#ifdef HAVE_CYRUS_SASL + bindfunc = "ldap_sasl_interactive_bind_s"; + rc = ldap_sasl_interactive_bind_s( ld, + dn, + config->mech, + NULL, NULL, + LDAP_SASL_QUIET, + lutil_sasl_interact, + config->defaults ); +#else /* HAVE_CYRUS_SASL */ + /* caller shouldn't have allowed this */ + assert(0); +#endif + } else if ( config->authmethod == LDAP_AUTH_SIMPLE ) { + bindfunc = "ldap_sasl_bind_s"; + rc = ldap_sasl_bind_s( ld, + dn, LDAP_SASL_SIMPLE, + pass, NULL, NULL, NULL ); + } + + if ( rc ) { + int first = tester_ignore_err( rc ); + + /* if ignore.. */ + if ( first ) { + /* only log if first occurrence */ + if ( ( force < 2 && first > 0 ) || abs(first) == 1 ) { + tester_ldap_error( ld, bindfunc, NULL ); + } + rc = LDAP_SUCCESS; + + } else { + tester_ldap_error( ld, bindfunc, NULL ); + } + } + + switch ( action_type ) { + case -1: + break; + + case TESTER_SEARCH: + { + LDAPURLDesc *ludp = (LDAPURLDesc *)action; + LDAPMessage *res = NULL; + struct timeval tv = { 0 }, *tvp = NULL; + + if ( timelimit ) { + tv.tv_sec = timelimit; + tvp = &tv; + } + + assert( action != NULL ); + + rc = ldap_search_ext_s( ld, + ludp->lud_dn, ludp->lud_scope, + ludp->lud_filter, ludp->lud_attrs, 0, + NULL, NULL, tvp, sizelimit, &res ); + ldap_msgfree( res ); + } break; + + default: + /* nothing to do yet */ + break; + } + + if ( !noinit ) { + ldap_unbind_ext( ld, NULL, NULL ); + ld = NULL; + } + + if ( rc != LDAP_SUCCESS ) { + break; + } + } + + if ( maxloop > 1 ) { + fprintf( stderr, " PID=%ld - Bind done (%d).\n", (long) pid, rc ); + } + + if ( ldp && noinit ) { + *ldp = ld; + + } else if ( ld != NULL ) { + ldap_unbind_ext( ld, NULL, NULL ); + } + + return rc; +} + + +static int +do_base( struct tester_conn_args *config, char *dn, char *base, char *filter, char *pwattr, + int force, int noinit, int action_type, void *action ) +{ + LDAP *ld = NULL; + int i = 0; + int rc = LDAP_SUCCESS; + ber_int_t msgid; + LDAPMessage *res, *msg; + char **dns = NULL; + struct berval *creds = NULL; + char *attrs[] = { LDAP_NO_ATTRS, NULL }; + int ndns = 0; +#ifdef _WIN32 + DWORD beg, end; +#else + struct timeval beg, end; +#endif + char *nullstr = ""; + + tester_init_ld( &ld, config, 0 ); + + fprintf( stderr, "PID=%ld - Bind(%d): base=\"%s\", filter=\"%s\" attr=\"%s\".\n", + (long) pid, config->loops, base, filter, pwattr ); + + if ( pwattr != NULL ) { + attrs[ 0 ] = pwattr; + } + rc = ldap_search_ext( ld, base, LDAP_SCOPE_SUBTREE, + filter, attrs, 0, NULL, NULL, 0, 0, &msgid ); + if ( rc != LDAP_SUCCESS ) { + tester_ldap_error( ld, "ldap_search_ext", NULL ); + exit( EXIT_FAILURE ); + } + + while ( ( rc = ldap_result( ld, LDAP_RES_ANY, LDAP_MSG_ONE, NULL, &res ) ) > 0 ) + { + BerElement *ber; + struct berval bv; + int done = 0; + + for ( msg = ldap_first_message( ld, res ); msg; + msg = ldap_next_message( ld, msg ) ) + { + switch ( ldap_msgtype( msg ) ) { + case LDAP_RES_SEARCH_ENTRY: + rc = ldap_get_dn_ber( ld, msg, &ber, &bv ); + dns = realloc( dns, (ndns + 1)*sizeof(char *) ); + if ( !dns ) { + tester_error( "realloc failed" ); + exit( EXIT_FAILURE ); + } + dns[ndns] = ber_strdup( bv.bv_val ); + if ( pwattr != NULL ) { + struct berval **values = ldap_get_values_len( ld, msg, pwattr ); + + creds = realloc( creds, (ndns + 1)*sizeof(struct berval) ); + if ( !creds ) { + tester_error( "realloc failed" ); + exit( EXIT_FAILURE ); + } + if ( values == NULL ) { +novals:; + creds[ndns].bv_len = 0; + creds[ndns].bv_val = nullstr; + + } else { + static struct berval cleartext = BER_BVC( "{CLEARTEXT} " ); + struct berval value = *values[ 0 ]; + + if ( value.bv_val[ 0 ] == '{' ) { + char *end = ber_bvchr( &value, '}' ); + + if ( end ) { + if ( ber_bvcmp( &value, &cleartext ) == 0 ) { + value.bv_val += cleartext.bv_len; + value.bv_len -= cleartext.bv_len; + + } else { + ldap_value_free_len( values ); + goto novals; + } + } + + } + + ber_dupbv( &creds[ndns], &value ); + ldap_value_free_len( values ); + } + } + ndns++; + ber_free( ber, 0 ); + break; + + case LDAP_RES_SEARCH_RESULT: + done = 1; + break; + } + if ( done ) + break; + } + ldap_msgfree( res ); + if ( done ) break; + } + +#ifdef _WIN32 + beg = GetTickCount(); +#else + gettimeofday( &beg, NULL ); +#endif + + if ( ndns == 0 ) { + tester_error( "No DNs" ); + if ( ld != NULL ) { + ldap_unbind_ext( ld, NULL, NULL ); + } + return 1; + } + + fprintf( stderr, " PID=%ld - Bind base=\"%s\" filter=\"%s\" got %d values.\n", + (long) pid, base, filter, ndns ); + + /* Ok, got list of DNs, now start binding to each */ + for ( i = 0; i < config->loops; i++ ) { + struct berval *pass = &config->pass; + int j; + +#if 0 /* use high-order bits for better randomness (Numerical Recipes in "C") */ + j = rand() % ndns; +#endif + j = ((double)ndns)*rand()/(RAND_MAX + 1.0); + + if ( creds && !BER_BVISEMPTY( &creds[j] ) ) { + pass = &creds[j]; + } + + if ( do_bind( config, dns[j], 1, force, noinit, &ld, pass, + action_type, action ) && !force ) + { + break; + } + } + + if ( ld != NULL ) { + ldap_unbind_ext( ld, NULL, NULL ); + ld = NULL; + } + +#ifdef _WIN32 + end = GetTickCount(); + end -= beg; + + fprintf( stderr, " PID=%ld - Bind done %d in %d.%03d seconds.\n", + (long) pid, i, end / 1000, end % 1000 ); +#else + gettimeofday( &end, NULL ); + end.tv_usec -= beg.tv_usec; + if (end.tv_usec < 0 ) { + end.tv_usec += 1000000; + end.tv_sec -= 1; + } + end.tv_sec -= beg.tv_sec; + + fprintf( stderr, " PID=%ld - Bind done %d in %ld.%06ld seconds.\n", + (long) pid, i, (long) end.tv_sec, (long) end.tv_usec ); +#endif + + if ( dns ) { + for ( i = 0; i < ndns; i++ ) { + ber_memfree( dns[i] ); + } + free( dns ); + } + + if ( creds ) { + for ( i = 0; i < ndns; i++ ) { + if ( creds[i].bv_val != nullstr ) { + ber_memfree( creds[i].bv_val ); + } + } + free( creds ); + } + + return 0; +} diff --git a/tests/progs/slapd-common.c b/tests/progs/slapd-common.c new file mode 100644 index 0000000..d9f509e --- /dev/null +++ b/tests/progs/slapd-common.c @@ -0,0 +1,550 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1999-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This work was initially developed by Howard Chu for inclusion + * in OpenLDAP Software. + */ + +#include "portable.h" + +#include <stdio.h> + +#include "ac/stdlib.h" +#include "ac/unistd.h" +#include "ac/string.h" +#include "ac/errno.h" + +#include "ldap.h" + +#include "lutil.h" +#include "lutil_ldap.h" +#include "ldap_pvt.h" +#include "slapd-common.h" + +/* global vars */ +pid_t pid; +int debug; + +/* static vars */ +static char progname[ BUFSIZ ]; +tester_t progtype; + +/* + * ignore_count[] is indexed by result code: + * negative for OpenLDAP client-side errors, positive for protocol codes. + */ +#define TESTER_CLIENT_FIRST LDAP_REFERRAL_LIMIT_EXCEEDED /* negative */ +#define TESTER_SERVER_LAST LDAP_OTHER +static int ignore_base [ -TESTER_CLIENT_FIRST + TESTER_SERVER_LAST + 1 ]; +#define ignore_count (ignore_base - TESTER_CLIENT_FIRST) + +static const struct { + const char *name; + int err; +} ignore_str2err[] = { + { "OPERATIONS_ERROR", LDAP_OPERATIONS_ERROR }, + { "PROTOCOL_ERROR", LDAP_PROTOCOL_ERROR }, + { "TIMELIMIT_EXCEEDED", LDAP_TIMELIMIT_EXCEEDED }, + { "SIZELIMIT_EXCEEDED", LDAP_SIZELIMIT_EXCEEDED }, + { "COMPARE_FALSE", LDAP_COMPARE_FALSE }, + { "COMPARE_TRUE", LDAP_COMPARE_TRUE }, + { "AUTH_METHOD_NOT_SUPPORTED", LDAP_AUTH_METHOD_NOT_SUPPORTED }, + { "STRONG_AUTH_NOT_SUPPORTED", LDAP_STRONG_AUTH_NOT_SUPPORTED }, + { "STRONG_AUTH_REQUIRED", LDAP_STRONG_AUTH_REQUIRED }, + { "STRONGER_AUTH_REQUIRED", LDAP_STRONGER_AUTH_REQUIRED }, + { "PARTIAL_RESULTS", LDAP_PARTIAL_RESULTS }, + + { "REFERRAL", LDAP_REFERRAL }, + { "ADMINLIMIT_EXCEEDED", LDAP_ADMINLIMIT_EXCEEDED }, + { "UNAVAILABLE_CRITICAL_EXTENSION", LDAP_UNAVAILABLE_CRITICAL_EXTENSION }, + { "CONFIDENTIALITY_REQUIRED", LDAP_CONFIDENTIALITY_REQUIRED }, + { "SASL_BIND_IN_PROGRESS", LDAP_SASL_BIND_IN_PROGRESS }, + + { "NO_SUCH_ATTRIBUTE", LDAP_NO_SUCH_ATTRIBUTE }, + { "UNDEFINED_TYPE", LDAP_UNDEFINED_TYPE }, + { "INAPPROPRIATE_MATCHING", LDAP_INAPPROPRIATE_MATCHING }, + { "CONSTRAINT_VIOLATION", LDAP_CONSTRAINT_VIOLATION }, + { "TYPE_OR_VALUE_EXISTS", LDAP_TYPE_OR_VALUE_EXISTS }, + { "INVALID_SYNTAX", LDAP_INVALID_SYNTAX }, + + { "NO_SUCH_OBJECT", LDAP_NO_SUCH_OBJECT }, + { "ALIAS_PROBLEM", LDAP_ALIAS_PROBLEM }, + { "INVALID_DN_SYNTAX", LDAP_INVALID_DN_SYNTAX }, + { "IS_LEAF", LDAP_IS_LEAF }, + { "ALIAS_DEREF_PROBLEM", LDAP_ALIAS_DEREF_PROBLEM }, + + /* obsolete */ + { "PROXY_AUTHZ_FAILURE", LDAP_X_PROXY_AUTHZ_FAILURE }, + { "INAPPROPRIATE_AUTH", LDAP_INAPPROPRIATE_AUTH }, + { "INVALID_CREDENTIALS", LDAP_INVALID_CREDENTIALS }, + { "INSUFFICIENT_ACCESS", LDAP_INSUFFICIENT_ACCESS }, + + { "BUSY", LDAP_BUSY }, + { "UNAVAILABLE", LDAP_UNAVAILABLE }, + { "UNWILLING_TO_PERFORM", LDAP_UNWILLING_TO_PERFORM }, + { "LOOP_DETECT", LDAP_LOOP_DETECT }, + + { "NAMING_VIOLATION", LDAP_NAMING_VIOLATION }, + { "OBJECT_CLASS_VIOLATION", LDAP_OBJECT_CLASS_VIOLATION }, + { "NOT_ALLOWED_ON_NONLEAF", LDAP_NOT_ALLOWED_ON_NONLEAF }, + { "NOT_ALLOWED_ON_RDN", LDAP_NOT_ALLOWED_ON_RDN }, + { "ALREADY_EXISTS", LDAP_ALREADY_EXISTS }, + { "NO_OBJECT_CLASS_MODS", LDAP_NO_OBJECT_CLASS_MODS }, + { "RESULTS_TOO_LARGE", LDAP_RESULTS_TOO_LARGE }, + { "AFFECTS_MULTIPLE_DSAS", LDAP_AFFECTS_MULTIPLE_DSAS }, + + { "OTHER", LDAP_OTHER }, + + { "SERVER_DOWN", LDAP_SERVER_DOWN }, + { "LOCAL_ERROR", LDAP_LOCAL_ERROR }, + { "ENCODING_ERROR", LDAP_ENCODING_ERROR }, + { "DECODING_ERROR", LDAP_DECODING_ERROR }, + { "TIMEOUT", LDAP_TIMEOUT }, + { "AUTH_UNKNOWN", LDAP_AUTH_UNKNOWN }, + { "FILTER_ERROR", LDAP_FILTER_ERROR }, + { "USER_CANCELLED", LDAP_USER_CANCELLED }, + { "PARAM_ERROR", LDAP_PARAM_ERROR }, + { "NO_MEMORY", LDAP_NO_MEMORY }, + { "CONNECT_ERROR", LDAP_CONNECT_ERROR }, + { "NOT_SUPPORTED", LDAP_NOT_SUPPORTED }, + { "CONTROL_NOT_FOUND", LDAP_CONTROL_NOT_FOUND }, + { "NO_RESULTS_RETURNED", LDAP_NO_RESULTS_RETURNED }, + { "MORE_RESULTS_TO_RETURN", LDAP_MORE_RESULTS_TO_RETURN }, + { "CLIENT_LOOP", LDAP_CLIENT_LOOP }, + { "REFERRAL_LIMIT_EXCEEDED", LDAP_REFERRAL_LIMIT_EXCEEDED }, + + { NULL } +}; + +#define UNKNOWN_ERR (1234567890) + +#define RETRIES 0 +#define LOOPS 100 + +static int +tester_ignore_str2err( const char *err ) +{ + int i, ignore = 1; + + if ( strcmp( err, "ALL" ) == 0 ) { + for ( i = 0; ignore_str2err[ i ].name != NULL; i++ ) { + ignore_count[ ignore_str2err[ i ].err ] = 1; + } + ignore_count[ LDAP_SUCCESS ] = 0; + + return 0; + } + + if ( err[ 0 ] == '!' ) { + ignore = 0; + err++; + + } else if ( err[ 0 ] == '*' ) { + ignore = -1; + err++; + } + + for ( i = 0; ignore_str2err[ i ].name != NULL; i++ ) { + if ( strcmp( err, ignore_str2err[ i ].name ) == 0 ) { + int err = ignore_str2err[ i ].err; + + if ( err != LDAP_SUCCESS ) { + ignore_count[ err ] = ignore; + } + + return err; + } + } + + return UNKNOWN_ERR; +} + +int +tester_ignore_str2errlist( const char *err ) +{ + int i; + char **errs = ldap_str2charray( err, "," ); + + for ( i = 0; errs[ i ] != NULL; i++ ) { + /* TODO: allow <err>:<prog> to ignore <err> only when <prog> */ + (void)tester_ignore_str2err( errs[ i ] ); + } + + ldap_charray_free( errs ); + + return 0; +} + +int +tester_ignore_err( int err ) +{ + int rc = 1; + + if ( err && TESTER_CLIENT_FIRST <= err && err <= TESTER_SERVER_LAST ) { + rc = ignore_count[ err ]; + if ( rc != 0 ) { + ignore_count[ err ] = rc + (rc > 0 ? 1 : -1); + } + } + + /* SUCCESS is always "ignored" */ + return rc; +} + +struct tester_conn_args * +tester_init( const char *pname, tester_t ptype ) +{ + static struct tester_conn_args config = { + .authmethod = -1, + .retries = RETRIES, + .loops = LOOPS, + .outerloops = 1, + + .uri = NULL, + }; + + pid = getpid(); + srand( pid ); + snprintf( progname, sizeof( progname ), "%s PID=%d", pname, pid ); + progtype = ptype; + + return &config; +} + +void +tester_ldap_error( LDAP *ld, const char *fname, const char *msg ) +{ + int err; + char *text = NULL; + LDAPControl **ctrls = NULL; + + ldap_get_option( ld, LDAP_OPT_RESULT_CODE, (void *)&err ); + if ( err != LDAP_SUCCESS ) { + ldap_get_option( ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void *)&text ); + } + + fprintf( stderr, "%s: %s: %s (%d) %s %s\n", + progname, fname, ldap_err2string( err ), err, + text == NULL ? "" : text, + msg ? msg : "" ); + + if ( text ) { + ldap_memfree( text ); + text = NULL; + } + + ldap_get_option( ld, LDAP_OPT_MATCHED_DN, (void *)&text ); + if ( text != NULL ) { + if ( text[ 0 ] != '\0' ) { + fprintf( stderr, "\tmatched: %s\n", text ); + } + ldap_memfree( text ); + text = NULL; + } + + ldap_get_option( ld, LDAP_OPT_SERVER_CONTROLS, (void *)&ctrls ); + if ( ctrls != NULL ) { + int i; + + fprintf( stderr, "\tcontrols:\n" ); + for ( i = 0; ctrls[ i ] != NULL; i++ ) { + fprintf( stderr, "\t\t%s\n", ctrls[ i ]->ldctl_oid ); + } + ldap_controls_free( ctrls ); + ctrls = NULL; + } + + if ( err == LDAP_REFERRAL ) { + char **refs = NULL; + + ldap_get_option( ld, LDAP_OPT_REFERRAL_URLS, (void *)&refs ); + + if ( refs ) { + int i; + + fprintf( stderr, "\treferral:\n" ); + for ( i = 0; refs[ i ] != NULL; i++ ) { + fprintf( stderr, "\t\t%s\n", refs[ i ] ); + } + + ber_memvfree( (void **)refs ); + } + } +} + +void +tester_perror( const char *fname, const char *msg ) +{ + int save_errno = errno; + char buf[ BUFSIZ ]; + + fprintf( stderr, "%s: %s: (%d) %s %s\n", + progname, fname, save_errno, + AC_STRERROR_R( save_errno, buf, sizeof( buf ) ), + msg ? msg : "" ); +} + +int +tester_config_opt( struct tester_conn_args *config, char opt, char *optarg ) +{ + switch ( opt ) { + case 'C': + config->chaserefs++; + break; + + case 'D': + config->binddn = optarg; + break; + + case 'd': + { + if ( lutil_atoi( &debug, optarg ) != 0 ) { + return -1; + } + + if ( ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &debug ) + != LBER_OPT_SUCCESS ) + { + fprintf( stderr, + "Could not set LBER_OPT_DEBUG_LEVEL %d\n", debug ); + } + + if ( ldap_set_option( NULL, LDAP_OPT_DEBUG_LEVEL, &debug ) + != LDAP_OPT_SUCCESS ) + { + fprintf( stderr, + "Could not set LDAP_OPT_DEBUG_LEVEL %d\n", debug ); + } + break; + } + + case 'H': + config->uri = optarg; + break; + + case 'i': + tester_ignore_str2errlist( optarg ); + break; + + case 'L': + if ( lutil_atoi( &config->outerloops, optarg ) != 0 ) { + return -1; + } + break; + + case 'l': + if ( lutil_atoi( &config->loops, optarg ) != 0 ) { + return -1; + } + break; + +#ifdef HAVE_CYRUS_SASL + case 'O': + if ( config->secprops != NULL ) { + return -1; + } + if ( config->authmethod != -1 && config->authmethod != LDAP_AUTH_SASL ) { + return -1; + } + config->authmethod = LDAP_AUTH_SASL; + config->secprops = optarg; + break; + + case 'R': + if ( config->realm != NULL ) { + return -1; + } + if ( config->authmethod != -1 && config->authmethod != LDAP_AUTH_SASL ) { + return -1; + } + config->authmethod = LDAP_AUTH_SASL; + config->realm = optarg; + break; + + case 'U': + if ( config->authc_id != NULL ) { + return -1; + } + if ( config->authmethod != -1 && config->authmethod != LDAP_AUTH_SASL ) { + return -1; + } + config->authmethod = LDAP_AUTH_SASL; + config->authc_id = optarg; + break; + + case 'X': + if ( config->authz_id != NULL ) { + return -1; + } + if ( config->authmethod != -1 && config->authmethod != LDAP_AUTH_SASL ) { + return -1; + } + config->authmethod = LDAP_AUTH_SASL; + config->authz_id = optarg; + break; + + case 'Y': + if ( config->mech != NULL ) { + return -1; + } + if ( config->authmethod != -1 && config->authmethod != LDAP_AUTH_SASL ) { + return -1; + } + config->authmethod = LDAP_AUTH_SASL; + config->mech = optarg; + break; +#endif + + case 'r': + if ( lutil_atoi( &config->retries, optarg ) != 0 ) { + return -1; + } + break; + + case 't': + if ( lutil_atoi( &config->delay, optarg ) != 0 ) { + return -1; + } + break; + + case 'w': + config->pass.bv_val = strdup( optarg ); + config->pass.bv_len = strlen( optarg ); + memset( optarg, '*', config->pass.bv_len ); + break; + + case 'x': + if ( config->authmethod != -1 && config->authmethod != LDAP_AUTH_SIMPLE ) { + return -1; + } + config->authmethod = LDAP_AUTH_SIMPLE; + break; + + default: + return -1; + } + + return LDAP_SUCCESS; +} + +void +tester_config_finish( struct tester_conn_args *config ) +{ + if ( config->authmethod == -1 ) { +#ifdef HAVE_CYRUS_SASL + if ( config->binddn != NULL ) { + config->authmethod = LDAP_AUTH_SIMPLE; + } else { + config->authmethod = LDAP_AUTH_SASL; + } +#else + config->authmethod = LDAP_AUTH_SIMPLE; +#endif + } + +#ifdef HAVE_CYRUS_SASL + if ( config->authmethod == LDAP_AUTH_SASL ) { + config->defaults = lutil_sasl_defaults( NULL, + config->mech, + config->realm, + config->authc_id, + config->pass.bv_val, + config->authz_id ); + + if ( config->defaults == NULL ) { + tester_error( "unable to prepare SASL defaults" ); + exit( EXIT_FAILURE ); + } + } +#endif +} + +void +tester_init_ld( LDAP **ldp, struct tester_conn_args *config, int flags ) +{ + LDAP *ld; + int rc, do_retry = config->retries; + int version = LDAP_VERSION3; + +retry:; + ldap_initialize( &ld, config->uri ); + if ( ld == NULL ) { + tester_perror( "ldap_initialize", NULL ); + exit( EXIT_FAILURE ); + } + + (void) ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version ); + (void) ldap_set_option( ld, LDAP_OPT_REFERRALS, + config->chaserefs ? LDAP_OPT_ON: LDAP_OPT_OFF ); + + if ( !( flags & TESTER_INIT_ONLY ) ) { + if ( config->authmethod == LDAP_AUTH_SASL ) { +#ifdef HAVE_CYRUS_SASL + if ( config->secprops != NULL ) { + rc = ldap_set_option( ld, + LDAP_OPT_X_SASL_SECPROPS, config->secprops ); + + if ( rc != LDAP_OPT_SUCCESS ) { + tester_ldap_error( ld, "ldap_set_option(SECPROPS)", NULL ); + ldap_unbind_ext( ld, NULL, NULL ); + exit( EXIT_FAILURE ); + } + } + + rc = ldap_sasl_interactive_bind_s( ld, + config->binddn, + config->mech, + NULL, NULL, + LDAP_SASL_QUIET, + lutil_sasl_interact, + config->defaults ); +#else /* HAVE_CYRUS_SASL */ + /* caller shouldn't have allowed this */ + assert(0); +#endif + } else if ( config->authmethod == LDAP_AUTH_SIMPLE ) { + rc = ldap_sasl_bind_s( ld, + config->binddn, LDAP_SASL_SIMPLE, + &config->pass, NULL, NULL, NULL ); + } + + if ( rc != LDAP_SUCCESS ) { + tester_ldap_error( ld, "ldap_sasl_bind_s", NULL ); + switch ( rc ) { + case LDAP_BUSY: + case LDAP_UNAVAILABLE: + if ( do_retry > 0 ) { + do_retry--; + if ( config->delay > 0 ) { + sleep( config->delay ); + } + goto retry; + } + } + ldap_unbind_ext( ld, NULL, NULL ); + ld = NULL; + if ( !( flags & TESTER_INIT_NOEXIT )) + exit( EXIT_FAILURE ); + } + } + + *ldp = ld; +} + +void +tester_error( const char *msg ) +{ + fprintf( stderr, "%s: %s\n", progname, msg ); +} diff --git a/tests/progs/slapd-common.h b/tests/progs/slapd-common.h new file mode 100644 index 0000000..64410c7 --- /dev/null +++ b/tests/progs/slapd-common.h @@ -0,0 +1,92 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1999-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This work was initially developed by Howard Chu for inclusion + * in OpenLDAP Software. + */ + +#ifndef SLAPD_COMMON_H +#define SLAPD_COMMON_H + +typedef enum { + TESTER_TESTER, + TESTER_ADDEL, + TESTER_BIND, + TESTER_MODIFY, + TESTER_MODRDN, + TESTER_READ, + TESTER_SEARCH, + TESTER_LAST +} tester_t; + +extern struct tester_conn_args * tester_init( const char *pname, tester_t ptype ); +extern char * tester_uri( char *uri ); +extern void tester_error( const char *msg ); +extern void tester_perror( const char *fname, const char *msg ); +extern void tester_ldap_error( LDAP *ld, const char *fname, const char *msg ); +extern int tester_ignore_str2errlist( const char *err ); +extern int tester_ignore_err( int err ); + +struct tester_conn_args { + char *uri; + + int outerloops; + int loops; + int retries; + int delay; + + int chaserefs; + + int authmethod; + + char *binddn; + struct berval pass; + +#ifdef HAVE_CYRUS_SASL + char *mech; + char *realm; + char *authz_id; + char *authc_id; + char *secprops; + void *defaults; +#endif +}; + +#define TESTER_INIT_ONLY (1 << 0) +#define TESTER_INIT_NOEXIT (1 << 1) +#define TESTER_COMMON_OPTS "CD:d:H:L:l:i:O:R:U:X:Y:r:t:w:x" +#define TESTER_COMMON_HELP \ + "[-C] " \ + "[-D <dn> [-w <passwd>]] " \ + "[-d <level>] " \ + "[-H <uri>]" \ + "[-i <ignore>] " \ + "[-l <loops>] " \ + "[-L <outerloops>] " \ + "[-r <maxretries>] " \ + "[-t <delay>] " \ + "[-O <SASL secprops>] " \ + "[-R <SASL realm>] " \ + "[-U <SASL authcid> [-X <SASL authzid>]] " \ + "[-x | -Y <SASL mech>] " + +extern int tester_config_opt( struct tester_conn_args *config, char opt, char *optarg ); +extern void tester_config_finish( struct tester_conn_args *config ); +extern void tester_init_ld( LDAP **ldp, struct tester_conn_args *conf, int flags ); + +extern pid_t pid; +extern int debug; + +#endif /* SLAPD_COMMON_H */ diff --git a/tests/progs/slapd-modify.c b/tests/progs/slapd-modify.c new file mode 100644 index 0000000..acc131a --- /dev/null +++ b/tests/progs/slapd-modify.c @@ -0,0 +1,225 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1999-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> + +#include "ac/stdlib.h" + +#include "ac/ctype.h" +#include "ac/param.h" +#include "ac/socket.h" +#include "ac/string.h" +#include "ac/unistd.h" +#include "ac/wait.h" + +#include "ldap.h" +#include "lutil.h" + +#include "slapd-common.h" + +#define LOOPS 100 + +static void +do_modify( struct tester_conn_args *config, char *entry, + char *attr, char *value, int friendly ); + + +static void +usage( char *name, int opt ) +{ + if ( opt ) { + fprintf( stderr, "%s: unable to handle option \'%c\'\n\n", + name, opt ); + } + + fprintf( stderr, "usage: %s " TESTER_COMMON_HELP + "-a <attr:val> " + "-e <entry> " + "[-F]\n", + name ); + exit( EXIT_FAILURE ); +} + +int +main( int argc, char **argv ) +{ + int i; + char *entry = NULL; + char *ava = NULL; + char *value = NULL; + int friendly = 0; + struct tester_conn_args *config; + + config = tester_init( "slapd-modify", TESTER_MODIFY ); + + while ( ( i = getopt( argc, argv, TESTER_COMMON_OPTS "a:e:F" ) ) != EOF ) + { + switch ( i ) { + case 'F': + friendly++; + break; + + case 'i': + /* ignored (!) by now */ + break; + + case 'e': /* entry to modify */ + entry = optarg; + break; + + case 'a': + ava = optarg; + break; + + default: + if ( tester_config_opt( config, i, optarg ) == LDAP_SUCCESS ) { + break; + } + usage( argv[0], i ); + break; + } + } + + if (( entry == NULL ) || ( ava == NULL )) + usage( argv[0], 0 ); + + if ( *entry == '\0' ) { + + fprintf( stderr, "%s: invalid EMPTY entry DN.\n", + argv[0] ); + exit( EXIT_FAILURE ); + + } + if ( *ava == '\0' ) { + fprintf( stderr, "%s: invalid EMPTY AVA.\n", + argv[0] ); + exit( EXIT_FAILURE ); + } + + if ( !( value = strchr( ava, ':' ))) { + fprintf( stderr, "%s: invalid AVA.\n", + argv[0] ); + exit( EXIT_FAILURE ); + } + *value++ = '\0'; + while ( *value && isspace( (unsigned char) *value )) + value++; + + tester_config_finish( config ); + + for ( i = 0; i < config->outerloops; i++ ) { + do_modify( config, entry, ava, value, friendly ); + } + + exit( EXIT_SUCCESS ); +} + + +static void +do_modify( struct tester_conn_args *config, + char *entry, char* attr, char* value, int friendly ) +{ + LDAP *ld = NULL; + int i = 0, do_retry = config->retries; + int rc = LDAP_SUCCESS; + + struct ldapmod mod; + struct ldapmod *mods[2]; + char *values[2]; + + values[0] = value; + values[1] = NULL; + mod.mod_op = LDAP_MOD_ADD; + mod.mod_type = attr; + mod.mod_values = values; + mods[0] = &mod; + mods[1] = NULL; + +retry:; + if ( ld == NULL ) { + tester_init_ld( &ld, config, 0 ); + } + + if ( do_retry == config->retries ) { + fprintf( stderr, "PID=%ld - Modify(%d): entry=\"%s\".\n", + (long) pid, config->loops, entry ); + } + + for ( ; i < config->loops; i++ ) { + mod.mod_op = LDAP_MOD_ADD; + rc = ldap_modify_ext_s( ld, entry, mods, NULL, NULL ); + if ( rc != LDAP_SUCCESS ) { + tester_ldap_error( ld, "ldap_modify_ext_s", NULL ); + switch ( rc ) { + case LDAP_TYPE_OR_VALUE_EXISTS: + /* NOTE: this likely means + * the second modify failed + * during the previous round... */ + if ( !friendly ) { + goto done; + } + break; + + case LDAP_BUSY: + case LDAP_UNAVAILABLE: + if ( do_retry > 0 ) { + do_retry--; + goto retry; + } + /* fall thru */ + + default: + goto done; + } + } + + mod.mod_op = LDAP_MOD_DELETE; + rc = ldap_modify_ext_s( ld, entry, mods, NULL, NULL ); + if ( rc != LDAP_SUCCESS ) { + tester_ldap_error( ld, "ldap_modify_ext_s", NULL ); + switch ( rc ) { + case LDAP_NO_SUCH_ATTRIBUTE: + /* NOTE: this likely means + * the first modify failed + * during the previous round... */ + if ( !friendly ) { + goto done; + } + break; + + case LDAP_BUSY: + case LDAP_UNAVAILABLE: + if ( do_retry > 0 ) { + do_retry--; + goto retry; + } + /* fall thru */ + + default: + goto done; + } + } + + } + +done:; + fprintf( stderr, " PID=%ld - Modify done (%d).\n", (long) pid, rc ); + + ldap_unbind_ext( ld, NULL, NULL ); +} + + diff --git a/tests/progs/slapd-modrdn.c b/tests/progs/slapd-modrdn.c new file mode 100644 index 0000000..e224c0a --- /dev/null +++ b/tests/progs/slapd-modrdn.c @@ -0,0 +1,229 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1999-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This work was initially developed by Howard Chu, based in part + * on other OpenLDAP test tools, for inclusion in OpenLDAP Software. + */ + +#include "portable.h" + +#include <stdio.h> + +#include "ac/stdlib.h" + +#include "ac/ctype.h" +#include "ac/param.h" +#include "ac/socket.h" +#include "ac/string.h" +#include "ac/unistd.h" +#include "ac/wait.h" + +#include "ldap.h" +#include "lutil.h" + +#include "slapd-common.h" + +#define LOOPS 100 +#define RETRIES 0 + +static void +do_modrdn( struct tester_conn_args *config, + char *entry, int friendly ); + +static void +usage( char *name, char opt ) +{ + if ( opt ) { + fprintf( stderr, "%s: unable to handle option \'%c\'\n\n", + name, opt ); + } + + fprintf( stderr, "usage: %s " TESTER_COMMON_HELP + "-e <entry> " + "[-F]\n", + name ); + exit( EXIT_FAILURE ); +} + +int +main( int argc, char **argv ) +{ + int i; + char *entry = NULL; + int friendly = 0; + struct tester_conn_args *config; + + config = tester_init( "slapd-modrdn", TESTER_MODRDN ); + + while ( ( i = getopt( argc, argv, TESTER_COMMON_OPTS "e:F" ) ) != EOF ) + { + switch ( i ) { + case 'F': + friendly++; + break; + + case 'i': + /* ignored (!) by now */ + break; + + case 'e': /* entry to rename */ + entry = optarg; + break; + + default: + if ( tester_config_opt( config, i, optarg ) == LDAP_SUCCESS ) { + break; + } + usage( argv[0], i ); + break; + } + } + + if ( entry == NULL ) + usage( argv[0], 0 ); + + if ( *entry == '\0' ) { + + fprintf( stderr, "%s: invalid EMPTY entry DN.\n", + argv[0] ); + exit( EXIT_FAILURE ); + + } + + tester_config_finish( config ); + + for ( i = 0; i < config->outerloops; i++ ) { + do_modrdn( config, entry, friendly ); + } + + exit( EXIT_SUCCESS ); +} + + +static void +do_modrdn( struct tester_conn_args *config, + char *entry, int friendly ) +{ + LDAP *ld = NULL; + int i, do_retry = config->retries; + char *DNs[2]; + char *rdns[2]; + int rc = LDAP_SUCCESS; + char *p1, *p2; + + DNs[0] = entry; + DNs[1] = strdup( entry ); + if ( DNs[1] == NULL ) { + tester_error( "strdup failed" ); + exit( EXIT_FAILURE ); + } + + /* reverse the RDN, make new DN */ + p1 = strchr( entry, '=' ) + 1; + p2 = strchr( p1, ',' ); + + *p2 = '\0'; + rdns[1] = strdup( entry ); + if ( rdns[1] == NULL ) { + tester_error( "strdup failed" ); + exit( EXIT_FAILURE ); + } + *p2-- = ','; + + for (i = p1 - entry;p2 >= p1;) + DNs[1][i++] = *p2--; + + DNs[1][i] = '\0'; + rdns[0] = strdup( DNs[1] ); + if ( rdns[0] == NULL ) { + tester_error( "strdup failed" ); + exit( EXIT_FAILURE ); + } + DNs[1][i] = ','; + + i = 0; + +retry:; + if ( ld == NULL ) { + tester_init_ld( &ld, config, 0 ); + } + + if ( do_retry == config->retries ) { + fprintf( stderr, "PID=%ld - Modrdn(%d): entry=\"%s\".\n", + (long) pid, config->loops, entry ); + } + + for ( ; i < config->loops; i++ ) { + rc = ldap_rename_s( ld, DNs[0], rdns[0], NULL, 0, NULL, NULL ); + if ( rc != LDAP_SUCCESS ) { + tester_ldap_error( ld, "ldap_rename_s", NULL ); + switch ( rc ) { + case LDAP_NO_SUCH_OBJECT: + /* NOTE: this likely means + * the second modrdn failed + * during the previous round... */ + if ( !friendly ) { + goto done; + } + break; + + case LDAP_BUSY: + case LDAP_UNAVAILABLE: + if ( do_retry > 0 ) { + do_retry--; + goto retry; + } + /* fall thru */ + + default: + goto done; + } + } + rc = ldap_rename_s( ld, DNs[1], rdns[1], NULL, 1, NULL, NULL ); + if ( rc != LDAP_SUCCESS ) { + tester_ldap_error( ld, "ldap_rename_s", NULL ); + switch ( rc ) { + case LDAP_NO_SUCH_OBJECT: + /* NOTE: this likely means + * the first modrdn failed + * during the previous round... */ + if ( !friendly ) { + goto done; + } + break; + + case LDAP_BUSY: + case LDAP_UNAVAILABLE: + if ( do_retry > 0 ) { + do_retry--; + goto retry; + } + /* fall thru */ + + default: + goto done; + } + } + } + +done:; + fprintf( stderr, " PID=%ld - Modrdn done (%d).\n", (long) pid, rc ); + + ldap_unbind_ext( ld, NULL, NULL ); + + free( DNs[1] ); + free( rdns[0] ); + free( rdns[1] ); +} diff --git a/tests/progs/slapd-mtread.c b/tests/progs/slapd-mtread.c new file mode 100644 index 0000000..587d3cf --- /dev/null +++ b/tests/progs/slapd-mtread.c @@ -0,0 +1,722 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1999-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This work was initially developed by Kurt Spanier for inclusion + * in OpenLDAP Software. + */ + +/* + * This tool is a MT reader. It behaves like slapd-read however + * with one or more threads simultaneously using the same connection. + * If -M is enabled, then M threads will also perform write operations. + */ + +#include "portable.h" + +/* Requires libldap with threads */ +#ifndef NO_THREADS + +#include <stdio.h> +#include "ldap_pvt_thread.h" + +#include "ac/stdlib.h" + +#include "ac/ctype.h" +#include "ac/param.h" +#include "ac/socket.h" +#include "ac/string.h" +#include "ac/unistd.h" +#include "ac/wait.h" + +#include "ldap.h" +#include "lutil.h" + +#include "ldap_pvt.h" + +#include "slapd-common.h" + +#define MAXCONN 512 +#define LOOPS 100 +#define RETRIES 0 +#define DEFAULT_BASE "ou=people,dc=example,dc=com" + +static void +do_read( LDAP *ld, char *entry, + char **attrs, int noattrs, int nobind, int maxloop, + int force, int idx ); + +static void +do_random( LDAP *ld, + char *sbase, char *filter, char **attrs, int noattrs, int nobind, + int force, int idx ); + +static void +do_random2( LDAP *ld, + char *sbase, char *filter, char **attrs, int noattrs, int nobind, + int force, int idx ); + +static void * +do_onethread( void *arg ); + +static void * +do_onerwthread( void *arg ); + +#define MAX_THREAD 1024 +/* Use same array for readers and writers, offset writers by MAX_THREAD */ +int rt_pass[MAX_THREAD*2]; +int rt_fail[MAX_THREAD*2]; +int *rwt_pass = rt_pass + MAX_THREAD; +int *rwt_fail = rt_fail + MAX_THREAD; +ldap_pvt_thread_t rtid[MAX_THREAD*2], *rwtid = rtid + MAX_THREAD; + +/* + * Shared globals (command line args) + */ +LDAP *ld = NULL; +struct tester_conn_args *config; +char *entry = NULL; +char *filter = NULL; +int force = 0; +char *srchattrs[] = { "1.1", NULL }; +char **attrs = srchattrs; +int noattrs = 0; +int nobind = 0; +int threads = 1; +int rwthreads = 0; +int verbose = 0; + +int noconns = 1; +LDAP **lds = NULL; + +static void +thread_error(int idx, char *string) +{ + char thrstr[BUFSIZ]; + + snprintf(thrstr, BUFSIZ, "error on tidx: %d: %s", idx, string); + tester_error( thrstr ); +} + +static void +thread_output(int idx, char *string) +{ + char thrstr[BUFSIZ]; + + snprintf(thrstr, BUFSIZ, "tidx: %d says: %s", idx, string); + tester_error( thrstr ); +} + +static void +thread_verbose(int idx, char *string) +{ + char thrstr[BUFSIZ]; + + if (!verbose) + return; + snprintf(thrstr, BUFSIZ, "tidx: %d says: %s", idx, string); + tester_error( thrstr ); +} + +static void +usage( char *name, char opt ) +{ + if ( opt ) { + fprintf( stderr, "%s: unable to handle option \'%c\'\n\n", + name, opt ); + } + + fprintf( stderr, "usage: %s " TESTER_COMMON_HELP + "-e <entry> " + "[-A] " + "[-F] " + "[-N] " + "[-v] " + "[-c connections] " + "[-f filter] " + "[-m threads] " + "[-M threads] " + "[-T <attrs>] " + "[<attrs>] " + "\n", + name ); + exit( EXIT_FAILURE ); +} + +int +main( int argc, char **argv ) +{ + int i; + char *uri = NULL; + char *manager = NULL; + struct berval passwd = { 0, NULL }; + char outstr[BUFSIZ]; + int ptpass; + int testfail = 0; + + config = tester_init( "slapd-mtread", TESTER_READ ); + + /* by default, tolerate referrals and no such object */ + tester_ignore_str2errlist( "REFERRAL,NO_SUCH_OBJECT" ); + + while ( (i = getopt( argc, argv, TESTER_COMMON_OPTS "Ac:e:Ff:M:m:NT:v" )) != EOF ) { + switch ( i ) { + case 'A': + noattrs++; + break; + + case 'N': + nobind = TESTER_INIT_ONLY; + break; + + case 'v': + verbose++; + break; + + case 'c': /* the number of connections */ + if ( lutil_atoi( &noconns, optarg ) != 0 ) { + usage( argv[0], i ); + } + break; + + case 'e': /* DN to search for */ + entry = optarg; + break; + + case 'f': /* the search request */ + filter = optarg; + break; + + case 'F': + force++; + break; + + case 'M': /* the number of R/W threads */ + if ( lutil_atoi( &rwthreads, optarg ) != 0 ) { + usage( argv[0], i ); + } + if (rwthreads > MAX_THREAD) + rwthreads = MAX_THREAD; + break; + + case 'm': /* the number of threads */ + if ( lutil_atoi( &threads, optarg ) != 0 ) { + usage( argv[0], i ); + } + if (threads > MAX_THREAD) + threads = MAX_THREAD; + break; + + case 'T': + attrs = ldap_str2charray( optarg, "," ); + if ( attrs == NULL ) { + usage( argv[0], i ); + } + break; + + default: + if ( tester_config_opt( config, i, optarg ) == LDAP_SUCCESS ) { + break; + } + usage( argv[0], i ); + break; + } + } + + if ( entry == NULL ) + usage( argv[0], 0 ); + + if ( *entry == '\0' ) { + fprintf( stderr, "%s: invalid EMPTY entry DN.\n", + argv[0] ); + exit( EXIT_FAILURE ); + } + + if ( argv[optind] != NULL ) { + attrs = &argv[optind]; + } + + if (noconns < 1) + noconns = 1; + if (noconns > MAXCONN) + noconns = MAXCONN; + lds = (LDAP **) calloc( sizeof(LDAP *), noconns); + if (lds == NULL) { + fprintf( stderr, "%s: Memory error: calloc noconns.\n", + argv[0] ); + exit( EXIT_FAILURE ); + } + + tester_config_finish( config ); + ldap_pvt_thread_initialize(); + + for (i = 0; i < noconns; i++) { + tester_init_ld( &lds[i], config, nobind ); + } + + snprintf(outstr, BUFSIZ, "MT Test Start: conns: %d (%s)", noconns, uri); + tester_error(outstr); + snprintf(outstr, BUFSIZ, "Threads: RO: %d RW: %d", threads, rwthreads); + tester_error(outstr); + + /* Set up read only threads */ + for ( i = 0; i < threads; i++ ) { + ldap_pvt_thread_create( &rtid[i], 0, do_onethread, &rtid[i]); + snprintf(outstr, BUFSIZ, "Created RO thread %d", i); + thread_verbose(-1, outstr); + } + /* Set up read/write threads */ + for ( i = 0; i < rwthreads; i++ ) { + ldap_pvt_thread_create( &rwtid[i], 0, do_onerwthread, &rwtid[i]); + snprintf(outstr, BUFSIZ, "Created RW thread %d", i + MAX_THREAD); + thread_verbose(-1, outstr); + } + + ptpass = config->outerloops * config->loops; + + /* wait for read only threads to complete */ + for ( i = 0; i < threads; i++ ) + ldap_pvt_thread_join(rtid[i], NULL); + /* wait for read/write threads to complete */ + for ( i = 0; i < rwthreads; i++ ) + ldap_pvt_thread_join(rwtid[i], NULL); + + for(i = 0; i < noconns; i++) { + if ( lds[i] != NULL ) { + ldap_unbind_ext( lds[i], NULL, NULL ); + } + } + free( lds ); + + for ( i = 0; i < threads; i++ ) { + snprintf(outstr, BUFSIZ, "RO thread %d pass=%d fail=%d", i, + rt_pass[i], rt_fail[i]); + tester_error(outstr); + if (rt_fail[i] != 0 || rt_pass[i] != ptpass) { + snprintf(outstr, BUFSIZ, "FAIL RO thread %d", i); + tester_error(outstr); + testfail++; + } + } + for ( i = 0; i < rwthreads; i++ ) { + snprintf(outstr, BUFSIZ, "RW thread %d pass=%d fail=%d", i + MAX_THREAD, + rwt_pass[i], rwt_fail[i]); + tester_error(outstr); + if (rwt_fail[i] != 0 || rwt_pass[i] != ptpass) { + snprintf(outstr, BUFSIZ, "FAIL RW thread %d", i); + tester_error(outstr); + testfail++; + } + } + snprintf(outstr, BUFSIZ, "MT Test complete" ); + tester_error(outstr); + + if (testfail) + exit( EXIT_FAILURE ); + exit( EXIT_SUCCESS ); +} + +static void * +do_onethread( void *arg ) +{ + int i, j, thisconn; + LDAP **mlds; + char thrstr[BUFSIZ]; + int rc, refcnt = 0; + int idx = (ldap_pvt_thread_t *)arg - rtid; + + mlds = (LDAP **) calloc( sizeof(LDAP *), noconns); + if (mlds == NULL) { + thread_error( idx, "Memory error: thread calloc for noconns" ); + exit( EXIT_FAILURE ); + } + + for ( j = 0; j < config->outerloops; j++ ) { + for(i = 0; i < noconns; i++) { + mlds[i] = ldap_dup(lds[i]); + if (mlds[i] == NULL) { + thread_error( idx, "ldap_dup error" ); + } + } + rc = ldap_get_option(mlds[0], LDAP_OPT_SESSION_REFCNT, &refcnt); + snprintf(thrstr, BUFSIZ, + "RO Thread conns: %d refcnt: %d (rc = %d)", + noconns, refcnt, rc); + thread_verbose(idx, thrstr); + + thisconn = (idx + j) % noconns; + if (thisconn < 0 || thisconn >= noconns) + thisconn = 0; + if (mlds[thisconn] == NULL) { + thread_error( idx, "(failed to dup)"); + tester_perror( "ldap_dup", "(failed to dup)" ); + exit( EXIT_FAILURE ); + } + snprintf(thrstr, BUFSIZ, "Using conn %d", thisconn); + thread_verbose(idx, thrstr); + if ( filter != NULL ) { + if (strchr(filter, '[')) + do_random2( mlds[thisconn], entry, filter, attrs, + noattrs, nobind, force, idx ); + else + do_random( mlds[thisconn], entry, filter, attrs, + noattrs, nobind, force, idx ); + + } else { + do_read( mlds[thisconn], entry, attrs, noattrs, + nobind, config->loops, force, idx ); + } + for(i = 0; i < noconns; i++) { + (void) ldap_destroy(mlds[i]); + mlds[i] = NULL; + } + } + free( mlds ); + return( NULL ); +} + +static void * +do_onerwthread( void *arg ) +{ + int i, j, thisconn; + LDAP **mlds, *ld; + char thrstr[BUFSIZ]; + char dn[256], uids[32], cns[32], *base; + LDAPMod *attrp[5], attrs[4]; + char *oc_vals[] = { "top", "OpenLDAPperson", NULL }; + char *cn_vals[] = { NULL, NULL }; + char *sn_vals[] = { NULL, NULL }; + char *uid_vals[] = { NULL, NULL }; + int ret; + int adds = 0; + int dels = 0; + int rc, refcnt = 0; + int idx = (ldap_pvt_thread_t *)arg - rtid; + + mlds = (LDAP **) calloc( sizeof(LDAP *), noconns); + if (mlds == NULL) { + thread_error( idx, "Memory error: thread calloc for noconns" ); + exit( EXIT_FAILURE ); + } + + snprintf(uids, sizeof(uids), "rwtest%04d", idx); + snprintf(cns, sizeof(cns), "rwtest%04d", idx); + /* add setup */ + for (i = 0; i < 4; i++) { + attrp[i] = &attrs[i]; + attrs[i].mod_op = 0; + } + attrp[4] = NULL; + attrs[0].mod_type = "objectClass"; + attrs[0].mod_values = oc_vals; + attrs[1].mod_type = "cn"; + attrs[1].mod_values = cn_vals; + cn_vals[0] = &cns[0]; + attrs[2].mod_type = "sn"; + attrs[2].mod_values = sn_vals; + sn_vals[0] = &cns[0]; + attrs[3].mod_type = "uid"; + attrs[3].mod_values = uid_vals; + uid_vals[0] = &uids[0]; + + for ( j = 0; j < config->outerloops; j++ ) { + for(i = 0; i < noconns; i++) { + mlds[i] = ldap_dup(lds[i]); + if (mlds[i] == NULL) { + thread_error( idx, "ldap_dup error" ); + } + } + rc = ldap_get_option(mlds[0], LDAP_OPT_SESSION_REFCNT, &refcnt); + snprintf(thrstr, BUFSIZ, + "RW Thread conns: %d refcnt: %d (rc = %d)", + noconns, refcnt, rc); + thread_verbose(idx, thrstr); + + thisconn = (idx + j) % noconns; + if (thisconn < 0 || thisconn >= noconns) + thisconn = 0; + if (mlds[thisconn] == NULL) { + thread_error( idx, "(failed to dup)"); + tester_perror( "ldap_dup", "(failed to dup)" ); + exit( EXIT_FAILURE ); + } + snprintf(thrstr, BUFSIZ, "START RW Thread using conn %d", thisconn); + thread_verbose(idx, thrstr); + + ld = mlds[thisconn]; + if (entry != NULL) + base = entry; + else + base = DEFAULT_BASE; + snprintf(dn, 256, "cn=%s,%s", cns, base); + + adds = 0; + dels = 0; + for (i = 0; i < config->loops; i++) { + ret = ldap_add_ext_s(ld, dn, &attrp[0], NULL, NULL); + if (ret == LDAP_SUCCESS) { + adds++; + ret = ldap_delete_ext_s(ld, dn, NULL, NULL); + if (ret == LDAP_SUCCESS) { + dels++; + rt_pass[idx]++; + } else { + thread_output(idx, ldap_err2string(ret)); + rt_fail[idx]++; + } + } else { + thread_output(idx, ldap_err2string(ret)); + rt_fail[idx]++; + } + } + + snprintf(thrstr, BUFSIZ, + "INNER STOP RW Thread using conn %d (%d/%d)", + thisconn, adds, dels); + thread_verbose(idx, thrstr); + + for(i = 0; i < noconns; i++) { + (void) ldap_destroy(mlds[i]); + mlds[i] = NULL; + } + } + + free( mlds ); + return( NULL ); +} + +static void +do_random( LDAP *ld, + char *sbase, char *filter, char **srchattrs, int noattrs, int nobind, + int force, int idx ) +{ + int i = 0, do_retry = config->retries; + char *attrs[ 2 ]; + int rc = LDAP_SUCCESS; + int nvalues = 0; + char **values = NULL; + LDAPMessage *res = NULL, *e = NULL; + char thrstr[BUFSIZ]; + + attrs[ 0 ] = LDAP_NO_ATTRS; + attrs[ 1 ] = NULL; + + snprintf( thrstr, BUFSIZ, + "Read(%d): base=\"%s\", filter=\"%s\".\n", + config->loops, sbase, filter ); + thread_verbose( idx, thrstr ); + + rc = ldap_search_ext_s( ld, sbase, LDAP_SCOPE_SUBTREE, + filter, attrs, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, &res ); + switch ( rc ) { + case LDAP_SIZELIMIT_EXCEEDED: + case LDAP_TIMELIMIT_EXCEEDED: + case LDAP_SUCCESS: + nvalues = ldap_count_entries( ld, res ); + if ( nvalues == 0 ) { + if ( rc ) { + tester_ldap_error( ld, "ldap_search_ext_s", NULL ); + } + break; + } + + values = malloc( ( nvalues + 1 ) * sizeof( char * ) ); + if (values == NULL) { + thread_error( idx, "(failed to malloc)"); + exit( EXIT_FAILURE ); + } + for ( i = 0, e = ldap_first_entry( ld, res ); e != NULL; i++, e = ldap_next_entry( ld, e ) ) + { + values[ i ] = ldap_get_dn( ld, e ); + } + values[ i ] = NULL; + + ldap_msgfree( res ); + + if ( do_retry == config->retries ) { + snprintf( thrstr, BUFSIZ, + "Read base=\"%s\" filter=\"%s\" got %d values.\n", + sbase, filter, nvalues ); + thread_verbose( idx, thrstr ); + } + + for ( i = 0; i < config->loops; i++ ) { + int r = ((double)nvalues)*rand()/(RAND_MAX + 1.0); + + do_read( ld, values[ r ], + srchattrs, noattrs, nobind, 1, force, idx ); + } + for( i = 0; i < nvalues; i++) { + if (values[i] != NULL) + ldap_memfree( values[i] ); + } + free( values ); + break; + + default: + tester_ldap_error( ld, "ldap_search_ext_s", NULL ); + break; + } + + snprintf( thrstr, BUFSIZ, "Search done (%d).\n", rc ); + thread_verbose( idx, thrstr ); +} + +/* substitute a generated int into the filter */ +static void +do_random2( LDAP *ld, + char *sbase, char *filter, char **srchattrs, int noattrs, int nobind, + int force, int idx ) +{ + int i = 0, do_retry = config->retries; + int rc = LDAP_SUCCESS; + int lo, hi, range; + int flen; + LDAPMessage *res = NULL; + char *ptr, *ftail; + char thrstr[BUFSIZ]; + char fbuf[BUFSIZ]; + + snprintf( thrstr, BUFSIZ, + "Read(%d): base=\"%s\", filter=\"%s\".\n", + config->loops, sbase, filter ); + thread_verbose( idx, thrstr ); + + ptr = strchr(filter, '['); + if (!ptr) + return; + ftail = strchr(filter, ']'); + if (!ftail || ftail < ptr) + return; + + sscanf(ptr, "[%d-%d]", &lo, &hi); + range = hi - lo + 1; + + flen = ptr - filter; + ftail++; + + for ( i = 0; i < config->loops; i++ ) { + int r = ((double)range)*rand()/(RAND_MAX + 1.0); + sprintf(fbuf, "%.*s%d%s", flen, filter, r, ftail); + + rc = ldap_search_ext_s( ld, sbase, LDAP_SCOPE_SUBTREE, + fbuf, srchattrs, noattrs, NULL, NULL, NULL, + LDAP_NO_LIMIT, &res ); + if ( res != NULL ) { + ldap_msgfree( res ); + } + if ( rc == 0 ) { + rt_pass[idx]++; + } else { + int first = tester_ignore_err( rc ); + char buf[ BUFSIZ ]; + + rt_fail[idx]++; + snprintf( buf, sizeof( buf ), "ldap_search_ext_s(%s)", entry ); + + /* if ignore.. */ + if ( first ) { + /* only log if first occurrence */ + if ( ( force < 2 && first > 0 ) || abs(first) == 1 ) { + tester_ldap_error( ld, buf, NULL ); + } + continue; + } + + /* busy needs special handling */ + tester_ldap_error( ld, buf, NULL ); + if ( rc == LDAP_BUSY && do_retry > 0 ) { + do_retry--; + continue; + } + break; + } + } + + snprintf( thrstr, BUFSIZ, "Search done (%d).\n", rc ); + thread_verbose( idx, thrstr ); +} + +static void +do_read( LDAP *ld, char *entry, + char **attrs, int noattrs, int nobind, int maxloop, + int force, int idx ) +{ + int i = 0, do_retry = config->retries; + int rc = LDAP_SUCCESS; + char thrstr[BUFSIZ]; + +retry:; + if ( do_retry == config->retries ) { + snprintf( thrstr, BUFSIZ, "Read(%d): entry=\"%s\".\n", + maxloop, entry ); + thread_verbose( idx, thrstr ); + } + + snprintf(thrstr, BUFSIZ, "LD %p cnt: %d (retried %d) (%s)", \ + (void *) ld, maxloop, (do_retry - config->retries), entry); + thread_verbose( idx, thrstr ); + + for ( ; i < maxloop; i++ ) { + LDAPMessage *res = NULL; + + rc = ldap_search_ext_s( ld, entry, LDAP_SCOPE_BASE, + NULL, attrs, noattrs, NULL, NULL, NULL, + LDAP_NO_LIMIT, &res ); + if ( res != NULL ) { + ldap_msgfree( res ); + } + + if ( rc == 0 ) { + rt_pass[idx]++; + } else { + int first = tester_ignore_err( rc ); + char buf[ BUFSIZ ]; + + rt_fail[idx]++; + snprintf( buf, sizeof( buf ), "ldap_search_ext_s(%s)", entry ); + + /* if ignore.. */ + if ( first ) { + /* only log if first occurrence */ + if ( ( force < 2 && first > 0 ) || abs(first) == 1 ) { + tester_ldap_error( ld, buf, NULL ); + } + continue; + } + + /* busy needs special handling */ + tester_ldap_error( ld, buf, NULL ); + if ( rc == LDAP_BUSY && do_retry > 0 ) { + do_retry--; + goto retry; + } + break; + } + } +} + +#else /* NO_THREADS */ + +#include <stdio.h> +#include <stdlib.h> + +int +main( int argc, char **argv ) +{ + fprintf( stderr, "%s: not available when configured --without-threads\n", argv[0] ); + exit( EXIT_FAILURE ); +} + +#endif /* NO_THREADS */ diff --git a/tests/progs/slapd-read.c b/tests/progs/slapd-read.c new file mode 100644 index 0000000..75d8c07 --- /dev/null +++ b/tests/progs/slapd-read.c @@ -0,0 +1,445 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1999-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This work was initially developed by Kurt Spanier for inclusion + * in OpenLDAP Software. + */ + +#include "portable.h" + +#include <stdio.h> + +#include "ac/stdlib.h" + +#include "ac/ctype.h" +#include "ac/param.h" +#include "ac/socket.h" +#include "ac/string.h" +#include "ac/unistd.h" +#include "ac/wait.h" + +#include "ldap.h" +#include "lutil.h" + +#include "ldap_pvt.h" + +#include "slapd-common.h" + +#define LOOPS 100 +#define RETRIES 0 + +static void +do_read( struct tester_conn_args *config, char *entry, LDAP **ld, + char **attrs, int noattrs, int nobind, int maxloop, int force ); + +static void +do_random( struct tester_conn_args *config, char *sbase, + char *filter, char **attrs, int noattrs, int nobind, int force ); + +static void +usage( char *name, int opt ) +{ + if ( opt ) { + fprintf( stderr, "%s: unable to handle option \'%c\'\n\n", + name, opt ); + } + + fprintf( stderr, "usage: %s " TESTER_COMMON_HELP + "-e <entry> " + "[-A] " + "[-F] " + "[-N] " + "[-S[S[S]]] " + "[-f filter] " + "[-T <attrs>] " + "[<attrs>] " + "\n", + name ); + exit( EXIT_FAILURE ); +} + +/* -S: just send requests without reading responses + * -SS: send all requests asynchronous and immediately start reading responses + * -SSS: send all requests asynchronous; then read responses + */ +static int swamp; + +int +main( int argc, char **argv ) +{ + int i; + char *entry = NULL; + char *filter = NULL; + int force = 0; + char *srchattrs[] = { "1.1", NULL }; + char **attrs = srchattrs; + int noattrs = 0; + int nobind = 0; + struct tester_conn_args *config; + + config = tester_init( "slapd-read", TESTER_READ ); + + /* by default, tolerate referrals and no such object */ + tester_ignore_str2errlist( "REFERRAL,NO_SUCH_OBJECT" ); + + while ( (i = getopt( argc, argv, TESTER_COMMON_OPTS "Ae:Ff:NST:" )) != EOF ) { + switch ( i ) { + case 'A': + noattrs++; + break; + + case 'N': + nobind = TESTER_INIT_ONLY; + break; + + case 'e': /* DN to search for */ + entry = optarg; + break; + + case 'f': /* the search request */ + filter = optarg; + break; + + case 'F': + force++; + break; + + case 'S': + swamp++; + break; + + case 'T': + attrs = ldap_str2charray( optarg, "," ); + if ( attrs == NULL ) { + usage( argv[0], i ); + } + break; + + default: + if ( tester_config_opt( config, i, optarg ) == LDAP_SUCCESS ) { + break; + } + usage( argv[0], i ); + break; + } + } + + if ( entry == NULL ) + usage( argv[0], 0 ); + + if ( *entry == '\0' ) { + fprintf( stderr, "%s: invalid EMPTY entry DN.\n", + argv[0] ); + exit( EXIT_FAILURE ); + } + + if ( argv[optind] != NULL ) { + attrs = &argv[optind]; + } + + tester_config_finish( config ); + + for ( i = 0; i < config->outerloops; i++ ) { + if ( filter != NULL ) { + do_random( config, entry, filter, attrs, + noattrs, nobind, force ); + + } else { + do_read( config, entry, NULL, attrs, + noattrs, nobind, config->loops, force ); + } + } + + exit( EXIT_SUCCESS ); +} + +static void +do_random( struct tester_conn_args *config, char *sbase, char *filter, + char **srchattrs, int noattrs, int nobind, int force ) +{ + LDAP *ld = NULL; + int i = 0, do_retry = config->retries; + char *attrs[ 2 ]; + int rc = LDAP_SUCCESS; + int nvalues = 0; + char **values = NULL; + LDAPMessage *res = NULL, *e = NULL; + + attrs[ 0 ] = LDAP_NO_ATTRS; + attrs[ 1 ] = NULL; + + tester_init_ld( &ld, config, nobind ); + + if ( do_retry == config->retries ) { + fprintf( stderr, "PID=%ld - Read(%d): base=\"%s\", filter=\"%s\".\n", + (long) pid, config->loops, sbase, filter ); + } + + rc = ldap_search_ext_s( ld, sbase, LDAP_SCOPE_SUBTREE, + filter, attrs, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, &res ); + switch ( rc ) { + case LDAP_SIZELIMIT_EXCEEDED: + case LDAP_TIMELIMIT_EXCEEDED: + case LDAP_SUCCESS: + nvalues = ldap_count_entries( ld, res ); + if ( nvalues == 0 ) { + if ( rc ) { + tester_ldap_error( ld, "ldap_search_ext_s", NULL ); + } + break; + } + + values = malloc( ( nvalues + 1 ) * sizeof( char * ) ); + if ( !values ) { + tester_error( "malloc failed" ); + exit( EXIT_FAILURE ); + } + for ( i = 0, e = ldap_first_entry( ld, res ); e != NULL; i++, e = ldap_next_entry( ld, e ) ) + { + values[ i ] = ldap_get_dn( ld, e ); + } + values[ i ] = NULL; + + ldap_msgfree( res ); + + if ( do_retry == config->retries ) { + fprintf( stderr, " PID=%ld - Read base=\"%s\" filter=\"%s\" got %d values.\n", + (long) pid, sbase, filter, nvalues ); + } + + for ( i = 0; i < config->loops; i++ ) { +#if 0 /* use high-order bits for better randomness (Numerical Recipes in "C") */ + int r = rand() % nvalues; +#endif + int r = ((double)nvalues)*rand()/(RAND_MAX + 1.0); + + do_read( config, values[ r ], &ld, + srchattrs, noattrs, nobind, 1, force ); + } + free( values ); + break; + + default: + tester_ldap_error( ld, "ldap_search_ext_s", NULL ); + break; + } + + fprintf( stderr, " PID=%ld - Read done (%d).\n", (long) pid, rc ); + + if ( ld != NULL ) { + ldap_unbind_ext( ld, NULL, NULL ); + } +} + +static void +do_read( struct tester_conn_args *config, char *entry, LDAP **ldp, + char **attrs, int noattrs, int nobind, int maxloop, int force ) +{ + LDAP *ld = ldp ? *ldp : NULL; + int i = 0, do_retry = config->retries; + int rc = LDAP_SUCCESS; + int *msgids = NULL, active = 0; + + /* make room for msgid */ + if ( swamp > 1 ) { + msgids = (int *)calloc( sizeof(int), maxloop ); + if ( !msgids ) { + tester_error( "calloc failed" ); + exit( EXIT_FAILURE ); + } + } + +retry:; + if ( ld == NULL ) { + tester_init_ld( &ld, config, nobind ); + } + + if ( do_retry == config->retries ) { + fprintf( stderr, "PID=%ld - Read(%d): entry=\"%s\".\n", + (long) pid, maxloop, entry ); + } + + if ( swamp > 1 ) { + do { + LDAPMessage *res = NULL; + int j, msgid; + + if ( i < maxloop ) { + rc = ldap_search_ext( ld, entry, LDAP_SCOPE_BASE, + NULL, attrs, noattrs, NULL, NULL, + NULL, LDAP_NO_LIMIT, &msgids[i] ); + + active++; +#if 0 + fprintf( stderr, + ">>> PID=%ld - Read maxloop=%d cnt=%d active=%d msgid=%d: " + "entry=\"%s\"\n", + (long) pid, maxloop, i, active, msgids[i], + entry ); +#endif + i++; + + if ( rc ) { + char buf[BUFSIZ]; + int first = tester_ignore_err( rc ); + /* if ignore.. */ + if ( first ) { + /* only log if first occurrence */ + if ( ( force < 2 && first > 0 ) || abs(first) == 1 ) { + tester_ldap_error( ld, "ldap_search_ext", NULL ); + } + continue; + } + + /* busy needs special handling */ + snprintf( buf, sizeof( buf ), "entry=\"%s\"\n", entry ); + tester_ldap_error( ld, "ldap_search_ext", buf ); + if ( rc == LDAP_BUSY && do_retry > 0 ) { + ldap_unbind_ext( ld, NULL, NULL ); + ld = NULL; + do_retry--; + goto retry; + } + break; + } + + if ( swamp > 2 ) { + continue; + } + } + + rc = ldap_result( ld, LDAP_RES_ANY, 0, NULL, &res ); + switch ( rc ) { + case -1: + /* gone really bad */ +#if 0 + fprintf( stderr, + ">>> PID=%ld - Read maxloop=%d cnt=%d active=%d: " + "entry=\"%s\" ldap_result()=%d\n", + (long) pid, maxloop, i, active, entry, rc ); +#endif + goto cleanup; + + case 0: + /* timeout (impossible) */ + break; + + case LDAP_RES_SEARCH_ENTRY: + case LDAP_RES_SEARCH_REFERENCE: + /* ignore */ + break; + + case LDAP_RES_SEARCH_RESULT: + /* just remove, no error checking (TODO?) */ + msgid = ldap_msgid( res ); + ldap_parse_result( ld, res, &rc, NULL, NULL, NULL, NULL, 1 ); + res = NULL; + + /* linear search, bah */ + for ( j = 0; j < i; j++ ) { + if ( msgids[ j ] == msgid ) { + msgids[ j ] = -1; + active--; +#if 0 + fprintf( stderr, + "<<< PID=%ld - ReadDone maxloop=%d cnt=%d active=%d msgid=%d: " + "entry=\"%s\"\n", + (long) pid, maxloop, j, active, msgid, entry ); +#endif + break; + } + } + break; + + default: + /* other messages unexpected */ + fprintf( stderr, + "### PID=%ld - Read(%d): " + "entry=\"%s\" attrs=%s%s. unexpected response tag=%d\n", + (long) pid, maxloop, + entry, attrs[0], attrs[1] ? " (more...)" : "", rc ); + break; + } + + if ( res != NULL ) { + ldap_msgfree( res ); + } + } while ( i < maxloop || active > 0 ); + + } else { + for ( ; i < maxloop; i++ ) { + LDAPMessage *res = NULL; + + if (swamp) { + int msgid; + rc = ldap_search_ext( ld, entry, LDAP_SCOPE_BASE, + NULL, attrs, noattrs, NULL, NULL, + NULL, LDAP_NO_LIMIT, &msgid ); + if ( rc == LDAP_SUCCESS ) continue; + else break; + } + + rc = ldap_search_ext_s( ld, entry, LDAP_SCOPE_BASE, + NULL, attrs, noattrs, NULL, NULL, NULL, + LDAP_NO_LIMIT, &res ); + if ( res != NULL ) { + ldap_msgfree( res ); + } + + if ( rc ) { + int first = tester_ignore_err( rc ); + char buf[ BUFSIZ ]; + + snprintf( buf, sizeof( buf ), "ldap_search_ext_s(%s)", entry ); + + /* if ignore.. */ + if ( first ) { + /* only log if first occurrence */ + if ( ( force < 2 && first > 0 ) || abs(first) == 1 ) { + tester_ldap_error( ld, buf, NULL ); + } + continue; + } + + /* busy needs special handling */ + tester_ldap_error( ld, buf, NULL ); + if ( rc == LDAP_BUSY && do_retry > 0 ) { + ldap_unbind_ext( ld, NULL, NULL ); + ld = NULL; + do_retry--; + goto retry; + } + break; + } + } + } + +cleanup:; + if ( msgids != NULL ) { + free( msgids ); + } + + if ( ldp != NULL ) { + *ldp = ld; + + } else { + fprintf( stderr, " PID=%ld - Read done (%d).\n", (long) pid, rc ); + + if ( ld != NULL ) { + ldap_unbind_ext( ld, NULL, NULL ); + } + } +} + diff --git a/tests/progs/slapd-search.c b/tests/progs/slapd-search.c new file mode 100644 index 0000000..13a2818 --- /dev/null +++ b/tests/progs/slapd-search.c @@ -0,0 +1,491 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1999-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This work was initially developed by Kurt Spanier for inclusion + * in OpenLDAP Software. + */ + +#include "portable.h" + +#include <stdio.h> + +#include "ac/stdlib.h" + +#include "ac/ctype.h" +#include "ac/param.h" +#include "ac/socket.h" +#include "ac/string.h" +#include "ac/unistd.h" +#include "ac/wait.h" + +#include "ldap.h" +#include "lutil.h" +#include "ldap_pvt.h" + +#include "slapd-common.h" + +#define LOOPS 100 +#define RETRIES 0 + +static void +do_search( struct tester_conn_args *config, + char *sbase, int scope, char *filter, LDAP **ldp, + char **attrs, int noattrs, int nobind, + int innerloop, int force ); + +static void +do_random( struct tester_conn_args *config, + char *sbase, int scope, char *filter, char *attr, + char **attrs, int noattrs, int nobind, int force ); + +static void +usage( char *name, char opt ) +{ + if ( opt != '\0' ) { + fprintf( stderr, "unknown/incorrect option \"%c\"\n", opt ); + } + + fprintf( stderr, "usage: %s " TESTER_COMMON_HELP + "-b <searchbase> " + "-s <scope> " + "-f <searchfilter> " + "[-a <attr>] " + "[-A] " + "[-F] " + "[-N] " + "[-S[S[S]]] " + "[<attrs>] " + "\n", + name ); + exit( EXIT_FAILURE ); +} + +/* -S: just send requests without reading responses + * -SS: send all requests asynchronous and immediately start reading responses + * -SSS: send all requests asynchronous; then read responses + */ +static int swamp; + +int +main( int argc, char **argv ) +{ + int i; + char *sbase = NULL; + int scope = LDAP_SCOPE_SUBTREE; + char *filter = NULL; + char *attr = NULL; + char *srchattrs[] = { "cn", "sn", NULL }; + char **attrs = srchattrs; + int force = 0; + int noattrs = 0; + int nobind = 0; + struct tester_conn_args *config; + + config = tester_init( "slapd-search", TESTER_SEARCH ); + + /* by default, tolerate referrals and no such object */ + tester_ignore_str2errlist( "REFERRAL,NO_SUCH_OBJECT" ); + + while ( ( i = getopt( argc, argv, TESTER_COMMON_OPTS "Aa:b:f:FNSs:T:" ) ) != EOF ) + { + switch ( i ) { + case 'A': + noattrs++; + break; + + case 'N': + nobind = TESTER_INIT_ONLY; + break; + + case 'a': + attr = optarg; + break; + + case 'b': /* file with search base */ + sbase = optarg; + break; + + case 'f': /* the search request */ + filter = optarg; + break; + + case 'F': + force++; + break; + + case 'T': + attrs = ldap_str2charray( optarg, "," ); + if ( attrs == NULL ) { + usage( argv[0], i ); + } + break; + + case 'S': + swamp++; + break; + + case 's': + scope = ldap_pvt_str2scope( optarg ); + if ( scope == -1 ) { + usage( argv[0], i ); + } + break; + + default: + if ( tester_config_opt( config, i, optarg ) == LDAP_SUCCESS ) { + break; + } + usage( argv[0], i ); + break; + } + } + + if (( sbase == NULL ) || ( filter == NULL )) + usage( argv[0], 0 ); + + if ( *filter == '\0' ) { + + fprintf( stderr, "%s: invalid EMPTY search filter.\n", + argv[0] ); + exit( EXIT_FAILURE ); + + } + + if ( argv[optind] != NULL ) { + attrs = &argv[optind]; + } + + tester_config_finish( config ); + + for ( i = 0; i < config->outerloops; i++ ) { + if ( attr != NULL ) { + do_random( config, + sbase, scope, filter, attr, + attrs, noattrs, nobind, force ); + + } else { + do_search( config, sbase, scope, filter, + NULL, attrs, noattrs, nobind, + config->loops, force ); + } + } + + exit( EXIT_SUCCESS ); +} + + +static void +do_random( struct tester_conn_args *config, + char *sbase, int scope, char *filter, char *attr, + char **srchattrs, int noattrs, int nobind, int force ) +{ + LDAP *ld = NULL; + int i = 0, do_retry = config->retries; + char *attrs[ 2 ]; + int rc = LDAP_SUCCESS; + int nvalues = 0; + char **values = NULL; + LDAPMessage *res = NULL, *e = NULL; + + attrs[ 0 ] = attr; + attrs[ 1 ] = NULL; + + tester_init_ld( &ld, config, nobind ); + + rc = ldap_search_ext_s( ld, sbase, LDAP_SCOPE_SUBTREE, + filter, attrs, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, &res ); + switch ( rc ) { + case LDAP_SIZELIMIT_EXCEEDED: + case LDAP_TIMELIMIT_EXCEEDED: + case LDAP_SUCCESS: + if ( ldap_count_entries( ld, res ) == 0 ) { + if ( rc ) { + tester_ldap_error( ld, "ldap_search_ext_s", NULL ); + } + break; + } + + for ( e = ldap_first_entry( ld, res ); e != NULL; e = ldap_next_entry( ld, e ) ) + { + struct berval **v = ldap_get_values_len( ld, e, attr ); + + if ( v != NULL ) { + int n = ldap_count_values_len( v ); + int j; + + values = realloc( values, ( nvalues + n + 1 )*sizeof( char * ) ); + if ( !values ) { + tester_error( "realloc failed" ); + exit( EXIT_FAILURE ); + } + for ( j = 0; j < n; j++ ) { + values[ nvalues + j ] = strdup( v[ j ]->bv_val ); + } + values[ nvalues + j ] = NULL; + nvalues += n; + ldap_value_free_len( v ); + } + } + + ldap_msgfree( res ); + + if ( !values ) { + fprintf( stderr, " PID=%ld - Search base=\"%s\" filter=\"%s\" got %d values.\n", + (long) pid, sbase, filter, nvalues ); + exit(EXIT_FAILURE); + } + + if ( do_retry == config->retries ) { + fprintf( stderr, " PID=%ld - Search base=\"%s\" filter=\"%s\" got %d values.\n", + (long) pid, sbase, filter, nvalues ); + } + + for ( i = 0; i < config->loops; i++ ) { + char buf[ BUFSIZ ]; +#if 0 /* use high-order bits for better randomness (Numerical Recipes in "C") */ + int r = rand() % nvalues; +#endif + int r = ((double)nvalues)*rand()/(RAND_MAX + 1.0); + + snprintf( buf, sizeof( buf ), "(%s=%s)", attr, values[ r ] ); + + do_search( config, + sbase, scope, buf, &ld, + srchattrs, noattrs, nobind, + 1, force ); + } + break; + + default: + tester_ldap_error( ld, "ldap_search_ext_s", NULL ); + break; + } + + fprintf( stderr, " PID=%ld - Search done (%d).\n", (long) pid, rc ); + + if ( values ) { + for ( i = 0; i < nvalues; i++ ) { + free( values[i] ); + } + free( values ); + } + + if ( ld != NULL ) { + ldap_unbind_ext( ld, NULL, NULL ); + } +} + +static void +do_search( struct tester_conn_args *config, + char *sbase, int scope, char *filter, LDAP **ldp, + char **attrs, int noattrs, int nobind, + int innerloop, int force ) +{ + LDAP *ld = ldp ? *ldp : NULL; + int i = 0, do_retry = config->retries; + int rc = LDAP_SUCCESS; + char buf[ BUFSIZ ]; + int *msgids = NULL, active = 0; + + /* make room for msgid */ + if ( swamp > 1 ) { + msgids = (int *)calloc( sizeof(int), innerloop ); + if ( !msgids ) { + tester_error( "calloc failed" ); + exit( EXIT_FAILURE ); + } + } + +retry:; + if ( ld == NULL ) { + fprintf( stderr, + "PID=%ld - Search(%d): " + "base=\"%s\" scope=%s filter=\"%s\" " + "attrs=%s%s.\n", + (long) pid, innerloop, + sbase, ldap_pvt_scope2str( scope ), filter, + attrs[0], attrs[1] ? " (more...)" : "" ); + + tester_init_ld( &ld, config, nobind ); + } + + if ( swamp > 1 ) { + do { + LDAPMessage *res = NULL; + int j, msgid; + + if ( i < innerloop ) { + rc = ldap_search_ext( ld, sbase, scope, + filter, NULL, noattrs, NULL, NULL, + NULL, LDAP_NO_LIMIT, &msgids[i] ); + + active++; +#if 0 + fprintf( stderr, + ">>> PID=%ld - Search maxloop=%d cnt=%d active=%d msgid=%d: " + "base=\"%s\" scope=%s filter=\"%s\"\n", + (long) pid, innerloop, i, active, msgids[i], + sbase, ldap_pvt_scope2str( scope ), filter ); +#endif + i++; + + if ( rc ) { + int first = tester_ignore_err( rc ); + /* if ignore.. */ + if ( first ) { + /* only log if first occurrence */ + if ( ( force < 2 && first > 0 ) || abs(first) == 1 ) { + tester_ldap_error( ld, "ldap_search_ext", NULL ); + } + continue; + } + + /* busy needs special handling */ + snprintf( buf, sizeof( buf ), + "base=\"%s\" filter=\"%s\"\n", + sbase, filter ); + tester_ldap_error( ld, "ldap_search_ext", buf ); + if ( rc == LDAP_BUSY && do_retry > 0 ) { + ldap_unbind_ext( ld, NULL, NULL ); + ld = NULL; + do_retry--; + goto retry; + } + break; + } + + if ( swamp > 2 ) { + continue; + } + } + + rc = ldap_result( ld, LDAP_RES_ANY, 0, NULL, &res ); + switch ( rc ) { + case -1: + /* gone really bad */ + goto cleanup; + + case 0: + /* timeout (impossible) */ + break; + + case LDAP_RES_SEARCH_ENTRY: + case LDAP_RES_SEARCH_REFERENCE: + /* ignore */ + break; + + case LDAP_RES_SEARCH_RESULT: + /* just remove, no error checking (TODO?) */ + msgid = ldap_msgid( res ); + ldap_parse_result( ld, res, &rc, NULL, NULL, NULL, NULL, 1 ); + res = NULL; + + /* linear search, bah */ + for ( j = 0; j < i; j++ ) { + if ( msgids[ j ] == msgid ) { + msgids[ j ] = -1; + active--; +#if 0 + fprintf( stderr, + "<<< PID=%ld - SearchDone maxloop=%d cnt=%d active=%d msgid=%d: " + "base=\"%s\" scope=%s filter=\"%s\"\n", + (long) pid, innerloop, j, active, msgid, + sbase, ldap_pvt_scope2str( scope ), filter ); +#endif + break; + } + } + break; + + default: + /* other messages unexpected */ + fprintf( stderr, + "### PID=%ld - Search(%d): " + "base=\"%s\" scope=%s filter=\"%s\" " + "attrs=%s%s. unexpected response tag=%d\n", + (long) pid, innerloop, + sbase, ldap_pvt_scope2str( scope ), filter, + attrs[0], attrs[1] ? " (more...)" : "", rc ); + break; + } + + if ( res != NULL ) { + ldap_msgfree( res ); + } + } while ( i < innerloop || active > 0 ); + + } else { + for ( ; i < innerloop; i++ ) { + LDAPMessage *res = NULL; + + if (swamp) { + int msgid; + rc = ldap_search_ext( ld, sbase, scope, + filter, NULL, noattrs, NULL, NULL, + NULL, LDAP_NO_LIMIT, &msgid ); + if ( rc == LDAP_SUCCESS ) continue; + else break; + } + + rc = ldap_search_ext_s( ld, sbase, scope, + filter, attrs, noattrs, NULL, NULL, + NULL, LDAP_NO_LIMIT, &res ); + if ( res != NULL ) { + ldap_msgfree( res ); + } + + if ( rc ) { + int first = tester_ignore_err( rc ); + /* if ignore.. */ + if ( first ) { + /* only log if first occurrence */ + if ( ( force < 2 && first > 0 ) || abs(first) == 1 ) { + tester_ldap_error( ld, "ldap_search_ext_s", NULL ); + } + continue; + } + + /* busy needs special handling */ + snprintf( buf, sizeof( buf ), + "base=\"%s\" filter=\"%s\"\n", + sbase, filter ); + tester_ldap_error( ld, "ldap_search_ext_s", buf ); + if ( rc == LDAP_BUSY && do_retry > 0 ) { + ldap_unbind_ext( ld, NULL, NULL ); + ld = NULL; + do_retry--; + goto retry; + } + break; + } + } + } + +cleanup:; + if ( msgids != NULL ) { + free( msgids ); + } + + if ( ldp != NULL ) { + *ldp = ld; + + } else { + fprintf( stderr, " PID=%ld - Search done (%d).\n", (long) pid, rc ); + + if ( ld != NULL ) { + ldap_unbind_ext( ld, NULL, NULL ); + } + } +} diff --git a/tests/progs/slapd-tester.c b/tests/progs/slapd-tester.c new file mode 100644 index 0000000..7ad88b8 --- /dev/null +++ b/tests/progs/slapd-tester.c @@ -0,0 +1,1143 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1999-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This work was initially developed by Kurt Spanier for inclusion + * in OpenLDAP Software. + */ + +#include "portable.h" + +#include <stdio.h> + +#include "ac/stdlib.h" + +#include "ac/ctype.h" +#include "ac/dirent.h" +#include "ac/param.h" +#include "ac/socket.h" +#include "ac/string.h" +#include "ac/unistd.h" +#include "ac/wait.h" + + +#include "ldap_defaults.h" +#include "lutil.h" + +#include "ldap.h" +#include "ldap_pvt.h" +#include "lber_pvt.h" +#include "slapd-common.h" + +#ifdef _WIN32 +#define EXE ".exe" +#else +#define EXE +#endif + +#define SEARCHCMD "slapd-search" EXE +#define READCMD "slapd-read" EXE +#define ADDCMD "slapd-addel" EXE +#define MODRDNCMD "slapd-modrdn" EXE +#define MODIFYCMD "slapd-modify" EXE +#define BINDCMD "slapd-bind" EXE +#define MAXARGS 100 +#define MAXREQS 5000 +#define LOOPS 100 +#define OUTERLOOPS "1" +#define RETRIES "0" + +#define TSEARCHFILE "do_search.0" +#define TREADFILE "do_read.0" +#define TADDFILE "do_add." +#define TMODRDNFILE "do_modrdn.0" +#define TMODIFYFILE "do_modify.0" +#define TBINDFILE "do_bind.0" + +static char *get_file_name( char *dirname, char *filename ); +static int get_search_filters( char *filename, char *filters[], char *attrs[], char *bases[], LDAPURLDesc *luds[] ); +static int get_read_entries( char *filename, char *entries[], char *filters[] ); +static void fork_child( char *prog, char **args ); +static void wait4kids( int nkidval ); + +static int maxkids = 20; +static int nkids; + +#ifdef HAVE_WINSOCK +static HANDLE *children; +static char argbuf[BUFSIZ]; +#define ArgDup(x) strdup(strcat(strcat(strcpy(argbuf,"\""),x),"\"")) +#else +#define ArgDup(x) strdup(x) +#endif + +static void +usage( char *name, char opt ) +{ + if ( opt ) { + fprintf( stderr, "%s: unable to handle option \'%c\'\n\n", + name, opt ); + } + + fprintf( stderr, + "usage: %s " + "-H <uri> " + "-D <manager> " + "-w <passwd> " + "-d <datadir> " + "[-i <ignore>] " + "[-j <maxchild>] " + "[-l {<loops>|<type>=<loops>[,...]}] " + "[-L <outerloops>] " + "-P <progdir> " + "[-r <maxretries>] " + "[-t <delay>] " + "[-C] " + "[-F] " + "[-I] " + "[-N]\n", + name ); + exit( EXIT_FAILURE ); +} + +int +main( int argc, char **argv ) +{ + int i, j; + char *uri = NULL; + char *manager = NULL; + char *passwd = NULL; + char *dirname = NULL; + char *progdir = NULL; + int loops = LOOPS; + char *outerloops = OUTERLOOPS; + char *retries = RETRIES; + char *delay = "0"; + DIR *datadir; + struct dirent *file; + int friendly = 0; + int chaserefs = 0; + int noattrs = 0; + int nobind = 0; + int noinit = 1; + char *ignore = NULL; + /* search */ + char *sfile = NULL; + char *sreqs[MAXREQS]; + char *sattrs[MAXREQS]; + char *sbase[MAXREQS]; + LDAPURLDesc *slud[MAXREQS]; + int snum = 0; + char *sargs[MAXARGS]; + int sanum; + int sextra_args = 0; + char scmd[MAXPATHLEN]; + int swamp = 0; + char swampopt[sizeof("-SSS")]; + /* static so that its address can be used in initializer below. */ + static char sloops[LDAP_PVT_INTTYPE_CHARS(unsigned long)]; + /* read */ + char *rfile = NULL; + char *rreqs[MAXREQS]; + int rnum = 0; + char *rargs[MAXARGS]; + char *rflts[MAXREQS]; + int ranum; + int rextra_args = 0; + char rcmd[MAXPATHLEN]; + static char rloops[LDAP_PVT_INTTYPE_CHARS(unsigned long)]; + /* addel */ + char *afiles[MAXREQS]; + int anum = 0; + char *aargs[MAXARGS]; + int aanum; + char acmd[MAXPATHLEN]; + static char aloops[LDAP_PVT_INTTYPE_CHARS(unsigned long)]; + /* modrdn */ + char *nfile = NULL; + char *nreqs[MAXREQS]; + int nnum = 0; + char *nargs[MAXARGS]; + int nanum; + char ncmd[MAXPATHLEN]; + static char nloops[LDAP_PVT_INTTYPE_CHARS(unsigned long)]; + /* modify */ + char *mfile = NULL; + char *mreqs[MAXREQS]; + char *mdn[MAXREQS]; + int mnum = 0; + char *margs[MAXARGS]; + int manum; + char mcmd[MAXPATHLEN]; + static char mloops[LDAP_PVT_INTTYPE_CHARS(unsigned long)]; + /* bind */ + char *bfile = NULL; + char *breqs[MAXREQS]; + char *bcreds[MAXREQS]; + char *battrs[MAXREQS]; + int bnum = 0; + char *bargs[MAXARGS]; + int banum; + char bcmd[MAXPATHLEN]; + static char bloops[LDAP_PVT_INTTYPE_CHARS(unsigned long)]; + char **bargs_extra = NULL; + + char *friendlyOpt = NULL; + int pw_ask = 0; + char *pw_file = NULL; + + /* extra action to do after bind... */ + typedef struct extra_t { + char *action; + struct extra_t *next; + } extra_t; + + extra_t *extra = NULL; + int nextra = 0; + + tester_init( "slapd-tester", TESTER_TESTER ); + + sloops[0] = '\0'; + rloops[0] = '\0'; + aloops[0] = '\0'; + nloops[0] = '\0'; + mloops[0] = '\0'; + bloops[0] = '\0'; + + while ( ( i = getopt( argc, argv, "AB:CD:d:FH:h:Ii:j:L:l:NP:p:r:St:Ww:y:" ) ) != EOF ) + { + switch ( i ) { + case 'A': + noattrs++; + break; + + case 'B': { + char **p, + **b = ldap_str2charray( optarg, "," ); + extra_t **epp; + + for ( epp = &extra; *epp; epp = &(*epp)->next ) + ; + + for ( p = b; p[0]; p++ ) { + *epp = calloc( 1, sizeof( extra_t ) ); + (*epp)->action = p[0]; + epp = &(*epp)->next; + nextra++; + } + + ldap_memfree( b ); + } break; + + case 'C': + chaserefs++; + break; + + case 'D': /* slapd manager */ + manager = ArgDup( optarg ); + break; + + case 'd': /* data directory */ + dirname = optarg; + break; + + case 'F': + friendly++; + break; + + case 'H': /* slapd uri */ + uri = optarg; + break; + + case 'I': + noinit = 0; + break; + + case 'i': + ignore = optarg; + break; + + case 'j': /* the number of parallel clients */ + if ( lutil_atoi( &maxkids, optarg ) != 0 ) { + usage( argv[0], 'j' ); + } + break; + + case 'l': /* the number of loops per client */ + if ( !isdigit( (unsigned char) optarg[0] ) ) { + char **p, + **l = ldap_str2charray( optarg, "," ); + + for ( p = l; p[0]; p++) { + struct { + struct berval type; + char *buf; + } types[] = { + { BER_BVC( "add=" ), aloops }, + { BER_BVC( "bind=" ), bloops }, + { BER_BVC( "modify=" ), mloops }, + { BER_BVC( "modrdn=" ), nloops }, + { BER_BVC( "read=" ), rloops }, + { BER_BVC( "search=" ), sloops }, + { BER_BVNULL, NULL } + }; + int c, n; + + for ( c = 0; types[c].type.bv_val; c++ ) { + if ( strncasecmp( p[0], types[c].type.bv_val, types[c].type.bv_len ) == 0 ) { + break; + } + } + + if ( types[c].type.bv_val == NULL ) { + usage( argv[0], 'l' ); + } + + if ( lutil_atoi( &n, &p[0][types[c].type.bv_len] ) != 0 ) { + usage( argv[0], 'l' ); + } + + snprintf( types[c].buf, sizeof( aloops ), "%d", n ); + } + + ldap_charray_free( l ); + + } else if ( lutil_atoi( &loops, optarg ) != 0 ) { + usage( argv[0], 'l' ); + } + break; + + case 'L': /* the number of outerloops per client */ + outerloops = optarg; + break; + + case 'N': + nobind++; + break; + + case 'P': /* prog directory */ + progdir = optarg; + break; + + case 'r': /* the number of retries in case of error */ + retries = optarg; + break; + + case 'S': + swamp++; + break; + + case 't': /* the delay in seconds between each retry */ + delay = optarg; + break; + + case 'w': /* the managers passwd */ + passwd = ArgDup( optarg ); + memset( optarg, '*', strlen( optarg ) ); + break; + + case 'W': + pw_ask++; + break; + + case 'y': + pw_file = optarg; + break; + + default: + usage( argv[0], '\0' ); + break; + } + } + + if (( dirname == NULL ) || ( uri == NULL ) || + ( manager == NULL ) || ( passwd == NULL ) || ( progdir == NULL )) + { + usage( argv[0], '\0' ); + } + +#ifdef HAVE_WINSOCK + children = malloc( maxkids * sizeof(HANDLE) ); +#endif + /* get the file list */ + if ( ( datadir = opendir( dirname )) == NULL ) { + fprintf( stderr, "%s: couldn't open data directory \"%s\".\n", + argv[0], dirname ); + exit( EXIT_FAILURE ); + } + + /* look for search, read, modrdn, and add/delete files */ + for ( file = readdir( datadir ); file; file = readdir( datadir )) { + + if ( !strcasecmp( file->d_name, TSEARCHFILE )) { + sfile = get_file_name( dirname, file->d_name ); + continue; + } else if ( !strcasecmp( file->d_name, TREADFILE )) { + rfile = get_file_name( dirname, file->d_name ); + continue; + } else if ( !strcasecmp( file->d_name, TMODRDNFILE )) { + nfile = get_file_name( dirname, file->d_name ); + continue; + } else if ( !strcasecmp( file->d_name, TMODIFYFILE )) { + mfile = get_file_name( dirname, file->d_name ); + continue; + } else if ( !strncasecmp( file->d_name, TADDFILE, strlen( TADDFILE )) + && ( anum < MAXREQS )) { + afiles[anum++] = get_file_name( dirname, file->d_name ); + continue; + } else if ( !strcasecmp( file->d_name, TBINDFILE )) { + bfile = get_file_name( dirname, file->d_name ); + continue; + } + } + + closedir( datadir ); + + if ( pw_ask ) { + passwd = getpassphrase( _("Enter LDAP Password: ") ); + + } else if ( pw_file ) { + struct berval pw; + + if ( lutil_get_filed_password( pw_file, &pw ) ) { + exit( EXIT_FAILURE ); + } + + passwd = pw.bv_val; + } + + if ( !sfile && !rfile && !nfile && !mfile && !bfile && !anum ) { + fprintf( stderr, "no data files found.\n" ); + exit( EXIT_FAILURE ); + } + + /* look for search requests */ + if ( sfile ) { + snum = get_search_filters( sfile, sreqs, sattrs, sbase, slud ); + if ( snum < 0 ) { + fprintf( stderr, + "unable to parse file \"%s\" line %d\n", + sfile, -2*(snum + 1)); + exit( EXIT_FAILURE ); + } + } + + /* look for read requests */ + if ( rfile ) { + rnum = get_read_entries( rfile, rreqs, rflts ); + if ( rnum < 0 ) { + fprintf( stderr, + "unable to parse file \"%s\" line %d\n", + rfile, -2*(rnum + 1) ); + exit( EXIT_FAILURE ); + } + } + + /* look for modrdn requests */ + if ( nfile ) { + nnum = get_read_entries( nfile, nreqs, NULL ); + if ( nnum < 0 ) { + fprintf( stderr, + "unable to parse file \"%s\" line %d\n", + nfile, -2*(nnum + 1) ); + exit( EXIT_FAILURE ); + } + } + + /* look for modify requests */ + if ( mfile ) { + mnum = get_search_filters( mfile, mreqs, NULL, mdn, NULL ); + if ( mnum < 0 ) { + fprintf( stderr, + "unable to parse file \"%s\" line %d\n", + mfile, -2*(mnum + 1) ); + exit( EXIT_FAILURE ); + } + } + + /* look for bind requests */ + if ( bfile ) { + bnum = get_search_filters( bfile, bcreds, battrs, breqs, NULL ); + if ( bnum < 0 ) { + fprintf( stderr, + "unable to parse file \"%s\" line %d\n", + bfile, -2*(bnum + 1) ); + exit( EXIT_FAILURE ); + } + } + + /* setup friendly option */ + switch ( friendly ) { + case 0: + break; + + case 1: + friendlyOpt = "-F"; + break; + + default: + /* NOTE: right now we don't need it more than twice */ + case 2: + friendlyOpt = "-FF"; + break; + } + + /* setup swamp option */ + if ( swamp ) { + swampopt[0] = '-'; + if ( swamp > 3 ) swamp = 3; + swampopt[swamp + 1] = '\0'; + for ( ; swamp-- > 0; ) swampopt[swamp + 1] = 'S'; + } + + /* setup loop options */ + if ( sloops[0] == '\0' ) snprintf( sloops, sizeof( sloops ), "%d", 10 * loops ); + if ( rloops[0] == '\0' ) snprintf( rloops, sizeof( rloops ), "%d", 20 * loops ); + if ( aloops[0] == '\0' ) snprintf( aloops, sizeof( aloops ), "%d", loops ); + if ( nloops[0] == '\0' ) snprintf( nloops, sizeof( nloops ), "%d", loops ); + if ( mloops[0] == '\0' ) snprintf( mloops, sizeof( mloops ), "%d", loops ); + if ( bloops[0] == '\0' ) snprintf( bloops, sizeof( bloops ), "%d", 20 * loops ); + + /* + * generate the search clients + */ + + sanum = 0; + snprintf( scmd, sizeof scmd, "%s" LDAP_DIRSEP SEARCHCMD, + progdir ); + sargs[sanum++] = scmd; + sargs[sanum++] = "-H"; + sargs[sanum++] = uri; + sargs[sanum++] = "-D"; + sargs[sanum++] = manager; + sargs[sanum++] = "-w"; + sargs[sanum++] = passwd; + sargs[sanum++] = "-l"; + sargs[sanum++] = sloops; + sargs[sanum++] = "-L"; + sargs[sanum++] = outerloops; + sargs[sanum++] = "-r"; + sargs[sanum++] = retries; + sargs[sanum++] = "-t"; + sargs[sanum++] = delay; + if ( friendly ) { + sargs[sanum++] = friendlyOpt; + } + if ( chaserefs ) { + sargs[sanum++] = "-C"; + } + if ( noattrs ) { + sargs[sanum++] = "-A"; + } + if ( nobind ) { + sargs[sanum++] = "-N"; + } + if ( ignore ) { + sargs[sanum++] = "-i"; + sargs[sanum++] = ignore; + } + if ( swamp ) { + sargs[sanum++] = swampopt; + } + sargs[sanum++] = "-b"; + sargs[sanum++] = NULL; /* will hold the search base */ + sargs[sanum++] = "-s"; + sargs[sanum++] = NULL; /* will hold the search scope */ + sargs[sanum++] = "-f"; + sargs[sanum++] = NULL; /* will hold the search request */ + + sargs[sanum++] = NULL; + sargs[sanum++] = NULL; /* might hold the "attr" request */ + sextra_args += 2; + + sargs[sanum] = NULL; + + /* + * generate the read clients + */ + + ranum = 0; + snprintf( rcmd, sizeof rcmd, "%s" LDAP_DIRSEP READCMD, + progdir ); + rargs[ranum++] = rcmd; + rargs[ranum++] = "-H"; + rargs[ranum++] = uri; + rargs[ranum++] = "-D"; + rargs[ranum++] = manager; + rargs[ranum++] = "-w"; + rargs[ranum++] = passwd; + rargs[ranum++] = "-l"; + rargs[ranum++] = rloops; + rargs[ranum++] = "-L"; + rargs[ranum++] = outerloops; + rargs[ranum++] = "-r"; + rargs[ranum++] = retries; + rargs[ranum++] = "-t"; + rargs[ranum++] = delay; + if ( friendly ) { + rargs[ranum++] = friendlyOpt; + } + if ( chaserefs ) { + rargs[ranum++] = "-C"; + } + if ( noattrs ) { + rargs[ranum++] = "-A"; + } + if ( ignore ) { + rargs[ranum++] = "-i"; + rargs[ranum++] = ignore; + } + if ( swamp ) { + rargs[ranum++] = swampopt; + } + rargs[ranum++] = "-e"; + rargs[ranum++] = NULL; /* will hold the read entry */ + + rargs[ranum++] = NULL; + rargs[ranum++] = NULL; /* might hold the filter arg */ + rextra_args += 2; + + rargs[ranum] = NULL; + + /* + * generate the modrdn clients + */ + + nanum = 0; + snprintf( ncmd, sizeof ncmd, "%s" LDAP_DIRSEP MODRDNCMD, + progdir ); + nargs[nanum++] = ncmd; + nargs[nanum++] = "-H"; + nargs[nanum++] = uri; + nargs[nanum++] = "-D"; + nargs[nanum++] = manager; + nargs[nanum++] = "-w"; + nargs[nanum++] = passwd; + nargs[nanum++] = "-l"; + nargs[nanum++] = nloops; + nargs[nanum++] = "-L"; + nargs[nanum++] = outerloops; + nargs[nanum++] = "-r"; + nargs[nanum++] = retries; + nargs[nanum++] = "-t"; + nargs[nanum++] = delay; + if ( friendly ) { + nargs[nanum++] = friendlyOpt; + } + if ( chaserefs ) { + nargs[nanum++] = "-C"; + } + if ( ignore ) { + nargs[nanum++] = "-i"; + nargs[nanum++] = ignore; + } + nargs[nanum++] = "-e"; + nargs[nanum++] = NULL; /* will hold the modrdn entry */ + nargs[nanum] = NULL; + + /* + * generate the modify clients + */ + + manum = 0; + snprintf( mcmd, sizeof mcmd, "%s" LDAP_DIRSEP MODIFYCMD, + progdir ); + margs[manum++] = mcmd; + margs[manum++] = "-H"; + margs[manum++] = uri; + margs[manum++] = "-D"; + margs[manum++] = manager; + margs[manum++] = "-w"; + margs[manum++] = passwd; + margs[manum++] = "-l"; + margs[manum++] = mloops; + margs[manum++] = "-L"; + margs[manum++] = outerloops; + margs[manum++] = "-r"; + margs[manum++] = retries; + margs[manum++] = "-t"; + margs[manum++] = delay; + if ( friendly ) { + margs[manum++] = friendlyOpt; + } + if ( chaserefs ) { + margs[manum++] = "-C"; + } + if ( ignore ) { + margs[manum++] = "-i"; + margs[manum++] = ignore; + } + margs[manum++] = "-e"; + margs[manum++] = NULL; /* will hold the modify entry */ + margs[manum++] = "-a";; + margs[manum++] = NULL; /* will hold the ava */ + margs[manum] = NULL; + + /* + * generate the add/delete clients + */ + + aanum = 0; + snprintf( acmd, sizeof acmd, "%s" LDAP_DIRSEP ADDCMD, + progdir ); + aargs[aanum++] = acmd; + aargs[aanum++] = "-H"; + aargs[aanum++] = uri; + aargs[aanum++] = "-D"; + aargs[aanum++] = manager; + aargs[aanum++] = "-w"; + aargs[aanum++] = passwd; + aargs[aanum++] = "-l"; + aargs[aanum++] = aloops; + aargs[aanum++] = "-L"; + aargs[aanum++] = outerloops; + aargs[aanum++] = "-r"; + aargs[aanum++] = retries; + aargs[aanum++] = "-t"; + aargs[aanum++] = delay; + if ( friendly ) { + aargs[aanum++] = friendlyOpt; + } + if ( chaserefs ) { + aargs[aanum++] = "-C"; + } + if ( ignore ) { + aargs[aanum++] = "-i"; + aargs[aanum++] = ignore; + } + aargs[aanum++] = "-f"; + aargs[aanum++] = NULL; /* will hold the add data file */ + aargs[aanum] = NULL; + + /* + * generate the bind clients + */ + + banum = 0; + snprintf( bcmd, sizeof bcmd, "%s" LDAP_DIRSEP BINDCMD, + progdir ); + bargs[banum++] = bcmd; + if ( !noinit ) { + bargs[banum++] = "-I"; /* init on each bind */ + } + bargs[banum++] = "-H"; + bargs[banum++] = uri; + bargs[banum++] = "-l"; + bargs[banum++] = bloops; + bargs[banum++] = "-L"; + bargs[banum++] = outerloops; + bargs[banum++] = "-r"; + bargs[banum++] = retries; + bargs[banum++] = "-t"; + bargs[banum++] = delay; + if ( friendly ) { + bargs[banum++] = friendlyOpt; + } + if ( chaserefs ) { + bargs[banum++] = "-C"; + } + if ( ignore ) { + bargs[banum++] = "-i"; + bargs[banum++] = ignore; + } + if ( nextra ) { + bargs[banum++] = "-B"; + bargs_extra = &bargs[banum++]; + } + bargs[banum++] = "-D"; + bargs[banum++] = NULL; + bargs[banum++] = "-w"; + bargs[banum++] = NULL; + bargs[banum] = NULL; + +#define DOREQ(n,j) ((n) && ((maxkids > (n)) ? ((j) < maxkids ) : ((j) < (n)))) + + for ( j = 0; j < MAXREQS; j++ ) { + /* search */ + if ( DOREQ( snum, j ) ) { + int jj = j % snum; + int x = sanum - sextra_args; + + /* base */ + if ( sbase[jj] != NULL ) { + sargs[sanum - 7] = sbase[jj]; + + } else { + sargs[sanum - 7] = slud[jj]->lud_dn; + } + + /* scope */ + if ( slud[jj] != NULL ) { + sargs[sanum - 5] = (char *)ldap_pvt_scope2str( slud[jj]->lud_scope ); + + } else { + sargs[sanum - 5] = "sub"; + } + + /* filter */ + if ( sreqs[jj] != NULL ) { + sargs[sanum - 3] = sreqs[jj]; + + } else if ( slud[jj]->lud_filter != NULL ) { + sargs[sanum - 3] = slud[jj]->lud_filter; + + } else { + sargs[sanum - 3] = "(objectClass=*)"; + } + + /* extras */ + sargs[x] = NULL; + + /* attr */ + if ( sattrs[jj] != NULL ) { + sargs[x++] = "-a"; + sargs[x++] = sattrs[jj]; + } + + /* attrs */ + if ( slud[jj] != NULL && slud[jj]->lud_attrs != NULL ) { + int i; + + for ( i = 0; slud[jj]->lud_attrs[ i ] != NULL && x + i < MAXARGS - 1; i++ ) { + sargs[x + i] = slud[jj]->lud_attrs[ i ]; + } + sargs[x + i] = NULL; + } + + fork_child( scmd, sargs ); + } + + /* read */ + if ( DOREQ( rnum, j ) ) { + int jj = j % rnum; + int x = ranum - rextra_args; + + rargs[ranum - 3] = rreqs[jj]; + if ( rflts[jj] != NULL ) { + rargs[x++] = "-f"; + rargs[x++] = rflts[jj]; + } + rargs[x] = NULL; + fork_child( rcmd, rargs ); + } + + /* rename */ + if ( j < nnum ) { + nargs[nanum - 1] = nreqs[j]; + fork_child( ncmd, nargs ); + } + + /* modify */ + if ( j < mnum ) { + margs[manum - 3] = mdn[j]; + margs[manum - 1] = mreqs[j]; + fork_child( mcmd, margs ); + } + + /* add/delete */ + if ( j < anum ) { + aargs[aanum - 1] = afiles[j]; + fork_child( acmd, aargs ); + } + + /* bind */ + if ( DOREQ( bnum, j ) ) { + int jj = j % bnum; + + if ( nextra ) { + int n = ((double)nextra)*rand()/(RAND_MAX + 1.0); + extra_t *e; + + for ( e = extra; n-- > 0; e = e->next ) + ; + *bargs_extra = e->action; + } + + if ( battrs[jj] != NULL ) { + bargs[banum - 3] = manager ? manager : ""; + bargs[banum - 1] = passwd ? passwd : ""; + + bargs[banum + 0] = "-b"; + bargs[banum + 1] = breqs[jj]; + bargs[banum + 2] = "-f"; + bargs[banum + 3] = bcreds[jj]; + bargs[banum + 4] = "-a"; + bargs[banum + 5] = battrs[jj]; + bargs[banum + 6] = NULL; + + } else { + bargs[banum - 3] = breqs[jj]; + bargs[banum - 1] = bcreds[jj]; + bargs[banum] = NULL; + } + + fork_child( bcmd, bargs ); + bargs[banum] = NULL; + } + } + + wait4kids( -1 ); + + exit( EXIT_SUCCESS ); +} + +static char * +get_file_name( char *dirname, char *filename ) +{ + char buf[MAXPATHLEN]; + + snprintf( buf, sizeof buf, "%s" LDAP_DIRSEP "%s", + dirname, filename ); + return( strdup( buf )); +} + + +static int +get_search_filters( char *filename, char *filters[], char *attrs[], char *bases[], LDAPURLDesc *luds[] ) +{ + FILE *fp; + int filter = 0; + + if ( (fp = fopen( filename, "r" )) != NULL ) { + char line[BUFSIZ]; + + while (( filter < MAXREQS ) && ( fgets( line, BUFSIZ, fp ))) { + char *nl; + int got_URL = 0; + + if (( nl = strchr( line, '\r' )) || ( nl = strchr( line, '\n' ))) + *nl = '\0'; + + if ( luds ) luds[filter] = NULL; + + if ( luds && strncmp( line, "ldap:///", STRLENOF( "ldap:///" ) ) == 0 ) { + LDAPURLDesc *lud; + + got_URL = 1; + bases[filter] = NULL; + if ( ldap_url_parse( line, &lud ) != LDAP_URL_SUCCESS ) { + filter = -filter - 1; + break; + } + + if ( lud->lud_dn == NULL || lud->lud_exts != NULL ) { + filter = -filter - 1; + ldap_free_urldesc( lud ); + break; + } + + luds[filter] = lud; + + } else { + bases[filter] = ArgDup( line ); + } + if ( fgets( line, BUFSIZ, fp ) == NULL ) + *line = '\0'; + if (( nl = strchr( line, '\r' )) || ( nl = strchr( line, '\n' ))) + *nl = '\0'; + + filters[filter] = ArgDup( line ); + if ( attrs ) { + if ( filters[filter][0] == '+') { + char *sep = strchr( filters[filter], ':' ); + + attrs[ filter ] = &filters[ filter ][ 1 ]; + if ( sep != NULL ) { + sep[ 0 ] = '\0'; + /* NOTE: don't free this! */ + filters[ filter ] = &sep[ 1 ]; + } + + } else { + attrs[ filter ] = NULL; + } + } + filter++; + + } + fclose( fp ); + } + + return filter; +} + + +static int +get_read_entries( char *filename, char *entries[], char *filters[] ) +{ + FILE *fp; + int entry = 0; + + if ( (fp = fopen( filename, "r" )) != NULL ) { + char line[BUFSIZ]; + + while (( entry < MAXREQS ) && ( fgets( line, BUFSIZ, fp ))) { + char *nl; + + if (( nl = strchr( line, '\r' )) || ( nl = strchr( line, '\n' ))) + *nl = '\0'; + if ( filters != NULL && line[0] == '+' ) { + LDAPURLDesc *lud; + + if ( ldap_url_parse( &line[1], &lud ) != LDAP_URL_SUCCESS ) { + entry = -entry - 1; + break; + } + + if ( lud->lud_dn == NULL || lud->lud_dn[ 0 ] == '\0' ) { + ldap_free_urldesc( lud ); + entry = -entry - 1; + break; + } + + entries[entry] = ArgDup( lud->lud_dn ); + + if ( lud->lud_filter ) { + filters[entry] = ArgDup( lud->lud_filter ); + + } else { + filters[entry] = ArgDup( "(objectClass=*)" ); + } + ldap_free_urldesc( lud ); + + } else { + if ( filters != NULL ) + filters[entry] = NULL; + + entries[entry] = ArgDup( line ); + } + + entry++; + + } + fclose( fp ); + } + + return( entry ); +} + +#ifndef HAVE_WINSOCK +static void +fork_child( char *prog, char **args ) +{ + /* note: obscures global pid var; intended */ + pid_t pid; + + wait4kids( maxkids ); + + switch ( pid = fork() ) { + case 0: /* child */ +#ifdef HAVE_EBCDIC + /* The __LIBASCII execvp only handles ASCII "prog", + * we still need to translate the arg vec ourselves. + */ + { char *arg2[MAXREQS]; + int i; + + for (i=0; args[i]; i++) { + arg2[i] = ArgDup(args[i]); + __atoe(arg2[i]); + } + arg2[i] = NULL; + args = arg2; } +#endif + execvp( prog, args ); + tester_perror( "execvp", NULL ); + { int i; + for (i=0; args[i]; i++); + fprintf(stderr,"%d args\n", i); + for (i=0; args[i]; i++) + fprintf(stderr,"%d %s\n", i, args[i]); + } + + exit( EXIT_FAILURE ); + break; + + case -1: /* trouble */ + tester_perror( "fork", NULL ); + break; + + default: /* parent */ + nkids++; + break; + } +} + +static void +wait4kids( int nkidval ) +{ + int status; + + while ( nkids >= nkidval ) { + pid_t pid = wait( &status ); + + if ( WIFSTOPPED(status) ) { + fprintf( stderr, + "stopping: child PID=%ld stopped with signal %d\n", + (long) pid, (int) WSTOPSIG(status) ); + + } else if ( WIFSIGNALED(status) ) { + fprintf( stderr, + "stopping: child PID=%ld terminated with signal %d%s\n", + (long) pid, (int) WTERMSIG(status), +#ifdef WCOREDUMP + WCOREDUMP(status) ? ", core dumped" : "" +#else + "" +#endif + ); + exit( WEXITSTATUS(status) ); + + } else if ( WEXITSTATUS(status) != 0 ) { + fprintf( stderr, + "stopping: child PID=%ld exited with status %d\n", + (long) pid, (int) WEXITSTATUS(status) ); + exit( WEXITSTATUS(status) ); + + } else { + nkids--; + } + } +} +#else + +static void +wait4kids( int nkidval ) +{ + int rc, i; + + while ( nkids >= nkidval ) { + rc = WaitForMultipleObjects( nkids, children, FALSE, INFINITE ); + for ( i=rc - WAIT_OBJECT_0; i<nkids-1; i++) + children[i] = children[i+1]; + nkids--; + } +} + +static void +fork_child( char *prog, char **args ) +{ + int rc; + + wait4kids( maxkids ); + + rc = _spawnvp( _P_NOWAIT, prog, args ); + + if ( rc == -1 ) { + tester_perror( "_spawnvp", NULL ); + } else { + children[nkids++] = (HANDLE)rc; + } +} +#endif diff --git a/tests/progs/slapd-watcher.c b/tests/progs/slapd-watcher.c new file mode 100644 index 0000000..0fed11f --- /dev/null +++ b/tests/progs/slapd-watcher.c @@ -0,0 +1,823 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1999-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This work was initially developed by Howard Chu for inclusion + * in OpenLDAP Software. + */ + +#include "portable.h" + +#include <stdio.h> + +#include "ac/signal.h" +#include "ac/stdlib.h" +#include "ac/time.h" + +#include "ac/ctype.h" +#include "ac/param.h" +#include "ac/socket.h" +#include "ac/string.h" +#include "ac/unistd.h" +#include "ac/wait.h" +#include "ac/time.h" + +#include "ldap.h" +#include "lutil.h" +#include "lutil_ldap.h" +#include "lber_pvt.h" +#include "ldap_pvt.h" + +#include "slapd-common.h" + +#define SLAP_SYNC_SID_MAX 4095 + +#define HAS_MONITOR 1 +#define HAS_BASE 2 +#define HAS_ENTRIES 4 +#define HAS_SREPL 8 +#define HAS_ALL (HAS_MONITOR|HAS_BASE|HAS_ENTRIES|HAS_SREPL) + + +#define WAS_LATE 0x100 +#define WAS_DOWN 0x200 + +#define MONFILTER "(objectClass=monitorOperation)" + +static const char *default_monfilter = MONFILTER; + +typedef enum { + SLAP_OP_BIND = 0, + SLAP_OP_UNBIND, + SLAP_OP_SEARCH, + SLAP_OP_COMPARE, + SLAP_OP_MODIFY, + SLAP_OP_MODRDN, + SLAP_OP_ADD, + SLAP_OP_DELETE, + SLAP_OP_ABANDON, + SLAP_OP_EXTENDED, + SLAP_OP_LAST +} slap_op_t; + +struct opname { + struct berval rdn; + char *display; +} opnames[] = { + { BER_BVC("cn=Bind"), "Bind" }, + { BER_BVC("cn=Unbind"), "Unbind" }, + { BER_BVC("cn=Search"), "Search" }, + { BER_BVC("cn=Compare"), "Compare" }, + { BER_BVC("cn=Modify"), "Modify" }, + { BER_BVC("cn=Modrdn"), "ModDN" }, + { BER_BVC("cn=Add"), "Add" }, + { BER_BVC("cn=Delete"), "Delete" }, + { BER_BVC("cn=Abandon"), "Abandon" }, + { BER_BVC("cn=Extended"), "Extended" }, + { BER_BVNULL, NULL } +}; + +typedef struct counters { + struct timeval time; + unsigned long entries; + unsigned long ops[SLAP_OP_LAST]; +} counters; + +typedef struct csns { + struct berval *vals; + struct timeval *tvs; +} csns; + +typedef struct activity { + time_t active; + time_t idle; + time_t maxlag; + time_t lag; +} activity; + +typedef struct server { + char *url; + LDAP *ld; + int flags; + int sid; + struct berval monitorbase; + char *monitorfilter; + time_t late; + time_t down; + counters c_prev; + counters c_curr; + csns csn_prev; + csns csn_curr; + activity *times; +} server; + +static void +usage( char *name, char opt ) +{ + if ( opt ) { + fprintf( stderr, "%s: unable to handle option \'%c\'\n\n", + name, opt ); + } + + fprintf( stderr, "usage: %s " + "[-D <dn> [ -w <passwd> ]] " + "[-d <level>] " + "[-O <SASL secprops>] " + "[-R <SASL realm>] " + "[-U <SASL authcid> [-X <SASL authzid>]] " + "[-x | -Y <SASL mech>] " + "[-i <interval>] " + "[-s <sids>] " + "[-c <contextDN>] " + "[-b <baseDN> ] URI[...]\n", + name ); + exit( EXIT_FAILURE ); +} + +struct berval base, cbase; +int interval = 10; +int numservers; +server *servers; +char *monfilter; + +struct berval at_namingContexts = BER_BVC("namingContexts"); +struct berval at_monitorOpCompleted = BER_BVC("monitorOpCompleted"); +struct berval at_olmMDBEntries = BER_BVC("olmMDBEntries"); +struct berval at_contextCSN = BER_BVC("contextCSN"); + +void timestamp(time_t *tt) +{ + struct tm *tm = gmtime(tt); + printf("%d-%02d-%02d %02d:%02d:%02d", + tm->tm_year + 1900, tm->tm_mon+1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); +} + +void deltat(time_t *tt) +{ + struct tm *tm = gmtime(tt); + if (tm->tm_mday-1) + printf("%02d+", tm->tm_mday-1); + printf("%02d:%02d:%02d", + tm->tm_hour, tm->tm_min, tm->tm_sec); +} + +static char *clearscreen = "\033[H\033[2J"; + +void rotate_stats( server *sv ) +{ + if ( sv->flags & HAS_MONITOR ) + sv->c_prev = sv->c_curr; + if ( sv->flags & HAS_BASE ) { + int i; + + for (i=0; i<numservers; i++) { + if ( sv->csn_curr.vals[i].bv_len ) { + ber_bvreplace(&sv->csn_prev.vals[i], + &sv->csn_curr.vals[i]); + sv->csn_prev.tvs[i] = sv->csn_curr.tvs[i]; + } else { + if ( sv->csn_prev.vals[i].bv_val ) + sv->csn_prev.vals[i].bv_val[0] = '\0'; + } + } + } +} + +void display() +{ + int i, j; + struct timeval now; + time_t now_t; + + gettimeofday(&now, NULL); + now_t = now.tv_sec; + printf("%s", clearscreen); + timestamp(&now_t); + printf("\n"); + + for (i=0; i<numservers; i++) { + printf("\n%s", servers[i].url ); + if ( servers[i].flags & WAS_DOWN ) { + printf(", down@"); + timestamp( &servers[i].down ); + } + if ( servers[i].flags & WAS_LATE ) { + printf(", late@"); + timestamp( &servers[i].late ); + } + printf("\n"); + if ( servers[i].flags & HAS_MONITOR ) { + struct timeval tv; + double rate, duration; + long delta; + printf(" "); + if ( servers[i].flags & HAS_ENTRIES ) + printf(" Entries "); + for ( j = 0; j<SLAP_OP_LAST; j++ ) + printf(" %9s ", opnames[j].display); + printf("\n"); + printf("Num "); + if ( servers[i].flags & HAS_ENTRIES ) + printf("%10lu ", servers[i].c_curr.entries); + for ( j = 0; j<SLAP_OP_LAST; j++ ) + printf("%10lu ", servers[i].c_curr.ops[j]); + printf("\n"); + printf("Num/s "); + tv.tv_usec = now.tv_usec - servers[i].c_prev.time.tv_usec; + tv.tv_sec = now.tv_sec - servers[i].c_prev.time.tv_sec; + if ( tv.tv_usec < 0 ) { + tv.tv_usec += 1000000; + tv.tv_sec--; + } + duration = tv.tv_sec + (tv.tv_usec / (double)1000000); + if ( servers[i].flags & HAS_ENTRIES ) { + delta = servers[i].c_curr.entries - servers[i].c_prev.entries; + rate = delta / duration; + printf("%10.2f ", rate); + } + for ( j = 0; j<SLAP_OP_LAST; j++ ) { + delta = servers[i].c_curr.ops[j] - servers[i].c_prev.ops[j]; + rate = delta / duration; + printf("%10.2f ", rate); + } + printf("\n"); + } + if ( servers[i].flags & HAS_BASE ) { + for (j=0; j<numservers; j++) { + /* skip empty CSNs */ + if (!servers[i].csn_curr.vals[j].bv_len || + !servers[i].csn_curr.vals[j].bv_val[0]) + continue; + printf("contextCSN: %s", servers[i].csn_curr.vals[j].bv_val ); + if (ber_bvcmp(&servers[i].csn_curr.vals[j], + &servers[i].csn_prev.vals[j])) { + /* a difference */ + if (servers[i].times[j].idle) { + servers[i].times[j].idle = 0; + servers[i].times[j].active = 0; + servers[i].times[j].maxlag = 0; + servers[i].times[j].lag = 0; + } +active: + if (!servers[i].times[j].active) + servers[i].times[j].active = now_t; + printf(" actv@"); + timestamp(&servers[i].times[j].active); + } else if ( servers[i].times[j].lag || ( servers[i].flags & WAS_LATE )) { + goto active; + } else { + if (servers[i].times[j].active && !servers[i].times[j].idle) + servers[i].times[j].idle = now_t; + if (servers[i].times[j].active) { + printf(" actv@"); + timestamp(&servers[i].times[j].active); + printf(", idle@"); + timestamp(&servers[i].times[j].idle); + } else { + printf(" idle"); + } + } + if (i != j) { + if (ber_bvcmp(&servers[i].csn_curr.vals[j], + &servers[j].csn_curr.vals[j])) { + struct timeval delta; + int ahead = 0; + time_t deltatt; + delta.tv_sec = servers[j].csn_curr.tvs[j].tv_sec - + servers[i].csn_curr.tvs[j].tv_sec; + delta.tv_usec = servers[j].csn_curr.tvs[j].tv_usec - + servers[i].csn_curr.tvs[j].tv_usec; + if (delta.tv_usec < 0) { + delta.tv_usec += 1000000; + delta.tv_sec--; + } + if (delta.tv_sec < 0) { + delta.tv_sec = -delta.tv_sec; + ahead = 1; + } + deltatt = delta.tv_sec; + if (ahead) + printf(", ahead "); + else + printf(", behind "); + deltat( &deltatt ); + servers[i].times[j].lag = deltatt; + if (deltatt > servers[i].times[j].maxlag) + servers[i].times[j].maxlag = deltatt; + } else { + servers[i].times[j].lag = 0; + printf(", sync'd"); + } + if (servers[i].times[j].maxlag) { + printf(", max delta "); + deltat( &servers[i].times[j].maxlag ); + } + } + printf("\n"); + } + } + if ( !( servers[i].flags & WAS_LATE )) + rotate_stats( &servers[i] ); + } +} + +void get_counters( + LDAP *ld, + LDAPMessage *e, + BerElement *ber, + counters *c ) +{ + int rc; + slap_op_t op = SLAP_OP_BIND; + struct berval dn, bv, *bvals, **bvp = &bvals; + + do { + int done = 0; + for ( rc = ldap_get_attribute_ber( ld, e, ber, &bv, bvp ); + rc == LDAP_SUCCESS; + rc = ldap_get_attribute_ber( ld, e, ber, &bv, bvp )) { + + if ( bv.bv_val == NULL ) break; + if ( !ber_bvcmp( &bv, &at_monitorOpCompleted ) && bvals ) { + c->ops[op] = strtoul( bvals[0].bv_val, NULL, 0 ); + done = 1; + } + if ( bvals ) { + ber_memfree( bvals ); + bvals = NULL; + } + if ( done ) + break; + } + ber_free( ber, 0 ); + e = ldap_next_entry( ld, e ); + if ( !e ) + break; + ldap_get_dn_ber( ld, e, &ber, &dn ); + op++; + } while ( op < SLAP_OP_LAST ); +} + +int +slap_parse_csn_sid( struct berval *csnp ) +{ + char *p, *q; + struct berval csn = *csnp; + int i; + + p = ber_bvchr( &csn, '#' ); + if ( !p ) + return -1; + p++; + csn.bv_len -= p - csn.bv_val; + csn.bv_val = p; + + p = ber_bvchr( &csn, '#' ); + if ( !p ) + return -1; + p++; + csn.bv_len -= p - csn.bv_val; + csn.bv_val = p; + + q = ber_bvchr( &csn, '#' ); + if ( !q ) + return -1; + + csn.bv_len = q - p; + + i = strtol( p, &q, 16 ); + if ( p == q || q != p + csn.bv_len || i < 0 || i > SLAP_SYNC_SID_MAX ) { + i = -1; + } + + return i; +} + +void get_csns( + csns *c, + struct berval *bvs +) +{ + int i, j; + + /* clear old values if any */ + for (i=0; i<numservers; i++) + if ( c->vals[i].bv_val ) + c->vals[i].bv_val[0] = '\0'; + + for (i=0; bvs[i].bv_val; i++) { + struct lutil_tm tm; + struct lutil_timet tt; + int sid = slap_parse_csn_sid( &bvs[i] ); + for (j=0; j<numservers; j++) + if (sid == servers[j].sid) break; + if (j < numservers) { + ber_bvreplace( &c->vals[j], &bvs[i] ); + lutil_parsetime(bvs[i].bv_val, &tm); + c->tvs[j].tv_usec = tm.tm_nsec / 1000; + lutil_tm2time( &tm, &tt ); + c->tvs[j].tv_sec = tt.tt_sec; + } + } +} + +int +setup_server( struct tester_conn_args *config, server *sv, int first ) +{ + config->uri = sv->url; + tester_init_ld( &sv->ld, config, first ? 0 : TESTER_INIT_NOEXIT ); + if ( !sv->ld ) + return -1; + + sv->flags &= ~HAS_ALL; + { + char *attrs[] = { at_namingContexts.bv_val, at_monitorOpCompleted.bv_val, + at_olmMDBEntries.bv_val, NULL }; + LDAPMessage *res = NULL, *e = NULL; + BerElement *ber = NULL; + LDAP *ld = sv->ld; + struct berval dn, bv, *bvals, **bvp = &bvals; + int j, rc; + + rc = ldap_search_ext_s( ld, "cn=monitor", LDAP_SCOPE_SUBTREE, monfilter, + attrs, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, &res ); + switch(rc) { + case LDAP_SIZELIMIT_EXCEEDED: + case LDAP_TIMELIMIT_EXCEEDED: + case LDAP_SUCCESS: + gettimeofday( &sv->c_curr.time, 0 ); + sv->flags |= HAS_MONITOR; + for ( e = ldap_first_entry( ld, res ); e; e = ldap_next_entry( ld, e )) { + ldap_get_dn_ber( ld, e, &ber, &dn ); + if ( !strncasecmp( dn.bv_val, "cn=Database", sizeof("cn=Database")-1 ) || + !strncasecmp( dn.bv_val, "cn=Frontend", sizeof("cn=Frontend")-1 )) { + int matched = 0; + for ( rc = ldap_get_attribute_ber( ld, e, ber, &bv, bvp ); + rc == LDAP_SUCCESS; + rc = ldap_get_attribute_ber( ld, e, ber, &bv, bvp )) { + if ( bv.bv_val == NULL ) break; + if (!ber_bvcmp( &bv, &at_namingContexts ) && bvals ) { + for (j=0; bvals[j].bv_val; j++) { + if ( !ber_bvstrcasecmp( &base, &bvals[j] )) { + matched = 1; + break; + } + } + if (!matched) { + ber_memfree( bvals ); + bvals = NULL; + break; + } + } + if (!ber_bvcmp( &bv, &at_olmMDBEntries )) { + ber_bvreplace( &sv->monitorbase, &dn ); + sv->flags |= HAS_ENTRIES; + sv->c_curr.entries = strtoul( bvals[0].bv_val, NULL, 0 ); + } + ber_memfree( bvals ); + bvals = NULL; + } + } else if (!strncasecmp( dn.bv_val, opnames[0].rdn.bv_val, + opnames[0].rdn.bv_len )) { + get_counters( ld, e, ber, &sv->c_curr ); + break; + } + if ( ber ) + ber_free( ber, 0 ); + } + break; + + case LDAP_NO_SUCH_OBJECT: + /* no cn=monitor */ + break; + + default: + tester_ldap_error( ld, "ldap_search_ext_s(cn=Monitor)", sv->url ); + if ( first ) + exit( EXIT_FAILURE ); + } + ldap_msgfree( res ); + + if ( cbase.bv_val ) { + char *attr2[] = { at_contextCSN.bv_val, NULL }; + rc = ldap_search_ext_s( ld, cbase.bv_val, LDAP_SCOPE_BASE, "(objectClass=*)", + attr2, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, &res ); + switch(rc) { + case LDAP_SUCCESS: + e = ldap_first_entry( ld, res ); + if ( e ) { + sv->flags |= HAS_BASE; + ldap_get_dn_ber( ld, e, &ber, &dn ); + for ( rc = ldap_get_attribute_ber( ld, e, ber, &bv, bvp ); + rc == LDAP_SUCCESS; + rc = ldap_get_attribute_ber( ld, e, ber, &bv, bvp )) { + int done = 0; + if ( bv.bv_val == NULL ) break; + if ( bvals ) { + if ( !ber_bvcmp( &bv, &at_contextCSN )) { + get_csns( &sv->csn_curr, bvals ); + done = 1; + } + ber_memfree( bvals ); + bvals = NULL; + if ( done ) + break; + } + } + } + ldap_msgfree( res ); + break; + + default: + tester_ldap_error( ld, "ldap_search_ext_s(baseDN)", sv->url ); + if ( first ) + exit( EXIT_FAILURE ); + } + } + } + + if ( sv->monitorfilter != default_monfilter ) + free( sv->monitorfilter ); + if ( sv->flags & HAS_ENTRIES ) { + int len = sv->monitorbase.bv_len + sizeof("(|(entryDN=)" MONFILTER ")"); + char *ptr = malloc(len); + sprintf(ptr, "(|(entryDN=%s)" MONFILTER ")", sv->monitorbase.bv_val ); + sv->monitorfilter = ptr; + } else if ( sv->flags & HAS_MONITOR ) { + sv->monitorfilter = (char *)default_monfilter; + } + if ( first ) + rotate_stats( sv ); + return 0; +} + +int +main( int argc, char **argv ) +{ + int i, rc, *msg1, *msg2; + char **sids = NULL; + struct tester_conn_args *config; + int first = 1; + + config = tester_init( "slapd-watcher", TESTER_TESTER ); + config->authmethod = LDAP_AUTH_SIMPLE; + + while ( ( i = getopt( argc, argv, "D:O:R:U:X:Y:b:c:d:i:s:w:x" ) ) != EOF ) + { + switch ( i ) { + case 'b': /* base DN for DB entrycount lookups */ + ber_str2bv( optarg, 0, 0, &base ); + if ( !cbase.bv_val ) + cbase = base; + break; + + case 'c': /* base DN for contextCSN lookups */ + ber_str2bv( optarg, 0, 0, &cbase ); + break; + + case 'i': + interval = atoi(optarg); + break; + + case 's': + sids = ldap_str2charray( optarg, "," ); + break; + + default: + if ( tester_config_opt( config, i, optarg ) == LDAP_SUCCESS ) + break; + + usage( argv[0], i ); + break; + } + } + + tester_config_finish( config ); +#ifdef SIGPIPE + (void) SIGNAL(SIGPIPE, SIG_IGN); +#endif + + /* don't clear the screen if debug is enabled */ + if (debug) + clearscreen = "\n\n"; + + numservers = argc - optind; + if ( !numservers ) + usage( argv[0], 0 ); + + if ( sids ) { + for (i=0; sids[i]; i++ ); + if ( i != numservers ) { + fprintf(stderr, "Number of sids doesn't equal number of server URLs\n"); + exit( EXIT_FAILURE ); + } + } + + argv += optind; + argc -= optind; + servers = calloc( numservers, sizeof(server)); + + if ( base.bv_val ) { + monfilter = "(|(entryDN:dnOneLevelMatch:=cn=Databases,cn=Monitor)" MONFILTER ")"; + } else { + monfilter = MONFILTER; + } + + if ( sids || numservers > 1 ) { + for ( i=0; i<numservers; i++ ) + if ( sids ) + servers[i].sid = atoi(sids[i]); + else + servers[i].sid = i+1; + } + + for ( i = 0; i < numservers; i++ ) { + servers[i].url = argv[i]; + servers[i].times = calloc( numservers, sizeof(activity)); + servers[i].csn_curr.vals = calloc( numservers, sizeof(struct berval)); + servers[i].csn_prev.vals = calloc( numservers, sizeof(struct berval)); + servers[i].csn_curr.tvs = calloc( numservers, sizeof(struct timeval)); + servers[i].csn_prev.tvs = calloc( numservers, sizeof(struct timeval)); + } + + msg1 = malloc( numservers * 2 * sizeof(int)); + msg2 = msg1 + numservers; + + for (;;) { + LDAPMessage *res = NULL, *e = NULL; + BerElement *ber = NULL; + struct berval dn, bv, *bvals, **bvp = &bvals; + struct timeval tv; + LDAP *ld; + + for (i=0; i<numservers; i++) { + if ( !servers[i].ld || !(servers[i].flags & WAS_LATE )) { + msg1[i] = 0; + msg2[i] = 0; + } + if ( !servers[i].ld ) { + setup_server( config, &servers[i], first ); + } else { + ld = servers[i].ld; + rc = -1; + if ( servers[i].flags & WAS_DOWN ) + servers[i].flags ^= WAS_DOWN; + if (( servers[i].flags & HAS_MONITOR ) && !msg1[i] ) { + char *attrs[3] = { at_monitorOpCompleted.bv_val }; + if ( servers[i].flags & HAS_ENTRIES ) + attrs[1] = at_olmMDBEntries.bv_val; + rc = ldap_search_ext( ld, "cn=monitor", + LDAP_SCOPE_SUBTREE, servers[i].monitorfilter, + attrs, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, &msg1[i] ); + if ( rc != LDAP_SUCCESS ) { + tester_ldap_error( ld, "ldap_search_ext(cn=Monitor)", servers[i].url ); + if ( first ) + exit( EXIT_FAILURE ); + else { +server_down1: + ldap_unbind_ext( ld, NULL, NULL ); + servers[i].flags |= WAS_DOWN; + servers[i].ld = NULL; + gettimeofday( &tv, NULL ); + servers[i].down = tv.tv_sec; + msg1[i] = 0; + msg2[i] = 0; + continue; + } + } + } + if (( servers[i].flags & HAS_BASE ) && !msg2[i] ) { + char *attrs[2] = { at_contextCSN.bv_val }; + rc = ldap_search_ext( ld, cbase.bv_val, + LDAP_SCOPE_BASE, "(objectClass=*)", + attrs, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, &msg2[i] ); + if ( rc != LDAP_SUCCESS ) { + tester_ldap_error( ld, "ldap_search_ext(baseDN)", servers[i].url ); + if ( first ) + exit( EXIT_FAILURE ); + else + goto server_down1; + } + } + if ( rc != -1 ) + gettimeofday( &servers[i].c_curr.time, 0 ); + } + } + + for (i=0; i<numservers; i++) { + ld = servers[i].ld; + if ( msg1[i] ) { + tv.tv_sec = 0; + tv.tv_usec = 250000; + rc = ldap_result( ld, msg1[i], LDAP_MSG_ALL, &tv, &res ); + if ( rc < 0 ) { + tester_ldap_error( ld, "ldap_result(cn=Monitor)", servers[i].url ); + if ( first ) + exit( EXIT_FAILURE ); + else { +server_down2: + ldap_unbind_ext( ld, NULL, NULL ); + servers[i].flags |= WAS_DOWN; + servers[i].ld = NULL; + servers[i].down = servers[i].c_curr.time.tv_sec; + msg1[i] = 0; + msg2[i] = 0; + continue; + } + } + if ( rc == 0 ) { + if ( !( servers[i].flags & WAS_LATE )) + servers[i].late = servers[i].c_curr.time.tv_sec; + servers[i].flags |= WAS_LATE; + continue; + } + if ( servers[i].flags & WAS_LATE ) + servers[i].flags ^= WAS_LATE; + for ( e = ldap_first_entry( ld, res ); e; e = ldap_next_entry( ld, e )) { + ldap_get_dn_ber( ld, e, &ber, &dn ); + if ( !strncasecmp( dn.bv_val, "cn=Database", sizeof("cn=Database")-1 ) || + !strncasecmp( dn.bv_val, "cn=Frontend", sizeof("cn=Frontend")-1 )) { + for ( rc = ldap_get_attribute_ber( ld, e, ber, &bv, bvp ); + rc == LDAP_SUCCESS; + rc = ldap_get_attribute_ber( ld, e, ber, &bv, bvp )) { + if ( bv.bv_val == NULL ) break; + if ( !ber_bvcmp( &bv, &at_olmMDBEntries )) { + if ( !BER_BVISNULL( &servers[i].monitorbase )) { + servers[i].c_curr.entries = strtoul( bvals[0].bv_val, NULL, 0 ); + } + } + ber_memfree( bvals ); + bvals = NULL; + } + } else if (!strncasecmp( dn.bv_val, opnames[0].rdn.bv_val, + opnames[0].rdn.bv_len )) { + get_counters( ld, e, ber, &servers[i].c_curr ); + break; + } + if ( ber ) + ber_free( ber, 0 ); + } + ldap_msgfree( res ); + } + if ( msg2[i] ) { + tv.tv_sec = 0; + tv.tv_usec = 250000; + rc = ldap_result( ld, msg2[i], LDAP_MSG_ALL, &tv, &res ); + if ( rc < 0 ) { + tester_ldap_error( ld, "ldap_result(baseDN)", servers[i].url ); + if ( first ) + exit( EXIT_FAILURE ); + else + goto server_down2; + } + if ( rc == 0 ) { + if ( !( servers[i].flags & WAS_LATE )) + servers[i].late = servers[i].c_curr.time.tv_sec; + servers[i].flags |= WAS_LATE; + continue; + } + if ( servers[i].flags & WAS_LATE ) + servers[i].flags ^= WAS_LATE; + e = ldap_first_entry( ld, res ); + if ( e ) { + ldap_get_dn_ber( ld, e, &ber, &dn ); + for ( rc = ldap_get_attribute_ber( ld, e, ber, &bv, bvp ); + rc == LDAP_SUCCESS; + rc = ldap_get_attribute_ber( ld, e, ber, &bv, bvp )) { + int done = 0; + if ( bv.bv_val == NULL ) break; + if ( bvals ) { + if ( !ber_bvcmp( &bv, &at_contextCSN )) { + get_csns( &servers[i].csn_curr, bvals ); + done = 1; + } + ber_memfree( bvals ); + bvals = NULL; + if ( done ) + break; + } + } + } + ldap_msgfree( res ); + } + } + display(); + sleep(interval); + first = 0; + } + + exit( EXIT_SUCCESS ); +} + |