blob: be58d0b1acb16860768b4776deb4bbc70eac5250 [file] [log] [blame]
// Copyright 2017 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 "bootfs.h"
#include "util.h"
#include <ldmsg/ldmsg.h>
#include <string.h>
#include <zircon/processargs.h>
#include <zircon/syscalls.h>
#define LOAD_OBJECT_FILE_PREFIX "lib/"
struct loader_state {
zx_handle_t log;
struct bootfs* bootfs;
char prefix[32];
size_t prefix_len;
bool exclusive;
};
static void loader_config(struct loader_state* state, const char* string, size_t len) {
state->exclusive = false;
if (string[len - 1] == '!') {
--len;
state->exclusive = true;
}
if (len >= sizeof(state->prefix) - 1) {
fail(state->log, "loader-service config string too long");
}
memcpy(state->prefix, string, len);
state->prefix[len++] = '/';
state->prefix_len = len;
}
static zx_handle_t try_load_object(struct loader_state* state,
const char* name, size_t len,
size_t prefix_len) {
char file[len + sizeof(LOAD_OBJECT_FILE_PREFIX) + prefix_len + 1];
memcpy(file, LOAD_OBJECT_FILE_PREFIX, sizeof(LOAD_OBJECT_FILE_PREFIX) - 1);
memcpy(&file[sizeof(LOAD_OBJECT_FILE_PREFIX) - 1],
state->prefix, prefix_len);
memcpy(&file[sizeof(LOAD_OBJECT_FILE_PREFIX) - 1 + prefix_len], name, len);
file[sizeof(LOAD_OBJECT_FILE_PREFIX) - 1 + prefix_len + len] = '\0';
return bootfs_open(state->log, "shared library", state->bootfs, file);
}
static zx_handle_t load_object(struct loader_state* state, const char* name, size_t len) {
zx_handle_t vmo = try_load_object(state, name, len, state->prefix_len);
if (vmo == ZX_HANDLE_INVALID && state->prefix_len > 0 && !state->exclusive)
vmo = try_load_object(state, name, len, 0);
if (vmo == ZX_HANDLE_INVALID)
fail(state->log, "cannot find shared library '%s'", name);
return vmo;
}
static bool handle_loader_rpc(struct loader_state* state,
zx_handle_t channel) {
ldmsg_req_t req;
zx_handle_t reqhandle;
uint32_t size;
uint32_t hcount;
zx_status_t status = zx_channel_read(
channel, 0, &req, &reqhandle, 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(state->log, "loader-service channel peer closed on read");
return false;
}
check(state->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(state->log, "loader-service request invalid");
}
ldmsg_rsp_t rsp;
memset(&rsp, 0, sizeof(rsp));
zx_handle_t handle = ZX_HANDLE_INVALID;
switch (req.header.ordinal) {
case LDMSG_OP_DONE_OLD:
case LDMSG_OP_DONE:
printl(state->log, "loader-service received DONE request");
goto no_reply;
case LDMSG_OP_CONFIG_OLD:
case LDMSG_OP_CONFIG:
loader_config(state, string, string_len);
break;
case LDMSG_OP_LOAD_OBJECT_OLD:
case LDMSG_OP_LOAD_OBJECT:
handle = load_object(state, string, string_len);
break;
case LDMSG_OP_CLONE_OLD:
case LDMSG_OP_CLONE:
rsp.rv = ZX_ERR_NOT_SUPPORTED;
goto error_reply;
case LDMSG_OP_LOAD_SCRIPT_INTERPRETER_OLD:
case LDMSG_OP_LOAD_SCRIPT_INTERPRETER:
fail(state->log, "loader-service received LOAD_SCRIPT_INTERP request");
break;
case LDMSG_OP_DEBUG_PUBLISH_DATA_SINK_OLD:
case LDMSG_OP_DEBUG_PUBLISH_DATA_SINK: {
if (hcount != 1) {
fail(state->log, "loader-service received DEBUG_PUBLISH_DATA_SINK request without VMO");
}
char name[ZX_MAX_NAME_LEN];
status = zx_object_get_property(reqhandle, ZX_PROP_NAME, name, sizeof(name));
if (status != ZX_OK) {
fail(state->log, "zx_object_get_property failed");
}
uint64_t size;
status = zx_vmo_get_size(reqhandle, &size);
if (status != ZX_OK) {
fail(state->log, "zx_vmo_get_size failed");
}
printl(state->log, "loader-service data-sink \"%s\" DATA DROPPED: \"%s\", "
"%zu bytes", string, name, (size_t)size);
break;
}
default:
fail(state->log, "loader-service received invalid opcode");
break;
}
rsp.rv = ZX_OK;
rsp.object = handle == ZX_HANDLE_INVALID ?
FIDL_HANDLE_ABSENT : FIDL_HANDLE_PRESENT;
error_reply:
rsp.header.txid = req.header.txid;
rsp.header.ordinal = req.header.ordinal;
// no opcodes which receive a handle are supported, but
// we need to receive (and discard) the handle to politely
// NAK clone requests
if (hcount == 1) {
zx_handle_close(reqhandle);
}
status = zx_channel_write(
channel, 0, &rsp,
static_cast<uint32_t>(ldmsg_rsp_get_size(&rsp)),
&handle, handle == ZX_HANDLE_INVALID ? 0 : 1);
check(state->log, status,
"zx_channel_write on loader-service channel failed");
return true;
no_reply:
if (hcount == 1) {
zx_handle_close(reqhandle);
}
return false;
}
void loader_service(zx_handle_t log, struct bootfs* bootfs,
zx_handle_t channel) {
printl(log, "waiting for loader-service requests...");
struct loader_state state = {};
state.log = log;
state.bootfs = bootfs;
do {
zx_signals_t signals;
zx_status_t status = zx_object_wait_one(
channel, 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 (handle_loader_rpc(&state, channel));
check(log, zx_handle_close(channel),
"zx_handle_close failed on loader-service channel");
}