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