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"
12 #include <sys/types.h>
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
{
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
=
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
) {
57 scoped_ptr
<Socket
> client_socket(new Socket());
58 if (!server_socket
->Accept(client_socket
.get())) {
59 if (server_socket
->DidReceiveEvent())
65 if (!client_socket
->Write(welcome_message
.c_str(),
66 welcome_message
.length() + 1)) {
71 server_delegate
->OnClientConnected(client_socket
.Pass());
76 void SigChildHandler(int signal_number
) {
77 DCHECK_EQ(signal_number
, SIGCHLD
);
79 pid_t child_pid
= waitpid(-1 /* any child */, &status
, WNOHANG
);
86 if (WIFEXITED(status
) && WEXITSTATUS(status
) == 0)
88 // Avoid using StringAppendF() since it's unsafe in a signal handler due to
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
));
97 string_builder
.Append("unknown reason.");
98 SIGNAL_SAFE_LOG(ERROR
, string_builder
.buffer());
101 scoped_ptr
<Socket
> ConnectToUnixDomainSocket(
102 const std::string
& socket_name
,
105 const std::string
& expected_welcome_message
) {
106 for (int i
= 0; i
< tries_count
; ++i
) {
107 scoped_ptr
<Socket
> socket(new Socket());
108 if (!socket
->ConnectUnix(socket_name
)) {
110 usleep(idle_time_msec
* 1000);
113 char buf
[kBufferSize
];
114 DCHECK(expected_welcome_message
.length() + 1 <= sizeof(buf
));
115 memset(buf
, 0, sizeof(buf
));
116 if (socket
->Read(buf
, expected_welcome_message
.length() + 1) < 0) {
120 if (expected_welcome_message
!= buf
) {
121 LOG(ERROR
) << "Unexpected message read from daemon: " << buf
;
124 return socket
.Pass();
126 return scoped_ptr
<Socket
>();
131 Daemon::Daemon(const std::string
& log_file_path
,
132 const std::string
& identifier
,
133 ClientDelegate
* client_delegate
,
134 ServerDelegate
* server_delegate
,
135 GetExitNotifierFDCallback get_exit_fd_callback
)
136 : log_file_path_(log_file_path
),
137 identifier_(identifier
),
138 client_delegate_(client_delegate
),
139 server_delegate_(server_delegate
),
140 get_exit_fd_callback_(get_exit_fd_callback
) {
141 DCHECK(client_delegate_
);
142 DCHECK(server_delegate_
);
143 DCHECK(get_exit_fd_callback_
);
148 bool Daemon::SpawnIfNeeded() {
149 const int kSingleTry
= 1;
150 const int kNoIdleTime
= 0;
151 scoped_ptr
<Socket
> client_socket
= ConnectToUnixDomainSocket(
152 identifier_
, kSingleTry
, kNoIdleTime
, identifier_
);
153 if (!client_socket
) {
160 if (setsid() < 0) { // Detach the child process from its parent.
164 InitLoggingForDaemon(log_file_path_
);
165 CloseFD(STDIN_FILENO
);
166 CloseFD(STDOUT_FILENO
);
167 CloseFD(STDERR_FILENO
);
168 const int null_fd
= open("/dev/null", O_RDWR
);
169 CHECK_EQ(null_fd
, STDIN_FILENO
);
170 CHECK_EQ(dup(null_fd
), STDOUT_FILENO
);
171 CHECK_EQ(dup(null_fd
), STDERR_FILENO
);
172 Socket command_socket
;
173 if (!command_socket
.BindUnix(identifier_
)) {
174 scoped_ptr
<Socket
> client_socket
= ConnectToUnixDomainSocket(
175 identifier_
, kSingleTry
, kNoIdleTime
, identifier_
);
176 if (client_socket
.get()) {
177 // The daemon was spawned by a concurrent process.
183 server_delegate_
->Init();
184 command_socket
.AddEventFd(get_exit_fd_callback_());
185 return RunServerAcceptLoop(
186 identifier_
, &command_socket
, server_delegate_
);
193 // Install the custom SIGCHLD handler.
194 sigset_t blocked_signals_set
;
195 if (sigprocmask(0 /* first arg ignored */, NULL
, &blocked_signals_set
) < 0) {
196 PError("sigprocmask()");
199 struct sigaction old_action
;
200 struct sigaction new_action
;
201 memset(&new_action
, 0, sizeof(new_action
));
202 new_action
.sa_handler
= SigChildHandler
;
203 new_action
.sa_flags
= SA_NOCLDSTOP
;
204 sigemptyset(&new_action
.sa_mask
);
205 if (sigaction(SIGCHLD
, &new_action
, &old_action
) < 0) {
206 PError("sigaction()");
209 // Connect to the daemon's Unix Domain Socket.
211 if (!client_socket
) {
212 client_socket
= ConnectToUnixDomainSocket(
213 identifier_
, kNumTries
, kIdleTimeMSec
, identifier_
);
214 if (!client_socket
) {
215 LOG(ERROR
) << "Could not connect to daemon's Unix Daemon socket";
220 client_delegate_
->OnDaemonReady(client_socket
.get());
221 // Restore the previous signal action for SIGCHLD.
222 if (sigaction(SIGCHLD
, &old_action
, NULL
) < 0) {
229 bool Daemon::Kill() {
230 pid_t daemon_pid
= Socket::GetUnixDomainSocketProcessOwner(identifier_
);
231 if (daemon_pid
< 0) {
232 LOG(ERROR
) << "No forwarder daemon seems to be running";
235 if (kill(daemon_pid
, SIGTERM
) < 0) {
236 if (errno
== ESRCH
/* invalid PID */) {
237 // The daemon exited for some reason (e.g. kill by a process other than
238 // us) right before the call to kill() above.
239 LOG(ERROR
) << "Could not kill daemon with PID " << daemon_pid
;
245 for (int i
= 0; i
< kNumTries
; ++i
) {
246 const pid_t previous_pid
= daemon_pid
;
247 daemon_pid
= Socket::GetUnixDomainSocketProcessOwner(identifier_
);
250 // Since we are polling we might not see the 'daemon exited' event if
251 // another daemon was spawned during our idle period.
252 if (daemon_pid
!= previous_pid
) {
253 LOG(WARNING
) << "Daemon (pid=" << previous_pid
254 << ") was successfully killed but a new daemon (pid="
255 << daemon_pid
<< ") seems to be running now.";
258 usleep(kIdleTimeMSec
* 1000);
260 LOG(ERROR
) << "Timed out while killing daemon. "
261 "It might still be tearing down.";
265 } // namespace forwarder2