feat: SSH 成功获取系统信息时视为机器在线

在连通性检测逻辑中,当 network ping / ICMP / TCP 三层探测均失败后,
若机器配置了 SSH 凭据,则同步尝试调用 GetSSHInfo 进行 SSH 信息采集:
- 若 SSH 采集成功,则将该机器判定为在线,并保存获取到的系统信息;
- 若 SSH 采集也失败,则保持离线状态,按原逻辑记录离线日志。

此改进可避免以下场景导致的误判:
- 机器禁 ping 或 ICMP 被防火墙拦截;
- SSH 端口未被网络层探测正确识别;
- 只要 SSH 服务正常且能正确登录并获取系统信息,即认为机器处于可用状态。

其他改动:
- 提取 saveSSHResult 辅助函数,统一保存 SSH 采集结果,避免重复代码。
- 同步更新了本地与 Linux 部署二进制文件。
This commit is contained in:
shirainbown
2026-04-15 01:39:16 +08:00
parent 1a5f5ee3b5
commit d34ed5842a

View File

@@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"lan-manager/server/db"
"lan-manager/server/models"
"lan-manager/server/utils"
"net"
"os"
@@ -146,9 +147,43 @@ type machinePing struct {
wasOnline bool
}
func saveSSHResult(mid int64, mip string, mport int, result *models.SSHInfoResult) {
portsStr := ""
if len(result.ListenPorts) > 0 {
b, _ := json.Marshal(result.ListenPorts)
portsStr = string(b)
if len(portsStr) > 2 {
portsStr = portsStr[1 : len(portsStr)-1]
}
}
_, dbErr := db.DB.Exec(`UPDATE machines SET cpu_info=?, memory_info=?, disk_info=?, uptime=?, listen_ports=?, ssh_synced_at=CURRENT_TIMESTAMP, updated_at=CURRENT_TIMESTAMP WHERE id=?`,
result.RawCPUInfo, result.RawMemoryInfo, result.RawDiskInfo, result.Uptime, portsStr, mid)
if dbErr != nil {
fmt.Printf("[SSH] auto-fetch db error for %s:%d: %v\n", mip, mport, dbErr)
} else {
fmt.Printf("[SSH] auto-fetch success for %s:%d\n", mip, mport)
}
}
func handlePingResult(m *machinePing, res PingResult) {
online := res.Online
reason := res.Reason
var sshResult *models.SSHInfoResult
// 如果网络层检测失败,尝试通过 SSH 获取系统信息来判定在线
if !online && m.sshUsername != "" && m.sshPassword != "" {
plainPass, decryptErr := utils.Decrypt(m.sshPassword)
if decryptErr == nil {
result, err := GetSSHInfo(m.ip, m.sshPort, m.sshUsername, plainPass)
if err == nil {
online = true
reason = ""
sshResult = result
fmt.Printf("[Ping] %s -> network unreachable, but SSH info fetched successfully, treating as online\n", m.ip)
}
}
}
var onlineInt int
if online {
onlineInt = 1
@@ -185,6 +220,9 @@ func handlePingResult(m *machinePing, res PingResult) {
_, _ = db.DB.Exec(`UPDATE machines SET total_offline_seconds=total_offline_seconds+? WHERE id=?`, duration, m.id)
}
m.wasOnline = true
if sshResult != nil {
saveSSHResult(m.id, m.ip, m.sshPort, sshResult)
}
return
}
@@ -197,33 +235,23 @@ func handlePingResult(m *machinePing, res PingResult) {
}
if online && m.sshUsername != "" && m.sshPassword != "" {
go func(mid int64, mip string, mport int, muser, mpass string) {
plainPass, decryptErr := utils.Decrypt(mpass)
if decryptErr != nil {
fmt.Printf("[SSH] decrypt failed for %s:%d: %v\n", mip, mport, decryptErr)
return
}
result, err := GetSSHInfo(mip, mport, muser, plainPass)
if err != nil {
fmt.Printf("[SSH] auto-fetch failed for %s:%d: %v\n", mip, mport, err)
return
}
portsStr := ""
if len(result.ListenPorts) > 0 {
b, _ := json.Marshal(result.ListenPorts)
portsStr = string(b)
if len(portsStr) > 2 {
portsStr = portsStr[1 : len(portsStr)-1]
if sshResult != nil {
saveSSHResult(m.id, m.ip, m.sshPort, sshResult)
} else {
go func(mid int64, mip string, mport int, muser, mpass string) {
plainPass, decryptErr := utils.Decrypt(mpass)
if decryptErr != nil {
fmt.Printf("[SSH] decrypt failed for %s:%d: %v\n", mip, mport, decryptErr)
return
}
}
_, dbErr := db.DB.Exec(`UPDATE machines SET cpu_info=?, memory_info=?, disk_info=?, uptime=?, listen_ports=?, ssh_synced_at=CURRENT_TIMESTAMP, updated_at=CURRENT_TIMESTAMP WHERE id=?`,
result.RawCPUInfo, result.RawMemoryInfo, result.RawDiskInfo, result.Uptime, portsStr, mid)
if dbErr != nil {
fmt.Printf("[SSH] auto-fetch db error for %s:%d: %v\n", mip, mport, dbErr)
} else {
fmt.Printf("[SSH] auto-fetch success for %s:%d\n", mip, mport)
}
}(m.id, m.ip, m.sshPort, m.sshUsername, m.sshPassword)
result, err := GetSSHInfo(mip, mport, muser, plainPass)
if err != nil {
fmt.Printf("[SSH] auto-fetch failed for %s:%d: %v\n", mip, mport, err)
return
}
saveSSHResult(mid, mip, mport, result)
}(m.id, m.ip, m.sshPort, m.sshUsername, m.sshPassword)
}
}
}