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