backend.c 16.4 KB
Newer Older
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
 * Copyright 1998-2020 The OpenLDAP Foundation.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted only as authorized by the OpenLDAP
 * Public License.
 *
 * A copy of this license is available in the file LICENSE in the
 * top-level directory of the distribution or, alternatively, at
 * <http://www.OpenLDAP.org/license.html>.
 */

#include "portable.h"

#include <ac/socket.h>
#include <ac/errno.h>
#include <ac/string.h>
#include <ac/time.h>
#include <ac/unistd.h>

#include <event2/event.h>
#include <event2/dns.h>

#include "lutil.h"
28
#include "lload.h"
Ondřej Kuzník's avatar
Ondřej Kuzník committed
29

30
31
32
static void
upstream_connect_cb( evutil_socket_t s, short what, void *arg )
{
33
34
    LloadPendingConnection *conn = arg;
    LloadBackend *b = conn->backend;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
35
    int error = 0, rc = -1;
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

    ldap_pvt_thread_mutex_lock( &b->b_mutex );
    Debug( LDAP_DEBUG_CONNS, "upstream_connect_cb: "
            "fd=%d connection callback for backend uri='%s'\n",
            s, b->b_uri.bv_val );
    if ( what == EV_WRITE ) {
        socklen_t optlen = sizeof(error);

        if ( getsockopt( conn->fd, SOL_SOCKET, SO_ERROR, (void *)&error,
                     &optlen ) < 0 ) {
            goto done;
        }
        if ( error == EINTR || error == EINPROGRESS || error == EWOULDBLOCK ) {
            ldap_pvt_thread_mutex_unlock( &b->b_mutex );
            return;
        } else if ( error ) {
            goto done;
        } else if ( !upstream_init( s, conn->backend ) ) {
            goto done;
        }
        rc = LDAP_SUCCESS;
    }

done:
    if ( rc ) {
        evutil_closesocket( conn->fd );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
62
        b->b_opening--;
63
        b->b_failed++;
64
65
66
67
68
69
70
71
72
73
74
        if ( what & EV_TIMEOUT ) {
            Debug( LDAP_DEBUG_ANY, "upstream_connect_cb: "
                    "fd=%d connection timed out\n",
                    s );
        } else {
            char ebuf[128];
            Debug( LDAP_DEBUG_ANY, "upstream_connect_cb: "
                    "fd=%d connection set up failed%s%s\n",
                    s, error ? ": " : "",
                    error ? sock_errstr( error, ebuf, sizeof(ebuf) ) : "" );
        }
75
76
77
    } else {
        b->b_failed = 0;
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
78
    LDAP_LIST_REMOVE( conn, next );
79
80
81
82
83
    ldap_pvt_thread_mutex_unlock( &b->b_mutex );

    event_free( conn->event );
    ch_free( conn );

Ondřej Kuzník's avatar
Ondřej Kuzník committed
84
85
86
    if ( rc ) {
        backend_retry( b );
    }
87
88
}

Ondřej Kuzník's avatar
Ondřej Kuzník committed
89
90
91
static void
upstream_name_cb( int result, struct evutil_addrinfo *res, void *arg )
{
92
    LloadBackend *b = arg;
93
    ber_socket_t s = AC_SOCKET_INVALID;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
94
95
    int rc;

96
97
    ldap_pvt_thread_mutex_lock( &b->b_mutex );

98
    b->b_dns_req = NULL;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
99
100
101
    if ( result || !res ) {
        Debug( LDAP_DEBUG_ANY, "upstream_name_cb: "
                "name resolution failed for backend '%s': %s\n",
102
                b->b_uri.bv_val, evutil_gai_strerror( result ) );
103
        goto fail;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
104
105
    }

106
107
108
109
    /* TODO: if we get failures, try the other addrinfos */
    if ( (s = socket( res->ai_family, SOCK_STREAM, 0 )) ==
            AC_SOCKET_INVALID ) {
        goto fail;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
110
111
    }

112
113
    if ( ber_pvt_socket_set_nonblock( s, 1 ) ) {
        goto fail;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
114
115
116
117
118
119
120
121
122
123
124
    }

    if ( res->ai_family == PF_INET ) {
        struct sockaddr_in *ai = (struct sockaddr_in *)res->ai_addr;
        ai->sin_port = htons( b->b_port );
        rc = connect( s, (struct sockaddr *)ai, res->ai_addrlen );
    } else {
        struct sockaddr_in6 *ai = (struct sockaddr_in6 *)res->ai_addr;
        ai->sin6_port = htons( b->b_port );
        rc = connect( s, (struct sockaddr *)ai, res->ai_addrlen );
    }
125
126
    /* Asynchronous connect */
    if ( rc ) {
127
        LloadPendingConnection *conn;
128
129
130
131
132
133
134
135
136

        if ( errno != EINPROGRESS && errno != EWOULDBLOCK ) {
            Debug( LDAP_DEBUG_ANY, "upstream_name_cb: "
                    "failed to connect to server '%s'\n",
                    b->b_uri.bv_val );
            evutil_closesocket( s );
            goto fail;
        }

137
        conn = ch_calloc( 1, sizeof(LloadPendingConnection) );
138
139
140
141
        LDAP_LIST_ENTRY_INIT( conn, next );
        conn->backend = b;
        conn->fd = s;

142
        conn->event = event_new( lload_get_base( s ), s, EV_WRITE|EV_PERSIST,
143
144
145
146
147
148
149
150
151
152
                upstream_connect_cb, conn );
        if ( !conn->event ) {
            Debug( LDAP_DEBUG_ANY, "upstream_name_cb: "
                    "failed to acquire an event to finish upstream "
                    "connection setup.\n" );
            ch_free( conn );
            evutil_closesocket( s );
            goto fail;
        }

153
        event_add( conn->event, lload_timeout_net );
154
155
156
        LDAP_LIST_INSERT_HEAD( &b->b_connecting, conn, next );
        Debug( LDAP_DEBUG_CONNS, "upstream_name_cb: "
                "connection to backend uri=%s in progress\n",
157
                b->b_uri.bv_val );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
158
    } else if ( !upstream_init( s, b ) ) {
159
160
        goto fail;
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
161

Ondřej Kuzník's avatar
Ondřej Kuzník committed
162
    ldap_pvt_thread_mutex_unlock( &b->b_mutex );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
163
    evutil_freeaddrinfo( res );
164
165
166
167
168
169
170
    return;

fail:
    if ( s != AC_SOCKET_INVALID ) {
        evutil_closesocket( s );
    }
    b->b_opening--;
171
    b->b_failed++;
172
    ldap_pvt_thread_mutex_unlock( &b->b_mutex );
173
174
175
    if ( result != EVUTIL_EAI_CANCEL ) {
        backend_retry( b );
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
176
177
178
    if ( res ) {
        evutil_freeaddrinfo( res );
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
179
180
}

181
LloadConnection *
182
backend_select( LloadOperation *op, int *res )
Ondřej Kuzník's avatar
Ondřej Kuzník committed
183
{
184
    LloadBackend *b, *first, *next;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
185
186
187
188
189

    ldap_pvt_thread_mutex_lock( &backend_mutex );
    first = b = current_backend;
    ldap_pvt_thread_mutex_unlock( &backend_mutex );

190
191
    *res = LDAP_UNAVAILABLE;

Ondřej Kuzník's avatar
Ondřej Kuzník committed
192
193
194
    if ( !first ) {
        return NULL;
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
195

196
197
    /* TODO: Two runs, one with trylock, then one actually locked if we don't
     * find anything? */
Ondřej Kuzník's avatar
Ondřej Kuzník committed
198
    do {
199
200
        lload_c_head *head;
        LloadConnection *c;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
201

Ondřej Kuzník's avatar
Ondřej Kuzník committed
202
        ldap_pvt_thread_mutex_lock( &b->b_mutex );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
203
        next = LDAP_CIRCLEQ_LOOP_NEXT( &backend, b, b_next );
204
205
206
207

        if ( b->b_max_pending && b->b_n_ops_executing >= b->b_max_pending ) {
            Debug( LDAP_DEBUG_CONNS, "backend_select: "
                    "backend %s too busy\n",
208
                    b->b_uri.bv_val );
209
            ldap_pvt_thread_mutex_unlock( &b->b_mutex );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
210
            b = next;
211
            *res = LDAP_BUSY;
212
213
214
            continue;
        }

215
216
217
218
219
        if ( op->o_tag == LDAP_REQ_BIND
#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
                && !(lload_features & LLOAD_FEATURE_VC)
#endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
        ) {
220
221
222
223
            head = &b->b_bindconns;
        } else {
            head = &b->b_conns;
        }
224
225
226
        if ( !LDAP_CIRCLEQ_EMPTY( head ) ) {
            *res = LDAP_BUSY;
        }
227

228
        LDAP_CIRCLEQ_FOREACH ( c, head, c_next ) {
229
            ldap_pvt_thread_mutex_lock( &c->c_io_mutex );
230
            CONNECTION_LOCK(c);
231
            if ( c->c_state == LLOAD_C_READY && !c->c_pendingber &&
232
233
                    ( b->b_max_conn_pending == 0 ||
                            c->c_n_ops_executing < b->b_max_conn_pending ) ) {
234
                Debug( LDAP_DEBUG_CONNS, "backend_select: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
235
236
                        "selected connection connid=%lu for client "
                        "connid=%lu msgid=%d\n",
237
238
                        c->c_connid, op->o_client_connid, op->o_client_msgid );

239
240
                /*
                 * Round-robin step:
Ondřej Kuzník's avatar
Ondřej Kuzník committed
241
242
                 * Rotate the queue to put this connection at the end, same for
                 * the backend.
243
244
245
                 */
                LDAP_CIRCLEQ_MAKE_TAIL( head, c, c_next );

Ondřej Kuzník's avatar
Ondřej Kuzník committed
246
247
248
249
                ldap_pvt_thread_mutex_lock( &backend_mutex );
                current_backend = next;
                ldap_pvt_thread_mutex_unlock( &backend_mutex );

250
                b->b_n_ops_executing++;
251
252
253
254
255
                if ( op->o_tag == LDAP_REQ_BIND ) {
                    b->b_counters[LLOAD_STATS_OPS_BIND].lc_ops_received++;
                } else {
                    b->b_counters[LLOAD_STATS_OPS_OTHER].lc_ops_received++;
                }
256
                c->c_n_ops_executing++;
257
                c->c_counters.lc_ops_received++;
258
                CONNECTION_UNLOCK_INCREF(c);
259

260
                ldap_pvt_thread_mutex_unlock( &b->b_mutex );
261
                *res = LDAP_SUCCESS;
262
263
                return c;
            }
264
            CONNECTION_UNLOCK(c);
265
            ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
266
        }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
267
        ldap_pvt_thread_mutex_unlock( &b->b_mutex );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
268
269
270

        b = next;
    } while ( b != first );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
271
272
273
274

    return NULL;
}

275
void
276
backend_retry( LloadBackend *b )
277
278
279
{
    int rc, requested;

Ondřej Kuzník's avatar
Ondřej Kuzník committed
280
281
282
283
284
285
    if ( slapd_shutdown ) {
        Debug( LDAP_DEBUG_CONNS, "backend_retry: "
                "shutting down\n" );
        return;
    }

286
287
288
    ldap_pvt_thread_mutex_lock( &b->b_mutex );

    requested = b->b_numconns;
289
290
291
292
#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
    if ( !(lload_features & LLOAD_FEATURE_VC) )
#endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
    {
293
294
295
        requested += b->b_numbindconns;
    }
    if ( b->b_active + b->b_bindavail + b->b_opening < requested ) {
296
        if ( b->b_opening > 0 || b->b_failed > 0 ) {
297
298
            if ( b->b_failed > 0 &&
                    !event_pending( b->b_retry_event, EV_TIMEOUT, NULL ) ) {
299
300
301
302
303
304
305
306
307
                Debug( LDAP_DEBUG_CONNS, "backend_retry: "
                        "scheduling a retry in %d ms\n",
                        b->b_retry_timeout );
                b->b_opening++;
                event_add( b->b_retry_event, &b->b_retry_tv );
                ldap_pvt_thread_mutex_unlock( &b->b_mutex );
                return;
            } else {
                Debug( LDAP_DEBUG_CONNS, "backend_retry: "
308
                        "retry in progress already\n" );
309
310
311
312
313
314
315
316
317
318
319
320
            }
        } else {
            Debug( LDAP_DEBUG_CONNS, "backend_retry: "
                    "scheduling re-connection straight away\n" );
            b->b_opening++;
            rc = ldap_pvt_thread_pool_submit(
                    &connection_pool, backend_connect_task, b );
            if ( rc ) {
                ldap_pvt_thread_mutex_unlock( &b->b_mutex );
                backend_connect( -1, 0, b );
                return;
            }
321
        }
322
323
324
    } else {
        Debug( LDAP_DEBUG_CONNS, "backend_retry: "
                "no more connections needed for this backend\n" );
325
326
327
328
    }
    ldap_pvt_thread_mutex_unlock( &b->b_mutex );
}

329
330
void
backend_connect( evutil_socket_t s, short what, void *arg )
Ondřej Kuzník's avatar
Ondřej Kuzník committed
331
332
{
    struct evutil_addrinfo hints = {};
333
    LloadBackend *b = arg;
334
    char *hostname;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
335

Ondřej Kuzník's avatar
Ondřej Kuzník committed
336
337
338
339
340
341
    if ( slapd_shutdown ) {
        Debug( LDAP_DEBUG_CONNS, "backend_connect: "
                "doing nothing, shutdown in progress\n" );
        return;
    }

342
    ldap_pvt_thread_mutex_lock( &b->b_mutex );
343
    Debug( LDAP_DEBUG_CONNS, "backend_connect: "
344
345
            "%sattempting connection to %s\n",
            (what & EV_TIMEOUT) ? "retry timeout finished, " : "",
346
347
            b->b_host );

Ondřej Kuzník's avatar
Ondřej Kuzník committed
348
349
350
351
352
353
354
#ifdef LDAP_PF_LOCAL
    if ( b->b_proto == LDAP_PROTO_IPC ) {
        struct sockaddr_un addr;
        ber_socket_t s = socket( PF_LOCAL, SOCK_STREAM, 0 );
        int rc;

        if ( s == AC_SOCKET_INVALID ) {
355
            goto fail;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
356
357
358
359
360
        }

        rc = ber_pvt_socket_set_nonblock( s, 1 );
        if ( rc ) {
            evutil_closesocket( s );
361
            goto fail;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
362
363
364
365
        }

        if ( strlen( b->b_host ) > ( sizeof(addr.sun_path) - 1 ) ) {
            evutil_closesocket( s );
366
            goto fail;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
367
368
369
370
371
372
373
        }
        memset( &addr, '\0', sizeof(addr) );
        addr.sun_family = AF_LOCAL;
        strcpy( addr.sun_path, b->b_host );

        rc = connect(
                s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un) );
374
375
        /* Asynchronous connect */
        if ( rc ) {
376
            LloadPendingConnection *conn;
377
378
379
380
381
382

            if ( errno != EINPROGRESS && errno != EWOULDBLOCK ) {
                evutil_closesocket( s );
                goto fail;
            }

383
            conn = ch_calloc( 1, sizeof(LloadPendingConnection) );
384
385
386
387
            LDAP_LIST_ENTRY_INIT( conn, next );
            conn->backend = b;
            conn->fd = s;

388
            conn->event = event_new( lload_get_base( s ), s,
389
390
391
392
393
394
395
396
397
398
                    EV_WRITE|EV_PERSIST, upstream_connect_cb, conn );
            if ( !conn->event ) {
                Debug( LDAP_DEBUG_ANY, "backend_connect: "
                        "failed to acquire an event to finish upstream "
                        "connection setup.\n" );
                ch_free( conn );
                evutil_closesocket( s );
                goto fail;
            }

399
            event_add( conn->event, lload_timeout_net );
400
401
402
403
404
            LDAP_LIST_INSERT_HEAD( &b->b_connecting, conn, next );
            Debug( LDAP_DEBUG_CONNS, "backend_connect: "
                    "connection to backend uri=%s in progress\n",
                    b->b_uri.bv_val );
        } else if ( !upstream_init( s, b ) ) {
405
406
            goto fail;
        }
407

408
        ldap_pvt_thread_mutex_unlock( &b->b_mutex );
409
        return;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
410
411
412
413
414
415
416
417
    }
#endif /* LDAP_PF_LOCAL */

    hints.ai_family = AF_UNSPEC;
    hints.ai_flags = EVUTIL_AI_CANONNAME;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

418
419
420
    hostname = b->b_host;
    ldap_pvt_thread_mutex_unlock( &b->b_mutex );

421
422
423
    assert( b->b_dns_req == NULL );
    b->b_dns_req = evdns_getaddrinfo(
            dnsbase, hostname, NULL, &hints, upstream_name_cb, b );
424
    return;
425
426
427

fail:
    b->b_opening--;
428
    b->b_failed++;
429
430
    ldap_pvt_thread_mutex_unlock( &b->b_mutex );
    backend_retry( b );
431
432
433
434
435
436
437
}

void *
backend_connect_task( void *ctx, void *arg )
{
    backend_connect( -1, 0, arg );
    return NULL;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
438
}
Ondřej Kuzník's avatar
Ondřej Kuzník committed
439
440

void
441
backend_reset( LloadBackend *b )
Ondřej Kuzník's avatar
Ondřej Kuzník committed
442
{
443
444
    while ( !LDAP_LIST_EMPTY( &b->b_connecting ) ) {
        LloadPendingConnection *pending = LDAP_LIST_FIRST( &b->b_connecting );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
445

446
447
448
        Debug( LDAP_DEBUG_CONNS, "backend_reset: "
                "destroying socket pending connect() fd=%d\n",
                pending->fd );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
449

450
451
452
453
454
455
456
        event_free( pending->event );
        evutil_closesocket( pending->fd );
        LDAP_LIST_REMOVE( pending, next );
        ch_free( pending );
    }
    while ( !LDAP_CIRCLEQ_EMPTY( &b->b_preparing ) ) {
        LloadConnection *c = LDAP_CIRCLEQ_FIRST( &b->b_preparing );
457

458
459
460
461
        CONNECTION_LOCK(c);
        Debug( LDAP_DEBUG_CONNS, "backend_reset: "
                "destroying connection being set up connid=%lu\n",
                c->c_connid );
462

463
464
465
466
467
468
        assert( c->c_live );
        CONNECTION_DESTROY(c);
        assert( !c );
    }
    while ( !LDAP_CIRCLEQ_EMPTY( &b->b_bindconns ) ) {
        LloadConnection *c = LDAP_CIRCLEQ_FIRST( &b->b_bindconns );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
469

470
471
472
473
        CONNECTION_LOCK(c);
        Debug( LDAP_DEBUG_CONNS, "backend_reset: "
                "destroying bind connection connid=%lu, pending ops=%ld\n",
                c->c_connid, c->c_n_ops_executing );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
474

475
476
477
478
479
480
        assert( c->c_live );
        CONNECTION_DESTROY(c);
        assert( !c );
    }
    while ( !LDAP_CIRCLEQ_EMPTY( &b->b_conns ) ) {
        LloadConnection *c = LDAP_CIRCLEQ_FIRST( &b->b_conns );
481

482
483
484
485
        CONNECTION_LOCK(c);
        Debug( LDAP_DEBUG_CONNS, "backend_reset: "
                "destroying regular connection connid=%lu, pending ops=%ld\n",
                c->c_connid, c->c_n_ops_executing );
486

487
488
489
490
        assert( c->c_live );
        CONNECTION_DESTROY(c);
        assert( !c );
    }
491
492
493
494
495
    if ( b->b_dns_req ) {
        evdns_getaddrinfo_cancel( b->b_dns_req );
        b->b_dns_req = NULL;
        b->b_opening--;
    }
496
}
497

498
499
500
501
void
lload_backend_destroy( LloadBackend *b )
{
    LloadBackend *next = LDAP_CIRCLEQ_LOOP_NEXT( &backend, b, b_next );
502

503
504
505
506
507
508
509
510
511
512
513
514
515
    Debug( LDAP_DEBUG_CONNS, "lload_backend_destroy: "
            "destroying backend uri='%s', numconns=%d, numbindconns=%d\n",
            b->b_uri.bv_val, b->b_numconns, b->b_numbindconns );

    backend_reset( b );

    LDAP_CIRCLEQ_REMOVE( &backend, b, b_next );
    if ( b == next ) {
        current_backend = NULL;
    } else {
        current_backend = next;
    }

516
517
518
519
520
521
522
523
524
525
526
527
528
529
#ifdef BALANCER_MODULE
    if ( b->b_monitor ) {
        BackendDB *be;
        struct berval monitordn = BER_BVC("cn=monitor");
        int rc;

        be = select_backend( &monitordn, 0 );

        /* FIXME: implement proper subsys shutdown in back-monitor or make
         * backend just an entry, not a subsys */
        rc = b->b_monitor->mss_destroy( be, b->b_monitor );
        assert( rc == LDAP_SUCCESS );
    }
#endif /* BALANCER_MODULE */
530
    ldap_pvt_thread_mutex_destroy( &b->b_mutex );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
531

532
533
    event_del( b->b_retry_event );
    event_free( b->b_retry_event );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
534

535
536
    ch_free( b->b_host );
    ch_free( b->b_uri.bv_val );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
537
    ch_free( b->b_name.bv_val );
538
539
540
541
542
543
544
545
    ch_free( b );
}

void
lload_backends_destroy( void )
{
    while ( !LDAP_CIRCLEQ_EMPTY( &backend ) ) {
        LloadBackend *b = LDAP_CIRCLEQ_FIRST( &backend );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
546

547
        lload_backend_destroy( b );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
548
549
    }
}