Clone of chromium aad1ce808763f59c7a3753e08f1500a104ecc6fd refs/remotes/origin/HEAD
diff --git a/tools/android/forwarder2/command.cc b/tools/android/forwarder2/command.cc new file mode 100644 index 0000000..9b0aa24 --- /dev/null +++ b/tools/android/forwarder2/command.cc
@@ -0,0 +1,96 @@ +// 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 "tools/android/forwarder2/command.h" + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "base/logging.h" +#include "base/safe_strerror_posix.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_piece.h" +#include "tools/android/forwarder2/socket.h" + +using base::StringPiece; + +namespace { + + +// Command format: +// <port>:<type> +// +// Where: +// <port> is a 5-chars zero-padded ASCII decimal integer +// matching the target port for the command (e.g. +// '08080' for port 8080) +// <type> is a 3-char zero-padded ASCII decimal integer +// matching a command::Type value (e.g. 002 for +// ACK). +// The column (:) is used as a separator for easier reading. +const int kPortStringSize = 5; +const int kCommandTypeStringSize = 2; +// Command string size also includes the ':' separator char. +const int kCommandStringSize = kPortStringSize + kCommandTypeStringSize + 1; + +} // namespace + +namespace forwarder2 { + +bool ReadCommand(Socket* socket, + int* port_out, + command::Type* command_type_out) { + char command_buffer[kCommandStringSize + 1]; + // To make logging easier. + command_buffer[kCommandStringSize] = '\0'; + + int bytes_read = socket->ReadNumBytes(command_buffer, kCommandStringSize); + if (bytes_read != kCommandStringSize) { + if (bytes_read < 0) + LOG(ERROR) << "Read() error: " << safe_strerror(errno); + else if (!bytes_read) + LOG(ERROR) << "Read() error, endpoint was unexpectedly closed."; + else + LOG(ERROR) << "Read() error, not enough data received from the socket."; + return false; + } + + StringPiece port_str(command_buffer, kPortStringSize); + if (!StringToInt(port_str, port_out)) { + LOG(ERROR) << "Could not parse the command port string: " + << port_str; + return false; + } + + StringPiece command_type_str( + &command_buffer[kPortStringSize + 1], kCommandTypeStringSize); + int command_type; + if (!StringToInt(command_type_str, &command_type)) { + LOG(ERROR) << "Could not parse the command type string: " + << command_type_str; + return false; + } + *command_type_out = static_cast<command::Type>(command_type); + return true; +} + +bool SendCommand(command::Type command, int port, Socket* socket) { + char buffer[kCommandStringSize + 1]; + int len = snprintf(buffer, sizeof(buffer), "%05d:%02d", port, command); + CHECK_EQ(len, kCommandStringSize); + // Write the full command minus the leading \0 char. + return socket->WriteNumBytes(buffer, len) == len; +} + +bool ReceivedCommand(command::Type command, Socket* socket) { + int port; + command::Type received_command; + if (!ReadCommand(socket, &port, &received_command)) + return false; + return received_command == command; +} + +} // namespace forwarder
diff --git a/tools/android/forwarder2/command.h b/tools/android/forwarder2/command.h new file mode 100644 index 0000000..8e222ef --- /dev/null +++ b/tools/android/forwarder2/command.h
@@ -0,0 +1,48 @@ +// 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. + +#ifndef TOOLS_ANDROID_FORWARDER2_COMMAND_H_ +#define TOOLS_ANDROID_FORWARDER2_COMMAND_H_ + +#include "base/basictypes.h" + +namespace forwarder2 { + +class Socket; + +namespace command { + +enum Type { + ACCEPT_ERROR = 0, + ACCEPT_SUCCESS, + ACK, + ADB_DATA_SOCKET_ERROR, + ADB_DATA_SOCKET_SUCCESS, + BIND_ERROR, + BIND_SUCCESS, + DATA_CONNECTION, + HOST_SERVER_ERROR, + HOST_SERVER_SUCCESS, + KILL_ALL_LISTENERS, + LISTEN, + UNLISTEN, + UNLISTEN_ERROR, + UNLISTEN_SUCCESS, +}; + +} // namespace command + +bool ReadCommand(Socket* socket, + int* port_out, + command::Type* command_type_out); + +// Helper function to read the command from the |socket| and return true if the +// |command| is equal to the given command parameter. +bool ReceivedCommand(command::Type command, Socket* socket); + +bool SendCommand(command::Type command, int port, Socket* socket); + +} // namespace forwarder + +#endif // TOOLS_ANDROID_FORWARDER2_COMMAND_H_
diff --git a/tools/android/forwarder2/common.cc b/tools/android/forwarder2/common.cc new file mode 100644 index 0000000..3b7387d --- /dev/null +++ b/tools/android/forwarder2/common.cc
@@ -0,0 +1,28 @@ +// 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 "tools/android/forwarder2/common.h" + +#include <errno.h> +#include <unistd.h> + +#include "base/logging.h" +#include "base/posix/eintr_wrapper.h" +#include "base/safe_strerror_posix.h" + +namespace forwarder2 { + +void PError(const char* msg) { + LOG(ERROR) << msg << ": " << safe_strerror(errno); +} + +void CloseFD(int fd) { + const int errno_copy = errno; + if (IGNORE_EINTR(close(fd)) < 0) { + PError("close"); + errno = errno_copy; + } +} + +} // namespace forwarder2
diff --git a/tools/android/forwarder2/common.h b/tools/android/forwarder2/common.h new file mode 100644 index 0000000..43de57b --- /dev/null +++ b/tools/android/forwarder2/common.h
@@ -0,0 +1,89 @@ +// 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. + +// Common helper functions/classes used both in the host and device forwarder. + +#ifndef TOOLS_ANDROID_FORWARDER2_COMMON_H_ +#define TOOLS_ANDROID_FORWARDER2_COMMON_H_ + +#include <stdarg.h> +#include <stdio.h> +#include <errno.h> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/posix/eintr_wrapper.h" + +// Preserving errno for Close() is important because the function is very often +// used in cleanup code, after an error occurred, and it is very easy to pass an +// invalid file descriptor to close() in this context, or more rarely, a +// spurious signal might make close() return -1 + setting errno to EINTR, +// masking the real reason for the original error. This leads to very unpleasant +// debugging sessions. +#define PRESERVE_ERRNO_HANDLE_EINTR(Func) \ + do { \ + int local_errno = errno; \ + (void) HANDLE_EINTR(Func); \ + errno = local_errno; \ + } while (false); + +// Wrapper around RAW_LOG() which is signal-safe. The only purpose of this macro +// is to avoid documenting uses of RawLog(). +#define SIGNAL_SAFE_LOG(Level, Msg) \ + RAW_LOG(Level, Msg); + +namespace forwarder2 { + +// Note that the two following functions are not signal-safe. + +// Chromium logging-aware implementation of libc's perror(). +void PError(const char* msg); + +// Closes the provided file descriptor and logs an error if it failed. +void CloseFD(int fd); + +// Helps build a formatted C-string allocated in a fixed-size array. This is +// useful in signal handlers where base::StringPrintf() can't be used safely +// (due to its use of LOG()). +template <int BufferSize> +class FixedSizeStringBuilder { + public: + FixedSizeStringBuilder() { + Reset(); + } + + const char* buffer() const { return buffer_; } + + void Reset() { + buffer_[0] = 0; + write_ptr_ = buffer_; + } + + // Returns the number of bytes appended to the underlying buffer or -1 if it + // failed. + int Append(const char* format, ...) PRINTF_FORMAT(/* + 1 for 'this' */ 2, 3) { + if (write_ptr_ >= buffer_ + BufferSize) + return -1; + va_list ap; + va_start(ap, format); + const int bytes_written = vsnprintf( + write_ptr_, BufferSize - (write_ptr_ - buffer_), format, ap); + va_end(ap); + if (bytes_written > 0) + write_ptr_ += bytes_written; + return bytes_written; + } + + private: + char* write_ptr_; + char buffer_[BufferSize]; + + COMPILE_ASSERT(BufferSize >= 1, Size_of_buffer_must_be_at_least_one); + DISALLOW_COPY_AND_ASSIGN(FixedSizeStringBuilder); +}; + +} // namespace forwarder2 + +#endif // TOOLS_ANDROID_FORWARDER2_COMMON_H_
diff --git a/tools/android/forwarder2/daemon.cc b/tools/android/forwarder2/daemon.cc new file mode 100644 index 0000000..19a1054 --- /dev/null +++ b/tools/android/forwarder2/daemon.cc
@@ -0,0 +1,290 @@ +// 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 "tools/android/forwarder2/daemon.h" + +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#include <cstdlib> +#include <cstring> +#include <string> + +#include "base/basictypes.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/posix/eintr_wrapper.h" +#include "base/safe_strerror_posix.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/stringprintf.h" +#include "tools/android/forwarder2/common.h" +#include "tools/android/forwarder2/socket.h" + +namespace forwarder2 { +namespace { + +const int kBufferSize = 256; + +// Timeout constant used for polling when connecting to the daemon's Unix Domain +// Socket and also when waiting for its death when it is killed. +const int kNumTries = 100; +const int kIdleTimeMSec = 20; + +void InitLoggingForDaemon(const std::string& log_file) { + logging::LoggingSettings settings; + settings.logging_dest = + log_file.empty() ? + logging::LOG_TO_SYSTEM_DEBUG_LOG : logging::LOG_TO_FILE; + settings.log_file = log_file.c_str(); + settings.lock_log = logging::DONT_LOCK_LOG_FILE; + CHECK(logging::InitLogging(settings)); +} + +bool RunServerAcceptLoop(const std::string& welcome_message, + Socket* server_socket, + Daemon::ServerDelegate* server_delegate) { + bool failed = false; + for (;;) { + scoped_ptr<Socket> client_socket(new Socket()); + if (!server_socket->Accept(client_socket.get())) { + if (server_socket->DidReceiveEvent()) + break; + PError("Accept()"); + failed = true; + break; + } + if (!client_socket->Write(welcome_message.c_str(), + welcome_message.length() + 1)) { + PError("Write()"); + failed = true; + continue; + } + server_delegate->OnClientConnected(client_socket.Pass()); + } + return !failed; +} + +void SigChildHandler(int signal_number) { + DCHECK_EQ(signal_number, SIGCHLD); + int status; + pid_t child_pid = waitpid(-1 /* any child */, &status, WNOHANG); + if (child_pid < 0) { + PError("waitpid"); + return; + } + if (child_pid == 0) + return; + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) + return; + // Avoid using StringAppendF() since it's unsafe in a signal handler due to + // its use of LOG(). + FixedSizeStringBuilder<256> string_builder; + string_builder.Append("Daemon (pid=%d) died unexpectedly with ", child_pid); + if (WIFEXITED(status)) + string_builder.Append("status %d.", WEXITSTATUS(status)); + else if (WIFSIGNALED(status)) + string_builder.Append("signal %d.", WTERMSIG(status)); + else + string_builder.Append("unknown reason."); + SIGNAL_SAFE_LOG(ERROR, string_builder.buffer()); +} + +// Note that 0 is written to |lock_owner_pid| in case the file is not locked. +bool GetFileLockOwnerPid(int fd, pid_t* lock_owner_pid) { + struct flock lock_info = {}; + lock_info.l_type = F_WRLCK; + lock_info.l_whence = SEEK_CUR; + const int ret = HANDLE_EINTR(fcntl(fd, F_GETLK, &lock_info)); + if (ret < 0) { + if (errno == EBADF) { + // Assume that the provided file descriptor corresponding to the PID file + // was valid until the daemon removed this file. + *lock_owner_pid = 0; + return true; + } + PError("fcntl"); + return false; + } + if (lock_info.l_type == F_UNLCK) { + *lock_owner_pid = 0; + return true; + } + CHECK_EQ(F_WRLCK /* exclusive lock */, lock_info.l_type); + *lock_owner_pid = lock_info.l_pid; + return true; +} + +scoped_ptr<Socket> ConnectToUnixDomainSocket( + const std::string& socket_name, + int tries_count, + int idle_time_msec, + const std::string& expected_welcome_message) { + for (int i = 0; i < tries_count; ++i) { + scoped_ptr<Socket> socket(new Socket()); + if (!socket->ConnectUnix(socket_name)) { + if (idle_time_msec) + usleep(idle_time_msec * 1000); + continue; + } + char buf[kBufferSize]; + DCHECK(expected_welcome_message.length() + 1 <= sizeof(buf)); + memset(buf, 0, sizeof(buf)); + if (socket->Read(buf, expected_welcome_message.length() + 1) < 0) { + perror("read"); + continue; + } + if (expected_welcome_message != buf) { + LOG(ERROR) << "Unexpected message read from daemon: " << buf; + break; + } + return socket.Pass(); + } + return scoped_ptr<Socket>(); +} + +} // namespace + +Daemon::Daemon(const std::string& log_file_path, + const std::string& identifier, + ClientDelegate* client_delegate, + ServerDelegate* server_delegate, + GetExitNotifierFDCallback get_exit_fd_callback) + : log_file_path_(log_file_path), + identifier_(identifier), + client_delegate_(client_delegate), + server_delegate_(server_delegate), + get_exit_fd_callback_(get_exit_fd_callback) { + DCHECK(client_delegate_); + DCHECK(server_delegate_); + DCHECK(get_exit_fd_callback_); +} + +Daemon::~Daemon() {} + +bool Daemon::SpawnIfNeeded() { + const int kSingleTry = 1; + const int kNoIdleTime = 0; + scoped_ptr<Socket> client_socket = ConnectToUnixDomainSocket( + identifier_, kSingleTry, kNoIdleTime, identifier_); + if (!client_socket) { + switch (fork()) { + case -1: + PError("fork()"); + return false; + // Child. + case 0: { + if (setsid() < 0) { // Detach the child process from its parent. + PError("setsid()"); + exit(1); + } + InitLoggingForDaemon(log_file_path_); + CloseFD(STDIN_FILENO); + CloseFD(STDOUT_FILENO); + CloseFD(STDERR_FILENO); + const int null_fd = open("/dev/null", O_RDWR); + CHECK_EQ(null_fd, STDIN_FILENO); + CHECK_EQ(dup(null_fd), STDOUT_FILENO); + CHECK_EQ(dup(null_fd), STDERR_FILENO); + Socket command_socket; + if (!command_socket.BindUnix(identifier_)) { + scoped_ptr<Socket> client_socket = ConnectToUnixDomainSocket( + identifier_, kSingleTry, kNoIdleTime, identifier_); + if (client_socket.get()) { + // The daemon was spawned by a concurrent process. + exit(0); + } + PError("bind()"); + exit(1); + } + server_delegate_->Init(); + command_socket.AddEventFd(get_exit_fd_callback_()); + return RunServerAcceptLoop( + identifier_, &command_socket, server_delegate_); + } + default: + break; + } + } + // Parent. + // Install the custom SIGCHLD handler. + sigset_t blocked_signals_set; + if (sigprocmask(0 /* first arg ignored */, NULL, &blocked_signals_set) < 0) { + PError("sigprocmask()"); + return false; + } + struct sigaction old_action; + struct sigaction new_action; + memset(&new_action, 0, sizeof(new_action)); + new_action.sa_handler = SigChildHandler; + new_action.sa_flags = SA_NOCLDSTOP; + sigemptyset(&new_action.sa_mask); + if (sigaction(SIGCHLD, &new_action, &old_action) < 0) { + PError("sigaction()"); + return false; + } + // Connect to the daemon's Unix Domain Socket. + bool failed = false; + if (!client_socket) { + client_socket = ConnectToUnixDomainSocket( + identifier_, kNumTries, kIdleTimeMSec, identifier_); + if (!client_socket) { + LOG(ERROR) << "Could not connect to daemon's Unix Daemon socket"; + failed = true; + } + } + if (!failed) + client_delegate_->OnDaemonReady(client_socket.get()); + // Restore the previous signal action for SIGCHLD. + if (sigaction(SIGCHLD, &old_action, NULL) < 0) { + PError("sigaction"); + failed = true; + } + return !failed; +} + +bool Daemon::Kill() { + pid_t daemon_pid = Socket::GetUnixDomainSocketProcessOwner(identifier_); + if (daemon_pid < 0) { + LOG(ERROR) << "No forwarder daemon seems to be running"; + return true; + } + if (kill(daemon_pid, SIGTERM) < 0) { + if (errno == ESRCH /* invalid PID */) { + // The daemon exited for some reason (e.g. kill by a process other than + // us) right before the call to kill() above. + LOG(ERROR) << "Could not kill daemon with PID " << daemon_pid; + return true; + } + PError("kill"); + return false; + } + for (int i = 0; i < kNumTries; ++i) { + const pid_t previous_pid = daemon_pid; + daemon_pid = Socket::GetUnixDomainSocketProcessOwner(identifier_); + if (daemon_pid < 0) + return true; + // Since we are polling we might not see the 'daemon exited' event if + // another daemon was spawned during our idle period. + if (daemon_pid != previous_pid) { + LOG(WARNING) << "Daemon (pid=" << previous_pid + << ") was successfully killed but a new daemon (pid=" + << daemon_pid << ") seems to be running now."; + return true; + } + usleep(kIdleTimeMSec * 1000); + } + LOG(ERROR) << "Timed out while killing daemon. " + "It might still be tearing down."; + return false; +} + +} // namespace forwarder2
diff --git a/tools/android/forwarder2/daemon.h b/tools/android/forwarder2/daemon.h new file mode 100644 index 0000000..4b05ea4 --- /dev/null +++ b/tools/android/forwarder2/daemon.h
@@ -0,0 +1,75 @@ +// 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. + +#ifndef TOOLS_ANDROID_FORWARDER2_DAEMON_H_ +#define TOOLS_ANDROID_FORWARDER2_DAEMON_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" + +namespace forwarder2 { + +class Socket; + +// Provides a way to spawn a daemon and communicate with it. +class Daemon { + public: + // Callback used by the daemon to shutdown properly. See pipe_notifier.h for + // more details. + typedef int (*GetExitNotifierFDCallback)(); + + class ClientDelegate { + public: + virtual ~ClientDelegate() {} + + // Called after the daemon is ready to receive commands. + virtual void OnDaemonReady(Socket* daemon_socket) = 0; + }; + + class ServerDelegate { + public: + virtual ~ServerDelegate() {} + + // Called after the daemon bound its Unix Domain Socket. This can be used to + // setup signal handlers or perform global initialization. + virtual void Init() = 0; + + virtual void OnClientConnected(scoped_ptr<Socket> client_socket) = 0; + }; + + // |identifier| should be a unique string identifier. It is used to + // bind/connect the underlying Unix Domain Socket. + // Note that this class does not take ownership of |client_delegate| and + // |server_delegate|. + Daemon(const std::string& log_file_path, + const std::string& identifier, + ClientDelegate* client_delegate, + ServerDelegate* server_delegate, + GetExitNotifierFDCallback get_exit_fd_callback); + + ~Daemon(); + + // Returns whether the daemon was successfully spawned. Note that this does + // not necessarily mean that the current process was forked in case the daemon + // is already running. + bool SpawnIfNeeded(); + + // Kills the daemon and blocks until it exited. Returns whether it succeeded. + bool Kill(); + + private: + const std::string log_file_path_; + const std::string identifier_; + ClientDelegate* const client_delegate_; + ServerDelegate* const server_delegate_; + const GetExitNotifierFDCallback get_exit_fd_callback_; + + DISALLOW_COPY_AND_ASSIGN(Daemon); +}; + +} // namespace forwarder2 + +#endif // TOOLS_ANDROID_FORWARDER2_DAEMON_H_
diff --git a/tools/android/forwarder2/device_controller.cc b/tools/android/forwarder2/device_controller.cc new file mode 100644 index 0000000..a4cb9c7 --- /dev/null +++ b/tools/android/forwarder2/device_controller.cc
@@ -0,0 +1,158 @@ +// 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 "tools/android/forwarder2/device_controller.h" + +#include <utility> + +#include "base/basictypes.h" +#include "base/bind.h" +#include "base/callback_helpers.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_loop_proxy.h" +#include "base/single_thread_task_runner.h" +#include "tools/android/forwarder2/command.h" +#include "tools/android/forwarder2/device_listener.h" +#include "tools/android/forwarder2/socket.h" +#include "tools/android/forwarder2/util.h" + +namespace forwarder2 { + +// static +scoped_ptr<DeviceController> DeviceController::Create( + const std::string& adb_unix_socket, + int exit_notifier_fd) { + scoped_ptr<DeviceController> device_controller; + scoped_ptr<Socket> host_socket(new Socket()); + if (!host_socket->BindUnix(adb_unix_socket)) { + PLOG(ERROR) << "Could not BindAndListen DeviceController socket on port " + << adb_unix_socket << ": "; + return device_controller.Pass(); + } + LOG(INFO) << "Listening on Unix Domain Socket " << adb_unix_socket; + device_controller.reset( + new DeviceController(host_socket.Pass(), exit_notifier_fd)); + return device_controller.Pass(); +} + +DeviceController::~DeviceController() { + DCHECK(construction_task_runner_->RunsTasksOnCurrentThread()); +} + +void DeviceController::Start() { + AcceptHostCommandSoon(); +} + +DeviceController::DeviceController(scoped_ptr<Socket> host_socket, + int exit_notifier_fd) + : host_socket_(host_socket.Pass()), + exit_notifier_fd_(exit_notifier_fd), + construction_task_runner_(base::MessageLoopProxy::current()), + weak_ptr_factory_(this) { + host_socket_->AddEventFd(exit_notifier_fd); +} + +void DeviceController::AcceptHostCommandSoon() { + base::MessageLoopProxy::current()->PostTask( + FROM_HERE, + base::Bind(&DeviceController::AcceptHostCommandInternal, + base::Unretained(this))); +} + +void DeviceController::AcceptHostCommandInternal() { + scoped_ptr<Socket> socket(new Socket); + if (!host_socket_->Accept(socket.get())) { + if (!host_socket_->DidReceiveEvent()) + PLOG(ERROR) << "Could not Accept DeviceController socket"; + else + LOG(INFO) << "Received exit notification"; + return; + } + base::ScopedClosureRunner accept_next_client( + base::Bind(&DeviceController::AcceptHostCommandSoon, + base::Unretained(this))); + // So that |socket| doesn't block on read if it has notifications. + socket->AddEventFd(exit_notifier_fd_); + int port; + command::Type command; + if (!ReadCommand(socket.get(), &port, &command)) { + LOG(ERROR) << "Invalid command received."; + return; + } + const ListenersMap::iterator listener_it = listeners_.find(port); + DeviceListener* const listener = listener_it == listeners_.end() + ? static_cast<DeviceListener*>(NULL) : listener_it->second.get(); + switch (command) { + case command::LISTEN: { + if (listener != NULL) { + LOG(WARNING) << "Already forwarding port " << port + << ". Attempting to restart the listener.\n"; + DeleteRefCountedValueInMapFromIterator(listener_it, &listeners_); + } + scoped_ptr<DeviceListener> new_listener( + DeviceListener::Create( + socket.Pass(), port, + base::Bind(&DeviceController::DeleteListenerOnError, + weak_ptr_factory_.GetWeakPtr()))); + if (!new_listener) + return; + new_listener->Start(); + // |port| can be zero, to allow dynamically allocated port, so instead, we + // call DeviceListener::listener_port() to retrieve the currently + // allocated port to this new listener. + const int listener_port = new_listener->listener_port(); + listeners_.insert( + std::make_pair(listener_port, + linked_ptr<DeviceListener>(new_listener.release()))); + LOG(INFO) << "Forwarding device port " << listener_port << " to host."; + break; + } + case command::DATA_CONNECTION: + if (listener == NULL) { + LOG(ERROR) << "Data Connection command received, but " + << "listener has not been set up yet for port " << port; + // After this point it is assumed that, once we close our Adb Data + // socket, the Adb forwarder command will propagate the closing of + // sockets all the way to the host side. + break; + } + listener->SetAdbDataSocket(socket.Pass()); + break; + case command::UNLISTEN: + LOG(INFO) << "Unmapping port " << port; + if (!listener) { + LOG(ERROR) << "No listener found for port " << port; + SendCommand(command::UNLISTEN_ERROR, port, socket.get()); + break; + } + DeleteRefCountedValueInMapFromIterator(listener_it, &listeners_); + SendCommand(command::UNLISTEN_SUCCESS, port, socket.get()); + break; + default: + // TODO(felipeg): add a KillAllListeners command. + LOG(ERROR) << "Invalid command received. Port: " << port + << " Command: " << command; + } +} + +// static +void DeviceController::DeleteListenerOnError( + const base::WeakPtr<DeviceController>& device_controller_ptr, + scoped_ptr<DeviceListener> device_listener) { + DeviceListener* const listener = device_listener.release(); + DeviceController* const controller = device_controller_ptr.get(); + if (!controller) { + // |listener| was already deleted by the controller that did have its + // ownership. + return; + } + DCHECK(controller->construction_task_runner_->RunsTasksOnCurrentThread()); + bool listener_did_exist = DeleteRefCountedValueInMap( + listener->listener_port(), &controller->listeners_); + DCHECK(listener_did_exist); + // Note that |listener| was deleted by DeleteRefCountedValueInMap(). +} + +} // namespace forwarder
diff --git a/tools/android/forwarder2/device_controller.h b/tools/android/forwarder2/device_controller.h new file mode 100644 index 0000000..567a08d --- /dev/null +++ b/tools/android/forwarder2/device_controller.h
@@ -0,0 +1,71 @@ +// 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. + +#ifndef TOOLS_ANDROID_FORWARDER2_DEVICE_CONTROLLER_H_ +#define TOOLS_ANDROID_FORWARDER2_DEVICE_CONTROLLER_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/containers/hash_tables.h" +#include "base/memory/linked_ptr.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "tools/android/forwarder2/socket.h" + +namespace base { +class SingleThreadTaskRunner; +} // namespace base + +namespace forwarder2 { + +class DeviceListener; + +// There is a single DeviceController per device_forwarder process, and it is in +// charge of managing all active redirections on the device side (one +// DeviceListener each). +class DeviceController { + public: + static scoped_ptr<DeviceController> Create(const std::string& adb_unix_socket, + int exit_notifier_fd); + ~DeviceController(); + + void Start(); + + private: + typedef base::hash_map< + int /* port */, linked_ptr<DeviceListener> > ListenersMap; + + DeviceController(scoped_ptr<Socket> host_socket, int exit_notifier_fd); + + void AcceptHostCommandSoon(); + void AcceptHostCommandInternal(); + + // Note that this can end up being called after the DeviceController is + // destroyed which is why a weak pointer is used. + static void DeleteListenerOnError( + const base::WeakPtr<DeviceController>& device_controller_ptr, + scoped_ptr<DeviceListener> device_listener); + + const scoped_ptr<Socket> host_socket_; + // Used to notify the controller to exit. + const int exit_notifier_fd_; + // Lets ensure DeviceListener instances are deleted on the thread they were + // created on. + const scoped_refptr<base::SingleThreadTaskRunner> construction_task_runner_; + ListenersMap listeners_; + + //WeakPtrFactory's documentation says: + // Member variables should appear before the WeakPtrFactory, to ensure + // that any WeakPtrs to Controller are invalidated before its members + // variable's destructors are executed, rendering them invalid. + base::WeakPtrFactory<DeviceController> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(DeviceController); +}; + +} // namespace forwarder + +#endif // TOOLS_ANDROID_FORWARDER2_DEVICE_CONTROLLER_H_
diff --git a/tools/android/forwarder2/device_forwarder_main.cc b/tools/android/forwarder2/device_forwarder_main.cc new file mode 100644 index 0000000..cad46f4 --- /dev/null +++ b/tools/android/forwarder2/device_forwarder_main.cc
@@ -0,0 +1,169 @@ +// 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) {} + + virtual ~ServerDelegate() { + 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_->message_loop_proxy()->PostTask( + FROM_HERE, + base::Bind(&ServerDelegate::DeleteControllerOnInternalThread, + base::Unretained(this))); + } + + void DeleteControllerOnInternalThread() { + DCHECK( + controller_thread_->message_loop_proxy()->RunsTasksOnCurrentThread()); + controller_.reset(); + } + + // 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(); + } + + virtual 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: + virtual 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(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) { + CommandLine::Init(argc, argv); // Needed by logging. + const bool kill_server = 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); +}
diff --git a/tools/android/forwarder2/device_listener.cc b/tools/android/forwarder2/device_listener.cc new file mode 100644 index 0000000..b48a746 --- /dev/null +++ b/tools/android/forwarder2/device_listener.cc
@@ -0,0 +1,130 @@ +// 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 "tools/android/forwarder2/device_listener.h" + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/callback.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_loop_proxy.h" +#include "base/single_thread_task_runner.h" +#include "tools/android/forwarder2/command.h" +#include "tools/android/forwarder2/forwarder.h" +#include "tools/android/forwarder2/socket.h" + +namespace forwarder2 { + +// static +scoped_ptr<DeviceListener> DeviceListener::Create( + scoped_ptr<Socket> host_socket, + int listener_port, + const ErrorCallback& error_callback) { + scoped_ptr<Socket> listener_socket(new Socket()); + scoped_ptr<DeviceListener> device_listener; + if (!listener_socket->BindTcp("", listener_port)) { + LOG(ERROR) << "Device could not bind and listen to local port " + << listener_port; + SendCommand(command::BIND_ERROR, listener_port, host_socket.get()); + return device_listener.Pass(); + } + // In case the |listener_port_| was zero, GetPort() will return the + // currently (non-zero) allocated port for this socket. + listener_port = listener_socket->GetPort(); + SendCommand(command::BIND_SUCCESS, listener_port, host_socket.get()); + device_listener.reset( + new DeviceListener(listener_socket.Pass(), host_socket.Pass(), + listener_port, error_callback)); + return device_listener.Pass(); +} + +DeviceListener::~DeviceListener() { + DCHECK(deletion_task_runner_->RunsTasksOnCurrentThread()); + deletion_notifier_.Notify(); +} + +void DeviceListener::Start() { + thread_.Start(); + AcceptNextClientSoon(); +} + +void DeviceListener::SetAdbDataSocket(scoped_ptr<Socket> adb_data_socket) { + thread_.message_loop_proxy()->PostTask( + FROM_HERE, + base::Bind(&DeviceListener::OnAdbDataSocketReceivedOnInternalThread, + base::Unretained(this), base::Passed(&adb_data_socket))); +} + +DeviceListener::DeviceListener(scoped_ptr<Socket> listener_socket, + scoped_ptr<Socket> host_socket, + int port, + const ErrorCallback& error_callback) + : self_deleter_helper_(this, error_callback), + listener_socket_(listener_socket.Pass()), + host_socket_(host_socket.Pass()), + listener_port_(port), + deletion_task_runner_(base::MessageLoopProxy::current()), + thread_("DeviceListener") { + CHECK(host_socket_.get()); + DCHECK(deletion_task_runner_.get()); + host_socket_->AddEventFd(deletion_notifier_.receiver_fd()); + listener_socket_->AddEventFd(deletion_notifier_.receiver_fd()); +} + +void DeviceListener::AcceptNextClientSoon() { + thread_.message_loop_proxy()->PostTask( + FROM_HERE, + base::Bind(&DeviceListener::AcceptClientOnInternalThread, + base::Unretained(this))); +} + +void DeviceListener::AcceptClientOnInternalThread() { + device_data_socket_.reset(new Socket()); + if (!listener_socket_->Accept(device_data_socket_.get())) { + if (listener_socket_->DidReceiveEvent()) { + LOG(INFO) << "Received exit notification, stopped accepting clients."; + OnInternalThreadError(); + return; + } + LOG(WARNING) << "Could not Accept in ListenerSocket."; + SendCommand(command::ACCEPT_ERROR, listener_port_, host_socket_.get()); + OnInternalThreadError(); + return; + } + SendCommand(command::ACCEPT_SUCCESS, listener_port_, host_socket_.get()); + if (!ReceivedCommand(command::HOST_SERVER_SUCCESS, + host_socket_.get())) { + SendCommand(command::ACK, listener_port_, host_socket_.get()); + LOG(ERROR) << "Host could not connect to server."; + device_data_socket_->Close(); + if (host_socket_->has_error()) { + LOG(ERROR) << "Adb Control connection lost. " + << "Listener port: " << listener_port_; + OnInternalThreadError(); + return; + } + // It can continue if the host forwarder could not connect to the host + // server but the control connection is still alive (no errors). The device + // acknowledged that (above), and it can re-try later. + AcceptNextClientSoon(); + return; + } +} + +void DeviceListener::OnAdbDataSocketReceivedOnInternalThread( + scoped_ptr<Socket> adb_data_socket) { + DCHECK(adb_data_socket); + SendCommand(command::ADB_DATA_SOCKET_SUCCESS, listener_port_, + host_socket_.get()); + forwarders_manager_.CreateAndStartNewForwarder( + device_data_socket_.Pass(), adb_data_socket.Pass()); + AcceptNextClientSoon(); +} + +void DeviceListener::OnInternalThreadError() { + self_deleter_helper_.MaybeSelfDeleteSoon(); +} + +} // namespace forwarder
diff --git a/tools/android/forwarder2/device_listener.h b/tools/android/forwarder2/device_listener.h new file mode 100644 index 0000000..c7724f4 --- /dev/null +++ b/tools/android/forwarder2/device_listener.h
@@ -0,0 +1,106 @@ +// 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. + +#ifndef TOOLS_ANDROID_FORWARDER2_DEVICE_LISTENER_H_ +#define TOOLS_ANDROID_FORWARDER2_DEVICE_LISTENER_H_ + +#include "base/basictypes.h" +#include "base/callback.h" +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/threading/thread.h" +#include "tools/android/forwarder2/forwarders_manager.h" +#include "tools/android/forwarder2/pipe_notifier.h" +#include "tools/android/forwarder2/self_deleter_helper.h" +#include "tools/android/forwarder2/socket.h" + +namespace base { +class SingleThreadTaskRunner; +} // namespace base + +namespace forwarder2 { + +class Forwarder; + +// A DeviceListener instance is used in the device_forwarder program to bind to +// a specific device-side |port| and wait for client connections. When a +// connection happens, it informs the corresponding HostController instance +// running on the host, through |host_socket|. Then the class expects a call to +// its SetAdbDataSocket() method (performed by the device controller) once the +// host opened a new connection to the device. When this happens, a new internal +// Forwarder instance is started. +// Note that instances of this class are owned by the device controller which +// creates and destroys them on the same thread. In case an internal error +// happens on the DeviceListener's internal thread, the DeviceListener +// can also self-delete by executing the user-provided callback on the thread +// the DeviceListener was created on. +// Note that the DeviceListener's destructor joins its internal thread (i.e. +// waits for its completion) which means that the internal thread is guaranteed +// not to be running anymore once the object is deleted. +class DeviceListener { + public: + // Callback that is used for self-deletion on error to let the device + // controller perform some additional cleanup work (e.g. removing the device + // listener instance from its internal map before deleting it). + typedef base::Callback<void (scoped_ptr<DeviceListener>)> ErrorCallback; + + static scoped_ptr<DeviceListener> Create(scoped_ptr<Socket> host_socket, + int port, + const ErrorCallback& error_callback); + + ~DeviceListener(); + + void Start(); + + void SetAdbDataSocket(scoped_ptr<Socket> adb_data_socket); + + int listener_port() const { return listener_port_; } + + private: + DeviceListener(scoped_ptr<Socket> listener_socket, + scoped_ptr<Socket> host_socket, + int port, + const ErrorCallback& error_callback); + + // Pushes an AcceptClientOnInternalThread() task to the internal thread's + // message queue in order to wait for a new client soon. + void AcceptNextClientSoon(); + + void AcceptClientOnInternalThread(); + + void OnAdbDataSocketReceivedOnInternalThread( + scoped_ptr<Socket> adb_data_socket); + + void OnInternalThreadError(); + + SelfDeleterHelper<DeviceListener> self_deleter_helper_; + // Used for the listener thread to be notified on destruction. We have one + // notifier per Listener thread since each Listener thread may be requested to + // exit for different reasons independently from each other and independent + // from the main program, ex. when the host requests to forward/listen the + // same port again. Both the |host_socket_| and |listener_socket_| must share + // the same receiver file descriptor from |deletion_notifier_| and it is set + // in the constructor. + PipeNotifier deletion_notifier_; + // The local device listener socket for accepting connections from the local + // port (listener_port_). + const scoped_ptr<Socket> listener_socket_; + // The listener socket for sending control commands. + const scoped_ptr<Socket> host_socket_; + scoped_ptr<Socket> device_data_socket_; + const int listener_port_; + // Task runner used for deletion set at construction time (i.e. the object is + // deleted on the same thread it is created on). + scoped_refptr<base::SingleThreadTaskRunner> deletion_task_runner_; + base::Thread thread_; + ForwardersManager forwarders_manager_; + + DISALLOW_COPY_AND_ASSIGN(DeviceListener); +}; + +} // namespace forwarder + +#endif // TOOLS_ANDROID_FORWARDER2_DEVICE_LISTENER_H_
diff --git a/tools/android/forwarder2/forwarder.cc b/tools/android/forwarder2/forwarder.cc new file mode 100644 index 0000000..1e0bcd0 --- /dev/null +++ b/tools/android/forwarder2/forwarder.cc
@@ -0,0 +1,255 @@ +// 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 "tools/android/forwarder2/forwarder.h" + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/posix/eintr_wrapper.h" +#include "tools/android/forwarder2/socket.h" + +namespace forwarder2 { +namespace { + +const int kBufferSize = 32 * 1024; + +} // namespace + + +// Helper class to buffer reads and writes from one socket to another. +// Each implements a small buffer connected two one input socket, and +// one output socket. +// +// socket_from_ ---> [BufferedCopier] ---> socket_to_ +// +// These objects are used in a pair to handle duplex traffic, as in: +// +// ------> [BufferedCopier_1] ---> +// / \ +// socket_1 * * socket_2 +// \ / +// <------ [BufferedCopier_2] <---- +// +// When a BufferedCopier is in the READING state (see below), it only listens +// to events on its input socket, and won't detect when its output socket +// disconnects. To work around this, its peer will call its Close() method +// when that happens. + +class Forwarder::BufferedCopier { + public: + // Possible states: + // READING - Empty buffer and Waiting for input. + // WRITING - Data in buffer, and waiting for output. + // CLOSING - Like WRITING, but do not try to read after that. + // CLOSED - Completely closed. + // + // State transitions are: + // + // T01: READING ---[receive data]---> WRITING + // T02: READING ---[error on input socket]---> CLOSED + // T03: READING ---[Close() call]---> CLOSED + // + // T04: WRITING ---[write partial data]---> WRITING + // T05: WRITING ---[write all data]----> READING + // T06: WRITING ---[error on output socket]----> CLOSED + // T07: WRITING ---[Close() call]---> CLOSING + // + // T08: CLOSING ---[write partial data]---> CLOSING + // T09: CLOSING ---[write all data]----> CLOSED + // T10: CLOSING ---[Close() call]---> CLOSING + // T11: CLOSING ---[error on output socket] ---> CLOSED + // + enum State { + STATE_READING = 0, + STATE_WRITING = 1, + STATE_CLOSING = 2, + STATE_CLOSED = 3, + }; + + // Does NOT own the pointers. + BufferedCopier(Socket* socket_from, Socket* socket_to) + : socket_from_(socket_from), + socket_to_(socket_to), + bytes_read_(0), + write_offset_(0), + peer_(NULL), + state_(STATE_READING) {} + + // Sets the 'peer_' field pointing to the other BufferedCopier in a pair. + void SetPeer(BufferedCopier* peer) { + DCHECK(!peer_); + peer_ = peer; + } + + bool is_closed() const { return state_ == STATE_CLOSED; } + + // Gently asks to close a buffer. Called either by the peer or the forwarder. + void Close() { + switch (state_) { + case STATE_READING: + state_ = STATE_CLOSED; // T03 + break; + case STATE_WRITING: + state_ = STATE_CLOSING; // T07 + break; + case STATE_CLOSING: + break; // T10 + case STATE_CLOSED: + ; + } + } + + // Call this before select(). This updates |read_fds|, + // |write_fds| and |max_fd| appropriately *if* the buffer isn't closed. + void PrepareSelect(fd_set* read_fds, fd_set* write_fds, int* max_fd) { + int fd; + switch (state_) { + case STATE_READING: + DCHECK(bytes_read_ == 0); + DCHECK(write_offset_ == 0); + fd = socket_from_->fd(); + if (fd < 0) { + ForceClose(); // T02 + return; + } + FD_SET(fd, read_fds); + break; + + case STATE_WRITING: + case STATE_CLOSING: + DCHECK(bytes_read_ > 0); + DCHECK(write_offset_ < bytes_read_); + fd = socket_to_->fd(); + if (fd < 0) { + ForceClose(); // T06 + return; + } + FD_SET(fd, write_fds); + break; + + case STATE_CLOSED: + return; + } + *max_fd = std::max(*max_fd, fd); + } + + // Call this after a select() call to operate over the buffer. + void ProcessSelect(const fd_set& read_fds, const fd_set& write_fds) { + int fd, ret; + switch (state_) { + case STATE_READING: + fd = socket_from_->fd(); + if (fd < 0) { + state_ = STATE_CLOSED; // T02 + return; + } + if (!FD_ISSET(fd, &read_fds)) + return; + + ret = socket_from_->NonBlockingRead(buffer_, kBufferSize); + if (ret <= 0) { + ForceClose(); // T02 + return; + } + bytes_read_ = ret; + write_offset_ = 0; + state_ = STATE_WRITING; // T01 + break; + + case STATE_WRITING: + case STATE_CLOSING: + fd = socket_to_->fd(); + if (fd < 0) { + ForceClose(); // T06 + T11 + return; + } + if (!FD_ISSET(fd, &write_fds)) + return; + + ret = socket_to_->NonBlockingWrite(buffer_ + write_offset_, + bytes_read_ - write_offset_); + if (ret <= 0) { + ForceClose(); // T06 + T11 + return; + } + + write_offset_ += ret; + if (write_offset_ < bytes_read_) + return; // T08 + T04 + + write_offset_ = 0; + bytes_read_ = 0; + if (state_ == STATE_CLOSING) { + ForceClose(); // T09 + return; + } + state_ = STATE_READING; // T05 + break; + + case STATE_CLOSED: + ; + } + } + + private: + // Internal method used to close the buffer and notify the peer, if any. + void ForceClose() { + if (peer_) { + peer_->Close(); + peer_ = NULL; + } + state_ = STATE_CLOSED; + } + + // Not owned. + Socket* socket_from_; + Socket* socket_to_; + + int bytes_read_; + int write_offset_; + BufferedCopier* peer_; + State state_; + char buffer_[kBufferSize]; + + DISALLOW_COPY_AND_ASSIGN(BufferedCopier); +}; + +Forwarder::Forwarder(scoped_ptr<Socket> socket1, + scoped_ptr<Socket> socket2) + : socket1_(socket1.Pass()), + socket2_(socket2.Pass()), + buffer1_(new BufferedCopier(socket1_.get(), socket2_.get())), + buffer2_(new BufferedCopier(socket2_.get(), socket1_.get())) { + buffer1_->SetPeer(buffer2_.get()); + buffer2_->SetPeer(buffer1_.get()); +} + +Forwarder::~Forwarder() { + DCHECK(thread_checker_.CalledOnValidThread()); +} + +void Forwarder::RegisterFDs(fd_set* read_fds, fd_set* write_fds, int* max_fd) { + DCHECK(thread_checker_.CalledOnValidThread()); + buffer1_->PrepareSelect(read_fds, write_fds, max_fd); + buffer2_->PrepareSelect(read_fds, write_fds, max_fd); +} + +void Forwarder::ProcessEvents(const fd_set& read_fds, const fd_set& write_fds) { + DCHECK(thread_checker_.CalledOnValidThread()); + buffer1_->ProcessSelect(read_fds, write_fds); + buffer2_->ProcessSelect(read_fds, write_fds); +} + +bool Forwarder::IsClosed() const { + DCHECK(thread_checker_.CalledOnValidThread()); + return buffer1_->is_closed() && buffer2_->is_closed(); +} + +void Forwarder::Shutdown() { + DCHECK(thread_checker_.CalledOnValidThread()); + buffer1_->Close(); + buffer2_->Close(); +} + +} // namespace forwarder2
diff --git a/tools/android/forwarder2/forwarder.gyp b/tools/android/forwarder2/forwarder.gyp new file mode 100644 index 0000000..fbf5eba --- /dev/null +++ b/tools/android/forwarder2/forwarder.gyp
@@ -0,0 +1,70 @@ +# 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. + +{ + 'targets': [ + { + 'target_name': 'forwarder2', + 'type': 'none', + 'dependencies': [ + 'device_forwarder', + 'host_forwarder#host', + ], + # For the component build, ensure dependent shared libraries are stripped + # and put alongside forwarder to simplify pushing to the device. + 'variables': { + 'output_dir': '<(PRODUCT_DIR)/forwarder_dist/', + 'native_binary': '<(PRODUCT_DIR)/device_forwarder', + }, + 'includes': ['../../../build/android/native_app_dependencies.gypi'], + }, + { + 'target_name': 'device_forwarder', + 'type': 'executable', + 'toolsets': ['target'], + 'dependencies': [ + '../../../base/base.gyp:base', + '../common/common.gyp:android_tools_common', + ], + 'include_dirs': [ + '../../..', + ], + 'sources': [ + 'command.cc', + 'common.cc', + 'daemon.cc', + 'device_controller.cc', + 'device_forwarder_main.cc', + 'device_listener.cc', + 'forwarder.cc', + 'forwarders_manager.cc', + 'pipe_notifier.cc', + 'socket.cc', + ], + }, + { + 'target_name': 'host_forwarder', + 'type': 'executable', + 'toolsets': ['host'], + 'dependencies': [ + '../../../base/base.gyp:base', + '../common/common.gyp:android_tools_common', + ], + 'include_dirs': [ + '../../..', + ], + 'sources': [ + 'command.cc', + 'common.cc', + 'daemon.cc', + 'forwarder.cc', + 'forwarders_manager.cc', + 'host_controller.cc', + 'host_forwarder_main.cc', + 'pipe_notifier.cc', + 'socket.cc', + ], + }, + ], +}
diff --git a/tools/android/forwarder2/forwarder.h b/tools/android/forwarder2/forwarder.h new file mode 100644 index 0000000..0be86fc --- /dev/null +++ b/tools/android/forwarder2/forwarder.h
@@ -0,0 +1,47 @@ +// 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. + +#ifndef TOOLS_ANDROID_FORWARDER2_FORWARDER_H_ +#define TOOLS_ANDROID_FORWARDER2_FORWARDER_H_ + +#include <sys/select.h> + +#include "base/memory/scoped_ptr.h" +#include "base/threading/thread_checker.h" + +namespace forwarder2 { + +class Socket; + +// Internal class that forwards traffic between |socket1| and |socket2|. Note +// that this class is not thread-safe. +class Forwarder { + public: + Forwarder(scoped_ptr<Socket> socket1, scoped_ptr<Socket> socket2); + + ~Forwarder(); + + void RegisterFDs(fd_set* read_fds, fd_set* write_fds, int* max_fd); + + void ProcessEvents(const fd_set& read_fds, const fd_set& write_fds); + + bool IsClosed() const; + + void Shutdown(); + + private: + class BufferedCopier; + + base::ThreadChecker thread_checker_; + const scoped_ptr<Socket> socket1_; + const scoped_ptr<Socket> socket2_; + // Copies data from socket1 to socket2. + const scoped_ptr<BufferedCopier> buffer1_; + // Copies data from socket2 to socket1. + const scoped_ptr<BufferedCopier> buffer2_; +}; + +} // namespace forwarder2 + +#endif // TOOLS_ANDROID_FORWARDER2_FORWARDER_H_
diff --git a/tools/android/forwarder2/forwarders_manager.cc b/tools/android/forwarder2/forwarders_manager.cc new file mode 100644 index 0000000..1795cb5 --- /dev/null +++ b/tools/android/forwarder2/forwarders_manager.cc
@@ -0,0 +1,132 @@ +// Copyright 2013 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 "tools/android/forwarder2/forwarders_manager.h" + +#include <sys/select.h> +#include <unistd.h> + +#include <algorithm> + +#include "base/basictypes.h" +#include "base/bind.h" +#include "base/callback_helpers.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/message_loop/message_loop_proxy.h" +#include "base/posix/eintr_wrapper.h" +#include "tools/android/forwarder2/forwarder.h" +#include "tools/android/forwarder2/socket.h" + +namespace forwarder2 { + +ForwardersManager::ForwardersManager() : thread_("ForwardersManagerThread") { + thread_.Start(); + WaitForEventsOnInternalThreadSoon(); +} + + +ForwardersManager::~ForwardersManager() { + deletion_notifier_.Notify(); +} + +void ForwardersManager::CreateAndStartNewForwarder(scoped_ptr<Socket> socket1, + scoped_ptr<Socket> socket2) { + // Note that the internal Forwarder vector is populated on the internal thread + // which is the only thread from which it's accessed. + thread_.message_loop_proxy()->PostTask( + FROM_HERE, + base::Bind(&ForwardersManager::CreateNewForwarderOnInternalThread, + base::Unretained(this), base::Passed(&socket1), + base::Passed(&socket2))); + + // Guarantees that the CreateNewForwarderOnInternalThread callback posted to + // the internal thread gets executed immediately. + wakeup_notifier_.Notify(); +} + +void ForwardersManager::CreateNewForwarderOnInternalThread( + scoped_ptr<Socket> socket1, + scoped_ptr<Socket> socket2) { + DCHECK(thread_.message_loop_proxy()->RunsTasksOnCurrentThread()); + forwarders_.push_back(new Forwarder(socket1.Pass(), socket2.Pass())); +} + +void ForwardersManager::WaitForEventsOnInternalThreadSoon() { + thread_.message_loop_proxy()->PostTask( + FROM_HERE, + base::Bind(&ForwardersManager::WaitForEventsOnInternalThread, + base::Unretained(this))); +} + +void ForwardersManager::WaitForEventsOnInternalThread() { + DCHECK(thread_.message_loop_proxy()->RunsTasksOnCurrentThread()); + fd_set read_fds; + fd_set write_fds; + + FD_ZERO(&read_fds); + FD_ZERO(&write_fds); + + // Populate the file descriptor sets. + int max_fd = -1; + for (ScopedVector<Forwarder>::iterator it = forwarders_.begin(); + it != forwarders_.end(); ++it) { + Forwarder* const forwarder = *it; + forwarder->RegisterFDs(&read_fds, &write_fds, &max_fd); + } + + const int notifier_fds[] = { + wakeup_notifier_.receiver_fd(), + deletion_notifier_.receiver_fd(), + }; + + for (int i = 0; i < arraysize(notifier_fds); ++i) { + const int notifier_fd = notifier_fds[i]; + DCHECK_GT(notifier_fd, -1); + FD_SET(notifier_fd, &read_fds); + max_fd = std::max(max_fd, notifier_fd); + } + + const int ret = HANDLE_EINTR( + select(max_fd + 1, &read_fds, &write_fds, NULL, NULL)); + if (ret < 0) { + PLOG(ERROR) << "select"; + return; + } + + const bool must_shutdown = FD_ISSET( + deletion_notifier_.receiver_fd(), &read_fds); + if (must_shutdown && forwarders_.empty()) + return; + + base::ScopedClosureRunner wait_for_events_soon( + base::Bind(&ForwardersManager::WaitForEventsOnInternalThreadSoon, + base::Unretained(this))); + + if (FD_ISSET(wakeup_notifier_.receiver_fd(), &read_fds)) { + // Note that the events on FDs other than the wakeup notifier one, if any, + // will be processed upon the next select(). + wakeup_notifier_.Reset(); + return; + } + + // Notify the Forwarder instances and remove the ones that are closed. + for (size_t i = 0; i < forwarders_.size(); ) { + Forwarder* const forwarder = forwarders_[i]; + forwarder->ProcessEvents(read_fds, write_fds); + + if (must_shutdown) + forwarder->Shutdown(); + + if (!forwarder->IsClosed()) { + ++i; + continue; + } + + std::swap(forwarders_[i], forwarders_.back()); + forwarders_.pop_back(); + } +} + +} // namespace forwarder2
diff --git a/tools/android/forwarder2/forwarders_manager.h b/tools/android/forwarder2/forwarders_manager.h new file mode 100644 index 0000000..4c6dea6 --- /dev/null +++ b/tools/android/forwarder2/forwarders_manager.h
@@ -0,0 +1,45 @@ +// Copyright 2013 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. + +#ifndef TOOLS_ANDROID_FORWARDER2_FORWARDERS_MANAGER_H_ +#define TOOLS_ANDROID_FORWARDER2_FORWARDERS_MANAGER_H_ + +#include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" +#include "base/threading/thread.h" +#include "tools/android/forwarder2/pipe_notifier.h" + +namespace forwarder2 { + +class Forwarder; +class Socket; + +// Creates, owns and notifies Forwarder instances on its own internal thread. +class ForwardersManager { + public: + ForwardersManager(); + + // Must be called on the thread the constructor was called on. + ~ForwardersManager(); + + // Can be called on any thread. + void CreateAndStartNewForwarder(scoped_ptr<Socket> socket1, + scoped_ptr<Socket> socket2); + + private: + void CreateNewForwarderOnInternalThread(scoped_ptr<Socket> socket1, + scoped_ptr<Socket> socket2); + + void WaitForEventsOnInternalThreadSoon(); + void WaitForEventsOnInternalThread(); + + ScopedVector<Forwarder> forwarders_; + PipeNotifier deletion_notifier_; + PipeNotifier wakeup_notifier_; + base::Thread thread_; +}; + +} // namespace forwarder2 + +#endif // TOOLS_ANDROID_FORWARDER2_FORWARDERS_MANAGER_H_
diff --git a/tools/android/forwarder2/host_controller.cc b/tools/android/forwarder2/host_controller.cc new file mode 100644 index 0000000..94e63ec --- /dev/null +++ b/tools/android/forwarder2/host_controller.cc
@@ -0,0 +1,170 @@ +// 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 "tools/android/forwarder2/host_controller.h" + +#include <string> + +#include "base/basictypes.h" +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "tools/android/forwarder2/command.h" +#include "tools/android/forwarder2/forwarder.h" +#include "tools/android/forwarder2/socket.h" + +namespace forwarder2 { + +// static +scoped_ptr<HostController> HostController::Create( + int device_port, + int host_port, + int adb_port, + int exit_notifier_fd, + const ErrorCallback& error_callback) { + scoped_ptr<HostController> host_controller; + scoped_ptr<PipeNotifier> delete_controller_notifier(new PipeNotifier()); + scoped_ptr<Socket> adb_control_socket(new Socket()); + adb_control_socket->AddEventFd(exit_notifier_fd); + adb_control_socket->AddEventFd(delete_controller_notifier->receiver_fd()); + if (!adb_control_socket->ConnectTcp(std::string(), adb_port)) { + LOG(ERROR) << "Could not connect HostController socket on port: " + << adb_port; + return host_controller.Pass(); + } + // Send the command to the device start listening to the "device_forward_port" + bool send_command_success = SendCommand( + command::LISTEN, device_port, adb_control_socket.get()); + CHECK(send_command_success); + int device_port_allocated; + command::Type command; + if (!ReadCommand( + adb_control_socket.get(), &device_port_allocated, &command) || + command != command::BIND_SUCCESS) { + LOG(ERROR) << "Device binding error using port " << device_port; + return host_controller.Pass(); + } + host_controller.reset( + new HostController( + device_port_allocated, host_port, adb_port, exit_notifier_fd, + error_callback, adb_control_socket.Pass(), + delete_controller_notifier.Pass())); + return host_controller.Pass(); +} + +HostController::~HostController() { + DCHECK(deletion_task_runner_->RunsTasksOnCurrentThread()); + delete_controller_notifier_->Notify(); +} + +void HostController::Start() { + thread_.Start(); + ReadNextCommandSoon(); +} + +HostController::HostController( + int device_port, + int host_port, + int adb_port, + int exit_notifier_fd, + const ErrorCallback& error_callback, + scoped_ptr<Socket> adb_control_socket, + scoped_ptr<PipeNotifier> delete_controller_notifier) + : self_deleter_helper_(this, error_callback), + device_port_(device_port), + host_port_(host_port), + adb_port_(adb_port), + global_exit_notifier_fd_(exit_notifier_fd), + adb_control_socket_(adb_control_socket.Pass()), + delete_controller_notifier_(delete_controller_notifier.Pass()), + deletion_task_runner_(base::MessageLoopProxy::current()), + thread_("HostControllerThread") { +} + +void HostController::ReadNextCommandSoon() { + thread_.message_loop_proxy()->PostTask( + FROM_HERE, + base::Bind(&HostController::ReadCommandOnInternalThread, + base::Unretained(this))); +} + +void HostController::ReadCommandOnInternalThread() { + if (!ReceivedCommand(command::ACCEPT_SUCCESS, adb_control_socket_.get())) { + LOG(ERROR) << "Did not receive ACCEPT_SUCCESS for port: " + << host_port_; + OnInternalThreadError(); + return; + } + // Try to connect to host server. + scoped_ptr<Socket> host_server_data_socket(new Socket()); + if (!host_server_data_socket->ConnectTcp(std::string(), host_port_)) { + LOG(ERROR) << "Could not Connect HostServerData socket on port: " + << host_port_; + SendCommand( + command::HOST_SERVER_ERROR, device_port_, adb_control_socket_.get()); + if (ReceivedCommand(command::ACK, adb_control_socket_.get())) { + // It can continue if the host forwarder could not connect to the host + // server but the device acknowledged that, so that the device could + // re-try later. + ReadNextCommandSoon(); + return; + } + OnInternalThreadError(); + return; + } + LOG(INFO) << "Will send HOST_SERVER_SUCCESS: " << host_port_; + SendCommand( + command::HOST_SERVER_SUCCESS, device_port_, adb_control_socket_.get()); + StartForwarder(host_server_data_socket.Pass()); + ReadNextCommandSoon(); +} + +void HostController::StartForwarder( + scoped_ptr<Socket> host_server_data_socket) { + scoped_ptr<Socket> adb_data_socket(new Socket()); + if (!adb_data_socket->ConnectTcp("", adb_port_)) { + LOG(ERROR) << "Could not connect AdbDataSocket on port: " << adb_port_; + OnInternalThreadError(); + return; + } + // Open the Adb data connection, and send a command with the + // |device_forward_port| as a way for the device to identify the connection. + SendCommand(command::DATA_CONNECTION, device_port_, adb_data_socket.get()); + + // Check that the device received the new Adb Data Connection. Note that this + // check is done through the |adb_control_socket_| that is handled in the + // DeviceListener thread just after the call to WaitForAdbDataSocket(). + if (!ReceivedCommand(command::ADB_DATA_SOCKET_SUCCESS, + adb_control_socket_.get())) { + LOG(ERROR) << "Device could not handle the new Adb Data Connection."; + OnInternalThreadError(); + return; + } + forwarders_manager_.CreateAndStartNewForwarder( + host_server_data_socket.Pass(), adb_data_socket.Pass()); +} + +void HostController::OnInternalThreadError() { + UnmapPortOnDevice(); + self_deleter_helper_.MaybeSelfDeleteSoon(); +} + +void HostController::UnmapPortOnDevice() { + Socket socket; + if (!socket.ConnectTcp("", adb_port_)) { + LOG(ERROR) << "Could not connect to device on port " << adb_port_; + return; + } + if (!SendCommand(command::UNLISTEN, device_port_, &socket)) { + LOG(ERROR) << "Could not send unmap command for port " << device_port_; + return; + } + if (!ReceivedCommand(command::UNLISTEN_SUCCESS, &socket)) { + LOG(ERROR) << "Unamp command failed for port " << device_port_; + return; + } +} + +} // namespace forwarder2
diff --git a/tools/android/forwarder2/host_controller.h b/tools/android/forwarder2/host_controller.h new file mode 100644 index 0000000..d228bcc --- /dev/null +++ b/tools/android/forwarder2/host_controller.h
@@ -0,0 +1,96 @@ +// 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. + +#ifndef TOOLS_ANDROID_FORWARDER2_HOST_CONTROLLER_H_ +#define TOOLS_ANDROID_FORWARDER2_HOST_CONTROLLER_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/callback.h" +#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/threading/thread.h" +#include "tools/android/forwarder2/forwarders_manager.h" +#include "tools/android/forwarder2/pipe_notifier.h" +#include "tools/android/forwarder2/self_deleter_helper.h" +#include "tools/android/forwarder2/socket.h" + +namespace forwarder2 { + +// This class partners with DeviceController and has the same lifetime and +// threading characteristics as DeviceListener. In a nutshell, this class +// operates on its own thread and is destroyed on the thread it was constructed +// on. The class' deletion can happen in two different ways: +// - Its destructor was called by its owner (HostControllersManager). +// - Its internal thread requested self-deletion after an error happened. In +// this case the owner (HostControllersManager) is notified on the +// construction thread through the provided ErrorCallback invoked with the +// HostController instance. When this callback is invoked, it's up to the +// owner to delete the instance. +class HostController { + public: + // Callback used for self-deletion when an error happens so that the client + // can perform some cleanup work before deleting the HostController instance. + typedef base::Callback<void (scoped_ptr<HostController>)> ErrorCallback; + + // If |device_port| is zero then a dynamic port is allocated (and retrievable + // through device_port() below). + static scoped_ptr<HostController> Create(int device_port, + int host_port, + int adb_port, + int exit_notifier_fd, + const ErrorCallback& error_callback); + + ~HostController(); + + // Starts the internal controller thread. + void Start(); + + int adb_port() const { return adb_port_; } + + int device_port() const { return device_port_; } + + private: + HostController(int device_port, + int host_port, + int adb_port, + int exit_notifier_fd, + const ErrorCallback& error_callback, + scoped_ptr<Socket> adb_control_socket, + scoped_ptr<PipeNotifier> delete_controller_notifier); + + void ReadNextCommandSoon(); + void ReadCommandOnInternalThread(); + + void StartForwarder(scoped_ptr<Socket> host_server_data_socket); + + // Note that this gets also called when ~HostController() is invoked. + void OnInternalThreadError(); + + void UnmapPortOnDevice(); + + SelfDeleterHelper<HostController> self_deleter_helper_; + const int device_port_; + const int host_port_; + const int adb_port_; + // Used to notify the controller when the process is killed. + const int global_exit_notifier_fd_; + scoped_ptr<Socket> adb_control_socket_; + // Used to cancel the pending blocking IO operations when the host controller + // instance is deleted. + scoped_ptr<PipeNotifier> delete_controller_notifier_; + // Task runner used for deletion set at deletion time (i.e. the object is + // deleted on the same thread it is created on). + const scoped_refptr<base::SingleThreadTaskRunner> deletion_task_runner_; + base::Thread thread_; + ForwardersManager forwarders_manager_; + + DISALLOW_COPY_AND_ASSIGN(HostController); +}; + +} // namespace forwarder2 + +#endif // TOOLS_ANDROID_FORWARDER2_HOST_CONTROLLER_H_
diff --git a/tools/android/forwarder2/host_forwarder_main.cc b/tools/android/forwarder2/host_forwarder_main.cc new file mode 100644 index 0000000..59571b6 --- /dev/null +++ b/tools/android/forwarder2/host_forwarder_main.cc
@@ -0,0 +1,460 @@ +// 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 <errno.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#include <cstdio> +#include <iostream> +#include <limits> +#include <string> +#include <utility> +#include <vector> + +#include "base/at_exit.h" +#include "base/basictypes.h" +#include "base/bind.h" +#include "base/command_line.h" +#include "base/compiler_specific.h" +#include "base/containers/hash_tables.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/logging.h" +#include "base/memory/linked_ptr.h" +#include "base/memory/scoped_vector.h" +#include "base/memory/weak_ptr.h" +#include "base/pickle.h" +#include "base/safe_strerror_posix.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_piece.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "base/task_runner.h" +#include "base/threading/thread.h" +#include "tools/android/forwarder2/common.h" +#include "tools/android/forwarder2/daemon.h" +#include "tools/android/forwarder2/host_controller.h" +#include "tools/android/forwarder2/pipe_notifier.h" +#include "tools/android/forwarder2/socket.h" +#include "tools/android/forwarder2/util.h" + +namespace forwarder2 { +namespace { + +const char kLogFilePath[] = "/tmp/host_forwarder_log"; +const char kDaemonIdentifier[] = "chrome_host_forwarder_daemon"; + +const char kKillServerCommand[] = "kill-server"; +const char kForwardCommand[] = "forward"; + +const int kBufSize = 256; + +// Needs to be global to be able to be accessed from the signal handler. +PipeNotifier* g_notifier = NULL; + +// 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); +} + +// Manages HostController instances. There is one HostController instance for +// each connection being forwarded. Note that forwarding can happen with many +// devices (identified with a serial id). +class HostControllersManager { + public: + HostControllersManager() + : weak_ptr_factory_(this), + controllers_(new HostControllerMap()), + has_failed_(false) { + } + + ~HostControllersManager() { + if (!thread_.get()) + return; + // Delete the controllers on the thread they were created on. + thread_->message_loop_proxy()->DeleteSoon( + FROM_HERE, controllers_.release()); + } + + void HandleRequest(const std::string& device_serial, + int device_port, + int host_port, + scoped_ptr<Socket> client_socket) { + // Lazy initialize so that the CLI process doesn't get this thread created. + InitOnce(); + thread_->message_loop_proxy()->PostTask( + FROM_HERE, + base::Bind( + &HostControllersManager::HandleRequestOnInternalThread, + base::Unretained(this), device_serial, device_port, host_port, + base::Passed(&client_socket))); + } + + bool has_failed() const { return has_failed_; } + + private: + typedef base::hash_map< + std::string, linked_ptr<HostController> > HostControllerMap; + + static std::string MakeHostControllerMapKey(int adb_port, int device_port) { + return base::StringPrintf("%d:%d", adb_port, device_port); + } + + void InitOnce() { + if (thread_.get()) + return; + at_exit_manager_.reset(new base::AtExitManager()); + thread_.reset(new base::Thread("HostControllersManagerThread")); + thread_->Start(); + } + + // Invoked when a HostController instance reports an error (e.g. due to a + // device connectivity issue). Note that this could be called after the + // controller manager was destroyed which is why a weak pointer is used. + static void DeleteHostController( + const base::WeakPtr<HostControllersManager>& manager_ptr, + scoped_ptr<HostController> host_controller) { + HostController* const controller = host_controller.release(); + HostControllersManager* const manager = manager_ptr.get(); + if (!manager) { + // Note that |controller| is not leaked in this case since the host + // controllers manager owns the controllers. If the manager was deleted + // then all the controllers (including |controller|) were also deleted. + return; + } + DCHECK(manager->thread_->message_loop_proxy()->RunsTasksOnCurrentThread()); + // Note that this will delete |controller| which is owned by the map. + DeleteRefCountedValueInMap( + MakeHostControllerMapKey( + controller->adb_port(), controller->device_port()), + manager->controllers_.get()); + } + + void HandleRequestOnInternalThread(const std::string& device_serial, + int device_port, + int host_port, + scoped_ptr<Socket> client_socket) { + const int adb_port = GetAdbPortForDevice(device_serial); + if (adb_port < 0) { + SendMessage( + "ERROR: could not get adb port for device. You might need to add " + "'adb' to your PATH or provide the device serial id.", + client_socket.get()); + return; + } + if (device_port < 0) { + // Remove the previously created host controller. + const std::string controller_key = MakeHostControllerMapKey( + adb_port, -device_port); + const bool controller_did_exist = DeleteRefCountedValueInMap( + controller_key, controllers_.get()); + SendMessage( + !controller_did_exist ? "ERROR: could not unmap port" : "OK", + client_socket.get()); + + RemoveAdbPortForDeviceIfNeeded(device_serial); + return; + } + if (host_port < 0) { + SendMessage("ERROR: missing host port", client_socket.get()); + return; + } + const bool use_dynamic_port_allocation = device_port == 0; + if (!use_dynamic_port_allocation) { + const std::string controller_key = MakeHostControllerMapKey( + adb_port, device_port); + if (controllers_->find(controller_key) != controllers_->end()) { + LOG(INFO) << "Already forwarding device port " << device_port + << " to host port " << host_port; + SendMessage(base::StringPrintf("%d:%d", device_port, host_port), + client_socket.get()); + return; + } + } + // Create a new host controller. + scoped_ptr<HostController> host_controller( + HostController::Create( + device_port, host_port, adb_port, GetExitNotifierFD(), + base::Bind(&HostControllersManager::DeleteHostController, + weak_ptr_factory_.GetWeakPtr()))); + if (!host_controller.get()) { + has_failed_ = true; + SendMessage("ERROR: Connection to device failed.", client_socket.get()); + return; + } + // Get the current allocated port. + device_port = host_controller->device_port(); + LOG(INFO) << "Forwarding device port " << device_port << " to host port " + << host_port; + const std::string msg = base::StringPrintf("%d:%d", device_port, host_port); + if (!SendMessage(msg, client_socket.get())) + return; + host_controller->Start(); + controllers_->insert( + std::make_pair(MakeHostControllerMapKey(adb_port, device_port), + linked_ptr<HostController>(host_controller.release()))); + } + + void RemoveAdbPortForDeviceIfNeeded(const std::string& device_serial) { + base::hash_map<std::string, int>::const_iterator it = + device_serial_to_adb_port_map_.find(device_serial); + if (it == device_serial_to_adb_port_map_.end()) + return; + + int port = it->second; + const std::string prefix = base::StringPrintf("%d:", port); + for (HostControllerMap::const_iterator others = controllers_->begin(); + others != controllers_->end(); ++others) { + if (others->first.find(prefix) == 0U) + return; + } + // No other port is being forwarded to this device: + // - Remove it from our internal serial -> adb port map. + // - Remove from "adb forward" command. + LOG(INFO) << "Device " << device_serial << " has no more ports."; + device_serial_to_adb_port_map_.erase(device_serial); + const std::string serial_part = device_serial.empty() ? + std::string() : std::string("-s ") + device_serial; + const std::string command = base::StringPrintf( + "adb %s forward --remove tcp:%d", + serial_part.c_str(), + port); + const int ret = system(command.c_str()); + LOG(INFO) << command << " ret: " << ret; + // Wait for the socket to be fully unmapped. + const std::string port_mapped_cmd = base::StringPrintf( + "lsof -nPi:%d", + port); + const int poll_interval_us = 500 * 1000; + int retries = 3; + while (retries) { + const int port_unmapped = system(port_mapped_cmd.c_str()); + LOG(INFO) << "Device " << device_serial << " port " << port << " unmap " + << port_unmapped; + if (port_unmapped) + break; + --retries; + usleep(poll_interval_us); + } + } + + int GetAdbPortForDevice(const std::string& device_serial) { + base::hash_map<std::string, int>::const_iterator it = + device_serial_to_adb_port_map_.find(device_serial); + if (it != device_serial_to_adb_port_map_.end()) + return it->second; + Socket bind_socket; + CHECK(bind_socket.BindTcp("127.0.0.1", 0)); + const int port = bind_socket.GetPort(); + bind_socket.Close(); + const std::string serial_part = device_serial.empty() ? + std::string() : std::string("-s ") + device_serial; + const std::string command = base::StringPrintf( + "adb %s forward tcp:%d localabstract:chrome_device_forwarder", + serial_part.c_str(), + port); + LOG(INFO) << command; + const int ret = system(command.c_str()); + if (ret < 0 || !WIFEXITED(ret) || WEXITSTATUS(ret) != 0) + return -1; + device_serial_to_adb_port_map_[device_serial] = port; + return port; + } + + bool SendMessage(const std::string& msg, Socket* client_socket) { + bool result = client_socket->WriteString(msg); + DCHECK(result); + if (!result) + has_failed_ = true; + return result; + } + + base::WeakPtrFactory<HostControllersManager> weak_ptr_factory_; + base::hash_map<std::string, int> device_serial_to_adb_port_map_; + scoped_ptr<HostControllerMap> controllers_; + bool has_failed_; + scoped_ptr<base::AtExitManager> at_exit_manager_; // Needed by base::Thread. + scoped_ptr<base::Thread> thread_; +}; + +class ServerDelegate : public Daemon::ServerDelegate { + public: + ServerDelegate() : has_failed_(false) {} + + bool has_failed() const { + return has_failed_ || controllers_manager_.has_failed(); + } + + // Daemon::ServerDelegate: + virtual void Init() OVERRIDE { + LOG(INFO) << "Starting host process daemon (pid=" << getpid() << ")"; + DCHECK(!g_notifier); + g_notifier = new PipeNotifier(); + signal(SIGTERM, KillHandler); + signal(SIGINT, KillHandler); + } + + 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->DidReceiveEvent()) + return; + PError("Read()"); + has_failed_ = true; + return; + } + const Pickle command_pickle(buf, bytes_read); + PickleIterator pickle_it(command_pickle); + std::string device_serial; + CHECK(pickle_it.ReadString(&device_serial)); + int device_port; + if (!pickle_it.ReadInt(&device_port)) { + client_socket->WriteString("ERROR: missing device port"); + return; + } + int host_port; + if (!pickle_it.ReadInt(&host_port)) + host_port = -1; + controllers_manager_.HandleRequest( + device_serial, device_port, host_port, client_socket.Pass()); + } + + private: + bool has_failed_; + HostControllersManager controllers_manager_; + + DISALLOW_COPY_AND_ASSIGN(ServerDelegate); +}; + +class ClientDelegate : public Daemon::ClientDelegate { + public: + ClientDelegate(const Pickle& command_pickle) + : command_pickle_(command_pickle), + has_failed_(false) { + } + + bool has_failed() const { return has_failed_; } + + // Daemon::ClientDelegate: + virtual void OnDaemonReady(Socket* daemon_socket) OVERRIDE { + // Send the forward command to the daemon. + CHECK_EQ(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(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; + } + printf("%s\n", buf); + } + + private: + const 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" + " --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>::max()) { + LOG(ERROR) << "Could not convert string " << s << " to port"; + ExitWithUsage(); + } + return value; +} + +int RunHostForwarder(int argc, char** argv) { + CommandLine::Init(argc, argv); + const CommandLine& cmd_line = *CommandLine::ForCurrentProcess(); + bool kill_server = false; + + 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(); + // Note the minus sign below. + pickle.WriteInt(-PortToInt(args[0])); + } else if (cmd_line.HasSwitch("map")) { + if (args.size() != 2) + ExitWithUsage(); + pickle.WriteInt(PortToInt(args[0])); + pickle.WriteInt(PortToInt(args[1])); + } else { + ExitWithUsage(); + } + + if (kill_server && args.size() > 0) + ExitWithUsage(); + + ClientDelegate client_delegate(pickle); + ServerDelegate daemon_delegate; + 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); +}
diff --git a/tools/android/forwarder2/pipe_notifier.cc b/tools/android/forwarder2/pipe_notifier.cc new file mode 100644 index 0000000..02842bd --- /dev/null +++ b/tools/android/forwarder2/pipe_notifier.cc
@@ -0,0 +1,54 @@ +// 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 "tools/android/forwarder2/pipe_notifier.h" + +#include <fcntl.h> +#include <unistd.h> +#include <sys/socket.h> +#include <sys/types.h> + +#include "base/logging.h" +#include "base/posix/eintr_wrapper.h" +#include "base/safe_strerror_posix.h" + +namespace forwarder2 { + +PipeNotifier::PipeNotifier() { + int pipe_fd[2]; + int ret = pipe(pipe_fd); + CHECK_EQ(0, ret); + receiver_fd_ = pipe_fd[0]; + sender_fd_ = pipe_fd[1]; + fcntl(sender_fd_, F_SETFL, O_NONBLOCK); +} + +PipeNotifier::~PipeNotifier() { + close(receiver_fd_); + close(sender_fd_); +} + +bool PipeNotifier::Notify() { + CHECK_NE(-1, sender_fd_); + errno = 0; + int ret = HANDLE_EINTR(write(sender_fd_, "1", 1)); + if (ret < 0) { + PLOG(ERROR) << "write"; + return false; + } + return true; +} + +void PipeNotifier::Reset() { + char c; + int ret = HANDLE_EINTR(read(receiver_fd_, &c, 1)); + if (ret < 0) { + PLOG(ERROR) << "read"; + return; + } + DCHECK_EQ(1, ret); + DCHECK_EQ('1', c); +} + +} // namespace forwarder
diff --git a/tools/android/forwarder2/pipe_notifier.h b/tools/android/forwarder2/pipe_notifier.h new file mode 100644 index 0000000..aadb269 --- /dev/null +++ b/tools/android/forwarder2/pipe_notifier.h
@@ -0,0 +1,37 @@ +// 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. + +#ifndef TOOLS_ANDROID_FORWARDER2_PIPE_NOTIFIER_H_ +#define TOOLS_ANDROID_FORWARDER2_PIPE_NOTIFIER_H_ + +#include "base/basictypes.h" + +namespace forwarder2 { + +// Helper class used to create a unix pipe that sends notifications to the +// |receiver_fd_| file descriptor when called |Notify()|. This should be used +// by the main thread to notify other threads that it must exit. +// The |receiver_fd_| can be put into a fd_set and used in a select together +// with a socket waiting to accept or read. +class PipeNotifier { + public: + PipeNotifier(); + ~PipeNotifier(); + + bool Notify(); + + int receiver_fd() const { return receiver_fd_; } + + void Reset(); + + private: + int sender_fd_; + int receiver_fd_; + + DISALLOW_COPY_AND_ASSIGN(PipeNotifier); +}; + +} // namespace forwarder + +#endif // TOOLS_ANDROID_FORWARDER2_PIPE_NOTIFIER_H_
diff --git a/tools/android/forwarder2/self_deleter_helper.h b/tools/android/forwarder2/self_deleter_helper.h new file mode 100644 index 0000000..d96903d --- /dev/null +++ b/tools/android/forwarder2/self_deleter_helper.h
@@ -0,0 +1,141 @@ +// Copyright 2013 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. + +#ifndef TOOLS_ANDROID_FORWARDER2_SELF_DELETER_HELPER_H_ +#define TOOLS_ANDROID_FORWARDER2_SELF_DELETER_HELPER_H_ + +#include "base/basictypes.h" +#include "base/bind.h" +#include "base/callback.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/message_loop/message_loop_proxy.h" + +namespace base { + +class SingleThreadTaskRunner; + +} // namespace base + +namespace forwarder2 { + +// Helper template class to be used in the following case: +// * T is the type of an object that implements some work through an internal +// or worker thread. +// * T wants the internal thread to invoke deletion of its own instance, on +// the thread where the instance was created. +// +// To make this easier, do something like: +// 1) Add a SelfDeleteHelper<T> member to your class T, and default-initialize +// it in its constructor. +// 2) In the internal thread, to trigger self-deletion, call the +// MaybeDeleteSoon() method on this member. +// +// MaybeDeleteSoon() posts a task on the message loop where the T instance was +// created to delete it. The task will be safely ignored if the instance is +// otherwise deleted. +// +// Usage example: +// class Object { +// public: +// typedef base::Callback<void (scoped_ptr<Object>)> ErrorCallback; +// +// Object(const ErrorCallback& error_callback) +// : self_deleter_helper_(this, error_callback) { +// } +// +// void StartWork() { +// // Post a callback to DoSomethingOnWorkerThread() below to another +// // thread. +// } +// +// void DoSomethingOnWorkerThread() { +// ... +// if (error_happened) +// self_deleter_helper_.MaybeDeleteSoon(); +// } +// +// private: +// SelfDeleterHelper<MySelfDeletingClass> self_deleter_helper_; +// }; +// +// class ObjectOwner { +// public: +// ObjectOwner() +// : object_(new Object(base::Bind(&ObjectOwner::DeleteObjectOnError, +// base::Unretained(this))) { +// // To keep this example simple base::Unretained(this) is used above but +// // note that in a real world scenario the client would have to make sure +// // that the ObjectOwner instance is still alive when +// // DeleteObjectOnError() gets called below. This can be achieved by +// // using a WeakPtr<ObjectOwner> for instance. +// } +// +// void StartWork() { +// object_->StartWork(); +// } +// +// private: +// void DeleteObjectOnError(scoped_ptr<Object> object) { +// DCHECK(thread_checker_.CalledOnValidThread()); +// DCHECK_EQ(object_, object); +// // Do some extra work with |object| before it gets deleted... +// object_.reset(); +// ignore_result(object.release()); +// } +// +// base::ThreadChecker thread_checker_; +// scoped_ptr<Object> object_; +// }; +// +template <typename T> +class SelfDeleterHelper { + public: + typedef base::Callback<void (scoped_ptr<T>)> DeletionCallback; + + SelfDeleterHelper(T* self_deleting_object, + const DeletionCallback& deletion_callback) + : construction_runner_(base::MessageLoopProxy::current()), + self_deleting_object_(self_deleting_object), + deletion_callback_(deletion_callback), + weak_ptr_factory_(this) { + } + + ~SelfDeleterHelper() { + DCHECK(construction_runner_->RunsTasksOnCurrentThread()); + } + + void MaybeSelfDeleteSoon() { + DCHECK(!construction_runner_->RunsTasksOnCurrentThread()); + construction_runner_->PostTask( + FROM_HERE, + base::Bind(&SelfDeleterHelper::SelfDelete, + weak_ptr_factory_.GetWeakPtr())); + } + + private: + void SelfDelete() { + DCHECK(construction_runner_->RunsTasksOnCurrentThread()); + deletion_callback_.Run(make_scoped_ptr(self_deleting_object_)); + } + + const scoped_refptr<base::SingleThreadTaskRunner> construction_runner_; + T* const self_deleting_object_; + const DeletionCallback deletion_callback_; + + //WeakPtrFactory's documentation says: + // Member variables should appear before the WeakPtrFactory, to ensure + // that any WeakPtrs to Controller are invalidated before its members + // variable's destructors are executed, rendering them invalid. + base::WeakPtrFactory<SelfDeleterHelper<T> > weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(SelfDeleterHelper); +}; + +} // namespace forwarder2 + +#endif // TOOLS_ANDROID_FORWARDER2_SELF_DELETER_HELPER_H_
diff --git a/tools/android/forwarder2/socket.cc b/tools/android/forwarder2/socket.cc new file mode 100644 index 0000000..9feac84 --- /dev/null +++ b/tools/android/forwarder2/socket.cc
@@ -0,0 +1,448 @@ +// 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 "tools/android/forwarder2/socket.h" + +#include <arpa/inet.h> +#include <fcntl.h> +#include <netdb.h> +#include <netinet/in.h> +#include <stdio.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <unistd.h> + +#include "base/logging.h" +#include "base/posix/eintr_wrapper.h" +#include "base/safe_strerror_posix.h" +#include "tools/android/common/net.h" +#include "tools/android/forwarder2/common.h" + +namespace { +const int kNoTimeout = -1; +const int kConnectTimeOut = 10; // Seconds. + +bool FamilyIsTCP(int family) { + return family == AF_INET || family == AF_INET6; +} +} // namespace + +namespace forwarder2 { + +bool Socket::BindUnix(const std::string& path) { + errno = 0; + if (!InitUnixSocket(path) || !BindAndListen()) { + Close(); + return false; + } + return true; +} + +bool Socket::BindTcp(const std::string& host, int port) { + errno = 0; + if (!InitTcpSocket(host, port) || !BindAndListen()) { + Close(); + return false; + } + return true; +} + +bool Socket::ConnectUnix(const std::string& path) { + errno = 0; + if (!InitUnixSocket(path) || !Connect()) { + Close(); + return false; + } + return true; +} + +bool Socket::ConnectTcp(const std::string& host, int port) { + errno = 0; + if (!InitTcpSocket(host, port) || !Connect()) { + Close(); + return false; + } + return true; +} + +Socket::Socket() + : socket_(-1), + port_(0), + socket_error_(false), + family_(AF_INET), + addr_ptr_(reinterpret_cast<sockaddr*>(&addr_.addr4)), + addr_len_(sizeof(sockaddr)) { + memset(&addr_, 0, sizeof(addr_)); +} + +Socket::~Socket() { + Close(); +} + +void Socket::Shutdown() { + if (!IsClosed()) { + PRESERVE_ERRNO_HANDLE_EINTR(shutdown(socket_, SHUT_RDWR)); + } +} + +void Socket::Close() { + if (!IsClosed()) { + CloseFD(socket_); + socket_ = -1; + } +} + +bool Socket::InitSocketInternal() { + socket_ = socket(family_, SOCK_STREAM, 0); + if (socket_ < 0) { + PLOG(ERROR) << "socket"; + return false; + } + tools::DisableNagle(socket_); + int reuse_addr = 1; + setsockopt(socket_, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, + sizeof(reuse_addr)); + if (!SetNonBlocking()) + return false; + return true; +} + +bool Socket::SetNonBlocking() { + const int flags = fcntl(socket_, F_GETFL); + if (flags < 0) { + PLOG(ERROR) << "fcntl"; + return false; + } + if (flags & O_NONBLOCK) + return true; + if (fcntl(socket_, F_SETFL, flags | O_NONBLOCK) < 0) { + PLOG(ERROR) << "fcntl"; + return false; + } + return true; +} + +bool Socket::InitUnixSocket(const std::string& path) { + static const size_t kPathMax = sizeof(addr_.addr_un.sun_path); + // For abstract sockets we need one extra byte for the leading zero. + if (path.size() + 2 /* '\0' */ > kPathMax) { + LOG(ERROR) << "The provided path is too big to create a unix " + << "domain socket: " << path; + return false; + } + family_ = PF_UNIX; + addr_.addr_un.sun_family = family_; + // Copied from net/socket/unix_domain_socket_posix.cc + // Convert the path given into abstract socket name. It must start with + // the '\0' character, so we are adding it. |addr_len| must specify the + // length of the structure exactly, as potentially the socket name may + // have '\0' characters embedded (although we don't support this). + // Note that addr_.addr_un.sun_path is already zero initialized. + memcpy(addr_.addr_un.sun_path + 1, path.c_str(), path.size()); + addr_len_ = path.size() + offsetof(struct sockaddr_un, sun_path) + 1; + addr_ptr_ = reinterpret_cast<sockaddr*>(&addr_.addr_un); + return InitSocketInternal(); +} + +bool Socket::InitTcpSocket(const std::string& host, int port) { + port_ = port; + if (host.empty()) { + // Use localhost: INADDR_LOOPBACK + family_ = AF_INET; + addr_.addr4.sin_family = family_; + addr_.addr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + } else if (!Resolve(host)) { + return false; + } + CHECK(FamilyIsTCP(family_)) << "Invalid socket family."; + if (family_ == AF_INET) { + addr_.addr4.sin_port = htons(port_); + addr_ptr_ = reinterpret_cast<sockaddr*>(&addr_.addr4); + addr_len_ = sizeof(addr_.addr4); + } else if (family_ == AF_INET6) { + addr_.addr6.sin6_port = htons(port_); + addr_ptr_ = reinterpret_cast<sockaddr*>(&addr_.addr6); + addr_len_ = sizeof(addr_.addr6); + } + return InitSocketInternal(); +} + +bool Socket::BindAndListen() { + errno = 0; + if (HANDLE_EINTR(bind(socket_, addr_ptr_, addr_len_)) < 0 || + HANDLE_EINTR(listen(socket_, SOMAXCONN)) < 0) { + PLOG(ERROR) << "bind/listen"; + SetSocketError(); + return false; + } + if (port_ == 0 && FamilyIsTCP(family_)) { + SockAddr addr; + memset(&addr, 0, sizeof(addr)); + socklen_t addrlen = 0; + sockaddr* addr_ptr = NULL; + uint16* port_ptr = NULL; + if (family_ == AF_INET) { + addr_ptr = reinterpret_cast<sockaddr*>(&addr.addr4); + port_ptr = &addr.addr4.sin_port; + addrlen = sizeof(addr.addr4); + } else if (family_ == AF_INET6) { + addr_ptr = reinterpret_cast<sockaddr*>(&addr.addr6); + port_ptr = &addr.addr6.sin6_port; + addrlen = sizeof(addr.addr6); + } + errno = 0; + if (getsockname(socket_, addr_ptr, &addrlen) != 0) { + PLOG(ERROR) << "getsockname"; + SetSocketError(); + return false; + } + port_ = ntohs(*port_ptr); + } + return true; +} + +bool Socket::Accept(Socket* new_socket) { + DCHECK(new_socket != NULL); + if (!WaitForEvent(READ, kNoTimeout)) { + SetSocketError(); + return false; + } + errno = 0; + int new_socket_fd = HANDLE_EINTR(accept(socket_, NULL, NULL)); + if (new_socket_fd < 0) { + SetSocketError(); + return false; + } + tools::DisableNagle(new_socket_fd); + new_socket->socket_ = new_socket_fd; + if (!new_socket->SetNonBlocking()) + return false; + return true; +} + +bool Socket::Connect() { + DCHECK(fcntl(socket_, F_GETFL) & O_NONBLOCK); + errno = 0; + if (HANDLE_EINTR(connect(socket_, addr_ptr_, addr_len_)) < 0 && + errno != EINPROGRESS) { + SetSocketError(); + return false; + } + // Wait for connection to complete, or receive a notification. + if (!WaitForEvent(WRITE, kConnectTimeOut)) { + SetSocketError(); + return false; + } + int socket_errno; + socklen_t opt_len = sizeof(socket_errno); + if (getsockopt(socket_, SOL_SOCKET, SO_ERROR, &socket_errno, &opt_len) < 0) { + PLOG(ERROR) << "getsockopt()"; + SetSocketError(); + return false; + } + if (socket_errno != 0) { + LOG(ERROR) << "Could not connect to host: " << safe_strerror(socket_errno); + SetSocketError(); + return false; + } + return true; +} + +bool Socket::Resolve(const std::string& host) { + struct addrinfo hints; + struct addrinfo* res; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags |= AI_CANONNAME; + + int errcode = getaddrinfo(host.c_str(), NULL, &hints, &res); + if (errcode != 0) { + errno = 0; + SetSocketError(); + freeaddrinfo(res); + return false; + } + family_ = res->ai_family; + switch (res->ai_family) { + case AF_INET: + memcpy(&addr_.addr4, + reinterpret_cast<sockaddr_in*>(res->ai_addr), + sizeof(sockaddr_in)); + break; + case AF_INET6: + memcpy(&addr_.addr6, + reinterpret_cast<sockaddr_in6*>(res->ai_addr), + sizeof(sockaddr_in6)); + break; + } + freeaddrinfo(res); + return true; +} + +int Socket::GetPort() { + if (!FamilyIsTCP(family_)) { + LOG(ERROR) << "Can't call GetPort() on an unix domain socket."; + return 0; + } + return port_; +} + +int Socket::ReadNumBytes(void* buffer, size_t num_bytes) { + int bytes_read = 0; + int ret = 1; + while (bytes_read < num_bytes && ret > 0) { + ret = Read(static_cast<char*>(buffer) + bytes_read, num_bytes - bytes_read); + if (ret >= 0) + bytes_read += ret; + } + return bytes_read; +} + +void Socket::SetSocketError() { + socket_error_ = true; + DCHECK_NE(EAGAIN, errno); + DCHECK_NE(EWOULDBLOCK, errno); + Close(); +} + +int Socket::Read(void* buffer, size_t buffer_size) { + if (!WaitForEvent(READ, kNoTimeout)) { + SetSocketError(); + return 0; + } + int ret = HANDLE_EINTR(read(socket_, buffer, buffer_size)); + if (ret < 0) { + PLOG(ERROR) << "read"; + SetSocketError(); + } + return ret; +} + +int Socket::NonBlockingRead(void* buffer, size_t buffer_size) { + DCHECK(fcntl(socket_, F_GETFL) & O_NONBLOCK); + int ret = HANDLE_EINTR(read(socket_, buffer, buffer_size)); + if (ret < 0) { + PLOG(ERROR) << "read"; + SetSocketError(); + } + return ret; +} + +int Socket::Write(const void* buffer, size_t count) { + if (!WaitForEvent(WRITE, kNoTimeout)) { + SetSocketError(); + return 0; + } + int ret = HANDLE_EINTR(send(socket_, buffer, count, MSG_NOSIGNAL)); + if (ret < 0) { + PLOG(ERROR) << "send"; + SetSocketError(); + } + return ret; +} + +int Socket::NonBlockingWrite(const void* buffer, size_t count) { + DCHECK(fcntl(socket_, F_GETFL) & O_NONBLOCK); + int ret = HANDLE_EINTR(send(socket_, buffer, count, MSG_NOSIGNAL)); + if (ret < 0) { + PLOG(ERROR) << "send"; + SetSocketError(); + } + return ret; +} + +int Socket::WriteString(const std::string& buffer) { + return WriteNumBytes(buffer.c_str(), buffer.size()); +} + +void Socket::AddEventFd(int event_fd) { + Event event; + event.fd = event_fd; + event.was_fired = false; + events_.push_back(event); +} + +bool Socket::DidReceiveEventOnFd(int fd) const { + for (size_t i = 0; i < events_.size(); ++i) + if (events_[i].fd == fd) + return events_[i].was_fired; + return false; +} + +bool Socket::DidReceiveEvent() const { + for (size_t i = 0; i < events_.size(); ++i) + if (events_[i].was_fired) + return true; + return false; +} + +int Socket::WriteNumBytes(const void* buffer, size_t num_bytes) { + int bytes_written = 0; + int ret = 1; + while (bytes_written < num_bytes && ret > 0) { + ret = Write(static_cast<const char*>(buffer) + bytes_written, + num_bytes - bytes_written); + if (ret >= 0) + bytes_written += ret; + } + return bytes_written; +} + +bool Socket::WaitForEvent(EventType type, int timeout_secs) { + if (socket_ == -1) + return true; + DCHECK(fcntl(socket_, F_GETFL) & O_NONBLOCK); + fd_set read_fds; + fd_set write_fds; + FD_ZERO(&read_fds); + FD_ZERO(&write_fds); + if (type == READ) + FD_SET(socket_, &read_fds); + else + FD_SET(socket_, &write_fds); + for (size_t i = 0; i < events_.size(); ++i) + FD_SET(events_[i].fd, &read_fds); + timeval tv = {}; + timeval* tv_ptr = NULL; + if (timeout_secs > 0) { + tv.tv_sec = timeout_secs; + tv.tv_usec = 0; + tv_ptr = &tv; + } + int max_fd = socket_; + for (size_t i = 0; i < events_.size(); ++i) + if (events_[i].fd > max_fd) + max_fd = events_[i].fd; + if (HANDLE_EINTR( + select(max_fd + 1, &read_fds, &write_fds, NULL, tv_ptr)) <= 0) { + PLOG(ERROR) << "select"; + return false; + } + bool event_was_fired = false; + for (size_t i = 0; i < events_.size(); ++i) { + if (FD_ISSET(events_[i].fd, &read_fds)) { + events_[i].was_fired = true; + event_was_fired = true; + } + } + return !event_was_fired; +} + +// static +pid_t Socket::GetUnixDomainSocketProcessOwner(const std::string& path) { + Socket socket; + if (!socket.ConnectUnix(path)) + return -1; + ucred ucred; + socklen_t len = sizeof(ucred); + if (getsockopt(socket.socket_, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1) { + CHECK_NE(ENOPROTOOPT, errno); + return -1; + } + return ucred.pid; +} + +} // namespace forwarder2
diff --git a/tools/android/forwarder2/socket.h b/tools/android/forwarder2/socket.h new file mode 100644 index 0000000..6047a1c --- /dev/null +++ b/tools/android/forwarder2/socket.h
@@ -0,0 +1,152 @@ +// 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. + +#ifndef TOOLS_ANDROID_FORWARDER2_SOCKET_H_ +#define TOOLS_ANDROID_FORWARDER2_SOCKET_H_ + +#include <fcntl.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <sys/un.h> + +#include <string> +#include <vector> + +#include "base/basictypes.h" + +namespace forwarder2 { + +// Wrapper class around unix socket api. Can be used to create, bind or +// connect to both Unix domain sockets and TCP sockets. +// TODO(pliard): Split this class into TCPSocket and UnixDomainSocket. +class Socket { + public: + Socket(); + ~Socket(); + + bool BindUnix(const std::string& path); + bool BindTcp(const std::string& host, int port); + bool ConnectUnix(const std::string& path); + bool ConnectTcp(const std::string& host, int port); + + // Just a wrapper around unix socket shutdown(), see man 2 shutdown. + void Shutdown(); + + // Just a wrapper around unix socket close(), see man 2 close. + void Close(); + bool IsClosed() const { return socket_ < 0; } + + int fd() const { return socket_; } + + bool Accept(Socket* new_socket); + + // Returns the port allocated to this socket or zero on error. + int GetPort(); + + // Just a wrapper around unix read() function. + // Reads up to buffer_size, but may read less then buffer_size. + // Returns the number of bytes read. + int Read(void* buffer, size_t buffer_size); + + // Non-blocking version of Read() above. This must be called after a + // successful call to select(). The socket must also be in non-blocking mode + // before calling this method. + int NonBlockingRead(void* buffer, size_t buffer_size); + + // Wrapper around send(). + int Write(const void* buffer, size_t count); + + // Same as NonBlockingRead() but for writing. + int NonBlockingWrite(const void* buffer, size_t count); + + // Calls Read() multiple times until num_bytes is written to the provided + // buffer. No bounds checking is performed. + // Returns number of bytes read, which can be different from num_bytes in case + // of errror. + int ReadNumBytes(void* buffer, size_t num_bytes); + + // Calls Write() multiple times until num_bytes is written. No bounds checking + // is performed. Returns number of bytes written, which can be different from + // num_bytes in case of errror. + int WriteNumBytes(const void* buffer, size_t num_bytes); + + // Calls WriteNumBytes for the given std::string. Note that the null + // terminator is not written to the socket. + int WriteString(const std::string& buffer); + + bool has_error() const { return socket_error_; } + + // |event_fd| must be a valid pipe file descriptor created from the + // PipeNotifier and must live (not be closed) at least as long as this socket + // is alive. + void AddEventFd(int event_fd); + + // Returns whether Accept() or Connect() was interrupted because the socket + // received an external event fired through the provided fd. + bool DidReceiveEventOnFd(int fd) const; + + bool DidReceiveEvent() const; + + static pid_t GetUnixDomainSocketProcessOwner(const std::string& path); + + private: + enum EventType { + READ, + WRITE + }; + + union SockAddr { + // IPv4 sockaddr + sockaddr_in addr4; + // IPv6 sockaddr + sockaddr_in6 addr6; + // Unix Domain sockaddr + sockaddr_un addr_un; + }; + + struct Event { + int fd; + bool was_fired; + }; + + bool SetNonBlocking(); + + // If |host| is empty, use localhost. + bool InitTcpSocket(const std::string& host, int port); + bool InitUnixSocket(const std::string& path); + bool BindAndListen(); + bool Connect(); + + bool Resolve(const std::string& host); + bool InitSocketInternal(); + void SetSocketError(); + + // Waits until either the Socket or the |exit_notifier_fd_| has received an + // event. + bool WaitForEvent(EventType type, int timeout_secs); + + int socket_; + int port_; + bool socket_error_; + + // Family of the socket (PF_INET, PF_INET6 or PF_UNIX). + int family_; + + SockAddr addr_; + + // Points to one of the members of the above union depending on the family. + sockaddr* addr_ptr_; + // Length of one of the members of the above union depending on the family. + socklen_t addr_len_; + + // Used to listen for external events (e.g. process received a SIGTERM) while + // blocking on I/O operations. + std::vector<Event> events_; + + DISALLOW_COPY_AND_ASSIGN(Socket); +}; + +} // namespace forwarder + +#endif // TOOLS_ANDROID_FORWARDER2_SOCKET_H_
diff --git a/tools/android/forwarder2/util.h b/tools/android/forwarder2/util.h new file mode 100644 index 0000000..9947628 --- /dev/null +++ b/tools/android/forwarder2/util.h
@@ -0,0 +1,36 @@ +// Copyright 2013 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. + +#ifndef TOOLS_ANDROID_FORWARDER2_UTIL_H_ +#define TOOLS_ANDROID_FORWARDER2_UTIL_H_ + +#include "base/logging.h" + +namespace forwarder2 { + +// Safely deletes a ref-counted value in a provided map by unlinking the object +// from the map before deleting it in case its destructor would access the map. +// Deletion will only happen by definition if the object's refcount is set to 1 +// before this function gets called. Returns whether the element could be found +// in the map. +template <typename Map, typename K> +bool DeleteRefCountedValueInMap(const K& key, Map* map) { + const typename Map::iterator it = map->find(key); + if (it == map->end()) + return false; + DeleteRefCountedValueInMapFromIterator(it, map); + return true; +} + +// See DeleteRefCountedValuetInMap() above. +template <typename Map, typename Iterator> +void DeleteRefCountedValueInMapFromIterator(Iterator it, Map* map) { + DCHECK(it != map->end()); + const typename Map::value_type::second_type shared_ptr_copy = it->second; + map->erase(it); +} + +} // namespace forwarder2 + +#endif // TOOLS_ANDROID_FORWARDER2_UTIL_H_