Minor Python style clean-up
[chromium-blink-merge.git] / tools / android / forwarder2 / daemon.cc
blobb8217afd0368bcd81efbf184c674fcd9e4ae92a8
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/strings/string_number_conversions.h"
27 #include "base/strings/stringprintf.h"
28 #include "tools/android/forwarder2/common.h"
29 #include "tools/android/forwarder2/socket.h"
31 namespace forwarder2 {
32 namespace {
34 const int kBufferSize = 256;
36 // Timeout constant used for polling when connecting to the daemon's Unix Domain
37 // Socket and also when waiting for its death when it is killed.
38 const int kNumTries = 100;
39 const int kIdleTimeMSec = 20;
41 void InitLoggingForDaemon(const std::string& log_file) {
42 logging::LoggingSettings settings;
43 settings.logging_dest =
44 log_file.empty() ?
45 logging::LOG_TO_SYSTEM_DEBUG_LOG : logging::LOG_TO_FILE;
46 settings.log_file = log_file.c_str();
47 settings.lock_log = logging::DONT_LOCK_LOG_FILE;
48 CHECK(logging::InitLogging(settings));
51 bool RunServerAcceptLoop(const std::string& welcome_message,
52 Socket* server_socket,
53 Daemon::ServerDelegate* server_delegate) {
54 bool failed = false;
55 for (;;) {
56 scoped_ptr<Socket> client_socket(new Socket());
57 if (!server_socket->Accept(client_socket.get())) {
58 if (server_socket->DidReceiveEvent())
59 break;
60 PError("Accept()");
61 failed = true;
62 break;
64 if (!client_socket->Write(welcome_message.c_str(),
65 welcome_message.length() + 1)) {
66 PError("Write()");
67 failed = true;
68 continue;
70 server_delegate->OnClientConnected(client_socket.Pass());
72 return !failed;
75 void SigChildHandler(int signal_number) {
76 DCHECK_EQ(signal_number, SIGCHLD);
77 int status;
78 pid_t child_pid = waitpid(-1 /* any child */, &status, WNOHANG);
79 if (child_pid < 0) {
80 PError("waitpid");
81 return;
83 if (child_pid == 0)
84 return;
85 if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
86 return;
87 // Avoid using StringAppendF() since it's unsafe in a signal handler due to
88 // its use of LOG().
89 FixedSizeStringBuilder<256> string_builder;
90 string_builder.Append("Daemon (pid=%d) died unexpectedly with ", child_pid);
91 if (WIFEXITED(status))
92 string_builder.Append("status %d.", WEXITSTATUS(status));
93 else if (WIFSIGNALED(status))
94 string_builder.Append("signal %d.", WTERMSIG(status));
95 else
96 string_builder.Append("unknown reason.");
97 SIGNAL_SAFE_LOG(ERROR, string_builder.buffer());
100 scoped_ptr<Socket> ConnectToUnixDomainSocket(
101 const std::string& socket_name,
102 int tries_count,
103 int idle_time_msec,
104 const std::string& expected_welcome_message) {
105 for (int i = 0; i < tries_count; ++i) {
106 scoped_ptr<Socket> socket(new Socket());
107 if (!socket->ConnectUnix(socket_name)) {
108 if (idle_time_msec)
109 usleep(idle_time_msec * 1000);
110 continue;
112 char buf[kBufferSize];
113 DCHECK(expected_welcome_message.length() + 1 <= sizeof(buf));
114 memset(buf, 0, sizeof(buf));
115 if (socket->Read(buf, expected_welcome_message.length() + 1) < 0) {
116 perror("read");
117 continue;
119 if (expected_welcome_message != buf) {
120 LOG(ERROR) << "Unexpected message read from daemon: " << buf;
121 break;
123 return socket.Pass();
125 return scoped_ptr<Socket>();
128 } // namespace
130 Daemon::Daemon(const std::string& log_file_path,
131 const std::string& identifier,
132 ClientDelegate* client_delegate,
133 ServerDelegate* server_delegate,
134 GetExitNotifierFDCallback get_exit_fd_callback)
135 : log_file_path_(log_file_path),
136 identifier_(identifier),
137 client_delegate_(client_delegate),
138 server_delegate_(server_delegate),
139 get_exit_fd_callback_(get_exit_fd_callback) {
140 DCHECK(client_delegate_);
141 DCHECK(server_delegate_);
142 DCHECK(get_exit_fd_callback_);
145 Daemon::~Daemon() {}
147 bool Daemon::SpawnIfNeeded() {
148 const int kSingleTry = 1;
149 const int kNoIdleTime = 0;
150 scoped_ptr<Socket> client_socket = ConnectToUnixDomainSocket(
151 identifier_, kSingleTry, kNoIdleTime, identifier_);
152 if (!client_socket) {
153 switch (fork()) {
154 case -1:
155 PError("fork()");
156 return false;
157 // Child.
158 case 0: {
159 if (setsid() < 0) { // Detach the child process from its parent.
160 PError("setsid()");
161 exit(1);
163 InitLoggingForDaemon(log_file_path_);
164 CloseFD(STDIN_FILENO);
165 CloseFD(STDOUT_FILENO);
166 CloseFD(STDERR_FILENO);
167 const int null_fd = open("/dev/null", O_RDWR);
168 CHECK_EQ(null_fd, STDIN_FILENO);
169 CHECK_EQ(dup(null_fd), STDOUT_FILENO);
170 CHECK_EQ(dup(null_fd), STDERR_FILENO);
171 Socket command_socket;
172 if (!command_socket.BindUnix(identifier_)) {
173 scoped_ptr<Socket> client_socket = ConnectToUnixDomainSocket(
174 identifier_, kSingleTry, kNoIdleTime, identifier_);
175 if (client_socket.get()) {
176 // The daemon was spawned by a concurrent process.
177 exit(0);
179 PError("bind()");
180 exit(1);
182 server_delegate_->Init();
183 command_socket.AddEventFd(get_exit_fd_callback_());
184 return RunServerAcceptLoop(
185 identifier_, &command_socket, server_delegate_);
187 default:
188 break;
191 // Parent.
192 // Install the custom SIGCHLD handler.
193 sigset_t blocked_signals_set;
194 if (sigprocmask(0 /* first arg ignored */, NULL, &blocked_signals_set) < 0) {
195 PError("sigprocmask()");
196 return false;
198 struct sigaction old_action;
199 struct sigaction new_action;
200 memset(&new_action, 0, sizeof(new_action));
201 new_action.sa_handler = SigChildHandler;
202 new_action.sa_flags = SA_NOCLDSTOP;
203 sigemptyset(&new_action.sa_mask);
204 if (sigaction(SIGCHLD, &new_action, &old_action) < 0) {
205 PError("sigaction()");
206 return false;
208 // Connect to the daemon's Unix Domain Socket.
209 bool failed = false;
210 if (!client_socket) {
211 client_socket = ConnectToUnixDomainSocket(
212 identifier_, kNumTries, kIdleTimeMSec, identifier_);
213 if (!client_socket) {
214 LOG(ERROR) << "Could not connect to daemon's Unix Daemon socket";
215 failed = true;
218 if (!failed)
219 client_delegate_->OnDaemonReady(client_socket.get());
220 // Restore the previous signal action for SIGCHLD.
221 if (sigaction(SIGCHLD, &old_action, NULL) < 0) {
222 PError("sigaction");
223 failed = true;
225 return !failed;
228 bool Daemon::Kill() {
229 pid_t daemon_pid = Socket::GetUnixDomainSocketProcessOwner(identifier_);
230 if (daemon_pid < 0) {
231 LOG(ERROR) << "No forwarder daemon seems to be running";
232 return true;
234 if (kill(daemon_pid, SIGTERM) < 0) {
235 if (errno == ESRCH /* invalid PID */) {
236 // The daemon exited for some reason (e.g. kill by a process other than
237 // us) right before the call to kill() above.
238 LOG(ERROR) << "Could not kill daemon with PID " << daemon_pid;
239 return true;
241 PError("kill");
242 return false;
244 for (int i = 0; i < kNumTries; ++i) {
245 const pid_t previous_pid = daemon_pid;
246 daemon_pid = Socket::GetUnixDomainSocketProcessOwner(identifier_);
247 if (daemon_pid < 0)
248 return true;
249 // Since we are polling we might not see the 'daemon exited' event if
250 // another daemon was spawned during our idle period.
251 if (daemon_pid != previous_pid) {
252 LOG(WARNING) << "Daemon (pid=" << previous_pid
253 << ") was successfully killed but a new daemon (pid="
254 << daemon_pid << ") seems to be running now.";
255 return true;
257 usleep(kIdleTimeMSec * 1000);
259 LOG(ERROR) << "Timed out while killing daemon. "
260 "It might still be tearing down.";
261 return false;
264 } // namespace forwarder2