Home | History | Annotate | Download | only in browser
      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/process_singleton.h"
      6 
      7 #include <sys/types.h>
      8 #include <sys/wait.h>
      9 #include <signal.h>
     10 #include <unistd.h>
     11 #include <vector>
     12 #include <string>
     13 
     14 #include "base/eintr_wrapper.h"
     15 #include "base/path_service.h"
     16 #include "base/string_util.h"
     17 #include "base/test/test_timeouts.h"
     18 #include "base/threading/thread.h"
     19 #include "base/utf_string_conversions.h"
     20 #include "chrome/common/chrome_constants.h"
     21 #include "chrome/common/chrome_paths.h"
     22 #include "chrome/common/chrome_switches.h"
     23 #include "chrome/test/chrome_process_util.h"
     24 #include "chrome/test/ui/ui_test.h"
     25 #include "net/base/net_util.h"
     26 #include "testing/gtest/include/gtest/gtest.h"
     27 
     28 namespace {
     29 
     30 class ProcessSingletonLinuxTest : public UITest {
     31  public:
     32   virtual void SetUp() {
     33     UITest::SetUp();
     34     lock_path_ = user_data_dir().Append(chrome::kSingletonLockFilename);
     35     socket_path_ = user_data_dir().Append(chrome::kSingletonSocketFilename);
     36     cookie_path_ = user_data_dir().Append(chrome::kSingletonCookieFilename);
     37   }
     38 
     39   virtual void TearDown() {
     40     UITest::TearDown();
     41 
     42     // Check that the test cleaned up after itself.
     43     struct stat statbuf;
     44     bool lock_exists = lstat(lock_path_.value().c_str(), &statbuf) == 0;
     45     EXPECT_FALSE(lock_exists);
     46 
     47     if (lock_exists) {
     48       // Unlink to prevent failing future tests if the lock still exists.
     49       EXPECT_EQ(unlink(lock_path_.value().c_str()), 0);
     50     }
     51   }
     52 
     53   FilePath lock_path_;
     54   FilePath socket_path_;
     55   FilePath cookie_path_;
     56 };
     57 
     58 ProcessSingleton* CreateProcessSingleton() {
     59   FilePath user_data_dir;
     60   PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
     61 
     62   return new ProcessSingleton(user_data_dir);
     63 }
     64 
     65 CommandLine CommandLineForUrl(const std::string& url) {
     66   // Hack: mutate the current process's command line so we don't show a dialog.
     67   // Note that this only works if we have no loose values on the command line,
     68   // but that's fine for unit tests.  In a UI test we disable error dialogs
     69   // when spawning Chrome, but this test hits the ProcessSingleton directly.
     70   CommandLine* cmd_line = CommandLine::ForCurrentProcess();
     71   if (!cmd_line->HasSwitch(switches::kNoProcessSingletonDialog))
     72     cmd_line->AppendSwitch(switches::kNoProcessSingletonDialog);
     73 
     74   CommandLine new_cmd_line(*cmd_line);
     75   new_cmd_line.AppendArg(url);
     76   return new_cmd_line;
     77 }
     78 
     79 // A helper method to call ProcessSingleton::NotifyOtherProcess().
     80 // |url| will be added to CommandLine for current process, so that it can be
     81 // sent to browser process by ProcessSingleton::NotifyOtherProcess().
     82 ProcessSingleton::NotifyResult NotifyOtherProcess(const std::string& url,
     83                                                   int timeout_ms) {
     84   scoped_ptr<ProcessSingleton> process_singleton(CreateProcessSingleton());
     85   return process_singleton->NotifyOtherProcessWithTimeout(
     86       CommandLineForUrl(url), timeout_ms / 1000, true);
     87 }
     88 
     89 // A helper method to call ProcessSingleton::NotifyOtherProcessOrCreate().
     90 // |url| will be added to CommandLine for current process, so that it can be
     91 // sent to browser process by ProcessSingleton::NotifyOtherProcessOrCreate().
     92 ProcessSingleton::NotifyResult NotifyOtherProcessOrCreate(
     93     const std::string& url,
     94     int timeout_ms) {
     95   scoped_ptr<ProcessSingleton> process_singleton(CreateProcessSingleton());
     96   return process_singleton->NotifyOtherProcessWithTimeoutOrCreate(
     97       CommandLineForUrl(url), timeout_ms / 1000);
     98 }
     99 
    100 }  // namespace
    101 
    102 // Test if the socket file and symbol link created by ProcessSingletonLinux
    103 // are valid. When running this test, the ProcessSingleton object is already
    104 // initiated by UITest. So we just test against this existing object.
    105 // This test is flaky as per http://crbug.com/74554.
    106 TEST_F(ProcessSingletonLinuxTest, FLAKY_CheckSocketFile) {
    107   struct stat statbuf;
    108   ASSERT_EQ(0, lstat(lock_path_.value().c_str(), &statbuf));
    109   ASSERT_TRUE(S_ISLNK(statbuf.st_mode));
    110   char buf[PATH_MAX];
    111   ssize_t len = readlink(lock_path_.value().c_str(), buf, PATH_MAX);
    112   ASSERT_GT(len, 0);
    113 
    114   ASSERT_EQ(0, lstat(socket_path_.value().c_str(), &statbuf));
    115   ASSERT_TRUE(S_ISLNK(statbuf.st_mode));
    116 
    117   len = readlink(socket_path_.value().c_str(), buf, PATH_MAX);
    118   ASSERT_GT(len, 0);
    119   FilePath socket_target_path = FilePath(std::string(buf, len));
    120 
    121   ASSERT_EQ(0, lstat(socket_target_path.value().c_str(), &statbuf));
    122   ASSERT_TRUE(S_ISSOCK(statbuf.st_mode));
    123 
    124   len = readlink(cookie_path_.value().c_str(), buf, PATH_MAX);
    125   ASSERT_GT(len, 0);
    126   std::string cookie(buf, len);
    127 
    128   FilePath remote_cookie_path = socket_target_path.DirName().
    129       Append(chrome::kSingletonCookieFilename);
    130   len = readlink(remote_cookie_path.value().c_str(), buf, PATH_MAX);
    131   ASSERT_GT(len, 0);
    132   EXPECT_EQ(cookie, std::string(buf, len));
    133 }
    134 
    135 #if defined(OS_LINUX) && defined(TOOLKIT_VIEWS)
    136 // The following tests in linux/view does not pass without a window manager,
    137 // which is true in build/try bots.
    138 // See http://crbug.com/30953.
    139 #define NotifyOtherProcessSuccess FAILS_NotifyOtherProcessSuccess
    140 #define NotifyOtherProcessHostChanged FAILS_NotifyOtherProcessHostChanged
    141 #endif
    142 
    143 // TODO(james.su (at) gmail.com): port following tests to Windows.
    144 // Test success case of NotifyOtherProcess().
    145 TEST_F(ProcessSingletonLinuxTest, NotifyOtherProcessSuccess) {
    146   std::string url("about:blank");
    147   int original_tab_count = GetTabCount();
    148 
    149   EXPECT_EQ(ProcessSingleton::PROCESS_NOTIFIED,
    150             NotifyOtherProcess(url, TestTimeouts::action_timeout_ms()));
    151   EXPECT_EQ(original_tab_count + 1, GetTabCount());
    152   EXPECT_EQ(url, GetActiveTabURL().spec());
    153 }
    154 
    155 // Test failure case of NotifyOtherProcess().
    156 TEST_F(ProcessSingletonLinuxTest, NotifyOtherProcessFailure) {
    157   base::ProcessId pid = browser_process_id();
    158 
    159   ASSERT_GE(pid, 1);
    160 
    161   // Block the browser process, then it'll be killed by
    162   // ProcessSingleton::NotifyOtherProcess().
    163   kill(pid, SIGSTOP);
    164 
    165   // Wait to make sure the browser process is actually stopped.
    166   // It's necessary when running with valgrind.
    167   EXPECT_GE(HANDLE_EINTR(waitpid(pid, 0, WUNTRACED)), 0);
    168 
    169   std::string url("about:blank");
    170   EXPECT_EQ(ProcessSingleton::PROCESS_NONE,
    171             NotifyOtherProcess(url, TestTimeouts::action_timeout_ms()));
    172 
    173   // Wait for a while to make sure the browser process is actually killed.
    174   int exit_code = 0;
    175   ASSERT_TRUE(launcher_->WaitForBrowserProcessToQuit(
    176                   TestTimeouts::action_max_timeout_ms(), &exit_code));
    177   EXPECT_EQ(-1, exit_code);  // Expect unclean shutdown.
    178 }
    179 
    180 // Test that we don't kill ourselves by accident if a lockfile with the same pid
    181 // happens to exist.
    182 // TODO(mattm): This doesn't really need to be a uitest.  (We don't use the
    183 // uitest created browser process, but we do use some uitest provided stuff like
    184 // the user_data_dir and the NotifyOtherProcess function in this file, which
    185 // would have to be duplicated or shared if this test was moved into a
    186 // unittest.)
    187 TEST_F(ProcessSingletonLinuxTest, NotifyOtherProcessNoSuicide) {
    188   // Replace lockfile with one containing our own pid.
    189   EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
    190   std::string symlink_content = StringPrintf(
    191       "%s%c%u",
    192       net::GetHostName().c_str(),
    193       '-',
    194       base::GetCurrentProcId());
    195   EXPECT_EQ(0, symlink(symlink_content.c_str(), lock_path_.value().c_str()));
    196 
    197   // Remove socket so that we will not be able to notify the existing browser.
    198   EXPECT_EQ(0, unlink(socket_path_.value().c_str()));
    199 
    200   std::string url("about:blank");
    201   EXPECT_EQ(ProcessSingleton::PROCESS_NONE,
    202             NotifyOtherProcess(url, TestTimeouts::action_timeout_ms()));
    203   // If we've gotten to this point without killing ourself, the test succeeded.
    204 }
    205 
    206 // Test that we can still notify a process on the same host even after the
    207 // hostname changed.
    208 TEST_F(ProcessSingletonLinuxTest, NotifyOtherProcessHostChanged) {
    209   EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
    210   EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
    211 
    212   int original_tab_count = GetTabCount();
    213 
    214   std::string url("about:blank");
    215   EXPECT_EQ(ProcessSingleton::PROCESS_NOTIFIED,
    216             NotifyOtherProcess(url, TestTimeouts::action_timeout_ms()));
    217   EXPECT_EQ(original_tab_count + 1, GetTabCount());
    218   EXPECT_EQ(url, GetActiveTabURL().spec());
    219 }
    220 
    221 // Test that we fail when lock says process is on another host and we can't
    222 // notify it over the socket.
    223 TEST_F(ProcessSingletonLinuxTest, NotifyOtherProcessDifferingHost) {
    224   base::ProcessId pid = browser_process_id();
    225 
    226   ASSERT_GE(pid, 1);
    227 
    228   // Kill the browser process, so that it does not respond on the socket.
    229   kill(pid, SIGKILL);
    230   // Wait for a while to make sure the browser process is actually killed.
    231   int exit_code = 0;
    232   ASSERT_TRUE(launcher_->WaitForBrowserProcessToQuit(
    233                   TestTimeouts::action_max_timeout_ms(), &exit_code));
    234   EXPECT_EQ(-1, exit_code);  // Expect unclean shutdown.
    235 
    236   EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
    237   EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
    238 
    239   std::string url("about:blank");
    240   EXPECT_EQ(ProcessSingleton::PROFILE_IN_USE,
    241             NotifyOtherProcess(url, TestTimeouts::action_timeout_ms()));
    242 
    243   ASSERT_EQ(0, unlink(lock_path_.value().c_str()));
    244 }
    245 
    246 // Test that we fail when lock says process is on another host and we can't
    247 // notify it over the socket.
    248 TEST_F(ProcessSingletonLinuxTest, NotifyOtherProcessOrCreate_DifferingHost) {
    249   base::ProcessId pid = browser_process_id();
    250 
    251   ASSERT_GE(pid, 1);
    252 
    253   // Kill the browser process, so that it does not respond on the socket.
    254   kill(pid, SIGKILL);
    255   // Wait for a while to make sure the browser process is actually killed.
    256   int exit_code = 0;
    257   ASSERT_TRUE(launcher_->WaitForBrowserProcessToQuit(
    258                   TestTimeouts::action_max_timeout_ms(), &exit_code));
    259   EXPECT_EQ(-1, exit_code);  // Expect unclean shutdown.
    260 
    261   EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
    262   EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
    263 
    264   std::string url("about:blank");
    265   EXPECT_EQ(ProcessSingleton::PROFILE_IN_USE,
    266             NotifyOtherProcessOrCreate(url, TestTimeouts::action_timeout_ms()));
    267 
    268   ASSERT_EQ(0, unlink(lock_path_.value().c_str()));
    269 }
    270 
    271 // Test that Create fails when another browser is using the profile directory.
    272 TEST_F(ProcessSingletonLinuxTest, CreateFailsWithExistingBrowser) {
    273   scoped_ptr<ProcessSingleton> process_singleton(CreateProcessSingleton());
    274   EXPECT_FALSE(process_singleton->Create());
    275 }
    276 
    277 // Test that Create fails when another browser is using the profile directory
    278 // but with the old socket location.
    279 TEST_F(ProcessSingletonLinuxTest, CreateChecksCompatibilitySocket) {
    280   scoped_ptr<ProcessSingleton> process_singleton(CreateProcessSingleton());
    281 
    282   // Do some surgery so as to look like the old configuration.
    283   char buf[PATH_MAX];
    284   ssize_t len = readlink(socket_path_.value().c_str(), buf, sizeof(buf));
    285   ASSERT_GT(len, 0);
    286   FilePath socket_target_path = FilePath(std::string(buf, len));
    287   ASSERT_EQ(0, unlink(socket_path_.value().c_str()));
    288   ASSERT_EQ(0, rename(socket_target_path.value().c_str(),
    289                       socket_path_.value().c_str()));
    290   ASSERT_EQ(0, unlink(cookie_path_.value().c_str()));
    291 
    292   EXPECT_FALSE(process_singleton->Create());
    293 }
    294 
    295 // Test that we fail when lock says process is on another host and we can't
    296 // notify it over the socket before of a bad cookie.
    297 TEST_F(ProcessSingletonLinuxTest, NotifyOtherProcessOrCreate_BadCookie) {
    298   // Change the cookie.
    299   EXPECT_EQ(0, unlink(cookie_path_.value().c_str()));
    300   EXPECT_EQ(0, symlink("INCORRECTCOOKIE", cookie_path_.value().c_str()));
    301 
    302   // Also change the hostname, so the remote does not retry.
    303   EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
    304   EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
    305 
    306   std::string url("about:blank");
    307   EXPECT_EQ(ProcessSingleton::PROFILE_IN_USE,
    308             NotifyOtherProcessOrCreate(url, TestTimeouts::action_timeout_ms()));
    309 }
    310