qemu-e2k/chardev/char.c

1119 lines
29 KiB
C
Raw Normal View History

/*
* QEMU System Emulator
*
* Copyright (c) 2003-2008 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include "qemu/cutils.h"
#include "monitor/monitor.h"
#include "sysemu/sysemu.h"
#include "qemu/config-file.h"
#include "qemu/error-report.h"
#include "chardev/char.h"
#include "qapi/error.h"
#include "qapi/qapi-commands-char.h"
#include "qapi/qmp/qerror.h"
#include "sysemu/replay.h"
#include "qemu/help_option.h"
#include "qemu/option.h"
#include "chardev/char-mux.h"
/***********************************************************/
/* character device */
static Object *get_chardevs_root(void)
{
return container_get(object_get_root(), "/chardevs");
}
static void chr_be_event(Chardev *s, int event)
{
CharBackend *be = s->be;
if (!be || !be->chr_event) {
return;
}
be->chr_event(be->opaque, event);
}
void qemu_chr_be_event(Chardev *s, int event)
{
/* Keep track if the char device is open */
switch (event) {
case CHR_EVENT_OPENED:
s->be_open = 1;
break;
case CHR_EVENT_CLOSED:
s->be_open = 0;
break;
}
CHARDEV_GET_CLASS(s)->chr_be_event(s, event);
}
qemu-char: add logfile facility to all chardev backends Typically a UNIX guest OS will log boot messages to a serial port in addition to any graphical console. An admin user may also wish to use the serial port for an interactive console. A virtualization management system may wish to collect system boot messages by logging the serial port, but also wish to allow admins interactive access. Currently providing such a feature forces the mgmt app to either provide 2 separate serial ports, one for logging boot messages and one for interactive console login, or to proxy all output via a separate service that can multiplex the two needs onto one serial port. While both are valid approaches, they each have their own downsides. The former causes confusion and extra setup work for VM admins creating disk images. The latter places an extra burden to re-implement much of the QEMU chardev backends logic in libvirt or even higher level mgmt apps and adds extra hops in the data transfer path. A simpler approach that is satisfactory for many use cases is to allow the QEMU chardev backends to have a "logfile" property associated with them. $QEMU -chardev socket,host=localhost,port=9000,\ server=on,nowait,id-charserial0,\ logfile=/var/log/libvirt/qemu/test-serial0.log -device isa-serial,chardev=charserial0,id=serial0 This patch introduces a 'ChardevCommon' struct which is setup as a base for all the ChardevBackend types. Ideally this would be registered directly as a base against ChardevBackend, rather than each type, but the QAPI generator doesn't allow that since the ChardevBackend is a non-discriminated union. The ChardevCommon struct provides the optional 'logfile' parameter, as well as 'logappend' which controls whether QEMU truncates or appends (default truncate). Signed-off-by: Daniel P. Berrange <berrange@redhat.com> Message-Id: <1452516281-27519-1-git-send-email-berrange@redhat.com> [Call qemu_chr_parse_common if cd->parse is NULL. - Paolo] Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2016-01-11 13:44:41 +01:00
/* Not reporting errors from writing to logfile, as logs are
* defined to be "best effort" only */
static void qemu_chr_write_log(Chardev *s, const uint8_t *buf, size_t len)
qemu-char: add logfile facility to all chardev backends Typically a UNIX guest OS will log boot messages to a serial port in addition to any graphical console. An admin user may also wish to use the serial port for an interactive console. A virtualization management system may wish to collect system boot messages by logging the serial port, but also wish to allow admins interactive access. Currently providing such a feature forces the mgmt app to either provide 2 separate serial ports, one for logging boot messages and one for interactive console login, or to proxy all output via a separate service that can multiplex the two needs onto one serial port. While both are valid approaches, they each have their own downsides. The former causes confusion and extra setup work for VM admins creating disk images. The latter places an extra burden to re-implement much of the QEMU chardev backends logic in libvirt or even higher level mgmt apps and adds extra hops in the data transfer path. A simpler approach that is satisfactory for many use cases is to allow the QEMU chardev backends to have a "logfile" property associated with them. $QEMU -chardev socket,host=localhost,port=9000,\ server=on,nowait,id-charserial0,\ logfile=/var/log/libvirt/qemu/test-serial0.log -device isa-serial,chardev=charserial0,id=serial0 This patch introduces a 'ChardevCommon' struct which is setup as a base for all the ChardevBackend types. Ideally this would be registered directly as a base against ChardevBackend, rather than each type, but the QAPI generator doesn't allow that since the ChardevBackend is a non-discriminated union. The ChardevCommon struct provides the optional 'logfile' parameter, as well as 'logappend' which controls whether QEMU truncates or appends (default truncate). Signed-off-by: Daniel P. Berrange <berrange@redhat.com> Message-Id: <1452516281-27519-1-git-send-email-berrange@redhat.com> [Call qemu_chr_parse_common if cd->parse is NULL. - Paolo] Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2016-01-11 13:44:41 +01:00
{
size_t done = 0;
ssize_t ret;
if (s->logfd < 0) {
return;
}
while (done < len) {
retry:
ret = write(s->logfd, buf + done, len - done);
if (ret == -1 && errno == EAGAIN) {
g_usleep(100);
goto retry;
}
qemu-char: add logfile facility to all chardev backends Typically a UNIX guest OS will log boot messages to a serial port in addition to any graphical console. An admin user may also wish to use the serial port for an interactive console. A virtualization management system may wish to collect system boot messages by logging the serial port, but also wish to allow admins interactive access. Currently providing such a feature forces the mgmt app to either provide 2 separate serial ports, one for logging boot messages and one for interactive console login, or to proxy all output via a separate service that can multiplex the two needs onto one serial port. While both are valid approaches, they each have their own downsides. The former causes confusion and extra setup work for VM admins creating disk images. The latter places an extra burden to re-implement much of the QEMU chardev backends logic in libvirt or even higher level mgmt apps and adds extra hops in the data transfer path. A simpler approach that is satisfactory for many use cases is to allow the QEMU chardev backends to have a "logfile" property associated with them. $QEMU -chardev socket,host=localhost,port=9000,\ server=on,nowait,id-charserial0,\ logfile=/var/log/libvirt/qemu/test-serial0.log -device isa-serial,chardev=charserial0,id=serial0 This patch introduces a 'ChardevCommon' struct which is setup as a base for all the ChardevBackend types. Ideally this would be registered directly as a base against ChardevBackend, rather than each type, but the QAPI generator doesn't allow that since the ChardevBackend is a non-discriminated union. The ChardevCommon struct provides the optional 'logfile' parameter, as well as 'logappend' which controls whether QEMU truncates or appends (default truncate). Signed-off-by: Daniel P. Berrange <berrange@redhat.com> Message-Id: <1452516281-27519-1-git-send-email-berrange@redhat.com> [Call qemu_chr_parse_common if cd->parse is NULL. - Paolo] Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2016-01-11 13:44:41 +01:00
if (ret <= 0) {
return;
}
done += ret;
}
}
static int qemu_chr_write_buffer(Chardev *s,
const uint8_t *buf, int len,
int *offset, bool write_all)
{
ChardevClass *cc = CHARDEV_GET_CLASS(s);
int res = 0;
*offset = 0;
qemu_mutex_lock(&s->chr_write_lock);
while (*offset < len) {
retry:
res = cc->chr_write(s, buf + *offset, len - *offset);
if (res < 0 && errno == EAGAIN && write_all) {
g_usleep(100);
goto retry;
}
if (res <= 0) {
break;
}
*offset += res;
if (!write_all) {
break;
}
}
if (*offset > 0) {
qemu_chr_write_log(s, buf, *offset);
}
qemu_mutex_unlock(&s->chr_write_lock);
return res;
}
int qemu_chr_write(Chardev *s, const uint8_t *buf, int len, bool write_all)
{
int offset = 0;
int res;
if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_PLAY) {
replay_char_write_event_load(&res, &offset);
assert(offset <= len);
qemu_chr_write_buffer(s, buf, offset, &offset, true);
return res;
}
res = qemu_chr_write_buffer(s, buf, len, &offset, write_all);
if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) {
replay_char_write_event_save(res, offset);
}
if (res < 0) {
return res;
}
return offset;
}
int qemu_chr_be_can_write(Chardev *s)
{
CharBackend *be = s->be;
if (!be || !be->chr_can_read) {
return 0;
}
return be->chr_can_read(be->opaque);
}
void qemu_chr_be_write_impl(Chardev *s, uint8_t *buf, int len)
{
CharBackend *be = s->be;
if (be && be->chr_read) {
be->chr_read(be->opaque, buf, len);
}
}
void qemu_chr_be_write(Chardev *s, uint8_t *buf, int len)
{
if (qemu_chr_replay(s)) {
if (replay_mode == REPLAY_MODE_PLAY) {
return;
}
replay_chr_be_write(s, buf, len);
} else {
qemu_chr_be_write_impl(s, buf, len);
}
}
void qemu_chr_be_update_read_handlers(Chardev *s,
GMainContext *context)
{
ChardevClass *cc = CHARDEV_GET_CLASS(s);
s->gcontext = context;
if (cc->chr_update_read_handler) {
cc->chr_update_read_handler(s);
}
}
int qemu_chr_add_client(Chardev *s, int fd)
{
return CHARDEV_GET_CLASS(s)->chr_add_client ?
CHARDEV_GET_CLASS(s)->chr_add_client(s, fd) : -1;
}
static void qemu_char_open(Chardev *chr, ChardevBackend *backend,
bool *be_opened, Error **errp)
{
ChardevClass *cc = CHARDEV_GET_CLASS(chr);
/* Any ChardevCommon member would work */
ChardevCommon *common = backend ? backend->u.null.data : NULL;
if (common && common->has_logfile) {
int flags = O_WRONLY | O_CREAT;
if (common->has_logappend &&
common->logappend) {
flags |= O_APPEND;
} else {
flags |= O_TRUNC;
}
chr->logfd = qemu_open(common->logfile, flags, 0666);
if (chr->logfd < 0) {
error_setg_errno(errp, errno,
"Unable to open logfile %s",
common->logfile);
return;
}
}
if (cc->open) {
cc->open(chr, backend, be_opened, errp);
}
}
static void char_init(Object *obj)
{
Chardev *chr = CHARDEV(obj);
chr->logfd = -1;
qemu_mutex_init(&chr->chr_write_lock);
}
static int null_chr_write(Chardev *chr, const uint8_t *buf, int len)
{
return len;
}
static void char_class_init(ObjectClass *oc, void *data)
{
ChardevClass *cc = CHARDEV_CLASS(oc);
cc->chr_write = null_chr_write;
cc->chr_be_event = chr_be_event;
}
static void char_finalize(Object *obj)
{
Chardev *chr = CHARDEV(obj);
if (chr->be) {
chr->be->chr = NULL;
}
g_free(chr->filename);
g_free(chr->label);
if (chr->logfd != -1) {
close(chr->logfd);
qemu-char: add logfile facility to all chardev backends Typically a UNIX guest OS will log boot messages to a serial port in addition to any graphical console. An admin user may also wish to use the serial port for an interactive console. A virtualization management system may wish to collect system boot messages by logging the serial port, but also wish to allow admins interactive access. Currently providing such a feature forces the mgmt app to either provide 2 separate serial ports, one for logging boot messages and one for interactive console login, or to proxy all output via a separate service that can multiplex the two needs onto one serial port. While both are valid approaches, they each have their own downsides. The former causes confusion and extra setup work for VM admins creating disk images. The latter places an extra burden to re-implement much of the QEMU chardev backends logic in libvirt or even higher level mgmt apps and adds extra hops in the data transfer path. A simpler approach that is satisfactory for many use cases is to allow the QEMU chardev backends to have a "logfile" property associated with them. $QEMU -chardev socket,host=localhost,port=9000,\ server=on,nowait,id-charserial0,\ logfile=/var/log/libvirt/qemu/test-serial0.log -device isa-serial,chardev=charserial0,id=serial0 This patch introduces a 'ChardevCommon' struct which is setup as a base for all the ChardevBackend types. Ideally this would be registered directly as a base against ChardevBackend, rather than each type, but the QAPI generator doesn't allow that since the ChardevBackend is a non-discriminated union. The ChardevCommon struct provides the optional 'logfile' parameter, as well as 'logappend' which controls whether QEMU truncates or appends (default truncate). Signed-off-by: Daniel P. Berrange <berrange@redhat.com> Message-Id: <1452516281-27519-1-git-send-email-berrange@redhat.com> [Call qemu_chr_parse_common if cd->parse is NULL. - Paolo] Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2016-01-11 13:44:41 +01:00
}
qemu_mutex_destroy(&chr->chr_write_lock);
}
static const TypeInfo char_type_info = {
.name = TYPE_CHARDEV,
.parent = TYPE_OBJECT,
.instance_size = sizeof(Chardev),
.instance_init = char_init,
.instance_finalize = char_finalize,
.abstract = true,
.class_size = sizeof(ChardevClass),
.class_init = char_class_init,
};
static int chardev_machine_done_notify_one(Object *child, void *opaque)
{
Chardev *chr = (Chardev *)child;
ChardevClass *class = CHARDEV_GET_CLASS(chr);
if (class->chr_machine_done) {
return class->chr_machine_done(chr);
}
return 0;
}
static void chardev_machine_done_hook(Notifier *notifier, void *unused)
{
int ret = object_child_foreach(get_chardevs_root(),
chardev_machine_done_notify_one, NULL);
if (ret) {
error_report("Failed to call chardev machine_done hooks");
exit(1);
}
}
static Notifier chardev_machine_done_notify = {
.notify = chardev_machine_done_hook,
};
static bool qemu_chr_is_busy(Chardev *s)
{
if (CHARDEV_IS_MUX(s)) {
MuxChardev *d = MUX_CHARDEV(s);
return d->mux_cnt >= 0;
} else {
return s->be != NULL;
}
}
int qemu_chr_wait_connected(Chardev *chr, Error **errp)
{
ChardevClass *cc = CHARDEV_GET_CLASS(chr);
if (cc->chr_wait_connected) {
return cc->chr_wait_connected(chr, errp);
}
return 0;
}
QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
{
char host[65], port[33], width[8], height[8];
int pos;
const char *p;
QemuOpts *opts;
Error *local_err = NULL;
opts = qemu_opts_create(qemu_find_opts("chardev"), label, 1, &local_err);
if (local_err) {
error_report_err(local_err);
return NULL;
}
if (strstart(filename, "mon:", &p)) {
filename = p;
qemu_opt_set(opts, "mux", "on", &error_abort);
if (strcmp(filename, "stdio") == 0) {
/* Monitor is muxed to stdio: do not exit on Ctrl+C by default
* but pass it to the guest. Handle this only for compat syntax,
* for -chardev syntax we have special option for this.
* This is what -nographic did, redirecting+muxing serial+monitor
* to stdio causing Ctrl+C to be passed to guest. */
qemu_opt_set(opts, "signal", "off", &error_abort);
}
}
if (strcmp(filename, "null") == 0 ||
strcmp(filename, "pty") == 0 ||
strcmp(filename, "msmouse") == 0 ||
strcmp(filename, "wctablet") == 0 ||
strcmp(filename, "braille") == 0 ||
strcmp(filename, "testdev") == 0 ||
strcmp(filename, "stdio") == 0) {
qemu_opt_set(opts, "backend", filename, &error_abort);
return opts;
}
if (strstart(filename, "vc", &p)) {
qemu_opt_set(opts, "backend", "vc", &error_abort);
if (*p == ':') {
if (sscanf(p+1, "%7[0-9]x%7[0-9]", width, height) == 2) {
/* pixels */
qemu_opt_set(opts, "width", width, &error_abort);
qemu_opt_set(opts, "height", height, &error_abort);
} else if (sscanf(p+1, "%7[0-9]Cx%7[0-9]C", width, height) == 2) {
/* chars */
qemu_opt_set(opts, "cols", width, &error_abort);
qemu_opt_set(opts, "rows", height, &error_abort);
} else {
goto fail;
}
}
return opts;
}
if (strcmp(filename, "con:") == 0) {
qemu_opt_set(opts, "backend", "console", &error_abort);
return opts;
}
if (strstart(filename, "COM", NULL)) {
qemu_opt_set(opts, "backend", "serial", &error_abort);
qemu_opt_set(opts, "path", filename, &error_abort);
return opts;
}
if (strstart(filename, "file:", &p)) {
qemu_opt_set(opts, "backend", "file", &error_abort);
qemu_opt_set(opts, "path", p, &error_abort);
return opts;
}
if (strstart(filename, "pipe:", &p)) {
qemu_opt_set(opts, "backend", "pipe", &error_abort);
qemu_opt_set(opts, "path", p, &error_abort);
return opts;
}
if (strstart(filename, "tcp:", &p) ||
strstart(filename, "telnet:", &p) ||
strstart(filename, "tn3270:", &p)) {
if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) {
host[0] = 0;
if (sscanf(p, ":%32[^,]%n", port, &pos) < 1)
goto fail;
}
qemu_opt_set(opts, "backend", "socket", &error_abort);
qemu_opt_set(opts, "host", host, &error_abort);
qemu_opt_set(opts, "port", port, &error_abort);
if (p[pos] == ',') {
qemu_opts_do_parse(opts, p+pos+1, NULL, &local_err);
if (local_err) {
error_report_err(local_err);
goto fail;
}
}
if (strstart(filename, "telnet:", &p)) {
qemu_opt_set(opts, "telnet", "on", &error_abort);
} else if (strstart(filename, "tn3270:", &p)) {
qemu_opt_set(opts, "tn3270", "on", &error_abort);
}
return opts;
}
if (strstart(filename, "udp:", &p)) {
qemu_opt_set(opts, "backend", "udp", &error_abort);
if (sscanf(p, "%64[^:]:%32[^@,]%n", host, port, &pos) < 2) {
host[0] = 0;
if (sscanf(p, ":%32[^@,]%n", port, &pos) < 1) {
goto fail;
}
}
qemu_opt_set(opts, "host", host, &error_abort);
qemu_opt_set(opts, "port", port, &error_abort);
if (p[pos] == '@') {
p += pos + 1;
if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) {
host[0] = 0;
if (sscanf(p, ":%32[^,]%n", port, &pos) < 1) {
goto fail;
}
}
qemu_opt_set(opts, "localaddr", host, &error_abort);
qemu_opt_set(opts, "localport", port, &error_abort);
}
return opts;
}
if (strstart(filename, "unix:", &p)) {
qemu_opt_set(opts, "backend", "socket", &error_abort);
qemu_opts_do_parse(opts, p, "path", &local_err);
if (local_err) {
error_report_err(local_err);
goto fail;
}
return opts;
}
if (strstart(filename, "/dev/parport", NULL) ||
strstart(filename, "/dev/ppi", NULL)) {
qemu_opt_set(opts, "backend", "parallel", &error_abort);
qemu_opt_set(opts, "path", filename, &error_abort);
return opts;
}
if (strstart(filename, "/dev/", NULL)) {
qemu_opt_set(opts, "backend", "serial", &error_abort);
qemu_opt_set(opts, "path", filename, &error_abort);
return opts;
}
fail:
qemu_opts_del(opts);
return NULL;
}
void qemu_chr_parse_common(QemuOpts *opts, ChardevCommon *backend)
qemu-char: add logfile facility to all chardev backends Typically a UNIX guest OS will log boot messages to a serial port in addition to any graphical console. An admin user may also wish to use the serial port for an interactive console. A virtualization management system may wish to collect system boot messages by logging the serial port, but also wish to allow admins interactive access. Currently providing such a feature forces the mgmt app to either provide 2 separate serial ports, one for logging boot messages and one for interactive console login, or to proxy all output via a separate service that can multiplex the two needs onto one serial port. While both are valid approaches, they each have their own downsides. The former causes confusion and extra setup work for VM admins creating disk images. The latter places an extra burden to re-implement much of the QEMU chardev backends logic in libvirt or even higher level mgmt apps and adds extra hops in the data transfer path. A simpler approach that is satisfactory for many use cases is to allow the QEMU chardev backends to have a "logfile" property associated with them. $QEMU -chardev socket,host=localhost,port=9000,\ server=on,nowait,id-charserial0,\ logfile=/var/log/libvirt/qemu/test-serial0.log -device isa-serial,chardev=charserial0,id=serial0 This patch introduces a 'ChardevCommon' struct which is setup as a base for all the ChardevBackend types. Ideally this would be registered directly as a base against ChardevBackend, rather than each type, but the QAPI generator doesn't allow that since the ChardevBackend is a non-discriminated union. The ChardevCommon struct provides the optional 'logfile' parameter, as well as 'logappend' which controls whether QEMU truncates or appends (default truncate). Signed-off-by: Daniel P. Berrange <berrange@redhat.com> Message-Id: <1452516281-27519-1-git-send-email-berrange@redhat.com> [Call qemu_chr_parse_common if cd->parse is NULL. - Paolo] Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2016-01-11 13:44:41 +01:00
{
const char *logfile = qemu_opt_get(opts, "logfile");
backend->has_logfile = logfile != NULL;
backend->logfile = g_strdup(logfile);
qemu-char: add logfile facility to all chardev backends Typically a UNIX guest OS will log boot messages to a serial port in addition to any graphical console. An admin user may also wish to use the serial port for an interactive console. A virtualization management system may wish to collect system boot messages by logging the serial port, but also wish to allow admins interactive access. Currently providing such a feature forces the mgmt app to either provide 2 separate serial ports, one for logging boot messages and one for interactive console login, or to proxy all output via a separate service that can multiplex the two needs onto one serial port. While both are valid approaches, they each have their own downsides. The former causes confusion and extra setup work for VM admins creating disk images. The latter places an extra burden to re-implement much of the QEMU chardev backends logic in libvirt or even higher level mgmt apps and adds extra hops in the data transfer path. A simpler approach that is satisfactory for many use cases is to allow the QEMU chardev backends to have a "logfile" property associated with them. $QEMU -chardev socket,host=localhost,port=9000,\ server=on,nowait,id-charserial0,\ logfile=/var/log/libvirt/qemu/test-serial0.log -device isa-serial,chardev=charserial0,id=serial0 This patch introduces a 'ChardevCommon' struct which is setup as a base for all the ChardevBackend types. Ideally this would be registered directly as a base against ChardevBackend, rather than each type, but the QAPI generator doesn't allow that since the ChardevBackend is a non-discriminated union. The ChardevCommon struct provides the optional 'logfile' parameter, as well as 'logappend' which controls whether QEMU truncates or appends (default truncate). Signed-off-by: Daniel P. Berrange <berrange@redhat.com> Message-Id: <1452516281-27519-1-git-send-email-berrange@redhat.com> [Call qemu_chr_parse_common if cd->parse is NULL. - Paolo] Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2016-01-11 13:44:41 +01:00
backend->has_logappend = true;
backend->logappend = qemu_opt_get_bool(opts, "logappend", false);
}
static const ChardevClass *char_get_class(const char *driver, Error **errp)
{
ObjectClass *oc;
const ChardevClass *cc;
char *typename = g_strdup_printf("chardev-%s", driver);
oc = object_class_by_name(typename);
g_free(typename);
if (!object_class_dynamic_cast(oc, TYPE_CHARDEV)) {
error_setg(errp, "'%s' is not a valid char driver name", driver);
return NULL;
}
if (object_class_is_abstract(oc)) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "driver",
"abstract device type");
return NULL;
}
cc = CHARDEV_CLASS(oc);
if (cc->internal) {
error_setg(errp, "'%s' is not a valid char driver name", driver);
return NULL;
}
return cc;
}
static const struct ChardevAlias {
const char *typename;
const char *alias;
} chardev_alias_table[] = {
#ifdef HAVE_CHARDEV_PARPORT
{ "parallel", "parport" },
#endif
#ifdef HAVE_CHARDEV_SERIAL
{ "serial", "tty" },
#endif
};
typedef struct ChadevClassFE {
void (*fn)(const char *name, void *opaque);
void *opaque;
} ChadevClassFE;
static void
chardev_class_foreach(ObjectClass *klass, void *opaque)
{
ChadevClassFE *fe = opaque;
assert(g_str_has_prefix(object_class_get_name(klass), "chardev-"));
if (CHARDEV_CLASS(klass)->internal) {
return;
}
fe->fn(object_class_get_name(klass) + 8, fe->opaque);
}
static void
chardev_name_foreach(void (*fn)(const char *name, void *opaque), void *opaque)
{
ChadevClassFE fe = { .fn = fn, .opaque = opaque };
int i;
object_class_foreach(chardev_class_foreach, TYPE_CHARDEV, false, &fe);
for (i = 0; i < (int)ARRAY_SIZE(chardev_alias_table); i++) {
fn(chardev_alias_table[i].alias, opaque);
}
}
static void
help_string_append(const char *name, void *opaque)
{
GString *str = opaque;
g_string_append_printf(str, "\n%s", name);
}
static const char *chardev_alias_translate(const char *name)
{
int i;
for (i = 0; i < (int)ARRAY_SIZE(chardev_alias_table); i++) {
if (g_strcmp0(chardev_alias_table[i].alias, name) == 0) {
return chardev_alias_table[i].typename;
}
}
return name;
}
ChardevBackend *qemu_chr_parse_opts(QemuOpts *opts, Error **errp)
{
Error *local_err = NULL;
const ChardevClass *cc;
ChardevBackend *backend = NULL;
const char *name = chardev_alias_translate(qemu_opt_get(opts, "backend"));
if (name == NULL) {
error_setg(errp, "chardev: \"%s\" missing backend",
qemu_opts_id(opts));
return NULL;
}
cc = char_get_class(name, errp);
if (cc == NULL) {
return NULL;
}
backend = g_new0(ChardevBackend, 1);
backend->type = CHARDEV_BACKEND_KIND_NULL;
if (cc->parse) {
cc->parse(opts, backend, &local_err);
if (local_err) {
error_propagate(errp, local_err);
qapi_free_ChardevBackend(backend);
return NULL;
}
} else {
ChardevCommon *ccom = g_new0(ChardevCommon, 1);
qemu_chr_parse_common(opts, ccom);
backend->u.null.data = ccom; /* Any ChardevCommon member would work */
}
return backend;
}
Chardev *qemu_chr_new_from_opts(QemuOpts *opts, Error **errp)
{
const ChardevClass *cc;
Chardev *chr = NULL;
ChardevBackend *backend = NULL;
const char *name = chardev_alias_translate(qemu_opt_get(opts, "backend"));
const char *id = qemu_opts_id(opts);
char *bid = NULL;
if (name && is_help_option(name)) {
GString *str = g_string_new("");
chardev_name_foreach(help_string_append, str);
error_report("Available chardev backend types: %s", str->str);
g_string_free(str, true);
return NULL;
}
if (id == NULL) {
error_setg(errp, "chardev: no id specified");
return NULL;
}
backend = qemu_chr_parse_opts(opts, errp);
if (backend == NULL) {
return NULL;
}
cc = char_get_class(name, errp);
if (cc == NULL) {
goto out;
}
if (qemu_opt_get_bool(opts, "mux", 0)) {
bid = g_strdup_printf("%s-base", id);
}
chr = qemu_chardev_new(bid ? bid : id,
object_class_get_name(OBJECT_CLASS(cc)),
backend, errp);
if (chr == NULL) {
goto out;
}
if (bid) {
Chardev *mux;
qapi_free_ChardevBackend(backend);
backend = g_new0(ChardevBackend, 1);
backend->type = CHARDEV_BACKEND_KIND_MUX;
backend->u.mux.data = g_new0(ChardevMux, 1);
qapi: Don't special-case simple union wrappers Simple unions were carrying a special case that hid their 'data' QMP member from the resulting C struct, via the hack method QAPISchemaObjectTypeVariant.simple_union_type(). But by using the work we started by unboxing flat union and alternate branches, coupled with the ability to visit the members of an implicit type, we can now expose the simple union's implicit type in qapi-types.h: | struct q_obj_ImageInfoSpecificQCow2_wrapper { | ImageInfoSpecificQCow2 *data; | }; | | struct q_obj_ImageInfoSpecificVmdk_wrapper { | ImageInfoSpecificVmdk *data; | }; ... | struct ImageInfoSpecific { | ImageInfoSpecificKind type; | union { /* union tag is @type */ | void *data; |- ImageInfoSpecificQCow2 *qcow2; |- ImageInfoSpecificVmdk *vmdk; |+ q_obj_ImageInfoSpecificQCow2_wrapper qcow2; |+ q_obj_ImageInfoSpecificVmdk_wrapper vmdk; | } u; | }; Doing this removes asymmetry between QAPI's QMP side and its C side (both sides now expose 'data'), and means that the treatment of a simple union as sugar for a flat union is now equivalent in both languages (previously the two approaches used a different layer of dereferencing, where the simple union could be converted to a flat union with equivalent C layout but different {} on the wire, or to an equivalent QMP wire form but with different C representation). Using the implicit type also lets us get rid of the simple_union_type() hack. Of course, now all clients of simple unions have to adjust from using su->u.member to using su->u.member.data; while this touches a number of files in the tree, some earlier cleanup patches helped minimize the change to the initialization of a temporary variable rather than every single member access. The generated qapi-visit.c code is also affected by the layout change: |@@ -7393,10 +7393,10 @@ void visit_type_ImageInfoSpecific_member | } | switch (obj->type) { | case IMAGE_INFO_SPECIFIC_KIND_QCOW2: |- visit_type_ImageInfoSpecificQCow2(v, "data", &obj->u.qcow2, &err); |+ visit_type_q_obj_ImageInfoSpecificQCow2_wrapper_members(v, &obj->u.qcow2, &err); | break; | case IMAGE_INFO_SPECIFIC_KIND_VMDK: |- visit_type_ImageInfoSpecificVmdk(v, "data", &obj->u.vmdk, &err); |+ visit_type_q_obj_ImageInfoSpecificVmdk_wrapper_members(v, &obj->u.vmdk, &err); | break; | default: | abort(); Signed-off-by: Eric Blake <eblake@redhat.com> Message-Id: <1458254921-17042-13-git-send-email-eblake@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-03-17 23:48:37 +01:00
backend->u.mux.data->chardev = g_strdup(bid);
mux = qemu_chardev_new(id, TYPE_CHARDEV_MUX, backend, errp);
if (mux == NULL) {
object_unparent(OBJECT(chr));
chr = NULL;
goto out;
}
chr = mux;
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
}
out:
qapi_free_ChardevBackend(backend);
g_free(bid);
return chr;
}
Chardev *qemu_chr_new_noreplay(const char *label, const char *filename)
{
const char *p;
Chardev *chr;
QemuOpts *opts;
Error *err = NULL;
if (strstart(filename, "chardev:", &p)) {
return qemu_chr_find(p);
}
opts = qemu_chr_parse_compat(label, filename);
if (!opts)
return NULL;
chr = qemu_chr_new_from_opts(opts, &err);
if (err) {
error_report_err(err);
}
if (chr && qemu_opt_get_bool(opts, "mux", 0)) {
monitor_init(chr, MONITOR_USE_READLINE);
}
net: don't poke at chardev internal QemuOpts The vhost-user & colo code is poking at the QemuOpts instance in the CharDriverState struct, not realizing that it is valid for this to be NULL. e.g. the following crash shows a codepath where it will be NULL: Program terminated with signal SIGSEGV, Segmentation fault. #0 0x000055baf6ab4adc in qemu_opt_foreach (opts=0x0, func=0x55baf696b650 <net_vhost_chardev_opts>, opaque=0x7ffc51368c00, errp=0x7ffc51368e48) at util/qemu-option.c:617 617 QTAILQ_FOREACH(opt, &opts->head, next) { [Current thread is 1 (Thread 0x7f1d4970bb40 (LWP 6603))] (gdb) bt #0 0x000055baf6ab4adc in qemu_opt_foreach (opts=0x0, func=0x55baf696b650 <net_vhost_chardev_opts>, opaque=0x7ffc51368c00, errp=0x7ffc51368e48) at util/qemu-option.c:617 #1 0x000055baf696b7da in net_vhost_parse_chardev (opts=0x55baf8ff9260, errp=0x7ffc51368e48) at net/vhost-user.c:314 #2 0x000055baf696b985 in net_init_vhost_user (netdev=0x55baf8ff9250, name=0x55baf879d270 "hostnet2", peer=0x0, errp=0x7ffc51368e48) at net/vhost-user.c:360 #3 0x000055baf6960216 in net_client_init1 (object=0x55baf8ff9250, is_netdev=true, errp=0x7ffc51368e48) at net/net.c:1051 #4 0x000055baf6960518 in net_client_init (opts=0x55baf776e7e0, is_netdev=true, errp=0x7ffc51368f00) at net/net.c:1108 #5 0x000055baf696083f in netdev_add (opts=0x55baf776e7e0, errp=0x7ffc51368f00) at net/net.c:1186 #6 0x000055baf69608c7 in qmp_netdev_add (qdict=0x55baf7afaf60, ret=0x7ffc51368f50, errp=0x7ffc51368f48) at net/net.c:1205 #7 0x000055baf6622135 in handle_qmp_command (parser=0x55baf77fb590, tokens=0x7f1d24011960) at /path/to/qemu.git/monitor.c:3978 #8 0x000055baf6a9d099 in json_message_process_token (lexer=0x55baf77fb598, input=0x55baf75acd20, type=JSON_RCURLY, x=113, y=19) at qobject/json-streamer.c:105 #9 0x000055baf6abf7aa in json_lexer_feed_char (lexer=0x55baf77fb598, ch=125 '}', flush=false) at qobject/json-lexer.c:319 #10 0x000055baf6abf8f2 in json_lexer_feed (lexer=0x55baf77fb598, buffer=0x7ffc51369170 "}R\204\367\272U", size=1) at qobject/json-lexer.c:369 #11 0x000055baf6a9d13c in json_message_parser_feed (parser=0x55baf77fb590, buffer=0x7ffc51369170 "}R\204\367\272U", size=1) at qobject/json-streamer.c:124 #12 0x000055baf66221f7 in monitor_qmp_read (opaque=0x55baf77fb530, buf=0x7ffc51369170 "}R\204\367\272U", size=1) at /path/to/qemu.git/monitor.c:3994 #13 0x000055baf6757014 in qemu_chr_be_write_impl (s=0x55baf7610a40, buf=0x7ffc51369170 "}R\204\367\272U", len=1) at qemu-char.c:387 #14 0x000055baf6757076 in qemu_chr_be_write (s=0x55baf7610a40, buf=0x7ffc51369170 "}R\204\367\272U", len=1) at qemu-char.c:399 #15 0x000055baf675b3b0 in tcp_chr_read (chan=0x55baf90244b0, cond=G_IO_IN, opaque=0x55baf7610a40) at qemu-char.c:2927 #16 0x000055baf6a5d655 in qio_channel_fd_source_dispatch (source=0x55baf7610df0, callback=0x55baf675b25a <tcp_chr_read>, user_data=0x55baf7610a40) at io/channel-watch.c:84 #17 0x00007f1d3e80cbbd in g_main_context_dispatch () from /usr/lib64/libglib-2.0.so.0 #18 0x000055baf69d3720 in glib_pollfds_poll () at main-loop.c:213 #19 0x000055baf69d37fd in os_host_main_loop_wait (timeout=126000000) at main-loop.c:258 #20 0x000055baf69d38ad in main_loop_wait (nonblocking=0) at main-loop.c:506 #21 0x000055baf676587b in main_loop () at vl.c:1908 #22 0x000055baf676d3bf in main (argc=101, argv=0x7ffc5136a6c8, envp=0x7ffc5136a9f8) at vl.c:4604 (gdb) p opts $1 = (QemuOpts *) 0x0 The crash occurred when attaching vhost-user net via QMP: { "execute": "chardev-add", "arguments": { "id": "charnet2", "backend": { "type": "socket", "data": { "addr": { "type": "unix", "data": { "path": "/var/run/openvswitch/vhost-user1" } }, "wait": false, "server": false } } }, "id": "libvirt-19" } { "return": { }, "id": "libvirt-19" } { "execute": "netdev_add", "arguments": { "type": "vhost-user", "chardev": "charnet2", "id": "hostnet2" }, "id": "libvirt-20" } Code using chardevs should not be poking at the internals of the CharDriverState struct. What vhost-user wants is a chardev that is operating as reconnectable network service, along with the ability to do FD passing over the connection. The colo code simply wants a network service. Add a feature concept to the char drivers so that chardev users can query the actual features they wish to have supported. The QemuOpts member is removed to prevent future mistakes in this area. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2016-10-07 14:18:34 +02:00
qemu_opts_del(opts);
return chr;
}
Chardev *qemu_chr_new(const char *label, const char *filename)
{
Chardev *chr;
chr = qemu_chr_new_noreplay(label, filename);
if (chr) {
if (replay_mode != REPLAY_MODE_NONE) {
qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_REPLAY);
}
if (qemu_chr_replay(chr) && CHARDEV_GET_CLASS(chr)->chr_ioctl) {
error_report("Replay: ioctl is not supported "
"for serial devices yet");
}
replay_register_char_driver(chr);
}
return chr;
}
static int qmp_query_chardev_foreach(Object *obj, void *data)
{
Chardev *chr = CHARDEV(obj);
ChardevInfoList **list = data;
ChardevInfoList *info = g_malloc0(sizeof(*info));
info->value = g_malloc0(sizeof(*info->value));
info->value->label = g_strdup(chr->label);
info->value->filename = g_strdup(chr->filename);
info->value->frontend_open = chr->be && chr->be->fe_open;
info->next = *list;
*list = info;
return 0;
}
ChardevInfoList *qmp_query_chardev(Error **errp)
{
ChardevInfoList *chr_list = NULL;
object_child_foreach(get_chardevs_root(),
qmp_query_chardev_foreach, &chr_list);
return chr_list;
}
static void
qmp_prepend_backend(const char *name, void *opaque)
{
ChardevBackendInfoList **list = opaque;
ChardevBackendInfoList *info = g_malloc0(sizeof(*info));
info->value = g_malloc0(sizeof(*info->value));
info->value->name = g_strdup(name);
info->next = *list;
*list = info;
}
ChardevBackendInfoList *qmp_query_chardev_backends(Error **errp)
{
ChardevBackendInfoList *backend_list = NULL;
chardev_name_foreach(qmp_prepend_backend, &backend_list);
return backend_list;
}
Chardev *qemu_chr_find(const char *name)
{
Object *obj = object_resolve_path_component(get_chardevs_root(), name);
return obj ? CHARDEV(obj) : NULL;
}
QemuOptsList qemu_chardev_opts = {
.name = "chardev",
.implied_opt_name = "backend",
.head = QTAILQ_HEAD_INITIALIZER(qemu_chardev_opts.head),
.desc = {
{
.name = "backend",
.type = QEMU_OPT_STRING,
},{
.name = "path",
.type = QEMU_OPT_STRING,
},{
.name = "host",
.type = QEMU_OPT_STRING,
},{
.name = "port",
.type = QEMU_OPT_STRING,
char: allow passing pre-opened socket file descriptor at startup When starting QEMU management apps will usually setup a monitor socket, and then open it immediately after startup. If not using QEMU's own -daemonize arg, this process can be troublesome to handle correctly. The mgmt app will need to repeatedly call connect() until it succeeds, because it does not know when QEMU has created the listener socket. If can't retry connect() forever though, because an error might have caused QEMU to exit before it even creates the monitor. The obvious way to fix this kind of problem is to just pass in a pre-opened socket file descriptor for the QEMU monitor to listen on. The management app can now immediately call connect() just once. If connect() fails it knows that QEMU has exited with an error. The SocketAddress(Legacy) structs allow for FD passing via the monitor, and now via inherited file descriptors from the process that spawned QEMU. The final missing piece is adding a 'fd' parameter in the socket chardev options. This allows both HMP usage, pass any FD number with SCM_RIGHTS, then running HMP commands: getfd myfd chardev-add socket,fd=myfd Note that numeric FDs cannot be referenced directly in HMP, only named FDs. And also CLI usage, by leak FD 3 from parent by clearing O_CLOEXEC, then spawning QEMU with -chardev socket,fd=3,id=mon -mon chardev=mon,mode=control Note that named FDs cannot be referenced in CLI args, only numeric FDs. We do not wire this up in the legacy chardev syntax, so you cannot use FD passing with '-qmp', you must use the modern '-mon' + '-chardev' pair. When passing pre-opened FDs there is a restriction on use of TLS encryption. It can be used on a server socket chardev, but cannot be used for a client socket chardev. This is because when validating a server's certificate, the client needs to have a hostname available to match against the certificate identity. An illustrative example of usage is: #!/usr/bin/perl use IO::Socket::UNIX; use Fcntl; unlink "/tmp/qmp"; my $srv = IO::Socket::UNIX->new( Type => SOCK_STREAM(), Local => "/tmp/qmp", Listen => 1, ); my $flags = fcntl $srv, F_GETFD, 0; fcntl $srv, F_SETFD, $flags & ~FD_CLOEXEC; my $fd = $srv->fileno(); exec "qemu-system-x86_64", \ "-chardev", "socket,fd=$fd,server,nowait,id=mon", \ "-mon", "chardev=mon,mode=control"; Reviewed-by: Eric Blake <eblake@redhat.com> Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2017-12-21 13:57:54 +01:00
},{
.name = "fd",
.type = QEMU_OPT_STRING,
},{
.name = "localaddr",
.type = QEMU_OPT_STRING,
},{
.name = "localport",
.type = QEMU_OPT_STRING,
},{
.name = "to",
.type = QEMU_OPT_NUMBER,
},{
.name = "ipv4",
.type = QEMU_OPT_BOOL,
},{
.name = "ipv6",
.type = QEMU_OPT_BOOL,
},{
.name = "wait",
.type = QEMU_OPT_BOOL,
},{
.name = "server",
.type = QEMU_OPT_BOOL,
},{
.name = "delay",
.type = QEMU_OPT_BOOL,
},{
.name = "reconnect",
.type = QEMU_OPT_NUMBER,
},{
.name = "telnet",
.type = QEMU_OPT_BOOL,
},{
.name = "tn3270",
.type = QEMU_OPT_BOOL,
char: introduce support for TLS encrypted TCP chardev backend This integrates support for QIOChannelTLS object in the TCP chardev backend. If the 'tls-creds=NAME' option is passed with the '-chardev tcp' argument, then it will setup the chardev such that the client is required to establish a TLS handshake when connecting. There is no support for checking the client certificate against ACLs in this initial patch. This is pending work to QOM-ify the ACL object code. A complete invocation to run QEMU as the server for a TLS encrypted serial dev might be $ qemu-system-x86_64 \ -nodefconfig -nodefaults -device sga -display none \ -chardev socket,id=s0,host=127.0.0.1,port=9000,tls-creds=tls0,server \ -device isa-serial,chardev=s0 \ -object tls-creds-x509,id=tls0,endpoint=server,verify-peer=off,\ dir=/home/berrange/security/qemutls To test with the gnutls-cli tool as the client: $ gnutls-cli --priority=NORMAL -p 9000 \ --x509cafile=/home/berrange/security/qemutls/ca-cert.pem \ 127.0.0.1 If QEMU was told to use 'anon' credential type, then use the priority string 'NORMAL:+ANON-DH' with gnutls-cli Alternatively, if setting up a chardev to operate as a client, then the TLS credentials registered must be for the client endpoint. First a TLS server must be setup, which can be done with the gnutls-serv tool $ gnutls-serv --priority=NORMAL -p 9000 --echo \ --x509cafile=/home/berrange/security/qemutls/ca-cert.pem \ --x509certfile=/home/berrange/security/qemutls/server-cert.pem \ --x509keyfile=/home/berrange/security/qemutls/server-key.pem Then QEMU can connect with $ qemu-system-x86_64 \ -nodefconfig -nodefaults -device sga -display none \ -chardev socket,id=s0,host=127.0.0.1,port=9000,tls-creds=tls0 \ -device isa-serial,chardev=s0 \ -object tls-creds-x509,id=tls0,endpoint=client,\ dir=/home/berrange/security/qemutls Signed-off-by: Daniel P. Berrange <berrange@redhat.com> Message-Id: <1453202071-10289-5-git-send-email-berrange@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2016-01-19 12:14:31 +01:00
},{
.name = "tls-creds",
.type = QEMU_OPT_STRING,
},{
.name = "width",
.type = QEMU_OPT_NUMBER,
},{
.name = "height",
.type = QEMU_OPT_NUMBER,
},{
.name = "cols",
.type = QEMU_OPT_NUMBER,
},{
.name = "rows",
.type = QEMU_OPT_NUMBER,
},{
.name = "mux",
.type = QEMU_OPT_BOOL,
},{
.name = "signal",
.type = QEMU_OPT_BOOL,
},{
.name = "name",
.type = QEMU_OPT_STRING,
},{
.name = "debug",
.type = QEMU_OPT_NUMBER,
},{
.name = "size",
.type = QEMU_OPT_SIZE,
},{
.name = "chardev",
.type = QEMU_OPT_STRING,
},{
.name = "append",
.type = QEMU_OPT_BOOL,
qemu-char: add logfile facility to all chardev backends Typically a UNIX guest OS will log boot messages to a serial port in addition to any graphical console. An admin user may also wish to use the serial port for an interactive console. A virtualization management system may wish to collect system boot messages by logging the serial port, but also wish to allow admins interactive access. Currently providing such a feature forces the mgmt app to either provide 2 separate serial ports, one for logging boot messages and one for interactive console login, or to proxy all output via a separate service that can multiplex the two needs onto one serial port. While both are valid approaches, they each have their own downsides. The former causes confusion and extra setup work for VM admins creating disk images. The latter places an extra burden to re-implement much of the QEMU chardev backends logic in libvirt or even higher level mgmt apps and adds extra hops in the data transfer path. A simpler approach that is satisfactory for many use cases is to allow the QEMU chardev backends to have a "logfile" property associated with them. $QEMU -chardev socket,host=localhost,port=9000,\ server=on,nowait,id-charserial0,\ logfile=/var/log/libvirt/qemu/test-serial0.log -device isa-serial,chardev=charserial0,id=serial0 This patch introduces a 'ChardevCommon' struct which is setup as a base for all the ChardevBackend types. Ideally this would be registered directly as a base against ChardevBackend, rather than each type, but the QAPI generator doesn't allow that since the ChardevBackend is a non-discriminated union. The ChardevCommon struct provides the optional 'logfile' parameter, as well as 'logappend' which controls whether QEMU truncates or appends (default truncate). Signed-off-by: Daniel P. Berrange <berrange@redhat.com> Message-Id: <1452516281-27519-1-git-send-email-berrange@redhat.com> [Call qemu_chr_parse_common if cd->parse is NULL. - Paolo] Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2016-01-11 13:44:41 +01:00
},{
.name = "logfile",
.type = QEMU_OPT_STRING,
},{
.name = "logappend",
.type = QEMU_OPT_BOOL,
},
{ /* end of list */ }
},
};
bool qemu_chr_has_feature(Chardev *chr,
ChardevFeature feature)
net: don't poke at chardev internal QemuOpts The vhost-user & colo code is poking at the QemuOpts instance in the CharDriverState struct, not realizing that it is valid for this to be NULL. e.g. the following crash shows a codepath where it will be NULL: Program terminated with signal SIGSEGV, Segmentation fault. #0 0x000055baf6ab4adc in qemu_opt_foreach (opts=0x0, func=0x55baf696b650 <net_vhost_chardev_opts>, opaque=0x7ffc51368c00, errp=0x7ffc51368e48) at util/qemu-option.c:617 617 QTAILQ_FOREACH(opt, &opts->head, next) { [Current thread is 1 (Thread 0x7f1d4970bb40 (LWP 6603))] (gdb) bt #0 0x000055baf6ab4adc in qemu_opt_foreach (opts=0x0, func=0x55baf696b650 <net_vhost_chardev_opts>, opaque=0x7ffc51368c00, errp=0x7ffc51368e48) at util/qemu-option.c:617 #1 0x000055baf696b7da in net_vhost_parse_chardev (opts=0x55baf8ff9260, errp=0x7ffc51368e48) at net/vhost-user.c:314 #2 0x000055baf696b985 in net_init_vhost_user (netdev=0x55baf8ff9250, name=0x55baf879d270 "hostnet2", peer=0x0, errp=0x7ffc51368e48) at net/vhost-user.c:360 #3 0x000055baf6960216 in net_client_init1 (object=0x55baf8ff9250, is_netdev=true, errp=0x7ffc51368e48) at net/net.c:1051 #4 0x000055baf6960518 in net_client_init (opts=0x55baf776e7e0, is_netdev=true, errp=0x7ffc51368f00) at net/net.c:1108 #5 0x000055baf696083f in netdev_add (opts=0x55baf776e7e0, errp=0x7ffc51368f00) at net/net.c:1186 #6 0x000055baf69608c7 in qmp_netdev_add (qdict=0x55baf7afaf60, ret=0x7ffc51368f50, errp=0x7ffc51368f48) at net/net.c:1205 #7 0x000055baf6622135 in handle_qmp_command (parser=0x55baf77fb590, tokens=0x7f1d24011960) at /path/to/qemu.git/monitor.c:3978 #8 0x000055baf6a9d099 in json_message_process_token (lexer=0x55baf77fb598, input=0x55baf75acd20, type=JSON_RCURLY, x=113, y=19) at qobject/json-streamer.c:105 #9 0x000055baf6abf7aa in json_lexer_feed_char (lexer=0x55baf77fb598, ch=125 '}', flush=false) at qobject/json-lexer.c:319 #10 0x000055baf6abf8f2 in json_lexer_feed (lexer=0x55baf77fb598, buffer=0x7ffc51369170 "}R\204\367\272U", size=1) at qobject/json-lexer.c:369 #11 0x000055baf6a9d13c in json_message_parser_feed (parser=0x55baf77fb590, buffer=0x7ffc51369170 "}R\204\367\272U", size=1) at qobject/json-streamer.c:124 #12 0x000055baf66221f7 in monitor_qmp_read (opaque=0x55baf77fb530, buf=0x7ffc51369170 "}R\204\367\272U", size=1) at /path/to/qemu.git/monitor.c:3994 #13 0x000055baf6757014 in qemu_chr_be_write_impl (s=0x55baf7610a40, buf=0x7ffc51369170 "}R\204\367\272U", len=1) at qemu-char.c:387 #14 0x000055baf6757076 in qemu_chr_be_write (s=0x55baf7610a40, buf=0x7ffc51369170 "}R\204\367\272U", len=1) at qemu-char.c:399 #15 0x000055baf675b3b0 in tcp_chr_read (chan=0x55baf90244b0, cond=G_IO_IN, opaque=0x55baf7610a40) at qemu-char.c:2927 #16 0x000055baf6a5d655 in qio_channel_fd_source_dispatch (source=0x55baf7610df0, callback=0x55baf675b25a <tcp_chr_read>, user_data=0x55baf7610a40) at io/channel-watch.c:84 #17 0x00007f1d3e80cbbd in g_main_context_dispatch () from /usr/lib64/libglib-2.0.so.0 #18 0x000055baf69d3720 in glib_pollfds_poll () at main-loop.c:213 #19 0x000055baf69d37fd in os_host_main_loop_wait (timeout=126000000) at main-loop.c:258 #20 0x000055baf69d38ad in main_loop_wait (nonblocking=0) at main-loop.c:506 #21 0x000055baf676587b in main_loop () at vl.c:1908 #22 0x000055baf676d3bf in main (argc=101, argv=0x7ffc5136a6c8, envp=0x7ffc5136a9f8) at vl.c:4604 (gdb) p opts $1 = (QemuOpts *) 0x0 The crash occurred when attaching vhost-user net via QMP: { "execute": "chardev-add", "arguments": { "id": "charnet2", "backend": { "type": "socket", "data": { "addr": { "type": "unix", "data": { "path": "/var/run/openvswitch/vhost-user1" } }, "wait": false, "server": false } } }, "id": "libvirt-19" } { "return": { }, "id": "libvirt-19" } { "execute": "netdev_add", "arguments": { "type": "vhost-user", "chardev": "charnet2", "id": "hostnet2" }, "id": "libvirt-20" } Code using chardevs should not be poking at the internals of the CharDriverState struct. What vhost-user wants is a chardev that is operating as reconnectable network service, along with the ability to do FD passing over the connection. The colo code simply wants a network service. Add a feature concept to the char drivers so that chardev users can query the actual features they wish to have supported. The QemuOpts member is removed to prevent future mistakes in this area. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2016-10-07 14:18:34 +02:00
{
return test_bit(feature, chr->features);
}
void qemu_chr_set_feature(Chardev *chr,
ChardevFeature feature)
net: don't poke at chardev internal QemuOpts The vhost-user & colo code is poking at the QemuOpts instance in the CharDriverState struct, not realizing that it is valid for this to be NULL. e.g. the following crash shows a codepath where it will be NULL: Program terminated with signal SIGSEGV, Segmentation fault. #0 0x000055baf6ab4adc in qemu_opt_foreach (opts=0x0, func=0x55baf696b650 <net_vhost_chardev_opts>, opaque=0x7ffc51368c00, errp=0x7ffc51368e48) at util/qemu-option.c:617 617 QTAILQ_FOREACH(opt, &opts->head, next) { [Current thread is 1 (Thread 0x7f1d4970bb40 (LWP 6603))] (gdb) bt #0 0x000055baf6ab4adc in qemu_opt_foreach (opts=0x0, func=0x55baf696b650 <net_vhost_chardev_opts>, opaque=0x7ffc51368c00, errp=0x7ffc51368e48) at util/qemu-option.c:617 #1 0x000055baf696b7da in net_vhost_parse_chardev (opts=0x55baf8ff9260, errp=0x7ffc51368e48) at net/vhost-user.c:314 #2 0x000055baf696b985 in net_init_vhost_user (netdev=0x55baf8ff9250, name=0x55baf879d270 "hostnet2", peer=0x0, errp=0x7ffc51368e48) at net/vhost-user.c:360 #3 0x000055baf6960216 in net_client_init1 (object=0x55baf8ff9250, is_netdev=true, errp=0x7ffc51368e48) at net/net.c:1051 #4 0x000055baf6960518 in net_client_init (opts=0x55baf776e7e0, is_netdev=true, errp=0x7ffc51368f00) at net/net.c:1108 #5 0x000055baf696083f in netdev_add (opts=0x55baf776e7e0, errp=0x7ffc51368f00) at net/net.c:1186 #6 0x000055baf69608c7 in qmp_netdev_add (qdict=0x55baf7afaf60, ret=0x7ffc51368f50, errp=0x7ffc51368f48) at net/net.c:1205 #7 0x000055baf6622135 in handle_qmp_command (parser=0x55baf77fb590, tokens=0x7f1d24011960) at /path/to/qemu.git/monitor.c:3978 #8 0x000055baf6a9d099 in json_message_process_token (lexer=0x55baf77fb598, input=0x55baf75acd20, type=JSON_RCURLY, x=113, y=19) at qobject/json-streamer.c:105 #9 0x000055baf6abf7aa in json_lexer_feed_char (lexer=0x55baf77fb598, ch=125 '}', flush=false) at qobject/json-lexer.c:319 #10 0x000055baf6abf8f2 in json_lexer_feed (lexer=0x55baf77fb598, buffer=0x7ffc51369170 "}R\204\367\272U", size=1) at qobject/json-lexer.c:369 #11 0x000055baf6a9d13c in json_message_parser_feed (parser=0x55baf77fb590, buffer=0x7ffc51369170 "}R\204\367\272U", size=1) at qobject/json-streamer.c:124 #12 0x000055baf66221f7 in monitor_qmp_read (opaque=0x55baf77fb530, buf=0x7ffc51369170 "}R\204\367\272U", size=1) at /path/to/qemu.git/monitor.c:3994 #13 0x000055baf6757014 in qemu_chr_be_write_impl (s=0x55baf7610a40, buf=0x7ffc51369170 "}R\204\367\272U", len=1) at qemu-char.c:387 #14 0x000055baf6757076 in qemu_chr_be_write (s=0x55baf7610a40, buf=0x7ffc51369170 "}R\204\367\272U", len=1) at qemu-char.c:399 #15 0x000055baf675b3b0 in tcp_chr_read (chan=0x55baf90244b0, cond=G_IO_IN, opaque=0x55baf7610a40) at qemu-char.c:2927 #16 0x000055baf6a5d655 in qio_channel_fd_source_dispatch (source=0x55baf7610df0, callback=0x55baf675b25a <tcp_chr_read>, user_data=0x55baf7610a40) at io/channel-watch.c:84 #17 0x00007f1d3e80cbbd in g_main_context_dispatch () from /usr/lib64/libglib-2.0.so.0 #18 0x000055baf69d3720 in glib_pollfds_poll () at main-loop.c:213 #19 0x000055baf69d37fd in os_host_main_loop_wait (timeout=126000000) at main-loop.c:258 #20 0x000055baf69d38ad in main_loop_wait (nonblocking=0) at main-loop.c:506 #21 0x000055baf676587b in main_loop () at vl.c:1908 #22 0x000055baf676d3bf in main (argc=101, argv=0x7ffc5136a6c8, envp=0x7ffc5136a9f8) at vl.c:4604 (gdb) p opts $1 = (QemuOpts *) 0x0 The crash occurred when attaching vhost-user net via QMP: { "execute": "chardev-add", "arguments": { "id": "charnet2", "backend": { "type": "socket", "data": { "addr": { "type": "unix", "data": { "path": "/var/run/openvswitch/vhost-user1" } }, "wait": false, "server": false } } }, "id": "libvirt-19" } { "return": { }, "id": "libvirt-19" } { "execute": "netdev_add", "arguments": { "type": "vhost-user", "chardev": "charnet2", "id": "hostnet2" }, "id": "libvirt-20" } Code using chardevs should not be poking at the internals of the CharDriverState struct. What vhost-user wants is a chardev that is operating as reconnectable network service, along with the ability to do FD passing over the connection. The colo code simply wants a network service. Add a feature concept to the char drivers so that chardev users can query the actual features they wish to have supported. The QemuOpts member is removed to prevent future mistakes in this area. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2016-10-07 14:18:34 +02:00
{
return set_bit(feature, chr->features);
}
Chardev *qemu_chardev_new(const char *id, const char *typename,
ChardevBackend *backend,
Error **errp)
{
Object *obj;
Chardev *chr = NULL;
Error *local_err = NULL;
bool be_opened = true;
assert(g_str_has_prefix(typename, "chardev-"));
obj = object_new(typename);
chr = CHARDEV(obj);
chr->label = g_strdup(id);
qemu_char_open(chr, backend, &be_opened, &local_err);
if (local_err) {
goto end;
}
if (!chr->filename) {
chr->filename = g_strdup(typename + 8);
}
if (be_opened) {
qemu_chr_be_event(chr, CHR_EVENT_OPENED);
}
if (id) {
object_property_add_child(get_chardevs_root(), id, obj, &local_err);
if (local_err) {
goto end;
}
object_unref(obj);
}
end:
if (local_err) {
error_propagate(errp, local_err);
object_unref(obj);
return NULL;
}
return chr;
}
ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
Error **errp)
{
const ChardevClass *cc;
ChardevReturn *ret;
Chardev *chr;
cc = char_get_class(ChardevBackendKind_str(backend->type), errp);
if (!cc) {
return NULL;
}
chr = qemu_chardev_new(id, object_class_get_name(OBJECT_CLASS(cc)),
backend, errp);
if (!chr) {
return NULL;
}
ret = g_new0(ChardevReturn, 1);
if (CHARDEV_IS_PTY(chr)) {
ret->pty = g_strdup(chr->filename + 4);
ret->has_pty = true;
}
return ret;
}
ChardevReturn *qmp_chardev_change(const char *id, ChardevBackend *backend,
Error **errp)
{
CharBackend *be;
const ChardevClass *cc;
Chardev *chr, *chr_new;
bool closed_sent = false;
ChardevReturn *ret;
chr = qemu_chr_find(id);
if (!chr) {
error_setg(errp, "Chardev '%s' does not exist", id);
return NULL;
}
if (CHARDEV_IS_MUX(chr)) {
error_setg(errp, "Mux device hotswap not supported yet");
return NULL;
}
if (qemu_chr_replay(chr)) {
error_setg(errp,
"Chardev '%s' cannot be changed in record/replay mode", id);
return NULL;
}
be = chr->be;
if (!be) {
/* easy case */
object_unparent(OBJECT(chr));
return qmp_chardev_add(id, backend, errp);
}
if (!be->chr_be_change) {
error_setg(errp, "Chardev user does not support chardev hotswap");
return NULL;
}
cc = char_get_class(ChardevBackendKind_str(backend->type), errp);
if (!cc) {
return NULL;
}
chr_new = qemu_chardev_new(NULL, object_class_get_name(OBJECT_CLASS(cc)),
backend, errp);
if (!chr_new) {
return NULL;
}
chr_new->label = g_strdup(id);
if (chr->be_open && !chr_new->be_open) {
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
closed_sent = true;
}
chr->be = NULL;
qemu_chr_fe_init(be, chr_new, &error_abort);
if (be->chr_be_change(be->opaque) < 0) {
error_setg(errp, "Chardev '%s' change failed", chr_new->label);
chr_new->be = NULL;
qemu_chr_fe_init(be, chr, &error_abort);
if (closed_sent) {
qemu_chr_be_event(chr, CHR_EVENT_OPENED);
}
object_unref(OBJECT(chr_new));
return NULL;
}
object_unparent(OBJECT(chr));
object_property_add_child(get_chardevs_root(), chr_new->label,
OBJECT(chr_new), &error_abort);
object_unref(OBJECT(chr_new));
ret = g_new0(ChardevReturn, 1);
if (CHARDEV_IS_PTY(chr_new)) {
ret->pty = g_strdup(chr_new->filename + 4);
ret->has_pty = true;
}
return ret;
}
void qmp_chardev_remove(const char *id, Error **errp)
{
Chardev *chr;
chr = qemu_chr_find(id);
if (chr == NULL) {
error_setg(errp, "Chardev '%s' not found", id);
return;
}
if (qemu_chr_is_busy(chr)) {
error_setg(errp, "Chardev '%s' is busy", id);
return;
}
if (qemu_chr_replay(chr)) {
error_setg(errp,
"Chardev '%s' cannot be unplugged in record/replay mode", id);
return;
}
object_unparent(OBJECT(chr));
}
void qmp_chardev_send_break(const char *id, Error **errp)
{
Chardev *chr;
chr = qemu_chr_find(id);
if (chr == NULL) {
error_setg(errp, "Chardev '%s' not found", id);
return;
}
qemu_chr_be_event(chr, CHR_EVENT_BREAK);
}
/*
* Add a timeout callback for the chardev (in milliseconds), return
* the GSource object created. Please use this to add timeout hook for
* chardev instead of g_timeout_add() and g_timeout_add_seconds(), to
* make sure the gcontext that the task bound to is correct.
*/
GSource *qemu_chr_timeout_add_ms(Chardev *chr, guint ms,
GSourceFunc func, void *private)
{
GSource *source = g_timeout_source_new(ms);
assert(func);
g_source_set_callback(source, func, private, NULL);
g_source_attach(source, chr->gcontext);
return source;
}
char: do not use atexit cleanup handler It turns out qemu is calling exit() in various places from various threads without taking much care of resources state. The atexit() cleanup handlers cannot easily destroy resources that are in use (by the same thread or other). Since c1111a24a3, TCG arm guests run into the following abort() when running tests, the chardev mutex is locked during the write, so qemu_mutex_destroy() returns an error: #0 0x00007fffdbb806f5 in raise () at /lib64/libc.so.6 #1 0x00007fffdbb822fa in abort () at /lib64/libc.so.6 #2 0x00005555557616fe in error_exit (err=<optimized out>, msg=msg@entry=0x555555c38c30 <__func__.14622> "qemu_mutex_destroy") at /home/drjones/code/qemu/util/qemu-thread-posix.c:39 #3 0x0000555555b0be20 in qemu_mutex_destroy (mutex=mutex@entry=0x5555566aa0e0) at /home/drjones/code/qemu/util/qemu-thread-posix.c:57 #4 0x00005555558aab00 in qemu_chr_free_common (chr=0x5555566aa0e0) at /home/drjones/code/qemu/qemu-char.c:4029 #5 0x00005555558b05f9 in qemu_chr_delete (chr=<optimized out>) at /home/drjones/code/qemu/qemu-char.c:4038 #6 0x00005555558b05f9 in qemu_chr_delete (chr=<optimized out>) at /home/drjones/code/qemu/qemu-char.c:4044 #7 0x00005555558b062c in qemu_chr_cleanup () at /home/drjones/code/qemu/qemu-char.c:4557 #8 0x00007fffdbb851e8 in __run_exit_handlers () at /lib64/libc.so.6 #9 0x00007fffdbb85235 in () at /lib64/libc.so.6 #10 0x00005555558d1b39 in testdev_write (testdev=0x5555566aa0a0) at /home/drjones/code/qemu/backends/testdev.c:71 #11 0x00005555558d1b39 in testdev_write (chr=<optimized out>, buf=0x7fffc343fd9a "", len=0) at /home/drjones/code/qemu/backends/testdev.c:95 #12 0x00005555558adced in qemu_chr_fe_write (s=0x5555566aa0e0, buf=buf@entry=0x7fffc343fd98 "0q", len=len@entry=2) at /home/drjones/code/qemu/qemu-char.c:282 Instead of using a atexit() handler, only run the chardev cleanup as initially proposed at the end of main(), where there are less chances (hic) of conflicts or other races. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reported-by: Andrew Jones <drjones@redhat.com> Message-Id: <20160704153823.16879-1-marcandre.lureau@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2016-07-04 17:38:23 +02:00
void qemu_chr_cleanup(void)
{
object_unparent(get_chardevs_root());
}
static void register_types(void)
{
type_register_static(&char_type_info);
/* 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(&chardev_machine_done_notify);
}
type_init(register_types);