getProcessPath gets the full path of a process by PID on Unix
(pid int)
| 149 | |
| 150 | // getProcessPath gets the full path of a process by PID on Unix |
| 151 | func getProcessPath(pid int) (string, error) { |
| 152 | // Read from /proc/PID/exe symlink |
| 153 | procPath := fmt.Sprintf("/proc/%d/exe", pid) |
| 154 | path, err := os.Readlink(procPath) |
| 155 | if err == nil { |
| 156 | return path, nil |
| 157 | } |
| 158 | |
| 159 | // Fallback for systems without /proc (e.g., macOS) |
| 160 | psOutput, psErr := exec.Command("ps", "-p", strconv.Itoa(pid), "-o", "command=").Output() |
| 161 | if psErr != nil { |
| 162 | return "", fmt.Errorf("failed to read process path: %w (ps fallback error: %v)", err, psErr) |
| 163 | } |
| 164 | |
| 165 | cmdline := strings.TrimSpace(string(psOutput)) |
| 166 | if cmdline == "" { |
| 167 | return "", fmt.Errorf("failed to read process path: empty command for pid %d", pid) |
| 168 | } |
| 169 | |
| 170 | fields := strings.Fields(cmdline) |
| 171 | if len(fields) == 0 { |
| 172 | return "", fmt.Errorf("failed to read process path: empty command for pid %d", pid) |
| 173 | } |
| 174 | |
| 175 | return fields[0], nil |
| 176 | } |
| 177 | |
| 178 | // killProcessTree kills a process and all its children on Unix |
| 179 | func killProcessTree(pid int) error { |