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

Side by Side Diff: chrome/browser/chrome_browser_main_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
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/chrome_browser_main_posix.h" 5 #include "chrome/browser/chrome_browser_main_posix.h"
6 6
7 #include <errno.h> 7 #include <errno.h>
8 #include <limits.h>
9 #include <pthread.h> 8 #include <pthread.h>
10 #include <signal.h> 9 #include <signal.h>
11 #include <stddef.h> 10 #include <stddef.h>
12 #include <string.h> 11 #include <string.h>
13 #include <sys/resource.h> 12 #include <sys/resource.h>
14 #include <unistd.h>
15 13
16 #include <string> 14 #include <string>
17 15
18 #include "base/bind.h" 16 #include "base/bind.h"
19 #include "base/command_line.h"
20 #include "base/logging.h" 17 #include "base/logging.h"
21 #include "base/macros.h" 18 #include "base/macros.h"
22 #include "base/posix/eintr_wrapper.h"
23 #include "base/strings/string_number_conversions.h"
24 #include "build/build_config.h" 19 #include "build/build_config.h"
25 #include "chrome/browser/chrome_notification_types.h" 20 #include "chrome/app/shutdown_signal_handlers_posix.h"
26 #include "chrome/browser/lifetime/application_lifetime.h" 21 #include "chrome/browser/lifetime/application_lifetime.h"
27 #include "chrome/browser/sessions/session_restore.h" 22 #include "chrome/browser/sessions/session_restore.h"
28 #include "chrome/common/chrome_switches.h"
29 #include "content/public/browser/browser_thread.h" 23 #include "content/public/browser/browser_thread.h"
30 24
31 using content::BrowserThread; 25 using content::BrowserThread;
32 26
33 namespace { 27 namespace {
34 28
35 // See comment in |PreEarlyInitialization()|, where sigaction is called. 29 // See comment in |PreEarlyInitialization()|, where sigaction is called.
36 void SIGCHLDHandler(int signal) { 30 void SIGCHLDHandler(int signal) {
37 } 31 }
38 32
39 // The OSX fork() implementation can crash in the child process before
40 // fork() returns. In that case, the shutdown pipe will still be
41 // shared with the parent process. To prevent child crashes from
42 // causing parent shutdowns, |g_pipe_pid| is the pid for the process
43 // which registered |g_shutdown_pipe_write_fd|.
44 // See <http://crbug.com/175341>.
45 pid_t g_pipe_pid = -1;
46 int g_shutdown_pipe_write_fd = -1;
47 int g_shutdown_pipe_read_fd = -1;
48
49 // Common code between SIG{HUP, INT, TERM}Handler.
50 void GracefulShutdownHandler(int signal) {
51 // Reinstall the default handler. We had one shot at graceful shutdown.
52 struct sigaction action;
53 memset(&action, 0, sizeof(action));
54 action.sa_handler = SIG_DFL;
55 RAW_CHECK(sigaction(signal, &action, NULL) == 0);
56
57 RAW_CHECK(g_pipe_pid != -1);
58 RAW_CHECK(g_shutdown_pipe_write_fd != -1);
59 RAW_CHECK(g_shutdown_pipe_read_fd != -1);
60 RAW_CHECK(g_pipe_pid == getpid());
61 size_t bytes_written = 0;
62 do {
63 int rv = HANDLE_EINTR(
64 write(g_shutdown_pipe_write_fd,
65 reinterpret_cast<const char*>(&signal) + bytes_written,
66 sizeof(signal) - bytes_written));
67 RAW_CHECK(rv >= 0);
68 bytes_written += rv;
69 } while (bytes_written < sizeof(signal));
70 }
71
72 // See comment in |PostMainMessageLoopStart()|, where sigaction is called.
73 void SIGHUPHandler(int signal) {
74 RAW_CHECK(signal == SIGHUP);
75 GracefulShutdownHandler(signal);
76 }
77
78 // See comment in |PostMainMessageLoopStart()|, where sigaction is called.
79 void SIGINTHandler(int signal) {
80 RAW_CHECK(signal == SIGINT);
81 GracefulShutdownHandler(signal);
82 }
83
84 // See comment in |PostMainMessageLoopStart()|, where sigaction is called.
85 void SIGTERMHandler(int signal) {
86 RAW_CHECK(signal == SIGTERM);
87 GracefulShutdownHandler(signal);
88 }
89
90 // ExitHandler takes care of servicing an exit (from a signal) at the 33 // ExitHandler takes care of servicing an exit (from a signal) at the
91 // appropriate time. Specifically if we get an exit and have not finished 34 // appropriate time. Specifically if we get an exit and have not finished
92 // session restore we delay the exit. To do otherwise means we're exiting part 35 // session restore we delay the exit. To do otherwise means we're exiting part
93 // way through startup which causes all sorts of problems. 36 // way through startup which causes all sorts of problems.
94 class ExitHandler { 37 class ExitHandler {
95 public: 38 public:
96 // Invokes exit when appropriate. 39 // Invokes exit when appropriate.
97 static void ExitWhenPossibleOnUIThread(); 40 static void ExitWhenPossibleOnUIThread();
98 41
99 private: 42 private:
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
152 // static 95 // static
153 void ExitHandler::Exit() { 96 void ExitHandler::Exit() {
154 #if defined(OS_CHROMEOS) 97 #if defined(OS_CHROMEOS)
155 // On ChromeOS, exiting on signal should be always clean. 98 // On ChromeOS, exiting on signal should be always clean.
156 chrome::ExitCleanly(); 99 chrome::ExitCleanly();
157 #else 100 #else
158 chrome::AttemptExit(); 101 chrome::AttemptExit();
159 #endif 102 #endif
160 } 103 }
161 104
162 class ShutdownDetector : public base::PlatformThread::Delegate {
163 public:
164 explicit ShutdownDetector(int shutdown_fd);
165
166 void ThreadMain() override;
167
168 private:
169 const int shutdown_fd_;
170
171 DISALLOW_COPY_AND_ASSIGN(ShutdownDetector);
172 };
173
174 ShutdownDetector::ShutdownDetector(int shutdown_fd)
175 : shutdown_fd_(shutdown_fd) {
176 CHECK_NE(shutdown_fd_, -1);
177 }
178
179 // These functions are used to help us diagnose crash dumps that happen
180 // during the shutdown process.
181 NOINLINE void ShutdownFDReadError() {
182 // Ensure function isn't optimized away.
183 asm("");
184 sleep(UINT_MAX);
185 }
186
187 NOINLINE void ShutdownFDClosedError() {
188 // Ensure function isn't optimized away.
189 asm("");
190 sleep(UINT_MAX);
191 }
192
193 NOINLINE void ExitPosted() {
194 // Ensure function isn't optimized away.
195 asm("");
196 sleep(UINT_MAX);
197 }
198
199 void ShutdownDetector::ThreadMain() {
200 base::PlatformThread::SetName("CrShutdownDetector");
201
202 int signal;
203 size_t bytes_read = 0;
204 ssize_t ret;
205 do {
206 ret = HANDLE_EINTR(
207 read(shutdown_fd_,
208 reinterpret_cast<char*>(&signal) + bytes_read,
209 sizeof(signal) - bytes_read));
210 if (ret < 0) {
211 NOTREACHED() << "Unexpected error: " << strerror(errno);
212 ShutdownFDReadError();
213 break;
214 } else if (ret == 0) {
215 NOTREACHED() << "Unexpected closure of shutdown pipe.";
216 ShutdownFDClosedError();
217 break;
218 }
219 bytes_read += ret;
220 } while (bytes_read < sizeof(signal));
221 VLOG(1) << "Handling shutdown for signal " << signal << ".";
222 base::Closure task = base::Bind(&ExitHandler::ExitWhenPossibleOnUIThread);
223
224 if (!BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, task)) {
225 // Without a UI thread to post the exit task to, there aren't many
226 // options. Raise the signal again. The default handler will pick it up
227 // and cause an ungraceful exit.
228 RAW_LOG(WARNING, "No UI thread, exiting ungracefully.");
229 kill(getpid(), signal);
230
231 // The signal may be handled on another thread. Give that a chance to
232 // happen.
233 sleep(3);
234
235 // We really should be dead by now. For whatever reason, we're not. Exit
236 // immediately, with the exit status set to the signal number with bit 8
237 // set. On the systems that we care about, this exit status is what is
238 // normally used to indicate an exit by this signal's default handler.
239 // This mechanism isn't a de jure standard, but even in the worst case, it
240 // should at least result in an immediate exit.
241 RAW_LOG(WARNING, "Still here, exiting really ungracefully.");
242 _exit(signal | (1 << 7));
243 }
244 ExitPosted();
245 }
246
247 } // namespace 105 } // namespace
248 106
249 // ChromeBrowserMainPartsPosix ------------------------------------------------- 107 // ChromeBrowserMainPartsPosix -------------------------------------------------
250 108
251 ChromeBrowserMainPartsPosix::ChromeBrowserMainPartsPosix( 109 ChromeBrowserMainPartsPosix::ChromeBrowserMainPartsPosix(
252 const content::MainFunctionParams& parameters) 110 const content::MainFunctionParams& parameters)
253 : ChromeBrowserMainParts(parameters) { 111 : ChromeBrowserMainParts(parameters) {
254 } 112 }
255 113
256 void ChromeBrowserMainPartsPosix::PreEarlyInitialization() { 114 void ChromeBrowserMainPartsPosix::PreEarlyInitialization() {
257 ChromeBrowserMainParts::PreEarlyInitialization(); 115 ChromeBrowserMainParts::PreEarlyInitialization();
258 116
259 // We need to accept SIGCHLD, even though our handler is a no-op because 117 // We need to accept SIGCHLD, even though our handler is a no-op because
260 // otherwise we cannot wait on children. (According to POSIX 2001.) 118 // otherwise we cannot wait on children. (According to POSIX 2001.)
James Cook 2017/03/01 18:24:38 It's unclear to me if we need this in mash so I di
261 struct sigaction action; 119 struct sigaction action;
262 memset(&action, 0, sizeof(action)); 120 memset(&action, 0, sizeof(action));
263 action.sa_handler = SIGCHLDHandler; 121 action.sa_handler = SIGCHLDHandler;
264 CHECK(sigaction(SIGCHLD, &action, NULL) == 0); 122 CHECK(sigaction(SIGCHLD, &action, NULL) == 0);
265 } 123 }
266 124
267 void ChromeBrowserMainPartsPosix::PostMainMessageLoopStart() { 125 void ChromeBrowserMainPartsPosix::PostMainMessageLoopStart() {
268 ChromeBrowserMainParts::PostMainMessageLoopStart(); 126 ChromeBrowserMainParts::PostMainMessageLoopStart();
269 127
270 int pipefd[2]; 128 // Exit in response to SIGINT, SIGTERM, etc.
271 int ret = pipe(pipefd); 129 InstallShutdownSignalHandlers(
272 if (ret < 0) { 130 base::Bind(&ExitHandler::ExitWhenPossibleOnUIThread),
273 PLOG(DFATAL) << "Failed to create pipe"; 131 BrowserThread::GetTaskRunnerForThread(BrowserThread::UI));
274 } else {
275 g_pipe_pid = getpid();
276 g_shutdown_pipe_read_fd = pipefd[0];
277 g_shutdown_pipe_write_fd = pipefd[1];
278 #if !defined(ADDRESS_SANITIZER) && !defined(KEEP_SHADOW_STACKS)
279 const size_t kShutdownDetectorThreadStackSize = PTHREAD_STACK_MIN * 2;
280 #else
281 // ASan instrumentation and -finstrument-functions (used for keeping the
282 // shadow stacks) bloat the stack frames, so we need to increase the stack
283 // size to avoid hitting the guard page.
284 const size_t kShutdownDetectorThreadStackSize = PTHREAD_STACK_MIN * 4;
285 #endif
286 // TODO(viettrungluu,willchan): crbug.com/29675 - This currently leaks, so
287 // if you change this, you'll probably need to change the suppression.
288 if (!base::PlatformThread::CreateNonJoinable(
289 kShutdownDetectorThreadStackSize,
290 new ShutdownDetector(g_shutdown_pipe_read_fd))) {
291 LOG(DFATAL) << "Failed to create shutdown detector task.";
292 }
293
294 // Setup signal handlers for shutdown AFTER shutdown pipe is setup because
295 // it may be called right away after handler is set.
296
297 // If adding to this list of signal handlers, note the new signal probably
298 // needs to be reset in child processes. See
299 // base/process_util_posix.cc:LaunchProcess.
300
301 // We need to handle SIGTERM, because that is how many POSIX-based distros
302 // ask processes to quit gracefully at shutdown time.
303 struct sigaction action;
304 memset(&action, 0, sizeof(action));
305 action.sa_handler = SIGTERMHandler;
306 CHECK(sigaction(SIGTERM, &action, NULL) == 0);
307
308 // Also handle SIGINT - when the user terminates the browser via Ctrl+C. If
309 // the browser process is being debugged, GDB will catch the SIGINT first.
310 action.sa_handler = SIGINTHandler;
311 CHECK(sigaction(SIGINT, &action, NULL) == 0);
312
313 // And SIGHUP, for when the terminal disappears. On shutdown, many Linux
314 // distros send SIGHUP, SIGTERM, and then SIGKILL.
315 action.sa_handler = SIGHUPHandler;
316 CHECK(sigaction(SIGHUP, &action, NULL) == 0);
317 }
318 } 132 }
319 133
320 void ChromeBrowserMainPartsPosix::ShowMissingLocaleMessageBox() { 134 void ChromeBrowserMainPartsPosix::ShowMissingLocaleMessageBox() {
321 #if defined(OS_CHROMEOS) 135 #if defined(OS_CHROMEOS)
322 NOTREACHED(); // Should not ever happen on ChromeOS. 136 NOTREACHED(); // Should not ever happen on ChromeOS.
323 #elif defined(OS_MACOSX) 137 #elif defined(OS_MACOSX)
324 // Not called on Mac because we load the locale files differently. 138 // Not called on Mac because we load the locale files differently.
325 NOTREACHED(); 139 NOTREACHED();
326 #elif defined(USE_AURA) 140 #elif defined(USE_AURA)
327 // TODO(port): We may want a views based message dialog here eventually, but 141 // TODO(port): We may want a views based message dialog here eventually, but
328 // for now, crash. 142 // for now, crash.
329 NOTREACHED(); 143 NOTREACHED();
330 #else 144 #else
331 #error "Need MessageBox implementation." 145 #error "Need MessageBox implementation."
332 #endif 146 #endif
333 } 147 }
OLDNEW
« chrome/app/shutdown_signal_handlers_posix.cc ('K') | « chrome/browser/BUILD.gn ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698