Allow psql's other uses of simple_prompt() to be interrupted by ^C.
authorTom Lane <[email protected]>
Fri, 19 Nov 2021 17:11:38 +0000 (12:11 -0500)
committerTom Lane <[email protected]>
Fri, 19 Nov 2021 17:11:46 +0000 (12:11 -0500)
This fills in the work left un-done by 5f1148224.  \prompt can
be canceled out of now, and so can password prompts issued during
\connect.  (We don't need to do anything for password prompts
issued during startup, because we aren't yet trapping SIGINT
at that point.)

Nathan Bossart

Discussion: https://postgr.es/m/747443.1635536754@sss.pgh.pa.us

src/bin/psql/command.c
src/bin/psql/startup.c
src/common/sprompt.c

index 102bc5956b7b48db9c5b6cae68c981bd87c4e332..f5b2cd12c0f67679127d474e7e7b4f40432cdbcb 100644 (file)
@@ -2132,6 +2132,12 @@ exec_command_prompt(PsqlScanState scan_state, bool active_branch,
        else
        {
            char       *result;
+           PromptInterruptContext prompt_ctx;
+
+           /* Set up to let SIGINT cancel simple_prompt_extended() */
+           prompt_ctx.jmpbuf = sigint_interrupt_jmp;
+           prompt_ctx.enabled = &sigint_interrupt_enabled;
+           prompt_ctx.canceled = false;
 
            if (arg2)
            {
@@ -2143,7 +2149,7 @@ exec_command_prompt(PsqlScanState scan_state, bool active_branch,
 
            if (!pset.inputfile)
            {
-               result = simple_prompt(prompt_text, true);
+               result = simple_prompt_extended(prompt_text, true, &prompt_ctx);
            }
            else
            {
@@ -2161,8 +2167,8 @@ exec_command_prompt(PsqlScanState scan_state, bool active_branch,
                }
            }
 
-           if (result &&
-               !SetVariable(pset.vars, opt, result))
+           if (prompt_ctx.canceled ||
+               (result && !SetVariable(pset.vars, opt, result)))
                success = false;
 
            if (result)
@@ -3058,24 +3064,36 @@ copy_previous_query(PQExpBuffer query_buf, PQExpBuffer previous_buf)
 
 /*
  * Ask the user for a password; 'username' is the username the
- * password is for, if one has been explicitly specified. Returns a
- * malloc'd string.
+ * password is for, if one has been explicitly specified.
+ * Returns a malloc'd string.
+ * If 'canceled' is provided, *canceled will be set to true if the prompt
+ * is canceled via SIGINT, and to false otherwise.
  */
 static char *
-prompt_for_password(const char *username)
+prompt_for_password(const char *username, bool *canceled)
 {
    char       *result;
+   PromptInterruptContext prompt_ctx;
+
+   /* Set up to let SIGINT cancel simple_prompt_extended() */
+   prompt_ctx.jmpbuf = sigint_interrupt_jmp;
+   prompt_ctx.enabled = &sigint_interrupt_enabled;
+   prompt_ctx.canceled = false;
 
    if (username == NULL || username[0] == '\0')
-       result = simple_prompt("Password: ", false);
+       result = simple_prompt_extended("Password: ", false, &prompt_ctx);
    else
    {
        char       *prompt_text;
 
        prompt_text = psprintf(_("Password for user %s: "), username);
-       result = simple_prompt(prompt_text, false);
+       result = simple_prompt_extended(prompt_text, false, &prompt_ctx);
        free(prompt_text);
    }
+
+   if (canceled)
+       *canceled = prompt_ctx.canceled;
+
    return result;
 }
 
@@ -3331,6 +3349,8 @@ do_connect(enum trivalue reuse_previous_specification,
     */
    if (pset.getPassword == TRI_YES && success)
    {
+       bool        canceled = false;
+
        /*
         * If a connstring or URI is provided, we don't know which username
         * will be used, since we haven't dug that out of the connstring.
@@ -3338,7 +3358,9 @@ do_connect(enum trivalue reuse_previous_specification,
         * not seem worth working harder, since this getPassword setting is
         * normally only used in noninteractive cases.
         */
-       password = prompt_for_password(has_connection_string ? NULL : user);
+       password = prompt_for_password(has_connection_string ? NULL : user,
+                                      &canceled);
+       success = !canceled;
    }
 
    /*
@@ -3417,13 +3439,16 @@ do_connect(enum trivalue reuse_previous_specification,
         */
        if (!password && PQconnectionNeedsPassword(n_conn) && pset.getPassword != TRI_NO)
        {
+           bool        canceled = false;
+
            /*
             * Prompt for password using the username we actually connected
             * with --- it might've come out of "dbname" rather than "user".
             */
-           password = prompt_for_password(PQuser(n_conn));
+           password = prompt_for_password(PQuser(n_conn), &canceled);
            PQfinish(n_conn);
            n_conn = NULL;
+           success = !canceled;
            continue;
        }
 
index 2931530f3385379623925e7e2a729c9d3dca18e5..f7ea4ce3d46d74c72a51303e882927be346b9703 100644 (file)
@@ -239,7 +239,8 @@ main(int argc, char *argv[])
        /*
         * We can't be sure yet of the username that will be used, so don't
         * offer a potentially wrong one.  Typical uses of this option are
-        * noninteractive anyway.
+        * noninteractive anyway.  (Note: since we've not yet set up our
+        * cancel handler, there's no need to use simple_prompt_extended.)
         */
        password = simple_prompt("Password: ", false);
    }
index 917676b58ceee8476fe170db48258a7f3458e4e8..8f5aeeccbc0f08e7ebe518c7be4495d4014b5b83 100644 (file)
@@ -164,6 +164,12 @@ simple_prompt_extended(const char *prompt, bool echo,
        fflush(termout);
 #endif
    }
+   else if (prompt_ctx && prompt_ctx->canceled)
+   {
+       /* also echo \n if prompt was canceled */
+       fputs("\n", termout);
+       fflush(termout);
+   }
 
    if (termin != stdin)
    {