From a5f1510dc67274f46781b3811ffa22044fa23f35 Mon Sep 17 00:00:00 2001 From: Stamate Viorel Date: Wed, 10 Jun 2026 14:33:33 +0200 Subject: [PATCH] Run LMS stream processes in their own process group disconnect() falls back to os.killpg(self.proc.pid, SIGKILL), but the spawned process was never made a process-group leader, so proc.pid is not a valid pgid: the killpg either fails or signals the parent's whole group. Start both squeezelite and the metadata reader with preexec_fn=os.setpgrp so each owns its group, kill via os.killpg(os.getpgid(pid)) on the real group, and tolerate ProcessLookupError when the process already exited. Signed-off-by: Stamate Viorel Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 1 + amplipi/streams/lms.py | 14 ++++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd1894792..2c374fd1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * Upgraded volume calculations to preserve relative positions when hitting the min or max setting via source volume bar * Update our spotify provider `go-librespot` to `0.7.3` * Upgrade from Logitech Media Server 8.5.2 to Lyrion Music Server 9.0.3 + * Fixed LMS stream shutdown force-killing the wrong process group # 0.4.11 * System diff --git a/amplipi/streams/lms.py b/amplipi/streams/lms.py index db9b097f5..9e615f3f7 100644 --- a/amplipi/streams/lms.py +++ b/amplipi/streams/lms.py @@ -90,9 +90,9 @@ def _activate(self, vsrc: int): meta_args.extend(["--server", f"{self.server}"]) if self.port is not None: meta_args.extend(["--port", f"{self.port}"]) - self.meta_proc = subprocess.Popen(args=meta_args, stdout=sys.stdout, stderr=sys.stderr) + self.meta_proc = subprocess.Popen(args=meta_args, stdout=sys.stdout, stderr=sys.stderr, preexec_fn=os.setpgrp) - self.proc = subprocess.Popen(args=lms_args) + self.proc = subprocess.Popen(args=lms_args, preexec_fn=os.setpgrp) except Exception as exc: logger.exception(f'error starting lms: {exc}') @@ -106,7 +106,10 @@ def _deactivate(self): except Exception as e: logger.exception(f"failed to gracefully terminate LMS stream {self.name}: {e}") logger.warning(f"forcefully killing LMS stream {self.name}") - os.killpg(self.proc.pid, signal.SIGKILL) + try: + os.killpg(os.getpgid(self.proc.pid), signal.SIGKILL) + except ProcessLookupError: + pass self.proc.communicate(timeout=3) if self.meta_proc is not None: @@ -116,7 +119,10 @@ def _deactivate(self): except Exception as e: logger.exception(f"failed to gracefully terminate LMS meta proc for {self.name}: {e}") logger.warning(f"forcefully killing LMS meta proc for {self.name}") - os.killpg(self.meta_proc.pid, signal.SIGKILL) + try: + os.killpg(os.getpgid(self.meta_proc.pid), signal.SIGKILL) + except ProcessLookupError: + pass self.meta_proc.communicate(timeout=3) self.proc = None