4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
42 #include <sys/types.h>
44 #define NBDKIT_API_VERSION 2
45 #include <nbdkit-plugin.h>
48 #include "const-string-vector.h"
49 #include "nbdkit-string.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.
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
;
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
82 fd
= open (cmdline_file
, O_RDONLY
|O_CLOEXEC
);
85 nbdkit_debug ("open: %s: %m", cmdline_file
);
92 if (string_reserve (&buf
, 512) == -1) {
93 nbdkit_error ("realloc: %m");
96 r
= read (fd
, buf
.ptr
+ buf
.len
, buf
.cap
- buf
.len
);
98 nbdkit_error ("read: %s: %m", cmdline_file
);
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;
116 if (const_string_vector_append (&argv
, arg
) == -1) {
118 nbdkit_error ("argv: realloc: %m");
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
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
138 if (seen_password
&& password
) {
139 fd
= mkstemp (tmpfile
);
141 nbdkit_error ("mkstemp: %m");
145 if (write (fd
, password
, strlen (password
)) != strlen (password
)) {
146 nbdkit_error ("write: %m");
149 lseek (fd
, 0, SEEK_SET
);
150 if (asprintf (&password_fd
, "password=-%d", fd
) == -1) {
151 nbdkit_error ("asprintf: %m");
154 if (const_string_vector_append (&argv
, password_fd
) == -1)
155 goto argv_realloc_fail
;
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
;
169 if (asprintf (&library
, "%s:%s", prepend
, env
) == -1)
170 assert (library
== NULL
);
173 library
= strdup (prepend
);
174 if (!library
|| setenv ("LD_LIBRARY_PATH", library
, 1) == -1) {
175 nbdkit_error ("failure to set LD_LIBRARY_PATH: %m");
179 nbdkit_debug ("re-executing with updated LD_LIBRARY_PATH=%s", library
);
181 execvp (exe_file
, (char **) argv
.ptr
);
182 nbdkit_debug ("execvp: %s: %m", exe_file
);
186 /* See if prepend is already in LD_LIBRARY_PATH; if not, re-exec. */
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
;
198 if (env
&& asprintf (&haystack
, ":%s:", env
) >= 0 &&
199 asprintf (&needle
, ":%s:", prepend
) >= 0 &&
200 strstr (haystack
, needle
) != NULL
)
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)
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");
226 if (setenv ("LD_LIBRARY_PATH", reexeced
, 1) == -1) {
227 nbdkit_error ("setenv: %m");
231 else if (unsetenv ("LD_LIBRARY_PATH") == -1) {
232 nbdkit_error ("unsetenv: %m");