@@ -19,12 +19,14 @@ import (
1919 "log/slog"
2020 "math"
2121 "os"
22+ "os/exec"
2223 "os/user"
2324 "path/filepath"
2425 "regexp"
2526 "slices"
2627 "strconv"
2728 "strings"
29+ "syscall"
2830 "unicode"
2931)
3032
@@ -537,13 +539,36 @@ func GetAppDir() string {
537539
538540// SignalProcess sends a signal to the process with the given PID
539541func SignalProcess (pid int , sig os.Signal ) error {
540- proc , err := os .FindProcess (pid )
542+ proc , err := os .FindProcess (pid ) // FindProcess always succeeds on Linux
541543 if err != nil {
542- return fmt .Errorf ("failed to find process: %w" , err )
544+ return fmt .Errorf ("this shouldn't happen: failed to find process: %w" , err )
545+ }
546+ err = proc .Signal (syscall .Signal (0 )) // detect if process exists
547+ if err != nil {
548+ return fmt .Errorf ("process with pid %d does not exist: %w" , pid , err )
543549 }
544550 slog .Debug ("sending signal to process" , slog .Int ("pid" , pid ), slog .String ("signal" , sig .String ()))
545551 err = proc .Signal (sig )
546552 if err != nil {
553+ // Check if EPERM - try sudo as fallback
554+ var errno syscall.Errno
555+ if errors .As (err , & errno ) && errno == syscall .EPERM {
556+ slog .Debug ("permission denied, attempting with sudo" , slog .Int ("pid" , pid ))
557+ // Convert os.Signal to signal number
558+ var sigNum int
559+ if s , ok := sig .(syscall.Signal ); ok {
560+ sigNum = int (s )
561+ } else {
562+ return fmt .Errorf ("unsupported signal type: %T" , sig )
563+ }
564+ // Try sudo kill as fallback
565+ cmd := exec .Command ("sudo" , "-n" , "kill" , "-" + strconv .Itoa (sigNum ), strconv .Itoa (pid ))
566+ if cmdErr := cmd .Run (); cmdErr == nil {
567+ slog .Debug ("successfully killed process with sudo" , slog .Int ("pid" , pid ))
568+ return nil
569+ }
570+ slog .Debug ("sudo kill also failed" , slog .Int ("pid" , pid ))
571+ }
547572 return fmt .Errorf ("failed to send signal to process (pid %d): %w" , pid , err )
548573 }
549574 return nil
0 commit comments