package accept import ( "fmt" "os" "os/exec" "path/filepath" "time" "git.asio.asia/nixevol/NixMsg/test/harness" ) // ManagedServer 支持 Kill 后用同一 data_dir 再 Restart(崩溃续传验收)。 type ManagedServer struct { BinPath string ConfigPath string DataDir string Addr string AdminPassword string HTTPBase string AdminHTTPBase string cmd *exec.Cmd } // StartManaged 启动随机端口进程。 func StartManaged() (*ManagedServer, error) { bin, err := harness.Binary() if err != nil { return nil, err } dataDir, err := os.MkdirTemp("", "nixmsg-q3-*") if err != nil { return nil, err } cfgPath := filepath.Join(dataDir, "config.yaml") cfg := fmt.Sprintf("listen: %q\ndata_dir: %q\n", "127.0.0.1:0", filepath.ToSlash(dataDir)) if err = os.WriteFile(cfgPath, []byte(cfg), 0o644); err != nil { _ = os.RemoveAll(dataDir) return nil, err } initCmd := exec.Command(bin, "admin", "init") initCmd.Env = append(os.Environ(), "NIXMSG_CONFIG="+cfgPath) initOut, initErr := initCmd.CombinedOutput() if initErr != nil { _ = os.RemoveAll(dataDir) return nil, fmt.Errorf("admin init: %w\n%s", initErr, initOut) } password := parsePassword(string(initOut)) if password == "" { _ = os.RemoveAll(dataDir) return nil, fmt.Errorf("admin init password not found:\n%s", initOut) } s := &ManagedServer{ BinPath: bin, ConfigPath: cfgPath, DataDir: dataDir, AdminPassword: password, } if err := s.startServe(); err != nil { _ = os.RemoveAll(dataDir) return nil, err } return s, nil } func (s *ManagedServer) startServe() error { _ = os.Remove(filepath.Join(s.DataDir, "listen.addr")) cmd := exec.Command(s.BinPath, "serve") cmd.Env = append(os.Environ(), "NIXMSG_CONFIG="+s.ConfigPath) cmd.Stdout = os.Stderr cmd.Stderr = os.Stderr if err := cmd.Start(); err != nil { return fmt.Errorf("start serve: %w", err) } s.cmd = cmd addr, err := waitListenAddr(filepath.Join(s.DataDir, "listen.addr"), 20*time.Second) if err != nil { _ = cmd.Process.Kill() _, _ = cmd.Process.Wait() return fmt.Errorf("wait listen.addr: %w", err) } s.Addr = addr s.HTTPBase = "http://" + addr s.AdminHTTPBase = s.HTTPBase return nil } // Kill 杀掉进程,保留数据目录。 func (s *ManagedServer) Kill() error { if s == nil || s.cmd == nil || s.cmd.Process == nil { return nil } _ = s.cmd.Process.Kill() _, _ = s.cmd.Process.Wait() s.cmd = nil return nil } // Restart 在同一配置与数据目录上重新 serve。 func (s *ManagedServer) Restart() error { if err := s.Kill(); err != nil { return err } return s.startServe() } // Cleanup 停进程并删除数据目录。 func (s *ManagedServer) Cleanup() error { if s == nil { return nil } _ = s.Kill() if s.DataDir != "" { return os.RemoveAll(s.DataDir) } return nil }