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