Add test for qemu broken -machine option (RHBZ#748266).
[libguestfs.git] / src / launch.c
index 48ddb8d..34d0950 100644 (file)
@@ -1,5 +1,5 @@
 /* libguestfs
- * Copyright (C) 2009-2010 Red Hat Inc.
+ * Copyright (C) 2009-2011 Red Hat Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -70,6 +70,8 @@
 #include "guestfs-internal-actions.h"
 #include "guestfs_protocol.h"
 
+static int launch_appliance (guestfs_h *g);
+static int64_t timeval_diff (const struct timeval *x, const struct timeval *y);
 static int qemu_supports (guestfs_h *g, const char *option);
 
 /* Add a string to the current command line. */
@@ -120,6 +122,30 @@ guestfs___rollback_cmdline (guestfs_h *g, int pos)
   g->cmdline_size = pos;
 }
 
+/* Internal command to return the command line. */
+char **
+guestfs__debug_cmdline (guestfs_h *g)
+{
+  int i;
+  char **r;
+
+  if (g->cmdline == NULL) {
+    r = safe_malloc (g, sizeof (char *) * 1);
+    r[0] = NULL;
+    return r;
+  }
+
+  r = safe_malloc (g, sizeof (char *) * (g->cmdline_size + 1));
+  r[0] = safe_strdup (g, g->qemu); /* g->cmdline[0] is always NULL */
+
+  for (i = 1; i < g->cmdline_size; ++i)
+    r[i] = safe_strdup (g, g->cmdline[i]);
+
+  r[g->cmdline_size] = NULL;
+
+  return r;                     /* caller frees */
+}
+
 int
 guestfs__config (guestfs_h *g,
                  const char *qemu_param, const char *qemu_value)
