qemu/qemu-char.c

4377 lines
114 KiB
C
Raw Normal View History

/*
* QEMU System Emulator
*
* Copyright (c) 2003-2008 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qemu-common.h"
#include "monitor/monitor.h"
#include "sysemu/sysemu.h"
#include "qemu/error-report.h"
#include "qemu/timer.h"
#include "sysemu/char.h"
#include "hw/usb.h"
#include "qmp-commands.h"
#include "qapi/qmp-input-visitor.h"
#include "qapi/qmp-output-visitor.h"
#include "qapi-visit.h"
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include <errno.h>
#include <sys/time.h>
#include <zlib.h>
#ifndef _WIN32
#include <sys/times.h>
#include <sys/wait.h>
#include <termios.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <dirent.h>
#include <netdb.h>
#include <sys/select.h>
#ifdef CONFIG_BSD
#include <sys/stat.h>
do not include <libutil.h> needlessly or if it doesn't exist <libutil.h> and <util.h> on *BSD (some have one, some another) were #included just for openpty() declaration. The only file where this function is actually used is qemu-char.c. In vl.c and net/tap-bsd.c, none of functions declared in libutil.h (login logout logwtmp timdomain openpty forkpty uu_lock realhostname fparseln and a few others depending on version) are used. Initially the code which is currently in qemu-char.c was in vl.c, it has been removed into separate file in commit 0e82f34d077dc2542 Fri Oct 31 18:44:40 2008, but the #includes were left in vl.c. So with vl.c, we just remove includes - libutil.h, util.h and pty.h (which declares only openpty() and forkpty()) from there. The code in net/tap-bsd.c, which come from net/tap.c, had this commit 5281d757efa6e40d74ce124be048b08d43887555 Author: Mark McLoughlin <markmc@redhat.com> Date: Thu Oct 22 17:49:07 2009 +0100 net: split all the tap code out into net/tap.c Note this commit not only moved stuff out of net.c to net/tap.c, but also rewrote large portions of the tap code, and added these completely unnecessary #includes -- as usual, I question why such a misleading commit messages are allowed. Again, no functions defined in libutil.h or util.h on *BSD are used by neither net/tap.c nor net/tap-bsd.c. Removing them. And finally, the only real user for these #includes, qemu-char.c, which actually uses openpty(). There, the #ifdef logic is wrong. A GLIBC-based system has <pty.h>, even if it is a variant of *BSD. So __GLIBC__ should be checked first, and instead of trying to include <libutil.h> or <util.h>, we include <pty.h>. If it is not GLIBC-based, we check for variations between <*util.h> as before. This patch fixes build of qemu 1.1 on Debian/kFreebsd (well, one of the two problems): it is a distribution with a FreeBSD kernel, so it #defines at least __FreeBSD_kernel__, but since it is based on GLIBC, it has <pty.h>, but current version does not have neither <util.h> nor <libutil.h>, which the code tries to include 3 times but uses only once. Signed-off-By: Michael Tokarev <mjt@tls.msk.ru> Cc: Aurelien Jarno <aurelien@aurel32.net> Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
2012-06-02 23:43:33 +04:00
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#include <dev/ppbus/ppi.h>
#include <dev/ppbus/ppbconf.h>
#elif defined(__DragonFly__)
#include <dev/misc/ppi/ppi.h>
#include <bus/ppbus/ppbconf.h>
#endif
#else
#ifdef __linux__
#include <linux/ppdev.h>
#include <linux/parport.h>
#endif
#ifdef __sun__
#include <sys/stat.h>
#include <sys/ethernet.h>
#include <sys/sockio.h>
#include <netinet/arp.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h> // must come after ip.h
#include <netinet/udp.h>
#include <netinet/tcp.h>
#endif
#endif
#endif
#include "qemu/sockets.h"
#include "ui/qemu-spice.h"
#define READ_BUF_LEN 4096
#define READ_RETRIES 10
#define CHR_MAX_FILENAME_SIZE 256
#define TCP_MAX_FDS 16
/***********************************************************/
/* Socket address helpers */
static void qapi_copy_SocketAddress(SocketAddress **p_dest,
SocketAddress *src)
{
QmpOutputVisitor *qov;
QmpInputVisitor *qiv;
Visitor *ov, *iv;
QObject *obj;
*p_dest = NULL;
qov = qmp_output_visitor_new();
ov = qmp_output_get_visitor(qov);
visit_type_SocketAddress(ov, &src, NULL, &error_abort);
obj = qmp_output_get_qobject(qov);
qmp_output_visitor_cleanup(qov);
if (!obj) {
return;
}
qiv = qmp_input_visitor_new(obj);
iv = qmp_input_get_visitor(qiv);
visit_type_SocketAddress(iv, p_dest, NULL, &error_abort);
qmp_input_visitor_cleanup(qiv);
qobject_decref(obj);
}
static int SocketAddress_to_str(char *dest, int max_len,
const char *prefix, SocketAddress *addr,
bool is_listen, bool is_telnet)
{
switch (addr->kind) {
case SOCKET_ADDRESS_KIND_INET:
return snprintf(dest, max_len, "%s%s:%s:%s%s", prefix,
is_telnet ? "telnet" : "tcp", addr->inet->host,
addr->inet->port, is_listen ? ",server" : "");
break;
case SOCKET_ADDRESS_KIND_UNIX:
return snprintf(dest, max_len, "%sunix:%s%s", prefix,
addr->q_unix->path, is_listen ? ",server" : "");
break;
case SOCKET_ADDRESS_KIND_FD:
return snprintf(dest, max_len, "%sfd:%s%s", prefix, addr->fd->str,
is_listen ? ",server" : "");
break;
default:
abort();
}
}
static int sockaddr_to_str(char *dest, int max_len,
struct sockaddr_storage *ss, socklen_t ss_len,
struct sockaddr_storage *ps, socklen_t ps_len,
bool is_listen, bool is_telnet)
{
char shost[NI_MAXHOST], sserv[NI_MAXSERV];
char phost[NI_MAXHOST], pserv[NI_MAXSERV];
const char *left = "", *right = "";
switch (ss->ss_family) {
#ifndef _WIN32
case AF_UNIX:
return snprintf(dest, max_len, "unix:%s%s",
((struct sockaddr_un *)(ss))->sun_path,
is_listen ? ",server" : "");
#endif
case AF_INET6:
left = "[";
right = "]";
/* fall through */
case AF_INET:
getnameinfo((struct sockaddr *) ss, ss_len, shost, sizeof(shost),
sserv, sizeof(sserv), NI_NUMERICHOST | NI_NUMERICSERV);
getnameinfo((struct sockaddr *) ps, ps_len, phost, sizeof(phost),
pserv, sizeof(pserv), NI_NUMERICHOST | NI_NUMERICSERV);
return snprintf(dest, max_len, "%s:%s%s%s:%s%s <-> %s%s%s:%s",
is_telnet ? "telnet" : "tcp",
left, shost, right, sserv,
is_listen ? ",server" : "",
left, phost, right, pserv);
default:
return snprintf(dest, max_len, "unknown");
}
}
/***********************************************************/
/* character device */
static QTAILQ_HEAD(CharDriverStateHead, CharDriverState) chardevs =
QTAILQ_HEAD_INITIALIZER(chardevs);
CharDriverState *qemu_chr_alloc(void)
{
CharDriverState *chr = g_malloc0(sizeof(CharDriverState));
qemu_mutex_init(&chr->chr_write_lock);
return chr;
}
void qemu_chr_be_event(CharDriverState *s, int event)
{
/* Keep track if the char device is open */
switch (event) {
case CHR_EVENT_OPENED:
s->be_open = 1;
break;
case CHR_EVENT_CLOSED:
s->be_open = 0;
break;
}
if (!s->chr_event)
return;
s->chr_event(s->handler_opaque, event);
}
void qemu_chr_be_generic_open(CharDriverState *s)
{
qemu-char: don't issue CHR_EVENT_OPEN in a BH When CHR_EVENT_OPENED was initially added, it was CHR_EVENT_RESET, and it was issued as a bottom-half: 86e94dea5b740dad65446c857f6959eae43e0ba6 Which we basically used to print out a greeting/prompt for the monitor. AFAICT the only reason this was ever done in a BH was because in some cases we'd modify the chr_write handler for a new chardev backend *after* the site where we issued the reset (see: 86e94d:qemu_chr_open_stdio()) At some point this event was renamed to CHR_EVENT_OPENED, and we've maintained the use of this BH ever since. However, due to 9f939df955a4152aad69a19a77e0898631bb2c18, we schedule the BH via g_idle_add(), which is causing events to sometimes be delivered after we've already begun processing data from backends, leading to: known bugs: QMP: session negotation resets with OPENED event, in some cases this is causing new sessions to get sporadically reset potential bugs: hw/usb/redirect.c: can_read handler checks for dev->parser != NULL, which may be true if CLOSED BH has not been executed yet. In the past, OPENED quiesced outstanding CLOSED events prior to us reading client data. If it's delayed, our check may allow reads to occur even though we haven't processed the OPENED event yet, and when we do finally get the OPENED event, our state may get reset. qtest.c: can begin session before OPENED event is processed, leading to a spurious reset of the system and irq_levels gdbstub.c: may start a gdb session prior to the machine being paused To fix these, let's just drop the BH. Since the initial reasoning for using it still applies to an extent, work around that by deferring the delivery of CHR_EVENT_OPENED until after the chardevs have been fully initialized, toward the end of qmp_chardev_add() (or some cases, qemu_chr_new_from_opts()). This defers delivery long enough that we can be assured a CharDriverState is fully initialized before CHR_EVENT_OPENED is sent. Also, rather than requiring each chardev to do an explicit open, do it automatically, and allow the small few who don't desire such behavior to suppress the OPENED-on-init behavior by setting a 'explicit_be_open' flag. We additionally add missing OPENED events for stdio backends on w32, which were previously not being issued, causing us to not recieve the banner and initial prompts for qmp/hmp. Reported-by: Stefan Priebe <s.priebe@profihost.ag> Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com> Message-id: 1370636393-21044-1-git-send-email-mdroth@linux.vnet.ibm.com Cc: qemu-stable@nongnu.org Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-06-08 00:19:53 +04:00
qemu_chr_be_event(s, CHR_EVENT_OPENED);
}
int qemu_chr_fe_write(CharDriverState *s, const uint8_t *buf, int len)
{
int ret;
qemu_mutex_lock(&s->chr_write_lock);
ret = s->chr_write(s, buf, len);
qemu_mutex_unlock(&s->chr_write_lock);
return ret;
}
int qemu_chr_fe_write_all(CharDriverState *s, const uint8_t *buf, int len)
{
int offset = 0;
int res = 0;
qemu_mutex_lock(&s->chr_write_lock);
while (offset < len) {
do {
res = s->chr_write(s, buf + offset, len - offset);
if (res == -1 && errno == EAGAIN) {
g_usleep(100);
}
} while (res == -1 && errno == EAGAIN);
if (res <= 0) {
break;
}
offset += res;
}
qemu_mutex_unlock(&s->chr_write_lock);
if (res < 0) {
return res;
}
return offset;
}
int qemu_chr_fe_read_all(CharDriverState *s, uint8_t *buf, int len)
{
int offset = 0, counter = 10;
int res;
if (!s->chr_sync_read) {
return 0;
}
while (offset < len) {
do {
res = s->chr_sync_read(s, buf + offset, len - offset);
if (res == -1 && errno == EAGAIN) {
g_usleep(100);
}
} while (res == -1 && errno == EAGAIN);
if (res == 0) {
break;
}
if (res < 0) {
return res;
}
offset += res;
if (!counter--) {
break;
}
}
return offset;
}
int qemu_chr_fe_ioctl(CharDriverState *s, int cmd, void *arg)
{
if (!s->chr_ioctl)
return -ENOTSUP;
return s->chr_ioctl(s, cmd, arg);
}
int qemu_chr_be_can_write(CharDriverState *s)
{
if (!s->chr_can_read)
return 0;
return s->chr_can_read(s->handler_opaque);
}
void qemu_chr_be_write(CharDriverState *s, uint8_t *buf, int len)
{
if (s->chr_read) {
s->chr_read(s->handler_opaque, buf, len);
}
}
int qemu_chr_fe_get_msgfd(CharDriverState *s)
{
int fd;
return (qemu_chr_fe_get_msgfds(s, &fd, 1) == 1) ? fd : -1;
}
int qemu_chr_fe_get_msgfds(CharDriverState *s, int *fds, int len)
{
return s->get_msgfds ? s->get_msgfds(s, fds, len) : -1;
}
int qemu_chr_fe_set_msgfds(CharDriverState *s, int *fds, int num)
{
return s->set_msgfds ? s->set_msgfds(s, fds, num) : -1;
}
int qemu_chr_add_client(CharDriverState *s, int fd)
{
return s->chr_add_client ? s->chr_add_client(s, fd) : -1;
}
void qemu_chr_accept_input(CharDriverState *s)
{
if (s->chr_accept_input)
s->chr_accept_input(s);
qemu_notify_event();
}
void qemu_chr_fe_printf(CharDriverState *s, const char *fmt, ...)
{
char buf[READ_BUF_LEN];
va_list ap;
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
qemu_chr_fe_write(s, (uint8_t *)buf, strlen(buf));
va_end(ap);
}
static void remove_fd_in_watch(CharDriverState *chr);
void qemu_chr_add_handlers(CharDriverState *s,
IOCanReadHandler *fd_can_read,
IOReadHandler *fd_read,
IOEventHandler *fd_event,
void *opaque)
{
int fe_open;
if (!opaque && !fd_can_read && !fd_read && !fd_event) {
fe_open = 0;
remove_fd_in_watch(s);
} else {
fe_open = 1;
}
s->chr_can_read = fd_can_read;
s->chr_read = fd_read;
s->chr_event = fd_event;
s->handler_opaque = opaque;
if (fe_open && s->chr_update_read_handler)
s->chr_update_read_handler(s);
if (!s->explicit_fe_open) {
qemu_chr_fe_set_open(s, fe_open);
}
/* We're connecting to an already opened device, so let's make sure we
also get the open event */
if (fe_open && s->be_open) {
qemu_chr_be_generic_open(s);
}
}
static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
return len;
}
static CharDriverState *qemu_chr_open_null(void)
{
CharDriverState *chr;
chr = qemu_chr_alloc();
chr->chr_write = null_chr_write;
qemu-char: don't issue CHR_EVENT_OPEN in a BH When CHR_EVENT_OPENED was initially added, it was CHR_EVENT_RESET, and it was issued as a bottom-half: 86e94dea5b740dad65446c857f6959eae43e0ba6 Which we basically used to print out a greeting/prompt for the monitor. AFAICT the only reason this was ever done in a BH was because in some cases we'd modify the chr_write handler for a new chardev backend *after* the site where we issued the reset (see: 86e94d:qemu_chr_open_stdio()) At some point this event was renamed to CHR_EVENT_OPENED, and we've maintained the use of this BH ever since. However, due to 9f939df955a4152aad69a19a77e0898631bb2c18, we schedule the BH via g_idle_add(), which is causing events to sometimes be delivered after we've already begun processing data from backends, leading to: known bugs: QMP: session negotation resets with OPENED event, in some cases this is causing new sessions to get sporadically reset potential bugs: hw/usb/redirect.c: can_read handler checks for dev->parser != NULL, which may be true if CLOSED BH has not been executed yet. In the past, OPENED quiesced outstanding CLOSED events prior to us reading client data. If it's delayed, our check may allow reads to occur even though we haven't processed the OPENED event yet, and when we do finally get the OPENED event, our state may get reset. qtest.c: can begin session before OPENED event is processed, leading to a spurious reset of the system and irq_levels gdbstub.c: may start a gdb session prior to the machine being paused To fix these, let's just drop the BH. Since the initial reasoning for using it still applies to an extent, work around that by deferring the delivery of CHR_EVENT_OPENED until after the chardevs have been fully initialized, toward the end of qmp_chardev_add() (or some cases, qemu_chr_new_from_opts()). This defers delivery long enough that we can be assured a CharDriverState is fully initialized before CHR_EVENT_OPENED is sent. Also, rather than requiring each chardev to do an explicit open, do it automatically, and allow the small few who don't desire such behavior to suppress the OPENED-on-init behavior by setting a 'explicit_be_open' flag. We additionally add missing OPENED events for stdio backends on w32, which were previously not being issued, causing us to not recieve the banner and initial prompts for qmp/hmp. Reported-by: Stefan Priebe <s.priebe@profihost.ag> Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com> Message-id: 1370636393-21044-1-git-send-email-mdroth@linux.vnet.ibm.com Cc: qemu-stable@nongnu.org Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-06-08 00:19:53 +04:00
chr->explicit_be_open = true;
Revert "qemu-char: Print strerror message on failure" and deps The commit's purpose is laudable: The only way for chardev drivers to communicate an error was to return a NULL pointer, which resulted in an error message that said _that_ something went wrong, but not _why_. It attempts to achieve it by changing the interface to return 0/-errno and update qemu_chr_open_opts() to use strerror() to display a more helpful error message. Unfortunately, it has serious flaws: 1. Backends "socket" and "udp" return bogus error codes, because qemu_chr_open_socket() and qemu_chr_open_udp() assume that unix_listen_opts(), unix_connect_opts(), inet_listen_opts(), inet_connect_opts() and inet_dgram_opts() fail with errno set appropriately. That assumption is wrong, and the commit turns unspecific error messages into misleading error messages. For instance: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx inet_connect: host and/or port not specified chardev: opening backend "socket" failed: No such file or directory ENOENT is what happens to be in my errno when the backend returns -errno. Let's put ERANGE there just for giggles: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx -drive if=none,iops=99999999999999999999 inet_connect: host and/or port not specified chardev: opening backend "socket" failed: Numerical result out of range Worse: when errno happens to be zero, return -errno erroneously signals success, and qemu_chr_new_from_opts() dies dereferencing uninitialized chr. I observe this with "-serial unix:". 2. All qemu_chr_open_opts() knows about the error is an errno error code. That's simply not enough for a decent message. For instance, when inet_dgram() can't resolve the parameter host, which errno code should it use? What if it can't resolve parameter localaddr? Clue: many backends already report errors in their open methods. Let's revert the flawed commit along with its dependencies, and fix up the silent error paths instead. This reverts commit 6e1db57b2ac9025c2443c665a0d9e78748637b26. Conflicts: console.c hw/baum.c qemu-char.c This reverts commit aad04cd024f0c59f0b96f032cde2e24eb3abba6d. The parts of commit db418a0a "Add stdio char device on windows" that depend on the reverted change fixed up. Signed-off-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2012-02-07 18:09:08 +04:00
return chr;
}
/* MUX driver for serial I/O splitting */
#define MAX_MUX 4
#define MUX_BUFFER_SIZE 32 /* Must be a power of 2. */
#define MUX_BUFFER_MASK (MUX_BUFFER_SIZE - 1)
typedef struct {
IOCanReadHandler *chr_can_read[MAX_MUX];
IOReadHandler *chr_read[MAX_MUX];
IOEventHandler *chr_event[MAX_MUX];
void *ext_opaque[MAX_MUX];
CharDriverState *drv;
int focus;
int mux_cnt;
int term_got_escape;
int max_size;
/* Intermediate input buffer allows to catch escape sequences even if the
currently active device is not accepting any input - but only until it
is full as well. */
unsigned char buffer[MAX_MUX][MUX_BUFFER_SIZE];
int prod[MAX_MUX];
int cons[MAX_MUX];
int timestamps;
/* Protected by the CharDriverState chr_write_lock. */
int linestart;
int64_t timestamps_start;
} MuxDriver;
/* Called with chr_write_lock held. */
static int mux_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
MuxDriver *d = chr->opaque;
int ret;
if (!d->timestamps) {
ret = qemu_chr_fe_write(d->drv, buf, len);
} else {
int i;
ret = 0;
for (i = 0; i < len; i++) {
if (d->linestart) {
char buf1[64];
int64_t ti;
int secs;
ti = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
if (d->timestamps_start == -1)
d->timestamps_start = ti;
ti -= d->timestamps_start;
secs = ti / 1000;
snprintf(buf1, sizeof(buf1),
"[%02d:%02d:%02d.%03d] ",
secs / 3600,
(secs / 60) % 60,
secs % 60,
(int)(ti % 1000));
qemu_chr_fe_write(d->drv, (uint8_t *)buf1, strlen(buf1));
d->linestart = 0;
}
ret += qemu_chr_fe_write(d->drv, buf+i, 1);
if (buf[i] == '\n') {
d->linestart = 1;
}
}
}
return ret;
}
static const char * const mux_help[] = {
"% h print this help\n\r",
"% x exit emulator\n\r",
"% s save disk data back to file (if -snapshot)\n\r",
"% t toggle console timestamps\n\r",
"% b send break (magic sysrq)\n\r",
"% c switch between console and monitor\n\r",
"% % sends %\n\r",
NULL
};
int term_escape_char = 0x01; /* ctrl-a is used for escape */
static void mux_print_help(CharDriverState *chr)
{
int i, j;
char ebuf[15] = "Escape-Char";
char cbuf[50] = "\n\r";
if (term_escape_char > 0 && term_escape_char < 26) {
snprintf(cbuf, sizeof(cbuf), "\n\r");
snprintf(ebuf, sizeof(ebuf), "C-%c", term_escape_char - 1 + 'a');
} else {
snprintf(cbuf, sizeof(cbuf),
"\n\rEscape-Char set to Ascii: 0x%02x\n\r\n\r",
term_escape_char);
}
qemu_chr_fe_write(chr, (uint8_t *)cbuf, strlen(cbuf));
for (i = 0; mux_help[i] != NULL; i++) {
for (j=0; mux_help[i][j] != '\0'; j++) {
if (mux_help[i][j] == '%')
qemu_chr_fe_write(chr, (uint8_t *)ebuf, strlen(ebuf));
else
qemu_chr_fe_write(chr, (uint8_t *)&mux_help[i][j], 1);
}
}
}
static void mux_chr_send_event(MuxDriver *d, int mux_nr, int event)
{
if (d->chr_event[mux_nr])
d->chr_event[mux_nr](d->ext_opaque[mux_nr], event);
}
static int mux_proc_byte(CharDriverState *chr, MuxDriver *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_fe_write(chr, (uint8_t *)term, strlen(term));
exit(0);
break;
}
case 's':
bdrv_commit_all();
break;
case 'b':
qemu_chr_be_event(chr, CHR_EVENT_BREAK);
break;
case 'c':
/* Switch to the next registered device */
mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT);
d->focus++;
if (d->focus >= d->mux_cnt)
d->focus = 0;
mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN);
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 mux_chr_accept_input(CharDriverState *chr)
{
MuxDriver *d = chr->opaque;
int m = d->focus;
while (d->prod[m] != d->cons[m] &&
d->chr_can_read[m] &&
d->chr_can_read[m](d->ext_opaque[m])) {
d->chr_read[m](d->ext_opaque[m],
&d->buffer[m][d->cons[m]++ & MUX_BUFFER_MASK], 1);
}
}
static int mux_chr_can_read(void *opaque)
{
CharDriverState *chr = opaque;
MuxDriver *d = chr->opaque;
int m = d->focus;
if ((d->prod[m] - d->cons[m]) < MUX_BUFFER_SIZE)
return 1;
if (d->chr_can_read[m])
return d->chr_can_read[m](d->ext_opaque[m]);
return 0;
}
static void mux_chr_read(void *opaque, const uint8_t *buf, int size)
{
CharDriverState *chr = opaque;
MuxDriver *d = chr->opaque;
int m = d->focus;
int i;
mux_chr_accept_input (opaque);
for(i = 0; i < size; i++)
if (mux_proc_byte(chr, d, buf[i])) {
if (d->prod[m] == d->cons[m] &&
d->chr_can_read[m] &&
d->chr_can_read[m](d->ext_opaque[m]))
d->chr_read[m](d->ext_opaque[m], &buf[i], 1);
else
d->buffer[m][d->prod[m]++ & MUX_BUFFER_MASK] = buf[i];
}
}
static void mux_chr_event(void *opaque, int event)
{
CharDriverState *chr = opaque;
MuxDriver *d = chr->opaque;
int i;
/* Send the event to all registered listeners */
for (i = 0; i < d->mux_cnt; i++)
mux_chr_send_event(d, i, event);
}
static void mux_chr_update_read_handler(CharDriverState *chr)
{
MuxDriver *d = chr->opaque;
if (d->mux_cnt >= MAX_MUX) {
fprintf(stderr, "Cannot add I/O handlers, MUX array is full\n");
return;
}
d->ext_opaque[d->mux_cnt] = chr->handler_opaque;
d->chr_can_read[d->mux_cnt] = chr->chr_can_read;
d->chr_read[d->mux_cnt] = chr->chr_read;
d->chr_event[d->mux_cnt] = chr->chr_event;
/* Fix up the real driver with mux routines */
if (d->mux_cnt == 0) {
qemu_chr_add_handlers(d->drv, mux_chr_can_read, mux_chr_read,
mux_chr_event, chr);
}
if (d->focus != -1) {
mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT);
}
d->focus = d->mux_cnt;
d->mux_cnt++;
mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN);
}
static bool muxes_realized;
/**
* 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 void muxes_realize_done(Notifier *notifier, void *unused)
{
CharDriverState *chr;
QTAILQ_FOREACH(chr, &chardevs, next) {
if (chr->is_mux) {
MuxDriver *d = chr->opaque;
int i;
/* send OPENED to all already-attached FEs */
for (i = 0; i < d->mux_cnt; i++) {
mux_chr_send_event(d, i, CHR_EVENT_OPENED);
}
/* mark mux as OPENED so any new FEs will immediately receive
* OPENED event
*/
qemu_chr_be_generic_open(chr);
}
}
muxes_realized = true;
}
static Notifier muxes_realize_notify = {
.notify = muxes_realize_done,
};
static GSource *mux_chr_add_watch(CharDriverState *s, GIOCondition cond)
{
MuxDriver *d = s->opaque;
return d->drv->chr_add_watch(d->drv, cond);
}
static CharDriverState *qemu_chr_open_mux(CharDriverState *drv)
{
CharDriverState *chr;
MuxDriver *d;
chr = qemu_chr_alloc();
d = g_malloc0(sizeof(MuxDriver));
chr->opaque = d;
d->drv = drv;
d->focus = -1;
chr->chr_write = mux_chr_write;
chr->chr_update_read_handler = mux_chr_update_read_handler;
chr->chr_accept_input = mux_chr_accept_input;
/* Frontend guest-open / -close notification is not support with muxes */
chr->chr_set_fe_open = NULL;
if (drv->chr_add_watch) {
chr->chr_add_watch = mux_chr_add_watch;
}
/* only default to opened state if we've realized the initial
* set of muxes
*/
chr->explicit_be_open = muxes_realized ? 0 : 1;
chr->is_mux = 1;
return chr;
}
#ifdef _WIN32
int send_all(int fd, const void *buf, int len1)
{
int ret, len;
len = len1;
while (len > 0) {
ret = send(fd, buf, len, 0);
if (ret < 0) {
errno = WSAGetLastError();
if (errno != WSAEWOULDBLOCK) {
return -1;
}
} else if (ret == 0) {
break;
} else {
buf += ret;
len -= ret;
}
}
return len1 - len;
}
#else
int send_all(int fd, const void *_buf, int len1)
{
int ret, len;
const uint8_t *buf = _buf;
len = len1;
while (len > 0) {
ret = write(fd, buf, len);
if (ret < 0) {
if (errno != EINTR && errno != EAGAIN)
return -1;
} else if (ret == 0) {
break;
} else {
buf += ret;
len -= ret;
}
}
return len1 - len;
}
Add a TPM Passthrough backend driver implementation This patch is based of off version 9 of Stefan Berger's patch series "QEMU Trusted Platform Module (TPM) integration" and adds a new backend driver for it. This patch adds a passthrough backend driver for passing commands sent to the emulated TPM device directly to a TPM device opened on the host machine. Thus it is possible to use a hardware TPM device in a system running on QEMU, providing the ability to access a TPM in a special state (e.g. after a Trusted Boot). This functionality is being used in the acTvSM Trusted Virtualization Platform which is available on [1]. Usage example: qemu-system-x86_64 -tpmdev passthrough,id=tpm0,path=/dev/tpm0 \ -device tpm-tis,tpmdev=tpm0 \ -cdrom test.iso -boot d Some notes about the host TPM: The TPM needs to be enabled and activated. If that's not the case one has to go through the BIOS/UEFI and enable and activate that TPM for TPM commands to work as expected. It may be necessary to boot the kernel using tpm_tis.force=1 in the boot command line or 'modprobe tpm_tis force=1' in case of using it as a module. Regards, Andreas Niederl, Stefan Berger [1] http://trustedjava.sourceforge.net/ Signed-off-by: Andreas Niederl <andreas.niederl@iaik.tugraz.at> Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com> Reviewed-by: Corey Bryant <coreyb@linux.vnet.ibm.com> Reviewed-by: Joel Schopp <jschopp@linux.vnet.ibm.com> Message-id: 1361987275-26289-6-git-send-email-stefanb@linux.vnet.ibm.com Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-02-27 21:47:53 +04:00
int recv_all(int fd, void *_buf, int len1, bool single_read)
{
int ret, len;
uint8_t *buf = _buf;
len = len1;
while ((len > 0) && (ret = read(fd, buf, len)) != 0) {
if (ret < 0) {
if (errno != EINTR && errno != EAGAIN) {
return -1;
}
continue;
} else {
if (single_read) {
return ret;
}
buf += ret;
len -= ret;
}
}
return len1 - len;
}
#endif /* !_WIN32 */
typedef struct IOWatchPoll
{
GSource parent;
GIOChannel *channel;
GSource *src;
IOCanReadHandler *fd_can_read;
GSourceFunc fd_read;
void *opaque;
} IOWatchPoll;
static IOWatchPoll *io_watch_poll_from_source(GSource *source)
{
return container_of(source, IOWatchPoll, parent);
}
static gboolean io_watch_poll_prepare(GSource *source, gint *timeout_)
{
IOWatchPoll *iwp = io_watch_poll_from_source(source);
bool now_active = iwp->fd_can_read(iwp->opaque) > 0;
bool was_active = iwp->src != NULL;
if (was_active == now_active) {
return FALSE;
}
if (now_active) {
iwp->src = g_io_create_watch(iwp->channel, G_IO_IN | G_IO_ERR | G_IO_HUP);
g_source_set_callback(iwp->src, iwp->fd_read, iwp->opaque, NULL);
g_source_attach(iwp->src, NULL);
} else {
g_source_destroy(iwp->src);
g_source_unref(iwp->src);
iwp->src = NULL;
}
return FALSE;
}
static gboolean io_watch_poll_check(GSource *source)
{
return FALSE;
}
static gboolean io_watch_poll_dispatch(GSource *source, GSourceFunc callback,
gpointer user_data)
{
abort();
}
static void io_watch_poll_finalize(GSource *source)
{
/* Due to a glib bug, removing the last reference to a source
* inside a finalize callback causes recursive locking (and a
* deadlock). This is not a problem inside other callbacks,
* including dispatch callbacks, so we call io_remove_watch_poll
* to remove this source. At this point, iwp->src must
* be NULL, or we would leak it.
*
* This would be solved much more elegantly by child sources,
* but we support older glib versions that do not have them.
*/
IOWatchPoll *iwp = io_watch_poll_from_source(source);
assert(iwp->src == NULL);
}
static GSourceFuncs io_watch_poll_funcs = {
.prepare = io_watch_poll_prepare,
.check = io_watch_poll_check,
.dispatch = io_watch_poll_dispatch,
.finalize = io_watch_poll_finalize,
};
/* Can only be used for read */
static guint io_add_watch_poll(GIOChannel *channel,
IOCanReadHandler *fd_can_read,
GIOFunc fd_read,
gpointer user_data)
{
IOWatchPoll *iwp;
int tag;
iwp = (IOWatchPoll *) g_source_new(&io_watch_poll_funcs, sizeof(IOWatchPoll));
iwp->fd_can_read = fd_can_read;
iwp->opaque = user_data;
iwp->channel = channel;
iwp->fd_read = (GSourceFunc) fd_read;
iwp->src = NULL;
tag = g_source_attach(&iwp->parent, NULL);
g_source_unref(&iwp->parent);
return tag;
}
static void io_remove_watch_poll(guint tag)
{
GSource *source;
IOWatchPoll *iwp;
g_return_if_fail (tag > 0);
source = g_main_context_find_source_by_id(NULL, tag);
g_return_if_fail (source != NULL);
iwp = io_watch_poll_from_source(source);
if (iwp->src) {
g_source_destroy(iwp->src);
g_source_unref(iwp->src);
iwp->src = NULL;
}
g_source_destroy(&iwp->parent);
}
static void remove_fd_in_watch(CharDriverState *chr)
{
if (chr->fd_in_tag) {
io_remove_watch_poll(chr->fd_in_tag);
chr->fd_in_tag = 0;
}
}
#ifndef _WIN32
static GIOChannel *io_channel_from_fd(int fd)
{
GIOChannel *chan;
if (fd == -1) {
return NULL;
}
chan = g_io_channel_unix_new(fd);
g_io_channel_set_encoding(chan, NULL, NULL);
g_io_channel_set_buffered(chan, FALSE);
return chan;
}
#endif
static GIOChannel *io_channel_from_socket(int fd)
{
GIOChannel *chan;
if (fd == -1) {
return NULL;
}
#ifdef _WIN32
chan = g_io_channel_win32_new_socket(fd);
#else
chan = g_io_channel_unix_new(fd);
#endif
g_io_channel_set_encoding(chan, NULL, NULL);
g_io_channel_set_buffered(chan, FALSE);
return chan;
}
static int io_channel_send(GIOChannel *fd, const void *buf, size_t len)
{
size_t offset = 0;
GIOStatus status = G_IO_STATUS_NORMAL;
while (offset < len && status == G_IO_STATUS_NORMAL) {
gsize bytes_written = 0;
status = g_io_channel_write_chars(fd, buf + offset, len - offset,
&bytes_written, NULL);
offset += bytes_written;
}
if (offset > 0) {
return offset;
}
switch (status) {
case G_IO_STATUS_NORMAL:
g_assert(len == 0);
return 0;
case G_IO_STATUS_AGAIN:
errno = EAGAIN;
return -1;
default:
break;
}
errno = EINVAL;
return -1;
}
#ifndef _WIN32
typedef struct FDCharDriver {
CharDriverState *chr;
GIOChannel *fd_in, *fd_out;
int max_size;
} FDCharDriver;
/* Called with chr_write_lock held. */
static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
FDCharDriver *s = chr->opaque;
return io_channel_send(s->fd_out, buf, len);
}
static gboolean fd_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
{
CharDriverState *chr = opaque;
FDCharDriver *s = chr->opaque;
int len;
uint8_t buf[READ_BUF_LEN];
GIOStatus status;
gsize bytes_read;
len = sizeof(buf);
if (len > s->max_size) {
len = s->max_size;
}
if (len == 0) {
return TRUE;
}
status = g_io_channel_read_chars(chan, (gchar *)buf,
len, &bytes_read, NULL);
if (status == G_IO_STATUS_EOF) {
remove_fd_in_watch(chr);
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
return FALSE;
}
if (status == G_IO_STATUS_NORMAL) {
qemu_chr_be_write(chr, buf, bytes_read);
}
return TRUE;
}
static int fd_chr_read_poll(void *opaque)
{
CharDriverState *chr = opaque;
FDCharDriver *s = chr->opaque;
s->max_size = qemu_chr_be_can_write(chr);
return s->max_size;
}
static GSource *fd_chr_add_watch(CharDriverState *chr, GIOCondition cond)
{
FDCharDriver *s = chr->opaque;
return g_io_create_watch(s->fd_out, cond);
}
static void fd_chr_update_read_handler(CharDriverState *chr)
{
FDCharDriver *s = chr->opaque;
remove_fd_in_watch(chr);
if (s->fd_in) {
chr->fd_in_tag = io_add_watch_poll(s->fd_in, fd_chr_read_poll,
fd_chr_read, chr);
}
}
static void fd_chr_close(struct CharDriverState *chr)
{
FDCharDriver *s = chr->opaque;
remove_fd_in_watch(chr);
if (s->fd_in) {
g_io_channel_unref(s->fd_in);
}
if (s->fd_out) {
g_io_channel_unref(s->fd_out);
}
g_free(s);
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
}
/* open a character device to a unix fd */
static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out)
{
CharDriverState *chr;
FDCharDriver *s;
chr = qemu_chr_alloc();
s = g_malloc0(sizeof(FDCharDriver));
s->fd_in = io_channel_from_fd(fd_in);
s->fd_out = io_channel_from_fd(fd_out);
qemu_set_nonblock(fd_out);
s->chr = chr;
chr->opaque = s;
chr->chr_add_watch = fd_chr_add_watch;
chr->chr_write = fd_chr_write;
chr->chr_update_read_handler = fd_chr_update_read_handler;
chr->chr_close = fd_chr_close;
return chr;
}
static CharDriverState *qemu_chr_open_pipe(ChardevHostdev *opts)
{
int fd_in, fd_out;
char filename_in[CHR_MAX_FILENAME_SIZE];
char filename_out[CHR_MAX_FILENAME_SIZE];
const char *filename = opts->device;
if (filename == NULL) {
fprintf(stderr, "chardev: pipe: no filename given\n");
Revert "qemu-char: Print strerror message on failure" and deps The commit's purpose is laudable: The only way for chardev drivers to communicate an error was to return a NULL pointer, which resulted in an error message that said _that_ something went wrong, but not _why_. It attempts to achieve it by changing the interface to return 0/-errno and update qemu_chr_open_opts() to use strerror() to display a more helpful error message. Unfortunately, it has serious flaws: 1. Backends "socket" and "udp" return bogus error codes, because qemu_chr_open_socket() and qemu_chr_open_udp() assume that unix_listen_opts(), unix_connect_opts(), inet_listen_opts(), inet_connect_opts() and inet_dgram_opts() fail with errno set appropriately. That assumption is wrong, and the commit turns unspecific error messages into misleading error messages. For instance: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx inet_connect: host and/or port not specified chardev: opening backend "socket" failed: No such file or directory ENOENT is what happens to be in my errno when the backend returns -errno. Let's put ERANGE there just for giggles: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx -drive if=none,iops=99999999999999999999 inet_connect: host and/or port not specified chardev: opening backend "socket" failed: Numerical result out of range Worse: when errno happens to be zero, return -errno erroneously signals success, and qemu_chr_new_from_opts() dies dereferencing uninitialized chr. I observe this with "-serial unix:". 2. All qemu_chr_open_opts() knows about the error is an errno error code. That's simply not enough for a decent message. For instance, when inet_dgram() can't resolve the parameter host, which errno code should it use? What if it can't resolve parameter localaddr? Clue: many backends already report errors in their open methods. Let's revert the flawed commit along with its dependencies, and fix up the silent error paths instead. This reverts commit 6e1db57b2ac9025c2443c665a0d9e78748637b26. Conflicts: console.c hw/baum.c qemu-char.c This reverts commit aad04cd024f0c59f0b96f032cde2e24eb3abba6d. The parts of commit db418a0a "Add stdio char device on windows" that depend on the reverted change fixed up. Signed-off-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2012-02-07 18:09:08 +04:00
return NULL;
}
snprintf(filename_in, CHR_MAX_FILENAME_SIZE, "%s.in", filename);
snprintf(filename_out, CHR_MAX_FILENAME_SIZE, "%s.out", filename);
TFR(fd_in = qemu_open(filename_in, O_RDWR | O_BINARY));
TFR(fd_out = qemu_open(filename_out, O_RDWR | O_BINARY));
if (fd_in < 0 || fd_out < 0) {
if (fd_in >= 0)
close(fd_in);
if (fd_out >= 0)
close(fd_out);
TFR(fd_in = fd_out = qemu_open(filename, O_RDWR | O_BINARY));
if (fd_in < 0) {
Revert "qemu-char: Print strerror message on failure" and deps The commit's purpose is laudable: The only way for chardev drivers to communicate an error was to return a NULL pointer, which resulted in an error message that said _that_ something went wrong, but not _why_. It attempts to achieve it by changing the interface to return 0/-errno and update qemu_chr_open_opts() to use strerror() to display a more helpful error message. Unfortunately, it has serious flaws: 1. Backends "socket" and "udp" return bogus error codes, because qemu_chr_open_socket() and qemu_chr_open_udp() assume that unix_listen_opts(), unix_connect_opts(), inet_listen_opts(), inet_connect_opts() and inet_dgram_opts() fail with errno set appropriately. That assumption is wrong, and the commit turns unspecific error messages into misleading error messages. For instance: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx inet_connect: host and/or port not specified chardev: opening backend "socket" failed: No such file or directory ENOENT is what happens to be in my errno when the backend returns -errno. Let's put ERANGE there just for giggles: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx -drive if=none,iops=99999999999999999999 inet_connect: host and/or port not specified chardev: opening backend "socket" failed: Numerical result out of range Worse: when errno happens to be zero, return -errno erroneously signals success, and qemu_chr_new_from_opts() dies dereferencing uninitialized chr. I observe this with "-serial unix:". 2. All qemu_chr_open_opts() knows about the error is an errno error code. That's simply not enough for a decent message. For instance, when inet_dgram() can't resolve the parameter host, which errno code should it use? What if it can't resolve parameter localaddr? Clue: many backends already report errors in their open methods. Let's revert the flawed commit along with its dependencies, and fix up the silent error paths instead. This reverts commit 6e1db57b2ac9025c2443c665a0d9e78748637b26. Conflicts: console.c hw/baum.c qemu-char.c This reverts commit aad04cd024f0c59f0b96f032cde2e24eb3abba6d. The parts of commit db418a0a "Add stdio char device on windows" that depend on the reverted change fixed up. Signed-off-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2012-02-07 18:09:08 +04:00
return NULL;
}
}
Revert "qemu-char: Print strerror message on failure" and deps The commit's purpose is laudable: The only way for chardev drivers to communicate an error was to return a NULL pointer, which resulted in an error message that said _that_ something went wrong, but not _why_. It attempts to achieve it by changing the interface to return 0/-errno and update qemu_chr_open_opts() to use strerror() to display a more helpful error message. Unfortunately, it has serious flaws: 1. Backends "socket" and "udp" return bogus error codes, because qemu_chr_open_socket() and qemu_chr_open_udp() assume that unix_listen_opts(), unix_connect_opts(), inet_listen_opts(), inet_connect_opts() and inet_dgram_opts() fail with errno set appropriately. That assumption is wrong, and the commit turns unspecific error messages into misleading error messages. For instance: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx inet_connect: host and/or port not specified chardev: opening backend "socket" failed: No such file or directory ENOENT is what happens to be in my errno when the backend returns -errno. Let's put ERANGE there just for giggles: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx -drive if=none,iops=99999999999999999999 inet_connect: host and/or port not specified chardev: opening backend "socket" failed: Numerical result out of range Worse: when errno happens to be zero, return -errno erroneously signals success, and qemu_chr_new_from_opts() dies dereferencing uninitialized chr. I observe this with "-serial unix:". 2. All qemu_chr_open_opts() knows about the error is an errno error code. That's simply not enough for a decent message. For instance, when inet_dgram() can't resolve the parameter host, which errno code should it use? What if it can't resolve parameter localaddr? Clue: many backends already report errors in their open methods. Let's revert the flawed commit along with its dependencies, and fix up the silent error paths instead. This reverts commit 6e1db57b2ac9025c2443c665a0d9e78748637b26. Conflicts: console.c hw/baum.c qemu-char.c This reverts commit aad04cd024f0c59f0b96f032cde2e24eb3abba6d. The parts of commit db418a0a "Add stdio char device on windows" that depend on the reverted change fixed up. Signed-off-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2012-02-07 18:09:08 +04:00
return qemu_chr_open_fd(fd_in, fd_out);
}
/* init terminal so that we can grab keys */
static struct termios oldtty;
static int old_fd0_flags;
static bool stdio_in_use;
static bool stdio_allow_signal;
static bool stdio_echo_state;
static void qemu_chr_set_echo_stdio(CharDriverState *chr, bool echo);
static void term_exit(void)
{
tcsetattr (0, TCSANOW, &oldtty);
fcntl(0, F_SETFL, old_fd0_flags);
}
static void term_stdio_handler(int sig)
{
/* restore echo after resume from suspend. */
qemu_chr_set_echo_stdio(NULL, stdio_echo_state);
}
static void qemu_chr_set_echo_stdio(CharDriverState *chr, bool echo)
{
struct termios tty;
stdio_echo_state = echo;
tty = oldtty;
if (!echo) {
tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
|INLCR|IGNCR|ICRNL|IXON);
tty.c_oflag |= OPOST;
tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN);
tty.c_cflag &= ~(CSIZE|PARENB);
tty.c_cflag |= CS8;
tty.c_cc[VMIN] = 1;
tty.c_cc[VTIME] = 0;
}
if (!stdio_allow_signal)
tty.c_lflag &= ~ISIG;
tcsetattr (0, TCSANOW, &tty);
}
static void qemu_chr_close_stdio(struct CharDriverState *chr)
{
term_exit();
fd_chr_close(chr);
}
static CharDriverState *qemu_chr_open_stdio(ChardevStdio *opts)
{
CharDriverState *chr;
struct sigaction act;
if (is_daemonized()) {
error_report("cannot use stdio with -daemonize");
return NULL;
}
if (stdio_in_use) {
error_report("cannot use stdio by multiple character devices");
exit(1);
}
stdio_in_use = true;
old_fd0_flags = fcntl(0, F_GETFL);
tcgetattr(0, &oldtty);
qemu_set_nonblock(0);
atexit(term_exit);
memset(&act, 0, sizeof(act));
act.sa_handler = term_stdio_handler;
sigaction(SIGCONT, &act, NULL);
chr = qemu_chr_open_fd(0, 1);
chr->chr_close = qemu_chr_close_stdio;
chr->chr_set_echo = qemu_chr_set_echo_stdio;
if (opts->has_signal) {
stdio_allow_signal = opts->signal;
}
qemu_chr_fe_set_echo(chr, false);
Revert "qemu-char: Print strerror message on failure" and deps The commit's purpose is laudable: The only way for chardev drivers to communicate an error was to return a NULL pointer, which resulted in an error message that said _that_ something went wrong, but not _why_. It attempts to achieve it by changing the interface to return 0/-errno and update qemu_chr_open_opts() to use strerror() to display a more helpful error message. Unfortunately, it has serious flaws: 1. Backends "socket" and "udp" return bogus error codes, because qemu_chr_open_socket() and qemu_chr_open_udp() assume that unix_listen_opts(), unix_connect_opts(), inet_listen_opts(), inet_connect_opts() and inet_dgram_opts() fail with errno set appropriately. That assumption is wrong, and the commit turns unspecific error messages into misleading error messages. For instance: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx inet_connect: host and/or port not specified chardev: opening backend "socket" failed: No such file or directory ENOENT is what happens to be in my errno when the backend returns -errno. Let's put ERANGE there just for giggles: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx -drive if=none,iops=99999999999999999999 inet_connect: host and/or port not specified chardev: opening backend "socket" failed: Numerical result out of range Worse: when errno happens to be zero, return -errno erroneously signals success, and qemu_chr_new_from_opts() dies dereferencing uninitialized chr. I observe this with "-serial unix:". 2. All qemu_chr_open_opts() knows about the error is an errno error code. That's simply not enough for a decent message. For instance, when inet_dgram() can't resolve the parameter host, which errno code should it use? What if it can't resolve parameter localaddr? Clue: many backends already report errors in their open methods. Let's revert the flawed commit along with its dependencies, and fix up the silent error paths instead. This reverts commit 6e1db57b2ac9025c2443c665a0d9e78748637b26. Conflicts: console.c hw/baum.c qemu-char.c This reverts commit aad04cd024f0c59f0b96f032cde2e24eb3abba6d. The parts of commit db418a0a "Add stdio char device on windows" that depend on the reverted change fixed up. Signed-off-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2012-02-07 18:09:08 +04:00
return chr;
}
#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
|| defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
|| defined(__GLIBC__)
#define HAVE_CHARDEV_TTY 1
typedef struct {
GIOChannel *fd;
int read_bytes;
/* Protected by the CharDriverState chr_write_lock. */
int connected;
guint timer_tag;
qemu-char: fix deadlock with "-monitor pty" qemu_chr_be_generic_open cannot be called with the write lock taken, because it calls client code that may call qemu_chr_fe_write. This actually happens for the monitor: 0x00007ffff27dbf79 in __GI_raise (sig=sig@entry=6) 0x00007ffff27df388 in __GI_abort () 0x00005555555ef489 in error_exit (err=<optimized out>, msg=msg@entry=0x5555559796d0 <__func__.5959> "qemu_mutex_lock") 0x00005555558f9080 in qemu_mutex_lock (mutex=mutex@entry=0x555556248a30) 0x0000555555713936 in qemu_chr_fe_write (s=0x555556248a30, buf=buf@entry=0x5555563d8870 "QEMU 2.0.90 monitor - type 'help' for more information\r\n", len=56) 0x00005555556217fd in monitor_flush_locked (mon=mon@entry=0x555556251fd0) 0x0000555555621a12 in monitor_flush_locked (mon=0x555556251fd0) monitor_puts (mon=mon@entry=0x555556251fd0, str=0x55555634bfa7 "", str@entry=0x55555634bf70 "QEMU 2.0.90 monitor - type 'help' for more information\n") 0x0000555555624359 in monitor_vprintf (mon=0x555556251fd0, fmt=<optimized out>, ap=<optimized out>) 0x0000555555624414 in monitor_printf (mon=<optimized out>, fmt=fmt@entry=0x5555559105a0 "QEMU %s monitor - type 'help' for more information\n") 0x0000555555629806 in monitor_event (opaque=0x555556251fd0, event=<optimized out>) 0x000055555571343c in qemu_chr_be_generic_open (s=0x555556248a30) To avoid this, defer the call to an idle callback, which will be called as soon as the main loop is re-entered. In order to simplify the cleanup and do it in one place only, change pty_chr_close to call pty_chr_state. To reproduce, run with "-monitor pty", then try to read from the slave /dev/pts/FOO that it creates. Fixes: 9005b2a7589540a3733b3abdcfbccfe7746cd1a1 Reported-by: Li Liang <liangx.z.li@intel.com> Reviewed-by: Fam Zheng <famz@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2014-07-11 14:11:38 +04:00
guint open_tag;
} PtyCharDriver;
static void pty_chr_update_read_handler_locked(CharDriverState *chr);
static void pty_chr_state(CharDriverState *chr, int connected);
static gboolean pty_chr_timer(gpointer opaque)
{
struct CharDriverState *chr = opaque;
PtyCharDriver *s = chr->opaque;
qemu_mutex_lock(&chr->chr_write_lock);
s->timer_tag = 0;
qemu-char: fix deadlock with "-monitor pty" qemu_chr_be_generic_open cannot be called with the write lock taken, because it calls client code that may call qemu_chr_fe_write. This actually happens for the monitor: 0x00007ffff27dbf79 in __GI_raise (sig=sig@entry=6) 0x00007ffff27df388 in __GI_abort () 0x00005555555ef489 in error_exit (err=<optimized out>, msg=msg@entry=0x5555559796d0 <__func__.5959> "qemu_mutex_lock") 0x00005555558f9080 in qemu_mutex_lock (mutex=mutex@entry=0x555556248a30) 0x0000555555713936 in qemu_chr_fe_write (s=0x555556248a30, buf=buf@entry=0x5555563d8870 "QEMU 2.0.90 monitor - type 'help' for more information\r\n", len=56) 0x00005555556217fd in monitor_flush_locked (mon=mon@entry=0x555556251fd0) 0x0000555555621a12 in monitor_flush_locked (mon=0x555556251fd0) monitor_puts (mon=mon@entry=0x555556251fd0, str=0x55555634bfa7 "", str@entry=0x55555634bf70 "QEMU 2.0.90 monitor - type 'help' for more information\n") 0x0000555555624359 in monitor_vprintf (mon=0x555556251fd0, fmt=<optimized out>, ap=<optimized out>) 0x0000555555624414 in monitor_printf (mon=<optimized out>, fmt=fmt@entry=0x5555559105a0 "QEMU %s monitor - type 'help' for more information\n") 0x0000555555629806 in monitor_event (opaque=0x555556251fd0, event=<optimized out>) 0x000055555571343c in qemu_chr_be_generic_open (s=0x555556248a30) To avoid this, defer the call to an idle callback, which will be called as soon as the main loop is re-entered. In order to simplify the cleanup and do it in one place only, change pty_chr_close to call pty_chr_state. To reproduce, run with "-monitor pty", then try to read from the slave /dev/pts/FOO that it creates. Fixes: 9005b2a7589540a3733b3abdcfbccfe7746cd1a1 Reported-by: Li Liang <liangx.z.li@intel.com> Reviewed-by: Fam Zheng <famz@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2014-07-11 14:11:38 +04:00
s->open_tag = 0;
if (!s->connected) {
/* Next poll ... */
pty_chr_update_read_handler_locked(chr);
}
qemu_mutex_unlock(&chr->chr_write_lock);
return FALSE;
}
/* Called with chr_write_lock held. */
static void pty_chr_rearm_timer(CharDriverState *chr, int ms)
{
PtyCharDriver *s = chr->opaque;
if (s->timer_tag) {
g_source_remove(s->timer_tag);
s->timer_tag = 0;
}
if (ms == 1000) {
s->timer_tag = g_timeout_add_seconds(1, pty_chr_timer, chr);
} else {
s->timer_tag = g_timeout_add(ms, pty_chr_timer, chr);
}
}
/* Called with chr_write_lock held. */
static void pty_chr_update_read_handler_locked(CharDriverState *chr)
{
PtyCharDriver *s = chr->opaque;
GPollFD pfd;
pfd.fd = g_io_channel_unix_get_fd(s->fd);
pfd.events = G_IO_OUT;
pfd.revents = 0;
g_poll(&pfd, 1, 0);
if (pfd.revents & G_IO_HUP) {
pty_chr_state(chr, 0);
} else {
pty_chr_state(chr, 1);
}
}
static void pty_chr_update_read_handler(CharDriverState *chr)
{
qemu_mutex_lock(&chr->chr_write_lock);
pty_chr_update_read_handler_locked(chr);
qemu_mutex_unlock(&chr->chr_write_lock);
}
/* Called with chr_write_lock held. */
static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
PtyCharDriver *s = chr->opaque;
if (!s->connected) {
/* guest sends data, check for (re-)connect */
pty_chr_update_read_handler_locked(chr);
if (!s->connected) {
return 0;
}
}
return io_channel_send(s->fd, buf, len);
}
static GSource *pty_chr_add_watch(CharDriverState *chr, GIOCondition cond)
{
PtyCharDriver *s = chr->opaque;
if (!s->connected) {
return NULL;
}
return g_io_create_watch(s->fd, cond);
}
static int pty_chr_read_poll(void *opaque)
{
CharDriverState *chr = opaque;
PtyCharDriver *s = chr->opaque;
s->read_bytes = qemu_chr_be_can_write(chr);
return s->read_bytes;
}
static gboolean pty_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
{
CharDriverState *chr = opaque;
PtyCharDriver *s = chr->opaque;
gsize size, len;
uint8_t buf[READ_BUF_LEN];
GIOStatus status;
len = sizeof(buf);
if (len > s->read_bytes)
len = s->read_bytes;
if (len == 0) {
return TRUE;
}
status = g_io_channel_read_chars(s->fd, (gchar *)buf, len, &size, NULL);
if (status != G_IO_STATUS_NORMAL) {
pty_chr_state(chr, 0);
return FALSE;
} else {
pty_chr_state(chr, 1);
qemu_chr_be_write(chr, buf, size);
}
return TRUE;
}
qemu-char: fix deadlock with "-monitor pty" qemu_chr_be_generic_open cannot be called with the write lock taken, because it calls client code that may call qemu_chr_fe_write. This actually happens for the monitor: 0x00007ffff27dbf79 in __GI_raise (sig=sig@entry=6) 0x00007ffff27df388 in __GI_abort () 0x00005555555ef489 in error_exit (err=<optimized out>, msg=msg@entry=0x5555559796d0 <__func__.5959> "qemu_mutex_lock") 0x00005555558f9080 in qemu_mutex_lock (mutex=mutex@entry=0x555556248a30) 0x0000555555713936 in qemu_chr_fe_write (s=0x555556248a30, buf=buf@entry=0x5555563d8870 "QEMU 2.0.90 monitor - type 'help' for more information\r\n", len=56) 0x00005555556217fd in monitor_flush_locked (mon=mon@entry=0x555556251fd0) 0x0000555555621a12 in monitor_flush_locked (mon=0x555556251fd0) monitor_puts (mon=mon@entry=0x555556251fd0, str=0x55555634bfa7 "", str@entry=0x55555634bf70 "QEMU 2.0.90 monitor - type 'help' for more information\n") 0x0000555555624359 in monitor_vprintf (mon=0x555556251fd0, fmt=<optimized out>, ap=<optimized out>) 0x0000555555624414 in monitor_printf (mon=<optimized out>, fmt=fmt@entry=0x5555559105a0 "QEMU %s monitor - type 'help' for more information\n") 0x0000555555629806 in monitor_event (opaque=0x555556251fd0, event=<optimized out>) 0x000055555571343c in qemu_chr_be_generic_open (s=0x555556248a30) To avoid this, defer the call to an idle callback, which will be called as soon as the main loop is re-entered. In order to simplify the cleanup and do it in one place only, change pty_chr_close to call pty_chr_state. To reproduce, run with "-monitor pty", then try to read from the slave /dev/pts/FOO that it creates. Fixes: 9005b2a7589540a3733b3abdcfbccfe7746cd1a1 Reported-by: Li Liang <liangx.z.li@intel.com> Reviewed-by: Fam Zheng <famz@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2014-07-11 14:11:38 +04:00
static gboolean qemu_chr_be_generic_open_func(gpointer opaque)
{
CharDriverState *chr = opaque;
PtyCharDriver *s = chr->opaque;
s->open_tag = 0;
qemu_chr_be_generic_open(chr);
return FALSE;
}
/* Called with chr_write_lock held. */
static void pty_chr_state(CharDriverState *chr, int connected)
{
PtyCharDriver *s = chr->opaque;
if (!connected) {
qemu-char: fix deadlock with "-monitor pty" qemu_chr_be_generic_open cannot be called with the write lock taken, because it calls client code that may call qemu_chr_fe_write. This actually happens for the monitor: 0x00007ffff27dbf79 in __GI_raise (sig=sig@entry=6) 0x00007ffff27df388 in __GI_abort () 0x00005555555ef489 in error_exit (err=<optimized out>, msg=msg@entry=0x5555559796d0 <__func__.5959> "qemu_mutex_lock") 0x00005555558f9080 in qemu_mutex_lock (mutex=mutex@entry=0x555556248a30) 0x0000555555713936 in qemu_chr_fe_write (s=0x555556248a30, buf=buf@entry=0x5555563d8870 "QEMU 2.0.90 monitor - type 'help' for more information\r\n", len=56) 0x00005555556217fd in monitor_flush_locked (mon=mon@entry=0x555556251fd0) 0x0000555555621a12 in monitor_flush_locked (mon=0x555556251fd0) monitor_puts (mon=mon@entry=0x555556251fd0, str=0x55555634bfa7 "", str@entry=0x55555634bf70 "QEMU 2.0.90 monitor - type 'help' for more information\n") 0x0000555555624359 in monitor_vprintf (mon=0x555556251fd0, fmt=<optimized out>, ap=<optimized out>) 0x0000555555624414 in monitor_printf (mon=<optimized out>, fmt=fmt@entry=0x5555559105a0 "QEMU %s monitor - type 'help' for more information\n") 0x0000555555629806 in monitor_event (opaque=0x555556251fd0, event=<optimized out>) 0x000055555571343c in qemu_chr_be_generic_open (s=0x555556248a30) To avoid this, defer the call to an idle callback, which will be called as soon as the main loop is re-entered. In order to simplify the cleanup and do it in one place only, change pty_chr_close to call pty_chr_state. To reproduce, run with "-monitor pty", then try to read from the slave /dev/pts/FOO that it creates. Fixes: 9005b2a7589540a3733b3abdcfbccfe7746cd1a1 Reported-by: Li Liang <liangx.z.li@intel.com> Reviewed-by: Fam Zheng <famz@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2014-07-11 14:11:38 +04:00
if (s->open_tag) {
g_source_remove(s->open_tag);
s->open_tag = 0;
}
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 {
if (s->timer_tag) {
g_source_remove(s->timer_tag);
s->timer_tag = 0;
}
if (!s->connected) {
qemu-char: fix deadlock with "-monitor pty" qemu_chr_be_generic_open cannot be called with the write lock taken, because it calls client code that may call qemu_chr_fe_write. This actually happens for the monitor: 0x00007ffff27dbf79 in __GI_raise (sig=sig@entry=6) 0x00007ffff27df388 in __GI_abort () 0x00005555555ef489 in error_exit (err=<optimized out>, msg=msg@entry=0x5555559796d0 <__func__.5959> "qemu_mutex_lock") 0x00005555558f9080 in qemu_mutex_lock (mutex=mutex@entry=0x555556248a30) 0x0000555555713936 in qemu_chr_fe_write (s=0x555556248a30, buf=buf@entry=0x5555563d8870 "QEMU 2.0.90 monitor - type 'help' for more information\r\n", len=56) 0x00005555556217fd in monitor_flush_locked (mon=mon@entry=0x555556251fd0) 0x0000555555621a12 in monitor_flush_locked (mon=0x555556251fd0) monitor_puts (mon=mon@entry=0x555556251fd0, str=0x55555634bfa7 "", str@entry=0x55555634bf70 "QEMU 2.0.90 monitor - type 'help' for more information\n") 0x0000555555624359 in monitor_vprintf (mon=0x555556251fd0, fmt=<optimized out>, ap=<optimized out>) 0x0000555555624414 in monitor_printf (mon=<optimized out>, fmt=fmt@entry=0x5555559105a0 "QEMU %s monitor - type 'help' for more information\n") 0x0000555555629806 in monitor_event (opaque=0x555556251fd0, event=<optimized out>) 0x000055555571343c in qemu_chr_be_generic_open (s=0x555556248a30) To avoid this, defer the call to an idle callback, which will be called as soon as the main loop is re-entered. In order to simplify the cleanup and do it in one place only, change pty_chr_close to call pty_chr_state. To reproduce, run with "-monitor pty", then try to read from the slave /dev/pts/FOO that it creates. Fixes: 9005b2a7589540a3733b3abdcfbccfe7746cd1a1 Reported-by: Li Liang <liangx.z.li@intel.com> Reviewed-by: Fam Zheng <famz@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2014-07-11 14:11:38 +04:00
g_assert(s->open_tag == 0);
s->connected = 1;
qemu-char: fix deadlock with "-monitor pty" qemu_chr_be_generic_open cannot be called with the write lock taken, because it calls client code that may call qemu_chr_fe_write. This actually happens for the monitor: 0x00007ffff27dbf79 in __GI_raise (sig=sig@entry=6) 0x00007ffff27df388 in __GI_abort () 0x00005555555ef489 in error_exit (err=<optimized out>, msg=msg@entry=0x5555559796d0 <__func__.5959> "qemu_mutex_lock") 0x00005555558f9080 in qemu_mutex_lock (mutex=mutex@entry=0x555556248a30) 0x0000555555713936 in qemu_chr_fe_write (s=0x555556248a30, buf=buf@entry=0x5555563d8870 "QEMU 2.0.90 monitor - type 'help' for more information\r\n", len=56) 0x00005555556217fd in monitor_flush_locked (mon=mon@entry=0x555556251fd0) 0x0000555555621a12 in monitor_flush_locked (mon=0x555556251fd0) monitor_puts (mon=mon@entry=0x555556251fd0, str=0x55555634bfa7 "", str@entry=0x55555634bf70 "QEMU 2.0.90 monitor - type 'help' for more information\n") 0x0000555555624359 in monitor_vprintf (mon=0x555556251fd0, fmt=<optimized out>, ap=<optimized out>) 0x0000555555624414 in monitor_printf (mon=<optimized out>, fmt=fmt@entry=0x5555559105a0 "QEMU %s monitor - type 'help' for more information\n") 0x0000555555629806 in monitor_event (opaque=0x555556251fd0, event=<optimized out>) 0x000055555571343c in qemu_chr_be_generic_open (s=0x555556248a30) To avoid this, defer the call to an idle callback, which will be called as soon as the main loop is re-entered. In order to simplify the cleanup and do it in one place only, change pty_chr_close to call pty_chr_state. To reproduce, run with "-monitor pty", then try to read from the slave /dev/pts/FOO that it creates. Fixes: 9005b2a7589540a3733b3abdcfbccfe7746cd1a1 Reported-by: Li Liang <liangx.z.li@intel.com> Reviewed-by: Fam Zheng <famz@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2014-07-11 14:11:38 +04:00
s->open_tag = g_idle_add(qemu_chr_be_generic_open_func, chr);
}
if (!chr->fd_in_tag) {
chr->fd_in_tag = io_add_watch_poll(s->fd, pty_chr_read_poll,
pty_chr_read, chr);
}
}
}
static void pty_chr_close(struct CharDriverState *chr)
{
PtyCharDriver *s = chr->opaque;
int fd;
qemu-char: fix deadlock with "-monitor pty" qemu_chr_be_generic_open cannot be called with the write lock taken, because it calls client code that may call qemu_chr_fe_write. This actually happens for the monitor: 0x00007ffff27dbf79 in __GI_raise (sig=sig@entry=6) 0x00007ffff27df388 in __GI_abort () 0x00005555555ef489 in error_exit (err=<optimized out>, msg=msg@entry=0x5555559796d0 <__func__.5959> "qemu_mutex_lock") 0x00005555558f9080 in qemu_mutex_lock (mutex=mutex@entry=0x555556248a30) 0x0000555555713936 in qemu_chr_fe_write (s=0x555556248a30, buf=buf@entry=0x5555563d8870 "QEMU 2.0.90 monitor - type 'help' for more information\r\n", len=56) 0x00005555556217fd in monitor_flush_locked (mon=mon@entry=0x555556251fd0) 0x0000555555621a12 in monitor_flush_locked (mon=0x555556251fd0) monitor_puts (mon=mon@entry=0x555556251fd0, str=0x55555634bfa7 "", str@entry=0x55555634bf70 "QEMU 2.0.90 monitor - type 'help' for more information\n") 0x0000555555624359 in monitor_vprintf (mon=0x555556251fd0, fmt=<optimized out>, ap=<optimized out>) 0x0000555555624414 in monitor_printf (mon=<optimized out>, fmt=fmt@entry=0x5555559105a0 "QEMU %s monitor - type 'help' for more information\n") 0x0000555555629806 in monitor_event (opaque=0x555556251fd0, event=<optimized out>) 0x000055555571343c in qemu_chr_be_generic_open (s=0x555556248a30) To avoid this, defer the call to an idle callback, which will be called as soon as the main loop is re-entered. In order to simplify the cleanup and do it in one place only, change pty_chr_close to call pty_chr_state. To reproduce, run with "-monitor pty", then try to read from the slave /dev/pts/FOO that it creates. Fixes: 9005b2a7589540a3733b3abdcfbccfe7746cd1a1 Reported-by: Li Liang <liangx.z.li@intel.com> Reviewed-by: Fam Zheng <famz@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2014-07-11 14:11:38 +04:00
qemu_mutex_lock(&chr->chr_write_lock);
pty_chr_state(chr, 0);
fd = g_io_channel_unix_get_fd(s->fd);
g_io_channel_unref(s->fd);
close(fd);
if (s->timer_tag) {
g_source_remove(s->timer_tag);
s->timer_tag = 0;
}
qemu-char: fix deadlock with "-monitor pty" qemu_chr_be_generic_open cannot be called with the write lock taken, because it calls client code that may call qemu_chr_fe_write. This actually happens for the monitor: 0x00007ffff27dbf79 in __GI_raise (sig=sig@entry=6) 0x00007ffff27df388 in __GI_abort () 0x00005555555ef489 in error_exit (err=<optimized out>, msg=msg@entry=0x5555559796d0 <__func__.5959> "qemu_mutex_lock") 0x00005555558f9080 in qemu_mutex_lock (mutex=mutex@entry=0x555556248a30) 0x0000555555713936 in qemu_chr_fe_write (s=0x555556248a30, buf=buf@entry=0x5555563d8870 "QEMU 2.0.90 monitor - type 'help' for more information\r\n", len=56) 0x00005555556217fd in monitor_flush_locked (mon=mon@entry=0x555556251fd0) 0x0000555555621a12 in monitor_flush_locked (mon=0x555556251fd0) monitor_puts (mon=mon@entry=0x555556251fd0, str=0x55555634bfa7 "", str@entry=0x55555634bf70 "QEMU 2.0.90 monitor - type 'help' for more information\n") 0x0000555555624359 in monitor_vprintf (mon=0x555556251fd0, fmt=<optimized out>, ap=<optimized out>) 0x0000555555624414 in monitor_printf (mon=<optimized out>, fmt=fmt@entry=0x5555559105a0 "QEMU %s monitor - type 'help' for more information\n") 0x0000555555629806 in monitor_event (opaque=0x555556251fd0, event=<optimized out>) 0x000055555571343c in qemu_chr_be_generic_open (s=0x555556248a30) To avoid this, defer the call to an idle callback, which will be called as soon as the main loop is re-entered. In order to simplify the cleanup and do it in one place only, change pty_chr_close to call pty_chr_state. To reproduce, run with "-monitor pty", then try to read from the slave /dev/pts/FOO that it creates. Fixes: 9005b2a7589540a3733b3abdcfbccfe7746cd1a1 Reported-by: Li Liang <liangx.z.li@intel.com> Reviewed-by: Fam Zheng <famz@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2014-07-11 14:11:38 +04:00
qemu_mutex_unlock(&chr->chr_write_lock);
g_free(s);
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
}
static CharDriverState *qemu_chr_open_pty(const char *id,
ChardevReturn *ret)
{
CharDriverState *chr;
PtyCharDriver *s;
int master_fd, slave_fd;
char pty_name[PATH_MAX];
master_fd = qemu_openpty_raw(&slave_fd, pty_name);
if (master_fd < 0) {
Revert "qemu-char: Print strerror message on failure" and deps The commit's purpose is laudable: The only way for chardev drivers to communicate an error was to return a NULL pointer, which resulted in an error message that said _that_ something went wrong, but not _why_. It attempts to achieve it by changing the interface to return 0/-errno and update qemu_chr_open_opts() to use strerror() to display a more helpful error message. Unfortunately, it has serious flaws: 1. Backends "socket" and "udp" return bogus error codes, because qemu_chr_open_socket() and qemu_chr_open_udp() assume that unix_listen_opts(), unix_connect_opts(), inet_listen_opts(), inet_connect_opts() and inet_dgram_opts() fail with errno set appropriately. That assumption is wrong, and the commit turns unspecific error messages into misleading error messages. For instance: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx inet_connect: host and/or port not specified chardev: opening backend "socket" failed: No such file or directory ENOENT is what happens to be in my errno when the backend returns -errno. Let's put ERANGE there just for giggles: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx -drive if=none,iops=99999999999999999999 inet_connect: host and/or port not specified chardev: opening backend "socket" failed: Numerical result out of range Worse: when errno happens to be zero, return -errno erroneously signals success, and qemu_chr_new_from_opts() dies dereferencing uninitialized chr. I observe this with "-serial unix:". 2. All qemu_chr_open_opts() knows about the error is an errno error code. That's simply not enough for a decent message. For instance, when inet_dgram() can't resolve the parameter host, which errno code should it use? What if it can't resolve parameter localaddr? Clue: many backends already report errors in their open methods. Let's revert the flawed commit along with its dependencies, and fix up the silent error paths instead. This reverts commit 6e1db57b2ac9025c2443c665a0d9e78748637b26. Conflicts: console.c hw/baum.c qemu-char.c This reverts commit aad04cd024f0c59f0b96f032cde2e24eb3abba6d. The parts of commit db418a0a "Add stdio char device on windows" that depend on the reverted change fixed up. Signed-off-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2012-02-07 18:09:08 +04:00
return NULL;
}
close(slave_fd);
qemu_set_nonblock(master_fd);
chr = qemu_chr_alloc();
chr->filename = g_strdup_printf("pty:%s", pty_name);
ret->pty = g_strdup(pty_name);
ret->has_pty = true;
fprintf(stderr, "char device redirected to %s (label %s)\n",
pty_name, id);
s = g_malloc0(sizeof(PtyCharDriver));
chr->opaque = s;
chr->chr_write = pty_chr_write;
chr->chr_update_read_handler = pty_chr_update_read_handler;
chr->chr_close = pty_chr_close;
chr->chr_add_watch = pty_chr_add_watch;
qemu-char: don't issue CHR_EVENT_OPEN in a BH When CHR_EVENT_OPENED was initially added, it was CHR_EVENT_RESET, and it was issued as a bottom-half: 86e94dea5b740dad65446c857f6959eae43e0ba6 Which we basically used to print out a greeting/prompt for the monitor. AFAICT the only reason this was ever done in a BH was because in some cases we'd modify the chr_write handler for a new chardev backend *after* the site where we issued the reset (see: 86e94d:qemu_chr_open_stdio()) At some point this event was renamed to CHR_EVENT_OPENED, and we've maintained the use of this BH ever since. However, due to 9f939df955a4152aad69a19a77e0898631bb2c18, we schedule the BH via g_idle_add(), which is causing events to sometimes be delivered after we've already begun processing data from backends, leading to: known bugs: QMP: session negotation resets with OPENED event, in some cases this is causing new sessions to get sporadically reset potential bugs: hw/usb/redirect.c: can_read handler checks for dev->parser != NULL, which may be true if CLOSED BH has not been executed yet. In the past, OPENED quiesced outstanding CLOSED events prior to us reading client data. If it's delayed, our check may allow reads to occur even though we haven't processed the OPENED event yet, and when we do finally get the OPENED event, our state may get reset. qtest.c: can begin session before OPENED event is processed, leading to a spurious reset of the system and irq_levels gdbstub.c: may start a gdb session prior to the machine being paused To fix these, let's just drop the BH. Since the initial reasoning for using it still applies to an extent, work around that by deferring the delivery of CHR_EVENT_OPENED until after the chardevs have been fully initialized, toward the end of qmp_chardev_add() (or some cases, qemu_chr_new_from_opts()). This defers delivery long enough that we can be assured a CharDriverState is fully initialized before CHR_EVENT_OPENED is sent. Also, rather than requiring each chardev to do an explicit open, do it automatically, and allow the small few who don't desire such behavior to suppress the OPENED-on-init behavior by setting a 'explicit_be_open' flag. We additionally add missing OPENED events for stdio backends on w32, which were previously not being issued, causing us to not recieve the banner and initial prompts for qmp/hmp. Reported-by: Stefan Priebe <s.priebe@profihost.ag> Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com> Message-id: 1370636393-21044-1-git-send-email-mdroth@linux.vnet.ibm.com Cc: qemu-stable@nongnu.org Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-06-08 00:19:53 +04:00
chr->explicit_be_open = true;
s->fd = io_channel_from_fd(master_fd);
s->timer_tag = 0;
Revert "qemu-char: Print strerror message on failure" and deps The commit's purpose is laudable: The only way for chardev drivers to communicate an error was to return a NULL pointer, which resulted in an error message that said _that_ something went wrong, but not _why_. It attempts to achieve it by changing the interface to return 0/-errno and update qemu_chr_open_opts() to use strerror() to display a more helpful error message. Unfortunately, it has serious flaws: 1. Backends "socket" and "udp" return bogus error codes, because qemu_chr_open_socket() and qemu_chr_open_udp() assume that unix_listen_opts(), unix_connect_opts(), inet_listen_opts(), inet_connect_opts() and inet_dgram_opts() fail with errno set appropriately. That assumption is wrong, and the commit turns unspecific error messages into misleading error messages. For instance: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx inet_connect: host and/or port not specified chardev: opening backend "socket" failed: No such file or directory ENOENT is what happens to be in my errno when the backend returns -errno. Let's put ERANGE there just for giggles: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx -drive if=none,iops=99999999999999999999 inet_connect: host and/or port not specified chardev: opening backend "socket" failed: Numerical result out of range Worse: when errno happens to be zero, return -errno erroneously signals success, and qemu_chr_new_from_opts() dies dereferencing uninitialized chr. I observe this with "-serial unix:". 2. All qemu_chr_open_opts() knows about the error is an errno error code. That's simply not enough for a decent message. For instance, when inet_dgram() can't resolve the parameter host, which errno code should it use? What if it can't resolve parameter localaddr? Clue: many backends already report errors in their open methods. Let's revert the flawed commit along with its dependencies, and fix up the silent error paths instead. This reverts commit 6e1db57b2ac9025c2443c665a0d9e78748637b26. Conflicts: console.c hw/baum.c qemu-char.c This reverts commit aad04cd024f0c59f0b96f032cde2e24eb3abba6d. The parts of commit db418a0a "Add stdio char device on windows" that depend on the reverted change fixed up. Signed-off-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2012-02-07 18:09:08 +04:00
return chr;
}
static void tty_serial_init(int fd, int speed,
int parity, int data_bits, int stop_bits)
{
struct termios tty;
speed_t spd;
#if 0
printf("tty_serial_init: speed=%d parity=%c data=%d stop=%d\n",
speed, parity, data_bits, stop_bits);
#endif
tcgetattr (fd, &tty);
#define check_speed(val) if (speed <= val) { spd = B##val; break; }
speed = speed * 10 / 11;
do {
check_speed(50);
check_speed(75);
check_speed(110);
check_speed(134);
check_speed(150);
check_speed(200);
check_speed(300);
check_speed(600);
check_speed(1200);
check_speed(1800);
check_speed(2400);
check_speed(4800);
check_speed(9600);
check_speed(19200);
check_speed(38400);
/* Non-Posix values follow. They may be unsupported on some systems. */
check_speed(57600);
check_speed(115200);
#ifdef B230400
check_speed(230400);
#endif
#ifdef B460800
check_speed(460800);
#endif
#ifdef B500000
check_speed(500000);
#endif
#ifdef B576000
check_speed(576000);
#endif
#ifdef B921600
check_speed(921600);
#endif
#ifdef B1000000
check_speed(1000000);
#endif
#ifdef B1152000
check_speed(1152000);
#endif
#ifdef B1500000
check_speed(1500000);
#endif
#ifdef B2000000
check_speed(2000000);
#endif
#ifdef B2500000
check_speed(2500000);
#endif
#ifdef B3000000
check_speed(3000000);
#endif
#ifdef B3500000
check_speed(3500000);
#endif
#ifdef B4000000
check_speed(4000000);
#endif
spd = B115200;
} while (0);
cfsetispeed(&tty, spd);
cfsetospeed(&tty, spd);
tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
|INLCR|IGNCR|ICRNL|IXON);
tty.c_oflag |= OPOST;
tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN|ISIG);
tty.c_cflag &= ~(CSIZE|PARENB|PARODD|CRTSCTS|CSTOPB);
switch(data_bits) {
default:
case 8:
tty.c_cflag |= CS8;
break;
case 7:
tty.c_cflag |= CS7;
break;
case 6:
tty.c_cflag |= CS6;
break;
case 5:
tty.c_cflag |= CS5;
break;
}
switch(parity) {
default:
case 'N':
break;
case 'E':
tty.c_cflag |= PARENB;
break;
case 'O':
tty.c_cflag |= PARENB | PARODD;
break;
}
if (stop_bits == 2)
tty.c_cflag |= CSTOPB;
tcsetattr (fd, TCSANOW, &tty);
}
static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg)
{
FDCharDriver *s = chr->opaque;
switch(cmd) {
case CHR_IOCTL_SERIAL_SET_PARAMS:
{
QEMUSerialSetParams *ssp = arg;
tty_serial_init(g_io_channel_unix_get_fd(s->fd_in),
ssp->speed, ssp->parity,
ssp->data_bits, ssp->stop_bits);
}
break;
case CHR_IOCTL_SERIAL_SET_BREAK:
{
int enable = *(int *)arg;
if (enable) {
tcsendbreak(g_io_channel_unix_get_fd(s->fd_in), 1);
}
}
break;
case CHR_IOCTL_SERIAL_GET_TIOCM:
{
int sarg = 0;
int *targ = (int *)arg;
ioctl(g_io_channel_unix_get_fd(s->fd_in), TIOCMGET, &sarg);
*targ = 0;
if (sarg & TIOCM_CTS)
*targ |= CHR_TIOCM_CTS;
if (sarg & TIOCM_CAR)
*targ |= CHR_TIOCM_CAR;
if (sarg & TIOCM_DSR)
*targ |= CHR_TIOCM_DSR;
if (sarg & TIOCM_RI)
*targ |= CHR_TIOCM_RI;
if (sarg & TIOCM_DTR)
*targ |= CHR_TIOCM_DTR;
if (sarg & TIOCM_RTS)
*targ |= CHR_TIOCM_RTS;
}
break;
case CHR_IOCTL_SERIAL_SET_TIOCM:
{
int sarg = *(int *)arg;
int targ = 0;
ioctl(g_io_channel_unix_get_fd(s->fd_in), TIOCMGET, &targ);
targ &= ~(CHR_TIOCM_CTS | CHR_TIOCM_CAR | CHR_TIOCM_DSR
| CHR_TIOCM_RI | CHR_TIOCM_DTR | CHR_TIOCM_RTS);
if (sarg & CHR_TIOCM_CTS)
targ |= TIOCM_CTS;
if (sarg & CHR_TIOCM_CAR)
targ |= TIOCM_CAR;
if (sarg & CHR_TIOCM_DSR)
targ |= TIOCM_DSR;
if (sarg & CHR_TIOCM_RI)
targ |= TIOCM_RI;
if (sarg & CHR_TIOCM_DTR)
targ |= TIOCM_DTR;
if (sarg & CHR_TIOCM_RTS)
targ |= TIOCM_RTS;
ioctl(g_io_channel_unix_get_fd(s->fd_in), TIOCMSET, &targ);
}
break;
default:
return -ENOTSUP;
}
return 0;
}
static void qemu_chr_close_tty(CharDriverState *chr)
{
FDCharDriver *s = chr->opaque;
int fd = -1;
if (s) {
fd = g_io_channel_unix_get_fd(s->fd_in);
}
fd_chr_close(chr);
if (fd >= 0) {
close(fd);
}
}
static CharDriverState *qemu_chr_open_tty_fd(int fd)
{
CharDriverState *chr;
tty_serial_init(fd, 115200, 'N', 8, 1);
chr = qemu_chr_open_fd(fd, fd);
chr->chr_ioctl = tty_serial_ioctl;
chr->chr_close = qemu_chr_close_tty;
return chr;
}
#endif /* __linux__ || __sun__ */
#if defined(__linux__)
#define HAVE_CHARDEV_PARPORT 1
typedef struct {
int fd;
int mode;
} ParallelCharDriver;
static int pp_hw_mode(ParallelCharDriver *s, uint16_t mode)
{
if (s->mode != mode) {
int m = mode;
if (ioctl(s->fd, PPSETMODE, &m) < 0)
return 0;
s->mode = mode;
}
return 1;
}
static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
{
ParallelCharDriver *drv = chr->opaque;
int fd = drv->fd;
uint8_t b;
switch(cmd) {
case CHR_IOCTL_PP_READ_DATA:
if (ioctl(fd, PPRDATA, &b) < 0)
return -ENOTSUP;
*(uint8_t *)arg = b;
break;
case CHR_IOCTL_PP_WRITE_DATA:
b = *(uint8_t *)arg;
if (ioctl(fd, PPWDATA, &b) < 0)
return -ENOTSUP;
break;
case CHR_IOCTL_PP_READ_CONTROL:
if (ioctl(fd, PPRCONTROL, &b) < 0)
return -ENOTSUP;
/* Linux gives only the lowest bits, and no way to know data
direction! For better compatibility set the fixed upper
bits. */
*(uint8_t *)arg = b | 0xc0;
break;
case CHR_IOCTL_PP_WRITE_CONTROL:
b = *(uint8_t *)arg;
if (ioctl(fd, PPWCONTROL, &b) < 0)
return -ENOTSUP;
break;
case CHR_IOCTL_PP_READ_STATUS:
if (ioctl(fd, PPRSTATUS, &b) < 0)
return -ENOTSUP;
*(uint8_t *)arg = b;
break;
case CHR_IOCTL_PP_DATA_DIR:
if (ioctl(fd, PPDATADIR, (int *)arg) < 0)
return -ENOTSUP;
break;
case CHR_IOCTL_PP_EPP_READ_ADDR:
if (pp_hw_mode(drv, IEEE1284_MODE_EPP|IEEE1284_ADDR)) {
struct ParallelIOArg *parg = arg;
int n = read(fd, parg->buffer, parg->count);
if (n != parg->count) {
return -EIO;
}
}
break;
case CHR_IOCTL_PP_EPP_READ:
if (pp_hw_mode(drv, IEEE1284_MODE_EPP)) {
struct ParallelIOArg *parg = arg;
int n = read(fd, parg->buffer, parg->count);
if (n != parg->count) {
return -EIO;
}
}
break;
case CHR_IOCTL_PP_EPP_WRITE_ADDR:
if (pp_hw_mode(drv, IEEE1284_MODE_EPP|IEEE1284_ADDR)) {
struct ParallelIOArg *parg = arg;
int n = write(fd, parg->buffer, parg->count);
if (n != parg->count) {
return -EIO;
}
}
break;
case CHR_IOCTL_PP_EPP_WRITE:
if (pp_hw_mode(drv, IEEE1284_MODE_EPP)) {
struct ParallelIOArg *parg = arg;
int n = write(fd, parg->buffer, parg->count);
if (n != parg->count) {
return -EIO;
}
}
break;
default:
return -ENOTSUP;
}
return 0;
}
static void pp_close(CharDriverState *chr)
{
ParallelCharDriver *drv = chr->opaque;
int fd = drv->fd;
pp_hw_mode(drv, IEEE1284_MODE_COMPAT);
ioctl(fd, PPRELEASE);
close(fd);
g_free(drv);
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
}
static CharDriverState *qemu_chr_open_pp_fd(int fd)
{
CharDriverState *chr;
ParallelCharDriver *drv;
if (ioctl(fd, PPCLAIM) < 0) {
close(fd);
Revert "qemu-char: Print strerror message on failure" and deps The commit's purpose is laudable: The only way for chardev drivers to communicate an error was to return a NULL pointer, which resulted in an error message that said _that_ something went wrong, but not _why_. It attempts to achieve it by changing the interface to return 0/-errno and update qemu_chr_open_opts() to use strerror() to display a more helpful error message. Unfortunately, it has serious flaws: 1. Backends "socket" and "udp" return bogus error codes, because qemu_chr_open_socket() and qemu_chr_open_udp() assume that unix_listen_opts(), unix_connect_opts(), inet_listen_opts(), inet_connect_opts() and inet_dgram_opts() fail with errno set appropriately. That assumption is wrong, and the commit turns unspecific error messages into misleading error messages. For instance: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx inet_connect: host and/or port not specified chardev: opening backend "socket" failed: No such file or directory ENOENT is what happens to be in my errno when the backend returns -errno. Let's put ERANGE there just for giggles: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx -drive if=none,iops=99999999999999999999 inet_connect: host and/or port not specified chardev: opening backend "socket" failed: Numerical result out of range Worse: when errno happens to be zero, return -errno erroneously signals success, and qemu_chr_new_from_opts() dies dereferencing uninitialized chr. I observe this with "-serial unix:". 2. All qemu_chr_open_opts() knows about the error is an errno error code. That's simply not enough for a decent message. For instance, when inet_dgram() can't resolve the parameter host, which errno code should it use? What if it can't resolve parameter localaddr? Clue: many backends already report errors in their open methods. Let's revert the flawed commit along with its dependencies, and fix up the silent error paths instead. This reverts commit 6e1db57b2ac9025c2443c665a0d9e78748637b26. Conflicts: console.c hw/baum.c qemu-char.c This reverts commit aad04cd024f0c59f0b96f032cde2e24eb3abba6d. The parts of commit db418a0a "Add stdio char device on windows" that depend on the reverted change fixed up. Signed-off-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2012-02-07 18:09:08 +04:00
return NULL;
}
drv = g_malloc0(sizeof(ParallelCharDriver));
drv->fd = fd;
drv->mode = IEEE1284_MODE_COMPAT;
chr = qemu_chr_alloc();
chr->chr_write = null_chr_write;
chr->chr_ioctl = pp_ioctl;
chr->chr_close = pp_close;
chr->opaque = drv;
Revert "qemu-char: Print strerror message on failure" and deps The commit's purpose is laudable: The only way for chardev drivers to communicate an error was to return a NULL pointer, which resulted in an error message that said _that_ something went wrong, but not _why_. It attempts to achieve it by changing the interface to return 0/-errno and update qemu_chr_open_opts() to use strerror() to display a more helpful error message. Unfortunately, it has serious flaws: 1. Backends "socket" and "udp" return bogus error codes, because qemu_chr_open_socket() and qemu_chr_open_udp() assume that unix_listen_opts(), unix_connect_opts(), inet_listen_opts(), inet_connect_opts() and inet_dgram_opts() fail with errno set appropriately. That assumption is wrong, and the commit turns unspecific error messages into misleading error messages. For instance: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx inet_connect: host and/or port not specified chardev: opening backend "socket" failed: No such file or directory ENOENT is what happens to be in my errno when the backend returns -errno. Let's put ERANGE there just for giggles: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx -drive if=none,iops=99999999999999999999 inet_connect: host and/or port not specified chardev: opening backend "socket" failed: Numerical result out of range Worse: when errno happens to be zero, return -errno erroneously signals success, and qemu_chr_new_from_opts() dies dereferencing uninitialized chr. I observe this with "-serial unix:". 2. All qemu_chr_open_opts() knows about the error is an errno error code. That's simply not enough for a decent message. For instance, when inet_dgram() can't resolve the parameter host, which errno code should it use? What if it can't resolve parameter localaddr? Clue: many backends already report errors in their open methods. Let's revert the flawed commit along with its dependencies, and fix up the silent error paths instead. This reverts commit 6e1db57b2ac9025c2443c665a0d9e78748637b26. Conflicts: console.c hw/baum.c qemu-char.c This reverts commit aad04cd024f0c59f0b96f032cde2e24eb3abba6d. The parts of commit db418a0a "Add stdio char device on windows" that depend on the reverted change fixed up. Signed-off-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2012-02-07 18:09:08 +04:00
return chr;
}
#endif /* __linux__ */
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
#define HAVE_CHARDEV_PARPORT 1
static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
{
int fd = (int)(intptr_t)chr->opaque;
uint8_t b;
switch(cmd) {
case CHR_IOCTL_PP_READ_DATA:
if (ioctl(fd, PPIGDATA, &b) < 0)
return -ENOTSUP;
*(uint8_t *)arg = b;
break;
case CHR_IOCTL_PP_WRITE_DATA:
b = *(uint8_t *)arg;
if (ioctl(fd, PPISDATA, &b) < 0)
return -ENOTSUP;
break;
case CHR_IOCTL_PP_READ_CONTROL:
if (ioctl(fd, PPIGCTRL, &b) < 0)
return -ENOTSUP;
*(uint8_t *)arg = b;
break;
case CHR_IOCTL_PP_WRITE_CONTROL:
b = *(uint8_t *)arg;
if (ioctl(fd, PPISCTRL, &b) < 0)
return -ENOTSUP;
break;
case CHR_IOCTL_PP_READ_STATUS:
if (ioctl(fd, PPIGSTATUS, &b) < 0)
return -ENOTSUP;
*(uint8_t *)arg = b;
break;
default:
return -ENOTSUP;
}
return 0;
}
static CharDriverState *qemu_chr_open_pp_fd(int fd)
{
CharDriverState *chr;
chr = qemu_chr_alloc();
chr->opaque = (void *)(intptr_t)fd;
chr->chr_write = null_chr_write;
chr->chr_ioctl = pp_ioctl;
qemu-char: don't issue CHR_EVENT_OPEN in a BH When CHR_EVENT_OPENED was initially added, it was CHR_EVENT_RESET, and it was issued as a bottom-half: 86e94dea5b740dad65446c857f6959eae43e0ba6 Which we basically used to print out a greeting/prompt for the monitor. AFAICT the only reason this was ever done in a BH was because in some cases we'd modify the chr_write handler for a new chardev backend *after* the site where we issued the reset (see: 86e94d:qemu_chr_open_stdio()) At some point this event was renamed to CHR_EVENT_OPENED, and we've maintained the use of this BH ever since. However, due to 9f939df955a4152aad69a19a77e0898631bb2c18, we schedule the BH via g_idle_add(), which is causing events to sometimes be delivered after we've already begun processing data from backends, leading to: known bugs: QMP: session negotation resets with OPENED event, in some cases this is causing new sessions to get sporadically reset potential bugs: hw/usb/redirect.c: can_read handler checks for dev->parser != NULL, which may be true if CLOSED BH has not been executed yet. In the past, OPENED quiesced outstanding CLOSED events prior to us reading client data. If it's delayed, our check may allow reads to occur even though we haven't processed the OPENED event yet, and when we do finally get the OPENED event, our state may get reset. qtest.c: can begin session before OPENED event is processed, leading to a spurious reset of the system and irq_levels gdbstub.c: may start a gdb session prior to the machine being paused To fix these, let's just drop the BH. Since the initial reasoning for using it still applies to an extent, work around that by deferring the delivery of CHR_EVENT_OPENED until after the chardevs have been fully initialized, toward the end of qmp_chardev_add() (or some cases, qemu_chr_new_from_opts()). This defers delivery long enough that we can be assured a CharDriverState is fully initialized before CHR_EVENT_OPENED is sent. Also, rather than requiring each chardev to do an explicit open, do it automatically, and allow the small few who don't desire such behavior to suppress the OPENED-on-init behavior by setting a 'explicit_be_open' flag. We additionally add missing OPENED events for stdio backends on w32, which were previously not being issued, causing us to not recieve the banner and initial prompts for qmp/hmp. Reported-by: Stefan Priebe <s.priebe@profihost.ag> Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com> Message-id: 1370636393-21044-1-git-send-email-mdroth@linux.vnet.ibm.com Cc: qemu-stable@nongnu.org Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-06-08 00:19:53 +04:00
chr->explicit_be_open = true;
Revert "qemu-char: Print strerror message on failure" and deps The commit's purpose is laudable: The only way for chardev drivers to communicate an error was to return a NULL pointer, which resulted in an error message that said _that_ something went wrong, but not _why_. It attempts to achieve it by changing the interface to return 0/-errno and update qemu_chr_open_opts() to use strerror() to display a more helpful error message. Unfortunately, it has serious flaws: 1. Backends "socket" and "udp" return bogus error codes, because qemu_chr_open_socket() and qemu_chr_open_udp() assume that unix_listen_opts(), unix_connect_opts(), inet_listen_opts(), inet_connect_opts() and inet_dgram_opts() fail with errno set appropriately. That assumption is wrong, and the commit turns unspecific error messages into misleading error messages. For instance: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx inet_connect: host and/or port not specified chardev: opening backend "socket" failed: No such file or directory ENOENT is what happens to be in my errno when the backend returns -errno. Let's put ERANGE there just for giggles: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx -drive if=none,iops=99999999999999999999 inet_connect: host and/or port not specified chardev: opening backend "socket" failed: Numerical result out of range Worse: when errno happens to be zero, return -errno erroneously signals success, and qemu_chr_new_from_opts() dies dereferencing uninitialized chr. I observe this with "-serial unix:". 2. All qemu_chr_open_opts() knows about the error is an errno error code. That's simply not enough for a decent message. For instance, when inet_dgram() can't resolve the parameter host, which errno code should it use? What if it can't resolve parameter localaddr? Clue: many backends already report errors in their open methods. Let's revert the flawed commit along with its dependencies, and fix up the silent error paths instead. This reverts commit 6e1db57b2ac9025c2443c665a0d9e78748637b26. Conflicts: console.c hw/baum.c qemu-char.c This reverts commit aad04cd024f0c59f0b96f032cde2e24eb3abba6d. The parts of commit db418a0a "Add stdio char device on windows" that depend on the reverted change fixed up. Signed-off-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2012-02-07 18:09:08 +04:00
return chr;
}
#endif
#else /* _WIN32 */
typedef struct {
int max_size;
HANDLE hcom, hrecv, hsend;
OVERLAPPED orecv;
BOOL fpipe;
DWORD len;
/* Protected by the CharDriverState chr_write_lock. */
OVERLAPPED osend;
} WinCharState;
typedef struct {
HANDLE hStdIn;
HANDLE hInputReadyEvent;
HANDLE hInputDoneEvent;
HANDLE hInputThread;
uint8_t win_stdio_buf;
} WinStdioCharState;
#define NSENDBUF 2048
#define NRECVBUF 2048
#define MAXCONNECT 1
#define NTIMEOUT 5000
static int win_chr_poll(void *opaque);
static int win_chr_pipe_poll(void *opaque);
static void win_chr_close(CharDriverState *chr)
{
WinCharState *s = chr->opaque;
if (s->hsend) {
CloseHandle(s->hsend);
s->hsend = NULL;
}
if (s->hrecv) {
CloseHandle(s->hrecv);
s->hrecv = NULL;
}
if (s->hcom) {
CloseHandle(s->hcom);
s->hcom = NULL;
}
if (s->fpipe)
qemu_del_polling_cb(win_chr_pipe_poll, chr);
else
qemu_del_polling_cb(win_chr_poll, chr);
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
}
static int win_chr_init(CharDriverState *chr, const char *filename)
{
WinCharState *s = chr->opaque;
COMMCONFIG comcfg;
COMMTIMEOUTS cto = { 0, 0, 0, 0, 0};
COMSTAT comstat;
DWORD size;
DWORD err;
s->hsend = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!s->hsend) {
fprintf(stderr, "Failed CreateEvent\n");
goto fail;
}
s->hrecv = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!s->hrecv) {
fprintf(stderr, "Failed CreateEvent\n");
goto fail;
}
s->hcom = CreateFile(filename, GENERIC_READ|GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
if (s->hcom == INVALID_HANDLE_VALUE) {
fprintf(stderr, "Failed CreateFile (%lu)\n", GetLastError());
s->hcom = NULL;
goto fail;
}
if (!SetupComm(s->hcom, NRECVBUF, NSENDBUF)) {
fprintf(stderr, "Failed SetupComm\n");
goto fail;
}
ZeroMemory(&comcfg, sizeof(COMMCONFIG));
size = sizeof(COMMCONFIG);
GetDefaultCommConfig(filename, &comcfg, &size);
comcfg.dcb.DCBlength = sizeof(DCB);
CommConfigDialog(filename, NULL, &comcfg);
if (!SetCommState(s->hcom, &comcfg.dcb)) {
fprintf(stderr, "Failed SetCommState\n");
goto fail;
}
if (!SetCommMask(s->hcom, EV_ERR)) {
fprintf(stderr, "Failed SetCommMask\n");
goto fail;
}
cto.ReadIntervalTimeout = MAXDWORD;
if (!SetCommTimeouts(s->hcom, &cto)) {
fprintf(stderr, "Failed SetCommTimeouts\n");
goto fail;
}
if (!ClearCommError(s->hcom, &err, &comstat)) {
fprintf(stderr, "Failed ClearCommError\n");
goto fail;
}
qemu_add_polling_cb(win_chr_poll, chr);
return 0;
fail:
win_chr_close(chr);
return -1;
}
/* Called with chr_write_lock held. */
static int win_chr_write(CharDriverState *chr, const uint8_t *buf, int len1)
{
WinCharState *s = chr->opaque;
DWORD len, ret, size, err;
len = len1;
ZeroMemory(&s->osend, sizeof(s->osend));
s->osend.hEvent = s->hsend;
while (len > 0) {
if (s->hsend)
ret = WriteFile(s->hcom, buf, len, &size, &s->osend);
else
ret = WriteFile(s->hcom, buf, len, &size, NULL);
if (!ret) {
err = GetLastError();
if (err == ERROR_IO_PENDING) {
ret = GetOverlappedResult(s->hcom, &s->osend, &size, TRUE);
if (ret) {
buf += size;
len -= size;
} else {
break;
}
} else {
break;
}
} else {
buf += size;
len -= size;
}
}
return len1 - len;
}
static int win_chr_read_poll(CharDriverState *chr)
{
WinCharState *s = chr->opaque;
s->max_size = qemu_chr_be_can_write(chr);
return s->max_size;
}
static void win_chr_readfile(CharDriverState *chr)
{
WinCharState *s = chr->opaque;
int ret, err;
uint8_t buf[READ_BUF_LEN];
DWORD size;
ZeroMemory(&s->orecv, sizeof(s->orecv));
s->orecv.hEvent = s->hrecv;
ret = ReadFile(s->hcom, buf, s->len, &size, &s->orecv);
if (!ret) {
err = GetLastError();
if (err == ERROR_IO_PENDING) {
ret = GetOverlappedResult(s->hcom, &s->orecv, &size, TRUE);
}
}
if (size > 0) {
qemu_chr_be_write(chr, buf, size);
}
}
static void win_chr_read(CharDriverState *chr)
{
WinCharState *s = chr->opaque;
if (s->len > s->max_size)
s->len = s->max_size;
if (s->len == 0)
return;
win_chr_readfile(chr);
}
static int win_chr_poll(void *opaque)
{
CharDriverState *chr = opaque;
WinCharState *s = chr->opaque;
COMSTAT status;
DWORD comerr;
ClearCommError(s->hcom, &comerr, &status);
if (status.cbInQue > 0) {
s->len = status.cbInQue;
win_chr_read_poll(chr);
win_chr_read(chr);
return 1;
}
return 0;
}
static CharDriverState *qemu_chr_open_win_path(const char *filename)
{
CharDriverState *chr;
WinCharState *s;
chr = qemu_chr_alloc();
s = g_malloc0(sizeof(WinCharState));
chr->opaque = s;
chr->chr_write = win_chr_write;
chr->chr_close = win_chr_close;
if (win_chr_init(chr, filename) < 0) {
g_free(s);
g_free(chr);
Revert "qemu-char: Print strerror message on failure" and deps The commit's purpose is laudable: The only way for chardev drivers to communicate an error was to return a NULL pointer, which resulted in an error message that said _that_ something went wrong, but not _why_. It attempts to achieve it by changing the interface to return 0/-errno and update qemu_chr_open_opts() to use strerror() to display a more helpful error message. Unfortunately, it has serious flaws: 1. Backends "socket" and "udp" return bogus error codes, because qemu_chr_open_socket() and qemu_chr_open_udp() assume that unix_listen_opts(), unix_connect_opts(), inet_listen_opts(), inet_connect_opts() and inet_dgram_opts() fail with errno set appropriately. That assumption is wrong, and the commit turns unspecific error messages into misleading error messages. For instance: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx inet_connect: host and/or port not specified chardev: opening backend "socket" failed: No such file or directory ENOENT is what happens to be in my errno when the backend returns -errno. Let's put ERANGE there just for giggles: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx -drive if=none,iops=99999999999999999999 inet_connect: host and/or port not specified chardev: opening backend "socket" failed: Numerical result out of range Worse: when errno happens to be zero, return -errno erroneously signals success, and qemu_chr_new_from_opts() dies dereferencing uninitialized chr. I observe this with "-serial unix:". 2. All qemu_chr_open_opts() knows about the error is an errno error code. That's simply not enough for a decent message. For instance, when inet_dgram() can't resolve the parameter host, which errno code should it use? What if it can't resolve parameter localaddr? Clue: many backends already report errors in their open methods. Let's revert the flawed commit along with its dependencies, and fix up the silent error paths instead. This reverts commit 6e1db57b2ac9025c2443c665a0d9e78748637b26. Conflicts: console.c hw/baum.c qemu-char.c This reverts commit aad04cd024f0c59f0b96f032cde2e24eb3abba6d. The parts of commit db418a0a "Add stdio char device on windows" that depend on the reverted change fixed up. Signed-off-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2012-02-07 18:09:08 +04:00
return NULL;
}
Revert "qemu-char: Print strerror message on failure" and deps The commit's purpose is laudable: The only way for chardev drivers to communicate an error was to return a NULL pointer, which resulted in an error message that said _that_ something went wrong, but not _why_. It attempts to achieve it by changing the interface to return 0/-errno and update qemu_chr_open_opts() to use strerror() to display a more helpful error message. Unfortunately, it has serious flaws: 1. Backends "socket" and "udp" return bogus error codes, because qemu_chr_open_socket() and qemu_chr_open_udp() assume that unix_listen_opts(), unix_connect_opts(), inet_listen_opts(), inet_connect_opts() and inet_dgram_opts() fail with errno set appropriately. That assumption is wrong, and the commit turns unspecific error messages into misleading error messages. For instance: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx inet_connect: host and/or port not specified chardev: opening backend "socket" failed: No such file or directory ENOENT is what happens to be in my errno when the backend returns -errno. Let's put ERANGE there just for giggles: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx -drive if=none,iops=99999999999999999999 inet_connect: host and/or port not specified chardev: opening backend "socket" failed: Numerical result out of range Worse: when errno happens to be zero, return -errno erroneously signals success, and qemu_chr_new_from_opts() dies dereferencing uninitialized chr. I observe this with "-serial unix:". 2. All qemu_chr_open_opts() knows about the error is an errno error code. That's simply not enough for a decent message. For instance, when inet_dgram() can't resolve the parameter host, which errno code should it use? What if it can't resolve parameter localaddr? Clue: many backends already report errors in their open methods. Let's revert the flawed commit along with its dependencies, and fix up the silent error paths instead. This reverts commit 6e1db57b2ac9025c2443c665a0d9e78748637b26. Conflicts: console.c hw/baum.c qemu-char.c This reverts commit aad04cd024f0c59f0b96f032cde2e24eb3abba6d. The parts of commit db418a0a "Add stdio char device on windows" that depend on the reverted change fixed up. Signed-off-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2012-02-07 18:09:08 +04:00
return chr;
}
static int win_chr_pipe_poll(void *opaque)
{
CharDriverState *chr = opaque;
WinCharState *s = chr->opaque;
DWORD size;
PeekNamedPipe(s->hcom, NULL, 0, NULL, &size, NULL);
if (size > 0) {
s->len = size;
win_chr_read_poll(chr);
win_chr_read(chr);
return 1;
}
return 0;
}
static int win_chr_pipe_init(CharDriverState *chr, const char *filename)
{
WinCharState *s = chr->opaque;
OVERLAPPED ov;
int ret;
DWORD size;
char openname[CHR_MAX_FILENAME_SIZE];
s->fpipe = TRUE;
s->hsend = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!s->hsend) {
fprintf(stderr, "Failed CreateEvent\n");
goto fail;
}
s->hrecv = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!s->hrecv) {
fprintf(stderr, "Failed CreateEvent\n");
goto fail;
}
snprintf(openname, sizeof(openname), "\\\\.\\pipe\\%s", filename);
s->hcom = CreateNamedPipe(openname, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE |
PIPE_WAIT,
MAXCONNECT, NSENDBUF, NRECVBUF, NTIMEOUT, NULL);
if (s->hcom == INVALID_HANDLE_VALUE) {
fprintf(stderr, "Failed CreateNamedPipe (%lu)\n", GetLastError());
s->hcom = NULL;
goto fail;
}
ZeroMemory(&ov, sizeof(ov));
ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
ret = ConnectNamedPipe(s->hcom, &ov);
if (ret) {
fprintf(stderr, "Failed ConnectNamedPipe\n");
goto fail;
}
ret = GetOverlappedResult(s->hcom, &ov, &size, TRUE);
if (!ret) {
fprintf(stderr, "Failed GetOverlappedResult\n");
if (ov.hEvent) {
CloseHandle(ov.hEvent);
ov.hEvent = NULL;
}
goto fail;
}
if (ov.hEvent) {
CloseHandle(ov.hEvent);
ov.hEvent = NULL;
}
qemu_add_polling_cb(win_chr_pipe_poll, chr);
return 0;
fail:
win_chr_close(chr);
return -1;
}
static CharDriverState *qemu_chr_open_pipe(ChardevHostdev *opts)
{
const char *filename = opts->device;
CharDriverState *chr;
WinCharState *s;
chr = qemu_chr_alloc();
s = g_malloc0(sizeof(WinCharState));
chr->opaque = s;
chr->chr_write = win_chr_write;
chr->chr_close = win_chr_close;
if (win_chr_pipe_init(chr, filename) < 0) {
g_free(s);
g_free(chr);
Revert "qemu-char: Print strerror message on failure" and deps The commit's purpose is laudable: The only way for chardev drivers to communicate an error was to return a NULL pointer, which resulted in an error message that said _that_ something went wrong, but not _why_. It attempts to achieve it by changing the interface to return 0/-errno and update qemu_chr_open_opts() to use strerror() to display a more helpful error message. Unfortunately, it has serious flaws: 1. Backends "socket" and "udp" return bogus error codes, because qemu_chr_open_socket() and qemu_chr_open_udp() assume that unix_listen_opts(), unix_connect_opts(), inet_listen_opts(), inet_connect_opts() and inet_dgram_opts() fail with errno set appropriately. That assumption is wrong, and the commit turns unspecific error messages into misleading error messages. For instance: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx inet_connect: host and/or port not specified chardev: opening backend "socket" failed: No such file or directory ENOENT is what happens to be in my errno when the backend returns -errno. Let's put ERANGE there just for giggles: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx -drive if=none,iops=99999999999999999999 inet_connect: host and/or port not specified chardev: opening backend "socket" failed: Numerical result out of range Worse: when errno happens to be zero, return -errno erroneously signals success, and qemu_chr_new_from_opts() dies dereferencing uninitialized chr. I observe this with "-serial unix:". 2. All qemu_chr_open_opts() knows about the error is an errno error code. That's simply not enough for a decent message. For instance, when inet_dgram() can't resolve the parameter host, which errno code should it use? What if it can't resolve parameter localaddr? Clue: many backends already report errors in their open methods. Let's revert the flawed commit along with its dependencies, and fix up the silent error paths instead. This reverts commit 6e1db57b2ac9025c2443c665a0d9e78748637b26. Conflicts: console.c hw/baum.c qemu-char.c This reverts commit aad04cd024f0c59f0b96f032cde2e24eb3abba6d. The parts of commit db418a0a "Add stdio char device on windows" that depend on the reverted change fixed up. Signed-off-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2012-02-07 18:09:08 +04:00
return NULL;
}
Revert "qemu-char: Print strerror message on failure" and deps The commit's purpose is laudable: The only way for chardev drivers to communicate an error was to return a NULL pointer, which resulted in an error message that said _that_ something went wrong, but not _why_. It attempts to achieve it by changing the interface to return 0/-errno and update qemu_chr_open_opts() to use strerror() to display a more helpful error message. Unfortunately, it has serious flaws: 1. Backends "socket" and "udp" return bogus error codes, because qemu_chr_open_socket() and qemu_chr_open_udp() assume that unix_listen_opts(), unix_connect_opts(), inet_listen_opts(), inet_connect_opts() and inet_dgram_opts() fail with errno set appropriately. That assumption is wrong, and the commit turns unspecific error messages into misleading error messages. For instance: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx inet_connect: host and/or port not specified chardev: opening backend "socket" failed: No such file or directory ENOENT is what happens to be in my errno when the backend returns -errno. Let's put ERANGE there just for giggles: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx -drive if=none,iops=99999999999999999999 inet_connect: host and/or port not specified chardev: opening backend "socket" failed: Numerical result out of range Worse: when errno happens to be zero, return -errno erroneously signals success, and qemu_chr_new_from_opts() dies dereferencing uninitialized chr. I observe this with "-serial unix:". 2. All qemu_chr_open_opts() knows about the error is an errno error code. That's simply not enough for a decent message. For instance, when inet_dgram() can't resolve the parameter host, which errno code should it use? What if it can't resolve parameter localaddr? Clue: many backends already report errors in their open methods. Let's revert the flawed commit along with its dependencies, and fix up the silent error paths instead. This reverts commit 6e1db57b2ac9025c2443c665a0d9e78748637b26. Conflicts: console.c hw/baum.c qemu-char.c This reverts commit aad04cd024f0c59f0b96f032cde2e24eb3abba6d. The parts of commit db418a0a "Add stdio char device on windows" that depend on the reverted change fixed up. Signed-off-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2012-02-07 18:09:08 +04:00
return chr;
}
Revert "qemu-char: Print strerror message on failure" and deps The commit's purpose is laudable: The only way for chardev drivers to communicate an error was to return a NULL pointer, which resulted in an error message that said _that_ something went wrong, but not _why_. It attempts to achieve it by changing the interface to return 0/-errno and update qemu_chr_open_opts() to use strerror() to display a more helpful error message. Unfortunately, it has serious flaws: 1. Backends "socket" and "udp" return bogus error codes, because qemu_chr_open_socket() and qemu_chr_open_udp() assume that unix_listen_opts(), unix_connect_opts(), inet_listen_opts(), inet_connect_opts() and inet_dgram_opts() fail with errno set appropriately. That assumption is wrong, and the commit turns unspecific error messages into misleading error messages. For instance: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx inet_connect: host and/or port not specified chardev: opening backend "socket" failed: No such file or directory ENOENT is what happens to be in my errno when the backend returns -errno. Let's put ERANGE there just for giggles: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx -drive if=none,iops=99999999999999999999 inet_connect: host and/or port not specified chardev: opening backend "socket" failed: Numerical result out of range Worse: when errno happens to be zero, return -errno erroneously signals success, and qemu_chr_new_from_opts() dies dereferencing uninitialized chr. I observe this with "-serial unix:". 2. All qemu_chr_open_opts() knows about the error is an errno error code. That's simply not enough for a decent message. For instance, when inet_dgram() can't resolve the parameter host, which errno code should it use? What if it can't resolve parameter localaddr? Clue: many backends already report errors in their open methods. Let's revert the flawed commit along with its dependencies, and fix up the silent error paths instead. This reverts commit 6e1db57b2ac9025c2443c665a0d9e78748637b26. Conflicts: console.c hw/baum.c qemu-char.c This reverts commit aad04cd024f0c59f0b96f032cde2e24eb3abba6d. The parts of commit db418a0a "Add stdio char device on windows" that depend on the reverted change fixed up. Signed-off-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2012-02-07 18:09:08 +04:00
static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out)
{
CharDriverState *chr;
WinCharState *s;
chr = qemu_chr_alloc();
s = g_malloc0(sizeof(WinCharState));
s->hcom = fd_out;
chr->opaque = s;
chr->chr_write = win_chr_write;
Revert "qemu-char: Print strerror message on failure" and deps The commit's purpose is laudable: The only way for chardev drivers to communicate an error was to return a NULL pointer, which resulted in an error message that said _that_ something went wrong, but not _why_. It attempts to achieve it by changing the interface to return 0/-errno and update qemu_chr_open_opts() to use strerror() to display a more helpful error message. Unfortunately, it has serious flaws: 1. Backends "socket" and "udp" return bogus error codes, because qemu_chr_open_socket() and qemu_chr_open_udp() assume that unix_listen_opts(), unix_connect_opts(), inet_listen_opts(), inet_connect_opts() and inet_dgram_opts() fail with errno set appropriately. That assumption is wrong, and the commit turns unspecific error messages into misleading error messages. For instance: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx inet_connect: host and/or port not specified chardev: opening backend "socket" failed: No such file or directory ENOENT is what happens to be in my errno when the backend returns -errno. Let's put ERANGE there just for giggles: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx -drive if=none,iops=99999999999999999999 inet_connect: host and/or port not specified chardev: opening backend "socket" failed: Numerical result out of range Worse: when errno happens to be zero, return -errno erroneously signals success, and qemu_chr_new_from_opts() dies dereferencing uninitialized chr. I observe this with "-serial unix:". 2. All qemu_chr_open_opts() knows about the error is an errno error code. That's simply not enough for a decent message. For instance, when inet_dgram() can't resolve the parameter host, which errno code should it use? What if it can't resolve parameter localaddr? Clue: many backends already report errors in their open methods. Let's revert the flawed commit along with its dependencies, and fix up the silent error paths instead. This reverts commit 6e1db57b2ac9025c2443c665a0d9e78748637b26. Conflicts: console.c hw/baum.c qemu-char.c This reverts commit aad04cd024f0c59f0b96f032cde2e24eb3abba6d. The parts of commit db418a0a "Add stdio char device on windows" that depend on the reverted change fixed up. Signed-off-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2012-02-07 18:09:08 +04:00
return chr;
}
static CharDriverState *qemu_chr_open_win_con(void)
{
Revert "qemu-char: Print strerror message on failure" and deps The commit's purpose is laudable: The only way for chardev drivers to communicate an error was to return a NULL pointer, which resulted in an error message that said _that_ something went wrong, but not _why_. It attempts to achieve it by changing the interface to return 0/-errno and update qemu_chr_open_opts() to use strerror() to display a more helpful error message. Unfortunately, it has serious flaws: 1. Backends "socket" and "udp" return bogus error codes, because qemu_chr_open_socket() and qemu_chr_open_udp() assume that unix_listen_opts(), unix_connect_opts(), inet_listen_opts(), inet_connect_opts() and inet_dgram_opts() fail with errno set appropriately. That assumption is wrong, and the commit turns unspecific error messages into misleading error messages. For instance: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx inet_connect: host and/or port not specified chardev: opening backend "socket" failed: No such file or directory ENOENT is what happens to be in my errno when the backend returns -errno. Let's put ERANGE there just for giggles: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx -drive if=none,iops=99999999999999999999 inet_connect: host and/or port not specified chardev: opening backend "socket" failed: Numerical result out of range Worse: when errno happens to be zero, return -errno erroneously signals success, and qemu_chr_new_from_opts() dies dereferencing uninitialized chr. I observe this with "-serial unix:". 2. All qemu_chr_open_opts() knows about the error is an errno error code. That's simply not enough for a decent message. For instance, when inet_dgram() can't resolve the parameter host, which errno code should it use? What if it can't resolve parameter localaddr? Clue: many backends already report errors in their open methods. Let's revert the flawed commit along with its dependencies, and fix up the silent error paths instead. This reverts commit 6e1db57b2ac9025c2443c665a0d9e78748637b26. Conflicts: console.c hw/baum.c qemu-char.c This reverts commit aad04cd024f0c59f0b96f032cde2e24eb3abba6d. The parts of commit db418a0a "Add stdio char device on windows" that depend on the reverted change fixed up. Signed-off-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2012-02-07 18:09:08 +04:00
return qemu_chr_open_win_file(GetStdHandle(STD_OUTPUT_HANDLE));
}
static int win_stdio_write(CharDriverState *chr, const uint8_t *buf, int len)
{
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD dwSize;
int len1;
len1 = len;
while (len1 > 0) {
if (!WriteFile(hStdOut, buf, len1, &dwSize, NULL)) {
break;
}
buf += dwSize;
len1 -= dwSize;
}
return len - len1;
}
static void win_stdio_wait_func(void *opaque)
{
CharDriverState *chr = opaque;
WinStdioCharState *stdio = chr->opaque;
INPUT_RECORD buf[4];
int ret;
DWORD dwSize;
int i;
ret = ReadConsoleInput(stdio->hStdIn, buf, ARRAY_SIZE(buf), &dwSize);
if (!ret) {
/* Avoid error storm */
qemu_del_wait_object(stdio->hStdIn, NULL, NULL);
return;
}
for (i = 0; i < dwSize; i++) {
KEY_EVENT_RECORD *kev = &buf[i].Event.KeyEvent;
if (buf[i].EventType == KEY_EVENT && kev->bKeyDown) {
int j;
if (kev->uChar.AsciiChar != 0) {
for (j = 0; j < kev->wRepeatCount; j++) {
if (qemu_chr_be_can_write(chr)) {
uint8_t c = kev->uChar.AsciiChar;
qemu_chr_be_write(chr, &c, 1);
}
}
}
}
}
}
static DWORD WINAPI win_stdio_thread(LPVOID param)
{
CharDriverState *chr = param;
WinStdioCharState *stdio = chr->opaque;
int ret;
DWORD dwSize;
while (1) {
/* Wait for one byte */
ret = ReadFile(stdio->hStdIn, &stdio->win_stdio_buf, 1, &dwSize, NULL);
/* Exit in case of error, continue if nothing read */
if (!ret) {
break;
}
if (!dwSize) {
continue;
}
/* Some terminal emulator returns \r\n for Enter, just pass \n */
if (stdio->win_stdio_buf == '\r') {
continue;
}
/* Signal the main thread and wait until the byte was eaten */
if (!SetEvent(stdio->hInputReadyEvent)) {
break;
}
if (WaitForSingleObject(stdio->hInputDoneEvent, INFINITE)
!= WAIT_OBJECT_0) {
break;
}
}
qemu_del_wait_object(stdio->hInputReadyEvent, NULL, NULL);
return 0;
}
static void win_stdio_thread_wait_func(void *opaque)
{
CharDriverState *chr = opaque;
WinStdioCharState *stdio = chr->opaque;
if (qemu_chr_be_can_write(chr)) {
qemu_chr_be_write(chr, &stdio->win_stdio_buf, 1);
}
SetEvent(stdio->hInputDoneEvent);
}
static void qemu_chr_set_echo_win_stdio(CharDriverState *chr, bool echo)
{
WinStdioCharState *stdio = chr->opaque;
DWORD dwMode = 0;
GetConsoleMode(stdio->hStdIn, &dwMode);
if (echo) {
SetConsoleMode(stdio->hStdIn, dwMode | ENABLE_ECHO_INPUT);
} else {
SetConsoleMode(stdio->hStdIn, dwMode & ~ENABLE_ECHO_INPUT);
}
}
static void win_stdio_close(CharDriverState *chr)
{
WinStdioCharState *stdio = chr->opaque;
if (stdio->hInputReadyEvent != INVALID_HANDLE_VALUE) {
CloseHandle(stdio->hInputReadyEvent);
}
if (stdio->hInputDoneEvent != INVALID_HANDLE_VALUE) {
CloseHandle(stdio->hInputDoneEvent);
}
if (stdio->hInputThread != INVALID_HANDLE_VALUE) {
TerminateThread(stdio->hInputThread, 0);
}
g_free(chr->opaque);
g_free(chr);
}
static CharDriverState *qemu_chr_open_stdio(ChardevStdio *opts)
{
CharDriverState *chr;
WinStdioCharState *stdio;
DWORD dwMode;
int is_console = 0;
chr = qemu_chr_alloc();
stdio = g_malloc0(sizeof(WinStdioCharState));
stdio->hStdIn = GetStdHandle(STD_INPUT_HANDLE);
if (stdio->hStdIn == INVALID_HANDLE_VALUE) {
fprintf(stderr, "cannot open stdio: invalid handle\n");
exit(1);
}
is_console = GetConsoleMode(stdio->hStdIn, &dwMode) != 0;
chr->opaque = stdio;
chr->chr_write = win_stdio_write;
chr->chr_close = win_stdio_close;
if (is_console) {
if (qemu_add_wait_object(stdio->hStdIn,
win_stdio_wait_func, chr)) {
fprintf(stderr, "qemu_add_wait_object: failed\n");
}
} else {
DWORD dwId;
stdio->hInputReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
stdio->hInputDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
stdio->hInputThread = CreateThread(NULL, 0, win_stdio_thread,
chr, 0, &dwId);
if (stdio->hInputThread == INVALID_HANDLE_VALUE
|| stdio->hInputReadyEvent == INVALID_HANDLE_VALUE
|| stdio->hInputDoneEvent == INVALID_HANDLE_VALUE) {
fprintf(stderr, "cannot create stdio thread or event\n");
exit(1);
}
if (qemu_add_wait_object(stdio->hInputReadyEvent,
win_stdio_thread_wait_func, chr)) {
fprintf(stderr, "qemu_add_wait_object: failed\n");
}
}
dwMode |= ENABLE_LINE_INPUT;
if (is_console) {
/* set the terminal in raw mode */
/* ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS */
dwMode |= ENABLE_PROCESSED_INPUT;
}
SetConsoleMode(stdio->hStdIn, dwMode);
chr->chr_set_echo = qemu_chr_set_echo_win_stdio;
qemu_chr_fe_set_echo(chr, false);
Revert "qemu-char: Print strerror message on failure" and deps The commit's purpose is laudable: The only way for chardev drivers to communicate an error was to return a NULL pointer, which resulted in an error message that said _that_ something went wrong, but not _why_. It attempts to achieve it by changing the interface to return 0/-errno and update qemu_chr_open_opts() to use strerror() to display a more helpful error message. Unfortunately, it has serious flaws: 1. Backends "socket" and "udp" return bogus error codes, because qemu_chr_open_socket() and qemu_chr_open_udp() assume that unix_listen_opts(), unix_connect_opts(), inet_listen_opts(), inet_connect_opts() and inet_dgram_opts() fail with errno set appropriately. That assumption is wrong, and the commit turns unspecific error messages into misleading error messages. For instance: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx inet_connect: host and/or port not specified chardev: opening backend "socket" failed: No such file or directory ENOENT is what happens to be in my errno when the backend returns -errno. Let's put ERANGE there just for giggles: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx -drive if=none,iops=99999999999999999999 inet_connect: host and/or port not specified chardev: opening backend "socket" failed: Numerical result out of range Worse: when errno happens to be zero, return -errno erroneously signals success, and qemu_chr_new_from_opts() dies dereferencing uninitialized chr. I observe this with "-serial unix:". 2. All qemu_chr_open_opts() knows about the error is an errno error code. That's simply not enough for a decent message. For instance, when inet_dgram() can't resolve the parameter host, which errno code should it use? What if it can't resolve parameter localaddr? Clue: many backends already report errors in their open methods. Let's revert the flawed commit along with its dependencies, and fix up the silent error paths instead. This reverts commit 6e1db57b2ac9025c2443c665a0d9e78748637b26. Conflicts: console.c hw/baum.c qemu-char.c This reverts commit aad04cd024f0c59f0b96f032cde2e24eb3abba6d. The parts of commit db418a0a "Add stdio char device on windows" that depend on the reverted change fixed up. Signed-off-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2012-02-07 18:09:08 +04:00
return chr;
}
#endif /* !_WIN32 */
/***********************************************************/
/* UDP Net console */
typedef struct {
int fd;
GIOChannel *chan;
uint8_t buf[READ_BUF_LEN];
int bufcnt;
int bufptr;
int max_size;
} NetCharDriver;
/* Called with chr_write_lock held. */
static int udp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
NetCharDriver *s = chr->opaque;
gsize bytes_written;
GIOStatus status;
status = g_io_channel_write_chars(s->chan, (const gchar *)buf, len, &bytes_written, NULL);
if (status == G_IO_STATUS_EOF) {
return 0;
} else if (status != G_IO_STATUS_NORMAL) {
return -1;
}
return bytes_written;
}
static int udp_chr_read_poll(void *opaque)
{
CharDriverState *chr = opaque;
NetCharDriver *s = chr->opaque;
s->max_size = qemu_chr_be_can_write(chr);
/* If there were any stray characters in the queue process them
* first
*/
while (s->max_size > 0 && s->bufptr < s->bufcnt) {
qemu_chr_be_write(chr, &s->buf[s->bufptr], 1);
s->bufptr++;
s->max_size = qemu_chr_be_can_write(chr);
}
return s->max_size;
}
static gboolean udp_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
{
CharDriverState *chr = opaque;
NetCharDriver *s = chr->opaque;
gsize bytes_read = 0;
GIOStatus status;
if (s->max_size == 0) {
return TRUE;
}
status = g_io_channel_read_chars(s->chan, (gchar *)s->buf, sizeof(s->buf),
&bytes_read, NULL);
s->bufcnt = bytes_read;
s->bufptr = s->bufcnt;
if (status != G_IO_STATUS_NORMAL) {
remove_fd_in_watch(chr);
return FALSE;
}
s->bufptr = 0;
while (s->max_size > 0 && s->bufptr < s->bufcnt) {
qemu_chr_be_write(chr, &s->buf[s->bufptr], 1);
s->bufptr++;
s->max_size = qemu_chr_be_can_write(chr);
}
return TRUE;
}
static void udp_chr_update_read_handler(CharDriverState *chr)
{
NetCharDriver *s = chr->opaque;
remove_fd_in_watch(chr);
if (s->chan) {
chr->fd_in_tag = io_add_watch_poll(s->chan, udp_chr_read_poll,
udp_chr_read, chr);
}
}
static void udp_chr_close(CharDriverState *chr)
{
NetCharDriver *s = chr->opaque;
remove_fd_in_watch(chr);
if (s->chan) {
g_io_channel_unref(s->chan);
closesocket(s->fd);
}
g_free(s);
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
}
static CharDriverState *qemu_chr_open_udp_fd(int fd)
{
CharDriverState *chr = NULL;
NetCharDriver *s = NULL;
chr = qemu_chr_alloc();
s = g_malloc0(sizeof(NetCharDriver));
s->fd = fd;
s->chan = io_channel_from_socket(s->fd);
s->bufcnt = 0;
s->bufptr = 0;
chr->opaque = s;
chr->chr_write = udp_chr_write;
chr->chr_update_read_handler = udp_chr_update_read_handler;
chr->chr_close = udp_chr_close;
qemu-char: don't issue CHR_EVENT_OPEN in a BH When CHR_EVENT_OPENED was initially added, it was CHR_EVENT_RESET, and it was issued as a bottom-half: 86e94dea5b740dad65446c857f6959eae43e0ba6 Which we basically used to print out a greeting/prompt for the monitor. AFAICT the only reason this was ever done in a BH was because in some cases we'd modify the chr_write handler for a new chardev backend *after* the site where we issued the reset (see: 86e94d:qemu_chr_open_stdio()) At some point this event was renamed to CHR_EVENT_OPENED, and we've maintained the use of this BH ever since. However, due to 9f939df955a4152aad69a19a77e0898631bb2c18, we schedule the BH via g_idle_add(), which is causing events to sometimes be delivered after we've already begun processing data from backends, leading to: known bugs: QMP: session negotation resets with OPENED event, in some cases this is causing new sessions to get sporadically reset potential bugs: hw/usb/redirect.c: can_read handler checks for dev->parser != NULL, which may be true if CLOSED BH has not been executed yet. In the past, OPENED quiesced outstanding CLOSED events prior to us reading client data. If it's delayed, our check may allow reads to occur even though we haven't processed the OPENED event yet, and when we do finally get the OPENED event, our state may get reset. qtest.c: can begin session before OPENED event is processed, leading to a spurious reset of the system and irq_levels gdbstub.c: may start a gdb session prior to the machine being paused To fix these, let's just drop the BH. Since the initial reasoning for using it still applies to an extent, work around that by deferring the delivery of CHR_EVENT_OPENED until after the chardevs have been fully initialized, toward the end of qmp_chardev_add() (or some cases, qemu_chr_new_from_opts()). This defers delivery long enough that we can be assured a CharDriverState is fully initialized before CHR_EVENT_OPENED is sent. Also, rather than requiring each chardev to do an explicit open, do it automatically, and allow the small few who don't desire such behavior to suppress the OPENED-on-init behavior by setting a 'explicit_be_open' flag. We additionally add missing OPENED events for stdio backends on w32, which were previously not being issued, causing us to not recieve the banner and initial prompts for qmp/hmp. Reported-by: Stefan Priebe <s.priebe@profihost.ag> Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com> Message-id: 1370636393-21044-1-git-send-email-mdroth@linux.vnet.ibm.com Cc: qemu-stable@nongnu.org Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-06-08 00:19:53 +04:00
/* be isn't opened until we get a connection */
chr->explicit_be_open = true;
Revert "qemu-char: Print strerror message on failure" and deps The commit's purpose is laudable: The only way for chardev drivers to communicate an error was to return a NULL pointer, which resulted in an error message that said _that_ something went wrong, but not _why_. It attempts to achieve it by changing the interface to return 0/-errno and update qemu_chr_open_opts() to use strerror() to display a more helpful error message. Unfortunately, it has serious flaws: 1. Backends "socket" and "udp" return bogus error codes, because qemu_chr_open_socket() and qemu_chr_open_udp() assume that unix_listen_opts(), unix_connect_opts(), inet_listen_opts(), inet_connect_opts() and inet_dgram_opts() fail with errno set appropriately. That assumption is wrong, and the commit turns unspecific error messages into misleading error messages. For instance: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx inet_connect: host and/or port not specified chardev: opening backend "socket" failed: No such file or directory ENOENT is what happens to be in my errno when the backend returns -errno. Let's put ERANGE there just for giggles: $ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx -drive if=none,iops=99999999999999999999 inet_connect: host and/or port not specified chardev: opening backend "socket" failed: Numerical result out of range Worse: when errno happens to be zero, return -errno erroneously signals success, and qemu_chr_new_from_opts() dies dereferencing uninitialized chr. I observe this with "-serial unix:". 2. All qemu_chr_open_opts() knows about the error is an errno error code. That's simply not enough for a decent message. For instance, when inet_dgram() can't resolve the parameter host, which errno code should it use? What if it can't resolve parameter localaddr? Clue: many backends already report errors in their open methods. Let's revert the flawed commit along with its dependencies, and fix up the silent error paths instead. This reverts commit 6e1db57b2ac9025c2443c665a0d9e78748637b26. Conflicts: console.c hw/baum.c qemu-char.c This reverts commit aad04cd024f0c59f0b96f032cde2e24eb3abba6d. The parts of commit db418a0a "Add stdio char device on windows" that depend on the reverted change fixed up. Signed-off-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2012-02-07 18:09:08 +04:00
return chr;
}
/***********************************************************/
/* TCP Net console */
typedef struct {
GIOChannel *chan, *listen_chan;
guint listen_tag;
int fd, listen_fd;
int connected;
int max_size;
int do_telnetopt;
int do_nodelay;
int is_unix;
int *read_msgfds;
int read_msgfds_num;
int *write_msgfds;
int write_msgfds_num;
SocketAddress *addr;
bool is_listen;
bool is_telnet;
guint reconnect_timer;
int64_t reconnect_time;
bool connect_err_reported;
} TCPCharDriver;
static gboolean socket_reconnect_timeout(gpointer opaque);
static void qemu_chr_socket_restart_timer(CharDriverState *chr)
{
TCPCharDriver *s = chr->opaque;
assert(s->connected == 0);
s->reconnect_timer = g_timeout_add_seconds(s->reconnect_time,
socket_reconnect_timeout, chr);
}
static void check_report_connect_error(CharDriverState *chr,
Error *err)
{
TCPCharDriver *s = chr->opaque;
if (!s->connect_err_reported) {
error_report("Unable to connect character device %s: %s",
chr->label, error_get_pretty(err));
s->connect_err_reported = true;
}
qemu_chr_socket_restart_timer(chr);
}
static gboolean tcp_chr_accept(GIOChannel *chan, GIOCondition cond, void *opaque);
#ifndef _WIN32
static int unix_send_msgfds(CharDriverState *chr, const uint8_t *buf, int len)
{
TCPCharDriver *s = chr->opaque;
struct msghdr msgh;
struct iovec iov;
int r;
size_t fd_size = s->write_msgfds_num * sizeof(int);
char control[CMSG_SPACE(fd_size)];
struct cmsghdr *cmsg;
memset(&msgh, 0, sizeof(msgh));
memset(control, 0, sizeof(control));
/* set the payload */
iov.iov_base = (uint8_t *) buf;
iov.iov_len = len;
msgh.msg_iov = &iov;
msgh.msg_iovlen = 1;
msgh.msg_control = control;
msgh.msg_controllen = sizeof(control);
cmsg = CMSG_FIRSTHDR(&msgh);
cmsg->cmsg_len = CMSG_LEN(fd_size);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
memcpy(CMSG_DATA(cmsg), s->write_msgfds, fd_size);
do {
r = sendmsg(s->fd, &msgh, 0);
} while (r < 0 && errno == EINTR);
/* free the written msgfds, no matter what */
if (s->write_msgfds_num) {
g_free(s->write_msgfds);
s->write_msgfds = 0;
s->write_msgfds_num = 0;
}
return r;
}
#endif
/* Called with chr_write_lock held. */
static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
TCPCharDriver *s = chr->opaque;
if (s->connected) {
#ifndef _WIN32
if (s->is_unix && s->write_msgfds_num) {
return unix_send_msgfds(chr, buf, len);
} else
#endif
{
return io_channel_send(s->chan, buf, len);
}
} else {
/* XXX: indicate an error ? */
return len;
}
}
static int tcp_chr_read_poll(void *opaque)
{
CharDriverState *chr = opaque;
TCPCharDriver *s = chr->opaque;
if (!s->connected)
return 0;
s->max_size = qemu_chr_be_can_write(chr);
return s->max_size;
}
#define IAC 255
#define IAC_BREAK 243
static void tcp_chr_process_IAC_bytes(CharDriverState *chr,
TCPCharDriver *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 int tcp_get_msgfds(CharDriverState *chr, int *fds, int num)
{
TCPCharDriver *s = chr->opaque;
int to_copy = (s->read_msgfds_num < num) ? s->read_msgfds_num : num;
assert(num <= TCP_MAX_FDS);
if (to_copy) {
int i;
memcpy(fds, s->read_msgfds, to_copy * sizeof(int));
/* Close unused fds */
for (i = to_copy; i < s->read_msgfds_num; i++) {
close(s->read_msgfds[i]);
}
g_free(s->read_msgfds);
s->read_msgfds = 0;
s->read_msgfds_num = 0;
}
return to_copy;
}
static int tcp_set_msgfds(CharDriverState *chr, int *fds, int num)
{
TCPCharDriver *s = chr->opaque;
/* clear old pending fd array */
if (s->write_msgfds) {
g_free(s->write_msgfds);
}
if (num) {
s->write_msgfds = g_malloc(num * sizeof(int));
memcpy(s->write_msgfds, fds, num * sizeof(int));
}
s->write_msgfds_num = num;
return 0;
}
#ifndef _WIN32
static void unix_process_msgfd(CharDriverState *chr, struct msghdr *msg)
{
TCPCharDriver *s = chr->opaque;
struct cmsghdr *cmsg;
for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
int fd_size, i;
if (cmsg->cmsg_len < CMSG_LEN(sizeof(int)) ||
cmsg->cmsg_level != SOL_SOCKET ||
cmsg->cmsg_type != SCM_RIGHTS) {
continue;
}
fd_size = cmsg->cmsg_len - CMSG_LEN(0);
if (!fd_size) {
continue;
}
/* close and clean read_msgfds */
for (i = 0; i < s->read_msgfds_num; i++) {
close(s->read_msgfds[i]);
}
if (s->read_msgfds_num) {
g_free(s->read_msgfds);
}
s->read_msgfds_num = fd_size / sizeof(int);
s->read_msgfds = g_malloc(fd_size);
memcpy(s->read_msgfds, CMSG_DATA(cmsg), fd_size);
for (i = 0; i < s->read_msgfds_num; i++) {
int fd = s->read_msgfds[i];
if (fd < 0) {
continue;
}
/* O_NONBLOCK is preserved across SCM_RIGHTS so reset it */
qemu_set_block(fd);
#ifndef MSG_CMSG_CLOEXEC
qemu_set_cloexec(fd);
#endif
}
}
}
static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len)
{
TCPCharDriver *s = chr->opaque;
struct msghdr msg = { NULL, };
struct iovec iov[1];
union {
struct cmsghdr cmsg;
char control[CMSG_SPACE(sizeof(int) * TCP_MAX_FDS)];
} msg_control;
int flags = 0;
ssize_t ret;
iov[0].iov_base = buf;
iov[0].iov_len = len;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = &msg_control;
msg.msg_controllen = sizeof(msg_control);
#ifdef MSG_CMSG_CLOEXEC
flags |= MSG_CMSG_CLOEXEC;
#endif
ret = recvmsg(s->fd, &msg, flags);
if (ret > 0 && s->is_unix) {
unix_process_msgfd(chr, &msg);
}
return ret;
}
#else
static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len)
{
TCPCharDriver *s = chr->opaque;
return qemu_recv(s->fd, buf, len, 0);
}
#endif
static GSource *tcp_chr_add_watch(CharDriverState *chr, GIOCondition cond)
{
TCPCharDriver *s = chr->opaque;
return g_io_create_watch(s->chan, cond);
}
static void tcp_chr_disconnect(CharDriverState *chr)
{
TCPCharDriver *s = chr->opaque;
s->connected = 0;
if (s->listen_chan) {
s->listen_tag = g_io_add_watch(s->listen_chan, G_IO_IN,
tcp_chr_accept, chr);
}
remove_fd_in_watch(chr);
g_io_channel_unref(s->chan);
s->chan = NULL;
closesocket(s->fd);
s->fd = -1;
SocketAddress_to_str(chr->filename, CHR_MAX_FILENAME_SIZE,
"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 gboolean tcp_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
{
CharDriverState *chr = opaque;
TCPCharDriver *s = chr->opaque;
uint8_t buf[READ_BUF_LEN];
int len, size;
if (cond & G_IO_HUP) {
/* connection closed */
tcp_chr_disconnect(chr);
return TRUE;
}
if (!s->connected || s->max_size <= 0) {
return TRUE;
}
len = sizeof(buf);
if (len > s->max_size)
len = s->max_size;
size = tcp_chr_recv(chr, (void *)buf, len);
if (size == 0) {
/* connection closed */
tcp_chr_disconnect(chr);
} else if (size > 0) {
if (s->do_telnetopt)
tcp_chr_process_IAC_bytes(chr, s, buf, &size);
if (size > 0)
qemu_chr_be_write(chr, buf, size);
}
return TRUE;
}
static int tcp_chr_sync_read(CharDriverState *chr, const uint8_t *buf, int len)
{
TCPCharDriver *s = chr->opaque;
int size;
if (!s->connected) {
return 0;
}
size = tcp_chr_recv(chr, (void *) buf, len);
if (size == 0) {
/* connection closed */
tcp_chr_disconnect(chr);
}
return size;
}
#ifndef _WIN32
CharDriverState *qemu_chr_open_eventfd(int eventfd)
{
CharDriverState *chr = qemu_chr_open_fd(eventfd, eventfd);
if (chr) {
chr->avail_connections = 1;
}
return chr;
}
#endif
static void tcp_chr_connect(void *opaque)
{
CharDriverState *chr = opaque;
TCPCharDriver *s = chr->opaque;
struct sockaddr_storage ss, ps;
socklen_t ss_len = sizeof(ss), ps_len = sizeof(ps);
memset(&ss, 0, ss_len);
if (getsockname(s->fd, (struct sockaddr *) &ss, &ss_len) != 0) {
snprintf(chr->filename, CHR_MAX_FILENAME_SIZE,
"Error in getsockname: %s\n", strerror(errno));
} else if (getpeername(s->fd, (struct sockaddr *) &ps, &ps_len) != 0) {
snprintf(chr->filename, CHR_MAX_FILENAME_SIZE,
"Error in getpeername: %s\n", strerror(errno));
} else {
sockaddr_to_str(chr->filename, CHR_MAX_FILENAME_SIZE,
&ss, ss_len, &ps, ps_len,
s->is_listen, s->is_telnet);
}
s->connected = 1;
if (s->chan) {
chr->fd_in_tag = io_add_watch_poll(s->chan, tcp_chr_read_poll,
tcp_chr_read, chr);
}
qemu_chr_be_generic_open(chr);
}
static void tcp_chr_update_read_handler(CharDriverState *chr)
{
TCPCharDriver *s = chr->opaque;
remove_fd_in_watch(chr);
if (s->chan) {
chr->fd_in_tag = io_add_watch_poll(s->chan, tcp_chr_read_poll,
tcp_chr_read, chr);
}
}
#define IACSET(x,a,b,c) x[0] = a; x[1] = b; x[2] = c;
static void tcp_chr_telnet_init(int fd)
{
char buf[3];
/* Send the telnet negotion to put telnet in binary, no echo, single char mode */
IACSET(buf, 0xff, 0xfb, 0x01); /* IAC WILL ECHO */
send(fd, (char *)buf, 3, 0);
IACSET(buf, 0xff, 0xfb, 0x03); /* IAC WILL Suppress go ahead */
send(fd, (char *)buf, 3, 0);
IACSET(buf, 0xff, 0xfb, 0x00); /* IAC WILL Binary */
send(fd, (char *)buf, 3, 0);
IACSET(buf, 0xff, 0xfd, 0x00); /* IAC DO Binary */
send(fd, (char *)buf, 3, 0);
}
static int tcp_chr_add_client(CharDriverState *chr, int fd)
{
TCPCharDriver *s = chr->opaque;
if (s->fd != -1)
return -1;
qemu_set_nonblock(fd);
if (s->do_nodelay)
socket_set_nodelay(fd);
s->fd = fd;
s->chan = io_channel_from_socket(fd);
if (s->listen_tag) {
g_source_remove(s->listen_tag);
s->listen_tag = 0;
}
tcp_chr_connect(chr);
return 0;
}
static gboolean tcp_chr_accept(GIOChannel *channel, GIOCondition cond, void *opaque)
{
CharDriverState *chr = opaque;
TCPCharDriver *s = chr->opaque;
struct sockaddr_in saddr;
#ifndef _WIN32
struct sockaddr_un uaddr;
#endif
struct sockaddr *addr;
socklen_t len;
int fd;
for(;;) {
#ifndef _WIN32
if (s->is_unix) {
len = sizeof(uaddr);
addr = (struct sockaddr *)&uaddr;
} else
#endif
{
len = sizeof(saddr);
addr = (struct sockaddr *)&saddr;
}
fd = qemu_accept(s->listen_fd, addr, &len);
if (fd < 0 && errno != EINTR) {
s->listen_tag = 0;
return FALSE;
} else if (fd >= 0) {
if (s->do_telnetopt)
tcp_chr_telnet_init(fd);
break;
}
}
if (tcp_chr_add_client(chr, fd) < 0)
close(fd);
return TRUE;
}
static void tcp_chr_close(CharDriverState *chr)
{
TCPCharDriver *s = chr->opaque;
int i;
if (s->reconnect_timer) {
g_source_remove(s->reconnect_timer);
s->reconnect_timer = 0;
}
qapi_free_SocketAddress(s->addr);
if (s->fd >= 0) {
remove_fd_in_watch(chr);
if (s->chan) {
g_io_channel_unref(s->chan);
}
closesocket(s->fd);
}
if (s->listen_fd >= 0) {
if (s->listen_tag) {
g_source_remove(s->listen_tag);
s->listen_tag = 0;
}
if (s->listen_chan) {
g_io_channel_unref(s->listen_chan);
}
closesocket(s->listen_fd);
}
if (s->read_msgfds_num) {
for (i = 0; i < s->read_msgfds_num; i++) {
close(s->read_msgfds[i]);
}
g_free(s->read_msgfds);
}
if (s->write_msgfds_num) {
g_free(s->write_msgfds);
}
g_free(s);
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
}
static void qemu_chr_finish_socket_connection(CharDriverState *chr, int fd)
{
TCPCharDriver *s = chr->opaque;
if (s->is_listen) {
s->listen_fd = fd;
s->listen_chan = io_channel_from_socket(s->listen_fd);
s->listen_tag = g_io_add_watch(s->listen_chan, G_IO_IN,
tcp_chr_accept, chr);
} else {
s->connected = 1;
s->fd = fd;
socket_set_nodelay(fd);
s->chan = io_channel_from_socket(s->fd);
tcp_chr_connect(chr);
}
}
static void qemu_chr_socket_connected(int fd, Error *err, void *opaque)
{
CharDriverState *chr = opaque;
TCPCharDriver *s = chr->opaque;
if (fd < 0) {
check_report_connect_error(chr, err);
return;
}
s->connect_err_reported = false;
qemu_chr_finish_socket_connection(chr, fd);
}
static bool qemu_chr_open_socket_fd(CharDriverState *chr, Error **errp)
{
TCPCharDriver *s = chr->opaque;
int fd;
if (s->is_listen) {
fd = socket_listen(s->addr, errp);
} else if (s->reconnect_time) {
fd = socket_connect(s->addr, errp, qemu_chr_socket_connected, chr);
return fd >= 0;
} else {
fd = socket_connect(s->addr, errp, NULL, NULL);
}
if (fd < 0) {
return false;
}
qemu_chr_finish_socket_connection(chr, fd);
return true;
}
/*********************************************************/
/* Ring buffer chardev */
typedef struct {
size_t size;
size_t prod;
size_t cons;
uint8_t *cbuf;
} RingBufCharDriver;
static size_t ringbuf_count(const CharDriverState *chr)
{
const RingBufCharDriver *d = chr->opaque;
return d->prod - d->cons;
}
/* Called with chr_write_lock held. */
static int ringbuf_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
RingBufCharDriver *d = chr->opaque;
int i;
if (!buf || (len < 0)) {
return -1;
}
for (i = 0; i < len; i++ ) {
d->cbuf[d->prod++ & (d->size - 1)] = buf[i];
if (d->prod - d->cons > d->size) {
d->cons = d->prod - d->size;
}
}
return 0;
}
static int ringbuf_chr_read(CharDriverState *chr, uint8_t *buf, int len)
{
RingBufCharDriver *d = chr->opaque;
int i;
qemu_mutex_lock(&chr->chr_write_lock);
for (i = 0; i < len && d->cons != d->prod; i++) {
buf[i] = d->cbuf[d->cons++ & (d->size - 1)];
}
qemu_mutex_unlock(&chr->chr_write_lock);
return i;
}
static void ringbuf_chr_close(struct CharDriverState *chr)
{
RingBufCharDriver *d = chr->opaque;
g_free(d->cbuf);
g_free(d);
chr->opaque = NULL;
}
Revert "chardev: Make the name of memory device consistent" This reverts commit 6a85e60cb994bd95d1537aafbff65816f3de4637. Commit 51767e7 "qemu-char: Add new char backend CirMemCharDriver" introduced a memory ring buffer character device driver named "memory". Commit 3949e59 "qemu-char: Saner naming of memchar stuff & doc fixes" changed the driver name to "ringbuf", along with a whole bunch of other names, with the following rationale: Naming is a mess. The code calls the device driver CirMemCharDriver, the public API calls it "memory", "memchardev", or "memchar", and the special commands are named like "memchar-FOO". "memory" is a particularly unfortunate choice, because there's another character device driver called MemoryDriver. Moreover, the device's distinctive property is that it's a ring buffer, not that's in memory. This is what we released in 1.4.0. Unfortunately, the rename missed a critical instance of "memory": the actual driver name. Thus, the new device could be used only by an entirely undocumented name. The documented name did not work. Bummer. Commit 6a85e60 fixes this by changing the documentation to match the code. It also changes some, but not all related occurences of "ringbuf" to "memory". Left alone are identifiers in C code, HMP and QMP commands. The latter are external interface, so they can't be changed. The result is an inconsistent mess. Moreover, "memory" is a rotten name. The device's distinctive property is that it's a ring buffer, not that's in memory. User's don't care whether it's in RAM, flash, or carved into chocolate tablets by Oompa Loompas. Revert the commit. Next commit will fix just the bug. Cc: qemu-stable@nongnu.org Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Message-id: 1374849874-25531-2-git-send-email-armbru@redhat.com Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-07-26 18:44:32 +04:00
static CharDriverState *qemu_chr_open_ringbuf(ChardevRingbuf *opts,
Error **errp)
{
CharDriverState *chr;
RingBufCharDriver *d;
chr = qemu_chr_alloc();
d = g_malloc(sizeof(*d));
d->size = opts->has_size ? opts->size : 65536;
/* The size must be power of 2 */
if (d->size & (d->size - 1)) {
Revert "chardev: Make the name of memory device consistent" This reverts commit 6a85e60cb994bd95d1537aafbff65816f3de4637. Commit 51767e7 "qemu-char: Add new char backend CirMemCharDriver" introduced a memory ring buffer character device driver named "memory". Commit 3949e59 "qemu-char: Saner naming of memchar stuff & doc fixes" changed the driver name to "ringbuf", along with a whole bunch of other names, with the following rationale: Naming is a mess. The code calls the device driver CirMemCharDriver, the public API calls it "memory", "memchardev", or "memchar", and the special commands are named like "memchar-FOO". "memory" is a particularly unfortunate choice, because there's another character device driver called MemoryDriver. Moreover, the device's distinctive property is that it's a ring buffer, not that's in memory. This is what we released in 1.4.0. Unfortunately, the rename missed a critical instance of "memory": the actual driver name. Thus, the new device could be used only by an entirely undocumented name. The documented name did not work. Bummer. Commit 6a85e60 fixes this by changing the documentation to match the code. It also changes some, but not all related occurences of "ringbuf" to "memory". Left alone are identifiers in C code, HMP and QMP commands. The latter are external interface, so they can't be changed. The result is an inconsistent mess. Moreover, "memory" is a rotten name. The device's distinctive property is that it's a ring buffer, not that's in memory. User's don't care whether it's in RAM, flash, or carved into chocolate tablets by Oompa Loompas. Revert the commit. Next commit will fix just the bug. Cc: qemu-stable@nongnu.org Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Message-id: 1374849874-25531-2-git-send-email-armbru@redhat.com Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-07-26 18:44:32 +04:00
error_setg(errp, "size of ringbuf chardev must be power of two");
goto fail;
}
d->prod = 0;
d->cons = 0;
d->cbuf = g_malloc0(d->size);
chr->opaque = d;
chr->chr_write = ringbuf_chr_write;
chr->chr_close = ringbuf_chr_close;
return chr;
fail:
g_free(d);
g_free(chr);
return NULL;
}
bool chr_is_ringbuf(const CharDriverState *chr)
{
return chr->chr_write == ringbuf_chr_write;
}
void qmp_ringbuf_write(const char *device, const char *data,
bool has_format, enum DataFormat format,
Error **errp)
{
CharDriverState *chr;
const uint8_t *write_data;
int ret;
gsize write_count;
chr = qemu_chr_find(device);
if (!chr) {
error_setg(errp, "Device '%s' not found", device);
return;
}
if (!chr_is_ringbuf(chr)) {
error_setg(errp,"%s is not a ringbuf device", device);
return;
}
if (has_format && (format == DATA_FORMAT_BASE64)) {
write_data = g_base64_decode(data, &write_count);
} else {
write_data = (uint8_t *)data;
write_count = strlen(data);
}
ret = ringbuf_chr_write(chr, write_data, write_count);
if (write_data != (uint8_t *)data) {
g_free((void *)write_data);
}
if (ret < 0) {
error_setg(errp, "Failed to write to device %s", device);
return;
}
}
char *qmp_ringbuf_read(const char *device, int64_t size,
bool has_format, enum DataFormat format,
Error **errp)
{
CharDriverState *chr;
uint8_t *read_data;
size_t count;
char *data;
chr = qemu_chr_find(device);
if (!chr) {
error_setg(errp, "Device '%s' not found", device);
return NULL;
}
if (!chr_is_ringbuf(chr)) {
error_setg(errp,"%s is not a ringbuf device", device);
return NULL;
}
if (size <= 0) {
error_setg(errp, "size must be greater than zero");
return NULL;
}
count = ringbuf_count(chr);
size = size > count ? count : size;
read_data = g_malloc(size + 1);
ringbuf_chr_read(chr, read_data, size);
if (has_format && (format == DATA_FORMAT_BASE64)) {
data = g_base64_encode(read_data, size);
g_free(read_data);
} else {
/*
* FIXME should read only complete, valid UTF-8 characters up
* to @size bytes. Invalid sequences should be replaced by a
* suitable replacement character. Except when (and only
* when) ring buffer lost characters since last read, initial
* continuation characters should be dropped.
*/
read_data[size] = 0;
data = (char *)read_data;
}
return data;
}
QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
{
char host[65], port[33], width[8], height[8];
int pos;
const char *p;
QemuOpts *opts;
Error *local_err = NULL;
opts = qemu_opts_create(qemu_find_opts("chardev"), label, 1, &local_err);
if (local_err) {
error_report_err(local_err);
return NULL;
}
if (strstart(filename, "mon:", &p)) {
filename = p;
qemu_opt_set(opts, "mux", "on", &error_abort);
if (strcmp(filename, "stdio") == 0) {
/* Monitor is muxed to stdio: do not exit on Ctrl+C by default
* but pass it to the guest. Handle this only for compat syntax,
* for -chardev syntax we have special option for this.
* This is what -nographic did, redirecting+muxing serial+monitor
* to stdio causing Ctrl+C to be passed to guest. */
qemu_opt_set(opts, "signal", "off", &error_abort);
}
}
if (strcmp(filename, "null") == 0 ||
strcmp(filename, "pty") == 0 ||
strcmp(filename, "msmouse") == 0 ||
strcmp(filename, "braille") == 0 ||
strcmp(filename, "testdev") == 0 ||
strcmp(filename, "stdio") == 0) {
qemu_opt_set(opts, "backend", filename, &error_abort);
return opts;
}
if (strstart(filename, "vc", &p)) {
qemu_opt_set(opts, "backend", "vc", &error_abort);
if (*p == ':') {
if (sscanf(p+1, "%7[0-9]x%7[0-9]", width, height) == 2) {
/* pixels */
qemu_opt_set(opts, "width", width, &error_abort);
qemu_opt_set(opts, "height", height, &error_abort);
} else if (sscanf(p+1, "%7[0-9]Cx%7[0-9]C", width, height) == 2) {
/* chars */
qemu_opt_set(opts, "cols", width, &error_abort);
qemu_opt_set(opts, "rows", height, &error_abort);
} else {
goto fail;
}
}
return opts;
}
if (strcmp(filename, "con:") == 0) {
qemu_opt_set(opts, "backend", "console", &error_abort);
return opts;
}
if (strstart(filename, "COM", NULL)) {
qemu_opt_set(opts, "backend", "serial", &error_abort);
qemu_opt_set(opts, "path", filename, &error_abort);
return opts;
}
if (strstart(filename, "file:", &p)) {
qemu_opt_set(opts, "backend", "file", &error_abort);
qemu_opt_set(opts, "path", p, &error_abort);
return opts;
}
if (strstart(filename, "pipe:", &p)) {
qemu_opt_set(opts, "backend", "pipe", &error_abort);
qemu_opt_set(opts, "path", p, &error_abort);
return opts;
}
if (strstart(filename, "tcp:", &p) ||
strstart(filename, "telnet:", &p)) {
if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) {
host[0] = 0;
if (sscanf(p, ":%32[^,]%n", port, &pos) < 1)
goto fail;
}
qemu_opt_set(opts, "backend", "socket", &error_abort);
qemu_opt_set(opts, "host", host, &error_abort);
qemu_opt_set(opts, "port", port, &error_abort);
if (p[pos] == ',') {
qemu_opts_do_parse(opts, p+pos+1, NULL, &local_err);
if (local_err) {
error_report_err(local_err);
goto fail;
}
}
if (strstart(filename, "telnet:", &p))
qemu_opt_set(opts, "telnet", "on", &error_abort);
return opts;
}
if (strstart(filename, "udp:", &p)) {
qemu_opt_set(opts, "backend", "udp", &error_abort);
if (sscanf(p, "%64[^:]:%32[^@,]%n", host, port, &pos) < 2) {
host[0] = 0;
if (sscanf(p, ":%32[^@,]%n", port, &pos) < 1) {
goto fail;
}
}
qemu_opt_set(opts, "host", host, &error_abort);
qemu_opt_set(opts, "port", port, &error_abort);
if (p[pos] == '@') {
p += pos + 1;
if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) {
host[0] = 0;
if (sscanf(p, ":%32[^,]%n", port, &pos) < 1) {
goto fail;
}
}
qemu_opt_set(opts, "localaddr", host, &error_abort);
qemu_opt_set(opts, "localport", port, &error_abort);
}
return opts;
}
if (strstart(filename, "unix:", &p)) {
qemu_opt_set(opts, "backend", "socket", &error_abort);
qemu_opts_do_parse(opts, p, "path", &local_err);
if (local_err) {
error_report_err(local_err);
goto fail;
}
return opts;
}
if (strstart(filename, "/dev/parport", NULL) ||
strstart(filename, "/dev/ppi", NULL)) {
qemu_opt_set(opts, "backend", "parport", &error_abort);
qemu_opt_set(opts, "path", filename, &error_abort);
return opts;
}
if (strstart(filename, "/dev/", NULL)) {
qemu_opt_set(opts, "backend", "tty", &error_abort);
qemu_opt_set(opts, "path", filename, &error_abort);
return opts;
}
fail:
qemu_opts_del(opts);
return NULL;
}
static void qemu_chr_parse_file_out(QemuOpts *opts, ChardevBackend *backend,
Error **errp)
{
const char *path = qemu_opt_get(opts, "path");
if (path == NULL) {
error_setg(errp, "chardev: file: no filename given");
return;
}
backend->file = g_new0(ChardevFile, 1);
backend->file->out = g_strdup(path);
}
static void qemu_chr_parse_stdio(QemuOpts *opts, ChardevBackend *backend,
Error **errp)
{
backend->stdio = g_new0(ChardevStdio, 1);
backend->stdio->has_signal = true;
backend->stdio->signal = qemu_opt_get_bool(opts, "signal", true);
}
static void qemu_chr_parse_serial(QemuOpts *opts, ChardevBackend *backend,
Error **errp)
{
const char *device = qemu_opt_get(opts, "path");
if (device == NULL) {
error_setg(errp, "chardev: serial/tty: no device path given");
return;
}
backend->serial = g_new0(ChardevHostdev, 1);
backend->serial->device = g_strdup(device);
}
static void qemu_chr_parse_parallel(QemuOpts *opts, ChardevBackend *backend,
Error **errp)
{
const char *device = qemu_opt_get(opts, "path");
if (device == NULL) {
error_setg(errp, "chardev: parallel: no device path given");
return;
}
backend->parallel = g_new0(ChardevHostdev, 1);
backend->parallel->device = g_strdup(device);
}
static void qemu_chr_parse_pipe(QemuOpts *opts, ChardevBackend *backend,
Error **errp)
{
const char *device = qemu_opt_get(opts, "path");
if (device == NULL) {
error_setg(errp, "chardev: pipe: no device path given");
return;
}
backend->pipe = g_new0(ChardevHostdev, 1);
backend->pipe->device = g_strdup(device);
}
Revert "chardev: Make the name of memory device consistent" This reverts commit 6a85e60cb994bd95d1537aafbff65816f3de4637. Commit 51767e7 "qemu-char: Add new char backend CirMemCharDriver" introduced a memory ring buffer character device driver named "memory". Commit 3949e59 "qemu-char: Saner naming of memchar stuff & doc fixes" changed the driver name to "ringbuf", along with a whole bunch of other names, with the following rationale: Naming is a mess. The code calls the device driver CirMemCharDriver, the public API calls it "memory", "memchardev", or "memchar", and the special commands are named like "memchar-FOO". "memory" is a particularly unfortunate choice, because there's another character device driver called MemoryDriver. Moreover, the device's distinctive property is that it's a ring buffer, not that's in memory. This is what we released in 1.4.0. Unfortunately, the rename missed a critical instance of "memory": the actual driver name. Thus, the new device could be used only by an entirely undocumented name. The documented name did not work. Bummer. Commit 6a85e60 fixes this by changing the documentation to match the code. It also changes some, but not all related occurences of "ringbuf" to "memory". Left alone are identifiers in C code, HMP and QMP commands. The latter are external interface, so they can't be changed. The result is an inconsistent mess. Moreover, "memory" is a rotten name. The device's distinctive property is that it's a ring buffer, not that's in memory. User's don't care whether it's in RAM, flash, or carved into chocolate tablets by Oompa Loompas. Revert the commit. Next commit will fix just the bug. Cc: qemu-stable@nongnu.org Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Message-id: 1374849874-25531-2-git-send-email-armbru@redhat.com Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-07-26 18:44:32 +04:00
static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend,
Error **errp)
{
int val;
backend->ringbuf = g_new0(ChardevRingbuf, 1);
val = qemu_opt_get_size(opts, "size", 0);
if (val != 0) {
backend->ringbuf->has_size = true;
backend->ringbuf->size = val;
}
}
static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend,
Error **errp)
{
const char *chardev = qemu_opt_get(opts, "chardev");
if (chardev == NULL) {
error_setg(errp, "chardev: mux: no chardev given");
return;
}
backend->mux = g_new0(ChardevMux, 1);
backend->mux->chardev = g_strdup(chardev);
}
static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
Error **errp)
{
bool is_listen = qemu_opt_get_bool(opts, "server", false);
bool is_waitconnect = is_listen && qemu_opt_get_bool(opts, "wait", true);
bool is_telnet = qemu_opt_get_bool(opts, "telnet", false);
bool do_nodelay = !qemu_opt_get_bool(opts, "delay", true);
int64_t reconnect = qemu_opt_get_number(opts, "reconnect", 0);
const char *path = qemu_opt_get(opts, "path");
const char *host = qemu_opt_get(opts, "host");
const char *port = qemu_opt_get(opts, "port");
SocketAddress *addr;
if (!path) {
if (!host) {
error_setg(errp, "chardev: socket: no host given");
return;
}
if (!port) {
error_setg(errp, "chardev: socket: no port given");
return;
}
}
backend->socket = g_new0(ChardevSocket, 1);
backend->socket->has_nodelay = true;
backend->socket->nodelay = do_nodelay;
backend->socket->has_server = true;
backend->socket->server = is_listen;
backend->socket->has_telnet = true;
backend->socket->telnet = is_telnet;
backend->socket->has_wait = true;
backend->socket->wait = is_waitconnect;
backend->socket->has_reconnect = true;
backend->socket->reconnect = reconnect;
addr = g_new0(SocketAddress, 1);
if (path) {
addr->kind = SOCKET_ADDRESS_KIND_UNIX;
addr->q_unix = g_new0(UnixSocketAddress, 1);
addr->q_unix->path = g_strdup(path);
} else {
addr->kind = SOCKET_ADDRESS_KIND_INET;
addr->inet = g_new0(InetSocketAddress, 1);
addr->inet->host = g_strdup(host);
addr->inet->port = g_strdup(port);
addr->inet->has_to = qemu_opt_get(opts, "to");
addr->inet->to = qemu_opt_get_number(opts, "to", 0);
addr->inet->has_ipv4 = qemu_opt_get(opts, "ipv4");
addr->inet->ipv4 = qemu_opt_get_bool(opts, "ipv4", 0);
addr->inet->has_ipv6 = qemu_opt_get(opts, "ipv6");
addr->inet->ipv6 = qemu_opt_get_bool(opts, "ipv6", 0);
}
backend->socket->addr = addr;
}
static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend,
Error **errp)
{
const char *host = qemu_opt_get(opts, "host");
const char *port = qemu_opt_get(opts, "port");
const char *localaddr = qemu_opt_get(opts, "localaddr");
const char *localport = qemu_opt_get(opts, "localport");
bool has_local = false;
SocketAddress *addr;
if (host == NULL || strlen(host) == 0) {
host = "localhost";
}
if (port == NULL || strlen(port) == 0) {
error_setg(errp, "chardev: udp: remote port not specified");
return;
}
if (localport == NULL || strlen(localport) == 0) {
localport = "0";
} else {
has_local = true;
}
if (localaddr == NULL || strlen(localaddr) == 0) {
localaddr = "";
} else {
has_local = true;
}
backend->udp = g_new0(ChardevUdp, 1);
addr = g_new0(SocketAddress, 1);
addr->kind = SOCKET_ADDRESS_KIND_INET;
addr->inet = g_new0(InetSocketAddress, 1);
addr->inet->host = g_strdup(host);
addr->inet->port = g_strdup(port);
addr->inet->has_ipv4 = qemu_opt_get(opts, "ipv4");
addr->inet->ipv4 = qemu_opt_get_bool(opts, "ipv4", 0);
addr->inet->has_ipv6 = qemu_opt_get(opts, "ipv6");
addr->inet->ipv6 = qemu_opt_get_bool(opts, "ipv6", 0);
backend->udp->remote = addr;
if (has_local) {
backend->udp->has_local = true;
addr = g_new0(SocketAddress, 1);
addr->kind = SOCKET_ADDRESS_KIND_INET;
addr->inet = g_new0(InetSocketAddress, 1);
addr->inet->host = g_strdup(localaddr);
addr->inet->port = g_strdup(localport);
backend->udp->local = addr;
}
}
typedef struct CharDriver {
const char *name;
ChardevBackendKind kind;
void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp);
} CharDriver;
static GSList *backends;
void register_char_driver(const char *name, ChardevBackendKind kind,
void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp))
{
CharDriver *s;
s = g_malloc0(sizeof(*s));
s->name = g_strdup(name);
s->kind = kind;
s->parse = parse;
backends = g_slist_append(backends, s);
}
CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
void (*init)(struct CharDriverState *s),
Error **errp)
{
Error *local_err = NULL;
CharDriver *cd;
CharDriverState *chr;
GSList *i;
ChardevReturn *ret = NULL;
ChardevBackend *backend;
const char *id = qemu_opts_id(opts);
char *bid = NULL;
if (id == NULL) {
error_setg(errp, "chardev: no id specified");
goto err;
}
if (qemu_opt_get(opts, "backend") == NULL) {
error_setg(errp, "chardev: \"%s\" missing backend",
qemu_opts_id(opts));
goto err;
}
for (i = backends; i; i = i->next) {
cd = i->data;
if (strcmp(cd->name, qemu_opt_get(opts, "backend")) == 0) {
break;
}
}
if (i == NULL) {
error_setg(errp, "chardev: backend \"%s\" not found",
qemu_opt_get(opts, "backend"));
goto err;
}
backend = g_new0(ChardevBackend, 1);
if (qemu_opt_get_bool(opts, "mux", 0)) {
bid = g_strdup_printf("%s-base", id);
}
chr = NULL;
backend->kind = cd->kind;
if (cd->parse) {
cd->parse(opts, backend, &local_err);
if (local_err) {
error_propagate(errp, local_err);
goto qapi_out;
}
}
ret = qmp_chardev_add(bid ? bid : id, backend, errp);
if (!ret) {
goto qapi_out;
}
if (bid) {
qapi_free_ChardevBackend(backend);
qapi_free_ChardevReturn(ret);
backend = g_new0(ChardevBackend, 1);
backend->mux = g_new0(ChardevMux, 1);
backend->kind = CHARDEV_BACKEND_KIND_MUX;
backend->mux->chardev = g_strdup(bid);
ret = qmp_chardev_add(id, backend, errp);
if (!ret) {
chr = qemu_chr_find(bid);
qemu_chr_delete(chr);
chr = NULL;
goto qapi_out;
}
qemu-char: don't issue CHR_EVENT_OPEN in a BH When CHR_EVENT_OPENED was initially added, it was CHR_EVENT_RESET, and it was issued as a bottom-half: 86e94dea5b740dad65446c857f6959eae43e0ba6 Which we basically used to print out a greeting/prompt for the monitor. AFAICT the only reason this was ever done in a BH was because in some cases we'd modify the chr_write handler for a new chardev backend *after* the site where we issued the reset (see: 86e94d:qemu_chr_open_stdio()) At some point this event was renamed to CHR_EVENT_OPENED, and we've maintained the use of this BH ever since. However, due to 9f939df955a4152aad69a19a77e0898631bb2c18, we schedule the BH via g_idle_add(), which is causing events to sometimes be delivered after we've already begun processing data from backends, leading to: known bugs: QMP: session negotation resets with OPENED event, in some cases this is causing new sessions to get sporadically reset potential bugs: hw/usb/redirect.c: can_read handler checks for dev->parser != NULL, which may be true if CLOSED BH has not been executed yet. In the past, OPENED quiesced outstanding CLOSED events prior to us reading client data. If it's delayed, our check may allow reads to occur even though we haven't processed the OPENED event yet, and when we do finally get the OPENED event, our state may get reset. qtest.c: can begin session before OPENED event is processed, leading to a spurious reset of the system and irq_levels gdbstub.c: may start a gdb session prior to the machine being paused To fix these, let's just drop the BH. Since the initial reasoning for using it still applies to an extent, work around that by deferring the delivery of CHR_EVENT_OPENED until after the chardevs have been fully initialized, toward the end of qmp_chardev_add() (or some cases, qemu_chr_new_from_opts()). This defers delivery long enough that we can be assured a CharDriverState is fully initialized before CHR_EVENT_OPENED is sent. Also, rather than requiring each chardev to do an explicit open, do it automatically, and allow the small few who don't desire such behavior to suppress the OPENED-on-init behavior by setting a 'explicit_be_open' flag. We additionally add missing OPENED events for stdio backends on w32, which were previously not being issued, causing us to not recieve the banner and initial prompts for qmp/hmp. Reported-by: Stefan Priebe <s.priebe@profihost.ag> Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com> Message-id: 1370636393-21044-1-git-send-email-mdroth@linux.vnet.ibm.com Cc: qemu-stable@nongnu.org Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-06-08 00:19:53 +04:00
}
chr = qemu_chr_find(id);
chr->opts = opts;
qapi_out:
qapi_free_ChardevBackend(backend);
qapi_free_ChardevReturn(ret);
g_free(bid);
return chr;
err:
qemu_opts_del(opts);
return NULL;
}
CharDriverState *qemu_chr_new(const char *label, const char *filename, void (*init)(struct CharDriverState *s))
{
const char *p;
CharDriverState *chr;
QemuOpts *opts;
Error *err = NULL;
if (strstart(filename, "chardev:", &p)) {
return qemu_chr_find(p);
}
opts = qemu_chr_parse_compat(label, filename);
if (!opts)
return NULL;
chr = qemu_chr_new_from_opts(opts, init, &err);
if (err) {
error_report_err(err);
}
if (chr && qemu_opt_get_bool(opts, "mux", 0)) {
qemu_chr_fe_claim_no_fail(chr);
monitor_init(chr, MONITOR_USE_READLINE);
}
return chr;
}
void qemu_chr_fe_set_echo(struct CharDriverState *chr, bool echo)
{
if (chr->chr_set_echo) {
chr->chr_set_echo(chr, echo);
}
}
void qemu_chr_fe_set_open(struct CharDriverState *chr, int fe_open)
{
if (chr->fe_open == fe_open) {
return;
}
chr->fe_open = fe_open;
if (chr->chr_set_fe_open) {
chr->chr_set_fe_open(chr, fe_open);
}
}
void qemu_chr_fe_event(struct CharDriverState *chr, int event)
{
if (chr->chr_fe_event) {
chr->chr_fe_event(chr, event);
}
}
int qemu_chr_fe_add_watch(CharDriverState *s, GIOCondition cond,
GIOFunc func, void *user_data)
{
GSource *src;
guint tag;
if (s->chr_add_watch == NULL) {
return -ENOSYS;
}
src = s->chr_add_watch(s, cond);
if (!src) {
return -EINVAL;
}
g_source_set_callback(src, (GSourceFunc)func, user_data, NULL);
tag = g_source_attach(src, NULL);
g_source_unref(src);
return tag;
}
int qemu_chr_fe_claim(CharDriverState *s)
{
if (s->avail_connections < 1) {
return -1;
}
s->avail_connections--;
return 0;
}
void qemu_chr_fe_claim_no_fail(CharDriverState *s)
{
if (qemu_chr_fe_claim(s) != 0) {
fprintf(stderr, "%s: error chardev \"%s\" already used\n",
__func__, s->label);
exit(1);
}
}
void qemu_chr_fe_release(CharDriverState *s)
{
s->avail_connections++;
}
void qemu_chr_delete(CharDriverState *chr)
{
QTAILQ_REMOVE(&chardevs, chr, next);
if (chr->chr_close) {
chr->chr_close(chr);
}
g_free(chr->filename);
g_free(chr->label);
qemu_opts_del(chr->opts);
g_free(chr);
}
ChardevInfoList *qmp_query_chardev(Error **errp)
{
ChardevInfoList *chr_list = NULL;
CharDriverState *chr;
QTAILQ_FOREACH(chr, &chardevs, next) {
ChardevInfoList *info = g_malloc0(sizeof(*info));
info->value = g_malloc0(sizeof(*info->value));
info->value->label = g_strdup(chr->label);
info->value->filename = g_strdup(chr->filename);
info->value->frontend_open = chr->fe_open;
info->next = chr_list;
chr_list = info;
}
return chr_list;
}
ChardevBackendInfoList *qmp_query_chardev_backends(Error **errp)
{
ChardevBackendInfoList *backend_list = NULL;
CharDriver *c = NULL;
GSList *i = NULL;
for (i = backends; i; i = i->next) {
ChardevBackendInfoList *info = g_malloc0(sizeof(*info));
c = i->data;
info->value = g_malloc0(sizeof(*info->value));
info->value->name = g_strdup(c->name);
info->next = backend_list;
backend_list = info;
}
return backend_list;
}
CharDriverState *qemu_chr_find(const char *name)
{
CharDriverState *chr;
QTAILQ_FOREACH(chr, &chardevs, next) {
if (strcmp(chr->label, name) != 0)
continue;
return chr;
}
return NULL;
}
/* Get a character (serial) device interface. */
CharDriverState *qemu_char_get_next_serial(void)
{
static int next_serial;
CharDriverState *chr;
/* FIXME: This function needs to go away: use chardev properties! */
while (next_serial < MAX_SERIAL_PORTS && serial_hds[next_serial]) {
chr = serial_hds[next_serial++];
qemu_chr_fe_claim_no_fail(chr);
return chr;
}
return NULL;
}
QemuOptsList qemu_chardev_opts = {
.name = "chardev",
.implied_opt_name = "backend",
.head = QTAILQ_HEAD_INITIALIZER(qemu_chardev_opts.head),
.desc = {
{
.name = "backend",
.type = QEMU_OPT_STRING,
},{
.name = "path",
.type = QEMU_OPT_STRING,
},{
.name = "host",
.type = QEMU_OPT_STRING,
},{
.name = "port",
.type = QEMU_OPT_STRING,
},{
.name = "localaddr",
.type = QEMU_OPT_STRING,
},{
.name = "localport",
.type = QEMU_OPT_STRING,
},{
.name = "to",
.type = QEMU_OPT_NUMBER,
},{
.name = "ipv4",
.type = QEMU_OPT_BOOL,
},{
.name = "ipv6",
.type = QEMU_OPT_BOOL,
},{
.name = "wait",
.type = QEMU_OPT_BOOL,
},{
.name = "server",
.type = QEMU_OPT_BOOL,
},{
.name = "delay",
.type = QEMU_OPT_BOOL,
},{
.name = "reconnect",
.type = QEMU_OPT_NUMBER,
},{
.name = "telnet",
.type = QEMU_OPT_BOOL,
},{
.name = "width",
.type = QEMU_OPT_NUMBER,
},{
.name = "height",
.type = QEMU_OPT_NUMBER,
},{
.name = "cols",
.type = QEMU_OPT_NUMBER,
},{
.name = "rows",
.type = QEMU_OPT_NUMBER,
},{
.name = "mux",
.type = QEMU_OPT_BOOL,
},{
.name = "signal",
.type = QEMU_OPT_BOOL,
},{
.name = "name",
.type = QEMU_OPT_STRING,
},{
.name = "debug",
.type = QEMU_OPT_NUMBER,
},{
.name = "size",
.type = QEMU_OPT_SIZE,
},{
.name = "chardev",
.type = QEMU_OPT_STRING,
},
{ /* end of list */ }
},
};
#ifdef _WIN32
static CharDriverState *qmp_chardev_open_file(ChardevFile *file, Error **errp)
{
HANDLE out;
if (file->has_in) {
error_setg(errp, "input file not supported");
return NULL;
}
out = CreateFile(file->out, GENERIC_WRITE, FILE_SHARE_READ, NULL,
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (out == INVALID_HANDLE_VALUE) {
error_setg(errp, "open %s failed", file->out);
return NULL;
}
return qemu_chr_open_win_file(out);
}
static CharDriverState *qmp_chardev_open_serial(ChardevHostdev *serial,
Error **errp)
{
return qemu_chr_open_win_path(serial->device);
}
static CharDriverState *qmp_chardev_open_parallel(ChardevHostdev *parallel,
Error **errp)
{
error_setg(errp, "character device backend type 'parallel' not supported");
return NULL;
}
#else /* WIN32 */
static int qmp_chardev_open_file_source(char *src, int flags,
Error **errp)
{
int fd = -1;
TFR(fd = qemu_open(src, flags, 0666));
if (fd == -1) {
error_setg_file_open(errp, errno, src);
}
return fd;
}
static CharDriverState *qmp_chardev_open_file(ChardevFile *file, Error **errp)
{
int flags, in = -1, out;
flags = O_WRONLY | O_TRUNC | O_CREAT | O_BINARY;
out = qmp_chardev_open_file_source(file->out, flags, errp);
if (out < 0) {
return NULL;
}
if (file->has_in) {
flags = O_RDONLY;
in = qmp_chardev_open_file_source(file->in, flags, errp);
if (in < 0) {
qemu_close(out);
return NULL;
}
}
return qemu_chr_open_fd(in, out);
}
static CharDriverState *qmp_chardev_open_serial(ChardevHostdev *serial,
Error **errp)
{
#ifdef HAVE_CHARDEV_TTY
int fd;
fd = qmp_chardev_open_file_source(serial->device, O_RDWR, errp);
if (fd < 0) {
return NULL;
}
qemu_set_nonblock(fd);
return qemu_chr_open_tty_fd(fd);
#else
error_setg(errp, "character device backend type 'serial' not supported");
return NULL;
#endif
}
static CharDriverState *qmp_chardev_open_parallel(ChardevHostdev *parallel,
Error **errp)
{
#ifdef HAVE_CHARDEV_PARPORT
int fd;
fd = qmp_chardev_open_file_source(parallel->device, O_RDWR, errp);
if (fd < 0) {
return NULL;
}
return qemu_chr_open_pp_fd(fd);
#else
error_setg(errp, "character device backend type 'parallel' not supported");
return NULL;
#endif
}
#endif /* WIN32 */
static void socket_try_connect(CharDriverState *chr)
{
Error *err = NULL;
if (!qemu_chr_open_socket_fd(chr, &err)) {
check_report_connect_error(chr, err);
}
}
static gboolean socket_reconnect_timeout(gpointer opaque)
{
CharDriverState *chr = opaque;
TCPCharDriver *s = chr->opaque;
s->reconnect_timer = 0;
if (chr->be_open) {
return false;
}
socket_try_connect(chr);
return false;
}
static CharDriverState *qmp_chardev_open_socket(ChardevSocket *sock,
Error **errp)
{
CharDriverState *chr;
TCPCharDriver *s;
SocketAddress *addr = sock->addr;
bool do_nodelay = sock->has_nodelay ? sock->nodelay : false;
bool is_listen = sock->has_server ? sock->server : true;
bool is_telnet = sock->has_telnet ? sock->telnet : false;
bool is_waitconnect = sock->has_wait ? sock->wait : false;
int64_t reconnect = sock->has_reconnect ? sock->reconnect : 0;
chr = qemu_chr_alloc();
s = g_malloc0(sizeof(TCPCharDriver));
s->fd = -1;
s->listen_fd = -1;
s->is_unix = addr->kind == SOCKET_ADDRESS_KIND_UNIX;
s->is_listen = is_listen;
s->is_telnet = is_telnet;
s->do_nodelay = do_nodelay;
qapi_copy_SocketAddress(&s->addr, sock->addr);
chr->opaque = s;
chr->chr_write = tcp_chr_write;
chr->chr_sync_read = tcp_chr_sync_read;
chr->chr_close = tcp_chr_close;
chr->get_msgfds = tcp_get_msgfds;
chr->set_msgfds = tcp_set_msgfds;
chr->chr_add_client = tcp_chr_add_client;
chr->chr_add_watch = tcp_chr_add_watch;
chr->chr_update_read_handler = tcp_chr_update_read_handler;
/* be isn't opened until we get a connection */
chr->explicit_be_open = true;
chr->filename = g_malloc(CHR_MAX_FILENAME_SIZE);
SocketAddress_to_str(chr->filename, CHR_MAX_FILENAME_SIZE, "disconnected:",
addr, is_listen, is_telnet);
if (is_listen) {
if (is_telnet) {
s->do_telnetopt = 1;
}
} else if (reconnect > 0) {
s->reconnect_time = reconnect;
}
if (s->reconnect_time) {
socket_try_connect(chr);
} else if (!qemu_chr_open_socket_fd(chr, errp)) {
g_free(s);
g_free(chr->filename);
g_free(chr);
return NULL;
}
if (is_listen && is_waitconnect) {
fprintf(stderr, "QEMU waiting for connection on: %s\n",
chr->filename);
tcp_chr_accept(s->listen_chan, G_IO_IN, chr);
qemu_set_nonblock(s->listen_fd);
}
return chr;
}
static CharDriverState *qmp_chardev_open_udp(ChardevUdp *udp,
Error **errp)
{
int fd;
fd = socket_dgram(udp->remote, udp->local, errp);
if (fd < 0) {
return NULL;
}
return qemu_chr_open_udp_fd(fd);
}
ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
Error **errp)
{
ChardevReturn *ret = g_new0(ChardevReturn, 1);
CharDriverState *base, *chr = NULL;
chr = qemu_chr_find(id);
if (chr) {
error_setg(errp, "Chardev '%s' already exists", id);
g_free(ret);
return NULL;
}
switch (backend->kind) {
case CHARDEV_BACKEND_KIND_FILE:
chr = qmp_chardev_open_file(backend->file, errp);
break;
case CHARDEV_BACKEND_KIND_SERIAL:
chr = qmp_chardev_open_serial(backend->serial, errp);
break;
case CHARDEV_BACKEND_KIND_PARALLEL:
chr = qmp_chardev_open_parallel(backend->parallel, errp);
break;
case CHARDEV_BACKEND_KIND_PIPE:
chr = qemu_chr_open_pipe(backend->pipe);
break;
case CHARDEV_BACKEND_KIND_SOCKET:
chr = qmp_chardev_open_socket(backend->socket, errp);
break;
case CHARDEV_BACKEND_KIND_UDP:
chr = qmp_chardev_open_udp(backend->udp, errp);
break;
#ifdef HAVE_CHARDEV_TTY
case CHARDEV_BACKEND_KIND_PTY:
chr = qemu_chr_open_pty(id, ret);
break;
#endif
case CHARDEV_BACKEND_KIND_NULL:
chr = qemu_chr_open_null();
break;
case CHARDEV_BACKEND_KIND_MUX:
base = qemu_chr_find(backend->mux->chardev);
if (base == NULL) {
error_setg(errp, "mux: base chardev %s not found",
backend->mux->chardev);
break;
}
chr = qemu_chr_open_mux(base);
break;
case CHARDEV_BACKEND_KIND_MSMOUSE:
chr = qemu_chr_open_msmouse();
break;
#ifdef CONFIG_BRLAPI
case CHARDEV_BACKEND_KIND_BRAILLE:
chr = chr_baum_init();
break;
#endif
case CHARDEV_BACKEND_KIND_TESTDEV:
chr = chr_testdev_init();
break;
case CHARDEV_BACKEND_KIND_STDIO:
chr = qemu_chr_open_stdio(backend->stdio);
break;
#ifdef _WIN32
case CHARDEV_BACKEND_KIND_CONSOLE:
chr = qemu_chr_open_win_con();
break;
#endif
#ifdef CONFIG_SPICE
case CHARDEV_BACKEND_KIND_SPICEVMC:
chr = qemu_chr_open_spice_vmc(backend->spicevmc->type);
break;
case CHARDEV_BACKEND_KIND_SPICEPORT:
chr = qemu_chr_open_spice_port(backend->spiceport->fqdn);
break;
#endif
case CHARDEV_BACKEND_KIND_VC:
chr = vc_init(backend->vc);
break;
case CHARDEV_BACKEND_KIND_RINGBUF:
case CHARDEV_BACKEND_KIND_MEMORY:
chr = qemu_chr_open_ringbuf(backend->ringbuf, errp);
break;
default:
error_setg(errp, "unknown chardev backend (%d)", backend->kind);
break;
}
/*
* Character backend open hasn't been fully converted to the Error
* API. Some opens fail without setting an error. Set a generic
* error then.
* TODO full conversion to Error API
*/
if (chr == NULL && errp && !*errp) {
error_setg(errp, "Failed to create chardev");
}
if (chr) {
chr->label = g_strdup(id);
chr->avail_connections =
(backend->kind == CHARDEV_BACKEND_KIND_MUX) ? MAX_MUX : 1;
if (!chr->filename) {
chr->filename = g_strdup(ChardevBackendKind_lookup[backend->kind]);
}
qemu-char: don't issue CHR_EVENT_OPEN in a BH When CHR_EVENT_OPENED was initially added, it was CHR_EVENT_RESET, and it was issued as a bottom-half: 86e94dea5b740dad65446c857f6959eae43e0ba6 Which we basically used to print out a greeting/prompt for the monitor. AFAICT the only reason this was ever done in a BH was because in some cases we'd modify the chr_write handler for a new chardev backend *after* the site where we issued the reset (see: 86e94d:qemu_chr_open_stdio()) At some point this event was renamed to CHR_EVENT_OPENED, and we've maintained the use of this BH ever since. However, due to 9f939df955a4152aad69a19a77e0898631bb2c18, we schedule the BH via g_idle_add(), which is causing events to sometimes be delivered after we've already begun processing data from backends, leading to: known bugs: QMP: session negotation resets with OPENED event, in some cases this is causing new sessions to get sporadically reset potential bugs: hw/usb/redirect.c: can_read handler checks for dev->parser != NULL, which may be true if CLOSED BH has not been executed yet. In the past, OPENED quiesced outstanding CLOSED events prior to us reading client data. If it's delayed, our check may allow reads to occur even though we haven't processed the OPENED event yet, and when we do finally get the OPENED event, our state may get reset. qtest.c: can begin session before OPENED event is processed, leading to a spurious reset of the system and irq_levels gdbstub.c: may start a gdb session prior to the machine being paused To fix these, let's just drop the BH. Since the initial reasoning for using it still applies to an extent, work around that by deferring the delivery of CHR_EVENT_OPENED until after the chardevs have been fully initialized, toward the end of qmp_chardev_add() (or some cases, qemu_chr_new_from_opts()). This defers delivery long enough that we can be assured a CharDriverState is fully initialized before CHR_EVENT_OPENED is sent. Also, rather than requiring each chardev to do an explicit open, do it automatically, and allow the small few who don't desire such behavior to suppress the OPENED-on-init behavior by setting a 'explicit_be_open' flag. We additionally add missing OPENED events for stdio backends on w32, which were previously not being issued, causing us to not recieve the banner and initial prompts for qmp/hmp. Reported-by: Stefan Priebe <s.priebe@profihost.ag> Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com> Message-id: 1370636393-21044-1-git-send-email-mdroth@linux.vnet.ibm.com Cc: qemu-stable@nongnu.org Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-06-08 00:19:53 +04:00
if (!chr->explicit_be_open) {
qemu_chr_be_event(chr, CHR_EVENT_OPENED);
}
QTAILQ_INSERT_TAIL(&chardevs, chr, next);
return ret;
} else {
g_free(ret);
return NULL;
}
}
void qmp_chardev_remove(const char *id, Error **errp)
{
CharDriverState *chr;
chr = qemu_chr_find(id);
if (chr == NULL) {
error_setg(errp, "Chardev '%s' not found", id);
return;
}
if (chr->chr_can_read || chr->chr_read ||
chr->chr_event || chr->handler_opaque) {
error_setg(errp, "Chardev '%s' is busy", id);
return;
}
qemu_chr_delete(chr);
}
static void register_types(void)
{
register_char_driver("null", CHARDEV_BACKEND_KIND_NULL, NULL);
register_char_driver("socket", CHARDEV_BACKEND_KIND_SOCKET,
qemu_chr_parse_socket);
register_char_driver("udp", CHARDEV_BACKEND_KIND_UDP, qemu_chr_parse_udp);
register_char_driver("ringbuf", CHARDEV_BACKEND_KIND_RINGBUF,
qemu_chr_parse_ringbuf);
register_char_driver("file", CHARDEV_BACKEND_KIND_FILE,
qemu_chr_parse_file_out);
register_char_driver("stdio", CHARDEV_BACKEND_KIND_STDIO,
qemu_chr_parse_stdio);
register_char_driver("serial", CHARDEV_BACKEND_KIND_SERIAL,
qemu_chr_parse_serial);
register_char_driver("tty", CHARDEV_BACKEND_KIND_SERIAL,
qemu_chr_parse_serial);
register_char_driver("parallel", CHARDEV_BACKEND_KIND_PARALLEL,
qemu_chr_parse_parallel);
register_char_driver("parport", CHARDEV_BACKEND_KIND_PARALLEL,
qemu_chr_parse_parallel);
register_char_driver("pty", CHARDEV_BACKEND_KIND_PTY, NULL);
register_char_driver("console", CHARDEV_BACKEND_KIND_CONSOLE, NULL);
register_char_driver("pipe", CHARDEV_BACKEND_KIND_PIPE,
qemu_chr_parse_pipe);
register_char_driver("mux", CHARDEV_BACKEND_KIND_MUX, qemu_chr_parse_mux);
/* Bug-compatibility: */
register_char_driver("memory", CHARDEV_BACKEND_KIND_MEMORY,
qemu_chr_parse_ringbuf);
/* this must be done after machine init, since we register FEs with muxes
* as part of realize functions like serial_isa_realizefn when -nographic
* is specified
*/
qemu_add_machine_init_done_notifier(&muxes_realize_notify);
}
type_init(register_types);