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
45 #include <sys/types.h>
48 #include <nbdkit-plugin.h>
50 #include "ascii-ctype.h"
51 #include "ascii-string.h"
58 #ifndef HAVE_ENVIRON_DECL
59 extern char **environ
;
62 /* Temporary directory for scripts to use. */
63 char tmpdir
[] = "/tmp/nbdkitXXXXXX";
65 /* Private copy of environ, with $tmpdir added. */
71 /* Create the temporary directory for the shell script to use. */
72 if (mkdtemp (tmpdir
) == NULL
) {
73 nbdkit_error ("mkdtemp: /tmp: %m");
77 nbdkit_debug ("load: tmpdir: %s", tmpdir
);
79 /* Copy the environment, and add $tmpdir. */
80 env
= copy_environ (environ
, "tmpdir", tmpdir
, NULL
);
85 #pragma GCC diagnostic push
86 #pragma GCC diagnostic ignored "-Wunused-result"
90 CLEANUP_FREE
char *cmd
= NULL
;
93 /* Delete the temporary directory. Ignore all errors. */
94 if (asprintf (&cmd
, "rm -rf %s", tmpdir
) >= 0)
97 /* Free the private copy of environ. */
98 for (i
= 0; env
[i
] != NULL
; ++i
)
102 #pragma GCC diagnostic pop
105 debug_call (const char **argv
)
107 CLEANUP_FREE
char *debug
= NULL
;
111 fp
= open_memstream (&debug
, &len
);
115 fprintf (fp
, "calling:");
116 for (i
= 0; argv
[i
] != NULL
; ++i
) {
118 shell_quote (argv
[i
], fp
);
123 nbdkit_debug ("%s", debug
);
126 /* This is the generic function that calls the script. It can
127 * optionally write to the script's stdin and read from the script's
128 * stdout and stderr. It returns the raw error code and does no error
132 call3 (const char *wbuf
, size_t wbuflen
, /* sent to stdin (can be NULL) */
133 string
*rbuf
, /* read from stdout */
134 string
*ebuf
, /* read from stderr */
135 const char **argv
) /* script + parameters */
137 const char *argv0
= argv
[0]; /* script name, used in error messages */
139 CLEANUP_FREE
const char **sh_argv
= NULL
;
145 int in_fd
[2] = { -1, -1 };
146 int out_fd
[2] = { -1, -1 };
147 int err_fd
[2] = { -1, -1 };
148 struct pollfd pfds
[3];
151 /* Ignore any previous contents of rbuf, ebuf. */
158 if (pipe2 (in_fd
, O_CLOEXEC
) == -1) {
159 nbdkit_error ("%s: pipe2: %m", argv0
);
162 if (pipe2 (out_fd
, O_CLOEXEC
) == -1) {
163 nbdkit_error ("%s: pipe2: %m", argv0
);
166 if (pipe2 (err_fd
, O_CLOEXEC
) == -1) {
167 nbdkit_error ("%s: pipe2: %m", argv0
);
171 /* Without pipe2, nbdkit forces the thread model maximum down to
172 * NBDKIT_THREAD_MODEL_SERIALIZE_ALL_REQUESTS, this in turn ensures
173 * no other thread will be trying to fork, and thus we can skip
174 * worrying about CLOEXEC races. Therefore, it's not worth adding a
175 * loop after fork to close unexpected fds.
177 if (pipe (in_fd
) == -1) {
178 nbdkit_error ("%s: pipe: %m", argv0
);
181 if (pipe (out_fd
) == -1) {
182 nbdkit_error ("%s: pipe: %m", argv0
);
185 if (pipe (err_fd
) == -1) {
186 nbdkit_error ("%s: pipe: %m", argv0
);
191 /* Ensure that stdin/out/err of the current process were not empty
192 * before we started creating pipes (otherwise, the close and dup2
193 * calls below become more complex to juggle fds around correctly).
195 assert (in_fd
[0] > STDERR_FILENO
&& in_fd
[1] > STDERR_FILENO
&&
196 out_fd
[0] > STDERR_FILENO
&& out_fd
[1] > STDERR_FILENO
&&
197 err_fd
[0] > STDERR_FILENO
&& err_fd
[1] > STDERR_FILENO
);
200 /* glibc contains a workaround for scripts which don't have a
201 * shebang. See maybe_script_execute in glibc posix/execvpe.c.
202 * We rely on this in nbdkit, so if not using glibc we emulate it.
203 * Note this is tested when we do CI on Alpine (which uses musl).
205 /* Count the number of arguments, ignoring script name. */
206 for (i
= 2; argv
[i
]; i
++)
208 sh_argv
= calloc (i
+ 2 /* /bin/sh + NULL */, sizeof (const char *));
209 if (sh_argv
== NULL
) {
210 nbdkit_error ("%s: calloc: %m", argv0
);
213 sh_argv
[0] = "/bin/sh";
214 for (i
= 0; argv
[i
]; i
++)
215 sh_argv
[i
+1] = argv
[i
];
220 nbdkit_error ("%s: fork: %m", argv0
);
224 if (pid
== 0) { /* Child. */
235 /* Restore SIGPIPE back to SIG_DFL, since shell can't undo SIG_IGN */
236 signal (SIGPIPE
, SIG_DFL
);
238 /* Note the assignment of environ avoids using execvpe which is a
239 * GNU extension. See also:
240 * https://github.com/libguestfs/libnbd/commit/dc64ac5cdd0bc80ca4e18935ad0e8801d11a8644
243 execvp (argv
[0], (char **) argv
);
245 /* Non-glibc workaround for missing shebang - see above. */
246 if (errno
== ENOEXEC
)
247 execvp (sh_argv
[0], (char **) sh_argv
);
250 _exit (EXIT_FAILURE
);
254 close (in_fd
[0]); in_fd
[0] = -1;
255 close (out_fd
[1]); out_fd
[1] = -1;
256 close (err_fd
[1]); err_fd
[1] = -1;
258 while (out_fd
[0] >= 0 || err_fd
[0] >= 0) {
259 pfds
[0].fd
= in_fd
[1]; /* Connected to child stdin. */
260 pfds
[0].events
= wbuflen
? POLLOUT
: 0;
261 pfds
[1].fd
= out_fd
[0]; /* Connected to child stdout. */
262 pfds
[1].events
= POLLIN
;
263 pfds
[2].fd
= err_fd
[0]; /* Connected to child stderr. */
264 pfds
[2].events
= POLLIN
;
266 if (poll (pfds
, 3, -1) == -1) {
267 if (errno
== EINTR
|| errno
== EAGAIN
)
269 nbdkit_error ("%s: poll: %m", argv0
);
273 /* Write more data to stdin. */
274 if (pfds
[0].revents
& POLLOUT
) {
275 r
= write (pfds
[0].fd
, wbuf
, wbuflen
);
277 if (errno
== EPIPE
) {
278 /* We tried to write to the script but it didn't consume
279 * the data. Probably the script exited without reading
280 * from stdin. This is an error in the script.
282 nbdkit_error ("%s: write to script failed because of a broken pipe: "
283 "this can happen if the script exits without "
284 "consuming stdin, which usually indicates a bug "
289 nbdkit_error ("%s: write: %m", argv0
);
294 /* After writing all the data we close the pipe so that
295 * the reader on the other end doesn't wait for more.
299 in_fd
[1] = -1; /* poll will ignore this fd */
304 if (pfds
[1].revents
& POLLIN
) {
305 if (rbuf
->cap
<= rbuf
->len
&& string_reserve (rbuf
, 64) == -1) {
306 nbdkit_error ("%s: realloc: %m", argv0
);
309 r
= read (pfds
[1].fd
, &rbuf
->ptr
[rbuf
->len
], rbuf
->cap
- rbuf
->len
);
311 nbdkit_error ("%s: read: %m", argv0
);
317 out_fd
[0] = -1; /* poll will ignore this fd */
322 else if (pfds
[1].revents
& POLLHUP
) {
327 if (pfds
[2].revents
& POLLIN
) {
328 if (ebuf
->cap
<= ebuf
->len
&& string_reserve (ebuf
, 64) == -1) {
329 nbdkit_error ("%s: realloc: %m", argv0
);
332 r
= read (pfds
[2].fd
, &ebuf
->ptr
[ebuf
->len
], ebuf
->cap
- ebuf
->len
);
334 nbdkit_error ("%s: read: %m", argv0
);
340 err_fd
[0] = -1; /* poll will ignore this fd */
345 else if (pfds
[2].revents
& POLLHUP
) {
350 if (waitpid (pid
, &status
, 0) == -1) {
351 nbdkit_error ("%s: waitpid: %m", argv0
);
357 if (WIFSIGNALED (status
)) {
358 nbdkit_error ("%s: script terminated by signal %d",
359 argv0
, WTERMSIG (status
));
363 if (WIFSTOPPED (status
)) {
364 nbdkit_error ("%s: script stopped by signal %d",
365 argv0
, WTERMSIG (status
));
369 /* \0-terminate both read buffers (for convenience). */
370 if ((rbuf
->cap
<= rbuf
->len
&& string_reserve (rbuf
, 1) == -1) ||
371 (ebuf
->cap
<= ebuf
->len
&& string_reserve (ebuf
, 1) == -1)) {
372 nbdkit_error ("%s: realloc: %m", argv0
);
375 rbuf
->ptr
[rbuf
->len
] = '\0';
376 ebuf
->ptr
[ebuf
->len
] = '\0';
378 ret
= WEXITSTATUS (status
);
379 nbdkit_debug ("completed: %s %s: status %d", argv0
, argv
[1], ret
);
395 waitpid (pid
, NULL
, 0);
400 /* Normalize return codes and parse error string. */
402 handle_script_error (const char *argv0
, string
*ebuf
, exit_code code
)
408 /* ebuf->ptr might be NULL on some return paths from call3(). To
409 * make the following code easier, allocate it and reserve one byte.
410 * Note that ebuf->len is still 0 after this.
412 if (ebuf
->len
== 0) {
413 if (string_reserve (ebuf
, 1) == -1) {
414 nbdkit_error ("realloc: %m");
418 ebuf
->ptr
[ebuf
->len
] = '\0';
425 /* Script successful. */
429 default: /* All other values behave the same as ERROR */
443 nbdkit_disconnect (1);
444 return MISSING
; /* Socket is killed, so client won't see response anyway */
447 nbdkit_disconnect (0);
451 nbdkit_disconnect (0);
456 /* Recognize the errno values that match NBD protocol errors */
457 if (ascii_strncasecmp (ebuf
->ptr
, "EPERM", 5) == 0) {
461 else if (ascii_strncasecmp (ebuf
->ptr
, "EIO", 3) == 0) {
465 else if (ascii_strncasecmp (ebuf
->ptr
, "ENOMEM", 6) == 0) {
469 else if (ascii_strncasecmp (ebuf
->ptr
, "EINVAL", 6) == 0) {
473 else if (ascii_strncasecmp (ebuf
->ptr
, "ENOSPC", 6) == 0) {
477 else if (ascii_strncasecmp (ebuf
->ptr
, "EOVERFLOW", 9) == 0) {
481 else if (ascii_strncasecmp (ebuf
->ptr
, "ESHUTDOWN", 9) == 0) {
485 else if (ascii_strncasecmp (ebuf
->ptr
, "ENOTSUP", 7) == 0) {
489 else if (ascii_strncasecmp (ebuf
->ptr
, "EOPNOTSUPP", 10) == 0) {
493 /* Other errno values that server/protocol.c treats specially */
494 else if (ascii_strncasecmp (ebuf
->ptr
, "EROFS", 5) == 0) {
498 else if (ascii_strncasecmp (ebuf
->ptr
, "EDQUOT", 6) == 0) {
506 else if (ascii_strncasecmp (ebuf
->ptr
, "EFBIG", 5) == 0) {
510 /* Otherwise, use value of err populated in switch above */
512 if (skip
&& ebuf
->ptr
[skip
]) {
513 if (!ascii_isspace (ebuf
->ptr
[skip
])) {
514 /* Treat 'EINVALID' as EIO, not EINVAL */
521 while (ascii_isspace (ebuf
->ptr
[skip
]));
524 while (ebuf
->len
> 0 && ebuf
->ptr
[ebuf
->len
-1] == '\n')
525 ebuf
->ptr
[--ebuf
->len
] = '\0';
528 p
= strchr (&ebuf
->ptr
[skip
], '\n');
530 /* More than one line, so write the whole message to debug ... */
531 nbdkit_debug ("%s: %s", argv0
, ebuf
->ptr
);
532 /* ... but truncate it for the error message below. */
535 nbdkit_error ("%s: %s", argv0
, &ebuf
->ptr
[skip
]);
538 nbdkit_error ("%s: script exited with error, "
539 "but did not print an error message on stderr", argv0
);
547 /* Call the script with parameters. Don't write to stdin or read from
548 * stdout, but handle stderr if an error occurs. Returns the exit
549 * code from the script.
552 call (const char **argv
)
555 CLEANUP_FREE_STRING string rbuf
= empty_vector
;
556 CLEANUP_FREE_STRING string ebuf
= empty_vector
;
558 r
= call3 (NULL
, 0, &rbuf
, &ebuf
, argv
);
559 return handle_script_error (argv
[0], &ebuf
, r
);
562 /* Call the script with parameters. Read from stdout and return the
563 * buffer. Returns the exit code from the script.
566 call_read (string
*rbuf
, const char **argv
)
569 CLEANUP_FREE_STRING string ebuf
= empty_vector
;
571 r
= call3 (NULL
, 0, rbuf
, &ebuf
, argv
);
572 r
= handle_script_error (argv
[0], &ebuf
, r
);
578 /* Call the script with parameters. Write to stdin of the script.
579 * Returns the exit code from the script.
582 call_write (const char *wbuf
, size_t wbuflen
, const char **argv
)
585 CLEANUP_FREE_STRING string rbuf
= empty_vector
;
586 CLEANUP_FREE_STRING string ebuf
= empty_vector
;
588 r
= call3 (wbuf
, wbuflen
, &rbuf
, &ebuf
, argv
);
589 return handle_script_error (argv
[0], &ebuf
, r
);