|  | // 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 <stdlib.h> | 
|  |  | 
|  | #include <iostream> | 
|  | #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/strings/string_piece.h" | 
|  | #include "base/strings/stringprintf.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; | 
|  |  | 
|  | const int kBufSize = 256; | 
|  |  | 
|  | const char kUnixDomainSocketPath[] = "chrome_device_forwarder"; | 
|  | const char kDaemonIdentifier[] = "chrome_device_forwarder_daemon"; | 
|  |  | 
|  | void KillHandler(int /* unused */) { | 
|  | CHECK(g_notifier); | 
|  | if (!g_notifier->Notify()) | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | // Lets the daemon fetch the exit notifier file descriptor. | 
|  | int GetExitNotifierFD() { | 
|  | DCHECK(g_notifier); | 
|  | return g_notifier->receiver_fd(); | 
|  | } | 
|  |  | 
|  | class ServerDelegate : public Daemon::ServerDelegate { | 
|  | public: | 
|  | ServerDelegate() : initialized_(false) {} | 
|  |  | 
|  | ~ServerDelegate() override { | 
|  | if (!controller_thread_.get()) | 
|  | return; | 
|  | // The DeviceController instance, if any, is constructed on the controller | 
|  | // thread. Make sure that it gets deleted on that same thread. Note that | 
|  | // DeleteSoon() is not used here since it would imply reading |controller_| | 
|  | // from the main thread while it's set on the internal thread. | 
|  | controller_thread_->task_runner()->PostTask( | 
|  | FROM_HERE, base::Bind(&ServerDelegate::DeleteControllerOnInternalThread, | 
|  | base::Unretained(this))); | 
|  | } | 
|  |  | 
|  | void DeleteControllerOnInternalThread() { | 
|  | DCHECK(controller_thread_->task_runner()->RunsTasksOnCurrentThread()); | 
|  | controller_.reset(); | 
|  | } | 
|  |  | 
|  | // Daemon::ServerDelegate: | 
|  | 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(); | 
|  | } | 
|  |  | 
|  | void OnClientConnected(scoped_ptr<Socket> client_socket) override { | 
|  | if (initialized_) { | 
|  | client_socket->WriteString("OK"); | 
|  | return; | 
|  | } | 
|  | controller_thread_->message_loop()->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&ServerDelegate::StartController, base::Unretained(this), | 
|  | GetExitNotifierFD(), base::Passed(&client_socket))); | 
|  | initialized_ = true; | 
|  | } | 
|  |  | 
|  | private: | 
|  | void StartController(int exit_notifier_fd, scoped_ptr<Socket> client_socket) { | 
|  | DCHECK(!controller_.get()); | 
|  | scoped_ptr<DeviceController> controller( | 
|  | DeviceController::Create(kUnixDomainSocketPath, exit_notifier_fd)); | 
|  | if (!controller.get()) { | 
|  | client_socket->WriteString( | 
|  | base::StringPrintf("ERROR: Could not initialize device controller " | 
|  | "with ADB socket path: %s", | 
|  | kUnixDomainSocketPath)); | 
|  | return; | 
|  | } | 
|  | controller_.swap(controller); | 
|  | controller_->Start(); | 
|  | client_socket->WriteString("OK"); | 
|  | client_socket->Close(); | 
|  | } | 
|  |  | 
|  | scoped_ptr<DeviceController> controller_; | 
|  | scoped_ptr<base::Thread> controller_thread_; | 
|  | bool initialized_; | 
|  | }; | 
|  |  | 
|  | class ClientDelegate : public Daemon::ClientDelegate { | 
|  | public: | 
|  | ClientDelegate() : has_failed_(false) {} | 
|  |  | 
|  | bool has_failed() const { return has_failed_; } | 
|  |  | 
|  | // Daemon::ClientDelegate: | 
|  | void OnDaemonReady(Socket* daemon_socket) override { | 
|  | 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<unsigned int>(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: | 
|  | bool has_failed_; | 
|  | }; | 
|  |  | 
|  | int RunDeviceForwarder(int argc, char** argv) { | 
|  | base::CommandLine::Init(argc, argv);  // Needed by logging. | 
|  | const bool kill_server = | 
|  | base::CommandLine::ForCurrentProcess()->HasSwitch("kill-server"); | 
|  | if ((kill_server && argc != 2) || (!kill_server && argc != 1)) { | 
|  | std::cerr << "Usage: device_forwarder [--kill-server]" << std::endl; | 
|  | return 1; | 
|  | } | 
|  | base::AtExitManager at_exit_manager;  // Used by base::Thread. | 
|  | ClientDelegate client_delegate; | 
|  | ServerDelegate daemon_delegate; | 
|  | const char kLogFilePath[] = "";  // Log to logcat. | 
|  | 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(); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  | }  // namespace forwarder2 | 
|  |  | 
|  | int main(int argc, char** argv) { | 
|  | return forwarder2::RunDeviceForwarder(argc, argv); | 
|  | } |