/*------------ MAIN ----------------------------------------*/ int main(int argc, char **argv) { int c; progname = get_progname(argv[0]); if (argc > 1) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { usage(); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) { puts("pg_standby (PostgreSQL) " PG_VERSION); exit(0); } } #ifndef WIN32 /* * You can send SIGUSR1 to trigger failover. * * Postmaster uses SIGQUIT to request immediate shutdown. The default * action is to core dump, but we don't want that, so trap it and commit * suicide without core dump. * * We used to use SIGINT and SIGQUIT to trigger failover, but that turned * out to be a bad idea because postmaster uses SIGQUIT to request * immediate shutdown. We still trap SIGINT, but that may change in a * future release. * * There's no way to trigger failover via signal on Windows. */ (void) pqsignal(SIGUSR1, sighandler); (void) pqsignal(SIGINT, sighandler); /* deprecated, use SIGUSR1 */ (void) pqsignal(SIGQUIT, sigquit_handler); #endif while ((c = getopt(argc, argv, "cdk:lr:s:t:w:")) != -1) { switch (c) { case 'c': /* Use copy */ restoreCommandType = RESTORE_COMMAND_COPY; break; case 'd': /* Debug mode */ debug = true; break; case 'k': /* keepfiles */ keepfiles = atoi(optarg); if (keepfiles < 0) { fprintf(stderr, "%s: -k keepfiles must be >= 0\n", progname); exit(2); } break; case 'l': /* Use link */ /* * Link feature disabled, possibly permanently. Linking causes * a problem after recovery ends that is not currently * resolved by PostgreSQL. 25 Jun 2009 */ #ifdef NOT_USED restoreCommandType = RESTORE_COMMAND_LINK; #endif break; case 'r': /* Retries */ maxretries = atoi(optarg); if (maxretries < 0) { fprintf(stderr, "%s: -r maxretries must be >= 0\n", progname); exit(2); } break; case 's': /* Sleep time */ sleeptime = atoi(optarg); if (sleeptime <= 0 || sleeptime > 60) { fprintf(stderr, "%s: -s sleeptime incorrectly set\n", progname); exit(2); } break; case 't': /* Trigger file */ triggerPath = strdup(optarg); break; case 'w': /* Max wait time */ maxwaittime = atoi(optarg); if (maxwaittime < 0) { fprintf(stderr, "%s: -w maxwaittime incorrectly set\n", progname); exit(2); } break; default: fprintf(stderr, "Try \"%s --help\" for more information.\n", progname); exit(2); break; } } /* * Parameter checking - after checking to see if trigger file present */ if (argc == 1) { fprintf(stderr, "%s: not enough command-line arguments\n", progname); exit(2); } /* * We will go to the archiveLocation to get nextWALFileName. * nextWALFileName may not exist yet, which would not be an error, so we * separate the archiveLocation and nextWALFileName so we can check * separately whether archiveLocation exists, if not that is an error */ if (optind < argc) { archiveLocation = argv[optind]; optind++; } else { fprintf(stderr, "%s: must specify archive location\n", progname); fprintf(stderr, "Try \"%s --help\" for more information.\n", progname); exit(2); } if (optind < argc) { nextWALFileName = argv[optind]; optind++; } else { fprintf(stderr, "%s: must specify WAL file name as second non-option argument (use \"%%f\")\n", progname); fprintf(stderr, "Try \"%s --help\" for more information.\n", progname); exit(2); } if (optind < argc) { xlogFilePath = argv[optind]; optind++; } else { fprintf(stderr, "%s: must specify xlog destination as third non-option argument (use \"%%p\")\n", progname); fprintf(stderr, "Try \"%s --help\" for more information.\n", progname); exit(2); } if (optind < argc) { restartWALFileName = argv[optind]; optind++; } CustomizableInitialize(); need_cleanup = SetWALFileNameForCleanup(); if (debug) { fprintf(stderr, "Trigger file: %s\n", triggerPath ? triggerPath : "<not set>"); fprintf(stderr, "Waiting for WAL file: %s\n", nextWALFileName); fprintf(stderr, "WAL file path: %s\n", WALFilePath); fprintf(stderr, "Restoring to: %s\n", xlogFilePath); fprintf(stderr, "Sleep interval: %d second%s\n", sleeptime, (sleeptime > 1 ? "s" : " ")); fprintf(stderr, "Max wait interval: %d %s\n", maxwaittime, (maxwaittime > 0 ? "seconds" : "forever")); fprintf(stderr, "Command for restore: %s\n", restoreCommand); fprintf(stderr, "Keep archive history: "); if (need_cleanup) fprintf(stderr, "%s and later\n", exclusiveCleanupFileName); else fprintf(stderr, "no cleanup required\n"); fflush(stderr); } /* * Check for initial history file: always the first file to be requested * It's OK if the file isn't there - all other files need to wait */ if (strlen(nextWALFileName) > 8 && strspn(nextWALFileName, "0123456789ABCDEF") == 8 && strcmp(nextWALFileName + strlen(nextWALFileName) - strlen(".history"), ".history") == 0) { nextWALFileType = XLOG_HISTORY; if (RestoreWALFileForRecovery()) exit(0); else { if (debug) { fprintf(stderr, "history file not found\n"); fflush(stderr); } exit(1); } } /* * Main wait loop */ for (;;) { /* Check for trigger file or signal first */ CheckForExternalTrigger(); #ifndef WIN32 if (signaled) { Failover = FastFailover; if (debug) { fprintf(stderr, "signaled to exit: fast failover\n"); fflush(stderr); } } #endif /* * Check for fast failover immediately, before checking if the * requested WAL file is available */ if (Failover == FastFailover) exit(1); if (CustomizableNextWALFileReady()) { /* * Once we have restored this file successfully we can remove some * prior WAL files. If this restore fails we musn't remove any * file because some of them will be requested again immediately * after the failed restore, or when we restart recovery. */ if (RestoreWALFileForRecovery()) { if (need_cleanup) CustomizableCleanupPriorWALFiles(); exit(0); } else { /* Something went wrong in copying the file */ exit(1); } } /* Check for smart failover if the next WAL file was not available */ if (Failover == SmartFailover) exit(1); if (sleeptime <= 60) pg_usleep(sleeptime * 1000000L); waittime += sleeptime; if (waittime >= maxwaittime && maxwaittime > 0) { Failover = FastFailover; if (debug) { fprintf(stderr, "Timed out after %d seconds: fast failover\n", waittime); fflush(stderr); } } if (debug) { fprintf(stderr, "WAL file not present yet."); if (triggerPath) fprintf(stderr, " Checking for trigger file..."); fprintf(stderr, "\n"); fflush(stderr); } } }
/*------------ MAIN ----------------------------------------*/ int main(int argc, char **argv) { int c; (void) signal(SIGINT, sighandler); (void) signal(SIGQUIT, sighandler); while ((c = getopt(argc, argv, "cdk:lr:s:t:w:")) != -1) { switch (c) { case 'c': /* Use copy */ restoreCommandType = RESTORE_COMMAND_COPY; break; case 'd': /* Debug mode */ debug = true; break; case 'k': /* keepfiles */ keepfiles = atoi(optarg); if (keepfiles <= 0) { fprintf(stderr, "usage: pg_standby -k keepfiles must be > 0\n"); usage(); exit(2); } break; case 'l': /* Use link */ restoreCommandType = RESTORE_COMMAND_LINK; break; case 'r': /* Retries */ maxretries = atoi(optarg); if (maxretries < 0) { fprintf(stderr, "usage: pg_standby -r maxretries must be > 0\n"); usage(); exit(2); } break; case 's': /* Sleep time */ sleeptime = atoi(optarg); if (sleeptime <= 0 || sleeptime > 60) { fprintf(stderr, "usage: pg_standby -s sleeptime incorrectly set\n"); usage(); exit(2); } break; case 't': /* Trigger file */ triggerPath = optarg; if (CheckForExternalTrigger()) exit(1); /* Normal exit, with non-zero */ break; case 'w': /* Max wait time */ maxwaittime = atoi(optarg); if (maxwaittime < 0) { fprintf(stderr, "usage: pg_standby -w maxwaittime incorrectly set\n"); usage(); exit(2); } break; default: usage(); exit(2); break; } } /* * Parameter checking - after checking to see if trigger file present */ if (argc == 1) { usage(); exit(2); } /* * We will go to the archiveLocation to get nextWALFileName. * nextWALFileName may not exist yet, which would not be an error, * so we separate the archiveLocation and nextWALFileName so we can check * separately whether archiveLocation exists, if not that is an error */ if (optind < argc) { archiveLocation = argv[optind]; optind++; } else { fprintf(stderr, "pg_standby: must specify archiveLocation\n"); usage(); exit(2); } if (optind < argc) { nextWALFileName = argv[optind]; optind++; } else { fprintf(stderr, "pg_standby: use %%f to specify nextWALFileName\n"); usage(); exit(2); } if (optind < argc) { xlogFilePath = argv[optind]; optind++; } else { fprintf(stderr, "pg_standby: use %%p to specify xlogFilePath\n"); usage(); exit(2); } CustomizableInitialize(); if (debug) { fprintf(stderr, "\nTrigger file : %s", triggerPath ? triggerPath : "<not set>"); fprintf(stderr, "\nWaiting for WAL file : %s", WALFilePath); fprintf(stderr, "\nWAL file path : %s", nextWALFileName); fprintf(stderr, "\nRestoring to... : %s", xlogFilePath); fprintf(stderr, "\nSleep interval : %d second%s", sleeptime, (sleeptime > 1 ? "s" : " ")); fprintf(stderr, "\nMax wait interval : %d %s", maxwaittime, (maxwaittime > 0 ? "seconds" : "forever")); fprintf(stderr, "\nCommand for restore : %s", restoreCommand); if (keepfiles > 0) fprintf(stderr, "\nNum archived files kept : last %d files", keepfiles); else fprintf(stderr, "\nNum archived files kept : all files"); fflush(stderr); } /* * Check for initial history file: always the first file to be requested * It's OK if the file isn't there - all other files need to wait */ if (strlen(nextWALFileName) > 8 && strspn(nextWALFileName, "0123456789ABCDEF") == 8 && strcmp(nextWALFileName + strlen(nextWALFileName) - strlen(".history"), ".history") == 0) { nextWALFileType = XLOG_HISTORY; if (RestoreWALFileForRecovery()) exit(0); else { if (debug) { fprintf(stderr, "history file not found\n"); fflush(stderr); } exit(1); } } /* * Main wait loop */ while (!CustomizableNextWALFileReady() && !triggered) { if (sleeptime <= 60) pg_usleep(sleeptime * 1000000L); if (signaled) { if (debug) { fprintf(stderr, "\nsignaled to exit\n"); fflush(stderr); } } else { if (debug) { fprintf(stderr, "\nWAL file not present yet."); if (triggerPath) fprintf(stderr, " Checking for trigger file..."); fflush(stderr); } waittime += sleeptime; if (!triggered && (CheckForExternalTrigger() || (waittime >= maxwaittime && maxwaittime > 0))) { triggered = true; if (debug && waittime >= maxwaittime && maxwaittime > 0) fprintf(stderr, "\nTimed out after %d seconds\n",waittime); } } } /* * Action on exit */ if (triggered) exit(1); /* Normal exit, with non-zero */ else { /* * Once we have restored this file successfully we * can remove some prior WAL files. * If this restore fails we musn't remove any * file because some of them will be requested again * immediately after the failed restore, or when * we restart recovery. */ if (RestoreWALFileForRecovery()) CustomizableCleanupPriorWALFiles(); exit(0); } }