diff --git a/configure.ac b/configure.ac index a0879bc38c..675bdc74d1 100644 --- a/configure.ac +++ b/configure.ac @@ -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],[[ diff --git a/tests/Makefile.in b/tests/Makefile.in index 1010cc675e..f583a1033b 100644 --- a/tests/Makefile.in +++ b/tests/Makefile.in @@ -14,7 +14,7 @@ ## . 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 index 0000000000..a950812dbe --- /dev/null +++ b/tests/modules/Makefile.in @@ -0,0 +1,16 @@ +# servers Makefile.in for OpenLDAP +# $OpenLDAP$ +## This work is part of OpenLDAP Software . +## +## 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 +## . + +SUBDIRS= mod-harness diff --git a/tests/modules/mod-harness/Makefile.in b/tests/modules/mod-harness/Makefile.in new file mode 100644 index 0000000000..2c702abd12 --- /dev/null +++ b/tests/modules/mod-harness/Makefile.in @@ -0,0 +1,36 @@ +# Makefile.in for mod-harness +# $OpenLDAP$ +## This work is part of OpenLDAP Software . +## +## 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 +## . + +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 index 0000000000..1c8407e09b --- /dev/null +++ b/tests/modules/mod-harness/config.c @@ -0,0 +1,129 @@ +/* config.c - configuration of the test harness backend */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ +/* ACKNOWLEDGEMENTS: + * This work was initially developed by Ondřej Kuzník for inclusion + * in OpenLDAP Software. + */ + +#include "portable.h" + +#include + +#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 index 0000000000..35f215203d --- /dev/null +++ b/tests/modules/mod-harness/connection.c @@ -0,0 +1,39 @@ +/* connection.c - communication with test harness */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ +/* ACKNOWLEDGEMENTS: + * This work was initially developed by Ondřej Kuzník for inclusion + * in OpenLDAP Software. + */ + +#include "portable.h" + +#include + +#include +#include + +#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 index 0000000000..0a72e6d49c --- /dev/null +++ b/tests/modules/mod-harness/init.c @@ -0,0 +1,389 @@ +/* init.c - initialize test harness backend */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ +/* ACKNOWLEDGEMENTS: + * This work was initially developed by Ondřej Kuzník for inclusion + * in OpenLDAP Software. + */ + +#include "portable.h" + +#include + +#include +#include +#include + +#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; isa_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 index 0000000000..37f3d08dca --- /dev/null +++ b/tests/modules/mod-harness/mod-harness.h @@ -0,0 +1,37 @@ +/* mod-harness.h - mod harness header file */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ + +#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 */