Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / tools / android / forwarder2 / daemon.cc
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.
4
5 #include "tools/android/forwarder2/daemon.h"
6
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>
15
16 #include <cstdlib>
17 #include <cstring>
18 #include <string>
19
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"
31
32 namespace forwarder2 {
33 namespace {
34
35 const int kBufferSize = 256;
36
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;
41
42 void InitLoggingForDaemon(const std::string& log_file) {
43   logging::LoggingSettings settings;
44   settings.logging_dest =
45       log_file.empty() ?
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));
50 }
51
52 bool RunServerAcceptLoop(const std::string& welcome_message,
53                          Socket* server_socket,
54                          Daemon::ServerDelegate* server_delegate) {
55   bool failed = false;
56   for (;;) {
57     scoped_ptr<Socket> client_socket(new Socket());
58     if (!server_socket->Accept(client_socket.get())) {
59       if (server_socket->DidReceiveEvent())
60         break;
61       PError("Accept()");
62       failed = true;
63       break;
64     }
65     if (!client_socket->Write(welcome_message.c_str(),
66                               welcome_message.length() + 1)) {
67       PError("Write()");
68       failed = true;
69       continue;
70     }
71     server_delegate->OnClientConnected(client_socket.Pass());
72   }
73   return !failed;
74 }
75
76 void SigChildHandler(int signal_number) {
77   DCHECK_EQ(signal_number, SIGCHLD);
78   int status;
79   pid_t child_pid = waitpid(-1 /* any child */, &status, WNOHANG);
80   if (child_pid < 0) {
81     PError("waitpid");
82     return;
83   }
84   if (child_pid == 0)
85     return;
86   if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
87     return;
88   // Avoid using StringAppendF() since it's unsafe in a signal handler due to
89   // its use of LOG().
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));
96   else
97     string_builder.Append("unknown reason.");
98   SIGNAL_SAFE_LOG(ERROR, string_builder.buffer());
99 }
100
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));
107   if (ret < 0) {
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.
111       *lock_owner_pid = 0;
112       return true;
113     }
114     PError("fcntl");
115     return false;
116   }
117   if (lock_info.l_type == F_UNLCK) {
118     *lock_owner_pid = 0;
119     return true;
120   }
121   CHECK_EQ(F_WRLCK /* exclusive lock */, lock_info.l_type);
122   *lock_owner_pid = lock_info.l_pid;
123   return true;
124 }
125
126 scoped_ptr<Socket> ConnectToUnixDomainSocket(
127     const std::string& socket_name,
128     int tries_count,
129     int idle_time_msec,
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)) {
134       if (idle_time_msec)
135         usleep(idle_time_msec * 1000);
136       continue;
137     }
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) {
142       perror("read");
143       continue;
144     }
145     if (expected_welcome_message != buf) {
146       LOG(ERROR) << "Unexpected message read from daemon: " << buf;
147       break;
148     }
149     return socket.Pass();
150   }
151   return scoped_ptr<Socket>();
152 }
153
154 }  // namespace
155
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_);
169 }
170
171 Daemon::~Daemon() {}
172
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) {
179     switch (fork()) {
180       case -1:
181         PError("fork()");
182         return false;
183       // Child.
184       case 0: {
185         if (setsid() < 0) {  // Detach the child process from its parent.
186           PError("setsid()");
187           exit(1);
188         }
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.
203             exit(0);
204           }
205           PError("bind()");
206           exit(1);
207         }
208         server_delegate_->Init();
209         command_socket.AddEventFd(get_exit_fd_callback_());
210         return RunServerAcceptLoop(
211             identifier_, &command_socket, server_delegate_);
212       }
213       default:
214         break;
215     }
216   }
217   // Parent.
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()");
222     return false;
223   }
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()");
232     return false;
233   }
234   // Connect to the daemon's Unix Domain Socket.
235   bool failed = false;
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";
241       failed = true;
242     }
243   }
244   if (!failed)
245     client_delegate_->OnDaemonReady(client_socket.get());
246   // Restore the previous signal action for SIGCHLD.
247   if (sigaction(SIGCHLD, &old_action, NULL) < 0) {
248     PError("sigaction");
249     failed = true;
250   }
251   return !failed;
252 }
253
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";
258     return true;
259   }
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;
265       return true;
266     }
267     PError("kill");
268     return false;
269   }
270   for (int i = 0; i < kNumTries; ++i) {
271     const pid_t previous_pid = daemon_pid;
272     daemon_pid = Socket::GetUnixDomainSocketProcessOwner(identifier_);
273     if (daemon_pid < 0)
274       return true;
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.";
281       return true;
282     }
283     usleep(kIdleTimeMSec * 1000);
284   }
285   LOG(ERROR) << "Timed out while killing daemon. "
286                 "It might still be tearing down.";
287   return false;
288 }
289
290 }  // namespace forwarder2