[Libguestfs] [PATCH nbdkit 2/5] vddk: Move reexec code to a new file.

Richard W.M. Jones
2020-06-02T12:27:03Z

Pure refactoring.  Just decouples the complicated reexec code from the
rest.
---
 plugins/vddk/Makefile.am |   2 +
 plugins/vddk/vddk.h      |  42 +++++++++
 plugins/vddk/reexec.c    | 196 +++++++++++++++++++++++++++++++++++++++
 plugins/vddk/vddk.c      | 151 ++----------------------------
 4 files changed, 246 insertions(+), 145 deletions(-)

diff --git a/plugins/vddk/Makefile.am b/plugins/vddk/Makefile.am
index 740c42d6..d4b123e9 100644
--- a/plugins/vddk/Makefile.am
+++ b/plugins/vddk/Makefile.am
@@ -42,6 +42,8 @@ plugin_LTLIBRARIES = nbdkit-vddk-plugin.la

 nbdkit_vddk_plugin_la_SOURCES = \
 	vddk.c \
+	vddk.h \
+	reexec.c \
 	vddk-structs.h \
 	vddk-stubs.h \
 	$(top_srcdir)/include/nbdkit-plugin.h \
diff --git a/plugins/vddk/vddk.h b/plugins/vddk/vddk.h
new file mode 100644
index 00000000..e229e55e
--- /dev/null
+++ b/plugins/vddk/vddk.h
@@ -0,0 +1,42 @@
+/* nbdkit
+ * Copyright (C) 2013-2020 Red Hat Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of Red Hat nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS
IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef NBDKIT_VDDK_H
+#define NBDKIT_VDDK_H
+
+extern char *libdir;
+extern char *reexeced;
+
+extern void reexec_if_needed (const char *prepend);
+extern int restore_ld_library_path (void);
+
+#endif /* NBDKIT_VDDK_H */
diff --git a/plugins/vddk/reexec.c b/plugins/vddk/reexec.c
new file mode 100644
index 00000000..5a5e9844
--- /dev/null
+++ b/plugins/vddk/reexec.c
@@ -0,0 +1,196 @@
+/* nbdkit
+ * Copyright (C) 2013-2020 Red Hat Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of Red Hat nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS
IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#define NBDKIT_API_VERSION 2
+#include <nbdkit-plugin.h>
+
+#include "cleanup.h"
+#include "vector.h"
+
+#include "vddk.h"
+
+char *reexeced;                 /* orig LD_LIBRARY_PATH on reexec */
+
+DEFINE_VECTOR_TYPE(string_vector, char *);
+
+#define CLEANUP_FREE_STRING_VECTOR \
+  __attribute__((cleanup (cleanup_free_string_vector)))
+
+static void
+cleanup_free_string_vector (string_vector *v)
+{
+  string_vector_iter (v, (void *) free);
+  free (v->ptr);
+}
+
+/* Perform a re-exec that temporarily modifies LD_LIBRARY_PATH.  Does
+ * not return on success; on failure, problems have been logged, but
+ * the caller prefers to proceed as if this had not been attempted.
+ * Thus, no return value is needed.
+ */
+static void
+perform_reexec (const char *env, const char *prepend)
+{
+  CLEANUP_FREE char *library = NULL;
+  CLEANUP_FREE_STRING_VECTOR string_vector argv = empty_vector;
+  int fd;
+  size_t len = 0, buflen = 512;
+  CLEANUP_FREE char *buf = NULL;
+
+  /* In order to re-exec, we need our original command line.  The
+   * Linux kernel does not make it easy to know in advance how large
+   * it was, so we just slurp in the whole file, doubling our reads
+   * until we get a short read.  This assumes nbdkit did not alter its
+   * original argv[].
+   */
+  fd = open ("/proc/self/cmdline", O_RDONLY);
+  if (fd == -1) {
+    nbdkit_debug ("failure to parse original argv: %m");
+    return;
+  }
+
+  do {
+    char *p = realloc (buf, buflen * 2);
+    ssize_t r;
+
+    if (!p) {
+      nbdkit_debug ("failure to parse original argv: %m");
+      return;
+    }
+    buf = p;
+    buflen *= 2;
+    r = read (fd, buf + len, buflen - len);
+    if (r == -1) {
+      nbdkit_debug ("failure to parse original argv: %m");
+      return;
+    }
+    len += r;
+  } while (len == buflen);
+  nbdkit_debug ("original command line occupies %zu bytes", len);
+
+  /* Split cmdline into argv, then append one more arg. */
+  buflen = len;
+  len = 0;
+  while (len < buflen) {
+    if (string_vector_append (&argv, buf + len) == -1) {
+    argv_realloc_fail:
+      nbdkit_debug ("argv: realloc: %m");
+      return;
+    }
+    len += strlen (buf + len) + 1;
+  }
+  if (!env)
+    env = "";
+  nbdkit_debug ("adding reexeced_=%s", env);
+  if (asprintf (&reexeced, "reexeced_=%s", env) == -1)
+    goto argv_realloc_fail;
+  if (string_vector_append (&argv, reexeced) == -1)
+    goto argv_realloc_fail;
+  if (string_vector_append (&argv, NULL) == -1)
+    goto argv_realloc_fail;
+
+  if (env[0]) {
+    if (asprintf (&library, "%s:%s", prepend, env) == -1)
+      assert (library == NULL);
+  }
+  else
+    library = strdup (prepend);
+  if (!library || setenv ("LD_LIBRARY_PATH", library, 1) == -1) {
+    nbdkit_debug ("failure to set LD_LIBRARY_PATH: %m");
+    return;
+  }
+
+  nbdkit_debug ("re-executing with updated LD_LIBRARY_PATH=%s",
library);
+  fflush (NULL);
+  execvp ("/proc/self/exe", argv.ptr);
+  nbdkit_debug ("failure to execvp: %m");
+}
+
+/* See if prepend is already in LD_LIBRARY_PATH; if not, re-exec. */
+void
+reexec_if_needed (const char *prepend)
+{
+  const char *env = getenv ("LD_LIBRARY_PATH");
+  CLEANUP_FREE char *haystack = NULL;
+  CLEANUP_FREE char *needle = NULL;
+
+  if (reexeced)
+    return;
+  if (env && asprintf (&haystack, ":%s:", env) >= 0
&&
+      asprintf (&needle, ":%s:", prepend) >= 0 &&
+      strstr (haystack, needle) != NULL)
+    return;
+
+  perform_reexec (env, prepend);
+}
+
+/* If load_library caused a re-execution with an expanded
+ * LD_LIBRARY_PATH, restore it back to its original contents, passed
+ * as the value of "reexeced_".  dlopen uses the value of
+ * LD_LIBRARY_PATH cached at program startup; our change is for the
+ * sake of child processes (such as --run) to see the same
+ * environment as the original nbdkit saw before re-exec.
+ */
+int
+restore_ld_library_path (void)
+{
+  if (reexeced) {
+    char *env = getenv ("LD_LIBRARY_PATH");
+
+    nbdkit_debug ("cleaning up after re-exec");
+    if (!env || strstr (env, reexeced) == NULL ||
+        (libdir && strncmp (env, libdir, strlen (libdir)) != 0)) {
+      nbdkit_error ("'reexeced_' set with garbled
environment");
+      return -1;
+    }
+    if (reexeced[0]) {
+      if (setenv ("LD_LIBRARY_PATH", reexeced, 1) == -1) {
+        nbdkit_error ("setenv: %m");
+        return -1;
+      }
+    }
+    else if (unsetenv ("LD_LIBRARY_PATH") == -1) {
+      nbdkit_error ("unsetenv: %m");
+      return -1;
+    }
+  }
+
+  return 0;
+}
diff --git a/plugins/vddk/vddk.c b/plugins/vddk/vddk.c
index 4815a43e..eb865cc2 100644
--- a/plugins/vddk/vddk.c
+++ b/plugins/vddk/vddk.c
@@ -40,7 +40,6 @@
 #include <string.h>
 #include <unistd.h>
 #include <dlfcn.h>
-#include <fcntl.h>
 #include <libgen.h>

 #include <pthread.h>
@@ -52,8 +51,8 @@
 #include "isaligned.h"
 #include "minmax.h"
 #include "rounding.h"
-#include "vector.h"

+#include "vddk.h"
 #include "vddk-structs.h"

 /* Debug flags. */
@@ -76,13 +75,12 @@ int vddk_debug_datapath = 1;

 static void *dl;                           /* dlopen handle */
 static bool init_called;                   /* was InitEx called */
-static char *reexeced;                     /* orig LD_LIBRARY_PATH on reexec */
 static pthread_key_t error_suppression;    /* threadlocal error suppression */

 static char *config;                       /* config */
 static const char *cookie;                 /* cookie */
 static const char *filename;               /* file */
-static char *libdir;                       /* libdir */
+char *libdir;                              /* libdir */
 static uint16_t nfc_host_port;             /* nfchostport */
 static char *password;                     /* password */
 static uint16_t port;                      /* port */
@@ -293,119 +291,6 @@ vddk_config (const char *key, const char *value)
   return 0;
 }

