[exim-cvs] Remove the daemon pid file when exit is due to SI…

Top Page
Delete this message
Reply to this message
Author: Exim Git Commits Mailing List
Date:  
To: exim-cvs
Subject: [exim-cvs] Remove the daemon pid file when exit is due to SIGTERM. Bug 340
Gitweb: https://git.exim.org/exim.git/commitdiff/01446a56c76aa5ac3213a86f8992a2371a8301f3
Commit:     01446a56c76aa5ac3213a86f8992a2371a8301f3
Parent:     580f325235cd1b9713bfa436719b7ca026fe9a16
Author:     Jeremy Harris <jgh146exb@???>
AuthorDate: Sat Nov 9 16:04:14 2019 +0000
Committer:  Jeremy Harris <jgh146exb@???>
CommitDate: Sat Nov 9 21:54:59 2019 +0000


    Remove the daemon pid file when exit is due to SIGTERM.  Bug 340
---
 doc/doc-docbook/spec.xfpt    |  11 +++++
 doc/doc-txt/ChangeLog        |   2 +
 src/src/daemon.c             | 110 +++++++++++++++++++++++++++++++++++++------
 src/src/exim.c               |   8 +++-
 src/src/functions.h          |   1 +
 test/log/0438                |   1 +
 test/runtest                 |   1 +
 test/scripts/0000-Basic/0438 |  13 +++++
 test/stderr/0438             |  22 +++++++++
 test/stdout/0438             |   3 ++
 10 files changed, 156 insertions(+), 16 deletions(-)


diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt
index 62109e9..19c15c2 100644
--- a/doc/doc-docbook/spec.xfpt
+++ b/doc/doc-docbook/spec.xfpt
@@ -4367,6 +4367,17 @@ written. When &%-oX%& is used with &%-bd%&, or when &%-q%& with a time is used
without &%-bd%&, this is the only way of causing Exim to write a pid file,
because in those cases, the normal pid file is not used.

+.new
+.vitem &%-oPX%&
+.oindex "&%-oPX%&"
+.cindex "pid (process id)" "of daemon"
+.cindex "daemon" "process id (pid)"
+This option is not intended for general use.
+The daemon uses it when terminating due to a SIGTEM, possibly in
+combination with &%-oP%&&~<&'path'&>.
+It causes the pid file to be removed.
+.wen
+
.vitem &%-or%&&~<&'time'&>
.oindex "&%-or%&"
.cindex "timeout" "for non-SMTP input"
diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog
index e9a614c..a8cd823 100644
--- a/doc/doc-txt/ChangeLog
+++ b/doc/doc-txt/ChangeLog
@@ -24,6 +24,8 @@ JH/04 Support CHUNKING from an smtp transport using a transport_filter, when

JH/05 Regard command-line receipients as tainted.

+JH/06 Bug 340: Remove the daemon pid file on exit, whe due to SIGTERM.
+

Exim version 4.93
-----------------
diff --git a/src/src/daemon.c b/src/src/daemon.c
index 3fc73ba..61371f5 100644
--- a/src/src/daemon.c
+++ b/src/src/daemon.c
@@ -31,6 +31,7 @@ static smtp_slot empty_smtp_slot = { .pid = 0, .host_address = NULL };

static SIGNAL_BOOL sigchld_seen;
static SIGNAL_BOOL sighup_seen;
+static SIGNAL_BOOL sigterm_seen;

static int accept_retry_count = 0;
static int accept_retry_errno;
@@ -87,6 +88,16 @@ sigchld_seen = TRUE;
}


+/* SIGTERM handler. Try to get the damon pif file removed
+before exiting. */
+
+static void
+main_sigterm_handler(int sig)
+{
+sigterm_seen = TRUE;
+}
+
+


