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