-/* Perform a re-exec that temporarily modifies LD_LIBRARY_PATH.  Does
- * not return on success; on failure, problems have been logged, but
- * the caller prefers to proceed as if this had not been attempted.
- * Thus, no return value is needed.
- */
-DEFINE_VECTOR_TYPE(string_vector, char *);
-
-#define CLEANUP_FREE_STRING_VECTOR \
-  __attribute__((cleanup (cleanup_free_string_vector)))
-
-static void
-cleanup_free_string_vector (string_vector *v)
-{
-  string_vector_iter (v, (void *) free);
-  free (v->ptr);
-}
-
-static void
-perform_reexec (const char *env, const char *prepend)
-{
-  CLEANUP_FREE char *library = NULL;
-  CLEANUP_FREE_STRING_VECTOR string_vector argv = empty_vector;
-  int fd;
-  size_t len = 0, buflen = 512;
-  CLEANUP_FREE char *buf = NULL;
-
-  /* In order to re-exec, we need our original command line.  The
-   * Linux kernel does not make it easy to know in advance how large
-   * it was, so we just slurp in the whole file, doubling our reads
-   * until we get a short read.  This assumes nbdkit did not alter its
-   * original argv[].
-   */
-  fd = open ("/proc/self/cmdline", O_RDONLY);
-  if (fd == -1) {
-    nbdkit_debug ("failure to parse original argv: %m");
-    return;
-  }
-
-  do {
-    char *p = realloc (buf, buflen * 2);
-    ssize_t r;
-
-    if (!p) {
-      nbdkit_debug ("failure to parse original argv: %m");
-      return;
-    }
-    buf = p;
-    buflen *= 2;
-    r = read (fd, buf + len, buflen - len);
-    if (r == -1) {
-      nbdkit_debug ("failure to parse original argv: %m");
-      return;
-    }
-    len += r;
-  } while (len == buflen);
-  nbdkit_debug ("original command line occupies %zu bytes", len);
-
-  /* Split cmdline into argv, then append one more arg. */
-  buflen = len;
-  len = 0;
-  while (len < buflen) {
-    if (string_vector_append (&argv, buf + len) == -1) {
-    argv_realloc_fail:
-      nbdkit_debug ("argv: realloc: %m");
-      return;
-    }
-    len += strlen (buf + len) + 1;
-  }
-  if (!env)
-    env = "";
-  nbdkit_debug ("adding reexeced_=%s", env);
-  if (asprintf (&reexeced, "reexeced_=%s", env) == -1)
-    goto argv_realloc_fail;
-  if (string_vector_append (&argv, reexeced) == -1)
-    goto argv_realloc_fail;
-  if (string_vector_append (&argv, NULL) == -1)
-    goto argv_realloc_fail;
-
-  if (env[0]) {
-    if (asprintf (&library, "%s:%s", prepend, env) == -1)
-      assert (library == NULL);
-  }
-  else
-    library = strdup (prepend);
-  if (!library || setenv ("LD_LIBRARY_PATH", library, 1) == -1) {
-    nbdkit_debug ("failure to set LD_LIBRARY_PATH: %m");
-    return;
-  }
-
-  nbdkit_debug ("re-executing with updated LD_LIBRARY_PATH=%s",
library);
-  fflush (NULL);
-  execvp ("/proc/self/exe", argv.ptr);
-  nbdkit_debug ("failure to execvp: %m");
-}
-
-/* See if prepend is already in LD_LIBRARY_PATH; if not, re-exec. */
-static void
-check_reexec (const char *prepend)
-{
-  const char *env = getenv ("LD_LIBRARY_PATH");
-  CLEANUP_FREE char *haystack = NULL;
-  CLEANUP_FREE char *needle = NULL;
-
-  if (reexeced)
-    return;
-  if (env && asprintf (&haystack, ":%s:", env) >= 0
&&
-      asprintf (&needle, ":%s:", prepend) >= 0 &&
-      strstr (haystack, needle) != NULL)
-    return;
-
-  perform_reexec (env, prepend);
-}
-
 /* Load the VDDK library. */
 static void
 load_library (bool load_error_is_fatal)
