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