• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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/browser_main_posix.h"
6 
7 #include <errno.h>
8 #include <limits.h>
9 #include <signal.h>
10 #include <sys/resource.h>
11 #include <unistd.h>
12 
13 #include <string>
14 
15 #include "base/command_line.h"
16 #include "base/eintr_wrapper.h"
17 #include "base/logging.h"
18 #include "base/string_number_conversions.h"
19 #include "chrome/browser/defaults.h"
20 #include "chrome/browser/ui/browser_list.h"
21 #include "chrome/common/chrome_switches.h"
22 #include "content/browser/browser_thread.h"
23 
24 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
25 #include "chrome/browser/printing/print_dialog_gtk.h"
26 #endif
27 
28 namespace {
29 
30 // See comment in |PreEarlyInitialization()|, where sigaction is called.
SIGCHLDHandler(int signal)31 void SIGCHLDHandler(int signal) {
32 }
33 
34 int g_shutdown_pipe_write_fd = -1;
35 int g_shutdown_pipe_read_fd = -1;
36 
37 // Common code between SIG{HUP, INT, TERM}Handler.
GracefulShutdownHandler(int signal)38 void GracefulShutdownHandler(int signal) {
39   // Reinstall the default handler.  We had one shot at graceful shutdown.
40   struct sigaction action;
41   memset(&action, 0, sizeof(action));
42   action.sa_handler = SIG_DFL;
43   RAW_CHECK(sigaction(signal, &action, NULL) == 0);
44 
45   RAW_CHECK(g_shutdown_pipe_write_fd != -1);
46   RAW_CHECK(g_shutdown_pipe_read_fd != -1);
47   size_t bytes_written = 0;
48   do {
49     int rv = HANDLE_EINTR(
50         write(g_shutdown_pipe_write_fd,
51               reinterpret_cast<const char*>(&signal) + bytes_written,
52               sizeof(signal) - bytes_written));
53     RAW_CHECK(rv >= 0);
54     bytes_written += rv;
55   } while (bytes_written < sizeof(signal));
56 }
57 
58 // See comment in |PreEarlyInitialization()|, where sigaction is called.
SIGHUPHandler(int signal)59 void SIGHUPHandler(int signal) {
60   RAW_CHECK(signal == SIGHUP);
61   GracefulShutdownHandler(signal);
62 }
63 
64 // See comment in |PreEarlyInitialization()|, where sigaction is called.
SIGINTHandler(int signal)65 void SIGINTHandler(int signal) {
66   RAW_CHECK(signal == SIGINT);
67   GracefulShutdownHandler(signal);
68 }
69 
70 // See comment in |PreEarlyInitialization()|, where sigaction is called.
SIGTERMHandler(int signal)71 void SIGTERMHandler(int signal) {
72   RAW_CHECK(signal == SIGTERM);
73   GracefulShutdownHandler(signal);
74 }
75 
76 class ShutdownDetector : public base::PlatformThread::Delegate {
77  public:
78   explicit ShutdownDetector(int shutdown_fd);
79 
80   virtual void ThreadMain();
81 
82  private:
83   const int shutdown_fd_;
84 
85   DISALLOW_COPY_AND_ASSIGN(ShutdownDetector);
86 };
87 
ShutdownDetector(int shutdown_fd)88 ShutdownDetector::ShutdownDetector(int shutdown_fd)
89     : shutdown_fd_(shutdown_fd) {
90   CHECK_NE(shutdown_fd_, -1);
91 }
92 
93 
94 // These functions are used to help us diagnose crash dumps that happen
95 // during the shutdown process.
ShutdownFDReadError()96 NOINLINE void ShutdownFDReadError() {
97   // Ensure function isn't optimized away.
98   asm("");
99   sleep(UINT_MAX);
100 }
101 
ShutdownFDClosedError()102 NOINLINE void ShutdownFDClosedError() {
103   // Ensure function isn't optimized away.
104   asm("");
105   sleep(UINT_MAX);
106 }
107 
CloseAllBrowsersAndExitPosted()108 NOINLINE void CloseAllBrowsersAndExitPosted() {
109   // Ensure function isn't optimized away.
110   asm("");
111   sleep(UINT_MAX);
112 }
113 
ThreadMain()114 void ShutdownDetector::ThreadMain() {
115   base::PlatformThread::SetName("CrShutdownDetector");
116 
117   int signal;
118   size_t bytes_read = 0;
119   ssize_t ret;
120   do {
121     ret = HANDLE_EINTR(
122         read(shutdown_fd_,
123              reinterpret_cast<char*>(&signal) + bytes_read,
124              sizeof(signal) - bytes_read));
125     if (ret < 0) {
126       NOTREACHED() << "Unexpected error: " << strerror(errno);
127       ShutdownFDReadError();
128       break;
129     } else if (ret == 0) {
130       NOTREACHED() << "Unexpected closure of shutdown pipe.";
131       ShutdownFDClosedError();
132       break;
133     }
134     bytes_read += ret;
135   } while (bytes_read < sizeof(signal));
136 
137   VLOG(1) << "Handling shutdown for signal " << signal << ".";
138 
139   if (!BrowserThread::PostTask(
140       BrowserThread::UI, FROM_HERE,
141       NewRunnableFunction(BrowserList::CloseAllBrowsersAndExit))) {
142     // Without a UI thread to post the exit task to, there aren't many
143     // options.  Raise the signal again.  The default handler will pick it up
144     // and cause an ungraceful exit.
145     RAW_LOG(WARNING, "No UI thread, exiting ungracefully.");
146     kill(getpid(), signal);
147 
148     // The signal may be handled on another thread.  Give that a chance to
149     // happen.
150     sleep(3);
151 
152     // We really should be dead by now.  For whatever reason, we're not. Exit
153     // immediately, with the exit status set to the signal number with bit 8
154     // set.  On the systems that we care about, this exit status is what is
155     // normally used to indicate an exit by this signal's default handler.
156     // This mechanism isn't a de jure standard, but even in the worst case, it
157     // should at least result in an immediate exit.
158     RAW_LOG(WARNING, "Still here, exiting really ungracefully.");
159     _exit(signal | (1 << 7));
160   }
161   CloseAllBrowsersAndExitPosted();
162 }
163 
164 // Sets the file descriptor soft limit to |max_descriptors| or the OS hard
165 // limit, whichever is lower.
SetFileDescriptorLimit(unsigned int max_descriptors)166 void SetFileDescriptorLimit(unsigned int max_descriptors) {
167   struct rlimit limits;
168   if (getrlimit(RLIMIT_NOFILE, &limits) == 0) {
169     unsigned int new_limit = max_descriptors;
170     if (limits.rlim_max > 0 && limits.rlim_max < max_descriptors) {
171       new_limit = limits.rlim_max;
172     }
173     limits.rlim_cur = new_limit;
174     if (setrlimit(RLIMIT_NOFILE, &limits) != 0) {
175       PLOG(INFO) << "Failed to set file descriptor limit";
176     }
177   } else {
178     PLOG(INFO) << "Failed to get file descriptor limit";
179   }
180 }
181 
182 }  // namespace
183 
184 // BrowserMainPartsPosix -------------------------------------------------------
185 
PreEarlyInitialization()186 void BrowserMainPartsPosix::PreEarlyInitialization() {
187   // We need to accept SIGCHLD, even though our handler is a no-op because
188   // otherwise we cannot wait on children. (According to POSIX 2001.)
189   struct sigaction action;
190   memset(&action, 0, sizeof(action));
191   action.sa_handler = SIGCHLDHandler;
192   CHECK(sigaction(SIGCHLD, &action, NULL) == 0);
193 
194   // If adding to this list of signal handlers, note the new signal probably
195   // needs to be reset in child processes. See
196   // base/process_util_posix.cc:LaunchApp
197 
198   // We need to handle SIGTERM, because that is how many POSIX-based distros ask
199   // processes to quit gracefully at shutdown time.
200   memset(&action, 0, sizeof(action));
201   action.sa_handler = SIGTERMHandler;
202   CHECK(sigaction(SIGTERM, &action, NULL) == 0);
203   // Also handle SIGINT - when the user terminates the browser via Ctrl+C. If
204   // the browser process is being debugged, GDB will catch the SIGINT first.
205   action.sa_handler = SIGINTHandler;
206   CHECK(sigaction(SIGINT, &action, NULL) == 0);
207   // And SIGHUP, for when the terminal disappears. On shutdown, many Linux
208   // distros send SIGHUP, SIGTERM, and then SIGKILL.
209   action.sa_handler = SIGHUPHandler;
210   CHECK(sigaction(SIGHUP, &action, NULL) == 0);
211 
212   const std::string fd_limit_string =
213       parsed_command_line().GetSwitchValueASCII(
214           switches::kFileDescriptorLimit);
215   int fd_limit = 0;
216   if (!fd_limit_string.empty()) {
217     base::StringToInt(fd_limit_string, &fd_limit);
218   }
219 #if defined(OS_MACOSX)
220   // We use quite a few file descriptors for our IPC, and the default limit on
221   // the Mac is low (256), so bump it up if there is no explicit override.
222   if (fd_limit == 0) {
223     fd_limit = 1024;
224   }
225 #endif  // OS_MACOSX
226   if (fd_limit > 0)
227     SetFileDescriptorLimit(fd_limit);
228 
229 #if defined(OS_CHROMEOS)
230   if (parsed_command_line().HasSwitch(switches::kGuestSession)) {
231     // Disable sync and extensions if we're in "browse without sign-in" mode.
232     CommandLine* singleton_command_line = CommandLine::ForCurrentProcess();
233     singleton_command_line->AppendSwitch(switches::kDisableSync);
234     singleton_command_line->AppendSwitch(switches::kDisableExtensions);
235     browser_defaults::bookmarks_enabled = false;
236   }
237 #endif
238 }
239 
PostMainMessageLoopStart()240 void BrowserMainPartsPosix::PostMainMessageLoopStart() {
241   int pipefd[2];
242   int ret = pipe(pipefd);
243   if (ret < 0) {
244     PLOG(DFATAL) << "Failed to create pipe";
245   } else {
246     g_shutdown_pipe_read_fd = pipefd[0];
247     g_shutdown_pipe_write_fd = pipefd[1];
248     const size_t kShutdownDetectorThreadStackSize = 4096;
249     // TODO(viettrungluu,willchan): crbug.com/29675 - This currently leaks, so
250     // if you change this, you'll probably need to change the suppression.
251     if (!base::PlatformThread::CreateNonJoinable(
252             kShutdownDetectorThreadStackSize,
253             new ShutdownDetector(g_shutdown_pipe_read_fd))) {
254       LOG(DFATAL) << "Failed to create shutdown detector task.";
255     }
256   }
257 
258 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
259   printing::PrintingContextCairo::SetCreatePrintDialogFunction(
260       &PrintDialogGtk::CreatePrintDialog);
261 #endif  // defined(OS_LINUX) && !defined(OS_CHROMEOS)
262 }
263