Merge quick/fix-cmdline-space-collapse: browser_list ve Chromium con cmdline colapsado
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) {
|
||||
|
||||
@@ -150,3 +150,48 @@ func TestMatchMaster(t *testing.T) {
|
||||
t.Errorf("unknown profile should not match")
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseCmdline cubre el parsing de /proc/<pid>/cmdline en sus dos formatos:
|
||||
// el canonico separado por NUL y el colapsado por espacios que produce Chromium
|
||||
// al reescribir su titulo de proceso in-place. El segundo caso es el que rompia
|
||||
// browser_list (los flags quedaban dentro de un unico argv[0] gigante).
|
||||
func TestParseCmdline(t *testing.T) {
|
||||
// Caso canonico: argv separado por NUL (proceso normal).
|
||||
nul := []byte("/usr/lib/chromium/chromium\x00--user-data-dir=/tmp/x\x00--remote-debugging-port=9333\x00")
|
||||
got := parseCmdline(nul)
|
||||
want := []string{"/usr/lib/chromium/chromium", "--user-data-dir=/tmp/x", "--remote-debugging-port=9333"}
|
||||
if len(got) != len(want) {
|
||||
t.Fatalf("NUL: got %v, want %v", got, want)
|
||||
}
|
||||
for i := range want {
|
||||
if got[i] != want[i] {
|
||||
t.Errorf("NUL[%d]: got %q, want %q", i, got[i], want[i])
|
||||
}
|
||||
}
|
||||
|
||||
// Caso Chromium: cmdline colapsado a una sola cadena separada por espacios.
|
||||
collapsed := []byte("/usr/lib/chromium/chromium --remote-debugging-port=9333 --user-data-dir=/tmp/browser_mcp_userdata --no-first-run https://www.alsa.es/")
|
||||
args := parseCmdline(collapsed)
|
||||
if len(args) == 1 {
|
||||
t.Fatalf("space-collapsed: parse devolvio un unico elemento gigante: %q", args[0])
|
||||
}
|
||||
if args[0] != "/usr/lib/chromium/chromium" {
|
||||
t.Errorf("space-collapsed argv[0]: got %q, want chromium binary", args[0])
|
||||
}
|
||||
|
||||
// El master debe detectarse a partir del cmdline colapsado (regresion de browser_list).
|
||||
m, ok := parseChromiumMaster(18148, args)
|
||||
if !ok {
|
||||
t.Fatalf("space-collapsed: parseChromiumMaster no detecto el master")
|
||||
}
|
||||
if m.UserDataDir != "/tmp/browser_mcp_userdata" {
|
||||
t.Errorf("space-collapsed udd: got %q, want /tmp/browser_mcp_userdata", m.UserDataDir)
|
||||
}
|
||||
if m.CDPPort != "9333" || !m.HasCDP {
|
||||
t.Errorf("space-collapsed cdp: got port=%q hasCDP=%v, want 9333/true", m.CDPPort, m.HasCDP)
|
||||
}
|
||||
|
||||
if parseCmdline([]byte("")) != nil || parseCmdline([]byte("\x00\x00")) != nil {
|
||||
t.Errorf("cmdline vacio debe devolver nil")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user