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) ? */
4853bool interrupted = false;
4954bool in_cleanup = false;
50- bool in_password = false;
5155
5256static 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[])
10811314static void
10821315prompt_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
11161334PGconn *
@@ -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 )))
0 commit comments