Newer
Older
/*
* opt.c
*
* options functions
* Copyright (c) 2010-2012, PostgreSQL Global Development Group
#include "postgres.h"
#include "pg_upgrade.h"
#include "getopt_long.h"
#ifdef WIN32
#include <io.h>
#endif
static void usage(void);
static void check_required_directory(char **dirpath,
char *envVarName, char *cmdLineOption, char *description);
UserOpts user_opts;
/*
* parseCommandLine()
*
* Parses the command line (argc, argv[]) and loads structures
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-options", required_argument, NULL, 'o'},
{"new-options", required_argument, NULL, 'O'},
{"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 os_user_effective_id;
char *return_buf;
user_opts.transfer_mode = TRANSFER_MODE_COPY;
os_info.progname = get_progname(argv[0]);
/* Process libpq env. variables; load values here for usage() output */
old_cluster.port = getenv("PGPORTOLD") ? atoi(getenv("PGPORTOLD")) : DEF_PGUPORT;
new_cluster.port = getenv("PGPORTNEW") ? atoi(getenv("PGPORTNEW")) : DEF_PGUPORT;
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"));
}
if (argc > 1)
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0 ||
strcmp(argv[1], "-?") == 0)
{
usage();
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
{
puts("pg_upgrade (PostgreSQL) " PG_VERSION);
exit(0);
/* Allow help and version to be run as root, so do the test here. */
if (os_user_effective_id == 0)
pg_log(PG_FATAL, "%s: cannot be run as root\n", os_info.progname);
return_buf = getcwd(os_info.cwd, MAXPGPATH);
if (return_buf == NULL)
pg_log(PG_FATAL, "Could not access current working directory: %s\n", getErrorText(errno));
while ((option = getopt_long(argc, argv, "d:D:b:B:cgG:kl:o:O:p:P:u:v",
long_options, &optindex)) != -1)
{
switch (option)
{
case 'b':
old_cluster.bindir = pg_strdup(optarg);
break;
case 'B':
new_cluster.bindir = pg_strdup(optarg);
break;
case 'c':
user_opts.check = true;
case 'd':
old_cluster.pgdata = pg_strdup(optarg);
old_cluster.pgconfig = pg_strdup(optarg);
break;
case 'D':
new_cluster.pgdata = pg_strdup(optarg);
new_cluster.pgconfig = pg_strdup(optarg);
break;
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(1);
}
break;
case 'k':
user_opts.transfer_mode = TRANSFER_MODE_LINK;
break;
case 'l':
log_opts.filename = pg_strdup(optarg);
case 'o':
old_cluster.pgopts = pg_strdup(optarg);
break;
case 'O':
new_cluster.pgopts = pg_strdup(optarg);
break;
/*
* 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.
*/
if ((old_cluster.port = atoi(optarg)) <= 0)
pg_log(PG_FATAL, "invalid old port number\n");
exit(1);
}
break;
case 'P':
if ((new_cluster.port = atoi(optarg)) <= 0)
pg_log(PG_FATAL, "invalid new port number\n");
exit(1);
}
break;
case 'u':
pg_free(os_info.user);
os_info.user = pg_strdup(optarg);
/*
* Push the user name into the environment so pre-9.1
* pg_ctl/libpq uses it.
*/
pg_putenv("PGUSER", os_info.user);
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);
log_opts.filename = pg_strdup(DEVNULL);
/* WIN32 files do not accept writes from multiple processes */
#ifndef WIN32
log_opts.filename2 = pg_strdup(log_opts.filename);
#else
log_opts.filename2 = pg_strdup(DEVNULL);
#endif
/* 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 */
check_required_directory(&old_cluster.bindir, "PGBINOLD", "-b",
"old cluster binaries reside");
check_required_directory(&new_cluster.bindir, "PGBINNEW", "-B",
"new cluster binaries reside");
check_required_directory(&old_cluster.pgdata, "PGDATAOLD", "-d",
"old cluster data resides");
check_required_directory(&new_cluster.pgdata, "PGDATANEW", "-D",
"new cluster data resides");
}
static void
usage(void)
printf(_("pg_upgrade upgrades a PostgreSQL cluster to a different major version.\n\
\nUsage:\n\
pg_upgrade [OPTIONS]...\n\
-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 internal activity to file\n\
-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\
-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 cluster 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 PGDATAOLD=oldCluster/data\n\
$ export PGDATANEW=newCluster/data\n\
$ export PGBINOLD=oldCluster/bin\n\
$ export PGBINNEW=newCluster/bin\n\
$ pg_upgrade\n"));
#else
printf(_("\
C:\\> set PGDATAOLD=oldCluster/data\n\
C:\\> set PGDATANEW=newCluster/data\n\
C:\\> set PGBINOLD=oldCluster/bin\n\
C:\\> set PGBINNEW=newCluster/bin\n\
C:\\> pg_upgrade\n"));
#endif
printf(_("\nReport bugs to <pgsql-bugs@postgresql.org>.\n"));
* check_required_directory()
* Checks 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
check_required_directory(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);
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] == '\\')
(*dirpath)[strlen(*dirpath) - 1] = 0;
}
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
/*
* 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];
char cmd[MAXPGPATH], cmd_output[MAX_STRING];
FILE *fd, *output;
/* If there is no postgresql.conf, it can't be a config-only dir */
snprintf(filename, sizeof(filename), "%s/postgresql.conf", cluster->pgconfig);
if ((fd = fopen(filename, "r")) == NULL)
return;
fclose(fd);
/* If PG_VERSION exists, it can't be a config-only dir */
snprintf(filename, sizeof(filename), "%s/PG_VERSION", cluster->pgconfig);
if ((fd = fopen(filename, "r")) != NULL)
{
fclose(fd);
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));
/*
* 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.
*/
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",
cmd, getErrorText(errno));
pclose(output);
/* Remove trailing newline */
if (strchr(cmd_output, '\n') != NULL)
*strchr(cmd_output, '\n') = '\0';
cluster->pgdata = pg_strdup(cmd_output);
check_ok();
}