daemon.c 57 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
28
29
30
31
32
33
34
35
36
37
38
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
 * Copyright 1998-2015 The OpenLDAP Foundation.
 * Portions Copyright 2007 by Howard Chu, Symas Corporation.
 * 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>.
 */
/* Portions Copyright (c) 1995 Regents of the University of Michigan.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that this notice is preserved and that due credit is given
 * to the University of Michigan at Ann Arbor. The name of the University
 * may not be used to endorse or promote products derived from this
 * software without specific prior written permission. This software
 * is provided ``as is'' without express or implied warranty.
 */

#include "portable.h"

#include <stdio.h>

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

#include <event2/event.h>
Ondřej Kuzník's avatar
Ondřej Kuzník committed
39
#include <event2/dns.h>
Ondřej Kuzník's avatar
Ondřej Kuzník committed
40
41
#include <event2/listener.h>

42
#include "lload.h"
Ondřej Kuzník's avatar
Ondřej Kuzník committed
43
44
45
46
47
48
49
50
51
52
53
#include "ldap_pvt_thread.h"
#include "lutil.h"

#include "ldap_rq.h"

#ifdef LDAP_PF_LOCAL
#include <sys/stat.h>
/* this should go in <ldap.h> as soon as it is accepted */
#define LDAPI_MOD_URLEXT "x-mod"
#endif /* LDAP_PF_LOCAL */

54
#ifndef BALANCER_MODULE
Ondřej Kuzník's avatar
Ondřej Kuzník committed
55
56
57
58
59
60
61
62
63
64
#ifdef LDAP_PF_INET6
int slap_inet4or6 = AF_UNSPEC;
#else /* ! INETv6 */
int slap_inet4or6 = AF_INET;
#endif /* ! INETv6 */

/* globals */
time_t starttime;
struct runqueue_s slapd_rq;

65
66
67
68
69
70
71
72
73
74
75
76
#ifdef LDAP_TCP_BUFFER
int slapd_tcp_rmem;
int slapd_tcp_wmem;
#endif /* LDAP_TCP_BUFFER */

volatile sig_atomic_t slapd_shutdown = 0;
volatile sig_atomic_t slapd_gentle_shutdown = 0;
volatile sig_atomic_t slapd_abrupt_shutdown = 0;
#endif /* !BALANCER_MODULE */

static int emfile;

Ondřej Kuzník's avatar
Ondřej Kuzník committed
77
78
79
80
ldap_pvt_thread_mutex_t lload_wait_mutex;
ldap_pvt_thread_cond_t lload_wait_cond;
ldap_pvt_thread_cond_t lload_pause_cond;

Ondřej Kuzník's avatar
Ondřej Kuzník committed
81
82
83
#ifndef SLAPD_MAX_DAEMON_THREADS
#define SLAPD_MAX_DAEMON_THREADS 16
#endif
84
85
int lload_daemon_threads = 1;
int lload_daemon_mask;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
86
87

struct event_base *listener_base = NULL;
88
LloadListener **lload_listeners = NULL;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
89
90
static ldap_pvt_thread_t listener_tid, *daemon_tid;

Ondřej Kuzník's avatar
Ondřej Kuzník committed
91
struct event_base *daemon_base = NULL;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
92
93
struct evdns_base *dnsbase;

94
95
struct event *lload_timeout_event;

96
97
98
99
/*
 * global lload statistics. Not mutex protected to preserve performance -
 * increment is atomic, at most we risk a bit of inconsistency
 */
Ondřej Kuzník's avatar
Ondřej Kuzník committed
100
lload_global_stats_t lload_stats = {};
101

Ondřej Kuzník's avatar
Ondřej Kuzník committed
102
103
104
105
#ifndef SLAPD_LISTEN_BACKLOG
#define SLAPD_LISTEN_BACKLOG 1024
#endif /* ! SLAPD_LISTEN_BACKLOG */

106
#define DAEMON_ID(fd) ( fd & lload_daemon_mask )
Ondřej Kuzník's avatar
Ondřej Kuzník committed
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125

#ifdef HAVE_WINSOCK
ldap_pvt_thread_mutex_t slapd_ws_mutex;
SOCKET *slapd_ws_sockets;
#define SD_READ 1
#define SD_WRITE 2
#define SD_ACTIVE 4
#define SD_LISTENER 8
#endif

#ifdef HAVE_TCPD
static ldap_pvt_thread_mutex_t sd_tcpd_mutex;
#endif /* TCP Wrappers */

typedef struct listener_item {
    struct evconnlistener *listener;
    ber_socket_t fd;
} listener_item;

126
typedef struct lload_daemon_st {
Ondřej Kuzník's avatar
Ondřej Kuzník committed
127
128
129
130
    ldap_pvt_thread_mutex_t sd_mutex;

    struct event_base *base;
    struct event *wakeup_event;
131
} lload_daemon_st;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
132

133
static lload_daemon_st lload_daemon[SLAPD_MAX_DAEMON_THREADS];
Ondřej Kuzník's avatar
Ondřej Kuzník committed
134
135
136
137

static void daemon_wakeup_cb( evutil_socket_t sig, short what, void *arg );

static void
138
lloadd_close( ber_socket_t s )
Ondřej Kuzník's avatar
Ondřej Kuzník committed
139
{
140
    Debug( LDAP_DEBUG_CONNS, "lloadd_close: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
141
            "closing fd=%ld\n",
Ondřej Kuzník's avatar
Ondřej Kuzník committed
142
143
144
145
            (long)s );
    tcp_close( s );
}

Ondřej Kuzník's avatar
Ondřej Kuzník committed
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
static int
lload_base_dispatch( struct event_base *base )
{
    int rc;

    while ( (rc = event_base_dispatch( base )) == 0 ) {
        if ( event_base_got_exit( base ) ) {
            break;
        }

        Debug( LDAP_DEBUG_TRACE, "lload_base_dispatch: "
                "handling pause\n" );
        /*
         * We are pausing, signal the pausing thread we've finished and
         * wait until the thread pool resumes operation.
         *
         * Do this in lockstep with the pausing thread.
         */
        ldap_pvt_thread_mutex_lock( &lload_wait_mutex );
        ldap_pvt_thread_cond_signal( &lload_wait_cond );

        /* Now wait until we resume */
        ldap_pvt_thread_cond_wait( &lload_pause_cond, &lload_wait_mutex );
        ldap_pvt_thread_mutex_unlock( &lload_wait_mutex );

        Debug( LDAP_DEBUG_TRACE, "lload_base_dispatch: "
                "resuming\n" );
    }

    if ( rc ) {
        Debug( LDAP_DEBUG_ANY, "lload_base_dispatch: "
                "event_base_dispatch() returned an error rc=%d\n",
                rc );
    }
    return rc;
}

Ondřej Kuzník's avatar
Ondřej Kuzník committed
183
static void
184
lload_free_listener_addresses( struct sockaddr **sal )
Ondřej Kuzník's avatar
Ondřej Kuzník committed
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
{
    struct sockaddr **sap;
    if ( sal == NULL ) return;
    for ( sap = sal; *sap != NULL; sap++ )
        ch_free(*sap);
    ch_free( sal );
}

