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