Skip to content

Commit 06aee9c

Browse files
committed
Replace simple_prompt() by pgut_simple().
Properly handle Ctrl+C.
1 parent 469e8f0 commit 06aee9c

File tree

2 files changed

+236
-28
lines changed

2 files changed

+236
-28
lines changed

src/utils/pgut.c

Lines changed: 236 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,13 @@
1414
#include "getopt_long.h"
1515
#include <limits.h>
1616
#include <time.h>
17+
#include <stdio.h>
1718
#include <unistd.h>
1819

20+
#ifdef HAVE_TERMIOS_H
21+
#include <termios.h>
22+
#endif
23+
1924
#include "logger.h"
2025
#include "pgut.h"
2126

@@ -47,7 +52,6 @@ static PGcancel *volatile cancel_conn = NULL;
4752
/* Interrupted by SIGINT (Ctrl+C) ? */
4853
bool interrupted = false;
4954
bool in_cleanup = false;
50-
bool in_password = false;
5155

5256
static bool parse_pair(const char buffer[], char key[], char value[]);
5357

@@ -1073,6 +1077,235 @@ parse_pair(const char buffer[], char key[], char value[])
10731077
return true;
10741078
}
10751079

1080+
/*
1081+
* pgut_prompt
1082+
*
1083+
* Generalized function especially intended for reading in usernames and
1084+
* passwords interactively. Reads from /dev/tty or stdin/stderr.
1085+
*
1086+
* prompt: The prompt to print, or NULL if none (automatically localized)
1087+
* destination: buffer in which to store result
1088+
* destlen: allocated length of destination
1089+
* echo: Set to false if you want to hide what is entered (for passwords)
1090+
*
1091+
* The input (without trailing newline) is returned in the destination buffer,
1092+
* with a '\0' appended.
1093+
*
1094+
* Copy of simple_prompt().
1095+
*/
1096+
static void
1097+
pgut_prompt(const char *prompt, char *destination, size_t destlen, bool echo)
1098+
{
1099+
size_t length = 0;
1100+
char *ptr = destination;
1101+
FILE *termin,
1102+
*termout;
1103+
bool exit_with_error = false;
1104+
1105+
#ifdef HAVE_TERMIOS_H
1106+
struct termios t_orig,
1107+
t;
1108+
#else
1109+
#ifdef WIN32
1110+
HANDLE t = NULL;
1111+
DWORD t_orig = 0;
1112+
#endif
1113+
#endif
1114+
1115+
#ifdef WIN32
1116+
1117+
/*
1118+
* A Windows console has an "input code page" and an "output code page";
1119+
* these usually match each other, but they rarely match the "Windows ANSI
1120+
* code page" defined at system boot and expected of "char *" arguments to
1121+
* Windows API functions. The Microsoft CRT write() implementation
1122+
* automatically converts text between these code pages when writing to a
1123+
* console. To identify such file descriptors, it calls GetConsoleMode()
1124+
* on the underlying HANDLE, which in turn requires GENERIC_READ access on
1125+
* the HANDLE. Opening termout in mode "w+" allows that detection to
1126+
* succeed. Otherwise, write() would not recognize the descriptor as a
1127+
* console, and non-ASCII characters would display incorrectly.
1128+
*
1129+
* XXX fgets() still receives text in the console's input code page. This
1130+
* makes non-ASCII credentials unportable.
1131+
*/
1132+
termin = fopen("CONIN$", "r");
1133+
termout = fopen("CONOUT$", "w+");
1134+
#else
1135+
1136+
/*
1137+
* Do not try to collapse these into one "w+" mode file. Doesn't work on
1138+
* some platforms (eg, HPUX 10.20).
1139+
*/
1140+
termin = fopen("/dev/tty", "r");
1141+
termout = fopen("/dev/tty", "w");
1142+
#endif
1143+
if (!termin || !termout
1144+
#ifdef WIN32
1145+
1146+
/*
1147+
* Direct console I/O does not work from the MSYS 1.0.10 console. Writes
1148+
* reach nowhere user-visible; reads block indefinitely. XXX This affects
1149+
* most Windows terminal environments, including rxvt, mintty, Cygwin
1150+
* xterm, Cygwin sshd, and PowerShell ISE. Switch to a more-generic test.
1151+
*/
1152+
|| (getenv("OSTYPE") && strcmp(getenv("OSTYPE"), "msys") == 0)
1153+
#endif
1154+
)
1155+
{
1156+
if (termin)
1157+
fclose(termin);
1158+
if (termout)
1159+
fclose(termout);
1160+
termin = stdin;
1161+
termout = stderr;
1162+
}
1163+
1164+
#ifdef HAVE_TERMIOS_H
1165+
if (!echo)
1166+
{
1167+
tcgetattr(fileno(termin), &t);
1168+
t_orig = t;
1169+
t.c_lflag &= ~ECHO;
1170+
tcsetattr(fileno(termin), TCSAFLUSH, &t);
1171+
}
1172+
#else
1173+
#ifdef WIN32
1174+
if (!echo)
1175+
{
1176+
/* get a new handle to turn echo off */
1177+
t = GetStdHandle(STD_INPUT_HANDLE);
1178+
1179+
/* save the old configuration first */
1180+
GetConsoleMode(t, &t_orig);
1181+
1182+
/* set to the new mode */
1183+
SetConsoleMode(t, ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT);
1184+
}
1185+
#endif
1186+
#endif
1187+
1188+
if (!pg_set_noblock(fileno(termin)))
1189+
elog(ERROR, "could not set terminal to nonblocking mode");
1190+
1191+
if (prompt)
1192+
{
1193+
fputs(_(prompt), termout);
1194+
fflush(termout);
1195+
}
1196+
1197+
if (interrupted)
1198+
{
1199+
exit_with_error = true;
1200+
goto cleanup;
1201+
}
1202+
1203+
do
1204+
{
1205+
int selres;
1206+
struct timeval timeout;
1207+
1208+
timeout.tv_sec = 0;
1209+
timeout.tv_usec = 100 * 1000L; /* 100 milliseconds */
1210+
1211+
selres = wait_for_socket(fileno(termin), &timeout);
1212+
if (selres < 0 || interrupted)
1213+
{
1214+
exit_with_error = true;
1215+
goto cleanup;
1216+
}
1217+
1218+
if (selres > 0)
1219+
{
1220+
size_t len = fread(ptr, sizeof(char), destlen - length, termin);
1221+
1222+
if (len <= 0)
1223+
{
1224+
exit_with_error = true;
1225+
goto cleanup;
1226+
}
1227+
1228+
length += len;
1229+
ptr += len;
1230+
}
1231+
1232+
if (length >0 && destination[length - 1] == '\n')
1233+
break;
1234+
} while (length < destlen);
1235+
1236+
if (length > 0 && destination[length - 1] != '\n')
1237+
{
1238+
/* eat rest of the line */
1239+
int selres;
1240+
struct timeval timeout;
1241+
1242+
while (true)
1243+
{
1244+
timeout.tv_sec = 0;
1245+
timeout.tv_usec = 100 * 1000L; /* 100 milliseconds */
1246+
1247+
selres = wait_for_socket(fileno(termin), &timeout);
1248+
if (selres < 0 || interrupted)
1249+
{
1250+
exit_with_error = true;
1251+
goto cleanup;
1252+
}
1253+
1254+
if (selres > 0)
1255+
{
1256+
char buf[128];
1257+
size_t len = fread(buf, sizeof(char), sizeof(buf), termin);
1258+
1259+
if (len <= 0)
1260+
{
1261+
exit_with_error = true;
1262+
goto cleanup;
1263+
}
1264+
1265+
if (buf[len - 1] == '\n')
1266+
break;
1267+
}
1268+
}
1269+
}
1270+
1271+
if (length > 0 && destination[length - 1] == '\n')
1272+
/* remove trailing newline */
1273+
destination[length - 1] = '\0';
1274+
1275+
cleanup:
1276+
1277+
#ifdef HAVE_TERMIOS_H
1278+
if (!echo)
1279+
{
1280+
tcsetattr(fileno(termin), TCSAFLUSH, &t_orig);
1281+
fputs("\n", termout);
1282+
fflush(termout);
1283+
}
1284+
#else
1285+
#ifdef WIN32
1286+
if (!echo)
1287+
{
1288+
/* reset to the original console mode */
1289+
SetConsoleMode(t, t_orig);
1290+
fputs("\n", termout);
1291+
fflush(termout);
1292+
}
1293+
#endif
1294+
#endif
1295+
1296+
if (!pg_set_block(fileno(termin)))
1297+
elog(ERROR, "could not set terminal to blocking mode");
1298+
1299+
if (termin != stdin)
1300+
{
1301+
fclose(termin);
1302+
fclose(termout);
1303+
}
1304+
1305+
if (exit_with_error)
1306+
exit_or_abort(ERROR);
1307+
}
1308+
10761309
/*
10771310
* Ask the user for a password; 'username' is the username the
10781311
* password is for, if one has been explicitly specified.
@@ -1081,36 +1314,21 @@ parse_pair(const char buffer[], char key[], char value[])
10811314
static void
10821315
prompt_for_password(const char *username)
10831316
{
1084-
in_password = true;
1085-
10861317
if (password)
10871318
{
10881319
free(password);
10891320
password = NULL;
10901321
}
10911322

1092-
#if PG_VERSION_NUM >= 100000
10931323
password = (char *) pgut_malloc(sizeof(char) * 100 + 1);
10941324
if (username == NULL)
1095-
simple_prompt("Password: ", password, 100, false);
1096-
else
1097-
{
1098-
char message[256];
1099-
snprintf(message, lengthof(message), "Password for user %s: ", username);
1100-
simple_prompt(message, password, 100, false);
1101-
}
1102-
#else
1103-
if (username == NULL)
1104-
password = simple_prompt("Password: ", 100, false);
1325+
pgut_prompt("Password: ", password, 100, false);
11051326
else
11061327
{
11071328
char message[256];
11081329
snprintf(message, lengthof(message), "Password for user %s: ", username);
1109-
password = simple_prompt(message, 100, false);
1330+
pgut_prompt(message, password, 100, false);
11101331
}
1111-
#endif
1112-
1113-
in_password = false;
11141332
}
11151333

11161334
PGconn *
@@ -1530,15 +1748,6 @@ on_interrupt(void)
15301748
/* Set interruped flag */
15311749
interrupted = true;
15321750

1533-
/* User promts password, call on_cleanup() byhand */
1534-
if (in_password)
1535-
{
1536-
on_cleanup();
1537-
1538-
pqsignal(SIGINT, oldhandler);
1539-
kill(0, SIGINT);
1540-
}
1541-
15421751
/* Send QueryCancel if we are processing a database query */
15431752
if (!in_cleanup && cancel_conn != NULL &&
15441753
PQcancel(cancel_conn, errbuf, sizeof(errbuf)))

src/utils/pgut.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,6 @@ extern bool prompt_password;
105105

106106
extern bool interrupted;
107107
extern bool in_cleanup;
108-
extern bool in_password; /* User prompts password */
109108

110109
extern int pgut_getopt(int argc, char **argv, pgut_option options[]);
111110
extern void pgut_readopt(const char *path, pgut_option options[], int elevel);

0 commit comments

Comments
 (0)