From ab024325433fe93c81e8553673a54cb51ae8c28a Mon Sep 17 00:00:00 2001 From: Virgil Date: Sat, 4 Apr 2026 01:22:58 +0000 Subject: [PATCH] fix(process): unify pid kill handling --- service.go | 27 +++++++++++++++++++++++++++ service_test.go | 11 +++++++++++ 2 files changed, 38 insertions(+) diff --git a/service.go b/service.go index 7e30f5a..4794310 100644 --- a/service.go +++ b/service.go @@ -417,6 +417,17 @@ func (s *Service) KillPID(pid int) error { return coreerr.E("Service.KillPID", "pid must be positive", nil) } + if proc := s.findByPID(pid); proc != nil { + sent, err := proc.kill() + if err != nil { + return err + } + if sent { + s.emitKilledAction(proc, "SIGKILL") + } + return nil + } + if err := syscall.Kill(pid, syscall.SIGTERM); err != nil { return coreerr.E("Service.KillPID", fmt.Sprintf("failed to signal pid %d", pid), err) } @@ -475,6 +486,22 @@ func (s *Service) Output(id string) (string, error) { return proc.Output(), nil } +// findByPID locates a managed process by operating-system PID. +func (s *Service) findByPID(pid int) *Process { + s.mu.RLock() + defer s.mu.RUnlock() + + for _, proc := range s.processes { + proc.mu.RLock() + matches := proc.cmd != nil && proc.cmd.Process != nil && proc.cmd.Process.Pid == pid + proc.mu.RUnlock() + if matches { + return proc + } + } + return nil +} + // Run executes a command and waits for completion. // Returns the combined output and any error. // diff --git a/service_test.go b/service_test.go index 4520d6c..5d91099 100644 --- a/service_test.go +++ b/service_test.go @@ -585,6 +585,14 @@ func TestService_OnStartup(t *testing.T) { require.NoError(t, err) require.True(t, proc.IsRunning()) + var killed []ActionProcessKilled + c.RegisterAction(func(cc *framework.Core, msg framework.Message) framework.Result { + if m, ok := msg.(ActionProcessKilled); ok { + killed = append(killed, m) + } + return framework.Result{OK: true} + }) + result := c.PERFORM(TaskProcessKill{PID: proc.Info().PID}) require.True(t, result.OK) @@ -595,6 +603,9 @@ func TestService_OnStartup(t *testing.T) { } assert.Equal(t, StatusKilled, proc.Status) + require.Len(t, killed, 1) + assert.Equal(t, proc.ID, killed[0].ID) + assert.NotEmpty(t, killed[0].Signal) }) t.Run("registers process.list task", func(t *testing.T) {