131 #include <sys/wait.h>
187 static int pipe_command_timeout;
188 static int pipe_command_maxtime;
192 static void get_pipe_args(
struct pipe_args * args, va_list ap)
194 const char *myname =
"get_pipe_args";
215 pipe_command_maxtime = -1;
223 args->
flags |= va_arg(ap,
int);
226 args->
sender = va_arg(ap,
char *);
235 args->
eol = va_arg(ap,
char *);
239 msg_panic(
"%s: got PIPE_CMD_ARGV and PIPE_CMD_COMMAND", myname);
240 args->
argv = va_arg(ap,
char **);
244 msg_panic(
"%s: got PIPE_CMD_ARGV and PIPE_CMD_COMMAND", myname);
245 args->
command = va_arg(ap,
char *);
248 args->
uid = va_arg(ap, uid_t);
251 args->
gid = va_arg(ap, gid_t);
254 pipe_command_maxtime = va_arg(ap,
int);
257 args->
env = va_arg(ap,
char **);
260 args->
export = va_arg(ap,
char **);
263 args->
shell = va_arg(ap,
char *);
266 args->
cwd = va_arg(ap,
char *);
269 args->
chroot = va_arg(ap,
char *);
272 msg_panic(
"%s: unknown key: %d", myname, key);
276 msg_panic(
"%s: missing PIPE_CMD_ARGV or PIPE_CMD_COMMAND", myname);
281 if (pipe_command_maxtime < 0)
282 msg_panic(
"%s: missing or invalid PIPE_CMD_TIME_LIMIT", myname);
287 static ssize_t pipe_command_write(
int fd,
void *buf,
size_t len,
289 void *unused_context)
291 int maxtime = (pipe_command_timeout == 0) ? pipe_command_maxtime : 0;
292 const char *myname =
"pipe_command_write";
298 if (pipe_command_timeout == 0) {
299 msg_warn(
"%s: write time limit exceeded", myname);
300 pipe_command_timeout = 1;
304 return (write(fd, buf, len));
310 static ssize_t pipe_command_read(
int fd,
void *buf,
size_t len,
312 void *unused_context)
314 int maxtime = (pipe_command_timeout == 0) ? pipe_command_maxtime : 0;
315 const char *myname =
"pipe_command_read";
321 if (pipe_command_timeout == 0) {
322 msg_warn(
"%s: read time limit exceeded", myname);
323 pipe_command_timeout = 1;
327 return (read(fd, buf, len));
333 static void kill_command(pid_t pid,
int sig, uid_t kill_uid, gid_t kill_gid)
335 uid_t saved_euid = geteuid();
336 gid_t saved_egid = getegid();
343 if (kill(-pid, sig) < 0 && kill(pid, sig) < 0)
344 msg_warn(
"cannot kill process (group) %lu: %m",
345 (
unsigned long) pid);
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)
354 int maxtime = (pipe_command_timeout == 0) ? pipe_command_maxtime : 1;
355 const char *myname =
"pipe_command_wait_or_kill";
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;
366 kill_command(pid, sig, kill_uid, kill_gid);
367 n = waitpid(pid, statusp, 0);
374 static void pipe_child_cleanup(
void)
391 const char *myname =
"pipe_command";
414 get_pipe_args(&args, ap);
440 if (pipe(cmd_in_pipe) < 0 || pipe(cmd_out_pipe) < 0)
453 switch (pid = fork()) {
462 "Delivery failed: %m");
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)
510 close(cmd_in_pipe[0]);
511 close(cmd_out_pipe[1]);
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);
527 if (setenv(
"PATH", _PATH_DEFPATH, 1))
530 for (cpp = args.
env; *cpp; cpp += 2)
531 if (setenv(cpp[0], cpp[1], 1))
564 close(cmd_in_pipe[0]);
565 close(cmd_out_pipe[1]);
580 pipe_command_timeout = 0;
589 cmd_in_stream, args.
flags,
598 log_len =
vstream_fread(cmd_out_stream, log_buf,
sizeof(log_buf) - 1);
600 log_buf[log_len] = 0;
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,
619 if (pipe_command_timeout) {
622 "Command time limit exceeded: \"%s\"%s%s",
624 log_len ?
". Command output: " :
"", log_buf);
633 if (WIFSIGNALED(wait_status)) {
636 "Command died with signal %d: \"%s\"%s%s",
637 WTERMSIG(wait_status), args.
command,
638 log_len ?
". Command output: " :
"", log_buf);
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' ?
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);
670 }
else if (write_status &
673 }
else if (write_status && write_errno != EPIPE) {
675 sizeof(
"Command failed: ") - 1);
#define DSN_CLASS(dsn_buf)
DSN_SPLIT * dsn_split(DSN_SPLIT *dp, const char *def_dsn, const char *text)
size_t dsn_valid(const char *text)
#define CA_VSTREAM_CTL_READ_FN(v)
NORETURN msg_panic(const char *fmt,...)
void chroot_uid(const char *root_dir, const char *user_name)
void argv_add(ARGV *argvp,...)
#define DSN_STATUS(dsn_buf)
char * translit(char *, const char *, const char *)
#define PIPE_STAT_CORRUPT
#define SYS_EXITS_CODE(n)
VSTRING * vstring_strcpy(VSTRING *vp, const char *src)
void clean_env(char **preserve_list)
#define PIPE_CMD_DELIVERED
#define PIPE_CMD_TIME_LIMIT
int pipe_command(VSTREAM *src, DSN_BUF *why,...)
VSTRING * vstring_prepend(VSTRING *vp, const char *buf, ssize_t len)
VSTRING * vstring_sprintf_append(VSTRING *vp, const char *format,...)
#define PIPE_CMD_COPY_FLAGS
#define write_wait(fd, timeout)
int vstream_fclose(VSTREAM *stream)
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)
#define read_wait(fd, timeout)
void msg_warn(const char *fmt,...)
int timed_waitpid(pid_t pid, WAIT_STATUS_T *statusp, int options, int time_limit)
NORETURN msg_fatal(const char *fmt,...)
#define vstream_fread(v, b, n)
void set_eugid(uid_t euid, gid_t egid)
ARGV * argv_split(const char *, const char *)
void set_ugid(uid_t uid, gid_t gid)
DSN_BUF * dsb_unix(DSN_BUF *dsb, const char *status, const char *dtext, const char *format,...)
int non_blocking(int, int)
void msg_vstream_init(const char *name, VSTREAM *vp)
#define CA_VSTREAM_CTL_END
#define PIPE_CMD_ORIG_RCPT
#define MAIL_COPY_STAT_CORRUPT
NORETURN exec_command(const char *command)
const SYS_EXITS_DETAIL * sys_exits_detail(int code)
#define CA_VSTREAM_CTL_WRITE_FN(v)
char * printable(char *string, int replacement)
void vstream_control(VSTREAM *stream, int name,...)
MSG_CLEANUP_FN msg_cleanup(MSG_CLEANUP_FN cleanup_fn)
#define NORMAL_EXIT_STATUS(status)
VSTREAM * vstream_fdopen(int fd, int flags)
void argv_terminate(ARGV *argvp)