From dcf96a4d9c07d5c49d050ebd055c9e2db6836fc1 Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Sat, 5 Mar 2011 15:32:03 -0800 Subject: don't rely on /proc for 'browse' mode 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 | 54 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/src/browse.cc b/src/browse.cc index 4507e0d..92b290d 100644 --- a/src/browse.cc +++ b/src/browse.cc @@ -15,6 +15,7 @@ #include "browse.h" #include +#include #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); + } } -- cgit v0.12