From 06d06fdd1e1d01da82b7c0ea3cecd41b2992f896 Mon Sep 17 00:00:00 2001 From: stswangzhiping <59632378+stswangzhiping@users.noreply.github.com> Date: Sat, 28 Mar 2026 14:54:56 +0800 Subject: [PATCH] fix(systemd): use systemd-notify + NotifyAccess=all for watchdog unix_dgram to NOTIFY_SOCKET failed on rk3528 (embedded Node), so WATCHDOG=1 never reached systemd despite WATCHDOG_USEC fallback. Switch to exec systemd-notify with NOTIFY_SOCKET in child env; unit sets NotifyAccess=all so cgroup may notify. Users must reload unit (re-run install.sh or add NotifyAccess=all manually). Made-with: Cursor --- install.sh | 2 ++ lib/client.js | 51 ++++++++++++++++++++------------------------------- 2 files changed, 22 insertions(+), 31 deletions(-) diff --git a/install.sh b/install.sh index 262ab98..1063444 100644 --- a/install.sh +++ b/install.sh @@ -188,6 +188,8 @@ Wants=NetworkManager.service [Service] Type=simple +# systemd-notify 由子进程执行,默认 NotifyAccess=main 会拒收;需 all 才能喂 WatchdogSec +NotifyAccess=all EnvironmentFile=$ENV_FILE ExecStart=$NODE_BIN $INSTALL_DIR/bin/clawd.js WorkingDirectory=$INSTALL_DIR diff --git a/lib/client.js b/lib/client.js index 191e7a7..db229a4 100644 --- a/lib/client.js +++ b/lib/client.js @@ -2,8 +2,7 @@ const { getNotifySocket } = require('./systemd-env'); const WebSocket = require('ws'); -const dgram = require('dgram'); -const { execSync } = require('child_process'); +const { execSync, execFileSync } = require('child_process'); const config = require('./config'); const log = require('./logger'); const { getBoxId } = require('./fingerprint'); @@ -56,10 +55,8 @@ class ClawClient { this._certTimeError = false; - // systemd watchdog(主进程 unix_dgram → NOTIFY_SOCKET) - this._sdTimer = null; - this._sdDgram = null; - this._sdNotifyAddr = null; + // systemd watchdog(systemd-notify 子进程 + unit 里 NotifyAccess=all) + this._sdTimer = null; this._setupGlobalHandlers(); } @@ -182,11 +179,6 @@ class ClawClient { if (this._ws) this._ws.terminate(); led.status.off(); // 进程退出,两灯全灭 this._sdNotify('STOPPING=1'); - if (this._sdDgram) { - try { this._sdDgram.close(); } catch (_) {} - this._sdDgram = null; - } - this._sdNotifyAddr = null; log.info('clawd', '已停止'); log.close(); } @@ -574,35 +566,32 @@ class ClawClient { } const intervalMs = Math.max(1000, Math.floor(usec / 2 / 1000)); - // 抽象套接字:NOTIFY_SOCKET 以 @ 开头,内核地址首字节为 \0(与 sd_notify 一致) - this._sdNotifyAddr = raw.startsWith('@') ? `\0${raw.slice(1)}` : raw; - - try { - this._sdDgram = dgram.createSocket('unix_dgram'); - this._sdDgram.on('error', (err) => { - log.warn('clawd', 'systemd notify socket:', err.message); - }); - } catch (err) { - this._sdNotifyAddr = null; - log.warn('clawd', 'systemd notify dgram 创建失败:', err.message); - return; - } - log.debug('clawd', `systemd watchdog 启用,通知间隔 ${intervalMs}ms`); this._sdNotify('READY=1'); this._sdTimer = setInterval(() => this._sdNotify('WATCHDOG=1'), intervalMs); } /** - * 必须由本进程(主 PID)发往 NOTIFY_SOCKET;exec systemd-notify 会换 PID, - * systemd 在 NotifyAccess=main 下会拒绝并刷屏 journal。 + * 通过 systemd-notify 写入 NOTIFY_SOCKET。嵌入式上 Node unix_dgram 对抽象套接字常无效; + * 子进程发 notify 需在 unit 中设置 NotifyAccess=all(install.sh 已写)。 */ _sdNotify(msg) { - if (!this._sdDgram || !this._sdNotifyAddr) return; - const payload = Buffer.from(msg.endsWith('\n') ? msg : `${msg}\n`); + const sock = getNotifySocket(); + if (!sock) return; + const arg = String(msg).replace(/\n+$/, ''); + if (!arg) return; try { - this._sdDgram.send(payload, 0, payload.length, this._sdNotifyAddr, () => {}); - } catch (_) { /* ignore */ } + execFileSync('systemd-notify', [arg], { + timeout: 3000, + encoding: 'utf8', + env: { + PATH: process.env.PATH || '/usr/bin:/bin', + NOTIFY_SOCKET: sock, + }, + }); + } catch (e) { + log.warn('clawd', 'systemd-notify 失败:', e.message); + } } }