/*************************************************
@@ -430,6 +441,7 @@ if (pid == 0)
#else
signal(SIGCHLD, SIG_IGN);
#endif
+ signal(SIGTERM, SIG_DFL);

/* Attempt to get an id from the sending machine via the RFC 1413
protocol. We do this in the sub-process in order not to hold up the
@@ -654,6 +666,7 @@ if (pid == 0)

         signal(SIGHUP,  SIG_DFL);
         signal(SIGCHLD, SIG_DFL);
+        signal(SIGTERM, SIG_DFL);


         if (geteuid() != root_uid && !deliver_drop_privilege)
           {
@@ -888,6 +901,77 @@ while ((pid = waitpid(-1, &status, WNOHANG)) > 0)




+static void
+set_pid_file_path(void)
+{
+if (override_pid_file_path)
+  pid_file_path = override_pid_file_path;
+
+if (!*pid_file_path)
+  pid_file_path = string_sprintf("%s/exim-daemon.pid", spool_directory);
+}
+
+
+/* Remove the daemon's pidfile.  Note: runs with root privilege,
+as a direct child of the daemon.  Does not return. */
+
+void
+delete_pid_file(void)
+{
+uschar * daemon_pid = string_sprintf("%d\n", (int)getppid());
+FILE * f;
+
+set_pid_file_path();
+if ((f = Ufopen(pid_file_path, "rb")))
+  {
+  if (  fgets(CS big_buffer, big_buffer_size, f)
+    && Ustrcmp(daemon_pid, big_buffer) == 0
+     )
+    if (Uunlink(pid_file_path) == 0)
+      {
+      DEBUG(D_any)
+    debug_printf("%s unlink: %s\n", pid_file_path, strerror(errno));
+      }
+    else
+      DEBUG(D_any)
+    debug_printf("unlinked %s\n", pid_file_path);
+  fclose(f);
+  }
+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");
+}
+
+
+/* Called by the daemon; exec a child to get the pid file deleted
+since we may require privs for the containing directory */
+
+static void
+daemon_die(void)
+{
+int pid;
+
+if (f.running_in_test_harness || write_pid)
+  {
+  if ((pid = fork()) == 0)
+    {
+    if (override_pid_file_path)
+      (void)child_exec_exim(CEE_EXEC_PANIC, FALSE, NULL, FALSE, 3,
+    "-oP", override_pid_file_path, "-oPX");
+    else
+      (void)child_exec_exim(CEE_EXEC_PANIC, FALSE, NULL, FALSE, 1, "-oPX");
+
+    /* Control never returns here. */
+    }
+  if (pid > 0)
+    child_close(pid, 1);
+  }
+exim_exit(EXIT_SUCCESS, US"daemon");
+}
+
+
 /*************************************************
 *              Exim Daemon Mainline              *
 *************************************************/
@@ -1068,19 +1152,14 @@ if (f.daemon_listen && !f.inetd_wait_mode)
     gstring * new_smtp_port = NULL;
     gstring * new_local_interfaces = NULL;


