fix: 修复systemd socket协议、格式化代码、拆分配置常量
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
INITIAL_GRACE = 300 # 初次启动等待 300 秒
|
||||||
|
TRY_TIMES = 3 # 尝试次数
|
||||||
|
RST_COOLDOWN_DURATION = 200 # 重启冷却期 200 秒
|
||||||
@@ -12,74 +12,70 @@
|
|||||||
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
# See the Mulan PubL v2 for more details.
|
# See the Mulan PubL v2 for more details.
|
||||||
|
|
||||||
# main.rb 顶部添加
|
def main
|
||||||
begin
|
puts ' ___ __ ___ ___ ___ '
|
||||||
# 原有代码
|
puts ' |\/| | |\ | |__ /__` |__ |\ | | | |\ | |__ | '
|
||||||
rescue => e
|
puts ' | | | | \| |___ .__/ |___ | \| | | | \| |___ |___ '
|
||||||
# 强制输出到stderr
|
puts " MineSentinel dev-rolling version by <*> S.A. [@SNOWARE] and owned by SCU team."
|
||||||
$stderr.puts "[CRITICAL] Unhandled exception: #{e.message}"
|
puts " Copyleft (c) 2026 S.A. SNOWARE-SCU. This program is licensed under Mulan PubL v2."
|
||||||
$stderr.puts e.backtrace.join("\n")
|
puts " E-mail: SALflake@qq.com | QQ: 758522192 | Website: https://swe-iss.rth1.xyz/softwares/minesentinel."
|
||||||
exit 1
|
puts
|
||||||
end
|
|
||||||
|
|
||||||
puts ' ___ __ ___ ___ ___ '
|
require_relative 'method_executor' # 日志系统在此时初始化
|
||||||
puts ' |\/| | |\ | |__ /__` |__ |\ | | | |\ | |__ | '
|
MethodExecutor.record("debug", "init", "Initialized MethodExecutor, which implements logging system and intervening functions.")
|
||||||
puts ' | | | | \| |___ .__/ |___ | \| | | | \| |___ |___ '
|
require_relative 'server_status_detector'
|
||||||
puts " MineSentinel dev-rolling version by <*> S.A. [@SNOWARE] and owned by SCU team."
|
require_relative 'systemd_notifier' # 心跳系统在此时初始化
|
||||||
puts " Copyleft (c) 2026 S.A. SNOWARE-SCU. This program is licensed under Mulan PubL v2."
|
require_relative 'config'
|
||||||
puts " E-mail: SALflake@qq.com | QQ: 758522192 | Website: https://https://swe-iss.rth1.xyz/softwares/minesentinel."
|
MethodExecutor.record("debug", "init", "Initialized SystemdNotifier, which implements systemd watchdog feeder.")
|
||||||
puts
|
|
||||||
|
|
||||||
require_relative 'method_executor' # 日志系统在此时初始化
|
# 确认是否具有展开干预操作的权限 如果因场景不同不需要请注释这部分内容
|
||||||
MethodExecutor.record("debug", "init", "Initialized MethodExecutor, which implements logging system and intervening functions.")
|
unless Process.euid == 0
|
||||||
require_relative 'server_status_detector'
|
MethodExecutor.record("error", "init", "Permission denied: root required to intervene Systemd Services.")
|
||||||
require_relative 'systemd_notifier' # 心跳系统在此时初始化
|
raise Errno::EPERM
|
||||||
MethodExecutor.record("debug", "init", "Initialized SystemdNotifier, which implements systemd watchdog feeder.")
|
|
||||||
|
|
||||||
# 确认是否具有展开干预操作的权限 如果因场景不同不需要请注释这部分内容
|
|
||||||
unless Process.euid == 0
|
|
||||||
MethodExecutor.record("error", "init", "Permission denied: root required to intervene Systemd Services.")
|
|
||||||
raise Errno::EPERM
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
MethodExecutor.record("info", "mainloop", "MineSentinel started, entering mainloop.")
|
|
||||||
|
|
||||||
# 初次启动:MineSentinel 先于 minecraft 启动(systemd Before=),
|
|
||||||
# 等待服务器端口就绪,避免误判为崩溃
|
|
||||||
INITIAL_GRACE = 300 # 初次启动等待 300 秒
|
|
||||||
MethodExecutor.record("info", "mainloop",
|
|
||||||
"Initial startup grace period #{INITIAL_GRACE}s — waiting for server to come up...")
|
|
||||||
grace_deadline = Time.now + INITIAL_GRACE
|
|
||||||
while Time.now < grace_deadline
|
|
||||||
sleep 5
|
|
||||||
if ServerStatusDetector::MinecraftServer.self_diag[:type]
|
|
||||||
MethodExecutor.record("info", "mainloop",
|
|
||||||
"Server port is open during grace period, resuming normal monitoring.")
|
|
||||||
break
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
MethodExecutor.record("info", "mainloop", "MineSentinel started, entering mainloop.")
|
||||||
|
|
||||||
|
# 初次启动:MineSentinel 先于 minecraft 启动(systemd Before=),
|
||||||
|
# 等待服务器端口就绪,避免误判为崩溃
|
||||||
|
MethodExecutor.record("info", "mainloop",
|
||||||
|
"Initial startup grace period #{INITIAL_GRACE}s — waiting for server to come up...")
|
||||||
|
grace_deadline = Time.now + INITIAL_GRACE
|
||||||
|
while Time.now < grace_deadline
|
||||||
|
sleep 5
|
||||||
|
if ServerStatusDetector::MinecraftServer.self_diag[:type]
|
||||||
|
MethodExecutor.record("info", "mainloop",
|
||||||
|
"Server port is open during grace period, resuming normal monitoring.")
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
MethodExecutor.record("info", "mainloop", "Grace period ended.")
|
||||||
|
main_loop
|
||||||
end
|
end
|
||||||
MethodExecutor.record("info", "mainloop", "Grace period ended.")
|
|
||||||
|
|
||||||
# Functional mainloop.
|
# Functional mainloop.
|
||||||
def main
|
def main_once
|
||||||
chkrslt = ServerStatusDetector::MinecraftServer.self_diag
|
chkrslts = []
|
||||||
if chkrslt[:type]
|
for i in 0..TRY_TIMES
|
||||||
|
chkrslt = ServerStatusDetector::MinecraftServer.self_diag
|
||||||
|
chkrslts.append(chkrslt)
|
||||||
|
end
|
||||||
|
if chkrslts.all? { |chkrslt_in| chkrslt_in[:type] }
|
||||||
MethodExecutor.record("debug", "mainloop", "Server health check passed.")
|
MethodExecutor.record("debug", "mainloop", "Server health check passed.")
|
||||||
else
|
else
|
||||||
MethodExecutor.record("warn", "mainloop", "Server health check FAILED: #{chkrslt[:message]}")
|
for chkrslt_in in chkrslts
|
||||||
for operation in chkrslt[:operation]
|
MethodExecutor.record("warn", "mainloop", "Server health check FAILED: #{chkrslt_in[:message]}")
|
||||||
if operation == "Record"
|
end
|
||||||
# 消息已在 self_diag 中生成,这里仅记录操作
|
MethodExecutor.record("info", "mainloop", "Checked #{TRY_TIMES} time.")
|
||||||
MethodExecutor.record("warn", "mainloop", "Action: Record -> Server died!")
|
if chkrslts.any? { |chkrslt_in| chkrslt_in[:operation] == "Record" }
|
||||||
elsif operation == "ForceResetServerProcess"
|
MethodExecutor.record("warn", "mainloop", "Action: Record -> Server died!")
|
||||||
|
if chkrslts.any? { |chkrslt_in| chkrslt_in[:message] == "Server died for unknown reason, and will be restarted." }
|
||||||
MethodExecutor.record("info", "mainloop", "Action: ForceResetServerProcess triggered.")
|
MethodExecutor.record("info", "mainloop", "Action: ForceResetServerProcess triggered.")
|
||||||
MethodExecutor.reset_minecraft_server
|
MethodExecutor.reset_minecraft_server
|
||||||
# 重启后等待服务器启动(MC服务器启动通常需要30秒~2分钟)
|
# 重启后等待服务器启动(MC服务器启动通常需要30秒~2分钟)
|
||||||
cooldown_duration = 200 # 200秒冷却期
|
|
||||||
cooldown_beg = Time.now
|
cooldown_beg = Time.now
|
||||||
MethodExecutor.record("info", "mainloop", "Cooling down #{cooldown}s for server startup...")
|
MethodExecutor.record("info", "mainloop", "Cooling down #{RST_COOLDOWN_DURATION}s for server startup...")
|
||||||
while Time.now < cooldown_beg + cooldown_duration
|
while Time.now < cooldown_beg + RST_COOLDOWN_DURATION
|
||||||
sleep 1
|
sleep 1
|
||||||
SystemdNotifier.notify("WATCHDOG=1")
|
SystemdNotifier.notify("WATCHDOG=1")
|
||||||
end
|
end
|
||||||
@@ -89,9 +85,19 @@ def main
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def main_loop
|
||||||
while true
|
while true
|
||||||
sleep 1.5
|
sleep 1.5
|
||||||
SystemdNotifier.notify("WATCHDOG=1")
|
SystemdNotifier.notify("WATCHDOG=1")
|
||||||
main
|
main_once
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
main
|
||||||
|
rescue => e
|
||||||
|
# 强制输出到stderr
|
||||||
|
$stderr.puts "[CRITICAL] Unhandled exception: #{e.message}"
|
||||||
|
$stderr.puts e.backtrace.join("\n")
|
||||||
|
exit 1
|
||||||
end
|
end
|
||||||
|
|||||||
+35
-37
@@ -77,48 +77,46 @@ class MethodExecutor
|
|||||||
|
|
||||||
@@rst_counter += 1
|
@@rst_counter += 1
|
||||||
|
|
||||||
unless Process.euid == 0
|
unless Process.euid == 0
|
||||||
self.record("error", "reset", "Permission denied: root required to manage systemd services.")
|
self.record("error", "reset", "Permission denied: root required to manage systemd services.")
|
||||||
raise Errno::EPERM
|
raise Errno::EPERM
|
||||||
end
|
end
|
||||||
|
|
||||||
self.record("info", "reset", "Spawning 'systemctl restart minecraft' (PID will be captured)...")
|
self.record("info", "reset", "Spawning 'systemctl restart minecraft' (PID will be captured)...")
|
||||||
pid = spawn "systemctl restart minecraft"
|
pid = spawn "systemctl restart minecraft"
|
||||||
self.record("debug", "reset", "systemctl restart spawned with PID #{pid}.")
|
self.record("debug", "reset", "systemctl restart spawned with PID #{pid}.")
|
||||||
tick_beg = Time.now
|
tick_beg = Time.now
|
||||||
|
|
||||||
while true
|
while true
|
||||||
sleep 0.5
|
sleep 0.5
|
||||||
begin
|
SystemdNotifier.notify("WATCHDOG=1")
|
||||||
SystemdNotifier.notify("WATCHDOG=1")
|
proc_status = Process.waitpid2(pid, Process::WNOHANG)
|
||||||
proc_status = Process.waitpid2(pid, Process::WNOHANG)
|
if proc_status # 子进程已退出,返回 [pid, status]
|
||||||
if proc_status # 子进程已退出,返回 [pid, status]
|
_, status = proc_status
|
||||||
_, status = proc_status
|
elapsed = (Time.now - tick_beg).round(2)
|
||||||
elapsed = (Time.now - tick_beg).round(2)
|
if status.success?
|
||||||
if status.success?
|
self.record("info", "reset", "'systemctl restart' succeeded in #{elapsed}s.")
|
||||||
self.record("info", "reset", "'systemctl restart' succeeded in #{elapsed}s.")
|
@@rst_counter = 0 # 成功时重置计数器
|
||||||
@@rst_counter = 0 # 成功时重置计数器
|
break
|
||||||
break
|
else
|
||||||
else
|
self.record("error", "reset", "'systemctl restart' failed with exitcode #{status.exitstatus} after #{elapsed}s.")
|
||||||
self.record("error", "reset", "'systemctl restart' failed with exitcode #{status.exitstatus} after #{elapsed}s.")
|
break
|
||||||
break
|
|
||||||
end
|
|
||||||
else # 子进程仍在运行 (WNOHANG 返回 nil)
|
|
||||||
if Time.now - tick_beg > PERSISTENCE_PERIOD then
|
|
||||||
self.record("warn", "reset",
|
|
||||||
"'systemctl restart' timed out after #{PERSISTENCE_PERIOD}s. Force-killing PID #{pid}...")
|
|
||||||
system "kill -9 #{pid}"
|
|
||||||
self.record("warn", "reset", "Running 'systemctl kill minecraft' (last resort)...")
|
|
||||||
system "systemctl kill minecraft" # 没招了,毁灭吧
|
|
||||||
sleep 0.5
|
|
||||||
self.record("info", "reset", "Running 'systemctl start minecraft' (rebirth)...")
|
|
||||||
system "systemctl start minecraft" # 重生
|
|
||||||
self.record("info", "reset", "Force-reset sequence completed.")
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
else # 子进程仍在运行 (WNOHANG 返回 nil)
|
||||||
|
if Time.now - tick_beg > PERSISTENCE_PERIOD then
|
||||||
|
self.record("warn", "reset",
|
||||||
|
"'systemctl restart' timed out after #{PERSISTENCE_PERIOD}s. Force-killing PID #{pid}...")
|
||||||
|
system "kill -9 #{pid}"
|
||||||
|
self.record("warn", "reset", "Running 'systemctl kill minecraft' (last resort)...")
|
||||||
|
system "systemctl kill minecraft" # 没招了,毁灭吧
|
||||||
|
sleep 0.5
|
||||||
|
self.record("info", "reset", "Running 'systemctl start minecraft' (rebirth)...")
|
||||||
|
system "systemctl start minecraft" # 重生
|
||||||
|
self.record("info", "reset", "Force-reset sequence completed.")
|
||||||
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ module ServerStatusDetector
|
|||||||
|
|
||||||
class MinecraftServer < TcpServer # 继承为TcpServer编写的属性和方法
|
class MinecraftServer < TcpServer # 继承为TcpServer编写的属性和方法
|
||||||
|
|
||||||
def self.self_diag()
|
def self_diag
|
||||||
|
|
||||||
res = test_port('127.0.0.1', 25565)
|
res = test_port('127.0.0.1', 25565)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user