Update Red Hat Copyright Notices
[nbdkit.git] / plugins / sh / call.c
bloba07935b9294629ae42fb59cb532c703fa2d3e1d6
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 <assert.h>
36 #include <fcntl.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <inttypes.h>
40 #include <string.h>
41 #include <unistd.h>
42 #include <errno.h>
43 #include <signal.h>
44 #include <poll.h>
45 #include <sys/types.h>
46 #include <sys/wait.h>
48 #include <nbdkit-plugin.h>
50 #include "ascii-ctype.h"
51 #include "ascii-string.h"
52 #include "cleanup.h"
53 #include "utils.h"
54 #include "vector.h"
56 #include "call.h"
58 #ifndef HAVE_ENVIRON_DECL
59 extern char **environ;
60 #endif
62 /* Temporary directory for scripts to use. */
63 char tmpdir[] = "/tmp/nbdkitXXXXXX";
65 /* Private copy of environ, with $tmpdir added. */
66 static char **env;
68 void
69 call_load (void)
71 /* Create the temporary directory for the shell script to use. */
72 if (mkdtemp (tmpdir) == NULL) {
73 nbdkit_error ("mkdtemp: /tmp: %m");
74 exit (EXIT_FAILURE);
77 nbdkit_debug ("load: tmpdir: %s", tmpdir);
79 /* Copy the environment, and add $tmpdir. */
80 env = copy_environ (environ, "tmpdir", tmpdir, NULL);
81 if (env == NULL)
82 exit (EXIT_FAILURE);
85 #pragma GCC diagnostic push
86 #pragma GCC diagnostic ignored "-Wunused-result"
87 void
88 call_unload (void)
90 CLEANUP_FREE char *cmd = NULL;
91 size_t i;
93 /* Delete the temporary directory. Ignore all errors. */
94 if (asprintf (&cmd, "rm -rf %s", tmpdir) >= 0)
95 system (cmd);
97 /* Free the private copy of environ. */
98 for (i = 0; env[i] != NULL; ++i)
99 free (env[i]);
100 free (env);
102 #pragma GCC diagnostic pop
104 static void
105 debug_call (const char **argv)
107 CLEANUP_FREE char *debug = NULL;
108 size_t i, len = 0;
109 FILE *fp;
111 fp = open_memstream (&debug, &len);
112 if (fp == NULL)
113 return;
115 fprintf (fp, "calling:");
116 for (i = 0; argv[i] != NULL; ++i) {
117 fputc (' ', fp);
118 shell_quote (argv[i], fp);
121 fclose (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
129 * processing.
131 static int
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 */
138 #ifndef __GLIBC__
139 CLEANUP_FREE const char **sh_argv = NULL;
140 size_t i;
141 #endif
142 pid_t pid = -1;
143 int status;
144 int ret = ERROR;
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];
149 ssize_t r;
151 /* Ignore any previous contents of rbuf, ebuf. */
152 string_reset (rbuf);
153 string_reset (ebuf);
155 debug_call (argv);
157 #ifdef HAVE_PIPE2
158 if (pipe2 (in_fd, O_CLOEXEC) == -1) {
159 nbdkit_error ("%s: pipe2: %m", argv0);
160 goto error;
162 if (pipe2 (out_fd, O_CLOEXEC) == -1) {
163 nbdkit_error ("%s: pipe2: %m", argv0);
164 goto error;
166 if (pipe2 (err_fd, O_CLOEXEC) == -1) {
167 nbdkit_error ("%s: pipe2: %m", argv0);
168 goto error;
170 #else
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);
179 goto error;
181 if (pipe (out_fd) == -1) {
182 nbdkit_error ("%s: pipe: %m", argv0);
183 goto error;
185 if (pipe (err_fd) == -1) {
186 nbdkit_error ("%s: pipe: %m", argv0);
187 goto error;
189 #endif
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);
199 #ifndef __GLIBC__
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);
211 goto error;
213 sh_argv[0] = "/bin/sh";
214 for (i = 0; argv[i]; i++)
215 sh_argv[i+1] = argv[i];
216 #endif
218 pid = fork ();
219 if (pid == -1) {
220 nbdkit_error ("%s: fork: %m", argv0);
221 goto error;
224 if (pid == 0) { /* Child. */
225 close (in_fd[1]);
226 close (out_fd[0]);
227 close (err_fd[0]);
228 dup2 (in_fd[0], 0);
229 dup2 (out_fd[1], 1);
230 dup2 (err_fd[1], 2);
231 close (in_fd[0]);
232 close (out_fd[1]);
233 close (err_fd[1]);
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
242 environ = env;
243 execvp (argv[0], (char **) argv);
244 #ifndef __GLIBC__
245 /* Non-glibc workaround for missing shebang - see above. */
246 if (errno == ENOEXEC)
247 execvp (sh_argv[0], (char **) sh_argv);
248 #endif
249 perror (argv[0]);
250 _exit (EXIT_FAILURE);
253 /* Parent. */
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)
268 continue;
269 nbdkit_error ("%s: poll: %m", argv0);
270 goto error;
273 /* Write more data to stdin. */
274 if (pfds[0].revents & POLLOUT) {
275 r = write (pfds[0].fd, wbuf, wbuflen);
276 if (r == -1) {
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 "
285 "in the script",
286 argv0);
288 else
289 nbdkit_error ("%s: write: %m", argv0);
290 goto error;
292 wbuf += r;
293 wbuflen -= r;
294 /* After writing all the data we close the pipe so that
295 * the reader on the other end doesn't wait for more.
297 if (wbuflen == 0) {
298 close (in_fd[1]);
299 in_fd[1] = -1; /* poll will ignore this fd */
303 /* Check stdout. */
304 if (pfds[1].revents & POLLIN) {
305 if (rbuf->cap <= rbuf->len && string_reserve (rbuf, 64) == -1) {
306 nbdkit_error ("%s: realloc: %m", argv0);
307 goto error;
309 r = read (pfds[1].fd, &rbuf->ptr[rbuf->len], rbuf->cap - rbuf->len);
310 if (r == -1) {
311 nbdkit_error ("%s: read: %m", argv0);
312 goto error;
314 else if (r == 0) {
315 close_out:
316 close (out_fd[0]);
317 out_fd[0] = -1; /* poll will ignore this fd */
319 else if (r > 0)
320 rbuf->len += r;
322 else if (pfds[1].revents & POLLHUP) {
323 goto close_out;
326 /* Check stderr. */
327 if (pfds[2].revents & POLLIN) {
328 if (ebuf->cap <= ebuf->len && string_reserve (ebuf, 64) == -1) {
329 nbdkit_error ("%s: realloc: %m", argv0);
330 goto error;
332 r = read (pfds[2].fd, &ebuf->ptr[ebuf->len], ebuf->cap - ebuf->len);
333 if (r == -1) {
334 nbdkit_error ("%s: read: %m", argv0);
335 goto error;
337 else if (r == 0) {
338 close_err:
339 close (err_fd[0]);
340 err_fd[0] = -1; /* poll will ignore this fd */
342 else if (r > 0)
343 ebuf->len += r;
345 else if (pfds[2].revents & POLLHUP) {
346 goto close_err;
350 if (waitpid (pid, &status, 0) == -1) {
351 nbdkit_error ("%s: waitpid: %m", argv0);
352 pid = -1;
353 goto error;
355 pid = -1;
357 if (WIFSIGNALED (status)) {
358 nbdkit_error ("%s: script terminated by signal %d",
359 argv0, WTERMSIG (status));
360 goto error;
363 if (WIFSTOPPED (status)) {
364 nbdkit_error ("%s: script stopped by signal %d",
365 argv0, WTERMSIG (status));
366 goto error;
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);
373 goto error;
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);
381 error:
382 if (in_fd[0] >= 0)
383 close (in_fd[0]);
384 if (in_fd[1] >= 0)
385 close (in_fd[1]);
386 if (out_fd[0] >= 0)
387 close (out_fd[0]);
388 if (out_fd[1] >= 0)
389 close (out_fd[1]);
390 if (err_fd[0] >= 0)
391 close (err_fd[0]);
392 if (err_fd[1] >= 0)
393 close (err_fd[1]);
394 if (pid >= 0)
395 waitpid (pid, NULL, 0);
397 return ret;
400 /* Normalize return codes and parse error string. */
401 static exit_code
402 handle_script_error (const char *argv0, string *ebuf, exit_code code)
404 int err;
405 size_t skip = 0;
406 char *p;
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");
415 err = EIO;
416 return ERROR;
418 ebuf->ptr[ebuf->len] = '\0';
421 switch (code) {
422 case OK:
423 case MISSING:
424 case RET_FALSE:
425 /* Script successful. */
426 return code;
428 case ERROR:
429 default: /* All other values behave the same as ERROR */
430 err = EIO;
431 break;
433 case SHUTDOWN_OK:
434 nbdkit_shutdown ();
435 return OK;
437 case SHUTDOWN_ERR:
438 nbdkit_shutdown ();
439 err = ESHUTDOWN;
440 break;
442 case DISC_FORCE:
443 nbdkit_disconnect (1);
444 return MISSING; /* Socket is killed, so client won't see response anyway */
446 case DISC_SOFT_OK:
447 nbdkit_disconnect (0);
448 return OK;
450 case DISC_SOFT_ERR:
451 nbdkit_disconnect (0);
452 err = ESHUTDOWN;
453 break;
456 /* Recognize the errno values that match NBD protocol errors */
457 if (ascii_strncasecmp (ebuf->ptr, "EPERM", 5) == 0) {
458 err = EPERM;
459 skip = 5;
461 else if (ascii_strncasecmp (ebuf->ptr, "EIO", 3) == 0) {
462 err = EIO;
463 skip = 3;
465 else if (ascii_strncasecmp (ebuf->ptr, "ENOMEM", 6) == 0) {
466 err = ENOMEM;
467 skip = 6;
469 else if (ascii_strncasecmp (ebuf->ptr, "EINVAL", 6) == 0) {
470 err = EINVAL;
471 skip = 6;
473 else if (ascii_strncasecmp (ebuf->ptr, "ENOSPC", 6) == 0) {
474 err = ENOSPC;
475 skip = 6;
477 else if (ascii_strncasecmp (ebuf->ptr, "EOVERFLOW", 9) == 0) {
478 err = EOVERFLOW;
479 skip = 9;
481 else if (ascii_strncasecmp (ebuf->ptr, "ESHUTDOWN", 9) == 0) {
482 err = ESHUTDOWN;
483 skip = 9;
485 else if (ascii_strncasecmp (ebuf->ptr, "ENOTSUP", 7) == 0) {
486 err = ENOTSUP;
487 skip = 7;
489 else if (ascii_strncasecmp (ebuf->ptr, "EOPNOTSUPP", 10) == 0) {
490 err = EOPNOTSUPP;
491 skip = 10;
493 /* Other errno values that server/protocol.c treats specially */
494 else if (ascii_strncasecmp (ebuf->ptr, "EROFS", 5) == 0) {
495 err = EROFS;
496 skip = 5;
498 else if (ascii_strncasecmp (ebuf->ptr, "EDQUOT", 6) == 0) {
499 #ifdef EDQUOT
500 err = EDQUOT;
501 #else
502 err = ENOSPC;
503 #endif
504 skip = 6;
506 else if (ascii_strncasecmp (ebuf->ptr, "EFBIG", 5) == 0) {
507 err = EFBIG;
508 skip = 5;
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 */
515 err = EIO;
516 skip = 0;
518 else
520 skip++;
521 while (ascii_isspace (ebuf->ptr[skip]));
524 while (ebuf->len > 0 && ebuf->ptr[ebuf->len-1] == '\n')
525 ebuf->ptr[--ebuf->len] = '\0';
527 if (ebuf->len > 0) {
528 p = strchr (&ebuf->ptr[skip], '\n');
529 if (p) {
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. */
533 *p = '\0';
535 nbdkit_error ("%s: %s", argv0, &ebuf->ptr[skip]);
537 else {
538 nbdkit_error ("%s: script exited with error, "
539 "but did not print an error message on stderr", argv0);
542 /* Set errno. */
543 errno = err;
544 return ERROR;
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.
551 exit_code
552 call (const char **argv)
554 int r;
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.
565 exit_code
566 call_read (string *rbuf, const char **argv)
568 int r;
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);
573 if (r == ERROR)
574 string_reset (rbuf);
575 return r;
578 /* Call the script with parameters. Write to stdin of the script.
579 * Returns the exit code from the script.
581 exit_code
582 call_write (const char *wbuf, size_t wbuflen, const char **argv)
584 int r;
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);