Header And Logo

PostgreSQL
| The world's most advanced open source database.

Functions | Variables

option.c File Reference

#include "postgres_fe.h"
#include "miscadmin.h"
#include "pg_upgrade.h"
#include <getopt_long.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
Include dependency graph for option.c:

Go to the source code of this file.

Functions

static void usage (void)
static void check_required_directory (char **dirpath, char **configpath, char *envVarName, char *cmdLineOption, char *description)
void parseCommandLine (int argc, char *argv[])
void adjust_data_dir (ClusterInfo *cluster)
void get_sock_dir (ClusterInfo *cluster, bool live_check)

Variables

UserOpts user_opts

Function Documentation

void adjust_data_dir ( ClusterInfo cluster  ) 

Definition at line 337 of file option.c.

References ClusterInfo::bindir, check_ok(), CLUSTER_NAME, filename, getErrorText(), NULL, PG_FATAL, pg_log(), pg_strdup(), ClusterInfo::pgconfig, ClusterInfo::pgdata, prep_status(), and snprintf().

Referenced by main().

{
    char        filename[MAXPGPATH];
    char        cmd[MAXPGPATH],
                cmd_output[MAX_STRING];
    FILE       *fp,
               *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 ((fp = fopen(filename, "r")) == NULL)
        return;
    fclose(fp);

    /* If PG_VERSION exists, it can't be a config-only dir */
    snprintf(filename, sizeof(filename), "%s/PG_VERSION", cluster->pgconfig);
    if ((fp = fopen(filename, "r")) != NULL)
    {
        fclose(fp);
        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();
}

static void check_required_directory ( char **  dirpath,
char **  configpath,
char *  envVarName,
char *  cmdLineOption,
char *  description 
) [static]

Definition at line 296 of file option.c.

References NULL, PG_FATAL, pg_log(), and pg_strdup().

Referenced by parseCommandLine().

{
    if (*dirpath == NULL || strlen(*dirpath) == 0)
    {
        const char *envVar;

        if ((envVar = getenv(envVarName)) && strlen(envVar))
        {
            *dirpath = pg_strdup(envVar);
            if (configpath)
                *configpath = 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 because we construct paths
     * by appending to this path.
     */
#ifndef WIN32
    if ((*dirpath)[strlen(*dirpath) - 1] == '/')
#else
    if ((*dirpath)[strlen(*dirpath) - 1] == '/' ||
        (*dirpath)[strlen(*dirpath) - 1] == '\\')
#endif
        (*dirpath)[strlen(*dirpath) - 1] = 0;
}

void get_sock_dir ( ClusterInfo cluster,
bool  live_check 
)

Definition at line 398 of file option.c.

References DEF_PGUPORT, filename, GET_MAJOR_VERSION, LOCK_FILE_LINE_PORT, LOCK_FILE_LINE_SOCKET_DIR, ClusterInfo::major_version, Max, MAXPGPATH, NULL, old_cluster, PG_FATAL, pg_log(), pg_malloc(), PG_WARNING, ClusterInfo::pgdata, ClusterInfo::port, snprintf(), and ClusterInfo::sockdir.

Referenced by main().

{
#ifdef HAVE_UNIX_SOCKETS
    /*
     *  sockdir and port were added to postmaster.pid in PG 9.1.
     *  Pre-9.1 cannot process pg_ctl -w for sockets in non-default
     *  locations.
     */
    if (GET_MAJOR_VERSION(cluster->major_version) >= 901)
    {
        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.
             */
            unsigned short orig_port = cluster->port;
            char        filename[MAXPGPATH], line[MAXPGPATH];
            FILE        *fp;
            int         lineno;
    
            snprintf(filename, sizeof(filename), "%s/postmaster.pid",
                     cluster->pgdata);
            if ((fp = fopen(filename, "r")) == NULL)
                pg_log(PG_FATAL, "Cannot open file %s: %m\n", filename);
    
            for (lineno = 1;
                 lineno <= Max(LOCK_FILE_LINE_PORT, LOCK_FILE_LINE_SOCKET_DIR);
                 lineno++)
            {
                if (fgets(line, sizeof(line), fp) == NULL)
                    pg_log(PG_FATAL, "Cannot read line %d from %s: %m\n", lineno, filename);
    
                /* potentially overwrite user-supplied value */
                if (lineno == LOCK_FILE_LINE_PORT)
                    sscanf(line, "%hu", &old_cluster.port);
                if (lineno == LOCK_FILE_LINE_SOCKET_DIR)
                {
                    cluster->sockdir = pg_malloc(MAXPGPATH);
                    /* strip off newline */
                    sscanf(line, "%s\n", cluster->sockdir);
                }
            }
            fclose(fp);
    
            /* warn of port number correction */
            if (orig_port != DEF_PGUPORT && old_cluster.port != orig_port)
                pg_log(PG_WARNING, "User-supplied old port number %hu corrected to %hu\n",
                orig_port, cluster->port);
        }
    }
    else
        /* Can't get sockdir and pg_ctl -w can't use a non-default, use default */
        cluster->sockdir = NULL;

#else /* !HAVE_UNIX_SOCKETS */
    cluster->sockdir = NULL;
#endif
}

void parseCommandLine ( int  argc,
char *  argv[] 
)

Definition at line 39 of file option.c.

References ClusterInfo::bindir, UserOpts::check, check_required_directory(), filename, fopen_priv(), get_progname(), get_user_info(), getopt_long(), LogOpts::internal, INTERNAL_LOG_FILE, UserOpts::jobs, log_opts, new_cluster, NULL, old_cluster, optarg, os_info, output_files, PG_FATAL, pg_free(), pg_log(), pg_putenv(), PG_REPORT, pg_strdup(), ClusterInfo::pgconfig, ClusterInfo::pgdata, ClusterInfo::pgopts, ClusterInfo::port, OSInfo::progname, LogOpts::retain, UserOpts::transfer_mode, usage(), OSInfo::user, and LogOpts::verbose.

Referenced by main().

{
    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'},
        {"link", no_argument, NULL, 'k'},
        {"retain", no_argument, NULL, 'r'},
        {"jobs", required_argument, NULL, 'j'},
        {"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;
    FILE       *fp;
    char      **filename;
    time_t      run_time = time(NULL);

    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);

    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);

    while ((option = getopt_long(argc, argv, "d:D:b:B:cj:ko:O:p:P:ru: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;
                break;

            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;

            case 'j':
                user_opts.jobs = atoi(optarg);
                break;

            case 'k':
                user_opts.transfer_mode = TRANSFER_MODE_LINK;
                break;

            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.
                 */
            case 'p':
                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 'r':
                log_opts.retain = true;
                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;
        }
    }

    /* label start of upgrade in logfiles */
    for (filename = output_files; *filename != NULL; filename++)
    {
        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. */
        fprintf(fp, "\n"
        "-----------------------------------------------------------------\n"
                "  pg_upgrade run on %s"
                "-----------------------------------------------------------------\n\n",
                ctime(&run_time));
        fclose(fp);
    }

    /* Get values from env if not already set */
    check_required_directory(&old_cluster.bindir, NULL, "PGBINOLD", "-b",
                             "old cluster binaries reside");
    check_required_directory(&new_cluster.bindir, NULL, "PGBINNEW", "-B",
                             "new cluster binaries reside");
    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");
}

static void usage ( void   )  [static]

Definition at line 225 of file option.c.

References _, new_cluster, old_cluster, os_info, ClusterInfo::port, and OSInfo::user.

{
    printf(_("pg_upgrade upgrades a PostgreSQL cluster to a different major version.\n\
\nUsage:\n\
  pg_upgrade [OPTION]...\n\
\n\
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\
  -j, --jobs                    number of simultaneous processes or threads to use\n\
  -k, --link                    link instead of copying files to new cluster\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\
  -r, --retain                  retain SQL and log files after success\n\
  -u, --user=NAME               cluster superuser (default \"%s\")\n\
  -v, --verbose                 enable verbose internal logging\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 <[email protected]>.\n"));
}


Variable Documentation