qemu/chardev/char-mux.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 "qapi/error.h"
  27#include "qemu/module.h"
  28#include "qemu/option.h"
  29#include "chardev/char.h"
  30#include "sysemu/block-backend.h"
  31#include "chardev-internal.h"
  32
  33/* MUX driver for serial I/O splitting */
  34
  35/*
  36 * Set to false by suspend_mux_open.  Open events are delayed until
  37 * resume_mux_open.  Usually suspend_mux_open is called before
  38 * command line processing and resume_mux_open afterwards.
  39 */
  40static bool muxes_opened = true;
  41
  42/* Called with chr_write_lock held.  */
  43static int mux_chr_write(Chardev *chr, const uint8_t *buf, int len)
  44{
  45    MuxChardev *d = MUX_CHARDEV(chr);
  46    int ret;
  47    if (!d->timestamps) {
  48        ret = qemu_chr_fe_write(&d->chr, buf, len);
  49    } else {
  50        int i;
  51
  52        ret = 0;
  53        for (i = 0; i < len; i++) {
  54            if (d->linestart) {
  55                char buf1[64];
  56                int64_t ti;
  57                int secs;
  58
  59                ti = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
  60                if (d->timestamps_start == -1) {
  61                    d->timestamps_start = ti;
  62                }
  63                ti -= d->timestamps_start;
  64                secs = ti / 1000;
  65                snprintf(buf1, sizeof(buf1),
  66                         "[%02d:%02d:%02d.%03d] ",
  67                         secs / 3600,
  68                         (secs / 60) % 60,
  69                         secs % 60,
  70                         (int)(ti % 1000));
  71                /* XXX this blocks entire thread. Rewrite to use
  72                 * qemu_chr_fe_write and background I/O callbacks */
  73                qemu_chr_fe_write_all(&d->chr,
  74                                      (uint8_t *)buf1, strlen(buf1));
  75                d->linestart = 0;
  76            }
  77            ret += qemu_chr_fe_write(&d->chr, buf + i, 1);
  78            if (buf[i] == '\n') {
  79                d->linestart = 1;
  80            }
  81        }
  82    }
  83    return ret;
  84}
  85
  86static const char * const mux_help[] = {
  87    "% h    print this help\n\r",
  88    "% x    exit emulator\n\r",
  89    "% s    save disk data back to file (if -snapshot)\n\r",
  90    "% t    toggle console timestamps\n\r",
  91    "% b    send break (magic sysrq)\n\r",
  92    "% c    switch between console and monitor\n\r",
  93    "% %  sends %\n\r",
  94    NULL
  95};
  96
  97int term_escape_char = 0x01; /* ctrl-a is used for escape */
  98static void mux_print_help(Chardev *chr)
  99{
 100    int i, j;
 101    char ebuf[15] = "Escape-Char";
 102    char cbuf[50] = "\n\r";
 103
 104    if (term_escape_char > 0 && term_escape_char < 26) {
 105        snprintf(cbuf, sizeof(cbuf), "\n\r");
 106        snprintf(ebuf, sizeof(ebuf), "C-%c", term_escape_char - 1 + 'a');
 107    } else {
 108        snprintf(cbuf, sizeof(cbuf),
 109                 "\n\rEscape-Char set to Ascii: 0x%02x\n\r\n\r",
 110                 term_escape_char);
 111    }
 112    /* XXX this blocks entire thread. Rewrite to use
 113     * qemu_chr_fe_write and background I/O callbacks */
 114    qemu_chr_write_all(chr, (uint8_t *)cbuf, strlen(cbuf));
 115    for (i = 0; mux_help[i] != NULL; i++) {
 116        for (j = 0; mux_help[i][j] != '\0'; j++) {
 117            if (mux_help[i][j] == '%') {
 118                qemu_chr_write_all(chr, (uint8_t *)ebuf, strlen(ebuf));
 119            } else {
 120                qemu_chr_write_all(chr, (uint8_t *)&mux_help[i][j], 1);
 121            }
 122        }
 123    }
 124}
 125
 126static void mux_chr_send_event(MuxChardev *d, int mux_nr, QEMUChrEvent event)
 127{
 128    CharBackend *be = d->backends[mux_nr];
 129
 130    if (be && be->chr_event) {
 131        be->chr_event(be->opaque, event);
 132    }
 133}
 134
 135static void mux_chr_be_event(Chardev *chr, QEMUChrEvent event)
 136{
 137    MuxChardev *d = MUX_CHARDEV(chr);
 138
 139    if (d->focus != -1) {
 140        mux_chr_send_event(d, d->focus, event);
 141    }
 142}
 143
 144static int mux_proc_byte(Chardev *chr, MuxChardev *d, int ch)
 145{
 146    if (d->term_got_escape) {
 147        d->term_got_escape = 0;
 148        if (ch == term_escape_char) {
 149            goto send_char;
 150        }
 151        switch (ch) {
 152        case '?':
 153        case 'h':
 154            mux_print_help(chr);
 155            break;
 156        case 'x':
 157            {
 158                 const char *term =  "QEMU: Terminated\n\r";
 159                 qemu_chr_write_all(chr, (uint8_t *)term, strlen(term));
 160                 exit(0);
 161                 break;
 162            }
 163        case 's':
 164            blk_commit_all();
 165            break;
 166        case 'b':
 167            qemu_chr_be_event(chr, CHR_EVENT_BREAK);
 168            break;
 169        case 'c':
 170            assert(d->mux_cnt > 0); /* handler registered with first fe */
 171            /* Switch to the next registered device */
 172            mux_set_focus(chr, (d->focus + 1) % d->mux_cnt);
 173            break;
 174        case 't':
 175            d->timestamps = !d->timestamps;
 176            d->timestamps_start = -1;
 177            d->linestart = 0;
 178            break;
 179        }
 180    } else if (ch == term_escape_char) {
 181        d->term_got_escape = 1;
 182    } else {
 183    send_char:
 184        return 1;
 185    }
 186    return 0;
 187}
 188
 189static void mux_chr_accept_input(Chardev *chr)
 190{
 191    MuxChardev *d = MUX_CHARDEV(chr);
 192    int m = d->focus;
 193    CharBackend *be = d->backends[m];
 194
 195    while (be && d->prod[m] != d->cons[m] &&
 196           be->chr_can_read && be->chr_can_read(be->opaque)) {
 197        be->chr_read(be->opaque,
 198                     &d->buffer[m][d->cons[m]++ & MUX_BUFFER_MASK], 1);
 199    }
 200}
 201
 202static int mux_chr_can_read(void *opaque)
 203{
 204    MuxChardev *d = MUX_CHARDEV(opaque);
 205    int m = d->focus;
 206    CharBackend *be = d->backends[m];
 207
 208    if ((d->prod[m] - d->cons[m]) < MUX_BUFFER_SIZE) {
 209        return 1;
 210    }
 211
 212    if (be && be->chr_can_read) {
 213        return be->chr_can_read(be->opaque);
 214    }
 215
 216    return 0;
 217}
 218
 219static void mux_chr_read(void *opaque, const uint8_t *buf, int size)
 220{
 221    Chardev *chr = CHARDEV(opaque);
 222    MuxChardev *d = MUX_CHARDEV(opaque);
 223    int m = d->focus;
 224    CharBackend *be = d->backends[m];
 225    int i;
 226
 227    mux_chr_accept_input(opaque);
 228
 229    for (i = 0; i < size; i++)
 230        if (mux_proc_byte(chr, d, buf[i])) {
 231            if (d->prod[m] == d->cons[m] &&
 232                be && be->chr_can_read &&
 233                be->chr_can_read(be->opaque)) {
 234                be->chr_read(be->opaque, &buf[i], 1);
 235            } else {
 236                d->buffer[m][d->prod[m]++ & MUX_BUFFER_MASK] = buf[i];
 237            }
 238        }
 239}
 240
 241void mux_chr_send_all_event(Chardev *chr, QEMUChrEvent event)
 242{
 243    MuxChardev *d = MUX_CHARDEV(chr);
 244    int i;
 245
 246    if (!muxes_opened) {
 247        return;
 248    }
 249
 250    /* Send the event to all registered listeners */
 251    for (i = 0; i < d->mux_cnt; i++) {
 252        mux_chr_send_event(d, i, event);
 253    }
 254}
 255
 256static void mux_chr_event(void *opaque, QEMUChrEvent event)
 257{
 258    mux_chr_send_all_event(CHARDEV(opaque), event);
 259}
 260
 261static GSource *mux_chr_add_watch(Chardev *s, GIOCondition cond)
 262{
 263    MuxChardev *d = MUX_CHARDEV(s);
 264    Chardev *chr = qemu_chr_fe_get_driver(&d->chr);
 265    ChardevClass *cc = CHARDEV_GET_CLASS(chr);
 266
 267    if (!cc->chr_add_watch) {
 268        return NULL;
 269    }
 270
 271    return cc->chr_add_watch(chr, cond);
 272}
 273
 274static void char_mux_finalize(Object *obj)
 275{
 276    MuxChardev *d = MUX_CHARDEV(obj);
 277    int i;
 278
 279    for (i = 0; i < d->mux_cnt; i++) {
 280        CharBackend *be = d->backends[i];
 281        if (be) {
 282            be->chr = NULL;
 283        }
 284    }
 285    qemu_chr_fe_deinit(&d->chr, false);
 286}
 287
 288static void mux_chr_update_read_handlers(Chardev *chr)
 289{
 290    MuxChardev *d = MUX_CHARDEV(chr);
 291
 292    /* Fix up the real driver with mux routines */
 293    qemu_chr_fe_set_handlers_full(&d->chr,
 294                                  mux_chr_can_read,
 295                                  mux_chr_read,
 296                                  mux_chr_event,
 297                                  NULL,
 298                                  chr,
 299                                  chr->gcontext, true, false);
 300}
 301
 302void mux_set_focus(Chardev *chr, int focus)
 303{
 304    MuxChardev *d = MUX_CHARDEV(chr);
 305
 306    assert(focus >= 0);
 307    assert(focus < d->mux_cnt);
 308
 309    if (d->focus != -1) {
 310        mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT);
 311    }
 312
 313    d->focus = focus;
 314    chr->be = d->backends[focus];
 315    mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN);
 316}
 317
 318static void qemu_chr_open_mux(Chardev *chr,
 319                              ChardevBackend *backend,
 320                              bool *be_opened,
 321                              Error **errp)
 322{
 323    ChardevMux *mux = backend->u.mux.data;
 324    Chardev *drv;
 325    MuxChardev *d = MUX_CHARDEV(chr);
 326
 327    drv = qemu_chr_find(mux->chardev);
 328    if (drv == NULL) {
 329        error_setg(errp, "mux: base chardev %s not found", mux->chardev);
 330        return;
 331    }
 332
 333    d->focus = -1;
 334    /* only default to opened state if we've realized the initial
 335     * set of muxes
 336     */
 337    *be_opened = muxes_opened;
 338    qemu_chr_fe_init(&d->chr, drv, errp);
 339}
 340
 341static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend,
 342                               Error **errp)
 343{
 344    const char *chardev = qemu_opt_get(opts, "chardev");
 345    ChardevMux *mux;
 346
 347    if (chardev == NULL) {
 348        error_setg(errp, "chardev: mux: no chardev given");
 349        return;
 350    }
 351    backend->type = CHARDEV_BACKEND_KIND_MUX;
 352    mux = backend->u.mux.data = g_new0(ChardevMux, 1);
 353    qemu_chr_parse_common(opts, qapi_ChardevMux_base(mux));
 354    mux->chardev = g_strdup(chardev);
 355}
 356
 357/**
 358 * Called after processing of default and command-line-specified
 359 * chardevs to deliver CHR_EVENT_OPENED events to any FEs attached
 360 * to a mux chardev. This is done here to ensure that
 361 * output/prompts/banners are only displayed for the FE that has
 362 * focus when initial command-line processing/machine init is
 363 * completed.
 364 *
 365 * After this point, any new FE attached to any new or existing
 366 * mux will receive CHR_EVENT_OPENED notifications for the BE
 367 * immediately.
 368 */
 369static void open_muxes(Chardev *chr)
 370{
 371    /* send OPENED to all already-attached FEs */
 372    mux_chr_send_all_event(chr, CHR_EVENT_OPENED);
 373
 374    /*
 375     * mark mux as OPENED so any new FEs will immediately receive
 376     * OPENED event
 377     */
 378    chr->be_open = 1;
 379}
 380
 381void suspend_mux_open(void)
 382{
 383    muxes_opened = false;
 384}
 385
 386static int chardev_options_parsed_cb(Object *child, void *opaque)
 387{
 388    Chardev *chr = (Chardev *)child;
 389    ChardevClass *class = CHARDEV_GET_CLASS(chr);
 390
 391    if (!chr->be_open && class->chr_options_parsed) {
 392        class->chr_options_parsed(chr);
 393    }
 394
 395    return 0;
 396}
 397
 398void resume_mux_open(void)
 399{
 400    muxes_opened = true;
 401    object_child_foreach(get_chardevs_root(),
 402                         chardev_options_parsed_cb, NULL);
 403}
 404
 405static void char_mux_class_init(ObjectClass *oc, void *data)
 406{
 407    ChardevClass *cc = CHARDEV_CLASS(oc);
 408
 409    cc->parse = qemu_chr_parse_mux;
 410    cc->open = qemu_chr_open_mux;
 411    cc->chr_write = mux_chr_write;
 412    cc->chr_accept_input = mux_chr_accept_input;
 413    cc->chr_add_watch = mux_chr_add_watch;
 414    cc->chr_be_event = mux_chr_be_event;
 415    cc->chr_options_parsed = open_muxes;
 416    cc->chr_update_read_handler = mux_chr_update_read_handlers;
 417}
 418
 419static const TypeInfo char_mux_type_info = {
 420    .name = TYPE_CHARDEV_MUX,
 421    .parent = TYPE_CHARDEV,
 422    .class_init = char_mux_class_init,
 423    .instance_size = sizeof(MuxChardev),
 424    .instance_finalize = char_mux_finalize,
 425};
 426
 427static void register_types(void)
 428{
 429    type_register_static(&char_mux_type_info);
 430}
 431
 432type_init(register_types);
 433