Chromium Code Reviews| 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..603afa450feb1b356e5294ffef4c46a0bc1b7c8b 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| then is will |
|
Scott Byer
2011/02/02 00:12:38
nit: then this will
dmac
2011/02/02 00:56:25
Done.
|
| +// make multiple attempts before failing. |
| +// 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(kShutDownMessage)) && (buffer == kShutDownMessage)) { |
|
Scott Byer
2011/02/02 00:12:38
Should be sizeof(buffer) to match the write side.
dmac
2011/02/02 00:56:25
Done.
|
| + 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; |
| +} |
| + |
| +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(); |
| } |
| -void ServiceProcessState::SignalStopped() { |
| - const FilePath path = GetServiceProcessLockFilePath(); |
| - file_util::Delete(path, false); |
| - shared_mem_service_data_.reset(); |
| +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,25 @@ 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; |
| + scoped_ptr<MultiProcessLock> running_lock(TakeServiceRunningLock(false)); |
| + return running_lock.get() == NULL; |
|
Scott Byer
2011/02/02 00:12:38
Shouldn't this just call CheckServiceProcessReady(
dmac
2011/02/02 00:56:25
Got rid of it instead.
|
| } |
| - |