[Libguestfs] [PATCH 07/10] Flexible guestfs_vmchannel parameter for future appliances.

Richard W.M. Jones rjones at redhat.com
Mon Sep 21 15:04:56 UTC 2009


From: Richard Jones <rjones at trick.home.annexia.org>

This reimplements parts of commit da0a4f8d1f6ddd302ceba028d87c6e009589e503
in a different, but compatible way.

We pass guestfs_vmchannel=tcp:<ip>:<port> on the command line.  This
is intended to be used as follows (now and in future versions):

  tcp:10.0.2.4:6666    for guestfwd vmchannel
  tcp:10.0.2.2:<port>  for future "no vmchannel" implementation
  /dev/vcon4           for future virtio-console vmchannel*

It also accepts the old-style guestfs=10.0.2.4:6666 parameter which
is sent by older libraries, and turns this transparently into the
correct format above.

If no guestfs_vmchannel is passed, then this defaults to the guestfwd
vmchannel which older libraries would expect.

* Maybe this last one should be dev:/dev/vcon4 or file:/dev/vcon4, but
we don't need to decide that now.
---
 daemon/guestfsd.c |  147 +++++++++++++++++++++++++++++++++++++++++++---------
 src/guestfs.c     |    4 ++
 2 files changed, 125 insertions(+), 26 deletions(-)

