aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/tools
diff options
context:
space:
mode:
authorGravatar philwo <philwo@google.com>2017-06-06 09:20:37 -0400
committerGravatar John Cater <jcater@google.com>2017-06-06 09:51:20 -0400
commit75b58db8bc94f591f64d83ff532128e1c672818a (patch)
treeae0077b886fcda96975e8eae6e07f4fbf55fefdc /src/main/tools
parent04c85337502668a089d3a75d8bba8f33e972a467 (diff)
Automated g4 rollback of commit 5608765ab737ebb8a98a04a6068143d53ae7065c.
*** Reason for rollback *** Rollforward with fix for FreeBSD. I added the missing headers and verified that it now builds fine on FreeBSD 11, too. *** Original change description *** Automated g4 rollback of commit 7f520a8286c39c5145b6d816cd0be5a6b7b18250. *** Reason for rollback *** This broke Bazel CI on freebsd: http://ci.bazel.io/view/Dashboard/job/Bazel/JAVA_VERSION=1.8,PLATFORM_NAME=freebsd-11/1516/console# *** Original change description *** Refactor process-wrapper code so the spawn/wait code is pluggable. In an upcoming change I'll reintroduce the new platform-specific implementations that can kill and wait for all descendant processes spawned by the wrapped process. This has no functional changes. PiperOrigin-RevId: 158133159
Diffstat (limited to 'src/main/tools')
-rw-r--r--src/main/tools/BUILD3
-rw-r--r--src/main/tools/process-wrapper-legacy.cc102
-rw-r--r--src/main/tools/process-wrapper-legacy.h49
-rw-r--r--src/main/tools/process-wrapper.cc94
-rw-r--r--src/main/tools/process-wrapper.h32
5 files changed, 191 insertions, 89 deletions
diff --git a/src/main/tools/BUILD b/src/main/tools/BUILD
index 260c0edce6..4aa0408d12 100644
--- a/src/main/tools/BUILD
+++ b/src/main/tools/BUILD
@@ -20,6 +20,9 @@ cc_binary(
"//src:windows_msvc": ["process-wrapper-windows.cc"],
"//conditions:default": [
"process-wrapper.cc",
+ "process-wrapper.h",
+ "process-wrapper-legacy.cc",
+ "process-wrapper-legacy.h",
],
}),
linkopts = ["-lm"],
diff --git a/src/main/tools/process-wrapper-legacy.cc b/src/main/tools/process-wrapper-legacy.cc
new file mode 100644
index 0000000000..2c6e6d224a
--- /dev/null
+++ b/src/main/tools/process-wrapper-legacy.cc
@@ -0,0 +1,102 @@
+// Copyright 2017 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/main/tools/process-wrapper-legacy.h"
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <vector>
+
+#include "src/main/tools/logging.h"
+#include "src/main/tools/process-tools.h"
+#include "src/main/tools/process-wrapper.h"
+
+pid_t LegacyProcessWrapper::child_pid = 0;
+volatile sig_atomic_t LegacyProcessWrapper::last_signal = 0;
+
+void LegacyProcessWrapper::RunCommand() {
+ SpawnChild();
+ WaitForChild();
+}
+
+void LegacyProcessWrapper::SpawnChild() {
+ child_pid = fork();
+ if (child_pid < 0) {
+ DIE("fork");
+ } else if (child_pid == 0) {
+ // In child.
+ if (setsid() < 0) {
+ DIE("setsid");
+ }
+ ClearSignalMask();
+
+ // Force umask to include read and execute for everyone, to make output
+ // permissions predictable.
+ umask(022);
+
+ // Does not return unless something went wrong.
+ if (execvp(opt.args[0], opt.args.data()) < 0) {
+ DIE("execvp(%s, ...)", opt.args[0]);
+ }
+ }
+}
+
+void LegacyProcessWrapper::WaitForChild() {
+ // Set up a signal handler which kills all subprocesses when the given signal
+ // is triggered.
+ InstallSignalHandler(SIGALRM, OnSignal);
+ InstallSignalHandler(SIGTERM, OnSignal);
+ InstallSignalHandler(SIGINT, OnSignal);
+ if (opt.timeout_secs > 0) {
+ SetTimeout(opt.timeout_secs);
+ }
+
+ int status = WaitChild(child_pid);
+
+ // The child is done for, but may have grandchildren that we still have to
+ // kill.
+ kill(-child_pid, SIGKILL);
+
+ if (last_signal > 0) {
+ // Don't trust the exit code if we got a timeout or signal.
+ InstallDefaultSignalHandler(last_signal);
+ raise(last_signal);
+ } else if (WIFEXITED(status)) {
+ exit(WEXITSTATUS(status));
+ } else {
+ int sig = WTERMSIG(status);
+ InstallDefaultSignalHandler(sig);
+ raise(sig);
+ }
+}
+
+// Called when timeout or signal occurs.
+void LegacyProcessWrapper::OnSignal(int sig) {
+ last_signal = sig;
+
+ if (sig == SIGALRM) {
+ // SIGALRM represents a timeout, so we should give the process a bit of time
+ // to die gracefully if it needs it.
+ KillEverything(child_pid, true, opt.kill_delay_secs);
+ } else {
+ // Signals should kill the process quickly, as it's typically blocking the
+ // return of the prompt after a user hits "Ctrl-C".
+ KillEverything(child_pid, false, opt.kill_delay_secs);
+ }
+}
diff --git a/src/main/tools/process-wrapper-legacy.h b/src/main/tools/process-wrapper-legacy.h
new file mode 100644
index 0000000000..5cea547175
--- /dev/null
+++ b/src/main/tools/process-wrapper-legacy.h
@@ -0,0 +1,49 @@
+// Copyright 2017 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_MAIN_TOOLS_PROCESS_WRAPPER_LEGACY_H_
+#define SRC_MAIN_TOOLS_PROCESS_WRAPPER_LEGACY_H_
+
+#include <signal.h>
+#include <vector>
+
+// The process-wrapper implementation that was used until and including Bazel
+// 0.4.5. Waits for the wrapped process to exit and then kills its process
+// group. Works on all POSIX operating systems (tested on Linux, macOS,
+// FreeBSD).
+//
+// Caveats:
+// - Killing just the process group of the spawned child means that daemons or
+// other processes spawned by the child may not be killed if they change their
+// process group.
+// - Does not wait for grandchildren to exit, thus processes spawned by the
+// child that could not be killed will linger around in the background.
+// - Has a PID reuse race condition, because the kill() to the process group is
+// sent after waitpid() was called on the main child.
+class LegacyProcessWrapper {
+ public:
+ // Run the command specified in the `opt.args` array and kill it after
+ // `opt.timeout_secs` seconds.
+ static void RunCommand();
+
+ private:
+ static void SpawnChild();
+ static void WaitForChild();
+ static void OnSignal(int sig);
+
+ static pid_t child_pid;
+ static volatile sig_atomic_t last_signal;
+};
+
+#endif
diff --git a/src/main/tools/process-wrapper.cc b/src/main/tools/process-wrapper.cc
index c60de6e4e3..09c1a88038 100644
--- a/src/main/tools/process-wrapper.cc
+++ b/src/main/tools/process-wrapper.cc
@@ -22,6 +22,8 @@
// die with raise(SIGTERM) even if the child process handles SIGTERM with
// exit(0).
+#include "src/main/tools/process-wrapper.h"
+
#include <err.h>
#include <errno.h>
#include <signal.h>
@@ -38,21 +40,9 @@
#include "src/main/tools/logging.h"
#include "src/main/tools/process-tools.h"
+#include "src/main/tools/process-wrapper-legacy.h"
-static double global_kill_delay;
-static pid_t global_child_pid;
-static volatile sig_atomic_t global_signal;
-
-// Options parsing result.
-struct Options {
- double timeout_secs;
- double kill_delay_secs;
- std::string stdout_path;
- std::string stderr_path;
- std::vector<char *> args;
-};
-
-static struct Options opt;
+struct Options opt;
// Print out a usage error and exit with EXIT_FAILURE.
static void Usage(char *program_name) {
@@ -86,83 +76,9 @@ static void ParseCommandLine(std::vector<char *> args) {
opt.args.push_back(nullptr);
}
-// Called when timeout or signal occurs.
-void OnSignal(int sig) {
- global_signal = sig;
-
- // Nothing to do if we received a signal before spawning the child.
- if (global_child_pid == -1) {
- return;
- }
-
- if (sig == SIGALRM) {
- // SIGALRM represents a timeout, so we should give the process a bit of
- // time to die gracefully if it needs it.
- KillEverything(global_child_pid, true, global_kill_delay);
- } else {
- // Signals should kill the process quickly, as it's typically blocking
- // the return of the prompt after a user hits "Ctrl-C".
- KillEverything(global_child_pid, false, global_kill_delay);
- }
-}
-
-// Run the command specified by the argv array and kill it after timeout
-// seconds.
-static void SpawnCommand(const std::vector<char *> &args, double timeout_secs) {
- global_child_pid = fork();
- if (global_child_pid < 0) {
- DIE("fork");
- } else if (global_child_pid == 0) {
- // In child.
- if (setsid() < 0) {
- DIE("setsid");
- }
- ClearSignalMask();
-
- // Force umask to include read and execute for everyone, to make
- // output permissions predictable.
- umask(022);
-
- // Does not return unless something went wrong.
- if (execvp(args[0], args.data()) < 0) {
- DIE("execvp(%s, ...)", args[0]);
- }
- } else {
- // In parent.
-
- // Set up a signal handler which kills all subprocesses when the given
- // signal is triggered.
- InstallSignalHandler(SIGALRM, OnSignal);
- InstallSignalHandler(SIGTERM, OnSignal);
- InstallSignalHandler(SIGINT, OnSignal);
- if (timeout_secs > 0) {
- SetTimeout(timeout_secs);
- }
-
- int status = WaitChild(global_child_pid);
-
- // The child is done for, but may have grandchildren that we still have to
- // kill.
- kill(-global_child_pid, SIGKILL);
-
- if (global_signal > 0) {
- // Don't trust the exit code if we got a timeout or signal.
- InstallDefaultSignalHandler(global_signal);
- raise(global_signal);
- } else if (WIFEXITED(status)) {
- exit(WEXITSTATUS(status));
- } else {
- int sig = WTERMSIG(status);
- InstallDefaultSignalHandler(sig);
- raise(sig);
- }
- }
-}
-
int main(int argc, char *argv[]) {
std::vector<char *> args(argv, argv + argc);
ParseCommandLine(args);
- global_kill_delay = opt.kill_delay_secs;
SwitchToEuid();
SwitchToEgid();
@@ -170,7 +86,7 @@ int main(int argc, char *argv[]) {
Redirect(opt.stdout_path, STDOUT_FILENO);
Redirect(opt.stderr_path, STDERR_FILENO);
- SpawnCommand(opt.args, opt.timeout_secs);
+ LegacyProcessWrapper::RunCommand();
return 0;
}
diff --git a/src/main/tools/process-wrapper.h b/src/main/tools/process-wrapper.h
new file mode 100644
index 0000000000..061eeace0e
--- /dev/null
+++ b/src/main/tools/process-wrapper.h
@@ -0,0 +1,32 @@
+// Copyright 2017 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_MAIN_TOOLS_PROCESS_WRAPPER_H_
+#define SRC_MAIN_TOOLS_PROCESS_WRAPPER_H_
+
+#include <string>
+#include <vector>
+
+// Options parsing result.
+struct Options {
+ double timeout_secs;
+ double kill_delay_secs;
+ std::string stdout_path;
+ std::string stderr_path;
+ std::vector<char *> args;
+};
+
+extern struct Options opt;
+
+#endif