option.c 12.5 KB
Newer Older
1 2 3 4
/*
 *	opt.c
 *
 *	options functions
5
 *
6
 *	Copyright (c) 2010-2012, PostgreSQL Global Development Group
7
 *	contrib/pg_upgrade/option.c
8 9
 */

10 11
#include "postgres.h"

12 13
#include "miscadmin.h"

14 15
#include "pg_upgrade.h"

16 17 18 19
#include <getopt_long.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
20 21 22 23 24
#ifdef WIN32
#include <io.h>
#endif


25
static void usage(void);
26
static void check_required_directory(char **dirpath, char **configpath,
27
				   char *envVarName, char *cmdLineOption, char *description);
28 29


30
UserOpts	user_opts;
31 32 33 34 35


/*
 * parseCommandLine()
 *
36
 *	Parses the command line (argc, argv[]) and loads structures
37 38
 */
void
39
parseCommandLine(int argc, char *argv[])
40 41 42 43 44 45
{
	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'},
46 47
		{"old-options", required_argument, NULL, 'o'},
		{"new-options", required_argument, NULL, 'O'},
48 49 50 51 52 53
		{"old-port", required_argument, NULL, 'p'},
		{"new-port", required_argument, NULL, 'P'},

		{"user", required_argument, NULL, 'u'},
		{"check", no_argument, NULL, 'c'},
		{"link", no_argument, NULL, 'k'},
54
		{"retain", no_argument, NULL, 'r'},
55 56 57
		{"verbose", no_argument, NULL, 'v'},
		{NULL, 0, NULL, 0}
	};
58
	int			option;			/* Command line option */
59
	int			optindex = 0;	/* used by getopt_long */
60
	int			os_user_effective_id;
61 62
	FILE	   *fp;
	char	  **filename;
63
	time_t		run_time = time(NULL);
64

65
	user_opts.transfer_mode = TRANSFER_MODE_COPY;
66

67
	os_info.progname = get_progname(argv[0]);
68 69

	/* Process libpq env. variables; load values here for usage() output */
70 71
	old_cluster.port = getenv("PGPORTOLD") ? atoi(getenv("PGPORTOLD")) : DEF_PGUPORT;
	new_cluster.port = getenv("PGPORTNEW") ? atoi(getenv("PGPORTNEW")) : DEF_PGUPORT;
72

73 74 75 76 77 78 79 80
	os_user_effective_id = get_user_info(&os_info.user);
	/* we override just the database user name;  we got the OS id above */
	if (getenv("PGUSER"))
	{
		pg_free(os_info.user);
		/* must save value, getenv()'s pointer is not stable */
		os_info.user = pg_strdup(getenv("PGUSER"));
	}
B
Bruce Momjian 已提交
81

82 83 84 85 86
	if (argc > 1)
	{
		if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0 ||
			strcmp(argv[1], "-?") == 0)
		{
87
			usage();
88
			exit(0);
89 90 91
		}
		if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
		{
92
			puts("pg_upgrade (PostgreSQL) " PG_VERSION);
93
			exit(0);
94 95 96
		}
	}

97 98
	/* Allow help and version to be run as root, so do the test here. */
	if (os_user_effective_id == 0)
99
		pg_log(PG_FATAL, "%s: cannot be run as root\n", os_info.progname);
100

101 102
	if ((log_opts.internal = fopen_priv(INTERNAL_LOG_FILE, "a")) == NULL)
		pg_log(PG_FATAL, "cannot write to log file %s\n", INTERNAL_LOG_FILE);
103

