Sometimes you want to be able to start up the receiving side of a live migration
and actually be able to run monitor commands before you do the migration.
Libvirt, in particular, wants to do this for setting up the migration.  This
patch implements a "nowait" option to the receiving side so that you start up
the receiving side similar to:

qemu-kvm -M pc -S blah blah -incoming tcp://0:4444,nowait

Then you are able to interact with the monitor before the live migration takes
place.

Signed-off-by: Chris Lalancette <[EMAIL PROTECTED]>
diff --git a/qemu/migration.c b/qemu/migration.c
index a64a287..d16e289 100644
--- a/qemu/migration.c
+++ b/qemu/migration.c
@@ -886,13 +886,10 @@ static int migrate_incoming_fd(int fd)
     return ret;
 }
 
-static int migrate_incoming_tcp(const char *host)
+static int migrate_listen_tcp(const char *host, int *outfd)
 {
     struct sockaddr_in addr;
-    socklen_t addrlen = sizeof(addr);
-    int fd, sfd;
-    ssize_t len;
-    uint8_t status = 0;
+    int fd = -1;
     int reuse = 1;
     int rc;
 
@@ -928,19 +925,43 @@ static int migrate_incoming_tcp(const char *host)
 	goto error_socket;
     }
 
+    *outfd = fd;
+
+    return 0;
+
+error_socket:
+    close(fd);
+error:
+    return rc;
+}
+
+struct migrate_tcp_data {
+    int listen_fd;
+    int rc;
+};
+
+static void migrate_incoming_tcp(void *opaque)
+{
+    struct sockaddr_in addr;
+    socklen_t addrlen = sizeof(addr);
+    struct migrate_tcp_data *data = (struct migrate_tcp_data *)opaque;
+    int sfd;
+    ssize_t len;
+    uint8_t status = 0;
+
 again:
-    sfd = accept(fd, (struct sockaddr *)&addr, &addrlen);
+    sfd = accept(data->listen_fd, (struct sockaddr *)&addr, &addrlen);
     if (sfd == -1) {
 	if (errno == EINTR)
 	    goto again;
         perror("accept() failed");
-        rc = MIG_STAT_DST_ACCEPT_FAILED;
+        data->rc = MIG_STAT_DST_ACCEPT_FAILED;
 	goto error_socket;
     }
 
-    rc = migrate_incoming_fd(sfd);
-    if (rc != 0) {
-        fprintf(stderr, "migrate_incoming_fd failed (rc=%d)\n", rc);
+    data->rc = migrate_incoming_fd(sfd);
+    if (data->rc != 0) {
+        fprintf(stderr, "migrate_incoming_fd failed (rc=%d)\n", data->rc);
 	goto error_accept;
     }
 
@@ -951,13 +972,13 @@ send_ack:
     if (len != 1) {
         fprintf(stderr, "migration: send_ack: write error len=%zu (%s)\n",
                 len, strerror(errno));
-        rc = MIG_STAT_DST_WRITE_FAILED;
+        data->rc = MIG_STAT_DST_WRITE_FAILED;
 	goto error_accept;
     }
 
-    rc = wait_for_message("WAIT FOR GO", sfd, wait_for_message_timeout);
-    if (rc) {
-        rc += 200;
+    data->rc = wait_for_message("WAIT FOR GO", sfd, wait_for_message_timeout);
+    if (data->rc) {
+        data->rc += 200;
         goto error_accept;
     }
 
@@ -966,7 +987,7 @@ wait_for_go:
     if (len == -1 && errno == EAGAIN)
 	goto wait_for_go;
     if (len != 1) {
-        rc = MIG_STAT_DST_READ_FAILED;
+        data->rc = MIG_STAT_DST_READ_FAILED;
         fprintf(stderr, "migration: wait_for_go: read error len=%zu (%s)\n",
                 len, strerror(errno));
     }
@@ -974,9 +995,10 @@ wait_for_go:
 error_accept:
     close(sfd);
 error_socket:
-    close(fd);
-error:
-    return rc;
+    qemu_set_fd_handler(data->listen_fd, NULL, NULL, NULL);
+    close(data->listen_fd);
+
+    qemu_free(data);
 }
 
 int migrate_incoming(const char *device)
@@ -996,16 +1018,57 @@ int migrate_incoming(const char *device)
 	}
     } else if (strstart(device, "tcp://", &ptr)) {
 	char *host, *end;
+	struct migrate_tcp_data *data;
+	int is_waitconnect = 1;
+
 	host = strdup(ptr);
+	if (!host)
+	    goto fail;
 	end = strchr(host, '/');
 	if (end) *end = 0;
-	ret = migrate_incoming_tcp(host);
+
+	data = qemu_mallocz(sizeof(struct migrate_tcp_data));
+	if (!data) {
+	    qemu_free(host);
+	    goto fail;
+	}
+
+	ptr = host;
+	while((ptr = strchr(ptr,','))) {
+	    ptr++;
+	    if (!strncmp(ptr,"nowait",6)) {
+	        is_waitconnect = 0;
+	    } else {
+	        printf("Unknown option: %s\n", ptr);
+		qemu_free(host);
+	        goto fail;
+	    }
+	}
+
+	ret = migrate_listen_tcp(host, &(data->listen_fd));
 	qemu_free(host);
+	if (ret != 0)
+	    goto fail;
+
+	/*
+	 * if we made it here, then migrate_incoming_tcp is responsible for
+	 * freeing the "data" structure
+	 */
+	if (!is_waitconnect) {
+	    socket_set_nonblock(data->listen_fd);
+	    qemu_set_fd_handler(data->listen_fd, migrate_incoming_tcp, NULL, data);
+	}
+	else {
+	    migrate_incoming_tcp(data);
+	    ret = data->rc;
+	}
+
     } else {
 	errno = EINVAL;
 	ret = MIG_STAT_DST_INVALID_PARAMS;
     }
 
+ fail:
     return ret;
 }
 

Reply via email to