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 int
try_upstream(LloadBackend * b,lload_c_head * head,LloadOperation * op,LloadConnection * c,int * res,char ** message)288 try_upstream(
289 LloadBackend *b,
290 lload_c_head *head,
291 LloadOperation *op,
292 LloadConnection *c,
293 int *res,
294 char **message )
295 {
296 assert_locked( &b->b_mutex );
297
298 checked_lock( &c->c_io_mutex );
299 CONNECTION_LOCK(c);
300 if ( c->c_state == LLOAD_C_READY && !c->c_pendingber &&
301 ( b->b_max_conn_pending == 0 ||
302 c->c_n_ops_executing < b->b_max_conn_pending ) ) {
303 Debug( LDAP_DEBUG_CONNS, "try_upstream: "
304 "selected connection connid=%lu for client "
305 "connid=%lu msgid=%d\n",
306 c->c_connid, op->o_client_connid, op->o_client_msgid );
307
308 /* c_state is DYING if we're about to be unlinked */
309 assert( IS_ALIVE( c, c_live ) );
310
311 if ( head ) {
312 /*
313 * Round-robin step:
314 * Rotate the queue to put this connection at the end.
315 */
316 LDAP_CIRCLEQ_MAKE_TAIL( head, c, c_next );
317 }
318
319 b->b_n_ops_executing++;
320 if ( op->o_tag == LDAP_REQ_BIND ) {
321 b->b_counters[LLOAD_STATS_OPS_BIND].lc_ops_received++;
322 } else {
323 b->b_counters[LLOAD_STATS_OPS_OTHER].lc_ops_received++;
324 }
325 c->c_n_ops_executing++;
326 c->c_counters.lc_ops_received++;
327
328 *res = LDAP_SUCCESS;
329 CONNECTION_ASSERT_LOCKED(c);
330 assert_locked( &c->c_io_mutex );
331 return 1;
332 }
333 CONNECTION_UNLOCK(c);
334 checked_unlock( &c->c_io_mutex );
335 return 0;
336 }
337
338 int
backend_select(LloadBackend * b,LloadOperation * op,LloadConnection ** cp,int * res,char ** message)339 backend_select(
340 LloadBackend *b,
341 LloadOperation *op,
342 LloadConnection **cp,
343 int *res,
344 char **message )
345 {
346 lload_c_head *head;
347 LloadConnection *c;
348
349 assert_locked( &b->b_mutex );
350 if ( b->b_max_pending && b->b_n_ops_executing >= b->b_max_pending ) {
351 Debug( LDAP_DEBUG_CONNS, "backend_select: "
352 "backend %s too busy\n",
353 b->b_uri.bv_val );
354 *res = LDAP_BUSY;
355 *message = "server busy";
356 return 1;
357 }
358
359 if ( op->o_tag == LDAP_REQ_BIND
360 #ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
361 && !(lload_features & LLOAD_FEATURE_VC)
362 #endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
363 ) {
364 head = &b->b_bindconns;
365 } else {
366 head = &b->b_conns;
367 }
368
369 if ( LDAP_CIRCLEQ_EMPTY( head ) ) {
370 return 0;
371 }
372
373 *res = LDAP_BUSY;
374 *message = "server busy";
375
376 LDAP_CIRCLEQ_FOREACH( c, head, c_next ) {
377 if ( try_upstream( b, head, op, c, res, message ) ) {
378 *cp = c;
379 CONNECTION_ASSERT_LOCKED(c);
380 assert_locked( &c->c_io_mutex );
381 return 1;
382 }
383 }
384
385 return 1;
386 }
387
388 int
upstream_select(LloadOperation * op,LloadConnection ** cp,int * res,char ** message)389 upstream_select(
390 LloadOperation *op,
391 LloadConnection **cp,
392 int *res,
393 char **message )
394 {
395 LloadTier *tier;
396 int finished = 0;
397
398 LDAP_STAILQ_FOREACH( tier, &tiers, t_next ) {
399 if ( (finished = tier->t_type.tier_select(
400 tier, op, cp, res, message )) ) {
401 break;
402 }
403 }
404
405 return finished;
406 }
407
408 /*
409 * Will schedule a connection attempt if there is a need for it. Need exclusive
410 * access to backend, its b_mutex is not touched here, though.
411 */
412 void
backend_retry(LloadBackend * b)413 backend_retry( LloadBackend *b )
414 {
415 int requested;
416
417 if ( slapd_shutdown ) {
418 Debug( LDAP_DEBUG_CONNS, "backend_retry: "
419 "shutting down\n" );
420 return;
421 }
422 assert_locked( &b->b_mutex );
423
424 requested = b->b_numconns;
425 #ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
426 if ( !(lload_features & LLOAD_FEATURE_VC) )
427 #endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
428 {
429 requested += b->b_numbindconns;
430 }
431
432 if ( b->b_active + b->b_bindavail + b->b_opening >= requested ) {
433 Debug( LDAP_DEBUG_CONNS, "backend_retry: "
434 "no more connections needed for this backend\n" );
435 assert_locked( &b->b_mutex );
436 return;
437 }
438
439 if ( b->b_opening > 0 ) {
440 Debug( LDAP_DEBUG_CONNS, "backend_retry: "
441 "retry in progress already\n" );
442 assert( b->b_opening == 1 );
443 assert_locked( &b->b_mutex );
444 return;
445 }
446
447 /* We incremented b_opening when we activated the event, so it can't be
448 * pending */
449 assert( !event_pending( b->b_retry_event, EV_TIMEOUT, NULL ) );
450 b->b_opening++;
451
452 if ( b->b_failed > 0 ) {
453 Debug( LDAP_DEBUG_CONNS, "backend_retry: "
454 "scheduling a retry in %d ms\n",
455 b->b_retry_timeout );
456 event_add( b->b_retry_event, &b->b_retry_tv );
457 assert_locked( &b->b_mutex );
458 return;
459 }
460
461 Debug( LDAP_DEBUG_CONNS, "backend_retry: "
462 "scheduling re-connection straight away\n" );
463
464 if ( ldap_pvt_thread_pool_submit2(
465 &connection_pool, backend_connect_task, b, &b->b_cookie ) ) {
466 Debug( LDAP_DEBUG_ANY, "backend_retry: "
467 "failed to submit retry task, scheduling a retry instead\n" );
468 /* The current implementation of ldap_pvt_thread_pool_submit2 can fail
469 * and still set (an invalid) cookie */
470 b->b_cookie = NULL;
471 b->b_failed++;
472 event_add( b->b_retry_event, &b->b_retry_tv );
473 }
474 assert_locked( &b->b_mutex );
475 }
476
477 void
backend_connect(evutil_socket_t s,short what,void * arg)478 backend_connect( evutil_socket_t s, short what, void *arg )
479 {
480 struct evutil_addrinfo hints = {};
481 LloadBackend *b = arg;
482 struct evdns_getaddrinfo_request *request, *placeholder;
483 char *hostname;
484 epoch_t epoch;
485
486 checked_lock( &b->b_mutex );
487 assert( b->b_dns_req == NULL );
488
489 if ( b->b_cookie ) {
490 b->b_cookie = NULL;
491 }
492
493 if ( slapd_shutdown ) {
494 Debug( LDAP_DEBUG_CONNS, "backend_connect: "
495 "doing nothing, shutdown in progress\n" );
496 b->b_opening--;
497 checked_unlock( &b->b_mutex );
498 return;
499 }
500
501 epoch = epoch_join();
502
503 Debug( LDAP_DEBUG_CONNS, "backend_connect: "
504 "%sattempting connection to %s\n",
505 (what & EV_TIMEOUT) ? "retry timeout finished, " : "",
506 b->b_host );
507
508 #ifdef LDAP_PF_LOCAL
509 if ( b->b_proto == LDAP_PROTO_IPC ) {
510 struct sockaddr_un addr;
511 ber_socket_t s = socket( PF_LOCAL, SOCK_STREAM, 0 );
512 int rc;
513
514 if ( s == AC_SOCKET_INVALID ) {
515 goto fail;
516 }
517
518 rc = ber_pvt_socket_set_nonblock( s, 1 );
519 if ( rc ) {
520 evutil_closesocket( s );
521 goto fail;
522 }
523
524 if ( strlen( b->b_host ) > ( sizeof(addr.sun_path) - 1 ) ) {
525 evutil_closesocket( s );
526 goto fail;
527 }
528 memset( &addr, '\0', sizeof(addr) );
529 addr.sun_family = AF_LOCAL;
530 strcpy( addr.sun_path, b->b_host );
531
532 rc = connect(
533 s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un) );
534 /* Asynchronous connect */
535 if ( rc ) {
536 LloadPendingConnection *conn;
537
538 if ( errno != EINPROGRESS && errno != EWOULDBLOCK ) {
539 evutil_closesocket( s );
540 goto fail;
541 }
542
543 conn = ch_calloc( 1, sizeof(LloadPendingConnection) );
544 LDAP_LIST_ENTRY_INIT( conn, next );
545 conn->backend = b;
546 conn->fd = s;
547
548 conn->event = event_new( lload_get_base( s ), s,
549 EV_WRITE|EV_PERSIST, upstream_connect_cb, conn );
550 if ( !conn->event ) {
551 Debug( LDAP_DEBUG_ANY, "backend_connect: "
552 "failed to acquire an event to finish upstream "
553 "connection setup.\n" );
554 ch_free( conn );
555 evutil_closesocket( s );
556 goto fail;
557 }
558
559 event_add( conn->event, lload_timeout_net );
560 LDAP_LIST_INSERT_HEAD( &b->b_connecting, conn, next );
561 Debug( LDAP_DEBUG_CONNS, "backend_connect: "
562 "connection to backend uri=%s in progress\n",
563 b->b_uri.bv_val );
564 } else if ( upstream_init( s, b ) == NULL ) {
565 goto fail;
566 }
567
568 checked_unlock( &b->b_mutex );
569 epoch_leave( epoch );
570 return;
571 }
572 #endif /* LDAP_PF_LOCAL */
573
574 hints.ai_family = AF_UNSPEC;
575 hints.ai_flags = EVUTIL_AI_CANONNAME;
576 hints.ai_socktype = SOCK_STREAM;
577 hints.ai_protocol = IPPROTO_TCP;
578
579 hostname = b->b_host;
580
581 /*
582 * Picking any value on the stack. This is unique to our thread without
583 * having to call ldap_pvt_thread_self.
584 * We might have to revert to using ldap_pvt_thread_self eventually since
585 * this betrays where exactly our stack lies - potentially weakening some
586 * protections like ASLR.
587 */
588 placeholder = (struct evdns_getaddrinfo_request *)&request;
589 b->b_dns_req = placeholder;
590 checked_unlock( &b->b_mutex );
591
592 request = evdns_getaddrinfo(
593 dnsbase, hostname, NULL, &hints, upstream_name_cb, b );
594
595 checked_lock( &b->b_mutex );
596 assert( request || b->b_dns_req != placeholder );
597
598 /* Record the request, unless upstream_name_cb or another thread
599 * cleared it. Another thread is usually backend_reset or backend_connect
600 * if upstream_name_cb finished and scheduled another one */
601 if ( b->b_dns_req == placeholder ) {
602 b->b_dns_req = request;
603 }
604 checked_unlock( &b->b_mutex );
605 epoch_leave( epoch );
606 return;
607
608 fail:
609 b->b_opening--;
610 b->b_failed++;
611 backend_retry( b );
612 checked_unlock( &b->b_mutex );
613 epoch_leave( epoch );
614 }
615
616 void *
backend_connect_task(void * ctx,void * arg)617 backend_connect_task( void *ctx, void *arg )
618 {
619 backend_connect( -1, 0, arg );
620 return NULL;
621 }
622
623 /*
624 * Needs exclusive access to the backend and no other thread is allowed to call
625 * backend_retry while we're handling this.
626 *
627 * If gentle == 0, a full pause must be in effect, else we risk deadlocking on
628 * event_free().
629 */
630 void
backend_reset(LloadBackend * b,int gentle)631 backend_reset( LloadBackend *b, int gentle )
632 {
633 assert_locked( &b->b_mutex );
634 if ( b->b_cookie ) {
635 if ( ldap_pvt_thread_pool_retract( b->b_cookie ) ) {
636 b->b_cookie = NULL;
637 b->b_opening--;
638 } else {
639 /*
640 * The task might not be cancelable because it just started
641 * executing.
642 *
643 * Shutdown should be the only time when the thread pool is
644 * in that state. Keep the cookie in to keep an eye on whether
645 * it's finished yet.
646 */
647 assert( slapd_shutdown );
648 }
649 }
650 /* Not safe to hold our mutex and call event_del/free if the event's
651 * callback is running, relinquish the mutex while we do so. */
652 if ( b->b_retry_event &&
653 event_pending( b->b_retry_event, EV_TIMEOUT, NULL ) ) {
654 assert( b->b_failed );
655 checked_unlock( &b->b_mutex );
656 event_del( b->b_retry_event );
657 checked_lock( &b->b_mutex );
658 b->b_opening--;
659 }
660 if ( b->b_dns_req ) {
661 evdns_getaddrinfo_cancel( b->b_dns_req );
662 b->b_dns_req = NULL;
663 b->b_opening--;
664 }
665 while ( !LDAP_LIST_EMPTY( &b->b_connecting ) ) {
666 LloadPendingConnection *pending = LDAP_LIST_FIRST( &b->b_connecting );
667
668 Debug( LDAP_DEBUG_CONNS, "backend_reset: "
669 "destroying socket pending connect() fd=%d\n",
670 pending->fd );
671
672 event_active( pending->event, EV_WRITE, 0 );
673 evutil_closesocket( pending->fd );
674 pending->fd = -1;
675 LDAP_LIST_REMOVE( pending, next );
676
677 if ( !gentle ) {
678 /* None of the event bases are running, we're safe to free the
679 * event right now and potentially free the backend itself */
680 event_free( pending->event );
681 ch_free( pending );
682 }
683 /* else, just let the event dispose of the resources on its own later */
684 b->b_opening--;
685 }
686 connections_walk(
687 &b->b_mutex, &b->b_preparing, lload_connection_close, &gentle );
688 assert( LDAP_CIRCLEQ_EMPTY( &b->b_preparing ) );
689 assert( b->b_opening == ( b->b_cookie ? 1 : 0 ) );
690 b->b_failed = 0;
691
692 connections_walk_last( &b->b_mutex, &b->b_bindconns, b->b_last_bindconn,
693 lload_connection_close, &gentle );
694 assert( gentle || b->b_bindavail == 0 );
695
696 connections_walk_last( &b->b_mutex, &b->b_conns, b->b_last_conn,
697 lload_connection_close, &gentle );
698 assert( gentle || b->b_active == 0 );
699 assert_locked( &b->b_mutex );
700 }
701
702 LloadBackend *
lload_backend_new(void)703 lload_backend_new( void )
704 {
705 LloadBackend *b;
706
707 b = ch_calloc( 1, sizeof(LloadBackend) );
708
709 LDAP_CIRCLEQ_INIT( &b->b_conns );
710 LDAP_CIRCLEQ_INIT( &b->b_bindconns );
711 LDAP_CIRCLEQ_INIT( &b->b_preparing );
712 LDAP_CIRCLEQ_ENTRY_INIT( b, b_next );
713
714 b->b_numconns = 1;
715 b->b_numbindconns = 1;
716 b->b_weight = 1;
717
718 b->b_retry_timeout = 5000;
719
720 ldap_pvt_thread_mutex_init( &b->b_mutex );
721
722 return b;
723 }
724
725 void
lload_backend_destroy(LloadBackend * b)726 lload_backend_destroy( LloadBackend *b )
727 {
728 Debug( LDAP_DEBUG_CONNS, "lload_backend_destroy: "
729 "destroying backend uri='%s', numconns=%d, numbindconns=%d\n",
730 b->b_uri.bv_val, b->b_numconns, b->b_numbindconns );
731
732 checked_lock( &b->b_mutex );
733 b->b_tier->t_type.tier_remove_backend( b->b_tier, b );
734 b->b_numconns = b->b_numbindconns = 0;
735 backend_reset( b, 0 );
736
737 #ifdef BALANCER_MODULE
738 if ( b->b_monitor ) {
739 BackendDB *be;
740 struct berval monitordn = BER_BVC("cn=monitor");
741 int rc;
742
743 be = select_backend( &monitordn, 0 );
744
745 /* FIXME: implement proper subsys shutdown in back-monitor or make
746 * backend just an entry, not a subsys */
747 rc = b->b_monitor->mss_destroy( be, b->b_monitor );
748 assert( rc == LDAP_SUCCESS );
749 }
750 #endif /* BALANCER_MODULE */
751
752 checked_unlock( &b->b_mutex );
753 ldap_pvt_thread_mutex_destroy( &b->b_mutex );
754
755 if ( b->b_retry_event ) {
756 event_del( b->b_retry_event );
757 event_free( b->b_retry_event );
758 b->b_retry_event = NULL;
759 }
760
761 ch_free( b->b_host );
762 ch_free( b->b_uri.bv_val );
763 ch_free( b->b_name.bv_val );
764 ch_free( b );
765 }
766