Add comments as to why smtp_log_no_mail() is not called when
[exim.git] / src / src / daemon.c
index 76e970eb346b3ffd80dd57a766b7c0069a726a86..1e639453cd1fc51fe39c520ee37fdf292d9da776 100644 (file)
@@ -1,10 +1,10 @@
-/* $Cambridge: exim/src/src/daemon.c,v 1.9 2005/02/17 11:58:25 ph10 Exp $ */
+/* $Cambridge: exim/src/src/daemon.c,v 1.21 2007/01/17 11:29:39 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2005 */
+/* Copyright (c) University of Cambridge 1995 - 2007 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Functions concerned with running Exim as a daemon */
@@ -142,7 +142,7 @@ handle_smtp_call(int *listen_sockets, int listen_socket_count,
 {
 pid_t pid;
 union sockaddr_46 interface_sockaddr;
-SOCKLEN_T ifsize = sizeof(interface_sockaddr);
+EXIM_SOCKLEN_T ifsize = sizeof(interface_sockaddr);
 int dup_accept_socket = -1;
 int max_for_this_host = 0;
 int wfsize = 0;
@@ -186,13 +186,14 @@ if (smtp_in == NULL)
   goto ERROR_RETURN;
   }
 
-/* Get the data for the local interface address. */
+/* Get the data for the local interface address. Panic for most errors, but
+"connection reset by peer" just means the connection went away. */
 
 if (getsockname(accept_socket, (struct sockaddr *)(&interface_sockaddr),
      &ifsize) < 0)
   {
-  log_write(0, LOG_MAIN|LOG_PANIC, "getsockname() failed: %s",
-    strerror(errno));
+  log_write(0, LOG_MAIN | ((errno == ECONNRESET)? 0 : LOG_PANIC),
+    "getsockname() failed: %s", strerror(errno));
   smtp_printf("421 Local problem: getsockname() failed; please try again later\r\n");
   goto ERROR_RETURN;
   }
@@ -419,7 +420,7 @@ if (pid == 0)
   extensive comment before the reception loop in exim.c for a fuller
   explanation of this logic. */
 
-  for (i = 0; i < listen_socket_count; i++) close(listen_sockets[i]);
+  for (i = 0; i < listen_socket_count; i++) (void)close(listen_sockets[i]);
 
   #ifdef SA_NOCLDWAIT
   act.sa_handler = SIG_IGN;
@@ -467,7 +468,12 @@ if (pid == 0)
   /* Handle the start of the SMTP session, then loop, accepting incoming
   messages from the SMTP connection. The end will come at the QUIT command,
   when smtp_setup_msg() returns 0. A break in the connection causes the
-  process to die (see accept.c). */
+  process to die (see accept.c).
+
+  NOTE: We do *not* call smtp_log_no_mail() if smtp_start_session() fails,
+  because a log line has already been written for all its failure exists
+  (usually "connection refused: <reason>") and writing another one is
+  unnecessary clutter. */
 
   if (!smtp_start_session())
     {
@@ -499,6 +505,7 @@ if (pid == 0)
       if (!ok)                            /* Connection was dropped */
         {
         mac_smtp_fflush();
+        smtp_log_no_mail();               /* Log no mail if configured */
         _exit(EXIT_SUCCESS);
         }
       if (message_id[0] == 0) continue;   /* No message was accepted */
@@ -507,6 +514,7 @@ if (pid == 0)
       {
       mac_smtp_fflush();
       search_tidyup();
+      smtp_log_no_mail();                 /* Log no mail if configured */
       _exit((rc == 0)? EXIT_SUCCESS : EXIT_FAILURE);
       }
 
@@ -603,8 +611,8 @@ if (pid == 0)
 
       if ((dpid = fork()) == 0)
         {
-        fclose(smtp_in);
-        fclose(smtp_out);
+        (void)fclose(smtp_in);
+        (void)fclose(smtp_out);
 
         /* Don't ever molest the parent's SSL connection, but do clean up
         the data structures if necessary. */
@@ -680,27 +688,28 @@ ERROR_RETURN:
 /* Close the streams associated with the socket which will also close the
 socket fds in this process. We can't do anything if fclose() fails, but
 logging brings it to someone's attention. However, "connection reset by peer"
-isn't really a problem, so skip that one. If the streams don't exist, something
-went wrong while setting things up. Make sure the socket descriptors are
-closed, in order to drop the connection. */
+isn't really a problem, so skip that one. On Solaris, a dropped connection can
+manifest itself as a broken pipe, so drop that one too. If the streams don't
+exist, something went wrong while setting things up. Make sure the socket
+descriptors are closed, in order to drop the connection. */
 
 if (smtp_out != NULL)
   {
-  if (fclose(smtp_out) != 0 && errno != ECONNRESET)
+  if (fclose(smtp_out) != 0 && errno != ECONNRESET && errno != EPIPE)
     log_write(0, LOG_MAIN|LOG_PANIC, "daemon: fclose(smtp_out) failed: %s",
       strerror(errno));
   smtp_out = NULL;
   }
-else close(accept_socket);
+else (void)close(accept_socket);
 
 if (smtp_in != NULL)
   {
-  if (fclose(smtp_in) != 0 && errno != ECONNRESET)
+  if (fclose(smtp_in) != 0 && errno != ECONNRESET && errno != EPIPE)
     log_write(0, LOG_MAIN|LOG_PANIC, "daemon: fclose(smtp_in) failed: %s",
       strerror(errno));
   smtp_in = NULL;
   }
-else close(dup_accept_socket);
+else (void)close(dup_accept_socket);
 
 /* Release any store used in this process, including the store used for holding
 the incoming host address and an expanded active_hostname. */
@@ -1208,9 +1217,9 @@ if (background_daemon)
   {
   log_close_all();    /* Just in case anything was logged earlier */
   search_tidyup();    /* Just in case any were used in reading the config. */
-  close(0);           /* Get rid of stdin/stdout/stderr */
-  close(1);
-  close(2);
+  (void)close(0);           /* Get rid of stdin/stdout/stderr */
+  (void)close(1);
+  (void)close(2);
   exim_nullstd();     /* Connect stdin/stdout/stderr to /dev/null */
   log_stderr = NULL;  /* So no attempt to copy paniclog output */
 
@@ -1249,7 +1258,6 @@ if (daemon_listen)
     {
     BOOL wildcard;
     ip_address_item *ipa2;
-    int retries = 9;
     int af;
 
     if (Ustrchr(ipa->address, ':') != NULL)
@@ -1321,19 +1329,22 @@ if (daemon_listen)
         {
         DEBUG(D_any) debug_printf("wildcard IPv4 bind() failed after IPv6 "
           "listen() success; EADDRINUSE ignored\n");
-        close(listen_sockets[sk]);
+        (void)close(listen_sockets[sk]);
         goto SKIP_SOCKET;
         }
       msg = US strerror(errno);
       addr = wildcard? ((af == AF_INET6)? US"(any IPv6)" : US"(any IPv4)") :
         ipa->address;
-      if (retries-- <= 0)
+      if (daemon_startup_retries <= 0)
         log_write(0, LOG_MAIN|LOG_PANIC_DIE,
           "socket bind() to port %d for address %s failed: %s: "
           "daemon abandoned", ipa->port, addr, msg);
       log_write(0, LOG_MAIN, "socket bind() to port %d for address %s "
-        "failed: %s: waiting before trying again", ipa->port, addr, msg);
-      sleep(30);
+        "failed: %s: waiting %s before trying again (%d more %s)",
+        ipa->port, addr, msg, readconf_printtime(daemon_startup_sleep),
+        daemon_startup_retries, (daemon_startup_retries > 1)? "tries" : "try");
+      daemon_startup_retries--;
+      sleep(daemon_startup_sleep);
       }
 
     DEBUG(D_any)
@@ -1364,7 +1375,7 @@ if (daemon_listen)
 
     DEBUG(D_any) debug_printf("wildcard IPv4 listen() failed after IPv6 "
       "listen() success; EADDRINUSE ignored\n");
-    close(listen_sockets[sk]);
+    (void)close(listen_sockets[sk]);
 
     /* Come here if there has been a problem with the socket which we
     are going to ignore. We remove the address from the chain, and back up the
@@ -1412,12 +1423,11 @@ if (running_in_test_harness || write_pid)
   if (pid_file_path[0] == 0)
     pid_file_path = string_sprintf("%s/exim-daemon.pid", spool_directory);
 
-  f = Ufopen(pid_file_path, "wb");
+  f = modefopen(pid_file_path, "wb", 0644);
   if (f != NULL)
     {
-    fprintf(f, "%d\n", (int)getpid());
-    fchmod(fileno(f), 0644);
-    fclose(f);
+    (void)fprintf(f, "%d\n", (int)getpid());
+    (void)fclose(f);
     DEBUG(D_any) debug_printf("pid written to %s\n", pid_file_path);
     }
   else
@@ -1578,7 +1588,7 @@ for (;;)
   struct sockaddr_in accepted;
   #endif
 
-  SOCKLEN_T len = sizeof(accepted);
+  EXIM_SOCKLEN_T len = sizeof(accepted);
   pid_t pid;
 
   /* This code is placed first in the loop, so that it gets obeyed at the
@@ -1611,7 +1621,8 @@ for (;;)
 
         /* Close any open listening sockets in the child */
 
-        for (sk = 0; sk < listen_socket_count; sk++) close(listen_sockets[sk]);
+        for (sk = 0; sk < listen_socket_count; sk++)
+          (void)close(listen_sockets[sk]);
 
         /* Reset SIGHUP and SIGCHLD in the child in both cases. */
 
@@ -1625,6 +1636,8 @@ for (;;)
           {
           uschar opt[8];
           uschar *p = opt;
+          uschar *extra[4];
+          int extracount = 1;
 
           signal(SIGALRM, SIG_DFL);
           *p++ = '-';
@@ -1635,8 +1648,29 @@ for (;;)
           if (deliver_force_thaw) *p++ = 'f';
           if (queue_run_local) *p++ = 'l';
           *p = 0;
+          extra[0] = opt;
+
+          /* If -R or -S were on the original command line, ensure they get
+          passed on. */
+
+          if (deliver_selectstring != NULL)
+            {
+            extra[extracount++] = deliver_selectstring_regex? US"-Rr" : US"-R";
+            extra[extracount++] = deliver_selectstring;
+            }
+
+          if (deliver_selectstring_sender != NULL)
+            {
+            extra[extracount++] = deliver_selectstring_sender_regex?
+              US"-Sr" : US"-S";
+            extra[extracount++] = deliver_selectstring_sender;
+            }
+
+          /* Overlay this process with a new execution. */
+
+          (void)child_exec_exim(CEE_EXEC_PANIC, FALSE, NULL, TRUE, extracount,
+            extra[0], extra[1], extra[2], extra[3], extra[4]);
 
-          (void)child_exec_exim(CEE_EXEC_PANIC, FALSE, NULL, TRUE, 1, opt);
           /* Control never returns here. */
           }
 
@@ -1855,7 +1889,8 @@ for (;;)
     int sk;
     log_write(0, LOG_MAIN, "pid %d: SIGHUP received: re-exec daemon",
       getpid());
-    for (sk = 0; sk < listen_socket_count; sk++) close(listen_sockets[sk]);
+    for (sk = 0; sk < listen_socket_count; sk++)
+      (void)close(listen_sockets[sk]);
     alarm(0);
     signal(SIGHUP, SIG_IGN);
     sighup_argv[0] = exim_path;