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