From 9e09e3b15eb2673c96a2a3f92133311bba0e949c Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Mon, 9 Jul 2007 01:15:14 +0000
Subject: [PATCH] Fix single-user mode so that interrupts (particularly SIGTERM
 and 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 | 126 ++++++++++++++++++++++--------------
 1 file changed, 77 insertions(+), 49 deletions(-)

diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index c5ede140042..58f1613975d 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -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 */
 
-- 
GitLab