Home | History | Annotate | Download | only in common
      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/common/service_process_util.h"
      6 
      7 #include "base/basictypes.h"
      8 #include "base/bind.h"
      9 #include "base/command_line.h"
     10 #include "base/files/file_path.h"
     11 #include "base/process/kill.h"
     12 #include "base/process/launch.h"
     13 #include "base/strings/string_split.h"
     14 
     15 #if !defined(OS_MACOSX)
     16 #include "base/at_exit.h"
     17 #include "base/memory/scoped_ptr.h"
     18 #include "base/strings/string_util.h"
     19 #include "base/strings/utf_string_conversions.h"
     20 #include "base/test/multiprocess_test.h"
     21 #include "base/test/test_timeouts.h"
     22 #include "base/threading/thread.h"
     23 #include "chrome/common/chrome_switches.h"
     24 #include "chrome/common/chrome_version_info.h"
     25 #include "testing/multiprocess_func_list.h"
     26 
     27 #if defined(OS_WIN)
     28 #include "base/win/win_util.h"
     29 #endif
     30 
     31 #if defined(OS_POSIX)
     32 #include "chrome/common/auto_start_linux.h"
     33 #endif
     34 
     35 #if defined(USE_AURA)
     36 // This test fails http://crbug.com/84854, and is very flaky on CrOS and
     37 // somewhat flaky on other Linux.
     38 #define MAYBE_ForceShutdown DISABLED_ForceShutdown
     39 #else
     40 #if defined(OS_LINUX) || defined(OS_WIN)
     41 #define MAYBE_ForceShutdown DISABLED_ForceShutdown
     42 #else
     43 #define MAYBE_ForceShutdown ForceShutdown
     44 #endif
     45 #endif
     46 
     47 namespace {
     48 
     49 bool g_good_shutdown = false;
     50 
     51 void ShutdownTask(base::MessageLoop* loop) {
     52   // Quit the main message loop.
     53   ASSERT_FALSE(g_good_shutdown);
     54   g_good_shutdown = true;
     55   loop->PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
     56 }
     57 
     58 }  // namespace
     59 
     60 TEST(ServiceProcessUtilTest, ScopedVersionedName) {
     61   std::string test_str = "test";
     62   std::string scoped_name = GetServiceProcessScopedVersionedName(test_str);
     63   chrome::VersionInfo version_info;
     64   DCHECK(version_info.is_valid());
     65   EXPECT_TRUE(EndsWith(scoped_name, test_str, true));
     66   EXPECT_NE(std::string::npos, scoped_name.find(version_info.Version()));
     67 }
     68 
     69 class ServiceProcessStateTest : public base::MultiProcessTest {
     70  public:
     71   ServiceProcessStateTest();
     72   virtual ~ServiceProcessStateTest();
     73   virtual void SetUp();
     74   base::MessageLoopProxy* IOMessageLoopProxy() {
     75     return io_thread_.message_loop_proxy().get();
     76   }
     77   void LaunchAndWait(const std::string& name);
     78 
     79  private:
     80   // This is used to release the ServiceProcessState singleton after each test.
     81   base::ShadowingAtExitManager at_exit_manager_;
     82   base::Thread io_thread_;
     83 };
     84 
     85 ServiceProcessStateTest::ServiceProcessStateTest()
     86     : io_thread_("ServiceProcessStateTestThread") {
     87 }
     88 
     89 ServiceProcessStateTest::~ServiceProcessStateTest() {
     90 }
     91 
     92 void ServiceProcessStateTest::SetUp() {
     93   base::Thread::Options options(base::MessageLoop::TYPE_IO, 0);
     94   ASSERT_TRUE(io_thread_.StartWithOptions(options));
     95 }
     96 
     97 void ServiceProcessStateTest::LaunchAndWait(const std::string& name) {
     98   base::ProcessHandle handle = SpawnChild(name);
     99   ASSERT_TRUE(handle);
    100   int exit_code = 0;
    101   ASSERT_TRUE(base::WaitForExitCode(handle, &exit_code));
    102   ASSERT_EQ(exit_code, 0);
    103 }
    104 
    105 TEST_F(ServiceProcessStateTest, Singleton) {
    106   ServiceProcessState state;
    107   ASSERT_TRUE(state.Initialize());
    108   LaunchAndWait("ServiceProcessStateTestSingleton");
    109 }
    110 
    111 // http://crbug.com/396390
    112 TEST_F(ServiceProcessStateTest, DISABLED_ReadyState) {
    113   ASSERT_FALSE(CheckServiceProcessReady());
    114   ServiceProcessState state;
    115   ASSERT_TRUE(state.Initialize());
    116   ASSERT_TRUE(state.SignalReady(IOMessageLoopProxy(), base::Closure()));
    117   LaunchAndWait("ServiceProcessStateTestReadyTrue");
    118   state.SignalStopped();
    119   LaunchAndWait("ServiceProcessStateTestReadyFalse");
    120 }
    121 
    122 TEST_F(ServiceProcessStateTest, AutoRun) {
    123   ServiceProcessState state;
    124   ASSERT_TRUE(state.AddToAutoRun());
    125   scoped_ptr<CommandLine> autorun_command_line;
    126 #if defined(OS_WIN)
    127   std::string value_name = GetServiceProcessScopedName("_service_run");
    128   base::string16 value;
    129   EXPECT_TRUE(base::win::ReadCommandFromAutoRun(HKEY_CURRENT_USER,
    130                                                 base::UTF8ToWide(value_name),
    131                                                 &value));
    132   autorun_command_line.reset(new CommandLine(CommandLine::FromString(value)));
    133 #elif defined(OS_POSIX) && !defined(OS_MACOSX)
    134 #if defined(GOOGLE_CHROME_BUILD)
    135   std::string base_desktop_name = "google-chrome-service.desktop";
    136 #else  // CHROMIUM_BUILD
    137   std::string base_desktop_name = "chromium-service.desktop";
    138 #endif
    139   std::string exec_value;
    140   EXPECT_TRUE(AutoStart::GetAutostartFileValue(
    141       GetServiceProcessScopedName(base_desktop_name), "Exec", &exec_value));
    142 
    143   // Make sure |exec_value| doesn't contain strings a shell would
    144   // treat specially.
    145   ASSERT_EQ(std::string::npos, exec_value.find('#'));
    146   ASSERT_EQ(std::string::npos, exec_value.find('\n'));
    147   ASSERT_EQ(std::string::npos, exec_value.find('"'));
    148   ASSERT_EQ(std::string::npos, exec_value.find('\''));
    149 
    150   CommandLine::StringVector argv;
    151   base::SplitString(exec_value, ' ', &argv);
    152   ASSERT_GE(argv.size(), 2U)
    153       << "Expected at least one command-line option in: " << exec_value;
    154   autorun_command_line.reset(new CommandLine(argv));
    155 #endif  // defined(OS_WIN)
    156   if (autorun_command_line.get()) {
    157     EXPECT_EQ(autorun_command_line->GetSwitchValueASCII(switches::kProcessType),
    158               std::string(switches::kServiceProcess));
    159   }
    160   ASSERT_TRUE(state.RemoveFromAutoRun());
    161 #if defined(OS_WIN)
    162   EXPECT_FALSE(base::win::ReadCommandFromAutoRun(HKEY_CURRENT_USER,
    163                                                  base::UTF8ToWide(value_name),
    164                                                  &value));
    165 #elif defined(OS_POSIX) && !defined(OS_MACOSX)
    166   EXPECT_FALSE(AutoStart::GetAutostartFileValue(
    167       GetServiceProcessScopedName(base_desktop_name), "Exec", &exec_value));
    168 #endif  // defined(OS_WIN)
    169 }
    170 
    171 // http://crbug.com/396390
    172 TEST_F(ServiceProcessStateTest, DISABLED_SharedMem) {
    173   std::string version;
    174   base::ProcessId pid;
    175 #if defined(OS_WIN)
    176   // On Posix, named shared memory uses a file on disk. This file
    177   // could be lying around from previous crashes which could cause
    178   // GetServiceProcessPid to lie. On Windows, we use a named event so we
    179   // don't have this issue. Until we have a more stable shared memory
    180   // implementation on Posix, this check will only execute on Windows.
    181   ASSERT_FALSE(GetServiceProcessData(&version, &pid));
    182 #endif  // defined(OS_WIN)
    183   ServiceProcessState state;
    184   ASSERT_TRUE(state.Initialize());
    185   ASSERT_TRUE(GetServiceProcessData(&version, &pid));
    186   ASSERT_EQ(base::GetCurrentProcId(), pid);
    187 }
    188 
    189 TEST_F(ServiceProcessStateTest, MAYBE_ForceShutdown) {
    190   base::ProcessHandle handle = SpawnChild("ServiceProcessStateTestShutdown");
    191   ASSERT_TRUE(handle);
    192   for (int i = 0; !CheckServiceProcessReady() && i < 10; ++i) {
    193     base::PlatformThread::Sleep(TestTimeouts::tiny_timeout());
    194   }
    195   ASSERT_TRUE(CheckServiceProcessReady());
    196   std::string version;
    197   base::ProcessId pid;
    198   ASSERT_TRUE(GetServiceProcessData(&version, &pid));
    199   ASSERT_TRUE(ForceServiceProcessShutdown(version, pid));
    200   int exit_code = 0;
    201   ASSERT_TRUE(base::WaitForExitCodeWithTimeout(handle,
    202       &exit_code, TestTimeouts::action_max_timeout()));
    203   base::CloseProcessHandle(handle);
    204   ASSERT_EQ(exit_code, 0);
    205 }
    206 
    207 MULTIPROCESS_TEST_MAIN(ServiceProcessStateTestSingleton) {
    208   ServiceProcessState state;
    209   EXPECT_FALSE(state.Initialize());
    210   return 0;
    211 }
    212 
    213 MULTIPROCESS_TEST_MAIN(ServiceProcessStateTestReadyTrue) {
    214   EXPECT_TRUE(CheckServiceProcessReady());
    215   return 0;
    216 }
    217 
    218 MULTIPROCESS_TEST_MAIN(ServiceProcessStateTestReadyFalse) {
    219   EXPECT_FALSE(CheckServiceProcessReady());
    220   return 0;
    221 }
    222 
    223 MULTIPROCESS_TEST_MAIN(ServiceProcessStateTestShutdown) {
    224   base::MessageLoop message_loop;
    225   message_loop.set_thread_name("ServiceProcessStateTestShutdownMainThread");
    226   base::Thread io_thread_("ServiceProcessStateTestShutdownIOThread");
    227   base::Thread::Options options(base::MessageLoop::TYPE_IO, 0);
    228   EXPECT_TRUE(io_thread_.StartWithOptions(options));
    229   ServiceProcessState state;
    230   EXPECT_TRUE(state.Initialize());
    231   EXPECT_TRUE(state.SignalReady(
    232       io_thread_.message_loop_proxy().get(),
    233       base::Bind(&ShutdownTask, base::MessageLoop::current())));
    234   message_loop.PostDelayedTask(FROM_HERE,
    235                                base::MessageLoop::QuitClosure(),
    236                                TestTimeouts::action_max_timeout());
    237   EXPECT_FALSE(g_good_shutdown);
    238   message_loop.Run();
    239   EXPECT_TRUE(g_good_shutdown);
    240   return 0;
    241 }
    242 
    243 #else  // !OS_MACOSX
    244 
    245 #include <CoreFoundation/CoreFoundation.h>
    246 
    247 #include "base/files/file_path.h"
    248 #include "base/files/file_util.h"
    249 #include "base/files/scoped_temp_dir.h"
    250 #include "base/mac/mac_util.h"
    251 #include "base/test/test_timeouts.h"
    252 #include "base/threading/thread.h"
    253 #include "chrome/common/mac/launchd.h"
    254 #include "chrome/common/mac/mock_launchd.h"
    255 #include "testing/gtest/include/gtest/gtest.h"
    256 
    257 class ServiceProcessStateFileManipulationTest : public ::testing::Test {
    258  protected:
    259   ServiceProcessStateFileManipulationTest()
    260       : io_thread_("ServiceProcessStateFileManipulationTest_IO") {
    261   }
    262   virtual ~ServiceProcessStateFileManipulationTest() { }
    263 
    264   virtual void SetUp() {
    265     base::Thread::Options options;
    266     options.message_loop_type = base::MessageLoop::TYPE_IO;
    267     ASSERT_TRUE(io_thread_.StartWithOptions(options));
    268     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
    269     ASSERT_TRUE(MockLaunchd::MakeABundle(GetTempDirPath(),
    270                                          "Test",
    271                                          &bundle_path_,
    272                                          &executable_path_));
    273     mock_launchd_.reset(new MockLaunchd(executable_path_, &loop_,
    274                                         false, false));
    275     scoped_launchd_instance_.reset(
    276         new Launchd::ScopedInstance(mock_launchd_.get()));
    277     ASSERT_TRUE(service_process_state_.Initialize());
    278     ASSERT_TRUE(service_process_state_.SignalReady(
    279         io_thread_.message_loop_proxy().get(), base::Closure()));
    280     loop_.PostDelayedTask(FROM_HERE,
    281                           base::MessageLoop::QuitClosure(),
    282                           TestTimeouts::action_max_timeout());
    283   }
    284 
    285   const MockLaunchd* mock_launchd() const { return mock_launchd_.get(); }
    286   const base::FilePath& executable_path() const { return executable_path_; }
    287   const base::FilePath& bundle_path() const { return bundle_path_; }
    288   const base::FilePath& GetTempDirPath() const { return temp_dir_.path(); }
    289 
    290   base::MessageLoopProxy* GetIOMessageLoopProxy() {
    291     return io_thread_.message_loop_proxy().get();
    292   }
    293   void Run() { loop_.Run(); }
    294 
    295  private:
    296   base::ScopedTempDir temp_dir_;
    297   base::MessageLoopForUI loop_;
    298   base::Thread io_thread_;
    299   base::FilePath executable_path_, bundle_path_;
    300   scoped_ptr<MockLaunchd> mock_launchd_;
    301   scoped_ptr<Launchd::ScopedInstance> scoped_launchd_instance_;
    302   ServiceProcessState service_process_state_;
    303 };
    304 
    305 void DeleteFunc(const base::FilePath& file) {
    306   EXPECT_TRUE(base::DeleteFile(file, true));
    307 }
    308 
    309 void MoveFunc(const base::FilePath& from, const base::FilePath& to) {
    310   EXPECT_TRUE(base::Move(from, to));
    311 }
    312 
    313 void ChangeAttr(const base::FilePath& from, int mode) {
    314   EXPECT_EQ(chmod(from.value().c_str(), mode), 0);
    315 }
    316 
    317 class ScopedAttributesRestorer {
    318  public:
    319   ScopedAttributesRestorer(const base::FilePath& path, int mode)
    320       : path_(path), mode_(mode) {
    321   }
    322   ~ScopedAttributesRestorer() {
    323     ChangeAttr(path_, mode_);
    324   }
    325  private:
    326   base::FilePath path_;
    327   int mode_;
    328 };
    329 
    330 void TrashFunc(const base::FilePath& src) {
    331   FSRef path_ref;
    332   FSRef new_path_ref;
    333   EXPECT_TRUE(base::mac::FSRefFromPath(src.value(), &path_ref));
    334   OSStatus status = FSMoveObjectToTrashSync(&path_ref,
    335                                             &new_path_ref,
    336                                             kFSFileOperationDefaultOptions);
    337   EXPECT_EQ(status, noErr) << "FSMoveObjectToTrashSync " << status;
    338 }
    339 
    340 TEST_F(ServiceProcessStateFileManipulationTest, VerifyLaunchD) {
    341   // There have been problems where launchd has gotten into a bad state, usually
    342   // because something had deleted all the files in /tmp. launchd depends on
    343   // a Unix Domain Socket that it creates at /tmp/launchd*/sock.
    344   // The symptom of this problem is that the service process connect fails
    345   // on Mac and "launch_msg(): Socket is not connected" appears.
    346   // This test is designed to make sure that launchd is working.
    347   // http://crbug/75518
    348   // Note: This particular problem no longer affects launchd in 10.10+, since
    349   // there is no user owned launchd process and sockets are no longer made at
    350   // /tmp/launchd*/sock. This test is still useful as a sanity check to make
    351   // sure that launchd appears to be working.
    352 
    353   CommandLine cl(base::FilePath("/bin/launchctl"));
    354   cl.AppendArg("limit");
    355 
    356   std::string output;
    357   int exit_code = -1;
    358   ASSERT_TRUE(base::GetAppOutputWithExitCode(cl, &output, &exit_code)
    359               && exit_code == 0)
    360       << " exit_code:" << exit_code << " " << output;
    361 }
    362 
    363 TEST_F(ServiceProcessStateFileManipulationTest, DeleteFile) {
    364   GetIOMessageLoopProxy()->PostTask(
    365       FROM_HERE,
    366       base::Bind(&DeleteFunc, executable_path()));
    367   Run();
    368   ASSERT_TRUE(mock_launchd()->remove_called());
    369   ASSERT_TRUE(mock_launchd()->delete_called());
    370 }
    371 
    372 TEST_F(ServiceProcessStateFileManipulationTest, DeleteBundle) {
    373   GetIOMessageLoopProxy()->PostTask(
    374       FROM_HERE,
    375       base::Bind(&DeleteFunc, bundle_path()));
    376   Run();
    377   ASSERT_TRUE(mock_launchd()->remove_called());
    378   ASSERT_TRUE(mock_launchd()->delete_called());
    379 }
    380 
    381 TEST_F(ServiceProcessStateFileManipulationTest, MoveBundle) {
    382   base::FilePath new_loc = GetTempDirPath().AppendASCII("MoveBundle");
    383   GetIOMessageLoopProxy()->PostTask(
    384       FROM_HERE,
    385       base::Bind(&MoveFunc, bundle_path(), new_loc));
    386   Run();
    387   ASSERT_TRUE(mock_launchd()->restart_called());
    388   ASSERT_TRUE(mock_launchd()->write_called());
    389 }
    390 
    391 TEST_F(ServiceProcessStateFileManipulationTest, MoveFile) {
    392   base::FilePath new_loc = GetTempDirPath().AppendASCII("MoveFile");
    393   GetIOMessageLoopProxy()->PostTask(
    394       FROM_HERE,
    395       base::Bind(&MoveFunc, executable_path(), new_loc));
    396   Run();
    397   ASSERT_TRUE(mock_launchd()->remove_called());
    398   ASSERT_TRUE(mock_launchd()->delete_called());
    399 }
    400 
    401 TEST_F(ServiceProcessStateFileManipulationTest, TrashBundle) {
    402   FSRef bundle_ref;
    403   ASSERT_TRUE(base::mac::FSRefFromPath(bundle_path().value(), &bundle_ref));
    404   GetIOMessageLoopProxy()->PostTask(
    405       FROM_HERE,
    406       base::Bind(&TrashFunc, bundle_path()));
    407   Run();
    408   ASSERT_TRUE(mock_launchd()->remove_called());
    409   ASSERT_TRUE(mock_launchd()->delete_called());
    410   std::string path(base::mac::PathFromFSRef(bundle_ref));
    411   base::FilePath file_path(path);
    412   ASSERT_TRUE(base::DeleteFile(file_path, true));
    413 }
    414 
    415 TEST_F(ServiceProcessStateFileManipulationTest, ChangeAttr) {
    416   ScopedAttributesRestorer restorer(bundle_path(), 0777);
    417   GetIOMessageLoopProxy()->PostTask(
    418       FROM_HERE,
    419       base::Bind(&ChangeAttr, bundle_path(), 0222));
    420   Run();
    421   ASSERT_TRUE(mock_launchd()->remove_called());
    422   ASSERT_TRUE(mock_launchd()->delete_called());
    423 }
    424 
    425 #endif  // !OS_MACOSX
    426