LCOV - differential code coverage report
Current view: top level - src/bin/pg_upgrade - exec.c (source / functions) Coverage Total Hit UNC LBC UIC UBC GBC GIC GNC CBC EUB ECB DUB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 76.4 % 127 97 16 6 7 1 3 63 7 24 24 58 2 9
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 8 8 7 1 4 3
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  Line data    Source code
       1                 : /*
       2                 :  *  exec.c
       3                 :  *
       4                 :  *  execution functions
       5                 :  *
       6                 :  *  Copyright (c) 2010-2023, 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 "pg_upgrade.h"
      16                 : 
      17                 : static void check_data_dir(ClusterInfo *cluster);
      18                 : static void check_bin_dir(ClusterInfo *cluster, bool check_versions);
      19                 : static void get_bin_version(ClusterInfo *cluster);
      20                 : static void check_exec(const char *dir, const char *program, bool check_version);
      21                 : 
      22                 : #ifdef WIN32
      23                 : static int  win32_check_directory_write_permissions(void);
      24                 : #endif
      25                 : 
      26                 : 
      27                 : /*
      28                 :  * get_bin_version
      29                 :  *
      30                 :  *  Fetch major version of binaries for cluster.
      31                 :  */
      32                 : static void
      33 CBC           4 : get_bin_version(ClusterInfo *cluster)
      34                 : {
      35                 :     char        cmd[MAXPGPATH],
      36                 :                 cmd_output[MAX_STRING];
      37                 :     FILE       *output;
      38                 :     int         rc;
      39 GIC           4 :     int         v1 = 0,
      40 CBC           4 :                 v2 = 0;
      41 ECB             : 
      42 GIC           4 :     snprintf(cmd, sizeof(cmd), "\"%s/pg_ctl\" --version", cluster->bindir);
      43 GNC           4 :     fflush(NULL);
      44 ECB             : 
      45 CBC           8 :     if ((output = popen(cmd, "r")) == NULL ||
      46 GIC           4 :         fgets(cmd_output, sizeof(cmd_output), output) == NULL)
      47 UNC           0 :         pg_fatal("could not get pg_ctl version data using %s: %s",
      48 LBC           0 :                  cmd, strerror(errno));
      49 EUB             : 
      50 GNC           4 :     rc = pclose(output);
      51               4 :     if (rc != 0)
      52 UNC           0 :         pg_fatal("could not get pg_ctl version data using %s: %s",
      53                 :                  cmd, wait_result_to_str(rc));
      54                 : 
      55 CBC           4 :     if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
      56 UNC           0 :         pg_fatal("could not get pg_ctl version output from %s", cmd);
      57 EUB             : 
      58 GIC           4 :     if (v1 < 10)
      59                 :     {
      60 ECB             :         /* old style, e.g. 9.6.1 */
      61 UBC           0 :         cluster->bin_version = v1 * 10000 + v2 * 100;
      62                 :     }
      63 ECB             :     else
      64                 :     {
      65                 :         /* new style, e.g. 10.1 */
      66 GBC           4 :         cluster->bin_version = v1 * 10000;
      67                 :     }
      68 GIC           4 : }
      69                 : 
      70                 : 
      71 ECB             : /*
      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 GIC          38 : exec_prog(const char *log_filename, const char *opt_log_file,
      87                 :           bool report_error, bool exit_on_error, const char *fmt,...)
      88                 : {
      89              38 :     int         result = 0;
      90                 :     int         written;
      91 ECB             :     char        log_file[MAXPGPATH];
      92                 : 
      93                 : #define MAXCMDLEN (2 * MAXPGPATH)
      94                 :     char        cmd[MAXCMDLEN];
      95                 :     FILE       *log;
      96                 :     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 GIC          38 :     snprintf(log_file, MAXPGPATH, "%s/%s", log_opts.logdir, log_filename);
     107                 : 
     108              38 :     written = 0;
     109              38 :     va_start(ap, fmt);
     110              38 :     written += vsnprintf(cmd + written, MAXCMDLEN - written, fmt, ap);
     111 CBC          38 :     va_end(ap);
     112 GIC          38 :     if (written >= MAXCMDLEN)
     113 UNC           0 :         pg_fatal("command too long");
     114 CBC          38 :     written += snprintf(cmd + written, MAXCMDLEN - written,
     115 ECB             :                         " >> \"%s\" 2>&1", log_file);
     116 CBC          38 :     if (written >= MAXCMDLEN)
     117 UNC           0 :         pg_fatal("command too long");
     118 EUB             : 
     119 GNC          38 :     pg_log(PG_VERBOSE, "%s", cmd);
     120                 : 
     121 ECB             : #ifdef WIN32
     122 EUB             : 
     123                 :     /*
     124 ECB             :      * 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 GIC          38 :     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 ECB             :          * 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 GIC          38 :     if (log == NULL)
     161 UNC           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 CBC          38 :     fprintf(log, "command: %s\n", cmd);
     169 EUB             : #ifdef WIN32
     170                 :     /* Are we printing "command:" after its output? */
     171                 :     if (mainThreadId != GetCurrentThreadId())
     172                 :         fprintf(log, "\n\n");
     173                 : #endif
     174                 : 
     175                 :     /*
     176 ECB             :      * 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 GIC          38 :     fclose(log);
     180                 : 
     181                 : #ifdef WIN32
     182                 :     /* see comment above */
     183                 :     if (mainThreadId == GetCurrentThreadId())
     184                 : #endif
     185                 :     {
     186 GNC          38 :         fflush(NULL);
     187 GIC          38 :         result = system(cmd);
     188                 :     }
     189                 : 
     190 CBC          38 :     if (result != 0 && report_error)
     191                 :     {
     192                 :         /* we might be in on a progress status line, so go to the next line */
     193 UIC           0 :         report_status(PG_REPORT, "\n*failure*");
     194               0 :         fflush(stdout);
     195                 : 
     196 UNC           0 :         pg_log(PG_VERBOSE, "There were problems executing \"%s\"", cmd);
     197 LBC           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 ECB             :                    log_file, opt_log_file);
     202                 :         else
     203 UIC           0 :             pg_log(exit_on_error ? PG_FATAL : PG_REPORT,
     204 EUB             :                    "Consult the last few lines of \"%s\" for\n"
     205                 :                    "the probable cause of the failure.",
     206                 :                    log_file);
     207                 :     }
     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 GIC          38 :     if ((log = fopen(log_file, "a")) == NULL)
     219 UNC           0 :         pg_fatal("could not write to log file \"%s\": %m", log_file);
     220 GIC          38 :     fprintf(log, "\n\n");
     221              38 :     fclose(log);
     222                 : #endif
     223                 : 
     224              38 :     return result == 0;
     225                 : }
     226                 : 
     227                 : 
     228                 : /*
     229 ECB             :  * pid_lock_file_exists()
     230 EUB             :  *
     231 ECB             :  * Checks whether the postmaster.pid file exists.
     232                 :  */
     233                 : bool
     234 GIC           4 : pid_lock_file_exists(const char *datadir)
     235 ECB             : {
     236                 :     char        path[MAXPGPATH];
     237                 :     int         fd;
     238                 : 
     239 GIC           4 :     snprintf(path, sizeof(path), "%s/postmaster.pid", datadir);
     240                 : 
     241               4 :     if ((fd = open(path, O_RDONLY, 0)) < 0)
     242                 :     {
     243                 :         /* ENOTDIR means we will throw a more useful error later */
     244               4 :         if (errno != ENOENT && errno != ENOTDIR)
     245 UNC           0 :             pg_fatal("could not open file \"%s\" for reading: %s",
     246 UIC           0 :                      path, strerror(errno));
     247                 : 
     248 GIC           4 :         return false;
     249                 :     }
     250 ECB             : 
     251 UIC           0 :     close(fd);
     252 LBC           0 :     return true;
     253                 : }
     254                 : 
     255 ECB             : 
     256 EUB             : /*
     257                 :  * verify_directories()
     258                 :  *
     259 ECB             :  * does all the hectic work of verifying directories and executables
     260                 :  * of old and new server.
     261                 :  *
     262 EUB             :  * NOTE: May update the values of all parameters
     263                 :  */
     264                 : void
     265 GIC           3 : verify_directories(void)
     266                 : {
     267                 : #ifndef WIN32
     268               3 :     if (access(".", R_OK | W_OK | X_OK) != 0)
     269                 : #else
     270                 :     if (win32_check_directory_write_permissions() != 0)
     271                 : #endif
     272 UNC           0 :         pg_fatal("You must have read and write access in the current directory.");
     273                 : 
     274 GIC           3 :     check_bin_dir(&old_cluster, false);
     275               2 :     check_data_dir(&old_cluster);
     276 CBC           2 :     check_bin_dir(&new_cluster, true);
     277 GIC           2 :     check_data_dir(&new_cluster);
     278               2 : }
     279 ECB             : 
     280                 : 
     281                 : #ifdef WIN32
     282                 : /*
     283 EUB             :  * win32_check_directory_write_permissions()
     284                 :  *
     285 ECB             :  *  access() on WIN32 can't check directory permissions, so we have to
     286                 :  *  optionally create, then delete a file to check.
     287                 :  *      http://msdn.microsoft.com/en-us/library/1w06ktdy%28v=vs.80%29.aspx
     288                 :  */
     289                 : static int
     290                 : win32_check_directory_write_permissions(void)
     291                 : {
     292                 :     int         fd;
     293                 : 
     294                 :     /*
     295                 :      * We open a file we would normally create anyway.  We do this even in
     296                 :      * 'check' mode, which isn't ideal, but this is the best we can do.
     297                 :      */
     298                 :     if ((fd = open(GLOBALS_DUMP_FILE, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)) < 0)
     299                 :         return -1;
     300                 :     close(fd);
     301                 : 
     302                 :     return unlink(GLOBALS_DUMP_FILE);
     303                 : }
     304                 : #endif
     305                 : 
     306                 : 
     307                 : /*
     308                 :  * check_single_dir()
     309                 :  *
     310                 :  *  Check for the presence of a single directory in PGDATA, and fail if
     311                 :  * is it missing or not accessible.
     312                 :  */
     313                 : static void
     314 GIC          36 : check_single_dir(const char *pg_data, const char *subdir)
     315                 : {
     316                 :     struct stat statBuf;
     317                 :     char        subDirName[MAXPGPATH];
     318                 : 
     319              36 :     snprintf(subDirName, sizeof(subDirName), "%s%s%s", pg_data,
     320                 :     /* Win32 can't stat() a directory with a trailing slash. */
     321              36 :              *subdir ? "/" : "",
     322                 :              subdir);
     323                 : 
     324              36 :     if (stat(subDirName, &statBuf) != 0)
     325 UNC           0 :         report_status(PG_FATAL, "check for \"%s\" failed: %s",
     326 UIC           0 :                       subDirName, strerror(errno));
     327 GIC          36 :     else if (!S_ISDIR(statBuf.st_mode))
     328 UNC           0 :         report_status(PG_FATAL, "\"%s\" is not a directory",
     329                 :                       subDirName);
     330 CBC          36 : }
     331                 : 
     332 ECB             : 
     333                 : /*
     334                 :  * check_data_dir()
     335                 :  *
     336 EUB             :  *  This function validates the given cluster directory - we search for a
     337                 :  *  small set of subdirectories that we expect to find in a valid $PGDATA
     338 ECB             :  *  directory.  If any of the subdirectories are missing (or secured against
     339 EUB             :  *  us) we display an error message and exit()
     340                 :  *
     341 ECB             :  */
     342                 : static void
     343 GIC           4 : check_data_dir(ClusterInfo *cluster)
     344                 : {
     345               4 :     const char *pg_data = cluster->pgdata;
     346                 : 
     347                 :     /* get the cluster version */
     348               4 :     cluster->major_version = get_major_server_version(cluster);
     349                 : 
     350               4 :     check_single_dir(pg_data, "");
     351               4 :     check_single_dir(pg_data, "base");
     352               4 :     check_single_dir(pg_data, "global");
     353               4 :     check_single_dir(pg_data, "pg_multixact");
     354 CBC           4 :     check_single_dir(pg_data, "pg_subtrans");
     355 GIC           4 :     check_single_dir(pg_data, "pg_tblspc");
     356 CBC           4 :     check_single_dir(pg_data, "pg_twophase");
     357                 : 
     358                 :     /* pg_xlog has been renamed to pg_wal in v10 */
     359               4 :     if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
     360 UIC           0 :         check_single_dir(pg_data, "pg_xlog");
     361 ECB             :     else
     362 CBC           4 :         check_single_dir(pg_data, "pg_wal");
     363 ECB             : 
     364                 :     /* pg_clog has been renamed to pg_xact in v10 */
     365 CBC           4 :     if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
     366 LBC           0 :         check_single_dir(pg_data, "pg_clog");
     367 ECB             :     else
     368 GIC           4 :         check_single_dir(pg_data, "pg_xact");
     369               4 : }
     370 ECB             : 
     371 EUB             : 
     372                 : /*
     373 ECB             :  * check_bin_dir()
     374                 :  *
     375                 :  *  This function searches for the executables that we expect to find
     376                 :  *  in the binaries directory.  If we find that a required executable
     377 EUB             :  *  is missing (or secured against us), we display an error message and
     378                 :  *  exit().
     379 ECB             :  *
     380                 :  *  If check_versions is true, then the versions of the binaries are checked
     381                 :  *  against the version of this pg_upgrade.  This is for checking the target
     382                 :  *  bindir.
     383                 :  */
     384                 : static void
     385 GIC           5 : check_bin_dir(ClusterInfo *cluster, bool check_versions)
     386                 : {
     387                 :     struct stat statBuf;
     388                 : 
     389                 :     /* check bindir */
     390               5 :     if (stat(cluster->bindir, &statBuf) != 0)
     391 GNC           1 :         report_status(PG_FATAL, "check for \"%s\" failed: %s",
     392 GIC           1 :                       cluster->bindir, strerror(errno));
     393               4 :     else if (!S_ISDIR(statBuf.st_mode))
     394 UNC           0 :         report_status(PG_FATAL, "\"%s\" is not a directory",
     395                 :                       cluster->bindir);
     396 ECB             : 
     397 GIC           4 :     check_exec(cluster->bindir, "postgres", check_versions);
     398               4 :     check_exec(cluster->bindir, "pg_controldata", check_versions);
     399               4 :     check_exec(cluster->bindir, "pg_ctl", check_versions);
     400                 : 
     401 ECB             :     /*
     402                 :      * Fetch the binary version after checking for the existence of pg_ctl.
     403                 :      * This way we report a useful error if the pg_ctl binary used for version
     404                 :      * fetching is missing/broken.
     405 EUB             :      */
     406 GIC           4 :     get_bin_version(cluster);
     407                 : 
     408 ECB             :     /* pg_resetxlog has been renamed to pg_resetwal in version 10 */
     409 CBC           4 :     if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
     410 LBC           0 :         check_exec(cluster->bindir, "pg_resetxlog", check_versions);
     411                 :     else
     412 GIC           4 :         check_exec(cluster->bindir, "pg_resetwal", check_versions);
     413                 : 
     414               4 :     if (cluster == &new_cluster)
     415                 :     {
     416                 :         /*
     417 ECB             :          * These binaries are only needed for the target version. pg_dump and
     418                 :          * pg_dumpall are used to dump the old cluster, but must be of the
     419                 :          * target version.
     420                 :          */
     421 GBC           2 :         check_exec(cluster->bindir, "initdb", check_versions);
     422 GIC           2 :         check_exec(cluster->bindir, "pg_dump", check_versions);
     423 CBC           2 :         check_exec(cluster->bindir, "pg_dumpall", check_versions);
     424 GIC           2 :         check_exec(cluster->bindir, "pg_restore", check_versions);
     425 CBC           2 :         check_exec(cluster->bindir, "psql", check_versions);
     426 GIC           2 :         check_exec(cluster->bindir, "vacuumdb", check_versions);
     427                 :     }
     428               4 : }
     429                 : 
     430                 : static void
     431              28 : check_exec(const char *dir, const char *program, bool check_version)
     432 ECB             : {
     433                 :     char        path[MAXPGPATH];
     434                 :     char        line[MAXPGPATH];
     435                 :     char        cmd[MAXPGPATH];
     436                 :     char        versionstr[128];
     437                 : 
     438 CBC          28 :     snprintf(path, sizeof(path), "%s/%s", dir, program);
     439                 : 
     440 GNC          28 :     if (validate_exec(path) != 0)
     441 UNC           0 :         pg_fatal("check for \"%s\" failed: %m", path);
     442 ECB             : 
     443 GIC          28 :     snprintf(cmd, sizeof(cmd), "\"%s\" -V", path);
     444 ECB             : 
     445 GBC          28 :     if (!pipe_read_line(cmd, line, sizeof(line)))
     446 UNC           0 :         pg_fatal("check for \"%s\" failed: cannot execute",
     447 ECB             :                  path);
     448                 : 
     449 CBC          28 :     if (check_version)
     450 EUB             :     {
     451 GIC          20 :         pg_strip_crlf(line);
     452                 : 
     453 CBC          20 :         snprintf(versionstr, sizeof(versionstr), "%s (PostgreSQL) " PG_VERSION, program);
     454                 : 
     455              20 :         if (strcmp(line, versionstr) != 0)
     456 UNC           0 :             pg_fatal("check for \"%s\" failed: incorrect version: found \"%s\", expected \"%s\"",
     457 ECB             :                      path, line, versionstr);
     458                 :     }
     459 CBC          28 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a