1// Copyright 2017 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "bootfs.h"
6#include "util.h"
7
8#pragma GCC visibility push(hidden)
9
10#include <ldmsg/ldmsg.h>
11#include <string.h>
12#include <zircon/processargs.h>
13#include <zircon/syscalls.h>
14
15#pragma GCC visibility pop
16
17#define LOAD_OBJECT_FILE_PREFIX "lib/"
18
19struct loader_state {
20    zx_handle_t log;
21    struct bootfs* bootfs;
22    char prefix[32];
23    size_t prefix_len;
24    bool exclusive;
25};
26
27static void loader_config(struct loader_state* state, const char* string, size_t len) {
28    state->exclusive = false;
29    if (string[len - 1] == '!') {
30        --len;
31        state->exclusive = true;
32    }
33    if (len >= sizeof(state->prefix) - 1) {
34        fail(state->log, "loader-service config string too long");
35    }
36    memcpy(state->prefix, string, len);
37    state->prefix[len++] = '/';
38    state->prefix_len = len;
39}
40
41static zx_handle_t try_load_object(struct loader_state* state,
42                                   const char* name, size_t len,
43                                   size_t prefix_len) {
44    char file[len + sizeof(LOAD_OBJECT_FILE_PREFIX) + prefix_len + 1];
45    memcpy(file, LOAD_OBJECT_FILE_PREFIX, sizeof(LOAD_OBJECT_FILE_PREFIX) - 1);
46    memcpy(&file[sizeof(LOAD_OBJECT_FILE_PREFIX) - 1],
47           state->prefix, prefix_len);
48    memcpy(&file[sizeof(LOAD_OBJECT_FILE_PREFIX) - 1 + prefix_len], name, len);
49    file[sizeof(LOAD_OBJECT_FILE_PREFIX) - 1 + prefix_len + len] = '\0';
50    return bootfs_open(state->log, "shared library", state->bootfs, file);
51}
52
53static zx_handle_t load_object(struct loader_state* state, const char* name, size_t len) {
54    zx_handle_t vmo = try_load_object(state, name, len, state->prefix_len);
55    if (vmo == ZX_HANDLE_INVALID && state->prefix_len > 0 && !state->exclusive)
56        vmo = try_load_object(state, name, len, 0);
57    if (vmo == ZX_HANDLE_INVALID)
58        fail(state->log, "cannot find shared library '%s'", name);
59    return vmo;
60}
61
62static bool handle_loader_rpc(struct loader_state* state,
63                              zx_handle_t channel) {
64    ldmsg_req_t req;
65    zx_handle_t reqhandle;
66
67    uint32_t size;
68    uint32_t hcount;
69    zx_status_t status = zx_channel_read(
70        channel, 0, &req, &reqhandle, sizeof(req), 1, &size, &hcount);
71
72    // This is the normal error for the other end going away,
73    // which happens when the process dies.
74    if (status == ZX_ERR_PEER_CLOSED) {
75        printl(state->log, "loader-service channel peer closed on read");
76        return false;
77    }
78
79    check(state->log, status,
80          "zx_channel_read on loader-service channel failed");
81
82    const char* string;
83    size_t string_len;
84    status = ldmsg_req_decode(&req, size, &string, &string_len);
85    if (status != ZX_OK) {
86        fail(state->log, "loader-service request invalid");
87    }
88
89    ldmsg_rsp_t rsp;
90    memset(&rsp, 0, sizeof(rsp));
91
92    zx_handle_t handle = ZX_HANDLE_INVALID;
93    switch (req.header.ordinal) {
94    case LDMSG_OP_DONE:
95        printl(state->log, "loader-service received DONE request");
96        goto no_reply;
97
98    case LDMSG_OP_CONFIG:
99        loader_config(state, string, string_len);
100        break;
101
102    case LDMSG_OP_LOAD_OBJECT:
103        handle = load_object(state, string, string_len);
104        break;
105
106    case LDMSG_OP_CLONE:
107        rsp.rv = ZX_ERR_NOT_SUPPORTED;
108        goto error_reply;
109
110    case LDMSG_OP_LOAD_SCRIPT_INTERPRETER:
111        fail(state->log, "loader-service received LOAD_SCRIPT_INTERP request");
112        break;
113
114    case LDMSG_OP_DEBUG_PUBLISH_DATA_SINK: {
115        if (hcount != 1) {
116            fail(state->log, "loader-service received DEBUG_PUBLISH_DATA_SINK request without VMO");
117        }
118
119        char name[ZX_MAX_NAME_LEN];
120        status = zx_object_get_property(reqhandle, ZX_PROP_NAME, name, sizeof(name));
121        if (status != ZX_OK) {
122            fail(state->log, "zx_object_get_property failed");
123        }
124
125        uint64_t size;
126        status = zx_vmo_get_size(reqhandle, &size);
127        if (status != ZX_OK) {
128            fail(state->log, "zx_vmo_get_size failed");
129        }
130
131        printl(state->log, "loader-service data-sink \"%s\" DATA DROPPED: \"%s\", "
132                           "%zu bytes", string, name, (size_t)size);
133        break;
134    }
135
136    default:
137        fail(state->log, "loader-service received invalid opcode");
138        break;
139    }
140
141    rsp.rv = ZX_OK;
142    rsp.object = handle == ZX_HANDLE_INVALID ?
143        FIDL_HANDLE_ABSENT : FIDL_HANDLE_PRESENT;
144error_reply:
145    rsp.header.txid = req.header.txid;
146    rsp.header.ordinal = req.header.ordinal;
147
148    // no opcodes which receive a handle are supported, but
149    // we need to receive (and discard) the handle to politely
150    // NAK clone requests
151    if (hcount == 1) {
152        zx_handle_close(reqhandle);
153    }
154
155    status = zx_channel_write(channel, 0, &rsp, ldmsg_rsp_get_size(&rsp),
156                              &handle, handle == ZX_HANDLE_INVALID ? 0 : 1);
157    check(state->log, status,
158          "zx_channel_write on loader-service channel failed");
159
160    return true;
161
162no_reply:
163    if (hcount == 1) {
164        zx_handle_close(reqhandle);
165    }
166    return false;
167}
168
169void loader_service(zx_handle_t log, struct bootfs* bootfs,
170                    zx_handle_t channel) {
171    printl(log, "waiting for loader-service requests...");
172
173    struct loader_state state = {
174        .log = log,
175        .bootfs = bootfs,
176    };
177
178    do {
179        zx_signals_t signals;
180        zx_status_t status = zx_object_wait_one(
181            channel, ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED,
182            ZX_TIME_INFINITE, &signals);
183        if (status == ZX_ERR_BAD_STATE) {
184            // This is the normal error for the other end going away,
185            // which happens when the process dies.
186            break;
187        }
188        check(log, status,
189              "zx_object_wait_one failed on loader-service channel");
190        if (signals & ZX_CHANNEL_PEER_CLOSED) {
191            printl(log, "loader-service channel peer closed");
192            break;
193        }
194        if (!(signals & ZX_CHANNEL_READABLE)) {
195            fail(log, "unexpected signal state on loader-service channel");
196        }
197    } while (handle_loader_rpc(&state, channel));
198
199    check(log, zx_handle_close(channel),
200          "zx_handle_close failed on loader-service channel");
201}
202