| // 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"); |
| } |