]> granicus.if.org Git - postgresql/blob - contrib/pg_upgrade/option.c
Improve formatting of pg_upgrade --help output
[postgresql] / contrib / pg_upgrade / option.c
1 /*
2  *      opt.c
3  *
4  *      options functions
5  *
6  *      Copyright (c) 2010-2011, PostgreSQL Global Development Group
7  *      contrib/pg_upgrade/option.c
8  */
9
10 #include "pg_upgrade.h"
11
12 #include "getopt_long.h"
13
14 #ifdef WIN32
15 #include <io.h>
16 #endif
17
18
19 static void usage(void);
20 static void validateDirectoryOption(char **dirpath,
21                                    char *envVarName, char *cmdLineOption, char *description);
22 static void get_pkglibdirs(void);
23 static char *get_pkglibdir(const char *bindir);
24
25
26 UserOpts        user_opts;
27
28
29 /*
30  * parseCommandLine()
31  *
32  *      Parses the command line (argc, argv[]) and loads structures
33  */
34 void
35 parseCommandLine(int argc, char *argv[])
36 {
37         static struct option long_options[] = {
38                 {"old-datadir", required_argument, NULL, 'd'},
39                 {"new-datadir", required_argument, NULL, 'D'},
40                 {"old-bindir", required_argument, NULL, 'b'},
41                 {"new-bindir", required_argument, NULL, 'B'},
42                 {"old-port", required_argument, NULL, 'p'},
43                 {"new-port", required_argument, NULL, 'P'},
44
45                 {"user", required_argument, NULL, 'u'},
46                 {"check", no_argument, NULL, 'c'},
47                 {"debug", no_argument, NULL, 'g'},
48                 {"debugfile", required_argument, NULL, 'G'},
49                 {"link", no_argument, NULL, 'k'},
50                 {"logfile", required_argument, NULL, 'l'},
51                 {"verbose", no_argument, NULL, 'v'},
52                 {NULL, 0, NULL, 0}
53         };
54         int                     option;                 /* Command line option */
55         int                     optindex = 0;   /* used by getopt_long */
56         int                     user_id;
57
58         if (getenv("PGUSER"))
59         {
60                 pg_free(os_info.user);
61                 os_info.user = pg_strdup(getenv("PGUSER"));
62         }
63
64         os_info.progname = get_progname(argv[0]);
65         old_cluster.port = getenv("PGPORT") ? atoi(getenv("PGPORT")) : DEF_PGPORT;
66         new_cluster.port = getenv("PGPORT") ? atoi(getenv("PGPORT")) : DEF_PGPORT;
67         /* must save value, getenv()'s pointer is not stable */
68
69         user_opts.transfer_mode = TRANSFER_MODE_COPY;
70
71         /* user lookup and 'root' test must be split because of usage() */
72         user_id = get_user_info(&os_info.user);
73
74         if (argc > 1)
75         {
76                 if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0 ||
77                         strcmp(argv[1], "-?") == 0)
78                 {
79                         usage();
80                         exit(0);
81                 }
82                 if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
83                 {
84                         pg_log(PG_REPORT, "pg_upgrade " PG_VERSION "\n");
85                         exit(0);
86                 }
87         }
88
89         if (user_id == 0)
90                 pg_log(PG_FATAL, "%s: cannot be run as root\n", os_info.progname);
91
92         getcwd(os_info.cwd, MAXPGPATH);
93
94         while ((option = getopt_long(argc, argv, "d:D:b:B:cgG:kl:p:P:u:v",
95                                                                  long_options, &optindex)) != -1)
96         {
97                 switch (option)
98                 {
99                         case 'd':
100                                 old_cluster.pgdata = pg_strdup(optarg);
101                                 break;
102
103                         case 'D':
104                                 new_cluster.pgdata = pg_strdup(optarg);
105                                 break;
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 'g':
120                                 pg_log(PG_REPORT, "Running in debug mode\n");
121                                 log_opts.debug = true;
122                                 break;
123
124                         case 'G':
125                                 if ((log_opts.debug_fd = fopen(optarg, "w")) == NULL)
126                                 {
127                                         pg_log(PG_FATAL, "cannot open debug file\n");
128                                         exit(1);
129                                 }
130                                 break;
131
132                         case 'k':
133                                 user_opts.transfer_mode = TRANSFER_MODE_LINK;
134                                 break;
135
136                         case 'l':
137                                 log_opts.filename = pg_strdup(optarg);
138                                 break;
139
140                         case 'p':
141                                 if ((old_cluster.port = atoi(optarg)) <= 0)
142                                 {
143                                         pg_log(PG_FATAL, "invalid old port number\n");
144                                         exit(1);
145                                 }
146                                 break;
147
148                         case 'P':
149                                 if ((new_cluster.port = atoi(optarg)) <= 0)
150                                 {
151                                         pg_log(PG_FATAL, "invalid new port number\n");
152                                         exit(1);
153                                 }
154                                 break;
155
156                         case 'u':
157                                 pg_free(os_info.user);
158                                 os_info.user = pg_strdup(optarg);
159                                 break;
160
161                         case 'v':
162                                 pg_log(PG_REPORT, "Running in verbose mode\n");
163                                 log_opts.verbose = true;
164                                 break;
165
166                         default:
167                                 pg_log(PG_FATAL,
168                                            "Try \"%s --help\" for more information.\n",
169                                            os_info.progname);
170                                 break;
171                 }
172         }
173
174         if (log_opts.filename != NULL)
175         {
176                 /*
177                  * We must use append mode so output generated by child processes via
178                  * ">>" will not be overwritten, and we want the file truncated on
179                  * start.
180                  */
181                 /* truncate */
182                 if ((log_opts.fd = fopen(log_opts.filename, "w")) == NULL)
183                         pg_log(PG_FATAL, "Cannot write to log file %s\n", log_opts.filename);
184                 fclose(log_opts.fd);
185                 if ((log_opts.fd = fopen(log_opts.filename, "a")) == NULL)
186                         pg_log(PG_FATAL, "Cannot write to log file %s\n", log_opts.filename);
187         }
188         else
189                 log_opts.filename = strdup(DEVNULL);
190
191         /* if no debug file name, output to the terminal */
192         if (log_opts.debug && !log_opts.debug_fd)
193         {
194                 log_opts.debug_fd = fopen(DEVTTY, "w");
195                 if (!log_opts.debug_fd)
196                         pg_log(PG_FATAL, "Cannot write to terminal\n");
197         }
198
199         /* Get values from env if not already set */
200         validateDirectoryOption(&old_cluster.pgdata, "OLDDATADIR", "-d",
201                                                         "old cluster data resides");
202         validateDirectoryOption(&new_cluster.pgdata, "NEWDATADIR", "-D",
203                                                         "new cluster data resides");
204         validateDirectoryOption(&old_cluster.bindir, "OLDBINDIR", "-b",
205                                                         "old cluster binaries reside");
206         validateDirectoryOption(&new_cluster.bindir, "NEWBINDIR", "-B",
207                                                         "new cluster binaries reside");
208
209         get_pkglibdirs();
210 }
211
212
213 static void
214 usage(void)
215 {
216         printf(_("pg_upgrade upgrades a PostgreSQL cluster to a different major version.\n\
217 \nUsage:\n\
218   pg_upgrade [OPTIONS]...\n\
219 \n\
220 Options:\n\
221   -b, --old-bindir=OLDBINDIR    old cluster executable directory\n\
222   -B, --new-bindir=NEWBINDIR    new cluster executable directory\n\
223   -c, --check                   check clusters only, don't change any data\n\
224   -d, --old-datadir=OLDDATADIR  old cluster data directory\n\
225   -D, --new-datadir=NEWDATADIR  new cluster data directory\n\
226   -g, --debug                   enable debugging\n\
227   -G, --debugfile=FILENAME      output debugging activity to file\n\
228   -k, --link                    link instead of copying files to new cluster\n\
229   -l, --logfile=FILENAME        log session activity to file\n\
230   -p, --old-port=OLDPORT        old cluster port number (default %d)\n\
231   -P, --new-port=NEWPORT        new cluster port number (default %d)\n\
232   -u, --user=NAME               clusters superuser (default \"%s\")\n\
233   -v, --verbose                 enable verbose output\n\
234   -V, --version                 display version information, then exit\n\
235   -h, --help                    show this help, then exit\n\
236 \n\
237 Before running pg_upgrade you must:\n\
238   create a new database cluster (using the new version of initdb)\n\
239   shutdown the postmaster servicing the old cluster\n\
240   shutdown the postmaster servicing the new cluster\n\
241 \n\
242 When you run pg_upgrade, you must provide the following information:\n\
243   the data directory for the old cluster  (-d OLDDATADIR)\n\
244   the data directory for the new cluster  (-D NEWDATADIR)\n\
245   the \"bin\" directory for the old version (-b OLDBINDIR)\n\
246   the \"bin\" directory for the new version (-B NEWBINDIR)\n\
247 \n\
248 For example:\n\
249   pg_upgrade -d oldCluster/data -D newCluster/data -b oldCluster/bin -B newCluster/bin\n\
250 or\n"), old_cluster.port, new_cluster.port, os_info.user);
251 #ifndef WIN32
252         printf(_("\
253   $ export OLDDATADIR=oldCluster/data\n\
254   $ export NEWDATADIR=newCluster/data\n\
255   $ export OLDBINDIR=oldCluster/bin\n\
256   $ export NEWBINDIR=newCluster/bin\n\
257   $ pg_upgrade\n"));
258 #else
259         printf(_("\
260   C:\\> set OLDDATADIR=oldCluster/data\n\
261   C:\\> set NEWDATADIR=newCluster/data\n\
262   C:\\> set OLDBINDIR=oldCluster/bin\n\
263   C:\\> set NEWBINDIR=newCluster/bin\n\
264   C:\\> pg_upgrade\n"));
265 #endif
266 }
267
268
269 /*
270  * validateDirectoryOption()
271  *
272  * Validates a directory option.
273  *      dirpath           - the directory name supplied on the command line
274  *      envVarName        - the name of an environment variable to get if dirpath is NULL
275  *      cmdLineOption - the command line option corresponds to this directory (-o, -O, -n, -N)
276  *      description   - a description of this directory option
277  *
278  * We use the last two arguments to construct a meaningful error message if the
279  * user hasn't provided the required directory name.
280  */
281 static void
282 validateDirectoryOption(char **dirpath,
283                                         char *envVarName, char *cmdLineOption, char *description)
284 {
285         if (*dirpath == NULL || (strlen(*dirpath) == 0))
286         {
287                 const char *envVar;
288
289                 if ((envVar = getenv(envVarName)) && strlen(envVar))
290                         *dirpath = pg_strdup(envVar);
291                 else
292                 {
293                         pg_log(PG_FATAL, "You must identify the directory where the %s\n"
294                                    "Please use the %s command-line option or the %s environment variable\n",
295                                    description, cmdLineOption, envVarName);
296                 }
297         }
298
299         /*
300          * Trim off any trailing path separators
301          */
302 #ifndef WIN32
303         if ((*dirpath)[strlen(*dirpath) - 1] == '/')
304 #else
305         if ((*dirpath)[strlen(*dirpath) - 1] == '/' ||
306                 (*dirpath)[strlen(*dirpath) - 1] == '\\')
307 #endif
308                 (*dirpath)[strlen(*dirpath) - 1] = 0;
309 }
310
311
312 static void
313 get_pkglibdirs(void)
314 {
315         /*
316          * we do not need to know the libpath in the old cluster, and might not
317          * have a working pg_config to ask for it anyway.
318          */
319         old_cluster.libpath = NULL;
320         new_cluster.libpath = get_pkglibdir(new_cluster.bindir);
321 }
322
323
324 static char *
325 get_pkglibdir(const char *bindir)
326 {
327         char            cmd[MAXPGPATH];
328         char            bufin[MAX_STRING];
329         FILE       *output;
330         int                     i;
331
332         snprintf(cmd, sizeof(cmd), "\"%s/pg_config\" --pkglibdir", bindir);
333
334         if ((output = popen(cmd, "r")) == NULL)
335                 pg_log(PG_FATAL, "Could not get pkglibdir data: %s\n",
336                            getErrorText(errno));
337
338         fgets(bufin, sizeof(bufin), output);
339
340         if (output)
341                 pclose(output);
342
343         /* Remove trailing newline */
344         i = strlen(bufin) - 1;
345
346         if (bufin[i] == '\n')
347                 bufin[i] = '\0';
348
349         return pg_strdup(bufin);
350 }