1 | /* |
2 | * server.c |
3 | * |
4 | * database server functions |
5 | * |
6 | * Copyright (c) 2010-2019, PostgreSQL Global Development Group |
7 | * src/bin/pg_upgrade/server.c |
8 | */ |
9 | |
10 | #include "postgres_fe.h" |
11 | |
12 | #include "fe_utils/connect.h" |
13 | #include "fe_utils/string_utils.h" |
14 | #include "pg_upgrade.h" |
15 | |
16 | |
17 | static PGconn *get_db_conn(ClusterInfo *cluster, const char *db_name); |
18 | |
19 | |
20 | /* |
21 | * connectToServer() |
22 | * |
23 | * Connects to the desired database on the designated server. |
24 | * If the connection attempt fails, this function logs an error |
25 | * message and calls exit() to kill the program. |
26 | */ |
27 | PGconn * |
28 | connectToServer(ClusterInfo *cluster, const char *db_name) |
29 | { |
30 | PGconn *conn = get_db_conn(cluster, db_name); |
31 | |
32 | if (conn == NULL || PQstatus(conn) != CONNECTION_OK) |
33 | { |
34 | pg_log(PG_REPORT, "connection to database failed: %s" , |
35 | PQerrorMessage(conn)); |
36 | |
37 | if (conn) |
38 | PQfinish(conn); |
39 | |
40 | printf(_("Failure, exiting\n" )); |
41 | exit(1); |
42 | } |
43 | |
44 | PQclear(executeQueryOrDie(conn, ALWAYS_SECURE_SEARCH_PATH_SQL)); |
45 | |
46 | return conn; |
47 | } |
48 | |
49 | |
50 | /* |
51 | * get_db_conn() |
52 | * |
53 | * get database connection, using named database + standard params for cluster |
54 | */ |
55 | static PGconn * |
56 | get_db_conn(ClusterInfo *cluster, const char *db_name) |
57 | { |
58 | PQExpBufferData conn_opts; |
59 | PGconn *conn; |
60 | |
61 | /* Build connection string with proper quoting */ |
62 | initPQExpBuffer(&conn_opts); |
63 | appendPQExpBufferStr(&conn_opts, "dbname=" ); |
64 | appendConnStrVal(&conn_opts, db_name); |
65 | appendPQExpBufferStr(&conn_opts, " user=" ); |
66 | appendConnStrVal(&conn_opts, os_info.user); |
67 | appendPQExpBuffer(&conn_opts, " port=%d" , cluster->port); |
68 | if (cluster->sockdir) |
69 | { |
70 | appendPQExpBufferStr(&conn_opts, " host=" ); |
71 | appendConnStrVal(&conn_opts, cluster->sockdir); |
72 | } |
73 | |
74 | conn = PQconnectdb(conn_opts.data); |
75 | termPQExpBuffer(&conn_opts); |
76 | return conn; |
77 | } |
78 | |
79 | |
80 | /* |
81 | * cluster_conn_opts() |
82 | * |
83 | * Return standard command-line options for connecting to this cluster when |
84 | * using psql, pg_dump, etc. Ideally this would match what get_db_conn() |
85 | * sets, but the utilities we need aren't very consistent about the treatment |
86 | * of database name options, so we leave that out. |
87 | * |
88 | * Result is valid until the next call to this function. |
89 | */ |
90 | char * |
91 | cluster_conn_opts(ClusterInfo *cluster) |
92 | { |
93 | static PQExpBuffer buf; |
94 | |
95 | if (buf == NULL) |
96 | buf = createPQExpBuffer(); |
97 | else |
98 | resetPQExpBuffer(buf); |
99 | |
100 | if (cluster->sockdir) |
101 | { |
102 | appendPQExpBufferStr(buf, "--host " ); |
103 | appendShellString(buf, cluster->sockdir); |
104 | appendPQExpBufferChar(buf, ' '); |
105 | } |
106 | appendPQExpBuffer(buf, "--port %d --username " , cluster->port); |
107 | appendShellString(buf, os_info.user); |
108 | |
109 | return buf->data; |
110 | } |
111 | |
112 | |
113 | /* |
114 | * executeQueryOrDie() |
115 | * |
116 | * Formats a query string from the given arguments and executes the |
117 | * resulting query. If the query fails, this function logs an error |
118 | * message and calls exit() to kill the program. |
119 | */ |
120 | PGresult * |
121 | executeQueryOrDie(PGconn *conn, const char *fmt,...) |
122 | { |
123 | static char query[QUERY_ALLOC]; |
124 | va_list args; |
125 | PGresult *result; |
126 | ExecStatusType status; |
127 | |
128 | va_start(args, fmt); |
129 | vsnprintf(query, sizeof(query), fmt, args); |
130 | va_end(args); |
131 | |
132 | pg_log(PG_VERBOSE, "executing: %s\n" , query); |
133 | result = PQexec(conn, query); |
134 | status = PQresultStatus(result); |
135 | |
136 | if ((status != PGRES_TUPLES_OK) && (status != PGRES_COMMAND_OK)) |
137 | { |
138 | pg_log(PG_REPORT, "SQL command failed\n%s\n%s" , query, |
139 | PQerrorMessage(conn)); |
140 | PQclear(result); |
141 | PQfinish(conn); |
142 | printf(_("Failure, exiting\n" )); |
143 | exit(1); |
144 | } |
145 | else |
146 | return result; |
147 | } |
148 | |
149 | |
150 | /* |
151 | * get_major_server_version() |
152 | * |
153 | * gets the version (in unsigned int form) for the given datadir. Assumes |
154 | * that datadir is an absolute path to a valid pgdata directory. The version |
155 | * is retrieved by reading the PG_VERSION file. |
156 | */ |
157 | uint32 |
158 | get_major_server_version(ClusterInfo *cluster) |
159 | { |
160 | FILE *version_fd; |
161 | char ver_filename[MAXPGPATH]; |
162 | int v1 = 0, |
163 | v2 = 0; |
164 | |
165 | snprintf(ver_filename, sizeof(ver_filename), "%s/PG_VERSION" , |
166 | cluster->pgdata); |
167 | if ((version_fd = fopen(ver_filename, "r" )) == NULL) |
168 | pg_fatal("could not open version file: %s\n" , ver_filename); |
169 | |
170 | if (fscanf(version_fd, "%63s" , cluster->major_version_str) == 0 || |
171 | sscanf(cluster->major_version_str, "%d.%d" , &v1, &v2) < 1) |
172 | pg_fatal("could not parse PG_VERSION file from %s\n" , cluster->pgdata); |
173 | |
174 | fclose(version_fd); |
175 | |
176 | if (v1 < 10) |
177 | { |
178 | /* old style, e.g. 9.6.1 */ |
179 | return v1 * 10000 + v2 * 100; |
180 | } |
181 | else |
182 | { |
183 | /* new style, e.g. 10.1 */ |
184 | return v1 * 10000; |
185 | } |
186 | } |
187 | |
188 | |
189 | static void |
190 | stop_postmaster_atexit(void) |
191 | { |
192 | stop_postmaster(true); |
193 | } |
194 | |
195 | |
196 | bool |
197 | start_postmaster(ClusterInfo *cluster, bool report_and_exit_on_error) |
198 | { |
199 | char cmd[MAXPGPATH * 4 + 1000]; |
200 | PGconn *conn; |
201 | bool pg_ctl_return = false; |
202 | char socket_string[MAXPGPATH + 200]; |
203 | |
204 | static bool exit_hook_registered = false; |
205 | |
206 | if (!exit_hook_registered) |
207 | { |
208 | atexit(stop_postmaster_atexit); |
209 | exit_hook_registered = true; |
210 | } |
211 | |
212 | socket_string[0] = '\0'; |
213 | |
214 | #ifdef HAVE_UNIX_SOCKETS |
215 | /* prevent TCP/IP connections, restrict socket access */ |
216 | strcat(socket_string, |
217 | " -c listen_addresses='' -c unix_socket_permissions=0700" ); |
218 | |
219 | /* Have a sockdir? Tell the postmaster. */ |
220 | if (cluster->sockdir) |
221 | snprintf(socket_string + strlen(socket_string), |
222 | sizeof(socket_string) - strlen(socket_string), |
223 | " -c %s='%s'" , |
224 | (GET_MAJOR_VERSION(cluster->major_version) < 903) ? |
225 | "unix_socket_directory" : "unix_socket_directories" , |
226 | cluster->sockdir); |
227 | #endif |
228 | |
229 | /* |
230 | * Since PG 9.1, we have used -b to disable autovacuum. For earlier |
231 | * releases, setting autovacuum=off disables cleanup vacuum and analyze, |
232 | * but freeze vacuums can still happen, so we set |
233 | * autovacuum_freeze_max_age to its maximum. |
234 | * (autovacuum_multixact_freeze_max_age was introduced after 9.1, so there |
235 | * is no need to set that.) We assume all datfrozenxid and relfrozenxid |
236 | * values are less than a gap of 2000000000 from the current xid counter, |
237 | * so autovacuum will not touch them. |
238 | * |
239 | * Turn off durability requirements to improve object creation speed, and |
240 | * we only modify the new cluster, so only use it there. If there is a |
241 | * crash, the new cluster has to be recreated anyway. fsync=off is a big |
242 | * win on ext4. |
243 | */ |
244 | snprintf(cmd, sizeof(cmd), |
245 | "\"%s/pg_ctl\" -w -l \"%s\" -D \"%s\" -o \"-p %d%s%s %s%s\" start" , |
246 | cluster->bindir, SERVER_LOG_FILE, cluster->pgconfig, cluster->port, |
247 | (cluster->controldata.cat_ver >= |
248 | BINARY_UPGRADE_SERVER_FLAG_CAT_VER) ? " -b" : |
249 | " -c autovacuum=off -c autovacuum_freeze_max_age=2000000000" , |
250 | (cluster == &new_cluster) ? |
251 | " -c synchronous_commit=off -c fsync=off -c full_page_writes=off" : "" , |
252 | cluster->pgopts ? cluster->pgopts : "" , socket_string); |
253 | |
254 | /* |
255 | * Don't throw an error right away, let connecting throw the error because |
256 | * it might supply a reason for the failure. |
257 | */ |
258 | pg_ctl_return = exec_prog(SERVER_START_LOG_FILE, |
259 | /* pass both file names if they differ */ |
260 | (strcmp(SERVER_LOG_FILE, |
261 | SERVER_START_LOG_FILE) != 0) ? |
262 | SERVER_LOG_FILE : NULL, |
263 | report_and_exit_on_error, false, |
264 | "%s" , cmd); |
265 | |
266 | /* Did it fail and we are just testing if the server could be started? */ |
267 | if (!pg_ctl_return && !report_and_exit_on_error) |
268 | return false; |
269 | |
270 | /* |
271 | * We set this here to make sure atexit() shuts down the server, but only |
272 | * if we started the server successfully. We do it before checking for |
273 | * connectivity in case the server started but there is a connectivity |
274 | * failure. If pg_ctl did not return success, we will exit below. |
275 | * |
276 | * Pre-9.1 servers do not have PQping(), so we could be leaving the server |
277 | * running if authentication was misconfigured, so someday we might went |
278 | * to be more aggressive about doing server shutdowns even if pg_ctl |
279 | * fails, but now (2013-08-14) it seems prudent to be cautious. We don't |
280 | * want to shutdown a server that might have been accidentally started |
281 | * during the upgrade. |
282 | */ |
283 | if (pg_ctl_return) |
284 | os_info.running_cluster = cluster; |
285 | |
286 | /* |
287 | * pg_ctl -w might have failed because the server couldn't be started, or |
288 | * there might have been a connection problem in _checking_ if the server |
289 | * has started. Therefore, even if pg_ctl failed, we continue and test |
290 | * for connectivity in case we get a connection reason for the failure. |
291 | */ |
292 | if ((conn = get_db_conn(cluster, "template1" )) == NULL || |
293 | PQstatus(conn) != CONNECTION_OK) |
294 | { |
295 | pg_log(PG_REPORT, "\nconnection to database failed: %s" , |
296 | PQerrorMessage(conn)); |
297 | if (conn) |
298 | PQfinish(conn); |
299 | if (cluster == &old_cluster) |
300 | pg_fatal("could not connect to source postmaster started with the command:\n" |
301 | "%s\n" , |
302 | cmd); |
303 | else |
304 | pg_fatal("could not connect to target postmaster started with the command:\n" |
305 | "%s\n" , |
306 | cmd); |
307 | } |
308 | PQfinish(conn); |
309 | |
310 | /* |
311 | * If pg_ctl failed, and the connection didn't fail, and |
312 | * report_and_exit_on_error is enabled, fail now. This could happen if |
313 | * the server was already running. |
314 | */ |
315 | if (!pg_ctl_return) |
316 | { |
317 | if (cluster == &old_cluster) |
318 | pg_fatal("pg_ctl failed to start the source server, or connection failed\n" ); |
319 | else |
320 | pg_fatal("pg_ctl failed to start the target server, or connection failed\n" ); |
321 | } |
322 | |
323 | return true; |
324 | } |
325 | |
326 | |
327 | void |
328 | stop_postmaster(bool in_atexit) |
329 | { |
330 | ClusterInfo *cluster; |
331 | |
332 | if (os_info.running_cluster == &old_cluster) |
333 | cluster = &old_cluster; |
334 | else if (os_info.running_cluster == &new_cluster) |
335 | cluster = &new_cluster; |
336 | else |
337 | return; /* no cluster running */ |
338 | |
339 | exec_prog(SERVER_STOP_LOG_FILE, NULL, !in_atexit, !in_atexit, |
340 | "\"%s/pg_ctl\" -w -D \"%s\" -o \"%s\" %s stop" , |
341 | cluster->bindir, cluster->pgconfig, |
342 | cluster->pgopts ? cluster->pgopts : "" , |
343 | in_atexit ? "-m fast" : "-m smart" ); |
344 | |
345 | os_info.running_cluster = NULL; |
346 | } |
347 | |
348 | |
349 | /* |
350 | * check_pghost_envvar() |
351 | * |
352 | * Tests that PGHOST does not point to a non-local server |
353 | */ |
354 | void |
355 | check_pghost_envvar(void) |
356 | { |
357 | PQconninfoOption *option; |
358 | PQconninfoOption *start; |
359 | |
360 | /* Get valid libpq env vars from the PQconndefaults function */ |
361 | |
362 | start = PQconndefaults(); |
363 | |
364 | if (!start) |
365 | pg_fatal("out of memory\n" ); |
366 | |
367 | for (option = start; option->keyword != NULL; option++) |
368 | { |
369 | if (option->envvar && (strcmp(option->envvar, "PGHOST" ) == 0 || |
370 | strcmp(option->envvar, "PGHOSTADDR" ) == 0)) |
371 | { |
372 | const char *value = getenv(option->envvar); |
373 | |
374 | if (value && strlen(value) > 0 && |
375 | /* check for 'local' host values */ |
376 | (strcmp(value, "localhost" ) != 0 && strcmp(value, "127.0.0.1" ) != 0 && |
377 | strcmp(value, "::1" ) != 0 && value[0] != '/')) |
378 | pg_fatal("libpq environment variable %s has a non-local server value: %s\n" , |
379 | option->envvar, value); |
380 | } |
381 | } |
382 | |
383 | /* Free the memory that libpq allocated on our behalf */ |
384 | PQconninfoFree(start); |
385 | } |
386 | |