blob: 237b030448fd8dce1a3a62a1b095314d60d87108 [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 <fuchsia/io/llcpp/fidl.h>
#include <lib/fdio/directory.h>
#include <lib/fdio/io.h>
#include <lib/fidl-async/cpp/bind.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 = ::llcpp::fuchsia::io;
LoaderServiceBase::~LoaderServiceBase() {}
const std::string& LoaderServiceBase::log_prefix() {
if (log_prefix_.empty()) {
log_prefix_ = fxl::StringPrintf("ldsvc (%s): ", name_.data());
}
return log_prefix_;
}
zx::status<zx::channel> LoaderServiceBase::Connect() {
zx::channel c1, c2;
auto status = zx::make_status(zx::channel::create(0, &c1, &c2));
if (status.is_error()) {
return status.take_error();
}
status = Bind(std::move(c1));
if (status.is_error()) {
return status.take_error();
}
return zx::ok(std::move(c2));
}
zx::status<> LoaderServiceBase::Bind(zx::channel 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 on success, 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.
auto status = fidl::BindServer(dispatcher_, std::move(channel), std::move(conn));
if (status.is_error()) {
return zx::error(status.take_error());
}
return zx::ok();
}
void LoaderConnection::Done(DoneCompleter::Sync& completer) { completer.Close(ZX_OK); }
void LoaderConnection::LoadObject(fidl::StringView object_name,
LoadObjectCompleter::Sync& completer) {
std::string name(object_name.data(), object_name.size());
auto reply = [this, &name, &completer](zx::status<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 << "'";
}
auto result = completer.Reply(status.status_value(), std::move(status).value_or(zx::vmo()));
if (!result.ok()) {
FX_LOGS(WARNING) << log_prefix() << "failed to reply to LoadObject(" << name
<< "): " << result.status_string() << " (" << 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(fidl::StringView config, ConfigCompleter::Sync& completer) {
// fidl::StringView is not null-terminated so must pass size to std::string constructor.
std::string config_str(config.data(), config.size());
auto reply = [this, &config_str, &completer](zx_status_t status) {
auto result = completer.Reply(status);
if (!result.ok()) {
FX_LOGS(WARNING) << log_prefix() << "failed to reply to Config(" << config_str
<< "): " << result.status_string() << " (" << 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(zx::channel loader, CloneCompleter::Sync& completer) {
completer.Reply(server_->Bind(std::move(loader)).status_value());
}
// 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::status<zx::vmo> LoaderService::LoadObjectImpl(std::string path) {
const uint32_t kFlags =
fio::OPEN_FLAG_NOT_DIRECTORY | fio::OPEN_RIGHT_READABLE | fio::OPEN_RIGHT_EXECUTABLE;
fbl::unique_fd fd;
zx_status_t status = fdio_open_fd_at(dir_.get(), path.data(), 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