~ubuntu-branches/ubuntu/oneiric/postgresql-9.1/oneiric-security

« back to all changes in this revision

Viewing changes to contrib/pg_archivecleanup/pg_archivecleanup.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2011-05-11 10:41:53 UTC
  • Revision ID: james.westby@ubuntu.com-20110511104153-psbh2o58553fv1m0
Tags: upstream-9.1~beta1
ImportĀ upstreamĀ versionĀ 9.1~beta1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * contrib/pg_archivecleanup/pg_archivecleanup.c
 
3
 *
 
4
 * pg_archivecleanup.c
 
5
 *
 
6
 * Production-ready example of an archive_cleanup_command
 
7
 * used to clean an archive when using standby_mode = on in 9.0
 
8
 * or for standalone use for any version of PostgreSQL 8.0+.
 
9
 *
 
10
 * Original author:             Simon Riggs  simon@2ndquadrant.com
 
11
 * Current maintainer:  Simon Riggs
 
12
 */
 
13
#include "postgres_fe.h"
 
14
 
 
15
#include <ctype.h>
 
16
#include <dirent.h>
 
17
#include <sys/stat.h>
 
18
#include <fcntl.h>
 
19
#include <signal.h>
 
20
 
 
21
#ifndef WIN32
 
22
#include <sys/time.h>
 
23
#include <unistd.h>
 
24
 
 
25
#ifdef HAVE_GETOPT_H
 
26
#include <getopt.h>
 
27
#endif
 
28
#else                                                   /* WIN32 */
 
29
extern int      getopt(int argc, char *const argv[], const char *optstring);
 
30
#endif   /* ! WIN32 */
 
31
 
 
32
extern char *optarg;
 
33
extern int      optind;
 
34
 
 
35
const char *progname;
 
36
 
 
37
/* Options and defaults */
 
38
bool            debug = false;          /* are we debugging? */
 
39
 
 
40
char       *archiveLocation;    /* where to find the archive? */
 
41
char       *restartWALFileName; /* the file from which we can restart restore */
 
42
char            WALFilePath[MAXPGPATH];         /* the file path including archive */
 
43
char            exclusiveCleanupFileName[MAXPGPATH];            /* the oldest file we
 
44
                                                                                                                 * want to remain in
 
45
                                                                                                                 * archive */
 
46
 
 
47
 
 
48
/* =====================================================================
 
49
 *
 
50
 *                Customizable section
 
51
 *
 
52
 * =====================================================================
 
53
 *
 
54
 *      Currently, this section assumes that the Archive is a locally
 
55
 *      accessible directory. If you want to make other assumptions,
 
56
 *      such as using a vendor-specific archive and access API, these
 
57
 *      routines are the ones you'll need to change. You're
 
58
 *      enouraged to submit any changes to pgsql-hackers@postgresql.org
 
59
 *      or personally to the current maintainer. Those changes may be
 
60
 *      folded in to later versions of this program.
 
61
 */
 
62
 
 
63
#define XLOG_DATA_FNAME_LEN             24
 
64
/* Reworked from access/xlog_internal.h */
 
65
#define XLogFileName(fname, tli, log, seg)      \
 
66
        snprintf(fname, XLOG_DATA_FNAME_LEN + 1, "%08X%08X%08X", tli, log, seg)
 
67
#define XLOG_BACKUP_FNAME_LEN   40
 
68
 
 
69
/*
 
70
 *      Initialize allows customized commands into the archive cleanup program.
 
71
 *
 
72
 *      You may wish to add code to check for tape libraries, etc..
 
73
 */
 
74
static void
 
75
Initialize(void)
 
76
{
 
77
        /*
 
78
         * This code assumes that archiveLocation is a directory, so we use stat
 
79
         * to test if it's accessible.
 
80
         */
 
81
        struct stat stat_buf;
 
82
 
 
83
        if (stat(archiveLocation, &stat_buf) != 0 ||
 
84
                !S_ISDIR(stat_buf.st_mode))
 
85
        {
 
86
                fprintf(stderr, "%s: archiveLocation \"%s\" does not exist\n",
 
87
                                progname, archiveLocation);
 
88
                exit(2);
 
89
        }
 
90
}
 
91
 
 
92
static void
 
93
CleanupPriorWALFiles(void)
 
