1 /* $NetBSD: backend.c,v 1.2 2021/08/14 16:14:58 christos Exp $ */ 2 3 /* $OpenLDAP$ */ 4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 5 * 6 * Copyright 1998-2021 The OpenLDAP Foundation. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted only as authorized by the OpenLDAP 11 * Public License. 12 * 13 * A copy of this license is available in the file LICENSE in the 14 * top-level directory of the distribution or, alternatively, at 15 * <http://www.OpenLDAP.org/license.html>. 16 */ 17 18 #include <sys/cdefs.h> 19 __RCSID("$NetBSD: backend.c,v 1.2 2021/08/14 16:14:58 christos Exp $"); 20 21 #include "portable.h" 22 23 #include <ac/socket.h> 24 #include <ac/errno.h> 25 #include <ac/string.h> 26 #include <ac/time.h> 27 #include <ac/unistd.h> 28 29 #include <event2/event.h> 30 #include <event2/dns.h> 31 32 #include "lutil.h" 33 #include "lload.h" 34 35 static void 36 upstream_connect_cb( evutil_socket_t s, short what, void *arg ) 37 { 38 LloadPendingConnection *conn = arg; 39 LloadBackend *b = conn->backend; 40 int error = 0, rc = -1; 41 epoch_t epoch; 42 43 checked_lock( &b->b_mutex ); 44 Debug( LDAP_DEBUG_CONNS, "upstream_connect_cb: " 45 "fd=%d connection callback for backend uri='%s'\n", 46 s, b->b_uri.bv_val ); 47 48 if ( s != conn->fd ) { 49 /* backend_reset has been here first */ 50 goto preempted; 51 } 52 53 epoch = epoch_join(); 54 55 if ( what == EV_WRITE ) { 56 socklen_t optlen = sizeof(error); 57 58 if ( getsockopt( conn->fd, SOL_SOCKET, SO_ERROR, (void *)&error, 59 &optlen ) < 0 ) { 60 goto done; 61 } 62 if ( error == EINTR || error == EINPROGRESS || error == EWOULDBLOCK ) { 63 checked_unlock( &b->b_mutex ); 64 epoch_leave( epoch ); 65 return; 66 } else if ( error ) { 67 goto done; 68 } else if ( upstream_init( s, conn->backend ) == NULL ) { 69 goto done; 70 } 71 rc = LDAP_SUCCESS; 72 } 73 74 done: 75 epoch_leave( epoch ); 76 77 LDAP_LIST_REMOVE( conn, next ); 78 if ( rc ) { 79 evutil_closesocket( conn->fd ); 80 b->b_opening--; 81 b->b_failed++; 82 if ( what & EV_TIMEOUT ) { 83 Debug( LDAP_DEBUG_ANY, "upstream_connect_cb: " 84 "fd=%d connection timed out\n", 85 s ); 86 } else { 87 char ebuf[128]; 88 Debug( LDAP_DEBUG_ANY, "upstream_connect_cb: " 89 "fd=%d connection set up failed%s%s\n", 90 s, error ? ": " : "", 91 error ? sock_errstr( error, ebuf, sizeof(ebuf) ) : "" ); 92 } 93 backend_retry( b ); 94 } 95 preempted: 96 checked_unlock( &b->b_mutex ); 97 98 event_free( conn->event ); 99 ch_free( conn ); 100 } 101 102 static void 103 upstream_name_cb( int result, struct evutil_addrinfo *res, void *arg ) 104 { 105 LloadBackend *b = arg; 106 ber_socket_t s = AC_SOCKET_INVALID; 107 epoch_t epoch; 108 int rc; 109 110 if ( result == EVUTIL_EAI_CANCEL ) { 111 Debug( LDAP_DEBUG_ANY, "upstream_name_cb: " 112 "cancelled\n" ); 113 return; 114 } 115 116 checked_lock( &b->b_mutex ); 117 /* We were already running when backend_reset tried to cancel us, but were 118 * already stuck waiting for the mutex, nothing to do and b_opening has 119 * been decremented as well */ 120 if ( b->b_dns_req == NULL ) { 121 checked_unlock( &b->b_mutex ); 122 return; 123 } 124 b->b_dns_req = NULL; 125 126 epoch = epoch_join(); 127 if ( result || !res ) { 128 Debug( LDAP_DEBUG_ANY, "upstream_name_cb: " 129 "name resolution failed for backend '%s': %s\n", 130 b->b_uri.bv_val, evutil_gai_strerror( result ) ); 131 goto fail; 132 } 133 134 /* TODO: if we get failures, try the other addrinfos */ 135 if ( (s = socket( res->ai_family, SOCK_STREAM, 0 )) == 136 AC_SOCKET_INVALID ) { 137 goto fail; 138 } 139 140 if ( ber_pvt_socket_set_nonblock( s, 1 ) ) { 141 goto fail; 142 } 143 144 #if defined(SO_KEEPALIVE) || defined(TCP_NODELAY) 145 if ( b->b_proto == LDAP_PROTO_TCP ) { 146 int dummy = 1; 147 #ifdef SO_KEEPALIVE 148 if ( setsockopt( s, SOL_SOCKET, SO_KEEPALIVE, (char *)&dummy, 149 sizeof(dummy) ) == AC_SOCKET_ERROR ) { 150 Debug( LDAP_DEBUG_TRACE, "upstream_name_cb: " 151 "setsockopt(%d, SO_KEEPALIVE) failed (ignored).\n", 152 s ); 153 } 154 if ( bindconf.sb_keepalive.sk_idle > 0 ) { 155 #ifdef TCP_KEEPIDLE 156 if ( setsockopt( s, IPPROTO_TCP, TCP_KEEPIDLE, 157 (void *)&bindconf.sb_keepalive.sk_idle, 158 sizeof(bindconf.sb_keepalive.sk_idle) ) == 159 AC_SOCKET_ERROR ) { 160 Debug( LDAP_DEBUG_TRACE, "upstream_name_cb: " 161 "setsockopt(%d, TCP_KEEPIDLE) failed (ignored).\n", 162 s ); 163 } 164 #else 165 Debug( LDAP_DEBUG_TRACE, "upstream_name_cb: " 166 "sockopt TCP_KEEPIDLE not supported on this system.\n" ); 167 #endif /* TCP_KEEPIDLE */ 168 } 169 if ( bindconf.sb_keepalive.sk_probes > 0 ) { 170 #ifdef TCP_KEEPCNT 171 if ( setsockopt( s, IPPROTO_TCP, TCP_KEEPCNT, 172 (void *)&bindconf.sb_keepalive.sk_probes, 173 sizeof(bindconf.sb_keepalive.sk_probes) ) == 174 AC_SOCKET_ERROR ) { 175 Debug( LDAP_DEBUG_TRACE, "upstream_name_cb: " 176 "setsockopt(%d, TCP_KEEPCNT) failed (ignored).\n", 177 s ); 178 } 179 #else 180 Debug( LDAP_DEBUG_TRACE, "upstream_name_cb: " 181 "sockopt TCP_KEEPCNT not supported on this system.\n" ); 182 #endif /* TCP_KEEPCNT */ 183 } 184 if ( bindconf.sb_keepalive.sk_interval > 0 ) { 185 #ifdef TCP_KEEPINTVL 186 if ( setsockopt( s, IPPROTO_TCP, TCP_KEEPINTVL, 187 (void *)&bindconf.sb_keepalive.sk_interval, 188 sizeof(bindconf.sb_keepalive.sk_interval) ) == 189 AC_SOCKET_ERROR ) { 190 Debug( LDAP_DEBUG_TRACE, "upstream_name_cb: " 191 "setsockopt(%d, TCP_KEEPINTVL) failed (ignored).\n", 192 s ); 193 } 194 #else 195 Debug( LDAP_DEBUG_TRACE, "upstream_name_cb: " 196 "sockopt TCP_KEEPINTVL not supported on this system.\n" ); 197 #endif /* TCP_KEEPINTVL */ 198 } 199 #endif /* SO_KEEPALIVE */ 200 if ( bindconf.sb_tcp_user_timeout > 0 ) { 201 #ifdef TCP_USER_TIMEOUT 202 if ( setsockopt( s, IPPROTO_TCP, TCP_USER_TIMEOUT, 203 (void *)&bindconf.sb_tcp_user_timeout, 204 sizeof(bindconf.sb_tcp_user_timeout) ) == 205 AC_SOCKET_ERROR ) { 206 Debug( LDAP_DEBUG_TRACE, "upstream_name_cb: " 207 "setsockopt(%d, TCP_USER_TIMEOUT) failed (ignored).\n", 208 s ); 209 } 210 #else 211 Debug( LDAP_DEBUG_TRACE, "upstream_name_cb: " 212 "sockopt TCP_USER_TIMEOUT not supported on this " 213 "system.\n" ); 214 #endif /* TCP_USER_TIMEOUT */ 215 } 216 #ifdef TCP_NODELAY 217 if ( setsockopt( s, IPPROTO_TCP, TCP_NODELAY, (char *)&dummy, 218 sizeof(dummy) ) == AC_SOCKET_ERROR ) { 219 Debug( LDAP_DEBUG_TRACE, "upstream_name_cb: " 220 "setsockopt(%d, TCP_NODELAY) failed (ignored).\n", 221 s ); 222 } 223 #endif /* TCP_NODELAY */ 224 } 225 #endif /* SO_KEEPALIVE || TCP_NODELAY */ 226 227 if ( res->ai_family == PF_INET ) { 228 struct sockaddr_in *ai = (struct sockaddr_in *)res->ai_addr; 229 ai->sin_port = htons( b->b_port ); 230 rc = connect( s, (struct sockaddr *)ai, res->ai_addrlen ); 231 } else { 232 struct sockaddr_in6 *ai = (struct sockaddr_in6 *)res->ai_addr; 233 ai->sin6_port = htons( b->b_port ); 234 rc = connect( s, (struct sockaddr *)ai, res->ai_addrlen ); 235 } 236 /* Asynchronous connect */ 237 if ( rc ) { 238 LloadPendingConnection *conn; 239 240 if ( errno != EINPROGRESS && errno != EWOULDBLOCK ) { 241 Debug( LDAP_DEBUG_ANY, "upstream_name_cb: " 242 "failed to connect to server '%s'\n", 243 b->b_uri.bv_val ); 244 evutil_closesocket( s ); 245 goto fail; 246 } 247 248 conn = ch_calloc( 1, sizeof(LloadPendingConnection) ); 249 LDAP_LIST_ENTRY_INIT( conn, next ); 250 conn->backend = b; 251 conn->fd = s; 252 253 conn->event = event_new( lload_get_base( s ), s, EV_WRITE|EV_PERSIST, 254 upstream_connect_cb, conn ); 255 if ( !conn->event ) { 256 Debug( LDAP_DEBUG_ANY, "upstream_name_cb: " 257 "failed to acquire an event to finish upstream " 258 "connection setup.\n" ); 259 ch_free( conn ); 260 evutil_closesocket( s ); 261 goto fail; 262 } 263 264 event_add( conn->event, lload_timeout_net ); 265 LDAP_LIST_INSERT_HEAD( &b->b_connecting, conn, next ); 266 Debug( LDAP_DEBUG_CONNS, "upstream_name_cb: " 267 "connection to backend uri=%s in progress\n", 268 b->b_uri.bv_val ); 269 } else if ( upstream_init( s, b ) == NULL ) { 270 goto fail; 271 } 272 273 checked_unlock( &b->b_mutex ); 274 evutil_freeaddrinfo( res ); 275 epoch_leave( epoch ); 276 return; 277 278 fail: 279 if ( s != AC_SOCKET_INVALID ) { 280 evutil_closesocket( s ); 281 } 282 b->b_opening--; 283 b->b_failed++; 284 backend_retry( b ); 285 checked_unlock( &b->b_mutex ); 286 if ( res ) { 287 evutil_freeaddrinfo( res ); 288 } 289 epoch_leave( epoch ); 290 } 291 292 LloadConnection * 293 backend_select( LloadOperation *op, int *res ) 294 { 295 LloadBackend *b, *first, *next; 296 297 checked_lock( &backend_mutex ); 298 first = b = current_backend; 299 checked_unlock( &backend_mutex ); 300 301 *res = LDAP_UNAVAILABLE; 302 303 if ( !first ) { 304 return NULL; 305 } 306 307 /* TODO: Two runs, one with trylock, then one actually locked if we don't 308 * find anything? */ 309 do { 310 lload_c_head *head; 311 LloadConnection *c; 312 313 checked_lock( &b->b_mutex ); 314 next = LDAP_CIRCLEQ_LOOP_NEXT( &backend, b, b_next ); 315 316 if ( b->b_max_pending && b->b_n_ops_executing >= b->b_max_pending ) { 317 Debug( LDAP_DEBUG_CONNS, "backend_select: " 318 "backend %s too busy\n", 319 b->b_uri.bv_val ); 320 checked_unlock( &b->b_mutex ); 321 b = next; 322 *res = LDAP_BUSY; 323 continue; 324 } 325 326 if ( op->o_tag == LDAP_REQ_BIND 327 #ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS 328 && !(lload_features & LLOAD_FEATURE_VC) 329 #endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */ 330 ) { 331 head = &b->b_bindconns; 332 } else { 333 head = &b->b_conns; 334 } 335 if ( !LDAP_CIRCLEQ_EMPTY( head ) ) { 336 *res = LDAP_BUSY; 337 } 338 339 LDAP_CIRCLEQ_FOREACH ( c, head, c_next ) { 340 checked_lock( &c->c_io_mutex ); 341 CONNECTION_LOCK(c); 342 if ( c->c_state == LLOAD_C_READY && !c->c_pendingber && 343 ( b->b_max_conn_pending == 0 || 344 c->c_n_ops_executing < b->b_max_conn_pending ) ) { 345 Debug( LDAP_DEBUG_CONNS, "backend_select: " 346 "selected connection connid=%lu for client " 347 "connid=%lu msgid=%d\n", 348 c->c_connid, op->o_client_connid, op->o_client_msgid ); 349 350 /* c_state is DYING if we're about to be unlinked */ 351 assert( IS_ALIVE( c, c_live ) ); 352 353 /* 354 * Round-robin step: 355 * Rotate the queue to put this connection at the end, same for 356 * the backend. 357 */ 358 LDAP_CIRCLEQ_MAKE_TAIL( head, c, c_next ); 359 360 checked_lock( &backend_mutex ); 361 current_backend = next; 362 checked_unlock( &backend_mutex ); 363 364 b->b_n_ops_executing++; 365 if ( op->o_tag == LDAP_REQ_BIND ) { 366 b->b_counters[LLOAD_STATS_OPS_BIND].lc_ops_received++; 367 } else { 368 b->b_counters[LLOAD_STATS_OPS_OTHER].lc_ops_received++; 369 } 370 c->c_n_ops_executing++; 371 c->c_counters.lc_ops_received++; 372 373 checked_unlock( &b->b_mutex ); 374 *res = LDAP_SUCCESS; 375 CONNECTION_ASSERT_LOCKED(c); 376 assert_locked( &c->c_io_mutex ); 377 return c; 378 } 379 CONNECTION_UNLOCK(c); 380 checked_unlock( &c->c_io_mutex ); 381 } 382 checked_unlock( &b->b_mutex ); 383 384 b = next; 385 } while ( b != first ); 386 387 return NULL; 388 } 389 390 /* 391 * Will schedule a connection attempt if there is a need for it. Need exclusive 392 * access to backend, its b_mutex is not touched here, though. 393 */ 394 void 395 backend_retry( LloadBackend *b ) 396 { 397 int requested; 398 399 if ( slapd_shutdown ) { 400 Debug( LDAP_DEBUG_CONNS, "backend_retry: " 401 "shutting down\n" ); 402 return; 403 } 404 assert_locked( &b->b_mutex ); 405 406 requested = b->b_numconns; 407 #ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS 408 if ( !(lload_features & LLOAD_FEATURE_VC) ) 409 #endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */ 410 { 411 requested += b->b_numbindconns; 412 } 413 414 if ( b->b_active + b->b_bindavail + b->b_opening >= requested ) { 415 Debug( LDAP_DEBUG_CONNS, "backend_retry: " 416 "no more connections needed for this backend\n" ); 417 assert_locked( &b->b_mutex ); 418 return; 419 } 420 421 if ( b->b_opening > 0 ) { 422 Debug( LDAP_DEBUG_CONNS, "backend_retry: " 423 "retry in progress already\n" ); 424 assert( b->b_opening == 1 ); 425 assert_locked( &b->b_mutex ); 426 return; 427 } 428 429 /* We incremented b_opening when we activated the event, so it can't be 430 * pending */ 431 assert( !event_pending( b->b_retry_event, EV_TIMEOUT, NULL ) ); 432 b->b_opening++; 433 434 if ( b->b_failed > 0 ) { 435 Debug( LDAP_DEBUG_CONNS, "backend_retry: " 436 "scheduling a retry in %d ms\n", 437 b->b_retry_timeout ); 438 event_add( b->b_retry_event, &b->b_retry_tv ); 439 assert_locked( &b->b_mutex ); 440 return; 441 } 442 443 Debug( LDAP_DEBUG_CONNS, "backend_retry: " 444 "scheduling re-connection straight away\n" ); 445 446 if ( ldap_pvt_thread_pool_submit2( 447 &connection_pool, backend_connect_task, b, &b->b_cookie ) ) { 448 Debug( LDAP_DEBUG_ANY, "backend_retry: " 449 "failed to submit retry task, scheduling a retry instead\n" ); 450 /* The current implementation of ldap_pvt_thread_pool_submit2 can fail 451 * and still set (an invalid) cookie */ 452 b->b_cookie = NULL; 453 b->b_failed++; 454 event_add( b->b_retry_event, &b->b_retry_tv ); 455 } 456 assert_locked( &b->b_mutex ); 457 } 458 459 void 460 backend_connect( evutil_socket_t s, short what, void *arg ) 461 { 462 struct evutil_addrinfo hints = {}; 463 LloadBackend *b = arg; 464 struct evdns_getaddrinfo_request *request, *placeholder; 465 char *hostname; 466 epoch_t epoch; 467 468 checked_lock( &b->b_mutex ); 469 assert( b->b_dns_req == NULL ); 470 471 if ( b->b_cookie ) { 472 b->b_cookie = NULL; 473 } 474 475 if ( slapd_shutdown ) { 476 Debug( LDAP_DEBUG_CONNS, "backend_connect: " 477 "doing nothing, shutdown in progress\n" ); 478 b->b_opening--; 479 checked_unlock( &b->b_mutex ); 480 return; 481 } 482 483 epoch = epoch_join(); 484 485 Debug( LDAP_DEBUG_CONNS, "backend_connect: " 486 "%sattempting connection to %s\n", 487 (what & EV_TIMEOUT) ? "retry timeout finished, " : "", 488 b->b_host ); 489 490 #ifdef LDAP_PF_LOCAL 491 if ( b->b_proto == LDAP_PROTO_IPC ) { 492 struct sockaddr_un addr; 493 ber_socket_t s = socket( PF_LOCAL, SOCK_STREAM, 0 ); 494 int rc; 495 496 if ( s == AC_SOCKET_INVALID ) { 497 goto fail; 498 } 499 500 rc = ber_pvt_socket_set_nonblock( s, 1 ); 501 if ( rc ) { 502 evutil_closesocket( s ); 503 goto fail; 504 } 505 506 if ( strlen( b->b_host ) > ( sizeof(addr.sun_path) - 1 ) ) { 507 evutil_closesocket( s ); 508 goto fail; 509 } 510 memset( &addr, '\0', sizeof(addr) ); 511 addr.sun_family = AF_LOCAL; 512 strcpy( addr.sun_path, b->b_host ); 513 514 rc = connect( 515 s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un) ); 516 /* Asynchronous connect */ 517 if ( rc ) { 518 LloadPendingConnection *conn; 519 520 if ( errno != EINPROGRESS && errno != EWOULDBLOCK ) { 521 evutil_closesocket( s ); 522 goto fail; 523 } 524 525 conn = ch_calloc( 1, sizeof(LloadPendingConnection) ); 526 LDAP_LIST_ENTRY_INIT( conn, next ); 527 conn->backend = b; 528 conn->fd = s; 529 530 conn->event = event_new( lload_get_base( s ), s, 531 EV_WRITE|EV_PERSIST, upstream_connect_cb, conn ); 532 if ( !conn->event ) { 533 Debug( LDAP_DEBUG_ANY, "backend_connect: " 534 "failed to acquire an event to finish upstream " 535 "connection setup.\n" ); 536 ch_free( conn ); 537 evutil_closesocket( s ); 538 goto fail; 539 } 540 541 event_add( conn->event, lload_timeout_net ); 542 LDAP_LIST_INSERT_HEAD( &b->b_connecting, conn, next ); 543 Debug( LDAP_DEBUG_CONNS, "backend_connect: " 544 "connection to backend uri=%s in progress\n", 545 b->b_uri.bv_val ); 546 } else if ( upstream_init( s, b ) == NULL ) { 547 goto fail; 548 } 549 550 checked_unlock( &b->b_mutex ); 551 epoch_leave( epoch ); 552 return; 553 } 554 #endif /* LDAP_PF_LOCAL */ 555 556 hints.ai_family = AF_UNSPEC; 557 hints.ai_flags = EVUTIL_AI_CANONNAME; 558 hints.ai_socktype = SOCK_STREAM; 559 hints.ai_protocol = IPPROTO_TCP; 560 561 hostname = b->b_host; 562 563 /* 564 * Picking any value on the stack. This is unique to our thread without 565 * having to call ldap_pvt_thread_self. 566 * We might have to revert to using ldap_pvt_thread_self eventually since 567 * this betrays where exactly our stack lies - potentially weakening some 568 * protections like ASLR. 569 */ 570 placeholder = (struct evdns_getaddrinfo_request *)&request; 571 b->b_dns_req = placeholder; 572 checked_unlock( &b->b_mutex ); 573 574 request = evdns_getaddrinfo( 575 dnsbase, hostname, NULL, &hints, upstream_name_cb, b ); 576 577 checked_lock( &b->b_mutex ); 578 assert( request || b->b_dns_req != placeholder ); 579 580 /* Record the request, unless upstream_name_cb or another thread 581 * cleared it. Another thread is usually backend_reset or backend_connect 582 * if upstream_name_cb finished and scheduled another one */ 583 if ( b->b_dns_req == placeholder ) { 584 b->b_dns_req = request; 585 } 586 checked_unlock( &b->b_mutex ); 587 epoch_leave( epoch ); 588 return; 589 590 fail: 591 b->b_opening--; 592 b->b_failed++; 593 backend_retry( b ); 594 checked_unlock( &b->b_mutex ); 595 epoch_leave( epoch ); 596 } 597 598 void * 599 backend_connect_task( void *ctx, void *arg ) 600 { 601 backend_connect( -1, 0, arg ); 602 return NULL; 603 } 604 605 /* 606 * Needs exclusive access to the backend and no other thread is allowed to call 607 * backend_retry while we're handling this. 608 * 609 * If gentle == 0, a full pause must be in effect, else we risk deadlocking on 610 * event_free(). 611 */ 612 void 613 backend_reset( LloadBackend *b, int gentle ) 614 { 615 assert_locked( &b->b_mutex ); 616 if ( b->b_cookie ) { 617 if ( ldap_pvt_thread_pool_retract( b->b_cookie ) ) { 618 b->b_cookie = NULL; 619 b->b_opening--; 620 } else { 621 /* 622 * The task might not be cancelable because it just started 623 * executing. 624 * 625 * Shutdown should be the only time when the thread pool is 626 * in that state. Keep the cookie in to keep an eye on whether 627 * it's finished yet. 628 */ 629 assert( slapd_shutdown ); 630 } 631 } 632 /* Not safe to hold our mutex and call event_del/free if the event's 633 * callback is running, relinquish the mutex while we do so. */ 634 if ( b->b_retry_event && 635 event_pending( b->b_retry_event, EV_TIMEOUT, NULL ) ) { 636 assert( b->b_failed ); 637 checked_unlock( &b->b_mutex ); 638 event_del( b->b_retry_event ); 639 checked_lock( &b->b_mutex ); 640 b->b_opening--; 641 } 642 if ( b->b_dns_req ) { 643 evdns_getaddrinfo_cancel( b->b_dns_req ); 644 b->b_dns_req = NULL; 645 b->b_opening--; 646 } 647 while ( !LDAP_LIST_EMPTY( &b->b_connecting ) ) { 648 LloadPendingConnection *pending = LDAP_LIST_FIRST( &b->b_connecting ); 649 650 Debug( LDAP_DEBUG_CONNS, "backend_reset: " 651 "destroying socket pending connect() fd=%d\n", 652 pending->fd ); 653 654 event_active( pending->event, EV_WRITE, 0 ); 655 evutil_closesocket( pending->fd ); 656 pending->fd = -1; 657 LDAP_LIST_REMOVE( pending, next ); 658 659 if ( !gentle ) { 660 /* None of the event bases are running, we're safe to free the 661 * event right now and potentially free the backend itself */ 662 event_free( pending->event ); 663 ch_free( pending ); 664 } 665 /* else, just let the event dispose of the resources on its own later */ 666 b->b_opening--; 667 } 668 connections_walk( 669 &b->b_mutex, &b->b_preparing, lload_connection_close, &gentle ); 670 assert( LDAP_CIRCLEQ_EMPTY( &b->b_preparing ) ); 671 assert( b->b_opening == ( b->b_cookie ? 1 : 0 ) ); 672 b->b_failed = 0; 673 674 connections_walk_last( &b->b_mutex, &b->b_bindconns, b->b_last_bindconn, 675 lload_connection_close, &gentle ); 676 assert( gentle || b->b_bindavail == 0 ); 677 678 connections_walk_last( &b->b_mutex, &b->b_conns, b->b_last_conn, 679 lload_connection_close, &gentle ); 680 assert( gentle || b->b_active == 0 ); 681 assert_locked( &b->b_mutex ); 682 } 683 684 void 685 lload_backend_destroy( LloadBackend *b ) 686 { 687 LloadBackend *next = LDAP_CIRCLEQ_LOOP_NEXT( &backend, b, b_next ); 688 689 Debug( LDAP_DEBUG_CONNS, "lload_backend_destroy: " 690 "destroying backend uri='%s', numconns=%d, numbindconns=%d\n", 691 b->b_uri.bv_val, b->b_numconns, b->b_numbindconns ); 692 693 checked_lock( &b->b_mutex ); 694 b->b_numconns = b->b_numbindconns = 0; 695 backend_reset( b, 0 ); 696 697 LDAP_CIRCLEQ_REMOVE( &backend, b, b_next ); 698 if ( b == next ) { 699 current_backend = NULL; 700 } else { 701 current_backend = next; 702 } 703 704 #ifdef BALANCER_MODULE 705 if ( b->b_monitor ) { 706 BackendDB *be; 707 struct berval monitordn = BER_BVC("cn=monitor"); 708 int rc; 709 710 be = select_backend( &monitordn, 0 ); 711 712 /* FIXME: implement proper subsys shutdown in back-monitor or make 713 * backend just an entry, not a subsys */ 714 rc = b->b_monitor->mss_destroy( be, b->b_monitor ); 715 assert( rc == LDAP_SUCCESS ); 716 } 717 #endif /* BALANCER_MODULE */ 718 checked_unlock( &b->b_mutex ); 719 ldap_pvt_thread_mutex_destroy( &b->b_mutex ); 720 721 if ( b->b_retry_event ) { 722 event_del( b->b_retry_event ); 723 event_free( b->b_retry_event ); 724 b->b_retry_event = NULL; 725 } 726 727 ch_free( b->b_host ); 728 ch_free( b->b_uri.bv_val ); 729 ch_free( b->b_name.bv_val ); 730 ch_free( b ); 731 } 732 733 void 734 lload_backends_destroy( void ) 735 { 736 while ( !LDAP_CIRCLEQ_EMPTY( &backend ) ) { 737 LloadBackend *b = LDAP_CIRCLEQ_FIRST( &backend ); 738 739 lload_backend_destroy( b ); 740 } 741 } 742