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