Changeset: 20ba6d492b51 for MonetDB
URL: http://dev.monetdb.org/hg/MonetDB?cmd=changeset;node=20ba6d492b51
Modified Files:
        tools/merovingian/daemon/client.c
        tools/merovingian/daemon/controlrunner.c
        tools/merovingian/daemon/forkmserver.c
        tools/merovingian/daemon/handlers.c
        tools/merovingian/daemon/handlers.h
        tools/merovingian/daemon/merovingian.c
Branch: Jun2016
Log Message:

Do not manipulate a shared linked list in a signal handler.

When a server exits, monetdbd gets a SIGCHLD signal.  In the signal
handler, the record for that child is removed from the linked list of
children.  This happens with the appropriate lock being held.

However, the locks are initialized so that if a thread tries to lock a
lock that it already holds, the second attempt succeeds.

So, if the thread executing the signal handler happens to already hold
the lock, say in logListener, the signal handler blithely ignores this
lock and manipulates the linked list that the thread (logListener)
thinks it is the sole owner of.  Chaos ensues.

This hopefully finally fixed bug 4066.


diffs (271 lines):

diff --git a/tools/merovingian/daemon/client.c 
b/tools/merovingian/daemon/client.c
--- a/tools/merovingian/daemon/client.c
+++ b/tools/merovingian/daemon/client.c
@@ -33,6 +33,7 @@
 #include "multiplex-funnel.h"
 #include "controlrunner.h"
 #include "client.h"
+#include "handlers.h"
 
 struct threads {
        struct threads *next;
@@ -460,6 +461,7 @@ acceptConnections(int sock, int usock)
                                        break;
                        }
                }
