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