-    if (override_pid_file_path == NULL) write_pid = FALSE;
+    if (!override_pid_file_path) write_pid = FALSE;


     list = override_local_interfaces;
     sep = 0;
     while ((s = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)))
       {
       uschar joinstr[4];
-      gstring ** gp;
-
-      if (Ustrpbrk(s, ".:") == NULL)
-        gp = &new_smtp_port;
-      else
-        gp = &new_local_interfaces;
+      gstring ** gp = Ustrpbrk(s, ".:") ? &new_local_interfaces : &new_smtp_port;


       if (!*gp)
         {
@@ -1538,12 +1617,7 @@ if (f.running_in_test_harness || write_pid)
   {
   FILE *f;


-  if (override_pid_file_path)
-    pid_file_path = override_pid_file_path;
-
-  if (pid_file_path[0] == 0)
-    pid_file_path = string_sprintf("%s/exim-daemon.pid", spool_directory);
-
+  set_pid_file_path();
   if ((f = modefopen(pid_file_path, "wb", 0644)))
     {
     (void)fprintf(f, "%d\n", (int)getpid());
@@ -1586,11 +1660,15 @@ if (queue_interval > 0 && local_queue_run_max > 0)
   for (int i = 0; i < local_queue_run_max; i++) queue_pid_slots[i] = 0;
   }


-/* Set up the handler for termination of child processes. */
+/* Set up the handler for termination of child processes, and the one
+telling us to die. */

sigchld_seen = FALSE;
os_non_restarting_signal(SIGCHLD, main_sigchld_handler);

+sigterm_seen = FALSE;
+os_non_restarting_signal(SIGTERM, main_sigterm_handler);
+
/* If we are to run the queue periodically, pretend the alarm has just gone
off. This will cause the first queue-runner to get kicked off straight away. */

@@ -1791,6 +1869,9 @@ for (;;)
EXIM_SOCKLEN_T len;
pid_t pid;

+  if (sigterm_seen)
+    daemon_die();    /* Does not return */
+
   /* This code is placed first in the loop, so that it gets obeyed at the
   start, before the first wait, for the queue-runner case, so that the first
   one can be started immediately.
@@ -1868,6 +1949,7 @@ for (;;)


           signal(SIGHUP,  SIG_DFL);
           signal(SIGCHLD, SIG_DFL);
+          signal(SIGTERM, SIG_DFL);


           /* Re-exec if privilege has been given up, unless deliver_drop_
           privilege is set. Reset SIGALRM before exec(). */
diff --git a/src/src/exim.c b/src/src/exim.c
index a30e35b..f294354 100644
--- a/src/src/exim.c
+++ b/src/src/exim.c
@@ -202,7 +202,7 @@ va_end(ap);
 static void
 term_handler(int sig)
 {
-  exit(1);
+exit(1);
 }



@@ -3067,11 +3067,15 @@ for (i = 1; i < argc; i++)

     else if (Ustrcmp(argrest, "o") == 0) {}


-    /* -oP <name>: set pid file path for daemon */
+    /* -oP <name>: set pid file path for daemon
+       -oPX:       delete pid file of daemon */


     else if (Ustrcmp(argrest, "P") == 0)
       override_pid_file_path = argv[++i];


+    else if (Ustrcmp(argrest, "PX") == 0)
+      delete_pid_file();
+
     /* -or <n>: set timeout for non-SMTP acceptance
        -os <n>: set timeout for SMTP acceptance */


diff --git a/src/src/functions.h b/src/src/functions.h
index 488e84c..187bdaf 100644
--- a/src/src/functions.h
+++ b/src/src/functions.h
@@ -175,6 +175,7 @@ extern void    debug_print_tree(tree_node *);
 extern void    debug_vprintf(int, const char *, va_list);
 extern void    decode_bits(unsigned int *, size_t, int *,
                uschar *, bit_table *, int, uschar *, int);
+extern void    delete_pid_file(void);
 extern address_item *deliver_make_addr(uschar *, BOOL);
 extern void    delivery_log(int, address_item *, int, uschar *);
 extern int     deliver_message(uschar *, BOOL, BOOL);
diff --git a/test/log/0438 b/test/log/0438
index 574cf9b..7879681 100644
--- a/test/log/0438
+++ b/test/log/0438
@@ -2,3 +2,4 @@
 ******** SERVER ********
 1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port PORT_D
 1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port PORT_D
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port PORT_D
diff --git a/test/runtest b/test/runtest
index 9f65691..97edbc2 100755
--- a/test/runtest
+++ b/test/runtest
@@ -1029,6 +1029,7 @@ RESET_AFTER_EXTRA_LINE_READ:


     s/(?<=^>>>>>>>>>>>>>>>> Exim pid=)\d+(?= terminating)/pppp/;
     s/^(proxy-proc \w{5}-pid) \d+$/$1 pppp/;
+    s/^(?:\d+)( exec .* -oPX)$/pppp$1/;


     # IP address lookups use gethostbyname() when IPv6 is not supported,
     # and gethostbyname2() or getipnodebyname() when it is.
diff --git a/test/scripts/0000-Basic/0438 b/test/scripts/0000-Basic/0438
index 099efba..a8287cf 100644
--- a/test/scripts/0000-Basic/0438
+++ b/test/scripts/0000-Basic/0438
@@ -8,3 +8,16 @@ killdaemon
 exim -d -DSERVER=server -bd -oX PORT_D -oP DIR/spool/exim-daemon.anotherpid
 ****
 killdaemon
+#
+# Check for a SIGTERM daemon kill removing the pid file
+exim -d -DSERVER=server -bd -oX PORT_D -oP DIR/spool/mypidfile
+****
+sleep 1
+ls DIR/spool
+sudo perl
+open(IN, "<", "DIR/spool/mypidfile");
+while (<IN>) { kill "TERM", $_; }
+****
+sleep 1
+ls DIR/spool
+#
diff --git a/test/stderr/0438 b/test/stderr/0438
index f44d7bb..bb6ba3f 100644
--- a/test/stderr/0438
+++ b/test/stderr/0438
@@ -36,3 +36,25 @@ LOG: MAIN
 set_process_info: pppp daemon(x.yz): no queue runs, listening for SMTP on port 1225
 daemon running with uid=EXIM_UID gid=EXIM_GID euid=EXIM_UID egid=EXIM_GID
 Listening...
+Exim version x.yz ....
+changed uid/gid: forcing real = effective
+  uid=uuuu gid=CALLER_GID pid=pppp
+configuration file is TESTSUITE/test-config
+admin user
+dropping to exim gid; retaining priv uid
+originator: uid=CALLER_UID gid=CALLER_GID login=CALLER name=CALLER_NAME
+daemon_smtp_port overridden by -oX:
+  <: 1225
+listening on all interfaces (IPv4) port 1225
+pid written to TESTSUITE/spool/mypidfile
+changed uid/gid: running as a daemon
+  uid=EXIM_UID gid=EXIM_GID pid=pppp
+LOG: MAIN
+  exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
+set_process_info: pppp daemon(x.yz): no queue runs, listening for SMTP on port 1225
+daemon running with uid=EXIM_UID gid=EXIM_GID euid=EXIM_UID egid=EXIM_GID
+Listening...
+OpenSSL: creating STEK
+pppp exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -DSERVER=server -C TESTSUITE/test-config -d=0xf7795cfd -oP TESTSUITE/spool/mypidfile -oPX
+search_tidyup called
+>>>>>>>>>>>>>>>> Exim pid=pppp (daemon) terminating with rc=0 >>>>>>>>>>>>>>>>
diff --git a/test/stdout/0438 b/test/stdout/0438
new file mode 100644
index 0000000..431c133
--- /dev/null
+++ b/test/stdout/0438
@@ -0,0 +1,3 @@
+log
+mypidfile
+log