1// Copyright 2017 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 <dirent.h> 6#include <fcntl.h> 7#include <stdlib.h> 8#include <stdio.h> 9#include <string.h> 10#include <unistd.h> 11 12#include <lib/fdio/io.h> 13#include <lib/fdio/namespace.h> 14#include <lib/fdio/spawn.h> 15#include <zircon/status.h> 16#include <zircon/syscalls.h> 17 18void print_namespace(fdio_flat_namespace_t* flat) { 19 for (size_t n = 0; n < flat->count; n++) { 20 fprintf(stderr, "{ .handle = 0x%08x, type = 0x%08x, .path = '%s' },\n", 21 flat->handle[n], flat->type[n], flat->path[n]); 22 } 23} 24 25zx_status_t load_file(const char* path, zx_handle_t* vmo) { 26 int fd = open(path, O_RDONLY); 27 if (fd < 0) 28 return ZX_ERR_IO; 29 zx_status_t status = fdio_get_vmo_clone(fd, vmo); 30 close(fd); 31 return status; 32} 33 34int run_in_namespace(const char** argv, size_t count, const char* const* mapping) { 35 zx_status_t status; 36 zx_handle_t binary; 37 if ((status = load_file(argv[0], &binary)) != ZX_OK) { 38 fprintf(stderr, "error: failed to read '%s': %d (%s)\n", argv[0], status, 39 zx_status_get_string(status)); 40 return -1; 41 } 42 43 fdio_ns_t* ns; 44 if ((status = fdio_ns_create(&ns)) < 0) { 45 fprintf(stderr, "error: failed to create namespace: %d (%s)\n", status, 46 zx_status_get_string(status)); 47 return -1; 48 } 49 const char* replacement_argv0 = NULL; 50 for (size_t n = 0; n < count; n++) { 51 const char* dst = *mapping++; 52 char* src = strchr(dst, '='); 53 if (src == NULL) { 54 fprintf(stderr, "error: mapping '%s' not in form of '<dst>=<src>'\n", dst); 55 return -1; 56 } 57 *src++ = 0; 58 if (strcmp(dst, "--replace-child-argv0") == 0) { 59 if (replacement_argv0) { 60 fprintf(stderr, "error: multiple --replace-child-argv0 specified\n"); 61 return -1; 62 } 63 replacement_argv0 = src; 64 continue; 65 } 66 int fd = open(src, O_RDONLY | O_DIRECTORY); 67 if (fd < 0) { 68 fprintf(stderr, "error: cannot open '%s'\n", src); 69 return -1; 70 } 71 if ((status = fdio_ns_bind_fd(ns, dst, fd)) < 0) { 72 fprintf(stderr, "error: binding fd %d to '%s' failed: %d (%s)\n", fd, dst, status, 73 zx_status_get_string(status)); 74 close(fd); 75 return -1; 76 } 77 close(fd); 78 } 79 fdio_flat_namespace_t* flat; 80 fdio_ns_opendir(ns); 81 status = fdio_ns_export(ns, &flat); 82 fdio_ns_destroy(ns); 83 if (status < 0) { 84 fprintf(stderr, "error: cannot flatten namespace: %d (%s)\n", status, 85 zx_status_get_string(status)); 86 return -1; 87 } 88 89 print_namespace(flat); 90 91 fdio_spawn_action_t actions[flat->count + 1]; 92 93 for (size_t i = 0; i < flat->count; ++i) { 94 fdio_spawn_action_t add_ns_entry = { 95 .action = FDIO_SPAWN_ACTION_ADD_NS_ENTRY, 96 .ns = { 97 .prefix = flat->path[i], 98 .handle = flat->handle[i], 99 }, 100 }; 101 actions[i] = add_ns_entry; 102 } 103 104 fdio_spawn_action_t set_name = {.action = FDIO_SPAWN_ACTION_SET_NAME, 105 .name = {.data = argv[0]}}; 106 actions[flat->count] = set_name; 107 108 uint32_t flags = FDIO_SPAWN_CLONE_ALL & ~FDIO_SPAWN_CLONE_NAMESPACE; 109 110 if (replacement_argv0) 111 argv[0] = replacement_argv0; 112 113 char err_msg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH]; 114 zx_handle_t proc = ZX_HANDLE_INVALID; 115 status = fdio_spawn_vmo(ZX_HANDLE_INVALID, flags, binary, argv, NULL, 116 countof(actions), actions, &proc, err_msg); 117 118 free(flat); 119 120 if (status != ZX_OK) { 121 fprintf(stderr, "error: failed to launch command: %d (%s): %s\n", 122 status, zx_status_get_string(status), err_msg); 123 return -1; 124 } 125 126 zx_object_wait_one(proc, ZX_PROCESS_TERMINATED, ZX_TIME_INFINITE, NULL); 127 zx_info_process_t info; 128 zx_object_get_info(proc, ZX_INFO_PROCESS, &info, sizeof(info), NULL, NULL); 129 fprintf(stderr, "[done]\n"); 130 return info.return_code; 131} 132 133int dump_current_namespace(void) { 134 fdio_flat_namespace_t* flat; 135 zx_status_t r = fdio_ns_export_root(&flat); 136 137 if (r < 0) { 138 fprintf(stderr, "error: cannot export namespace: %d\n", r); 139 return -1; 140 } 141 142 print_namespace(flat); 143 return 0; 144} 145 146int main(int argc, const char** argv) { 147 if (argc == 2 && strcmp(argv[1], "--dump") == 0) { 148 return dump_current_namespace(); 149 } 150 151 if (argc > 1) { 152 const char* kDefaultArgv[] = { "/boot/bin/sh", NULL }; 153 const char** child_argv = kDefaultArgv; 154 size_t count = 0; 155 const char* const* mapping = argv + 1; 156 for (int i = 1; i < argc; ++i) { 157 if (strcmp(argv[i], "--") == 0) { 158 if (i + 1 < argc) 159 child_argv = argv + i + 1; 160 break; 161 } 162 ++count; 163 } 164 return run_in_namespace(child_argv, count, mapping); 165 } 166 167 printf("Usage: %s ( --dump | [dst=src]+ [--replace-child-argv0=child_argv0] [ -- cmd arg1 ... argn ] )\n" 168 "Dumps the current namespace or runs a command with src mapped to dst.\n" 169 "If no command is specified, runs a shell.\n" 170 "If --replace-child-argv0 is supplied, that string will be used for argv[0]\n" 171 "as the child process sees it.\n", 172 argv[0]); 173 return -1; 174} 175