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.
4
5 #include "chrome/browser/chrome_browser_main_posix.h"
6
7 #include <errno.h>
8 #include <limits.h>
9 #include <pthread.h>
10 #include <signal.h>
11 #include <sys/resource.h>
12 #include <unistd.h>
13
14 #include <string>
15
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"
26 #include "content/public/browser/notification_observer.h"
27 #include "content/public/browser/notification_registrar.h"
28 #include "content/public/browser/notification_service.h"
29
30 #if defined(TOOLKIT_GTK)
31 #include "chrome/browser/ui/gtk/chrome_browser_main_extra_parts_gtk.h"
32
33 #if defined(ENABLE_PRINTING)
34 #include "chrome/browser/printing/print_dialog_gtk.h"
35 #endif // defined(ENABLE_PRINTING)
36 #endif // defined(TOOLKIT_GTK)
37
38 using content::BrowserThread;
39
40 namespace {
41
42 // See comment in |PreEarlyInitialization()|, where sigaction is called.
SIGCHLDHandler(int signal)43 void SIGCHLDHandler(int signal) {
44 }
45
46 // The OSX fork() implementation can crash in the child process before
47 // fork() returns. In that case, the shutdown pipe will still be
48 // shared with the parent process. To prevent child crashes from
49 // causing parent shutdowns, |g_pipe_pid| is the pid for the process
50 // which registered |g_shutdown_pipe_write_fd|.
51 // See <http://crbug.com/175341>.
52 pid_t g_pipe_pid = -1;
53 int g_shutdown_pipe_write_fd = -1;
54 int g_shutdown_pipe_read_fd = -1;
55
56 // Common code between SIG{HUP, INT, TERM}Handler.
GracefulShutdownHandler(int signal)57 void GracefulShutdownHandler(int signal) {
58 // Reinstall the default handler. We had one shot at graceful shutdown.
59 struct sigaction action;
60 memset(&action, 0, sizeof(action));
61 action.sa_handler = SIG_DFL;
62 RAW_CHECK(sigaction(signal, &action, NULL) == 0);
63
64 RAW_CHECK(g_pipe_pid == getpid());
65 RAW_CHECK(g_shutdown_pipe_write_fd != -1);
66 RAW_CHECK(g_shutdown_pipe_read_fd != -1);
67 size_t bytes_written = 0;
68 do {
69 int rv = HANDLE_EINTR(
70 write(g_shutdown_pipe_write_fd,
71 reinterpret_cast<const char*>(&signal) + bytes_written,
72 sizeof(signal) - bytes_written));
73 RAW_CHECK(rv >= 0);
74 bytes_written += rv;
75 } while (bytes_written < sizeof(signal));
76 }
77
78 // See comment in |PostMainMessageLoopStart()|, where sigaction is called.
SIGHUPHandler(int signal)79 void SIGHUPHandler(int signal) {
80 RAW_CHECK(signal == SIGHUP);
81 GracefulShutdownHandler(signal);
82 }
83
84 // See comment in |PostMainMessageLoopStart()|, where sigaction is called.
SIGINTHandler(int signal)85 void SIGINTHandler(int signal) {
86 RAW_CHECK(signal == SIGINT);
87 GracefulShutdownHandler(signal);
88 }
89
90 // See comment in |PostMainMessageLoopStart()|, where sigaction is called.
SIGTERMHandler(int signal)91 void SIGTERMHandler(int signal) {
92 RAW_CHECK(signal == SIGTERM);
93 GracefulShutdownHandler(signal);
94 }
95
96 // ExitHandler takes care of servicing an exit (from a signal) at the
97 // appropriate time. Specifically if we get an exit and have not finished
98 // session restore we delay the exit. To do otherwise means we're exiting part
99 // way through startup which causes all sorts of problems.
100 class ExitHandler : public content::NotificationObserver {
101 public:
102 // Invokes exit when appropriate.
103 static void ExitWhenPossibleOnUIThread();
104
105 // Overridden from content::NotificationObserver:
106 virtual void Observe(int type,
107 const content::NotificationSource& source,
108 const content::NotificationDetails& details) OVERRIDE;
109
110 private:
111 ExitHandler();
112 virtual ~ExitHandler();
113
114 // Does the appropriate call to Exit.
115 static void Exit();
116
117 content::NotificationRegistrar registrar_;
118
119 DISALLOW_COPY_AND_ASSIGN(ExitHandler);
120 };
121
122 // static
ExitWhenPossibleOnUIThread()123 void ExitHandler::ExitWhenPossibleOnUIThread() {
124 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
125 if (SessionRestore::IsRestoringSynchronously()) {
126 // ExitHandler takes care of deleting itself.
127 new ExitHandler();
128 } else {
129 Exit();
130 }
131 }
132
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)133 void ExitHandler::Observe(int type,
134 const content::NotificationSource& source,
135 const content::NotificationDetails& details) {
136 if (!SessionRestore::IsRestoringSynchronously()) {
137 // At this point the message loop may not be running (meaning we haven't
138 // gotten through browser startup, but are close). Post the task to at which
139 // point the message loop is running.
140 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
141 base::Bind(&ExitHandler::Exit));
142 delete this;
143 }
144 }
145
ExitHandler()146 ExitHandler::ExitHandler() {
147 registrar_.Add(
148 this, chrome::NOTIFICATION_SESSION_RESTORE_DONE,
149 content::NotificationService::AllBrowserContextsAndSources());
150 }
151
~ExitHandler()152 ExitHandler::~ExitHandler() {
153 }
154
155 // static
Exit()156 void ExitHandler::Exit() {
157 #if defined(OS_CHROMEOS)
158 // On ChromeOS, exiting on signal should be always clean.
159 chrome::ExitCleanly();
160 #else
161 chrome::AttemptExit();
162 #endif
163 }
164
165 class ShutdownDetector : public base::PlatformThread::Delegate {
166 public:
167 explicit ShutdownDetector(int shutdown_fd);
168
169 virtual void ThreadMain() OVERRIDE;
170
171 private:
172 const int shutdown_fd_;
173
174 DISALLOW_COPY_AND_ASSIGN(ShutdownDetector);
175 };
176
ShutdownDetector(int shutdown_fd)177 ShutdownDetector::ShutdownDetector(int shutdown_fd)
178 : shutdown_fd_(shutdown_fd) {
179 CHECK_NE(shutdown_fd_, -1);
180 }
181
182 // These functions are used to help us diagnose crash dumps that happen
183 // during the shutdown process.
ShutdownFDReadError()184 NOINLINE void ShutdownFDReadError() {
185 // Ensure function isn't optimized away.
186 asm("");
187 sleep(UINT_MAX);
188 }
189
ShutdownFDClosedError()190 NOINLINE void ShutdownFDClosedError() {
191 // Ensure function isn't optimized away.
192 asm("");
193 sleep(UINT_MAX);
194 }
195
ExitPosted()196 NOINLINE void ExitPosted() {
197 // Ensure function isn't optimized away.
198 asm("");
199 sleep(UINT_MAX);
200 }
201
ThreadMain()202 void ShutdownDetector::ThreadMain() {
203 base::PlatformThread::SetName("CrShutdownDetector");
204
205 int signal;
206 size_t bytes_read = 0;
207 ssize_t ret;
208 do {
209 ret = HANDLE_EINTR(
210 read(shutdown_fd_,
211 reinterpret_cast<char*>(&signal) + bytes_read,
212 sizeof(signal) - bytes_read));
213 if (ret < 0) {
214 NOTREACHED() << "Unexpected error: " << strerror(errno);
215 ShutdownFDReadError();
216 break;
217 } else if (ret == 0) {
218 NOTREACHED() << "Unexpected closure of shutdown pipe.";
219 ShutdownFDClosedError();
220 break;
221 }
222 bytes_read += ret;
223 } while (bytes_read < sizeof(signal));
224 VLOG(1) << "Handling shutdown for signal " << signal << ".";
225 base::Closure task = base::Bind(&ExitHandler::ExitWhenPossibleOnUIThread);
226
227 if (!BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, task)) {
228 // Without a UI thread to post the exit task to, there aren't many
229 // options. Raise the signal again. The default handler will pick it up
230 // and cause an ungraceful exit.
231 RAW_LOG(WARNING, "No UI thread, exiting ungracefully.");
232 kill(getpid(), signal);
233
234 // The signal may be handled on another thread. Give that a chance to
235 // happen.
236 sleep(3);
237
238 // We really should be dead by now. For whatever reason, we're not. Exit
239 // immediately, with the exit status set to the signal number with bit 8
240 // set. On the systems that we care about, this exit status is what is
241 // normally used to indicate an exit by this signal's default handler.
242 // This mechanism isn't a de jure standard, but even in the worst case, it
243 // should at least result in an immediate exit.
244 RAW_LOG(WARNING, "Still here, exiting really ungracefully.");
245 _exit(signal | (1 << 7));
246 }
247 ExitPosted();
248 }
249
250 // Sets the file descriptor soft limit to |max_descriptors| or the OS hard
251 // limit, whichever is lower.
SetFileDescriptorLimit(unsigned int max_descriptors)252 void SetFileDescriptorLimit(unsigned int max_descriptors) {
253 struct rlimit limits;
254 if (getrlimit(RLIMIT_NOFILE, &limits) == 0) {
255 unsigned int new_limit = max_descriptors;
256 if (limits.rlim_max > 0 && limits.rlim_max < max_descriptors) {
257 new_limit = limits.rlim_max;
258 }
259 limits.rlim_cur = new_limit;
260 if (setrlimit(RLIMIT_NOFILE, &limits) != 0) {
261 PLOG(INFO) << "Failed to set file descriptor limit";
262 }
263 } else {
264 PLOG(INFO) << "Failed to get file descriptor limit";
265 }
266 }
267
268 } // namespace
269
270 // ChromeBrowserMainPartsPosix -------------------------------------------------
271
ChromeBrowserMainPartsPosix(const content::MainFunctionParams & parameters)272 ChromeBrowserMainPartsPosix::ChromeBrowserMainPartsPosix(
273 const content::MainFunctionParams& parameters)
274 : ChromeBrowserMainParts(parameters) {
275 }
276
PreEarlyInitialization()277 void ChromeBrowserMainPartsPosix::PreEarlyInitialization() {
278 ChromeBrowserMainParts::PreEarlyInitialization();
279
280 // We need to accept SIGCHLD, even though our handler is a no-op because
281 // otherwise we cannot wait on children. (According to POSIX 2001.)
282 struct sigaction action;
283 memset(&action, 0, sizeof(action));
284 action.sa_handler = SIGCHLDHandler;
285 CHECK(sigaction(SIGCHLD, &action, NULL) == 0);
286
287 const std::string fd_limit_string =
288 parsed_command_line().GetSwitchValueASCII(
289 switches::kFileDescriptorLimit);
290 int fd_limit = 0;
291 if (!fd_limit_string.empty()) {
292 base::StringToInt(fd_limit_string, &fd_limit);
293 }
294 #if defined(OS_MACOSX)
295 // We use quite a few file descriptors for our IPC, and the default limit on
296 // the Mac is low (256), so bump it up if there is no explicit override.
297 if (fd_limit == 0) {
298 fd_limit = 1024;
299 }
300 #endif // OS_MACOSX
301 if (fd_limit > 0)
302 SetFileDescriptorLimit(fd_limit);
303 }
304
PostMainMessageLoopStart()305 void ChromeBrowserMainPartsPosix::PostMainMessageLoopStart() {
306 ChromeBrowserMainParts::PostMainMessageLoopStart();
307
308 int pipefd[2];
309 int ret = pipe(pipefd);
310 if (ret < 0) {
311 PLOG(DFATAL) << "Failed to create pipe";
312 } else {
313 g_pipe_pid = getpid();
314 g_shutdown_pipe_read_fd = pipefd[0];
315 g_shutdown_pipe_write_fd = pipefd[1];
316 #if !defined(ADDRESS_SANITIZER) && !defined(KEEP_SHADOW_STACKS)
317 const size_t kShutdownDetectorThreadStackSize = PTHREAD_STACK_MIN;
318 #else
319 // ASan instrumentation and -finstrument-functions (used for keeping the
320 // shadow stacks) bloat the stack frames, so we need to increase the stack
321 // size to avoid hitting the guard page.
322 const size_t kShutdownDetectorThreadStackSize = PTHREAD_STACK_MIN * 4;
323 #endif
324 // TODO(viettrungluu,willchan): crbug.com/29675 - This currently leaks, so
325 // if you change this, you'll probably need to change the suppression.
326 if (!base::PlatformThread::CreateNonJoinable(
327 kShutdownDetectorThreadStackSize,
328 new ShutdownDetector(g_shutdown_pipe_read_fd))) {
329 LOG(DFATAL) << "Failed to create shutdown detector task.";
330 }
331 }
332 // Setup signal handlers for shutdown AFTER shutdown pipe is setup because
333 // it may be called right away after handler is set.
334
335 // If adding to this list of signal handlers, note the new signal probably
336 // needs to be reset in child processes. See
337 // base/process_util_posix.cc:LaunchProcess.
338
339 // We need to handle SIGTERM, because that is how many POSIX-based distros ask
340 // processes to quit gracefully at shutdown time.
341 struct sigaction action;
342 memset(&action, 0, sizeof(action));
343 action.sa_handler = SIGTERMHandler;
344 CHECK(sigaction(SIGTERM, &action, NULL) == 0);
345 // Also handle SIGINT - when the user terminates the browser via Ctrl+C. If
346 // the browser process is being debugged, GDB will catch the SIGINT first.
347 action.sa_handler = SIGINTHandler;
348 CHECK(sigaction(SIGINT, &action, NULL) == 0);
349 // And SIGHUP, for when the terminal disappears. On shutdown, many Linux
350 // distros send SIGHUP, SIGTERM, and then SIGKILL.
351 action.sa_handler = SIGHUPHandler;
352 CHECK(sigaction(SIGHUP, &action, NULL) == 0);
353
354 #if defined(TOOLKIT_GTK) && defined(ENABLE_PRINTING)
355 printing::PrintingContextGtk::SetCreatePrintDialogFunction(
356 &PrintDialogGtk::CreatePrintDialog);
357 #endif
358 }
359
ShowMissingLocaleMessageBox()360 void ChromeBrowserMainPartsPosix::ShowMissingLocaleMessageBox() {
361 #if defined(OS_CHROMEOS)
362 NOTREACHED(); // Should not ever happen on ChromeOS.
363 #elif defined(OS_MACOSX)
364 // Not called on Mac because we load the locale files differently.
365 NOTREACHED();
366 #elif defined(TOOLKIT_GTK)
367 ChromeBrowserMainExtraPartsGtk::ShowMessageBox(
368 chrome_browser::kMissingLocaleDataMessage);
369 #elif defined(USE_AURA)
370 // TODO(port): We may want a views based message dialog here eventually, but
371 // for now, crash.
372 NOTREACHED();
373 #else
374 #error "Need MessageBox implementation."
375 #endif
376 }
377