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