1 | /* |
2 | * pg_controldata |
3 | * |
4 | * reads the data from $PGDATA/global/pg_control |
5 | * |
6 | * copyright (c) Oliver Elphick <olly@lfix.co.uk>, 2001; |
7 | * licence: BSD |
8 | * |
9 | * src/bin/pg_controldata/pg_controldata.c |
10 | */ |
11 | |
12 | /* |
13 | * We have to use postgres.h not postgres_fe.h here, because there's so much |
14 | * backend-only stuff in the XLOG include files we need. But we need a |
15 | * frontend-ish environment otherwise. Hence this ugly hack. |
16 | */ |
17 | #define FRONTEND 1 |
18 | |
19 | #include "postgres.h" |
20 | |
21 | #include <time.h> |
22 | |
23 | #include "access/transam.h" |
24 | #include "access/xlog.h" |
25 | #include "access/xlog_internal.h" |
26 | #include "catalog/pg_control.h" |
27 | #include "common/controldata_utils.h" |
28 | #include "common/logging.h" |
29 | #include "pg_getopt.h" |
30 | #include "getopt_long.h" |
31 | |
32 | |
33 | static void |
34 | usage(const char *progname) |
35 | { |
36 | printf(_("%s displays control information of a PostgreSQL database cluster.\n\n" ), progname); |
37 | printf(_("Usage:\n" )); |
38 | printf(_(" %s [OPTION] [DATADIR]\n" ), progname); |
39 | printf(_("\nOptions:\n" )); |
40 | printf(_(" [-D, --pgdata=]DATADIR data directory\n" )); |
41 | printf(_(" -V, --version output version information, then exit\n" )); |
42 | printf(_(" -?, --help show this help, then exit\n" )); |
43 | printf(_("\nIf no data directory (DATADIR) is specified, " |
44 | "the environment variable PGDATA\nis used.\n\n" )); |
45 | printf(_("Report bugs to <pgsql-bugs@lists.postgresql.org>.\n" )); |
46 | } |
47 | |
48 | |
49 | static const char * |
50 | dbState(DBState state) |
51 | { |
52 | switch (state) |
53 | { |
54 | case DB_STARTUP: |
55 | return _("starting up" ); |
56 | case DB_SHUTDOWNED: |
57 | return _("shut down" ); |
58 | case DB_SHUTDOWNED_IN_RECOVERY: |
59 | return _("shut down in recovery" ); |
60 | case DB_SHUTDOWNING: |
61 | return _("shutting down" ); |
62 | case DB_IN_CRASH_RECOVERY: |
63 | return _("in crash recovery" ); |
64 | case DB_IN_ARCHIVE_RECOVERY: |
65 | return _("in archive recovery" ); |
66 | case DB_IN_PRODUCTION: |
67 | return _("in production" ); |
68 | } |
69 | return _("unrecognized status code" ); |
70 | } |
71 | |
72 | static const char * |
73 | wal_level_str(WalLevel wal_level) |
74 | { |
75 | switch (wal_level) |
76 | { |
77 | case WAL_LEVEL_MINIMAL: |
78 | return "minimal" ; |
79 | case WAL_LEVEL_REPLICA: |
80 | return "replica" ; |
81 | case WAL_LEVEL_LOGICAL: |
82 | return "logical" ; |
83 | } |
84 | return _("unrecognized wal_level" ); |
85 | } |
86 | |
87 | |
88 | int |
89 | main(int argc, char *argv[]) |
90 | { |
91 | static struct option long_options[] = { |
92 | {"pgdata" , required_argument, NULL, 'D'}, |
93 | {NULL, 0, NULL, 0} |
94 | }; |
95 | |
96 | ControlFileData *ControlFile; |
97 | bool crc_ok; |
98 | char *DataDir = NULL; |
99 | time_t time_tmp; |
100 | char pgctime_str[128]; |
101 | char ckpttime_str[128]; |
102 | char sysident_str[32]; |
103 | char mock_auth_nonce_str[MOCK_AUTH_NONCE_LEN * 2 + 1]; |
104 | const char *strftime_fmt = "%c" ; |
105 | const char *progname; |
106 | char xlogfilename[MAXFNAMELEN]; |
107 | int c; |
108 | int i; |
109 | int WalSegSz; |
110 | |
111 | pg_logging_init(argv[0]); |
112 | set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_controldata" )); |
113 | progname = get_progname(argv[0]); |
114 | |
115 | if (argc > 1) |
116 | { |
117 | if (strcmp(argv[1], "--help" ) == 0 || strcmp(argv[1], "-?" ) == 0) |
118 | { |
119 | usage(progname); |
120 | exit(0); |
121 | } |
122 | if (strcmp(argv[1], "--version" ) == 0 || strcmp(argv[1], "-V" ) == 0) |
123 | { |
124 | puts("pg_controldata (PostgreSQL) " PG_VERSION); |
125 | exit(0); |
126 | } |
127 | } |
128 | |
129 | while ((c = getopt_long(argc, argv, "D:" , long_options, NULL)) != -1) |
130 | { |
131 | switch (c) |
132 | { |
133 | case 'D': |
134 | DataDir = optarg; |
135 | break; |
136 | |
137 | default: |
138 | fprintf(stderr, _("Try \"%s --help\" for more information.\n" ), progname); |
139 | exit(1); |
140 | } |
141 | } |
142 | |
143 | if (DataDir == NULL) |
144 | { |
145 | if (optind < argc) |
146 | DataDir = argv[optind++]; |
147 | else |
148 | DataDir = getenv("PGDATA" ); |
149 | } |
150 | |
151 | /* Complain if any arguments remain */ |
152 | if (optind < argc) |
153 | { |
154 | pg_log_error("too many command-line arguments (first is \"%s\")" , |
155 | argv[optind]); |
156 | fprintf(stderr, _("Try \"%s --help\" for more information.\n" ), |
157 | progname); |
158 | exit(1); |
159 | } |
160 | |
161 | if (DataDir == NULL) |
162 | { |
163 | pg_log_error("no data directory specified" ); |
164 | fprintf(stderr, _("Try \"%s --help\" for more information.\n" ), progname); |
165 | exit(1); |
166 | } |
167 | |
168 | /* get a copy of the control file */ |
169 | ControlFile = get_controlfile(DataDir, &crc_ok); |
170 | if (!crc_ok) |
171 | printf(_("WARNING: Calculated CRC checksum does not match value stored in file.\n" |
172 | "Either the file is corrupt, or it has a different layout than this program\n" |
173 | "is expecting. The results below are untrustworthy.\n\n" )); |
174 | |
175 | /* set wal segment size */ |
176 | WalSegSz = ControlFile->xlog_seg_size; |
177 | |
178 | if (!IsValidWalSegSize(WalSegSz)) |
179 | { |
180 | printf(_("WARNING: invalid WAL segment size\n" )); |
181 | printf(ngettext("The WAL segment size stored in the file, %d byte, is not a power of two\n" |
182 | "between 1 MB and 1 GB. The file is corrupt and the results below are\n" |
183 | "untrustworthy.\n\n" , |
184 | "The WAL segment size stored in the file, %d bytes, is not a power of two\n" |
185 | "between 1 MB and 1 GB. The file is corrupt and the results below are\n" |
186 | "untrustworthy.\n\n" , |
187 | WalSegSz), |
188 | WalSegSz); |
189 | } |
190 | |
191 | /* |
192 | * This slightly-chintzy coding will work as long as the control file |
193 | * timestamps are within the range of time_t; that should be the case in |
194 | * all foreseeable circumstances, so we don't bother importing the |
195 | * backend's timezone library into pg_controldata. |
196 | * |
197 | * Use variable for format to suppress overly-anal-retentive gcc warning |
198 | * about %c |
199 | */ |
200 | time_tmp = (time_t) ControlFile->time; |
201 | strftime(pgctime_str, sizeof(pgctime_str), strftime_fmt, |
202 | localtime(&time_tmp)); |
203 | time_tmp = (time_t) ControlFile->checkPointCopy.time; |
204 | strftime(ckpttime_str, sizeof(ckpttime_str), strftime_fmt, |
205 | localtime(&time_tmp)); |
206 | |
207 | /* |
208 | * Calculate name of the WAL file containing the latest checkpoint's REDO |
209 | * start point. |
210 | * |
211 | * A corrupted control file could report a WAL segment size of 0, and to |
212 | * guard against division by zero, we need to treat that specially. |
213 | */ |
214 | if (WalSegSz != 0) |
215 | { |
216 | XLogSegNo segno; |
217 | |
218 | XLByteToSeg(ControlFile->checkPointCopy.redo, segno, WalSegSz); |
219 | XLogFileName(xlogfilename, ControlFile->checkPointCopy.ThisTimeLineID, |
220 | segno, WalSegSz); |
221 | } |
222 | else |
223 | strcpy(xlogfilename, _("???" )); |
224 | |
225 | /* |
226 | * Format system_identifier and mock_authentication_nonce separately to |
227 | * keep platform-dependent format code out of the translatable message |
228 | * string. |
229 | */ |
230 | snprintf(sysident_str, sizeof(sysident_str), UINT64_FORMAT, |
231 | ControlFile->system_identifier); |
232 | for (i = 0; i < MOCK_AUTH_NONCE_LEN; i++) |
233 | snprintf(&mock_auth_nonce_str[i * 2], 3, "%02x" , |
234 | (unsigned char) ControlFile->mock_authentication_nonce[i]); |
235 | |
236 | printf(_("pg_control version number: %u\n" ), |
237 | ControlFile->pg_control_version); |
238 | printf(_("Catalog version number: %u\n" ), |
239 | ControlFile->catalog_version_no); |
240 | printf(_("Database system identifier: %s\n" ), |
241 | sysident_str); |
242 | printf(_("Database cluster state: %s\n" ), |
243 | dbState(ControlFile->state)); |
244 | printf(_("pg_control last modified: %s\n" ), |
245 | pgctime_str); |
246 | printf(_("Latest checkpoint location: %X/%X\n" ), |
247 | (uint32) (ControlFile->checkPoint >> 32), |
248 | (uint32) ControlFile->checkPoint); |
249 | printf(_("Latest checkpoint's REDO location: %X/%X\n" ), |
250 | (uint32) (ControlFile->checkPointCopy.redo >> 32), |
251 | (uint32) ControlFile->checkPointCopy.redo); |
252 | printf(_("Latest checkpoint's REDO WAL file: %s\n" ), |
253 | xlogfilename); |
254 | printf(_("Latest checkpoint's TimeLineID: %u\n" ), |
255 | ControlFile->checkPointCopy.ThisTimeLineID); |
256 | printf(_("Latest checkpoint's PrevTimeLineID: %u\n" ), |
257 | ControlFile->checkPointCopy.PrevTimeLineID); |
258 | printf(_("Latest checkpoint's full_page_writes: %s\n" ), |
259 | ControlFile->checkPointCopy.fullPageWrites ? _("on" ) : _("off" )); |
260 | printf(_("Latest checkpoint's NextXID: %u:%u\n" ), |
261 | EpochFromFullTransactionId(ControlFile->checkPointCopy.nextFullXid), |
262 | XidFromFullTransactionId(ControlFile->checkPointCopy.nextFullXid)); |
263 | printf(_("Latest checkpoint's NextOID: %u\n" ), |
264 | ControlFile->checkPointCopy.nextOid); |
265 | printf(_("Latest checkpoint's NextMultiXactId: %u\n" ), |
266 | ControlFile->checkPointCopy.nextMulti); |
267 | printf(_("Latest checkpoint's NextMultiOffset: %u\n" ), |
268 | ControlFile->checkPointCopy.nextMultiOffset); |
269 | printf(_("Latest checkpoint's oldestXID: %u\n" ), |
270 | ControlFile->checkPointCopy.oldestXid); |
271 | printf(_("Latest checkpoint's oldestXID's DB: %u\n" ), |
272 | ControlFile->checkPointCopy.oldestXidDB); |
273 | printf(_("Latest checkpoint's oldestActiveXID: %u\n" ), |
274 | ControlFile->checkPointCopy.oldestActiveXid); |
275 | printf(_("Latest checkpoint's oldestMultiXid: %u\n" ), |
276 | ControlFile->checkPointCopy.oldestMulti); |
277 | printf(_("Latest checkpoint's oldestMulti's DB: %u\n" ), |
278 | ControlFile->checkPointCopy.oldestMultiDB); |
279 | printf(_("Latest checkpoint's oldestCommitTsXid:%u\n" ), |
280 | ControlFile->checkPointCopy.oldestCommitTsXid); |
281 | printf(_("Latest checkpoint's newestCommitTsXid:%u\n" ), |
282 | ControlFile->checkPointCopy.newestCommitTsXid); |
283 | printf(_("Time of latest checkpoint: %s\n" ), |
284 | ckpttime_str); |
285 | printf(_("Fake LSN counter for unlogged rels: %X/%X\n" ), |
286 | (uint32) (ControlFile->unloggedLSN >> 32), |
287 | (uint32) ControlFile->unloggedLSN); |
288 | printf(_("Minimum recovery ending location: %X/%X\n" ), |
289 | (uint32) (ControlFile->minRecoveryPoint >> 32), |
290 | (uint32) ControlFile->minRecoveryPoint); |
291 | printf(_("Min recovery ending loc's timeline: %u\n" ), |
292 | ControlFile->minRecoveryPointTLI); |
293 | printf(_("Backup start location: %X/%X\n" ), |
294 | (uint32) (ControlFile->backupStartPoint >> 32), |
295 | (uint32) ControlFile->backupStartPoint); |
296 | printf(_("Backup end location: %X/%X\n" ), |
297 | (uint32) (ControlFile->backupEndPoint >> 32), |
298 | (uint32) ControlFile->backupEndPoint); |
299 | printf(_("End-of-backup record required: %s\n" ), |
300 | ControlFile->backupEndRequired ? _("yes" ) : _("no" )); |
301 | printf(_("wal_level setting: %s\n" ), |
302 | wal_level_str(ControlFile->wal_level)); |
303 | printf(_("wal_log_hints setting: %s\n" ), |
304 | ControlFile->wal_log_hints ? _("on" ) : _("off" )); |
305 | printf(_("max_connections setting: %d\n" ), |
306 | ControlFile->MaxConnections); |
307 | printf(_("max_worker_processes setting: %d\n" ), |
308 | ControlFile->max_worker_processes); |
309 | printf(_("max_wal_senders setting: %d\n" ), |
310 | ControlFile->max_wal_senders); |
311 | printf(_("max_prepared_xacts setting: %d\n" ), |
312 | ControlFile->max_prepared_xacts); |
313 | printf(_("max_locks_per_xact setting: %d\n" ), |
314 | ControlFile->max_locks_per_xact); |
315 | printf(_("track_commit_timestamp setting: %s\n" ), |
316 | ControlFile->track_commit_timestamp ? _("on" ) : _("off" )); |
317 | printf(_("Maximum data alignment: %u\n" ), |
318 | ControlFile->maxAlign); |
319 | /* we don't print floatFormat since can't say much useful about it */ |
320 | printf(_("Database block size: %u\n" ), |
321 | ControlFile->blcksz); |
322 | printf(_("Blocks per segment of large relation: %u\n" ), |
323 | ControlFile->relseg_size); |
324 | printf(_("WAL block size: %u\n" ), |
325 | ControlFile->xlog_blcksz); |
326 | printf(_("Bytes per WAL segment: %u\n" ), |
327 | ControlFile->xlog_seg_size); |
328 | printf(_("Maximum length of identifiers: %u\n" ), |
329 | ControlFile->nameDataLen); |
330 | printf(_("Maximum columns in an index: %u\n" ), |
331 | ControlFile->indexMaxKeys); |
332 | printf(_("Maximum size of a TOAST chunk: %u\n" ), |
333 | ControlFile->toast_max_chunk_size); |
334 | printf(_("Size of a large-object chunk: %u\n" ), |
335 | ControlFile->loblksize); |
336 | /* This is no longer configurable, but users may still expect to see it: */ |
337 | printf(_("Date/time type storage: %s\n" ), |
338 | _("64-bit integers" )); |
339 | printf(_("Float4 argument passing: %s\n" ), |
340 | (ControlFile->float4ByVal ? _("by value" ) : _("by reference" ))); |
341 | printf(_("Float8 argument passing: %s\n" ), |
342 | (ControlFile->float8ByVal ? _("by value" ) : _("by reference" ))); |
343 | printf(_("Data page checksum version: %u\n" ), |
344 | ControlFile->data_checksum_version); |
345 | printf(_("Mock authentication nonce: %s\n" ), |
346 | mock_auth_nonce_str); |
347 | return 0; |
348 | } |
349 | |