X-Git-Url: https://git.exim.org/users/jgh/exim.git/blobdiff_plain/929ba01ccb7fafbe89e4fa60e93ab2b5f4aab1df..bfe645c1570343d3adca657ab67998e122ca8792:/src/src/transports/pipe.c diff --git a/src/src/transports/pipe.c b/src/src/transports/pipe.c index 97d69db0f..eaf04d150 100644 --- a/src/src/transports/pipe.c +++ b/src/src/transports/pipe.c @@ -1,17 +1,15 @@ -/* $Cambridge: exim/src/src/transports/pipe.c,v 1.10 2006/02/21 16:24:20 ph10 Exp $ */ - /************************************************* * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2006 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" #include "pipe.h" -#ifdef HAVE_LOGIN_CAP +#ifdef HAVE_SETCLASSRESOURCES #include #endif @@ -39,8 +37,12 @@ optionlist pipe_transport_options[] = { (void *)offsetof(pipe_transport_options_block, environment) }, { "escape_string", opt_stringptr, (void *)offsetof(pipe_transport_options_block, escape_string) }, + { "force_command", opt_bool, + (void *)offsetof(pipe_transport_options_block, force_command) }, { "freeze_exec_fail", opt_bool, (void *)offsetof(pipe_transport_options_block, freeze_exec_fail) }, + { "freeze_signal", opt_bool, + (void *)offsetof(pipe_transport_options_block, freeze_signal) }, { "ignore_status", opt_bool, (void *)offsetof(pipe_transport_options_block, ignore_status) }, { "log_defer_output", opt_bool | opt_public, @@ -57,6 +59,8 @@ optionlist pipe_transport_options[] = { (void *)offsetof(pipe_transport_options_block, message_suffix) }, { "path", opt_stringptr, (void *)offsetof(pipe_transport_options_block, path) }, + { "permit_coredump", opt_bool, + (void *)offsetof(pipe_transport_options_block, permit_coredump) }, { "pipe_as_creator", opt_bool | opt_public, (void *)offsetof(transport_instance, deliver_as_creator) }, { "restrict_to_path", opt_bool, @@ -75,7 +79,7 @@ optionlist pipe_transport_options[] = { (void *)offsetof(pipe_transport_options_block, umask) }, { "use_bsmtp", opt_bool, (void *)offsetof(pipe_transport_options_block, use_bsmtp) }, - #ifdef HAVE_LOGIN_CAP + #ifdef HAVE_SETCLASSRESOURCES { "use_classresources", opt_bool, (void *)offsetof(pipe_transport_options_block, use_classresources) }, #endif @@ -108,8 +112,11 @@ pipe_transport_options_block pipe_transport_option_defaults = { 20480, /* max_output */ 60*60, /* timeout */ 0, /* options */ + FALSE, /* force_command */ FALSE, /* freeze_exec_fail */ + FALSE, /* freeze_signal */ FALSE, /* ignore_status */ + FALSE, /* permit_coredump */ FALSE, /* restrict_to_path */ FALSE, /* timeout_defer */ FALSE, /* use_shell */ @@ -127,7 +134,7 @@ pipe_transport_options_block pipe_transport_option_defaults = { /* Called for each delivery in the privileged state, just before the uid/gid are changed and the main entry point is called. In a system that supports the login_cap facilities, this function is used to set the class resource limits -for the user. +for the user. It may also re-enable coredumps. Arguments: tblock points to the transport instance @@ -154,7 +161,7 @@ gid = gid; errmsg = errmsg; ob = ob; -#ifdef HAVE_LOGIN_CAP +#ifdef HAVE_SETCLASSRESOURCES if (ob->use_classresources) { struct passwd *pw = getpwuid(uid); @@ -170,6 +177,24 @@ if (ob->use_classresources) } #endif +#ifdef RLIMIT_CORE +if (ob->permit_coredump) + { + struct rlimit rl; + rl.rlim_cur = RLIM_INFINITY; + rl.rlim_max = RLIM_INFINITY; + if (setrlimit(RLIMIT_CORE, &rl) < 0) + { +#ifdef SETRLIMIT_NOT_SUPPORTED + if (errno != ENOSYS && errno != ENOTSUP) +#endif + log_write(0, LOG_MAIN, + "delivery setrlimit(RLIMIT_CORE, RLIM_INFINITY) failed: %s", + strerror(errno)); + } + } +#endif + return OK; } @@ -301,12 +326,12 @@ Returns: TRUE if all went well; otherwise an error will be */ static BOOL -set_up_direct_command(uschar ***argvptr, uschar *cmd, BOOL expand_arguments, - int expand_fail, address_item *addr, uschar *tname, +set_up_direct_command(const uschar ***argvptr, uschar *cmd, + BOOL expand_arguments, int expand_fail, address_item *addr, uschar *tname, pipe_transport_options_block *ob) { BOOL permitted = FALSE; -uschar **argv; +const uschar **argv; uschar buffer[64]; /* Set up "transport " to be put in any error messages, and then @@ -328,11 +353,11 @@ argv = *argvptr; if (ob->allow_commands != NULL) { int sep = 0; - uschar *s, *p; + const uschar *s; + uschar *p; uschar buffer[256]; - s = expand_string(ob->allow_commands); - if (s == NULL) + if (!(s = expand_string(ob->allow_commands))) { addr->transport_return = DEFER; addr->message = string_sprintf("failed to expand string \"%s\" " @@ -340,10 +365,8 @@ if (ob->allow_commands != NULL) return FALSE; } - while ((p = string_nextinlist(&s, &sep, buffer, sizeof(buffer))) != NULL) - { + while ((p = string_nextinlist(&s, &sep, buffer, sizeof(buffer)))) if (Ustrcmp(p, argv[0]) == 0) { permitted = TRUE; break; } - } } /* If permitted is TRUE it means the command was found in the allowed list, and @@ -382,7 +405,7 @@ if (argv[0][0] != '/') { int sep = 0; uschar *p; - uschar *listptr = ob->path; + const uschar *listptr = ob->path; uschar buffer[1024]; while ((p = string_nextinlist(&listptr, &sep, buffer, sizeof(buffer))) != NULL) @@ -428,10 +451,10 @@ Returns: TRUE if all went well; otherwise an error will be */ static BOOL -set_up_shell_command(uschar ***argvptr, uschar *cmd, BOOL expand_arguments, - int expand_fail, address_item *addr, uschar *tname) +set_up_shell_command(const uschar ***argvptr, uschar *cmd, + BOOL expand_arguments, int expand_fail, address_item *addr, uschar *tname) { -uschar **argv; +const uschar **argv; *argvptr = argv = store_get((4)*sizeof(uschar *)); @@ -526,9 +549,9 @@ pipe_transport_options_block *ob = int timeout = ob->timeout; BOOL written_ok = FALSE; BOOL expand_arguments; -uschar **argv; +const uschar **argv; uschar *envp[50]; -uschar *envlist = ob->environment; +const uschar *envlist = ob->environment; uschar *cmd, *ss; uschar *eol = (ob->use_crlf)? US"\r\n" : US"\n"; @@ -547,10 +570,21 @@ options. */ if (testflag(addr, af_pfr) && addr->local_part[0] == '|') { - cmd = addr->local_part + 1; - while (isspace(*cmd)) cmd++; - expand_arguments = testflag(addr, af_expand_pipe); - expand_fail = FAIL; + if (ob->force_command) + { + /* Enables expansion of $address_pipe into seperate arguments */ + setflag(addr, af_force_command); + cmd = ob->cmd; + expand_arguments = TRUE; + expand_fail = PANIC; + } + else + { + cmd = addr->local_part + 1; + while (isspace(*cmd)) cmd++; + expand_arguments = testflag(addr, af_expand_pipe); + expand_fail = FAIL; + } } else { @@ -559,9 +593,12 @@ else expand_fail = PANIC; } -/* If no command has been supplied, we are in trouble. */ +/* If no command has been supplied, we are in trouble. + * We also check for an empty string since it may be + * coming from addr->local_part[0] == '|' + */ -if (cmd == NULL) +if (cmd == NULL || *cmd == '\0') { addr->transport_return = DEFER; addr->message = string_sprintf("no command specified for %s transport", @@ -630,9 +667,9 @@ else if (timezone_string != NULL && timezone_string[0] != 0) /* Add any requested items */ -if (envlist != NULL) +if (envlist) { - envlist = expand_string(envlist); + envlist = expand_cstring(envlist); if (envlist == NULL) { addr->transport_return = DEFER; @@ -690,7 +727,7 @@ reading of the output pipe. */ uid/gid and current directory. Request that the new process be a process group leader, so we can kill it and all its children on a timeout. */ -if ((pid = child_open(argv, envp, ob->umask, &fd_in, &fd_out, TRUE)) < 0) +if ((pid = child_open(USS argv, envp, ob->umask, &fd_in, &fd_out, TRUE)) < 0) { addr->transport_return = DEFER; addr->message = string_sprintf( @@ -727,14 +764,19 @@ if (outpid == 0) while ((rc = read(fd_out, big_buffer, big_buffer_size)) > 0) { if (addr->return_file >= 0) - write(addr->return_file, big_buffer, rc); + if(write(addr->return_file, big_buffer, rc) != rc) + DEBUG(D_transport) debug_printf("Problem writing to return_file\n"); count += rc; if (count > ob->max_output) { - uschar *message = US"\n\n*** Too much output - remainder discarded ***\n"; DEBUG(D_transport) debug_printf("Too much output from pipe - killed\n"); if (addr->return_file >= 0) - write(addr->return_file, message, Ustrlen(message)); + { + uschar *message = US"\n\n*** Too much output - remainder discarded ***\n"; + rc = Ustrlen(message); + if(write(addr->return_file, message, rc) != rc) + DEBUG(D_transport) debug_printf("Problem writing to return_file\n"); + } killpg(pid, SIGKILL); break; } @@ -924,14 +966,35 @@ if ((rc = child_close(pid, timeout)) != 0) "transport: %s%s", tblock->name, strerror(errno), tmsg); } + /* Since the transport_filter timed out we assume it has sent the child process + a malformed or incomplete data stream. Kill off the child process + and prevent checking its exit status as it will has probably exited in error. + This prevents the transport_filter timeout message from getting overwritten + by the exit error which is not the cause of the problem. */ + + else if (transport_filter_timed_out) + { + killpg(pid, SIGKILL); + kill(outpid, SIGKILL); + } + /* Either the process completed, but yielded a non-zero (necessarily positive) status, or the process was terminated by a signal (rc will contain the negation of the signal number). Treat killing by signal as failure unless - status is being ignored. */ + status is being ignored. By default, the message is bounced back, unless + freeze_signal is set, in which case it is frozen instead. */ else if (rc < 0) { - if (!ob->ignore_status) + if (ob->freeze_signal) + { + addr->transport_return = DEFER; + addr->special_action = SPECIAL_FREEZE; + addr->message = string_sprintf("Child process of %s transport (running " + "command \"%s\") was terminated by signal %d (%s)%s", tblock->name, cmd, + -rc, os_strsignal(-rc), tmsg); + } + else if (!ob->ignore_status) { addr->transport_return = FAIL; addr->message = string_sprintf("Child process of %s transport (running " @@ -1008,16 +1071,14 @@ if ((rc = child_close(pid, timeout)) != 0) else { - uschar *s = ob->temp_errors; + const uschar *s = ob->temp_errors; uschar *p; uschar buffer[64]; int sep = 0; addr->transport_return = FAIL; - while ((p = string_nextinlist(&s,&sep,buffer,sizeof(buffer))) != NULL) - { + while ((p = string_nextinlist(&s,&sep,buffer,sizeof(buffer)))) if (rc == Uatoi(p)) { addr->transport_return = DEFER; break; } - } } /* Ensure the message contains the expanded command and arguments. This