Line data Source code
1 : /*
2 : * option.c
3 : *
4 : * options functions
5 : *
6 : * Copyright (c) 2010-2023, PostgreSQL Global Development Group
7 : * src/bin/pg_upgrade/option.c
8 : */
9 :
10 : #include "postgres_fe.h"
11 :
12 : #ifdef WIN32
13 : #include <io.h>
14 : #endif
15 :
16 : #include "common/string.h"
17 : #include "fe_utils/option_utils.h"
18 : #include "getopt_long.h"
19 : #include "pg_upgrade.h"
20 : #include "utils/pidfile.h"
21 :
22 : static void usage(void);
23 : static void check_required_directory(char **dirpath,
24 : const char *envVarName, bool useCwd,
25 : const char *cmdLineOption, const char *description,
26 : bool missingOk);
27 : #define FIX_DEFAULT_READ_ONLY "-c default_transaction_read_only=false"
28 :
29 :
30 : UserOpts user_opts;
31 :
32 :
33 : /*
34 : * parseCommandLine()
35 : *
36 : * Parses the command line (argc, argv[]) and loads structures
37 : */
38 : void
39 14 : parseCommandLine(int argc, char *argv[])
40 : {
41 : static struct option long_options[] = {
42 : {"old-datadir", required_argument, NULL, 'd'},
43 : {"new-datadir", required_argument, NULL, 'D'},
44 : {"old-bindir", required_argument, NULL, 'b'},
45 : {"new-bindir", required_argument, NULL, 'B'},
46 : {"no-sync", no_argument, NULL, 'N'},
47 : {"old-options", required_argument, NULL, 'o'},
48 : {"new-options", required_argument, NULL, 'O'},
49 : {"old-port", required_argument, NULL, 'p'},
50 : {"new-port", required_argument, NULL, 'P'},
51 :
52 : {"username", required_argument, NULL, 'U'},
53 : {"check", no_argument, NULL, 'c'},
54 : {"link", no_argument, NULL, 'k'},
55 : {"retain", no_argument, NULL, 'r'},
56 : {"jobs", required_argument, NULL, 'j'},
57 : {"socketdir", required_argument, NULL, 's'},
58 : {"verbose", no_argument, NULL, 'v'},
59 : {"clone", no_argument, NULL, 1},
60 : {"copy", no_argument, NULL, 2},
61 : {"sync-method", required_argument, NULL, 3},
62 :
63 : {NULL, 0, NULL, 0}
64 : };
65 : int option; /* Command line option */
66 14 : int optindex = 0; /* used by getopt_long */
67 : int os_user_effective_id;
68 : DataDirSyncMethod unused;
69 :
70 14 : user_opts.do_sync = true;
71 14 : user_opts.transfer_mode = TRANSFER_MODE_COPY;
72 :
73 14 : os_info.progname = get_progname(argv[0]);
74 :
75 : /* Process libpq env. variables; load values here for usage() output */
76 14 : old_cluster.port = getenv("PGPORTOLD") ? atoi(getenv("PGPORTOLD")) : DEF_PGUPORT;
77 14 : new_cluster.port = getenv("PGPORTNEW") ? atoi(getenv("PGPORTNEW")) : DEF_PGUPORT;
78 :
79 14 : os_user_effective_id = get_user_info(&os_info.user);
80 : /* we override just the database user name; we got the OS id above */
81 14 : if (getenv("PGUSER"))
82 : {
83 0 : pg_free(os_info.user);
84 : /* must save value, getenv()'s pointer is not stable */
85 0 : os_info.user = pg_strdup(getenv("PGUSER"));
86 : }
87 :
88 14 : if (argc > 1)
89 : {
90 14 : if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
91 : {
92 2 : usage();
93 2 : exit(0);
94 : }
95 12 : if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
96 : {
97 2 : puts("pg_upgrade (PostgreSQL) " PG_VERSION);
98 2 : exit(0);
99 : }
100 : }
101 :
102 : /* Allow help and version to be run as root, so do the test here. */
103 10 : if (os_user_effective_id == 0)
104 0 : pg_fatal("%s: cannot be run as root", os_info.progname);
105 :
106 88 : while ((option = getopt_long(argc, argv, "b:B:cd:D:j:kNo:O:p:P:rs:U:v",
107 : long_options, &optindex)) != -1)
108 : {
109 80 : switch (option)
110 : {
111 8 : case 'b':
112 8 : old_cluster.bindir = pg_strdup(optarg);
113 8 : break;
114 :
115 8 : case 'B':
116 8 : new_cluster.bindir = pg_strdup(optarg);
117 8 : break;
118 :
119 6 : case 'c':
120 6 : user_opts.check = true;
121 6 : break;
122 :
123 8 : case 'd':
124 8 : old_cluster.pgdata = pg_strdup(optarg);
125 8 : break;
126 :
127 8 : case 'D':
128 8 : new_cluster.pgdata = pg_strdup(optarg);
129 8 : break;
130 :
131 0 : case 'j':
132 0 : user_opts.jobs = atoi(optarg);
133 0 : break;
134 :
135 0 : case 'k':
136 0 : user_opts.transfer_mode = TRANSFER_MODE_LINK;
137 0 : break;
138 :
139 8 : case 'N':
140 8 : user_opts.do_sync = false;
141 8 : break;
142 :
143 0 : case 'o':
144 : /* append option? */
145 0 : if (!old_cluster.pgopts)
146 0 : old_cluster.pgopts = pg_strdup(optarg);
147 : else
148 : {
149 0 : char *old_pgopts = old_cluster.pgopts;
150 :
151 0 : old_cluster.pgopts = psprintf("%s %s", old_pgopts, optarg);
152 0 : free(old_pgopts);
153 : }
154 0 : break;
155 :
156 0 : case 'O':
157 : /* append option? */
158 0 : if (!new_cluster.pgopts)
159 0 : new_cluster.pgopts = pg_strdup(optarg);
160 : else
161 : {
162 0 : char *new_pgopts = new_cluster.pgopts;
163 :
164 0 : new_cluster.pgopts = psprintf("%s %s", new_pgopts, optarg);
165 0 : free(new_pgopts);
166 : }
167 0 : break;
168 :
169 8 : case 'p':
170 8 : if ((old_cluster.port = atoi(optarg)) <= 0)
171 0 : pg_fatal("invalid old port number");
172 8 : break;
173 :
174 8 : case 'P':
175 8 : if ((new_cluster.port = atoi(optarg)) <= 0)
176 0 : pg_fatal("invalid new port number");
177 8 : break;
178 :
179 0 : case 'r':
180 0 : log_opts.retain = true;
181 0 : break;
182 :
183 8 : case 's':
184 8 : user_opts.socketdir = pg_strdup(optarg);
185 8 : break;
186 :
187 0 : case 'U':
188 0 : pg_free(os_info.user);
189 0 : os_info.user = pg_strdup(optarg);
190 0 : os_info.user_specified = true;
191 0 : break;
192 :
193 0 : case 'v':
194 0 : log_opts.verbose = true;
195 0 : break;
196 :
197 0 : case 1:
198 0 : user_opts.transfer_mode = TRANSFER_MODE_CLONE;
199 0 : break;
200 :
201 8 : case 2:
202 8 : user_opts.transfer_mode = TRANSFER_MODE_COPY;
203 8 : break;
204 :
205 0 : case 3:
206 0 : if (!parse_sync_method(optarg, &unused))
207 0 : exit(1);
208 0 : user_opts.sync_method = pg_strdup(optarg);
209 0 : break;
210 :
211 2 : default:
212 2 : fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
213 : os_info.progname);
214 2 : exit(1);
215 : }
216 : }
217 :
218 8 : if (optind < argc)
219 0 : pg_fatal("too many command-line arguments (first is \"%s\")", argv[optind]);
220 :
221 8 : if (!user_opts.sync_method)
222 8 : user_opts.sync_method = pg_strdup("fsync");
223 :
224 8 : if (log_opts.verbose)
225 0 : pg_log(PG_REPORT, "Running in verbose mode");
226 :
227 8 : log_opts.isatty = isatty(fileno(stdout));
228 :
229 : /* Turn off read-only mode; add prefix to PGOPTIONS? */
230 8 : if (getenv("PGOPTIONS"))
231 : {
232 0 : char *pgoptions = psprintf("%s %s", FIX_DEFAULT_READ_ONLY,
233 : getenv("PGOPTIONS"));
234 :
235 0 : setenv("PGOPTIONS", pgoptions, 1);
236 0 : pfree(pgoptions);
237 : }
238 : else
239 8 : setenv("PGOPTIONS", FIX_DEFAULT_READ_ONLY, 1);
240 :
241 : /* Get values from env if not already set */
242 8 : check_required_directory(&old_cluster.bindir, "PGBINOLD", false,
243 8 : "-b", _("old cluster binaries reside"), false);
244 8 : check_required_directory(&new_cluster.bindir, "PGBINNEW", false,
245 8 : "-B", _("new cluster binaries reside"), true);
246 8 : check_required_directory(&old_cluster.pgdata, "PGDATAOLD", false,
247 8 : "-d", _("old cluster data resides"), false);
248 8 : check_required_directory(&new_cluster.pgdata, "PGDATANEW", false,
249 8 : "-D", _("new cluster data resides"), false);
250 8 : check_required_directory(&user_opts.socketdir, "PGSOCKETDIR", true,
251 8 : "-s", _("sockets will be created"), false);
252 :
253 : #ifdef WIN32
254 :
255 : /*
256 : * On Windows, initdb --sync-only will fail with a "Permission denied"
257 : * error on file pg_upgrade_utility.log if pg_upgrade is run inside the
258 : * new cluster directory, so we do a check here.
259 : */
260 : {
261 : char cwd[MAXPGPATH],
262 : new_cluster_pgdata[MAXPGPATH];
263 :
264 : strlcpy(new_cluster_pgdata, new_cluster.pgdata, MAXPGPATH);
265 : canonicalize_path(new_cluster_pgdata);
266 :
267 : if (!getcwd(cwd, MAXPGPATH))
268 : pg_fatal("could not determine current directory");
269 : canonicalize_path(cwd);
270 : if (path_is_prefix_of_path(new_cluster_pgdata, cwd))
271 : pg_fatal("cannot run pg_upgrade from inside the new cluster data directory on Windows");
272 : }
273 : #endif
274 8 : }
275 :
276 :
277 : static void
278 2 : usage(void)
279 : {
280 2 : printf(_("pg_upgrade upgrades a PostgreSQL cluster to a different major version.\n\n"));
281 2 : printf(_("Usage:\n"));
282 2 : printf(_(" pg_upgrade [OPTION]...\n\n"));
283 2 : printf(_("Options:\n"));
284 2 : printf(_(" -b, --old-bindir=BINDIR old cluster executable directory\n"));
285 2 : printf(_(" -B, --new-bindir=BINDIR new cluster executable directory (default\n"
286 : " same directory as pg_upgrade)\n"));
287 2 : printf(_(" -c, --check check clusters only, don't change any data\n"));
288 2 : printf(_(" -d, --old-datadir=DATADIR old cluster data directory\n"));
289 2 : printf(_(" -D, --new-datadir=DATADIR new cluster data directory\n"));
290 2 : printf(_(" -j, --jobs=NUM number of simultaneous processes or threads to use\n"));
291 2 : printf(_(" -k, --link link instead of copying files to new cluster\n"));
292 2 : printf(_(" -N, --no-sync do not wait for changes to be written safely to disk\n"));
293 2 : printf(_(" -o, --old-options=OPTIONS old cluster options to pass to the server\n"));
294 2 : printf(_(" -O, --new-options=OPTIONS new cluster options to pass to the server\n"));
295 2 : printf(_(" -p, --old-port=PORT old cluster port number (default %d)\n"), old_cluster.port);
296 2 : printf(_(" -P, --new-port=PORT new cluster port number (default %d)\n"), new_cluster.port);
297 2 : printf(_(" -r, --retain retain SQL and log files after success\n"));
298 2 : printf(_(" -s, --socketdir=DIR socket directory to use (default current dir.)\n"));
299 2 : printf(_(" -U, --username=NAME cluster superuser (default \"%s\")\n"), os_info.user);
300 2 : printf(_(" -v, --verbose enable verbose internal logging\n"));
301 2 : printf(_(" -V, --version display version information, then exit\n"));
302 2 : printf(_(" --clone clone instead of copying files to new cluster\n"));
303 2 : printf(_(" --copy copy files to new cluster (default)\n"));
304 2 : printf(_(" --sync-method=METHOD set method for syncing files to disk\n"));
305 2 : printf(_(" -?, --help show this help, then exit\n"));
306 2 : printf(_("\n"
307 : "Before running pg_upgrade you must:\n"
308 : " create a new database cluster (using the new version of initdb)\n"
309 : " shutdown the postmaster servicing the old cluster\n"
310 : " shutdown the postmaster servicing the new cluster\n"));
311 2 : printf(_("\n"
312 : "When you run pg_upgrade, you must provide the following information:\n"
313 : " the data directory for the old cluster (-d DATADIR)\n"
314 : " the data directory for the new cluster (-D DATADIR)\n"
315 : " the \"bin\" directory for the old version (-b BINDIR)\n"
316 : " the \"bin\" directory for the new version (-B BINDIR)\n"));
317 2 : printf(_("\n"
318 : "For example:\n"
319 : " pg_upgrade -d oldCluster/data -D newCluster/data -b oldCluster/bin -B newCluster/bin\n"
320 : "or\n"));
321 : #ifndef WIN32
322 2 : printf(_(" $ export PGDATAOLD=oldCluster/data\n"
323 : " $ export PGDATANEW=newCluster/data\n"
324 : " $ export PGBINOLD=oldCluster/bin\n"
325 : " $ export PGBINNEW=newCluster/bin\n"
326 : " $ pg_upgrade\n"));
327 : #else
328 : printf(_(" C:\\> set PGDATAOLD=oldCluster/data\n"
329 : " C:\\> set PGDATANEW=newCluster/data\n"
330 : " C:\\> set PGBINOLD=oldCluster/bin\n"
331 : " C:\\> set PGBINNEW=newCluster/bin\n"
332 : " C:\\> pg_upgrade\n"));
333 : #endif
334 2 : printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
335 2 : printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
336 2 : }
337 :
338 :
339 : /*
340 : * check_required_directory()
341 : *
342 : * Checks a directory option.
343 : * dirpath - the directory name supplied on the command line, or NULL
344 : * envVarName - the name of an environment variable to get if dirpath is NULL
345 : * useCwd - true if OK to default to CWD
346 : * cmdLineOption - the command line option for this directory
347 : * description - a description of this directory option
348 : * missingOk - true if OK that both dirpath and envVarName are not existing
349 : *
350 : * We use the last two arguments to construct a meaningful error message if the
351 : * user hasn't provided the required directory name.
352 : */
353 : static void
354 40 : check_required_directory(char **dirpath, const char *envVarName, bool useCwd,
355 : const char *cmdLineOption, const char *description,
356 : bool missingOk)
357 : {
358 40 : if (*dirpath == NULL || strlen(*dirpath) == 0)
359 : {
360 : const char *envVar;
361 :
362 0 : if ((envVar = getenv(envVarName)) && strlen(envVar))
363 0 : *dirpath = pg_strdup(envVar);
364 0 : else if (useCwd)
365 : {
366 : char cwd[MAXPGPATH];
367 :
368 0 : if (!getcwd(cwd, MAXPGPATH))
369 0 : pg_fatal("could not determine current directory");
370 0 : *dirpath = pg_strdup(cwd);
371 : }
372 0 : else if (missingOk)
373 0 : return;
374 : else
375 0 : pg_fatal("You must identify the directory where the %s.\n"
376 : "Please use the %s command-line option or the %s environment variable.",
377 : description, cmdLineOption, envVarName);
378 : }
379 :
380 : /*
381 : * Clean up the path, in particular trimming any trailing path separators,
382 : * because we construct paths by appending to this path.
383 : */
384 40 : canonicalize_path(*dirpath);
385 : }
386 :
387 : /*
388 : * adjust_data_dir
389 : *
390 : * If a configuration-only directory was specified, find the real data dir
391 : * by querying the running server. This has limited checking because we
392 : * can't check for a running server because we can't find postmaster.pid.
393 : *
394 : * On entry, cluster->pgdata has been set from command line or env variable,
395 : * but cluster->pgconfig isn't set. We fill both variables with corrected
396 : * values.
397 : */
398 : void
399 16 : adjust_data_dir(ClusterInfo *cluster)
400 : {
401 : char filename[MAXPGPATH];
402 : char cmd[MAXPGPATH],
403 : cmd_output[MAX_STRING];
404 : FILE *fp,
405 : *output;
406 : int rc;
407 :
408 : /* Initially assume config dir and data dir are the same */
409 16 : cluster->pgconfig = pg_strdup(cluster->pgdata);
410 :
411 : /* If there is no postgresql.conf, it can't be a config-only dir */
412 16 : snprintf(filename, sizeof(filename), "%s/postgresql.conf", cluster->pgconfig);
413 16 : if ((fp = fopen(filename, "r")) == NULL)
414 16 : return;
415 16 : fclose(fp);
416 :
417 : /* If PG_VERSION exists, it can't be a config-only dir */
418 16 : snprintf(filename, sizeof(filename), "%s/PG_VERSION", cluster->pgconfig);
419 16 : if ((fp = fopen(filename, "r")) != NULL)
420 : {
421 16 : fclose(fp);
422 16 : return;
423 : }
424 :
425 : /* Must be a configuration directory, so find the real data directory. */
426 :
427 0 : if (cluster == &old_cluster)
428 0 : prep_status("Finding the real data directory for the source cluster");
429 : else
430 0 : prep_status("Finding the real data directory for the target cluster");
431 :
432 : /*
433 : * We don't have a data directory yet, so we can't check the PG version,
434 : * so this might fail --- only works for PG 9.2+. If this fails,
435 : * pg_upgrade will fail anyway because the data files will not be found.
436 : */
437 0 : snprintf(cmd, sizeof(cmd), "\"%s/postgres\" -D \"%s\" -C data_directory",
438 : cluster->bindir, cluster->pgconfig);
439 0 : fflush(NULL);
440 :
441 0 : if ((output = popen(cmd, "r")) == NULL ||
442 0 : fgets(cmd_output, sizeof(cmd_output), output) == NULL)
443 0 : pg_fatal("could not get data directory using %s: %s",
444 0 : cmd, strerror(errno));
445 :
446 0 : rc = pclose(output);
447 0 : if (rc != 0)
448 0 : pg_fatal("could not get data directory using %s: %s",
449 : cmd, wait_result_to_str(rc));
450 :
451 : /* strip trailing newline and carriage return */
452 0 : (void) pg_strip_crlf(cmd_output);
453 :
454 0 : cluster->pgdata = pg_strdup(cmd_output);
455 :
456 0 : check_ok();
457 : }
458 :
459 :
460 : /*
461 : * get_sock_dir
462 : *
463 : * Identify the socket directory to use for this cluster. If we're doing
464 : * a live check (old cluster only), we need to find out where the postmaster
465 : * is listening. Otherwise, we're going to put the socket into the current
466 : * directory.
467 : */
468 : void
469 12 : get_sock_dir(ClusterInfo *cluster, bool live_check)
470 : {
471 : #if !defined(WIN32)
472 12 : if (!live_check)
473 12 : cluster->sockdir = user_opts.socketdir;
474 : else
475 : {
476 : /*
477 : * If we are doing a live check, we will use the old cluster's Unix
478 : * domain socket directory so we can connect to the live server.
479 : */
480 0 : unsigned short orig_port = cluster->port;
481 : char filename[MAXPGPATH],
482 : line[MAXPGPATH];
483 : FILE *fp;
484 : int lineno;
485 :
486 0 : snprintf(filename, sizeof(filename), "%s/postmaster.pid",
487 : cluster->pgdata);
488 0 : if ((fp = fopen(filename, "r")) == NULL)
489 0 : pg_fatal("could not open file \"%s\": %s",
490 0 : filename, strerror(errno));
491 :
492 0 : for (lineno = 1;
493 : lineno <= Max(LOCK_FILE_LINE_PORT, LOCK_FILE_LINE_SOCKET_DIR);
494 0 : lineno++)
495 : {
496 0 : if (fgets(line, sizeof(line), fp) == NULL)
497 0 : pg_fatal("could not read line %d from file \"%s\": %s",
498 0 : lineno, filename, strerror(errno));
499 :
500 : /* potentially overwrite user-supplied value */
501 0 : if (lineno == LOCK_FILE_LINE_PORT)
502 0 : sscanf(line, "%hu", &old_cluster.port);
503 0 : if (lineno == LOCK_FILE_LINE_SOCKET_DIR)
504 : {
505 : /* strip trailing newline and carriage return */
506 0 : cluster->sockdir = pg_strdup(line);
507 0 : (void) pg_strip_crlf(cluster->sockdir);
508 : }
509 : }
510 0 : fclose(fp);
511 :
512 : /* warn of port number correction */
513 0 : if (orig_port != DEF_PGUPORT && old_cluster.port != orig_port)
514 0 : pg_log(PG_WARNING, "user-supplied old port number %hu corrected to %hu",
515 0 : orig_port, cluster->port);
516 : }
517 : #else /* WIN32 */
518 : cluster->sockdir = NULL;
519 : #endif
520 12 : }
|