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