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