#if defined(LDAP_PF_LOCAL) || defined(SLAP_X_LISTENER_MOD)
static int
get_url_perms( char **exts, mode_t *perms, int *crit )
{
    int i;

    assert( exts != NULL );
    assert( perms != NULL );
    assert( crit != NULL );

    *crit = 0;
    for ( i = 0; exts[i]; i++ ) {
        char *type = exts[i];
        int c = 0;

        if ( type[0] == '!' ) {
            c = 1;
            type++;
        }

        if ( strncasecmp( type, LDAPI_MOD_URLEXT "=",
                     sizeof(LDAPI_MOD_URLEXT "=") - 1 ) == 0 ) {
            char *value = type + ( sizeof(LDAPI_MOD_URLEXT "=") - 1 );
            mode_t p = 0;
            int j;

            switch ( strlen( value ) ) {
                case 4:
                    /* skip leading '0' */
                    if ( value[0] != '0' ) return LDAP_OTHER;
                    value++;

                case 3:
                    for ( j = 0; j < 3; j++ ) {
                        int v;

                        v = value[j] - '0';

                        if ( v < 0 || v > 7 ) return LDAP_OTHER;

                        p |= v << 3 * ( 2 - j );
                    }
                    break;

                case 10:
                    for ( j = 1; j < 10; j++ ) {
                        static mode_t m[] = { 0, S_IRUSR, S_IWUSR, S_IXUSR,
                                S_IRGRP, S_IWGRP, S_IXGRP, S_IROTH, S_IWOTH,
                                S_IXOTH };
                        static const char c[] = "-rwxrwxrwx";

                        if ( value[j] == c[j] ) {
                            p |= m[j];

                        } else if ( value[j] != '-' ) {
                            return LDAP_OTHER;
                        }
                    }
                    break;

                default:
                    return LDAP_OTHER;
            }

            *crit = c;
            *perms = p;

            return LDAP_SUCCESS;
        }
    }

    return LDAP_OTHER;
}
#endif /* LDAP_PF_LOCAL || SLAP_X_LISTENER_MOD */

/* port = 0 indicates AF_LOCAL */
static int
270
lload_get_listener_addresses(
Ondřej Kuzník's avatar
Ondřej Kuzník committed
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
        const char *host,
        unsigned short port,
        struct sockaddr ***sal )
{
    struct sockaddr **sap;

#ifdef LDAP_PF_LOCAL
    if ( port == 0 ) {
        sap = *sal = ch_malloc( 2 * sizeof(void *) );

        *sap = ch_calloc( 1, sizeof(struct sockaddr_un) );
        sap[1] = NULL;

        if ( strlen( host ) >
                ( sizeof( ((struct sockaddr_un *)*sap)->sun_path ) - 1 ) ) {
286
            Debug( LDAP_DEBUG_ANY, "lload_get_listener_addresses: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
                    "domain socket path (%s) too long in URL\n",
                    host );
            goto errexit;
        }

        (*sap)->sa_family = AF_LOCAL;
        strcpy( ((struct sockaddr_un *)*sap)->sun_path, host );
    } else
#endif /* LDAP_PF_LOCAL */
    {
#ifdef HAVE_GETADDRINFO
        struct addrinfo hints, *res, *sai;
        int n, err;
        char serv[7];

        memset( &hints, '\0', sizeof(hints) );
        hints.ai_flags = AI_PASSIVE;
        hints.ai_socktype = SOCK_STREAM;
        hints.ai_family = slap_inet4or6;
        snprintf( serv, sizeof(serv), "%d", port );

        if ( (err = getaddrinfo( host, serv, &hints, &res )) ) {
309
            Debug( LDAP_DEBUG_ANY, "lload_get_listener_addresses: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
                    "getaddrinfo() failed: %s\n",
                    AC_GAI_STRERROR(err) );
            return -1;
        }

        sai = res;
        for ( n = 2; ( sai = sai->ai_next ) != NULL; n++ ) {
            /* EMPTY */;
        }
        sap = *sal = ch_calloc( n, sizeof(void *) );

        *sap = NULL;

        for ( sai = res; sai; sai = sai->ai_next ) {
            if ( sai->ai_addr == NULL ) {
325
                Debug( LDAP_DEBUG_ANY, "lload_get_listener_addresses: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
                        "getaddrinfo ai_addr is NULL?\n" );
                freeaddrinfo( res );
                goto errexit;
            }

            switch ( sai->ai_family ) {
#ifdef LDAP_PF_INET6
                case AF_INET6:
                    *sap = ch_malloc( sizeof(struct sockaddr_in6) );
                    *(struct sockaddr_in6 *)*sap =
                            *((struct sockaddr_in6 *)sai->ai_addr);
                    break;
#endif /* LDAP_PF_INET6 */
                case AF_INET:
                    *sap = ch_malloc( sizeof(struct sockaddr_in) );
                    *(struct sockaddr_in *)*sap =
                            *((struct sockaddr_in *)sai->ai_addr);
                    break;
                default:
                    *sap = NULL;
                    break;
            }

            if ( *sap != NULL ) {
                (*sap)->sa_family = sai->ai_family;
                sap++;
                *sap = NULL;
            }
        }

        freeaddrinfo( res );

#else /* ! HAVE_GETADDRINFO */
        int i, n = 1;
        struct in_addr in;
        struct hostent *he = NULL;

        if ( host == NULL ) {
            in.s_addr = htonl( INADDR_ANY );

        } else if ( !inet_aton( host, &in ) ) {
            he = gethostbyname( host );
            if ( he == NULL ) {
369
                Debug( LDAP_DEBUG_ANY, "lload_get_listener_addresses: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
                        "invalid host %s\n",
                        host );
                return -1;
            }
            for ( n = 0; he->h_addr_list[n]; n++ ) /* empty */;
        }

        sap = *sal = ch_malloc( ( n + 1 ) * sizeof(void *) );

        for ( i = 0; i < n; i++ ) {
            sap[i] = ch_calloc( 1, sizeof(struct sockaddr_in) );
            sap[i]->sa_family = AF_INET;
            ((struct sockaddr_in *)sap[i])->sin_port = htons( port );
            AC_MEMCPY( &((struct sockaddr_in *)sap[i])->sin_addr,
                    he ? (struct in_addr *)he->h_addr_list[i] : &in,
                    sizeof(struct in_addr) );
        }
        sap[i] = NULL;
#endif /* ! HAVE_GETADDRINFO */
    }

    return 0;

errexit:
394
    lload_free_listener_addresses(*sal);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
395
396
397
398
    return -1;
}

