don't rely on /proc for 'browse' mode
authorEvan Martin <martine@danga.com>
Sat, 5 Mar 2011 23:32:03 +0000 (15:32 -0800)
committerEvan Martin <martine@danga.com>
Sat, 5 Mar 2011 23:32:03 +0000 (15:32 -0800)
Instead, pass the script code via stdin.
We end up with a zombie (the child process that passes the script code)
because we can't ignore SIGCHLD in the parent (the Python code relies on
being able to wait for children), but the zombie dies with the outer
process.  This probably could be avoided by double-forking or other
approaches, but it doesn't seem worth the effort.

src/browse.cc

index 4507e0d05378d7dafca9580819ea0692fa645feb..92b290d91ad13622423f06516e94c035a6fa42b7 100644 (file)
@@ -15,6 +15,7 @@
 #include "browse.h"
 
 #include <stdio.h>
+#include <unistd.h>
 
 #include "ninja.h"
 
@@ -30,24 +31,45 @@ extern const char browse_data_begin[];
 extern const char browse_data_end[];
 
 void RunBrowsePython(State* state, const char* ninja_command) {
-  // Create a temporary file, dump the Python code into it, and
-  // delete the file, keeping our open handle to it.
-  char tmpl[] = "browsepy-XXXXXX";
-  int fd = mkstemp(tmpl);
-  unlink(tmpl);
-  const int browse_data_len = browse_data_end - browse_data_begin;
-  int len = write(fd, browse_data_begin, browse_data_len);
-  if (len < browse_data_len) {
-    perror("write");
+  // Fork off a Python process and have it run our code via its stdin.
+  // (Actually the Python process becomes the parent.)
+  int pipefd[2];
+  if (pipe(pipefd) < 0) {
+    perror("pipe");
     return;
   }
 
-  // exec Python, telling it to use our script file.
-  const char* command[] = {
-    "python", "/proc/self/fd/3", ninja_command, NULL
-  };
-  execvp(command[0], (char**)command);
+  pid_t pid = fork();
+  if (pid < 0) {
+    perror("fork");
+    return;
+  }
+
+  if (pid > 0) {  // Parent.
+    close(pipefd[1]);
+    do {
+      if (dup2(pipefd[0], 0) < 0) {
+        perror("dup2");
+        break;
+      }
 
-  // If we get here, the exec failed.
-  printf("ERROR: Failed to spawn python for graph browsing, aborting.\n");
+      // exec Python, telling it to run the program from stdin.
+      const char* command[] = {
+        "python", "-", ninja_command, NULL
+      };
+      execvp(command[0], (char**)command);
+      perror("execvp");
+    } while (false);
+    _exit(1);
+  } else {  // Child.
+    close(pipefd[0]);
+
+    // Write the script file into the stdin of the Python process.
+    const int browse_data_len = browse_data_end - browse_data_begin;
+    int len = write(pipefd[1], browse_data_begin, browse_data_len);
+    if (len < browse_data_len)
+      perror("write");
+    close(pipefd[1]);
+    exit(0);
+  }
 }