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