| // Copyright 2018 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 <lib/fdio/spawn.h> |
| |
| #include <fcntl.h> |
| #include <lib/fdio/io.h> |
| #include <lib/fdio/limits.h> |
| #include <lib/fdio/namespace.h> |
| #include <lib/fdio/util.h> |
| #include <fuchsia/process/c/fidl.h> |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <zircon/assert.h> |
| #include <zircon/dlfcn.h> |
| #include <zircon/process.h> |
| #include <zircon/processargs.h> |
| #include <zircon/syscalls.h> |
| |
| #define FDIO_SPAWN_LAUNCH_HANDLE_EXECUTABLE ((size_t)0u) |
| #define FDIO_SPAWN_LAUNCH_HANDLE_JOB ((size_t)1u) |
| #define FDIO_SPAWN_LAUNCH_HANDLE_COUNT ((size_t)2u) |
| |
| #define FDIO_SPAWN_LAUNCH_REPLY_HANDLE_COUNT ((size_t)1u) |
| |
| // Even though FDIO_MAX_HANDLES is 3, the clone and transfer operations can only |
| // ever generate 2 handles. |
| #define FDIO_MAX_HANDLES_FOR_CLONE_OR_TRANSFER ((size_t)2u) |
| |
| // The fdio_spawn_action_t is replicated in various ffi interfaces, including |
| // the rust and golang standard libraries. |
| static_assert(sizeof(fdio_spawn_action_t) == 24, |
| "fdio_spawn_action_t must have a stable ABI"); |
| static_assert(offsetof(fdio_spawn_action_t, action) == 0, |
| "fdio_spawn_action_t must have a stable ABI"); |
| static_assert(offsetof(fdio_spawn_action_t, fd) == 8, |
| "fdio_spawn_action_t must have a stable ABI"); |
| static_assert(offsetof(fdio_spawn_action_t, fd.local_fd) == 8, |
| "fdio_spawn_action_t must have a stable ABI"); |
| static_assert(offsetof(fdio_spawn_action_t, fd.target_fd) == 12, |
| "fdio_spawn_action_t must have a stable ABI"); |
| static_assert(offsetof(fdio_spawn_action_t, ns) == 8, |
| "fdio_spawn_action_t must have a stable ABI"); |
| static_assert(offsetof(fdio_spawn_action_t, ns.prefix) == 8, |
| "fdio_spawn_action_t must have a stable ABI"); |
| static_assert(offsetof(fdio_spawn_action_t, ns.handle) == 16, |
| "fdio_spawn_action_t must have a stable ABI"); |
| static_assert(offsetof(fdio_spawn_action_t, h) == 8, |
| "fdio_spawn_action_t must have a stable ABI"); |
| static_assert(offsetof(fdio_spawn_action_t, h.id) == 8, |
| "fdio_spawn_action_t must have a stable ABI"); |
| static_assert(offsetof(fdio_spawn_action_t, h.handle) == 12, |
| "fdio_spawn_action_t must have a stable ABI"); |
| static_assert(offsetof(fdio_spawn_action_t, name) == 8, |
| "fdio_spawn_action_t must have a stable ABI"); |
| static_assert(offsetof(fdio_spawn_action_t, name.data) == 8, |
| "fdio_spawn_action_t must have a stable ABI"); |
| |
| static zx_status_t load_path(const char* path, zx_handle_t* vmo) { |
| int fd = open(path, O_RDONLY); |
| if (fd < 0) |
| return ZX_ERR_IO; |
| zx_status_t status = fdio_get_vmo_clone(fd, vmo); |
| close(fd); |
| |
| if (status == ZX_OK) { |
| if (strlen(path) >= ZX_MAX_NAME_LEN) { |
| const char* p = strrchr(path, '/'); |
| if (p != NULL) { |
| path = p + 1; |
| } |
| } |
| |
| zx_object_set_property(*vmo, ZX_PROP_NAME, path, strlen(path)); |
| } |
| |
| return status; |
| } |
| |
| static void measure_string_array(const char* const* array, size_t* count_out, size_t* len_out) { |
| size_t i = 0; |
| size_t len = 0; |
| while (array[i]) { |
| len += FIDL_ALIGN(strlen(array[i])); |
| ++i; |
| } |
| *count_out = i; |
| *len_out = len; |
| } |
| |
| static void report_error(char* err_msg, const char* format, ...) { |
| if (!err_msg) |
| return; |
| va_list args; |
| va_start(args, format); |
| vsnprintf(err_msg, FDIO_SPAWN_ERR_MSG_MAX_LENGTH, format, args); |
| va_end(args); |
| } |
| |
| static zx_status_t send_string_array(zx_handle_t launcher, int ordinal, const char* const* array) { |
| size_t count = 0; |
| size_t len = 0; |
| |
| // TODO(abarth): In principle, we should chunk array into separate |
| // messages if we exceed ZX_CHANNEL_MAX_MSG_BYTES. |
| measure_string_array(array, &count, &len); |
| |
| if (count == 0) |
| return ZX_OK; |
| |
| size_t msg_len = sizeof(fidl_message_header_t) + sizeof(fidl_vector_t) + count * sizeof(fidl_string_t) + FIDL_ALIGN(len); |
| uint8_t msg[msg_len]; |
| memset(msg, 0, msg_len); |
| |
| fidl_message_header_t* hdr = (fidl_message_header_t*)msg; |
| fidl_vector_t* vector = (fidl_vector_t*)hdr + 1; |
| fidl_string_t* strings = (fidl_string_t*)(vector + 1); |
| uint8_t* payload = (uint8_t*)(strings + count); |
| |
| hdr->ordinal = ordinal; |
| vector->count = count; |
| vector->data = (void*)FIDL_ALLOC_PRESENT; |
| |
| size_t offset = 0; |
| for (size_t i = 0; i < count; ++i) { |
| size_t size = strlen(array[i]); |
| strings[i].size = size; |
| strings[i].data = (void*)FIDL_ALLOC_PRESENT; |
| memcpy(payload + offset, array[i], size); |
| offset += FIDL_ALIGN(size); |
| } |
| |
| return zx_channel_write(launcher, 0, msg, msg_len, NULL, 0); |
| } |
| |
| static zx_status_t send_handles(zx_handle_t launcher, size_t handle_capacity, |
| uint32_t flags, zx_handle_t job, size_t action_count, |
| const fdio_spawn_action_t* actions, char* err_msg) { |
| // TODO(abarth): In principle, we should chunk array into separate |
| // messages if we exceed ZX_CHANNEL_MAX_MSG_HANDLES. |
| |
| size_t msg_capacity = sizeof(fuchsia_process_LauncherAddHandlesRequest) + FIDL_ALIGN(handle_capacity * sizeof(fuchsia_process_HandleInfo)); |
| uint8_t msg[msg_capacity]; |
| memset(msg, 0, msg_capacity); |
| |
| fuchsia_process_LauncherAddHandlesRequest* req = (fuchsia_process_LauncherAddHandlesRequest*)msg; |
| fuchsia_process_HandleInfo* handle_infos = (fuchsia_process_HandleInfo*)(req + 1); |
| |
| zx_handle_t handles[handle_capacity]; |
| |
| memset(handles, 0, sizeof(handles)); |
| |
| req->hdr.ordinal = fuchsia_process_LauncherAddHandlesOrdinal; |
| |
| zx_status_t status = ZX_OK; |
| size_t h = 0; |
| size_t a = 0; |
| |
| if ((flags & FDIO_SPAWN_CLONE_JOB) != 0) { |
| handle_infos[h].handle = FIDL_HANDLE_PRESENT; |
| handle_infos[h].id = PA_JOB_DEFAULT; |
| status = zx_handle_duplicate(job, ZX_RIGHT_SAME_RIGHTS, &handles[h++]); |
| if (status != ZX_OK) { |
| report_error(err_msg, "failed to duplicate job: %d", status); |
| goto cleanup; |
| } |
| } |
| |
| if ((flags & FDIO_SPAWN_CLONE_LDSVC) != 0) { |
| handle_infos[h].handle = FIDL_HANDLE_PRESENT; |
| handle_infos[h].id = PA_SVC_LOADER; |
| status = dl_clone_loader_service(&handles[h++]); |
| if (status != ZX_OK) { |
| report_error(err_msg, "failed to clone library loader service: %d", status); |
| goto cleanup; |
| } |
| } |
| |
| if ((flags & FDIO_SPAWN_CLONE_STDIO) != 0) { |
| for (int fd = 0; fd < 3; ++fd) { |
| zx_handle_t fdio_handles[FDIO_MAX_HANDLES]; |
| uint32_t fdio_types[FDIO_MAX_HANDLES]; |
| status = fdio_clone_fd(fd, fd, fdio_handles, fdio_types); |
| if (status == ZX_ERR_BAD_HANDLE) { |
| // This file descriptor is closed. We just skip it rather than |
| // generating an error. |
| continue; |
| } |
| if (status < ZX_OK) { |
| report_error(err_msg, "failed to clone fd %d: %d", fd, status); |
| goto cleanup; |
| } |
| ZX_ASSERT((size_t)status <= FDIO_MAX_HANDLES_FOR_CLONE_OR_TRANSFER); |
| for (int i = 0; i < status; ++i) { |
| handle_infos[h].handle = FIDL_HANDLE_PRESENT; |
| handle_infos[h].id = fdio_types[i]; |
| handles[h++] = fdio_handles[i]; |
| } |
| } |
| } |
| |
| for ( ; a < action_count; ++a) { |
| zx_handle_t fdio_handles[FDIO_MAX_HANDLES]; |
| uint32_t fdio_types[FDIO_MAX_HANDLES]; |
| |
| switch (actions[a].action) { |
| case FDIO_SPAWN_ACTION_CLONE_FD: |
| status = fdio_clone_fd(actions[a].fd.local_fd, actions[a].fd.target_fd, fdio_handles, fdio_types); |
| if (status < ZX_OK) { |
| report_error(err_msg, "failed to clone fd %d (action index %zu): %d", actions[a].fd.local_fd, a, status); |
| goto cleanup; |
| } |
| break; |
| case FDIO_SPAWN_ACTION_TRANSFER_FD: |
| status = fdio_transfer_fd(actions[a].fd.local_fd, actions[a].fd.target_fd, fdio_handles, fdio_types); |
| if (status < ZX_OK) { |
| report_error(err_msg, "failed to transfer fd %d (action index %zu): %d", actions[a].fd.local_fd, a, status); |
| goto cleanup; |
| } |
| break; |
| case FDIO_SPAWN_ACTION_ADD_HANDLE: |
| handle_infos[h].handle = FIDL_HANDLE_PRESENT; |
| handle_infos[h].id = actions[a].h.id; |
| handles[h++] = actions[a].h.handle; |
| continue; |
| default: |
| continue; |
| } |
| |
| ZX_ASSERT((size_t)status <= FDIO_MAX_HANDLES_FOR_CLONE_OR_TRANSFER); |
| for (int j = 0; j < status; ++j) { |
| handle_infos[h].handle = FIDL_HANDLE_PRESENT; |
| handle_infos[h].id = fdio_types[j]; |
| handles[h++] = fdio_handles[j]; |
| } |
| } |
| |
| req->handles.count = h; |
| req->handles.data = (void*)FIDL_ALLOC_PRESENT; |
| |
| ZX_DEBUG_ASSERT(h <= handle_capacity); |
| |
| size_t msg_len = sizeof(fuchsia_process_LauncherAddHandlesRequest) + FIDL_ALIGN(h * sizeof(fuchsia_process_HandleInfo)); |
| status = zx_channel_write(launcher, 0, msg, msg_len, handles, h); |
| |
| if (status == ZX_OK) |
| return status; |
| |
| report_error(err_msg, "failed send handles: %d", status); |
| |
| cleanup: |
| for (size_t i = 0; i < h; ++i) { |
| zx_handle_close(handles[i]); |
| } |
| |
| // If |a| is less than |action_count|, that means we encountered an error |
| // before we processed all the actions. We need to iterate through the rest |
| // of the table and close the file descriptors and handles that we're |
| // supposed to consume. |
| for (size_t i = a; i < action_count; ++i) { |
| switch (actions[i].action) { |
| case FDIO_SPAWN_ACTION_TRANSFER_FD: |
| close(actions[i].fd.local_fd); |
| break; |
| case FDIO_SPAWN_ACTION_ADD_HANDLE: |
| zx_handle_close(actions[i].h.handle); |
| break; |
| } |
| } |
| |
| return status; |
| } |
| |
| static zx_status_t send_namespace(zx_handle_t launcher, size_t name_count, size_t name_len, |
| fdio_flat_namespace_t* flat, size_t action_count, |
| const fdio_spawn_action_t* actions, char* err_msg) { |
| size_t msg_len = sizeof(fuchsia_process_LauncherAddNamesRequest) + FIDL_ALIGN(name_count * sizeof(fuchsia_process_NameInfo)) + FIDL_ALIGN(name_len); |
| uint8_t msg[msg_len]; |
| memset(msg, 0, msg_len); |
| |
| fuchsia_process_LauncherAddNamesRequest* req = (fuchsia_process_LauncherAddNamesRequest*)msg; |
| fuchsia_process_NameInfo* names = (fuchsia_process_NameInfo*)(req + 1); |
| uint8_t* payload = (uint8_t*)(names + name_count); |
| |
| zx_handle_t handles[name_count]; |
| |
| memset(handles, 0, sizeof(handles)); |
| |
| req->hdr.ordinal = fuchsia_process_LauncherAddNamesOrdinal; |
| req->names.count = name_count; |
| req->names.data = (void*)FIDL_ALLOC_PRESENT; |
| |
| size_t n = 0; |
| size_t h = 0; |
| size_t offset = 0; |
| |
| if (flat) { |
| while (n < flat->count) { |
| size_t size = strlen(flat->path[n]); |
| names[n].path.size = size; |
| names[n].path.data = (void*)FIDL_ALLOC_PRESENT; |
| names[n].directory = FIDL_HANDLE_PRESENT; |
| memcpy(payload + offset, flat->path[n], size); |
| offset += FIDL_ALIGN(size); |
| handles[h++] = flat->handle[n]; |
| n++; |
| } |
| } |
| |
| for (size_t i = 0; i < action_count; ++i) { |
| if (actions[i].action == FDIO_SPAWN_ACTION_ADD_NS_ENTRY) { |
| size_t size = strlen(actions[i].ns.prefix); |
| names[n].path.size = size; |
| names[n].path.data = (void*)FIDL_ALLOC_PRESENT; |
| names[n].directory = FIDL_HANDLE_PRESENT; |
| memcpy(payload + offset, actions[i].ns.prefix, size); |
| offset += FIDL_ALIGN(size); |
| handles[h++] = actions[i].ns.handle; |
| n++; |
| } |
| } |
| |
| ZX_DEBUG_ASSERT(n == name_count); |
| ZX_DEBUG_ASSERT(h == name_count); |
| |
| zx_status_t status = zx_channel_write(launcher, 0, msg, msg_len, handles, h); |
| |
| if (status != ZX_OK) { |
| report_error(err_msg, "failed send namespace: %d", status); |
| |
| for (size_t i = 0; i < h; ++i) { |
| zx_handle_close(handles[i]); |
| } |
| } |
| |
| return status; |
| } |
| |
| zx_status_t fdio_spawn(zx_handle_t job, |
| uint32_t flags, |
| const char* path, |
| const char* const* argv, |
| zx_handle_t* process_out) { |
| return fdio_spawn_etc(job, flags, path, argv, NULL, 0, NULL, process_out, NULL); |
| } |
| |
| zx_status_t fdio_spawn_etc(zx_handle_t job, |
| uint32_t flags, |
| const char* path, |
| const char* const* argv, |
| const char* const* explicit_environ, |
| size_t action_count, |
| const fdio_spawn_action_t* actions, |
| zx_handle_t* process_out, |
| char* err_msg) { |
| zx_status_t status = ZX_OK; |
| fdio_flat_namespace_t* flat = NULL; |
| size_t name_count = 0; |
| size_t name_len = 0; |
| size_t handle_capacity = 0; |
| zx_handle_t vmo = ZX_HANDLE_INVALID; |
| zx_handle_t launcher = ZX_HANDLE_INVALID; |
| zx_handle_t launcher_request = ZX_HANDLE_INVALID; |
| zx_handle_t msg_handles[FDIO_SPAWN_LAUNCH_HANDLE_COUNT]; |
| |
| memset(msg_handles, 0, sizeof(msg_handles)); |
| |
| if (err_msg) |
| err_msg[0] = '\0'; |
| |
| if (!path || !argv || (action_count != 0 && !actions)) { |
| status = ZX_ERR_INVALID_ARGS; |
| goto cleanup; |
| } |
| |
| if (job == ZX_HANDLE_INVALID) |
| job = zx_job_default(); |
| |
| const char* process_name = argv[0]; |
| |
| for (size_t i = 0; i < action_count; ++i) { |
| switch (actions[i].action) { |
| case FDIO_SPAWN_ACTION_CLONE_FD: |
| case FDIO_SPAWN_ACTION_TRANSFER_FD: |
| handle_capacity += FDIO_MAX_HANDLES_FOR_CLONE_OR_TRANSFER; |
| break; |
| case FDIO_SPAWN_ACTION_ADD_NS_ENTRY: |
| if (!actions[i].ns.handle || !actions[i].ns.prefix) { |
| status = ZX_ERR_INVALID_ARGS; |
| goto cleanup; |
| } |
| ++name_count; |
| name_len += FIDL_ALIGN(strlen(actions[i].ns.prefix)); |
| break; |
| case FDIO_SPAWN_ACTION_ADD_HANDLE: |
| if (!actions[i].h.handle) { |
| status = ZX_ERR_INVALID_ARGS; |
| goto cleanup; |
| } |
| ++handle_capacity; |
| break; |
| case FDIO_SPAWN_ACTION_SET_NAME: |
| if (!actions[i].name.data) { |
| status = ZX_ERR_INVALID_ARGS; |
| goto cleanup; |
| } |
| process_name = actions[i].name.data; |
| break; |
| default: |
| break; |
| } |
| } |
| |
| if (!process_name) { |
| status = ZX_ERR_INVALID_ARGS; |
| goto cleanup; |
| } |
| |
| if ((flags & FDIO_SPAWN_CLONE_JOB) != 0) |
| ++handle_capacity; |
| |
| if ((flags & FDIO_SPAWN_CLONE_LDSVC) != 0) |
| ++handle_capacity; |
| |
| if ((flags & FDIO_SPAWN_CLONE_STDIO) != 0) |
| handle_capacity += 3 * FDIO_MAX_HANDLES_FOR_CLONE_OR_TRANSFER; |
| |
| if ((flags & FDIO_SPAWN_CLONE_NAMESPACE) != 0) { |
| status = fdio_ns_export_root(&flat); |
| name_count += flat->count; |
| for (size_t i = 0; i < flat->count; ++i) { |
| name_len += FIDL_ALIGN(strlen(flat->path[i])); |
| } |
| } |
| |
| status = load_path(path, &vmo); |
| if (status != ZX_OK) { |
| report_error(err_msg, "failed to load executable from %s", path); |
| goto cleanup; |
| } |
| |
| status = zx_channel_create(0, &launcher, &launcher_request); |
| if (status != ZX_OK) { |
| report_error(err_msg, "failed to create channel for process launcher: %d", status); |
| goto cleanup; |
| } |
| |
| status = fdio_service_connect("/svc/fuchsia.process.Launcher", launcher_request); |
| launcher_request = ZX_HANDLE_INVALID; |
| if (status != ZX_OK) { |
| report_error(err_msg, "failed to connect to launcher service: %d", status); |
| goto cleanup; |
| } |
| |
| status = send_string_array(launcher, fuchsia_process_LauncherAddArgsOrdinal, argv); |
| if (status != ZX_OK) { |
| report_error(err_msg, "failed to send argument vector: %d", status); |
| goto cleanup; |
| } |
| |
| if (explicit_environ) { |
| status = send_string_array(launcher, fuchsia_process_LauncherAddEnvironsOrdinal, explicit_environ); |
| if (status != ZX_OK) { |
| report_error(err_msg, "failed to send environment: %d", status); |
| goto cleanup; |
| } |
| } else if ((flags & FDIO_SPAWN_CLONE_ENVIRON) != 0) { |
| status = send_string_array(launcher, fuchsia_process_LauncherAddEnvironsOrdinal, (const char* const*)environ); |
| if (status != ZX_OK) { |
| report_error(err_msg, "failed to send environment clone with FDIO_SPAWN_CLONE_ENVIRON: %d", status); |
| goto cleanup; |
| } |
| } |
| |
| if (handle_capacity) { |
| status = send_handles(launcher, handle_capacity, flags, job, action_count, actions, err_msg); |
| if (status != ZX_OK) { |
| // When |send_handles| fails, it consumes all the action handles |
| // that it knows about, but it doesn't consume the handles used for |
| // |FDIO_SPAWN_ACTION_ADD_NS_ENTRY|. |
| |
| for (size_t i = 0; i < action_count; ++i) { |
| switch (actions[i].action) { |
| case FDIO_SPAWN_ACTION_ADD_NS_ENTRY: |
| zx_handle_close(actions[i].ns.handle); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| action_count = 0; // We've now consumed all the handles. |
| goto cleanup; |
| } |
| } |
| |
| if (name_count) { |
| status = send_namespace(launcher, name_count, name_len, flat, action_count, actions, err_msg); |
| if (status != ZX_OK) { |
| action_count = 0; |
| goto cleanup; |
| } |
| } |
| |
| action_count = 0; // We've consumed all the actions at this point. |
| |
| size_t process_name_size = strlen(process_name); |
| if (process_name_size >= ZX_MAX_NAME_LEN) |
| process_name_size = ZX_MAX_NAME_LEN - 1; |
| |
| { |
| |
| struct { |
| FIDL_ALIGNDECL |
| fuchsia_process_LauncherLaunchRequest req; |
| uint8_t process_name[FIDL_ALIGN(ZX_MAX_NAME_LEN)]; |
| } msg; |
| |
| memset(&msg, 0, sizeof(msg)); |
| size_t msg_len = sizeof(fuchsia_process_LauncherLaunchRequest) + FIDL_ALIGN(process_name_size); |
| |
| msg.req.hdr.ordinal = fuchsia_process_LauncherLaunchOrdinal; |
| msg.req.info.executable = FIDL_HANDLE_PRESENT; |
| msg.req.info.job = FIDL_HANDLE_PRESENT; |
| msg.req.info.name.size = process_name_size; |
| msg.req.info.name.data = (void*)FIDL_ALLOC_PRESENT; |
| memcpy(msg.process_name, process_name, process_name_size); |
| |
| msg_handles[FDIO_SPAWN_LAUNCH_HANDLE_EXECUTABLE] = vmo; |
| vmo = ZX_HANDLE_INVALID; |
| |
| status = zx_handle_duplicate(job, ZX_RIGHT_SAME_RIGHTS, &msg_handles[FDIO_SPAWN_LAUNCH_HANDLE_JOB]); |
| if (status != ZX_OK) { |
| report_error(err_msg, "failed to duplicate job handle: %d", status); |
| goto cleanup; |
| } |
| |
| struct { |
| FIDL_ALIGNDECL |
| fuchsia_process_LauncherLaunchResponse rsp; |
| uint8_t err_msg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH]; |
| } reply; |
| |
| zx_handle_t process = ZX_HANDLE_INVALID; |
| |
| memset(&reply, 0, sizeof(reply)); |
| |
| zx_channel_call_args_t args; |
| args.wr_bytes = &msg; |
| args.wr_handles = msg_handles; |
| args.rd_bytes = &reply; |
| args.rd_handles = &process; |
| args.wr_num_bytes = msg_len; |
| args.wr_num_handles = FDIO_SPAWN_LAUNCH_HANDLE_COUNT; |
| args.rd_num_bytes = sizeof(reply); |
| args.rd_num_handles = FDIO_SPAWN_LAUNCH_REPLY_HANDLE_COUNT; |
| |
| uint32_t actual_bytes = 0; |
| uint32_t actual_handles = 0; |
| zx_status_t read_status = ZX_OK; |
| |
| status = zx_channel_call(launcher, 0, ZX_TIME_INFINITE, &args, |
| &actual_bytes, &actual_handles, &read_status); |
| |
| if (status == ZX_OK || status == ZX_ERR_CALL_FAILED) |
| memset(msg_handles, 0, sizeof(msg_handles)); |
| |
| if (status != ZX_OK) { |
| report_error(err_msg, "failed to send launch message: %d", status); |
| goto cleanup; |
| } |
| |
| status = reply.rsp.result.status; |
| |
| if (status == ZX_OK) { |
| // The launcher claimed to succeed but didn't actually give us a |
| // process handle. Something is wrong with the launcher. |
| if (process == ZX_HANDLE_INVALID) { |
| status = ZX_ERR_BAD_HANDLE; |
| report_error(err_msg, "failed receive process handle"); |
| // This jump skips over closing the process handle, but that's |
| // fine because we didn't receive a process handle. |
| goto cleanup; |
| } |
| |
| if (process_out) { |
| *process_out = process; |
| process = ZX_HANDLE_INVALID; |
| } |
| } else { |
| if (err_msg) { |
| size_t n = reply.rsp.result.error_message.size; |
| if (n >= FDIO_SPAWN_ERR_MSG_MAX_LENGTH) |
| n = FDIO_SPAWN_ERR_MSG_MAX_LENGTH - 1; |
| memcpy(err_msg, reply.err_msg, n); |
| err_msg[n] = '\0'; |
| } |
| } |
| |
| if (process != ZX_HANDLE_INVALID) |
| zx_handle_close(process); |
| } |
| |
| cleanup: |
| if (actions) { |
| for (size_t i = 0; i < action_count; ++i) { |
| switch (actions[i].action) { |
| case FDIO_SPAWN_ACTION_ADD_NS_ENTRY: |
| zx_handle_close(actions[i].ns.handle); |
| break; |
| case FDIO_SPAWN_ACTION_ADD_HANDLE: |
| zx_handle_close(actions[i].h.handle); |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| |
| if (msg_handles[FDIO_SPAWN_LAUNCH_HANDLE_EXECUTABLE] != ZX_HANDLE_INVALID) |
| zx_handle_close(msg_handles[FDIO_SPAWN_LAUNCH_HANDLE_EXECUTABLE]); |
| |
| if (msg_handles[FDIO_SPAWN_LAUNCH_HANDLE_JOB] != ZX_HANDLE_INVALID) |
| zx_handle_close(msg_handles[FDIO_SPAWN_LAUNCH_HANDLE_JOB]); |
| |
| return status; |
| } |