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
3 // found in the LICENSE file.
5 #include "chrome/browser/chrome_browser_main_posix.h"
11 #include <sys/resource.h>
16 #include "base/bind.h"
17 #include "base/command_line.h"
18 #include "base/logging.h"
19 #include "base/posix/eintr_wrapper.h"
20 #include "base/strings/string_number_conversions.h"
21 #include "chrome/browser/chrome_notification_types.h"
22 #include "chrome/browser/lifetime/application_lifetime.h"
23 #include "chrome/browser/sessions/session_restore.h"
24 #include "chrome/common/chrome_switches.h"
25 #include "content/public/browser/browser_thread.h"
27 using content::BrowserThread
;
31 // See comment in |PreEarlyInitialization()|, where sigaction is called.
32 void SIGCHLDHandler(int signal
) {
35 // The OSX fork() implementation can crash in the child process before
36 // fork() returns. In that case, the shutdown pipe will still be
37 // shared with the parent process. To prevent child crashes from
38 // causing parent shutdowns, |g_pipe_pid| is the pid for the process
39 // which registered |g_shutdown_pipe_write_fd|.
40 // See <http://crbug.com/175341>.
41 pid_t g_pipe_pid
= -1;
42 int g_shutdown_pipe_write_fd
= -1;
43 int g_shutdown_pipe_read_fd
= -1;
45 // Common code between SIG{HUP, INT, TERM}Handler.
46 void GracefulShutdownHandler(int signal
) {
47 // Reinstall the default handler. We had one shot at graceful shutdown.
48 struct sigaction action
;
49 memset(&action
, 0, sizeof(action
));
50 action
.sa_handler
= SIG_DFL
;
51 RAW_CHECK(sigaction(signal
, &action
, NULL
) == 0);
53 RAW_CHECK(g_pipe_pid
== getpid());
54 RAW_CHECK(g_shutdown_pipe_write_fd
!= -1);
55 RAW_CHECK(g_shutdown_pipe_read_fd
!= -1);
56 size_t bytes_written
= 0;
58 int rv
= HANDLE_EINTR(
59 write(g_shutdown_pipe_write_fd
,
60 reinterpret_cast<const char*>(&signal
) + bytes_written
,
61 sizeof(signal
) - bytes_written
));
64 } while (bytes_written
< sizeof(signal
));
67 // See comment in |PostMainMessageLoopStart()|, where sigaction is called.
68 void SIGHUPHandler(int signal
) {
69 RAW_CHECK(signal
== SIGHUP
);
70 GracefulShutdownHandler(signal
);
73 // See comment in |PostMainMessageLoopStart()|, where sigaction is called.
74 void SIGINTHandler(int signal
) {
75 RAW_CHECK(signal
== SIGINT
);
76 GracefulShutdownHandler(signal
);
79 // See comment in |PostMainMessageLoopStart()|, where sigaction is called.
80 void SIGTERMHandler(int signal
) {
81 RAW_CHECK(signal
== SIGTERM
);
82 GracefulShutdownHandler(signal
);
85 // ExitHandler takes care of servicing an exit (from a signal) at the
86 // appropriate time. Specifically if we get an exit and have not finished
87 // session restore we delay the exit. To do otherwise means we're exiting part
88 // way through startup which causes all sorts of problems.
91 // Invokes exit when appropriate.
92 static void ExitWhenPossibleOnUIThread();
98 // Called when a session restore has finished.
99 void OnSessionRestoreDone(int num_tabs_restored
);
101 // Does the appropriate call to Exit.
104 // Points to the on-session-restored callback that was registered with
105 // SessionRestore's callback list. When objects of this class are destroyed,
106 // the subscription object's destructor will automatically unregister the
107 // callback in SessionRestore, so that the callback list does not contain any
108 // obsolete callbacks.
109 SessionRestore::CallbackSubscription
110 on_session_restored_callback_subscription_
;
112 DISALLOW_COPY_AND_ASSIGN(ExitHandler
);
116 void ExitHandler::ExitWhenPossibleOnUIThread() {
117 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
118 if (SessionRestore::IsRestoringSynchronously()) {
119 // ExitHandler takes care of deleting itself.
126 ExitHandler::ExitHandler() {
127 on_session_restored_callback_subscription_
=
128 SessionRestore::RegisterOnSessionRestoredCallback(
129 base::Bind(&ExitHandler::OnSessionRestoreDone
,
130 base::Unretained(this)));
133 ExitHandler::~ExitHandler() {
136 void ExitHandler::OnSessionRestoreDone(int /* num_tabs */) {
137 if (!SessionRestore::IsRestoringSynchronously()) {
138 // At this point the message loop may not be running (meaning we haven't
139 // gotten through browser startup, but are close). Post the task to at which
140 // point the message loop is running.
141 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
142 base::Bind(&ExitHandler::Exit
));
148 void ExitHandler::Exit() {
149 #if defined(OS_CHROMEOS)
150 // On ChromeOS, exiting on signal should be always clean.
151 chrome::ExitCleanly();
153 chrome::AttemptExit();
157 class ShutdownDetector
: public base::PlatformThread::Delegate
{
159 explicit ShutdownDetector(int shutdown_fd
);
161 void ThreadMain() override
;
164 const int shutdown_fd_
;
166 DISALLOW_COPY_AND_ASSIGN(ShutdownDetector
);
169 ShutdownDetector::ShutdownDetector(int shutdown_fd
)
170 : shutdown_fd_(shutdown_fd
) {
171 CHECK_NE(shutdown_fd_
, -1);
174 // These functions are used to help us diagnose crash dumps that happen
175 // during the shutdown process.
176 NOINLINE
void ShutdownFDReadError() {
177 // Ensure function isn't optimized away.
182 NOINLINE
void ShutdownFDClosedError() {
183 // Ensure function isn't optimized away.
188 NOINLINE
void ExitPosted() {
189 // Ensure function isn't optimized away.
194 void ShutdownDetector::ThreadMain() {
195 base::PlatformThread::SetName("CrShutdownDetector");
198 size_t bytes_read
= 0;
203 reinterpret_cast<char*>(&signal
) + bytes_read
,
204 sizeof(signal
) - bytes_read
));
206 NOTREACHED() << "Unexpected error: " << strerror(errno
);
207 ShutdownFDReadError();
209 } else if (ret
== 0) {
210 NOTREACHED() << "Unexpected closure of shutdown pipe.";
211 ShutdownFDClosedError();
215 } while (bytes_read
< sizeof(signal
));
216 VLOG(1) << "Handling shutdown for signal " << signal
<< ".";
217 base::Closure task
= base::Bind(&ExitHandler::ExitWhenPossibleOnUIThread
);
219 if (!BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
, task
)) {
220 // Without a UI thread to post the exit task to, there aren't many
221 // options. Raise the signal again. The default handler will pick it up
222 // and cause an ungraceful exit.
223 RAW_LOG(WARNING
, "No UI thread, exiting ungracefully.");
224 kill(getpid(), signal
);
226 // The signal may be handled on another thread. Give that a chance to
230 // We really should be dead by now. For whatever reason, we're not. Exit
231 // immediately, with the exit status set to the signal number with bit 8
232 // set. On the systems that we care about, this exit status is what is
233 // normally used to indicate an exit by this signal's default handler.
234 // This mechanism isn't a de jure standard, but even in the worst case, it
235 // should at least result in an immediate exit.
236 RAW_LOG(WARNING
, "Still here, exiting really ungracefully.");
237 _exit(signal
| (1 << 7));
244 // ChromeBrowserMainPartsPosix -------------------------------------------------
246 ChromeBrowserMainPartsPosix::ChromeBrowserMainPartsPosix(
247 const content::MainFunctionParams
& parameters
)
248 : ChromeBrowserMainParts(parameters
) {
251 void ChromeBrowserMainPartsPosix::PreEarlyInitialization() {
252 ChromeBrowserMainParts::PreEarlyInitialization();
254 // We need to accept SIGCHLD, even though our handler is a no-op because
255 // otherwise we cannot wait on children. (According to POSIX 2001.)
256 struct sigaction action
;
257 memset(&action
, 0, sizeof(action
));
258 action
.sa_handler
= SIGCHLDHandler
;
259 CHECK(sigaction(SIGCHLD
, &action
, NULL
) == 0);
262 void ChromeBrowserMainPartsPosix::PostMainMessageLoopStart() {
263 ChromeBrowserMainParts::PostMainMessageLoopStart();
266 int ret
= pipe(pipefd
);
268 PLOG(DFATAL
) << "Failed to create pipe";
270 g_pipe_pid
= getpid();
271 g_shutdown_pipe_read_fd
= pipefd
[0];
272 g_shutdown_pipe_write_fd
= pipefd
[1];
273 #if !defined(ADDRESS_SANITIZER) && !defined(KEEP_SHADOW_STACKS)
274 const size_t kShutdownDetectorThreadStackSize
= PTHREAD_STACK_MIN
* 2;
276 // ASan instrumentation and -finstrument-functions (used for keeping the
277 // shadow stacks) bloat the stack frames, so we need to increase the stack
278 // size to avoid hitting the guard page.
279 const size_t kShutdownDetectorThreadStackSize
= PTHREAD_STACK_MIN
* 4;
281 // TODO(viettrungluu,willchan): crbug.com/29675 - This currently leaks, so
282 // if you change this, you'll probably need to change the suppression.
283 if (!base::PlatformThread::CreateNonJoinable(
284 kShutdownDetectorThreadStackSize
,
285 new ShutdownDetector(g_shutdown_pipe_read_fd
))) {
286 LOG(DFATAL
) << "Failed to create shutdown detector task.";
289 // Setup signal handlers for shutdown AFTER shutdown pipe is setup because
290 // it may be called right away after handler is set.
292 // If adding to this list of signal handlers, note the new signal probably
293 // needs to be reset in child processes. See
294 // base/process_util_posix.cc:LaunchProcess.
296 // We need to handle SIGTERM, because that is how many POSIX-based distros ask
297 // processes to quit gracefully at shutdown time.
298 struct sigaction action
;
299 memset(&action
, 0, sizeof(action
));
300 action
.sa_handler
= SIGTERMHandler
;
301 CHECK(sigaction(SIGTERM
, &action
, NULL
) == 0);
302 // Also handle SIGINT - when the user terminates the browser via Ctrl+C. If
303 // the browser process is being debugged, GDB will catch the SIGINT first.
304 action
.sa_handler
= SIGINTHandler
;
305 CHECK(sigaction(SIGINT
, &action
, NULL
) == 0);
306 // And SIGHUP, for when the terminal disappears. On shutdown, many Linux
307 // distros send SIGHUP, SIGTERM, and then SIGKILL.
308 action
.sa_handler
= SIGHUPHandler
;
309 CHECK(sigaction(SIGHUP
, &action
, NULL
) == 0);
312 void ChromeBrowserMainPartsPosix::ShowMissingLocaleMessageBox() {
313 #if defined(OS_CHROMEOS)
314 NOTREACHED(); // Should not ever happen on ChromeOS.
315 #elif defined(OS_MACOSX)
316 // Not called on Mac because we load the locale files differently.
318 #elif defined(USE_AURA)
319 // TODO(port): We may want a views based message dialog here eventually, but
323 #error "Need MessageBox implementation."