Allow psql's other uses of simple_prompt() to be interrupted by ^C.

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
This commit is contained in:
Tom Lane 2021-11-19 12:11:38 -05:00
parent 3b34645678
commit 46d665bc26
3 changed files with 43 additions and 11 deletions

View File

@ -2132,6 +2132,12 @@ exec_command_prompt(PsqlScanState scan_state, bool active_branch,
else else
{ {
char *result; 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) if (arg2)
{ {
@ -2143,7 +2149,7 @@ exec_command_prompt(PsqlScanState scan_state, bool active_branch,
if (!pset.inputfile) if (!pset.inputfile)
{ {
result = simple_prompt(prompt_text, true); result = simple_prompt_extended(prompt_text, true, &prompt_ctx);
} }
else else
{ {
@ -2161,8 +2167,8 @@ exec_command_prompt(PsqlScanState scan_state, bool active_branch,
} }
} }
if (result && if (prompt_ctx.canceled ||
!SetVariable(pset.vars, opt, result)) (result && !SetVariable(pset.vars, opt, result)))
success = false; success = false;
if (result) 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 * Ask the user for a password; 'username' is the username the
* password is for, if one has been explicitly specified. Returns a * password is for, if one has been explicitly specified.
* malloc'd string. * 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 * static char *
prompt_for_password(const char *username) prompt_for_password(const char *username, bool *canceled)
{ {
char *result; 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') if (username == NULL || username[0] == '\0')
result = simple_prompt("Password: ", false); result = simple_prompt_extended("Password: ", false, &prompt_ctx);
else else
{ {
char *prompt_text; char *prompt_text;
prompt_text = psprintf(_("Password for user %s: "), username); 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); free(prompt_text);
} }
if (canceled)
*canceled = prompt_ctx.canceled;
return result; return result;
} }
@ -3331,6 +3349,8 @@ do_connect(enum trivalue reuse_previous_specification,
*/ */
if (pset.getPassword == TRI_YES && success) if (pset.getPassword == TRI_YES && success)
{ {
bool canceled = false;
/* /*
* If a connstring or URI is provided, we don't know which username * 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. * 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 * not seem worth working harder, since this getPassword setting is
* normally only used in noninteractive cases. * 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) if (!password && PQconnectionNeedsPassword(n_conn) && pset.getPassword != TRI_NO)
{ {
bool canceled = false;
/* /*
* Prompt for password using the username we actually connected * Prompt for password using the username we actually connected
* with --- it might've come out of "dbname" rather than "user". * 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); PQfinish(n_conn);
n_conn = NULL; n_conn = NULL;
success = !canceled;
continue; continue;
} }

View 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 * 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 * 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); password = simple_prompt("Password: ", false);
} }

View File

@ -164,6 +164,12 @@ simple_prompt_extended(const char *prompt, bool echo,
fflush(termout); fflush(termout);
#endif #endif
} }
else if (prompt_ctx && prompt_ctx->canceled)
{
/* also echo \n if prompt was canceled */
fputs("\n", termout);
fflush(termout);
}
if (termin != stdin) if (termin != stdin)
{ {