[Libguestfs] Libguestfs (1.22.6) driver/changes for mingw/win32

Richard W.M. Jones rjones at redhat.com
Mon Feb 17 13:53:02 UTC 2014


On Thu, Feb 13, 2014 at 04:54:09PM +0200, Or Goshen wrote:
> Hi,
> 
> I attached the changes I made to a vanilla libguestfs-1.22.6 in order to
> make it work in mingw/win32.
> Added is also the patch required to make QEMU compatible (add a command to
> QMP that lists the supported devices (the regilat way you do it print it to
> stderr, which is difficult to redirect in win32)).

A couple of points first:

- You need to start using git.

- You need to develop against the git head, not the old 1.22 branch.

I took the tarball and turned it into a git commit, which I then
cherry picked on top of head.  (Except for 'bootstrap' which seems to
be a completely rewritten file -- so I created it as
'bootstrap.win32').

This is attached so you can take it from here.  As this was mostly an
automatic process -- git did the hard work, I just resolved a few
things that git could not resolve -- you need to check carefully that
this patch still works.

About the patch itself:

The patch looks fairly sensible.  There's some tidying up that could
be done, and it would be nice to confine more of the changes so they
don't affect the main code.  But there's nothing here that can't be
pushed upstream in some form, after a proper review and more testing.

The qemu change needs to separately be submitted to the qemu mailing
list, along with an explanation for why that is necessary.

Thanks,

Rich.

-- 
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
Read my programming blog: http://rwmj.wordpress.com
Fedora now supports 80 OCaml packages (the OPEN alternative to F#)
-------------- next part --------------
>From 90795f15605c8e98cbe4815b3a8b8db99627aba2 Mon Sep 17 00:00:00 2001
From: Or Goshen <oberonc at gmail.com>
Date: Mon, 17 Feb 2014 13:31:08 +0000
Subject: [PATCH] lib: Add MinGW (Windows) backend.

---
 bootstrap.win32          |   96 ++++
 src/actions-support.c    |    4 +
 src/appliance.c          |   23 +-
 src/command.c            |  158 ++++++-
 src/conn-socket.c        |  286 +++++++++++-
 src/guestfs-internal.h   |    1 +
 src/handle.c             |    4 +-
 src/inspect-fs-windows.c |    8 +
 src/launch-direct.c      |  108 +++++
 src/launch-libvirt.c     |    3 +-
 src/launch-mingw.c       | 1158 ++++++++++++++++++++++++++++++++++++++++++++++
 src/launch-unix.c        |   10 +-
 src/launch.c             |    2 +
 src/proto.c              |   24 +-
 14 files changed, 1869 insertions(+), 16 deletions(-)
 create mode 100755 bootstrap.win32
 create mode 100644 src/launch-mingw.c

diff --git a/bootstrap.win32 b/bootstrap.win32
new file mode 100755
index 0000000..1d17e31
--- /dev/null
+++ b/bootstrap.win32
@@ -0,0 +1,96 @@
+#!/bin/sh
+GNULIB_SRCDIR=.gnulib
+
+gnulib_tool=$GNULIB_SRCDIR/gnulib-tool
+
+modules='
+accept4
+accept
+areadlink
+areadlinkat
+arpa_inet
+bind
+byteswap
+c-ctype
+canonicalize-lgpl
+cloexec
+close
+closeout
+connect
+dup3
+error
+filevercmp
+fstatat
+fsusage
+fts
+full-read
+full-write
+futimens
+getaddrinfo
+getline
+getsockname
+gitlog-to-changelog
+glob
+gnu-make
+gnumakefile
+hash
+hash-pjw
+human
+ignore-value
+listen
+lock
+maintainer-makefile
+manywarnings
+memmem
+mkdtemp
+mkstemps
+netdb
+netinet_in
+openat
+poll
+perror
+pipe2
+pread
+read
+read-file
+readlink
+select
+setsockopt
+setenv
+sleep
+socket
+stat-time
+strchrnul
+strerror
+strndup
+strsignal
+symlinkat
+sys_select
+sys_wait
+termios
+vasprintf
+vc-list-files
+warnings
+waitpid
+xalloc
+xalloc-die
+xstrtol
+xstrtoll
+xvasprintf
+'
+
+# If any tests fail, avoid including them by adding them to
+# this list.
+avoid="--avoid=dummy --avoid=getlogin_r-tests"
+
+$gnulib_tool			\
+  $avoid			\
+  --with-tests			\
+  --m4-base=m4			\
+  --source-base=gnulib/lib	\
+  --tests-base=gnulib/tests	\
+  --libtool			\
+  --import $modules
+
+# Disable autopoint and libtoolize, since they were already done above.
+AUTOPOINT=true LIBTOOLIZE=true autoreconf --verbose --install
diff --git a/src/actions-support.c b/src/actions-support.c
index c3bd863..2c2d927 100644
--- a/src/actions-support.c
+++ b/src/actions-support.c
@@ -81,7 +81,11 @@ guestfs___trace_open (struct trace_buffer *tb)
 {
   tb->buf = NULL;
   tb->len = 0;
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+  tb->fp = NULL;
+#else
   tb->fp = open_memstream (&tb->buf, &tb->len);
+#endif
   if (tb->fp)
     tb->opened = true;
   else {
diff --git a/src/appliance.c b/src/appliance.c
index 3eaf635..d7dad0f 100644
--- a/src/appliance.c
+++ b/src/appliance.c
@@ -173,11 +173,12 @@ build_appliance (guestfs_h *g,
                  char **appliance)
 {
   int r;
-  uid_t uid = geteuid ();
   CLEANUP_FREE char *supermin_path = NULL;
   CLEANUP_FREE char *path = NULL;
 
   /* Step (1). */
+#if (!defined _WIN32 && !defined __WIN32__) || defined __CYGWIN__
+  uid_t uid = geteuid ();
   r = find_path (g, contains_supermin_appliance, NULL, &supermin_path);
   if (r == -1)
     return -1;
@@ -198,6 +199,7 @@ build_appliance (guestfs_h *g,
                                        kernel, dtb, initrd, appliance);
     }
   }
+#endif
 
   /* Step (5). */
   r = find_path (g, contains_fixed_appliance, NULL, &path);
@@ -276,6 +278,7 @@ read_checksum (guestfs_h *g, void *checksumv, const char *line, size_t len)
   strcpy (checksum, line);
 }
 
+#if (!defined _WIN32 && !defined __WIN32__) || defined __CYGWIN__
 static int
 process_exists (int pid)
 {
@@ -287,6 +290,7 @@ process_exists (int pid)
 
   return -1;
 }
+#endif
 
 /* Garbage collect appliance hard links.  Files that match
  * (kernel|dtb|initrd|root).$PID where the corresponding PID doesn't
@@ -322,7 +326,9 @@ garbage_collect_appliances (const char *cachedir)
 
   closedir (dir);
 }
+#endif
 
+#if (!defined _WIN32 && !defined __WIN32__) || defined __CYGWIN__
 static int
 check_for_cached_appliance (guestfs_h *g,
                             const char *supermin_path, const char *checksum,
@@ -437,6 +443,7 @@ check_for_cached_appliance (guestfs_h *g,
   /* Exists! */
   return 1;
 }
+#endif
 
 /* Build supermin appliance from supermin_path to $TMPDIR/.guestfs-$UID.
  *
@@ -444,6 +451,7 @@ check_for_cached_appliance (guestfs_h *g,
  * 0 = built
  * -1 = error (aborts launch)
  */
