]> granicus.if.org Git - postgresql/blob - contrib/pg_upgrade/option.c
Add postgres.h to *.c files for pg_upgrade, ltree, and btree_gist, and
[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                                 break;
116
117                         case 'D':
118                                 new_cluster.pgdata = pg_strdup(optarg);
119                                 break;
120
121                         case 'g':
122                                 pg_log(PG_REPORT, "Running in debug mode\n");
123                                 log_opts.debug = true;
124                                 break;
125
126                         case 'G':
127                                 if ((log_opts.debug_fd = fopen(optarg, "w")) == NULL)
128                                 {
129                                         pg_log(PG_FATAL, "cannot open debug file\n");
130                                         exit(1);
131                                 }
132                                 break;
133
134                         case 'k':
135                                 user_opts.transfer_mode = TRANSFER_MODE_LINK;
136                                 break;
137
138                         case 'l':
139                                 log_opts.filename = pg_strdup(optarg);
140                                 break;
141
142                         case 'p':
143                                 if ((old_cluster.port = atoi(optarg)) <= 0)
144                                 {
145                                         pg_log(PG_FATAL, "invalid old port number\n");
146                                         exit(1);
147                                 }
148                                 break;
149
150                         case 'P':
151                                 if ((new_cluster.port = atoi(optarg)) <= 0)
152                                 {
153                                         pg_log(PG_FATAL, "invalid new port number\n");
154                                         exit(1);
155                                 }
156                                 break;
157
158                         case 'u':
159                                 pg_free(os_info.user);
160                                 os_info.user = pg_strdup(optarg);
161
162                                 /*
163                                  * Push the user name into the environment so pre-9.1
164                                  * pg_ctl/libpq uses it.
165                                  */
166                                 pg_putenv("PGUSER", os_info.user);
167                                 break;
168
169                         case 'v':
170                                 pg_log(PG_REPORT, "Running in verbose mode\n");
171                                 log_opts.verbose = true;
172                                 break;
173
174                         default:
175                                 pg_log(PG_FATAL,
176                                            "Try \"%s --help\" for more information.\n",
177                                            os_info.progname);
178                                 break;
179                 }
180         }
181
182         if (log_opts.filename != NULL)
183         {
184                 /*
185                  * We must use append mode so output generated by child processes via
186                  * ">>" will not be overwritten, and we want the file truncated on
187                  * start.
188                  */
189                 /* truncate */
190                 if ((log_opts.fd = fopen(log_opts.filename, "w")) == NULL)
191                         pg_log(PG_FATAL, "cannot write to log file %s\n", log_opts.filename);
192                 fclose(log_opts.fd);
193                 if ((log_opts.fd = fopen(log_opts.filename, "a")) == NULL)
194                         pg_log(PG_FATAL, "cannot write to log file %s\n", log_opts.filename);
195         }
196         else
197                 log_opts.filename = pg_strdup(DEVNULL);
198
199         /* WIN32 files do not accept writes from multiple processes */
200 #ifndef WIN32
201         log_opts.filename2 = pg_strdup(log_opts.filename);
202 #else
203         log_opts.filename2 = pg_strdup(DEVNULL);
204 #endif
205                 
206         /* if no debug file name, output to the terminal */
207         if (log_opts.debug && !log_opts.debug_fd)
208         {
209                 log_opts.debug_fd = fopen(DEVTTY, "w");
210                 if (!log_opts.debug_fd)
211                         pg_log(PG_FATAL, "cannot write to terminal\n");
212         }
213
214         /* Get values from env if not already set */
215         check_required_directory(&old_cluster.bindir, "PGBINOLD", "-b",
216                                                         "old cluster binaries reside");
217         check_required_directory(&new_cluster.bindir, "PGBINNEW", "-B",
218                                                         "new cluster binaries reside");
219         check_required_directory(&old_cluster.pgdata, "PGDATAOLD", "-d",
220                                                         "old cluster data resides");
221         check_required_directory(&new_cluster.pgdata, "PGDATANEW", "-D",
222                                                         "new cluster data resides");
223 }
224
225
226 static void
227 usage(void)
228 {
229         printf(_("pg_upgrade upgrades a PostgreSQL cluster to a different major version.\n\
230 \nUsage:\n\
231   pg_upgrade [OPTIONS]...\n\
232 \n\
233 Options:\n\
234   -b, --old-bindir=OLDBINDIR    old cluster executable directory\n\
235   -B, --new-bindir=NEWBINDIR    new cluster executable directory\n\
236   -c, --check                   check clusters only, don't change any data\n\
237   -d, --old-datadir=OLDDATADIR  old cluster data directory\n\
238   -D, --new-datadir=NEWDATADIR  new cluster data directory\n\
239   -g, --debug                   enable debugging\n\
240   -G, --debugfile=FILENAME      output debugging activity to file\n\
241   -k, --link                    link instead of copying files to new cluster\n\
242   -l, --logfile=FILENAME        log session activity to file\n\
243   -p, --old-port=OLDPORT        old cluster port number (default %d)\n\
244   -P, --new-port=NEWPORT        new cluster port number (default %d)\n\
245   -u, --user=NAME               clusters superuser (default \"%s\")\n\
246   -v, --verbose                 enable verbose output\n\
247   -V, --version                 display version information, then exit\n\
248   -h, --help                    show this help, then exit\n\
249 \n\
250 Before running pg_upgrade you must:\n\
251   create a new database cluster (using the new version of initdb)\n\
252   shutdown the postmaster servicing the old cluster\n\
253   shutdown the postmaster servicing the new cluster\n\
254 \n\
255 When you run pg_upgrade, you must provide the following information:\n\
256   the data directory for the old cluster  (-d OLDDATADIR)\n\
257   the data directory for the new cluster  (-D NEWDATADIR)\n\
258   the \"bin\" directory for the old version (-b OLDBINDIR)\n\
259   the \"bin\" directory for the new version (-B NEWBINDIR)\n\
260 \n\
261 For example:\n\
262   pg_upgrade -d oldCluster/data -D newCluster/data -b oldCluster/bin -B newCluster/bin\n\
263 or\n"), old_cluster.port, new_cluster.port, os_info.user);
264 #ifndef WIN32
265         printf(_("\
266   $ export PGDATAOLD=oldCluster/data\n\
267   $ export PGDATANEW=newCluster/data\n\
268   $ export PGBINOLD=oldCluster/bin\n\
269   $ export PGBINNEW=newCluster/bin\n\
270   $ pg_upgrade\n"));
271 #else
272         printf(_("\
273   C:\\> set PGDATAOLD=oldCluster/data\n\
274   C:\\> set PGDATANEW=newCluster/data\n\
275   C:\\> set PGBINOLD=oldCluster/bin\n\
276   C:\\> set PGBINNEW=newCluster/bin\n\
277   C:\\> pg_upgrade\n"));
278 #endif
279         printf(_("\nReport bugs to <pgsql-bugs@postgresql.org>.\n"));
280 }
281
282
283 /*
284  * check_required_directory()
285  *
286  * Checks a directory option.
287  *      dirpath           - the directory name supplied on the command line
288  *      envVarName        - the name of an environment variable to get if dirpath is NULL
289  *      cmdLineOption - the command line option corresponds to this directory (-o, -O, -n, -N)
290  *      description   - a description of this directory option
291  *
292  * We use the last two arguments to construct a meaningful error message if the
293  * user hasn't provided the required directory name.
294  */
295 static void
296 check_required_directory(char **dirpath, char *envVarName,
297                                                 char *cmdLineOption, char *description)
298 {
299         if (*dirpath == NULL || strlen(*dirpath) == 0)
300         {
301                 const char *envVar;
302
303                 if ((envVar = getenv(envVarName)) && strlen(envVar))
304                         *dirpath = pg_strdup(envVar);
305                 else
306                         pg_log(PG_FATAL, "You must identify the directory where the %s.\n"
307                                    "Please use the %s command-line option or the %s environment variable.\n",
308                                    description, cmdLineOption, envVarName);
309         }
310
311         /*
312          * Trim off any trailing path separators
313          */
314 #ifndef WIN32
315         if ((*dirpath)[strlen(*dirpath) - 1] == '/')
316 #else
317         if ((*dirpath)[strlen(*dirpath) - 1] == '/' ||
318                 (*dirpath)[strlen(*dirpath) - 1] == '\\')
319 #endif
320                 (*dirpath)[strlen(*dirpath) - 1] = 0;
321 }