]> git.ipfire.org Git - thirdparty/openldap.git/commitdiff
ITS#9596 Introduce mod_harness module
authorOndřej Kuzník <ondra@mistotebe.net>
Tue, 16 Jul 2019 17:18:38 +0000 (19:18 +0200)
committerQuanah Gibson-Mount <quanah@openldap.org>
Tue, 14 Dec 2021 16:30:54 +0000 (16:30 +0000)
configure.ac
tests/Makefile.in
tests/modules/Makefile.in [new file with mode: 0644]
tests/modules/mod-harness/Makefile.in [new file with mode: 0644]
tests/modules/mod-harness/config.c [new file with mode: 0644]
tests/modules/mod-harness/connection.c [new file with mode: 0644]
tests/modules/mod-harness/init.c [new file with mode: 0644]
tests/modules/mod-harness/mod-harness.h [new file with mode: 0644]

index a0879bc38cef5ec496baf18c66d4038bcf25368b..675bdc74d151492f563c92ffde73705c0104feea 100644 (file)
@@ -436,6 +436,13 @@ LLOADD (Load Balancer Daemon) Options:])
 OL_ARG_ENABLE(balancer, [AS_HELP_STRING([--enable-balancer], [enable load balancer])],
        no, [no yes mod])
 
+dnl ----------------------------------------------------------------
+dnl TESTSUITE OPTIONS
+AC_ARG_ENABLE(testoptions,[
+Test suite Options:])
+OL_ARG_ENABLE(harness, [AS_HELP_STRING([--enable-harness], [enable mod_harness])],
+       no, [no yes])
+
 dnl ----------------------------------------------------------------
 AC_ARG_ENABLE(xxliboptions,[
 Library Generation & Linking Options])
@@ -471,6 +478,11 @@ if test $ol_enable_slapd = no ; then
                AC_MSG_WARN([slapd disabled, ignoring --enable-balancer=mod argument])
                ol_enable_balancer=no
        fi
+
+       if test $ol_enable_harness != no ; then
+               AC_MSG_WARN([slapd disabled, ignoring --enable-harness=$ol_enable_harness argument])
+               ol_enable_harness=no
+       fi
 else
        dnl If slapd enabled and loadable module support disabled
        dnl then require at least one built-in backend
@@ -484,7 +496,7 @@ else
                        fi
                done
 
-               for i in $Pwmods; do
+               for i in harness $Pwmods; do
                        eval "ol_tmp=\$ol_enable_$i"
                        if test -n "$ol_tmp" && test "$ol_tmp" = yes ; then
                                AC_MSG_ERROR([--enable-$i=yes requires --enable-modules])
@@ -610,6 +622,8 @@ BUILD_VALSORT=no
 
 BUILD_PW_ARGON2=no
 
+BUILD_HARNESS=no
+
 SLAPD_STATIC_OVERLAYS=
 SLAPD_DYNAMIC_OVERLAYS=
 
@@ -3072,6 +3086,14 @@ if test $ol_enable_versioning != no; then
        fi
 fi
 
+dnl ----------------------------------------------------------------
+dnl Test suite
+
+dnl We use 'yes' but mean 'mod' as far as our Makefile infra is concerned
+if test $ol_enable_harness != no; then
+    BUILD_HARNESS=mod
+fi
+
 dnl ----------------------------------------------------------------
 
 dnl
@@ -3146,6 +3168,8 @@ dnl overlays
   AC_SUBST(BUILD_BALANCER)
 dnl pwmods
   AC_SUBST(BUILD_PW_ARGON2)
+dnl test suite
+  AC_SUBST(BUILD_HARNESS)
 
 AC_SUBST(LDAP_LIBS)
 AC_SUBST(CLIENT_LIBS)
