mirror of
				https://github.com/postgres/postgres.git
				synced 2025-10-31 10:30:33 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			349 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			349 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  *	opt.c
 | |
|  *
 | |
|  *	options functions
 | |
|  *
 | |
|  *	Copyright (c) 2010-2011, PostgreSQL Global Development Group
 | |
|  *	contrib/pg_upgrade/option.c
 | |
|  */
 | |
| 
 | |
| #include "pg_upgrade.h"
 | |
| 
 | |
| #include "getopt_long.h"
 | |
| 
 | |
| #ifdef WIN32
 | |
| #include <io.h>
 | |
| #endif
 | |
| 
 | |
| 
 | |
| static void usage(void);
 | |
| static void validateDirectoryOption(char **dirpath,
 | |
| 				   char *envVarName, char *cmdLineOption, char *description);
 | |
| static void get_pkglibdirs(void);
 | |
| static char *get_pkglibdir(const char *bindir);
 | |
| 
 | |
| 
 | |
| UserOpts	user_opts;
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * parseCommandLine()
 | |
|  *
 | |
|  *	Parses the command line (argc, argv[]) and loads structures
 | |
|  */
 | |
| void
 | |
| parseCommandLine(int argc, char *argv[])
 | |
| {
 | |
| 	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}
 | |
| 	};
 | |
| 	int			option;			/* Command line option */
 | |
| 	int			optindex = 0;	/* used by getopt_long */
 | |
| 	int			user_id;
 | |
| 
 | |
| 	if (getenv("PGUSER"))
 | |
| 	{
 | |
| 		pg_free(os_info.user);
 | |
| 		os_info.user = pg_strdup(getenv("PGUSER"));
 | |
| 	}
 | |
| 
 | |
| 	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;
 | |
| 	/* must save value, getenv()'s pointer is not stable */
 | |
| 
 | |
| 	user_opts.transfer_mode = TRANSFER_MODE_COPY;
 | |
| 
 | |
| 	/* user lookup and 'root' test must be split because of usage() */
 | |
| 	user_id = get_user_info(&os_info.user);
 | |
| 
 | |
| 	if (argc > 1)
 | |
| 	{
 | |
| 		if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0 ||
 | |
| 			strcmp(argv[1], "-?") == 0)
 | |
| 		{
 | |
| 			usage();
 | |
| 			exit_nicely(false);
 | |
| 		}
 | |
| 		if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
 | |