104
	while ((option = getopt_long(argc, argv, "d:D:b:B:cko:O:p:P:ru:v",
105 106 107 108 109
								 long_options, &optindex)) != -1)
	{
		switch (option)
		{
			case 'b':
110
				old_cluster.bindir = pg_strdup(optarg);
111 112 113
				break;

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

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

121 122
			case 'd':
				old_cluster.pgdata = pg_strdup(optarg);
123
				old_cluster.pgconfig = pg_strdup(optarg);
124 125 126 127
				break;

			case 'D':
				new_cluster.pgdata = pg_strdup(optarg);
128
				new_cluster.pgconfig = pg_strdup(optarg);
129 130
				break;

131
			case 'k':
132
				user_opts.transfer_mode = TRANSFER_MODE_LINK;
133 134
				break;

135 136 137 138 139 140 141 142
			case 'o':
				old_cluster.pgopts = pg_strdup(optarg);
				break;

			case 'O':
				new_cluster.pgopts = pg_strdup(optarg);
				break;

143 144 145 146 147
				/*
				 * Someday, the port number option could be removed and passed
				 * using -o/-O, but that requires postmaster -C to be
				 * supported on all old/new versions.
				 */
148
			case 'p':
149
				if ((old_cluster.port = atoi(optarg)) <= 0)
150
				{
151
					pg_log(PG_FATAL, "invalid old port number\n");
152
					exit(1);
153 154 155 156
				}
				break;

			case 'P':
157
				if ((new_cluster.port = atoi(optarg)) <= 0)
158
				{
159
					pg_log(PG_FATAL, "invalid new port number\n");
160
					exit(1);
161 162 163
				}
				break;

164 165 166 167
			case 'r':
				log_opts.retain = true;
				break;

168
			case 'u':
169 170
				pg_free(os_info.user);
				os_info.user = pg_strdup(optarg);
B
Bruce Momjian 已提交
171

172 173 174 175 176
				/*
				 * Push the user name into the environment so pre-9.1
				 * pg_ctl/libpq uses it.
				 */
				pg_putenv("PGUSER", os_info.user);
177 178 179
				break;

			case 'v':
180
				pg_log(PG_REPORT, "Running in verbose mode\n");
181
				log_opts.verbose = true;
182 183 184
				break;

			default:
185
				pg_log(PG_FATAL,
186
					   "Try \"%s --help\" for more information.\n",
187
					   os_info.progname);
188 189 190 191
				break;
		}
	}

192
	/* label start of upgrade in logfiles */
193
	for (filename = output_files; *filename != NULL; filename++)
194
	{
195 196 197 198
		if ((fp = fopen_priv(*filename, "a")) == NULL)
			pg_log(PG_FATAL, "cannot write to log file %s\n", *filename);

		/* Start with newline because we might be appending to a file. */
199 200
		fprintf(fp, "\n"
		"-----------------------------------------------------------------\n"
201 202 203
				"  pg_upgrade run on %s"
				"-----------------------------------------------------------------\n\n",
				ctime(&run_time));
204
		fclose(fp);
205 206 207
	}

	/* Get values from env if not already set */
208
	check_required_directory(&old_cluster.bindir, NULL, "PGBINOLD", "-b",
209
							 "old cluster binaries reside");
210
	check_required_directory(&new_cluster.bindir, NULL, "PGBINNEW", "-B",
211
							 "new cluster binaries reside");
212 213 214 215
	check_required_directory(&old_cluster.pgdata, &old_cluster.pgconfig,
							 "PGDATAOLD", "-d", "old cluster data resides");
	check_required_directory(&new_cluster.pgdata, &new_cluster.pgconfig,
							 "PGDATANEW", "-D", "new cluster data resides");
216 217 218 219
}


