blob: ddfd9d85b5af41e6f34a5ad77409816159e1d76f [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include "loader-service.h"
#include <lib/fidl/cpp/transaction_header.h>
#include <zircon/boot/bootfs.h>
#include <zircon/processargs.h>
#include <zircon/syscalls.h>
#include <cstring>
#include <fbl/string_buffer.h>
#include <ldmsg/ldmsg.h>
#include "bootfs.h"
#include "util.h"
void LoaderService::Config(std::string_view string) {
exclusive_ = false;
if (!string.empty() && string.back() == '!') {
string.remove_suffix(1);
exclusive_ = true;
}
if (string.size() >= subdir_.size() - 1) {
fail(log_, "loader-service config string too long");
}
string.copy(subdir_.data(), subdir_.size());
subdir_len_ = string.size();
}
zx::vmo LoaderService::TryLoadObject(std::string_view name, bool use_subdir) {
fbl::StringBuffer<ZBI_BOOTFS_MAX_NAME_LEN> file;
file.Append(kLoadObjectFileDir).Append('/');
if (use_subdir && subdir_len_ > 0) {
file.Append(subdir_.data(), subdir_len_).Append('/');
}
file.Append(name);
return fs_->Open(root_, file, "shared library");
}
zx::vmo LoaderService::LoadObject(std::string_view name) {
zx::vmo vmo = TryLoadObject(name, true);
if (!vmo && subdir_len_ > 0 && !exclusive_) {
vmo = TryLoadObject(name, false);
}
if (!vmo) {
fail(log_, "cannot find shared library '%.*s'", static_cast<int>(name.size()), name.data());
}
return vmo;
}
bool LoaderService::HandleRequest(const zx::channel& channel) {
ldmsg_req_t req;
zx::vmo reqhandle;
uint32_t size;
uint32_t hcount;
zx_status_t status =
channel.read(0, &req, reqhandle.reset_and_get_address(), sizeof(req), 1, &size, &hcount);
// This is the normal error for the other end going away,
// which happens when the process dies.
if (status == ZX_ERR_PEER_CLOSED) {
printl(log_, "loader-service channel peer closed on read");
return false;
}
check(log_, status, "zx_channel_read on loader-service channel failed");
const char* string;
size_t string_len;
status = ldmsg_req_decode(&req, size, &string, &string_len);
if (status != ZX_OK) {
fail(log_, "loader-service request invalid");
}
ldmsg_rsp_t rsp;
memset(&rsp, 0, sizeof(rsp));
zx::vmo vmo;
switch (req.header.ordinal) {
case LDMSG_OP_DONE:
printl(log_, "loader-service received DONE request");
return false;
case LDMSG_OP_CONFIG:
Config({string, string_len});
break;
case LDMSG_OP_LOAD_OBJECT:
vmo = LoadObject({string, string_len});
break;
case LDMSG_OP_CLONE:
rsp.rv = ZX_ERR_NOT_SUPPORTED;
goto error_reply;
default:
fail(log_, "loader-service received invalid opcode");
break;
}
rsp.rv = ZX_OK;
rsp.object = vmo ? FIDL_HANDLE_PRESENT : FIDL_HANDLE_ABSENT;
error_reply:
fidl::InitTxnHeader(&rsp.header, req.header.txid, req.header.ordinal,
fidl::MessageDynamicFlags::kStrictMethod);
if (vmo) {
zx_handle_t handles[] = {vmo.release()};
status = channel.write(0, &rsp, static_cast<uint32_t>(ldmsg_rsp_get_size(&rsp)), handles, 1);
} else {
status = channel.write(0, &rsp, static_cast<uint32_t>(ldmsg_rsp_get_size(&rsp)), nullptr, 0);
}
check(log_, status, "zx_channel_write on loader-service channel failed");
return true;
}
void LoaderService::Serve(zx::channel channel) {
printl(log_, "waiting for loader-service requests...");
do {
zx_signals_t signals;
zx_status_t status = channel.wait_one(ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED,
zx::time::infinite(), &signals);
if (status == ZX_ERR_BAD_STATE) {
// This is the normal error for the other end going away,
// which happens when the process dies.
break;
}
check(log_, status, "zx_object_wait_one failed on loader-service channel");
if (signals & ZX_CHANNEL_PEER_CLOSED) {
printl(log_, "loader-service channel peer closed");
break;
}
if (!(signals & ZX_CHANNEL_READABLE)) {
fail(log_, "unexpected signal state on loader-service channel");
}
} while (HandleRequest(channel));
}