// Copyright (c) 2010 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/message_loop_proxy.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() {} void ServiceProcessState::StateData::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; #if defined(OS_LINUX) initializing_lock_.reset(); #endif // OS_LINUX } else { PLOG(ERROR) << "sigaction"; } } ServiceProcessState::StateData::~StateData() {} bool ServiceProcessState::InitializeState() { CHECK(!state_); state_ = new StateData; state_->AddRef(); state_->sockets_[0] = -1; state_->sockets_[1] = -1; state_->set_action_ = false; return true; } bool ServiceProcessState::SignalReady( base::MessageLoopProxy* message_loop_proxy, Task* shutdown_task) { CHECK(state_); CHECK_EQ(g_signal_socket, -1); #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(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; } 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; } }