@@ -3245,6 +3269,8 @@ AC_CONFIG_FILES([Makefile:build/top.mk:Makefile.in:build/dir.mk]
 [servers/lloadd/Makefile.module:servers/lloadd/Makefile_module.in:build/mod.mk]
 [tests/Makefile:build/top.mk:tests/Makefile.in:build/dir.mk]
 [tests/run]
+[tests/modules/Makefile:build/top.mk:tests/modules/Makefile.in:build/dir.mk]
+[tests/modules/mod-harness/Makefile:build/top.mk:tests/modules/mod-harness/Makefile.in:build/mod.mk]
 [tests/progs/Makefile:build/top.mk:tests/progs/Makefile.in:build/rules.mk])
 
 AC_CONFIG_COMMANDS([default],[[
index 1010cc675e56d78625f76b59f9f77000b2d0bbba..f583a1033b3901906651bb147a3e21d0224d72b8 100644 (file)
@@ -14,7 +14,7 @@
 ## <http://www.OpenLDAP.org/license.html>.
 
 RUN=./run
-SUBDIRS= progs
+SUBDIRS= modules progs
 
 BUILD_MDB=@BUILD_MDB@
 BUILD_SQL=@BUILD_SQL@
diff --git a/tests/modules/Makefile.in b/tests/modules/Makefile.in
new file mode 100644 (file)
index 0000000..a950812
--- /dev/null
@@ -0,0 +1,16 @@
+# servers Makefile.in for OpenLDAP
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2019 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>.
+
+SUBDIRS= mod-harness
diff --git a/tests/modules/mod-harness/Makefile.in b/tests/modules/mod-harness/Makefile.in
new file mode 100644 (file)
index 0000000..2c702ab
--- /dev/null
@@ -0,0 +1,36 @@
+# Makefile.in for mod-harness
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2007-2019 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>.
+
+SRCS   = init.c config.c connection.c
+OBJS   = $(patsubst %.c,%.lo,$(SRCS)) $(UNIX_OBJS)
+
+LDAP_INCDIR= ../../../include
+LDAP_LIBDIR= ../../../libraries
+
+BUILD_OPT = "--enable-harness"
+BUILD_MOD = @BUILD_HARNESS@
+
+mod_DEFS = -DSLAPD_IMPORT
+MOD_DEFS = $(@BUILD_HARNESS@_DEFS)
+
+shared_LDAP_LIBS = $(LDAP_LIBLDAP_R_LA) $(LDAP_LIBLBER_LA)
+NT_LINK_LIBS = -L.. -lslapd $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+UNIX_LINK_LIBS = $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+
+LIBBASE = mod_harness
+
+XINCPATH = -I$(top_srcdir)/servers/slapd
+XDEFS = $(MODULES_CPPFLAGS)
+
diff --git a/tests/modules/mod-harness/config.c b/tests/modules/mod-harness/config.c
new file mode 100644 (file)
index 0000000..1c8407e
--- /dev/null
@@ -0,0 +1,129 @@
+/* config.c - configuration of the test harness backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2007-2019 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>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Ondřej Kuzník for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "mod-harness.h"
+
+static int config_generic(ConfigArgs *c);
+
+enum {
+       CFG_HOST = 1,
+       CFG_PORT,
+       CFG_IDENTIFIER,
+
+       CFG_LAST
+};
+
+static ConfigTable harness_cf_table[] = {
+       { "host", "hostname", 2, 2, 0, ARG_OFFSET|ARG_STRING|CFG_HOST,
+               (void *)offsetof(struct harness_conf_info, h_host),
+               "( OLcfgDbAt:14.1 NAME 'olcBkHarnessHost' "
+                       "DESC 'Hostname to connect to' "
+                       "EQUALITY caseIgnoreMatch "
+                       "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+       { "port", "port", 2, 2, 0, ARG_MAGIC|ARG_UINT|CFG_PORT,
+               &config_generic,
+               "( OLcfgDbAt:14.2 NAME 'olcBkHarnessPort' "
+                       "DESC 'Port to connect to' "
+                       "EQUALITY integerMatch "
+                       "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+       { "identifier", "identifier", 2, 2, 0, ARG_OFFSET|ARG_STRING|CFG_IDENTIFIER,
+               (void *)offsetof(struct harness_conf_info, h_identifier),
+               "( OLcfgDbAt:14.3 NAME 'olcBkHarnessIdentifier' "
+                       "DESC 'A token identifying this server' "
+                       "EQUALITY caseIgnoreMatch "
+                       "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+       { NULL, NULL, 0, 0, 0, ARG_IGNORED, NULL }
+};
+
+static ConfigOCs harness_ocs[] = {
+       { "( OLcfgBkOc:14.1 "
+               "NAME 'olcBkHarnessConfig' "
+               "DESC 'Harness module backend configuration' "
+               "SUP olcBackendConfig "
+               "MUST ( olcBkHarnessHost "
+                       "$ olcBkHarnessPort "
+                       "$ olcBkHarnessIdentifier "
+               ") )",
+               Cft_Backend, harness_cf_table,
+       },
+       { NULL, 0, NULL }
+};
+
+static int
+config_generic(ConfigArgs *c)
+{
+       struct harness_conf_info *hi = c->bi->bi_private;
+       int rc = LDAP_SUCCESS;
+
+       if ( c->op == SLAP_CONFIG_EMIT ) {
+               switch( c->type ) {
+                       case CFG_PORT:
+                               c->value_uint = hi->h_port;
+                               break;
+                       default:
+                               rc = 1;
+                               break;
+               }
+               return rc;
+
+       } else if ( c->op == LDAP_MOD_DELETE ) {
+               /* We don't allow removing/reconfiguration (yet) */
+               Debug( LDAP_DEBUG_ANY,
+                               "%s: mod_harness doesn't support reconfiguration\n",
+                               c->log );
+               return 1;
+       }
+
+       switch ( c->type ) {
+               case CFG_PORT:
+                       if ( c->value_uint <= 0 || c->value_uint > 65535 ) {
+                               Debug( LDAP_DEBUG_ANY,
+                                               "%s: port %d invalid\n",
+                                               c->log, c->value_uint );
+                               rc = 1;
+                       }
+                       hi->h_port = c->value_uint;
+                       break;
+               default:
+                       Debug( LDAP_DEBUG_ANY,
+                                       "%s: unknown CFG_TYPE %d\n",
+                                       c->log, c->type );
+                       return 1;
+       }
+
+       return rc;
+}
+
+int
+harness_back_init_cf( BackendInfo *bi )
+{
+       bi->bi_cf_ocs = harness_ocs;
+
+       /* Make sure we don't exceed the bits reserved for userland */
+       config_check_userland( CFG_LAST );
+
+       return config_register_schema( harness_cf_table, harness_ocs );
+}
diff --git a/tests/modules/mod-harness/connection.c b/tests/modules/mod-harness/connection.c
new file mode 100644 (file)
index 0000000..35f2152
--- /dev/null
@@ -0,0 +1,39 @@
+/* connection.c - communication with test harness */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2007-2019 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>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Ondřej Kuzník for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/errno.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "mod-harness.h"
+
+void *
+harness_callback( void *ctx, void *arg )
+{
+    Debug( LDAP_DEBUG_ANY, "harness_callback: "
+            "not expecting to receive anything yet on this connection!\n" );
+    assert( slapd_shutdown );
+
+    return NULL;
+}
diff --git a/tests/modules/mod-harness/init.c b/tests/modules/mod-harness/init.c
new file mode 100644 (file)
index 0000000..0a72e6d
--- /dev/null
@@ -0,0 +1,389 @@
+/* init.c - initialize test harness backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2007-2019 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>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Ondřej Kuzník for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "mod-harness.h"
+
+struct harness_conf_info harness_info;
+
+static void *
+harness_ready( void *ctx, void *arg )
+{
+    BackendInfo *bi = arg;
+    struct harness_conf_info *hi = bi->bi_private;
+
+    ldap_pvt_thread_mutex_lock( &slapd_init_mutex );
+    while ( !slapd_ready && !slapd_shutdown ) {
+        ldap_pvt_thread_cond_wait( &slapd_init_cond, &slapd_init_mutex );
+    }
+    ldap_pvt_thread_mutex_unlock( &slapd_init_mutex );
+
+    if ( !slapd_shutdown ) {
+        dprintf( hi->h_conn->c_sd, "SLAPD READY\n" );
+    }
+    return NULL;
+}
+
+static int
+harness_resolve_addresses(
+    const char *host,
+    unsigned short port,
+    struct sockaddr ***sal )
+{
+    struct sockaddr **sap;
+
+#ifdef LDAP_PF_LOCAL
+    if ( port == 0 ) {
+        *sal = ch_malloc(2 * sizeof(void *));
+
+        sap = *sal;
+        *sap = ch_malloc(sizeof(struct sockaddr_un));
+        sap[1] = NULL;
+
+        if ( strlen(host) >
+                (sizeof(((struct sockaddr_un *)*sap)->sun_path) - 1) )
+        {
+            Debug( LDAP_DEBUG_ANY, "harness_resolve_addresses: "
+                    "domain socket path (%s) too long in URL\n",
+                    host );
+            goto errexit;
+        }
+
+        (void)memset( (void *)*sap, '\0', sizeof(struct sockaddr_un) );
+        (*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_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, "harness_resolve_addresses: "
+                    "getaddrinfo() failed: %s\n",
+                    AC_GAI_STRERROR(err) );
+            return -1;
+        }
+
+        sai = res;
+        for (n=2; (sai = sai->ai_next) != NULL; n++) {
+            /* EMPTY */ ;
+        }
+        *sal = ch_calloc(n, sizeof(void *));
+        if (*sal == NULL) return -1;
+
+        sap = *sal;
+        *sap = NULL;
+
+        for ( sai=res; sai; sai=sai->ai_next ) {
+            if( sai->ai_addr == NULL ) {
+                Debug( LDAP_DEBUG_ANY, "harness_resolve_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 ( !inet_aton( host, &in ) ) {
+            he = gethostbyname( host );
+            if( he == NULL ) {
+                Debug( LDAP_DEBUG_ANY, "harness_resolve_addresses: "
+                        "invalid host %s\n", host );
+                return -1;
+            }
+            for (n = 0; he->h_addr_list[n]; n++) /* empty */;
+        }
+
+        *sal = ch_malloc((n+1) * sizeof(void *));
+
+        sap = *sal;
+        for ( i = 0; i<n; i++ ) {
+            sap[i] = ch_malloc(sizeof(struct sockaddr_in));
+
+            (void)memset( (void *)sap[i], '\0', sizeof(struct sockaddr_in) );
+            sap[i]->sa_family = he->h_addrtype;
+            switch ( he->h_addrtype ) {
+                case AF_INET:
+                    ((struct sockaddr_in *)sap[i])->sin_port = htons(port);
+                    break;
+#  ifdef LDAP_PF_INET6
+                case AF_INET6:
+                    ((struct sockaddr_in6 *)sap[i])->sin6_port = htons(port);
+                    break;
+#  endif /* LDAP_PF_INET6 */
+                default:
+                    Debug( LDAP_DEBUG_ANY, "harness_resolve_addresses: "
+                            "unknown protocol family from gethostbyname\n" );
+                    goto errexit;
+            }
+
+            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:
+    for (sap = *sal; *sap != NULL; sap++) ch_free(*sap);
+    ch_free(*sal);
+    return -1;
+}
+
+static int
+harness_connect( BackendInfo *bi )
+{
+    struct harness_conf_info *hi = bi->bi_private;
+    struct sockaddr **res, **sal;
+    int rc = -1;
+
+    if ( !hi->h_host || !hi->h_port ) {
+        Debug( LDAP_DEBUG_ANY, "harness_connect: "
+                "configuration incomplete, host or port missing\n" );
+        return rc;
+    }
+
+    if ( harness_resolve_addresses( hi->h_host, hi->h_port, &res ) ) {
+        return rc;
+    }
+
+    for ( sal=res; *sal; sal++ ) {
+        char ebuf[128];
+        Connection *c;
+        char *af;
+        ber_socket_t s;
+        socklen_t addrlen;
+
+        switch ( (*sal)->sa_family ) {
+        case AF_INET:
+            af = "IPv4";
+            addrlen = sizeof(struct sockaddr_in);
+            break;
+#ifdef LDAP_PF_INET6
+        case AF_INET6:
+            af = "IPv6";
+            addrlen = sizeof(struct sockaddr_in6);
+            break;
+#endif /* LDAP_PF_INET6 */
+#ifdef LDAP_PF_LOCAL
+        case AF_LOCAL:
+            af = "Local";
+            addrlen = sizeof(struct sockaddr_un);
+            break;
+#endif /* LDAP_PF_LOCAL */
+        default:
+            sal++;
+            continue;
+        }
+
+        s = socket( (*sal)->sa_family, SOCK_STREAM, 0 );
+        if ( s == AC_SOCKET_INVALID ) {
+            int err = sock_errno();
+            Debug( LDAP_DEBUG_ANY, "harness_connect: "
+                    "%s socket() failed errno=%d (%s)\n",
+                    af, err, sock_errstr( err, ebuf, sizeof(ebuf) ) );
+            continue;
+        }
+
+        if ( connect( s, (struct sockaddr *)*sal, addrlen ) == AC_SOCKET_ERROR ) {
+            int err = sock_errno();
+            Debug( LDAP_DEBUG_ANY, "harness_connect: "
+                    "connect() failed errno=%d (%s)\n",
+                    err, sock_errstr( err, ebuf, sizeof(ebuf) ) );
+            close( s );
+            continue;
+        }
+
+        c = connection_client_setup( s, harness_callback, hi );
+        if ( c == NULL ) {
+            Debug( LDAP_DEBUG_ANY, "harness_connect: "
+                    "could not allocate connection\n" );
+            close( s );
+        }
+
+        hi->h_conn = c;
+        dprintf( c->c_sd, "PID %d %s\n", getpid(), hi->h_identifier );
+        rc = 0;
+        break;
+    }
+
+    ch_free( res );
+    return rc;
+}
+
+static int
+harness_back_open( BackendInfo *bi )
+{
+    struct harness_conf_info *hi = bi->bi_private;
+    Listener **l;
+
+    if ( slapMode & SLAP_TOOL_MODE ) {
+        return 0;
+    }
+
+    if ( harness_connect( bi ) ) {
+        Debug( LDAP_DEBUG_ANY, "harness_back_open: "
+                "failed to contact test harness\n" );
+        return -1;
+    }
+
+    if ( ( l = slapd_get_listeners() ) == NULL ) {
+        Debug( LDAP_DEBUG_ANY, "harness_back_open: "
+                "unable to get listeners\n" );
+        return -1;
+    }
+
+    /* FIXME: A temporary text protocol for human consumption */
+    dprintf( hi->h_conn->c_sd, "LISTENERS\n" );
+    for ( ; *l; l++ ) {
+        dprintf( hi->h_conn->c_sd, "URI=%s %s\n",
+                (*l)->sl_url.bv_val, (*l)->sl_name.bv_val );
+    }
+    dprintf( hi->h_conn->c_sd, "LISTENERS END\n" );
+
+    /* Contact harness as soon as startup finishes and slapd is running */
+    return ldap_pvt_thread_pool_submit( &connection_pool, harness_ready, bi );
+}
+
+static int
+harness_back_close( BackendInfo *bi )
+{
+    struct harness_conf_info *hi = bi->bi_private;
+
+    if ( slapMode & SLAP_TOOL_MODE ) {
+        return 0;
+    }
+
+    if ( slapd_shutdown ) {
+        dprintf( hi->h_conn->c_sd, "SLAPD SHUTDOWN\n" );
+    } else {
+        dprintf( hi->h_conn->c_sd, "MODULE STOPPED\n" );
+    }
+
+    return 0;
+}
+
+static int
+harness_global_init( void )
+{
+    return 0;
+}
+
+static int
+harness_global_destroy( BackendInfo *bi )
+{
+    return 0;
+}
+
+int
+harness_back_initialize( BackendInfo *bi )
+{
+    Debug( LDAP_DEBUG_TRACE, "harness_back_initialize: "
+            "module loaded\n" );
+
+    bi->bi_flags = SLAP_BFLAG_STANDALONE;
+    bi->bi_open = harness_back_open;
+    bi->bi_config = 0;
+    bi->bi_pause = 0;
+    bi->bi_unpause = 0;
+    bi->bi_close = harness_back_close;
+    bi->bi_destroy = harness_global_destroy;
+
+    bi->bi_db_init = 0;
+    bi->bi_db_config = 0;
+    bi->bi_db_open = 0;
+    bi->bi_db_close = 0;
+    bi->bi_db_destroy = 0;
+
+    bi->bi_op_bind = 0;
+    bi->bi_op_unbind = 0;
+    bi->bi_op_search = 0;
+    bi->bi_op_compare = 0;
+    bi->bi_op_modify = 0;
+    bi->bi_op_modrdn = 0;
+    bi->bi_op_add = 0;
+    bi->bi_op_delete = 0;
+    bi->bi_op_abandon = 0;
+
+    bi->bi_extended = 0;
+
+    bi->bi_chk_referrals = 0;
+
+    bi->bi_connection_init = 0;
+    bi->bi_connection_destroy = 0;
+
+    if ( harness_global_init() ) {
+        return -1;
+    }
+
+    bi->bi_private = &harness_info;
+    return harness_back_init_cf( bi );
+}
+
+SLAP_BACKEND_INIT_MODULE( harness )
diff --git a/tests/modules/mod-harness/mod-harness.h b/tests/modules/mod-harness/mod-harness.h
new file mode 100644 (file)
index 0000000..37f3d08
--- /dev/null
@@ -0,0 +1,37 @@
+/* mod-harness.h - mod harness header file */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2019 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 SLAPD_HARNESS_H
+#define SLAPD_HARNESS_H
+
+LDAP_BEGIN_DECL
+
+struct harness_conf_info {
+    char *h_host;
+    in_port_t h_port;
+
+    char *h_identifier;
+
+    Connection *h_conn;
+};
+
+ldap_pvt_thread_start_t harness_callback;
+
+int harness_back_init_cf( BackendInfo *bi );
+
+LDAP_END_DECL
+
+#endif /* SLAPD_HARNESS_H */