+#if (!defined _WIN32 && !defined __WIN32__) || defined __CYGWIN__
 static int
 build_supermin_appliance (guestfs_h *g,
                           const char *supermin_path, const char *checksum,
@@ -700,6 +708,7 @@ hard_link_to_cached_appliance (guestfs_h *g,
 static char *
 calculate_supermin_checksum (guestfs_h *g, const char *supermin_path)
 {
+#if (!defined _WIN32 && !defined __WIN32__) || defined __CYGWIN__
   size_t len;
   CLEANUP_CMD_CLOSE struct command *cmd = guestfs___new_command (g);
   int pass_u_g_args = getuid () != geteuid () || getgid () != getegid ();
@@ -734,6 +743,9 @@ calculate_supermin_checksum (guestfs_h *g, const char *supermin_path)
   }
 
   return safe_strndup (g, checksum, len);
+#else
+  return NULL;
+#endif
 }
 
 static int
@@ -877,6 +889,7 @@ run_supermin_helper (guestfs_h *g, const char *supermin_path,
 
   return 0;
 }
+#endif
 
 #endif /* ! SUPERMIN_HELPER_NEW_STYLE_SYNTAX */
 
@@ -907,7 +920,11 @@ find_path (guestfs_h *g,
    * libguestfs < 1.5.4).
    */
   do {
+#if (!defined _WIN32 && !defined __WIN32__) || defined __CYGWIN__
     len = strcspn (pelem, ":");
+#else
+    len = strcspn (pelem, ";");
+#endif
 
     /* Empty element or "." means current directory. */
     if (len == 0)
@@ -926,7 +943,11 @@ find_path (guestfs_h *g,
 
     free (*pelem_ret);
 
+#if (!defined _WIN32 && !defined __WIN32__) || defined __CYGWIN__
     if (pelem[len] == ':')
+#else
+    if (pelem[len] == ';')
+#endif
       pelem += len + 1;
     else
       pelem += len;
diff --git a/src/command.c b/src/command.c
index 02e5801..0b120f2 100644
--- a/src/command.c
+++ b/src/command.c
@@ -77,6 +77,11 @@
 #include <sys/wait.h>
 #include <sys/select.h>
 
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+# include <process.h>
+# include <cloexec.h>
+#endif
+
 #include "guestfs.h"
 #include "guestfs-internal.h"
 
@@ -129,6 +134,10 @@ struct command
   int outfd;
   struct buffering outbuf;
 
+  /* Supply input to be passed into stdin right after invacation */
+  char *stdin_data;
+  int infd;
+
   /* For programs that send output to stderr.  Hello qemu. */
   bool stderr_to_stdout;
 
@@ -148,6 +157,7 @@ guestfs___new_command (guestfs_h *g)
   cmd->close_files = true;
   cmd->errorfd = -1;
   cmd->outfd = -1;
+  cmd->stdin_data = NULL;
   return cmd;
 }
 
@@ -288,6 +298,14 @@ guestfs___cmd_set_stderr_to_stdout (struct command *cmd)
   cmd->stderr_to_stdout = true;
 }
 
+/* Data in this buffer will be passed to stdin right after execution.
+ */
+void
+guestfs___cmd_set_stdin_data (struct command *cmd, const char *data)
+{
+  cmd->stdin_data = safe_strdup (cmd->g, data);
+}
+
 /* Clear the capture_errors flag.  This means that any errors will go
  * to stderr, instead of being captured in the event log, and that is
  * usually undesirable.
@@ -358,6 +376,134 @@ debug_command (struct command *cmd)
   }
 }
 
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+static void
+restore_file_desc(int orig_stdout, int orig_stderr, int orig_stdin)
+{
+  if (orig_stdout >= 0)
+  {
+    dup2(orig_stdout, STDOUT_FILENO);
+    close(orig_stdout);
+  }
+  if (orig_stderr >= 0)
+  {
+    dup2(orig_stderr, STDERR_FILENO);
+    close(orig_stderr);
+  }
+  if (orig_stdin >= 0)
+  {
+    dup2(orig_stdin, STDIN_FILENO);
+    close(orig_stdin);
+  }
+}
+
+static int
+run_command_mingw (struct command *cmd)
+{
+  int i, fd, max_fd, r;
+  int errorfd[2] = { -1, -1 };
+  int outfd[2] = { -1, -1 };
+  int infd[2] = { -1, -1 };
+  int orig_stdout = -1, orig_stderr = -1, orig_stdin = -1;
+  char status_string[80];
+
+  /* Set up a pipe to capture command output and send it to the error log. */
+  if (cmd->capture_errors) {
+    if (pipe2 (errorfd, 0) == -1) {
+      perrorf (cmd->g, "pipe2");
+      goto error;
+    }
+    orig_stderr = dup_cloexec(STDERR_FILENO);
+    if (dup2(errorfd[1], STDERR_FILENO) < 0)
+      goto error;
+  }
+
+  /* Set up a pipe to capture stdout for the callback. */
+  if (cmd->stdout_callback) {
+    if (pipe2 (outfd, 0) == -1) {
+      perrorf (cmd->g, "pipe2");
+      goto error;
+    }
+    orig_stdout = dup_cloexec(STDOUT_FILENO);
+    if (dup2(outfd[1], STDOUT_FILENO) < 0)
+      goto error;
+  }
+
+  /* Set up a pipe to which stdin will be passed. */
+  if (cmd->stdin_data) {
+    if (pipe2 (infd, 0) == -1) {
+	  perrorf (cmd->g, "pipe2");
+    goto error;
+    }
+    orig_stdin = dup_cloexec(STDIN_FILENO);
+    if (dup2(infd[0], STDIN_FILENO) < 0)
+      goto error;
+  }
+
+  /* Redirect stderr to stdout */
+  if (cmd->stderr_to_stdout)
+    dup2 (1, 2);
+
+  /* Spawn child process and exec command */
+  switch (cmd->style) {
+    case COMMAND_STYLE_EXECV:
+      cmd->pid = spawnvp(P_NOWAIT, cmd->argv.argv[0], (const char * const *)cmd->argv.argv);
+      if (cmd->pid < 0)
+        goto error;
+      break;
+
+    case COMMAND_STYLE_SYSTEM:
+      perror ("system not supported");
+      goto error;
+
+    case COMMAND_STYLE_NOT_SELECTED:
+      abort ();
+    }
+
+  /* Restore original stdout,err file descriptors */
+  restore_file_desc(orig_stdout, orig_stderr, orig_stdin);
+
+  /* Close pipe ends that arent relevant to the parent */
+  if (cmd->capture_errors) {
+    close (errorfd[1]);
+    cmd->errorfd = errorfd[0];
+  }
+
+  if (cmd->stdout_callback) {
+    close (outfd[1]);
+    cmd->outfd = outfd[0];
+  }
+
+  if (cmd->stdin_data) {
+    close(infd[0]);
+
+    write(infd[1], cmd->stdin_data, strlen(cmd->stdin_data));
+    cmd->infd = infd[1];
+  }
+
+  return 0;
+
+ error:
+
+  /* Restore original stdout,err file descriptors (if duplicated) */
+  restore_file_desc(orig_stdout, orig_stderr, orig_stdin);
+
+  if (errorfd[0] >= 0)
+    close (errorfd[0]);
+  if (errorfd[1] >= 0)
+    close (errorfd[1]);
+  if (outfd[0] >= 0)
+    close (outfd[0]);
+  if (outfd[1] >= 0)
+    close (outfd[1]);
+  if (infd[1] >= 0)
+    close (infd[1]);
+  if (infd[0] >= 0)
+    close (infd[0]);
+
+  return -1;
+}
+#else
 static int
 run_command (struct command *cmd)
 {
@@ -495,6 +641,7 @@ run_command (struct command *cmd)
 
   return -1;
 }
+#endif
 
 /* The loop which reads errors and output and directs it either
  * to the log or to the stdout callback as appropriate.
@@ -524,7 +671,7 @@ loop (struct command *cmd)
 
   while (nr_fds > 0) {
     rset2 = rset;
-    r = select (maxfd+1, &rset2, NULL, NULL, NULL);
+    r = 1;//select (maxfd+1, &rset2, NULL, NULL, NULL);
     if (r == -1) {
       if (errno == EINTR || errno == EAGAIN)
         continue;
@@ -614,8 +761,13 @@ guestfs___cmd_run (struct command *cmd)
   if (cmd->g->verbose)
     debug_command (cmd);
 
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+  if (run_command_mingw (cmd) == -1)
+      return -1;
+#else
   if (run_command (cmd) == -1)
     return -1;
+#endif
 
   if (loop (cmd) == -1)
     return -1;
@@ -649,6 +801,10 @@ guestfs___cmd_close (struct command *cmd)
   if (cmd->outfd >= 0)
     close (cmd->outfd);
 
+  if (cmd->infd >= 0)
+    close (cmd->infd);
+
+  free (cmd->stdin_data);
   free (cmd->outbuf.buffer);
 
   if (cmd->pid > 0)
diff --git a/src/conn-socket.c b/src/conn-socket.c
index fe3ca04..e21fde7 100644
--- a/src/conn-socket.c
+++ b/src/conn-socket.c
@@ -20,16 +20,22 @@
 
 #include <config.h>
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+#include <io.h>
+#include <Winsock2.h>
+#else
 #include <unistd.h>
 #include <fcntl.h>
-#include <errno.h>
 #include <poll.h>
 #include <sys/stat.h>
 #include <sys/socket.h>
 #include <sys/types.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
 #include <assert.h>
 
 #include "guestfs.h"
@@ -47,8 +53,12 @@ struct connection_socket {
   int daemon_accept_sock;
 };
 
+#define FD_TO_SOCKET(fd)   ((SOCKET) _get_osfhandle ((fd)))
+#define SOCKET_TO_FD(fh)   (_open_osfhandle ((intptr_t) (fh), O_RDWR | O_BINARY))
+
 static int handle_log_message (guestfs_h *g, struct connection_socket *conn);
 
+#if (!defined _WIN32 && !defined __WIN32__) || defined __CYGWIN__
 static int
 accept_connection (guestfs_h *g, struct connection *connv)
 {
@@ -125,12 +135,75 @@ accept_connection (guestfs_h *g, struct connection *connv)
   /* Make sure the new socket is non-blocking. */
   if (fcntl (conn->daemon_sock, F_SETFL, O_NONBLOCK) == -1) {
     perrorf (g, "accept_connection: fcntl");
-    return -1;
+	return -1;
   }
-
   return 1;
 }
+#else
+static int accept_connection(guestfs_h *g, struct connection *connv) {
+	struct connection_socket *conn = (struct connection_socket *) connv;
+	SOCKET sock = INVALID_SOCKET;
 
+	if (conn->daemon_accept_sock == -1) {
+		error(g, _("accept_connection called twice"));
+		return -1;
+	}
+
+	while (sock == INVALID_SOCKET) {
+		int r;
+		fd_set rfds;
+
+		FD_ZERO (&rfds);
+
+		FD_SET (FD_TO_SOCKET(conn->daemon_accept_sock), &rfds);
+		if (conn->console_sock >= 0) {
+		  FD_SET (FD_TO_SOCKET(conn->console_sock), &rfds);
+		}
+
+		r = select(0, &rfds, NULL, NULL, NULL);
+		if (r > 0) {
+			if (FD_ISSET(FD_TO_SOCKET(conn->console_sock), &rfds)) {
+			  r = handle_log_message (g, conn);
+			  if (r <= 0)
+				return r;
+			}
+
+			if (FD_ISSET(FD_TO_SOCKET(conn->daemon_accept_sock), &rfds)) {
+				sock = accept(FD_TO_SOCKET(conn->daemon_accept_sock), NULL, NULL);
+				if (sock == INVALID_SOCKET) {
+					if (errno == EINTR || errno == EAGAIN)
+						continue;
+					perrorf(g, "accept_connection: accept");
+					return -1;
+				}
+			}
+		} else if (r == SOCKET_ERROR) {
+		  errno = WSAGetLastError();
+		  if (errno == EINTR || errno == EAGAIN)
+			continue;
+		  perrorf (g, "accept_connection: select");
+		  return -1;
+		}
+	}
+
+	/* Got a connection and accepted it, so update the connection's
+	 * internal status.
+	 */
+	closesocket(FD_TO_SOCKET(conn->daemon_accept_sock));
+	conn->daemon_accept_sock = -1;
+	conn->daemon_sock = sock;
+
+	/* Make sure the new socket is non-blocking. */
+	u_long iMode = 0;
+	if (ioctlsocket(conn->daemon_sock, FIONBIO,	&iMode) == SOCKET_ERROR) {
+		perrorf(g, "accept_connection: ioctlsocket");
+		return -1;
+	}
+	return 1;
+}
+#endif
+
+#if (!defined _WIN32 && !defined __WIN32__) || defined __CYGWIN__
 static ssize_t
 read_data (guestfs_h *g, struct connection *connv, void *bufv, size_t len)
 {
@@ -205,7 +278,75 @@ read_data (guestfs_h *g, struct connection *connv, void *bufv, size_t len)
 
   return original_len;
 }
