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