static int
399
lload_open_listener( const char *url, int *listeners, int *cur )
Ondřej Kuzník's avatar
Ondřej Kuzník committed
400
401
{
    int num, tmp, rc;
402
403
    LloadListener l;
    LloadListener *li;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
    LDAPURLDesc *lud;
    unsigned short port;
    int err, addrlen = 0;
    struct sockaddr **sal = NULL, **psal;
    int socktype = SOCK_STREAM; /* default to COTS */
    ber_socket_t s;
    char ebuf[128];

#if defined(LDAP_PF_LOCAL) || defined(SLAP_X_LISTENER_MOD)
    /*
     * use safe defaults
     */
    int crit = 1;
#endif /* LDAP_PF_LOCAL || SLAP_X_LISTENER_MOD */

    rc = ldap_url_parse( url, &lud );

    if ( rc != LDAP_URL_SUCCESS ) {
422
        Debug( LDAP_DEBUG_ANY, "lload_open_listener: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
423
424
425
426
427
428
429
430
431
432
433
                "listen URL \"%s\" parse error=%d\n",
                url, rc );
        return rc;
    }

    l.sl_url.bv_val = NULL;
    l.sl_mute = 0;
    l.sl_busy = 0;

#ifndef HAVE_TLS
    if ( ldap_pvt_url_scheme2tls( lud->lud_scheme ) ) {
434
        Debug( LDAP_DEBUG_ANY, "lload_open_listener: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
                "TLS not supported (%s)\n",
                url );
        ldap_free_urldesc( lud );
        return -1;
    }

    if ( !lud->lud_port ) lud->lud_port = LDAP_PORT;

#else /* HAVE_TLS */
    l.sl_is_tls = ldap_pvt_url_scheme2tls( lud->lud_scheme );

    if ( !lud->lud_port ) {
        lud->lud_port = l.sl_is_tls ? LDAPS_PORT : LDAP_PORT;
    }
#endif /* HAVE_TLS */

#ifdef LDAP_TCP_BUFFER
    l.sl_tcp_rmem = 0;
    l.sl_tcp_wmem = 0;
#endif /* LDAP_TCP_BUFFER */

    port = (unsigned short)lud->lud_port;

    tmp = ldap_pvt_url_scheme2proto( lud->lud_scheme );
    if ( tmp == LDAP_PROTO_IPC ) {
#ifdef LDAP_PF_LOCAL
        if ( lud->lud_host == NULL || lud->lud_host[0] == '\0' ) {
462
            err = lload_get_listener_addresses( LDAPI_SOCK, 0, &sal );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
463
        } else {
464
            err = lload_get_listener_addresses( lud->lud_host, 0, &sal );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
465
466
467
        }
#else /* ! LDAP_PF_LOCAL */

468
        Debug( LDAP_DEBUG_ANY, "lload_open_listener: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
469
470
471
472
473
474
475
476
                "URL scheme not supported: %s\n",
                url );
        ldap_free_urldesc( lud );
        return -1;
#endif /* ! LDAP_PF_LOCAL */
    } else {
        if ( lud->lud_host == NULL || lud->lud_host[0] == '\0' ||
                strcmp( lud->lud_host, "*" ) == 0 ) {
477
            err = lload_get_listener_addresses( NULL, port, &sal );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
478
        } else {
479
            err = lload_get_listener_addresses( lud->lud_host, port, &sal );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
480
481
482
483
484
485
486
487
488
489
490
491
492
        }
    }

#if defined(LDAP_PF_LOCAL) || defined(SLAP_X_LISTENER_MOD)
    if ( lud->lud_exts ) {
        err = get_url_perms( lud->lud_exts, &l.sl_perms, &crit );
    } else {
        l.sl_perms = S_IRWXU | S_IRWXO;
    }
#endif /* LDAP_PF_LOCAL || SLAP_X_LISTENER_MOD */

    ldap_free_urldesc( lud );
    if ( err ) {
493
        lload_free_listener_addresses( sal );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
494
495
496
497
        return -1;
    }

    /* If we got more than one address returned, we need to make space
498
     * for it in the lload_listeners array.
Ondřej Kuzník's avatar
Ondřej Kuzník committed
499
500
501
502
     */
    for ( num = 0; sal[num]; num++ ) /* empty */;
    if ( num > 1 ) {
        *listeners += num - 1;
503
504
        lload_listeners = ch_realloc( lload_listeners,
                ( *listeners + 1 ) * sizeof(LloadListener *) );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
    }

    psal = sal;
    while ( *sal != NULL ) {
        char *af;
        switch ( (*sal)->sa_family ) {
            case AF_INET:
                af = "IPv4";
                break;
#ifdef LDAP_PF_INET6
            case AF_INET6:
                af = "IPv6";
                break;
#endif /* LDAP_PF_INET6 */
#ifdef LDAP_PF_LOCAL
            case AF_LOCAL:
                af = "Local";
                break;
#endif /* LDAP_PF_LOCAL */
            default:
                sal++;
                continue;
        }

        s = socket( (*sal)->sa_family, socktype, 0 );
        if ( s == AC_SOCKET_INVALID ) {
            int err = sock_errno();
532
            Debug( LDAP_DEBUG_ANY, "lload_open_listener: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
                    "%s socket() failed errno=%d (%s)\n",
                    af, err, sock_errstr( err, ebuf, sizeof(ebuf) ) );
            sal++;
            continue;
        }
        ber_pvt_socket_set_nonblock( s, 1 );
        l.sl_sd = s;

#ifdef LDAP_PF_LOCAL
        if ( (*sal)->sa_family == AF_LOCAL ) {
            unlink( ((struct sockaddr_un *)*sal)->sun_path );
        } else
#endif /* LDAP_PF_LOCAL */
        {
#ifdef SO_REUSEADDR
            /* enable address reuse */
            tmp = 1;
            rc = setsockopt(
                    s, SOL_SOCKET, SO_REUSEADDR, (char *)&tmp, sizeof(tmp) );
            if ( rc == AC_SOCKET_ERROR ) {
                int err = sock_errno();
554
                Debug( LDAP_DEBUG_ANY, "lload_open_listener(%ld): "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
                        "setsockopt(SO_REUSEADDR) failed errno=%d (%s)\n",
                        (long)l.sl_sd, err,
                        sock_errstr( err, ebuf, sizeof(ebuf) ) );
            }
#endif /* SO_REUSEADDR */
        }

        switch ( (*sal)->sa_family ) {
            case AF_INET:
                addrlen = sizeof(struct sockaddr_in);
                break;
#ifdef LDAP_PF_INET6
            case AF_INET6:
#ifdef IPV6_V6ONLY
                /* Try to use IPv6 sockets for IPv6 only */
                tmp = 1;
                rc = setsockopt( s, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&tmp,
                        sizeof(tmp) );
                if ( rc == AC_SOCKET_ERROR ) {
                    int err = sock_errno();
575
                    Debug( LDAP_DEBUG_ANY, "lload_open_listener(%ld): "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
                            "setsockopt(IPV6_V6ONLY) failed errno=%d (%s)\n",
                            (long)l.sl_sd, err,
                            sock_errstr( err, ebuf, sizeof(ebuf) ) );
                }
#endif /* IPV6_V6ONLY */
                addrlen = sizeof(struct sockaddr_in6);
                break;
#endif /* LDAP_PF_INET6 */

#ifdef LDAP_PF_LOCAL
            case AF_LOCAL:
#ifdef LOCAL_CREDS
            {
                int one = 1;
                setsockopt( s, 0, LOCAL_CREDS, &one, sizeof(one) );
            }
#endif /* LOCAL_CREDS */

                addrlen = sizeof(struct sockaddr_un);
                break;
#endif /* LDAP_PF_LOCAL */
        }

#ifdef LDAP_PF_LOCAL
        /* create socket with all permissions set for those systems
         * that honor permissions on sockets (e.g. Linux); typically,
         * only write is required.  To exploit filesystem permissions,
         * place the socket in a directory and use directory's
         * permissions.  Need write perms to the directory to
         * create/unlink the socket; likely need exec perms to access
         * the socket (ITS#4709) */
        {
            mode_t old_umask = 0;

            if ( (*sal)->sa_family == AF_LOCAL ) {
                old_umask = umask( 0 );
            }
#endif /* LDAP_PF_LOCAL */
            rc = bind( s, *sal, addrlen );
#ifdef LDAP_PF_LOCAL
            if ( old_umask != 0 ) {
                umask( old_umask );
            }
        }
#endif /* LDAP_PF_LOCAL */
        if ( rc ) {
            err = sock_errno();
623
            Debug( LDAP_DEBUG_ANY, "lload_open_listener: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
                    "bind(%ld) failed errno=%d (%s)\n",
                    (long)l.sl_sd, err,
                    sock_errstr( err, ebuf, sizeof(ebuf) ) );
            tcp_close( s );
            sal++;
            continue;
        }

        switch ( (*sal)->sa_family ) {
#ifdef LDAP_PF_LOCAL
            case AF_LOCAL: {
                char *path = ((struct sockaddr_un *)*sal)->sun_path;
                l.sl_name.bv_len = strlen( path ) + STRLENOF("PATH=");
                l.sl_name.bv_val = ch_malloc( l.sl_name.bv_len + 1 );
                snprintf( l.sl_name.bv_val, l.sl_name.bv_len + 1, "PATH=%s",
                        path );
            } break;
#endif /* LDAP_PF_LOCAL */

            case AF_INET: {
                char addr[INET_ADDRSTRLEN];
                const char *s;
#if defined(HAVE_GETADDRINFO) && defined(HAVE_INET_NTOP)
                s = inet_ntop( AF_INET,
                        &((struct sockaddr_in *)*sal)->sin_addr, addr,
                        sizeof(addr) );
#else /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
                s = inet_ntoa( ((struct sockaddr_in *)*sal)->sin_addr );
#endif /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
                if ( !s ) s = SLAP_STRING_UNKNOWN;
                port = ntohs( ((struct sockaddr_in *)*sal)->sin_port );
                l.sl_name.bv_val =
                        ch_malloc( sizeof("IP=255.255.255.255:65535") );
                snprintf( l.sl_name.bv_val,
                        sizeof("IP=255.255.255.255:65535"), "IP=%s:%d", s,
                        port );
                l.sl_name.bv_len = strlen( l.sl_name.bv_val );
            } break;

#ifdef LDAP_PF_INET6
            case AF_INET6: {
                char addr[INET6_ADDRSTRLEN];
                const char *s;
                s = inet_ntop( AF_INET6,
                        &((struct sockaddr_in6 *)*sal)->sin6_addr, addr,
                        sizeof(addr) );
                if ( !s ) s = SLAP_STRING_UNKNOWN;
                port = ntohs( ((struct sockaddr_in6 *)*sal)->sin6_port );
                l.sl_name.bv_len = strlen( s ) + sizeof("IP=[]:65535");
                l.sl_name.bv_val = ch_malloc( l.sl_name.bv_len );
                snprintf( l.sl_name.bv_val, l.sl_name.bv_len, "IP=[%s]:%d", s,
                        port );
                l.sl_name.bv_len = strlen( l.sl_name.bv_val );
            } break;
#endif /* LDAP_PF_INET6 */

            default:
681
                Debug( LDAP_DEBUG_ANY, "lload_open_listener: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
682
683
684
685
686
687
688
                        "unsupported address family (%d)\n",
                        (int)(*sal)->sa_family );
                break;
        }

        AC_MEMCPY( &l.sl_sa, *sal, addrlen );
        ber_str2bv( url, 0, 1, &l.sl_url );
689
        li = ch_malloc( sizeof(LloadListener) );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
690
        *li = l;
691
        lload_listeners[*cur] = li;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
692
693
694
695
        (*cur)++;
        sal++;
    }

696
    lload_free_listener_addresses( psal );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
697
698

    if ( l.sl_url.bv_val == NULL ) {
699
        Debug( LDAP_DEBUG_ANY, "lload_open_listener: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
700
701
702
703
704
                "failed on %s\n",
                url );
        return -1;
    }

705
    Debug( LDAP_DEBUG_TRACE, "lload_open_listener: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
706
707
708
709
710
711
712
713
714
            "listener initialized %s\n",
            l.sl_url.bv_val );

    return 0;
}

