Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure no zombie processes are left over by run.sh #47

Merged
merged 10 commits into from
Oct 23, 2023
49 changes: 33 additions & 16 deletions rosys/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 ''
Expand All @@ -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:
Expand Down
8 changes: 6 additions & 2 deletions rosys/vision/rtsp_camera_provider_hardware.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import asyncio
import io
import logging
import os
import shlex
import subprocess
import sys
Expand Down Expand Up @@ -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)
Expand Down
5 changes: 4 additions & 1 deletion scripts/watch_wifi_configs.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#!/usr/bin/env python3
import os
import subprocess
import time
from pathlib import Path
Expand All @@ -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)


Expand Down
Loading