Home | History | Annotate | Line # | Download | only in progs
slapd-watcher.c revision 1.2
      1 /*	$NetBSD: slapd-watcher.c,v 1.2 2021/08/14 16:15:03 christos Exp $	*/
      2 
      3 /* $OpenLDAP$ */
      4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      5  *
      6  * Copyright 1999-2021 The OpenLDAP Foundation.
      7  * All rights reserved.
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted only as authorized by the OpenLDAP
     11  * Public License.
     12  *
     13  * A copy of this license is available in file LICENSE in the
     14  * top-level directory of the distribution or, alternatively, at
     15  * <http://www.OpenLDAP.org/license.html>.
     16  */
     17 /* ACKNOWLEDGEMENTS:
     18  * This work was initially developed by Howard Chu for inclusion
     19  * in OpenLDAP Software.
     20  */
     21 
     22 #include <sys/cdefs.h>
     23 __RCSID("$NetBSD: slapd-watcher.c,v 1.2 2021/08/14 16:15:03 christos Exp $");
     24 
     25 #include "portable.h"
     26 
     27 #include <stdio.h>
     28 
     29 #include "ac/signal.h"
     30 #include "ac/stdlib.h"
     31 #include "ac/time.h"
     32 
     33 #include "ac/ctype.h"
     34 #include "ac/param.h"
     35 #include "ac/socket.h"
     36 #include "ac/string.h"
     37 #include "ac/unistd.h"
     38 #include "ac/wait.h"
     39 #include "ac/time.h"
     40 
     41 #include "ldap.h"
     42 #include "lutil.h"
     43 #include "lutil_ldap.h"
     44 #include "lber_pvt.h"
     45 #include "ldap_pvt.h"
     46 
     47 #include "slapd-common.h"
     48 
     49 #define SLAP_SYNC_SID_MAX	4095
     50 
     51 #define	HAS_MONITOR	1
     52 #define	HAS_BASE	2
     53 #define	HAS_ENTRIES	4
     54 #define	HAS_SREPL	8
     55 #define HAS_ALL (HAS_MONITOR|HAS_BASE|HAS_ENTRIES|HAS_SREPL)
     56 
     57 
     58 #define WAS_LATE	0x100
     59 #define WAS_DOWN	0x200
     60 
     61 #define	MONFILTER	"(objectClass=monitorOperation)"
     62 
     63 static const char *default_monfilter = MONFILTER;
     64 
     65 typedef enum {
     66     SLAP_OP_BIND = 0,
     67     SLAP_OP_UNBIND,
     68     SLAP_OP_SEARCH,
     69     SLAP_OP_COMPARE,
     70     SLAP_OP_MODIFY,
     71     SLAP_OP_MODRDN,
     72     SLAP_OP_ADD,
     73     SLAP_OP_DELETE,
     74     SLAP_OP_ABANDON,
     75     SLAP_OP_EXTENDED,
     76     SLAP_OP_LAST
     77 } slap_op_t;
     78 
     79 struct opname {
     80 	struct berval rdn;
     81 	char *display;
     82 } opnames[] = {
     83 	{ BER_BVC("cn=Bind"),		"Bind" },
     84 	{ BER_BVC("cn=Unbind"),		"Unbind" },
     85 	{ BER_BVC("cn=Search"),		"Search" },
     86 	{ BER_BVC("cn=Compare"),	"Compare" },
     87 	{ BER_BVC("cn=Modify"),		"Modify" },
     88 	{ BER_BVC("cn=Modrdn"),		"ModDN" },
     89 	{ BER_BVC("cn=Add"),		"Add" },
     90 	{ BER_BVC("cn=Delete"),		"Delete" },
     91 	{ BER_BVC("cn=Abandon"),	"Abandon" },
     92 	{ BER_BVC("cn=Extended"),	"Extended" },
     93 	{ BER_BVNULL, NULL }
     94 };
     95 
     96 typedef struct counters {
     97 	struct timeval time;
     98 	unsigned long entries;
     99 	unsigned long ops[SLAP_OP_LAST];
    100 } counters;
    101 
    102 typedef struct csns {
    103 	struct berval *vals;
    104 	struct timeval *tvs;
    105 } csns;
    106 
    107 typedef struct activity {
    108 	time_t active;
    109 	time_t idle;
    110 	time_t maxlag;
    111 	time_t lag;
    112 } activity;
    113 
    114 typedef struct server {
    115 	char *url;
    116 	LDAP *ld;
    117 	int flags;
    118 	int sid;
    119 	struct berval monitorbase;
    120 	char *monitorfilter;
    121 	time_t late;
    122 	time_t down;
    123 	counters c_prev;
    124 	counters c_curr;
    125 	csns csn_prev;
    126 	csns csn_curr;
    127 	activity *times;
    128 } server;
    129 
    130 static void
    131 usage( char *name, char opt )
    132 {
    133 	if ( opt ) {
    134 		fprintf( stderr, "%s: unable to handle option \'%c\'\n\n",
    135 			name, opt );
    136 	}
    137 
    138 	fprintf( stderr, "usage: %s "
    139 		"[-D <dn> [ -w <passwd> ]] "
    140 		"[-d <level>] "
    141 		"[-O <SASL secprops>] "
    142 		"[-R <SASL realm>] "
    143 		"[-U <SASL authcid> [-X <SASL authzid>]] "
    144 		"[-x | -Y <SASL mech>] "
    145 		"[-i <interval>] "
    146 		"[-s <sids>] "
    147 		"[-b <baseDN> ] URI[...]\n",
    148 		name );
    149 	exit( EXIT_FAILURE );
    150 }
    151 
    152 struct berval base;
    153 int interval = 10;
    154 int numservers;
    155 server *servers;
    156 char *monfilter;
    157 
    158 struct berval at_namingContexts = BER_BVC("namingContexts");
    159 struct berval at_monitorOpCompleted = BER_BVC("monitorOpCompleted");
    160 struct berval at_olmMDBEntries = BER_BVC("olmMDBEntries");
    161 struct berval at_contextCSN = BER_BVC("contextCSN");
    162 
    163 void timestamp(time_t *tt)
    164 {
    165 	struct tm *tm = gmtime(tt);
    166 	printf("%d-%02d-%02d %02d:%02d:%02d",
    167 		tm->tm_year + 1900, tm->tm_mon+1, tm->tm_mday,
    168 		tm->tm_hour, tm->tm_min, tm->tm_sec);
    169 }
    170 
    171 void deltat(time_t *tt)
    172 {
    173 	struct tm *tm = gmtime(tt);
    174 	if (tm->tm_mday-1)
    175 		printf("%02d+", tm->tm_mday-1);
    176 	printf("%02d:%02d:%02d",
    177 		tm->tm_hour, tm->tm_min, tm->tm_sec);
    178 }
    179 
    180 static char *clearscreen = "\033[H\033[2J";
    181 
    182 void rotate_stats( server *sv )
    183 {
    184 	if ( sv->flags & HAS_MONITOR )
    185 		sv->c_prev = sv->c_curr;
    186 	if ( sv->flags & HAS_BASE ) {
    187 		int i;
    188 
    189 		for (i=0; i<numservers; i++) {
    190 			if ( sv->csn_curr.vals[i].bv_len ) {
    191 				ber_bvreplace(&sv->csn_prev.vals[i],
    192 					&sv->csn_curr.vals[i]);
    193 				sv->csn_prev.tvs[i] = sv->csn_curr.tvs[i];
    194 			} else {
    195 				if ( sv->csn_prev.vals[i].bv_val )
    196 					sv->csn_prev.vals[i].bv_val[0] = '\0';
    197 			}
    198 		}
    199 	}
    200 }
    201 
    202 void display()
    203 {
    204 	int i, j;
    205 	struct timeval now;
    206 	time_t now_t;
    207 
    208 	gettimeofday(&now, NULL);
    209 	now_t = now.tv_sec;
    210 	printf("%s", clearscreen);
    211 	timestamp(&now_t);
    212 	printf("\n");
    213 
    214 	for (i=0; i<numservers; i++) {
    215 		printf("\n%s", servers[i].url );
    216 		if ( servers[i].flags & WAS_DOWN ) {
    217 			printf(", down@");
    218 			timestamp( &servers[i].down );
    219 		}
    220 		if ( servers[i].flags & WAS_LATE ) {
    221 			printf(", late@");
    222 			timestamp( &servers[i].late );
    223 		}
    224 		printf("\n");
    225 		if ( servers[i].flags & HAS_MONITOR ) {
    226 			struct timeval tv;
    227 			double rate, duration;
    228 			long delta;
    229 			printf("      ");
    230 			if ( servers[i].flags & HAS_ENTRIES )
    231 				printf("  Entries  ");
    232 			for ( j = 0; j<SLAP_OP_LAST; j++ )
    233 				printf(" %9s ", opnames[j].display);
    234 			printf("\n");
    235 			printf("Num   ");
    236 			if ( servers[i].flags & HAS_ENTRIES )
    237 				printf("%10lu ", servers[i].c_curr.entries);
    238 			for ( j = 0; j<SLAP_OP_LAST; j++ )
    239 				printf("%10lu ", servers[i].c_curr.ops[j]);
    240 			printf("\n");
    241 			printf("Num/s ");
    242 			tv.tv_usec = now.tv_usec - servers[i].c_prev.time.tv_usec;
    243 			tv.tv_sec = now.tv_sec - servers[i].c_prev.time.tv_sec;
    244 			if ( tv.tv_usec < 0 ) {
    245 				tv.tv_usec += 1000000;
    246 				tv.tv_sec--;
    247 			}
    248 			duration = tv.tv_sec + (tv.tv_usec / (double)1000000);
    249 			if ( servers[i].flags & HAS_ENTRIES ) {
    250 				delta = servers[i].c_curr.entries - servers[i].c_prev.entries;
    251 				rate = delta / duration;
    252 				printf("%10.2f ", rate);
    253 			}
    254 			for ( j = 0; j<SLAP_OP_LAST; j++ ) {
    255 				delta = servers[i].c_curr.ops[j] - servers[i].c_prev.ops[j];
    256 				rate = delta / duration;
    257 				printf("%10.2f ", rate);
    258 			}
    259 			printf("\n");
    260 		}
    261 		if ( servers[i].flags & HAS_BASE ) {
    262 			for (j=0; j<numservers; j++) {
    263 				/* skip empty CSNs */
    264 				if (!servers[i].csn_curr.vals[j].bv_len ||
    265 					!servers[i].csn_curr.vals[j].bv_val[0])
    266 					continue;
    267 				printf("contextCSN: %s", servers[i].csn_curr.vals[j].bv_val );
    268 				if (ber_bvcmp(&servers[i].csn_curr.vals[j],
    269 							&servers[i].csn_prev.vals[j])) {
    270 					/* a difference */
    271 					if (servers[i].times[j].idle) {
    272 						servers[i].times[j].idle = 0;
    273 						servers[i].times[j].active = 0;
    274 						servers[i].times[j].maxlag = 0;
    275 						servers[i].times[j].lag = 0;
    276 					}
    277 active:
    278 					if (!servers[i].times[j].active)
    279 						servers[i].times[j].active = now_t;
    280 					printf(" actv@");
    281 					timestamp(&servers[i].times[j].active);
    282 				} else if ( servers[i].times[j].lag || ( servers[i].flags & WAS_LATE )) {
    283 					goto active;
    284 				} else {
    285 					if (servers[i].times[j].active && !servers[i].times[j].idle)
    286 						servers[i].times[j].idle = now_t;
    287 					if (servers[i].times[j].active) {
    288 						printf(" actv@");
    289 						timestamp(&servers[i].times[j].active);
    290 						printf(", idle@");
    291 						timestamp(&servers[i].times[j].idle);
    292 					} else {
    293 						printf(" idle");
    294 					}
    295 				}
    296 				if (i != j) {
    297 					if (ber_bvcmp(&servers[i].csn_curr.vals[j],
    298 						&servers[j].csn_curr.vals[j])) {
    299 						struct timeval delta;
    300 						int ahead = 0;
    301 						time_t deltatt;
    302 						delta.tv_sec = servers[j].csn_curr.tvs[j].tv_sec -
    303 							servers[i].csn_curr.tvs[j].tv_sec;
    304 						delta.tv_usec = servers[j].csn_curr.tvs[j].tv_usec -
    305 							servers[i].csn_curr.tvs[j].tv_usec;
    306 						if (delta.tv_usec < 0) {
    307 							delta.tv_usec += 1000000;
    308 							delta.tv_sec--;
    309 						}
    310 						if (delta.tv_sec < 0) {
    311 							delta.tv_sec = -delta.tv_sec;
    312 							ahead = 1;
    313 						}
    314 						deltatt = delta.tv_sec;
    315 						if (ahead)
    316 							printf(", ahead ");
    317 						else
    318 							printf(", behind ");
    319 						deltat( &deltatt );
    320 						servers[i].times[j].lag = deltatt;
    321 						if (deltatt > servers[i].times[j].maxlag)
    322 							servers[i].times[j].maxlag = deltatt;
    323 					} else {
    324 						servers[i].times[j].lag = 0;
    325 						printf(", sync'd");
    326 					}
    327 					if (servers[i].times[j].maxlag) {
    328 						printf(", max delta ");
    329 						deltat( &servers[i].times[j].maxlag );
    330 					}
    331 				}
    332 				printf("\n");
    333 			}
    334 		}
    335 		if ( !( servers[i].flags & WAS_LATE ))
    336 			rotate_stats( &servers[i] );
    337 	}
    338 }
    339 
    340 void get_counters(
    341 	LDAP *ld,
    342 	LDAPMessage *e,
    343 	BerElement *ber,
    344 	counters *c )
    345 {
    346 	int rc;
    347 	slap_op_t op = SLAP_OP_BIND;
    348 	struct berval dn, bv, *bvals, **bvp = &bvals;
    349 
    350 	do {
    351 		int done = 0;
    352 		for ( rc = ldap_get_attribute_ber( ld, e, ber, &bv, bvp );
    353 			rc == LDAP_SUCCESS;
    354 			rc = ldap_get_attribute_ber( ld, e, ber, &bv, bvp )) {
    355 
    356 			if ( bv.bv_val == NULL ) break;
    357 			if ( !ber_bvcmp( &bv, &at_monitorOpCompleted ) && bvals ) {
    358 				c->ops[op] = strtoul( bvals[0].bv_val, NULL, 0 );
    359 				done = 1;
    360 			}
    361 			if ( bvals ) {
    362 				ber_memfree( bvals );
    363 				bvals = NULL;
    364 			}
    365 			if ( done )
    366 				break;
    367 		}
    368 		ber_free( ber, 0 );
    369 		e = ldap_next_entry( ld, e );
    370 		if ( !e )
    371 			break;
    372 		ldap_get_dn_ber( ld, e, &ber, &dn );
    373 		op++;
    374 	} while ( op < SLAP_OP_LAST );
    375 }
    376 
    377 int
    378 slap_parse_csn_sid( struct berval *csnp )
    379 {
    380 	char *p, *q;
    381 	struct berval csn = *csnp;
    382 	int i;
    383 
    384 	p = ber_bvchr( &csn, '#' );
    385 	if ( !p )
    386 		return -1;
    387 	p++;
    388 	csn.bv_len -= p - csn.bv_val;
    389 	csn.bv_val = p;
    390 
    391 	p = ber_bvchr( &csn, '#' );
    392 	if ( !p )
    393 		return -1;
    394 	p++;
    395 	csn.bv_len -= p - csn.bv_val;
    396 	csn.bv_val = p;
    397 
    398 	q = ber_bvchr( &csn, '#' );
    399 	if ( !q )
    400 		return -1;
    401 
    402 	csn.bv_len = q - p;
    403 
    404 	i = strtol( p, &q, 16 );
    405 	if ( p == q || q != p + csn.bv_len || i < 0 || i > SLAP_SYNC_SID_MAX ) {
    406 		i = -1;
    407 	}
    408 
    409 	return i;
    410 }
    411 
    412 void get_csns(
    413 	csns *c,
    414 	struct berval *bvs
    415 )
    416 {
    417 	int i, j;
    418 
    419 	/* clear old values if any */
    420 	for (i=0; i<numservers; i++)
    421 		if ( c->vals[i].bv_val )
    422 			c->vals[i].bv_val[0] = '\0';
    423 
    424 	for (i=0; bvs[i].bv_val; i++) {
    425 		struct lutil_tm tm;
    426 		struct lutil_timet tt;
    427 		int sid = slap_parse_csn_sid( &bvs[i] );
    428 		for (j=0; j<numservers; j++)
    429 			if (sid == servers[j].sid) break;
    430 		if (j < numservers) {
    431 			ber_bvreplace( &c->vals[j], &bvs[i] );
    432 			lutil_parsetime(bvs[i].bv_val, &tm);
    433 			c->tvs[j].tv_usec = tm.tm_nsec / 1000;
    434 			lutil_tm2time( &tm, &tt );
    435 			c->tvs[j].tv_sec = tt.tt_sec;
    436 		}
    437 	}
    438 }
    439 
    440 int
    441 setup_server( struct tester_conn_args *config, server *sv, int first )
    442 {
    443 	config->uri = sv->url;
    444 	tester_init_ld( &sv->ld, config, first ? 0 : TESTER_INIT_NOEXIT );
    445 	if ( !sv->ld )
    446 		return -1;
    447 
    448 	sv->flags &= ~HAS_ALL;
    449 	{
    450 		char *attrs[] = { at_namingContexts.bv_val, at_monitorOpCompleted.bv_val,
    451 			at_olmMDBEntries.bv_val, NULL };
    452 		LDAPMessage *res = NULL, *e = NULL;
    453 		BerElement *ber = NULL;
    454 		LDAP *ld = sv->ld;
    455 		struct berval dn, bv, *bvals, **bvp = &bvals;
    456 		int j, rc;
    457 
    458 		rc = ldap_search_ext_s( ld, "cn=monitor", LDAP_SCOPE_SUBTREE, monfilter,
    459 			attrs, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, &res );
    460 		switch(rc) {
    461 		case LDAP_SIZELIMIT_EXCEEDED:
    462 		case LDAP_TIMELIMIT_EXCEEDED:
    463 		case LDAP_SUCCESS:
    464 			gettimeofday( &sv->c_curr.time, 0 );
    465 			sv->flags |= HAS_MONITOR;
    466 			for ( e = ldap_first_entry( ld, res ); e; e = ldap_next_entry( ld, e )) {
    467 				ldap_get_dn_ber( ld, e, &ber, &dn );
    468 				if ( !strncasecmp( dn.bv_val, "cn=Database", sizeof("cn=Database")-1 ) ||
    469 					!strncasecmp( dn.bv_val, "cn=Frontend", sizeof("cn=Frontend")-1 )) {
    470 					int matched = 0;
    471 					for ( rc = ldap_get_attribute_ber( ld, e, ber, &bv, bvp );
    472 						rc == LDAP_SUCCESS;
    473 						rc = ldap_get_attribute_ber( ld, e, ber, &bv, bvp )) {
    474 						if ( bv.bv_val == NULL ) break;
    475 						if (!ber_bvcmp( &bv, &at_namingContexts ) && bvals ) {
    476 							for (j=0; bvals[j].bv_val; j++) {
    477 								if ( !ber_bvstrcasecmp( &base, &bvals[j] )) {
    478 									matched = 1;
    479 									break;
    480 								}
    481 							}
    482 							if (!matched) {
    483 								ber_memfree( bvals );
    484 								bvals = NULL;
    485 								break;
    486 							}
    487 						}
    488 						if (!ber_bvcmp( &bv, &at_olmMDBEntries )) {
    489 							ber_bvreplace( &sv->monitorbase, &dn );
    490 							sv->flags |= HAS_ENTRIES;
    491 							sv->c_curr.entries = strtoul( bvals[0].bv_val, NULL, 0 );
    492 						}
    493 						ber_memfree( bvals );
    494 						bvals = NULL;
    495 					}
    496 				} else if (!strncasecmp( dn.bv_val, opnames[0].rdn.bv_val,
    497 					opnames[0].rdn.bv_len )) {
    498 					get_counters( ld, e, ber, &sv->c_curr );
    499 					break;
    500 				}
    501 				if ( ber )
    502 					ber_free( ber, 0 );
    503 			}
    504 			break;
    505 
    506 		case LDAP_NO_SUCH_OBJECT:
    507 			/* no cn=monitor */
    508 			break;
    509 
    510 		default:
    511 			tester_ldap_error( ld, "ldap_search_ext_s(cn=Monitor)", sv->url );
    512 			if ( first )
    513 				exit( EXIT_FAILURE );
    514 		}
    515 		ldap_msgfree( res );
    516 
    517 		if ( base.bv_val ) {
    518 			char *attr2[] = { at_contextCSN.bv_val, NULL };
    519 			rc = ldap_search_ext_s( ld, base.bv_val, LDAP_SCOPE_BASE, "(objectClass=*)",
    520 				attr2, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, &res );
    521 			switch(rc) {
    522 			case LDAP_SUCCESS:
    523 				e = ldap_first_entry( ld, res );
    524 				if ( e ) {
    525 					sv->flags |= HAS_BASE;
    526 					ldap_get_dn_ber( ld, e, &ber, &dn );
    527 					for ( rc = ldap_get_attribute_ber( ld, e, ber, &bv, bvp );
    528 						rc == LDAP_SUCCESS;
    529 						rc = ldap_get_attribute_ber( ld, e, ber, &bv, bvp )) {
    530 						int done = 0;
    531 						if ( bv.bv_val == NULL ) break;
    532 						if ( bvals ) {
    533 							if ( !ber_bvcmp( &bv, &at_contextCSN )) {
    534 								get_csns( &sv->csn_curr, bvals );
    535 								done = 1;
    536 							}
    537 							ber_memfree( bvals );
    538 							bvals = NULL;
    539 							if ( done )
    540 								break;
    541 						}
    542 					}
    543 				}
    544 				ldap_msgfree( res );
    545 				break;
    546 
    547 			default:
    548 				tester_ldap_error( ld, "ldap_search_ext_s(baseDN)", sv->url );
    549 				if ( first )
    550 					exit( EXIT_FAILURE );
    551 			}
    552 		}
    553 	}
    554 
    555 	if ( sv->monitorfilter != default_monfilter )
    556 		free( sv->monitorfilter );
    557 	if ( sv->flags & HAS_ENTRIES ) {
    558 		int len = sv->monitorbase.bv_len + sizeof("(|(entryDN=)" MONFILTER ")");
    559 		char *ptr = malloc(len);
    560 		sprintf(ptr, "(|(entryDN=%s)" MONFILTER ")", sv->monitorbase.bv_val );
    561 		sv->monitorfilter = ptr;
    562 	} else if ( sv->flags & HAS_MONITOR ) {
    563 		sv->monitorfilter = (char *)default_monfilter;
    564 	}
    565 	if ( first )
    566 		rotate_stats( sv );
    567 	return 0;
    568 }
    569 
    570 int
    571 main( int argc, char **argv )
    572 {
    573 	int		i, rc, *msg1, *msg2;
    574 	char **sids = NULL;
    575 	struct tester_conn_args *config;
    576 	int first = 1;
    577 
    578 	config = tester_init( "slapd-watcher", TESTER_TESTER );
    579 	config->authmethod = LDAP_AUTH_SIMPLE;
    580 
    581 	while ( ( i = getopt( argc, argv, "D:O:R:U:X:Y:b:d:i:s:w:x" ) ) != EOF )
    582 	{
    583 		switch ( i ) {
    584 		case 'b':		/* base DN for contextCSN lookups */
    585 			ber_str2bv( optarg, 0, 0, &base );
    586 			break;
    587 
    588 		case 'i':
    589 			interval = atoi(optarg);
    590 			break;
    591 
    592 		case 's':
    593 			sids = ldap_str2charray( optarg, "," );
    594 			break;
    595 
    596 		default:
    597 			if ( tester_config_opt( config, i, optarg ) == LDAP_SUCCESS )
    598 				break;
    599 
    600 			usage( argv[0], i );
    601 			break;
    602 		}
    603 	}
    604 
    605 	tester_config_finish( config );
    606 #ifdef SIGPIPE
    607 	(void) SIGNAL(SIGPIPE, SIG_IGN);
    608 #endif
    609 
    610 	/* don't clear the screen if debug is enabled */
    611 	if (debug)
    612 		clearscreen = "\n\n";
    613 
    614 	numservers = argc - optind;
    615 	if ( !numservers )
    616 		usage( argv[0], 0 );
    617 
    618 	if ( sids ) {
    619 		for (i=0; sids[i]; i++ );
    620 		if ( i != numservers ) {
    621 			fprintf(stderr, "Number of sids doesn't equal number of server URLs\n");
    622 			exit( EXIT_FAILURE );
    623 		}
    624 	}
    625 
    626 	argv += optind;
    627 	argc -= optind;
    628 	servers = calloc( numservers, sizeof(server));
    629 
    630 	if ( base.bv_val ) {
    631 		monfilter = "(|(entryDN:dnOneLevelMatch:=cn=Databases,cn=Monitor)" MONFILTER ")";
    632 	} else {
    633 		monfilter = MONFILTER;
    634 	}
    635 
    636 	if ( numservers > 1 ) {
    637 		for ( i=0; i<numservers; i++ )
    638 			if ( sids )
    639 				servers[i].sid = atoi(sids[i]);
    640 			else
    641 				servers[i].sid = i+1;
    642 	}
    643 
    644 	for ( i = 0; i < numservers; i++ ) {
    645 		servers[i].url = argv[i];
    646 		servers[i].times = calloc( numservers, sizeof(activity));
    647 		servers[i].csn_curr.vals = calloc( numservers, sizeof(struct berval));
    648 		servers[i].csn_prev.vals = calloc( numservers, sizeof(struct berval));
    649 		servers[i].csn_curr.tvs = calloc( numservers, sizeof(struct timeval));
    650 		servers[i].csn_prev.tvs = calloc( numservers, sizeof(struct timeval));
    651 	}
    652 
    653 	msg1 = malloc( numservers * 2 * sizeof(int));
    654 	msg2 = msg1 + numservers;
    655 
    656 	for (;;) {
    657 		LDAPMessage *res = NULL, *e = NULL;
    658 		BerElement *ber = NULL;
    659 		struct berval dn, bv, *bvals, **bvp = &bvals;
    660 		struct timeval tv;
    661 		LDAP *ld;
    662 
    663 		for (i=0; i<numservers; i++) {
    664 			if ( !servers[i].ld || !(servers[i].flags & WAS_LATE )) {
    665 				msg1[i] = 0;
    666 				msg2[i] = 0;
    667 			}
    668 			if ( !servers[i].ld ) {
    669 				setup_server( config, &servers[i], first );
    670 			} else {
    671 				ld = servers[i].ld;
    672 				rc = -1;
    673 				if ( servers[i].flags & WAS_DOWN )
    674 					servers[i].flags ^= WAS_DOWN;
    675 				if (( servers[i].flags & HAS_MONITOR ) && !msg1[i] ) {
    676 					char *attrs[3] = { at_monitorOpCompleted.bv_val };
    677 					if ( servers[i].flags & HAS_ENTRIES )
    678 						attrs[1] = at_olmMDBEntries.bv_val;
    679 					rc = ldap_search_ext( ld, "cn=monitor",
    680 						LDAP_SCOPE_SUBTREE, servers[i].monitorfilter,
    681 						attrs, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, &msg1[i] );
    682 					if ( rc != LDAP_SUCCESS ) {
    683 						tester_ldap_error( ld, "ldap_search_ext(cn=Monitor)", servers[i].url );
    684 						if ( first )
    685 							exit( EXIT_FAILURE );
    686 						else {
    687 server_down1:
    688 							ldap_unbind_ext( ld, NULL, NULL );
    689 							servers[i].flags |= WAS_DOWN;
    690 							servers[i].ld = NULL;
    691 							gettimeofday( &tv, NULL );
    692 							servers[i].down = tv.tv_sec;
    693 							msg1[i] = 0;
    694 							msg2[i] = 0;
    695 							continue;
    696 						}
    697 					}
    698 				}
    699 				if (( servers[i].flags & HAS_BASE ) && !msg2[i] ) {
    700 					char *attrs[2] = { at_contextCSN.bv_val };
    701 					rc = ldap_search_ext( ld, base.bv_val,
    702 						LDAP_SCOPE_BASE, "(objectClass=*)",
    703 						attrs, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, &msg2[i] );
    704 					if ( rc != LDAP_SUCCESS ) {
    705 						tester_ldap_error( ld, "ldap_search_ext(baseDN)", servers[i].url );
    706 						if ( first )
    707 							exit( EXIT_FAILURE );
    708 						else
    709 							goto server_down1;
    710 					}
    711 				}
    712 				if ( rc != -1 )
    713 					gettimeofday( &servers[i].c_curr.time, 0 );
    714 			}
    715 		}
    716 
    717 		for (i=0; i<numservers; i++) {
    718 			ld = servers[i].ld;
    719 			if ( msg1[i] ) {
    720 				tv.tv_sec = 0;
    721 				tv.tv_usec = 250000;
    722 				rc = ldap_result( ld, msg1[i], LDAP_MSG_ALL, &tv, &res );
    723 				if ( rc < 0 ) {
    724 					tester_ldap_error( ld, "ldap_result(cn=Monitor)", servers[i].url );
    725 					if ( first )
    726 						exit( EXIT_FAILURE );
    727 					else {
    728 server_down2:
    729 						ldap_unbind_ext( ld, NULL, NULL );
    730 						servers[i].flags |= WAS_DOWN;
    731 						servers[i].ld = NULL;
    732 						servers[i].down = servers[i].c_curr.time.tv_sec;
    733 						msg1[i] = 0;
    734 						msg2[i] = 0;
    735 						continue;
    736 					}
    737 				}
    738 				if ( rc == 0 ) {
    739 					if ( !( servers[i].flags & WAS_LATE ))
    740 						servers[i].late = servers[i].c_curr.time.tv_sec;
    741 					servers[i].flags |= WAS_LATE;
    742 					continue;
    743 				}
    744 				if ( servers[i].flags & WAS_LATE )
    745 					servers[i].flags ^= WAS_LATE;
    746 				for ( e = ldap_first_entry( ld, res ); e; e = ldap_next_entry( ld, e )) {
    747 					ldap_get_dn_ber( ld, e, &ber, &dn );
    748 					if ( !strncasecmp( dn.bv_val, "cn=Database", sizeof("cn=Database")-1 ) ||
    749 						!strncasecmp( dn.bv_val, "cn=Frontend", sizeof("cn=Frontend")-1 )) {
    750 						for ( rc = ldap_get_attribute_ber( ld, e, ber, &bv, bvp );
    751 							rc == LDAP_SUCCESS;
    752 							rc = ldap_get_attribute_ber( ld, e, ber, &bv, bvp )) {
    753 							if ( bv.bv_val == NULL ) break;
    754 							if ( !ber_bvcmp( &bv, &at_olmMDBEntries )) {
    755 								if ( !BER_BVISNULL( &servers[i].monitorbase )) {
    756 									servers[i].c_curr.entries = strtoul( bvals[0].bv_val, NULL, 0 );
    757 								}
    758 							}
    759 							ber_memfree( bvals );
    760 							bvals = NULL;
    761 						}
    762 					} else if (!strncasecmp( dn.bv_val, opnames[0].rdn.bv_val,
    763 						opnames[0].rdn.bv_len )) {
    764 						get_counters( ld, e, ber, &servers[i].c_curr );
    765 						break;
    766 					}
    767 					if ( ber )
    768 						ber_free( ber, 0 );
    769 				}
    770 				ldap_msgfree( res );
    771 			}
    772 			if ( msg2[i] ) {
    773 				tv.tv_sec = 0;
    774 				tv.tv_usec = 250000;
    775 				rc = ldap_result( ld, msg2[i], LDAP_MSG_ALL, &tv, &res );
    776 				if ( rc < 0 ) {
    777 					tester_ldap_error( ld, "ldap_result(baseDN)", servers[i].url );
    778 					if ( first )
    779 						exit( EXIT_FAILURE );
    780 					else
    781 						goto server_down2;
    782 				}
    783 				if ( rc == 0 ) {
    784 					if ( !( servers[i].flags & WAS_LATE ))
    785 						servers[i].late = servers[i].c_curr.time.tv_sec;
    786 					servers[i].flags |= WAS_LATE;
    787 					continue;
    788 				}
    789 				if ( servers[i].flags & WAS_LATE )
    790 					servers[i].flags ^= WAS_LATE;
    791 				e = ldap_first_entry( ld, res );
    792 				if ( e ) {
    793 					ldap_get_dn_ber( ld, e, &ber, &dn );
    794 					for ( rc = ldap_get_attribute_ber( ld, e, ber, &bv, bvp );
    795 						rc == LDAP_SUCCESS;
    796 						rc = ldap_get_attribute_ber( ld, e, ber, &bv, bvp )) {
    797 						int done = 0;
    798 						if ( bv.bv_val == NULL ) break;
    799 						if ( bvals ) {
    800 							if ( !ber_bvcmp( &bv, &at_contextCSN )) {
    801 								get_csns( &servers[i].csn_curr, bvals );
    802 								done = 1;
    803 							}
    804 							ber_memfree( bvals );
    805 							bvals = NULL;
    806 							if ( done )
    807 								break;
    808 						}
    809 					}
    810 				}
    811 				ldap_msgfree( res );
    812 			}
    813 		}
    814 		display();
    815 		sleep(interval);
    816 		first = 0;
    817 	}
    818 
    819 	exit( EXIT_SUCCESS );
    820 }
    821 
    822