summaryrefslogtreecommitdiffstats
path: root/chrome/common/service_process_util_posix.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/common/service_process_util_posix.cc')
-rw-r--r--chrome/common/service_process_util_posix.cc220
1 files changed, 187 insertions, 33 deletions
diff --git a/chrome/common/service_process_util_posix.cc b/chrome/common/service_process_util_posix.cc
index e547223..7deebaa 100644
--- a/chrome/common/service_process_util_posix.cc
+++ b/chrome/common/service_process_util_posix.cc
@@ -4,14 +4,23 @@
#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_loop_proxy.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 +30,181 @@ 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.
+} // namespace
+
+// 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.
+static 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";
+ }
+}
+
+// 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(
+ base::MessageLoopProxy* message_loop_proxy, Task* shutdown_task) {
+ CHECK(state_);
+ CHECK_EQ(g_signal_socket, -1);
+
+ 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_proxy->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;
-}
-