1 /*-------------------------------------------------------------------------
2  *
3  * Facilities for frontend code to connect to and disconnect from databases.
4  *
5  * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
6  * Portions Copyright (c) 1994, Regents of the University of California
7  *
8  * src/fe_utils/connect_utils.c
9  *
10  *-------------------------------------------------------------------------
11  */
12 #include "postgres_fe.h"
13 
14 #include "common/connect.h"
15 #include "common/logging.h"
16 #include "common/string.h"
17 #include "fe_utils/connect_utils.h"
18 #include "fe_utils/query_utils.h"
19 
20 /*
21  * Make a database connection with the given parameters.
22  *
23  * An interactive password prompt is automatically issued if needed and
24  * allowed by cparams->prompt_password.
25  *
26  * If allow_password_reuse is true, we will try to re-use any password
27  * given during previous calls to this routine.  (Callers should not pass
28  * allow_password_reuse=true unless reconnecting to the same database+user
29  * as before, else we might create password exposure hazards.)
30  */
31 PGconn *
connectDatabase(const ConnParams * cparams,const char * progname,bool echo,bool fail_ok,bool allow_password_reuse)32 connectDatabase(const ConnParams *cparams, const char *progname,
33 				bool echo, bool fail_ok, bool allow_password_reuse)
34 {
35 	PGconn	   *conn;
36 	bool		new_pass;
37 	static char *password = NULL;
38 
39 	/* Callers must supply at least dbname; other params can be NULL */
40 	Assert(cparams->dbname);
41 
42 	if (!allow_password_reuse && password)
43 	{
44 		free(password);
45 		password = NULL;
46 	}
47 
48 	if (cparams->prompt_password == TRI_YES && password == NULL)
49 		password = simple_prompt("Password: ", false);
50 
51 	/*
52 	 * Start the connection.  Loop until we have a password if requested by
53 	 * backend.
54 	 */
55 	do
56 	{
57 		const char *keywords[8];
58 		const char *values[8];
59 		int			i = 0;
60 
61 		/*
62 		 * If dbname is a connstring, its entries can override the other
63 		 * values obtained from cparams; but in turn, override_dbname can
64 		 * override the dbname component of it.
65 		 */
66 		keywords[i] = "host";
67 		values[i++] = cparams->pghost;
68 		keywords[i] = "port";
69 		values[i++] = cparams->pgport;
70 		keywords[i] = "user";
71 		values[i++] = cparams->pguser;
72 		keywords[i] = "password";
73 		values[i++] = password;
74 		keywords[i] = "dbname";
75 		values[i++] = cparams->dbname;
76 		if (cparams->override_dbname)
77 		{
78 			keywords[i] = "dbname";
79 			values[i++] = cparams->override_dbname;
80 		}
81 		keywords[i] = "fallback_application_name";
82 		values[i++] = progname;
83 		keywords[i] = NULL;
84 		values[i++] = NULL;
85 		Assert(i <= lengthof(keywords));
86 
87 		new_pass = false;
88 		conn = PQconnectdbParams(keywords, values, true);
89 
90 		if (!conn)
91 		{
92 			pg_log_error("could not connect to database %s: out of memory",
93 						 cparams->dbname);
94 			exit(1);
95 		}
96 
97 		/*
98 		 * No luck?  Trying asking (again) for a password.
99 		 */
100 		if (PQstatus(conn) == CONNECTION_BAD &&
101 			PQconnectionNeedsPassword(conn) &&
102 			cparams->prompt_password != TRI_NO)
103 		{
104 			PQfinish(conn);
105 			if (password)
106 				free(password);
107 			password = simple_prompt("Password: ", false);
108 			new_pass = true;
109 		}
110 	} while (new_pass);
111 
112 	/* check to see that the backend connection was successfully made */
113 	if (PQstatus(conn) == CONNECTION_BAD)
114 	{
115 		if (fail_ok)
116 		{
117 			PQfinish(conn);
118 			return NULL;
119 		}
120 		pg_log_error("%s", PQerrorMessage(conn));
121 		exit(1);
122 	}
123 
124 	/* Start strict; callers may override this. */
125 	PQclear(executeQuery(conn, ALWAYS_SECURE_SEARCH_PATH_SQL, echo));
126 
127 	return conn;
128 }
129 
130 /*
131  * Try to connect to the appropriate maintenance database.
132  *
133  * This differs from connectDatabase only in that it has a rule for
134  * inserting a default "dbname" if none was given (which is why cparams
135  * is not const).  Note that cparams->dbname should typically come from
136  * a --maintenance-db command line parameter.
137  */
138 PGconn *
connectMaintenanceDatabase(ConnParams * cparams,const char * progname,bool echo)139 connectMaintenanceDatabase(ConnParams *cparams,
140 						   const char *progname, bool echo)
141 {
142 	PGconn	   *conn;
143 
144 	/* If a maintenance database name was specified, just connect to it. */
145 	if (cparams->dbname)
146 		return connectDatabase(cparams, progname, echo, false, false);
147 
148 	/* Otherwise, try postgres first and then template1. */
149 	cparams->dbname = "postgres";
150 	conn = connectDatabase(cparams, progname, echo, true, false);
151 	if (!conn)
152 	{
153 		cparams->dbname = "template1";
154 		conn = connectDatabase(cparams, progname, echo, false, false);
155 	}
156 	return conn;
157 }
158 
159 /*
160  * Disconnect the given connection, canceling any statement if one is active.
161  */
162 void
disconnectDatabase(PGconn * conn)163 disconnectDatabase(PGconn *conn)
164 {
165 	char		errbuf[256];
166 
167 	Assert(conn != NULL);
168 
169 	if (PQtransactionStatus(conn) == PQTRANS_ACTIVE)
170 	{
171 		PGcancel   *cancel;
172 
173 		if ((cancel = PQgetCancel(conn)))
174 		{
175 			(void) PQcancel(cancel, errbuf, sizeof(errbuf));
176 			PQfreeCancel(cancel);
177 		}
178 	}
179 
180 	PQfinish(conn);
181 }
182