Expand PMF_FN_* macros.
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / global / pipe_command.c
blob7188be5f44095b8eead0f62a1ab8bbb7fb2012f2
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* pipe_command 3
6 /* SUMMARY
7 /* deliver message to external command
8 /* SYNOPSIS
9 /* #include <pipe_command.h>
11 /* int pipe_command(src, why, key, value, ...)
12 /* VSTREAM *src;
13 /* DSN_BUF *why;
14 /* int key;
15 /* DESCRIPTION
16 /* pipe_command() runs a command with a message as standard
17 /* input. A limited amount of standard output and standard error
18 /* output is captured for diagnostics purposes.
20 /* If the command invokes exit() with a non-zero status,
21 /* the delivery status is taken from an RFC 3463-style code
22 /* at the beginning of command output. If that information is
23 /* unavailable, the delivery status is taken from the command
24 /* exit status as per <sysexits.h>.
26 /* Arguments:
27 /* .IP src
28 /* An open message queue file, positioned at the start of the actual
29 /* message content.
30 /* .IP why
31 /* Delivery status information.
32 /* .IP key
33 /* Specifies what value will follow. pipe_command() takes a list
34 /* of (key, value) arguments, terminated by PIPE_CMD_END. The
35 /* following is a listing of key codes together with the expected
36 /* value type.
37 /* .RS
38 /* .IP "PIPE_CMD_COMMAND (char *)"
39 /* Specifies the command to execute as a string. The string is
40 /* passed to the shell when it contains shell meta characters
41 /* or when it appears to be a shell built-in command, otherwise
42 /* the command is executed without invoking a shell.
43 /* One of PIPE_CMD_COMMAND or PIPE_CMD_ARGV must be specified.
44 /* See also the PIPE_CMD_SHELL attribute below.
45 /* .IP "PIPE_CMD_ARGV (char **)"
46 /* The command is specified as an argument vector. This vector is
47 /* passed without further inspection to the \fIexecvp\fR() routine.
48 /* One of PIPE_CMD_COMMAND or PIPE_CMD_ARGV must be specified.
49 /* .IP "PIPE_CMD_CHROOT (char *)"
50 /* Root and working directory for command execution. This takes
51 /* effect before PIPE_CMD_CWD. A null pointer means don't
52 /* change root and working directory anyway. Failure to change
53 /* directory causes mail delivery to be deferred.
54 /* .IP "PIPE_CMD_CWD (char *)"
55 /* Working directory for command execution, after changing process
56 /* privileges to PIPE_CMD_UID and PIPE_CMD_GID. A null pointer means
57 /* don't change directory anyway. Failure to change directory
58 /* causes mail delivery to be deferred.
59 /* .IP "PIPE_CMD_ENV (char **)"
60 /* Additional environment information, in the form of a null-terminated
61 /* list of name, value, name, value, ... elements. By default only the
62 /* command search path is initialized to _PATH_DEFPATH.
63 /* .IP "PIPE_CMD_EXPORT (char **)"
64 /* Null-terminated array with names of environment parameters
65 /* that can be exported. By default, everything is exported.
66 /* .IP "PIPE_CMD_COPY_FLAGS (int)"
67 /* Flags that are passed on to the \fImail_copy\fR() routine.
68 /* The default flags value is 0 (zero).
69 /* .IP "PIPE_CMD_SENDER (char *)"
70 /* The envelope sender address, which is passed on to the
71 /* \fImail_copy\fR() routine.
72 /* .IP "PIPE_CMD_ORIG_RCPT (char *)"
73 /* The original recipient envelope address, which is passed on
74 /* to the \fImail_copy\fR() routine.
75 /* .IP "PIPE_CMD_DELIVERED (char *)"
76 /* The recipient envelope address, which is passed on to the
77 /* \fImail_copy\fR() routine.
78 /* .IP "PIPE_CMD_EOL (char *)"
79 /* End-of-line delimiter. The default is to use the newline character.
80 /* .IP "PIPE_CMD_UID (uid_t)"
81 /* The user ID to execute the command as. The default is
82 /* the user ID corresponding to the \fIdefault_privs\fR
83 /* configuration parameter. The user ID must be non-zero.
84 /* .IP "PIPE_CMD_GID (gid_t)"
85 /* The group ID to execute the command as. The default is
86 /* the group ID corresponding to the \fIdefault_privs\fR
87 /* configuration parameter. The group ID must be non-zero.
88 /* .IP "PIPE_CMD_TIME_LIMIT (int)"
89 /* The amount of time the command is allowed to run before it
90 /* is terminated with SIGKILL. The default is the limit given
91 /* with the \fIcommand_time_limit\fR configuration parameter.
92 /* .IP "PIPE_CMD_SHELL (char *)"
93 /* The shell to use when executing the command specified with
94 /* PIPE_CMD_COMMAND. This shell is invoked regardless of the
95 /* command content.
96 /* .RE
97 /* DIAGNOSTICS
98 /* Panic: interface violations (for example, a zero-valued
99 /* user ID or group ID, or a missing command).
101 /* pipe_command() returns one of the following status codes:
102 /* .IP PIPE_STAT_OK
103 /* The command has taken responsibility for further delivery of
104 /* the message.
105 /* .IP PIPE_STAT_DEFER
106 /* The command failed with a "try again" type error.
107 /* The reason is given via the \fIwhy\fR argument.
108 /* .IP PIPE_STAT_BOUNCE
109 /* The command indicated that the message was not acceptable,
110 /* or the command did not finish within the time limit.
111 /* The reason is given via the \fIwhy\fR argument.
112 /* .IP PIPE_STAT_CORRUPT
113 /* The queue file is corrupted.
114 /* SEE ALSO
115 /* mail_copy(3) deliver to any.
116 /* mark_corrupt(3) mark queue file as corrupt.
117 /* sys_exits(3) sendmail-compatible exit status codes.
118 /* LICENSE
119 /* .ad
120 /* .fi
121 /* The Secure Mailer license must be distributed with this software.
122 /* AUTHOR(S)
123 /* Wietse Venema
124 /* IBM T.J. Watson Research
125 /* P.O. Box 704
126 /* Yorktown Heights, NY 10598, USA
127 /*--*/
129 /* System library. */
131 #include <sys_defs.h>
132 #include <sys/wait.h>
133 #include <signal.h>
134 #include <unistd.h>
135 #include <errno.h>
136 #include <stdarg.h>
137 #include <fcntl.h>
138 #include <stdlib.h>
139 #ifdef USE_PATHS_H
140 #include <paths.h>
141 #endif
142 #include <syslog.h>
144 /* Utility library. */
146 #include <msg.h>
147 #include <vstream.h>
148 #include <msg_vstream.h>
149 #include <vstring.h>
150 #include <stringops.h>
151 #include <iostuff.h>
152 #include <timed_wait.h>
153 #include <set_ugid.h>
154 #include <set_eugid.h>
155 #include <argv.h>
156 #include <chroot_uid.h>
158 /* Global library. */
160 #include <mail_params.h>
161 #include <mail_copy.h>
162 #include <clean_env.h>
163 #include <pipe_command.h>
164 #include <exec_command.h>
165 #include <sys_exits.h>
166 #include <dsn_util.h>
167 #include <dsn_buf.h>
169 /* Application-specific. */
171 struct pipe_args {
172 int flags; /* see mail_copy.h */
173 char *sender; /* envelope sender */
174 char *orig_rcpt; /* original recipient */
175 char *delivered; /* envelope recipient */
176 char *eol; /* carriagecontrol */
177 char **argv; /* either an array */
178 char *command; /* or a plain string */
179 uid_t uid; /* privileges */
180 gid_t gid; /* privileges */
181 char **env; /* extra environment */
182 char **export; /* exportable environment */
183 char *shell; /* command shell */
184 char *cwd; /* preferred working directory */
185 char *chroot; /* root directory */
188 static int pipe_command_timeout; /* command has timed out */
189 static int pipe_command_maxtime; /* available time to complete */
191 /* get_pipe_args - capture the variadic argument list */
193 static void get_pipe_args(struct pipe_args * args, va_list ap)
195 const char *myname = "get_pipe_args";
196 int key;
199 * First, set the default values.
201 args->flags = 0;
202 args->sender = 0;
203 args->orig_rcpt = 0;
204 args->delivered = 0;
205 args->eol = "\n";
206 args->argv = 0;
207 args->command = 0;
208 args->uid = var_default_uid;
209 args->gid = var_default_gid;
210 args->env = 0;
211 args->export = 0;
212 args->shell = 0;
213 args->cwd = 0;
214 args->chroot = 0;
216 pipe_command_maxtime = var_command_maxtime;
219 * Then, override the defaults with user-supplied inputs.
221 while ((key = va_arg(ap, int)) != PIPE_CMD_END) {
222 switch (key) {
223 case PIPE_CMD_COPY_FLAGS:
224 args->flags |= va_arg(ap, int);
225 break;
226 case PIPE_CMD_SENDER:
227 args->sender = va_arg(ap, char *);
228 break;
229 case PIPE_CMD_ORIG_RCPT:
230 args->orig_rcpt = va_arg(ap, char *);
231 break;
232 case PIPE_CMD_DELIVERED:
233 args->delivered = va_arg(ap, char *);
234 break;
235 case PIPE_CMD_EOL:
236 args->eol = va_arg(ap, char *);
237 break;
238 case PIPE_CMD_ARGV:
239 if (args->command)
240 msg_panic("%s: got PIPE_CMD_ARGV and PIPE_CMD_COMMAND", myname);
241 args->argv = va_arg(ap, char **);
242 break;
243 case PIPE_CMD_COMMAND:
244 if (args->argv)
245 msg_panic("%s: got PIPE_CMD_ARGV and PIPE_CMD_COMMAND", myname);
246 args->command = va_arg(ap, char *);
247 break;
248 case PIPE_CMD_UID:
249 args->uid = va_arg(ap, uid_t); /* in case uid_t is short */
250 break;
251 case PIPE_CMD_GID:
252 args->gid = va_arg(ap, gid_t); /* in case gid_t is short */
253 break;
254 case PIPE_CMD_TIME_LIMIT:
255 pipe_command_maxtime = va_arg(ap, int);
256 break;
257 case PIPE_CMD_ENV:
258 args->env = va_arg(ap, char **);
259 break;
260 case PIPE_CMD_EXPORT:
261 args->export = va_arg(ap, char **);
262 break;
263 case PIPE_CMD_SHELL:
264 args->shell = va_arg(ap, char *);
265 break;
266 case PIPE_CMD_CWD:
267 args->cwd = va_arg(ap, char *);
268 break;
269 case PIPE_CMD_CHROOT:
270 args->chroot = va_arg(ap, char *);
271 break;
272 default:
273 msg_panic("%s: unknown key: %d", myname, key);
276 if (args->command == 0 && args->argv == 0)
277 msg_panic("%s: missing PIPE_CMD_ARGV or PIPE_CMD_COMMAND", myname);
278 if (args->uid == 0)
279 msg_panic("%s: privileged uid", myname);
280 if (args->gid == 0)
281 msg_panic("%s: privileged gid", myname);
284 /* pipe_command_write - write to command with time limit */
286 static ssize_t pipe_command_write(int fd, void *buf, size_t len,
287 int unused_timeout,
288 void *unused_context)
290 int maxtime = (pipe_command_timeout == 0) ? pipe_command_maxtime : 0;
291 const char *myname = "pipe_command_write";
294 * Don't wait when all available time was already used up.
296 if (write_wait(fd, maxtime) < 0) {
297 if (pipe_command_timeout == 0) {
298 msg_warn("%s: write time limit exceeded", myname);
299 pipe_command_timeout = 1;
301 return (0);
302 } else {
303 return (write(fd, buf, len));
307 /* pipe_command_read - read from command with time limit */
309 static ssize_t pipe_command_read(int fd, void *buf, ssize_t len,
310 int unused_timeout,
311 void *unused_context)
313 int maxtime = (pipe_command_timeout == 0) ? pipe_command_maxtime : 0;
314 const char *myname = "pipe_command_read";
317 * Don't wait when all available time was already used up.
319 if (read_wait(fd, maxtime) < 0) {
320 if (pipe_command_timeout == 0) {
321 msg_warn("%s: read time limit exceeded", myname);
322 pipe_command_timeout = 1;
324 return (0);
325 } else {
326 return (read(fd, buf, len));
330 /* kill_command - terminate command forcibly */
332 static void kill_command(pid_t pid, int sig, uid_t kill_uid, gid_t kill_gid)
334 uid_t saved_euid = geteuid();
335 gid_t saved_egid = getegid();
338 * Switch privileges to that of the child process. Terminate the child
339 * and its offspring.
341 set_eugid(kill_uid, kill_gid);
342 if (kill(-pid, sig) < 0 && kill(pid, sig) < 0)
343 msg_warn("cannot kill process (group) %lu: %m",
344 (unsigned long) pid);
345 set_eugid(saved_euid, saved_egid);
348 /* pipe_command_wait_or_kill - wait for command with time limit, or kill it */
350 static int pipe_command_wait_or_kill(pid_t pid, WAIT_STATUS_T *statusp, int sig,
351 uid_t kill_uid, gid_t kill_gid)
353 int maxtime = (pipe_command_timeout == 0) ? pipe_command_maxtime : 1;
354 const char *myname = "pipe_command_wait_or_kill";
355 int n;
358 * Don't wait when all available time was already used up.
360 if ((n = timed_waitpid(pid, statusp, 0, maxtime)) < 0 && errno == ETIMEDOUT) {
361 if (pipe_command_timeout == 0) {
362 msg_warn("%s: child wait time limit exceeded", myname);
363 pipe_command_timeout = 1;
365 kill_command(pid, sig, kill_uid, kill_gid);
366 n = waitpid(pid, statusp, 0);
368 return (n);
371 /* pipe_child_cleanup - child fatal error handler */
373 static void pipe_child_cleanup(void)
377 * WARNING: don't place code here. This code may run as mail_owner, as
378 * root, or as the user/group specified with the "user" attribute. The
379 * only safe action is to terminate.
381 * Future proofing. If you need exit() here then you broke Postfix.
383 _exit(EX_TEMPFAIL);
386 /* pipe_command - execute command with extreme prejudice */
388 int pipe_command(VSTREAM *src, DSN_BUF *why,...)
390 const char *myname = "pipe_command";
391 va_list ap;
392 VSTREAM *cmd_in_stream;
393 VSTREAM *cmd_out_stream;
394 char log_buf[VSTREAM_BUFSIZE + 1];
395 int log_len;
396 pid_t pid;
397 int write_status;
398 int write_errno;
399 WAIT_STATUS_T wait_status;
400 int cmd_in_pipe[2];
401 int cmd_out_pipe[2];
402 struct pipe_args args;
403 char **cpp;
404 ARGV *argv;
405 DSN_SPLIT dp;
406 const SYS_EXITS_DETAIL *sp;
409 * Process the variadic argument list. This also does sanity checks on
410 * what data the caller is passing to us.
412 va_start(ap, why);
413 get_pipe_args(&args, ap);
414 va_end(ap);
417 * For convenience...
419 if (args.command == 0)
420 args.command = args.argv[0];
423 * Set up pipes that connect us to the command input and output streams.
424 * We're using a rather disgusting hack to capture command output: set
425 * the output to non-blocking mode, and don't attempt to read the output
426 * until AFTER the process has terminated. The rationale for this is: 1)
427 * the command output will be used only when delivery fails; 2) the
428 * amount of output is expected to be small; 3) the output can be
429 * truncated without too much loss. I could even argue that truncating
430 * the amount of diagnostic output is a good thing to do, but I won't go
431 * that far.
433 * Turn on non-blocking writes to the child process so that we can enforce
434 * timeouts after partial writes.
436 * XXX Too much trouble with different systems returning weird write()
437 * results when a pipe is writable.
439 if (pipe(cmd_in_pipe) < 0 || pipe(cmd_out_pipe) < 0)
440 msg_fatal("%s: pipe: %m", myname);
441 non_blocking(cmd_out_pipe[1], NON_BLOCKING);
442 #if 0
443 non_blocking(cmd_in_pipe[1], NON_BLOCKING);
444 #endif
447 * Spawn off a child process and irrevocably change privilege to the
448 * user. This includes revoking all rights on open files (via the close
449 * on exec flag). If we cannot run the command now, try again some time
450 * later.
452 switch (pid = fork()) {
455 * Error. Instead of trying again right now, back off, give the
456 * system a chance to recover, and try again later.
458 case -1:
459 msg_warn("fork: %m");
460 dsb_unix(why, "4.3.0", sys_exits_detail(EX_OSERR)->text,
461 "Delivery failed: %m");
462 return (PIPE_STAT_DEFER);
465 * Child. Run the child in a separate process group so that the
466 * parent can kill not just the child but also its offspring.
468 * Redirect fatal exits to our own fatal exit handler (never leave the
469 * parent's handler enabled :-) so we can replace random exit status
470 * codes by EX_TEMPFAIL.
472 case 0:
473 (void) msg_cleanup(pipe_child_cleanup);
476 * In order to chroot it is necessary to switch euid back to root.
477 * Right after chroot we call set_ugid() so all privileges will be
478 * dropped again.
480 * XXX For consistency we use chroot_uid() to change root+current
481 * directory. However, we must not use chroot_uid() to change process
482 * privileges (assuming a version that accepts numeric privileges).
483 * That would create a maintenance problem, because we would have two
484 * different code paths to set the external command's privileges.
486 if (args.chroot) {
487 seteuid(0);
488 chroot_uid(args.chroot, (char *) 0);
492 * XXX If we put code before the set_ugid() call, then the code that
493 * changes root directory must switch back to the mail_owner UID,
494 * otherwise we'd be running with root privileges.
496 set_ugid(args.uid, args.gid);
497 if (setsid() < 0)
498 msg_warn("setsid failed: %m");
501 * Pipe plumbing.
503 close(cmd_in_pipe[1]);
504 close(cmd_out_pipe[0]);
505 if (DUP2(cmd_in_pipe[0], STDIN_FILENO) < 0
506 || DUP2(cmd_out_pipe[1], STDOUT_FILENO) < 0
507 || DUP2(cmd_out_pipe[1], STDERR_FILENO) < 0)
508 msg_fatal("%s: dup2: %m", myname);
509 close(cmd_in_pipe[0]);
510 close(cmd_out_pipe[1]);
513 * Working directory plumbing.
515 if (args.cwd && chdir(args.cwd) < 0)
516 msg_fatal("cannot change directory to \"%s\" for uid=%lu gid=%lu: %m",
517 args.cwd, (unsigned long) args.uid,
518 (unsigned long) args.gid);
521 * Environment plumbing. Always reset the command search path. XXX
522 * That should probably be done by clean_env().
524 if (args.export)
525 clean_env(args.export);
526 if (setenv("PATH", _PATH_DEFPATH, 1))
527 msg_fatal("%s: setenv: %m", myname);
528 if (args.env)
529 for (cpp = args.env; *cpp; cpp += 2)
530 if (setenv(cpp[0], cpp[1], 1))
531 msg_fatal("setenv: %m");
534 * Process plumbing. If possible, avoid running a shell.
536 * As a safety for buggy libraries, we close the syslog socket.
537 * Otherwise we could leak a file descriptor that was created by a
538 * privileged process.
540 * XXX To avoid losing fatal error messages we open a VSTREAM and
541 * capture the output in the parent process.
543 closelog();
544 msg_vstream_init(var_procname, VSTREAM_ERR);
545 if (args.argv) {
546 execvp(args.argv[0], args.argv);
547 msg_fatal("%s: execvp %s: %m", myname, args.argv[0]);
548 } else if (args.shell && *args.shell) {
549 argv = argv_split(args.shell, " \t\r\n");
550 argv_add(argv, args.command, (char *) 0);
551 argv_terminate(argv);
552 execvp(argv->argv[0], argv->argv);
553 msg_fatal("%s: execvp %s: %m", myname, argv->argv[0]);
554 } else {
555 exec_command(args.command);
557 /* NOTREACHED */
560 * Parent.
562 default:
563 close(cmd_in_pipe[0]);
564 close(cmd_out_pipe[1]);
566 cmd_in_stream = vstream_fdopen(cmd_in_pipe[1], O_WRONLY);
567 cmd_out_stream = vstream_fdopen(cmd_out_pipe[0], O_RDONLY);
570 * Give the command a limited amount of time to run, by enforcing
571 * timeouts on all I/O from and to it.
573 vstream_control(cmd_in_stream,
574 VSTREAM_CTL_WRITE_FN, pipe_command_write,
575 VSTREAM_CTL_END);
576 vstream_control(cmd_out_stream,
577 VSTREAM_CTL_READ_FN, pipe_command_read,
578 VSTREAM_CTL_END);
579 pipe_command_timeout = 0;
582 * Pipe the message into the command. Examine the error report only
583 * if we can't recognize a more specific error from the command exit
584 * status or from the command output.
586 write_status = mail_copy(args.sender, args.orig_rcpt,
587 args.delivered, src,
588 cmd_in_stream, args.flags,
589 args.eol, why);
590 write_errno = errno;
593 * Capture a limited amount of command output, for inclusion in a
594 * bounce message. Turn tabs and newlines into whitespace, and
595 * replace other non-printable characters by underscore.
597 log_len = vstream_fread(cmd_out_stream, log_buf, sizeof(log_buf) - 1);
598 (void) vstream_fclose(cmd_out_stream);
599 log_buf[log_len] = 0;
600 translit(log_buf, "\t\n", " ");
601 printable(log_buf, '_');
604 * Just because the child closes its output streams, don't assume
605 * that it will terminate. Instead, be prepared for the situation
606 * that the child does not terminate, even when the parent
607 * experiences no read/write timeout. Make sure that the child
608 * terminates before the parent attempts to retrieve its exit status,
609 * otherwise the parent could become stuck, and the mail system would
610 * eventually run out of delivery agents. Do a thorough job, and kill
611 * not just the child process but also its offspring.
613 if (pipe_command_timeout)
614 kill_command(pid, SIGKILL, args.uid, args.gid);
615 if (pipe_command_wait_or_kill(pid, &wait_status, SIGKILL,
616 args.uid, args.gid) < 0)
617 msg_fatal("wait: %m");
618 if (pipe_command_timeout) {
619 dsb_unix(why, "5.3.0", log_len ?
620 log_buf : sys_exits_detail(EX_SOFTWARE)->text,
621 "Command time limit exceeded: \"%s\"%s%s",
622 args.command,
623 log_len ? ". Command output: " : "", log_buf);
624 return (PIPE_STAT_BOUNCE);
628 * Command exits. Give special treatment to sendmail style exit
629 * status codes.
631 if (!NORMAL_EXIT_STATUS(wait_status)) {
632 if (WIFSIGNALED(wait_status)) {
633 dsb_unix(why, "5.3.0", log_len ?
634 log_buf : sys_exits_detail(EX_SOFTWARE)->text,
635 "Command died with signal %d: \"%s\"%s%s",
636 WTERMSIG(wait_status), args.command,
637 log_len ? ". Command output: " : "", log_buf);
638 return (PIPE_STAT_DEFER);
640 /* Use "D.S.N text" command output. XXX What diagnostic code? */
641 else if (dsn_valid(log_buf) > 0) {
642 dsn_split(&dp, "5.3.0", log_buf);
643 dsb_unix(why, DSN_STATUS(dp.dsn), dp.text, "%s", dp.text);
644 return (DSN_CLASS(dp.dsn) == '4' ?
645 PIPE_STAT_DEFER : PIPE_STAT_BOUNCE);
647 /* Use <sysexits.h> compatible exit status. */
648 else if (SYS_EXITS_CODE(WEXITSTATUS(wait_status))) {
649 sp = sys_exits_detail(WEXITSTATUS(wait_status));
650 dsb_unix(why, sp->dsn,
651 log_len ? log_buf : sp->text, "%s%s%s", sp->text,
652 log_len ? ". Command output: " : "", log_buf);
653 return (sp->dsn[0] == '4' ?
654 PIPE_STAT_DEFER : PIPE_STAT_BOUNCE);
658 * No "D.S.N text" or <sysexits.h> compatible status. Fake it.
660 else {
661 sp = sys_exits_detail(WEXITSTATUS(wait_status));
662 dsb_unix(why, sp->dsn,
663 log_len ? log_buf : sp->text,
664 "Command died with status %d: \"%s\"%s%s",
665 WEXITSTATUS(wait_status), args.command,
666 log_len ? ". Command output: " : "", log_buf);
667 return (PIPE_STAT_BOUNCE);
669 } else if (write_status &
670 MAIL_COPY_STAT_CORRUPT) {
671 return (PIPE_STAT_CORRUPT);
672 } else if (write_status && write_errno != EPIPE) {
673 vstring_prepend(why->reason, "Command failed: ",
674 sizeof("Command failed: ") - 1);
675 vstring_sprintf_append(why->reason, ": \"%s\"", args.command);
676 return (PIPE_STAT_BOUNCE);
677 } else {
678 return (PIPE_STAT_OK);