Factor out various ways to execute subprocesses into separate functions.
This is so that they can be implemented properly for Windows. For now, though, they are left in blaze_util.cc since the Windows implementations aren't there yet.
--
MOS_MIGRATED_REVID=120709884
diff --git a/src/main/cpp/blaze.cc b/src/main/cpp/blaze.cc
index 65f7b65..3b7a044 100644
--- a/src/main/cpp/blaze.cc
+++ b/src/main/cpp/blaze.cc
@@ -492,38 +492,6 @@
return result;
}
-// Causes the current process to become a daemon (i.e. a child of
-// init, detached from the terminal, in its own session.) We don't
-// change cwd, though.
-static void Daemonize() {
- // Don't call die() or exit() in this function; we're already in a
- // child process so it won't work as expected. Just don't do
- // anything that can possibly fail. :)
-
- signal(SIGHUP, SIG_IGN);
- if (fork() > 0) {
- // This second fork is required iff there's any chance cmd will
- // open an specific tty explicitly, e.g., open("/dev/tty23"). If
- // not, this fork can be removed.
- _exit(blaze_exit_code::SUCCESS);
- }
-
- setsid();
-
- close(STDIN_FILENO);
- close(STDOUT_FILENO);
- close(STDERR_FILENO);
-
- open("/dev/null", O_RDONLY); // stdin
- // stdout:
- if (open(globals->jvm_log_file.c_str(),
- O_WRONLY | O_CREAT | O_TRUNC, 0666) == -1) {
- // In a daemon, no-one can hear you scream.
- open("/dev/null", O_WRONLY);
- }
- dup(STDOUT_FILENO); // stderr (2>&1)
-}
-
// Do a chdir into the workspace, and die if it fails.
static void GoToWorkspace() {
if (BlazeStartupOptions::InWorkspace(globals->workspace) &&
@@ -589,33 +557,8 @@
// we can still print errors to the terminal.
GoToWorkspace();
- int fds[2];
- if (pipe(fds)) {
- pdie(blaze_exit_code::INTERNAL_ERROR, "pipe creation failed");
- }
- int child = fork();
- if (child == -1) {
- pdie(blaze_exit_code::INTERNAL_ERROR, "fork() failed");
- } else if (child > 0) { // we're the parent
- close(fds[1]); // parent keeps only the reading side
- return fds[0];
- } else {
- close(fds[0]); // child keeps only the writing side
- }
-
- Daemonize();
-
- // TODO(lberki): This writes the wrong PID on Windows because ExecuteProgram()
- // invokes CreateProcess() there.
- if (!WriteFile(ToString(getpid()), server_dir + "/server.pid")) {
- // The exit code does not matter because we are already in the daemonized
- // server. The output of this operation will end up in jvm.out .
- pdie(0, "Cannot write PID file");
- }
-
- ExecuteProgram(exe, jvm_args_vector);
- pdie(blaze_exit_code::INTERNAL_ERROR, "execv of '%s' failed", exe.c_str());
- return -1;
+ return ExecuteDaemon(exe, jvm_args_vector, globals->jvm_log_file.c_str(),
+ server_dir + "/server.pid");
}
static bool KillRunningServerIfAny(BlazeServer *server);
diff --git a/src/main/cpp/blaze_util.cc b/src/main/cpp/blaze_util.cc
index 5740cb4..5421fb7 100644
--- a/src/main/cpp/blaze_util.cc
+++ b/src/main/cpp/blaze_util.cc
@@ -18,6 +18,7 @@
#include <fcntl.h>
#include <limits.h>
#include <pwd.h>
+#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@@ -283,22 +284,20 @@
// Read the Jvm version from a file descriptor. The read fd
// should contains a similar output as the java -version output.
-string ReadJvmVersion(int fd) {
- string version_string;
- if (ReadFileDescriptor(fd, &version_string)) {
- // try to look out for 'version "'
- static const string version_pattern = "version \"";
- size_t found = version_string.find(version_pattern);
- if (found != string::npos) {
- found += version_pattern.size();
- // If we found "version \"", process until next '"'
- size_t end = version_string.find("\"", found);
- if (end == string::npos) { // consider end of string as a '"'
- end = version_string.size();
- }
- return version_string.substr(found, end - found);
+string ReadJvmVersion(const string& version_string) {
+ // try to look out for 'version "'
+ static const string version_pattern = "version \"";
+ size_t found = version_string.find(version_pattern);
+ if (found != string::npos) {
+ found += version_pattern.size();
+ // If we found "version \"", process until next '"'
+ size_t end = version_string.find("\"", found);
+ if (end == string::npos) { // consider end of string as a '"'
+ end = version_string.size();
}
+ return version_string.substr(found, end - found);
}
+
return "";
}
@@ -307,28 +306,8 @@
args.push_back("java");
args.push_back("-version");
- int fds[2];
- if (pipe(fds)) {
- pdie(blaze_exit_code::INTERNAL_ERROR, "pipe creation failed");
- }
-
- int child = fork();
- if (child == -1) {
- pdie(blaze_exit_code::INTERNAL_ERROR, "fork() failed");
- } else if (child > 0) { // we're the parent
- close(fds[1]); // parent keeps only the reading side
- return ReadJvmVersion(fds[0]);
- } else {
- close(fds[0]); // child keeps only the writing side
- // Redirect output to the writing side of the dup.
- dup2(fds[1], STDOUT_FILENO);
- dup2(fds[1], STDERR_FILENO);
- // Execute java -version
- ExecuteProgram(java_exe, args);
- pdie(blaze_exit_code::INTERNAL_ERROR, "Failed to run java -version");
- }
- // The if never falls through here.
- return NULL;
+ string version_string = RunProgram(java_exe, args);
+ return ReadJvmVersion(version_string);
}
bool CheckJavaVersionIsAtLeast(const string &jvm_version,
@@ -357,4 +336,90 @@
return true;
}
+// Causes the current process to become a daemon (i.e. a child of
+// init, detached from the terminal, in its own session.) We don't
+// change cwd, though.
+static void Daemonize(const string& daemon_output) {
+ // Don't call die() or exit() in this function; we're already in a
+ // child process so it won't work as expected. Just don't do
+ // anything that can possibly fail. :)
+
+ signal(SIGHUP, SIG_IGN);
+ if (fork() > 0) {
+ // This second fork is required iff there's any chance cmd will
+ // open an specific tty explicitly, e.g., open("/dev/tty23"). If
+ // not, this fork can be removed.
+ _exit(blaze_exit_code::SUCCESS);
+ }
+
+ setsid();
+
+ close(0);
+ close(1);
+ close(2);
+
+ open("/dev/null", O_RDONLY); // stdin
+ // stdout:
+ if (open(daemon_output.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666) == -1) {
+ // In a daemon, no-one can hear you scream.
+ open("/dev/null", O_WRONLY);
+ }
+ dup(STDOUT_FILENO); // stderr (2>&1)
+}
+
+int ExecuteDaemon(const string& exe, const std::vector<string>& args_vector,
+ const string& daemon_output, const string& pid_file) {
+ int fds[2];
+ if (pipe(fds)) {
+ pdie(blaze_exit_code::INTERNAL_ERROR, "pipe creation failed");
+ }
+ int child = fork();
+ if (child == -1) {
+ pdie(blaze_exit_code::INTERNAL_ERROR, "fork() failed");
+ } else if (child > 0) { // we're the parent
+ close(fds[1]); // parent keeps only the reading side
+ return fds[0];
+ } else {
+ close(fds[0]); // child keeps only the writing side
+ }
+
+ Daemonize(daemon_output);
+ if (!WriteFile(ToString(getpid()), pid_file)) {
+ // The exit code does not matter because we are already in the daemonized
+ // server. The output of this operation will end up in jvm.out .
+ pdie(0, "Cannot write PID file");
+ }
+
+ ExecuteProgram(exe, args_vector);
+ pdie(0, "Cannot execute %s", exe.c_str());
+}
+
+string RunProgram(const string& exe, const std::vector<string>& args_vector) {
+ int fds[2];
+ if (pipe(fds)) {
+ pdie(blaze_exit_code::INTERNAL_ERROR, "pipe creation failed");
+ }
+
+ int child = fork();
+ if (child == -1) {
+ pdie(blaze_exit_code::INTERNAL_ERROR, "fork() failed");
+ } else if (child > 0) { // we're the parent
+ close(fds[1]); // parent keeps only the reading side
+ string result;
+ if (!ReadFileDescriptor(fds[0], &result)) {
+ pdie(blaze_exit_code::INTERNAL_ERROR, "Cannot read subprocess output");
+ }
+
+ return result;
+ } else { // We're the child
+ close(fds[0]); // child keeps only the writing side
+ // Redirect output to the writing side of the dup.
+ dup2(fds[1], STDOUT_FILENO);
+ dup2(fds[1], STDERR_FILENO);
+ // Execute the binary
+ ExecuteProgram(exe, args_vector);
+ pdie(blaze_exit_code::INTERNAL_ERROR, "Failed to run %s", exe.c_str());
+ }
+}
+
} // namespace blaze
diff --git a/src/main/cpp/blaze_util.h b/src/main/cpp/blaze_util.h
index e0d1b2b..86a8256 100644
--- a/src/main/cpp/blaze_util.h
+++ b/src/main/cpp/blaze_util.h
@@ -82,12 +82,11 @@
// Enable messages mostly of interest to developers.
bool VerboseLogging();
-// Read the JVM version from a file descriptor. The fd should point
-// to the output of a "java -version" execution and is supposed to contains
-// a string of the form 'version "version-number"' in the first 255 bytes.
-// If the string is found, version-number is returned, else the empty string
-// is returned.
-string ReadJvmVersion(int fd);
+// Read the JVM version from a string. The string should contain the output of a
+// "java -version" execution and is supposed to contain a string of the form
+// 'version "version-number"' in the first 255 bytes. If the string is found,
+// version-number is returned, else the empty string is returned.
+string ReadJvmVersion(const string& version_string);
// Get the version string from the given java executable. The java executable
// is supposed to output a string in the form '.*version ".*".*'. This method
diff --git a/src/main/cpp/blaze_util_platform.h b/src/main/cpp/blaze_util_platform.h
index 4aa462e..42a2f5d 100644
--- a/src/main/cpp/blaze_util_platform.h
+++ b/src/main/cpp/blaze_util_platform.h
@@ -59,6 +59,17 @@
// This function does not return on success.
void ExecuteProgram(const string& exe, const std::vector<string>& args_vector);
+// Starts a daemon process with its standard output and standard error
+// redirected to the file "daemon_output". Returns a file descriptor of a named
+// pipe whose other end is held by the daemon and which is closed if the daemon
+// exits.
+int ExecuteDaemon(const string& exe, const std::vector<string>& args_vector,
+ const string& daemon_output, const string& pid_file);
+
+// Executes a subprocess and returns its standard output and standard error.
+// If this fails, exits with the appropriate error code.
+string RunProgram(const string& exe, const std::vector<string>& args_vector);
+
// Convert a path from Bazel internal form to underlying OS form.
// On Unixes this is an identity operation.
// On Windows, Bazel internal from is cygwin path, and underlying OS form