char: move pty chardev in its own file
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Acked-by: Eric Blake <eblake@redhat.com>
This commit is contained in:
		
							parent
							
								
									1fcb3841d0
								
							
						
					
					
						commit
						c3b7d62097
					
				@ -6,6 +6,7 @@ chardev-obj-y += char-io.o
 | 
				
			|||||||
chardev-obj-y += char-mux.o
 | 
					chardev-obj-y += char-mux.o
 | 
				
			||||||
chardev-obj-y += char-null.o
 | 
					chardev-obj-y += char-null.o
 | 
				
			||||||
chardev-obj-y += char-pipe.o
 | 
					chardev-obj-y += char-pipe.o
 | 
				
			||||||
 | 
					chardev-obj-$(CONFIG_POSIX) += char-pty.o
 | 
				
			||||||
chardev-obj-y += char-ringbuf.o
 | 
					chardev-obj-y += char-ringbuf.o
 | 
				
			||||||
chardev-obj-y += char-socket.o
 | 
					chardev-obj-y += char-socket.o
 | 
				
			||||||
chardev-obj-y += char-stdio.o
 | 
					chardev-obj-y += char-stdio.o
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										300
									
								
								chardev/char-pty.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										300
									
								
								chardev/char-pty.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,300 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * QEMU System Emulator
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (c) 2003-2008 Fabrice Bellard
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
				
			||||||
 | 
					 * of this software and associated documentation files (the "Software"), to deal
 | 
				
			||||||
 | 
					 * in the Software without restriction, including without limitation the rights
 | 
				
			||||||
 | 
					 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
				
			||||||
 | 
					 * copies of the Software, and to permit persons to whom the Software is
 | 
				
			||||||
 | 
					 * furnished to do so, subject to the following conditions:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The above copyright notice and this permission notice shall be included in
 | 
				
			||||||
 | 
					 * all copies or substantial portions of the Software.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
				
			||||||
 | 
					 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
				
			||||||
 | 
					 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 | 
				
			||||||
 | 
					 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
				
			||||||
 | 
					 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
				
			||||||
 | 
					 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
				
			||||||
 | 
					 * THE SOFTWARE.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#include "qemu/osdep.h"
 | 
				
			||||||
 | 
					#include "qapi/error.h"
 | 
				
			||||||
 | 
					#include "qemu-common.h"
 | 
				
			||||||
 | 
					#include "sysemu/char.h"
 | 
				
			||||||
 | 
					#include "io/channel-file.h"
 | 
				
			||||||
 | 
					#include "qemu/sockets.h"
 | 
				
			||||||
 | 
					#include "qemu/error-report.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "char-io.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__)      \
 | 
				
			||||||
 | 
					    || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
 | 
				
			||||||
 | 
					    || defined(__GLIBC__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
					    Chardev parent;
 | 
				
			||||||
 | 
					    QIOChannel *ioc;
 | 
				
			||||||
 | 
					    int read_bytes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Protected by the Chardev chr_write_lock.  */
 | 
				
			||||||
 | 
					    int connected;
 | 
				
			||||||
 | 
					    guint timer_tag;
 | 
				
			||||||
 | 
					    guint open_tag;
 | 
				
			||||||
 | 
					} PtyChardev;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define PTY_CHARDEV(obj) OBJECT_CHECK(PtyChardev, (obj), TYPE_CHARDEV_PTY)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void pty_chr_update_read_handler_locked(Chardev *chr);
 | 
				
			||||||
 | 
					static void pty_chr_state(Chardev *chr, int connected);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static gboolean pty_chr_timer(gpointer opaque)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    struct Chardev *chr = CHARDEV(opaque);
 | 
				
			||||||
 | 
					    PtyChardev *s = PTY_CHARDEV(opaque);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    qemu_mutex_lock(&chr->chr_write_lock);
 | 
				
			||||||
 | 
					    s->timer_tag = 0;
 | 
				
			||||||
 | 
					    s->open_tag = 0;
 | 
				
			||||||
 | 
					    if (!s->connected) {
 | 
				
			||||||
 | 
					        /* Next poll ... */
 | 
				
			||||||
 | 
					        pty_chr_update_read_handler_locked(chr);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    qemu_mutex_unlock(&chr->chr_write_lock);
 | 
				
			||||||
 | 
					    return FALSE;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Called with chr_write_lock held.  */
 | 
				
			||||||
 | 
					static void pty_chr_rearm_timer(Chardev *chr, int ms)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    PtyChardev *s = PTY_CHARDEV(chr);
 | 
				
			||||||
 | 
					    char *name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (s->timer_tag) {
 | 
				
			||||||
 | 
					        g_source_remove(s->timer_tag);
 | 
				
			||||||
 | 
					        s->timer_tag = 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (ms == 1000) {
 | 
				
			||||||
 | 
					        name = g_strdup_printf("pty-timer-secs-%s", chr->label);
 | 
				
			||||||
 | 
					        s->timer_tag = g_timeout_add_seconds(1, pty_chr_timer, chr);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        name = g_strdup_printf("pty-timer-ms-%s", chr->label);
 | 
				
			||||||
 | 
					        s->timer_tag = g_timeout_add(ms, pty_chr_timer, chr);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    g_source_set_name_by_id(s->timer_tag, name);
 | 
				
			||||||
 | 
					    g_free(name);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Called with chr_write_lock held.  */
 | 
				
			||||||
 | 
					static void pty_chr_update_read_handler_locked(Chardev *chr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    PtyChardev *s = PTY_CHARDEV(chr);
 | 
				
			||||||
 | 
					    GPollFD pfd;
 | 
				
			||||||
 | 
					    int rc;
 | 
				
			||||||
 | 
					    QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pfd.fd = fioc->fd;
 | 
				
			||||||
 | 
					    pfd.events = G_IO_OUT;
 | 
				
			||||||
 | 
					    pfd.revents = 0;
 | 
				
			||||||
 | 
					    do {
 | 
				
			||||||
 | 
					        rc = g_poll(&pfd, 1, 0);
 | 
				
			||||||
 | 
					    } while (rc == -1 && errno == EINTR);
 | 
				
			||||||
 | 
					    assert(rc >= 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (pfd.revents & G_IO_HUP) {
 | 
				
			||||||
 | 
					        pty_chr_state(chr, 0);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        pty_chr_state(chr, 1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void pty_chr_update_read_handler(Chardev *chr,
 | 
				
			||||||
 | 
					                                        GMainContext *context)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    qemu_mutex_lock(&chr->chr_write_lock);
 | 
				
			||||||
 | 
					    pty_chr_update_read_handler_locked(chr);
 | 
				
			||||||
 | 
					    qemu_mutex_unlock(&chr->chr_write_lock);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Called with chr_write_lock held.  */
 | 
				
			||||||
 | 
					static int char_pty_chr_write(Chardev *chr, const uint8_t *buf, int len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    PtyChardev *s = PTY_CHARDEV(chr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!s->connected) {
 | 
				
			||||||
 | 
					        /* guest sends data, check for (re-)connect */
 | 
				
			||||||
 | 
					        pty_chr_update_read_handler_locked(chr);
 | 
				
			||||||
 | 
					        if (!s->connected) {
 | 
				
			||||||
 | 
					            return 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return io_channel_send(s->ioc, buf, len);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static GSource *pty_chr_add_watch(Chardev *chr, GIOCondition cond)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    PtyChardev *s = PTY_CHARDEV(chr);
 | 
				
			||||||
 | 
					    if (!s->connected) {
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return qio_channel_create_watch(s->ioc, cond);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int pty_chr_read_poll(void *opaque)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    Chardev *chr = CHARDEV(opaque);
 | 
				
			||||||
 | 
					    PtyChardev *s = PTY_CHARDEV(opaque);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    s->read_bytes = qemu_chr_be_can_write(chr);
 | 
				
			||||||
 | 
					    return s->read_bytes;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static gboolean pty_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    Chardev *chr = CHARDEV(opaque);
 | 
				
			||||||
 | 
					    PtyChardev *s = PTY_CHARDEV(opaque);
 | 
				
			||||||
 | 
					    gsize len;
 | 
				
			||||||
 | 
					    uint8_t buf[CHR_READ_BUF_LEN];
 | 
				
			||||||
 | 
					    ssize_t ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    len = sizeof(buf);
 | 
				
			||||||
 | 
					    if (len > s->read_bytes) {
 | 
				
			||||||
 | 
					        len = s->read_bytes;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (len == 0) {
 | 
				
			||||||
 | 
					        return TRUE;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    ret = qio_channel_read(s->ioc, (char *)buf, len, NULL);
 | 
				
			||||||
 | 
					    if (ret <= 0) {
 | 
				
			||||||
 | 
					        pty_chr_state(chr, 0);
 | 
				
			||||||
 | 
					        return FALSE;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        pty_chr_state(chr, 1);
 | 
				
			||||||
 | 
					        qemu_chr_be_write(chr, buf, ret);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return TRUE;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static gboolean qemu_chr_be_generic_open_func(gpointer opaque)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    Chardev *chr = CHARDEV(opaque);
 | 
				
			||||||
 | 
					    PtyChardev *s = PTY_CHARDEV(opaque);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    s->open_tag = 0;
 | 
				
			||||||
 | 
					    qemu_chr_be_generic_open(chr);
 | 
				
			||||||
 | 
					    return FALSE;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Called with chr_write_lock held.  */
 | 
				
			||||||
 | 
					static void pty_chr_state(Chardev *chr, int connected)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    PtyChardev *s = PTY_CHARDEV(chr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!connected) {
 | 
				
			||||||
 | 
					        if (s->open_tag) {
 | 
				
			||||||
 | 
					            g_source_remove(s->open_tag);
 | 
				
			||||||
 | 
					            s->open_tag = 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        remove_fd_in_watch(chr);
 | 
				
			||||||
 | 
					        s->connected = 0;
 | 
				
			||||||
 | 
					        /* (re-)connect poll interval for idle guests: once per second.
 | 
				
			||||||
 | 
					         * We check more frequently in case the guests sends data to
 | 
				
			||||||
 | 
					         * the virtual device linked to our pty. */
 | 
				
			||||||
 | 
					        pty_chr_rearm_timer(chr, 1000);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        if (s->timer_tag) {
 | 
				
			||||||
 | 
					            g_source_remove(s->timer_tag);
 | 
				
			||||||
 | 
					            s->timer_tag = 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (!s->connected) {
 | 
				
			||||||
 | 
					            g_assert(s->open_tag == 0);
 | 
				
			||||||
 | 
					            s->connected = 1;
 | 
				
			||||||
 | 
					            s->open_tag = g_idle_add(qemu_chr_be_generic_open_func, chr);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (!chr->fd_in_tag) {
 | 
				
			||||||
 | 
					            chr->fd_in_tag = io_add_watch_poll(chr, s->ioc,
 | 
				
			||||||
 | 
					                                               pty_chr_read_poll,
 | 
				
			||||||
 | 
					                                               pty_chr_read,
 | 
				
			||||||
 | 
					                                               chr, NULL);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void char_pty_finalize(Object *obj)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    Chardev *chr = CHARDEV(obj);
 | 
				
			||||||
 | 
					    PtyChardev *s = PTY_CHARDEV(obj);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    qemu_mutex_lock(&chr->chr_write_lock);
 | 
				
			||||||
 | 
					    pty_chr_state(chr, 0);
 | 
				
			||||||
 | 
					    object_unref(OBJECT(s->ioc));
 | 
				
			||||||
 | 
					    if (s->timer_tag) {
 | 
				
			||||||
 | 
					        g_source_remove(s->timer_tag);
 | 
				
			||||||
 | 
					        s->timer_tag = 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    qemu_mutex_unlock(&chr->chr_write_lock);
 | 
				
			||||||
 | 
					    qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void char_pty_open(Chardev *chr,
 | 
				
			||||||
 | 
					                          ChardevBackend *backend,
 | 
				
			||||||
 | 
					                          bool *be_opened,
 | 
				
			||||||
 | 
					                          Error **errp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    PtyChardev *s;
 | 
				
			||||||
 | 
					    int master_fd, slave_fd;
 | 
				
			||||||
 | 
					    char pty_name[PATH_MAX];
 | 
				
			||||||
 | 
					    char *name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    master_fd = qemu_openpty_raw(&slave_fd, pty_name);
 | 
				
			||||||
 | 
					    if (master_fd < 0) {
 | 
				
			||||||
 | 
					        error_setg_errno(errp, errno, "Failed to create PTY");
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    close(slave_fd);
 | 
				
			||||||
 | 
					    qemu_set_nonblock(master_fd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    chr->filename = g_strdup_printf("pty:%s", pty_name);
 | 
				
			||||||
 | 
					    error_report("char device redirected to %s (label %s)",
 | 
				
			||||||
 | 
					                 pty_name, chr->label);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    s = PTY_CHARDEV(chr);
 | 
				
			||||||
 | 
					    s->ioc = QIO_CHANNEL(qio_channel_file_new_fd(master_fd));
 | 
				
			||||||
 | 
					    name = g_strdup_printf("chardev-pty-%s", chr->label);
 | 
				
			||||||
 | 
					    qio_channel_set_name(QIO_CHANNEL(s->ioc), name);
 | 
				
			||||||
 | 
					    g_free(name);
 | 
				
			||||||
 | 
					    s->timer_tag = 0;
 | 
				
			||||||
 | 
					    *be_opened = false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void char_pty_class_init(ObjectClass *oc, void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    ChardevClass *cc = CHARDEV_CLASS(oc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cc->open = char_pty_open;
 | 
				
			||||||
 | 
					    cc->chr_write = char_pty_chr_write;
 | 
				
			||||||
 | 
					    cc->chr_update_read_handler = pty_chr_update_read_handler;
 | 
				
			||||||
 | 
					    cc->chr_add_watch = pty_chr_add_watch;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const TypeInfo char_pty_type_info = {
 | 
				
			||||||
 | 
					    .name = TYPE_CHARDEV_PTY,
 | 
				
			||||||
 | 
					    .parent = TYPE_CHARDEV,
 | 
				
			||||||
 | 
					    .instance_size = sizeof(PtyChardev),
 | 
				
			||||||
 | 
					    .instance_finalize = char_pty_finalize,
 | 
				
			||||||
 | 
					    .class_init = char_pty_class_init,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void register_types(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    type_register_static(&char_pty_type_info);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type_init(register_types);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										258
									
								
								chardev/char.c
									
									
									
									
									
								
							
							
						
						
									
										258
									
								
								chardev/char.c
									
									
									
									
									
								
							@ -661,261 +661,6 @@ void qemu_chr_fe_take_focus(CharBackend *b)
 | 
				
			|||||||
    || defined(__GLIBC__)
 | 
					    || defined(__GLIBC__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define HAVE_CHARDEV_SERIAL 1
 | 
					#define HAVE_CHARDEV_SERIAL 1
 | 
				
			||||||
#define HAVE_CHARDEV_PTY 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef struct {
 | 
					 | 
				
			||||||
    Chardev parent;
 | 
					 | 
				
			||||||
    QIOChannel *ioc;
 | 
					 | 
				
			||||||
    int read_bytes;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* Protected by the Chardev chr_write_lock.  */
 | 
					 | 
				
			||||||
    int connected;
 | 
					 | 
				
			||||||
    guint timer_tag;
 | 
					 | 
				
			||||||
    guint open_tag;
 | 
					 | 
				
			||||||
} PtyChardev;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define PTY_CHARDEV(obj) OBJECT_CHECK(PtyChardev, (obj), TYPE_CHARDEV_PTY)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void pty_chr_update_read_handler_locked(Chardev *chr);
 | 
					 | 
				
			||||||
static void pty_chr_state(Chardev *chr, int connected);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static gboolean pty_chr_timer(gpointer opaque)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    struct Chardev *chr = CHARDEV(opaque);
 | 
					 | 
				
			||||||
    PtyChardev *s = PTY_CHARDEV(opaque);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    qemu_mutex_lock(&chr->chr_write_lock);
 | 
					 | 
				
			||||||
    s->timer_tag = 0;
 | 
					 | 
				
			||||||
    s->open_tag = 0;
 | 
					 | 
				
			||||||
    if (!s->connected) {
 | 
					 | 
				
			||||||
        /* Next poll ... */
 | 
					 | 
				
			||||||
        pty_chr_update_read_handler_locked(chr);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    qemu_mutex_unlock(&chr->chr_write_lock);
 | 
					 | 
				
			||||||
    return FALSE;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Called with chr_write_lock held.  */
 | 
					 | 
				
			||||||
static void pty_chr_rearm_timer(Chardev *chr, int ms)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    PtyChardev *s = PTY_CHARDEV(chr);
 | 
					 | 
				
			||||||
    char *name;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (s->timer_tag) {
 | 
					 | 
				
			||||||
        g_source_remove(s->timer_tag);
 | 
					 | 
				
			||||||
        s->timer_tag = 0;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (ms == 1000) {
 | 
					 | 
				
			||||||
        name = g_strdup_printf("pty-timer-secs-%s", chr->label);
 | 
					 | 
				
			||||||
        s->timer_tag = g_timeout_add_seconds(1, pty_chr_timer, chr);
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        name = g_strdup_printf("pty-timer-ms-%s", chr->label);
 | 
					 | 
				
			||||||
        s->timer_tag = g_timeout_add(ms, pty_chr_timer, chr);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    g_source_set_name_by_id(s->timer_tag, name);
 | 
					 | 
				
			||||||
    g_free(name);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Called with chr_write_lock held.  */
 | 
					 | 
				
			||||||
static void pty_chr_update_read_handler_locked(Chardev *chr)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    PtyChardev *s = PTY_CHARDEV(chr);
 | 
					 | 
				
			||||||
    GPollFD pfd;
 | 
					 | 
				
			||||||
    int rc;
 | 
					 | 
				
			||||||
    QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pfd.fd = fioc->fd;
 | 
					 | 
				
			||||||
    pfd.events = G_IO_OUT;
 | 
					 | 
				
			||||||
    pfd.revents = 0;
 | 
					 | 
				
			||||||
    do {
 | 
					 | 
				
			||||||
        rc = g_poll(&pfd, 1, 0);
 | 
					 | 
				
			||||||
    } while (rc == -1 && errno == EINTR);
 | 
					 | 
				
			||||||
    assert(rc >= 0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (pfd.revents & G_IO_HUP) {
 | 
					 | 
				
			||||||
        pty_chr_state(chr, 0);
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        pty_chr_state(chr, 1);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void pty_chr_update_read_handler(Chardev *chr,
 | 
					 | 
				
			||||||
                                        GMainContext *context)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    qemu_mutex_lock(&chr->chr_write_lock);
 | 
					 | 
				
			||||||
    pty_chr_update_read_handler_locked(chr);
 | 
					 | 
				
			||||||
    qemu_mutex_unlock(&chr->chr_write_lock);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Called with chr_write_lock held.  */
 | 
					 | 
				
			||||||
static int char_pty_chr_write(Chardev *chr, const uint8_t *buf, int len)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    PtyChardev *s = PTY_CHARDEV(chr);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (!s->connected) {
 | 
					 | 
				
			||||||
        /* guest sends data, check for (re-)connect */
 | 
					 | 
				
			||||||
        pty_chr_update_read_handler_locked(chr);
 | 
					 | 
				
			||||||
        if (!s->connected) {
 | 
					 | 
				
			||||||
            return 0;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return io_channel_send(s->ioc, buf, len);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static GSource *pty_chr_add_watch(Chardev *chr, GIOCondition cond)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    PtyChardev *s = PTY_CHARDEV(chr);
 | 
					 | 
				
			||||||
    if (!s->connected) {
 | 
					 | 
				
			||||||
        return NULL;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return qio_channel_create_watch(s->ioc, cond);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int pty_chr_read_poll(void *opaque)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    Chardev *chr = CHARDEV(opaque);
 | 
					 | 
				
			||||||
    PtyChardev *s = PTY_CHARDEV(opaque);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    s->read_bytes = qemu_chr_be_can_write(chr);
 | 
					 | 
				
			||||||
    return s->read_bytes;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static gboolean pty_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    Chardev *chr = CHARDEV(opaque);
 | 
					 | 
				
			||||||
    PtyChardev *s = PTY_CHARDEV(opaque);
 | 
					 | 
				
			||||||
    gsize len;
 | 
					 | 
				
			||||||
    uint8_t buf[CHR_READ_BUF_LEN];
 | 
					 | 
				
			||||||
    ssize_t ret;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    len = sizeof(buf);
 | 
					 | 
				
			||||||
    if (len > s->read_bytes)
 | 
					 | 
				
			||||||
        len = s->read_bytes;
 | 
					 | 
				
			||||||
    if (len == 0) {
 | 
					 | 
				
			||||||
        return TRUE;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    ret = qio_channel_read(s->ioc, (char *)buf, len, NULL);
 | 
					 | 
				
			||||||
    if (ret <= 0) {
 | 
					 | 
				
			||||||
        pty_chr_state(chr, 0);
 | 
					 | 
				
			||||||
        return FALSE;
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        pty_chr_state(chr, 1);
 | 
					 | 
				
			||||||
        qemu_chr_be_write(chr, buf, ret);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return TRUE;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static gboolean qemu_chr_be_generic_open_func(gpointer opaque)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    Chardev *chr = CHARDEV(opaque);
 | 
					 | 
				
			||||||
    PtyChardev *s = PTY_CHARDEV(opaque);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    s->open_tag = 0;
 | 
					 | 
				
			||||||
    qemu_chr_be_generic_open(chr);
 | 
					 | 
				
			||||||
    return FALSE;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Called with chr_write_lock held.  */
 | 
					 | 
				
			||||||
static void pty_chr_state(Chardev *chr, int connected)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    PtyChardev *s = PTY_CHARDEV(chr);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (!connected) {
 | 
					 | 
				
			||||||
        if (s->open_tag) {
 | 
					 | 
				
			||||||
            g_source_remove(s->open_tag);
 | 
					 | 
				
			||||||
            s->open_tag = 0;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        remove_fd_in_watch(chr);
 | 
					 | 
				
			||||||
        s->connected = 0;
 | 
					 | 
				
			||||||
        /* (re-)connect poll interval for idle guests: once per second.
 | 
					 | 
				
			||||||
         * We check more frequently in case the guests sends data to
 | 
					 | 
				
			||||||
         * the virtual device linked to our pty. */
 | 
					 | 
				
			||||||
        pty_chr_rearm_timer(chr, 1000);
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        if (s->timer_tag) {
 | 
					 | 
				
			||||||
            g_source_remove(s->timer_tag);
 | 
					 | 
				
			||||||
            s->timer_tag = 0;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (!s->connected) {
 | 
					 | 
				
			||||||
            g_assert(s->open_tag == 0);
 | 
					 | 
				
			||||||
            s->connected = 1;
 | 
					 | 
				
			||||||
            s->open_tag = g_idle_add(qemu_chr_be_generic_open_func, chr);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (!chr->fd_in_tag) {
 | 
					 | 
				
			||||||
            chr->fd_in_tag = io_add_watch_poll(chr, s->ioc,
 | 
					 | 
				
			||||||
                                               pty_chr_read_poll,
 | 
					 | 
				
			||||||
                                               pty_chr_read,
 | 
					 | 
				
			||||||
                                               chr, NULL);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void char_pty_finalize(Object *obj)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    Chardev *chr = CHARDEV(obj);
 | 
					 | 
				
			||||||
    PtyChardev *s = PTY_CHARDEV(obj);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    qemu_mutex_lock(&chr->chr_write_lock);
 | 
					 | 
				
			||||||
    pty_chr_state(chr, 0);
 | 
					 | 
				
			||||||
    object_unref(OBJECT(s->ioc));
 | 
					 | 
				
			||||||
    if (s->timer_tag) {
 | 
					 | 
				
			||||||
        g_source_remove(s->timer_tag);
 | 
					 | 
				
			||||||
        s->timer_tag = 0;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    qemu_mutex_unlock(&chr->chr_write_lock);
 | 
					 | 
				
			||||||
    qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void char_pty_open(Chardev *chr,
 | 
					 | 
				
			||||||
                          ChardevBackend *backend,
 | 
					 | 
				
			||||||
                          bool *be_opened,
 | 
					 | 
				
			||||||
                          Error **errp)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    PtyChardev *s;
 | 
					 | 
				
			||||||
    int master_fd, slave_fd;
 | 
					 | 
				
			||||||
    char pty_name[PATH_MAX];
 | 
					 | 
				
			||||||
    char *name;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    master_fd = qemu_openpty_raw(&slave_fd, pty_name);
 | 
					 | 
				
			||||||
    if (master_fd < 0) {
 | 
					 | 
				
			||||||
        error_setg_errno(errp, errno, "Failed to create PTY");
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    close(slave_fd);
 | 
					 | 
				
			||||||
    qemu_set_nonblock(master_fd);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    chr->filename = g_strdup_printf("pty:%s", pty_name);
 | 
					 | 
				
			||||||
    error_report("char device redirected to %s (label %s)",
 | 
					 | 
				
			||||||
                 pty_name, chr->label);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    s = PTY_CHARDEV(chr);
 | 
					 | 
				
			||||||
    s->ioc = QIO_CHANNEL(qio_channel_file_new_fd(master_fd));
 | 
					 | 
				
			||||||
    name = g_strdup_printf("chardev-pty-%s", chr->label);
 | 
					 | 
				
			||||||
    qio_channel_set_name(QIO_CHANNEL(s->ioc), name);
 | 
					 | 
				
			||||||
    g_free(name);
 | 
					 | 
				
			||||||
    s->timer_tag = 0;
 | 
					 | 
				
			||||||
    *be_opened = false;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void char_pty_class_init(ObjectClass *oc, void *data)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    ChardevClass *cc = CHARDEV_CLASS(oc);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    cc->open = char_pty_open;
 | 
					 | 
				
			||||||
    cc->chr_write = char_pty_chr_write;
 | 
					 | 
				
			||||||
    cc->chr_update_read_handler = pty_chr_update_read_handler;
 | 
					 | 
				
			||||||
    cc->chr_add_watch = pty_chr_add_watch;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static const TypeInfo char_pty_type_info = {
 | 
					 | 
				
			||||||
    .name = TYPE_CHARDEV_PTY,
 | 
					 | 
				
			||||||
    .parent = TYPE_CHARDEV,
 | 
					 | 
				
			||||||
    .instance_size = sizeof(PtyChardev),
 | 
					 | 
				
			||||||
    .instance_finalize = char_pty_finalize,
 | 
					 | 
				
			||||||
    .class_init = char_pty_class_init,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void tty_serial_init(int fd, int speed,
 | 
					static void tty_serial_init(int fd, int speed,
 | 
				
			||||||
                            int parity, int data_bits, int stop_bits)
 | 
					                            int parity, int data_bits, int stop_bits)
 | 
				
			||||||
@ -2175,9 +1920,6 @@ static void register_types(void)
 | 
				
			|||||||
#ifdef HAVE_CHARDEV_PARPORT
 | 
					#ifdef HAVE_CHARDEV_PARPORT
 | 
				
			||||||
    type_register_static(&char_parallel_type_info);
 | 
					    type_register_static(&char_parallel_type_info);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#ifdef HAVE_CHARDEV_PTY
 | 
					 | 
				
			||||||
    type_register_static(&char_pty_type_info);
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* this must be done after machine init, since we register FEs with muxes
 | 
					    /* this must be done after machine init, since we register FEs with muxes
 | 
				
			||||||
     * as part of realize functions like serial_isa_realizefn when -nographic
 | 
					     * as part of realize functions like serial_isa_realizefn when -nographic
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user