{"record":{"id":"998173654b98079c","repo":"gastownhall/beads","slug":"read-cmdline-for-pid-d-w","errorCode":null,"errorMessage":"read cmdline for pid %d: %w","messagePattern":"read cmdline for pid (.+?): %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/dbproxy/proxy/unverified_process_linux.go","lineNumber":56,"sourceCode":"\t\treturn &unverifiedProcess{pid: pid, pidfd: -1}, false, nil\n\t}\n\treturn nil, false, fmt.Errorf(\"pidfd open %d: %w\", pid, err)\n}\n\nfunc (p *unverifiedProcess) executableBasename() (basename string, gone bool, err error) {\n\treturn processExecutableBasename(p.pid)\n}\n\n// commandLineContains reports whether the process command line contains\n// needle. The managed proxy child is spawned as \"db-proxy-child --root\n// <rootDir>\", so a workspace's own processes always match their root path.\nfunc (p *unverifiedProcess) commandLineContains(needle string) (matched bool, gone bool, err error) {\n\tdata, err := os.ReadFile(\"/proc/\" + strconv.Itoa(p.pid) + \"/cmdline\")\n\tif err != nil {\n\t\tif errors.Is(err, fs.ErrNotExist) || errors.Is(err, unix.ESRCH) {\n\t\t\treturn false, true, nil\n\t\t}\n\t\treturn false, false, fmt.Errorf(\"read cmdline for pid %d: %w\", p.pid, err)\n\t}\n\tif len(data) == 0 {\n\t\t// Zombies expose an empty cmdline; the process has effectively exited.\n\t\treturn false, true, nil\n\t}\n\tcmdline := strings.ReplaceAll(strings.TrimRight(string(data), \"\\x00\"), \"\\x00\", \" \")\n\treturn strings.Contains(cmdline, needle), false, nil\n}\n\n// kill sends SIGKILL through the held handle. gone reports a target that had\n// already exited.\nfunc (p *unverifiedProcess) kill() (gone bool, err error) {\n\tif p.pidfd >= 0 {\n\t\tif err := unix.PidfdSendSignal(p.pidfd, unix.SIGKILL, nil, 0); err != nil {\n\t\t\tif errors.Is(err, unix.ESRCH) {\n\t\t\t\treturn true, nil\n\t\t\t}\n\t\t\treturn false, fmt.Errorf(\"pidfd signal %d: %w\", p.pid, err)","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dbproxy/proxy/unverified_process_linux.go#L38-L74","documentation":"unverifiedProcess.commandLineContains on Linux reads /proc/<pid>/cmdline to check what the PID is running. Missing cmdline (ENoENT/ESRCH) or empty data (zombie) is treated as 'gone'; any other read error is wrapped here. This gate exists so the proxy never kills a PID it could not identify.","triggerScenarios":"os.ReadFile(\"/proc/<pid>/cmdline\") fails with an error other than ENOENT/ESRCH — e.g. EACCES on the /proc entry, or the /proc filesystem is not mounted (containers/chroots).","commonSituations":"Running inside a container without /proc mounted; hidepid mount option on /proc making other users' process entries inaccessible; hardened LSM (SELinux/AppArmor) denying reads; PID namespaces mismatch between host and container.","solutions":["Ensure /proc is mounted inside the container/chroot (mount -t proc proc /proc).","Fix /proc permissions (avoid hidepid=2, or run the tool as the process owner/root).","Adjust SELinux/AppArmor policy to allow reading /proc/<pid>/cmdline.","Kill the PID manually after verifying identity another way, then remove the stale pidfile."],"exampleFix":"// before: /proc missing in chroot\nmount -t proc proc /proc\n// after: cmdline readable\nls /proc/<pid>/cmdline && bd ...","handlingStrategy":"try-catch","validationCode":"if _, err := os.Stat(\"/proc/self/cmdline\"); err != nil {\n    // /proc unavailable: force-stop via /proc cannot work; use manual cleanup\n}","typeGuard":null,"tryCatchPattern":"matched, gone, err := proc.commandLineContains(needle)\nif err != nil {\n    var pathErr *fs.PathError\n    if errors.As(err, &pathErr) && errors.Is(pathErr.Err, fs.ErrPermission) {\n        // hidepid/LSM issue: rerun as process owner or fix mount options\n    }\n    return err\n}","preventionTips":["Mount /proc in containers/chroots where bd runs.","Avoid hidepid mount options on /proc for multi-user bd usage.","Configure SELinux/AppArmor to allow reading /proc/<pid>/cmdline for the bd user.","Do not run bd inside PID namespaces that hide the daemon's PID."],"tags":["go","linux","procfs","process-inspection","permissions"],"backgroundTag":"proc-cmdline-read-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}