Home | History | Annotate | Download | only in forwarder2
      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/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/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   settings.dcheck_state =
     50       logging::ENABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS;
     51   CHECK(logging::InitLogging(settings));
     52 }
     53 
     54 bool RunServerAcceptLoop(const std::string& welcome_message,
     55                          Socket* server_socket,
     56                          Daemon::ServerDelegate* server_delegate) {
     57   bool failed = false;
     58   for (;;) {
     59     scoped_ptr<Socket> client_socket(new Socket());
     60     if (!server_socket->Accept(client_socket.get())) {
     61       if (server_socket->DidReceiveEvent())
     62         break;
     63       PError("Accept()");
     64       failed = true;
     65       break;
     66     }
     67     if (!client_socket->Write(welcome_message.c_str(),
     68                               welcome_message.length() + 1)) {
     69       PError("Write()");
     70       failed = true;
     71       continue;
     72     }
     73     server_delegate->OnClientConnected(client_socket.Pass());
     74   }
     75   return !failed;
     76 }
     77 
     78 void SigChildHandler(int signal_number) {
     79   DCHECK_EQ(signal_number, SIGCHLD);
     80   int status;
     81   pid_t child_pid = waitpid(-1 /* any child */, &status, WNOHANG);
     82   if (child_pid < 0) {
     83     PError("waitpid");
     84     return;
     85   }
     86   if (child_pid == 0)
     87     return;
     88   if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
     89     return;
     90   // Avoid using StringAppendF() since it's unsafe in a signal handler due to
     91   // its use of LOG().
     92   FixedSizeStringBuilder<256> string_builder;
     93   string_builder.Append("Daemon (pid=%d) died unexpectedly with ", child_pid);
     94   if (WIFEXITED(status))
     95     string_builder.Append("status %d.", WEXITSTATUS(status));
     96   else if (WIFSIGNALED(status))
     97     string_builder.Append("signal %d.", WTERMSIG(status));
     98   else
     99     string_builder.Append("unknown reason.");
    100   SIGNAL_SAFE_LOG(ERROR, string_builder.buffer());
    101 }
    102 
    103 // Note that 0 is written to |lock_owner_pid| in case the file is not locked.
    104 bool GetFileLockOwnerPid(int fd, pid_t* lock_owner_pid) {
    105   struct flock lock_info = {};
    106   lock_info.l_type = F_WRLCK;
    107   lock_info.l_whence = SEEK_CUR;
    108   const int ret = HANDLE_EINTR(fcntl(fd, F_GETLK, &lock_info));
    109   if (ret < 0) {
    110     if (errno == EBADF) {
    111       // Assume that the provided file descriptor corresponding to the PID file
    112       // was valid until the daemon removed this file.
    113       *lock_owner_pid = 0;
    114       return true;
    115     }
    116     PError("fcntl");
    117     return false;
    118   }
    119   if (lock_info.l_type == F_UNLCK) {
    120     *lock_owner_pid = 0;
    121     return true;
    122   }
    123   CHECK_EQ(F_WRLCK /* exclusive lock */, lock_info.l_type);
    124   *lock_owner_pid = lock_info.l_pid;
    125   return true;
    126 }
    127 
    128 scoped_ptr<Socket> ConnectToUnixDomainSocket(
    129     const std::string& socket_name,
    130     int tries_count,
    131     int idle_time_msec,
    132     const std::string& expected_welcome_message) {
    133   for (int i = 0; i < tries_count; ++i) {
    134     scoped_ptr<Socket> socket(new Socket());
    135     if (!socket->ConnectUnix(socket_name)) {
    136       if (idle_time_msec)
    137         usleep(idle_time_msec * 1000);
    138       continue;
    139     }
    140     char buf[kBufferSize];
    141     DCHECK(expected_welcome_message.length() + 1 <= sizeof(buf));
    142     memset(buf, 0, sizeof(buf));
    143     if (socket->Read(buf, expected_welcome_message.length() + 1) < 0) {
    144       perror("read");
    145       continue;
    146     }
    147     if (expected_welcome_message != buf) {
    148       LOG(ERROR) << "Unexpected message read from daemon: " << buf;
    149       break;
    150     }
    151     return socket.Pass();
    152   }
    153   return scoped_ptr<Socket>();
    154 }
    155 
    156 }  // namespace
    157 
    158 Daemon::Daemon(const std::string& log_file_path,
    159                const std::string& identifier,
    160                ClientDelegate* client_delegate,
    161                ServerDelegate* server_delegate,
    162                GetExitNotifierFDCallback get_exit_fd_callback)
    163   : log_file_path_(log_file_path),
    164     identifier_(identifier),
    165     client_delegate_(client_delegate),
    166     server_delegate_(server_delegate),
    167     get_exit_fd_callback_(get_exit_fd_callback) {
    168   DCHECK(client_delegate_);
    169   DCHECK(server_delegate_);
    170   DCHECK(get_exit_fd_callback_);
    171 }
    172 
    173 Daemon::~Daemon() {}
    174 
    175 bool Daemon::SpawnIfNeeded() {
    176   const int kSingleTry = 1;
    177   const int kNoIdleTime = 0;
    178   scoped_ptr<Socket> client_socket = ConnectToUnixDomainSocket(
    179       identifier_, kSingleTry, kNoIdleTime, identifier_);
    180   if (!client_socket) {
    181     switch (fork()) {
    182       case -1:
    183         PError("fork()");
    184         return false;
    185       // Child.
    186       case 0: {
    187         if (setsid() < 0) {  // Detach the child process from its parent.
    188           PError("setsid()");
    189           exit(1);
    190         }
    191         InitLoggingForDaemon(log_file_path_);
    192         CloseFD(STDIN_FILENO);
    193         CloseFD(STDOUT_FILENO);
    194         CloseFD(STDERR_FILENO);
    195         const int null_fd = open("/dev/null", O_RDWR);
    196         CHECK_EQ(null_fd, STDIN_FILENO);
    197         CHECK_EQ(dup(null_fd), STDOUT_FILENO);
    198         CHECK_EQ(dup(null_fd), STDERR_FILENO);
    199         Socket command_socket;
    200         if (!command_socket.BindUnix(identifier_)) {
    201           scoped_ptr<Socket> client_socket = ConnectToUnixDomainSocket(
    202               identifier_, kSingleTry, kNoIdleTime, identifier_);
    203           if (client_socket.get()) {
    204             // The daemon was spawned by a concurrent process.
    205             exit(0);
    206           }
    207           PError("bind()");
    208           exit(1);
    209         }
    210         server_delegate_->Init();
    211         command_socket.AddEventFd(get_exit_fd_callback_());
    212         return RunServerAcceptLoop(
    213             identifier_, &command_socket, server_delegate_);
    214       }
    215       default:
    216         break;
    217     }
    218   }
    219   // Parent.
    220   // Install the custom SIGCHLD handler.
    221   sigset_t blocked_signals_set;
    222   if (sigprocmask(0 /* first arg ignored */, NULL, &blocked_signals_set) < 0) {
    223     PError("sigprocmask()");
    224     return false;
    225   }
    226   struct sigaction old_action;
    227   struct sigaction new_action;
    228   memset(&new_action, 0, sizeof(new_action));
    229   new_action.sa_handler = SigChildHandler;
    230   new_action.sa_flags = SA_NOCLDSTOP;
    231   sigemptyset(&new_action.sa_mask);
    232   if (sigaction(SIGCHLD, &new_action, &old_action) < 0) {
    233     PError("sigaction()");
    234     return false;
    235   }
    236   // Connect to the daemon's Unix Domain Socket.
    237   bool failed = false;
    238   if (!client_socket) {
    239     client_socket = ConnectToUnixDomainSocket(
    240         identifier_, kNumTries, kIdleTimeMSec, identifier_);
    241     if (!client_socket) {
    242       LOG(ERROR) << "Could not connect to daemon's Unix Daemon socket";
    243       failed = true;
    244     }
    245   }
    246   if (!failed)
    247     client_delegate_->OnDaemonReady(client_socket.get());
    248   // Restore the previous signal action for SIGCHLD.
    249   if (sigaction(SIGCHLD, &old_action, NULL) < 0) {
    250     PError("sigaction");
    251     failed = true;
    252   }
    253   return !failed;
    254 }
    255 
    256 bool Daemon::Kill() {
    257   pid_t daemon_pid = Socket::GetUnixDomainSocketProcessOwner(identifier_);
    258   if (daemon_pid < 0)
    259     return true;  // No daemon 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       return true;
    265     PError("kill");
    266     return false;
    267   }
    268   for (int i = 0; i < kNumTries; ++i) {
    269     const pid_t previous_pid = daemon_pid;
    270     daemon_pid = Socket::GetUnixDomainSocketProcessOwner(identifier_);
    271     if (daemon_pid < 0)
    272       return true;
    273     // Since we are polling we might not see the 'daemon exited' event if
    274     // another daemon was spawned during our idle period.
    275     if (daemon_pid != previous_pid) {
    276       LOG(WARNING) << "Daemon (pid=" << previous_pid
    277                    << ") was successfully killed but a new daemon (pid="
    278                    << daemon_pid << ") seems to be running now.";
    279       return true;
    280     }
    281     usleep(kIdleTimeMSec * 1000);
    282   }
    283   LOG(ERROR) << "Timed out while killing daemon. "
    284                 "It might still be tearing down.";
    285   return false;
    286 }
    287 
    288 }  // namespace forwarder2
    289