| 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 | get_bin_version(ClusterInfo *cluster) |
| 33 | { |
| 34 | char cmd[MAXPGPATH], |
| 35 | cmd_output[MAX_STRING]; |
| 36 | FILE *output; |
| 37 | int v1 = 0, |
| 38 | v2 = 0; |
| 39 | |
| 40 | snprintf(cmd, sizeof(cmd), "\"%s/pg_ctl\" --version" , cluster->bindir); |
| 41 | |
| 42 | if ((output = popen(cmd, "r" )) == NULL || |
| 43 | fgets(cmd_output, sizeof(cmd_output), output) == NULL) |
| 44 | pg_fatal("could not get pg_ctl version data using %s: %s\n" , |
| 45 | cmd, strerror(errno)); |
| 46 | |
| 47 | pclose(output); |
| 48 | |
| 49 | if (sscanf(cmd_output, "%*s %*s %d.%d" , &v1, &v2) < 1) |
| 50 | pg_fatal("could not get pg_ctl version output from %s\n" , cmd); |
| 51 | |
| 52 | if (v1 < 10) |
| 53 | { |
| 54 | /* old style, e.g. 9.6.1 */ |
| 55 | cluster->bin_version = v1 * 10000 + v2 * 100; |
| 56 | } |
| 57 | else |
| 58 | { |
| 59 | /* new style, e.g. 10.1 */ |
| 60 | cluster->bin_version = v1 * 10000; |
| 61 | } |
| 62 | } |
| 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 | exec_prog(const char *log_file, const char *opt_log_file, |
| 81 | bool report_error, bool exit_on_error, const char *fmt,...) |
| 82 | { |
| 83 | 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 | written = 0; |
| 100 | va_start(ap, fmt); |
| 101 | written += vsnprintf(cmd + written, MAXCMDLEN - written, fmt, ap); |
| 102 | va_end(ap); |
| 103 | if (written >= MAXCMDLEN) |
| 104 | pg_fatal("command too long\n" ); |
| 105 | written += snprintf(cmd + written, MAXCMDLEN - written, |
| 106 | " >> \"%s\" 2>&1" , log_file); |
| 107 | if (written >= MAXCMDLEN) |
| 108 | pg_fatal("command too long\n" ); |
| 109 | |
| 110 | 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 | 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 | if (log == NULL) |
| 149 | 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 | 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 | fclose(log); |
| 168 | |
| 169 | #ifdef WIN32 |
| 170 | /* see comment above */ |
| 171 | if (mainThreadId == GetCurrentThreadId()) |
| 172 | #endif |
| 173 | result = system(cmd); |
| 174 | |
| 175 | if (result != 0 && report_error) |
| 176 | { |
| 177 | /* we might be in on a progress status line, so go to the next line */ |
| 178 | report_status(PG_REPORT, "\n*failure*" ); |
| 179 | fflush(stdout); |
| 180 | |
| 181 | pg_log(PG_VERBOSE, "There were problems executing \"%s\"\n" , cmd); |
| 182 | if (opt_log_file) |
| 183 | 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 | 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 | if ((log = fopen(log_file, "a" )) == NULL) |
| 204 | pg_fatal("could not write to log file \"%s\": %m\n" , log_file); |
| 205 | fprintf(log, "\n\n" ); |
| 206 | fclose(log); |
| 207 | #endif |
| 208 | |
| 209 | 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 | pid_lock_file_exists(const char *datadir) |
| 220 | { |
| 221 | char path[MAXPGPATH]; |
| 222 | int fd; |
| 223 | |
| 224 | snprintf(path, sizeof(path), "%s/postmaster.pid" , datadir); |
| 225 | |
| 226 | if ((fd = open(path, O_RDONLY, 0)) < 0) |
| 227 | { |
| 228 | /* ENOTDIR means we will throw a more useful error later */ |
| 229 | if (errno != ENOENT && errno != ENOTDIR) |
| 230 | pg_fatal("could not open file \"%s\" for reading: %s\n" , |
| 231 | path, strerror(errno)); |
| 232 | |
| 233 | return false; |
| 234 | } |
| 235 | |
| 236 | close(fd); |
| 237 | 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 | verify_directories(void) |
| 251 | { |
| 252 | #ifndef WIN32 |
| 253 | if (access("." , R_OK | W_OK | X_OK) != 0) |
| 254 | #else |
| 255 | if (win32_check_directory_write_permissions() != 0) |
| 256 | #endif |
| 257 | pg_fatal("You must have read and write access in the current directory.\n" ); |
| 258 | |
| 259 | check_bin_dir(&old_cluster); |
| 260 | check_data_dir(&old_cluster); |
| 261 | check_bin_dir(&new_cluster); |
| 262 | check_data_dir(&new_cluster); |
| 263 | } |
| 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 | check_single_dir(const char *pg_data, const char *subdir) |
| 300 | { |
| 301 | struct stat statBuf; |
| 302 | char subDirName[MAXPGPATH]; |
| 303 | |
| 304 | snprintf(subDirName, sizeof(subDirName), "%s%s%s" , pg_data, |
| 305 | /* Win32 can't stat() a directory with a trailing slash. */ |
| 306 | *subdir ? "/" : "" , |
| 307 | subdir); |
| 308 | |
| 309 | if (stat(subDirName, &statBuf) != 0) |
| 310 | report_status(PG_FATAL, "check for \"%s\" failed: %s\n" , |
| 311 | subDirName, strerror(errno)); |
| 312 | else if (!S_ISDIR(statBuf.st_mode)) |
| 313 | report_status(PG_FATAL, "\"%s\" is not a directory\n" , |
| 314 | subDirName); |
| 315 | } |
| 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 | check_data_dir(ClusterInfo *cluster) |
| 329 | { |
| 330 | const char *pg_data = cluster->pgdata; |
| 331 | |
| 332 | /* get the cluster version */ |
| 333 | cluster->major_version = get_major_server_version(cluster); |
| 334 | |
| 335 | check_single_dir(pg_data, "" ); |
| 336 | check_single_dir(pg_data, "base" ); |
| 337 | check_single_dir(pg_data, "global" ); |
| 338 | check_single_dir(pg_data, "pg_multixact" ); |
| 339 | check_single_dir(pg_data, "pg_subtrans" ); |
| 340 | check_single_dir(pg_data, "pg_tblspc" ); |
| 341 | check_single_dir(pg_data, "pg_twophase" ); |
| 342 | |
| 343 | /* pg_xlog has been renamed to pg_wal in v10 */ |
| 344 | if (GET_MAJOR_VERSION(cluster->major_version) < 1000) |
| 345 | check_single_dir(pg_data, "pg_xlog" ); |
| 346 | else |
| 347 | check_single_dir(pg_data, "pg_wal" ); |
| 348 | |
| 349 | /* pg_clog has been renamed to pg_xact in v10 */ |
| 350 | if (GET_MAJOR_VERSION(cluster->major_version) < 1000) |
| 351 | check_single_dir(pg_data, "pg_clog" ); |
| 352 | else |
| 353 | check_single_dir(pg_data, "pg_xact" ); |
| 354 | } |
| 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 | check_bin_dir(ClusterInfo *cluster) |
| 367 | { |
| 368 | struct stat statBuf; |
| 369 | |
| 370 | /* check bindir */ |
| 371 | if (stat(cluster->bindir, &statBuf) != 0) |
| 372 | report_status(PG_FATAL, "check for \"%s\" failed: %s\n" , |
| 373 | cluster->bindir, strerror(errno)); |
| 374 | else if (!S_ISDIR(statBuf.st_mode)) |
| 375 | report_status(PG_FATAL, "\"%s\" is not a directory\n" , |
| 376 | cluster->bindir); |
| 377 | |
| 378 | validate_exec(cluster->bindir, "postgres" ); |
| 379 | validate_exec(cluster->bindir, "pg_ctl" ); |
| 380 | |
| 381 | /* |
| 382 | * Fetch the binary version after checking for the existence of pg_ctl. |
| 383 | * This way we report a useful error if the pg_ctl binary used for version |
| 384 | * fetching is missing/broken. |
| 385 | */ |
| 386 | get_bin_version(cluster); |
| 387 | |
| 388 | /* pg_resetxlog has been renamed to pg_resetwal in version 10 */ |
| 389 | if (GET_MAJOR_VERSION(cluster->bin_version) < 1000) |
| 390 | validate_exec(cluster->bindir, "pg_resetxlog" ); |
| 391 | else |
| 392 | validate_exec(cluster->bindir, "pg_resetwal" ); |
| 393 | if (cluster == &new_cluster) |
| 394 | { |
| 395 | /* these are only needed in the new cluster */ |
| 396 | validate_exec(cluster->bindir, "psql" ); |
| 397 | validate_exec(cluster->bindir, "pg_dump" ); |
| 398 | validate_exec(cluster->bindir, "pg_dumpall" ); |
| 399 | } |
| 400 | } |
| 401 | |
| 402 | |
| 403 | /* |
| 404 | * validate_exec() |
| 405 | * |
| 406 | * validate "path" as an executable file |
| 407 | */ |
| 408 | static void |
| 409 | validate_exec(const char *dir, const char *cmdName) |
| 410 | { |
| 411 | char path[MAXPGPATH]; |
| 412 | struct stat buf; |
| 413 | |
| 414 | snprintf(path, sizeof(path), "%s/%s" , dir, cmdName); |
| 415 | |
| 416 | #ifdef WIN32 |
| 417 | /* Windows requires a .exe suffix for stat() */ |
| 418 | if (strlen(path) <= strlen(EXE_EXT) || |
| 419 | pg_strcasecmp(path + strlen(path) - strlen(EXE_EXT), EXE_EXT) != 0) |
| 420 | strlcat(path, EXE_EXT, sizeof(path)); |
| 421 | #endif |
| 422 | |
| 423 | /* |
| 424 | * Ensure that the file exists and is a regular file. |
| 425 | */ |
| 426 | if (stat(path, &buf) < 0) |
| 427 | pg_fatal("check for \"%s\" failed: %s\n" , |
| 428 | path, strerror(errno)); |
| 429 | else if (!S_ISREG(buf.st_mode)) |
| 430 | pg_fatal("check for \"%s\" failed: not a regular file\n" , |
| 431 | path); |
| 432 | |
| 433 | /* |
| 434 | * Ensure that the file is both executable and readable (required for |
| 435 | * dynamic loading). |
| 436 | */ |
| 437 | #ifndef WIN32 |
| 438 | if (access(path, R_OK) != 0) |
| 439 | #else |
| 440 | if ((buf.st_mode & S_IRUSR) == 0) |
| 441 | #endif |
| 442 | pg_fatal("check for \"%s\" failed: cannot read file (permission denied)\n" , |
| 443 | path); |
| 444 | |
| 445 | #ifndef WIN32 |
| 446 | if (access(path, X_OK) != 0) |
| 447 | #else |
| 448 | if ((buf.st_mode & S_IXUSR) == 0) |
| 449 | #endif |
| 450 | pg_fatal("check for \"%s\" failed: cannot execute (permission denied)\n" , |
| 451 | path); |
| 452 | } |
| 453 | |