// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <signal.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
#include <cstdio>
#include <iostream>
#include <limits>
#include <memory>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
#include "base/pickle.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "tools/android/forwarder2/common.h"
#include "tools/android/forwarder2/daemon.h"
#include "tools/android/forwarder2/host_controllers_manager.h"
#include "tools/android/forwarder2/pipe_notifier.h"
namespace forwarder2 {
// Needs to be global to be able to be accessed from the signal handler.
PipeNotifier* g_notifier = NULL;
namespace {
const char kLogFilePath[] = "/tmp/host_forwarder_log";
const char kDaemonIdentifier[] = "chrome_host_forwarder_daemon";
const int kBufSize = 256;
// 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);
}
class ServerDelegate : public Daemon::ServerDelegate {
public:
explicit ServerDelegate(const std::string& adb_path)
: adb_path_(adb_path),
has_failed_(false),
controllers_manager_(base::BindRepeating(&GetExitNotifierFD)) {}
ServerDelegate(const ServerDelegate&) = delete;
ServerDelegate& operator=(const ServerDelegate&) = delete;
bool has_failed() const {
return has_failed_ || controllers_manager_.has_failed();
}
// Daemon::ServerDelegate:
void Init() override {
LOG(INFO) << "Starting host process daemon (pid=" << getpid() << ")";
DCHECK(!g_notifier);
g_notifier = new PipeNotifier();
signal(SIGTERM, KillHandler);
signal(SIGINT, KillHandler);
}
void OnClientConnected(std::unique_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 base::Pickle command_pickle =
base::Pickle::WithUnownedBuffer(base::as_bytes(
base::span(buf, base::checked_cast<size_t>(bytes_read))));
base::PickleIterator pickle_it(command_pickle);
std::string device_serial;
CHECK(pickle_it.ReadString(&device_serial));
int command;
if (!pickle_it.ReadInt(&command)) {
client_socket->WriteString("Error: missing command\n");
return;
}
int device_port;
if (!pickle_it.ReadInt(&device_port))
device_port = -1;
int host_port;
if (!pickle_it.ReadInt(&host_port))
host_port = -1;
controllers_manager_.HandleRequest(adb_path_, device_serial, command,
device_port, host_port,
std::move(client_socket));
}
private:
std::string adb_path_;
bool has_failed_;
HostControllersManager controllers_manager_;
};
class ClientDelegate : public Daemon::ClientDelegate {
public:
explicit ClientDelegate(const base::Pickle& command_pickle)
: command_pickle_(command_pickle), has_failed_(false) {}
bool has_failed() const { return has_failed_; }
// Daemon::ClientDelegate:
void OnDaemonReady(Socket* daemon_socket) override {
// Send the forward command to the daemon.
CHECK_EQ(static_cast<long>(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(static_cast<size_t>(bytes_read) < sizeof(buf));
buf[bytes_read] = 0;
std::string_view msg(buf, bytes_read);
if (base::StartsWith(msg, "ERROR")) {
LOG(ERROR) << msg;
has_failed_ = true;
return;
}
printf("%s\n", buf);
}
private:
const base::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"
" --unmap-all\n"
" --adb PATH_TO_ADB\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_t>::max()) {
LOG(ERROR) << "Could not convert string " << s << " to port";
ExitWithUsage();
}
return value;
}
int RunHostForwarder(int argc, char** argv) {
base::CommandLine::Init(argc, argv);
const base::CommandLine& cmd_line = *base::CommandLine::ForCurrentProcess();
std::string adb_path = "adb";
bool kill_server = false;
base::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();
pickle.WriteInt(UNMAP);
pickle.WriteInt(PortToInt(args[0]));
} else if (cmd_line.HasSwitch("unmap-all")) {
pickle.WriteInt(UNMAP_ALL);
} else if (cmd_line.HasSwitch("map")) {
if (args.size() != 2)
ExitWithUsage();
pickle.WriteInt(MAP);
pickle.WriteInt(PortToInt(args[0]));
pickle.WriteInt(PortToInt(args[1]));
} else {
ExitWithUsage();
}
if (cmd_line.HasSwitch("adb")) {
adb_path = cmd_line.GetSwitchValueASCII("adb");
}
if (kill_server && args.size() > 0)
ExitWithUsage();
ClientDelegate client_delegate(pickle);
ServerDelegate daemon_delegate(adb_path);
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);
}