summaryrefslogtreecommitdiffstats
path: root/tools/android
diff options
context:
space:
mode:
authorpliard@chromium.org <pliard@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-23 12:39:34 +0000
committerpliard@chromium.org <pliard@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-23 12:39:34 +0000
commit2ffd1b1ca57c97ea404633a43d200c379cd9b11c (patch)
tree1bd21734d68be2fbe0f087f198ba4518afbe112b /tools/android
parent33a0c9ab6e6ad970f39def1d3f8f9e52ce795e05 (diff)
downloadchromium_src-2ffd1b1ca57c97ea404633a43d200c379cd9b11c.zip
chromium_src-2ffd1b1ca57c97ea404633a43d200c379cd9b11c.tar.gz
chromium_src-2ffd1b1ca57c97ea404633a43d200c379cd9b11c.tar.bz2
Use the new forwarder2's Daemon implementation in device_forwarder.
This lets device_forwarder be controlled (started and killed) like host_forwarder (see r167167). This has the main benefit of moving the setup/tear down complexity from the Python Forwarder wrapper code to the forwarder itself so that various clients (including WebKit) can use forwarder2 more easily (e.g. no need to use pexpect anymore (which is not available in WebKit)). This implied a refactoring in the Daemon class so that its clients don't have to duplicate the socket-related code. Review URL: https://chromiumcodereview.appspot.com/11360248 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@169334 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'tools/android')
-rw-r--r--tools/android/forwarder2/daemon.cc288
-rw-r--r--tools/android/forwarder2/daemon.h63
-rw-r--r--tools/android/forwarder2/device_controller.cc6
-rw-r--r--tools/android/forwarder2/device_forwarder_main.cc179
-rw-r--r--tools/android/forwarder2/forwarder.gyp1
-rw-r--r--tools/android/forwarder2/host_forwarder_main.cc246
6 files changed, 521 insertions, 262 deletions
diff --git a/tools/android/forwarder2/daemon.cc b/tools/android/forwarder2/daemon.cc
index 2928f8f..5bdf936 100644
--- a/tools/android/forwarder2/daemon.cc
+++ b/tools/android/forwarder2/daemon.cc
@@ -7,29 +7,32 @@
#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 <cstdlib>
+#include <cstring>
#include <string>
#include "base/basictypes.h"
#include "base/file_path.h"
#include "base/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/string_number_conversions.h"
#include "base/stringprintf.h"
#include "tools/android/forwarder2/common.h"
+#include "tools/android/forwarder2/socket.h"
namespace forwarder2 {
namespace {
-const char kLogFilePath[] = "/tmp/host_forwarder_log";
+const int kBufferSize = 256;
class FileDescriptorAutoCloser {
public:
@@ -54,71 +57,44 @@ class FileDescriptorAutoCloser {
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) {
+void InitLoggingForDaemon(const std::string& log_file) {
CHECK(
logging::InitLogging(
- log_file,
- logging::LOG_ONLY_TO_FILE,
- logging::DONT_LOCK_LOG_FILE,
- logging::APPEND_TO_OLD_LOG_FILE,
+ log_file.c_str(),
+ log_file.empty() ?
+ logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG : logging::LOG_ONLY_TO_FILE,
+ logging::DONT_LOCK_LOG_FILE, logging::APPEND_TO_OLD_LOG_FILE,
logging::ENABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS));
}
+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->exited())
+ 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());
+ }
+ server_delegate->OnServerExited();
+ return !failed;
+}
+
void SigChildHandler(int signal_number) {
DCHECK_EQ(signal_number, SIGCHLD);
+ SIGNAL_SAFE_LOG(ERROR, "Caught unexpected SIGCHLD");
// The daemon should not terminate while its parent is still running.
int status;
pid_t child_pid = waitpid(-1 /* any child */, &status, WNOHANG);
@@ -166,39 +142,181 @@ bool GetFileLockOwnerPid(int fd, pid_t* lock_owner_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, true)) {
+ 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, sizeof(buf)) < 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>(NULL);
+}
+
} // namespace
-Daemon::Daemon(const std::string& pid_file_path)
- : pid_file_path_(pid_file_path) {
+// Handles creation and destruction of the PID file.
+class Daemon::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);
+};
+
+Daemon::Daemon(const std::string& log_file_path,
+ const std::string& pid_file_path,
+ const std::string& identifier,
+ ClientDelegate* client_delegate,
+ ServerDelegate* server_delegate,
+ GetExitNotifierFDCallback get_exit_fd_callback)
+ : log_file_path_(log_file_path),
+ pid_file_path_(pid_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_);
}
-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");
+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: {
+ DCHECK(!pid_file_);
+ pid_file_ = PIDFile::Create(pid_file_path_);
+ if (!pid_file_)
+ exit(1);
+ 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_, true)) {
+ PError("bind()");
+ exit(1);
+ }
+ server_delegate_->Init();
+ command_socket.set_exit_notifier_fd(get_exit_fd_callback_());
+ exit(!RunServerAcceptLoop(identifier_, &command_socket,
+ server_delegate_));
}
- CloseFD(STDOUT_FILENO);
- CloseFD(STDERR_FILENO);
- InitLogging(kLogFilePath);
- break;
+ default:
+ break;
}
- default: // Parent.
- *is_daemon = false;
- signal(SIGCHLD, SigChildHandler);
}
- return true;
+ // 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) {
+ const int kConnectTries = 20;
+ const int kConnectIdleTimeMSec = 10;
+ client_socket = ConnectToUnixDomainSocket(
+ identifier_, kConnectTries, kConnectIdleTimeMSec, 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() {
diff --git a/tools/android/forwarder2/daemon.h b/tools/android/forwarder2/daemon.h
index f3654e0..84f646d 100644
--- a/tools/android/forwarder2/daemon.h
+++ b/tools/android/forwarder2/daemon.h
@@ -7,28 +7,73 @@
#include <string>
-#include "base/at_exit.h"
#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;
+
+ virtual void OnServerExited() = 0;
+ };
+
// |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);
+ // |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& pid_file_path,
+ const std::string& identifier,
+ ClientDelegate* client_delegate,
+ ServerDelegate* server_delegate,
+ GetExitNotifierFDCallback get_exit_fd_callback);
- // Returns whether the daemon was successfully spawned. Use |is_daemon| to
- // distinguish the parent from the child (daemon) process.
- bool Spawn(bool* is_daemon);
+ ~Daemon();
- // Kills the daemon and blocks until it exited.
+ // 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:
+ class PIDFile;
+
+ const std::string log_file_path_;
const std::string pid_file_path_;
- base::AtExitManager at_exit_manager;
+ const std::string identifier_;
+ ClientDelegate* const client_delegate_;
+ ServerDelegate* const server_delegate_;
+ const GetExitNotifierFDCallback get_exit_fd_callback_;
+ scoped_ptr<PIDFile> pid_file_;
DISALLOW_COPY_AND_ASSIGN(Daemon);
};
diff --git a/tools/android/forwarder2/device_controller.cc b/tools/android/forwarder2/device_controller.cc
index 9ab0b48..4a19787 100644
--- a/tools/android/forwarder2/device_controller.cc
+++ b/tools/android/forwarder2/device_controller.cc
@@ -64,6 +64,8 @@ void DeviceController::Start() {
if (!kickstart_adb_socket_.exited()) {
LOG(ERROR) << "Could not Accept DeviceController socket: "
<< safe_strerror(errno);
+ } else {
+ LOG(INFO) << "Received exit notification";
}
break;
}
@@ -73,7 +75,6 @@ void DeviceController::Start() {
command::Type command;
if (!ReadCommand(socket.get(), &port, &command)) {
LOG(ERROR) << "Invalid command received.";
- socket->Close();
continue;
}
DeviceListener* listener = listeners_.Lookup(port);
@@ -110,7 +111,6 @@ void DeviceController::Start() {
// 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.
- socket->Close();
continue;
} else if (!listener->SetAdbDataSocket(socket.Pass())) {
LOG(ERROR) << "Could not set Adb Data Socket for port: " << port;
@@ -123,12 +123,10 @@ void DeviceController::Start() {
// TODO(felipeg): add a KillAllListeners command.
LOG(ERROR) << "Invalid command received. Port: " << port
<< " Command: " << command;
- socket->Close();
}
}
KillAllListeners();
CleanUpDeadListeners();
- kickstart_adb_socket_.Close();
}
} // namespace forwarder
diff --git a/tools/android/forwarder2/device_forwarder_main.cc b/tools/android/forwarder2/device_forwarder_main.cc
index 4a731b2..c6f0442 100644
--- a/tools/android/forwarder2/device_forwarder_main.cc
+++ b/tools/android/forwarder2/device_forwarder_main.cc
@@ -8,20 +8,32 @@
#include <string>
+#include "base/at_exit.h"
+#include "base/bind.h"
#include "base/command_line.h"
+#include "base/compiler_specific.h"
#include "base/logging.h"
+#include "base/string_piece.h"
#include "base/stringprintf.h"
-#include "tools/android/common/daemon.h"
+#include "base/threading/thread.h"
+#include "tools/android/forwarder2/common.h"
+#include "tools/android/forwarder2/daemon.h"
#include "tools/android/forwarder2/device_controller.h"
#include "tools/android/forwarder2/pipe_notifier.h"
+namespace forwarder2 {
namespace {
// Leaky global instance, accessed from the signal handler.
forwarder2::PipeNotifier* g_notifier = NULL;
-// Unix domain socket name for Device Controller.
-const char kDefaultAdbSocket[] = "chrome_device_forwarder";
+const int kBufSize = 256;
+
+const char kPIDFilePath[] = "/data/local/tmp/chrome_device_forwarder_pid";
+const char kDaemonIdentifier[] = "chrome_device_forwarder_daemon";
+
+const char kKillServerCommand[] = "kill-server";
+const char kStartCommand[] = "start";
void KillHandler(int /* unused */) {
CHECK(g_notifier);
@@ -29,38 +41,141 @@ void KillHandler(int /* unused */) {
exit(1);
}
-} // namespace
+// Lets the daemon fetch the exit notifier file descriptor.
+int GetExitNotifierFD() {
+ DCHECK(g_notifier);
+ return g_notifier->receiver_fd();
+}
-int main(int argc, char** argv) {
- printf("Device forwarder to forward connections to the Host machine.\n");
- printf("Like 'adb forward' but in the reverse direction\n");
-
- CommandLine command_line(argc, argv);
- std::string adb_socket_path = command_line.GetSwitchValueASCII("adb_sock");
- if (adb_socket_path.empty())
- adb_socket_path = kDefaultAdbSocket;
- if (tools::HasHelpSwitch(command_line)) {
- tools::ShowHelp(
- argv[0],
- "[--adb_sock=<adb sock>]",
- base::StringPrintf(
- " <adb sock> is the Abstract Unix Domain Socket path "
- " where Adb is configured to forward from."
- " Default is %s\n", kDefaultAdbSocket).c_str());
- return 0;
+class ServerDelegate : public Daemon::ServerDelegate {
+ public:
+ // Daemon::ServerDelegate:
+ virtual void Init() OVERRIDE {
+ DCHECK(!g_notifier);
+ g_notifier = new forwarder2::PipeNotifier();
+ signal(SIGTERM, KillHandler);
+ signal(SIGINT, KillHandler);
+ controller_thread_.reset(new base::Thread("controller_thread"));
+ controller_thread_->Start();
}
- if (!tools::HasNoSpawnDaemonSwitch(command_line))
- tools::SpawnDaemon(0);
- g_notifier = new forwarder2::PipeNotifier();
+ virtual void OnClientConnected(scoped_ptr<Socket> client_socket) OVERRIDE {
+ char buf[kBufSize];
+ const int bytes_read = client_socket->Read(buf, sizeof(buf));
+ if (bytes_read <= 0) {
+ if (client_socket->exited())
+ return;
+ PError("Read()");
+ return;
+ }
+ const std::string adb_socket_path(buf, bytes_read);
+ if (adb_socket_path == adb_socket_path_) {
+ client_socket->WriteString("OK");
+ return;
+ }
+ if (!adb_socket_path_.empty()) {
+ client_socket->WriteString(
+ base::StringPrintf(
+ "ERROR: Device controller already running (adb_socket_path=%s)",
+ adb_socket_path_.c_str()));
+ return;
+ }
+ adb_socket_path_ = adb_socket_path;
+ controller_thread_->message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&ServerDelegate::StartController, adb_socket_path,
+ GetExitNotifierFD(), base::Passed(&client_socket)));
+ }
- signal(SIGTERM, KillHandler);
- signal(SIGINT, KillHandler);
- CHECK(g_notifier);
- forwarder2::DeviceController controller(g_notifier->receiver_fd());
- if (!controller.Init(adb_socket_path))
+ virtual void OnServerExited() OVERRIDE {}
+
+ private:
+ static void StartController(const std::string& adb_socket_path,
+ int exit_notifier_fd,
+ scoped_ptr<Socket> client_socket) {
+ forwarder2::DeviceController controller(exit_notifier_fd);
+ if (!controller.Init(adb_socket_path)) {
+ client_socket->WriteString(
+ base::StringPrintf("ERROR: Could not initialize device controller "
+ "with ADB socket path: %s",
+ adb_socket_path.c_str()));
+ return;
+ }
+ client_socket->WriteString("OK");
+ client_socket->Close();
+ // Note that the following call is blocking which explains why the device
+ // controller has to live on a separate thread (so that the daemon command
+ // server is not blocked).
+ controller.Start();
+ }
+
+ base::AtExitManager at_exit_manager_; // Used by base::Thread.
+ scoped_ptr<base::Thread> controller_thread_;
+ std::string adb_socket_path_;
+};
+
+class ClientDelegate : public Daemon::ClientDelegate {
+ public:
+ ClientDelegate(const std::string& adb_socket)
+ : adb_socket_(adb_socket),
+ has_failed_(false) {
+ }
+
+ bool has_failed() const { return has_failed_; }
+
+ // Daemon::ClientDelegate:
+ virtual void OnDaemonReady(Socket* daemon_socket) OVERRIDE {
+ // Send the adb socket path to the daemon.
+ CHECK(daemon_socket->Write(adb_socket_.c_str(),
+ adb_socket_.length()));
+ char buf[kBufSize];
+ const int bytes_read = daemon_socket->Read(
+ buf, sizeof(buf) - 1 /* leave space for null terminator */);
+ CHECK_GT(bytes_read, 0);
+ DCHECK(bytes_read < sizeof(buf));
+ buf[bytes_read] = 0;
+ base::StringPiece msg(buf, bytes_read);
+ if (msg.starts_with("ERROR")) {
+ LOG(ERROR) << msg;
+ has_failed_ = true;
+ return;
+ }
+ }
+
+ private:
+ const std::string adb_socket_;
+ bool has_failed_;
+};
+
+int RunDeviceForwarder(int argc, char** argv) {
+ if (argc != 2) {
+ fprintf(stderr,
+ "Usage: %s kill-server|<adb_socket>\n"
+ " <adb_socket> is the abstract Unix Domain Socket path "
+ "where Adb is configured to forward from.\n", argv[0]);
return 1;
- printf("Starting Device Forwarder.\n");
- controller.Start();
- return 0;
+ }
+ CommandLine::Init(argc, argv); // Needed by logging.
+ const char* const command =
+ !strcmp(argv[1], kKillServerCommand) ? kKillServerCommand : kStartCommand;
+ ClientDelegate client_delegate(argv[1]);
+ ServerDelegate daemon_delegate;
+ const char kLogFilePath[] = ""; // Log to logcat.
+ Daemon daemon(kLogFilePath, kPIDFilePath, kDaemonIdentifier, &client_delegate,
+ &daemon_delegate, &GetExitNotifierFD);
+
+ if (command == kKillServerCommand)
+ return !daemon.Kill();
+
+ DCHECK(command == kStartCommand);
+ if (!daemon.SpawnIfNeeded())
+ return 1;
+ return client_delegate.has_failed();
+}
+
+} // namespace
+} // namespace forwarder2
+
+int main(int argc, char** argv) {
+ return forwarder2::RunDeviceForwarder(argc, argv);
}
diff --git a/tools/android/forwarder2/forwarder.gyp b/tools/android/forwarder2/forwarder.gyp
index 205975f..db21208 100644
--- a/tools/android/forwarder2/forwarder.gyp
+++ b/tools/android/forwarder2/forwarder.gyp
@@ -39,6 +39,7 @@
'sources': [
'command.cc',
'common.cc',
+ 'daemon.cc',
'device_controller.cc',
'device_forwarder_main.cc',
'device_listener.cc',
diff --git a/tools/android/forwarder2/host_forwarder_main.cc b/tools/android/forwarder2/host_forwarder_main.cc
index d7f52d1..435cae4 100644
--- a/tools/android/forwarder2/host_forwarder_main.cc
+++ b/tools/android/forwarder2/host_forwarder_main.cc
@@ -4,14 +4,15 @@
#include <errno.h>
#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
#include <unistd.h>
-#include <vector>
+#include <cstdio>
+#include <cstring>
#include <string>
+#include <vector>
#include "base/command_line.h"
+#include "base/compiler_specific.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/logging.h"
@@ -29,26 +30,36 @@
#include "tools/android/forwarder2/pipe_notifier.h"
#include "tools/android/forwarder2/socket.h"
-using base::StringToInt;
-
namespace forwarder2 {
namespace {
+const char kLogFilePath[] = "/tmp/host_forwarder_log";
const char kPIDFilePath[] = "/tmp/host_forwarder_pid";
-const char kCommandSocketPath[] = "host_forwarder_command_socket";
-const char kWelcomeMessage[] = "forwarder2";
+const char kDaemonIdentifier[] = "chrome_host_forwarder_daemon";
+
+const char kKillServerCommand[] = "kill-server";
+const char kForwardCommand[] = "forward";
+
const int kBufSize = 256;
-// Need to be global to be able to accessed from the signal handler.
-PipeNotifier* g_notifier;
+// 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) {
- char buf[kBufSize];
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,
@@ -59,34 +70,6 @@ void KillHandler(int signal_number) {
exit(1);
}
-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,
@@ -98,12 +81,12 @@ bool ParseForwardCommand(const std::string& command,
base::SplitString(command, ':', &command_pieces);
if (command_pieces.size() < 2 ||
- !StringToInt(command_pieces[0], adb_port) ||
- !StringToInt(command_pieces[1], device_port))
+ !base::StringToInt(command_pieces[0], adb_port) ||
+ !base::StringToInt(command_pieces[1], device_port))
return false;
if (command_pieces.size() > 2) {
- if (!StringToInt(command_pieces[2], forward_to_port))
+ if (!base::StringToInt(command_pieces[2], forward_to_port))
return false;
if (command_pieces.size() > 3)
*forward_to_host = command_pieces[3];
@@ -121,44 +104,30 @@ bool IsForwardCommandValid(const std::string& command) {
command, &adb_port, &device_port, &forward_to_host, &forward_to_port);
}
-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);
+class ServerDelegate : public Daemon::ServerDelegate {
+ public:
+ ServerDelegate() : has_failed_(false) {}
- signal(SIGTERM, KillHandler);
- signal(SIGINT, KillHandler);
+ bool has_failed() const { return has_failed_; }
- ScopedVector<HostController> controllers;
- int failed_count = 0;
+ // 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);
+ }
- 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;
- }
+ virtual void OnClientConnected(scoped_ptr<Socket> client_socket) OVERRIDE {
char buf[kBufSize];
- const int bytes_read = client_socket.Read(buf, sizeof(buf));
+ const int bytes_read = client_socket->Read(buf, sizeof(buf));
if (bytes_read <= 0) {
- if (client_socket.exited())
- break;
+ if (client_socket->exited())
+ return;
PError("Read()");
- ++failed_count;
+ has_failed_ = true;
+ return;
}
const std::string command(buf, bytes_read);
int adb_port = 0;
@@ -168,41 +137,81 @@ bool DaemonHandler() {
const bool succeeded = ParseForwardCommand(
command, &adb_port, &device_port, &forward_to_host, &forward_to_port);
if (!succeeded) {
- ++failed_count;
- client_socket.WriteString(
+ has_failed_ = true;
+ client_socket->WriteString(
base::StringPrintf("ERROR: Could not parse forward command '%s'",
command.c_str()));
- continue;
+ return;
}
scoped_ptr<HostController> host_controller(
new HostController(device_port, forward_to_host, forward_to_port,
- adb_port, notifier_fd));
+ adb_port, GetExitNotifierFD()));
if (!host_controller->Connect()) {
- ++failed_count;
- client_socket.WriteString("ERROR: Connection to device failed.");
- continue;
+ has_failed_ = true;
+ client_socket->WriteString("ERROR: Connection to device failed.");
+ return;
}
// 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(
+ if (!client_socket->WriteString(
base::StringPrintf("%d:%d", device_port, forward_to_port))) {
- ++failed_count;
- continue;
+ has_failed_ = true;
+ return;
}
host_controller->Start();
- controllers.push_back(host_controller.release());
+ controllers_.push_back(host_controller.release());
}
- for (int i = 0; i < controllers.size(); ++i)
- controllers[i]->Join();
- if (controllers.size() == 0) {
- LOG(ERROR) << "No forwarder servers could be started. Exiting.";
- return false;
+ virtual void OnServerExited() OVERRIDE {
+ for (int i = 0; i < controllers_.size(); ++i)
+ controllers_[i]->Join();
+ if (controllers_.size() == 0) {
+ LOG(ERROR) << "No forwarder servers could be started. Exiting.";
+ has_failed_ = true;
+ }
+ }
+
+ private:
+ ScopedVector<HostController> controllers_;
+ bool has_failed_;
+
+ DISALLOW_COPY_AND_ASSIGN(ServerDelegate);
+};
+
+class ClientDelegate : public Daemon::ClientDelegate {
+ public:
+ ClientDelegate(const std::string& forward_command)
+ : forward_command_(forward_command),
+ has_failed_(false) {
}
- return true;
-}
+
+ bool has_failed() const { return has_failed_; }
+
+ // Daemon::ClientDelegate:
+ virtual void OnDaemonReady(Socket* daemon_socket) OVERRIDE {
+ // Send the forward command to the daemon.
+ CHECK(daemon_socket->WriteString(forward_command_));
+ 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 std::string forward_command_;
+ bool has_failed_;
+};
void PrintUsage(const char* program_name) {
LOG(ERROR) << program_name << " adb_port:from_port:to_port:to_host\n"
@@ -215,63 +224,36 @@ int RunHostForwarder(int argc, char** argv) {
return 1;
}
const CommandLine& command_line = *CommandLine::ForCurrentProcess();
- std::string command;
+ const char* command = NULL;
int adb_port = 0;
if (argc != 2) {
PrintUsage(argv[0]);
return 1;
}
- if (!strcmp(argv[1], "kill-server")) {
- command = "kill-server";
+ if (!strcmp(argv[1], kKillServerCommand)) {
+ command = kKillServerCommand;
} else {
- command = "forward";
+ command = kForwardCommand;
if (!IsForwardCommandValid(argv[1])) {
PrintUsage(argv[0]);
return 1;
}
}
- Daemon daemon(kPIDFilePath);
+ ClientDelegate client_delegate(argv[1]);
+ ServerDelegate daemon_delegate;
+ Daemon daemon(
+ kLogFilePath, kPIDFilePath, kDaemonIdentifier, &client_delegate,
+ &daemon_delegate, &GetExitNotifierFD);
- if (command == "kill-server")
+ if (command == kKillServerCommand)
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;
+ DCHECK(command == kForwardCommand);
+ if (!daemon.SpawnIfNeeded())
return 1;
- }
- printf("%s\n", buf);
- return 0;
+
+ return client_delegate.has_failed() || daemon_delegate.has_failed();
}
} // namespace