]> granicus.if.org Git - postgresql/blob - contrib/pg_upgrade/option.c
Add pg_upgrade to /contrib; will be in 9.0 beta2.
[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
52         if (getenv("PGUSER"))
53         {
54                 pg_free(ctx->user);
55                 ctx->user = pg_strdup(ctx, getenv("PGUSER"));
56         }
57
58         ctx->progname = get_progname(argv[0]);
59         ctx->old.port = getenv("PGPORT") ? atoi(getenv("PGPORT")) : DEF_PGPORT;
60         ctx->new.port = getenv("PGPORT") ? atoi(getenv("PGPORT")) : DEF_PGPORT;
61         /* must save value, getenv()'s pointer is not stable */
62
63         ctx->transfer_mode = TRANSFER_MODE_COPY;
64
65         if (argc > 1)
66         {
67                 if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0 ||
68                         strcmp(argv[1], "-?") == 0)
69                 {
70                         usage(ctx);
71                         exit_nicely(ctx, false);
72                 }
73                 if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
74                 {
75                         pg_log(ctx, PG_REPORT, "pg_upgrade " PG_VERSION "\n");
76                         exit_nicely(ctx, false);
77                 }
78         }
79
80         if ((get_user_info(ctx, &ctx->user)) == 0)
81                 pg_log(ctx, PG_FATAL, "%s: cannot be run as root\n", ctx->progname);
82
83 #ifndef WIN32
84         get_home_path(ctx->home_dir);
85 #else
86         {
87                 char       *tmppath;
88
89                 /* TMP is the best place on Windows, rather than APPDATA */
90                 if ((tmppath = getenv("TMP")) == NULL)
91                         pg_log(ctx, PG_FATAL, "TMP environment variable is not set.\n");
92                 snprintf(ctx->home_dir, MAXPGPATH, "%s", tmppath);
93         }
94 #endif
95
96         snprintf(ctx->output_dir, MAXPGPATH, "%s/" OUTPUT_SUBDIR, ctx->home_dir);
97
98         while ((option = getopt_long(argc, argv, "d:D:b:B:cgG:kl:p:P:u:v",
99                                                                  long_options, &optindex)) != -1)
100         {
101                 switch (option)
102                 {
103                         case 'd':
104                                 ctx->old.pgdata = pg_strdup(ctx, optarg);
105                                 break;
106
107                         case 'D':
108                                 ctx->new.pgdata = pg_strdup(ctx, optarg);
109                                 break;
110
111                         case 'b':
112                                 ctx->old.bindir = pg_strdup(ctx, optarg);
113                                 break;
114
115                         case 'B':
116                                 ctx->new.bindir = pg_strdup(ctx, optarg);
117                                 break;
118
119                         case 'c':
120                                 ctx->check = true;
121                                 break;
122
123                         case 'g':
124                                 pg_log(ctx, PG_REPORT, "Running in debug mode\n");
125                                 ctx->debug = true;
126                                 break;
127
128                         case 'G':
129                                 if ((ctx->debug_fd = fopen(optarg, "w")) == NULL)
130                                 {
131                                         pg_log(ctx, PG_FATAL, "cannot open debug file\n");
132                                         exit_nicely(ctx, false);
133                                 }
134                                 break;
135
136                         case 'k':
137                                 ctx->transfer_mode = TRANSFER_MODE_LINK;
138                                 break;
139
140                         case 'l':
141                                 ctx->logfile = pg_strdup(ctx, optarg);
142                                 break;
143
144                         case 'p':
145                                 if ((ctx->old.port = atoi(optarg)) <= 0)
146                                 {
147                                         pg_log(ctx, PG_FATAL, "invalid old port number\n");
148                                         exit_nicely(ctx, false);
149                                 }
150                                 break;
151
152                         case 'P':
153                                 if ((ctx->new.port = atoi(optarg)) <= 0)
154                                 {
155                                         pg_log(ctx, PG_FATAL, "invalid new port number\n");
156                                         exit_nicely(ctx, false);
157                                 }
158                                 break;
159
160                         case 'u':
161                                 pg_free(ctx->user);
162                                 ctx->user = pg_strdup(ctx, optarg);
163                                 break;
164
165                         case 'v':
166                                 pg_log(ctx, PG_REPORT, "Running in verbose mode\n");
167                                 ctx->verbose = true;
168                                 break;
169
170                         default:
171                                 pg_log(ctx, PG_FATAL,
172                                            "Try \"%s --help\" for more information.\n",
173                                            ctx->progname);
174                                 break;
175                 }
176         }
177
178         if (ctx->logfile != NULL)
179         {
180                 /*
181                  * We must use append mode so output generated by child processes via
182                  * ">>" will not be overwritten, and we want the file truncated on
183                  * start.
184                  */
185                 /* truncate */
186                 ctx->log_fd = fopen(ctx->logfile, "w");
187                 if (!ctx->log_fd)
188                         pg_log(ctx, PG_FATAL, "Cannot write to log file %s\n", ctx->logfile);
189                 fclose(ctx->log_fd);
190                 ctx->log_fd = fopen(ctx->logfile, "a");
191                 if (!ctx->log_fd)
192                         pg_log(ctx, PG_FATAL, "Cannot write to log file %s\n", ctx->logfile);
193         }
194         else
195                 ctx->logfile = strdup(DEVNULL);
196
197         /* if no debug file name, output to the terminal */
198         if (ctx->debug && !ctx->debug_fd)
199         {
200                 ctx->debug_fd = fopen(DEVTTY, "w");
201                 if (!ctx->debug_fd)
202                         pg_log(ctx, PG_FATAL, "Cannot write to terminal\n");
203         }
204
205         /* Get values from env if not already set */
206         validateDirectoryOption(ctx, &ctx->old.pgdata, "OLDDATADIR", "-d",
207                                                         "old cluster data resides");
208         validateDirectoryOption(ctx, &ctx->new.pgdata, "NEWDATADIR", "-D",
209                                                         "new cluster data resides");
210         validateDirectoryOption(ctx, &ctx->old.bindir, "OLDBINDIR", "-b",
211                                                         "old cluster binaries reside");
212         validateDirectoryOption(ctx, &ctx->new.bindir, "NEWBINDIR", "-B",
213                                                         "new cluster binaries reside");
214
215         get_pkglibdirs(ctx);
216 }
217
218
219 static void
220 usage(migratorContext *ctx)
221 {
222         printf(_("\nUsage: pg_upgrade [OPTIONS]...\n\
223 \n\
224 Options:\n\
225  -d, --old-datadir=OLDDATADIR    old cluster data directory\n\
226  -D, --new-datadir=NEWDATADIR    new cluster data directory\n\
227  -b, --old-bindir=OLDBINDIR      old cluster executable directory\n\
228  -B, --new-bindir=NEWBINDIR      new cluster executable directory\n\
229  -p, --old-port=portnum          old cluster port number (default %d)\n\
230  -P, --new-port=portnum          new cluster port number (default %d)\n\
231  \n\
232  -u, --user=username             clusters superuser (default \"%s\")\n\
233  -c, --check                     check clusters only, don't change any data\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  -v, --verbose                   enable verbose output\n\
239  -V, --version                   display version information, then exit\n\
240  -h, --help                      show this help, then exit\n\
241 \n\
242 Before running pg_upgrade you must:\n\
243   create a new database cluster (using the new version of initdb)\n\
244   shutdown the postmaster servicing the old cluster\n\
245   shutdown the postmaster servicing the new cluster\n\
246 \n\
247 When you run pg_upgrade, you must provide the following information:\n\
248   the data directory for the old cluster  (-d OLDDATADIR)\n\
249   the data directory for the new cluster  (-D NEWDATADIR)\n\
250   the 'bin' directory for the old version (-b OLDBINDIR)\n\
251   the 'bin' directory for the new version (-B NEWBINDIR)\n\
252 \n\
253 For example:\n\
254   pg_upgrade -d oldCluster/data -D newCluster/data -b oldCluster/bin -B newCluster/bin\n\
255 or\n"), ctx->old.port, ctx->new.port, ctx->user);
256 #ifndef WIN32
257         printf(_("\
258   $ export OLDDATADIR=oldCluster/data\n\
259   $ export NEWDATADIR=newCluster/data\n\
260   $ export OLDBINDIR=oldCluster/bin\n\
261   $ export NEWBINDIR=newCluster/bin\n\
262   $ pg_upgrade\n"));
263 #else
264         printf(_("\
265   C:\\> set OLDDATADIR=oldCluster/data\n\
266   C:\\> set NEWDATADIR=newCluster/data\n\
267   C:\\> set OLDBINDIR=oldCluster/bin\n\
268   C:\\> set NEWBINDIR=newCluster/bin\n\
269   C:\\> pg_upgrade\n"));
270 #endif
271         printf(_("\n\
272 You may find it useful to save the preceding 5 commands in a shell script\n\
273 \n\
274 Report bugs to <pg-migrator-general@lists.pgfoundry.org>\n"));
275 }
276
277
278 /*
279  * validateDirectoryOption()
280  *
281  * Validates a directory option.
282  *      dirpath           - the directory name supplied on the command line
283  *      envVarName        - the name of an environment variable to get if dirpath is NULL
284  *      cmdLineOption - the command line option corresponds to this directory (-o, -O, -n, -N)
285  *      description   - a description of this directory option
286  *
287  * We use the last two arguments to construct a meaningful error message if the
288  * user hasn't provided the required directory name.
289  */
290 static void
291 validateDirectoryOption(migratorContext *ctx, char **dirpath,
292                                         char *envVarName, char *cmdLineOption, char *description)
293 {
294         if (*dirpath == NULL || (strlen(*dirpath) == 0))
295         {
296                 const char *envVar;
297
298                 if ((envVar = getenv(envVarName)) && strlen(envVar))
299                         *dirpath = pg_strdup(ctx, envVar);
300                 else
301                 {
302                         pg_log(ctx, PG_FATAL, "You must identify the directory where the %s\n"
303                                    "Please use the %s command-line option or the %s environment variable\n",
304                                    description, cmdLineOption, envVarName);
305                 }
306         }
307
308         /*
309          * Trim off any trailing path separators
310          */
311         if ((*dirpath)[strlen(*dirpath) - 1] == pathSeparator)
312                 (*dirpath)[strlen(*dirpath) - 1] = 0;
313
314 }
315
316
317 static void
318 get_pkglibdirs(migratorContext *ctx)
319 {
320         ctx->old.libpath = get_pkglibdir(ctx, ctx->old.bindir);
321         ctx->new.libpath = get_pkglibdir(ctx, ctx->new.bindir);
322 }
323
324
325 static char *
326 get_pkglibdir(migratorContext *ctx, 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(ctx, 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(ctx, bufin);
351 }