1/* monitor.c - monitor ldap backend */ 2/* $OpenLDAP$ */ 3/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 4 * 5 * Copyright 2003-2011 The OpenLDAP Foundation. 6 * Portions Copyright 1999-2003 Howard Chu. 7 * Portions Copyright 2000-2003 Pierangelo Masarati. 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/* ACKNOWLEDGEMENTS: 19 * This work was initially developed by the Howard Chu for inclusion 20 * in OpenLDAP Software and subsequently enhanced by Pierangelo 21 * Masarati. 22 */ 23 24#include "portable.h" 25 26#include <stdio.h> 27#include <ac/string.h> 28#include <ac/unistd.h> 29#include <ac/stdlib.h> 30#include <ac/errno.h> 31#include <sys/stat.h> 32#include "lutil.h" 33#include "back-ldap.h" 34 35#include "config.h" 36 37static ObjectClass *oc_olmLDAPDatabase; 38 39static AttributeDescription *ad_olmDbURIList; 40 41/* 42 * NOTE: there's some confusion in monitor OID arc; 43 * by now, let's consider: 44 * 45 * Subsystems monitor attributes 1.3.6.1.4.1.4203.666.1.55.0 46 * Databases monitor attributes 1.3.6.1.4.1.4203.666.1.55.0.1 47 * LDAP database monitor attributes 1.3.6.1.4.1.4203.666.1.55.0.1.2 48 * 49 * Subsystems monitor objectclasses 1.3.6.1.4.1.4203.666.3.16.0 50 * Databases monitor objectclasses 1.3.6.1.4.1.4203.666.3.16.0.1 51 * LDAP database monitor objectclasses 1.3.6.1.4.1.4203.666.3.16.0.1.2 52 */ 53 54static struct { 55 char *name; 56 char *oid; 57} s_oid[] = { 58 { "olmLDAPAttributes", "olmDatabaseAttributes:2" }, 59 { "olmLDAPObjectClasses", "olmDatabaseObjectClasses:2" }, 60 61 { NULL } 62}; 63 64static struct { 65 char *desc; 66 AttributeDescription **ad; 67} s_at[] = { 68 { "( olmLDAPAttributes:1 " 69 "NAME ( 'olmDbURIList' ) " 70 "DESC 'List of URIs a proxy is serving; can be modified run-time' " 71 "SUP managedInfo )", 72 &ad_olmDbURIList }, 73 74 { NULL } 75}; 76 77static struct { 78 char *desc; 79 ObjectClass **oc; 80} s_oc[] = { 81 /* augments an existing object, so it must be AUXILIARY 82 * FIXME: derive from some ABSTRACT "monitoredEntity"? */ 83 { "( olmLDAPObjectClasses:1 " 84 "NAME ( 'olmLDAPDatabase' ) " 85 "SUP top AUXILIARY " 86 "MAY ( " 87 "olmDbURIList " 88 ") )", 89 &oc_olmLDAPDatabase }, 90 91 { NULL } 92}; 93 94static int 95ldap_back_monitor_info_destroy( ldapinfo_t * li ) 96{ 97 if ( !BER_BVISNULL( &li->li_monitor_info.lmi_rdn ) ) 98 ch_free( li->li_monitor_info.lmi_rdn.bv_val ); 99 if ( !BER_BVISNULL( &li->li_monitor_info.lmi_nrdn ) ) 100 ch_free( li->li_monitor_info.lmi_nrdn.bv_val ); 101 if ( !BER_BVISNULL( &li->li_monitor_info.lmi_filter ) ) 102 ch_free( li->li_monitor_info.lmi_filter.bv_val ); 103 if ( !BER_BVISNULL( &li->li_monitor_info.lmi_more_filter ) ) 104 ch_free( li->li_monitor_info.lmi_more_filter.bv_val ); 105 106 memset( &li->li_monitor_info, 0, sizeof( li->li_monitor_info ) ); 107 108 return 0; 109} 110 111static int 112ldap_back_monitor_update( 113 Operation *op, 114 SlapReply *rs, 115 Entry *e, 116 void *priv ) 117{ 118 ldapinfo_t *li = (ldapinfo_t *)priv; 119 120 Attribute *a; 121 122 /* update olmDbURIList */ 123 a = attr_find( e->e_attrs, ad_olmDbURIList ); 124 if ( a != NULL ) { 125 struct berval bv; 126 127 assert( a->a_vals != NULL ); 128 assert( !BER_BVISNULL( &a->a_vals[ 0 ] ) ); 129 assert( BER_BVISNULL( &a->a_vals[ 1 ] ) ); 130 131 ldap_pvt_thread_mutex_lock( &li->li_uri_mutex ); 132 if ( li->li_uri ) { 133 ber_str2bv( li->li_uri, 0, 0, &bv ); 134 if ( !bvmatch( &a->a_vals[ 0 ], &bv ) ) { 135 ber_bvreplace( &a->a_vals[ 0 ], &bv ); 136 } 137 } 138 ldap_pvt_thread_mutex_unlock( &li->li_uri_mutex ); 139 } 140 141 return SLAP_CB_CONTINUE; 142} 143 144static int 145ldap_back_monitor_modify( 146 Operation *op, 147 SlapReply *rs, 148 Entry *e, 149 void *priv ) 150{ 151 ldapinfo_t *li = (ldapinfo_t *) priv; 152 153 Attribute *save_attrs = NULL; 154 Modifications *ml, 155 *ml_olmDbURIList = NULL; 156 struct berval ul = BER_BVNULL; 157 int got = 0; 158 159 for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) { 160 if ( ml->sml_desc == ad_olmDbURIList ) { 161 if ( ml_olmDbURIList != NULL ) { 162 rs->sr_err = LDAP_CONSTRAINT_VIOLATION; 163 rs->sr_text = "conflicting modifications"; 164 goto done; 165 } 166 167 if ( ml->sml_op != LDAP_MOD_REPLACE ) { 168 rs->sr_err = LDAP_CONSTRAINT_VIOLATION; 169 rs->sr_text = "modification not allowed"; 170 goto done; 171 } 172 173 ml_olmDbURIList = ml; 174 got++; 175 continue; 176 } 177 } 178 179 if ( got == 0 ) { 180 return SLAP_CB_CONTINUE; 181 } 182 183 save_attrs = attrs_dup( e->e_attrs ); 184 185 if ( ml_olmDbURIList != NULL ) { 186 Attribute *a = NULL; 187 LDAPURLDesc *ludlist = NULL; 188 int rc; 189 190 ml = ml_olmDbURIList; 191 assert( ml->sml_nvalues != NULL ); 192 193 if ( BER_BVISNULL( &ml->sml_nvalues[ 0 ] ) ) { 194 rs->sr_err = LDAP_CONSTRAINT_VIOLATION; 195 rs->sr_text = "no value provided"; 196 goto done; 197 } 198 199 if ( !BER_BVISNULL( &ml->sml_nvalues[ 1 ] ) ) { 200 rs->sr_err = LDAP_CONSTRAINT_VIOLATION; 201 rs->sr_text = "multiple values provided"; 202 goto done; 203 } 204 205 rc = ldap_url_parselist_ext( &ludlist, 206 ml->sml_nvalues[ 0 ].bv_val, NULL, 207 LDAP_PVT_URL_PARSE_NOEMPTY_HOST 208 | LDAP_PVT_URL_PARSE_DEF_PORT ); 209 if ( rc != LDAP_URL_SUCCESS ) { 210 rs->sr_err = LDAP_INVALID_SYNTAX; 211 rs->sr_text = "unable to parse URI list"; 212 goto done; 213 } 214 215 ul.bv_val = ldap_url_list2urls( ludlist ); 216 ldap_free_urllist( ludlist ); 217 if ( ul.bv_val == NULL ) { 218 rs->sr_err = LDAP_OTHER; 219 goto done; 220 } 221 ul.bv_len = strlen( ul.bv_val ); 222 223 a = attr_find( e->e_attrs, ad_olmDbURIList ); 224 if ( a != NULL ) { 225 if ( a->a_nvals == a->a_vals ) { 226 a->a_nvals = ch_calloc( sizeof( struct berval ), 2 ); 227 } 228 229 ber_bvreplace( &a->a_vals[ 0 ], &ul ); 230 ber_bvreplace( &a->a_nvals[ 0 ], &ul ); 231 232 } else { 233 attr_merge_normalize_one( e, ad_olmDbURIList, &ul, NULL ); 234 } 235 } 236 237 /* apply changes */ 238 if ( !BER_BVISNULL( &ul ) ) { 239 ldap_pvt_thread_mutex_lock( &li->li_uri_mutex ); 240 if ( li->li_uri ) { 241 ch_free( li->li_uri ); 242 } 243 li->li_uri = ul.bv_val; 244 ldap_pvt_thread_mutex_unlock( &li->li_uri_mutex ); 245 246 BER_BVZERO( &ul ); 247 } 248 249done:; 250 if ( !BER_BVISNULL( &ul ) ) { 251 ldap_memfree( ul.bv_val ); 252 } 253 254 if ( rs->sr_err == LDAP_SUCCESS ) { 255 attrs_free( save_attrs ); 256 return SLAP_CB_CONTINUE; 257 } 258 259 attrs_free( e->e_attrs ); 260 e->e_attrs = save_attrs; 261 262 return rs->sr_err; 263} 264 265static int 266ldap_back_monitor_free( 267 Entry *e, 268 void **priv ) 269{ 270 ldapinfo_t *li = (ldapinfo_t *)(*priv); 271 272 *priv = NULL; 273 274 if ( !slapd_shutdown && !BER_BVISNULL( &li->li_monitor_info.lmi_rdn ) ) { 275 ldap_back_monitor_info_destroy( li ); 276 } 277 278 return SLAP_CB_CONTINUE; 279} 280 281static int 282ldap_back_monitor_conn_create( 283 Operation *op, 284 SlapReply *rs, 285 struct berval *ndn, 286 Entry *e_parent, 287 Entry **ep ) 288{ 289 monitor_entry_t *mp_parent; 290 ldap_monitor_info_t *lmi; 291 ldapinfo_t *li; 292 293 assert( e_parent->e_private != NULL ); 294 295 mp_parent = e_parent->e_private; 296 lmi = (ldap_monitor_info_t *)mp_parent->mp_info; 297 li = lmi->lmi_li; 298 299 /* do the hard work! */ 300 301 return 1; 302} 303 304/* 305 * call from within ldap_back_initialize() 306 */ 307static int 308ldap_back_monitor_initialize( void ) 309{ 310 int i, code; 311 ConfigArgs c; 312 char *argv[ 3 ]; 313 314 static int ldap_back_monitor_initialized = 0; 315 316 /* set to 0 when successfully initialized; otherwise, remember failure */ 317 static int ldap_back_monitor_initialized_failure = 1; 318 319 /* register schema here; if compiled as dynamic object, 320 * must be loaded __after__ back_monitor.la */ 321 322 if ( ldap_back_monitor_initialized++ ) { 323 return ldap_back_monitor_initialized_failure; 324 } 325 326 if ( backend_info( "monitor" ) == NULL ) { 327 return -1; 328 } 329 330 argv[ 0 ] = "back-ldap monitor"; 331 c.argv = argv; 332 c.argc = 3; 333 c.fname = argv[0]; 334 for ( i = 0; s_oid[ i ].name; i++ ) { 335 336 argv[ 1 ] = s_oid[ i ].name; 337 argv[ 2 ] = s_oid[ i ].oid; 338 339 if ( parse_oidm( &c, 0, NULL ) != 0 ) { 340 Debug( LDAP_DEBUG_ANY, 341 "ldap_back_monitor_initialize: unable to add " 342 "objectIdentifier \"%s=%s\"\n", 343 s_oid[ i ].name, s_oid[ i ].oid, 0 ); 344 return 2; 345 } 346 } 347 348 for ( i = 0; s_at[ i ].desc != NULL; i++ ) { 349 code = register_at( s_at[ i ].desc, s_at[ i ].ad, 1 ); 350 if ( code != LDAP_SUCCESS ) { 351 Debug( LDAP_DEBUG_ANY, 352 "ldap_back_monitor_initialize: register_at failed for attributeType (%s)\n", 353 s_at[ i ].desc, 0, 0 ); 354 return 3; 355 356 } else { 357 (*s_at[ i ].ad)->ad_type->sat_flags |= SLAP_AT_HIDE; 358 } 359 } 360 361 for ( i = 0; s_oc[ i ].desc != NULL; i++ ) { 362 code = register_oc( s_oc[ i ].desc, s_oc[ i ].oc, 1 ); 363 if ( code != LDAP_SUCCESS ) { 364 Debug( LDAP_DEBUG_ANY, 365 "ldap_back_monitor_initialize: register_oc failed for objectClass (%s)\n", 366 s_oc[ i ].desc, 0, 0 ); 367 return 4; 368 369 } else { 370 (*s_oc[ i ].oc)->soc_flags |= SLAP_OC_HIDE; 371 } 372 } 373 374 return ( ldap_back_monitor_initialized_failure = LDAP_SUCCESS ); 375} 376 377/* 378 * call from within ldap_back_db_init() 379 */ 380int 381ldap_back_monitor_db_init( BackendDB *be ) 382{ 383 int rc; 384 385 rc = ldap_back_monitor_initialize(); 386 if ( rc != LDAP_SUCCESS ) { 387 return rc; 388 } 389 390#if 0 /* uncomment to turn monitoring on by default */ 391 SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_MONITORING; 392#endif 393 394 return 0; 395} 396 397/* 398 * call from within ldap_back_db_open() 399 */ 400int 401ldap_back_monitor_db_open( BackendDB *be ) 402{ 403 ldapinfo_t *li = (ldapinfo_t *) be->be_private; 404 char buf[ BACKMONITOR_BUFSIZE ]; 405 Entry *e = NULL; 406 monitor_callback_t *cb = NULL; 407 struct berval suffix, *filter, *base; 408 char *ptr; 409 time_t now; 410 char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ]; 411 struct berval timestamp; 412 int rc = 0; 413 BackendInfo *mi; 414 monitor_extra_t *mbe; 415 416 if ( !SLAP_DBMONITORING( be ) ) { 417 return 0; 418 } 419 420 /* check if monitor is configured and usable */ 421 mi = backend_info( "monitor" ); 422 if ( !mi || !mi->bi_extra ) { 423 SLAP_DBFLAGS( be ) ^= SLAP_DBFLAG_MONITORING; 424 return 0; 425 } 426 mbe = mi->bi_extra; 427 428 /* don't bother if monitor is not configured */ 429 if ( !mbe->is_configured() ) { 430 static int warning = 0; 431 432 if ( warning++ == 0 ) { 433 Debug( LDAP_DEBUG_ANY, "ldap_back_monitor_db_open: " 434 "monitoring disabled; " 435 "configure monitor database to enable\n", 436 0, 0, 0 ); 437 } 438 439 return 0; 440 } 441 442 /* set up the fake subsystem that is used to create 443 * the volatile connection entries */ 444 li->li_monitor_info.lmi_mss.mss_name = "back-ldap"; 445 li->li_monitor_info.lmi_mss.mss_flags = MONITOR_F_VOLATILE_CH; 446 li->li_monitor_info.lmi_mss.mss_create = ldap_back_monitor_conn_create; 447 448 li->li_monitor_info.lmi_li = li; 449 li->li_monitor_info.lmi_scope = LDAP_SCOPE_SUBORDINATE; 450 base = &li->li_monitor_info.lmi_base; 451 BER_BVSTR( base, "cn=databases,cn=monitor" ); 452 filter = &li->li_monitor_info.lmi_filter; 453 BER_BVZERO( filter ); 454 455 suffix.bv_len = ldap_bv2escaped_filter_value_len( &be->be_nsuffix[ 0 ] ); 456 if ( suffix.bv_len == be->be_nsuffix[ 0 ].bv_len ) { 457 suffix = be->be_nsuffix[ 0 ]; 458 459 } else { 460 ldap_bv2escaped_filter_value( &be->be_nsuffix[ 0 ], &suffix ); 461 } 462 463 filter->bv_len = STRLENOF( "(&" ) 464 + li->li_monitor_info.lmi_more_filter.bv_len 465 + STRLENOF( "(monitoredInfo=" ) 466 + strlen( be->bd_info->bi_type ) 467 + STRLENOF( ")(!(monitorOverlay=" ) 468 + strlen( be->bd_info->bi_type ) 469 + STRLENOF( "))(namingContexts:distinguishedNameMatch:=" ) 470 + suffix.bv_len + STRLENOF( "))" ); 471 ptr = filter->bv_val = ch_malloc( filter->bv_len + 1 ); 472 ptr = lutil_strcopy( ptr, "(&" ); 473 ptr = lutil_strncopy( ptr, li->li_monitor_info.lmi_more_filter.bv_val, 474 li->li_monitor_info.lmi_more_filter.bv_len ); 475 ptr = lutil_strcopy( ptr, "(monitoredInfo=" ); 476 ptr = lutil_strcopy( ptr, be->bd_info->bi_type ); 477 ptr = lutil_strcopy( ptr, ")(!(monitorOverlay=" ); 478 ptr = lutil_strcopy( ptr, be->bd_info->bi_type ); 479 ptr = lutil_strcopy( ptr, "))(namingContexts:distinguishedNameMatch:=" ); 480 ptr = lutil_strncopy( ptr, suffix.bv_val, suffix.bv_len ); 481 ptr = lutil_strcopy( ptr, "))" ); 482 ptr[ 0 ] = '\0'; 483 assert( ptr == &filter->bv_val[ filter->bv_len ] ); 484 485 if ( suffix.bv_val != be->be_nsuffix[ 0 ].bv_val ) { 486 ch_free( suffix.bv_val ); 487 } 488 489 now = slap_get_time(); 490 timestamp.bv_val = timebuf; 491 timestamp.bv_len = sizeof( timebuf ); 492 slap_timestamp( &now, ×tamp ); 493 494 /* caller (e.g. an overlay based on back-ldap) may want to use 495 * a different RDN... */ 496 if ( BER_BVISNULL( &li->li_monitor_info.lmi_rdn ) ) { 497 ber_str2bv( "cn=Connections", 0, 1, &li->li_monitor_info.lmi_rdn ); 498 } 499 500 ptr = ber_bvchr( &li->li_monitor_info.lmi_rdn, '=' ); 501 assert( ptr != NULL ); 502 ptr[ 0 ] = '\0'; 503 ptr++; 504 505 snprintf( buf, sizeof( buf ), 506 "dn: %s=%s\n" 507 "objectClass: monitorContainer\n" 508 "%s: %s\n" 509 "creatorsName: %s\n" 510 "createTimestamp: %s\n" 511 "modifiersName: %s\n" 512 "modifyTimestamp: %s\n", 513 li->li_monitor_info.lmi_rdn.bv_val, 514 ptr, 515 li->li_monitor_info.lmi_rdn.bv_val, 516 ptr, 517 BER_BVISNULL( &be->be_rootdn ) ? SLAPD_ANONYMOUS : be->be_rootdn.bv_val, 518 timestamp.bv_val, 519 BER_BVISNULL( &be->be_rootdn ) ? SLAPD_ANONYMOUS : be->be_rootdn.bv_val, 520 timestamp.bv_val ); 521 e = str2entry( buf ); 522 if ( e == NULL ) { 523 rc = -1; 524 goto cleanup; 525 } 526 527 ptr[ -1 ] = '='; 528 529 /* add labeledURI and special, modifiable URI value */ 530 if ( li->li_uri != NULL ) { 531 struct berval bv; 532 LDAPURLDesc *ludlist = NULL; 533 int rc; 534 535 rc = ldap_url_parselist_ext( &ludlist, 536 li->li_uri, NULL, 537 LDAP_PVT_URL_PARSE_NOEMPTY_HOST 538 | LDAP_PVT_URL_PARSE_DEF_PORT ); 539 if ( rc != LDAP_URL_SUCCESS ) { 540 Debug( LDAP_DEBUG_ANY, 541 "ldap_back_monitor_db_open: " 542 "unable to parse URI list (ignored)\n", 543 0, 0, 0 ); 544 } else { 545 for ( ; ludlist != NULL; ) { 546 LDAPURLDesc *next = ludlist->lud_next; 547 548 bv.bv_val = ldap_url_desc2str( ludlist ); 549 assert( bv.bv_val != NULL ); 550 ldap_free_urldesc( ludlist ); 551 bv.bv_len = strlen( bv.bv_val ); 552 attr_merge_normalize_one( e, slap_schema.si_ad_labeledURI, 553 &bv, NULL ); 554 ch_free( bv.bv_val ); 555 556 ludlist = next; 557 } 558 } 559 560 ber_str2bv( li->li_uri, 0, 0, &bv ); 561 attr_merge_normalize_one( e, ad_olmDbURIList, 562 &bv, NULL ); 563 } 564 565 ber_dupbv( &li->li_monitor_info.lmi_nrdn, &e->e_nname ); 566 567 cb = ch_calloc( sizeof( monitor_callback_t ), 1 ); 568 cb->mc_update = ldap_back_monitor_update; 569 cb->mc_modify = ldap_back_monitor_modify; 570 cb->mc_free = ldap_back_monitor_free; 571 cb->mc_private = (void *)li; 572 573 rc = mbe->register_entry_parent( e, cb, 574 (monitor_subsys_t *)&li->li_monitor_info, 575 MONITOR_F_VOLATILE_CH, 576 base, LDAP_SCOPE_SUBORDINATE, filter ); 577 578cleanup:; 579 if ( rc != 0 ) { 580 if ( cb != NULL ) { 581 ch_free( cb ); 582 cb = NULL; 583 } 584 585 if ( e != NULL ) { 586 entry_free( e ); 587 e = NULL; 588 } 589 590 if ( !BER_BVISNULL( filter ) ) { 591 ch_free( filter->bv_val ); 592 BER_BVZERO( filter ); 593 } 594 } 595 596 /* store for cleanup */ 597 li->li_monitor_info.lmi_cb = (void *)cb; 598 599 if ( e != NULL ) { 600 entry_free( e ); 601 } 602 603 return rc; 604} 605 606/* 607 * call from within ldap_back_db_close() 608 */ 609int 610ldap_back_monitor_db_close( BackendDB *be ) 611{ 612 ldapinfo_t *li = (ldapinfo_t *) be->be_private; 613 614 if ( li && !BER_BVISNULL( &li->li_monitor_info.lmi_filter ) ) { 615 BackendInfo *mi; 616 monitor_extra_t *mbe; 617 618 /* check if monitor is configured and usable */ 619 mi = backend_info( "monitor" ); 620 if ( mi && mi->bi_extra ) { 621 mbe = mi->bi_extra; 622 623 mbe->unregister_entry_parent( 624 &li->li_monitor_info.lmi_nrdn, 625 (monitor_callback_t *)li->li_monitor_info.lmi_cb, 626 &li->li_monitor_info.lmi_base, 627 li->li_monitor_info.lmi_scope, 628 &li->li_monitor_info.lmi_filter ); 629 } 630 } 631 632 return 0; 633} 634 635/* 636 * call from within ldap_back_db_destroy() 637 */ 638int 639ldap_back_monitor_db_destroy( BackendDB *be ) 640{ 641 ldapinfo_t *li = (ldapinfo_t *) be->be_private; 642 643 if ( li ) { 644 (void)ldap_back_monitor_info_destroy( li ); 645 } 646 647 return 0; 648} 649 650