diff --git a/daemon/guestfsd.c b/daemon/guestfsd.c
index a38452c..ce14dbf 100644
--- a/daemon/guestfsd.c
+++ b/daemon/guestfsd.c
@@ -68,8 +68,9 @@ int sysroot_len = 8;
 int
 main (int argc, char *argv[])
 {
-  static const char *options = "f?";
+  static const char *options = "fc:?";
   static const struct option long_options[] = {
+    { "channel", 0, 0, 'c' },
     { "foreground", 0, 0, 'f' },
     { "help", 0, 0, '?' },
     { 0, 0, 0, 0 }
@@ -77,6 +78,7 @@ main (int argc, char *argv[])
   int c;
   int dont_fork = 0;
   char *cmdline;
+  char *vmchannel = NULL;
 
 #ifdef HAVE_REGISTER_PRINTF_SPECIFIER
   /* http://udrepper.livejournal.com/20948.html */
@@ -96,6 +98,10 @@ main (int argc, char *argv[])
     if (c == -1) break;
 
     switch (c) {
+    case 'c':
+      vmchannel = optarg;
+      break;
+
     case 'f':
       dont_fork = 1;
       break;
@@ -148,38 +154,127 @@ main (int argc, char *argv[])
   /* We document that umask defaults to 022 (it should be this anyway). */
   umask (022);
 
-  /* Resolve the hostname. */
-  struct addrinfo *res, *rr;
-  struct addrinfo hints;
-  int r;
-  memset (&hints, 0, sizeof hints);
-  hints.ai_socktype = SOCK_STREAM;
-  hints.ai_flags = AI_ADDRCONFIG;
-  r = getaddrinfo (GUESTFWD_ADDR, GUESTFWD_PORT, &hints, &res);
-  if (r != 0) {
-    fprintf (stderr, "%s:%s: %s\n",
-             GUESTFWD_ADDR, GUESTFWD_PORT, gai_strerror (r));
-    exit (1);
+  /* Get the vmchannel string.
+   *
+   * Sources:
+   *   --channel/-c option on the command line
+   *   guestfs_vmchannel=... from the kernel command line
+   *   guestfs=... from the kernel command line
+   *   built-in default
+   *
+   * At the moment we expect this to contain "tcp:ip:port" but in
+   * future it might contain a device name, eg. "/dev/vcon4" for
+   * virtio-console vmchannel.
+   */
+  if (vmchannel == NULL && cmdline) {
+    char *p;
+    int len;
+
+    p = strstr (cmdline, "guestfs_vmchannel=");
+    if (p) {
+      len = strcspn (p + 18, " \t\n");
+      vmchannel = strndup (p + 18, len);
+      if (!vmchannel) {
+        perror ("strndup");
+        exit (1);
+      }
+    }
+
+    /* Old libraries passed guestfs=host:port.  Rewrite it as tcp:host:port. */
+    if (vmchannel == NULL) {
+      p = strstr (cmdline, "guestfs=");
+      if (p) {
+        len = strcspn (p + 4, " \t\n");
+        vmchannel = strndup (p + 4, len);
+        if (!vmchannel) {
+          perror ("strndup");
+          exit (1);
+        }
+        memcpy (vmchannel, "tcp:", 4);
+      }
+    }
   }
 
-  /* Connect to the given TCP socket. */
+  /* Default vmchannel. */
+  if (vmchannel == NULL) {
+    vmchannel = strdup ("tcp:" GUESTFWD_ADDR ":" GUESTFWD_PORT);
+    if (!vmchannel) {
+      perror ("strdup");
+      exit (1);
+    }
+  }
+
+  if (verbose)
+    printf ("vmchannel: %s\n", vmchannel);
+
+  /* Connect to vmchannel. */
   int sock = -1;
-  for (rr = res; rr != NULL; rr = rr->ai_next) {
-    sock = socket (rr->ai_family, rr->ai_socktype, rr->ai_protocol);
-    if (sock != -1) {
-      if (connect (sock, rr->ai_addr, rr->ai_addrlen) == 0)
-        break;
-      perror ("connect");
-
-      close (sock);
-      sock = -1;
+
+  if (strncmp (vmchannel, "tcp:", 4) == 0) {
+    /* Resolve the hostname. */
+    struct addrinfo *res, *rr;
+    struct addrinfo hints;
+    int r;
+    char *host, *port;
+
+    host = vmchannel+4;
+    port = strchr (host, ':');
+    if (port) {
+      port[0] = '\0';
+      port++;
+    } else {
+      fprintf (stderr, "vmchannel: expecting \"tcp:<ip>:<port>\": %s\n",
+               vmchannel);
+      exit (1);
     }
+
+    memset (&hints, 0, sizeof hints);
+    hints.ai_socktype = SOCK_STREAM;
+    hints.ai_flags = AI_ADDRCONFIG;
+    r = getaddrinfo (host, port, &hints, &res);
+    if (r != 0) {
+      fprintf (stderr, "%s:%s: %s\n",
+               host, port, gai_strerror (r));
+      exit (1);
+    }
+
+    /* Connect to the given TCP socket. */
+    for (rr = res; rr != NULL; rr = rr->ai_next) {
+      sock = socket (rr->ai_family, rr->ai_socktype, rr->ai_protocol);
+      if (sock != -1) {
+        if (connect (sock, rr->ai_addr, rr->ai_addrlen) == 0)
+          break;
+        perror ("connect");
+
+        close (sock);
+        sock = -1;
+      }
+    }
+    freeaddrinfo (res);
+  } else {
+    fprintf (stderr,
+             "unknown vmchannel connection type: %s\n"
+             "expecting \"tcp:<ip>:<port>\"\n",
+             vmchannel);
+    exit (1);
   }
-  freeaddrinfo (res);
 
   if (sock == -1) {
-    fprintf (stderr, "connection to %s:%s failed\n",
-             GUESTFWD_ADDR, GUESTFWD_PORT);
+    fprintf (stderr,
+             "\n"
+             "Failed to connect to any vmchannel implementation.\n"
+             "vmchannel: %s\n"
+             "\n"
+             "This is a fatal error and the appliance will now exit.\n"
+             "\n"
+             "Usually this error is caused by either QEMU or the appliance\n"
+             "kernel not supporting the vmchannel method that the\n"
+             "libguestfs library chose to use.  Please run\n"
+             "'libguestfs-test-tool' and provide the complete, unedited\n"
+             "output to the libguestfs developers, either in a bug report\n"
+             "or on the libguestfs redhat com mailing list.\n"
+             "\n",
+             vmchannel);
     exit (1);
   }
 
diff --git a/src/guestfs.c b/src/guestfs.c
index 55732f9..ec7473e 100644
--- a/src/guestfs.c
+++ b/src/guestfs.c
@@ -984,6 +984,7 @@ guestfs__launch (guestfs_h *g)
 
   if (r == 0) {			/* Child (qemu). */
     char buf[256];
+    const char *vmchannel = NULL;
 
     /* Set up the full command line.  Do this in the subprocess so we
      * don't need to worry about cleaning up.
@@ -1045,6 +1046,7 @@ guestfs__launch (guestfs_h *g)
     }
     add_cmdline (g, "-net");
     add_cmdline (g, "nic,model=" NET_IF ",vlan=0");
+    vmchannel = "guestfs_vmchannel=tcp:" GUESTFWD_ADDR ":" GUESTFWD_PORT " ";
 
 #define LINUX_CMDLINE							\
     "panic=1 "         /* force kernel to panic if daemon exits */	\
@@ -1058,9 +1060,11 @@ guestfs__launch (guestfs_h *g)
     snprintf (buf, sizeof buf,
               LINUX_CMDLINE
               "%s"              /* (selinux) */
+              "%s"              /* (vmchannel) */
               "%s"              /* (verbose) */
               "%s",             /* (append) */
               g->selinux ? "selinux=1 enforcing=0 " : "selinux=0 ",
+              vmchannel ? vmchannel : "",
               g->verbose ? "guestfs_verbose=1 " : "",
               g->append ? g->append : "");
 
-- 
1.6.2.5




More information about the Libguestfs mailing list