1#!/usr/bin/env python3 2# $OpenLDAP$ 3## This work is part of OpenLDAP Software <http://www.openldap.org/>. 4## 5## Copyright 2020-2021 The OpenLDAP Foundation. 6## All rights reserved. 7## 8## Redistribution and use in source and binary forms, with or without 9## modification, are permitted only as authorized by the OpenLDAP 10## Public License. 11## 12## A copy of this license is available in the file LICENSE in the 13## top-level directory of the distribution or, alternatively, at 14## <http://www.OpenLDAP.org/license.html>. 15""" 16Running slapd under GDB in our testsuite, KILLPIDS would record gdb's PID 17rather than slapd's. When we want the server to shut down, SIGHUP is sent to 18KILLPIDS but GDB cannot handle being signalled directly and the entire thing is 19terminated immediately. There might be tests that rely on slapd being given the 20chance to shut down gracefully, to do this, we need to make sure the signal is 21actually sent to slapd. 22 23This script attempts to address this shortcoming in our test suite, serving as 24the front for gdb/other wrappers, catching SIGHUPs and redirecting them to the 25oldest living grandchild. The way we start up gdb, that process should be 26slapd, our intended target. 27 28This requires the pgrep utility provided by the procps package on Debian 29systems. 30""" 31 32import asyncio 33import os 34import signal 35import sys 36 37 38async def signal_to_grandchild(child): 39 # Get the first child, that should be the one we're after 40 pgrep = await asyncio.create_subprocess_exec( 41 "pgrep", "-o", "--parent", str(child.pid), 42 stdout=asyncio.subprocess.PIPE) 43 44 stdout, _ = await pgrep.communicate() 45 if not stdout: 46 return 47 48 grandchild = [int(pid) for pid in stdout.split()][0] 49 50 os.kill(grandchild, signal.SIGHUP) 51 52 53def sighup_handler(child): 54 asyncio.create_task(signal_to_grandchild(child)) 55 56 57async def main(args=None): 58 if args is None: 59 args = sys.argv[1:] 60 61 child = await asyncio.create_subprocess_exec(*args) 62 63 # If we got a SIGHUP before we got the child fully started, there's no 64 # point signalling anyway 65 loop = asyncio.get_running_loop() 66 loop.add_signal_handler(signal.SIGHUP, sighup_handler, child) 67 68 raise SystemExit(await child.wait()) 69 70 71if __name__ == '__main__': 72 asyncio.run(main()) 73