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