diff --git a/rosys/run.py b/rosys/run.py index 5cdfb3adb..139387557 100644 --- a/rosys/run.py +++ b/rosys/run.py @@ -61,22 +61,31 @@ async def sh(command: list[str] | str, *, def popen() -> str: cmd_list = command if isinstance(command, list) else shlex.split(command) if timeout is not None: - cmd_list = ['timeout', '--signal=SIGKILL', str(timeout)] + cmd_list + cmd_list = ['timeout', '--signal=SIGTERM', str(timeout)] + cmd_list cmd = ' '.join(cmd_list) if shell else cmd_list - # log.info(f'running sh: "{cmd}"') - proc = subprocess.Popen( # pylint: disable=consider-using-with - cmd, - cwd=working_dir, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - shell=shell, - start_new_session=True, - ) - running_sh_processes.append(proc) - stdout, *_ = proc.communicate() - _kill(proc) - running_sh_processes.remove(proc) - return stdout.decode('utf-8') + try: + with subprocess.Popen( + cmd, + cwd=working_dir, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + shell=shell, + start_new_session=True, + ) as proc: + running_sh_processes.append(proc) + stdout, stderr = proc.communicate() + assert proc.stdout is not None + assert proc.stderr is not None + proc.stdout.close() + proc.stderr.close() + _kill(proc) + running_sh_processes.remove(proc) + return stdout.decode('utf-8') if proc.returncode == 0 else stderr.decode('utf-8') + except Exception: + log.exception(f'failed to run command "{cmd}"') + if 'proc' in locals() and proc: + _kill(proc) + return '' if is_stopping(): return '' @@ -90,9 +99,17 @@ def popen() -> str: def _kill(proc: Popen) -> None: try: os.killpg(os.getpgid(proc.pid), signal.SIGTERM) - log.info(f'killed {proc.pid}') + log.info(f'sent SIGTERM to {proc.pid}') + try: + proc.wait(timeout=5) # wait for 5 seconds + except subprocess.TimeoutExpired: + os.killpg(os.getpgid(proc.pid), signal.SIGKILL) # force kill if process didn't terminate + log.info(f'sent SIGKILL to {proc.pid}') + proc.wait() # ensure the process is reaped except ProcessLookupError: pass + except Exception: + log.exception(f'Failed to kill and/or wait for process {proc.pid}') def tear_down() -> None: diff --git a/rosys/vision/rtsp_camera_provider_hardware.py b/rosys/vision/rtsp_camera_provider_hardware.py index c1f63ec42..282205568 100644 --- a/rosys/vision/rtsp_camera_provider_hardware.py +++ b/rosys/vision/rtsp_camera_provider_hardware.py @@ -1,6 +1,7 @@ import asyncio import io import logging +import os import shlex import subprocess import sys @@ -47,10 +48,13 @@ def __init__(self, *, frame_rate: int = 6, jovision_profile: int = 0) -> None: self._cameras: dict[str, RtspCamera] = {} self._capture_tasks: dict[str, asyncio.Task] = {} self._processes: list[Process] = [] + if sys.platform.startswith('darwin'): - self.arpscan_cmd = 'sudo arp-scan' + self.arpscan_cmd = 'arp-scan' else: - self.arpscan_cmd = 'sudo /usr/sbin/arp-scan' + self.arpscan_cmd = '/usr/sbin/arp-scan' + if os.getuid() != 0: + self.arpscan_cmd = f'sudo {self.arpscan_cmd}' rosys.on_shutdown(self.shutdown) rosys.on_repeat(self.update_device_list, 1) diff --git a/scripts/watch_wifi_configs.py b/scripts/watch_wifi_configs.py index 2c6c26ccb..c472b4556 100755 --- a/scripts/watch_wifi_configs.py +++ b/scripts/watch_wifi_configs.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +import os import subprocess import time from pathlib import Path @@ -10,7 +11,9 @@ def nmcli(cmd: str) -> None: - cmd = 'sudo nmcli connection ' + cmd + cmd = f'nmcli connection {cmd}' + if os.getuid() != 0: + cmd = f'sudo {cmd}' subprocess.Popen(cmd, shell=True)