]> granicus.if.org Git - postgresql/blob - contrib/pg_upgrade/option.c
Add bug report line to 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         printf(_("\nReport bugs to <pgsql-bugs@postgresql.org>.\n"));
267 }
268
269
270 /*
271  * validateDirectoryOption()
272  *
273  * Validates a directory option.
274  *      dirpath           - the directory name supplied on the command line
275  *      envVarName        - the name of an environment variable to get if dirpath is NULL
276  *      cmdLineOption - the command line option corresponds to this directory (-o, -O, -n, -N)
277  *      description   - a description of this directory option
278  *
279  * We use the last two arguments to construct a meaningful error message if the
280  * user hasn't provided the required directory name.
281  */
282 static void
283 validateDirectoryOption(char **dirpath,
284                                         char *envVarName, char *cmdLineOption, char *description)
285 {
286         if (*dirpath == NULL || (strlen(*dirpath) == 0))
287         {
288                 const char *envVar;
289
290                 if ((envVar = getenv(envVarName)) && strlen(envVar))
291                         *dirpath = pg_strdup(envVar);
292                 else
293                 {
294                         pg_log(PG_FATAL, "You must identify the directory where the %s\n"
295                                    "Please use the %s command-line option or the %s environment variable\n",
296                                    description, cmdLineOption, envVarName);
297                 }
298         }
299
300         /*
301          * Trim off any trailing path separators
302          */
303 #ifndef WIN32
304         if ((*dirpath)[strlen(*dirpath) - 1] == '/')
305 #else
306         if ((*dirpath)[strlen(*dirpath) - 1] == '/' ||
307                 (*dirpath)[strlen(*dirpath) - 1] == '\\')
308 #endif
309                 (*dirpath)[strlen(*dirpath) - 1] = 0;
310 }
311
312
313 static void
314 get_pkglibdirs(void)
315 {
316         /*
317          * we do not need to know the libpath in the old cluster, and might not
318          * have a working pg_config to ask for it anyway.
319          */
320         old_cluster.libpath = NULL;
321         new_cluster.libpath = get_pkglibdir(new_cluster.bindir);
322 }
323
324
325 static char *
326 get_pkglibdir(const char *bindir)
327 {
328         char            cmd[MAXPGPATH];
329         char            bufin[MAX_STRING];
330         FILE       *output;
331         int                     i;
332
333         snprintf(cmd, sizeof(cmd), "\"%s/pg_config\" --pkglibdir", bindir);
334
335         if ((output = popen(cmd, "r")) == NULL)
336                 pg_log(PG_FATAL, "Could not get pkglibdir data: %s\n",
337                            getErrorText(errno));
338
339         fgets(bufin, sizeof(bufin), output);
340
341         if (output)
342                 pclose(output);
343
344         /* Remove trailing newline */
345         i = strlen(bufin) - 1;
346
347         if (bufin[i] == '\n')
348                 bufin[i] = '\0';
349
350         return pg_strdup(bufin);
351 }