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 <lib/fdio/spawn.h>
6
7#include <fcntl.h>
8#include <fuchsia/process/c/fidl.h>
9#include <lib/fdio/io.h>
10#include <lib/fdio/limits.h>
11#include <lib/fdio/namespace.h>
12#include <lib/fdio/util.h>
13#include <stdarg.h>
14#include <stdio.h>
15#include <string.h>
16#include <unistd.h>
17#include <zircon/assert.h>
18#include <zircon/dlfcn.h>
19#include <zircon/process.h>
20#include <zircon/processargs.h>
21#include <zircon/syscalls.h>
22
23#define FDIO_SPAWN_LAUNCH_HANDLE_EXECUTABLE ((size_t)0u)
24#define FDIO_SPAWN_LAUNCH_HANDLE_JOB ((size_t)1u)
25#define FDIO_SPAWN_LAUNCH_HANDLE_COUNT ((size_t)2u)
26
27#define FDIO_SPAWN_LAUNCH_REPLY_HANDLE_COUNT ((size_t)1u)
28
29// Even though FDIO_MAX_HANDLES is 3, the clone and transfer operations can only
30// ever generate 2 handles.
31#define FDIO_MAX_HANDLES_FOR_CLONE_OR_TRANSFER ((size_t)2u)
32
33// The fdio_spawn_action_t is replicated in various ffi interfaces, including
34// the rust and golang standard libraries.
35static_assert(sizeof(fdio_spawn_action_t) == 24,
36              "fdio_spawn_action_t must have a stable ABI");
37static_assert(offsetof(fdio_spawn_action_t, action) == 0,
38              "fdio_spawn_action_t must have a stable ABI");
39static_assert(offsetof(fdio_spawn_action_t, fd) == 8,
40              "fdio_spawn_action_t must have a stable ABI");
41static_assert(offsetof(fdio_spawn_action_t, fd.local_fd) == 8,
42              "fdio_spawn_action_t must have a stable ABI");
43static_assert(offsetof(fdio_spawn_action_t, fd.target_fd) == 12,
44              "fdio_spawn_action_t must have a stable ABI");
45static_assert(offsetof(fdio_spawn_action_t, ns) == 8,
46              "fdio_spawn_action_t must have a stable ABI");
47static_assert(offsetof(fdio_spawn_action_t, ns.prefix) == 8,
48              "fdio_spawn_action_t must have a stable ABI");
49static_assert(offsetof(fdio_spawn_action_t, ns.handle) == 16,
50              "fdio_spawn_action_t must have a stable ABI");
51static_assert(offsetof(fdio_spawn_action_t, h) == 8,
52              "fdio_spawn_action_t must have a stable ABI");
53static_assert(offsetof(fdio_spawn_action_t, h.id) == 8,
54              "fdio_spawn_action_t must have a stable ABI");
55static_assert(offsetof(fdio_spawn_action_t, h.handle) == 12,
56              "fdio_spawn_action_t must have a stable ABI");
57static_assert(offsetof(fdio_spawn_action_t, name) == 8,
58              "fdio_spawn_action_t must have a stable ABI");
59static_assert(offsetof(fdio_spawn_action_t, name.data) == 8,
60              "fdio_spawn_action_t must have a stable ABI");
61
62static zx_status_t load_path(const char* path, zx_handle_t* vmo) {
63    int fd = open(path, O_RDONLY);
64    if (fd < 0)
65        return ZX_ERR_IO;
66    zx_status_t status = fdio_get_vmo_clone(fd, vmo);
67    close(fd);
68
69    if (status == ZX_OK) {
70        if (strlen(path) >= ZX_MAX_NAME_LEN) {
71            const char* p = strrchr(path, '/');
72            if (p != NULL) {
73                path = p + 1;
74            }
75        }
76
77        zx_object_set_property(*vmo, ZX_PROP_NAME, path, strlen(path));
78    }
79
80    return status;
81}
82
83static void measure_string_array(const char* const* array, size_t* count_out, size_t* len_out) {
84    size_t i = 0;
85    size_t len = 0;
86    while (array[i]) {
87        len += FIDL_ALIGN(strlen(array[i]));
88        ++i;
89    }
90    *count_out = i;
91    *len_out = len;
92}
93
94static void report_error(char* err_msg, const char* format, ...) {
95    if (!err_msg)
96        return;
97    va_list args;
98    va_start(args, format);
99    vsnprintf(err_msg, FDIO_SPAWN_ERR_MSG_MAX_LENGTH, format, args);
100    va_end(args);
101}
102
103static zx_status_t send_string_array(zx_handle_t launcher, int ordinal, const char* const* array) {
104    size_t count = 0;
105    size_t len = 0;
106
107    // TODO(abarth): In principle, we should chunk array into separate
108    // messages if we exceed ZX_CHANNEL_MAX_MSG_BYTES.
109    measure_string_array(array, &count, &len);
110
111    if (count == 0)
112        return ZX_OK;
113
114    size_t msg_len = sizeof(fidl_message_header_t) + sizeof(fidl_vector_t) + count * sizeof(fidl_string_t) + FIDL_ALIGN(len);
115    uint8_t msg[msg_len];
116    memset(msg, 0, msg_len);
117
118    fidl_message_header_t* hdr = (fidl_message_header_t*)msg;
119    fidl_vector_t* vector = (fidl_vector_t*)hdr + 1;
120    fidl_string_t* strings = (fidl_string_t*)(vector + 1);
121    uint8_t* payload = (uint8_t*)(strings + count);
122
123    hdr->ordinal = ordinal;
124    vector->count = count;
125    vector->data = (void*)FIDL_ALLOC_PRESENT;
126
127    size_t offset = 0;
128    for (size_t i = 0; i < count; ++i) {
129        size_t size = strlen(array[i]);
130        strings[i].size = size;
131        strings[i].data = (void*)FIDL_ALLOC_PRESENT;
132        memcpy(payload + offset, array[i], size);
133        offset += FIDL_ALIGN(size);
134    }
135
136    return zx_channel_write(launcher, 0, msg, msg_len, NULL, 0);
137}
138
139static zx_status_t send_handles(zx_handle_t launcher, size_t handle_capacity,
140                                uint32_t flags, zx_handle_t job, size_t action_count,
141                                const fdio_spawn_action_t* actions, char* err_msg) {
142    // TODO(abarth): In principle, we should chunk array into separate
143    // messages if we exceed ZX_CHANNEL_MAX_MSG_HANDLES.
144
145    size_t msg_capacity = sizeof(fuchsia_process_LauncherAddHandlesRequest) + FIDL_ALIGN(handle_capacity * sizeof(fuchsia_process_HandleInfo));
146    uint8_t msg[msg_capacity];
147    memset(msg, 0, msg_capacity);
148
149    fuchsia_process_LauncherAddHandlesRequest* req = (fuchsia_process_LauncherAddHandlesRequest*)msg;
150    fuchsia_process_HandleInfo* handle_infos = (fuchsia_process_HandleInfo*)(req + 1);
151
152    zx_handle_t handles[handle_capacity];
153
154    memset(handles, 0, sizeof(handles));
155
156    req->hdr.ordinal = fuchsia_process_LauncherAddHandlesOrdinal;
157
158    zx_status_t status = ZX_OK;
159    size_t h = 0;
160    size_t a = 0;
161
162    if ((flags & FDIO_SPAWN_CLONE_JOB) != 0) {
163        handle_infos[h].handle = FIDL_HANDLE_PRESENT;
164        handle_infos[h].id = PA_JOB_DEFAULT;
165        status = zx_handle_duplicate(job, ZX_RIGHT_SAME_RIGHTS, &handles[h++]);
166        if (status != ZX_OK) {
167            report_error(err_msg, "failed to duplicate job: %d", status);
168            goto cleanup;
169        }
170    }
171
172    if ((flags & FDIO_SPAWN_CLONE_LDSVC) != 0) {
173        handle_infos[h].handle = FIDL_HANDLE_PRESENT;
174        handle_infos[h].id = PA_LDSVC_LOADER;
175        status = dl_clone_loader_service(&handles[h++]);
176        if (status != ZX_OK) {
177            report_error(err_msg, "failed to clone library loader service: %d", status);
178            goto cleanup;
179        }
180    }
181
182    if ((flags & FDIO_SPAWN_CLONE_STDIO) != 0) {
183        for (int fd = 0; fd < 3; ++fd) {
184            zx_handle_t fdio_handles[FDIO_MAX_HANDLES];
185            uint32_t fdio_types[FDIO_MAX_HANDLES];
186            status = fdio_clone_fd(fd, fd, fdio_handles, fdio_types);
187            if (status == ZX_ERR_BAD_HANDLE) {
188                // This file descriptor is closed. We just skip it rather than
189                // generating an error.
190                continue;
191            }
192            if (status < ZX_OK) {
193                report_error(err_msg, "failed to clone fd %d: %d", fd, status);
194                goto cleanup;
195            }
196            ZX_ASSERT((size_t)status <= FDIO_MAX_HANDLES_FOR_CLONE_OR_TRANSFER);
197            for (int i = 0; i < status; ++i) {
198                handle_infos[h].handle = FIDL_HANDLE_PRESENT;
199                handle_infos[h].id = fdio_types[i];
200                handles[h++] = fdio_handles[i];
201            }
202        }
203    }
204
205    for (; a < action_count; ++a) {
206        zx_handle_t fdio_handles[FDIO_MAX_HANDLES];
207        uint32_t fdio_types[FDIO_MAX_HANDLES];
208
209        switch (actions[a].action) {
210        case FDIO_SPAWN_ACTION_CLONE_FD:
211            status = fdio_clone_fd(actions[a].fd.local_fd, actions[a].fd.target_fd, fdio_handles, fdio_types);
212            if (status < ZX_OK) {
213                report_error(err_msg, "failed to clone fd %d (action index %zu): %d", actions[a].fd.local_fd, a, status);
214                goto cleanup;
215            }
216            break;
217        case FDIO_SPAWN_ACTION_TRANSFER_FD:
218            status = fdio_transfer_fd(actions[a].fd.local_fd, actions[a].fd.target_fd, fdio_handles, fdio_types);
219            if (status < ZX_OK) {
220                report_error(err_msg, "failed to transfer fd %d (action index %zu): %d", actions[a].fd.local_fd, a, status);
221                goto cleanup;
222            }
223            break;
224        case FDIO_SPAWN_ACTION_ADD_HANDLE:
225            handle_infos[h].handle = FIDL_HANDLE_PRESENT;
226            handle_infos[h].id = actions[a].h.id;
227            handles[h++] = actions[a].h.handle;
228            continue;
229        default:
230            continue;
231        }
232
233        ZX_ASSERT((size_t)status <= FDIO_MAX_HANDLES_FOR_CLONE_OR_TRANSFER);
234        for (int j = 0; j < status; ++j) {
235            handle_infos[h].handle = FIDL_HANDLE_PRESENT;
236            handle_infos[h].id = fdio_types[j];
237            handles[h++] = fdio_handles[j];
238        }
239    }
240
241    req->handles.count = h;
242    req->handles.data = (void*)FIDL_ALLOC_PRESENT;
243
244    ZX_DEBUG_ASSERT(h <= handle_capacity);
245
246    size_t msg_len = sizeof(fuchsia_process_LauncherAddHandlesRequest) + FIDL_ALIGN(h * sizeof(fuchsia_process_HandleInfo));
247    status = zx_channel_write(launcher, 0, msg, msg_len, handles, h);
248
249    if (status != ZX_OK)
250        report_error(err_msg, "failed send handles: %d", status);
251
252    return status;
253
254cleanup:
255    zx_handle_close_many(handles, h);
256
257    // If |a| is less than |action_count|, that means we encountered an error
258    // before we processed all the actions. We need to iterate through the rest
259    // of the table and close the file descriptors and handles that we're
260    // supposed to consume.
261    for (size_t i = a; i < action_count; ++i) {
262        switch (actions[i].action) {
263        case FDIO_SPAWN_ACTION_TRANSFER_FD:
264            close(actions[i].fd.local_fd);
265            break;
266        case FDIO_SPAWN_ACTION_ADD_HANDLE:
267            zx_handle_close(actions[i].h.handle);
268            break;
269        }
270    }
271
272    return status;
273}
274
275static zx_status_t send_namespace(zx_handle_t launcher, size_t name_count, size_t name_len,
276                                  fdio_flat_namespace_t* flat, size_t action_count,
277                                  const fdio_spawn_action_t* actions, char* err_msg) {
278    size_t msg_len = sizeof(fuchsia_process_LauncherAddNamesRequest) + FIDL_ALIGN(name_count * sizeof(fuchsia_process_NameInfo)) + FIDL_ALIGN(name_len);
279    uint8_t msg[msg_len];
280    memset(msg, 0, msg_len);
281
282    fuchsia_process_LauncherAddNamesRequest* req = (fuchsia_process_LauncherAddNamesRequest*)msg;
283    fuchsia_process_NameInfo* names = (fuchsia_process_NameInfo*)(req + 1);
284    uint8_t* payload = (uint8_t*)(names + name_count);
285
286    zx_handle_t handles[name_count];
287
288    memset(handles, 0, sizeof(handles));
289
290    req->hdr.ordinal = fuchsia_process_LauncherAddNamesOrdinal;
291    req->names.count = name_count;
292    req->names.data = (void*)FIDL_ALLOC_PRESENT;
293
294    size_t n = 0;
295    size_t h = 0;
296    size_t offset = 0;
297
298    if (flat) {
299        while (n < flat->count) {
300            size_t size = strlen(flat->path[n]);
301            names[n].path.size = size;
302            names[n].path.data = (void*)FIDL_ALLOC_PRESENT;
303            names[n].directory = FIDL_HANDLE_PRESENT;
304            memcpy(payload + offset, flat->path[n], size);
305            offset += FIDL_ALIGN(size);
306            handles[h++] = flat->handle[n];
307            n++;
308        }
309    }
310
311    for (size_t i = 0; i < action_count; ++i) {
312        if (actions[i].action == FDIO_SPAWN_ACTION_ADD_NS_ENTRY) {
313            size_t size = strlen(actions[i].ns.prefix);
314            names[n].path.size = size;
315            names[n].path.data = (void*)FIDL_ALLOC_PRESENT;
316            names[n].directory = FIDL_HANDLE_PRESENT;
317            memcpy(payload + offset, actions[i].ns.prefix, size);
318            offset += FIDL_ALIGN(size);
319            handles[h++] = actions[i].ns.handle;
320            n++;
321        }
322    }
323
324    ZX_DEBUG_ASSERT(n == name_count);
325    ZX_DEBUG_ASSERT(h == name_count);
326
327    zx_status_t status = zx_channel_write(launcher, 0, msg, msg_len, handles, h);
328
329    if (status != ZX_OK)
330        report_error(err_msg, "failed send namespace: %d", status);
331
332    return status;
333}
334
335__EXPORT
336zx_status_t fdio_spawn(zx_handle_t job,
337                       uint32_t flags,
338                       const char* path,
339                       const char* const* argv,
340                       zx_handle_t* process_out) {
341    return fdio_spawn_etc(job, flags, path, argv, NULL, 0, NULL, process_out, NULL);
342}
343
344__EXPORT
345zx_status_t fdio_spawn_etc(zx_handle_t job,
346                           uint32_t flags,
347                           const char* path,
348                           const char* const* argv,
349                           const char* const* explicit_environ,
350                           size_t action_count,
351                           const fdio_spawn_action_t* actions,
352                           zx_handle_t* process_out,
353                           char* err_msg) {
354    zx_handle_t executable_vmo = ZX_HANDLE_INVALID;
355
356    zx_status_t status = load_path(path, &executable_vmo);
357    if (status != ZX_OK) {
358        report_error(err_msg, "failed to load executable from %s", path);
359        // Set |err_msg| to NULL to prevent |fdio_spawn_vmo| from generating
360        // a less useful error message.
361        err_msg = NULL;
362    }
363
364    // Always call fdio_spawn_vmo to clean up arguments. If |executable_vmo| is
365    // |ZX_HANDLE_INVALID|, then |fdio_spawn_vmo| will generate an error.
366    zx_status_t spawn_status = fdio_spawn_vmo(job, flags, executable_vmo, argv, explicit_environ,
367                                              action_count, actions, process_out, err_msg);
368
369    // Use |status| if we already had an error before calling |fdio_spawn_vmo|.
370    // Otherwise, we'll always return |ZX_ERR_INVALID_ARGS| rather than the more
371    // useful status from |load_path|.
372    return status != ZX_OK ? status : spawn_status;
373}
374
375__EXPORT
376zx_status_t fdio_spawn_vmo(zx_handle_t job,
377                           uint32_t flags,
378                           zx_handle_t executable_vmo,
379                           const char* const* argv,
380                           const char* const* explicit_environ,
381                           size_t action_count,
382                           const fdio_spawn_action_t* actions,
383                           zx_handle_t* process_out,
384                           char* err_msg) {
385    zx_status_t status = ZX_OK;
386    fdio_flat_namespace_t* flat = NULL;
387    size_t name_count = 0;
388    size_t name_len = 0;
389    size_t handle_capacity = 0;
390    zx_handle_t launcher = ZX_HANDLE_INVALID;
391    zx_handle_t launcher_request = ZX_HANDLE_INVALID;
392    zx_handle_t msg_handles[FDIO_SPAWN_LAUNCH_HANDLE_COUNT];
393
394    memset(msg_handles, 0, sizeof(msg_handles));
395
396    if (err_msg)
397        err_msg[0] = '\0';
398
399    if (executable_vmo == ZX_HANDLE_INVALID || !argv || (action_count != 0 && !actions)) {
400        status = ZX_ERR_INVALID_ARGS;
401        goto cleanup;
402    }
403
404    if (job == ZX_HANDLE_INVALID)
405        job = zx_job_default();
406
407    const char* process_name = argv[0];
408
409    for (size_t i = 0; i < action_count; ++i) {
410        switch (actions[i].action) {
411        case FDIO_SPAWN_ACTION_CLONE_FD:
412        case FDIO_SPAWN_ACTION_TRANSFER_FD:
413            handle_capacity += FDIO_MAX_HANDLES_FOR_CLONE_OR_TRANSFER;
414            break;
415        case FDIO_SPAWN_ACTION_ADD_NS_ENTRY:
416            if (actions[i].ns.handle == ZX_HANDLE_INVALID || !actions[i].ns.prefix) {
417                status = ZX_ERR_INVALID_ARGS;
418                goto cleanup;
419            }
420            ++name_count;
421            name_len += FIDL_ALIGN(strlen(actions[i].ns.prefix));
422            break;
423        case FDIO_SPAWN_ACTION_ADD_HANDLE:
424            if (actions[i].h.handle == ZX_HANDLE_INVALID) {
425                status = ZX_ERR_INVALID_ARGS;
426                goto cleanup;
427            }
428            ++handle_capacity;
429            break;
430        case FDIO_SPAWN_ACTION_SET_NAME:
431            if (actions[i].name.data == NULL) {
432                status = ZX_ERR_INVALID_ARGS;
433                goto cleanup;
434            }
435            process_name = actions[i].name.data;
436            break;
437        default:
438            break;
439        }
440    }
441
442    if (!process_name) {
443        status = ZX_ERR_INVALID_ARGS;
444        goto cleanup;
445    }
446
447    if ((flags & FDIO_SPAWN_CLONE_JOB) != 0)
448        ++handle_capacity;
449
450    if ((flags & FDIO_SPAWN_CLONE_LDSVC) != 0)
451        ++handle_capacity;
452
453    if ((flags & FDIO_SPAWN_CLONE_STDIO) != 0)
454        handle_capacity += 3 * FDIO_MAX_HANDLES_FOR_CLONE_OR_TRANSFER;
455
456    if ((flags & FDIO_SPAWN_CLONE_NAMESPACE) != 0) {
457        status = fdio_ns_export_root(&flat);
458        name_count += flat->count;
459        for (size_t i = 0; i < flat->count; ++i) {
460            name_len += FIDL_ALIGN(strlen(flat->path[i]));
461        }
462    }
463
464    status = zx_channel_create(0, &launcher, &launcher_request);
465    if (status != ZX_OK) {
466        report_error(err_msg, "failed to create channel for process launcher: %d", status);
467        goto cleanup;
468    }
469
470    status = fdio_service_connect("/svc/fuchsia.process.Launcher", launcher_request);
471    launcher_request = ZX_HANDLE_INVALID;
472    if (status != ZX_OK) {
473        report_error(err_msg, "failed to connect to launcher service: %d", status);
474        goto cleanup;
475    }
476
477    status = send_string_array(launcher, fuchsia_process_LauncherAddArgsOrdinal, argv);
478    if (status != ZX_OK) {
479        report_error(err_msg, "failed to send argument vector: %d", status);
480        goto cleanup;
481    }
482
483    if (explicit_environ) {
484        status = send_string_array(launcher, fuchsia_process_LauncherAddEnvironsOrdinal, explicit_environ);
485        if (status != ZX_OK) {
486            report_error(err_msg, "failed to send environment: %d", status);
487            goto cleanup;
488        }
489    } else if ((flags & FDIO_SPAWN_CLONE_ENVIRON) != 0) {
490        status = send_string_array(launcher, fuchsia_process_LauncherAddEnvironsOrdinal, (const char* const*)environ);
491        if (status != ZX_OK) {
492            report_error(err_msg, "failed to send environment clone with FDIO_SPAWN_CLONE_ENVIRON: %d", status);
493            goto cleanup;
494        }
495    }
496
497    if (handle_capacity) {
498        status = send_handles(launcher, handle_capacity, flags, job, action_count, actions, err_msg);
499        if (status != ZX_OK) {
500            // When |send_handles| fails, it consumes all the action handles
501            // that it knows about, but it doesn't consume the handles used for
502            // |FDIO_SPAWN_ACTION_ADD_NS_ENTRY|.
503
504            for (size_t i = 0; i < action_count; ++i) {
505                switch (actions[i].action) {
506                case FDIO_SPAWN_ACTION_ADD_NS_ENTRY:
507                    zx_handle_close(actions[i].ns.handle);
508                    break;
509                default:
510                    break;
511                }
512            }
513
514            action_count = 0; // We've now consumed all the handles.
515            goto cleanup;
516        }
517    }
518
519    if (name_count) {
520        status = send_namespace(launcher, name_count, name_len, flat, action_count, actions, err_msg);
521        if (status != ZX_OK) {
522            action_count = 0;
523            goto cleanup;
524        }
525    }
526
527    action_count = 0; // We've consumed all the actions at this point.
528
529    size_t process_name_size = strlen(process_name);
530    if (process_name_size >= ZX_MAX_NAME_LEN)
531        process_name_size = ZX_MAX_NAME_LEN - 1;
532
533    {
534
535        struct {
536            FIDL_ALIGNDECL
537            fuchsia_process_LauncherLaunchRequest req;
538            uint8_t process_name[FIDL_ALIGN(ZX_MAX_NAME_LEN)];
539        } msg;
540
541        memset(&msg, 0, sizeof(msg));
542        size_t msg_len = sizeof(fuchsia_process_LauncherLaunchRequest) + FIDL_ALIGN(process_name_size);
543
544        msg.req.hdr.ordinal = fuchsia_process_LauncherLaunchOrdinal;
545        msg.req.info.executable = FIDL_HANDLE_PRESENT;
546        msg.req.info.job = FIDL_HANDLE_PRESENT;
547        msg.req.info.name.size = process_name_size;
548        msg.req.info.name.data = (void*)FIDL_ALLOC_PRESENT;
549        memcpy(msg.process_name, process_name, process_name_size);
550
551        msg_handles[FDIO_SPAWN_LAUNCH_HANDLE_EXECUTABLE] = executable_vmo;
552        executable_vmo = ZX_HANDLE_INVALID;
553
554        status = zx_handle_duplicate(job, ZX_RIGHT_SAME_RIGHTS, &msg_handles[FDIO_SPAWN_LAUNCH_HANDLE_JOB]);
555        if (status != ZX_OK) {
556            report_error(err_msg, "failed to duplicate job handle: %d", status);
557            goto cleanup;
558        }
559
560        struct {
561            FIDL_ALIGNDECL
562            fuchsia_process_LauncherLaunchResponse rsp;
563            uint8_t err_msg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH];
564        } reply;
565
566        zx_handle_t process = ZX_HANDLE_INVALID;
567
568        memset(&reply, 0, sizeof(reply));
569
570        zx_channel_call_args_t args;
571        args.wr_bytes = &msg;
572        args.wr_handles = msg_handles;
573        args.rd_bytes = &reply;
574        args.rd_handles = &process;
575        args.wr_num_bytes = msg_len;
576        args.wr_num_handles = FDIO_SPAWN_LAUNCH_HANDLE_COUNT;
577        args.rd_num_bytes = sizeof(reply);
578        args.rd_num_handles = FDIO_SPAWN_LAUNCH_REPLY_HANDLE_COUNT;
579
580        uint32_t actual_bytes = 0;
581        uint32_t actual_handles = 0;
582
583        status = zx_channel_call(launcher, 0, ZX_TIME_INFINITE, &args,
584                                 &actual_bytes, &actual_handles);
585
586        // zx_channel_call always consumes handles.
587        memset(msg_handles, 0, sizeof(msg_handles));
588
589        if (status != ZX_OK) {
590            report_error(err_msg, "failed to send launch message: %d", status);
591            goto cleanup;
592        }
593
594        status = reply.rsp.result.status;
595
596        if (status == ZX_OK) {
597            // The launcher claimed to succeed but didn't actually give us a
598            // process handle. Something is wrong with the launcher.
599            if (process == ZX_HANDLE_INVALID) {
600                status = ZX_ERR_BAD_HANDLE;
601                report_error(err_msg, "failed receive process handle");
602                // This jump skips over closing the process handle, but that's
603                // fine because we didn't receive a process handle.
604                goto cleanup;
605            }
606
607            if (process_out) {
608                *process_out = process;
609                process = ZX_HANDLE_INVALID;
610            }
611        } else {
612            if (err_msg) {
613                size_t n = reply.rsp.result.error_message.size;
614                if (n >= FDIO_SPAWN_ERR_MSG_MAX_LENGTH)
615                    n = FDIO_SPAWN_ERR_MSG_MAX_LENGTH - 1;
616                memcpy(err_msg, reply.err_msg, n);
617                err_msg[n] = '\0';
618            }
619        }
620
621        if (process != ZX_HANDLE_INVALID)
622            zx_handle_close(process);
623    }
624
625cleanup:
626    if (actions) {
627        for (size_t i = 0; i < action_count; ++i) {
628            switch (actions[i].action) {
629            case FDIO_SPAWN_ACTION_ADD_NS_ENTRY:
630                zx_handle_close(actions[i].ns.handle);
631                break;
632            case FDIO_SPAWN_ACTION_ADD_HANDLE:
633                zx_handle_close(actions[i].h.handle);
634                break;
635            default:
636                break;
637            }
638        }
639    }
640
641    free(flat);
642
643    if (executable_vmo != ZX_HANDLE_INVALID)
644        zx_handle_close(executable_vmo);
645
646    if (launcher != ZX_HANDLE_INVALID)
647        zx_handle_close(launcher);
648
649    if (launcher_request != ZX_HANDLE_INVALID)
650        zx_handle_close(launcher_request);
651
652    if (msg_handles[FDIO_SPAWN_LAUNCH_HANDLE_EXECUTABLE] != ZX_HANDLE_INVALID)
653        zx_handle_close(msg_handles[FDIO_SPAWN_LAUNCH_HANDLE_EXECUTABLE]);
654
655    if (msg_handles[FDIO_SPAWN_LAUNCH_HANDLE_JOB] != ZX_HANDLE_INVALID)
656        zx_handle_close(msg_handles[FDIO_SPAWN_LAUNCH_HANDLE_JOB]);
657
658    return status;
659}
660