From c4f1d251c338e1232c99b56d37be46efb220320c Mon Sep 17 00:00:00 2001
From: Eric Callahan <arksine.code@gmail.com>
Date: Mon, 15 Jan 2024 12:20:05 -0500
Subject: [PATCH] klippy_connection: add support for service info fallback

If Klipper is using systemd socket activation to generate its
unix socket the PID reported by PEERCRED will be 1, that of
systemd itself.  Klipper now reports its process id in the
"info" endpoint, use that as a fallback to retreive service info.

Signed-off-by: Eric Callahan <arksine.code@gmail.com>
---
 moonraker/components/klippy_connection.py | 39 ++++++++++++++++-------
 1 file changed, 27 insertions(+), 12 deletions(-)

diff --git a/moonraker/components/klippy_connection.py b/moonraker/components/klippy_connection.py
index 9867730..0694838 100644
--- a/moonraker/components/klippy_connection.py
+++ b/moonraker/components/klippy_connection.py
@@ -289,16 +289,7 @@ class KlippyConnection:
                 logging.info("Klippy Connection Established")
                 self.writer = writer
                 if self._get_peer_credentials(writer):
-                    machine: Machine = self.server.lookup_component("machine")
-                    provider = machine.get_system_provider()
-                    svc_info = await provider.extract_service_info(
-                        "klipper", self._peer_cred["process_id"]
-                    )
-                    if svc_info != self._service_info:
-                        db: Database = self.server.lookup_component('database')
-                        db.insert_item("moonraker", SVC_INFO_KEY, svc_info)
-                        self._service_info = svc_info
-                        machine.log_service_info(svc_info)
+                    await self._get_service_info(self._peer_cred["process_id"])
             self.event_loop.create_task(self._read_stream(reader))
             return await self._init_klippy_connection()
 
@@ -311,14 +302,28 @@ class KlippyConnection:
             str(self.uds_address), limit=UNIX_BUFFER_LIMIT)
 
     def _get_peer_credentials(self, writer: asyncio.StreamWriter) -> bool:
-        self._peer_cred = get_unix_peer_credentials(writer, "Klippy")
-        if not self._peer_cred:
+        peer_cred = get_unix_peer_credentials(writer, "Klippy")
+        if not peer_cred:
             return False
+        if peer_cred.get("process_id") == 1:
+            logging.debug("Klipper Unix Socket created via Systemd Socket Activation")
+            return False
+        self._peer_cred = peer_cred
         logging.debug(
             f"Klippy Connection: Received Peer Credentials: {self._peer_cred}"
         )
         return True
 
+    async def _get_service_info(self, process_id: int) -> None:
+        machine: Machine = self.server.lookup_component("machine")
+        provider = machine.get_system_provider()
+        svc_info = await provider.extract_service_info("klipper", process_id)
+        if svc_info != self._service_info:
+            db: Database = self.server.lookup_component('database')
+            db.insert_item("moonraker", SVC_INFO_KEY, svc_info)
+            self._service_info = svc_info
+            machine.log_service_info(svc_info)
+
     async def _init_klippy_connection(self) -> bool:
         self._klippy_identified = False
         self._klippy_started = False
@@ -387,6 +392,16 @@ class KlippyConnection:
             self._klipper_version = version
             msg = f"Klipper Version: {version}"
             self.server.add_log_rollover_item("klipper_version", msg)
+        klipper_pid: Optional[int] = result.get("process_id")
+        if klipper_pid is not None:
+            cur_pid: Optional[int] = self._peer_cred.get("process_id")
+            if cur_pid is None or klipper_pid != cur_pid:
+                self._peer_cred = dict(
+                    process_id=klipper_pid,
+                    group_id=result.get("group_id", -1),
+                    user_id=result.get("user_id", -1)
+                )
+                await self._get_service_info(klipper_pid)
         self._klippy_info = dict(result)
         state_message: str = self._state.message
         if "state_message" in self._klippy_info: