Copyright updates:
[exim.git] / src / src / daemon.c
index 265ee2bb6bd4acc8f8f3fc591b8ca72efaf22bb3..2bed143a188ac1ed57e263904c142309545bd813 100644 (file)
@@ -3,6 +3,7 @@
 *************************************************/
 
 /* Copyright (c) University of Cambridge 1995 - 2018 */
+/* Copyright (c) The Exim Maintainers 2020 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Functions concerned with running Exim as a daemon */
@@ -126,6 +127,18 @@ if (smtp_out) smtp_printf("421 %s\r\n", FALSE, smtp_msg);
 
 
 
+/*************************************************
+*************************************************/
+
+static void
+close_daemon_sockets(int daemon_notifier_fd,
+  int * listen_sockets, int listen_socket_count)
+{
+if (daemon_notifier_fd >= 0) (void) close(daemon_notifier_fd);
+for (int i = 0; i < listen_socket_count; i++) (void) close(listen_sockets[i]);
+}
+
+
 /*************************************************
 *            Handle a connected SMTP call        *
 *************************************************/
@@ -355,13 +368,12 @@ if (LOGGING(smtp_connection))
 expansion above did a lookup. */
 
 search_tidyup();
-pid = fork();
+pid = exim_fork(US"daemon-accept");
 
 /* Handle the child process */
 
 if (pid == 0)
   {
-  int i;
   int queue_only_reason = 0;
   int old_pool = store_pool;
   int save_debug_selector = debug_selector;
@@ -424,7 +436,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++) (void)close(listen_sockets[i]);
+  close_daemon_sockets(daemon_notifier_fd, listen_sockets, listen_socket_count);
 
   /* Set FD_CLOEXEC on the SMTP socket. We don't want any rogue child processes
   to be able to communicate with them, under any circumstances. */
@@ -651,7 +663,7 @@ if (pid == 0)
 
       mac_smtp_fflush();
 
-      if ((dpid = fork()) == 0)
+      if ((dpid = exim_fork(US"daemon-accept-delivery")) == 0)
         {
         (void)fclose(smtp_in);
         (void)fclose(smtp_out);
@@ -942,7 +954,7 @@ else
   DEBUG(D_any)
     debug_printf("%s\n", string_open_failed(errno, "pid file %s",
       pid_file_path));
-exim_exit(EXIT_SUCCESS, US"pid file remover");
+exim_exit(EXIT_SUCCESS);
 }
 
 
@@ -954,9 +966,22 @@ daemon_die(void)
 {
 int pid;
 
+if (daemon_notifier_fd >= 0)
+  {
+  close(daemon_notifier_fd);
+  daemon_notifier_fd = -1;
+#ifndef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS
+    {
+    uschar * s = expand_string(notifier_socket);
+    DEBUG(D_any) debug_printf("unlinking notifier socket %s\n", s);
+    Uunlink(s);
+    }
+#endif
+  }
+
 if (f.running_in_test_harness || write_pid)
   {
-  if ((pid = fork()) == 0)
+  if ((pid = exim_fork(US"daemon-del-pidfile")) == 0)
     {
     if (override_pid_file_path)
       (void)child_exec_exim(CEE_EXEC_PANIC, FALSE, NULL, FALSE, 3,
@@ -969,7 +994,7 @@ if (f.running_in_test_harness || write_pid)
   if (pid > 0)
     child_close(pid, 1);
   }
-exim_exit(EXIT_SUCCESS, US"daemon");
+exim_exit(EXIT_SUCCESS);
 }
 
 
@@ -982,40 +1007,64 @@ daemon_notifier_socket(void)
 {
 int fd;
 const uschar * where;
-struct sockaddr_un sun = {.sun_family = AF_UNIX};
+struct sockaddr_un sa_un = {.sun_family = AF_UNIX};
 int len;
 
+if (override_local_interfaces && !override_pid_file_path)
+  {
+  DEBUG(D_any)
+    debug_printf("-oX used without -oP so not creating notifier socket\n");
+  return;
+  }
+
 DEBUG(D_any) debug_printf("creating notifier socket\n");
 
-where = US"socket";
 #ifdef SOCK_CLOEXEC
-if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0)
-  goto bad;
+if ((fd = socket(PF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0)
+  { where = US"socket"; goto bad; }
 #else
-if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
-  goto bad;
+if ((fd = socket(PF_UNIX, SOCK_DGRAM, 0)) < 0)
+  { where = US"socket"; goto bad; }
 (void)fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
 #endif
 
-sun.sun_path[0] = 0;   /* Abstract local socket addr - Linux-specific? */
+#ifdef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS
+sa_un.sun_path[0] = 0; /* Abstract local socket addr - Linux-specific? */
 len = offsetof(struct sockaddr_un, sun_path) + 1
-  + snprintf(sun.sun_path+1, sizeof(sun.sun_path)-1, "%s", NOTIFIER_SOCKET_NAME);
+  + snprintf(sa_un.sun_path+1, sizeof(sa_un.sun_path)-1, "%s",
+             expand_string(notifier_socket));
+DEBUG(D_any) debug_printf(" @%s\n", sa_un.sun_path+1);
+#else                  /* filesystem-visible and persistent; will neeed removal */
+len = offsetof(struct sockaddr_un, sun_path)
+  + snprintf(sa_un.sun_path, sizeof(sa_un.sun_path), "%s", 
+             expand_string(notifier_socket));
+DEBUG(D_any) debug_printf(" %s\n", sa_un.sun_path);
+#endif
 
-where = US"bind";
-if (bind(fd, (const struct sockaddr *)&sun, len) < 0)
-  goto bad;
+if (bind(fd, (const struct sockaddr *)&sa_un, len) < 0)
+  { where = US"bind"; goto bad; }
 
-where = US"SO_PASSCRED";
+#ifdef SO_PASSCRED             /* Linux */
 if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0)
-  goto bad;
+  { where = US"SO_PASSCRED"; goto bad2; }
+#elif defined(LOCAL_CREDS)     /* FreeBSD-ish */
+if (setsockopt(fd, 0, LOCAL_CREDS, &on, sizeof(on)) < 0)
+  { where = US"LOCAL_CREDS"; goto bad2; }
+#endif
 
 /* debug_printf("%s: fd %d\n", __FUNCTION__, fd); */
 daemon_notifier_fd = fd;
 return;
 
+bad2:
+#ifndef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS
+  Uunlink(sa_un.sun_path);
+#endif
 bad:
-  log_write(0, LOG_MAIN|LOG_PANIC, "%s: %s: %s",
+  log_write(0, LOG_MAIN|LOG_PANIC, "%s %s: %s",
     __FUNCTION__, where, strerror(errno));
+  close(fd);
+  return;
 }
 
 
@@ -1026,17 +1075,16 @@ static BOOL
 daemon_notification(void)
 {
 uschar buf[256], cbuf[256];
-struct sockaddr_un sun;
+struct sockaddr_un sa_un;
 struct iovec iov = {.iov_base = buf, .iov_len = sizeof(buf)-1};
-struct msghdr msg = { .msg_name = &sun,
-                     .msg_namelen = sizeof(sun),
+struct msghdr msg = { .msg_name = &sa_un,
+                     .msg_namelen = sizeof(sa_un),
                      .msg_iov = &iov,
                      .msg_iovlen = 1,
                      .msg_control = cbuf,
                      .msg_controllen = sizeof(cbuf)
                    };
 ssize_t sz;
-struct cmsghdr * cp;
 
 buf[sizeof(buf)-1] = 0;
 if ((sz = recvmsg(daemon_notifier_fd, &msg, 0)) <= 0) return FALSE;
@@ -1045,41 +1093,47 @@ 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__,
-  *sun.sun_path ? "" : " abstract", sun.sun_path+ (*sun.sun_path ? 0 : 1));
+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));
 
 /* Refuse to handle the item unless the peer has good credentials */
 #ifdef SCM_CREDENTIALS
 # define EXIM_SCM_CR_TYPE SCM_CREDENTIALS
-#elif defined(SCM_CREDS)
+#elif defined(LOCAL_CREDS) && defined(SCM_CREDS)
 # define EXIM_SCM_CR_TYPE SCM_CREDS
 #else
-# error no SCM creds knowlege
+       /* The OS has no way to get the creds of the caller (for a unix/datagram socket.
+       Punt; don't try to check. */
 #endif
 
+#ifdef EXIM_SCM_CR_TYPE
 for (struct cmsghdr * cp = CMSG_FIRSTHDR(&msg);
      cp;
      cp = CMSG_NXTHDR(&msg, cp))
   if (cp->cmsg_level == SOL_SOCKET && cp->cmsg_type == EXIM_SCM_CR_TYPE)
   {
-#ifdef SCM_CREDENTIALS
+# ifdef SCM_CREDENTIALS                                        /* Linux */
   struct ucred * cr = (struct ucred *) CMSG_DATA(cp);
   if (cr->uid && cr->uid != exim_uid)
     {
     DEBUG(D_queue_run) debug_printf("%s: sender creds pid %d uid %d gid %d\n",
       __FUNCTION__, (int)cr->pid, (int)cr->uid, (int)cr->gid);
     return FALSE;
-#elif defined(SCM_CREDS)
-  struct cmsgcred * cr = (struct cmsgcred *) CMSG_DATA(cp);
-  if (cr->cmcred_uid && cr->cmcred_uid != exim_uid)
+    }
+# elif defined(LOCAL_CREDS)                            /* BSD-ish */
+  struct sockcred * cr = (struct sockcred *) CMSG_DATA(cp);
+  if (cr->sc_uid && cr->sc_uid != exim_uid)
     {
-    DEBUG(D_queue_run) debug_printf("%s: sender creds pid %d uid %d gid %d\n",
-      __FUNCTION__, (int)cr->cmcred_pid, (int)cr->cmcred_uid, (int)cr->cmcred_gid);
+    DEBUG(D_queue_run) debug_printf("%s: sender creds pid ??? uid %d gid %d\n",
+      __FUNCTION__, (int)cr->sc_uid, (int)cr->sc_gid);
     return FALSE;
-#endif
     }
+# endif
   break;
   }
+#endif
 
 buf[sz] = 0;
 switch (buf[0])
@@ -1102,7 +1156,7 @@ switch (buf[0])
       debug_printf("%s: queue size request: %s\n", __FUNCTION__, buf);
 
     if (sendto(daemon_notifier_fd, buf, len, 0,
-               (const struct sockaddr *)&sun, msg.msg_namelen) < 0)
+               (const struct sockaddr *)&sa_un, msg.msg_namelen) < 0)
       log_write(0, LOG_MAIN|LOG_PANIC,
        "%s: sendto: %s\n", __FUNCTION__, strerror(errno));
     return FALSE;
