Line data Source code
1 : /*
2 : * exec.c
3 : *
4 : * execution functions
5 : *
6 : * Copyright (c) 2010-2024, PostgreSQL Global Development Group
7 : * src/bin/pg_upgrade/exec.c
8 : */
9 :
10 : #include "postgres_fe.h"
11 :
12 : #include <fcntl.h>
13 :
14 : #include "common/string.h"
15 : #include "pg_upgrade.h"
16 :
17 : static void check_data_dir(ClusterInfo *cluster);
18 : static void check_bin_dir(ClusterInfo *cluster, bool check_versions);
19 : static void get_bin_version(ClusterInfo *cluster);
20 : static void check_exec(const char *dir, const char *program, bool check_version);
21 :
22 : #ifdef WIN32
23 : static int win32_check_directory_write_permissions(void);
24 : #endif
25 :
26 :
27 : /*
28 : * get_bin_version
29 : *
30 : * Fetch major version of binaries for cluster.
31 : */
32 : static void
33 36 : get_bin_version(ClusterInfo *cluster)
34 : {
35 : char cmd[MAXPGPATH],
36 : cmd_output[MAX_STRING];
37 : FILE *output;
38 : int rc;
39 36 : int v1 = 0,
40 36 : v2 = 0;
41 :
42 36 : snprintf(cmd, sizeof(cmd), "\"%s/pg_ctl\" --version", cluster->bindir);
43 36 : fflush(NULL);
44 :
45 72 : if ((output = popen(cmd, "r")) == NULL ||
46 36 : fgets(cmd_output, sizeof(cmd_output), output) == NULL)
47 0 : pg_fatal("could not get pg_ctl version data using %s: %m", cmd);
48 :
49 36 : rc = pclose(output);
50 36 : if (rc != 0)
51 0 : pg_fatal("could not get pg_ctl version data using %s: %s",
52 : cmd, wait_result_to_str(rc));
53 :
54 36 : if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
55 0 : pg_fatal("could not get pg_ctl version output from %s", cmd);
56 :
57 36 : if (v1 < 10)
58 : {
59 : /* old style, e.g. 9.6.1 */
60 0 : cluster->bin_version = v1 * 10000 + v2 * 100;
61 : }
62 : else
63 : {
64 : /* new style, e.g. 10.1 */
65 36 : cluster->bin_version = v1 * 10000;
66 : }
67 36 : }
68 :
69 :
70 : /*
71 : * exec_prog()
72 : * Execute an external program with stdout/stderr redirected, and report
73 : * errors
74 : *
75 : * Formats a command from the given argument list, logs it to the log file,
76 : * and attempts to execute that command. If the command executes
77 : * successfully, exec_prog() returns true.
78 : *
79 : * If the command fails, an error message is optionally written to the specified
80 : * log_file, and the program optionally exits.
81 : *
82 : * The code requires it be called first from the primary thread on Windows.
83 : */
84 : bool
85 218 : exec_prog(const char *log_filename, const char *opt_log_file,
86 : bool report_error, bool exit_on_error, const char *fmt,...)
87 : {
88 218 : int result = 0;
89 : int written;
90 : char log_file[MAXPGPATH];
91 :
92 : #define MAXCMDLEN (2 * MAXPGPATH)
93 : char cmd[MAXCMDLEN];
94 : FILE *log;
95 : va_list ap;
96 :
97 : #ifdef WIN32
98 : static DWORD mainThreadId = 0;
99 :
100 : /* We assume we are called from the primary thread first */
101 : if (mainThreadId == 0)
102 : mainThreadId = GetCurrentThreadId();
103 : #endif
104 :
105 218 : snprintf(log_file, MAXPGPATH, "%s/%s", log_opts.logdir, log_filename);
106 :
107 218 : written = 0;
108 218 : va_start(ap, fmt);
109 218 : written += vsnprintf(cmd + written, MAXCMDLEN - written, fmt, ap);
110 218 : va_end(ap);
111 218 : if (written >= MAXCMDLEN)
112 0 : pg_fatal("command too long");
113 218 : written += snprintf(cmd + written, MAXCMDLEN - written,
114 : " >> \"%s\" 2>&1", log_file);
115 218 : if (written >= MAXCMDLEN)
116 0 : pg_fatal("command too long");
117 :
118 218 : pg_log(PG_VERBOSE, "%s", cmd);
119 :
120 : #ifdef WIN32
121 :
122 : /*
123 : * For some reason, Windows issues a file-in-use error if we write data to
124 : * the log file from a non-primary thread just before we create a
125 : * subprocess that also writes to the same log file. One fix is to sleep
126 : * for 100ms. A cleaner fix is to write to the log file _after_ the
127 : * subprocess has completed, so we do this only when writing from a
128 : * non-primary thread. fflush(), running system() twice, and pre-creating
129 : * the file do not see to help.
130 : */
131 : if (mainThreadId != GetCurrentThreadId())
132 : {
133 : fflush(NULL);
134 : result = system(cmd);
135 : }
136 : #endif
137 :
138 218 : log = fopen(log_file, "a");
139 :
140 : #ifdef WIN32
141 : {
142 : /*
143 : * "pg_ctl -w stop" might have reported that the server has stopped
144 : * because the postmaster.pid file has been removed, but "pg_ctl -w
145 : * start" might still be in the process of closing and might still be
146 : * holding its stdout and -l log file descriptors open. Therefore,
147 : * try to open the log file a few more times.
148 : */
149 : int iter;
150 :
151 : for (iter = 0; iter < 4 && log == NULL; iter++)
152 : {
153 : pg_usleep(1000000); /* 1 sec */
154 : log = fopen(log_file, "a");
155 : }
156 : }
157 : #endif
158 :
159 218 : if (log == NULL)
160 0 : pg_fatal("could not open log file \"%s\": %m", log_file);
161 :
162 : #ifdef WIN32
163 : /* Are we printing "command:" before its output? */
164 : if (mainThreadId == GetCurrentThreadId())
165 : fprintf(log, "\n\n");
166 : #endif
167 218 : fprintf(log, "command: %s\n", cmd);
168 : #ifdef WIN32
169 : /* Are we printing "command:" after its output? */
170 : if (mainThreadId != GetCurrentThreadId())
171 : fprintf(log, "\n\n");
172 : #endif
173 :
174 : /*
175 : * In Windows, we must close the log file at this point so the file is not
176 : * open while the command is running, or we get a share violation.
177 : */
178 218 : fclose(log);
179 :
180 : #ifdef WIN32
181 : /* see comment above */
182 : if (mainThreadId == GetCurrentThreadId())
183 : #endif
184 : {
185 218 : fflush(NULL);
186 218 : result = system(cmd);
187 : }
188 :
189 218 : if (result != 0 && report_error)
190 : {
191 : /* we might be in on a progress status line, so go to the next line */
192 0 : report_status(PG_REPORT, "\n*failure*");
193 0 : fflush(stdout);
194 :
195 0 : pg_log(PG_VERBOSE, "There were problems executing \"%s\"", cmd);
196 0 : if (opt_log_file)
197 0 : pg_log(exit_on_error ? PG_FATAL : PG_REPORT,
198 : "Consult the last few lines of \"%s\" or \"%s\" for\n"
199 : "the probable cause of the failure.",
200 : log_file, opt_log_file);
201 : else
202 0 : pg_log(exit_on_error ? PG_FATAL : PG_REPORT,
203 : "Consult the last few lines of \"%s\" for\n"
204 : "the probable cause of the failure.",
205 : log_file);
206 : }
207 :
208 : #ifndef WIN32
209 :
210 : /*
211 : * We can't do this on Windows because it will keep the "pg_ctl start"
212 : * output filename open until the server stops, so we do the \n\n above on
213 : * that platform. We use a unique filename for "pg_ctl start" that is
214 : * never reused while the server is running, so it works fine. We could
215 : * log these commands to a third file, but that just adds complexity.
216 : */
217 218 : if ((log = fopen(log_file, "a")) == NULL)
218 0 : pg_fatal("could not write to log file \"%s\": %m", log_file);
219 218 : fprintf(log, "\n\n");
220 218 : fclose(log);
221 : #endif
222 :
223 218 : return result == 0;
224 : }
225 :
226 :
227 : /*
228 : * pid_lock_file_exists()
229 : *
230 : * Checks whether the postmaster.pid file exists.
231 : */
232 : bool
233 36 : pid_lock_file_exists(const char *datadir)
234 : {
235 : char path[MAXPGPATH];
236 : int fd;
237 :
238 36 : snprintf(path, sizeof(path), "%s/postmaster.pid", datadir);
239 :
240 36 : if ((fd = open(path, O_RDONLY, 0)) < 0)
241 : {
242 : /* ENOTDIR means we will throw a more useful error later */
243 36 : if (errno != ENOENT && errno != ENOTDIR)
244 0 : pg_fatal("could not open file \"%s\" for reading: %m", path);
245 :
246 36 : return false;
247 : }
248 :
249 0 : close(fd);
250 0 : return true;
251 : }
252 :
253 :
254 : /*
255 : * verify_directories()
256 : *
257 : * does all the hectic work of verifying directories and executables
258 : * of old and new server.
259 : *
260 : * NOTE: May update the values of all parameters
261 : */
262 : void
263 20 : verify_directories(void)
264 : {
265 : #ifndef WIN32
266 20 : if (access(".", R_OK | W_OK | X_OK) != 0)
267 : #else
268 : if (win32_check_directory_write_permissions() != 0)
269 : #endif
270 0 : pg_fatal("You must have read and write access in the current directory.");
271 :
272 20 : check_bin_dir(&old_cluster, false);
273 18 : check_data_dir(&old_cluster);
274 18 : check_bin_dir(&new_cluster, true);
275 18 : check_data_dir(&new_cluster);
276 18 : }
277 :
278 :
279 : #ifdef WIN32
280 : /*
281 : * win32_check_directory_write_permissions()
282 : *
283 : * access() on WIN32 can't check directory permissions, so we have to
284 : * optionally create, then delete a file to check.
285 : * http://msdn.microsoft.com/en-us/library/1w06ktdy%28v=vs.80%29.aspx
286 : */
287 : static int
288 : win32_check_directory_write_permissions(void)
289 : {
290 : int fd;
291 :
292 : /*
293 : * We open a file we would normally create anyway. We do this even in
294 : * 'check' mode, which isn't ideal, but this is the best we can do.
295 : */
296 : if ((fd = open(GLOBALS_DUMP_FILE, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)) < 0)
297 : return -1;
298 : close(fd);
299 :
300 : return unlink(GLOBALS_DUMP_FILE);
301 : }
302 : #endif
303 :
304 :
305 : /*
306 : * check_single_dir()
307 : *
308 : * Check for the presence of a single directory in PGDATA, and fail if
309 : * is it missing or not accessible.
310 : */
311 : static void
312 324 : check_single_dir(const char *pg_data, const char *subdir)
313 : {
314 : struct stat statBuf;
315 : char subDirName[MAXPGPATH];
316 :
317 324 : snprintf(subDirName, sizeof(subDirName), "%s%s%s", pg_data,
318 : /* Win32 can't stat() a directory with a trailing slash. */
319 324 : *subdir ? "/" : "",
320 : subdir);
321 :
322 324 : if (stat(subDirName, &statBuf) != 0)
323 0 : report_status(PG_FATAL, "check for \"%s\" failed: %m",
324 : subDirName);
325 324 : else if (!S_ISDIR(statBuf.st_mode))
326 0 : report_status(PG_FATAL, "\"%s\" is not a directory",
327 : subDirName);
328 324 : }
329 :
330 :
331 : /*
332 : * check_data_dir()
333 : *
334 : * This function validates the given cluster directory - we search for a
335 : * small set of subdirectories that we expect to find in a valid $PGDATA
336 : * directory. If any of the subdirectories are missing (or secured against
337 : * us) we display an error message and exit()
338 : *
339 : */
340 : static void
341 36 : check_data_dir(ClusterInfo *cluster)
342 : {
343 36 : const char *pg_data = cluster->pgdata;
344 :
345 : /* get the cluster version */
346 36 : cluster->major_version = get_major_server_version(cluster);
347 :
348 36 : check_single_dir(pg_data, "");
349 36 : check_single_dir(pg_data, "base");
350 36 : check_single_dir(pg_data, "global");
351 36 : check_single_dir(pg_data, "pg_multixact");
352 36 : check_single_dir(pg_data, "pg_subtrans");
353 36 : check_single_dir(pg_data, PG_TBLSPC_DIR);
354 36 : check_single_dir(pg_data, "pg_twophase");
355 :
356 : /* pg_xlog has been renamed to pg_wal in v10 */
357 36 : if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
358 0 : check_single_dir(pg_data, "pg_xlog");
359 : else
360 36 : check_single_dir(pg_data, "pg_wal");
361 :
362 : /* pg_clog has been renamed to pg_xact in v10 */
363 36 : if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
364 0 : check_single_dir(pg_data, "pg_clog");
365 : else
366 36 : check_single_dir(pg_data, "pg_xact");
367 36 : }
368 :
369 :
370 : /*
371 : * check_bin_dir()
372 : *
373 : * This function searches for the executables that we expect to find
374 : * in the binaries directory. If we find that a required executable
375 : * is missing (or secured against us), we display an error message and
376 : * exit().
377 : *
378 : * If check_versions is true, then the versions of the binaries are checked
379 : * against the version of this pg_upgrade. This is for checking the target
380 : * bindir.
381 : */
382 : static void
383 38 : check_bin_dir(ClusterInfo *cluster, bool check_versions)
384 : {
385 : struct stat statBuf;
386 :
387 : /* check bindir */
388 38 : if (stat(cluster->bindir, &statBuf) != 0)
389 2 : report_status(PG_FATAL, "check for \"%s\" failed: %m",
390 : cluster->bindir);
391 36 : else if (!S_ISDIR(statBuf.st_mode))
392 0 : report_status(PG_FATAL, "\"%s\" is not a directory",
393 : cluster->bindir);
394 :
395 36 : check_exec(cluster->bindir, "postgres", check_versions);
396 36 : check_exec(cluster->bindir, "pg_controldata", check_versions);
397 36 : check_exec(cluster->bindir, "pg_ctl", check_versions);
398 :
399 : /*
400 : * Fetch the binary version after checking for the existence of pg_ctl.
401 : * This way we report a useful error if the pg_ctl binary used for version
402 : * fetching is missing/broken.
403 : */
404 36 : get_bin_version(cluster);
405 :
406 : /* pg_resetxlog has been renamed to pg_resetwal in version 10 */
407 36 : if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
408 0 : check_exec(cluster->bindir, "pg_resetxlog", check_versions);
409 : else
410 36 : check_exec(cluster->bindir, "pg_resetwal", check_versions);
411 :
412 36 : if (cluster == &new_cluster)
413 : {
414 : /*
415 : * These binaries are only needed for the target version. pg_dump and
416 : * pg_dumpall are used to dump the old cluster, but must be of the
417 : * target version.
418 : */
419 18 : check_exec(cluster->bindir, "initdb", check_versions);
420 18 : check_exec(cluster->bindir, "pg_dump", check_versions);
421 18 : check_exec(cluster->bindir, "pg_dumpall", check_versions);
422 18 : check_exec(cluster->bindir, "pg_restore", check_versions);
423 18 : check_exec(cluster->bindir, "psql", check_versions);
424 18 : check_exec(cluster->bindir, "vacuumdb", check_versions);
425 : }
426 36 : }
427 :
428 : static void
429 252 : check_exec(const char *dir, const char *program, bool check_version)
430 : {
431 : char path[MAXPGPATH];
432 : char *line;
433 : char cmd[MAXPGPATH];
434 : char versionstr[128];
435 :
436 252 : snprintf(path, sizeof(path), "%s/%s", dir, program);
437 :
438 252 : if (validate_exec(path) != 0)
439 0 : pg_fatal("check for \"%s\" failed: %m", path);
440 :
441 252 : snprintf(cmd, sizeof(cmd), "\"%s\" -V", path);
442 :
443 252 : if ((line = pipe_read_line(cmd)) == NULL)
444 0 : pg_fatal("check for \"%s\" failed: cannot execute",
445 : path);
446 :
447 252 : if (check_version)
448 : {
449 180 : pg_strip_crlf(line);
450 :
451 180 : snprintf(versionstr, sizeof(versionstr), "%s (PostgreSQL) " PG_VERSION, program);
452 :
453 180 : if (strcmp(line, versionstr) != 0)
454 0 : pg_fatal("check for \"%s\" failed: incorrect version: found \"%s\", expected \"%s\"",
455 : path, line, versionstr);
456 : }
457 :
458 252 : pg_free(line);
459 252 : }
|