Chromium Code Reviews| Index: tools/android/forwarder2/device_forwarder_main.cc |
| diff --git a/tools/android/forwarder2/device_forwarder_main.cc b/tools/android/forwarder2/device_forwarder_main.cc |
| index 4a731b20a796e754da7333ecc9fe5ff6601cded5..37e1d3d418584736bfede387373329658b5681b6 100644 |
| --- a/tools/android/forwarder2/device_forwarder_main.cc |
| +++ b/tools/android/forwarder2/device_forwarder_main.cc |
| @@ -8,20 +8,32 @@ |
| #include <string> |
| +#include "base/at_exit.h" |
| +#include "base/bind.h" |
| #include "base/command_line.h" |
| +#include "base/compiler_specific.h" |
| #include "base/logging.h" |
| +#include "base/string_piece.h" |
| #include "base/stringprintf.h" |
| -#include "tools/android/common/daemon.h" |
| +#include "base/threading/thread.h" |
| +#include "tools/android/forwarder2/common.h" |
| +#include "tools/android/forwarder2/daemon.h" |
| #include "tools/android/forwarder2/device_controller.h" |
| #include "tools/android/forwarder2/pipe_notifier.h" |
| +namespace forwarder2 { |
| namespace { |
| // Leaky global instance, accessed from the signal handler. |
| forwarder2::PipeNotifier* g_notifier = NULL; |
| -// Unix domain socket name for Device Controller. |
| -const char kDefaultAdbSocket[] = "chrome_device_forwarder"; |
| +const int kBufSize = 256; |
| + |
| +const char kPIDFilePath[] = "/data/local/tmp/chrome_device_forwarder_pid"; |
| +const char kDaemonIdentifier[] = "chrome_device_forwarder_daemon"; |
| + |
| +const char kKillServerCommand[] = "kill-server"; |
| +const char kStartCommand[] = "start"; |
| void KillHandler(int /* unused */) { |
| CHECK(g_notifier); |
| @@ -29,38 +41,141 @@ void KillHandler(int /* unused */) { |
| exit(1); |
| } |
| -} // namespace |
| +// Lets the daemon fetch the exit notifier file descriptor. |
| +int GetExitNotifierFD() { |
| + DCHECK(g_notifier); |
| + return g_notifier->receiver_fd(); |
| +} |
| -int main(int argc, char** argv) { |
| - printf("Device forwarder to forward connections to the Host machine.\n"); |
| - printf("Like 'adb forward' but in the reverse direction\n"); |
| - |
| - CommandLine command_line(argc, argv); |
| - std::string adb_socket_path = command_line.GetSwitchValueASCII("adb_sock"); |
| - if (adb_socket_path.empty()) |
| - adb_socket_path = kDefaultAdbSocket; |
| - if (tools::HasHelpSwitch(command_line)) { |
| - tools::ShowHelp( |
| - argv[0], |
| - "[--adb_sock=<adb sock>]", |
| - base::StringPrintf( |
| - " <adb sock> is the Abstract Unix Domain Socket path " |
| - " where Adb is configured to forward from." |
| - " Default is %s\n", kDefaultAdbSocket).c_str()); |
| - return 0; |
| +class DaemonDelegate : public Daemon::ServerDelegate { |
|
bulach
2012/11/15 02:08:19
nit: it'd be clearer as ServerDelegate
Philippe
2012/11/15 09:56:20
Indeed.
|
| + public: |
| + // Daemon::ServerDelegate: |
| + virtual void Init() OVERRIDE { |
| + DCHECK(!g_notifier); |
| + g_notifier = new forwarder2::PipeNotifier(); |
| + signal(SIGTERM, KillHandler); |
| + signal(SIGINT, KillHandler); |
| + controller_thread_.reset(new base::Thread("controller_thread")); |
| + controller_thread_->Start(); |
| } |
| - if (!tools::HasNoSpawnDaemonSwitch(command_line)) |
| - tools::SpawnDaemon(0); |
| - g_notifier = new forwarder2::PipeNotifier(); |
| + virtual void OnClientConnected(scoped_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->exited()) |
| + return; |
| + PError("Read()"); |
| + return; |
| + } |
| + const std::string adb_socket_path(buf, bytes_read); |
| + if (adb_socket_path == adb_socket_path_) { |
| + client_socket->WriteString("OK"); |
| + return; |
| + } |
| + if (!adb_socket_path_.empty()) { |
| + client_socket->WriteString( |
| + base::StringPrintf( |
| + "ERROR: Device controller already running (adb_socket_path=%s)", |
| + adb_socket_path_.c_str())); |
| + return; |
| + } |
| + adb_socket_path_ = adb_socket_path; |
| + controller_thread_->message_loop()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&DaemonDelegate::StartController, adb_socket_path, |
| + g_notifier->receiver_fd(), base::Passed(&client_socket))); |
| + } |
| - signal(SIGTERM, KillHandler); |
| - signal(SIGINT, KillHandler); |
| - CHECK(g_notifier); |
| - forwarder2::DeviceController controller(g_notifier->receiver_fd()); |
| - if (!controller.Init(adb_socket_path)) |
| + virtual void OnServerExited() OVERRIDE {} |
| + |
| + private: |
| + static void StartController(const std::string& adb_socket_path, |
| + int exit_notifier_fd, |
| + scoped_ptr<Socket> client_socket) { |
| + forwarder2::DeviceController controller(exit_notifier_fd); |
| + if (!controller.Init(adb_socket_path)) { |
| + client_socket->WriteString( |
| + base::StringPrintf("ERROR: Could not initialize device controller " |
| + "with ADB socket path: %s", |
| + adb_socket_path.c_str())); |
| + return; |
| + } |
| + client_socket->WriteString("OK"); |
| + client_socket->Close(); |
| + // Note that the following call is blocking which explains why the device |
| + // controller has to live on a separate thread (so that the daemon command |
| + // server is not blocked). |
| + controller.Start(); |
| + } |
| + |
| + base::AtExitManager at_exit_manager_; // Used by base::Thread. |
| + scoped_ptr<base::Thread> controller_thread_; |
| + std::string adb_socket_path_; |
| +}; |
| + |
| +class ClientDelegate : public Daemon::ClientDelegate { |
| + public: |
| + ClientDelegate(const std::string& adb_socket) |
| + : adb_socket_(adb_socket), |
| + has_failed_(false) { |
| + } |
| + |
| + bool has_failed() const { return has_failed_; } |
| + |
| + // Daemon::ClientDelegate: |
| + virtual void OnDaemonReady(Socket* daemon_socket) OVERRIDE { |
| + // Send the adb socket path to the daemon. |
| + CHECK(daemon_socket->Write(adb_socket_.c_str(), |
| + adb_socket_.length())); |
| + 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(bytes_read < sizeof(buf)); |
| + buf[bytes_read] = 0; |
| + base::StringPiece msg(buf, bytes_read); |
| + if (msg.starts_with("ERROR")) { |
| + LOG(ERROR) << msg; |
| + has_failed_ = true; |
| + return; |
| + } |
| + } |
| + |
| + private: |
| + const std::string adb_socket_; |
| + bool has_failed_; |
| +}; |
| + |
| +int RunDeviceForwarder(int argc, char** argv) { |
| + if (argc != 2) { |
| + fprintf(stderr, |
| + "Usage: %s kill-server|<adb_socket>\n" |
| + " <adb_socket> is the abstract Unix Domain Socket path " |
| + "where Adb is configured to forward from.\n", argv[0]); |
| return 1; |
| - printf("Starting Device Forwarder.\n"); |
| - controller.Start(); |
| - return 0; |
| + } |
| + CommandLine::Init(argc, argv); // Needed by logging. |
| + const char* const command = |
| + !strcmp(argv[1], kKillServerCommand) ? kKillServerCommand : kStartCommand; |
| + ClientDelegate client_delegate(argv[1]); |
| + DaemonDelegate daemon_delegate; |
| + const char kLogFilePath[] = ""; // Log to logcat. |
| + Daemon daemon(kLogFilePath, kPIDFilePath, kDaemonIdentifier, &client_delegate, |
| + &daemon_delegate, &GetExitNotifierFD); |
| + |
| + if (command == kKillServerCommand) |
| + return !daemon.Kill(); |
| + |
| + DCHECK(command == kStartCommand); |
| + if (!daemon.SpawnIfNeeded()) |
| + return 1; |
| + return client_delegate.has_failed(); |
| +} |
| + |
| +} // namespace |
| +} // namespace forwarder2 |
| + |
| +int main(int argc, char** argv) { |
| + return forwarder2::RunDeviceForwarder(argc, argv); |
| } |