blob: 83ae3f3349d2d91f025af9a695040e09319a5951 [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 <launchpad/launchpad.h>
#include <launchpad/vmo.h>
#include <fdio/namespace.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]);
}
}
int run_in_namespace(int argc, const char* const* argv,
size_t count, const char* const* mapping) {
zx_status_t r;
zx_handle_t binary;
r = launchpad_vmo_from_file(argv[0], &binary);
if (r != ZX_OK) {
fprintf(stderr, "error: failed to read '%s': %d\n", argv[0], r);
return -1;
}
fdio_ns_t* ns;
if ((r = fdio_ns_create(&ns)) < 0) {
fprintf(stderr, "error: failed to create namespace: %d\n", r);
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 ((r = fdio_ns_bind_fd(ns, dst, fd)) < 0) {
fprintf(stderr, "error: binding fd %d to '%s' failed: %d\n", fd, dst, r);
close(fd);
return -1;
}
close(fd);
}
fdio_flat_namespace_t* flat;
fdio_ns_opendir(ns);
r = fdio_ns_export(ns, &flat);
fdio_ns_destroy(ns);
if (r < 0) {
fprintf(stderr, "error: cannot flatten namespace: %d\n", r);
return -1;
}
print_namespace(flat);
launchpad_t* lp;
launchpad_create(0, argv[0], &lp);
launchpad_clone(lp, LP_CLONE_FDIO_STDIO | LP_CLONE_ENVIRON | LP_CLONE_DEFAULT_JOB);
if (replacement_argv0) {
const char** argv_with_replaced_argv0 = malloc(argc * sizeof(const char*));
argv_with_replaced_argv0[0] = replacement_argv0;
for (int i = 1; i < argc; i++)
argv_with_replaced_argv0[i] = argv[i];
launchpad_set_args(lp, argc, argv_with_replaced_argv0);
free(argv_with_replaced_argv0);
} else {
launchpad_set_args(lp, argc, argv);
}
launchpad_set_nametable(lp, flat->count, flat->path);
launchpad_add_handles(lp, flat->count, flat->handle, flat->type);
launchpad_load_from_vmo(lp, binary);
free(flat);
const char* errmsg;
zx_handle_t proc;
if ((r = launchpad_go(lp, &proc, &errmsg)) < 0) {
fprintf(stderr, "error: failed to launch command: %s\n", errmsg);
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;
}
static const char* kShell[] = { "/boot/bin/sh" };
int main(int argc, const char* const* argv) {
if (argc == 2 && strcmp(argv[1], "--dump") == 0) {
return dump_current_namespace();
}
if (argc > 1) {
int child_argc = 1;
const char* const* child_argv = kShell;
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_argc = argc - i - 1;
child_argv = &argv[i + 1];
}
break;
}
++count;
}
return run_in_namespace(child_argc, 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;
}