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{
 117    qemu_mutex_lock(&chr->chr_write_lock);
 118    pty_chr_update_read_handler_locked(chr);
 119    qemu_mutex_unlock(&chr->chr_write_lock);
 120}
 121
 122/* Called with chr_write_lock held.  */
 123static int char_pty_chr_write(Chardev *chr, const uint8_t *buf, int len)
 124{
 125    PtyChardev *s = PTY_CHARDEV(chr);
 126
 127    if (!s->connected) {
 128        /* guest sends data, check for (re-)connect */
 129        pty_chr_update_read_handler_locked(chr);
 130        if (!s->connected) {
 131            return len;
 132        }
 133    }
 134    return io_channel_send(s->ioc, buf, len);
 135}
 136
 137static GSource *pty_chr_add_watch(Chardev *chr, GIOCondition cond)
 138{
 139    PtyChardev *s = PTY_CHARDEV(chr);
 140    if (!s->connected) {
 141        return NULL;
 142    }
 143    return qio_channel_create_watch(s->ioc, cond);
 144}
 145
 146static int pty_chr_read_poll(void *opaque)
 147{
 148    Chardev *chr = CHARDEV(opaque);
 149    PtyChardev *s = PTY_CHARDEV(opaque);
 150
 151    s->read_bytes = qemu_chr_be_can_write(chr);
 152    return s->read_bytes;
 153}
 154
 155static gboolean pty_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 156{
 157    Chardev *chr = CHARDEV(opaque);
 158    PtyChardev *s = PTY_CHARDEV(opaque);
 159    gsize len;
 160    uint8_t buf[CHR_READ_BUF_LEN];
 161    ssize_t ret;
 162
 163    len = sizeof(buf);
 164    if (len > s->read_bytes) {
 165        len = s->read_bytes;
 166    }
 167    if (len == 0) {
 168        return TRUE;
 169    }
 170    ret = qio_channel_read(s->ioc, (char *)buf, len, NULL);
 171    if (ret <= 0) {
 172        pty_chr_state(chr, 0);
 173        return FALSE;
 174    } else {
 175        pty_chr_state(chr, 1);
 176        qemu_chr_be_write(chr, buf, ret);
 177    }
 178    return TRUE;
 179}
 180
 181static gboolean qemu_chr_be_generic_open_func(gpointer opaque)
 182{
 183    Chardev *chr = CHARDEV(opaque);
 184    PtyChardev *s = PTY_CHARDEV(opaque);
 185
 186    s->open_tag = 0;
 187    qemu_chr_be_event(chr, CHR_EVENT_OPENED);
 188    return FALSE;
 189}
 190
 191/* Called with chr_write_lock held.  */
 192static void pty_chr_state(Chardev *chr, int connected)
 193{
 194    PtyChardev *s = PTY_CHARDEV(chr);
 195
 196    if (!connected) {
 197        if (s->open_tag) {
 198            g_source_remove(s->open_tag);
 199            s->open_tag = 0;
 200        }
 201        remove_fd_in_watch(chr);
 202        s->connected = 0;
 203        /* (re-)connect poll interval for idle guests: once per second.
 204         * We check more frequently in case the guests sends data to
 205         * the virtual device linked to our pty. */
 206        pty_chr_rearm_timer(chr, 1000);
 207    } else {
 208        if (s->timer_tag) {
 209            g_source_remove(s->timer_tag);
 210            s->timer_tag = 0;
 211        }
 212        if (!s->connected) {
 213            g_assert(s->open_tag == 0);
 214            s->connected = 1;
 215            s->open_tag = g_idle_add(qemu_chr_be_generic_open_func, chr);
 216        }
 217        if (!chr->gsource) {
 218            chr->gsource = io_add_watch_poll(chr, s->ioc,
 219                                               pty_chr_read_poll,
 220                                               pty_chr_read,
 221                                               chr, chr->gcontext);
 222        }
 223    }
 224}
 225
 226static void char_pty_finalize(Object *obj)
 227{
 228    Chardev *chr = CHARDEV(obj);
 229    PtyChardev *s = PTY_CHARDEV(obj);
 230
 231    qemu_mutex_lock(&chr->chr_write_lock);
 232    pty_chr_state(chr, 0);
 233    object_unref(OBJECT(s->ioc));
 234    if (s->timer_tag) {
 235        g_source_remove(s->timer_tag);
 236        s->timer_tag = 0;
 237    }
 238    qemu_mutex_unlock(&chr->chr_write_lock);
 239    qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 240}
 241
 242static void char_pty_open(Chardev *chr,
 243                          ChardevBackend *backend,
 244                          bool *be_opened,
 245                          Error **errp)
 246{
 247    PtyChardev *s;
 248    int master_fd, slave_fd;
 249    char pty_name[PATH_MAX];
 250    char *name;
 251
 252    master_fd = qemu_openpty_raw(&slave_fd, pty_name);
 253    if (master_fd < 0) {
 254        error_setg_errno(errp, errno, "Failed to create PTY");
 255        return;
 256    }
 257
 258    close(slave_fd);
 259    qemu_set_nonblock(master_fd);
 260
 261    chr->filename = g_strdup_printf("pty:%s", pty_name);
 262    error_report("char device redirected to %s (label %s)",
 263                 pty_name, chr->label);
 264
 265    s = PTY_CHARDEV(chr);
 266    s->ioc = QIO_CHANNEL(qio_channel_file_new_fd(master_fd));
 267    name = g_strdup_printf("chardev-pty-%s", chr->label);
 268    qio_channel_set_name(QIO_CHANNEL(s->ioc), name);
 269    g_free(name);
 270    s->timer_tag = 0;
 271    *be_opened = false;
 272}
 273
 274static void char_pty_class_init(ObjectClass *oc, void *data)
 275{
 276    ChardevClass *cc = CHARDEV_CLASS(oc);
 277
 278    cc->open = char_pty_open;
 279    cc->chr_write = char_pty_chr_write;
 280    cc->chr_update_read_handler = pty_chr_update_read_handler;
 281    cc->chr_add_watch = pty_chr_add_watch;
 282}
 283
 284static const TypeInfo char_pty_type_info = {
 285    .name = TYPE_CHARDEV_PTY,
 286    .parent = TYPE_CHARDEV,
 287    .instance_size = sizeof(PtyChardev),
 288    .instance_finalize = char_pty_finalize,
 289    .class_init = char_pty_class_init,
 290};
 291
 292static void register_types(void)
 293{
 294    type_register_static(&char_pty_type_info);
 295}
 296
 297type_init(register_types);
 298
 299#endif
 300