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