]> git.ipfire.org Git - thirdparty/openldap.git/commitdiff
lloadd ahoy
authorOndřej Kuzník <ondra@mistotebe.net>
Wed, 8 Mar 2017 22:59:57 +0000 (22:59 +0000)
committerOndřej Kuzník <okuznik@symas.com>
Tue, 17 Nov 2020 17:15:40 +0000 (17:15 +0000)
25 files changed:
.gitignore
build/top.mk
configure.ac
doc/devel/lloadd/design.md [new file with mode: 0644]
include/ldap_defaults.h
servers/Makefile.in
servers/lloadd/Makefile.in [new file with mode: 0644]
servers/lloadd/ch_malloc.c [new symlink]
servers/lloadd/client.c [new file with mode: 0644]
servers/lloadd/config.c [new file with mode: 0644]
servers/lloadd/config.h [new file with mode: 0644]
servers/lloadd/connection.c [new file with mode: 0644]
servers/lloadd/daemon.c [new file with mode: 0644]
servers/lloadd/design.md [new symlink]
servers/lloadd/globals.c [new symlink]
servers/lloadd/init.c [new file with mode: 0644]
servers/lloadd/libevent_support.c [new file with mode: 0644]
servers/lloadd/main.c [new file with mode: 0644]
servers/lloadd/nt_svc.c [new symlink]
servers/lloadd/proto-slap.h [new file with mode: 0644]
servers/lloadd/sl_malloc.c [new symlink]
servers/lloadd/slap.h [new file with mode: 0644]
servers/lloadd/user.c [new symlink]
servers/lloadd/value.c [new file with mode: 0644]
servers/slapd/bconfig.c

index 0f8012c05a264a852c447a7dce825ac170928f15..0f8b363cb45f90bfb17319371eba95ce5b6f537d 100644 (file)
@@ -96,6 +96,7 @@ servers/slapd/slapmodify
 servers/slapd/slappasswd
 servers/slapd/slapschema
 servers/slapd/slaptest
+servers/lloadd/lloadd
 tests/progs/ldif-filter
 tests/progs/slapd-addel
 tests/progs/slapd-bind
index 86fafca025aaeea863f98a2c6d6003b447a972f2..be03fdde7248891e1b054a5e5a5064d96a845b31 100644 (file)
@@ -164,6 +164,8 @@ LTHREAD_LIBS = @LTHREAD_LIBS@
 SLAPD_NDB_LIBS = @SLAPD_NDB_LIBS@
 WT_LIBS = @WT_LIBS@
 
+LEVENT_LIBS = @LEVENT_LIBS@
+
 LDAP_LIBLBER_LA = $(LDAP_LIBDIR)/liblber/liblber.la
 LDAP_LIBLDAP_LA = $(LDAP_LIBDIR)/libldap/libldap.la
 
@@ -175,6 +177,8 @@ LDAP_L = $(LDAP_LIBLUTIL_A) \
        $(LDAP_LIBLDAP_LA) $(LDAP_LIBLBER_LA)
 SLAPD_L = $(LDAP_LIBLUNICODE_A) $(LDAP_LIBREWRITE_A) \
        $(LDAP_LIBLUTIL_A) $(LDAP_LIBLDAP_LA) $(LDAP_LIBLBER_LA)
+LLOADD_L = $(LDAP_LIBLUTIL_A) $(LDAP_LIBLDAP_LA) \
+       $(LDAP_LIBLBER_LA)
 
 WRAP_LIBS = @WRAP_LIBS@
 # AutoConfig generated 
@@ -202,6 +206,7 @@ SLAPD_SQL_INCLUDES = @SLAPD_SQL_INCLUDES@
 SLAPD_SQL_LIBS = @SLAPD_SQL_LIBS@
 
 SLAPD_LIBS = @SLAPD_LIBS@ @SLAPD_PERL_LDFLAGS@ @SLAPD_SQL_LDFLAGS@ @SLAPD_SQL_LIBS@ @SLAPD_SLP_LIBS@ @SLAPD_GMP_LIBS@
+LLOADD_LIBS = @BALANCER_LIBS@ $(LEVENT_LIBS)
 
 # Our Defaults
 CC = $(AC_CC)
