sh: Log invoked subcommands
[nbdkit/ericb.git] / plugins / sh / call.c
blobdd4b15c5d0d1ee0fa8fe11b5d4c7496a9800734a
1 /* nbdkit
2 * Copyright (C) 2018-2019 Red Hat Inc.
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>
47 #include <ctype.h>
49 #include <nbdkit-plugin.h>
51 #include "cleanup.h"
53 #include "call.h"
55 /* Ensure there is at least 1 byte of space in the buffer. */
56 static int
57 expand_buf (char **buf, size_t *buflen, size_t *bufalloc)
59 char *nb;
61 if (*bufalloc > *buflen)
62 return 0;
64 *bufalloc = *bufalloc == 0 ? 64 : *bufalloc * 2;
65 nb = realloc (*buf, *bufalloc);
66 if (nb == NULL) {
67 nbdkit_error ("%s: malloc: %m", script);
68 return -1;
70 *buf = nb;
71 return 0;
74 /* This is the generic function that calls the script. It can
75 * optionally write to the script's stdin and read from the script's
76 * stdout and stderr. It returns the raw error code and does no error
77 * processing.
79 static int
80 call3 (const char *wbuf, size_t wbuflen, /* sent to stdin */
81 char **rbuf, size_t *rbuflen, /* read from stdout */
82 char **ebuf, size_t *ebuflen, /* read from stderr */
83 const char **argv) /* script + parameters */
85 pid_t pid = -1;
86 int status;
87 int ret = ERROR;
88 int in_fd[2] = { -1, -1 };
89 int out_fd[2] = { -1, -1 };
90 int err_fd[2] = { -1, -1 };
91 size_t rbufalloc, ebufalloc;
92 struct pollfd pfds[3];
93 ssize_t r;
95 *rbuf = *ebuf = NULL;
96 *rbuflen = *ebuflen = 0;
97 rbufalloc = ebufalloc = 0;
99 /* Decent logging is worth the hassle. We don't send more than 5 args. */
100 const char *arg1 = argv[1], *arg2 = "", *arg3 = "", *arg4 = "", *arg5 = "";
101 assert (arg1);
102 if (argv[2]) {
103 arg2 = argv[2];
104 if (argv[3]) {
105 arg3 = argv[3];
106 if (argv[4]) {
107 arg4 = argv[4];
108 if (argv[5])
109 arg5 = argv[5];
113 nbdkit_debug ("%s: invoking %s %s %s %s %s ...",
114 script, arg1, arg2, arg3, arg4, arg5);
117 #ifdef HAVE_PIPE2
118 if (pipe2 (in_fd, O_CLOEXEC) == -1) {
119 nbdkit_error ("%s: pipe2: %m", script);
120 goto error;
122 if (pipe2 (out_fd, O_CLOEXEC) == -1) {
123 nbdkit_error ("%s: pipe2: %m", script);
124 goto error;
126 if (pipe2 (err_fd, O_CLOEXEC) == -1) {
127 nbdkit_error ("%s: pipe2: %m", script);
128 goto error;
130 #else
131 /* Without pipe2, nbdkit forces the thread model maximum down to
132 * NBDKIT_THREAD_MODEL_SERIALIZE_ALL_REQUESTS, this in turn ensures
133 * no other thread will be trying to fork, and thus we can skip
134 * worrying about CLOEXEC races. Therefore, it's not worth adding a
135 * loop after fork to close unexpected fds.
137 if (pipe (in_fd) == -1) {
138 nbdkit_error ("%s: pipe: %m", script);
139 goto error;
141 if (pipe (out_fd) == -1) {
142 nbdkit_error ("%s: pipe: %m", script);
143 goto error;
145 if (pipe (err_fd) == -1) {
146 nbdkit_error ("%s: pipe: %m", script);
147 goto error;
149 #endif
151 /* Ensure that stdin/out/err of the current process were not empty
152 * before we started creating pipes (otherwise, the close and dup2
153 * calls below become more complex to juggle fds around correctly).
155 assert (in_fd[0] > STDERR_FILENO && in_fd[1] > STDERR_FILENO &&
156 out_fd[0] > STDERR_FILENO && out_fd[1] > STDERR_FILENO &&
157 err_fd[0] > STDERR_FILENO && err_fd[1] > STDERR_FILENO);
159 pid = fork ();
160 if (pid == -1) {
161 nbdkit_error ("%s: fork: %m", script);
162 goto error;
165 if (pid == 0) { /* Child. */
166 close (in_fd[1]);
167 close (out_fd[0]);
168 close (err_fd[0]);
169 dup2 (in_fd[0], 0);
170 dup2 (out_fd[1], 1);
171 dup2 (err_fd[1], 2);
172 close (in_fd[0]);
173 close (out_fd[1]);
174 close (err_fd[1]);
176 /* Restore SIGPIPE back to SIG_DFL, since shell can't undo SIG_IGN */
177 signal (SIGPIPE, SIG_DFL);
179 execvp (argv[0], (char **) argv);
180 perror (argv[0]);
181 _exit (EXIT_FAILURE);
184 /* Parent. */
185 close (in_fd[0]); in_fd[0] = -1;
186 close (out_fd[1]); out_fd[1] = -1;
187 close (err_fd[1]); err_fd[1] = -1;
189 while (out_fd[0] >= 0 || err_fd[0] >= 0) {
190 pfds[0].fd = in_fd[1]; /* Connected to child stdin. */
191 pfds[0].events = wbuflen ? POLLOUT : 0;
192 pfds[1].fd = out_fd[0]; /* Connected to child stdout. */
193 pfds[1].events = POLLIN;
194 pfds[2].fd = err_fd[0]; /* Connected to child stderr. */
195 pfds[2].events = POLLIN;
197 if (poll (pfds, 3, -1) == -1) {
198 if (errno == EINTR || errno == EAGAIN)
199 continue;
200 nbdkit_error ("%s: poll: %m", script);
201 goto error;
204 /* Write more data to stdin. */
205 if (pfds[0].revents & POLLOUT) {
206 r = write (pfds[0].fd, wbuf, wbuflen);
207 if (r == -1) {
208 nbdkit_error ("%s: write: %m", script);
209 goto error;
211 wbuf += r;
212 wbuflen -= r;
213 /* After writing all the data we close the pipe so that
214 * the reader on the other end doesn't wait for more.
216 if (wbuflen == 0) {
217 close (in_fd[1]);
218 in_fd[1] = -1; /* poll will ignore this fd */
222 /* Check stdout. */
223 if (pfds[1].revents & POLLIN) {
224 if (expand_buf (rbuf, rbuflen, &rbufalloc) == -1)
225 goto error;
226 r = read (pfds[1].fd, *rbuf + *rbuflen, rbufalloc - *rbuflen);
227 if (r == -1) {
228 nbdkit_error ("%s: read: %m", script);
229 goto error;
231 else if (r == 0) {
232 close_out:
233 close (out_fd[0]);
234 out_fd[0] = -1; /* poll will ignore this fd */
236 else if (r > 0)
237 *rbuflen += r;
239 else if (pfds[1].revents & POLLHUP) {
240 goto close_out;
243 /* Check stderr. */
244 if (pfds[2].revents & POLLIN) {
245 if (expand_buf (ebuf, ebuflen, &ebufalloc) == -1)
246 goto error;
247 r = read (pfds[2].fd, *ebuf + *ebuflen, ebufalloc - *ebuflen);
248 if (r == -1) {
249 nbdkit_error ("%s: read: %m", script);
250 goto error;
252 else if (r == 0) {
253 close_err:
254 close (err_fd[0]);
255 err_fd[0] = -1; /* poll will ignore this fd */
257 else if (r > 0)
258 *ebuflen += r;
260 else if (pfds[2].revents & POLLHUP) {
261 goto close_err;
265 if (waitpid (pid, &status, 0) == -1) {
266 nbdkit_error ("%s: waitpid: %m", script);
267 pid = -1;
268 goto error;
270 pid = -1;
272 if (WIFSIGNALED (status)) {
273 nbdkit_error ("%s: script terminated by signal %d",
274 script, WTERMSIG (status));
275 goto error;
278 if (WIFSTOPPED (status)) {
279 nbdkit_error ("%s: script stopped by signal %d",
280 script, WTERMSIG (status));
281 goto error;
284 /* \0-terminate both read buffers (for convenience). */
285 if (expand_buf (rbuf, rbuflen, &rbufalloc) == -1)
286 goto error;
287 if (expand_buf (ebuf, ebuflen, &ebufalloc) == -1)
288 goto error;
289 (*rbuf)[*rbuflen] = '\0';
290 (*ebuf)[*ebuflen] = '\0';
292 ret = WEXITSTATUS (status);
293 nbdkit_debug ("%s: ... %s completed with status %d", script, argv[1], ret);
295 error:
296 if (in_fd[0] >= 0)
297 close (in_fd[0]);
298 if (in_fd[1] >= 0)
299 close (in_fd[1]);
300 if (out_fd[0] >= 0)
301 close (out_fd[0]);
302 if (out_fd[1] >= 0)
303 close (out_fd[1]);
304 if (err_fd[0] >= 0)
305 close (err_fd[0]);
306 if (err_fd[1] >= 0)
307 close (err_fd[1]);
308 if (pid >= 0)
309 waitpid (pid, NULL, 0);
311 return ret;
314 static void
315 handle_script_error (char *ebuf, size_t len)
317 int err;
318 size_t skip = 0;
319 char *p;
321 if (strncasecmp (ebuf, "EPERM", 5) == 0) {
322 err = EPERM;
323 skip = 5;
325 else if (strncasecmp (ebuf, "EIO", 3) == 0) {
326 err = EIO;
327 skip = 3;
329 else if (strncasecmp (ebuf, "ENOMEM", 6) == 0) {
330 err = ENOMEM;
331 skip = 6;
333 else if (strncasecmp (ebuf, "EINVAL", 6) == 0) {
334 err = EINVAL;
335 skip = 6;
337 else if (strncasecmp (ebuf, "ENOSPC", 6) == 0) {
338 err = ENOSPC;
339 skip = 6;
341 else if (strncasecmp (ebuf, "EOVERFLOW", 9) == 0) {
342 err = EOVERFLOW;
343 skip = 9;
345 else if (strncasecmp (ebuf, "ESHUTDOWN", 9) == 0) {
346 err = ESHUTDOWN;
347 skip = 9;
349 else {
350 /* Default to EIO. */
351 err = EIO;
352 skip = 0;
355 if (skip && ebuf[skip]) {
356 if (!isspace ((unsigned char) ebuf[skip])) {
357 /* Treat 'EINVALID' as EIO, not EINVAL */
358 err = EIO;
359 skip = 0;
361 else
363 skip++;
364 while (isspace ((unsigned char) ebuf[skip]));
367 while (len > 0 && ebuf[len-1] == '\n')
368 ebuf[--len] = '\0';
370 if (len > 0) {
371 p = strchr (ebuf + skip, '\n');
372 if (p) {
373 /* More than one line, so write the whole message to debug ... */
374 nbdkit_debug ("%s: %s", script, ebuf);
375 /* ... but truncate it for the error message below. */
376 *p = '\0';
378 ebuf += skip;
379 nbdkit_error ("%s: %s", script, ebuf);
381 else
382 nbdkit_error ("%s: script exited with error, "
383 "but did not print an error message on stderr", script);
385 /* Set errno. */
386 errno = err;
389 /* Call the script with parameters. Don't write to stdin or read from
390 * stdout, but handle stderr if an error occurs. Returns the exit
391 * code from the script.
393 exit_code
394 call (const char **argv)
396 int r;
397 CLEANUP_FREE char *rbuf = NULL;
398 size_t rbuflen;
399 CLEANUP_FREE char *ebuf = NULL;
400 size_t ebuflen;
402 r = call3 (NULL, 0, &rbuf, &rbuflen, &ebuf, &ebuflen, argv);
403 switch (r) {
404 case OK:
405 case MISSING:
406 case RET_FALSE:
407 /* Script successful. */
408 return r;
410 case ERROR:
411 default:
412 /* Error case. */
413 handle_script_error (ebuf, ebuflen);
414 return ERROR;
418 /* Call the script with parameters. Read from stdout and return the
419 * buffer. Returns the exit code from the script.
421 exit_code
422 call_read (char **rbuf, size_t *rbuflen, const char **argv)
424 int r;
425 CLEANUP_FREE char *ebuf = NULL;
426 size_t ebuflen;
428 r = call3 (NULL, 0, rbuf, rbuflen, &ebuf, &ebuflen, argv);
429 switch (r) {
430 case OK:
431 case MISSING:
432 case RET_FALSE:
433 /* Script successful. */
434 return r;
436 case ERROR:
437 default:
438 /* Error case. */
439 free (*rbuf);
440 *rbuf = NULL;
441 handle_script_error (ebuf, ebuflen);
442 return ERROR;
446 /* Call the script with parameters. Write to stdin of the script.
447 * Returns the exit code from the script.
449 exit_code
450 call_write (const char *wbuf, size_t wbuflen, const char **argv)
452 int r;
453 CLEANUP_FREE char *rbuf = NULL;
454 size_t rbuflen;
455 CLEANUP_FREE char *ebuf = NULL;
456 size_t ebuflen;
458 r = call3 (wbuf, wbuflen, &rbuf, &rbuflen, &ebuf, &ebuflen, argv);
459 switch (r) {
460 case OK:
461 case MISSING:
462 case RET_FALSE:
463 /* Script successful. */
464 return r;
466 case ERROR:
467 default:
468 /* Error case. */
469 handle_script_error (ebuf, ebuflen);
470 return ERROR;