int lloadd_inited = 0;

int
715
lloadd_daemon_init( const char *urls )
Ondřej Kuzník's avatar
Ondřej Kuzník committed
716
717
718
719
{
    int i, j, n;
    char **u;

720
    Debug( LDAP_DEBUG_ARGS, "lloadd_daemon_init: %s\n",
Ondřej Kuzník's avatar
Ondřej Kuzník committed
721
722
723
724
725
726
727
728
729
730
731
            urls ? urls : "<null>" );

#ifdef HAVE_TCPD
    ldap_pvt_thread_mutex_init( &sd_tcpd_mutex );
#endif /* TCP Wrappers */

    if ( urls == NULL ) urls = "ldap:///";

    u = ldap_str2charray( urls, " " );

    if ( u == NULL || u[0] == NULL ) {
732
        Debug( LDAP_DEBUG_ANY, "lloadd_daemon_init: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
733
                "no urls (%s) provided\n",
Ondřej Kuzník's avatar
Ondřej Kuzník committed
734
735
736
737
738
739
                urls );
        if ( u ) ldap_charray_free( u );
        return -1;
    }

    for ( i = 0; u[i] != NULL; i++ ) {
740
        Debug( LDAP_DEBUG_TRACE, "lloadd_daemon_init: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
741
742
743
744
745
                "listen on %s\n",
                u[i] );
    }

    if ( i == 0 ) {
746
        Debug( LDAP_DEBUG_ANY, "lloadd_daemon_init: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
747
748
749
750
751
752
                "no listeners to open (%s)\n",
                urls );
        ldap_charray_free( u );
        return -1;
    }

753
    Debug( LDAP_DEBUG_TRACE, "lloadd_daemon_init: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
754
755
            "%d listeners to open...\n",
            i );
756
    lload_listeners = ch_malloc( ( i + 1 ) * sizeof(LloadListener *) );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
757
758

    for ( n = 0, j = 0; u[n]; n++ ) {
759
        if ( lload_open_listener( u[n], &i, &j ) ) {
Ondřej Kuzník's avatar
Ondřej Kuzník committed
760
761
762
763
            ldap_charray_free( u );
            return -1;
        }
    }
764
    lload_listeners[j] = NULL;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
765

766
    Debug( LDAP_DEBUG_TRACE, "lloadd_daemon_init: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
767
768
769
770
771
772
773
774
775
            "%d listeners opened\n",
            i );

    ldap_charray_free( u );

    return !i;
}

int
776
lloadd_daemon_destroy( void )
Ondřej Kuzník's avatar
Ondřej Kuzník committed
777
778
779
780
{
    if ( lloadd_inited ) {
        int i;

781
782
783
784
        for ( i = 0; i < lload_daemon_threads; i++ ) {
            ldap_pvt_thread_mutex_destroy( &lload_daemon[i].sd_mutex );
            if ( lload_daemon[i].wakeup_event ) {
                event_free( lload_daemon[i].wakeup_event );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
785
            }
786
787
            if ( lload_daemon[i].base ) {
                event_base_free( lload_daemon[i].base );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
788
            }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
789
        }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
790
791
792
793

        event_base_free( daemon_base );
        daemon_base = NULL;

Ondřej Kuzník's avatar
Ondřej Kuzník committed
794
795
796
797
798
799
800
801
802
803
804
805
        lloadd_inited = 0;
#ifdef HAVE_TCPD
        ldap_pvt_thread_mutex_destroy( &sd_tcpd_mutex );
#endif /* TCP Wrappers */
    }

    return 0;
}

