1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/common/service_process_util_posix.h" 6 7#include "base/basictypes.h" 8#include "base/bind.h" 9#include "base/message_loop/message_loop_proxy.h" 10#include "base/posix/eintr_wrapper.h" 11#include "base/synchronization/waitable_event.h" 12#include "chrome/common/multi_process_lock.h" 13 14namespace { 15int g_signal_socket = -1; 16} 17 18// Attempts to take a lock named |name|. If |waiting| is true then this will 19// make multiple attempts to acquire the lock. 20// Caller is responsible for ownership of the MultiProcessLock. 21MultiProcessLock* TakeNamedLock(const std::string& name, bool waiting) { 22 scoped_ptr<MultiProcessLock> lock(MultiProcessLock::Create(name)); 23 if (lock == NULL) return NULL; 24 bool got_lock = false; 25 for (int i = 0; i < 10; ++i) { 26 if (lock->TryLock()) { 27 got_lock = true; 28 break; 29 } 30 if (!waiting) break; 31 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100 * i)); 32 } 33 if (!got_lock) { 34 lock.reset(); 35 } 36 return lock.release(); 37} 38 39ServiceProcessTerminateMonitor::ServiceProcessTerminateMonitor( 40 const base::Closure& terminate_task) 41 : terminate_task_(terminate_task) { 42} 43 44ServiceProcessTerminateMonitor::~ServiceProcessTerminateMonitor() { 45} 46 47void ServiceProcessTerminateMonitor::OnFileCanReadWithoutBlocking(int fd) { 48 if (!terminate_task_.is_null()) { 49 int buffer; 50 int length = read(fd, &buffer, sizeof(buffer)); 51 if ((length == sizeof(buffer)) && (buffer == kTerminateMessage)) { 52 terminate_task_.Run(); 53 terminate_task_.Reset(); 54 } else if (length > 0) { 55 DLOG(ERROR) << "Unexpected read: " << buffer; 56 } else if (length == 0) { 57 DLOG(ERROR) << "Unexpected fd close"; 58 } else if (length < 0) { 59 DPLOG(ERROR) << "read"; 60 } 61 } 62} 63 64void ServiceProcessTerminateMonitor::OnFileCanWriteWithoutBlocking(int fd) { 65 NOTIMPLEMENTED(); 66} 67 68// "Forced" Shutdowns on POSIX are done via signals. The magic signal for 69// a shutdown is SIGTERM. "write" is a signal safe function. PLOG(ERROR) is 70// not, but we don't ever expect it to be called. 71static void SigTermHandler(int sig, siginfo_t* info, void* uap) { 72 // TODO(dmaclach): add security here to make sure that we are being shut 73 // down by an appropriate process. 74 int message = ServiceProcessTerminateMonitor::kTerminateMessage; 75 if (write(g_signal_socket, &message, sizeof(message)) < 0) { 76 DPLOG(ERROR) << "write"; 77 } 78} 79 80ServiceProcessState::StateData::StateData() : set_action_(false) { 81 memset(sockets_, -1, sizeof(sockets_)); 82 memset(&old_action_, 0, sizeof(old_action_)); 83} 84 85void ServiceProcessState::StateData::SignalReady(base::WaitableEvent* signal, 86 bool* success) { 87 DCHECK_EQ(g_signal_socket, -1); 88 DCHECK(!signal->IsSignaled()); 89 *success = base::MessageLoopForIO::current()->WatchFileDescriptor( 90 sockets_[0], 91 true, 92 base::MessageLoopForIO::WATCH_READ, 93 &watcher_, 94 terminate_monitor_.get()); 95 if (!*success) { 96 DLOG(ERROR) << "WatchFileDescriptor"; 97 signal->Signal(); 98 return; 99 } 100 g_signal_socket = sockets_[1]; 101 102 // Set up signal handler for SIGTERM. 103 struct sigaction action; 104 memset(&action, 0, sizeof(action)); 105 action.sa_sigaction = SigTermHandler; 106 sigemptyset(&action.sa_mask); 107 action.sa_flags = SA_SIGINFO; 108 *success = sigaction(SIGTERM, &action, &old_action_) == 0; 109 if (!*success) { 110 DPLOG(ERROR) << "sigaction"; 111 signal->Signal(); 112 return; 113 } 114 115 // If the old_action is not default, somebody else has installed a 116 // a competing handler. Our handler is going to override it so it 117 // won't be called. If this occurs it needs to be fixed. 118 DCHECK_EQ(old_action_.sa_handler, SIG_DFL); 119 set_action_ = true; 120 121#if defined(OS_MACOSX) 122 *success = WatchExecutable(); 123 if (!*success) { 124 DLOG(ERROR) << "WatchExecutable"; 125 signal->Signal(); 126 return; 127 } 128#elif defined(OS_POSIX) 129 initializing_lock_.reset(); 130#endif // OS_POSIX 131 signal->Signal(); 132} 133 134ServiceProcessState::StateData::~StateData() { 135 if (sockets_[0] != -1) { 136 if (IGNORE_EINTR(close(sockets_[0]))) { 137 DPLOG(ERROR) << "close"; 138 } 139 } 140 if (sockets_[1] != -1) { 141 if (IGNORE_EINTR(close(sockets_[1]))) { 142 DPLOG(ERROR) << "close"; 143 } 144 } 145 if (set_action_) { 146 if (sigaction(SIGTERM, &old_action_, NULL) < 0) { 147 DPLOG(ERROR) << "sigaction"; 148 } 149 } 150 g_signal_socket = -1; 151} 152 153void ServiceProcessState::CreateState() { 154 DCHECK(!state_); 155 state_ = new StateData; 156 157 // Explicitly adding a reference here (and removing it in TearDownState) 158 // because StateData is refcounted on Mac and Linux so that methods can 159 // be called on other threads. 160 // It is not refcounted on Windows at this time. 161 state_->AddRef(); 162} 163 164bool ServiceProcessState::SignalReady( 165 base::MessageLoopProxy* message_loop_proxy, 166 const base::Closure& terminate_task) { 167 DCHECK(state_); 168 169#if defined(OS_POSIX) && !defined(OS_MACOSX) 170 state_->running_lock_.reset(TakeServiceRunningLock(true)); 171 if (state_->running_lock_.get() == NULL) { 172 return false; 173 } 174#endif 175 state_->terminate_monitor_.reset( 176 new ServiceProcessTerminateMonitor(terminate_task)); 177 if (pipe(state_->sockets_) < 0) { 178 DPLOG(ERROR) << "pipe"; 179 return false; 180 } 181 base::WaitableEvent signal_ready(true, false); 182 bool success = false; 183 184 message_loop_proxy->PostTask(FROM_HERE, 185 base::Bind(&ServiceProcessState::StateData::SignalReady, 186 state_, 187 &signal_ready, 188 &success)); 189 signal_ready.Wait(); 190 return success; 191} 192 193void ServiceProcessState::TearDownState() { 194 if (state_) { 195 state_->Release(); 196 state_ = NULL; 197 } 198} 199