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/spawn.h>
7 #include <lib/fdio/unsafe.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
launch(const char * filename,const char * const * argv,const char * const * envp,zx_handle_t * process,zx_handle_t job,char err_msg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH])25 static 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_DEFAULT_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.
41 static void
addfuncdef(struct cmdentry * entry,void * token)42 addfuncdef(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
process_subshell(union node * n,const char * const * envp,zx_handle_t * process,zx_handle_t job,int * fds,char err_msg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH])53 zx_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_DEFAULT_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_DEFAULT_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
process_launch(const char * const * argv,const char * path,int index,zx_handle_t * process,zx_handle_t job,zx_status_t * status_out,char err_msg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH])110 int 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
isapty(int fd)145 int isapty(int fd) {
146 fdio_t* io = fdio_unsafe_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_unsafe_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. */
process_await_termination(zx_handle_t process,zx_handle_t job,bool blocking)170 int 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_unsafe_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_unsafe_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_unsafe_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_unsafe_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