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
upstream_connect_cb(evutil_socket_t s,short what,void * arg)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
upstream_name_cb(int result,struct evutil_addrinfo * res,void * arg)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 *
backend_select(LloadOperation * op,int * res)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
backend_retry(LloadBackend * b)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
backend_connect(evutil_socket_t s,short what,void * arg)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 *
backend_connect_task(void * ctx,void * arg)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
backend_reset(LloadBackend * b,int gentle)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
lload_backend_destroy(LloadBackend * b)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
lload_backends_destroy(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