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