blob: 4606602ca588ad25fb39bbd749565bfe18c3172c [file] [log] [blame]
// Copyright 2017 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 <dirent.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <lib/fdio/io.h>
#include <lib/fdio/namespace.h>
#include <lib/fdio/spawn.h>
#include <zircon/status.h>
#include <zircon/syscalls.h>
void print_namespace(fdio_flat_namespace_t* flat) {
for (size_t n = 0; n < flat->count; n++) {
fprintf(stderr, "{ .handle = 0x%08x, type = 0x%08x, .path = '%s' },\n",
flat->handle[n], flat->type[n], flat->path[n]);
}
}
zx_status_t load_file(const char* path, zx_handle_t* out_vmo) {
int fd = open(path, O_RDONLY);
if (fd < 0)
return ZX_ERR_IO;
zx_handle_t vmo;
zx_status_t status = fdio_get_vmo_clone(fd, &vmo);
close(fd);
if (status != ZX_OK) {
return status;
}
status = zx_vmo_replace_as_executable(vmo, ZX_HANDLE_INVALID, out_vmo);
if (status != ZX_OK) {
zx_handle_close(vmo);
return status;
}
return ZX_OK;
}
int run_in_namespace(const char** argv, size_t count, const char* const* mapping) {
zx_status_t status;
zx_handle_t binary;
if ((status = load_file(argv[0], &binary)) != ZX_OK) {
fprintf(stderr, "error: failed to read '%s': %d (%s)\n", argv[0], status,
zx_status_get_string(status));
return -1;
}
fdio_ns_t* ns;
if ((status = fdio_ns_create(&ns)) < 0) {
fprintf(stderr, "error: failed to create namespace: %d (%s)\n", status,
zx_status_get_string(status));
return -1;
}
const char* replacement_argv0 = NULL;
for (size_t n = 0; n < count; n++) {
const char* dst = *mapping++;
char* src = strchr(dst, '=');
if (src == NULL) {
fprintf(stderr, "error: mapping '%s' not in form of '<dst>=<src>'\n", dst);
return -1;
}
*src++ = 0;
if (strcmp(dst, "--replace-child-argv0") == 0) {
if (replacement_argv0) {
fprintf(stderr, "error: multiple --replace-child-argv0 specified\n");
return -1;
}
replacement_argv0 = src;
continue;
}
int fd = open(src, O_RDONLY | O_DIRECTORY);
if (fd < 0) {
fprintf(stderr, "error: cannot open '%s'\n", src);
return -1;
}
if ((status = fdio_ns_bind_fd(ns, dst, fd)) < 0) {
fprintf(stderr, "error: binding fd %d to '%s' failed: %d (%s)\n", fd, dst, status,
zx_status_get_string(status));
close(fd);
return -1;
}
close(fd);
}
fdio_flat_namespace_t* flat;
fdio_ns_opendir(ns);
status = fdio_ns_export(ns, &flat);
fdio_ns_destroy(ns);
if (status < 0) {
fprintf(stderr, "error: cannot flatten namespace: %d (%s)\n", status,
zx_status_get_string(status));
return -1;
}
print_namespace(flat);
fdio_spawn_action_t actions[flat->count + 1];
for (size_t i = 0; i < flat->count; ++i) {
fdio_spawn_action_t add_ns_entry = {
.action = FDIO_SPAWN_ACTION_ADD_NS_ENTRY,
.ns = {
.prefix = flat->path[i],
.handle = flat->handle[i],
},
};
actions[i] = add_ns_entry;
}
fdio_spawn_action_t set_name = {.action = FDIO_SPAWN_ACTION_SET_NAME,
.name = {.data = argv[0]}};
actions[flat->count] = set_name;
uint32_t flags = FDIO_SPAWN_CLONE_ALL & ~FDIO_SPAWN_CLONE_NAMESPACE;
if (replacement_argv0)
argv[0] = replacement_argv0;
char err_msg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH];
zx_handle_t proc = ZX_HANDLE_INVALID;
status = fdio_spawn_vmo(ZX_HANDLE_INVALID, flags, binary, argv, NULL,
countof(actions), actions, &proc, err_msg);
free(flat);
if (status != ZX_OK) {
fprintf(stderr, "error: failed to launch command: %d (%s): %s\n",
status, zx_status_get_string(status), err_msg);
return -1;
}
zx_object_wait_one(proc, ZX_PROCESS_TERMINATED, ZX_TIME_INFINITE, NULL);
zx_info_process_t info;
zx_object_get_info(proc, ZX_INFO_PROCESS, &info, sizeof(info), NULL, NULL);
fprintf(stderr, "[done]\n");
return info.return_code;
}
int dump_current_namespace(void) {
fdio_flat_namespace_t* flat;
zx_status_t r = fdio_ns_export_root(&flat);
if (r < 0) {
fprintf(stderr, "error: cannot export namespace: %d\n", r);
return -1;
}
print_namespace(flat);
return 0;
}
int main(int argc, const char** argv) {
if (argc == 2 && strcmp(argv[1], "--dump") == 0) {
return dump_current_namespace();
}
if (argc > 1) {
const char* kDefaultArgv[] = { "/boot/bin/sh", NULL };
const char** child_argv = kDefaultArgv;
size_t count = 0;
const char* const* mapping = argv + 1;
for (int i = 1; i < argc; ++i) {
if (strcmp(argv[i], "--") == 0) {
if (i + 1 < argc)
child_argv = argv + i + 1;
break;
}
++count;
}
return run_in_namespace(child_argv, count, mapping);
}
printf("Usage: %s ( --dump | [dst=src]+ [--replace-child-argv0=child_argv0] [ -- cmd arg1 ... argn ] )\n"
"Dumps the current namespace or runs a command with src mapped to dst.\n"
"If no command is specified, runs a shell.\n"
"If --replace-child-argv0 is supplied, that string will be used for argv[0]\n"
"as the child process sees it.\n",
argv[0]);
return -1;
}