refactor
[exim.git] / src / src / daemon.c
index be008c3d46c80a3a40f0ad4f156fba782c2af7da..7666626ed39a7a4ee9f1833fb0e95ff3178523e3 100644 (file)
@@ -45,6 +45,9 @@ static smtp_slot *smtp_slots = NULL;
 
 static BOOL  write_pid = TRUE;
 
+#ifndef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS
+static uschar * notifier_socket_name;
+#endif
 
 
 /*************************************************
@@ -129,15 +132,14 @@ if (smtp_out) smtp_printf("421 %s\r\n", FALSE, smtp_msg);
 /*************************************************
 *************************************************/
 
-#ifndef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS
 static void
 unlink_notifier_socket(void)
 {
-uschar * s = expand_string(notifier_socket);
-DEBUG(D_any) debug_printf("unlinking notifier socket %s\n", s);
-Uunlink(s);
-}
+#ifndef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS
+DEBUG(D_any) debug_printf("unlinking notifier socket %s\n", notifier_socket_name);
+Uunlink(notifier_socket_name);
 #endif
+}
 
 
 static void
@@ -148,9 +150,6 @@ if (daemon_notifier_fd >= 0)
   {
   (void) close(daemon_notifier_fd);
   daemon_notifier_fd = -1;
-#ifndef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS
-  unlink_notifier_socket();
-#endif
   }
 
 for (int i = 0; i < listen_socket_count; i++) (void) close(fd_polls[i].fd);
@@ -1105,9 +1104,7 @@ if (daemon_notifier_fd >= 0)
   {
   close(daemon_notifier_fd);
   daemon_notifier_fd = -1;
-#ifndef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS
   unlink_notifier_socket();
-#endif
   }
 
 if (f.running_in_test_harness || write_pid)
@@ -1143,7 +1140,7 @@ return offsetof(struct sockaddr_un, sun_path) + 1
 #else
 *sname = string_sprintf("%s/p_%d", spool_directory, getpid());
 return offsetof(struct sockaddr_un, sun_path)
-  + snprintf(sup->sun_path, sizeof(sup->sun_path), "%s", sname);
+  + snprintf(sup->sun_path, sizeof(sup->sun_path), "%s", CS *sname);
 #endif
 }
 
@@ -1154,11 +1151,12 @@ daemon_notifier_sockname(struct sockaddr_un * sup)
 sup->sun_path[0] = 0;  /* Abstract local socket addr - Linux-specific? */
 return offsetof(struct sockaddr_un, sun_path) + 1
   + snprintf(sup->sun_path+1, sizeof(sup->sun_path)-1, "%s",
-              expand_string(notifier_socket));
+              CS expand_string(notifier_socket));
 #else
+notifier_socket_name = expand_string(notifier_socket);
 return offsetof(struct sockaddr_un, sun_path)
   + snprintf(sup->sun_path, sizeof(sup->sun_path), "%s",
-              expand_string(notifier_socket));
+              CS notifier_socket_name);
 #endif
 }
 
@@ -1171,7 +1169,7 @@ const uschar * where;
 struct sockaddr_un sa_un = {.sun_family = AF_UNIX};
 ssize_t len;
 
-if (!notifier_socket || !*notifier_socket)
+if (!f.notifier_socket_en)
   {
   DEBUG(D_any) debug_printf("-oY used so not creating notifier socket\n");
   return;
@@ -1182,6 +1180,11 @@ if (override_local_interfaces && !override_pid_file_path)
     debug_printf("-oX used without -oP so not creating notifier socket\n");
   return;
   }
+if (!notifier_socket || !*notifier_socket)
+  {
+  DEBUG(D_any) debug_printf("no name for notifier socket\n");
+  return;
+  }
 
 DEBUG(D_any) debug_printf("creating notifier socket\n");
 
@@ -1258,10 +1261,21 @@ if (sz >= sizeof(buf)) return FALSE;
 #ifdef notdef
 debug_printf("addrlen %d\n", msg.msg_namelen);
 #endif
