qemu/chardev/char-pty.c
<<
>>
Prefs
   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#include "qemu/osdep.h"
  25#include "qapi/error.h"
  26#include "qemu-common.h"
  27#include "chardev/char.h"
  28#include "io/channel-file.h"
  29#include "qemu/sockets.h"
  30#include "qemu/error-report.h"
  31
  32#include "chardev/char-io.h"
  33
  34typedef struct {
  35    Chardev parent;
  36    QIOChannel *ioc;
  37    int read_bytes;
  38
  39    int connected;
  40    GSource *timer_src;
  41} PtyChardev;
  42
  43#define PTY_CHARDEV(obj) OBJECT_CHECK(PtyChardev, (obj), TYPE_CHARDEV_PTY)
  44
  45static void pty_chr_state(Chardev *chr, int connected);
  46
  47static void pty_chr_timer_cancel(PtyChardev *s)
  48{
  49    if (s->timer_src) {
  50        g_source_destroy(s->timer_src);
  51        g_source_unref(s->timer_src);
  52        s->timer_src = NULL;
  53    }
  54}
  55
  56static gboolean pty_chr_timer(gpointer opaque)
  57{
  58    struct Chardev *chr = CHARDEV(opaque);
  59    PtyChardev *s = PTY_CHARDEV(opaque);
  60
  61    pty_chr_timer_cancel(s);
  62    if (!s->connected) {
  63        /* Next poll ... */
  64        qemu_chr_be_update_read_handlers(chr, chr->gcontext);
  65    }
  66    return FALSE;
  67}
  68
  69static void pty_chr_rearm_timer(Chardev *chr, int ms)
  70{
  71    PtyChardev *s = PTY_CHARDEV(chr);
  72    char *name;
  73
  74    pty_chr_timer_cancel(s);
  75    name = g_strdup_printf("pty-timer-%s", chr->label);
  76    s->timer_src = qemu_chr_timeout_add_ms(chr, ms, pty_chr_timer, chr);
  77    g_source_set_name(s->timer_src, name);
  78    g_free(name);
  79}
  80
  81static void pty_chr_update_read_handler(Chardev *chr)
  82{
  83    PtyChardev *s = PTY_CHARDEV(chr);
  84    GPollFD pfd;
  85    int rc;
  86    QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc);
  87
  88    pfd.fd = fioc->fd;
  89    pfd.events = G_IO_OUT;
  90    pfd.revents = 0;
  91    do {
  92        rc = g_poll(&pfd, 1, 0);
  93    } while (rc == -1 && errno == EINTR);
  94    assert(rc >= 0);
  95
  96    if (pfd.revents & G_IO_HUP) {
  97        pty_chr_state(chr, 0);
  98    } else {
  99        pty_chr_state(chr, 1);
 100    }
 101}
 102
 103static int char_pty_chr_write(Chardev *chr, const uint8_t *buf, int len)
 104{
 105    PtyChardev *s = PTY_CHARDEV(chr);
 106
 107    if (!s->connected) {
 108        return len;
 109    }
 110    return io_channel_send(s->ioc, buf, len);
 111}
 112
 113static GSource *pty_chr_add_watch(Chardev *chr, GIOCondition cond)
 114{
 115    PtyChardev *s = PTY_CHARDEV(chr);
 116    if (!s->connected) {
 117        return NULL;
 118    }
 119    return qio_channel_create_watch(s->ioc, cond);
 120}
 121
 122static int pty_chr_read_poll(void *opaque)
 123{
 124    Chardev *chr = CHARDEV(opaque);
 125    PtyChardev *s = PTY_CHARDEV(opaque);
 126
 127    s->read_bytes = qemu_chr_be_can_write(chr);
 128    return s->read_bytes;
 129}
 130
 131static gboolean pty_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 132{
 133    Chardev *chr = CHARDEV(opaque);
 134    PtyChardev *s = PTY_CHARDEV(opaque);
 135    gsize len;
 136    uint8_t buf[CHR_READ_BUF_LEN];
 137    ssize_t ret;
 138
 139    len = sizeof(buf);
 140    if (len > s->read_bytes) {
 141        len = s->read_bytes;
 142    }
 143    if (len == 0) {
 144        return TRUE;
 145    }
 146    ret = qio_channel_read(s->ioc, (char *)buf, len, NULL);
 147    if (ret <= 0) {
 148        pty_chr_state(chr, 0);
 149        return FALSE;
 150    } else {
 151        pty_chr_state(chr, 1);
 152        qemu_chr_be_write(chr, buf, ret);
 153    }
 154    return TRUE;
 155}
 156
 157static void pty_chr_state(Chardev *chr, int connected)
 158{
 159    PtyChardev *s = PTY_CHARDEV(chr);
 160
 161    if (!connected) {
 162        remove_fd_in_watch(chr);
 163        s->connected = 0;
 164        /* (re-)connect poll interval for idle guests: once per second.
 165         * We check more frequently in case the guests sends data to
 166         * the virtual device linked to our pty. */
 167        pty_chr_rearm_timer(chr, 1000);
 168    } else {
 169        pty_chr_timer_cancel(s);
 170        if (!s->connected) {
 171            s->connected = 1;
 172            qemu_chr_be_event(chr, CHR_EVENT_OPENED);
 173        }
 174        if (!chr->gsource) {
 175            chr->gsource = io_add_watch_poll(chr, s->ioc,
 176                                               pty_chr_read_poll,
 177                                               pty_chr_read,
 178                                               chr, chr->gcontext);
 179        }
 180    }
 181}
 182
 183static void char_pty_finalize(Object *obj)
 184{
 185    Chardev *chr = CHARDEV(obj);
 186    PtyChardev *s = PTY_CHARDEV(obj);
 187
 188    pty_chr_state(chr, 0);
 189    object_unref(OBJECT(s->ioc));
 190    pty_chr_timer_cancel(s);
 191    qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 192}
 193
 194static void char_pty_open(Chardev *chr,
 195                          ChardevBackend *backend,
 196                          bool *be_opened,
 197                          Error **errp)
 198{
 199    PtyChardev *s;
 200    int master_fd, slave_fd;
 201    char pty_name[PATH_MAX];
 202    char *name;
 203
 204    master_fd = qemu_openpty_raw(&slave_fd, pty_name);
 205    if (master_fd < 0) {
 206        error_setg_errno(errp, errno, "Failed to create PTY");
 207        return;
 208    }
 209
 210    close(slave_fd);
 211    qemu_set_nonblock(master_fd);
 212
 213    chr->filename = g_strdup_printf("pty:%s", pty_name);
 214    error_printf("char device redirected to %s (label %s)\n",
 215                 pty_name, chr->label);
 216
 217    s = PTY_CHARDEV(chr);
 218    s->ioc = QIO_CHANNEL(qio_channel_file_new_fd(master_fd));
 219    name = g_strdup_printf("chardev-pty-%s", chr->label);
 220    qio_channel_set_name(QIO_CHANNEL(s->ioc), name);
 221    g_free(name);
 222    s->timer_src = NULL;
 223    *be_opened = false;
 224}
 225
 226static void char_pty_class_init(ObjectClass *oc, void *data)
 227{
 228    ChardevClass *cc = CHARDEV_CLASS(oc);
 229
 230    cc->open = char_pty_open;
 231    cc->chr_write = char_pty_chr_write;
 232    cc->chr_update_read_handler = pty_chr_update_read_handler;
 233    cc->chr_add_watch = pty_chr_add_watch;
 234}
 235
 236static const TypeInfo char_pty_type_info = {
 237    .name = TYPE_CHARDEV_PTY,
 238    .parent = TYPE_CHARDEV,
 239    .instance_size = sizeof(PtyChardev),
 240    .instance_finalize = char_pty_finalize,
 241    .class_init = char_pty_class_init,
 242};
 243
 244static void register_types(void)
 245{
 246    type_register_static(&char_pty_type_info);
 247}
 248
 249type_init(register_types);
 250