2008-10-31 19:49:55 +01: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 18:19:49 +01:00
|
|
|
#include "monitor/monitor.h"
|
2012-12-17 18:20:04 +01:00
|
|
|
#include "sysemu/sysemu.h"
|
2015-03-17 18:29:20 +01:00
|
|
|
#include "qemu/error-report.h"
|
2012-12-17 18:20:00 +01:00
|
|
|
#include "qemu/timer.h"
|
2013-04-08 16:55:25 +02:00
|
|
|
#include "sysemu/char.h"
|
2008-11-01 01:53:09 +01:00
|
|
|
#include "hw/usb.h"
|
2011-09-14 21:05:49 +02:00
|
|
|
#include "qmp-commands.h"
|
2014-10-02 18:17:35 +02:00
|
|
|
#include "qapi/qmp-input-visitor.h"
|
|
|
|
#include "qapi/qmp-output-visitor.h"
|
|
|
|
#include "qapi-visit.h"
|
2008-10-31 19:49:55 +01: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 17:55:48 +01:00
|
|
|
#include <sys/resource.h>
|
2008-10-31 19:49:55 +01:00
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <netinet/in.h>
|
2008-11-07 17:55:48 +01:00
|
|
|
#include <net/if.h>
|
|
|
|
#include <arpa/inet.h>
|
2008-10-31 19:49:55 +01:00
|
|
|
#include <netdb.h>
|
|
|
|
#include <sys/select.h>
|
2009-07-27 16:12:56 +02:00
|
|
|
#ifdef CONFIG_BSD
|
2008-10-31 19:49:55 +01:00
|
|
|
#include <sys/stat.h>
|
do not include <libutil.h> needlessly or if it doesn't exist
<libutil.h> and <util.h> on *BSD (some have one, some another)
were #included just for openpty() declaration. The only file
where this function is actually used is qemu-char.c.
In vl.c and net/tap-bsd.c, none of functions declared in libutil.h
(login logout logwtmp timdomain openpty forkpty uu_lock realhostname
fparseln and a few others depending on version) are used.
Initially the code which is currently in qemu-char.c was in vl.c,
it has been removed into separate file in commit 0e82f34d077dc2542
Fri Oct 31 18:44:40 2008, but the #includes were left in vl.c.
So with vl.c, we just remove includes - libutil.h, util.h and
pty.h (which declares only openpty() and forkpty()) from there.
The code in net/tap-bsd.c, which come from net/tap.c, had this
commit 5281d757efa6e40d74ce124be048b08d43887555
Author: Mark McLoughlin <markmc@redhat.com>
Date: Thu Oct 22 17:49:07 2009 +0100
net: split all the tap code out into net/tap.c
Note this commit not only moved stuff out of net.c to net/tap.c,
but also rewrote large portions of the tap code, and added these
completely unnecessary #includes -- as usual, I question why such
a misleading commit messages are allowed.
Again, no functions defined in libutil.h or util.h on *BSD are
used by neither net/tap.c nor net/tap-bsd.c. Removing them.
And finally, the only real user for these #includes, qemu-char.c,
which actually uses openpty(). There, the #ifdef logic is wrong.
A GLIBC-based system has <pty.h>, even if it is a variant of *BSD.
So __GLIBC__ should be checked first, and instead of trying to
include <libutil.h> or <util.h>, we include <pty.h>. If it is not
GLIBC-based, we check for variations between <*util.h> as before.
This patch fixes build of qemu 1.1 on Debian/kFreebsd (well, one
of the two problems): it is a distribution with a FreeBSD kernel,
so it #defines at least __FreeBSD_kernel__, but since it is based
on GLIBC, it has <pty.h>, but current version does not have neither
<util.h> nor <libutil.h>, which the code tries to include 3 times
but uses only once.
Signed-off-By: Michael Tokarev <mjt@tls.msk.ru>
Cc: Aurelien Jarno <aurelien@aurel32.net>
Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
2012-06-02 21:43:33 +02:00
|
|
|
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
|
|
|
#include <dev/ppbus/ppi.h>
|
|
|
|
#include <dev/ppbus/ppbconf.h>
|
2009-03-07 21:06:23 +01:00
|
|
|
#elif defined(__DragonFly__)
|
|
|
|
#include <dev/misc/ppi/ppi.h>
|
|
|
|
#include <bus/ppbus/ppbconf.h>
|
2008-10-31 19:49:55 +01:00
|
|
|
#endif
|
2009-11-30 15:42:59 +01:00
|
|
|
#else
|
2008-10-31 19:49:55 +01:00
|
|
|
#ifdef __linux__
|
|
|
|
#include <linux/ppdev.h>
|
|
|
|
#include <linux/parport.h>
|
|
|
|
#endif
|
|
|
|
#ifdef __sun__
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/ethernet.h>
|
|
|
|
#include <sys/sockio.h>
|
|
|
|
#include <netinet/arp.h>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <netinet/in_systm.h>
|
|
|
|
#include <netinet/ip.h>
|
|
|
|
#include <netinet/ip_icmp.h> // must come after ip.h
|
|
|
|
#include <netinet/udp.h>
|
|
|
|
#include <netinet/tcp.h>
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
2012-12-17 18:20:00 +01:00
|
|
|
#include "qemu/sockets.h"
|
2011-01-19 09:49:50 +01:00
|
|
|
#include "ui/qemu-spice.h"
|
2008-10-31 19:49:55 +01:00
|
|
|
|
2009-11-03 15:29:54 +01:00
|
|
|
#define READ_BUF_LEN 4096
|
2014-05-27 14:03:48 +02:00
|
|
|
#define READ_RETRIES 10
|
2014-10-02 18:17:33 +02:00
|
|
|
#define CHR_MAX_FILENAME_SIZE 256
|
2014-11-02 17:48:32 +01:00
|
|
|
#define TCP_MAX_FDS 16
|
2009-11-03 15:29:54 +01:00
|
|
|
|
2014-10-02 18:17:35 +02:00
|
|
|
/***********************************************************/
|
|
|
|
/* Socket address helpers */
|
|
|
|
|
2014-10-02 18:17:36 +02:00
|
|
|
static int SocketAddress_to_str(char *dest, int max_len,
|
|
|
|
const char *prefix, SocketAddress *addr,
|
|
|
|
bool is_listen, bool is_telnet)
|
|
|
|
{
|
2015-10-26 23:34:57 +01:00
|
|
|
switch (addr->type) {
|
2014-10-02 18:17:36 +02:00
|
|
|
case SOCKET_ADDRESS_KIND_INET:
|
|
|
|
return snprintf(dest, max_len, "%s%s:%s:%s%s", prefix,
|
2015-10-26 23:34:57 +01:00
|
|
|
is_telnet ? "telnet" : "tcp", addr->u.inet->host,
|
|
|
|
addr->u.inet->port, is_listen ? ",server" : "");
|
2014-10-02 18:17:36 +02:00
|
|
|
break;
|
|
|
|
case SOCKET_ADDRESS_KIND_UNIX:
|
|
|
|
return snprintf(dest, max_len, "%sunix:%s%s", prefix,
|
2015-10-26 23:34:57 +01:00
|
|
|
addr->u.q_unix->path, is_listen ? ",server" : "");
|
2014-10-02 18:17:36 +02:00
|
|
|
break;
|
|
|
|
case SOCKET_ADDRESS_KIND_FD:
|
2015-10-26 23:34:57 +01:00
|
|
|
return snprintf(dest, max_len, "%sfd:%s%s", prefix, addr->u.fd->str,
|
2014-10-02 18:17:36 +02:00
|
|
|
is_listen ? ",server" : "");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sockaddr_to_str(char *dest, int max_len,
|
|
|
|
struct sockaddr_storage *ss, socklen_t ss_len,
|
2014-10-02 18:17:38 +02:00
|
|
|
struct sockaddr_storage *ps, socklen_t ps_len,
|
2014-10-02 18:17:36 +02:00
|
|
|
bool is_listen, bool is_telnet)
|
|
|
|
{
|
2014-10-02 18:17:38 +02:00
|
|
|
char shost[NI_MAXHOST], sserv[NI_MAXSERV];
|
|
|
|
char phost[NI_MAXHOST], pserv[NI_MAXSERV];
|
2014-10-02 18:17:36 +02:00
|
|
|
const char *left = "", *right = "";
|
|
|
|
|
|
|
|
switch (ss->ss_family) {
|
|
|
|
#ifndef _WIN32
|
|
|
|
case AF_UNIX:
|
|
|
|
return snprintf(dest, max_len, "unix:%s%s",
|
|
|
|
((struct sockaddr_un *)(ss))->sun_path,
|
|
|
|
is_listen ? ",server" : "");
|
|
|
|
#endif
|
|
|
|
case AF_INET6:
|
|
|
|
left = "[";
|
|
|
|
right = "]";
|
|
|
|
/* fall through */
|
|
|
|
case AF_INET:
|
2014-10-02 18:17:38 +02:00
|
|
|
getnameinfo((struct sockaddr *) ss, ss_len, shost, sizeof(shost),
|
|
|
|
sserv, sizeof(sserv), NI_NUMERICHOST | NI_NUMERICSERV);
|
|
|
|
getnameinfo((struct sockaddr *) ps, ps_len, phost, sizeof(phost),
|
|
|
|
pserv, sizeof(pserv), NI_NUMERICHOST | NI_NUMERICSERV);
|
|
|
|
return snprintf(dest, max_len, "%s:%s%s%s:%s%s <-> %s%s%s:%s",
|
2014-10-02 18:17:36 +02:00
|
|
|
is_telnet ? "telnet" : "tcp",
|
2014-10-02 18:17:38 +02:00
|
|
|
left, shost, right, sserv,
|
|
|
|
is_listen ? ",server" : "",
|
|
|
|
left, phost, right, pserv);
|
2014-10-02 18:17:36 +02:00
|
|
|
|
|
|
|
default:
|
|
|
|
return snprintf(dest, max_len, "unknown");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-10-31 19:49:55 +01:00
|
|
|
/***********************************************************/
|
|
|
|
/* character device */
|
|
|
|
|
2009-09-12 09:36:22 +02:00
|
|
|
static QTAILQ_HEAD(CharDriverStateHead, CharDriverState) chardevs =
|
|
|
|
QTAILQ_HEAD_INITIALIZER(chardevs);
|
2009-03-05 23:59:58 +01:00
|
|
|
|
2014-06-18 08:43:55 +02:00
|
|
|
CharDriverState *qemu_chr_alloc(void)
|
|
|
|
{
|
|
|
|
CharDriverState *chr = g_malloc0(sizeof(CharDriverState));
|
2014-06-25 09:04:57 +02:00
|
|
|
qemu_mutex_init(&chr->chr_write_lock);
|
2014-06-18 08:43:55 +02:00
|
|
|
return chr;
|
|
|
|
}
|
|
|
|
|
2011-11-19 10:22:43 +01:00
|
|
|
void qemu_chr_be_event(CharDriverState *s, int event)
|
2008-10-31 19:49:55 +01:00
|
|
|
{
|
2010-04-01 18:42:39 +02:00
|
|
|
/* Keep track if the char device is open */
|
|
|
|
switch (event) {
|
|
|
|
case CHR_EVENT_OPENED:
|
2013-03-26 11:07:53 +01:00
|
|
|
s->be_open = 1;
|
2010-04-01 18:42:39 +02:00
|
|
|
break;
|
|
|
|
case CHR_EVENT_CLOSED:
|
2013-03-26 11:07:53 +01:00
|
|
|
s->be_open = 0;
|
2010-04-01 18:42:39 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-10-31 19:49:55 +01:00
|
|
|
if (!s->chr_event)
|
|
|
|
return;
|
|
|
|
s->chr_event(s->handler_opaque, event);
|
|
|
|
}
|
|
|
|
|
2013-03-26 11:07:54 +01:00
|
|
|
void qemu_chr_be_generic_open(CharDriverState *s)
|
2008-10-31 19:49:55 +01:00
|
|
|
{
|
qemu-char: don't issue CHR_EVENT_OPEN in a BH
When CHR_EVENT_OPENED was initially added, it was CHR_EVENT_RESET,
and it was issued as a bottom-half:
86e94dea5b740dad65446c857f6959eae43e0ba6
Which we basically used to print out a greeting/prompt for the
monitor.
AFAICT the only reason this was ever done in a BH was because in
some cases we'd modify the chr_write handler for a new chardev
backend *after* the site where we issued the reset (see:
86e94d:qemu_chr_open_stdio())
At some point this event was renamed to CHR_EVENT_OPENED, and we've
maintained the use of this BH ever since.
However, due to 9f939df955a4152aad69a19a77e0898631bb2c18, we schedule
the BH via g_idle_add(), which is causing events to sometimes be
delivered after we've already begun processing data from backends,
leading to:
known bugs:
QMP:
session negotation resets with OPENED event, in some cases this
is causing new sessions to get sporadically reset
potential bugs:
hw/usb/redirect.c:
can_read handler checks for dev->parser != NULL, which may be
true if CLOSED BH has not been executed yet. In the past, OPENED
quiesced outstanding CLOSED events prior to us reading client
data. If it's delayed, our check may allow reads to occur even
though we haven't processed the OPENED event yet, and when we
do finally get the OPENED event, our state may get reset.
qtest.c:
can begin session before OPENED event is processed, leading to
a spurious reset of the system and irq_levels
gdbstub.c:
may start a gdb session prior to the machine being paused
To fix these, let's just drop the BH.
Since the initial reasoning for using it still applies to an extent,
work around that by deferring the delivery of CHR_EVENT_OPENED until
after the chardevs have been fully initialized, toward the end of
qmp_chardev_add() (or some cases, qemu_chr_new_from_opts()). This
defers delivery long enough that we can be assured a CharDriverState
is fully initialized before CHR_EVENT_OPENED is sent.
Also, rather than requiring each chardev to do an explicit open, do it
automatically, and allow the small few who don't desire such behavior to
suppress the OPENED-on-init behavior by setting a 'explicit_be_open'
flag.
We additionally add missing OPENED events for stdio backends on w32,
which were previously not being issued, causing us to not recieve the
banner and initial prompts for qmp/hmp.
Reported-by: Stefan Priebe <s.priebe@profihost.ag>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
Message-id: 1370636393-21044-1-git-send-email-mdroth@linux.vnet.ibm.com
Cc: qemu-stable@nongnu.org
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-06-07 22:19:53 +02:00
|
|
|
qemu_chr_be_event(s, CHR_EVENT_OPENED);
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
|
|
|
|
2011-08-15 18:17:28 +02:00
|
|
|
int qemu_chr_fe_write(CharDriverState *s, const uint8_t *buf, int len)
|
2008-10-31 19:49:55 +01:00
|
|
|
{
|
2014-06-18 08:43:58 +02:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
qemu_mutex_lock(&s->chr_write_lock);
|
|
|
|
ret = s->chr_write(s, buf, len);
|
|
|
|
qemu_mutex_unlock(&s->chr_write_lock);
|
|
|
|
return ret;
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
|
|
|
|
2013-03-26 16:04:17 +01:00
|
|
|
int qemu_chr_fe_write_all(CharDriverState *s, const uint8_t *buf, int len)
|
|
|
|
{
|
|
|
|
int offset = 0;
|
2014-06-25 10:00:41 +02:00
|
|
|
int res = 0;
|
2013-03-26 16:04:17 +01:00
|
|
|
|
2014-06-18 08:43:58 +02:00
|
|
|
qemu_mutex_lock(&s->chr_write_lock);
|
2013-03-26 16:04:17 +01:00
|
|
|
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);
|
|
|
|
|
2014-06-18 08:43:58 +02:00
|
|
|
if (res <= 0) {
|
2013-03-26 16:04:17 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
offset += res;
|
|
|
|
}
|
2014-06-18 08:43:58 +02:00
|
|
|
qemu_mutex_unlock(&s->chr_write_lock);
|
2013-03-26 16:04:17 +01:00
|
|
|
|
2014-06-18 08:43:58 +02:00
|
|
|
if (res < 0) {
|
|
|
|
return res;
|
|
|
|
}
|
2013-03-26 16:04:17 +01:00
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
2014-05-27 14:03:48 +02:00
|
|
|
int qemu_chr_fe_read_all(CharDriverState *s, uint8_t *buf, int len)
|
|
|
|
{
|
|
|
|
int offset = 0, counter = 10;
|
|
|
|
int res;
|
|
|
|
|
|
|
|
if (!s->chr_sync_read) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (offset < len) {
|
|
|
|
do {
|
|
|
|
res = s->chr_sync_read(s, buf + offset, len - offset);
|
|
|
|
if (res == -1 && errno == EAGAIN) {
|
|
|
|
g_usleep(100);
|
|
|
|
}
|
|
|
|
} while (res == -1 && errno == EAGAIN);
|
|
|
|
|
|
|
|
if (res == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (res < 0) {
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
offset += res;
|
|
|
|
|
|
|
|
if (!counter--) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
2011-08-15 18:17:34 +02:00
|
|
|
int qemu_chr_fe_ioctl(CharDriverState *s, int cmd, void *arg)
|
2008-10-31 19:49:55 +01:00
|
|
|
{
|
|
|
|
if (!s->chr_ioctl)
|
|
|
|
return -ENOTSUP;
|
|
|
|
return s->chr_ioctl(s, cmd, arg);
|
|
|
|
}
|
|
|
|
|
2011-08-15 18:17:31 +02:00
|
|
|
int qemu_chr_be_can_write(CharDriverState *s)
|
2008-10-31 19:49:55 +01:00
|
|
|
{
|
|
|
|
if (!s->chr_can_read)
|
|
|
|
return 0;
|
|
|
|
return s->chr_can_read(s->handler_opaque);
|
|
|
|
}
|
|
|
|
|
2011-08-15 18:17:30 +02:00
|
|
|
void qemu_chr_be_write(CharDriverState *s, uint8_t *buf, int len)
|
2008-10-31 19:49:55 +01:00
|
|
|
{
|
2012-04-19 22:27:14 +02:00
|
|
|
if (s->chr_read) {
|
|
|
|
s->chr_read(s->handler_opaque, buf, len);
|
|
|
|
}
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
|
|
|
|
2011-08-15 18:17:39 +02:00
|
|
|
int qemu_chr_fe_get_msgfd(CharDriverState *s)
|
2009-07-22 10:11:39 +02:00
|
|
|
{
|
2014-05-27 14:04:15 +02:00
|
|
|
int fd;
|
qemu-char: fix qemu_chr_fe_get_msgfd()
Commit c76bf6bb8fbbb233a7d3641e09229d23747d5ee3 ("Add chardev API
qemu_chr_fe_get_msgfds") broke qemu_chr_fe_get_msgfd() because it
changed the return value.
Callers expect -1 if no fd is available. The commit changed the return
value to 0 (which is a valid file descriptor number) so callers always
detected a file descriptor even if none was available.
This patch fixes qemu-iotests 045:
$ cd tests/qemu-iotests && ./check 045
[...]
+FAIL: test_add_fd_invalid_fd (__main__.TestFdSets)
+----------------------------------------------------------------------
+Traceback (most recent call last):
+ File "./045", line 123, in test_add_fd_invalid_fd
+ self.assert_qmp(result, 'error/class', 'GenericError')
+ File "/home/stefanha/qemu/tests/qemu-iotests/iotests.py", line 232, in assert_qmp
+ result = self.dictpath(d, path)
+ File "/home/stefanha/qemu/tests/qemu-iotests/iotests.py", line 211, in dictpath
+ self.fail('failed path traversal for "%s" in "%s"' % (path, str(d)))
+AssertionError: failed path traversal for "error/class" in "{u'return': {u'fdset-id': 2, u'fd': 0}}"
Cc: Nikolay Nikolaev <n.nikolaev@virtualopensystems.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2014-06-22 04:38:36 +02:00
|
|
|
return (qemu_chr_fe_get_msgfds(s, &fd, 1) == 1) ? fd : -1;
|
2014-05-27 14:04:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int qemu_chr_fe_get_msgfds(CharDriverState *s, int *fds, int len)
|
|
|
|
{
|
|
|
|
return s->get_msgfds ? s->get_msgfds(s, fds, len) : -1;
|
2009-07-22 10:11:39 +02:00
|
|
|
}
|
|
|
|
|
2014-05-27 14:04:02 +02:00
|
|
|
int qemu_chr_fe_set_msgfds(CharDriverState *s, int *fds, int num)
|
|
|
|
{
|
|
|
|
return s->set_msgfds ? s->set_msgfds(s, fds, num) : -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 14:31:42 +02: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 19:49:55 +01:00
|
|
|
void qemu_chr_accept_input(CharDriverState *s)
|
|
|
|
{
|
|
|
|
if (s->chr_accept_input)
|
|
|
|
s->chr_accept_input(s);
|
2012-03-16 13:18:00 +01:00
|
|
|
qemu_notify_event();
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
|
|
|
|
2011-08-15 18:17:29 +02:00
|
|
|
void qemu_chr_fe_printf(CharDriverState *s, const char *fmt, ...)
|
2008-10-31 19:49:55 +01:00
|
|
|
{
|
2009-11-03 15:29:54 +01:00
|
|
|
char buf[READ_BUF_LEN];
|
2008-10-31 19:49:55 +01:00
|
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
|
|
vsnprintf(buf, sizeof(buf), fmt, ap);
|
2011-08-15 18:17:28 +02:00
|
|
|
qemu_chr_fe_write(s, (uint8_t *)buf, strlen(buf));
|
2008-10-31 19:49:55 +01:00
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
2013-08-28 11:54:05 +02:00
|
|
|
static void remove_fd_in_watch(CharDriverState *chr);
|
|
|
|
|
2008-10-31 19:49:55 +01:00
|
|
|
void qemu_chr_add_handlers(CharDriverState *s,
|
2010-03-11 17:55:39 +01:00
|
|
|
IOCanReadHandler *fd_can_read,
|
2008-10-31 19:49:55 +01:00
|
|
|
IOReadHandler *fd_read,
|
|
|
|
IOEventHandler *fd_event,
|
|
|
|
void *opaque)
|
|
|
|
{
|
2013-03-26 11:07:56 +01:00
|
|
|
int fe_open;
|
|
|
|
|
2011-04-25 11:48:22 +02:00
|
|
|
if (!opaque && !fd_can_read && !fd_read && !fd_event) {
|
2013-03-26 11:07:56 +01:00
|
|
|
fe_open = 0;
|
2013-08-28 11:54:05 +02:00
|
|
|
remove_fd_in_watch(s);
|
2013-03-26 11:07:56 +01:00
|
|
|
} else {
|
|
|
|
fe_open = 1;
|
2011-02-10 08:25:20 +01:00
|
|
|
}
|
2008-10-31 19:49:55 +01:00
|
|
|
s->chr_can_read = fd_can_read;
|
|
|
|
s->chr_read = fd_read;
|
|
|
|
s->chr_event = fd_event;
|
|
|
|
s->handler_opaque = opaque;
|
2014-02-25 11:12:35 +01:00
|
|
|
if (fe_open && s->chr_update_read_handler)
|
2008-10-31 19:49:55 +01:00
|
|
|
s->chr_update_read_handler(s);
|
2010-04-01 18:42:39 +02:00
|
|
|
|
2013-03-26 11:07:56 +01:00
|
|
|
if (!s->explicit_fe_open) {
|
2013-03-26 11:07:57 +01:00
|
|
|
qemu_chr_fe_set_open(s, fe_open);
|
2013-03-26 11:07:56 +01:00
|
|
|
}
|
|
|
|
|
2010-04-01 18:42:39 +02:00
|
|
|
/* We're connecting to an already opened device, so let's make sure we
|
|
|
|
also get the open event */
|
2013-03-26 11:08:00 +01:00
|
|
|
if (fe_open && s->be_open) {
|
2013-03-26 11:07:54 +01:00
|
|
|
qemu_chr_be_generic_open(s);
|
2010-04-01 18:42:39 +02:00
|
|
|
}
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
|
|
|
|
{
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2015-09-29 15:24:29 +02:00
|
|
|
static CharDriverState *qemu_chr_open_null(const char *id,
|
|
|
|
ChardevBackend *backend,
|
|
|
|
ChardevReturn *ret,
|
|
|
|
Error **errp)
|
2008-10-31 19:49:55 +01:00
|
|
|
{
|
|
|
|
CharDriverState *chr;
|
|
|
|
|
2014-06-18 08:43:55 +02:00
|
|
|
chr = qemu_chr_alloc();
|
2008-10-31 19:49:55 +01:00
|
|
|
chr->chr_write = null_chr_write;
|
qemu-char: don't issue CHR_EVENT_OPEN in a BH
When CHR_EVENT_OPENED was initially added, it was CHR_EVENT_RESET,
and it was issued as a bottom-half:
86e94dea5b740dad65446c857f6959eae43e0ba6
Which we basically used to print out a greeting/prompt for the
monitor.
AFAICT the only reason this was ever done in a BH was because in
some cases we'd modify the chr_write handler for a new chardev
backend *after* the site where we issued the reset (see:
86e94d:qemu_chr_open_stdio())
At some point this event was renamed to CHR_EVENT_OPENED, and we've
maintained the use of this BH ever since.
However, due to 9f939df955a4152aad69a19a77e0898631bb2c18, we schedule
the BH via g_idle_add(), which is causing events to sometimes be
delivered after we've already begun processing data from backends,
leading to:
known bugs:
QMP:
session negotation resets with OPENED event, in some cases this
is causing new sessions to get sporadically reset
potential bugs:
hw/usb/redirect.c:
can_read handler checks for dev->parser != NULL, which may be
true if CLOSED BH has not been executed yet. In the past, OPENED
quiesced outstanding CLOSED events prior to us reading client
data. If it's delayed, our check may allow reads to occur even
though we haven't processed the OPENED event yet, and when we
do finally get the OPENED event, our state may get reset.
qtest.c:
can begin session before OPENED event is processed, leading to
a spurious reset of the system and irq_levels
gdbstub.c:
may start a gdb session prior to the machine being paused
To fix these, let's just drop the BH.
Since the initial reasoning for using it still applies to an extent,
work around that by deferring the delivery of CHR_EVENT_OPENED until
after the chardevs have been fully initialized, toward the end of
qmp_chardev_add() (or some cases, qemu_chr_new_from_opts()). This
defers delivery long enough that we can be assured a CharDriverState
is fully initialized before CHR_EVENT_OPENED is sent.
Also, rather than requiring each chardev to do an explicit open, do it
automatically, and allow the small few who don't desire such behavior to
suppress the OPENED-on-init behavior by setting a 'explicit_be_open'
flag.
We additionally add missing OPENED events for stdio backends on w32,
which were previously not being issued, causing us to not recieve the
banner and initial prompts for qmp/hmp.
Reported-by: Stefan Priebe <s.priebe@profihost.ag>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
Message-id: 1370636393-21044-1-git-send-email-mdroth@linux.vnet.ibm.com
Cc: qemu-stable@nongnu.org
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-06-07 22:19:53 +02:00
|
|
|
chr->explicit_be_open = true;
|
Revert "qemu-char: Print strerror message on failure" and deps
The commit's purpose is laudable:
The only way for chardev drivers to communicate an error was to
return a NULL pointer, which resulted in an error message that
said _that_ something went wrong, but not _why_.
It attempts to achieve it by changing the interface to return 0/-errno
and update qemu_chr_open_opts() to use strerror() to display a more
helpful error message. Unfortunately, it has serious flaws:
1. Backends "socket" and "udp" return bogus error codes, because
qemu_chr_open_socket() and qemu_chr_open_udp() assume that
unix_listen_opts(), unix_connect_opts(), inet_listen_opts(),
inet_connect_opts() and inet_dgram_opts() fail with errno set
appropriately. That assumption is wrong, and the commit turns
unspecific error messages into misleading error messages. For
instance:
$ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx
inet_connect: host and/or port not specified
chardev: opening backend "socket" failed: No such file or directory
ENOENT is what happens to be in my errno when the backend returns
-errno. Let's put ERANGE there just for giggles:
$ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx -drive if=none,iops=99999999999999999999
inet_connect: host and/or port not specified
chardev: opening backend "socket" failed: Numerical result out of range
Worse: when errno happens to be zero, return -errno erroneously
signals success, and qemu_chr_new_from_opts() dies dereferencing
uninitialized chr. I observe this with "-serial unix:".
2. All qemu_chr_open_opts() knows about the error is an errno error
code. That's simply not enough for a decent message. For instance,
when inet_dgram() can't resolve the parameter host, which errno code
should it use? What if it can't resolve parameter localaddr?
Clue: many backends already report errors in their open methods.
Let's revert the flawed commit along with its dependencies, and fix up
the silent error paths instead.
This reverts commit 6e1db57b2ac9025c2443c665a0d9e78748637b26.
Conflicts:
console.c
hw/baum.c
qemu-char.c
This reverts commit aad04cd024f0c59f0b96f032cde2e24eb3abba6d.
The parts of commit db418a0a "Add stdio char device on windows" that
depend on the reverted change fixed up.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2012-02-07 15:09:08 +01:00
|
|
|
return chr;
|
2008-10-31 19:49:55 +01: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 17:55:39 +01:00
|
|
|
IOCanReadHandler *chr_can_read[MAX_MUX];
|
2008-10-31 19:49:55 +01:00
|
|
|
IOReadHandler *chr_read[MAX_MUX];
|
|
|
|
IOEventHandler *chr_event[MAX_MUX];
|
|
|
|
void *ext_opaque[MAX_MUX];
|
|
|
|
CharDriverState *drv;
|
2009-09-10 10:58:55 +02:00
|
|
|
int focus;
|
2008-10-31 19:49:55 +01:00
|
|
|
int mux_cnt;
|
|
|
|
int term_got_escape;
|
|
|
|
int max_size;
|
2009-03-06 00:00:02 +01: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 22:25:30 +02:00
|
|
|
int timestamps;
|
2014-06-18 08:43:58 +02:00
|
|
|
|
|
|
|
/* Protected by the CharDriverState chr_write_lock. */
|
2009-06-15 22:25:34 +02:00
|
|
|
int linestart;
|
2009-06-15 22:25:30 +02:00
|
|
|
int64_t timestamps_start;
|
2008-10-31 19:49:55 +01:00
|
|
|
} MuxDriver;
|
|
|
|
|
|
|
|
|
2014-06-18 08:43:58 +02:00
|
|
|
/* Called with chr_write_lock held. */
|
2008-10-31 19:49:55 +01:00
|
|
|
static int mux_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
|
|
|
|
{
|
|
|
|
MuxDriver *d = chr->opaque;
|
|
|
|
int ret;
|
2009-06-15 22:25:30 +02:00
|
|
|
if (!d->timestamps) {
|
2014-06-18 08:43:56 +02:00
|
|
|
ret = qemu_chr_fe_write(d->drv, buf, len);
|
2008-10-31 19:49:55 +01:00
|
|
|
} else {
|
|
|
|
int i;
|
|
|
|
|
|
|
|
ret = 0;
|
2009-06-15 22:25:34 +02:00
|
|
|
for (i = 0; i < len; i++) {
|
|
|
|
if (d->linestart) {
|
2008-10-31 19:49:55 +01:00
|
|
|
char buf1[64];
|
|
|
|
int64_t ti;
|
|
|
|
int secs;
|
|
|
|
|
2013-08-21 17:03:08 +02:00
|
|
|
ti = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
|
2009-06-15 22:25:30 +02:00
|
|
|
if (d->timestamps_start == -1)
|
|
|
|
d->timestamps_start = ti;
|
|
|
|
ti -= d->timestamps_start;
|
2009-01-22 18:15:16 +01:00
|
|
|
secs = ti / 1000;
|
2008-10-31 19:49:55 +01:00
|
|
|
snprintf(buf1, sizeof(buf1),
|
|
|
|
"[%02d:%02d:%02d.%03d] ",
|
|
|
|
secs / 3600,
|
|
|
|
(secs / 60) % 60,
|
|
|
|
secs % 60,
|
2009-01-22 18:15:16 +01:00
|
|
|
(int)(ti % 1000));
|
2014-06-18 08:43:56 +02:00
|
|
|
qemu_chr_fe_write(d->drv, (uint8_t *)buf1, strlen(buf1));
|
2009-06-15 22:25:34 +02:00
|
|
|
d->linestart = 0;
|
|
|
|
}
|
2014-06-18 08:43:56 +02:00
|
|
|
ret += qemu_chr_fe_write(d->drv, buf+i, 1);
|
2009-06-15 22:25:34 +02:00
|
|
|
if (buf[i] == '\n') {
|
|
|
|
d->linestart = 1;
|
2008-10-31 19:49:55 +01: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",
|
2014-11-15 11:06:46 +01:00
|
|
|
"% t toggle console timestamps\n\r",
|
2008-10-31 19:49:55 +01:00
|
|
|
"% 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);
|
|
|
|
}
|
2014-06-18 08:43:56 +02:00
|
|
|
qemu_chr_fe_write(chr, (uint8_t *)cbuf, strlen(cbuf));
|
2008-10-31 19:49:55 +01:00
|
|
|
for (i = 0; mux_help[i] != NULL; i++) {
|
|
|
|
for (j=0; mux_help[i][j] != '\0'; j++) {
|
|
|
|
if (mux_help[i][j] == '%')
|
2014-06-18 08:43:56 +02:00
|
|
|
qemu_chr_fe_write(chr, (uint8_t *)ebuf, strlen(ebuf));
|
2008-10-31 19:49:55 +01:00
|
|
|
else
|
2014-06-18 08:43:56 +02:00
|
|
|
qemu_chr_fe_write(chr, (uint8_t *)&mux_help[i][j], 1);
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-06 00:01:47 +01: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 19:49:55 +01: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";
|
2014-06-18 08:43:56 +02:00
|
|
|
qemu_chr_fe_write(chr, (uint8_t *)term, strlen(term));
|
2008-10-31 19:49:55 +01:00
|
|
|
exit(0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 's':
|
2010-06-02 18:55:18 +02:00
|
|
|
bdrv_commit_all();
|
2008-10-31 19:49:55 +01:00
|
|
|
break;
|
|
|
|
case 'b':
|
2011-11-19 10:22:43 +01:00
|
|
|
qemu_chr_be_event(chr, CHR_EVENT_BREAK);
|
2008-10-31 19:49:55 +01:00
|
|
|
break;
|
|
|
|
case 'c':
|
|
|
|
/* Switch to the next registered device */
|
2009-09-10 10:58:55 +02: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 19:49:55 +01:00
|
|
|
break;
|
2009-06-15 22:25:30 +02:00
|
|
|
case 't':
|
|
|
|
d->timestamps = !d->timestamps;
|
|
|
|
d->timestamps_start = -1;
|
2009-06-15 22:25:34 +02:00
|
|
|
d->linestart = 0;
|
2009-06-15 22:25:30 +02:00
|
|
|
break;
|
2008-10-31 19:49:55 +01: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 10:58:55 +02:00
|
|
|
int m = d->focus;
|
2008-10-31 19:49:55 +01:00
|
|
|
|
2009-03-06 00:00:02 +01:00
|
|
|
while (d->prod[m] != d->cons[m] &&
|
2008-10-31 19:49:55 +01: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-06 00:00:02 +01:00
|
|
|
&d->buffer[m][d->cons[m]++ & MUX_BUFFER_MASK], 1);
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mux_chr_can_read(void *opaque)
|
|
|
|
{
|
|
|
|
CharDriverState *chr = opaque;
|
|
|
|
MuxDriver *d = chr->opaque;
|
2009-09-10 10:58:55 +02:00
|
|
|
int m = d->focus;
|
2008-10-31 19:49:55 +01:00
|
|
|
|
2009-03-06 00:00:02 +01:00
|
|
|
if ((d->prod[m] - d->cons[m]) < MUX_BUFFER_SIZE)
|
2008-10-31 19:49:55 +01:00
|
|
|
return 1;
|
2009-03-06 00:00:02 +01:00
|
|
|
if (d->chr_can_read[m])
|
|
|
|
return d->chr_can_read[m](d->ext_opaque[m]);
|
2008-10-31 19:49:55 +01: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 10:58:55 +02:00
|
|
|
int m = d->focus;
|
2008-10-31 19:49:55 +01:00
|
|
|
int i;
|
|
|
|
|
|
|
|
mux_chr_accept_input (opaque);
|
|
|
|
|
|
|
|
for(i = 0; i < size; i++)
|
|
|
|
if (mux_proc_byte(chr, d, buf[i])) {
|
2009-03-06 00:00:02 +01:00
|
|
|
if (d->prod[m] == d->cons[m] &&
|
2008-10-31 19:49:55 +01: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-06 00:00:02 +01:00
|
|
|
d->buffer[m][d->prod[m]++ & MUX_BUFFER_MASK] = buf[i];
|
2008-10-31 19:49:55 +01: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-06 00:01:47 +01:00
|
|
|
mux_chr_send_event(d, i, event);
|
2008-10-31 19:49:55 +01: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 10:58:55 +02:00
|
|
|
if (d->focus != -1) {
|
|
|
|
mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT);
|
2009-09-10 10:58:54 +02:00
|
|
|
}
|
2009-09-10 10:58:55 +02:00
|
|
|
d->focus = d->mux_cnt;
|
2008-10-31 19:49:55 +01:00
|
|
|
d->mux_cnt++;
|
2009-09-10 10:58:55 +02:00
|
|
|
mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN);
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
|
|
|
|
chardev: fix CHR_EVENT_OPENED events for mux chardevs
As of bd5c51ee6c4f1c79cae5ad2516d711a27b4ea8ec, chardevs no longer use
bottom-halves to issue CHR_EVENT_OPENED events. To maintain past
semantics, we instead defer the CHR_EVENT_OPENED events toward the end
of chardev initialization.
For muxes, this isn't good enough, since a range of FEs must be able
to attach to the mux prior to any CHR_EVENT_OPENED being issued, else
each FE will immediately print it's initial output (prompts, banners,
etc.) just prior to us switching to the next FE as part of
initialization.
The is new and confusing behavior for users, as they'll see output for
things like the HMP monitor, even though their the current mux focus
may be a guest serial port with potentially no output.
We fix this by further deferring CHR_EVENT_OPENED events for FEs
associated with muxes until after machine init by flagging mux chardevs
with 'explicit_be_open', which suppresses emission of CHR_EVENT_OPENED
events until we explicitly set the mux as opened later.
Currently, we must defer till after machine init since we potentially
associate FEs with muxes as part of realize (for instance,
serial_isa_realizefn).
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
Message-id: 1375207462-8141-1-git-send-email-mdroth@linux.vnet.ibm.com
Cc: qemu-stable@nongnu.org
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-07-30 20:04:22 +02:00
|
|
|
static bool muxes_realized;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Called after processing of default and command-line-specified
|
|
|
|
* chardevs to deliver CHR_EVENT_OPENED events to any FEs attached
|
|
|
|
* to a mux chardev. This is done here to ensure that
|
|
|
|
* output/prompts/banners are only displayed for the FE that has
|
|
|
|
* focus when initial command-line processing/machine init is
|
|
|
|
* completed.
|
|
|
|
*
|
|
|
|
* After this point, any new FE attached to any new or existing
|
|
|
|
* mux will receive CHR_EVENT_OPENED notifications for the BE
|
|
|
|
* immediately.
|
|
|
|
*/
|
|
|
|
static void muxes_realize_done(Notifier *notifier, void *unused)
|
|
|
|
{
|
|
|
|
CharDriverState *chr;
|
|
|
|
|
|
|
|
QTAILQ_FOREACH(chr, &chardevs, next) {
|
|
|
|
if (chr->is_mux) {
|
|
|
|
MuxDriver *d = chr->opaque;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* send OPENED to all already-attached FEs */
|
|
|
|
for (i = 0; i < d->mux_cnt; i++) {
|
|
|
|
mux_chr_send_event(d, i, CHR_EVENT_OPENED);
|
|
|
|
}
|
|
|
|
/* mark mux as OPENED so any new FEs will immediately receive
|
|
|
|
* OPENED event
|
|
|
|
*/
|
|
|
|
qemu_chr_be_generic_open(chr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
muxes_realized = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Notifier muxes_realize_notify = {
|
|
|
|
.notify = muxes_realize_done,
|
|
|
|
};
|
|
|
|
|
2014-07-04 14:43:15 +02:00
|
|
|
static GSource *mux_chr_add_watch(CharDriverState *s, GIOCondition cond)
|
|
|
|
{
|
|
|
|
MuxDriver *d = s->opaque;
|
|
|
|
return d->drv->chr_add_watch(d->drv, cond);
|
|
|
|
}
|
|
|
|
|
2015-09-29 15:27:24 +02:00
|
|
|
static CharDriverState *qemu_chr_open_mux(const char *id,
|
|
|
|
ChardevBackend *backend,
|
|
|
|
ChardevReturn *ret, Error **errp)
|
2008-10-31 19:49:55 +01:00
|
|
|
{
|
2015-10-26 23:34:57 +01:00
|
|
|
ChardevMux *mux = backend->u.mux;
|
2015-09-29 15:27:24 +02:00
|
|
|
CharDriverState *chr, *drv;
|
2008-10-31 19:49:55 +01:00
|
|
|
MuxDriver *d;
|
|
|
|
|
2015-09-29 15:27:24 +02:00
|
|
|
drv = qemu_chr_find(mux->chardev);
|
|
|
|
if (drv == NULL) {
|
|
|
|
error_setg(errp, "mux: base chardev %s not found", mux->chardev);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-06-18 08:43:55 +02:00
|
|
|
chr = qemu_chr_alloc();
|
2015-09-14 13:54:03 +02:00
|
|
|
d = g_new0(MuxDriver, 1);
|
2008-10-31 19:49:55 +01:00
|
|
|
|
|
|
|
chr->opaque = d;
|
|
|
|
d->drv = drv;
|
2009-09-10 10:58:55 +02:00
|
|
|
d->focus = -1;
|
2008-10-31 19:49:55 +01: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 11:12:02 +01:00
|
|
|
/* Frontend guest-open / -close notification is not support with muxes */
|
2013-03-26 11:07:58 +01:00
|
|
|
chr->chr_set_fe_open = NULL;
|
2014-07-04 14:43:15 +02:00
|
|
|
if (drv->chr_add_watch) {
|
|
|
|
chr->chr_add_watch = mux_chr_add_watch;
|
|
|
|
}
|
chardev: fix CHR_EVENT_OPENED events for mux chardevs
As of bd5c51ee6c4f1c79cae5ad2516d711a27b4ea8ec, chardevs no longer use
bottom-halves to issue CHR_EVENT_OPENED events. To maintain past
semantics, we instead defer the CHR_EVENT_OPENED events toward the end
of chardev initialization.
For muxes, this isn't good enough, since a range of FEs must be able
to attach to the mux prior to any CHR_EVENT_OPENED being issued, else
each FE will immediately print it's initial output (prompts, banners,
etc.) just prior to us switching to the next FE as part of
initialization.
The is new and confusing behavior for users, as they'll see output for
things like the HMP monitor, even though their the current mux focus
may be a guest serial port with potentially no output.
We fix this by further deferring CHR_EVENT_OPENED events for FEs
associated with muxes until after machine init by flagging mux chardevs
with 'explicit_be_open', which suppresses emission of CHR_EVENT_OPENED
events until we explicitly set the mux as opened later.
Currently, we must defer till after machine init since we potentially
associate FEs with muxes as part of realize (for instance,
serial_isa_realizefn).
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
Message-id: 1375207462-8141-1-git-send-email-mdroth@linux.vnet.ibm.com
Cc: qemu-stable@nongnu.org
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-07-30 20:04:22 +02:00
|
|
|
/* only default to opened state if we've realized the initial
|
|
|
|
* set of muxes
|
|
|
|
*/
|
|
|
|
chr->explicit_be_open = muxes_realized ? 0 : 1;
|
|
|
|
chr->is_mux = 1;
|
2010-04-01 18:42:39 +02:00
|
|
|
|
2008-10-31 19:49:55 +01:00
|
|
|
return chr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
2008-11-11 21:46:40 +01:00
|
|
|
int send_all(int fd, const void *buf, int len1)
|
2008-10-31 19:49:55 +01: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 20:02:23 +01:00
|
|
|
int send_all(int fd, const void *_buf, int len1)
|
2008-10-31 19:49:55 +01:00
|
|
|
{
|
|
|
|
int ret, len;
|
2010-11-01 20:02:23 +01:00
|
|
|
const uint8_t *buf = _buf;
|
2008-10-31 19:49:55 +01: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 18:47:53 +01: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 19:49:55 +01:00
|
|
|
#endif /* !_WIN32 */
|
|
|
|
|
2013-03-05 18:51:18 +01:00
|
|
|
typedef struct IOWatchPoll
|
|
|
|
{
|
2013-04-05 17:59:33 +02:00
|
|
|
GSource parent;
|
|
|
|
|
2013-04-08 15:03:15 +02:00
|
|
|
GIOChannel *channel;
|
2013-03-05 18:51:18 +01:00
|
|
|
GSource *src;
|
|
|
|
|
|
|
|
IOCanReadHandler *fd_can_read;
|
2013-04-08 15:03:15 +02:00
|
|
|
GSourceFunc fd_read;
|
2013-03-05 18:51:18 +01:00
|
|
|
void *opaque;
|
|
|
|
} IOWatchPoll;
|
|
|
|
|
|
|
|
static IOWatchPoll *io_watch_poll_from_source(GSource *source)
|
|
|
|
{
|
2013-04-05 17:59:33 +02:00
|
|
|
return container_of(source, IOWatchPoll, parent);
|
2013-03-05 18:51:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean io_watch_poll_prepare(GSource *source, gint *timeout_)
|
|
|
|
{
|
|
|
|
IOWatchPoll *iwp = io_watch_poll_from_source(source);
|
2013-04-05 17:59:33 +02:00
|
|
|
bool now_active = iwp->fd_can_read(iwp->opaque) > 0;
|
2013-04-08 15:03:15 +02:00
|
|
|
bool was_active = iwp->src != NULL;
|
2013-04-05 17:59:33 +02:00
|
|
|
if (was_active == now_active) {
|
2013-03-05 18:51:18 +01:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2013-04-05 17:59:33 +02:00
|
|
|
if (now_active) {
|
2015-07-19 22:39:56 +02:00
|
|
|
iwp->src = g_io_create_watch(iwp->channel,
|
|
|
|
G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL);
|
2013-04-08 15:03:15 +02:00
|
|
|
g_source_set_callback(iwp->src, iwp->fd_read, iwp->opaque, NULL);
|
2013-04-05 17:59:33 +02:00
|
|
|
g_source_attach(iwp->src, NULL);
|
|
|
|
} else {
|
2013-04-08 15:03:15 +02:00
|
|
|
g_source_destroy(iwp->src);
|
|
|
|
g_source_unref(iwp->src);
|
|
|
|
iwp->src = NULL;
|
2013-04-05 17:59:33 +02:00
|
|
|
}
|
|
|
|
return FALSE;
|
2013-03-05 18:51:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean io_watch_poll_check(GSource *source)
|
|
|
|
{
|
2013-04-05 17:59:33 +02:00
|
|
|
return FALSE;
|
2013-03-05 18:51:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean io_watch_poll_dispatch(GSource *source, GSourceFunc callback,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
2013-04-05 17:59:33 +02:00
|
|
|
abort();
|
2013-03-05 18:51:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void io_watch_poll_finalize(GSource *source)
|
|
|
|
{
|
2013-04-19 17:32:09 +02:00
|
|
|
/* Due to a glib bug, removing the last reference to a source
|
|
|
|
* inside a finalize callback causes recursive locking (and a
|
|
|
|
* deadlock). This is not a problem inside other callbacks,
|
|
|
|
* including dispatch callbacks, so we call io_remove_watch_poll
|
|
|
|
* to remove this source. At this point, iwp->src must
|
|
|
|
* be NULL, or we would leak it.
|
|
|
|
*
|
|
|
|
* This would be solved much more elegantly by child sources,
|
|
|
|
* but we support older glib versions that do not have them.
|
|
|
|
*/
|
2013-03-05 18:51:18 +01:00
|
|
|
IOWatchPoll *iwp = io_watch_poll_from_source(source);
|
2013-04-19 17:32:09 +02:00
|
|
|
assert(iwp->src == NULL);
|
2013-03-05 18:51:18 +01: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 15:23:27 +02:00
|
|
|
int tag;
|
2013-03-05 18:51:18 +01:00
|
|
|
|
2013-04-05 17:59:33 +02:00
|
|
|
iwp = (IOWatchPoll *) g_source_new(&io_watch_poll_funcs, sizeof(IOWatchPoll));
|
2013-03-05 18:51:18 +01:00
|
|
|
iwp->fd_can_read = fd_can_read;
|
|
|
|
iwp->opaque = user_data;
|
2013-04-08 15:03:15 +02:00
|
|
|
iwp->channel = channel;
|
|
|
|
iwp->fd_read = (GSourceFunc) fd_read;
|
|
|
|
iwp->src = NULL;
|
2013-03-05 18:51:18 +01:00
|
|
|
|
2013-04-10 15:23:27 +02:00
|
|
|
tag = g_source_attach(&iwp->parent, NULL);
|
|
|
|
g_source_unref(&iwp->parent);
|
|
|
|
return tag;
|
2013-03-05 18:51:18 +01:00
|
|
|
}
|
|
|
|
|
2013-04-19 17:32:09 +02:00
|
|
|
static void io_remove_watch_poll(guint tag)
|
|
|
|
{
|
|
|
|
GSource *source;
|
|
|
|
IOWatchPoll *iwp;
|
|
|
|
|
|
|
|
g_return_if_fail (tag > 0);
|
|
|
|
|
|
|
|
source = g_main_context_find_source_by_id(NULL, tag);
|
|
|
|
g_return_if_fail (source != NULL);
|
|
|
|
|
|
|
|
iwp = io_watch_poll_from_source(source);
|
|
|
|
if (iwp->src) {
|
|
|
|
g_source_destroy(iwp->src);
|
|
|
|
g_source_unref(iwp->src);
|
|
|
|
iwp->src = NULL;
|
|
|
|
}
|
|
|
|
g_source_destroy(&iwp->parent);
|
|
|
|
}
|
|
|
|
|
2013-08-28 11:53:37 +02:00
|
|
|
static void remove_fd_in_watch(CharDriverState *chr)
|
|
|
|
{
|
|
|
|
if (chr->fd_in_tag) {
|
|
|
|
io_remove_watch_poll(chr->fd_in_tag);
|
|
|
|
chr->fd_in_tag = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-09 10:56:04 +01:00
|
|
|
#ifndef _WIN32
|
2013-03-05 18:51:18 +01: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 10:56:04 +01:00
|
|
|
#endif
|
2013-03-05 18:51:18 +01:00
|
|
|
|
2013-03-05 18:51:21 +01: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 17:39:50 +01:00
|
|
|
static int io_channel_send(GIOChannel *fd, const void *buf, size_t len)
|
2013-03-05 18:51:18 +01:00
|
|
|
{
|
2013-07-16 20:19:40 +02:00
|
|
|
size_t offset = 0;
|
|
|
|
GIOStatus status = G_IO_STATUS_NORMAL;
|
2013-03-05 18:51:18 +01:00
|
|
|
|
2013-07-16 20:19:40 +02:00
|
|
|
while (offset < len && status == G_IO_STATUS_NORMAL) {
|
|
|
|
gsize bytes_written = 0;
|
2013-03-29 17:39:50 +01:00
|
|
|
|
|
|
|
status = g_io_channel_write_chars(fd, buf + offset, len - offset,
|
2013-03-05 18:51:18 +01:00
|
|
|
&bytes_written, NULL);
|
2013-03-29 17:39:50 +01:00
|
|
|
offset += bytes_written;
|
2013-03-05 18:51:18 +01:00
|
|
|
}
|
2013-03-29 17:39:50 +01:00
|
|
|
|
2013-07-16 20:19:40 +02:00
|
|
|
if (offset > 0) {
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
switch (status) {
|
|
|
|
case G_IO_STATUS_NORMAL:
|
|
|
|
g_assert(len == 0);
|
|
|
|
return 0;
|
|
|
|
case G_IO_STATUS_AGAIN:
|
|
|
|
errno = EAGAIN;
|
|
|
|
return -1;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
2013-03-05 18:51:18 +01:00
|
|
|
}
|
|
|
|
|
2013-03-09 10:56:04 +01:00
|
|
|
#ifndef _WIN32
|
|
|
|
|
2013-03-05 18:51:19 +01:00
|
|
|
typedef struct FDCharDriver {
|
|
|
|
CharDriverState *chr;
|
|
|
|
GIOChannel *fd_in, *fd_out;
|
2008-10-31 19:49:55 +01:00
|
|
|
int max_size;
|
|
|
|
} FDCharDriver;
|
|
|
|
|
2014-06-18 08:43:58 +02:00
|
|
|
/* Called with chr_write_lock held. */
|
2008-10-31 19:49:55 +01:00
|
|
|
static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
|
|
|
|
{
|
|
|
|
FDCharDriver *s = chr->opaque;
|
2013-03-05 18:51:19 +01:00
|
|
|
|
2013-03-29 17:39:50 +01:00
|
|
|
return io_channel_send(s->fd_out, buf, len);
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
|
|
|
|
2013-03-05 18:51:19 +01:00
|
|
|
static gboolean fd_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
|
2008-10-31 19:49:55 +01:00
|
|
|
{
|
|
|
|
CharDriverState *chr = opaque;
|
|
|
|
FDCharDriver *s = chr->opaque;
|
2013-03-05 18:51:19 +01:00
|
|
|
int len;
|
2009-11-03 15:29:54 +01:00
|
|
|
uint8_t buf[READ_BUF_LEN];
|
2013-03-05 18:51:19 +01:00
|
|
|
GIOStatus status;
|
|
|
|
gsize bytes_read;
|
2008-10-31 19:49:55 +01:00
|
|
|
|
|
|
|
len = sizeof(buf);
|
2013-03-05 18:51:19 +01:00
|
|
|
if (len > s->max_size) {
|
2008-10-31 19:49:55 +01:00
|
|
|
len = s->max_size;
|
2013-03-05 18:51:19 +01:00
|
|
|
}
|
|
|
|
if (len == 0) {
|
2013-04-19 17:32:08 +02:00
|
|
|
return TRUE;
|
2013-03-05 18:51:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
status = g_io_channel_read_chars(chan, (gchar *)buf,
|
|
|
|
len, &bytes_read, NULL);
|
|
|
|
if (status == G_IO_STATUS_EOF) {
|
2013-08-28 11:53:37 +02:00
|
|
|
remove_fd_in_watch(chr);
|
2011-11-19 10:22:43 +01:00
|
|
|
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
|
2013-03-05 18:51:19 +01:00
|
|
|
return FALSE;
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
2013-03-05 18:51:19 +01:00
|
|
|
if (status == G_IO_STATUS_NORMAL) {
|
|
|
|
qemu_chr_be_write(chr, buf, bytes_read);
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
2013-03-05 18:51:19 +01: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 19:49:55 +01:00
|
|
|
}
|
|
|
|
|
2013-03-05 18:51:23 +01: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 19:49:55 +01:00
|
|
|
static void fd_chr_update_read_handler(CharDriverState *chr)
|
|
|
|
{
|
|
|
|
FDCharDriver *s = chr->opaque;
|
|
|
|
|
2013-08-28 11:53:37 +02:00
|
|
|
remove_fd_in_watch(chr);
|
2013-03-05 18:51:19 +01:00
|
|
|
if (s->fd_in) {
|
2013-08-28 11:48:29 +02:00
|
|
|
chr->fd_in_tag = io_add_watch_poll(s->fd_in, fd_chr_read_poll,
|
|
|
|
fd_chr_read, chr);
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void fd_chr_close(struct CharDriverState *chr)
|
|
|
|
{
|
|
|
|
FDCharDriver *s = chr->opaque;
|
|
|
|
|
2013-08-28 11:53:37 +02:00
|
|
|
remove_fd_in_watch(chr);
|
2013-03-05 18:51:19 +01:00
|
|
|
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 19:49:55 +01:00
|
|
|
}
|
|
|
|
|
2011-08-21 05:09:37 +02:00
|
|
|
g_free(s);
|
2011-11-19 10:22:43 +01:00
|
|
|
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
|
2008-10-31 19:49:55 +01: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;
|
|
|
|
|
2014-06-18 08:43:55 +02:00
|
|
|
chr = qemu_chr_alloc();
|
2015-09-14 13:54:03 +02:00
|
|
|
s = g_new0(FDCharDriver, 1);
|
2013-03-05 18:51:19 +01:00
|
|
|
s->fd_in = io_channel_from_fd(fd_in);
|
|
|
|
s->fd_out = io_channel_from_fd(fd_out);
|
2014-08-11 11:34:20 +02:00
|
|
|
qemu_set_nonblock(fd_out);
|
2013-03-05 18:51:19 +01:00
|
|
|
s->chr = chr;
|
2008-10-31 19:49:55 +01:00
|
|
|
chr->opaque = s;
|
2013-03-05 18:51:23 +01:00
|
|
|
chr->chr_add_watch = fd_chr_add_watch;
|
2008-10-31 19:49:55 +01:00
|
|
|
chr->chr_write = fd_chr_write;
|
|
|
|
chr->chr_update_read_handler = fd_chr_update_read_handler;
|
|
|
|
chr->chr_close = fd_chr_close;
|
|
|
|
|
|
|
|
return chr;
|
|
|
|
}
|
|
|
|
|
2015-09-29 15:14:07 +02:00
|
|
|
static CharDriverState *qemu_chr_open_pipe(const char *id,
|
|
|
|
ChardevBackend *backend,
|
|
|
|
ChardevReturn *ret,
|
|
|
|
Error **errp)
|
2008-10-31 19:49:55 +01:00
|
|
|
{
|
2015-10-26 23:34:57 +01:00
|
|
|
ChardevHostdev *opts = backend->u.pipe;
|
2008-10-31 19:49:55 +01:00
|
|
|
int fd_in, fd_out;
|
2014-10-02 18:17:33 +02:00
|
|
|
char filename_in[CHR_MAX_FILENAME_SIZE];
|
|
|
|
char filename_out[CHR_MAX_FILENAME_SIZE];
|
2013-02-25 11:50:55 +01:00
|
|
|
const char *filename = opts->device;
|
2009-09-10 10:58:36 +02:00
|
|
|
|
2014-10-02 18:17:33 +02:00
|
|
|
snprintf(filename_in, CHR_MAX_FILENAME_SIZE, "%s.in", filename);
|
|
|
|
snprintf(filename_out, CHR_MAX_FILENAME_SIZE, "%s.out", filename);
|
2009-12-02 12:24:42 +01: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 19:49:55 +01: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 15:09:09 +01:00
|
|
|
TFR(fd_in = fd_out = qemu_open(filename, O_RDWR | O_BINARY));
|
2012-02-07 15:09:10 +01:00
|
|
|
if (fd_in < 0) {
|
2015-09-29 15:14:07 +02:00
|
|
|
error_setg_file_open(errp, errno, filename);
|
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 15:09:08 +01:00
|
|
|
return NULL;
|
2012-02-07 15:09:10 +01:00
|
|
|
}
|
2008-10-31 19:49:55 +01: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 15:09:08 +01:00
|
|
|
return qemu_chr_open_fd(fd_in, fd_out);
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* init terminal so that we can grab keys */
|
|
|
|
static struct termios oldtty;
|
|
|
|
static int old_fd0_flags;
|
2014-09-09 13:19:48 +02:00
|
|
|
static bool stdio_in_use;
|
2010-12-23 13:42:50 +01:00
|
|
|
static bool stdio_allow_signal;
|
2015-01-07 09:38:35 +01:00
|
|
|
static bool stdio_echo_state;
|
|
|
|
|
|
|
|
static void qemu_chr_set_echo_stdio(CharDriverState *chr, bool echo);
|
2008-10-31 19:49:55 +01:00
|
|
|
|
|
|
|
static void term_exit(void)
|
|
|
|
{
|
|
|
|
tcsetattr (0, TCSANOW, &oldtty);
|
|
|
|
fcntl(0, F_SETFL, old_fd0_flags);
|
|
|
|
}
|
|
|
|
|
2015-01-07 09:38:35 +01:00
|
|
|
static void term_stdio_handler(int sig)
|
|
|
|
{
|
|
|
|
/* restore echo after resume from suspend. */
|
|
|
|
qemu_chr_set_echo_stdio(NULL, stdio_echo_state);
|
|
|
|
}
|
|
|
|
|
2010-12-23 13:42:50 +01:00
|
|
|
static void qemu_chr_set_echo_stdio(CharDriverState *chr, bool echo)
|
2008-10-31 19:49:55 +01:00
|
|
|
{
|
|
|
|
struct termios tty;
|
|
|
|
|
2015-01-07 09:38:35 +01:00
|
|
|
stdio_echo_state = echo;
|
2010-12-23 13:42:49 +01:00
|
|
|
tty = oldtty;
|
2010-12-23 13:42:50 +01:00
|
|
|
if (!echo) {
|
|
|
|
tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
|
2008-10-31 19:49:55 +01:00
|
|
|
|INLCR|IGNCR|ICRNL|IXON);
|
2010-12-23 13:42:50 +01: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;
|
|
|
|
}
|
|
|
|
if (!stdio_allow_signal)
|
2008-10-31 19:49:55 +01:00
|
|
|
tty.c_lflag &= ~ISIG;
|
|
|
|
|
|
|
|
tcsetattr (0, TCSANOW, &tty);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void qemu_chr_close_stdio(struct CharDriverState *chr)
|
|
|
|
{
|
|
|
|
term_exit();
|
|
|
|
fd_chr_close(chr);
|
|
|
|
}
|
|
|
|
|
2015-09-29 15:40:28 +02:00
|
|
|
static CharDriverState *qemu_chr_open_stdio(const char *id,
|
|
|
|
ChardevBackend *backend,
|
|
|
|
ChardevReturn *ret,
|
|
|
|
Error **errp)
|
2008-10-31 19:49:55 +01:00
|
|
|
{
|
2015-10-26 23:34:57 +01:00
|
|
|
ChardevStdio *opts = backend->u.stdio;
|
2008-10-31 19:49:55 +01:00
|
|
|
CharDriverState *chr;
|
2015-01-07 09:38:35 +01:00
|
|
|
struct sigaction act;
|
2008-10-31 19:49:55 +01:00
|
|
|
|
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 09:48:14 +01:00
|
|
|
if (is_daemonized()) {
|
2015-09-29 15:40:28 +02:00
|
|
|
error_setg(errp, "cannot use stdio with -daemonize");
|
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 09:48:14 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
2014-09-09 13:19:48 +02:00
|
|
|
|
|
|
|
if (stdio_in_use) {
|
2015-09-29 15:40:28 +02:00
|
|
|
error_setg(errp, "cannot use stdio by multiple character devices");
|
|
|
|
return NULL;
|
2014-09-09 13:19:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
stdio_in_use = true;
|
2013-03-05 18:51:17 +01:00
|
|
|
old_fd0_flags = fcntl(0, F_GETFL);
|
2014-09-09 13:19:48 +02:00
|
|
|
tcgetattr(0, &oldtty);
|
2014-08-11 11:34:20 +02:00
|
|
|
qemu_set_nonblock(0);
|
2013-03-05 18:51:17 +01:00
|
|
|
atexit(term_exit);
|
2010-12-23 13:42:49 +01:00
|
|
|
|
2015-01-07 09:38:35 +01:00
|
|
|
memset(&act, 0, sizeof(act));
|
|
|
|
act.sa_handler = term_stdio_handler;
|
|
|
|
sigaction(SIGCONT, &act, NULL);
|
|
|
|
|
2008-10-31 19:49:55 +01:00
|
|
|
chr = qemu_chr_open_fd(0, 1);
|
|
|
|
chr->chr_close = qemu_chr_close_stdio;
|
2010-12-23 13:42:50 +01:00
|
|
|
chr->chr_set_echo = qemu_chr_set_echo_stdio;
|
2013-02-21 12:34:58 +01:00
|
|
|
if (opts->has_signal) {
|
|
|
|
stdio_allow_signal = opts->signal;
|
|
|
|
}
|
2011-08-15 18:17:35 +02:00
|
|
|
qemu_chr_fe_set_echo(chr, false);
|
2008-10-31 19:49:55 +01: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 15:09:08 +01:00
|
|
|
return chr;
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
|
2009-11-29 18:00:41 +01:00
|
|
|
|| defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
|
|
|
|
|| defined(__GLIBC__)
|
2008-10-31 19:49:55 +01:00
|
|
|
|
2015-10-12 09:46:23 +02:00
|
|
|
#define HAVE_CHARDEV_SERIAL 1
|
|
|
|
#define HAVE_CHARDEV_PTY 1
|
2012-12-19 16:35:42 +01:00
|
|
|
|
2008-10-31 19:49:55 +01:00
|
|
|
typedef struct {
|
2013-03-05 18:51:20 +01:00
|
|
|
GIOChannel *fd;
|
2008-10-31 19:49:55 +01:00
|
|
|
int read_bytes;
|
2014-06-18 08:43:58 +02:00
|
|
|
|
|
|
|
/* Protected by the CharDriverState chr_write_lock. */
|
|
|
|
int connected;
|
2013-03-05 18:51:26 +01:00
|
|
|
guint timer_tag;
|
qemu-char: fix deadlock with "-monitor pty"
qemu_chr_be_generic_open cannot be called with the write lock taken,
because it calls client code that may call qemu_chr_fe_write. This
actually happens for the monitor:
0x00007ffff27dbf79 in __GI_raise (sig=sig@entry=6)
0x00007ffff27df388 in __GI_abort ()
0x00005555555ef489 in error_exit (err=<optimized out>, msg=msg@entry=0x5555559796d0 <__func__.5959> "qemu_mutex_lock")
0x00005555558f9080 in qemu_mutex_lock (mutex=mutex@entry=0x555556248a30)
0x0000555555713936 in qemu_chr_fe_write (s=0x555556248a30, buf=buf@entry=0x5555563d8870 "QEMU 2.0.90 monitor - type 'help' for more information\r\n", len=56)
0x00005555556217fd in monitor_flush_locked (mon=mon@entry=0x555556251fd0)
0x0000555555621a12 in monitor_flush_locked (mon=0x555556251fd0)
monitor_puts (mon=mon@entry=0x555556251fd0, str=0x55555634bfa7 "", str@entry=0x55555634bf70 "QEMU 2.0.90 monitor - type 'help' for more information\n")
0x0000555555624359 in monitor_vprintf (mon=0x555556251fd0, fmt=<optimized out>, ap=<optimized out>)
0x0000555555624414 in monitor_printf (mon=<optimized out>, fmt=fmt@entry=0x5555559105a0 "QEMU %s monitor - type 'help' for more information\n")
0x0000555555629806 in monitor_event (opaque=0x555556251fd0, event=<optimized out>)
0x000055555571343c in qemu_chr_be_generic_open (s=0x555556248a30)
To avoid this, defer the call to an idle callback, which will be
called as soon as the main loop is re-entered. In order to simplify
the cleanup and do it in one place only, change pty_chr_close to
call pty_chr_state.
To reproduce, run with "-monitor pty", then try to read from the
slave /dev/pts/FOO that it creates.
Fixes: 9005b2a7589540a3733b3abdcfbccfe7746cd1a1
Reported-by: Li Liang <liangx.z.li@intel.com>
Reviewed-by: Fam Zheng <famz@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2014-07-11 12:11:38 +02:00
|
|
|
guint open_tag;
|
2008-10-31 19:49:55 +01:00
|
|
|
} PtyCharDriver;
|
|
|
|
|
2014-06-18 08:43:58 +02:00
|
|
|
static void pty_chr_update_read_handler_locked(CharDriverState *chr);
|
2008-10-31 19:49:55 +01:00
|
|
|
static void pty_chr_state(CharDriverState *chr, int connected);
|
|
|
|
|
2013-03-05 18:51:26 +01:00
|
|
|
static gboolean pty_chr_timer(gpointer opaque)
|
|
|
|
{
|
|
|
|
struct CharDriverState *chr = opaque;
|
|
|
|
PtyCharDriver *s = chr->opaque;
|
|
|
|
|
2014-06-18 08:43:58 +02:00
|
|
|
qemu_mutex_lock(&chr->chr_write_lock);
|
2013-04-25 13:53:02 +02:00
|
|
|
s->timer_tag = 0;
|
qemu-char: fix deadlock with "-monitor pty"
qemu_chr_be_generic_open cannot be called with the write lock taken,
because it calls client code that may call qemu_chr_fe_write. This
actually happens for the monitor:
0x00007ffff27dbf79 in __GI_raise (sig=sig@entry=6)
0x00007ffff27df388 in __GI_abort ()
0x00005555555ef489 in error_exit (err=<optimized out>, msg=msg@entry=0x5555559796d0 <__func__.5959> "qemu_mutex_lock")
0x00005555558f9080 in qemu_mutex_lock (mutex=mutex@entry=0x555556248a30)
0x0000555555713936 in qemu_chr_fe_write (s=0x555556248a30, buf=buf@entry=0x5555563d8870 "QEMU 2.0.90 monitor - type 'help' for more information\r\n", len=56)
0x00005555556217fd in monitor_flush_locked (mon=mon@entry=0x555556251fd0)
0x0000555555621a12 in monitor_flush_locked (mon=0x555556251fd0)
monitor_puts (mon=mon@entry=0x555556251fd0, str=0x55555634bfa7 "", str@entry=0x55555634bf70 "QEMU 2.0.90 monitor - type 'help' for more information\n")
0x0000555555624359 in monitor_vprintf (mon=0x555556251fd0, fmt=<optimized out>, ap=<optimized out>)
0x0000555555624414 in monitor_printf (mon=<optimized out>, fmt=fmt@entry=0x5555559105a0 "QEMU %s monitor - type 'help' for more information\n")
0x0000555555629806 in monitor_event (opaque=0x555556251fd0, event=<optimized out>)
0x000055555571343c in qemu_chr_be_generic_open (s=0x555556248a30)
To avoid this, defer the call to an idle callback, which will be
called as soon as the main loop is re-entered. In order to simplify
the cleanup and do it in one place only, change pty_chr_close to
call pty_chr_state.
To reproduce, run with "-monitor pty", then try to read from the
slave /dev/pts/FOO that it creates.
Fixes: 9005b2a7589540a3733b3abdcfbccfe7746cd1a1
Reported-by: Li Liang <liangx.z.li@intel.com>
Reviewed-by: Fam Zheng <famz@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2014-07-11 12:11:38 +02:00
|
|
|
s->open_tag = 0;
|
2013-08-22 11:43:58 +02:00
|
|
|
if (!s->connected) {
|
|
|
|
/* Next poll ... */
|
2014-06-18 08:43:58 +02:00
|
|
|
pty_chr_update_read_handler_locked(chr);
|
2013-08-22 11:43:58 +02:00
|
|
|
}
|
2014-06-18 08:43:58 +02:00
|
|
|
qemu_mutex_unlock(&chr->chr_write_lock);
|
2013-03-05 18:51:26 +01:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2014-06-18 08:43:58 +02:00
|
|
|
/* Called with chr_write_lock held. */
|
2013-03-05 18:51:26 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-18 08:43:58 +02:00
|
|
|
/* Called with chr_write_lock held. */
|
|
|
|
static void pty_chr_update_read_handler_locked(CharDriverState *chr)
|
2014-06-18 08:43:57 +02:00
|
|
|
{
|
|
|
|
PtyCharDriver *s = chr->opaque;
|
|
|
|
GPollFD pfd;
|
2015-12-01 11:27:00 +01:00
|
|
|
int rc;
|
2014-06-18 08:43:57 +02:00
|
|
|
|
|
|
|
pfd.fd = g_io_channel_unix_get_fd(s->fd);
|
|
|
|
pfd.events = G_IO_OUT;
|
|
|
|
pfd.revents = 0;
|
2015-12-01 11:27:00 +01:00
|
|
|
do {
|
|
|
|
rc = g_poll(&pfd, 1, 0);
|
|
|
|
} while (rc == -1 && errno == EINTR);
|
|
|
|
assert(rc >= 0);
|
|
|
|
|
2014-06-18 08:43:57 +02:00
|
|
|
if (pfd.revents & G_IO_HUP) {
|
|
|
|
pty_chr_state(chr, 0);
|
|
|
|
} else {
|
|
|
|
pty_chr_state(chr, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-18 08:43:58 +02:00
|
|
|
static void pty_chr_update_read_handler(CharDriverState *chr)
|
|
|
|
{
|
|
|
|
qemu_mutex_lock(&chr->chr_write_lock);
|
|
|
|
pty_chr_update_read_handler_locked(chr);
|
|
|
|
qemu_mutex_unlock(&chr->chr_write_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Called with chr_write_lock held. */
|
2008-10-31 19:49:55 +01: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 */
|
2014-06-18 08:43:58 +02:00
|
|
|
pty_chr_update_read_handler_locked(chr);
|
2014-07-28 13:39:14 +02:00
|
|
|
if (!s->connected) {
|
|
|
|
return 0;
|
|
|
|
}
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
2013-03-29 17:39:50 +01:00
|
|
|
return io_channel_send(s->fd, buf, len);
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
|
|
|
|
2013-03-05 18:51:24 +01:00
|
|
|
static GSource *pty_chr_add_watch(CharDriverState *chr, GIOCondition cond)
|
|
|
|
{
|
|
|
|
PtyCharDriver *s = chr->opaque;
|
2014-07-24 16:08:04 +02:00
|
|
|
if (!s->connected) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2013-03-05 18:51:24 +01:00
|
|
|
return g_io_create_watch(s->fd, cond);
|
|
|
|
}
|
|
|
|
|
2008-10-31 19:49:55 +01:00
|
|
|
static int pty_chr_read_poll(void *opaque)
|
|
|
|
{
|
|
|
|
CharDriverState *chr = opaque;
|
|
|
|
PtyCharDriver *s = chr->opaque;
|
|
|
|
|
2011-08-15 18:17:31 +02:00
|
|
|
s->read_bytes = qemu_chr_be_can_write(chr);
|
2008-10-31 19:49:55 +01:00
|
|
|
return s->read_bytes;
|
|
|
|
}
|
|
|
|
|
2013-03-05 18:51:20 +01:00
|
|
|
static gboolean pty_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
|
2008-10-31 19:49:55 +01:00
|
|
|
{
|
|
|
|
CharDriverState *chr = opaque;
|
|
|
|
PtyCharDriver *s = chr->opaque;
|
2013-03-05 18:51:20 +01:00
|
|
|
gsize size, len;
|
2009-11-03 15:29:54 +01:00
|
|
|
uint8_t buf[READ_BUF_LEN];
|
2013-03-05 18:51:20 +01:00
|
|
|
GIOStatus status;
|
2008-10-31 19:49:55 +01:00
|
|
|
|
|
|
|
len = sizeof(buf);
|
|
|
|
if (len > s->read_bytes)
|
|
|
|
len = s->read_bytes;
|
2013-04-19 17:32:08 +02:00
|
|
|
if (len == 0) {
|
|
|
|
return TRUE;
|
|
|
|
}
|
2013-03-05 18:51:20 +01:00
|
|
|
status = g_io_channel_read_chars(s->fd, (gchar *)buf, len, &size, NULL);
|
|
|
|
if (status != G_IO_STATUS_NORMAL) {
|
2008-10-31 19:49:55 +01:00
|
|
|
pty_chr_state(chr, 0);
|
2013-03-05 18:51:20 +01:00
|
|
|
return FALSE;
|
|
|
|
} else {
|
2008-10-31 19:49:55 +01:00
|
|
|
pty_chr_state(chr, 1);
|
2011-08-15 18:17:30 +02:00
|
|
|
qemu_chr_be_write(chr, buf, size);
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
2013-03-05 18:51:20 +01:00
|
|
|
return TRUE;
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
|
|
|
|
qemu-char: fix deadlock with "-monitor pty"
qemu_chr_be_generic_open cannot be called with the write lock taken,
because it calls client code that may call qemu_chr_fe_write. This
actually happens for the monitor:
0x00007ffff27dbf79 in __GI_raise (sig=sig@entry=6)
0x00007ffff27df388 in __GI_abort ()
0x00005555555ef489 in error_exit (err=<optimized out>, msg=msg@entry=0x5555559796d0 <__func__.5959> "qemu_mutex_lock")
0x00005555558f9080 in qemu_mutex_lock (mutex=mutex@entry=0x555556248a30)
0x0000555555713936 in qemu_chr_fe_write (s=0x555556248a30, buf=buf@entry=0x5555563d8870 "QEMU 2.0.90 monitor - type 'help' for more information\r\n", len=56)
0x00005555556217fd in monitor_flush_locked (mon=mon@entry=0x555556251fd0)
0x0000555555621a12 in monitor_flush_locked (mon=0x555556251fd0)
monitor_puts (mon=mon@entry=0x555556251fd0, str=0x55555634bfa7 "", str@entry=0x55555634bf70 "QEMU 2.0.90 monitor - type 'help' for more information\n")
0x0000555555624359 in monitor_vprintf (mon=0x555556251fd0, fmt=<optimized out>, ap=<optimized out>)
0x0000555555624414 in monitor_printf (mon=<optimized out>, fmt=fmt@entry=0x5555559105a0 "QEMU %s monitor - type 'help' for more information\n")
0x0000555555629806 in monitor_event (opaque=0x555556251fd0, event=<optimized out>)
0x000055555571343c in qemu_chr_be_generic_open (s=0x555556248a30)
To avoid this, defer the call to an idle callback, which will be
called as soon as the main loop is re-entered. In order to simplify
the cleanup and do it in one place only, change pty_chr_close to
call pty_chr_state.
To reproduce, run with "-monitor pty", then try to read from the
slave /dev/pts/FOO that it creates.
Fixes: 9005b2a7589540a3733b3abdcfbccfe7746cd1a1
Reported-by: Li Liang <liangx.z.li@intel.com>
Reviewed-by: Fam Zheng <famz@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2014-07-11 12:11:38 +02:00
|
|
|
static gboolean qemu_chr_be_generic_open_func(gpointer opaque)
|
|
|
|
{
|
|
|
|
CharDriverState *chr = opaque;
|
|
|
|
PtyCharDriver *s = chr->opaque;
|
|
|
|
|
|
|
|
s->open_tag = 0;
|
|
|
|
qemu_chr_be_generic_open(chr);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2014-06-18 08:43:58 +02:00
|
|
|
/* Called with chr_write_lock held. */
|
2008-10-31 19:49:55 +01:00
|
|
|
static void pty_chr_state(CharDriverState *chr, int connected)
|
|
|
|
{
|
|
|
|
PtyCharDriver *s = chr->opaque;
|
|
|
|
|
|
|
|
if (!connected) {
|
qemu-char: fix deadlock with "-monitor pty"
qemu_chr_be_generic_open cannot be called with the write lock taken,
because it calls client code that may call qemu_chr_fe_write. This
actually happens for the monitor:
0x00007ffff27dbf79 in __GI_raise (sig=sig@entry=6)
0x00007ffff27df388 in __GI_abort ()
0x00005555555ef489 in error_exit (err=<optimized out>, msg=msg@entry=0x5555559796d0 <__func__.5959> "qemu_mutex_lock")
0x00005555558f9080 in qemu_mutex_lock (mutex=mutex@entry=0x555556248a30)
0x0000555555713936 in qemu_chr_fe_write (s=0x555556248a30, buf=buf@entry=0x5555563d8870 "QEMU 2.0.90 monitor - type 'help' for more information\r\n", len=56)
0x00005555556217fd in monitor_flush_locked (mon=mon@entry=0x555556251fd0)
0x0000555555621a12 in monitor_flush_locked (mon=0x555556251fd0)
monitor_puts (mon=mon@entry=0x555556251fd0, str=0x55555634bfa7 "", str@entry=0x55555634bf70 "QEMU 2.0.90 monitor - type 'help' for more information\n")
0x0000555555624359 in monitor_vprintf (mon=0x555556251fd0, fmt=<optimized out>, ap=<optimized out>)
0x0000555555624414 in monitor_printf (mon=<optimized out>, fmt=fmt@entry=0x5555559105a0 "QEMU %s monitor - type 'help' for more information\n")
0x0000555555629806 in monitor_event (opaque=0x555556251fd0, event=<optimized out>)
0x000055555571343c in qemu_chr_be_generic_open (s=0x555556248a30)
To avoid this, defer the call to an idle callback, which will be
called as soon as the main loop is re-entered. In order to simplify
the cleanup and do it in one place only, change pty_chr_close to
call pty_chr_state.
To reproduce, run with "-monitor pty", then try to read from the
slave /dev/pts/FOO that it creates.
Fixes: 9005b2a7589540a3733b3abdcfbccfe7746cd1a1
Reported-by: Li Liang <liangx.z.li@intel.com>
Reviewed-by: Fam Zheng <famz@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2014-07-11 12:11:38 +02:00
|
|
|
if (s->open_tag) {
|
|
|
|
g_source_remove(s->open_tag);
|
|
|
|
s->open_tag = 0;
|
|
|
|
}
|
2013-08-28 11:53:37 +02:00
|
|
|
remove_fd_in_watch(chr);
|
2008-10-31 19:49:55 +01: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 18:51:26 +01:00
|
|
|
pty_chr_rearm_timer(chr, 1000);
|
2008-10-31 19:49:55 +01:00
|
|
|
} else {
|
2013-04-19 17:32:07 +02:00
|
|
|
if (s->timer_tag) {
|
|
|
|
g_source_remove(s->timer_tag);
|
|
|
|
s->timer_tag = 0;
|
|
|
|
}
|
|
|
|
if (!s->connected) {
|
qemu-char: fix deadlock with "-monitor pty"
qemu_chr_be_generic_open cannot be called with the write lock taken,
because it calls client code that may call qemu_chr_fe_write. This
actually happens for the monitor:
0x00007ffff27dbf79 in __GI_raise (sig=sig@entry=6)
0x00007ffff27df388 in __GI_abort ()
0x00005555555ef489 in error_exit (err=<optimized out>, msg=msg@entry=0x5555559796d0 <__func__.5959> "qemu_mutex_lock")
0x00005555558f9080 in qemu_mutex_lock (mutex=mutex@entry=0x555556248a30)
0x0000555555713936 in qemu_chr_fe_write (s=0x555556248a30, buf=buf@entry=0x5555563d8870 "QEMU 2.0.90 monitor - type 'help' for more information\r\n", len=56)
0x00005555556217fd in monitor_flush_locked (mon=mon@entry=0x555556251fd0)
0x0000555555621a12 in monitor_flush_locked (mon=0x555556251fd0)
monitor_puts (mon=mon@entry=0x555556251fd0, str=0x55555634bfa7 "", str@entry=0x55555634bf70 "QEMU 2.0.90 monitor - type 'help' for more information\n")
0x0000555555624359 in monitor_vprintf (mon=0x555556251fd0, fmt=<optimized out>, ap=<optimized out>)
0x0000555555624414 in monitor_printf (mon=<optimized out>, fmt=fmt@entry=0x5555559105a0 "QEMU %s monitor - type 'help' for more information\n")
0x0000555555629806 in monitor_event (opaque=0x555556251fd0, event=<optimized out>)
0x000055555571343c in qemu_chr_be_generic_open (s=0x555556248a30)
To avoid this, defer the call to an idle callback, which will be
called as soon as the main loop is re-entered. In order to simplify
the cleanup and do it in one place only, change pty_chr_close to
call pty_chr_state.
To reproduce, run with "-monitor pty", then try to read from the
slave /dev/pts/FOO that it creates.
Fixes: 9005b2a7589540a3733b3abdcfbccfe7746cd1a1
Reported-by: Li Liang <liangx.z.li@intel.com>
Reviewed-by: Fam Zheng <famz@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2014-07-11 12:11:38 +02:00
|
|
|
g_assert(s->open_tag == 0);
|
2013-04-19 17:32:07 +02:00
|
|
|
s->connected = 1;
|
qemu-char: fix deadlock with "-monitor pty"
qemu_chr_be_generic_open cannot be called with the write lock taken,
because it calls client code that may call qemu_chr_fe_write. This
actually happens for the monitor:
0x00007ffff27dbf79 in __GI_raise (sig=sig@entry=6)
0x00007ffff27df388 in __GI_abort ()
0x00005555555ef489 in error_exit (err=<optimized out>, msg=msg@entry=0x5555559796d0 <__func__.5959> "qemu_mutex_lock")
0x00005555558f9080 in qemu_mutex_lock (mutex=mutex@entry=0x555556248a30)
0x0000555555713936 in qemu_chr_fe_write (s=0x555556248a30, buf=buf@entry=0x5555563d8870 "QEMU 2.0.90 monitor - type 'help' for more information\r\n", len=56)
0x00005555556217fd in monitor_flush_locked (mon=mon@entry=0x555556251fd0)
0x0000555555621a12 in monitor_flush_locked (mon=0x555556251fd0)
monitor_puts (mon=mon@entry=0x555556251fd0, str=0x55555634bfa7 "", str@entry=0x55555634bf70 "QEMU 2.0.90 monitor - type 'help' for more information\n")
0x0000555555624359 in monitor_vprintf (mon=0x555556251fd0, fmt=<optimized out>, ap=<optimized out>)
0x0000555555624414 in monitor_printf (mon=<optimized out>, fmt=fmt@entry=0x5555559105a0 "QEMU %s monitor - type 'help' for more information\n")
0x0000555555629806 in monitor_event (opaque=0x555556251fd0, event=<optimized out>)
0x000055555571343c in qemu_chr_be_generic_open (s=0x555556248a30)
To avoid this, defer the call to an idle callback, which will be
called as soon as the main loop is re-entered. In order to simplify
the cleanup and do it in one place only, change pty_chr_close to
call pty_chr_state.
To reproduce, run with "-monitor pty", then try to read from the
slave /dev/pts/FOO that it creates.
Fixes: 9005b2a7589540a3733b3abdcfbccfe7746cd1a1
Reported-by: Li Liang <liangx.z.li@intel.com>
Reviewed-by: Fam Zheng <famz@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2014-07-11 12:11:38 +02:00
|
|
|
s->open_tag = g_idle_add(qemu_chr_be_generic_open_func, chr);
|
2014-02-25 11:12:35 +01:00
|
|
|
}
|
|
|
|
if (!chr->fd_in_tag) {
|
2013-08-28 11:48:29 +02:00
|
|
|
chr->fd_in_tag = io_add_watch_poll(s->fd, pty_chr_read_poll,
|
|
|
|
pty_chr_read, chr);
|
2013-04-19 17:32:07 +02:00
|
|
|
}
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pty_chr_close(struct CharDriverState *chr)
|
|
|
|
{
|
|
|
|
PtyCharDriver *s = chr->opaque;
|
2013-03-05 18:51:20 +01:00
|
|
|
int fd;
|
2008-10-31 19:49:55 +01:00
|
|
|
|
qemu-char: fix deadlock with "-monitor pty"
qemu_chr_be_generic_open cannot be called with the write lock taken,
because it calls client code that may call qemu_chr_fe_write. This
actually happens for the monitor:
0x00007ffff27dbf79 in __GI_raise (sig=sig@entry=6)
0x00007ffff27df388 in __GI_abort ()
0x00005555555ef489 in error_exit (err=<optimized out>, msg=msg@entry=0x5555559796d0 <__func__.5959> "qemu_mutex_lock")
0x00005555558f9080 in qemu_mutex_lock (mutex=mutex@entry=0x555556248a30)
0x0000555555713936 in qemu_chr_fe_write (s=0x555556248a30, buf=buf@entry=0x5555563d8870 "QEMU 2.0.90 monitor - type 'help' for more information\r\n", len=56)
0x00005555556217fd in monitor_flush_locked (mon=mon@entry=0x555556251fd0)
0x0000555555621a12 in monitor_flush_locked (mon=0x555556251fd0)
monitor_puts (mon=mon@entry=0x555556251fd0, str=0x55555634bfa7 "", str@entry=0x55555634bf70 "QEMU 2.0.90 monitor - type 'help' for more information\n")
0x0000555555624359 in monitor_vprintf (mon=0x555556251fd0, fmt=<optimized out>, ap=<optimized out>)
0x0000555555624414 in monitor_printf (mon=<optimized out>, fmt=fmt@entry=0x5555559105a0 "QEMU %s monitor - type 'help' for more information\n")
0x0000555555629806 in monitor_event (opaque=0x555556251fd0, event=<optimized out>)
0x000055555571343c in qemu_chr_be_generic_open (s=0x555556248a30)
To avoid this, defer the call to an idle callback, which will be
called as soon as the main loop is re-entered. In order to simplify
the cleanup and do it in one place only, change pty_chr_close to
call pty_chr_state.
To reproduce, run with "-monitor pty", then try to read from the
slave /dev/pts/FOO that it creates.
Fixes: 9005b2a7589540a3733b3abdcfbccfe7746cd1a1
Reported-by: Li Liang <liangx.z.li@intel.com>
Reviewed-by: Fam Zheng <famz@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2014-07-11 12:11:38 +02:00
|
|
|
qemu_mutex_lock(&chr->chr_write_lock);
|
|
|
|
pty_chr_state(chr, 0);
|
2013-03-05 18:51:20 +01:00
|
|
|
fd = g_io_channel_unix_get_fd(s->fd);
|
|
|
|
g_io_channel_unref(s->fd);
|
|
|
|
close(fd);
|
2013-03-05 18:51:26 +01:00
|
|
|
if (s->timer_tag) {
|
|
|
|
g_source_remove(s->timer_tag);
|
2013-04-19 17:32:06 +02:00
|
|
|
s->timer_tag = 0;
|
2013-03-05 18:51:26 +01:00
|
|
|
}
|
qemu-char: fix deadlock with "-monitor pty"
qemu_chr_be_generic_open cannot be called with the write lock taken,
because it calls client code that may call qemu_chr_fe_write. This
actually happens for the monitor:
0x00007ffff27dbf79 in __GI_raise (sig=sig@entry=6)
0x00007ffff27df388 in __GI_abort ()
0x00005555555ef489 in error_exit (err=<optimized out>, msg=msg@entry=0x5555559796d0 <__func__.5959> "qemu_mutex_lock")
0x00005555558f9080 in qemu_mutex_lock (mutex=mutex@entry=0x555556248a30)
0x0000555555713936 in qemu_chr_fe_write (s=0x555556248a30, buf=buf@entry=0x5555563d8870 "QEMU 2.0.90 monitor - type 'help' for more information\r\n", len=56)
0x00005555556217fd in monitor_flush_locked (mon=mon@entry=0x555556251fd0)
0x0000555555621a12 in monitor_flush_locked (mon=0x555556251fd0)
monitor_puts (mon=mon@entry=0x555556251fd0, str=0x55555634bfa7 "", str@entry=0x55555634bf70 "QEMU 2.0.90 monitor - type 'help' for more information\n")
0x0000555555624359 in monitor_vprintf (mon=0x555556251fd0, fmt=<optimized out>, ap=<optimized out>)
0x0000555555624414 in monitor_printf (mon=<optimized out>, fmt=fmt@entry=0x5555559105a0 "QEMU %s monitor - type 'help' for more information\n")
0x0000555555629806 in monitor_event (opaque=0x555556251fd0, event=<optimized out>)
0x000055555571343c in qemu_chr_be_generic_open (s=0x555556248a30)
To avoid this, defer the call to an idle callback, which will be
called as soon as the main loop is re-entered. In order to simplify
the cleanup and do it in one place only, change pty_chr_close to
call pty_chr_state.
To reproduce, run with "-monitor pty", then try to read from the
slave /dev/pts/FOO that it creates.
Fixes: 9005b2a7589540a3733b3abdcfbccfe7746cd1a1
Reported-by: Li Liang <liangx.z.li@intel.com>
Reviewed-by: Fam Zheng <famz@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2014-07-11 12:11:38 +02:00
|
|
|
qemu_mutex_unlock(&chr->chr_write_lock);
|
2011-08-21 05:09:37 +02:00
|
|
|
g_free(s);
|
2011-11-19 10:22:43 +01:00
|
|
|
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
|
|
|
|
2013-02-25 10:16:46 +01:00
|
|
|
static CharDriverState *qemu_chr_open_pty(const char *id,
|
2015-09-29 15:23:42 +02:00
|
|
|
ChardevBackend *backend,
|
|
|
|
ChardevReturn *ret,
|
|
|
|
Error **errp)
|
2008-10-31 19:49:55 +01:00
|
|
|
{
|
|
|
|
CharDriverState *chr;
|
|
|
|
PtyCharDriver *s;
|
2013-02-25 10:16:46 +01:00
|
|
|
int master_fd, slave_fd;
|
2008-10-31 19:49:55 +01:00
|
|
|
char pty_name[PATH_MAX];
|
|
|
|
|
2013-06-05 16:44:54 +02:00
|
|
|
master_fd = qemu_openpty_raw(&slave_fd, pty_name);
|
|
|
|
if (master_fd < 0) {
|
2015-09-29 15:23:42 +02:00
|
|
|
error_setg_errno(errp, errno, "Failed to create PTY");
|
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 15:09:08 +01:00
|
|
|
return NULL;
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
close(slave_fd);
|
2014-12-22 16:04:00 +01:00
|
|
|
qemu_set_nonblock(master_fd);
|
2008-10-31 19:49:55 +01:00
|
|
|
|
2014-06-18 08:43:55 +02:00
|
|
|
chr = qemu_chr_alloc();
|
2011-11-11 10:40:05 +01:00
|
|
|
|
2013-06-05 16:44:54 +02:00
|
|
|
chr->filename = g_strdup_printf("pty:%s", pty_name);
|
|
|
|
ret->pty = g_strdup(pty_name);
|
2013-02-25 10:16:46 +01:00
|
|
|
ret->has_pty = true;
|
2012-12-21 05:26:38 +01:00
|
|
|
|
2013-02-25 10:16:46 +01:00
|
|
|
fprintf(stderr, "char device redirected to %s (label %s)\n",
|
2013-06-05 16:44:54 +02:00
|
|
|
pty_name, id);
|
2008-10-31 19:49:55 +01:00
|
|
|
|
2015-09-14 13:54:03 +02:00
|
|
|
s = g_new0(PtyCharDriver, 1);
|
2008-10-31 19:49:55 +01: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 18:51:24 +01:00
|
|
|
chr->chr_add_watch = pty_chr_add_watch;
|
qemu-char: don't issue CHR_EVENT_OPEN in a BH
When CHR_EVENT_OPENED was initially added, it was CHR_EVENT_RESET,
and it was issued as a bottom-half:
86e94dea5b740dad65446c857f6959eae43e0ba6
Which we basically used to print out a greeting/prompt for the
monitor.
AFAICT the only reason this was ever done in a BH was because in
some cases we'd modify the chr_write handler for a new chardev
backend *after* the site where we issued the reset (see:
86e94d:qemu_chr_open_stdio())
At some point this event was renamed to CHR_EVENT_OPENED, and we've
maintained the use of this BH ever since.
However, due to 9f939df955a4152aad69a19a77e0898631bb2c18, we schedule
the BH via g_idle_add(), which is causing events to sometimes be
delivered after we've already begun processing data from backends,
leading to:
known bugs:
QMP:
session negotation resets with OPENED event, in some cases this
is causing new sessions to get sporadically reset
potential bugs:
hw/usb/redirect.c:
can_read handler checks for dev->parser != NULL, which may be
true if CLOSED BH has not been executed yet. In the past, OPENED
quiesced outstanding CLOSED events prior to us reading client
data. If it's delayed, our check may allow reads to occur even
though we haven't processed the OPENED event yet, and when we
do finally get the OPENED event, our state may get reset.
qtest.c:
can begin session before OPENED event is processed, leading to
a spurious reset of the system and irq_levels
gdbstub.c:
may start a gdb session prior to the machine being paused
To fix these, let's just drop the BH.
Since the initial reasoning for using it still applies to an extent,
work around that by deferring the delivery of CHR_EVENT_OPENED until
after the chardevs have been fully initialized, toward the end of
qmp_chardev_add() (or some cases, qemu_chr_new_from_opts()). This
defers delivery long enough that we can be assured a CharDriverState
is fully initialized before CHR_EVENT_OPENED is sent.
Also, rather than requiring each chardev to do an explicit open, do it
automatically, and allow the small few who don't desire such behavior to
suppress the OPENED-on-init behavior by setting a 'explicit_be_open'
flag.
We additionally add missing OPENED events for stdio backends on w32,
which were previously not being issued, causing us to not recieve the
banner and initial prompts for qmp/hmp.
Reported-by: Stefan Priebe <s.priebe@profihost.ag>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
Message-id: 1370636393-21044-1-git-send-email-mdroth@linux.vnet.ibm.com
Cc: qemu-stable@nongnu.org
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-06-07 22:19:53 +02:00
|
|
|
chr->explicit_be_open = true;
|
2008-10-31 19:49:55 +01:00
|
|
|
|
2013-03-05 18:51:20 +01:00
|
|
|
s->fd = io_channel_from_fd(master_fd);
|
2013-03-05 18:51:26 +01:00
|
|
|
s->timer_tag = 0;
|
2008-10-31 19:49:55 +01: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 15:09:08 +01:00
|
|
|
return chr;
|
2008-10-31 19:49:55 +01: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 16:10:10 +01: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 19:49:55 +01:00
|
|
|
spd = B115200;
|
2009-10-26 16:10:10 +01:00
|
|
|
} while (0);
|
2008-10-31 19:49:55 +01: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 18:51:19 +01:00
|
|
|
tty_serial_init(g_io_channel_unix_get_fd(s->fd_in),
|
|
|
|
ssp->speed, ssp->parity,
|
2008-10-31 19:49:55 +01:00
|
|
|
ssp->data_bits, ssp->stop_bits);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case CHR_IOCTL_SERIAL_SET_BREAK:
|
|
|
|
{
|
|
|
|
int enable = *(int *)arg;
|
2013-03-05 18:51:19 +01:00
|
|
|
if (enable) {
|
|
|
|
tcsendbreak(g_io_channel_unix_get_fd(s->fd_in), 1);
|
|
|
|
}
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case CHR_IOCTL_SERIAL_GET_TIOCM:
|
|
|
|
{
|
|
|
|
int sarg = 0;
|
|
|
|
int *targ = (int *)arg;
|
2013-03-05 18:51:19 +01:00
|
|
|
ioctl(g_io_channel_unix_get_fd(s->fd_in), TIOCMGET, &sarg);
|
2008-10-31 19:49:55 +01:00
|
|
|
*targ = 0;
|
2009-02-08 15:46:17 +01:00
|
|
|
if (sarg & TIOCM_CTS)
|
2008-10-31 19:49:55 +01:00
|
|
|
*targ |= CHR_TIOCM_CTS;
|
2009-02-08 15:46:17 +01:00
|
|
|
if (sarg & TIOCM_CAR)
|
2008-10-31 19:49:55 +01:00
|
|
|
*targ |= CHR_TIOCM_CAR;
|
2009-02-08 15:46:17 +01:00
|
|
|
if (sarg & TIOCM_DSR)
|
2008-10-31 19:49:55 +01:00
|
|
|
*targ |= CHR_TIOCM_DSR;
|
2009-02-08 15:46:17 +01:00
|
|
|
if (sarg & TIOCM_RI)
|
2008-10-31 19:49:55 +01:00
|
|
|
*targ |= CHR_TIOCM_RI;
|
2009-02-08 15:46:17 +01:00
|
|
|
if (sarg & TIOCM_DTR)
|
2008-10-31 19:49:55 +01:00
|
|
|
*targ |= CHR_TIOCM_DTR;
|
2009-02-08 15:46:17 +01:00
|
|
|
if (sarg & TIOCM_RTS)
|
2008-10-31 19:49:55 +01:00
|
|
|
*targ |= CHR_TIOCM_RTS;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case CHR_IOCTL_SERIAL_SET_TIOCM:
|
|
|
|
{
|
|
|
|
int sarg = *(int *)arg;
|
|
|
|
int targ = 0;
|
2013-03-05 18:51:19 +01:00
|
|
|
ioctl(g_io_channel_unix_get_fd(s->fd_in), TIOCMGET, &targ);
|
2009-02-08 15:46:17 +01: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 19:49:55 +01:00
|
|
|
targ |= TIOCM_DTR;
|
2009-02-08 15:46:17 +01:00
|
|
|
if (sarg & CHR_TIOCM_RTS)
|
2008-10-31 19:49:55 +01:00
|
|
|
targ |= TIOCM_RTS;
|
2013-03-05 18:51:19 +01:00
|
|
|
ioctl(g_io_channel_unix_get_fd(s->fd_in), TIOCMSET, &targ);
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-02-11 02:27:17 +01:00
|
|
|
static void qemu_chr_close_tty(CharDriverState *chr)
|
|
|
|
{
|
|
|
|
FDCharDriver *s = chr->opaque;
|
|
|
|
int fd = -1;
|
|
|
|
|
|
|
|
if (s) {
|
2013-03-05 18:51:19 +01:00
|
|
|
fd = g_io_channel_unix_get_fd(s->fd_in);
|
2010-02-11 02:27:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fd_chr_close(chr);
|
|
|
|
|
|
|
|
if (fd >= 0) {
|
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-19 13:50:29 +01: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 19:49:55 +01:00
|
|
|
#endif /* __linux__ || __sun__ */
|
|
|
|
|
|
|
|
#if defined(__linux__)
|
2012-12-19 16:35:42 +01:00
|
|
|
|
|
|
|
#define HAVE_CHARDEV_PARPORT 1
|
|
|
|
|
2008-10-31 19:49:55 +01: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 05:09:37 +02:00
|
|
|
g_free(drv);
|
2011-11-19 10:22:43 +01:00
|
|
|
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
|
|
|
|
2015-10-12 09:49:28 +02:00
|
|
|
static CharDriverState *qemu_chr_open_pp_fd(int fd, Error **errp)
|
2008-10-31 19:49:55 +01:00
|
|
|
{
|
|
|
|
CharDriverState *chr;
|
|
|
|
ParallelCharDriver *drv;
|
|
|
|
|
|
|
|
if (ioctl(fd, PPCLAIM) < 0) {
|
2015-10-12 09:49:28 +02:00
|
|
|
error_setg_errno(errp, errno, "not a parallel port");
|
2008-10-31 19:49:55 +01:00
|
|
|
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 15:09:08 +01:00
|
|
|
return NULL;
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
|
|
|
|
2015-09-14 13:54:03 +02:00
|
|
|
drv = g_new0(ParallelCharDriver, 1);
|
2008-10-31 19:49:55 +01:00
|
|
|
drv->fd = fd;
|
|
|
|
drv->mode = IEEE1284_MODE_COMPAT;
|
|
|
|
|
2014-06-18 08:43:55 +02:00
|
|
|
chr = qemu_chr_alloc();
|
2008-10-31 19:49:55 +01:00
|
|
|
chr->chr_write = null_chr_write;
|
|
|
|
chr->chr_ioctl = pp_ioctl;
|
|
|
|
chr->chr_close = pp_close;
|
|
|
|
chr->opaque = drv;
|
|
|
|
|
Revert "qemu-char: Print strerror message on failure" and deps
The commit's purpose is laudable:
The only way for chardev drivers to communicate an error was to
return a NULL pointer, which resulted in an error message that
said _that_ something went wrong, but not _why_.
It attempts to achieve it by changing the interface to return 0/-errno
and update qemu_chr_open_opts() to use strerror() to display a more
helpful error message. Unfortunately, it has serious flaws:
1. Backends "socket" and "udp" return bogus error codes, because
qemu_chr_open_socket() and qemu_chr_open_udp() assume that
unix_listen_opts(), unix_connect_opts(), inet_listen_opts(),
inet_connect_opts() and inet_dgram_opts() fail with errno set
appropriately. That assumption is wrong, and the commit turns
unspecific error messages into misleading error messages. For
instance:
$ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx
inet_connect: host and/or port not specified
chardev: opening backend "socket" failed: No such file or directory
ENOENT is what happens to be in my errno when the backend returns
-errno. Let's put ERANGE there just for giggles:
$ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx -drive if=none,iops=99999999999999999999
inet_connect: host and/or port not specified
chardev: opening backend "socket" failed: Numerical result out of range
Worse: when errno happens to be zero, return -errno erroneously
signals success, and qemu_chr_new_from_opts() dies dereferencing
uninitialized chr. I observe this with "-serial unix:".
2. All qemu_chr_open_opts() knows about the error is an errno error
code. That's simply not enough for a decent message. For instance,
when inet_dgram() can't resolve the parameter host, which errno code
should it use? What if it can't resolve parameter localaddr?
Clue: many backends already report errors in their open methods.
Let's revert the flawed commit along with its dependencies, and fix up
the silent error paths instead.
This reverts commit 6e1db57b2ac9025c2443c665a0d9e78748637b26.
Conflicts:
console.c
hw/baum.c
qemu-char.c
This reverts commit aad04cd024f0c59f0b96f032cde2e24eb3abba6d.
The parts of commit db418a0a "Add stdio char device on windows" that
depend on the reverted change fixed up.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2012-02-07 15:09:08 +01:00
|
|
|
return chr;
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
|
|
|
#endif /* __linux__ */
|
|
|
|
|
2009-11-29 18:00:41 +01:00
|
|
|
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
|
2012-12-19 16:35:42 +01:00
|
|
|
|
|
|
|
#define HAVE_CHARDEV_PARPORT 1
|
|
|
|
|
2008-11-22 21:49:12 +01:00
|
|
|
static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
|
|
|
|
{
|
2011-02-23 19:09:16 +01:00
|
|
|
int fd = (int)(intptr_t)chr->opaque;
|
2008-11-22 21:49:12 +01: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;
|
|
|
|
}
|
|
|
|
|
2015-10-12 09:49:28 +02:00
|
|
|
static CharDriverState *qemu_chr_open_pp_fd(int fd, Error **errp)
|
2008-11-22 21:49:12 +01:00
|
|
|
{
|
|
|
|
CharDriverState *chr;
|
|
|
|
|
2014-06-18 08:43:55 +02:00
|
|
|
chr = qemu_chr_alloc();
|
2011-02-23 19:09:16 +01:00
|
|
|
chr->opaque = (void *)(intptr_t)fd;
|
2008-11-22 21:49:12 +01:00
|
|
|
chr->chr_write = null_chr_write;
|
|
|
|
chr->chr_ioctl = pp_ioctl;
|
qemu-char: don't issue CHR_EVENT_OPEN in a BH
When CHR_EVENT_OPENED was initially added, it was CHR_EVENT_RESET,
and it was issued as a bottom-half:
86e94dea5b740dad65446c857f6959eae43e0ba6
Which we basically used to print out a greeting/prompt for the
monitor.
AFAICT the only reason this was ever done in a BH was because in
some cases we'd modify the chr_write handler for a new chardev
backend *after* the site where we issued the reset (see:
86e94d:qemu_chr_open_stdio())
At some point this event was renamed to CHR_EVENT_OPENED, and we've
maintained the use of this BH ever since.
However, due to 9f939df955a4152aad69a19a77e0898631bb2c18, we schedule
the BH via g_idle_add(), which is causing events to sometimes be
delivered after we've already begun processing data from backends,
leading to:
known bugs:
QMP:
session negotation resets with OPENED event, in some cases this
is causing new sessions to get sporadically reset
potential bugs:
hw/usb/redirect.c:
can_read handler checks for dev->parser != NULL, which may be
true if CLOSED BH has not been executed yet. In the past, OPENED
quiesced outstanding CLOSED events prior to us reading client
data. If it's delayed, our check may allow reads to occur even
though we haven't processed the OPENED event yet, and when we
do finally get the OPENED event, our state may get reset.
qtest.c:
can begin session before OPENED event is processed, leading to
a spurious reset of the system and irq_levels
gdbstub.c:
may start a gdb session prior to the machine being paused
To fix these, let's just drop the BH.
Since the initial reasoning for using it still applies to an extent,
work around that by deferring the delivery of CHR_EVENT_OPENED until
after the chardevs have been fully initialized, toward the end of
qmp_chardev_add() (or some cases, qemu_chr_new_from_opts()). This
defers delivery long enough that we can be assured a CharDriverState
is fully initialized before CHR_EVENT_OPENED is sent.
Also, rather than requiring each chardev to do an explicit open, do it
automatically, and allow the small few who don't desire such behavior to
suppress the OPENED-on-init behavior by setting a 'explicit_be_open'
flag.
We additionally add missing OPENED events for stdio backends on w32,
which were previously not being issued, causing us to not recieve the
banner and initial prompts for qmp/hmp.
Reported-by: Stefan Priebe <s.priebe@profihost.ag>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
Message-id: 1370636393-21044-1-git-send-email-mdroth@linux.vnet.ibm.com
Cc: qemu-stable@nongnu.org
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-06-07 22:19:53 +02:00
|
|
|
chr->explicit_be_open = true;
|
Revert "qemu-char: Print strerror message on failure" and deps
The commit's purpose is laudable:
The only way for chardev drivers to communicate an error was to
return a NULL pointer, which resulted in an error message that
said _that_ something went wrong, but not _why_.
It attempts to achieve it by changing the interface to return 0/-errno
and update qemu_chr_open_opts() to use strerror() to display a more
helpful error message. Unfortunately, it has serious flaws:
1. Backends "socket" and "udp" return bogus error codes, because
qemu_chr_open_socket() and qemu_chr_open_udp() assume that
unix_listen_opts(), unix_connect_opts(), inet_listen_opts(),
inet_connect_opts() and inet_dgram_opts() fail with errno set
appropriately. That assumption is wrong, and the commit turns
unspecific error messages into misleading error messages. For
instance:
$ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx
inet_connect: host and/or port not specified
chardev: opening backend "socket" failed: No such file or directory
ENOENT is what happens to be in my errno when the backend returns
-errno. Let's put ERANGE there just for giggles:
$ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx -drive if=none,iops=99999999999999999999
inet_connect: host and/or port not specified
chardev: opening backend "socket" failed: Numerical result out of range
Worse: when errno happens to be zero, return -errno erroneously
signals success, and qemu_chr_new_from_opts() dies dereferencing
uninitialized chr. I observe this with "-serial unix:".
2. All qemu_chr_open_opts() knows about the error is an errno error
code. That's simply not enough for a decent message. For instance,
when inet_dgram() can't resolve the parameter host, which errno code
should it use? What if it can't resolve parameter localaddr?
Clue: many backends already report errors in their open methods.
Let's revert the flawed commit along with its dependencies, and fix up
the silent error paths instead.
This reverts commit 6e1db57b2ac9025c2443c665a0d9e78748637b26.
Conflicts:
console.c
hw/baum.c
qemu-char.c
This reverts commit aad04cd024f0c59f0b96f032cde2e24eb3abba6d.
The parts of commit db418a0a "Add stdio char device on windows" that
depend on the reverted change fixed up.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2012-02-07 15:09:08 +01:00
|
|
|
return chr;
|
2008-11-22 21:49:12 +01:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-10-31 19:49:55 +01:00
|
|
|
#else /* _WIN32 */
|
|
|
|
|
2015-10-12 09:46:23 +02:00
|
|
|
#define HAVE_CHARDEV_SERIAL 1
|
|
|
|
|
2008-10-31 19:49:55 +01:00
|
|
|
typedef struct {
|
|
|
|
int max_size;
|
|
|
|
HANDLE hcom, hrecv, hsend;
|
2014-06-18 08:43:58 +02:00
|
|
|
OVERLAPPED orecv;
|
2008-10-31 19:49:55 +01:00
|
|
|
BOOL fpipe;
|
|
|
|
DWORD len;
|
2014-06-18 08:43:58 +02:00
|
|
|
|
|
|
|
/* Protected by the CharDriverState chr_write_lock. */
|
|
|
|
OVERLAPPED osend;
|
2008-10-31 19:49:55 +01:00
|
|
|
} WinCharState;
|
|
|
|
|
2011-10-06 16:37:51 +02:00
|
|
|
typedef struct {
|
|
|
|
HANDLE hStdIn;
|
|
|
|
HANDLE hInputReadyEvent;
|
|
|
|
HANDLE hInputDoneEvent;
|
|
|
|
HANDLE hInputThread;
|
|
|
|
uint8_t win_stdio_buf;
|
|
|
|
} WinStdioCharState;
|
|
|
|
|
2008-10-31 19:49:55 +01: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 17:57:48 +02:00
|
|
|
|
2011-11-19 10:22:43 +01:00
|
|
|
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
|
|
|
|
2015-09-29 15:08:05 +02:00
|
|
|
static int win_chr_init(CharDriverState *chr, const char *filename, Error **errp)
|
2008-10-31 19:49:55 +01:00
|
|
|
{
|
|
|
|
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) {
|
2015-09-29 15:08:05 +02:00
|
|
|
error_setg(errp, "Failed CreateEvent");
|
2008-10-31 19:49:55 +01:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
s->hrecv = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
|
|
if (!s->hrecv) {
|
2015-09-29 15:08:05 +02:00
|
|
|
error_setg(errp, "Failed CreateEvent");
|
2008-10-31 19:49:55 +01:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
s->hcom = CreateFile(filename, GENERIC_READ|GENERIC_WRITE, 0, NULL,
|
|
|
|
OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
|
|
|
|
if (s->hcom == INVALID_HANDLE_VALUE) {
|
2015-09-29 15:08:05 +02:00
|
|
|
error_setg(errp, "Failed CreateFile (%lu)", GetLastError());
|
2008-10-31 19:49:55 +01:00
|
|
|
s->hcom = NULL;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!SetupComm(s->hcom, NRECVBUF, NSENDBUF)) {
|
2015-09-29 15:08:05 +02:00
|
|
|
error_setg(errp, "Failed SetupComm");
|
2008-10-31 19:49:55 +01:00
|
|
|
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)) {
|
2015-09-29 15:08:05 +02:00
|
|
|
error_setg(errp, "Failed SetCommState");
|
2008-10-31 19:49:55 +01:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!SetCommMask(s->hcom, EV_ERR)) {
|
2015-09-29 15:08:05 +02:00
|
|
|
error_setg(errp, "Failed SetCommMask");
|
2008-10-31 19:49:55 +01:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
cto.ReadIntervalTimeout = MAXDWORD;
|
|
|
|
if (!SetCommTimeouts(s->hcom, &cto)) {
|
2015-09-29 15:08:05 +02:00
|
|
|
error_setg(errp, "Failed SetCommTimeouts");
|
2008-10-31 19:49:55 +01:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ClearCommError(s->hcom, &err, &comstat)) {
|
2015-09-29 15:08:05 +02:00
|
|
|
error_setg(errp, "Failed ClearCommError");
|
2008-10-31 19:49:55 +01:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
qemu_add_polling_cb(win_chr_poll, chr);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
win_chr_close(chr);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-06-18 08:43:58 +02:00
|
|
|
/* Called with chr_write_lock held. */
|
2008-10-31 19:49:55 +01:00
|
|
|
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 18:17:31 +02:00
|
|
|
s->max_size = qemu_chr_be_can_write(chr);
|
2008-10-31 19:49:55 +01:00
|
|
|
return s->max_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void win_chr_readfile(CharDriverState *chr)
|
|
|
|
{
|
|
|
|
WinCharState *s = chr->opaque;
|
|
|
|
int ret, err;
|
2009-11-03 15:29:54 +01:00
|
|
|
uint8_t buf[READ_BUF_LEN];
|
2008-10-31 19:49:55 +01: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 18:17:30 +02:00
|
|
|
qemu_chr_be_write(chr, buf, size);
|
2008-10-31 19:49:55 +01: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;
|
|
|
|
}
|
|
|
|
|
2015-09-29 15:08:05 +02:00
|
|
|
static CharDriverState *qemu_chr_open_win_path(const char *filename,
|
|
|
|
Error **errp)
|
2008-10-31 19:49:55 +01:00
|
|
|
{
|
|
|
|
CharDriverState *chr;
|
|
|
|
WinCharState *s;
|
|
|
|
|
2014-06-18 08:43:55 +02:00
|
|
|
chr = qemu_chr_alloc();
|
2015-09-14 13:54:03 +02:00
|
|
|
s = g_new0(WinCharState, 1);
|
2008-10-31 19:49:55 +01:00
|
|
|
chr->opaque = s;
|
|
|
|
chr->chr_write = win_chr_write;
|
|
|
|
chr->chr_close = win_chr_close;
|
|
|
|
|
2015-09-29 15:08:05 +02:00
|
|
|
if (win_chr_init(chr, filename, errp) < 0) {
|
2011-10-07 07:38:46 +02: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 15:09:08 +01:00
|
|
|
return NULL;
|
2008-10-31 19:49:55 +01: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 15:09:08 +01:00
|
|
|
return chr;
|
2008-10-31 19:49:55 +01: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;
|
|
|
|
}
|
|
|
|
|
2015-09-29 15:14:07 +02:00
|
|
|
static int win_chr_pipe_init(CharDriverState *chr, const char *filename,
|
|
|
|
Error **errp)
|
2008-10-31 19:49:55 +01:00
|
|
|
{
|
|
|
|
WinCharState *s = chr->opaque;
|
|
|
|
OVERLAPPED ov;
|
|
|
|
int ret;
|
|
|
|
DWORD size;
|
2014-10-02 18:17:33 +02:00
|
|
|
char openname[CHR_MAX_FILENAME_SIZE];
|
2008-10-31 19:49:55 +01:00
|
|
|
|
|
|
|
s->fpipe = TRUE;
|
|
|
|
|
|
|
|
s->hsend = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
|
|
if (!s->hsend) {
|
2015-09-29 15:14:07 +02:00
|
|
|
error_setg(errp, "Failed CreateEvent");
|
2008-10-31 19:49:55 +01:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
s->hrecv = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
|
|
if (!s->hrecv) {
|
2015-09-29 15:14:07 +02:00
|
|
|
error_setg(errp, "Failed CreateEvent");
|
2008-10-31 19:49:55 +01:00
|
|
|
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) {
|
2015-09-29 15:14:07 +02:00
|
|
|
error_setg(errp, "Failed CreateNamedPipe (%lu)", GetLastError());
|
2008-10-31 19:49:55 +01:00
|
|
|
s->hcom = NULL;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
ZeroMemory(&ov, sizeof(ov));
|
|
|
|
ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
|
|
ret = ConnectNamedPipe(s->hcom, &ov);
|
|
|
|
if (ret) {
|
2015-09-29 15:14:07 +02:00
|
|
|
error_setg(errp, "Failed ConnectNamedPipe");
|
2008-10-31 19:49:55 +01:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = GetOverlappedResult(s->hcom, &ov, &size, TRUE);
|
|
|
|
if (!ret) {
|
2015-09-29 15:14:07 +02:00
|
|
|
error_setg(errp, "Failed GetOverlappedResult");
|
2008-10-31 19:49:55 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-09-29 15:14:07 +02:00
|
|
|
static CharDriverState *qemu_chr_open_pipe(const char *id,
|
|
|
|
ChardevBackend *backend,
|
|
|
|
ChardevReturn *ret,
|
|
|
|
Error **errp)
|
2008-10-31 19:49:55 +01:00
|
|
|
{
|
2015-10-26 23:34:57 +01:00
|
|
|
ChardevHostdev *opts = backend->u.pipe;
|
2013-02-25 11:50:55 +01:00
|
|
|
const char *filename = opts->device;
|
2008-10-31 19:49:55 +01:00
|
|
|
CharDriverState *chr;
|
|
|
|
WinCharState *s;
|
|
|
|
|
2014-06-18 08:43:55 +02:00
|
|
|
chr = qemu_chr_alloc();
|
2015-09-14 13:54:03 +02:00
|
|
|
s = g_new0(WinCharState, 1);
|
2008-10-31 19:49:55 +01:00
|
|
|
chr->opaque = s;
|
|
|
|
chr->chr_write = win_chr_write;
|
|
|
|
chr->chr_close = win_chr_close;
|
|
|
|
|
2015-09-29 15:14:07 +02:00
|
|
|
if (win_chr_pipe_init(chr, filename, errp) < 0) {
|
2011-10-07 07:38:46 +02: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 15:09:08 +01:00
|
|
|
return NULL;
|
2008-10-31 19:49:55 +01: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 15:09:08 +01:00
|
|
|
return chr;
|
2008-10-31 19:49:55 +01: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 15:09:08 +01:00
|
|
|
static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out)
|
2008-10-31 19:49:55 +01:00
|
|
|
{
|
|
|
|
CharDriverState *chr;
|
|
|
|
WinCharState *s;
|
|
|
|
|
2014-06-18 08:43:55 +02:00
|
|
|
chr = qemu_chr_alloc();
|
2015-09-14 13:54:03 +02:00
|
|
|
s = g_new0(WinCharState, 1);
|
2008-10-31 19:49:55 +01:00
|
|
|
s->hcom = fd_out;
|
|
|
|
chr->opaque = s;
|
|
|
|
chr->chr_write = win_chr_write;
|
Revert "qemu-char: Print strerror message on failure" and deps
The commit's purpose is laudable:
The only way for chardev drivers to communicate an error was to
return a NULL pointer, which resulted in an error message that
said _that_ something went wrong, but not _why_.
It attempts to achieve it by changing the interface to return 0/-errno
and update qemu_chr_open_opts() to use strerror() to display a more
helpful error message. Unfortunately, it has serious flaws:
1. Backends "socket" and "udp" return bogus error codes, because
qemu_chr_open_socket() and qemu_chr_open_udp() assume that
unix_listen_opts(), unix_connect_opts(), inet_listen_opts(),
inet_connect_opts() and inet_dgram_opts() fail with errno set
appropriately. That assumption is wrong, and the commit turns
unspecific error messages into misleading error messages. For
instance:
$ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx
inet_connect: host and/or port not specified
chardev: opening backend "socket" failed: No such file or directory
ENOENT is what happens to be in my errno when the backend returns
-errno. Let's put ERANGE there just for giggles:
$ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx -drive if=none,iops=99999999999999999999
inet_connect: host and/or port not specified
chardev: opening backend "socket" failed: Numerical result out of range
Worse: when errno happens to be zero, return -errno erroneously
signals success, and qemu_chr_new_from_opts() dies dereferencing
uninitialized chr. I observe this with "-serial unix:".
2. All qemu_chr_open_opts() knows about the error is an errno error
code. That's simply not enough for a decent message. For instance,
when inet_dgram() can't resolve the parameter host, which errno code
should it use? What if it can't resolve parameter localaddr?
Clue: many backends already report errors in their open methods.
Let's revert the flawed commit along with its dependencies, and fix up
the silent error paths instead.
This reverts commit 6e1db57b2ac9025c2443c665a0d9e78748637b26.
Conflicts:
console.c
hw/baum.c
qemu-char.c
This reverts commit aad04cd024f0c59f0b96f032cde2e24eb3abba6d.
The parts of commit db418a0a "Add stdio char device on windows" that
depend on the reverted change fixed up.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2012-02-07 15:09:08 +01:00
|
|
|
return chr;
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
|
|
|
|
2015-09-29 15:42:04 +02:00
|
|
|
static CharDriverState *qemu_chr_open_win_con(const char *id,
|
|
|
|
ChardevBackend *backend,
|
|
|
|
ChardevReturn *ret,
|
|
|
|
Error **errp)
|
2008-10-31 19:49:55 +01: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 15:09:08 +01:00
|
|
|
return qemu_chr_open_win_file(GetStdHandle(STD_OUTPUT_HANDLE));
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
|
|
|
|
2011-10-06 16:37:51 +02: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;
|
|
|
|
|
2013-12-07 14:48:04 +01:00
|
|
|
ret = ReadConsoleInput(stdio->hStdIn, buf, ARRAY_SIZE(buf), &dwSize);
|
2011-10-06 16:37:51 +02:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2015-09-29 15:40:28 +02:00
|
|
|
static CharDriverState *qemu_chr_open_stdio(const char *id,
|
|
|
|
ChardevBackend *backend,
|
|
|
|
ChardevReturn *ret,
|
|
|
|
Error **errp)
|
2011-10-06 16:37:51 +02:00
|
|
|
{
|
|
|
|
CharDriverState *chr;
|
|
|
|
WinStdioCharState *stdio;
|
|
|
|
DWORD dwMode;
|
|
|
|
int is_console = 0;
|
|
|
|
|
2014-06-18 08:43:55 +02:00
|
|
|
chr = qemu_chr_alloc();
|
2015-09-14 13:54:03 +02:00
|
|
|
stdio = g_new0(WinStdioCharState, 1);
|
2011-10-06 16:37:51 +02:00
|
|
|
|
|
|
|
stdio->hStdIn = GetStdHandle(STD_INPUT_HANDLE);
|
|
|
|
if (stdio->hStdIn == INVALID_HANDLE_VALUE) {
|
2015-09-29 15:40:28 +02:00
|
|
|
error_setg(errp, "cannot open stdio: invalid handle");
|
|
|
|
return NULL;
|
2011-10-06 16:37:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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 18:51:17 +01:00
|
|
|
if (is_console) {
|
|
|
|
if (qemu_add_wait_object(stdio->hStdIn,
|
|
|
|
win_stdio_wait_func, chr)) {
|
2015-09-29 15:40:28 +02:00
|
|
|
error_setg(errp, "qemu_add_wait_object: failed");
|
|
|
|
goto err1;
|
2013-03-05 18:51:17 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
DWORD dwId;
|
|
|
|
|
|
|
|
stdio->hInputReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
|
stdio->hInputDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
2015-09-29 15:40:28 +02:00
|
|
|
if (stdio->hInputReadyEvent == INVALID_HANDLE_VALUE
|
2013-03-05 18:51:17 +01:00
|
|
|
|| stdio->hInputDoneEvent == INVALID_HANDLE_VALUE) {
|
2015-09-29 15:40:28 +02:00
|
|
|
error_setg(errp, "cannot create event");
|
|
|
|
goto err2;
|
2013-03-05 18:51:17 +01:00
|
|
|
}
|
|
|
|
if (qemu_add_wait_object(stdio->hInputReadyEvent,
|
|
|
|
win_stdio_thread_wait_func, chr)) {
|
2015-09-29 15:40:28 +02:00
|
|
|
error_setg(errp, "qemu_add_wait_object: failed");
|
|
|
|
goto err2;
|
|
|
|
}
|
|
|
|
stdio->hInputThread = CreateThread(NULL, 0, win_stdio_thread,
|
|
|
|
chr, 0, &dwId);
|
|
|
|
|
|
|
|
if (stdio->hInputThread == INVALID_HANDLE_VALUE) {
|
|
|
|
error_setg(errp, "cannot create stdio thread");
|
|
|
|
goto err3;
|
2011-10-06 16:37:51 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
dwMode |= ENABLE_LINE_INPUT;
|
|
|
|
|
2013-03-05 18:51:17 +01:00
|
|
|
if (is_console) {
|
2011-10-06 16:37:51 +02: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 15:09:08 +01:00
|
|
|
return chr;
|
2015-09-29 15:40:28 +02:00
|
|
|
|
|
|
|
err3:
|
|
|
|
qemu_del_wait_object(stdio->hInputReadyEvent, NULL, NULL);
|
|
|
|
err2:
|
|
|
|
CloseHandle(stdio->hInputReadyEvent);
|
|
|
|
CloseHandle(stdio->hInputDoneEvent);
|
|
|
|
err1:
|
|
|
|
qemu_del_wait_object(stdio->hStdIn, NULL, NULL);
|
|
|
|
return NULL;
|
2011-10-06 16:37:51 +02:00
|
|
|
}
|
2008-10-31 19:49:55 +01:00
|
|
|
#endif /* !_WIN32 */
|
|
|
|
|
2013-03-05 18:51:31 +01:00
|
|
|
|
2008-10-31 19:49:55 +01:00
|
|
|
/***********************************************************/
|
|
|
|
/* UDP Net console */
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
int fd;
|
2013-03-05 18:51:21 +01:00
|
|
|
GIOChannel *chan;
|
2009-11-03 15:29:54 +01:00
|
|
|
uint8_t buf[READ_BUF_LEN];
|
2008-10-31 19:49:55 +01:00
|
|
|
int bufcnt;
|
|
|
|
int bufptr;
|
|
|
|
int max_size;
|
|
|
|
} NetCharDriver;
|
|
|
|
|
2014-06-18 08:43:58 +02:00
|
|
|
/* Called with chr_write_lock held. */
|
2008-10-31 19:49:55 +01:00
|
|
|
static int udp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
|
|
|
|
{
|
|
|
|
NetCharDriver *s = chr->opaque;
|
2013-03-05 18:51:21 +01: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 19:49:55 +01:00
|
|
|
|
2013-03-05 18:51:21 +01:00
|
|
|
return bytes_written;
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int udp_chr_read_poll(void *opaque)
|
|
|
|
{
|
|
|
|
CharDriverState *chr = opaque;
|
|
|
|
NetCharDriver *s = chr->opaque;
|
|
|
|
|
2011-08-15 18:17:31 +02:00
|
|
|
s->max_size = qemu_chr_be_can_write(chr);
|
2008-10-31 19:49:55 +01: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 18:17:30 +02:00
|
|
|
qemu_chr_be_write(chr, &s->buf[s->bufptr], 1);
|
2008-10-31 19:49:55 +01:00
|
|
|
s->bufptr++;
|
2011-08-15 18:17:31 +02:00
|
|
|
s->max_size = qemu_chr_be_can_write(chr);
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
|
|
|
return s->max_size;
|
|
|
|
}
|
|
|
|
|
2013-03-05 18:51:21 +01:00
|
|
|
static gboolean udp_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
|
2008-10-31 19:49:55 +01:00
|
|
|
{
|
|
|
|
CharDriverState *chr = opaque;
|
|
|
|
NetCharDriver *s = chr->opaque;
|
2013-03-05 18:51:21 +01:00
|
|
|
gsize bytes_read = 0;
|
|
|
|
GIOStatus status;
|
2008-10-31 19:49:55 +01:00
|
|
|
|
2013-04-19 17:32:08 +02:00
|
|
|
if (s->max_size == 0) {
|
|
|
|
return TRUE;
|
|
|
|
}
|
2013-03-05 18:51:21 +01: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 19:49:55 +01:00
|
|
|
s->bufptr = s->bufcnt;
|
2013-03-05 18:51:21 +01:00
|
|
|
if (status != G_IO_STATUS_NORMAL) {
|
2013-08-28 11:53:37 +02:00
|
|
|
remove_fd_in_watch(chr);
|
2013-03-05 18:51:21 +01:00
|
|
|
return FALSE;
|
|
|
|
}
|
2008-10-31 19:49:55 +01:00
|
|
|
|
|
|
|
s->bufptr = 0;
|
|
|
|
while (s->max_size > 0 && s->bufptr < s->bufcnt) {
|
2011-08-15 18:17:30 +02:00
|
|
|
qemu_chr_be_write(chr, &s->buf[s->bufptr], 1);
|
2008-10-31 19:49:55 +01:00
|
|
|
s->bufptr++;
|
2011-08-15 18:17:31 +02:00
|
|
|
s->max_size = qemu_chr_be_can_write(chr);
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
2013-03-05 18:51:21 +01:00
|
|
|
|
|
|
|
return TRUE;
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void udp_chr_update_read_handler(CharDriverState *chr)
|
|
|
|
{
|
|
|
|
NetCharDriver *s = chr->opaque;
|
|
|
|
|
2013-08-28 11:53:37 +02:00
|
|
|
remove_fd_in_watch(chr);
|
2013-03-05 18:51:21 +01:00
|
|
|
if (s->chan) {
|
2013-08-28 11:48:29 +02:00
|
|
|
chr->fd_in_tag = io_add_watch_poll(s->chan, udp_chr_read_poll,
|
|
|
|
udp_chr_read, chr);
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-28 18:58:14 +01:00
|
|
|
static void udp_chr_close(CharDriverState *chr)
|
|
|
|
{
|
|
|
|
NetCharDriver *s = chr->opaque;
|
2013-08-28 11:53:37 +02:00
|
|
|
|
|
|
|
remove_fd_in_watch(chr);
|
2013-03-05 18:51:21 +01:00
|
|
|
if (s->chan) {
|
|
|
|
g_io_channel_unref(s->chan);
|
2009-03-28 18:58:14 +01:00
|
|
|
closesocket(s->fd);
|
|
|
|
}
|
2011-08-21 05:09:37 +02:00
|
|
|
g_free(s);
|
2011-11-19 10:22:43 +01:00
|
|
|
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
|
2009-03-28 18:58:14 +01:00
|
|
|
}
|
|
|
|
|
2013-02-27 14:10:47 +01:00
|
|
|
static CharDriverState *qemu_chr_open_udp_fd(int fd)
|
2008-10-31 19:49:55 +01:00
|
|
|
{
|
|
|
|
CharDriverState *chr = NULL;
|
|
|
|
NetCharDriver *s = NULL;
|
|
|
|
|
2014-06-18 08:43:55 +02:00
|
|
|
chr = qemu_chr_alloc();
|
2015-09-14 13:54:03 +02:00
|
|
|
s = g_new0(NetCharDriver, 1);
|
2008-10-31 19:49:55 +01:00
|
|
|
|
|
|
|
s->fd = fd;
|
2013-03-05 18:51:21 +01:00
|
|
|
s->chan = io_channel_from_socket(s->fd);
|
2008-10-31 19:49:55 +01: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 18:58:14 +01:00
|
|
|
chr->chr_close = udp_chr_close;
|
qemu-char: don't issue CHR_EVENT_OPEN in a BH
When CHR_EVENT_OPENED was initially added, it was CHR_EVENT_RESET,
and it was issued as a bottom-half:
86e94dea5b740dad65446c857f6959eae43e0ba6
Which we basically used to print out a greeting/prompt for the
monitor.
AFAICT the only reason this was ever done in a BH was because in
some cases we'd modify the chr_write handler for a new chardev
backend *after* the site where we issued the reset (see:
86e94d:qemu_chr_open_stdio())
At some point this event was renamed to CHR_EVENT_OPENED, and we've
maintained the use of this BH ever since.
However, due to 9f939df955a4152aad69a19a77e0898631bb2c18, we schedule
the BH via g_idle_add(), which is causing events to sometimes be
delivered after we've already begun processing data from backends,
leading to:
known bugs:
QMP:
session negotation resets with OPENED event, in some cases this
is causing new sessions to get sporadically reset
potential bugs:
hw/usb/redirect.c:
can_read handler checks for dev->parser != NULL, which may be
true if CLOSED BH has not been executed yet. In the past, OPENED
quiesced outstanding CLOSED events prior to us reading client
data. If it's delayed, our check may allow reads to occur even
though we haven't processed the OPENED event yet, and when we
do finally get the OPENED event, our state may get reset.
qtest.c:
can begin session before OPENED event is processed, leading to
a spurious reset of the system and irq_levels
gdbstub.c:
may start a gdb session prior to the machine being paused
To fix these, let's just drop the BH.
Since the initial reasoning for using it still applies to an extent,
work around that by deferring the delivery of CHR_EVENT_OPENED until
after the chardevs have been fully initialized, toward the end of
qmp_chardev_add() (or some cases, qemu_chr_new_from_opts()). This
defers delivery long enough that we can be assured a CharDriverState
is fully initialized before CHR_EVENT_OPENED is sent.
Also, rather than requiring each chardev to do an explicit open, do it
automatically, and allow the small few who don't desire such behavior to
suppress the OPENED-on-init behavior by setting a 'explicit_be_open'
flag.
We additionally add missing OPENED events for stdio backends on w32,
which were previously not being issued, causing us to not recieve the
banner and initial prompts for qmp/hmp.
Reported-by: Stefan Priebe <s.priebe@profihost.ag>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
Message-id: 1370636393-21044-1-git-send-email-mdroth@linux.vnet.ibm.com
Cc: qemu-stable@nongnu.org
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-06-07 22:19:53 +02:00
|
|
|
/* be isn't opened until we get a connection */
|
|
|
|
chr->explicit_be_open = true;
|
Revert "qemu-char: Print strerror message on failure" and deps
The commit's purpose is laudable:
The only way for chardev drivers to communicate an error was to
return a NULL pointer, which resulted in an error message that
said _that_ something went wrong, but not _why_.
It attempts to achieve it by changing the interface to return 0/-errno
and update qemu_chr_open_opts() to use strerror() to display a more
helpful error message. Unfortunately, it has serious flaws:
1. Backends "socket" and "udp" return bogus error codes, because
qemu_chr_open_socket() and qemu_chr_open_udp() assume that
unix_listen_opts(), unix_connect_opts(), inet_listen_opts(),
inet_connect_opts() and inet_dgram_opts() fail with errno set
appropriately. That assumption is wrong, and the commit turns
unspecific error messages into misleading error messages. For
instance:
$ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx
inet_connect: host and/or port not specified
chardev: opening backend "socket" failed: No such file or directory
ENOENT is what happens to be in my errno when the backend returns
-errno. Let's put ERANGE there just for giggles:
$ qemu-system-x86_64 -nodefaults -vnc :0 -chardev socket,id=bar,host=xxx -drive if=none,iops=99999999999999999999
inet_connect: host and/or port not specified
chardev: opening backend "socket" failed: Numerical result out of range
Worse: when errno happens to be zero, return -errno erroneously
signals success, and qemu_chr_new_from_opts() dies dereferencing
uninitialized chr. I observe this with "-serial unix:".
2. All qemu_chr_open_opts() knows about the error is an errno error
code. That's simply not enough for a decent message. For instance,
when inet_dgram() can't resolve the parameter host, which errno code
should it use? What if it can't resolve parameter localaddr?
Clue: many backends already report errors in their open methods.
Let's revert the flawed commit along with its dependencies, and fix up
the silent error paths instead.
This reverts commit 6e1db57b2ac9025c2443c665a0d9e78748637b26.
Conflicts:
console.c
hw/baum.c
qemu-char.c
This reverts commit aad04cd024f0c59f0b96f032cde2e24eb3abba6d.
The parts of commit db418a0a "Add stdio char device on windows" that
depend on the reverted change fixed up.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2012-02-07 15:09:08 +01:00
|
|
|
return chr;
|
2013-02-27 14:10:47 +01:00
|
|
|
}
|
2008-10-31 19:49:55 +01:00
|
|
|
|
|
|
|
/***********************************************************/
|
|
|
|
/* TCP Net console */
|
|
|
|
|
|
|
|
typedef struct {
|
2013-03-05 18:51:22 +01:00
|
|
|
|
|
|
|
GIOChannel *chan, *listen_chan;
|
2013-08-28 11:48:29 +02:00
|
|
|
guint listen_tag;
|
2008-10-31 19:49:55 +01:00
|
|
|
int fd, listen_fd;
|
|
|
|
int connected;
|
|
|
|
int max_size;
|
|
|
|
int do_telnetopt;
|
|
|
|
int do_nodelay;
|
|
|
|
int is_unix;
|
2014-05-27 14:04:15 +02:00
|
|
|
int *read_msgfds;
|
|
|
|
int read_msgfds_num;
|
2014-05-27 14:04:02 +02:00
|
|
|
int *write_msgfds;
|
|
|
|
int write_msgfds_num;
|
2014-10-02 18:17:35 +02:00
|
|
|
|
|
|
|
SocketAddress *addr;
|
|
|
|
bool is_listen;
|
|
|
|
bool is_telnet;
|
2014-10-02 18:17:37 +02:00
|
|
|
|
|
|
|
guint reconnect_timer;
|
|
|
|
int64_t reconnect_time;
|
2014-10-08 14:11:55 +02:00
|
|
|
bool connect_err_reported;
|
2008-10-31 19:49:55 +01:00
|
|
|
} TCPCharDriver;
|
|
|
|
|
2014-10-02 18:17:37 +02:00
|
|
|
static gboolean socket_reconnect_timeout(gpointer opaque);
|
|
|
|
|
|
|
|
static void qemu_chr_socket_restart_timer(CharDriverState *chr)
|
|
|
|
{
|
|
|
|
TCPCharDriver *s = chr->opaque;
|
|
|
|
assert(s->connected == 0);
|
|
|
|
s->reconnect_timer = g_timeout_add_seconds(s->reconnect_time,
|
|
|
|
socket_reconnect_timeout, chr);
|
|
|
|
}
|
|
|
|
|
2014-10-08 14:11:55 +02:00
|
|
|
static void check_report_connect_error(CharDriverState *chr,
|
|
|
|
Error *err)
|
|
|
|
{
|
|
|
|
TCPCharDriver *s = chr->opaque;
|
|
|
|
|
|
|
|
if (!s->connect_err_reported) {
|
|
|
|
error_report("Unable to connect character device %s: %s",
|
|
|
|
chr->label, error_get_pretty(err));
|
|
|
|
s->connect_err_reported = true;
|
|
|
|
}
|
|
|
|
qemu_chr_socket_restart_timer(chr);
|
|
|
|
}
|
|
|
|
|
2013-03-05 18:51:22 +01:00
|
|
|
static gboolean tcp_chr_accept(GIOChannel *chan, GIOCondition cond, void *opaque);
|
2008-10-31 19:49:55 +01:00
|
|
|
|
2014-05-27 14:04:02 +02:00
|
|
|
#ifndef _WIN32
|
|
|
|
static int unix_send_msgfds(CharDriverState *chr, const uint8_t *buf, int len)
|
|
|
|
{
|
|
|
|
TCPCharDriver *s = chr->opaque;
|
|
|
|
struct msghdr msgh;
|
|
|
|
struct iovec iov;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
size_t fd_size = s->write_msgfds_num * sizeof(int);
|
|
|
|
char control[CMSG_SPACE(fd_size)];
|
|
|
|
struct cmsghdr *cmsg;
|
|
|
|
|
|
|
|
memset(&msgh, 0, sizeof(msgh));
|
|
|
|
memset(control, 0, sizeof(control));
|
|
|
|
|
|
|
|
/* set the payload */
|
|
|
|
iov.iov_base = (uint8_t *) buf;
|
|
|
|
iov.iov_len = len;
|
|
|
|
|
|
|
|
msgh.msg_iov = &iov;
|
|
|
|
msgh.msg_iovlen = 1;
|
|
|
|
|
|
|
|
msgh.msg_control = control;
|
|
|
|
msgh.msg_controllen = sizeof(control);
|
|
|
|
|
|
|
|
cmsg = CMSG_FIRSTHDR(&msgh);
|
|
|
|
|
|
|
|
cmsg->cmsg_len = CMSG_LEN(fd_size);
|
|
|
|
cmsg->cmsg_level = SOL_SOCKET;
|
|
|
|
cmsg->cmsg_type = SCM_RIGHTS;
|
|
|
|
memcpy(CMSG_DATA(cmsg), s->write_msgfds, fd_size);
|
|
|
|
|
|
|
|
do {
|
|
|
|
r = sendmsg(s->fd, &msgh, 0);
|
|
|
|
} while (r < 0 && errno == EINTR);
|
|
|
|
|
|
|
|
/* free the written msgfds, no matter what */
|
|
|
|
if (s->write_msgfds_num) {
|
|
|
|
g_free(s->write_msgfds);
|
|
|
|
s->write_msgfds = 0;
|
|
|
|
s->write_msgfds_num = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-06-18 08:43:58 +02:00
|
|
|
/* Called with chr_write_lock held. */
|
2008-10-31 19:49:55 +01:00
|
|
|
static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
|
|
|
|
{
|
|
|
|
TCPCharDriver *s = chr->opaque;
|
|
|
|
if (s->connected) {
|
2014-05-27 14:04:02 +02:00
|
|
|
#ifndef _WIN32
|
|
|
|
if (s->is_unix && s->write_msgfds_num) {
|
|
|
|
return unix_send_msgfds(chr, buf, len);
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
return io_channel_send(s->chan, buf, len);
|
|
|
|
}
|
2012-09-05 20:52:49 +02:00
|
|
|
} else {
|
2012-09-12 21:34:07 +02:00
|
|
|
/* XXX: indicate an error ? */
|
2012-09-05 20:52:49 +02:00
|
|
|
return len;
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int tcp_chr_read_poll(void *opaque)
|
|
|
|
{
|
|
|
|
CharDriverState *chr = opaque;
|
|
|
|
TCPCharDriver *s = chr->opaque;
|
|
|
|
if (!s->connected)
|
|
|
|
return 0;
|
2011-08-15 18:17:31 +02:00
|
|
|
s->max_size = qemu_chr_be_can_write(chr);
|
2008-10-31 19:49:55 +01: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 10:22:43 +01:00
|
|
|
qemu_chr_be_event(chr, CHR_EVENT_BREAK);
|
2008-10-31 19:49:55 +01: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;
|
|
|
|
}
|
|
|
|
|
2014-05-27 14:04:15 +02:00
|
|
|
static int tcp_get_msgfds(CharDriverState *chr, int *fds, int num)
|
2009-07-22 10:11:39 +02:00
|
|
|
{
|
|
|
|
TCPCharDriver *s = chr->opaque;
|
2014-05-27 14:04:15 +02:00
|
|
|
int to_copy = (s->read_msgfds_num < num) ? s->read_msgfds_num : num;
|
|
|
|
|
2014-11-02 17:48:32 +01:00
|
|
|
assert(num <= TCP_MAX_FDS);
|
|
|
|
|
2014-05-27 14:04:15 +02:00
|
|
|
if (to_copy) {
|
2014-06-22 04:38:37 +02:00
|
|
|
int i;
|
|
|
|
|
2014-05-27 14:04:15 +02:00
|
|
|
memcpy(fds, s->read_msgfds, to_copy * sizeof(int));
|
|
|
|
|
2014-06-22 04:38:37 +02:00
|
|
|
/* Close unused fds */
|
|
|
|
for (i = to_copy; i < s->read_msgfds_num; i++) {
|
|
|
|
close(s->read_msgfds[i]);
|
|
|
|
}
|
|
|
|
|
2014-05-27 14:04:15 +02:00
|
|
|
g_free(s->read_msgfds);
|
|
|
|
s->read_msgfds = 0;
|
|
|
|
s->read_msgfds_num = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return to_copy;
|
2009-07-22 10:11:39 +02:00
|
|
|
}
|
|
|
|
|
2014-05-27 14:04:02 +02:00
|
|
|
static int tcp_set_msgfds(CharDriverState *chr, int *fds, int num)
|
|
|
|
{
|
|
|
|
TCPCharDriver *s = chr->opaque;
|
|
|
|
|
|
|
|
/* clear old pending fd array */
|
2015-08-26 13:17:18 +02:00
|
|
|
g_free(s->write_msgfds);
|
2014-05-27 14:04:02 +02:00
|
|
|
|
|
|
|
if (num) {
|
2015-09-14 13:54:03 +02:00
|
|
|
s->write_msgfds = g_new(int, num);
|
2014-05-27 14:04:02 +02:00
|
|
|
memcpy(s->write_msgfds, fds, num * sizeof(int));
|
|
|
|
}
|
|
|
|
|
|
|
|
s->write_msgfds_num = num;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-07-27 21:55:25 +02:00
|
|
|
#ifndef _WIN32
|
2009-07-22 10:11:39 +02: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)) {
|
2014-05-27 14:04:15 +02:00
|
|
|
int fd_size, i;
|
2009-07-22 10:11:39 +02:00
|
|
|
|
2014-05-27 14:04:15 +02:00
|
|
|
if (cmsg->cmsg_len < CMSG_LEN(sizeof(int)) ||
|
2009-07-22 10:11:39 +02:00
|
|
|
cmsg->cmsg_level != SOL_SOCKET ||
|
2014-05-27 14:04:15 +02:00
|
|
|
cmsg->cmsg_type != SCM_RIGHTS) {
|
2009-07-22 10:11:39 +02:00
|
|
|
continue;
|
2014-05-27 14:04:15 +02:00
|
|
|
}
|
2009-07-22 10:11:39 +02:00
|
|
|
|
2014-05-27 14:04:15 +02:00
|
|
|
fd_size = cmsg->cmsg_len - CMSG_LEN(0);
|
|
|
|
|
|
|
|
if (!fd_size) {
|
2009-07-22 10:11:39 +02:00
|
|
|
continue;
|
2014-05-27 14:04:15 +02:00
|
|
|
}
|
2009-07-22 10:11:39 +02:00
|
|
|
|
2014-05-27 14:04:15 +02:00
|
|
|
/* close and clean read_msgfds */
|
|
|
|
for (i = 0; i < s->read_msgfds_num; i++) {
|
|
|
|
close(s->read_msgfds[i]);
|
|
|
|
}
|
2013-03-27 10:10:46 +01:00
|
|
|
|
2014-05-27 14:04:15 +02:00
|
|
|
if (s->read_msgfds_num) {
|
|
|
|
g_free(s->read_msgfds);
|
|
|
|
}
|
|
|
|
|
|
|
|
s->read_msgfds_num = fd_size / sizeof(int);
|
|
|
|
s->read_msgfds = g_malloc(fd_size);
|
|
|
|
memcpy(s->read_msgfds, CMSG_DATA(cmsg), fd_size);
|
|
|
|
|
|
|
|
for (i = 0; i < s->read_msgfds_num; i++) {
|
|
|
|
int fd = s->read_msgfds[i];
|
|
|
|
if (fd < 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* O_NONBLOCK is preserved across SCM_RIGHTS so reset it */
|
|
|
|
qemu_set_block(fd);
|
|
|
|
|
|
|
|
#ifndef MSG_CMSG_CLOEXEC
|
|
|
|
qemu_set_cloexec(fd);
|
|
|
|
#endif
|
|
|
|
}
|
2009-07-22 10:11:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-07-22 10:11:38 +02:00
|
|
|
static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len)
|
|
|
|
{
|
|
|
|
TCPCharDriver *s = chr->opaque;
|
2009-08-01 12:13:20 +02:00
|
|
|
struct msghdr msg = { NULL, };
|
2009-07-22 10:11:38 +02:00
|
|
|
struct iovec iov[1];
|
2009-07-22 10:11:39 +02:00
|
|
|
union {
|
|
|
|
struct cmsghdr cmsg;
|
2014-11-02 17:48:32 +01:00
|
|
|
char control[CMSG_SPACE(sizeof(int) * TCP_MAX_FDS)];
|
2009-07-22 10:11:39 +02:00
|
|
|
} msg_control;
|
2012-08-14 22:43:42 +02:00
|
|
|
int flags = 0;
|
2009-07-22 10:11:39 +02:00
|
|
|
ssize_t ret;
|
2009-07-22 10:11:38 +02:00
|
|
|
|
|
|
|
iov[0].iov_base = buf;
|
|
|
|
iov[0].iov_len = len;
|
|
|
|
|
|
|
|
msg.msg_iov = iov;
|
|
|
|
msg.msg_iovlen = 1;
|
2009-07-22 10:11:39 +02:00
|
|
|
msg.msg_control = &msg_control;
|
|
|
|
msg.msg_controllen = sizeof(msg_control);
|
|
|
|
|
2012-08-14 22:43:42 +02:00
|
|
|
#ifdef MSG_CMSG_CLOEXEC
|
|
|
|
flags |= MSG_CMSG_CLOEXEC;
|
|
|
|
#endif
|
2015-07-21 09:25:54 +02:00
|
|
|
do {
|
|
|
|
ret = recvmsg(s->fd, &msg, flags);
|
|
|
|
} while (ret == -1 && errno == EINTR);
|
|
|
|
|
2012-08-14 22:43:42 +02:00
|
|
|
if (ret > 0 && s->is_unix) {
|
2009-07-22 10:11:39 +02:00
|
|
|
unix_process_msgfd(chr, &msg);
|
2012-08-14 22:43:42 +02:00
|
|
|
}
|
2009-07-22 10:11:38 +02:00
|
|
|
|
2009-07-22 10:11:39 +02:00
|
|
|
return ret;
|
2009-07-22 10:11:38 +02:00
|
|
|
}
|
|
|
|
#else
|
|
|
|
static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len)
|
|
|
|
{
|
|
|
|
TCPCharDriver *s = chr->opaque;
|
2015-07-21 09:25:54 +02:00
|
|
|
ssize_t ret;
|
|
|
|
|
|
|
|
do {
|
|
|
|
ret = qemu_recv(s->fd, buf, len, 0);
|
|
|
|
} while (ret == -1 && socket_error() == EINTR);
|
|
|
|
|
|
|
|
return ret;
|
2009-07-22 10:11:38 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2013-03-05 18:51:25 +01:00
|
|
|
static GSource *tcp_chr_add_watch(CharDriverState *chr, GIOCondition cond)
|
|
|
|
{
|
|
|
|
TCPCharDriver *s = chr->opaque;
|
|
|
|
return g_io_create_watch(s->chan, cond);
|
|
|
|
}
|
|
|
|
|
2014-05-27 14:03:48 +02:00
|
|
|
static void tcp_chr_disconnect(CharDriverState *chr)
|
|
|
|
{
|
|
|
|
TCPCharDriver *s = chr->opaque;
|
|
|
|
|
|
|
|
s->connected = 0;
|
|
|
|
if (s->listen_chan) {
|
|
|
|
s->listen_tag = g_io_add_watch(s->listen_chan, G_IO_IN,
|
|
|
|
tcp_chr_accept, chr);
|
|
|
|
}
|
|
|
|
remove_fd_in_watch(chr);
|
|
|
|
g_io_channel_unref(s->chan);
|
|
|
|
s->chan = NULL;
|
|
|
|
closesocket(s->fd);
|
|
|
|
s->fd = -1;
|
2014-10-02 18:17:36 +02:00
|
|
|
SocketAddress_to_str(chr->filename, CHR_MAX_FILENAME_SIZE,
|
|
|
|
"disconnected:", s->addr, s->is_listen, s->is_telnet);
|
2014-05-27 14:03:48 +02:00
|
|
|
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
|
2014-10-02 18:17:37 +02:00
|
|
|
if (s->reconnect_time) {
|
|
|
|
qemu_chr_socket_restart_timer(chr);
|
|
|
|
}
|
2014-05-27 14:03:48 +02:00
|
|
|
}
|
|
|
|
|
2013-03-05 18:51:22 +01:00
|
|
|
static gboolean tcp_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
|
2008-10-31 19:49:55 +01:00
|
|
|
{
|
|
|
|
CharDriverState *chr = opaque;
|
|
|
|
TCPCharDriver *s = chr->opaque;
|
2009-11-03 15:29:54 +01:00
|
|
|
uint8_t buf[READ_BUF_LEN];
|
2008-10-31 19:49:55 +01:00
|
|
|
int len, size;
|
|
|
|
|
2013-03-05 18:51:22 +01:00
|
|
|
if (!s->connected || s->max_size <= 0) {
|
2013-04-19 17:32:08 +02:00
|
|
|
return TRUE;
|
2013-03-05 18:51:22 +01:00
|
|
|
}
|
2008-10-31 19:49:55 +01:00
|
|
|
len = sizeof(buf);
|
|
|
|
if (len > s->max_size)
|
|
|
|
len = s->max_size;
|
2009-07-22 10:11:38 +02:00
|
|
|
size = tcp_chr_recv(chr, (void *)buf, len);
|
2015-07-19 22:39:56 +02:00
|
|
|
if (size == 0 ||
|
|
|
|
(size < 0 &&
|
|
|
|
socket_error() != EAGAIN && socket_error() != EWOULDBLOCK)) {
|
2008-10-31 19:49:55 +01:00
|
|
|
/* connection closed */
|
2014-05-27 14:03:48 +02:00
|
|
|
tcp_chr_disconnect(chr);
|
2008-10-31 19:49:55 +01:00
|
|
|
} else if (size > 0) {
|
|
|
|
if (s->do_telnetopt)
|
|
|
|
tcp_chr_process_IAC_bytes(chr, s, buf, &size);
|
|
|
|
if (size > 0)
|
2011-08-15 18:17:30 +02:00
|
|
|
qemu_chr_be_write(chr, buf, size);
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
2013-03-05 18:51:22 +01:00
|
|
|
|
|
|
|
return TRUE;
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
|
|
|
|
2014-05-27 14:03:48 +02:00
|
|
|
static int tcp_chr_sync_read(CharDriverState *chr, const uint8_t *buf, int len)
|
|
|
|
{
|
|
|
|
TCPCharDriver *s = chr->opaque;
|
|
|
|
int size;
|
|
|
|
|
|
|
|
if (!s->connected) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
size = tcp_chr_recv(chr, (void *) buf, len);
|
|
|
|
if (size == 0) {
|
|
|
|
/* connection closed */
|
|
|
|
tcp_chr_disconnect(chr);
|
|
|
|
}
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2010-08-15 11:46:24 +02:00
|
|
|
#ifndef _WIN32
|
|
|
|
CharDriverState *qemu_chr_open_eventfd(int eventfd)
|
|
|
|
{
|
2014-06-11 17:25:16 +02:00
|
|
|
CharDriverState *chr = qemu_chr_open_fd(eventfd, eventfd);
|
|
|
|
|
|
|
|
if (chr) {
|
|
|
|
chr->avail_connections = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return chr;
|
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 18:54:13 +02:00
|
|
|
}
|
2010-08-15 11:46:24 +02: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 18:54:13 +02:00
|
|
|
|
2008-10-31 19:49:55 +01:00
|
|
|
static void tcp_chr_connect(void *opaque)
|
|
|
|
{
|
|
|
|
CharDriverState *chr = opaque;
|
|
|
|
TCPCharDriver *s = chr->opaque;
|
2014-10-02 18:17:38 +02:00
|
|
|
struct sockaddr_storage ss, ps;
|
|
|
|
socklen_t ss_len = sizeof(ss), ps_len = sizeof(ps);
|
2014-10-02 18:17:36 +02:00
|
|
|
|
|
|
|
memset(&ss, 0, ss_len);
|
|
|
|
if (getsockname(s->fd, (struct sockaddr *) &ss, &ss_len) != 0) {
|
|
|
|
snprintf(chr->filename, CHR_MAX_FILENAME_SIZE,
|
|
|
|
"Error in getsockname: %s\n", strerror(errno));
|
2014-10-02 18:17:38 +02:00
|
|
|
} else if (getpeername(s->fd, (struct sockaddr *) &ps, &ps_len) != 0) {
|
|
|
|
snprintf(chr->filename, CHR_MAX_FILENAME_SIZE,
|
|
|
|
"Error in getpeername: %s\n", strerror(errno));
|
2014-10-02 18:17:36 +02:00
|
|
|
} else {
|
2014-10-02 18:17:38 +02:00
|
|
|
sockaddr_to_str(chr->filename, CHR_MAX_FILENAME_SIZE,
|
|
|
|
&ss, ss_len, &ps, ps_len,
|
2014-10-02 18:17:36 +02:00
|
|
|
s->is_listen, s->is_telnet);
|
|
|
|
}
|
2008-10-31 19:49:55 +01:00
|
|
|
|
|
|
|
s->connected = 1;
|
2013-03-05 18:51:22 +01:00
|
|
|
if (s->chan) {
|
2013-08-28 11:48:29 +02:00
|
|
|
chr->fd_in_tag = io_add_watch_poll(s->chan, tcp_chr_read_poll,
|
|
|
|
tcp_chr_read, chr);
|
2012-09-10 04:30:56 +02:00
|
|
|
}
|
2013-03-26 11:07:54 +01:00
|
|
|
qemu_chr_be_generic_open(chr);
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
|
|
|
|
2014-02-25 11:12:35 +01:00
|
|
|
static void tcp_chr_update_read_handler(CharDriverState *chr)
|
|
|
|
{
|
|
|
|
TCPCharDriver *s = chr->opaque;
|
|
|
|
|
|
|
|
remove_fd_in_watch(chr);
|
|
|
|
if (s->chan) {
|
|
|
|
chr->fd_in_tag = io_add_watch_poll(s->chan, tcp_chr_read_poll,
|
|
|
|
tcp_chr_read, chr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-10-31 19:49:55 +01: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 14:31:42 +02:00
|
|
|
static int tcp_chr_add_client(CharDriverState *chr, int fd)
|
|
|
|
{
|
|
|
|
TCPCharDriver *s = chr->opaque;
|
|
|
|
if (s->fd != -1)
|
|
|
|
return -1;
|
|
|
|
|
2013-03-27 10:10:43 +01: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 14:31:42 +02:00
|
|
|
if (s->do_nodelay)
|
|
|
|
socket_set_nodelay(fd);
|
|
|
|
s->fd = fd;
|
2013-03-05 18:51:22 +01:00
|
|
|
s->chan = io_channel_from_socket(fd);
|
2013-04-19 17:32:06 +02: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 14:31:42 +02:00
|
|
|
tcp_chr_connect(chr);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-03-05 18:51:22 +01:00
|
|
|
static gboolean tcp_chr_accept(GIOChannel *channel, GIOCondition cond, void *opaque)
|
2008-10-31 19:49:55 +01: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 12:24:42 +01:00
|
|
|
fd = qemu_accept(s->listen_fd, addr, &len);
|
2008-10-31 19:49:55 +01:00
|
|
|
if (fd < 0 && errno != EINTR) {
|
2013-04-25 13:53:02 +02:00
|
|
|
s->listen_tag = 0;
|
2013-03-05 18:51:22 +01:00
|
|
|
return FALSE;
|
2008-10-31 19:49:55 +01: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 14:31:42 +02:00
|
|
|
if (tcp_chr_add_client(chr, fd) < 0)
|
|
|
|
close(fd);
|
2013-03-05 18:51:22 +01:00
|
|
|
|
|
|
|
return TRUE;
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void tcp_chr_close(CharDriverState *chr)
|
|
|
|
{
|
|
|
|
TCPCharDriver *s = chr->opaque;
|
2014-05-27 14:04:15 +02:00
|
|
|
int i;
|
2014-10-02 18:17:35 +02:00
|
|
|
|
2014-10-02 18:17:37 +02:00
|
|
|
if (s->reconnect_timer) {
|
|
|
|
g_source_remove(s->reconnect_timer);
|
|
|
|
s->reconnect_timer = 0;
|
|
|
|
}
|
2014-10-02 18:17:35 +02:00
|
|
|
qapi_free_SocketAddress(s->addr);
|
2009-03-28 18:58:14 +01:00
|
|
|
if (s->fd >= 0) {
|
2013-08-28 11:53:37 +02:00
|
|
|
remove_fd_in_watch(chr);
|
2013-03-05 18:51:22 +01:00
|
|
|
if (s->chan) {
|
|
|
|
g_io_channel_unref(s->chan);
|
|
|
|
}
|
2008-10-31 19:49:55 +01:00
|
|
|
closesocket(s->fd);
|
2009-03-28 18:58:14 +01:00
|
|
|
}
|
|
|
|
if (s->listen_fd >= 0) {
|
2013-03-05 18:51:22 +01:00
|
|
|
if (s->listen_tag) {
|
|
|
|
g_source_remove(s->listen_tag);
|
2013-04-19 17:32:06 +02:00
|
|
|
s->listen_tag = 0;
|
2013-03-05 18:51:22 +01:00
|
|
|
}
|
|
|
|
if (s->listen_chan) {
|
|
|
|
g_io_channel_unref(s->listen_chan);
|
|
|
|
}
|
2008-10-31 19:49:55 +01:00
|
|
|
closesocket(s->listen_fd);
|
2009-03-28 18:58:14 +01:00
|
|
|
}
|
2014-05-27 14:04:15 +02:00
|
|
|
if (s->read_msgfds_num) {
|
|
|
|
for (i = 0; i < s->read_msgfds_num; i++) {
|
|
|
|
close(s->read_msgfds[i]);
|
|
|
|
}
|
|
|
|
g_free(s->read_msgfds);
|
|
|
|
}
|
2014-05-27 14:04:02 +02:00
|
|
|
if (s->write_msgfds_num) {
|
|
|
|
g_free(s->write_msgfds);
|
|
|
|
}
|
2011-08-21 05:09:37 +02:00
|
|
|
g_free(s);
|
2011-11-19 10:22:43 +01:00
|
|
|
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
|
|
|
|
2014-10-02 18:17:36 +02:00
|
|
|
static void qemu_chr_finish_socket_connection(CharDriverState *chr, int fd)
|
2008-10-31 19:49:55 +01:00
|
|
|
{
|
2014-10-02 18:17:34 +02:00
|
|
|
TCPCharDriver *s = chr->opaque;
|
2012-12-20 13:53:12 +01:00
|
|
|
|
2014-10-02 18:17:35 +02:00
|
|
|
if (s->is_listen) {
|
2012-12-20 13:53:12 +01:00
|
|
|
s->listen_fd = fd;
|
2013-03-05 18:51:22 +01:00
|
|
|
s->listen_chan = io_channel_from_socket(s->listen_fd);
|
2014-10-02 18:17:34 +02:00
|
|
|
s->listen_tag = g_io_add_watch(s->listen_chan, G_IO_IN,
|
|
|
|
tcp_chr_accept, chr);
|
2012-12-20 13:53:12 +01:00
|
|
|
} else {
|
|
|
|
s->connected = 1;
|
|
|
|
s->fd = fd;
|
|
|
|
socket_set_nodelay(fd);
|
2013-03-05 18:51:22 +01:00
|
|
|
s->chan = io_channel_from_socket(s->fd);
|
2012-12-20 13:53:12 +01:00
|
|
|
tcp_chr_connect(chr);
|
|
|
|
}
|
2014-10-02 18:17:34 +02:00
|
|
|
}
|
|
|
|
|
2014-10-08 14:11:56 +02:00
|
|
|
static void qemu_chr_socket_connected(int fd, Error *err, void *opaque)
|
2014-10-02 18:17:37 +02:00
|
|
|
{
|
|
|
|
CharDriverState *chr = opaque;
|
2014-10-08 14:11:55 +02:00
|
|
|
TCPCharDriver *s = chr->opaque;
|
2014-10-02 18:17:37 +02:00
|
|
|
|
|
|
|
if (fd < 0) {
|
2014-10-08 14:11:55 +02:00
|
|
|
check_report_connect_error(chr, err);
|
2014-10-02 18:17:37 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-10-08 14:11:55 +02:00
|
|
|
s->connect_err_reported = false;
|
2014-10-02 18:17:37 +02:00
|
|
|
qemu_chr_finish_socket_connection(chr, fd);
|
|
|
|
}
|
|
|
|
|
2014-10-02 18:17:35 +02:00
|
|
|
static bool qemu_chr_open_socket_fd(CharDriverState *chr, Error **errp)
|
2014-10-02 18:17:34 +02:00
|
|
|
{
|
2014-10-02 18:17:35 +02:00
|
|
|
TCPCharDriver *s = chr->opaque;
|
2014-10-02 18:17:34 +02:00
|
|
|
int fd;
|
|
|
|
|
2014-10-02 18:17:35 +02:00
|
|
|
if (s->is_listen) {
|
|
|
|
fd = socket_listen(s->addr, errp);
|
2014-10-02 18:17:37 +02:00
|
|
|
} else if (s->reconnect_time) {
|
|
|
|
fd = socket_connect(s->addr, errp, qemu_chr_socket_connected, chr);
|
|
|
|
return fd >= 0;
|
|
|
|
} else {
|
2014-10-02 18:17:35 +02:00
|
|
|
fd = socket_connect(s->addr, errp, NULL, NULL);
|
2012-12-20 13:53:12 +01:00
|
|
|
}
|
2014-10-02 18:17:34 +02:00
|
|
|
if (fd < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-10-02 18:17:36 +02:00
|
|
|
qemu_chr_finish_socket_connection(chr, fd);
|
|
|
|
return true;
|
2012-12-20 13:53:12 +01:00
|
|
|
}
|
|
|
|
|
2013-01-24 17:03:19 +01: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 21:27:24 +01:00
|
|
|
/* Ring buffer chardev */
|
2013-01-24 17:03:19 +01: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 21:27:24 +01:00
|
|
|
} RingBufCharDriver;
|
2013-01-24 17:03:19 +01: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 21:27:24 +01:00
|
|
|
static size_t ringbuf_count(const CharDriverState *chr)
|
2013-01-24 17:03:19 +01: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 21:27:24 +01:00
|
|
|
const RingBufCharDriver *d = chr->opaque;
|
2013-01-24 17:03:19 +01:00
|
|
|
|
2013-02-06 21:27:23 +01:00
|
|
|
return d->prod - d->cons;
|
2013-01-24 17:03:19 +01:00
|
|
|
}
|
|
|
|
|
2014-06-18 08:43:58 +02:00
|
|
|
/* Called with chr_write_lock held. */
|
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 21:27:24 +01:00
|
|
|
static int ringbuf_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
|
2013-01-24 17:03:19 +01: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 21:27:24 +01:00
|
|
|
RingBufCharDriver *d = chr->opaque;
|
2013-01-24 17:03:19 +01:00
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!buf || (len < 0)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < len; i++ ) {
|
2013-02-06 21:27:23 +01:00
|
|
|
d->cbuf[d->prod++ & (d->size - 1)] = buf[i];
|
|
|
|
if (d->prod - d->cons > d->size) {
|
2013-01-24 17:03:19 +01: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 21:27:24 +01:00
|
|
|
static int ringbuf_chr_read(CharDriverState *chr, uint8_t *buf, int len)
|
2013-01-24 17:03:19 +01: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 21:27:24 +01:00
|
|
|
RingBufCharDriver *d = chr->opaque;
|
2013-01-24 17:03:19 +01:00
|
|
|
int i;
|
|
|
|
|
2014-06-18 08:43:58 +02:00
|
|
|
qemu_mutex_lock(&chr->chr_write_lock);
|
2013-02-06 21:27:23 +01:00
|
|
|
for (i = 0; i < len && d->cons != d->prod; i++) {
|
|
|
|
buf[i] = d->cbuf[d->cons++ & (d->size - 1)];
|
2013-01-24 17:03:19 +01:00
|
|
|
}
|
2014-06-18 08:43:58 +02:00
|
|
|
qemu_mutex_unlock(&chr->chr_write_lock);
|
2013-01-24 17:03:19 +01: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 21:27:24 +01:00
|
|
|
static void ringbuf_chr_close(struct CharDriverState *chr)
|
2013-01-24 17:03:19 +01: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 21:27:24 +01:00
|
|
|
RingBufCharDriver *d = chr->opaque;
|
2013-01-24 17:03:19 +01:00
|
|
|
|
|
|
|
g_free(d->cbuf);
|
|
|
|
g_free(d);
|
|
|
|
chr->opaque = NULL;
|
|
|
|
}
|
|
|
|
|
2015-09-29 15:51:14 +02:00
|
|
|
static CharDriverState *qemu_chr_open_ringbuf(const char *id,
|
|
|
|
ChardevBackend *backend,
|
|
|
|
ChardevReturn *ret,
|
Revert "chardev: Make the name of memory device consistent"
This reverts commit 6a85e60cb994bd95d1537aafbff65816f3de4637.
Commit 51767e7 "qemu-char: Add new char backend CirMemCharDriver"
introduced a memory ring buffer character device driver named
"memory". Commit 3949e59 "qemu-char: Saner naming of memchar stuff &
doc fixes" changed the driver name to "ringbuf", along with a whole
bunch of other names, with the following rationale:
Naming is a mess. The code calls the device driver
CirMemCharDriver, the public API calls it "memory", "memchardev",
or "memchar", and the special commands are named like
"memchar-FOO". "memory" is a particularly unfortunate choice,
because there's another character device driver called
MemoryDriver. Moreover, the device's distinctive property is that
it's a ring buffer, not that's in memory.
This is what we released in 1.4.0.
Unfortunately, the rename missed a critical instance of "memory": the
actual driver name. Thus, the new device could be used only by an
entirely undocumented name. The documented name did not work.
Bummer.
Commit 6a85e60 fixes this by changing the documentation to match the
code. It also changes some, but not all related occurences of
"ringbuf" to "memory". Left alone are identifiers in C code, HMP and
QMP commands. The latter are external interface, so they can't be
changed.
The result is an inconsistent mess. Moreover, "memory" is a rotten
name. The device's distinctive property is that it's a ring buffer,
not that's in memory. User's don't care whether it's in RAM, flash,
or carved into chocolate tablets by Oompa Loompas.
Revert the commit. Next commit will fix just the bug.
Cc: qemu-stable@nongnu.org
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-id: 1374849874-25531-2-git-send-email-armbru@redhat.com
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-07-26 16:44:32 +02:00
|
|
|
Error **errp)
|
2013-01-24 17:03:19 +01:00
|
|
|
{
|
2015-10-26 23:34:57 +01:00
|
|
|
ChardevRingbuf *opts = backend->u.ringbuf;
|
2013-01-24 17:03:19 +01: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 21:27:24 +01:00
|
|
|
RingBufCharDriver *d;
|
2013-01-24 17:03:19 +01:00
|
|
|
|
2014-06-18 08:43:55 +02:00
|
|
|
chr = qemu_chr_alloc();
|
2013-01-24 17:03:19 +01:00
|
|
|
d = g_malloc(sizeof(*d));
|
|
|
|
|
2013-02-26 16:21:11 +01:00
|
|
|
d->size = opts->has_size ? opts->size : 65536;
|
2013-01-24 17:03:19 +01:00
|
|
|
|
|
|
|
/* The size must be power of 2 */
|
|
|
|
if (d->size & (d->size - 1)) {
|
Revert "chardev: Make the name of memory device consistent"
This reverts commit 6a85e60cb994bd95d1537aafbff65816f3de4637.
Commit 51767e7 "qemu-char: Add new char backend CirMemCharDriver"
introduced a memory ring buffer character device driver named
"memory". Commit 3949e59 "qemu-char: Saner naming of memchar stuff &
doc fixes" changed the driver name to "ringbuf", along with a whole
bunch of other names, with the following rationale:
Naming is a mess. The code calls the device driver
CirMemCharDriver, the public API calls it "memory", "memchardev",
or "memchar", and the special commands are named like
"memchar-FOO". "memory" is a particularly unfortunate choice,
because there's another character device driver called
MemoryDriver. Moreover, the device's distinctive property is that
it's a ring buffer, not that's in memory.
This is what we released in 1.4.0.
Unfortunately, the rename missed a critical instance of "memory": the
actual driver name. Thus, the new device could be used only by an
entirely undocumented name. The documented name did not work.
Bummer.
Commit 6a85e60 fixes this by changing the documentation to match the
code. It also changes some, but not all related occurences of
"ringbuf" to "memory". Left alone are identifiers in C code, HMP and
QMP commands. The latter are external interface, so they can't be
changed.
The result is an inconsistent mess. Moreover, "memory" is a rotten
name. The device's distinctive property is that it's a ring buffer,
not that's in memory. User's don't care whether it's in RAM, flash,
or carved into chocolate tablets by Oompa Loompas.
Revert the commit. Next commit will fix just the bug.
Cc: qemu-stable@nongnu.org
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-id: 1374849874-25531-2-git-send-email-armbru@redhat.com
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-07-26 16:44:32 +02:00
|
|
|
error_setg(errp, "size of ringbuf chardev must be power of two");
|
2013-01-24 17:03:19 +01: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 21:27:24 +01:00
|
|
|
chr->chr_write = ringbuf_chr_write;
|
|
|
|
chr->chr_close = ringbuf_chr_close;
|
2013-01-24 17:03:19 +01:00
|
|
|
|
|
|
|
return chr;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
g_free(d);
|
|
|
|
g_free(chr);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-05-28 00:39:30 +02:00
|
|
|
bool chr_is_ringbuf(const CharDriverState *chr)
|
2013-01-24 17:03:20 +01: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 21:27:24 +01:00
|
|
|
return chr->chr_write == ringbuf_chr_write;
|
2013-01-24 17:03:20 +01: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 21:27:24 +01:00
|
|
|
void qmp_ringbuf_write(const char *device, const char *data,
|
2013-02-06 21:27:14 +01:00
|
|
|
bool has_format, enum DataFormat format,
|
2013-01-24 17:03:20 +01:00
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
CharDriverState *chr;
|
2013-02-06 21:27:17 +01:00
|
|
|
const uint8_t *write_data;
|
2013-01-24 17:03:20 +01:00
|
|
|
int ret;
|
2013-05-22 05:01:43 +02:00
|
|
|
gsize write_count;
|
2013-01-24 17:03:20 +01:00
|
|
|
|
|
|
|
chr = qemu_chr_find(device);
|
|
|
|
if (!chr) {
|
2013-02-06 21:27:16 +01:00
|
|
|
error_setg(errp, "Device '%s' not found", device);
|
2013-01-24 17:03:20 +01: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 21:27:24 +01:00
|
|
|
if (!chr_is_ringbuf(chr)) {
|
|
|
|
error_setg(errp,"%s is not a ringbuf device", device);
|
2013-01-24 17:03:20 +01: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 21:27:14 +01:00
|
|
|
write_count = strlen(data);
|
2013-01-24 17:03:20 +01: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 21:27:24 +01:00
|
|
|
ret = ringbuf_chr_write(chr, write_data, write_count);
|
2013-01-24 17:03:20 +01:00
|
|
|
|
2013-02-06 21:27:18 +01:00
|
|
|
if (write_data != (uint8_t *)data) {
|
|
|
|
g_free((void *)write_data);
|
|
|
|
}
|
|
|
|
|
2013-01-24 17:03:20 +01: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 21:27:24 +01:00
|
|
|
char *qmp_ringbuf_read(const char *device, int64_t size,
|
2013-02-06 21:27:15 +01:00
|
|
|
bool has_format, enum DataFormat format,
|
|
|
|
Error **errp)
|
2013-01-24 17:03:21 +01:00
|
|
|
{
|
|
|
|
CharDriverState *chr;
|
2013-02-06 21:27:17 +01:00
|
|
|
uint8_t *read_data;
|
2013-01-24 17:03:21 +01:00
|
|
|
size_t count;
|
2013-02-06 21:27:15 +01:00
|
|
|
char *data;
|
2013-01-24 17:03:21 +01:00
|
|
|
|
|
|
|
chr = qemu_chr_find(device);
|
|
|
|
if (!chr) {
|
2013-02-06 21:27:16 +01:00
|
|
|
error_setg(errp, "Device '%s' not found", device);
|
2013-01-24 17:03:21 +01: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 21:27:24 +01:00
|
|
|
if (!chr_is_ringbuf(chr)) {
|
|
|
|
error_setg(errp,"%s is not a ringbuf device", device);
|
2013-01-24 17:03:21 +01: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 21:27:24 +01:00
|
|
|
count = ringbuf_count(chr);
|
2013-01-24 17:03:21 +01:00
|
|
|
size = size > count ? count : size;
|
2013-02-06 21:27:20 +01:00
|
|
|
read_data = g_malloc(size + 1);
|
2013-01-24 17:03:21 +01: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 21:27:24 +01:00
|
|
|
ringbuf_chr_read(chr, read_data, size);
|
2013-01-24 17:03:21 +01:00
|
|
|
|
|
|
|
if (has_format && (format == DATA_FORMAT_BASE64)) {
|
2013-02-06 21:27:15 +01:00
|
|
|
data = g_base64_encode(read_data, size);
|
2013-02-06 21:27:18 +01:00
|
|
|
g_free(read_data);
|
2013-01-24 17:03:21 +01: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 21:27:24 +01: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 21:27:20 +01:00
|
|
|
read_data[size] = 0;
|
2013-02-06 21:27:15 +01:00
|
|
|
data = (char *)read_data;
|
2013-01-24 17:03:21 +01:00
|
|
|
}
|
|
|
|
|
2013-02-06 21:27:15 +01:00
|
|
|
return data;
|
2013-01-24 17:03:21 +01:00
|
|
|
}
|
|
|
|
|
2009-12-08 13:11:49 +01:00
|
|
|
QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
|
2009-09-10 10:58:35 +02:00
|
|
|
{
|
2009-09-10 10:58:49 +02:00
|
|
|
char host[65], port[33], width[8], height[8];
|
2009-09-10 10:58:42 +02:00
|
|
|
int pos;
|
2009-09-10 10:58:36 +02:00
|
|
|
const char *p;
|
2009-09-10 10:58:35 +02:00
|
|
|
QemuOpts *opts;
|
2012-03-20 19:51:57 +01:00
|
|
|
Error *local_err = NULL;
|
2009-09-10 10:58:35 +02:00
|
|
|
|
2012-03-20 19:51:57 +01:00
|
|
|
opts = qemu_opts_create(qemu_find_opts("chardev"), label, 1, &local_err);
|
2014-01-30 15:07:28 +01:00
|
|
|
if (local_err) {
|
2015-02-10 15:21:26 +01:00
|
|
|
error_report_err(local_err);
|
2009-09-10 10:58:35 +02:00
|
|
|
return NULL;
|
2012-03-20 19:51:57 +01:00
|
|
|
}
|
2009-09-10 10:58:35 +02:00
|
|
|
|
2009-09-10 10:58:50 +02:00
|
|
|
if (strstart(filename, "mon:", &p)) {
|
|
|
|
filename = p;
|
2015-02-12 17:52:20 +01:00
|
|
|
qemu_opt_set(opts, "mux", "on", &error_abort);
|
2013-07-03 18:29:45 +02:00
|
|
|
if (strcmp(filename, "stdio") == 0) {
|
|
|
|
/* Monitor is muxed to stdio: do not exit on Ctrl+C by default
|
|
|
|
* but pass it to the guest. Handle this only for compat syntax,
|
|
|
|
* for -chardev syntax we have special option for this.
|
|
|
|
* This is what -nographic did, redirecting+muxing serial+monitor
|
|
|
|
* to stdio causing Ctrl+C to be passed to guest. */
|
2015-02-12 17:52:20 +01:00
|
|
|
qemu_opt_set(opts, "signal", "off", &error_abort);
|
2013-07-03 18:29:45 +02:00
|
|
|
}
|
2009-09-10 10:58:50 +02:00
|
|
|
}
|
|
|
|
|
2009-09-10 10:58:45 +02:00
|
|
|
if (strcmp(filename, "null") == 0 ||
|
|
|
|
strcmp(filename, "pty") == 0 ||
|
|
|
|
strcmp(filename, "msmouse") == 0 ||
|
2009-09-10 10:58:46 +02:00
|
|
|
strcmp(filename, "braille") == 0 ||
|
backends: Introduce chr-testdev
From: Paolo Bonzini <pbonzini@redhat.com>
chr-testdev enables a virtio serial channel to be used for guest
initiated qemu exits. hw/misc/debugexit already enables guest
initiated qemu exits, but only for PC targets. chr-testdev supports
any virtio-capable target. kvm-unit-tests/arm is already making use
of this backend.
Currently there is a single command implemented, "q". It takes a
(prefix) argument for the exit code, thus an exit is implemented by
writing, e.g. "1q", to the virtio-serial port.
It can be used as:
$QEMU ... \
-device virtio-serial-device \
-device virtserialport,chardev=ctd -chardev testdev,id=ctd
or, use:
$QEMU ... \
-device virtio-serial-device \
-device virtconsole,chardev=ctd -chardev testdev,id=ctd
to bind it to virtio-serial port0.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Andrew Jones <drjones@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2014-07-11 09:44:26 +02:00
|
|
|
strcmp(filename, "testdev") == 0 ||
|
2009-09-10 10:58:45 +02:00
|
|
|
strcmp(filename, "stdio") == 0) {
|
2015-02-12 17:52:20 +01:00
|
|
|
qemu_opt_set(opts, "backend", filename, &error_abort);
|
2009-09-10 10:58:35 +02:00
|
|
|
return opts;
|
|
|
|
}
|
2009-09-10 10:58:49 +02:00
|
|
|
if (strstart(filename, "vc", &p)) {
|
2015-02-12 17:52:20 +01:00
|
|
|
qemu_opt_set(opts, "backend", "vc", &error_abort);
|
2009-09-10 10:58:49 +02:00
|
|
|
if (*p == ':') {
|
2013-09-30 23:04:49 +02:00
|
|
|
if (sscanf(p+1, "%7[0-9]x%7[0-9]", width, height) == 2) {
|
2009-09-10 10:58:49 +02:00
|
|
|
/* pixels */
|
2015-02-12 17:52:20 +01:00
|
|
|
qemu_opt_set(opts, "width", width, &error_abort);
|
|
|
|
qemu_opt_set(opts, "height", height, &error_abort);
|
2013-09-30 23:04:49 +02:00
|
|
|
} else if (sscanf(p+1, "%7[0-9]Cx%7[0-9]C", width, height) == 2) {
|
2009-09-10 10:58:49 +02:00
|
|
|
/* chars */
|
2015-02-12 17:52:20 +01:00
|
|
|
qemu_opt_set(opts, "cols", width, &error_abort);
|
|
|
|
qemu_opt_set(opts, "rows", height, &error_abort);
|
2009-09-10 10:58:49 +02:00
|
|
|
} else {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return opts;
|
|
|
|
}
|
2009-09-10 10:58:47 +02:00
|
|
|
if (strcmp(filename, "con:") == 0) {
|
2015-02-12 17:52:20 +01:00
|
|
|
qemu_opt_set(opts, "backend", "console", &error_abort);
|
2009-09-10 10:58:47 +02:00
|
|
|
return opts;
|
|
|
|
}
|
2009-09-10 10:58:48 +02:00
|
|
|
if (strstart(filename, "COM", NULL)) {
|
2015-02-12 17:52:20 +01:00
|
|
|
qemu_opt_set(opts, "backend", "serial", &error_abort);
|
|
|
|
qemu_opt_set(opts, "path", filename, &error_abort);
|
2009-09-10 10:58:48 +02:00
|
|
|
return opts;
|
|
|
|
}
|
2009-09-10 10:58:36 +02:00
|
|
|
if (strstart(filename, "file:", &p)) {
|
2015-02-12 17:52:20 +01:00
|
|
|
qemu_opt_set(opts, "backend", "file", &error_abort);
|
|
|
|
qemu_opt_set(opts, "path", p, &error_abort);
|
2009-09-10 10:58:36 +02:00
|
|
|
return opts;
|
|
|
|
}
|
|
|
|
if (strstart(filename, "pipe:", &p)) {
|
2015-02-12 17:52:20 +01:00
|
|
|
qemu_opt_set(opts, "backend", "pipe", &error_abort);
|
|
|
|
qemu_opt_set(opts, "path", p, &error_abort);
|
2009-09-10 10:58:36 +02:00
|
|
|
return opts;
|
|
|
|
}
|
2009-09-10 10:58:42 +02: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;
|
|
|
|
}
|
2015-02-12 17:52:20 +01:00
|
|
|
qemu_opt_set(opts, "backend", "socket", &error_abort);
|
|
|
|
qemu_opt_set(opts, "host", host, &error_abort);
|
|
|
|
qemu_opt_set(opts, "port", port, &error_abort);
|
2009-09-10 10:58:42 +02:00
|
|
|
if (p[pos] == ',') {
|
2015-02-12 18:37:11 +01:00
|
|
|
qemu_opts_do_parse(opts, p+pos+1, NULL, &local_err);
|
|
|
|
if (local_err) {
|
|
|
|
error_report_err(local_err);
|
2009-09-10 10:58:42 +02:00
|
|
|
goto fail;
|
2015-02-12 18:37:11 +01:00
|
|
|
}
|
2009-09-10 10:58:42 +02:00
|
|
|
}
|
|
|
|
if (strstart(filename, "telnet:", &p))
|
2015-02-12 17:52:20 +01:00
|
|
|
qemu_opt_set(opts, "telnet", "on", &error_abort);
|
2009-09-10 10:58:42 +02:00
|
|
|
return opts;
|
|
|
|
}
|
2009-09-10 10:58:51 +02:00
|
|
|
if (strstart(filename, "udp:", &p)) {
|
2015-02-12 17:52:20 +01:00
|
|
|
qemu_opt_set(opts, "backend", "udp", &error_abort);
|
2009-09-10 10:58:51 +02:00
|
|
|
if (sscanf(p, "%64[^:]:%32[^@,]%n", host, port, &pos) < 2) {
|
|
|
|
host[0] = 0;
|
2010-03-07 11:28:48 +01:00
|
|
|
if (sscanf(p, ":%32[^@,]%n", port, &pos) < 1) {
|
2009-09-10 10:58:51 +02:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
2015-02-12 17:52:20 +01:00
|
|
|
qemu_opt_set(opts, "host", host, &error_abort);
|
|
|
|
qemu_opt_set(opts, "port", port, &error_abort);
|
2009-09-10 10:58:51 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2015-02-12 17:52:20 +01:00
|
|
|
qemu_opt_set(opts, "localaddr", host, &error_abort);
|
|
|
|
qemu_opt_set(opts, "localport", port, &error_abort);
|
2009-09-10 10:58:51 +02:00
|
|
|
}
|
|
|
|
return opts;
|
|
|
|
}
|
2009-09-10 10:58:42 +02:00
|
|
|
if (strstart(filename, "unix:", &p)) {
|
2015-02-12 17:52:20 +01:00
|
|
|
qemu_opt_set(opts, "backend", "socket", &error_abort);
|
2015-02-12 18:37:11 +01:00
|
|
|
qemu_opts_do_parse(opts, p, "path", &local_err);
|
|
|
|
if (local_err) {
|
|
|
|
error_report_err(local_err);
|
2009-09-10 10:58:42 +02:00
|
|
|
goto fail;
|
2015-02-12 18:37:11 +01:00
|
|
|
}
|
2009-09-10 10:58:42 +02:00
|
|
|
return opts;
|
|
|
|
}
|
2009-09-10 10:58:48 +02:00
|
|
|
if (strstart(filename, "/dev/parport", NULL) ||
|
|
|
|
strstart(filename, "/dev/ppi", NULL)) {
|
2015-02-12 17:52:20 +01:00
|
|
|
qemu_opt_set(opts, "backend", "parport", &error_abort);
|
|
|
|
qemu_opt_set(opts, "path", filename, &error_abort);
|
2009-09-10 10:58:48 +02:00
|
|
|
return opts;
|
|
|
|
}
|
|
|
|
if (strstart(filename, "/dev/", NULL)) {
|
2015-02-12 17:52:20 +01:00
|
|
|
qemu_opt_set(opts, "backend", "tty", &error_abort);
|
|
|
|
qemu_opt_set(opts, "path", filename, &error_abort);
|
2009-09-10 10:58:48 +02:00
|
|
|
return opts;
|
|
|
|
}
|
2009-09-10 10:58:35 +02:00
|
|
|
|
2009-09-10 10:58:42 +02:00
|
|
|
fail:
|
2009-09-10 10:58:35 +02:00
|
|
|
qemu_opts_del(opts);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-02-21 12:07:14 +01: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;
|
|
|
|
}
|
2015-10-26 23:34:57 +01:00
|
|
|
backend->u.file = g_new0(ChardevFile, 1);
|
|
|
|
backend->u.file->out = g_strdup(path);
|
2013-02-21 12:07:14 +01:00
|
|
|
}
|
|
|
|
|
2013-02-21 12:34:58 +01:00
|
|
|
static void qemu_chr_parse_stdio(QemuOpts *opts, ChardevBackend *backend,
|
|
|
|
Error **errp)
|
|
|
|
{
|
2015-10-26 23:34:57 +01:00
|
|
|
backend->u.stdio = g_new0(ChardevStdio, 1);
|
|
|
|
backend->u.stdio->has_signal = true;
|
|
|
|
backend->u.stdio->signal = qemu_opt_get_bool(opts, "signal", true);
|
2013-02-21 12:34:58 +01:00
|
|
|
}
|
|
|
|
|
2015-09-29 15:08:05 +02:00
|
|
|
#ifdef HAVE_CHARDEV_SERIAL
|
2013-02-22 15:48:05 +01: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;
|
|
|
|
}
|
2015-10-26 23:34:57 +01:00
|
|
|
backend->u.serial = g_new0(ChardevHostdev, 1);
|
|
|
|
backend->u.serial->device = g_strdup(device);
|
2013-02-22 15:48:05 +01:00
|
|
|
}
|
2015-09-29 15:08:05 +02:00
|
|
|
#endif
|
2013-02-22 15:48:05 +01:00
|
|
|
|
2015-10-12 09:49:28 +02:00
|
|
|
#ifdef HAVE_CHARDEV_PARPORT
|
2013-02-22 16:17:01 +01: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;
|
|
|
|
}
|
2015-10-26 23:34:57 +01:00
|
|
|
backend->u.parallel = g_new0(ChardevHostdev, 1);
|
|
|
|
backend->u.parallel->device = g_strdup(device);
|
2013-02-22 16:17:01 +01:00
|
|
|
}
|
2015-10-12 09:49:28 +02:00
|
|
|
#endif
|
2013-02-22 16:17:01 +01:00
|
|
|
|
2013-02-25 11:50:55 +01: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;
|
|
|
|
}
|
2015-10-26 23:34:57 +01:00
|
|
|
backend->u.pipe = g_new0(ChardevHostdev, 1);
|
|
|
|
backend->u.pipe->device = g_strdup(device);
|
2013-02-25 11:50:55 +01:00
|
|
|
}
|
|
|
|
|
Revert "chardev: Make the name of memory device consistent"
This reverts commit 6a85e60cb994bd95d1537aafbff65816f3de4637.
Commit 51767e7 "qemu-char: Add new char backend CirMemCharDriver"
introduced a memory ring buffer character device driver named
"memory". Commit 3949e59 "qemu-char: Saner naming of memchar stuff &
doc fixes" changed the driver name to "ringbuf", along with a whole
bunch of other names, with the following rationale:
Naming is a mess. The code calls the device driver
CirMemCharDriver, the public API calls it "memory", "memchardev",
or "memchar", and the special commands are named like
"memchar-FOO". "memory" is a particularly unfortunate choice,
because there's another character device driver called
MemoryDriver. Moreover, the device's distinctive property is that
it's a ring buffer, not that's in memory.
This is what we released in 1.4.0.
Unfortunately, the rename missed a critical instance of "memory": the
actual driver name. Thus, the new device could be used only by an
entirely undocumented name. The documented name did not work.
Bummer.
Commit 6a85e60 fixes this by changing the documentation to match the
code. It also changes some, but not all related occurences of
"ringbuf" to "memory". Left alone are identifiers in C code, HMP and
QMP commands. The latter are external interface, so they can't be
changed.
The result is an inconsistent mess. Moreover, "memory" is a rotten
name. The device's distinctive property is that it's a ring buffer,
not that's in memory. User's don't care whether it's in RAM, flash,
or carved into chocolate tablets by Oompa Loompas.
Revert the commit. Next commit will fix just the bug.
Cc: qemu-stable@nongnu.org
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-id: 1374849874-25531-2-git-send-email-armbru@redhat.com
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-07-26 16:44:32 +02:00
|
|
|
static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend,
|
|
|
|
Error **errp)
|
2013-02-26 16:21:11 +01:00
|
|
|
{
|
|
|
|
int val;
|
|
|
|
|
2015-10-26 23:34:57 +01:00
|
|
|
backend->u.ringbuf = g_new0(ChardevRingbuf, 1);
|
2013-02-26 16:21:11 +01:00
|
|
|
|
2013-06-27 16:22:07 +02:00
|
|
|
val = qemu_opt_get_size(opts, "size", 0);
|
2013-02-26 16:21:11 +01:00
|
|
|
if (val != 0) {
|
2015-10-26 23:34:57 +01:00
|
|
|
backend->u.ringbuf->has_size = true;
|
|
|
|
backend->u.ringbuf->size = val;
|
2013-02-26 16:21:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-24 08:39:54 +02:00
|
|
|
static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
const char *chardev = qemu_opt_get(opts, "chardev");
|
|
|
|
|
|
|
|
if (chardev == NULL) {
|
|
|
|
error_setg(errp, "chardev: mux: no chardev given");
|
|
|
|
return;
|
|
|
|
}
|
2015-10-26 23:34:57 +01:00
|
|
|
backend->u.mux = g_new0(ChardevMux, 1);
|
|
|
|
backend->u.mux->chardev = g_strdup(chardev);
|
2013-06-24 08:39:54 +02:00
|
|
|
}
|
|
|
|
|
2014-09-02 12:24:13 +02:00
|
|
|
static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
bool is_listen = qemu_opt_get_bool(opts, "server", false);
|
|
|
|
bool is_waitconnect = is_listen && qemu_opt_get_bool(opts, "wait", true);
|
|
|
|
bool is_telnet = qemu_opt_get_bool(opts, "telnet", false);
|
|
|
|
bool do_nodelay = !qemu_opt_get_bool(opts, "delay", true);
|
2014-10-02 18:17:37 +02:00
|
|
|
int64_t reconnect = qemu_opt_get_number(opts, "reconnect", 0);
|
2014-09-02 12:24:13 +02:00
|
|
|
const char *path = qemu_opt_get(opts, "path");
|
|
|
|
const char *host = qemu_opt_get(opts, "host");
|
|
|
|
const char *port = qemu_opt_get(opts, "port");
|
|
|
|
SocketAddress *addr;
|
|
|
|
|
|
|
|
if (!path) {
|
|
|
|
if (!host) {
|
|
|
|
error_setg(errp, "chardev: socket: no host given");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!port) {
|
|
|
|
error_setg(errp, "chardev: socket: no port given");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-26 23:34:57 +01:00
|
|
|
backend->u.socket = g_new0(ChardevSocket, 1);
|
2014-09-02 12:24:13 +02:00
|
|
|
|
2015-10-26 23:34:57 +01:00
|
|
|
backend->u.socket->has_nodelay = true;
|
|
|
|
backend->u.socket->nodelay = do_nodelay;
|
|
|
|
backend->u.socket->has_server = true;
|
|
|
|
backend->u.socket->server = is_listen;
|
|
|
|
backend->u.socket->has_telnet = true;
|
|
|
|
backend->u.socket->telnet = is_telnet;
|
|
|
|
backend->u.socket->has_wait = true;
|
|
|
|
backend->u.socket->wait = is_waitconnect;
|
|
|
|
backend->u.socket->has_reconnect = true;
|
|
|
|
backend->u.socket->reconnect = reconnect;
|
2014-09-02 12:24:13 +02:00
|
|
|
|
|
|
|
addr = g_new0(SocketAddress, 1);
|
|
|
|
if (path) {
|
2015-10-26 23:34:57 +01:00
|
|
|
addr->type = SOCKET_ADDRESS_KIND_UNIX;
|
|
|
|
addr->u.q_unix = g_new0(UnixSocketAddress, 1);
|
|
|
|
addr->u.q_unix->path = g_strdup(path);
|
2014-09-02 12:24:13 +02:00
|
|
|
} else {
|
2015-10-26 23:34:57 +01:00
|
|
|
addr->type = SOCKET_ADDRESS_KIND_INET;
|
|
|
|
addr->u.inet = g_new0(InetSocketAddress, 1);
|
|
|
|
addr->u.inet->host = g_strdup(host);
|
|
|
|
addr->u.inet->port = g_strdup(port);
|
|
|
|
addr->u.inet->has_to = qemu_opt_get(opts, "to");
|
|
|
|
addr->u.inet->to = qemu_opt_get_number(opts, "to", 0);
|
|
|
|
addr->u.inet->has_ipv4 = qemu_opt_get(opts, "ipv4");
|
|
|
|
addr->u.inet->ipv4 = qemu_opt_get_bool(opts, "ipv4", 0);
|
|
|
|
addr->u.inet->has_ipv6 = qemu_opt_get(opts, "ipv6");
|
|
|
|
addr->u.inet->ipv6 = qemu_opt_get_bool(opts, "ipv6", 0);
|
2014-09-02 12:24:13 +02:00
|
|
|
}
|
2015-10-26 23:34:57 +01:00
|
|
|
backend->u.socket->addr = addr;
|
2014-09-02 12:24:13 +02:00
|
|
|
}
|
|
|
|
|
2014-09-02 12:24:15 +02:00
|
|
|
static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
const char *host = qemu_opt_get(opts, "host");
|
|
|
|
const char *port = qemu_opt_get(opts, "port");
|
|
|
|
const char *localaddr = qemu_opt_get(opts, "localaddr");
|
|
|
|
const char *localport = qemu_opt_get(opts, "localport");
|
|
|
|
bool has_local = false;
|
|
|
|
SocketAddress *addr;
|
|
|
|
|
|
|
|
if (host == NULL || strlen(host) == 0) {
|
|
|
|
host = "localhost";
|
|
|
|
}
|
|
|
|
if (port == NULL || strlen(port) == 0) {
|
|
|
|
error_setg(errp, "chardev: udp: remote port not specified");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (localport == NULL || strlen(localport) == 0) {
|
|
|
|
localport = "0";
|
|
|
|
} else {
|
|
|
|
has_local = true;
|
|
|
|
}
|
|
|
|
if (localaddr == NULL || strlen(localaddr) == 0) {
|
|
|
|
localaddr = "";
|
|
|
|
} else {
|
|
|
|
has_local = true;
|
|
|
|
}
|
|
|
|
|
2015-10-26 23:34:57 +01:00
|
|
|
backend->u.udp = g_new0(ChardevUdp, 1);
|
2014-09-02 12:24:15 +02:00
|
|
|
|
|
|
|
addr = g_new0(SocketAddress, 1);
|
2015-10-26 23:34:57 +01:00
|
|
|
addr->type = SOCKET_ADDRESS_KIND_INET;
|
|
|
|
addr->u.inet = g_new0(InetSocketAddress, 1);
|
|
|
|
addr->u.inet->host = g_strdup(host);
|
|
|
|
addr->u.inet->port = g_strdup(port);
|
|
|
|
addr->u.inet->has_ipv4 = qemu_opt_get(opts, "ipv4");
|
|
|
|
addr->u.inet->ipv4 = qemu_opt_get_bool(opts, "ipv4", 0);
|
|
|
|
addr->u.inet->has_ipv6 = qemu_opt_get(opts, "ipv6");
|
|
|
|
addr->u.inet->ipv6 = qemu_opt_get_bool(opts, "ipv6", 0);
|
|
|
|
backend->u.udp->remote = addr;
|
2014-09-02 12:24:15 +02:00
|
|
|
|
|
|
|
if (has_local) {
|
2015-10-26 23:34:57 +01:00
|
|
|
backend->u.udp->has_local = true;
|
2014-09-02 12:24:15 +02:00
|
|
|
addr = g_new0(SocketAddress, 1);
|
2015-10-26 23:34:57 +01:00
|
|
|
addr->type = SOCKET_ADDRESS_KIND_INET;
|
|
|
|
addr->u.inet = g_new0(InetSocketAddress, 1);
|
|
|
|
addr->u.inet->host = g_strdup(localaddr);
|
|
|
|
addr->u.inet->port = g_strdup(localport);
|
|
|
|
backend->u.udp->local = addr;
|
2014-09-02 12:24:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-05 18:51:28 +01:00
|
|
|
typedef struct CharDriver {
|
2009-09-10 10:58:35 +02:00
|
|
|
const char *name;
|
2013-06-24 08:39:52 +02:00
|
|
|
ChardevBackendKind kind;
|
2013-02-21 11:39:12 +01:00
|
|
|
void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp);
|
2015-09-29 14:55:59 +02:00
|
|
|
CharDriverState *(*create)(const char *id, ChardevBackend *backend,
|
|
|
|
ChardevReturn *ret, Error **errp);
|
2013-03-05 18:51:28 +01:00
|
|
|
} CharDriver;
|
|
|
|
|
|
|
|
static GSList *backends;
|
|
|
|
|
2014-09-02 12:24:17 +02:00
|
|
|
void register_char_driver(const char *name, ChardevBackendKind kind,
|
2015-09-29 14:55:59 +02:00
|
|
|
void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp),
|
|
|
|
CharDriverState *(*create)(const char *id, ChardevBackend *backend,
|
|
|
|
ChardevReturn *ret, Error **errp))
|
2013-02-21 11:39:12 +01:00
|
|
|
{
|
|
|
|
CharDriver *s;
|
|
|
|
|
|
|
|
s = g_malloc0(sizeof(*s));
|
|
|
|
s->name = g_strdup(name);
|
|
|
|
s->kind = kind;
|
|
|
|
s->parse = parse;
|
2015-09-29 14:55:59 +02:00
|
|
|
s->create = create;
|
2013-02-21 11:39:12 +01:00
|
|
|
|
|
|
|
backends = g_slist_append(backends, s);
|
|
|
|
}
|
|
|
|
|
2011-08-15 18:17:37 +02:00
|
|
|
CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
|
2012-10-15 09:28:05 +02:00
|
|
|
void (*init)(struct CharDriverState *s),
|
|
|
|
Error **errp)
|
2009-09-10 10:58:35 +02:00
|
|
|
{
|
2014-05-19 18:57:35 +02:00
|
|
|
Error *local_err = NULL;
|
2013-03-05 18:51:28 +01:00
|
|
|
CharDriver *cd;
|
2009-09-10 10:58:35 +02:00
|
|
|
CharDriverState *chr;
|
2013-03-05 18:51:28 +01:00
|
|
|
GSList *i;
|
2014-09-02 12:24:16 +02:00
|
|
|
ChardevReturn *ret = NULL;
|
|
|
|
ChardevBackend *backend;
|
|
|
|
const char *id = qemu_opts_id(opts);
|
|
|
|
char *bid = NULL;
|
2009-09-10 10:58:35 +02:00
|
|
|
|
2014-09-02 12:24:16 +02:00
|
|
|
if (id == 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 21:22:16 +01:00
|
|
|
error_setg(errp, "chardev: no id specified");
|
2012-10-15 09:30:59 +02:00
|
|
|
goto err;
|
2009-09-10 10:58:35 +02:00
|
|
|
}
|
|
|
|
|
2011-01-22 14:07:26 +01: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 21:22:16 +01:00
|
|
|
error_setg(errp, "chardev: \"%s\" missing backend",
|
2012-10-15 09:28:05 +02:00
|
|
|
qemu_opts_id(opts));
|
2012-10-15 09:30:59 +02:00
|
|
|
goto err;
|
2011-01-22 14:07:26 +01:00
|
|
|
}
|
2013-03-05 18:51:28 +01:00
|
|
|
for (i = backends; i; i = i->next) {
|
|
|
|
cd = i->data;
|
|
|
|
|
|
|
|
if (strcmp(cd->name, qemu_opt_get(opts, "backend")) == 0) {
|
2009-09-10 10:58:35 +02:00
|
|
|
break;
|
2013-03-05 18:51:28 +01:00
|
|
|
}
|
2009-09-10 10:58:35 +02:00
|
|
|
}
|
2013-03-05 18:51:28 +01: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 21:22:16 +01:00
|
|
|
error_setg(errp, "chardev: backend \"%s\" not found",
|
2012-10-15 09:28:05 +02:00
|
|
|
qemu_opt_get(opts, "backend"));
|
2013-06-24 08:39:51 +02:00
|
|
|
goto err;
|
2009-09-10 10:58:35 +02:00
|
|
|
}
|
|
|
|
|
2014-09-02 12:24:16 +02:00
|
|
|
backend = g_new0(ChardevBackend, 1);
|
2013-02-21 16:16:42 +01:00
|
|
|
|
2014-09-02 12:24:16 +02:00
|
|
|
if (qemu_opt_get_bool(opts, "mux", 0)) {
|
|
|
|
bid = g_strdup_printf("%s-base", id);
|
|
|
|
}
|
2013-02-21 11:39:12 +01:00
|
|
|
|
2014-09-02 12:24:16 +02:00
|
|
|
chr = NULL;
|
2015-10-26 23:34:57 +01:00
|
|
|
backend->type = cd->kind;
|
2014-09-02 12:24:16 +02:00
|
|
|
if (cd->parse) {
|
|
|
|
cd->parse(opts, backend, &local_err);
|
|
|
|
if (local_err) {
|
|
|
|
error_propagate(errp, local_err);
|
2013-02-21 11:39:12 +01:00
|
|
|
goto qapi_out;
|
|
|
|
}
|
|
|
|
}
|
2014-09-02 12:24:16 +02:00
|
|
|
ret = qmp_chardev_add(bid ? bid : id, backend, errp);
|
|
|
|
if (!ret) {
|
|
|
|
goto qapi_out;
|
2009-09-10 10:58:35 +02:00
|
|
|
}
|
|
|
|
|
2014-09-02 12:24:16 +02:00
|
|
|
if (bid) {
|
|
|
|
qapi_free_ChardevBackend(backend);
|
|
|
|
qapi_free_ChardevReturn(ret);
|
|
|
|
backend = g_new0(ChardevBackend, 1);
|
2015-10-26 23:34:57 +01:00
|
|
|
backend->u.mux = g_new0(ChardevMux, 1);
|
|
|
|
backend->type = CHARDEV_BACKEND_KIND_MUX;
|
|
|
|
backend->u.mux->chardev = g_strdup(bid);
|
2014-09-02 12:24:16 +02:00
|
|
|
ret = qmp_chardev_add(id, backend, errp);
|
|
|
|
if (!ret) {
|
|
|
|
chr = qemu_chr_find(bid);
|
|
|
|
qemu_chr_delete(chr);
|
|
|
|
chr = NULL;
|
|
|
|
goto qapi_out;
|
|
|
|
}
|
qemu-char: don't issue CHR_EVENT_OPEN in a BH
When CHR_EVENT_OPENED was initially added, it was CHR_EVENT_RESET,
and it was issued as a bottom-half:
86e94dea5b740dad65446c857f6959eae43e0ba6
Which we basically used to print out a greeting/prompt for the
monitor.
AFAICT the only reason this was ever done in a BH was because in
some cases we'd modify the chr_write handler for a new chardev
backend *after* the site where we issued the reset (see:
86e94d:qemu_chr_open_stdio())
At some point this event was renamed to CHR_EVENT_OPENED, and we've
maintained the use of this BH ever since.
However, due to 9f939df955a4152aad69a19a77e0898631bb2c18, we schedule
the BH via g_idle_add(), which is causing events to sometimes be
delivered after we've already begun processing data from backends,
leading to:
known bugs:
QMP:
session negotation resets with OPENED event, in some cases this
is causing new sessions to get sporadically reset
potential bugs:
hw/usb/redirect.c:
can_read handler checks for dev->parser != NULL, which may be
true if CLOSED BH has not been executed yet. In the past, OPENED
quiesced outstanding CLOSED events prior to us reading client
data. If it's delayed, our check may allow reads to occur even
though we haven't processed the OPENED event yet, and when we
do finally get the OPENED event, our state may get reset.
qtest.c:
can begin session before OPENED event is processed, leading to
a spurious reset of the system and irq_levels
gdbstub.c:
may start a gdb session prior to the machine being paused
To fix these, let's just drop the BH.
Since the initial reasoning for using it still applies to an extent,
work around that by deferring the delivery of CHR_EVENT_OPENED until
after the chardevs have been fully initialized, toward the end of
qmp_chardev_add() (or some cases, qemu_chr_new_from_opts()). This
defers delivery long enough that we can be assured a CharDriverState
is fully initialized before CHR_EVENT_OPENED is sent.
Also, rather than requiring each chardev to do an explicit open, do it
automatically, and allow the small few who don't desire such behavior to
suppress the OPENED-on-init behavior by setting a 'explicit_be_open'
flag.
We additionally add missing OPENED events for stdio backends on w32,
which were previously not being issued, causing us to not recieve the
banner and initial prompts for qmp/hmp.
Reported-by: Stefan Priebe <s.priebe@profihost.ag>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
Message-id: 1370636393-21044-1-git-send-email-mdroth@linux.vnet.ibm.com
Cc: qemu-stable@nongnu.org
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-06-07 22:19:53 +02:00
|
|
|
}
|
2009-09-10 10:58:50 +02:00
|
|
|
|
2014-09-02 12:24:16 +02:00
|
|
|
chr = qemu_chr_find(id);
|
2012-10-15 09:30:59 +02:00
|
|
|
chr->opts = opts;
|
2014-09-02 12:24:16 +02:00
|
|
|
|
|
|
|
qapi_out:
|
|
|
|
qapi_free_ChardevBackend(backend);
|
|
|
|
qapi_free_ChardevReturn(ret);
|
|
|
|
g_free(bid);
|
2009-09-10 10:58:35 +02:00
|
|
|
return chr;
|
2012-10-15 09:30:59 +02:00
|
|
|
|
|
|
|
err:
|
|
|
|
qemu_opts_del(opts);
|
|
|
|
return NULL;
|
2009-09-10 10:58:35 +02:00
|
|
|
}
|
|
|
|
|
2011-08-15 18:17:36 +02:00
|
|
|
CharDriverState *qemu_chr_new(const char *label, const char *filename, void (*init)(struct CharDriverState *s))
|
2008-10-31 19:49:55 +01:00
|
|
|
{
|
|
|
|
const char *p;
|
|
|
|
CharDriverState *chr;
|
2009-09-10 10:58:35 +02:00
|
|
|
QemuOpts *opts;
|
2012-10-15 09:28:05 +02:00
|
|
|
Error *err = NULL;
|
2009-09-10 10:58:35 +02:00
|
|
|
|
2009-09-10 10:58:52 +02:00
|
|
|
if (strstart(filename, "chardev:", &p)) {
|
|
|
|
return qemu_chr_find(p);
|
|
|
|
}
|
|
|
|
|
2009-09-10 10:58:35 +02:00
|
|
|
opts = qemu_chr_parse_compat(label, filename);
|
2009-09-10 10:58:51 +02:00
|
|
|
if (!opts)
|
|
|
|
return NULL;
|
2008-10-31 19:49:55 +01:00
|
|
|
|
2012-10-15 09:28:05 +02:00
|
|
|
chr = qemu_chr_new_from_opts(opts, init, &err);
|
2014-01-30 15:07:28 +01:00
|
|
|
if (err) {
|
2015-02-12 13:55:05 +01:00
|
|
|
error_report_err(err);
|
2012-10-15 09:28:05 +02:00
|
|
|
}
|
2009-09-10 10:58:51 +02:00
|
|
|
if (chr && qemu_opt_get_bool(opts, "mux", 0)) {
|
2013-03-27 20:29:40 +01:00
|
|
|
qemu_chr_fe_claim_no_fail(chr);
|
2009-09-10 10:58:51 +02:00
|
|
|
monitor_init(chr, MONITOR_USE_READLINE);
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
|
|
|
return chr;
|
|
|
|
}
|
|
|
|
|
2011-08-15 18:17:35 +02:00
|
|
|
void qemu_chr_fe_set_echo(struct CharDriverState *chr, bool echo)
|
2010-12-23 13:42:48 +01:00
|
|
|
{
|
|
|
|
if (chr->chr_set_echo) {
|
|
|
|
chr->chr_set_echo(chr, echo);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-26 11:07:57 +01:00
|
|
|
void qemu_chr_fe_set_open(struct CharDriverState *chr, int fe_open)
|
2011-03-24 11:12:02 +01:00
|
|
|
{
|
2013-03-26 11:07:57 +01:00
|
|
|
if (chr->fe_open == fe_open) {
|
2013-03-26 11:07:55 +01:00
|
|
|
return;
|
|
|
|
}
|
2013-03-26 11:07:57 +01:00
|
|
|
chr->fe_open = fe_open;
|
2013-03-26 11:07:58 +01:00
|
|
|
if (chr->chr_set_fe_open) {
|
|
|
|
chr->chr_set_fe_open(chr, fe_open);
|
2011-03-24 11:12:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-01 22:23:39 +01:00
|
|
|
void qemu_chr_fe_event(struct CharDriverState *chr, int event)
|
|
|
|
{
|
|
|
|
if (chr->chr_fe_event) {
|
|
|
|
chr->chr_fe_event(chr, event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-19 13:38:09 +01:00
|
|
|
int qemu_chr_fe_add_watch(CharDriverState *s, GIOCondition cond,
|
|
|
|
GIOFunc func, void *user_data)
|
2013-03-05 18:51:23 +01:00
|
|
|
{
|
|
|
|
GSource *src;
|
|
|
|
guint tag;
|
|
|
|
|
|
|
|
if (s->chr_add_watch == NULL) {
|
|
|
|
return -ENOSYS;
|
|
|
|
}
|
|
|
|
|
|
|
|
src = s->chr_add_watch(s, cond);
|
2014-07-24 16:08:04 +02:00
|
|
|
if (!src) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2013-03-05 18:51:23 +01:00
|
|
|
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 20:29:39 +01: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++;
|
|
|
|
}
|
|
|
|
|
2015-06-22 18:20:18 +02:00
|
|
|
void qemu_chr_free(CharDriverState *chr)
|
2008-10-31 19:49:55 +01:00
|
|
|
{
|
2012-10-15 09:30:59 +02:00
|
|
|
if (chr->chr_close) {
|
2008-10-31 19:49:55 +01:00
|
|
|
chr->chr_close(chr);
|
2012-10-15 09:30:59 +02:00
|
|
|
}
|
2011-08-21 05:09:37 +02:00
|
|
|
g_free(chr->filename);
|
|
|
|
g_free(chr->label);
|
2014-12-03 11:28:02 +01:00
|
|
|
qemu_opts_del(chr->opts);
|
2011-08-21 05:09:37 +02:00
|
|
|
g_free(chr);
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
|
|
|
|
2015-06-22 18:20:18 +02:00
|
|
|
void qemu_chr_delete(CharDriverState *chr)
|
|
|
|
{
|
|
|
|
QTAILQ_REMOVE(&chardevs, chr, next);
|
|
|
|
qemu_chr_free(chr);
|
|
|
|
}
|
|
|
|
|
2011-09-14 21:05:49 +02:00
|
|
|
ChardevInfoList *qmp_query_chardev(Error **errp)
|
2008-10-31 19:49:55 +01:00
|
|
|
{
|
2011-09-14 21:05:49 +02:00
|
|
|
ChardevInfoList *chr_list = NULL;
|
2008-10-31 19:49:55 +01:00
|
|
|
CharDriverState *chr;
|
|
|
|
|
2009-09-12 09:36:22 +02:00
|
|
|
QTAILQ_FOREACH(chr, &chardevs, next) {
|
2011-09-14 21:05:49 +02: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);
|
2014-06-26 17:50:03 +02:00
|
|
|
info->value->frontend_open = chr->fe_open;
|
2011-09-14 21:05:49 +02:00
|
|
|
|
|
|
|
info->next = chr_list;
|
|
|
|
chr_list = info;
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
2009-12-10 20:16:08 +01:00
|
|
|
|
2011-09-14 21:05:49 +02:00
|
|
|
return chr_list;
|
2008-10-31 19:49:55 +01:00
|
|
|
}
|
2009-09-10 10:58:52 +02:00
|
|
|
|
2014-02-01 12:52:42 +01:00
|
|
|
ChardevBackendInfoList *qmp_query_chardev_backends(Error **errp)
|
|
|
|
{
|
|
|
|
ChardevBackendInfoList *backend_list = NULL;
|
|
|
|
CharDriver *c = NULL;
|
|
|
|
GSList *i = NULL;
|
|
|
|
|
|
|
|
for (i = backends; i; i = i->next) {
|
|
|
|
ChardevBackendInfoList *info = g_malloc0(sizeof(*info));
|
|
|
|
c = i->data;
|
|
|
|
info->value = g_malloc0(sizeof(*info->value));
|
|
|
|
info->value->name = g_strdup(c->name);
|
|
|
|
|
|
|
|
info->next = backend_list;
|
|
|
|
backend_list = info;
|
|
|
|
}
|
|
|
|
|
|
|
|
return backend_list;
|
|
|
|
}
|
|
|
|
|
2009-09-10 10:58:52 +02:00
|
|
|
CharDriverState *qemu_chr_find(const char *name)
|
|
|
|
{
|
|
|
|
CharDriverState *chr;
|
|
|
|
|
2009-09-12 09:36:22 +02:00
|
|
|
QTAILQ_FOREACH(chr, &chardevs, next) {
|
2009-09-10 10:58:52 +02:00
|
|
|
if (strcmp(chr->label, name) != 0)
|
|
|
|
continue;
|
|
|
|
return chr;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
2011-12-22 22:29:25 +01:00
|
|
|
|
|
|
|
/* Get a character (serial) device interface. */
|
|
|
|
CharDriverState *qemu_char_get_next_serial(void)
|
|
|
|
{
|
|
|
|
static int next_serial;
|
2013-03-27 20:29:40 +01:00
|
|
|
CharDriverState *chr;
|
2011-12-22 22:29:25 +01:00
|
|
|
|
|
|
|
/* FIXME: This function needs to go away: use chardev properties! */
|
2013-03-27 20:29:40 +01: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 22:29:25 +01:00
|
|
|
}
|
|
|
|
|
2012-11-26 16:03:42 +01: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,
|
2014-10-02 18:17:37 +02:00
|
|
|
},{
|
|
|
|
.name = "reconnect",
|
|
|
|
.type = QEMU_OPT_NUMBER,
|
2012-11-26 16:03:42 +01:00
|
|
|
},{
|
|
|
|
.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 17:03:19 +01: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 21:27:24 +01:00
|
|
|
.name = "size",
|
2013-02-06 21:27:25 +01:00
|
|
|
.type = QEMU_OPT_SIZE,
|
2013-06-24 08:39:54 +02:00
|
|
|
},{
|
|
|
|
.name = "chardev",
|
|
|
|
.type = QEMU_OPT_STRING,
|
2012-11-26 16:03:42 +01:00
|
|
|
},
|
|
|
|
{ /* end of list */ }
|
|
|
|
},
|
|
|
|
};
|
2012-12-19 10:33:56 +01:00
|
|
|
|
2012-12-19 13:13:57 +01:00
|
|
|
#ifdef _WIN32
|
|
|
|
|
2015-09-29 15:06:02 +02:00
|
|
|
static CharDriverState *qmp_chardev_open_file(const char *id,
|
|
|
|
ChardevBackend *backend,
|
|
|
|
ChardevReturn *ret,
|
|
|
|
Error **errp)
|
2012-12-19 13:13:57 +01:00
|
|
|
{
|
2015-10-26 23:34:57 +01:00
|
|
|
ChardevFile *file = backend->u.file;
|
2012-12-19 13:13:57 +01:00
|
|
|
HANDLE out;
|
|
|
|
|
2013-06-24 08:39:47 +02:00
|
|
|
if (file->has_in) {
|
2012-12-19 13:13:57 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2015-09-29 15:08:05 +02:00
|
|
|
static CharDriverState *qmp_chardev_open_serial(const char *id,
|
|
|
|
ChardevBackend *backend,
|
|
|
|
ChardevReturn *ret,
|
2013-02-13 15:54:16 +01:00
|
|
|
Error **errp)
|
2012-12-19 13:50:29 +01:00
|
|
|
{
|
2015-10-26 23:34:57 +01:00
|
|
|
ChardevHostdev *serial = backend->u.serial;
|
2015-09-29 15:08:05 +02:00
|
|
|
return qemu_chr_open_win_path(serial->device, errp);
|
2013-02-13 15:54:16 +01:00
|
|
|
}
|
|
|
|
|
2012-12-19 13:13:57 +01: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) {
|
2013-06-24 08:39:48 +02:00
|
|
|
error_setg_file_open(errp, errno, src);
|
2012-12-19 13:13:57 +01:00
|
|
|
}
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
2015-09-29 15:06:02 +02:00
|
|
|
static CharDriverState *qmp_chardev_open_file(const char *id,
|
|
|
|
ChardevBackend *backend,
|
|
|
|
ChardevReturn *ret,
|
|
|
|
Error **errp)
|
2012-12-19 13:13:57 +01:00
|
|
|
{
|
2015-10-26 23:34:57 +01:00
|
|
|
ChardevFile *file = backend->u.file;
|
2014-05-19 18:57:34 +02:00
|
|
|
int flags, in = -1, out;
|
2012-12-19 13:13:57 +01:00
|
|
|
|
|
|
|
flags = O_WRONLY | O_TRUNC | O_CREAT | O_BINARY;
|
|
|
|
out = qmp_chardev_open_file_source(file->out, flags, errp);
|
2014-05-19 18:57:34 +02:00
|
|
|
if (out < 0) {
|
2012-12-19 13:13:57 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-06-24 08:39:47 +02:00
|
|
|
if (file->has_in) {
|
2012-12-19 13:13:57 +01:00
|
|
|
flags = O_RDONLY;
|
|
|
|
in = qmp_chardev_open_file_source(file->in, flags, errp);
|
2014-05-19 18:57:34 +02:00
|
|
|
if (in < 0) {
|
2012-12-19 13:13:57 +01:00
|
|
|
qemu_close(out);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return qemu_chr_open_fd(in, out);
|
|
|
|
}
|
|
|
|
|
2015-10-12 09:46:23 +02:00
|
|
|
#ifdef HAVE_CHARDEV_SERIAL
|
2015-09-29 15:08:05 +02:00
|
|
|
static CharDriverState *qmp_chardev_open_serial(const char *id,
|
|
|
|
ChardevBackend *backend,
|
|
|
|
ChardevReturn *ret,
|
2013-02-13 15:54:16 +01:00
|
|
|
Error **errp)
|
2012-12-19 13:50:29 +01:00
|
|
|
{
|
2015-10-26 23:34:57 +01:00
|
|
|
ChardevHostdev *serial = backend->u.serial;
|
2013-02-13 15:54:16 +01:00
|
|
|
int fd;
|
|
|
|
|
|
|
|
fd = qmp_chardev_open_file_source(serial->device, O_RDWR, errp);
|
2014-05-19 18:57:34 +02:00
|
|
|
if (fd < 0) {
|
2013-02-13 15:54:16 +01:00
|
|
|
return NULL;
|
2013-01-24 17:14:39 +01:00
|
|
|
}
|
2013-03-27 10:10:43 +01:00
|
|
|
qemu_set_nonblock(fd);
|
2013-02-13 15:54:16 +01:00
|
|
|
return qemu_chr_open_tty_fd(fd);
|
|
|
|
}
|
2015-09-29 15:08:05 +02:00
|
|
|
#endif
|
2013-02-13 15:54:16 +01:00
|
|
|
|
2015-10-12 09:46:23 +02:00
|
|
|
#ifdef HAVE_CHARDEV_PARPORT
|
2015-10-12 09:49:28 +02:00
|
|
|
static CharDriverState *qmp_chardev_open_parallel(const char *id,
|
|
|
|
ChardevBackend *backend,
|
|
|
|
ChardevReturn *ret,
|
2013-02-13 15:54:16 +01:00
|
|
|
Error **errp)
|
|
|
|
{
|
2015-10-26 23:34:57 +01:00
|
|
|
ChardevHostdev *parallel = backend->u.parallel;
|
2013-02-13 15:54:16 +01:00
|
|
|
int fd;
|
|
|
|
|
|
|
|
fd = qmp_chardev_open_file_source(parallel->device, O_RDWR, errp);
|
2014-05-19 18:57:34 +02:00
|
|
|
if (fd < 0) {
|
2012-12-19 13:50:29 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
2015-10-12 09:49:28 +02:00
|
|
|
return qemu_chr_open_pp_fd(fd, errp);
|
2012-12-19 13:50:29 +01:00
|
|
|
}
|
2015-10-12 09:46:23 +02:00
|
|
|
#endif
|
2012-12-19 13:50:29 +01:00
|
|
|
|
2012-12-19 13:13:57 +01:00
|
|
|
#endif /* WIN32 */
|
|
|
|
|
2014-10-08 14:11:55 +02:00
|
|
|
static void socket_try_connect(CharDriverState *chr)
|
|
|
|
{
|
|
|
|
Error *err = NULL;
|
|
|
|
|
|
|
|
if (!qemu_chr_open_socket_fd(chr, &err)) {
|
|
|
|
check_report_connect_error(chr, err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-02 18:17:37 +02:00
|
|
|
static gboolean socket_reconnect_timeout(gpointer opaque)
|
|
|
|
{
|
|
|
|
CharDriverState *chr = opaque;
|
|
|
|
TCPCharDriver *s = chr->opaque;
|
|
|
|
|
|
|
|
s->reconnect_timer = 0;
|
|
|
|
|
|
|
|
if (chr->be_open) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-10-08 14:11:55 +02:00
|
|
|
socket_try_connect(chr);
|
2014-10-02 18:17:37 +02:00
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-09-29 15:15:53 +02:00
|
|
|
static CharDriverState *qmp_chardev_open_socket(const char *id,
|
|
|
|
ChardevBackend *backend,
|
|
|
|
ChardevReturn *ret,
|
2012-12-20 13:53:12 +01:00
|
|
|
Error **errp)
|
|
|
|
{
|
2014-10-02 18:17:34 +02:00
|
|
|
CharDriverState *chr;
|
|
|
|
TCPCharDriver *s;
|
2015-10-26 23:34:57 +01:00
|
|
|
ChardevSocket *sock = backend->u.socket;
|
2012-12-20 13:53:12 +01:00
|
|
|
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;
|
2014-10-02 18:17:37 +02:00
|
|
|
int64_t reconnect = sock->has_reconnect ? sock->reconnect : 0;
|
2014-10-02 18:17:34 +02:00
|
|
|
|
|
|
|
chr = qemu_chr_alloc();
|
2015-09-14 13:54:03 +02:00
|
|
|
s = g_new0(TCPCharDriver, 1);
|
2014-10-02 18:17:34 +02:00
|
|
|
|
|
|
|
s->fd = -1;
|
|
|
|
s->listen_fd = -1;
|
2015-10-26 23:34:57 +01:00
|
|
|
s->is_unix = addr->type == SOCKET_ADDRESS_KIND_UNIX;
|
2014-10-02 18:17:35 +02:00
|
|
|
s->is_listen = is_listen;
|
|
|
|
s->is_telnet = is_telnet;
|
2014-10-02 18:17:34 +02:00
|
|
|
s->do_nodelay = do_nodelay;
|
2014-10-02 18:17:35 +02:00
|
|
|
qapi_copy_SocketAddress(&s->addr, sock->addr);
|
2014-10-02 18:17:34 +02:00
|
|
|
|
|
|
|
chr->opaque = s;
|
|
|
|
chr->chr_write = tcp_chr_write;
|
|
|
|
chr->chr_sync_read = tcp_chr_sync_read;
|
|
|
|
chr->chr_close = tcp_chr_close;
|
|
|
|
chr->get_msgfds = tcp_get_msgfds;
|
|
|
|
chr->set_msgfds = tcp_set_msgfds;
|
|
|
|
chr->chr_add_client = tcp_chr_add_client;
|
|
|
|
chr->chr_add_watch = tcp_chr_add_watch;
|
|
|
|
chr->chr_update_read_handler = tcp_chr_update_read_handler;
|
|
|
|
/* be isn't opened until we get a connection */
|
|
|
|
chr->explicit_be_open = true;
|
|
|
|
|
|
|
|
chr->filename = g_malloc(CHR_MAX_FILENAME_SIZE);
|
2014-10-02 18:17:36 +02:00
|
|
|
SocketAddress_to_str(chr->filename, CHR_MAX_FILENAME_SIZE, "disconnected:",
|
|
|
|
addr, is_listen, is_telnet);
|
2012-12-20 13:53:12 +01:00
|
|
|
|
|
|
|
if (is_listen) {
|
2014-10-02 18:17:34 +02:00
|
|
|
if (is_telnet) {
|
|
|
|
s->do_telnetopt = 1;
|
|
|
|
}
|
2014-10-02 18:17:37 +02:00
|
|
|
} else if (reconnect > 0) {
|
|
|
|
s->reconnect_time = reconnect;
|
2012-12-20 13:53:12 +01:00
|
|
|
}
|
2014-10-02 18:17:34 +02:00
|
|
|
|
2014-10-08 14:11:55 +02:00
|
|
|
if (s->reconnect_time) {
|
|
|
|
socket_try_connect(chr);
|
|
|
|
} else if (!qemu_chr_open_socket_fd(chr, errp)) {
|
|
|
|
g_free(s);
|
|
|
|
g_free(chr->filename);
|
|
|
|
g_free(chr);
|
|
|
|
return NULL;
|
2012-12-20 13:53:12 +01:00
|
|
|
}
|
2014-10-02 18:17:34 +02:00
|
|
|
|
|
|
|
if (is_listen && is_waitconnect) {
|
|
|
|
fprintf(stderr, "QEMU waiting for connection on: %s\n",
|
|
|
|
chr->filename);
|
|
|
|
tcp_chr_accept(s->listen_chan, G_IO_IN, chr);
|
|
|
|
qemu_set_nonblock(s->listen_fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
return chr;
|
2012-12-20 13:53:12 +01:00
|
|
|
}
|
|
|
|
|
2015-09-29 15:17:20 +02:00
|
|
|
static CharDriverState *qmp_chardev_open_udp(const char *id,
|
|
|
|
ChardevBackend *backend,
|
|
|
|
ChardevReturn *ret,
|
2013-05-20 08:51:03 +02:00
|
|
|
Error **errp)
|
2013-02-27 14:10:47 +01:00
|
|
|
{
|
2015-10-26 23:34:57 +01:00
|
|
|
ChardevUdp *udp = backend->u.udp;
|
2013-02-27 14:10:47 +01:00
|
|
|
int fd;
|
|
|
|
|
2013-05-20 08:51:03 +02:00
|
|
|
fd = socket_dgram(udp->remote, udp->local, errp);
|
2014-05-19 18:57:34 +02:00
|
|
|
if (fd < 0) {
|
2013-02-27 14:10:47 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return qemu_chr_open_udp_fd(fd);
|
|
|
|
}
|
|
|
|
|
2012-12-19 10:33:56 +01:00
|
|
|
ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
ChardevReturn *ret = g_new0(ChardevReturn, 1);
|
2015-09-29 15:27:24 +02:00
|
|
|
CharDriverState *chr = NULL;
|
2015-09-29 14:54:05 +02:00
|
|
|
Error *local_err = NULL;
|
2015-09-29 14:55:59 +02:00
|
|
|
GSList *i;
|
|
|
|
CharDriver *cd;
|
2012-12-19 10:33:56 +01:00
|
|
|
|
|
|
|
chr = qemu_chr_find(id);
|
|
|
|
if (chr) {
|
|
|
|
error_setg(errp, "Chardev '%s' already exists", id);
|
|
|
|
g_free(ret);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-09-29 14:55:59 +02:00
|
|
|
for (i = backends; i; i = i->next) {
|
|
|
|
cd = i->data;
|
|
|
|
|
2015-10-26 23:34:57 +01:00
|
|
|
if (cd->kind == backend->type) {
|
2015-09-29 14:55:59 +02:00
|
|
|
chr = cd->create(id, backend, ret, &local_err);
|
|
|
|
if (local_err) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
goto out_error;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (chr == NULL) {
|
2015-10-12 09:51:41 +02:00
|
|
|
assert(!i);
|
|
|
|
error_setg(errp, "chardev backend not available");
|
|
|
|
goto out_error;
|
2015-09-29 14:54:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
chr->label = g_strdup(id);
|
|
|
|
chr->avail_connections =
|
2015-10-26 23:34:57 +01:00
|
|
|
(backend->type == CHARDEV_BACKEND_KIND_MUX) ? MAX_MUX : 1;
|
2015-09-29 14:54:05 +02:00
|
|
|
if (!chr->filename) {
|
2015-10-26 23:34:57 +01:00
|
|
|
chr->filename = g_strdup(ChardevBackendKind_lookup[backend->type]);
|
2012-12-19 10:33:56 +01:00
|
|
|
}
|
2015-09-29 14:54:05 +02:00
|
|
|
if (!chr->explicit_be_open) {
|
|
|
|
qemu_chr_be_event(chr, CHR_EVENT_OPENED);
|
|
|
|
}
|
|
|
|
QTAILQ_INSERT_TAIL(&chardevs, chr, next);
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
out_error:
|
|
|
|
g_free(ret);
|
|
|
|
return NULL;
|
2012-12-19 10:33:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void qmp_chardev_remove(const char *id, Error **errp)
|
|
|
|
{
|
|
|
|
CharDriverState *chr;
|
|
|
|
|
|
|
|
chr = qemu_chr_find(id);
|
2014-08-11 15:00:55 +02:00
|
|
|
if (chr == NULL) {
|
2012-12-19 10:33:56 +01:00
|
|
|
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 18:51:28 +01:00
|
|
|
|
|
|
|
static void register_types(void)
|
|
|
|
{
|
2015-09-29 14:55:59 +02:00
|
|
|
register_char_driver("null", CHARDEV_BACKEND_KIND_NULL, NULL,
|
2015-09-29 15:24:29 +02:00
|
|
|
qemu_chr_open_null);
|
2014-09-02 12:24:17 +02:00
|
|
|
register_char_driver("socket", CHARDEV_BACKEND_KIND_SOCKET,
|
2015-09-29 15:15:53 +02:00
|
|
|
qemu_chr_parse_socket, qmp_chardev_open_socket);
|
2015-09-29 14:55:59 +02:00
|
|
|
register_char_driver("udp", CHARDEV_BACKEND_KIND_UDP, qemu_chr_parse_udp,
|
2015-09-29 15:17:20 +02:00
|
|
|
qmp_chardev_open_udp);
|
2014-09-02 12:24:17 +02:00
|
|
|
register_char_driver("ringbuf", CHARDEV_BACKEND_KIND_RINGBUF,
|
2015-09-29 15:51:14 +02:00
|
|
|
qemu_chr_parse_ringbuf, qemu_chr_open_ringbuf);
|
2014-09-02 12:24:17 +02:00
|
|
|
register_char_driver("file", CHARDEV_BACKEND_KIND_FILE,
|
2015-09-29 15:06:02 +02:00
|
|
|
qemu_chr_parse_file_out, qmp_chardev_open_file);
|
2014-09-02 12:24:17 +02:00
|
|
|
register_char_driver("stdio", CHARDEV_BACKEND_KIND_STDIO,
|
2015-09-29 15:40:28 +02:00
|
|
|
qemu_chr_parse_stdio, qemu_chr_open_stdio);
|
2015-09-29 15:08:05 +02:00
|
|
|
#if defined HAVE_CHARDEV_SERIAL
|
2014-09-02 12:24:17 +02:00
|
|
|
register_char_driver("serial", CHARDEV_BACKEND_KIND_SERIAL,
|
2015-09-29 15:08:05 +02:00
|
|
|
qemu_chr_parse_serial, qmp_chardev_open_serial);
|
2014-09-02 12:24:17 +02:00
|
|
|
register_char_driver("tty", CHARDEV_BACKEND_KIND_SERIAL,
|
2015-09-29 15:08:05 +02:00
|
|
|
qemu_chr_parse_serial, qmp_chardev_open_serial);
|
|
|
|
#endif
|
2015-10-12 09:49:28 +02:00
|
|
|
#ifdef HAVE_CHARDEV_PARPORT
|
2014-09-02 12:24:17 +02:00
|
|
|
register_char_driver("parallel", CHARDEV_BACKEND_KIND_PARALLEL,
|
2015-10-12 09:49:28 +02:00
|
|
|
qemu_chr_parse_parallel, qmp_chardev_open_parallel);
|
2014-09-02 12:24:17 +02:00
|
|
|
register_char_driver("parport", CHARDEV_BACKEND_KIND_PARALLEL,
|
2015-10-12 09:49:28 +02:00
|
|
|
qemu_chr_parse_parallel, qmp_chardev_open_parallel);
|
|
|
|
#endif
|
2015-09-29 15:23:42 +02:00
|
|
|
#ifdef HAVE_CHARDEV_PTY
|
2015-09-29 14:55:59 +02:00
|
|
|
register_char_driver("pty", CHARDEV_BACKEND_KIND_PTY, NULL,
|
2015-09-29 15:23:42 +02:00
|
|
|
qemu_chr_open_pty);
|
|
|
|
#endif
|
2015-09-29 15:42:04 +02:00
|
|
|
#ifdef _WIN32
|
2015-09-29 14:55:59 +02:00
|
|
|
register_char_driver("console", CHARDEV_BACKEND_KIND_CONSOLE, NULL,
|
2015-09-29 15:42:04 +02:00
|
|
|
qemu_chr_open_win_con);
|
|
|
|
#endif
|
2014-09-02 12:24:17 +02:00
|
|
|
register_char_driver("pipe", CHARDEV_BACKEND_KIND_PIPE,
|
2015-09-29 15:14:07 +02:00
|
|
|
qemu_chr_parse_pipe, qemu_chr_open_pipe);
|
2015-09-29 14:55:59 +02:00
|
|
|
register_char_driver("mux", CHARDEV_BACKEND_KIND_MUX, qemu_chr_parse_mux,
|
2015-09-29 15:27:24 +02:00
|
|
|
qemu_chr_open_mux);
|
2013-07-26 16:44:33 +02:00
|
|
|
/* Bug-compatibility: */
|
2014-09-02 12:24:17 +02:00
|
|
|
register_char_driver("memory", CHARDEV_BACKEND_KIND_MEMORY,
|
2015-09-29 15:51:14 +02:00
|
|
|
qemu_chr_parse_ringbuf, qemu_chr_open_ringbuf);
|
chardev: fix CHR_EVENT_OPENED events for mux chardevs
As of bd5c51ee6c4f1c79cae5ad2516d711a27b4ea8ec, chardevs no longer use
bottom-halves to issue CHR_EVENT_OPENED events. To maintain past
semantics, we instead defer the CHR_EVENT_OPENED events toward the end
of chardev initialization.
For muxes, this isn't good enough, since a range of FEs must be able
to attach to the mux prior to any CHR_EVENT_OPENED being issued, else
each FE will immediately print it's initial output (prompts, banners,
etc.) just prior to us switching to the next FE as part of
initialization.
The is new and confusing behavior for users, as they'll see output for
things like the HMP monitor, even though their the current mux focus
may be a guest serial port with potentially no output.
We fix this by further deferring CHR_EVENT_OPENED events for FEs
associated with muxes until after machine init by flagging mux chardevs
with 'explicit_be_open', which suppresses emission of CHR_EVENT_OPENED
events until we explicitly set the mux as opened later.
Currently, we must defer till after machine init since we potentially
associate FEs with muxes as part of realize (for instance,
serial_isa_realizefn).
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
Message-id: 1375207462-8141-1-git-send-email-mdroth@linux.vnet.ibm.com
Cc: qemu-stable@nongnu.org
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-07-30 20:04:22 +02:00
|
|
|
/* this must be done after machine init, since we register FEs with muxes
|
|
|
|
* as part of realize functions like serial_isa_realizefn when -nographic
|
|
|
|
* is specified
|
|
|
|
*/
|
|
|
|
qemu_add_machine_init_done_notifier(&muxes_realize_notify);
|
2013-03-05 18:51:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
type_init(register_types);
|