Merge remote-tracking branch 'bonzini/nbd-next' into staging

* bonzini/nbd-next: (30 commits)
  qmp: add NBD server commands
  block: add close notifiers
  block: prepare code for adding block notifiers
  qemu-sockets: add socket_listen, socket_connect, socket_parse
  tests: do not include tools-obj-y Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
  qemu-sockets: return InetSocketAddress from inet_parse
  qapi: add socket address types
  build: add QAPI files to the tools
  vnc: drop QERR_VNC_SERVER_FAILED
  qemu-sockets: add error propagation to Unix socket functions
  qemu-sockets: add error propagation to inet_parse
  qemu-sockets: add error propagation to inet_dgram_opts
  qemu-sockets: add error propagation to inet_connect_addr
  qemu-sockets: include strerror or gai_strerror output in error messages
  vnc: add error propagation to vnc_display_open
  vnc: reorganize code for reverse mode
  vnc: introduce a single label for error returns
  vnc: avoid Yoda conditionals
  qemu-ga: ask and print error information from qemu-sockets
  nbd: ask and print error information from qemu-sockets
  ...
This commit is contained in:
Anthony Liguori 2012-10-24 09:39:49 -05:00
commit c6b8141b84
28 changed files with 764 additions and 421 deletions

View file

@ -161,11 +161,10 @@ qemu-img.o: qemu-img-cmds.h
tools-obj-y = $(oslib-obj-y) $(trace-obj-y) qemu-tool.o qemu-timer.o \
qemu-timer-common.o main-loop.o notify.o \
iohandler.o cutils.o iov.o async.o
iohandler.o cutils.o iov.o async.o error.o
tools-obj-$(CONFIG_POSIX) += compatfd.o
qemu-img$(EXESUF): qemu-img.o $(tools-obj-y) $(block-obj-y) $(qapi-obj-y) \
qapi-visit.o qapi-types.o
qemu-img$(EXESUF): qemu-img.o $(tools-obj-y) $(block-obj-y)
qemu-nbd$(EXESUF): qemu-nbd.o $(tools-obj-y) $(block-obj-y)
qemu-io$(EXESUF): qemu-io.o cmd.o $(tools-obj-y) $(block-obj-y)

View file

@ -43,11 +43,12 @@ coroutine-obj-$(CONFIG_WIN32) += coroutine-win32.o
block-obj-y = cutils.o iov.o cache-utils.o qemu-option.o module.o async.o
block-obj-y += nbd.o block.o blockjob.o aio.o aes.o qemu-config.o
block-obj-y += qemu-progress.o qemu-sockets.o uri.o
block-obj-y += qemu-progress.o qemu-sockets.o uri.o notify.o
block-obj-y += $(coroutine-obj-y) $(qobject-obj-y) $(version-obj-y)
block-obj-$(CONFIG_POSIX) += posix-aio-compat.o
block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
block-obj-y += block/
block-obj-y += $(qapi-obj-y) qapi-types.o qapi-visit.o
ifeq ($(CONFIG_VIRTIO)$(CONFIG_VIRTFS)$(CONFIG_PCI),yyy)
# Lots of the fsdev/9pcode is pulled in by vl.c via qemu_fsdev_add.
@ -60,7 +61,7 @@ endif
# suppress *all* target specific code in case of system emulation, i.e. a
# single QEMU executable should support all CPUs and machines.
common-obj-y = $(block-obj-y) blockdev.o block/
common-obj-y = $(block-obj-y) blockdev.o blockdev-nbd.o block/
common-obj-y += net.o net/
common-obj-y += qom/
common-obj-y += readline.o console.o cursor.o
@ -93,7 +94,7 @@ common-obj-y += bt-host.o bt-vhci.o
common-obj-y += dma-helpers.o
common-obj-y += iov.o acl.o
common-obj-$(CONFIG_POSIX) += compatfd.o
common-obj-y += notify.o event_notifier.o
common-obj-y += event_notifier.o
common-obj-y += qemu-timer.o qemu-timer-common.o
common-obj-y += qtest.o
common-obj-y += vl.o
@ -239,9 +240,9 @@ QEMU_CFLAGS+=$(GLIB_CFLAGS)
nested-vars += \
qga-obj-y \
block-obj-y \
qom-obj-y \
qapi-obj-y \
block-obj-y \
user-obj-y \
common-obj-y \
extra-obj-y

19
block.c
View file