+               childhandler();
                if (retval == 0) {
                        /* nothing interesting has happened */
                        continue;
diff --git a/tools/merovingian/daemon/controlrunner.c 
b/tools/merovingian/daemon/controlrunner.c
--- a/tools/merovingian/daemon/controlrunner.c
+++ b/tools/merovingian/daemon/controlrunner.c
@@ -390,13 +390,6 @@ static void ctl_handle_client(
                                } else {
                                        if (*p != '\0') {
                                                pid_t child;
-                                               sigset_t blocksig;
-                                               /* temporarily block SIGCHLD 
signals until
-                                                * we've waited for the child 
we're about to
-                                                * create. See bug 
http://bugs.monetdb.org/3603. */
-                                               sigemptyset(&blocksig);
-                                               sigaddset(&blocksig, SIGCHLD);
-                                               pthread_sigmask(SIG_BLOCK, 
&blocksig, (sigset_t *) 0);
                                                if ((child = fork()) == 0) {
                                                        FILE *secretf;
                                                        size_t len;
@@ -406,10 +399,6 @@ static void ctl_handle_client(
                                                        int setlen = 0;
                                                        char *sadbfarm;
 
-                                                       sigemptyset(&blocksig);
-                                                       sigaddset(&blocksig, 
SIGCHLD);
-                                                       
pthread_sigmask(SIG_UNBLOCK, &blocksig, (sigset_t *) 0);
-
                                                        if ((err = 
msab_getDBfarm(&sadbfarm)) != NULL) {
                                                                
Mfprintf(_mero_ctlerr, "%s: internal error: %s\n",
                                                                                
 origin, err);
@@ -465,9 +454,6 @@ static void ctl_handle_client(
                                                        Mfprintf(_mero_ctlout, 
"%s: forking failed\n",
                                                                         
origin);
                                                }
-                                               sigemptyset(&blocksig);
-                                               sigaddset(&blocksig, SIGCHLD);
-                                               pthread_sigmask(SIG_UNBLOCK, 
&blocksig, (sigset_t *) 0);
                                        }
 
                                        Mfprintf(_mero_ctlout, "%s: created 
database '%s'\n",
diff --git a/tools/merovingian/daemon/forkmserver.c 
b/tools/merovingian/daemon/forkmserver.c
--- a/tools/merovingian/daemon/forkmserver.c
+++ b/tools/merovingian/daemon/forkmserver.c
@@ -54,7 +54,6 @@ forkMserver(char *database, sabdb** stat
        char upmax[8];
        confkeyval *ckv, *kv, *list;
        SABdbState state;
-       sigset_t blocksig;
 
        er = msab_getStatus(stats, database);
        if (er != NULL) {
@@ -258,9 +257,6 @@ forkMserver(char *database, sabdb** stat
                                        database, database));
        }
 
-       sigemptyset(&blocksig);
-       sigaddset(&blocksig, SIGCHLD);
-       pthread_sigmask(SIG_BLOCK, &blocksig, (sigset_t *) 0);
        pid = fork();
        if (pid == 0) {
                char *sabdbfarm;
@@ -279,10 +275,6 @@ forkMserver(char *database, sabdb** stat
                int c = 0;
                unsigned int mport;
 
-               sigemptyset(&blocksig);
-               sigaddset(&blocksig, SIGCHLD);
-               pthread_sigmask(SIG_UNBLOCK, &blocksig, (sigset_t *) 0);
-
                er = msab_getDBfarm(&sabdbfarm);
                if (er != NULL) {
                        Mfprintf(stderr, "unexpected error: %s\n", er);
@@ -454,10 +446,6 @@ forkMserver(char *database, sabdb** stat
 
                        pthread_mutex_unlock(&_mero_topdp_lock);
 
-                       sigemptyset(&blocksig);
-                       sigaddset(&blocksig, SIGCHLD);
-                       pthread_sigmask(SIG_UNBLOCK, &blocksig, (sigset_t *) 0);
-
                        /* wait for the child to finish starting, at some point 
we
                         * decided that we should wait indefinitely here 
because if the
                         * mserver needs time to start up, we shouldn't 
interrupt it,
diff --git a/tools/merovingian/daemon/handlers.c 
b/tools/merovingian/daemon/handlers.c
--- a/tools/merovingian/daemon/handlers.c
+++ b/tools/merovingian/daemon/handlers.c
@@ -143,83 +143,66 @@ huphandler(int sig)
 }
 
 /**
- * Handles SIGCHLD signals, that is, signals that a parent receives
- * about its children.  This handler deals with terminated children, by
- * deregistering them from the internal administration (_mero_topdp)
- * with the necessary cleanup.
+ * Wait for and deal with any children that may have exited.  This
+ * handler deals with terminated children, by deregistering them from
+ * the internal administration (_mero_topdp) with the necessary
+ * cleanup.
  */
 void
-childhandler(int sig, siginfo_t *si, void *unused)
+childhandler(void)
 {
        dpair p, q;
+       pid_t pid;
+       int wstatus;
 
-       (void)sig;
-       (void)unused;
+       while ((pid = waitpid(-1, &wstatus, WNOHANG)) > 0) {
+               pthread_mutex_lock(&_mero_topdp_lock);
 
-       /* wait for the child to get properly terminated, hopefully filling
-        * in the siginfo struct on FreeBSD */
-       if (waitpid(-1, NULL, WNOHANG) <= 0) {
-               /* if no child has exited, we may have already waited for it
-                * in e.g. ctl_handle_client() */
-               return;
+               /* get the pid from the former child, and locate it in our list 
*/
+               q = _mero_topdp->next;
+               p = q->next;
+               while (p != NULL) {
+                       if (p->pid == pid) {
+                               /* log everything that's still in the pipes */
+                               logFD(p->out, "MSG", p->dbname, (long long 
int)p->pid, _mero_logfile);
+                               /* remove from the list */
+                               q->next = p->next;
+                               /* close the descriptors */
+                               close(p->out);
+                               close(p->err);
+                               if (WIFEXITED(wstatus)) {
+                                       Mfprintf(stdout, "database '%s' (%lld) 
has exited with "
+                                                        "exit status %d\n", 
p->dbname,
+                                                        (long long int)p->pid, 
WEXITSTATUS(wstatus));
+                               } else if (WIFSIGNALED(wstatus)) {
+                                       if (WCOREDUMP(wstatus)) {
+                                               Mfprintf(stdout, "database '%s' 
(%lld) has crashed "
+                                                                "(dumped 
core)\n", p->dbname,
+                                                                (long long 
int)p->pid);
+                                       } else {
+                                               const char *sigstr = 
sigtostr(WTERMSIG(wstatus));
+                                               char signum[8];
+                                               if (sigstr == NULL) {
+                                                       snprintf(signum, 8, 
"%d", WTERMSIG(wstatus));
+                                                       sigstr = signum;
+                                               }
+                                               Mfprintf(stdout, "database '%s' 
(%lld) was killed by signal "
+                                                                "%s\n", 
p->dbname,
+                                                                (long long 
int)p->pid, sigstr);
+                                       }
+                               }
+                               if (p->dbname)
+                                       free(p->dbname);
+                               free(p);
+                               pthread_mutex_unlock(&_mero_topdp_lock);
+                               break;
+                       }
+                       q = p;
+                       p = q->next;
+               }
+
+               pthread_mutex_unlock(&_mero_topdp_lock);
        }
-
-       if (si->si_code != CLD_EXITED &&
-                       si->si_code != CLD_KILLED &&
-                       si->si_code != CLD_DUMPED)
-       {
-               /* ignore traps, stops and continues, we only want terminations
-                * of the client process */
-               return;
-       }
-
-       pthread_mutex_lock(&_mero_topdp_lock);
-
-       /* get the pid from the former child, and locate it in our list */
-       q = _mero_topdp->next;
-       p = q->next;
-       while (p != NULL) {
-               if (p->pid == si->si_pid) {
-                       /* log everything that's still in the pipes */
-                       logFD(p->out, "MSG", p->dbname, (long long int)p->pid, 
_mero_logfile);
-                       /* remove from the list */
-                       q->next = p->next;
-                       /* close the descriptors */
-                       close(p->out);
-                       close(p->err);
-                       if (si->si_code == CLD_EXITED) {
-                               Mfprintf(stdout, "database '%s' (%lld) has 
exited with "
-                                               "exit status %d\n", p->dbname,
-                                               (long long int)p->pid, 
si->si_status);
-                       } else if (si->si_code == CLD_KILLED) {
-                               const char *sigstr = sigtostr(si->si_status);
-                               char signum[8];
-                               if (sigstr == NULL) {
-                                       snprintf(signum, 8, "%d", 
si->si_status);
-                                       sigstr = signum;
-                               }
-                               Mfprintf(stdout, "database '%s' (%lld) was 
killed by signal "
-                                               "%s\n", p->dbname,
-                                               (long long int)p->pid, sigstr);
-                       } else if (si->si_code == CLD_DUMPED) {
-                               Mfprintf(stdout, "database '%s' (%lld) has 
crashed "
-                                               "(dumped core)\n", p->dbname,
-                                               (long long int)p->pid);
-                       }
-                       if (p->dbname)
-                               free(p->dbname);
-                       free(p);
-                       pthread_mutex_unlock(&_mero_topdp_lock);
-                       return;
-               }
-               q = p;
-               p = q->next;
-       }
-
-       pthread_mutex_unlock(&_mero_topdp_lock);
-
-       Mfprintf(stdout, "received SIGCHLD from unknown child with pid %lld\n",
-                       (long long int)si->si_pid);
 }
 
 /**
diff --git a/tools/merovingian/daemon/handlers.h 
b/tools/merovingian/daemon/handlers.h
--- a/tools/merovingian/daemon/handlers.h
+++ b/tools/merovingian/daemon/handlers.h
@@ -13,7 +13,7 @@
 
 void handler(int sig);
 void huphandler(int sig);
-void childhandler(int sig, siginfo_t *si, void *unused);
+void childhandler(void);
 void segvhandler(int sig);
 
 #endif
diff --git a/tools/merovingian/daemon/merovingian.c 
b/tools/merovingian/daemon/merovingian.c
--- a/tools/merovingian/daemon/merovingian.c
+++ b/tools/merovingian/daemon/merovingian.c
@@ -888,15 +888,6 @@ main(int argc, char *argv[])
                MERO_EXIT(1);
        }
 
-       sa.sa_flags = SA_SIGINFO;
-       sigemptyset(&sa.sa_mask);
-       sa.sa_sigaction = childhandler;
-       if (sigaction(SIGCHLD, &sa, NULL) == -1) {
-               Mfprintf(oerr, "%s: FATAL: unable to create signal handlers: 
%s\n",
-                               argv[0], strerror(errno));
-               MERO_EXIT(1);
-       }
-
        /* make sure we will be able to write our pid */
        if ((pidfile = fopen(pidfilename, "w")) == NULL) {
                Mfprintf(stderr, "unable to open '%s%s%s' for writing: %s\n",
_______________________________________________
checkin-list mailing list
[email protected]
https://www.monetdb.org/mailman/listinfo/checkin-list

Reply via email to