option.c 9.2 KB
Newer Older
1 2 3 4
/*
 *	opt.c
 *
 *	options functions
5
 *
B
Bruce Momjian 已提交
6
 *	Copyright (c) 2010-2011, PostgreSQL Global Development Group
7
 *	contrib/pg_upgrade/option.c
8 9 10 11 12 13 14 15 16 17 18
 */

#include "pg_upgrade.h"

#include "getopt_long.h"

#ifdef WIN32
#include <io.h>
#endif


19 20
static void usage(void);
static void validateDirectoryOption(char **dirpath,
21
				   char *envVarName, char *cmdLineOption, char *description);
22 23 24 25
static void get_pkglibdirs(void);
static char *get_pkglibdir(const char *bindir);


26
UserOpts	user_opts;
27 28 29 30 31


/*
 * parseCommandLine()
 *
32
 *	Parses the command line (argc, argv[]) and loads structures
33 34
 */
void
35
parseCommandLine(int argc, char *argv[])
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
{
	static struct option long_options[] = {
		{"old-datadir", required_argument, NULL, 'd'},
		{"new-datadir", required_argument, NULL, 'D'},
		{"old-bindir", required_argument, NULL, 'b'},
		{"new-bindir", required_argument, NULL, 'B'},
		{"old-port", required_argument, NULL, 'p'},
		{"new-port", required_argument, NULL, 'P'},

		{"user", required_argument, NULL, 'u'},
		{"check", no_argument, NULL, 'c'},
		{"debug", no_argument, NULL, 'g'},
		{"debugfile", required_argument, NULL, 'G'},
		{"link", no_argument, NULL, 'k'},
		{"logfile", required_argument, NULL, 'l'},
		{"verbose", no_argument, NULL, 'v'},
		{NULL, 0, NULL, 0}
	};
54
	int			option;			/* Command line option */
55
	int			optindex = 0;	/* used by getopt_long */
56
	int			user_id;
B
Bruce Momjian 已提交
57

58 59
	if (getenv("PGUSER"))
	{
60 61
		pg_free(os_info.user);
		os_info.user = pg_strdup(getenv("PGUSER"));
62 63
	}

64 65 66
	os_info.progname = get_progname(argv[0]);
	old_cluster.port = getenv("PGPORT") ? atoi(getenv("PGPORT")) : DEF_PGPORT;
	new_cluster.port = getenv("PGPORT") ? atoi(getenv("PGPORT")) : DEF_PGPORT;
67 68
	/* must save value, getenv()'s pointer is not stable */

69
	user_opts.transfer_mode = TRANSFER_MODE_COPY;
70

71
	/* user lookup and 'root' test must be split because of usage() */
72
	user_id = get_user_info(&os_info.user);
B
Bruce Momjian 已提交
73

74 75 76 77 78
	if (argc > 1)
	{
		if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0 ||
			strcmp(argv[1], "-?") == 0)
		{
79
			usage();
80
			exit(0);
81 82 83
		}
		if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
		{
84
			pg_log(PG_REPORT, "pg_upgrade " PG_VERSION "\n");
85
			exit(0);
86 87 88
		}
	}

89
	if (user_id == 0)
90
		pg_log(PG_FATAL, "%s: cannot be run as root\n", os_info.progname);
91

92
	getcwd(os_info.cwd, MAXPGPATH);