@@ -453,7 +338,7 @@ load_library (bool load_error_is_fatal)
        * includes its directory for all future loads.  This may modify
        * path in-place and/or re-exec nbdkit, but that's okay.
        */
-      check_reexec (dirname (path));
+      reexec_if_needed (dirname (path));
       break;
     }
     if (i == 0) {
@@ -498,33 +383,9 @@ vddk_config_complete (void)
     return -1;
   }

-  /* If load_library caused a re-execution with an expanded
-   * LD_LIBRARY_PATH, restore it back to its original contents, passed
-   * as the value of "reexeced_".  dlopen uses the value of
-   * LD_LIBRARY_PATH cached at program startup; our change is for the
-   * sake of child processes (such as --run) to see the same
-   * environment as the original nbdkit saw before re-exec.
-   */
-  if (reexeced) {
-    char *env = getenv ("LD_LIBRARY_PATH");
-
-    nbdkit_debug ("cleaning up after re-exec");
-    if (!env || strstr (env, reexeced) == NULL ||
-        (libdir && strncmp (env, libdir, strlen (libdir)) != 0)) {
-      nbdkit_error ("'reexeced_' set with garbled
environment");
-      return -1;
-    }
-    if (reexeced[0]) {
-      if (setenv ("LD_LIBRARY_PATH", reexeced, 1) == -1) {
-        nbdkit_error ("setenv: %m");
-        return -1;
-      }
-    }
-    else if (unsetenv ("LD_LIBRARY_PATH") == -1) {
-      nbdkit_error ("unsetenv: %m");
-      return -1;
-    }
-  }
+  /* Restore original LD_LIBRARY_PATH after reexec. */
+  if (restore_ld_library_path () == -1)
+    return -1;

   /* For remote connections, check all the parameters have been
    * passed.  Note that VDDK will segfault if parameters that it
--
2.25.0