service_process_util_posix.cc revision 72a454cd3513ac24fbdd0e0cb9ad70b86a99b801
1// Copyright (c) 2010 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.h"
6
7#include <signal.h>
8#include <unistd.h>
9
10#include "base/file_util.h"
11#include "base/logging.h"
12#include "base/message_loop.h"
13#include "base/message_loop_proxy.h"
14#include "base/message_pump_libevent.h"
15#include "base/path_service.h"
16#include "chrome/common/chrome_paths.h"
17#include "chrome/common/chrome_version_info.h"
18#include "chrome/common/multi_process_lock.h"
19
20namespace {
21
22int g_signal_socket = -1;
23
24// Gets the name of the lock file for service process.
25FilePath GetServiceProcessLockFilePath() {
26  FilePath user_data_dir;
27  PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
28  chrome::VersionInfo version_info;
29  std::string lock_file_name = version_info.Version() + "Service Process Lock";
30  return user_data_dir.Append(lock_file_name);
31}
32
33// Attempts to take a lock named |name|. If |waiting| is true then this will
34// make multiple attempts to acquire the lock.
35// Caller is responsible for ownership of the MultiProcessLock.
36MultiProcessLock* TakeNamedLock(const std::string& name, bool waiting) {
37  scoped_ptr<MultiProcessLock> lock(MultiProcessLock::Create(name));
38  if (lock == NULL) return NULL;
39  bool got_lock = false;
40  for (int i = 0; i < 10; ++i) {
41    if (lock->TryLock()) {
42      got_lock = true;
43      break;
44    }
45    if (!waiting) break;
46    base::PlatformThread::Sleep(100 * i);
47  }
48  if (!got_lock) {
49    lock.reset();
50  }
51  return lock.release();
52}
53
54MultiProcessLock* TakeServiceRunningLock(bool waiting) {
55  std::string lock_name =
56      GetServiceProcessScopedName("_service_running");
57  return TakeNamedLock(lock_name, waiting);
58}
59
60MultiProcessLock* TakeServiceInitializingLock(bool waiting) {
61  std::string lock_name =
62      GetServiceProcessScopedName("_service_initializing");
63  return TakeNamedLock(lock_name, waiting);
64}
65
66}  // namespace
67
68// Watches for |kShutDownMessage| to be written to the file descriptor it is
69// watching. When it reads |kShutDownMessage|, it performs |shutdown_task_|.
70// Used here to monitor the socket listening to g_signal_socket.
71class ServiceProcessShutdownMonitor
72    : public base::MessagePumpLibevent::Watcher {
73 public:
74
75  enum {
76    kShutDownMessage = 0xdecea5e
77  };
78
79  explicit ServiceProcessShutdownMonitor(Task* shutdown_task)
80      : shutdown_task_(shutdown_task) {
81  }
82
83  virtual ~ServiceProcessShutdownMonitor();
84
85  virtual void OnFileCanReadWithoutBlocking(int fd);
86  virtual void OnFileCanWriteWithoutBlocking(int fd);
87
88 private:
89  scoped_ptr<Task> shutdown_task_;
90};
91
92ServiceProcessShutdownMonitor::~ServiceProcessShutdownMonitor() {
93}
94
95void ServiceProcessShutdownMonitor::OnFileCanReadWithoutBlocking(int fd) {
96  if (shutdown_task_.get()) {
97    int buffer;
98    int length = read(fd, &buffer, sizeof(buffer));
99    if ((length == sizeof(buffer)) && (buffer == kShutDownMessage)) {
100      shutdown_task_->Run();
101      shutdown_task_.reset();
102    } else if (length > 0) {
103      LOG(ERROR) << "Unexpected read: " << buffer;
104    } else if (length == 0) {
105      LOG(ERROR) << "Unexpected fd close";
106    } else if (length < 0) {
107      PLOG(ERROR) << "read";
108    }
109  }
110}
111
112void ServiceProcessShutdownMonitor::OnFileCanWriteWithoutBlocking(int fd) {
113  NOTIMPLEMENTED();
114}
115
116// "Forced" Shutdowns on POSIX are done via signals. The magic signal for
117// a shutdown is SIGTERM. "write" is a signal safe function. PLOG(ERROR) is
118// not, but we don't ever expect it to be called.
119static void SigTermHandler(int sig, siginfo_t* info, void* uap) {
120  // TODO(dmaclach): add security here to make sure that we are being shut
121  //                 down by an appropriate process.
122  int message = ServiceProcessShutdownMonitor::kShutDownMessage;
123  if (write(g_signal_socket, &message, sizeof(message)) < 0) {
124    PLOG(ERROR) << "write";
125  }
126}
127
128// See comment for SigTermHandler.
129bool ForceServiceProcessShutdown(const std::string& version,
130                                 base::ProcessId process_id) {
131  if (kill(process_id, SIGTERM) < 0) {
132    PLOG(ERROR) << "kill";
133    return false;
134  }
135  return true;
136}
137
138bool CheckServiceProcessReady() {
139  scoped_ptr<MultiProcessLock> running_lock(TakeServiceRunningLock(false));
140  return running_lock.get() == NULL;
141}
142
143struct ServiceProcessState::StateData
144    : public base::RefCountedThreadSafe<ServiceProcessState::StateData> {
145  scoped_ptr<MultiProcessLock> initializing_lock_;
146  scoped_ptr<MultiProcessLock> running_lock_;
147  scoped_ptr<ServiceProcessShutdownMonitor> shut_down_monitor_;
148  base::MessagePumpLibevent::FileDescriptorWatcher watcher_;
149  int sockets_[2];
150  struct sigaction old_action_;
151  bool set_action_;
152
153  // WatchFileDescriptor needs to be set up by the thread that is going
154  // to be monitoring it.
155  void SignalReady() {
156    CHECK(MessageLoopForIO::current()->WatchFileDescriptor(
157        sockets_[0], true, MessageLoopForIO::WATCH_READ,
158        &watcher_, shut_down_monitor_.get()));
159    g_signal_socket = sockets_[1];
160
161    // Set up signal handler for SIGTERM.
162    struct sigaction action;
163    action.sa_sigaction = SigTermHandler;
164    sigemptyset(&action.sa_mask);
165    action.sa_flags = SA_SIGINFO;
166    if (sigaction(SIGTERM, &action, &old_action_) == 0) {
167      // If the old_action is not default, somebody else has installed a
168      // a competing handler. Our handler is going to override it so it
169      // won't be called. If this occurs it needs to be fixed.
170      DCHECK_EQ(old_action_.sa_handler, SIG_DFL);
171      set_action_ = true;
172      initializing_lock_.reset();
173    } else {
174      PLOG(ERROR) << "sigaction";
175    }
176  }
177};
178
179bool ServiceProcessState::TakeSingletonLock() {
180  CHECK(!state_);
181  state_ = new StateData;
182  state_->AddRef();
183  state_->sockets_[0] = -1;
184  state_->sockets_[1] = -1;
185  state_->set_action_ = false;
186  state_->initializing_lock_.reset(TakeServiceInitializingLock(true));
187  return state_->initializing_lock_.get();
188}
189
190bool ServiceProcessState::SignalReady(
191    base::MessageLoopProxy* message_loop_proxy, Task* shutdown_task) {
192  CHECK(state_);
193  CHECK_EQ(g_signal_socket, -1);
194
195  state_->running_lock_.reset(TakeServiceRunningLock(true));
196  if (state_->running_lock_.get() == NULL) {
197    return false;
198  }
199  state_->shut_down_monitor_.reset(
200      new ServiceProcessShutdownMonitor(shutdown_task));
201  if (pipe(state_->sockets_) < 0) {
202    PLOG(ERROR) << "pipe";
203    return false;
204  }
205  message_loop_proxy->PostTask(FROM_HERE,
206      NewRunnableMethod(state_, &ServiceProcessState::StateData::SignalReady));
207  return true;
208}
209
210bool ServiceProcessState::AddToAutoRun() {
211  NOTIMPLEMENTED();
212  return false;
213}
214
215bool ServiceProcessState::RemoveFromAutoRun() {
216  NOTIMPLEMENTED();
217  return false;
218}
219
220void ServiceProcessState::TearDownState() {
221  g_signal_socket = -1;
222  if (state_) {
223    if (state_->sockets_[0] != -1) {
224      close(state_->sockets_[0]);
225    }
226    if (state_->sockets_[1] != -1) {
227      close(state_->sockets_[1]);
228    }
229    if (state_->set_action_) {
230      if (sigaction(SIGTERM, &state_->old_action_, NULL) < 0) {
231        PLOG(ERROR) << "sigaction";
232      }
233    }
234    state_->Release();
235    state_ = NULL;
236  }
237}
238