2008-10-31 18:49:55 +00:00
|
|
|
/*
|
|
|
|
* 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"
|
2012-12-17 17:19:49 +00:00
|
|
|
#include "monitor/monitor.h"
|
2012-11-28 11:06:30 +00:00
|
|
|
#include "ui/console.h"
|
2012-12-17 17:20:04 +00:00
|
|
|
#include "sysemu/sysemu.h"
|
2012-12-17 17:20:00 +00:00
|
|
|
#include "qemu/timer.h"
|
2013-04-08 14:55:25 +00:00
|
|
|
#include "sysemu/char.h"
|
2008-11-01 00:53:09 +00:00
|
|
|
#include "hw/usb.h"
|
2011-09-14 19:05:49 +00:00
|
|
|
#include "qmp-commands.h"
|
2008-10-31 18:49:55 +00:00
|
|
|
|
|
|
|
#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>
|
2008-11-07 16:55:48 +00:00
|
|
|
#include <sys/resource.h>
|
2008-10-31 18:49:55 +00:00
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <netinet/in.h>
|
2008-11-07 16:55:48 +00:00
|
|
|
#include <net/if.h>
|
|
|
|
#include <arpa/inet.h>
|
2008-10-31 18:49:55 +00:00
|
|
|
#include <dirent.h>
|
|
|
|
#include <netdb.h>
|
|
|
|
#include <sys/select.h>
|
2009-07-27 14:12:56 +00:00
|
|
|
#ifdef CONFIG_BSD
|
2008-10-31 18:49:55 +00:00
|
|
|
#include <sys/stat.h>
|
2009-11-29 17:00:41 +00:00
|
|
|
#if defined(__GLIBC__)
|
|
|
|
#include <pty.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 19:43:33 +00:00
|
|
|
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
|
|
|
|
#include <libutil.h>
|
|
|
|
#else
|
|
|
|
#include <util.h>
|
2009-11-29 17:00:41 +00:00
|
|
|
#endif
|
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 19:43:33 +00:00
|
|
|
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
|
|
|
#include <dev/ppbus/ppi.h>
|
|
|
|
#include <dev/ppbus/ppbconf.h>
|
2009-03-07 20:06:23 +00:00
|
|
|
#elif defined(__DragonFly__)
|
|
|
|
#include <dev/misc/ppi/ppi.h>
|
|
|
|
#include <bus/ppbus/ppbconf.h>
|
2008-10-31 18:49:55 +00:00
|
|
|
#endif
|
2009-11-30 14:42:59 +00:00
|
|
|
#else
|
2008-10-31 18:49:55 +00:00
|
|
|
#ifdef __linux__
|
|
|
|
#include <pty.h>
|
|
|
|
|
|
|
|
#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>
|
|
|
|
#include <net/if.h>
|
|
|
|
#include <syslog.h>
|
|
|
|
#include <stropts.h>
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
2012-12-17 17:20:00 +00:00
|
|
|
#include "qemu/sockets.h"
|
2011-01-19 08:49:50 +00:00
|
|
|
#include "ui/qemu-spice.h"
|
2008-10-31 18:49:55 +00:00
|
|
|
|
2009-11-03 14:29:54 +00:00
|
|
|
#define READ_BUF_LEN 4096
|
|
|
|
|
2008-10-31 18:49:55 +00:00
|
|
|
/***********************************************************/
|
|
|
|
/* character device */
|
|
|
|
|
2009-09-12 07:36:22 +00:00
|
|
|
static QTAILQ_HEAD(CharDriverStateHead, CharDriverState) chardevs =
|
|
|
|
QTAILQ_HEAD_INITIALIZER(chardevs);
|
2009-03-05 22:59:58 +00:00
|
|
|
|
2011-11-19 09:22:43 +00:00
|
|
|
void qemu_chr_be_event(CharDriverState *s, int event)
|
2008-10-31 18:49:55 +00:00
|
|
|
{
|
2010-04-01 16:42:39 +00:00
|
|
|
/* Keep track if the char device is open */
|
|
|
|
switch (event) {
|
|
|
|
case CHR_EVENT_OPENED:
|
2013-03-26 10:07:53 +00:00
|
|
|
s->be_open = 1;
|
2010-04-01 16:42:39 +00:00
|
|
|
break;
|
|
|
|
case CHR_EVENT_CLOSED:
|
2013-03-26 10:07:53 +00:00
|
|
|
s->be_open = 0;
|
2010-04-01 16:42:39 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-10-31 18:49:55 +00:00
|
|
|
if (!s->chr_event)
|
|
|
|
return;
|
|
|
|
s->chr_event(s->handler_opaque, event);
|
|
|
|
}
|
|
|
|
|
2013-03-26 10:07:54 +00:00
|
|
|
static gboolean qemu_chr_be_generic_open_bh(gpointer opaque)
|
2008-10-31 18:49:55 +00:00
|
|
|
{
|
|
|
|
CharDriverState *s = opaque;
|
2011-11-19 09:22:43 +00:00
|
|
|
qemu_chr_be_event(s, CHR_EVENT_OPENED);
|
2013-03-05 17:51:27 +00:00
|
|
|
s->idle_tag = 0;
|
|
|
|
return FALSE;
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
|
2013-03-26 10:07:54 +00:00
|
|
|
void qemu_chr_be_generic_open(CharDriverState *s)
|
2008-10-31 18:49:55 +00:00
|
|
|
{
|
2013-03-05 17:51:27 +00:00
|
|
|
if (s->idle_tag == 0) {
|
2013-03-26 10:07:54 +00:00
|
|
|
s->idle_tag = g_idle_add(qemu_chr_be_generic_open_bh, s);
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-15 16:17:28 +00:00
|
|
|
int qemu_chr_fe_write(CharDriverState *s, const uint8_t *buf, int len)
|
2008-10-31 18:49:55 +00:00
|
|
|
{
|
|
|
|
return s->chr_write(s, buf, len);
|
|
|
|
}
|
|
|
|
|
2013-03-26 15:04:17 +00:00
|
|
|
int qemu_chr_fe_write_all(CharDriverState *s, const uint8_t *buf, int len)
|
|
|
|
{
|
|
|
|
int offset = 0;
|
|
|
|
int res;
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (res < 0) {
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
offset += res;
|
|
|
|
}
|
|
|
|
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
2011-08-15 16:17:34 +00:00
|
|
|
int qemu_chr_fe_ioctl(CharDriverState *s, int cmd, void *arg)
|
2008-10-31 18:49:55 +00:00
|
|
|
{
|
|
|
|
if (!s->chr_ioctl)
|
|
|
|
return -ENOTSUP;
|
|
|
|
return s->chr_ioctl(s, cmd, arg);
|
|
|
|
}
|
|
|
|
|
2011-08-15 16:17:31 +00:00
|
|
|
int qemu_chr_be_can_write(CharDriverState *s)
|
2008-10-31 18:49:55 +00:00
|
|
|
{
|
|
|
|
if (!s->chr_can_read)
|
|
|
|
return 0;
|
|
|
|
return s->chr_can_read(s->handler_opaque);
|
|
|
|
}
|
|
|
|
|
2011-08-15 16:17:30 +00:00
|
|
|
void qemu_chr_be_write(CharDriverState *s, uint8_t *buf, int len)
|
2008-10-31 18:49:55 +00:00
|
|
|
{
|
2012-04-19 20:27:14 +00:00
|
|
|
if (s->chr_read) {
|
|
|
|
s->chr_read(s->handler_opaque, buf, len);
|
|
|
|
}
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
|
2011-08-15 16:17:39 +00:00
|
|
|
int qemu_chr_fe_get_msgfd(CharDriverState *s)
|
2009-07-22 08:11:39 +00:00
|
|
|
{
|
|
|
|
return s->get_msgfd ? s->get_msgfd(s) : -1;
|
|
|
|
}
|
|
|
|
|
Introduce a 'client_add' monitor command accepting an open FD
Allow client connections for VNC and socket based character
devices to be passed in over the monitor using SCM_RIGHTS.
One intended usage scenario is to start QEMU with VNC on a
UNIX domain socket. An unprivileged user which cannot access
the UNIX domain socket, can then connect to QEMU's VNC server
by passing an open FD to libvirt, which passes it onto QEMU.
{ "execute": "get_fd", "arguments": { "fdname": "myclient" } }
{ "return": {} }
{ "execute": "add_client", "arguments": { "protocol": "vnc",
"fdname": "myclient",
"skipauth": true } }
{ "return": {} }
In this case 'protocol' can be 'vnc' or 'spice', or the name
of a character device (eg from -chardev id=XXXX)
The 'skipauth' parameter can be used to skip any configured
VNC authentication scheme, which is useful if the mgmt layer
talking to the monitor has already authenticated the client
in another way.
* console.h: Define 'vnc_display_add_client' method
* monitor.c: Implement 'client_add' command
* qemu-char.c, qemu-char.h: Add 'qemu_char_add_client' method
* qerror.c, qerror.h: Add QERR_ADD_CLIENT_FAILED
* qmp-commands.hx: Declare 'client_add' command
* ui/vnc.c: Implement 'vnc_display_add_client' method
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2011-06-23 12:31:42 +00:00
|
|
|
int qemu_chr_add_client(CharDriverState *s, int fd)
|
|
|
|
{
|
|
|
|
return s->chr_add_client ? s->chr_add_client(s, fd) : -1;
|
|
|
|
}
|
|
|
|
|
2008-10-31 18:49:55 +00:00
|
|
|
void qemu_chr_accept_input(CharDriverState *s)
|
|
|
|
{
|
|
|
|
if (s->chr_accept_input)
|
|
|
|
s->chr_accept_input(s);
|
2012-03-16 12:18:00 +00:00
|
|
|
qemu_notify_event();
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
|
2011-08-15 16:17:29 +00:00
|
|
|
void qemu_chr_fe_printf(CharDriverState *s, const char *fmt, ...)
|
2008-10-31 18:49:55 +00:00
|
|
|
{
|
2009-11-03 14:29:54 +00:00
|
|
|
char buf[READ_BUF_LEN];
|
2008-10-31 18:49:55 +00:00
|
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
|
|
vsnprintf(buf, sizeof(buf), fmt, ap);
|
2011-08-15 16:17:28 +00:00
|
|
|
qemu_chr_fe_write(s, (uint8_t *)buf, strlen(buf));
|
2008-10-31 18:49:55 +00:00
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
|
|
|
void qemu_chr_add_handlers(CharDriverState *s,
|
2010-03-11 16:55:39 +00:00
|
|
|
IOCanReadHandler *fd_can_read,
|
2008-10-31 18:49:55 +00:00
|
|
|
IOReadHandler *fd_read,
|
|
|
|
IOEventHandler *fd_event,
|
|
|
|
void *opaque)
|
|
|
|
{
|
2013-03-26 10:07:56 +00:00
|
|
|
int fe_open;
|
|
|
|
|
2011-04-25 09:48:22 +00:00
|
|
|
if (!opaque && !fd_can_read && !fd_read && !fd_event) {
|
2013-03-26 10:07:56 +00:00
|
|
|
fe_open = 0;
|
|
|
|
} else {
|
|
|
|
fe_open = 1;
|
2011-02-10 07:25:20 +00:00
|
|
|
}
|
2008-10-31 18:49:55 +00:00
|
|
|
s->chr_can_read = fd_can_read;
|
|
|
|
s->chr_read = fd_read;
|
|
|
|
s->chr_event = fd_event;
|
|
|
|
s->handler_opaque = opaque;
|
|
|
|
if (s->chr_update_read_handler)
|
|
|
|
s->chr_update_read_handler(s);
|
2010-04-01 16:42:39 +00:00
|
|
|
|
2013-03-26 10:07:56 +00:00
|
|
|
if (!s->explicit_fe_open) {
|
2013-03-26 10:07:57 +00:00
|
|
|
qemu_chr_fe_set_open(s, fe_open);
|
2013-03-26 10:07:56 +00:00
|
|
|
}
|
|
|
|
|
2010-04-01 16:42:39 +00:00
|
|
|
/* We're connecting to an already opened device, so let's make sure we
|
|
|
|
also get the open event */
|
2013-03-26 10:08:00 +00:00
|
|
|
if (fe_open && s->be_open) {
|
2013-03-26 10:07:54 +00:00
|
|
|
qemu_chr_be_generic_open(s);
|
2010-04-01 16:42:39 +00:00
|
|
|
}
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
|
|
|
|
{
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2013-02-21 10:41:26 +00:00
|
|
|
static CharDriverState *qemu_chr_open_null(void)
|
2008-10-31 18:49:55 +00:00
|
|
|
{
|
|
|
|
CharDriverState *chr;
|
|
|
|
|
2011-08-21 03:09:37 +00:00
|
|
|
chr = g_malloc0(sizeof(CharDriverState));
|
2008-10-31 18:49:55 +00:00
|
|
|
chr->chr_write = null_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 14:09:08 +00:00
|
|
|
return chr;
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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 {
|
2010-03-11 16:55:39 +00:00
|
|
|
IOCanReadHandler *chr_can_read[MAX_MUX];
|
2008-10-31 18:49:55 +00:00
|
|
|
IOReadHandler *chr_read[MAX_MUX];
|
|
|
|
IOEventHandler *chr_event[MAX_MUX];
|
|
|
|
void *ext_opaque[MAX_MUX];
|
|
|
|
CharDriverState *drv;
|
2009-09-10 08:58:55 +00:00
|
|
|
int focus;
|
2008-10-31 18:49:55 +00:00
|
|
|
int mux_cnt;
|
|
|
|
int term_got_escape;
|
|
|
|
int max_size;
|
2009-03-05 23:00:02 +00:00
|
|
|
/* 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];
|
2009-06-15 20:25:30 +00:00
|
|
|
int timestamps;
|
2009-06-15 20:25:34 +00:00
|
|
|
int linestart;
|
2009-06-15 20:25:30 +00:00
|
|
|
int64_t timestamps_start;
|
2008-10-31 18:49:55 +00:00
|
|
|
} MuxDriver;
|
|
|
|
|
|
|
|
|
|
|
|
static int mux_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
|
|
|
|
{
|
|
|
|
MuxDriver *d = chr->opaque;
|
|
|
|
int ret;
|
2009-06-15 20:25:30 +00:00
|
|
|
if (!d->timestamps) {
|
2008-10-31 18:49:55 +00:00
|
|
|
ret = d->drv->chr_write(d->drv, buf, len);
|
|
|
|
} else {
|
|
|
|
int i;
|
|
|
|
|
|
|
|
ret = 0;
|
2009-06-15 20:25:34 +00:00
|
|
|
for (i = 0; i < len; i++) {
|
|
|
|
if (d->linestart) {
|
2008-10-31 18:49:55 +00:00
|
|
|
char buf1[64];
|
|
|
|
int64_t ti;
|
|
|
|
int secs;
|
|
|
|
|
2011-03-11 15:47:48 +00:00
|
|
|
ti = qemu_get_clock_ms(rt_clock);
|
2009-06-15 20:25:30 +00:00
|
|
|
if (d->timestamps_start == -1)
|
|
|
|
d->timestamps_start = ti;
|
|
|
|
ti -= d->timestamps_start;
|
2009-01-22 17:15:16 +00:00
|
|
|
secs = ti / 1000;
|
2008-10-31 18:49:55 +00:00
|
|
|
snprintf(buf1, sizeof(buf1),
|
|
|
|
"[%02d:%02d:%02d.%03d] ",
|
|
|
|
secs / 3600,
|
|
|
|
(secs / 60) % 60,
|
|
|
|
secs % 60,
|
2009-01-22 17:15:16 +00:00
|
|
|
(int)(ti % 1000));
|
2008-10-31 18:49:55 +00:00
|
|
|
d->drv->chr_write(d->drv, (uint8_t *)buf1, strlen(buf1));
|
2009-06-15 20:25:34 +00:00
|
|
|
d->linestart = 0;
|
|
|
|
}
|
|
|
|
ret += d->drv->chr_write(d->drv, buf+i, 1);
|
|
|
|
if (buf[i] == '\n') {
|
|
|
|
d->linestart = 1;
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
chr->chr_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] == '%')
|
|
|
|
chr->chr_write(chr, (uint8_t *)ebuf, strlen(ebuf));
|
|
|
|
else
|
|
|
|
chr->chr_write(chr, (uint8_t *)&mux_help[i][j], 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-05 23:01:47 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2008-10-31 18:49:55 +00:00
|
|
|
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";
|
|
|
|
chr->chr_write(chr,(uint8_t *)term,strlen(term));
|
|
|
|
exit(0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 's':
|
2010-06-02 16:55:18 +00:00
|
|
|
bdrv_commit_all();
|
2008-10-31 18:49:55 +00:00
|
|
|
break;
|
|
|
|
case 'b':
|
2011-11-19 09:22:43 +00:00
|
|
|
qemu_chr_be_event(chr, CHR_EVENT_BREAK);
|
2008-10-31 18:49:55 +00:00
|
|
|
break;
|
|
|
|
case 'c':
|
|
|
|
/* Switch to the next registered device */
|
2009-09-10 08:58:55 +00:00
|
|
|
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);
|
2008-10-31 18:49:55 +00:00
|
|
|
break;
|
2009-06-15 20:25:30 +00:00
|
|
|
case 't':
|
|
|
|
d->timestamps = !d->timestamps;
|
|
|
|
d->timestamps_start = -1;
|
2009-06-15 20:25:34 +00:00
|
|
|
d->linestart = 0;
|
2009-06-15 20:25:30 +00:00
|
|
|
break;
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
} 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;
|
2009-09-10 08:58:55 +00:00
|
|
|
int m = d->focus;
|
2008-10-31 18:49:55 +00:00
|
|
|
|
2009-03-05 23:00:02 +00:00
|
|
|
while (d->prod[m] != d->cons[m] &&
|
2008-10-31 18:49:55 +00:00
|
|
|
d->chr_can_read[m] &&
|
|
|
|
d->chr_can_read[m](d->ext_opaque[m])) {
|
|
|
|
d->chr_read[m](d->ext_opaque[m],
|
2009-03-05 23:00:02 +00:00
|
|
|
&d->buffer[m][d->cons[m]++ & MUX_BUFFER_MASK], 1);
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mux_chr_can_read(void *opaque)
|
|
|
|
{
|
|
|
|
CharDriverState *chr = opaque;
|
|
|
|
MuxDriver *d = chr->opaque;
|
2009-09-10 08:58:55 +00:00
|
|
|
int m = d->focus;
|
2008-10-31 18:49:55 +00:00
|
|
|
|
2009-03-05 23:00:02 +00:00
|
|
|
if ((d->prod[m] - d->cons[m]) < MUX_BUFFER_SIZE)
|
2008-10-31 18:49:55 +00:00
|
|
|
return 1;
|
2009-03-05 23:00:02 +00:00
|
|
|
if (d->chr_can_read[m])
|
|
|
|
return d->chr_can_read[m](d->ext_opaque[m]);
|
2008-10-31 18:49:55 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mux_chr_read(void *opaque, const uint8_t *buf, int size)
|
|
|
|
{
|
|
|
|
CharDriverState *chr = opaque;
|
|
|
|
MuxDriver *d = chr->opaque;
|
2009-09-10 08:58:55 +00:00
|
|
|
int m = d->focus;
|
2008-10-31 18:49:55 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
mux_chr_accept_input (opaque);
|
|
|
|
|
|
|
|
for(i = 0; i < size; i++)
|
|
|
|
if (mux_proc_byte(chr, d, buf[i])) {
|
2009-03-05 23:00:02 +00:00
|
|
|
if (d->prod[m] == d->cons[m] &&
|
2008-10-31 18:49:55 +00:00
|
|
|
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
|
2009-03-05 23:00:02 +00:00
|
|
|
d->buffer[m][d->prod[m]++ & MUX_BUFFER_MASK] = buf[i];
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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++)
|
2009-03-05 23:01:47 +00:00
|
|
|
mux_chr_send_event(d, i, event);
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2009-09-10 08:58:55 +00:00
|
|
|
if (d->focus != -1) {
|
|
|
|
mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT);
|
2009-09-10 08:58:54 +00:00
|
|
|
}
|
2009-09-10 08:58:55 +00:00
|
|
|
d->focus = d->mux_cnt;
|
2008-10-31 18:49:55 +00:00
|
|
|
d->mux_cnt++;
|
2009-09-10 08:58:55 +00:00
|
|
|
mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN);
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static CharDriverState *qemu_chr_open_mux(CharDriverState *drv)
|
|
|
|
{
|
|
|
|
CharDriverState *chr;
|
|
|
|
MuxDriver *d;
|
|
|
|
|
2011-08-21 03:09:37 +00:00
|
|
|
chr = g_malloc0(sizeof(CharDriverState));
|
|
|
|
d = g_malloc0(sizeof(MuxDriver));
|
2008-10-31 18:49:55 +00:00
|
|
|
|
|
|
|
chr->opaque = d;
|
|
|
|
d->drv = drv;
|
2009-09-10 08:58:55 +00:00
|
|
|
d->focus = -1;
|
2008-10-31 18:49:55 +00:00
|
|
|
chr->chr_write = mux_chr_write;
|
|
|
|
chr->chr_update_read_handler = mux_chr_update_read_handler;
|
|
|
|
chr->chr_accept_input = mux_chr_accept_input;
|
2011-03-24 10:12:02 +00:00
|
|
|
/* Frontend guest-open / -close notification is not support with muxes */
|
2013-03-26 10:07:58 +00:00
|
|
|
chr->chr_set_fe_open = NULL;
|
2010-04-01 16:42:39 +00:00
|
|
|
|
|
|
|
/* Muxes are always open on creation */
|
2013-03-26 10:07:54 +00:00
|
|
|
qemu_chr_be_generic_open(chr);
|
2010-04-01 16:42:39 +00:00
|
|
|
|
2008-10-31 18:49:55 +00:00
|
|
|
return chr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
2008-11-11 20:46:40 +00:00
|
|
|
int send_all(int fd, const void *buf, int len1)
|
2008-10-31 18:49:55 +00:00
|
|
|
{
|
|
|
|
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
|
|
|
|
|
2010-11-01 19:02:23 +00:00
|
|
|
int send_all(int fd, const void *_buf, int len1)
|
2008-10-31 18:49:55 +00:00
|
|
|
{
|
|
|
|
int ret, len;
|
2010-11-01 19:02:23 +00:00
|
|
|
const uint8_t *buf = _buf;
|
2008-10-31 18:49:55 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2013-02-27 17:47:53 +00: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;
|
|
|
|
}
|
|
|
|
|
2008-10-31 18:49:55 +00:00
|
|
|
#endif /* !_WIN32 */
|
|
|
|
|
2013-03-05 17:51:18 +00:00
|
|
|
typedef struct IOWatchPoll
|
|
|
|
{
|
2013-04-05 15:59:33 +00:00
|
|
|
GSource parent;
|
|
|
|
|
2013-04-08 13:03:15 +00:00
|
|
|
GIOChannel *channel;
|
2013-03-05 17:51:18 +00:00
|
|
|
GSource *src;
|
|
|
|
|
|
|
|
IOCanReadHandler *fd_can_read;
|
2013-04-08 13:03:15 +00:00
|
|
|
GSourceFunc fd_read;
|
2013-03-05 17:51:18 +00:00
|
|
|
void *opaque;
|
|
|
|
} IOWatchPoll;
|
|
|
|
|
|
|
|
static IOWatchPoll *io_watch_poll_from_source(GSource *source)
|
|
|
|
{
|
2013-04-05 15:59:33 +00:00
|
|
|
return container_of(source, IOWatchPoll, parent);
|
2013-03-05 17:51:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean io_watch_poll_prepare(GSource *source, gint *timeout_)
|
|
|
|
{
|
|
|
|
IOWatchPoll *iwp = io_watch_poll_from_source(source);
|
2013-04-05 15:59:33 +00:00
|
|
|
bool now_active = iwp->fd_can_read(iwp->opaque) > 0;
|
2013-04-08 13:03:15 +00:00
|
|
|
bool was_active = iwp->src != NULL;
|
2013-04-05 15:59:33 +00:00
|
|
|
if (was_active == now_active) {
|
2013-03-05 17:51:18 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2013-04-05 15:59:33 +00:00
|
|
|
if (now_active) {
|
2013-04-08 13:03:15 +00:00
|
|
|
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);
|
2013-04-05 15:59:33 +00:00
|
|
|
g_source_attach(iwp->src, NULL);
|
|
|
|
} else {
|
2013-04-08 13:03:15 +00:00
|
|
|
g_source_destroy(iwp->src);
|
|
|
|
g_source_unref(iwp->src);
|
|
|
|
iwp->src = NULL;
|
2013-04-05 15:59:33 +00:00
|
|
|
}
|
|
|
|
return FALSE;
|
2013-03-05 17:51:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean io_watch_poll_check(GSource *source)
|
|
|
|
{
|
2013-04-05 15:59:33 +00:00
|
|
|
return FALSE;
|
2013-03-05 17:51:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean io_watch_poll_dispatch(GSource *source, GSourceFunc callback,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
2013-04-05 15:59:33 +00:00
|
|
|
abort();
|
2013-03-05 17:51:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void io_watch_poll_finalize(GSource *source)
|
|
|
|
{
|
|
|
|
IOWatchPoll *iwp = io_watch_poll_from_source(source);
|
2013-04-19 15:32:06 +00:00
|
|
|
if (iwp->src) {
|
|
|
|
g_source_destroy(iwp->src);
|
|
|
|
g_source_unref(iwp->src);
|
|
|
|
iwp->src = NULL;
|
|
|
|
}
|
2013-03-05 17:51:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2013-04-10 13:23:27 +00:00
|
|
|
int tag;
|
2013-03-05 17:51:18 +00:00
|
|
|
|
2013-04-05 15:59:33 +00:00
|
|
|
iwp = (IOWatchPoll *) g_source_new(&io_watch_poll_funcs, sizeof(IOWatchPoll));
|
2013-03-05 17:51:18 +00:00
|
|
|
iwp->fd_can_read = fd_can_read;
|
|
|
|
iwp->opaque = user_data;
|
2013-04-08 13:03:15 +00:00
|
|
|
iwp->channel = channel;
|
|
|
|
iwp->fd_read = (GSourceFunc) fd_read;
|
|
|
|
iwp->src = NULL;
|
2013-03-05 17:51:18 +00:00
|
|
|
|
2013-04-10 13:23:27 +00:00
|
|
|
tag = g_source_attach(&iwp->parent, NULL);
|
|
|
|
g_source_unref(&iwp->parent);
|
|
|
|
return tag;
|
2013-03-05 17:51:18 +00:00
|
|
|
}
|
|
|
|
|
2013-03-09 09:56:04 +00:00
|
|
|
#ifndef _WIN32
|
2013-03-05 17:51:18 +00:00
|
|
|
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;
|
|
|
|
}
|
2013-03-09 09:56:04 +00:00
|
|
|
#endif
|
2013-03-05 17:51:18 +00:00
|
|
|
|
2013-03-05 17:51:21 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-03-29 16:39:50 +00:00
|
|
|
static int io_channel_send(GIOChannel *fd, const void *buf, size_t len)
|
2013-03-05 17:51:18 +00:00
|
|
|
{
|
|
|
|
GIOStatus status;
|
2013-03-29 16:39:50 +00:00
|
|
|
size_t offset;
|
2013-03-05 17:51:18 +00:00
|
|
|
|
2013-03-29 16:39:50 +00:00
|
|
|
offset = 0;
|
|
|
|
while (offset < len) {
|
|
|
|
gsize bytes_written;
|
|
|
|
|
|
|
|
status = g_io_channel_write_chars(fd, buf + offset, len - offset,
|
2013-03-05 17:51:18 +00:00
|
|
|
&bytes_written, NULL);
|
|
|
|
if (status != G_IO_STATUS_NORMAL) {
|
2013-03-05 17:51:23 +00:00
|
|
|
if (status == G_IO_STATUS_AGAIN) {
|
2013-03-29 16:39:50 +00:00
|
|
|
/* If we've written any data, return a partial write. */
|
|
|
|
if (offset) {
|
|
|
|
break;
|
|
|
|
}
|
2013-03-05 17:51:23 +00:00
|
|
|
errno = EAGAIN;
|
|
|
|
} else {
|
|
|
|
errno = EINVAL;
|
2013-03-05 17:51:18 +00:00
|
|
|
}
|
2013-03-29 16:39:50 +00:00
|
|
|
|
|
|
|
return -1;
|
2013-03-05 17:51:18 +00:00
|
|
|
} else if (status == G_IO_STATUS_EOF) {
|
|
|
|
break;
|
|
|
|
}
|
2013-03-29 16:39:50 +00:00
|
|
|
|
|
|
|
offset += bytes_written;
|
2013-03-05 17:51:18 +00:00
|
|
|
}
|
2013-03-29 16:39:50 +00:00
|
|
|
|
|
|
|
return offset;
|
2013-03-05 17:51:18 +00:00
|
|
|
}
|
|
|
|
|
2013-03-09 09:56:04 +00:00
|
|
|
#ifndef _WIN32
|
|
|
|
|
2013-03-05 17:51:19 +00:00
|
|
|
typedef struct FDCharDriver {
|
|
|
|
CharDriverState *chr;
|
|
|
|
GIOChannel *fd_in, *fd_out;
|
|
|
|
guint fd_in_tag;
|
2008-10-31 18:49:55 +00:00
|
|
|
int max_size;
|
2013-03-05 17:51:19 +00:00
|
|
|
QTAILQ_ENTRY(FDCharDriver) node;
|
2008-10-31 18:49:55 +00:00
|
|
|
} FDCharDriver;
|
|
|
|
|
|
|
|
static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
|
|
|
|
{
|
|
|
|
FDCharDriver *s = chr->opaque;
|
2013-03-05 17:51:19 +00:00
|
|
|
|
2013-03-29 16:39:50 +00:00
|
|
|
return io_channel_send(s->fd_out, buf, len);
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
|
2013-03-05 17:51:19 +00:00
|
|
|
static gboolean fd_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
|
2008-10-31 18:49:55 +00:00
|
|
|
{
|
|
|
|
CharDriverState *chr = opaque;
|
|
|
|
FDCharDriver *s = chr->opaque;
|
2013-03-05 17:51:19 +00:00
|
|
|
int len;
|
2009-11-03 14:29:54 +00:00
|
|
|
uint8_t buf[READ_BUF_LEN];
|
2013-03-05 17:51:19 +00:00
|
|
|
GIOStatus status;
|
|
|
|
gsize bytes_read;
|
2008-10-31 18:49:55 +00:00
|
|
|
|
|
|
|
len = sizeof(buf);
|
2013-03-05 17:51:19 +00:00
|
|
|
if (len > s->max_size) {
|
2008-10-31 18:49:55 +00:00
|
|
|
len = s->max_size;
|
2013-03-05 17:51:19 +00:00
|
|
|
}
|
|
|
|
if (len == 0) {
|
2013-04-19 15:32:08 +00:00
|
|
|
return TRUE;
|
2013-03-05 17:51:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
status = g_io_channel_read_chars(chan, (gchar *)buf,
|
|
|
|
len, &bytes_read, NULL);
|
|
|
|
if (status == G_IO_STATUS_EOF) {
|
2013-04-19 15:32:08 +00:00
|
|
|
if (s->fd_in_tag) {
|
|
|
|
g_source_remove(s->fd_in_tag);
|
|
|
|
s->fd_in_tag = 0;
|
|
|
|
}
|
2011-11-19 09:22:43 +00:00
|
|
|
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
|
2013-03-05 17:51:19 +00:00
|
|
|
return FALSE;
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
2013-03-05 17:51:19 +00:00
|
|
|
if (status == G_IO_STATUS_NORMAL) {
|
|
|
|
qemu_chr_be_write(chr, buf, bytes_read);
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
2013-03-05 17:51:19 +00:00
|
|
|
|
|
|
|
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;
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
|
2013-03-05 17:51:23 +00:00
|
|
|
static GSource *fd_chr_add_watch(CharDriverState *chr, GIOCondition cond)
|
|
|
|
{
|
|
|
|
FDCharDriver *s = chr->opaque;
|
|
|
|
return g_io_create_watch(s->fd_out, cond);
|
|
|
|
}
|
|
|
|
|
2008-10-31 18:49:55 +00:00
|
|
|
static void fd_chr_update_read_handler(CharDriverState *chr)
|
|
|
|
{
|
|
|
|
FDCharDriver *s = chr->opaque;
|
|
|
|
|
2013-03-05 17:51:19 +00:00
|
|
|
if (s->fd_in_tag) {
|
|
|
|
g_source_remove(s->fd_in_tag);
|
2013-04-19 15:32:06 +00:00
|
|
|
s->fd_in_tag = 0;
|
2013-03-05 17:51:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (s->fd_in) {
|
|
|
|
s->fd_in_tag = io_add_watch_poll(s->fd_in, fd_chr_read_poll, fd_chr_read, chr);
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void fd_chr_close(struct CharDriverState *chr)
|
|
|
|
{
|
|
|
|
FDCharDriver *s = chr->opaque;
|
|
|
|
|
2013-03-05 17:51:19 +00:00
|
|
|
if (s->fd_in_tag) {
|
|
|
|
g_source_remove(s->fd_in_tag);
|
|
|
|
s->fd_in_tag = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (s->fd_in) {
|
|
|
|
g_io_channel_unref(s->fd_in);
|
|
|
|
}
|
|
|
|
if (s->fd_out) {
|
|
|
|
g_io_channel_unref(s->fd_out);
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
|
2011-08-21 03:09:37 +00:00
|
|
|
g_free(s);
|
2011-11-19 09:22:43 +00:00
|
|
|
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* open a character device to a unix fd */
|
|
|
|
static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out)
|
|
|
|
{
|
|
|
|
CharDriverState *chr;
|
|
|
|
FDCharDriver *s;
|
|
|
|
|
2011-08-21 03:09:37 +00:00
|
|
|
chr = g_malloc0(sizeof(CharDriverState));
|
|
|
|
s = g_malloc0(sizeof(FDCharDriver));
|
2013-03-05 17:51:19 +00:00
|
|
|
s->fd_in = io_channel_from_fd(fd_in);
|
|
|
|
s->fd_out = io_channel_from_fd(fd_out);
|
2013-03-05 17:51:23 +00:00
|
|
|
fcntl(fd_out, F_SETFL, O_NONBLOCK);
|
2013-03-05 17:51:19 +00:00
|
|
|
s->chr = chr;
|
2008-10-31 18:49:55 +00:00
|
|
|
chr->opaque = s;
|
2013-03-05 17:51:23 +00:00
|
|
|
chr->chr_add_watch = fd_chr_add_watch;
|
2008-10-31 18:49:55 +00:00
|
|
|
chr->chr_write = fd_chr_write;
|
|
|
|
chr->chr_update_read_handler = fd_chr_update_read_handler;
|
|
|
|
chr->chr_close = fd_chr_close;
|
|
|
|
|
2013-03-26 10:07:54 +00:00
|
|
|
qemu_chr_be_generic_open(chr);
|
2008-10-31 18:49:55 +00:00
|
|
|
|
|
|
|
return chr;
|
|
|
|
}
|
|
|
|
|
2013-02-25 10:50:55 +00:00
|
|
|
static CharDriverState *qemu_chr_open_pipe(ChardevHostdev *opts)
|
2008-10-31 18:49:55 +00:00
|
|
|
{
|
|
|
|
int fd_in, fd_out;
|
|
|
|
char filename_in[256], filename_out[256];
|
2013-02-25 10:50:55 +00:00
|
|
|
const char *filename = opts->device;
|
2009-09-10 08:58:36 +00:00
|
|
|
|
|
|
|
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 14:09:08 +00:00
|
|
|
return NULL;
|
2009-09-10 08:58:36 +00:00
|
|
|
}
|
2008-10-31 18:49:55 +00:00
|
|
|
|
|
|
|
snprintf(filename_in, 256, "%s.in", filename);
|
|
|
|
snprintf(filename_out, 256, "%s.out", filename);
|
2009-12-02 11:24:42 +00:00
|
|
|
TFR(fd_in = qemu_open(filename_in, O_RDWR | O_BINARY));
|
|
|
|
TFR(fd_out = qemu_open(filename_out, O_RDWR | O_BINARY));
|
2008-10-31 18:49:55 +00:00
|
|
|
if (fd_in < 0 || fd_out < 0) {
|
|
|
|
if (fd_in >= 0)
|
|
|
|
close(fd_in);
|
|
|
|
if (fd_out >= 0)
|
|
|
|
close(fd_out);
|
2012-02-07 14:09:09 +00:00
|
|
|
TFR(fd_in = fd_out = qemu_open(filename, O_RDWR | O_BINARY));
|
2012-02-07 14:09:10 +00:00
|
|
|
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 14:09:08 +00:00
|
|
|
return NULL;
|
2012-02-07 14:09:10 +00:00
|
|
|
}
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
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 14:09:08 +00:00
|
|
|
return qemu_chr_open_fd(fd_in, fd_out);
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* init terminal so that we can grab keys */
|
|
|
|
static struct termios oldtty;
|
|
|
|
static int old_fd0_flags;
|
2010-12-23 12:42:50 +00:00
|
|
|
static bool stdio_allow_signal;
|
2008-10-31 18:49:55 +00:00
|
|
|
|
|
|
|
static void term_exit(void)
|
|
|
|
{
|
|
|
|
tcsetattr (0, TCSANOW, &oldtty);
|
|
|
|
fcntl(0, F_SETFL, old_fd0_flags);
|
|
|
|
}
|
|
|
|
|
2010-12-23 12:42:50 +00:00
|
|
|
static void qemu_chr_set_echo_stdio(CharDriverState *chr, bool echo)
|
2008-10-31 18:49:55 +00:00
|
|
|
{
|
|
|
|
struct termios tty;
|
|
|
|
|
2010-12-23 12:42:49 +00:00
|
|
|
tty = oldtty;
|
2010-12-23 12:42:50 +00:00
|
|
|
if (!echo) {
|
|
|
|
tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
|
2008-10-31 18:49:55 +00:00
|
|
|
|INLCR|IGNCR|ICRNL|IXON);
|
2010-12-23 12:42:50 +00:00
|
|
|
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;
|
|
|
|
}
|
2008-10-31 18:49:55 +00:00
|
|
|
/* if graphical mode, we allow Ctrl-C handling */
|
2010-12-23 12:42:50 +00:00
|
|
|
if (!stdio_allow_signal)
|
2008-10-31 18:49:55 +00:00
|
|
|
tty.c_lflag &= ~ISIG;
|
|
|
|
|
|
|
|
tcsetattr (0, TCSANOW, &tty);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void qemu_chr_close_stdio(struct CharDriverState *chr)
|
|
|
|
{
|
|
|
|
term_exit();
|
|
|
|
fd_chr_close(chr);
|
|
|
|
}
|
|
|
|
|
2013-02-21 11:34:58 +00:00
|
|
|
static CharDriverState *qemu_chr_open_stdio(ChardevStdio *opts)
|
2008-10-31 18:49:55 +00:00
|
|
|
{
|
|
|
|
CharDriverState *chr;
|
|
|
|
|
disallow -daemonize usage of stdio (curses display, -nographic, -serial stdio etc)
Curses display requires stdin/out to stay on the terminal,
so -daemonize makes no sense in this case. Instead of
leaving display uninitialized like is done since 995ee2bf469de6bb,
explicitly detect this case earlier and error out.
-nographic can actually be used with -daemonize, by redirecting
everything to a null device, but the problem is that according
to documentation and historical behavour, -nographic redirects
guest ports to stdin/out, which, again, makes no sense in case
of -daemonize. Since -nographic is a legacy option, don't bother
fixing this case (to allow -nographic and -daemonize by redirecting
guest ports to null instead of stdin/out in this case), but disallow
it completely instead, to stop garbling host terminal.
If no display display needed and user wants to use -nographic,
the right way to go is to use
-serial null -parallel null -monitor none -display none -vga none
instead of -nographic.
Also prevent the same issue -- it was possible to get garbled
host tty after
-nographic -daemonize
and it is still possible to have it by using
-serial stdio -daemonize
Fix this by disallowing opening stdio chardev when -daemonize
is specified.
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2012-12-30 08:48:14 +00:00
|
|
|
if (is_daemonized()) {
|
|
|
|
error_report("cannot use stdio with -daemonize");
|
|
|
|
return NULL;
|
|
|
|
}
|
2013-03-05 17:51:17 +00:00
|
|
|
old_fd0_flags = fcntl(0, F_GETFL);
|
|
|
|
tcgetattr (0, &oldtty);
|
|
|
|
fcntl(0, F_SETFL, O_NONBLOCK);
|
|
|
|
atexit(term_exit);
|
2010-12-23 12:42:49 +00:00
|
|
|
|
2008-10-31 18:49:55 +00:00
|
|
|
chr = qemu_chr_open_fd(0, 1);
|
|
|
|
chr->chr_close = qemu_chr_close_stdio;
|
2010-12-23 12:42:50 +00:00
|
|
|
chr->chr_set_echo = qemu_chr_set_echo_stdio;
|
2013-02-21 11:34:58 +00:00
|
|
|
stdio_allow_signal = display_type != DT_NOGRAPHIC;
|
|
|
|
if (opts->has_signal) {
|
|
|
|
stdio_allow_signal = opts->signal;
|
|
|
|
}
|
2011-08-15 16:17:35 +00:00
|
|
|
qemu_chr_fe_set_echo(chr, false);
|
2008-10-31 18:49:55 +00:00
|
|
|
|
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 14:09:08 +00:00
|
|
|
return chr;
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef __sun__
|
|
|
|
/* Once Solaris has openpty(), this is going to be removed. */
|
2009-04-13 16:31:01 +00:00
|
|
|
static int openpty(int *amaster, int *aslave, char *name,
|
|
|
|
struct termios *termp, struct winsize *winp)
|
2008-10-31 18:49:55 +00:00
|
|
|
{
|
|
|
|
const char *slave;
|
|
|
|
int mfd = -1, sfd = -1;
|
|
|
|
|
|
|
|
*amaster = *aslave = -1;
|
|
|
|
|
|
|
|
mfd = open("/dev/ptmx", O_RDWR | O_NOCTTY);
|
|
|
|
if (mfd < 0)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
if (grantpt(mfd) == -1 || unlockpt(mfd) == -1)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
if ((slave = ptsname(mfd)) == NULL)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
if ((sfd = open(slave, O_RDONLY | O_NOCTTY)) == -1)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
if (ioctl(sfd, I_PUSH, "ptem") == -1 ||
|
|
|
|
(termp != NULL && tcgetattr(sfd, termp) < 0))
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
if (amaster)
|
|
|
|
*amaster = mfd;
|
|
|
|
if (aslave)
|
|
|
|
*aslave = sfd;
|
|
|
|
if (winp)
|
|
|
|
ioctl(sfd, TIOCSWINSZ, winp);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err:
|
|
|
|
if (sfd != -1)
|
|
|
|
close(sfd);
|
|
|
|
close(mfd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-04-13 16:31:01 +00:00
|
|
|
static void cfmakeraw (struct termios *termios_p)
|
2008-10-31 18:49:55 +00:00
|
|
|
{
|
|
|
|
termios_p->c_iflag &=
|
|
|
|
~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
|
|
|
|
termios_p->c_oflag &= ~OPOST;
|
|
|
|
termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
|
|
|
|
termios_p->c_cflag &= ~(CSIZE|PARENB);
|
|
|
|
termios_p->c_cflag |= CS8;
|
|
|
|
|
|
|
|
termios_p->c_cc[VMIN] = 0;
|
|
|
|
termios_p->c_cc[VTIME] = 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
|
2009-11-29 17:00:41 +00:00
|
|
|
|| defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
|
|
|
|
|| defined(__GLIBC__)
|
2008-10-31 18:49:55 +00:00
|
|
|
|
2012-12-19 15:35:42 +00:00
|
|
|
#define HAVE_CHARDEV_TTY 1
|
|
|
|
|
2008-10-31 18:49:55 +00:00
|
|
|
typedef struct {
|
2013-03-05 17:51:20 +00:00
|
|
|
GIOChannel *fd;
|
|
|
|
guint fd_tag;
|
2008-10-31 18:49:55 +00:00
|
|
|
int connected;
|
|
|
|
int read_bytes;
|
2013-03-05 17:51:26 +00:00
|
|
|
guint timer_tag;
|
2008-10-31 18:49:55 +00:00
|
|
|
} PtyCharDriver;
|
|
|
|
|
|
|
|
static void pty_chr_update_read_handler(CharDriverState *chr);
|
|
|
|
static void pty_chr_state(CharDriverState *chr, int connected);
|
|
|
|
|
2013-03-05 17:51:26 +00:00
|
|
|
static gboolean pty_chr_timer(gpointer opaque)
|
|
|
|
{
|
|
|
|
struct CharDriverState *chr = opaque;
|
|
|
|
PtyCharDriver *s = chr->opaque;
|
|
|
|
|
|
|
|
if (s->connected) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Next poll ... */
|
|
|
|
pty_chr_update_read_handler(chr);
|
|
|
|
|
|
|
|
out:
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-10-31 18:49:55 +00:00
|
|
|
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(chr);
|
|
|
|
return 0;
|
|
|
|
}
|
2013-03-29 16:39:50 +00:00
|
|
|
return io_channel_send(s->fd, buf, len);
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
|
2013-03-05 17:51:24 +00:00
|
|
|
static GSource *pty_chr_add_watch(CharDriverState *chr, GIOCondition cond)
|
|
|
|
{
|
|
|
|
PtyCharDriver *s = chr->opaque;
|
|
|
|
return g_io_create_watch(s->fd, cond);
|
|
|
|
}
|
|
|
|
|
2008-10-31 18:49:55 +00:00
|
|
|
static int pty_chr_read_poll(void *opaque)
|
|
|
|
{
|
|
|
|
CharDriverState *chr = opaque;
|
|
|
|
PtyCharDriver *s = chr->opaque;
|
|
|
|
|
2011-08-15 16:17:31 +00:00
|
|
|
s->read_bytes = qemu_chr_be_can_write(chr);
|
2008-10-31 18:49:55 +00:00
|
|
|
return s->read_bytes;
|
|
|
|
}
|
|
|
|
|
2013-03-05 17:51:20 +00:00
|
|
|
static gboolean pty_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
|
2008-10-31 18:49:55 +00:00
|
|
|
{
|
|
|
|
CharDriverState *chr = opaque;
|
|
|
|
PtyCharDriver *s = chr->opaque;
|
2013-03-05 17:51:20 +00:00
|
|
|
gsize size, len;
|
2009-11-03 14:29:54 +00:00
|
|
|
uint8_t buf[READ_BUF_LEN];
|
2013-03-05 17:51:20 +00:00
|
|
|
GIOStatus status;
|
2008-10-31 18:49:55 +00:00
|
|
|
|
|
|
|
len = sizeof(buf);
|
|
|
|
if (len > s->read_bytes)
|
|
|
|
len = s->read_bytes;
|
2013-04-19 15:32:08 +00:00
|
|
|
if (len == 0) {
|
|
|
|
return TRUE;
|
|
|
|
}
|
2013-03-05 17:51:20 +00:00
|
|
|
status = g_io_channel_read_chars(s->fd, (gchar *)buf, len, &size, NULL);
|
|
|
|
if (status != G_IO_STATUS_NORMAL) {
|
2008-10-31 18:49:55 +00:00
|
|
|
pty_chr_state(chr, 0);
|
2013-03-05 17:51:20 +00:00
|
|
|
return FALSE;
|
|
|
|
} else {
|
2008-10-31 18:49:55 +00:00
|
|
|
pty_chr_state(chr, 1);
|
2011-08-15 16:17:30 +00:00
|
|
|
qemu_chr_be_write(chr, buf, size);
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
2013-03-05 17:51:20 +00:00
|
|
|
return TRUE;
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void pty_chr_update_read_handler(CharDriverState *chr)
|
|
|
|
{
|
|
|
|
PtyCharDriver *s = chr->opaque;
|
2013-04-19 15:32:07 +00:00
|
|
|
GPollFD pfd;
|
2008-10-31 18:49:55 +00:00
|
|
|
|
2013-04-19 15:32:07 +00:00
|
|
|
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);
|
2013-03-05 17:51:20 +00:00
|
|
|
}
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void pty_chr_state(CharDriverState *chr, int connected)
|
|
|
|
{
|
|
|
|
PtyCharDriver *s = chr->opaque;
|
|
|
|
|
|
|
|
if (!connected) {
|
2013-04-19 15:32:06 +00:00
|
|
|
if (s->fd_tag) {
|
|
|
|
g_source_remove(s->fd_tag);
|
|
|
|
s->fd_tag = 0;
|
|
|
|
}
|
2008-10-31 18:49:55 +00:00
|
|
|
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. */
|
2013-03-05 17:51:26 +00:00
|
|
|
pty_chr_rearm_timer(chr, 1000);
|
2008-10-31 18:49:55 +00:00
|
|
|
} else {
|
2013-04-19 15:32:07 +00:00
|
|
|
if (s->timer_tag) {
|
|
|
|
g_source_remove(s->timer_tag);
|
|
|
|
s->timer_tag = 0;
|
|
|
|
}
|
|
|
|
if (!s->connected) {
|
2013-03-26 10:07:54 +00:00
|
|
|
qemu_chr_be_generic_open(chr);
|
2013-04-19 15:32:07 +00:00
|
|
|
s->connected = 1;
|
|
|
|
s->fd_tag = io_add_watch_poll(s->fd, pty_chr_read_poll, pty_chr_read, chr);
|
|
|
|
}
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void pty_chr_close(struct CharDriverState *chr)
|
|
|
|
{
|
|
|
|
PtyCharDriver *s = chr->opaque;
|
2013-03-05 17:51:20 +00:00
|
|
|
int fd;
|
2008-10-31 18:49:55 +00:00
|
|
|
|
2013-03-05 17:51:20 +00:00
|
|
|
if (s->fd_tag) {
|
|
|
|
g_source_remove(s->fd_tag);
|
2013-04-19 15:32:06 +00:00
|
|
|
s->fd_tag = 0;
|
2013-03-05 17:51:20 +00:00
|
|
|
}
|
|
|
|
fd = g_io_channel_unix_get_fd(s->fd);
|
|
|
|
g_io_channel_unref(s->fd);
|
|
|
|
close(fd);
|
2013-03-05 17:51:26 +00:00
|
|
|
if (s->timer_tag) {
|
|
|
|
g_source_remove(s->timer_tag);
|
2013-04-19 15:32:06 +00:00
|
|
|
s->timer_tag = 0;
|
2013-03-05 17:51:26 +00:00
|
|
|
}
|
2011-08-21 03:09:37 +00:00
|
|
|
g_free(s);
|
2011-11-19 09:22:43 +00:00
|
|
|
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
|
2013-02-25 09:16:46 +00:00
|
|
|
static CharDriverState *qemu_chr_open_pty(const char *id,
|
|
|
|
ChardevReturn *ret)
|
2008-10-31 18:49:55 +00:00
|
|
|
{
|
|
|
|
CharDriverState *chr;
|
|
|
|
PtyCharDriver *s;
|
|
|
|
struct termios tty;
|
2013-02-25 09:16:46 +00:00
|
|
|
int master_fd, slave_fd;
|
2009-03-07 20:06:23 +00:00
|
|
|
#if defined(__OpenBSD__) || defined(__DragonFly__)
|
2008-10-31 18:49:55 +00:00
|
|
|
char pty_name[PATH_MAX];
|
|
|
|
#define q_ptsname(x) pty_name
|
|
|
|
#else
|
|
|
|
char *pty_name = NULL;
|
|
|
|
#define q_ptsname(x) ptsname(x)
|
|
|
|
#endif
|
|
|
|
|
2011-11-11 09:40:05 +00:00
|
|
|
if (openpty(&master_fd, &slave_fd, pty_name, NULL, NULL) < 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 14:09:08 +00:00
|
|
|
return NULL;
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Set raw attributes on the pty. */
|
2008-11-12 15:00:36 +00:00
|
|
|
tcgetattr(slave_fd, &tty);
|
2008-10-31 18:49:55 +00:00
|
|
|
cfmakeraw(&tty);
|
|
|
|
tcsetattr(slave_fd, TCSAFLUSH, &tty);
|
|
|
|
close(slave_fd);
|
|
|
|
|
2011-11-11 09:40:05 +00:00
|
|
|
chr = g_malloc0(sizeof(CharDriverState));
|
|
|
|
|
2013-02-25 09:16:46 +00:00
|
|
|
chr->filename = g_strdup_printf("pty:%s", q_ptsname(master_fd));
|
|
|
|
ret->pty = g_strdup(q_ptsname(master_fd));
|
|
|
|
ret->has_pty = true;
|
2012-12-21 04:26:38 +00:00
|
|
|
|
2013-02-25 09:16:46 +00:00
|
|
|
fprintf(stderr, "char device redirected to %s (label %s)\n",
|
|
|
|
q_ptsname(master_fd), id);
|
2008-10-31 18:49:55 +00:00
|
|
|
|
2011-11-11 09:40:05 +00:00
|
|
|
s = g_malloc0(sizeof(PtyCharDriver));
|
2008-10-31 18:49:55 +00:00
|
|
|
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;
|
2013-03-05 17:51:24 +00:00
|
|
|
chr->chr_add_watch = pty_chr_add_watch;
|
2008-10-31 18:49:55 +00:00
|
|
|
|
2013-03-05 17:51:20 +00:00
|
|
|
s->fd = io_channel_from_fd(master_fd);
|
2013-03-05 17:51:26 +00:00
|
|
|
s->timer_tag = 0;
|
2008-10-31 18:49:55 +00:00
|
|
|
|
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 14:09:08 +00:00
|
|
|
return chr;
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2009-10-26 15:10:10 +00:00
|
|
|
#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
|
2008-10-31 18:49:55 +00:00
|
|
|
spd = B115200;
|
2009-10-26 15:10:10 +00:00
|
|
|
} while (0);
|
2008-10-31 18:49:55 +00:00
|
|
|
|
|
|
|
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;
|
2013-03-05 17:51:19 +00:00
|
|
|
tty_serial_init(g_io_channel_unix_get_fd(s->fd_in),
|
|
|
|
ssp->speed, ssp->parity,
|
2008-10-31 18:49:55 +00:00
|
|
|
ssp->data_bits, ssp->stop_bits);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case CHR_IOCTL_SERIAL_SET_BREAK:
|
|
|
|
{
|
|
|
|
int enable = *(int *)arg;
|
2013-03-05 17:51:19 +00:00
|
|
|
if (enable) {
|
|
|
|
tcsendbreak(g_io_channel_unix_get_fd(s->fd_in), 1);
|
|
|
|
}
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case CHR_IOCTL_SERIAL_GET_TIOCM:
|
|
|
|
{
|
|
|
|
int sarg = 0;
|
|
|
|
int *targ = (int *)arg;
|
2013-03-05 17:51:19 +00:00
|
|
|
ioctl(g_io_channel_unix_get_fd(s->fd_in), TIOCMGET, &sarg);
|
2008-10-31 18:49:55 +00:00
|
|
|
*targ = 0;
|
2009-02-08 14:46:17 +00:00
|
|
|
if (sarg & TIOCM_CTS)
|
2008-10-31 18:49:55 +00:00
|
|
|
*targ |= CHR_TIOCM_CTS;
|
2009-02-08 14:46:17 +00:00
|
|
|
if (sarg & TIOCM_CAR)
|
2008-10-31 18:49:55 +00:00
|
|
|
*targ |= CHR_TIOCM_CAR;
|
2009-02-08 14:46:17 +00:00
|
|
|
if (sarg & TIOCM_DSR)
|
2008-10-31 18:49:55 +00:00
|
|
|
*targ |= CHR_TIOCM_DSR;
|
2009-02-08 14:46:17 +00:00
|
|
|
if (sarg & TIOCM_RI)
|
2008-10-31 18:49:55 +00:00
|
|
|
*targ |= CHR_TIOCM_RI;
|
2009-02-08 14:46:17 +00:00
|
|
|
if (sarg & TIOCM_DTR)
|
2008-10-31 18:49:55 +00:00
|
|
|
*targ |= CHR_TIOCM_DTR;
|
2009-02-08 14:46:17 +00:00
|
|
|
if (sarg & TIOCM_RTS)
|
2008-10-31 18:49:55 +00:00
|
|
|
*targ |= CHR_TIOCM_RTS;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case CHR_IOCTL_SERIAL_SET_TIOCM:
|
|
|
|
{
|
|
|
|
int sarg = *(int *)arg;
|
|
|
|
int targ = 0;
|
2013-03-05 17:51:19 +00:00
|
|
|
ioctl(g_io_channel_unix_get_fd(s->fd_in), TIOCMGET, &targ);
|
2009-02-08 14:46:17 +00:00
|
|
|
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)
|
2008-10-31 18:49:55 +00:00
|
|
|
targ |= TIOCM_DTR;
|
2009-02-08 14:46:17 +00:00
|
|
|
if (sarg & CHR_TIOCM_RTS)
|
2008-10-31 18:49:55 +00:00
|
|
|
targ |= TIOCM_RTS;
|
2013-03-05 17:51:19 +00:00
|
|
|
ioctl(g_io_channel_unix_get_fd(s->fd_in), TIOCMSET, &targ);
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-02-11 01:27:17 +00:00
|
|
|
static void qemu_chr_close_tty(CharDriverState *chr)
|
|
|
|
{
|
|
|
|
FDCharDriver *s = chr->opaque;
|
|
|
|
int fd = -1;
|
|
|
|
|
|
|
|
if (s) {
|
2013-03-05 17:51:19 +00:00
|
|
|
fd = g_io_channel_unix_get_fd(s->fd_in);
|
2010-02-11 01:27:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fd_chr_close(chr);
|
|
|
|
|
|
|
|
if (fd >= 0) {
|
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-19 12:50:29 +00:00
|
|
|
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;
|
|
|
|
}
|
2008-10-31 18:49:55 +00:00
|
|
|
#endif /* __linux__ || __sun__ */
|
|
|
|
|
|
|
|
#if defined(__linux__)
|
2012-12-19 15:35:42 +00:00
|
|
|
|
|
|
|
#define HAVE_CHARDEV_PARPORT 1
|
|
|
|
|
2008-10-31 18:49:55 +00:00
|
|
|
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);
|
2011-08-21 03:09:37 +00:00
|
|
|
g_free(drv);
|
2011-11-19 09:22:43 +00:00
|
|
|
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
|
2013-01-10 13:20:58 +00:00
|
|
|
static CharDriverState *qemu_chr_open_pp_fd(int fd)
|
2008-10-31 18:49:55 +00:00
|
|
|
{
|
|
|
|
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 14:09:08 +00:00
|
|
|
return NULL;
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
|
2011-08-21 03:09:37 +00:00
|
|
|
drv = g_malloc0(sizeof(ParallelCharDriver));
|
2008-10-31 18:49:55 +00:00
|
|
|
drv->fd = fd;
|
|
|
|
drv->mode = IEEE1284_MODE_COMPAT;
|
|
|
|
|
2011-08-21 03:09:37 +00:00
|
|
|
chr = g_malloc0(sizeof(CharDriverState));
|
2008-10-31 18:49:55 +00:00
|
|
|
chr->chr_write = null_chr_write;
|
|
|
|
chr->chr_ioctl = pp_ioctl;
|
|
|
|
chr->chr_close = pp_close;
|
|
|
|
chr->opaque = drv;
|
|
|
|
|
2013-03-26 10:07:54 +00:00
|
|
|
qemu_chr_be_generic_open(chr);
|
2008-10-31 18:49:55 +00:00
|
|
|
|
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 14:09:08 +00:00
|
|
|
return chr;
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
#endif /* __linux__ */
|
|
|
|
|
2009-11-29 17:00:41 +00:00
|
|
|
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
|
2012-12-19 15:35:42 +00:00
|
|
|
|
|
|
|
#define HAVE_CHARDEV_PARPORT 1
|
|
|
|
|
2008-11-22 20:49:12 +00:00
|
|
|
static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
|
|
|
|
{
|
2011-02-23 18:09:16 +00:00
|
|
|
int fd = (int)(intptr_t)chr->opaque;
|
2008-11-22 20:49:12 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-01-10 13:20:58 +00:00
|
|
|
static CharDriverState *qemu_chr_open_pp_fd(int fd)
|
2008-11-22 20:49:12 +00:00
|
|
|
{
|
|
|
|
CharDriverState *chr;
|
|
|
|
|
2011-08-21 03:09:37 +00:00
|
|
|
chr = g_malloc0(sizeof(CharDriverState));
|
2011-02-23 18:09:16 +00:00
|
|
|
chr->opaque = (void *)(intptr_t)fd;
|
2008-11-22 20:49:12 +00:00
|
|
|
chr->chr_write = null_chr_write;
|
|
|
|
chr->chr_ioctl = pp_ioctl;
|
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 14:09:08 +00:00
|
|
|
return chr;
|
2008-11-22 20:49:12 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-10-31 18:49:55 +00:00
|
|
|
#else /* _WIN32 */
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
int max_size;
|
|
|
|
HANDLE hcom, hrecv, hsend;
|
|
|
|
OVERLAPPED orecv, osend;
|
|
|
|
BOOL fpipe;
|
|
|
|
DWORD len;
|
|
|
|
} WinCharState;
|
|
|
|
|
2011-10-06 14:37:51 +00:00
|
|
|
typedef struct {
|
|
|
|
HANDLE hStdIn;
|
|
|
|
HANDLE hInputReadyEvent;
|
|
|
|
HANDLE hInputDoneEvent;
|
|
|
|
HANDLE hInputThread;
|
|
|
|
uint8_t win_stdio_buf;
|
|
|
|
} WinStdioCharState;
|
|
|
|
|
2008-10-31 18:49:55 +00:00
|
|
|
#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);
|
2009-08-11 15:57:48 +00:00
|
|
|
|
2011-11-19 09:22:43 +00:00
|
|
|
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2011-08-15 16:17:31 +00:00
|
|
|
s->max_size = qemu_chr_be_can_write(chr);
|
2008-10-31 18:49:55 +00:00
|
|
|
return s->max_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void win_chr_readfile(CharDriverState *chr)
|
|
|
|
{
|
|
|
|
WinCharState *s = chr->opaque;
|
|
|
|
int ret, err;
|
2009-11-03 14:29:54 +00:00
|
|
|
uint8_t buf[READ_BUF_LEN];
|
2008-10-31 18:49:55 +00:00
|
|
|
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) {
|
2011-08-15 16:17:30 +00:00
|
|
|
qemu_chr_be_write(chr, buf, size);
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-12-19 12:50:29 +00:00
|
|
|
static CharDriverState *qemu_chr_open_win_path(const char *filename)
|
2008-10-31 18:49:55 +00:00
|
|
|
{
|
|
|
|
CharDriverState *chr;
|
|
|
|
WinCharState *s;
|
|
|
|
|
2011-08-21 03:09:37 +00:00
|
|
|
chr = g_malloc0(sizeof(CharDriverState));
|
|
|
|
s = g_malloc0(sizeof(WinCharState));
|
2008-10-31 18:49:55 +00:00
|
|
|
chr->opaque = s;
|
|
|
|
chr->chr_write = win_chr_write;
|
|
|
|
chr->chr_close = win_chr_close;
|
|
|
|
|
|
|
|
if (win_chr_init(chr, filename) < 0) {
|
2011-10-07 05:38:46 +00:00
|
|
|
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 14:09:08 +00:00
|
|
|
return NULL;
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
2013-03-26 10:07:54 +00:00
|
|
|
qemu_chr_be_generic_open(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 14:09:08 +00:00
|
|
|
return chr;
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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[256];
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-02-25 10:50:55 +00:00
|
|
|
static CharDriverState *qemu_chr_open_pipe(ChardevHostdev *opts)
|
2008-10-31 18:49:55 +00:00
|
|
|
{
|
2013-02-25 10:50:55 +00:00
|
|
|
const char *filename = opts->device;
|
2008-10-31 18:49:55 +00:00
|
|
|
CharDriverState *chr;
|
|
|
|
WinCharState *s;
|
|
|
|
|
2011-08-21 03:09:37 +00:00
|
|
|
chr = g_malloc0(sizeof(CharDriverState));
|
|
|
|
s = g_malloc0(sizeof(WinCharState));
|
2008-10-31 18:49:55 +00:00
|
|
|
chr->opaque = s;
|
|
|
|
chr->chr_write = win_chr_write;
|
|
|
|
chr->chr_close = win_chr_close;
|
|
|
|
|
|
|
|
if (win_chr_pipe_init(chr, filename) < 0) {
|
2011-10-07 05:38:46 +00:00
|
|
|
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 14:09:08 +00:00
|
|
|
return NULL;
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
2013-03-26 10:07:54 +00:00
|
|
|
qemu_chr_be_generic_open(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 14:09:08 +00:00
|
|
|
return chr;
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
|
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 14:09:08 +00:00
|
|
|
static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out)
|
2008-10-31 18:49:55 +00:00
|
|
|
{
|
|
|
|
CharDriverState *chr;
|
|
|
|
WinCharState *s;
|
|
|
|
|
2011-08-21 03:09:37 +00:00
|
|
|
chr = g_malloc0(sizeof(CharDriverState));
|
|
|
|
s = g_malloc0(sizeof(WinCharState));
|
2008-10-31 18:49:55 +00:00
|
|
|
s->hcom = fd_out;
|
|
|
|
chr->opaque = s;
|
|
|
|
chr->chr_write = win_chr_write;
|
2013-03-26 10:07:54 +00:00
|
|
|
qemu_chr_be_generic_open(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 14:09:08 +00:00
|
|
|
return chr;
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
|
2013-02-25 10:48:06 +00:00
|
|
|
static CharDriverState *qemu_chr_open_win_con(void)
|
2008-10-31 18:49:55 +00:00
|
|
|
{
|
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 14:09:08 +00:00
|
|
|
return qemu_chr_open_win_file(GetStdHandle(STD_OUTPUT_HANDLE));
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
|
2011-10-06 14:37:51 +00:00
|
|
|
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, sizeof(buf) / sizeof(*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);
|
|
|
|
}
|
|
|
|
|
2013-02-21 11:34:58 +00:00
|
|
|
static CharDriverState *qemu_chr_open_stdio(ChardevStdio *opts)
|
2011-10-06 14:37:51 +00:00
|
|
|
{
|
|
|
|
CharDriverState *chr;
|
|
|
|
WinStdioCharState *stdio;
|
|
|
|
DWORD dwMode;
|
|
|
|
int is_console = 0;
|
|
|
|
|
|
|
|
chr = g_malloc0(sizeof(CharDriverState));
|
|
|
|
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;
|
|
|
|
|
2013-03-05 17:51:17 +00:00
|
|
|
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");
|
2011-10-06 14:37:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
dwMode |= ENABLE_LINE_INPUT;
|
|
|
|
|
2013-03-05 17:51:17 +00:00
|
|
|
if (is_console) {
|
2011-10-06 14:37:51 +00:00
|
|
|
/* 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 14:09:08 +00:00
|
|
|
return chr;
|
2011-10-06 14:37:51 +00:00
|
|
|
}
|
2008-10-31 18:49:55 +00:00
|
|
|
#endif /* !_WIN32 */
|
|
|
|
|
2013-03-05 17:51:31 +00:00
|
|
|
|
2008-10-31 18:49:55 +00:00
|
|
|
/***********************************************************/
|
|
|
|
/* UDP Net console */
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
int fd;
|
2013-03-05 17:51:21 +00:00
|
|
|
GIOChannel *chan;
|
|
|
|
guint tag;
|
2009-11-03 14:29:54 +00:00
|
|
|
uint8_t buf[READ_BUF_LEN];
|
2008-10-31 18:49:55 +00:00
|
|
|
int bufcnt;
|
|
|
|
int bufptr;
|
|
|
|
int max_size;
|
|
|
|
} NetCharDriver;
|
|
|
|
|
|
|
|
static int udp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
|
|
|
|
{
|
|
|
|
NetCharDriver *s = chr->opaque;
|
2013-03-05 17:51:21 +00:00
|
|
|
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;
|
|
|
|
}
|
2008-10-31 18:49:55 +00:00
|
|
|
|
2013-03-05 17:51:21 +00:00
|
|
|
return bytes_written;
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int udp_chr_read_poll(void *opaque)
|
|
|
|
{
|
|
|
|
CharDriverState *chr = opaque;
|
|
|
|
NetCharDriver *s = chr->opaque;
|
|
|
|
|
2011-08-15 16:17:31 +00:00
|
|
|
s->max_size = qemu_chr_be_can_write(chr);
|
2008-10-31 18:49:55 +00:00
|
|
|
|
|
|
|
/* If there were any stray characters in the queue process them
|
|
|
|
* first
|
|
|
|
*/
|
|
|
|
while (s->max_size > 0 && s->bufptr < s->bufcnt) {
|
2011-08-15 16:17:30 +00:00
|
|
|
qemu_chr_be_write(chr, &s->buf[s->bufptr], 1);
|
2008-10-31 18:49:55 +00:00
|
|
|
s->bufptr++;
|
2011-08-15 16:17:31 +00:00
|
|
|
s->max_size = qemu_chr_be_can_write(chr);
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
return s->max_size;
|
|
|
|
}
|
|
|
|
|
2013-03-05 17:51:21 +00:00
|
|
|
static gboolean udp_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
|
2008-10-31 18:49:55 +00:00
|
|
|
{
|
|
|
|
CharDriverState *chr = opaque;
|
|
|
|
NetCharDriver *s = chr->opaque;
|
2013-03-05 17:51:21 +00:00
|
|
|
gsize bytes_read = 0;
|
|
|
|
GIOStatus status;
|
2008-10-31 18:49:55 +00:00
|
|
|
|
2013-04-19 15:32:08 +00:00
|
|
|
if (s->max_size == 0) {
|
|
|
|
return TRUE;
|
|
|
|
}
|
2013-03-05 17:51:21 +00:00
|
|
|
status = g_io_channel_read_chars(s->chan, (gchar *)s->buf, sizeof(s->buf),
|
|
|
|
&bytes_read, NULL);
|
|
|
|
s->bufcnt = bytes_read;
|
2008-10-31 18:49:55 +00:00
|
|
|
s->bufptr = s->bufcnt;
|
2013-03-05 17:51:21 +00:00
|
|
|
if (status != G_IO_STATUS_NORMAL) {
|
2013-04-19 15:32:08 +00:00
|
|
|
if (s->tag) {
|
|
|
|
g_source_remove(s->tag);
|
|
|
|
s->tag = 0;
|
|
|
|
}
|
2013-03-05 17:51:21 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
2008-10-31 18:49:55 +00:00
|
|
|
|
|
|
|
s->bufptr = 0;
|
|
|
|
while (s->max_size > 0 && s->bufptr < s->bufcnt) {
|
2011-08-15 16:17:30 +00:00
|
|
|
qemu_chr_be_write(chr, &s->buf[s->bufptr], 1);
|
2008-10-31 18:49:55 +00:00
|
|
|
s->bufptr++;
|
2011-08-15 16:17:31 +00:00
|
|
|
s->max_size = qemu_chr_be_can_write(chr);
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
2013-03-05 17:51:21 +00:00
|
|
|
|
|
|
|
return TRUE;
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void udp_chr_update_read_handler(CharDriverState *chr)
|
|
|
|
{
|
|
|
|
NetCharDriver *s = chr->opaque;
|
|
|
|
|
2013-03-05 17:51:21 +00:00
|
|
|
if (s->tag) {
|
|
|
|
g_source_remove(s->tag);
|
|
|
|
s->tag = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (s->chan) {
|
|
|
|
s->tag = io_add_watch_poll(s->chan, udp_chr_read_poll, udp_chr_read, chr);
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-28 17:58:14 +00:00
|
|
|
static void udp_chr_close(CharDriverState *chr)
|
|
|
|
{
|
|
|
|
NetCharDriver *s = chr->opaque;
|
2013-03-05 17:51:21 +00:00
|
|
|
if (s->tag) {
|
|
|
|
g_source_remove(s->tag);
|
2013-04-19 15:32:06 +00:00
|
|
|
s->tag = 0;
|
2013-03-05 17:51:21 +00:00
|
|
|
}
|
|
|
|
if (s->chan) {
|
|
|
|
g_io_channel_unref(s->chan);
|
2009-03-28 17:58:14 +00:00
|
|
|
closesocket(s->fd);
|
|
|
|
}
|
2011-08-21 03:09:37 +00:00
|
|
|
g_free(s);
|
2011-11-19 09:22:43 +00:00
|
|
|
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
|
2009-03-28 17:58:14 +00:00
|
|
|
}
|
|
|
|
|
2013-02-27 13:10:47 +00:00
|
|
|
static CharDriverState *qemu_chr_open_udp_fd(int fd)
|
2008-10-31 18:49:55 +00:00
|
|
|
{
|
|
|
|
CharDriverState *chr = NULL;
|
|
|
|
NetCharDriver *s = NULL;
|
|
|
|
|
2011-08-21 03:09:37 +00:00
|
|
|
chr = g_malloc0(sizeof(CharDriverState));
|
|
|
|
s = g_malloc0(sizeof(NetCharDriver));
|
2008-10-31 18:49:55 +00:00
|
|
|
|
|
|
|
s->fd = fd;
|
2013-03-05 17:51:21 +00:00
|
|
|
s->chan = io_channel_from_socket(s->fd);
|
2008-10-31 18:49:55 +00:00
|
|
|
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;
|
2009-03-28 17:58:14 +00:00
|
|
|
chr->chr_close = udp_chr_close;
|
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 14:09:08 +00:00
|
|
|
return chr;
|
2013-02-27 13:10:47 +00:00
|
|
|
}
|
2008-10-31 18:49:55 +00:00
|
|
|
|
2013-02-27 13:10:47 +00:00
|
|
|
static CharDriverState *qemu_chr_open_udp(QemuOpts *opts)
|
|
|
|
{
|
|
|
|
Error *local_err = NULL;
|
|
|
|
int fd = -1;
|
|
|
|
|
|
|
|
fd = inet_dgram_opts(opts, &local_err);
|
|
|
|
if (fd < 0) {
|
|
|
|
return NULL;
|
2011-06-01 11:29:11 +00:00
|
|
|
}
|
2013-02-27 13:10:47 +00:00
|
|
|
return qemu_chr_open_udp_fd(fd);
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************/
|
|
|
|
/* TCP Net console */
|
|
|
|
|
|
|
|
typedef struct {
|
2013-03-05 17:51:22 +00:00
|
|
|
|
|
|
|
GIOChannel *chan, *listen_chan;
|
|
|
|
guint tag, listen_tag;
|
2008-10-31 18:49:55 +00:00
|
|
|
int fd, listen_fd;
|
|
|
|
int connected;
|
|
|
|
int max_size;
|
|
|
|
int do_telnetopt;
|
|
|
|
int do_nodelay;
|
|
|
|
int is_unix;
|
2009-07-22 08:11:39 +00:00
|
|
|
int msgfd;
|
2008-10-31 18:49:55 +00:00
|
|
|
} TCPCharDriver;
|
|
|
|
|
2013-03-05 17:51:22 +00:00
|
|
|
static gboolean tcp_chr_accept(GIOChannel *chan, GIOCondition cond, void *opaque);
|
2008-10-31 18:49:55 +00:00
|
|
|
|
|
|
|
static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
|
|
|
|
{
|
|
|
|
TCPCharDriver *s = chr->opaque;
|
|
|
|
if (s->connected) {
|
2013-03-29 16:39:50 +00:00
|
|
|
return io_channel_send(s->chan, buf, len);
|
2012-09-05 18:52:49 +00:00
|
|
|
} else {
|
2012-09-12 19:34:07 +00:00
|
|
|
/* XXX: indicate an error ? */
|
2012-09-05 18:52:49 +00:00
|
|
|
return len;
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int tcp_chr_read_poll(void *opaque)
|
|
|
|
{
|
|
|
|
CharDriverState *chr = opaque;
|
|
|
|
TCPCharDriver *s = chr->opaque;
|
|
|
|
if (!s->connected)
|
|
|
|
return 0;
|
2011-08-15 16:17:31 +00:00
|
|
|
s->max_size = qemu_chr_be_can_write(chr);
|
2008-10-31 18:49:55 +00:00
|
|
|
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 */
|
2011-11-19 09:22:43 +00:00
|
|
|
qemu_chr_be_event(chr, CHR_EVENT_BREAK);
|
2008-10-31 18:49:55 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2009-07-22 08:11:39 +00:00
|
|
|
static int tcp_get_msgfd(CharDriverState *chr)
|
|
|
|
{
|
|
|
|
TCPCharDriver *s = chr->opaque;
|
2010-04-16 15:25:23 +00:00
|
|
|
int fd = s->msgfd;
|
|
|
|
s->msgfd = -1;
|
|
|
|
return fd;
|
2009-07-22 08:11:39 +00:00
|
|
|
}
|
|
|
|
|
2009-07-27 19:55:25 +00:00
|
|
|
#ifndef _WIN32
|
2009-07-22 08:11:39 +00:00
|
|
|
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;
|
|
|
|
|
|
|
|
if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)) ||
|
|
|
|
cmsg->cmsg_level != SOL_SOCKET ||
|
|
|
|
cmsg->cmsg_type != SCM_RIGHTS)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
fd = *((int *)CMSG_DATA(cmsg));
|
|
|
|
if (fd < 0)
|
|
|
|
continue;
|
|
|
|
|
2013-03-27 09:10:46 +00:00
|
|
|
/* O_NONBLOCK is preserved across SCM_RIGHTS so reset it */
|
|
|
|
qemu_set_block(fd);
|
|
|
|
|
2012-08-14 20:43:42 +00:00
|
|
|
#ifndef MSG_CMSG_CLOEXEC
|
|
|
|
qemu_set_cloexec(fd);
|
|
|
|
#endif
|
2009-07-22 08:11:39 +00:00
|
|
|
if (s->msgfd != -1)
|
|
|
|
close(s->msgfd);
|
|
|
|
s->msgfd = fd;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-07-22 08:11:38 +00:00
|
|
|
static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len)
|
|
|
|
{
|
|
|
|
TCPCharDriver *s = chr->opaque;
|
2009-08-01 10:13:20 +00:00
|
|
|
struct msghdr msg = { NULL, };
|
2009-07-22 08:11:38 +00:00
|
|
|
struct iovec iov[1];
|
2009-07-22 08:11:39 +00:00
|
|
|
union {
|
|
|
|
struct cmsghdr cmsg;
|
|
|
|
char control[CMSG_SPACE(sizeof(int))];
|
|
|
|
} msg_control;
|
2012-08-14 20:43:42 +00:00
|
|
|
int flags = 0;
|
2009-07-22 08:11:39 +00:00
|
|
|
ssize_t ret;
|
2009-07-22 08:11:38 +00:00
|
|
|
|
|
|
|
iov[0].iov_base = buf;
|
|
|
|
iov[0].iov_len = len;
|
|
|
|
|
|
|
|
msg.msg_iov = iov;
|
|
|
|
msg.msg_iovlen = 1;
|
2009-07-22 08:11:39 +00:00
|
|
|
msg.msg_control = &msg_control;
|
|
|
|
msg.msg_controllen = sizeof(msg_control);
|
|
|
|
|
2012-08-14 20:43:42 +00:00
|
|
|
#ifdef MSG_CMSG_CLOEXEC
|
|
|
|
flags |= MSG_CMSG_CLOEXEC;
|
|
|
|
#endif
|
|
|
|
ret = recvmsg(s->fd, &msg, flags);
|
|
|
|
if (ret > 0 && s->is_unix) {
|
2009-07-22 08:11:39 +00:00
|
|
|
unix_process_msgfd(chr, &msg);
|
2012-08-14 20:43:42 +00:00
|
|
|
}
|
2009-07-22 08:11:38 +00:00
|
|
|
|
2009-07-22 08:11:39 +00:00
|
|
|
return ret;
|
2009-07-22 08:11:38 +00:00
|
|
|
}
|
|
|
|
#else
|
|
|
|
static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len)
|
|
|
|
{
|
|
|
|
TCPCharDriver *s = chr->opaque;
|
2011-07-23 20:04:29 +00:00
|
|
|
return qemu_recv(s->fd, buf, len, 0);
|
2009-07-22 08:11:38 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2013-03-05 17:51:25 +00:00
|
|
|
static GSource *tcp_chr_add_watch(CharDriverState *chr, GIOCondition cond)
|
|
|
|
{
|
|
|
|
TCPCharDriver *s = chr->opaque;
|
|
|
|
return g_io_create_watch(s->chan, cond);
|
|
|
|
}
|
|
|
|
|
2013-03-05 17:51:22 +00:00
|
|
|
static gboolean tcp_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
|
2008-10-31 18:49:55 +00:00
|
|
|
{
|
|
|
|
CharDriverState *chr = opaque;
|
|
|
|
TCPCharDriver *s = chr->opaque;
|
2009-11-03 14:29:54 +00:00
|
|
|
uint8_t buf[READ_BUF_LEN];
|
2008-10-31 18:49:55 +00:00
|
|
|
int len, size;
|
|
|
|
|
2013-03-05 17:51:22 +00:00
|
|
|
if (!s->connected || s->max_size <= 0) {
|
2013-04-19 15:32:08 +00:00
|
|
|
return TRUE;
|
2013-03-05 17:51:22 +00:00
|
|
|
}
|
2008-10-31 18:49:55 +00:00
|
|
|
len = sizeof(buf);
|
|
|
|
if (len > s->max_size)
|
|
|
|
len = s->max_size;
|
2009-07-22 08:11:38 +00:00
|
|
|
size = tcp_chr_recv(chr, (void *)buf, len);
|
2008-10-31 18:49:55 +00:00
|
|
|
if (size == 0) {
|
|
|
|
/* connection closed */
|
|
|
|
s->connected = 0;
|
2013-03-05 17:51:22 +00:00
|
|
|
if (s->listen_chan) {
|
|
|
|
s->listen_tag = g_io_add_watch(s->listen_chan, G_IO_IN, tcp_chr_accept, chr);
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
2013-04-19 15:32:06 +00:00
|
|
|
if (s->tag) {
|
|
|
|
g_source_remove(s->tag);
|
|
|
|
s->tag = 0;
|
|
|
|
}
|
2013-03-05 17:51:22 +00:00
|
|
|
g_io_channel_unref(s->chan);
|
|
|
|
s->chan = NULL;
|
2008-10-31 18:49:55 +00:00
|
|
|
closesocket(s->fd);
|
|
|
|
s->fd = -1;
|
2011-11-19 09:22:43 +00:00
|
|
|
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
|
2008-10-31 18:49:55 +00:00
|
|
|
} else if (size > 0) {
|
|
|
|
if (s->do_telnetopt)
|
|
|
|
tcp_chr_process_IAC_bytes(chr, s, buf, &size);
|
|
|
|
if (size > 0)
|
2011-08-15 16:17:30 +00:00
|
|
|
qemu_chr_be_write(chr, buf, size);
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
2013-03-05 17:51:22 +00:00
|
|
|
|
|
|
|
return TRUE;
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
|
2010-08-15 09:46:24 +00:00
|
|
|
#ifndef _WIN32
|
|
|
|
CharDriverState *qemu_chr_open_eventfd(int eventfd)
|
|
|
|
{
|
RESEND: Inter-VM shared memory PCI device
resend for bug fix related to removal of irqfd
Support an inter-vm shared memory device that maps a shared-memory object as a
PCI device in the guest. This patch also supports interrupts between guest by
communicating over a unix domain socket. This patch applies to the qemu-kvm
repository.
-device ivshmem,size=<size in format accepted by -m>[,shm=<shm name>]
Interrupts are supported between multiple VMs by using a shared memory server
by using a chardev socket.
-device ivshmem,size=<size in format accepted by -m>[,shm=<shm name>]
[,chardev=<id>][,msi=on][,ioeventfd=on][,vectors=n][,role=peer|master]
-chardev socket,path=<path>,id=<id>
The shared memory server, sample programs and init scripts are in a git repo here:
www.gitorious.org/nahanni
Signed-off-by: Cam Macdonell <cam@cs.ualberta.ca>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2010-07-27 16:54:13 +00:00
|
|
|
return qemu_chr_open_fd(eventfd, eventfd);
|
|
|
|
}
|
2010-08-15 09:46:24 +00:00
|
|
|
#endif
|
RESEND: Inter-VM shared memory PCI device
resend for bug fix related to removal of irqfd
Support an inter-vm shared memory device that maps a shared-memory object as a
PCI device in the guest. This patch also supports interrupts between guest by
communicating over a unix domain socket. This patch applies to the qemu-kvm
repository.
-device ivshmem,size=<size in format accepted by -m>[,shm=<shm name>]
Interrupts are supported between multiple VMs by using a shared memory server
by using a chardev socket.
-device ivshmem,size=<size in format accepted by -m>[,shm=<shm name>]
[,chardev=<id>][,msi=on][,ioeventfd=on][,vectors=n][,role=peer|master]
-chardev socket,path=<path>,id=<id>
The shared memory server, sample programs and init scripts are in a git repo here:
www.gitorious.org/nahanni
Signed-off-by: Cam Macdonell <cam@cs.ualberta.ca>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2010-07-27 16:54:13 +00:00
|
|
|
|
2008-10-31 18:49:55 +00:00
|
|
|
static void tcp_chr_connect(void *opaque)
|
|
|
|
{
|
|
|
|
CharDriverState *chr = opaque;
|
|
|
|
TCPCharDriver *s = chr->opaque;
|
|
|
|
|
|
|
|
s->connected = 1;
|
2013-03-05 17:51:22 +00:00
|
|
|
if (s->chan) {
|
|
|
|
s->tag = io_add_watch_poll(s->chan, tcp_chr_read_poll, tcp_chr_read, chr);
|
2012-09-10 02:30:56 +00:00
|
|
|
}
|
2013-03-26 10:07:54 +00:00
|
|
|
qemu_chr_be_generic_open(chr);
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#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);
|
|
|
|
}
|
|
|
|
|
Introduce a 'client_add' monitor command accepting an open FD
Allow client connections for VNC and socket based character
devices to be passed in over the monitor using SCM_RIGHTS.
One intended usage scenario is to start QEMU with VNC on a
UNIX domain socket. An unprivileged user which cannot access
the UNIX domain socket, can then connect to QEMU's VNC server
by passing an open FD to libvirt, which passes it onto QEMU.
{ "execute": "get_fd", "arguments": { "fdname": "myclient" } }
{ "return": {} }
{ "execute": "add_client", "arguments": { "protocol": "vnc",
"fdname": "myclient",
"skipauth": true } }
{ "return": {} }
In this case 'protocol' can be 'vnc' or 'spice', or the name
of a character device (eg from -chardev id=XXXX)
The 'skipauth' parameter can be used to skip any configured
VNC authentication scheme, which is useful if the mgmt layer
talking to the monitor has already authenticated the client
in another way.
* console.h: Define 'vnc_display_add_client' method
* monitor.c: Implement 'client_add' command
* qemu-char.c, qemu-char.h: Add 'qemu_char_add_client' method
* qerror.c, qerror.h: Add QERR_ADD_CLIENT_FAILED
* qmp-commands.hx: Declare 'client_add' command
* ui/vnc.c: Implement 'vnc_display_add_client' method
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2011-06-23 12:31:42 +00:00
|
|
|
static int tcp_chr_add_client(CharDriverState *chr, int fd)
|
|
|
|
{
|
|
|
|
TCPCharDriver *s = chr->opaque;
|
|
|
|
if (s->fd != -1)
|
|
|
|
return -1;
|
|
|
|
|
2013-03-27 09:10:43 +00:00
|
|
|
qemu_set_nonblock(fd);
|
Introduce a 'client_add' monitor command accepting an open FD
Allow client connections for VNC and socket based character
devices to be passed in over the monitor using SCM_RIGHTS.
One intended usage scenario is to start QEMU with VNC on a
UNIX domain socket. An unprivileged user which cannot access
the UNIX domain socket, can then connect to QEMU's VNC server
by passing an open FD to libvirt, which passes it onto QEMU.
{ "execute": "get_fd", "arguments": { "fdname": "myclient" } }
{ "return": {} }
{ "execute": "add_client", "arguments": { "protocol": "vnc",
"fdname": "myclient",
"skipauth": true } }
{ "return": {} }
In this case 'protocol' can be 'vnc' or 'spice', or the name
of a character device (eg from -chardev id=XXXX)
The 'skipauth' parameter can be used to skip any configured
VNC authentication scheme, which is useful if the mgmt layer
talking to the monitor has already authenticated the client
in another way.
* console.h: Define 'vnc_display_add_client' method
* monitor.c: Implement 'client_add' command
* qemu-char.c, qemu-char.h: Add 'qemu_char_add_client' method
* qerror.c, qerror.h: Add QERR_ADD_CLIENT_FAILED
* qmp-commands.hx: Declare 'client_add' command
* ui/vnc.c: Implement 'vnc_display_add_client' method
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2011-06-23 12:31:42 +00:00
|
|
|
if (s->do_nodelay)
|
|
|
|
socket_set_nodelay(fd);
|
|
|
|
s->fd = fd;
|
2013-03-05 17:51:22 +00:00
|
|
|
s->chan = io_channel_from_socket(fd);
|
2013-04-19 15:32:06 +00:00
|
|
|
if (s->listen_tag) {
|
|
|
|
g_source_remove(s->listen_tag);
|
|
|
|
s->listen_tag = 0;
|
|
|
|
}
|
Introduce a 'client_add' monitor command accepting an open FD
Allow client connections for VNC and socket based character
devices to be passed in over the monitor using SCM_RIGHTS.
One intended usage scenario is to start QEMU with VNC on a
UNIX domain socket. An unprivileged user which cannot access
the UNIX domain socket, can then connect to QEMU's VNC server
by passing an open FD to libvirt, which passes it onto QEMU.
{ "execute": "get_fd", "arguments": { "fdname": "myclient" } }
{ "return": {} }
{ "execute": "add_client", "arguments": { "protocol": "vnc",
"fdname": "myclient",
"skipauth": true } }
{ "return": {} }
In this case 'protocol' can be 'vnc' or 'spice', or the name
of a character device (eg from -chardev id=XXXX)
The 'skipauth' parameter can be used to skip any configured
VNC authentication scheme, which is useful if the mgmt layer
talking to the monitor has already authenticated the client
in another way.
* console.h: Define 'vnc_display_add_client' method
* monitor.c: Implement 'client_add' command
* qemu-char.c, qemu-char.h: Add 'qemu_char_add_client' method
* qerror.c, qerror.h: Add QERR_ADD_CLIENT_FAILED
* qmp-commands.hx: Declare 'client_add' command
* ui/vnc.c: Implement 'vnc_display_add_client' method
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2011-06-23 12:31:42 +00:00
|
|
|
tcp_chr_connect(chr);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-03-05 17:51:22 +00:00
|
|
|
static gboolean tcp_chr_accept(GIOChannel *channel, GIOCondition cond, void *opaque)
|
2008-10-31 18:49:55 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
2009-12-02 11:24:42 +00:00
|
|
|
fd = qemu_accept(s->listen_fd, addr, &len);
|
2008-10-31 18:49:55 +00:00
|
|
|
if (fd < 0 && errno != EINTR) {
|
2013-03-05 17:51:22 +00:00
|
|
|
return FALSE;
|
2008-10-31 18:49:55 +00:00
|
|
|
} else if (fd >= 0) {
|
|
|
|
if (s->do_telnetopt)
|
|
|
|
tcp_chr_telnet_init(fd);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
Introduce a 'client_add' monitor command accepting an open FD
Allow client connections for VNC and socket based character
devices to be passed in over the monitor using SCM_RIGHTS.
One intended usage scenario is to start QEMU with VNC on a
UNIX domain socket. An unprivileged user which cannot access
the UNIX domain socket, can then connect to QEMU's VNC server
by passing an open FD to libvirt, which passes it onto QEMU.
{ "execute": "get_fd", "arguments": { "fdname": "myclient" } }
{ "return": {} }
{ "execute": "add_client", "arguments": { "protocol": "vnc",
"fdname": "myclient",
"skipauth": true } }
{ "return": {} }
In this case 'protocol' can be 'vnc' or 'spice', or the name
of a character device (eg from -chardev id=XXXX)
The 'skipauth' parameter can be used to skip any configured
VNC authentication scheme, which is useful if the mgmt layer
talking to the monitor has already authenticated the client
in another way.
* console.h: Define 'vnc_display_add_client' method
* monitor.c: Implement 'client_add' command
* qemu-char.c, qemu-char.h: Add 'qemu_char_add_client' method
* qerror.c, qerror.h: Add QERR_ADD_CLIENT_FAILED
* qmp-commands.hx: Declare 'client_add' command
* ui/vnc.c: Implement 'vnc_display_add_client' method
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2011-06-23 12:31:42 +00:00
|
|
|
if (tcp_chr_add_client(chr, fd) < 0)
|
|
|
|
close(fd);
|
2013-03-05 17:51:22 +00:00
|
|
|
|
|
|
|
return TRUE;
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void tcp_chr_close(CharDriverState *chr)
|
|
|
|
{
|
|
|
|
TCPCharDriver *s = chr->opaque;
|
2009-03-28 17:58:14 +00:00
|
|
|
if (s->fd >= 0) {
|
2013-03-05 17:51:22 +00:00
|
|
|
if (s->tag) {
|
|
|
|
g_source_remove(s->tag);
|
2013-04-19 15:32:06 +00:00
|
|
|
s->tag = 0;
|
2013-03-05 17:51:22 +00:00
|
|
|
}
|
|
|
|
if (s->chan) {
|
|
|
|
g_io_channel_unref(s->chan);
|
|
|
|
}
|
2008-10-31 18:49:55 +00:00
|
|
|
closesocket(s->fd);
|
2009-03-28 17:58:14 +00:00
|
|
|
}
|
|
|
|
if (s->listen_fd >= 0) {
|
2013-03-05 17:51:22 +00:00
|
|
|
if (s->listen_tag) {
|
|
|
|
g_source_remove(s->listen_tag);
|
2013-04-19 15:32:06 +00:00
|
|
|
s->listen_tag = 0;
|
2013-03-05 17:51:22 +00:00
|
|
|
}
|
|
|
|
if (s->listen_chan) {
|
|
|
|
g_io_channel_unref(s->listen_chan);
|
|
|
|
}
|
2008-10-31 18:49:55 +00:00
|
|
|
closesocket(s->listen_fd);
|
2009-03-28 17:58:14 +00:00
|
|
|
}
|
2011-08-21 03:09:37 +00:00
|
|
|
g_free(s);
|
2011-11-19 09:22:43 +00:00
|
|
|
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
|
2012-12-20 12:53:12 +00:00
|
|
|
static CharDriverState *qemu_chr_open_socket_fd(int fd, bool do_nodelay,
|
|
|
|
bool is_listen, bool is_telnet,
|
|
|
|
bool is_waitconnect,
|
|
|
|
Error **errp)
|
2008-10-31 18:49:55 +00:00
|
|
|
{
|
|
|
|
CharDriverState *chr = NULL;
|
|
|
|
TCPCharDriver *s = NULL;
|
2012-12-20 12:53:12 +00:00
|
|
|
char host[NI_MAXHOST], serv[NI_MAXSERV];
|
|
|
|
const char *left = "", *right = "";
|
|
|
|
struct sockaddr_storage ss;
|
|
|
|
socklen_t ss_len = sizeof(ss);
|
|
|
|
|
|
|
|
memset(&ss, 0, ss_len);
|
|
|
|
if (getsockname(fd, (struct sockaddr *) &ss, &ss_len) != 0) {
|
|
|
|
error_setg(errp, "getsockname: %s", strerror(errno));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
chr = g_malloc0(sizeof(CharDriverState));
|
|
|
|
s = g_malloc0(sizeof(TCPCharDriver));
|
|
|
|
|
|
|
|
s->connected = 0;
|
|
|
|
s->fd = -1;
|
|
|
|
s->listen_fd = -1;
|
|
|
|
s->msgfd = -1;
|
|
|
|
|
|
|
|
chr->filename = g_malloc(256);
|
|
|
|
switch (ss.ss_family) {
|
|
|
|
#ifndef _WIN32
|
|
|
|
case AF_UNIX:
|
|
|
|
s->is_unix = 1;
|
|
|
|
snprintf(chr->filename, 256, "unix:%s%s",
|
|
|
|
((struct sockaddr_un *)(&ss))->sun_path,
|
|
|
|
is_listen ? ",server" : "");
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
case AF_INET6:
|
|
|
|
left = "[";
|
|
|
|
right = "]";
|
|
|
|
/* fall through */
|
|
|
|
case AF_INET:
|
|
|
|
s->do_nodelay = do_nodelay;
|
|
|
|
getnameinfo((struct sockaddr *) &ss, ss_len, host, sizeof(host),
|
|
|
|
serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV);
|
2013-03-10 13:58:05 +00:00
|
|
|
snprintf(chr->filename, 256, "%s:%s%s%s:%s%s",
|
2012-12-20 12:53:12 +00:00
|
|
|
is_telnet ? "telnet" : "tcp",
|
|
|
|
left, host, right, serv,
|
|
|
|
is_listen ? ",server" : "");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
chr->opaque = s;
|
|
|
|
chr->chr_write = tcp_chr_write;
|
|
|
|
chr->chr_close = tcp_chr_close;
|
|
|
|
chr->get_msgfd = tcp_get_msgfd;
|
|
|
|
chr->chr_add_client = tcp_chr_add_client;
|
2013-03-05 17:51:25 +00:00
|
|
|
chr->chr_add_watch = tcp_chr_add_watch;
|
2012-12-20 12:53:12 +00:00
|
|
|
|
|
|
|
if (is_listen) {
|
|
|
|
s->listen_fd = fd;
|
2013-03-05 17:51:22 +00:00
|
|
|
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);
|
2012-12-20 12:53:12 +00:00
|
|
|
if (is_telnet) {
|
|
|
|
s->do_telnetopt = 1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
s->connected = 1;
|
|
|
|
s->fd = fd;
|
|
|
|
socket_set_nodelay(fd);
|
2013-03-05 17:51:22 +00:00
|
|
|
s->chan = io_channel_from_socket(s->fd);
|
2012-12-20 12:53:12 +00:00
|
|
|
tcp_chr_connect(chr);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_listen && is_waitconnect) {
|
|
|
|
printf("QEMU waiting for connection on: %s\n",
|
|
|
|
chr->filename);
|
2013-03-05 17:51:22 +00:00
|
|
|
tcp_chr_accept(s->listen_chan, G_IO_IN, chr);
|
2013-03-27 09:10:43 +00:00
|
|
|
qemu_set_nonblock(s->listen_fd);
|
2012-12-20 12:53:12 +00:00
|
|
|
}
|
|
|
|
return chr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static CharDriverState *qemu_chr_open_socket(QemuOpts *opts)
|
|
|
|
{
|
|
|
|
CharDriverState *chr = NULL;
|
2012-10-02 07:16:49 +00:00
|
|
|
Error *local_err = NULL;
|
2009-09-10 08:58:42 +00:00
|
|
|
int fd = -1;
|
|
|
|
int is_listen;
|
|
|
|
int is_waitconnect;
|
|
|
|
int do_nodelay;
|
|
|
|
int is_unix;
|
|
|
|
int is_telnet;
|
|
|
|
|
|
|
|
is_listen = qemu_opt_get_bool(opts, "server", 0);
|
|
|
|
is_waitconnect = qemu_opt_get_bool(opts, "wait", 1);
|
|
|
|
is_telnet = qemu_opt_get_bool(opts, "telnet", 0);
|
|
|
|
do_nodelay = !qemu_opt_get_bool(opts, "delay", 1);
|
|
|
|
is_unix = qemu_opt_get(opts, "path") != NULL;
|
2008-10-31 18:49:55 +00:00
|
|
|
if (!is_listen)
|
|
|
|
is_waitconnect = 0;
|
|
|
|
|
2008-11-11 20:54:09 +00:00
|
|
|
if (is_unix) {
|
|
|
|
if (is_listen) {
|
2012-10-02 07:16:49 +00:00
|
|
|
fd = unix_listen_opts(opts, &local_err);
|
2008-11-11 20:54:09 +00:00
|
|
|
} else {
|
2012-10-02 07:16:49 +00:00
|
|
|
fd = unix_connect_opts(opts, &local_err, NULL, NULL);
|
2008-11-11 20:54:09 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (is_listen) {
|
2012-10-02 07:16:49 +00:00
|
|
|
fd = inet_listen_opts(opts, 0, &local_err);
|
2008-11-11 20:54:09 +00:00
|
|
|
} else {
|
2012-10-02 07:16:49 +00:00
|
|
|
fd = inet_connect_opts(opts, &local_err, NULL, NULL);
|
2008-11-11 20:54:09 +00:00
|
|
|
}
|
|
|
|
}
|
2012-02-07 14:09:10 +00:00
|
|
|
if (fd < 0) {
|
2008-10-31 18:49:55 +00:00
|
|
|
goto fail;
|
2012-02-07 14:09:10 +00:00
|
|
|
}
|
2008-10-31 18:49:55 +00:00
|
|
|
|
|
|
|
if (!is_waitconnect)
|
2013-03-27 09:10:43 +00:00
|
|
|
qemu_set_nonblock(fd);
|
2008-10-31 18:49:55 +00:00
|
|
|
|
2012-12-20 12:53:12 +00:00
|
|
|
chr = qemu_chr_open_socket_fd(fd, do_nodelay, is_listen, is_telnet,
|
|
|
|
is_waitconnect, &local_err);
|
|
|
|
if (error_is_set(&local_err)) {
|
|
|
|
goto fail;
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
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 14:09:08 +00:00
|
|
|
return chr;
|
2009-09-10 08:58:42 +00:00
|
|
|
|
2012-12-20 12:53:12 +00:00
|
|
|
|
2008-10-31 18:49:55 +00:00
|
|
|
fail:
|
2012-10-02 07:16:49 +00:00
|
|
|
if (local_err) {
|
|
|
|
qerror_report_err(local_err);
|
|
|
|
error_free(local_err);
|
|
|
|
}
|
|
|
|
if (fd >= 0) {
|
2008-10-31 18:49:55 +00:00
|
|
|
closesocket(fd);
|
2012-10-02 07:16:49 +00:00
|
|
|
}
|
2012-12-20 12:53:12 +00:00
|
|
|
if (chr) {
|
|
|
|
g_free(chr->opaque);
|
|
|
|
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 14:09:08 +00:00
|
|
|
return NULL;
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
|
2013-01-24 16:03:19 +00:00
|
|
|
/*********************************************************/
|
qemu-char: Saner naming of memchar stuff & doc fixes
New device, has never been released, so we can still improve things
without worrying about compatibility.
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. Therefore:
* Rename CirMemCharDriver to RingBufCharDriver, and call the thing a
"ringbuf" in the API.
* Rename QMP and HMP commands from memchar-FOO to ringbuf-FOO.
* Rename device parameter from maxcapacity to size (simple words are
good for you).
* Clearly mark the parameter as optional in documentation.
* Fix error reporting so that chardev-add reports to current monitor,
not stderr.
* Replace cirmem in C identifiers by ringbuf.
* Rework documentation. Document the impact of our crappy UTF-8
handling on reading.
* QMP examples that even work.
I could split this up into multiple commits, but they'd change the
same documentation lines multiple times. Not worth it.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-02-06 20:27:24 +00:00
|
|
|
/* Ring buffer chardev */
|
2013-01-24 16:03:19 +00:00
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
size_t size;
|
|
|
|
size_t prod;
|
|
|
|
size_t cons;
|
|
|
|
uint8_t *cbuf;
|
qemu-char: Saner naming of memchar stuff & doc fixes
New device, has never been released, so we can still improve things
without worrying about compatibility.
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. Therefore:
* Rename CirMemCharDriver to RingBufCharDriver, and call the thing a
"ringbuf" in the API.
* Rename QMP and HMP commands from memchar-FOO to ringbuf-FOO.
* Rename device parameter from maxcapacity to size (simple words are
good for you).
* Clearly mark the parameter as optional in documentation.
* Fix error reporting so that chardev-add reports to current monitor,
not stderr.
* Replace cirmem in C identifiers by ringbuf.
* Rework documentation. Document the impact of our crappy UTF-8
handling on reading.
* QMP examples that even work.
I could split this up into multiple commits, but they'd change the
same documentation lines multiple times. Not worth it.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-02-06 20:27:24 +00:00
|
|
|
} RingBufCharDriver;
|
2013-01-24 16:03:19 +00:00
|
|
|
|
qemu-char: Saner naming of memchar stuff & doc fixes
New device, has never been released, so we can still improve things
without worrying about compatibility.
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. Therefore:
* Rename CirMemCharDriver to RingBufCharDriver, and call the thing a
"ringbuf" in the API.
* Rename QMP and HMP commands from memchar-FOO to ringbuf-FOO.
* Rename device parameter from maxcapacity to size (simple words are
good for you).
* Clearly mark the parameter as optional in documentation.
* Fix error reporting so that chardev-add reports to current monitor,
not stderr.
* Replace cirmem in C identifiers by ringbuf.
* Rework documentation. Document the impact of our crappy UTF-8
handling on reading.
* QMP examples that even work.
I could split this up into multiple commits, but they'd change the
same documentation lines multiple times. Not worth it.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-02-06 20:27:24 +00:00
|
|
|
static size_t ringbuf_count(const CharDriverState *chr)
|
2013-01-24 16:03:19 +00:00
|
|
|
{
|
qemu-char: Saner naming of memchar stuff & doc fixes
New device, has never been released, so we can still improve things
without worrying about compatibility.
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. Therefore:
* Rename CirMemCharDriver to RingBufCharDriver, and call the thing a
"ringbuf" in the API.
* Rename QMP and HMP commands from memchar-FOO to ringbuf-FOO.
* Rename device parameter from maxcapacity to size (simple words are
good for you).
* Clearly mark the parameter as optional in documentation.
* Fix error reporting so that chardev-add reports to current monitor,
not stderr.
* Replace cirmem in C identifiers by ringbuf.
* Rework documentation. Document the impact of our crappy UTF-8
handling on reading.
* QMP examples that even work.
I could split this up into multiple commits, but they'd change the
same documentation lines multiple times. Not worth it.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-02-06 20:27:24 +00:00
|
|
|
const RingBufCharDriver *d = chr->opaque;
|
2013-01-24 16:03:19 +00:00
|
|
|
|
2013-02-06 20:27:23 +00:00
|
|
|
return d->prod - d->cons;
|
2013-01-24 16:03:19 +00:00
|
|
|
}
|
|
|
|
|
qemu-char: Saner naming of memchar stuff & doc fixes
New device, has never been released, so we can still improve things
without worrying about compatibility.
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. Therefore:
* Rename CirMemCharDriver to RingBufCharDriver, and call the thing a
"ringbuf" in the API.
* Rename QMP and HMP commands from memchar-FOO to ringbuf-FOO.
* Rename device parameter from maxcapacity to size (simple words are
good for you).
* Clearly mark the parameter as optional in documentation.
* Fix error reporting so that chardev-add reports to current monitor,
not stderr.
* Replace cirmem in C identifiers by ringbuf.
* Rework documentation. Document the impact of our crappy UTF-8
handling on reading.
* QMP examples that even work.
I could split this up into multiple commits, but they'd change the
same documentation lines multiple times. Not worth it.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-02-06 20:27:24 +00:00
|
|
|
static int ringbuf_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
|
2013-01-24 16:03:19 +00:00
|
|
|
{
|
qemu-char: Saner naming of memchar stuff & doc fixes
New device, has never been released, so we can still improve things
without worrying about compatibility.
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. Therefore:
* Rename CirMemCharDriver to RingBufCharDriver, and call the thing a
"ringbuf" in the API.
* Rename QMP and HMP commands from memchar-FOO to ringbuf-FOO.
* Rename device parameter from maxcapacity to size (simple words are
good for you).
* Clearly mark the parameter as optional in documentation.
* Fix error reporting so that chardev-add reports to current monitor,
not stderr.
* Replace cirmem in C identifiers by ringbuf.
* Rework documentation. Document the impact of our crappy UTF-8
handling on reading.
* QMP examples that even work.
I could split this up into multiple commits, but they'd change the
same documentation lines multiple times. Not worth it.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-02-06 20:27:24 +00:00
|
|
|
RingBufCharDriver *d = chr->opaque;
|
2013-01-24 16:03:19 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!buf || (len < 0)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < len; i++ ) {
|
2013-02-06 20:27:23 +00:00
|
|
|
d->cbuf[d->prod++ & (d->size - 1)] = buf[i];
|
|
|
|
if (d->prod - d->cons > d->size) {
|
2013-01-24 16:03:19 +00:00
|
|
|
d->cons = d->prod - d->size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
qemu-char: Saner naming of memchar stuff & doc fixes
New device, has never been released, so we can still improve things
without worrying about compatibility.
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. Therefore:
* Rename CirMemCharDriver to RingBufCharDriver, and call the thing a
"ringbuf" in the API.
* Rename QMP and HMP commands from memchar-FOO to ringbuf-FOO.
* Rename device parameter from maxcapacity to size (simple words are
good for you).
* Clearly mark the parameter as optional in documentation.
* Fix error reporting so that chardev-add reports to current monitor,
not stderr.
* Replace cirmem in C identifiers by ringbuf.
* Rework documentation. Document the impact of our crappy UTF-8
handling on reading.
* QMP examples that even work.
I could split this up into multiple commits, but they'd change the
same documentation lines multiple times. Not worth it.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-02-06 20:27:24 +00:00
|
|
|
static int ringbuf_chr_read(CharDriverState *chr, uint8_t *buf, int len)
|
2013-01-24 16:03:19 +00:00
|
|
|
{
|
qemu-char: Saner naming of memchar stuff & doc fixes
New device, has never been released, so we can still improve things
without worrying about compatibility.
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. Therefore:
* Rename CirMemCharDriver to RingBufCharDriver, and call the thing a
"ringbuf" in the API.
* Rename QMP and HMP commands from memchar-FOO to ringbuf-FOO.
* Rename device parameter from maxcapacity to size (simple words are
good for you).
* Clearly mark the parameter as optional in documentation.
* Fix error reporting so that chardev-add reports to current monitor,
not stderr.
* Replace cirmem in C identifiers by ringbuf.
* Rework documentation. Document the impact of our crappy UTF-8
handling on reading.
* QMP examples that even work.
I could split this up into multiple commits, but they'd change the
same documentation lines multiple times. Not worth it.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-02-06 20:27:24 +00:00
|
|
|
RingBufCharDriver *d = chr->opaque;
|
2013-01-24 16:03:19 +00:00
|
|
|
int i;
|
|
|
|
|
2013-02-06 20:27:23 +00:00
|
|
|
for (i = 0; i < len && d->cons != d->prod; i++) {
|
|
|
|
buf[i] = d->cbuf[d->cons++ & (d->size - 1)];
|
2013-01-24 16:03:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
qemu-char: Saner naming of memchar stuff & doc fixes
New device, has never been released, so we can still improve things
without worrying about compatibility.
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. Therefore:
* Rename CirMemCharDriver to RingBufCharDriver, and call the thing a
"ringbuf" in the API.
* Rename QMP and HMP commands from memchar-FOO to ringbuf-FOO.
* Rename device parameter from maxcapacity to size (simple words are
good for you).
* Clearly mark the parameter as optional in documentation.
* Fix error reporting so that chardev-add reports to current monitor,
not stderr.
* Replace cirmem in C identifiers by ringbuf.
* Rework documentation. Document the impact of our crappy UTF-8
handling on reading.
* QMP examples that even work.
I could split this up into multiple commits, but they'd change the
same documentation lines multiple times. Not worth it.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-02-06 20:27:24 +00:00
|
|
|
static void ringbuf_chr_close(struct CharDriverState *chr)
|
2013-01-24 16:03:19 +00:00
|
|
|
{
|
qemu-char: Saner naming of memchar stuff & doc fixes
New device, has never been released, so we can still improve things
without worrying about compatibility.
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. Therefore:
* Rename CirMemCharDriver to RingBufCharDriver, and call the thing a
"ringbuf" in the API.
* Rename QMP and HMP commands from memchar-FOO to ringbuf-FOO.
* Rename device parameter from maxcapacity to size (simple words are
good for you).
* Clearly mark the parameter as optional in documentation.
* Fix error reporting so that chardev-add reports to current monitor,
not stderr.
* Replace cirmem in C identifiers by ringbuf.
* Rework documentation. Document the impact of our crappy UTF-8
handling on reading.
* QMP examples that even work.
I could split this up into multiple commits, but they'd change the
same documentation lines multiple times. Not worth it.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-02-06 20:27:24 +00:00
|
|
|
RingBufCharDriver *d = chr->opaque;
|
2013-01-24 16:03:19 +00:00
|
|
|
|
|
|
|
g_free(d->cbuf);
|
|
|
|
g_free(d);
|
|
|
|
chr->opaque = NULL;
|
|
|
|
}
|
|
|
|
|
2013-02-26 15:21:11 +00:00
|
|
|
static CharDriverState *qemu_chr_open_ringbuf(ChardevRingbuf *opts,
|
|
|
|
Error **errp)
|
2013-01-24 16:03:19 +00:00
|
|
|
{
|
|
|
|
CharDriverState *chr;
|
qemu-char: Saner naming of memchar stuff & doc fixes
New device, has never been released, so we can still improve things
without worrying about compatibility.
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. Therefore:
* Rename CirMemCharDriver to RingBufCharDriver, and call the thing a
"ringbuf" in the API.
* Rename QMP and HMP commands from memchar-FOO to ringbuf-FOO.
* Rename device parameter from maxcapacity to size (simple words are
good for you).
* Clearly mark the parameter as optional in documentation.
* Fix error reporting so that chardev-add reports to current monitor,
not stderr.
* Replace cirmem in C identifiers by ringbuf.
* Rework documentation. Document the impact of our crappy UTF-8
handling on reading.
* QMP examples that even work.
I could split this up into multiple commits, but they'd change the
same documentation lines multiple times. Not worth it.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-02-06 20:27:24 +00:00
|
|
|
RingBufCharDriver *d;
|
2013-01-24 16:03:19 +00:00
|
|
|
|
|
|
|
chr = g_malloc0(sizeof(CharDriverState));
|
|
|
|
d = g_malloc(sizeof(*d));
|
|
|
|
|
2013-02-26 15:21:11 +00:00
|
|
|
d->size = opts->has_size ? opts->size : 65536;
|
2013-01-24 16:03:19 +00:00
|
|
|
|
|
|
|
/* The size must be power of 2 */
|
|
|
|
if (d->size & (d->size - 1)) {
|
2013-02-26 15:21:11 +00:00
|
|
|
error_setg(errp, "size of ringbuf chardev must be power of two");
|
2013-01-24 16:03:19 +00:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
d->prod = 0;
|
|
|
|
d->cons = 0;
|
|
|
|
d->cbuf = g_malloc0(d->size);
|
|
|
|
|
|
|
|
chr->opaque = d;
|
qemu-char: Saner naming of memchar stuff & doc fixes
New device, has never been released, so we can still improve things
without worrying about compatibility.
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. Therefore:
* Rename CirMemCharDriver to RingBufCharDriver, and call the thing a
"ringbuf" in the API.
* Rename QMP and HMP commands from memchar-FOO to ringbuf-FOO.
* Rename device parameter from maxcapacity to size (simple words are
good for you).
* Clearly mark the parameter as optional in documentation.
* Fix error reporting so that chardev-add reports to current monitor,
not stderr.
* Replace cirmem in C identifiers by ringbuf.
* Rework documentation. Document the impact of our crappy UTF-8
handling on reading.
* QMP examples that even work.
I could split this up into multiple commits, but they'd change the
same documentation lines multiple times. Not worth it.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-02-06 20:27:24 +00:00
|
|
|
chr->chr_write = ringbuf_chr_write;
|
|
|
|
chr->chr_close = ringbuf_chr_close;
|
2013-01-24 16:03:19 +00:00
|
|
|
|
|
|
|
return chr;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
g_free(d);
|
|
|
|
g_free(chr);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
qemu-char: Saner naming of memchar stuff & doc fixes
New device, has never been released, so we can still improve things
without worrying about compatibility.
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. Therefore:
* Rename CirMemCharDriver to RingBufCharDriver, and call the thing a
"ringbuf" in the API.
* Rename QMP and HMP commands from memchar-FOO to ringbuf-FOO.
* Rename device parameter from maxcapacity to size (simple words are
good for you).
* Clearly mark the parameter as optional in documentation.
* Fix error reporting so that chardev-add reports to current monitor,
not stderr.
* Replace cirmem in C identifiers by ringbuf.
* Rework documentation. Document the impact of our crappy UTF-8
handling on reading.
* QMP examples that even work.
I could split this up into multiple commits, but they'd change the
same documentation lines multiple times. Not worth it.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-02-06 20:27:24 +00:00
|
|
|
static bool chr_is_ringbuf(const CharDriverState *chr)
|
2013-01-24 16:03:20 +00:00
|
|
|
{
|
qemu-char: Saner naming of memchar stuff & doc fixes
New device, has never been released, so we can still improve things
without worrying about compatibility.
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. Therefore:
* Rename CirMemCharDriver to RingBufCharDriver, and call the thing a
"ringbuf" in the API.
* Rename QMP and HMP commands from memchar-FOO to ringbuf-FOO.
* Rename device parameter from maxcapacity to size (simple words are
good for you).
* Clearly mark the parameter as optional in documentation.
* Fix error reporting so that chardev-add reports to current monitor,
not stderr.
* Replace cirmem in C identifiers by ringbuf.
* Rework documentation. Document the impact of our crappy UTF-8
handling on reading.
* QMP examples that even work.
I could split this up into multiple commits, but they'd change the
same documentation lines multiple times. Not worth it.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-02-06 20:27:24 +00:00
|
|
|
return chr->chr_write == ringbuf_chr_write;
|
2013-01-24 16:03:20 +00:00
|
|
|
}
|
|
|
|
|
qemu-char: Saner naming of memchar stuff & doc fixes
New device, has never been released, so we can still improve things
without worrying about compatibility.
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. Therefore:
* Rename CirMemCharDriver to RingBufCharDriver, and call the thing a
"ringbuf" in the API.
* Rename QMP and HMP commands from memchar-FOO to ringbuf-FOO.
* Rename device parameter from maxcapacity to size (simple words are
good for you).
* Clearly mark the parameter as optional in documentation.
* Fix error reporting so that chardev-add reports to current monitor,
not stderr.
* Replace cirmem in C identifiers by ringbuf.
* Rework documentation. Document the impact of our crappy UTF-8
handling on reading.
* QMP examples that even work.
I could split this up into multiple commits, but they'd change the
same documentation lines multiple times. Not worth it.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-02-06 20:27:24 +00:00
|
|
|
void qmp_ringbuf_write(const char *device, const char *data,
|
2013-02-06 20:27:14 +00:00
|
|
|
bool has_format, enum DataFormat format,
|
2013-01-24 16:03:20 +00:00
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
CharDriverState *chr;
|
2013-02-06 20:27:17 +00:00
|
|
|
const uint8_t *write_data;
|
2013-01-24 16:03:20 +00:00
|
|
|
int ret;
|
2013-02-06 20:27:17 +00:00
|
|
|
size_t write_count;
|
2013-01-24 16:03:20 +00:00
|
|
|
|
|
|
|
chr = qemu_chr_find(device);
|
|
|
|
if (!chr) {
|
2013-02-06 20:27:16 +00:00
|
|
|
error_setg(errp, "Device '%s' not found", device);
|
2013-01-24 16:03:20 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
qemu-char: Saner naming of memchar stuff & doc fixes
New device, has never been released, so we can still improve things
without worrying about compatibility.
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. Therefore:
* Rename CirMemCharDriver to RingBufCharDriver, and call the thing a
"ringbuf" in the API.
* Rename QMP and HMP commands from memchar-FOO to ringbuf-FOO.
* Rename device parameter from maxcapacity to size (simple words are
good for you).
* Clearly mark the parameter as optional in documentation.
* Fix error reporting so that chardev-add reports to current monitor,
not stderr.
* Replace cirmem in C identifiers by ringbuf.
* Rework documentation. Document the impact of our crappy UTF-8
handling on reading.
* QMP examples that even work.
I could split this up into multiple commits, but they'd change the
same documentation lines multiple times. Not worth it.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-02-06 20:27:24 +00:00
|
|
|
if (!chr_is_ringbuf(chr)) {
|
|
|
|
error_setg(errp,"%s is not a ringbuf device", device);
|
2013-01-24 16:03:20 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (has_format && (format == DATA_FORMAT_BASE64)) {
|
|
|
|
write_data = g_base64_decode(data, &write_count);
|
|
|
|
} else {
|
|
|
|
write_data = (uint8_t *)data;
|
2013-02-06 20:27:14 +00:00
|
|
|
write_count = strlen(data);
|
2013-01-24 16:03:20 +00:00
|
|
|
}
|
|
|
|
|
qemu-char: Saner naming of memchar stuff & doc fixes
New device, has never been released, so we can still improve things
without worrying about compatibility.
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. Therefore:
* Rename CirMemCharDriver to RingBufCharDriver, and call the thing a
"ringbuf" in the API.
* Rename QMP and HMP commands from memchar-FOO to ringbuf-FOO.
* Rename device parameter from maxcapacity to size (simple words are
good for you).
* Clearly mark the parameter as optional in documentation.
* Fix error reporting so that chardev-add reports to current monitor,
not stderr.
* Replace cirmem in C identifiers by ringbuf.
* Rework documentation. Document the impact of our crappy UTF-8
handling on reading.
* QMP examples that even work.
I could split this up into multiple commits, but they'd change the
same documentation lines multiple times. Not worth it.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-02-06 20:27:24 +00:00
|
|
|
ret = ringbuf_chr_write(chr, write_data, write_count);
|
2013-01-24 16:03:20 +00:00
|
|
|
|
2013-02-06 20:27:18 +00:00
|
|
|
if (write_data != (uint8_t *)data) {
|
|
|
|
g_free((void *)write_data);
|
|
|
|
}
|
|
|
|
|
2013-01-24 16:03:20 +00:00
|
|
|
if (ret < 0) {
|
|
|
|
error_setg(errp, "Failed to write to device %s", device);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
qemu-char: Saner naming of memchar stuff & doc fixes
New device, has never been released, so we can still improve things
without worrying about compatibility.
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. Therefore:
* Rename CirMemCharDriver to RingBufCharDriver, and call the thing a
"ringbuf" in the API.
* Rename QMP and HMP commands from memchar-FOO to ringbuf-FOO.
* Rename device parameter from maxcapacity to size (simple words are
good for you).
* Clearly mark the parameter as optional in documentation.
* Fix error reporting so that chardev-add reports to current monitor,
not stderr.
* Replace cirmem in C identifiers by ringbuf.
* Rework documentation. Document the impact of our crappy UTF-8
handling on reading.
* QMP examples that even work.
I could split this up into multiple commits, but they'd change the
same documentation lines multiple times. Not worth it.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-02-06 20:27:24 +00:00
|
|
|
char *qmp_ringbuf_read(const char *device, int64_t size,
|
2013-02-06 20:27:15 +00:00
|
|
|
bool has_format, enum DataFormat format,
|
|
|
|
Error **errp)
|
2013-01-24 16:03:21 +00:00
|
|
|
{
|
|
|
|
CharDriverState *chr;
|
2013-02-06 20:27:17 +00:00
|
|
|
uint8_t *read_data;
|
2013-01-24 16:03:21 +00:00
|
|
|
size_t count;
|
2013-02-06 20:27:15 +00:00
|
|
|
char *data;
|
2013-01-24 16:03:21 +00:00
|
|
|
|
|
|
|
chr = qemu_chr_find(device);
|
|
|
|
if (!chr) {
|
2013-02-06 20:27:16 +00:00
|
|
|
error_setg(errp, "Device '%s' not found", device);
|
2013-01-24 16:03:21 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
qemu-char: Saner naming of memchar stuff & doc fixes
New device, has never been released, so we can still improve things
without worrying about compatibility.
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. Therefore:
* Rename CirMemCharDriver to RingBufCharDriver, and call the thing a
"ringbuf" in the API.
* Rename QMP and HMP commands from memchar-FOO to ringbuf-FOO.
* Rename device parameter from maxcapacity to size (simple words are
good for you).
* Clearly mark the parameter as optional in documentation.
* Fix error reporting so that chardev-add reports to current monitor,
not stderr.
* Replace cirmem in C identifiers by ringbuf.
* Rework documentation. Document the impact of our crappy UTF-8
handling on reading.
* QMP examples that even work.
I could split this up into multiple commits, but they'd change the
same documentation lines multiple times. Not worth it.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-02-06 20:27:24 +00:00
|
|
|
if (!chr_is_ringbuf(chr)) {
|
|
|
|
error_setg(errp,"%s is not a ringbuf device", device);
|
2013-01-24 16:03:21 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (size <= 0) {
|
|
|
|
error_setg(errp, "size must be greater than zero");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
qemu-char: Saner naming of memchar stuff & doc fixes
New device, has never been released, so we can still improve things
without worrying about compatibility.
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. Therefore:
* Rename CirMemCharDriver to RingBufCharDriver, and call the thing a
"ringbuf" in the API.
* Rename QMP and HMP commands from memchar-FOO to ringbuf-FOO.
* Rename device parameter from maxcapacity to size (simple words are
good for you).
* Clearly mark the parameter as optional in documentation.
* Fix error reporting so that chardev-add reports to current monitor,
not stderr.
* Replace cirmem in C identifiers by ringbuf.
* Rework documentation. Document the impact of our crappy UTF-8
handling on reading.
* QMP examples that even work.
I could split this up into multiple commits, but they'd change the
same documentation lines multiple times. Not worth it.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-02-06 20:27:24 +00:00
|
|
|
count = ringbuf_count(chr);
|
2013-01-24 16:03:21 +00:00
|
|
|
size = size > count ? count : size;
|
2013-02-06 20:27:20 +00:00
|
|
|
read_data = g_malloc(size + 1);
|
2013-01-24 16:03:21 +00:00
|
|
|
|
qemu-char: Saner naming of memchar stuff & doc fixes
New device, has never been released, so we can still improve things
without worrying about compatibility.
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. Therefore:
* Rename CirMemCharDriver to RingBufCharDriver, and call the thing a
"ringbuf" in the API.
* Rename QMP and HMP commands from memchar-FOO to ringbuf-FOO.
* Rename device parameter from maxcapacity to size (simple words are
good for you).
* Clearly mark the parameter as optional in documentation.
* Fix error reporting so that chardev-add reports to current monitor,
not stderr.
* Replace cirmem in C identifiers by ringbuf.
* Rework documentation. Document the impact of our crappy UTF-8
handling on reading.
* QMP examples that even work.
I could split this up into multiple commits, but they'd change the
same documentation lines multiple times. Not worth it.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-02-06 20:27:24 +00:00
|
|
|
ringbuf_chr_read(chr, read_data, size);
|
2013-01-24 16:03:21 +00:00
|
|
|
|
|
|
|
if (has_format && (format == DATA_FORMAT_BASE64)) {
|
2013-02-06 20:27:15 +00:00
|
|
|
data = g_base64_encode(read_data, size);
|
2013-02-06 20:27:18 +00:00
|
|
|
g_free(read_data);
|
2013-01-24 16:03:21 +00:00
|
|
|
} else {
|
qemu-char: Saner naming of memchar stuff & doc fixes
New device, has never been released, so we can still improve things
without worrying about compatibility.
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. Therefore:
* Rename CirMemCharDriver to RingBufCharDriver, and call the thing a
"ringbuf" in the API.
* Rename QMP and HMP commands from memchar-FOO to ringbuf-FOO.
* Rename device parameter from maxcapacity to size (simple words are
good for you).
* Clearly mark the parameter as optional in documentation.
* Fix error reporting so that chardev-add reports to current monitor,
not stderr.
* Replace cirmem in C identifiers by ringbuf.
* Rework documentation. Document the impact of our crappy UTF-8
handling on reading.
* QMP examples that even work.
I could split this up into multiple commits, but they'd change the
same documentation lines multiple times. Not worth it.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-02-06 20:27:24 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2013-02-06 20:27:20 +00:00
|
|
|
read_data[size] = 0;
|
2013-02-06 20:27:15 +00:00
|
|
|
data = (char *)read_data;
|
2013-01-24 16:03:21 +00:00
|
|
|
}
|
|
|
|
|
2013-02-06 20:27:15 +00:00
|
|
|
return data;
|
2013-01-24 16:03:21 +00:00
|
|
|
}
|
|
|
|
|
2009-12-08 12:11:49 +00:00
|
|
|
QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
|
2009-09-10 08:58:35 +00:00
|
|
|
{
|
2009-09-10 08:58:49 +00:00
|
|
|
char host[65], port[33], width[8], height[8];
|
2009-09-10 08:58:42 +00:00
|
|
|
int pos;
|
2009-09-10 08:58:36 +00:00
|
|
|
const char *p;
|
2009-09-10 08:58:35 +00:00
|
|
|
QemuOpts *opts;
|
2012-03-20 18:51:57 +00:00
|
|
|
Error *local_err = NULL;
|
2009-09-10 08:58:35 +00:00
|
|
|
|
2012-03-20 18:51:57 +00:00
|
|
|
opts = qemu_opts_create(qemu_find_opts("chardev"), label, 1, &local_err);
|
|
|
|
if (error_is_set(&local_err)) {
|
|
|
|
qerror_report_err(local_err);
|
|
|
|
error_free(local_err);
|
2009-09-10 08:58:35 +00:00
|
|
|
return NULL;
|
2012-03-20 18:51:57 +00:00
|
|
|
}
|
2009-09-10 08:58:35 +00:00
|
|
|
|
2009-09-10 08:58:50 +00:00
|
|
|
if (strstart(filename, "mon:", &p)) {
|
|
|
|
filename = p;
|
|
|
|
qemu_opt_set(opts, "mux", "on");
|
|
|
|
}
|
|
|
|
|
2009-09-10 08:58:45 +00:00
|
|
|
if (strcmp(filename, "null") == 0 ||
|
|
|
|
strcmp(filename, "pty") == 0 ||
|
|
|
|
strcmp(filename, "msmouse") == 0 ||
|
2009-09-10 08:58:46 +00:00
|
|
|
strcmp(filename, "braille") == 0 ||
|
2009-09-10 08:58:45 +00:00
|
|
|
strcmp(filename, "stdio") == 0) {
|
2009-09-10 08:58:43 +00:00
|
|
|
qemu_opt_set(opts, "backend", filename);
|
2009-09-10 08:58:35 +00:00
|
|
|
return opts;
|
|
|
|
}
|
2009-09-10 08:58:49 +00:00
|
|
|
if (strstart(filename, "vc", &p)) {
|
|
|
|
qemu_opt_set(opts, "backend", "vc");
|
|
|
|
if (*p == ':') {
|
|
|
|
if (sscanf(p+1, "%8[0-9]x%8[0-9]", width, height) == 2) {
|
|
|
|
/* pixels */
|
|
|
|
qemu_opt_set(opts, "width", width);
|
|
|
|
qemu_opt_set(opts, "height", height);
|
|
|
|
} else if (sscanf(p+1, "%8[0-9]Cx%8[0-9]C", width, height) == 2) {
|
|
|
|
/* chars */
|
|
|
|
qemu_opt_set(opts, "cols", width);
|
|
|
|
qemu_opt_set(opts, "rows", height);
|
|
|
|
} else {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return opts;
|
|
|
|
}
|
2009-09-10 08:58:47 +00:00
|
|
|
if (strcmp(filename, "con:") == 0) {
|
|
|
|
qemu_opt_set(opts, "backend", "console");
|
|
|
|
return opts;
|
|
|
|
}
|
2009-09-10 08:58:48 +00:00
|
|
|
if (strstart(filename, "COM", NULL)) {
|
|
|
|
qemu_opt_set(opts, "backend", "serial");
|
|
|
|
qemu_opt_set(opts, "path", filename);
|
|
|
|
return opts;
|
|
|
|
}
|
2009-09-10 08:58:36 +00:00
|
|
|
if (strstart(filename, "file:", &p)) {
|
|
|
|
qemu_opt_set(opts, "backend", "file");
|
|
|
|
qemu_opt_set(opts, "path", p);
|
|
|
|
return opts;
|
|
|
|
}
|
|
|
|
if (strstart(filename, "pipe:", &p)) {
|
|
|
|
qemu_opt_set(opts, "backend", "pipe");
|
|
|
|
qemu_opt_set(opts, "path", p);
|
|
|
|
return opts;
|
|
|
|
}
|
2009-09-10 08:58:42 +00:00
|
|
|
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");
|
|
|
|
qemu_opt_set(opts, "host", host);
|
|
|
|
qemu_opt_set(opts, "port", port);
|
|
|
|
if (p[pos] == ',') {
|
|
|
|
if (qemu_opts_do_parse(opts, p+pos+1, NULL) != 0)
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
if (strstart(filename, "telnet:", &p))
|
|
|
|
qemu_opt_set(opts, "telnet", "on");
|
|
|
|
return opts;
|
|
|
|
}
|
2009-09-10 08:58:51 +00:00
|
|
|
if (strstart(filename, "udp:", &p)) {
|
|
|
|
qemu_opt_set(opts, "backend", "udp");
|
|
|
|
if (sscanf(p, "%64[^:]:%32[^@,]%n", host, port, &pos) < 2) {
|
|
|
|
host[0] = 0;
|
2010-03-07 10:28:48 +00:00
|
|
|
if (sscanf(p, ":%32[^@,]%n", port, &pos) < 1) {
|
2009-09-10 08:58:51 +00:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
qemu_opt_set(opts, "host", host);
|
|
|
|
qemu_opt_set(opts, "port", port);
|
|
|
|
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);
|
|
|
|
qemu_opt_set(opts, "localport", port);
|
|
|
|
}
|
|
|
|
return opts;
|
|
|
|
}
|
2009-09-10 08:58:42 +00:00
|
|
|
if (strstart(filename, "unix:", &p)) {
|
|
|
|
qemu_opt_set(opts, "backend", "socket");
|
|
|
|
if (qemu_opts_do_parse(opts, p, "path") != 0)
|
|
|
|
goto fail;
|
|
|
|
return opts;
|
|
|
|
}
|
2009-09-10 08:58:48 +00:00
|
|
|
if (strstart(filename, "/dev/parport", NULL) ||
|
|
|
|
strstart(filename, "/dev/ppi", NULL)) {
|
|
|
|
qemu_opt_set(opts, "backend", "parport");
|
|
|
|
qemu_opt_set(opts, "path", filename);
|
|
|
|
return opts;
|
|
|
|
}
|
|
|
|
if (strstart(filename, "/dev/", NULL)) {
|
|
|
|
qemu_opt_set(opts, "backend", "tty");
|
|
|
|
qemu_opt_set(opts, "path", filename);
|
|
|
|
return opts;
|
|
|
|
}
|
2009-09-10 08:58:35 +00:00
|
|
|
|
2009-09-10 08:58:42 +00:00
|
|
|
fail:
|
2009-09-10 08:58:35 +00:00
|
|
|
qemu_opts_del(opts);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-02-21 11:07:14 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2013-02-21 11:34:58 +00:00
|
|
|
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", display_type != DT_NOGRAPHIC);
|
|
|
|
}
|
|
|
|
|
2013-02-22 14:48:05 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2013-02-22 15:17:01 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2013-02-25 10:50:55 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2013-02-26 15:21:11 +00:00
|
|
|
static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
int val;
|
|
|
|
|
|
|
|
backend->memory = g_new0(ChardevRingbuf, 1);
|
|
|
|
|
|
|
|
val = qemu_opt_get_number(opts, "size", 0);
|
|
|
|
if (val != 0) {
|
|
|
|
backend->memory->has_size = true;
|
|
|
|
backend->memory->size = val;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-05 17:51:28 +00:00
|
|
|
typedef struct CharDriver {
|
2009-09-10 08:58:35 +00:00
|
|
|
const char *name;
|
2013-02-21 10:39:12 +00:00
|
|
|
/* old, pre qapi */
|
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 14:09:08 +00:00
|
|
|
CharDriverState *(*open)(QemuOpts *opts);
|
2013-02-21 10:39:12 +00:00
|
|
|
/* new, qapi-based */
|
|
|
|
int kind;
|
|
|
|
void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp);
|
2013-03-05 17:51:28 +00:00
|
|
|
} CharDriver;
|
|
|
|
|
|
|
|
static GSList *backends;
|
|
|
|
|
|
|
|
void register_char_driver(const char *name, CharDriverState *(*open)(QemuOpts *))
|
|
|
|
{
|
|
|
|
CharDriver *s;
|
|
|
|
|
|
|
|
s = g_malloc0(sizeof(*s));
|
|
|
|
s->name = g_strdup(name);
|
|
|
|
s->open = open;
|
|
|
|
|
|
|
|
backends = g_slist_append(backends, s);
|
|
|
|
}
|
2009-09-10 08:58:35 +00:00
|
|
|
|
2013-02-21 10:39:12 +00:00
|
|
|
void register_char_driver_qapi(const char *name, int 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);
|
|
|
|
}
|
|
|
|
|
2011-08-15 16:17:37 +00:00
|
|
|
CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
|
2012-10-15 07:28:05 +00:00
|
|
|
void (*init)(struct CharDriverState *s),
|
|
|
|
Error **errp)
|
2009-09-10 08:58:35 +00:00
|
|
|
{
|
2013-03-05 17:51:28 +00:00
|
|
|
CharDriver *cd;
|
2009-09-10 08:58:35 +00:00
|
|
|
CharDriverState *chr;
|
2013-03-05 17:51:28 +00:00
|
|
|
GSList *i;
|
2009-09-10 08:58:35 +00:00
|
|
|
|
|
|
|
if (qemu_opts_id(opts) == NULL) {
|
error: Strip trailing '\n' from error string arguments (again)
Commit 6daf194d and be62a2eb got rid of a bunch, but they keep coming
back. Tracked down with this Coccinelle semantic patch:
@r@
expression err, eno, cls, fmt;
position p;
@@
(
error_report(fmt, ...)@p
|
error_set(err, cls, fmt, ...)@p
|
error_set_errno(err, eno, cls, fmt, ...)@p
|
error_setg(err, fmt, ...)@p
|
error_setg_errno(err, eno, fmt, ...)@p
)
@script:python@
fmt << r.fmt;
p << r.p;
@@
if "\\n" in str(fmt):
print "%s:%s:%s:%s" % (p[0].file, p[0].line, p[0].column, fmt)
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-id: 1360354939-10994-4-git-send-email-armbru@redhat.com
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-02-08 20:22:16 +00:00
|
|
|
error_setg(errp, "chardev: no id specified");
|
2012-10-15 07:30:59 +00:00
|
|
|
goto err;
|
2009-09-10 08:58:35 +00:00
|
|
|
}
|
|
|
|
|
2011-01-22 13:07:26 +00:00
|
|
|
if (qemu_opt_get(opts, "backend") == NULL) {
|
error: Strip trailing '\n' from error string arguments (again)
Commit 6daf194d and be62a2eb got rid of a bunch, but they keep coming
back. Tracked down with this Coccinelle semantic patch:
@r@
expression err, eno, cls, fmt;
position p;
@@
(
error_report(fmt, ...)@p
|
error_set(err, cls, fmt, ...)@p
|
error_set_errno(err, eno, cls, fmt, ...)@p
|
error_setg(err, fmt, ...)@p
|
error_setg_errno(err, eno, fmt, ...)@p
)
@script:python@
fmt << r.fmt;
p << r.p;
@@
if "\\n" in str(fmt):
print "%s:%s:%s:%s" % (p[0].file, p[0].line, p[0].column, fmt)
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-id: 1360354939-10994-4-git-send-email-armbru@redhat.com
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-02-08 20:22:16 +00:00
|
|
|
error_setg(errp, "chardev: \"%s\" missing backend",
|
2012-10-15 07:28:05 +00:00
|
|
|
qemu_opts_id(opts));
|
2012-10-15 07:30:59 +00:00
|
|
|
goto err;
|
2011-01-22 13:07:26 +00:00
|
|
|
}
|
2013-03-05 17:51:28 +00:00
|
|
|
for (i = backends; i; i = i->next) {
|
|
|
|
cd = i->data;
|
|
|
|
|
|
|
|
if (strcmp(cd->name, qemu_opt_get(opts, "backend")) == 0) {
|
2009-09-10 08:58:35 +00:00
|
|
|
break;
|
2013-03-05 17:51:28 +00:00
|
|
|
}
|
2009-09-10 08:58:35 +00:00
|
|
|
}
|
2013-03-05 17:51:28 +00:00
|
|
|
if (i == NULL) {
|
error: Strip trailing '\n' from error string arguments (again)
Commit 6daf194d and be62a2eb got rid of a bunch, but they keep coming
back. Tracked down with this Coccinelle semantic patch:
@r@
expression err, eno, cls, fmt;
position p;
@@
(
error_report(fmt, ...)@p
|
error_set(err, cls, fmt, ...)@p
|
error_set_errno(err, eno, cls, fmt, ...)@p
|
error_setg(err, fmt, ...)@p
|
error_setg_errno(err, eno, fmt, ...)@p
)
@script:python@
fmt << r.fmt;
p << r.p;
@@
if "\\n" in str(fmt):
print "%s:%s:%s:%s" % (p[0].file, p[0].line, p[0].column, fmt)
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-id: 1360354939-10994-4-git-send-email-armbru@redhat.com
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-02-08 20:22:16 +00:00
|
|
|
error_setg(errp, "chardev: backend \"%s\" not found",
|
2012-10-15 07:28:05 +00:00
|
|
|
qemu_opt_get(opts, "backend"));
|
2013-03-05 17:51:28 +00:00
|
|
|
return NULL;
|
2009-09-10 08:58:35 +00:00
|
|
|
}
|
|
|
|
|
2013-02-21 10:39:12 +00:00
|
|
|
if (!cd->open) {
|
|
|
|
/* using new, qapi init */
|
|
|
|
ChardevBackend *backend = g_new0(ChardevBackend, 1);
|
|
|
|
ChardevReturn *ret = NULL;
|
|
|
|
const char *id = qemu_opts_id(opts);
|
2013-02-21 15:16:42 +00:00
|
|
|
const char *bid = NULL;
|
|
|
|
|
|
|
|
if (qemu_opt_get_bool(opts, "mux", 0)) {
|
|
|
|
bid = g_strdup_printf("%s-base", id);
|
|
|
|
}
|
2013-02-21 10:39:12 +00:00
|
|
|
|
|
|
|
chr = NULL;
|
|
|
|
backend->kind = cd->kind;
|
|
|
|
if (cd->parse) {
|
|
|
|
cd->parse(opts, backend, errp);
|
|
|
|
if (error_is_set(errp)) {
|
|
|
|
goto qapi_out;
|
|
|
|
}
|
|
|
|
}
|
2013-02-21 15:16:42 +00:00
|
|
|
ret = qmp_chardev_add(bid ? bid : id, backend, errp);
|
2013-02-21 10:39:12 +00:00
|
|
|
if (error_is_set(errp)) {
|
|
|
|
goto qapi_out;
|
|
|
|
}
|
2013-02-21 15:16:42 +00:00
|
|
|
|
|
|
|
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 (error_is_set(errp)) {
|
|
|
|
goto qapi_out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-21 10:39:12 +00:00
|
|
|
chr = qemu_chr_find(id);
|
|
|
|
|
|
|
|
qapi_out:
|
|
|
|
qapi_free_ChardevBackend(backend);
|
|
|
|
qapi_free_ChardevReturn(ret);
|
|
|
|
return chr;
|
|
|
|
}
|
|
|
|
|
2013-03-05 17:51:28 +00:00
|
|
|
chr = cd->open(opts);
|
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 14:09:08 +00:00
|
|
|
if (!chr) {
|
error: Strip trailing '\n' from error string arguments (again)
Commit 6daf194d and be62a2eb got rid of a bunch, but they keep coming
back. Tracked down with this Coccinelle semantic patch:
@r@
expression err, eno, cls, fmt;
position p;
@@
(
error_report(fmt, ...)@p
|
error_set(err, cls, fmt, ...)@p
|
error_set_errno(err, eno, cls, fmt, ...)@p
|
error_setg(err, fmt, ...)@p
|
error_setg_errno(err, eno, fmt, ...)@p
)
@script:python@
fmt << r.fmt;
p << r.p;
@@
if "\\n" in str(fmt):
print "%s:%s:%s:%s" % (p[0].file, p[0].line, p[0].column, fmt)
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-id: 1360354939-10994-4-git-send-email-armbru@redhat.com
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-02-08 20:22:16 +00:00
|
|
|
error_setg(errp, "chardev: opening backend \"%s\" failed",
|
2012-10-15 07:28:05 +00:00
|
|
|
qemu_opt_get(opts, "backend"));
|
2012-10-15 07:30:59 +00:00
|
|
|
goto err;
|
2009-09-10 08:58:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!chr->filename)
|
2011-08-21 03:09:37 +00:00
|
|
|
chr->filename = g_strdup(qemu_opt_get(opts, "backend"));
|
2009-09-10 08:58:35 +00:00
|
|
|
chr->init = init;
|
2009-09-12 07:36:22 +00:00
|
|
|
QTAILQ_INSERT_TAIL(&chardevs, chr, next);
|
2009-09-10 08:58:50 +00:00
|
|
|
|
|
|
|
if (qemu_opt_get_bool(opts, "mux", 0)) {
|
|
|
|
CharDriverState *base = chr;
|
|
|
|
int len = strlen(qemu_opts_id(opts)) + 6;
|
2011-08-21 03:09:37 +00:00
|
|
|
base->label = g_malloc(len);
|
2009-09-10 08:58:50 +00:00
|
|
|
snprintf(base->label, len, "%s-base", qemu_opts_id(opts));
|
|
|
|
chr = qemu_chr_open_mux(base);
|
|
|
|
chr->filename = base->filename;
|
2011-04-26 10:19:26 +00:00
|
|
|
chr->avail_connections = MAX_MUX;
|
2009-09-12 07:36:22 +00:00
|
|
|
QTAILQ_INSERT_TAIL(&chardevs, chr, next);
|
2011-04-26 10:19:26 +00:00
|
|
|
} else {
|
|
|
|
chr->avail_connections = 1;
|
2009-09-10 08:58:50 +00:00
|
|
|
}
|
2011-08-21 03:09:37 +00:00
|
|
|
chr->label = g_strdup(qemu_opts_id(opts));
|
2012-10-15 07:30:59 +00:00
|
|
|
chr->opts = opts;
|
2009-09-10 08:58:35 +00:00
|
|
|
return chr;
|
2012-10-15 07:30:59 +00:00
|
|
|
|
|
|
|
err:
|
|
|
|
qemu_opts_del(opts);
|
|
|
|
return NULL;
|
2009-09-10 08:58:35 +00:00
|
|
|
}
|
|
|
|
|
2011-08-15 16:17:36 +00:00
|
|
|
CharDriverState *qemu_chr_new(const char *label, const char *filename, void (*init)(struct CharDriverState *s))
|
2008-10-31 18:49:55 +00:00
|
|
|
{
|
|
|
|
const char *p;
|
|
|
|
CharDriverState *chr;
|
2009-09-10 08:58:35 +00:00
|
|
|
QemuOpts *opts;
|
2012-10-15 07:28:05 +00:00
|
|
|
Error *err = NULL;
|
2009-09-10 08:58:35 +00:00
|
|
|
|
2009-09-10 08:58:52 +00:00
|
|
|
if (strstart(filename, "chardev:", &p)) {
|
|
|
|
return qemu_chr_find(p);
|
|
|
|
}
|
|
|
|
|
2009-09-10 08:58:35 +00:00
|
|
|
opts = qemu_chr_parse_compat(label, filename);
|
2009-09-10 08:58:51 +00:00
|
|
|
if (!opts)
|
|
|
|
return NULL;
|
2008-10-31 18:49:55 +00:00
|
|
|
|
2012-10-15 07:28:05 +00:00
|
|
|
chr = qemu_chr_new_from_opts(opts, init, &err);
|
|
|
|
if (error_is_set(&err)) {
|
|
|
|
fprintf(stderr, "%s\n", error_get_pretty(err));
|
|
|
|
error_free(err);
|
|
|
|
}
|
2009-09-10 08:58:51 +00:00
|
|
|
if (chr && qemu_opt_get_bool(opts, "mux", 0)) {
|
2013-03-27 19:29:40 +00:00
|
|
|
qemu_chr_fe_claim_no_fail(chr);
|
2009-09-10 08:58:51 +00:00
|
|
|
monitor_init(chr, MONITOR_USE_READLINE);
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
return chr;
|
|
|
|
}
|
|
|
|
|
2011-08-15 16:17:35 +00:00
|
|
|
void qemu_chr_fe_set_echo(struct CharDriverState *chr, bool echo)
|
2010-12-23 12:42:48 +00:00
|
|
|
{
|
|
|
|
if (chr->chr_set_echo) {
|
|
|
|
chr->chr_set_echo(chr, echo);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-26 10:07:57 +00:00
|
|
|
void qemu_chr_fe_set_open(struct CharDriverState *chr, int fe_open)
|
2011-03-24 10:12:02 +00:00
|
|
|
{
|
2013-03-26 10:07:57 +00:00
|
|
|
if (chr->fe_open == fe_open) {
|
2013-03-26 10:07:55 +00:00
|
|
|
return;
|
|
|
|
}
|
2013-03-26 10:07:57 +00:00
|
|
|
chr->fe_open = fe_open;
|
2013-03-26 10:07:58 +00:00
|
|
|
if (chr->chr_set_fe_open) {
|
|
|
|
chr->chr_set_fe_open(chr, fe_open);
|
2011-03-24 10:12:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-19 12:38:09 +00:00
|
|
|
int qemu_chr_fe_add_watch(CharDriverState *s, GIOCondition cond,
|
|
|
|
GIOFunc func, void *user_data)
|
2013-03-05 17:51:23 +00:00
|
|
|
{
|
|
|
|
GSource *src;
|
|
|
|
guint tag;
|
|
|
|
|
|
|
|
if (s->chr_add_watch == NULL) {
|
|
|
|
return -ENOSYS;
|
|
|
|
}
|
|
|
|
|
|
|
|
src = s->chr_add_watch(s, cond);
|
|
|
|
g_source_set_callback(src, (GSourceFunc)func, user_data, NULL);
|
|
|
|
tag = g_source_attach(src, NULL);
|
|
|
|
g_source_unref(src);
|
|
|
|
|
|
|
|
return tag;
|
|
|
|
}
|
|
|
|
|
2013-03-27 19:29:39 +00:00
|
|
|
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++;
|
|
|
|
}
|
|
|
|
|
2011-08-15 16:17:38 +00:00
|
|
|
void qemu_chr_delete(CharDriverState *chr)
|
2008-10-31 18:49:55 +00:00
|
|
|
{
|
2009-09-12 07:36:22 +00:00
|
|
|
QTAILQ_REMOVE(&chardevs, chr, next);
|
2012-10-15 07:30:59 +00:00
|
|
|
if (chr->chr_close) {
|
2008-10-31 18:49:55 +00:00
|
|
|
chr->chr_close(chr);
|
2012-10-15 07:30:59 +00:00
|
|
|
}
|
2011-08-21 03:09:37 +00:00
|
|
|
g_free(chr->filename);
|
|
|
|
g_free(chr->label);
|
2012-10-15 07:30:59 +00:00
|
|
|
if (chr->opts) {
|
|
|
|
qemu_opts_del(chr->opts);
|
|
|
|
}
|
2011-08-21 03:09:37 +00:00
|
|
|
g_free(chr);
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
|
|
|
|
2011-09-14 19:05:49 +00:00
|
|
|
ChardevInfoList *qmp_query_chardev(Error **errp)
|
2008-10-31 18:49:55 +00:00
|
|
|
{
|
2011-09-14 19:05:49 +00:00
|
|
|
ChardevInfoList *chr_list = NULL;
|
2008-10-31 18:49:55 +00:00
|
|
|
CharDriverState *chr;
|
|
|
|
|
2009-09-12 07:36:22 +00:00
|
|
|
QTAILQ_FOREACH(chr, &chardevs, next) {
|
2011-09-14 19:05:49 +00:00
|
|
|
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->next = chr_list;
|
|
|
|
chr_list = info;
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
2009-12-10 19:16:08 +00:00
|
|
|
|
2011-09-14 19:05:49 +00:00
|
|
|
return chr_list;
|
2008-10-31 18:49:55 +00:00
|
|
|
}
|
2009-09-10 08:58:52 +00:00
|
|
|
|
|
|
|
CharDriverState *qemu_chr_find(const char *name)
|
|
|
|
{
|
|
|
|
CharDriverState *chr;
|
|
|
|
|
2009-09-12 07:36:22 +00:00
|
|
|
QTAILQ_FOREACH(chr, &chardevs, next) {
|
2009-09-10 08:58:52 +00:00
|
|
|
if (strcmp(chr->label, name) != 0)
|
|
|
|
continue;
|
|
|
|
return chr;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
2011-12-22 21:29:25 +00:00
|
|
|
|
|
|
|
/* Get a character (serial) device interface. */
|
|
|
|
CharDriverState *qemu_char_get_next_serial(void)
|
|
|
|
{
|
|
|
|
static int next_serial;
|
2013-03-27 19:29:40 +00:00
|
|
|
CharDriverState *chr;
|
2011-12-22 21:29:25 +00:00
|
|
|
|
|
|
|
/* FIXME: This function needs to go away: use chardev properties! */
|
2013-03-27 19:29:40 +00:00
|
|
|
|
|
|
|
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;
|
2011-12-22 21:29:25 +00:00
|
|
|
}
|
|
|
|
|
2012-11-26 15:03:42 +00:00
|
|
|
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 = "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,
|
2013-01-24 16:03:19 +00:00
|
|
|
},{
|
qemu-char: Saner naming of memchar stuff & doc fixes
New device, has never been released, so we can still improve things
without worrying about compatibility.
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. Therefore:
* Rename CirMemCharDriver to RingBufCharDriver, and call the thing a
"ringbuf" in the API.
* Rename QMP and HMP commands from memchar-FOO to ringbuf-FOO.
* Rename device parameter from maxcapacity to size (simple words are
good for you).
* Clearly mark the parameter as optional in documentation.
* Fix error reporting so that chardev-add reports to current monitor,
not stderr.
* Replace cirmem in C identifiers by ringbuf.
* Rework documentation. Document the impact of our crappy UTF-8
handling on reading.
* QMP examples that even work.
I could split this up into multiple commits, but they'd change the
same documentation lines multiple times. Not worth it.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-02-06 20:27:24 +00:00
|
|
|
.name = "size",
|
2013-02-06 20:27:25 +00:00
|
|
|
.type = QEMU_OPT_SIZE,
|
2012-11-26 15:03:42 +00:00
|
|
|
},
|
|
|
|
{ /* end of list */ }
|
|
|
|
},
|
|
|
|
};
|
2012-12-19 09:33:56 +00:00
|
|
|
|
2012-12-19 12:13:57 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
|
|
|
|
static CharDriverState *qmp_chardev_open_file(ChardevFile *file, Error **errp)
|
|
|
|
{
|
|
|
|
HANDLE out;
|
|
|
|
|
|
|
|
if (file->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);
|
|
|
|
}
|
|
|
|
|
2013-02-13 14:54:16 +00:00
|
|
|
static CharDriverState *qmp_chardev_open_serial(ChardevHostdev *serial,
|
|
|
|
Error **errp)
|
2012-12-19 12:50:29 +00:00
|
|
|
{
|
2013-02-13 14:54:16 +00:00
|
|
|
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;
|
2012-12-19 12:50:29 +00:00
|
|
|
}
|
|
|
|
|
2012-12-19 12:13:57 +00:00
|
|
|
#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(errp, "open %s: %s", src, strerror(errno));
|
|
|
|
}
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
static CharDriverState *qmp_chardev_open_file(ChardevFile *file, Error **errp)
|
|
|
|
{
|
|
|
|
int flags, in = -1, out = -1;
|
|
|
|
|
|
|
|
flags = O_WRONLY | O_TRUNC | O_CREAT | O_BINARY;
|
|
|
|
out = qmp_chardev_open_file_source(file->out, flags, errp);
|
|
|
|
if (error_is_set(errp)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (file->in) {
|
|
|
|
flags = O_RDONLY;
|
|
|
|
in = qmp_chardev_open_file_source(file->in, flags, errp);
|
|
|
|
if (error_is_set(errp)) {
|
|
|
|
qemu_close(out);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return qemu_chr_open_fd(in, out);
|
|
|
|
}
|
|
|
|
|
2013-02-13 14:54:16 +00:00
|
|
|
static CharDriverState *qmp_chardev_open_serial(ChardevHostdev *serial,
|
|
|
|
Error **errp)
|
2012-12-19 12:50:29 +00:00
|
|
|
{
|
|
|
|
#ifdef HAVE_CHARDEV_TTY
|
2013-02-13 14:54:16 +00:00
|
|
|
int fd;
|
|
|
|
|
|
|
|
fd = qmp_chardev_open_file_source(serial->device, O_RDWR, errp);
|
|
|
|
if (error_is_set(errp)) {
|
|
|
|
return NULL;
|
2013-01-24 16:14:39 +00:00
|
|
|
}
|
2013-03-27 09:10:43 +00:00
|
|
|
qemu_set_nonblock(fd);
|
2013-02-13 14:54:16 +00:00
|
|
|
return qemu_chr_open_tty_fd(fd);
|
|
|
|
#else
|
|
|
|
error_setg(errp, "character device backend type 'serial' not supported");
|
|
|
|
return NULL;
|
2013-01-10 13:20:58 +00:00
|
|
|
#endif
|
2013-02-13 14:54:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static CharDriverState *qmp_chardev_open_parallel(ChardevHostdev *parallel,
|
|
|
|
Error **errp)
|
|
|
|
{
|
2013-01-10 13:20:58 +00:00
|
|
|
#ifdef HAVE_CHARDEV_PARPORT
|
2013-02-13 14:54:16 +00:00
|
|
|
int fd;
|
|
|
|
|
|
|
|
fd = qmp_chardev_open_file_source(parallel->device, O_RDWR, errp);
|
|
|
|
if (error_is_set(errp)) {
|
2012-12-19 12:50:29 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2013-02-13 14:54:16 +00:00
|
|
|
return qemu_chr_open_pp_fd(fd);
|
|
|
|
#else
|
|
|
|
error_setg(errp, "character device backend type 'parallel' not supported");
|
|
|
|
return NULL;
|
|
|
|
#endif
|
2012-12-19 12:50:29 +00:00
|
|
|
}
|
|
|
|
|
2012-12-19 12:13:57 +00:00
|
|
|
#endif /* WIN32 */
|
|
|
|
|
2012-12-20 12:53:12 +00:00
|
|
|
static CharDriverState *qmp_chardev_open_socket(ChardevSocket *sock,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
if (is_listen) {
|
|
|
|
fd = socket_listen(addr, errp);
|
|
|
|
} else {
|
|
|
|
fd = socket_connect(addr, errp, NULL, NULL);
|
|
|
|
}
|
|
|
|
if (error_is_set(errp)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return qemu_chr_open_socket_fd(fd, do_nodelay, is_listen,
|
|
|
|
is_telnet, is_waitconnect, errp);
|
|
|
|
}
|
|
|
|
|
2013-02-27 13:10:47 +00:00
|
|
|
static CharDriverState *qmp_chardev_open_dgram(ChardevDgram *dgram,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
fd = socket_dgram(dgram->remote, dgram->local, errp);
|
|
|
|
if (error_is_set(errp)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return qemu_chr_open_udp_fd(fd);
|
|
|
|
}
|
|
|
|
|
2012-12-19 09:33:56 +00:00
|
|
|
ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
ChardevReturn *ret = g_new0(ChardevReturn, 1);
|
2013-02-21 15:16:42 +00:00
|
|
|
CharDriverState *base, *chr = NULL;
|
2012-12-19 09:33:56 +00:00
|
|
|
|
|
|
|
chr = qemu_chr_find(id);
|
|
|
|
if (chr) {
|
|
|
|
error_setg(errp, "Chardev '%s' already exists", id);
|
|
|
|
g_free(ret);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (backend->kind) {
|
2012-12-19 12:13:57 +00:00
|
|
|
case CHARDEV_BACKEND_KIND_FILE:
|
|
|
|
chr = qmp_chardev_open_file(backend->file, errp);
|
|
|
|
break;
|
2013-02-13 14:54:16 +00:00
|
|
|
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);
|
2012-12-19 12:50:29 +00:00
|
|
|
break;
|
2013-02-25 10:50:55 +00:00
|
|
|
case CHARDEV_BACKEND_KIND_PIPE:
|
|
|
|
chr = qemu_chr_open_pipe(backend->pipe);
|
|
|
|
break;
|
2012-12-20 12:53:12 +00:00
|
|
|
case CHARDEV_BACKEND_KIND_SOCKET:
|
|
|
|
chr = qmp_chardev_open_socket(backend->socket, errp);
|
|
|
|
break;
|
2013-02-27 13:10:47 +00:00
|
|
|
case CHARDEV_BACKEND_KIND_DGRAM:
|
|
|
|
chr = qmp_chardev_open_dgram(backend->dgram, errp);
|
|
|
|
break;
|
2012-12-20 13:39:13 +00:00
|
|
|
#ifdef HAVE_CHARDEV_TTY
|
|
|
|
case CHARDEV_BACKEND_KIND_PTY:
|
2013-02-25 09:16:46 +00:00
|
|
|
chr = qemu_chr_open_pty(id, ret);
|
2012-12-20 13:39:13 +00:00
|
|
|
break;
|
|
|
|
#endif
|
2012-12-19 09:33:56 +00:00
|
|
|
case CHARDEV_BACKEND_KIND_NULL:
|
2013-02-21 10:41:26 +00:00
|
|
|
chr = qemu_chr_open_null();
|
2012-12-19 09:33:56 +00:00
|
|
|
break;
|
2013-02-21 15:16:42 +00:00
|
|
|
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;
|
2013-02-21 10:58:44 +00:00
|
|
|
case CHARDEV_BACKEND_KIND_MSMOUSE:
|
|
|
|
chr = qemu_chr_open_msmouse();
|
|
|
|
break;
|
2013-02-21 11:56:10 +00:00
|
|
|
#ifdef CONFIG_BRLAPI
|
|
|
|
case CHARDEV_BACKEND_KIND_BRAILLE:
|
|
|
|
chr = chr_baum_init();
|
|
|
|
break;
|
|
|
|
#endif
|
2013-02-21 11:34:58 +00:00
|
|
|
case CHARDEV_BACKEND_KIND_STDIO:
|
|
|
|
chr = qemu_chr_open_stdio(backend->stdio);
|
|
|
|
break;
|
2013-02-25 10:48:06 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
case CHARDEV_BACKEND_KIND_CONSOLE:
|
|
|
|
chr = qemu_chr_open_win_con();
|
|
|
|
break;
|
2013-02-25 11:39:06 +00:00
|
|
|
#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;
|
2013-02-25 10:48:06 +00:00
|
|
|
#endif
|
2013-02-25 14:52:32 +00:00
|
|
|
case CHARDEV_BACKEND_KIND_VC:
|
|
|
|
chr = vc_init(backend->vc);
|
|
|
|
break;
|
2013-02-26 15:21:11 +00:00
|
|
|
case CHARDEV_BACKEND_KIND_MEMORY:
|
|
|
|
chr = qemu_chr_open_ringbuf(backend->memory, errp);
|
|
|
|
break;
|
2012-12-19 09:33:56 +00:00
|
|
|
default:
|
|
|
|
error_setg(errp, "unknown chardev backend (%d)", backend->kind);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (chr == NULL && !error_is_set(errp)) {
|
|
|
|
error_setg(errp, "Failed to create chardev");
|
|
|
|
}
|
|
|
|
if (chr) {
|
|
|
|
chr->label = g_strdup(id);
|
2013-02-21 15:16:42 +00:00
|
|
|
chr->avail_connections =
|
|
|
|
(backend->kind == CHARDEV_BACKEND_KIND_MUX) ? MAX_MUX : 1;
|
2012-12-19 09:33:56 +00:00
|
|
|
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 (NULL == chr) {
|
|
|
|
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);
|
|
|
|
}
|
2013-03-05 17:51:28 +00:00
|
|
|
|
|
|
|
static void register_types(void)
|
|
|
|
{
|
2013-02-21 10:41:26 +00:00
|
|
|
register_char_driver_qapi("null", CHARDEV_BACKEND_KIND_NULL, NULL);
|
2013-03-05 17:51:28 +00:00
|
|
|
register_char_driver("socket", qemu_chr_open_socket);
|
|
|
|
register_char_driver("udp", qemu_chr_open_udp);
|
2013-02-26 15:21:11 +00:00
|
|
|
register_char_driver_qapi("memory", CHARDEV_BACKEND_KIND_MEMORY,
|
|
|
|
qemu_chr_parse_ringbuf);
|
2013-02-21 11:07:14 +00:00
|
|
|
register_char_driver_qapi("file", CHARDEV_BACKEND_KIND_FILE,
|
|
|
|
qemu_chr_parse_file_out);
|
2013-02-21 11:34:58 +00:00
|
|
|
register_char_driver_qapi("stdio", CHARDEV_BACKEND_KIND_STDIO,
|
|
|
|
qemu_chr_parse_stdio);
|
2013-02-22 14:48:05 +00:00
|
|
|
register_char_driver_qapi("serial", CHARDEV_BACKEND_KIND_SERIAL,
|
|
|
|
qemu_chr_parse_serial);
|
|
|
|
register_char_driver_qapi("tty", CHARDEV_BACKEND_KIND_SERIAL,
|
|
|
|
qemu_chr_parse_serial);
|
2013-02-22 15:17:01 +00:00
|
|
|
register_char_driver_qapi("parallel", CHARDEV_BACKEND_KIND_PARALLEL,
|
|
|
|
qemu_chr_parse_parallel);
|
|
|
|
register_char_driver_qapi("parport", CHARDEV_BACKEND_KIND_PARALLEL,
|
|
|
|
qemu_chr_parse_parallel);
|
2013-02-25 09:16:46 +00:00
|
|
|
register_char_driver_qapi("pty", CHARDEV_BACKEND_KIND_PTY, NULL);
|
2013-02-25 10:48:06 +00:00
|
|
|
register_char_driver_qapi("console", CHARDEV_BACKEND_KIND_CONSOLE, NULL);
|
2013-02-25 10:50:55 +00:00
|
|
|
register_char_driver_qapi("pipe", CHARDEV_BACKEND_KIND_PIPE,
|
|
|
|
qemu_chr_parse_pipe);
|
2013-03-05 17:51:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type_init(register_types);
|