[M85 Dev][EFL] Fix errors to generate ninja files
[platform/framework/web/chromium-efl.git] / chrome / browser / process_singleton_posix_unittest.cc
1 // Copyright 2014 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
7 #include <fcntl.h>
8 #include <limits.h>
9 #include <signal.h>
10 #include <stddef.h>
11 #include <sys/types.h>
12 #include <sys/un.h>
13 #include <sys/wait.h>
14 #include <unistd.h>
15
16 #include <memory>
17 #include <string>
18 #include <vector>
19
20 #include "base/bind.h"
21 #include "base/command_line.h"
22 #include "base/files/file_path.h"
23 #include "base/files/file_util.h"
24 #include "base/files/scoped_temp_dir.h"
25 #include "base/location.h"
26 #include "base/posix/eintr_wrapper.h"
27 #include "base/single_thread_task_runner.h"
28 #include "base/strings/stringprintf.h"
29 #include "base/synchronization/waitable_event.h"
30 #include "base/test/metrics/histogram_tester.h"
31 #include "base/test/test_timeouts.h"
32 #include "base/test/thread_test_helper.h"
33 #include "base/threading/thread.h"
34 #include "build/build_config.h"
35 #include "chrome/common/chrome_constants.h"
36 #include "content/public/browser/browser_task_traits.h"
37 #include "content/public/browser/browser_thread.h"
38 #include "content/public/test/browser_task_environment.h"
39 #include "net/base/network_interfaces.h"
40 #include "testing/gtest/include/gtest/gtest.h"
41
42 namespace {
43
44 class ProcessSingletonPosixTest : public testing::Test {
45  public:
46   // A ProcessSingleton exposing some protected methods for testing.
47   class TestableProcessSingleton : public ProcessSingleton {
48    public:
49     explicit TestableProcessSingleton(const base::FilePath& user_data_dir)
50         : ProcessSingleton(
51             user_data_dir,
52             base::Bind(&TestableProcessSingleton::NotificationCallback,
53                        base::Unretained(this))) {}
54
55     std::vector<base::CommandLine::StringVector> callback_command_lines_;
56
57     using ProcessSingleton::NotifyOtherProcessWithTimeout;
58     using ProcessSingleton::NotifyOtherProcessWithTimeoutOrCreate;
59     using ProcessSingleton::OverrideCurrentPidForTesting;
60     using ProcessSingleton::OverrideKillCallbackForTesting;
61
62    private:
63     bool NotificationCallback(const base::CommandLine& command_line,
64                               const base::FilePath& current_directory) {
65       callback_command_lines_.push_back(command_line.argv());
66       return true;
67     }
68   };
69
70   ProcessSingletonPosixTest()
71       : kill_callbacks_(0),
72         task_environment_(content::BrowserTaskEnvironment::REAL_IO_THREAD),
73         wait_event_(base::WaitableEvent::ResetPolicy::MANUAL,
74                     base::WaitableEvent::InitialState::NOT_SIGNALED),
75         signal_event_(base::WaitableEvent::ResetPolicy::MANUAL,
76                       base::WaitableEvent::InitialState::NOT_SIGNALED),
77         process_singleton_on_thread_(nullptr) {}
78
79   void SetUp() override {
80     testing::Test::SetUp();
81
82     ProcessSingleton::DisablePromptForTesting();
83     ProcessSingleton::SkipIsChromeProcessCheckForTesting(false);
84     ProcessSingleton::SetUserOptedUnlockInUseProfileForTesting(false);
85     // Put the lock in a temporary directory.  Doesn't need to be a
86     // full profile to test this code.
87     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
88     // Use a long directory name to ensure that the socket isn't opened through
89     // the symlink.
90     user_data_path_ = temp_dir_.GetPath().Append(
91         std::string(sizeof(sockaddr_un::sun_path), 'a'));
92     ASSERT_TRUE(CreateDirectory(user_data_path_));
93
94     lock_path_ = user_data_path_.Append(chrome::kSingletonLockFilename);
95     socket_path_ = user_data_path_.Append(chrome::kSingletonSocketFilename);
96     cookie_path_ = user_data_path_.Append(chrome::kSingletonCookieFilename);
97   }
98
99   void TearDown() override {
100     scoped_refptr<base::ThreadTestHelper> io_helper(
101         new base::ThreadTestHelper(content::GetIOThreadTaskRunner({}).get()));
102     ASSERT_TRUE(io_helper->Run());
103
104     // Destruct the ProcessSingleton object before the IO thread so that its
105     // internals are destructed properly.
106     if (process_singleton_on_thread_) {
107       worker_thread_->task_runner()->PostTask(
108           FROM_HERE,
109           base::BindOnce(&ProcessSingletonPosixTest::DestructProcessSingleton,
110                          base::Unretained(this)));
111
112       scoped_refptr<base::ThreadTestHelper> helper(
113           new base::ThreadTestHelper(worker_thread_->task_runner().get()));
114       ASSERT_TRUE(helper->Run());
115     }
116
117     testing::Test::TearDown();
118   }
119
120   void CreateProcessSingletonOnThread() {
121     ASSERT_FALSE(worker_thread_.get());
122     worker_thread_.reset(new base::Thread("BlockingThread"));
123     worker_thread_->Start();
124
125     worker_thread_->task_runner()->PostTask(
126         FROM_HERE,
127         base::BindOnce(
128             &ProcessSingletonPosixTest::CreateProcessSingletonInternal,
129             base::Unretained(this)));
130
131     scoped_refptr<base::ThreadTestHelper> helper(
132         new base::ThreadTestHelper(worker_thread_->task_runner().get()));
133     ASSERT_TRUE(helper->Run());
134   }
135
136   TestableProcessSingleton* CreateProcessSingleton() {
137     return new TestableProcessSingleton(user_data_path_);
138   }
139
140   void VerifyFiles() {
141     struct stat statbuf;
142     ASSERT_EQ(0, lstat(lock_path_.value().c_str(), &statbuf));
143     ASSERT_TRUE(S_ISLNK(statbuf.st_mode));
144     char buf[PATH_MAX];
145     ssize_t len = readlink(lock_path_.value().c_str(), buf, PATH_MAX);
146     ASSERT_GT(len, 0);
147
148     ASSERT_EQ(0, lstat(socket_path_.value().c_str(), &statbuf));
149     ASSERT_TRUE(S_ISLNK(statbuf.st_mode));
150
151     len = readlink(socket_path_.value().c_str(), buf, PATH_MAX);
152     ASSERT_GT(len, 0);
153     base::FilePath socket_target_path = base::FilePath(std::string(buf, len));
154
155     ASSERT_EQ(0, lstat(socket_target_path.value().c_str(), &statbuf));
156     ASSERT_TRUE(S_ISSOCK(statbuf.st_mode));
157
158     len = readlink(cookie_path_.value().c_str(), buf, PATH_MAX);
159     ASSERT_GT(len, 0);
160     std::string cookie(buf, len);
161
162     base::FilePath remote_cookie_path = socket_target_path.DirName().
163         Append(chrome::kSingletonCookieFilename);
164     len = readlink(remote_cookie_path.value().c_str(), buf, PATH_MAX);
165     ASSERT_GT(len, 0);
166     EXPECT_EQ(cookie, std::string(buf, len));
167   }
168
169   ProcessSingleton::NotifyResult NotifyOtherProcess(bool override_kill) {
170     std::unique_ptr<TestableProcessSingleton> process_singleton(
171         CreateProcessSingleton());
172     base::CommandLine command_line(
173         base::CommandLine::ForCurrentProcess()->GetProgram());
174     command_line.AppendArg("about:blank");
175     if (override_kill) {
176       process_singleton->OverrideCurrentPidForTesting(
177           base::GetCurrentProcId() + 1);
178       process_singleton->OverrideKillCallbackForTesting(
179           base::Bind(&ProcessSingletonPosixTest::KillCallback,
180                      base::Unretained(this)));
181     }
182
183     return process_singleton->NotifyOtherProcessWithTimeout(
184         command_line, kRetryAttempts, timeout(), true);
185   }
186
187   // A helper method to call ProcessSingleton::NotifyOtherProcessOrCreate().
188   ProcessSingleton::NotifyResult NotifyOtherProcessOrCreate(
189       const std::string& url) {
190     std::unique_ptr<TestableProcessSingleton> process_singleton(
191         CreateProcessSingleton());
192     base::CommandLine command_line(
193         base::CommandLine::ForCurrentProcess()->GetProgram());
194     command_line.AppendArg(url);
195     return process_singleton->NotifyOtherProcessWithTimeoutOrCreate(
196         command_line, kRetryAttempts, timeout());
197   }
198
199   void CheckNotified() {
200     ASSERT_TRUE(process_singleton_on_thread_);
201     ASSERT_EQ(1u, process_singleton_on_thread_->callback_command_lines_.size());
202     bool found = false;
203     for (size_t i = 0;
204          i < process_singleton_on_thread_->callback_command_lines_[0].size();
205          ++i) {
206       if (process_singleton_on_thread_->callback_command_lines_[0][i] ==
207           "about:blank") {
208         found = true;
209         break;
210       }
211     }
212     ASSERT_TRUE(found);
213     ASSERT_EQ(0, kill_callbacks_);
214   }
215
216   void BlockWorkerThread() {
217     worker_thread_->task_runner()->PostTask(
218         FROM_HERE, base::BindOnce(&ProcessSingletonPosixTest::BlockThread,
219                                   base::Unretained(this)));
220   }
221
222   void UnblockWorkerThread() {
223     wait_event_.Signal();  // Unblock the worker thread for shutdown.
224     signal_event_.Wait();  // Ensure thread unblocks before continuing.
225   }
226
227   void BlockThread() {
228     wait_event_.Wait();
229     signal_event_.Signal();
230   }
231
232   base::FilePath user_data_path_;
233   base::FilePath lock_path_;
234   base::FilePath socket_path_;
235   base::FilePath cookie_path_;
236   int kill_callbacks_;
237
238  private:
239   static const int kRetryAttempts = 2;
240
241   base::TimeDelta timeout() const {
242     return TestTimeouts::tiny_timeout() * kRetryAttempts;
243   }
244
245   void CreateProcessSingletonInternal() {
246     ASSERT_TRUE(!process_singleton_on_thread_);
247     process_singleton_on_thread_ = CreateProcessSingleton();
248     ASSERT_EQ(ProcessSingleton::PROCESS_NONE,
249               process_singleton_on_thread_->NotifyOtherProcessOrCreate());
250   }
251
252   void DestructProcessSingleton() {
253     ASSERT_TRUE(process_singleton_on_thread_);
254     delete process_singleton_on_thread_;
255   }
256
257   void KillCallback(int pid) {
258     kill_callbacks_++;
259   }
260
261   content::BrowserTaskEnvironment task_environment_;
262   base::ScopedTempDir temp_dir_;
263   base::WaitableEvent wait_event_;
264   base::WaitableEvent signal_event_;
265
266   std::unique_ptr<base::Thread> worker_thread_;
267   TestableProcessSingleton* process_singleton_on_thread_;
268 };
269
270 }  // namespace
271
272 // Test if the socket file and symbol link created by ProcessSingletonPosix
273 // are valid.
274 // If this test flakes, use http://crbug.com/74554.
275 TEST_F(ProcessSingletonPosixTest, CheckSocketFile) {
276   CreateProcessSingletonOnThread();
277   VerifyFiles();
278 }
279
280 // TODO(james.su@gmail.com): port following tests to Windows.
281 // Test success case of NotifyOtherProcess().
282 TEST_F(ProcessSingletonPosixTest, NotifyOtherProcessSuccess) {
283   CreateProcessSingletonOnThread();
284   EXPECT_EQ(ProcessSingleton::PROCESS_NOTIFIED, NotifyOtherProcess(true));
285   CheckNotified();
286 }
287
288 // Test failure case of NotifyOtherProcess().
289 TEST_F(ProcessSingletonPosixTest, NotifyOtherProcessFailure) {
290   base::HistogramTester histogram_tester;
291   CreateProcessSingletonOnThread();
292
293   BlockWorkerThread();
294   EXPECT_EQ(ProcessSingleton::PROCESS_NONE, NotifyOtherProcess(true));
295   ASSERT_EQ(1, kill_callbacks_);
296   UnblockWorkerThread();
297   histogram_tester.ExpectUniqueSample(
298       "Chrome.ProcessSingleton.RemoteHungProcessTerminateReason",
299       ProcessSingleton::SOCKET_READ_FAILED, 1u);
300 }
301
302 // Test that we don't kill ourselves by accident if a lockfile with the same pid
303 // happens to exist.
304 TEST_F(ProcessSingletonPosixTest, NotifyOtherProcessNoSuicide) {
305   base::HistogramTester histogram_tester;
306   CreateProcessSingletonOnThread();
307   // Replace lockfile with one containing our own pid.
308   EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
309   std::string symlink_content = base::StringPrintf(
310       "%s%c%u",
311       net::GetHostName().c_str(),
312       '-',
313       base::GetCurrentProcId());
314   EXPECT_EQ(0, symlink(symlink_content.c_str(), lock_path_.value().c_str()));
315
316   // Remove socket so that we will not be able to notify the existing browser.
317   EXPECT_EQ(0, unlink(socket_path_.value().c_str()));
318
319   // Pretend we are browser process.
320   ProcessSingleton::SkipIsChromeProcessCheckForTesting(true);
321
322   EXPECT_EQ(ProcessSingleton::PROCESS_NONE, NotifyOtherProcess(false));
323   // If we've gotten to this point without killing ourself, the test succeeded.
324   histogram_tester.ExpectUniqueSample(
325       "Chrome.ProcessSingleton.RemoteProcessInteractionResult",
326       ProcessSingleton::SAME_BROWSER_INSTANCE, 1u);
327 }
328
329 // Test that we can still notify a process on the same host even after the
330 // hostname changed.
331 TEST_F(ProcessSingletonPosixTest, NotifyOtherProcessHostChanged) {
332   CreateProcessSingletonOnThread();
333   EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
334   EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
335
336   EXPECT_EQ(ProcessSingleton::PROCESS_NOTIFIED, NotifyOtherProcess(false));
337   CheckNotified();
338 }
339
340 // Test that we kill hung browser when lock says process is on another host and
341 // we can't notify it over the socket.
342 TEST_F(ProcessSingletonPosixTest, NotifyOtherProcessDifferingHost) {
343   base::HistogramTester histogram_tester;
344   CreateProcessSingletonOnThread();
345
346   BlockWorkerThread();
347
348   EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
349   EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
350
351   EXPECT_EQ(ProcessSingleton::PROCESS_NONE, NotifyOtherProcess(true));
352   ASSERT_EQ(1, kill_callbacks_);
353
354   // lock_path_ should be unlinked in NotifyOtherProcess().
355   base::FilePath target_path;
356   EXPECT_FALSE(base::ReadSymbolicLink(lock_path_, &target_path));
357
358   UnblockWorkerThread();
359
360   histogram_tester.ExpectUniqueSample(
361       "Chrome.ProcessSingleton.RemoteHungProcessTerminateReason",
362       ProcessSingleton::SOCKET_READ_FAILED, 1u);
363 }
364
365 // Test that we'll start creating ProcessSingleton when we have old lock file
366 // that says process is on another host and there is browser with the same pid
367 // but with another user data dir. Also suppose that user opted to unlock
368 // profile.
369 TEST_F(ProcessSingletonPosixTest,
370        NotifyOtherProcessDifferingHost_UnlockedProfileBeforeKill) {
371   base::HistogramTester histogram_tester;
372   CreateProcessSingletonOnThread();
373
374   BlockWorkerThread();
375
376   EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
377   EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
378
379   // Remove socket so that we will not be able to notify the existing browser.
380   EXPECT_EQ(0, unlink(socket_path_.value().c_str()));
381
382   // Unlock profile that was locked by process on another host.
383   ProcessSingleton::SetUserOptedUnlockInUseProfileForTesting(true);
384   // Treat process with pid 1234 as browser with different user data dir.
385   ProcessSingleton::SkipIsChromeProcessCheckForTesting(true);
386
387   EXPECT_EQ(ProcessSingleton::PROCESS_NONE, NotifyOtherProcess(false));
388
389   // lock_path_ should be unlinked in NotifyOtherProcess().
390   base::FilePath target_path;
391   EXPECT_FALSE(base::ReadSymbolicLink(lock_path_, &target_path));
392
393   UnblockWorkerThread();
394
395   histogram_tester.ExpectUniqueSample(
396       "Chrome.ProcessSingleton.RemoteHungProcessTerminateReason",
397       ProcessSingleton::NOTIFY_ATTEMPTS_EXCEEDED, 1u);
398   histogram_tester.ExpectUniqueSample(
399       "Chrome.ProcessSingleton.RemoteProcessInteractionResult",
400       ProcessSingleton::PROFILE_UNLOCKED_BEFORE_KILL, 1u);
401 }
402
403 // Test that we unlock profile when lock says process is on another host and we
404 // can't notify it over the socket.
405 TEST_F(ProcessSingletonPosixTest, NotifyOtherProcessOrCreate_DifferingHost) {
406   base::HistogramTester histogram_tester;
407   CreateProcessSingletonOnThread();
408
409   BlockWorkerThread();
410
411   EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
412   EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
413
414   // Remove socket so that we will not be able to notify the existing browser.
415   EXPECT_EQ(0, unlink(socket_path_.value().c_str()));
416   // Unlock profile that was locked by process on another host.
417   ProcessSingleton::SetUserOptedUnlockInUseProfileForTesting(true);
418
419   std::string url("about:blank");
420   EXPECT_EQ(ProcessSingleton::PROCESS_NONE, NotifyOtherProcessOrCreate(url));
421
422   ASSERT_EQ(0, unlink(lock_path_.value().c_str()));
423
424   UnblockWorkerThread();
425
426   histogram_tester.ExpectUniqueSample(
427       "Chrome.ProcessSingleton.RemoteProcessInteractionResult",
428       ProcessSingleton::PROFILE_UNLOCKED, 1u);
429 }
430
431 // Test that Create fails when another browser is using the profile directory.
432 TEST_F(ProcessSingletonPosixTest, CreateFailsWithExistingBrowser) {
433   CreateProcessSingletonOnThread();
434
435   std::unique_ptr<TestableProcessSingleton> process_singleton(
436       CreateProcessSingleton());
437   process_singleton->OverrideCurrentPidForTesting(base::GetCurrentProcId() + 1);
438   EXPECT_FALSE(process_singleton->Create());
439 }
440
441 // Test that Create fails when another browser is using the profile directory
442 // but with the old socket location.
443 TEST_F(ProcessSingletonPosixTest, CreateChecksCompatibilitySocket) {
444   CreateProcessSingletonOnThread();
445   std::unique_ptr<TestableProcessSingleton> process_singleton(
446       CreateProcessSingleton());
447   process_singleton->OverrideCurrentPidForTesting(base::GetCurrentProcId() + 1);
448
449   // Do some surgery so as to look like the old configuration.
450   char buf[PATH_MAX];
451   ssize_t len = readlink(socket_path_.value().c_str(), buf, sizeof(buf));
452   ASSERT_GT(len, 0);
453   base::FilePath socket_target_path = base::FilePath(std::string(buf, len));
454   ASSERT_EQ(0, unlink(socket_path_.value().c_str()));
455   ASSERT_EQ(0, rename(socket_target_path.value().c_str(),
456                       socket_path_.value().c_str()));
457   ASSERT_EQ(0, unlink(cookie_path_.value().c_str()));
458
459   EXPECT_FALSE(process_singleton->Create());
460 }
461
462 // Test that we fail when lock says process is on another host and we can't
463 // notify it over the socket before of a bad cookie.
464 TEST_F(ProcessSingletonPosixTest, NotifyOtherProcessOrCreate_BadCookie) {
465   CreateProcessSingletonOnThread();
466   // Change the cookie.
467   EXPECT_EQ(0, unlink(cookie_path_.value().c_str()));
468   EXPECT_EQ(0, symlink("INCORRECTCOOKIE", cookie_path_.value().c_str()));
469
470   // Also change the hostname, so the remote does not retry.
471   EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
472   EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
473
474   std::string url("about:blank");
475   EXPECT_EQ(ProcessSingleton::PROFILE_IN_USE, NotifyOtherProcessOrCreate(url));
476 }
477
478 TEST_F(ProcessSingletonPosixTest, IgnoreSocketSymlinkWithTooLongTarget) {
479   base::HistogramTester histogram_tester;
480   CreateProcessSingletonOnThread();
481   // Change the symlink to one with a too-long target.
482   char buf[PATH_MAX];
483   ssize_t len = readlink(socket_path_.value().c_str(), buf, PATH_MAX);
484   ASSERT_GT(len, 0);
485   base::FilePath socket_target_path = base::FilePath(std::string(buf, len));
486   base::FilePath long_socket_target_path = socket_target_path.DirName().Append(
487       std::string(sizeof(sockaddr_un::sun_path), 'b'));
488   ASSERT_EQ(0, unlink(socket_path_.value().c_str()));
489   ASSERT_EQ(0, symlink(long_socket_target_path.value().c_str(),
490                        socket_path_.value().c_str()));
491
492   // A new ProcessSingleton should ignore the invalid socket path target.
493   std::string url("about:blank");
494   EXPECT_EQ(ProcessSingleton::PROCESS_NONE, NotifyOtherProcessOrCreate(url));
495
496   // Lock file contains PID of unit_tests process. It is non browser process so
497   // we treat lock file as orphaned.
498   histogram_tester.ExpectUniqueSample(
499       "Chrome.ProcessSingleton.RemoteProcessInteractionResult",
500       ProcessSingleton::ORPHANED_LOCK_FILE, 1u);
501 }
502
503 #if defined(OS_MACOSX)
504 // Test that if there is an existing lock file, and we could not flock()
505 // it, then exit.
506 TEST_F(ProcessSingletonPosixTest, CreateRespectsOldMacLock) {
507   std::unique_ptr<TestableProcessSingleton> process_singleton(
508       CreateProcessSingleton());
509   base::ScopedFD lock_fd(HANDLE_EINTR(
510       open(lock_path_.value().c_str(), O_RDWR | O_CREAT | O_EXLOCK, 0644)));
511   ASSERT_TRUE(lock_fd.is_valid());
512   EXPECT_FALSE(process_singleton->Create());
513   base::File::Info info;
514   EXPECT_TRUE(base::GetFileInfo(lock_path_, &info));
515   EXPECT_FALSE(info.is_directory);
516   EXPECT_FALSE(info.is_symbolic_link);
517 }
518
519 // Test that if there is an existing lock file, and it's not locked, we replace
520 // it.
521 TEST_F(ProcessSingletonPosixTest, CreateReplacesOldMacLock) {
522   std::unique_ptr<TestableProcessSingleton> process_singleton(
523       CreateProcessSingleton());
524   EXPECT_EQ(0, base::WriteFile(lock_path_, "", 0));
525   EXPECT_TRUE(process_singleton->Create());
526   VerifyFiles();
527 }
528 #endif  // defined(OS_MACOSX)