import {platform} from "node:os"; import type {UnixDgramSocket} from "unix-dgram"; import logger from "./logger"; /** * Handle sd_notify protocol, @see https://www.freedesktop.org/software/systemd/man/latest/sd_notify.html * No-op if running on unsupported platforms or without Type=notify * Soft-fails if improperly setup (this is not necessary for Zigbee2MQTT to function properly) */ export async function initSdNotify(): Promise<{notifyStopping: () => void; stop: () => void} | undefined> { if (!process.env.NOTIFY_SOCKET) { return; } let socket: UnixDgramSocket | undefined; try { const {createSocket} = await import("unix-dgram"); socket = createSocket("unix_dgram"); } catch (error) { if (platform() !== "win32" || process.env.WSL_DISTRO_NAME) { // not on plain Windows logger.error(`Could not init sd_notify: ${(error as Error).message}`); // biome-ignore lint/style/noNonNullAssertion: always Error logger.debug((error as Error).stack!); } else { // this should not happen logger.warning(`NOTIFY_SOCKET env is set: ${(error as Error).message}`); } return; } const sendToSystemd = (msg: string): void => { const buffer = Buffer.from(msg); // biome-ignore lint/style/noNonNullAssertion: valid from start of function socket.send(buffer, 0, buffer.byteLength, process.env.NOTIFY_SOCKET!, (err) => { if (err) { logger.warning(`Failed to send "${msg}" to systemd: ${err.message}`); } }); }; const notifyStopping = (): void => sendToSystemd("STOPPING=1"); sendToSystemd("READY=1"); const wdUSec = process.env.WATCHDOG_USEC !== undefined ? Math.max(0, Number.parseInt(process.env.WATCHDOG_USEC, 10)) : -1; if (wdUSec > 0) { // Convert us to ms, send twice as frequently as the timeout const watchdogInterval = setInterval(() => sendToSystemd("WATCHDOG=1"), wdUSec / 1000 / 2); return { notifyStopping, stop: (): void => clearInterval(watchdogInterval), }; } if (wdUSec !== -1) { logger.warning(`WATCHDOG_USEC invalid: "${process.env.WATCHDOG_USEC}", parsed to "${wdUSec}"`); } return { notifyStopping, stop: (): void => {}, }; }