@ -30,6 +30,7 @@
#include "module.h"
#include "qjson.h"
#include "sysemu.h"
#include "notify.h"
#include "qemu-coroutine.h"
#include "qmp-commands.h"
#include "qemu-timer.h"
@ -312,9 +313,16 @@ BlockDriverState *bdrv_new(const char *device_name)
QTAILQ_INSERT_TAIL(&bdrv_states, bs, list);
}
bdrv_iostatus_disable(bs);
notifier_list_init(&bs->close_notifiers);
return bs;
}
void bdrv_add_close_notifier(BlockDriverState *bs, Notifier *notify)
{
notifier_list_add(&bs->close_notifiers, notify);
}
BlockDriver *bdrv_find_format(const char *format_name)
{
BlockDriver *drv1;
@ -1098,12 +1106,13 @@ void bdrv_reopen_abort(BDRVReopenState *reopen_state)
void bdrv_close(BlockDriverState *bs)
{
bdrv_flush(bs);
if (bs->drv) {
if (bs->job) {
block_job_cancel_sync(bs->job);
}
bdrv_drain_all();
if (bs->job) {
block_job_cancel_sync(bs->job);
}
bdrv_drain_all();
notifier_list_notify(&bs->close_notifiers, bs);
if (bs->drv) {
if (bs == bs_snapshots) {
bs_snapshots = NULL;
}

View file

@ -144,6 +144,7 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
void bdrv_reopen_commit(BDRVReopenState *reopen_state);
void bdrv_reopen_abort(BDRVReopenState *reopen_state);
void bdrv_close(BlockDriverState *bs);
void bdrv_add_close_notifier(BlockDriverState *bs, Notifier *notify);
int bdrv_attach_dev(BlockDriverState *bs, void *dev);
void bdrv_attach_dev_nofail(BlockDriverState *bs, void *dev);
void bdrv_detach_dev(BlockDriverState *bs, void *dev);

View file

@ -233,6 +233,8 @@ struct BlockDriverState {
BlockDriverState *backing_hd;
BlockDriverState *file;
NotifierList close_notifiers;
/* number of in-flight copy-on-read requests */
unsigned int copy_on_read_in_flight;

119
blockdev-nbd.c Normal file
View file

@ -0,0 +1,119 @@
/*
* Serving QEMU block devices via NBD
*
* Copyright (c) 2012 Red Hat, Inc.
*
* Author: Paolo Bonzini <pbonzini@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or
* later. See the COPYING file in the top-level directory.
*/
#include "blockdev.h"
#include "hw/block-common.h"
#include "monitor.h"
#include "qerror.h"
#include "sysemu.h"
#include "qmp-commands.h"
#include "trace.h"
#include "nbd.h"
#include "qemu_socket.h"
static int server_fd = -1;
static void nbd_accept(void *opaque)
{
struct sockaddr_in addr;
socklen_t addr_len = sizeof(addr);
int fd = accept(server_fd, (struct sockaddr *)&addr, &addr_len);
if (fd >= 0) {
nbd_client_new(NULL, fd, nbd_client_put);
}
}
void qmp_nbd_server_start(SocketAddress *addr, Error **errp)
{
if (server_fd != -1) {
error_setg(errp, "NBD server already running");
return;
}
server_fd = socket_listen(addr, errp);
if (server_fd != -1) {
qemu_set_fd_handler2(server_fd, NULL, nbd_accept, NULL, NULL);
}
}
/* Hook into the BlockDriverState notifiers to close the export when
* the file is closed.
*/
typedef struct NBDCloseNotifier {
Notifier n;
NBDExport *exp;
QTAILQ_ENTRY(NBDCloseNotifier) next;
} NBDCloseNotifier;
static QTAILQ_HEAD(, NBDCloseNotifier) close_notifiers =
QTAILQ_HEAD_INITIALIZER(close_notifiers);
static void nbd_close_notifier(Notifier *n, void *data)
{
NBDCloseNotifier *cn = DO_UPCAST(NBDCloseNotifier, n, n);
notifier_remove(&cn->n);
QTAILQ_REMOVE(&close_notifiers, cn, next);
nbd_export_close(cn->exp);
nbd_export_put(cn->exp);
g_free(cn);
}
static void nbd_server_put_ref(NBDExport *exp)
{
BlockDriverState *bs = nbd_export_get_blockdev(exp);
drive_put_ref(drive_get_by_blockdev(bs));
}
void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
Error **errp)
{
BlockDriverState *bs;
NBDExport *exp;
NBDCloseNotifier *n;
if (nbd_export_find(device)) {
error_setg(errp, "NBD server already exporting device '%s'", device);
return;
}
bs = bdrv_find(device);
if (!bs) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return;
}
exp = nbd_export_new(bs, 0, -1, writable ? 0 : NBD_FLAG_READ_ONLY,
nbd_server_put_ref);
nbd_export_set_name(exp, device);
drive_get_ref(drive_get_by_blockdev(bs));
n = g_malloc0(sizeof(NBDCloseNotifier));
n->n.notify = nbd_close_notifier;
n->exp = exp;
bdrv_add_close_notifier(bs, &n->n);
QTAILQ_INSERT_TAIL(&close_notifiers, n, next);
}
void qmp_nbd_server_stop(Error **errp)
{
while (!QTAILQ_EMPTY(&close_notifiers)) {
NBDCloseNotifier *cn = QTAILQ_FIRST(&close_notifiers);
nbd_close_notifier(&cn->n, nbd_export_get_blockdev(cn->exp));
}
qemu_set_fd_handler2(server_fd, NULL, NULL, NULL, NULL);
close(server_fd);
server_fd = -1;
}

View file

@ -378,7 +378,7 @@ void cocoa_display_init(DisplayState *ds, int full_screen);
/* vnc.c */
void vnc_display_init(DisplayState *ds);
void vnc_display_close(DisplayState *ds);
int vnc_display_open(DisplayState *ds, const char *display);
void vnc_display_open(DisplayState *ds, const char *display, Error **errp);
void vnc_display_add_client(DisplayState *ds, int csock, int skipauth);
int vnc_display_disable_login(DisplayState *ds);
char *vnc_display_local_addr(DisplayState *ds);

28
error.c
View file

@ -43,6 +43,34 @@ void error_set(Error **errp, ErrorClass err_class, const char *fmt, ...)
*errp = err;
}
void error_set_errno(Error **errp, int os_errno, ErrorClass err_class,
const char *fmt, ...)
{
Error *err;
char *msg1;
va_list ap;
if (errp == NULL) {
return;
}
assert(*errp == NULL);
err = g_malloc0(sizeof(*err));
va_start(ap, fmt);
msg1 = g_strdup_vprintf(fmt, ap);
if (os_errno != 0) {
err->msg = g_strdup_printf("%s: %s", msg1, strerror(os_errno));
g_free(msg1);
} else {
err->msg = msg1;
}
va_end(ap);
err->err_class = err_class;
*errp = err;
}
Error *error_copy(const Error *err)
{
Error *err_new;

View file

@ -29,11 +29,20 @@ typedef struct Error Error;
*/
void error_set(Error **err, ErrorClass err_class, const char *fmt, ...) GCC_FMT_ATTR(3, 4);
/**
* Set an indirect pointer to an error given a ErrorClass value and a
* printf-style human message, followed by a strerror() string if
* @os_error is not zero.
*/
void error_set_errno(Error **err, int os_error, ErrorClass err_class, const char *fmt, ...) GCC_FMT_ATTR(4, 5);
/**
* Same as error_set(), but sets a generic error
*/
#define error_setg(err, fmt, ...) \
error_set(err, ERROR_CLASS_GENERIC_ERROR, fmt, ## __VA_ARGS__)
#define error_setg_errno(err, os_error, fmt, ...) \
error_set_errno(err, os_error, ERROR_CLASS_GENERIC_ERROR, fmt, ## __VA_ARGS__)
/**
* Returns true if an indirect pointer to an error is pointing to a valid

View file

@ -60,22 +60,18 @@ static int exec_close(MigrationState *s)
return ret;
}
int exec_start_outgoing_migration(MigrationState *s, const char *command)
void exec_start_outgoing_migration(MigrationState *s, const char *command, Error **errp)
{
FILE *f;
f = popen(command, "w");
if (f == NULL) {
DPRINTF("Unable to popen exec target\n");
goto err_after_popen;
error_setg_errno(errp, errno, "failed to popen the migration target");
return;
}
s->fd = fileno(f);
if (s->fd == -1) {
DPRINTF("Unable to retrieve file descriptor for popen'd handle\n");
goto err_after_open;
}
assert(s->fd != -1);
socket_set_nonblock(s->fd);
s->opaque = qemu_popen(f, "w");
@ -85,12 +81,6 @@ int exec_start_outgoing_migration(MigrationState *s, const char *command)
s->write = file_write;
migrate_fd_connect(s);
return 0;
err_after_open:
pclose(f);
err_after_popen:
return -1;
}
static void exec_accept_incoming_migration(void *opaque)
@ -102,19 +92,17 @@ static void exec_accept_incoming_migration(void *opaque)
qemu_fclose(f);
}
int exec_start_incoming_migration(const char *command)
void exec_start_incoming_migration(const char *command, Error **errp)
{
QEMUFile *f;
DPRINTF("Attempting to start an incoming migration\n");
f = qemu_popen_cmd(command, "r");
if(f == NULL) {
DPRINTF("Unable to apply qemu wrapper to popen file\n");
return -errno;
error_setg_errno(errp, errno, "failed to popen the migration source");
return;
}
qemu_set_fd_handler2(qemu_stdio_fd(f), NULL,
exec_accept_incoming_migration, NULL, f);
return 0;
}

View file

@ -73,30 +73,19 @@ static int fd_close(MigrationState *s)
return 0;
}
int fd_start_outgoing_migration(MigrationState *s, const char *fdname)
void fd_start_outgoing_migration(MigrationState *s, const char *fdname, Error **errp)
{
s->fd = monitor_get_fd(cur_mon, fdname, NULL);
s->fd = monitor_get_fd(cur_mon, fdname, errp);
if (s->fd == -1) {
DPRINTF("fd_migration: invalid file descriptor identifier\n");
goto err_after_get_fd;
}
if (fcntl(s->fd, F_SETFL, O_NONBLOCK) == -1) {
DPRINTF("Unable to set nonblocking mode on file descriptor\n");
goto err_after_open;
return;
}
fcntl(s->fd, F_SETFL, O_NONBLOCK);
s->get_error = fd_errno;
s->write = fd_write;
s->close = fd_close;
migrate_fd_connect(s);
return 0;
err_after_open:
close(s->fd);
err_after_get_fd:
return -1;
}
static void fd_accept_incoming_migration(void *opaque)
@ -108,7 +97,7 @@ static void fd_accept_incoming_migration(void *opaque)
qemu_fclose(f);
}
int fd_start_incoming_migration(const char *infd)
void fd_start_incoming_migration(const char *infd, Error **errp)
{
int fd;
QEMUFile *f;
@ -118,11 +107,9 @@ int fd_start_incoming_migration(const char *infd)
fd = strtol(infd, NULL, 0);
f = qemu_fdopen(fd, "rb");
if(f == NULL) {
DPRINTF("Unable to apply qemu wrapper to file descriptor\n");
return -errno;
error_setg_errno(errp, errno, "failed to open the source descriptor");
return;
}
qemu_set_fd_handler2(fd, NULL, fd_accept_incoming_migration, NULL, f);
return 0;
}

View file

@ -68,21 +68,13 @@ static void tcp_wait_for_connect(int fd, void *opaque)
}
}
int tcp_start_outgoing_migration(MigrationState *s, const char *host_port,
Error **errp)
void tcp_start_outgoing_migration(MigrationState *s, const char *host_port, Error **errp)
{
s->get_error = socket_errno;
s->write = socket_write;
s->close = tcp_close;
s->fd = inet_nonblocking_connect(host_port, tcp_wait_for_connect, s,
errp);
if (error_is_set(errp)) {
migrate_fd_error(s);
return -1;
}
return 0;
s->fd = inet_nonblocking_connect(host_port, tcp_wait_for_connect, s, errp);
}
static void tcp_accept_incoming_migration(void *opaque)
@ -119,18 +111,15 @@ out2:
close(s);
}
int tcp_start_incoming_migration(const char *host_port, Error **errp)
void tcp_start_incoming_migration(const char *host_port, Error **errp)
{
int s;
s = inet_listen(host_port, NULL, 256, SOCK_STREAM, 0, errp);
if (s < 0) {
return -1;
return;
}
qemu_set_fd_handler2(s, NULL, tcp_accept_incoming_migration, NULL,
(void *)(intptr_t)s);
return 0;
}

View file

@ -53,69 +53,28 @@ static int unix_close(MigrationState *s)
return r;
}
static void unix_wait_for_connect(void *opaque)
static void unix_wait_for_connect(int fd, void *opaque)
{
MigrationState *s = opaque;
int val, ret;
socklen_t valsize = sizeof(val);
DPRINTF("connect completed\n");
do {
ret = getsockopt(s->fd, SOL_SOCKET, SO_ERROR, (void *) &val, &valsize);
} while (ret == -1 && errno == EINTR);
if (ret < 0) {
if (fd < 0) {
DPRINTF("migrate connect error\n");
s->fd = -1;
migrate_fd_error(s);
return;
}
qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
if (val == 0)
} else {
DPRINTF("migrate connect success\n");
s->fd = fd;
migrate_fd_connect(s);
else {
DPRINTF("error connecting %d\n", val);
migrate_fd_error(s);
}
}
int unix_start_outgoing_migration(MigrationState *s, const char *path)
void unix_start_outgoing_migration(MigrationState *s, const char *path, Error **errp)
{
struct sockaddr_un addr;
int ret;
addr.sun_family = AF_UNIX;
snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", path);
s->get_error = unix_errno;
s->write = unix_write;
s->close = unix_close;
s->fd = qemu_socket(PF_UNIX, SOCK_STREAM, 0);
if (s->fd == -1) {
DPRINTF("Unable to open socket");
return -errno;
}
socket_set_nonblock(s->fd);
do {
ret = connect(s->fd, (struct sockaddr *)&addr, sizeof(addr));
if (ret == -1) {
ret = -errno;
}
if (ret == -EINPROGRESS || ret == -EWOULDBLOCK) {
qemu_set_fd_handler2(s->fd, NULL, NULL, unix_wait_for_connect, s);
return 0;
}
} while (ret == -EINTR);
if (ret < 0) {
DPRINTF("connect failed\n");
migrate_fd_error(s);
return ret;
}
migrate_fd_connect(s);
return 0;
s->fd = unix_nonblocking_connect(path, unix_wait_for_connect, s, errp);
}
static void unix_accept_incoming_migration(void *opaque)
@ -152,43 +111,15 @@ out2:
close(s);
}
int unix_start_incoming_migration(const char *path)
void unix_start_incoming_migration(const char *path, Error **errp)
{
struct sockaddr_un addr;
int s;
int ret;
DPRINTF("Attempting to start an incoming migration\n");
s = qemu_socket(PF_UNIX, SOCK_STREAM, 0);
if (s == -1) {
fprintf(stderr, "Could not open unix socket: %s\n", strerror(errno));
return -errno;
}
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", path);
unlink(addr.sun_path);
if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
ret = -errno;
fprintf(stderr, "bind(unix:%s): %s\n", addr.sun_path, strerror(errno));
goto err;
}
if (listen(s, 1) == -1) {
fprintf(stderr, "listen(unix:%s): %s\n", addr.sun_path,
strerror(errno));
ret = -errno;
goto err;
s = unix_listen(path, NULL, 0, errp);
if (s < 0) {
return;
}
qemu_set_fd_handler2(s, NULL, unix_accept_incoming_migration, NULL,
(void *)(intptr_t)s);
return 0;
err:
close(s);
return ret;
}

View file

@ -64,26 +64,23 @@ MigrationState *migrate_get_current(void)
return &current_migration;
}
int qemu_start_incoming_migration(const char *uri, Error **errp)
void qemu_start_incoming_migration(const char *uri, Error **errp)
{
const char *p;
int ret;
if (strstart(uri, "tcp:", &p))
ret = tcp_start_incoming_migration(p, errp);
tcp_start_incoming_migration(p, errp);
#if !defined(WIN32)
else if (strstart(uri, "exec:", &p))
ret = exec_start_incoming_migration(p);
exec_start_incoming_migration(p, errp);
else if (strstart(uri, "unix:", &p))
ret = unix_start_incoming_migration(p);
unix_start_incoming_migration(p, errp);
else if (strstart(uri, "fd:", &p))
ret = fd_start_incoming_migration(p);
fd_start_incoming_migration(p, errp);
#endif
else {
fprintf(stderr, "unknown migration protocol: %s\n", uri);
ret = -EPROTONOSUPPORT;
error_setg(errp, "unknown migration protocol: %s\n", uri);
}
return ret;
}
void process_incoming_migration(QEMUFile *f)
@ -483,10 +480,10 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk,
bool has_inc, bool inc, bool has_detach, bool detach,
Error **errp)
{
Error *local_err = NULL;
MigrationState *s = migrate_get_current();
MigrationParams params;
const char *p;
int ret;
params.blk = blk;
params.shared = inc;
@ -508,26 +505,23 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk,
s = migrate_init(&params);
if (strstart(uri, "tcp:", &p)) {
ret = tcp_start_outgoing_migration(s, p, errp);
tcp_start_outgoing_migration(s, p, &local_err);
#if !defined(WIN32)
} else if (strstart(uri, "exec:", &p)) {
ret = exec_start_outgoing_migration(s, p);
exec_start_outgoing_migration(s, p, &local_err);
} else if (strstart(uri, "unix:", &p)) {
ret = unix_start_outgoing_migration(s, p);
unix_start_outgoing_migration(s, p, &local_err);
} else if (strstart(uri, "fd:", &p)) {
ret = fd_start_outgoing_migration(s, p);
fd_start_outgoing_migration(s, p, &local_err);
#endif
} else {
error_set(errp, QERR_INVALID_PARAMETER_VALUE, "uri", "a valid migration protocol");
return;
}
if (ret < 0) {
if (!error_is_set(errp)) {
DPRINTF("migration failed: %s\n", strerror(-ret));
/* FIXME: we should return meaningful errors */
error_set(errp, QERR_UNDEFINED_ERROR);
}
if (local_err) {
migrate_fd_error(s);
error_propagate(errp, local_err);
return;
}

View file

@ -49,7 +49,7 @@ struct MigrationState
void process_incoming_migration(QEMUFile *f);
int qemu_start_incoming_migration(const char *uri, Error **errp);
void qemu_start_incoming_migration(const char *uri, Error **errp);
uint64_t migrate_max_downtime(void);
@ -57,22 +57,21 @@ void do_info_migrate_print(Monitor *mon, const QObject *data);
void do_info_migrate(Monitor *mon, QObject **ret_data);
int exec_start_incoming_migration(const char *host_port);
void exec_start_incoming_migration(const char *host_port, Error **errp);
int exec_start_outgoing_migration(MigrationState *s, const char *host_port);
void exec_start_outgoing_migration(MigrationState *s, const char *host_port, Error **errp);
int tcp_start_incoming_migration(const char *host_port, Error **errp);
void tcp_start_incoming_migration(const char *host_port, Error **errp);
int tcp_start_outgoing_migration(MigrationState *s, const char *host_port,
Error **errp);
void tcp_start_outgoing_migration(MigrationState *s, const char *host_port, Error **errp);
int unix_start_incoming_migration(const char *path);
void unix_start_incoming_migration(const char *path, Error **errp);
int unix_start_outgoing_migration(MigrationState *s, const char *path);
void unix_start_outgoing_migration(MigrationState *s, const char *path, Error **errp);
int fd_start_incoming_migration(const char *path);
void fd_start_incoming_migration(const char *path, Error **errp);
int fd_start_outgoing_migration(MigrationState *s, const char *fdname);
void fd_start_outgoing_migration(MigrationState *s, const char *fdname, Error **errp);
void migrate_fd_error(MigrationState *s);

39
nbd.c
View file

@ -208,7 +208,14 @@ int tcp_socket_outgoing(const char *address, uint16_t port)
int tcp_socket_outgoing_spec(const char *address_and_port)
{
return inet_connect(address_and_port, NULL);
Error *local_err = NULL;
int fd = inet_connect(address_and_port, &local_err);
if (local_err != NULL) {
qerror_report_err(local_err);
error_free(local_err);
}
return fd;
}
int tcp_socket_incoming(const char *address, uint16_t port)
@ -220,22 +227,38 @@ int tcp_socket_incoming(const char *address, uint16_t port)
int tcp_socket_incoming_spec(const char *address_and_port)
{
char *ostr = NULL;
int olen = 0;
return inet_listen(address_and_port, ostr, olen, SOCK_STREAM, 0, NULL);
Error *local_err = NULL;
int fd = inet_listen(address_and_port, NULL, 0, SOCK_STREAM, 0, &local_err);
if (local_err != NULL) {
qerror_report_err(local_err);
error_free(local_err);
}
return fd;
}
int unix_socket_incoming(const char *path)
{
char *ostr = NULL;
int olen = 0;
Error *local_err = NULL;
int fd = unix_listen(path, NULL, 0, &local_err);
return unix_listen(path, ostr, olen);
if (local_err != NULL) {
qerror_report_err(local_err);
error_free(local_err);
}
return fd;
}
int unix_socket_outgoing(const char *path)
{
return unix_connect(path);
Error *local_err = NULL;
int fd = unix_connect(path, &local_err);
if (local_err != NULL) {
qerror_report_err(local_err);
error_free(local_err);
}
return fd;
}
/* Basic flow for negotiation

View file

@ -2518,6 +2518,59 @@
'id': 'str',
'opts': 'NetClientOptions' } }
##
# @InetSocketAddress
#
# Captures a socket address or address range in the Internet namespace.
#
# @host: host part of the address
#
# @port: port part of the address, or lowest port if @to is present
#
# @to: highest port to try
#
# @ipv4: whether to accept IPv4 addresses, default try both IPv4 and IPv6
# #optional
#
# @ipv6: whether to accept IPv6 addresses, default try both IPv4 and IPv6
# #optional
#
# Since 1.3
##
{ 'type': 'InetSocketAddress',
'data': {
'host': 'str',
'port': 'str',
'*to': 'uint16',
'*ipv4': 'bool',
'*ipv6': 'bool' } }
##
# @UnixSocketAddress
#
# Captures a socket address in the local ("Unix socket") namespace.
#
# @path: filesystem path to use
#
# Since 1.3
##
{ 'type': 'UnixSocketAddress',
'data': {
'path': 'str' } }
##
# @SocketAddress
#
# Captures the address of a socket, which could also be a named file descriptor
#
# Since 1.3
##
{ 'union': 'SocketAddress',
'data': {
'inet': 'InetSocketAddress',
'unix': 'UnixSocketAddress',
'fd': 'String' } }
##
# @getfd:
#
@ -2810,3 +2863,46 @@
# Since: 0.14.0
##
{ 'command': 'screendump', 'data': {'filename': 'str'} }
##
# @nbd-server-start:
#
# Start an NBD server listening on the given host and port. Block
# devices can then be exported using @nbd-server-add. The NBD
# server will present them as named exports; for example, another
# QEMU instance could refer to them as "nbd:HOST:PORT:exportname=NAME".
#
# @addr: Address on which to listen.
#
# Returns: error if the server is already running.
#
# Since: 1.3.0
##
{ 'command': 'nbd-server-start',
'data': { 'addr': 'SocketAddress' } }
##
# @nbd-server-add:
#
# Export a device to QEMU's embedded NBD server.
#
# @device: Block device to be exported
#
# @writable: Whether clients should be able to write to the device via the
# NBD connection (default false). #optional
#
# Returns: error if the device is already marked for export.
#
# Since: 1.3.0
##
{ 'command': 'nbd-server-add', 'data': {'device': 'str', '*writable': 'bool'} }
##
# @nbd-server-stop:
#
# Stop QEMU's embedded NBD server, and unregister all devices previously
# added via @nbd-server-add.
#
# Since: 1.3.0
##
{ 'command': 'nbd-server-stop' }

View file

@ -2097,14 +2097,14 @@ static CharDriverState *qemu_chr_open_udp(QemuOpts *opts)
{
CharDriverState *chr = NULL;
NetCharDriver *s = NULL;
Error *local_err = NULL;
int fd = -1;
chr = g_malloc0(sizeof(CharDriverState));
s = g_malloc0(sizeof(NetCharDriver));
fd = inet_dgram_opts(opts);
fd = inet_dgram_opts(opts, &local_err);
if (fd < 0) {
fprintf(stderr, "inet_dgram_opts failed\n");
goto return_err;
}
@ -2118,6 +2118,10 @@ static CharDriverState *qemu_chr_open_udp(QemuOpts *opts)
return chr;
return_err:
if (local_err) {
qerror_report_err(local_err);
error_free(local_err);
}
g_free(chr);
g_free(s);
if (fd >= 0) {
@ -2428,6 +2432,7 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts)
{
CharDriverState *chr = NULL;
TCPCharDriver *s = NULL;
Error *local_err = NULL;
int fd = -1;
int is_listen;
int is_waitconnect;
@ -2448,15 +2453,15 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts)
if (is_unix) {
if (is_listen) {
fd = unix_listen_opts(opts);
fd = unix_listen_opts(opts, &local_err);
} else {
fd = unix_connect_opts(opts);
fd = unix_connect_opts(opts, &local_err, NULL, NULL);
}
} else {
if (is_listen) {
fd = inet_listen_opts(opts, 0, NULL);
fd = inet_listen_opts(opts, 0, &local_err);
} else {
fd = inet_connect_opts(opts, NULL, NULL, NULL);
fd = inet_connect_opts(opts, &local_err, NULL, NULL);
}
}
if (fd < 0) {
@ -2517,8 +2522,13 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts)
return chr;
fail:
if (fd >= 0)
if (local_err) {
qerror_report_err(local_err);
error_free(local_err);
}
if (fd >= 0) {
closesocket(fd);
}
g_free(s);
g_free(chr);
return NULL;

View file

@ -22,6 +22,7 @@
#include <errno.h>
#include <unistd.h>
#include "monitor.h"
#include "qemu_socket.h"
#include "qemu-common.h" /* for qemu_isdigit */
#include "main-loop.h"
@ -120,8 +121,7 @@ int inet_listen_opts(QemuOpts *opts, int port_offset, Error **errp)
if ((qemu_opt_get(opts, "host") == NULL) ||
(qemu_opt_get(opts, "port") == NULL)) {
fprintf(stderr, "%s: host and/or port not specified\n", __FUNCTION__);
error_set(errp, QERR_SOCKET_CREATE_FAILED);
error_setg(errp, "host and/or port not specified");
return -1;
}
pstrcpy(port, sizeof(port), qemu_opt_get(opts, "port"));
@ -138,9 +138,8 @@ int inet_listen_opts(QemuOpts *opts, int port_offset, Error **errp)
snprintf(port, sizeof(port), "%d", atoi(port) + port_offset);
rc = getaddrinfo(strlen(addr) ? addr : NULL, port, &ai, &res);
if (rc != 0) {
fprintf(stderr,"getaddrinfo(%s,%s): %s\n", addr, port,
gai_strerror(rc));
error_set(errp, QERR_SOCKET_CREATE_FAILED);
error_setg(errp, "address resolution failed for %s:%s: %s", addr, port,
gai_strerror(rc));
return -1;
}
@ -151,10 +150,8 @@ int inet_listen_opts(QemuOpts *opts, int port_offset, Error **errp)
NI_NUMERICHOST | NI_NUMERICSERV);
slisten = qemu_socket(e->ai_family, e->ai_socktype, e->ai_protocol);
if (slisten < 0) {
fprintf(stderr,"%s: socket(%s): %s\n", __FUNCTION__,
inet_strfamily(e->ai_family), strerror(errno));
if (!e->ai_next) {
error_set(errp, QERR_SOCKET_CREATE_FAILED);
error_set_errno(errp, errno, QERR_SOCKET_CREATE_FAILED);
}
continue;
}
@ -176,24 +173,19 @@ int inet_listen_opts(QemuOpts *opts, int port_offset, Error **errp)
goto listen;
}
if (p == port_max) {
fprintf(stderr,"%s: bind(%s,%s,%d): %s\n", __FUNCTION__,
inet_strfamily(e->ai_family), uaddr, inet_getport(e),
strerror(errno));
if (!e->ai_next) {
error_set(errp, QERR_SOCKET_BIND_FAILED);
error_set_errno(errp, errno, QERR_SOCKET_BIND_FAILED);
}
}
}
closesocket(slisten);
}
fprintf(stderr, "%s: FAILED\n", __FUNCTION__);
freeaddrinfo(res);
return -1;
listen:
if (listen(slisten,1) != 0) {
error_set(errp, QERR_SOCKET_LISTEN_FAILED);
perror("listen");
error_set_errno(errp, errno, QERR_SOCKET_LISTEN_FAILED);
closesocket(slisten);
freeaddrinfo(res);
return -1;
@ -225,7 +217,7 @@ typedef struct ConnectState {
} ConnectState;
static int inet_connect_addr(struct addrinfo *addr, bool *in_progress,
ConnectState *connect_state);
ConnectState *connect_state, Error **errp);
static void wait_for_connect(void *opaque)
{
@ -252,16 +244,19 @@ static void wait_for_connect(void *opaque)
}
/* try to connect to the next address on the list */
while (s->current_addr->ai_next != NULL && s->fd < 0) {
s->current_addr = s->current_addr->ai_next;
s->fd = inet_connect_addr(s->current_addr, &in_progress, s);
/* connect in progress */
if (in_progress) {
return;
if (s->current_addr) {
while (s->current_addr->ai_next != NULL && s->fd < 0) {
s->current_addr = s->current_addr->ai_next;
s->fd = inet_connect_addr(s->current_addr, &in_progress, s, NULL);
/* connect in progress */
if (in_progress) {
return;
}
}
freeaddrinfo(s->addr_list);
}
freeaddrinfo(s->addr_list);
if (s->callback) {
s->callback(s->fd, s->opaque);
}
@ -269,7 +264,7 @@ static void wait_for_connect(void *opaque)
}
static int inet_connect_addr(struct addrinfo *addr, bool *in_progress,
ConnectState *connect_state)
ConnectState *connect_state, Error **errp)
{
int sock, rc;
@ -277,8 +272,7 @@ static int inet_connect_addr(struct addrinfo *addr, bool *in_progress,
sock = qemu_socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (sock < 0) {
fprintf(stderr, "%s: socket(%s): %s\n", __func__,
inet_strfamily(addr->ai_family), strerror(errno));
error_set_errno(errp, errno, QERR_SOCKET_CREATE_FAILED);
return -1;
}
qemu_setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
@ -299,6 +293,7 @@ static int inet_connect_addr(struct addrinfo *addr, bool *in_progress,
connect_state);
*in_progress = true;
} else if (rc < 0) {
error_set_errno(errp, errno, QERR_SOCKET_CONNECT_FAILED);
closesocket(sock);
return -1;
}
@ -321,9 +316,7 @@ static struct addrinfo *inet_parse_connect_opts(QemuOpts *opts, Error **errp)
addr = qemu_opt_get(opts, "host");
port = qemu_opt_get(opts, "port");
if (addr == NULL || port == NULL) {
fprintf(stderr,
"inet_parse_connect_opts: host and/or port not specified\n");
error_set(errp, QERR_SOCKET_CREATE_FAILED);
error_setg(errp, "host and/or port not specified");
return NULL;
}
@ -337,9 +330,8 @@ static struct addrinfo *inet_parse_connect_opts(QemuOpts *opts, Error **errp)
/* lookup */
rc = getaddrinfo(addr, port, &ai, &res);
if (rc != 0) {
fprintf(stderr, "getaddrinfo(%s,%s): %s\n", addr, port,
gai_strerror(rc));
error_set(errp, QERR_SOCKET_CREATE_FAILED);
error_setg(errp, "address resolution failed for %s:%s: %s", addr, port,
gai_strerror(rc));
return NULL;
}
return res;
@ -384,7 +376,7 @@ int inet_connect_opts(QemuOpts *opts, Error **errp,
if (connect_state != NULL) {
connect_state->current_addr = e;
}
sock = inet_connect_addr(e, &in_progress, connect_state);
sock = inet_connect_addr(e, &in_progress, connect_state, errp);
if (in_progress) {
return sock;
} else if (sock >= 0) {
@ -395,21 +387,16 @@ int inet_connect_opts(QemuOpts *opts, Error **errp,
break;
}
}
if (sock < 0) {
error_set(errp, QERR_SOCKET_CONNECT_FAILED);
}
g_free(connect_state);
freeaddrinfo(res);
return sock;
}
int inet_dgram_opts(QemuOpts *opts)
int inet_dgram_opts(QemuOpts *opts, Error **errp)
{
struct addrinfo ai, *peer = NULL, *local = NULL;
const char *addr;
const char *port;
char uaddr[INET6_ADDRSTRLEN+1];
char uport[33];
int sock = -1, rc;
/* lookup peer addr */
@ -424,7 +411,7 @@ int inet_dgram_opts(QemuOpts *opts)
addr = "localhost";
}
if (port == NULL || strlen(port) == 0) {
fprintf(stderr, "inet_dgram: port not specified\n");
error_setg(errp, "remote port not specified");
return -1;
}
@ -434,8 +421,8 @@ int inet_dgram_opts(QemuOpts *opts)
ai.ai_family = PF_INET6;
if (0 != (rc = getaddrinfo(addr, port, &ai, &peer))) {
fprintf(stderr,"getaddrinfo(%s,%s): %s\n", addr, port,
gai_strerror(rc));
error_setg(errp, "address resolution failed for %s:%s: %s", addr, port,
gai_strerror(rc));
return -1;
}
@ -454,44 +441,28 @@ int inet_dgram_opts(QemuOpts *opts)
port = "0";
if (0 != (rc = getaddrinfo(addr, port, &ai, &local))) {
fprintf(stderr,"getaddrinfo(%s,%s): %s\n", addr, port,
gai_strerror(rc));
error_setg(errp, "address resolution failed for %s:%s: %s", addr, port,
gai_strerror(rc));
goto err;
}
/* create socket */
sock = qemu_socket(peer->ai_family, peer->ai_socktype, peer->ai_protocol);
if (sock < 0) {
fprintf(stderr,"%s: socket(%s): %s\n", __FUNCTION__,
inet_strfamily(peer->ai_family), strerror(errno));
error_set_errno(errp, errno, QERR_SOCKET_CREATE_FAILED);
goto err;
}
setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(void*)&on,sizeof(on));
/* bind socket */
if (getnameinfo((struct sockaddr*)local->ai_addr,local->ai_addrlen,
uaddr,INET6_ADDRSTRLEN,uport,32,
NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
fprintf(stderr, "%s: getnameinfo: oops\n", __FUNCTION__);
goto err;
}
if (bind(sock, local->ai_addr, local->ai_addrlen) < 0) {
fprintf(stderr,"%s: bind(%s,%s,%d): OK\n", __FUNCTION__,
inet_strfamily(local->ai_family), uaddr, inet_getport(local));
error_set_errno(errp, errno, QERR_SOCKET_BIND_FAILED);
goto err;
}
/* connect to peer */
if (getnameinfo((struct sockaddr*)peer->ai_addr, peer->ai_addrlen,
uaddr, INET6_ADDRSTRLEN, uport, 32,
NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
fprintf(stderr, "%s: getnameinfo: oops\n", __FUNCTION__);
goto err;
}
if (connect(sock,peer->ai_addr,peer->ai_addrlen) < 0) {
fprintf(stderr, "%s: connect(%s,%s,%s,%s): %s\n", __FUNCTION__,
inet_strfamily(peer->ai_family),
peer->ai_canonname, uaddr, uport, strerror(errno));
error_set_errno(errp, errno, QERR_SOCKET_CONNECT_FAILED);
goto err;
}
@ -510,59 +481,91 @@ err:
}
/* compatibility wrapper */
static int inet_parse(QemuOpts *opts, const char *str)
static InetSocketAddress *inet_parse(const char *str, Error **errp)
{
InetSocketAddress *addr;
const char *optstr, *h;
char addr[64];
char host[64];
char port[33];
int to;
int pos;
addr = g_new0(InetSocketAddress, 1);
/* parse address */
if (str[0] == ':') {
/* no host given */
addr[0] = '\0';
if (1 != sscanf(str,":%32[^,]%n",port,&pos)) {
fprintf(stderr, "%s: portonly parse error (%s)\n",
__FUNCTION__, str);
return -1;
host[0] = '\0';
if (1 != sscanf(str, ":%32[^,]%n", port, &pos)) {
error_setg(errp, "error parsing port in address '%s'", str);
goto fail;
}
} else if (str[0] == '[') {
/* IPv6 addr */
if (2 != sscanf(str,"[%64[^]]]:%32[^,]%n",addr,port,&pos)) {
fprintf(stderr, "%s: ipv6 parse error (%s)\n",
__FUNCTION__, str);
return -1;
if (2 != sscanf(str, "[%64[^]]]:%32[^,]%n", host, port, &pos)) {
error_setg(errp, "error parsing IPv6 address '%s'", str);
goto fail;
}
qemu_opt_set(opts, "ipv6", "on");
addr->ipv6 = addr->has_ipv6 = true;
} else if (qemu_isdigit(str[0])) {
/* IPv4 addr */
if (2 != sscanf(str,"%64[0-9.]:%32[^,]%n",addr,port,&pos)) {
fprintf(stderr, "%s: ipv4 parse error (%s)\n",
__FUNCTION__, str);
return -1;
if (2 != sscanf(str, "%64[0-9.]:%32[^,]%n", host, port, &pos)) {
error_setg(errp, "error parsing IPv4 address '%s'", str);
goto fail;
}
qemu_opt_set(opts, "ipv4", "on");
addr->ipv4 = addr->has_ipv4 = true;
} else {
/* hostname */
if (2 != sscanf(str,"%64[^:]:%32[^,]%n",addr,port,&pos)) {
fprintf(stderr, "%s: hostname parse error (%s)\n",
__FUNCTION__, str);
return -1;
if (2 != sscanf(str, "%64[^:]:%32[^,]%n", host, port, &pos)) {
error_setg(errp, "error parsing address '%s'", str);
goto fail;
}
}
qemu_opt_set(opts, "host", addr);
qemu_opt_set(opts, "port", port);
addr->host = g_strdup(host);
addr->port = g_strdup(port);
/* parse options */
optstr = str + pos;
h = strstr(optstr, ",to=");
if (h)
qemu_opt_set(opts, "to", h+4);
if (strstr(optstr, ",ipv4"))
qemu_opt_set(opts, "ipv4", "on");
if (strstr(optstr, ",ipv6"))
qemu_opt_set(opts, "ipv6", "on");
return 0;
if (h) {
if (1 != sscanf(str, "%d%n", &to, &pos) ||
(str[pos] != '\0' && str[pos] != ',')) {
error_setg(errp, "error parsing to= argument");
goto fail;
}
addr->has_to = true;
addr->to = to;
}
if (strstr(optstr, ",ipv4")) {
addr->ipv4 = addr->has_ipv4 = true;
}
if (strstr(optstr, ",ipv6")) {
addr->ipv6 = addr->has_ipv6 = true;
}
return addr;
fail:
qapi_free_InetSocketAddress(addr);
return NULL;
}
static void inet_addr_to_opts(QemuOpts *opts, InetSocketAddress *addr)
{
bool ipv4 = addr->ipv4 || !addr->has_ipv4;
bool ipv6 = addr->ipv6 || !addr->has_ipv6;
if (!ipv4 || !ipv6) {
qemu_opt_set_bool(opts, "ipv4", ipv4);
qemu_opt_set_bool(opts, "ipv6", ipv6);
}
if (addr->has_to) {
char to[20];
snprintf(to, sizeof(to), "%d", addr->to);
qemu_opt_set(opts, "to", to);
}
qemu_opt_set(opts, "host", addr->host);
qemu_opt_set(opts, "port", addr->port);
}
int inet_listen(const char *str, char *ostr, int olen,
@ -571,9 +574,13 @@ int inet_listen(const char *str, char *ostr, int olen,
QemuOpts *opts;
char *optstr;
int sock = -1;
InetSocketAddress *addr;
opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
if (inet_parse(opts, str) == 0) {
addr = inet_parse(str, errp);
if (addr != NULL) {
opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
inet_addr_to_opts(opts, addr);
qapi_free_InetSocketAddress(addr);
sock = inet_listen_opts(opts, port_offset, errp);
if (sock != -1 && ostr) {
optstr = strchr(str, ',');
@ -589,10 +596,8 @@ int inet_listen(const char *str, char *ostr, int olen,
optstr ? optstr : "");
}
}
} else {
error_set(errp, QERR_SOCKET_CREATE_FAILED);
qemu_opts_del(opts);
}
qemu_opts_del(opts);
return sock;
}
@ -608,14 +613,16 @@ int inet_connect(const char *str, Error **errp)
{
QemuOpts *opts;
int sock = -1;
InetSocketAddress *addr;
opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
if (inet_parse(opts, str) == 0) {
addr = inet_parse(str, errp);
if (addr != NULL) {
opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
inet_addr_to_opts(opts, addr);
qapi_free_InetSocketAddress(addr);
sock = inet_connect_opts(opts, errp, NULL, NULL);
} else {
error_set(errp, QERR_SOCKET_CREATE_FAILED);
qemu_opts_del(opts);
}
qemu_opts_del(opts);
return sock;
}
@ -638,22 +645,24 @@ int inet_nonblocking_connect(const char *str,
{
QemuOpts *opts;
int sock = -1;
InetSocketAddress *addr;
g_assert(callback != NULL);
opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
if (inet_parse(opts, str) == 0) {
addr = inet_parse(str, errp);
if (addr != NULL) {
opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
inet_addr_to_opts(opts, addr);
qapi_free_InetSocketAddress(addr);
sock = inet_connect_opts(opts, errp, callback, opaque);
} else {
error_set(errp, QERR_SOCKET_CREATE_FAILED);
qemu_opts_del(opts);
}
qemu_opts_del(opts);
return sock;
}
#ifndef _WIN32
int unix_listen_opts(QemuOpts *opts)
int unix_listen_opts(QemuOpts *opts, Error **errp)
{
struct sockaddr_un un;
const char *path = qemu_opt_get(opts, "path");
@ -661,7 +670,7 @@ int unix_listen_opts(QemuOpts *opts)
sock = qemu_socket(PF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
perror("socket(unix)");
error_set_errno(errp, errno, QERR_SOCKET_CREATE_FAILED);
return -1;
}
@ -686,11 +695,11 @@ int unix_listen_opts(QemuOpts *opts)
unlink(un.sun_path);
if (bind(sock, (struct sockaddr*) &un, sizeof(un)) < 0) {
fprintf(stderr, "bind(unix:%s): %s\n", un.sun_path, strerror(errno));
error_set_errno(errp, errno, QERR_SOCKET_BIND_FAILED);
goto err;
}
if (listen(sock, 1) < 0) {
fprintf(stderr, "listen(unix:%s): %s\n", un.sun_path, strerror(errno));
error_set_errno(errp, errno, QERR_SOCKET_LISTEN_FAILED);
goto err;
}
@ -701,37 +710,85 @@ err:
return -1;
}
int unix_connect_opts(QemuOpts *opts)
int unix_connect_opts(QemuOpts *opts, Error **errp,
NonBlockingConnectHandler *callback, void *opaque)
{
struct sockaddr_un un;
const char *path = qemu_opt_get(opts, "path");
int sock;
ConnectState *connect_state = NULL;
int sock, rc;
if (NULL == path) {
fprintf(stderr, "unix connect: no path specified\n");
error_setg(errp, "unix connect: no path specified\n");
return -1;
}
sock = qemu_socket(PF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
perror("socket(unix)");
error_set_errno(errp, errno, QERR_SOCKET_CREATE_FAILED);
return -1;
}
if (callback != NULL) {
connect_state = g_malloc0(sizeof(*connect_state));
connect_state->callback = callback;
connect_state->opaque = opaque;
socket_set_nonblock(sock);
}
memset(&un, 0, sizeof(un));
un.sun_family = AF_UNIX;
snprintf(un.sun_path, sizeof(un.sun_path), "%s", path);
if (connect(sock, (struct sockaddr*) &un, sizeof(un)) < 0) {
fprintf(stderr, "connect(unix:%s): %s\n", path, strerror(errno));
close(sock);
return -1;
/* connect to peer */
do {
rc = 0;
if (connect(sock, (struct sockaddr *) &un, sizeof(un)) < 0) {
rc = -socket_error();
}
} while (rc == -EINTR);
if (connect_state != NULL && QEMU_SOCKET_RC_INPROGRESS(rc)) {
connect_state->fd = sock;
qemu_set_fd_handler2(sock, NULL, NULL, wait_for_connect,
connect_state);
return sock;
} else if (rc >= 0) {
/* non blocking socket immediate success, call callback */
if (callback != NULL) {
callback(sock, opaque);
}
}
if (rc < 0) {
error_set_errno(errp, -rc, QERR_SOCKET_CONNECT_FAILED);
close(sock);
sock = -1;
}
g_free(connect_state);
return sock;
}
#else
int unix_listen_opts(QemuOpts *opts, Error **errp)
{
error_setg(errp, "unix sockets are not available on windows");
errno = ENOTSUP;
return -1;
}
int unix_connect_opts(QemuOpts *opts, Error **errp,
NonBlockingConnectHandler *callback, void *opaque)
{
error_setg(errp, "unix sockets are not available on windows");
errno = ENOTSUP;
return -1;
}
#endif
/* compatibility wrapper */
int unix_listen(const char *str, char *ostr, int olen)
int unix_listen(const char *str, char *ostr, int olen, Error **errp)
{
QemuOpts *opts;
char *path, *optstr;
@ -752,7 +809,7 @@ int unix_listen(const char *str, char *ostr, int olen)
qemu_opt_set(opts, "path", str);
}
sock = unix_listen_opts(opts);
sock = unix_listen_opts(opts, errp);
if (sock != -1 && ostr)
snprintf(ostr, olen, "%s%s", qemu_opt_get(opts, "path"), optstr ? optstr : "");
@ -760,49 +817,132 @@ int unix_listen(const char *str, char *ostr, int olen)
return sock;
}
int unix_connect(const char *path)
int unix_connect(const char *path, Error **errp)
{
QemuOpts *opts;
int sock;
opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
qemu_opt_set(opts, "path", path);
sock = unix_connect_opts(opts);
sock = unix_connect_opts(opts, errp, NULL, NULL);
qemu_opts_del(opts);
return sock;
}
#else
int unix_listen_opts(QemuOpts *opts)
int unix_nonblocking_connect(const char *path,
NonBlockingConnectHandler *callback,
void *opaque, Error **errp)
{
fprintf(stderr, "unix sockets are not available on windows\n");
errno = ENOTSUP;
return -1;
QemuOpts *opts;
int sock = -1;
g_assert(callback != NULL);
opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
qemu_opt_set(opts, "path", path);
sock = unix_connect_opts(opts, errp, callback, opaque);
qemu_opts_del(opts);
return sock;
}
int unix_connect_opts(QemuOpts *opts)
SocketAddress *socket_parse(const char *str, Error **errp)
{
fprintf(stderr, "unix sockets are not available on windows\n");
errno = ENOTSUP;
return -1;
SocketAddress *addr = NULL;
addr = g_new(SocketAddress, 1);
if (strstart(str, "unix:", NULL)) {
if (str[5] == '\0') {
error_setg(errp, "invalid Unix socket address\n");
goto fail;
} else {
addr->kind = SOCKET_ADDRESS_KIND_UNIX;
addr->q_unix = g_new(UnixSocketAddress, 1);
addr->q_unix->path = g_strdup(str + 5);
}
} else if (strstart(str, "fd:", NULL)) {
if (str[3] == '\0') {
error_setg(errp, "invalid file descriptor address\n");
goto fail;
} else {
addr->kind = SOCKET_ADDRESS_KIND_FD;
addr->fd = g_new(String, 1);
addr->fd->str = g_strdup(str + 3);
}
} else {
addr->kind = SOCKET_ADDRESS_KIND_INET;
addr->inet = g_new(InetSocketAddress, 1);
addr->inet = inet_parse(str, errp);
if (addr->inet == NULL) {
goto fail;
}
}
return addr;
fail:
qapi_free_SocketAddress(addr);
return NULL;
}
int unix_listen(const char *path, char *ostr, int olen)
int socket_connect(SocketAddress *addr, Error **errp,
NonBlockingConnectHandler *callback, void *opaque)
{
fprintf(stderr, "unix sockets are not available on windows\n");
errno = ENOTSUP;
return -1;
QemuOpts *opts;
int fd;
opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
switch (addr->kind) {
case SOCKET_ADDRESS_KIND_INET:
inet_addr_to_opts(opts, addr->inet);
fd = inet_connect_opts(opts, errp, callback, opaque);
break;
case SOCKET_ADDRESS_KIND_UNIX:
qemu_opt_set(opts, "path", addr->q_unix->path);
fd = unix_connect_opts(opts, errp, callback, opaque);
break;
case SOCKET_ADDRESS_KIND_FD:
fd = monitor_get_fd(cur_mon, addr->fd->str, errp);
if (callback) {
callback(fd, opaque);
}
break;
default:
abort();
}
qemu_opts_del(opts);
return fd;
}
int unix_connect(const char *path)
int socket_listen(SocketAddress *addr, Error **errp)
{
fprintf(stderr, "unix sockets are not available on windows\n");
errno = ENOTSUP;
return -1;
}
QemuOpts *opts;
int fd;
#endif
opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
switch (addr->kind) {
case SOCKET_ADDRESS_KIND_INET:
inet_addr_to_opts(opts, addr->inet);
fd = inet_listen_opts(opts, 0, errp);
break;
case SOCKET_ADDRESS_KIND_UNIX:
qemu_opt_set(opts, "path", addr->q_unix->path);
fd = unix_listen_opts(opts, errp);
break;
case SOCKET_ADDRESS_KIND_FD:
fd = monitor_get_fd(cur_mon, addr->fd->str, errp);
break;
default:
abort();
}
qemu_opts_del(opts);
return fd;
}
#ifdef _WIN32
static void socket_cleanup(void)

View file

@ -38,6 +38,12 @@ const char *qemu_get_vm_name(void)
Monitor *cur_mon;
int monitor_get_fd(Monitor *mon, const char *name, Error **errp)
{
error_setg(errp, "only QEMU supports file descriptor passing");
return -1;
}
void vm_stop(RunState state)
{
abort();

View file

@ -53,13 +53,22 @@ int inet_nonblocking_connect(const char *str,
NonBlockingConnectHandler *callback,
void *opaque, Error **errp);
int inet_dgram_opts(QemuOpts *opts);
int inet_dgram_opts(QemuOpts *opts, Error **errp);
const char *inet_strfamily(int family);
int unix_listen_opts(QemuOpts *opts);
int unix_listen(const char *path, char *ostr, int olen);
int unix_connect_opts(QemuOpts *opts);
int unix_connect(const char *path);
int unix_listen_opts(QemuOpts *opts, Error **errp);
int unix_listen(const char *path, char *ostr, int olen, Error **errp);
int unix_connect_opts(QemuOpts *opts, Error **errp,
NonBlockingConnectHandler *callback, void *opaque);
int unix_connect(const char *path, Error **errp);
int unix_nonblocking_connect(const char *str,
NonBlockingConnectHandler *callback,
void *opaque, Error **errp);
SocketAddress *socket_parse(const char *str, Error **errp);
int socket_connect(SocketAddress *addr, Error **errp,
NonBlockingConnectHandler *callback, void *opaque);
int socket_listen(SocketAddress *addr, Error **errp);
/* Old, ipv4 only bits. Don't use for new code. */
int parse_host_port(struct sockaddr_in *saddr, const char *str);

View file

@ -237,9 +237,6 @@ void assert_no_error(Error *err);
#define QERR_VIRTFS_FEATURE_BLOCKS_MIGRATION \
ERROR_CLASS_GENERIC_ERROR, "Migration is disabled when VirtFS export path '%s' is mounted in the guest using mount_tag '%s'"
#define QERR_VNC_SERVER_FAILED \
ERROR_CLASS_GENERIC_ERROR, "Could not start VNC server on %s"
#define QERR_SOCKET_CONNECT_FAILED \
ERROR_CLASS_GENERIC_ERROR, "Failed to connect to socket"

View file

@ -181,9 +181,11 @@ static gboolean ga_channel_open(GAChannel *c, const gchar *path, GAChannelMethod
break;
}
case GA_CHANNEL_UNIX_LISTEN: {
int fd = unix_listen(path, NULL, strlen(path));
if (fd == -1) {
g_critical("error opening path: %s", strerror(errno));
Error *local_err = NULL;
int fd = unix_listen(path, NULL, strlen(path), &local_err);
if (local_err != NULL) {
g_critical("%s", error_get_pretty(local_err));
error_free(local_err);
return false;
}
ga_channel_listen_add(c, fd, true);

View file

@ -2551,6 +2551,22 @@ EQMP
.mhandler.cmd_new = qmp_qom_get,
},
{
.name = "nbd-server-start",
.args_type = "addr:q",
.mhandler.cmd_new = qmp_marshal_input_nbd_server_start,
},
{
.name = "nbd-server-add",
.args_type = "device:B,writable:b?",
.mhandler.cmd_new = qmp_marshal_input_nbd_server_add,
},
{
.name = "nbd-server-stop",
.args_type = "",
.mhandler.cmd_new = qmp_marshal_input_nbd_server_stop,
},
{
.name = "change-vnc-password",
.args_type = "password:s",

6
qmp.c
View file

@ -349,11 +349,9 @@ void qmp_change_vnc_password(const char *password, Error **errp)
}
}
static void qmp_change_vnc_listen(const char *target, Error **err)
static void qmp_change_vnc_listen(const char *target, Error **errp)
{
if (vnc_display_open(NULL, target) < 0) {
error_set(err, QERR_VNC_SERVER_FAILED, target);
}
vnc_display_open(NULL, target, errp);
}
static void qmp_change_vnc(const char *target, bool has_arg, const char *arg,

View file

@ -42,11 +42,11 @@ test-qapi-obj-y += module.o
$(test-obj-y): QEMU_INCLUDES += -Itests
tests/check-qint$(EXESUF): tests/check-qint.o qint.o $(tools-obj-y)
tests/check-qstring$(EXESUF): tests/check-qstring.o qstring.o $(tools-obj-y)
tests/check-qdict$(EXESUF): tests/check-qdict.o qdict.o qfloat.o qint.o qstring.o qbool.o qlist.o $(tools-obj-y)
tests/check-qlist$(EXESUF): tests/check-qlist.o qlist.o qint.o $(tools-obj-y)
tests/check-qfloat$(EXESUF): tests/check-qfloat.o qfloat.o $(tools-obj-y)
tests/check-qint$(EXESUF): tests/check-qint.o qint.o
tests/check-qstring$(EXESUF): tests/check-qstring.o qstring.o
tests/check-qdict$(EXESUF): tests/check-qdict.o qdict.o qfloat.o qint.o qstring.o qbool.o qlist.o
tests/check-qlist$(EXESUF): tests/check-qlist.o qlist.o qint.o
tests/check-qfloat$(EXESUF): tests/check-qfloat.o qfloat.o
tests/check-qjson$(EXESUF): tests/check-qjson.o $(qobject-obj-y) $(tools-obj-y)
tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(coroutine-obj-y) $(tools-obj-y)
tests/test-iov$(EXESUF): tests/test-iov.o iov.o

View file

@ -2850,7 +2850,7 @@ char *vnc_display_local_addr(DisplayState *ds)
return vnc_socket_local_addr("%s:%s", vs->lsock);
}
int vnc_display_open(DisplayState *ds, const char *display)
void vnc_display_open(DisplayState *ds, const char *display, Error **errp)
{
VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
const char *options;
@ -2868,14 +2868,15 @@ int vnc_display_open(DisplayState *ds, const char *display)
#endif
int lock_key_sync = 1;
if (!vnc_display)
return -1;
if (!vnc_display) {
error_setg(errp, "VNC display not active");
return;
}
vnc_display_close(ds);
if (strcmp(display, "none") == 0)
return 0;
return;
if (!(vs->display = strdup(display)))
return -1;
vs->display = g_strdup(display);
vs->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE;
options = display;
@ -2883,13 +2884,11 @@ int vnc_display_open(DisplayState *ds, const char *display)
options++;
if (strncmp(options, "password", 8) == 0) {
if (fips_get_state()) {
fprintf(stderr,
"VNC password auth disabled due to FIPS mode, "
"consider using the VeNCrypt or SASL authentication "
"methods as an alternative\n");
g_free(vs->display);
vs->display = NULL;
return -1;
error_setg(errp,
"VNC password auth disabled due to FIPS mode, "
"consider using the VeNCrypt or SASL authentication "
"methods as an alternative");
goto fail;
}
password = 1; /* Require password auth */
} else if (strncmp(options, "reverse", 7) == 0) {
@ -2919,18 +2918,14 @@ int vnc_display_open(DisplayState *ds, const char *display)
VNC_DEBUG("Trying certificate path '%s'\n", path);
if (vnc_tls_set_x509_creds_dir(vs, path) < 0) {
fprintf(stderr, "Failed to find x509 certificates/keys in %s\n", path);
error_setg(errp, "Failed to find x509 certificates/keys in %s", path);
g_free(path);
g_free(vs->display);
vs->display = NULL;
return -1;
goto fail;
}
g_free(path);
} else {
fprintf(stderr, "No certificate path provided\n");
g_free(vs->display);
vs->display = NULL;
return -1;
error_setg(errp, "No certificate path provided");
goto fail;
}
#endif
#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL)
@ -2949,10 +2944,8 @@ int vnc_display_open(DisplayState *ds, const char *display)
} else if (strncmp(options+6, "force-shared", 12) == 0) {
vs->share_policy = VNC_SHARE_POLICY_FORCE_SHARED;
} else {
fprintf(stderr, "unknown vnc share= option\n");
g_free(vs->display);
vs->display = NULL;
return -1;
error_setg(errp, "unknown vnc share= option");
goto fail;
}
}
}
@ -3053,52 +3046,50 @@ int vnc_display_open(DisplayState *ds, const char *display)
#ifdef CONFIG_VNC_SASL
if ((saslErr = sasl_server_init(NULL, "qemu")) != SASL_OK) {
fprintf(stderr, "Failed to initialize SASL auth %s",
sasl_errstring(saslErr, NULL, NULL));
g_free(vs->display);
vs->display = NULL;
return -1;
error_setg(errp, "Failed to initialize SASL auth: %s",
sasl_errstring(saslErr, NULL, NULL));
goto fail;
}
#endif
vs->lock_key_sync = lock_key_sync;
if (reverse) {
/* connect to viewer */
if (strncmp(display, "unix:", 5) == 0)
vs->lsock = unix_connect(display+5);
else
vs->lsock = inet_connect(display, NULL);
if (-1 == vs->lsock) {
g_free(vs->display);
vs->display = NULL;
return -1;
int csock;
vs->lsock = -1;
if (strncmp(display, "unix:", 5) == 0) {
csock = unix_connect(display+5, errp);
} else {
int csock = vs->lsock;
vs->lsock = -1;
vnc_connect(vs, csock, 0);
csock = inet_connect(display, errp);
}
return 0;
if (csock < 0) {
goto fail;
}
vnc_connect(vs, csock, 0);
} else {
/* listen for connects */
char *dpy;
dpy = g_malloc(256);
if (strncmp(display, "unix:", 5) == 0) {
pstrcpy(dpy, 256, "unix:");
vs->lsock = unix_listen(display+5, dpy+5, 256-5);
vs->lsock = unix_listen(display+5, dpy+5, 256-5, errp);
} else {
vs->lsock = inet_listen(display, dpy, 256,
SOCK_STREAM, 5900, NULL);
SOCK_STREAM, 5900, errp);
}
if (-1 == vs->lsock) {
if (vs->lsock < 0) {
g_free(dpy);
return -1;
} else {
g_free(vs->display);
vs->display = dpy;
goto fail;
}
g_free(vs->display);
vs->display = dpy;
qemu_set_fd_handler2(vs->lsock, NULL, vnc_listen_read, NULL, vs);
}
return qemu_set_fd_handler2(vs->lsock, NULL, vnc_listen_read, NULL, vs);
return;
fail:
g_free(vs->display);
vs->display = NULL;
}
void vnc_display_add_client(DisplayState *ds, int csock, int skipauth)

25
vl.c
View file

@ -3711,10 +3711,13 @@ int main(int argc, char **argv, char **envp)
#ifdef CONFIG_VNC
/* init remote displays */
if (vnc_display) {
Error *local_err = NULL;
vnc_display_init(ds);
if (vnc_display_open(ds, vnc_display) < 0) {
fprintf(stderr, "Failed to start VNC server on `%s'\n",
vnc_display);
vnc_display_open(ds, vnc_display, &local_err);
if (local_err != NULL) {
fprintf(stderr, "Failed to start VNC server on `%s': %s\n",
vnc_display, error_get_pretty(local_err));
error_free(local_err);
exit(1);
}
@ -3766,16 +3769,12 @@ int main(int argc, char **argv, char **envp)
}
if (incoming) {
Error *errp = NULL;
int ret = qemu_start_incoming_migration(incoming, &errp);
if (ret < 0) {
if (error_is_set(&errp)) {
fprintf(stderr, "Migrate: %s\n", error_get_pretty(errp));
error_free(errp);
}
fprintf(stderr, "Migration failed. Exit code %s(%d), exiting.\n",
incoming, ret);
exit(ret);
Error *local_err = NULL;
qemu_start_incoming_migration(incoming, &local_err);
if (local_err) {
fprintf(stderr, "-incoming %s: %s\n", incoming, error_get_pretty(local_err));
error_free(local_err);
exit(1);
}
} else if (autostart) {
vm_start();