]> granicus.if.org Git - postgresql/blob - contrib/pg_upgrade/option.c
83e0ea6c151af337c1a26bf58d9d53740fd9342e
[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  -d, --old-datadir=OLDDATADIR    old cluster data directory\n\
230  -D, --new-datadir=NEWDATADIR    new cluster data directory\n\
231  -b, --old-bindir=OLDBINDIR      old cluster executable directory\n\
232  -B, --new-bindir=NEWBINDIR      new cluster executable directory\n\
233  -p, --old-port=portnum          old cluster port number (default %d)\n\
234  -P, --new-port=portnum          new cluster port number (default %d)\n\
235  \n\
236  -u, --user=username             clusters superuser (default \"%s\")\n\
237  -c, --check                     check clusters only, don't change any data\n\
238  -g, --debug                     enable debugging\n\
239  -G, --debugfile=DEBUGFILENAME   output debugging activity to file\n\
240  -k, --link                      link instead of copying files to new cluster\n\
241  -l, --logfile=LOGFILENAME       log session activity to file\n\
242  -v, --verbose                   enable verbose output\n\
243  -V, --version                   display version information, then exit\n\
244  -h, --help                      show this help, then exit\n\
245 \n\
246 Before running pg_upgrade you must:\n\
247   create a new database cluster (using the new version of initdb)\n\
248   shutdown the postmaster servicing the old cluster\n\
249   shutdown the postmaster servicing the new cluster\n\
250 \n\
251 When you run pg_upgrade, you must provide the following information:\n\
252   the data directory for the old cluster  (-d OLDDATADIR)\n\
253   the data directory for the new cluster  (-D NEWDATADIR)\n\
254   the 'bin' directory for the old version (-b OLDBINDIR)\n\
255   the 'bin' directory for the new version (-B NEWBINDIR)\n\
256 \n\
257 For example:\n\
258   pg_upgrade -d oldCluster/data -D newCluster/data -b oldCluster/bin -B newCluster/bin\n\
259 or\n"), ctx->old.port, ctx->new.port, ctx->user);
260 #ifndef WIN32
261         printf(_("\
262   $ export OLDDATADIR=oldCluster/data\n\
263   $ export NEWDATADIR=newCluster/data\n\
264   $ export OLDBINDIR=oldCluster/bin\n\
265   $ export NEWBINDIR=newCluster/bin\n\
266   $ pg_upgrade\n"));
267 #else
268         printf(_("\
269   C:\\> set OLDDATADIR=oldCluster/data\n\
270   C:\\> set NEWDATADIR=newCluster/data\n\
271   C:\\> set OLDBINDIR=oldCluster/bin\n\
272   C:\\> set NEWBINDIR=newCluster/bin\n\
273   C:\\> pg_upgrade\n"));
274 #endif
275         printf(_("\n\
276 You may find it useful to save the preceding 5 commands in a shell script\n\
277 \n\
278 Report bugs to <pg-migrator-general@lists.pgfoundry.org>\n"));
279 }
280
281
282 /*
283  * validateDirectoryOption()
284  *
285  * Validates a directory option.
286  *      dirpath           - the directory name supplied on the command line
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 validateDirectoryOption(migratorContext *ctx, char **dirpath,
296                                         char *envVarName, char *cmdLineOption, char *description)
297 {
298         if (*dirpath == NULL || (strlen(*dirpath) == 0))
299         {
300                 const char *envVar;
301
302                 if ((envVar = getenv(envVarName)) && strlen(envVar))
303                         *dirpath = pg_strdup(ctx, envVar);
304                 else
305                 {
306                         pg_log(ctx, PG_FATAL, "You must identify the directory where the %s\n"
307                                    "Please use the %s command-line option or the %s environment variable\n",
308                                    description, cmdLineOption, envVarName);
309                 }
310         }
311
312         /*
313          * Trim off any trailing path separators
314          */
315 #ifndef WIN32
316         if ((*dirpath)[strlen(*dirpath) - 1] == '/')
317 #else
318         if ((*dirpath)[strlen(*dirpath) - 1] == '/' ||
319             (*dirpath)[strlen(*dirpath) - 1] == '\\')
320 #endif
321                 (*dirpath)[strlen(*dirpath) - 1] = 0;
322 }
323
324
325 static void
326 get_pkglibdirs(migratorContext *ctx)
327 {
328         ctx->old.libpath = get_pkglibdir(ctx, ctx->old.bindir);
329         ctx->new.libpath = get_pkglibdir(ctx, ctx->new.bindir);
330 }
331
332
333 static char *
334 get_pkglibdir(migratorContext *ctx, const char *bindir)
335 {
336         char            cmd[MAXPGPATH];
337         char            bufin[MAX_STRING];
338         FILE       *output;
339         int                     i;
340
341         snprintf(cmd, sizeof(cmd), "\"%s/pg_config\" --pkglibdir", bindir);
342
343         if ((output = popen(cmd, "r")) == NULL)
344                 pg_log(ctx, PG_FATAL, "Could not get pkglibdir data: %s\n",
345                            getErrorText(errno));
346
347         fgets(bufin, sizeof(bufin), output);
348
349         if (output)
350                 pclose(output);
351
352         /* Remove trailing newline */
353         i = strlen(bufin) - 1;
354
355         if (bufin[i] == '\n')
356                 bufin[i] = '\0';
357
358         return pg_strdup(ctx, bufin);
359 }