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 <errno.h> 6#include <lib/fdio/private.h> 7#include <lib/fdio/spawn.h> 8#include <poll.h> 9#include <string.h> 10#include <unistd.h> 11#include <zircon/device/pty.h> 12#include <zircon/process.h> 13#include <zircon/processargs.h> 14#include <zircon/syscalls.h> 15#include <zircon/syscalls/object.h> 16 17#include "shell.h" 18#include "memalloc.h" 19#include "nodes.h" 20#include "exec.h" 21#include "process.h" 22#include "options.h" 23#include "var.h" 24 25static zx_status_t launch(const char* filename, const char* const* argv, 26 const char* const* envp, zx_handle_t* process, 27 zx_handle_t job, char err_msg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH]) { 28 // cancel any ^c generated before running the command 29 uint32_t events = 0; 30 ioctl_pty_read_events(STDIN_FILENO, &events); // ignore any error 31 32 // TODO(abarth): Including FDIO_SPAWN_CLONE_LDSVC doesn't fully make sense. 33 // We should find a library loader that's appropriate for this program 34 // rather than cloning the library loader used by the shell. 35 uint32_t flags = FDIO_SPAWN_CLONE_ALL & ~FDIO_SPAWN_CLONE_ENVIRON; 36 return fdio_spawn_etc(job, flags, filename, argv, envp, 0, NULL, process, err_msg); 37} 38 39// Add all function definitions to our nodelist, so we can package them up for a 40// subshell. 41static void 42addfuncdef(struct cmdentry *entry, void *token) 43{ 44 if (entry->cmdtype == CMDFUNCTION) { 45 struct nodelist **cmdlist = (struct nodelist **) token; 46 struct nodelist *newnode = ckmalloc(sizeof(struct nodelist)); 47 newnode->next = *cmdlist; 48 newnode->n = &entry->u.func->n; 49 *cmdlist = newnode; 50 } 51} 52 53zx_status_t process_subshell(union node* n, const char* const* envp, 54 zx_handle_t* process, zx_handle_t job, 55 int *fds, char err_msg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH]) 56{ 57 if (!orig_arg0) 58 return ZX_ERR_NOT_FOUND; 59 60 // TODO(abarth): Handle the redirects properly (i.e., implement 61 // redirect(n->nredir.redirect) using launchpad); 62 zx_handle_t ast_vmo = ZX_HANDLE_INVALID; 63 64 // Create a node for our expression 65 struct nodelist *nlist = ckmalloc(sizeof(struct nodelist)); 66 nlist->n = n; 67 nlist->next = NULL; 68 69 // Create nodes for all function definitions 70 hashiter(addfuncdef, &nlist); 71 72 // Encode the node list 73 zx_status_t status = codec_encode(nlist, &ast_vmo); 74 75 // Clean up 76 while (nlist) { 77 struct nodelist *next = nlist->next; 78 ckfree(nlist); 79 nlist = next; 80 } 81 82 if (status != ZX_OK) 83 return status; 84 85 // Construct an argv array 86 int argc = 1 + shellparam.nparam; 87 const char *argv[argc + 1]; 88 argv[0] = orig_arg0; 89 size_t arg_ndx; 90 for (arg_ndx = 0; arg_ndx < shellparam.nparam; arg_ndx++) { 91 argv[arg_ndx + 1] = shellparam.p[arg_ndx]; 92 } 93 argv[argc] = NULL; 94 95 fdio_spawn_action_t actions[] = { 96 {.action = FDIO_SPAWN_ACTION_CLONE_FD, .fd = {.local_fd = fds ? fds[0] : 0, .target_fd = 0}}, 97 {.action = FDIO_SPAWN_ACTION_CLONE_FD, .fd = {.local_fd = fds ? fds[1] : 1, .target_fd = 1}}, 98 {.action = FDIO_SPAWN_ACTION_CLONE_FD, .fd = {.local_fd = fds ? fds[2] : 2, .target_fd = 2}}, 99 {.action = FDIO_SPAWN_ACTION_ADD_HANDLE, .h = {.id = PA_HND(PA_USER0, 0), .handle = ast_vmo}}, 100 }; 101 102 // TODO(abarth): Including FDIO_SPAWN_CLONE_LDSVC doesn't fully make sense. 103 // We should find a library loader that's appropriate for this program 104 // rather than cloning the library loader used by the shell. 105 uint32_t flags = FDIO_SPAWN_CLONE_JOB | FDIO_SPAWN_CLONE_LDSVC | FDIO_SPAWN_CLONE_NAMESPACE; 106 return fdio_spawn_etc(job, flags, orig_arg0, argv, envp, 107 countof(actions), actions, process, err_msg); 108} 109 110int process_launch(const char* const* argv, const char* path, int index, 111 zx_handle_t* process, zx_handle_t job, 112 zx_status_t* status_out, char err_msg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH]) { 113 zx_status_t status = ZX_OK; 114 115 // All exported variables 116 const char* const* envp = (const char* const*)environment(); 117 118 if (strchr(argv[0], '/') != NULL) { 119 status = launch(argv[0], argv, envp, process, job, err_msg); 120 } else { 121 status = ZX_ERR_NOT_FOUND; 122 const char* filename = NULL; 123 while (status != ZX_OK && (filename = padvance(&path, argv[0])) != NULL) { 124 if (--index < 0 && pathopt == NULL) 125 status = launch(filename, argv, envp, process, job, err_msg); 126 stunalloc(filename); 127 } 128 } 129 130 *status_out = status; 131 132 switch (status) { 133 case ZX_OK: 134 return 0; 135 case ZX_ERR_ACCESS_DENIED: 136 return 126; 137 case ZX_ERR_NOT_FOUND: 138 return 127; 139 default: 140 return 2; 141 } 142} 143 144// TODO(ZX-972) When isatty correctly examines the fd, use that instead 145int isapty(int fd) { 146 fdio_t* io = __fdio_fd_to_io(fd); 147 if (io == NULL) { 148 errno = EBADF; 149 return 0; 150 } 151 152 // if we can find the window size, it's a tty 153 int ret; 154 pty_window_size_t ws; 155 int noread = ioctl_pty_get_window_size(fd, &ws); 156 if (noread == sizeof(ws)) { 157 ret = 1; 158 } else { 159 ret = 0; 160 errno = ENOTTY; 161 } 162 163 __fdio_release(io); 164 165 return ret; 166} 167 168/* Check for process termination (block if requested). When not blocking, 169 returns ZX_ERR_TIMED_OUT if process hasn't exited yet. */ 170int process_await_termination(zx_handle_t process, zx_handle_t job, bool blocking) { 171 zx_time_t timeout = blocking ? ZX_TIME_INFINITE : 0; 172 zx_status_t status; 173 zx_wait_item_t wait_objects[2]; 174 fdio_t* tty = (isapty(STDIN_FILENO) ? __fdio_fd_to_io(STDIN_FILENO) : NULL); 175 176 bool running = true; 177 while (running) { 178 int no_wait_obj = 0; 179 int tty_wait_obj = -1; 180 wait_objects[no_wait_obj].handle = process; 181 wait_objects[no_wait_obj].waitfor = ZX_TASK_TERMINATED; 182 wait_objects[no_wait_obj].pending = 0; 183 no_wait_obj++; 184 185 if (tty) { 186 wait_objects[no_wait_obj].pending = 0; 187 __fdio_wait_begin(tty, POLLPRI, &wait_objects[no_wait_obj].handle, &wait_objects[no_wait_obj].waitfor); 188 tty_wait_obj = no_wait_obj; 189 no_wait_obj++; 190 } 191 192 status = zx_object_wait_many(wait_objects, no_wait_obj, timeout); 193 194 uint32_t interrupt_event = 0; 195 if (tty) { 196 __fdio_wait_end(tty, wait_objects[tty_wait_obj].pending, &interrupt_event); 197 } 198 199 if (status != ZX_OK && status != ZX_ERR_TIMED_OUT) { 200 running = false; 201 } else if (wait_objects[0].pending & ZX_TASK_TERMINATED) { 202 // process ended normally 203 status = ZX_OK; 204 running = false; 205 } else if (tty && (interrupt_event & POLLPRI)) { 206 // interrupted - kill process 207 uint32_t events = 0; 208 int noread = ioctl_pty_read_events(STDIN_FILENO, &events); 209 if (noread == sizeof(events) && (events & PTY_EVENT_INTERRUPT)) { 210 // process belongs to job, so killing the job kills the process 211 status = zx_task_kill(job); 212 // If the kill failed status is going to be ZX_ERR_ACCESS_DENIED 213 // which is not likely since the user started this process 214 if (status == ZX_OK) { 215 status = ZX_ERR_CANCELED; 216 } 217 running = false; 218 } 219 } 220 } 221 if (tty) 222 __fdio_release(tty); 223 224 if (status != ZX_OK) 225 return status; 226 227 zx_info_process_t proc_info; 228 status = zx_object_get_info(process, ZX_INFO_PROCESS, &proc_info, sizeof(proc_info), NULL, NULL); 229 if (status != ZX_OK) 230 return status; 231 232 return proc_info.return_code; 233} 234