/* $OpenLDAP$ */ /* This work is part of OpenLDAP Software . * * Copyright 1999-2020 The OpenLDAP Foundation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted only as authorized by the OpenLDAP * Public License. * * A copy of this license is available in file LICENSE in the * top-level directory of the distribution or, alternatively, at * . */ /* ACKNOWLEDGEMENTS: * This work was initially developed by Howard Chu for inclusion * in OpenLDAP Software. */ #include "portable.h" #include #include "ac/stdlib.h" #include "ac/time.h" #include "ac/ctype.h" #include "ac/param.h" #include "ac/socket.h" #include "ac/string.h" #include "ac/unistd.h" #include "ac/wait.h" #include "ac/time.h" #include "ldap.h" #include "lutil.h" #include "lutil_ldap.h" #include "lber_pvt.h" #include "ldap_pvt.h" #include "slapd-common.h" #define HAS_MONITOR 1 #define HAS_BASE 2 #define HAS_ENTRIES 4 #define HAS_SREPL 8 #define MONFILTER "(objectClass=monitorOperation)" #define SLAP_SYNC_SID_MAX 4095 typedef enum { SLAP_OP_BIND = 0, SLAP_OP_UNBIND, SLAP_OP_SEARCH, SLAP_OP_COMPARE, SLAP_OP_MODIFY, SLAP_OP_MODRDN, SLAP_OP_ADD, SLAP_OP_DELETE, SLAP_OP_ABANDON, SLAP_OP_EXTENDED, SLAP_OP_LAST } slap_op_t; struct opname { struct berval rdn; char *display; } opnames[] = { { BER_BVC("cn=Bind"), "Bind" }, { BER_BVC("cn=Unbind"), "Unbind" }, { BER_BVC("cn=Search"), "Search" }, { BER_BVC("cn=Compare"), "Compare" }, { BER_BVC("cn=Modify"), "Modify" }, { BER_BVC("cn=Modrdn"), "ModDN" }, { BER_BVC("cn=Add"), "Add" }, { BER_BVC("cn=Delete"), "Delete" }, { BER_BVC("cn=Abandon"), "Abandon" }, { BER_BVC("cn=Extended"), "Extended" }, { BER_BVNULL, NULL } }; typedef struct counters { struct timeval time; unsigned long entries; unsigned long ops[SLAP_OP_LAST]; } counters; typedef struct csns { int num; int *sids; struct berval *vals; struct timeval *tvs; } csns; typedef struct activity { time_t active; time_t idle; time_t maxlag; time_t lag; } activity; typedef struct server { char *url; LDAP *ld; int flags; int sid; struct berval monitorbase; char *monitorfilter; counters c_prev; counters c_curr; csns csn_prev; csns csn_curr; activity *times; } server; static void usage( char *name, char opt ) { if ( opt ) { fprintf( stderr, "%s: unable to handle option \'%c\'\n\n", name, opt ); } fprintf( stderr, "usage: %s " "[-D [ -w ]] " "[-d ] " "[-O ] " "[-R ] " "[-U [-X ]] " "[-x | -Y ] " "[-i ] " "[-s ] " "[-b ] URI[...]\n", name ); exit( EXIT_FAILURE ); } struct berval base; int interval = 10; int numservers; server *servers; char *monfilter; struct berval at_namingContexts = BER_BVC("namingContexts"); struct berval at_monitorOpCompleted = BER_BVC("monitorOpCompleted"); struct berval at_olmMDBEntries = BER_BVC("olmMDBEntries"); struct berval at_contextCSN = BER_BVC("contextCSN"); void timestamp(time_t *tt) { struct tm *tm = gmtime(tt); printf("%d-%02d-%02d %02d:%02d:%02d", tm->tm_year + 1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); } void deltat(time_t *tt) { struct tm *tm = gmtime(tt); if (tm->tm_mday-1) printf("%02d+", tm->tm_mday-1); printf("%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec); } static char *clearscreen = "\033[H\033[2J"; void display() { int i, j; struct timeval now; struct tm *tm; time_t now_t; gettimeofday(&now, NULL); now_t = now.tv_sec; printf("%s", clearscreen); timestamp(&now_t); printf("\n"); for (i=0; i servers[i].times[j].maxlag) servers[i].times[j].maxlag = deltatt; } else { servers[i].times[j].lag = 0; printf(", sync'd"); } if (servers[i].times[j].maxlag) { printf(", max delta "); deltat( &servers[i].times[j].maxlag ); } } } } } } printf("\n"); } if ( servers[i].csn_prev.num != servers[i].csn_curr.num ) { servers[i].csn_prev.sids = realloc(servers[i].csn_prev.sids, servers[i].csn_curr.num * sizeof(int)); servers[i].csn_prev.vals = realloc(servers[i].csn_prev.vals, servers[i].csn_curr.num * sizeof(struct berval)); servers[i].csn_prev.tvs = realloc(servers[i].csn_prev.tvs, servers[i].csn_curr.num * sizeof(struct timeval)); for (j=servers[i].csn_prev.num; j < servers[i].csn_curr.num; j++) { BER_BVZERO( &servers[i].csn_prev.vals[j] ); } servers[i].csn_prev.num = servers[i].csn_curr.num; for (j=0; jops[op] = strtoul( bvals[0].bv_val, NULL, 0 ); done = 1; } if ( bvals ) { ber_memfree( bvals ); bvals = NULL; } if ( done ) break; } ber_free( ber, 0 ); e = ldap_next_entry( ld, e ); if ( !e ) break; ldap_get_dn_ber( ld, e, &ber, &dn ); op++; } while ( op < SLAP_OP_LAST ); } int slap_parse_csn_sid( struct berval *csnp ) { char *p, *q; struct berval csn = *csnp; int i; p = ber_bvchr( &csn, '#' ); if ( !p ) return -1; p++; csn.bv_len -= p - csn.bv_val; csn.bv_val = p; p = ber_bvchr( &csn, '#' ); if ( !p ) return -1; p++; csn.bv_len -= p - csn.bv_val; csn.bv_val = p; q = ber_bvchr( &csn, '#' ); if ( !q ) return -1; csn.bv_len = q - p; i = strtol( p, &q, 16 ); if ( p == q || q != p + csn.bv_len || i < 0 || i > SLAP_SYNC_SID_MAX ) { i = -1; } return i; } void get_csns( csns *c, struct berval *bvs ) { int i; for (i=0; bvs[i].bv_val; i++) ; if ( c->num != i ) { int j; c->vals = realloc( c->vals, i*sizeof(struct berval)); c->sids = realloc( c->sids, i*sizeof(int)); c->tvs = realloc( c->tvs, i*sizeof(struct timeval)); for (j=c->num; jvals[j] ); } } c->num = i; for (i=0; inum; i++) { struct lutil_tm tm; struct lutil_timet tt; ber_bvreplace( &c->vals[i], &bvs[i] ); c->sids[i] = slap_parse_csn_sid( &bvs[i] ); lutil_parsetime(c->vals[i].bv_val, &tm); c->tvs[i].tv_usec = tm.tm_usec; lutil_tm2time( &tm, &tt ); c->tvs[i].tv_sec = tt.tt_sec; } } int main( int argc, char **argv ) { int i, rc, *msg1, *msg2; char **sids = NULL; struct tester_conn_args *config; config = tester_init( "slapd-watcher", TESTER_TESTER ); config->authmethod = LDAP_AUTH_SIMPLE; while ( ( i = getopt( argc, argv, "D:O:R:U:X:Y:b:d:i:s:w:x" ) ) != EOF ) { switch ( i ) { case 'b': /* base DN for contextCSN lookups */ ber_str2bv( optarg, 0, 0, &base ); break; case 'i': interval = atoi(optarg); break; case 's': sids = ldap_str2charray( optarg, "," ); break; default: if ( tester_config_opt( config, i, optarg ) == LDAP_SUCCESS ) break; usage( argv[0], i ); break; } } tester_config_finish( config ); /* don't clear the screen if debug is enabled */ if (debug) clearscreen = "\n\n"; numservers = argc - optind; if ( !numservers ) usage( argv[0], 0 ); if ( sids ) { for (i=0; sids[i]; i++ ); if ( i != numservers ) { fprintf(stderr, "Number of sids doesn't equal number of server URLs\n"); exit( EXIT_FAILURE ); } } argv += optind; argc -= optind; servers = calloc( numservers, sizeof(server)); if ( base.bv_val ) { monfilter = "(|(entryDN:dnOneLevelMatch:=cn=Databases,cn=Monitor)" MONFILTER ")"; } else { monfilter = MONFILTER; } if ( numservers > 1 ) { for ( i=0; iuri = argv[i]; tester_init_ld( &servers[i].ld, config, 0 ); servers[i].flags = 0; { char *attrs[] = { at_namingContexts.bv_val, at_monitorOpCompleted.bv_val, at_olmMDBEntries.bv_val, NULL }; LDAPMessage *res = NULL, *e = NULL; BerElement *ber = NULL; LDAP *ld = servers[i].ld; struct berval dn, bv, *bvals, **bvp = &bvals; int j; rc = ldap_search_ext_s( ld, "cn=monitor", LDAP_SCOPE_SUBTREE, monfilter, attrs, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, &res ); switch(rc) { case LDAP_SIZELIMIT_EXCEEDED: case LDAP_TIMELIMIT_EXCEEDED: case LDAP_SUCCESS: gettimeofday( &servers[i].c_curr.time, 0 ); servers[i].flags |= HAS_MONITOR; for ( e = ldap_first_entry( ld, res ); e; e = ldap_next_entry( ld, e )) { ldap_get_dn_ber( ld, e, &ber, &dn ); if ( !strncasecmp( dn.bv_val, "cn=Database", sizeof("cn=Database")-1 ) || !strncasecmp( dn.bv_val, "cn=Frontend", sizeof("cn=Frontend")-1 )) { int matched = 0; for ( rc = ldap_get_attribute_ber( ld, e, ber, &bv, bvp ); rc == LDAP_SUCCESS; rc = ldap_get_attribute_ber( ld, e, ber, &bv, bvp )) { if ( bv.bv_val == NULL ) break; if (!ber_bvcmp( &bv, &at_namingContexts ) && bvals ) { for (j=0; bvals[j].bv_val; j++) { if ( !ber_bvstrcasecmp( &base, &bvals[j] )) { matched = 1; break; } } if (!matched) { ber_memfree( bvals ); bvals = NULL; break; } } if (!ber_bvcmp( &bv, &at_olmMDBEntries )) { ber_dupbv( &servers[i].monitorbase, &dn ); servers[i].flags |= HAS_ENTRIES; servers[i].c_curr.entries = strtoul( bvals[0].bv_val, NULL, 0 ); } ber_memfree( bvals ); bvals = NULL; } } else if (!strncasecmp( dn.bv_val, opnames[0].rdn.bv_val, opnames[0].rdn.bv_len )) { get_counters( ld, e, ber, &servers[i].c_curr ); break; } if ( ber ) ber_free( ber, 0 ); } break; case LDAP_NO_SUCH_OBJECT: /* no cn=monitor */ break; default: tester_ldap_error( ld, "ldap_search_ext_s(cn=Monitor)", NULL ); exit( EXIT_FAILURE ); } ldap_msgfree( res ); if ( base.bv_val ) { char *attr2[] = { at_contextCSN.bv_val, NULL }; rc = ldap_search_ext_s( ld, base.bv_val, LDAP_SCOPE_BASE, "(objectClass=*)", attr2, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, &res ); switch(rc) { case LDAP_SUCCESS: e = ldap_first_entry( ld, res ); if ( e ) { servers[i].flags |= HAS_BASE; ldap_get_dn_ber( ld, e, &ber, &dn ); for ( rc = ldap_get_attribute_ber( ld, e, ber, &bv, bvp ); rc == LDAP_SUCCESS; rc = ldap_get_attribute_ber( ld, e, ber, &bv, bvp )) { int done = 0; if ( bv.bv_val == NULL ) break; if ( bvals ) { if ( !ber_bvcmp( &bv, &at_contextCSN )) { get_csns( &servers[i].csn_curr, bvals ); done = 1; } ber_memfree( bvals ); bvals = NULL; if ( done ) break; } } } ldap_msgfree( res ); break; default: tester_ldap_error( ld, "ldap_search_ext_s(baseDN)", NULL ); exit( EXIT_FAILURE ); } } } } for (i=0; i