]> granicus.if.org Git - postgresql/blob - contrib/pg_upgrade/option.c
In pg_upgrade, use the new postmaster -C option to get the real data
[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 "postgres.h"
11
12 #include "pg_upgrade.h"
13
14 #include "getopt_long.h"
15
16 #ifdef WIN32
17 #include <io.h>
18 #endif
19
20
21 static void usage(void);
22 static void check_required_directory(char **dirpath,
23                                    char *envVarName, char *cmdLineOption, char *description);
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                     os_user_effective_id;
57
58         user_opts.transfer_mode = TRANSFER_MODE_COPY;
59
60         os_info.progname = get_progname(argv[0]);
61
62         /* Process libpq env. variables; load values here for usage() output */
63         old_cluster.port = getenv("PGPORTOLD") ? atoi(getenv("PGPORTOLD")) : DEF_PGUPORT;
64         new_cluster.port = getenv("PGPORTNEW") ? atoi(getenv("PGPORTNEW")) : DEF_PGUPORT;
65
66         os_user_effective_id = get_user_info(&os_info.user);
67         /* we override just the database user name;  we got the OS id above */
68         if (getenv("PGUSER"))
69         {
70                 pg_free(os_info.user);
71                 /* must save value, getenv()'s pointer is not stable */
72                 os_info.user = pg_strdup(getenv("PGUSER"));
73         }
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(0);
82                 }
83                 if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
84                 {
85                         puts("pg_upgrade (PostgreSQL) " PG_VERSION);
86                         exit(0);
87                 }
88         }
89
90         /* Allow help and version to be run as root, so do the test here. */
91         if (os_user_effective_id == 0)
92                 pg_log(PG_FATAL, "%s: cannot be run as root\n", os_info.progname);
93
94         getcwd(os_info.cwd, MAXPGPATH);
95
96         while ((option = getopt_long(argc, argv, "d:D:b:B:cgG:kl:p:P:u:v",
97                                                                  long_options, &optindex)) != -1)
98         {
99                 switch (option)
100                 {
101                         case 'b':
102                                 old_cluster.bindir = pg_strdup(optarg);
103                                 break;
104
105                         case 'B':
106                                 new_cluster.bindir = pg_strdup(optarg);
107                                 break;
108
109                         case 'c':
110                                 user_opts.check = true;
111                                 break;
112
113                         case 'd':
114                                 old_cluster.pgdata = pg_strdup(optarg);
115                                 old_cluster.pgconfig = pg_strdup(optarg);
116                                 break;
117
118                         case 'D':
119                                 new_cluster.pgdata = pg_strdup(optarg);
120                                 new_cluster.pgconfig = pg_strdup(optarg);
121                                 break;
122
123                         case 'g':
124                                 pg_log(PG_REPORT, "Running in debug mode\n");
125                                 log_opts.debug = true;
126                                 break;
127
128                         case 'G':
129                                 if ((log_opts.debug_fd = fopen(optarg, "w")) == NULL)
130                                 {
131                                         pg_log(PG_FATAL, "cannot open debug file\n");
132                                         exit(1);
133                                 }
134                                 break;
135
136                         case 'k':
137                                 user_opts.transfer_mode = TRANSFER_MODE_LINK;
138                                 break;
139
140                         case 'l':
141                                 log_opts.filename = pg_strdup(optarg);
142                                 break;
143
144                         case 'p':
145                                 if ((old_cluster.port = atoi(optarg)) <= 0)
146                                 {
147                                         pg_log(PG_FATAL, "invalid old port number\n");
148                                         exit(1);
149                                 }
150                                 break;
151
152                         case 'P':
153                                 if ((new_cluster.port = atoi(optarg)) <= 0)
154                                 {
155                                         pg_log(PG_FATAL, "invalid new port number\n");
156                                         exit(1);
157                                 }
158                                 break;
159
160                         case 'u':
161                                 pg_free(os_info.user);
162                                 os_info.user = pg_strdup(optarg);
163
164                                 /*
165                                  * Push the user name into the environment so pre-9.1
166                                  * pg_ctl/libpq uses it.
167                                  */
168                                 pg_putenv("PGUSER", os_info.user);
169                                 break;
170
171                         case 'v':
172                                 pg_log(PG_REPORT, "Running in verbose mode\n");
173                                 log_opts.verbose = true;
174                                 break;
175
176                         default:
177                                 pg_log(PG_FATAL,
178                                            "Try \"%s --help\" for more information.\n",
179                                            os_info.progname);
180                                 break;
181                 }
182         }
183
184         if (log_opts.filename != NULL)
185         {
186                 /*
187                  * We must use append mode so output generated by child processes via
188                  * ">>" will not be overwritten, and we want the file truncated on
189                  * start.
190                  */
191                 /* truncate */
192                 if ((log_opts.fd = fopen(log_opts.filename, "w")) == NULL)
193                         pg_log(PG_FATAL, "cannot write to log file %s\n", log_opts.filename);
194                 fclose(log_opts.fd);
195                 if ((log_opts.fd = fopen(log_opts.filename, "a")) == NULL)
196                         pg_log(PG_FATAL, "cannot write to log file %s\n", log_opts.filename);
197         }
198         else
199                 log_opts.filename = pg_strdup(DEVNULL);
200
201         /* WIN32 files do not accept writes from multiple processes */
202 #ifndef WIN32
203         log_opts.filename2 = pg_strdup(log_opts.filename);
204 #else
205         log_opts.filename2 = pg_strdup(DEVNULL);
206 #endif
207                 
208         /* if no debug file name, output to the terminal */
209         if (log_opts.debug && !log_opts.debug_fd)
210         {
211                 log_opts.debug_fd = fopen(DEVTTY, "w");
212                 if (!log_opts.debug_fd)
213                         pg_log(PG_FATAL, "cannot write to terminal\n");
214         }
215
216         /* Get values from env if not already set */
217         check_required_directory(&old_cluster.bindir, "PGBINOLD", "-b",
218                                                         "old cluster binaries reside");
219         check_required_directory(&new_cluster.bindir, "PGBINNEW", "-B",
220                                                         "new cluster binaries reside");
221         check_required_directory(&old_cluster.pgdata, "PGDATAOLD", "-d",
222                                                         "old cluster data resides");
223         check_required_directory(&new_cluster.pgdata, "PGDATANEW", "-D",
224                                                         "new cluster data resides");
225 }
226
227
228 static void
229 usage(void)
230 {
231         printf(_("pg_upgrade upgrades a PostgreSQL cluster to a different major version.\n\
232 \nUsage:\n\
233   pg_upgrade [OPTIONS]...\n\
234 \n\
235 Options:\n\
236   -b, --old-bindir=OLDBINDIR    old cluster executable directory\n\
237   -B, --new-bindir=NEWBINDIR    new cluster executable directory\n\
238   -c, --check                   check clusters only, don't change any data\n\
239   -d, --old-datadir=OLDDATADIR  old cluster data directory\n\
240   -D, --new-datadir=NEWDATADIR  new cluster data directory\n\
241   -g, --debug                   enable debugging\n\
242   -G, --debugfile=FILENAME      output debugging activity to file\n\
243   -k, --link                    link instead of copying files to new cluster\n\
244   -l, --logfile=FILENAME        log session activity to file\n\
245   -p, --old-port=OLDPORT        old cluster port number (default %d)\n\
246   -P, --new-port=NEWPORT        new cluster port number (default %d)\n\
247   -u, --user=NAME               clusters superuser (default \"%s\")\n\
248   -v, --verbose                 enable verbose output\n\
249   -V, --version                 display version information, then exit\n\
250   -h, --help                    show this help, then exit\n\
251 \n\
252 Before running pg_upgrade you must:\n\
253   create a new database cluster (using the new version of initdb)\n\
254   shutdown the postmaster servicing the old cluster\n\
255   shutdown the postmaster servicing the new cluster\n\
256 \n\
257 When you run pg_upgrade, you must provide the following information:\n\
258   the data directory for the old cluster  (-d OLDDATADIR)\n\
259   the data directory for the new cluster  (-D NEWDATADIR)\n\
260   the \"bin\" directory for the old version (-b OLDBINDIR)\n\
261   the \"bin\" directory for the new version (-B NEWBINDIR)\n\
262 \n\
263 For example:\n\
264   pg_upgrade -d oldCluster/data -D newCluster/data -b oldCluster/bin -B newCluster/bin\n\
265 or\n"), old_cluster.port, new_cluster.port, os_info.user);
266 #ifndef WIN32
267         printf(_("\
268   $ export PGDATAOLD=oldCluster/data\n\
269   $ export PGDATANEW=newCluster/data\n\
270   $ export PGBINOLD=oldCluster/bin\n\
271   $ export PGBINNEW=newCluster/bin\n\
272   $ pg_upgrade\n"));
273 #else
274         printf(_("\
275   C:\\> set PGDATAOLD=oldCluster/data\n\
276   C:\\> set PGDATANEW=newCluster/data\n\
277   C:\\> set PGBINOLD=oldCluster/bin\n\
278   C:\\> set PGBINNEW=newCluster/bin\n\
279   C:\\> pg_upgrade\n"));
280 #endif
281         printf(_("\nReport bugs to <pgsql-bugs@postgresql.org>.\n"));
282 }
283
284
285 /*
286  * check_required_directory()
287  *
288  * Checks a directory option.
289  *      dirpath           - the directory name supplied on the command line
290  *      envVarName        - the name of an environment variable to get if dirpath is NULL
291  *      cmdLineOption - the command line option corresponds to this directory (-o, -O, -n, -N)
292  *      description   - a description of this directory option
293  *
294  * We use the last two arguments to construct a meaningful error message if the
295  * user hasn't provided the required directory name.
296  */
297 static void
298 check_required_directory(char **dirpath, char *envVarName,
299                                                 char *cmdLineOption, char *description)
300 {
301         if (*dirpath == NULL || strlen(*dirpath) == 0)
302         {
303                 const char *envVar;
304
305                 if ((envVar = getenv(envVarName)) && strlen(envVar))
306                         *dirpath = pg_strdup(envVar);
307                 else
308                         pg_log(PG_FATAL, "You must identify the directory where the %s.\n"
309                                    "Please use the %s command-line option or the %s environment variable.\n",
310                                    description, cmdLineOption, envVarName);
311         }
312
313         /*
314          * Trim off any trailing path separators
315          */
316 #ifndef WIN32
317         if ((*dirpath)[strlen(*dirpath) - 1] == '/')
318 #else
319         if ((*dirpath)[strlen(*dirpath) - 1] == '/' ||
320                 (*dirpath)[strlen(*dirpath) - 1] == '\\')
321 #endif
322                 (*dirpath)[strlen(*dirpath) - 1] = 0;
323 }
324
325 /*
326  * adjust_data_dir
327  *
328  * If a configuration-only directory was specified, find the real data dir
329  * by quering the running server.  This has limited checking because we
330  * can't check for a running server because we can't find postmaster.pid.
331  */
332 void
333 adjust_data_dir(ClusterInfo *cluster)
334 {
335         char            filename[MAXPGPATH];
336         char            cmd[MAXPGPATH], cmd_output[MAX_STRING];
337         FILE       *fd, *output;
338
339         /* If there is no postgresql.conf, it can't be a config-only dir */
340         snprintf(filename, sizeof(filename), "%s/postgresql.conf", cluster->pgconfig);
341         if ((fd = fopen(filename, "r")) == NULL)
342                 return;
343         fclose(fd);
344
345         /* If PG_VERSION exists, it can't be a config-only dir */
346         snprintf(filename, sizeof(filename), "%s/PG_VERSION", cluster->pgconfig);
347         if ((fd = fopen(filename, "r")) != NULL)
348         {
349                 fclose(fd);
350                 return;
351         }
352
353         /* Must be a configuration directory, so find the real data directory. */
354
355         prep_status("Finding the real data directory for the %s cluster",
356                                 CLUSTER_NAME(cluster));
357
358         /*
359          * We don't have a data directory yet, so we can't check the PG
360          * version, so this might fail --- only works for PG 9.2+.   If this
361          * fails, pg_upgrade will fail anyway because the data files will not
362          * be found.
363          */
364         snprintf(cmd, sizeof(cmd), "\"%s/postmaster\" -D \"%s\" -C data_directory",
365                          cluster->bindir, cluster->pgconfig);
366
367         if ((output = popen(cmd, "r")) == NULL ||
368                 fgets(cmd_output, sizeof(cmd_output), output) == NULL)
369                 pg_log(PG_FATAL, "Could not get data directory using %s: %s\n",
370                 cmd, getErrorText(errno));
371
372         pclose(output);
373
374         /* Remove trailing newline */
375         if (strchr(cmd_output, '\n') != NULL)
376                 *strchr(cmd_output, '\n') = '\0';
377
378         cluster->pgdata = pg_strdup(cmd_output);
379
380         check_ok();
381 }