From c66283a25e99daa59b7716216c3a640f0e9602a3 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 cc553efe5..12094e2fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ * Added in-place preamp recovery when I2C writes fail persistently * Fixed all loopback dmix devices sharing one ipc_key in asound.conf, which made loopback playback devices intermittently fail to open with EINVAL (#957) * Disabled USB autosuspend for the CM6206 USB audio device + * Fixed LMS stream shutdown force-killing the wrong process group * Web App * Add warning for older versions of the webapp running on newer backends 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