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/file_util.h"
22 #include "base/files/file_path.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/string_number_conversions.h"
28 #include "base/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
) {
47 logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG
: logging::LOG_ONLY_TO_FILE
,
48 logging::DONT_LOCK_LOG_FILE
, logging::APPEND_TO_OLD_LOG_FILE
,
49 logging::ENABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS
));
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());
73 server_delegate
->OnServerExited();
77 void SigChildHandler(int signal_number
) {
78 DCHECK_EQ(signal_number
, SIGCHLD
);
80 pid_t child_pid
= waitpid(-1 /* any child */, &status
, WNOHANG
);
87 if (WIFEXITED(status
) && WEXITSTATUS(status
) == 0)
89 // Avoid using StringAppendF() since it's unsafe in a signal handler due to
91 FixedSizeStringBuilder
<256> string_builder
;
92 string_builder
.Append("Daemon (pid=%d) died unexpectedly with ", child_pid
);
93 if (WIFEXITED(status
))
94 string_builder
.Append("status %d.", WEXITSTATUS(status
));
95 else if (WIFSIGNALED(status
))
96 string_builder
.Append("signal %d.", WTERMSIG(status
));
98 string_builder
.Append("unknown reason.");
99 SIGNAL_SAFE_LOG(ERROR
, string_builder
.buffer());
102 // Note that 0 is written to |lock_owner_pid| in case the file is not locked.
103 bool GetFileLockOwnerPid(int fd
, pid_t
* lock_owner_pid
) {
104 struct flock lock_info
= {};
105 lock_info
.l_type
= F_WRLCK
;
106 lock_info
.l_whence
= SEEK_CUR
;
107 const int ret
= HANDLE_EINTR(fcntl(fd
, F_GETLK
, &lock_info
));
109 if (errno
== EBADF
) {
110 // Assume that the provided file descriptor corresponding to the PID file
111 // was valid until the daemon removed this file.
118 if (lock_info
.l_type
== F_UNLCK
) {
122 CHECK_EQ(F_WRLCK
/* exclusive lock */, lock_info
.l_type
);
123 *lock_owner_pid
= lock_info
.l_pid
;
127 scoped_ptr
<Socket
> ConnectToUnixDomainSocket(
128 const std::string
& socket_name
,
131 const std::string
& expected_welcome_message
) {
132 for (int i
= 0; i
< tries_count
; ++i
) {
133 scoped_ptr
<Socket
> socket(new Socket());
134 if (!socket
->ConnectUnix(socket_name
)) {
136 usleep(idle_time_msec
* 1000);
139 char buf
[kBufferSize
];
140 DCHECK(expected_welcome_message
.length() + 1 <= sizeof(buf
));
141 memset(buf
, 0, sizeof(buf
));
142 if (socket
->Read(buf
, sizeof(buf
)) < 0) {
146 if (expected_welcome_message
!= buf
) {
147 LOG(ERROR
) << "Unexpected message read from daemon: " << buf
;
150 return socket
.Pass();
152 return scoped_ptr
<Socket
>(NULL
);
157 Daemon::Daemon(const std::string
& log_file_path
,
158 const std::string
& identifier
,
159 ClientDelegate
* client_delegate
,
160 ServerDelegate
* server_delegate
,
161 GetExitNotifierFDCallback get_exit_fd_callback
)
162 : log_file_path_(log_file_path
),
163 identifier_(identifier
),
164 client_delegate_(client_delegate
),
165 server_delegate_(server_delegate
),
166 get_exit_fd_callback_(get_exit_fd_callback
) {
167 DCHECK(client_delegate_
);
168 DCHECK(server_delegate_
);
169 DCHECK(get_exit_fd_callback_
);
174 bool Daemon::SpawnIfNeeded() {
175 const int kSingleTry
= 1;
176 const int kNoIdleTime
= 0;
177 scoped_ptr
<Socket
> client_socket
= ConnectToUnixDomainSocket(
178 identifier_
, kSingleTry
, kNoIdleTime
, identifier_
);
179 if (!client_socket
) {
186 if (setsid() < 0) { // Detach the child process from its parent.
190 InitLoggingForDaemon(log_file_path_
);
191 CloseFD(STDIN_FILENO
);
192 CloseFD(STDOUT_FILENO
);
193 CloseFD(STDERR_FILENO
);
194 const int null_fd
= open("/dev/null", O_RDWR
);
195 CHECK_EQ(null_fd
, STDIN_FILENO
);
196 CHECK_EQ(dup(null_fd
), STDOUT_FILENO
);
197 CHECK_EQ(dup(null_fd
), STDERR_FILENO
);
198 Socket command_socket
;
199 if (!command_socket
.BindUnix(identifier_
)) {
200 scoped_ptr
<Socket
> client_socket
= ConnectToUnixDomainSocket(
201 identifier_
, kSingleTry
, kNoIdleTime
, identifier_
);
202 if (client_socket
.get()) {
203 // The daemon was spawned by a concurrent process.
209 server_delegate_
->Init();
210 command_socket
.AddEventFd(get_exit_fd_callback_());
211 exit(!RunServerAcceptLoop(identifier_
, &command_socket
,
219 // Install the custom SIGCHLD handler.
220 sigset_t blocked_signals_set
;
221 if (sigprocmask(0 /* first arg ignored */, NULL
, &blocked_signals_set
) < 0) {
222 PError("sigprocmask()");
225 struct sigaction old_action
;
226 struct sigaction new_action
;
227 memset(&new_action
, 0, sizeof(new_action
));
228 new_action
.sa_handler
= SigChildHandler
;
229 new_action
.sa_flags
= SA_NOCLDSTOP
;
230 sigemptyset(&new_action
.sa_mask
);
231 if (sigaction(SIGCHLD
, &new_action
, &old_action
) < 0) {
232 PError("sigaction()");
235 // Connect to the daemon's Unix Domain Socket.
237 if (!client_socket
) {
238 client_socket
= ConnectToUnixDomainSocket(
239 identifier_
, kNumTries
, kIdleTimeMSec
, identifier_
);
240 if (!client_socket
) {
241 LOG(ERROR
) << "Could not connect to daemon's Unix Daemon socket";
246 client_delegate_
->OnDaemonReady(client_socket
.get());
247 // Restore the previous signal action for SIGCHLD.
248 if (sigaction(SIGCHLD
, &old_action
, NULL
) < 0) {
255 bool Daemon::Kill() {
256 pid_t daemon_pid
= Socket::GetUnixDomainSocketProcessOwner(identifier_
);
258 return true; // No daemon running.
259 if (kill(daemon_pid
, SIGTERM
) < 0) {
260 if (errno
== ESRCH
/* invalid PID */)
261 // The daemon exited for some reason (e.g. kill by a process other than
262 // us) right before the call to kill() above.
267 for (int i
= 0; i
< kNumTries
; ++i
) {
268 const pid_t previous_pid
= daemon_pid
;
269 daemon_pid
= Socket::GetUnixDomainSocketProcessOwner(identifier_
);
272 // Since we are polling we might not see the 'daemon exited' event if
273 // another daemon was spawned during our idle period.
274 if (daemon_pid
!= previous_pid
) {
275 LOG(WARNING
) << "Daemon (pid=" << previous_pid
276 << ") was successfully killed but a new daemon (pid="
277 << daemon_pid
<< ") seems to be running now.";
280 usleep(kIdleTimeMSec
* 1000);
282 LOG(ERROR
) << "Timed out while killing daemon. "
283 "It might still be tearing down.";
287 } // namespace forwarder2