When Retrier succeeds, record errors it encountered.
[chromium-blink-merge.git] / tools / android / forwarder2 / daemon.cc
blobc17cbc797416345b76b3a83c6009d4b5c06c614c
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.
5 #include "tools/android/forwarder2/daemon.h"
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <signal.h>
10 #include <sys/file.h>
11 #include <sys/stat.h>
12 #include <sys/types.h>
13 #include <sys/wait.h>
14 #include <unistd.h>
16 #include <cstdlib>
17 #include <cstring>
18 #include <string>
20 #include "base/basictypes.h"
21 #include "base/file_util.h"
22 #include "base/files/file_path.h"
23 #include "base/logging.h"
24 #include "base/memory/scoped_ptr.h"
25 #include "base/posix/eintr_wrapper.h"
26 #include "base/safe_strerror_posix.h"
27 #include "base/string_number_conversions.h"
28 #include "base/stringprintf.h"
29 #include "tools/android/forwarder2/common.h"
30 #include "tools/android/forwarder2/socket.h"
32 namespace forwarder2 {
33 namespace {
35 const int kBufferSize = 256;
37 // Timeout constant used for polling when connecting to the daemon's Unix Domain
38 // Socket and also when waiting for its death when it is killed.
39 const int kNumTries = 100;
40 const int kIdleTimeMSec = 20;
42 void InitLoggingForDaemon(const std::string& log_file) {
43 CHECK(
44 logging::InitLogging(
45 log_file.c_str(),
46 log_file.empty() ?
47 logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG : logging::LOG_ONLY_TO_FILE,
48 logging::DONT_LOCK_LOG_FILE, logging::APPEND_TO_OLD_LOG_FILE,
49 logging::ENABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS));
52 bool RunServerAcceptLoop(const std::string& welcome_message,
53 Socket* server_socket,
54 Daemon::ServerDelegate* server_delegate) {
55 bool failed = false;
56 for (;;) {
57 scoped_ptr<Socket> client_socket(new Socket());
58 if (!server_socket->Accept(client_socket.get())) {
59 if (server_socket->DidReceiveEvent())
60 break;
61 PError("Accept()");
62 failed = true;
63 break;
65 if (!client_socket->Write(welcome_message.c_str(),
66 welcome_message.length() + 1)) {
67 PError("Write()");
68 failed = true;
69 continue;
71 server_delegate->OnClientConnected(client_socket.Pass());
73 server_delegate->OnServerExited();
74 return !failed;
77 void SigChildHandler(int signal_number) {
78 DCHECK_EQ(signal_number, SIGCHLD);
79 int status;
80 pid_t child_pid = waitpid(-1 /* any child */, &status, WNOHANG);
81 if (child_pid < 0) {
82 PError("waitpid");
83 return;
85 if (child_pid == 0)
86 return;
87 if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
88 return;
89 // Avoid using StringAppendF() since it's unsafe in a signal handler due to
90 // its use of LOG().
91 FixedSizeStringBuilder<256> string_builder;
92 string_builder.Append("Daemon (pid=%d) died unexpectedly with ", child_pid);
93 if (WIFEXITED(status))
94 string_builder.Append("status %d.", WEXITSTATUS(status));
95 else if (WIFSIGNALED(status))
96 string_builder.Append("signal %d.", WTERMSIG(status));
97 else
98 string_builder.Append("unknown reason.");
99 SIGNAL_SAFE_LOG(ERROR, string_builder.buffer());
102 // Note that 0 is written to |lock_owner_pid| in case the file is not locked.
103 bool GetFileLockOwnerPid(int fd, pid_t* lock_owner_pid) {
104 struct flock lock_info = {};
105 lock_info.l_type = F_WRLCK;
106 lock_info.l_whence = SEEK_CUR;
107 const int ret = HANDLE_EINTR(fcntl(fd, F_GETLK, &lock_info));
108 if (ret < 0) {
109 if (errno == EBADF) {
110 // Assume that the provided file descriptor corresponding to the PID file
111 // was valid until the daemon removed this file.
112 *lock_owner_pid = 0;
113 return true;
115 PError("fcntl");
116 return false;
118 if (lock_info.l_type == F_UNLCK) {
119 *lock_owner_pid = 0;
120 return true;
122 CHECK_EQ(F_WRLCK /* exclusive lock */, lock_info.l_type);
123 *lock_owner_pid = lock_info.l_pid;
124 return true;
127 scoped_ptr<Socket> ConnectToUnixDomainSocket(
128 const std::string& socket_name,
129 int tries_count,
130 int idle_time_msec,
131 const std::string& expected_welcome_message) {
132 for (int i = 0; i < tries_count; ++i) {
133 scoped_ptr<Socket> socket(new Socket());
134 if (!socket->ConnectUnix(socket_name)) {
135 if (idle_time_msec)
136 usleep(idle_time_msec * 1000);
137 continue;
139 char buf[kBufferSize];
140 DCHECK(expected_welcome_message.length() + 1 <= sizeof(buf));
141 memset(buf, 0, sizeof(buf));
142 if (socket->Read(buf, sizeof(buf)) < 0) {
143 perror("read");
144 continue;
146 if (expected_welcome_message != buf) {
147 LOG(ERROR) << "Unexpected message read from daemon: " << buf;
148 break;
150 return socket.Pass();
152 return scoped_ptr<Socket>(NULL);
155 } // namespace
157 Daemon::Daemon(const std::string& log_file_path,
158 const std::string& identifier,
159 ClientDelegate* client_delegate,
160 ServerDelegate* server_delegate,
161 GetExitNotifierFDCallback get_exit_fd_callback)
162 : log_file_path_(log_file_path),
163 identifier_(identifier),
164 client_delegate_(client_delegate),
165 server_delegate_(server_delegate),
166 get_exit_fd_callback_(get_exit_fd_callback) {
167 DCHECK(client_delegate_);
168 DCHECK(server_delegate_);
169 DCHECK(get_exit_fd_callback_);
172 Daemon::~Daemon() {}
174 bool Daemon::SpawnIfNeeded() {
175 const int kSingleTry = 1;
176 const int kNoIdleTime = 0;
177 scoped_ptr<Socket> client_socket = ConnectToUnixDomainSocket(
178 identifier_, kSingleTry, kNoIdleTime, identifier_);
179 if (!client_socket) {
180 switch (fork()) {
181 case -1:
182 PError("fork()");
183 return false;
184 // Child.
185 case 0: {
186 if (setsid() < 0) { // Detach the child process from its parent.
187 PError("setsid()");
188 exit(1);
190 InitLoggingForDaemon(log_file_path_);
191 CloseFD(STDIN_FILENO);
192 CloseFD(STDOUT_FILENO);
193 CloseFD(STDERR_FILENO);
194 const int null_fd = open("/dev/null", O_RDWR);
195 CHECK_EQ(null_fd, STDIN_FILENO);
196 CHECK_EQ(dup(null_fd), STDOUT_FILENO);
197 CHECK_EQ(dup(null_fd), STDERR_FILENO);
198 Socket command_socket;
199 if (!command_socket.BindUnix(identifier_)) {
200 scoped_ptr<Socket> client_socket = ConnectToUnixDomainSocket(
201 identifier_, kSingleTry, kNoIdleTime, identifier_);
202 if (client_socket.get()) {
203 // The daemon was spawned by a concurrent process.
204 exit(0);
206 PError("bind()");
207 exit(1);
209 server_delegate_->Init();
210 command_socket.AddEventFd(get_exit_fd_callback_());
211 exit(!RunServerAcceptLoop(identifier_, &command_socket,
212 server_delegate_));
214 default:
215 break;
218 // Parent.
219 // Install the custom SIGCHLD handler.
220 sigset_t blocked_signals_set;
221 if (sigprocmask(0 /* first arg ignored */, NULL, &blocked_signals_set) < 0) {
222 PError("sigprocmask()");
223 return false;
225 struct sigaction old_action;
226 struct sigaction new_action;
227 memset(&new_action, 0, sizeof(new_action));
228 new_action.sa_handler = SigChildHandler;
229 new_action.sa_flags = SA_NOCLDSTOP;
230 sigemptyset(&new_action.sa_mask);
231 if (sigaction(SIGCHLD, &new_action, &old_action) < 0) {
232 PError("sigaction()");
233 return false;
235 // Connect to the daemon's Unix Domain Socket.
236 bool failed = false;
237 if (!client_socket) {
238 client_socket = ConnectToUnixDomainSocket(
239 identifier_, kNumTries, kIdleTimeMSec, identifier_);
240 if (!client_socket) {
241 LOG(ERROR) << "Could not connect to daemon's Unix Daemon socket";
242 failed = true;
245 if (!failed)
246 client_delegate_->OnDaemonReady(client_socket.get());
247 // Restore the previous signal action for SIGCHLD.
248 if (sigaction(SIGCHLD, &old_action, NULL) < 0) {
249 PError("sigaction");
250 failed = true;
252 return !failed;
255 bool Daemon::Kill() {
256 pid_t daemon_pid = Socket::GetUnixDomainSocketProcessOwner(identifier_);
257 if (daemon_pid < 0)
258 return true; // No daemon running.
259 if (kill(daemon_pid, SIGTERM) < 0) {
260 if (errno == ESRCH /* invalid PID */)
261 // The daemon exited for some reason (e.g. kill by a process other than
262 // us) right before the call to kill() above.
263 return true;
264 PError("kill");
265 return false;
267 for (int i = 0; i < kNumTries; ++i) {
268 const pid_t previous_pid = daemon_pid;
269 daemon_pid = Socket::GetUnixDomainSocketProcessOwner(identifier_);
270 if (daemon_pid < 0)
271 return true;
272 // Since we are polling we might not see the 'daemon exited' event if
273 // another daemon was spawned during our idle period.
274 if (daemon_pid != previous_pid) {
275 LOG(WARNING) << "Daemon (pid=" << previous_pid
276 << ") was successfully killed but a new daemon (pid="
277 << daemon_pid << ") seems to be running now.";
278 return true;
280 usleep(kIdleTimeMSec * 1000);
282 LOG(ERROR) << "Timed out while killing daemon. "
283 "It might still be tearing down.";
284 return false;
287 } // namespace forwarder2