From aae5bc36de59392942f8d8c66f70c4baed818ea2 Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Sun, 2 Aug 2026 12:10:44 +0800 Subject: [PATCH] =?UTF-8?q?fix(agent):=20systemctl=20=E6=A8=A1=E5=BC=8F=20?= =?UTF-8?q?Status=20=E8=A1=A5=E5=85=85=E5=A4=B1=E8=B4=A5=E5=8E=9F=E5=9B=A0?= =?UTF-8?q?(LastError)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit enable=false 时走 NewSystemCtlController,其 Status() 在服务未运行时 只返回 Running:false 无原因。现通过 systemctl show 获取 ActiveState/SubState/Result/ExecMainStatus/NRestarts, 给出可读提示: '启动失败(退出码 X, 已重启 N 次)' 或 '未运行(systemctl: x/y)' --- edge/agent/internal/procctl/systemctl.go | 42 ++++++++++++++++++++---- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/edge/agent/internal/procctl/systemctl.go b/edge/agent/internal/procctl/systemctl.go index 0cf3eae..7a4c71d 100644 --- a/edge/agent/internal/procctl/systemctl.go +++ b/edge/agent/internal/procctl/systemctl.go @@ -29,24 +29,24 @@ func (s *SystemCtlController) Status() (Status, error) { // 检查服务是否运行 cmd := exec.Command("systemctl", "is-active", "--quiet", s.serviceName) err := cmd.Run() - + if err != nil { - // 服务未运行 - return Status{Running: false}, nil + // 服务未运行:附带失败原因(退出码/重启次数),供 UI 展示 + return Status{Running: false, LastError: s.failureHint()}, nil } - + // 获取 PID pidCmd := exec.Command("systemctl", "show", "--property=MainPID", "--value", s.serviceName) out, err := pidCmd.Output() if err != nil { return Status{Running: true}, nil // 运行但无法获取 PID } - + pid, _ := strconv.Atoi(strings.TrimSpace(string(out))) if pid <= 0 { return Status{Running: true}, nil } - + return Status{ Running: true, Pid: pid, @@ -54,6 +54,36 @@ func (s *SystemCtlController) Status() (Status, error) { }, nil } +// failureHint 查询 systemctl 状态,给出服务未运行的可用原因(如启动失败/退出码) +func (s *SystemCtlController) failureHint() string { + out, err := exec.Command("systemctl", "show", "--property=ActiveState,SubState,Result,ExecMainStatus,NRestarts", s.serviceName).Output() + if err != nil { + return fmt.Sprintf("媒体服务未运行 (%s)", s.serviceName) + } + props := map[string]string{} + for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n") { + if idx := strings.IndexByte(line, '='); idx > 0 { + props[strings.TrimSpace(line[:idx])] = strings.TrimSpace(line[idx+1:]) + } + } + switch { + case props["SubState"] == "failed" || props["Result"] == "exit-code": + code := props["ExecMainStatus"] + if code == "" { + code = "?" + } + restarts := props["NRestarts"] + if restarts == "" { + restarts = "0" + } + return fmt.Sprintf("媒体服务启动失败(退出码 %s,已重启 %s 次)", code, restarts) + case props["ActiveState"] != "" || props["SubState"] != "": + return fmt.Sprintf("媒体服务未运行(systemctl: %s/%s)", props["ActiveState"], props["SubState"]) + default: + return fmt.Sprintf("媒体服务未运行 (%s)", s.serviceName) + } +} + func (s *SystemCtlController) Version() (string, error) { // 从 systemctl 获取 ExecStart 路径,再调用 --version // Output format: { path=/path/to/binary ; argv[]=... }