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
28namespace {
29
30// See comment in |PreEarlyInitialization()|, where sigaction is called.
31void SIGCHLDHandler(int signal) {
32}
33
34int g_shutdown_pipe_write_fd = -1;
35int g_shutdown_pipe_read_fd = -1;
36
37// Common code between SIG{HUP, INT, TERM}Handler.
38void 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.
59void SIGHUPHandler(int signal) {
60  RAW_CHECK(signal == SIGHUP);
61  GracefulShutdownHandler(signal);
62}
63
64// See comment in |PreEarlyInitialization()|, where sigaction is called.
65void SIGINTHandler(int signal) {
66  RAW_CHECK(signal == SIGINT);
67  GracefulShutdownHandler(signal);
68}
69
70// See comment in |PreEarlyInitialization()|, where sigaction is called.
71void SIGTERMHandler(int signal) {
72  RAW_CHECK(signal == SIGTERM);
73  GracefulShutdownHandler(signal);
74}
75
76class 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
88ShutdownDetector::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.
96NOINLINE void ShutdownFDReadError() {
97  // Ensure function isn't optimized away.
98  asm("");
99  sleep(UINT_MAX);
100}
101
102NOINLINE void ShutdownFDClosedError() {
103  // Ensure function isn't optimized away.
104  asm("");
105  sleep(UINT_MAX);
106}
107
108NOINLINE void CloseAllBrowsersAndExitPosted() {
109  // Ensure function isn't optimized away.
110  asm("");
111  sleep(UINT_MAX);
112}
113
114void 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.
166void 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
186void 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
240void 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