Home | History | Annotate | Line # | Download | only in progs
      1  1.1  christos /*	$NetBSD: slapd-watcher.c,v 1.3 2025/09/05 21:16:33 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.3  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.3 2025/09/05 21:16:33 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.3  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.3  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.3  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.3  christos 		if ( !(servers[i].flags & WAS_INIT )) {
    219  1.3  christos 			printf(", unknown");
    220  1.3  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.3  christos setup_server( struct tester_conn_args *config, server *sv )
    447  1.1  christos {
    448  1.1  christos 	config->uri = sv->url;
    449  1.3  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.3  christos 		if ( cbase.bv_val ) {
    521  1.1  christos 			char *attr2[] = { at_contextCSN.bv_val, NULL };
    522  1.3  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.3  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.3  christos 	if ( !( sv->flags & WAS_INIT )) {
    567  1.3  christos 		sv->flags |= WAS_INIT;
    568  1.1  christos 		rotate_stats( sv );
    569  1.3  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.3  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.3  christos 		case 'b':		/* base DN for DB entrycount lookups */
    587  1.1  christos 			ber_str2bv( optarg, 0, 0, &base );
    588  1.3  christos 			if ( !cbase.bv_val )
    589  1.3  christos 				cbase = base;
    590  1.3  christos 			break;
    591  1.3  christos 
    592  1.3  christos 		case 'c':		/* base DN for contextCSN lookups */
    593  1.3  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.3  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.3  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.3  christos 						ldap_unbind_ext( ld, NULL, NULL );
    694  1.3  christos 						servers[i].flags |= WAS_DOWN;
    695  1.3  christos 						servers[i].ld = NULL;
    696  1.3  christos 						gettimeofday( &tv, NULL );
    697  1.3  christos 						servers[i].down = tv.tv_sec;
    698  1.3  christos 						msg1[i] = 0;
    699  1.3  christos 						msg2[i] = 0;
    700  1.3  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.3  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.3  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.3  christos 					ldap_unbind_ext( ld, NULL, NULL );
    728  1.3  christos 					servers[i].flags |= WAS_DOWN;
    729  1.3  christos 					servers[i].ld = NULL;
    730  1.3  christos 					servers[i].down = servers[i].c_curr.time.tv_sec;
    731  1.3  christos 					msg1[i] = 0;
    732  1.3  christos 					msg2[i] = 0;
    733  1.3  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.3  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