findXrayProcesses finds all running xray processes by executable path Returns process information including PID, PPID, and zombie state
(executablePath string)
| 17 | // findXrayProcesses finds all running xray processes by executable path |
| 18 | // Returns process information including PID, PPID, and zombie state |
| 19 | func findXrayProcesses(executablePath string) ([]ProcessInfo, error) { |
| 20 | absPath, err := filepath.Abs(executablePath) |
| 21 | if err != nil { |
| 22 | return nil, err |
| 23 | } |
| 24 | |
| 25 | // Prefer /proc on Linux (works on Alpine/busybox) to avoid relying on ps flags |
| 26 | if runtime.GOOS == "linux" { |
| 27 | if procs, perr := findXrayProcessesFromProc(absPath); perr == nil { |
| 28 | // /proc is authoritative; even if empty, that's a valid result |
| 29 | return procs, nil |
| 30 | } |
| 31 | } |
| 32 | |
| 33 | // Use ps to find processes with PID, PPID, state, and command |
| 34 | cmd := exec.Command("ps", "-eo", "pid,ppid,state,command") |
| 35 | output, err := cmd.Output() |
| 36 | if err != nil { |
| 37 | return nil, fmt.Errorf("failed to list processes: %w", err) |
| 38 | } |
| 39 | |
| 40 | var processes []ProcessInfo |
| 41 | lines := strings.Split(string(output), "\n") |
| 42 | executableName := filepath.Base(absPath) |
| 43 | |
| 44 | for _, line := range lines { |
| 45 | line = strings.TrimSpace(line) |
| 46 | if line == "" || strings.HasPrefix(line, "PID") { |
| 47 | continue |
| 48 | } |
| 49 | |
| 50 | fields := strings.Fields(line) |
| 51 | if len(fields) < 4 { |
| 52 | continue |
| 53 | } |
| 54 | |
| 55 | pidStr := fields[0] |
| 56 | ppidStr := fields[1] |
| 57 | state := fields[2] |
| 58 | |
| 59 | cmdPath := fields[3] |
| 60 | |
| 61 | pid, err := strconv.Atoi(pidStr) |
| 62 | if err != nil { |
| 63 | continue |
| 64 | } |
| 65 | |
| 66 | ppid, err := strconv.Atoi(ppidStr) |
| 67 | if err != nil { |
| 68 | continue |
| 69 | } |
| 70 | |
| 71 | // Verify it's actually the same executable by checking full path when possible |
| 72 | match := false |
| 73 | if procPath, err := getProcessPath(pid); err == nil { |
| 74 | match = pathsMatch(procPath, absPath) |
| 75 | } |
| 76 |