LCOV - code coverage report
Current view: top level - src/bin/pg_upgrade - exec.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 0.0 % 159 0
Test Date: 2026-01-26 10:56:24 Functions: 0.0 % 8 0
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*
       2              :  *      exec.c
       3              :  *
       4              :  *      execution functions
       5              :  *
       6              :  *      Copyright (c) 2010-2026, PostgreSQL Global Development Group
       7              :  *      src/bin/pg_upgrade/exec.c
       8              :  */
       9              : 
      10              : #include "postgres_fe.h"
      11              : 
      12              : #include <fcntl.h>
      13              : 
      14              : #include "common/string.h"
      15              : #include "fe_utils/version.h"
      16              : #include "pg_upgrade.h"
      17              : 
      18              : static void check_data_dir(ClusterInfo *cluster);
      19              : static void check_bin_dir(ClusterInfo *cluster, bool check_versions);
      20              : static void get_bin_version(ClusterInfo *cluster);
      21              : static void check_exec(const char *dir, const char *program, bool check_version);
      22              : 
      23              : #ifdef WIN32
      24              : static int      win32_check_directory_write_permissions(void);
      25              : #endif
      26              : 
      27              : 
      28              : /*
      29              :  * get_bin_version
      30              :  *
      31              :  *      Fetch major version of binaries for cluster.
      32              :  */
      33              : static void
      34            0 : get_bin_version(ClusterInfo *cluster)
      35              : {
      36            0 :         char            cmd[MAXPGPATH],
      37              :                                 cmd_output[MAX_STRING];
      38            0 :         FILE       *output;
      39            0 :         int                     rc;
      40            0 :         int                     v1 = 0,
      41            0 :                                 v2 = 0;
      42              : 
      43            0 :         snprintf(cmd, sizeof(cmd), "\"%s/pg_ctl\" --version", cluster->bindir);
      44            0 :         fflush(NULL);
      45              : 
      46            0 :         if ((output = popen(cmd, "r")) == NULL ||
      47            0 :                 fgets(cmd_output, sizeof(cmd_output), output) == NULL)
      48            0 :                 pg_fatal("could not get pg_ctl version data using %s: %m", cmd);
      49              : 
      50            0 :         rc = pclose(output);
      51            0 :         if (rc != 0)
      52            0 :                 pg_fatal("could not get pg_ctl version data using %s: %s",
      53            0 :                                  cmd, wait_result_to_str(rc));
      54              : 
      55            0 :         if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
      56            0 :                 pg_fatal("could not get pg_ctl version output from %s", cmd);
      57              : 
      58            0 :         if (v1 < 10)
      59              :         {
      60              :                 /* old style, e.g. 9.6.1 */
      61            0 :                 cluster->bin_version = v1 * 10000 + v2 * 100;
      62            0 :         }
      63              :         else
      64              :         {
      65              :                 /* new style, e.g. 10.1 */
      66            0 :                 cluster->bin_version = v1 * 10000;
      67              :         }
      68            0 : }
      69              : 
      70              : 
      71              : /*
      72              :  * exec_prog()
      73              :  *              Execute an external program with stdout/stderr redirected, and report
      74              :  *              errors
      75              :  *
      76              :  * Formats a command from the given argument list, logs it to the log file,
      77              :  * and attempts to execute that command.  If the command executes
      78              :  * successfully, exec_prog() returns true.
      79              :  *
      80              :  * If the command fails, an error message is optionally written to the specified
      81              :  * log_file, and the program optionally exits.
      82              :  *
      83              :  * The code requires it be called first from the primary thread on Windows.
      84              :  */
      85              : bool
      86            0 : exec_prog(const char *log_filename, const char *opt_log_file,
      87              :                   bool report_error, bool exit_on_error, const char *fmt,...)
      88              : {
      89            0 :         int                     result = 0;
      90            0 :         int                     written;
      91            0 :         char            log_file[MAXPGPATH];
      92              : 
      93              : #define MAXCMDLEN (2 * MAXPGPATH)
      94            0 :         char            cmd[MAXCMDLEN];
      95            0 :         FILE       *log;
      96            0 :         va_list         ap;
      97              : 
      98              : #ifdef WIN32
      99              :         static DWORD mainThreadId = 0;
     100              : 
     101              :         /* We assume we are called from the primary thread first */
     102              :         if (mainThreadId == 0)
     103              :                 mainThreadId = GetCurrentThreadId();
     104              : #endif
     105              : 
     106            0 :         snprintf(log_file, MAXPGPATH, "%s/%s", log_opts.logdir, log_filename);
     107              : 
     108            0 :         written = 0;
     109            0 :         va_start(ap, fmt);
     110            0 :         written += vsnprintf(cmd + written, MAXCMDLEN - written, fmt, ap);
     111            0 :         va_end(ap);
     112            0 :         if (written >= MAXCMDLEN)
     113            0 :                 pg_fatal("command too long");
     114            0 :         written += snprintf(cmd + written, MAXCMDLEN - written,
     115            0 :                                                 " >> \"%s\" 2>&1", log_file);
     116            0 :         if (written >= MAXCMDLEN)
     117            0 :                 pg_fatal("command too long");
     118              : 
     119            0 :         pg_log(PG_VERBOSE, "%s", cmd);
     120              : 
     121              : #ifdef WIN32
     122              : 
     123              :         /*
     124              :          * For some reason, Windows issues a file-in-use error if we write data to
     125              :          * the log file from a non-primary thread just before we create a
     126              :          * subprocess that also writes to the same log file.  One fix is to sleep
     127              :          * for 100ms.  A cleaner fix is to write to the log file _after_ the
     128              :          * subprocess has completed, so we do this only when writing from a
     129              :          * non-primary thread.  fflush(), running system() twice, and pre-creating
     130              :          * the file do not see to help.
     131              :          */
     132              :         if (mainThreadId != GetCurrentThreadId())
     133              :         {
     134              :                 fflush(NULL);
     135              :                 result = system(cmd);
     136              :         }
     137              : #endif
     138              : 
     139            0 :         log = fopen(log_file, "a");
     140              : 
     141              : #ifdef WIN32
     142              :         {
     143              :                 /*
     144              :                  * "pg_ctl -w stop" might have reported that the server has stopped
     145              :                  * because the postmaster.pid file has been removed, but "pg_ctl -w
     146              :                  * start" might still be in the process of closing and might still be
     147              :                  * holding its stdout and -l log file descriptors open.  Therefore,
     148              :                  * try to open the log file a few more times.
     149              :                  */
     150              :                 int                     iter;
     151              : 
     152              :                 for (iter = 0; iter < 4 && log == NULL; iter++)
     153              :                 {
     154              :                         pg_usleep(1000000); /* 1 sec */
     155              :                         log = fopen(log_file, "a");
     156              :                 }
     157              :         }
     158              : #endif
     159              : 
     160            0 :         if (log == NULL)
     161            0 :                 pg_fatal("could not open log file \"%s\": %m", log_file);
     162              : 
     163              : #ifdef WIN32
     164              :         /* Are we printing "command:" before its output? */
     165              :         if (mainThreadId == GetCurrentThreadId())
     166              :                 fprintf(log, "\n\n");
     167              : #endif
     168            0 :         fprintf(log, "command: %s\n", cmd);
     169              : #ifdef WIN32
     170              :         /* Are we printing "command:" after its output? */
     171              :         if (mainThreadId != GetCurrentThreadId())
     172              :                 fprintf(log, "\n\n");
     173              : #endif
     174              : 
     175              :         /*
     176              :          * In Windows, we must close the log file at this point so the file is not
     177              :          * open while the command is running, or we get a share violation.
     178              :          */
     179            0 :         fclose(log);
     180              : 
     181              : #ifdef WIN32
     182              :         /* see comment above */
     183              :         if (mainThreadId == GetCurrentThreadId())
     184              : #endif
     185              :         {
     186            0 :                 fflush(NULL);
     187            0 :                 result = system(cmd);
     188              :         }
     189              : 
     190            0 :         if (result != 0 && report_error)
     191              :         {
     192              :                 /* we might be in on a progress status line, so go to the next line */
     193            0 :                 report_status(PG_REPORT, "\n*failure*");
     194            0 :                 fflush(stdout);
     195              : 
     196            0 :                 pg_log(PG_VERBOSE, "There were problems executing \"%s\"", cmd);
     197            0 :                 if (opt_log_file)
     198            0 :                         pg_log(exit_on_error ? PG_FATAL : PG_REPORT,
     199              :                                    "Consult the last few lines of \"%s\" or \"%s\" for\n"
     200              :                                    "the probable cause of the failure.",
     201            0 :                                    log_file, opt_log_file);
     202              :                 else
     203            0 :                         pg_log(exit_on_error ? PG_FATAL : PG_REPORT,
     204              :                                    "Consult the last few lines of \"%s\" for\n"
     205              :                                    "the probable cause of the failure.",
     206            0 :                                    log_file);
     207            0 :         }
     208              : 
     209              : #ifndef WIN32
     210              : 
     211              :         /*
     212              :          * We can't do this on Windows because it will keep the "pg_ctl start"
     213              :          * output filename open until the server stops, so we do the \n\n above on
     214              :          * that platform.  We use a unique filename for "pg_ctl start" that is
     215              :          * never reused while the server is running, so it works fine.  We could
     216              :          * log these commands to a third file, but that just adds complexity.
     217              :          */
     218            0 :         if ((log = fopen(log_file, "a")) == NULL)
     219            0 :                 pg_fatal("could not write to log file \"%s\": %m", log_file);
     220            0 :         fprintf(log, "\n\n");
     221            0 :         fclose(log);
     222              : #endif
     223              : 
     224            0 :         return result == 0;
     225            0 : }
     226              : 
     227              : 
     228              : /*
     229              :  * pid_lock_file_exists()
     230              :  *
     231              :  * Checks whether the postmaster.pid file exists.
     232              :  */
     233              : bool
     234            0 : pid_lock_file_exists(const char *datadir)
     235              : {
     236            0 :         char            path[MAXPGPATH];
     237            0 :         int                     fd;
     238              : 
     239            0 :         snprintf(path, sizeof(path), "%s/postmaster.pid", datadir);
     240              : 
     241            0 :         if ((fd = open(path, O_RDONLY, 0)) < 0)
     242              :         {
     243              :                 /* ENOTDIR means we will throw a more useful error later */
     244            0 :                 if (errno != ENOENT && errno != ENOTDIR)
     245            0 :                         pg_fatal("could not open file \"%s\" for reading: %m", path);
     246              : 
     247            0 :                 return false;
     248              :         }
     249              : 
     250            0 :         close(fd);
     251            0 :         return true;
     252            0 : }
     253              : 
     254              : 
     255              : /*
     256              :  * verify_directories()
     257              :  *
     258              :  * does all the hectic work of verifying directories and executables
     259              :  * of old and new server.
     260              :  *
     261              :  * NOTE: May update the values of all parameters
     262              :  */
     263              : void
     264            0 : verify_directories(void)
     265              : {
     266              : #ifndef WIN32
     267            0 :         if (access(".", R_OK | W_OK | X_OK) != 0)
     268              : #else
     269              :         if (win32_check_directory_write_permissions() != 0)
     270              : #endif
     271            0 :                 pg_fatal("You must have read and write access in the current directory.");
     272              : 
     273            0 :         check_bin_dir(&old_cluster, false);
     274            0 :         check_data_dir(&old_cluster);
     275            0 :         check_bin_dir(&new_cluster, true);
     276            0 :         check_data_dir(&new_cluster);
     277            0 : }
     278              : 
     279              : 
     280              : #ifdef WIN32
     281              : /*
     282              :  * win32_check_directory_write_permissions()
     283              :  *
     284              :  *      access() on WIN32 can't check directory permissions, so we have to
     285              :  *      optionally create, then delete a file to check.
     286              :  *              http://msdn.microsoft.com/en-us/library/1w06ktdy%28v=vs.80%29.aspx
     287              :  */
     288              : static int
     289              : win32_check_directory_write_permissions(void)
     290              : {
     291              :         int                     fd;
     292              : 
     293              :         /*
     294              :          * We open a file we would normally create anyway.  We do this even in
     295              :          * 'check' mode, which isn't ideal, but this is the best we can do.
     296              :          */
     297              :         if ((fd = open(GLOBALS_DUMP_FILE, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)) < 0)
     298              :                 return -1;
     299              :         close(fd);
     300              : 
     301              :         return unlink(GLOBALS_DUMP_FILE);
     302              : }
     303              : #endif
     304              : 
     305              : 
     306              : /*
     307              :  * check_single_dir()
     308              :  *
     309              :  *      Check for the presence of a single directory in PGDATA, and fail if
     310              :  * is it missing or not accessible.
     311              :  */
     312              : static void
     313            0 : check_single_dir(const char *pg_data, const char *subdir)
     314              : {
     315            0 :         struct stat statBuf;
     316            0 :         char            subDirName[MAXPGPATH];
     317              : 
     318            0 :         snprintf(subDirName, sizeof(subDirName), "%s%s%s", pg_data,
     319              :         /* Win32 can't stat() a directory with a trailing slash. */
     320            0 :                          *subdir ? "/" : "",
     321            0 :                          subdir);
     322              : 
     323            0 :         if (stat(subDirName, &statBuf) != 0)
     324            0 :                 report_status(PG_FATAL, "check for \"%s\" failed: %m",
     325            0 :                                           subDirName);
     326            0 :         else if (!S_ISDIR(statBuf.st_mode))
     327            0 :                 report_status(PG_FATAL, "\"%s\" is not a directory",
     328            0 :                                           subDirName);
     329            0 : }
     330              : 
     331              : 
     332              : /*
     333              :  * check_data_dir()
     334              :  *
     335              :  *      This function validates the given cluster directory - we search for a
     336              :  *      small set of subdirectories that we expect to find in a valid $PGDATA
     337              :  *      directory.  If any of the subdirectories are missing (or secured against
     338              :  *      us) we display an error message and exit()
     339              :  *
     340              :  */
     341              : static void
     342            0 : check_data_dir(ClusterInfo *cluster)
     343              : {
     344            0 :         const char *pg_data = cluster->pgdata;
     345              : 
     346              :         /* get the cluster version */
     347            0 :         cluster->major_version = get_pg_version(cluster->pgdata,
     348            0 :                                                                                         &cluster->major_version_str);
     349            0 :         check_single_dir(pg_data, "");
     350            0 :         check_single_dir(pg_data, "base");
     351            0 :         check_single_dir(pg_data, "global");
     352            0 :         check_single_dir(pg_data, "pg_multixact");
     353            0 :         check_single_dir(pg_data, "pg_subtrans");
     354            0 :         check_single_dir(pg_data, PG_TBLSPC_DIR);
     355            0 :         check_single_dir(pg_data, "pg_twophase");
     356              : 
     357              :         /* pg_xlog has been renamed to pg_wal in v10 */
     358            0 :         if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
     359            0 :                 check_single_dir(pg_data, "pg_xlog");
     360              :         else
     361            0 :                 check_single_dir(pg_data, "pg_wal");
     362              : 
     363              :         /* pg_clog has been renamed to pg_xact in v10 */
     364            0 :         if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
     365            0 :                 check_single_dir(pg_data, "pg_clog");
     366              :         else
     367            0 :                 check_single_dir(pg_data, "pg_xact");
     368            0 : }
     369              : 
     370              : 
     371              : /*
     372              :  * check_bin_dir()
     373              :  *
     374              :  *      This function searches for the executables that we expect to find
     375              :  *      in the binaries directory.  If we find that a required executable
     376              :  *      is missing (or secured against us), we display an error message and
     377              :  *      exit().
     378              :  *
     379              :  *      If check_versions is true, then the versions of the binaries are checked
     380              :  *      against the version of this pg_upgrade.  This is for checking the target
     381              :  *      bindir.
     382              :  */
     383              : static void
     384            0 : check_bin_dir(ClusterInfo *cluster, bool check_versions)
     385              : {
     386            0 :         struct stat statBuf;
     387              : 
     388              :         /* check bindir */
     389            0 :         if (stat(cluster->bindir, &statBuf) != 0)
     390            0 :                 report_status(PG_FATAL, "check for \"%s\" failed: %m",
     391            0 :                                           cluster->bindir);
     392            0 :         else if (!S_ISDIR(statBuf.st_mode))
     393            0 :                 report_status(PG_FATAL, "\"%s\" is not a directory",
     394            0 :                                           cluster->bindir);
     395              : 
     396            0 :         check_exec(cluster->bindir, "postgres", check_versions);
     397            0 :         check_exec(cluster->bindir, "pg_controldata", check_versions);
     398            0 :         check_exec(cluster->bindir, "pg_ctl", check_versions);
     399              : 
     400              :         /*
     401              :          * Fetch the binary version after checking for the existence of pg_ctl.
     402              :          * This way we report a useful error if the pg_ctl binary used for version
     403              :          * fetching is missing/broken.
     404              :          */
     405            0 :         get_bin_version(cluster);
     406              : 
     407              :         /* pg_resetxlog has been renamed to pg_resetwal in version 10 */
     408            0 :         if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
     409            0 :                 check_exec(cluster->bindir, "pg_resetxlog", check_versions);
     410              :         else
     411            0 :                 check_exec(cluster->bindir, "pg_resetwal", check_versions);
     412              : 
     413            0 :         if (cluster == &new_cluster)
     414              :         {
     415              :                 /*
     416              :                  * These binaries are only needed for the target version. pg_dump and
     417              :                  * pg_dumpall are used to dump the old cluster, but must be of the
     418              :                  * target version.
     419              :                  */
     420            0 :                 check_exec(cluster->bindir, "initdb", check_versions);
     421            0 :                 check_exec(cluster->bindir, "pg_dump", check_versions);
     422            0 :                 check_exec(cluster->bindir, "pg_dumpall", check_versions);
     423            0 :                 check_exec(cluster->bindir, "pg_restore", check_versions);
     424            0 :                 check_exec(cluster->bindir, "psql", check_versions);
     425            0 :                 check_exec(cluster->bindir, "vacuumdb", check_versions);
     426            0 :         }
     427            0 : }
     428              : 
     429              : static void
     430            0 : check_exec(const char *dir, const char *program, bool check_version)
     431              : {
     432            0 :         char            path[MAXPGPATH];
     433            0 :         char       *line;
     434            0 :         char            cmd[MAXPGPATH];
     435            0 :         char            versionstr[128];
     436              : 
     437            0 :         snprintf(path, sizeof(path), "%s/%s", dir, program);
     438              : 
     439            0 :         if (validate_exec(path) != 0)
     440            0 :                 pg_fatal("check for \"%s\" failed: %m", path);
     441              : 
     442            0 :         snprintf(cmd, sizeof(cmd), "\"%s\" -V", path);
     443              : 
     444            0 :         if ((line = pipe_read_line(cmd)) == NULL)
     445            0 :                 pg_fatal("check for \"%s\" failed: cannot execute",
     446            0 :                                  path);
     447              : 
     448            0 :         if (check_version)
     449              :         {
     450            0 :                 pg_strip_crlf(line);
     451              : 
     452            0 :                 snprintf(versionstr, sizeof(versionstr), "%s (PostgreSQL) " PG_VERSION, program);
     453              : 
     454            0 :                 if (strcmp(line, versionstr) != 0)
     455            0 :                         pg_fatal("check for \"%s\" failed: incorrect version: found \"%s\", expected \"%s\"",
     456            0 :                                          path, line, versionstr);
     457            0 :         }
     458              : 
     459            0 :         pg_free(line);
     460            0 : }
        

Generated by: LCOV version 2.3.2-1