diff options
author | Anthony Liguori <aliguori@us.ibm.com> | 2012-10-24 09:39:49 -0500 |
---|---|---|
committer | Anthony Liguori <aliguori@us.ibm.com> | 2012-10-24 09:39:49 -0500 |
commit | c6b8141b84e1b80eff1f04e2a5feb38decae65d8 (patch) | |
tree | 0be44fcd574751487e2e4bb932897881a2ee0966 | |
parent | a8170e5e97ad17ca169c64ba87ae2f53850dab4c (diff) | |
parent | 6dd844db4a49a367cc15cd0e8bfb72cfc6652766 (diff) | |
download | qemu-c6b8141b84e1b80eff1f04e2a5feb38decae65d8.zip qemu-c6b8141b84e1b80eff1f04e2a5feb38decae65d8.tar.gz qemu-c6b8141b84e1b80eff1f04e2a5feb38decae65d8.tar.bz2 |
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
...
-rw-r--r-- | Makefile | 5 | ||||
-rw-r--r-- | Makefile.objs | 9 | ||||
-rw-r--r-- | block.c | 19 | ||||
-rw-r--r-- | block.h | 1 | ||||
-rw-r--r-- | block_int.h | 2 | ||||
-rw-r--r-- | blockdev-nbd.c | 119 | ||||
-rw-r--r-- | console.h | 2 | ||||
-rw-r--r-- | error.c | 28 | ||||
-rw-r--r-- | error.h | 9 | ||||
-rw-r--r-- | migration-exec.c | 26 | ||||
-rw-r--r-- | migration-fd.c | 27 | ||||
-rw-r--r-- | migration-tcp.c | 19 | ||||
-rw-r--r-- | migration-unix.c | 95 | ||||
-rw-r--r-- | migration.c | 34 | ||||
-rw-r--r-- | migration.h | 19 | ||||
-rw-r--r-- | nbd.c | 39 | ||||
-rw-r--r-- | qapi-schema.json | 96 | ||||
-rw-r--r-- | qemu-char.c | 24 | ||||
-rw-r--r-- | qemu-sockets.c | 424 | ||||
-rw-r--r-- | qemu-tool.c | 6 | ||||
-rw-r--r-- | qemu_socket.h | 19 | ||||
-rw-r--r-- | qerror.h | 3 | ||||
-rw-r--r-- | qga/channel-posix.c | 8 | ||||
-rw-r--r-- | qmp-commands.hx | 16 | ||||
-rw-r--r-- | qmp.c | 6 | ||||
-rw-r--r-- | tests/Makefile | 10 | ||||
-rw-r--r-- | ui/vnc.c | 93 | ||||
-rw-r--r-- | vl.c | 25 |
28 files changed, 763 insertions, 420 deletions
@@ -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) diff --git a/Makefile.objs b/Makefile.objs index 74b3542..9eca179 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -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 @@ -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; } @@ -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); diff --git a/block_int.h b/block_int.h index f4bae04..cedabbd 100644 --- a/block_int.h +++ b/block_int.h @@ -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; diff --git a/blockdev-nbd.c b/blockdev-nbd.c new file mode 100644 index 0000000..8031813 --- /dev/null +++ b/blockdev-nbd.c @@ -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); @@ -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; @@ -30,10 +30,19 @@ 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 diff --git a/migration-exec.c b/migration-exec.c index 6c97db9..519af57 100644 --- a/migration-exec.c +++ b/migration-exec.c @@ -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; } diff --git a/migration-fd.c b/migration-fd.c index 7335167..ce6932d 100644 --- a/migration-fd.c +++ b/migration-fd.c @@ -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; } diff --git a/migration-tcp.c b/migration-tcp.c index a15c2b8..46f6ac5 100644 --- a/migration-tcp.c +++ b/migration-tcp.c @@ -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; } diff --git a/migration-unix.c b/migration-unix.c index 169de88..ed3db3a 100644 --- a/migration-unix.c +++ b/migration-unix.c @@ -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; } diff --git a/migration.c b/migration.c index 62e0304..bd55a15 100644 --- a/migration.c +++ b/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; } diff --git a/migration.h b/migration.h index 1c3e9b7..c3a23cc 100644 --- a/migration.h +++ b/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); @@ -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 diff --git a/qapi-schema.json b/qapi-schema.json index c615ee2..6fd263e 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -2519,6 +2519,59 @@ '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: # # Receive a file descriptor via SCM rights and assign it a name @@ -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' } diff --git a/qemu-char.c b/qemu-char.c index b082bae..afe2bfb 100644 --- a/qemu-char.c +++ b/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; diff --git a/qemu-sockets.c b/qemu-sockets.c index 2b1ed2f..cfed9c5 100644 --- a/qemu-sockets.c +++ b/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)); + + /* 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); - return -1; + 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) diff --git a/qemu-tool.c b/qemu-tool.c index f2f9813..da4c05a 100644 --- a/qemu-tool.c +++ b/qemu-tool.c @@ -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(); diff --git a/qemu_socket.h b/qemu_socket.h index 3e8aee9..02490ad 100644 --- a/qemu_socket.h +++ b/qemu_socket.h @@ -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); @@ -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" diff --git a/qga/channel-posix.c b/qga/channel-posix.c index 57eea06..d152827 100644 --- a/qga/channel-posix.c +++ b/qga/channel-posix.c @@ -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); diff --git a/qmp-commands.hx b/qmp-commands.hx index 5ba8c48..ebe9a78 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -2552,6 +2552,22 @@ EQMP }, { + .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", .mhandler.cmd_new = qmp_marshal_input_change_vnc_password, @@ -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, diff --git a/tests/Makefile b/tests/Makefile index 26a67ce..86c9b79 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -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 @@ -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) @@ -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(); |