Flexible guestfs_vmchannel parameter for future appliances.
authorRichard Jones <rjones@trick.home.annexia.org>
Mon, 21 Sep 2009 12:03:27 +0000 (13:03 +0100)
committerRichard Jones <rjones@trick.home.annexia.org>
Tue, 22 Sep 2009 09:26:58 +0000 (10:26 +0100)
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
src/guestfs.c

index b4fb15e..e3b8883 100644 (file)
@@ -40,7 +40,6 @@
 
 #include "daemon.h"
 
 
 #include "daemon.h"
 
-static void usage (void);
 static char *read_cmdline (void);
 
 /* Also in guestfs.c */
 static char *read_cmdline (void);
 
 /* Also in guestfs.c */
@@ -65,18 +64,28 @@ static int print_arginfo (const struct printf_info *info, size_t n, int *argtype
 const char *sysroot = "/sysroot"; /* No trailing slash. */
 int sysroot_len = 8;
 
 const char *sysroot = "/sysroot"; /* No trailing slash. */
 int sysroot_len = 8;
 
+static void
+usage (void)
+{
+  fprintf (stderr,
+    "guestfsd [-f|--foreground] [-c|--channel vmchannel] [-v|--verbose]\n");
+}
+
 int
 main (int argc, char *argv[])
 {
 int
 main (int argc, char *argv[])
 {
-  static const char *options = "f?";
+  static const char *options = "fc:v?";
   static const struct option long_options[] = {
   static const struct option long_options[] = {
+    { "channel", required_argument, 0, 'c' },
     { "foreground", 0, 0, 'f' },
     { "help", 0, 0, '?' },
     { "foreground", 0, 0, 'f' },
     { "help", 0, 0, '?' },
+    { "verbose", 0, 0, 'v' },
     { 0, 0, 0, 0 }
   };
   int c;
   int dont_fork = 0;
   char *cmdline;
     { 0, 0, 0, 0 }
   };
   int c;
   int dont_fork = 0;
   char *cmdline;
+  char *vmchannel = NULL;
 
 #ifdef HAVE_REGISTER_PRINTF_SPECIFIER
   /* http://udrepper.livejournal.com/20948.html */
 
 #ifdef HAVE_REGISTER_PRINTF_SPECIFIER
   /* http://udrepper.livejournal.com/20948.html */
@@ -96,10 +105,18 @@ main (int argc, char *argv[])
     if (c == -1) break;
 
     switch (c) {
     if (c == -1) break;
 
     switch (c) {
+    case 'c':
+      vmchannel = optarg;
+      break;
+
     case 'f':
       dont_fork = 1;
       break;
 
     case 'f':
       dont_fork = 1;
       break;
 
+    case 'v':
+      verbose = 1;
+      break;
+
     case '?':
       usage ();
       exit (0);
     case '?':
       usage ();
       exit (0);
@@ -118,7 +135,8 @@ main (int argc, char *argv[])
   cmdline = read_cmdline ();
 
   /* Set the verbose flag. */
   cmdline = read_cmdline ();
 
   /* Set the verbose flag. */
-  verbose = cmdline && strstr (cmdline, "guestfs_verbose=1") != NULL;
+  verbose = verbose ||
+    (cmdline && strstr (cmdline, "guestfs_verbose=1") != NULL);
   if (verbose)
     printf ("verbose daemon enabled\n");
 
   if (verbose)
     printf ("verbose daemon enabled\n");
 
@@ -148,38 +166,129 @@ main (int argc, char *argv[])
   /* We document that umask defaults to 022 (it should be this anyway). */
   umask (022);
 
   /* 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;
+    size_t 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) {
+      /* We will rewrite it part of the "guestfs=" string with
+       *                       "tcp:"       hence p + 4 below.    */
+      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;
   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) {
 
   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);
   }
 
     exit (1);
   }
 
@@ -323,12 +432,6 @@ xread (int sock, void *v_buf, size_t len)
   return 0;
 }
 
   return 0;
 }
 
-static void
-usage (void)
-{
-  fprintf (stderr, "guestfsd [-f]\n");
-}
-
 int
 add_string (char ***argv, int *size, int *alloc, const char *str)
 {
 int
 add_string (char ***argv, int *size, int *alloc, const char *str)
 {
index 55732f9..ec7473e 100644 (file)
@@ -984,6 +984,7 @@ guestfs__launch (guestfs_h *g)
 
   if (r == 0) {                        /* Child (qemu). */
     char buf[256];
 
   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.
 
     /* 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");
     }
     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 */     \
 
 #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) */
     snprintf (buf, sizeof buf,
               LINUX_CMDLINE
               "%s"              /* (selinux) */
+              "%s"              /* (vmchannel) */
               "%s"              /* (verbose) */
               "%s",             /* (append) */
               g->selinux ? "selinux=1 enforcing=0 " : "selinux=0 ",
               "%s"              /* (verbose) */
               "%s",             /* (append) */
               g->selinux ? "selinux=1 enforcing=0 " : "selinux=0 ",
+              vmchannel ? vmchannel : "",
               g->verbose ? "guestfs_verbose=1 " : "",
               g->append ? g->append : "");
 
               g->verbose ? "guestfs_verbose=1 " : "",
               g->append ? g->append : "");