94
{
 
95
        int                     rc;
 
96
        DIR                *xldir;
 
97
        struct dirent *xlde;
 
98
 
 
99
        if ((xldir = opendir(archiveLocation)) != NULL)
 
100
        {
 
101
                while ((xlde = readdir(xldir)) != NULL)
 
102
                {
 
103
                        /*
 
104
                         * We ignore the timeline part of the XLOG segment identifiers in
 
105
                         * deciding whether a segment is still needed.  This ensures that
 
106
                         * we won't prematurely remove a segment from a parent timeline.
 
107
                         * We could probably be a little more proactive about removing
 
108
                         * segments of non-parent timelines, but that would be a whole lot
 
109
                         * more complicated.
 
110
                         *
 
111
                         * We use the alphanumeric sorting property of the filenames to
 
112
                         * decide which ones are earlier than the exclusiveCleanupFileName
 
113
                         * file. Note that this means files are not removed in the order
 
114
                         * they were originally written, in case this worries you.
 
115
                         */
 
116
                        if (strlen(xlde->d_name) == XLOG_DATA_FNAME_LEN &&
 
117
                        strspn(xlde->d_name, "0123456789ABCDEF") == XLOG_DATA_FNAME_LEN &&
 
118
                                strcmp(xlde->d_name + 8, exclusiveCleanupFileName + 8) < 0)
 
119
                        {
 
120
                                snprintf(WALFilePath, MAXPGPATH, "%s/%s",
 
121
                                                 archiveLocation, xlde->d_name);
 
122
                                if (debug)
 
123
                                        fprintf(stderr, "%s: removing file \"%s\"\n",
 
124
                                                        progname, WALFilePath);
 
125
 
 
126
                                rc = unlink(WALFilePath);
 
127
                                if (rc != 0)
 
128
                                {
 
129
                                        fprintf(stderr, "%s: ERROR: could not remove file \"%s\": %s\n",
 
130
                                                        progname, WALFilePath, strerror(errno));
 
131
                                        break;
 
132
                                }
 
133
                        }
 
134
                }
 
135
                closedir(xldir);
 
136
        }
 
137
        else
 
138
                fprintf(stderr, "%s: could not open archiveLocation \"%s\": %s\n",
 
139
                                progname, archiveLocation, strerror(errno));
 
140
}
 
141
 
 
142
/*
 
143
 * SetWALFileNameForCleanup()
 
144
 *
 
145
 *        Set the earliest WAL filename that we want to keep on the archive
 
146
 *        and decide whether we need_cleanup
 
147
 */
 
148
static void
 
149
SetWALFileNameForCleanup(void)
 
150
{
 
151
        bool            fnameOK = false;
 
152
 
 
153
        /*
 
154
         * If restartWALFileName is a WAL file name then just use it directly. If
 
155
         * restartWALFileName is a .backup filename, make sure we use the prefix
 
156
         * of the filename, otherwise we will remove wrong files since
 
157
         * 000000010000000000000010.00000020.backup is after
 
158
         * 000000010000000000000010.
 
159
         */
 
160
        if (strlen(restartWALFileName) == XLOG_DATA_FNAME_LEN &&
 
161
                strspn(restartWALFileName, "0123456789ABCDEF") == XLOG_DATA_FNAME_LEN)
 
162
        {
 
163
                strcpy(exclusiveCleanupFileName, restartWALFileName);
 
164
                fnameOK = true;
 
165
        }
 
166
        else if (strlen(restartWALFileName) == XLOG_BACKUP_FNAME_LEN)
 
167
        {
 
168
                int                     args;
 
169
                uint32          tli = 1,
 
170
                                        log = 0,
 
171
                                        seg = 0,
 
172
                                        offset = 0;
 
173
 
 
174
                args = sscanf(restartWALFileName, "%08X%08X%08X.%08X.backup", &tli, &log, &seg, &offset);
 
175
                if (args == 4)
 
176
                {
 
177
                        fnameOK = true;
 
178
 
 
179
                        /*
 
180
                         * Use just the prefix of the filename, ignore everything after
 
181
                         * first period
 
182
                         */
 
183
                        XLogFileName(exclusiveCleanupFileName, tli, log, seg);
 
184
                }
 
185
        }
 
186
 
 
187
        if (!fnameOK)
 
188
        {
 
189
                fprintf(stderr, "%s: invalid filename input\n", progname);
 
190
                fprintf(stderr, "Try \"%s --help\" for more information.\n", progname);
 
191
                exit(2);
 
192
        }
 
193
}
 
194
 
 
195
/* =====================================================================
 
196
 *                End of Customizable section
 
197
 * =====================================================================
 
198
 */
 
199
 
 
200
static void
 
201
usage(void)
 
