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