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