Fix single-user mode so that interrupts (particularly SIGTERM and
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 9 Jul 2007 01:15:14 +0000 (01:15 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 9 Jul 2007 01:15:14 +0000 (01:15 +0000)
SIGQUIT) will be recognized and processed while waiting for input,
rather than only after something has been typed.  Also make SIGQUIT
do the same thing as SIGTERM in single-user mode, ie, do a normal
shutdown and exit.  Since it's relatively easy to provoke SIGQUIT
from the keyboard, people may try that instead of control-D, and we'd
rather this leads to orderly shutdown.  Per report from Leon Mergen
and subsequent discussion.

src/backend/tcop/postgres.c

index c5ede140042cacdbe1512f655b1819756e61b299..58f1613975d24d9f3fbff29ecf72fa564c704c1f 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.535 2007/06/29 17:07:39 alvherre Exp $
+ *   $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.536 2007/07/09 01:15:14 tgl Exp $
  *
  * NOTES
  *   this is the "main" module of the postgres backend and
@@ -164,6 +164,7 @@ static int  UseNewLine = 0;     /* Use EOF as query delimiters */
  * ----------------------------------------------------------------
  */
 static int InteractiveBackend(StringInfo inBuf);
+static int interactive_getc(void);
 static int SocketBackend(StringInfo inBuf);
 static int ReadCommand(StringInfo inBuf);
 static List *pg_rewrite_query(Query *query);
@@ -210,64 +211,61 @@ InteractiveBackend(StringInfo inBuf)
 
    resetStringInfo(inBuf);
 
-   for (;;)
+   if (UseNewLine)
    {
-       if (UseNewLine)
+       /*
+        * if we are using \n as a delimiter, then read characters until
+        * the \n.
+        */
+       while ((c = interactive_getc()) != EOF)
        {
-           /*
-            * if we are using \n as a delimiter, then read characters until
-            * the \n.
-            */
-           while ((c = getc(stdin)) != EOF)
+           if (c == '\n')
            {
-               if (c == '\n')
+               if (backslashSeen)
                {
-                   if (backslashSeen)
-                   {
-                       /* discard backslash from inBuf */
-                       inBuf->data[--inBuf->len] = '\0';
-                       backslashSeen = false;
-                       continue;
-                   }
-                   else
-                   {
-                       /* keep the newline character */
-                       appendStringInfoChar(inBuf, '\n');
-                       break;
-                   }
+                   /* discard backslash from inBuf */
+                   inBuf->data[--inBuf->len] = '\0';
+                   backslashSeen = false;
+                   continue;
                }
-               else if (c == '\\')
-                   backslashSeen = true;
                else
-                   backslashSeen = false;
-
-               appendStringInfoChar(inBuf, (char) c);
+               {
+                   /* keep the newline character */
+                   appendStringInfoChar(inBuf, '\n');
+                   break;
+               }
            }
+           else if (c == '\\')
+               backslashSeen = true;
+           else
+               backslashSeen = false;
 
-           if (c == EOF)
-               end = true;
-       }
-       else
-       {
-           /*
-            * otherwise read characters until EOF.
-            */
-           while ((c = getc(stdin)) != EOF)
-               appendStringInfoChar(inBuf, (char) c);
-
-           if (inBuf->len == 0)
-               end = true;
+           appendStringInfoChar(inBuf, (char) c);
        }
 
-       if (end)
-           return EOF;
-
+       if (c == EOF)
+           end = true;
+   }
+   else
+   {
        /*
-        * otherwise we have a user query so process it.
+        * otherwise read characters until EOF.
         */
-       break;
+       while ((c = interactive_getc()) != EOF)
+           appendStringInfoChar(inBuf, (char) c);
+
+       /* No input before EOF signal means time to quit. */
+       if (inBuf->len == 0)
+           end = true;
    }
 
+   if (end)
+       return EOF;
+
+   /*
+    * otherwise we have a user query so process it.
+    */
+
    /* Add '\0' to make it look the same as message case. */
    appendStringInfoChar(inBuf, (char) '\0');
 
@@ -281,6 +279,24 @@ InteractiveBackend(StringInfo inBuf)
    return 'Q';
 }
 
+/*
+ * interactive_getc -- collect one character from stdin
+ *
+ * Even though we are not reading from a "client" process, we still want to
+ * respond to signals, particularly SIGTERM/SIGQUIT.  Hence we must use
+ * prepare_for_client_read and client_read_ended.
+ */
+static int
+interactive_getc(void)
+{
+   int         c;
+
+   prepare_for_client_read();
+   c = getc(stdin);
+   client_read_ended();
+   return c;
+}
+
 /* ----------------
  * SocketBackend()     Is called for frontend-backend connections
  *
@@ -3092,7 +3108,16 @@ PostgresMain(int argc, char *argv[], const char *username)
    pqsignal(SIGHUP, SigHupHandler);    /* set flag to read config file */
    pqsignal(SIGINT, StatementCancelHandler);   /* cancel current query */
    pqsignal(SIGTERM, die);     /* cancel current query and exit */
-   pqsignal(SIGQUIT, quickdie);    /* hard crash time */
+
+   /*
+    * In a standalone backend, SIGQUIT can be generated from the keyboard
+    * easily, while SIGTERM cannot, so we make both signals do die() rather
+    * than quickdie().
+    */
+   if (IsUnderPostmaster)
+       pqsignal(SIGQUIT, quickdie);    /* hard crash time */
+   else
+       pqsignal(SIGQUIT, die);     /* cancel current query and exit */
    pqsignal(SIGALRM, handle_sig_alarm);        /* timeout conditions */
 
    /*
@@ -3113,12 +3138,15 @@ PostgresMain(int argc, char *argv[], const char *username)
 
    pqinitmask();
 
-   /* We allow SIGQUIT (quickdie) at all times */
+   if (IsUnderPostmaster)
+   {
+       /* We allow SIGQUIT (quickdie) at all times */
 #ifdef HAVE_SIGPROCMASK
-   sigdelset(&BlockSig, SIGQUIT);
+       sigdelset(&BlockSig, SIGQUIT);
 #else
-   BlockSig &= ~(sigmask(SIGQUIT));
+       BlockSig &= ~(sigmask(SIGQUIT));
 #endif
+   }
 
    PG_SETMASK(&BlockSig);      /* block everything except SIGQUIT */