diff options
-rw-r--r-- | chrome/chrome_common.gypi | 2 | ||||
-rw-r--r-- | chrome/chrome_tests.gypi | 1 | ||||
-rw-r--r-- | chrome/common/service_process_util.cc | 257 | ||||
-rw-r--r-- | chrome/common/service_process_util.h | 91 | ||||
-rw-r--r-- | chrome/common/service_process_util_posix.cc | 83 | ||||
-rw-r--r-- | chrome/common/service_process_util_unittest.cc | 62 | ||||
-rw-r--r-- | chrome/common/service_process_util_win.cc | 145 | ||||
-rw-r--r-- | chrome/service/service_main.cc | 3 | ||||
-rw-r--r-- | chrome/service/service_process.cc | 43 | ||||
-rw-r--r-- | chrome/service/service_process.h | 3 |
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); |