Revert of Enabling audio quality test on mac. (patchset #1 id:1 of https://codereview...
[chromium-blink-merge.git] / tools / android / forwarder2 / daemon.cc
blob19a1054c0e93de26d5d69e5de80f6ee1198f983d
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/files/file_path.h"
22 #include "base/files/file_util.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/strings/string_number_conversions.h"
28 #include "base/strings/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 logging::LoggingSettings settings;
44 settings.logging_dest =
45 log_file.empty() ?
46 logging::LOG_TO_SYSTEM_DEBUG_LOG : logging::LOG_TO_FILE;
47 settings.log_file = log_file.c_str();
48 settings.lock_log = logging::DONT_LOCK_LOG_FILE;
49 CHECK(logging::InitLogging(settings));
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 return !failed;
76 void SigChildHandler(int signal_number) {
77 DCHECK_EQ(signal_number, SIGCHLD);
78 int status;
79 pid_t child_pid = waitpid(-1 /* any child */, &status, WNOHANG);
80 if (child_pid < 0) {
81 PError("waitpid");
82 return;
84 if (child_pid == 0)
85 return;
86 if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
87 return;
88 // Avoid using StringAppendF() since it's unsafe in a signal handler due to
89 // its use of LOG().
90 FixedSizeStringBuilder<256> string_builder;
91 string_builder.Append("Daemon (pid=%d) died unexpectedly with ", child_pid);
92 if (WIFEXITED(status))
93 string_builder.Append("status %d.", WEXITSTATUS(status));
94 else if (WIFSIGNALED(status))
95 string_builder.Append("signal %d.", WTERMSIG(status));
96 else
97 string_builder.Append("unknown reason.");
98 SIGNAL_SAFE_LOG(ERROR, string_builder.buffer());
101 // Note that 0 is written to |lock_owner_pid| in case the file is not locked.
102 bool GetFileLockOwnerPid(int fd, pid_t* lock_owner_pid) {
103 struct flock lock_info = {};
104 lock_info.l_type = F_WRLCK;
105 lock_info.l_whence = SEEK_CUR;
106 const int ret = HANDLE_EINTR(fcntl(fd, F_GETLK, &lock_info));
107 if (ret < 0) {
108 if (errno == EBADF) {
109 // Assume that the provided file descriptor corresponding to the PID file
110 // was valid until the daemon removed this file.
111 *lock_owner_pid = 0;
112 return true;
114 PError("fcntl");
115 return false;
117 if (lock_info.l_type == F_UNLCK) {
118 *lock_owner_pid = 0;
119 return true;
121 CHECK_EQ(F_WRLCK /* exclusive lock */, lock_info.l_type);
122 *lock_owner_pid = lock_info.l_pid;
123 return true;
126 scoped_ptr<Socket> ConnectToUnixDomainSocket(
127 const std::string& socket_name,
128 int tries_count,
129 int idle_time_msec,
130 const std::string& expected_welcome_message) {
131 for (int i = 0; i < tries_count; ++i) {
132 scoped_ptr<Socket> socket(new Socket());
133 if (!socket->ConnectUnix(socket_name)) {
134 if (idle_time_msec)
135 usleep(idle_time_msec * 1000);
136 continue;
138 char buf[kBufferSize];
139 DCHECK(expected_welcome_message.length() + 1 <= sizeof(buf));
140 memset(buf, 0, sizeof(buf));
141 if (socket->Read(buf, expected_welcome_message.length() + 1) < 0) {
142 perror("read");
143 continue;
145 if (expected_welcome_message != buf) {
146 LOG(ERROR) << "Unexpected message read from daemon: " << buf;
147 break;
149 return socket.Pass();
151 return scoped_ptr<Socket>();
154 } // namespace
156 Daemon::Daemon(const std::string& log_file_path,
157 const std::string& identifier,
158 ClientDelegate* client_delegate,
159 ServerDelegate* server_delegate,
160 GetExitNotifierFDCallback get_exit_fd_callback)
161 : log_file_path_(log_file_path),
162 identifier_(identifier),
163 client_delegate_(client_delegate),
164 server_delegate_(server_delegate),
165 get_exit_fd_callback_(get_exit_fd_callback) {
166 DCHECK(client_delegate_);
167 DCHECK(server_delegate_);
168 DCHECK(get_exit_fd_callback_);
171 Daemon::~Daemon() {}
173 bool Daemon::SpawnIfNeeded() {
174 const int kSingleTry = 1;
175 const int kNoIdleTime = 0;
176 scoped_ptr<Socket> client_socket = ConnectToUnixDomainSocket(
177 identifier_, kSingleTry, kNoIdleTime, identifier_);
178 if (!client_socket) {
179 switch (fork()) {
180 case -1:
181 PError("fork()");
182 return false;
183 // Child.
184 case 0: {
185 if (setsid() < 0) { // Detach the child process from its parent.
186 PError("setsid()");
187 exit(1);
189 InitLoggingForDaemon(log_file_path_);
190 CloseFD(STDIN_FILENO);
191 CloseFD(STDOUT_FILENO);
192 CloseFD(STDERR_FILENO);
193 const int null_fd = open("/dev/null", O_RDWR);
194 CHECK_EQ(null_fd, STDIN_FILENO);
195 CHECK_EQ(dup(null_fd), STDOUT_FILENO);
196 CHECK_EQ(dup(null_fd), STDERR_FILENO);
197 Socket command_socket;
198 if (!command_socket.BindUnix(identifier_)) {
199 scoped_ptr<Socket> client_socket = ConnectToUnixDomainSocket(
200 identifier_, kSingleTry, kNoIdleTime, identifier_);
201 if (client_socket.get()) {
202 // The daemon was spawned by a concurrent process.
203 exit(0);
205 PError("bind()");
206 exit(1);
208 server_delegate_->Init();
209 command_socket.AddEventFd(get_exit_fd_callback_());
210 return RunServerAcceptLoop(
211 identifier_, &command_socket, server_delegate_);
213 default:
214 break;
217 // Parent.
218 // Install the custom SIGCHLD handler.
219 sigset_t blocked_signals_set;
220 if (sigprocmask(0 /* first arg ignored */, NULL, &blocked_signals_set) < 0) {
221 PError("sigprocmask()");
222 return false;
224 struct sigaction old_action;
225 struct sigaction new_action;
226 memset(&new_action, 0, sizeof(new_action));
227 new_action.sa_handler = SigChildHandler;
228 new_action.sa_flags = SA_NOCLDSTOP;
229 sigemptyset(&new_action.sa_mask);
230 if (sigaction(SIGCHLD, &new_action, &old_action) < 0) {
231 PError("sigaction()");
232 return false;
234 // Connect to the daemon's Unix Domain Socket.
235 bool failed = false;
236 if (!client_socket) {
237 client_socket = ConnectToUnixDomainSocket(
238 identifier_, kNumTries, kIdleTimeMSec, identifier_);
239 if (!client_socket) {
240 LOG(ERROR) << "Could not connect to daemon's Unix Daemon socket";
241 failed = true;
244 if (!failed)
245 client_delegate_->OnDaemonReady(client_socket.get());
246 // Restore the previous signal action for SIGCHLD.
247 if (sigaction(SIGCHLD, &old_action, NULL) < 0) {
248 PError("sigaction");
249 failed = true;
251 return !failed;
254 bool Daemon::Kill() {
255 pid_t daemon_pid = Socket::GetUnixDomainSocketProcessOwner(identifier_);
256 if (daemon_pid < 0) {
257 LOG(ERROR) << "No forwarder daemon seems to be running";
258 return true;
260 if (kill(daemon_pid, SIGTERM) < 0) {
261 if (errno == ESRCH /* invalid PID */) {
262 // The daemon exited for some reason (e.g. kill by a process other than
263 // us) right before the call to kill() above.
264 LOG(ERROR) << "Could not kill daemon with PID " << daemon_pid;
265 return true;
267 PError("kill");
268 return false;
270 for (int i = 0; i < kNumTries; ++i) {
271 const pid_t previous_pid = daemon_pid;
272 daemon_pid = Socket::GetUnixDomainSocketProcessOwner(identifier_);
273 if (daemon_pid < 0)
274 return true;
275 // Since we are polling we might not see the 'daemon exited' event if
276 // another daemon was spawned during our idle period.
277 if (daemon_pid != previous_pid) {
278 LOG(WARNING) << "Daemon (pid=" << previous_pid
279 << ") was successfully killed but a new daemon (pid="
280 << daemon_pid << ") seems to be running now.";
281 return true;
283 usleep(kIdleTimeMSec * 1000);
285 LOG(ERROR) << "Timed out while killing daemon. "
286 "It might still be tearing down.";
287 return false;
290 } // namespace forwarder2