[Libguestfs] [PATCH nbdkit 12/13] wrapper: Port the wrapper to run on Windows.

Richard W.M. Jones rjones at redhat.com
Thu Aug 20 11:37:45 UTC 2020


This also defines SOEXT as the extension of shared objects (ie. "so"
or "dll"), and uses it everywhere.  I assumed this must already be
defined by either autoconf or mingw (like OBJEXT) but I cannot find
anything except in glib (G_MODULE_SUFFIX).

Thanks: Zebediah Figura for helping out with exec vs spawn on Windows.
---
 configure.ac                  |  8 +++-
 common/utils/windows-compat.h |  3 ++
 server/fuzzer.c               |  2 +-
 server/main.c                 |  6 +--
 wrapper.c                     | 72 ++++++++++++++++++++++++++++++++---
 README                        | 18 ++++-----
 6 files changed, 87 insertions(+), 22 deletions(-)

diff --git a/configure.ac b/configure.ac
index 0b17ef95..61ee3a02 100644
--- a/configure.ac
+++ b/configure.ac
@@ -489,12 +489,18 @@ AS_CASE([$host_os],
         LIBS="$LIBS -lmsvcrt -lkernel32 -luser32"
         NO_UNDEFINED_ON_WINDOWS="-no-undefined"
         IMPORT_LIBRARY_ON_WINDOWS='-Wl,-L$(top_builddir)/server -Wl,-lnbdkit'
+        SOEXT="dll"
     ],
-    [is_windows=no]
+    [is_windows=no], [
+        SOEXT="so"
+    ]
 )
 AC_MSG_RESULT([$is_windows])
 AC_SUBST([NO_UNDEFINED_ON_WINDOWS])
 AC_SUBST([IMPORT_LIBRARY_ON_WINDOWS])
+AC_SUBST([SOEXT])
+AC_DEFINE_UNQUOTED([SOEXT],["$SOEXT"],[Extension used for shared objects/DLLs.])
+AC_DEFINE_UNQUOTED([EXEEXT],["$EXEEXT"],[Extension used for executables.])
 AM_CONDITIONAL([IS_WINDOWS],[test "x$is_windows" = "xyes"])
 
 AS_IF([test "x$is_windows" = "xyes"],[
diff --git a/common/utils/windows-compat.h b/common/utils/windows-compat.h
index 74241a19..0fcb20d2 100644
--- a/common/utils/windows-compat.h
+++ b/common/utils/windows-compat.h
@@ -103,6 +103,9 @@ extern int win_send (int fd, const void *buf, size_t len, int flags);
 #define dup _dup
 #define dup2 _dup2
 
+/* setenv replacement. */
+#define setenv(k, v, replace) _putenv_s ((k), (v));
+
 /* Unfortunately quite commonly used at the moment.  Make it a common
  * macro so we can easily find places which need porting.
  *
diff --git a/server/fuzzer.c b/server/fuzzer.c
index c28a1798..4bbb0061 100644
--- a/server/fuzzer.c
+++ b/server/fuzzer.c
@@ -119,7 +119,7 @@ server (int sock)
     "nbdkit",
     "-s",         /* take input from stdin/stdout */
     "--log=null", /* discard error messages */
-    "plugins/memory/.libs/nbdkit-memory-plugin.so", "1M",
+    "plugins/memory/.libs/nbdkit-memory-plugin." SOEXT, "1M",
     NULL
   };
   const int argc = sizeof argv / sizeof argv[0] - 1;
diff --git a/server/main.c b/server/main.c
index fa5073d6..4b6f72e1 100644
--- a/server/main.c
+++ b/server/main.c
@@ -497,7 +497,7 @@ main (int argc, char *argv[])
       /* Incorrect use of --dump-plugin. */
       fprintf (stderr,
                "%s: use 'nbdkit plugin --dump-plugin' or\n"
-               "'nbdkit /path/to/plugin.so --dump-plugin'\n",
+               "'nbdkit /path/to/plugin." SOEXT " --dump-plugin'\n",
                program_name);
       exit (EXIT_FAILURE);
     }
@@ -819,7 +819,7 @@ open_plugin_so (size_t i, const char *name, int short_name)
   if (short_name) {
     /* Short names are rewritten relative to the plugindir. */
     if (asprintf (&filename,
-                  "%s/nbdkit-%s-plugin.so", plugindir, name) == -1) {
+                  "%s/nbdkit-%s-plugin." SOEXT, plugindir, name) == -1) {
       perror ("asprintf");
       exit (EXIT_FAILURE);
     }
@@ -872,7 +872,7 @@ open_filter_so (struct backend *next, size_t i,
   if (short_name) {
     /* Short names are rewritten relative to the filterdir. */
     if (asprintf (&filename,
-                  "%s/nbdkit-%s-filter.so", filterdir, name) == -1) {
+                  "%s/nbdkit-%s-filter." SOEXT, filterdir, name) == -1) {
       perror ("asprintf");
       exit (EXIT_FAILURE);
     }
diff --git a/wrapper.c b/wrapper.c
index c27afae0..fe750936 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -72,6 +72,7 @@
 #include <time.h>
 
 #include "options.h"
+#include "windows-compat.h"
 #include "utils.h"
 
 /* Construct an array of parameters passed through to real nbdkit. */
@@ -131,6 +132,45 @@ print_command (void)
   fputc ('\n', stderr);
 }
 
