char-mux.c (12275B)
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 390 if (!chr->be_open && CHARDEV_IS_MUX(chr)) { 391 open_muxes(chr); 392 } 393 394 return 0; 395} 396 397void resume_mux_open(void) 398{ 399 muxes_opened = true; 400 object_child_foreach(get_chardevs_root(), 401 chardev_options_parsed_cb, NULL); 402} 403 404static void char_mux_class_init(ObjectClass *oc, void *data) 405{ 406 ChardevClass *cc = CHARDEV_CLASS(oc); 407 408 cc->parse = qemu_chr_parse_mux; 409 cc->open = qemu_chr_open_mux; 410 cc->chr_write = mux_chr_write; 411 cc->chr_accept_input = mux_chr_accept_input; 412 cc->chr_add_watch = mux_chr_add_watch; 413 cc->chr_be_event = mux_chr_be_event; 414 cc->chr_update_read_handler = mux_chr_update_read_handlers; 415} 416 417static const TypeInfo char_mux_type_info = { 418 .name = TYPE_CHARDEV_MUX, 419 .parent = TYPE_CHARDEV, 420 .class_init = char_mux_class_init, 421 .instance_size = sizeof(MuxChardev), 422 .instance_finalize = char_mux_finalize, 423}; 424 425static void register_types(void) 426{ 427 type_register_static(&char_mux_type_info); 428} 429 430type_init(register_types);