static void
destroy_listeners( void )
{
806
    LloadListener *lr, **ll = lload_listeners;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
807
808
809
810
811
812
813
814
815
816
817
818
819
820

    if ( ll == NULL ) return;

    ldap_pvt_thread_join( listener_tid, (void *)NULL );

    while ( (lr = *ll++) != NULL ) {
        if ( lr->sl_url.bv_val ) {
            ber_memfree( lr->sl_url.bv_val );
        }

        if ( lr->sl_name.bv_val ) {
            ber_memfree( lr->sl_name.bv_val );
        }

Ondřej Kuzník's avatar
Ondřej Kuzník committed
821
822
823
824
825
826
#ifdef LDAP_PF_LOCAL
        if ( lr->sl_sa.sa_addr.sa_family == AF_LOCAL ) {
            unlink( lr->sl_sa.sa_un_addr.sun_path );
        }
#endif /* LDAP_PF_LOCAL */

Ondřej Kuzník's avatar
Ondřej Kuzník committed
827
828
829
830
831
        evconnlistener_free( lr->listener );

        free( lr );
    }

832
833
    free( lload_listeners );
    lload_listeners = NULL;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
834
835
836
837

    if ( listener_base ) {
        event_base_free( listener_base );
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
838
839
840
}

static void
841
lload_listener(
Ondřej Kuzník's avatar
Ondřej Kuzník committed
842
843
844
845
846
847
        struct evconnlistener *listener,
        ber_socket_t s,
        struct sockaddr *a,
        int len,
        void *arg )
{
848
849
    LloadListener *sl = arg;
    LloadConnection *c;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
    Sockaddr *from = (Sockaddr *)a;
#ifdef SLAPD_RLOOKUPS
    char hbuf[NI_MAXHOST];
#endif /* SLAPD_RLOOKUPS */

    const char *peeraddr = NULL;
    /* we assume INET6_ADDRSTRLEN > INET_ADDRSTRLEN */
    char addr[INET6_ADDRSTRLEN];
#ifdef LDAP_PF_LOCAL
    char peername[MAXPATHLEN + sizeof("PATH=")];
#ifdef LDAP_PF_LOCAL_SENDMSG
    char peerbuf[8];
    struct berval peerbv = BER_BVNULL;
#endif
#elif defined(LDAP_PF_INET6)
    char peername[sizeof("IP=[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535")];
#else /* ! LDAP_PF_LOCAL && ! LDAP_PF_INET6 */
    char peername[sizeof("IP=255.255.255.255:65336")];
#endif /* LDAP_PF_LOCAL */
    int cflag;
    int tid;
    char ebuf[128];

873
    Debug( LDAP_DEBUG_TRACE, ">>> lload_listener(%s)\n", sl->sl_url.bv_val );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
874
875
876
877
878
879
880
881
882
883

    peername[0] = '\0';

    /* Resume the listener FD to allow concurrent-processing of
     * additional incoming connections.
     */
    sl->sl_busy = 0;

    tid = DAEMON_ID(s);

884
    Debug( LDAP_DEBUG_CONNS, "lload_listener: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
            "listen=%ld, new connection fd=%ld\n",
            (long)sl->sl_sd, (long)s );

#if defined(SO_KEEPALIVE) || defined(TCP_NODELAY)
#ifdef LDAP_PF_LOCAL
    /* for IPv4 and IPv6 sockets only */
    if ( from->sa_addr.sa_family != AF_LOCAL )
#endif /* LDAP_PF_LOCAL */
    {
        int rc;
        int tmp;
#ifdef SO_KEEPALIVE
        /* enable keep alives */
        tmp = 1;
        rc = setsockopt(
                s, SOL_SOCKET, SO_KEEPALIVE, (char *)&tmp, sizeof(tmp) );
        if ( rc == AC_SOCKET_ERROR ) {
            int err = sock_errno();
903
            Debug( LDAP_DEBUG_ANY, "lload_listener(%ld): "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
904
905
906
907
908
909
910
911
912
913
914
                    "setsockopt(SO_KEEPALIVE) failed errno=%d (%s)\n",
                    (long)s, err, sock_errstr( err, ebuf, sizeof(ebuf) ) );
        }
#endif /* SO_KEEPALIVE */
#ifdef TCP_NODELAY
        /* enable no delay */
        tmp = 1;
        rc = setsockopt(
                s, IPPROTO_TCP, TCP_NODELAY, (char *)&tmp, sizeof(tmp) );
        if ( rc == AC_SOCKET_ERROR ) {
            int err = sock_errno();
915
            Debug( LDAP_DEBUG_ANY, "lload_listener(%ld): "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
                    "setsockopt(TCP_NODELAY) failed errno=%d (%s)\n",
                    (long)s, err, sock_errstr( err, ebuf, sizeof(ebuf) ) );
        }
#endif /* TCP_NODELAY */
    }
#endif /* SO_KEEPALIVE || TCP_NODELAY */

    cflag = 0;
    switch ( from->sa_addr.sa_family ) {
#ifdef LDAP_PF_LOCAL
        case AF_LOCAL:
            cflag |= CONN_IS_IPC;

            /* FIXME: apparently accept doesn't fill the sun_path member */
            sprintf( peername, "PATH=%s", sl->sl_sa.sa_un_addr.sun_path );
            break;
#endif /* LDAP_PF_LOCAL */

#ifdef LDAP_PF_INET6
        case AF_INET6:
            if ( IN6_IS_ADDR_V4MAPPED( &from->sa_in6_addr.sin6_addr ) ) {
#if defined(HAVE_GETADDRINFO) && defined(HAVE_INET_NTOP)
                peeraddr = inet_ntop( AF_INET,
                        ( (struct in_addr *)&from->sa_in6_addr.sin6_addr
                                        .s6_addr[12] ),
                        addr, sizeof(addr) );
#else /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
                peeraddr = inet_ntoa( *( (struct in_addr *)&from->sa_in6_addr
                                                 .sin6_addr.s6_addr[12] ) );
#endif /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
                if ( !peeraddr ) peeraddr = SLAP_STRING_UNKNOWN;
                sprintf( peername, "IP=%s:%d", peeraddr,
                        (unsigned)ntohs( from->sa_in6_addr.sin6_port ) );
            } else {
                peeraddr = inet_ntop( AF_INET6, &from->sa_in6_addr.sin6_addr,
                        addr, sizeof(addr) );
                if ( !peeraddr ) peeraddr = SLAP_STRING_UNKNOWN;
                sprintf( peername, "IP=[%s]:%d", peeraddr,
                        (unsigned)ntohs( from->sa_in6_addr.sin6_port ) );
            }
            break;
#endif /* LDAP_PF_INET6 */

        case AF_INET: {
#if defined(HAVE_GETADDRINFO) && defined(HAVE_INET_NTOP)
            peeraddr = inet_ntop(
                    AF_INET, &from->sa_in_addr.sin_addr, addr, sizeof(addr) );
#else /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
            peeraddr = inet_ntoa( from->sa_in_addr.sin_addr );
#endif /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
            if ( !peeraddr ) peeraddr = SLAP_STRING_UNKNOWN;
            sprintf( peername, "IP=%s:%d", peeraddr,
                    (unsigned)ntohs( from->sa_in_addr.sin_port ) );
        } break;

        default:
972
            lloadd_close( s );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
973
974
975
976
977
978
            return;
    }

#ifdef HAVE_TLS
    if ( sl->sl_is_tls ) cflag |= CONN_IS_TLS;
#endif
979
    c = client_init( s, sl, peername, lload_daemon[tid].base, cflag );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
980
981

    if ( !c ) {
982
        Debug( LDAP_DEBUG_ANY, "lload_listener: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
983
                "client_init(%ld, %s, %s) failed\n",
Ondřej Kuzník's avatar
Ondřej Kuzník committed
984
                (long)s, peername, sl->sl_name.bv_val );
985
        lloadd_close( s );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
986
987
988
989
990
991
    }

    return;
}

static void *
992
lload_listener_thread( void *ctx )
Ondřej Kuzník's avatar
Ondřej Kuzník committed
993
{
Ondřej Kuzník's avatar
Ondřej Kuzník committed
994
    int rc = lload_base_dispatch( listener_base );
995
    Debug( LDAP_DEBUG_ANY, "lload_listener_thread: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
996
997
998
999
1000
1001
1002
1003
1004
            "event loop finished: rc=%d\n",
            rc );

    return (void *)NULL;
}

static void
listener_error_cb( struct evconnlistener *lev, void *arg )
{
1005
    LloadListener *l = arg;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
    int err = EVUTIL_SOCKET_ERROR();

    assert( l->listener == lev );
    if (
#ifdef EMFILE
            err == EMFILE ||
#endif /* EMFILE */
#ifdef ENFILE
            err == ENFILE ||
#endif /* ENFILE */
            0 ) {
1017
        ldap_pvt_thread_mutex_lock( &lload_daemon[0].sd_mutex );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1018
1019
1020
1021
        emfile++;
        /* Stop listening until an existing session closes */
        l->sl_mute = 1;
        evconnlistener_disable( lev );
1022
        ldap_pvt_thread_mutex_unlock( &lload_daemon[0].sd_mutex );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1023
1024
1025
1026
        Debug( LDAP_DEBUG_ANY, "listener_error_cb: "
                "too many open files, cannot accept new connections on "
                "url=%s\n",
                l->sl_url.bv_val );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1027
1028
1029
1030
1031
1032
1033
1034
1035
    } else {
        char ebuf[128];
        Debug( LDAP_DEBUG_ANY, "listener_error_cb: "
                "received an error on a listener, shutting down: '%s'\n",
                sock_errstr( err, ebuf, sizeof(ebuf) ) );
        event_base_loopexit( l->base, NULL );
    }
}

Ondřej Kuzník's avatar
Ondřej Kuzník committed
1036
1037
1038
1039
1040
void
listeners_reactivate( void )
{
    int i;

1041
1042
1043
    ldap_pvt_thread_mutex_lock( &lload_daemon[0].sd_mutex );
    for ( i = 0; emfile && lload_listeners[i] != NULL; i++ ) {
        LloadListener *lr = lload_listeners[i];
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054

        if ( lr->sl_sd == AC_SOCKET_INVALID ) continue;
        if ( lr->sl_mute ) {
            emfile--;
            evconnlistener_enable( lr->listener );
            lr->sl_mute = 0;
            Debug( LDAP_DEBUG_CONNS, "listeners_reactivate: "
                    "reactivated listener url=%s\n",
                    lr->sl_url.bv_val );
        }
    }
1055
    if ( emfile && lload_listeners[i] == NULL ) {
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1056
1057
1058
1059
        /* Walked the entire list without enabling anything; emfile
         * counter is stale. Reset it. */
        emfile = 0;
    }
1060
    ldap_pvt_thread_mutex_unlock( &lload_daemon[0].sd_mutex );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1061
1062
}

Ondřej Kuzník's avatar
Ondřej Kuzník committed
1063
static int
1064
lload_listener_activate( void )
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1065
1066
1067
1068
1069
1070
1071
1072
{
    struct evconnlistener *listener;
    int l, rc;
    char ebuf[128];

    listener_base = event_base_new();
    if ( !listener_base ) return -1;

1073
1074
    for ( l = 0; lload_listeners[l] != NULL; l++ ) {
        if ( lload_listeners[l]->sl_sd == AC_SOCKET_INVALID ) continue;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1075
1076
1077
1078
1079
1080
1081
1082

            /* FIXME: TCP-only! */
#ifdef LDAP_TCP_BUFFER
        if ( 1 ) {
            int origsize, size, realsize, rc;
            socklen_t optlen;

            size = 0;
1083
1084
            if ( lload_listeners[l]->sl_tcp_rmem > 0 ) {
                size = lload_listeners[l]->sl_tcp_rmem;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1085
1086
1087
1088
1089
1090
            } else if ( slapd_tcp_rmem > 0 ) {
                size = slapd_tcp_rmem;
            }

            if ( size > 0 ) {
                optlen = sizeof(origsize);
1091
                rc = getsockopt( lload_listeners[l]->sl_sd, SOL_SOCKET,
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1092
1093
1094
1095
                        SO_RCVBUF, (void *)&origsize, &optlen );

                if ( rc ) {
                    int err = sock_errno();
1096
                    Debug( LDAP_DEBUG_ANY, "lload_listener_activate: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1097
1098
1099
1100
1101
                            "getsockopt(SO_RCVBUF) failed errno=%d (%s)\n",
                            err, AC_STRERROR_R( err, ebuf, sizeof(ebuf) ) );
                }

                optlen = sizeof(size);
1102
                rc = setsockopt( lload_listeners[l]->sl_sd, SOL_SOCKET,
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1103
1104
1105
1106
                        SO_RCVBUF, (const void *)&size, optlen );

                if ( rc ) {
                    int err = sock_errno();
1107
                    Debug( LDAP_DEBUG_ANY, "lload_listener_activate: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1108
1109
1110
1111
1112
                            "setsockopt(SO_RCVBUF) failed errno=%d (%s)\n",
                            err, sock_errstr( err, ebuf, sizeof(ebuf) ) );
                }

                optlen = sizeof(realsize);
1113
                rc = getsockopt( lload_listeners[l]->sl_sd, SOL_SOCKET,
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1114
1115
1116
1117
                        SO_RCVBUF, (void *)&realsize, &optlen );

                if ( rc ) {
                    int err = sock_errno();
1118
                    Debug( LDAP_DEBUG_ANY, "lload_listener_activate: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1119
1120
1121
1122
                            "getsockopt(SO_RCVBUF) failed errno=%d (%s)\n",
                            err, sock_errstr( err, ebuf, sizeof(ebuf) ) );
                }

1123
                Debug( LDAP_DEBUG_ANY, "lload_listener_activate: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1124
1125
                        "url=%s (#%d) RCVBUF original size=%d requested "
                        "size=%d real size=%d\n",
1126
                        lload_listeners[l]->sl_url.bv_val, l, origsize, size,
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1127
1128
1129
1130
                        realsize );
            }

            size = 0;
1131
1132
            if ( lload_listeners[l]->sl_tcp_wmem > 0 ) {
                size = lload_listeners[l]->sl_tcp_wmem;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1133
1134
1135
1136
1137
1138
            } else if ( slapd_tcp_wmem > 0 ) {
                size = slapd_tcp_wmem;
            }

            if ( size > 0 ) {
                optlen = sizeof(origsize);
1139
                rc = getsockopt( lload_listeners[l]->sl_sd, SOL_SOCKET,
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1140
1141
1142
1143
                        SO_SNDBUF, (void *)&origsize, &optlen );

                if ( rc ) {
                    int err = sock_errno();
1144
                    Debug( LDAP_DEBUG_ANY, "lload_listener_activate: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1145
1146
1147
1148
1149
                            "getsockopt(SO_SNDBUF) failed errno=%d (%s)\n",
                            err, sock_errstr( err, ebuf, sizeof(ebuf) ) );
                }

                optlen = sizeof(size);
1150
                rc = setsockopt( lload_listeners[l]->sl_sd, SOL_SOCKET,
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1151
1152
1153
1154
                        SO_SNDBUF, (const void *)&size, optlen );

                if ( rc ) {
                    int err = sock_errno();
1155
                    Debug( LDAP_DEBUG_ANY, "lload_listener_activate: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1156
1157
1158
1159
1160
                            "setsockopt(SO_SNDBUF) failed errno=%d (%s)\n",
                            err, sock_errstr( err, ebuf, sizeof(ebuf) ) );
                }

                optlen = sizeof(realsize);
1161
                rc = getsockopt( lload_listeners[l]->sl_sd, SOL_SOCKET,
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1162
1163
1164
1165
                        SO_SNDBUF, (void *)&realsize, &optlen );

                if ( rc ) {
                    int err = sock_errno();
1166
                    Debug( LDAP_DEBUG_ANY, "lload_listener_activate: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1167
1168
1169
1170
                            "getsockopt(SO_SNDBUF) failed errno=%d (%s)\n",
                            err, sock_errstr( err, ebuf, sizeof(ebuf) ) );
                }

1171
                Debug( LDAP_DEBUG_ANY, "lload_listener_activate: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1172
1173
                        "url=%s (#%d) SNDBUF original size=%d requested "
                        "size=%d real size=%d\n",
1174
                        lload_listeners[l]->sl_url.bv_val, l, origsize, size,
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1175
1176
1177
1178
1179
                        realsize );
            }
        }
#endif /* LDAP_TCP_BUFFER */

1180
1181
1182
1183
        lload_listeners[l]->sl_busy = 1;
        listener = evconnlistener_new( listener_base, lload_listener,
                lload_listeners[l], LEV_OPT_THREADSAFE, SLAPD_LISTEN_BACKLOG,
                lload_listeners[l]->sl_sd );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
        if ( !listener ) {
            int err = sock_errno();

#ifdef LDAP_PF_INET6
            /* If error is EADDRINUSE, we are trying to listen to INADDR_ANY and
             * we are already listening to in6addr_any, then we want to ignore
             * this and continue.
             */
            if ( err == EADDRINUSE ) {
                int i;
1194
                struct sockaddr_in sa = lload_listeners[l]->sl_sa.sa_in_addr;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1195
1196
1197
1198
1199
                struct sockaddr_in6 sa6;

                if ( sa.sin_family == AF_INET &&
                        sa.sin_addr.s_addr == htonl( INADDR_ANY ) ) {
                    for ( i = 0; i < l; i++ ) {
1200
                        sa6 = lload_listeners[i]->sl_sa.sa_in6_addr;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1201
1202
1203
1204
1205
1206
1207
1208
1209
                        if ( sa6.sin6_family == AF_INET6 &&
                                !memcmp( &sa6.sin6_addr, &in6addr_any,
                                        sizeof(struct in6_addr) ) ) {
                            break;
                        }
                    }

                    if ( i < l ) {
                        /* We are already listening to in6addr_any */
1210
                        Debug( LDAP_DEBUG_CONNS, "lload_listener_activate: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1211
1212
1213
                                "Attempt to listen to 0.0.0.0 failed, "
                                "already listening on ::, assuming IPv4 "
                                "included\n" );
1214
1215
                        lloadd_close( lload_listeners[l]->sl_sd );
                        lload_listeners[l]->sl_sd = AC_SOCKET_INVALID;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1216
1217
1218
1219
1220
                        continue;
                    }
                }
            }
#endif /* LDAP_PF_INET6 */
1221
            Debug( LDAP_DEBUG_ANY, "lload_listener_activate: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1222
                    "listen(%s, 5) failed errno=%d (%s)\n",
1223
                    lload_listeners[l]->sl_url.bv_val, err,
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1224
1225
1226
1227
                    sock_errstr( err, ebuf, sizeof(ebuf) ) );
            return -1;
        }

1228
1229
        lload_listeners[l]->base = listener_base;
        lload_listeners[l]->listener = listener;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1230
1231
1232
1233
        evconnlistener_set_error_cb( listener, listener_error_cb );
    }

    rc = ldap_pvt_thread_create(
1234
            &listener_tid, 0, lload_listener_thread, lload_listeners[l] );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1235
1236

    if ( rc != 0 ) {
1237
        Debug( LDAP_DEBUG_ANY, "lload_listener_activate(%d): "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1238
                "submit failed (%d)\n",
1239
                lload_listeners[l]->sl_sd, rc );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1240
1241
1242
1243
1244
    }
    return rc;
}

static void *
1245
lloadd_io_task( void *ptr )
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1246
1247
1248
{
    int rc;
    int tid = (ldap_pvt_thread_t *)ptr - daemon_tid;
1249
    struct event_base *base = lload_daemon[tid].base;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1250
1251
1252
1253
    struct event *event;

    event = event_new( base, -1, EV_WRITE, daemon_wakeup_cb, ptr );
    if ( !event ) {
1254
        Debug( LDAP_DEBUG_ANY, "lloadd_io_task: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1255
1256
1257
1258
                "failed to set up the wakeup event\n" );
        return (void *)-1;
    }
    event_add( event, NULL );
1259
    lload_daemon[tid].wakeup_event = event;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1260
1261

    /* run */
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1262
    rc = lload_base_dispatch( base );
1263
    Debug( LDAP_DEBUG_ANY, "lloadd_io_task: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
            "Daemon %d, event loop finished: rc=%d\n",
            tid, rc );

    if ( !slapd_gentle_shutdown ) {
        slapd_abrupt_shutdown = 1;
    }

    return NULL;
}

int
1275
lloadd_daemon( struct event_base *daemon_base )
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1276
1277
{
    int i, rc;
1278
    LloadBackend *b;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1279
    struct event_base *base;
1280
    struct event *event;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1281
1282
1283

    assert( daemon_base != NULL );

Ondřej Kuzník's avatar
Ondřej Kuzník committed
1284
1285
1286
1287
1288
1289
1290
1291
1292
    dnsbase = evdns_base_new( daemon_base,
            EVDNS_BASE_INITIALIZE_NAMESERVERS |
                    EVDNS_BASE_DISABLE_WHEN_INACTIVE );
    if ( !dnsbase ) {
        Debug( LDAP_DEBUG_ANY, "lloadd startup: "
                "failed to set up for async name resolution\n" );
        return -1;
    }

1293
1294
    if ( lload_daemon_threads > SLAPD_MAX_DAEMON_THREADS )
        lload_daemon_threads = SLAPD_MAX_DAEMON_THREADS;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1295
1296

    daemon_tid =
1297
            ch_malloc( lload_daemon_threads * sizeof(ldap_pvt_thread_t) );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1298

1299
    for ( i = 0; i < lload_daemon_threads; i++ ) {
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1300
1301
1302
1303
1304
1305
        base = event_base_new();
        if ( !base ) {
            Debug( LDAP_DEBUG_ANY, "lloadd startup: "
                    "failed to acquire event base for an I/O thread\n" );
            return -1;
        }
1306
        lload_daemon[i].base = base;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1307

1308
        ldap_pvt_thread_mutex_init( &lload_daemon[i].sd_mutex );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1309
1310
        /* threads that handle client and upstream sockets */
        rc = ldap_pvt_thread_create(
1311
                &daemon_tid[i], 0, lloadd_io_task, &daemon_tid[i] );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1312
1313
1314
1315
1316
1317
1318
1319
1320

        if ( rc != 0 ) {
            Debug( LDAP_DEBUG_ANY, "lloadd startup: "
                    "listener ldap_pvt_thread_create failed (%d)\n",
                    rc );
            return rc;
        }
    }

1321
    if ( (rc = lload_listener_activate()) != 0 ) {
1322
1323
1324
        return rc;
    }

1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
    if ( !LDAP_CIRCLEQ_EMPTY( &backend ) ) {
        current_backend = LDAP_CIRCLEQ_FIRST( &backend );
        LDAP_CIRCLEQ_FOREACH ( b, &backend, b_next ) {
            event = evtimer_new( daemon_base, backend_connect, b );
            if ( !event ) {
                Debug( LDAP_DEBUG_ANY, "lloadd: "
                        "failed to allocate retry event\n" );
                return -1;
            }
            b->b_retry_event = event;
1335

1336
1337
            backend_retry( b );
        }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1338
1339
    }

1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
    event = evtimer_new( daemon_base, operations_timeout, event_self_cbarg() );
    if ( !event ) {
        Debug( LDAP_DEBUG_ANY, "lloadd: "
                "failed to allocate timeout event\n" );
        return -1;
    }
    lload_timeout_event = event;

    /* TODO: should we just add it with any timeout and re-add when the timeout
     * changes? */
    if ( lload_timeout_api ) {
        event_add( event, lload_timeout_api );
    }

Ondřej Kuzník's avatar
Ondřej Kuzník committed
1354
    lloadd_inited = 1;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1355
    rc = lload_base_dispatch( daemon_base );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
    Debug( LDAP_DEBUG_ANY, "lloadd shutdown: "
            "Main event loop finished: rc=%d\n",
            rc );

    /* shutdown */
    event_base_loopexit( listener_base, 0 );

    /* wait for the listener threads to complete */
    destroy_listeners();

1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
    /* TODO: Mark upstream connections closing */

    for ( i = 0; i < lload_daemon_threads; i++ ) {
        /*
         * https://github.com/libevent/libevent/issues/623
         * deleting the event doesn't notify the base, just activate it and
         * let it delete itself
         */
        event_active( lload_daemon[i].wakeup_event, EV_READ, 0 );
    }

Ondřej Kuzník's avatar
Ondřej Kuzník committed
1377
    for ( i = 0; i < lload_daemon_threads; i++ ) {
1378
        ldap_pvt_thread_join( daemon_tid[i], (void *)NULL );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1379
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1380

Ondřej Kuzník's avatar
Ondřej Kuzník committed
1381
#ifndef BALANCER_MODULE
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1382
1383
1384
1385
1386
1387
1388
    if ( LogTest( LDAP_DEBUG_ANY ) ) {
        int t = ldap_pvt_thread_pool_backload( &connection_pool );
        Debug( LDAP_DEBUG_ANY, "lloadd shutdown: "
                "waiting for %d operations/tasks to finish\n",
                t );
    }
    ldap_pvt_thread_pool_close( &connection_pool, 1 );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1389
1390
#endif

1391
    lload_backends_destroy();
1392
    clients_destroy();
1393
    lload_bindconf_free( &bindconf );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1394
    evdns_base_free( dnsbase, 0 );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1395
1396
1397
1398

    ch_free( daemon_tid );
    daemon_tid = NULL;

1399
    lloadd_daemon_destroy();
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1400

Ondřej Kuzník's avatar
Ondřej Kuzník committed
1401
1402
1403
1404
    /* If we're a slapd module, let the thread that initiated the shut down
     * know we've finished */
    ldap_pvt_thread_cond_signal( &lload_wait_cond );

Ondřej Kuzník's avatar
Ondřej Kuzník committed
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
    return 0;
}

static void
daemon_wakeup_cb( evutil_socket_t sig, short what, void *arg )
{
    int tid = (ldap_pvt_thread_t *)arg - daemon_tid;

    Debug( LDAP_DEBUG_TRACE, "daemon_wakeup_cb: "
            "Daemon thread %d woken up\n",
            tid );
1416
    event_del( lload_daemon[tid].wakeup_event );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1417
1418
}

1419
1420
LloadChange lload_change = { .type = LLOAD_UNDEFINED };

Ondřej Kuzník's avatar
Ondřej Kuzník committed
1421
#ifdef BALANCER_MODULE
1422
1423
1424
1425
1426
1427
int
backend_connect_cb( ldap_pvt_thread_start_t *start, void *startarg, void *arg )
{
    return arg == NULL || arg == startarg;
}

1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
int
backend_conn_cb( ldap_pvt_thread_start_t *start, void *startarg, void *arg )
{
    LloadConnection *c = startarg;
    LloadBackend *b = arg;

    if ( b == NULL || c->c_private == b ) {
        if ( start == upstream_bind ) {
            /* FIXME: is upstream_bind safe without a reference of its own? */
            CONNECTION_LOCK(c);
        } else {
            CONNECTION_LOCK_DECREF(c);
        }
        CONNECTION_DESTROY(c);
        return 1;
    }
    return 0;
}

int
client_tls_cb( ldap_pvt_thread_start_t *start, void *startarg, void *arg )
{
    LloadConnection *c = startarg;

    if ( c->c_destroy == client_destroy &&
            c->c_is_tls == LLOAD_TLS_ESTABLISHED ) {
        CONNECTION_LOCK_DESTROY(c);
        return 1;
    }
    return 0;
}

void
lload_handle_backend_invalidation( LloadChange *change )
{
    LloadBackend *b = change->target;

    assert( change->object == LLOAD_BACKEND );

    if ( change->type == LDAP_REQ_ADD ) {
1468
1469
1470
1471
1472
1473
1474
1475
1476
        BackendInfo *mi = backend_info( "monitor" );

        if ( mi ) {
            monitor_extra_t *mbe = mi->bi_extra;
            if ( mbe->is_configured() ) {
                lload_monitor_backend_init( mi, b );
            }
        }

1477
1478
1479
        if ( !current_backend ) {
            current_backend = b;
        }
1480
1481
1482
        backend_retry( b );
        return;
    } else if ( change->type == LDAP_REQ_DELETE ) {
1483
1484
1485
1486
1487
1488
1489
        ldap_pvt_thread_pool_walk(
                &connection_pool, handle_pdus, backend_conn_cb, b );
        ldap_pvt_thread_pool_walk(
                &connection_pool, upstream_bind, backend_conn_cb, b );
        /* Check there are no pending connection tasks either */
        ldap_pvt_thread_pool_walk(
                &connection_pool, backend_connect_task, backend_connect_cb, b );
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
        lload_backend_destroy( b );
        return;
    }
    assert( change->type == LDAP_REQ_MODIFY );
    assert( change->flags.generic != 0 );

    /*
     * A change that can't be handled gracefully, terminate all connections and
     * start over.
     */
    if ( change->flags.backend & LLOAD_BACKEND_MOD_OTHER ) {
        ldap_pvt_thread_pool_walk(
                &connection_pool, handle_pdus, backend_conn_cb, b );
        ldap_pvt_thread_pool_walk(
                &connection_pool, upstream_bind, backend_conn_cb, b );
        backend_reset( b );
        backend_retry( b );
        return;
    }

    /*
     * Handle changes to number of connections:
     * - a change might get the connection limit above the pool size:
     *   - consider closing (in order of priority?):
     *     - connections awaiting connect() completion
     *     - connections currently preparing
     *     - bind connections over limit (which is 0 if 'feature vc' is on
     *     - regular connections over limit
     * - below pool size
     *   - call backend_retry if there are no opening connections
     * - one pool size above and one below the configured size
     *   - still close the ones above limit, it should sort itself out
     *     the only issue is if a closing connection isn't guaranteed to do
     *     that at some point
     */
    if ( change->flags.backend & LLOAD_BACKEND_MOD_CONNS ) {
        int bind_requested = 0, need_close = 0, need_open = 0;
        LloadConnection *c;

        bind_requested =
#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
                (lload_features & LLOAD_FEATURE_VC) ? 0 :
#endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
                b->b_numbindconns;

        if ( b->b_bindavail > bind_requested ) {
            need_close += b->b_bindavail - bind_requested;
        } else if ( b->b_bindavail < bind_requested ) {
            need_open = 1;
        }

        if ( b->b_active > b->b_numconns ) {
            need_close += b->b_active - b->b_numconns;
        } else if ( b->b_active < b->b_numconns ) {
            need_open = 1;
        }

        if ( !need_open ) {
            need_close += b->b_opening;

            while ( !LDAP_LIST_EMPTY( &b->b_connecting ) ) {
                LloadPendingConnection *p = LDAP_LIST_FIRST( &b->b_connecting );

                LDAP_LIST_REMOVE( p, next );
                event_free( p->event );
                evutil_closesocket( p->fd );
                ch_free( p );
                b->b_opening--;
                need_close--;
            }
        }

        if ( need_close || !need_open ) {
            /* It might be too late to repurpose a preparing connection, just
             * close them all */
            while ( !LDAP_CIRCLEQ_EMPTY( &b->b_preparing ) ) {
                c = LDAP_CIRCLEQ_FIRST( &b->b_preparing );

                event_del( c->c_read_event );
                CONNECTION_LOCK_DESTROY(c);
                assert( c == NULL );
                b->b_opening--;
                need_close--;
            }
            event_del( b->b_retry_event );
            assert( b->b_opening == 0 );
        }

        if ( b->b_bindavail > bind_requested ) {
            int diff = b->b_bindavail - bind_requested;

            assert( need_close >= diff );

            LDAP_CIRCLEQ_FOREACH ( c, &b->b_bindconns, c_next ) {
                lload_connection_close( c );
                need_close--;
                diff--;
                if ( !diff ) {
                    break;
                }
            }
            assert( diff == 0 );
        }

        if ( b->b_active > b->b_numconns ) {
            int diff = b->b_active - b->b_numconns;

            assert( need_close >= diff );

            LDAP_CIRCLEQ_FOREACH ( c, &b->b_conns, c_next ) {
                lload_connection_close( c );
                need_close--;
                diff--;
                if ( !diff ) {
                    break;
                }
            }
            assert( diff == 0 );
        }
        assert( need_close == 0 );

        if ( need_open ) {
            backend_retry( b );
        }
    }
}

void
lload_handle_bindconf_invalidation( LloadChange *change )
{
    LloadBackend *b;
    LloadConnection *c;

    assert( change->type == LDAP_REQ_MODIFY );
    assert( change->object == LLOAD_BINDCONF );

1626
1627
1628
    change->flags.bindconf &= ~LLOAD_BINDCONF_MOD_TIMEOUTS;

    if ( !change->flags.bindconf ) {
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
        /* Nothing needs doing, things will generally fall into place */
        return;
    }

    /*
     * Only timeout changes can be handled gracefully, terminate all
     * connections and start over.
     */
    ldap_pvt_thread_pool_walk(
            &connection_pool, handle_pdus, backend_conn_cb, NULL );
    ldap_pvt_thread_pool_walk(
            &connection_pool, upstream_bind, backend_conn_cb, NULL );

    LDAP_CIRCLEQ_FOREACH ( b, &backend, b_next ) {
        backend_reset( b );
        backend_retry( b );
    }

1647
    /* Reconsider the PRIVILEGED flag on all clients */
1648
1649
1650
    LDAP_CIRCLEQ_FOREACH ( c, &clients, c_next ) {
        int privileged = ber_bvstrcasecmp( &c->c_auth, &lloadd_identity );

1651
1652
        /* We have just terminated all pending operations (even pins), there
         * should be no connections still binding/closing */
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
        assert( c->c_state == LLOAD_C_READY );

        c->c_type = privileged ? LLOAD_C_PRIVILEGED : LLOAD_C_OPEN;
    }
}

void
lload_handle_global_invalidation( LloadChange *change )
{
    assert( change->type == LDAP_REQ_MODIFY );
    assert( change->object == LLOAD_DAEMON );

    if ( change->flags.daemon & LLOAD_DAEMON_MOD_THREADS ) {
        /* walk the task queue to remove any tasks belonging to us. */
        /* TODO: initiate a full module restart, everything will fall into
         * place at that point */
        ldap_pvt_thread_pool_walk(
                &connection_pool, handle_pdus, backend_conn_cb, NULL );
        ldap_pvt_thread_pool_walk(
                &connection_pool, upstream_bind, backend_conn_cb, NULL );
        assert(0);
        return;
    }

    if ( change->flags.daemon & LLOAD_DAEMON_MOD_FEATURES ) {
1678