summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/chrome_common.gypi2
-rw-r--r--chrome/chrome_tests.gypi1
-rw-r--r--chrome/common/service_process_util.cc257
-rw-r--r--chrome/common/service_process_util.h91
-rw-r--r--chrome/common/service_process_util_posix.cc83
-rw-r--r--chrome/common/service_process_util_unittest.cc62
-rw-r--r--chrome/common/service_process_util_win.cc145
-rw-r--r--chrome/service/service_main.cc3
-rw-r--r--chrome/service/service_process.cc43
-rw-r--r--chrome/service/service_process.h3
10 files changed, 426 insertions, 264 deletions
diff --git a/chrome/chrome_common.gypi b/chrome/chrome_common.gypi
index fc943de..b9e2806 100644
--- a/chrome/chrome_common.gypi
+++ b/chrome/chrome_common.gypi
@@ -289,6 +289,8 @@
'common/service_process_type.h',
'common/service_process_util.cc',
'common/service_process_util.h',
+ 'common/service_process_util_posix.cc',
+ 'common/service_process_util_win.cc',
'common/socket_stream_dispatcher.cc',
'common/socket_stream_dispatcher.h',
'common/spellcheck_common.cc',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 63c479a..f0421d5 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -1546,6 +1546,7 @@
'common/sandbox_mac_unittest_helper.h',
'common/sandbox_mac_unittest_helper.mm',
'common/sandbox_mac_system_access_unittest.mm',
+ 'common/service_process_util_unittest.cc',
'common/switch_utils_unittest.cc',
'common/thumbnail_score_unittest.cc',
'common/time_format_unittest.cc',
diff --git a/chrome/common/service_process_util.cc b/chrome/common/service_process_util.cc
index 150ceb2..f636f2b 100644
--- a/chrome/common/service_process_util.cc
+++ b/chrome/common/service_process_util.cc
@@ -6,7 +6,6 @@
#include "base/logging.h"
#include "base/path_service.h"
#include "base/process_util.h"
-#include "base/shared_memory.h"
#include "base/string16.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
@@ -16,13 +15,6 @@
#include "chrome/common/service_process_util.h"
#include "chrome/installer/util/version.h"
-#if defined(OS_WIN)
-#include "base/object_watcher.h"
-#include "base/scoped_handle_win.h"
-#endif
-
-// TODO(hclam): Split this file for different platforms.
-
namespace {
// This should be more than enough to hold a version string assuming each part
@@ -35,34 +27,6 @@ struct ServiceProcessSharedData {
base::ProcessId service_process_pid;
};
-// Return a name that is scoped to this instance of the service process. We
-// use the user-data-dir as a scoping prefix.
-std::string GetServiceProcessScopedName(const std::string& append_str) {
- FilePath user_data_dir;
- PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
-#if defined(OS_WIN)
- std::string scoped_name = WideToUTF8(user_data_dir.value());
-#elif defined(OS_POSIX)
- std::string scoped_name = user_data_dir.value();
-#endif // defined(OS_WIN)
- std::replace(scoped_name.begin(), scoped_name.end(), '\\', '!');
- std::replace(scoped_name.begin(), scoped_name.end(), '/', '!');
- scoped_name.append(append_str);
- return scoped_name;
-}
-
-// Return a name that is scoped to this instance of the service process. We
-// use the user-data-dir and the version as a scoping prefix.
-std::string GetServiceProcessScopedVersionedName(
- const std::string& append_str) {
- std::string versioned_str;
- chrome::VersionInfo version_info;
- DCHECK(version_info.is_valid());
- versioned_str.append(version_info.Version());
- versioned_str.append(append_str);
- return GetServiceProcessScopedName(versioned_str);
-}
-
// Gets the name of the shared memory used by the service process to write its
// version. The name is not versioned.
std::string GetServiceProcessSharedMemName() {
@@ -99,7 +63,6 @@ enum ServiceProcessRunningState {
SERVICE_NEWER_VERSION_RUNNING,
};
-#if defined(OS_WIN)
ServiceProcessRunningState GetServiceProcessRunningState(
std::string* service_version_out) {
std::string version;
@@ -143,85 +106,76 @@ ServiceProcessRunningState GetServiceProcessRunningState(
}
return SERVICE_SAME_VERSION_RUNNING;
}
-#endif // defined(OS_WIN)
-
-#if defined(OS_WIN)
-string16 GetServiceProcessReadyEventName() {
- return UTF8ToWide(
- GetServiceProcessScopedVersionedName("_service_ready"));
-}
-
-string16 GetServiceProcessShutdownEventName() {
- return UTF8ToWide(
- GetServiceProcessScopedVersionedName("_service_shutdown_evt"));
-}
-class ServiceProcessShutdownMonitor : public base::ObjectWatcher::Delegate {
- public:
- explicit ServiceProcessShutdownMonitor(Task* shutdown_task)
- : shutdown_task_(shutdown_task) {
- }
- void Start() {
- string16 event_name = GetServiceProcessShutdownEventName();
- CHECK(event_name.length() <= MAX_PATH);
- shutdown_event_.Set(CreateEvent(NULL, TRUE, FALSE, event_name.c_str()));
- watcher_.StartWatching(shutdown_event_.Get(), this);
- }
- // base::ObjectWatcher::Delegate implementation.
- virtual void OnObjectSignaled(HANDLE object) {
- shutdown_task_->Run();
- shutdown_task_.reset();
- }
+} // namespace
- private:
- ScopedHandle shutdown_event_;
- base::ObjectWatcher watcher_;
- scoped_ptr<Task> shutdown_task_;
-};
-#else // defined(OS_WIN)
-// Gets the name of the lock file for service process. Used on non-Windows OSes.
-FilePath GetServiceProcessLockFilePath() {
+// Return a name that is scoped to this instance of the service process. We
+// use the user-data-dir as a scoping prefix.
+std::string GetServiceProcessScopedName(const std::string& append_str) {
FilePath user_data_dir;
PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
- chrome::VersionInfo version_info;
- std::string lock_file_name = version_info.Version() + "Service Process Lock";
- return user_data_dir.Append(lock_file_name);
-}
-#endif // defined(OS_WIN)
-
-struct ServiceProcessGlobalState {
- scoped_ptr<base::SharedMemory> shared_mem_service_data;
#if defined(OS_WIN)
- // An event that is signaled when a service process is ready.
- ScopedHandle ready_event;
- scoped_ptr<ServiceProcessShutdownMonitor> shutdown_monitor;
+ std::string scoped_name = WideToUTF8(user_data_dir.value());
+#elif defined(OS_POSIX)
+ std::string scoped_name = user_data_dir.value();
#endif // defined(OS_WIN)
-};
-
-// TODO(sanjeevr): Remove this ugly global pointer and move all methods and
-// data members used by the service process into one singleton class which
-// could be owned by the service process.
-ServiceProcessGlobalState* g_service_globals = NULL;
+ std::replace(scoped_name.begin(), scoped_name.end(), '\\', '!');
+ std::replace(scoped_name.begin(), scoped_name.end(), '/', '!');
+ scoped_name.append(append_str);
+ return scoped_name;
+}
-} // namespace
+// Return a name that is scoped to this instance of the service process. We
+// use the user-data-dir and the version as a scoping prefix.
+std::string GetServiceProcessScopedVersionedName(
+ const std::string& append_str) {
+ std::string versioned_str;
+ chrome::VersionInfo version_info;
+ DCHECK(version_info.is_valid());
+ versioned_str.append(version_info.Version());
+ versioned_str.append(append_str);
+ return GetServiceProcessScopedName(versioned_str);
+}
// Gets the name of the service process IPC channel.
std::string GetServiceProcessChannelName() {
return GetServiceProcessScopedVersionedName("_service_ipc");
}
-std::string GetServiceProcessAutoRunKey() {
- return GetServiceProcessScopedName("_service_run");
+base::ProcessId GetServiceProcessPid() {
+ base::ProcessId pid = 0;
+ GetServiceProcessSharedData(NULL, &pid);
+ return pid;
}
-bool TakeServiceProcessSingletonLock() {
- DCHECK(g_service_globals == NULL);
- // On Linux shared menory is implemented using file. In case of incorrect
- // shutdown or process kill memshared file stay on the disk and prevents
- // next instance of service from starting. So, on Linux we have to disable
- // check for another running instance of the service.
-#if defined(OS_WIN)
+ServiceProcessState::ServiceProcessState() : state_(NULL) {
+}
+
+ServiceProcessState::~ServiceProcessState() {
+ TearDownState();
+}
+
+bool ServiceProcessState::Initialize() {
+ if (!TakeSingletonLock()) {
+ return false;
+ }
+ // Now that we have the singleton, take care of killing an older version, if
+ // it exists.
+ if (ShouldHandleOtherVersion() && !HandleOtherVersion())
+ return false;
+
+ // TODO(sanjeevr): We can probably use the shared mem as the sole singleton
+ // mechanism. For that the shared mem class needs to return whether it created
+ // new instance or opened an existing one. Also shared memory on Linux uses
+ // a file on disk which is not deleted when the process exits.
+
+ // Now that we have the singleton, let is also write the version we are using
+ // to shared memory. This can be used by a newer service to signal us to exit.
+ return CreateSharedData();
+}
+
+bool ServiceProcessState::HandleOtherVersion() {
std::string running_version;
ServiceProcessRunningState state =
GetServiceProcessRunningState(&running_version);
@@ -236,31 +190,10 @@ bool TakeServiceProcessSingletonLock() {
case SERVICE_NOT_RUNNING:
break;
}
-#endif
- g_service_globals = new ServiceProcessGlobalState;
- // TODO(sanjeevr): We can probably use the shared mem as the sole singleton
- // mechanism. For that the shared mem class needs to return whether it created
- // new instance or opened an existing one.
-#if defined(OS_WIN)
- string16 event_name = GetServiceProcessReadyEventName();
- CHECK(event_name.length() <= MAX_PATH);
- ScopedHandle service_process_ready_event;
- service_process_ready_event.Set(
- CreateEvent(NULL, TRUE, FALSE, event_name.c_str()));
- DWORD error = GetLastError();
- if ((error == ERROR_ALREADY_EXISTS) || (error == ERROR_ACCESS_DENIED)) {
- delete g_service_globals;
- g_service_globals = NULL;
- return false;
- }
- DCHECK(service_process_ready_event.IsValid());
- g_service_globals->ready_event.Set(service_process_ready_event.Take());
-#else
- // TODO(sanjeevr): Implement singleton mechanism for other platforms.
- NOTIMPLEMENTED();
-#endif
- // Now that we have the singleton, let is also write the version we are using
- // to shared memory. This can be used by a newer service to signal us to exit.
+ return true;
+}
+
+bool ServiceProcessState::CreateSharedData() {
chrome::VersionInfo version_info;
if (!version_info.is_valid()) {
NOTREACHED() << "Failed to get current file version";
@@ -293,80 +226,12 @@ bool TakeServiceProcessSingletonLock() {
memcpy(shared_data->service_process_version, version_info.Version().c_str(),
version_info.Version().length());
shared_data->service_process_pid = base::GetCurrentProcId();
- g_service_globals->shared_mem_service_data.reset(
- shared_mem_service_data.release());
- return true;
-}
-
-void SignalServiceProcessReady(Task* shutdown_task) {
-#if defined(OS_WIN)
- DCHECK(g_service_globals != NULL);
- DCHECK(g_service_globals->ready_event.IsValid());
- SetEvent(g_service_globals->ready_event.Get());
- g_service_globals->shutdown_monitor.reset(
- new ServiceProcessShutdownMonitor(shutdown_task));
- g_service_globals->shutdown_monitor->Start();
-#else
- // TODO(hclam): Implement better mechanism for these platform.
- // Also we need to save shutdown task. For now we just delete the shutdown
- // task because we have not way to listen for shutdown requests.
- delete shutdown_task;
- const FilePath path = GetServiceProcessLockFilePath();
- FILE* file = file_util::OpenFile(path, "wb+");
- if (!file)
- return;
- LOG(INFO) << "Created Service Process lock file: " << path.value();
- file_util::TruncateFile(file) && file_util::CloseFile(file);
-#endif
-}
-
-void SignalServiceProcessStopped() {
- delete g_service_globals;
- g_service_globals = NULL;
-#if !defined(OS_WIN)
- // TODO(hclam): Implement better mechanism for these platform.
- const FilePath path = GetServiceProcessLockFilePath();
- file_util::Delete(path, false);
-#endif
-}
-
-bool ForceServiceProcessShutdown(const std::string& version) {
-#if defined(OS_WIN)
- ScopedHandle shutdown_event;
- std::string versioned_name = version;
- versioned_name.append("_service_shutdown_evt");
- string16 event_name =
- UTF8ToWide(GetServiceProcessScopedName(versioned_name));
- shutdown_event.Set(OpenEvent(EVENT_MODIFY_STATE, FALSE, event_name.c_str()));
- if (!shutdown_event.IsValid())
- return false;
- SetEvent(shutdown_event.Get());
+ shared_mem_service_data_.reset(shared_mem_service_data.release());
return true;
-#else // defined(OS_WIN)
- NOTIMPLEMENTED();
- return false;
-#endif // defined(OS_WIN)
}
-bool CheckServiceProcessReady() {
-#if defined(OS_WIN)
- string16 event_name = GetServiceProcessReadyEventName();
- ScopedHandle event(
- OpenEvent(SYNCHRONIZE | READ_CONTROL, false, event_name.c_str()));
- if (!event.IsValid())
- return false;
- // Check if the event is signaled.
- return WaitForSingleObject(event, 0) == WAIT_OBJECT_0;
-#else
- // TODO(hclam): Implement better mechanism for these platform.
- const FilePath path = GetServiceProcessLockFilePath();
- return file_util::PathExists(path);
-#endif
-}
-base::ProcessId GetServiceProcessPid() {
- base::ProcessId pid = 0;
- GetServiceProcessSharedData(NULL, &pid);
- return pid;
+std::string ServiceProcessState::GetAutoRunKey() {
+ return GetServiceProcessScopedName("_service_run");
}
diff --git a/chrome/common/service_process_util.h b/chrome/common/service_process_util.h
index 8876426..5227dc91 100644
--- a/chrome/common/service_process_util.h
+++ b/chrome/common/service_process_util.h
@@ -8,43 +8,24 @@
#include <string>
#include "base/process.h"
+#include "base/shared_memory.h"
#include "base/task.h"
// Return the IPC channel to connect to the service process.
//
std::string GetServiceProcessChannelName();
-// The following methods are used within the service process.
-// --------------------------------------------------------------------------
-
-// Tries to become the sole service process for the current user data dir.
-// Returns false is another service process is already running.
-bool TakeServiceProcessSingletonLock();
-
-// Signal that the service process is ready.
-// This method is called when the service process is running and initialized.
-// |shutdown_task| is invoked when we get a shutdown request from another
-// process (in the same thread that called SignalServiceProcessReady).
-void SignalServiceProcessReady(Task* shutdown_task);
-
-// Signal that the service process is stopped.
-void SignalServiceProcessStopped();
-
-// Key used to register the service process to auto-run.
-std::string GetServiceProcessAutoRunKey();
-
-// This is exposed for testing only. Forces a service process matching the
-// specified version to shut down.
-bool ForceServiceProcessShutdown(const std::string& version);
-
-// --------------------------------------------------------------------------
+// Return a name that is scoped to this instance of the service process. We
+// use the user-data-dir as a scoping prefix.
+std::string GetServiceProcessScopedName(const std::string& append_str);
+// Return a name that is scoped to this instance of the service process. We
+// use the user-data-dir and the version as a scoping prefix.
+std::string GetServiceProcessScopedVersionedName(const std::string& append_str);
// The following methods are used in a process that acts as a client to the
// service process (typically the browser process).
-
// --------------------------------------------------------------------------
-
// This method checks that if the service process is ready to receive
// IPC commands.
bool CheckServiceProcessReady();
@@ -57,5 +38,63 @@ bool CheckServiceProcessReady();
base::ProcessId GetServiceProcessPid();
// --------------------------------------------------------------------------
+// Forces a service process matching the specified version to shut down.
+bool ForceServiceProcessShutdown(const std::string& version);
+
+// This is a class that is used by the service process to signal events and
+// share data with external clients. This class lives in this file because the
+// internal data structures and mechanisms used by the utility methods above
+// and this class are shared.
+class ServiceProcessState {
+ public:
+ ServiceProcessState();
+ ~ServiceProcessState();
+
+ // Tries to become the sole service process for the current user data dir.
+ // Returns false is another service process is already running.
+ bool Initialize();
+
+ // Signal that the service process is ready.
+ // This method is called when the service process is running and initialized.
+ // |shutdown_task| is invoked when we get a shutdown request from another
+ // process (in the same thread that called SignalReady). It can be NULL.
+ void SignalReady(Task* shutdown_task);
+
+ // Signal that the service process is stopped.
+ void SignalStopped();
+
+ // Register the service process to run on startup.
+ bool AddToAutoRun();
+
+ // Unregister the service process to run on startup.
+ bool RemoveFromAutoRun();
+ private:
+
+ // Create the shared memory data for the service process.
+ bool CreateSharedData();
+
+ // If an older version of the service process running, it should be shutdown.
+ // Returns false if this process needs to exit.
+ bool HandleOtherVersion();
+
+ // Acquires a singleton lock for the service process. A return value of false
+ // means that a service process instance is already running.
+ bool TakeSingletonLock();
+
+ // Key used to register the service process to auto-run.
+ std::string GetAutoRunKey();
+
+ // Tear down the platform specific state.
+ void TearDownState();
+
+ // Allows each platform to specify whether it supports killing older versions.
+ bool ShouldHandleOtherVersion();
+ // An opaque object that maintains state. The actual definition of this is
+ // platform dependent.
+ struct StateData;
+ StateData* state_;
+ scoped_ptr<base::SharedMemory> shared_mem_service_data_;
+};
+
#endif // CHROME_COMMON_SERVICE_PROCESS_UTIL_H_
diff --git a/chrome/common/service_process_util_posix.cc b/chrome/common/service_process_util_posix.cc
new file mode 100644
index 0000000..e3db81d
--- /dev/null
+++ b/chrome/common/service_process_util_posix.cc
@@ -0,0 +1,83 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/common/service_process_util.h"
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/chrome_version_info.h"
+
+namespace {
+
+// Gets the name of the lock file for service process.
+FilePath GetServiceProcessLockFilePath() {
+ FilePath user_data_dir;
+ PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
+ chrome::VersionInfo version_info;
+ std::string lock_file_name = version_info.Version() + "Service Process Lock";
+ return user_data_dir.Append(lock_file_name);
+}
+
+} // namespace
+
+bool ForceServiceProcessShutdown(const std::string& version) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+bool CheckServiceProcessReady() {
+ const FilePath path = GetServiceProcessLockFilePath();
+ return file_util::PathExists(path);
+}
+
+struct ServiceProcessState::StateData {
+ // No state yet for Posix.
+};
+
+bool ServiceProcessState::TakeSingletonLock() {
+ // TODO(sanjeevr): Implement singleton mechanism for POSIX.
+ NOTIMPLEMENTED();
+ return true;
+}
+
+void ServiceProcessState::SignalReady(Task* shutdown_task) {
+ // TODO(hclam): Implement better mechanism for these platform.
+ // Also we need to save shutdown task. For now we just delete the shutdown
+ // task because we have not way to listen for shutdown requests.
+ delete shutdown_task;
+ const FilePath path = GetServiceProcessLockFilePath();
+ FILE* file = file_util::OpenFile(path, "wb+");
+ if (!file)
+ return;
+ LOG(INFO) << "Created Service Process lock file: " << path.value();
+ file_util::TruncateFile(file) && file_util::CloseFile(file);
+}
+
+void ServiceProcessState::SignalStopped() {
+ const FilePath path = GetServiceProcessLockFilePath();
+ file_util::Delete(path, false);
+ shared_mem_service_data_.reset();
+}
+
+bool ServiceProcessState::AddToAutoRun() {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+bool ServiceProcessState::RemoveFromAutoRun() {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+void ServiceProcessState::TearDownState() {
+}
+
+bool ServiceProcessState::ShouldHandleOtherVersion() {
+ // On POSIX, the shared memory is a file in disk. We may have a stale file
+ // lying around from a previous run. So the check is not reliable.
+ return false;
+}
+
diff --git a/chrome/common/service_process_util_unittest.cc b/chrome/common/service_process_util_unittest.cc
new file mode 100644
index 0000000..6000b5c
--- /dev/null
+++ b/chrome/common/service_process_util_unittest.cc
@@ -0,0 +1,62 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/process_util.h"
+#include "base/string_util.h"
+#include "chrome/common/chrome_version_info.h"
+#include "chrome/common/service_process_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+
+TEST(ServiceProcessUtilTest, ScopedVersionedName) {
+ std::string test_str = "test";
+ std::string scoped_name = GetServiceProcessScopedVersionedName(test_str);
+ chrome::VersionInfo version_info;
+ DCHECK(version_info.is_valid());
+ EXPECT_TRUE(EndsWith(scoped_name, test_str, true));
+ EXPECT_NE(std::string::npos, scoped_name.find(version_info.Version()));
+}
+
+#if defined(OS_WIN)
+// Singleton-ness is only implemented on Windows.
+TEST(ServiceProcessStateTest, Singleton) {
+ ServiceProcessState state;
+ EXPECT_TRUE(state.Initialize());
+ // The second instance should fail to Initialize.
+ ServiceProcessState another_state;
+ EXPECT_FALSE(another_state.Initialize());
+}
+#endif // defined(OS_WIN)
+
+TEST(ServiceProcessStateTest, ReadyState) {
+#if defined(OS_WIN)
+ // On Posix, we use a lock file on disk to signal readiness. This lock file
+ // could be lying around from previous crashes which could cause
+ // CheckServiceProcessReady to lie. On Windows, we use a named event so we
+ // don't have this issue. Until we have a more stable signalling mechanism on
+ // Posix, this check will only execute on Windows.
+ EXPECT_FALSE(CheckServiceProcessReady());
+#endif // defined(OS_WIN)
+ ServiceProcessState state;
+ EXPECT_TRUE(state.Initialize());
+ state.SignalReady(NULL);
+ EXPECT_TRUE(CheckServiceProcessReady());
+ state.SignalStopped();
+ EXPECT_FALSE(CheckServiceProcessReady());
+}
+
+TEST(ServiceProcessStateTest, SharedMem) {
+#if defined(OS_WIN)
+ // On Posix, named shared memory uses a file on disk. This file
+ // could be lying around from previous crashes which could cause
+ // GetServiceProcessPid to lie. On Windows, we use a named event so we
+ // don't have this issue. Until we have a more stable shared memory
+ // implementation on Posix, this check will only execute on Windows.
+ EXPECT_EQ(0, GetServiceProcessPid());
+#endif // defined(OS_WIN)
+ ServiceProcessState state;
+ EXPECT_TRUE(state.Initialize());
+ EXPECT_EQ(base::GetCurrentProcId(), GetServiceProcessPid());
+}
+
diff --git a/chrome/common/service_process_util_win.cc b/chrome/common/service_process_util_win.cc
new file mode 100644
index 0000000..6d233e9
--- /dev/null
+++ b/chrome/common/service_process_util_win.cc
@@ -0,0 +1,145 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/common/service_process_util.h"
+
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/object_watcher.h"
+#include "base/path_service.h"
+#include "base/scoped_handle_win.h"
+#include "base/string16.h"
+#include "base/utf_string_conversions.h"
+#include "base/win_util.h"
+#include "chrome/common/chrome_switches.h"
+
+namespace {
+
+string16 GetServiceProcessReadyEventName() {
+ return UTF8ToWide(
+ GetServiceProcessScopedVersionedName("_service_ready"));
+}
+
+string16 GetServiceProcessShutdownEventName() {
+ return UTF8ToWide(
+ GetServiceProcessScopedVersionedName("_service_shutdown_evt"));
+}
+
+class ServiceProcessShutdownMonitor : public base::ObjectWatcher::Delegate {
+ public:
+ explicit ServiceProcessShutdownMonitor(Task* shutdown_task)
+ : shutdown_task_(shutdown_task) {
+ }
+ void Start() {
+ string16 event_name = GetServiceProcessShutdownEventName();
+ CHECK(event_name.length() <= MAX_PATH);
+ shutdown_event_.Set(CreateEvent(NULL, TRUE, FALSE, event_name.c_str()));
+ watcher_.StartWatching(shutdown_event_.Get(), this);
+ }
+
+ // base::ObjectWatcher::Delegate implementation.
+ virtual void OnObjectSignaled(HANDLE object) {
+ shutdown_task_->Run();
+ shutdown_task_.reset();
+ }
+
+ private:
+ ScopedHandle shutdown_event_;
+ base::ObjectWatcher watcher_;
+ scoped_ptr<Task> shutdown_task_;
+};
+
+} // namespace
+
+bool ForceServiceProcessShutdown(const std::string& version) {
+ ScopedHandle shutdown_event;
+ std::string versioned_name = version;
+ versioned_name.append("_service_shutdown_evt");
+ string16 event_name =
+ UTF8ToWide(GetServiceProcessScopedName(versioned_name));
+ shutdown_event.Set(OpenEvent(EVENT_MODIFY_STATE, FALSE, event_name.c_str()));
+ if (!shutdown_event.IsValid())
+ return false;
+ SetEvent(shutdown_event.Get());
+ return true;
+}
+
+bool CheckServiceProcessReady() {
+ string16 event_name = GetServiceProcessReadyEventName();
+ ScopedHandle event(
+ OpenEvent(SYNCHRONIZE | READ_CONTROL, false, event_name.c_str()));
+ if (!event.IsValid())
+ return false;
+ // Check if the event is signaled.
+ return WaitForSingleObject(event, 0) == WAIT_OBJECT_0;
+}
+
+struct ServiceProcessState::StateData {
+ // An event that is signaled when a service process is ready.
+ ScopedHandle ready_event;
+ scoped_ptr<ServiceProcessShutdownMonitor> shutdown_monitor;
+};
+
+bool ServiceProcessState::TakeSingletonLock() {
+ DCHECK(!state_);
+ string16 event_name = GetServiceProcessReadyEventName();
+ CHECK(event_name.length() <= MAX_PATH);
+ ScopedHandle service_process_ready_event;
+ service_process_ready_event.Set(
+ CreateEvent(NULL, TRUE, FALSE, event_name.c_str()));
+ DWORD error = GetLastError();
+ if ((error == ERROR_ALREADY_EXISTS) || (error == ERROR_ACCESS_DENIED))
+ return false;
+ DCHECK(service_process_ready_event.IsValid());
+ state_ = new StateData;
+ state_->ready_event.Set(service_process_ready_event.Take());
+ return true;
+}
+
+void ServiceProcessState::SignalReady(Task* shutdown_task) {
+ DCHECK(state_);
+ DCHECK(state_->ready_event.IsValid());
+ SetEvent(state_->ready_event.Get());
+ if (shutdown_task) {
+ state_->shutdown_monitor.reset(
+ new ServiceProcessShutdownMonitor(shutdown_task));
+ state_->shutdown_monitor->Start();
+ }
+}
+
+void ServiceProcessState::SignalStopped() {
+ TearDownState();
+ shared_mem_service_data_.reset();
+}
+
+bool ServiceProcessState::AddToAutoRun() {
+ FilePath chrome_path;
+ if (PathService::Get(base::FILE_EXE, &chrome_path)) {
+ CommandLine cmd_line(chrome_path);
+ cmd_line.AppendSwitchASCII(switches::kProcessType,
+ switches::kServiceProcess);
+ // We need a unique name for the command per user-date-dir. Just use the
+ // channel name.
+ return win_util::AddCommandToAutoRun(
+ HKEY_CURRENT_USER,
+ UTF8ToWide(GetAutoRunKey()),
+ cmd_line.command_line_string());
+ }
+ return false;
+}
+
+bool ServiceProcessState::RemoveFromAutoRun() {
+ return win_util::RemoveCommandFromAutoRun(
+ HKEY_CURRENT_USER, UTF8ToWide(GetAutoRunKey()));
+}
+
+void ServiceProcessState::TearDownState() {
+ delete state_;
+ state_ = NULL;
+}
+
+bool ServiceProcessState::ShouldHandleOtherVersion() {
+ return true;
+}
diff --git a/chrome/service/service_main.cc b/chrome/service/service_main.cc
index b0765fa..6686ccc 100644
--- a/chrome/service/service_main.cc
+++ b/chrome/service/service_main.cc
@@ -5,6 +5,7 @@
#include "base/debug_util.h"
#include "base/message_loop.h"
#include "base/path_service.h"
+#include "base/singleton.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/main_function_params.h"
@@ -16,7 +17,7 @@
// Mainline routine for running as the service process.
int ServiceProcessMain(const MainFunctionParams& parameters) {
// If there is already a service process running, quit now.
- if (!TakeServiceProcessSingletonLock())
+ if (!Singleton<ServiceProcessState>::get()->Initialize())
return 0;
MessageLoopForUI main_message_loop;
diff --git a/chrome/service/service_process.cc b/chrome/service/service_process.cc
index 140a313..c4523e2 100644
--- a/chrome/service/service_process.cc
+++ b/chrome/service/service_process.cc
@@ -8,12 +8,10 @@
#include "base/command_line.h"
#include "base/path_service.h"
+#include "base/singleton.h"
#include "base/string16.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
-#if defined(OS_WIN)
-#include "base/win_util.h"
-#endif // defined(OS_WIN)
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
@@ -113,7 +111,7 @@ bool ServiceProcess::Initialize(MessageLoop* message_loop,
// After the IPC server has started we signal that the service process is
// ready.
- SignalServiceProcessReady(
+ Singleton<ServiceProcessState>::get()->SignalReady(
NewRunnableMethod(this, &ServiceProcess::Shutdown));
// See if we need to stay running.
@@ -139,8 +137,7 @@ bool ServiceProcess::Teardown() {
// might use it have been shut down.
network_change_notifier_.reset();
- // Delete the service process lock file when it shuts down.
- SignalServiceProcessStopped();
+ Singleton<ServiceProcessState>::get()->SignalStopped();
return true;
}
@@ -185,7 +182,7 @@ void ServiceProcess::OnCloudPrintProxyDisabled() {
void ServiceProcess::OnServiceEnabled() {
enabled_services_++;
if (1 == enabled_services_) {
- AddServiceProcessToAutoStart();
+ Singleton<ServiceProcessState>::get()->AddToAutoRun();
}
}
@@ -193,7 +190,7 @@ void ServiceProcess::OnServiceDisabled() {
DCHECK_NE(enabled_services_, 0);
enabled_services_--;
if (0 == enabled_services_) {
- RemoveServiceProcessFromAutoStart();
+ Singleton<ServiceProcessState>::get()->RemoveFromAutoRun();
// We will wait for some time to respond to IPCs before shutting down.
ScheduleShutdownCheck();
}
@@ -220,36 +217,6 @@ void ServiceProcess::ShutdownIfNeeded() {
}
}
-bool ServiceProcess::AddServiceProcessToAutoStart() {
-// TODO(sanjeevr): This needs to move to some common place like base or
-// chrome/common and implementation for non-Windows platforms needs to be added.
-#if defined(OS_WIN)
- FilePath chrome_path;
- if (PathService::Get(base::FILE_EXE, &chrome_path)) {
- CommandLine cmd_line(chrome_path);
- cmd_line.AppendSwitchASCII(switches::kProcessType,
- switches::kServiceProcess);
- // We need a unique name for the command per user-date-dir. Just use the
- // channel name.
- return win_util::AddCommandToAutoRun(
- HKEY_CURRENT_USER, UTF8ToWide(GetServiceProcessAutoRunKey()),
- cmd_line.command_line_string());
- }
-#endif // defined(OS_WIN)
- return false;
-}
-
-bool ServiceProcess::RemoveServiceProcessFromAutoStart() {
-// TODO(sanjeevr): This needs to move to some common place like base or
-// chrome/common and implementation for non-Windows platforms needs to be added.
-#if defined(OS_WIN)
- return win_util::RemoveCommandFromAutoRun(
- HKEY_CURRENT_USER, UTF8ToWide(GetServiceProcessAutoRunKey()));
-#endif // defined(OS_WIN)
- return false;
-}
-
-
#if defined(ENABLE_REMOTING)
bool ServiceProcess::EnableChromotingHostWithTokens(
const std::string& login,
diff --git a/chrome/service/service_process.h b/chrome/service/service_process.h
index a8a5e5a..91d51a0 100644
--- a/chrome/service/service_process.h
+++ b/chrome/service/service_process.h
@@ -128,9 +128,6 @@ class ServiceProcess : public RemotingDirectoryService::Client,
// disabled in this process (note that shutdown != disabled).
void OnServiceDisabled();
- bool AddServiceProcessToAutoStart();
- bool RemoveServiceProcessFromAutoStart();
-
#if defined(ENABLE_REMOTING)
FRIEND_TEST_ALL_PREFIXES(ServiceProcessTest, RunChromoting);
FRIEND_TEST_ALL_PREFIXES(ServiceProcessTest, RunChromotingUntilShutdown);