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