Merge branch 'master' of git://git.exim.org/exim
[exim.git] / src / src / transports / pipe.c
index 2464abd14a6af5b6a75b6829ca6405cf524ef542..3366a6dcfc64997e6bfd5c6233141f15fb29bf6c 100644 (file)
@@ -1,10 +1,8 @@
-/* $Cambridge: exim/src/src/transports/pipe.c,v 1.15 2010/06/05 10:04:44 pdp Exp $ */
-
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2009 */
+/* Copyright (c) University of Cambridge 1995 - 2014 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 
@@ -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,
@@ -110,7 +112,9 @@ 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 */
@@ -185,7 +189,7 @@ if (ob->permit_coredump)
     if (errno != ENOSYS && errno != ENOTSUP)
 #endif
       log_write(0, LOG_MAIN,
-          "delivery setrlimit(RLIMIT_CORE, RLIMI_INFINITY) failed: %s",
+          "delivery setrlimit(RLIMIT_CORE, RLIM_INFINITY) failed: %s",
           strerror(errno));
     }
   }
@@ -568,10 +572,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
   {
@@ -580,9 +595,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",
@@ -748,14 +766,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;
       }
@@ -960,11 +983,20 @@ if ((rc = child_close(pid, timeout)) != 0)
   /* 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 "