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