Handle short writes on logfiles.
[exim.git] / src / src / log.c
index 231db6581db9c95bb0e7018b378eec83753bca31..bfd1fef86efc15c1a447cbb18556b5e53231cf98 100644 (file)
@@ -1,5 +1,3 @@
-/* $Cambridge: exim/src/src/log.c,v 1.15 2010/06/06 00:27:52 pdp Exp $ */
-
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
@@ -482,6 +480,11 @@ log, which can happen if a disk gets full or a file gets too large or whatever.
 We try to save the relevant message in the panic_save buffer before crashing
 out.
 
+The potential invoker should probably not call us for EINTR -1 writes.  But
+otherwise, short writes are bad as we don't do non-blocking writes to fds
+subject to flow control.  (If we do, that's new and the logic of this should
+be reconsidered).
+
 Arguments:
   name      the name of the log being written
   length    the string length being written
@@ -510,6 +513,49 @@ log_write(0, LOG_PANIC_DIE, "failed to write to %s: length=%d result=%d "
 
 
 
+/*************************************************
+*     Write to an fd, retrying after signals     *
+*************************************************/
+
+/* Basic write to fd for logs, handling EINTR.
+
+Arguments:
+  fd        the fd to write to
+  buf       the string to write
+  length    the string length being written
+
+Returns:
+  length actually written, persisting an errno from write()
+*/
+ssize_t
+write_to_fd_buf(int fd, uschar *buf, size_t length)
+{
+ssize_t wrote;
+size_t total_written = 0;
+uschar *p = buf;
+size_t left = length;
+
+while (1)
+  {
+  wrote = write(fd, p, left);
+  if (wrote == (ssize_t)-1)
+    {
+    if (errno == EINTR) continue;
+    return wrote;
+    }
+  total_written += wrote;
+  if (wrote == left)
+    break;
+  else
+    {
+    p += wrote;
+    left -= wrote;
+    }
+  }
+return total_written;
+}
+
+
 /*************************************************
 *            Write message to log file           *
 *************************************************/
@@ -571,8 +617,9 @@ void
 log_write(unsigned int selector, int flags, const char *format, ...)
 {
 uschar *ptr;
-int length, rc;
+int length;
 int paniclogfd;
+ssize_t written_len;
 va_list ap;
 
 /* If panic_recurseflag is set, we have failed to open the panic log. This is
@@ -715,7 +762,7 @@ DEBUG(D_any|D_v)
       }
     }
 
-  sprintf(CS ptr, "%s%s%s%s%s\n  ",
+  sprintf(CS ptr, "%s%s%s%s\n  ",
     ((flags & LOG_MAIN) != 0)?    " MAIN"   : "",
     ((flags & LOG_PANIC) != 0)?   " PANIC"  : "",
     ((flags & LOG_PANIC_DIE) == LOG_PANIC_DIE)? " DIE" : "",
@@ -888,9 +935,10 @@ if ((flags & LOG_MAIN) != 0 &&
 
     /* Failing to write to the log is disastrous */
 
-    if ((rc = write(mainlogfd, log_buffer, length)) != length)
+    written_len = write_to_fd_buf(mainlogfd, log_buffer, length);
+    if (written_len != length)
       {
-      log_write_failed(US"main log", length, rc);
+      log_write_failed(US"main log", length, written_len);
       /* That function does not return */
       }
     }
@@ -1010,9 +1058,10 @@ if ((flags & LOG_REJECT) != 0)
       if (fstat(rejectlogfd, &statbuf) >= 0) rejectlog_inode = statbuf.st_ino;
       }
 
-    if ((rc = write(rejectlogfd, log_buffer, length)) != length)
+    written_len = write_to_fd_buf(rejectlogfd, log_buffer, length);
+    if (written_len != length)
       {
-      log_write_failed(US"reject log", length, rc);
+      log_write_failed(US"reject log", length, written_len);
       /* That function does not return */
       }
     }
@@ -1046,12 +1095,13 @@ if ((flags & LOG_PANIC) != 0)
     if (panic_save_buffer != NULL)
       (void) write(paniclogfd, panic_save_buffer, Ustrlen(panic_save_buffer));
 
-    if ((rc = write(paniclogfd, log_buffer, length)) != length)
+    written_len = write_to_fd_buf(paniclogfd, log_buffer, length);
+    if (written_len != length)
       {
       int save_errno = errno;
       write_syslog(LOG_CRIT, log_buffer);
       sprintf(CS log_buffer, "write failed on panic log: length=%d result=%d "
-        "errno=%d (%s)", length, rc, save_errno, strerror(save_errno));
+        "errno=%d (%s)", length, (int)written_len, save_errno, strerror(save_errno));
       write_syslog(LOG_CRIT, log_buffer);
       flags |= LOG_PANIC_DIE;
       }