diff options
Diffstat (limited to 'tests/scripts/grandchild_wrapper.py')
-rwxr-xr-x | tests/scripts/grandchild_wrapper.py | 72 |
1 files changed, 72 insertions, 0 deletions
diff --git a/tests/scripts/grandchild_wrapper.py b/tests/scripts/grandchild_wrapper.py new file mode 100755 index 0000000..b5e7194 --- /dev/null +++ b/tests/scripts/grandchild_wrapper.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +# $OpenLDAP$ +## This work is part of OpenLDAP Software <http://www.openldap.org/>. +## +## Copyright 2020-2022 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()) |