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:
@@ -30,6 +30,7 @@ async def start_cli_process(*args: str) -> asyncio.subprocess.Process:
|
|||||||
*cmd,
|
*cmd,
|
||||||
stdout=asyncio.subprocess.PIPE,
|
stdout=asyncio.subprocess.PIPE,
|
||||||
stderr=asyncio.subprocess.STDOUT,
|
stderr=asyncio.subprocess.STDOUT,
|
||||||
|
start_new_session=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
19
tui/jobs.py
19
tui/jobs.py
@@ -1,4 +1,6 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
|
import os
|
||||||
|
import signal
|
||||||
import uuid
|
import uuid
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@@ -77,23 +79,24 @@ class JobManager:
|
|||||||
app.post_message(JobFinished(job.id, rc))
|
app.post_message(JobFinished(job.id, rc))
|
||||||
return job.return_code if job.return_code is not None else 1
|
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:
|
def kill_job(self, job_id: str) -> bool:
|
||||||
job = self._jobs.get(job_id)
|
job = self._jobs.get(job_id)
|
||||||
if not job or job._proc is None:
|
if not job or job._proc is None:
|
||||||
return False
|
return False
|
||||||
try:
|
self._kill_process_group(job._proc)
|
||||||
job._proc.terminate()
|
|
||||||
return True
|
return True
|
||||||
except ProcessLookupError:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def kill_running(self) -> None:
|
def kill_running(self) -> None:
|
||||||
for job in self._jobs.values():
|
for job in self._jobs.values():
|
||||||
if job._proc is not None:
|
if job._proc is not None:
|
||||||
try:
|
self._kill_process_group(job._proc)
|
||||||
job._proc.terminate()
|
|
||||||
except ProcessLookupError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
job_manager = JobManager()
|
job_manager = JobManager()
|
||||||
|
|||||||
Reference in New Issue
Block a user