| Index: chrome/common/service_process_util_posix.cc
|
| diff --git a/chrome/common/service_process_util_posix.cc b/chrome/common/service_process_util_posix.cc
|
| index e547223865decae38b3fd719bbb2f8fea2f75cd0..6abcf274a72ceeaeef5ab4dd047483d7084ddf2e 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;
|
| -}
|
| -
|
|
|