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