@@ -1145,6 +1199,8 @@ ip_address_item *addresses = NULL;
 time_t last_connection_time = (time_t)0;
 int local_queue_run_max = atoi(CS expand_string(queue_run_max));
 
+process_purpose = US"daemon";
+
 /* If any debugging options are set, turn on the D_pid bit so that all
 debugging lines get the pid added. */
 
@@ -1296,7 +1352,7 @@ if (f.daemon_listen && !f.inetd_wait_mode)
 
     list = override_local_interfaces;
     sep = 0;
-    while ((s = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)))
+    while ((s = string_nextinlist(&list, &sep, NULL, 0)))
       {
       uschar joinstr[4];
       gstring ** gp = Ustrpbrk(s, ".:") ? &new_local_interfaces : &new_smtp_port;
@@ -1334,13 +1390,13 @@ if (f.daemon_listen && !f.inetd_wait_mode)
 
   list = daemon_smtp_port;
   sep = 0;
-  while ((s = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)))
+  while ((s = string_nextinlist(&list, &sep, NULL, 0)))
     pct++;
   default_smtp_port = store_get((pct+1) * sizeof(int), FALSE);
   list = daemon_smtp_port;
   sep = 0;
   for (pct = 0;
-       (s = string_nextinlist(&list, &sep, big_buffer, big_buffer_size));
+       (s = string_nextinlist(&list, &sep, NULL, 0));
        pct++)
     {
     if (isdigit(*s))
@@ -1546,7 +1602,7 @@ if (f.background_daemon)
 
   if (getppid() != 1)
     {
-    pid_t pid = fork();
+    pid_t pid = exim_fork(US"daemon");
     if (pid < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE,
       "fork() failed when starting daemon: %s", strerror(errno));
     if (pid > 0) exit(EXIT_SUCCESS);      /* in parent process, just exit */
@@ -1844,7 +1900,8 @@ else if (f.daemon_listen)
   ip_address_item * ipa;
   uschar * p;
   uschar * qinfo = queue_interval > 0
-    ? string_sprintf("-q%s", readconf_printtime(queue_interval))
+    ? string_sprintf("-q%s%s",
+       f.queue_2stage ? "q" : "", readconf_printtime(queue_interval))
     : US"no queue runs";
 
   /* Build a list of listening addresses in big_buffer, but limit it to 10
@@ -2072,14 +2129,11 @@ for (;;)
       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 (  queue_interval > 0
+         && (local_queue_run_max <= 0 || queue_run_count < local_queue_run_max))
         {
-        if ((pid = fork()) == 0)
+        if ((pid = exim_fork(US"queue-runner")) == 0)
           {
-          DEBUG(D_any) debug_printf("Starting queue-runner: pid %d\n",
-            (int)getpid());
-
           /* 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. */
@@ -2088,10 +2142,8 @@ for (;;)
 
           /* Close any open listening sockets in the child */
 
-         if (daemon_notifier_fd >= 0)
-           (void) close(daemon_notifier_fd);
-          for (int sk = 0; sk < listen_socket_count; sk++)
-            (void) close(listen_sockets[sk]);
+         close_daemon_sockets(daemon_notifier_fd,
+           listen_sockets, listen_socket_count);
 
           /* Reset SIGHUP and SIGCHLD in the child in both cases. */
 
@@ -2128,6 +2180,7 @@ for (;;)
 #ifdef EXPERIMENTAL_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      */
              }
@@ -2162,6 +2215,7 @@ for (;;)
 #ifdef EXPERIMENTAL_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);
            }
@@ -2399,8 +2453,8 @@ for (;;)
     {
     log_write(0, LOG_MAIN, "pid %d: SIGHUP received: re-exec daemon",
       getpid());
-    for (int sk = 0; sk < listen_socket_count; sk++)
-      (void)close(listen_sockets[sk]);
+    close_daemon_sockets(daemon_notifier_fd,
+      listen_sockets, listen_socket_count);
     ALARM_CLR(0);
     signal(SIGHUP, SIG_IGN);
     sighup_argv[0] = exim_path;