]> granicus.if.org Git - postgresql/blob - src/bin/pg_resetxlog/pg_resetxlog.c
Centralize getopt-related declarations in a new header file pg_getopt.h.
[postgresql] / src / bin / pg_resetxlog / pg_resetxlog.c
1 /*-------------------------------------------------------------------------
2  *
3  * pg_resetxlog.c
4  *        A utility to "zero out" the xlog when it's corrupt beyond recovery.
5  *        Can also rebuild pg_control if needed.
6  *
7  * The theory of operation is fairly simple:
8  *        1. Read the existing pg_control (which will include the last
9  *               checkpoint record).  If it is an old format then update to
10  *               current format.
11  *        2. If pg_control is corrupt, attempt to intuit reasonable values,
12  *               by scanning the old xlog if necessary.
13  *        3. Modify pg_control to reflect a "shutdown" state with a checkpoint
14  *               record at the start of xlog.
15  *        4. Flush the existing xlog files and write a new segment with
16  *               just a checkpoint record in it.  The new segment is positioned
17  *               just past the end of the old xlog, so that existing LSNs in
18  *               data pages will appear to be "in the past".
19  * This is all pretty straightforward except for the intuition part of
20  * step 2 ...
21  *
22  *
23  * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
24  * Portions Copyright (c) 1994, Regents of the University of California
25  *
26  * src/bin/pg_resetxlog/pg_resetxlog.c
27  *
28  *-------------------------------------------------------------------------
29  */
30
31 /*
32  * We have to use postgres.h not postgres_fe.h here, because there's so much
33  * backend-only stuff in the XLOG include files we need.  But we need a
34  * frontend-ish environment otherwise.  Hence this ugly hack.
35  */
36 #define FRONTEND 1
37
38 #include "postgres.h"
39
40 #include <dirent.h>
41 #include <fcntl.h>
42 #include <locale.h>
43 #include <sys/stat.h>
44 #include <sys/time.h>
45 #include <time.h>
46 #include <unistd.h>
47
48 #include "access/transam.h"
49 #include "access/tuptoaster.h"
50 #include "access/multixact.h"
51 #include "access/xlog_internal.h"
52 #include "catalog/catversion.h"
53 #include "catalog/pg_control.h"
54 #include "common/fe_memutils.h"
55 #include "pg_getopt.h"
56
57
58 static ControlFileData ControlFile;             /* pg_control values */
59 static XLogSegNo newXlogSegNo;  /* new XLOG segment # */
60 static bool guessed = false;    /* T if we had to guess at any values */
61 static const char *progname;
62 static uint32 set_xid_epoch = (uint32) -1;
63 static TransactionId set_xid = 0;
64 static Oid      set_oid = 0;
65 static MultiXactId set_mxid = 0;
66 static MultiXactOffset set_mxoff = (MultiXactOffset) -1;
67 static uint32 minXlogTli = 0;
68 static XLogSegNo minXlogSegNo = 0;
69
70 static bool ReadControlFile(void);
71 static void GuessControlValues(void);
72 static void PrintControlValues(bool guessed);
73 static void PrintNewControlValues(void);
74 static void RewriteControlFile(void);
75 static void FindEndOfXLOG(void);
76 static void KillExistingXLOG(void);
77 static void KillExistingArchiveStatus(void);
78 static void WriteEmptyXLOG(void);
79 static void usage(void);
80
81
82 int
83 main(int argc, char *argv[])
84 {
85         int                     c;
86         bool            force = false;
87         bool            noupdate = false;
88         MultiXactId set_oldestmxid = 0;
89         char       *endptr;
90         char       *endptr2;
91         char       *DataDir;
92         int                     fd;
93
94         set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_resetxlog"));
95
96         progname = get_progname(argv[0]);
97
98         if (argc > 1)
99         {
100                 if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
101                 {
102                         usage();
103                         exit(0);
104                 }
105                 if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
106                 {
107                         puts("pg_resetxlog (PostgreSQL) " PG_VERSION);
108                         exit(0);
109                 }
110         }
111
112
113         while ((c = getopt(argc, argv, "fl:m:no:O:x:e:")) != -1)
114         {
115                 switch (c)
116                 {
117                         case 'f':
118                                 force = true;
119                                 break;
120
121                         case 'n':
122                                 noupdate = true;
123                                 break;
124
125                         case 'e':
126                                 set_xid_epoch = strtoul(optarg, &endptr, 0);
127                                 if (endptr == optarg || *endptr != '\0')
128                                 {
129                                         fprintf(stderr, _("%s: invalid argument for option -e\n"), progname);
130                                         fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
131                                         exit(1);
132                                 }
133                                 if (set_xid_epoch == -1)
134                                 {
135                                         fprintf(stderr, _("%s: transaction ID epoch (-e) must not be -1\n"), progname);
136                                         exit(1);
137                                 }
138                                 break;
139
140                         case 'x':
141                                 set_xid = strtoul(optarg, &endptr, 0);
142                                 if (endptr == optarg || *endptr != '\0')
143                                 {
144                                         fprintf(stderr, _("%s: invalid argument for option -x\n"), progname);
145                                         fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
146                                         exit(1);
147                                 }
148                                 if (set_xid == 0)
149                                 {
150                                         fprintf(stderr, _("%s: transaction ID (-x) must not be 0\n"), progname);
151                                         exit(1);
152                                 }
153                                 break;
154
155                         case 'o':
156                                 set_oid = strtoul(optarg, &endptr, 0);
157                                 if (endptr == optarg || *endptr != '\0')
158                                 {
159                                         fprintf(stderr, _("%s: invalid argument for option -o\n"), progname);
160                                         fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
161                                         exit(1);
162                                 }
163                                 if (set_oid == 0)
164                                 {
165                                         fprintf(stderr, _("%s: OID (-o) must not be 0\n"), progname);
166                                         exit(1);
167                                 }
168                                 break;
169
170                         case 'm':
171                                 set_mxid = strtoul(optarg, &endptr, 0);
172                                 if (endptr == optarg || *endptr != ',')
173                                 {
174                                         fprintf(stderr, _("%s: invalid argument for option -m\n"), progname);
175                                         fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
176                                         exit(1);
177                                 }
178
179                                 set_oldestmxid = strtoul(endptr + 1, &endptr2, 0);
180                                 if (endptr2 == endptr + 1 || *endptr2 != '\0')
181                                 {
182                                         fprintf(stderr, _("%s: invalid argument for option -m\n"), progname);
183                                         fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
184                                         exit(1);
185                                 }
186                                 if (set_mxid == 0)
187                                 {
188                                         fprintf(stderr, _("%s: multitransaction ID (-m) must not be 0\n"), progname);
189                                         exit(1);
190                                 }
191
192                                 /*
193                                  * XXX It'd be nice to have more sanity checks here, e.g. so
194                                  * that oldest is not wrapped around w.r.t. nextMulti.
195                                  */
196                                 if (set_oldestmxid == 0)
197                                 {
198                                         fprintf(stderr, _("%s: oldest multitransaction ID (-m) must not be 0\n"),
199                                                         progname);
200                                         exit(1);
201                                 }
202                                 break;
203
204                         case 'O':
205                                 set_mxoff = strtoul(optarg, &endptr, 0);
206                                 if (endptr == optarg || *endptr != '\0')
207                                 {
208                                         fprintf(stderr, _("%s: invalid argument for option -O\n"), progname);
209                                         fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
210                                         exit(1);
211                                 }
212                                 if (set_mxoff == -1)
213                                 {
214                                         fprintf(stderr, _("%s: multitransaction offset (-O) must not be -1\n"), progname);
215                                         exit(1);
216                                 }
217                                 break;
218
219                         case 'l':
220                                 if (strspn(optarg, "01234567890ABCDEFabcdef") != 24)
221                                 {
222                                         fprintf(stderr, _("%s: invalid argument for option -l\n"), progname);
223                                         fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
224                                         exit(1);
225                                 }
226                                 XLogFromFileName(optarg, &minXlogTli, &minXlogSegNo);
227                                 break;
228
229                         default:
230                                 fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
231                                 exit(1);
232                 }
233         }
234
235         if (optind == argc)
236         {
237                 fprintf(stderr, _("%s: no data directory specified\n"), progname);
238                 fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
239                 exit(1);
240         }
241
242         /*
243          * Don't allow pg_resetxlog to be run as root, to avoid overwriting the
244          * ownership of files in the data directory. We need only check for root
245          * -- any other user won't have sufficient permissions to modify files in
246          * the data directory.
247          */
248 #ifndef WIN32
249         if (geteuid() == 0)
250         {
251                 fprintf(stderr, _("%s: cannot be executed by \"root\"\n"),
252                                 progname);
253                 fprintf(stderr, _("You must run %s as the PostgreSQL superuser.\n"),
254                                 progname);
255                 exit(1);
256         }
257 #endif
258
259         DataDir = argv[optind];
260
261         if (chdir(DataDir) < 0)
262         {
263                 fprintf(stderr, _("%s: could not change directory to \"%s\": %s\n"),
264                                 progname, DataDir, strerror(errno));
265                 exit(1);
266         }
267
268         /*
269          * Check for a postmaster lock file --- if there is one, refuse to
270          * proceed, on grounds we might be interfering with a live installation.
271          */
272         if ((fd = open("postmaster.pid", O_RDONLY, 0)) < 0)
273         {
274                 if (errno != ENOENT)
275                 {
276                         fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
277                                         progname, "postmaster.pid", strerror(errno));
278                         exit(1);
279                 }
280         }
281         else
282         {
283                 fprintf(stderr, _("%s: lock file \"%s\" exists\n"
284                                                   "Is a server running?  If not, delete the lock file and try again.\n"),
285                                 progname, "postmaster.pid");
286                 exit(1);
287         }
288
289         /*
290          * Attempt to read the existing pg_control file
291          */
292         if (!ReadControlFile())
293                 GuessControlValues();
294
295         /*
296          * Also look at existing segment files to set up newXlogSegNo
297          */
298         FindEndOfXLOG();
299
300         /*
301          * If we're not going to proceed with the reset, print the current control
302          * file parameters.
303          */
304         if ((guessed && !force) || noupdate)
305                 PrintControlValues(guessed);
306
307         /*
308          * Adjust fields if required by switches.  (Do this now so that printout,
309          * if any, includes these values.)
310          */
311         if (set_xid_epoch != -1)
312                 ControlFile.checkPointCopy.nextXidEpoch = set_xid_epoch;
313
314         if (set_xid != 0)
315         {
316                 ControlFile.checkPointCopy.nextXid = set_xid;
317
318                 /*
319                  * For the moment, just set oldestXid to a value that will force
320                  * immediate autovacuum-for-wraparound.  It's not clear whether adding
321                  * user control of this is useful, so let's just do something that's
322                  * reasonably safe.  The magic constant here corresponds to the
323                  * maximum allowed value of autovacuum_freeze_max_age.
324                  */
325                 ControlFile.checkPointCopy.oldestXid = set_xid - 2000000000;
326                 if (ControlFile.checkPointCopy.oldestXid < FirstNormalTransactionId)
327                         ControlFile.checkPointCopy.oldestXid += FirstNormalTransactionId;
328                 ControlFile.checkPointCopy.oldestXidDB = InvalidOid;
329         }
330
331         if (set_oid != 0)
332                 ControlFile.checkPointCopy.nextOid = set_oid;
333
334         if (set_mxid != 0)
335         {
336                 ControlFile.checkPointCopy.nextMulti = set_mxid;
337
338                 ControlFile.checkPointCopy.oldestMulti = set_oldestmxid;
339                 if (ControlFile.checkPointCopy.oldestMulti < FirstMultiXactId)
340                         ControlFile.checkPointCopy.oldestMulti += FirstMultiXactId;
341                 ControlFile.checkPointCopy.oldestMultiDB = InvalidOid;
342         }
343
344         if (set_mxoff != -1)
345                 ControlFile.checkPointCopy.nextMultiOffset = set_mxoff;
346
347         if (minXlogTli > ControlFile.checkPointCopy.ThisTimeLineID)
348         {
349                 ControlFile.checkPointCopy.ThisTimeLineID = minXlogTli;
350                 ControlFile.checkPointCopy.PrevTimeLineID = minXlogTli;
351         }
352
353         if (minXlogSegNo > newXlogSegNo)
354                 newXlogSegNo = minXlogSegNo;
355
356         /*
357          * If we had to guess anything, and -f was not given, just print the
358          * guessed values and exit.  Also print if -n is given.
359          */
360         if ((guessed && !force) || noupdate)
361         {
362                 PrintNewControlValues();
363                 if (!noupdate)
364                 {
365                         printf(_("\nIf these values seem acceptable, use -f to force reset.\n"));
366                         exit(1);
367                 }
368                 else
369                         exit(0);
370         }
371
372         /*
373          * Don't reset from a dirty pg_control without -f, either.
374          */
375         if (ControlFile.state != DB_SHUTDOWNED && !force)
376         {
377                 printf(_("The database server was not shut down cleanly.\n"
378                            "Resetting the transaction log might cause data to be lost.\n"
379                                  "If you want to proceed anyway, use -f to force reset.\n"));
380                 exit(1);
381         }
382
383         /*
384          * Else, do the dirty deed.
385          */
386         RewriteControlFile();
387         KillExistingXLOG();
388         KillExistingArchiveStatus();
389         WriteEmptyXLOG();
390
391         printf(_("Transaction log reset\n"));
392         return 0;
393 }
394
395
396 /*
397  * Try to read the existing pg_control file.
398  *
399  * This routine is also responsible for updating old pg_control versions
400  * to the current format.  (Currently we don't do anything of the sort.)
401  */
402 static bool
403 ReadControlFile(void)
404 {
405         int                     fd;
406         int                     len;
407         char       *buffer;
408         pg_crc32        crc;
409
410         if ((fd = open(XLOG_CONTROL_FILE, O_RDONLY | PG_BINARY, 0)) < 0)
411         {
412                 /*
413                  * If pg_control is not there at all, or we can't read it, the odds
414                  * are we've been handed a bad DataDir path, so give up. User can do
415                  * "touch pg_control" to force us to proceed.
416                  */
417                 fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
418                                 progname, XLOG_CONTROL_FILE, strerror(errno));
419                 if (errno == ENOENT)
420                         fprintf(stderr, _("If you are sure the data directory path is correct, execute\n"
421                                                           "  touch %s\n"
422                                                           "and try again.\n"),
423                                         XLOG_CONTROL_FILE);
424                 exit(1);
425         }
426
427         /* Use malloc to ensure we have a maxaligned buffer */
428         buffer = (char *) pg_malloc(PG_CONTROL_SIZE);
429
430         len = read(fd, buffer, PG_CONTROL_SIZE);
431         if (len < 0)
432         {
433                 fprintf(stderr, _("%s: could not read file \"%s\": %s\n"),
434                                 progname, XLOG_CONTROL_FILE, strerror(errno));
435                 exit(1);
436         }
437         close(fd);
438
439         if (len >= sizeof(ControlFileData) &&
440           ((ControlFileData *) buffer)->pg_control_version == PG_CONTROL_VERSION)
441         {
442                 /* Check the CRC. */
443                 INIT_CRC32(crc);
444                 COMP_CRC32(crc,
445                                    buffer,
446                                    offsetof(ControlFileData, crc));
447                 FIN_CRC32(crc);
448
449                 if (EQ_CRC32(crc, ((ControlFileData *) buffer)->crc))
450                 {
451                         /* Valid data... */
452                         memcpy(&ControlFile, buffer, sizeof(ControlFile));
453                         return true;
454                 }
455
456                 fprintf(stderr, _("%s: pg_control exists but has invalid CRC; proceed with caution\n"),
457                                 progname);
458                 /* We will use the data anyway, but treat it as guessed. */
459                 memcpy(&ControlFile, buffer, sizeof(ControlFile));
460                 guessed = true;
461                 return true;
462         }
463
464         /* Looks like it's a mess. */
465         fprintf(stderr, _("%s: pg_control exists but is broken or unknown version; ignoring it\n"),
466                         progname);
467         return false;
468 }
469
470
471 /*
472  * Guess at pg_control values when we can't read the old ones.
473  */
474 static void
475 GuessControlValues(void)
476 {
477         uint64          sysidentifier;
478         struct timeval tv;
479
480         /*
481          * Set up a completely default set of pg_control values.
482          */
483         guessed = true;
484         memset(&ControlFile, 0, sizeof(ControlFile));
485
486         ControlFile.pg_control_version = PG_CONTROL_VERSION;
487         ControlFile.catalog_version_no = CATALOG_VERSION_NO;
488
489         /*
490          * Create a new unique installation identifier, since we can no longer use
491          * any old XLOG records.  See notes in xlog.c about the algorithm.
492          */
493         gettimeofday(&tv, NULL);
494         sysidentifier = ((uint64) tv.tv_sec) << 32;
495         sysidentifier |= (uint32) (tv.tv_sec | tv.tv_usec);
496
497         ControlFile.system_identifier = sysidentifier;
498
499         ControlFile.checkPointCopy.redo = SizeOfXLogLongPHD;
500         ControlFile.checkPointCopy.ThisTimeLineID = 1;
501         ControlFile.checkPointCopy.PrevTimeLineID = 1;
502         ControlFile.checkPointCopy.fullPageWrites = false;
503         ControlFile.checkPointCopy.nextXidEpoch = 0;
504         ControlFile.checkPointCopy.nextXid = FirstNormalTransactionId;
505         ControlFile.checkPointCopy.nextOid = FirstBootstrapObjectId;
506         ControlFile.checkPointCopy.nextMulti = FirstMultiXactId;
507         ControlFile.checkPointCopy.nextMultiOffset = 0;
508         ControlFile.checkPointCopy.oldestXid = FirstNormalTransactionId;
509         ControlFile.checkPointCopy.oldestXidDB = InvalidOid;
510         ControlFile.checkPointCopy.oldestMulti = FirstMultiXactId;
511         ControlFile.checkPointCopy.oldestMultiDB = InvalidOid;
512         ControlFile.checkPointCopy.time = (pg_time_t) time(NULL);
513         ControlFile.checkPointCopy.oldestActiveXid = InvalidTransactionId;
514
515         ControlFile.state = DB_SHUTDOWNED;
516         ControlFile.time = (pg_time_t) time(NULL);
517         ControlFile.checkPoint = ControlFile.checkPointCopy.redo;
518         ControlFile.unloggedLSN = 1;
519
520         /* minRecoveryPoint, backupStartPoint and backupEndPoint can be left zero */
521
522         ControlFile.wal_level = WAL_LEVEL_MINIMAL;
523         ControlFile.wal_log_hints = false;
524         ControlFile.MaxConnections = 100;
525         ControlFile.max_worker_processes = 8;
526         ControlFile.max_prepared_xacts = 0;
527         ControlFile.max_locks_per_xact = 64;
528
529         ControlFile.maxAlign = MAXIMUM_ALIGNOF;
530         ControlFile.floatFormat = FLOATFORMAT_VALUE;
531         ControlFile.blcksz = BLCKSZ;
532         ControlFile.relseg_size = RELSEG_SIZE;
533         ControlFile.xlog_blcksz = XLOG_BLCKSZ;
534         ControlFile.xlog_seg_size = XLOG_SEG_SIZE;
535         ControlFile.nameDataLen = NAMEDATALEN;
536         ControlFile.indexMaxKeys = INDEX_MAX_KEYS;
537         ControlFile.toast_max_chunk_size = TOAST_MAX_CHUNK_SIZE;
538 #ifdef HAVE_INT64_TIMESTAMP
539         ControlFile.enableIntTimes = true;
540 #else
541         ControlFile.enableIntTimes = false;
542 #endif
543         ControlFile.float4ByVal = FLOAT4PASSBYVAL;
544         ControlFile.float8ByVal = FLOAT8PASSBYVAL;
545
546         /*
547          * XXX eventually, should try to grovel through old XLOG to develop more
548          * accurate values for TimeLineID, nextXID, etc.
549          */
550 }
551
552
553 /*
554  * Print the guessed pg_control values when we had to guess.
555  *
556  * NB: this display should be just those fields that will not be
557  * reset by RewriteControlFile().
558  */
559 static void
560 PrintControlValues(bool guessed)
561 {
562         char            sysident_str[32];
563
564         if (guessed)
565                 printf(_("Guessed pg_control values:\n\n"));
566         else
567                 printf(_("Current pg_control values:\n\n"));
568
569         /*
570          * Format system_identifier separately to keep platform-dependent format
571          * code out of the translatable message string.
572          */
573         snprintf(sysident_str, sizeof(sysident_str), UINT64_FORMAT,
574                          ControlFile.system_identifier);
575
576         printf(_("pg_control version number:            %u\n"),
577                    ControlFile.pg_control_version);
578         printf(_("Catalog version number:               %u\n"),
579                    ControlFile.catalog_version_no);
580         printf(_("Database system identifier:           %s\n"),
581                    sysident_str);
582         printf(_("Latest checkpoint's TimeLineID:       %u\n"),
583                    ControlFile.checkPointCopy.ThisTimeLineID);
584         printf(_("Latest checkpoint's full_page_writes: %s\n"),
585                    ControlFile.checkPointCopy.fullPageWrites ? _("on") : _("off"));
586         printf(_("Latest checkpoint's NextXID:          %u/%u\n"),
587                    ControlFile.checkPointCopy.nextXidEpoch,
588                    ControlFile.checkPointCopy.nextXid);
589         printf(_("Latest checkpoint's NextOID:          %u\n"),
590                    ControlFile.checkPointCopy.nextOid);
591         printf(_("Latest checkpoint's NextMultiXactId:  %u\n"),
592                    ControlFile.checkPointCopy.nextMulti);
593         printf(_("Latest checkpoint's NextMultiOffset:  %u\n"),
594                    ControlFile.checkPointCopy.nextMultiOffset);
595         printf(_("Latest checkpoint's oldestXID:        %u\n"),
596                    ControlFile.checkPointCopy.oldestXid);
597         printf(_("Latest checkpoint's oldestXID's DB:   %u\n"),
598                    ControlFile.checkPointCopy.oldestXidDB);
599         printf(_("Latest checkpoint's oldestActiveXID:  %u\n"),
600                    ControlFile.checkPointCopy.oldestActiveXid);
601         printf(_("Latest checkpoint's oldestMultiXid:   %u\n"),
602                    ControlFile.checkPointCopy.oldestMulti);
603         printf(_("Latest checkpoint's oldestMulti's DB: %u\n"),
604                    ControlFile.checkPointCopy.oldestMultiDB);
605         printf(_("Maximum data alignment:               %u\n"),
606                    ControlFile.maxAlign);
607         /* we don't print floatFormat since can't say much useful about it */
608         printf(_("Database block size:                  %u\n"),
609                    ControlFile.blcksz);
610         printf(_("Blocks per segment of large relation: %u\n"),
611                    ControlFile.relseg_size);
612         printf(_("WAL block size:                       %u\n"),
613                    ControlFile.xlog_blcksz);
614         printf(_("Bytes per WAL segment:                %u\n"),
615                    ControlFile.xlog_seg_size);
616         printf(_("Maximum length of identifiers:        %u\n"),
617                    ControlFile.nameDataLen);
618         printf(_("Maximum columns in an index:          %u\n"),
619                    ControlFile.indexMaxKeys);
620         printf(_("Maximum size of a TOAST chunk:        %u\n"),
621                    ControlFile.toast_max_chunk_size);
622         printf(_("Date/time type storage:               %s\n"),
623                    (ControlFile.enableIntTimes ? _("64-bit integers") : _("floating-point numbers")));
624         printf(_("Float4 argument passing:              %s\n"),
625                    (ControlFile.float4ByVal ? _("by value") : _("by reference")));
626         printf(_("Float8 argument passing:              %s\n"),
627                    (ControlFile.float8ByVal ? _("by value") : _("by reference")));
628         printf(_("Data page checksum version:           %u\n"),
629                    ControlFile.data_checksum_version);
630 }
631
632
633 /*
634  * Print the values to be changed.
635  */
636 static void
637 PrintNewControlValues()
638 {
639         char            fname[MAXFNAMELEN];
640
641         /* This will be always printed in order to keep format same. */
642         printf(_("\n\nValues to be changed:\n\n"));
643
644         XLogFileName(fname, ControlFile.checkPointCopy.ThisTimeLineID, newXlogSegNo);
645         printf(_("First log segment after reset:        %s\n"), fname);
646
647         if (set_mxid != 0)
648         {
649                 printf(_("NextMultiXactId:                      %u\n"),
650                            ControlFile.checkPointCopy.nextMulti);
651                 printf(_("OldestMultiXid:                       %u\n"),
652                            ControlFile.checkPointCopy.oldestMulti);
653                 printf(_("OldestMulti's DB:                     %u\n"),
654                            ControlFile.checkPointCopy.oldestMultiDB);
655         }
656
657         if (set_mxoff != -1)
658         {
659                 printf(_("NextMultiOffset:                      %u\n"),
660                            ControlFile.checkPointCopy.nextMultiOffset);
661         }
662
663         if (set_oid != 0)
664         {
665                 printf(_("NextOID:                              %u\n"),
666                            ControlFile.checkPointCopy.nextOid);
667         }
668
669         if (set_xid != 0)
670         {
671                 printf(_("NextXID:                              %u\n"),
672                            ControlFile.checkPointCopy.nextXid);
673                 printf(_("OldestXID:                            %u\n"),
674                            ControlFile.checkPointCopy.oldestXid);
675                 printf(_("OldestXID's DB:                       %u\n"),
676                            ControlFile.checkPointCopy.oldestXidDB);
677         }
678
679         if (set_xid_epoch != -1)
680         {
681                 printf(_("NextXID Epoch:                        %u\n"),
682                            ControlFile.checkPointCopy.nextXidEpoch);
683         }
684 }
685
686
687 /*
688  * Write out the new pg_control file.
689  */
690 static void
691 RewriteControlFile(void)
692 {
693         int                     fd;
694         char            buffer[PG_CONTROL_SIZE];                /* need not be aligned */
695
696         /*
697          * Adjust fields as needed to force an empty XLOG starting at
698          * newXlogSegNo.
699          */
700         XLogSegNoOffsetToRecPtr(newXlogSegNo, SizeOfXLogLongPHD,
701                                                         ControlFile.checkPointCopy.redo);
702         ControlFile.checkPointCopy.time = (pg_time_t) time(NULL);
703
704         ControlFile.state = DB_SHUTDOWNED;
705         ControlFile.time = (pg_time_t) time(NULL);
706         ControlFile.checkPoint = ControlFile.checkPointCopy.redo;
707         ControlFile.prevCheckPoint = 0;
708         ControlFile.minRecoveryPoint = 0;
709         ControlFile.minRecoveryPointTLI = 0;
710         ControlFile.backupStartPoint = 0;
711         ControlFile.backupEndPoint = 0;
712         ControlFile.backupEndRequired = false;
713
714         /*
715          * Force the defaults for max_* settings. The values don't really matter
716          * as long as wal_level='minimal'; the postmaster will reset these fields
717          * anyway at startup.
718          */
719         ControlFile.wal_level = WAL_LEVEL_MINIMAL;
720         ControlFile.wal_log_hints = false;
721         ControlFile.MaxConnections = 100;
722         ControlFile.max_worker_processes = 8;
723         ControlFile.max_prepared_xacts = 0;
724         ControlFile.max_locks_per_xact = 64;
725
726         /* Now we can force the recorded xlog seg size to the right thing. */
727         ControlFile.xlog_seg_size = XLogSegSize;
728
729         /* Contents are protected with a CRC */
730         INIT_CRC32(ControlFile.crc);
731         COMP_CRC32(ControlFile.crc,
732                            (char *) &ControlFile,
733                            offsetof(ControlFileData, crc));
734         FIN_CRC32(ControlFile.crc);
735
736         /*
737          * We write out PG_CONTROL_SIZE bytes into pg_control, zero-padding the
738          * excess over sizeof(ControlFileData).  This reduces the odds of
739          * premature-EOF errors when reading pg_control.  We'll still fail when we
740          * check the contents of the file, but hopefully with a more specific
741          * error than "couldn't read pg_control".
742          */
743         if (sizeof(ControlFileData) > PG_CONTROL_SIZE)
744         {
745                 fprintf(stderr,
746                                 _("%s: internal error -- sizeof(ControlFileData) is too large ... fix PG_CONTROL_SIZE\n"),
747                                 progname);
748                 exit(1);
749         }
750
751         memset(buffer, 0, PG_CONTROL_SIZE);
752         memcpy(buffer, &ControlFile, sizeof(ControlFileData));
753
754         unlink(XLOG_CONTROL_FILE);
755
756         fd = open(XLOG_CONTROL_FILE,
757                           O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
758                           S_IRUSR | S_IWUSR);
759         if (fd < 0)
760         {
761                 fprintf(stderr, _("%s: could not create pg_control file: %s\n"),
762                                 progname, strerror(errno));
763                 exit(1);
764         }
765
766         errno = 0;
767         if (write(fd, buffer, PG_CONTROL_SIZE) != PG_CONTROL_SIZE)
768         {
769                 /* if write didn't set errno, assume problem is no disk space */
770                 if (errno == 0)
771                         errno = ENOSPC;
772                 fprintf(stderr, _("%s: could not write pg_control file: %s\n"),
773                                 progname, strerror(errno));
774                 exit(1);
775         }
776
777         if (fsync(fd) != 0)
778         {
779                 fprintf(stderr, _("%s: fsync error: %s\n"), progname, strerror(errno));
780                 exit(1);
781         }
782
783         close(fd);
784 }
785
786
787 /*
788  * Scan existing XLOG files and determine the highest existing WAL address
789  *
790  * On entry, ControlFile.checkPointCopy.redo and ControlFile.xlog_seg_size
791  * are assumed valid (note that we allow the old xlog seg size to differ
792  * from what we're using).  On exit, newXlogId and newXlogSeg are set to
793  * suitable values for the beginning of replacement WAL (in our seg size).
794  */
795 static void
796 FindEndOfXLOG(void)
797 {
798         DIR                *xldir;
799         struct dirent *xlde;
800         uint64          segs_per_xlogid;
801         uint64          xlogbytepos;
802
803         /*
804          * Initialize the max() computation using the last checkpoint address from
805          * old pg_control.      Note that for the moment we are working with segment
806          * numbering according to the old xlog seg size.
807          */
808         segs_per_xlogid = (UINT64CONST(0x0000000100000000) / ControlFile.xlog_seg_size);
809         newXlogSegNo = ControlFile.checkPointCopy.redo / ControlFile.xlog_seg_size;
810
811         /*
812          * Scan the pg_xlog directory to find existing WAL segment files. We
813          * assume any present have been used; in most scenarios this should be
814          * conservative, because of xlog.c's attempts to pre-create files.
815          */
816         xldir = opendir(XLOGDIR);
817         if (xldir == NULL)
818         {
819                 fprintf(stderr, _("%s: could not open directory \"%s\": %s\n"),
820                                 progname, XLOGDIR, strerror(errno));
821                 exit(1);
822         }
823
824         errno = 0;
825         while ((xlde = readdir(xldir)) != NULL)
826         {
827                 if (strlen(xlde->d_name) == 24 &&
828                         strspn(xlde->d_name, "0123456789ABCDEF") == 24)
829                 {
830                         unsigned int tli,
831                                                 log,
832                                                 seg;
833                         XLogSegNo       segno;
834
835                         sscanf(xlde->d_name, "%08X%08X%08X", &tli, &log, &seg);
836                         segno = ((uint64) log) * segs_per_xlogid + seg;
837
838                         /*
839                          * Note: we take the max of all files found, regardless of their
840                          * timelines.  Another possibility would be to ignore files of
841                          * timelines other than the target TLI, but this seems safer.
842                          * Better too large a result than too small...
843                          */
844                         if (segno > newXlogSegNo)
845                                 newXlogSegNo = segno;
846                 }
847                 errno = 0;
848         }
849 #ifdef WIN32
850
851         /*
852          * This fix is in mingw cvs (runtime/mingwex/dirent.c rev 1.4), but not in
853          * released version
854          */
855         if (GetLastError() == ERROR_NO_MORE_FILES)
856                 errno = 0;
857 #endif
858
859         if (errno)
860         {
861                 fprintf(stderr, _("%s: could not read from directory \"%s\": %s\n"),
862                                 progname, XLOGDIR, strerror(errno));
863                 exit(1);
864         }
865         closedir(xldir);
866
867         /*
868          * Finally, convert to new xlog seg size, and advance by one to ensure we
869          * are in virgin territory.
870          */
871         xlogbytepos = newXlogSegNo * ControlFile.xlog_seg_size;
872         newXlogSegNo = (xlogbytepos + XLogSegSize - 1) / XLogSegSize;
873         newXlogSegNo++;
874 }
875
876
877 /*
878  * Remove existing XLOG files
879  */
880 static void
881 KillExistingXLOG(void)
882 {
883         DIR                *xldir;
884         struct dirent *xlde;
885         char            path[MAXPGPATH];
886
887         xldir = opendir(XLOGDIR);
888         if (xldir == NULL)
889         {
890                 fprintf(stderr, _("%s: could not open directory \"%s\": %s\n"),
891                                 progname, XLOGDIR, strerror(errno));
892                 exit(1);
893         }
894
895         errno = 0;
896         while ((xlde = readdir(xldir)) != NULL)
897         {
898                 if (strlen(xlde->d_name) == 24 &&
899                         strspn(xlde->d_name, "0123456789ABCDEF") == 24)
900                 {
901                         snprintf(path, MAXPGPATH, "%s/%s", XLOGDIR, xlde->d_name);
902                         if (unlink(path) < 0)
903                         {
904                                 fprintf(stderr, _("%s: could not delete file \"%s\": %s\n"),
905                                                 progname, path, strerror(errno));
906                                 exit(1);
907                         }
908                 }
909                 errno = 0;
910         }
911 #ifdef WIN32
912
913         /*
914          * This fix is in mingw cvs (runtime/mingwex/dirent.c rev 1.4), but not in
915          * released version
916          */
917         if (GetLastError() == ERROR_NO_MORE_FILES)
918                 errno = 0;
919 #endif
920
921         if (errno)
922         {
923                 fprintf(stderr, _("%s: could not read from directory \"%s\": %s\n"),
924                                 progname, XLOGDIR, strerror(errno));
925                 exit(1);
926         }
927         closedir(xldir);
928 }
929
930
931 /*
932  * Remove existing archive status files
933  */
934 static void
935 KillExistingArchiveStatus(void)
936 {
937         DIR                *xldir;
938         struct dirent *xlde;
939         char            path[MAXPGPATH];
940
941 #define ARCHSTATDIR XLOGDIR "/archive_status"
942
943         xldir = opendir(ARCHSTATDIR);
944         if (xldir == NULL)
945         {
946                 fprintf(stderr, _("%s: could not open directory \"%s\": %s\n"),
947                                 progname, ARCHSTATDIR, strerror(errno));
948                 exit(1);
949         }
950
951         errno = 0;
952         while ((xlde = readdir(xldir)) != NULL)
953         {
954                 if (strspn(xlde->d_name, "0123456789ABCDEF") == 24 &&
955                         (strcmp(xlde->d_name + 24, ".ready") == 0 ||
956                          strcmp(xlde->d_name + 24, ".done") == 0))
957                 {
958                         snprintf(path, MAXPGPATH, "%s/%s", ARCHSTATDIR, xlde->d_name);
959                         if (unlink(path) < 0)
960                         {
961                                 fprintf(stderr, _("%s: could not delete file \"%s\": %s\n"),
962                                                 progname, path, strerror(errno));
963                                 exit(1);
964                         }
965                 }
966                 errno = 0;
967         }
968 #ifdef WIN32
969
970         /*
971          * This fix is in mingw cvs (runtime/mingwex/dirent.c rev 1.4), but not in
972          * released version
973          */
974         if (GetLastError() == ERROR_NO_MORE_FILES)
975                 errno = 0;
976 #endif
977
978         if (errno)
979         {
980                 fprintf(stderr, _("%s: could not read from directory \"%s\": %s\n"),
981                                 progname, ARCHSTATDIR, strerror(errno));
982                 exit(1);
983         }
984         closedir(xldir);
985 }
986
987
988 /*
989  * Write an empty XLOG file, containing only the checkpoint record
990  * already set up in ControlFile.
991  */
992 static void
993 WriteEmptyXLOG(void)
994 {
995         char       *buffer;
996         XLogPageHeader page;
997         XLogLongPageHeader longpage;
998         XLogRecord *record;
999         pg_crc32        crc;
1000         char            path[MAXPGPATH];
1001         int                     fd;
1002         int                     nbytes;
1003
1004         /* Use malloc() to ensure buffer is MAXALIGNED */
1005         buffer = (char *) pg_malloc(XLOG_BLCKSZ);
1006         page = (XLogPageHeader) buffer;
1007         memset(buffer, 0, XLOG_BLCKSZ);
1008
1009         /* Set up the XLOG page header */
1010         page->xlp_magic = XLOG_PAGE_MAGIC;
1011         page->xlp_info = XLP_LONG_HEADER;
1012         page->xlp_tli = ControlFile.checkPointCopy.ThisTimeLineID;
1013         page->xlp_pageaddr = ControlFile.checkPointCopy.redo - SizeOfXLogLongPHD;
1014         longpage = (XLogLongPageHeader) page;
1015         longpage->xlp_sysid = ControlFile.system_identifier;
1016         longpage->xlp_seg_size = XLogSegSize;
1017         longpage->xlp_xlog_blcksz = XLOG_BLCKSZ;
1018
1019         /* Insert the initial checkpoint record */
1020         record = (XLogRecord *) ((char *) page + SizeOfXLogLongPHD);
1021         record->xl_prev = 0;
1022         record->xl_xid = InvalidTransactionId;
1023         record->xl_tot_len = SizeOfXLogRecord + sizeof(CheckPoint);
1024         record->xl_len = sizeof(CheckPoint);
1025         record->xl_info = XLOG_CHECKPOINT_SHUTDOWN;
1026         record->xl_rmid = RM_XLOG_ID;
1027         memcpy(XLogRecGetData(record), &ControlFile.checkPointCopy,
1028                    sizeof(CheckPoint));
1029
1030         INIT_CRC32(crc);
1031         COMP_CRC32(crc, &ControlFile.checkPointCopy, sizeof(CheckPoint));
1032         COMP_CRC32(crc, (char *) record, offsetof(XLogRecord, xl_crc));
1033         FIN_CRC32(crc);
1034         record->xl_crc = crc;
1035
1036         /* Write the first page */
1037         XLogFilePath(path, ControlFile.checkPointCopy.ThisTimeLineID, newXlogSegNo);
1038
1039         unlink(path);
1040
1041         fd = open(path, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
1042                           S_IRUSR | S_IWUSR);
1043         if (fd < 0)
1044         {
1045                 fprintf(stderr, _("%s: could not open file \"%s\": %s\n"),
1046                                 progname, path, strerror(errno));
1047                 exit(1);
1048         }
1049
1050         errno = 0;
1051         if (write(fd, buffer, XLOG_BLCKSZ) != XLOG_BLCKSZ)
1052         {
1053                 /* if write didn't set errno, assume problem is no disk space */
1054                 if (errno == 0)
1055                         errno = ENOSPC;
1056                 fprintf(stderr, _("%s: could not write file \"%s\": %s\n"),
1057                                 progname, path, strerror(errno));
1058                 exit(1);
1059         }
1060
1061         /* Fill the rest of the file with zeroes */
1062         memset(buffer, 0, XLOG_BLCKSZ);
1063         for (nbytes = XLOG_BLCKSZ; nbytes < XLogSegSize; nbytes += XLOG_BLCKSZ)
1064         {
1065                 errno = 0;
1066                 if (write(fd, buffer, XLOG_BLCKSZ) != XLOG_BLCKSZ)
1067                 {
1068                         if (errno == 0)
1069                                 errno = ENOSPC;
1070                         fprintf(stderr, _("%s: could not write file \"%s\": %s\n"),
1071                                         progname, path, strerror(errno));
1072                         exit(1);
1073                 }
1074         }
1075
1076         if (fsync(fd) != 0)
1077         {
1078                 fprintf(stderr, _("%s: fsync error: %s\n"), progname, strerror(errno));
1079                 exit(1);
1080         }
1081
1082         close(fd);
1083 }
1084
1085
1086 static void
1087 usage(void)
1088 {
1089         printf(_("%s resets the PostgreSQL transaction log.\n\n"), progname);
1090         printf(_("Usage:\n  %s [OPTION]... DATADIR\n\n"), progname);
1091         printf(_("Options:\n"));
1092         printf(_("  -e XIDEPOCH      set next transaction ID epoch\n"));
1093         printf(_("  -f               force update to be done\n"));
1094         printf(_("  -l XLOGFILE      force minimum WAL starting location for new transaction log\n"));
1095         printf(_("  -m MXID,MXID     set next and oldest multitransaction ID\n"));
1096         printf(_("  -n               no update, just show what would be done (for testing)\n"));
1097         printf(_("  -o OID           set next OID\n"));
1098         printf(_("  -O OFFSET        set next multitransaction offset\n"));
1099         printf(_("  -V, --version    output version information, then exit\n"));
1100         printf(_("  -x XID           set next transaction ID\n"));
1101         printf(_("  -?, --help       show this help, then exit\n"));
1102         printf(_("\nReport bugs to <pgsql-bugs@postgresql.org>.\n"));
1103 }