1/* $NetBSD: monitor.c,v 1.1.1.4 2010/12/12 15:22:58 adam Exp $ */ 2 3/* monitor.c - monitor bdb backend */ 4/* OpenLDAP: pkg/ldap/servers/slapd/back-bdb/monitor.c,v 1.19.2.15 2010/04/13 20:23:25 kurt Exp */ 5/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 * 7 * Copyright 2000-2010 The OpenLDAP Foundation. 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted only as authorized by the OpenLDAP 12 * Public License. 13 * 14 * A copy of this license is available in the file LICENSE in the 15 * top-level directory of the distribution or, alternatively, at 16 * <http://www.OpenLDAP.org/license.html>. 17 */ 18 19#include "portable.h" 20 21#include <stdio.h> 22#include <ac/string.h> 23#include <ac/unistd.h> 24#include <ac/stdlib.h> 25#include <ac/errno.h> 26#include <sys/stat.h> 27#include "lutil.h" 28#include "back-bdb.h" 29 30#include "../back-monitor/back-monitor.h" 31 32#include "config.h" 33 34static ObjectClass *oc_olmBDBDatabase; 35 36static AttributeDescription *ad_olmBDBEntryCache, 37 *ad_olmBDBDNCache, *ad_olmBDBIDLCache, 38 *ad_olmDbDirectory; 39 40#ifdef BDB_MONITOR_IDX 41static int 42bdb_monitor_idx_entry_add( 43 struct bdb_info *bdb, 44 Entry *e ); 45 46static AttributeDescription *ad_olmBDBNotIndexed; 47#endif /* BDB_MONITOR_IDX */ 48 49/* 50 * NOTE: there's some confusion in monitor OID arc; 51 * by now, let's consider: 52 * 53 * Subsystems monitor attributes 1.3.6.1.4.1.4203.666.1.55.0 54 * Databases monitor attributes 1.3.6.1.4.1.4203.666.1.55.0.1 55 * BDB database monitor attributes 1.3.6.1.4.1.4203.666.1.55.0.1.1 56 * 57 * Subsystems monitor objectclasses 1.3.6.1.4.1.4203.666.3.16.0 58 * Databases monitor objectclasses 1.3.6.1.4.1.4203.666.3.16.0.1 59 * BDB database monitor objectclasses 1.3.6.1.4.1.4203.666.3.16.0.1.1 60 */ 61 62static struct { 63 char *name; 64 char *oid; 65} s_oid[] = { 66 { "olmBDBAttributes", "olmDatabaseAttributes:1" }, 67 { "olmBDBObjectClasses", "olmDatabaseObjectClasses:1" }, 68 69 { NULL } 70}; 71 72static struct { 73 char *desc; 74 AttributeDescription **ad; 75} s_at[] = { 76 { "( olmBDBAttributes:1 " 77 "NAME ( 'olmBDBEntryCache' ) " 78 "DESC 'Number of items in Entry Cache' " 79 "SUP monitorCounter " 80 "NO-USER-MODIFICATION " 81 "USAGE dSAOperation )", 82 &ad_olmBDBEntryCache }, 83 84 { "( olmBDBAttributes:2 " 85 "NAME ( 'olmBDBDNCache' ) " 86 "DESC 'Number of items in DN Cache' " 87 "SUP monitorCounter " 88 "NO-USER-MODIFICATION " 89 "USAGE dSAOperation )", 90 &ad_olmBDBDNCache }, 91 92 { "( olmBDBAttributes:3 " 93 "NAME ( 'olmBDBIDLCache' ) " 94 "DESC 'Number of items in IDL Cache' " 95 "SUP monitorCounter " 96 "NO-USER-MODIFICATION " 97 "USAGE dSAOperation )", 98 &ad_olmBDBIDLCache }, 99 100 { "( olmBDBAttributes:4 " 101 "NAME ( 'olmDbDirectory' ) " 102 "DESC 'Path name of the directory " 103 "where the database environment resides' " 104 "SUP monitoredInfo " 105 "NO-USER-MODIFICATION " 106 "USAGE dSAOperation )", 107 &ad_olmDbDirectory }, 108 109#ifdef BDB_MONITOR_IDX 110 { "( olmBDBAttributes:5 " 111 "NAME ( 'olmBDBNotIndexed' ) " 112 "DESC 'Missing indexes resulting from candidate selection' " 113 "SUP monitoredInfo " 114 "NO-USER-MODIFICATION " 115 "USAGE dSAOperation )", 116 &ad_olmBDBNotIndexed }, 117#endif /* BDB_MONITOR_IDX */ 118 119 { NULL } 120}; 121 122static struct { 123 char *desc; 124 ObjectClass **oc; 125} s_oc[] = { 126 /* augments an existing object, so it must be AUXILIARY 127 * FIXME: derive from some ABSTRACT "monitoredEntity"? */ 128 { "( olmBDBObjectClasses:1 " 129 "NAME ( 'olmBDBDatabase' ) " 130 "SUP top AUXILIARY " 131 "MAY ( " 132 "olmBDBEntryCache " 133 "$ olmBDBDNCache " 134 "$ olmBDBIDLCache " 135 "$ olmDbDirectory " 136#ifdef BDB_MONITOR_IDX 137 "$ olmBDBNotIndexed " 138#endif /* BDB_MONITOR_IDX */ 139 ") )", 140 &oc_olmBDBDatabase }, 141 142 { NULL } 143}; 144 145static int 146bdb_monitor_update( 147 Operation *op, 148 SlapReply *rs, 149 Entry *e, 150 void *priv ) 151{ 152 struct bdb_info *bdb = (struct bdb_info *) priv; 153 Attribute *a; 154 155 char buf[ BUFSIZ ]; 156 struct berval bv; 157 158 assert( ad_olmBDBEntryCache != NULL ); 159 160 a = attr_find( e->e_attrs, ad_olmBDBEntryCache ); 161 assert( a != NULL ); 162 bv.bv_val = buf; 163 bv.bv_len = snprintf( buf, sizeof( buf ), "%lu", bdb->bi_cache.c_cursize ); 164 ber_bvreplace( &a->a_vals[ 0 ], &bv ); 165 166 a = attr_find( e->e_attrs, ad_olmBDBDNCache ); 167 assert( a != NULL ); 168 bv.bv_len = snprintf( buf, sizeof( buf ), "%lu", bdb->bi_cache.c_eiused ); 169 ber_bvreplace( &a->a_vals[ 0 ], &bv ); 170 171 a = attr_find( e->e_attrs, ad_olmBDBIDLCache ); 172 assert( a != NULL ); 173 bv.bv_len = snprintf( buf, sizeof( buf ), "%lu", bdb->bi_idl_cache_size ); 174 ber_bvreplace( &a->a_vals[ 0 ], &bv ); 175 176#ifdef BDB_MONITOR_IDX 177 bdb_monitor_idx_entry_add( bdb, e ); 178#endif /* BDB_MONITOR_IDX */ 179 180 return SLAP_CB_CONTINUE; 181} 182 183#if 0 /* uncomment if required */ 184static int 185bdb_monitor_modify( 186 Operation *op, 187 SlapReply *rs, 188 Entry *e, 189 void *priv ) 190{ 191 return SLAP_CB_CONTINUE; 192} 193#endif 194 195static int 196bdb_monitor_free( 197 Entry *e, 198 void **priv ) 199{ 200 struct berval values[ 2 ]; 201 Modification mod = { 0 }; 202 203 const char *text; 204 char textbuf[ SLAP_TEXT_BUFLEN ]; 205 206 int i, rc; 207 208 /* NOTE: if slap_shutdown != 0, priv might have already been freed */ 209 *priv = NULL; 210 211 /* Remove objectClass */ 212 mod.sm_op = LDAP_MOD_DELETE; 213 mod.sm_desc = slap_schema.si_ad_objectClass; 214 mod.sm_values = values; 215 mod.sm_numvals = 1; 216 values[ 0 ] = oc_olmBDBDatabase->soc_cname; 217 BER_BVZERO( &values[ 1 ] ); 218 219 rc = modify_delete_values( e, &mod, 1, &text, 220 textbuf, sizeof( textbuf ) ); 221 /* don't care too much about return code... */ 222 223 /* remove attrs */ 224 mod.sm_values = NULL; 225 mod.sm_numvals = 0; 226 for ( i = 0; s_at[ i ].desc != NULL; i++ ) { 227 mod.sm_desc = *s_at[ i ].ad; 228 rc = modify_delete_values( e, &mod, 1, &text, 229 textbuf, sizeof( textbuf ) ); 230 /* don't care too much about return code... */ 231 } 232 233 return SLAP_CB_CONTINUE; 234} 235 236#define bdb_monitor_initialize BDB_SYMBOL(monitor_initialize) 237 238/* 239 * call from within bdb_initialize() 240 */ 241static int 242bdb_monitor_initialize( void ) 243{ 244 int i, code; 245 ConfigArgs c; 246 char *argv[ 3 ]; 247 248 static int bdb_monitor_initialized = 0; 249 250 if ( backend_info( "monitor" ) == NULL ) { 251 return -1; 252 } 253 254 if ( bdb_monitor_initialized++ ) { 255 return 0; 256 } 257 258 /* register schema here */ 259 260 argv[ 0 ] = "back-bdb/back-hdb monitor"; 261 c.argv = argv; 262 c.argc = 3; 263 c.fname = argv[0]; 264 265 for ( i = 0; s_oid[ i ].name; i++ ) { 266 c.lineno = i; 267 argv[ 1 ] = s_oid[ i ].name; 268 argv[ 2 ] = s_oid[ i ].oid; 269 270 if ( parse_oidm( &c, 0, NULL ) != 0 ) { 271 Debug( LDAP_DEBUG_ANY, LDAP_XSTRING(bdb_monitor_initialize) 272 ": unable to add " 273 "objectIdentifier \"%s=%s\"\n", 274 s_oid[ i ].name, s_oid[ i ].oid, 0 ); 275 return 1; 276 } 277 } 278 279 for ( i = 0; s_at[ i ].desc != NULL; i++ ) { 280 code = register_at( s_at[ i ].desc, s_at[ i ].ad, 1 ); 281 if ( code != LDAP_SUCCESS ) { 282 Debug( LDAP_DEBUG_ANY, LDAP_XSTRING(bdb_monitor_initialize) 283 ": register_at failed\n", 284 0, 0, 0 ); 285 } else { 286 (*s_at[ i ].ad)->ad_type->sat_flags |= SLAP_AT_HIDE; 287 } 288 } 289 290 for ( i = 0; s_oc[ i ].desc != NULL; i++ ) { 291 code = register_oc( s_oc[ i ].desc, s_oc[ i ].oc, 1 ); 292 if ( code != LDAP_SUCCESS ) { 293 Debug( LDAP_DEBUG_ANY, LDAP_XSTRING(bdb_monitor_initialize) 294 ": register_oc failed\n", 295 0, 0, 0 ); 296 } else { 297 (*s_oc[ i ].oc)->soc_flags |= SLAP_OC_HIDE; 298 } 299 } 300 301 return 0; 302} 303 304/* 305 * call from within bdb_db_init() 306 */ 307int 308bdb_monitor_db_init( BackendDB *be ) 309{ 310 struct bdb_info *bdb = (struct bdb_info *) be->be_private; 311 312 if ( bdb_monitor_initialize() == LDAP_SUCCESS ) { 313 /* monitoring in back-bdb is on by default */ 314 SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_MONITORING; 315 } 316 317#ifdef BDB_MONITOR_IDX 318 bdb->bi_idx = NULL; 319 ldap_pvt_thread_mutex_init( &bdb->bi_idx_mutex ); 320#endif /* BDB_MONITOR_IDX */ 321 322 return 0; 323} 324 325/* 326 * call from within bdb_db_open() 327 */ 328int 329bdb_monitor_db_open( BackendDB *be ) 330{ 331 struct bdb_info *bdb = (struct bdb_info *) be->be_private; 332 Attribute *a, *next; 333 monitor_callback_t *cb = NULL; 334 int rc = 0; 335 BackendInfo *mi; 336 monitor_extra_t *mbe; 337 struct berval dummy = BER_BVC(""); 338 339 if ( !SLAP_DBMONITORING( be ) ) { 340 return 0; 341 } 342 343 mi = backend_info( "monitor" ); 344 if ( !mi || !mi->bi_extra ) { 345 SLAP_DBFLAGS( be ) ^= SLAP_DBFLAG_MONITORING; 346 return 0; 347 } 348 mbe = mi->bi_extra; 349 350 /* don't bother if monitor is not configured */ 351 if ( !mbe->is_configured() ) { 352 static int warning = 0; 353 354 if ( warning++ == 0 ) { 355 Debug( LDAP_DEBUG_ANY, LDAP_XSTRING(bdb_monitor_db_open) 356 ": monitoring disabled; " 357 "configure monitor database to enable\n", 358 0, 0, 0 ); 359 } 360 361 return 0; 362 } 363 364 /* alloc as many as required (plus 1 for objectClass) */ 365 a = attrs_alloc( 1 + 4 ); 366 if ( a == NULL ) { 367 rc = 1; 368 goto cleanup; 369 } 370 371 a->a_desc = slap_schema.si_ad_objectClass; 372 attr_valadd( a, &oc_olmBDBDatabase->soc_cname, NULL, 1 ); 373 next = a->a_next; 374 375 { 376 struct berval bv = BER_BVC( "0" ); 377 378 next->a_desc = ad_olmBDBEntryCache; 379 attr_valadd( next, &bv, NULL, 1 ); 380 next = next->a_next; 381 382 next->a_desc = ad_olmBDBDNCache; 383 attr_valadd( next, &bv, NULL, 1 ); 384 next = next->a_next; 385 386 next->a_desc = ad_olmBDBIDLCache; 387 attr_valadd( next, &bv, NULL, 1 ); 388 next = next->a_next; 389 } 390 391 { 392 struct berval bv, nbv; 393 ber_len_t pathlen = 0, len = 0; 394 char path[ MAXPATHLEN ] = { '\0' }; 395 char *fname = bdb->bi_dbenv_home, 396 *ptr; 397 398 len = strlen( fname ); 399 if ( fname[ 0 ] != '/' ) { 400 /* get full path name */ 401 getcwd( path, sizeof( path ) ); 402 pathlen = strlen( path ); 403 404 if ( fname[ 0 ] == '.' && fname[ 1 ] == '/' ) { 405 fname += 2; 406 len -= 2; 407 } 408 } 409 410 bv.bv_len = pathlen + STRLENOF( "/" ) + len; 411 ptr = bv.bv_val = ch_malloc( bv.bv_len + STRLENOF( "/" ) + 1 ); 412 if ( pathlen ) { 413 ptr = lutil_strncopy( ptr, path, pathlen ); 414 ptr[ 0 ] = '/'; 415 ptr++; 416 } 417 ptr = lutil_strncopy( ptr, fname, len ); 418 if ( ptr[ -1 ] != '/' ) { 419 ptr[ 0 ] = '/'; 420 ptr++; 421 } 422 ptr[ 0 ] = '\0'; 423 424 attr_normalize_one( ad_olmDbDirectory, &bv, &nbv, NULL ); 425 426 next->a_desc = ad_olmDbDirectory; 427 next->a_vals = ch_calloc( sizeof( struct berval ), 2 ); 428 next->a_vals[ 0 ] = bv; 429 next->a_numvals = 1; 430 431 if ( BER_BVISNULL( &nbv ) ) { 432 next->a_nvals = next->a_vals; 433 434 } else { 435 next->a_nvals = ch_calloc( sizeof( struct berval ), 2 ); 436 next->a_nvals[ 0 ] = nbv; 437 } 438 439 next = next->a_next; 440 } 441 442 cb = ch_calloc( sizeof( monitor_callback_t ), 1 ); 443 cb->mc_update = bdb_monitor_update; 444#if 0 /* uncomment if required */ 445 cb->mc_modify = bdb_monitor_modify; 446#endif 447 cb->mc_free = bdb_monitor_free; 448 cb->mc_private = (void *)bdb; 449 450 /* make sure the database is registered; then add monitor attributes */ 451 rc = mbe->register_database( be, &bdb->bi_monitor.bdm_ndn ); 452 if ( rc == 0 ) { 453 rc = mbe->register_entry_attrs( &bdb->bi_monitor.bdm_ndn, a, cb, 454 &dummy, 0, &dummy ); 455 } 456 457cleanup:; 458 if ( rc != 0 ) { 459 if ( cb != NULL ) { 460 ch_free( cb ); 461 cb = NULL; 462 } 463 464 if ( a != NULL ) { 465 attrs_free( a ); 466 a = NULL; 467 } 468 } 469 470 /* store for cleanup */ 471 bdb->bi_monitor.bdm_cb = (void *)cb; 472 473 /* we don't need to keep track of the attributes, because 474 * bdb_monitor_free() takes care of everything */ 475 if ( a != NULL ) { 476 attrs_free( a ); 477 } 478 479 return rc; 480} 481 482/* 483 * call from within bdb_db_close() 484 */ 485int 486bdb_monitor_db_close( BackendDB *be ) 487{ 488 struct bdb_info *bdb = (struct bdb_info *) be->be_private; 489 490 if ( !BER_BVISNULL( &bdb->bi_monitor.bdm_ndn ) ) { 491 BackendInfo *mi = backend_info( "monitor" ); 492 monitor_extra_t *mbe; 493 494 if ( mi && &mi->bi_extra ) { 495 mbe = mi->bi_extra; 496 mbe->unregister_entry_callback( &bdb->bi_monitor.bdm_ndn, 497 (monitor_callback_t *)bdb->bi_monitor.bdm_cb, 498 NULL, 0, NULL ); 499 } 500 501 memset( &bdb->bi_monitor, 0, sizeof( bdb->bi_monitor ) ); 502 } 503 504 return 0; 505} 506 507/* 508 * call from within bdb_db_destroy() 509 */ 510int 511bdb_monitor_db_destroy( BackendDB *be ) 512{ 513#ifdef BDB_MONITOR_IDX 514 struct bdb_info *bdb = (struct bdb_info *) be->be_private; 515 516 /* TODO: free tree */ 517 ldap_pvt_thread_mutex_destroy( &bdb->bi_idx_mutex ); 518 avl_free( bdb->bi_idx, ch_free ); 519#endif /* BDB_MONITOR_IDX */ 520 521 return 0; 522} 523 524#ifdef BDB_MONITOR_IDX 525 526#define BDB_MONITOR_IDX_TYPES (4) 527 528typedef struct monitor_idx_t monitor_idx_t; 529 530struct monitor_idx_t { 531 AttributeDescription *idx_ad; 532 unsigned long idx_count[BDB_MONITOR_IDX_TYPES]; 533}; 534 535static int 536bdb_monitor_bitmask2key( slap_mask_t bitmask ) 537{ 538 int key; 539 540 for ( key = 0; key < 8 * (int)sizeof(slap_mask_t) && !( bitmask & 0x1U ); 541 key++ ) 542 bitmask >>= 1; 543 544 return key; 545} 546 547static struct berval idxbv[] = { 548 BER_BVC( "present=" ), 549 BER_BVC( "equality=" ), 550 BER_BVC( "approx=" ), 551 BER_BVC( "substr=" ), 552 BER_BVNULL 553}; 554 555static ber_len_t 556bdb_monitor_idx2len( monitor_idx_t *idx ) 557{ 558 int i; 559 ber_len_t len = 0; 560 561 for ( i = 0; i < BDB_MONITOR_IDX_TYPES; i++ ) { 562 if ( idx->idx_count[ i ] != 0 ) { 563 len += idxbv[i].bv_len; 564 } 565 } 566 567 return len; 568} 569 570static int 571monitor_idx_cmp( const void *p1, const void *p2 ) 572{ 573 const monitor_idx_t *idx1 = (const monitor_idx_t *)p1; 574 const monitor_idx_t *idx2 = (const monitor_idx_t *)p2; 575 576 return SLAP_PTRCMP( idx1->idx_ad, idx2->idx_ad ); 577} 578 579static int 580monitor_idx_dup( void *p1, void *p2 ) 581{ 582 monitor_idx_t *idx1 = (monitor_idx_t *)p1; 583 monitor_idx_t *idx2 = (monitor_idx_t *)p2; 584 585 return SLAP_PTRCMP( idx1->idx_ad, idx2->idx_ad ) == 0 ? -1 : 0; 586} 587 588int 589bdb_monitor_idx_add( 590 struct bdb_info *bdb, 591 AttributeDescription *desc, 592 slap_mask_t type ) 593{ 594 monitor_idx_t idx_dummy = { 0 }, 595 *idx; 596 int rc = 0, key; 597 598 idx_dummy.idx_ad = desc; 599 key = bdb_monitor_bitmask2key( type ) - 1; 600 if ( key >= BDB_MONITOR_IDX_TYPES ) { 601 /* invalid index type */ 602 return -1; 603 } 604 605 ldap_pvt_thread_mutex_lock( &bdb->bi_idx_mutex ); 606 607 idx = (monitor_idx_t *)avl_find( bdb->bi_idx, 608 (caddr_t)&idx_dummy, monitor_idx_cmp ); 609 if ( idx == NULL ) { 610 idx = (monitor_idx_t *)ch_calloc( sizeof( monitor_idx_t ), 1 ); 611 idx->idx_ad = desc; 612 idx->idx_count[ key ] = 1; 613 614 switch ( avl_insert( &bdb->bi_idx, (caddr_t)idx, 615 monitor_idx_cmp, monitor_idx_dup ) ) 616 { 617 case 0: 618 break; 619 620 default: 621 ch_free( idx ); 622 rc = -1; 623 } 624 625 } else { 626 idx->idx_count[ key ]++; 627 } 628 629 ldap_pvt_thread_mutex_unlock( &bdb->bi_idx_mutex ); 630 631 return rc; 632} 633 634static int 635bdb_monitor_idx_apply( void *v_idx, void *v_valp ) 636{ 637 monitor_idx_t *idx = (monitor_idx_t *)v_idx; 638 BerVarray *valp = (BerVarray *)v_valp; 639 640 struct berval bv; 641 char *ptr; 642 char count_buf[ BDB_MONITOR_IDX_TYPES ][ SLAP_TEXT_BUFLEN ]; 643 ber_len_t count_len[ BDB_MONITOR_IDX_TYPES ], 644 idx_len; 645 int i, num = 0; 646 647 idx_len = bdb_monitor_idx2len( idx ); 648 649 bv.bv_len = 0; 650 for ( i = 0; i < BDB_MONITOR_IDX_TYPES; i++ ) { 651 if ( idx->idx_count[ i ] == 0 ) { 652 continue; 653 } 654 655 count_len[ i ] = snprintf( count_buf[ i ], 656 sizeof( count_buf[ i ] ), "%lu", idx->idx_count[ i ] ); 657 bv.bv_len += count_len[ i ]; 658 num++; 659 } 660 661 bv.bv_len += idx->idx_ad->ad_cname.bv_len 662 + num 663 + idx_len; 664 ptr = bv.bv_val = ch_malloc( bv.bv_len + 1 ); 665 ptr = lutil_strcopy( ptr, idx->idx_ad->ad_cname.bv_val ); 666 for ( i = 0; i < BDB_MONITOR_IDX_TYPES; i++ ) { 667 if ( idx->idx_count[ i ] == 0 ) { 668 continue; 669 } 670 671 ptr[ 0 ] = '#'; 672 ++ptr; 673 ptr = lutil_strcopy( ptr, idxbv[ i ].bv_val ); 674 ptr = lutil_strcopy( ptr, count_buf[ i ] ); 675 } 676 677 ber_bvarray_add( valp, &bv ); 678 679 return 0; 680} 681 682static int 683bdb_monitor_idx_entry_add( 684 struct bdb_info *bdb, 685 Entry *e ) 686{ 687 BerVarray vals = NULL; 688 Attribute *a; 689 690 a = attr_find( e->e_attrs, ad_olmBDBNotIndexed ); 691 692 ldap_pvt_thread_mutex_lock( &bdb->bi_idx_mutex ); 693 694 avl_apply( bdb->bi_idx, bdb_monitor_idx_apply, 695 &vals, -1, AVL_INORDER ); 696 697 ldap_pvt_thread_mutex_unlock( &bdb->bi_idx_mutex ); 698 699 if ( vals != NULL ) { 700 if ( a != NULL ) { 701 assert( a->a_nvals == a->a_vals ); 702 703 ber_bvarray_free( a->a_vals ); 704 705 } else { 706 Attribute **ap; 707 708 for ( ap = &e->e_attrs; *ap != NULL; ap = &(*ap)->a_next ) 709 ; 710 *ap = attr_alloc( ad_olmBDBNotIndexed ); 711 a = *ap; 712 } 713 a->a_vals = vals; 714 a->a_nvals = a->a_vals; 715 } 716 717 return 0; 718} 719 720#endif /* BDB_MONITOR_IDX */ 721