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