LCOV - code coverage report
Current view: top level - src/bin/pg_upgrade - exec.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 88 121 72.7 %
Date: 2019-11-21 15:06:52 Functions: 8 8 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13