summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorpliard@chromium.org <pliard@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-12 12:46:37 +0000
committerpliard@chromium.org <pliard@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-12 12:46:37 +0000
commit4c0871855cba62292ca55b2f7467d075247c725a (patch)
tree8b96fdf32f0c50b5e9e97e438f83921a9158dac4 /tools
parenta756e8791d0ef190b89a80cb71c0722270f0731d (diff)
downloadchromium_src-4c0871855cba62292ca55b2f7467d075247c725a.zip
chromium_src-4c0871855cba62292ca55b2f7467d075247c725a.tar.gz
chromium_src-4c0871855cba62292ca55b2f7467d075247c725a.tar.bz2
Support HTTP test-server based net unit tests on Android.
This changes forwarder2 to support both test-server spawner and HTTP server forwarding. The main issue was that the device_forwarder was killed when instantiating a second Forwarder (Python object). Test server based unit tests require two device-to-host redirections, one for the test server spawner and one for the HTTP server. The port used by the HTTP server is allocated dynamically which means that we can't know the full forwarding configuration before we spawn a HTTP server (through the test server spawner). This CL changes the forwarder to let it forward new ports while it is running by making host_forwarder a daemon. This is similar to how ADB works. This also means that a single host_forwarder process (daemon) can now handle multiple devices. BUG=146979 Review URL: https://chromiumcodereview.appspot.com/11269036 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@167167 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'tools')
-rw-r--r--tools/android/common/daemon.cc1
-rw-r--r--tools/android/forwarder2/command.cc12
-rw-r--r--tools/android/forwarder2/common.cc28
-rw-r--r--tools/android/forwarder2/common.h89
-rw-r--r--tools/android/forwarder2/daemon.cc256
-rw-r--r--tools/android/forwarder2/daemon.h38
-rw-r--r--tools/android/forwarder2/device_controller.cc11
-rw-r--r--tools/android/forwarder2/device_listener.cc7
-rw-r--r--tools/android/forwarder2/forwarder.gyp3
-rw-r--r--tools/android/forwarder2/host_controller.cc9
-rw-r--r--tools/android/forwarder2/host_forwarder_main.cc301
-rw-r--r--tools/android/forwarder2/socket.cc59
-rw-r--r--tools/android/forwarder2/socket.h26
13 files changed, 705 insertions, 135 deletions
diff --git a/tools/android/common/daemon.cc b/tools/android/common/daemon.cc
index 225c84e..48e9cd6 100644
--- a/tools/android/common/daemon.cc
+++ b/tools/android/common/daemon.cc
@@ -73,4 +73,3 @@ void SpawnDaemon(int exit_status) {
}
} // namespace tools
-
diff --git a/tools/android/forwarder2/command.cc b/tools/android/forwarder2/command.cc
index 9b9e401..c070ed9 100644
--- a/tools/android/forwarder2/command.cc
+++ b/tools/android/forwarder2/command.cc
@@ -10,6 +10,7 @@
#include <string.h>
#include "base/logging.h"
+#include "base/safe_strerror_posix.h"
#include "base/string_number_conversions.h"
#include "base/string_piece.h"
#include "tools/android/forwarder2/socket.h"
@@ -46,9 +47,14 @@ bool ReadCommand(Socket* socket,
// To make logging easier.
command_buffer[kCommandStringSize] = '\0';
- if (socket->ReadNumBytes(command_buffer, kCommandStringSize) !=
- kCommandStringSize) {
- LOG(ERROR) << "Not enough data received from the socket.";
+ 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;
}
diff --git a/tools/android/forwarder2/common.cc b/tools/android/forwarder2/common.cc
new file mode 100644
index 0000000..6accd30
--- /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/eintr_wrapper.h"
+#include "base/logging.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 (HANDLE_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..3f546c6
--- /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/eintr_wrapper.h"
+#include "base/logging.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..c056460
--- /dev/null
+++ b/tools/android/forwarder2/daemon.cc
@@ -0,0 +1,256 @@
+// 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 <stdio.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/eintr_wrapper.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/safe_strerror_posix.h"
+#include "base/string_number_conversions.h"
+#include "base/stringprintf.h"
+#include "tools/android/forwarder2/common.h"
+
+namespace forwarder2 {
+namespace {
+
+const char kLogFilePath[] = "/tmp/host_forwarder_log";
+
+class FileDescriptorAutoCloser {
+ public:
+ explicit FileDescriptorAutoCloser(int fd) : fd_(fd) {
+ DCHECK(fd_ >= 0);
+ }
+
+ ~FileDescriptorAutoCloser() {
+ if (fd_ > -1)
+ CloseFD(fd_);
+ }
+
+ int Release() {
+ const int fd = fd_;
+ fd_ = -1;
+ return fd;
+ }
+
+ private:
+ int fd_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileDescriptorAutoCloser);
+};
+
+// Handles creation and destruction of the PID file.
+class PIDFile {
+ public:
+ static scoped_ptr<PIDFile> Create(const std::string& path) {
+ scoped_ptr<PIDFile> pid_file;
+ const int pid_file_fd = HANDLE_EINTR(
+ open(path.c_str(), O_CREAT | O_WRONLY, 0600));
+ if (pid_file_fd < 0) {
+ PError("open()");
+ return pid_file.Pass();
+ }
+ FileDescriptorAutoCloser fd_closer(pid_file_fd);
+ struct flock lock_info = {};
+ lock_info.l_type = F_WRLCK;
+ lock_info.l_whence = SEEK_CUR;
+ if (HANDLE_EINTR(fcntl(pid_file_fd, F_SETLK, &lock_info)) < 0) {
+ if (errno == EAGAIN || errno == EACCES) {
+ LOG(ERROR) << "Daemon already running (PID file already locked)";
+ return pid_file.Pass();
+ }
+ PError("lockf()");
+ return pid_file.Pass();
+ }
+ const std::string pid_string = base::StringPrintf("%d\n", getpid());
+ CHECK(HANDLE_EINTR(write(pid_file_fd, pid_string.c_str(),
+ pid_string.length())));
+ pid_file.reset(new PIDFile(fd_closer.Release(), path));
+ return pid_file.Pass();
+ }
+
+ ~PIDFile() {
+ CloseFD(fd_); // This also releases the lock.
+ if (remove(path_.c_str()) < 0)
+ PError("remove");
+ }
+
+ private:
+ PIDFile(int fd, const std::string& path) : fd_(fd), path_(path) {
+ DCHECK(fd_ >= 0);
+ }
+
+ const int fd_;
+ const std::string path_;
+
+ DISALLOW_COPY_AND_ASSIGN(PIDFile);
+};
+
+// Takes ownership of |data|.
+void ReleaseDaemonResourcesAtExit(void* data) {
+ DCHECK(data);
+ delete reinterpret_cast<PIDFile*>(data);
+}
+
+void InitLogging(const char* log_file) {
+ CHECK(
+ logging::InitLogging(
+ log_file,
+ logging::LOG_ONLY_TO_FILE,
+ logging::DONT_LOCK_LOG_FILE,
+ logging::APPEND_TO_OLD_LOG_FILE,
+ logging::ENABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS));
+}
+
+void SigChildHandler(int signal_number) {
+ DCHECK_EQ(signal_number, SIGCHLD);
+ // The daemon should not terminate while its parent is still running.
+ int status;
+ pid_t child_pid = waitpid(-1 /* any child */, &status, WNOHANG);
+ if (child_pid < 0) {
+ PError("waitpid");
+ return;
+ }
+ if (child_pid == 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;
+}
+
+} // namespace
+
+Daemon::Daemon(const std::string& pid_file_path)
+ : pid_file_path_(pid_file_path) {
+}
+
+bool Daemon::Spawn(bool* is_daemon) {
+ switch (fork()) {
+ case -1:
+ *is_daemon = false;
+ PError("fork()");
+ return false;
+ case 0: { // Child.
+ *is_daemon = true;
+ scoped_ptr<PIDFile> pid_file = PIDFile::Create(pid_file_path_);
+ if (!pid_file)
+ return false;
+ base::AtExitManager::RegisterCallback(
+ &ReleaseDaemonResourcesAtExit, pid_file.release());
+ if (setsid() < 0) { // Detach the child process from its parent.
+ PError("setsid");
+ return false;
+ }
+ CloseFD(STDOUT_FILENO);
+ CloseFD(STDERR_FILENO);
+ InitLogging(kLogFilePath);
+ break;
+ }
+ default: // Parent.
+ *is_daemon = false;
+ signal(SIGCHLD, SigChildHandler);
+ }
+ return true;
+}
+
+bool Daemon::Kill() {
+ int pid_file_fd = HANDLE_EINTR(open(pid_file_path_.c_str(), O_WRONLY));
+ if (pid_file_fd < 0) {
+ if (errno == ENOENT)
+ return true;
+ LOG(ERROR) << "Could not open " << pid_file_path_ << " in write mode: "
+ << safe_strerror(errno);
+ return false;
+ }
+ const FileDescriptorAutoCloser fd_closer(pid_file_fd);
+ pid_t lock_owner_pid;
+ if (!GetFileLockOwnerPid(pid_file_fd, &lock_owner_pid))
+ return false;
+ if (lock_owner_pid == 0)
+ // No daemon running.
+ return true;
+ if (kill(lock_owner_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.
+ return true;
+ PError("kill");
+ return false;
+ }
+ // Wait until the daemon exits. Rely on the fact that the daemon releases the
+ // lock on the PID file when it exits.
+ // TODO(pliard): Consider using a mutex + condition in shared memory to avoid
+ // polling.
+ const int kTries = 20;
+ const int kIdleTimeMS = 50;
+ for (int i = 0; i < kTries; ++i) {
+ pid_t current_lock_owner_pid;
+ if (!GetFileLockOwnerPid(pid_file_fd, &current_lock_owner_pid))
+ return false;
+ if (current_lock_owner_pid == 0)
+ // The daemon released the PID file's lock.
+ return true;
+ // Since we are polling we might not see the 'daemon exited' event if
+ // another daemon was spawned during our idle period.
+ if (current_lock_owner_pid != lock_owner_pid) {
+ LOG(WARNING) << "Daemon (pid=" << lock_owner_pid
+ << ") was successfully killed but a new daemon (pid="
+ << current_lock_owner_pid << ") seems to be running now.";
+ return true;
+ }
+ usleep(kIdleTimeMS * 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..f3654e0
--- /dev/null
+++ b/tools/android/forwarder2/daemon.h
@@ -0,0 +1,38 @@
+// 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/at_exit.h"
+#include "base/basictypes.h"
+
+namespace forwarder2 {
+
+class Daemon {
+ public:
+ // |pid_file_path| is the file path to which the daemon's PID will be written.
+ // Note that a lock on the file is also acquired to guarantee that a single
+ // instance of daemon is running.
+ explicit Daemon(const std::string& pid_file_path);
+
+ // Returns whether the daemon was successfully spawned. Use |is_daemon| to
+ // distinguish the parent from the child (daemon) process.
+ bool Spawn(bool* is_daemon);
+
+ // Kills the daemon and blocks until it exited.
+ bool Kill();
+
+ private:
+ const std::string pid_file_path_;
+ base::AtExitManager at_exit_manager;
+
+ 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
index ff4470a..9ab0b48 100644
--- a/tools/android/forwarder2/device_controller.cc
+++ b/tools/android/forwarder2/device_controller.cc
@@ -52,6 +52,7 @@ bool DeviceController::Init(const std::string& adb_unix_socket) {
<< adb_unix_socket << ": " << safe_strerror(errno);
return false;
}
+ LOG(INFO) << "Listening on Unix Domain Socket " << adb_unix_socket;
return true;
}
@@ -60,8 +61,10 @@ void DeviceController::Start() {
CleanUpDeadListeners();
scoped_ptr<Socket> socket(new Socket);
if (!kickstart_adb_socket_.Accept(socket.get())) {
- LOG(ERROR) << "Could not Accept DeviceController socket: "
- << safe_strerror(errno);
+ if (!kickstart_adb_socket_.exited()) {
+ LOG(ERROR) << "Could not Accept DeviceController socket: "
+ << safe_strerror(errno);
+ }
break;
}
// So that |socket| doesn't block on read if it has notifications.
@@ -97,7 +100,7 @@ void DeviceController::Start() {
const int listener_port = new_listener->listener_port();
// |new_listener| is now owned by listeners_ map.
listeners_.AddWithID(new_listener.release(), listener_port);
- printf("Forwarding device port %d to host.\n", listener_port);
+ LOG(INFO) << "Forwarding device port " << listener_port << " to host.";
break;
}
case command::DATA_CONNECTION:
@@ -121,8 +124,6 @@ void DeviceController::Start() {
LOG(ERROR) << "Invalid command received. Port: " << port
<< " Command: " << command;
socket->Close();
- continue;
- break;
}
}
KillAllListeners();
diff --git a/tools/android/forwarder2/device_listener.cc b/tools/android/forwarder2/device_listener.cc
index fdabc70..d67151d 100644
--- a/tools/android/forwarder2/device_listener.cc
+++ b/tools/android/forwarder2/device_listener.cc
@@ -106,6 +106,10 @@ void DeviceListener::RunInternal() {
while (!must_exit_) {
scoped_ptr<Socket> device_data_socket(new Socket);
if (!listener_socket_.Accept(device_data_socket.get())) {
+ if (listener_socket_.exited()) {
+ LOG(INFO) << "Received exit notification, stopped accepting clients.";
+ break;
+ }
LOG(WARNING) << "Could not Accept in ListenerSocket.";
SendCommand(command::ACCEPT_ERROR,
listener_port_,
@@ -160,7 +164,8 @@ bool DeviceListener::BindListenerSocket() {
adb_control_socket_.get());
is_alive_ = true;
} else {
- LOG(ERROR) << "Device could not bind and listen to local port.";
+ LOG(ERROR) << "Device could not bind and listen to local port "
+ << listener_port_;
SendCommand(command::BIND_ERROR,
listener_port_,
adb_control_socket_.get());
diff --git a/tools/android/forwarder2/forwarder.gyp b/tools/android/forwarder2/forwarder.gyp
index f636ad4..205975f 100644
--- a/tools/android/forwarder2/forwarder.gyp
+++ b/tools/android/forwarder2/forwarder.gyp
@@ -38,6 +38,7 @@
],
'sources': [
'command.cc',
+ 'common.cc',
'device_controller.cc',
'device_forwarder_main.cc',
'device_listener.cc',
@@ -60,6 +61,8 @@
],
'sources': [
'command.cc',
+ 'common.cc',
+ 'daemon.cc',
'forwarder.cc',
'host_controller.cc',
'host_forwarder_main.cc',
diff --git a/tools/android/forwarder2/host_controller.cc b/tools/android/forwarder2/host_controller.cc
index 98e487f..b95d986 100644
--- a/tools/android/forwarder2/host_controller.cc
+++ b/tools/android/forwarder2/host_controller.cc
@@ -35,7 +35,7 @@ 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: "
+ LOG(ERROR) << "Could not connect AdbDataSocket on port: "
<< adb_port_;
return;
}
@@ -63,7 +63,7 @@ void HostController::StartForwarder(
bool HostController::Connect() {
if (!adb_control_socket_.ConnectTcp("", adb_port_)) {
- LOG(ERROR) << "Could not Connect HostController socket on port: "
+ LOG(ERROR) << "Could not connect HostController socket on port: "
<< adb_port_;
return false;
}
@@ -91,6 +91,11 @@ void HostController::Run() {
while (true) {
if (!ReceivedCommand(command::ACCEPT_SUCCESS,
&adb_control_socket_)) {
+ // TODO(pliard): This can also happen if device_forwarder was
+ // intentionally killed before host_forwarder. In that case,
+ // device_forwarder should send a notification to the host. Currently the
+ // error message below is always emitted to the log file although this is
+ // not necessarily an error.
LOG(ERROR) << "Device socket error on accepting using port "
<< device_port_;
break;
diff --git a/tools/android/forwarder2/host_forwarder_main.cc b/tools/android/forwarder2/host_forwarder_main.cc
index 6e54189..dff0c8f 100644
--- a/tools/android/forwarder2/host_forwarder_main.cc
+++ b/tools/android/forwarder2/host_forwarder_main.cc
@@ -6,32 +6,49 @@
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
+#include <unistd.h>
#include <vector>
#include <string>
#include "base/command_line.h"
+#include "base/eintr_wrapper.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
#include "base/logging.h"
#include "base/memory/scoped_vector.h"
+#include "base/safe_strerror_posix.h"
#include "base/string_number_conversions.h"
#include "base/string_piece.h"
#include "base/string_split.h"
+#include "base/string_util.h"
#include "base/stringprintf.h"
-#include "tools/android/common/daemon.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"
using base::StringToInt;
-using forwarder2::HostController;
+namespace forwarder2 {
namespace {
-const int kDefaultAdbPort = 3000;
+const char kPIDFilePath[] = "/tmp/host_forwarder_pid";
+const char kCommandSocketPath[] = "host_forwarder_command_socket";
+const char kWelcomeMessage[] = "forwarder2";
+const int kBufSize = 256;
// Need to be global to be able to accessed from the signal handler.
-forwarder2::PipeNotifier* g_notifier;
+PipeNotifier* g_notifier;
-void KillHandler(int /* unused */) {
+void KillHandler(int signal_number) {
+ if (signal_number != SIGTERM && signal_number != SIGINT) {
+ char buf[kBufSize];
+ snprintf(buf, sizeof(buf), "Ignoring unexpected signal %d.", signal_number);
+ SIGNAL_SAFE_LOG(WARNING, buf);
+ return;
+ }
static int s_kill_handler_count = 0;
CHECK(g_notifier);
// If for some reason the forwarder get stuck in any socket waiting forever,
@@ -42,110 +59,224 @@ void KillHandler(int /* unused */) {
exit(1);
}
-// Format of arg: <Device port>[:<Forward to port>:<Forward to address>]
-bool ParseForwardArg(const std::string& arg,
- int* device_port,
- std::string* forward_to_host,
- int* forward_to_port) {
- std::vector<std::string> arg_pieces;
- base::SplitString(arg, ':', &arg_pieces);
- if (arg_pieces.size() == 0 || !StringToInt(arg_pieces[0], device_port))
+enum {
+ kConnectSingleTry = 1,
+ kConnectNoIdleTime = 0,
+};
+
+scoped_ptr<Socket> ConnectToDaemon(int tries_count, int idle_time_msec) {
+ for (int i = 0; i < tries_count; ++i) {
+ scoped_ptr<Socket> socket(new Socket());
+ if (!socket->ConnectUnix(kCommandSocketPath, true)) {
+ if (idle_time_msec)
+ usleep(idle_time_msec * 1000);
+ continue;
+ }
+ char buf[sizeof(kWelcomeMessage)];
+ memset(buf, 0, sizeof(buf));
+ if (socket->Read(buf, sizeof(buf)) < 0) {
+ perror("read");
+ continue;
+ }
+ if (strcmp(buf, kWelcomeMessage)) {
+ LOG(ERROR) << "Unexpected message read from daemon: " << buf;
+ break;
+ }
+ return socket.Pass();
+ }
+ return scoped_ptr<Socket>(NULL);
+}
+
+// Format of |command|:
+// <ADB port>:<Device port>[:<Forward to port>:<Forward to address>].
+bool ParseForwardCommand(const std::string& command,
+ int* adb_port,
+ int* device_port,
+ std::string* forward_to_host,
+ int* forward_to_port) {
+ std::vector<std::string> command_pieces;
+ base::SplitString(command, ':', &command_pieces);
+
+ if (command_pieces.size() < 2 ||
+ !StringToInt(command_pieces[0], adb_port) ||
+ !StringToInt(command_pieces[1], device_port))
return false;
- if (arg_pieces.size() > 1) {
- if (!StringToInt(arg_pieces[1], forward_to_port))
+ if (command_pieces.size() > 2) {
+ if (!StringToInt(command_pieces[2], forward_to_port))
return false;
- if (arg_pieces.size() > 2)
- *forward_to_host = arg_pieces[2];
+ if (command_pieces.size() > 3)
+ *forward_to_host = command_pieces[3];
} else {
*forward_to_port = *device_port;
}
return true;
}
-} // namespace
+bool IsForwardCommandValid(const std::string& command) {
+ int adb_port, device_port, forward_to_port;
+ std::string forward_to_host;
+ std::vector<std::string> command_pieces;
+ return ParseForwardCommand(
+ command, &adb_port, &device_port, &forward_to_host, &forward_to_port);
+}
-int main(int argc, char** argv) {
- printf("Host forwarder to handle incoming connections from Android.\n");
- printf("Like 'adb forward' but in the reverse direction\n");
-
- CommandLine command_line(argc, argv);
- bool show_help = tools::HasHelpSwitch(command_line);
- std::string adb_port_str = command_line.GetSwitchValueASCII("adb_port");
- int adb_port = kDefaultAdbPort;
- if (!adb_port_str.empty() && !StringToInt(adb_port_str, &adb_port)) {
- printf("Could not parse adb port number: %s\n", adb_port_str.c_str());
- show_help = true;
- }
- if (adb_port <= 0) {
- printf("Invalid adb port number: %s. Adb port must be a "
- "postivie integer.\n", adb_port_str.c_str());
- show_help = true;
- }
- CommandLine::StringVector forward_args = command_line.GetArgs();
- if (show_help || forward_args.empty()) {
- tools::ShowHelp(
- argv[0],
- "[--adb_port=<adb port>] "
- "<Device port>[:<Forward to port>:<Forward to address>] ...",
- base::StringPrintf(
- " <adb port> is the TCP port Adb is configured to forward to."
- " Default is %d\n"
- " <Forward to port> default is <Device port>\n"
- " <Forward to address> default is 127.0.0.1.",
- kDefaultAdbPort).c_str());
- return 1;
+bool DaemonHandler() {
+ LOG(INFO) << "Starting host process daemon (pid=" << getpid() << ")";
+ DCHECK(!g_notifier);
+ g_notifier = new PipeNotifier();
+
+ const int notifier_fd = g_notifier->receiver_fd();
+ Socket command_socket;
+ if (!command_socket.BindUnix(kCommandSocketPath, true)) {
+ LOG(ERROR) << "Could not bind Unix Domain Socket";
+ return false;
}
+ command_socket.set_exit_notifier_fd(notifier_fd);
+
+ signal(SIGTERM, KillHandler);
+ signal(SIGINT, KillHandler);
- g_notifier = new forwarder2::PipeNotifier();
ScopedVector<HostController> controllers;
int failed_count = 0;
- for (size_t i = 0; i < forward_args.size(); ++i) {
+
+ for (;;) {
+ Socket client_socket;
+ if (!command_socket.Accept(&client_socket)) {
+ if (command_socket.exited())
+ return true;
+ PError("Accept()");
+ return false;
+ }
+ if (!client_socket.Write(kWelcomeMessage, sizeof(kWelcomeMessage))) {
+ PError("Write()");
+ continue;
+ }
+ char buf[kBufSize];
+ const int bytes_read = client_socket.Read(buf, sizeof(buf));
+ if (bytes_read <= 0) {
+ if (client_socket.exited())
+ break;
+ PError("Read()");
+ ++failed_count;
+ }
+ const std::string command(buf, bytes_read);
+ int adb_port = 0;
int device_port = 0;
std::string forward_to_host;
int forward_to_port = 0;
- if (ParseForwardArg(forward_args[i],
- &device_port,
- &forward_to_host,
- &forward_to_port)) {
- scoped_ptr<HostController> host_controller(
- new HostController(device_port,
- forward_to_host,
- forward_to_port,
- adb_port,
- g_notifier->receiver_fd()));
- if (!host_controller->Connect())
- continue;
- host_controller->Start();
- // Get the current allocated port.
- device_port = host_controller->device_port();
- printf("Forwarding device port %d to host %d:%s\n",
- device_port, forward_to_port, forward_to_host.c_str());
-
- controllers.push_back(host_controller.release());
- } else {
- printf("Couldn't start forwarder server for port spec: %s\n",
- forward_args[i].c_str());
+ const bool succeeded = ParseForwardCommand(
+ command, &adb_port, &device_port, &forward_to_host, &forward_to_port);
+ if (!succeeded) {
+ ++failed_count;
+ client_socket.WriteString(
+ base::StringPrintf("ERROR: Could not parse forward command '%s'",
+ command.c_str()));
+ continue;
+ }
+ scoped_ptr<HostController> host_controller(
+ new HostController(device_port, forward_to_host, forward_to_port,
+ adb_port, notifier_fd));
+ if (!host_controller->Connect()) {
++failed_count;
+ client_socket.WriteString("ERROR: Connection to device failed.");
+ continue;
}
+ // Get the current allocated port.
+ device_port = host_controller->device_port();
+ LOG(INFO) << "Forwarding device port " << device_port << " to host "
+ << forward_to_host << ":" << forward_to_port;
+ if (!client_socket.WriteString(
+ base::StringPrintf("%d:%d", device_port, forward_to_port))) {
+ ++failed_count;
+ continue;
+ }
+ host_controller->Start();
+ controllers.push_back(host_controller.release());
}
-
- // Signal handler must be installed after the for loop above where we start
- // the host_controllers and push_back into the vector. Otherwise a race
- // condition may occur.
- signal(SIGTERM, KillHandler);
- signal(SIGINT, KillHandler);
+ for (int i = 0; i < controllers.size(); ++i)
+ controllers[i]->Join();
if (controllers.size() == 0) {
- printf("No forwarder servers could be started. Exiting.\n");
- return failed_count;
+ LOG(ERROR) << "No forwarder servers could be started. Exiting.";
+ return false;
}
+ return true;
+}
- // TODO(felipeg): We should check if the controllers are really alive before
- // printing Ready.
- printf("Host Forwarder Ready.\n");
- for (int i = 0; i < controllers.size(); ++i)
- controllers[i]->Join();
+void PrintUsage(const char* program_name) {
+ LOG(ERROR) << program_name << " adb_port:from_port:to_port:to_host\n"
+ "<adb port> is the TCP port Adb is configured to forward to.";
+}
+
+int RunHostForwarder(int argc, char** argv) {
+ if (!CommandLine::Init(argc, argv)) {
+ LOG(ERROR) << "Could not initialize command line";
+ return 1;
+ }
+ const CommandLine& command_line = *CommandLine::ForCurrentProcess();
+ std::string command;
+ int adb_port = 0;
+ if (argc != 2) {
+ PrintUsage(argv[0]);
+ return 1;
+ }
+ if (!strcmp(argv[1], "kill-server")) {
+ command = "kill-server";
+ } else {
+ command = "forward";
+ if (!IsForwardCommandValid(argv[1])) {
+ PrintUsage(argv[0]);
+ return 1;
+ }
+ }
+
+ Daemon daemon(kPIDFilePath);
+
+ if (command == "kill-server")
+ return !daemon.Kill();
+
+ bool is_daemon = false;
+ scoped_ptr<Socket> daemon_socket = ConnectToDaemon(
+ kConnectSingleTry, kConnectNoIdleTime);
+ if (!daemon_socket) {
+ if (!daemon.Spawn(&is_daemon))
+ return 1;
+ }
+
+ if (is_daemon)
+ return !DaemonHandler();
+
+ if (!daemon_socket) {
+ const int kTries = 10;
+ const int kIdleTimeMsec = 10;
+ daemon_socket = ConnectToDaemon(kTries, kIdleTimeMsec);
+ if (!daemon_socket) {
+ LOG(ERROR) << "Could not connect to daemon.";
+ return 1;
+ }
+ }
+ // Send the forward command to the daemon.
+ CHECK(daemon_socket->Write(argv[1], strlen(argv[1])));
+ 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;
+ return 1;
+ }
+ printf("%s\n", buf);
return 0;
}
+
+} // namespace
+} // namespace forwarder2
+
+int main(int argc, char** argv) {
+ return forwarder2::RunHostForwarder(argc, argv);
+}
diff --git a/tools/android/forwarder2/socket.cc b/tools/android/forwarder2/socket.cc
index 35553bb..397c096 100644
--- a/tools/android/forwarder2/socket.cc
+++ b/tools/android/forwarder2/socket.cc
@@ -17,21 +17,7 @@
#include "base/logging.h"
#include "base/safe_strerror_posix.h"
#include "tools/android/common/net.h"
-
-// This is used in Close and Shutdown.
-// 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);
-
+#include "tools/android/forwarder2/common.h"
namespace {
const int kNoTimeout = -1;
@@ -88,12 +74,13 @@ Socket::Socket()
abstract_(false),
addr_ptr_(reinterpret_cast<sockaddr*>(&addr_.addr4)),
addr_len_(sizeof(sockaddr)),
- exit_notifier_fd_(-1) {
+ exit_notifier_fd_(-1),
+ exited_(false) {
memset(&addr_, 0, sizeof(addr_));
}
Socket::~Socket() {
- CHECK(IsClosed());
+ Close();
}
void Socket::Shutdown() {
@@ -104,7 +91,7 @@ void Socket::Shutdown() {
void Socket::Close() {
if (!IsClosed()) {
- PRESERVE_ERRNO_HANDLE_EINTR(close(socket_));
+ CloseFD(socket_);
socket_ = -1;
}
}
@@ -242,7 +229,20 @@ bool Socket::Connect() {
PRESERVE_ERRNO_HANDLE_EINTR(fcntl(socket_, F_SETFL, kFlags));
return false;
}
- // Disable non-block since our code assumes blocking semantics.
+ int socket_errno;
+ socklen_t opt_len = sizeof(socket_errno);
+ if (!getsockopt(socket_, SOL_SOCKET, SO_ERROR, &socket_errno, &opt_len) < 0) {
+ LOG(ERROR) << "getsockopt(): " << safe_strerror(errno);
+ SetSocketError();
+ PRESERVE_ERRNO_HANDLE_EINTR(fcntl(socket_, F_SETFL, kFlags));
+ return false;
+ }
+ if (socket_errno != 0) {
+ LOG(ERROR) << "Could not connect to host: " << safe_strerror(socket_errno);
+ SetSocketError();
+ PRESERVE_ERRNO_HANDLE_EINTR(fcntl(socket_, F_SETFL, kFlags));
+ return false;
+ }
fcntl(socket_, F_SETFL, kFlags);
return true;
}
@@ -297,11 +297,11 @@ bool Socket::AddFdToSet(fd_set* fds) const {
return true;
}
-int Socket::ReadNumBytes(char* buffer, size_t num_bytes) {
+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(buffer + bytes_read, num_bytes - bytes_read);
+ ret = Read(static_cast<char*>(buffer) + bytes_read, num_bytes - bytes_read);
if (ret >= 0)
bytes_read += ret;
}
@@ -315,7 +315,7 @@ void Socket::SetSocketError() {
Close();
}
-int Socket::Read(char* buffer, size_t buffer_size) {
+int Socket::Read(void* buffer, size_t buffer_size) {
if (!WaitForEvent(READ, kNoTimeout)) {
SetSocketError();
return 0;
@@ -326,7 +326,7 @@ int Socket::Read(char* buffer, size_t buffer_size) {
return ret;
}
-int Socket::Write(const char* buffer, size_t count) {
+int Socket::Write(const void* buffer, size_t count) {
int ret = HANDLE_EINTR(send(socket_, buffer, count, MSG_NOSIGNAL));
if (ret < 0)
SetSocketError();
@@ -337,18 +337,19 @@ int Socket::WriteString(const std::string& buffer) {
return WriteNumBytes(buffer.c_str(), buffer.size());
}
-int Socket::WriteNumBytes(const char* buffer, size_t num_bytes) {
+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(buffer + bytes_written, num_bytes - bytes_written);
+ 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) const {
+bool Socket::WaitForEvent(EventType type, int timeout_secs) {
if (exit_notifier_fd_ == -1 || socket_ == -1)
return true;
const int nfds = std::max(socket_, exit_notifier_fd_) + 1;
@@ -371,7 +372,11 @@ bool Socket::WaitForEvent(EventType type, int timeout_secs) const {
}
if (HANDLE_EINTR(select(nfds, &read_fds, &write_fds, NULL, tv_ptr)) <= 0)
return false;
- return !FD_ISSET(exit_notifier_fd_, &read_fds);
+ if (FD_ISSET(exit_notifier_fd_, &read_fds)) {
+ exited_ = true;
+ return false;
+ }
+ return true;
}
// static
diff --git a/tools/android/forwarder2/socket.h b/tools/android/forwarder2/socket.h
index 15a04b0..7014069 100644
--- a/tools/android/forwarder2/socket.h
+++ b/tools/android/forwarder2/socket.h
@@ -46,23 +46,24 @@ class Socket {
// 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(char* buffer, size_t buffer_size);
+ int Read(void* buffer, size_t buffer_size);
// Same as Read(), just a wrapper around write().
- int Write(const char* buffer, size_t count);
+ int Write(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(char* buffer, size_t num_bytes);
+ 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 char* buffer, size_t num_bytes);
+ int WriteNumBytes(const void* buffer, size_t num_bytes);
- // Calls WriteNumBytes for the given std::string.
+ // 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_; }
@@ -77,6 +78,10 @@ class Socket {
// anymore.
void reset_exit_notifier_fd() { exit_notifier_fd_ = -1; }
+ // Returns whether Accept() or Connect() was interrupted because the socket
+ // received an exit notification.
+ bool exited() const { return exited_; }
+
static int GetHighestFileDescriptor(const Socket& s1, const Socket& s2);
private:
@@ -104,12 +109,9 @@ class Socket {
WRITE
};
- // Waits until either the Socket or the |exit_notifier_fd_| has received a
- // read event (accept or read). Returns false iff an exit notification was
- // received. If |read| is false, it waits until Socket is ready to write,
- // instead. If |timeout_secs| is a non-negative value, it sets the timeout,
- // for the select operation.
- bool WaitForEvent(EventType type, int timeout_secs) const;
+ // 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_;
@@ -133,6 +135,8 @@ class Socket {
// and Accept.
int exit_notifier_fd_;
+ bool exited_;
+
DISALLOW_COPY_AND_ASSIGN(Socket);
};