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

Generated by: LCOV version 1.14