Header And Logo

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

option.c

Go to the documentation of this file.
00001 /*
00002  *  opt.c
00003  *
00004  *  options functions
00005  *
00006  *  Copyright (c) 2010-2013, PostgreSQL Global Development Group
00007  *  contrib/pg_upgrade/option.c
00008  */
00009 
00010 #include "postgres_fe.h"
00011 
00012 #include "miscadmin.h"
00013 
00014 #include "pg_upgrade.h"
00015 
00016 #include <getopt_long.h>
00017 #include <time.h>
00018 #include <sys/types.h>
00019 #include <sys/stat.h>
00020 #ifdef WIN32
00021 #include <io.h>
00022 #endif
00023 
00024 
00025 static void usage(void);
00026 static void check_required_directory(char **dirpath, char **configpath,
00027                    char *envVarName, char *cmdLineOption, char *description);
00028 
00029 
00030 UserOpts    user_opts;
00031 
00032 
00033 /*
00034  * parseCommandLine()
00035  *
00036  *  Parses the command line (argc, argv[]) and loads structures
00037  */
00038 void
00039 parseCommandLine(int argc, char *argv[])
00040 {
00041     static struct option long_options[] = {
00042         {"old-datadir", required_argument, NULL, 'd'},
00043         {"new-datadir", required_argument, NULL, 'D'},
00044         {"old-bindir", required_argument, NULL, 'b'},
00045         {"new-bindir", required_argument, NULL, 'B'},
00046         {"old-options", required_argument, NULL, 'o'},
00047         {"new-options", required_argument, NULL, 'O'},
00048         {"old-port", required_argument, NULL, 'p'},
00049         {"new-port", required_argument, NULL, 'P'},
00050 
00051         {"user", required_argument, NULL, 'u'},
00052         {"check", no_argument, NULL, 'c'},
00053         {"link", no_argument, NULL, 'k'},
00054         {"retain", no_argument, NULL, 'r'},
00055         {"jobs", required_argument, NULL, 'j'},
00056         {"verbose", no_argument, NULL, 'v'},
00057         {NULL, 0, NULL, 0}
00058     };
00059     int         option;         /* Command line option */
00060     int         optindex = 0;   /* used by getopt_long */
00061     int         os_user_effective_id;
00062     FILE       *fp;
00063     char      **filename;
00064     time_t      run_time = time(NULL);
00065 
00066     user_opts.transfer_mode = TRANSFER_MODE_COPY;
00067 
00068     os_info.progname = get_progname(argv[0]);
00069 
00070     /* Process libpq env. variables; load values here for usage() output */
00071     old_cluster.port = getenv("PGPORTOLD") ? atoi(getenv("PGPORTOLD")) : DEF_PGUPORT;
00072     new_cluster.port = getenv("PGPORTNEW") ? atoi(getenv("PGPORTNEW")) : DEF_PGUPORT;
00073 
00074     os_user_effective_id = get_user_info(&os_info.user);
00075     /* we override just the database user name;  we got the OS id above */
00076     if (getenv("PGUSER"))
00077     {
00078         pg_free(os_info.user);
00079         /* must save value, getenv()'s pointer is not stable */
00080         os_info.user = pg_strdup(getenv("PGUSER"));
00081     }
00082 
00083     if (argc > 1)
00084     {
00085         if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0 ||
00086             strcmp(argv[1], "-?") == 0)
00087         {
00088             usage();
00089             exit(0);
00090         }
00091         if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
00092         {
00093             puts("pg_upgrade (PostgreSQL) " PG_VERSION);
00094             exit(0);
00095         }
00096     }
00097 
00098     /* Allow help and version to be run as root, so do the test here. */
00099     if (os_user_effective_id == 0)
00100         pg_log(PG_FATAL, "%s: cannot be run as root\n", os_info.progname);
00101 
00102     if ((log_opts.internal = fopen_priv(INTERNAL_LOG_FILE, "a")) == NULL)
00103         pg_log(PG_FATAL, "cannot write to log file %s\n", INTERNAL_LOG_FILE);
00104 
00105     while ((option = getopt_long(argc, argv, "d:D:b:B:cj:ko:O:p:P:ru:v",
00106                                  long_options, &optindex)) != -1)
00107     {
00108         switch (option)
00109         {
00110             case 'b':
00111                 old_cluster.bindir = pg_strdup(optarg);
00112                 break;
00113 
00114             case 'B':
00115                 new_cluster.bindir = pg_strdup(optarg);
00116                 break;
00117 
00118             case 'c':
00119                 user_opts.check = true;
00120                 break;
00121 
00122             case 'd':
00123                 old_cluster.pgdata = pg_strdup(optarg);
00124                 old_cluster.pgconfig = pg_strdup(optarg);
00125                 break;
00126 
00127             case 'D':
00128                 new_cluster.pgdata = pg_strdup(optarg);
00129                 new_cluster.pgconfig = pg_strdup(optarg);
00130                 break;
00131 
00132             case 'j':
00133                 user_opts.jobs = atoi(optarg);
00134                 break;
00135 
00136             case 'k':
00137                 user_opts.transfer_mode = TRANSFER_MODE_LINK;
00138                 break;
00139 
00140             case 'o':
00141                 old_cluster.pgopts = pg_strdup(optarg);
00142                 break;
00143 
00144             case 'O':
00145                 new_cluster.pgopts = pg_strdup(optarg);
00146                 break;
00147 
00148                 /*
00149                  * Someday, the port number option could be removed and passed
00150                  * using -o/-O, but that requires postmaster -C to be
00151                  * supported on all old/new versions.
00152                  */
00153             case 'p':
00154                 if ((old_cluster.port = atoi(optarg)) <= 0)
00155                 {
00156                     pg_log(PG_FATAL, "invalid old port number\n");
00157                     exit(1);
00158                 }
00159                 break;
00160 
00161             case 'P':
00162                 if ((new_cluster.port = atoi(optarg)) <= 0)
00163                 {
00164                     pg_log(PG_FATAL, "invalid new port number\n");
00165                     exit(1);
00166                 }
00167                 break;
00168 
00169             case 'r':
00170                 log_opts.retain = true;
00171                 break;
00172 
00173             case 'u':
00174                 pg_free(os_info.user);
00175                 os_info.user = pg_strdup(optarg);
00176 
00177                 /*
00178                  * Push the user name into the environment so pre-9.1
00179                  * pg_ctl/libpq uses it.
00180                  */
00181                 pg_putenv("PGUSER", os_info.user);
00182                 break;
00183 
00184             case 'v':
00185                 pg_log(PG_REPORT, "Running in verbose mode\n");
00186                 log_opts.verbose = true;
00187                 break;
00188 
00189             default:
00190                 pg_log(PG_FATAL,
00191                        "Try \"%s --help\" for more information.\n",
00192                        os_info.progname);
00193                 break;
00194         }
00195     }
00196 
00197     /* label start of upgrade in logfiles */
00198     for (filename = output_files; *filename != NULL; filename++)
00199     {
00200         if ((fp = fopen_priv(*filename, "a")) == NULL)
00201             pg_log(PG_FATAL, "cannot write to log file %s\n", *filename);
00202 
00203         /* Start with newline because we might be appending to a file. */
00204         fprintf(fp, "\n"
00205         "-----------------------------------------------------------------\n"
00206                 "  pg_upgrade run on %s"
00207                 "-----------------------------------------------------------------\n\n",
00208                 ctime(&run_time));
00209         fclose(fp);
00210     }
00211 
00212     /* Get values from env if not already set */
00213     check_required_directory(&old_cluster.bindir, NULL, "PGBINOLD", "-b",
00214                              "old cluster binaries reside");
00215     check_required_directory(&new_cluster.bindir, NULL, "PGBINNEW", "-B",
00216                              "new cluster binaries reside");
00217     check_required_directory(&old_cluster.pgdata, &old_cluster.pgconfig,
00218                              "PGDATAOLD", "-d", "old cluster data resides");
00219     check_required_directory(&new_cluster.pgdata, &new_cluster.pgconfig,
00220                              "PGDATANEW", "-D", "new cluster data resides");
00221 }
00222 
00223 
00224 static void
00225 usage(void)
00226 {
00227     printf(_("pg_upgrade upgrades a PostgreSQL cluster to a different major version.\n\
00228 \nUsage:\n\
00229   pg_upgrade [OPTION]...\n\
00230 \n\
00231 Options:\n\
00232   -b, --old-bindir=OLDBINDIR    old cluster executable directory\n\
00233   -B, --new-bindir=NEWBINDIR    new cluster executable directory\n\
00234   -c, --check                   check clusters only, don't change any data\n\
00235   -d, --old-datadir=OLDDATADIR  old cluster data directory\n\
00236   -D, --new-datadir=NEWDATADIR  new cluster data directory\n\
00237   -j, --jobs                    number of simultaneous processes or threads to use\n\
00238   -k, --link                    link instead of copying files to new cluster\n\
00239   -o, --old-options=OPTIONS     old cluster options to pass to the server\n\
00240   -O, --new-options=OPTIONS     new cluster options to pass to the server\n\
00241   -p, --old-port=OLDPORT        old cluster port number (default %d)\n\
00242   -P, --new-port=NEWPORT        new cluster port number (default %d)\n\
00243   -r, --retain                  retain SQL and log files after success\n\
00244   -u, --user=NAME               cluster superuser (default \"%s\")\n\
00245   -v, --verbose                 enable verbose internal logging\n\
00246   -V, --version                 display version information, then exit\n\
00247   -?, -h, --help                show this help, then exit\n\
00248 \n\
00249 Before running pg_upgrade you must:\n\
00250   create a new database cluster (using the new version of initdb)\n\
00251   shutdown the postmaster servicing the old cluster\n\
00252   shutdown the postmaster servicing the new cluster\n\
00253 \n\
00254 When you run pg_upgrade, you must provide the following information:\n\
00255   the data directory for the old cluster  (-d OLDDATADIR)\n\
00256   the data directory for the new cluster  (-D NEWDATADIR)\n\
00257   the \"bin\" directory for the old version (-b OLDBINDIR)\n\
00258   the \"bin\" directory for the new version (-B NEWBINDIR)\n\
00259 \n\
00260 For example:\n\
00261   pg_upgrade -d oldCluster/data -D newCluster/data -b oldCluster/bin -B newCluster/bin\n\
00262 or\n"), old_cluster.port, new_cluster.port, os_info.user);
00263 #ifndef WIN32
00264     printf(_("\
00265   $ export PGDATAOLD=oldCluster/data\n\
00266   $ export PGDATANEW=newCluster/data\n\
00267   $ export PGBINOLD=oldCluster/bin\n\
00268   $ export PGBINNEW=newCluster/bin\n\
00269   $ pg_upgrade\n"));
00270 #else
00271     printf(_("\
00272   C:\\> set PGDATAOLD=oldCluster/data\n\
00273   C:\\> set PGDATANEW=newCluster/data\n\
00274   C:\\> set PGBINOLD=oldCluster/bin\n\
00275   C:\\> set PGBINNEW=newCluster/bin\n\
00276   C:\\> pg_upgrade\n"));
00277 #endif
00278     printf(_("\nReport bugs to <[email protected]>.\n"));
00279 }
00280 
00281 
00282 /*
00283  * check_required_directory()
00284  *
00285  * Checks a directory option.
00286  *  dirpath       - the directory name supplied on the command line
00287  *  configpath    - optional configuration directory
00288  *  envVarName    - the name of an environment variable to get if dirpath is NULL
00289  *  cmdLineOption - the command line option corresponds to this directory (-o, -O, -n, -N)
00290  *  description   - a description of this directory option
00291  *
00292  * We use the last two arguments to construct a meaningful error message if the
00293  * user hasn't provided the required directory name.
00294  */
00295 static void
00296 check_required_directory(char **dirpath, char **configpath,
00297                          char *envVarName, char *cmdLineOption,
00298                          char *description)
00299 {
00300     if (*dirpath == NULL || strlen(*dirpath) == 0)
00301     {
00302         const char *envVar;
00303 
00304         if ((envVar = getenv(envVarName)) && strlen(envVar))
00305         {
00306             *dirpath = pg_strdup(envVar);
00307             if (configpath)
00308                 *configpath = pg_strdup(envVar);
00309         }
00310         else
00311             pg_log(PG_FATAL, "You must identify the directory where the %s.\n"
00312                    "Please use the %s command-line option or the %s environment variable.\n",
00313                    description, cmdLineOption, envVarName);
00314     }
00315 
00316     /*
00317      * Trim off any trailing path separators because we construct paths
00318      * by appending to this path.
00319      */
00320 #ifndef WIN32
00321     if ((*dirpath)[strlen(*dirpath) - 1] == '/')
00322 #else
00323     if ((*dirpath)[strlen(*dirpath) - 1] == '/' ||
00324         (*dirpath)[strlen(*dirpath) - 1] == '\\')
00325 #endif
00326         (*dirpath)[strlen(*dirpath) - 1] = 0;
00327 }
00328 
00329 /*
00330  * adjust_data_dir
00331  *
00332  * If a configuration-only directory was specified, find the real data dir
00333  * by quering the running server.  This has limited checking because we
00334  * can't check for a running server because we can't find postmaster.pid.
00335  */
00336 void
00337 adjust_data_dir(ClusterInfo *cluster)
00338 {
00339     char        filename[MAXPGPATH];
00340     char        cmd[MAXPGPATH],
00341                 cmd_output[MAX_STRING];
00342     FILE       *fp,
00343                *output;
00344 
00345     /* If there is no postgresql.conf, it can't be a config-only dir */
00346     snprintf(filename, sizeof(filename), "%s/postgresql.conf", cluster->pgconfig);
00347     if ((fp = fopen(filename, "r")) == NULL)
00348         return;
00349     fclose(fp);
00350 
00351     /* If PG_VERSION exists, it can't be a config-only dir */
00352     snprintf(filename, sizeof(filename), "%s/PG_VERSION", cluster->pgconfig);
00353     if ((fp = fopen(filename, "r")) != NULL)
00354     {
00355         fclose(fp);
00356         return;
00357     }
00358 
00359     /* Must be a configuration directory, so find the real data directory. */
00360 
00361     prep_status("Finding the real data directory for the %s cluster",
00362                 CLUSTER_NAME(cluster));
00363 
00364     /*
00365      * We don't have a data directory yet, so we can't check the PG version,
00366      * so this might fail --- only works for PG 9.2+.   If this fails,
00367      * pg_upgrade will fail anyway because the data files will not be found.
00368      */
00369     snprintf(cmd, sizeof(cmd), "\"%s/postmaster\" -D \"%s\" -C data_directory",
00370              cluster->bindir, cluster->pgconfig);
00371 
00372     if ((output = popen(cmd, "r")) == NULL ||
00373         fgets(cmd_output, sizeof(cmd_output), output) == NULL)
00374         pg_log(PG_FATAL, "Could not get data directory using %s: %s\n",
00375                cmd, getErrorText(errno));
00376 
00377     pclose(output);
00378 
00379     /* Remove trailing newline */
00380     if (strchr(cmd_output, '\n') != NULL)
00381         *strchr(cmd_output, '\n') = '\0';
00382 
00383     cluster->pgdata = pg_strdup(cmd_output);
00384 
00385     check_ok();
00386 }
00387 
00388 
00389 /*
00390  * get_sock_dir
00391  *
00392  * Identify the socket directory to use for this cluster.  If we're doing
00393  * a live check (old cluster only), we need to find out where the postmaster
00394  * is listening.  Otherwise, we're going to put the socket into the current
00395  * directory.
00396  */
00397 void
00398 get_sock_dir(ClusterInfo *cluster, bool live_check)
00399 {
00400 #ifdef HAVE_UNIX_SOCKETS
00401     /*
00402      *  sockdir and port were added to postmaster.pid in PG 9.1.
00403      *  Pre-9.1 cannot process pg_ctl -w for sockets in non-default
00404      *  locations.
00405      */
00406     if (GET_MAJOR_VERSION(cluster->major_version) >= 901)
00407     {
00408         if (!live_check)
00409         {
00410             /* Use the current directory for the socket */
00411             cluster->sockdir = pg_malloc(MAXPGPATH);
00412             if (!getcwd(cluster->sockdir, MAXPGPATH))
00413                 pg_log(PG_FATAL, "cannot find current directory\n");
00414         }
00415         else
00416         {
00417             /*
00418              *  If we are doing a live check, we will use the old cluster's Unix
00419              *  domain socket directory so we can connect to the live server.
00420              */
00421             unsigned short orig_port = cluster->port;
00422             char        filename[MAXPGPATH], line[MAXPGPATH];
00423             FILE        *fp;
00424             int         lineno;
00425     
00426             snprintf(filename, sizeof(filename), "%s/postmaster.pid",
00427                      cluster->pgdata);
00428             if ((fp = fopen(filename, "r")) == NULL)
00429                 pg_log(PG_FATAL, "Cannot open file %s: %m\n", filename);
00430     
00431             for (lineno = 1;
00432                  lineno <= Max(LOCK_FILE_LINE_PORT, LOCK_FILE_LINE_SOCKET_DIR);
00433                  lineno++)
00434             {
00435                 if (fgets(line, sizeof(line), fp) == NULL)
00436                     pg_log(PG_FATAL, "Cannot read line %d from %s: %m\n", lineno, filename);
00437     
00438                 /* potentially overwrite user-supplied value */
00439                 if (lineno == LOCK_FILE_LINE_PORT)
00440                     sscanf(line, "%hu", &old_cluster.port);
00441                 if (lineno == LOCK_FILE_LINE_SOCKET_DIR)
00442                 {
00443                     cluster->sockdir = pg_malloc(MAXPGPATH);
00444                     /* strip off newline */
00445                     sscanf(line, "%s\n", cluster->sockdir);
00446                 }
00447             }
00448             fclose(fp);
00449     
00450             /* warn of port number correction */
00451             if (orig_port != DEF_PGUPORT && old_cluster.port != orig_port)
00452                 pg_log(PG_WARNING, "User-supplied old port number %hu corrected to %hu\n",
00453                 orig_port, cluster->port);
00454         }
00455     }
00456     else
00457         /* Can't get sockdir and pg_ctl -w can't use a non-default, use default */
00458         cluster->sockdir = NULL;
00459 
00460 #else /* !HAVE_UNIX_SOCKETS */
00461     cluster->sockdir = NULL;
00462 #endif
00463 }