]> granicus.if.org Git - postgresql/blob - src/bin/pg_resetxlog/pg_resetxlog.c
Update copyright for 2009.
[postgresql] / src / bin / pg_resetxlog / pg_resetxlog.c
1 /*-------------------------------------------------------------------------
2  *
3  * pg_resetxlog.c
4  *        A utility to "zero out" the xlog when it's corrupt beyond recovery.
5  *        Can also rebuild pg_control if needed.
6  *
7  * The theory of operation is fairly simple:
8  *        1. Read the existing pg_control (which will include the last
9  *               checkpoint record).  If it is an old format then update to
10  *               current format.
11  *        2. If pg_control is corrupt, attempt to intuit reasonable values,
12  *               by scanning the old xlog if necessary.
13  *        3. Modify pg_control to reflect a "shutdown" state with a checkpoint
14  *               record at the start of xlog.
15  *        4. Flush the existing xlog files and write a new segment with
16  *               just a checkpoint record in it.  The new segment is positioned
17  *               just past the end of the old xlog, so that existing LSNs in
18  *               data pages will appear to be "in the past".
19  * This is all pretty straightforward except for the intuition part of
20  * step 2 ...
21  *
22  *
23  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
24  * Portions Copyright (c) 1994, Regents of the University of California
25  *
26  * $PostgreSQL: pgsql/src/bin/pg_resetxlog/pg_resetxlog.c,v 1.71 2009/01/01 17:23:54 momjian Exp $
27  *
28  *-------------------------------------------------------------------------
29  */
30
31 /*
32  * We have to use postgres.h not postgres_fe.h here, because there's so much
33  * backend-only stuff in the XLOG include files we need.  But we need a
34  * frontend-ish environment otherwise.  Hence this ugly hack.
35  */
36 #define FRONTEND 1
37
38 #include "postgres.h"
39
40 #include <dirent.h>
41 #include <fcntl.h>
42 #include <locale.h>
43 #include <sys/stat.h>
44 #include <sys/time.h>
45 #include <time.h>
46 #include <unistd.h>
47 #ifdef HAVE_GETOPT_H
48 #include <getopt.h>
49 #endif
50
51 #include "access/transam.h"
52 #include "access/tuptoaster.h"
53 #include "access/multixact.h"
54 #include "access/xlog_internal.h"
55 #include "catalog/catversion.h"
56 #include "catalog/pg_control.h"
57
58 extern int      optind;
59 extern char *optarg;
60
61
62 static ControlFileData ControlFile;             /* pg_control values */
63 static uint32 newXlogId,
64                         newXlogSeg;                     /* ID/Segment of new XLOG segment */
65 static bool guessed = false;    /* T if we had to guess at any values */
66 static const char *progname;
67
68 static bool ReadControlFile(void);
69 static void GuessControlValues(void);
70 static void PrintControlValues(bool guessed);
71 static void RewriteControlFile(void);
72 static void FindEndOfXLOG(void);
73 static void KillExistingXLOG(void);
74 static void WriteEmptyXLOG(void);
75 static void usage(void);
76
77
78 int
79 main(int argc, char *argv[])
80 {
81         int                     c;
82         bool            force = false;
83         bool            noupdate = false;
84         uint32          set_xid_epoch = (uint32) -1;
85         TransactionId set_xid = 0;
86         Oid                     set_oid = 0;
87         MultiXactId set_mxid = 0;
88         MultiXactOffset set_mxoff = (MultiXactOffset) -1;
89         uint32          minXlogTli = 0,
90                                 minXlogId = 0,
91                                 minXlogSeg = 0;
92         char       *endptr;
93         char       *endptr2;
94         char       *endptr3;
95         char       *DataDir;
96         int                     fd;
97         char            path[MAXPGPATH];
98
99         set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_resetxlog"));
100
101         progname = get_progname(argv[0]);
102
103         if (argc > 1)
104         {
105                 if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
106                 {
107                         usage();
108                         exit(0);
109                 }
110                 if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
111                 {
112                         puts("pg_resetxlog (PostgreSQL) " PG_VERSION);
113                         exit(0);
114                 }
115         }
116
117
118         while ((c = getopt(argc, argv, "fl:m:no:O:x:e:")) != -1)
119         {
120                 switch (c)
121                 {
122                         case 'f':
123                                 force = true;
124                                 break;
125
126                         case 'n':
127                                 noupdate = true;
128                                 break;
129
130                         case 'e':
131                                 set_xid_epoch = strtoul(optarg, &endptr, 0);
132                                 if (endptr == optarg || *endptr != '\0')
133                                 {
134                                         fprintf(stderr, _("%s: invalid argument for option -e\n"), progname);
135                                         fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
136                                         exit(1);
137                                 }
138                                 if (set_xid_epoch == -1)
139                                 {
140                                         fprintf(stderr, _("%s: transaction ID epoch (-e) must not be -1\n"), progname);
141                                         exit(1);
142                                 }
143                                 break;
144
145                         case 'x':
146                                 set_xid = strtoul(optarg, &endptr, 0);
147                                 if (endptr == optarg || *endptr != '\0')
148                                 {
149                                         fprintf(stderr, _("%s: invalid argument for option -x\n"), progname);
150                                         fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
151                                         exit(1);
152                                 }
153                                 if (set_xid == 0)
154                                 {
155                                         fprintf(stderr, _("%s: transaction ID (-x) must not be 0\n"), progname);
156                                         exit(1);
157                                 }
158                                 break;
159
160                         case 'o':
161                                 set_oid = strtoul(optarg, &endptr, 0);
162                                 if (endptr == optarg || *endptr != '\0')
163                                 {
164                                         fprintf(stderr, _("%s: invalid argument for option -o\n"), progname);
165                                         fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
166                                         exit(1);
167                                 }
168                                 if (set_oid == 0)
169                                 {
170                                         fprintf(stderr, _("%s: OID (-o) must not be 0\n"), progname);
171                                         exit(1);
172                                 }
173                                 break;
174
175                         case 'm':
176                                 set_mxid = strtoul(optarg, &endptr, 0);
177                                 if (endptr == optarg || *endptr != '\0')
178                                 {
179                                         fprintf(stderr, _("%s: invalid argument for option -m\n"), progname);
180                                         fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
181                                         exit(1);
182                                 }
183                                 if (set_mxid == 0)
184                                 {
185                                         fprintf(stderr, _("%s: multitransaction ID (-m) must not be 0\n"), progname);
186                                         exit(1);
187                                 }
188                                 break;
189
190                         case 'O':
191                                 set_mxoff = strtoul(optarg, &endptr, 0);
192                                 if (endptr == optarg || *endptr != '\0')
193                                 {
194                                         fprintf(stderr, _("%s: invalid argument for option -O\n"), progname);
195                                         fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
196                                         exit(1);
197                                 }
198                                 if (set_mxoff == -1)
199                                 {
200                                         fprintf(stderr, _("%s: multitransaction offset (-O) must not be -1\n"), progname);
201                                         exit(1);
202                                 }
203                                 break;
204
205                         case 'l':
206                                 minXlogTli = strtoul(optarg, &endptr, 0);
207                                 if (endptr == optarg || *endptr != ',')
208                                 {
209                                         fprintf(stderr, _("%s: invalid argument for option -l\n"), progname);
210                                         fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
211                                         exit(1);
212                                 }
213                                 minXlogId = strtoul(endptr + 1, &endptr2, 0);
214                                 if (endptr2 == endptr + 1 || *endptr2 != ',')
215                                 {
216                                         fprintf(stderr, _("%s: invalid argument for option -l\n"), progname);
217                                         fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
218                                         exit(1);
219                                 }
220                                 minXlogSeg = strtoul(endptr2 + 1, &endptr3, 0);
221                                 if (endptr3 == endptr2 + 1 || *endptr3 != '\0')
222                                 {
223                                         fprintf(stderr, _("%s: invalid argument for option -l\n"), progname);
224                                         fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
225                                         exit(1);
226                                 }
227                                 break;
228
229                         default:
230                                 fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
231                                 exit(1);
232                 }
233         }
234
235         if (optind == argc)
236         {
237                 fprintf(stderr, _("%s: no data directory specified\n"), progname);
238                 fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
239                 exit(1);
240         }
241
242         /*
243          * Don't allow pg_resetxlog to be run as root, to avoid overwriting the
244          * ownership of files in the data directory. We need only check for root
245          * -- any other user won't have sufficient permissions to modify files in
246          * the data directory.
247          */
248 #ifndef WIN32
249         if (geteuid() == 0)
250         {
251                 fprintf(stderr, _("%s: cannot be executed by \"root\"\n"),
252                                 progname);
253                 fprintf(stderr, _("You must run %s as the PostgreSQL superuser.\n"),
254                                 progname);
255                 exit(1);
256         }
257 #endif
258
259         DataDir = argv[optind];
260
261         if (chdir(DataDir) < 0)
262         {
263                 fprintf(stderr, _("%s: could not change directory to \"%s\": %s\n"),
264                                 progname, DataDir, strerror(errno));
265                 exit(1);
266         }
267
268         /*
269          * Check for a postmaster lock file --- if there is one, refuse to
270          * proceed, on grounds we might be interfering with a live installation.
271          */
272         snprintf(path, MAXPGPATH, "%s/postmaster.pid", DataDir);
273
274         if ((fd = open(path, O_RDONLY, 0)) < 0)
275         {
276                 if (errno != ENOENT)
277                 {
278                         fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"), progname, path, strerror(errno));
279                         exit(1);
280                 }
281         }
282         else
283         {
284                 fprintf(stderr, _("%s: lock file \"%s\" exists\n"
285                                                   "Is a server running?  If not, delete the lock file and try again.\n"),
286                                 progname, path);
287                 exit(1);
288         }
289
290         /*
291          * Attempt to read the existing pg_control file
292          */
293         if (!ReadControlFile())
294                 GuessControlValues();
295
296         /*
297          * Also look at existing segment files to set up newXlogId/newXlogSeg
298          */
299         FindEndOfXLOG();
300
301         /*
302          * Adjust fields if required by switches.  (Do this now so that printout,
303          * if any, includes these values.)
304          */
305         if (set_xid_epoch != -1)
306                 ControlFile.checkPointCopy.nextXidEpoch = set_xid_epoch;
307
308         if (set_xid != 0)
309                 ControlFile.checkPointCopy.nextXid = set_xid;
310
311         if (set_oid != 0)
312                 ControlFile.checkPointCopy.nextOid = set_oid;
313
314         if (set_mxid != 0)
315                 ControlFile.checkPointCopy.nextMulti = set_mxid;
316
317         if (set_mxoff != -1)
318                 ControlFile.checkPointCopy.nextMultiOffset = set_mxoff;
319
320         if (minXlogTli > ControlFile.checkPointCopy.ThisTimeLineID)
321                 ControlFile.checkPointCopy.ThisTimeLineID = minXlogTli;
322
323         if (minXlogId > newXlogId ||
324                 (minXlogId == newXlogId &&
325                  minXlogSeg > newXlogSeg))
326         {
327                 newXlogId = minXlogId;
328                 newXlogSeg = minXlogSeg;
329         }
330
331         /*
332          * If we had to guess anything, and -f was not given, just print the
333          * guessed values and exit.  Also print if -n is given.
334          */
335         if ((guessed && !force) || noupdate)
336         {
337                 PrintControlValues(guessed);
338                 if (!noupdate)
339                 {
340                         printf(_("\nIf these values seem acceptable, use -f to force reset.\n"));
341                         exit(1);
342                 }
343                 else
344                         exit(0);
345         }
346
347         /*
348          * Don't reset from a dirty pg_control without -f, either.
349          */
350         if (ControlFile.state != DB_SHUTDOWNED && !force)
351         {
352                 printf(_("The database server was not shut down cleanly.\n"
353                            "Resetting the transaction log might cause data to be lost.\n"
354                                  "If you want to proceed anyway, use -f to force reset.\n"));
355                 exit(1);
356         }
357
358         /*
359          * Else, do the dirty deed.
360          */
361         RewriteControlFile();
362         KillExistingXLOG();
363         WriteEmptyXLOG();
364
365         printf(_("Transaction log reset\n"));
366         return 0;
367 }
368
369
370 /*
371  * Try to read the existing pg_control file.
372  *
373  * This routine is also responsible for updating old pg_control versions
374  * to the current format.  (Currently we don't do anything of the sort.)
375  */
376 static bool
377 ReadControlFile(void)
378 {
379         int                     fd;
380         int                     len;
381         char       *buffer;
382         pg_crc32        crc;
383
384         if ((fd = open(XLOG_CONTROL_FILE, O_RDONLY | PG_BINARY, 0)) < 0)
385         {
386                 /*
387                  * If pg_control is not there at all, or we can't read it, the odds
388                  * are we've been handed a bad DataDir path, so give up. User can do
389                  * "touch pg_control" to force us to proceed.
390                  */
391                 fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
392                                 progname, XLOG_CONTROL_FILE, strerror(errno));
393                 if (errno == ENOENT)
394                         fprintf(stderr, _("If you are sure the data directory path is correct, execute\n"
395                                                           "  touch %s\n"
396                                                           "and try again.\n"),
397                                         XLOG_CONTROL_FILE);
398                 exit(1);
399         }
400
401         /* Use malloc to ensure we have a maxaligned buffer */
402         buffer = (char *) malloc(PG_CONTROL_SIZE);
403
404         len = read(fd, buffer, PG_CONTROL_SIZE);
405         if (len < 0)
406         {
407                 fprintf(stderr, _("%s: could not read file \"%s\": %s\n"),
408                                 progname, XLOG_CONTROL_FILE, strerror(errno));
409                 exit(1);
410         }
411         close(fd);
412
413         if (len >= sizeof(ControlFileData) &&
414           ((ControlFileData *) buffer)->pg_control_version == PG_CONTROL_VERSION)
415         {
416                 /* Check the CRC. */
417                 INIT_CRC32(crc);
418                 COMP_CRC32(crc,
419                                    buffer,
420                                    offsetof(ControlFileData, crc));
421                 FIN_CRC32(crc);
422
423                 if (EQ_CRC32(crc, ((ControlFileData *) buffer)->crc))
424                 {
425                         /* Valid data... */
426                         memcpy(&ControlFile, buffer, sizeof(ControlFile));
427                         return true;
428                 }
429
430                 fprintf(stderr, _("%s: pg_control exists but has invalid CRC; proceed with caution\n"),
431                                 progname);
432                 /* We will use the data anyway, but treat it as guessed. */
433                 memcpy(&ControlFile, buffer, sizeof(ControlFile));
434                 guessed = true;
435                 return true;
436         }
437
438         /* Looks like it's a mess. */
439         fprintf(stderr, _("%s: pg_control exists but is broken or unknown version; ignoring it\n"),
440                         progname);
441         return false;
442 }
443
444
445 /*
446  * Guess at pg_control values when we can't read the old ones.
447  */
448 static void
449 GuessControlValues(void)
450 {
451         uint64          sysidentifier;
452         struct timeval tv;
453
454         /*
455          * Set up a completely default set of pg_control values.
456          */
457         guessed = true;
458         memset(&ControlFile, 0, sizeof(ControlFile));
459
460         ControlFile.pg_control_version = PG_CONTROL_VERSION;
461         ControlFile.catalog_version_no = CATALOG_VERSION_NO;
462
463         /*
464          * Create a new unique installation identifier, since we can no longer use
465          * any old XLOG records.  See notes in xlog.c about the algorithm.
466          */
467         gettimeofday(&tv, NULL);
468         sysidentifier = ((uint64) tv.tv_sec) << 32;
469         sysidentifier |= (uint32) (tv.tv_sec | tv.tv_usec);
470
471         ControlFile.system_identifier = sysidentifier;
472
473         ControlFile.checkPointCopy.redo.xlogid = 0;
474         ControlFile.checkPointCopy.redo.xrecoff = SizeOfXLogLongPHD;
475         ControlFile.checkPointCopy.ThisTimeLineID = 1;
476         ControlFile.checkPointCopy.nextXidEpoch = 0;
477         ControlFile.checkPointCopy.nextXid = (TransactionId) 514;       /* XXX */
478         ControlFile.checkPointCopy.nextOid = FirstBootstrapObjectId;
479         ControlFile.checkPointCopy.nextMulti = FirstMultiXactId;
480         ControlFile.checkPointCopy.nextMultiOffset = 0;
481         ControlFile.checkPointCopy.time = (pg_time_t) time(NULL);
482
483         ControlFile.state = DB_SHUTDOWNED;
484         ControlFile.time = (pg_time_t) time(NULL);
485         ControlFile.checkPoint = ControlFile.checkPointCopy.redo;
486
487         ControlFile.maxAlign = MAXIMUM_ALIGNOF;
488         ControlFile.floatFormat = FLOATFORMAT_VALUE;
489         ControlFile.blcksz = BLCKSZ;
490         ControlFile.relseg_size = RELSEG_SIZE;
491         ControlFile.xlog_blcksz = XLOG_BLCKSZ;
492         ControlFile.xlog_seg_size = XLOG_SEG_SIZE;
493         ControlFile.nameDataLen = NAMEDATALEN;
494         ControlFile.indexMaxKeys = INDEX_MAX_KEYS;
495         ControlFile.toast_max_chunk_size = TOAST_MAX_CHUNK_SIZE;
496 #ifdef HAVE_INT64_TIMESTAMP
497         ControlFile.enableIntTimes = true;
498 #else
499         ControlFile.enableIntTimes = false;
500 #endif
501         ControlFile.float4ByVal = FLOAT4PASSBYVAL;
502         ControlFile.float8ByVal = FLOAT8PASSBYVAL;
503
504         /*
505          * XXX eventually, should try to grovel through old XLOG to develop more
506          * accurate values for TimeLineID, nextXID, etc.
507          */
508 }
509
510
511 /*
512  * Print the guessed pg_control values when we had to guess.
513  *
514  * NB: this display should be just those fields that will not be
515  * reset by RewriteControlFile().
516  */
517 static void
518 PrintControlValues(bool guessed)
519 {
520         char            sysident_str[32];
521
522         if (guessed)
523                 printf(_("Guessed pg_control values:\n\n"));
524         else
525                 printf(_("pg_control values:\n\n"));
526
527         /*
528          * Format system_identifier separately to keep platform-dependent format
529          * code out of the translatable message string.
530          */
531         snprintf(sysident_str, sizeof(sysident_str), UINT64_FORMAT,
532                          ControlFile.system_identifier);
533
534         printf(_("First log file ID after reset:        %u\n"),
535                    newXlogId);
536         printf(_("First log file segment after reset:   %u\n"),
537                    newXlogSeg);
538         printf(_("pg_control version number:            %u\n"),
539                    ControlFile.pg_control_version);
540         printf(_("Catalog version number:               %u\n"),
541                    ControlFile.catalog_version_no);
542         printf(_("Database system identifier:           %s\n"),
543                    sysident_str);
544         printf(_("Latest checkpoint's TimeLineID:       %u\n"),
545                    ControlFile.checkPointCopy.ThisTimeLineID);
546         printf(_("Latest checkpoint's NextXID:          %u/%u\n"),
547                    ControlFile.checkPointCopy.nextXidEpoch,
548                    ControlFile.checkPointCopy.nextXid);
549         printf(_("Latest checkpoint's NextOID:          %u\n"),
550                    ControlFile.checkPointCopy.nextOid);
551         printf(_("Latest checkpoint's NextMultiXactId:  %u\n"),
552                    ControlFile.checkPointCopy.nextMulti);
553         printf(_("Latest checkpoint's NextMultiOffset:  %u\n"),
554                    ControlFile.checkPointCopy.nextMultiOffset);
555         printf(_("Maximum data alignment:               %u\n"),
556                    ControlFile.maxAlign);
557         /* we don't print floatFormat since can't say much useful about it */
558         printf(_("Database block size:                  %u\n"),
559                    ControlFile.blcksz);
560         printf(_("Blocks per segment of large relation: %u\n"),
561                    ControlFile.relseg_size);
562         printf(_("WAL block size:                       %u\n"),
563                    ControlFile.xlog_blcksz);
564         printf(_("Bytes per WAL segment:                %u\n"),
565                    ControlFile.xlog_seg_size);
566         printf(_("Maximum length of identifiers:        %u\n"),
567                    ControlFile.nameDataLen);
568         printf(_("Maximum columns in an index:          %u\n"),
569                    ControlFile.indexMaxKeys);
570         printf(_("Maximum size of a TOAST chunk:        %u\n"),
571                    ControlFile.toast_max_chunk_size);
572         printf(_("Date/time type storage:               %s\n"),
573                    (ControlFile.enableIntTimes ? _("64-bit integers") : _("floating-point numbers")));
574         printf(_("Float4 argument passing:              %s\n"),
575                    (ControlFile.float4ByVal ? _("by value") : _("by reference")));
576         printf(_("Float8 argument passing:              %s\n"),
577                    (ControlFile.float8ByVal ? _("by value") : _("by reference")));
578 }
579
580
581 /*
582  * Write out the new pg_control file.
583  */
584 static void
585 RewriteControlFile(void)
586 {
587         int                     fd;
588         char            buffer[PG_CONTROL_SIZE];                /* need not be aligned */
589
590         /*
591          * Adjust fields as needed to force an empty XLOG starting at
592          * newXlogId/newXlogSeg.
593          */
594         ControlFile.checkPointCopy.redo.xlogid = newXlogId;
595         ControlFile.checkPointCopy.redo.xrecoff =
596                 newXlogSeg * XLogSegSize + SizeOfXLogLongPHD;
597         ControlFile.checkPointCopy.time = (pg_time_t) time(NULL);
598
599         ControlFile.state = DB_SHUTDOWNED;
600         ControlFile.time = (pg_time_t) time(NULL);
601         ControlFile.checkPoint = ControlFile.checkPointCopy.redo;
602         ControlFile.prevCheckPoint.xlogid = 0;
603         ControlFile.prevCheckPoint.xrecoff = 0;
604         ControlFile.minRecoveryPoint.xlogid = 0;
605         ControlFile.minRecoveryPoint.xrecoff = 0;
606
607         /* Now we can force the recorded xlog seg size to the right thing. */
608         ControlFile.xlog_seg_size = XLogSegSize;
609
610         /* Contents are protected with a CRC */
611         INIT_CRC32(ControlFile.crc);
612         COMP_CRC32(ControlFile.crc,
613                            (char *) &ControlFile,
614                            offsetof(ControlFileData, crc));
615         FIN_CRC32(ControlFile.crc);
616
617         /*
618          * We write out PG_CONTROL_SIZE bytes into pg_control, zero-padding the
619          * excess over sizeof(ControlFileData).  This reduces the odds of
620          * premature-EOF errors when reading pg_control.  We'll still fail when we
621          * check the contents of the file, but hopefully with a more specific
622          * error than "couldn't read pg_control".
623          */
624         if (sizeof(ControlFileData) > PG_CONTROL_SIZE)
625         {
626                 fprintf(stderr,
627                                 _("%s: internal error -- sizeof(ControlFileData) is too large ... fix PG_CONTROL_SIZE\n"),
628                                 progname);
629                 exit(1);
630         }
631
632         memset(buffer, 0, PG_CONTROL_SIZE);
633         memcpy(buffer, &ControlFile, sizeof(ControlFileData));
634
635         unlink(XLOG_CONTROL_FILE);
636
637         fd = open(XLOG_CONTROL_FILE,
638                           O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
639                           S_IRUSR | S_IWUSR);
640         if (fd < 0)
641         {
642                 fprintf(stderr, _("%s: could not create pg_control file: %s\n"),
643                                 progname, strerror(errno));
644                 exit(1);
645         }
646
647         errno = 0;
648         if (write(fd, buffer, PG_CONTROL_SIZE) != PG_CONTROL_SIZE)
649         {
650                 /* if write didn't set errno, assume problem is no disk space */
651                 if (errno == 0)
652                         errno = ENOSPC;
653                 fprintf(stderr, _("%s: could not write pg_control file: %s\n"),
654                                 progname, strerror(errno));
655                 exit(1);
656         }
657
658         if (fsync(fd) != 0)
659         {
660                 fprintf(stderr, _("%s: fsync error: %s\n"), progname, strerror(errno));
661                 exit(1);
662         }
663
664         close(fd);
665 }
666
667
668 /*
669  * Scan existing XLOG files and determine the highest existing WAL address
670  *
671  * On entry, ControlFile.checkPointCopy.redo and ControlFile.xlog_seg_size
672  * are assumed valid (note that we allow the old xlog seg size to differ
673  * from what we're using).  On exit, newXlogId and newXlogSeg are set to
674  * suitable values for the beginning of replacement WAL (in our seg size).
675  */
676 static void
677 FindEndOfXLOG(void)
678 {
679         DIR                *xldir;
680         struct dirent *xlde;
681
682         /*
683          * Initialize the max() computation using the last checkpoint address from
684          * old pg_control.      Note that for the moment we are working with segment
685          * numbering according to the old xlog seg size.
686          */
687         newXlogId = ControlFile.checkPointCopy.redo.xlogid;
688         newXlogSeg = ControlFile.checkPointCopy.redo.xrecoff / ControlFile.xlog_seg_size;
689
690         /*
691          * Scan the pg_xlog directory to find existing WAL segment files. We
692          * assume any present have been used; in most scenarios this should be
693          * conservative, because of xlog.c's attempts to pre-create files.
694          */
695         xldir = opendir(XLOGDIR);
696         if (xldir == NULL)
697         {
698                 fprintf(stderr, _("%s: could not open directory \"%s\": %s\n"),
699                                 progname, XLOGDIR, strerror(errno));
700                 exit(1);
701         }
702
703         errno = 0;
704         while ((xlde = readdir(xldir)) != NULL)
705         {
706                 if (strlen(xlde->d_name) == 24 &&
707                         strspn(xlde->d_name, "0123456789ABCDEF") == 24)
708                 {
709                         unsigned int tli,
710                                                 log,
711                                                 seg;
712
713                         sscanf(xlde->d_name, "%08X%08X%08X", &tli, &log, &seg);
714
715                         /*
716                          * Note: we take the max of all files found, regardless of their
717                          * timelines.  Another possibility would be to ignore files of
718                          * timelines other than the target TLI, but this seems safer.
719                          * Better too large a result than too small...
720                          */
721                         if (log > newXlogId ||
722                                 (log == newXlogId && seg > newXlogSeg))
723                         {
724                                 newXlogId = log;
725                                 newXlogSeg = seg;
726                         }
727                 }
728                 errno = 0;
729         }
730 #ifdef WIN32
731
732         /*
733          * This fix is in mingw cvs (runtime/mingwex/dirent.c rev 1.4), but not in
734          * released version
735          */
736         if (GetLastError() == ERROR_NO_MORE_FILES)
737                 errno = 0;
738 #endif
739
740         if (errno)
741         {
742                 fprintf(stderr, _("%s: could not read from directory \"%s\": %s\n"),
743                                 progname, XLOGDIR, strerror(errno));
744                 exit(1);
745         }
746         closedir(xldir);
747
748         /*
749          * Finally, convert to new xlog seg size, and advance by one to ensure we
750          * are in virgin territory.
751          */
752         newXlogSeg *= ControlFile.xlog_seg_size;
753         newXlogSeg = (newXlogSeg + XLogSegSize - 1) / XLogSegSize;
754
755         /* be sure we wrap around correctly at end of a logfile */
756         NextLogSeg(newXlogId, newXlogSeg);
757 }
758
759
760 /*
761  * Remove existing XLOG files
762  */
763 static void
764 KillExistingXLOG(void)
765 {
766         DIR                *xldir;
767         struct dirent *xlde;
768         char            path[MAXPGPATH];
769
770         xldir = opendir(XLOGDIR);
771         if (xldir == NULL)
772         {
773                 fprintf(stderr, _("%s: could not open directory \"%s\": %s\n"),
774                                 progname, XLOGDIR, strerror(errno));
775                 exit(1);
776         }
777
778         errno = 0;
779         while ((xlde = readdir(xldir)) != NULL)
780         {
781                 if (strlen(xlde->d_name) == 24 &&
782                         strspn(xlde->d_name, "0123456789ABCDEF") == 24)
783                 {
784                         snprintf(path, MAXPGPATH, "%s/%s", XLOGDIR, xlde->d_name);
785                         if (unlink(path) < 0)
786                         {
787                                 fprintf(stderr, _("%s: could not delete file \"%s\": %s\n"),
788                                                 progname, path, strerror(errno));
789                                 exit(1);
790                         }
791                 }
792                 errno = 0;
793         }
794 #ifdef WIN32
795
796         /*
797          * This fix is in mingw cvs (runtime/mingwex/dirent.c rev 1.4), but not in
798          * released version
799          */
800         if (GetLastError() == ERROR_NO_MORE_FILES)
801                 errno = 0;
802 #endif
803
804         if (errno)
805         {
806                 fprintf(stderr, _("%s: could not read from directory \"%s\": %s\n"),
807                                 progname, XLOGDIR, strerror(errno));
808                 exit(1);
809         }
810         closedir(xldir);
811 }
812
813
814 /*
815  * Write an empty XLOG file, containing only the checkpoint record
816  * already set up in ControlFile.
817  */
818 static void
819 WriteEmptyXLOG(void)
820 {
821         char       *buffer;
822         XLogPageHeader page;
823         XLogLongPageHeader longpage;
824         XLogRecord *record;
825         pg_crc32        crc;
826         char            path[MAXPGPATH];
827         int                     fd;
828         int                     nbytes;
829
830         /* Use malloc() to ensure buffer is MAXALIGNED */
831         buffer = (char *) malloc(XLOG_BLCKSZ);
832         page = (XLogPageHeader) buffer;
833         memset(buffer, 0, XLOG_BLCKSZ);
834
835         /* Set up the XLOG page header */
836         page->xlp_magic = XLOG_PAGE_MAGIC;
837         page->xlp_info = XLP_LONG_HEADER;
838         page->xlp_tli = ControlFile.checkPointCopy.ThisTimeLineID;
839         page->xlp_pageaddr.xlogid =
840                 ControlFile.checkPointCopy.redo.xlogid;
841         page->xlp_pageaddr.xrecoff =
842                 ControlFile.checkPointCopy.redo.xrecoff - SizeOfXLogLongPHD;
843         longpage = (XLogLongPageHeader) page;
844         longpage->xlp_sysid = ControlFile.system_identifier;
845         longpage->xlp_seg_size = XLogSegSize;
846         longpage->xlp_xlog_blcksz = XLOG_BLCKSZ;
847
848         /* Insert the initial checkpoint record */
849         record = (XLogRecord *) ((char *) page + SizeOfXLogLongPHD);
850         record->xl_prev.xlogid = 0;
851         record->xl_prev.xrecoff = 0;
852         record->xl_xid = InvalidTransactionId;
853         record->xl_tot_len = SizeOfXLogRecord + sizeof(CheckPoint);
854         record->xl_len = sizeof(CheckPoint);
855         record->xl_info = XLOG_CHECKPOINT_SHUTDOWN;
856         record->xl_rmid = RM_XLOG_ID;
857         memcpy(XLogRecGetData(record), &ControlFile.checkPointCopy,
858                    sizeof(CheckPoint));
859
860         INIT_CRC32(crc);
861         COMP_CRC32(crc, &ControlFile.checkPointCopy, sizeof(CheckPoint));
862         COMP_CRC32(crc, (char *) record + sizeof(pg_crc32),
863                            SizeOfXLogRecord - sizeof(pg_crc32));
864         FIN_CRC32(crc);
865         record->xl_crc = crc;
866
867         /* Write the first page */
868         XLogFilePath(path, ControlFile.checkPointCopy.ThisTimeLineID,
869                                  newXlogId, newXlogSeg);
870
871         unlink(path);
872
873         fd = open(path, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
874                           S_IRUSR | S_IWUSR);
875         if (fd < 0)
876         {
877                 fprintf(stderr, _("%s: could not open file \"%s\": %s\n"),
878                                 progname, path, strerror(errno));
879                 exit(1);
880         }
881
882         errno = 0;
883         if (write(fd, buffer, XLOG_BLCKSZ) != XLOG_BLCKSZ)
884         {
885                 /* if write didn't set errno, assume problem is no disk space */
886                 if (errno == 0)
887                         errno = ENOSPC;
888                 fprintf(stderr, _("%s: could not write file \"%s\": %s\n"),
889                                 progname, path, strerror(errno));
890                 exit(1);
891         }
892
893         /* Fill the rest of the file with zeroes */
894         memset(buffer, 0, XLOG_BLCKSZ);
895         for (nbytes = XLOG_BLCKSZ; nbytes < XLogSegSize; nbytes += XLOG_BLCKSZ)
896         {
897                 errno = 0;
898                 if (write(fd, buffer, XLOG_BLCKSZ) != XLOG_BLCKSZ)
899                 {
900                         if (errno == 0)
901                                 errno = ENOSPC;
902                         fprintf(stderr, _("%s: could not write file \"%s\": %s\n"),
903                                         progname, path, strerror(errno));
904                         exit(1);
905                 }
906         }
907
908         if (fsync(fd) != 0)
909         {
910                 fprintf(stderr, _("%s: fsync error: %s\n"), progname, strerror(errno));
911                 exit(1);
912         }
913
914         close(fd);
915 }
916
917
918 static void
919 usage(void)
920 {
921         printf(_("%s resets the PostgreSQL transaction log.\n\n"), progname);
922         printf(_("Usage:\n  %s [OPTION]... DATADIR\n\n"), progname);
923         printf(_("Options:\n"));
924         printf(_("  -f              force update to be done\n"));
925         printf(_("  -l TLI,FILE,SEG force minimum WAL starting location for new transaction log\n"));
926         printf(_("  -m XID          set next multitransaction ID\n"));
927         printf(_("  -n              no update, just show extracted control values (for testing)\n"));
928         printf(_("  -o OID          set next OID\n"));
929         printf(_("  -O OFFSET       set next multitransaction offset\n"));
930         printf(_("  -x XID          set next transaction ID\n"));
931         printf(_("  -e XIDEPOCH     set next transaction ID epoch\n"));
932         printf(_("  --help          show this help, then exit\n"));
933         printf(_("  --version       output version information, then exit\n"));
934         printf(_("\nReport bugs to <pgsql-bugs@postgresql.org>.\n"));
935 }