index 49fb967efb75cc76c721729266398ca33ca0e47a..65372c7de20e6bf2cd1d644a72b9dbedda52cca6 100644 (file)
@@ -410,6 +410,12 @@ OL_ARG_ENABLE(unique, [AS_HELP_STRING([--enable-unique], [Attribute Uniqueness o
 OL_ARG_ENABLE(valsort, [AS_HELP_STRING([--enable-valsort], [Value Sorting overlay])],
        no, [no yes mod], ol_enable_overlays)
 
+dnl ----------------------------------------------------------------
+dnl BALANCER OPTIONS
+AC_ARG_ENABLE(balanceroptions,[
+LLOADD (Load Balancer Daemon) Options:])
+OL_ARG_ENABLE(balancer,[  --enable-balancer      enable building load balancer], auto)dnl
+
 dnl ----------------------------------------------------------------
 AC_ARG_ENABLE(xxliboptions,[
 Library Generation & Linking Options])
@@ -481,6 +487,13 @@ if test $ol_enable_modules = yes ; then
        ol_enable_dynamic=yes
 fi
 
+if test $ol_enable_balancer = yes ; then
+       dnl Load Balancer was specifically enabled
+       if test $ol_with_threads = no ; then
+               AC_MSG_ERROR([Load balancer requires threads])
+       fi
+fi
+
 if test $ol_enable_spasswd = yes ; then
        if test $ol_with_cyrus_sasl = no ; then
                AC_MSG_ERROR([--enable-spasswd requires --with-cyrus-sasl])
@@ -504,13 +517,16 @@ LDAP_LIBS=
 SLAPD_NDB_LIBS=
 SLAPD_NDB_INCS=
 LTHREAD_LIBS=
+LEVENT_LIBS=
 LUTIL_LIBS=
 
 CLIENT_LIBS=
 
 SLAPD_LIBS=
+BALANCER_LIBS=
 
 BUILD_SLAPD=no
+BUILD_BALANCER=no
 
 BUILD_THREAD=no
 
@@ -2128,6 +2144,24 @@ if test $ol_enable_slp != no ; then
        fi
 fi
 
+dnl ----------------------------------------------------------------
+dnl Libevent
+if test $ol_enable_balancer != no ; then
+       AC_CHECK_LIB(event_core, evconnlistener_set_error_cb,
+               [have_libevent=yes
+               LEVENT_LIBS="$LEVENT_LIBS -levent_core"],
+               [AC_CHECK_LIB(event, evconnlistener_set_error_cb,
+                       [have_libevent=yes
+                       LEVENT_LIBS="$LEVENT_LIBS -levent"],
+                       [have_libevent=no])])
+
+       if test $have_libevent = yes ; then
+               AC_DEFINE(HAVE_LIBEVENT, 1, [define if you have -levent])
+       elif test $ol_enable_balancer = yes ; then
+               AC_MSG_ERROR([You need libevent 2.0 or later to build the load balancer])
+       fi
+fi
+
 dnl ----------------------------------------------------------------
 dnl Checks for typedefs, structures, and compiler characteristics.
 
@@ -2930,6 +2964,12 @@ if test "$ol_enable_valsort" != no ; then
        AC_DEFINE_UNQUOTED(SLAPD_OVER_VALSORT,$MFLAG,[define for Value Sorting overlay])
 fi
 
+if test "$ol_enable_balancer" != no \
+               -a "$ol_with_threads" != no \
+               -a "$have_libevent" = yes ; then
+       BUILD_BALANCER=yes
+fi
+
 if test "$ol_enable_slapi" != no ; then
        AC_DEFINE(ENABLE_SLAPI,1,[define to enable slapi library])
        BUILD_SLAPI=yes
@@ -3002,14 +3042,17 @@ dnl overlays
   AC_SUBST(BUILD_TRANSLUCENT)
   AC_SUBST(BUILD_UNIQUE)
   AC_SUBST(BUILD_VALSORT)
+  AC_SUBST(BUILD_BALANCER)
 
 AC_SUBST(LDAP_LIBS)
 AC_SUBST(CLIENT_LIBS)
 AC_SUBST(SLAPD_LIBS)
+AC_SUBST(BALANCER_LIBS)
 AC_SUBST(SLAPD_NDB_LIBS)
 AC_SUBST(SLAPD_NDB_INCS)
 AC_SUBST(LTHREAD_LIBS)
 AC_SUBST(LUTIL_LIBS)
+AC_SUBST(LEVENT_LIBS)
 AC_SUBST(WRAP_LIBS)
 
 AC_SUBST(SLAPD_MODULES_CPPFLAGS)
@@ -3091,6 +3134,7 @@ AC_CONFIG_FILES([Makefile:build/top.mk:Makefile.in:build/dir.mk]
 [servers/slapd/shell-backends/Makefile:build/top.mk:servers/slapd/shell-backends/Makefile.in:build/srv.mk]
 [servers/slapd/slapi/Makefile:build/top.mk:servers/slapd/slapi/Makefile.in:build/lib.mk:build/lib-shared.mk]
 [servers/slapd/overlays/Makefile:build/top.mk:servers/slapd/overlays/Makefile.in:build/lib.mk]
+[servers/lloadd/Makefile:build/top.mk:servers/lloadd/Makefile.in:build/srv.mk]
 [tests/Makefile:build/top.mk:tests/Makefile.in:build/dir.mk]
 [tests/run]
 [tests/progs/Makefile:build/top.mk:tests/progs/Makefile.in:build/rules.mk])
diff --git a/doc/devel/lloadd/design.md b/doc/devel/lloadd/design.md
new file mode 100644 (file)
index 0000000..62fcd88
--- /dev/null
@@ -0,0 +1,282 @@
+TODO:
+- [ ] keep a global op in-flight counter? (might need locking)
+- [-] scheduling (who does what, more than one select thread? How does the proxy
+      work get distributed between threads?)
+- [ ] managing timeouts?
+- [X] outline locking policy: seems like there might be a lock inversion in the
+      design looming: when working with op, might need a lock on both client and
+      upstream but depending on where we started, we might want to start with
+      locking one, then other
+- [ ] how to deal with the balancer running out of fds? Especially when we hit
+      the limit, then lose an upstream connection and accept() a client, we
+      wouldn't be able to initiate a new one. A bit of a DoS... But probably not
+      a concern for Ericsson
+- [ ] non-Linux? No idea how anything other than poll works (moot if building a
+      libevent/libuv-based load balancer since they take care of that, except
+      edge-triggered I/O?)
+- [-] rootDSE? Controls and exops might have different semantics and need
+      binding to the same upstream connection.
+- [ ] Just piggybacking on OpenLDAP as a module? Would still need some updates
+      in the core and the module/subsystem would be a very invasive one. On the
+      other hand, allows to expose live configuration and monitoring over LDAP
+      over the current slapd listeners without re-inventing the wheel.
+
+
+Expecting to handle only LDAPv3
+
+terms:
+  server - configured target
+  upstream - a single connection to a server
+  client - an incoming connection
+
+To maintain fairness `G( requested => ( F( progressed | failed ) ) )`, use
+queues and put timeouts in
+
+Runtime organisation
+------
+- main thread with its own event base handling signals
+- one thread (later possibly more) listening on the rendezvous sockets, handing
+  the new sockets to worker threads
+- n worker threads dealing with client and server I/O (dispatching actual work
+  to the thread pool most likely)
+- a thread pool to handle actual work
+
+Operational behaviour
+------
+
+- client read -> upstream write:
+  - client read:
+    - if TLS_SETUP, keep processing, set state back when finished and note that
+      we're under TLS
+    - ber_get_next(), if we don't have a tag, finished (unless we have true
+      edge-triggered I/O, also put the fd back into the ones we're waiting for)
+    - peek at op tag:
+      - unbind:
+        - with a single lock, mark all pending ops in upstreams abandoned, clear
+          client link (would it be fast enough if we remove them from upstream
+          map instead?)
+        - locked per op:
+          - remove op from upstream map
+          - check upstream is not write-suspended, if it is ...
+          - try to write the abandon op to upstream, suspend upstream if not
+            fully sent
+          - remove op from client map (how if we're in avl_apply?, another pass?)
+        - would be nice if we could wipe the complete client map then, otherwise
+          we need to queue it to have it freed when all abandons get passed onto
+          the upstream (just dropping them might put extra strain on upstreams,
+          will probably have a queue on each client/upstream anyway, not just a
+          single Ber)
+      - bind:
+        - check mechanism is not EXTERNAL (or implement it)
+        - abandon existing ops (see unbind)
+        - set state to BINDING, put DN into authzid
+        - pick upstream, create PDU and sent
+      - abandon:
+        - find op, mark for abandon, send to appropriate upstream
+      - Exop:
+        - check not BINDING (unless it's a cancel?)
+        - check OID:
+          - STARTTLS:
+            - check we don't have TLS yet
+            - abandon all
+            - set state to TLS_SETUP
+            - send the hello
+          - VC(?):
+            - similar to bind except for the abandons/state change
+      - other:
+        - check not BINDING
+        - pick an upstream
+        - create a PDU, send (marking upstream suspended if not written in full)
+      - check if should read again (keep a counter of number of times to read
+        off a connection in a single pass so that we maintain fairness)
+      - if read enough requests and can still read, re-queue ourselves (if we
+        don't have true edge-triggered I/O, we can just register the fd again)
+  - upstream write (only when suspended):
+    - flush the current BER
+    - there shouldn't be anything else?
+- upstream read -> client write:
+  - upstream read:
+    - ber_get_next(), if we don't have a tag, finished (unless we have true
+      edge-triggered I/O, also put the fd back into the ones we're waiting for)
+    - when we get it, peek at msgid, resolve client connection, lock, check:
+      - if unsolicited, handle as close (and mark connection closing)
+      - if op is abandoned or does not exist, drop PDU and op, update counters
+      - if client backlogged, suspend upstream, register callback to unsuspend
+        (on progress when writing to client or abandon from client (connection
+        death, abandon proper, ...))
+    - reconstruct final PDU, write BER to client, if did not write fully,
+      suspend client
+    - if a final response, decrement operation counts on upstream and client
+    - check if should read again (keep a counter of number of responses to read
+      off a connection in a single pass so that we don't starve any?)
+  - client write ready (only checked for when suspended):
+    - write the rest of pending BER if any
+    - on successful write, pick all pending ops that need failure response, push
+      to client (are there any controls that need to be present in response even
+      in the case of failure?, what to do with them?)
+    - on successfully flushing them, walk through suspended upstreams, picking
+      the pending PDU (unsuspending the upstream) and writing, if PDU flushed
+      successfully, pick next upstream
+    - if we successfully flushed all suspended upstreams, unsuspend client
+      (and disable the write callback)
+- upstream close/error:
+  - look up pending ops, try to write to clients, mark clients suspended that
+    have ops that need responses (another queue associated with client to speed
+    up?)
+  - schedule a new connection open
+- client close/error:
+  - same as unbind
+- client inactive (no pending ops and nothing happened in x seconds)
+  - might just send notice of disconnection and close
+- op timeout handling:
+  - mark for abandon
+  - send abandon
+  - send timeLimitExceeded/adminLimitExceeded to client
+
+Picking an upstream:
+- while there is a level available:
+  - pick a random ordering of upstreams based on weights
+  - while there is an upstream in the level:
+    - check number of ops in-flight (this is where we lock the upstream map)
+    - find the least busy connection (and check if a new connection should be
+      opened)
+    - try to lock for socket write, if available (no BER queued) we have our
+      upstream
+
+PDU processing:
+- request (have an upstream selected):
+  - get new msgid from upstream
+  - create an Op structure (actually, with the need for freelist lock, we can
+    make it a cache for freed operation structures, avoiding some malloc
+    traffic, to reset, we need slap_sl_mem_create( ,,, 1 ))
+  - check proxyauthz is not present? or just let upstream reject it if there are
+    two?
+  - add own controls at the end:
+    - construct proxyauthz from authzid
+    - construct session tracking from remote IP, own name, authzid
+  - send over
+  - insert Op into client and upstream maps
+- response/intermediate/entry:
+  - look up Op in upstream's map
+  - write old msgid, rest of the response can go unchanged
+  - if a response, remove Op from all maps (client and upstream)
+
+Managing upstreams:
+- async connect up to min_connections (is there a point in having a connection
+  count range if we can't use it when needed since all of the below is async?)
+- when connected, set up TLS (if requested)
+- when done, send a bind
+- go for the bind interaction
+- when done, add it to the upstream's connection list
+- (if a connection is suspended or connections are over 75 % op limit, schedule
+  creating a new connection setup unless connection limit has been hit)
+
+Managing timeouts:
+- two options:
+  - maintain a separate locked priority queue to give a perfect ordering to when
+    each operation is to time out, would need to maintain yet another place
+    where operations can be found.
+    - the locking protocol for disposing of the operation would need to be
+      adjusted and might become even more complicated, might do the alternative
+      initially and then attempt this if it helps performance
+  - just do a sweep over all clients (that mutex is less contended) every so
+    often. With many in-flight operations might be a lot of wasted work.
+    - we still need to sweep over all clients to check if they should be killed
+      anyway
+
+Dispatcher thread (2^n of them, fd x is handled by thread no x % (2^n)):
+- poll on all registered fds
+- remove each fd that's ready from the registered list and schedule the work
+- work threads can put their fd back in if they deem necessary (=not suspended)
+- this works as a poor man's edge-triggered polling, with enough workers, should
+  we do proper edge triggered I/O? What about non-Linux?
+
+Listener thread:
+- slapd has just one, which then reassigns the sockets to separate I/O
+  threads
+
+Threading:
+- if using slap_sl_malloc, how much perf do we gain? To allocate a context per
+  op, we should have a dedicated parent context so that when we free it, we can
+  use that exclusively. The parent context's parent would be the main thread's
+  context. This implies a lot of slap_sl_mem_setctx/slap_sl_mem_create( ,,, 0 )
+  and making sure an op does not allocate/free things from two threads at the
+  same time (might need an Op mutex after all? Not such a huge cost if we
+  routinely reuse Op structures)
+
+Locking policy:
+- read mutexes are unnecessary, we only have one thread receiving data from the
+  connection - the one started from the dispatcher
+- two reference counters of operation structures (an op is accessible from
+  client and upstream map, each counter is consistent when thread has a lock on
+  corresponding map), when decreasing the counter to zero, start freeing
+  procedure
+- place to mark disposal finished for each side, consistency enforced by holding
+  the freelist lock when reading/manipulating
+- when op is created, we already have a write lock on upstream socket and map,
+  start writing, insert to upstream map with upstream refcount 1, unlock, lock
+  client, insert (client refcount 0), unlock, lock upstream, decrement refcount
+  (triggers a test if we need to drop it now), unlock upstream, done
+- when upstream processes a PDU, locks its map, increments counter, (potentially
+  removes if it's a response), unlocks, locks client's map, write mutex (this
+  order?) and full client mutex (if a bind response)
+- when client side wants to work with a PDU (abandon, (un)bind), locks its map,
+  increase refcount, unlocks, locks upstream map, write mutex, sends or queues
+  abandon, unlocks write mutex, initiates freeing procedure from upstream side
+  (or if having to remember we've already increased client-side refcount, mark
+  for deletion, lose upstream lock, lock client, decref, either triggering
+  deletion from client or mark for it)
+- if we have operation lock, we can simplify a bit (no need for three-stage
+  locking above)
+
+Shutdown:
+- stop accept() thread(s) - potentially add a channel to hand these listening
+  sockets over for zero-downtime restart
+- if very gentle, mark connections as closing, start timeout and:
+  - when a new non-abandon PDU comes in from client - return LDAP_UNAVAILABLE
+  - when receiving a PDU from upstream, send over to client, if no ops pending,
+    send unsolicited response and close (RFC4511 suggests unsolicited response
+    is the last PDU coming from the upstream and libldap agrees, so we can't
+    send it for a socket we want to shut down more gracefully)
+- gentle (or very gentle timed out):
+  - set timeout
+  - mark all ops as abandoned
+  - send unbind to all upstreams
+  - send unsolicited to all clients
+- imminent (or gentle timed out):
+  - async close all connections?
+  - exit()
+
+RootDSE:
+- default option is not to care and if a control/exop has special restrictions,
+  it is the admin's job to flag it as such in the load-balancer's config
+- another is not to care about the search request but check each search entry
+  being passed back, check DN and if it's a rootDSE, filter the list of
+  controls/exops/sasl mechs (external!) that are supported
+- last one is to check all search requests for the DN/scope and synthesise the
+  response locally - probably not (would need to configure the complete list of
+  controls, exops, sasl mechs, naming contexts in the balancer)
+
+Potential red flags:
+- we suspend upstreams, if we ever suspend clients we need to be sure we can't
+  create dependency cycles
+  - is this an issue when only suspending the read side of each? Because even if
+    we stop reading from everything, we should eventually flush data to those we
+    can still talk to, as upstreams are flushed, we can start sending new
+    requests from live clients (those that are suspended are due to their own
+    inability to accept data)
+  - we might need to suspend a client if there is a reason to choose a
+    particular upstream (multi-request operation - bind, VC, PR, TXN, ...)
+    - a SASL bind, but that means there are no outstanding ops to receive
+      it holds that !suspended(client) \or !suspended(upstream), so they
+      cannot participate in a cycle
+    - VC - multiple binds at the same time - !!! more analysis needed
+    - PR - should only be able to have one per connection (that's a problem
+      for later, maybe even needs a dedicated upstream connection)
+    - TXN - ??? probably same situation as PR
+  - or if we have a queue for pending Bers on the server, we not need to suspend
+    clients, upstream is only chosen if the queue is free or there is a reason
+    to send it to that particular upstream (multi-stage bind/VC, PR, ...), but
+    that still makes it possible for a client to exhaust all our memory by
+    sending requests (VC or other ones bound to a slow upstream or by not
+    reading the responses at all)
index 9468049a1c0693bc8bb018e7123711b591fb6150..6d7f0f16c0761537b8084bd6ccae6732753ecd06 100644 (file)
@@ -63,4 +63,9 @@
        /* dn of the default "monitor" subentry */
 #define SLAPD_MONITOR_DN               "cn=Monitor"
 
+/*
+ * LLOADD DEFINITIONS
+ */
+#define LLOADD_DEFAULT_CONFIGFILE      LDAP_SYSCONFDIR LDAP_DIRSEP "lloadd.conf"
+
 #endif /* _LDAP_CONFIG_H */
index 4fbb1ad1ea5e001b9be8835e751440174433d3c9..95ef69c4d0af08535154cc8fd6a6aa75e79a3653 100644 (file)
@@ -13,5 +13,5 @@
 ## top-level directory of the distribution or, alternatively, at
 ## <http://www.OpenLDAP.org/license.html>.
 
-SUBDIRS= slapd
+SUBDIRS= slapd lloadd
 
diff --git a/servers/lloadd/Makefile.in b/servers/lloadd/Makefile.in
new file mode 100644 (file)
index 0000000..768d3e7
--- /dev/null
@@ -0,0 +1,74 @@
+# Makefile.in for Load Balancer
+# $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>.
+
+PROGRAMS = lloadd
+XPROGRAMS = slloadd
+
+XSRCS  = version.c
+
+NT_SRCS = nt_svc.c
+NT_OBJS = nt_svc.o ../../libraries/liblutil/slapdmsg.res
+
+SRCS   = main.c globals.c config.c connection.c client.c daemon.c \
+               ch_malloc.c init.c user.c sl_malloc.c value.c \
+               libevent_support.c \
+               $(@PLAT@_SRCS)
+
+OBJS   = $(patsubst %.c,%.o,$(SRCS)) $(@PLAT@_OBJS)
+
+LDAP_INCDIR= ../../include -I$(srcdir)
+LDAP_LIBDIR= ../../libraries
+
+BUILD_OPT = "--enable-balancer"
+BUILD_SRV = @BUILD_BALANCER@
+
+all-local-srv:         $(PROGRAMS)
+
+# $(LTHREAD_LIBS) must be last!
+XLIBS = $(LLOADD_L)
+XXLIBS = $(LLOADD_LIBS) $(SECURITY_LIBS) $(LUTIL_LIBS)
+XXXLIBS = $(LTHREAD_LIBS)
+
+NT_DEPENDS = slapd.exp
+NT_OBJECTS = slapd.exp symdummy.o $(LLOADD_OBJS) version.o
+
+UNIX_DEPENDS = version.o $(LLOADD_L)
+UNIX_OBJECTS = $(OBJS) version.o
+
+LLOADD_DEPENDS = $(@PLAT@_DEPENDS)
+LLOADD_OBJECTS = $(@PLAT@_OBJECTS)
+
+lloadd:        $(LLOADD_DEPENDS) version.o
+       $(LTLINK) -o $@ $(OBJS) version.o $(LIBS)
+
+slloadd:       version.o
+       $(LTLINK) -static -o $@ $(OBJS) version.o $(LIBS)
+
+version.c: Makefile
+       @-$(RM) $@
+       $(MKVERSION) -s -n Versionstr lloadd > $@
+
+version.o: version.c $(OBJS) $(LLOADD_L)
+
+install-local-srv: FORCE
+       -$(MKDIR) $(DESTDIR)$(libexecdir)
+       @-$(INSTALL) -m 700 -d $(DESTDIR)$(localstatedir)/openldap-lloadd
+       @(                                                              \
+           for prg in $(PROGRAMS); do                                  \
+               $(LTINSTALL) $(INSTALLFLAGS) $(STRIP) -m 755 $$prg$(EXEEXT)     \
+                   $(DESTDIR)$(libexecdir);                            \
+           done                                                        \
+       )
+
diff --git a/servers/lloadd/ch_malloc.c b/servers/lloadd/ch_malloc.c
new file mode 120000 (symlink)
index 0000000..6b13fb5
--- /dev/null
@@ -0,0 +1 @@
+../slapd/ch_malloc.c
\ No newline at end of file
diff --git a/servers/lloadd/client.c b/servers/lloadd/client.c
new file mode 100644 (file)
index 0000000..1392689
--- /dev/null
@@ -0,0 +1,103 @@
+/* $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 "lutil.h"
+#include "slap.h"
+
+static void client_destroy( Connection *c );
+
+static void
+client_read_cb( evutil_socket_t s, short what, void *arg )
+{
+    Connection *c = arg;
+    Debug( LDAP_DEBUG_CONNS, "client_read_cb: "
+            "connection %lu ready to read\n",
+            c->c_connid );
+    evutil_closesocket( s );
+    client_destroy( c );
+}
+
+static void
+client_write_cb( evutil_socket_t s, short what, void *arg )
+{
+    Connection *c = arg;
+}
+
+Connection *
+client_init(
+        ber_socket_t s,
+        Listener *listener,
+        const char *peername,
+        struct event_base *base,
+        int flags )
+{
+    Connection *c;
+    struct event *event;
+
+    assert( listener != NULL );
+
+    c = connection_init( s, peername, flags );
+
+    event = event_new( base, s, EV_READ|EV_PERSIST, client_read_cb, c );
+    if ( !event ) {
+        Debug( LDAP_DEBUG_ANY, "Read event could not be allocated\n" );
+        goto fail;
+    }
+    event_add( event, NULL );
+    c->c_read_event = event;
+
+    event = event_new( base, s, EV_WRITE, client_write_cb, c );
+    if ( !event ) {
+        Debug( LDAP_DEBUG_ANY, "Write event could not be allocated\n" );
+        goto fail;
+    }
+    /* We only register the write event when we have data pending */
+    c->c_write_event = event;
+
+    c->c_private = listener;
+
+    return c;
+fail:
+    if ( c->c_write_event ) {
+        event_del( c->c_write_event );
+        event_free( c->c_write_event );
+    }
+    if ( c->c_read_event ) {
+        event_del( c->c_read_event );
+        event_free( c->c_read_event );
+    }
+    connection_destroy( c );
+    return NULL;
+}
+
+static void
+client_destroy( Connection *c )
+{
+    event_del( c->c_read_event );
+    event_free( c->c_read_event );
+
+    event_del( c->c_write_event );
+    event_free( c->c_write_event );
+
+    connection_destroy( c );
+}
diff --git a/servers/lloadd/config.c b/servers/lloadd/config.c
new file mode 100644 (file)
index 0000000..0c64b3f
--- /dev/null
@@ -0,0 +1,2374 @@
+/* config.c - configuration file handling routines */
+/* $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>.
+ */
+/* 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/string.h>
+#include <ac/ctype.h>
+#include <ac/signal.h>
+#include <ac/socket.h>
+#include <ac/errno.h>
+#include <ac/unistd.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifndef S_ISREG
+#define S_ISREG(m) ( ((m) & _S_IFMT ) == _S_IFREG )
+#endif
+
+#include "slap.h"
+#include "lutil.h"
+#include "lutil_ldap.h"
+#include "config.h"
+
+#ifdef _WIN32
+#define LUTIL_ATOULX lutil_atoullx
+#define Z "I"
+#else
+#define LUTIL_ATOULX lutil_atoulx
+#define Z "z"
+#endif
+
+#define ARGS_STEP 512
+
+/*
+ * defaults for various global variables
+ */
+slap_mask_t global_allows = 0;
+slap_mask_t global_disallows = 0;
+int global_gentlehup = 0;
+int global_idletimeout = 0;
+char *global_host = NULL;
+
+static FILE *logfile;
+static char *logfileName;
+
+ber_len_t sockbuf_max_incoming = SLAP_SB_MAX_INCOMING_DEFAULT;
+ber_len_t sockbuf_max_incoming_auth = SLAP_SB_MAX_INCOMING_AUTH;
+
+char *slapd_pid_file = NULL;
+char *slapd_args_file = NULL;
+
+static int fp_getline( FILE *fp, ConfigArgs *c );
+static void fp_getline_init( ConfigArgs *c );
+
+static char *strtok_quote(
+        char *line,
+        char *sep,
+        char **quote_ptr,
+        int *inquote );
+
+typedef struct ConfigFile {
+    struct ConfigFile *c_sibs;
+    struct ConfigFile *c_kids;
+    struct berval c_file;
+    BerVarray c_dseFiles;
+} ConfigFile;
+
+static ConfigFile *cfn;
+
+static ConfigDriver config_fname;
+static ConfigDriver config_generic;
+#ifdef LDAP_TCP_BUFFER
+static ConfigDriver config_tcp_buffer;
+#endif /* LDAP_TCP_BUFFER */
+static ConfigDriver config_restrict;
+static ConfigDriver config_loglevel;
+static ConfigDriver config_include;
+#ifdef HAVE_TLS
+static ConfigDriver config_tls_option;
+static ConfigDriver config_tls_config;
+#endif
+
+enum {
+    CFG_ACL = 1,
+    CFG_BACKEND,
+    CFG_DATABASE,
+    CFG_TLS_RAND,
+    CFG_TLS_CIPHER,
+    CFG_TLS_PROTOCOL_MIN,
+    CFG_TLS_CERT_FILE,
+    CFG_TLS_CERT_KEY,
+    CFG_TLS_CA_PATH,
+    CFG_TLS_CA_FILE,
+    CFG_TLS_DH_FILE,
+    CFG_TLS_VERIFY,
+    CFG_TLS_CRLCHECK,
+    CFG_TLS_CRL_FILE,
+    CFG_CONCUR,
+    CFG_THREADS,
+    CFG_LOGFILE,
+    CFG_MIRRORMODE,
+    CFG_LTHREADS,
+    CFG_THREADQS,
+    CFG_TLS_ECNAME,
+    CFG_TLS_CACERT,
+    CFG_TLS_CERT,
+    CFG_TLS_KEY,
+
+    CFG_LAST
+};
+
+/* alphabetical ordering */
+
+static ConfigTable config_back_cf_table[] = {
+    /* This attr is read-only */
+    { "", "", 0, 0, 0,
+        ARG_MAGIC,
+        &config_fname,
+    },
+    { "argsfile", "file", 2, 2, 0,
+        ARG_STRING,
+        &slapd_args_file,
+    },
+    { "concurrency", "level", 2, 2, 0,
+        ARG_INT|ARG_MAGIC|CFG_CONCUR,
+        &config_generic,
+    },
+    { "database", "type", 2, 2, 0,
+        ARG_MAGIC|CFG_DATABASE,
+        &config_generic,
+    },
+    { "gentlehup", "on|off", 2, 2, 0,
+#ifdef SIGHUP
+        ARG_ON_OFF,
+        &global_gentlehup,
+#else
+        ARG_IGNORED,
+        NULL,
+#endif
+    },
+    { "idletimeout", "timeout", 2, 2, 0,
+        ARG_INT,
+        &global_idletimeout,
+    },
+    { "include", "file", 2, 2, 0,
+        ARG_MAGIC,
+        &config_include,
+    },
+    { "listener-threads", "count", 2, 0, 0,
+        ARG_UINT|ARG_MAGIC|CFG_LTHREADS,
+        &config_generic,
+    },
+    { "logfile", "file", 2, 2, 0,
+        ARG_STRING|ARG_MAGIC|CFG_LOGFILE,
+        &config_generic,
+    },
+    { "loglevel", "level", 2, 0, 0,
+        ARG_MAGIC,
+        &config_loglevel,
+    },
+    { "pidfile", "file", 2, 2, 0,
+        ARG_STRING,
+        &slapd_pid_file,
+    },
+    { "restrict", "op_list", 2, 0, 0,
+        ARG_MAGIC,
+        &config_restrict,
+    },
+    { "sockbuf_max_incoming", "max", 2, 2, 0,
+        ARG_BER_LEN_T,
+        &sockbuf_max_incoming,
+    },
+    { "sockbuf_max_incoming_auth", "max", 2, 2, 0,
+        ARG_BER_LEN_T,
+        &sockbuf_max_incoming_auth,
+    },
+    { "tcp-buffer", "[listener=<listener>] [{read|write}=]size", 0, 0, 0,
+#ifdef LDAP_TCP_BUFFER
+        ARG_MAGIC,
+        &config_tcp_buffer,
+#else
+        ARG_IGNORED,
+        NULL,
+#endif
+    },
+    { "threads", "count", 2, 2, 0,
+        ARG_INT|ARG_MAGIC|CFG_THREADS,
+        &config_generic,
+    },
+    { "threadqueues", "count", 2, 2, 0,
+        ARG_INT|ARG_MAGIC|CFG_THREADQS,
+        &config_generic,
+    },
+    { "TLSCACertificate", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+        CFG_TLS_CACERT|ARG_BINARY|ARG_MAGIC,
+        &config_tls_option,
+#else
+        ARG_IGNORED,
+        NULL,
+#endif
+    },
+    { "TLSCACertificateFile", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+        CFG_TLS_CA_FILE|ARG_STRING|ARG_MAGIC,
+        &config_tls_option,
+#else
+        ARG_IGNORED,
+        NULL,
+#endif
+    },
+    { "TLSCACertificatePath", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+        CFG_TLS_CA_PATH|ARG_STRING|ARG_MAGIC,
+        &config_tls_option,
+#else
+        ARG_IGNORED,
+        NULL,
+#endif
+    },
+    { "TLSCertificate", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+        CFG_TLS_CERT|ARG_BINARY|ARG_MAGIC,
+        &config_tls_option,
+#else
+        ARG_IGNORED,
+        NULL,
+#endif
+    },
+    { "TLSCertificateFile", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+        CFG_TLS_CERT_FILE|ARG_STRING|ARG_MAGIC,
+        &config_tls_option,
+#else
+        ARG_IGNORED,
+        NULL,
+#endif
+    },
+    { "TLSCertificateKey", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+        CFG_TLS_KEY|ARG_BINARY|ARG_MAGIC,
+        &config_tls_option,
+#else
+        ARG_IGNORED,
+        NULL,
+#endif
+    },
+    { "TLSCertificateKeyFile", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+        CFG_TLS_CERT_KEY|ARG_STRING|ARG_MAGIC,
+        &config_tls_option,
+#else
+        ARG_IGNORED,
+        NULL,
+#endif
+    },
+    { "TLSCipherSuite", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+        CFG_TLS_CIPHER|ARG_STRING|ARG_MAGIC,
+        &config_tls_option,
+#else
+        ARG_IGNORED,
+        NULL,
+#endif
+    },
+    { "TLSCRLCheck", NULL, 2, 2, 0,
+#if defined(HAVE_TLS) && defined(HAVE_OPENSSL)
+        CFG_TLS_CRLCHECK|ARG_STRING|ARG_MAGIC,
+        &config_tls_config,
+#else
+        ARG_IGNORED,
+        NULL,
+#endif
+    },
+    { "TLSCRLFile", NULL, 2, 2, 0,
+#if defined(HAVE_GNUTLS)
+        CFG_TLS_CRL_FILE|ARG_STRING|ARG_MAGIC,
+        &config_tls_option,
+#else
+        ARG_IGNORED,
+        NULL,
+#endif
+    },
+    { "TLSRandFile", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+        CFG_TLS_RAND|ARG_STRING|ARG_MAGIC,
+        &config_tls_option,
+#else
+        ARG_IGNORED,
+        NULL,
+#endif
+    },
+    { "TLSVerifyClient", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+        CFG_TLS_VERIFY|ARG_STRING|ARG_MAGIC,
+        &config_tls_config,
+#else
+        ARG_IGNORED,
+        NULL,
+#endif
+    },
+    { "TLSDHParamFile", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+        CFG_TLS_DH_FILE|ARG_STRING|ARG_MAGIC,
+        &config_tls_option,
+#else
+        ARG_IGNORED,
+        NULL,
+#endif
+    },
+    { "TLSECName", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+        CFG_TLS_ECNAME|ARG_STRING|ARG_MAGIC,
+        &config_tls_option,
+#else
+        ARG_IGNORED,
+        NULL,
+#endif
+    },
+    { "TLSProtocolMin", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+        CFG_TLS_PROTOCOL_MIN|ARG_STRING|ARG_MAGIC,
+        &config_tls_config,
+#else
+        ARG_IGNORED,
+        NULL,
+#endif
+    },
+
+    { NULL, NULL, 0, 0, 0, ARG_IGNORED, NULL }
+};
+
+static int
+config_generic( ConfigArgs *c )
+{
+    switch ( c->type ) {
+        case CFG_CONCUR:
+            ldap_pvt_thread_set_concurrency( c->value_int );
+            break;
+
+        case CFG_THREADS:
+            if ( c->value_int < 2 ) {
+                snprintf( c->cr_msg, sizeof(c->cr_msg),
+                        "threads=%d smaller than minimum value 2",
+                        c->value_int );
+                Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                return 1;
+
+            } else if ( c->value_int > 2 * SLAP_MAX_WORKER_THREADS ) {
+                snprintf( c->cr_msg, sizeof(c->cr_msg),
+                        "warning, threads=%d larger than twice the default "
+                        "(2*%d=%d); YMMV",
+                        c->value_int, SLAP_MAX_WORKER_THREADS,
+                        2 * SLAP_MAX_WORKER_THREADS );
+                Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+            }
+            if ( slapMode & SLAP_SERVER_MODE )
+                ldap_pvt_thread_pool_maxthreads(
+                        &connection_pool, c->value_int );
+            connection_pool_max = c->value_int; /* save for reference */
+            break;
+
+        case CFG_THREADQS:
+            if ( c->value_int < 1 ) {
+                snprintf( c->cr_msg, sizeof(c->cr_msg),
+                        "threadqueues=%d smaller than minimum value 1",
+                        c->value_int );
+                Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+                return 1;
+            }
+            if ( slapMode & SLAP_SERVER_MODE )
+                ldap_pvt_thread_pool_queues( &connection_pool, c->value_int );
+            connection_pool_queues = c->value_int; /* save for reference */
+            break;
+
+        case CFG_LTHREADS: {
+            int mask = 0;
+            /* use a power of two */
+            while ( c->value_uint > 1 ) {
+                c->value_uint >>= 1;
+                mask <<= 1;
+                mask |= 1;
+            }
+            slapd_daemon_mask = mask;
+            slapd_daemon_threads = mask + 1;
+        } break;
+
+        case CFG_LOGFILE: {
+            if ( logfileName ) ch_free( logfileName );
+            logfileName = c->value_string;
+            logfile = fopen( logfileName, "w" );
+            if ( logfile ) lutil_debug_file( logfile );
+        } break;
+
+        default:
+            Debug( LDAP_DEBUG_ANY, "%s: unknown CFG_TYPE %d.\n",
+                    c->log, c->type );
+            return 1;
+    }
+    return 0;
+}
+
+static int
+config_fname( ConfigArgs *c )
+{
+    return 0;
+}
+
+/*
+ * [listener=<listener>] [{read|write}=]<size>
+ */
+
+#ifdef LDAP_TCP_BUFFER
+static BerVarray tcp_buffer;
+int tcp_buffer_num;
+
+#define SLAP_TCP_RMEM ( 0x1U )
+#define SLAP_TCP_WMEM ( 0x2U )
+
+static int
+tcp_buffer_parse(
+        struct berval *val,
+        int argc,
+        char **argv,
+        int *size,
+        int *rw,
+        Listener **l )
+{
+    int i, rc = LDAP_SUCCESS;
+    LDAPURLDesc *lud = NULL;
+    char *ptr;
+
+    if ( val != NULL && argv == NULL ) {
+        char *s = val->bv_val;
+
+        argv = ldap_str2charray( s, " \t" );
+        if ( argv == NULL ) {
+            return LDAP_OTHER;
+        }
+    }
+
+    i = 0;
+    if ( strncasecmp( argv[i], "listener=", STRLENOF("listener=") ) == 0 ) {
+        char *url = argv[i] + STRLENOF("listener=");
+
+        if ( ldap_url_parse( url, &lud ) ) {
+            rc = LDAP_INVALID_SYNTAX;
+            goto done;
+        }
+
+        *l = config_check_my_url( url, lud );
+        if ( *l == NULL ) {
+            rc = LDAP_NO_SUCH_ATTRIBUTE;
+            goto done;
+        }
+
+        i++;
+    }
+
+    ptr = argv[i];
+    if ( strncasecmp( ptr, "read=", STRLENOF("read=") ) == 0 ) {
+        *rw |= SLAP_TCP_RMEM;
+        ptr += STRLENOF("read=");
+
+    } else if ( strncasecmp( ptr, "write=", STRLENOF("write=") ) == 0 ) {
+        *rw |= SLAP_TCP_WMEM;
+        ptr += STRLENOF("write=");
+
+    } else {
+        *rw |= ( SLAP_TCP_RMEM | SLAP_TCP_WMEM );
+    }
+
+    /* accept any base */
+    if ( lutil_atoix( size, ptr, 0 ) ) {
+        rc = LDAP_INVALID_SYNTAX;
+        goto done;
+    }
+
+done:;
+    if ( val != NULL && argv != NULL ) {
+        ldap_charray_free( argv );
+    }
+
+    if ( lud != NULL ) {
+        ldap_free_urldesc( lud );
+    }
+
+    return rc;
+}
+
+static int
+tcp_buffer_unparse( int size, int rw, Listener *l, struct berval *val )
+{
+    char buf[sizeof("2147483648")], *ptr;
+
+    /* unparse for later use */
+    val->bv_len = snprintf( buf, sizeof(buf), "%d", size );
+    if ( l != NULL ) {
+        val->bv_len += STRLENOF( "listener="
+                                 " " ) +
+                l->sl_url.bv_len;
+    }
+
+    if ( rw != ( SLAP_TCP_RMEM | SLAP_TCP_WMEM ) ) {
+        if ( rw & SLAP_TCP_RMEM ) {
+            val->bv_len += STRLENOF("read=");
+        } else if ( rw & SLAP_TCP_WMEM ) {
+            val->bv_len += STRLENOF("write=");
+        }
+    }
+
+    val->bv_val = SLAP_MALLOC( val->bv_len + 1 );
+
+    ptr = val->bv_val;
+
+    if ( l != NULL ) {
+        ptr = lutil_strcopy( ptr, "listener=" );
+        ptr = lutil_strncopy( ptr, l->sl_url.bv_val, l->sl_url.bv_len );
+        *ptr++ = ' ';
+    }
+
+    if ( rw != ( SLAP_TCP_RMEM | SLAP_TCP_WMEM ) ) {
+        if ( rw & SLAP_TCP_RMEM ) {
+            ptr = lutil_strcopy( ptr, "read=" );
+        } else if ( rw & SLAP_TCP_WMEM ) {
+            ptr = lutil_strcopy( ptr, "write=" );
+        }
+    }
+
+    ptr = lutil_strcopy( ptr, buf );
+    *ptr = '\0';
+
+    assert( val->bv_val + val->bv_len == ptr );
+
+    return LDAP_SUCCESS;
+}
+
+static int
+tcp_buffer_add_one( int argc, char **argv )
+{
+    int rc = 0;
+    int size = -1, rw = 0;
+    Listener *l = NULL;
+
+    struct berval val;
+
+    /* parse */
+    rc = tcp_buffer_parse( NULL, argc, argv, &size, &rw, &l );
+    if ( rc != 0 ) {
+        return rc;
+    }
+
+    /* unparse for later use */
+    rc = tcp_buffer_unparse( size, rw, l, &val );
+    if ( rc != LDAP_SUCCESS ) {
+        return rc;
+    }
+
+    /* use parsed values */
+    if ( l != NULL ) {
+        int i;
+        Listener **ll = slapd_get_listeners();
+
+        for ( i = 0; ll[i] != NULL; i++ ) {
+            if ( ll[i] == l ) break;
+        }
+
+        if ( ll[i] == NULL ) {
+            return LDAP_NO_SUCH_ATTRIBUTE;
+        }
+
+        /* buffer only applies to TCP listeners;
+         * we do not do any check here, and delegate them
+         * to setsockopt(2) */
+        if ( rw & SLAP_TCP_RMEM ) l->sl_tcp_rmem = size;
+        if ( rw & SLAP_TCP_WMEM ) l->sl_tcp_wmem = size;
+
+        for ( i++; ll[i] != NULL && bvmatch( &l->sl_url, &ll[i]->sl_url );
+                i++ ) {
+            if ( rw & SLAP_TCP_RMEM ) ll[i]->sl_tcp_rmem = size;
+            if ( rw & SLAP_TCP_WMEM ) ll[i]->sl_tcp_wmem = size;
+        }
+
+    } else {
+        /* NOTE: this affects listeners without a specific setting,
+         * does not set all listeners */
+        if ( rw & SLAP_TCP_RMEM ) slapd_tcp_rmem = size;
+        if ( rw & SLAP_TCP_WMEM ) slapd_tcp_wmem = size;
+    }
+
+    tcp_buffer = SLAP_REALLOC(
+            tcp_buffer, sizeof(struct berval) * ( tcp_buffer_num + 2 ) );
+    /* append */
+    tcp_buffer[tcp_buffer_num] = val;
+
+    tcp_buffer_num++;
+    BER_BVZERO( &tcp_buffer[tcp_buffer_num] );
+
+    return rc;
+}
+
+static int
+config_tcp_buffer( ConfigArgs *c )
+{
+    int rc;
+
+    rc = tcp_buffer_add_one( c->argc - 1, &c->argv[1] );
+    if ( rc ) {
+        snprintf( c->cr_msg, sizeof(c->cr_msg), "<%s> unable to add value #%d",
+                c->argv[0], tcp_buffer_num );
+        Debug( LDAP_DEBUG_ANY, "%s: %s\n", c->log, c->cr_msg );
+        return 1;
+    }
+
+    return 0;
+}
+#endif /* LDAP_TCP_BUFFER */
+
+static int
+config_restrict( ConfigArgs *c )
+{
+    slap_mask_t restrictops = 0;
+    int i;
+    slap_verbmasks restrictable_ops[] = {
+        { BER_BVC("bind"), SLAP_RESTRICT_OP_BIND },
+        { BER_BVC("add"), SLAP_RESTRICT_OP_ADD },
+        { BER_BVC("modify"), SLAP_RESTRICT_OP_MODIFY },
+        { BER_BVC("rename"), SLAP_RESTRICT_OP_RENAME },
+        { BER_BVC("modrdn"), 0 },
+        { BER_BVC("delete"), SLAP_RESTRICT_OP_DELETE },
+        { BER_BVC("search"), SLAP_RESTRICT_OP_SEARCH },
+        { BER_BVC("compare"), SLAP_RESTRICT_OP_COMPARE },
+        { BER_BVC("read"), SLAP_RESTRICT_OP_READS },
+        { BER_BVC("write"), SLAP_RESTRICT_OP_WRITES },
+        { BER_BVC("extended"), SLAP_RESTRICT_OP_EXTENDED },
+        { BER_BVC("extended=" LDAP_EXOP_START_TLS), SLAP_RESTRICT_EXOP_START_TLS },
+        { BER_BVC("extended=" LDAP_EXOP_MODIFY_PASSWD), SLAP_RESTRICT_EXOP_MODIFY_PASSWD },
+        { BER_BVC("extended=" LDAP_EXOP_X_WHO_AM_I), SLAP_RESTRICT_EXOP_WHOAMI },
+        { BER_BVC("extended=" LDAP_EXOP_X_CANCEL), SLAP_RESTRICT_EXOP_CANCEL },
+        { BER_BVC("all"), SLAP_RESTRICT_OP_ALL },
+        { BER_BVNULL, 0 }
+    };
+
+    i = verbs_to_mask( c->argc, c->argv, restrictable_ops, &restrictops );
+    if ( i ) {
+        snprintf( c->cr_msg, sizeof(c->cr_msg), "<%s> unknown operation",
+                c->argv[0] );
+        Debug( LDAP_DEBUG_ANY, "%s: %s %s\n",
+                c->log, c->cr_msg, c->argv[i] );
+        return 1;
+    }
+    if ( restrictops & SLAP_RESTRICT_OP_EXTENDED )
+        restrictops &= ~SLAP_RESTRICT_EXOP_MASK;
+    return 0;
+}
+
+static slap_verbmasks *loglevel_ops;
+
+static int
+loglevel_init( void )
+{
+    slap_verbmasks lo[] = {
+        { BER_BVC("Any"), (slap_mask_t)LDAP_DEBUG_ANY },
+        { BER_BVC("Trace"), LDAP_DEBUG_TRACE },
+        { BER_BVC("Packets"), LDAP_DEBUG_PACKETS },
+        { BER_BVC("Args"), LDAP_DEBUG_ARGS },
+        { BER_BVC("Conns"), LDAP_DEBUG_CONNS },
+        { BER_BVC("BER"), LDAP_DEBUG_BER },
+        { BER_BVC("Filter"), LDAP_DEBUG_FILTER },
+        { BER_BVC("Config"), LDAP_DEBUG_CONFIG },
+        { BER_BVC("ACL"), LDAP_DEBUG_ACL },
+        { BER_BVC("Stats"), LDAP_DEBUG_STATS },
+        { BER_BVC("Stats2"), LDAP_DEBUG_STATS2 },
+        { BER_BVC("Shell"), LDAP_DEBUG_SHELL },
+        { BER_BVC("Parse"), LDAP_DEBUG_PARSE },
+        { BER_BVC("Sync"), LDAP_DEBUG_SYNC },
+        { BER_BVC("None"), LDAP_DEBUG_NONE },
+        { BER_BVNULL, 0 }
+    };
+
+    return slap_verbmasks_init( &loglevel_ops, lo );
+}
+
+static void
+loglevel_destroy( void )
+{
+    if ( loglevel_ops ) {
+        (void)slap_verbmasks_destroy( loglevel_ops );
+    }
+    loglevel_ops = NULL;
+}
+
+int
+str2loglevel( const char *s, int *l )
+{
+    int i;
+
+    if ( loglevel_ops == NULL ) {
+        loglevel_init();
+    }
+
+    i = verb_to_mask( s, loglevel_ops );
+
+    if ( BER_BVISNULL( &loglevel_ops[i].word ) ) {
+        return -1;
+    }
+
+    *l = loglevel_ops[i].mask;
+
+    return 0;
+}
+
+int
+loglevel2bvarray( int l, BerVarray *bva )
+{
+    if ( loglevel_ops == NULL ) {
+        loglevel_init();
+    }
+
+    if ( l == 0 ) {
+        struct berval bv = BER_BVC("0");
+        return value_add_one( bva, &bv );
+    }
+
+    return mask_to_verbs( loglevel_ops, l, bva );
+}
+
+int
+loglevel_print( FILE *out )
+{
+    int i;
+
+    if ( loglevel_ops == NULL ) {
+        loglevel_init();
+    }
+
+    fprintf( out, "Installed log subsystems:\n\n" );
+    for ( i = 0; !BER_BVISNULL( &loglevel_ops[i].word ); i++ ) {
+        unsigned mask = loglevel_ops[i].mask & 0xffffffffUL;
+        fprintf( out,
+                ( mask == ( (slap_mask_t)-1 & 0xffffffffUL ) ?
+                                "\t%-30s (-1, 0xffffffff)\n" :
+                                "\t%-30s (%u, 0x%x)\n" ),
+                loglevel_ops[i].word.bv_val, mask, mask );
+    }
+
+    fprintf( out,
+            "\nNOTE: custom log subsystems may be later installed "
+            "by specific code\n\n" );
+
+    return 0;
+}
+
+static int config_syslog;
+
+static int
+config_loglevel( ConfigArgs *c )
+{
+    int i;
+
+    if ( loglevel_ops == NULL ) {
+        loglevel_init();
+    }
+
+    if ( c->op == SLAP_CONFIG_EMIT ) {
+        /* Get default or commandline slapd setting */
+        if ( ldap_syslog && !config_syslog ) config_syslog = ldap_syslog;
+        return loglevel2bvarray( config_syslog, &c->rvalue_vals );
+
+    } else if ( c->op == LDAP_MOD_DELETE ) {
+        if ( !c->line ) {
+            config_syslog = 0;
+        } else {
+            i = verb_to_mask( c->line, loglevel_ops );
+            config_syslog &= ~loglevel_ops[i].mask;
+        }
+        if ( slapMode & SLAP_SERVER_MODE ) {
+            ldap_syslog = config_syslog;
+        }
+        return 0;
+    }
+
+    for ( i = 1; i < c->argc; i++ ) {
+        int level;
+
+        if ( isdigit( (unsigned char)c->argv[i][0] ) || c->argv[i][0] == '-' ) {
+            if ( lutil_atoix( &level, c->argv[i], 0 ) != 0 ) {
+                snprintf( c->cr_msg, sizeof(c->cr_msg),
+                        "<%s> unable to parse level",
+                        c->argv[0] );
+                Debug( LDAP_DEBUG_ANY, "%s: %s \"%s\"\n",
+                        c->log, c->cr_msg, c->argv[i] );
+                return 1;
+            }
+        } else {
+            if ( str2loglevel( c->argv[i], &level ) ) {
+                snprintf( c->cr_msg, sizeof(c->cr_msg), "<%s> unknown level",
+                        c->argv[0] );
+                Debug( LDAP_DEBUG_ANY, "%s: %s \"%s\"\n",
+                        c->log, c->cr_msg, c->argv[i] );
+                return 1;
+            }
+        }
+        /* Explicitly setting a zero clears all the levels */
+        if ( level )
+            config_syslog |= level;
+        else
+            config_syslog = 0;
+    }
+    if ( slapMode & SLAP_SERVER_MODE ) {
+        ldap_syslog = config_syslog;
+    }
+    return 0;
+}
+
+static int
+config_include( ConfigArgs *c )
+{
+    int savelineno = c->lineno;
+    int rc;
+    ConfigFile *cf;
+    ConfigFile *cfsave = cfn;
+    ConfigFile *cf2 = NULL;
+
+    /* Leftover from RE23. No dynamic config for include files */
+    if ( c->op == SLAP_CONFIG_EMIT || c->op == LDAP_MOD_DELETE ) return 1;
+
+    cf = ch_calloc( 1, sizeof(ConfigFile) );
+    if ( cfn->c_kids ) {
+        for ( cf2 = cfn->c_kids; cf2 && cf2->c_sibs; cf2 = cf2->c_sibs )
+            /* empty */;
+        cf2->c_sibs = cf;
+    } else {
+        cfn->c_kids = cf;
+    }
+    cfn = cf;
+    ber_str2bv( c->argv[1], 0, 1, &cf->c_file );
+    rc = read_config_file( c->argv[1], c->depth + 1, c, config_back_cf_table );
+    c->lineno = savelineno - 1;
+    cfn = cfsave;
+    if ( rc ) {
+        if ( cf2 )
+            cf2->c_sibs = NULL;
+        else
+            cfn->c_kids = NULL;
+        ch_free( cf->c_file.bv_val );
+        ch_free( cf );
+    } else {
+        c->ca_private = cf;
+    }
+    return rc;
+}
+
+#ifdef HAVE_TLS
+static int
+config_tls_cleanup( ConfigArgs *c )
+{
+    int rc = 0;
+
+    if ( slap_tls_ld ) {
+        int opt = 1;
+
+        ldap_pvt_tls_ctx_free( slap_tls_ctx );
+        slap_tls_ctx = NULL;
+
+        /* Force new ctx to be created */
+        rc = ldap_pvt_tls_set_option(
+                slap_tls_ld, LDAP_OPT_X_TLS_NEWCTX, &opt );
+        if ( rc == 0 ) {
+            /* The ctx's refcount is bumped up here */
+            ldap_pvt_tls_get_option(
+                    slap_tls_ld, LDAP_OPT_X_TLS_CTX, &slap_tls_ctx );
+        } else {
+            if ( rc == LDAP_NOT_SUPPORTED )
+                rc = LDAP_UNWILLING_TO_PERFORM;
+            else
+                rc = LDAP_OTHER;
+        }
+    }
+    return rc;
+}
+
+static int
+config_tls_option( ConfigArgs *c )
+{
+    int flag, rc;
+    int berval = 0;
+    LDAP *ld = slap_tls_ld;
+    switch ( c->type ) {
+        case CFG_TLS_RAND:
+            flag = LDAP_OPT_X_TLS_RANDOM_FILE;
+            ld = NULL;
+            break;
+        case CFG_TLS_CIPHER:
+            flag = LDAP_OPT_X_TLS_CIPHER_SUITE;
+            break;
+        case CFG_TLS_CERT_FILE:
+            flag = LDAP_OPT_X_TLS_CERTFILE;
+            break;
+        case CFG_TLS_CERT_KEY:
+            flag = LDAP_OPT_X_TLS_KEYFILE;
+            break;
+        case CFG_TLS_CA_PATH:
+            flag = LDAP_OPT_X_TLS_CACERTDIR;
+            break;
+        case CFG_TLS_CA_FILE:
+            flag = LDAP_OPT_X_TLS_CACERTFILE;
+            break;
+        case CFG_TLS_DH_FILE:
+            flag = LDAP_OPT_X_TLS_DHFILE;
+            break;
+        case CFG_TLS_ECNAME:
+            flag = LDAP_OPT_X_TLS_ECNAME;
+            break;
+#ifdef HAVE_GNUTLS
+        case CFG_TLS_CRL_FILE:
+            flag = LDAP_OPT_X_TLS_CRLFILE;
+            break;
+#endif
+        case CFG_TLS_CACERT:
+            flag = LDAP_OPT_X_TLS_CACERT;
+            berval = 1;
+            break;
+        case CFG_TLS_CERT:
+            flag = LDAP_OPT_X_TLS_CERT;
+            berval = 1;
+            break;
+        case CFG_TLS_KEY:
+            flag = LDAP_OPT_X_TLS_KEY;
+            berval = 1;
+            break;
+        default:
+            Debug( LDAP_DEBUG_ANY, "%s: "
+                    "unknown tls_option <0x%x>\n",
+                    c->log, c->type );
+            return 1;
+    }
+    if ( c->op == SLAP_CONFIG_EMIT ) {
+        return ldap_pvt_tls_get_option( ld, flag,
+                berval ? (void *)&c->value_bv : (void *)&c->value_string );
+    } else if ( c->op == LDAP_MOD_DELETE ) {
+        config_push_cleanup( c, config_tls_cleanup );
+        return ldap_pvt_tls_set_option( ld, flag, NULL );
+    }
+    if ( !berval ) ch_free( c->value_string );
+    config_push_cleanup( c, config_tls_cleanup );
+    rc = ldap_pvt_tls_set_option(
+            ld, flag, berval ? (void *)&c->value_bv : (void *)c->argv[1] );
+    if ( berval ) ch_free( c->value_bv.bv_val );
+    return rc;
+}
+
+/* FIXME: this ought to be provided by libldap */
+static int
+config_tls_config( ConfigArgs *c )
+{
+    int i, flag;
+    switch ( c->type ) {
+        case CFG_TLS_CRLCHECK:
+            flag = LDAP_OPT_X_TLS_CRLCHECK;
+            break;
+        case CFG_TLS_VERIFY:
+            flag = LDAP_OPT_X_TLS_REQUIRE_CERT;
+            break;
+        case CFG_TLS_PROTOCOL_MIN:
+            flag = LDAP_OPT_X_TLS_PROTOCOL_MIN;
+            break;
+        default:
+            Debug( LDAP_DEBUG_ANY, "%s: "
+                    "unknown tls_option <0x%x>\n",
+                    c->log, c->type );
+            return 1;
+    }
+    if ( c->op == SLAP_CONFIG_EMIT ) {
+        return slap_tls_get_config( slap_tls_ld, flag, &c->value_string );
+    } else if ( c->op == LDAP_MOD_DELETE ) {
+        int i = 0;
+        config_push_cleanup( c, config_tls_cleanup );
+        return ldap_pvt_tls_set_option( slap_tls_ld, flag, &i );
+    }
+    ch_free( c->value_string );
+    config_push_cleanup( c, config_tls_cleanup );
+    if ( isdigit( (unsigned char)c->argv[1][0] ) &&
+            c->type != CFG_TLS_PROTOCOL_MIN ) {
+        if ( lutil_atoi( &i, c->argv[1] ) != 0 ) {
+            Debug( LDAP_DEBUG_ANY, "%s: "
+                    "unable to parse %s \"%s\"\n",
+                    c->log, c->argv[0], c->argv[1] );
+            return 1;
+        }
+        return ldap_pvt_tls_set_option( slap_tls_ld, flag, &i );
+    } else {
+        return ldap_pvt_tls_config( slap_tls_ld, flag, c->argv[1] );
+    }
+}
+#endif
+
+void
+init_config_argv( ConfigArgs *c )
+{
+    c->argv = ch_calloc( ARGS_STEP + 1, sizeof(*c->argv) );
+    c->argv_size = ARGS_STEP + 1;
+}
+
+ConfigTable *
+config_find_keyword( ConfigTable *Conf, ConfigArgs *c )
+{
+    int i;
+
+    for ( i = 0; Conf[i].name; i++ )
+        if ( ( Conf[i].length &&
+                     ( !strncasecmp(
+                             c->argv[0], Conf[i].name, Conf[i].length ) ) ) ||
+                ( !strcasecmp( c->argv[0], Conf[i].name ) ) )
+            break;
+    if ( !Conf[i].name ) return NULL;
+    if ( (Conf[i].arg_type & ARGS_TYPES) == ARG_BINARY ) {
+        size_t decode_len = LUTIL_BASE64_DECODE_LEN( c->linelen );
+        ch_free( c->tline );
+        c->tline = ch_malloc( decode_len + 1 );
+        c->linelen = lutil_b64_pton( c->line, c->tline, decode_len );
+        if ( c->linelen < 0 ) {
+            ch_free( c->tline );
+            c->tline = NULL;
+            return NULL;
+        }
+        c->line = c->tline;
+    }
+    return Conf + i;
+}
+
+int
+config_check_vals( ConfigTable *Conf, ConfigArgs *c, int check_only )
+{
+    int arg_user, arg_type, arg_syn, iarg;
+    unsigned uiarg;
+    long larg;
+    unsigned long ularg;
+    ber_len_t barg;
+
+    if ( Conf->arg_type == ARG_IGNORED ) {
+        Debug( LDAP_DEBUG_CONFIG, "%s: keyword <%s> ignored\n",
+                c->log, Conf->name );
+        return 0;
+    }
+    arg_type = Conf->arg_type & ARGS_TYPES;
+    arg_user = Conf->arg_type & ARGS_USERLAND;
+    arg_syn = Conf->arg_type & ARGS_SYNTAX;
+
+    if ( Conf->min_args && ( c->argc < Conf->min_args ) ) {
+        snprintf( c->cr_msg, sizeof(c->cr_msg), "<%s> missing <%s> argument",
+                c->argv[0], Conf->what ? Conf->what : "" );
+        Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: keyword %s\n",
+                c->log, c->cr_msg );
+        return ARG_BAD_CONF;
+    }
+    if ( Conf->max_args && ( c->argc > Conf->max_args ) ) {
+        char *ignored = " ignored";
+
+        snprintf( c->cr_msg, sizeof(c->cr_msg), "<%s> extra cruft after <%s>",
+                c->argv[0], Conf->what );
+
+        ignored = "";
+        Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s%s.\n",
+                c->log, c->cr_msg, ignored );
+        return ARG_BAD_CONF;
+    }
+    if ( (arg_syn & ARG_PAREN) && *c->argv[1] != '(' /*')'*/ ) {
+        snprintf( c->cr_msg, sizeof(c->cr_msg), "<%s> old format not supported",
+                c->argv[0] );
+        Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s\n",
+                c->log, c->cr_msg );
+        return ARG_BAD_CONF;
+    }
+    if ( arg_type && !Conf->arg_item && !(arg_syn & ARG_OFFSET) ) {
+        snprintf( c->cr_msg, sizeof(c->cr_msg),
+                "<%s> invalid config_table, arg_item is NULL",
+                c->argv[0] );
+        Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s\n",
+                c->log, c->cr_msg );
+        return ARG_BAD_CONF;
+    }
+    c->type = arg_user;
+    memset( &c->values, 0, sizeof(c->values) );
+    if ( arg_type == ARG_STRING ) {
+        assert( c->argc == 2 );
+        if ( !check_only ) c->value_string = ch_strdup( c->argv[1] );
+    } else if ( arg_type == ARG_BERVAL ) {
+        assert( c->argc == 2 );
+        if ( !check_only ) ber_str2bv( c->argv[1], 0, 1, &c->value_bv );
+    } else if ( arg_type == ARG_BINARY ) {
+        assert( c->argc == 2 );
+        if ( !check_only ) {
+            c->value_bv.bv_len = c->linelen;
+            c->value_bv.bv_val = ch_malloc( c->linelen );
+            AC_MEMCPY( c->value_bv.bv_val, c->line, c->linelen );
+        }
+    } else { /* all numeric */
+        int j;
+        iarg = 0;
+        larg = 0;
+        barg = 0;
+        switch ( arg_type ) {
+            case ARG_INT:
+                assert( c->argc == 2 );
+                if ( lutil_atoix( &iarg, c->argv[1], 0 ) != 0 ) {
+                    snprintf( c->cr_msg, sizeof(c->cr_msg),
+                            "<%s> unable to parse \"%s\" as int",
+                            c->argv[0], c->argv[1] );
+                    Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s\n",
+                            c->log, c->cr_msg );
+                    return ARG_BAD_CONF;
+                }
+                break;
+            case ARG_UINT:
+                assert( c->argc == 2 );
+                if ( lutil_atoux( &uiarg, c->argv[1], 0 ) != 0 ) {
+                    snprintf( c->cr_msg, sizeof(c->cr_msg),
+                            "<%s> unable to parse \"%s\" as unsigned int",
+                            c->argv[0], c->argv[1] );
+                    Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s\n",
+                            c->log, c->cr_msg );
+                    return ARG_BAD_CONF;
+                }
+                break;
+            case ARG_LONG:
+                assert( c->argc == 2 );
+                if ( lutil_atolx( &larg, c->argv[1], 0 ) != 0 ) {
+                    snprintf( c->cr_msg, sizeof(c->cr_msg),
+                            "<%s> unable to parse \"%s\" as long",
+                            c->argv[0], c->argv[1] );
+                    Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s\n",
+                            c->log, c->cr_msg );
+                    return ARG_BAD_CONF;
+                }
+                break;
+            case ARG_ULONG:
+                assert( c->argc == 2 );
+                if ( LUTIL_ATOULX( &ularg, c->argv[1], 0 ) != 0 ) {
+                    snprintf( c->cr_msg, sizeof(c->cr_msg),
+                            "<%s> unable to parse \"%s\" as unsigned long",
+                            c->argv[0], c->argv[1] );
+                    Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s\n",
+                            c->log, c->cr_msg );
+                    return ARG_BAD_CONF;
+                }
+                break;
+            case ARG_BER_LEN_T: {
+                unsigned long l;
+                assert( c->argc == 2 );
+                if ( lutil_atoulx( &l, c->argv[1], 0 ) != 0 ) {
+                    snprintf( c->cr_msg, sizeof(c->cr_msg),
+                            "<%s> unable to parse \"%s\" as ber_len_t",
+                            c->argv[0], c->argv[1] );
+                    Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s\n",
+                            c->log, c->cr_msg );
+                    return ARG_BAD_CONF;
+                }
+                barg = (ber_len_t)l;
+            } break;
+            case ARG_ON_OFF:
+                /* note: this is an explicit exception
+                 * to the "need exactly 2 args" rule */
+                if ( c->argc == 1 ) {
+                    iarg = 1;
+                } else if ( !strcasecmp( c->argv[1], "on" ) ||
+                        !strcasecmp( c->argv[1], "true" ) ||
+                        !strcasecmp( c->argv[1], "yes" ) ) {
+                    iarg = 1;
+                } else if ( !strcasecmp( c->argv[1], "off" ) ||
+                        !strcasecmp( c->argv[1], "false" ) ||
+                        !strcasecmp( c->argv[1], "no" ) ) {
+                    iarg = 0;
+                } else {
+                    snprintf( c->cr_msg, sizeof(c->cr_msg),
+                            "<%s> invalid value",
+                            c->argv[0] );
+                    Debug( LDAP_DEBUG_ANY|LDAP_DEBUG_NONE, "%s: %s\n",
+                            c->log, c->cr_msg );
+                    return ARG_BAD_CONF;
+                }
+                break;
+        }
+        j = (arg_type & ARG_NONZERO) ? 1 : 0;
+        if ( iarg < j && larg < j && barg < (unsigned)j ) {
+            larg = larg ? larg : ( barg ? (long)barg : iarg );
+            snprintf( c->cr_msg, sizeof(c->cr_msg), "<%s> invalid value",
+                    c->argv[0] );
+            Debug( LDAP_DEBUG_ANY|LDAP_DEBUG_NONE, "%s: %s\n",
+                    c->log, c->cr_msg );
+            return ARG_BAD_CONF;
+        }
+        switch ( arg_type ) {
+            case ARG_ON_OFF:
+            case ARG_INT:
+                c->value_int = iarg;
+                break;
+            case ARG_UINT:
+                c->value_uint = uiarg;
+                break;
+            case ARG_LONG:
+                c->value_long = larg;
+                break;
+            case ARG_ULONG:
+                c->value_ulong = ularg;
+                break;
+            case ARG_BER_LEN_T:
+                c->value_ber_t = barg;
+                break;
+        }
+    }
+    return 0;
+}
+
+int
+config_set_vals( ConfigTable *Conf, ConfigArgs *c )
+{
+    int rc, arg_type;
+    void *ptr = NULL;
+
+    arg_type = Conf->arg_type;
+    if ( arg_type & ARG_MAGIC ) {
+        c->cr_msg[0] = '\0';
+        rc = ( *( (ConfigDriver *)Conf->arg_item ) )( c );
+        if ( rc ) {
+            if ( !c->cr_msg[0] ) {
+                snprintf( c->cr_msg, sizeof(c->cr_msg),
+                        "<%s> handler exited with %d",
+                        c->argv[0], rc );
+                Debug( LDAP_DEBUG_CONFIG, "%s: %s!\n", c->log, c->cr_msg );
+            }
+            return ARG_BAD_CONF;
+        }
+        return 0;
+    }
+    if ( arg_type & ARG_OFFSET ) {
+        {
+            snprintf( c->cr_msg, sizeof(c->cr_msg),
+                    "<%s> offset is missing base pointer",
+                    c->argv[0] );
+            Debug( LDAP_DEBUG_CONFIG, "%s: %s!\n", c->log, c->cr_msg );
+            return ARG_BAD_CONF;
+        }
+        ptr = (void *)( (char *)ptr + (long)Conf->arg_item );
+    } else if ( arg_type & ARGS_TYPES ) {
+        ptr = Conf->arg_item;
+    }
+    if ( arg_type & ARGS_TYPES ) switch ( arg_type & ARGS_TYPES ) {
+            case ARG_ON_OFF:
+            case ARG_INT:
+                *(int *)ptr = c->value_int;
+                break;
+            case ARG_UINT:
+                *(unsigned *)ptr = c->value_uint;
+                break;
+            case ARG_LONG:
+                *(long *)ptr = c->value_long;
+                break;
+            case ARG_ULONG:
+                *(size_t *)ptr = c->value_ulong;
+                break;
+            case ARG_BER_LEN_T:
+                *(ber_len_t *)ptr = c->value_ber_t;
+                break;
+            case ARG_STRING: {
+                char *cc = *(char **)ptr;
+                if ( cc ) {
+                    if ( (arg_type & ARG_UNIQUE) &&
+                            c->op == SLAP_CONFIG_ADD ) {
+                        Debug( LDAP_DEBUG_CONFIG, "%s: already set %s!\n",
+                                c->log, Conf->name );
+                        return ARG_BAD_CONF;
+                    }
+                    ch_free( cc );
+                }
+                *(char **)ptr = c->value_string;
+                break;
+            }
+            case ARG_BERVAL:
+            case ARG_BINARY:
+                *(struct berval *)ptr = c->value_bv;
+                break;
+        }
+    return 0;
+}
+
+int
+config_add_vals( ConfigTable *Conf, ConfigArgs *c )
+{
+    int rc, arg_type;
+
+    arg_type = Conf->arg_type;
+    if ( arg_type == ARG_IGNORED ) {
+        Debug( LDAP_DEBUG_CONFIG, "%s: keyword <%s> ignored\n",
+                c->log, Conf->name );
+        return 0;
+    }
+    rc = config_check_vals( Conf, c, 0 );
+    if ( rc ) return rc;
+    return config_set_vals( Conf, c );
+}
+
+int
+read_config_file(
+        const char *fname,
+        int depth,
+        ConfigArgs *cf,
+        ConfigTable *cft )
+{
+    FILE *fp;
+    ConfigTable *ct;
+    ConfigArgs *c;
+    int rc;
+    struct stat s;
+
+    c = ch_calloc( 1, sizeof(ConfigArgs) );
+    if ( c == NULL ) {
+        return 1;
+    }
+
+    if ( depth ) {
+        memcpy( c, cf, sizeof(ConfigArgs) );
+    } else {
+        c->depth = depth; /* XXX */
+    }
+
+    c->valx = -1;
+    c->fname = fname;
+    init_config_argv( c );
+
+    if ( stat( fname, &s ) != 0 ) {
+        char ebuf[128];
+        int saved_errno = errno;
+        ldap_syslog = 1;
+        Debug( LDAP_DEBUG_ANY, "could not stat config file \"%s\": %s (%d)\n",
+                fname, AC_STRERROR_R( saved_errno, ebuf, sizeof(ebuf) ),
+                saved_errno );
+        ch_free( c->argv );
+        ch_free( c );
+        return 1;
+    }
+
+    if ( !S_ISREG(s.st_mode) ) {
+        ldap_syslog = 1;
+        Debug( LDAP_DEBUG_ANY, "regular file expected, got \"%s\"\n", fname );
+        ch_free( c->argv );
+        ch_free( c );
+        return 1;
+    }
+
+    fp = fopen( fname, "r" );
+    if ( fp == NULL ) {
+        char ebuf[128];
+        int saved_errno = errno;
+        ldap_syslog = 1;
+        Debug( LDAP_DEBUG_ANY, "could not open config file \"%s\": %s (%d)\n",
+                fname, AC_STRERROR_R( saved_errno, ebuf, sizeof(ebuf) ),
+                saved_errno );
+        ch_free( c->argv );
+        ch_free( c );
+        return 1;
+    }
+
+    Debug( LDAP_DEBUG_CONFIG, "reading config file %s\n", fname );
+
+    fp_getline_init( c );
+
+    c->tline = NULL;
+
+    while ( fp_getline( fp, c ) ) {
+        /* skip comments and blank lines */
+        if ( c->line[0] == '#' || c->line[0] == '\0' ) {
+            continue;
+        }
+
+        snprintf( c->log, sizeof(c->log), "%s: line %d",
+                c->fname, c->lineno );
+
+        c->argc = 0;
+        ch_free( c->tline );
+        if ( config_fp_parse_line( c ) ) {
+            rc = 1;
+            goto done;
+        }
+
+        if ( c->argc < 1 ) {
+            Debug( LDAP_DEBUG_ANY, "%s: bad config line.\n", c->log );
+            rc = 1;
+            goto done;
+        }
+
+        c->op = SLAP_CONFIG_ADD;
+
+        ct = config_find_keyword( cft, c );
+        if ( ct ) {
+            c->table = Cft_Global;
+            rc = config_add_vals( ct, c );
+            if ( !rc ) continue;
+
+            if ( rc & ARGS_USERLAND ) {
+                /* XXX a usertype would be opaque here */
+                Debug( LDAP_DEBUG_CONFIG, "%s: unknown user type <%s>\n",
+                        c->log, c->argv[0] );
+                rc = 1;
+                goto done;
+
+            } else if ( rc == ARG_BAD_CONF ) {
+                rc = 1;
+                goto done;
+            }
+
+        } else {
+            Debug( LDAP_DEBUG_ANY, "%s: unknown directive "
+                    "<%s> outside backend info and database definitions.\n",
+                    c->log, *c->argv );
+            rc = 1;
+            goto done;
+        }
+    }
+
+    rc = 0;
+
+done:
+    ch_free( c->tline );
+    fclose( fp );
+    ch_free( c->argv );
+    ch_free( c );
+    return rc;
+}
+
+int
+read_config( const char *fname, const char *dir )
+{
+    if ( !fname ) fname = LLOADD_DEFAULT_CONFIGFILE;
+
+    cfn = ch_calloc( 1, sizeof(ConfigFile) );
+
+    return read_config_file( fname, 0, NULL, config_back_cf_table );
+}
+
+/* restrictops, allows, disallows, requires, loglevel */
+
+int
+bverb_to_mask( struct berval *bword, slap_verbmasks *v )
+{
+    int i;
+    for ( i = 0; !BER_BVISNULL( &v[i].word ); i++ ) {
+        if ( !ber_bvstrcasecmp( bword, &v[i].word ) ) break;
+    }
+    return i;
+}
+
+int
+verb_to_mask( const char *word, slap_verbmasks *v )
+{
+    struct berval bword;
+    ber_str2bv( word, 0, 0, &bword );
+    return bverb_to_mask( &bword, v );
+}
+
+int
+verbs_to_mask( int argc, char *argv[], slap_verbmasks *v, slap_mask_t *m )
+{
+    int i, j;
+    for ( i = 1; i < argc; i++ ) {
+        j = verb_to_mask( argv[i], v );
+        if ( BER_BVISNULL( &v[j].word ) ) return i;
+        while ( !v[j].mask )
+            j--;
+        *m |= v[j].mask;
+    }
+    return 0;
+}
+
+/* Mask keywords that represent multiple bits should occur before single
+ * bit keywords in the verbmasks array.
+ */
+int
+mask_to_verbs( slap_verbmasks *v, slap_mask_t m, BerVarray *bva )
+{
+    int i, rc = 1;
+
+    if ( m ) {
+        for ( i = 0; !BER_BVISNULL( &v[i].word ); i++ ) {
+            if ( !v[i].mask ) continue;
+            if ( (m & v[i].mask) == v[i].mask ) {
+                value_add_one( bva, &v[i].word );
+                rc = 0;
+                m ^= v[i].mask;
+                if ( !m ) break;
+            }
+        }
+    }
+    return rc;
+}
+
+int
+slap_verbmasks_init( slap_verbmasks **vp, slap_verbmasks *v )
+{
+    int i;
+
+    assert( *vp == NULL );
+
+    for ( i = 0; !BER_BVISNULL( &v[i].word ); i++ ) /* EMPTY */;
+
+    *vp = ch_calloc( i + 1, sizeof(slap_verbmasks) );
+
+    for ( i = 0; !BER_BVISNULL( &v[i].word ); i++ ) {
+        ber_dupbv( &(*vp)[i].word, &v[i].word );
+        *( (slap_mask_t *)&(*vp)[i].mask ) = v[i].mask;
+    }
+
+    BER_BVZERO( &(*vp)[i].word );
+
+    return 0;
+}
+
+int
+slap_verbmasks_destroy( slap_verbmasks *v )
+{
+    int i;
+
+    assert( v != NULL );
+
+    for ( i = 0; !BER_BVISNULL( &v[i].word ); i++ ) {
+        ch_free( v[i].word.bv_val );
+    }
+
+    ch_free( v );
+
+    return 0;
+}
+
+int
+config_push_cleanup( ConfigArgs *ca, ConfigDriver *cleanup )
+{
+    /* Stub, cleanups only run in online config */
+    return 0;
+}
+
+static slap_verbmasks tlskey[] = {
+    { BER_BVC("no"), SB_TLS_OFF },
+    { BER_BVC("yes"), SB_TLS_ON },
+    { BER_BVC("critical"), SB_TLS_CRITICAL },
+    { BER_BVNULL, 0 }
+};
+
+static slap_verbmasks crlkeys[] = {
+    { BER_BVC("none"), LDAP_OPT_X_TLS_CRL_NONE },
+    { BER_BVC("peer"), LDAP_OPT_X_TLS_CRL_PEER },
+    { BER_BVC("all"), LDAP_OPT_X_TLS_CRL_ALL },
+    { BER_BVNULL, 0 }
+};
+
+static slap_verbmasks vfykeys[] = {
+    { BER_BVC("never"), LDAP_OPT_X_TLS_NEVER },
+    { BER_BVC("allow"), LDAP_OPT_X_TLS_ALLOW },
+    { BER_BVC("try"), LDAP_OPT_X_TLS_TRY },
+    { BER_BVC("demand"), LDAP_OPT_X_TLS_DEMAND },
+    { BER_BVC("hard"), LDAP_OPT_X_TLS_HARD },
+    { BER_BVC("true"), LDAP_OPT_X_TLS_HARD },
+    { BER_BVNULL, 0 }
+};
+
+static slap_verbmasks methkey[] = {
+    { BER_BVC("none"), LDAP_AUTH_NONE },
+    { BER_BVC("simple"), LDAP_AUTH_SIMPLE },
+#ifdef HAVE_CYRUS_SASL
+    { BER_BVC("sasl"), LDAP_AUTH_SASL },
+#endif
+    { BER_BVNULL, 0 }
+};
+
+static slap_verbmasks versionkey[] = {
+    { BER_BVC("2"), LDAP_VERSION2 },
+    { BER_BVC("3"), LDAP_VERSION3 },
+    { BER_BVNULL, 0 }
+};
+
+int
+slap_keepalive_parse(
+        struct berval *val,
+        void *bc,
+        slap_cf_aux_table *tab0,
+        const char *tabmsg,
+        int unparse )
+{
+    if ( unparse ) {
+        slap_keepalive *sk = (slap_keepalive *)bc;
+        int rc = snprintf( val->bv_val, val->bv_len, "%d:%d:%d",
+                sk->sk_idle, sk->sk_probes, sk->sk_interval );
+        if ( rc < 0 ) {
+            return -1;
+        }
+
+        if ( (unsigned)rc >= val->bv_len ) {
+            return -1;
+        }
+
+        val->bv_len = rc;
+
+    } else {
+        char *s = val->bv_val;
+        char *next;
+        slap_keepalive *sk = (slap_keepalive *)bc;
+        slap_keepalive sk2;
+
+        if ( s[0] == ':' ) {
+            sk2.sk_idle = 0;
+            s++;
+
+        } else {
+            sk2.sk_idle = strtol( s, &next, 10 );
+            if ( next == s || next[0] != ':' ) {
+                return -1;
+            }
+
+            if ( sk2.sk_idle < 0 ) {
+                return -1;
+            }
+
+            s = ++next;
+        }
+
+        if ( s[0] == ':' ) {
+            sk2.sk_probes = 0;
+            s++;
+
+        } else {
+            sk2.sk_probes = strtol( s, &next, 10 );
+            if ( next == s || next[0] != ':' ) {
+                return -1;
+            }
+
+            if ( sk2.sk_probes < 0 ) {
+                return -1;
+            }
+
+            s = ++next;
+        }
+
+        if ( *s == '\0' ) {
+            sk2.sk_interval = 0;
+
+        } else {
+            sk2.sk_interval = strtol( s, &next, 10 );
+            if ( next == s || next[0] != '\0' ) {
+                return -1;
+            }
+
+            if ( sk2.sk_interval < 0 ) {
+                return -1;
+            }
+        }
+
+        *sk = sk2;
+
+        ber_memfree( val->bv_val );
+        BER_BVZERO( val );
+    }
+
+    return 0;
+}
+
+static int
+slap_sb_uri(
+        struct berval *val,
+        void *bcp,
+        slap_cf_aux_table *tab0,
+        const char *tabmsg,
+        int unparse )
+{
+    slap_bindconf *bc = bcp;
+    if ( unparse ) {
+        if ( bc->sb_uri.bv_len >= val->bv_len ) return -1;
+        val->bv_len = bc->sb_uri.bv_len;
+        AC_MEMCPY( val->bv_val, bc->sb_uri.bv_val, val->bv_len );
+    } else {
+        bc->sb_uri = *val;
+#ifdef HAVE_TLS
+        if ( ldap_is_ldaps_url( val->bv_val ) ) bc->sb_tls_do_init = 1;
+#endif
+    }
+    return 0;
+}
+
+static slap_cf_aux_table bindkey[] = {
+    { BER_BVC("uri="), 0, 'x', 1, slap_sb_uri },
+    { BER_BVC("version="), offsetof(slap_bindconf, sb_version), 'i', 0, versionkey },
+    { BER_BVC("bindmethod="), offsetof(slap_bindconf, sb_method), 'i', 0, methkey },
+    { BER_BVC("timeout="), offsetof(slap_bindconf, sb_timeout_api), 'i', 0, NULL },
+    { BER_BVC("network-timeout="), offsetof(slap_bindconf, sb_timeout_net), 'i', 0, NULL },
+    { BER_BVC("binddn="), offsetof(slap_bindconf, sb_binddn), 'b', 1, NULL },
+    { BER_BVC("credentials="), offsetof(slap_bindconf, sb_cred), 'b', 1, NULL },
+    { BER_BVC("saslmech="), offsetof(slap_bindconf, sb_saslmech), 'b', 0, NULL },
+    { BER_BVC("secprops="), offsetof(slap_bindconf, sb_secprops), 's', 0, NULL },
+    { BER_BVC("realm="), offsetof(slap_bindconf, sb_realm), 'b', 0, NULL },
+    { BER_BVC("authcID="), offsetof(slap_bindconf, sb_authcId), 'b', 1, NULL },
+    { BER_BVC("authzID="), offsetof(slap_bindconf, sb_authzId), 'b', 1, NULL },
+    { BER_BVC("keepalive="), offsetof(slap_bindconf, sb_keepalive), 'x', 0, (slap_verbmasks *)slap_keepalive_parse },
+#ifdef HAVE_TLS
+    { BER_BVC("starttls="), offsetof(slap_bindconf, sb_tls), 'i', 0, tlskey },
+    { BER_BVC("tls_cert="), offsetof(slap_bindconf, sb_tls_cert), 's', 1, NULL },
+    { BER_BVC("tls_key="), offsetof(slap_bindconf, sb_tls_key), 's', 1, NULL },
+    { BER_BVC("tls_cacert="), offsetof(slap_bindconf, sb_tls_cacert), 's', 1, NULL },
+    { BER_BVC("tls_cacertdir="), offsetof(slap_bindconf, sb_tls_cacertdir), 's', 1, NULL },
+    { BER_BVC("tls_reqcert="), offsetof(slap_bindconf, sb_tls_reqcert), 's', 0, NULL },
+    { BER_BVC("tls_reqsan="), offsetof(slap_bindconf, sb_tls_reqsan), 's', 0, NULL },
+    { BER_BVC("tls_cipher_suite="), offsetof(slap_bindconf, sb_tls_cipher_suite), 's', 0, NULL },
+    { BER_BVC("tls_protocol_min="), offsetof(slap_bindconf, sb_tls_protocol_min), 's', 0, NULL },
+    { BER_BVC("tls_ecname="), offsetof(slap_bindconf, sb_tls_ecname), 's', 0, NULL },
+#ifdef HAVE_OPENSSL
+    { BER_BVC("tls_crlcheck="), offsetof(slap_bindconf, sb_tls_crlcheck), 's', 0, NULL },
+#endif
+#endif
+    { BER_BVNULL, 0, 0, 0, NULL }
+};
+
+/*
+ * 's': char *
+ * 'b': struct berval
+ * 'i': int; if !NULL, compute using ((slap_verbmasks *)aux)
+ * 'u': unsigned
+ * 'I': long
+ * 'U': unsigned long
+ */
+
+int
+slap_cf_aux_table_parse(
+        const char *word,
+        void *dst,
+        slap_cf_aux_table *tab0,
+        LDAP_CONST char *tabmsg )
+{
+    int rc = SLAP_CONF_UNKNOWN;
+    slap_cf_aux_table *tab;
+
+    for ( tab = tab0; !BER_BVISNULL( &tab->key ); tab++ ) {
+        if ( !strncasecmp( word, tab->key.bv_val, tab->key.bv_len ) ) {
+            char **cptr;
+            int *iptr, j;
+            unsigned *uptr;
+            long *lptr;
+            unsigned long *ulptr;
+            struct berval *bptr;
+            const char *val = word + tab->key.bv_len;
+
+            switch ( tab->type ) {
+                case 's':
+                    cptr = (char **)( (char *)dst + tab->off );
+                    *cptr = ch_strdup( val );
+                    rc = 0;
+                    break;
+
+                case 'b':
+                    bptr = (struct berval *)( (char *)dst + tab->off );
+                    assert( tab->aux == NULL );
+                    ber_str2bv( val, 0, 1, bptr );
+                    rc = 0;
+                    break;
+
+                case 'i':
+                    iptr = (int *)( (char *)dst + tab->off );
+
+                    if ( tab->aux != NULL ) {
+                        slap_verbmasks *aux = (slap_verbmasks *)tab->aux;
+
+                        assert( aux != NULL );
+
+                        rc = 1;
+                        for ( j = 0; !BER_BVISNULL( &aux[j].word ); j++ ) {
+                            if ( !strcasecmp( val, aux[j].word.bv_val ) ) {
+                                *iptr = aux[j].mask;
+                                rc = 0;
+                                break;
+                            }
+                        }
+
+                    } else {
+                        rc = lutil_atoix( iptr, val, 0 );
+                    }
+                    break;
+
+                case 'u':
+                    uptr = (unsigned *)( (char *)dst + tab->off );
+
+                    rc = lutil_atoux( uptr, val, 0 );
+                    break;
+
+                case 'I':
+                    lptr = (long *)( (char *)dst + tab->off );
+
+                    rc = lutil_atolx( lptr, val, 0 );
+                    break;
+
+                case 'U':
+                    ulptr = (unsigned long *)( (char *)dst + tab->off );
+
+                    rc = lutil_atoulx( ulptr, val, 0 );
+                    break;
+
+                case 'x':
+                    if ( tab->aux != NULL ) {
+                        struct berval value;
+                        slap_cf_aux_table_parse_x *func =
+                                (slap_cf_aux_table_parse_x *)tab->aux;
+
+                        ber_str2bv( val, 0, 1, &value );
+
+                        rc = func( &value, (void *)( (char *)dst + tab->off ),
+                                tab, tabmsg, 0 );
+
+                    } else {
+                        rc = 1;
+                    }
+                    break;
+            }
+
+            if ( rc ) {
+                Debug( LDAP_DEBUG_ANY, "invalid %s value %s\n", tabmsg, word );
+            }
+
+            return rc;
+        }
+    }
+
+    return rc;
+}
+
+int
+slap_cf_aux_table_unparse(
+        void *src,
+        struct berval *bv,
+        slap_cf_aux_table *tab0 )
+{
+    char buf[AC_LINE_MAX], *ptr;
+    slap_cf_aux_table *tab;
+    struct berval tmp;
+
+    ptr = buf;
+    for ( tab = tab0; !BER_BVISNULL( &tab->key ); tab++ ) {
+        char **cptr;
+        int *iptr, i;
+        unsigned *uptr;
+        long *lptr;
+        unsigned long *ulptr;
+        struct berval *bptr;
+
+        cptr = (char **)( (char *)src + tab->off );
+
+        switch ( tab->type ) {
+            case 'b':
+                bptr = (struct berval *)( (char *)src + tab->off );
+                cptr = &bptr->bv_val;
+
+            case 's':
+                if ( *cptr ) {
+                    *ptr++ = ' ';
+                    ptr = lutil_strcopy( ptr, tab->key.bv_val );
+                    if ( tab->quote ) *ptr++ = '"';
+                    ptr = lutil_strcopy( ptr, *cptr );
+                    if ( tab->quote ) *ptr++ = '"';
+                }
+                break;
+
+            case 'i':
+                iptr = (int *)( (char *)src + tab->off );
+
+                if ( tab->aux != NULL ) {
+                    slap_verbmasks *aux = (slap_verbmasks *)tab->aux;
+
+                    for ( i = 0; !BER_BVISNULL( &aux[i].word ); i++ ) {
+                        if ( *iptr == aux[i].mask ) {
+                            *ptr++ = ' ';
+                            ptr = lutil_strcopy( ptr, tab->key.bv_val );
+                            ptr = lutil_strcopy( ptr, aux[i].word.bv_val );
+                            break;
+                        }
+                    }
+
+                } else {
+                    *ptr++ = ' ';
+                    ptr = lutil_strcopy( ptr, tab->key.bv_val );
+                    ptr += snprintf( ptr, sizeof(buf) - ( ptr - buf ), "%d",
+                            *iptr );
+                }
+                break;
+
+            case 'u':
+                uptr = (unsigned *)( (char *)src + tab->off );
+                *ptr++ = ' ';
+                ptr = lutil_strcopy( ptr, tab->key.bv_val );
+                ptr += snprintf( ptr, sizeof(buf) - ( ptr - buf ), "%u",
+                        *uptr );
+                break;
+
+            case 'I':
+                lptr = (long *)( (char *)src + tab->off );
+                *ptr++ = ' ';
+                ptr = lutil_strcopy( ptr, tab->key.bv_val );
+                ptr += snprintf( ptr, sizeof(buf) - ( ptr - buf ), "%ld",
+                        *lptr );
+                break;
+
+            case 'U':
+                ulptr = (unsigned long *)( (char *)src + tab->off );
+                *ptr++ = ' ';
+                ptr = lutil_strcopy( ptr, tab->key.bv_val );
+                ptr += snprintf( ptr, sizeof(buf) - ( ptr - buf ), "%lu",
+                        *ulptr );
+                break;
+
+            case 'x': {
+                char *saveptr = ptr;
+                *ptr++ = ' ';
+                ptr = lutil_strcopy( ptr, tab->key.bv_val );
+                if ( tab->quote ) *ptr++ = '"';
+                if ( tab->aux != NULL ) {
+                    struct berval value;
+                    slap_cf_aux_table_parse_x *func =
+                            (slap_cf_aux_table_parse_x *)tab->aux;
+                    int rc;
+
+                    value.bv_val = ptr;
+                    value.bv_len = buf + sizeof(buf) - ptr;
+
+                    rc = func( &value, (void *)( (char *)src + tab->off ), tab,
+                            "(unparse)", 1 );
+                    if ( rc == 0 ) {
+                        if ( value.bv_len ) {
+                            ptr += value.bv_len;
+                        } else {
+                            ptr = saveptr;
+                            break;
+                        }
+                    }
+                }
+                if ( tab->quote ) *ptr++ = '"';
+            } break;
+
+            default:
+                assert(0);
+        }
+    }
+    tmp.bv_val = buf;
+    tmp.bv_len = ptr - buf;
+    ber_dupbv( bv, &tmp );
+    return 0;
+}
+
+int
+slap_tls_get_config( LDAP *ld, int opt, char **val )
+{
+#ifdef HAVE_TLS
+    slap_verbmasks *keys;
+    int i, ival;
+
+    *val = NULL;
+    switch ( opt ) {
+        case LDAP_OPT_X_TLS_CRLCHECK:
+            keys = crlkeys;
+            break;
+        case LDAP_OPT_X_TLS_REQUIRE_CERT:
+            keys = vfykeys;
+            break;
+        case LDAP_OPT_X_TLS_PROTOCOL_MIN: {
+            char buf[8];
+            ldap_pvt_tls_get_option( ld, opt, &ival );
+            snprintf( buf, sizeof(buf), "%d.%d",
+                    ( ival >> 8 ) & 0xff, ival & 0xff );
+            *val = ch_strdup( buf );
+            return 0;
+        }
+        default:
+            return -1;
+    }
+    ldap_pvt_tls_get_option( ld, opt, &ival );
+    for ( i = 0; !BER_BVISNULL( &keys[i].word ); i++ ) {
+        if ( keys[i].mask == ival ) {
+            *val = ch_strdup( keys[i].word.bv_val );
+            return 0;
+        }
+    }
+#endif
+    return -1;
+}
+
+int
+bindconf_parse( const char *word, slap_bindconf *bc )
+{
+    return slap_cf_aux_table_parse( word, bc, bindkey, "bind config" );
+}
+
+int
+bindconf_unparse( slap_bindconf *bc, struct berval *bv )
+{
+    return slap_cf_aux_table_unparse( bc, bv, bindkey );
+}
+
+void
+bindconf_free( slap_bindconf *bc )
+{
+    if ( !BER_BVISNULL( &bc->sb_uri ) ) {
+        ch_free( bc->sb_uri.bv_val );
+        BER_BVZERO( &bc->sb_uri );
+    }
+    if ( !BER_BVISNULL( &bc->sb_binddn ) ) {
+        ch_free( bc->sb_binddn.bv_val );
+        BER_BVZERO( &bc->sb_binddn );
+    }
+    if ( !BER_BVISNULL( &bc->sb_cred ) ) {
+        ch_free( bc->sb_cred.bv_val );
+        BER_BVZERO( &bc->sb_cred );
+    }
+    if ( !BER_BVISNULL( &bc->sb_saslmech ) ) {
+        ch_free( bc->sb_saslmech.bv_val );
+        BER_BVZERO( &bc->sb_saslmech );
+    }
+    if ( bc->sb_secprops ) {
+        ch_free( bc->sb_secprops );
+        bc->sb_secprops = NULL;
+    }
+    if ( !BER_BVISNULL( &bc->sb_realm ) ) {
+        ch_free( bc->sb_realm.bv_val );
+        BER_BVZERO( &bc->sb_realm );
+    }
+    if ( !BER_BVISNULL( &bc->sb_authcId ) ) {
+        ch_free( bc->sb_authcId.bv_val );
+        BER_BVZERO( &bc->sb_authcId );
+    }
+    if ( !BER_BVISNULL( &bc->sb_authzId ) ) {
+        ch_free( bc->sb_authzId.bv_val );
+        BER_BVZERO( &bc->sb_authzId );
+    }
+#ifdef HAVE_TLS
+    if ( bc->sb_tls_cert ) {
+        ch_free( bc->sb_tls_cert );
+        bc->sb_tls_cert = NULL;
+    }
+    if ( bc->sb_tls_key ) {
+        ch_free( bc->sb_tls_key );
+        bc->sb_tls_key = NULL;
+    }
+    if ( bc->sb_tls_cacert ) {
+        ch_free( bc->sb_tls_cacert );
+        bc->sb_tls_cacert = NULL;
+    }
+    if ( bc->sb_tls_cacertdir ) {
+        ch_free( bc->sb_tls_cacertdir );
+        bc->sb_tls_cacertdir = NULL;
+    }
+    if ( bc->sb_tls_reqcert ) {
+        ch_free( bc->sb_tls_reqcert );
+        bc->sb_tls_reqcert = NULL;
+    }
+    if ( bc->sb_tls_cipher_suite ) {
+        ch_free( bc->sb_tls_cipher_suite );
+        bc->sb_tls_cipher_suite = NULL;
+    }
+    if ( bc->sb_tls_protocol_min ) {
+        ch_free( bc->sb_tls_protocol_min );
+        bc->sb_tls_protocol_min = NULL;
+    }
+#ifdef HAVE_OPENSSL_CRL
+    if ( bc->sb_tls_crlcheck ) {
+        ch_free( bc->sb_tls_crlcheck );
+        bc->sb_tls_crlcheck = NULL;
+    }
+#endif
+    if ( bc->sb_tls_ctx ) {
+        ldap_pvt_tls_ctx_free( bc->sb_tls_ctx );
+        bc->sb_tls_ctx = NULL;
+    }
+#endif
+}
+
+void
+bindconf_tls_defaults( slap_bindconf *bc )
+{
+#ifdef HAVE_TLS
+    if ( bc->sb_tls_do_init ) {
+        if ( !bc->sb_tls_cacert )
+            ldap_pvt_tls_get_option( slap_tls_ld, LDAP_OPT_X_TLS_CACERTFILE,
+                    &bc->sb_tls_cacert );
+        if ( !bc->sb_tls_cacertdir )
+            ldap_pvt_tls_get_option( slap_tls_ld, LDAP_OPT_X_TLS_CACERTDIR,
+                    &bc->sb_tls_cacertdir );
+        if ( !bc->sb_tls_cert )
+            ldap_pvt_tls_get_option(
+                    slap_tls_ld, LDAP_OPT_X_TLS_CERTFILE, &bc->sb_tls_cert );
+        if ( !bc->sb_tls_key )
+            ldap_pvt_tls_get_option(
+                    slap_tls_ld, LDAP_OPT_X_TLS_KEYFILE, &bc->sb_tls_key );
+        if ( !bc->sb_tls_cipher_suite )
+            ldap_pvt_tls_get_option( slap_tls_ld, LDAP_OPT_X_TLS_CIPHER_SUITE,
+                    &bc->sb_tls_cipher_suite );
+        if ( !bc->sb_tls_reqcert ) bc->sb_tls_reqcert = ch_strdup( "demand" );
+#ifdef HAVE_OPENSSL_CRL
+        if ( !bc->sb_tls_crlcheck )
+            slap_tls_get_config( slap_tls_ld, LDAP_OPT_X_TLS_CRLCHECK,
+                    &bc->sb_tls_crlcheck );
+#endif
+    }
+#endif
+}
+
+/* -------------------------------------- */
+
+static char *
+strtok_quote( char *line, char *sep, char **quote_ptr, int *iqp )
+{
+    int inquote;
+    char *tmp;
+    static char *next;
+
+    *quote_ptr = NULL;
+    if ( line != NULL ) {
+        next = line;
+    }
+    while ( *next && strchr( sep, *next ) ) {
+        next++;
+    }
+
+    if ( *next == '\0' ) {
+        next = NULL;
+        return NULL;
+    }
+    tmp = next;
+
+    for ( inquote = 0; *next; ) {
+        switch ( *next ) {
+            case '"':
+                if ( inquote ) {
+                    inquote = 0;
+                } else {
+                    inquote = 1;
+                }
+                AC_MEMCPY( next, next + 1, strlen( next + 1 ) + 1 );
+                break;
+
+            case '\\':
+                if ( next[1] )
+                    AC_MEMCPY( next, next + 1, strlen( next + 1 ) + 1 );
+                next++; /* dont parse the escaped character */
+                break;
+
+            default:
+                if ( !inquote ) {
+                    if ( strchr( sep, *next ) != NULL ) {
+                        *quote_ptr = next;
+                        *next++ = '\0';
+                        return tmp;
+                    }
+                }
+                next++;
+                break;
+        }
+    }
+    *iqp = inquote;
+
+    return tmp;
+}
+
+static char buf[AC_LINE_MAX];
+static char *line;
+static size_t lmax, lcur;
+
+#define CATLINE( buf ) \
+    do { \
+        size_t len = strlen( buf ); \
+        while ( lcur + len + 1 > lmax ) { \
+            lmax += AC_LINE_MAX; \
+            line = (char *)ch_realloc( line, lmax ); \
+        } \
+        strcpy( line + lcur, buf ); \
+        lcur += len; \
+    } while (0)
+
+static void
+fp_getline_init( ConfigArgs *c )
+{
+    c->lineno = -1;
+    buf[0] = '\0';
+}
+
+static int
+fp_getline( FILE *fp, ConfigArgs *c )
+{
+    char *p;
+
+    lcur = 0;
+    CATLINE( buf );
+    c->lineno++;
+
+    /* avoid stack of bufs */
+    if ( strncasecmp( line, "include", STRLENOF("include") ) == 0 ) {
+        buf[0] = '\0';
+        c->line = line;
+        return 1;
+    }
+
+    while ( fgets( buf, sizeof(buf), fp ) ) {
+        p = strchr( buf, '\n' );
+        if ( p ) {
+            if ( p > buf && p[-1] == '\r' ) {
+                --p;
+            }
+            *p = '\0';
+        }
+        /* XXX ugly */
+        c->line = line;
+        if ( line[0] && ( p = line + strlen( line ) - 1 )[0] == '\\' &&
+                p[-1] != '\\' ) {
+            p[0] = '\0';
+            lcur--;
+
+        } else {
+            if ( !isspace( (unsigned char)buf[0] ) ) {
+                return 1;
+            }
+            buf[0] = ' ';
+        }
+        CATLINE( buf );
+        c->lineno++;
+    }
+
+    buf[0] = '\0';
+    c->line = line;
+    return ( line[0] ? 1 : 0 );
+}
+
+int
+config_fp_parse_line( ConfigArgs *c )
+{
+    char *token;
+    static char *const hide[] = { "bindconf", NULL };
+    static char *const raw[] = { NULL };
+    char *quote_ptr;
+    int i = (int)( sizeof(hide) / sizeof(hide[0]) ) - 1;
+    int inquote = 0;
+
+    c->tline = ch_strdup( c->line );
+    c->linelen = strlen( c->line );
+    token = strtok_quote( c->tline, " \t", &quote_ptr, &inquote );
+
+    if ( token )
+        for ( i = 0; hide[i]; i++ )
+            if ( !strcasecmp( token, hide[i] ) ) break;
+    if ( quote_ptr ) *quote_ptr = ' ';
+    Debug( LDAP_DEBUG_CONFIG, "%s (%s%s)\n",
+            c->log, hide[i] ? hide[i] : c->line, hide[i] ? " ***" : "" );
+    if ( quote_ptr ) *quote_ptr = '\0';
+
+    for ( ;; token = strtok_quote( NULL, " \t", &quote_ptr, &inquote ) ) {
+        if ( c->argc >= c->argv_size ) {
+            char **tmp;
+            tmp = ch_realloc( c->argv,
+                    ( c->argv_size + ARGS_STEP ) * sizeof(*c->argv) );
+            if ( !tmp ) {
+                Debug( LDAP_DEBUG_ANY, "%s: out of memory\n", c->log );
+                return -1;
+            }
+            c->argv = tmp;
+            c->argv_size += ARGS_STEP;
+        }
+        if ( token == NULL ) break;
+        c->argv[c->argc++] = token;
+    }
+    c->argv[c->argc] = NULL;
+    if ( inquote ) {
+        /* these directives parse c->line independently of argv tokenizing */
+        for ( i = 0; raw[i]; i++ )
+            if ( !strcasecmp( c->argv[0], raw[i] ) ) return 0;
+
+        Debug( LDAP_DEBUG_ANY, "%s: unterminated quoted string \"%s\"\n",
+                c->log, c->argv[c->argc - 1] );
+        return -1;
+    }
+    return 0;
+}
+
+void
+config_destroy( void )
+{
+    free( line );
+    if ( slapd_args_file ) free( slapd_args_file );
+    if ( slapd_pid_file ) free( slapd_pid_file );
+    loglevel_destroy();
+}
+
+/* See if the given URL (in plain and parsed form) matches
+ * any of the server's listener addresses. Return matching
+ * Listener or NULL for no match.
+ */
+Listener *
+config_check_my_url( const char *url, LDAPURLDesc *lud )
+{
+    Listener **l = slapd_get_listeners();
+    int i, isMe;
+
+    /* Try a straight compare with Listener strings */
+    for ( i = 0; l && l[i]; i++ ) {
+        if ( !strcasecmp( url, l[i]->sl_url.bv_val ) ) {
+            return l[i];
+        }
+    }
+
+    isMe = 0;
+    /* If hostname is empty, or is localhost, or matches
+     * our hostname, this url refers to this host.
+     * Compare it against listeners and ports.
+     */
+    if ( !lud->lud_host || !lud->lud_host[0] ||
+            !strncasecmp(
+                    "localhost", lud->lud_host, STRLENOF("localhost") ) ||
+            !strcasecmp( global_host, lud->lud_host ) ) {
+        for ( i = 0; l && l[i]; i++ ) {
+            LDAPURLDesc *lu2;
+            ldap_url_parse( l[i]->sl_url.bv_val, &lu2 );
+            do {
+                if ( strcasecmp( lud->lud_scheme, lu2->lud_scheme ) ) break;
+                if ( lud->lud_port != lu2->lud_port ) break;
+                /* Listener on ANY address */
+                if ( !lu2->lud_host || !lu2->lud_host[0] ) {
+                    isMe = 1;
+                    break;
+                }
+                /* URL on ANY address */
+                if ( !lud->lud_host || !lud->lud_host[0] ) {
+                    isMe = 1;
+                    break;
+                }
+                /* Listener has specific host, must
+                 * match it
+                 */
+                if ( !strcasecmp( lud->lud_host, lu2->lud_host ) ) {
+                    isMe = 1;
+                    break;
+                }
+            } while (0);
+            ldap_free_urldesc( lu2 );
+            if ( isMe ) {
+                return l[i];
+            }
+        }
+    }
+    return NULL;
+}
diff --git a/servers/lloadd/config.h b/servers/lloadd/config.h
new file mode 100644 (file)
index 0000000..fb1e2d0
--- /dev/null
@@ -0,0 +1,147 @@
+/* config.h - configuration abstraction structure */
+/* $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>.
+ */
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#include <ac/string.h>
+
+LDAP_BEGIN_DECL
+
+typedef struct ConfigTable {
+    const char *name;
+    const char *what;
+    int min_args;
+    int max_args;
+    int length;
+    unsigned int arg_type;
+    void *arg_item;
+} ConfigTable;
+
+/* search entries are returned according to this order */
+typedef enum {
+    Cft_Abstract = 0,
+    Cft_Global,
+    Cft_Module,
+    Cft_Schema,
+    Cft_Backend,
+    Cft_Database,
+    Cft_Overlay,
+    Cft_Misc /* backend/overlay defined */
+} ConfigType;
+
+#define ARGS_USERLAND 0x00000fff
+
+/* types are enumerated, not a bitmask */
+#define ARGS_TYPES 0x0000f000
+#define ARG_INT 0x00001000
+#define ARG_LONG 0x00002000
+#define ARG_BER_LEN_T 0x00003000
+#define ARG_ON_OFF 0x00004000
+#define ARG_STRING 0x00005000
+#define ARG_BERVAL 0x00006000
+#define ARG_UINT 0x00008000
+#define ARG_ULONG 0x0000a000
+#define ARG_BINARY 0x0000b000
+
+#define ARGS_SYNTAX 0xffff0000
+#define ARG_IGNORED 0x00080000
+#define ARG_PAREN 0x01000000
+#define ARG_NONZERO 0x02000000
+#define ARG_NO_INSERT 0x04000000 /* no arbitrary inserting */
+#define ARG_NO_DELETE 0x08000000 /* no runtime deletes */
+#define ARG_UNIQUE 0x10000000
+#define ARG_QUOTE 0x20000000 /* wrap with quotes before parsing */
+#define ARG_OFFSET 0x40000000
+#define ARG_MAGIC 0x80000000
+
+#define ARG_BAD_CONF 0xdead0000 /* overload return values */
+
+struct config_args_s;
+
+typedef int (ConfigDriver)( struct config_args_s *c );
+
+struct config_reply_s {
+    int err;
+    char msg[SLAP_TEXT_BUFLEN];
+};
+
+typedef struct config_args_s {
+    int argc;
+    char **argv;
+    int argv_size;
+    char *line;
+    char *tline;
+    const char *fname;
+    int lineno;
+    int linelen;
+    char log[MAXPATHLEN + STRLENOF(": line ") +
+            LDAP_PVT_INTTYPE_CHARS(unsigned long)];
+#define cr_msg reply.msg
+    ConfigReply reply;
+    int depth;
+    int valx; /* multi-valued value index */
+    /* parsed first val for simple cases */
+    union {
+        int v_int;
+        unsigned v_uint;
+        long v_long;
+        size_t v_ulong;
+        ber_len_t v_ber_t;
+        char *v_string;
+        struct berval v_bv;
+    } values;
+    /* return values for emit mode */
+    BerVarray rvalue_vals;
+    BerVarray rvalue_nvals;
+#define SLAP_CONFIG_EMIT 0x2000 /* emit instead of set */
+#define SLAP_CONFIG_ADD 0x4000 /* config file add vs LDAP add */
+    int op;
+    int type;         /* ConfigTable.arg_type & ARGS_USERLAND */
+    void *ca_private; /* anything */
+#ifndef SLAP_CONFIG_CLEANUP_MAX
+#define SLAP_CONFIG_CLEANUP_MAX 16
+#endif
+    ConfigDriver *cleanups[SLAP_CONFIG_CLEANUP_MAX];
+    ConfigType table; /* which config table did we come from */
+    int num_cleanups;
+} ConfigArgs;
+
+#define value_int values.v_int
+#define value_uint values.v_uint
+#define value_long values.v_long
+#define value_ulong values.v_ulong
+#define value_ber_t values.v_ber_t
+#define value_string values.v_string
+#define value_bv values.v_bv
+
+int config_fp_parse_line( ConfigArgs *c );
+
+int config_set_vals( ConfigTable *ct, ConfigArgs *c );
+int config_add_vals( ConfigTable *ct, ConfigArgs *c );
+
+int config_push_cleanup( ConfigArgs *c, ConfigDriver *cleanup );
+
+void init_config_argv( ConfigArgs *c );
+int read_config_file( const char *fname, int depth, ConfigArgs *cf, ConfigTable *cft );
+
+ConfigTable *config_find_keyword( ConfigTable *ct, ConfigArgs *c );
+
+Listener *config_check_my_url( const char *url, LDAPURLDesc *lud );
+
+LDAP_END_DECL
+
+#endif /* CONFIG_H */
diff --git a/servers/lloadd/connection.c b/servers/lloadd/connection.c
new file mode 100644 (file)
index 0000000..f1d4ea5
--- /dev/null
@@ -0,0 +1,148 @@
+/* $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>.
+ */
+/* 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>
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#include <ac/socket.h>
+#include <ac/errno.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#include "lutil.h"
+#include "slap.h"
+
+static ldap_pvt_thread_mutex_t conn_nextid_mutex;
+static unsigned long conn_nextid = 0;
+
+static void
+connection_assign_nextid( Connection *conn )
+{
+    ldap_pvt_thread_mutex_lock( &conn_nextid_mutex );
+    conn->c_connid = conn_nextid++;
+    ldap_pvt_thread_mutex_unlock( &conn_nextid_mutex );
+}
+
+void
+connection_destroy( Connection *c )
+{
+    assert( c );
+
+    ldap_pvt_thread_mutex_destroy( &c->c_io_mutex );
+    ldap_pvt_thread_mutex_destroy( &c->c_mutex );
+
+    ber_sockbuf_free( c->c_sb );
+    ch_free( c );
+}
+
+Connection *
+connection_init( ber_socket_t s, const char *peername, int flags )
+{
+    Connection *c;
+
+    assert( peername != NULL );
+
+#ifndef HAVE_TLS
+    assert( !(flags & CONN_IS_TLS) );
+#endif
+
+    if ( s == AC_SOCKET_INVALID ) {
+        Debug( LDAP_DEBUG_ANY, "connection_init: "
+                "init of socket fd=%ld invalid.\n",
+                (long)s );
+        return NULL;
+    }
+
+    assert( s >= 0 );
+
+    c = ch_calloc( 1, sizeof(Connection) );
+
+    c->c_sb = ber_sockbuf_alloc();
+    ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_SET_FD, &s );
+
+    {
+        ber_len_t max = sockbuf_max_incoming;
+        ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_SET_MAX_INCOMING, &max );
+    }
+
+#ifdef LDAP_PF_LOCAL
+    if ( flags & CONN_IS_IPC ) {
+#ifdef LDAP_DEBUG
+        ber_sockbuf_add_io( c->c_sb, &ber_sockbuf_io_debug,
+                LBER_SBIOD_LEVEL_PROVIDER, (void *)"ipc_" );
+#endif
+        ber_sockbuf_add_io( c->c_sb, &ber_sockbuf_io_fd,
+                LBER_SBIOD_LEVEL_PROVIDER, (void *)&s );
+#ifdef LDAP_PF_LOCAL_SENDMSG
+        if ( !BER_BVISEMPTY( peerbv ) )
+            ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_UNGET_BUF, peerbv );
+#endif
+    } else
+#endif /* LDAP_PF_LOCAL */
+    {
+#ifdef LDAP_DEBUG
+        ber_sockbuf_add_io( c->c_sb, &ber_sockbuf_io_debug,
+                LBER_SBIOD_LEVEL_PROVIDER, (void *)"tcp_" );
+#endif
+        ber_sockbuf_add_io( c->c_sb, &ber_sockbuf_io_tcp,
+                LBER_SBIOD_LEVEL_PROVIDER, (void *)&s );
+    }
+
+#ifdef LDAP_DEBUG
+    ber_sockbuf_add_io(
+            c->c_sb, &ber_sockbuf_io_debug, INT_MAX, (void *)"lload_" );
+#endif
+
+#ifdef HAVE_TLS
+    if ( flags & CONN_IS_TLS ) {
+        /* TODO: will need an asynchronous TLS implementation in libldap */
+        assert(0);
+        c->c_is_tls = 1;
+        c->c_needs_tls_accept = 1;
+    } else {
+        c->c_is_tls = 0;
+        c->c_needs_tls_accept = 0;
+    }
+#endif
+
+    ldap_pvt_thread_mutex_init( &c->c_mutex );
+    ldap_pvt_thread_mutex_init( &c->c_io_mutex );
+
+    connection_assign_nextid( c );
+
+    Debug( LDAP_DEBUG_CONNS, "connection_init: "
+            "connection connid=%lu allocated for socket fd=%d\n",
+            c->c_connid, s );
+
+    return c;
+fail:
+    connection_destroy( c );
+    return NULL;
+}
diff --git a/servers/lloadd/daemon.c b/servers/lloadd/daemon.c
new file mode 100644 (file)
index 0000000..3017e34
--- /dev/null
@@ -0,0 +1,1400 @@
+/* $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>
+#include <event2/listener.h>
+
+#include "slap.h"
+#include "ldap_pvt_thread.h"
+#include "lutil.h"
+
+#include "ldap_rq.h"
+
+#ifdef HAVE_TCPD
+int allow_severity = LOG_INFO;
+int deny_severity = LOG_NOTICE;
+#endif /* TCP Wrappers */
+
+#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 */
+
+#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;
+
+#ifndef SLAPD_MAX_DAEMON_THREADS
+#define SLAPD_MAX_DAEMON_THREADS 16
+#endif
+int slapd_daemon_threads = 1;
+int slapd_daemon_mask;
+
+#ifdef LDAP_TCP_BUFFER
+int slapd_tcp_rmem;
+int slapd_tcp_wmem;
+#endif /* LDAP_TCP_BUFFER */
+
+struct event_base *listener_base = NULL;
+Listener **slap_listeners = NULL;
+static volatile sig_atomic_t listening = 1; /* 0 when slap_listeners closed */
+static ldap_pvt_thread_t listener_tid, *daemon_tid;
+
+#ifndef SLAPD_LISTEN_BACKLOG
+#define SLAPD_LISTEN_BACKLOG 1024
+#endif /* ! SLAPD_LISTEN_BACKLOG */
+
+#define DAEMON_ID(fd) ( fd & slapd_daemon_mask )
+
+static int emfile;
+
+static volatile int waking;
+#define WAKE_DAEMON( l, w ) \
+    do { \
+        if ( w ) { \
+            event_active( slap_daemon[l].wakeup_event, EV_WRITE, 0 ); \
+        } \
+    } while (0)
+
+volatile sig_atomic_t slapd_shutdown = 0;
+volatile sig_atomic_t slapd_gentle_shutdown = 0;
+volatile sig_atomic_t slapd_abrupt_shutdown = 0;
+
+#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;
+
+typedef struct slap_daemon_st {
+    ldap_pvt_thread_mutex_t sd_mutex;
+
+    struct event_base *base;
+    struct event *wakeup_event;
+} slap_daemon_st;
+
+static slap_daemon_st slap_daemon[SLAPD_MAX_DAEMON_THREADS];
+
+static void daemon_wakeup_cb( evutil_socket_t sig, short what, void *arg );
+
+/*
+ * Remove the descriptor from daemon control
+ */
+void
+slapd_remove( ber_socket_t s, Sockbuf *sb, int wasactive, int wake, int locked )
+{
+    int id = DAEMON_ID(s);
+
+    if ( !locked ) ldap_pvt_thread_mutex_lock( &slap_daemon[id].sd_mutex );
+
+    if ( sb ) ber_sockbuf_free( sb );
+
+    /* If we ran out of file descriptors, we dropped a listener from
+     * the select() loop. Now that we're removing a session from our
+     * control, we can try to resume a dropped listener to use.
+     */
+    if ( emfile && listening ) {
+        int i;
+        for ( i = 0; slap_listeners[i] != NULL; i++ ) {
+            Listener *lr = slap_listeners[i];
+
+            if ( lr->sl_sd == AC_SOCKET_INVALID ) continue;
+            if ( lr->sl_sd == s ) continue;
+            if ( lr->sl_mute ) {
+                lr->sl_mute = 0;
+                emfile--;
+                if ( DAEMON_ID(lr->sl_sd) != id )
+                    WAKE_DAEMON( DAEMON_ID(lr->sl_sd), wake );
+                break;
+            }
+        }
+        /* Walked the entire list without enabling anything; emfile
+         * counter is stale. Reset it.
+         */
+        if ( slap_listeners[i] == NULL ) emfile = 0;
+    }
+    ldap_pvt_thread_mutex_unlock( &slap_daemon[id].sd_mutex );
+    WAKE_DAEMON( id, wake || slapd_gentle_shutdown == 2 );
+}
+
+static void
+slapd_close( ber_socket_t s )
+{
+    Debug( LDAP_DEBUG_CONNS, "slapd_close: "
+            "closing %ld\n",
+            (long)s );
+    tcp_close( s );
+}
+
+static void
+slap_free_listener_addresses( struct sockaddr **sal )
+{
+    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
+slap_get_listener_addresses(
+        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 ) ) {
+            Debug( LDAP_DEBUG_ANY, "slap_get_listener_addresses: "
+                    "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 )) ) {
+            Debug( LDAP_DEBUG_ANY, "slap_get_listener_addresses: "
+                    "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 ) {
+                Debug( LDAP_DEBUG_ANY, "slap_get_listener_addresses: "
+                        "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 ) {
+                Debug( LDAP_DEBUG_ANY, "slap_get_listener_addresses: "
+                        "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:
+    slap_free_listener_addresses(*sal);
+    return -1;
+}
+
+static int
+slap_open_listener( const char *url, int *listeners, int *cur )
+{
+    int num, tmp, rc;
+    Listener l;
+    Listener *li;
+    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 ) {
+        Debug( LDAP_DEBUG_ANY, "slap_open_listener: "
+                "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 ) ) {
+        Debug( LDAP_DEBUG_ANY, "slap_open_listener: "
+                "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' ) {
+            err = slap_get_listener_addresses( LDAPI_SOCK, 0, &sal );
+        } else {
+            err = slap_get_listener_addresses( lud->lud_host, 0, &sal );
+        }
+#else /* ! LDAP_PF_LOCAL */
+
+        Debug( LDAP_DEBUG_ANY, "slap_open_listener: "
+                "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 ) {
+            err = slap_get_listener_addresses( NULL, port, &sal );
+        } else {
+            err = slap_get_listener_addresses( lud->lud_host, port, &sal );
+        }
+    }
+
+#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 ) {
+        slap_free_listener_addresses( sal );
+        return -1;
+    }
+
+    /* If we got more than one address returned, we need to make space
+     * for it in the slap_listeners array.
+     */
+    for ( num = 0; sal[num]; num++ ) /* empty */;
+    if ( num > 1 ) {
+        *listeners += num - 1;
+        slap_listeners = ch_realloc(
+                slap_listeners, ( *listeners + 1 ) * sizeof(Listener *) );
+    }
+
+    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();
+            Debug( LDAP_DEBUG_ANY, "slap_open_listener: "
+                    "%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();
+                Debug( LDAP_DEBUG_ANY, "slap_open_listener(%ld): "
+                        "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();
+                    Debug( LDAP_DEBUG_ANY, "slap_open_listener(%ld): "
+                            "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();
+            Debug( LDAP_DEBUG_ANY, "slap_open_listener: "
+                    "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:
+                Debug( LDAP_DEBUG_ANY, "slap_open_listener: "
+                        "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 );
+        li = ch_malloc( sizeof(Listener) );
+        *li = l;
+        slap_listeners[*cur] = li;
+        (*cur)++;
+        sal++;
+    }
+
+    slap_free_listener_addresses( psal );
+
+    if ( l.sl_url.bv_val == NULL ) {
+        Debug( LDAP_DEBUG_ANY, "slap_open_listener: "
+                "failed on %s\n",
+                url );
+        return -1;
+    }
+
+    Debug( LDAP_DEBUG_TRACE, "slap_open_listener: "
+            "listener initialized %s\n",
+            l.sl_url.bv_val );
+
+    return 0;
+}
+
+int lloadd_inited = 0;
+
+int
+slapd_daemon_init( const char *urls )
+{
+    int i, j, n;
+    char **u;
+
+    Debug( LDAP_DEBUG_ARGS, "slapd_daemon_init: %s\n",
+            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 ) {
+        Debug( LDAP_DEBUG_ANY, "slapd_daemon_init: "
+                "no urls (%s) provided.\n",
+                urls );
+        if ( u ) ldap_charray_free( u );
+        return -1;
+    }
+
+    for ( i = 0; u[i] != NULL; i++ ) {
+        Debug( LDAP_DEBUG_TRACE, "slapd_daemon_init: "
+                "listen on %s\n",
+                u[i] );
+    }
+
+    if ( i == 0 ) {
+        Debug( LDAP_DEBUG_ANY, "slapd_daemon_init: "
+                "no listeners to open (%s)\n",
+                urls );
+        ldap_charray_free( u );
+        return -1;
+    }
+
+    Debug( LDAP_DEBUG_TRACE, "slapd_daemon_init: "
+            "%d listeners to open...\n",
+            i );
+    slap_listeners = ch_malloc( ( i + 1 ) * sizeof(Listener *) );
+
+    for ( n = 0, j = 0; u[n]; n++ ) {
+        if ( slap_open_listener( u[n], &i, &j ) ) {
+            ldap_charray_free( u );
+            return -1;
+        }
+    }
+    slap_listeners[j] = NULL;
+
+    Debug( LDAP_DEBUG_TRACE, "slapd_daemon_init: "
+            "%d listeners opened\n",
+            i );
+
+    ldap_charray_free( u );
+
+    return !i;
+}
+
+int
+slapd_daemon_destroy( void )
+{
+    if ( lloadd_inited ) {
+        int i;
+
+        for ( i = 0; i < slapd_daemon_threads; i++ ) {
+            ldap_pvt_thread_mutex_destroy( &slap_daemon[i].sd_mutex );
+        }
+        lloadd_inited = 0;
+#ifdef HAVE_TCPD
+        ldap_pvt_thread_mutex_destroy( &sd_tcpd_mutex );
+#endif /* TCP Wrappers */
+    }
+
+    return 0;
+}
+
+static void
+close_listeners( int remove )
+{
+    int l;
+
+    if ( !listening ) return;
+    listening = 0;
+
+    for ( l = 0; slap_listeners[l] != NULL; l++ ) {
+        Listener *lr = slap_listeners[l];
+
+        if ( lr->sl_sd != AC_SOCKET_INVALID ) {
+            int s = lr->sl_sd;
+            lr->sl_sd = AC_SOCKET_INVALID;
+            if ( remove ) slapd_remove( s, NULL, 0, 0, 0 );
+
+#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 */
+
+            slapd_close( s );
+        }
+    }
+}
+
+static void
+destroy_listeners( void )
+{
+    Listener *lr, **ll = slap_listeners;
+
+    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 );
+        }
+
+        evconnlistener_free( lr->listener );
+
+        free( lr );
+    }
+
+    free( slap_listeners );
+    slap_listeners = NULL;
+}
+
+static void
+slap_listener(
+        struct evconnlistener *listener,
+        ber_socket_t s,
+        struct sockaddr *a,
+        int len,
+        void *arg )
+{
+    Listener *sl = arg;
+    Connection *c;
+    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];
+
+    Debug( LDAP_DEBUG_TRACE, ">>> slap_listener(%s)\n", sl->sl_url.bv_val );
+
+    peername[0] = '\0';
+
+    /* Resume the listener FD to allow concurrent-processing of
+     * additional incoming connections.
+     */
+    sl->sl_busy = 0;
+
+    tid = DAEMON_ID(s);
+
+    Debug( LDAP_DEBUG_CONNS, "slap_listener: "
+            "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();
+            Debug( LDAP_DEBUG_ANY, "slap_listener(%ld): "
+                    "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();
+            Debug( LDAP_DEBUG_ANY, "slap_listener(%ld): "
+                    "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:
+            slapd_close( s );
+            return;
+    }
+
+#ifdef HAVE_TLS
+    if ( sl->sl_is_tls ) cflag |= CONN_IS_TLS;
+#endif
+    c = client_init( s, sl, peername, slap_daemon[tid].base, cflag );
+
+    if ( !c ) {
+        Debug( LDAP_DEBUG_ANY, "slap_listener: "
+                "client_init(%ld, %s, %s) failed.\n",
+                (long)s, peername, sl->sl_name.bv_val );
+        slapd_close( s );
+    }
+
+    return;
+}
+
+static void *
+slap_listener_thread( void *ctx )
+{
+    int rc = event_base_dispatch( listener_base );
+    Debug( LDAP_DEBUG_ANY, "slap_listener_thread: "
+            "event loop finished: rc=%d\n",
+            rc );
+
+    return (void *)NULL;
+}
+
+static void
+listener_error_cb( struct evconnlistener *lev, void *arg )
+{
+    Listener *l = arg;
+    int err = EVUTIL_SOCKET_ERROR();
+
+    assert( l->listener == lev );
+    if (
+#ifdef EMFILE
+            err == EMFILE ||
+#endif /* EMFILE */
+#ifdef ENFILE
+            err == ENFILE ||
+#endif /* ENFILE */
+            0 ) {
+        ldap_pvt_thread_mutex_lock( &slap_daemon[0].sd_mutex );
+        emfile++;
+        /* Stop listening until an existing session closes */
+        l->sl_mute = 1;
+        evconnlistener_disable( lev );
+        ldap_pvt_thread_mutex_unlock( &slap_daemon[0].sd_mutex );
+    } 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 );
+    }
+}
+
+static int
+slap_listener_activate( void )
+{
+    struct evconnlistener *listener;
+    int l, rc;
+    char ebuf[128];
+
+    listener_base = event_base_new();
+    if ( !listener_base ) return -1;
+
+    for ( l = 0; slap_listeners[l] != NULL; l++ ) {
+        if ( slap_listeners[l]->sl_sd == AC_SOCKET_INVALID ) continue;
+
+            /* FIXME: TCP-only! */
+#ifdef LDAP_TCP_BUFFER
+        if ( 1 ) {
+            int origsize, size, realsize, rc;
+            socklen_t optlen;
+
+            size = 0;
+            if ( slap_listeners[l]->sl_tcp_rmem > 0 ) {
+                size = slap_listeners[l]->sl_tcp_rmem;
+            } else if ( slapd_tcp_rmem > 0 ) {
+                size = slapd_tcp_rmem;
+            }
+
+            if ( size > 0 ) {
+                optlen = sizeof(origsize);
+                rc = getsockopt( slap_listeners[l]->sl_sd, SOL_SOCKET,
+                        SO_RCVBUF, (void *)&origsize, &optlen );
+
+                if ( rc ) {
+                    int err = sock_errno();
+                    Debug( LDAP_DEBUG_ANY, "slap_listener_activate: "
+                            "getsockopt(SO_RCVBUF) failed errno=%d (%s)\n",
+                            err, AC_STRERROR_R( err, ebuf, sizeof(ebuf) ) );
+                }
+
+                optlen = sizeof(size);
+                rc = setsockopt( slap_listeners[l]->sl_sd, SOL_SOCKET,
+                        SO_RCVBUF, (const void *)&size, optlen );
+
+                if ( rc ) {
+                    int err = sock_errno();
+                    Debug( LDAP_DEBUG_ANY, "slapd_listener_activate: "
+                            "setsockopt(SO_RCVBUF) failed errno=%d (%s)\n",
+                            err, sock_errstr( err, ebuf, sizeof(ebuf) ) );
+                }
+
+                optlen = sizeof(realsize);
+                rc = getsockopt( slap_listeners[l]->sl_sd, SOL_SOCKET,
+                        SO_RCVBUF, (void *)&realsize, &optlen );
+
+                if ( rc ) {
+                    int err = sock_errno();
+                    Debug( LDAP_DEBUG_ANY, "slapd_listener_activate: "
+                            "getsockopt(SO_RCVBUF) failed errno=%d (%s)\n",
+                            err, sock_errstr( err, ebuf, sizeof(ebuf) ) );
+                }
+
+                Debug( LDAP_DEBUG_ANY, "slapd_listener_activate: "
+                        "url=%s (#%d) RCVBUF original size=%d requested "
+                        "size=%d real size=%d\n",
+                        slap_listeners[l]->sl_url.bv_val, l, origsize, size,
+                        realsize );
+            }
+
+            size = 0;
+            if ( slap_listeners[l]->sl_tcp_wmem > 0 ) {
+                size = slap_listeners[l]->sl_tcp_wmem;
+            } else if ( slapd_tcp_wmem > 0 ) {
+                size = slapd_tcp_wmem;
+            }
+
+            if ( size > 0 ) {
+                optlen = sizeof(origsize);
+                rc = getsockopt( slap_listeners[l]->sl_sd, SOL_SOCKET,
+                        SO_SNDBUF, (void *)&origsize, &optlen );
+
+                if ( rc ) {
+                    int err = sock_errno();
+                    Debug( LDAP_DEBUG_ANY, "slapd_listener_activate: "
+                            "getsockopt(SO_SNDBUF) failed errno=%d (%s)\n",
+                            err, sock_errstr( err, ebuf, sizeof(ebuf) ) );
+                }
+
+                optlen = sizeof(size);
+                rc = setsockopt( slap_listeners[l]->sl_sd, SOL_SOCKET,
+                        SO_SNDBUF, (const void *)&size, optlen );
+
+                if ( rc ) {
+                    int err = sock_errno();
+                    Debug( LDAP_DEBUG_ANY, "slapd_listener_activate: "
+                            "setsockopt(SO_SNDBUF) failed errno=%d (%s)\n",
+                            err, sock_errstr( err, ebuf, sizeof(ebuf) ) );
+                }
+
+                optlen = sizeof(realsize);
+                rc = getsockopt( slap_listeners[l]->sl_sd, SOL_SOCKET,
+                        SO_SNDBUF, (void *)&realsize, &optlen );
+
+                if ( rc ) {
+                    int err = sock_errno();
+                    Debug( LDAP_DEBUG_ANY, "slapd_listener_activate: "
+                            "getsockopt(SO_SNDBUF) failed errno=%d (%s)\n",
+                            err, sock_errstr( err, ebuf, sizeof(ebuf) ) );
+                }
+
+                Debug( LDAP_DEBUG_ANY, "slapd_listener_activate: "
+                        "url=%s (#%d) SNDBUF original size=%d requested "
+                        "size=%d real size=%d\n",
+                        slap_listeners[l]->sl_url.bv_val, l, origsize, size,
+                        realsize );
+            }
+        }
+#endif /* LDAP_TCP_BUFFER */
+
+        slap_listeners[l]->sl_busy = 1;
+        listener = evconnlistener_new( listener_base, slap_listener,
+                slap_listeners[l], LEV_OPT_THREADSAFE, SLAPD_LISTEN_BACKLOG,
+                slap_listeners[l]->sl_sd );
+        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;
+                struct sockaddr_in sa = slap_listeners[l]->sl_sa.sa_in_addr;
+                struct sockaddr_in6 sa6;
+
+                if ( sa.sin_family == AF_INET &&
+                        sa.sin_addr.s_addr == htonl( INADDR_ANY ) ) {
+                    for ( i = 0; i < l; i++ ) {
+                        sa6 = slap_listeners[i]->sl_sa.sa_in6_addr;
+                        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 */
+                        Debug( LDAP_DEBUG_CONNS, "slap_listener_activate: "
+                                "Attempt to listen to 0.0.0.0 failed, "
+                                "already listening on ::, assuming IPv4 "
+                                "included\n" );
+                        slapd_close( slap_listeners[l]->sl_sd );
+                        slap_listeners[l]->sl_sd = AC_SOCKET_INVALID;
+                        continue;
+                    }
+                }
+            }
+#endif /* LDAP_PF_INET6 */
+            Debug( LDAP_DEBUG_ANY, "slap_listener_activate: "
+                    "listen(%s, 5) failed errno=%d (%s)\n",
+                    slap_listeners[l]->sl_url.bv_val, err,
+                    sock_errstr( err, ebuf, sizeof(ebuf) ) );
+            return -1;
+        }
+
+        slap_listeners[l]->base = listener_base;
+        slap_listeners[l]->listener = listener;
+        evconnlistener_set_error_cb( listener, listener_error_cb );
+    }
+
+    rc = ldap_pvt_thread_create(
+            &listener_tid, 0, slap_listener_thread, slap_listeners[l] );
+
+    if ( rc != 0 ) {
+        Debug( LDAP_DEBUG_ANY, "slap_listener_activate(%d): "
+                "submit failed (%d)\n",
+                slap_listeners[l]->sl_sd, rc );
+    }
+    return rc;
+}
+
+static void *
+slapd_daemon_task( void *ptr )
+{
+    int rc;
+    int tid = (ldap_pvt_thread_t *)ptr - daemon_tid;
+    struct event_base *base;
+    struct event *event;
+
+    base = event_base_new();
+    if ( !base ) {
+        Debug( LDAP_DEBUG_ANY, "slapd_daemon_task: "
+                "failed to acquire event base\n" );
+        return (void *)-1;
+    }
+    slap_daemon[tid].base = base;
+
+    event = event_new( base, -1, EV_WRITE, daemon_wakeup_cb, ptr );
+    if ( !event ) {
+        Debug( LDAP_DEBUG_ANY, "slapd_daemon_task: "
+                "failed to set up the wakeup event\n" );
+        return (void *)-1;
+    }
+    event_add( event, NULL );
+    slap_daemon[tid].wakeup_event = event;
+
+    /* run */
+    rc = event_base_dispatch( base );
+    Debug( LDAP_DEBUG_ANY, "slapd_daemon_task: "
+            "Daemon %d, event loop finished: rc=%d\n",
+            tid, rc );
+
+    if ( !slapd_gentle_shutdown ) {
+        slapd_abrupt_shutdown = 1;
+    }
+
+    return NULL;
+}
+
+int
+slapd_daemon( struct event_base *daemon_base )
+{
+    int i, rc;
+
+    assert( daemon_base != NULL );
+
+    if ( slapd_daemon_threads > SLAPD_MAX_DAEMON_THREADS )
+        slapd_daemon_threads = SLAPD_MAX_DAEMON_THREADS;
+
+    daemon_tid =
+            ch_malloc( slapd_daemon_threads * sizeof(ldap_pvt_thread_t) );
+
+    if ( (rc = slap_listener_activate()) != 0 ) {
+        return rc;
+    }
+
+    for ( i = 0; i < slapd_daemon_threads; i++ ) {
+        ldap_pvt_thread_mutex_init( &slap_daemon[i].sd_mutex );
+        /* threads that handle client and upstream sockets */
+        rc = ldap_pvt_thread_create(
+                &daemon_tid[i], 0, slapd_daemon_task, &daemon_tid[i] );
+
+        if ( rc != 0 ) {
+            Debug( LDAP_DEBUG_ANY, "lloadd startup: "
+                    "listener ldap_pvt_thread_create failed (%d)\n",
+                    rc );
+            return rc;
+        }
+    }
+
+    lloadd_inited = 1;
+    rc = event_base_dispatch( daemon_base );
+    Debug( LDAP_DEBUG_ANY, "lloadd shutdown: "
+            "Main event loop finished: rc=%d\n",
+            rc );
+
+    /* shutdown */
+    event_base_loopexit( listener_base, 0 );
+    close_listeners( 0 );
+
+    /* wait for the listener threads to complete */
+    destroy_listeners();
+
+    for ( i = 0; i < slapd_daemon_threads; i++ )
+        ldap_pvt_thread_join( daemon_tid[i], (void *)NULL );
+
+    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 );
+
+    ch_free( daemon_tid );
+    daemon_tid = NULL;
+
+    slapd_daemon_destroy();
+
+    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 );
+    if ( slapd_shutdown ) {
+        event_base_loopexit( slap_daemon[tid].base, NULL );
+    }
+}
+
+void
+slap_sig_shutdown( evutil_socket_t sig, short what, void *arg )
+{
+    struct event_base *daemon_base = arg;
+    int save_errno = errno;
+    int i;
+
+    /*
+     * If the NT Service Manager is controlling the server, we don't
+     * want SIGBREAK to kill the server. For some strange reason,
+     * SIGBREAK is generated when a user logs out.
+     */
+
+#if defined(HAVE_NT_SERVICE_MANAGER) && defined(SIGBREAK)
+    if ( is_NT_Service && sig == SIGBREAK ) {
+        /* empty */;
+    } else
+#endif /* HAVE_NT_SERVICE_MANAGER && SIGBREAK */
+#ifdef SIGHUP
+    if ( sig == SIGHUP && global_gentlehup && slapd_gentle_shutdown == 0 ) {
+        slapd_gentle_shutdown = 1;
+    } else
+#endif /* SIGHUP */
+    {
+        slapd_shutdown = 1;
+    }
+
+    for ( i = 0; i < slapd_daemon_threads; i++ ) {
+        WAKE_DAEMON( i, 1 );
+    }
+    event_base_loopexit( daemon_base, NULL );
+
+    errno = save_errno;
+}
+
+Listener **
+slapd_get_listeners( void )
+{
+    /* Could return array with no listeners if !listening, but current
+     * callers mostly look at the URLs.  E.g. syncrepl uses this to
+     * identify the server, which means it wants the startup arguments.
+     */
+    return slap_listeners;
+}
+
+/* Reject all incoming requests */
+void
+slap_suspend_listeners( void )
+{
+    int i;
+    for ( i = 0; slap_listeners[i]; i++ ) {
+        slap_listeners[i]->sl_mute = 1;
+        evconnlistener_disable( slap_listeners[i]->listener );
+        listen( slap_listeners[i]->sl_sd, 0 );
+    }
+}
+
+/* Resume after a suspend */
+void
+slap_resume_listeners( void )
+{
+    int i;
+    for ( i = 0; slap_listeners[i]; i++ ) {
+        slap_listeners[i]->sl_mute = 0;
+        listen( slap_listeners[i]->sl_sd, SLAPD_LISTEN_BACKLOG );
+        evconnlistener_enable( slap_listeners[i]->listener );
+    }
+}
diff --git a/servers/lloadd/design.md b/servers/lloadd/design.md
new file mode 120000 (symlink)
index 0000000..757e340
--- /dev/null
@@ -0,0 +1 @@
+../../doc/devel/lloadd/design.md
\ No newline at end of file
diff --git a/servers/lloadd/globals.c b/servers/lloadd/globals.c
new file mode 120000 (symlink)
index 0000000..3277f28
--- /dev/null
@@ -0,0 +1 @@
+../slapd/globals.c
\ No newline at end of file
diff --git a/servers/lloadd/init.c b/servers/lloadd/init.c
new file mode 100644 (file)
index 0000000..07bd849
--- /dev/null
@@ -0,0 +1,144 @@
+/* init.c - initialize various things */
+/* $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>.
+ */
+/* 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/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "slap.h"
+#include "lber_pvt.h"
+
+#include "ldap_rq.h"
+
+/*
+ * read-only global variables or variables only written by the listener
+ * thread (after they are initialized) - no need to protect them with a mutex.
+ */
+int slap_debug = 0;
+
+#ifdef LDAP_DEBUG
+int ldap_syslog = LDAP_DEBUG_STATS;
+#else
+int ldap_syslog;
+#endif
+
+#ifdef LOG_DEBUG
+int ldap_syslog_level = LOG_DEBUG;
+#endif
+
+/*
+ * global variables that need mutex protection
+ */
+ldap_pvt_thread_pool_t connection_pool;
+int connection_pool_max = SLAP_MAX_WORKER_THREADS;
+int connection_pool_queues = 1;
+int slap_tool_thread_max = 1;
+
+static const char *slap_name = NULL;
+int slapMode = SLAP_UNDEFINED_MODE;
+
+int
+slap_init( int mode, const char *name )
+{
+    int rc = LDAP_SUCCESS;
+
+    assert( mode );
+
+    if ( slapMode != SLAP_UNDEFINED_MODE ) {
+        /* Make sure we write something to stderr */
+        slap_debug |= LDAP_DEBUG_NONE;
+        Debug( LDAP_DEBUG_ANY, "%s init: "
+                "init called twice (old=%d, new=%d)\n",
+                name, slapMode, mode );
+
+        return 1;
+    }
+
+    slapMode = mode;
+
+    switch ( slapMode & SLAP_MODE ) {
+        case SLAP_SERVER_MODE:
+            Debug( LDAP_DEBUG_TRACE, "%s init: "
+                    "initiated server.\n",
+                    name );
+
+            slap_name = name;
+
+            ldap_pvt_thread_pool_init_q( &connection_pool, connection_pool_max,
+                    0, connection_pool_queues );
+
+            ldap_pvt_thread_mutex_init( &slapd_rq.rq_mutex );
+            LDAP_STAILQ_INIT( &slapd_rq.task_list );
+            LDAP_STAILQ_INIT( &slapd_rq.run_list );
+
+            break;
+
+        default:
+            slap_debug |= LDAP_DEBUG_NONE;
+            Debug( LDAP_DEBUG_ANY, "%s init: "
+                    "undefined mode (%d).\n",
+                    name, mode );
+
+            rc = 1;
+            break;
+    }
+
+    return rc;
+}
+
+int
+slap_destroy( void )
+{
+    int rc = LDAP_SUCCESS;
+
+    Debug( LDAP_DEBUG_TRACE, "%s destroy: "
+            "freeing system resources.\n",
+            slap_name );
+
+    ldap_pvt_thread_pool_free( &connection_pool );
+
+    switch ( slapMode & SLAP_MODE ) {
+        case SLAP_SERVER_MODE:
+            break;
+
+        default:
+            Debug( LDAP_DEBUG_ANY, "slap_destroy(): "
+                    "undefined mode (%d).\n",
+                    slapMode );
+
+            rc = 1;
+            break;
+    }
+
+    ldap_pvt_thread_destroy();
+
+    /* should destroy the above mutex */
+    return rc;
+}
diff --git a/servers/lloadd/libevent_support.c b/servers/lloadd/libevent_support.c
new file mode 100644 (file)
index 0000000..7a5be04
--- /dev/null
@@ -0,0 +1,161 @@
+/* libevent_support.c - routines to bridge libldap and libevent */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2017-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/time.h>
+
+#include <event2/event.h>
+#include <event2/thread.h>
+
+#include "slap.h"
+#include "ldap_pvt_thread.h"
+
+static void *
+lload_libevent_mutex_init( unsigned locktype )
+{
+    int rc;
+    ldap_pvt_thread_mutex_t *mutex =
+            ch_malloc( sizeof(ldap_pvt_thread_mutex_t) );
+
+    if ( locktype & EVTHREAD_LOCKTYPE_RECURSIVE ) {
+        rc = ldap_pvt_thread_mutex_recursive_init( mutex );
+    } else {
+        rc = ldap_pvt_thread_mutex_init( mutex );
+    }
+    if ( rc ) {
+        ch_free( mutex );
+        mutex = NULL;
+    }
+    return mutex;
+}
+
+static void
+lload_libevent_mutex_destroy( void *lock, unsigned locktype )
+{
+    int rc;
+    ldap_pvt_thread_mutex_t *mutex = lock;
+
+    rc = ldap_pvt_thread_mutex_destroy( mutex );
+    assert( rc == 0 );
+    ch_free( mutex );
+}
+
+static int
+lload_libevent_mutex_lock( unsigned mode, void *lock )
+{
+    ldap_pvt_thread_mutex_t *mutex = lock;
+
+    if ( mode & EVTHREAD_TRY ) {
+        return ldap_pvt_thread_mutex_trylock( mutex );
+    } else {
+        return ldap_pvt_thread_mutex_lock( mutex );
+    }
+}
+
+static int
+lload_libevent_mutex_unlock( unsigned mode, void *lock )
+{
+    ldap_pvt_thread_mutex_t *mutex = lock;
+
+    return ldap_pvt_thread_mutex_unlock( mutex );
+}
+
+static void *
+lload_libevent_cond_init( unsigned condtype )
+{
+    int rc;
+    ldap_pvt_thread_cond_t *cond =
+            ch_malloc( sizeof(ldap_pvt_thread_cond_t) );
+
+    assert( condtype == 0 );
+    rc = ldap_pvt_thread_cond_init( cond );
+    if ( rc ) {
+        ch_free( cond );
+        cond = NULL;
+    }
+    return cond;
+}
+
+static void
+lload_libevent_cond_destroy( void *c )
+{
+    int rc;
+    ldap_pvt_thread_cond_t *cond = c;
+
+    rc = ldap_pvt_thread_cond_destroy( cond );
+    assert( rc == 0 );
+    ch_free( c );
+}
+
+static int
+lload_libevent_cond_signal( void *c, int broadcast )
+{
+    ldap_pvt_thread_cond_t *cond = c;
+
+    if ( broadcast ) {
+        return ldap_pvt_thread_cond_broadcast( cond );
+    } else {
+        return ldap_pvt_thread_cond_signal( cond );
+    }
+}
+
+static int
+lload_libevent_cond_timedwait(
+        void *c,
+        void *lock,
+        const struct timeval *timeout )
+{
+    ldap_pvt_thread_cond_t *cond = c;
+    ldap_pvt_thread_mutex_t *mutex = lock;
+
+    /*
+     * libevent does not seem to request a timeout, this is true as of 2.1.8
+     * that has just been marked the first stable release of the 2.1 series
+     */
+    assert( timeout == NULL );
+
+    return ldap_pvt_thread_cond_wait( cond, mutex );
+}
+
+int
+lload_libevent_init( void )
+{
+    struct evthread_lock_callbacks cbs = {
+        EVTHREAD_LOCK_API_VERSION,
+        EVTHREAD_LOCKTYPE_RECURSIVE,
+        lload_libevent_mutex_init,
+        lload_libevent_mutex_destroy,
+        lload_libevent_mutex_lock,
+        lload_libevent_mutex_unlock
+    };
+    struct evthread_condition_callbacks cond_cbs = {
+        EVTHREAD_CONDITION_API_VERSION,
+        lload_libevent_cond_init,
+        lload_libevent_cond_destroy,
+        lload_libevent_cond_signal,
+        lload_libevent_cond_timedwait
+    };
+
+    if ( ldap_pvt_thread_initialize() ) {
+        return -1;
+    }
+
+    evthread_set_lock_callbacks( &cbs );
+    evthread_set_condition_callbacks( &cond_cbs );
+    evthread_set_id_callback( ldap_pvt_thread_self );
+    return 0;
+}
diff --git a/servers/lloadd/main.c b/servers/lloadd/main.c
new file mode 100644 (file)
index 0000000..0fd5200
--- /dev/null
@@ -0,0 +1,974 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2015 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>.
+ */
+/* 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/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+#include <ac/wait.h>
+#include <ac/errno.h>
+
+#include <event2/event.h>
+
+#include "slap.h"
+#include "lutil.h"
+#include "ldif.h"
+
+#ifdef LDAP_SIGCHLD
+static void wait4child( evutil_socket_t sig, short what, void *arg );
+#endif
+
+#ifdef SIGPIPE
+static void sigpipe( evutil_socket_t sig, short what, void *arg );
+#endif
+
+#ifdef HAVE_NT_SERVICE_MANAGER
+#define MAIN_RETURN(x) return
+static struct sockaddr_in bind_addr;
+
+#define SERVICE_EXIT( e, n ) \
+    do { \
+        if ( is_NT_Service ) { \
+            lutil_ServiceStatus.dwWin32ExitCode = (e); \
+            lutil_ServiceStatus.dwServiceSpecificExitCode = (n); \
+        } \
+    } while (0)
+
+#else
+#define SERVICE_EXIT( e, n )
+#define MAIN_RETURN(x) return (x)
+#endif
+
+struct signal_handler {
+    int signal;
+    event_callback_fn handler;
+    struct event *event;
+} signal_handlers[] = {
+        { LDAP_SIGUSR2, slap_sig_shutdown },
+
+#ifdef SIGPIPE
+        { SIGPIPE, sigpipe },
+#endif
+#ifdef SIGHUP
+        { SIGHUP, slap_sig_shutdown },
+#endif
+        { SIGINT, slap_sig_shutdown },
+        { SIGTERM, slap_sig_shutdown },
+#ifdef SIGTRAP
+        { SIGTRAP, slap_sig_shutdown },
+#endif
+#ifdef LDAP_SIGCHLD
+        { LDAP_SIGCHLD, wait4child },
+#endif
+#ifdef SIGBREAK
+        /* SIGBREAK is generated when Ctrl-Break is pressed. */
+        { SIGBREAK, slap_sig_shutdown },
+#endif
+        { 0, NULL }
+};
+
+/*
+ * when more than one lloadd is running on one machine, each one might have
+ * it's own LOCAL for syslogging and must have its own pid/args files
+ */
+
+#ifndef HAVE_MKVERSION
+const char Versionstr[] = OPENLDAP_PACKAGE
+        " " OPENLDAP_VERSION " LDAP Load Balancer Server (lloadd)";
+#endif
+
+#define CHECK_NONE 0x00
+#define CHECK_CONFIG 0x01
+#define CHECK_LOGLEVEL 0x02
+static int check = CHECK_NONE;
+static int version = 0;
+
+void *slap_tls_ctx;
+LDAP *slap_tls_ld;
+
+static int
+slapd_opt_slp( const char *val, void *arg )
+{
+#ifdef HAVE_SLP
+    /* NULL is default */
+    if ( val == NULL || *val == '(' || strcasecmp( val, "on" ) == 0 ) {
+        slapd_register_slp = 1;
+        slapd_slp_attrs = ( val != NULL && *val == '(' ) ? val : NULL;
+
+    } else if ( strcasecmp( val, "off" ) == 0 ) {
+        slapd_register_slp = 0;
+
+        /* NOTE: add support for URL specification? */
+
+    } else {
+        fprintf( stderr, "unrecognized value \"%s\" for SLP option\n", val );
+        return -1;
+    }
+
+    return 0;
+
+#else
+    fputs( "lloadd: SLP support is not available\n", stderr );
+    return 0;
+#endif
+}
+
+/*
+ * Option helper structure:
+ *
+ * oh_nam   is left-hand part of <option>[=<value>]
+ * oh_fnc   is handler function
+ * oh_arg   is an optional arg to oh_fnc
+ * oh_usage is the one-line usage string related to the option,
+ *          which is assumed to start with <option>[=<value>]
+ *
+ * please leave valid options in the structure, and optionally #ifdef
+ * their processing inside the helper, so that reasonable and helpful
+ * error messages can be generated if a disabled option is requested.
+ */
+struct option_helper {
+    struct berval oh_name;
+    int (*oh_fnc)( const char *val, void *arg );
+    void *oh_arg;
+    const char *oh_usage;
+} option_helpers[] = {
+        { BER_BVC("slp"), slapd_opt_slp, NULL,
+                "slp[={on|off|(attrs)}] enable/disable SLP using (attrs)" },
+        { BER_BVNULL, 0, NULL, NULL }
+};
+
+#if defined(LDAP_DEBUG) && defined(LDAP_SYSLOG)
+#ifdef LOG_LOCAL4
+int
+parse_syslog_user( const char *arg, int *syslogUser )
+{
+    static slap_verbmasks syslogUsers[] = {
+            { BER_BVC("LOCAL0"), LOG_LOCAL0 },
+            { BER_BVC("LOCAL1"), LOG_LOCAL1 },
+            { BER_BVC("LOCAL2"), LOG_LOCAL2 },
+            { BER_BVC("LOCAL3"), LOG_LOCAL3 },
+            { BER_BVC("LOCAL4"), LOG_LOCAL4 },
+            { BER_BVC("LOCAL5"), LOG_LOCAL5 },
+            { BER_BVC("LOCAL6"), LOG_LOCAL6 },
+            { BER_BVC("LOCAL7"), LOG_LOCAL7 },
+#ifdef LOG_USER
+            { BER_BVC("USER"), LOG_USER },
+#endif /* LOG_USER */
+#ifdef LOG_DAEMON
+            { BER_BVC("DAEMON"), LOG_DAEMON },
+#endif /* LOG_DAEMON */
+            { BER_BVNULL, 0 }
+};
+    int i = verb_to_mask( arg, syslogUsers );
+
+    if ( BER_BVISNULL( &syslogUsers[i].word ) ) {
+        Debug( LDAP_DEBUG_ANY, "unrecognized syslog user \"%s\".\n", arg );
+        return 1;
+    }
+
+    *syslogUser = syslogUsers[i].mask;
+
+    return 0;
+}
+#endif /* LOG_LOCAL4 */
+
+int
+parse_syslog_level( const char *arg, int *levelp )
+{
+    static slap_verbmasks str2syslog_level[] = {
+            { BER_BVC("EMERG"), LOG_EMERG },
+            { BER_BVC("ALERT"), LOG_ALERT },
+            { BER_BVC("CRIT"), LOG_CRIT },
+            { BER_BVC("ERR"), LOG_ERR },
+            { BER_BVC("WARNING"), LOG_WARNING },
+            { BER_BVC("NOTICE"), LOG_NOTICE },
+            { BER_BVC("INFO"), LOG_INFO },
+            { BER_BVC("DEBUG"), LOG_DEBUG },
+            { BER_BVNULL, 0 }
+};
+    int i = verb_to_mask( arg, str2syslog_level );
+    if ( BER_BVISNULL( &str2syslog_level[i].word ) ) {
+        Debug( LDAP_DEBUG_ANY, "unknown syslog level \"%s\".\n", arg );
+        return 1;
+    }
+
+    *levelp = str2syslog_level[i].mask;
+
+    return 0;
+}
+#endif /* LDAP_DEBUG && LDAP_SYSLOG */
+
+int
+parse_debug_unknowns( char **unknowns, int *levelp )
+{
+    int i, level, rc = 0;
+
+    for ( i = 0; unknowns[i] != NULL; i++ ) {
+        level = 0;
+        if ( str2loglevel( unknowns[i], &level ) ) {
+            fprintf( stderr, "unrecognized log level \"%s\"\n", unknowns[i] );
+            rc = 1;
+        } else {
+            *levelp |= level;
+        }
+    }
+    return rc;
+}
+
+int
+parse_debug_level( const char *arg, int *levelp, char ***unknowns )
+{
+    int level;
+
+    if ( arg && arg[0] != '-' && !isdigit( (unsigned char)arg[0] ) ) {
+        int i;
+        char **levels;
+
+        levels = ldap_str2charray( arg, "," );
+
+        for ( i = 0; levels[i] != NULL; i++ ) {
+            level = 0;
+
+            if ( str2loglevel( levels[i], &level ) ) {
+                /* remember this for later */
+                ldap_charray_add( unknowns, levels[i] );
+                fprintf( stderr, "unrecognized log level \"%s\" (deferred)\n",
+                        levels[i] );
+            } else {
+                *levelp |= level;
+            }
+        }
+
+        ldap_charray_free( levels );
+
+    } else {
+        int rc;
+
+        if ( arg[0] == '-' ) {
+            rc = lutil_atoix( &level, arg, 0 );
+        } else {
+            unsigned ulevel;
+
+            rc = lutil_atoux( &ulevel, arg, 0 );
+            level = (int)ulevel;
+        }
+
+        if ( rc ) {
+            fprintf( stderr,
+                    "unrecognized log level "
+                    "\"%s\"\n",
+                    arg );
+            return 1;
+        }
+
+        if ( level == 0 ) {
+            *levelp = 0;
+
+        } else {
+            *levelp |= level;
+        }
+    }
+
+    return 0;
+}
+
+static void
+usage( char *name )
+{
+    fprintf( stderr, "usage: %s options\n", name );
+    fprintf( stderr,
+            "\t-4\t\tIPv4 only\n"
+            "\t-6\t\tIPv6 only\n"
+            "\t-d level\tDebug level"
+            "\n"
+            "\t-f filename\tConfiguration file\n"
+#if defined(HAVE_SETUID) && defined(HAVE_SETGID)
+            "\t-g group\tGroup (id or name) to run as\n"
+#endif
+            "\t-h URLs\t\tList of URLs to serve\n"
+#ifdef SLAP_DEFAULT_SYSLOG_USER
+            "\t-l facility\tSyslog facility (default: LOCAL4)\n"
+#endif
+            "\t-n serverName\tService name\n"
+            "\t-o <opt>[=val] generic means to specify options" );
+    if ( !BER_BVISNULL( &option_helpers[0].oh_name ) ) {
+        int i;
+
+        fprintf( stderr, "; supported options:\n" );
+        for ( i = 0; !BER_BVISNULL( &option_helpers[i].oh_name ); i++ ) {
+            fprintf( stderr, "\t\t%s\n", option_helpers[i].oh_usage );
+        }
+    } else {
+        fprintf( stderr, "\n" );
+    }
+    fprintf( stderr,
+#ifdef HAVE_CHROOT
+            "\t-r directory\tSandbox directory to chroot to\n"
+#endif
+            "\t-s level\tSyslog level\n"
+            "\t-t\t\tCheck configuration file\n"
+#if defined(HAVE_SETUID) && defined(HAVE_SETGID)
+            "\t-u user\t\tUser (id or name) to run as\n"
+#endif
+            "\t-V\t\tprint version info (-VV exit afterwards)\n" );
+}
+
+#ifdef HAVE_NT_SERVICE_MANAGER
+void WINAPI
+ServiceMain( DWORD argc, LPTSTR *argv )
+#else
+int
+main( int argc, char **argv )
+#endif
+{
+    int i, no_detach = 0;
+    int rc = 1;
+    char *urls = NULL;
+#if defined(HAVE_SETUID) && defined(HAVE_SETGID)
+    char *username = NULL;
+    char *groupname = NULL;
+#endif
+#if defined(HAVE_CHROOT)
+    char *sandbox = NULL;
+#endif
+#ifdef SLAP_DEFAULT_SYSLOG_USER
+    int syslogUser = SLAP_DEFAULT_SYSLOG_USER;
+#endif
+
+#ifndef HAVE_WINSOCK
+    int pid, waitfds[2];
+#endif
+    int g_argc = argc;
+    char **g_argv = argv;
+
+    char *configfile = NULL;
+    char *configdir = NULL;
+    char *serverName;
+    int serverMode = SLAP_SERVER_MODE;
+
+    char **debug_unknowns = NULL;
+    char **syslog_unknowns = NULL;
+
+    int slapd_pid_file_unlink = 0, slapd_args_file_unlink = 0;
+    int firstopt = 1;
+
+    struct event_base *daemon_base = NULL;
+
+    slap_sl_mem_init();
+
+    (void)lload_libevent_init();
+
+    serverName = lutil_progname( "lloadd", argc, argv );
+
+#ifdef HAVE_NT_SERVICE_MANAGER
+    {
+        int *ip;
+        char *newConfigFile;
+        char *newConfigDir;
+        char *newUrls;
+        char *regService = NULL;
+
+        if ( is_NT_Service ) {
+            lutil_CommenceStartupProcessing( serverName, slap_sig_shutdown );
+            if ( strcmp( serverName, SERVICE_NAME ) ) regService = serverName;
+        }
+
+        ip = (int *)lutil_getRegParam( regService, "DebugLevel" );
+        if ( ip != NULL ) {
+            slap_debug = *ip;
+            Debug( LDAP_DEBUG_ANY, "new debug level from registry is: %d\n",
+                    slap_debug );
+        }
+
+        newUrls = (char *)lutil_getRegParam( regService, "Urls" );
+        if ( newUrls ) {
+            if ( urls ) ch_free( urls );
+
+            urls = ch_strdup( newUrls );
+            Debug( LDAP_DEBUG_ANY, "new urls from registry: %s\n", urls );
+        }
+
+        newConfigFile = (char *)lutil_getRegParam( regService, "ConfigFile" );
+        if ( newConfigFile != NULL ) {
+            configfile = ch_strdup( newConfigFile );
+            Debug( LDAP_DEBUG_ANY, "new config file from registry is: %s\n",
+                    configfile );
+        }
+
+        newConfigDir = (char *)lutil_getRegParam( regService, "ConfigDir" );
+        if ( newConfigDir != NULL ) {
+            configdir = ch_strdup( newConfigDir );
+            Debug( LDAP_DEBUG_ANY, "new config dir from registry is: %s\n",
+                    configdir );
+        }
+    }
+#endif
+
+    while ( (i = getopt( argc, argv,
+                      "c:d:f:F:h:n:o:s:tV"
+#ifdef LDAP_PF_INET6
+                      "46"
+#endif
+#ifdef HAVE_CHROOT
+                      "r:"
+#endif
+#if defined(LDAP_DEBUG) && defined(LDAP_SYSLOG)
+                      "S:"
+#ifdef LOG_LOCAL4
+                      "l:"
+#endif
+#endif
+#if defined(HAVE_SETUID) && defined(HAVE_SETGID)
+                      "u:g:"
+#endif
+                      )) != EOF ) {
+        switch ( i ) {
+#ifdef LDAP_PF_INET6
+            case '4':
+                slap_inet4or6 = AF_INET;
+                break;
+            case '6':
+                slap_inet4or6 = AF_INET6;
+                break;
+#endif
+
+            case 'h': /* listen URLs */
+                if ( urls != NULL ) free( urls );
+                urls = ch_strdup( optarg );
+                break;
+
+            case 'd': { /* set debug level and 'do not detach' flag */
+                int level = 0;
+
+                if ( strcmp( optarg, "?" ) == 0 ) {
+                    check |= CHECK_LOGLEVEL;
+                    break;
+                }
+
+                no_detach = 1;
+                if ( parse_debug_level( optarg, &level, &debug_unknowns ) ) {
+                    goto destroy;
+                }
+#ifdef LDAP_DEBUG
+                slap_debug |= level;
+#else
+                if ( level != 0 )
+                    fputs( "must compile with LDAP_DEBUG for debugging\n",
+                            stderr );
+#endif
+            } break;
+
+            case 'f': /* read config file */
+                configfile = ch_strdup( optarg );
+                break;
+
+            case 'o': {
+                char *val = strchr( optarg, '=' );
+                struct berval opt;
+
+                opt.bv_val = optarg;
+
+                if ( val ) {
+                    opt.bv_len = ( val - optarg );
+                    val++;
+
+                } else {
+                    opt.bv_len = strlen( optarg );
+                }
+
+                for ( i = 0; !BER_BVISNULL( &option_helpers[i].oh_name );
+                        i++ ) {
+                    if ( ber_bvstrcasecmp( &option_helpers[i].oh_name, &opt ) ==
+                            0 ) {
+                        assert( option_helpers[i].oh_fnc != NULL );
+                        if ( (*option_helpers[i].oh_fnc)(
+                                     val, option_helpers[i].oh_arg ) == -1 ) {
+                            /* we assume the option parsing helper
+                         * issues appropriate and self-explanatory
+                         * error messages... */
+                            goto stop;
+                        }
+                        break;
+                    }
+                }
+
+                if ( BER_BVISNULL( &option_helpers[i].oh_name ) ) {
+                    goto unhandled_option;
+                }
+                break;
+            }
+
+            case 's': /* set syslog level */
+                if ( strcmp( optarg, "?" ) == 0 ) {
+                    check |= CHECK_LOGLEVEL;
+                    break;
+                }
+
+                if ( parse_debug_level(
+                             optarg, &ldap_syslog, &syslog_unknowns ) ) {
+                    goto destroy;
+                }
+                break;
+
+#if defined(LDAP_DEBUG) && defined(LDAP_SYSLOG)
+            case 'S':
+                if ( parse_syslog_level( optarg, &ldap_syslog_level ) ) {
+                    goto destroy;
+                }
+                break;
+
+#ifdef LOG_LOCAL4
+            case 'l': /* set syslog local user */
+                if ( parse_syslog_user( optarg, &syslogUser ) ) {
+                    goto destroy;
+                }
+                break;
+#endif
+#endif /* LDAP_DEBUG && LDAP_SYSLOG */
+
+#ifdef HAVE_CHROOT
+            case 'r':
+                if ( sandbox ) free( sandbox );
+                sandbox = ch_strdup( optarg );
+                break;
+#endif
+
+#if defined(HAVE_SETUID) && defined(HAVE_SETGID)
+            case 'u': /* user name */
+                if ( username ) free( username );
+                username = ch_strdup( optarg );
+                break;
+
+            case 'g': /* group name */
+                if ( groupname ) free( groupname );
+                groupname = ch_strdup( optarg );
+                break;
+#endif /* SETUID && GETUID */
+
+            case 'n': /* NT service name */
+                serverName = ch_strdup( optarg );
+                break;
+
+            case 't':
+                check |= CHECK_CONFIG;
+                break;
+
+            case 'V':
+                version++;
+                break;
+
+            default:
+unhandled_option:;
+                usage( argv[0] );
+                rc = 1;
+                SERVICE_EXIT( ERROR_SERVICE_SPECIFIC_ERROR, 15 );
+                goto stop;
+        }
+
+        if ( firstopt ) {
+            firstopt = 0;
+        }
+    }
+
+    if ( optind != argc ) goto unhandled_option;
+
+    ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &slap_debug );
+    ldap_set_option( NULL, LDAP_OPT_DEBUG_LEVEL, &slap_debug );
+    ldif_debug = slap_debug;
+
+    if ( version ) {
+        fprintf( stderr, "%s\n", Versionstr );
+
+        if ( version > 1 ) goto stop;
+    }
+
+#if defined(LDAP_DEBUG) && defined(LDAP_SYSLOG)
+    {
+        char *logName;
+#ifdef HAVE_EBCDIC
+        logName = ch_strdup( serverName );
+        __atoe( logName );
+#else
+        logName = serverName;
+#endif
+
+#ifdef LOG_LOCAL4
+        openlog( logName, OPENLOG_OPTIONS, syslogUser );
+#elif defined LOG_DEBUG
+        openlog( logName, OPENLOG_OPTIONS );
+#endif
+#ifdef HAVE_EBCDIC
+        free( logName );
+#endif
+    }
+#endif /* LDAP_DEBUG && LDAP_SYSLOG */
+
+    Debug( LDAP_DEBUG_ANY, "%s", Versionstr );
+
+    global_host = ldap_pvt_get_fqdn( NULL );
+
+    if ( check == CHECK_NONE && slapd_daemon_init( urls ) != 0 ) {
+        rc = 1;
+        SERVICE_EXIT( ERROR_SERVICE_SPECIFIC_ERROR, 16 );
+        goto stop;
+    }
+
+#if defined(HAVE_CHROOT)
+    if ( sandbox ) {
+        if ( chdir( sandbox ) ) {
+            perror( "chdir" );
+            rc = 1;
+            goto stop;
+        }
+        if ( chroot( sandbox ) ) {
+            perror( "chroot" );
+            rc = 1;
+            goto stop;
+        }
+        if ( chdir( "/" ) ) {
+            perror( "chdir" );
+            rc = 1;
+            goto stop;
+        }
+    }
+#endif
+
+#if defined(HAVE_SETUID) && defined(HAVE_SETGID)
+    if ( username != NULL || groupname != NULL ) {
+        slap_init_user( username, groupname );
+    }
+#endif
+
+#ifdef HAVE_TLS
+    rc = ldap_create( &slap_tls_ld );
+    if ( rc ) {
+        SERVICE_EXIT( ERROR_SERVICE_SPECIFIC_ERROR, 20 );
+        goto destroy;
+    }
+    /* Library defaults to full certificate checking. This is correct when
+     * a client is verifying a server because all servers should have a
+     * valid cert. But few clients have valid certs, so we want our default
+     * to be no checking. The config file can override this as usual.
+     */
+    rc = LDAP_OPT_X_TLS_NEVER;
+    (void)ldap_pvt_tls_set_option(
+            slap_tls_ld, LDAP_OPT_X_TLS_REQUIRE_CERT, &rc );
+#endif
+
+    rc = slap_init( serverMode, serverName );
+    if ( rc ) {
+        SERVICE_EXIT( ERROR_SERVICE_SPECIFIC_ERROR, 18 );
+        goto destroy;
+    }
+
+    if ( read_config( configfile, configdir ) != 0 ) {
+        rc = 1;
+        SERVICE_EXIT( ERROR_SERVICE_SPECIFIC_ERROR, 19 );
+
+        if ( check & CHECK_CONFIG ) {
+            fprintf( stderr, "config check failed\n" );
+        }
+
+        goto destroy;
+    }
+
+    if ( debug_unknowns ) {
+        rc = parse_debug_unknowns( debug_unknowns, &slap_debug );
+        ldap_charray_free( debug_unknowns );
+        debug_unknowns = NULL;
+        if ( rc ) goto destroy;
+    }
+    if ( syslog_unknowns ) {
+        rc = parse_debug_unknowns( syslog_unknowns, &ldap_syslog );
+        ldap_charray_free( syslog_unknowns );
+        syslog_unknowns = NULL;
+        if ( rc ) goto destroy;
+    }
+
+    if ( check & CHECK_LOGLEVEL ) {
+        rc = 0;
+        goto destroy;
+    }
+
+    if ( check & CHECK_CONFIG ) {
+        fprintf( stderr, "config check succeeded\n" );
+
+        check &= ~CHECK_CONFIG;
+        if ( check == CHECK_NONE ) {
+            rc = 0;
+            goto destroy;
+        }
+    }
+
+#ifdef HAVE_TLS
+    rc = ldap_pvt_tls_init();
+    if ( rc != 0 ) {
+        Debug( LDAP_DEBUG_ANY, "main: "
+                "TLS init failed: %d\n",
+                rc );
+        rc = 1;
+        SERVICE_EXIT( ERROR_SERVICE_SPECIFIC_ERROR, 20 );
+        goto destroy;
+    }
+
+    {
+        int opt = 1;
+
+        /* Force new ctx to be created */
+        rc = ldap_pvt_tls_set_option(
+                slap_tls_ld, LDAP_OPT_X_TLS_NEWCTX, &opt );
+        if ( rc == 0 ) {
+            /* The ctx's refcount is bumped up here */
+            ldap_pvt_tls_get_option(
+                    slap_tls_ld, LDAP_OPT_X_TLS_CTX, &slap_tls_ctx );
+        } else if ( rc != LDAP_NOT_SUPPORTED ) {
+            Debug( LDAP_DEBUG_ANY, "main: "
+                    "TLS init def ctx failed: %d\n",
+                    rc );
+            rc = 1;
+            SERVICE_EXIT( ERROR_SERVICE_SPECIFIC_ERROR, 20 );
+            goto destroy;
+        }
+    }
+#endif
+
+    daemon_base = event_base_new();
+    if ( !daemon_base ) {
+        Debug( LDAP_DEBUG_ANY, "main: "
+                "main event base allocation failed\n" );
+        rc = 1;
+        SERVICE_EXIT( ERROR_SERVICE_SPECIFIC_ERROR, 21 );
+        goto destroy;
+    }
+
+    for ( i = 0; signal_handlers[i].signal; i++ ) {
+        struct event *event;
+        event = evsignal_new( daemon_base, signal_handlers[i].signal,
+                signal_handlers[i].handler, daemon_base );
+        if ( !event || event_add( event, NULL ) ) {
+            Debug( LDAP_DEBUG_ANY, "main: "
+                    "failed to register a handler for signal %d\n",
+                    signal_handlers[i].signal );
+            rc = 1;
+            SERVICE_EXIT( ERROR_SERVICE_SPECIFIC_ERROR, 21 );
+            goto destroy;
+        }
+        signal_handlers[i].event = event;
+    }
+
+#ifndef HAVE_WINSOCK
+    if ( !no_detach ) {
+        if ( lutil_pair( waitfds ) < 0 ) {
+            Debug( LDAP_DEBUG_ANY, "main: "
+                    "lutil_pair failed\n" );
+            rc = 1;
+            goto destroy;
+        }
+        pid = lutil_detach( no_detach, 0 );
+        if ( pid ) {
+            char buf[4];
+            rc = EXIT_SUCCESS;
+            close( waitfds[1] );
+            if ( read( waitfds[0], buf, 1 ) != 1 ) rc = EXIT_FAILURE;
+            _exit( rc );
+        } else {
+            close( waitfds[0] );
+        }
+    }
+#endif /* HAVE_WINSOCK */
+
+    if ( slapd_pid_file != NULL ) {
+        FILE *fp = fopen( slapd_pid_file, "w" );
+
+        if ( fp == NULL ) {
+            char ebuf[128];
+            int save_errno = errno;
+
+            Debug( LDAP_DEBUG_ANY, "unable to open pid file "
+                    "\"%s\": %d (%s)\n",
+                    slapd_pid_file, save_errno,
+                    AC_STRERROR_R( save_errno, ebuf, sizeof(ebuf) ) );
+
+            free( slapd_pid_file );
+            slapd_pid_file = NULL;
+
+            rc = 1;
+            goto destroy;
+        }
+        fprintf( fp, "%d\n", (int)getpid() );
+        fclose( fp );
+        slapd_pid_file_unlink = 1;
+    }
+
+    if ( slapd_args_file != NULL ) {
+        FILE *fp = fopen( slapd_args_file, "w" );
+
+        if ( fp == NULL ) {
+            char ebuf[128];
+            int save_errno = errno;
+
+            Debug( LDAP_DEBUG_ANY, "unable to open args file "
+                    "\"%s\": %d (%s)\n",
+                    slapd_args_file, save_errno,
+                    AC_STRERROR_R( save_errno, ebuf, sizeof(ebuf) ) );
+
+            free( slapd_args_file );
+            slapd_args_file = NULL;
+
+            rc = 1;
+            goto destroy;
+        }
+
+        for ( i = 0; i < g_argc; i++ ) {
+            fprintf( fp, "%s ", g_argv[i] );
+        }
+        fprintf( fp, "\n" );
+        fclose( fp );
+        slapd_args_file_unlink = 1;
+    }
+
+    /*
+     * FIXME: moved here from slapd_daemon_task()
+     * because back-monitor db_open() needs it
+     */
+    time( &starttime );
+
+    Debug( LDAP_DEBUG_ANY, "lloadd starting\n" );
+
+#ifndef HAVE_WINSOCK
+    if ( !no_detach ) {
+        write( waitfds[1], "1", 1 );
+        close( waitfds[1] );
+    }
+#endif
+
+#ifdef HAVE_NT_EVENT_LOG
+    if ( is_NT_Service )
+        lutil_LogStartedEvent( serverName, slap_debug,
+                configfile ? configfile : LLOADD_DEFAULT_CONFIGFILE, urls );
+#endif
+
+    rc = slapd_daemon( daemon_base );
+
+#ifdef HAVE_NT_SERVICE_MANAGER
+    /* Throw away the event that we used during the startup process. */
+    if ( is_NT_Service ) ldap_pvt_thread_cond_destroy( &started_event );
+#endif
+
+destroy:
+    if ( check & CHECK_LOGLEVEL ) {
+        (void)loglevel_print( stdout );
+    }
+    /* remember an error during destroy */
+    rc |= slap_destroy();
+
+stop:
+#ifdef HAVE_NT_EVENT_LOG
+    if ( is_NT_Service ) lutil_LogStoppedEvent( serverName );
+#endif
+
+    Debug( LDAP_DEBUG_ANY, "lloadd stopped.\n" );
+
+#ifdef HAVE_NT_SERVICE_MANAGER
+    lutil_ReportShutdownComplete();
+#endif
+
+#ifdef LOG_DEBUG
+    closelog();
+#endif
+    slapd_daemon_destroy();
+
+#ifdef HAVE_TLS
+    if ( slap_tls_ld ) {
+        ldap_pvt_tls_ctx_free( slap_tls_ctx );
+        ldap_unbind_ext( slap_tls_ld, NULL, NULL );
+    }
+    ldap_pvt_tls_destroy();
+#endif
+
+    if ( slapd_pid_file_unlink ) {
+        unlink( slapd_pid_file );
+    }
+    if ( slapd_args_file_unlink ) {
+        unlink( slapd_args_file );
+    }
+
+    config_destroy();
+
+    if ( configfile ) ch_free( configfile );
+    if ( configdir ) ch_free( configdir );
+    if ( urls ) ch_free( urls );
+    if ( global_host ) ch_free( global_host );
+
+    /* kludge, get symbols referenced */
+    tavl_free( NULL, NULL );
+
+    MAIN_RETURN(rc);
+}
+
+#ifdef SIGPIPE
+
+/*
+ *  Catch and discard terminated child processes, to avoid zombies.
+ */
+
+static void
+sigpipe( evutil_socket_t sig, short what, void *arg )
+{
+}
+
+#endif /* SIGPIPE */
+
+#ifdef LDAP_SIGCHLD
+
+/*
+ *  Catch and discard terminated child processes, to avoid zombies.
+ */
+
+static void
+wait4child( evutil_socket_t sig, short what, void *arg )
+{
+    int save_errno = errno;
+
+#ifdef WNOHANG
+    do
+        errno = 0;
+#ifdef HAVE_WAITPID
+    while ( waitpid( (pid_t)-1, NULL, WNOHANG ) > 0 || errno == EINTR );
+#else
+    while ( wait3( NULL, WNOHANG, NULL ) > 0 || errno == EINTR );
+#endif
+#else
+    (void)wait( NULL );
+#endif
+    errno = save_errno;
+}
+
+#endif /* LDAP_SIGCHLD */
diff --git a/servers/lloadd/nt_svc.c b/servers/lloadd/nt_svc.c
new file mode 120000 (symlink)
index 0000000..eb3ffdb
--- /dev/null
@@ -0,0 +1 @@
+../slapd/nt_svc.c
\ No newline at end of file
diff --git a/servers/lloadd/proto-slap.h b/servers/lloadd/proto-slap.h
new file mode 100644 (file)
index 0000000..a036371
--- /dev/null
@@ -0,0 +1,268 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2015 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>.
+ */
+/* 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.
+ */
+
+#ifndef PROTO_SLAP_H
+#define PROTO_SLAP_H
+
+#include <ldap_cdefs.h>
+#include "ldap_pvt.h"
+
+#include <event2/event.h>
+
+LDAP_BEGIN_DECL
+
+struct config_args_s;  /* config.h */
+struct config_reply_s; /* config.h */
+
+/*
+ * ch_malloc.c
+ */
+LDAP_SLAPD_V (BerMemoryFunctions) ch_mfuncs;
+LDAP_SLAPD_F (void *) ch_malloc( ber_len_t size );
+LDAP_SLAPD_F (void *) ch_realloc( void *block, ber_len_t size );
+LDAP_SLAPD_F (void *) ch_calloc( ber_len_t nelem, ber_len_t size );
+LDAP_SLAPD_F (char *) ch_strdup( const char *string );
+LDAP_SLAPD_F (void) ch_free( void * );
+
+#ifndef CH_FREE
+#undef free
+#define free ch_free
+#endif
+
+/*
+ * config.c
+ */
+LDAP_SLAPD_F (int) read_config( const char *fname, const char *dir );
+LDAP_SLAPD_F (void) config_destroy( void );
+LDAP_SLAPD_F (int) verb_to_mask( const char *word, slap_verbmasks *v );
+LDAP_SLAPD_F (int) str2loglevel( const char *s, int *l );
+LDAP_SLAPD_F (void) bindconf_free( slap_bindconf *bc );
+
+/*
+ * connection.c
+ */
+LDAP_SLAPD_F (Connection *) connection_init( ber_socket_t s, const char *peername, int use_tls );
+LDAP_SLAPD_F (Connection *) client_init( ber_socket_t s, Listener *url, const char *peername, struct event_base *base, int use_tls );
+LDAP_SLAPD_F (void) connection_destroy( Connection *c );
+
+/*
+ * daemon.c
+ */
+LDAP_SLAPD_F (int) slapd_daemon_init( const char *urls );
+LDAP_SLAPD_F (int) slapd_daemon_destroy( void );
+LDAP_SLAPD_F (int) slapd_daemon( struct event_base *daemon_base );
+LDAP_SLAPD_F (Listener **) slapd_get_listeners( void );
+
+LDAP_SLAPD_F (void) slap_sig_shutdown( evutil_socket_t sig, short what, void *arg );
+
+LDAP_SLAPD_V (volatile sig_atomic_t) slapd_shutdown;
+LDAP_SLAPD_V (int) lloadd_inited;
+LDAP_SLAPD_V (struct runqueue_s) slapd_rq;
+LDAP_SLAPD_V (int) slapd_daemon_threads;
+LDAP_SLAPD_V (int) slapd_daemon_mask;
+#ifdef LDAP_TCP_BUFFER
+LDAP_SLAPD_V (int) slapd_tcp_rmem;
+LDAP_SLAPD_V (int) slapd_tcp_wmem;
+#endif /* LDAP_TCP_BUFFER */
+
+#define bvmatch( bv1, bv2 ) \
+    ( ( (bv1)->bv_len == (bv2)->bv_len ) && \
+            ( memcmp( (bv1)->bv_val, (bv2)->bv_val, (bv1)->bv_len ) == 0 ) )
+
+/*
+ * globals.c
+ */
+LDAP_SLAPD_V (const struct berval) slap_empty_bv;
+LDAP_SLAPD_V (const struct berval) slap_unknown_bv;
+LDAP_SLAPD_V (const struct berval) slap_true_bv;
+LDAP_SLAPD_V (const struct berval) slap_false_bv;
+LDAP_SLAPD_V (struct slap_sync_cookie_s) slap_sync_cookie;
+LDAP_SLAPD_V (void *) slap_tls_ctx;
+LDAP_SLAPD_V (LDAP *) slap_tls_ld;
+
+/*
+ * init.c
+ */
+LDAP_SLAPD_F (int) slap_init( int mode, const char *name );
+LDAP_SLAPD_F (int) slap_destroy( void );
+
+/*
+ * libevent_support.c
+ */
+LDAP_SLAPD_F (int) lload_libevent_init( void );
+
+/*
+ * main.c
+ */
+LDAP_SLAPD_V (int) slapd_register_slp;
+LDAP_SLAPD_V (const char *) slapd_slp_attrs;
+
+/*
+ * sl_malloc.c
+ */
+LDAP_SLAPD_F (void *) slap_sl_malloc( ber_len_t size, void *ctx );
+LDAP_SLAPD_F (void *) slap_sl_realloc( void *block, ber_len_t size, void *ctx );
+LDAP_SLAPD_F (void *) slap_sl_calloc( ber_len_t nelem, ber_len_t size, void *ctx );
+LDAP_SLAPD_F (void) slap_sl_free( void *, void *ctx );
+
+LDAP_SLAPD_V (BerMemoryFunctions) slap_sl_mfuncs;
+
+LDAP_SLAPD_F (void) slap_sl_mem_init( void );
+LDAP_SLAPD_F (void *) slap_sl_mem_create( ber_len_t size, int stack, void *ctx, int flag );
+LDAP_SLAPD_F (void) slap_sl_mem_setctx( void *ctx, void *memctx );
+LDAP_SLAPD_F (void) slap_sl_mem_destroy( void *key, void *data );
+LDAP_SLAPD_F (void *) slap_sl_context( void *ptr );
+
+/* assumes (x) > (y) returns 1 if true, 0 otherwise */
+#define SLAP_PTRCMP(x, y) ( (x) < (y) ? -1 : (x) > (y) )
+
+/*
+ * user.c
+ */
+#if defined(HAVE_PWD_H) && defined(HAVE_GRP_H)
+LDAP_SLAPD_F (void) slap_init_user( char *username, char *groupname );
+#endif
+
+/*
+ * value.c
+ */
+LDAP_SLAPD_F (int) value_add_one( BerVarray *vals, struct berval *addval );
+
+#ifdef SLAP_ZONE_ALLOC
+/*
+ * zn_malloc.c
+ */
+LDAP_SLAPD_F (void *) slap_zn_malloc( ber_len_t, void * );
+LDAP_SLAPD_F (void *) slap_zn_realloc( void *, ber_len_t, void * );
+LDAP_SLAPD_F (void *) slap_zn_calloc( ber_len_t, ber_len_t, void * );
+LDAP_SLAPD_F (void) slap_zn_free( void *, void * );
+
+LDAP_SLAPD_F (void *) slap_zn_mem_create( ber_len_t, ber_len_t, ber_len_t, ber_len_t );
+LDAP_SLAPD_F (void) slap_zn_mem_destroy( void * );
+LDAP_SLAPD_F (int) slap_zn_validate( void *, void *, int );
+LDAP_SLAPD_F (int) slap_zn_invalidate( void *, void * );
+LDAP_SLAPD_F (int) slap_zh_rlock( void * );
+LDAP_SLAPD_F (int) slap_zh_runlock( void * );
+LDAP_SLAPD_F (int) slap_zh_wlock( void * );
+LDAP_SLAPD_F (int) slap_zh_wunlock( void * );
+LDAP_SLAPD_F (int) slap_zn_rlock( void *, void * );
+LDAP_SLAPD_F (int) slap_zn_runlock( void *, void * );
+LDAP_SLAPD_F (int) slap_zn_wlock( void *, void * );
+LDAP_SLAPD_F (int) slap_zn_wunlock( void *, void * );
+#endif
+
+LDAP_SLAPD_V (ber_len_t) sockbuf_max_incoming;
+LDAP_SLAPD_V (ber_len_t) sockbuf_max_incoming_auth;
+
+LDAP_SLAPD_V (slap_mask_t) global_allows;
+LDAP_SLAPD_V (slap_mask_t) global_disallows;
+
+LDAP_SLAPD_V (const char) Versionstr[];
+
+LDAP_SLAPD_V (int) global_gentlehup;
+LDAP_SLAPD_V (int) global_idletimeout;
+LDAP_SLAPD_V (char *) global_host;
+LDAP_SLAPD_V (int) lber_debug;
+LDAP_SLAPD_V (int) ldap_syslog;
+
+LDAP_SLAPD_V (char *) slapd_pid_file;
+LDAP_SLAPD_V (char *) slapd_args_file;
+LDAP_SLAPD_V (time_t) starttime;
+
+/* use time(3) -- no mutex */
+#define slap_get_time() time( NULL )
+
+LDAP_SLAPD_V (ldap_pvt_thread_pool_t) connection_pool;
+LDAP_SLAPD_V (int) connection_pool_max;
+LDAP_SLAPD_V (int) connection_pool_queues;
+LDAP_SLAPD_V (int) slap_tool_thread_max;
+
+#ifdef USE_MP_BIGNUM
+#define UI2BVX( bv, ui, ctx ) \
+    do { \
+        char *val; \
+        ber_len_t len; \
+        val = BN_bn2dec( ui ); \
+        if (val) { \
+            len = strlen( val ); \
+            if ( len > (bv)->bv_len ) { \
+                (bv)->bv_val = ber_memrealloc_x( (bv)->bv_val, len + 1, (ctx) ); \
+            } \
+            AC_MEMCPY( (bv)->bv_val, val, len + 1 ); \
+            (bv)->bv_len = len; \
+            OPENSSL_free( val ); \
+        } else { \
+            ber_memfree_x( (bv)->bv_val, (ctx) ); \
+            BER_BVZERO( (bv) ); \
+        } \
+    } while (0)
+
+#elif defined(USE_MP_GMP)
+/* NOTE: according to the documentation, the result
+ * of mpz_sizeinbase() can exceed the length of the
+ * string representation of the number by 1
+ */
+#define UI2BVX( bv, ui, ctx ) \
+    do { \
+        ber_len_t len = mpz_sizeinbase( (ui), 10 ); \
+        if ( len > (bv)->bv_len ) { \
+            (bv)->bv_val = ber_memrealloc_x( (bv)->bv_val, len + 1, (ctx) ); \
+        } \
+        (void)mpz_get_str( (bv)->bv_val, 10, (ui) ); \
+        if ( (bv)->bv_val[len - 1] == '\0' ) { \
+            len--; \
+        } \
+        (bv)->bv_len = len; \
+    } while (0)
+
+#else
+#ifdef USE_MP_LONG_LONG
+#define UI2BV_FORMAT "%llu"
+#elif defined USE_MP_LONG
+#define UI2BV_FORMAT "%lu"
+#elif defined HAVE_LONG_LONG
+#define UI2BV_FORMAT "%llu"
+#else
+#define UI2BV_FORMAT "%lu"
+#endif
+
+#define UI2BVX( bv, ui, ctx ) \
+    do { \
+        char buf[LDAP_PVT_INTTYPE_CHARS(long)]; \
+        ber_len_t len; \
+        len = snprintf( buf, sizeof( buf ), UI2BV_FORMAT, (ui) ); \
+        if ( len > (bv)->bv_len ) { \
+            (bv)->bv_val = ber_memrealloc_x( (bv)->bv_val, len + 1, (ctx) ); \
+        } \
+        (bv)->bv_len = len; \
+        AC_MEMCPY( (bv)->bv_val, buf, len + 1 ); \
+    } while (0)
+#endif
+
+#define UI2BV( bv, ui ) UI2BVX( bv, ui, NULL )
+
+LDAP_END_DECL
+
+#endif /* PROTO_SLAP_H */
diff --git a/servers/lloadd/sl_malloc.c b/servers/lloadd/sl_malloc.c
new file mode 120000 (symlink)
index 0000000..37a1d04
--- /dev/null
@@ -0,0 +1 @@
+../slapd/sl_malloc.c
\ No newline at end of file
diff --git a/servers/lloadd/slap.h b/servers/lloadd/slap.h
new file mode 100644 (file)
index 0000000..d89c7e2
--- /dev/null
@@ -0,0 +1,332 @@
+/* slap.h - stand alone ldap server include file */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2015 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>.
+ */
+/* 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.
+ */
+
+#ifndef _SLAP_H_
+#define _SLAP_H_
+
+#include "ldap_defaults.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+
+#include <sys/types.h>
+#include <ac/syslog.h>
+#include <ac/regex.h>
+#include <ac/signal.h>
+#include <ac/socket.h>
+#include <ac/time.h>
+#include <ac/param.h>
+
+#include "avl.h"
+
+#ifndef ldap_debug
+#define ldap_debug slap_debug
+#endif
+
+#include "ldap_log.h"
+
+#include <ldap.h>
+#include <ldap_schema.h>
+
+#include "lber_pvt.h"
+#include "ldap_pvt.h"
+#include "ldap_pvt_thread.h"
+#include "ldap_queue.h"
+
+#include <event2/event.h>
+
+LDAP_BEGIN_DECL
+
+/*
+ * SLAPD Memory allocation macros
+ *
+ * Unlike ch_*() routines, these routines do not assert() upon
+ * allocation error.  They are intended to be used instead of
+ * ch_*() routines where the caller has implemented proper
+ * checking for and handling of allocation errors.
+ *
+ * Patches to convert ch_*() calls to SLAP_*() calls welcomed.
+ */
+#define SLAP_MALLOC(s) ber_memalloc( ( s ) )
+#define SLAP_CALLOC(n, s) ber_memcalloc( ( n ), ( s ) )
+#define SLAP_REALLOC(p, s) ber_memrealloc( ( p ), ( s ) )
+#define SLAP_FREE(p) ber_memfree( ( p ) )
+#define SLAP_VFREE(v) ber_memvfree( (void **)( v ) )
+#define SLAP_STRDUP(s) ber_strdup( ( s ) )
+#define SLAP_STRNDUP(s, l) ber_strndup( ( s ), ( l ) )
+
+#define SERVICE_NAME OPENLDAP_PACKAGE "-slapd"
+#define SLAPD_ANONYMOUS ""
+#define SLAP_STRING_UNKNOWN "unknown"
+
+#define SLAP_MAX_WORKER_THREADS ( 16 )
+
+#define SLAP_SB_MAX_INCOMING_DEFAULT ( ( 1 << 18 ) - 1 )
+#define SLAP_SB_MAX_INCOMING_AUTH ( ( 1 << 24 ) - 1 )
+
+#define SLAP_TEXT_BUFLEN ( 256 )
+
+/* unknown config file directive */
+#define SLAP_CONF_UNKNOWN ( -1026 )
+
+LDAP_SLAPD_V (int) slap_debug;
+
+typedef unsigned long slap_mask_t;
+
+typedef struct Connection Connection;
+/* end of forward declarations */
+
+typedef union Sockaddr {
+    struct sockaddr sa_addr;
+    struct sockaddr_in sa_in_addr;
+#ifdef LDAP_PF_INET6
+    struct sockaddr_storage sa_storage;
+    struct sockaddr_in6 sa_in6_addr;
+#endif
+#ifdef LDAP_PF_LOCAL
+    struct sockaddr_un sa_un_addr;
+#endif
+} Sockaddr;
+
+#ifdef LDAP_PF_INET6
+extern int slap_inet4or6;
+#endif
+
+LDAP_SLAPD_V (int) slapMode;
+#define SLAP_UNDEFINED_MODE 0x0000
+#define SLAP_SERVER_MODE 0x0001
+#define SLAP_TOOL_MODE 0x0002
+#define SLAP_MODE 0x0003
+
+#define SLAP_SERVER_RUNNING 0x8000
+
+#define SB_TLS_DEFAULT ( -1 )
+#define SB_TLS_OFF 0
+#define SB_TLS_ON 1
+#define SB_TLS_CRITICAL 2
+
+typedef struct slap_keepalive {
+    int sk_idle;
+    int sk_probes;
+    int sk_interval;
+} slap_keepalive;
+
+typedef struct slap_bindconf {
+    struct berval sb_uri;
+    int sb_version;
+    int sb_tls;
+    int sb_method;
+    int sb_timeout_api;
+    int sb_timeout_net;
+    struct berval sb_binddn;
+    struct berval sb_cred;
+    struct berval sb_saslmech;
+    char *sb_secprops;
+    struct berval sb_realm;
+    struct berval sb_authcId;
+    struct berval sb_authzId;
+    slap_keepalive sb_keepalive;
+#ifdef HAVE_TLS
+    void *sb_tls_ctx;
+    char *sb_tls_cert;
+    char *sb_tls_key;
+    char *sb_tls_cacert;
+    char *sb_tls_cacertdir;
+    char *sb_tls_reqcert;
+    char *sb_tls_reqsan;
+    char *sb_tls_cipher_suite;
+    char *sb_tls_protocol_min;
+    char *sb_tls_ecname;
+#ifdef HAVE_OPENSSL
+    char *sb_tls_crlcheck;
+#endif
+    int sb_tls_int_reqcert;
+    int sb_tls_int_reqsan;
+    int sb_tls_do_init;
+#endif
+} slap_bindconf;
+
+typedef struct slap_verbmasks {
+    struct berval word;
+    const slap_mask_t mask;
+} slap_verbmasks;
+
+typedef struct slap_cf_aux_table {
+    struct berval key;
+    int off;
+    char type;
+    char quote;
+    void *aux;
+} slap_cf_aux_table;
+
+typedef int slap_cf_aux_table_parse_x( struct berval *val,
+        void *bc,
+        slap_cf_aux_table *tab0,
+        const char *tabmsg,
+        int unparse );
+
+#define SLAP_RESTRICT_OP_ADD 0x0001U
+#define SLAP_RESTRICT_OP_BIND 0x0002U
+#define SLAP_RESTRICT_OP_COMPARE 0x0004U
+#define SLAP_RESTRICT_OP_DELETE 0x0008U
+#define SLAP_RESTRICT_OP_EXTENDED 0x0010U
+#define SLAP_RESTRICT_OP_MODIFY 0x0020U
+#define SLAP_RESTRICT_OP_RENAME 0x0040U
+#define SLAP_RESTRICT_OP_SEARCH 0x0080U
+#define SLAP_RESTRICT_OP_MASK 0x00FFU
+
+#define SLAP_RESTRICT_READONLY 0x80000000U
+
+#define SLAP_RESTRICT_EXOP_START_TLS 0x0100U
+#define SLAP_RESTRICT_EXOP_MODIFY_PASSWD 0x0200U
+#define SLAP_RESTRICT_EXOP_WHOAMI 0x0400U
+#define SLAP_RESTRICT_EXOP_CANCEL 0x0800U
+#define SLAP_RESTRICT_EXOP_MASK 0xFF00U
+
+#define SLAP_RESTRICT_OP_READS \
+    ( SLAP_RESTRICT_OP_COMPARE | SLAP_RESTRICT_OP_SEARCH )
+#define SLAP_RESTRICT_OP_WRITES \
+    ( SLAP_RESTRICT_OP_ADD | SLAP_RESTRICT_OP_DELETE | SLAP_RESTRICT_OP_MODIFY | SLAP_RESTRICT_OP_RENAME )
+#define SLAP_RESTRICT_OP_ALL \
+    ( SLAP_RESTRICT_OP_READS | SLAP_RESTRICT_OP_WRITES | SLAP_RESTRICT_OP_BIND | SLAP_RESTRICT_OP_EXTENDED )
+
+typedef struct config_reply_s ConfigReply; /* config.h */
+
+typedef struct Listener Listener;
+
+/*
+ * represents a connection from an ldap client
+ */
+/* structure state (protected by connections_mutex) */
+enum sc_struct_state {
+    SLAP_C_UNINITIALIZED = 0, /* MUST BE ZERO (0) */
+    SLAP_C_UNUSED,
+    SLAP_C_USED,
+    SLAP_C_PENDING
+};
+
+/* connection state (protected by c_mutex) */
+enum sc_conn_state {
+    SLAP_C_INVALID = 0, /* MUST BE ZERO (0) */
+    SLAP_C_INACTIVE,    /* zero threads */
+    SLAP_C_CLOSING,     /* closing */
+    SLAP_C_ACTIVE,      /* one or more threads */
+    SLAP_C_BINDING,     /* binding */
+    SLAP_C_CLIENT       /* outbound client conn */
+};
+struct Connection {
+    enum sc_struct_state c_struct_state; /* structure management state */
+    enum sc_conn_state c_conn_state;     /* connection state */
+    ber_socket_t c_sd;
+
+    ldap_pvt_thread_mutex_t c_mutex; /* protect the connection */
+    Sockbuf *c_sb;                   /* ber connection stuff */
+
+    /* set by connection_init */
+    unsigned long c_connid;    /* unique id of this connection */
+    struct berval c_peer_name; /* peer name (trans=addr:port) */
+    time_t c_starttime;        /* when the connection was opened */
+
+    time_t c_activitytime; /* when the connection was last used */
+
+    struct event *c_read_event, *c_write_event;
+
+    /* can only be changed by binding thread */
+    struct berval c_sasl_bind_mech; /* mech in progress */
+
+    ldap_pvt_thread_mutex_t c_io_mutex; /* only one pdu written at a time */
+
+    BerElement *c_currentber;   /* ber we're attempting to read */
+    struct berval c_pendingber; /* ber we're attempting to write */
+
+#define CONN_IS_TLS 1
+#define CONN_IS_CLIENT 4
+#define CONN_IS_IPC 8
+
+#ifdef HAVE_TLS
+    char c_is_tls;           /* true if this LDAP over raw TLS */
+    char c_needs_tls_accept; /* true if SSL_accept should be called */
+#endif
+
+    long c_n_ops_executing; /* num of ops currently executing */
+    long c_n_ops_completed; /* num of ops completed */
+
+    void *c_private;
+};
+
+#ifdef LDAP_DEBUG
+#ifdef LDAP_SYSLOG
+#ifdef LOG_LOCAL4
+#define SLAP_DEFAULT_SYSLOG_USER LOG_LOCAL4
+#endif /* LOG_LOCAL4 */
+
+#define Statslog( level, fmt, connid, opid, arg1, arg2, arg3 ) \
+    Log( (level), ldap_syslog_level, (fmt), (connid), (opid), \
+            ( arg1 ), ( arg2 ), ( arg3 ) )
+#define StatslogTest( level ) ( ( ldap_debug | ldap_syslog ) & ( level ) )
+#else /* !LDAP_SYSLOG */
+#define Statslog( level, fmt, connid, opid, arg1, arg2, arg3 ) \
+    do { \
+        if ( ldap_debug & (level) ) \
+            lutil_debug( ldap_debug, (level), (fmt), (connid), (opid), \
+                    ( arg1 ), ( arg2 ), ( arg3 ) ); \
+    } while (0)
+#define StatslogTest( level ) ( ldap_debug & ( level ) )
+#endif /* !LDAP_SYSLOG */
+#else /* !LDAP_DEBUG */
+#define Statslog( level, fmt, connid, opid, arg1, arg2, arg3 ) ( (void)0 )
+#define StatslogTest( level ) ( 0 )
+#endif /* !LDAP_DEBUG */
+
+/*
+ * listener; need to access it from monitor backend
+ */
+struct Listener {
+    struct berval sl_url;
+    struct berval sl_name;
+    mode_t sl_perms;
+#ifdef HAVE_TLS
+    int sl_is_tls;
+#endif
+    struct event_base *base;
+    struct evconnlistener *listener;
+    int sl_mute; /* Listener is temporarily disabled due to emfile */
+    int sl_busy; /* Listener is busy (accept thread activated) */
+    ber_socket_t sl_sd;
+    Sockaddr sl_sa;
+#define sl_addr sl_sa.sa_in_addr
+#define LDAP_TCP_BUFFER
+#ifdef LDAP_TCP_BUFFER
+    int sl_tcp_rmem; /* custom TCP read buffer size */
+    int sl_tcp_wmem; /* custom TCP write buffer size */
+#endif
+};
+
+LDAP_END_DECL
+
+#include "proto-slap.h"
+
+#endif /* _SLAP_H_ */
diff --git a/servers/lloadd/user.c b/servers/lloadd/user.c
new file mode 120000 (symlink)
index 0000000..069bec6
--- /dev/null
@@ -0,0 +1 @@
+../slapd/user.c
\ No newline at end of file
diff --git a/servers/lloadd/value.c b/servers/lloadd/value.c
new file mode 100644 (file)
index 0000000..cfc15fe
--- /dev/null
@@ -0,0 +1,67 @@
+/* value.c - routines for dealing with values */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2015 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>.
+ */
+/*
+ * 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 "slap.h"
+
+int
+value_add_one( BerVarray *vals, struct berval *addval )
+{
+    int n;
+    BerVarray v2;
+
+    if ( *vals == NULL ) {
+        *vals = (BerVarray)SLAP_MALLOC( 2 * sizeof(struct berval) );
+        if ( *vals == NULL ) {
+            Debug( LDAP_DEBUG_TRACE, "value_add_one: "
+                    "SLAP_MALLOC failed.\n" );
+            return LBER_ERROR_MEMORY;
+        }
+        n = 0;
+
+    } else {
+        for ( n = 0; !BER_BVISNULL( &(*vals)[n] ); n++ ) {
+            ; /* Empty */
+        }
+        *vals = (BerVarray)SLAP_REALLOC(
+                (char *)*vals, ( n + 2 ) * sizeof(struct berval) );
+        if ( *vals == NULL ) {
+            Debug( LDAP_DEBUG_TRACE, "value_add_one: "
+                    "SLAP_MALLOC failed.\n" );
+            return LBER_ERROR_MEMORY;
+        }
+    }
+
+    v2 = &(*vals)[n];
+    ber_dupbv( v2, addval );
+
+    v2++;
+    BER_BVZERO( v2 );
+
+    return LDAP_SUCCESS;
+}
index ff05ce24ae8d0b52bd8be6db2a91ecb7102735e9..9763385f3a6e9b2d1c85509f081d0fbe43ed6e5d 100644 (file)
@@ -257,6 +257,7 @@ static OidRec OidMacros[] = {
  * OLcfg{Bk|Db}{Oc|At}:10              -> back-shell
  * OLcfg{Bk|Db}{Oc|At}:11              -> back-perl
  * OLcfg{Bk|Db}{Oc|At}:12              -> back-mdb
+ * OLcfg{Bk|Db}{Oc|At}:13              -> lloadd
  */
 
 /*