blob: ccc19a0d47e9a32c6fad9330df1f9583ca128b8f [file]
// Copyright 2022 The Fuchsia 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 "adb-file-sync.h"
#include <fuchsia/io/cpp/fidl.h>
#include <lib/component/incoming/cpp/protocol.h>
#include <lib/component/outgoing/cpp/outgoing_directory.h>
#include <lib/fdio/fdio.h>
#include <lib/syslog/cpp/macros.h>
#include <vector>
#include "fidl/fuchsia.sys2/cpp/common_types.h"
#include "src/developer/adb/third_party/adb-file-sync/file_sync_service.h"
#include "src/developer/adb/third_party/adb-file-sync/util.h"
namespace adb_file_sync {
zx_status_t AdbFileSync::StartService(adb_file_sync_config::Config config) {
FX_LOGS(DEBUG) << "Starting ADB File Sync Service";
async::Loop loop{&kAsyncLoopConfigNeverAttachToThread};
AdbFileSync file_sync(std::move(config), loop.dispatcher());
auto endpoints = fidl::CreateEndpoints<fuchsia_sys2::RealmQuery>();
if (endpoints.is_error()) {
FX_LOGS(ERROR) << "Could not create endpoints " << endpoints.error_value();
return endpoints.error_value();
}
auto status = file_sync.context_->svc()->Connect("fuchsia.sys2.RealmQuery.root",
endpoints->server.TakeChannel());
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Could not connect to cache RealmQuery " << status;
return status;
}
file_sync.realm_query_.Bind(std::move(endpoints->client));
auto lifecycle_ep = fidl::CreateEndpoints<fuchsia_sys2::LifecycleController>();
if (lifecycle_ep.is_error()) {
FX_LOGS(ERROR) << "Could not create endpoints " << lifecycle_ep.error_value();
return lifecycle_ep.error_value();
}
status = file_sync.context_->svc()->Connect("fuchsia.sys2.LifecycleController.root",
lifecycle_ep->server.TakeChannel());
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Could not connect to cache RealmQuery " << status;
return status;
}
file_sync.lifecycle_.Bind(std::move(lifecycle_ep->client));
component::OutgoingDirectory outgoing = component::OutgoingDirectory(loop.dispatcher());
auto result = outgoing.AddUnmanagedProtocol<fuchsia_hardware_adb::Provider>(
[&file_sync, &loop](fidl::ServerEnd<fuchsia_hardware_adb::Provider> server_end) {
file_sync.binding_ref_.emplace(fidl::BindServer(loop.dispatcher(), std::move(server_end),
&file_sync,
std::mem_fn(&AdbFileSync::OnUnbound)));
});
if (result.is_error()) {
FX_LOGS(ERROR) << "Could not publish service " << result.error_value();
return result.error_value();
}
result = outgoing.ServeFromStartupInfo();
if (result.is_error()) {
FX_LOGS(ERROR) << "Failed to serve outgoing directory: " << result.status_string();
return result.error_value();
}
loop.Run();
return ZX_OK;
}
void AdbFileSync::OnUnbound(fidl::UnbindInfo info,
fidl::ServerEnd<fuchsia_hardware_adb::Provider> server_end) {
if (info.is_user_initiated()) {
return;
}
if (info.is_peer_closed()) {
// If the peer (the client) closed their endpoint, log that as DEBUG.
FX_LOGS(DEBUG) << "Client disconnected";
} else {
// Treat other unbind causes as errors.
FX_LOGS(ERROR) << "Server error: " << info;
}
}
void AdbFileSync::ConnectToService(
fuchsia_hardware_adb::wire::ProviderConnectToServiceRequest* request,
ConnectToServiceCompleter::Sync& completer) {
completer.Reply(fit::ok());
file_sync_service(this, std::move(request->socket));
}
zx::result<zx::channel> AdbFileSync::ConnectToComponent(std::string name,
std::vector<std::string>* out_path) {
const std::string kDeliminator = "::";
// Parse component moniker
const auto component_path = split_string(name, kDeliminator);
std::string component_moniker;
std::string path;
if (component_path.size() == 1) {
component_moniker = config_.filesync_moniker();
path = component_path[0];
} else if (component_path.size() == 2) {
component_moniker = component_path[0];
path = component_path[1];
} else {
FX_LOGS(ERROR) << "Invalid address! " << component_path.size() << " " << name;
return zx::error(ZX_ERR_INVALID_ARGS);
}
if (component_moniker.empty()) {
FX_LOGS(ERROR) << "Must have a component!";
return zx::error(ZX_ERR_INVALID_ARGS);
}
if (component_moniker[0] != '.') {
component_moniker.insert(0, ".");
}
// Resolve component moniker
auto resolve_result = lifecycle_->ResolveInstance(component_moniker);
if (resolve_result.is_error()) {
FX_LOGS(ERROR) << "FIDL call to resolve moniker failed" << resolve_result.error_value();
return zx::error(resolve_result.error_value().is_domain_error()
? static_cast<uint32_t>(resolve_result.error_value().domain_error())
: resolve_result.error_value().framework_error().status());
}
// Connect to component
auto result = realm_query_->ConstructNamespace(component_moniker);
if (result.is_error()) {
FX_LOGS(ERROR) << "RealmQuery failed " << result.error_value().FormatDescription();
return zx::error(result.error_value().is_domain_error()
? static_cast<uint32_t>(result.error_value().domain_error())
: result.error_value().framework_error().status());
}
if (result->namespace_().empty()) {
FX_LOGS(ERROR) << "RealmQuery did not return any directories.";
return zx::error(ZX_ERR_INVALID_ARGS);
}
if (!path.starts_with("/")) {
path = "/" + path;
}
for (auto& entry : result->namespace_()) {
// `entry.path()` might contain more than one "/", like "/config/data"
// `path` might include extra mode at the end like "/some/path,0755", and we should
// keep that in out_path->back()
if (entry.path().has_value() && path.starts_with(entry.path().value())) {
auto sub_path = path.substr(entry.path()->size());
// prevent matching if path is "/ab" and entry.path() is "/a"
if (sub_path != "" && sub_path[0] != '/' && sub_path[0] != ',') {
continue;
}
*out_path = split_string(sub_path, "/");
return zx::success(entry.directory()->TakeChannel());
}
}
FX_LOGS(ERROR) << "Could not find directory for " << path;
return zx::error(ZX_ERR_NOT_FOUND);
}
} // namespace adb_file_sync