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