blob: ce95d0308258dd438db5f696a6a4273776767c23 [file] [log] [blame]
[email protected]65718d92012-05-02 23:02:581// 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/browser/process_singleton.h"
6
[email protected]ea1a3f62012-11-16 20:34:237#include <signal.h>
[email protected]65718d92012-05-02 23:02:588#include <sys/types.h>
9#include <sys/wait.h>
[email protected]65718d92012-05-02 23:02:5810#include <unistd.h>
[email protected]ea1a3f62012-11-16 20:34:2311
[email protected]65718d92012-05-02 23:02:5812#include <string>
[email protected]ea1a3f62012-11-16 20:34:2313#include <vector>
[email protected]65718d92012-05-02 23:02:5814
15#include "base/bind.h"
16#include "base/command_line.h"
[email protected]57999812013-02-24 05:40:5217#include "base/files/file_path.h"
[email protected]ea1a3f62012-11-16 20:34:2318#include "base/files/scoped_temp_dir.h"
[email protected]65718d92012-05-02 23:02:5819#include "base/message_loop.h"
20#include "base/process_util.h"
[email protected]65718d92012-05-02 23:02:5821#include "base/stringprintf.h"
22#include "base/synchronization/waitable_event.h"
23#include "base/test/test_timeouts.h"
24#include "base/test/thread_test_helper.h"
25#include "base/threading/thread.h"
26#include "chrome/common/chrome_constants.h"
[email protected]e97882f2012-06-04 02:23:1727#include "content/public/test/test_browser_thread.h"
[email protected]65718d92012-05-02 23:02:5828#include "net/base/net_util.h"
29#include "testing/gtest/include/gtest/gtest.h"
30
[email protected]3f24ae222012-05-03 22:27:4231using content::BrowserThread;
[email protected]65718d92012-05-02 23:02:5832
33namespace {
34
35bool NotificationCallback(const CommandLine& command_line,
[email protected]650b2d52013-02-10 03:41:4536 const base::FilePath& current_directory) {
[email protected]65718d92012-05-02 23:02:5837 return true;
38}
39
40class ProcessSingletonLinuxTest : public testing::Test {
41 public:
[email protected]7707b21c2012-12-03 20:42:3742 // A ProcessSingleton exposing some protected methods for testing.
43 class TestableProcessSingleton : public ProcessSingleton {
44 public:
[email protected]650b2d52013-02-10 03:41:4545 explicit TestableProcessSingleton(const base::FilePath& user_data_dir)
[email protected]dd85d452013-03-28 12:39:5946 : ProcessSingleton(
47 user_data_dir,
[email protected]9c009092013-05-01 03:14:0948 base::Bind(&TestableProcessSingleton::NotificationCallback,
49 base::Unretained(this))) {}
[email protected]dd85d452013-03-28 12:39:5950
51
52 std::vector<CommandLine::StringVector> callback_command_lines_;
53
[email protected]7707b21c2012-12-03 20:42:3754 using ProcessSingleton::NotifyOtherProcessWithTimeout;
55 using ProcessSingleton::NotifyOtherProcessWithTimeoutOrCreate;
56 using ProcessSingleton::OverrideCurrentPidForTesting;
57 using ProcessSingleton::OverrideKillCallbackForTesting;
[email protected]dd85d452013-03-28 12:39:5958
59 private:
60 bool NotificationCallback(const CommandLine& command_line,
61 const base::FilePath& current_directory) {
62 callback_command_lines_.push_back(command_line.argv());
63 return true;
64 }
[email protected]7707b21c2012-12-03 20:42:3765 };
66
[email protected]65718d92012-05-02 23:02:5867 ProcessSingletonLinuxTest()
68 : kill_callbacks_(0),
[email protected]3f24ae222012-05-03 22:27:4269 io_thread_(BrowserThread::IO),
[email protected]65718d92012-05-02 23:02:5870 wait_event_(true, false),
71 signal_event_(true, false),
72 process_singleton_on_thread_(NULL) {
73 io_thread_.StartIOThread();
74 }
75
76 virtual void SetUp() {
77 testing::Test::SetUp();
78
79 ProcessSingleton::DisablePromptForTesting();
80 // Put the lock in a temporary directory. Doesn't need to be a
81 // full profile to test this code.
82 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
83 lock_path_ = temp_dir_.path().Append(chrome::kSingletonLockFilename);
84 socket_path_ = temp_dir_.path().Append(chrome::kSingletonSocketFilename);
85 cookie_path_ = temp_dir_.path().Append(chrome::kSingletonCookieFilename);
86 }
87
88 virtual void TearDown() {
[email protected]3f24ae222012-05-03 22:27:4289 scoped_refptr<base::ThreadTestHelper> io_helper(new base::ThreadTestHelper(
90 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO)));
91 ASSERT_TRUE(io_helper->Run());
[email protected]65718d92012-05-02 23:02:5892
[email protected]3f24ae222012-05-03 22:27:4293 // Destruct the ProcessSingleton object before the IO thread so that its
94 // internals are destructed properly.
[email protected]65718d92012-05-02 23:02:5895 if (process_singleton_on_thread_) {
96 worker_thread_->message_loop()->PostTask(
97 FROM_HERE,
98 base::Bind(&ProcessSingletonLinuxTest::DestructProcessSingleton,
99 base::Unretained(this)));
100
101 scoped_refptr<base::ThreadTestHelper> helper(
[email protected]3f24ae222012-05-03 22:27:42102 new base::ThreadTestHelper(worker_thread_->message_loop_proxy()));
[email protected]65718d92012-05-02 23:02:58103 ASSERT_TRUE(helper->Run());
104 }
[email protected]3f24ae222012-05-03 22:27:42105
106 io_thread_.Stop();
107 testing::Test::TearDown();
[email protected]65718d92012-05-02 23:02:58108 }
109
110 void CreateProcessSingletonOnThread() {
111 ASSERT_EQ(NULL, worker_thread_.get());
112 worker_thread_.reset(new base::Thread("BlockingThread"));
113 worker_thread_->Start();
114
115 worker_thread_->message_loop()->PostTask(
116 FROM_HERE,
117 base::Bind(&ProcessSingletonLinuxTest::
118 CreateProcessSingletonInternal,
119 base::Unretained(this)));
120
121 scoped_refptr<base::ThreadTestHelper> helper(
122 new base::ThreadTestHelper(
123 worker_thread_->message_loop_proxy()));
124 ASSERT_TRUE(helper->Run());
125 }
126
[email protected]7707b21c2012-12-03 20:42:37127 TestableProcessSingleton* CreateProcessSingleton() {
128 return new TestableProcessSingleton(temp_dir_.path());
[email protected]65718d92012-05-02 23:02:58129 }
130
131 ProcessSingleton::NotifyResult NotifyOtherProcess(
132 bool override_kill,
[email protected]8270c6fe2012-07-09 19:59:21133 base::TimeDelta timeout) {
[email protected]7707b21c2012-12-03 20:42:37134 scoped_ptr<TestableProcessSingleton> process_singleton(
135 CreateProcessSingleton());
[email protected]65718d92012-05-02 23:02:58136 CommandLine command_line(CommandLine::ForCurrentProcess()->GetProgram());
137 command_line.AppendArg("about:blank");
138 if (override_kill) {
[email protected]dd85d452013-03-28 12:39:59139 process_singleton->OverrideCurrentPidForTesting(
140 base::GetCurrentProcId() + 1);
[email protected]65718d92012-05-02 23:02:58141 process_singleton->OverrideKillCallbackForTesting(
142 base::Bind(&ProcessSingletonLinuxTest::KillCallback,
143 base::Unretained(this)));
144 }
145
146 return process_singleton->NotifyOtherProcessWithTimeout(
[email protected]8270c6fe2012-07-09 19:59:21147 command_line, timeout.InSeconds(), true);
[email protected]65718d92012-05-02 23:02:58148 }
149
150 // A helper method to call ProcessSingleton::NotifyOtherProcessOrCreate().
151 ProcessSingleton::NotifyResult NotifyOtherProcessOrCreate(
152 const std::string& url,
[email protected]8270c6fe2012-07-09 19:59:21153 base::TimeDelta timeout) {
[email protected]7707b21c2012-12-03 20:42:37154 scoped_ptr<TestableProcessSingleton> process_singleton(
155 CreateProcessSingleton());
[email protected]65718d92012-05-02 23:02:58156 CommandLine command_line(CommandLine::ForCurrentProcess()->GetProgram());
157 command_line.AppendArg(url);
158 return process_singleton->NotifyOtherProcessWithTimeoutOrCreate(
[email protected]dd85d452013-03-28 12:39:59159 command_line, timeout.InSeconds());
[email protected]65718d92012-05-02 23:02:58160 }
161
162 void CheckNotified() {
[email protected]dd85d452013-03-28 12:39:59163 ASSERT_TRUE(process_singleton_on_thread_ != NULL);
164 ASSERT_EQ(1u, process_singleton_on_thread_->callback_command_lines_.size());
[email protected]65718d92012-05-02 23:02:58165 bool found = false;
[email protected]dd85d452013-03-28 12:39:59166 for (size_t i = 0;
167 i < process_singleton_on_thread_->callback_command_lines_[0].size();
168 ++i) {
169 if (process_singleton_on_thread_->callback_command_lines_[0][i] ==
170 "about:blank") {
[email protected]65718d92012-05-02 23:02:58171 found = true;
172 break;
173 }
174 }
175 ASSERT_TRUE(found);
176 ASSERT_EQ(0, kill_callbacks_);
177 }
178
179 void BlockWorkerThread() {
180 worker_thread_->message_loop()->PostTask(
181 FROM_HERE,
182 base::Bind(&ProcessSingletonLinuxTest::BlockThread,
183 base::Unretained(this)));
184 }
185
186 void UnblockWorkerThread() {
187 wait_event_.Signal(); // Unblock the worker thread for shutdown.
188 signal_event_.Wait(); // Ensure thread unblocks before continuing.
189 }
190
191 void BlockThread() {
192 wait_event_.Wait();
193 signal_event_.Signal();
194 }
195
[email protected]650b2d52013-02-10 03:41:45196 base::FilePath lock_path_;
197 base::FilePath socket_path_;
198 base::FilePath cookie_path_;
[email protected]65718d92012-05-02 23:02:58199 int kill_callbacks_;
200
201 private:
202 void CreateProcessSingletonInternal() {
203 ASSERT_TRUE(!process_singleton_on_thread_);
204 process_singleton_on_thread_ = CreateProcessSingleton();
205 ASSERT_EQ(ProcessSingleton::PROCESS_NONE,
[email protected]dd85d452013-03-28 12:39:59206 process_singleton_on_thread_->NotifyOtherProcessOrCreate());
[email protected]65718d92012-05-02 23:02:58207 }
208
209 void DestructProcessSingleton() {
210 ASSERT_TRUE(process_singleton_on_thread_);
211 delete process_singleton_on_thread_;
212 }
213
[email protected]65718d92012-05-02 23:02:58214 void KillCallback(int pid) {
215 kill_callbacks_++;
216 }
217
218 content::TestBrowserThread io_thread_;
[email protected]ea1a3f62012-11-16 20:34:23219 base::ScopedTempDir temp_dir_;
[email protected]65718d92012-05-02 23:02:58220 base::WaitableEvent wait_event_;
221 base::WaitableEvent signal_event_;
222
223 scoped_ptr<base::Thread> worker_thread_;
[email protected]7707b21c2012-12-03 20:42:37224 TestableProcessSingleton* process_singleton_on_thread_;
[email protected]65718d92012-05-02 23:02:58225};
226
227} // namespace
228
229// Test if the socket file and symbol link created by ProcessSingletonLinux
230// are valid.
231// If this test flakes, use http://crbug.com/74554.
232TEST_F(ProcessSingletonLinuxTest, CheckSocketFile) {
233 CreateProcessSingletonOnThread();
234 struct stat statbuf;
235 ASSERT_EQ(0, lstat(lock_path_.value().c_str(), &statbuf));
236 ASSERT_TRUE(S_ISLNK(statbuf.st_mode));
237 char buf[PATH_MAX];
238 ssize_t len = readlink(lock_path_.value().c_str(), buf, PATH_MAX);
239 ASSERT_GT(len, 0);
240
241 ASSERT_EQ(0, lstat(socket_path_.value().c_str(), &statbuf));
242 ASSERT_TRUE(S_ISLNK(statbuf.st_mode));
243
244 len = readlink(socket_path_.value().c_str(), buf, PATH_MAX);
245 ASSERT_GT(len, 0);
[email protected]650b2d52013-02-10 03:41:45246 base::FilePath socket_target_path = base::FilePath(std::string(buf, len));
[email protected]65718d92012-05-02 23:02:58247
248 ASSERT_EQ(0, lstat(socket_target_path.value().c_str(), &statbuf));
249 ASSERT_TRUE(S_ISSOCK(statbuf.st_mode));
250
251 len = readlink(cookie_path_.value().c_str(), buf, PATH_MAX);
252 ASSERT_GT(len, 0);
253 std::string cookie(buf, len);
254
[email protected]650b2d52013-02-10 03:41:45255 base::FilePath remote_cookie_path = socket_target_path.DirName().
[email protected]65718d92012-05-02 23:02:58256 Append(chrome::kSingletonCookieFilename);
257 len = readlink(remote_cookie_path.value().c_str(), buf, PATH_MAX);
258 ASSERT_GT(len, 0);
259 EXPECT_EQ(cookie, std::string(buf, len));
260}
261
262// TODO([email protected]): port following tests to Windows.
263// Test success case of NotifyOtherProcess().
264TEST_F(ProcessSingletonLinuxTest, NotifyOtherProcessSuccess) {
265 CreateProcessSingletonOnThread();
266 EXPECT_EQ(ProcessSingleton::PROCESS_NOTIFIED,
[email protected]8270c6fe2012-07-09 19:59:21267 NotifyOtherProcess(true, TestTimeouts::action_timeout()));
[email protected]65718d92012-05-02 23:02:58268 CheckNotified();
269}
270
271// Test failure case of NotifyOtherProcess().
272TEST_F(ProcessSingletonLinuxTest, NotifyOtherProcessFailure) {
273 CreateProcessSingletonOnThread();
274
275 BlockWorkerThread();
276 EXPECT_EQ(ProcessSingleton::PROCESS_NONE,
[email protected]8270c6fe2012-07-09 19:59:21277 NotifyOtherProcess(true, TestTimeouts::action_timeout()));
[email protected]65718d92012-05-02 23:02:58278
279 ASSERT_EQ(1, kill_callbacks_);
280 UnblockWorkerThread();
281}
282
283// Test that we don't kill ourselves by accident if a lockfile with the same pid
284// happens to exist.
285TEST_F(ProcessSingletonLinuxTest, NotifyOtherProcessNoSuicide) {
286 CreateProcessSingletonOnThread();
287 // Replace lockfile with one containing our own pid.
288 EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
289 std::string symlink_content = base::StringPrintf(
290 "%s%c%u",
291 net::GetHostName().c_str(),
292 '-',
293 base::GetCurrentProcId());
294 EXPECT_EQ(0, symlink(symlink_content.c_str(), lock_path_.value().c_str()));
295
296 // Remove socket so that we will not be able to notify the existing browser.
297 EXPECT_EQ(0, unlink(socket_path_.value().c_str()));
298
299 EXPECT_EQ(ProcessSingleton::PROCESS_NONE,
[email protected]8270c6fe2012-07-09 19:59:21300 NotifyOtherProcess(false, TestTimeouts::action_timeout()));
[email protected]65718d92012-05-02 23:02:58301 // If we've gotten to this point without killing ourself, the test succeeded.
302}
303
304// Test that we can still notify a process on the same host even after the
305// hostname changed.
306TEST_F(ProcessSingletonLinuxTest, NotifyOtherProcessHostChanged) {
307 CreateProcessSingletonOnThread();
308 EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
309 EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
310
311 EXPECT_EQ(ProcessSingleton::PROCESS_NOTIFIED,
[email protected]8270c6fe2012-07-09 19:59:21312 NotifyOtherProcess(false, TestTimeouts::action_timeout()));
[email protected]65718d92012-05-02 23:02:58313 CheckNotified();
314}
315
316// Test that we fail when lock says process is on another host and we can't
317// notify it over the socket.
318TEST_F(ProcessSingletonLinuxTest, NotifyOtherProcessDifferingHost) {
319 CreateProcessSingletonOnThread();
320
321 BlockWorkerThread();
322
323 EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
324 EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
325
326 EXPECT_EQ(ProcessSingleton::PROFILE_IN_USE,
[email protected]8270c6fe2012-07-09 19:59:21327 NotifyOtherProcess(false, TestTimeouts::action_timeout()));
[email protected]65718d92012-05-02 23:02:58328
329 ASSERT_EQ(0, unlink(lock_path_.value().c_str()));
330
331 UnblockWorkerThread();
332}
333
334// Test that we fail when lock says process is on another host and we can't
335// notify it over the socket.
336TEST_F(ProcessSingletonLinuxTest, NotifyOtherProcessOrCreate_DifferingHost) {
337 CreateProcessSingletonOnThread();
338
339 BlockWorkerThread();
340
341 EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
342 EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
343
344 std::string url("about:blank");
345 EXPECT_EQ(ProcessSingleton::PROFILE_IN_USE,
[email protected]8270c6fe2012-07-09 19:59:21346 NotifyOtherProcessOrCreate(url, TestTimeouts::action_timeout()));
[email protected]65718d92012-05-02 23:02:58347
348 ASSERT_EQ(0, unlink(lock_path_.value().c_str()));
349
350 UnblockWorkerThread();
351}
352
353// Test that Create fails when another browser is using the profile directory.
354TEST_F(ProcessSingletonLinuxTest, CreateFailsWithExistingBrowser) {
355 CreateProcessSingletonOnThread();
356
[email protected]7707b21c2012-12-03 20:42:37357 scoped_ptr<TestableProcessSingleton> process_singleton(
358 CreateProcessSingleton());
[email protected]65718d92012-05-02 23:02:58359 process_singleton->OverrideCurrentPidForTesting(base::GetCurrentProcId() + 1);
[email protected]dd85d452013-03-28 12:39:59360 EXPECT_FALSE(process_singleton->Create());
[email protected]65718d92012-05-02 23:02:58361}
362
363// Test that Create fails when another browser is using the profile directory
364// but with the old socket location.
365TEST_F(ProcessSingletonLinuxTest, CreateChecksCompatibilitySocket) {
366 CreateProcessSingletonOnThread();
[email protected]7707b21c2012-12-03 20:42:37367 scoped_ptr<TestableProcessSingleton> process_singleton(
368 CreateProcessSingleton());
[email protected]65718d92012-05-02 23:02:58369 process_singleton->OverrideCurrentPidForTesting(base::GetCurrentProcId() + 1);
370
371 // Do some surgery so as to look like the old configuration.
372 char buf[PATH_MAX];
373 ssize_t len = readlink(socket_path_.value().c_str(), buf, sizeof(buf));
374 ASSERT_GT(len, 0);
[email protected]650b2d52013-02-10 03:41:45375 base::FilePath socket_target_path = base::FilePath(std::string(buf, len));
[email protected]65718d92012-05-02 23:02:58376 ASSERT_EQ(0, unlink(socket_path_.value().c_str()));
377 ASSERT_EQ(0, rename(socket_target_path.value().c_str(),
378 socket_path_.value().c_str()));
379 ASSERT_EQ(0, unlink(cookie_path_.value().c_str()));
380
[email protected]dd85d452013-03-28 12:39:59381 EXPECT_FALSE(process_singleton->Create());
[email protected]65718d92012-05-02 23:02:58382}
383
384// Test that we fail when lock says process is on another host and we can't
385// notify it over the socket before of a bad cookie.
386TEST_F(ProcessSingletonLinuxTest, NotifyOtherProcessOrCreate_BadCookie) {
387 CreateProcessSingletonOnThread();
388 // Change the cookie.
389 EXPECT_EQ(0, unlink(cookie_path_.value().c_str()));
390 EXPECT_EQ(0, symlink("INCORRECTCOOKIE", cookie_path_.value().c_str()));
391
392 // Also change the hostname, so the remote does not retry.
393 EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
394 EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
395
396 std::string url("about:blank");
397 EXPECT_EQ(ProcessSingleton::PROFILE_IN_USE,
[email protected]8270c6fe2012-07-09 19:59:21398 NotifyOtherProcessOrCreate(url, TestTimeouts::action_timeout()));
[email protected]65718d92012-05-02 23:02:58399}
400