summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authordmaclach@chromium.org <dmaclach@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-02-02 04:14:42 +0000
committerdmaclach@chromium.org <dmaclach@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-02-02 04:14:42 +0000
commitc3498a1c9ead237b365f316b761ea3d534b671f9 (patch)
tree7c287f6bd53347a2e91a447ce28320ae2a55fa09 /chrome
parent09f2b5517f8cb83a2a22e0249caf3dbd07dd8d08 (diff)
downloadchromium_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.cc34
-rw-r--r--chrome/browser/service/service_process_control_browsertest.cc17
-rw-r--r--chrome/common/service_process_util.cc113
-rw-r--r--chrome/common/service_process_util.h23
-rw-r--r--chrome/common/service_process_util_posix.cc220
-rw-r--r--chrome/common/service_process_util_unittest.cc140
-rw-r--r--chrome/common/service_process_util_win.cc21
-rw-r--r--chrome/service/service_ipc_server.cc2
-rw-r--r--chrome/service/service_main.cc37
-rw-r--r--chrome/service/service_process.cc7
-rw-r--r--chrome/service/service_process_prefs.cc16
-rw-r--r--chrome/service/service_process_prefs.h2
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);
};