-DEBUG(D_queue_run) debug_printf("%s from addr '%s%.*s'\n", __FUNCTION__,
-  *sa_un.sun_path ? "" : "@",
-  (int)msg.msg_namelen - (*sa_un.sun_path ? 0 : 1),
-  sa_un.sun_path + (*sa_un.sun_path ? 0 : 1));
+DEBUG(D_queue_run)
+  if (msg.msg_namelen > 0)
+    {
+    BOOL abstract = !*sa_un.sun_path;
+    char * name = sa_un.sun_path + (abstract ? 1 : 0);
+    int namelen =  (int)msg.msg_namelen - abstract ? 1 : 0;
+    if (*name)
+      debug_printf("%s from addr '%s%.*s'\n", __FUNCTION__,
+       abstract ? "@" : "",
+       namelen, name);
+    else
+      debug_printf("%s (from unknown addr)\n", __FUNCTION__);
+    }
+  else
+    debug_printf("%s (from unknown addr)\n", __FUNCTION__);
 
 /* Refuse to handle the item unless the peer has good credentials */
 #ifdef SCM_CREDENTIALS
@@ -1334,6 +1348,184 @@ return FALSE;
 
 
 
+static void
+daemon_inetd_wtimeout(time_t last_connection_time)
+{
+time_t resignal_interval = inetd_wait_timeout;
+
+if (last_connection_time == (time_t)0)
+  {
+  DEBUG(D_any)
+    debug_printf("inetd wait timeout expired, but still not seen first message, ignoring\n");
+  }
+else
+  {
+  time_t now = time(NULL);
+  if (now == (time_t)-1)
+    {
+    DEBUG(D_any) debug_printf("failed to get time: %s\n", strerror(errno));
+    }
+  else if ((now - last_connection_time) >= inetd_wait_timeout)
+    {
+    DEBUG(D_any)
+      debug_printf("inetd wait timeout %d expired, ending daemon\n",
+         inetd_wait_timeout);
+    log_write(0, LOG_MAIN, "exim %s daemon terminating, inetd wait timeout reached.\n",
+       version_string);
+    daemon_die();              /* Does not return */
+    }
+  else
+    resignal_interval -= (now - last_connection_time);
+  }
+
+sigalrm_seen = FALSE;
+ALARM(resignal_interval);
+}
+
+
+static void
+daemon_qrun(int local_queue_run_max, struct pollfd * fd_polls, int listen_socket_count)
+{
+DEBUG(D_any) debug_printf("%s received\n",
+#ifndef DISABLE_QUEUE_RAMP
+  *queuerun_msgid ? "qrun notification" :
+#endif
+  "SIGALRM");
+
+/* Do a full queue run in a child process, if required, unless we already
+have enough queue runners on the go. If we are not running as root, a
+re-exec is required. */
+
+if (  queue_interval > 0
+   && (local_queue_run_max <= 0 || queue_run_count < local_queue_run_max))
+  {
+pid_t pid;
+
+  if ((pid = exim_fork(US"queue-runner")) == 0)
+    {
+    /* Disable debugging if it's required only for the daemon process. We
+    leave the above message, because it ties up with the "child ended"
+    debugging messages. */
+
+    if (f.debug_daemon) debug_selector = 0;
+
+    /* Close any open listening sockets in the child */
+
+    close_daemon_sockets(daemon_notifier_fd,
+      fd_polls, listen_socket_count);
+
+    /* Reset SIGHUP and SIGCHLD in the child in both cases. */
+
+    signal(SIGHUP,  SIG_DFL);
+    signal(SIGCHLD, SIG_DFL);
+    signal(SIGTERM, SIG_DFL);
+    signal(SIGINT, SIG_DFL);
+
+    /* Re-exec if privilege has been given up, unless deliver_drop_
+    privilege is set. Reset SIGALRM before exec(). */
+
+    if (geteuid() != root_uid && !deliver_drop_privilege)
+      {
+      uschar opt[8];
+      uschar *p = opt;
+      uschar *extra[7];
+      int extracount = 1;
+
+      signal(SIGALRM, SIG_DFL);
+      *p++ = '-';
+      *p++ = 'q';
+      if (  f.queue_2stage
+#ifndef DISABLE_QUEUE_RAMP
+        && !*queuerun_msgid
+#endif
+        ) *p++ = 'q';
+      if (f.queue_run_first_delivery) *p++ = 'i';
+      if (f.queue_run_force) *p++ = 'f';
+      if (f.deliver_force_thaw) *p++ = 'f';
+      if (f.queue_run_local) *p++ = 'l';
+      *p = 0;
+      extra[0] = *queue_name
+       ? string_sprintf("%sG%s", opt, queue_name) : opt;
+
+#ifndef DISABLE_QUEUE_RAMP
+      if (*queuerun_msgid)
+       {
+       log_write(0, LOG_MAIN, "notify triggered queue run");
+       extra[extracount++] = queuerun_msgid;   /* Trigger only the */
+       extra[extracount++] = queuerun_msgid;   /* one message      */
+       }
+#endif
+
+      /* If -R or -S were on the original command line, ensure they get
+      passed on. */
+
+      if (deliver_selectstring)
+       {
+       extra[extracount++] = f.deliver_selectstring_regex ? US"-Rr" : US"-R";
+       extra[extracount++] = deliver_selectstring;
+       }
+
+      if (deliver_selectstring_sender)
+       {
+       extra[extracount++] = f.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, FALSE, extracount,
+       extra[0], extra[1], extra[2], extra[3], extra[4], extra[5], extra[6]);
+
+      /* Control never returns here. */
+      }
+
+    /* No need to re-exec; SIGALRM remains set to the default handler */
+
+#ifndef DISABLE_QUEUE_RAMP
+    if (*queuerun_msgid)
+      {
+      log_write(0, LOG_MAIN, "notify triggered queue run");
+      f.queue_2stage = FALSE;
+      queue_run(queuerun_msgid, queuerun_msgid, FALSE);
+      }
+    else
+#endif
+      queue_run(NULL, NULL, FALSE);
+    exim_underbar_exit(EXIT_SUCCESS);
+    }
+
+  if (pid < 0)
+    {
+    log_write(0, LOG_MAIN|LOG_PANIC, "daemon: fork of queue-runner "
+      "process failed: %s", strerror(errno));
+    log_close_all();
+    }
+  else
+    {
+    for (int i = 0; i < local_queue_run_max; ++i)
+      if (queue_pid_slots[i] <= 0)
+       {
+       queue_pid_slots[i] = pid;
+       queue_run_count++;
+       break;
+       }
+    DEBUG(D_any) debug_printf("%d queue-runner process%s running\n",
+      queue_run_count, queue_run_count == 1 ? "" : "es");
+    }
+  }
+
+/* Reset the alarm clock */
+
+sigalrm_seen = FALSE;
+#ifndef DISABLE_QUEUE_RAMP
+if (*queuerun_msgid)
+  *queuerun_msgid = 0;
+else
+#endif
+  ALARM(queue_interval);
+}
+
 /*************************************************
 *              Exim Daemon Mainline              *
 *************************************************/
