slapd-watcher.c revision 1.3 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