diff options
author | dmaclach@chromium.org <dmaclach@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-02 04:14:42 +0000 |
---|---|---|
committer | dmaclach@chromium.org <dmaclach@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-02 04:14:42 +0000 |
commit | c3498a1c9ead237b365f316b761ea3d534b671f9 (patch) | |
tree | 7c287f6bd53347a2e91a447ce28320ae2a55fa09 /chrome | |
parent | 09f2b5517f8cb83a2a22e0249caf3dbd07dd8d08 (diff) | |
download | chromium_src-c3498a1c9ead237b365f316b761ea3d534b671f9.zip chromium_src-c3498a1c9ead237b365f316b761ea3d534b671f9.tar.gz chromium_src-c3498a1c9ead237b365f316b761ea3d534b671f9.tar.bz2 |
Get service processes working on Mac and Linux.
Service processes will now launch, and attach to ipc channels.
BUG=NONE
TEST=BUILD
Review URL: http://codereview.chromium.org/6349029
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@73425 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/browser/service/service_process_control.cc | 34 | ||||
-rw-r--r-- | chrome/browser/service/service_process_control_browsertest.cc | 17 | ||||
-rw-r--r-- | chrome/common/service_process_util.cc | 113 | ||||
-rw-r--r-- | chrome/common/service_process_util.h | 23 | ||||
-rw-r--r-- | chrome/common/service_process_util_posix.cc | 220 | ||||
-rw-r--r-- | chrome/common/service_process_util_unittest.cc | 140 | ||||
-rw-r--r-- | chrome/common/service_process_util_win.cc | 21 | ||||
-rw-r--r-- | chrome/service/service_ipc_server.cc | 2 | ||||
-rw-r--r-- | chrome/service/service_main.cc | 37 | ||||
-rw-r--r-- | chrome/service/service_process.cc | 7 | ||||
-rw-r--r-- | chrome/service/service_process_prefs.cc | 16 | ||||
-rw-r--r-- | chrome/service/service_process_prefs.h | 2 |
12 files changed, 444 insertions, 188 deletions
diff --git a/chrome/browser/service/service_process_control.cc b/chrome/browser/service/service_process_control.cc index 13a8a3e4..09bc1ad 100644 --- a/chrome/browser/service/service_process_control.cc +++ b/chrome/browser/service/service_process_control.cc @@ -122,20 +122,9 @@ void ServiceProcessControl::ConnectInternal() { // TODO(hclam): Handle error connecting to channel. const std::string channel_id = GetServiceProcessChannelName(); channel_.reset( - new IPC::SyncChannel(channel_id, IPC::Channel::MODE_CLIENT, this, + new IPC::SyncChannel(channel_id, IPC::Channel::MODE_NAMED_CLIENT, this, io_thread->message_loop(), true, g_browser_process->shutdown_event())); - channel_->set_sync_messages_with_no_timeout_allowed(false); - - // We just established a channel with the service process. Notify it if an - // upgrade is available. - if (UpgradeDetector::GetInstance()->notify_upgrade()) { - Send(new ServiceMsg_UpdateAvailable); - } else { - if (registrar_.IsEmpty()) - registrar_.Add(this, NotificationType::UPGRADE_RECOMMENDED, - NotificationService::AllSources()); - } } void ServiceProcessControl::RunConnectDoneTasks() { @@ -179,14 +168,14 @@ void ServiceProcessControl::Launch(Task* success_task, Task* failure_task) { if (failure_task) connect_failure_tasks_.push_back(failure_task); - // If the service process is already running then connects to it. - if (CheckServiceProcessReady()) { - ConnectInternal(); + // If we already in the process of launching, then we are done. + if (launcher_) { return; } - // If we already in the process of launching, then we are done. - if (launcher_) { + // If the service process is already running then connects to it. + if (CheckServiceProcessReady()) { + ConnectInternal(); return; } @@ -255,6 +244,17 @@ bool ServiceProcessControl::OnMessageReceived(const IPC::Message& message) { void ServiceProcessControl::OnChannelConnected(int32 peer_pid) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + channel_->set_sync_messages_with_no_timeout_allowed(false); + + // We just established a channel with the service process. Notify it if an + // upgrade is available. + if (UpgradeDetector::GetInstance()->notify_upgrade()) { + Send(new ServiceMsg_UpdateAvailable); + } else { + if (registrar_.IsEmpty()) + registrar_.Add(this, NotificationType::UPGRADE_RECOMMENDED, + NotificationService::AllSources()); + } RunConnectDoneTasks(); } diff --git a/chrome/browser/service/service_process_control_browsertest.cc b/chrome/browser/service/service_process_control_browsertest.cc index afe4594..a0dee5d 100644 --- a/chrome/browser/service/service_process_control_browsertest.cc +++ b/chrome/browser/service/service_process_control_browsertest.cc @@ -71,7 +71,8 @@ class ServiceProcessControlBrowserTest } void ProcessControlLaunched() { - base::ProcessId service_pid = GetServiceProcessPid(); + base::ProcessId service_pid; + EXPECT_TRUE(GetServiceProcessSharedData(NULL, &service_pid)); EXPECT_NE(static_cast<base::ProcessId>(0), service_pid); EXPECT_TRUE(base::OpenProcessHandleWithAccess( service_pid, @@ -96,7 +97,6 @@ class ServiceProcessControlBrowserTest base::ProcessHandle service_process_handle_; }; -#if defined(OS_WIN) // They way that the IPC is implemented only works on windows. This has to // change when we implement a different scheme for IPC. // Times out flakily, http://crbug.com/70076. @@ -193,18 +193,21 @@ IN_PROC_BROWSER_TEST_F(ServiceProcessControlBrowserTest, LaunchServiceProcessControl(); // Make sure we are connected to the service process. EXPECT_TRUE(process()->is_connected()); + base::ProcessId service_pid; + EXPECT_TRUE(GetServiceProcessSharedData(NULL, &service_pid)); + EXPECT_NE(static_cast<base::ProcessId>(0), service_pid); chrome::VersionInfo version_info; - ForceServiceProcessShutdown(version_info.Version()); + ForceServiceProcessShutdown(version_info.Version(), service_pid); WaitForShutdown(); } IN_PROC_BROWSER_TEST_F(ServiceProcessControlBrowserTest, CheckPid) { - EXPECT_EQ(0, GetServiceProcessPid()); + base::ProcessId service_pid; + EXPECT_FALSE(GetServiceProcessSharedData(NULL, &service_pid)); // Launch the service process. LaunchServiceProcessControl(); - EXPECT_NE(static_cast<base::ProcessId>(0), GetServiceProcessPid()); + EXPECT_TRUE(GetServiceProcessSharedData(NULL, &service_pid)); + EXPECT_NE(static_cast<base::ProcessId>(0), service_pid); } -#endif - DISABLE_RUNNABLE_METHOD_REFCOUNT(ServiceProcessControlBrowserTest); diff --git a/chrome/common/service_process_util.cc b/chrome/common/service_process_util.cc index 4062e0f..458f662 100644 --- a/chrome/common/service_process_util.cc +++ b/chrome/common/service_process_util.cc @@ -6,10 +6,13 @@ #include "base/file_util.h" #include "base/logging.h" +#include "base/mac/scoped_nsautorelease_pool.h" #include "base/path_service.h" #include "base/process_util.h" +#include "base/sha1.h" #include "base/singleton.h" #include "base/string16.h" +#include "base/string_number_conversions.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" #include "base/version.h" @@ -36,29 +39,6 @@ std::string GetServiceProcessSharedMemName() { return GetServiceProcessScopedName("_service_shmem"); } -// Reads the named shared memory to get the shared data. Returns false if no -// matching shared memory was found. -bool GetServiceProcessSharedData(std::string* version, base::ProcessId* pid) { - scoped_ptr<base::SharedMemory> shared_mem_service_data; - shared_mem_service_data.reset(new base::SharedMemory()); - ServiceProcessSharedData* service_data = NULL; - if (shared_mem_service_data.get() && - shared_mem_service_data->Open(GetServiceProcessSharedMemName(), true) && - shared_mem_service_data->Map(sizeof(ServiceProcessSharedData))) { - service_data = reinterpret_cast<ServiceProcessSharedData*>( - shared_mem_service_data->memory()); - // Make sure the version in shared memory is null-terminated. If it is not, - // treat it as invalid. - if (version && memchr(service_data->service_process_version, '\0', - sizeof(service_data->service_process_version))) - *version = service_data->service_process_version; - if (pid) - *pid = service_data->service_process_pid; - return true; - } - return false; -} - enum ServiceProcessRunningState { SERVICE_NOT_RUNNING, SERVICE_OLDER_VERSION_RUNNING, @@ -67,12 +47,20 @@ enum ServiceProcessRunningState { }; ServiceProcessRunningState GetServiceProcessRunningState( - std::string* service_version_out) { + std::string* service_version_out, base::ProcessId* pid_out) { std::string version; - GetServiceProcessSharedData(&version, NULL); - if (version.empty()) + if (!GetServiceProcessSharedData(&version, pid_out)) return SERVICE_NOT_RUNNING; +#if defined(OS_POSIX) + // We only need to check for service running on POSIX because Windows cleans + // up shared memory files when an app crashes, so there isn't a chance of + // us reading bogus data from shared memory for an app that has died. + if (!CheckServiceProcessReady()) { + return SERVICE_NOT_RUNNING; + } +#endif // defined(OS_POSIX) + // At this time we have a version string. Set the out param if it exists. if (service_version_out) *service_version_out = version; @@ -107,23 +95,22 @@ ServiceProcessRunningState GetServiceProcessRunningState( return SERVICE_SAME_VERSION_RUNNING; } - } // namespace // Return a name that is scoped to this instance of the service process. We -// use the user-data-dir as a scoping prefix. +// use the hash of the user-data-dir as a scoping prefix. We can't use +// the user-data-dir itself as we have limits on the size of the lock names. 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()); + std::string user_data_dir_path = WideToUTF8(user_data_dir.value()); #elif defined(OS_POSIX) - std::string scoped_name = user_data_dir.value(); + std::string user_data_dir_path = 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; + std::string hash = base::SHA1HashString(user_data_dir_path); + std::string hex_hash = base::HexEncode(hash.c_str(), hash.length()); + return hex_hash + "." + append_str; } // Return a name that is scoped to this instance of the service process. We @@ -143,16 +130,40 @@ std::string GetServiceProcessChannelName() { return GetServiceProcessScopedVersionedName("_service_ipc"); } -base::ProcessId GetServiceProcessPid() { - base::ProcessId pid = 0; - GetServiceProcessSharedData(NULL, &pid); - return pid; +// Reads the named shared memory to get the shared data. Returns false if no +// matching shared memory was found. +bool GetServiceProcessSharedData(std::string* version, base::ProcessId* pid) { + scoped_ptr<base::SharedMemory> shared_mem_service_data; + shared_mem_service_data.reset(new base::SharedMemory()); + ServiceProcessSharedData* service_data = NULL; + if (shared_mem_service_data.get() && + shared_mem_service_data->Open(GetServiceProcessSharedMemName(), true) && + shared_mem_service_data->Map(sizeof(ServiceProcessSharedData))) { + service_data = reinterpret_cast<ServiceProcessSharedData*>( + shared_mem_service_data->memory()); + // Make sure the version in shared memory is null-terminated. If it is not, + // treat it as invalid. + if (version && memchr(service_data->service_process_version, '\0', + sizeof(service_data->service_process_version))) + *version = service_data->service_process_version; + if (pid) + *pid = service_data->service_process_pid; + return true; + } + return false; } ServiceProcessState::ServiceProcessState() : state_(NULL) { } ServiceProcessState::~ServiceProcessState() { + if (shared_mem_service_data_.get()) { + // Delete needs a pool wrapped around it because it calls some Obj-C on Mac, + // and since ServiceProcessState is a singleton, it gets destructed after + // the standard NSAutoreleasePools have already been cleaned up. + base::mac::ScopedNSAutoreleasePool pool; + shared_mem_service_data_->Delete(GetServiceProcessSharedMemName()); + } TearDownState(); } @@ -167,30 +178,26 @@ bool ServiceProcessState::Initialize() { } // Now that we have the singleton, take care of killing an older version, if // it exists. - if (ShouldHandleOtherVersion() && !HandleOtherVersion()) + if (!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. + // 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; + base::ProcessId process_id; ServiceProcessRunningState state = - GetServiceProcessRunningState(&running_version); + GetServiceProcessRunningState(&running_version, &process_id); switch (state) { case SERVICE_SAME_VERSION_RUNNING: case SERVICE_NEWER_VERSION_RUNNING: return false; case SERVICE_OLDER_VERSION_RUNNING: // If an older version is running, kill it. - ForceServiceProcessShutdown(running_version); + ForceServiceProcessShutdown(running_version, process_id); break; case SERVICE_NOT_RUNNING: break; @@ -211,8 +218,8 @@ bool ServiceProcessState::CreateSharedData() { return false; } - scoped_ptr<base::SharedMemory> shared_mem_service_data; - shared_mem_service_data.reset(new base::SharedMemory()); + scoped_ptr<base::SharedMemory> shared_mem_service_data( + new base::SharedMemory()); if (!shared_mem_service_data.get()) return false; @@ -235,7 +242,11 @@ bool ServiceProcessState::CreateSharedData() { return true; } - std::string ServiceProcessState::GetAutoRunKey() { return GetServiceProcessScopedName("_service_run"); } + +void ServiceProcessState::SignalStopped() { + TearDownState(); + shared_mem_service_data_.reset(); +} diff --git a/chrome/common/service_process_util.h b/chrome/common/service_process_util.h index 6f2685e..c3ad259 100644 --- a/chrome/common/service_process_util.h +++ b/chrome/common/service_process_util.h @@ -10,7 +10,9 @@ #include "base/process.h" #include "base/scoped_ptr.h" #include "base/shared_memory.h" -#include "base/task.h" + +class Task; +class MessageLoop; template <typename T> struct DefaultSingletonTraits; @@ -33,16 +35,17 @@ std::string GetServiceProcessScopedVersionedName(const std::string& append_str); // IPC commands. bool CheckServiceProcessReady(); -// Returns the process id of the currently running service process. Returns 0 -// if no service process is running. +// Returns the process id and version of the currently running service process. // Note: DO NOT use this check whether the service process is ready because -// a non-zero return value only means that the process is running and not that -// it is ready to receive IPC commands. This method is only exposed for testing. -base::ProcessId GetServiceProcessPid(); +// a true return value only means that some process shared data was available, +// and not that the process is ready to receive IPC commands, or even running. +// This method is only exposed for testing. +bool GetServiceProcessSharedData(std::string* version, base::ProcessId* pid); // -------------------------------------------------------------------------- // Forces a service process matching the specified version to shut down. -bool ForceServiceProcessShutdown(const std::string& version); +bool ForceServiceProcessShutdown(const std::string& version, + base::ProcessId process_id); // 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 @@ -61,7 +64,9 @@ class ServiceProcessState { // 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); + // |message_loop| must be of type IO and is the loop that POSIX uses + // to monitor the service process. + bool SignalReady(MessageLoop *message_loop, Task* shutdown_task); // Signal that the service process is stopped. void SignalStopped(); @@ -93,8 +98,6 @@ class ServiceProcessState { // 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; diff --git a/chrome/common/service_process_util_posix.cc b/chrome/common/service_process_util_posix.cc index e547223..6abcf27 100644 --- a/chrome/common/service_process_util_posix.cc +++ b/chrome/common/service_process_util_posix.cc @@ -4,14 +4,22 @@ #include "chrome/common/service_process_util.h" +#include <signal.h> +#include <unistd.h> + #include "base/file_util.h" #include "base/logging.h" +#include "base/message_loop.h" +#include "base/message_pump_libevent.h" #include "base/path_service.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_version_info.h" +#include "chrome/common/multi_process_lock.h" namespace { +int g_signal_socket = -1; + // Gets the name of the lock file for service process. FilePath GetServiceProcessLockFilePath() { FilePath user_data_dir; @@ -21,45 +29,182 @@ FilePath GetServiceProcessLockFilePath() { return user_data_dir.Append(lock_file_name); } -} // namespace +// Attempts to take a lock named |name|. If |waiting| is true then this will +// make multiple attempts to acquire the lock. +// Caller is responsible for ownership of the MultiProcessLock. +MultiProcessLock* TakeNamedLock(const std::string& name, bool waiting) { + scoped_ptr<MultiProcessLock> lock(MultiProcessLock::Create(name)); + if (lock == NULL) return NULL; + bool got_lock = false; + for (int i = 0; i < 10; ++i) { + if (lock->TryLock()) { + got_lock = true; + break; + } + if (!waiting) break; + base::PlatformThread::Sleep(100 * i); + } + if (!got_lock) { + lock.reset(); + } + return lock.release(); +} -bool ForceServiceProcessShutdown(const std::string& version) { - NOTIMPLEMENTED(); - return false; +MultiProcessLock* TakeServiceRunningLock(bool waiting) { + std::string lock_name = + GetServiceProcessScopedName("_service_running"); + return TakeNamedLock(lock_name, waiting); } -bool CheckServiceProcessReady() { - const FilePath path = GetServiceProcessLockFilePath(); - return file_util::PathExists(path); +MultiProcessLock* TakeServiceInitializingLock(bool waiting) { + std::string lock_name = + GetServiceProcessScopedName("_service_initializing"); + return TakeNamedLock(lock_name, waiting); } -struct ServiceProcessState::StateData { - // No state yet for Posix. +// Watches for |kShutDownMessage| to be written to the file descriptor it is +// watching. When it reads |kShutDownMessage|, it performs |shutdown_task_|. +// Used here to monitor the socket listening to g_signal_socket. +class ServiceProcessShutdownMonitor + : public base::MessagePumpLibevent::Watcher { + public: + + enum { + kShutDownMessage = 0xdecea5e + }; + + explicit ServiceProcessShutdownMonitor(Task* shutdown_task) + : shutdown_task_(shutdown_task) { + } + + virtual ~ServiceProcessShutdownMonitor(); + + virtual void OnFileCanReadWithoutBlocking(int fd); + virtual void OnFileCanWriteWithoutBlocking(int fd); + + private: + scoped_ptr<Task> shutdown_task_; }; -bool ServiceProcessState::TakeSingletonLock() { - // TODO(sanjeevr): Implement singleton mechanism for POSIX. +ServiceProcessShutdownMonitor::~ServiceProcessShutdownMonitor() { +} + +void ServiceProcessShutdownMonitor::OnFileCanReadWithoutBlocking(int fd) { + if (shutdown_task_.get()) { + int buffer; + int length = read(fd, &buffer, sizeof(buffer)); + if ((length == sizeof(buffer)) && (buffer == kShutDownMessage)) { + shutdown_task_->Run(); + shutdown_task_.reset(); + } else if (length > 0) { + LOG(ERROR) << "Unexpected read: " << buffer; + } else if (length == 0) { + LOG(ERROR) << "Unexpected fd close"; + } else if (length < 0) { + PLOG(ERROR) << "read"; + } + } +} + +void ServiceProcessShutdownMonitor::OnFileCanWriteWithoutBlocking(int fd) { NOTIMPLEMENTED(); +} + +// "Forced" Shutdowns on POSIX are done via signals. The magic signal for +// a shutdown is SIGTERM. "write" is a signal safe function. PLOG(ERROR) is +// not, but we don't ever expect it to be called. +void SigTermHandler(int sig, siginfo_t* info, void* uap) { + // TODO(dmaclach): add security here to make sure that we are being shut + // down by an appropriate process. + int message = ServiceProcessShutdownMonitor::kShutDownMessage; + if (write(g_signal_socket, &message, sizeof(message)) < 0) { + PLOG(ERROR) << "write"; + } +} + +} // namespace + +// See comment for SigTermHandler. +bool ForceServiceProcessShutdown(const std::string& version, + base::ProcessId process_id) { + if (kill(process_id, SIGTERM) < 0) { + PLOG(ERROR) << "kill"; + return false; + } 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; - VLOG(1) << "Created Service Process lock file: " << path.value(); - file_util::TruncateFile(file) && file_util::CloseFile(file); +bool CheckServiceProcessReady() { + scoped_ptr<MultiProcessLock> running_lock(TakeServiceRunningLock(false)); + return running_lock.get() == NULL; } -void ServiceProcessState::SignalStopped() { - const FilePath path = GetServiceProcessLockFilePath(); - file_util::Delete(path, false); - shared_mem_service_data_.reset(); +struct ServiceProcessState::StateData + : public base::RefCountedThreadSafe<ServiceProcessState::StateData> { + scoped_ptr<MultiProcessLock> initializing_lock_; + scoped_ptr<MultiProcessLock> running_lock_; + scoped_ptr<ServiceProcessShutdownMonitor> shut_down_monitor_; + base::MessagePumpLibevent::FileDescriptorWatcher watcher_; + int sockets_[2]; + struct sigaction old_action_; + bool set_action_; + + // WatchFileDescriptor needs to be set up by the thread that is going + // to be monitoring it. + void SignalReady() { + CHECK(MessageLoopForIO::current()->WatchFileDescriptor( + sockets_[0], true, MessageLoopForIO::WATCH_READ, + &watcher_, shut_down_monitor_.get())); + g_signal_socket = sockets_[1]; + + // Set up signal handler for SIGTERM. + struct sigaction action; + action.sa_sigaction = SigTermHandler; + sigemptyset(&action.sa_mask); + action.sa_flags = SA_SIGINFO; + if (sigaction(SIGTERM, &action, &old_action_) == 0) { + // If the old_action is not default, somebody else has installed a + // a competing handler. Our handler is going to override it so it + // won't be called. If this occurs it needs to be fixed. + DCHECK_EQ(old_action_.sa_handler, SIG_DFL); + set_action_ = true; + initializing_lock_.reset(); + } else { + PLOG(ERROR) << "sigaction"; + } + } +}; + +bool ServiceProcessState::TakeSingletonLock() { + CHECK(!state_); + state_ = new StateData; + state_->AddRef(); + state_->sockets_[0] = -1; + state_->sockets_[1] = -1; + state_->set_action_ = false; + state_->initializing_lock_.reset(TakeServiceInitializingLock(true)); + return state_->initializing_lock_.get(); +} + +bool ServiceProcessState::SignalReady(MessageLoop* message_loop, + Task* shutdown_task) { + CHECK(state_); + CHECK_EQ(g_signal_socket, -1); + DCHECK_EQ(message_loop->type(), MessageLoop::TYPE_IO); + + state_->running_lock_.reset(TakeServiceRunningLock(true)); + if (state_->running_lock_.get() == NULL) { + return false; + } + state_->shut_down_monitor_.reset( + new ServiceProcessShutdownMonitor(shutdown_task)); + if (pipe(state_->sockets_) < 0) { + PLOG(ERROR) << "pipe"; + return false; + } + message_loop->PostTask(FROM_HERE, + NewRunnableMethod(state_, &ServiceProcessState::StateData::SignalReady)); + return true; } bool ServiceProcessState::AddToAutoRun() { @@ -73,11 +218,20 @@ bool ServiceProcessState::RemoveFromAutoRun() { } void ServiceProcessState::TearDownState() { + g_signal_socket = -1; + if (state_) { + if (state_->sockets_[0] != -1) { + close(state_->sockets_[0]); + } + if (state_->sockets_[1] != -1) { + close(state_->sockets_[1]); + } + if (state_->set_action_) { + if (sigaction(SIGTERM, &state_->old_action_, NULL) < 0) { + PLOG(ERROR) << "sigaction"; + } + } + state_->Release(); + state_ = NULL; + } } - -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 index 58b68e1..b65812d 100644 --- a/chrome/common/service_process_util_unittest.cc +++ b/chrome/common/service_process_util_unittest.cc @@ -5,10 +5,25 @@ #include "base/at_exit.h" #include "base/process_util.h" #include "base/string_util.h" +#include "base/test/multiprocess_test.h" +#include "base/test/test_timeouts.h" +#include "base/threading/thread.h" #include "chrome/common/chrome_version_info.h" #include "chrome/common/service_process_util.h" -#include "testing/gtest/include/gtest/gtest.h" +#include "testing/multiprocess_func_list.h" +namespace { + +bool g_good_shutdown = false; + +void ShutdownTask(MessageLoop* loop) { + // Quit the main message loop. + ASSERT_FALSE(g_good_shutdown); + g_good_shutdown = true; + loop->PostTask(FROM_HERE, new MessageLoop::QuitTask()); +} + +} // namespace TEST(ServiceProcessUtilTest, ScopedVersionedName) { std::string test_str = "test"; @@ -19,55 +34,124 @@ TEST(ServiceProcessUtilTest, ScopedVersionedName) { EXPECT_NE(std::string::npos, scoped_name.find(version_info.Version())); } -class ServiceProcessStateTest : public testing::Test { +class ServiceProcessStateTest : public base::MultiProcessTest { + public: + ServiceProcessStateTest(); + ~ServiceProcessStateTest(); + virtual void SetUp(); + MessageLoop* IOMessageLoop() { return io_thread_.message_loop(); } + void LaunchAndWait(const std::string& name); + private: // This is used to release the ServiceProcessState singleton after each test. base::ShadowingAtExitManager at_exit_manager_; + base::Thread io_thread_; }; -#if defined(OS_WIN) -// Singleton-ness is only implemented on Windows. -// TODO(sanjeev): Rewrite this test to spawn a new process and test using the -// ServiceProcessState singleton across processes. -/* +ServiceProcessStateTest::ServiceProcessStateTest() + : io_thread_("ServiceProcessStateTestThread") { +} + +ServiceProcessStateTest::~ServiceProcessStateTest() { +} + +void ServiceProcessStateTest::SetUp() { + base::Thread::Options options(MessageLoop::TYPE_IO, 0); + ASSERT_TRUE(io_thread_.StartWithOptions(options)); +} + +void ServiceProcessStateTest::LaunchAndWait(const std::string& name) { + base::ProcessHandle handle = SpawnChild(name, false); + ASSERT_TRUE(handle); + int exit_code = 0; + ASSERT_TRUE(base::WaitForExitCode(handle, &exit_code)); + ASSERT_EQ(exit_code, 0); +} + TEST_F(ServiceProcessStateTest, Singleton) { - ServiceProcessState state; - EXPECT_TRUE(state.Initialize()); - // The second instance should fail to Initialize. - ServiceProcessState another_state; - EXPECT_FALSE(another_state.Initialize()); + ServiceProcessState* state = ServiceProcessState::GetInstance(); + ASSERT_TRUE(state->Initialize()); + LaunchAndWait("ServiceProcessStateTestSingleton"); } -*/ -#endif // defined(OS_WIN) TEST_F(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) + ASSERT_FALSE(CheckServiceProcessReady()); ServiceProcessState* state = ServiceProcessState::GetInstance(); - EXPECT_TRUE(state->Initialize()); - state->SignalReady(NULL); - EXPECT_TRUE(CheckServiceProcessReady()); + ASSERT_TRUE(state->Initialize()); + ASSERT_TRUE(state->SignalReady(IOMessageLoop(), NULL)); + LaunchAndWait("ServiceProcessStateTestReadyTrue"); state->SignalStopped(); - EXPECT_FALSE(CheckServiceProcessReady()); + LaunchAndWait("ServiceProcessStateTestReadyFalse"); } TEST_F(ServiceProcessStateTest, SharedMem) { + std::string version; + base::ProcessId pid; #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()); + ASSERT_FALSE(GetServiceProcessSharedData(&version, &pid)); #endif // defined(OS_WIN) ServiceProcessState* state = ServiceProcessState::GetInstance(); + ASSERT_TRUE(state->Initialize()); + ASSERT_TRUE(GetServiceProcessSharedData(&version, &pid)); + ASSERT_EQ(base::GetCurrentProcId(), pid); +} + +TEST_F(ServiceProcessStateTest, ForceShutdown) { + base::ProcessHandle handle = SpawnChild("ServiceProcessStateTestShutdown", + true); + ASSERT_TRUE(handle); + for (int i = 0; !CheckServiceProcessReady() && i < 10; ++i) { + base::PlatformThread::Sleep(TestTimeouts::tiny_timeout_ms()); + } + ASSERT_TRUE(CheckServiceProcessReady()); + std::string version; + base::ProcessId pid; + ASSERT_TRUE(GetServiceProcessSharedData(&version, &pid)); + ASSERT_TRUE(ForceServiceProcessShutdown(version, pid)); + int exit_code = 0; + ASSERT_TRUE(base::WaitForExitCodeWithTimeout(handle, + &exit_code, TestTimeouts::action_timeout_ms() * 2)); + ASSERT_EQ(exit_code, 0); +} + +MULTIPROCESS_TEST_MAIN(ServiceProcessStateTestSingleton) { + ServiceProcessState* state = ServiceProcessState::GetInstance(); + EXPECT_FALSE(state->Initialize()); + return 0; +} + +MULTIPROCESS_TEST_MAIN(ServiceProcessStateTestReadyTrue) { + EXPECT_TRUE(CheckServiceProcessReady()); + return 0; +} + +MULTIPROCESS_TEST_MAIN(ServiceProcessStateTestReadyFalse) { + EXPECT_FALSE(CheckServiceProcessReady()); + return 0; +} + +MULTIPROCESS_TEST_MAIN(ServiceProcessStateTestShutdown) { + MessageLoop message_loop; + message_loop.set_thread_name("ServiceProcessStateTestShutdownMainThread"); + base::Thread io_thread_("ServiceProcessStateTestShutdownIOThread"); + base::Thread::Options options(MessageLoop::TYPE_IO, 0); + EXPECT_TRUE(io_thread_.StartWithOptions(options)); + ServiceProcessState* state = ServiceProcessState::GetInstance(); EXPECT_TRUE(state->Initialize()); - EXPECT_EQ(base::GetCurrentProcId(), GetServiceProcessPid()); + EXPECT_TRUE(state->SignalReady(io_thread_.message_loop(), + NewRunnableFunction(&ShutdownTask, + MessageLoop::current()))); + message_loop.PostDelayedTask(FROM_HERE, + new MessageLoop::QuitTask(), + TestTimeouts::action_max_timeout_ms()); + EXPECT_FALSE(g_good_shutdown); + message_loop.Run(); + EXPECT_TRUE(g_good_shutdown); + return 0; } diff --git a/chrome/common/service_process_util_win.cc b/chrome/common/service_process_util_win.cc index eb784a5..e78bd73 100644 --- a/chrome/common/service_process_util_win.cc +++ b/chrome/common/service_process_util_win.cc @@ -9,6 +9,7 @@ #include "base/logging.h" #include "base/path_service.h" #include "base/string16.h" +#include "base/task.h" #include "base/utf_string_conversions.h" #include "base/win/object_watcher.h" #include "base/win/scoped_handle.h" @@ -54,7 +55,8 @@ class ServiceProcessShutdownMonitor } // namespace -bool ForceServiceProcessShutdown(const std::string& version) { +bool ForceServiceProcessShutdown(const std::string& version, + base::ProcessId process_id) { base::win::ScopedHandle shutdown_event; std::string versioned_name = version; versioned_name.append("_service_shutdown_evt"); @@ -99,20 +101,19 @@ bool ServiceProcessState::TakeSingletonLock() { return true; } -void ServiceProcessState::SignalReady(Task* shutdown_task) { +bool ServiceProcessState::SignalReady(MessageLoop *message_loop, + Task* shutdown_task) { DCHECK(state_); DCHECK(state_->ready_event.IsValid()); - SetEvent(state_->ready_event.Get()); + if (!SetEvent(state_->ready_event.Get())) { + return false; + } if (shutdown_task) { state_->shutdown_monitor.reset( new ServiceProcessShutdownMonitor(shutdown_task)); state_->shutdown_monitor->Start(); } -} - -void ServiceProcessState::SignalStopped() { - TearDownState(); - shared_mem_service_data_.reset(); + return true; } bool ServiceProcessState::AddToAutoRun() { @@ -140,7 +141,3 @@ void ServiceProcessState::TearDownState() { delete state_; state_ = NULL; } - -bool ServiceProcessState::ShouldHandleOtherVersion() { - return true; -} diff --git a/chrome/service/service_ipc_server.cc b/chrome/service/service_ipc_server.cc index 24f7600..5e503c8 100644 --- a/chrome/service/service_ipc_server.cc +++ b/chrome/service/service_ipc_server.cc @@ -25,7 +25,7 @@ bool ServiceIPCServer::Init() { void ServiceIPCServer::CreateChannel() { channel_.reset(new IPC::SyncChannel(channel_name_, - IPC::Channel::MODE_SERVER, this, + IPC::Channel::MODE_NAMED_SERVER, this, g_service_process->io_thread()->message_loop(), true, g_service_process->shutdown_event())); DCHECK(sync_message_filter_.get()); diff --git a/chrome/service/service_main.cc b/chrome/service/service_main.cc index 67f7c94..722ce3d 100644 --- a/chrome/service/service_main.cc +++ b/chrome/service/service_main.cc @@ -4,53 +4,54 @@ #include "base/debug/debugger.h" #include "base/message_loop.h" -#include "base/path_service.h" -#include "base/singleton.h" -#include "base/threading/platform_thread.h" -#include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/main_function_params.h" -#include "chrome/common/sandbox_policy.h" #include "chrome/common/service_process_util.h" -#include "chrome/service/cloud_print/cloud_print_proxy.h" #include "chrome/service/service_process.h" -#if defined(OS_MACOSX) +#if defined(OS_WIN) +#include "chrome/common/sandbox_policy.h" +#elif defined(OS_MACOSX) #include "chrome/common/chrome_application_mac.h" -#endif +#endif // defined(OS_WIN) // Mainline routine for running as the service process. int ServiceProcessMain(const MainFunctionParams& parameters) { - // If there is already a service process running, quit now. - if (!ServiceProcessState::GetInstance()->Initialize()) - return 0; - MessageLoopForUI main_message_loop; + main_message_loop.set_thread_name("MainThread"); if (parameters.command_line_.HasSwitch(switches::kWaitForDebugger)) { base::debug::WaitForDebugger(60, true); } + VLOG(1) << "Service process launched: " + << parameters.command_line_.command_line_string(); + #if defined(OS_MACOSX) chrome_application_mac::RegisterCrApp(); #endif base::PlatformThread::SetName("CrServiceMain"); + // If there is already a service process running, quit now. + if (!ServiceProcessState::GetInstance()->Initialize()) + return 0; + #if defined(OS_WIN) sandbox::BrokerServices* broker_services = parameters.sandbox_info_.BrokerServices(); if (broker_services) sandbox::InitBrokerServices(broker_services); -#endif // defined(OS_WIN) +#elif defined(OS_MACOSX) + chrome_application_mac::RegisterCrApp(); +#endif // defined(OS_WIN) ServiceProcess service_process; - if (!service_process.Initialize(&main_message_loop, - parameters.command_line_)) { + if (service_process.Initialize(&main_message_loop, + parameters.command_line_)) { + MessageLoop::current()->Run(); + } else { LOG(ERROR) << "Service process failed to initialize"; - return 0; } - - MessageLoop::current()->Run(); service_process.Teardown(); return 0; } diff --git a/chrome/service/service_process.cc b/chrome/service/service_process.cc index c521e32..4183fae 100644 --- a/chrome/service/service_process.cc +++ b/chrome/service/service_process.cc @@ -141,8 +141,11 @@ bool ServiceProcess::Initialize(MessageLoop* message_loop, // After the IPC server has started we signal that the service process is // ready. - ServiceProcessState::GetInstance()->SignalReady( - NewRunnableMethod(this, &ServiceProcess::Shutdown)); + if (!ServiceProcessState::GetInstance()->SignalReady( + io_thread_->message_loop(), + NewRunnableMethod(this, &ServiceProcess::Shutdown))) { + return false; + } // See if we need to stay running. ScheduleShutdownCheck(); diff --git a/chrome/service/service_process_prefs.cc b/chrome/service/service_process_prefs.cc index 182b92f..d0e4796 100644 --- a/chrome/service/service_process_prefs.cc +++ b/chrome/service/service_process_prefs.cc @@ -9,43 +9,43 @@ ServiceProcessPrefs::ServiceProcessPrefs( const FilePath& pref_filename, base::MessageLoopProxy* file_message_loop_proxy) - : prefs_(pref_filename, file_message_loop_proxy) { + : prefs_(new JsonPrefStore(pref_filename, file_message_loop_proxy)) { } void ServiceProcessPrefs::ReadPrefs() { - prefs_.ReadPrefs(); + prefs_->ReadPrefs(); } void ServiceProcessPrefs::WritePrefs() { - prefs_.WritePrefs(); + prefs_->WritePrefs(); } void ServiceProcessPrefs::GetString(const std::string& key, std::string* result) { Value* value; - if (prefs_.GetValue(key, &value) == PersistentPrefStore::READ_OK) + if (prefs_->GetValue(key, &value) == PersistentPrefStore::READ_OK) value->GetAsString(result); } void ServiceProcessPrefs::SetString(const std::string& key, const std::string& value) { - prefs_.SetValue(key, Value::CreateStringValue(value)); + prefs_->SetValue(key, Value::CreateStringValue(value)); } void ServiceProcessPrefs::GetBoolean(const std::string& key, bool* result) { Value* value; - if (prefs_.GetValue(key, &value) == PersistentPrefStore::READ_OK) + if (prefs_->GetValue(key, &value) == PersistentPrefStore::READ_OK) value->GetAsBoolean(result); } void ServiceProcessPrefs::SetBoolean(const std::string& key, bool value) { - prefs_.SetValue(key, Value::CreateBooleanValue(value)); + prefs_->SetValue(key, Value::CreateBooleanValue(value)); } void ServiceProcessPrefs::GetDictionary(const std::string& key, DictionaryValue** result) { Value* value; - if (prefs_.GetValue(key, &value) != PersistentPrefStore::READ_OK || + if (prefs_->GetValue(key, &value) != PersistentPrefStore::READ_OK || !value->IsType(Value::TYPE_DICTIONARY)) { return; } diff --git a/chrome/service/service_process_prefs.h b/chrome/service/service_process_prefs.h index ce86b03..a8cc3ce 100644 --- a/chrome/service/service_process_prefs.h +++ b/chrome/service/service_process_prefs.h @@ -41,7 +41,7 @@ class ServiceProcessPrefs { void GetDictionary(const std::string& key, DictionaryValue** result); private: - JsonPrefStore prefs_; + scoped_refptr<JsonPrefStore> prefs_; DISALLOW_COPY_AND_ASSIGN(ServiceProcessPrefs); }; |