LCOV - code coverage report
Current view: top level - src/bin/pg_upgrade - server.c (source / functions) Hit Total Coverage
Test: PostgreSQL 15devel Lines: 97 122 79.5 %
Date: 2021-12-09 04:09:06 Functions: 9 9 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  *  server.c
       3             :  *
       4             :  *  database server functions
       5             :  *
       6             :  *  Copyright (c) 2010-2021, PostgreSQL Global Development Group
       7             :  *  src/bin/pg_upgrade/server.c
       8             :  */
       9             : 
      10             : #include "postgres_fe.h"
      11             : 
      12             : #include "common/connect.h"
      13             : #include "fe_utils/string_utils.h"
      14             : #include "pg_upgrade.h"
      15             : 
      16             : static PGconn *get_db_conn(ClusterInfo *cluster, const char *db_name);
      17             : 
      18             : 
      19             : /*
      20             :  * connectToServer()
      21             :  *
      22             :  *  Connects to the desired database on the designated server.
      23             :  *  If the connection attempt fails, this function logs an error
      24             :  *  message and calls exit() to kill the program.
      25             :  */
      26             : PGconn *
      27         104 : connectToServer(ClusterInfo *cluster, const char *db_name)
      28             : {
      29         104 :     PGconn     *conn = get_db_conn(cluster, db_name);
      30             : 
      31         104 :     if (conn == NULL || PQstatus(conn) != CONNECTION_OK)
      32             :     {
      33           0 :         pg_log(PG_REPORT, "%s", PQerrorMessage(conn));
      34             : 
      35           0 :         if (conn)
      36           0 :             PQfinish(conn);
      37             : 
      38           0 :         printf(_("Failure, exiting\n"));
      39           0 :         exit(1);
      40             :     }
      41             : 
      42         104 :     PQclear(executeQueryOrDie(conn, ALWAYS_SECURE_SEARCH_PATH_SQL));
      43             : 
      44         104 :     return conn;
      45             : }
      46             : 
      47             : 
      48             : /*
      49             :  * get_db_conn()
      50             :  *
      51             :  * get database connection, using named database + standard params for cluster
      52             :  *
      53             :  * Caller must check for connection failure!
      54             :  */
      55             : static PGconn *
      56         112 : get_db_conn(ClusterInfo *cluster, const char *db_name)
      57             : {
      58             :     PQExpBufferData conn_opts;
      59             :     PGconn     *conn;
      60             : 
      61             :     /* Build connection string with proper quoting */
      62         112 :     initPQExpBuffer(&conn_opts);
      63         112 :     appendPQExpBufferStr(&conn_opts, "dbname=");
      64         112 :     appendConnStrVal(&conn_opts, db_name);
      65         112 :     appendPQExpBufferStr(&conn_opts, " user=");
      66         112 :     appendConnStrVal(&conn_opts, os_info.user);
      67         112 :     appendPQExpBuffer(&conn_opts, " port=%d", cluster->port);
      68         112 :     if (cluster->sockdir)
      69             :     {
      70         112 :         appendPQExpBufferStr(&conn_opts, " host=");
      71         112 :         appendConnStrVal(&conn_opts, cluster->sockdir);
      72             :     }
      73             : 
      74         112 :     conn = PQconnectdb(conn_opts.data);
      75         112 :     termPQExpBuffer(&conn_opts);
      76         112 :     return conn;
      77             : }
      78             : 
      79             : 
      80             : /*
      81             :  * cluster_conn_opts()
      82             :  *
      83             :  * Return standard command-line options for connecting to this cluster when
      84             :  * using psql, pg_dump, etc.  Ideally this would match what get_db_conn()
      85             :  * sets, but the utilities we need aren't very consistent about the treatment
      86             :  * of database name options, so we leave that out.
      87             :  *
      88             :  * Result is valid until the next call to this function.
      89             :  */
      90             : char *
      91          32 : cluster_conn_opts(ClusterInfo *cluster)
      92             : {
      93             :     static PQExpBuffer buf;
      94             : 
      95          32 :     if (buf == NULL)
      96           2 :         buf = createPQExpBuffer();
      97             :     else
      98          30 :         resetPQExpBuffer(buf);
      99             : 
     100          32 :     if (cluster->sockdir)
     101             :     {
     102          32 :         appendPQExpBufferStr(buf, "--host ");
     103          32 :         appendShellString(buf, cluster->sockdir);
     104          32 :         appendPQExpBufferChar(buf, ' ');
     105             :     }
     106          32 :     appendPQExpBuffer(buf, "--port %d --username ", cluster->port);
     107          32 :     appendShellString(buf, os_info.user);
     108             : 
     109          32 :     return buf->data;
     110             : }
     111             : 
     112             : 
     113             : /*
     114             :  * executeQueryOrDie()
     115             :  *
     116             :  *  Formats a query string from the given arguments and executes the
     117             :  *  resulting query.  If the query fails, this function logs an error
     118             :  *  message and calls exit() to kill the program.
     119             :  */
     120             : PGresult *
     121         224 : executeQueryOrDie(PGconn *conn, const char *fmt,...)
     122             : {
     123             :     static char query[QUERY_ALLOC];
     124             :     va_list     args;
     125             :     PGresult   *result;
     126             :     ExecStatusType status;
     127             : 
     128         224 :     va_start(args, fmt);
     129         224 :     vsnprintf(query, sizeof(query), fmt, args);
     130         224 :     va_end(args);
     131             : 
     132         224 :     pg_log(PG_VERBOSE, "executing: %s\n", query);
     133         224 :     result = PQexec(conn, query);
     134         224 :     status = PQresultStatus(result);
     135             : 
     136         224 :     if ((status != PGRES_TUPLES_OK) && (status != PGRES_COMMAND_OK))
     137             :     {
     138           0 :         pg_log(PG_REPORT, "SQL command failed\n%s\n%s", query,
     139             :                PQerrorMessage(conn));
     140           0 :         PQclear(result);
     141           0 :         PQfinish(conn);
     142           0 :         printf(_("Failure, exiting\n"));
     143           0 :         exit(1);
     144             :     }
     145             :     else
     146         224 :         return result;
     147             : }
     148             : 
     149             : 
     150             : /*
     151             :  * get_major_server_version()
     152             :  *
     153             :  * gets the version (in unsigned int form) for the given datadir. Assumes
     154             :  * that datadir is an absolute path to a valid pgdata directory. The version
     155             :  * is retrieved by reading the PG_VERSION file.
     156             :  */
     157             : uint32
     158           4 : get_major_server_version(ClusterInfo *cluster)
     159             : {
     160             :     FILE       *version_fd;
     161             :     char        ver_filename[MAXPGPATH];
     162           4 :     int         v1 = 0,
     163           4 :                 v2 = 0;
     164             : 
     165           4 :     snprintf(ver_filename, sizeof(ver_filename), "%s/PG_VERSION",
     166             :              cluster->pgdata);
     167           4 :     if ((version_fd = fopen(ver_filename, "r")) == NULL)
     168           0 :         pg_fatal("could not open version file \"%s\": %m\n", ver_filename);
     169             : 
     170           4 :     if (fscanf(version_fd, "%63s", cluster->major_version_str) == 0 ||
     171           4 :         sscanf(cluster->major_version_str, "%d.%d", &v1, &v2) < 1)
     172           0 :         pg_fatal("could not parse version file \"%s\"\n", ver_filename);
     173             : 
     174           4 :     fclose(version_fd);
     175             : 
     176           4 :     if (v1 < 10)
     177             :     {
     178             :         /* old style, e.g. 9.6.1 */
     179           0 :         return v1 * 10000 + v2 * 100;
     180             :     }
     181             :     else
     182             :     {
     183             :         /* new style, e.g. 10.1 */
     184           4 :         return v1 * 10000;
     185             :     }
     186             : }
     187             : 
     188             : 
     189             : static void
     190           2 : stop_postmaster_atexit(void)
     191             : {
     192           2 :     stop_postmaster(true);
     193           2 : }
     194             : 
     195             : 
     196             : bool
     197           8 : start_postmaster(ClusterInfo *cluster, bool report_and_exit_on_error)
     198             : {
     199             :     char        cmd[MAXPGPATH * 4 + 1000];
     200             :     PGconn     *conn;
     201           8 :     bool        pg_ctl_return = false;
     202             :     char        socket_string[MAXPGPATH + 200];
     203             : 
     204             :     static bool exit_hook_registered = false;
     205             : 
     206           8 :     if (!exit_hook_registered)
     207             :     {
     208           2 :         atexit(stop_postmaster_atexit);
     209           2 :         exit_hook_registered = true;
     210             :     }
     211             : 
     212           8 :     socket_string[0] = '\0';
     213             : 
     214             : #if defined(HAVE_UNIX_SOCKETS) && !defined(WIN32)
     215             :     /* prevent TCP/IP connections, restrict socket access */
     216           8 :     strcat(socket_string,
     217             :            " -c listen_addresses='' -c unix_socket_permissions=0700");
     218             : 
     219             :     /* Have a sockdir?  Tell the postmaster. */
     220           8 :     if (cluster->sockdir)
     221           8 :         snprintf(socket_string + strlen(socket_string),
     222           8 :                  sizeof(socket_string) - strlen(socket_string),
     223             :                  " -c %s='%s'",
     224           8 :                  (GET_MAJOR_VERSION(cluster->major_version) <= 902) ?
     225             :                  "unix_socket_directory" : "unix_socket_directories",
     226             :                  cluster->sockdir);
     227             : #endif
     228             : 
     229             :     /*
     230             :      * Since PG 9.1, we have used -b to disable autovacuum.  For earlier
     231             :      * releases, setting autovacuum=off disables cleanup vacuum and analyze,
     232             :      * but freeze vacuums can still happen, so we set
     233             :      * autovacuum_freeze_max_age to its maximum.
     234             :      * (autovacuum_multixact_freeze_max_age was introduced after 9.1, so there
     235             :      * is no need to set that.)  We assume all datfrozenxid and relfrozenxid
     236             :      * values are less than a gap of 2000000000 from the current xid counter,
     237             :      * so autovacuum will not touch them.
     238             :      *
     239             :      * Turn off durability requirements to improve object creation speed, and
     240             :      * we only modify the new cluster, so only use it there.  If there is a
     241             :      * crash, the new cluster has to be recreated anyway.  fsync=off is a big
     242             :      * win on ext4.
     243             :      *
     244             :      * Force vacuum_defer_cleanup_age to 0 on the new cluster, so that
     245             :      * vacuumdb --freeze actually freezes the tuples.
     246             :      */
     247          16 :     snprintf(cmd, sizeof(cmd),
     248             :              "\"%s/pg_ctl\" -w -l \"%s\" -D \"%s\" -o \"-p %d%s%s %s%s\" start",
     249           8 :              cluster->bindir, SERVER_LOG_FILE, cluster->pgconfig, cluster->port,
     250           8 :              (cluster->controldata.cat_ver >=
     251             :               BINARY_UPGRADE_SERVER_FLAG_CAT_VER) ? " -b" :
     252             :              " -c autovacuum=off -c autovacuum_freeze_max_age=2000000000",
     253             :              (cluster == &new_cluster) ?
     254             :              " -c synchronous_commit=off -c fsync=off -c full_page_writes=off -c vacuum_defer_cleanup_age=0" : "",
     255           8 :              cluster->pgopts ? cluster->pgopts : "", socket_string);
     256             : 
     257             :     /*
     258             :      * Don't throw an error right away, let connecting throw the error because
     259             :      * it might supply a reason for the failure.
     260             :      */
     261           8 :     pg_ctl_return = exec_prog(SERVER_START_LOG_FILE,
     262             :     /* pass both file names if they differ */
     263             :                               (strcmp(SERVER_LOG_FILE,
     264             :                                       SERVER_START_LOG_FILE) != 0) ?
     265             :                               SERVER_LOG_FILE : NULL,
     266             :                               report_and_exit_on_error, false,
     267             :                               "%s", cmd);
     268             : 
     269             :     /* Did it fail and we are just testing if the server could be started? */
     270           8 :     if (!pg_ctl_return && !report_and_exit_on_error)
     271           0 :         return false;
     272             : 
     273             :     /*
     274             :      * We set this here to make sure atexit() shuts down the server, but only
     275             :      * if we started the server successfully.  We do it before checking for
     276             :      * connectivity in case the server started but there is a connectivity
     277             :      * failure.  If pg_ctl did not return success, we will exit below.
     278             :      *
     279             :      * Pre-9.1 servers do not have PQping(), so we could be leaving the server
     280             :      * running if authentication was misconfigured, so someday we might went
     281             :      * to be more aggressive about doing server shutdowns even if pg_ctl
     282             :      * fails, but now (2013-08-14) it seems prudent to be cautious.  We don't
     283             :      * want to shutdown a server that might have been accidentally started
     284             :      * during the upgrade.
     285             :      */
     286           8 :     if (pg_ctl_return)
     287           8 :         os_info.running_cluster = cluster;
     288             : 
     289             :     /*
     290             :      * pg_ctl -w might have failed because the server couldn't be started, or
     291             :      * there might have been a connection problem in _checking_ if the server
     292             :      * has started.  Therefore, even if pg_ctl failed, we continue and test
     293             :      * for connectivity in case we get a connection reason for the failure.
     294             :      */
     295          16 :     if ((conn = get_db_conn(cluster, "template1")) == NULL ||
     296           8 :         PQstatus(conn) != CONNECTION_OK)
     297             :     {
     298           0 :         pg_log(PG_REPORT, "\n%s", PQerrorMessage(conn));
     299           0 :         if (conn)
     300           0 :             PQfinish(conn);
     301           0 :         if (cluster == &old_cluster)
     302           0 :             pg_fatal("could not connect to source postmaster started with the command:\n"
     303             :                      "%s\n",
     304             :                      cmd);
     305             :         else
     306           0 :             pg_fatal("could not connect to target postmaster started with the command:\n"
     307             :                      "%s\n",
     308             :                      cmd);
     309             :     }
     310           8 :     PQfinish(conn);
     311             : 
     312             :     /*
     313             :      * If pg_ctl failed, and the connection didn't fail, and
     314             :      * report_and_exit_on_error is enabled, fail now.  This could happen if
     315             :      * the server was already running.
     316             :      */
     317           8 :     if (!pg_ctl_return)
     318             :     {
     319           0 :         if (cluster == &old_cluster)
     320           0 :             pg_fatal("pg_ctl failed to start the source server, or connection failed\n");
     321             :         else
     322           0 :             pg_fatal("pg_ctl failed to start the target server, or connection failed\n");
     323             :     }
     324             : 
     325           8 :     return true;
     326             : }
     327             : 
     328             : 
     329             : void
     330          10 : stop_postmaster(bool in_atexit)
     331             : {
     332             :     ClusterInfo *cluster;
     333             : 
     334          10 :     if (os_info.running_cluster == &old_cluster)
     335           2 :         cluster = &old_cluster;
     336           8 :     else if (os_info.running_cluster == &new_cluster)
     337           6 :         cluster = &new_cluster;
     338             :     else
     339           2 :         return;                 /* no cluster running */
     340             : 
     341          16 :     exec_prog(SERVER_STOP_LOG_FILE, NULL, !in_atexit, !in_atexit,
     342             :               "\"%s/pg_ctl\" -w -D \"%s\" -o \"%s\" %s stop",
     343             :               cluster->bindir, cluster->pgconfig,
     344           8 :               cluster->pgopts ? cluster->pgopts : "",
     345           8 :               in_atexit ? "-m fast" : "-m smart");
     346             : 
     347           8 :     os_info.running_cluster = NULL;
     348             : }
     349             : 
     350             : 
     351             : /*
     352             :  * check_pghost_envvar()
     353             :  *
     354             :  * Tests that PGHOST does not point to a non-local server
     355             :  */
     356             : void
     357           2 : check_pghost_envvar(void)
     358             : {
     359             :     PQconninfoOption *option;
     360             :     PQconninfoOption *start;
     361             : 
     362             :     /* Get valid libpq env vars from the PQconndefaults function */
     363             : 
     364           2 :     start = PQconndefaults();
     365             : 
     366           2 :     if (!start)
     367           0 :         pg_fatal("out of memory\n");
     368             : 
     369          74 :     for (option = start; option->keyword != NULL; option++)
     370             :     {
     371          72 :         if (option->envvar && (strcmp(option->envvar, "PGHOST") == 0 ||
     372          54 :                                strcmp(option->envvar, "PGHOSTADDR") == 0))
     373             :         {
     374           4 :             const char *value = getenv(option->envvar);
     375             : 
     376           4 :             if (value && strlen(value) > 0 &&
     377             :             /* check for 'local' host values */
     378           2 :                 (strcmp(value, "localhost") != 0 && strcmp(value, "127.0.0.1") != 0 &&
     379           2 :                  strcmp(value, "::1") != 0 && value[0] != '/'))
     380           0 :                 pg_fatal("libpq environment variable %s has a non-local server value: %s\n",
     381             :                          option->envvar, value);
     382             :         }
     383             :     }
     384             : 
     385             :     /* Free the memory that libpq allocated on our behalf */
     386           2 :     PQconninfoFree(start);
     387           2 : }

Generated by: LCOV version 1.14