1 /*-------------------------------------------------------------------------
2  *
3  *	common.c
4  *		Common support routines for bin/scripts/
5  *
6  *
7  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
8  * Portions Copyright (c) 1994, Regents of the University of California
9  *
10  * src/bin/scripts/common.c
11  *
12  *-------------------------------------------------------------------------
13  */
14 
15 #include "postgres_fe.h"
16 
17 #include <signal.h>
18 #include <unistd.h>
19 
20 #include "common.h"
21 #include "fe_utils/connect.h"
22 #include "fe_utils/string_utils.h"
23 
24 #define PQmblenBounded(s, e)  strnlen(s, PQmblen(s, e))
25 
26 
27 static PGcancel *volatile cancelConn = NULL;
28 bool		CancelRequested = false;
29 
30 #ifdef WIN32
31 static CRITICAL_SECTION cancelConnLock;
32 #endif
33 
34 /*
35  * Provide strictly harmonized handling of --help and --version
36  * options.
37  */
38 void
handle_help_version_opts(int argc,char * argv[],const char * fixed_progname,help_handler hlp)39 handle_help_version_opts(int argc, char *argv[],
40 						 const char *fixed_progname, help_handler hlp)
41 {
42 	if (argc > 1)
43 	{
44 		if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
45 		{
46 			hlp(get_progname(argv[0]));
47 			exit(0);
48 		}
49 		if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
50 		{
51 			printf("%s (PostgreSQL) " PG_VERSION "\n", fixed_progname);
52 			exit(0);
53 		}
54 	}
55 }
56 
57 
58 /*
59  * Make a database connection with the given parameters.
60  *
61  * An interactive password prompt is automatically issued if needed and
62  * allowed by cparams->prompt_password.
63  *
64  * If allow_password_reuse is true, we will try to re-use any password
65  * given during previous calls to this routine.  (Callers should not pass
66  * allow_password_reuse=true unless reconnecting to the same database+user
67  * as before, else we might create password exposure hazards.)
68  */
69 PGconn *
connectDatabase(const ConnParams * cparams,const char * progname,bool echo,bool fail_ok,bool allow_password_reuse)70 connectDatabase(const ConnParams *cparams, const char *progname,
71 				bool echo, bool fail_ok, bool allow_password_reuse)
72 {
73 	PGconn	   *conn;
74 	static char *password = NULL;
75 	bool		new_pass;
76 
77 	/* Callers must supply at least dbname; other params can be NULL */
78 	Assert(cparams->dbname);
79 
80 	if (!allow_password_reuse)
81 	{
82 		if (password)
83 			free(password);
84 		password = NULL;
85 	}
86 
87 	if (cparams->prompt_password == TRI_YES && password == NULL)
88 		password = simple_prompt("Password: ", 100, false);
89 
90 	/*
91 	 * Start the connection.  Loop until we have a password if requested by
92 	 * backend.
93 	 */
94 	do
95 	{
96 		const char *keywords[8];
97 		const char *values[8];
98 		int			i = 0;
99 
100 		/*
101 		 * If dbname is a connstring, its entries can override the other
102 		 * values obtained from cparams; but in turn, override_dbname can
103 		 * override the dbname component of it.
104 		 */
105 		keywords[i] = "host";
106 		values[i++] = cparams->pghost;
107 		keywords[i] = "port";
108 		values[i++] = cparams->pgport;
109 		keywords[i] = "user";
110 		values[i++] = cparams->pguser;
111 		keywords[i] = "password";
112 		values[i++] = password;
113 		keywords[i] = "dbname";
114 		values[i++] = cparams->dbname;
115 		if (cparams->override_dbname)
116 		{
117 			keywords[i] = "dbname";
118 			values[i++] = cparams->override_dbname;
119 		}
120 		keywords[i] = "fallback_application_name";
121 		values[i++] = progname;
122 		keywords[i] = NULL;
123 		values[i++] = NULL;
124 		Assert(i <= lengthof(keywords));
125 
126 		new_pass = false;
127 		conn = PQconnectdbParams(keywords, values, true);
128 
129 		if (!conn)
130 		{
131 			fprintf(stderr, _("%s: could not connect to database %s: out of memory\n"),
132 					progname, cparams->dbname);
133 			exit(1);
134 		}
135 
136 		/*
137 		 * No luck?  Trying asking (again) for a password.
138 		 */
139 		if (PQstatus(conn) == CONNECTION_BAD &&
140 			PQconnectionNeedsPassword(conn) &&
141 			cparams->prompt_password != TRI_NO)
142 		{
143 			PQfinish(conn);
144 			if (password)
145 				free(password);
146 			password = simple_prompt("Password: ", 100, false);
147 			new_pass = true;
148 		}
149 	} while (new_pass);
150 
151 	/* check to see that the backend connection was successfully made */
152 	if (PQstatus(conn) == CONNECTION_BAD)
153 	{
154 		if (fail_ok)
155 		{
156 			PQfinish(conn);
157 			return NULL;
158 		}
159 		fprintf(stderr, _("%s: could not connect to database %s: %s"),
160 				progname, cparams->dbname, PQerrorMessage(conn));
161 		exit(1);
162 	}
163 
164 	/* Start strict; callers may override this. */
165 	PQclear(executeQuery(conn, ALWAYS_SECURE_SEARCH_PATH_SQL,
166 						 progname, echo));
167 
168 	return conn;
169 }
170 
171 /*
172  * Try to connect to the appropriate maintenance database.
173  *
174  * This differs from connectDatabase only in that it has a rule for
175  * inserting a default "dbname" if none was given (which is why cparams
176  * is not const).  Note that cparams->dbname should typically come from
177  * a --maintenance-db command line parameter.
178  */
179 PGconn *
connectMaintenanceDatabase(ConnParams * cparams,const char * progname,bool echo)180 connectMaintenanceDatabase(ConnParams *cparams,
181 						   const char *progname, bool echo)
182 {
183 	PGconn	   *conn;
184 
185 	/* If a maintenance database name was specified, just connect to it. */
186 	if (cparams->dbname)
187 		return connectDatabase(cparams, progname, echo, false, false);
188 
189 	/* Otherwise, try postgres first and then template1. */
190 	cparams->dbname = "postgres";
191 	conn = connectDatabase(cparams, progname, echo, true, false);
192 	if (!conn)
193 	{
194 		cparams->dbname = "template1";
195 		conn = connectDatabase(cparams, progname, echo, false, false);
196 	}
197 	return conn;
198 }
199 
200 /*
201  * Run a query, return the results, exit program on failure.
202  */
203 PGresult *
executeQuery(PGconn * conn,const char * query,const char * progname,bool echo)204 executeQuery(PGconn *conn, const char *query, const char *progname, bool echo)
205 {
206 	PGresult   *res;
207 
208 	if (echo)
209 		printf("%s\n", query);
210 
211 	res = PQexec(conn, query);
212 	if (!res ||
213 		PQresultStatus(res) != PGRES_TUPLES_OK)
214 	{
215 		fprintf(stderr, _("%s: query failed: %s"),
216 				progname, PQerrorMessage(conn));
217 		fprintf(stderr, _("%s: query was: %s\n"),
218 				progname, query);
219 		PQfinish(conn);
220 		exit(1);
221 	}
222 
223 	return res;
224 }
225 
226 
227 /*
228  * As above for a SQL command (which returns nothing).
229  */
230 void
executeCommand(PGconn * conn,const char * query,const char * progname,bool echo)231 executeCommand(PGconn *conn, const char *query,
232 			   const char *progname, bool echo)
233 {
234 	PGresult   *res;
235 
236 	if (echo)
237 		printf("%s\n", query);
238 
239 	res = PQexec(conn, query);
240 	if (!res ||
241 		PQresultStatus(res) != PGRES_COMMAND_OK)
242 	{
243 		fprintf(stderr, _("%s: query failed: %s"),
244 				progname, PQerrorMessage(conn));
245 		fprintf(stderr, _("%s: query was: %s\n"),
246 				progname, query);
247 		PQfinish(conn);
248 		exit(1);
249 	}
250 
251 	PQclear(res);
252 }
253 
254 
255 /*
256  * As above for a SQL maintenance command (returns command success).
257  * Command is executed with a cancel handler set, so Ctrl-C can
258  * interrupt it.
259  */
260 bool
executeMaintenanceCommand(PGconn * conn,const char * query,bool echo)261 executeMaintenanceCommand(PGconn *conn, const char *query, bool echo)
262 {
263 	PGresult   *res;
264 	bool		r;
265 
266 	if (echo)
267 		printf("%s\n", query);
268 
269 	SetCancelConn(conn);
270 	res = PQexec(conn, query);
271 	ResetCancelConn();
272 
273 	r = (res && PQresultStatus(res) == PGRES_COMMAND_OK);
274 
275 	if (res)
276 		PQclear(res);
277 
278 	return r;
279 }
280 
281 
282 /*
283  * Split TABLE[(COLUMNS)] into TABLE and [(COLUMNS)] portions.  When you
284  * finish using them, pg_free(*table).  *columns is a pointer into "spec",
285  * possibly to its NUL terminator.
286  */
287 static void
split_table_columns_spec(const char * spec,int encoding,char ** table,const char ** columns)288 split_table_columns_spec(const char *spec, int encoding,
289 						 char **table, const char **columns)
290 {
291 	bool		inquotes = false;
292 	const char *cp = spec;
293 
294 	/*
295 	 * Find the first '(' not identifier-quoted.  Based on
296 	 * dequote_downcase_identifier().
297 	 */
298 	while (*cp && (*cp != '(' || inquotes))
299 	{
300 		if (*cp == '"')
301 		{
302 			if (inquotes && cp[1] == '"')
303 				cp++;			/* pair does not affect quoting */
304 			else
305 				inquotes = !inquotes;
306 			cp++;
307 		}
308 		else
309 			cp += PQmblenBounded(cp, encoding);
310 	}
311 	*table = pg_strdup(spec);
312 	(*table)[cp - spec] = '\0'; /* no strndup */
313 	*columns = cp;
314 }
315 
316 /*
317  * Break apart TABLE[(COLUMNS)] of "spec".  With the reset_val of search_path
318  * in effect, have regclassin() interpret the TABLE portion.  Append to "buf"
319  * the qualified name of TABLE, followed by any (COLUMNS).  Exit on failure.
320  * We use this to interpret --table=foo under the search path psql would get,
321  * in advance of "ANALYZE public.foo" under the always-secure search path.
322  */
323 void
appendQualifiedRelation(PQExpBuffer buf,const char * spec,PGconn * conn,const char * progname,bool echo)324 appendQualifiedRelation(PQExpBuffer buf, const char *spec,
325 						PGconn *conn, const char *progname, bool echo)
326 {
327 	char	   *table;
328 	const char *columns;
329 	PQExpBufferData sql;
330 	PGresult   *res;
331 	int			ntups;
332 
333 	split_table_columns_spec(spec, PQclientEncoding(conn), &table, &columns);
334 
335 	/*
336 	 * Query must remain ABSOLUTELY devoid of unqualified names.  This would
337 	 * be unnecessary given a regclassin() variant taking a search_path
338 	 * argument.
339 	 */
340 	initPQExpBuffer(&sql);
341 	appendPQExpBufferStr(&sql,
342 						 "SELECT c.relname, ns.nspname\n"
343 						 " FROM pg_catalog.pg_class c,"
344 						 " pg_catalog.pg_namespace ns\n"
345 						 " WHERE c.relnamespace OPERATOR(pg_catalog.=) ns.oid\n"
346 						 "  AND c.oid OPERATOR(pg_catalog.=) ");
347 	appendStringLiteralConn(&sql, table, conn);
348 	appendPQExpBufferStr(&sql, "::pg_catalog.regclass;");
349 
350 	executeCommand(conn, "RESET search_path", progname, echo);
351 
352 	/*
353 	 * One row is a typical result, as is a nonexistent relation ERROR.
354 	 * regclassin() unconditionally accepts all-digits input as an OID; if no
355 	 * relation has that OID; this query returns no rows.  Catalog corruption
356 	 * might elicit other row counts.
357 	 */
358 	res = executeQuery(conn, sql.data, progname, echo);
359 	ntups = PQntuples(res);
360 	if (ntups != 1)
361 	{
362 		fprintf(stderr,
363 				ngettext("%s: query returned %d row instead of one: %s\n",
364 						 "%s: query returned %d rows instead of one: %s\n",
365 						 ntups),
366 				progname, ntups, sql.data);
367 		PQfinish(conn);
368 		exit(1);
369 	}
370 	appendPQExpBufferStr(buf,
371 						 fmtQualifiedId(PQserverVersion(conn),
372 										PQgetvalue(res, 0, 1),
373 										PQgetvalue(res, 0, 0)));
374 	appendPQExpBufferStr(buf, columns);
375 	PQclear(res);
376 	termPQExpBuffer(&sql);
377 	pg_free(table);
378 
379 	PQclear(executeQuery(conn, ALWAYS_SECURE_SEARCH_PATH_SQL,
380 						 progname, echo));
381 }
382 
383 
384 /*
385  * Check yes/no answer in a localized way.  1=yes, 0=no, -1=neither.
386  */
387 
388 /* translator: abbreviation for "yes" */
389 #define PG_YESLETTER gettext_noop("y")
390 /* translator: abbreviation for "no" */
391 #define PG_NOLETTER gettext_noop("n")
392 
393 bool
yesno_prompt(const char * question)394 yesno_prompt(const char *question)
395 {
396 	char		prompt[256];
397 
398 	/*------
399 	   translator: This is a question followed by the translated options for
400 	   "yes" and "no". */
401 	snprintf(prompt, sizeof(prompt), _("%s (%s/%s) "),
402 			 _(question), _(PG_YESLETTER), _(PG_NOLETTER));
403 
404 	for (;;)
405 	{
406 		char	   *resp;
407 
408 		resp = simple_prompt(prompt, 1, true);
409 
410 		if (strcmp(resp, _(PG_YESLETTER)) == 0)
411 		{
412 			free(resp);
413 			return true;
414 		}
415 		else if (strcmp(resp, _(PG_NOLETTER)) == 0)
416 		{
417 			free(resp);
418 			return false;
419 		}
420 
421 		free(resp);
422 		printf(_("Please answer \"%s\" or \"%s\".\n"),
423 			   _(PG_YESLETTER), _(PG_NOLETTER));
424 	}
425 }
426 
427 /*
428  * SetCancelConn
429  *
430  * Set cancelConn to point to the current database connection.
431  */
432 void
SetCancelConn(PGconn * conn)433 SetCancelConn(PGconn *conn)
434 {
435 	PGcancel   *oldCancelConn;
436 
437 #ifdef WIN32
438 	EnterCriticalSection(&cancelConnLock);
439 #endif
440 
441 	/* Free the old one if we have one */
442 	oldCancelConn = cancelConn;
443 
444 	/* be sure handle_sigint doesn't use pointer while freeing */
445 	cancelConn = NULL;
446 
447 	if (oldCancelConn != NULL)
448 		PQfreeCancel(oldCancelConn);
449 
450 	cancelConn = PQgetCancel(conn);
451 
452 #ifdef WIN32
453 	LeaveCriticalSection(&cancelConnLock);
454 #endif
455 }
456 
457 /*
458  * ResetCancelConn
459  *
460  * Free the current cancel connection, if any, and set to NULL.
461  */
462 void
ResetCancelConn(void)463 ResetCancelConn(void)
464 {
465 	PGcancel   *oldCancelConn;
466 
467 #ifdef WIN32
468 	EnterCriticalSection(&cancelConnLock);
469 #endif
470 
471 	oldCancelConn = cancelConn;
472 
473 	/* be sure handle_sigint doesn't use pointer while freeing */
474 	cancelConn = NULL;
475 
476 	if (oldCancelConn != NULL)
477 		PQfreeCancel(oldCancelConn);
478 
479 #ifdef WIN32
480 	LeaveCriticalSection(&cancelConnLock);
481 #endif
482 }
483 
484 #ifndef WIN32
485 /*
486  * Handle interrupt signals by canceling the current command, if a cancelConn
487  * is set.
488  */
489 static void
handle_sigint(SIGNAL_ARGS)490 handle_sigint(SIGNAL_ARGS)
491 {
492 	int			save_errno = errno;
493 	char		errbuf[256];
494 
495 	/* Send QueryCancel if we are processing a database query */
496 	if (cancelConn != NULL)
497 	{
498 		if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
499 		{
500 			CancelRequested = true;
501 			fprintf(stderr, _("Cancel request sent\n"));
502 		}
503 		else
504 			fprintf(stderr, _("Could not send cancel request: %s"), errbuf);
505 	}
506 	else
507 		CancelRequested = true;
508 
509 	errno = save_errno;			/* just in case the write changed it */
510 }
511 
512 void
setup_cancel_handler(void)513 setup_cancel_handler(void)
514 {
515 	pqsignal(SIGINT, handle_sigint);
516 }
517 #else							/* WIN32 */
518 
519 /*
520  * Console control handler for Win32. Note that the control handler will
521  * execute on a *different thread* than the main one, so we need to do
522  * proper locking around those structures.
523  */
524 static BOOL WINAPI
consoleHandler(DWORD dwCtrlType)525 consoleHandler(DWORD dwCtrlType)
526 {
527 	char		errbuf[256];
528 
529 	if (dwCtrlType == CTRL_C_EVENT ||
530 		dwCtrlType == CTRL_BREAK_EVENT)
531 	{
532 		/* Send QueryCancel if we are processing a database query */
533 		EnterCriticalSection(&cancelConnLock);
534 		if (cancelConn != NULL)
535 		{
536 			if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
537 			{
538 				fprintf(stderr, _("Cancel request sent\n"));
539 				CancelRequested = true;
540 			}
541 			else
542 				fprintf(stderr, _("Could not send cancel request: %s"), errbuf);
543 		}
544 		else
545 			CancelRequested = true;
546 
547 		LeaveCriticalSection(&cancelConnLock);
548 
549 		return TRUE;
550 	}
551 	else
552 		/* Return FALSE for any signals not being handled */
553 		return FALSE;
554 }
555 
556 void
setup_cancel_handler(void)557 setup_cancel_handler(void)
558 {
559 	InitializeCriticalSection(&cancelConnLock);
560 
561 	SetConsoleCtrlHandler(consoleHandler, TRUE);
562 }
563 
564 #endif   /* WIN32 */
565