+#ifdef WIN32
+/* Windows behaviour of _spawnvp is completely retarded:
+ * https://stackoverflow.com/questions/4146980/how-to-avoid-space-splitting-and-quote-removal-with-spawnvp
+ */
+static const char *
+quote_string_for_spawn (const char *str)
+{
+  size_t i, len;
+  char *p, *ret = (char *) str;
+
+  if (strchr (str, ' ') || strchr (str, '"')) {
+    len = strlen (str);
+
+    p = ret = malloc (2 + len*2 + 1);
+    if (ret == NULL) {
+      perror ("malloc");
+      exit (EXIT_FAILURE);
+    }
+
+    *p++ = '"';
+    for (i = 0; i < len; ++i) {
+      switch (str[i]) {
+      case '"':
+        *p++ = '\\';
+        *p++ = '"';
+        break;
+      default:
+        *p++ = str[i];
+      }
+    }
+    *p++ = '"';
+    *p++ = '\0';
+  }
+
+  /* We never free these strings. */
+  return ret;
+}
+#endif /* WIN32 */
+
 int
 main (int argc, char *argv[])
 {
@@ -141,6 +181,7 @@ main (int argc, char *argv[])
   char ts[32];
   int r;
 
+#ifndef WIN32
   /* If NBDKIT_VALGRIND=1 is set in the environment, then we run the
    * program under valgrind.  This is used by the tests.  Similarly if
    * NBDKIT_GDB=1 is set, we run the program under GDB, useful during
@@ -167,9 +208,15 @@ main (int argc, char *argv[])
       passthru ("--args");
     }
   }
+#endif
 
   /* Needed for plugins written in OCaml. */
-  s = getenv ("LD_LIBRARY_PATH");
+#ifndef WIN32
+#define LD_LIBRARY_PATH "LD_LIBRARY_PATH"
+#else
+#define LD_LIBRARY_PATH "PATH"
+#endif
+  s = getenv (LD_LIBRARY_PATH);
   if (s)
     r = asprintf (&s, "%s/plugins/ocaml/.libs:%s", builddir, s);
   else
@@ -178,7 +225,7 @@ main (int argc, char *argv[])
     perror ("asprintf");
     exit (EXIT_FAILURE);
   }
-  setenv ("LD_LIBRARY_PATH", s, 1);
+  setenv (LD_LIBRARY_PATH, s, 1);
   free (s);
   s = getenv ("LIBRARY_PATH");
   if (s)
@@ -193,7 +240,7 @@ main (int argc, char *argv[])
   free (s);
 
   /* Absolute path of the real nbdkit command. */
-  passthru_format ("%s/server/nbdkit", builddir);
+  passthru_format ("%s/server/nbdkit" EXEEXT, builddir);
 
   /* Option parsing.  We don't really parse options here.  We are only
    * interested in which options have arguments and which need
@@ -225,7 +272,8 @@ main (int argc, char *argv[])
     /* Filters can be rewritten if they are a short name. */
     else if (c == FILTER_OPTION) {
       if (is_short_name (optarg))
-        passthru_format ("--filter=%s/filters/%s/.libs/nbdkit-%s-filter.so",
+        passthru_format ("--filter="
+                         "%s/filters/%s/.libs/nbdkit-%s-filter." SOEXT,
                          builddir, optarg, optarg);
       else
         passthru_format ("--filter=%s", optarg);
@@ -258,13 +306,13 @@ main (int argc, char *argv[])
     if (is_short_name (argv[optind])) {
       /* Special plugins written in Perl. */
       if (is_perl_plugin (argv[optind])) {
-        passthru_format ("%s/plugins/perl/.libs/nbdkit-perl-plugin.so",
+        passthru_format ("%s/plugins/perl/.libs/nbdkit-perl-plugin." SOEXT,
                          builddir);
         passthru_format ("%s/plugins/%s/nbdkit-%s-plugin",
                          builddir, argv[optind], argv[optind]);
       }
       else {
-        passthru_format ("%s/plugins/%s/.libs/nbdkit-%s-plugin.so",
+        passthru_format ("%s/plugins/%s/.libs/nbdkit-%s-plugin." SOEXT,
                          builddir, argv[optind], argv[optind]);
       }
       ++optind;
@@ -295,7 +343,19 @@ main (int argc, char *argv[])
   setenv ("MALLOC_PERTURB_", ts, 0);
 
   /* Run the final command. */
+#ifndef WIN32
   execvp (cmd[0], (char **) cmd);
   perror (cmd[0]);
   exit (EXIT_FAILURE);
+#else /* WIN32 */
+  size_t i;
+  for (i = 1; cmd[i] != NULL; ++i)
+    cmd[i] = quote_string_for_spawn (cmd[i]);
+  r = _spawnvp (_P_WAIT, cmd[0], cmd);
+  if (r == -1) {
+    perror (cmd[0]);
+    exit (EXIT_FAILURE);
+  }
+  exit (r == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
+#endif /* WIN32 */
 }
diff --git a/README b/README
index 2bf8ce5f..56b0c984 100644
--- a/README
+++ b/README
@@ -331,21 +331,17 @@ To cross compile do:
     mingw64-configure --disable-ocaml --disable-perl --disable-vddk
     mingw64-make
 
-It is expected to fail, but check that it gets as far as building
-server/nbdkit.exe.  You can test if the server is working by doing:
+You can test if the server is working by doing:
 
-    wine server/nbdkit.exe --dump-config
+    ./nbdkit.exe --dump-config
 
-Now try to build plugins and filters (many will not compile):
+(This usually runs wine automatically.  If not, you may need to prefix
+the command "wine ./nbdkit.exe ...")
 
-    mingw64-make -k
+To see which plugins and filters were compiled:
 
-To see which ones were compiled:
-
-    find -name '*.dll'
+    find plugins filters -name '*.dll'
 
 You can run them under Wine without installing using eg:
 
-    wine server/nbdkit.exe -f -v \
-                           plugins/memory/.libs/nbdkit-memory-plugin.dll \
-                           size=1G
+    ./nbdkit.exe -f -v memory 1G
-- 
2.27.0




More information about the Libguestfs mailing list