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