@@ -2253,8 +2445,6 @@ report_time_since(&timestamp_startup, US"daemon loop start");     /* testcase 0022 *
 
 for (;;)
   {
-  pid_t pid;
-
   if (sigterm_seen)
     daemon_die();      /* Does not return */
 
@@ -2265,186 +2455,10 @@ for (;;)
   The other option is that we have an inetd wait timeout specified to -bw. */
 
   if (sigalrm_seen)
-    {
     if (inetd_wait_timeout > 0)
-      {
-      time_t resignal_interval = inetd_wait_timeout;
-
-      if (last_connection_time == (time_t)0)
-        {
-        DEBUG(D_any)
-          debug_printf("inetd wait timeout expired, but still not seen first message, ignoring\n");
-        }
-      else
-        {
-        time_t now = time(NULL);
-        if (now == (time_t)-1)
-          {
-          DEBUG(D_any) debug_printf("failed to get time: %s\n", strerror(errno));
-          }
-        else
-          {
-          if ((now - last_connection_time) >= inetd_wait_timeout)
-            {
-            DEBUG(D_any)
-              debug_printf("inetd wait timeout %d expired, ending daemon\n",
-                  inetd_wait_timeout);
-            log_write(0, LOG_MAIN, "exim %s daemon terminating, inetd wait timeout reached.\n",
-                version_string);
-            exit(EXIT_SUCCESS);
-            }
-          else
-            {
-            resignal_interval -= (now - last_connection_time);
-            }
-          }
-        }
-
-      sigalrm_seen = FALSE;
-      ALARM(resignal_interval);
-      }
-
+      daemon_inetd_wtimeout(last_connection_time);     /* Might not return */
     else
-      {
-      DEBUG(D_any) debug_printf("%s received\n",
-#ifndef DISABLE_QUEUE_RAMP
-       *queuerun_msgid ? "qrun notification" :
-#endif
-       "SIGALRM");
-
-      /* Do a full queue run in a child process, if required, unless we already
-      have enough queue runners on the go. If we are not running as root, a
-      re-exec is required. */
-
-      if (  queue_interval > 0
-         && (local_queue_run_max <= 0 || queue_run_count < local_queue_run_max))
-        {
-        if ((pid = exim_fork(US"queue-runner")) == 0)
-          {
-          /* Disable debugging if it's required only for the daemon process. We
-          leave the above message, because it ties up with the "child ended"
-          debugging messages. */
-
-          if (f.debug_daemon) debug_selector = 0;
-
-          /* Close any open listening sockets in the child */
-
-         close_daemon_sockets(daemon_notifier_fd,
-           fd_polls, listen_socket_count);
-
-          /* Reset SIGHUP and SIGCHLD in the child in both cases. */
-
-          signal(SIGHUP,  SIG_DFL);
-          signal(SIGCHLD, SIG_DFL);
-          signal(SIGTERM, SIG_DFL);
-          signal(SIGINT, SIG_DFL);
-
-          /* Re-exec if privilege has been given up, unless deliver_drop_
-          privilege is set. Reset SIGALRM before exec(). */
-
-          if (geteuid() != root_uid && !deliver_drop_privilege)
-            {
-            uschar opt[8];
-            uschar *p = opt;
-            uschar *extra[7];
-            int extracount = 1;
-
-            signal(SIGALRM, SIG_DFL);
-            *p++ = '-';
-            *p++ = 'q';
-            if (  f.queue_2stage
-#ifndef DISABLE_QUEUE_RAMP
-              && !*queuerun_msgid
-#endif
-              ) *p++ = 'q';
-            if (f.queue_run_first_delivery) *p++ = 'i';
-            if (f.queue_run_force) *p++ = 'f';
-            if (f.deliver_force_thaw) *p++ = 'f';
-            if (f.queue_run_local) *p++ = 'l';
-            *p = 0;
-           extra[0] = *queue_name
-             ? string_sprintf("%sG%s", opt, queue_name) : opt;
-
-#ifndef DISABLE_QUEUE_RAMP
-           if (*queuerun_msgid)
-             {
-             log_write(0, LOG_MAIN, "notify triggered queue run");
-             extra[extracount++] = queuerun_msgid;     /* Trigger only the */
-             extra[extracount++] = queuerun_msgid;     /* one message      */
-             }
-#endif
-
-            /* If -R or -S were on the original command line, ensure they get
-            passed on. */
-
-            if (deliver_selectstring)
-              {
-              extra[extracount++] = f.deliver_selectstring_regex ? US"-Rr" : US"-R";
-              extra[extracount++] = deliver_selectstring;
-              }
-
-            if (deliver_selectstring_sender)
-              {
-              extra[extracount++] = f.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, FALSE, extracount,
-              extra[0], extra[1], extra[2], extra[3], extra[4], extra[5], extra[6]);
-
-            /* Control never returns here. */
-            }
-
-          /* No need to re-exec; SIGALRM remains set to the default handler */
-
-#ifndef DISABLE_QUEUE_RAMP
-         if (*queuerun_msgid)
-           {
-           log_write(0, LOG_MAIN, "notify triggered queue run");
-           f.queue_2stage = FALSE;
-           queue_run(queuerun_msgid, queuerun_msgid, FALSE);
-           }
-         else
-#endif
-           queue_run(NULL, NULL, FALSE);
-          exim_underbar_exit(EXIT_SUCCESS);
-          }
-
-        if (pid < 0)
-          {
-          log_write(0, LOG_MAIN|LOG_PANIC, "daemon: fork of queue-runner "
-            "process failed: %s", strerror(errno));
-          log_close_all();
-          }
-        else
-          {
-          for (int i = 0; i < local_queue_run_max; ++i)
-            if (queue_pid_slots[i] <= 0)
-              {
-              queue_pid_slots[i] = pid;
-              queue_run_count++;
-              break;
-              }
-          DEBUG(D_any) debug_printf("%d queue-runner process%s running\n",
-            queue_run_count, queue_run_count == 1 ? "" : "es");
-          }
-        }
-
-      /* Reset the alarm clock */
-
-      sigalrm_seen = FALSE;
-#ifndef DISABLE_QUEUE_RAMP
-      if (*queuerun_msgid)
-       *queuerun_msgid = 0;
-      else
-#endif
-       ALARM(queue_interval);
-      }
-
-    } /* sigalrm_seen */
+      daemon_qrun(local_queue_run_max, fd_polls, listen_socket_count);
 
 
   /* Sleep till a connection happens if listening, and handle the connection if
@@ -2544,7 +2558,19 @@ for (;;)
          if (p->revents & POLLIN)
             {
            EXIM_SOCKLEN_T alen = sizeof(accepted);
-#ifdef TCP_INFO
+#if defined(__FreeBSD__) && defined(SO_LISTENQLEN)
+           int backlog;
+           socklen_t blen = sizeof(backlog);
+
+           if (  smtp_backlog_monitor > 0
+              && getsockopt(p->fd, SOL_SOCKET, SO_LISTENQLEN, &backlog, &blen) == 0)
+             {
+             DEBUG(D_interface)
+               debug_printf("listen fd %d queue curr %d\n", p->fd, backlog);
+             smtp_listen_backlog = backlog;
+             }
+
+#elif defined(TCP_INFO) && defined(EXIM_HAVE_TCPI_UNACKED)
            struct tcp_info ti;
            socklen_t tlen = sizeof(ti);
 
@@ -2554,15 +2580,9 @@ for (;;)
            if (  smtp_backlog_monitor > 0
               && getsockopt(p->fd, IPPROTO_TCP, TCP_INFO, &ti, &tlen) == 0)
              {
-# ifdef EXIM_HAVE_TCPI_UNACKED
              DEBUG(D_interface) debug_printf("listen fd %d queue max %u curr %u\n",
                      p->fd, ti.tcpi_sacked, ti.tcpi_unacked);
              smtp_listen_backlog = ti.tcpi_unacked;
-# elif defined(__FreeBSD__)    /* This does not work. Investigate kernel sourcecode. */
-             DEBUG(D_interface) debug_printf("listen fd %d queue max %u curr %u\n",
-                     p->fd, ti.__tcpi_sacked, ti.__tcpi_unacked);
-             smtp_listen_backlog = ti.__tcpi_unacked;
-# endif
              }
 #endif
            p->revents = 0;
@@ -2669,6 +2689,7 @@ for (;;)
     log_write(0, LOG_MAIN, "pid %d: SIGHUP received: re-exec daemon",
       getpid());
     close_daemon_sockets(daemon_notifier_fd, fd_polls, listen_socket_count);
+    unlink_notifier_socket();
     ALARM_CLR(0);
     signal(SIGHUP, SIG_IGN);
     sighup_argv[0] = exim_path;