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 // 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
));
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.
117 if (lock_info
.l_type
== F_UNLCK
) {
121 CHECK_EQ(F_WRLCK
/* exclusive lock */, lock_info
.l_type
);
122 *lock_owner_pid
= lock_info
.l_pid
;
126 scoped_ptr
<Socket
> ConnectToUnixDomainSocket(
127 const std::string
& socket_name
,
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
)) {
135 usleep(idle_time_msec
* 1000);
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) {
145 if (expected_welcome_message
!= buf
) {
146 LOG(ERROR
) << "Unexpected message read from daemon: " << buf
;
149 return socket
.Pass();
151 return scoped_ptr
<Socket
>();
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_
);
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
) {
185 if (setsid() < 0) { // Detach the child process from its parent.
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.
208 server_delegate_
->Init();
209 command_socket
.AddEventFd(get_exit_fd_callback_());
210 return RunServerAcceptLoop(
211 identifier_
, &command_socket
, server_delegate_
);
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()");
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()");
234 // Connect to the daemon's Unix Domain Socket.
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";
245 client_delegate_
->OnDaemonReady(client_socket
.get());
246 // Restore the previous signal action for SIGCHLD.
247 if (sigaction(SIGCHLD
, &old_action
, NULL
) < 0) {
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";
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
;
270 for (int i
= 0; i
< kNumTries
; ++i
) {
271 const pid_t previous_pid
= daemon_pid
;
272 daemon_pid
= Socket::GetUnixDomainSocketProcessOwner(identifier_
);
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.";
283 usleep(kIdleTimeMSec
* 1000);
285 LOG(ERROR
) << "Timed out while killing daemon. "
286 "It might still be tearing down.";
290 } // namespace forwarder2