Update Red Hat Copyright Notices
[nbdkit.git] / plugins / vddk / reexec.c
blob9439f013ebc97baeffc08471092e0706aebdc8c2
1 /* nbdkit
2 * Copyright Red Hat
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * * Neither the name of Red Hat nor the names of its contributors may be
16 * used to endorse or promote products derived from this software without
17 * specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
33 #include <config.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <stdbool.h>
38 #include <string.h>
39 #include <unistd.h>
40 #include <fcntl.h>
41 #include <assert.h>
42 #include <sys/types.h>
44 #define NBDKIT_API_VERSION 2
45 #include <nbdkit-plugin.h>
47 #include "cleanup.h"
48 #include "const-string-vector.h"
49 #include "nbdkit-string.h"
51 #include "vddk.h"
53 bool noreexec = false; /* hidden noreexec option */
54 char *reexeced; /* orig LD_LIBRARY_PATH on reexec */
56 /* Perform a re-exec that temporarily modifies LD_LIBRARY_PATH. Does
57 * not return on success. Some failures such as /proc/self/... not
58 * present are not errors - it means we are not on a Linux-like
59 * platform, VDDK probably doesn't work anyway, and we simply return.
60 * Memory allocation failures etc result in an exit.
62 static void
63 perform_reexec (const char *env, const char *prepend)
65 static const char cmdline_file[] = "/proc/self/cmdline";
66 static const char exe_file[] = "/proc/self/exe";
67 CLEANUP_FREE char *library = NULL;
68 CLEANUP_FREE_STRING string buf = empty_vector;
69 CLEANUP_FREE_CONST_STRING_VECTOR const_string_vector argv = empty_vector;
70 int fd;
71 size_t len;
72 bool seen_password = false;
73 char tmpfile[] = "/tmp/XXXXXX";
74 CLEANUP_FREE char *password_fd = NULL;
76 /* In order to re-exec, we need our original command line. The
77 * Linux kernel does not make it easy to know in advance how large
78 * it was, so we just slurp in the whole file, doubling our reads
79 * until we get a short read. This assumes nbdkit did not alter its
80 * original argv[].
82 fd = open (cmdline_file, O_RDONLY|O_CLOEXEC);
83 if (fd == -1) {
84 /* Not an error. */
85 nbdkit_debug ("open: %s: %m", cmdline_file);
86 return;
89 for (;;) {
90 ssize_t r;
92 if (string_reserve (&buf, 512) == -1) {
93 nbdkit_error ("realloc: %m");
94 exit (EXIT_FAILURE);
96 r = read (fd, buf.ptr + buf.len, buf.cap - buf.len);
97 if (r == -1) {
98 nbdkit_error ("read: %s: %m", cmdline_file);
99 exit (EXIT_FAILURE);
101 if (r == 0)
102 break;
103 buf.len += r;
105 close (fd);
106 nbdkit_debug ("original command line occupies %zu bytes", buf.len);
108 /* Split cmdline into argv, then append one more arg. */
109 for (len = 0; len < buf.len; len += strlen (buf.ptr + len) + 1) {
110 char *arg = buf.ptr + len; /* Next \0-terminated argument. */
112 /* See below for why we eat password parameter(s). */
113 if (strncmp (arg, "password=", 9) == 0)
114 seen_password = true;
115 else {
116 if (const_string_vector_append (&argv, arg) == -1) {
117 argv_realloc_fail:
118 nbdkit_error ("argv: realloc: %m");
119 exit (EXIT_FAILURE);
124 /* password parameter requires special handling for reexec. For
125 * password=- and password=-FD, after reexec we might try to
126 * reread these, but stdin has gone away and FD has been consumed
127 * already so that won't work. Even password=+FILE is a little
128 * problematic since the file will be read twice, which may break
129 * for special files.
131 * However we may write the password to a temporary file and
132 * substitute password=-<FD> of the opened temporary file here.
133 * The trick is described by Eric Blake here:
134 * https://www.redhat.com/archives/libguestfs/2020-June/msg00021.html
136 * (RHBZ#1842440)
138 if (seen_password && password) {
139 fd = mkstemp (tmpfile);
140 if (fd == -1) {
141 nbdkit_error ("mkstemp: %m");
142 exit (EXIT_FAILURE);
144 unlink (tmpfile);
145 if (write (fd, password, strlen (password)) != strlen (password)) {
146 nbdkit_error ("write: %m");
147 exit (EXIT_FAILURE);
149 lseek (fd, 0, SEEK_SET);
150 if (asprintf (&password_fd, "password=-%d", fd) == -1) {
151 nbdkit_error ("asprintf: %m");
152 exit (EXIT_FAILURE);
154 if (const_string_vector_append (&argv, password_fd) == -1)
155 goto argv_realloc_fail;
158 if (!env)
159 env = "";
160 nbdkit_debug ("adding reexeced_=%s", env);
161 if (asprintf (&reexeced, "reexeced_=%s", env) == -1)
162 goto argv_realloc_fail;
163 if (const_string_vector_append (&argv, reexeced) == -1)
164 goto argv_realloc_fail;
165 if (const_string_vector_append (&argv, NULL) == -1)
166 goto argv_realloc_fail;
168 if (env[0]) {
169 if (asprintf (&library, "%s:%s", prepend, env) == -1)
170 assert (library == NULL);
172 else
173 library = strdup (prepend);
174 if (!library || setenv ("LD_LIBRARY_PATH", library, 1) == -1) {
175 nbdkit_error ("failure to set LD_LIBRARY_PATH: %m");
176 exit (EXIT_FAILURE);
179 nbdkit_debug ("re-executing with updated LD_LIBRARY_PATH=%s", library);
180 fflush (NULL);
181 execvp (exe_file, (char **) argv.ptr);
182 nbdkit_debug ("execvp: %s: %m", exe_file);
183 /* Not an error. */
186 /* See if prepend is already in LD_LIBRARY_PATH; if not, re-exec. */
187 void
188 reexec_if_needed (const char *prepend)
190 const char *env = getenv ("LD_LIBRARY_PATH");
191 CLEANUP_FREE char *haystack = NULL;
192 CLEANUP_FREE char *needle = NULL;
194 if (noreexec)
195 return;
196 if (reexeced)
197 return;
198 if (env && asprintf (&haystack, ":%s:", env) >= 0 &&
199 asprintf (&needle, ":%s:", prepend) >= 0 &&
200 strstr (haystack, needle) != NULL)
201 return;
203 perform_reexec (env, prepend);
206 /* If load_library caused a re-execution with an expanded
207 * LD_LIBRARY_PATH, restore it back to its original contents, passed
208 * as the value of "reexeced_". dlopen uses the value of
209 * LD_LIBRARY_PATH cached at program startup; our change is for the
210 * sake of child processes (such as --run) to see the same
211 * environment as the original nbdkit saw before re-exec.
214 restore_ld_library_path (void)
216 if (reexeced) {
217 char *env = getenv ("LD_LIBRARY_PATH");
219 nbdkit_debug ("cleaning up after re-exec");
220 if (!env || strstr (env, reexeced) == NULL ||
221 (libdir && strncmp (env, libdir, strlen (libdir)) != 0)) {
222 nbdkit_error ("'reexeced_' set with garbled environment");
223 return -1;
225 if (reexeced[0]) {
226 if (setenv ("LD_LIBRARY_PATH", reexeced, 1) == -1) {
227 nbdkit_error ("setenv: %m");
228 return -1;
231 else if (unsetenv ("LD_LIBRARY_PATH") == -1) {
232 nbdkit_error ("unsetenv: %m");
233 return -1;
237 return 0;