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