| /* |
| * 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; |
| } |
| } |
| } |