1// Copyright 2016 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 "devmgr.h"
6
7#include <launchpad/launchpad.h>
8#include <launchpad/vmo.h>
9
10#include <lib/fdio/io.h>
11#include <lib/fdio/util.h>
12
13#include <zircon/paths.h>
14#include <zircon/processargs.h>
15#include <zircon/syscalls.h>
16#include <zircon/syscalls/log.h>
17
18#include <limits.h>
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22#include <unistd.h>
23
24void devmgr_io_init() {
25    // setup stdout
26    zx_handle_t h;
27    if (zx_debuglog_create(ZX_HANDLE_INVALID, 0, &h) < 0) {
28        return;
29    }
30    fdio_t* logger;
31    if ((logger = fdio_logger_create(h)) == nullptr) {
32        return;
33    }
34    close(1);
35    fdio_bind_to_fd(logger, 1, 0);
36}
37
38#define USER_MAX_HANDLES 4
39#define MAX_ENVP 16
40#define CHILD_JOB_RIGHTS (ZX_RIGHTS_BASIC | ZX_RIGHT_READ | ZX_RIGHT_WRITE)
41
42static struct {
43    const char* mount;
44    const char* name;
45    uint32_t flags;
46} FSTAB[] = {
47    { "/svc",       "svc",       FS_SVC },
48    { "/hub",       "hub",       FS_HUB },
49    { "/dev",       "dev",       FS_DEV },
50    { "/boot",      "boot",      FS_BOOT },
51    { "/data",      "data",      FS_DATA },
52    { "/system",    "system",    FS_SYSTEM },
53    { "/install",   "install",   FS_INSTALL },
54    { "/volume",    "volume",    FS_VOLUME },
55    { "/blob",      "blob",      FS_BLOB },
56    { "/pkgfs",     "pkgfs",     FS_PKGFS },
57    { "/tmp",       "tmp",       FS_TMP },
58};
59
60void devmgr_disable_appmgr_services() {
61    FSTAB[1].flags = 0;
62}
63
64zx_status_t devmgr_launch(
65    zx_handle_t job, const char* name,
66    zx_status_t (*load)(void*, launchpad_t*, const char*), void* ctx,
67    int argc, const char* const* argv,
68    const char** _envp, int stdiofd,
69    const zx_handle_t* handles, const uint32_t* types, size_t hcount,
70    zx_handle_t* proc, uint32_t flags) {
71    zx_status_t status;
72    const char* envp[MAX_ENVP + 1];
73    unsigned envn = 0;
74
75    if (getenv(LDSO_TRACE_CMDLINE)) {
76        envp[envn++] = LDSO_TRACE_ENV;
77    }
78    envp[envn++] = ZX_SHELL_ENV_PATH;
79    while ((_envp && _envp[0]) && (envn < MAX_ENVP)) {
80        envp[envn++] = *_envp++;
81    }
82    envp[envn++] = nullptr;
83
84    zx_handle_t job_copy = ZX_HANDLE_INVALID;
85    zx_handle_duplicate(job, CHILD_JOB_RIGHTS, &job_copy);
86
87    launchpad_t* lp;
88    launchpad_create(job_copy, name, &lp);
89
90    status = (*load)(ctx, lp, argv[0]);
91    if (status != ZX_OK) {
92        launchpad_abort(lp, status, "cannot load file");
93    }
94    launchpad_set_args(lp, argc, argv);
95    launchpad_set_environ(lp, envp);
96
97    // create namespace based on FS_* flags
98    const char* nametable[countof(FSTAB)] = { };
99    uint32_t count = 0;
100    zx_handle_t h;
101    for (unsigned n = 0; n < countof(FSTAB); n++) {
102        if (!(FSTAB[n].flags & flags)) {
103            continue;
104        }
105        if ((h = fs_clone(FSTAB[n].name).release()) != ZX_HANDLE_INVALID) {
106            nametable[count] = FSTAB[n].mount;
107            launchpad_add_handle(lp, h, PA_HND(PA_NS_DIR, count++));
108        }
109    }
110    launchpad_set_nametable(lp, count, nametable);
111
112    if (stdiofd < 0) {
113        if ((status = zx_debuglog_create(ZX_HANDLE_INVALID, 0, &h) < 0)) {
114            launchpad_abort(lp, status, "devmgr: cannot create debuglog handle");
115        } else {
116            launchpad_add_handle(lp, h, PA_HND(PA_FDIO_LOGGER, FDIO_FLAG_USE_FOR_STDIO | 0));
117        }
118    } else {
119        launchpad_clone_fd(lp, stdiofd, FDIO_FLAG_USE_FOR_STDIO | 0);
120        close(stdiofd);
121    }
122
123    launchpad_add_handles(lp, hcount, handles, types);
124
125    const char* errmsg;
126    if ((status = launchpad_go(lp, proc, &errmsg)) < 0) {
127        printf("devmgr: launchpad %s (%s) failed: %s: %d\n",
128               argv[0], name, errmsg, status);
129    } else {
130        printf("devmgr: launch %s (%s) OK\n", argv[0], name);
131    }
132    zx_handle_close(job_copy);
133    return status;
134}
135
136zx_status_t devmgr_launch_cmdline(
137    const char* me, zx_handle_t job, const char* name,
138    zx_status_t (*load)(void* ctx, launchpad_t*, const char* file), void* ctx,
139    const char* cmdline,
140    const zx_handle_t* handles, const uint32_t* types, size_t hcount,
141    zx_handle_t* proc, uint32_t flags) {
142
143    // Get the full commandline by splitting on '+'.
144    char* buf = strdup(cmdline);
145    if (buf == nullptr) {
146        printf("%s: Can't parse + command: %s\n", me, cmdline);
147        return ZX_ERR_UNAVAILABLE;
148    }
149    const int MAXARGS = 8;
150    char* argv[MAXARGS];
151    int argc = 0;
152    char* token;
153    char* rest = buf;
154    while (argc < MAXARGS && (token = strtok_r(rest, "+", &rest))) {
155        argv[argc++] = token;
156    }
157
158    printf("%s: starting", me);
159    for (int i = 0; i < argc; i++) {
160        printf(" '%s'", argv[i]);
161    }
162    printf("...\n");
163
164    zx_status_t status = devmgr_launch(
165        job, name, load, ctx, argc, (const char* const*)argv, nullptr, -1,
166        handles, types, hcount, proc, flags);
167
168    free(buf);
169
170    return status;
171}
172
173zx_status_t copy_vmo(zx_handle_t src, zx_off_t offset, size_t length, zx_handle_t* out_dest) {
174    zx_handle_t dest;
175    zx_status_t status = zx_vmo_create(length, 0, &dest);
176    if (status != ZX_OK) {
177        return status;
178    }
179
180    char buffer[PAGE_SIZE];
181    zx_off_t src_offset = offset;
182    zx_off_t dest_offset = 0;
183
184    while (length > 0) {
185        size_t copy = (length > sizeof(buffer) ? sizeof(buffer) : length);
186        if ((status = zx_vmo_read(src, buffer, src_offset, copy)) != ZX_OK) {
187            goto fail;
188        }
189        if ((status = zx_vmo_write(dest, buffer, dest_offset, copy)) != ZX_OK) {
190            goto fail;
191        }
192        src_offset += copy;
193        dest_offset += copy;
194        length -= copy;
195    }
196
197    *out_dest = dest;
198    return ZX_OK;
199
200fail:
201    zx_handle_close(dest);
202    return status;
203}
204