OLD | NEW |
---|---|
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 Loading... | |
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 } |
OLD | NEW |