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; |
-} |
- |