static void
220
usage(void)
221
{
222 223
	printf(_("pg_upgrade upgrades a PostgreSQL cluster to a different major version.\n\
\nUsage:\n\
224
  pg_upgrade [OPTION]...\n\
225 226
\n\
Options:\n\
227 228 229 230 231 232
  -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\
  -k, --link                    link instead of copying files to new cluster\n\
233 234
  -o, --old-options=OPTIONS     old cluster options to pass to the server\n\
  -O, --new-options=OPTIONS     new cluster options to pass to the server\n\
235 236
  -p, --old-port=OLDPORT        old cluster port number (default %d)\n\
  -P, --new-port=NEWPORT        new cluster port number (default %d)\n\
237
  -r, --retain                  retain SQL and log files after success\n\
238
  -u, --user=NAME               cluster superuser (default \"%s\")\n\
239
  -v, --verbose                 enable verbose internal logging\n\
240
  -V, --version                 display version information, then exit\n\
241
  -?, -h, --help                show this help, then exit\n\
242 243 244 245 246 247 248 249 250
\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\
251 252
  the \"bin\" directory for the old version (-b OLDBINDIR)\n\
  the \"bin\" directory for the new version (-B NEWBINDIR)\n\
253 254 255
\n\
For example:\n\
  pg_upgrade -d oldCluster/data -D newCluster/data -b oldCluster/bin -B newCluster/bin\n\
256
or\n"), old_cluster.port, new_cluster.port, os_info.user);
257 258
#ifndef WIN32
	printf(_("\
259 260 261 262
  $ export PGDATAOLD=oldCluster/data\n\
  $ export PGDATANEW=newCluster/data\n\
  $ export PGBINOLD=oldCluster/bin\n\
  $ export PGBINNEW=newCluster/bin\n\
263 264 265
  $ pg_upgrade\n"));
#else
	printf(_("\
266 267 268 269
  C:\\> set PGDATAOLD=oldCluster/data\n\
  C:\\> set PGDATANEW=newCluster/data\n\
  C:\\> set PGBINOLD=oldCluster/bin\n\
  C:\\> set PGBINNEW=newCluster/bin\n\
270 271
  C:\\> pg_upgrade\n"));
#endif
272
	printf(_("\nReport bugs to <pgsql-bugs@postgresql.org>.\n"));
273 274 275 276
}


/*
277
 * check_required_directory()
278
 *
279
 * Checks a directory option.
280
 *	dirpath		  - the directory name supplied on the command line
281
 *	configpath	  - optional configuration directory
282 283 284 285 286 287 288 289
 *	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
290 291 292
check_required_directory(char **dirpath, char **configpath,
						 char *envVarName, char *cmdLineOption,
						 char *description)
293
{
294
	if (*dirpath == NULL || strlen(*dirpath) == 0)
295 296 297 298
	{
		const char *envVar;

		if ((envVar = getenv(envVarName)) && strlen(envVar))
299
		{
300
			*dirpath = pg_strdup(envVar);
301 302 303
			if (configpath)
				*configpath = pg_strdup(envVar);
		}
304
		else
305 306
			pg_log(PG_FATAL, "You must identify the directory where the %s.\n"
				   "Please use the %s command-line option or the %s environment variable.\n",
307 308 309 310
				   description, cmdLineOption, envVarName);
	}

	/*
311 312
	 * Trim off any trailing path separators because we construct paths
	 * by appending to this path.
313
	 */
314 315 316 317
#ifndef WIN32
	if ((*dirpath)[strlen(*dirpath) - 1] == '/')
#else
	if ((*dirpath)[strlen(*dirpath) - 1] == '/' ||
B
Bruce Momjian 已提交
318
		(*dirpath)[strlen(*dirpath) - 1] == '\\')
319
#endif
320 321
		(*dirpath)[strlen(*dirpath) - 1] = 0;
}
322 323 324 325 326 327 328 329 330 331 332 333

/*
 * adjust_data_dir
 *
 * If a configuration-only directory was specified, find the real data dir
 * by quering the running server.  This has limited checking because we
 * can't check for a running server because we can't find postmaster.pid.
 */
void
adjust_data_dir(ClusterInfo *cluster)
{
	char		filename[MAXPGPATH];
334 335 336 337
	char		cmd[MAXPGPATH],
				cmd_output[MAX_STRING];
	FILE	   *fp,
			   *output;
338 339 340

	/* If there is no postgresql.conf, it can't be a config-only dir */
	snprintf(filename, sizeof(filename), "%s/postgresql.conf", cluster->pgconfig);
341
	if ((fp = fopen(filename, "r")) == NULL)
342
		return;
343
	fclose(fp);
344 345 346

	/* If PG_VERSION exists, it can't be a config-only dir */
	snprintf(filename, sizeof(filename), "%s/PG_VERSION", cluster->pgconfig);
347
	if ((fp = fopen(filename, "r")) != NULL)
348
	{
349
		fclose(fp);
350 351 352 353 354 355 356 357 358
		return;
	}

	/* Must be a configuration directory, so find the real data directory. */

	prep_status("Finding the real data directory for the %s cluster",
				CLUSTER_NAME(cluster));

	/*
359 360 361
	 * We don't have a data directory yet, so we can't check the PG version,
	 * so this might fail --- only works for PG 9.2+.	If this fails,
	 * pg_upgrade will fail anyway because the data files will not be found.
362 363 364 365 366 367 368
	 */
	snprintf(cmd, sizeof(cmd), "\"%s/postmaster\" -D \"%s\" -C data_directory",
			 cluster->bindir, cluster->pgconfig);

	if ((output = popen(cmd, "r")) == NULL ||
		fgets(cmd_output, sizeof(cmd_output), output) == NULL)
		pg_log(PG_FATAL, "Could not get data directory using %s: %s\n",
369
			   cmd, getErrorText(errno));
370 371 372 373 374 375 376 377 378 379 380

	pclose(output);

	/* Remove trailing newline */
	if (strchr(cmd_output, '\n') != NULL)
		*strchr(cmd_output, '\n') = '\0';

	cluster->pgdata = pg_strdup(cmd_output);

	check_ok();
}
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441


/*
 * get_sock_dir
 *
 * Identify the socket directory to use for this cluster.  If we're doing
 * a live check (old cluster only), we need to find out where the postmaster
 * is listening.  Otherwise, we're going to put the socket into the current
 * directory.
 */
void
get_sock_dir(ClusterInfo *cluster, bool live_check)
{
#ifdef HAVE_UNIX_SOCKETS
	if (!live_check)
	{
		/* Use the current directory for the socket */
		cluster->sockdir = pg_malloc(MAXPGPATH);
		if (!getcwd(cluster->sockdir, MAXPGPATH))
			pg_log(PG_FATAL, "cannot find current directory\n");
	}
	else
	{
		/*
		 *	If we are doing a live check, we will use the old cluster's Unix
		 *	domain socket directory so we can connect to the live server.
		 */

		/* sockdir was added to postmaster.pid in PG 9.1 */
		if (GET_MAJOR_VERSION(cluster->major_version) >= 901)
		{
			char		filename[MAXPGPATH];
			FILE		*fp;
			int			i;

			snprintf(filename, sizeof(filename), "%s/postmaster.pid",
					 cluster->pgdata);
			if ((fp = fopen(filename, "r")) == NULL)
				pg_log(PG_FATAL, "Could not get socket directory of the old server\n");

			cluster->sockdir = pg_malloc(MAXPGPATH);
			for (i = 0; i < LOCK_FILE_LINE_SOCKET_DIR; i++)
				if (fgets(cluster->sockdir, MAXPGPATH, fp) == NULL)
					pg_log(PG_FATAL, "Could not get socket directory of the old server\n");

			fclose(fp);

			/* Remove trailing newline */
			if (strchr(cluster->sockdir, '\n') != NULL)
				*strchr(cluster->sockdir, '\n') = '\0';
		}
		else
		{
			/* Can't get live sockdir, so assume the default is OK. */
			cluster->sockdir = NULL;
		}
	}
#else /* !HAVE_UNIX_SOCKETS */
	cluster->sockdir = NULL;
#endif
}