]> granicus.if.org Git - postgresql/blob - contrib/pg_upgrade/option.c
Make documentation of --help and --version options more consistent
[postgresql] / contrib / pg_upgrade / option.c
1 /*
2  *      opt.c
3  *
4  *      options functions
5  *
6  *      Copyright (c) 2010-2012, PostgreSQL Global Development Group
7  *      contrib/pg_upgrade/option.c
8  */
9
10 #include "postgres.h"
11
12 #include "pg_upgrade.h"
13
14 #include <getopt_long.h>
15 #include <time.h>
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #ifdef WIN32
19 #include <io.h>
20 #endif
21
22
23 static void usage(void);
24 static void check_required_directory(char **dirpath,
25                                    char *envVarName, char *cmdLineOption, char *description);
26
27
28 UserOpts        user_opts;
29
30
31 /*
32  * parseCommandLine()
33  *
34  *      Parses the command line (argc, argv[]) and loads structures
35  */
36 void
37 parseCommandLine(int argc, char *argv[])
38 {
39         static struct option long_options[] = {
40                 {"old-datadir", required_argument, NULL, 'd'},
41                 {"new-datadir", required_argument, NULL, 'D'},
42                 {"old-bindir", required_argument, NULL, 'b'},
43                 {"new-bindir", required_argument, NULL, 'B'},
44                 {"old-options", required_argument, NULL, 'o'},
45                 {"new-options", required_argument, NULL, 'O'},
46                 {"old-port", required_argument, NULL, 'p'},
47                 {"new-port", required_argument, NULL, 'P'},
48
49                 {"user", required_argument, NULL, 'u'},
50                 {"check", no_argument, NULL, 'c'},
51                 {"link", no_argument, NULL, 'k'},
52                 {"retain", no_argument, NULL, 'r'},
53                 {"verbose", no_argument, NULL, 'v'},
54                 {NULL, 0, NULL, 0}
55         };
56         int                     option;                 /* Command line option */
57         int                     optindex = 0;   /* used by getopt_long */
58         int                     os_user_effective_id;
59         FILE       *fp;
60         char      **filename;
61         time_t          run_time = time(NULL);
62
63         user_opts.transfer_mode = TRANSFER_MODE_COPY;
64
65         os_info.progname = get_progname(argv[0]);
66
67         /* Process libpq env. variables; load values here for usage() output */
68         old_cluster.port = getenv("PGPORTOLD") ? atoi(getenv("PGPORTOLD")) : DEF_PGUPORT;
69         new_cluster.port = getenv("PGPORTNEW") ? atoi(getenv("PGPORTNEW")) : DEF_PGUPORT;
70
71         os_user_effective_id = get_user_info(&os_info.user);
72         /* we override just the database user name;  we got the OS id above */
73         if (getenv("PGUSER"))
74         {
75                 pg_free(os_info.user);
76                 /* must save value, getenv()'s pointer is not stable */
77                 os_info.user = pg_strdup(getenv("PGUSER"));
78         }
79
80         if (argc > 1)
81         {
82                 if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0 ||
83                         strcmp(argv[1], "-?") == 0)
84                 {
85                         usage();
86                         exit(0);
87                 }
88                 if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
89                 {
90                         puts("pg_upgrade (PostgreSQL) " PG_VERSION);
91                         exit(0);
92                 }
93         }
94
95         /* Allow help and version to be run as root, so do the test here. */
96         if (os_user_effective_id == 0)
97                 pg_log(PG_FATAL, "%s: cannot be run as root\n", os_info.progname);
98
99         if ((log_opts.internal = fopen_priv(INTERNAL_LOG_FILE, "a")) == NULL)
100                 pg_log(PG_FATAL, "cannot write to log file %s\n", INTERNAL_LOG_FILE);
101
102         while ((option = getopt_long(argc, argv, "d:D:b:B:cko:O:p:P:ru:v",
103                                                                  long_options, &optindex)) != -1)
104         {
105                 switch (option)
106                 {
107                         case 'b':
108                                 old_cluster.bindir = pg_strdup(optarg);
109                                 break;
110
111                         case 'B':
112                                 new_cluster.bindir = pg_strdup(optarg);
113                                 break;
114
115                         case 'c':
116                                 user_opts.check = true;
117                                 break;
118
119                         case 'd':
120                                 old_cluster.pgdata = pg_strdup(optarg);
121                                 old_cluster.pgconfig = pg_strdup(optarg);
122                                 break;
123
124                         case 'D':
125                                 new_cluster.pgdata = pg_strdup(optarg);
126                                 new_cluster.pgconfig = pg_strdup(optarg);
127                                 break;
128
129                         case 'k':
130                                 user_opts.transfer_mode = TRANSFER_MODE_LINK;
131                                 break;
132
133                         case 'o':
134                                 old_cluster.pgopts = pg_strdup(optarg);
135                                 break;
136
137                         case 'O':
138                                 new_cluster.pgopts = pg_strdup(optarg);
139                                 break;
140
141                                 /*
142                                  * Someday, the port number option could be removed and passed
143                                  * using -o/-O, but that requires postmaster -C to be
144                                  * supported on all old/new versions.
145                                  */
146                         case 'p':
147                                 if ((old_cluster.port = atoi(optarg)) <= 0)
148                                 {
149                                         pg_log(PG_FATAL, "invalid old port number\n");
150                                         exit(1);
151                                 }
152                                 break;
153
154                         case 'P':
155                                 if ((new_cluster.port = atoi(optarg)) <= 0)
156                                 {
157                                         pg_log(PG_FATAL, "invalid new port number\n");
158                                         exit(1);
159                                 }
160                                 break;
161
162                         case 'r':
163                                 log_opts.retain = true;
164                                 break;
165
166                         case 'u':
167                                 pg_free(os_info.user);
168                                 os_info.user = pg_strdup(optarg);
169
170                                 /*
171                                  * Push the user name into the environment so pre-9.1
172                                  * pg_ctl/libpq uses it.
173                                  */
174                                 pg_putenv("PGUSER", os_info.user);
175                                 break;
176
177                         case 'v':
178                                 pg_log(PG_REPORT, "Running in verbose mode\n");
179                                 log_opts.verbose = true;
180                                 break;
181
182                         default:
183                                 pg_log(PG_FATAL,
184                                            "Try \"%s --help\" for more information.\n",
185                                            os_info.progname);
186                                 break;
187                 }
188         }
189
190         /* label start of upgrade in logfiles */
191         for (filename = output_files; *filename != NULL; filename++)
192         {
193                 if ((fp = fopen_priv(*filename, "a")) == NULL)
194                         pg_log(PG_FATAL, "cannot write to log file %s\n", *filename);
195
196                 /* Start with newline because we might be appending to a file. */
197                 fprintf(fp, "\n"
198                 "-----------------------------------------------------------------\n"
199                                 "  pg_upgrade run on %s"
200                                 "-----------------------------------------------------------------\n\n",
201                                 ctime(&run_time));
202                 fclose(fp);
203         }
204
205         /* Get values from env if not already set */
206         check_required_directory(&old_cluster.bindir, "PGBINOLD", "-b",
207                                                          "old cluster binaries reside");
208         check_required_directory(&new_cluster.bindir, "PGBINNEW", "-B",
209                                                          "new cluster binaries reside");
210         check_required_directory(&old_cluster.pgdata, "PGDATAOLD", "-d",
211                                                          "old cluster data resides");
212         check_required_directory(&new_cluster.pgdata, "PGDATANEW", "-D",
213                                                          "new cluster data resides");
214 }
215
216
217 static void
218 usage(void)
219 {
220         printf(_("pg_upgrade upgrades a PostgreSQL cluster to a different major version.\n\
221 \nUsage:\n\
222   pg_upgrade [OPTION]...\n\
223 \n\
224 Options:\n\
225   -b, --old-bindir=OLDBINDIR    old cluster executable directory\n\
226   -B, --new-bindir=NEWBINDIR    new cluster executable directory\n\
227   -c, --check                   check clusters only, don't change any data\n\
228   -d, --old-datadir=OLDDATADIR  old cluster data directory\n\
229   -D, --new-datadir=NEWDATADIR  new cluster data directory\n\
230   -k, --link                    link instead of copying files to new cluster\n\
231   -o, --old-options=OPTIONS     old cluster options to pass to the server\n\
232   -O, --new-options=OPTIONS     new cluster options to pass to the server\n\
233   -p, --old-port=OLDPORT        old cluster port number (default %d)\n\
234   -P, --new-port=NEWPORT        new cluster port number (default %d)\n\
235   -r, --retain                  retain SQL and log files after success\n\
236   -u, --user=NAME               cluster superuser (default \"%s\")\n\
237   -v, --verbose                 enable verbose internal logging\n\
238   -V, --version                 display version information, then exit\n\
239   -?, -h, --help                show this help, then exit\n\
240 \n\
241 Before running pg_upgrade you must:\n\
242   create a new database cluster (using the new version of initdb)\n\
243   shutdown the postmaster servicing the old cluster\n\
244   shutdown the postmaster servicing the new cluster\n\
245 \n\
246 When you run pg_upgrade, you must provide the following information:\n\
247   the data directory for the old cluster  (-d OLDDATADIR)\n\
248   the data directory for the new cluster  (-D NEWDATADIR)\n\
249   the \"bin\" directory for the old version (-b OLDBINDIR)\n\
250   the \"bin\" directory for the new version (-B NEWBINDIR)\n\
251 \n\
252 For example:\n\
253   pg_upgrade -d oldCluster/data -D newCluster/data -b oldCluster/bin -B newCluster/bin\n\
254 or\n"), old_cluster.port, new_cluster.port, os_info.user);
255 #ifndef WIN32
256         printf(_("\
257   $ export PGDATAOLD=oldCluster/data\n\
258   $ export PGDATANEW=newCluster/data\n\
259   $ export PGBINOLD=oldCluster/bin\n\
260   $ export PGBINNEW=newCluster/bin\n\
261   $ pg_upgrade\n"));
262 #else
263         printf(_("\
264   C:\\> set PGDATAOLD=oldCluster/data\n\
265   C:\\> set PGDATANEW=newCluster/data\n\
266   C:\\> set PGBINOLD=oldCluster/bin\n\
267   C:\\> set PGBINNEW=newCluster/bin\n\
268   C:\\> pg_upgrade\n"));
269 #endif
270         printf(_("\nReport bugs to <pgsql-bugs@postgresql.org>.\n"));
271 }
272
273
274 /*
275  * check_required_directory()
276  *
277  * Checks a directory option.
278  *      dirpath           - the directory name supplied on the command line
279  *      envVarName        - the name of an environment variable to get if dirpath is NULL
280  *      cmdLineOption - the command line option corresponds to this directory (-o, -O, -n, -N)
281  *      description   - a description of this directory option
282  *
283  * We use the last two arguments to construct a meaningful error message if the
284  * user hasn't provided the required directory name.
285  */
286 static void
287 check_required_directory(char **dirpath, char *envVarName,
288                                                  char *cmdLineOption, char *description)
289 {
290         if (*dirpath == NULL || strlen(*dirpath) == 0)
291         {
292                 const char *envVar;
293
294                 if ((envVar = getenv(envVarName)) && strlen(envVar))
295                         *dirpath = pg_strdup(envVar);
296                 else
297                         pg_log(PG_FATAL, "You must identify the directory where the %s.\n"
298                                    "Please use the %s command-line option or the %s environment variable.\n",
299                                    description, cmdLineOption, envVarName);
300         }
301
302         /*
303          * Trim off any trailing path separators
304          */
305 #ifndef WIN32
306         if ((*dirpath)[strlen(*dirpath) - 1] == '/')
307 #else
308         if ((*dirpath)[strlen(*dirpath) - 1] == '/' ||
309                 (*dirpath)[strlen(*dirpath) - 1] == '\\')
310 #endif
311                 (*dirpath)[strlen(*dirpath) - 1] = 0;
312 }
313
314 /*
315  * adjust_data_dir
316  *
317  * If a configuration-only directory was specified, find the real data dir
318  * by quering the running server.  This has limited checking because we
319  * can't check for a running server because we can't find postmaster.pid.
320  */
321 void
322 adjust_data_dir(ClusterInfo *cluster)
323 {
324         char            filename[MAXPGPATH];
325         char            cmd[MAXPGPATH],
326                                 cmd_output[MAX_STRING];
327         FILE       *fp,
328                            *output;
329
330         /* If there is no postgresql.conf, it can't be a config-only dir */
331         snprintf(filename, sizeof(filename), "%s/postgresql.conf", cluster->pgconfig);
332         if ((fp = fopen(filename, "r")) == NULL)
333                 return;
334         fclose(fp);
335
336         /* If PG_VERSION exists, it can't be a config-only dir */
337         snprintf(filename, sizeof(filename), "%s/PG_VERSION", cluster->pgconfig);
338         if ((fp = fopen(filename, "r")) != NULL)
339         {
340                 fclose(fp);
341                 return;
342         }
343
344         /* Must be a configuration directory, so find the real data directory. */
345
346         prep_status("Finding the real data directory for the %s cluster",
347                                 CLUSTER_NAME(cluster));
348
349         /*
350          * We don't have a data directory yet, so we can't check the PG version,
351          * so this might fail --- only works for PG 9.2+.       If this fails,
352          * pg_upgrade will fail anyway because the data files will not be found.
353          */
354         snprintf(cmd, sizeof(cmd), "\"%s/postmaster\" -D \"%s\" -C data_directory",
355                          cluster->bindir, cluster->pgconfig);
356
357         if ((output = popen(cmd, "r")) == NULL ||
358                 fgets(cmd_output, sizeof(cmd_output), output) == NULL)
359                 pg_log(PG_FATAL, "Could not get data directory using %s: %s\n",
360                            cmd, getErrorText(errno));
361
362         pclose(output);
363
364         /* Remove trailing newline */
365         if (strchr(cmd_output, '\n') != NULL)
366                 *strchr(cmd_output, '\n') = '\0';
367
368         cluster->pgdata = pg_strdup(cmd_output);
369
370         check_ok();
371 }