93 94 95 96 97 98 99

	while ((option = getopt_long(argc, argv, "d:D:b:B:cgG:kl:p:P:u:v",
								 long_options, &optindex)) != -1)
	{
		switch (option)
		{
			case 'd':
100
				old_cluster.pgdata = pg_strdup(optarg);
101 102 103
				break;

			case 'D':
104
				new_cluster.pgdata = pg_strdup(optarg);
105 106 107
				break;

			case 'b':
108
				old_cluster.bindir = pg_strdup(optarg);
109 110 111
				break;

			case 'B':
112
				new_cluster.bindir = pg_strdup(optarg);
113 114 115
				break;

			case 'c':
116
				user_opts.check = true;
117 118 119
				break;

			case 'g':
120
				pg_log(PG_REPORT, "Running in debug mode\n");
121
				log_opts.debug = true;
122 123 124
				break;

			case 'G':
125
				if ((log_opts.debug_fd = fopen(optarg, "w")) == NULL)
126
				{
127
					pg_log(PG_FATAL, "cannot open debug file\n");
128
					exit(1);
129 130 131 132
				}
				break;

			case 'k':
133
				user_opts.transfer_mode = TRANSFER_MODE_LINK;
134 135 136
				break;

			case 'l':
137
				log_opts.filename = pg_strdup(optarg);
138 139 140
				break;

			case 'p':
141
				if ((old_cluster.port = atoi(optarg)) <= 0)
142
				{
143
					pg_log(PG_FATAL, "invalid old port number\n");
144
					exit(1);
145 146 147 148
				}
				break;

			case 'P':
149
				if ((new_cluster.port = atoi(optarg)) <= 0)
150
				{
151
					pg_log(PG_FATAL, "invalid new port number\n");
152
					exit(1);
153 154 155 156
				}
				break;

			case 'u':
157 158
				pg_free(os_info.user);
				os_info.user = pg_strdup(optarg);
159 160 161
				break;

			case 'v':
162
				pg_log(PG_REPORT, "Running in verbose mode\n");
163
				log_opts.verbose = true;
164 165 166
				break;

			default:
167
				pg_log(PG_FATAL,
168
					   "Try \"%s --help\" for more information.\n",
169
					   os_info.progname);
170 171 172 173
				break;
		}
	}

174
	if (log_opts.filename != NULL)
175 176 177 178 179 180 181
	{
		/*
		 * We must use append mode so output generated by child processes via
		 * ">>" will not be overwritten, and we want the file truncated on
		 * start.
		 */
		/* truncate */
182
		if ((log_opts.fd = fopen(log_opts.filename, "w")) == NULL)
183
			pg_log(PG_FATAL, "cannot write to log file %s\n", log_opts.filename);
184 185
		fclose(log_opts.fd);
		if ((log_opts.fd = fopen(log_opts.filename, "a")) == NULL)
186
			pg_log(PG_FATAL, "cannot write to log file %s\n", log_opts.filename);
187 188
	}
	else
189
		log_opts.filename = strdup(DEVNULL);
190 191

	/* if no debug file name, output to the terminal */
192
	if (log_opts.debug && !log_opts.debug_fd)
193
	{
194 195
		log_opts.debug_fd = fopen(DEVTTY, "w");
		if (!log_opts.debug_fd)
196
			pg_log(PG_FATAL, "cannot write to terminal\n");
197 198 199
	}

	/* Get values from env if not already set */
200
	validateDirectoryOption(&old_cluster.pgdata, "OLDDATADIR", "-d",
201
							"old cluster data resides");
202
	validateDirectoryOption(&new_cluster.pgdata, "NEWDATADIR", "-D",
203
							"new cluster data resides");
204
	validateDirectoryOption(&old_cluster.bindir, "OLDBINDIR", "-b",
205
							"old cluster binaries reside");
206
	validateDirectoryOption(&new_cluster.bindir, "NEWBINDIR", "-B",
207 208
							"new cluster binaries reside");

209
	get_pkglibdirs();
210 211 212 213
}