+#else
+static ssize_t
+read_data (guestfs_h *g, struct connection *connv, void *bufv, size_t len)
+{
+  char *buf = bufv;
+  struct connection_socket *conn = (struct connection_socket *) connv;
+  size_t original_len = len;
 
+  if (conn->daemon_sock == -1) {
+    error (g, _("read_data: socket not connected"));
+    return -1;
+  }
+
+  while (len > 0) {
+    int r;
+    fd_set rfds;
+
+    FD_ZERO (&rfds);
+
+    FD_SET (conn->daemon_sock, &rfds);
+    if (conn->console_sock >= 0)
+      FD_SET (FD_TO_SOCKET(conn->console_sock), &rfds);
+
+    r = select(0, &rfds, NULL, NULL, NULL);
+    if (r > 0) {
+    	if (FD_ISSET(FD_TO_SOCKET(conn->console_sock), &rfds)) {
+    	  r = handle_log_message (g, conn);
+		  if (r <= 0)
+			return r;
+    	}
+
+    	if (FD_ISSET(conn->daemon_sock, &rfds)) {
+			int n = recv(conn->daemon_sock, buf, len, 0);
+			if (n == -1) {
+				if (errno == EINTR || errno == EAGAIN)
+					continue;
+				if (errno == ECONNRESET) /* essentially the same as EOF case */
+					goto closed;
+				perrorf(g, "read_data: read");
+				return -1;
+			}
+			if (n == 0) {
+				closed:
+				/* Even though qemu has gone away, there could be more log
+				 * messages in the console socket buffer in the kernel.  Read
+				 * them out here.
+				 */
+				if (g->verbose && conn->console_sock >= 0) {
+					while (handle_log_message(g, conn) == 1);
+				}
+				return 0;
+			}
+
+			buf += n;
+			len -= n;
+    	}
+    } else if (r == -1) {
+      if (errno == EINTR || errno == EAGAIN)
+        continue;
+      perrorf (g, "read_data: select");
+      return -1;
+    }
+  }
+
+  return original_len;
+}
+#endif
+
+#if (!defined _WIN32 && !defined __WIN32__) || defined __CYGWIN__
 static int
 can_read_data (guestfs_h *g, struct connection *connv)
 {
@@ -233,7 +374,40 @@ can_read_data (guestfs_h *g, struct connection *connv)
 
   return (fd.revents & POLLIN) != 0 ? 1 : 0;
 }
+#else
+static int
+can_read_data (guestfs_h *g, struct connection *connv)
+{
+  struct connection_socket *conn = (struct connection_socket *) connv;
+  int r;
+  fd_set rfds;
+  //struct timeval tv0 = { .tv_sec = 0, .tv_usec = 0};
+  const struct timeval timeout = {0, 0};
 
+
+  if (conn->daemon_sock == -1) {
+    error (g, _("can_read_data: socket not connected"));
+    return -1;
+  }
+
+  FD_ZERO (&rfds);
+
+  FD_SET (conn->daemon_sock, &rfds);
+
+ again:
+  r = select(0, &rfds, NULL, NULL, &timeout);
+  if (r == -1) {
+    if (errno == EINTR || errno == EAGAIN)
+      goto again;
+    perrorf (g, "can_read_data: select");
+    return -1;
+  }
+
+  return FD_ISSET(conn->daemon_sock, &rfds) ? 1 : 0;
+}
+#endif
+
+#if (!defined _WIN32 && !defined __WIN32__) || defined __CYGWIN__
 static ssize_t
 write_data (guestfs_h *g, struct connection *connv,
             const void *bufv, size_t len)
@@ -263,7 +437,8 @@ write_data (guestfs_h *g, struct connection *connv,
       nfds++;
     }
 
