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