@@ -326,33 +352,19 @@ static int is_openable (guestfs_h *g, const char *path, int flags);
 int
 guestfs__launch (guestfs_h *g)
 {
-  int r;
-  int wfd[2], rfd[2];
-  int tries;
-  char unixsock[256];
-  struct sockaddr_un addr;
-
   /* Configured? */
-  if (!g->cmdline) {
-    error (g, _("you must call guestfs_add_drive before guestfs_launch"));
-    return -1;
-  }
-
   if (g->state != CONFIG) {
     error (g, _("the libguestfs handle has already been launched"));
     return -1;
   }
 
-  /* Start the clock ... */
-  gettimeofday (&g->launch_t, NULL);
-
   /* Make the temporary directory. */
   if (!g->tmpdir) {
     TMP_TEMPLATE_ON_STACK (dir_template);
     g->tmpdir = safe_strdup (g, dir_template);
     if (mkdtemp (g->tmpdir) == NULL) {
       perrorf (g, _("%s: cannot create temporary directory"), dir_template);
-      goto cleanup0;
+      return -1;
     }
   }
 
@@ -363,6 +375,28 @@ guestfs__launch (guestfs_h *g)
   if (chmod (g->tmpdir, 0755) == -1)
     fprintf (stderr, "chmod: %s: %m (ignored)\n", g->tmpdir);
 
+  return launch_appliance (g);
+}
+
+static int
+launch_appliance (guestfs_h *g)
+{
+  int r;
+  int wfd[2], rfd[2];
+  char guestfsd_sock[256];
+  struct sockaddr_un addr;
+
+  /* At present you must add drives before starting the appliance.  In
+   * future when we enable hotplugging you won't need to do this.
+   */
+  if (!g->cmdline) {
+    error (g, _("you must call guestfs_add_drive before guestfs_launch"));
+    return -1;
+  }
+
+  /* Start the clock ... */
+  gettimeofday (&g->launch_t, NULL);
+
   /* Locate and/or build the appliance. */
   char *kernel = NULL, *initrd = NULL, *appliance = NULL;
   if (guestfs___build_appliance (g, &kernel, &initrd, &appliance) == -1)
@@ -378,8 +412,8 @@ guestfs__launch (guestfs_h *g)
   /* Using virtio-serial, we need to create a local Unix domain socket
    * for qemu to connect to.
    */
-  snprintf (unixsock, sizeof unixsock, "%s/sock", g->tmpdir);
-  unlink (unixsock);
+  snprintf (guestfsd_sock, sizeof guestfsd_sock, "%s/guestfsd.sock", g->tmpdir);
+  unlink (guestfsd_sock);
 
   g->sock = socket (AF_UNIX, SOCK_STREAM, 0);
   if (g->sock == -1) {
@@ -393,7 +427,7 @@ guestfs__launch (guestfs_h *g)
   }
 
   addr.sun_family = AF_UNIX;
-  strncpy (addr.sun_path, unixsock, UNIX_PATH_MAX);
+  strncpy (addr.sun_path, guestfsd_sock, UNIX_PATH_MAX);
   addr.sun_path[UNIX_PATH_MAX-1] = '\0';
 
   if (bind (g->sock, &addr, sizeof addr) == -1) {
@@ -439,21 +473,38 @@ guestfs__launch (guestfs_h *g)
     if (qemu_supports (g, "-nodefconfig"))
       add_cmdline (g, "-nodefconfig");
 
-    /* qemu sometimes needs this option to enable hardware
-     * virtualization, but some versions of 'qemu-kvm' will use KVM
-     * regardless (even where this option appears in the help text).
-     * It is rumoured that there are versions of qemu where supplying
-     * this option when hardware virtualization is not available will
-     * cause qemu to fail, so we we have to check at least that
-     * /dev/kvm is openable.  That's not reliable, since /dev/kvm
-     * might be openable by qemu but not by us (think: SELinux) in
-     * which case the user would not get hardware virtualization,
-     * although at least shouldn't fail.  A giant clusterfuck with the
-     * qemu command line, again.
-     */
-    if (qemu_supports (g, "-enable-kvm") &&
-        is_openable (g, "/dev/kvm", O_RDWR))
-      add_cmdline (g, "-enable-kvm");
+    if (qemu_supports (g, "-machine")) {
+      add_cmdline (g, "-machine");
+#if QEMU_MACHINE_TYPE_IS_BROKEN
+      /* Workaround for qemu 0.15: We have to add the '[type=]pc'
+       * since there is no default.  This is not a permanent solution
+       * because this only works on PC-like hardware.  Other platforms
+       * like ppc would need a different machine type.
+       *
+       * This bug is fixed in qemu commit 2645c6dcaf6ea2a51a, and was
+       * not a problem in qemu < 0.15.
+       */
+      add_cmdline (g, "pc,accel=kvm:tcg");
+#else
+      add_cmdline (g, "accel=kvm:tcg");
+#endif
+    } else {
+      /* qemu sometimes needs this option to enable hardware
+       * virtualization, but some versions of 'qemu-kvm' will use KVM
+       * regardless (even where this option appears in the help text).
+       * It is rumoured that there are versions of qemu where supplying
+       * this option when hardware virtualization is not available will
+       * cause qemu to fail, so we we have to check at least that
+       * /dev/kvm is openable.  That's not reliable, since /dev/kvm
+       * might be openable by qemu but not by us (think: SELinux) in
+       * which case the user would not get hardware virtualization,
+       * although at least shouldn't fail.  A giant clusterfuck with the
+       * qemu command line, again.
+       */
+      if (qemu_supports (g, "-enable-kvm") &&
+          is_openable (g, "/dev/kvm", O_RDWR))
+        add_cmdline (g, "-enable-kvm");
+    }
 
     /* Newer versions of qemu (from around 2009/12) changed the
      * behaviour of monitors so that an implicit '-monitor stdio' is
@@ -502,7 +553,7 @@ guestfs__launch (guestfs_h *g)
 
     /* Set up virtio-serial for the communications channel. */
     add_cmdline (g, "-chardev");
-    snprintf (buf, sizeof buf, "socket,path=%s,id=channel0", unixsock);
+    snprintf (buf, sizeof buf, "socket,path=%s,id=channel0", guestfsd_sock);
     add_cmdline (g, buf);
     add_cmdline (g, "-device");
     add_cmdline (g, "virtserialport,chardev=channel0,name=org.libguestfs.channel.0");
@@ -568,17 +619,27 @@ guestfs__launch (guestfs_h *g)
       guestfs___print_timestamped_argv (g, (const char **)g->cmdline);
 
     if (!g->direct) {
-      /* Set up stdin, stdout. */
+      /* Set up stdin, stdout, stderr. */
       close (0);
       close (1);
       close (wfd[1]);
       close (rfd[0]);
 
+      /* Stdin. */
       if (dup (wfd[0]) == -1) {
       dup_failed:
         perror ("dup failed");
         _exit (EXIT_FAILURE);
       }
+      /* Stdout. */
+      if (dup (rfd[1]) == -1)
+        goto dup_failed;
+
+      /* Particularly since qemu 0.15, qemu spews all sorts of debug
+       * information on stderr.  It is useful to both capture this and
+       * not confuse casual users, so send stderr to the pipe as well.
+       */
+      close (2);
       if (dup (rfd[1]) == -1)
         goto dup_failed;
 
@@ -621,7 +682,7 @@ guestfs__launch (guestfs_h *g)
       pid_t parent_pid = getppid ();
 
       /* Writing to argv is hideously complicated and error prone.  See:
-       * http://anoncvs.postgresql.org/cvsweb.cgi/pgsql/src/backend/utils/misc/ps_status.c?rev=1.33.2.1;content-type=text%2Fplain
+       * http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/misc/ps_status.c;hb=HEAD
        */
 
       /* Loop around waiting for one or both of the other processes to
@@ -725,7 +786,7 @@ guestfs__launch (guestfs_h *g)
   }
   if (g->pid > 0) kill (g->pid, 9);
   if (g->recoverypid > 0) kill (g->recoverypid, 9);
-  waitpid (g->pid, NULL, 0);
+  if (g->pid > 0) waitpid (g->pid, NULL, 0);
   if (g->recoverypid > 0) waitpid (g->recoverypid, NULL, 0);
   g->fd[0] = -1;
   g->fd[1] = -1;