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/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
{
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
=
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
) {
56 scoped_ptr
<Socket
> client_socket(new Socket());
57 if (!server_socket
->Accept(client_socket
.get())) {
58 if (server_socket
->DidReceiveEvent())
64 if (!client_socket
->Write(welcome_message
.c_str(),
65 welcome_message
.length() + 1)) {
70 server_delegate
->OnClientConnected(client_socket
.Pass());
75 void SigChildHandler(int signal_number
) {
76 DCHECK_EQ(signal_number
, SIGCHLD
);
78 pid_t child_pid
= waitpid(-1 /* any child */, &status
, WNOHANG
);
85 if (WIFEXITED(status
) && WEXITSTATUS(status
) == 0)
87 // Avoid using StringAppendF() since it's unsafe in a signal handler due to
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
));
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
,
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
)) {
109 usleep(idle_time_msec
* 1000);
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) {
119 if (expected_welcome_message
!= buf
) {
120 LOG(ERROR
) << "Unexpected message read from daemon: " << buf
;
123 return socket
.Pass();
125 return scoped_ptr
<Socket
>();
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_
);
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
) {
159 if (setsid() < 0) { // Detach the child process from its parent.
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.
182 server_delegate_
->Init();
183 command_socket
.AddEventFd(get_exit_fd_callback_());
184 return RunServerAcceptLoop(
185 identifier_
, &command_socket
, server_delegate_
);
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()");
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()");
208 // Connect to the daemon's Unix Domain Socket.
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";
219 client_delegate_
->OnDaemonReady(client_socket
.get());
220 // Restore the previous signal action for SIGCHLD.
221 if (sigaction(SIGCHLD
, &old_action
, NULL
) < 0) {
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";
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
;
244 for (int i
= 0; i
< kNumTries
; ++i
) {
245 const pid_t previous_pid
= daemon_pid
;
246 daemon_pid
= Socket::GetUnixDomainSocketProcessOwner(identifier_
);
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.";
257 usleep(kIdleTimeMSec
* 1000);
259 LOG(ERROR
) << "Timed out while killing daemon. "
260 "It might still be tearing down.";
264 } // namespace forwarder2