• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ShutdownTask(base::MessageLoop * loop)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 
TEST(ServiceProcessUtilTest,ScopedVersionedName)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();
IOMessageLoopProxy()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 
ServiceProcessStateTest()85 ServiceProcessStateTest::ServiceProcessStateTest()
86     : io_thread_("ServiceProcessStateTestThread") {
87 }
88 
~ServiceProcessStateTest()89 ServiceProcessStateTest::~ServiceProcessStateTest() {
90 }
91 
SetUp()92 void ServiceProcessStateTest::SetUp() {
93   base::Thread::Options options(base::MessageLoop::TYPE_IO, 0);
94   ASSERT_TRUE(io_thread_.StartWithOptions(options));
95 }
96 
LaunchAndWait(const std::string & name)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 
TEST_F(ServiceProcessStateTest,Singleton)105 TEST_F(ServiceProcessStateTest, Singleton) {
106   ServiceProcessState state;
107   ASSERT_TRUE(state.Initialize());
108   LaunchAndWait("ServiceProcessStateTestSingleton");
109 }
110 
TEST_F(ServiceProcessStateTest,ReadyState)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 
TEST_F(ServiceProcessStateTest,AutoRun)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   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 
TEST_F(ServiceProcessStateTest,SharedMem)170 TEST_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 
TEST_F(ServiceProcessStateTest,MAYBE_ForceShutdown)187 TEST_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 
MULTIPROCESS_TEST_MAIN(ServiceProcessStateTestSingleton)205 MULTIPROCESS_TEST_MAIN(ServiceProcessStateTestSingleton) {
206   ServiceProcessState state;
207   EXPECT_FALSE(state.Initialize());
208   return 0;
209 }
210 
MULTIPROCESS_TEST_MAIN(ServiceProcessStateTestReadyTrue)211 MULTIPROCESS_TEST_MAIN(ServiceProcessStateTestReadyTrue) {
212   EXPECT_TRUE(CheckServiceProcessReady());
213   return 0;
214 }
215 
MULTIPROCESS_TEST_MAIN(ServiceProcessStateTestReadyFalse)216 MULTIPROCESS_TEST_MAIN(ServiceProcessStateTestReadyFalse) {
217   EXPECT_FALSE(CheckServiceProcessReady());
218   return 0;
219 }
220 
MULTIPROCESS_TEST_MAIN(ServiceProcessStateTestShutdown)221 MULTIPROCESS_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 
255 class ServiceProcessStateFileManipulationTest : public ::testing::Test {
256  protected:
ServiceProcessStateFileManipulationTest()257   ServiceProcessStateFileManipulationTest()
258       : io_thread_("ServiceProcessStateFileManipulationTest_IO") {
259   }
~ServiceProcessStateFileManipulationTest()260   virtual ~ServiceProcessStateFileManipulationTest() { }
261 
SetUp()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 
mock_launchd() const283   const MockLaunchd* mock_launchd() const { return mock_launchd_.get(); }
executable_path() const284   const base::FilePath& executable_path() const { return executable_path_; }
bundle_path() const285   const base::FilePath& bundle_path() const { return bundle_path_; }
GetTempDirPath() const286   const base::FilePath& GetTempDirPath() const { return temp_dir_.path(); }
287 
GetIOMessageLoopProxy()288   base::MessageLoopProxy* GetIOMessageLoopProxy() {
289     return io_thread_.message_loop_proxy().get();
290   }
Run()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 
DeleteFunc(const base::FilePath & file)303 void DeleteFunc(const base::FilePath& file) {
304   EXPECT_TRUE(base::DeleteFile(file, true));
305 }
306 
MoveFunc(const base::FilePath & from,const base::FilePath & to)307 void MoveFunc(const base::FilePath& from, const base::FilePath& to) {
308   EXPECT_TRUE(base::Move(from, to));
309 }
310 
ChangeAttr(const base::FilePath & from,int mode)311 void ChangeAttr(const base::FilePath& from, int mode) {
312   EXPECT_EQ(chmod(from.value().c_str(), mode), 0);
313 }
314 
315 class ScopedAttributesRestorer {
316  public:
ScopedAttributesRestorer(const base::FilePath & path,int mode)317   ScopedAttributesRestorer(const base::FilePath& path, int mode)
318       : path_(path), mode_(mode) {
319   }
~ScopedAttributesRestorer()320   ~ScopedAttributesRestorer() {
321     ChangeAttr(path_, mode_);
322   }
323  private:
324   base::FilePath path_;
325   int mode_;
326 };
327 
TrashFunc(const base::FilePath & src)328 void 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 
TEST_F(ServiceProcessStateFileManipulationTest,VerifyLaunchD)338 TEST_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 
TEST_F(ServiceProcessStateFileManipulationTest,DeleteFile)358 TEST_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 
TEST_F(ServiceProcessStateFileManipulationTest,DeleteBundle)367 TEST_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 
TEST_F(ServiceProcessStateFileManipulationTest,MoveBundle)376 TEST_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 
TEST_F(ServiceProcessStateFileManipulationTest,MoveFile)386 TEST_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 
TEST_F(ServiceProcessStateFileManipulationTest,TrashBundle)396 TEST_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 
TEST_F(ServiceProcessStateFileManipulationTest,ChangeAttr)410 TEST_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