mirror of
https://github.com/xemu-project/xemu.git
synced 2024-11-24 20:19:44 +00:00
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:
commit
c6b8141b84
5
Makefile
5
Makefile
@ -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)
|
||||
|
||||
|
@ -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
19
block.c
@ -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;
|
||||
}
|
||||
|
1
block.h
1
block.h
@ -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);
|
||||
|
@ -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
119
blockdev-nbd.c
Normal 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;
|
||||
}
|
@ -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
28
error.c
@ -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;
|
||||
|
9
error.h
9
error.h
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
34
migration.c
34
migration.c
@ -64,26 +64,23 @@ MigrationState *migrate_get_current(void)
|
||||
return ¤t_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(¶ms);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
19
migration.h
19
migration.h
@ -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
39
nbd.c
@ -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
|
||||
|
@ -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' }
|
||||
|
24
qemu-char.c
24
qemu-char.c
@ -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;
|
||||
|
426
qemu-sockets.c
426
qemu-sockets.c
@ -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)
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
3
qerror.h
3
qerror.h
@ -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"
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
6
qmp.c
@ -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,
|
||||
|
@ -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
|
||||
|
93
ui/vnc.c
93
ui/vnc.c
@ -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
25
vl.c
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user