mirror of
https://git.openldap.org/openldap/openldap.git
synced 2025-12-28 02:29:34 -05:00
Allow wrapping of slapd invocations
This commit is contained in:
parent
dc3ad2338b
commit
e051eb28fb
3 changed files with 185 additions and 7 deletions
|
|
@ -66,6 +66,10 @@ case "$SCHEMADIR" in
|
|||
.*) ABS_SCHEMADIR="$TESTWD/$SCHEMADIR" ;;
|
||||
*) ABS_SCHEMADIR="$SCHEMADIR" ;;
|
||||
esac
|
||||
case "$SRCDIR" in
|
||||
.*) ABS_SRCDIR="$TESTWD/$SRCDIR" ;;
|
||||
*) ABS_SRCDIR="$SRCDIR" ;;
|
||||
esac
|
||||
|
||||
DBDIR1A=$TESTDIR/db.1.a
|
||||
DBDIR1B=$TESTDIR/db.1.b
|
||||
|
|
@ -182,6 +186,23 @@ SLURPLOG=$TESTDIR/slurp.log
|
|||
|
||||
CONFIGPWF=$TESTDIR/configpw
|
||||
|
||||
# wrappers (valgrind, gdb, environment variables, etc.)
|
||||
if [ -n "$WRAPPER" ]; then
|
||||
: # skip
|
||||
elif [ "$SLAPD_COMMON_WRAPPER" = gdb ]; then
|
||||
WRAPPER="$ABS_SRCDIR/scripts/grandchild_wrapper.py gdb -nx -x $ABS_SRCDIR/scripts/gdb.py -batch-silent -return-child-result --args"
|
||||
elif [ "$SLAPD_COMMON_WRAPPER" = valgrind ]; then
|
||||
WRAPPER="valgrind --log-file=$TESTDIR/valgrind.%p.log --fullpath-after=`dirname $ABS_SRCDIR` --keep-debuginfo=yes --leak-check=full"
|
||||
elif [ "$SLAPD_COMMON_WRAPPER" = "valgrind-errstop" ]; then
|
||||
WRAPPER="valgrind --log-file=$TESTDIR/valgrind.%p.log --vgdb=yes --vgdb-error=1"
|
||||
elif [ "$SLAPD_COMMON_WRAPPER" = vgdb ]; then
|
||||
WRAPPER="valgrind --log-file=$TESTDIR/valgrind.%p.log --vgdb=yes --vgdb-error=0"
|
||||
fi
|
||||
|
||||
if [ -n "$WRAPPER" ]; then
|
||||
SLAPD_WRAPPER="$TESTWD/../libtool --mode=execute env $WRAPPER"
|
||||
fi
|
||||
|
||||
# args
|
||||
SASLARGS="-Q"
|
||||
TOOLARGS="-x $LDAP_TOOLARGS"
|
||||
|
|
@ -193,11 +214,11 @@ CONFDIRSYNC=$SRCDIR/scripts/confdirsync.sh
|
|||
|
||||
MONITORDATA=$SRCDIR/scripts/monitor_data.sh
|
||||
|
||||
SLAPADD="$TESTWD/../servers/slapd/slapd -Ta -d 0 $LDAP_VERBOSE"
|
||||
SLAPCAT="$TESTWD/../servers/slapd/slapd -Tc -d 0 $LDAP_VERBOSE"
|
||||
SLAPINDEX="$TESTWD/../servers/slapd/slapd -Ti -d 0 $LDAP_VERBOSE"
|
||||
SLAPMODIFY="$TESTWD/../servers/slapd/slapd -Tm -d 0 $LDAP_VERBOSE"
|
||||
SLAPPASSWD="$TESTWD/../servers/slapd/slapd -Tpasswd"
|
||||
SLAPADD="$SLAPD_WRAPPER $TESTWD/../servers/slapd/slapd -Ta -d 0 $LDAP_VERBOSE"
|
||||
SLAPCAT="$SLAPD_WRAPPER $TESTWD/../servers/slapd/slapd -Tc -d 0 $LDAP_VERBOSE"
|
||||
SLAPINDEX="$SLAPD_WRAPPER $TESTWD/../servers/slapd/slapd -Ti -d 0 $LDAP_VERBOSE"
|
||||
SLAPMODIFY="$SLAPD_WRAPPER $TESTWD/../servers/slapd/slapd -Tm -d 0 $LDAP_VERBOSE"
|
||||
SLAPPASSWD="$SLAPD_WRAPPER $TESTWD/../servers/slapd/slapd -Tpasswd"
|
||||
|
||||
unset DIFF_OPTIONS
|
||||
# NOTE: -u/-c is not that portable...
|
||||
|
|
@ -205,8 +226,8 @@ DIFF="diff -i"
|
|||
CMP="diff -i"
|
||||
BCMP="diff -iB"
|
||||
CMPOUT=/dev/null
|
||||
SLAPD="$TESTWD/../servers/slapd/slapd -s0"
|
||||
LLOADD="$TESTWD/../servers/lloadd/lloadd -s0"
|
||||
SLAPD="$SLAPD_WRAPPER $TESTWD/../servers/slapd/slapd -s0"
|
||||
LLOADD="$SLAPD_WRAPPER $TESTWD/../servers/lloadd/lloadd -s0"
|
||||
LDAPPASSWD="$CLIENTDIR/ldappasswd $TOOLARGS"
|
||||
LDAPSASLSEARCH="$CLIENTDIR/ldapsearch $SASLARGS $TOOLPROTO $LDAP_TOOLARGS -LLL"
|
||||
LDAPSASLWHOAMI="$CLIENTDIR/ldapwhoami $SASLARGS $LDAP_TOOLARGS"
|
||||
|
|
|
|||
85
tests/scripts/gdb.py
Normal file
85
tests/scripts/gdb.py
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
# $OpenLDAP$
|
||||
## This work is part of OpenLDAP Software <http://www.openldap.org/>.
|
||||
##
|
||||
## Copyright 2020-2021 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>.
|
||||
"""
|
||||
This GDB script sets up the debugger to run the program and see if it finishes
|
||||
of its own accord or is terminated by a signal (like SIGABRT/SIGSEGV). In the
|
||||
latter case, it saves a full backtrace and core file.
|
||||
|
||||
These signals are considered part of normal operation and will not trigger the
|
||||
above handling:
|
||||
- SIGPIPE: normal in a networked environmnet
|
||||
- SIGHUP: normally used to tell a process to shut down
|
||||
"""
|
||||
|
||||
import os
|
||||
import os.path
|
||||
|
||||
import gdb
|
||||
|
||||
|
||||
def format_program(inferior=None, thread=None):
|
||||
"Format program name and p(t)id"
|
||||
|
||||
if thread:
|
||||
inferior = thread.inferior
|
||||
elif inferior is None:
|
||||
inferior = gdb.selected_inferior()
|
||||
|
||||
try:
|
||||
name = os.path.basename(inferior.progspace.filename)
|
||||
except AttributeError: # inferior has died already
|
||||
name = "unknown"
|
||||
|
||||
if thread:
|
||||
pid = ".".join(tid for tid in thread.ptid if tid)
|
||||
else:
|
||||
pid = inferior.pid
|
||||
|
||||
return "{}.{}".format(name, pid)
|
||||
|
||||
|
||||
def stop_handler(event):
|
||||
"Inferior stopped on a signal, record core, backtrace and exit"
|
||||
|
||||
if not isinstance(event, gdb.SignalEvent):
|
||||
# Ignore breakpoints
|
||||
return
|
||||
|
||||
thread = event.inferior_thread
|
||||
|
||||
identifier = format_program(thread=thread)
|
||||
prefix = os.path.expandvars("${TESTDIR}/") + identifier
|
||||
|
||||
if event.stop_signal == "SIGHUP":
|
||||
# TODO: start a timer to catch shutdown issues/deadlocks
|
||||
gdb.execute("continue")
|
||||
return
|
||||
|
||||
gdb.execute('generate-core-file {}.core'.format(prefix))
|
||||
|
||||
with open(prefix + ".backtrace", "w") as bt_file:
|
||||
backtrace = gdb.execute("thread apply all backtrace full",
|
||||
to_string=True)
|
||||
bt_file.write(backtrace)
|
||||
|
||||
gdb.execute("continue")
|
||||
|
||||
|
||||
# We or we could allow the runner to disable randomisation
|
||||
gdb.execute("set disable-randomization off")
|
||||
|
||||
gdb.execute("handle SIGPIPE noprint")
|
||||
gdb.execute("handle SIGINT pass")
|
||||
gdb.events.stop.connect(stop_handler)
|
||||
gdb.execute("run")
|
||||
72
tests/scripts/grandchild_wrapper.py
Executable file
72
tests/scripts/grandchild_wrapper.py
Executable file
|
|
@ -0,0 +1,72 @@
|
|||
#!/usr/bin/env python3
|
||||
# $OpenLDAP$
|
||||
## This work is part of OpenLDAP Software <http://www.openldap.org/>.
|
||||
##
|
||||
## Copyright 2020-2021 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>.
|
||||
"""
|
||||
Running slapd under GDB in our testsuite, KILLPIDS would record gdb's PID
|
||||
rather than slapd's. When we want the server to shut down, SIGHUP is sent to
|
||||
KILLPIDS but GDB cannot handle being signalled directly and the entire thing is
|
||||
terminated immediately. There might be tests that rely on slapd being given the
|
||||
chance to shut down gracefully, to do this, we need to make sure the signal is
|
||||
actually sent to slapd.
|
||||
|
||||
This script attempts to address this shortcoming in our test suite, serving as
|
||||
the front for gdb/other wrappers, catching SIGHUPs and redirecting them to the
|
||||
oldest living grandchild. The way we start up gdb, that process should be
|
||||
slapd, our intended target.
|
||||
|
||||
This requires the pgrep utility provided by the procps package on Debian
|
||||
systems.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
import signal
|
||||
import sys
|
||||
|
||||
|
||||
async def signal_to_grandchild(child):
|
||||
# Get the first child, that should be the one we're after
|
||||
pgrep = await asyncio.create_subprocess_exec(
|
||||
"pgrep", "-o", "--parent", str(child.pid),
|
||||
stdout=asyncio.subprocess.PIPE)
|
||||
|
||||
stdout, _ = await pgrep.communicate()
|
||||
if not stdout:
|
||||
return
|
||||
|
||||
grandchild = [int(pid) for pid in stdout.split()][0]
|
||||
|
||||
os.kill(grandchild, signal.SIGHUP)
|
||||
|
||||
|
||||
def sighup_handler(child):
|
||||
asyncio.create_task(signal_to_grandchild(child))
|
||||
|
||||
|
||||
async def main(args=None):
|
||||
if args is None:
|
||||
args = sys.argv[1:]
|
||||
|
||||
child = await asyncio.create_subprocess_exec(*args)
|
||||
|
||||
# If we got a SIGHUP before we got the child fully started, there's no
|
||||
# point signalling anyway
|
||||
loop = asyncio.get_running_loop()
|
||||
loop.add_signal_handler(signal.SIGHUP, sighup_handler, child)
|
||||
|
||||
raise SystemExit(await child.wait())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main())
|
||||
Loading…
Reference in a new issue