| 		{
 | |
| 			pg_log(PG_REPORT, "pg_upgrade " PG_VERSION "\n");
 | |
| 			exit_nicely(false);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (user_id == 0)
 | |
| 		pg_log(PG_FATAL, "%s: cannot be run as root\n", os_info.progname);
 | |
| 
 | |
| 	getcwd(os_info.cwd, MAXPGPATH);
 | |
| 
 | |
| 	while ((option = getopt_long(argc, argv, "d:D:b:B:cgG:kl:p:P:u:v",
 | |
| 								 long_options, &optindex)) != -1)
 | |
| 	{
 | |
| 		switch (option)
 | |
| 		{
 | |
| 			case 'd':
 | |
| 				old_cluster.pgdata = pg_strdup(optarg);
 | |
| 				break;
 | |
| 
 | |
| 			case 'D':
 | |
| 				new_cluster.pgdata = pg_strdup(optarg);
 | |
| 				break;
 | |
| 
 | |
| 			case 'b':
 | |
| 				old_cluster.bindir = pg_strdup(optarg);
 | |
| 				break;
 | |
| 
 | |
| 			case 'B':
 | |
| 				new_cluster.bindir = pg_strdup(optarg);
 | |
| 				break;
 | |
| 
 | |
| 			case 'c':
 | |
| 				user_opts.check = true;
 | |
| 				break;
 | |
| 
 | |
| 			case 'g':
 | |
| 				pg_log(PG_REPORT, "Running in debug mode\n");
 | |
| 				log_opts.debug = true;
 | |
| 				break;
 | |
| 
 | |
| 			case 'G':
 | |
| 				if ((log_opts.debug_fd = fopen(optarg, "w")) == NULL)
 | |
| 				{
 | |
| 					pg_log(PG_FATAL, "cannot open debug file\n");
 | |
| 					exit_nicely(false);
 | |
| 				}
 | |
| 				break;
 | |
| 
 | |
| 			case 'k':
 | |
| 				user_opts.transfer_mode = TRANSFER_MODE_LINK;
 | |
| 				break;
 | |
| 
 | |
| 			case 'l':
 | |
| 				log_opts.filename = pg_strdup(optarg);
 | |
| 				break;
 | |
| 
 | |
| 			case 'p':
 | |
| 				if ((old_cluster.port = atoi(optarg)) <= 0)
 | |
| 				{
 | |
| 					pg_log(PG_FATAL, "invalid old port number\n");
 | |
| 					exit_nicely(false);
 | |
| 				}
 | |
| 				break;
 | |
| 
 | |
| 			case 'P':
 | |
| 				if ((new_cluster.port = atoi(optarg)) <= 0)
 | |
| 				{
 | |
| 					pg_log(PG_FATAL, "invalid new port number\n");
 | |
| 					exit_nicely(false);
 | |
| 				}
 | |
| 				break;
 | |
| 
 | |
| 			case 'u':
 | |
| 				pg_free(os_info.user);
 | |
| 				os_info.user = pg_strdup(optarg);
 | |
| 				break;
 | |
| 
 | |
| 			case 'v':
 | |
| 				pg_log(PG_REPORT, "Running in verbose mode\n");
 | |
| 				log_opts.verbose = true;
 | |
| 				break;
 | |
| 
 | |
| 			default:
 | |
| 				pg_log(PG_FATAL,
 | |
| 					   "Try \"%s --help\" for more information.\n",
 | |
| 					   os_info.progname);
 | |
| 				break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (log_opts.filename != NULL)
 | |
| 	{
 | |
| 		/*
 | |
| 		 * 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 */
 | |
| 		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);
 | |
| 	}
 | |
| 	else
 | |
| 		log_opts.filename = strdup(DEVNULL);
 | |
| 
 | |
| 	/* if no debug file name, output to the terminal */
 | |
| 	if (log_opts.debug && !log_opts.debug_fd)
 | |
| 	{
 | |
| 		log_opts.debug_fd = fopen(DEVTTY, "w");
 | |
| 		if (!log_opts.debug_fd)
 | |
| 			pg_log(PG_FATAL, "Cannot write to terminal\n");
 | |
| 	}
 | |
| 
 | |
| 	/* Get values from env if not already set */
 | |
| 	validateDirectoryOption(&old_cluster.pgdata, "OLDDATADIR", "-d",
 | |
| 							"old cluster data resides");
 | |
| 	validateDirectoryOption(&new_cluster.pgdata, "NEWDATADIR", "-D",
 | |
| 							"new cluster data resides");
 | |
| 	validateDirectoryOption(&old_cluster.bindir, "OLDBINDIR", "-b",
 | |
| 							"old cluster binaries reside");
 | |
| 	validateDirectoryOption(&new_cluster.bindir, "NEWBINDIR", "-B",
 | |
| 							"new cluster binaries reside");
 | |
| 
 | |
| 	get_pkglibdirs();
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| usage(void)
 | |
| {
 | |
| 	printf(_("\nUsage: pg_upgrade [OPTIONS]...\n\
 | |
| \n\
 | |
| Options:\n\
 | |
|  -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\
 | |
| \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\
 | |
| or\n"), old_cluster.port, new_cluster.port, os_info.user);
 | |
| #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
 | |
| validateDirectoryOption(char **dirpath,
 | |
| 					char *envVarName, char *cmdLineOption, char *description)
 | |
| {
 | |
| 	if (*dirpath == NULL || (strlen(*dirpath) == 0))
 | |
| 	{
 | |
| 		const char *envVar;
 | |
| 
 | |
| 		if ((envVar = getenv(envVarName)) && strlen(envVar))
 | |
| 			*dirpath = pg_strdup(envVar);
 | |
| 		else
 | |
| 		{
 | |
| 			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",
 | |
| 				   description, cmdLineOption, envVarName);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * Trim off any trailing path separators
 | |
| 	 */
 | |
| #ifndef WIN32
 | |
| 	if ((*dirpath)[strlen(*dirpath) - 1] == '/')
 | |
| #else
 | |
| 	if ((*dirpath)[strlen(*dirpath) - 1] == '/' ||
 | |
| 		(*dirpath)[strlen(*dirpath) - 1] == '\\')
 | |
| #endif
 | |
| 		(*dirpath)[strlen(*dirpath) - 1] = 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| get_pkglibdirs(void)
 | |
| {
 | |
| 	/*
 | |
| 	 * 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;
 | |
| 	new_cluster.libpath = get_pkglibdir(new_cluster.bindir);
 | |
| }
 | |
| 
 | |
| 
 | |
| static char *
 | |
| get_pkglibdir(const char *bindir)
 | |
| {
 | |
| 	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)
 | |
| 		pg_log(PG_FATAL, "Could not get pkglibdir data: %s\n",
 | |
| 			   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';
 | |
| 
 | |
| 	return pg_strdup(bufin);
 | |
| }
 |