xref: /netbsd-src/external/bsd/openldap/dist/servers/lloadd/backend.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
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