From 9e09e3b15eb2673c96a2a3f92133311bba0e949c Mon Sep 17 00:00:00 2001 From: Tom Lane 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 c5ede14004..58f1613975 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 */ -- 2.11.0