// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/common/service_process_util_posix.h" #include "base/basictypes.h" #include "base/eintr_wrapper.h" #include "base/message_loop_proxy.h" #include "base/synchronization/waitable_event.h" namespace { int g_signal_socket = -1; } ServiceProcessShutdownMonitor::ServiceProcessShutdownMonitor( Task* shutdown_task) : shutdown_task_(shutdown_task) { } 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"; } } ServiceProcessState::StateData::StateData() : set_action_(false) { memset(sockets_, -1, sizeof(sockets_)); memset(&old_action_, 0, sizeof(old_action_)); } void ServiceProcessState::StateData::SignalReady(base::WaitableEvent* signal, bool* success) { CHECK_EQ(g_signal_socket, -1); CHECK(!signal->IsSignaled()); *success = MessageLoopForIO::current()->WatchFileDescriptor( sockets_[0], true, MessageLoopForIO::WATCH_READ, &watcher_, shut_down_monitor_.get()); if (!*success) { LOG(ERROR) << "WatchFileDescriptor"; signal->Signal(); return; } 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; *success = sigaction(SIGTERM, &action, &old_action_) == 0; if (!*success) { PLOG(ERROR) << "sigaction"; signal->Signal(); return; } // 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; #if defined(OS_LINUX) initializing_lock_.reset(); #endif // OS_LINUX #if defined(OS_MACOSX) *success = WatchExecutable(); if (!*success) { LOG(ERROR) << "WatchExecutable"; signal->Signal(); return; } #endif // OS_MACOSX signal->Signal(); } ServiceProcessState::StateData::~StateData() { if (sockets_[0] != -1) { if (HANDLE_EINTR(close(sockets_[0]))) { PLOG(ERROR) << "close"; } } if (sockets_[1] != -1) { if (HANDLE_EINTR(close(sockets_[1]))) { PLOG(ERROR) << "close"; } } if (set_action_) { if (sigaction(SIGTERM, &old_action_, NULL) < 0) { PLOG(ERROR) << "sigaction"; } } g_signal_socket = -1; } void ServiceProcessState::CreateState() { CHECK(!state_); state_ = new StateData; // Explicitly adding a reference here (and removing it in TearDownState) // because StateData is refcounted on Mac and Linux so that methods can // be called on other threads. // It is not refcounted on Windows at this time. state_->AddRef(); } bool ServiceProcessState::SignalReady( base::MessageLoopProxy* message_loop_proxy, Task* shutdown_task) { CHECK(state_); scoped_ptr scoped_shutdown_task(shutdown_task); #if defined(OS_LINUX) state_->running_lock_.reset(TakeServiceRunningLock(true)); if (state_->running_lock_.get() == NULL) { return false; } #endif // OS_LINUX state_->shut_down_monitor_.reset( new ServiceProcessShutdownMonitor(scoped_shutdown_task.release())); if (pipe(state_->sockets_) < 0) { PLOG(ERROR) << "pipe"; return false; } base::WaitableEvent signal_ready(true, false); bool success = false; message_loop_proxy->PostTask(FROM_HERE, NewRunnableMethod(state_, &ServiceProcessState::StateData::SignalReady, &signal_ready, &success)); signal_ready.Wait(); return success; } void ServiceProcessState::TearDownState() { if (state_) { state_->Release(); state_ = NULL; } }