static void pty_chr_state(Chardev *chr, int connected) { PtyChardev *s = PTY_CHARDEV(chr); if (!connected) { remove_fd_in_watch(chr); s->connected = 0; /* (re-)connect poll interval for idle guests: once per second. * We check more frequently in case the guests sends data to * the virtual device linked to our pty. */ pty_chr_rearm_timer(chr, 1000); } else { pty_chr_timer_cancel(s); if (!s->connected) { s->connected = 1; qemu_chr_be_event(chr, CHR_EVENT_OPENED); } if (!chr->gsource) { chr->gsource = io_add_watch_poll(chr, s->ioc, pty_chr_read_poll, pty_chr_read, chr, chr->gcontext); } } }
static void vmc_state(SpiceCharDeviceInstance *sin, int connected) { SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin); #if SPICE_SERVER_VERSION < 0x000901 /* * spice-server calls the state callback for the agent channel when the * spice client connects / disconnects. Given that not the client but * the server is doing the parsing of the messages this is wrong as the * server is still listening. Worse, this causes the parser in the server * to go out of sync, so we ignore state calls for subtype vdagent * spicevmc chardevs. For the full story see: * http://lists.freedesktop.org/archives/spice-devel/2011-July/004837.html */ if (strcmp(sin->subtype, "vdagent") == 0) { return; } #endif if ((scd->chr->opened && connected) || (!scd->chr->opened && !connected)) { return; } qemu_chr_be_event(scd->chr, connected ? CHR_EVENT_OPENED : CHR_EVENT_CLOSED); }
static gboolean fd_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque) { Chardev *chr = CHARDEV(opaque); FDChardev *s = FD_CHARDEV(opaque); int len; uint8_t buf[CHR_READ_BUF_LEN]; ssize_t ret; len = sizeof(buf); if (len > s->max_size) { len = s->max_size; } if (len == 0) { return TRUE; } ret = qio_channel_read( chan, (gchar *)buf, len, NULL); if (ret == 0) { remove_fd_in_watch(chr); qemu_chr_be_event(chr, CHR_EVENT_CLOSED); return FALSE; } if (ret > 0) { qemu_chr_be_write(chr, buf, ret); } return TRUE; }
static void char_socket_finalize(Object *obj) { Chardev *chr = CHARDEV(obj); SocketChardev *s = SOCKET_CHARDEV(obj); tcp_chr_free_connection(chr); if (s->reconnect_timer) { g_source_remove(s->reconnect_timer); s->reconnect_timer = 0; } qapi_free_SocketAddress(s->addr); if (s->listen_tag) { g_source_remove(s->listen_tag); s->listen_tag = 0; } if (s->listen_ioc) { object_unref(OBJECT(s->listen_ioc)); } if (s->tls_creds) { object_unref(OBJECT(s->tls_creds)); } qemu_chr_be_event(chr, CHR_EVENT_CLOSED); }
static gboolean qemu_chr_be_generic_open_func(gpointer opaque) { Chardev *chr = CHARDEV(opaque); PtyChardev *s = PTY_CHARDEV(opaque); s->open_source = NULL; qemu_chr_be_event(chr, CHR_EVENT_OPENED); return FALSE; }
static void char_pty_finalize(Object *obj) { Chardev *chr = CHARDEV(obj); PtyChardev *s = PTY_CHARDEV(obj); pty_chr_state(chr, 0); object_unref(OBJECT(s->ioc)); pty_chr_timer_cancel(s); qemu_chr_be_event(chr, CHR_EVENT_CLOSED); }
static void char_udp_finalize(Object *obj) { Chardev *chr = CHARDEV(obj); UdpChardev *s = UDP_CHARDEV(obj); remove_fd_in_watch(chr); if (s->ioc) { object_unref(OBJECT(s->ioc)); } qemu_chr_be_event(chr, CHR_EVENT_CLOSED); }
/** * Called after processing of default and command-line-specified * chardevs to deliver CHR_EVENT_OPENED events to any FEs attached * to a mux chardev. This is done here to ensure that * output/prompts/banners are only displayed for the FE that has * focus when initial command-line processing/machine init is * completed. * * After this point, any new FE attached to any new or existing * mux will receive CHR_EVENT_OPENED notifications for the BE * immediately. */ static int open_muxes(Chardev *chr) { /* send OPENED to all already-attached FEs */ mux_chr_send_all_event(chr, CHR_EVENT_OPENED); /* * mark mux as OPENED so any new FEs will immediately receive * OPENED event */ qemu_chr_be_event(chr, CHR_EVENT_OPENED); return 0; }
/** * Called after processing of default and command-line-specified * chardevs to deliver CHR_EVENT_OPENED events to any FEs attached * to a mux chardev. This is done here to ensure that * output/prompts/banners are only displayed for the FE that has * focus when initial command-line processing/machine init is * completed. * * After this point, any new FE attached to any new or existing * mux will receive CHR_EVENT_OPENED notifications for the BE * immediately. */ static int open_muxes(Object *child, void *opaque) { if (CHARDEV_IS_MUX(child)) { /* send OPENED to all already-attached FEs */ mux_chr_send_all_event(CHARDEV(child), CHR_EVENT_OPENED); /* mark mux as OPENED so any new FEs will immediately receive * OPENED event */ qemu_chr_be_event(CHARDEV(child), CHR_EVENT_OPENED); } return 0; }
static void tcp_chr_process_IAC_bytes(Chardev *chr, SocketChardev *s, uint8_t *buf, int *size) { /* Handle any telnet client's basic IAC options to satisfy char by * char mode with no echo. All IAC options will be removed from * the buf and the do_telnetopt variable will be used to track the * state of the width of the IAC information. * * IAC commands come in sets of 3 bytes with the exception of the * "IAC BREAK" command and the double IAC. */ int i; int j = 0; for (i = 0; i < *size; i++) { if (s->do_telnetopt > 1) { if ((unsigned char)buf[i] == IAC && s->do_telnetopt == 2) { /* Double IAC means send an IAC */ if (j != i) { buf[j] = buf[i]; } j++; s->do_telnetopt = 1; } else { if ((unsigned char)buf[i] == IAC_BREAK && s->do_telnetopt == 2) { /* Handle IAC break commands by sending a serial break */ qemu_chr_be_event(chr, CHR_EVENT_BREAK); s->do_telnetopt++; } s->do_telnetopt++; } if (s->do_telnetopt >= 4) { s->do_telnetopt = 1; } } else { if ((unsigned char)buf[i] == IAC) { s->do_telnetopt = 2; } else { if (j != i) { buf[j] = buf[i]; } j++; } } } *size = j; }
static void char_fd_finalize(Object *obj) { Chardev *chr = CHARDEV(obj); FDChardev *s = FD_CHARDEV(obj); remove_fd_in_watch(chr); if (s->ioc_in) { object_unref(OBJECT(s->ioc_in)); } if (s->ioc_out) { object_unref(OBJECT(s->ioc_out)); } qemu_chr_be_event(chr, CHR_EVENT_CLOSED); }
static int mux_proc_byte(Chardev *chr, MuxChardev *d, int ch) { if (d->term_got_escape) { d->term_got_escape = 0; if (ch == term_escape_char) { goto send_char; } switch (ch) { case '?': case 'h': mux_print_help(chr); break; case 'x': { const char *term = "QEMU: Terminated\n\r"; qemu_chr_write_all(chr, (uint8_t *)term, strlen(term)); exit(0); break; } case 's': blk_commit_all(); break; case 'b': qemu_chr_be_event(chr, CHR_EVENT_BREAK); break; case 'c': assert(d->mux_cnt > 0); /* handler registered with first fe */ /* Switch to the next registered device */ mux_set_focus(chr, (d->focus + 1) % d->mux_cnt); break; case 't': d->timestamps = !d->timestamps; d->timestamps_start = -1; d->linestart = 0; break; } } else if (ch == term_escape_char) { d->term_got_escape = 1; } else { send_char: return 1; } return 0; }
static void tcp_chr_connect(void *opaque) { Chardev *chr = CHARDEV(opaque); SocketChardev *s = SOCKET_CHARDEV(opaque); g_free(chr->filename); chr->filename = sockaddr_to_str( &s->sioc->localAddr, s->sioc->localAddrLen, &s->sioc->remoteAddr, s->sioc->remoteAddrLen, s->is_listen, s->is_telnet); s->connected = 1; if (s->ioc) { chr->gsource = io_add_watch_poll(chr, s->ioc, tcp_chr_read_poll, tcp_chr_read, chr, NULL); } qemu_chr_be_event(chr, CHR_EVENT_OPENED); }
static void tcp_chr_disconnect(Chardev *chr) { SocketChardev *s = SOCKET_CHARDEV(chr); if (!s->connected) { return; } tcp_chr_free_connection(chr); if (s->listen_ioc) { s->listen_tag = qio_channel_add_watch( QIO_CHANNEL(s->listen_ioc), G_IO_IN, tcp_chr_accept, chr, NULL); } update_disconnected_filename(s); qemu_chr_be_event(chr, CHR_EVENT_CLOSED); if (s->reconnect_time) { qemu_chr_socket_restart_timer(chr); } }
static void tcp_chr_disconnect(Chardev *chr) { SocketChardev *s = SOCKET_CHARDEV(chr); if (!s->connected) { return; } tcp_chr_free_connection(chr); if (s->listen_ioc) { s->listen_tag = qio_channel_add_watch( QIO_CHANNEL(s->listen_ioc), G_IO_IN, tcp_chr_accept, chr, NULL); } chr->filename = SocketAddress_to_str("disconnected:", s->addr, s->is_listen, s->is_telnet); qemu_chr_be_event(chr, CHR_EVENT_CLOSED); if (s->reconnect_time) { qemu_chr_socket_restart_timer(chr); } }
static void tcp_chr_process_IAC_bytes(Chardev *chr, SocketChardev *s, uint8_t *buf, int *size) { /* Handle any telnet or tn3270 client's basic IAC options. * For telnet options, it satisfies char by char mode with no echo. * For tn3270 options, it satisfies binary mode with EOR. * All IAC options will be removed from the buf and the do_opt * pointer will be used to track the state of the width of the * IAC information. * * RFC854: "All TELNET commands consist of at least a two byte sequence. * The commands dealing with option negotiation are three byte sequences, * the third byte being the code for the option referenced." * "IAC BREAK", "IAC IP", "IAC NOP" and the double IAC are two bytes. * "IAC SB", "IAC SE" and "IAC EOR" are saved to split up data boundary * for tn3270. * NOP, Break and Interrupt Process(IP) might be encountered during a TN3270 * session, and NOP and IP need to be done later. */ int i; int j = 0; for (i = 0; i < *size; i++) { if (s->do_telnetopt > 1) { if ((unsigned char)buf[i] == IAC && s->do_telnetopt == 2) { /* Double IAC means send an IAC */ if (j != i) { buf[j] = buf[i]; } j++; s->do_telnetopt = 1; } else { if ((unsigned char)buf[i] == IAC_BREAK && s->do_telnetopt == 2) { /* Handle IAC break commands by sending a serial break */ qemu_chr_be_event(chr, CHR_EVENT_BREAK); s->do_telnetopt++; } else if (s->is_tn3270 && ((unsigned char)buf[i] == IAC_EOR || (unsigned char)buf[i] == IAC_SB || (unsigned char)buf[i] == IAC_SE) && s->do_telnetopt == 2) { buf[j++] = IAC; buf[j++] = buf[i]; s->do_telnetopt++; } else if (s->is_tn3270 && ((unsigned char)buf[i] == IAC_IP || (unsigned char)buf[i] == IAC_NOP) && s->do_telnetopt == 2) { /* TODO: IP and NOP need to be implemented later. */ s->do_telnetopt++; } s->do_telnetopt++; } if (s->do_telnetopt >= 4) { s->do_telnetopt = 1; } } else { if ((unsigned char)buf[i] == IAC) { s->do_telnetopt = 2; } else { if (j != i) { buf[j] = buf[i]; } j++; } } } *size = j; }
static void char_mux_test(void) { QemuOpts *opts; Chardev *chr, *base; char *data; FeHandler h1 = { 0, }, h2 = { 0, }; CharBackend chr_be1, chr_be2; opts = qemu_opts_create(qemu_find_opts("chardev"), "mux-label", 1, &error_abort); qemu_opt_set(opts, "backend", "ringbuf", &error_abort); qemu_opt_set(opts, "size", "128", &error_abort); qemu_opt_set(opts, "mux", "on", &error_abort); chr = qemu_chr_new_from_opts(opts, &error_abort); g_assert_nonnull(chr); qemu_opts_del(opts); qemu_chr_fe_init(&chr_be1, chr, &error_abort); qemu_chr_fe_set_handlers(&chr_be1, fe_can_read, fe_read, fe_event, NULL, &h1, NULL, true); qemu_chr_fe_init(&chr_be2, chr, &error_abort); qemu_chr_fe_set_handlers(&chr_be2, fe_can_read, fe_read, fe_event, NULL, &h2, NULL, true); qemu_chr_fe_take_focus(&chr_be2); base = qemu_chr_find("mux-label-base"); g_assert_cmpint(qemu_chr_be_can_write(base), !=, 0); qemu_chr_be_write(base, (void *)"hello", 6); g_assert_cmpint(h1.read_count, ==, 0); g_assert_cmpint(h2.read_count, ==, 6); g_assert_cmpstr(h2.read_buf, ==, "hello"); h2.read_count = 0; g_assert_cmpint(h1.last_event, !=, 42); /* should be MUX_OUT or OPENED */ g_assert_cmpint(h2.last_event, !=, 42); /* should be MUX_IN or OPENED */ /* sending event on the base broadcast to all fe, historical reasons? */ qemu_chr_be_event(base, 42); g_assert_cmpint(h1.last_event, ==, 42); g_assert_cmpint(h2.last_event, ==, 42); qemu_chr_be_event(chr, -1); g_assert_cmpint(h1.last_event, ==, 42); g_assert_cmpint(h2.last_event, ==, -1); /* switch focus */ qemu_chr_be_write(base, (void *)"\1b", 2); g_assert_cmpint(h1.last_event, ==, 42); g_assert_cmpint(h2.last_event, ==, CHR_EVENT_BREAK); qemu_chr_be_write(base, (void *)"\1c", 2); g_assert_cmpint(h1.last_event, ==, CHR_EVENT_MUX_IN); g_assert_cmpint(h2.last_event, ==, CHR_EVENT_MUX_OUT); qemu_chr_be_event(chr, -1); g_assert_cmpint(h1.last_event, ==, -1); g_assert_cmpint(h2.last_event, ==, CHR_EVENT_MUX_OUT); qemu_chr_be_write(base, (void *)"hello", 6); g_assert_cmpint(h2.read_count, ==, 0); g_assert_cmpint(h1.read_count, ==, 6); g_assert_cmpstr(h1.read_buf, ==, "hello"); h1.read_count = 0; qemu_chr_be_write(base, (void *)"\1b", 2); g_assert_cmpint(h1.last_event, ==, CHR_EVENT_BREAK); g_assert_cmpint(h2.last_event, ==, CHR_EVENT_MUX_OUT); /* remove first handler */ qemu_chr_fe_set_handlers(&chr_be1, NULL, NULL, NULL, NULL, NULL, NULL, true); qemu_chr_be_write(base, (void *)"hello", 6); g_assert_cmpint(h1.read_count, ==, 0); g_assert_cmpint(h2.read_count, ==, 0); qemu_chr_be_write(base, (void *)"\1c", 2); qemu_chr_be_write(base, (void *)"hello", 6); g_assert_cmpint(h1.read_count, ==, 0); g_assert_cmpint(h2.read_count, ==, 6); g_assert_cmpstr(h2.read_buf, ==, "hello"); h2.read_count = 0; /* print help */ qemu_chr_be_write(base, (void *)"\1?", 2); data = qmp_ringbuf_read("mux-label-base", 128, false, 0, &error_abort); g_assert_cmpint(strlen(data), !=, 0); g_free(data); qemu_chr_fe_deinit(&chr_be1, false); qemu_chr_fe_deinit(&chr_be2, true); }