Kill entire process group when stopping a job

Start CLI subprocesses in their own session so SIGTERM via
os.killpg reaches child processes (rsync, etc.) not just the shell.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
shuki
2026-03-06 18:11:37 +02:00
parent 2379c2fdeb
commit c96930f3ff
2 changed files with 13 additions and 9 deletions

View File

@@ -30,6 +30,7 @@ async def start_cli_process(*args: str) -> asyncio.subprocess.Process:
*cmd,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.STDOUT,
start_new_session=True,
)

View File

@@ -1,4 +1,6 @@
import asyncio
import os
import signal
import uuid
from dataclasses import dataclass, field
from datetime import datetime
@@ -77,23 +79,24 @@ class JobManager:
app.post_message(JobFinished(job.id, rc))
return job.return_code if job.return_code is not None else 1
@staticmethod
def _kill_process_group(proc: asyncio.subprocess.Process) -> None:
try:
os.killpg(proc.pid, signal.SIGTERM)
except (ProcessLookupError, PermissionError):
pass
def kill_job(self, job_id: str) -> bool:
job = self._jobs.get(job_id)
if not job or job._proc is None:
return False
try:
job._proc.terminate()
return True
except ProcessLookupError:
return False
self._kill_process_group(job._proc)
return True
def kill_running(self) -> None:
for job in self._jobs.values():
if job._proc is not None:
try:
job._proc.terminate()
except ProcessLookupError:
pass
self._kill_process_group(job._proc)
job_manager = JobManager()