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