1// Copyright 2018 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 "launcher.h"
6
7#include <fbl/string.h>
8#include <fbl/vector.h>
9#include <fuchsia/process/c/fidl.h>
10#include <lib/fidl/cpp/message_buffer.h>
11#include <lib/zx/channel.h>
12#include <lib/zx/job.h>
13#include <stdint.h>
14#include <zircon/processargs.h>
15#include <zircon/status.h>
16
17namespace launcher {
18namespace {
19
20fbl::String GetString(fidl_string_t string) {
21    return fbl::String(string.data, string.size);
22}
23
24void PushStrings(fidl_vector_t* input, fbl::Vector<fbl::String>* target) {
25    fidl_string_t* data = static_cast<fidl_string_t*>(input->data);
26    for (size_t i = 0; i < input->count; ++i) {
27        target->push_back(GetString(data[i]));
28    }
29}
30
31void PushCStrs(const fbl::Vector<fbl::String>& source, fbl::Vector<const char*>* target) {
32    target->reserve(target->size() + source.size());
33    for (const auto& str : source) {
34        target->push_back(str.c_str());
35    }
36}
37
38} // namespace
39
40LauncherImpl::LauncherImpl(zx::channel channel)
41    : channel_(fbl::move(channel)),
42      wait_(this, channel_.get(), ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED) {
43}
44
45LauncherImpl::~LauncherImpl() = default;
46
47zx_status_t LauncherImpl::Begin(async_dispatcher_t* dispatcher) {
48    return wait_.Begin(dispatcher);
49}
50
51void LauncherImpl::OnHandleReady(async_dispatcher_t* dispatcher, async::WaitBase* wait, zx_status_t status,
52                                 const zx_packet_signal_t* signal) {
53    if (status != ZX_OK) {
54        NotifyError(status);
55        return;
56    }
57
58    if (signal->observed & ZX_CHANNEL_READABLE) {
59        fidl::MessageBuffer buffer;
60        for (uint64_t i = 0; i < signal->count; i++) {
61            status = ReadAndDispatchMessage(&buffer);
62            if (status == ZX_ERR_SHOULD_WAIT)
63                break;
64            if (status != ZX_OK) {
65                NotifyError(status);
66                return;
67            }
68        }
69        status = wait_.Begin(dispatcher);
70        if (status != ZX_OK) {
71            NotifyError(status);
72        }
73        return;
74    }
75
76    ZX_DEBUG_ASSERT(signal->observed & ZX_CHANNEL_PEER_CLOSED);
77    // Notice that we don't notify an error until we've drained all the messages
78    // out of the channel.
79    NotifyError(ZX_ERR_PEER_CLOSED);
80}
81
82zx_status_t LauncherImpl::ReadAndDispatchMessage(fidl::MessageBuffer* buffer) {
83    fidl::Message message = buffer->CreateEmptyMessage();
84    zx_status_t status = message.Read(channel_.get(), 0);
85    if (status != ZX_OK)
86        return status;
87    if (!message.has_header())
88        return ZX_ERR_INVALID_ARGS;
89    switch (message.ordinal()) {
90    case fuchsia_process_LauncherLaunchOrdinal:
91        return Launch(buffer, fbl::move(message));
92    case fuchsia_process_LauncherCreateWithoutStartingOrdinal:
93        return CreateWithoutStarting(buffer, fbl::move(message));
94    case fuchsia_process_LauncherAddArgsOrdinal:
95        return AddArgs(fbl::move(message));
96    case fuchsia_process_LauncherAddEnvironsOrdinal:
97        return AddEnvirons(fbl::move(message));
98    case fuchsia_process_LauncherAddNamesOrdinal:
99        return AddNames(fbl::move(message));
100    case fuchsia_process_LauncherAddHandlesOrdinal:
101        return AddHandles(fbl::move(message));
102    default:
103        fprintf(stderr, "launcher: error: Unknown message ordinal: %d\n", message.ordinal());
104        return ZX_ERR_NOT_SUPPORTED;
105    }
106}
107
108zx_status_t LauncherImpl::Launch(fidl::MessageBuffer* buffer, fidl::Message message) {
109    const char* error_msg = nullptr;
110    zx_status_t status = message.Decode(&fuchsia_process_LauncherLaunchRequestTable, &error_msg);
111    if (status != ZX_OK) {
112        fprintf(stderr, "launcher: error: Launch: %s\n", error_msg);
113        return status;
114    }
115
116    zx_txid_t txid = message.txid();
117    uint32_t ordinal = message.ordinal();
118
119    launchpad_t* lp = nullptr;
120    PrepareLaunchpad(message, &lp);
121
122    fidl::Builder builder = buffer->CreateBuilder();
123    fidl_message_header_t* header = builder.New<fidl_message_header_t>();
124    header->txid = txid;
125    header->ordinal = ordinal;
126    fuchsia_process_LaunchResult* result = builder.New<fuchsia_process_LaunchResult>();
127
128    status = launchpad_go(lp, &result->process, &error_msg);
129
130    result->status = status;
131    if (status != ZX_OK && error_msg) {
132        uint32_t len = static_cast<uint32_t>(strlen(error_msg));
133        result->error_message.size = len;
134        result->error_message.data = builder.NewArray<char>(len);
135        strncpy(result->error_message.data, error_msg, len);
136    }
137
138    message.set_bytes(builder.Finalize());
139    Reset();
140
141    status = message.Encode(&fuchsia_process_LauncherLaunchResponseTable, &error_msg);
142    if (status != ZX_OK) {
143        fprintf(stderr, "launcher: error: Launch: %s\n", error_msg);
144        return status;
145    }
146    return message.Write(channel_.get(), 0);
147}
148
149zx_status_t LauncherImpl::CreateWithoutStarting(fidl::MessageBuffer* buffer, fidl::Message message) {
150    const char* error_msg = nullptr;
151    zx_status_t status = message.Decode(&fuchsia_process_LauncherCreateWithoutStartingRequestTable, &error_msg);
152    if (status != ZX_OK) {
153        fprintf(stderr, "launcher: error: CreateWithoutStarting: %s\n", error_msg);
154        return status;
155    }
156
157    zx_txid_t txid = message.txid();
158    uint32_t ordinal = message.ordinal();
159
160    launchpad_t* lp = nullptr;
161    PrepareLaunchpad(message, &lp);
162
163    fidl::Builder builder = buffer->CreateBuilder();
164    fidl_message_header_t* header = builder.New<fidl_message_header_t>();
165    header->txid = txid;
166    header->ordinal = ordinal;
167    fuchsia_process_CreateWithoutStartingResult* result = builder.New<fuchsia_process_CreateWithoutStartingResult>();
168
169    launchpad_start_data_t data;
170    status = launchpad_ready_set(lp, &data, &error_msg);
171
172    result->status = status;
173    if (status == ZX_OK) {
174        result->data = builder.New<fuchsia_process_ProcessStartData>();
175        result->data->process = data.process;
176        result->data->root_vmar = data.root_vmar;
177        result->data->thread = data.thread;
178        result->data->entry = data.entry;
179        result->data->sp = data.sp;
180        result->data->bootstrap = data.bootstrap;
181        result->data->vdso_base = data.vdso_base;
182        result->data->base = data.base;
183    } else if (error_msg) {
184        uint32_t len = static_cast<uint32_t>(strlen(error_msg));
185        result->error_message.size = len;
186        result->error_message.data = builder.NewArray<char>(len);
187        strncpy(result->error_message.data, error_msg, len);
188    }
189
190    message.set_bytes(builder.Finalize());
191    Reset();
192
193    status = message.Encode(&fuchsia_process_LauncherCreateWithoutStartingResponseTable, &error_msg);
194    if (status != ZX_OK) {
195        fprintf(stderr, "launcher: error: CreateWithoutStarting: %s\n", error_msg);
196        return status;
197    }
198    return message.Write(channel_.get(), 0);
199}
200
201zx_status_t LauncherImpl::AddArgs(fidl::Message message) {
202    const char* error_msg = nullptr;
203    zx_status_t status = message.Decode(&fuchsia_process_LauncherAddArgsRequestTable, &error_msg);
204    if (status != ZX_OK) {
205        fprintf(stderr, "launcher: error: AddArgs: %s\n", error_msg);
206        return status;
207    }
208    PushStrings(message.GetPayloadAs<fidl_vector_t>(), &args_);
209    return ZX_OK;
210}
211
212zx_status_t LauncherImpl::AddEnvirons(fidl::Message message) {
213    const char* error_msg = nullptr;
214    zx_status_t status = message.Decode(&fuchsia_process_LauncherAddEnvironsRequestTable, &error_msg);
215    if (status != ZX_OK) {
216        fprintf(stderr, "launcher: error: AddEnvirons: %s\n", error_msg);
217        return status;
218    }
219    PushStrings(message.GetPayloadAs<fidl_vector_t>(), &environs_);
220    return ZX_OK;
221}
222
223zx_status_t LauncherImpl::AddNames(fidl::Message message) {
224    const char* error_msg = nullptr;
225    zx_status_t status = message.Decode(&fuchsia_process_LauncherAddNamesRequestTable, &error_msg);
226    if (status != ZX_OK) {
227        fprintf(stderr, "launcher: error: AddNames: %s\n", error_msg);
228        return status;
229    }
230    fidl_vector_t* payload = message.GetPayloadAs<fidl_vector_t>();
231    fuchsia_process_NameInfo* names = static_cast<fuchsia_process_NameInfo*>(payload->data);
232    for (size_t i = 0; i < payload->count; ++i) {
233        ids_.push_back(PA_HND(PA_NS_DIR, static_cast<uint32_t>(nametable_.size())));
234        handles_.push_back(zx::handle(names[i].directory));
235        nametable_.push_back(GetString(names[i].path));
236    }
237    return ZX_OK;
238}
239
240zx_status_t LauncherImpl::AddHandles(fidl::Message message) {
241    const char* error_msg = nullptr;
242    zx_status_t status = message.Decode(&fuchsia_process_LauncherAddHandlesRequestTable, &error_msg);
243    if (status != ZX_OK) {
244        fprintf(stderr, "launcher: error: AddHandles: %s\n", error_msg);
245        return status;
246    }
247    fidl_vector_t* payload = message.GetPayloadAs<fidl_vector_t>();
248    fuchsia_process_HandleInfo* handles = static_cast<fuchsia_process_HandleInfo*>(payload->data);
249    for (size_t i = 0; i < payload->count; ++i) {
250        if (handles[i].id == PA_LDSVC_LOADER) {
251            // We need to feed PA_LDSVC_LOADER to launchpad through a different API.
252            ldsvc_.reset(handles[i].handle);
253        } else {
254            ids_.push_back(handles[i].id);
255            handles_.push_back(zx::handle(handles[i].handle));
256        }
257    }
258    return ZX_OK;
259}
260
261void LauncherImpl::PrepareLaunchpad(const fidl::Message& message, launchpad_t** lp_out) {
262    fuchsia_process_LaunchInfo* info = message.GetPayloadAs<fuchsia_process_LaunchInfo>();
263
264    // Grab an owning reference to the job because launchpad does not take
265    // ownership of the job. We need to close the handle ourselves.
266    zx::job job(info->job);
267    fbl::String name = GetString(info->name);
268
269    fbl::Vector<const char*> args, environs, nametable;
270    PushCStrs(args_, &args);
271    PushCStrs(environs_, &environs);
272    PushCStrs(nametable_, &nametable);
273    environs.push_back(nullptr);
274
275    launchpad_t* lp = nullptr;
276    launchpad_create_with_jobs(job.get(), ZX_HANDLE_INVALID, name.c_str(), &lp);
277
278    if (!ldsvc_) {
279        launchpad_abort(lp, ZX_ERR_INVALID_ARGS, "need ldsvc to load PT_INTERP");
280    }
281
282    // There's a subtle issue at this point. The problem is that launchpad will
283    // make a synchronous call into the loader service to read the PT_INTERP,
284    // but this handle was provided by our client, which means our client can
285    // hang the launcher.
286    zx::channel old_ldsvc(launchpad_use_loader_service(lp, ldsvc_.release()));
287
288    launchpad_load_from_vmo(lp, info->executable);
289    launchpad_set_args(lp, static_cast<int>(args.size()), args.get());
290    launchpad_set_environ(lp, environs.get());
291    launchpad_set_nametable(lp, nametable.size(), nametable.get());
292    launchpad_add_handles(lp, ids_.size(), reinterpret_cast<zx_handle_t*>(handles_.get()), ids_.get());
293    // launchpad_add_handles() took ownership of the handles in handles_.
294    for (auto& handle : handles_) {
295        __UNUSED zx_handle_t old_handle = handle.release();
296    }
297
298    *lp_out = lp;
299}
300
301void LauncherImpl::NotifyError(zx_status_t error) {
302    Reset();
303    channel_.reset();
304    if (error_handler_)
305        error_handler_(error);
306    // We might be deleted now.
307}
308
309void LauncherImpl::Reset() {
310    args_.reset();
311    environs_.reset();
312    nametable_.reset();
313    ids_.reset();
314    handles_.reset();
315    ldsvc_.reset();
316}
317
318} // namespace launcher
319