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
  34#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__)      \
  35    || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
  36    || defined(__GLIBC__)
  37
  38typedef struct {
  39    Chardev parent;
  40    QIOChannel *ioc;
  41    int read_bytes;
  42
  43    /* Protected by the Chardev chr_write_lock.  */
  44    int connected;
  45    guint timer_tag;
  46    guint open_tag;
  47} PtyChardev;
  48
  49#define PTY_CHARDEV(obj) OBJECT_CHECK(PtyChardev, (obj), TYPE_CHARDEV_PTY)
  50
  51static void pty_chr_update_read_handler_locked(Chardev *chr);
  52static void pty_chr_state(Chardev *chr, int connected);
  53
  54static gboolean pty_chr_timer(gpointer opaque)
  55{
  56    struct Chardev *chr = CHARDEV(opaque);
  57    PtyChardev *s = PTY_CHARDEV(opaque);
  58
  59    qemu_mutex_lock(&chr->chr_write_lock);
  60    s->timer_tag = 0;
  61    s->open_tag = 0;
  62    if (!s->connected) {
  63        /* Next poll ... */
  64        pty_chr_update_read_handler_locked(chr);
  65    }
  66    qemu_mutex_unlock(&chr->chr_write_lock);
  67    return FALSE;
  68}
  69
  70/* Called with chr_write_lock held.  */
  71static void pty_chr_rearm_timer(Chardev *chr, int ms)
  72{
  73    PtyChardev *s = PTY_CHARDEV(chr);
  74    char *name;
  75
  76    if (s->timer_tag) {
  77        g_source_remove(s->timer_tag);
  78        s->timer_tag = 0;
  79    }
  80
  81    if (ms == 1000) {
  82        name = g_strdup_printf("pty-timer-secs-%s", chr->label);
  83        s->timer_tag = g_timeout_add_seconds(1, pty_chr_timer, chr);
  84    } else {
  85        name = g_strdup_printf("pty-timer-ms-%s", chr->label);
  86        s->timer_tag = g_timeout_add(ms, pty_chr_timer, chr);
  87    }
  88    g_source_set_name_by_id(s->timer_tag, name);
  89    g_free(name);
  90}
  91
  92/* Called with chr_write_lock held.  */
  93static void pty_chr_update_read_handler_locked(Chardev *chr)
  94{
  95    PtyChardev *s = PTY_CHARDEV(chr);
  96    GPollFD pfd;
  97    int rc;
  98    QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc);
  99
 100    pfd.fd = fioc->fd;
 101    pfd.events = G_IO_OUT;
 102    pfd.revents = 0;
 103    do {
 104        rc = g_poll(&pfd, 1, 0);
 105    } while (rc == -1 && errno == EINTR);
 106    assert(rc >= 0);
 107
 108    if (pfd.revents & G_IO_HUP) {
 109        pty_chr_state(chr, 0);
 110    } else {
 111        pty_chr_state(chr, 1);
 112    }
 113}
 114
 115static void pty_chr_update_read_handler(Chardev *chr,
 116                                        GMainContext *context)
 117{
 118    qemu_mutex_lock(&chr->chr_write_lock);
 119    pty_chr_update_read_handler_locked(chr);
 120    qemu_mutex_unlock(&chr->chr_write_lock);
 121}
 122
 123/* Called with chr_write_lock held.  */
 124static int char_pty_chr_write(Chardev *chr, const uint8_t *buf, int len)
 125{
 126    PtyChardev *s = PTY_CHARDEV(chr);
 127
 128    if (!s->connected) {
 129        /* guest sends data, check for (re-)connect */
 130        pty_chr_update_read_handler_locked(chr);
 131        if (!s->connected) {
 132            return len;
 133        }
 134    }
 135    return io_channel_send(s->ioc, buf, len);
 136}
 137
 138static GSource *pty_chr_add_watch(Chardev *chr, GIOCondition cond)
 139{
 140    PtyChardev *s = PTY_CHARDEV(chr);
 141    if (!s->connected) {
 142        return NULL;
 143    }
 144    return qio_channel_create_watch(s->ioc, cond);
 145}
 146
 147static int pty_chr_read_poll(void *opaque)
 148{
 149    Chardev *chr = CHARDEV(opaque);
 150    PtyChardev *s = PTY_CHARDEV(opaque);
 151
 152    s->read_bytes = qemu_chr_be_can_write(chr);
 153    return s->read_bytes;
 154}
 155
 156static gboolean pty_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 157{
 158    Chardev *chr = CHARDEV(opaque);
 159    PtyChardev *s = PTY_CHARDEV(opaque);
 160    gsize len;
 161    uint8_t buf[CHR_READ_BUF_LEN];
 162    ssize_t ret;
 163
 164    len = sizeof(buf);
 165    if (len > s->read_bytes) {
 166        len = s->read_bytes;
 167    }
 168    if (len == 0) {
 169        return TRUE;
 170    }
 171    ret = qio_channel_read(s->ioc, (char *)buf, len, NULL);
 172    if (ret <= 0) {
 173        pty_chr_state(chr, 0);
 174        return FALSE;
 175    } else {
 176        pty_chr_state(chr, 1);
 177        qemu_chr_be_write(chr, buf, ret);
 178    }
 179    return TRUE;
 180}
 181
 182static gboolean qemu_chr_be_generic_open_func(gpointer opaque)
 183{
 184    Chardev *chr = CHARDEV(opaque);
 185    PtyChardev *s = PTY_CHARDEV(opaque);
 186
 187    s->open_tag = 0;
 188    qemu_chr_be_event(chr, CHR_EVENT_OPENED);
 189    return FALSE;
 190}
 191
 192/* Called with chr_write_lock held.  */
 193static void pty_chr_state(Chardev *chr, int connected)
 194{
 195    PtyChardev *s = PTY_CHARDEV(chr);
 196
 197    if (!connected) {
 198        if (s->open_tag) {
 199            g_source_remove(s->open_tag);
 200            s->open_tag = 0;
 201        }
 202        remove_fd_in_watch(chr);
 203        s->connected = 0;
 204        /* (re-)connect poll interval for idle guests: once per second.
 205         * We check more frequently in case the guests sends data to
 206         * the virtual device linked to our pty. */
 207        pty_chr_rearm_timer(chr, 1000);
 208    } else {
 209        if (s->timer_tag) {
 210            g_source_remove(s->timer_tag);
 211            s->timer_tag = 0;
 212        }
 213        if (!s->connected) {
 214            g_assert(s->open_tag == 0);
 215            s->connected = 1;
 216            s->open_tag = g_idle_add(qemu_chr_be_generic_open_func, chr);
 217        }
 218        if (!chr->gsource) {
 219            chr->gsource = io_add_watch_poll(chr, s->ioc,
 220                                               pty_chr_read_poll,
 221                                               pty_chr_read,
 222                                               chr, NULL);
 223        }
 224    }
 225}
 226
 227static void char_pty_finalize(Object *obj)
 228{
 229    Chardev *chr = CHARDEV(obj);
 230    PtyChardev *s = PTY_CHARDEV(obj);
 231
 232    qemu_mutex_lock(&chr->chr_write_lock);
 233    pty_chr_state(chr, 0);
 234    object_unref(OBJECT(s->ioc));
 235    if (s->timer_tag) {
 236        g_source_remove(s->timer_tag);
 237        s->timer_tag = 0;
 238    }
 239    qemu_mutex_unlock(&chr->chr_write_lock);
 240    qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 241}
 242
 243static void char_pty_open(Chardev *chr,
 244                          ChardevBackend *backend,
 245                          bool *be_opened,
 246                          Error **errp)
 247{
 248    PtyChardev *s;
 249    int master_fd, slave_fd;
 250    char pty_name[PATH_MAX];
 251    char *name;
 252
 253    master_fd = qemu_openpty_raw(&slave_fd, pty_name);
 254    if (master_fd < 0) {
 255        error_setg_errno(errp, errno, "Failed to create PTY");
 256        return;
 257    }
 258
 259    close(slave_fd);
 260    qemu_set_nonblock(master_fd);
 261
 262    chr->filename = g_strdup_printf("pty:%s", pty_name);
 263    error_report("char device redirected to %s (label %s)",
 264                 pty_name, chr->label);
 265
 266    s = PTY_CHARDEV(chr);
 267    s->ioc = QIO_CHANNEL(qio_channel_file_new_fd(master_fd));
 268    name = g_strdup_printf("chardev-pty-%s", chr->label);
 269    qio_channel_set_name(QIO_CHANNEL(s->ioc), name);
 270    g_free(name);
 271    s->timer_tag = 0;
 272    *be_opened = false;
 273}
 274
 275static void char_pty_class_init(ObjectClass *oc, void *data)
 276{
 277    ChardevClass *cc = CHARDEV_CLASS(oc);
 278
 279    cc->open = char_pty_open;
 280    cc->chr_write = char_pty_chr_write;
 281    cc->chr_update_read_handler = pty_chr_update_read_handler;
 282    cc->chr_add_watch = pty_chr_add_watch;
 283}
 284
 285static const TypeInfo char_pty_type_info = {
 286    .name = TYPE_CHARDEV_PTY,
 287    .parent = TYPE_CHARDEV,
 288    .instance_size = sizeof(PtyChardev),
 289    .instance_finalize = char_pty_finalize,
 290    .class_init = char_pty_class_init,
 291};
 292
 293static void register_types(void)
 294{
 295    type_register_static(&char_pty_type_info);
 296}
 297
 298type_init(register_types);
 299
 300#endif
 301