Add command line prefix flags.

This adds --peer-cmd-prefix=PREFIX and --xwayland-cmd-prefix=PREFIX
flags that can be used to better control how child processes are
spawned.

Useful values might be "valgrind" or "xterm -e gdb --args".

Bug: 826923
Change-Id: If9852a4e58f55f0b5185cb73d254591242243352
Reviewed-on: https://chromium-review.googlesource.com/997976
Tested-by: David Reveman <reveman@chromium.org>
Reviewed-by: David Reveman <reveman@chromium.org>
diff --git a/sommelier.c b/sommelier.c
index fd365a2..9e76683 100644
--- a/sommelier.c
+++ b/sommelier.c
@@ -6452,6 +6452,46 @@
   return 1;
 }
 
+// Break |str| into a sequence of zero or more nonempty arguments. No more
+// than |argc| arguments will be added to |argv|. Returns the total number of
+// argments found in |str|.
+static int xwl_parse_cmd_prefix(char *str, int argc, char **argv) {
+  char *s = str;
+  int n = 0;
+  int delim = 0;
+
+  do {
+    // Look for ending delimiter if |delim| is set.
+    if (delim) {
+      if (*s == delim) {
+        delim = 0;
+        *s = '\0';
+      }
+      ++s;
+    } else {
+      // Skip forward to first non-space character.
+      while (*s == ' ' && *s != '\0')
+        ++s;
+
+      // Check for quote delimiter.
+      if (*s == '"') {
+        delim = '"';
+        ++s;
+      } else {
+        delim = ' ';
+      }
+
+      // Add string to arguments if there's room.
+      if (n < argc)
+        argv[n] = s;
+
+      ++n;
+    }
+  } while (*s != '\0');
+
+  return n;
+}
+
 static void xwl_print_usage() {
   printf("usage: sommelier [options] [program] [args...]\n\n"
          "options:\n"
@@ -6463,10 +6503,12 @@
          "  --shm-driver=DRIVER\t\tSHM driver to use (noop, dmabuf, virtwl)\n"
          "  --data-driver=DRIVER\t\tData driver to use (noop, virtwl)\n"
          "  --scale=SCALE\t\t\tScale factor for contents\n"
+         "  --peer-cmd-prefix=PREFIX\tPeer process command line prefix\n"
          "  --accelerators=ACCELERATORS\tList of keyboard accelerators\n"
          "  --app-id=ID\t\t\tForced application ID for X11 clients\n"
          "  --x-display=DISPLAY\t\tX11 display to listen on\n"
          "  --xwayland-path=PATH\t\tPath to Xwayland executable\n"
+         "  --xwayland-cmd-prefix=PREFIX\tXwayland command line prefix\n"
          "  --no-exit-with-child\t\tKeep process alive after child exists\n"
          "  --no-clipboard-manager\tDisable X11 clipboard manager\n"
          "  --frame-color=COLOR\t\tWindow frame color for X11 clients\n"
@@ -6582,6 +6624,8 @@
   const char *glamor = getenv("SOMMELIER_GLAMOR");
   const char *shm_driver = getenv("SOMMELIER_SHM_DRIVER");
   const char *data_driver = getenv("SOMMELIER_DATA_DRIVER");
+  const char *peer_cmd_prefix = getenv("SOMMELIER_PEER_CMD_PREFIX");
+  const char *xwayland_cmd_prefix = getenv("SOMMELIER_XWAYLAND_CMD_PREFIX");
   const char *accelerators = getenv("SOMMELIER_ACCELERATORS");
   const char *xwayland_path = getenv("SOMMELIER_XWAYLAND_PATH");
   const char *socket_name = "wayland-0";
@@ -6631,6 +6675,14 @@
       const char *s = strchr(arg, '=');
       ++s;
       xwl.peer_pid = atoi(s);
+    } else if (strstr(arg, "--peer-cmd-prefix") == arg) {
+      const char *s = strchr(arg, '=');
+      ++s;
+      peer_cmd_prefix = s;
+    } else if (strstr(arg, "--xwayland-cmd-prefix") == arg) {
+      const char *s = strchr(arg, '=');
+      ++s;
+      xwayland_cmd_prefix = s;
     } else if (strstr(arg, "--client-fd") == arg) {
       const char *s = strchr(arg, '=');
       ++s;
@@ -6768,12 +6820,24 @@
       assert(pid != -1);
       if (pid == 0) {
         char client_fd_str[64], peer_pid_str[64];
-        char *args[32];
+        char peer_cmd_prefix_str[1024];
+        char *args[64];
         int i = 0, j;
 
         close(sock_fd);
         close(lock_fd);
 
+        if (peer_cmd_prefix) {
+          snprintf(peer_cmd_prefix_str, sizeof(peer_cmd_prefix_str), "%s",
+                   peer_cmd_prefix);
+
+          i = xwl_parse_cmd_prefix(peer_cmd_prefix_str, 32, args);
+          if (i > 32) {
+            fprintf(stderr, "error: too many arguments in cmd prefix: %d\n", i);
+            i = 0;
+          }
+        }
+
         args[i++] = argv[0];
         snprintf(peer_pid_str, sizeof(peer_pid_str), "--peer-pid=%d",
                  ucred.pid);
@@ -6798,7 +6862,7 @@
 
         args[i++] = NULL;
 
-        execvp(argv[0], args);
+        execvp(args[0], args);
         _exit(EXIT_FAILURE);
       }
       close(client_fd);
@@ -7057,10 +7121,22 @@
       if (pid == 0) {
         char display_str[8], display_fd_str[8], wm_fd_str[8];
         char xwayland_path_str[1024];
-        char *args[32];
+        char xwayland_cmd_prefix_str[1024];
+        char *args[64];
         int i = 0;
         int fd;
 
+        if (xwayland_cmd_prefix) {
+          snprintf(xwayland_cmd_prefix_str, sizeof(xwayland_cmd_prefix_str),
+                   "%s", xwayland_cmd_prefix);
+
+          i = xwl_parse_cmd_prefix(xwayland_cmd_prefix_str, 32, args);
+          if (i > 32) {
+            fprintf(stderr, "error: too many arguments in cmd prefix: %d\n", i);
+            i = 0;
+          }
+        }
+
         snprintf(xwayland_path_str, sizeof(xwayland_path_str), "%s",
                  xwayland_path ? xwayland_path : XWAYLAND_PATH);
         args[i++] = xwayland_path_str;