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