blob: 252a1bcda5074668efcaee3a9febc1697a1eef52 [file] [log] [blame]
/*
* Copyright 2019 The Chromium OS 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 <fcntl.h>
#include <base/at_exit.h>
#include <base/logging.h>
#include <base/posix/eintr_wrapper.h>
#include <brillo/flag_helper.h>
#include <brillo/syslog_logging.h>
#include "arc/adbd/adbd.h"
namespace {
constexpr char kRuntimePath[] = "/run/arc/adbd";
} // namespace
int main(int argc, char** argv) {
DEFINE_string(serialnumber, "", "Serial number of the Android container");
DEFINE_bool(arcvm, true, "setup adb over usb for arcvm");
DEFINE_uint32(arcvm_cid, adbd::kVmAddrCidInvalid,
"specify cid (>=3) of ARCVM for vsock connection");
base::AtExitManager at_exit;
brillo::FlagHelper::Init(argc, argv, "ADB over USB proxy.");
brillo::InitLog(brillo::kLogToSyslog | brillo::kLogToStderrIfTty);
if (FLAGS_arcvm) {
// The two options, arcvm and arcvm_cid, must work together and there is no
// point to have cid without VM (ignored). It is attempting to have cid arg
// only to tell arcvm from ARC++ since only VM have cid and vsock is the
// only way to talk with a VM. However, we still keep arcvm for the sake of
// clarity in code and usage, instead of relying on any unacceptable value
// of cid, either provided by user or from an initial value, to obscurely
// branch to the container-based route which differs in many ways.
if (FLAGS_arcvm_cid < adbd::kFirstGuestVmAddr) {
LOG(ERROR) << "Invalid or no cid provided when VM(vsock) is selected.";
return 1;
}
}
const base::FilePath runtime_path(kRuntimePath);
adbd::AdbdConfiguration config;
if (!adbd::GetConfiguration(&config)) {
LOG(INFO) << "Unable to find the configuration for this service. "
<< "This device does not support ADB over USB.";
return 0;
}
const std::string board = adbd::GetStrippedReleaseBoard();
const base::FilePath control_pipe_path = runtime_path.Append("ep0");
if (!FLAGS_arcvm && !adbd::CreatePipe(control_pipe_path))
return 1;
char buffer[4096];
bool configured = false;
base::ScopedFD control_file;
base::ScopedFD control_pipe;
while (true) {
if (!FLAGS_arcvm) {
LOG(INFO) << "arc-adbd ready to receive connections";
// O_RDONLY on a FIFO waits until another endpoint has opened the file
// with O_WRONLY or O_RDWR.
control_pipe = base::ScopedFD(
HANDLE_EINTR(open(control_pipe_path.value().c_str(), O_RDONLY)));
if (!control_pipe.is_valid()) {
PLOG(ERROR) << "Failed to open FIFO at " << control_pipe_path.value();
return 1;
}
LOG(INFO) << "arc-adbd connected";
// Given that a FIFO can be opened by multiple processes, once a process
// has opened it, we atomically replace it with a new FIFO (by using
// rename(2)) so no other process can open it. This causes that when that
// process close(2)s the FD, we will get an EOF when we attempt to read(2)
// from it. This also causes any other process that attempts to open the
// new FIFO to block until we are done processing the current one.
//
// There is a very small chance there is a race here if multiple processes
// get to open the FIFO between the point in time where this process opens
// the FIFO and CreatePipe() returns. That seems unavoidable, but should
// not present too much of a problem since exactly one process in Android
// has the correct user to open this file in the first place (adbd).
if (!adbd::CreatePipe(control_pipe_path))
return 1;
}
// Once adbd has opened the control pipe, we set up the adb gadget on behalf
// of that process, if we have not already.
if (!configured) {
if (!adbd::SetupKernelModules(config.kernel_modules)) {
LOG(ERROR) << "Failed to load kernel modules";
return 1;
}
const std::string udc_driver_name = adbd::GetUDCDriver();
if (udc_driver_name.empty()) {
LOG(ERROR)
<< "Unable to find any registered UDC drivers in /sys/class/udc/. "
<< "This device does not support ADB using GadgetFS.";
return 1;
}
if (!adbd::SetupConfigFS(FLAGS_serialnumber, config.usb_product_id,
board)) {
LOG(ERROR) << "Failed to configure ConfigFS";
return 1;
}
control_file = adbd::SetupFunctionFS(udc_driver_name);
if (!control_file.is_valid()) {
LOG(ERROR) << "Failed to configure FunctionFS";
return 1;
}
if (!FLAGS_arcvm && !adbd::BindMountUsbBulkEndpoints()) {
LOG(ERROR) << "Failed to bind mount FunctionFS";
return 1;
}
configured = true;
}
if (FLAGS_arcvm) {
adbd::StartArcVmAdbBridge(FLAGS_arcvm_cid);
// TODO(crbug.com/1087440): Once we change the design of bridge to return
// instead of terminating the process in error cases, we would
// need to replace the LOG(FATAL) with something else since we
// don't always want to trigger a crash dump.
LOG(FATAL) << "Should not reach here";
}
// Drain the FIFO and wait until the other side closes it.
// The data that is sent is kControlPayloadV2 (or kControlPayloadV1)
// followed by kControlStrings. We ignore it completely since we have
// already sent it to the underlying FunctionFS file, and also to avoid
// parsing it to decrease the attack surface area.
while (true) {
ssize_t bytes_read =
HANDLE_EINTR(read(control_pipe.get(), buffer, sizeof(buffer)));
if (bytes_read < 0)
PLOG(ERROR) << "Failed to read from FIFO";
if (bytes_read <= 0)
break;
}
}
}