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
63static const char *default_monfilter = MONFILTER;
64
65typedef 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
79struct 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
96typedef struct counters {
97	struct timeval time;
98	unsigned long entries;
99	unsigned long ops[SLAP_OP_LAST];
100} counters;
101
102typedef struct csns {
103	struct berval *vals;
104	struct timeval *tvs;
105} csns;
106
107typedef struct activity {
108	time_t active;
109	time_t idle;
110	time_t maxlag;
111	time_t lag;
112} activity;
113
114typedef 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
130static void
131usage( 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
152struct berval base;
153int interval = 10;
154int numservers;
155server *servers;
156char *monfilter;
157
158struct berval at_namingContexts = BER_BVC("namingContexts");
159struct berval at_monitorOpCompleted = BER_BVC("monitorOpCompleted");
160struct berval at_olmMDBEntries = BER_BVC("olmMDBEntries");
161struct berval at_contextCSN = BER_BVC("contextCSN");
162
163void 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
171void 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
180static char *clearscreen = "\033[H\033[2J";
181
182void 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
202void 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					}
277active:
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
340void 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
377int
378slap_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
412void 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
440int
441setup_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
570int
571main( 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 {
687server_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 {
728server_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