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 
     14 #if !defined(OS_MACOSX)
     15 #include "base/at_exit.h"
     16 #include "base/memory/scoped_ptr.h"
     17 #include "base/strings/string_util.h"
     18 #include "base/strings/utf_string_conversions.h"
     19 #include "base/test/multiprocess_test.h"
     20 #include "base/test/test_timeouts.h"
     21 #include "base/threading/thread.h"
     22 #include "chrome/common/chrome_switches.h"
     23 #include "chrome/common/chrome_version_info.h"
     24 #include "testing/multiprocess_func_list.h"
     25 
     26 #if defined(OS_WIN)
     27 #include "base/win/win_util.h"
     28 #endif
     29 
     30 #if defined(OS_POSIX)
     31 #include <glib.h>
     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, false);
     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 TEST_F(ServiceProcessStateTest, ReadyState) {
    112   ASSERT_FALSE(CheckServiceProcessReady());
    113   ServiceProcessState state;
    114   ASSERT_TRUE(state.Initialize());
    115   ASSERT_TRUE(state.SignalReady(IOMessageLoopProxy(), base::Closure()));
    116   LaunchAndWait("ServiceProcessStateTestReadyTrue");
    117   state.SignalStopped();
    118   LaunchAndWait("ServiceProcessStateTestReadyFalse");
    119 }
    120 
    121 TEST_F(ServiceProcessStateTest, AutoRun) {
    122   ServiceProcessState state;
    123   ASSERT_TRUE(state.AddToAutoRun());
    124   scoped_ptr<CommandLine> autorun_command_line;
    125 #if defined(OS_WIN)
    126   std::string value_name = GetServiceProcessScopedName("_service_run");
    127   string16 value;
    128   EXPECT_TRUE(base::win::ReadCommandFromAutoRun(HKEY_CURRENT_USER,
    129                                                 UTF8ToWide(value_name),
    130                                                 &value));
    131   autorun_command_line.reset(new CommandLine(CommandLine::FromString(value)));
    132 #elif defined(OS_POSIX) && !defined(OS_MACOSX)
    133 #if defined(GOOGLE_CHROME_BUILD)
    134   std::string base_desktop_name = "google-chrome-service.desktop";
    135 #else  // CHROMIUM_BUILD
    136   std::string base_desktop_name = "chromium-service.desktop";
    137 #endif
    138   std::string exec_value;
    139   EXPECT_TRUE(AutoStart::GetAutostartFileValue(
    140       GetServiceProcessScopedName(base_desktop_name), "Exec", &exec_value));
    141   GError *error = NULL;
    142   gchar **argv = NULL;
    143   gint argc = 0;
    144   if (g_shell_parse_argv(exec_value.c_str(), &argc, &argv, &error)) {
    145     autorun_command_line.reset(new CommandLine(argc, argv));
    146     g_strfreev(argv);
    147   } else {
    148     ADD_FAILURE();
    149     g_error_free(error);
    150   }
    151 #endif  // defined(OS_WIN)
    152   if (autorun_command_line.get()) {
    153     EXPECT_EQ(autorun_command_line->GetSwitchValueASCII(switches::kProcessType),
    154               std::string(switches::kServiceProcess));
    155   }
    156   ASSERT_TRUE(state.RemoveFromAutoRun());
    157 #if defined(OS_WIN)
    158   EXPECT_FALSE(base::win::ReadCommandFromAutoRun(HKEY_CURRENT_USER,
    159                                                  UTF8ToWide(value_name),
    160                                                  &value));
    161 #elif defined(OS_POSIX) && !defined(OS_MACOSX)
    162   EXPECT_FALSE(AutoStart::GetAutostartFileValue(
    163       GetServiceProcessScopedName(base_desktop_name), "Exec", &exec_value));
    164 #endif  // defined(OS_WIN)
    165 }
    166 
    167 TEST_F(ServiceProcessStateTest, SharedMem) {
    168   std::string version;
    169   base::ProcessId pid;
    170 #if defined(OS_WIN)
    171   // On Posix, named shared memory uses a file on disk. This file
    172   // could be lying around from previous crashes which could cause
    173   // GetServiceProcessPid to lie. On Windows, we use a named event so we
    174   // don't have this issue. Until we have a more stable shared memory
    175   // implementation on Posix, this check will only execute on Windows.
    176   ASSERT_FALSE(GetServiceProcessData(&version, &pid));
    177 #endif  // defined(OS_WIN)
    178   ServiceProcessState state;
    179   ASSERT_TRUE(state.Initialize());
    180   ASSERT_TRUE(GetServiceProcessData(&version, &pid));
    181   ASSERT_EQ(base::GetCurrentProcId(), pid);
    182 }
    183 
    184 TEST_F(ServiceProcessStateTest, MAYBE_ForceShutdown) {
    185   base::ProcessHandle handle = SpawnChild("ServiceProcessStateTestShutdown",
    186                                           true);
    187   ASSERT_TRUE(handle);
    188   for (int i = 0; !CheckServiceProcessReady() && i < 10; ++i) {
    189     base::PlatformThread::Sleep(TestTimeouts::tiny_timeout());
    190   }
    191   ASSERT_TRUE(CheckServiceProcessReady());
    192   std::string version;
    193   base::ProcessId pid;
    194   ASSERT_TRUE(GetServiceProcessData(&version, &pid));
    195   ASSERT_TRUE(ForceServiceProcessShutdown(version, pid));
    196   int exit_code = 0;
    197   ASSERT_TRUE(base::WaitForExitCodeWithTimeout(handle,
    198       &exit_code, TestTimeouts::action_max_timeout()));
    199   base::CloseProcessHandle(handle);
    200   ASSERT_EQ(exit_code, 0);
    201 }
    202 
    203 MULTIPROCESS_TEST_MAIN(ServiceProcessStateTestSingleton) {
    204   ServiceProcessState state;
    205   EXPECT_FALSE(state.Initialize());
    206   return 0;
    207 }
    208 
    209 MULTIPROCESS_TEST_MAIN(ServiceProcessStateTestReadyTrue) {
    210   EXPECT_TRUE(CheckServiceProcessReady());
    211   return 0;
    212 }
    213 
    214 MULTIPROCESS_TEST_MAIN(ServiceProcessStateTestReadyFalse) {
    215   EXPECT_FALSE(CheckServiceProcessReady());
    216   return 0;
    217 }
    218 
    219 MULTIPROCESS_TEST_MAIN(ServiceProcessStateTestShutdown) {
    220   base::MessageLoop message_loop;
    221   message_loop.set_thread_name("ServiceProcessStateTestShutdownMainThread");
    222   base::Thread io_thread_("ServiceProcessStateTestShutdownIOThread");
    223   base::Thread::Options options(base::MessageLoop::TYPE_IO, 0);
    224   EXPECT_TRUE(io_thread_.StartWithOptions(options));
    225   ServiceProcessState state;
    226   EXPECT_TRUE(state.Initialize());
    227   EXPECT_TRUE(state.SignalReady(
    228       io_thread_.message_loop_proxy().get(),
    229       base::Bind(&ShutdownTask, base::MessageLoop::current())));
    230   message_loop.PostDelayedTask(FROM_HERE,
    231                                base::MessageLoop::QuitClosure(),
    232                                TestTimeouts::action_max_timeout());
    233   EXPECT_FALSE(g_good_shutdown);
    234   message_loop.Run();
    235   EXPECT_TRUE(g_good_shutdown);
    236   return 0;
    237 }
    238 
    239 #else  // !OS_MACOSX
    240 
    241 #include <CoreFoundation/CoreFoundation.h>
    242 
    243 #include "base/file_util.h"
    244 #include "base/files/file_path.h"
    245 #include "base/files/scoped_temp_dir.h"
    246 #include "base/mac/mac_util.h"
    247 #include "base/test/test_timeouts.h"
    248 #include "base/threading/thread.h"
    249 #include "chrome/common/mac/launchd.h"
    250 #include "chrome/common/mac/mock_launchd.h"
    251 #include "testing/gtest/include/gtest/gtest.h"
    252 
    253 class ServiceProcessStateFileManipulationTest : public ::testing::Test {
    254  protected:
    255   ServiceProcessStateFileManipulationTest()
    256       : io_thread_("ServiceProcessStateFileManipulationTest_IO") {
    257   }
    258   virtual ~ServiceProcessStateFileManipulationTest() { }
    259 
    260   virtual void SetUp() {
    261     base::Thread::Options options;
    262     options.message_loop_type = base::MessageLoop::TYPE_IO;
    263     ASSERT_TRUE(io_thread_.StartWithOptions(options));
    264     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
    265     ASSERT_TRUE(MockLaunchd::MakeABundle(GetTempDirPath(),
    266                                          "Test",
    267                                          &bundle_path_,
    268                                          &executable_path_));
    269     mock_launchd_.reset(new MockLaunchd(executable_path_, &loop_,
    270                                         false, false));
    271     scoped_launchd_instance_.reset(
    272         new Launchd::ScopedInstance(mock_launchd_.get()));
    273     ASSERT_TRUE(service_process_state_.Initialize());
    274     ASSERT_TRUE(service_process_state_.SignalReady(
    275         io_thread_.message_loop_proxy().get(), base::Closure()));
    276     loop_.PostDelayedTask(FROM_HERE,
    277                           base::MessageLoop::QuitClosure(),
    278                           TestTimeouts::action_max_timeout());
    279   }
    280 
    281   const MockLaunchd* mock_launchd() const { return mock_launchd_.get(); }
    282   const base::FilePath& executable_path() const { return executable_path_; }
    283   const base::FilePath& bundle_path() const { return bundle_path_; }
    284   const base::FilePath& GetTempDirPath() const { return temp_dir_.path(); }
    285 
    286   base::MessageLoopProxy* GetIOMessageLoopProxy() {
    287     return io_thread_.message_loop_proxy().get();
    288   }
    289   void Run() { loop_.Run(); }
    290 
    291  private:
    292   base::ScopedTempDir temp_dir_;
    293   base::MessageLoopForUI loop_;
    294   base::Thread io_thread_;
    295   base::FilePath executable_path_, bundle_path_;
    296   scoped_ptr<MockLaunchd> mock_launchd_;
    297   scoped_ptr<Launchd::ScopedInstance> scoped_launchd_instance_;
    298   ServiceProcessState service_process_state_;
    299 };
    300 
    301 void DeleteFunc(const base::FilePath& file) {
    302   EXPECT_TRUE(base::DeleteFile(file, true));
    303 }
    304 
    305 void MoveFunc(const base::FilePath& from, const base::FilePath& to) {
    306   EXPECT_TRUE(base::Move(from, to));
    307 }
    308 
    309 void ChangeAttr(const base::FilePath& from, int mode) {
    310   EXPECT_EQ(chmod(from.value().c_str(), mode), 0);
    311 }
    312 
    313 class ScopedAttributesRestorer {
    314  public:
    315   ScopedAttributesRestorer(const base::FilePath& path, int mode)
    316       : path_(path), mode_(mode) {
    317   }
    318   ~ScopedAttributesRestorer() {
    319     ChangeAttr(path_, mode_);
    320   }
    321  private:
    322   base::FilePath path_;
    323   int mode_;
    324 };
    325 
    326 void TrashFunc(const base::FilePath& src) {
    327   FSRef path_ref;
    328   FSRef new_path_ref;
    329   EXPECT_TRUE(base::mac::FSRefFromPath(src.value(), &path_ref));
    330   OSStatus status = FSMoveObjectToTrashSync(&path_ref,
    331                                             &new_path_ref,
    332                                             kFSFileOperationDefaultOptions);
    333   EXPECT_EQ(status, noErr) << "FSMoveObjectToTrashSync " << status;
    334 }
    335 
    336 TEST_F(ServiceProcessStateFileManipulationTest, VerifyLaunchD) {
    337   // There have been problems where launchd has gotten into a bad state, usually
    338   // because something had deleted all the files in /tmp. launchd depends on
    339   // a Unix Domain Socket that it creates at /tmp/launchd*/sock.
    340   // The symptom of this problem is that the service process connect fails
    341   // on Mac and "launch_msg(): Socket is not connected" appears.
    342   // This test is designed to make sure that launchd is working.
    343   // http://crbug/75518
    344 
    345   CommandLine cl(base::FilePath("/bin/launchctl"));
    346   cl.AppendArg("list");
    347   cl.AppendArg("com.apple.launchctl.Aqua");
    348 
    349   std::string output;
    350   int exit_code = -1;
    351   ASSERT_TRUE(base::GetAppOutputWithExitCode(cl, &output, &exit_code)
    352               && exit_code == 0)
    353       << " exit_code:" << exit_code << " " << output;
    354 }
    355 
    356 TEST_F(ServiceProcessStateFileManipulationTest, DeleteFile) {
    357   GetIOMessageLoopProxy()->PostTask(
    358       FROM_HERE,
    359       base::Bind(&DeleteFunc, executable_path()));
    360   Run();
    361   ASSERT_TRUE(mock_launchd()->remove_called());
    362   ASSERT_TRUE(mock_launchd()->delete_called());
    363 }
    364 
    365 TEST_F(ServiceProcessStateFileManipulationTest, DeleteBundle) {
    366   GetIOMessageLoopProxy()->PostTask(
    367       FROM_HERE,
    368       base::Bind(&DeleteFunc, bundle_path()));
    369   Run();
    370   ASSERT_TRUE(mock_launchd()->remove_called());
    371   ASSERT_TRUE(mock_launchd()->delete_called());
    372 }
    373 
    374 TEST_F(ServiceProcessStateFileManipulationTest, MoveBundle) {
    375   base::FilePath new_loc = GetTempDirPath().AppendASCII("MoveBundle");
    376   GetIOMessageLoopProxy()->PostTask(
    377       FROM_HERE,
    378       base::Bind(&MoveFunc, bundle_path(), new_loc));
    379   Run();
    380   ASSERT_TRUE(mock_launchd()->restart_called());
    381   ASSERT_TRUE(mock_launchd()->write_called());
    382 }
    383 
    384 TEST_F(ServiceProcessStateFileManipulationTest, MoveFile) {
    385   base::FilePath new_loc = GetTempDirPath().AppendASCII("MoveFile");
    386   GetIOMessageLoopProxy()->PostTask(
    387       FROM_HERE,
    388       base::Bind(&MoveFunc, executable_path(), new_loc));
    389   Run();
    390   ASSERT_TRUE(mock_launchd()->remove_called());
    391   ASSERT_TRUE(mock_launchd()->delete_called());
    392 }
    393 
    394 TEST_F(ServiceProcessStateFileManipulationTest, TrashBundle) {
    395   FSRef bundle_ref;
    396   ASSERT_TRUE(base::mac::FSRefFromPath(bundle_path().value(), &bundle_ref));
    397   GetIOMessageLoopProxy()->PostTask(
    398       FROM_HERE,
    399       base::Bind(&TrashFunc, bundle_path()));
    400   Run();
    401   ASSERT_TRUE(mock_launchd()->remove_called());
    402   ASSERT_TRUE(mock_launchd()->delete_called());
    403   std::string path(base::mac::PathFromFSRef(bundle_ref));
    404   base::FilePath file_path(path);
    405   ASSERT_TRUE(base::DeleteFile(file_path, true));
    406 }
    407 
    408 TEST_F(ServiceProcessStateFileManipulationTest, ChangeAttr) {
    409   ScopedAttributesRestorer restorer(bundle_path(), 0777);
    410   GetIOMessageLoopProxy()->PostTask(
    411       FROM_HERE,
    412       base::Bind(&ChangeAttr, bundle_path(), 0222));
    413   Run();
    414   ASSERT_TRUE(mock_launchd()->remove_called());
    415   ASSERT_TRUE(mock_launchd()->delete_called());
    416 }
    417 
    418 #endif  // !OS_MACOSX
    419