2026-06-08 07:44:37 +08:00
|
|
|
# <<*>> -^v- SNOWARE APPLICATION
|
|
|
|
|
# usage: Notify systemd.
|
|
|
|
|
# author: S.A.
|
|
|
|
|
# time: 2026-06-07
|
|
|
|
|
# Copyright (c) [2026] [S.A. SNOWARE-SCU]
|
|
|
|
|
# [MineSentinel] is licensed under Mulan PubL v2.
|
|
|
|
|
# You can use this software according to the terms and conditions of the Mulan PubL v2.
|
|
|
|
|
# You may obtain a copy of Mulan PubL v2 at:
|
|
|
|
|
# http://license.coscl.org.cn/MulanPubL-2.0
|
|
|
|
|
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
|
|
|
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
|
|
|
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
|
|
|
# See the Mulan PubL v2 for more details.
|
|
|
|
|
|
2026-06-08 07:30:21 +08:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
2026-06-08 11:47:00 +08:00
|
|
|
# Sends keep-alive notifications (including WATCHDOG=1) to systemd
|
|
|
|
|
# via the NOTIFY_SOCKET datagram socket, enabling service supervision.
|
2026-06-08 07:30:21 +08:00
|
|
|
class SystemdNotifier
|
|
|
|
|
require_relative 'method_executor'
|
|
|
|
|
|
|
|
|
|
|
2026-06-08 11:47:00 +08:00
|
|
|
@notify_socket = ENV.fetch('NOTIFY_SOCKET', nil)
|
|
|
|
|
@watchdog_usec = (ENV['WATCHDOG_USEC'] || '0').to_i
|
|
|
|
|
|
|
|
|
|
if @notify_socket && File.socket?(@notify_socket)
|
|
|
|
|
MethodExecutor.record('debug', 'heartbeat', 'Found NOTIFY_SOCKET, enabling heartbeat')
|
2026-06-08 07:30:21 +08:00
|
|
|
@enable_heartbeat = true
|
2026-06-08 08:10:18 +08:00
|
|
|
# Fix(2026-06-08): 将 UNIXSocket(SOCK_STREAM) 改为 SOCK_DGRAM
|
|
|
|
|
# systemd NOTIFY_SOCKET 是 datagram socket,用 stream socket 连接会报
|
|
|
|
|
# Protocol wrong type for socket (Errno::EPROTOTYPE)
|
|
|
|
|
@socket = Socket.new(Socket::AF_UNIX, Socket::SOCK_DGRAM)
|
|
|
|
|
@socket.connect(Socket.pack_sockaddr_un(@notify_socket))
|
2026-06-08 11:47:00 +08:00
|
|
|
MethodExecutor.record('debug', 'heartbeat', 'Heartbeat socket opened')
|
|
|
|
|
else
|
|
|
|
|
MethodExecutor.record('error', 'heartbeat', 'NOTIFY_SOCKET is not set or is not a socket, am I a service?')
|
|
|
|
|
@enable_heartbeat = false
|
2026-06-08 07:30:21 +08:00
|
|
|
end
|
|
|
|
|
|
2026-06-08 11:35:51 +08:00
|
|
|
# @return [Boolean]
|
2026-06-08 07:30:21 +08:00
|
|
|
def enabled?
|
|
|
|
|
@enable_heartbeat
|
|
|
|
|
end
|
|
|
|
|
|
2026-06-08 11:35:51 +08:00
|
|
|
# @param message [String]
|
|
|
|
|
# @return [void]
|
2026-06-08 07:54:55 +08:00
|
|
|
def self.notify(message)
|
2026-06-08 07:30:21 +08:00
|
|
|
return unless @enable_heartbeat
|
2026-06-08 11:47:00 +08:00
|
|
|
|
2026-06-08 07:30:21 +08:00
|
|
|
begin
|
|
|
|
|
@socket.send(message, 0)
|
2026-06-08 11:47:00 +08:00
|
|
|
MethodExecutor.record('debug', 'heartbeat', "Heartbeat sent: #{message}")
|
|
|
|
|
rescue StandardError => e
|
|
|
|
|
MethodExecutor.record('error', 'heartbeat', "Failed to send heartbeat: #{e}")
|
2026-06-08 07:30:21 +08:00
|
|
|
@enable_heartbeat = false
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|