static void
214
usage(void)
215
{
216 217 218
	printf(_("pg_upgrade upgrades a PostgreSQL cluster to a different major version.\n\
\nUsage:\n\
  pg_upgrade [OPTIONS]...\n\
219 220
\n\
Options:\n\
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
  -b, --old-bindir=OLDBINDIR    old cluster executable directory\n\
  -B, --new-bindir=NEWBINDIR    new cluster executable directory\n\
  -c, --check                   check clusters only, don't change any data\n\
  -d, --old-datadir=OLDDATADIR  old cluster data directory\n\
  -D, --new-datadir=NEWDATADIR  new cluster data directory\n\
  -g, --debug                   enable debugging\n\
  -G, --debugfile=FILENAME      output debugging activity to file\n\
  -k, --link                    link instead of copying files to new cluster\n\
  -l, --logfile=FILENAME        log session activity to file\n\
  -p, --old-port=OLDPORT        old cluster port number (default %d)\n\
  -P, --new-port=NEWPORT        new cluster port number (default %d)\n\
  -u, --user=NAME               clusters superuser (default \"%s\")\n\
  -v, --verbose                 enable verbose output\n\
  -V, --version                 display version information, then exit\n\
  -h, --help                    show this help, then exit\n\
236 237 238 239 240 241 242 243 244
\n\
Before running pg_upgrade you must:\n\
  create a new database cluster (using the new version of initdb)\n\
  shutdown the postmaster servicing the old cluster\n\
  shutdown the postmaster servicing the new cluster\n\
\n\
When you run pg_upgrade, you must provide the following information:\n\
  the data directory for the old cluster  (-d OLDDATADIR)\n\
  the data directory for the new cluster  (-D NEWDATADIR)\n\
245 246
  the \"bin\" directory for the old version (-b OLDBINDIR)\n\
  the \"bin\" directory for the new version (-B NEWBINDIR)\n\
247 248 249
\n\
For example:\n\
  pg_upgrade -d oldCluster/data -D newCluster/data -b oldCluster/bin -B newCluster/bin\n\
250
or\n"), old_cluster.port, new_cluster.port, os_info.user);
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
#ifndef WIN32
	printf(_("\
  $ export OLDDATADIR=oldCluster/data\n\
  $ export NEWDATADIR=newCluster/data\n\
  $ export OLDBINDIR=oldCluster/bin\n\
  $ export NEWBINDIR=newCluster/bin\n\
  $ pg_upgrade\n"));
#else
	printf(_("\
  C:\\> set OLDDATADIR=oldCluster/data\n\
  C:\\> set NEWDATADIR=newCluster/data\n\
  C:\\> set OLDBINDIR=oldCluster/bin\n\
  C:\\> set NEWBINDIR=newCluster/bin\n\
  C:\\> pg_upgrade\n"));
#endif
}


/*
 * validateDirectoryOption()
 *
 * Validates a directory option.
 *	dirpath		  - the directory name supplied on the command line
 *	envVarName	  - the name of an environment variable to get if dirpath is NULL
 *	cmdLineOption - the command line option corresponds to this directory (-o, -O, -n, -N)
 *	description   - a description of this directory option
 *
 * We use the last two arguments to construct a meaningful error message if the
 * user hasn't provided the required directory name.
 */
static void
282
validateDirectoryOption(char **dirpath,
283 284 285 286 287 288 289
					char *envVarName, char *cmdLineOption, char *description)
{
	if (*dirpath == NULL || (strlen(*dirpath) == 0))
	{
		const char *envVar;

		if ((envVar = getenv(envVarName)) && strlen(envVar))
290
			*dirpath = pg_strdup(envVar);
291 292
		else
		{
293
			pg_log(PG_FATAL, "You must identify the directory where the %s\n"
294 295 296 297 298 299 300 301
				   "Please use the %s command-line option or the %s environment variable\n",
				   description, cmdLineOption, envVarName);
		}
	}

	/*
	 * Trim off any trailing path separators
	 */
302 303 304 305
#ifndef WIN32
	if ((*dirpath)[strlen(*dirpath) - 1] == '/')
#else
	if ((*dirpath)[strlen(*dirpath) - 1] == '/' ||
B
Bruce Momjian 已提交
306
		(*dirpath)[strlen(*dirpath) - 1] == '\\')
307
#endif
308 309 310 311 312
		(*dirpath)[strlen(*dirpath) - 1] = 0;
}


static void
313
get_pkglibdirs(void)
314
{
315 316 317 318 319
	/*
	 * we do not need to know the libpath in the old cluster, and might not
	 * have a working pg_config to ask for it anyway.
	 */
	old_cluster.libpath = NULL;
320
	new_cluster.libpath = get_pkglibdir(new_cluster.bindir);
321 322 323 324
}


static char *
325
get_pkglibdir(const char *bindir)
326 327 328 329 330 331 332 333 334
{
	char		cmd[MAXPGPATH];
	char		bufin[MAX_STRING];
	FILE	   *output;
	int			i;

	snprintf(cmd, sizeof(cmd), "\"%s/pg_config\" --pkglibdir", bindir);

	if ((output = popen(cmd, "r")) == NULL)
335
		pg_log(PG_FATAL, "Could not get pkglibdir data: %s\n",
336 337 338 339 340 341 342 343 344 345 346 347 348
			   getErrorText(errno));

	fgets(bufin, sizeof(bufin), output);

	if (output)
		pclose(output);

	/* Remove trailing newline */
	i = strlen(bufin) - 1;

	if (bufin[i] == '\n')
		bufin[i] = '\0';

349
	return pg_strdup(bufin);
350
}