]> granicus.if.org Git - postgresql/blob - contrib/pg_upgrade/option.c
Stamp copyrights for year 2011.
[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_nicely(false);
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_nicely(false);
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_nicely(false);
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_nicely(false);
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_nicely(false);
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(_("\nUsage: pg_upgrade [OPTIONS]...\n\
217 \n\
218 Options:\n\
219  -b, --old-bindir=old_bindir      old cluster executable directory\n\
220  -B, --new-bindir=new_bindir      new cluster executable directory\n\
221  -c, --check                      check clusters only, don't change any data\n\
222  -d, --old-datadir=old_datadir    old cluster data directory\n\
223  -D, --new-datadir=new_datadir    new cluster data directory\n\
224  -g, --debug                      enable debugging\n\
225  -G, --debugfile=debug_filename   output debugging activity to file\n\
226  -k, --link                       link instead of copying files to new cluster\n\
227  -l, --logfile=log_filename       log session activity to file\n\
228  -p, --old-port=old_portnum       old cluster port number (default %d)\n\
229  -P, --new-port=new_portnum       new cluster port number (default %d)\n\
230  -u, --user=username              clusters superuser (default \"%s\")\n\
231  -v, --verbose                    enable verbose output\n\
232  -V, --version                    display version information, then exit\n\
233  -h, --help                       show this help, then exit\n\
234 \n\
235 Before running pg_upgrade you must:\n\
236   create a new database cluster (using the new version of initdb)\n\
237   shutdown the postmaster servicing the old cluster\n\
238   shutdown the postmaster servicing the new cluster\n\
239 \n\
240 When you run pg_upgrade, you must provide the following information:\n\
241   the data directory for the old cluster  (-d OLDDATADIR)\n\
242   the data directory for the new cluster  (-D NEWDATADIR)\n\
243   the 'bin' directory for the old version (-b OLDBINDIR)\n\
244   the 'bin' directory for the new version (-B NEWBINDIR)\n\
245 \n\
246 For example:\n\
247   pg_upgrade -d oldCluster/data -D newCluster/data -b oldCluster/bin -B newCluster/bin\n\
248 or\n"), old_cluster.port, new_cluster.port, os_info.user);
249 #ifndef WIN32
250         printf(_("\
251   $ export OLDDATADIR=oldCluster/data\n\
252   $ export NEWDATADIR=newCluster/data\n\
253   $ export OLDBINDIR=oldCluster/bin\n\
254   $ export NEWBINDIR=newCluster/bin\n\
255   $ pg_upgrade\n"));
256 #else
257         printf(_("\
258   C:\\> set OLDDATADIR=oldCluster/data\n\
259   C:\\> set NEWDATADIR=newCluster/data\n\
260   C:\\> set OLDBINDIR=oldCluster/bin\n\
261   C:\\> set NEWBINDIR=newCluster/bin\n\
262   C:\\> pg_upgrade\n"));
263 #endif
264 }
265
266
267 /*
268  * validateDirectoryOption()
269  *
270  * Validates a directory option.
271  *      dirpath           - the directory name supplied on the command line
272  *      envVarName        - the name of an environment variable to get if dirpath is NULL
273  *      cmdLineOption - the command line option corresponds to this directory (-o, -O, -n, -N)
274  *      description   - a description of this directory option
275  *
276  * We use the last two arguments to construct a meaningful error message if the
277  * user hasn't provided the required directory name.
278  */
279 static void
280 validateDirectoryOption(char **dirpath,
281                                         char *envVarName, char *cmdLineOption, char *description)
282 {
283         if (*dirpath == NULL || (strlen(*dirpath) == 0))
284         {
285                 const char *envVar;
286
287                 if ((envVar = getenv(envVarName)) && strlen(envVar))
288                         *dirpath = pg_strdup(envVar);
289                 else
290                 {
291                         pg_log(PG_FATAL, "You must identify the directory where the %s\n"
292                                    "Please use the %s command-line option or the %s environment variable\n",
293                                    description, cmdLineOption, envVarName);
294                 }
295         }
296
297         /*
298          * Trim off any trailing path separators
299          */
300 #ifndef WIN32
301         if ((*dirpath)[strlen(*dirpath) - 1] == '/')
302 #else
303         if ((*dirpath)[strlen(*dirpath) - 1] == '/' ||
304                 (*dirpath)[strlen(*dirpath) - 1] == '\\')
305 #endif
306                 (*dirpath)[strlen(*dirpath) - 1] = 0;
307 }
308
309
310 static void
311 get_pkglibdirs(void)
312 {
313         /*
314          * we do not need to know the libpath in the old cluster, and might not
315          * have a working pg_config to ask for it anyway.
316          */
317         old_cluster.libpath = NULL;
318         new_cluster.libpath = get_pkglibdir(new_cluster.bindir);
319 }
320
321
322 static char *
323 get_pkglibdir(const char *bindir)
324 {
325         char            cmd[MAXPGPATH];
326         char            bufin[MAX_STRING];
327         FILE       *output;
328         int                     i;
329
330         snprintf(cmd, sizeof(cmd), "\"%s/pg_config\" --pkglibdir", bindir);
331
332         if ((output = popen(cmd, "r")) == NULL)
333                 pg_log(PG_FATAL, "Could not get pkglibdir data: %s\n",
334                            getErrorText(errno));
335
336         fgets(bufin, sizeof(bufin), output);
337
338         if (output)
339                 pclose(output);
340
341         /* Remove trailing newline */
342         i = strlen(bufin) - 1;
343
344         if (bufin[i] == '\n')
345                 bufin[i] = '\0';
346
347         return pg_strdup(bufin);
348 }