• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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