blob: 16166bad60a270d0f95bc58144580f3fc6719843 [file] [log] [blame]
// Copyright 2020 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 "src/lib/loader_service/loader_service.h"
#include <fidl/fuchsia.io/cpp/wire.h>
#include <lib/fdio/directory.h>
#include <lib/fdio/io.h>
#include <lib/syslog/cpp/macros.h>
#include <zircon/errors.h>
#include "src/lib/files/path.h"
#include "src/lib/fxl/strings/string_printf.h"
namespace loader {
namespace fio = fuchsia_io;
LoaderServiceBase::~LoaderServiceBase() = default;
const std::string& LoaderServiceBase::log_prefix() {
if (log_prefix_.empty()) {
log_prefix_ = fxl::StringPrintf("ldsvc (%s): ", name_.data());
}
return log_prefix_;
}
zx::result<fidl::ClientEnd<fuchsia_ldsvc::Loader>> LoaderServiceBase::Connect() {
auto [client, server] = fidl::Endpoints<fuchsia_ldsvc::Loader>::Create();
Bind(std::move(server));
return zx::ok(std::move(client));
}
void LoaderServiceBase::Bind(fidl::ServerEnd<fuchsia_ldsvc::Loader> channel) {
// Each connection gets a strong (shared_ptr) reference to the server, which keeps the overall
// service alive as long as there is one open connection even if the original reference is
// dropped.
auto conn = std::make_unique<LoaderConnection>(shared_from_this());
// This returns a ServerBindingRef, but we don't need it since we don't currently need a way
// to unbind connections from the server side. Dropping it does not automatically unbind.
fidl::BindServer(
dispatcher_, std::move(channel), std::move(conn),
[](LoaderConnection* connection, fidl::UnbindInfo info,
fidl::ServerEnd<fuchsia_ldsvc::Loader> server_end) {
if (info.is_peer_closed() || info.is_user_initiated() || info.is_dispatcher_shutdown()) {
// Client broke away or server shutting down.
return;
}
FX_LOGS(ERROR) << "Loader connection error: " << info;
});
}
void LoaderConnection::Done(DoneCompleter::Sync& completer) { completer.Close(ZX_OK); }
void LoaderConnection::LoadObject(LoadObjectRequestView request,
LoadObjectCompleter::Sync& completer) {
std::string name(request->object_name.data(), request->object_name.size());
auto reply = [this, &name, &completer](zx::result<zx::vmo> status) {
// Generally we wouldn't want to log in a library, but these logs have proven to be useful in
// past, and the new loader name in the prefix will make them moreso.
if (status.status_value() == ZX_ERR_NOT_FOUND) {
FX_LOGS(WARNING) << log_prefix() << "could not find '" << name << "'";
}
completer.Reply(status.status_value(), std::move(status).value_or(zx::vmo()));
fidl::Status result = completer.result_of_reply();
if (!result.ok()) {
FX_LOGS(WARNING) << log_prefix() << "failed to reply to LoadObject(" << name
<< "): " << result.error();
}
};
if (config_.subdir.empty()) {
reply(server_->LoadObjectImpl(name));
return;
}
// If subdir is non-empty, the loader should search this subdirectory for the object first. If
// exclusive is also true, only subdir should be searched.
std::string prefixed_name = files::JoinPath(config_.subdir, name);
auto status = server_->LoadObjectImpl(prefixed_name);
if (status.is_error() && !config_.exclusive) {
reply(server_->LoadObjectImpl(name));
return;
}
reply(std::move(status));
}
void LoaderConnection::Config(ConfigRequestView request, ConfigCompleter::Sync& completer) {
// fidl::StringView is not null-terminated so must pass size to std::string constructor.
std::string config_str(request->config.data(), request->config.size());
auto reply = [this, &config_str, &completer](zx_status_t status) {
completer.Reply(status);
fidl::Status result = completer.result_of_reply();
if (!result.ok()) {
FX_LOGS(WARNING) << log_prefix() << "failed to reply to Config(" << config_str
<< "): " << result.error();
}
};
// Config strings must not contain path separators.
if (config_str.find('/') != std::string::npos) {
reply(ZX_ERR_INVALID_ARGS);
return;
}
// The config string is a single subdirectory name to be searched for objects first, optionally
// followed by a '!' character, which indicates that only the subdirectory should be searched.
bool exclusive = false;
if (!config_str.empty() && config_str.back() == '!') {
exclusive = true;
config_str.pop_back();
// Make sure config wasn't "!" (though just "" is ok to reset config)
if (config_str.empty()) {
reply(ZX_ERR_INVALID_ARGS);
return;
}
}
config_ = LoadConfig{.subdir = config_str, .exclusive = exclusive};
reply(ZX_OK);
}
void LoaderConnection::Clone(CloneRequestView request, CloneCompleter::Sync& completer) {
server_->Bind(std::move(request->loader));
completer.Reply(ZX_OK);
}
// static
std::shared_ptr<LoaderService> LoaderService::Create(async_dispatcher_t* dispatcher,
fbl::unique_fd lib_dir, std::string name) {
// Can't use make_shared because constructor is protected
return std::shared_ptr<LoaderService>(
new LoaderService(dispatcher, std::move(lib_dir), std::move(name)));
}
zx::result<zx::vmo> LoaderService::LoadObjectImpl(std::string path) {
const fio::wire::OpenFlags kFlags = fio::wire::OpenFlags::kNotDirectory |
fio::wire::OpenFlags::kRightReadable |
fio::wire::OpenFlags::kRightExecutable;
fbl::unique_fd fd;
zx_status_t status = fdio_open_fd_at(dir_.get(), path.data(), static_cast<uint32_t>(kFlags),
fd.reset_and_get_address());
if (status != ZX_OK) {
return zx::error(status);
}
zx::vmo vmo;
status = fdio_get_vmo_exec(fd.get(), vmo.reset_and_get_address());
if (status != ZX_OK) {
return zx::error(status);
}
return zx::ok(std::move(vmo));
}
} // namespace loader