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