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