| // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include <signal.h> | 
 | #include <stddef.h> | 
 | #include <stdint.h> | 
 | #include <sys/types.h> | 
 |  | 
 | #include <cstdio> | 
 | #include <iostream> | 
 | #include <limits> | 
 | #include <memory> | 
 | #include <string> | 
 | #include <utility> | 
 | #include <vector> | 
 |  | 
 | #include "base/bind.h" | 
 | #include "base/command_line.h" | 
 | #include "base/logging.h" | 
 | #include "base/macros.h" | 
 | #include "base/pickle.h" | 
 | #include "base/strings/string_number_conversions.h" | 
 | #include "base/strings/string_piece.h" | 
 | #include "base/strings/string_util.h" | 
 | #include "tools/android/forwarder2/common.h" | 
 | #include "tools/android/forwarder2/daemon.h" | 
 | #include "tools/android/forwarder2/host_controllers_manager.h" | 
 | #include "tools/android/forwarder2/pipe_notifier.h" | 
 |  | 
 | namespace forwarder2 { | 
 |  | 
 | // Needs to be global to be able to be accessed from the signal handler. | 
 | PipeNotifier* g_notifier = NULL; | 
 |  | 
 | namespace { | 
 |  | 
 | const char kLogFilePath[] = "/tmp/host_forwarder_log"; | 
 | const char kDaemonIdentifier[] = "chrome_host_forwarder_daemon"; | 
 |  | 
 | const int kBufSize = 256; | 
 |  | 
 | // Lets the daemon fetch the exit notifier file descriptor. | 
 | int GetExitNotifierFD() { | 
 |   DCHECK(g_notifier); | 
 |   return g_notifier->receiver_fd(); | 
 | } | 
 |  | 
 | void KillHandler(int signal_number) { | 
 |   char buf[kBufSize]; | 
 |   if (signal_number != SIGTERM && signal_number != SIGINT) { | 
 |     snprintf(buf, sizeof(buf), "Ignoring unexpected signal %d.", signal_number); | 
 |     SIGNAL_SAFE_LOG(WARNING, buf); | 
 |     return; | 
 |   } | 
 |   snprintf(buf, sizeof(buf), "Received signal %d.", signal_number); | 
 |   SIGNAL_SAFE_LOG(WARNING, buf); | 
 |   static int s_kill_handler_count = 0; | 
 |   CHECK(g_notifier); | 
 |   // If for some reason the forwarder get stuck in any socket waiting forever, | 
 |   // we can send a SIGKILL or SIGINT three times to force it die | 
 |   // (non-nicely). This is useful when debugging. | 
 |   ++s_kill_handler_count; | 
 |   if (!g_notifier->Notify() || s_kill_handler_count > 2) | 
 |     exit(1); | 
 | } | 
 |  | 
 | class ServerDelegate : public Daemon::ServerDelegate { | 
 |  public: | 
 |   explicit ServerDelegate(const std::string& adb_path) | 
 |       : adb_path_(adb_path), | 
 |         has_failed_(false), | 
 |         controllers_manager_(base::BindRepeating(&GetExitNotifierFD)) {} | 
 |  | 
 |   bool has_failed() const { | 
 |     return has_failed_ || controllers_manager_.has_failed(); | 
 |   } | 
 |  | 
 |   // Daemon::ServerDelegate: | 
 |   void Init() override { | 
 |     LOG(INFO) << "Starting host process daemon (pid=" << getpid() << ")"; | 
 |     DCHECK(!g_notifier); | 
 |     g_notifier = new PipeNotifier(); | 
 |     signal(SIGTERM, KillHandler); | 
 |     signal(SIGINT, KillHandler); | 
 |   } | 
 |  | 
 |   void OnClientConnected(std::unique_ptr<Socket> client_socket) override { | 
 |     char buf[kBufSize]; | 
 |     const int bytes_read = client_socket->Read(buf, sizeof(buf)); | 
 |     if (bytes_read <= 0) { | 
 |       if (client_socket->DidReceiveEvent()) | 
 |         return; | 
 |       PError("Read()"); | 
 |       has_failed_ = true; | 
 |       return; | 
 |     } | 
 |     const base::Pickle command_pickle(buf, bytes_read); | 
 |     base::PickleIterator pickle_it(command_pickle); | 
 |  | 
 |     std::string device_serial; | 
 |     CHECK(pickle_it.ReadString(&device_serial)); | 
 |  | 
 |     int command; | 
 |     if (!pickle_it.ReadInt(&command)) { | 
 |       client_socket->WriteString("Error: missing command\n"); | 
 |       return; | 
 |     } | 
 |  | 
 |     int device_port; | 
 |     if (!pickle_it.ReadInt(&device_port)) | 
 |       device_port = -1; | 
 |  | 
 |     int host_port; | 
 |     if (!pickle_it.ReadInt(&host_port)) | 
 |       host_port = -1; | 
 |     controllers_manager_.HandleRequest(adb_path_, device_serial, command, | 
 |                                        device_port, host_port, | 
 |                                        std::move(client_socket)); | 
 |   } | 
 |  | 
 |  private: | 
 |   std::string adb_path_; | 
 |   bool has_failed_; | 
 |   HostControllersManager controllers_manager_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(ServerDelegate); | 
 | }; | 
 |  | 
 | class ClientDelegate : public Daemon::ClientDelegate { | 
 |  public: | 
 |   explicit ClientDelegate(const base::Pickle& command_pickle) | 
 |       : command_pickle_(command_pickle), has_failed_(false) {} | 
 |  | 
 |   bool has_failed() const { return has_failed_; } | 
 |  | 
 |   // Daemon::ClientDelegate: | 
 |   void OnDaemonReady(Socket* daemon_socket) override { | 
 |     // Send the forward command to the daemon. | 
 |     CHECK_EQ(static_cast<long>(command_pickle_.size()), | 
 |              daemon_socket->WriteNumBytes(command_pickle_.data(), | 
 |                                           command_pickle_.size())); | 
 |     char buf[kBufSize]; | 
 |     const int bytes_read = daemon_socket->Read( | 
 |         buf, sizeof(buf) - 1 /* leave space for null terminator */); | 
 |     CHECK_GT(bytes_read, 0); | 
 |     DCHECK(static_cast<size_t>(bytes_read) < sizeof(buf)); | 
 |     buf[bytes_read] = 0; | 
 |     base::StringPiece msg(buf, bytes_read); | 
 |     if (base::StartsWith(msg, "ERROR")) { | 
 |       LOG(ERROR) << msg; | 
 |       has_failed_ = true; | 
 |       return; | 
 |     } | 
 |     printf("%s\n", buf); | 
 |   } | 
 |  | 
 |  private: | 
 |   const base::Pickle command_pickle_; | 
 |   bool has_failed_; | 
 | }; | 
 |  | 
 | void ExitWithUsage() { | 
 |   std::cerr << "Usage: host_forwarder [options]\n\n" | 
 |                "Options:\n" | 
 |                "  --serial-id=[0-9A-Z]{16}]\n" | 
 |                "  --map DEVICE_PORT HOST_PORT\n" | 
 |                "  --unmap DEVICE_PORT\n" | 
 |                "  --unmap-all\n" | 
 |                "  --adb PATH_TO_ADB\n" | 
 |                "  --kill-server\n"; | 
 |   exit(1); | 
 | } | 
 |  | 
 | int PortToInt(const std::string& s) { | 
 |   int value; | 
 |   // Note that 0 is a valid port (used for dynamic port allocation). | 
 |   if (!base::StringToInt(s, &value) || value < 0 || | 
 |       value > std::numeric_limits<uint16_t>::max()) { | 
 |     LOG(ERROR) << "Could not convert string " << s << " to port"; | 
 |     ExitWithUsage(); | 
 |   } | 
 |   return value; | 
 | } | 
 |  | 
 | int RunHostForwarder(int argc, char** argv) { | 
 |   base::CommandLine::Init(argc, argv); | 
 |   const base::CommandLine& cmd_line = *base::CommandLine::ForCurrentProcess(); | 
 |   std::string adb_path = "adb"; | 
 |   bool kill_server = false; | 
 |  | 
 |   base::Pickle pickle; | 
 |   pickle.WriteString( | 
 |       cmd_line.HasSwitch("serial-id") ? | 
 |           cmd_line.GetSwitchValueASCII("serial-id") : std::string()); | 
 |  | 
 |   const std::vector<std::string> args = cmd_line.GetArgs(); | 
 |   if (cmd_line.HasSwitch("kill-server")) { | 
 |     kill_server = true; | 
 |   } else if (cmd_line.HasSwitch("unmap")) { | 
 |     if (args.size() != 1) | 
 |       ExitWithUsage(); | 
 |     pickle.WriteInt(UNMAP); | 
 |     pickle.WriteInt(PortToInt(args[0])); | 
 |   } else if (cmd_line.HasSwitch("unmap-all")) { | 
 |     pickle.WriteInt(UNMAP_ALL); | 
 |   } else if (cmd_line.HasSwitch("map")) { | 
 |     if (args.size() != 2) | 
 |       ExitWithUsage(); | 
 |     pickle.WriteInt(MAP); | 
 |     pickle.WriteInt(PortToInt(args[0])); | 
 |     pickle.WriteInt(PortToInt(args[1])); | 
 |   } else { | 
 |     ExitWithUsage(); | 
 |   } | 
 |  | 
 |   if (cmd_line.HasSwitch("adb")) { | 
 |     adb_path = cmd_line.GetSwitchValueASCII("adb"); | 
 |   } | 
 |  | 
 |   if (kill_server && args.size() > 0) | 
 |     ExitWithUsage(); | 
 |  | 
 |   ClientDelegate client_delegate(pickle); | 
 |   ServerDelegate daemon_delegate(adb_path); | 
 |   Daemon daemon( | 
 |       kLogFilePath, kDaemonIdentifier, &client_delegate, &daemon_delegate, | 
 |       &GetExitNotifierFD); | 
 |  | 
 |   if (kill_server) | 
 |     return !daemon.Kill(); | 
 |   if (!daemon.SpawnIfNeeded()) | 
 |     return 1; | 
 |  | 
 |   return client_delegate.has_failed() || daemon_delegate.has_failed(); | 
 | } | 
 |  | 
 | }  // namespace | 
 | }  // namespace forwarder2 | 
 |  | 
 | int main(int argc, char** argv) { | 
 |   return forwarder2::RunHostForwarder(argc, argv); | 
 | } |