LCOV - code coverage report
Current view: top level - src/bin/pg_upgrade - server.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 79.8 % 114 91
Test Date: 2026-03-03 13:15:30 Functions: 100.0 % 8 8
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*
       2              :  *  server.c
       3              :  *
       4              :  *  database server functions
       5              :  *
       6              :  *  Copyright (c) 2010-2026, 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          289 : connectToServer(ClusterInfo *cluster, const char *db_name)
      29              : {
      30          289 :     PGconn     *conn = get_db_conn(cluster, db_name);
      31              : 
      32          289 :     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          289 :     PQclear(executeQueryOrDie(conn, ALWAYS_SECURE_SEARCH_PATH_SQL));
      44              : 
      45          289 :     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          340 : 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          340 :     initPQExpBuffer(&conn_opts);
      64          340 :     appendPQExpBufferStr(&conn_opts, "dbname=");
      65          340 :     appendConnStrVal(&conn_opts, db_name);
      66          340 :     appendPQExpBufferStr(&conn_opts, " user=");
      67          340 :     appendConnStrVal(&conn_opts, os_info.user);
      68          340 :     appendPQExpBuffer(&conn_opts, " port=%d", cluster->port);
      69          340 :     if (cluster->sockdir)
      70              :     {
      71          340 :         appendPQExpBufferStr(&conn_opts, " host=");
      72          340 :         appendConnStrVal(&conn_opts, cluster->sockdir);
      73              :     }
      74          340 :     if (!protocol_negotiation_supported(cluster))
      75            0 :         appendPQExpBufferStr(&conn_opts, " max_protocol_version=3.0");
      76              : 
      77          340 :     conn = PQconnectdb(conn_opts.data);
      78          340 :     termPQExpBuffer(&conn_opts);
      79          340 :     return conn;
      80              : }
      81              : 
      82              : 
      83              : /*
      84              :  * cluster_conn_opts()
      85              :  *
      86              :  * Return standard command-line options for connecting to this cluster when
      87              :  * using psql, pg_dump, etc.  Ideally this would match what get_db_conn()
      88              :  * sets, but the utilities we need aren't very consistent about the treatment
      89              :  * of database name options, so we leave that out.
      90              :  *
      91              :  * Result is valid until the next call to this function.
      92              :  */
      93              : char *
      94          104 : cluster_conn_opts(ClusterInfo *cluster)
      95              : {
      96              :     static PQExpBuffer buf;
      97              : 
      98          104 :     if (buf == NULL)
      99           11 :         buf = createPQExpBuffer();
     100              :     else
     101           93 :         resetPQExpBuffer(buf);
     102              : 
     103          104 :     if (cluster->sockdir)
     104              :     {
     105          104 :         appendPQExpBufferStr(buf, "--host ");
     106          104 :         appendShellString(buf, cluster->sockdir);
     107          104 :         appendPQExpBufferChar(buf, ' ');
     108              :     }
     109          104 :     appendPQExpBuffer(buf, "--port %d --username ", cluster->port);
     110          104 :     appendShellString(buf, os_info.user);
     111              : 
     112          104 :     return buf->data;
     113              : }
     114              : 
     115              : 
     116              : /*
     117              :  * executeQueryOrDie()
     118              :  *
     119              :  *  Formats a query string from the given arguments and executes the
     120              :  *  resulting query.  If the query fails, this function logs an error
     121              :  *  message and calls exit() to kill the program.
     122              :  */
     123              : PGresult *
     124          660 : executeQueryOrDie(PGconn *conn, const char *fmt,...)
     125              : {
     126              :     static char query[QUERY_ALLOC];
     127              :     va_list     args;
     128              :     PGresult   *result;
     129              :     ExecStatusType status;
     130              : 
     131          660 :     va_start(args, fmt);
     132          660 :     vsnprintf(query, sizeof(query), fmt, args);
     133          660 :     va_end(args);
     134              : 
     135          660 :     pg_log(PG_VERBOSE, "executing: %s", query);
     136          660 :     result = PQexec(conn, query);
     137          660 :     status = PQresultStatus(result);
     138              : 
     139          660 :     if ((status != PGRES_TUPLES_OK) && (status != PGRES_COMMAND_OK))
     140              :     {
     141            0 :         pg_log(PG_REPORT, "SQL command failed\n%s\n%s", query,
     142              :                PQerrorMessage(conn));
     143            0 :         PQclear(result);
     144            0 :         PQfinish(conn);
     145            0 :         printf(_("Failure, exiting\n"));
     146            0 :         exit(1);
     147              :     }
     148              :     else
     149          660 :         return result;
     150              : }
     151              : 
     152              : 
     153              : static void
     154           17 : stop_postmaster_atexit(void)
     155              : {
     156           17 :     stop_postmaster(true);
     157           17 : }
     158              : 
     159              : 
     160              : bool
     161           51 : start_postmaster(ClusterInfo *cluster, bool report_and_exit_on_error)
     162              : {
     163              :     char        cmd[MAXPGPATH * 4 + 1000];
     164              :     PGconn     *conn;
     165           51 :     bool        pg_ctl_return = false;
     166              :     char        socket_string[MAXPGPATH + 200];
     167              :     PQExpBufferData pgoptions;
     168              : 
     169              :     static bool exit_hook_registered = false;
     170              : 
     171           51 :     if (!exit_hook_registered)
     172              :     {
     173           17 :         atexit(stop_postmaster_atexit);
     174           17 :         exit_hook_registered = true;
     175              :     }
     176              : 
     177           51 :     socket_string[0] = '\0';
     178              : 
     179              : #if !defined(WIN32)
     180              :     /* prevent TCP/IP connections, restrict socket access */
     181           51 :     strcat(socket_string,
     182              :            " -c listen_addresses='' -c unix_socket_permissions=0700");
     183              : 
     184              :     /* Have a sockdir?  Tell the postmaster. */
     185           51 :     if (cluster->sockdir)
     186           51 :         snprintf(socket_string + strlen(socket_string),
     187           51 :                  sizeof(socket_string) - strlen(socket_string),
     188              :                  " -c %s='%s'",
     189           51 :                  (GET_MAJOR_VERSION(cluster->major_version) <= 902) ?
     190              :                  "unix_socket_directory" : "unix_socket_directories",
     191              :                  cluster->sockdir);
     192              : #endif
     193              : 
     194           51 :     initPQExpBuffer(&pgoptions);
     195              : 
     196              :     /*
     197              :      * Construct a parameter string which is passed to the server process.
     198              :      *
     199              :      * Turn off durability requirements to improve object creation speed, and
     200              :      * we only modify the new cluster, so only use it there.  If there is a
     201              :      * crash, the new cluster has to be recreated anyway.  fsync=off is a big
     202              :      * win on ext4.
     203              :      */
     204           51 :     if (cluster == &new_cluster)
     205           34 :         appendPQExpBufferStr(&pgoptions, " -c synchronous_commit=off -c fsync=off -c full_page_writes=off");
     206              : 
     207              :     /*
     208              :      * Use -b to disable autovacuum and logical replication launcher
     209              :      * (effective in PG17 or later for the latter).
     210              :      */
     211           51 :     snprintf(cmd, sizeof(cmd),
     212              :              "\"%s/pg_ctl\" -w -l \"%s/%s\" -D \"%s\" -o \"-p %d -b%s %s%s\" start",
     213              :              cluster->bindir,
     214              :              log_opts.logdir,
     215           51 :              SERVER_LOG_FILE, cluster->pgconfig, cluster->port,
     216              :              pgoptions.data,
     217           51 :              cluster->pgopts ? cluster->pgopts : "", socket_string);
     218              : 
     219           51 :     termPQExpBuffer(&pgoptions);
     220              : 
     221              :     /*
     222              :      * Don't throw an error right away, let connecting throw the error because
     223              :      * it might supply a reason for the failure.
     224              :      */
     225           51 :     pg_ctl_return = exec_prog(SERVER_START_LOG_FILE,
     226              :     /* pass both file names if they differ */
     227              :                               (strcmp(SERVER_LOG_FILE,
     228              :                                       SERVER_START_LOG_FILE) != 0) ?
     229              :                               SERVER_LOG_FILE : NULL,
     230              :                               report_and_exit_on_error, false,
     231              :                               "%s", cmd);
     232              : 
     233              :     /* Did it fail and we are just testing if the server could be started? */
     234           51 :     if (!pg_ctl_return && !report_and_exit_on_error)
     235            0 :         return false;
     236              : 
     237              :     /*
     238              :      * We set this here to make sure atexit() shuts down the server, but only
     239              :      * if we started the server successfully.  We do it before checking for
     240              :      * connectivity in case the server started but there is a connectivity
     241              :      * failure.  If pg_ctl did not return success, we will exit below.
     242              :      *
     243              :      * Pre-9.1 servers do not have PQping(), so we could be leaving the server
     244              :      * running if authentication was misconfigured, so someday we might went
     245              :      * to be more aggressive about doing server shutdowns even if pg_ctl
     246              :      * fails, but now (2013-08-14) it seems prudent to be cautious.  We don't
     247              :      * want to shutdown a server that might have been accidentally started
     248              :      * during the upgrade.
     249              :      */
     250           51 :     if (pg_ctl_return)
     251           51 :         os_info.running_cluster = cluster;
     252              : 
     253              :     /*
     254              :      * pg_ctl -w might have failed because the server couldn't be started, or
     255              :      * there might have been a connection problem in _checking_ if the server
     256              :      * has started.  Therefore, even if pg_ctl failed, we continue and test
     257              :      * for connectivity in case we get a connection reason for the failure.
     258              :      */
     259          102 :     if ((conn = get_db_conn(cluster, "template1")) == NULL ||
     260           51 :         PQstatus(conn) != CONNECTION_OK)
     261              :     {
     262            0 :         pg_log(PG_REPORT, "\n%s", PQerrorMessage(conn));
     263            0 :         if (conn)
     264            0 :             PQfinish(conn);
     265            0 :         if (cluster == &old_cluster)
     266            0 :             pg_fatal("could not connect to source postmaster started with the command:\n"
     267              :                      "%s",
     268              :                      cmd);
     269              :         else
     270            0 :             pg_fatal("could not connect to target postmaster started with the command:\n"
     271              :                      "%s",
     272              :                      cmd);
     273              :     }
     274           51 :     PQfinish(conn);
     275              : 
     276              :     /*
     277              :      * If pg_ctl failed, and the connection didn't fail, and
     278              :      * report_and_exit_on_error is enabled, fail now.  This could happen if
     279              :      * the server was already running.
     280              :      */
     281           51 :     if (!pg_ctl_return)
     282              :     {
     283            0 :         if (cluster == &old_cluster)
     284            0 :             pg_fatal("pg_ctl failed to start the source server, or connection failed");
     285              :         else
     286            0 :             pg_fatal("pg_ctl failed to start the target server, or connection failed");
     287              :     }
     288              : 
     289           51 :     return true;
     290              : }
     291              : 
     292              : 
     293              : void
     294           61 : stop_postmaster(bool in_atexit)
     295              : {
     296              :     ClusterInfo *cluster;
     297              : 
     298           61 :     if (os_info.running_cluster == &old_cluster)
     299           17 :         cluster = &old_cluster;
     300           44 :     else if (os_info.running_cluster == &new_cluster)
     301           34 :         cluster = &new_cluster;
     302              :     else
     303           10 :         return;                 /* no cluster running */
     304              : 
     305          102 :     exec_prog(SERVER_STOP_LOG_FILE, NULL, !in_atexit, !in_atexit,
     306              :               "\"%s/pg_ctl\" -w -D \"%s\" -o \"%s\" %s stop",
     307              :               cluster->bindir, cluster->pgconfig,
     308           51 :               cluster->pgopts ? cluster->pgopts : "",
     309           51 :               in_atexit ? "-m fast" : "-m smart");
     310              : 
     311           51 :     os_info.running_cluster = NULL;
     312              : }
     313              : 
     314              : 
     315              : /*
     316              :  * check_pghost_envvar()
     317              :  *
     318              :  * Tests that PGHOST does not point to a non-local server
     319              :  */
     320              : void
     321           19 : check_pghost_envvar(void)
     322              : {
     323              :     PQconninfoOption *option;
     324              :     PQconninfoOption *start;
     325              : 
     326              :     /* Get valid libpq env vars from the PQconndefaults function */
     327              : 
     328           19 :     start = PQconndefaults();
     329              : 
     330           19 :     if (!start)
     331            0 :         pg_fatal("out of memory");
     332              : 
     333          988 :     for (option = start; option->keyword != NULL; option++)
     334              :     {
     335          969 :         if (option->envvar && (strcmp(option->envvar, "PGHOST") == 0 ||
     336          665 :                                strcmp(option->envvar, "PGHOSTADDR") == 0))
     337              :         {
     338           38 :             const char *value = getenv(option->envvar);
     339              : 
     340           38 :             if (value && strlen(value) > 0 &&
     341              :             /* check for 'local' host values */
     342           19 :                 (strcmp(value, "localhost") != 0 && strcmp(value, "127.0.0.1") != 0 &&
     343           19 :                  strcmp(value, "::1") != 0 && !is_unixsock_path(value)))
     344            0 :                 pg_fatal("libpq environment variable %s has a non-local server value: %s",
     345              :                          option->envvar, value);
     346              :         }
     347              :     }
     348              : 
     349              :     /* Free the memory that libpq allocated on our behalf */
     350           19 :     PQconninfoFree(start);
     351           19 : }
        

Generated by: LCOV version 2.0-1