#!/usr/bin/env python3 # $OpenLDAP$ ## This work is part of OpenLDAP Software . ## ## 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 ## . """ 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())