[Pkg-libvirt-commits] [libguestfs] 121/146: p2v: Fix wait_qemu_nbd to use getaddrinfo.

Hilko Bengen bengen at moszumanska.debian.org
Sun Mar 29 17:01:17 UTC 2015


This is an automated email from the git hooks/post-receive script.

bengen pushed a commit to branch master
in repository libguestfs.

commit 768d7fa79a6163bbb7ede73eed61439b38ddea1a
Author: Richard W.M. Jones <rjones at redhat.com>
Date:   Tue Jan 20 09:42:12 2015 +0000

    p2v: Fix wait_qemu_nbd to use getaddrinfo.
    
    wait_qemu_nbd would fail in Rawhide where /etc/hosts contains:
    
      127.0.0.1   localhost [...]
      ::1         localhost [...]
    
    This peculiar mapping caused qemu-nbd to always select an IPv6
    address.  However since we used the obsolete inet_pton function and
    forced AF_INET, we were always trying to connect to the IPv4
    localhost.
    
    As this was never going to work anyway, fix the code to use
    getaddrinfo to resolve "localhost", and use a loop to connect to all
    possible addresses.
    
    Also: Add a short sleep in the reconnection loop so that we don't
    hammer the qemu-nbd server while it is starting up.
    
    (cherry picked from commit 91c34ff8ff59a45accf179394257e36cff6a30e9)
---
 p2v/conversion.c | 174 +++++++++++++++++++++++++++++++++++++++----------------
 1 file changed, 124 insertions(+), 50 deletions(-)

diff --git a/p2v/conversion.c b/p2v/conversion.c
index f30fc63..007006f 100644
--- a/p2v/conversion.c
+++ b/p2v/conversion.c
@@ -29,10 +29,9 @@
 #include <errno.h>
 #include <locale.h>
 #include <libintl.h>
+#include <netdb.h>
 #include <sys/types.h>
 #include <sys/wait.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
 
 #include <glib.h>
 
@@ -393,72 +392,147 @@ start_qemu_nbd (int port, const char *device)
   return pid;
 }
 
+/* Connect to a host/port, resolving the address using getaddrinfo and
+ * setting the source port kernel hint.  This may involve multiple
+ * connections - to IPv4 and IPv6 for instance.
+ */
+static int bind_source_port (int sockfd, int family, int source_port);
+
 static int
