xref: /qemu/chardev/char-pty.c (revision 06b40d250ecfa1633209c2e431a7a38acfd03a98)
1 /*
2  * QEMU System Emulator
3  *
4  * Copyright (c) 2003-2008 Fabrice Bellard
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24 
25 #include "qemu/osdep.h"
26 #include "qapi/error.h"
27 #include "chardev/char.h"
28 #include "io/channel-file.h"
29 #include "qemu/sockets.h"
30 #include "qemu/error-report.h"
31 #include "qemu/module.h"
32 #include "qemu/option.h"
33 #include "qemu/qemu-print.h"
34 
35 #include "chardev/char-io.h"
36 #include "qom/object.h"
37 
38 struct PtyChardev {
39     Chardev parent;
40     QIOChannel *ioc;
41     int read_bytes;
42 
43     int connected;
44     GSource *timer_src;
45     char *path;
46 };
47 typedef struct PtyChardev PtyChardev;
48 
49 DECLARE_INSTANCE_CHECKER(PtyChardev, PTY_CHARDEV,
50                          TYPE_CHARDEV_PTY)
51 
52 static void pty_chr_state(Chardev *chr, int connected);
53 
pty_chr_timer_cancel(PtyChardev * s)54 static void pty_chr_timer_cancel(PtyChardev *s)
55 {
56     if (s->timer_src) {
57         g_source_destroy(s->timer_src);
58         g_source_unref(s->timer_src);
59         s->timer_src = NULL;
60     }
61 }
62 
pty_chr_timer(gpointer opaque)63 static gboolean pty_chr_timer(gpointer opaque)
64 {
65     struct Chardev *chr = CHARDEV(opaque);
66     PtyChardev *s = PTY_CHARDEV(opaque);
67 
68     pty_chr_timer_cancel(s);
69     if (!s->connected) {
70         /* Next poll ... */
71         qemu_chr_be_update_read_handlers(chr, chr->gcontext);
72     }
73     return FALSE;
74 }
75 
pty_chr_rearm_timer(Chardev * chr,int ms)76 static void pty_chr_rearm_timer(Chardev *chr, int ms)
77 {
78     PtyChardev *s = PTY_CHARDEV(chr);
79     char *name;
80 
81     pty_chr_timer_cancel(s);
82     name = g_strdup_printf("pty-timer-%s", chr->label);
83     s->timer_src = qemu_chr_timeout_add_ms(chr, ms, pty_chr_timer, chr);
84     g_source_set_name(s->timer_src, name);
85     g_free(name);
86 }
87 
pty_chr_update_read_handler(Chardev * chr)88 static void pty_chr_update_read_handler(Chardev *chr)
89 {
90     PtyChardev *s = PTY_CHARDEV(chr);
91     GPollFD pfd;
92     int rc;
93     QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc);
94 
95     pfd.fd = fioc->fd;
96     pfd.events = G_IO_OUT;
97     pfd.revents = 0;
98     rc = RETRY_ON_EINTR(g_poll(&pfd, 1, 0));
99     assert(rc >= 0);
100 
101     if (pfd.revents & G_IO_HUP) {
102         pty_chr_state(chr, 0);
103     } else {
104         pty_chr_state(chr, 1);
105     }
106 }
107 
char_pty_chr_write(Chardev * chr,const uint8_t * buf,int len)108 static int char_pty_chr_write(Chardev *chr, const uint8_t *buf, int len)
109 {
110     PtyChardev *s = PTY_CHARDEV(chr);
111     GPollFD pfd;
112     int rc;
113 
114     if (s->connected) {
115         return io_channel_send(s->ioc, buf, len);
116     }
117 
118     /*
119      * The other side might already be re-connected, but the timer might
120      * not have fired yet. So let's check here whether we can write again:
121      */
122     pfd.fd = QIO_CHANNEL_FILE(s->ioc)->fd;
123     pfd.events = G_IO_OUT;
124     pfd.revents = 0;
125     rc = RETRY_ON_EINTR(g_poll(&pfd, 1, 0));
126     g_assert(rc >= 0);
127     if (!(pfd.revents & G_IO_HUP) && (pfd.revents & G_IO_OUT)) {
128         io_channel_send(s->ioc, buf, len);
129     }
130 
131     return len;
132 }
133 
pty_chr_add_watch(Chardev * chr,GIOCondition cond)134 static GSource *pty_chr_add_watch(Chardev *chr, GIOCondition cond)
135 {
136     PtyChardev *s = PTY_CHARDEV(chr);
137     if (!s->connected) {
138         return NULL;
139     }
140     return qio_channel_create_watch(s->ioc, cond);
141 }
142 
pty_chr_read_poll(void * opaque)143 static int pty_chr_read_poll(void *opaque)
144 {
145     Chardev *chr = CHARDEV(opaque);
146     PtyChardev *s = PTY_CHARDEV(opaque);
147 
148     s->read_bytes = qemu_chr_be_can_write(chr);
149     return s->read_bytes;
150 }
151 
pty_chr_read(QIOChannel * chan,GIOCondition cond,void * opaque)152 static gboolean pty_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
153 {
154     Chardev *chr = CHARDEV(opaque);
155     PtyChardev *s = PTY_CHARDEV(opaque);
156     gsize len;
157     uint8_t buf[CHR_READ_BUF_LEN];
158     ssize_t ret;
159 
160     len = sizeof(buf);
161     if (len > s->read_bytes) {
162         len = s->read_bytes;
163     }
164     if (len == 0) {
165         return TRUE;
166     }
167     ret = qio_channel_read(s->ioc, (char *)buf, len, NULL);
168     if (ret <= 0) {
169         pty_chr_state(chr, 0);
170         return FALSE;
171     } else {
172         pty_chr_state(chr, 1);
173         qemu_chr_be_write(chr, buf, ret);
174     }
175     return TRUE;
176 }
177 
pty_chr_state(Chardev * chr,int connected)178 static void pty_chr_state(Chardev *chr, int connected)
179 {
180     PtyChardev *s = PTY_CHARDEV(chr);
181 
182     if (!connected) {
183         remove_fd_in_watch(chr);
184         if (s->connected) {
185             qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
186         }
187         s->connected = 0;
188         /* (re-)connect poll interval for idle guests: once per second.
189          * We check more frequently in case the guests sends data to
190          * the virtual device linked to our pty. */
191         pty_chr_rearm_timer(chr, 1000);
192     } else {
193         pty_chr_timer_cancel(s);
194         if (!s->connected) {
195             s->connected = 1;
196             qemu_chr_be_event(chr, CHR_EVENT_OPENED);
197         }
198         if (!chr->gsource) {
199             chr->gsource = io_add_watch_poll(chr, s->ioc,
200                                                pty_chr_read_poll,
201                                                pty_chr_read,
202                                                chr, chr->gcontext);
203         }
204     }
205 }
206 
char_pty_finalize(Object * obj)207 static void char_pty_finalize(Object *obj)
208 {
209     Chardev *chr = CHARDEV(obj);
210     PtyChardev *s = PTY_CHARDEV(obj);
211 
212     /* unlink symlink */
213     if (s->path) {
214         unlink(s->path);
215         g_free(s->path);
216     }
217 
218     pty_chr_state(chr, 0);
219     object_unref(OBJECT(s->ioc));
220     pty_chr_timer_cancel(s);
221 }
222 
223 #if defined HAVE_PTY_H
224 # include <pty.h>
225 #elif defined CONFIG_BSD
226 # include <termios.h>
227 # if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
228 #  include <libutil.h>
229 # else
230 #  include <util.h>
231 # endif
232 #elif defined CONFIG_SOLARIS
233 # include <termios.h>
234 # include <stropts.h>
235 #else
236 # include <termios.h>
237 #endif
238 
239 #ifdef __sun__
240 
241 #if !defined(HAVE_OPENPTY)
242 /* Once illumos has openpty(), this is going to be removed. */
openpty(int * amaster,int * aslave,char * name,struct termios * termp,struct winsize * winp)243 static int openpty(int *amaster, int *aslave, char *name,
244                    struct termios *termp, struct winsize *winp)
245 {
246     const char *slave;
247     int mfd = -1, sfd = -1;
248 
249     *amaster = *aslave = -1;
250 
251     mfd = open("/dev/ptmx", O_RDWR | O_NOCTTY);
252     if (mfd < 0) {
253         goto err;
254     }
255 
256     if (grantpt(mfd) == -1 || unlockpt(mfd) == -1) {
257         goto err;
258     }
259 
260     if ((slave = ptsname(mfd)) == NULL) {
261         goto err;
262     }
263 
264     if ((sfd = open(slave, O_RDONLY | O_NOCTTY)) == -1) {
265         goto err;
266     }
267 
268     if (ioctl(sfd, I_PUSH, "ptem") == -1 ||
269         (termp != NULL && tcgetattr(sfd, termp) < 0)) {
270         goto err;
271     }
272 
273     *amaster = mfd;
274     *aslave = sfd;
275 
276     if (winp) {
277         ioctl(sfd, TIOCSWINSZ, winp);
278     }
279 
280     return 0;
281 
282 err:
283     if (sfd != -1) {
284         close(sfd);
285     }
286     close(mfd);
287     return -1;
288 }
289 #endif
290 
cfmakeraw(struct termios * termios_p)291 static void cfmakeraw (struct termios *termios_p)
292 {
293     termios_p->c_iflag &=
294         ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
295     termios_p->c_oflag &= ~OPOST;
296     termios_p->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
297     termios_p->c_cflag &= ~(CSIZE | PARENB);
298     termios_p->c_cflag |= CS8;
299 
300     termios_p->c_cc[VMIN] = 0;
301     termios_p->c_cc[VTIME] = 0;
302 }
303 #endif
304 
305 /* like openpty() but also makes it raw; return master fd */
qemu_openpty_raw(int * aslave,char * pty_name)306 static int qemu_openpty_raw(int *aslave, char *pty_name)
307 {
308     int amaster;
309     struct termios tty;
310 #if defined(__OpenBSD__) || defined(__DragonFly__)
311     char pty_buf[PATH_MAX];
312 #define q_ptsname(x) pty_buf
313 #else
314     char *pty_buf = NULL;
315 #define q_ptsname(x) ptsname(x)
316 #endif
317 
318     if (openpty(&amaster, aslave, pty_buf, NULL, NULL) < 0) {
319         return -1;
320     }
321 
322     /* Set raw attributes on the pty. */
323     tcgetattr(*aslave, &tty);
324     cfmakeraw(&tty);
325     tcsetattr(*aslave, TCSAFLUSH, &tty);
326 
327     if (pty_name) {
328         strcpy(pty_name, q_ptsname(amaster));
329     }
330 
331     return amaster;
332 }
333 
char_pty_open(Chardev * chr,ChardevBackend * backend,bool * be_opened,Error ** errp)334 static void char_pty_open(Chardev *chr,
335                           ChardevBackend *backend,
336                           bool *be_opened,
337                           Error **errp)
338 {
339     PtyChardev *s;
340     int master_fd, slave_fd;
341     char pty_name[PATH_MAX];
342     char *name;
343     char *path = backend->u.pty.data->path;
344 
345     master_fd = qemu_openpty_raw(&slave_fd, pty_name);
346     if (master_fd < 0) {
347         error_setg_errno(errp, errno, "Failed to create PTY");
348         return;
349     }
350 
351     close(slave_fd);
352     if (!g_unix_set_fd_nonblocking(master_fd, true, NULL)) {
353         error_setg_errno(errp, errno, "Failed to set FD nonblocking");
354         return;
355     }
356 
357     chr->filename = g_strdup_printf("pty:%s", pty_name);
358     qemu_printf("char device redirected to %s (label %s)\n",
359                 pty_name, chr->label);
360 
361     s = PTY_CHARDEV(chr);
362     s->ioc = QIO_CHANNEL(qio_channel_file_new_fd(master_fd));
363     name = g_strdup_printf("chardev-pty-%s", chr->label);
364     qio_channel_set_name(s->ioc, name);
365     g_free(name);
366     s->timer_src = NULL;
367     *be_opened = false;
368 
369     /* create symbolic link */
370     if (path) {
371         int res = symlink(pty_name, path);
372 
373         if (res != 0) {
374             error_setg_errno(errp, errno, "Failed to create PTY symlink");
375         } else {
376             s->path = g_strdup(path);
377         }
378     }
379 }
380 
char_pty_parse(QemuOpts * opts,ChardevBackend * backend,Error ** errp)381 static void char_pty_parse(QemuOpts *opts, ChardevBackend *backend,
382                            Error **errp)
383 {
384     const char *path = qemu_opt_get(opts, "path");
385     ChardevPty *pty;
386 
387     backend->type = CHARDEV_BACKEND_KIND_PTY;
388     pty = backend->u.pty.data = g_new0(ChardevPty, 1);
389     qemu_chr_parse_common(opts, qapi_ChardevPty_base(pty));
390     pty->path = g_strdup(path);
391 }
392 
char_pty_class_init(ObjectClass * oc,const void * data)393 static void char_pty_class_init(ObjectClass *oc, const void *data)
394 {
395     ChardevClass *cc = CHARDEV_CLASS(oc);
396 
397     cc->parse = char_pty_parse;
398     cc->open = char_pty_open;
399     cc->chr_write = char_pty_chr_write;
400     cc->chr_update_read_handler = pty_chr_update_read_handler;
401     cc->chr_add_watch = pty_chr_add_watch;
402 }
403 
404 static const TypeInfo char_pty_type_info = {
405     .name = TYPE_CHARDEV_PTY,
406     .parent = TYPE_CHARDEV,
407     .instance_size = sizeof(PtyChardev),
408     .instance_finalize = char_pty_finalize,
409     .class_init = char_pty_class_init,
410 };
411 
register_types(void)412 static void register_types(void)
413 {
414     type_register_static(&char_pty_type_info);
415 }
416 
417 type_init(register_types);
418