1# Copyright (c) 2023 Nordic Semiconductor ASA
2#
3# SPDX-License-Identifier: Apache-2.0
4
5from __future__ import annotations
6
7import contextlib
8import logging
9import os
10import platform
11import shlex
12import signal
13import subprocess
14import time
15
16import psutil
17
18_WINDOWS = platform.system() == 'Windows'
19
20
21def log_command(logger: logging.Logger, msg: str, args: list, level: int = logging.DEBUG) -> None:
22    """
23    Platform-independent helper for logging subprocess invocations.
24
25    Will log a command string that can be copy/pasted into a POSIX
26    shell on POSIX platforms. This is not available on Windows, so
27    the entire args array is logged instead.
28
29    :param logger: logging.Logger to use
30    :param msg: message to associate with the command
31    :param args: argument list as passed to subprocess module
32    :param level: log level
33    """
34    msg = f'{msg}: %s'
35    if _WINDOWS:
36        logger.log(level, msg, str(args))
37    else:
38        logger.log(level, msg, shlex.join(args))
39
40
41def terminate_process(proc: subprocess.Popen) -> None:
42    """
43    Try to terminate provided process and all its subprocesses recursively.
44    """
45    with contextlib.suppress(ProcessLookupError, psutil.NoSuchProcess):
46        for child in psutil.Process(proc.pid).children(recursive=True):
47            with contextlib.suppress(ProcessLookupError, psutil.NoSuchProcess):
48                os.kill(child.pid, signal.SIGTERM)
49    proc.terminate()
50    # sleep for a while before attempting to kill
51    time.sleep(0.5)
52    proc.kill()
53