Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(69)

Side by Side Diff: chrome/app/shutdown_signal_handlers_posix.cc

Issue 2729633002: mash: Cleanly exit on SIGTERM, SIGINT, SIGHUP (Closed)
Patch Set: Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/app/shutdown_signal_handlers_posix.h"
6
7 #include <limits.h>
8 #include <signal.h>
9 #include <string.h>
10 #include <unistd.h>
11
12 #include "base/callback.h"
13 #include "base/debug/leak_annotations.h"
14 #include "base/logging.h"
15 #include "base/macros.h"
16 #include "base/posix/eintr_wrapper.h"
17 #include "base/single_thread_task_runner.h"
18 #include "base/threading/platform_thread.h"
19 #include "base/threading/thread_task_runner_handle.h"
20
21 namespace {
22
23 // The OSX fork() implementation can crash in the child process before
James Cook 2017/03/01 18:24:38 This code is a straight cut-paste from chrome_brow
24 // fork() returns. In that case, the shutdown pipe will still be
25 // shared with the parent process. To prevent child crashes from
26 // causing parent shutdowns, |g_pipe_pid| is the pid for the process
27 // which registered |g_shutdown_pipe_write_fd|.
28 // See <http://crbug.com/175341>.
29 pid_t g_pipe_pid = -1;
30 int g_shutdown_pipe_write_fd = -1;
31 int g_shutdown_pipe_read_fd = -1;
32
33 // Common code between SIG{HUP, INT, TERM}Handler.
34 void GracefulShutdownHandler(int signal) {
35 // Reinstall the default handler. We had one shot at graceful shutdown.
36 struct sigaction action;
37 memset(&action, 0, sizeof(action));
38 action.sa_handler = SIG_DFL;
39 RAW_CHECK(sigaction(signal, &action, NULL) == 0);
40
41 RAW_CHECK(g_pipe_pid != -1);
42 RAW_CHECK(g_shutdown_pipe_write_fd != -1);
43 RAW_CHECK(g_shutdown_pipe_read_fd != -1);
44 RAW_CHECK(g_pipe_pid == getpid());
45 size_t bytes_written = 0;
46 do {
47 int rv = HANDLE_EINTR(
48 write(g_shutdown_pipe_write_fd,
49 reinterpret_cast<const char*>(&signal) + bytes_written,
50 sizeof(signal) - bytes_written));
51 RAW_CHECK(rv >= 0);
52 bytes_written += rv;
53 } while (bytes_written < sizeof(signal));
54 }
55
56 void SIGHUPHandler(int signal) {
57 RAW_CHECK(signal == SIGHUP);
58 GracefulShutdownHandler(signal);
59 }
60
61 void SIGINTHandler(int signal) {
62 RAW_CHECK(signal == SIGINT);
63 GracefulShutdownHandler(signal);
64 }
65
66 void SIGTERMHandler(int signal) {
67 RAW_CHECK(signal == SIGTERM);
68 GracefulShutdownHandler(signal);
69 }
70
71 // Runs a thread that invokes a callback when a termination signal handler
72 // is invoked. Uses a pipe to wait for the signal handler to run.
73 class ShutdownDetector : public base::PlatformThread::Delegate {
74 public:
75 ShutdownDetector(
76 int shutdown_fd,
77 const base::Closure& shutdown_callback,
78 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner);
James Cook 2017/03/01 18:24:38 Added the task runner and callback.
79 ~ShutdownDetector() override;
James Cook 2017/03/01 18:24:38 Added destructor.
80
81 // base::PlatformThread::Delegate:
82 void ThreadMain() override;
83
84 private:
85 const int shutdown_fd_;
86 const base::Closure shutdown_callback_;
87 const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
88
89 DISALLOW_COPY_AND_ASSIGN(ShutdownDetector);
90 };
91
92 ShutdownDetector::ShutdownDetector(
93 int shutdown_fd,
94 const base::Closure& shutdown_callback,
95 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner)
96 : shutdown_fd_(shutdown_fd),
97 shutdown_callback_(shutdown_callback),
98 task_runner_(task_runner) {
99 CHECK_NE(shutdown_fd_, -1);
100 CHECK(!shutdown_callback.is_null());
101 CHECK(task_runner_);
102 }
103
104 ShutdownDetector::~ShutdownDetector() {}
105
106 // These functions are used to help us diagnose crash dumps that happen
107 // during the shutdown process.
108 NOINLINE void ShutdownFDReadError() {
109 // Ensure function isn't optimized away.
110 asm("");
111 sleep(UINT_MAX);
112 }
113
114 NOINLINE void ShutdownFDClosedError() {
115 // Ensure function isn't optimized away.
116 asm("");
117 sleep(UINT_MAX);
118 }
119
120 NOINLINE void ExitPosted() {
121 // Ensure function isn't optimized away.
122 asm("");
123 sleep(UINT_MAX);
124 }
125
126 void ShutdownDetector::ThreadMain() {
127 base::PlatformThread::SetName("CrShutdownDetector");
128
129 int signal;
130 size_t bytes_read = 0;
131 ssize_t ret;
132 do {
133 ret = HANDLE_EINTR(read(shutdown_fd_,
134 reinterpret_cast<char*>(&signal) + bytes_read,
135 sizeof(signal) - bytes_read));
136 if (ret < 0) {
137 NOTREACHED() << "Unexpected error: " << strerror(errno);
138 ShutdownFDReadError();
139 break;
140 } else if (ret == 0) {
141 NOTREACHED() << "Unexpected closure of shutdown pipe.";
142 ShutdownFDClosedError();
143 break;
144 }
145 bytes_read += ret;
146 } while (bytes_read < sizeof(signal));
147 VLOG(1) << "Handling shutdown for signal " << signal << ".";
148
149 if (!task_runner_->PostTask(FROM_HERE, shutdown_callback_)) {
James Cook 2017/03/01 18:24:38 Used it here.
150 // Without a valid task runner to post the exit task to, there aren't many
151 // options. Raise the signal again. The default handler will pick it up
152 // and cause an ungraceful exit.
153 RAW_LOG(WARNING, "No valid task runner, exiting ungracefully.");
154 kill(getpid(), signal);
155
156 // The signal may be handled on another thread. Give that a chance to
157 // happen.
158 sleep(3);
159
160 // We really should be dead by now. For whatever reason, we're not. Exit
161 // immediately, with the exit status set to the signal number with bit 8
162 // set. On the systems that we care about, this exit status is what is
163 // normally used to indicate an exit by this signal's default handler.
164 // This mechanism isn't a de jure standard, but even in the worst case, it
165 // should at least result in an immediate exit.
166 RAW_LOG(WARNING, "Still here, exiting really ungracefully.");
167 _exit(signal | (1 << 7));
168 }
169 ExitPosted();
170 }
171
172 } // namespace
173
174 void InstallShutdownSignalHandlers(
175 const base::Closure& shutdown_callback,
176 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) {
177 int pipefd[2];
178 int ret = pipe(pipefd);
179 if (ret < 0) {
180 PLOG(DFATAL) << "Failed to create pipe";
181 return;
James Cook 2017/03/01 18:24:38 Added early exit to reduce indenting.
182 }
183 g_pipe_pid = getpid();
184 g_shutdown_pipe_read_fd = pipefd[0];
185 g_shutdown_pipe_write_fd = pipefd[1];
186 #if !defined(ADDRESS_SANITIZER) && !defined(KEEP_SHADOW_STACKS)
187 const size_t kShutdownDetectorThreadStackSize = PTHREAD_STACK_MIN * 2;
188 #else
189 // ASan instrumentation and -finstrument-functions (used for keeping the
190 // shadow stacks) bloat the stack frames, so we need to increase the stack
191 // size to avoid hitting the guard page.
192 const size_t kShutdownDetectorThreadStackSize = PTHREAD_STACK_MIN * 4;
193 #endif
194 ShutdownDetector* detector = new ShutdownDetector(
James Cook 2017/03/01 18:24:38 Used local variable so I could add the leak annota
195 g_shutdown_pipe_read_fd, shutdown_callback, task_runner);
196 // PlatformThread does not delete its delegate.
197 ANNOTATE_LEAKING_OBJECT_PTR(detector);
198 if (!base::PlatformThread::CreateNonJoinable(kShutdownDetectorThreadStackSize,
199 detector)) {
200 LOG(DFATAL) << "Failed to create shutdown detector task.";
201 }
202
203 // Setup signal handlers for shutdown AFTER shutdown pipe is setup because
204 // it may be called right away after handler is set.
205
206 // If adding to this list of signal handlers, note the new signal probably
207 // needs to be reset in child processes. See
208 // base/process_util_posix.cc:LaunchProcess.
209
210 // We need to handle SIGTERM, because that is how many POSIX-based distros
211 // ask processes to quit gracefully at shutdown time.
212 struct sigaction action;
213 memset(&action, 0, sizeof(action));
214 action.sa_handler = SIGTERMHandler;
215 CHECK(sigaction(SIGTERM, &action, nullptr) == 0);
216
217 // Also handle SIGINT - when the user terminates the browser via Ctrl+C. If
218 // the browser process is being debugged, GDB will catch the SIGINT first.
219 action.sa_handler = SIGINTHandler;
220 CHECK(sigaction(SIGINT, &action, nullptr) == 0);
221
222 // And SIGHUP, for when the terminal disappears. On shutdown, many Linux
223 // distros send SIGHUP, SIGTERM, and then SIGKILL.
224 action.sa_handler = SIGHUPHandler;
225 CHECK(sigaction(SIGHUP, &action, nullptr) == 0);
226 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698