xref: /netbsd-src/external/bsd/openldap/dist/tests/progs/slapd-watcher.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
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
usage(char * name,char opt)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 
timestamp(time_t * tt)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 
deltat(time_t * tt)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 
rotate_stats(server * sv)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 
display()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 
get_counters(LDAP * ld,LDAPMessage * e,BerElement * ber,counters * c)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
slap_parse_csn_sid(struct berval * csnp)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 
get_csns(csns * c,struct berval * bvs)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
setup_server(struct tester_conn_args * config,server * sv,int first)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
main(int argc,char ** argv)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