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 80
			usage();
			exit_nicely(false);
81 82 83
		}
		if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
		{
84 85
			pg_log(PG_REPORT, "pg_upgrade " PG_VERSION "\n");
			exit_nicely(false);
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 128
					pg_log(PG_FATAL, "cannot open debug file\n");
					exit_nicely(false);
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 144
					pg_log(PG_FATAL, "invalid old port number\n");
					exit_nicely(false);
145 146 147 148
				}
				break;

			case 'P':
149
				if ((new_cluster.port = atoi(optarg)) <= 0)
150
				{
151 152
					pg_log(PG_FATAL, "invalid new port number\n");
					exit_nicely(false);
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 183 184 185 186
		if ((log_opts.fd = fopen(log_opts.filename, "w")) == NULL)
			pg_log(PG_FATAL, "Cannot write to log file %s\n", log_opts.filename);
		fclose(log_opts.fd);
		if ((log_opts.fd = fopen(log_opts.filename, "a")) == NULL)
			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(_("\nUsage: pg_upgrade [OPTIONS]...\n\
\n\
Options:\n\
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
 -b, --old-bindir=old_bindir      old cluster executable directory\n\
 -B, --new-bindir=new_bindir      new cluster executable directory\n\
 -c, --check                      check clusters only, don't change any data\n\
 -d, --old-datadir=old_datadir    old cluster data directory\n\
 -D, --new-datadir=new_datadir    new cluster data directory\n\
 -g, --debug                      enable debugging\n\
 -G, --debugfile=debug_filename   output debugging activity to file\n\
 -k, --link                       link instead of copying files to new cluster\n\
 -l, --logfile=log_filename       log session activity to file\n\
 -p, --old-port=old_portnum       old cluster port number (default %d)\n\
 -P, --new-port=new_portnum       new cluster port number (default %d)\n\
 -u, --user=username              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\
234 235 236 237 238 239 240 241 242 243 244 245 246 247
\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\
  the 'bin' directory for the old version (-b OLDBINDIR)\n\
  the 'bin' directory for the new version (-B NEWBINDIR)\n\
\n\
For example:\n\
  pg_upgrade -d oldCluster/data -D newCluster/data -b oldCluster/bin -B newCluster/bin\n\
248
or\n"), old_cluster.port, new_cluster.port, os_info.user);
249 250 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
#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
280
validateDirectoryOption(char **dirpath,
281 282 283 284 285 286 287
					char *envVarName, char *cmdLineOption, char *description)
{
	if (*dirpath == NULL || (strlen(*dirpath) == 0))
	{
		const char *envVar;

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

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


static void
311
get_pkglibdirs(void)
312
{
313 314 315 316 317
	/*
	 * 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;
318
	new_cluster.libpath = get_pkglibdir(new_cluster.bindir);
319 320 321 322
}


static char *
323
get_pkglibdir(const char *bindir)
324 325 326 327 328 329 330 331 332
{
	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)
333
		pg_log(PG_FATAL, "Could not get pkglibdir data: %s\n",
334 335 336 337 338 339 340 341 342 343 344 345 346
			   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';

347
	return pg_strdup(bufin);
348
}