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