diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index eccef8386e..160e0ff878 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -6513,23 +6513,88 @@ static int doTunnelMigrate(virDomainPtr dom, unsigned long resource) { struct qemud_driver *driver = dom->conn->privateData; - int client_sock, qemu_sock; + int client_sock = -1; + int qemu_sock = -1; struct sockaddr_un sa_qemu, sa_client; socklen_t addrlen; - virDomainPtr ddomain; + virDomainPtr ddomain = NULL; int retval = -1; - ssize_t bytes; - virStreamPtr st; + virStreamPtr st = NULL; char *unixfile = NULL; int internalret; unsigned int qemuCmdFlags; int status; unsigned long long transferred, remaining, total; - /* the order of operations is important here; we make sure the - * destination side is completely setup before we touch the source + /* + * The order of operations is important here to avoid touching + * the source VM until we are very sure we can successfully + * start the migration operation. + * + * 1. setup local support infrastructure (eg sockets) + * 2. setup destination fully + * 3. start migration on source */ + + /* Stage 1. setup local support infrastructure */ + + if (virAsprintf(&unixfile, "%s/qemu.tunnelmigrate.src.%s", + driver->stateDir, vm->def->name) < 0) { + virReportOOMError(dom->conn); + goto cleanup; + } + + qemu_sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (qemu_sock < 0) { + virReportSystemError(dom->conn, errno, "%s", + _("cannot open tunnelled migration socket")); + goto cleanup; + } + memset(&sa_qemu, 0, sizeof(sa_qemu)); + sa_qemu.sun_family = AF_UNIX; + if (virStrcpy(sa_qemu.sun_path, unixfile, + sizeof(sa_qemu.sun_path)) == NULL) { + qemudReportError(dom->conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("Unix socket '%s' too big for destination"), + unixfile); + goto cleanup; + } + unlink(unixfile); + if (bind(qemu_sock, (struct sockaddr *)&sa_qemu, sizeof(sa_qemu)) < 0) { + virReportSystemError(dom->conn, errno, + _("Cannot bind to unix socket '%s' for tunnelled migration"), + unixfile); + goto cleanup; + } + if (listen(qemu_sock, 1) < 0) { + virReportSystemError(dom->conn, errno, + _("Cannot listen on unix socket '%s' for tunnelled migration"), + unixfile); + goto cleanup; + } + + /* check that this qemu version supports the unix migration */ + if (qemudExtractVersionInfo(vm->def->emulator, NULL, &qemuCmdFlags) < 0) { + qemudReportError(dom->conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("Cannot extract Qemu version from '%s'"), + vm->def->emulator); + goto cleanup; + } + + if (!(qemuCmdFlags & QEMUD_CMD_FLAG_MIGRATE_QEMU_UNIX) && + !(qemuCmdFlags & QEMUD_CMD_FLAG_MIGRATE_QEMU_EXEC)) { + qemudReportError(dom->conn, NULL, NULL, VIR_ERR_OPERATION_FAILED, + "%s", _("Source qemu is too old to support tunnelled migration")); + goto cleanup; + } + + + /* Stage 2. setup destination fully + * + * Once stage 2 has completed successfully, we *must* call finish + * to cleanup the target whether we succeed or fail + */ st = virStreamNew(dconn, 0); if (st == NULL) /* virStreamNew only fails on OOM, and it reports the error itself */ @@ -6541,67 +6606,26 @@ static int doTunnelMigrate(virDomainPtr dom, if (internalret < 0) /* domainMigratePrepareTunnel sets the error for us */ - goto close_stream; + goto cleanup; - if (virAsprintf(&unixfile, "%s/qemu.tunnelmigrate.src.%s", - driver->stateDir, vm->def->name) < 0) { - virReportOOMError(dom->conn); - goto finish_migrate; - } - - qemu_sock = socket(AF_UNIX, SOCK_STREAM, 0); - if (qemu_sock < 0) { - virReportSystemError(dom->conn, errno, "%s", - _("cannot open tunnelled migration socket")); - goto free_unix_path; - } - memset(&sa_qemu, 0, sizeof(sa_qemu)); - sa_qemu.sun_family = AF_UNIX; - if (virStrcpy(sa_qemu.sun_path, unixfile, - sizeof(sa_qemu.sun_path)) == NULL) { - qemudReportError(dom->conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("Unix socket '%s' too big for destination"), - unixfile); - goto close_qemu_sock; - } - unlink(unixfile); - if (bind(qemu_sock, (struct sockaddr *)&sa_qemu, sizeof(sa_qemu)) < 0) { - virReportSystemError(dom->conn, errno, - _("Cannot bind to unix socket '%s' for tunnelled migration"), - unixfile); - goto close_qemu_sock; - } - if (listen(qemu_sock, 1) < 0) { - virReportSystemError(dom->conn, errno, - _("Cannot listen on unix socket '%s' for tunnelled migration"), - unixfile); - goto close_qemu_sock; - } - - /* check that this qemu version supports the unix migration */ - if (qemudExtractVersionInfo(vm->def->emulator, NULL, &qemuCmdFlags) < 0) { - qemudReportError(dom->conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("Cannot extract Qemu version from '%s'"), - vm->def->emulator); - goto close_qemu_sock; - } + /* 3. start migration on source */ if (qemuCmdFlags & QEMUD_CMD_FLAG_MIGRATE_QEMU_UNIX) internalret = qemuMonitorMigrateToUnix(vm, 1, unixfile); else if (qemuCmdFlags & QEMUD_CMD_FLAG_MIGRATE_QEMU_EXEC) { const char *args[] = { "nc", "-U", unixfile, NULL }; internalret = qemuMonitorMigrateToCommand(vm, 1, args, "/dev/null"); - } - else { - qemudReportError(dom->conn, NULL, NULL, VIR_ERR_OPERATION_FAILED, - "%s", _("Source qemu is too old to support tunnelled migration")); - goto close_qemu_sock; + } else { + internalret = -1; } if (internalret < 0) { qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, "%s", _("tunnelled migration monitor command failed")); - goto close_qemu_sock; + goto finish; } + /* From this point onwards we *must* call cancel to abort the + * migration on source if anything goes wrong */ + /* it is also possible that the migrate didn't fail initially, but * rather failed later on. Check the output of "info migrate" */ @@ -6609,13 +6633,13 @@ static int doTunnelMigrate(virDomainPtr dom, &transferred, &remaining, &total) < 0) { - goto qemu_cancel_migration; + goto cancel; } if (status == QEMU_MONITOR_MIGRATION_STATUS_ERROR) { qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, "%s",_("migrate failed")); - goto qemu_cancel_migration; + goto cancel; } addrlen = sizeof(sa_client); @@ -6624,36 +6648,37 @@ static int doTunnelMigrate(virDomainPtr dom, continue; virReportSystemError(dom->conn, errno, "%s", _("tunnelled migration failed to accept from qemu")); - goto qemu_cancel_migration; + goto cancel; } retval = doTunnelSendAll(dom, st, client_sock); - close(client_sock); - -qemu_cancel_migration: +cancel: if (retval != 0) qemuMonitorMigrateCancel(vm); -close_qemu_sock: - close(qemu_sock); - -free_unix_path: - unlink(unixfile); - VIR_FREE(unixfile); - -finish_migrate: +finish: dname = dname ? dname : dom->name; ddomain = dconn->driver->domainMigrateFinish2 (dconn, dname, NULL, 0, uri, flags, retval); + +cleanup: + if (client_sock != -1) + close(client_sock); + if (qemu_sock != -1) + close(qemu_sock); + if (ddomain) virUnrefDomain(ddomain); -close_stream: - /* don't call virStreamFree(), because that resets any pending errors */ - virUnrefStream(st); + if (unixfile) { + unlink(unixfile); + VIR_FREE(unixfile); + } -cleanup: + if (st) + /* don't call virStreamFree(), because that resets any pending errors */ + virUnrefStream(st); return retval; }