| 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 | |