-wait_qemu_nbd (int nbd_local_port, int timeout_seconds)
+connect_with_source_port (const char *hostname, int dest_port, int source_port)
 {
-  int sockfd;
-  int result = -1;
+  struct addrinfo hints;
+  struct addrinfo *results, *rp;
+  char dest_port_str[16];
+  int r, sockfd = -1;
   int reuseaddr = 1;
-  struct sockaddr_in src_addr, dst_addr;
-  time_t start_t, now_t;
-  struct timeval timeout = { .tv_usec = 0 };
-  char magic[8]; /* NBDMAGIC */
-  size_t bytes_read = 0;
-  ssize_t recvd;
 
-  time (&start_t);
+  snprintf (dest_port_str, sizeof dest_port_str, "%d", dest_port);
+
+  memset (&hints, 0, sizeof hints);
+  hints.ai_family = AF_UNSPEC;     /* allow IPv4 or IPv6 */
+  hints.ai_socktype = SOCK_STREAM;
+  hints.ai_flags = AI_NUMERICSERV; /* numeric dest port number */
+  hints.ai_protocol = 0;           /* any protocol */
 
-  sockfd = socket (AF_INET, SOCK_STREAM, 0);
-  if (sockfd == -1) {
-    perror ("socket");
+  r = getaddrinfo (hostname, dest_port_str, &hints, &results);
+  if (r != 0) {
+    set_conversion_error ("getaddrinfo: %s/%s: %s",
+                          hostname, dest_port_str, gai_strerror (r));
     return -1;
   }
 
-  memset (&src_addr, 0, sizeof src_addr);
-  src_addr.sin_family = AF_INET;
-  /* Source port for probing qemu-nbd should be one greater than
-   * nbd_local_port.  It's not guaranteed to always bind to this port,
-   * but it will hint the kernel to start there and try incrementally
-   * higher ports if needed.  This avoids the case where the kernel
-   * selects nbd_local_port as our source port, and we immediately
-   * connect to ourself.  See:
-   * https://bugzilla.redhat.com/show_bug.cgi?id=1167774#c9
-   */
-  src_addr.sin_port = htons (nbd_local_port+1);
-  inet_pton (AF_INET, "localhost", &src_addr.sin_addr);
-
-  memset (&dst_addr, 0, sizeof dst_addr);
-  dst_addr.sin_family = AF_INET;
-  dst_addr.sin_port = htons (nbd_local_port);
-  inet_pton (AF_INET, "localhost", &dst_addr.sin_addr);
-
-  /* If we run p2v repeatedly (say, running the tests in a loop),
-   * there's a decent chance we'll end up trying to bind() to a port
-   * that is in TIME_WAIT from a prior run.  Handle that gracefully
-   * with SO_REUSEADDR.
-   */
-  if (setsockopt (sockfd, SOL_SOCKET, SO_REUSEADDR,
-                  &reuseaddr, sizeof reuseaddr) == -1) {
-    set_conversion_error ("waiting for qemu-nbd to start: setsockopt: %m");
-    goto cleanup;
+  for (rp = results; rp != NULL; rp = rp->ai_next) {
+    sockfd = socket (rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+    if (sockfd == -1)
+      continue;
+
+    /* If we run p2v repeatedly (say, running the tests in a loop),
+     * there's a decent chance we'll end up trying to bind() to a port
+     * that is in TIME_WAIT from a prior run.  Handle that gracefully
+     * with SO_REUSEADDR.
+     */
+    if (setsockopt (sockfd, SOL_SOCKET, SO_REUSEADDR,
+                    &reuseaddr, sizeof reuseaddr) == -1)
+      perror ("warning: setsockopt");
+
+    /* Need to bind the source port. */
+    if (bind_source_port (sockfd, rp->ai_family, source_port) == -1) {
+      close (sockfd);
+      sockfd = -1;
+      continue;
+    }
+
+    /* Connect. */
+    if (connect (sockfd, rp->ai_addr, rp->ai_addrlen) == -1) {
+      set_conversion_error ("waiting for qemu-nbd to start: "
+                            "connect to %s/%s: %m",
+                            hostname, dest_port_str);
+      close (sockfd);
+      sockfd = -1;
+      continue;
+    }
+
+    break;
   }
 
-  if (bind (sockfd, (struct sockaddr *) &src_addr, sizeof src_addr) == -1) {
-    set_conversion_error ("waiting for qemu-nbd to start: bind(%d): %m",
-                          ntohs (src_addr.sin_port));
-    goto cleanup;
+  freeaddrinfo (results);
+  return sockfd;
+}
+
+static int
+bind_source_port (int sockfd, int family, int source_port)
+{
+  struct addrinfo hints;
+  struct addrinfo *results, *rp;
+  char source_port_str[16];
+  int r;
+
+  snprintf (source_port_str, sizeof source_port_str, "%d", source_port);
+
+  memset (&hints, 0, sizeof (hints));
+  hints.ai_family = family;
+  hints.ai_socktype = SOCK_STREAM;
+  hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV; /* numeric port number */
+  hints.ai_protocol = 0;                        /* any protocol */
+
+  r = getaddrinfo ("localhost", source_port_str, &hints, &results);
+  if (r != 0) {
+    set_conversion_error ("getaddrinfo (bind): localhost/%s: %s",
+                          source_port_str, gai_strerror (r));
+    return -1;
+  }
+
+  for (rp = results; rp != NULL; rp = rp->ai_next) {
+    if (bind (sockfd, rp->ai_addr, rp->ai_addrlen) == 0)
+      goto bound;
   }
 
+  set_conversion_error ("waiting for qemu-nbd to start: "
+                        "bind to source port %d: %m",
+                        source_port);
+  freeaddrinfo (results);
+  return -1;
+
+ bound:
+  freeaddrinfo (results);
+  return 0;
+}
+
+static int
+wait_qemu_nbd (int nbd_local_port, int timeout_seconds)
+{
+  int sockfd = -1;
+  int result = -1;
+  time_t start_t, now_t;
+  struct timespec half_sec = { .tv_sec = 0, .tv_nsec = 500000000 };
+  struct timeval timeout = { .tv_usec = 0 };
+  char magic[8]; /* NBDMAGIC */
+  size_t bytes_read = 0;
+  ssize_t recvd;
+
+  time (&start_t);
+
   for (;;) {
     time (&now_t);
 
-    if (now_t - start_t >= timeout_seconds) {
-      set_conversion_error ("waiting for qemu-nbd to start: connect: %m");
+    if (now_t - start_t >= timeout_seconds)
       goto cleanup;
-    }
 
-    if (connect (sockfd, (struct sockaddr *) &dst_addr, sizeof dst_addr) == 0)
+    /* Source port for probing qemu-nbd should be one greater than
+     * nbd_local_port.  It's not guaranteed to always bind to this port,
+     * but it will hint the kernel to start there and try incrementally
+     * higher ports if needed.  This avoids the case where the kernel
+     * selects nbd_local_port as our source port, and we immediately
+     * connect to ourself.  See:
+     * https://bugzilla.redhat.com/show_bug.cgi?id=1167774#c9
+     */
+    sockfd = connect_with_source_port ("localhost", nbd_local_port,
+                                       nbd_local_port+1);
+    if (sockfd >= 0)
       break;
+
+    nanosleep (&half_sec, NULL);
   }
 
   time (&now_t);

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-libvirt/libguestfs.git



More information about the Pkg-libvirt-commits mailing list