From 0c80ae1108207726ae8ec55578722ee7ecc18d23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 1 Apr 2024 15:30:30 +0200 Subject: [PATCH] powerprofilesctl: Use posix-compliant exit codes on signals Do not return the python exit code when the launched process exit because of a signal, but instead expose it as it is. --- src/powerprofilesctl | 21 +++++++++++++++------ tests/integration_test.py | 23 ++++++++++++++++++++--- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/src/powerprofilesctl b/src/powerprofilesctl index d70d767..4d55661 100755 --- a/src/powerprofilesctl +++ b/src/powerprofilesctl @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import argparse +import os import signal import subprocess import sys @@ -124,21 +125,29 @@ def _launch(args): ) cookie = proxy.HoldProfile("(sss)", profile, reason, appid) - # Kill child when we go away - def receive_signal(_signum, _stack): - launched_app.terminate() - - signal.signal(signal.SIGTERM, receive_signal) - # print (f'Got {cookie} for {profile} hold') with subprocess.Popen(args.arguments) as launched_app: + # Redirect the same signal to the child + def receive_signal(signum, _stack): + launched_app.send_signal(signum) + + signal.signal(signal.SIGTERM, receive_signal) + try: launched_app.wait() ret = launched_app.returncode except KeyboardInterrupt: ret = launched_app.returncode + + signal.signal(signal.SIGTERM, signal.SIG_DFL) + proxy.ReleaseProfile("(u)", cookie) + if ret < 0: + # Use standard POSIX signal exit code. + os.kill(os.getpid(), -ret) + return + sys.exit(ret) diff --git a/tests/integration_test.py b/tests/integration_test.py index 1ecf46f..e29a312 100644 --- a/tests/integration_test.py +++ b/tests/integration_test.py @@ -21,6 +21,7 @@ import os import subprocess +import signal import sys import tempfile import time @@ -1625,9 +1626,9 @@ class Tests(dbusmock.DBusTestCase): released_cookie = None - def signal_cb(_, sender, signal, params): + def signal_cb(_, sender, signal_name, params): nonlocal released_cookie - if signal == "ProfileReleased": + if signal_name == "ProfileReleased": released_cookie = params self.addCleanup( @@ -1772,6 +1773,22 @@ class Tests(dbusmock.DBusTestCase): cmd = subprocess.run(tool_cmd + ["launch", "sh", "-c", "exit 55"], check=False) self.assertEqual(cmd.returncode, 55) + def test_launch_with_command_signaled(self): + self.create_platform_profile() + self.start_daemon() + self.assert_eventually(lambda: self.get_dbus_property("ActiveProfile")) + + tool_cmd = self.powerprofilesctl_command() + cmd = subprocess.run( + tool_cmd + ["launch", "sh", "-c", f"kill -{signal.SIGKILL} $$"], check=False + ) + self.assertEqual(cmd.returncode, -signal.SIGKILL) + + cmd = subprocess.run( + tool_cmd + ["launch", "sh", "-c", f"kill -{signal.SIGINT} $$"], check=False + ) + self.assertEqual(cmd.returncode, -signal.SIGINT) + def test_vanishing_hold(self): self.create_platform_profile() self.start_daemon() @@ -1793,7 +1810,7 @@ class Tests(dbusmock.DBusTestCase): # Make sure to handle vanishing clients launch_process.terminate() retcode = launch_process.wait() - self.assertTrue(os.WIFSIGNALED(retcode)) + self.assertEqual(retcode, -signal.SIGTERM) holds = self.get_dbus_property("ActiveProfileHolds") self.assertEqual(len(holds), 0)