202
{
 
203
        printf("%s removes older WAL files from PostgreSQL archives.\n\n", progname);
 
204
        printf("Usage:\n");
 
205
        printf("  %s [OPTION]... ARCHIVELOCATION OLDESTKEPTWALFILE\n", progname);
 
206
        printf("\n"
 
207
                   "for use as an archive_cleanup_command in the recovery.conf when standby_mode = on:\n"
 
208
                   "  archive_cleanup_command = 'pg_archivecleanup [OPTION]... ARCHIVELOCATION %%r'\n"
 
209
                   "e.g.\n"
 
210
                   "  archive_cleanup_command = 'pg_archivecleanup /mnt/server/archiverdir %%r'\n");
 
211
        printf("\n"
 
212
                   "or for use as a standalone archive cleaner:\n"
 
213
                   "e.g.\n"
 
214
                   "  pg_archivecleanup /mnt/server/archiverdir 000000010000000000000010.00000020.backup\n");
 
215
        printf("\nOptions:\n");
 
216
        printf("  -d                 generates debug output (verbose mode)\n");
 
217
        printf("  --help             show this help, then exit\n");
 
218
        printf("  --version          output version information, then exit\n");
 
219
        printf("\nReport bugs to <pgsql-bugs@postgresql.org>.\n");
 
220
}
 
221
 
 
222
/*------------ MAIN ----------------------------------------*/
 
223
int
 
224
main(int argc, char **argv)
 
225
{
 
226
        int                     c;
 
227
 
 
228
        progname = get_progname(argv[0]);
 
229
 
 
230
        if (argc > 1)
 
231
        {
 
232
                if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
 
233
                {
 
234
                        usage();
 
235
                        exit(0);
 
236
                }
 
237
                if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
 
238
                {
 
239
                        puts("pg_archivecleanup (PostgreSQL) " PG_VERSION);
 
240
                        exit(0);
 
241
                }
 
242
        }
 
243
 
 
244
        while ((c = getopt(argc, argv, "d")) != -1)
 
245
        {
 
246
                switch (c)
 
247
                {
 
248
                        case 'd':                       /* Debug mode */
 
249
                                debug = true;
 
250
                                break;
 
251
                        default:
 
252
                                fprintf(stderr, "Try \"%s --help\" for more information.\n", progname);
 
253
                                exit(2);
 
254
                                break;
 
255
                }
 
256
        }
 
257
 
 
258
        /*
 
259
         * We will go to the archiveLocation to check restartWALFileName.
 
260
         * restartWALFileName may not exist anymore, which would not be an error,
 
261
         * so we separate the archiveLocation and restartWALFileName so we can
 
262
         * check separately whether archiveLocation exists, if not that is an
 
263
         * error
 
264
         */
 
265
        if (optind < argc)
 
266
        {
 
267
                archiveLocation = argv[optind];
 
268
                optind++;
 
269
        }
 
270
        else
 
271
        {
 
272
                fprintf(stderr, "%s: must specify archive location\n", progname);
 
273
                fprintf(stderr, "Try \"%s --help\" for more information.\n", progname);
 
274
                exit(2);
 
275
        }
 
276
 
 
277
        if (optind < argc)
 
278
        {
 
279
                restartWALFileName = argv[optind];
 
280
                optind++;
 
281
        }
 
282
        else
 
283
        {
 
284
                fprintf(stderr, "%s: must specify restartfilename\n", progname);
 
285
                fprintf(stderr, "Try \"%s --help\" for more information.\n", progname);
 
286
                exit(2);
 
287
        }
 
288
 
 
289
        if (optind < argc)
 
290
        {
 
291
                fprintf(stderr, "%s: too many parameters\n", progname);
 
292
                fprintf(stderr, "Try \"%s --help\" for more information.\n", progname);
 
293
                exit(2);
 
294
        }
 
295
 
 
296
        /*
 
297
         * Check archive exists and other initialization if required.
 
298
         */
 
299
        Initialize();
 
300
 
 
301
        /*
 
302
         * Check filename is a valid name, then process to find cut-off
 
303
         */
 
304
        SetWALFileNameForCleanup();
 
305
 
 
306
        if (debug)
 
307
        {
 
308
                snprintf(WALFilePath, MAXPGPATH, "%s/%s",
 
309
                                 archiveLocation, exclusiveCleanupFileName);
 
310
                fprintf(stderr, "%s: keep WAL file \"%s\" and later\n",
 
311
                                progname, WALFilePath);
 
312
        }
 
313
 
 
314
        /*
 
315
         * Remove WAL files older than cut-off
 
316
         */
 
317
        CleanupPriorWALFiles();
 
318
 
 
319
        exit(0);
 
320
}