blob: 89ef45197f8cd73177c9d4e09fe932f8c2f46bd0 [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/txn_header.h>
#include <zircon/processargs.h>
#include <zircon/syscalls.h>
#include <cstring>
#include <ldmsg/ldmsg.h>
#include "bootfs.h"
#include "util.h"
void LoaderService::Config(const char* string, size_t len) {
exclusive_ = false;
if (string[len - 1] == '!') {
--len;
exclusive_ = true;
}
if (len >= sizeof(prefix_) - 1) {
fail(log_, "loader-service config string too long");
}
memcpy(prefix_, string, len);
prefix_[len++] = '/';
prefix_len_ = len;
}
zx::vmo LoaderService::TryLoadObject(const char* name, size_t len, bool use_prefix) {
size_t prefix_len = use_prefix ? prefix_len_ : 0;
char file[len + sizeof(kLoadObjectFilePrefix) + prefix_len + 1];
memcpy(file, kLoadObjectFilePrefix, sizeof(kLoadObjectFilePrefix) - 1);
memcpy(&file[sizeof(kLoadObjectFilePrefix) - 1], prefix_, prefix_len);
memcpy(&file[sizeof(kLoadObjectFilePrefix) - 1 + prefix_len], name, len);
file[sizeof(kLoadObjectFilePrefix) - 1 + prefix_len + len] = '\0';
return fs_->Open(root_, file, "shared library");
}
zx::vmo LoaderService::LoadObject(const char* name, size_t len) {
zx::vmo vmo = TryLoadObject(name, len, true);
if (!vmo && prefix_len_ > 0 && !exclusive_) {
vmo = TryLoadObject(name, len, false);
}
if (!vmo) {
fail(log_, "cannot find shared library '%s'", name);
}
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_init_txn_header(&rsp.header, req.header.txid, req.header.ordinal);
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));
}