dc4a8fbd41435910226d8cb4ddd2f15d02aa4115
[akaros.git] / user / vmm / virtio_lguest_console.c
1 /* Virtio helper functions from linux/tools/lguest/lguest.c
2  *
3  * Copyright (C) 1991-2016, the Linux Kernel authors
4  * Copyright (c) 2016 Google Inc.
5  *
6  * Author:
7  *  Rusty Russell <rusty@rustcorp.com.au>
8  *  Michael Taufen <mtaufen@gmail.com>
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License as
12  * published by the Free Software Foundation; either version 2 of
13  * the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * The code from lguest.c has been modified for Akaros.
21  *
22  * Original linux/tools/lguest/lguest.c:
23  *   https://github.com/torvalds/linux/blob/v4.5/tools/lguest/lguest.c
24  *   most recent hash on the file as of v4.5 tag:
25  *     e523caa601f4a7c2fa1ecd040db921baf7453798
26  */
27
28 /* This is the routine which handles console input (ie. stdin). */
29 static void console_input(struct virtqueue *vq)
30 {
31         int len;
32         unsigned int head, in_num, out_num;
33         struct console_abort *abort = vq->dev->priv;
34         struct iovec iov[vq->vring.num];
35
36         /* Make sure there's a descriptor available. */
37         head = wait_for_vq_desc(vq, iov, &out_num, &in_num);
38         if (out_num)
39                 bad_driver_vq(vq, "Output buffers in console in queue?");
40
41         /* Read into it.  This is where we usually wait. */
42         len = readv(STDIN_FILENO, iov, in_num);
43         if (len <= 0) {
44                 /* Ran out of input? */
45                 warnx("Failed to get console input, ignoring console.");
46                 /*
47                  * For simplicity, dying threads kill the whole Launcher.  So
48                  * just nap here.
49                  */
50                 for (;;)
51                         pause();
52         }
53
54         /* Tell the Guest we used a buffer. */
55         add_used_and_trigger(vq, head, len);
56
57         /*
58          * Three ^C within one second?  Exit.
59          *
60          * This is such a hack, but works surprisingly well.  Each ^C has to
61          * be in a buffer by itself, so they can't be too fast.  But we check
62          * that we get three within about a second, so they can't be too
63          * slow.
64          */
65         if (len != 1 || ((char *)iov[0].iov_base)[0] != 3) {
66                 abort->count = 0;
67                 return;
68         }
69
70         abort->count++;
71         if (abort->count == 1)
72                 gettimeofday(&abort->start, NULL);
73         else if (abort->count == 3) {
74                 struct timeval now;
75
76                 gettimeofday(&now, NULL);
77                 /* Kill all Launcher processes with SIGINT, like normal ^C */
78                 if (now.tv_sec <= abort->start.tv_sec+1)
79                         kill(0, SIGINT);
80                 abort->count = 0;
81         }
82 }
83
84 /* This is the routine which handles console output (ie. stdout). */
85 static void console_output(struct virtqueue *vq)
86 {
87         unsigned int head, out, in;
88         struct iovec iov[vq->vring.num];
89
90         /* We usually wait in here, for the Guest to give us something. */
91         head = wait_for_vq_desc(vq, iov, &out, &in);
92         if (in)
93                 bad_driver_vq(vq, "Input buffers in console output queue?");
94
95         /* writev can return a partial write, so we loop here. */
96         while (!iov_empty(iov, out)) {
97                 int len = writev(STDOUT_FILENO, iov, out);
98
99                 if (len <= 0) {
100                         warn("Write to stdout gave %i (%d)", len, errno);
101                         break;
102                 }
103                 iov_consume(vq->dev, iov, out, NULL, len);
104         }
105
106         /*
107          * We're finished with that buffer: if we're going to sleep,
108          * wait_for_vq_desc() will prod the Guest with an interrupt.
109          */
110         add_used(vq, head, 0);
111 }