From dfd040a9de6987d3bb22d46a057d450b0bb510b7 Mon Sep 17 00:00:00 2001 From: ryan Date: Tue, 2 Jun 2026 21:30:48 +0800 Subject: [PATCH] =?UTF-8?q?[=E4=BC=98=E5=8C=96]=20=E7=A1=AE=E4=BF=9D?= =?UTF-8?q?=E6=89=80=E6=9C=89=E7=AE=A1=E7=90=86=E7=9A=84=E8=BF=9B=E7=A8=8B?= =?UTF-8?q?=E5=9C=A8=E5=81=9C=E6=AD=A2=E6=97=B6=E8=A2=AB=E6=AD=A3=E7=A1=AE?= =?UTF-8?q?=E5=8F=96=E6=B6=88=E5=92=8C=E6=B8=85=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + openflared/internal/flared/runner.go | 1 + openflared/internal/frpc/manager.go | 19 ++++++++- openflared/internal/frpc/manager_test.go | 50 ++++++++++++++++++++++++ 4 files changed, 70 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 077ebe52..e8967240 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,4 @@ go.work.sum *-source *-source.* +.codex* \ No newline at end of file diff --git a/openflared/internal/flared/runner.go b/openflared/internal/flared/runner.go index ef2a958d..8f35dc8c 100644 --- a/openflared/internal/flared/runner.go +++ b/openflared/internal/flared/runner.go @@ -31,6 +31,7 @@ func (r *Runner) Run(ctx context.Context) error { for { select { case <-ctx.Done(): + r.FrpcManager.Stop() return ctx.Err() default: } diff --git a/openflared/internal/frpc/manager.go b/openflared/internal/frpc/manager.go index b6528931..32f85503 100644 --- a/openflared/internal/frpc/manager.go +++ b/openflared/internal/frpc/manager.go @@ -150,7 +150,10 @@ func (m *Manager) restartProcess(ctx context.Context, relayID string, configPath _ = os.Remove(pidPath) } - procCtx, cancel := context.WithCancel(context.Background()) + if ctx == nil { + ctx = context.Background() + } + procCtx, cancel := context.WithCancel(ctx) proc := &Process{ RelayID: relayID, Cancel: cancel, @@ -223,6 +226,20 @@ func (m *Manager) restartProcess(ctx context.Context, relayID string, configPath }() } +func (m *Manager) Stop() { + m.mu.Lock() + defer m.mu.Unlock() + + for relayID, proc := range m.processes { + if proc != nil && proc.Cancel != nil { + proc.Cancel() + } + pidPath := filepath.Join(m.cfg.DataDir, fmt.Sprintf("frpc_%s.pid", relayID)) + _ = os.Remove(pidPath) + delete(m.processes, relayID) + } +} + func buildFrpcToml(relay service.FlaredRelayInfo, proxies []service.FlaredProxyEntry) string { var buf bytes.Buffer diff --git a/openflared/internal/frpc/manager_test.go b/openflared/internal/frpc/manager_test.go index 5e0d0bb1..3c85b829 100644 --- a/openflared/internal/frpc/manager_test.go +++ b/openflared/internal/frpc/manager_test.go @@ -335,3 +335,53 @@ func TestUpdateConfigKillsOrphanProcessBeforeRestart(t *testing.T) { m.mu.RUnlock() proc.Cancel() } + +func TestStopCancelsRunningProcesses(t *testing.T) { + scriptPath, dir := setupDummyScript(t) + writeControl(t, dir, 0, 30) + + cfg := &config.Config{ + ServerURL: "http://localhost:8080", + TunnelToken: "test-token", + FrpcPath: scriptPath, + DataDir: dir, + StatePath: filepath.Join(dir, "flared-state.json"), + } + + m := NewManager(cfg) + newConfig := &service.FlaredTunnelConfigResponse{ + Version: "1", + Checksum: "sum1", + Relays: []service.FlaredRelayInfo{ + { + RelayNodeID: "relay-1", + Address: "127.0.0.1:7000", + AuthToken: "auth-1", + }, + }, + } + + if err := m.UpdateConfig(context.Background(), newConfig); err != nil { + t.Fatalf("failed to UpdateConfig: %v", err) + } + + assertStatusEventually(t, m, "relay-1", "running", 4*time.Second) + + m.mu.RLock() + proc := m.processes["relay-1"] + if proc == nil || proc.Cmd == nil { + m.mu.RUnlock() + t.Fatal("expected running process to have a command handle") + } + cmd := proc.Cmd + m.mu.RUnlock() + + m.Stop() + assertCommandExitedEventually(t, cmd, 2*time.Second) + + m.mu.RLock() + defer m.mu.RUnlock() + if len(m.processes) != 0 { + t.Fatalf("expected no managed processes after stop, got %d", len(m.processes)) + } +}