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