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 <assert.h>
6 #include <fcntl.h>
7 #include <poll.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <unistd.h>
12 
13 #include <fbl/algorithm.h>
14 #include <fbl/unique_fd.h>
15 #include <fuchsia/io/c/fidl.h>
16 #include <lib/fdio/io.h>
17 #include <lib/fdio/spawn.h>
18 #include <lib/fdio/util.h>
19 #include <lib/fdio/watcher.h>
20 #include <lib/fzl/fdio.h>
21 #include <lib/zx/channel.h>
22 #include <port/port.h>
23 #include <zircon/device/pty.h>
24 #include <zircon/device/vfs.h>
25 #include <zircon/listnode.h>
26 #include <zircon/process.h>
27 #include <zircon/processargs.h>
28 #include <zircon/status.h>
29 #include <zircon/syscalls.h>
30 #include <zircon/syscalls/log.h>
31 #include <zircon/syscalls/object.h>
32 
33 #include <utility>
34 
35 #include "vc.h"
36 
37 port_t port;
38 static port_handler_t log_ph;
39 static port_handler_t new_vc_ph;
40 static port_handler_t input_ph;
41 
42 static int input_dir_fd;
43 
44 static vc_t* log_vc;
45 static zx_koid_t proc_koid;
46 
log_reader_cb(port_handler_t * ph,zx_signals_t signals,uint32_t evt)47 static zx_status_t log_reader_cb(port_handler_t* ph, zx_signals_t signals, uint32_t evt) {
48     char buf[ZX_LOG_RECORD_MAX];
49     zx_log_record_t* rec = (zx_log_record_t*)buf;
50     zx_status_t status;
51     for (;;) {
52         if ((status = zx_debuglog_read(ph->handle, 0, rec, ZX_LOG_RECORD_MAX)) < 0) {
53             if (status == ZX_ERR_SHOULD_WAIT) {
54                 // return non-OK to avoid needlessly re-arming the repeating wait
55                 return ZX_ERR_NEXT;
56             }
57             break;
58         }
59         // don't print log messages from ourself
60         if (rec->pid == proc_koid) {
61             continue;
62         }
63         char tmp[64];
64         snprintf(tmp, 64, "\033[32m%05d.%03d\033[39m] \033[31m%05" PRIu64 ".\033[36m%05" PRIu64 "\033[39m> ",
65                  (int)(rec->timestamp / 1000000000ULL),
66                  (int)((rec->timestamp / 1000000ULL) % 1000ULL),
67                  rec->pid, rec->tid);
68         vc_write(log_vc, tmp, strlen(tmp), 0);
69         vc_write(log_vc, rec->data, rec->datalen, 0);
70         if ((rec->datalen == 0) ||
71             (rec->data[rec->datalen - 1] != '\n')) {
72             vc_write(log_vc, "\n", 1, 0);
73         }
74     }
75 
76     const char* oops = "<<LOG ERROR>>\n";
77     vc_write(log_vc, oops, strlen(oops), 0);
78 
79     // Error reading the log, no point in continuing to try to read
80     // log messages.
81     port_cancel(&port, &log_ph);
82     return status;
83 }
84 
launch_shell(vc_t * vc,int fd,const char * cmd)85 static zx_status_t launch_shell(vc_t* vc, int fd, const char* cmd) {
86     const char* argv[] = { "/boot/bin/sh", nullptr, nullptr, nullptr };
87 
88     if (cmd) {
89         argv[1] = "-c";
90         argv[2] = cmd;
91     }
92 
93     fdio_spawn_action_t actions[2] = {};
94     actions[0].action = FDIO_SPAWN_ACTION_SET_NAME;
95     actions[0].name.data = "vc:sh";
96     actions[1].action = FDIO_SPAWN_ACTION_TRANSFER_FD;
97     actions[1].fd = {.local_fd = fd, .target_fd = FDIO_FLAG_USE_FOR_STDIO};
98 
99     uint32_t flags = FDIO_SPAWN_CLONE_ALL & ~FDIO_SPAWN_CLONE_STDIO;
100 
101     char err_msg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH];
102     zx_status_t status = fdio_spawn_etc(ZX_HANDLE_INVALID, flags, argv[0], argv,
103                                         nullptr, fbl::count_of(actions), actions,
104                                         &vc->proc, err_msg);
105     if (status != ZX_OK) {
106         printf("vc: cannot spawn shell: %s: %d (%s)\n", err_msg, status,
107                zx_status_get_string(status));
108     }
109     return status;
110 }
111 
session_destroy(vc_t * vc)112 static void session_destroy(vc_t* vc) {
113     if (vc->fd >= 0) {
114         port_fd_handler_done(&vc->fh);
115         // vc_destroy() closes the fd
116     }
117     if (vc->proc != ZX_HANDLE_INVALID) {
118         zx_task_kill(vc->proc);
119     }
120     vc_destroy(vc);
121 }
122 
session_io_cb(port_fd_handler_t * fh,unsigned pollevt,uint32_t evt)123 static zx_status_t session_io_cb(port_fd_handler_t* fh, unsigned pollevt, uint32_t evt) {
124     vc_t* vc = containerof(fh, vc_t, fh);
125 
126     if (pollevt & POLLIN) {
127         char data[1024];
128         ssize_t r = read(vc->fd, data, sizeof(data));
129         if (r > 0) {
130             vc_write(vc, data, r, 0);
131             return ZX_OK;
132         }
133     }
134 
135     if (pollevt & (POLLRDHUP | POLLHUP)) {
136         // shell sessions get restarted on exit
137         if (vc->is_shell) {
138             zx_task_kill(vc->proc);
139             vc->proc = ZX_HANDLE_INVALID;
140 
141             int fd = openat(vc->fd, "0", O_RDWR);
142             if (fd < 0) {
143                 goto fail;
144             }
145 
146             if (launch_shell(vc, fd, NULL) < 0) {
147                 goto fail;
148             }
149             return ZX_OK;
150         }
151     }
152 
153 fail:
154     session_destroy(vc);
155     return ZX_ERR_STOP;
156 }
157 
session_create(vc_t ** out,int * out_fd,bool make_active,bool special)158 static zx_status_t session_create(vc_t** out, int* out_fd, bool make_active, bool special) {
159     int fd;
160 
161     // The ptmx device can start later than these threads
162     int retry = 30;
163     while ((fd = open("/dev/misc/ptmx", O_RDWR | O_NONBLOCK)) < 0) {
164         if (--retry == 0) {
165             return ZX_ERR_IO;
166         }
167         usleep(100000);
168     }
169 
170     int client_fd = openat(fd, "0", O_RDWR);
171     if (client_fd < 0) {
172         close(fd);
173         return ZX_ERR_IO;
174     }
175 
176     vc_t* vc;
177     if (vc_create(&vc, special)) {
178         close(fd);
179         close(client_fd);
180         return ZX_ERR_INTERNAL;
181     }
182     zx_status_t r;
183     if ((r = port_fd_handler_init(&vc->fh, fd, POLLIN | POLLRDHUP | POLLHUP)) < 0) {
184         vc_destroy(vc);
185         close(fd);
186         close(client_fd);
187         return r;
188     }
189     vc->fd = fd;
190 
191     if (make_active) {
192         vc_set_active(-1, vc);
193     }
194 
195     pty_window_size_t wsz = {
196         .width = vc->columns,
197         .height = vc->rows,
198     };
199     ioctl_pty_set_window_size(fd, &wsz);
200 
201     vc->fh.func = session_io_cb;
202 
203     *out = vc;
204     *out_fd = client_fd;
205     return ZX_OK;
206 }
207 
start_shell(bool make_active,const char * cmd)208 static void start_shell(bool make_active, const char* cmd) {
209     vc_t* vc;
210     int fd;
211 
212     if (session_create(&vc, &fd, make_active, cmd != NULL) < 0) {
213         return;
214     }
215 
216     vc->is_shell = true;
217 
218     if (launch_shell(vc, fd, cmd) < 0) {
219         session_destroy(vc);
220     } else {
221         port_wait(&port, &vc->fh.ph);
222     }
223 }
224 
new_vc_cb(port_handler_t * ph,zx_signals_t signals,uint32_t evt)225 static zx_status_t new_vc_cb(port_handler_t* ph, zx_signals_t signals, uint32_t evt) {
226     zx_handle_t h;
227     uint32_t dcount, hcount;
228     if (zx_channel_read(ph->handle, 0, NULL, &h, 0, 1, &dcount, &hcount) < 0) {
229         return ZX_OK;
230     }
231     if (hcount != 1) {
232         return ZX_OK;
233     }
234 
235     vc_t* vc;
236     int fd;
237     if (session_create(&vc, &fd, true, false) < 0) {
238         zx_handle_close(h);
239         return ZX_OK;
240     }
241 
242     zx_handle_t handles[FDIO_MAX_HANDLES];
243     uint32_t types[FDIO_MAX_HANDLES];
244     zx_status_t r = fdio_transfer_fd(fd, FDIO_FLAG_USE_FOR_STDIO | 0, handles, types);
245     if (zx_channel_write(h, 0, types,
246                          static_cast<uint32_t>(r * sizeof(uint32_t)), handles, r) != ZX_OK) {
247         session_destroy(vc);
248     } else {
249         port_wait(&port, &vc->fh.ph);
250     }
251 
252     zx_handle_close(h);
253     return ZX_OK;
254 }
255 
input_dir_event(unsigned evt,const char * name)256 static zx_status_t input_dir_event(unsigned evt, const char* name) {
257     if ((evt != fuchsia_io_WATCH_EVENT_EXISTING) && (evt != fuchsia_io_WATCH_EVENT_ADDED)) {
258         return ZX_OK;
259     }
260 
261     printf("vc: new input device /dev/class/input/%s\n", name);
262 
263     int fd;
264     if ((fd = openat(input_dir_fd, name, O_RDONLY)) < 0) {
265         return ZX_OK;
266     }
267 
268     new_input_device(fd, handle_key_press);
269     return ZX_OK;
270 }
271 
setup_dir_watcher(const char * dir,zx_status_t (* cb)(port_handler_t *,zx_signals_t,uint32_t),port_handler_t * ph,int * fd_out)272 static void setup_dir_watcher(const char* dir,
273                               zx_status_t (*cb)(port_handler_t*, zx_signals_t, uint32_t),
274                               port_handler_t* ph,
275                               int* fd_out) {
276     *fd_out = -1;
277     fbl::unique_fd fd(open(dir, O_DIRECTORY | O_RDONLY));
278     if (!fd) {
279         return;
280     }
281     zx::channel client, server;
282     if (zx::channel::create(0, &client, &server) != ZX_OK) {
283         return;
284     }
285 
286     fzl::FdioCaller caller(std::move(fd));
287     zx_status_t status;
288     zx_status_t io_status = fuchsia_io_DirectoryWatch(caller.borrow_channel(),
289                                                       fuchsia_io_WATCH_MASK_ALL, 0,
290                                                       server.release(),
291                                                       &status);
292     if (io_status != ZX_OK || status != ZX_OK) {
293         return;
294     }
295 
296     *fd_out = caller.release().release();
297     ph->handle = client.release();
298     ph->waitfor = ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED;
299     ph->func = cb;
300     port_wait(&port, ph);
301 }
302 
handle_device_dir_event(port_handler_t * ph,zx_signals_t signals,zx_status_t (* event_handler)(unsigned event,const char * msg))303 zx_status_t handle_device_dir_event(port_handler_t* ph, zx_signals_t signals,
304                                     zx_status_t (*event_handler)(unsigned event, const char* msg)) {
305     if (!(signals & ZX_CHANNEL_READABLE)) {
306         printf("vc: device directory died\n");
307         return ZX_ERR_STOP;
308     }
309 
310     // Buffer contains events { Opcode, Len, Name[Len] }
311     // See zircon/device/vfs.h for more detail
312     // extra byte is for temporary NUL
313     uint8_t buf[fuchsia_io_MAX_BUF + 1];
314     uint32_t len;
315     if (zx_channel_read(ph->handle, 0, buf, NULL, sizeof(buf) - 1, 0, &len, NULL) < 0) {
316         printf("vc: failed to read from device directory\n");
317         return ZX_ERR_STOP;
318     }
319 
320     uint8_t* msg = buf;
321     while (len >= 2) {
322         uint8_t event = *msg++;
323         uint8_t namelen = *msg++;
324         if (len < (namelen + 2u)) {
325             printf("vc: malformed device directory message\n");
326             return ZX_ERR_STOP;
327         }
328         // add temporary nul
329         uint8_t tmp = msg[namelen];
330         msg[namelen] = 0;
331         zx_status_t status = event_handler(event, (char*)msg);
332         if (status != ZX_OK) {
333             return status;
334         }
335         msg[namelen] = tmp;
336         msg += namelen;
337         len -= (namelen + 2u);
338     }
339     return ZX_OK;
340 }
341 
input_cb(port_handler_t * ph,zx_signals_t signals,uint32_t evt)342 static zx_status_t input_cb(port_handler_t* ph, zx_signals_t signals, uint32_t evt) {
343     return handle_device_dir_event(ph, signals, input_dir_event);
344 }
345 
set_log_listener_active(bool active)346 void set_log_listener_active(bool active) {
347     if (active) {
348         port_wait_repeating(&port, &log_ph);
349     } else {
350         port_cancel(&port, &log_ph);
351     }
352 }
353 
main(int argc,char ** argv)354 int main(int argc, char** argv) {
355     // NOTE: devmgr has getenv_bool. when more options are added, consider
356     // sharing that.
357     bool keep_log = false;
358     const char* value = getenv("virtcon.keep-log-visible");
359     if (value == NULL ||
360         ((strcmp(value, "0") == 0) ||
361          (strcmp(value, "false") == 0) ||
362          (strcmp(value, "off") == 0))) {
363         keep_log = false;
364     } else {
365         keep_log = true;
366     }
367 
368     const char* cmd = NULL;
369     int shells = 0;
370     while (argc > 1) {
371         if (!strcmp(argv[1], "--run")) {
372             if (argc > 2) {
373                 argc--;
374                 argv++;
375                 cmd = argv[1];
376                 if (shells < 1)
377                     shells = 1;
378                 printf("CMD: %s\n", cmd);
379             }
380         } else if (!strcmp(argv[1], "--shells")) {
381             if (argc > 2) {
382                 argc--;
383                 argv++;
384                 shells = atoi(argv[1]);
385             }
386         }
387         argc--;
388         argv++;
389     }
390 
391     if (port_init(&port) < 0) {
392         return -1;
393     }
394 
395     // create initial console for debug log
396     if (vc_create(&log_vc, false) != ZX_OK) {
397         return -1;
398     }
399     snprintf(log_vc->title, sizeof(log_vc->title), "debuglog");
400 
401     // Get our process koid so the log reader can
402     // filter out our own debug messages from the log
403     zx_info_handle_basic_t info;
404     if (zx_object_get_info(zx_process_self(), ZX_INFO_HANDLE_BASIC, &info,
405                            sizeof(info), NULL, NULL) == ZX_OK) {
406         proc_koid = info.koid;
407     }
408 
409     // TODO: receive from launching process
410     if (zx_debuglog_create(ZX_HANDLE_INVALID, ZX_LOG_FLAG_READABLE, &log_ph.handle) < 0) {
411         printf("vc log listener: cannot open log\n");
412         return -1;
413     }
414 
415     log_ph.func = log_reader_cb;
416     log_ph.waitfor = ZX_LOG_READABLE;
417 
418     if ((new_vc_ph.handle = zx_take_startup_handle(PA_HND(PA_USER0, 0))) != ZX_HANDLE_INVALID) {
419         new_vc_ph.func = new_vc_cb;
420         new_vc_ph.waitfor = ZX_CHANNEL_READABLE;
421         port_wait(&port, &new_vc_ph);
422     }
423 
424     setup_dir_watcher("/dev/class/input", input_cb, &input_ph, &input_dir_fd);
425 
426     if (!vc_display_init()) {
427         return -1;
428     }
429 
430     setenv("TERM", "xterm", 1);
431 
432     for (int i = 0; i < shells; ++i) {
433         if (i == 0)
434             start_shell(!keep_log, cmd);
435         else
436             start_shell(false, NULL);
437     }
438 
439     zx_status_t r = port_dispatch(&port, ZX_TIME_INFINITE, false);
440     printf("vc: port failure: %d\n", r);
441     return -1;
442 }
443