1// Copyright (c) 2011 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/eintr_wrapper.h"
9#include "base/message_loop_proxy.h"
10#include "base/synchronization/waitable_event.h"
11
12namespace {
13int g_signal_socket = -1;
14}
15
16ServiceProcessShutdownMonitor::ServiceProcessShutdownMonitor(
17    Task* shutdown_task)
18    : shutdown_task_(shutdown_task) {
19}
20
21ServiceProcessShutdownMonitor::~ServiceProcessShutdownMonitor() {
22}
23
24void ServiceProcessShutdownMonitor::OnFileCanReadWithoutBlocking(int fd) {
25  if (shutdown_task_.get()) {
26    int buffer;
27    int length = read(fd, &buffer, sizeof(buffer));
28    if ((length == sizeof(buffer)) && (buffer == kShutDownMessage)) {
29      shutdown_task_->Run();
30      shutdown_task_.reset();
31    } else if (length > 0) {
32      LOG(ERROR) << "Unexpected read: " << buffer;
33    } else if (length == 0) {
34      LOG(ERROR) << "Unexpected fd close";
35    } else if (length < 0) {
36      PLOG(ERROR) << "read";
37    }
38  }
39}
40
41void ServiceProcessShutdownMonitor::OnFileCanWriteWithoutBlocking(int fd) {
42  NOTIMPLEMENTED();
43}
44
45// "Forced" Shutdowns on POSIX are done via signals. The magic signal for
46// a shutdown is SIGTERM. "write" is a signal safe function. PLOG(ERROR) is
47// not, but we don't ever expect it to be called.
48static void SigTermHandler(int sig, siginfo_t* info, void* uap) {
49  // TODO(dmaclach): add security here to make sure that we are being shut
50  //                 down by an appropriate process.
51  int message = ServiceProcessShutdownMonitor::kShutDownMessage;
52  if (write(g_signal_socket, &message, sizeof(message)) < 0) {
53    PLOG(ERROR) << "write";
54  }
55}
56
57ServiceProcessState::StateData::StateData() : set_action_(false) {
58  memset(sockets_, -1, sizeof(sockets_));
59  memset(&old_action_, 0, sizeof(old_action_));
60}
61
62void ServiceProcessState::StateData::SignalReady(base::WaitableEvent* signal,
63                                                 bool* success) {
64  CHECK_EQ(g_signal_socket, -1);
65  CHECK(!signal->IsSignaled());
66   *success = MessageLoopForIO::current()->WatchFileDescriptor(
67      sockets_[0], true, MessageLoopForIO::WATCH_READ,
68      &watcher_, shut_down_monitor_.get());
69  if (!*success) {
70    LOG(ERROR) << "WatchFileDescriptor";
71    signal->Signal();
72    return;
73  }
74  g_signal_socket = sockets_[1];
75
76  // Set up signal handler for SIGTERM.
77  struct sigaction action;
78  action.sa_sigaction = SigTermHandler;
79  sigemptyset(&action.sa_mask);
80  action.sa_flags = SA_SIGINFO;
81  *success = sigaction(SIGTERM, &action, &old_action_) == 0;
82  if (!*success) {
83    PLOG(ERROR) << "sigaction";
84    signal->Signal();
85    return;
86  }
87
88  // If the old_action is not default, somebody else has installed a
89  // a competing handler. Our handler is going to override it so it
90  // won't be called. If this occurs it needs to be fixed.
91  DCHECK_EQ(old_action_.sa_handler, SIG_DFL);
92  set_action_ = true;
93
94#if defined(OS_LINUX)
95  initializing_lock_.reset();
96#endif  // OS_LINUX
97#if defined(OS_MACOSX)
98  *success = WatchExecutable();
99  if (!*success) {
100    LOG(ERROR) << "WatchExecutable";
101    signal->Signal();
102    return;
103  }
104#endif  // OS_MACOSX
105  signal->Signal();
106}
107
108ServiceProcessState::StateData::~StateData() {
109  if (sockets_[0] != -1) {
110    if (HANDLE_EINTR(close(sockets_[0]))) {
111      PLOG(ERROR) << "close";
112    }
113  }
114  if (sockets_[1] != -1) {
115    if (HANDLE_EINTR(close(sockets_[1]))) {
116      PLOG(ERROR) << "close";
117    }
118  }
119  if (set_action_) {
120    if (sigaction(SIGTERM, &old_action_, NULL) < 0) {
121      PLOG(ERROR) << "sigaction";
122    }
123  }
124  g_signal_socket = -1;
125}
126
127void ServiceProcessState::CreateState() {
128  CHECK(!state_);
129  state_ = new StateData;
130
131  // Explicitly adding a reference here (and removing it in TearDownState)
132  // because StateData is refcounted on Mac and Linux so that methods can
133  // be called on other threads.
134  // It is not refcounted on Windows at this time.
135  state_->AddRef();
136}
137
138bool ServiceProcessState::SignalReady(
139    base::MessageLoopProxy* message_loop_proxy, Task* shutdown_task) {
140  CHECK(state_);
141
142  scoped_ptr<Task> scoped_shutdown_task(shutdown_task);
143#if defined(OS_LINUX)
144  state_->running_lock_.reset(TakeServiceRunningLock(true));
145  if (state_->running_lock_.get() == NULL) {
146    return false;
147  }
148#endif // OS_LINUX
149  state_->shut_down_monitor_.reset(
150      new ServiceProcessShutdownMonitor(scoped_shutdown_task.release()));
151  if (pipe(state_->sockets_) < 0) {
152    PLOG(ERROR) << "pipe";
153    return false;
154  }
155  base::WaitableEvent signal_ready(true, false);
156  bool success = false;
157
158  message_loop_proxy->PostTask(FROM_HERE,
159      NewRunnableMethod(state_, &ServiceProcessState::StateData::SignalReady,
160                        &signal_ready,
161                        &success));
162  signal_ready.Wait();
163  return success;
164}
165
166void ServiceProcessState::TearDownState() {
167  if (state_) {
168    state_->Release();
169    state_ = NULL;
170  }
171}
172