fix(browser_list): parse cmdline colapsado por espacios de Chromium
/proc/<pid>/cmdline normalmente separa argv por NUL, pero Chromium reescribe su titulo de proceso in-place colapsando la region de argv a una sola cadena separada por espacios. readProcCmdline asumia solo NUL, asi que para los masters de Chromium devolvia un unico argv[0] gigante: isChromiumExe y el prefijo --user-data-dir= fallaban y browser_list devolvia [] aunque hubiera navegadores vivos. Extrae parseCmdline (pura, testeable) con fallback a split por espacios cuando no hay NUL. Test cubre ambos formatos + regresion de deteccion de master. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+33
-6
@@ -57,14 +57,31 @@ type chromiumMaster struct {
|
||||
HasCDP bool `json:"has_cdp"`
|
||||
}
|
||||
|
||||
// readProcCmdline reads /proc/<pid>/cmdline and splits it on NUL into argv.
|
||||
// Returns nil if the process is gone or unreadable.
|
||||
func readProcCmdline(pid int) []string {
|
||||
b, err := os.ReadFile(filepath.Join("/proc", strconv.Itoa(pid), "cmdline"))
|
||||
if err != nil || len(b) == 0 {
|
||||
// parseCmdline turns the raw bytes of /proc/<pid>/cmdline into argv.
|
||||
//
|
||||
// Canonically the kernel separates arguments with NUL bytes. But Chromium (and
|
||||
// other programs that rewrite their process title in place) collapse the argv
|
||||
// region into a single space-separated string, losing the NUL separators. In
|
||||
// that case splitting on NUL yields a single giant element holding the whole
|
||||
// command line, which breaks argv[0] detection and "--flag=" prefix matching.
|
||||
//
|
||||
// So: if the data still carries NUL separators we split on NUL (the correct,
|
||||
// space-safe path). Otherwise we fall back to splitting on whitespace. The
|
||||
// fallback is best-effort and would mis-split a flag value containing spaces
|
||||
// (e.g. a user-data-dir path with a space), but Chromium's own flags don't, so
|
||||
// it recovers the master-detection flags (--user-data-dir, --type=,
|
||||
// --remote-debugging-port, --profile-directory) reliably in practice.
|
||||
func parseCmdline(b []byte) []string {
|
||||
s := strings.TrimRight(string(b), "\x00")
|
||||
if s == "" {
|
||||
return nil
|
||||
}
|
||||
raw := strings.Split(string(b), "\x00")
|
||||
var raw []string
|
||||
if strings.Contains(s, "\x00") {
|
||||
raw = strings.Split(s, "\x00")
|
||||
} else {
|
||||
raw = strings.Fields(s)
|
||||
}
|
||||
args := make([]string, 0, len(raw))
|
||||
for _, a := range raw {
|
||||
if a != "" {
|
||||
@@ -74,6 +91,16 @@ func readProcCmdline(pid int) []string {
|
||||
return args
|
||||
}
|
||||
|
||||
// readProcCmdline reads /proc/<pid>/cmdline and parses it into argv.
|
||||
// Returns nil if the process is gone or unreadable.
|
||||
func readProcCmdline(pid int) []string {
|
||||
b, err := os.ReadFile(filepath.Join("/proc", strconv.Itoa(pid), "cmdline"))
|
||||
if err != nil || len(b) == 0 {
|
||||
return nil
|
||||
}
|
||||
return parseCmdline(b)
|
||||
}
|
||||
|
||||
// flagValue returns the value of a "--name=value" flag from argv, plus whether it
|
||||
// was present. Matches the exact "--name=" prefix; the first occurrence wins.
|
||||
func flagValue(args []string, name string) (string, bool) {
|
||||
|
||||
Reference in New Issue
Block a user