-    r = poll (fds, nfds, -1);
+    r = 1;//poll (fds, nfds, -1);
+    fds[0].revents = POLLOUT;
     if (r == -1) {
       if (errno == EINTR || errno == EAGAIN)
         continue;
@@ -297,6 +472,64 @@ write_data (guestfs_h *g, struct connection *connv,
 
   return original_len;
 }
+#else
+static ssize_t
+write_data (guestfs_h *g, struct connection *connv,
+            const void *bufv, size_t len)
+{
+  const char *buf = bufv;
+  struct connection_socket *conn = (struct connection_socket *) connv;
+  size_t original_len = len;
+
+  if (conn->daemon_sock == -1) {
+    error (g, _("write_data: socket not connected"));
+    return -1;
+  }
+
+  while (len > 0) {
+    int r;
+	fd_set rfds, wfds;
+
+	FD_ZERO (&rfds);
+	FD_ZERO (&wfds);
+
+	FD_SET (conn->daemon_sock, &wfds);
+	if (conn->console_sock >= 0)
+	  FD_SET (FD_TO_SOCKET(conn->console_sock), &rfds);
+
+	r = select(0, &rfds, &wfds, NULL, NULL);
+	if (r > 0) {
+		if (FD_ISSET(FD_TO_SOCKET(conn->console_sock), &rfds)) {
+		  r = handle_log_message (g, conn);
+		  if (r <= 0)
+			return r;
+		}
+
+		if (FD_ISSET(conn->daemon_sock, &wfds)) {
+			int n = send(conn->daemon_sock, buf, len, 0);
+			if (n == -1) {
+				if (errno == EINTR || errno == EAGAIN)
+					continue;
+				if (errno == EPIPE) /* Disconnected from guest (RHBZ#508713). */
+					return 0;
+				perrorf(g, "write_data: write");
+				return -1;
+			}
+
+			buf += n;
+			len -= n;
+		}
+	} else if (r == -1) {
+	  if (errno == EINTR || errno == EAGAIN)
+		continue;
+	  perrorf (g, "write_data: select");
+	  return -1;
+	}
+  }
+
+  return original_len;
+}
+#endif
 
 /* This is called if conn->console_sock becomes ready to read while we
  * are doing one of the connection operations above.  It reads and
@@ -326,7 +559,9 @@ handle_log_message (guestfs_h *g,
    *   based console (not yet implemented) we may be able to remove
    *   this.  XXX"
    */
+#if (!defined _WIN32 && !defined __WIN32__) || defined __CYGWIN__
   usleep (1000);
+#endif
 
   n = read (conn->console_sock, buf, sizeof buf);
   if (n == 0)
@@ -366,6 +601,7 @@ handle_log_message (guestfs_h *g,
   return 1;
 }
 
+#if (!defined _WIN32 && !defined __WIN32__) || defined __CYGWIN__
 static void
 free_conn_socket (guestfs_h *g, struct connection *connv)
 {
@@ -380,6 +616,22 @@ free_conn_socket (guestfs_h *g, struct connection *connv)
 
   free (conn);
 }
+#else
+static void
+free_conn_socket (guestfs_h *g, struct connection *connv)
+{
+  struct connection_socket *conn = (struct connection_socket *) connv;
+
+  if (conn->console_sock >= 0)
+    closesocket (FD_TO_SOCKET(conn->console_sock));
+  if (conn->daemon_sock >= 0)
+    closesocket (conn->daemon_sock);
+  if (conn->daemon_accept_sock >= 0)
+    closesocket (FD_TO_SOCKET(conn->daemon_accept_sock));
+
+  free (conn);
+}
+#endif
 
 static struct connection_ops ops = {
   .free_connection = free_conn_socket,
@@ -407,13 +659,23 @@ guestfs___new_conn_socket_listening (guestfs_h *g,
 
   assert (daemon_accept_sock >= 0);
 
+#if (!defined _WIN32 && !defined __WIN32__) || defined __CYGWIN__
   if (fcntl (daemon_accept_sock, F_SETFL, O_NONBLOCK) == -1) {
+#else
+  u_long iMode = 0;
+  if (ioctlsocket(FD_TO_SOCKET(daemon_accept_sock), FIONBIO, &iMode) == SOCKET_ERROR) {
+#endif
     perrorf (g, "new_conn_socket_listening: fcntl");
     return NULL;
   }
 
   if (console_sock >= 0) {
+#if (!defined _WIN32 && !defined __WIN32__) || defined __CYGWIN__
     if (fcntl (console_sock, F_SETFL, O_NONBLOCK) == -1) {
+#else
+    u_long iMode1 = 0;
+    if (ioctlsocket(FD_TO_SOCKET(console_sock), FIONBIO, &iMode1) == SOCKET_ERROR) {
+#endif
       perrorf (g, "new_conn_socket_listening: fcntl");
       return NULL;
     }
@@ -446,13 +708,23 @@ guestfs___new_conn_socket_connected (guestfs_h *g,
 
   assert (daemon_sock >= 0);
 
+#if (!defined _WIN32 && !defined __WIN32__) || defined __CYGWIN__
   if (fcntl (daemon_sock, F_SETFL, O_NONBLOCK) == -1) {
+#else
+  u_long iMode = 0;
+  if (ioctlsocket(FD_TO_SOCKET(daemon_sock), FIONBIO, &iMode) == SOCKET_ERROR) {
+#endif
     perrorf (g, "new_conn_socket_connected: fcntl");
     return NULL;
   }
 
   if (console_sock >= 0) {
+#if (!defined _WIN32 && !defined __WIN32__) || defined __CYGWIN__
     if (fcntl (console_sock, F_SETFL, O_NONBLOCK) == -1) {
+#else
+    u_long iMode1 = 0;
+    if (ioctlsocket(FD_TO_SOCKET(console_sock), FIONBIO, &iMode1) == SOCKET_ERROR) {
+#endif
       perrorf (g, "new_conn_socket_connected: fcntl");
       return NULL;
     }
diff --git a/src/guestfs-internal.h b/src/guestfs-internal.h
index 545146f..ad4efbc 100644
--- a/src/guestfs-internal.h
+++ b/src/guestfs-internal.h
@@ -814,6 +814,7 @@ extern void guestfs___cmd_add_arg_format (struct command *, const char *fs, ...)
 extern void guestfs___cmd_add_string_unquoted (struct command *, const char *str);
 extern void guestfs___cmd_add_string_quoted (struct command *, const char *str);
 extern void guestfs___cmd_set_stdout_callback (struct command *, cmd_stdout_callback stdout_callback, void *data, unsigned flags);
+extern void guestfs___cmd_set_stdin_data (struct command *cmd, const char *data);
 #define CMD_STDOUT_FLAG_LINE_BUFFER    0
 #define CMD_STDOUT_FLAG_UNBUFFERED      1
 #define CMD_STDOUT_FLAG_WHOLE_BUFFER    2
diff --git a/src/handle.c b/src/handle.c
index 687f059..1010a97 100644
--- a/src/handle.c
+++ b/src/handle.c
@@ -422,10 +422,10 @@ shutdown_backend (guestfs_h *g, int check_for_errors)
     ret = -1;
 
   /* Close sockets. */
-  if (g->conn) {
+  /*if (g->conn) {
     g->conn->ops->free_connection (g, g->conn);
     g->conn = NULL;
-  }
+  }*/
 
   guestfs___free_drives (g);
 
diff --git a/src/inspect-fs-windows.c b/src/inspect-fs-windows.c
index 20e4d7f..91c3372 100644
--- a/src/inspect-fs-windows.c
+++ b/src/inspect-fs-windows.c
@@ -376,6 +376,9 @@ check_windows_software_registry (guestfs_h *g, struct inspect_fs *fs)
 static int
 check_windows_system_registry (guestfs_h *g, struct inspect_fs *fs)
 {
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+  return -1;
+#else
   int r;
   size_t len = strlen (fs->windows_systemroot) + 64;
   char system[len];
@@ -540,6 +543,7 @@ check_windows_system_registry (guestfs_h *g, struct inspect_fs *fs)
   guestfs_hivex_close (g);
 
   return ret;
+#endif
 }
 
 /* Windows Registry HKLM\SYSTEM\MountedDevices uses a blob of data
@@ -551,6 +555,9 @@ check_windows_system_registry (guestfs_h *g, struct inspect_fs *fs)
 static char *
 map_registry_disk_blob (guestfs_h *g, const void *blob)
 {
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+  return NULL;
+#else
   CLEANUP_FREE_STRING_LIST char **devices = NULL;
   CLEANUP_FREE_PARTITION_LIST struct guestfs_partition_list *partitions = NULL;
   size_t i, j, len;
@@ -597,6 +604,7 @@ map_registry_disk_blob (guestfs_h *g, const void *blob)
  found_partition:
   /* Construct the full device name. */
   return safe_asprintf (g, "%s%d", devices[i], partitions->val[j].part_num);
+#endif
 }
 
 /* NB: This function DOES NOT test for the existence of the file.  It
diff --git a/src/launch-direct.c b/src/launch-direct.c
index 964a507..1bc33c5 100644
--- a/src/launch-direct.c
+++ b/src/launch-direct.c
@@ -257,6 +257,90 @@ debian_kvm_warning (guestfs_h *g)
 }
 
 static int
+get_listener_socket()
+{
+	union
+	{
+		struct sockaddr_in inaddr;
+		struct sockaddr addr;
+	} a;
+
+	int listener;
+	int reuse = 1;
+
+	listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+	if (listener == -1)
+		return -1;
+
+	memset(&a, 0, sizeof(a));
+	a.inaddr.sin_family = AF_INET;
+	a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+	a.inaddr.sin_port = 0;
+
+	if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, (socklen_t)sizeof(reuse)) == -1)
+		goto error1;
+
+	if (bind(listener, &a.addr, sizeof(a.inaddr)) == -1)
+		goto error1;
+
+	return listener;
+
+error1:
+	close(listener);
+
+	return -1;
+}
+
+static int
+mingw_socketpair(int socks[2])
+{
+	union
+	{
+		struct sockaddr_in inaddr;
+		struct sockaddr addr;
+	} a;
+
+	socklen_t addrlen;
+	int listener = get_listener_socket();
+	if (listener == -1)
+		return -1;
+
+	memset(&a, 0, sizeof(a));
+
+	if (getsockname(listener, &a.addr, &addrlen) == -1)
+		goto error1;
+
+	a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+	a.inaddr.sin_family = AF_INET;
+
+	if (listen(listener, 1) == -1)
+		goto error1;
+
+	socks[0] = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+	if (socks[0] == -1)
+		goto error1;
+
+	if (connect(socks[0], &a.addr, sizeof(a.inaddr)) == -1)
+		goto error2;
+
+	socks[1] = accept(listener, NULL, NULL);
+	if (socks[1] == -1)
+		goto error2;
+
+	close(listener);
+
+	return 0;
+
+error2:
+	close(socks[0]);
+
+error1:
+	close(listener);
+
+	return -1;
+}
+
+static int
 launch_direct (guestfs_h *g, void *datav, const char *arg)
 {
   struct backend_direct_data *data = datav;
@@ -316,6 +400,21 @@ launch_direct (guestfs_h *g, void *datav, const char *arg)
   /* Using virtio-serial, we need to create a local Unix domain socket
    * for qemu to connect to.
    */
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+  union
+  {
+    struct sockaddr_in inaddr;
+    struct sockaddr addr;
+  } a;
+  socklen_t addrlen;
+
+  daemon_accept_sock = get_listener_socket();
+  if (daemon_accept_sock == -1)
+    goto cleanup0;
+
+  if (getsockname(daemon_accept_sock, &a.addr, &addrlen) == -1)
+    goto cleanup0;
+#else
   snprintf (guestfsd_sock, sizeof guestfsd_sock, "%s/guestfsd.sock", g->tmpdir);
   unlink (guestfsd_sock);
 
@@ -333,6 +432,7 @@ launch_direct (guestfs_h *g, void *datav, const char *arg)
     perrorf (g, "bind");
     goto cleanup0;
   }
+#endif
 
   if (listen (daemon_accept_sock, 1) == -1) {
     perrorf (g, "listen");
@@ -340,7 +440,11 @@ launch_direct (guestfs_h *g, void *datav, const char *arg)
   }
 
   if (!g->direct_mode) {
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+	if (mingw_socketpair (sv) == -1) {
+#else
     if (socketpair (AF_LOCAL, SOCK_STREAM|SOCK_CLOEXEC, 0, sv) == -1) {
+#endif
       perrorf (g, "socketpair");
       goto cleanup0;
     }
@@ -617,7 +721,11 @@ launch_direct (guestfs_h *g, void *datav, const char *arg)
 
   /* Set up virtio-serial for the communications channel. */
   ADD_CMDLINE ("-chardev");
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+  ADD_CMDLINE_PRINTF ("socket,path=%d,id=channel0", ntohl(a.inaddr.sin_port));
+#else
   ADD_CMDLINE_PRINTF ("socket,path=%s,id=channel0", guestfsd_sock);
+#endif
   ADD_CMDLINE ("-device");
   ADD_CMDLINE ("virtserialport,chardev=channel0,name=org.libguestfs.channel.0");
 
diff --git a/src/launch-libvirt.c b/src/launch-libvirt.c
index 60213fd..1198bca 100644
--- a/src/launch-libvirt.c
+++ b/src/launch-libvirt.c
@@ -25,7 +25,6 @@
 #include <unistd.h>
 #include <fcntl.h>
 #include <limits.h>
-#include <grp.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
@@ -73,6 +72,8 @@
 #if defined(HAVE_LIBVIRT) && \
   LIBVIR_VERSION_NUMBER >= MIN_LIBVIRT_VERSION
 
+#include <grp.h>
+
 #ifndef HAVE_XMLBUFFERDETACH
 /* Added in libxml2 2.8.0.  This is mostly a copy of the function from
  * upstream libxml2, which is under a more permissive license.
diff --git a/src/launch-mingw.c b/src/launch-mingw.c
new file mode 100644
index 0000000..b944644
--- /dev/null
+++ b/src/launch-mingw.c
@@ -0,0 +1,1158 @@
+/* libguestfs
+ * Copyright (C) 2009-2013 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
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <sys/socket.h>
+
+#include <pcre.h>
+
+#include "cloexec.h"
+#include "ignore-value.h"
+
+#include "guestfs.h"
+#include "guestfs-internal.h"
+#include "guestfs-internal-actions.h"
+#include "guestfs_protocol.h"
+
+/* Compile all the regular expressions once when the shared library is
+ * loaded.  PCRE is thread safe so we're supposedly OK here if
+ * multiple threads call into the libguestfs API functions below
+ * simultaneously.
+ */
+static pcre *re_major_minor;
+
+static void compile_regexps (void) __attribute__((constructor));
+static void free_regexps (void) __attribute__((destructor));
+
+static void
+compile_regexps (void)
+{
+  const char *err;
+  int offset;
+
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+#define kill(pid,sig) TerminateProcess ((HANDLE) (pid), sig)
+#endif
+
+#define COMPILE(re,pattern,options)                                     \
+  do {                                                                  \
+    re = pcre_compile ((pattern), (options), &err, &offset, NULL);      \
+    if (re == NULL) {                                                   \
+      ignore_value (write (2, err, strlen (err)));                      \
+      abort ();                                                         \
+    }                                                                   \
+  } while (0)
+
+  COMPILE (re_major_minor, "(\\d+)\\.(\\d+)", 0);
+}
+
+static void
+free_regexps (void)
+{
+  pcre_free (re_major_minor);
+}
+
+static char *make_appliance_dev (guestfs_h *g, int virtio_scsi);
+static void print_qemu_command_line (guestfs_h *g, char **argv);
+static int qemu_supports (guestfs_h *g, const char *option);
+static int qemu_supports_device (guestfs_h *g, const char *device_name);
+static int qemu_supports_virtio_scsi (guestfs_h *g);
+static char *qemu_drive_param (guestfs_h *g, const struct drive *drv, size_t index);
+
+/* Like 'add_cmdline' but allowing a shell-quoted string of zero or
+ * more options.  XXX The unquoting is not very clever.
+ */
+static void
+add_cmdline_shell_unquoted (guestfs_h *g, struct stringsbuf *sb,
+                            const char *options)
+{
+  char quote;
+  const char *startp, *endp, *nextp;
+
+  while (*options) {
+    quote = *options;
+    if (quote == '\'' || quote == '"')
+      startp = options+1;
+    else {
+      startp = options;
+      quote = ' ';
+    }
+
+    endp = strchr (options, quote);
+    if (endp == NULL) {
+      if (quote != ' ') {
+        fprintf (stderr,
+                 _("unclosed quote character (%c) in command line near: %s"),
+                 quote, options);
+        _exit (EXIT_FAILURE);
+      }
+      endp = options + strlen (options);
+    }
+
+    if (quote == ' ') {
+      if (endp[0] == '\0')
+        nextp = endp;
+      else
+        nextp = endp+1;
+    }
+    else {
+      if (!endp[1])
+        nextp = endp+1;
+      else if (endp[1] == ' ')
+        nextp = endp+2;
+      else {
+        fprintf (stderr, _("cannot parse quoted string near: %s"), options);
+        _exit (EXIT_FAILURE);
+      }
+    }
+    while (*nextp && *nextp == ' ')
+      nextp++;
+
+    guestfs___add_string_nodup (g, sb,
+                                safe_strndup (g, startp, endp-startp));
+
+    options = nextp;
+  }
+}
+
+#define QEMU_PORT 0
+static int
+get_listener_socket()
+{
+	union
+	{
+		struct sockaddr_in inaddr;
+		struct sockaddr addr;
+	} a;
+
+	int listener;
+	int reuse = 1;
+
+	listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+	if (listener == -1)
+		return -1;
+
+	memset(&a, 0, sizeof(a));
+	a.inaddr.sin_family = AF_INET;
+	a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+	a.inaddr.sin_port = 0;//htons(QEMU_PORT);
+
+	if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, (socklen_t)sizeof(reuse)) == -1)
+		goto error1;
+
+	if (bind(listener, &a.addr, sizeof(a.inaddr)) == -1)
+		goto error1;
+
+	return listener;
+
+error1:
+	close(listener);
+
+	return -1;
+}
+
+static int
+mingw_socketpair(int socks[2])
+{
+	union
+	{
+		struct sockaddr_in inaddr;
+		struct sockaddr addr;
+	} a;
+
+	socklen_t addrlen = sizeof a.addr;
+	int listener;
+	int reuse = 1;
+
+	listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+	if (listener == -1)
+		return -1;
+
+	memset(&a, 0, sizeof(a));
+	a.inaddr.sin_family = AF_INET;
+	a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+	a.inaddr.sin_port = 0;
+
+	if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, (socklen_t)sizeof(reuse)) == -1)
+		goto error1;
+
+	if (bind(listener, &a.addr, sizeof(a.inaddr)) == -1)
+		goto error1;
+
+	if (getsockname(listener, &a.addr, &addrlen) == -1)
+		goto error1;
+
+	// port number for console to listen to
+	int port = ntohs(a.inaddr.sin_port);
+
+	if (listen(listener, 1) == -1)
+		goto error1;
+
+	a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+	a.inaddr.sin_family = AF_INET;
+
+	socks[0] = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+	if (socks[0] == -1)
+		goto error1;
+
+	if (connect(socks[0], &a.addr, sizeof(a.inaddr)) == -1)
+		goto error2;
+
+	socks[1] = accept(listener, NULL, NULL);
+	if (socks[1] == -1)
+		goto error2;
+
+	close(listener);
+
+	return 0;
+
+error2:
+	close(socks[0]);
+
+error1:
+	close(listener);
+
+	return -1;
+}
+
+
+enum spawn_pipe_lgpl_pipe_t {
+	SPAWN_PIPE_LGPL_STDIN = 1,
+	SPAWN_PIPE_LGPL_STDOUT = 2,
+	SPAWN_PIPE_LGPL_STDERR = 4,
+	SPAWN_PIPE_LGPL_STDIN_PROVIDED = 8,
+	SPAWN_PIPE_LGPL_STDOUT_PROVIDED = 16,
+	SPAWN_PIPE_LGPL_STDERR_PROVIDED = 32,
+};
+typedef enum spawn_pipe_lgpl_pipe_t spawn_pipe_lgpl_pipe_t;
+
+static void
+restore_file_desc(int orig_stdin, int orig_stdout, int orig_stderr)
+{
+	if (orig_stdin >= 0)
+	{
+		dup2(orig_stdin, STDIN_FILENO);
+		close(orig_stdin);
+	}
+	if (orig_stdout >= 0)
+	{
+		dup2(orig_stdout, STDOUT_FILENO);
+		close(orig_stdout);
+	}
+	if (orig_stderr >= 0)
+	{
+		dup2(orig_stderr, STDERR_FILENO);
+		close(orig_stderr);
+	}
+}
+
+static pid_t
+spawn_dup (const char *prog_path, const char **prog_argv,
+             spawn_pipe_lgpl_pipe_t pipes, int fd[3])
+{
+	int ifd[2] = {-1, -1},
+		ofd[2] = {-1, -1},
+		efd[2] = {-1, -1},
+		orig_stdin = -1, orig_stdout = -1, orig_stderr = -1,
+		child = -1;
+
+	/* Prepare pipes (or use provided file descriptors), save original std file descriptors and replace */
+	if (pipes & SPAWN_PIPE_LGPL_STDIN)
+	{
+		if (pipes & SPAWN_PIPE_LGPL_STDIN_PROVIDED)
+		{
+			ifd[0] = fd[STDIN_FILENO];
+
+		} else if (pipe2(ifd, 0) < 0)
+			goto exit1;
+
+		else fd[STDIN_FILENO] = ifd[1];
+
+		orig_stdin = dup_cloexec(STDIN_FILENO);
+		if (orig_stdin < 0)
+			goto exit1;
+
+		if (dup2(ifd[0], STDIN_FILENO) < 0)
+			goto exit1;
+	}
+
+	if (pipes & SPAWN_PIPE_LGPL_STDOUT)
+	{
+		if (pipes & SPAWN_PIPE_LGPL_STDOUT_PROVIDED)
+		{
+			ofd[1] = fd[STDOUT_FILENO];
+
+		} else if (pipe2(ofd, 0) < 0)
+			goto exit1;
+
+		else fd[STDOUT_FILENO] = ofd[0];
+
+		orig_stdout = dup_cloexec(STDOUT_FILENO);
+		if (orig_stdout < 0)
+			goto exit1;
+
+		if (dup2(ofd[1], STDOUT_FILENO) < 0)
+			goto exit1;
+	}
+	if (pipes & SPAWN_PIPE_LGPL_STDERR)
+	{
+		if (pipes & SPAWN_PIPE_LGPL_STDERR_PROVIDED)
+		{
+			efd[1] = fd[STDERR_FILENO];
+
+		} else if (pipe2(efd, 0) < 0)
+			goto exit1;
+
+		else fd[STDERR_FILENO] = efd[0];
+
+		orig_stderr = dup_cloexec(STDERR_FILENO);
+		if (orig_stderr < 0)
+			goto exit1;
+
+		if (dup2(efd[1], STDERR_FILENO) < 0)
+			goto exit1;
+	}
+
+	/* Execute child */
+	child = spawnvp(P_NOWAIT, prog_path, prog_argv);
+
+exit1:
+	/* Close pipes */
+	if ((pipes & SPAWN_PIPE_LGPL_STDIN) && !(pipes & SPAWN_PIPE_LGPL_STDIN_PROVIDED) && (ifd[0] != -1))
+	{
+		close(ifd[0]);
+		if (child == -1) close(ifd[1]);
+	}
+	if ((pipes & SPAWN_PIPE_LGPL_STDOUT) && !(pipes & SPAWN_PIPE_LGPL_STDOUT_PROVIDED) && (ofd[1] != -1))
+	{
+		if (child == -1) close(ofd[0]);
+		close(ofd[1]);
+	}
+	if ((pipes & SPAWN_PIPE_LGPL_STDERR) && !(pipes & SPAWN_PIPE_LGPL_STDERR_PROVIDED) && (efd[1] != -1))
+	{
+		if (child == -1) close(efd[0]);
+		close(efd[1]);
+	}
+
+	/* Restore original std file descriptors */
+	restore_file_desc(orig_stdin, orig_stdout, orig_stderr);
+
+	return child;
+}
+
+static int
+launch_mingw (guestfs_h *g, const char *arg)
+{
+  int daemon_accept_sock = -1, console_sock = -1;
+  int r;
+  int sv[2];
+  char guestfsd_sock[256];
+  CLEANUP_FREE char *kernel = NULL, *initrd = NULL, *appliance = NULL;
+  int has_appliance_drive;
+  CLEANUP_FREE char *appliance_dev = NULL;
+  uint32_t size;
+  CLEANUP_FREE void *buf = NULL;
+  union
+  {
+    struct sockaddr_in inaddr;
+    struct sockaddr addr;
+  } a;
+  socklen_t addrlen = sizeof(a.addr);
+  CLEANUP_FREE_STRINGSBUF DECLARE_STRINGSBUF (cmdline);
+
+  /* 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->nr_drives) {
+    error (g, _("you must call guestfs_add_drive before guestfs_launch"));
+    return -1;
+  }
+
+  guestfs___launch_send_progress (g, 0);
+
+  TRACE0 (launch_build_appliance_start);
+
+  /* Locate and/or build the appliance. */
+  if (guestfs___build_appliance (g, &kernel, &initrd, &appliance) == -1)
+    return -1;
+  has_appliance_drive = appliance != NULL;
+
+  TRACE0 (launch_build_appliance_end);
+
+  guestfs___launch_send_progress (g, 3);
+
+  if (g->verbose)
+    guestfs___print_timestamped_message (g, "begin testing qemu features");
+
+  /* Using virtio-serial, we need to create a local Unix domain socket
+   * for qemu to connect to.
+   */
+  daemon_accept_sock = get_listener_socket();
+  if (daemon_accept_sock == -1)
+    goto cleanup0;
+
+  if (getsockname(daemon_accept_sock, &a.addr, &addrlen) == -1)
+    goto cleanup0;
+
+  if (listen (daemon_accept_sock, 1) == -1) {
+    perrorf (g, "listen");
+    goto cleanup0;
+  }
+
+  if (!g->direct_mode) {
+    if (mingw_socketpair (sv) == -1) {
+	    perrorf (g, "socketpair");
+	    goto cleanup0;
+    }
+  }
+
+  /* Get qemu help text and version. */
+  if (qemu_supports (g, NULL) == -1)
+    goto cleanup0;
+
+  if (g->verbose)
+    guestfs___print_timestamped_message (g, "finished testing qemu features");
+
+#define ADD_CMDLINE(str) \
+  guestfs___add_string (g, &cmdline, (str))
+#define ADD_CMDLINE_STRING_NODUP(str) \
+  guestfs___add_string_nodup (g, &cmdline, (str))
+#define ADD_CMDLINE_PRINTF(fs,...) \
+  guestfs___add_sprintf (g, &cmdline, (fs), ##__VA_ARGS__)
+#define END_CMDLINE() \
+  guestfs___end_stringsbuf (g, &cmdline);
+
+  /* Prepare QEMU command
+   */
+  ADD_CMDLINE (g->qemu);
+
+  /* CVE-2011-4127 mitigation: Disable SCSI ioctls on virtio-blk
+   * devices.  The -global option must exist, but you can pass any
+   * strings to it so we don't need to check for the specific virtio
+   * feature.
+   */
+  if (qemu_supports (g, "-global")) {
+    ADD_CMDLINE ("-global");
+    ADD_CMDLINE ("virtio-blk-pci.scsi=off");
+  }
+
+  if (qemu_supports (g, "-nodefconfig"))
+    ADD_CMDLINE ("-nodefconfig");
+
+  /* Newer versions of qemu (from around 2009/12) changed the
+   * behaviour of monitors so that an implicit '-monitor stdio' is
+   * assumed if we are in -nographic mode and there is no other
+   * -monitor option.  Only a single stdio device is allowed, so
+   * this broke the '-serial stdio' option.  There is a new flag
+   * called -nodefaults which gets rid of all this default crud, so
+   * let's use that to avoid this and any future surprises.
+   */
+  if (qemu_supports (g, "-nodefaults"))
+    ADD_CMDLINE ("-nodefaults");
+
+  ADD_CMDLINE ("-nographic");
+  //ADD_CMDLINE ("-device");
+  //ADD_CMDLINE ("VGA");
+
+  /* Under Mingw QEMU has a bug that forces you to create at least one monitor device ("-nodefaults" removes everything)
+   *
+   */
+  ADD_CMDLINE ("-monitor");
+  ADD_CMDLINE ("telnet:127.0.0.1:0,server,nowait");
+
+  /* The qemu -machine option (added 2010-12) is a bit more sane
+   * since it falls back through various different acceleration
+   * modes, so try that first (thanks Markus Armbruster).
+   */
+  if (qemu_supports (g, "-machine")) {
+    ADD_CMDLINE ("-machine");
+    ADD_CMDLINE ("accel=tcg");
+  } 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"))
+      ADD_CMDLINE ("-enable-kvm");
+  }
+
+  if (g->smp > 1) {
+	ADD_CMDLINE ("-smp");
+	ADD_CMDLINE_PRINTF ("%d", g->smp);
+  }
+
+  ADD_CMDLINE ("-m");
+  ADD_CMDLINE_PRINTF ("%d", g->memsize);
+
+  /* Force exit instead of reboot on panic */
+  ADD_CMDLINE ("-no-reboot");
+
+  /* These options recommended by KVM developers to improve reliability. */
+#ifndef __arm__
+  /* qemu-system-arm advertises the -no-hpet option but if you try
+   * to use it, it usefully says:
+   *   "Option no-hpet not supported for this target".
+   * Cheers qemu developers.  How many years have we been asking for
+   * capabilities?  Could be 3 or 4 years, I forget.
+   */
+  if (qemu_supports (g, "-no-hpet"))
+    ADD_CMDLINE ("-no-hpet");
+#endif
+
+  if (qemu_supports (g, "-rtc-td-hack"))
+    ADD_CMDLINE ("-rtc-td-hack");
+
+  ADD_CMDLINE ("-kernel");
+  ADD_CMDLINE (kernel);
+  ADD_CMDLINE ("-initrd");
+  ADD_CMDLINE (initrd);
+
+  /* Add drives */
+  struct drive *drv;
+  size_t i;
+  int virtio_scsi = qemu_supports_virtio_scsi (g);
+
+  if (virtio_scsi) {
+	/* Create the virtio-scsi bus. */
+    ADD_CMDLINE ("-device");
+    ADD_CMDLINE ("virtio-scsi-pci,id=scsi");
+  }
+
+  ITER_DRIVES (g, i, drv) {
+	/* Construct the final -drive parameter. */
+	CLEANUP_FREE char *buff = qemu_drive_param (g, drv, i);
+
+	ADD_CMDLINE ("-drive");
+	ADD_CMDLINE (buff);
+
+	if (virtio_scsi && drv->iface == NULL) {
+	  ADD_CMDLINE ("-device");
+	  ADD_CMDLINE_PRINTF ("scsi-hd,drive=hd%zu", i);
+	}
+  }
+
+  /* Add the ext2 appliance drive (after all the drives). */
+  if (has_appliance_drive) {
+	const char *cachemode = "";
+	if (qemu_supports (g, "cache=")) {
+	  if (qemu_supports (g, "unsafe"))
+		cachemode = ",cache=unsafe";
+	  else if (qemu_supports (g, "writeback"))
+		cachemode = ",cache=writeback";
+	}
+
+	ADD_CMDLINE ("-drive");
+	ADD_CMDLINE_PRINTF ("file=%s,snapshot=on,id=appliance,if=%s%s",
+			  appliance, virtio_scsi ? "none" : "virtio", cachemode);
+
+	if (virtio_scsi) {
+      ADD_CMDLINE ("-device");
+      ADD_CMDLINE ("scsi-hd,drive=appliance");
+	}
+
+	appliance_dev = make_appliance_dev (g, virtio_scsi);
+  }
+
+  /* Create the virtio serial bus. */
+  ADD_CMDLINE ("-device");
+  ADD_CMDLINE ("virtio-serial");
+
+#if 0
+  /* Use virtio-console (a variant form of virtio-serial) for the
+   * guest's serial console.
+   */
+  ADD_CMDLINE ("-chardev");
+  ADD_CMDLINE ("stdio,id=console");
+  ADD_CMDLINE ("-device");
+  ADD_CMDLINE ("virtconsole,chardev=console,name=org.libguestfs.console.0");
+#else
+  /* When the above works ...  until then: */
+  //ADD_CMDLINE ("-serial");
+  //ADD_CMDLINE ("stdio");
+#endif
+
+  if (qemu_supports_device (g, "Serial Graphics Adapter")) {
+	/* Use sgabios instead of vgabios.  This means we'll see BIOS
+	 * messages on the serial port, and also works around this bug
+	 * in qemu 1.1.0:
+	 * https://bugs.launchpad.net/qemu/+bug/1021649
+	 * QEmu has included sgabios upstream since just before 1.0.
+	 */
+	//ADD_CMDLINE ("-device");
+    //ADD_CMDLINE ("sga");
+  }
+
+  /* Set up virtio-serial for the communications channel. */
+  ADD_CMDLINE ("-chardev");
+  ntohl(1);
+  ADD_CMDLINE_PRINTF ("socket,host=127.0.0.1,port=%d,id=channel0", ntohs(a.inaddr.sin_port));
+  ADD_CMDLINE ("-device");
+  ADD_CMDLINE ("virtserialport,chardev=channel0,name=org.libguestfs.channel.0");
+
+  /* Enable user networking. */
+  if (g->enable_network) {
+    ADD_CMDLINE ("-netdev");
+    ADD_CMDLINE ("user,id=usernet,net=169.254.0.0/16");
+    ADD_CMDLINE ("-device");
+    ADD_CMDLINE ("virtio-net-pci,netdev=usernet");
+  }
+
+  ADD_CMDLINE ("-append");
+  CLEANUP_FREE char *appcmdline =
+	guestfs___appliance_command_line (g, appliance_dev, 0);
+  ADD_CMDLINE_PRINTF ("\"%s\"", appcmdline);
+
+  /* Add the extra options for the qemu command line specified
+   * at configure time.
+   * FIXME - NOT SUPPORTED FOR NOW
+   */
+  if (STRNEQ (QEMU_OPTIONS, ""))
+    add_cmdline_shell_unquoted (g, &cmdline, QEMU_OPTIONS);
+
+  struct qemu_param *qp;
+  /* Note: custom command line parameters must come last so that
+   * qemu -set parameters can modify previously added options.
+   */
+  /* Add any qemu parameters. */
+  for (qp = g->qemu_params; qp; qp = qp->next) {
+    ADD_CMDLINE (qp->qemu_param);
+	if (qp->qemu_value)
+      ADD_CMDLINE (qp->qemu_value);
+  }
+
+  /* Execute QEMU
+   */
+  END_CMDLINE();
+
+  if (g->verbose)
+    print_qemu_command_line (g, cmdline.argv);
+
+  int fds[3];
+
+  if (!g->direct_mode) {
+    fds[STDIN_FILENO] = sv[0];
+    fds[STDOUT_FILENO] = sv[0];
+    fds[STDERR_FILENO] = sv[0];
+
+    g->direct.pid = spawn_dup(cmdline.argv[0], (const char **)cmdline.argv,
+    		SPAWN_PIPE_LGPL_STDIN|SPAWN_PIPE_LGPL_STDIN_PROVIDED|
+    		SPAWN_PIPE_LGPL_STDOUT|SPAWN_PIPE_LGPL_STDOUT_PROVIDED|
+    		SPAWN_PIPE_LGPL_STDERR|SPAWN_PIPE_LGPL_STDERR_PROVIDED,
+    		fds);
+
+  } else g->direct.pid = spawn_dup(cmdline.argv[0], (const char **)cmdline.argv,
+		  0, fds);
+
+  if (g->direct.pid == -1) {
+    perrorf (g, "spawn_dup");
+    if (!g->direct_mode) {
+      close(sv[0]);
+      close(sv[1]);
+    }
+    goto cleanup0;
+  }
+
+  /* No recovery process in mingw driver .. for now
+   */
+  g->direct.recoverypid = -1;
+
+  if (!g->direct_mode) {
+    /* Close the other end of the socketpair. */
+    close(sv[0]);
+
+    console_sock = sv[1];       /* stdin of child */
+    sv[1] = -1;
+  }
+
+  g->state = LAUNCHING;
+
+  /* Wait for qemu to start and to connect back to us via
+   * virtio-serial and send the GUESTFS_LAUNCH_FLAG message.
+   */
+  g->conn =
+    guestfs___new_conn_socket_listening (g, daemon_accept_sock, console_sock);
+  if (!g->conn)
+    goto cleanup1;
+
+  /* g->conn now owns these sockets. */
+  daemon_accept_sock = console_sock = -1;
+
+  r = g->conn->ops->accept_connection (g, g->conn);
+  if (r == -1)
+    goto cleanup1;
+  if (r == 0) {
+    guestfs___launch_failed_error (g);
+    goto cleanup1;
+  }
+
+  /* NB: We reach here just because qemu has opened the socket.  It
+   * does not mean the daemon is up until we read the
+   * GUESTFS_LAUNCH_FLAG below.  Failures in qemu startup can still
+   * happen even if we reach here, even early failures like not being
+   * able to open a drive.
+   */
+  r = guestfs___recv_from_daemon (g, &size, &buf);
+
+  if (r == -1) {
+    guestfs___launch_failed_error (g);
+    goto cleanup1;
+  }
+
+  if (size != GUESTFS_LAUNCH_FLAG) {
+    guestfs___launch_failed_error (g);
+    goto cleanup1;
+  }
+
+  if (g->verbose)
+    guestfs___print_timestamped_message (g, "appliance is up");
+
+  /* This is possible in some really strange situations, such as
+   * guestfsd starts up OK but then qemu immediately exits.  Check for
+   * it because the caller is probably expecting to be able to send
+   * commands after this function returns.
+   */
+  if (g->state != READY) {
+    error (g, _("qemu launched and contacted daemon, but state != READY"));
+    goto cleanup1;
+  }
+
+  TRACE0 (launch_end);
+
+  guestfs___launch_send_progress (g, 12);
+
+  if (has_appliance_drive)
+    guestfs___add_dummy_appliance_drive (g);
+
+  return 0;
+
+ cleanup1:
+  if (!g->direct_mode && sv[1] >= 0)
+    close (sv[1]);
+  if (g->direct.pid > 0) kill (g->direct.pid, 9);
+  if (g->direct.recoverypid > 0) kill (g->direct.recoverypid, 9);
+  if (g->direct.pid > 0) waitpid (g->direct.pid, NULL, 0);
+  if (g->direct.recoverypid > 0) waitpid (g->direct.recoverypid, NULL, 0);
+  g->direct.pid = 0;
+  g->direct.recoverypid = 0;
+  memset (&g->launch_t, 0, sizeof g->launch_t);
+
+ cleanup0:
+  if (daemon_accept_sock >= 0)
+    close (daemon_accept_sock);
+  if (console_sock >= 0)
+    close (console_sock);
+  if (g->conn) {
+    g->conn->ops->free_connection (g, g->conn);
+    g->conn = NULL;
+  }
+  g->state = CONFIG;
+  return -1;
+}
+
+/* Calculate the appliance device name.
+ *
+ * The easy thing would be to use g->nr_drives (indeed, that's what we
+ * used to do).  However this breaks if some of the drives being added
+ * use the deprecated 'iface' parameter.  To further add confusion,
+ * the format of the 'iface' parameter has never been defined, but
+ * given existing usage we can assume it has one of only three values:
+ * NULL, "ide" or "virtio" (which means virtio-blk).  See RHBZ#975797.
+ */
+static char *
+make_appliance_dev (guestfs_h *g, int virtio_scsi)
+{
+  size_t i, index = 0;
+  struct drive *drv;
+  char dev[64] = "/dev/Xd";
+
+  /* Calculate the index of the drive. */
+  ITER_DRIVES (g, i, drv) {
+    if (virtio_scsi) {
+      if (drv->iface == NULL || STREQ (drv->iface, "ide"))
+        index++;
+    }
+    else /* virtio-blk */ {
+      if (drv->iface == NULL || STRNEQ (drv->iface, "virtio"))
+        index++;
+    }
+  }
+
+  dev[5] = virtio_scsi ? 's' : 'v';
+  guestfs___drive_name (index, &dev[7]);
+
+  return safe_strdup (g, dev);  /* Caller frees. */
+}
+
+/* This is called from the forked subprocess just before qemu runs, so
+ * it can just print the message straight to stderr, where it will be
+ * picked up and funnelled through the usual appliance event API.
+ */
+static void
+print_qemu_command_line (guestfs_h *g, char **argv)
+{
+  int i = 0;
+  int needs_quote;
+
+  struct timeval tv;
+  gettimeofday (&tv, NULL);
+  fprintf (stderr, "[%05" PRIi64 "ms] ",
+           guestfs___timeval_diff (&g->launch_t, &tv));
+
+  while (argv[i]) {
+    if (argv[i][0] == '-') /* -option starts a new line */
+      fprintf (stderr, " \\\n   ");
+
+    if (i > 0) fputc (' ', stderr);
+
+    /* Does it need shell quoting?  This only deals with simple cases. */
+    needs_quote = strcspn (argv[i], " ") != strlen (argv[i]);
+
+    if (needs_quote) fputc ('\'', stderr);
+    fprintf (stderr, "%s", argv[i]);
+    if (needs_quote) fputc ('\'', stderr);
+    i++;
+  }
+
+  fputc ('\n', stderr);
+}
+
+static void parse_qemu_version (guestfs_h *g);
+static void read_all (guestfs_h *g, void *retv, const char *buf, size_t len);
+
+/* Test qemu binary (or wrapper) runs, and do 'qemu -help' and
+ * 'qemu -version' so we know what options this qemu supports and
+ * the version.
+ */
+static int
+test_qemu (guestfs_h *g)
+{
+  CLEANUP_CMD_CLOSE struct command *cmd1 = guestfs___new_command (g);
+  CLEANUP_CMD_CLOSE struct command *cmd2 = guestfs___new_command (g);
+  CLEANUP_CMD_CLOSE struct command *cmd3 = guestfs___new_command (g);
+  int r;
+
+  free (g->direct.qemu_help);
+  g->direct.qemu_help = NULL;
+  free (g->direct.qemu_version);
+  g->direct.qemu_version = NULL;
+  free (g->direct.qemu_devices);
+  g->direct.qemu_devices = NULL;
+
+  guestfs___cmd_add_arg (cmd1, g->qemu);
+  guestfs___cmd_add_arg (cmd1, "-nographic");
+  guestfs___cmd_add_arg (cmd1, "-help");
+  guestfs___cmd_clear_capture_errors (cmd1);
+  guestfs___cmd_set_stdout_callback (cmd1, read_all, &g->direct.qemu_help,
+                                     CMD_STDOUT_FLAG_WHOLE_BUFFER);
+  r = guestfs___cmd_run (cmd1);
+  if (r == -1 || !WIFEXITED (r) || WEXITSTATUS (r) != 0)
+    goto error;
+
+  guestfs___cmd_add_arg (cmd2, g->qemu);
+  guestfs___cmd_add_arg (cmd2, "-nographic");
+  guestfs___cmd_add_arg (cmd2, "-version");
+  guestfs___cmd_clear_capture_errors (cmd2);
+  guestfs___cmd_set_stdout_callback (cmd2, read_all, &g->direct.qemu_version,
+                                     CMD_STDOUT_FLAG_WHOLE_BUFFER);
+  r = guestfs___cmd_run (cmd2);
+  if (r == -1 || !WIFEXITED (r) || WEXITSTATUS (r) != 0)
+    goto error;
+
+  parse_qemu_version (g);
+
+  guestfs___cmd_add_arg (cmd3, g->qemu);
+  guestfs___cmd_add_arg (cmd3, "-nographic");
+  guestfs___cmd_add_arg (cmd3, "-nodefaults");
+  guestfs___cmd_add_arg (cmd3, "-nodefconfig");
+  guestfs___cmd_add_arg (cmd3, "-qmp");
+  guestfs___cmd_add_arg (cmd3, "stdio");
+  guestfs___cmd_clear_capture_errors (cmd3);
+  //guestfs___cmd_set_stderr_to_stdout (cmd3);
+  guestfs___cmd_set_stdout_callback (cmd3, read_all, &g->direct.qemu_devices,
+                                     CMD_STDOUT_FLAG_WHOLE_BUFFER);
+  guestfs___cmd_set_stdin_data (cmd3, "{ \"execute\": \"qmp_capabilities\" }\r\n{ \"execute\": \"query-devices\" }\r\n{ \"execute\": \"quit\" }\r\n");
+  r = guestfs___cmd_run (cmd3);
+  if (r == -1 || !WIFEXITED (r) || WEXITSTATUS (r) != 0)
+    goto error;
+
+  return 0;
+
+ error:
+  if (r == -1)
+    return -1;
+
+  guestfs___external_command_failed (g, r, g->qemu, NULL);
+  return -1;
+}
+
+/* Parse g->direct.qemu_version (if not NULL) into the major and minor
+ * version of qemu, but don't fail if parsing is not possible.
+ */
+static void
+parse_qemu_version (guestfs_h *g)
+{
+  CLEANUP_FREE char *major_s = NULL, *minor_s = NULL;
+  int major_i, minor_i;
+
+  g->direct.qemu_version_major = 0;
+  g->direct.qemu_version_minor = 0;
+
+  if (!g->direct.qemu_version)
+    return;
+
+  if (!match2 (g, g->direct.qemu_version, re_major_minor, &major_s, &minor_s)) {
+  parse_failed:
+    debug (g, "%s: failed to parse qemu version string '%s'",
+           __func__, g->direct.qemu_version);
+    return;
+  }
+
+  major_i = guestfs___parse_unsigned_int (g, major_s);
+  if (major_i == -1)
+    goto parse_failed;
+
+  minor_i = guestfs___parse_unsigned_int (g, minor_s);
+  if (minor_i == -1)
+    goto parse_failed;
+
+  g->direct.qemu_version_major = major_i;
+  g->direct.qemu_version_minor = minor_i;
+
+  debug (g, "qemu version %d.%d", major_i, minor_i);
+}
+
+static void
+read_all (guestfs_h *g, void *retv, const char *buf, size_t len)
+{
+  char **ret = retv;
+
+  *ret = safe_strndup (g, buf, len);
+}
+
+/* Test if option is supported by qemu command line (just by grepping
+ * the help text).
+ *
+ * The first time this is used, it has to run the external qemu
+ * binary.  If that fails, it returns -1.
+ *
+ * To just do the first-time run of the qemu binary, call this with
+ * option == NULL, in which case it will return -1 if there was an
+ * error doing that.
+ */
+static int
+qemu_supports (guestfs_h *g, const char *option)
+{
+  if (!g->direct.qemu_help) {
+    if (test_qemu (g) == -1)
+      return -1;
+  }
+
+  if (option == NULL)
+    return 1;
+
+  return strstr (g->direct.qemu_help, option) != NULL;
+}
+
+/* Test if device is supported by qemu (currently just greps the -device ?
+ * output).
+ */
+static int
+qemu_supports_device (guestfs_h *g, const char *device_name)
+{
+  if (!g->direct.qemu_devices) {
+    if (test_qemu (g) == -1)
+      return -1;
+  }
+
+  return strstr (g->direct.qemu_devices, device_name) != NULL;
+}
+
+static int
+old_or_broken_virtio_scsi (guestfs_h *g)
+{
+  /* qemu 1.1 claims to support virtio-scsi but in reality it's broken. */
+  if (g->direct.qemu_version_major == 1 && g->direct.qemu_version_minor < 2)
+    return 1;
+
+  return 0;
+}
+
+/* Returns 1 = use virtio-scsi, or 0 = use virtio-blk. */
+static int
+qemu_supports_virtio_scsi (guestfs_h *g)
+{
+  int r;
+
+  if (!g->direct.qemu_help) {
+    if (test_qemu (g) == -1)
+      return 0;                 /* safe option? */
+  }
+
+  /* g->direct.virtio_scsi has these values:
+   *   0 = untested (after handle creation)
+   *   1 = supported
+   *   2 = not supported (use virtio-blk)
+   *   3 = test failed (use virtio-blk)
+   */
+  if (g->direct.virtio_scsi == 0) {
+    if (old_or_broken_virtio_scsi (g))
+      g->direct.virtio_scsi = 2;
+    else {
+      r = qemu_supports_device (g, "virtio-scsi-pci");
+      if (r > 0)
+        g->direct.virtio_scsi = 1;
+      else if (r == 0)
+        g->direct.virtio_scsi = 2;
+      else
+        g->direct.virtio_scsi = 3;
+    }
+  }
+
+  return g->direct.virtio_scsi == 1;
+}
+
+/* Convert a struct drive into a qemu -drive parameter.  Note that if
+ * using virtio-scsi, then the code above adds a second -device
+ * parameter to connect this drive to the SCSI HBA, as is required by
+ * virtio-scsi.
+ */
+static char *
+qemu_drive_param (guestfs_h *g, const struct drive *drv, size_t index)
+{
+  CLEANUP_FREE char *file = NULL, *escaped_file = NULL;
+  size_t i, len;
+  const char *iface;
+  char *p;
+
+  /* Make the file= parameter. */
+  file = guestfs___drive_source_qemu_param (g, &drv->src);
+
+  /* Escape the file= parameter.  Every ',' becomes ',,'. */
+  len = strlen (file);
+  p = escaped_file = safe_malloc (g, len*2 + 1); /* max length of escaped name*/
+  for (i = 0; i < len; ++i) {
+    *p++ = file[i];
+    if (file[i] == ',')
+      *p++ = ',';
+  }
+  *p = '\0';
+
+  if (drv->iface)
+    iface = drv->iface;
+  else if (qemu_supports_virtio_scsi (g))
+    iface = "none"; /* sic */
+  else
+    iface = "virtio";
+
+  return safe_asprintf
+    (g, "file=%s%s%s%s%s%s%s,id=hd%zu,if=%s",
+     escaped_file,
+     drv->readonly ? ",snapshot=on" : "",
+     drv->use_cache_none ? ",cache=none" : "",
+     drv->format ? ",format=" : "",
+     drv->format ? drv->format : "",
+     drv->disk_label ? ",serial=" : "",
+     drv->disk_label ? drv->disk_label : "",
+     index,
+     iface);
+}
+
+static int
+shutdown_mingw (guestfs_h *g, int check_for_errors)
+{
+  int ret = 0;
+  int status;
+
+  /* Signal qemu to shutdown cleanly, and kill the recovery process. */
+  if (g->direct.pid > 0) {
+    debug (g, "sending SIGTERM to process %d", g->direct.pid);
+    kill (g->direct.pid, SIGTERM);
+  }
+  if (g->direct.recoverypid > 0) kill (g->direct.recoverypid, 9);
+
+  /* Wait for subprocess(es) to exit. */
+  if (g->recovery_proc /* RHBZ#998482 */ && g->direct.pid > 0) {
+    if (waitpid (g->direct.pid, &status, 0) == -1) {
+      perrorf (g, "waitpid (qemu)");
+      ret = -1;
+    }
+    else if (!WIFEXITED (status) || WEXITSTATUS (status) != SIGTERM) { // under mingw it exits with the code of the signal that terminated it
+      guestfs___external_command_failed (g, status, g->qemu, NULL);
+      ret = -1;
+    }
+  }
+  if (g->direct.recoverypid > 0) waitpid (g->direct.recoverypid, NULL, 0);
+
+  g->direct.pid = g->direct.recoverypid = 0;
+
+  free (g->direct.qemu_help);
+  g->direct.qemu_help = NULL;
+  free (g->direct.qemu_version);
+  g->direct.qemu_version = NULL;
+  free (g->direct.qemu_devices);
+  g->direct.qemu_devices = NULL;
+
+  return ret;
+}
+
+static int
+get_pid_mingw (guestfs_h *g)
+{
+  if (g->direct.pid > 0)
+    return g->direct.pid;
+  else {
+    error (g, "get_pid: no qemu subprocess");
+    return -1;
+  }
+}
+
+/* Maximum number of disks. */
+static int
+max_disks_mingw (guestfs_h *g)
+{
+  if (qemu_supports_virtio_scsi (g))
+    return 255;
+  else
+    return 27;                  /* conservative estimate */
+}
+
+struct backend_ops backend_ops_direct = {
+  .launch = launch_mingw,
+  .shutdown = shutdown_mingw,
+  .get_pid = get_pid_mingw,
+  .max_disks = max_disks_mingw,
+};
diff --git a/src/launch-unix.c b/src/launch-unix.c
index 489a046..e968e6b 100644
--- a/src/launch-unix.c
+++ b/src/launch-unix.c
@@ -23,7 +23,6 @@
 #include <unistd.h>
 #include <fcntl.h>
 #include <sys/socket.h>
-#include <sys/un.h>
 
 #include "guestfs.h"
 #include "guestfs-internal.h"
@@ -33,6 +32,15 @@
 /* Alternate backend: instead of launching the appliance,
  * connect to an existing unix socket.
  */
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+static int
+launch_unix (guestfs_h *g, const char *sockpath)
+{
+	return -1;
+}
+
+#else
+#include <sys/un.h>
 
 static int
 launch_unix (guestfs_h *g, void *datav, const char *sockpath)
diff --git a/src/launch.c b/src/launch.c
index 3851b6a..ee840ee 100644
--- a/src/launch.c
+++ b/src/launch.c
@@ -86,7 +86,9 @@ guestfs__launch (guestfs_h *g)
 
     debug (g, "launch: tmpdir=%s", g->tmpdir);
     debug (g, "launch: umask=0%03o", get_umask (g));
+#if (!defined _WIN32 && !defined __WIN32__) || defined __CYGWIN__
     debug (g, "launch: euid=%d", geteuid ());
+#endif
   }
 
   /* Launch the appliance. */
diff --git a/src/proto.c b/src/proto.c
index 8001c8c..4744b37 100644
--- a/src/proto.c
+++ b/src/proto.c
@@ -167,10 +167,9 @@ check_daemon_socket (guestfs_h *g)
 
   assert (g->conn); /* callers must check this */
 
- again:
+again:
   if (! g->conn->ops->can_read_data (g, g->conn))
     return 1;
-
   n = g->conn->ops->read_data (g, g->conn, buf, 4);
   if (n <= 0) /* 0 or -1 */
     return n;
@@ -183,7 +182,6 @@ check_daemon_socket (guestfs_h *g)
   if (flag == GUESTFS_PROGRESS_FLAG) {
     char buf[PROGRESS_MESSAGE_SIZE];
     guestfs_progress message;
-
     n = g->conn->ops->read_data (g, g->conn, buf, PROGRESS_MESSAGE_SIZE);
     if (n <= 0) /* 0 or -1 */
       return n;
@@ -328,7 +326,11 @@ guestfs___send_file (guestfs_h *g, const char *filename)
 
   g->user_cancel = 0;
 
+#if (!defined _WIN32 && !defined __WIN32__) || defined __CYGWIN__
   fd = open (filename, O_RDONLY|O_CLOEXEC);
+#else
+  fd = _open (filename, _O_RDONLY|_O_BINARY|_O_NOINHERIT|_O_SEQUENTIAL);
+#endif
   if (fd == -1) {
     perrorf (g, "open: %s", filename);
     send_file_cancellation (g);
@@ -339,7 +341,11 @@ guestfs___send_file (guestfs_h *g, const char *filename)
 
   /* Send file in chunked encoding. */
   while (!g->user_cancel) {
+#if (!defined _WIN32 && !defined __WIN32__) || defined __CYGWIN__
     r = read (fd, buf, sizeof buf);
+#else
+    r = _read (fd, buf, sizeof buf);
+#endif
     if (r == -1 && (errno == EINTR || errno == EAGAIN))
       continue;
     if (r <= 0) break;
@@ -370,7 +376,11 @@ guestfs___send_file (guestfs_h *g, const char *filename)
   /* End of file, but before we send that, we need to close
    * the file and check for errors.
    */
+#if (!defined _WIN32 && !defined __WIN32__) || defined __CYGWIN__
   if (close (fd) == -1) {
+#else
+  if (_close (fd) == -1) {
+#endif
     perrorf (g, "close: %s", filename);
     send_file_cancellation (g);
     return -1;
@@ -734,7 +744,11 @@ xwrite (int fd, const void *v_buf, size_t len)
   int r;
 
   while (len > 0) {
+#if (!defined _WIN32 && !defined __WIN32__) || defined __CYGWIN__
     r = write (fd, buf, len);
+#else
+    r = _write (fd, buf, len);
+#endif
     if (r == -1)
       return -1;
 
@@ -756,7 +770,11 @@ guestfs___recv_file (guestfs_h *g, const char *filename)
 
   g->user_cancel = 0;
 
+#if (!defined _WIN32 && !defined __WIN32__) || defined __CYGWIN__
   fd = open (filename, O_WRONLY|O_CREAT|O_TRUNC|O_NOCTTY|O_CLOEXEC, 0666);
+#else
+  fd = _open (filename, _O_WRONLY|_O_CREAT|_O_BINARY|_O_TRUNC|_O_NOINHERIT, 0666);
+#endif
   if (fd == -1) {
     perrorf (g, "open: %s", filename);
     goto cancel;
-- 
1.8.4.2



More information about the Libguestfs mailing list