/*------------------------------------------------------------------------- * * show.c: show backup information. * * Portions Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION * Portions Copyright (c) 2015-2017, Postgres Professional * *------------------------------------------------------------------------- */ #include "pg_probackup.h" #include static const char *backupModes[] = {"", "PAGE", "PTRACK", "FULL"}; static void show_backup_list(FILE *out, parray *backup_list); static void show_backup_detail(FILE *out, pgBackup *backup); /* * If 'requested_backup_id' is INVALID_BACKUP_ID, show brief meta information * about all backups in the backup catalog. * If valid backup id is passed, show detailed meta information * about specified backup. */ int do_show(time_t requested_backup_id) { if (requested_backup_id != INVALID_BACKUP_ID) { pgBackup *backup; backup = read_backup(requested_backup_id); if (backup == NULL) { elog(INFO, "Requested backup \"%s\" is not found.", base36enc(requested_backup_id)); /* This is not error */ return 0; } show_backup_detail(stdout, backup); /* cleanup */ pgBackupFree(backup); } else { parray *backup_list; backup_list = catalog_get_backup_list(INVALID_BACKUP_ID); if (backup_list == NULL) elog(ERROR, "Failed to get backup list."); show_backup_list(stdout, backup_list); /* cleanup */ parray_walk(backup_list, pgBackupFree); parray_free(backup_list); } return 0; } static void pretty_size(int64 size, char *buf, size_t len) { int exp = 0; /* minus means the size is invalid */ if (size < 0) { strncpy(buf, "----", len); return; } /* determine postfix */ while (size > 9999) { ++exp; size /= 1000; } switch (exp) { case 0: snprintf(buf, len, INT64_FORMAT "B", size); break; case 1: snprintf(buf, len, INT64_FORMAT "kB", size); break; case 2: snprintf(buf, len, INT64_FORMAT "MB", size); break; case 3: snprintf(buf, len, INT64_FORMAT "GB", size); break; case 4: snprintf(buf, len, INT64_FORMAT "TB", size); break; case 5: snprintf(buf, len, INT64_FORMAT "PB", size); break; default: strncpy(buf, "***", len); break; } } /* TODO Add comment */ static TimeLineID get_parent_tli(TimeLineID child_tli) { TimeLineID result = 0; char path[MAXPGPATH]; char fline[MAXPGPATH]; FILE *fd; /* Search history file in archives */ snprintf(path, lengthof(path), "%s/%08X.history", arclog_path, child_tli); fd = fopen(path, "rt"); if (fd == NULL) { if (errno != ENOENT) elog(ERROR, "could not open file \"%s\": %s", path, strerror(errno)); return 0; } /* * Parse the file... */ while (fgets(fline, sizeof(fline), fd) != NULL) { /* skip leading whitespace and check for # comment */ char *ptr; char *endptr; for (ptr = fline; *ptr; ptr++) { if (!IsSpace(*ptr)) break; } if (*ptr == '\0' || *ptr == '#') continue; /* expect a numeric timeline ID as first field of line */ result = (TimeLineID) strtoul(ptr, &endptr, 0); if (endptr == ptr) elog(ERROR, "syntax error(timeline ID) in history file: %s", fline); } fclose(fd); /* TLI of the last line is parent TLI */ return result; } static void show_backup_list(FILE *out, parray *backup_list) { int i; /* show header */ fputs("==================================================================================================================\n", out); fputs("ID Recovery time Mode Current/Parent TLI Time Data Start LSN Stop LSN Status \n", out); fputs("==================================================================================================================\n", out); for (i = 0; i < parray_num(backup_list); i++) { pgBackup *backup = parray_get(backup_list, i); TimeLineID parent_tli; char timestamp[20] = "----"; char duration[20] = "----"; char data_bytes_str[10] = "----"; if (backup->recovery_time != (time_t) 0) time2iso(timestamp, lengthof(timestamp), backup->recovery_time); if (backup->end_time != (time_t) 0) snprintf(duration, lengthof(duration), "%.*lfs", 0, difftime(backup->end_time, backup->start_time)); /* * Calculate Data field, in the case of full backup this shows the * total amount of data. For an differential backup, this size is only * the difference of data accumulated. */ pretty_size(backup->data_bytes, data_bytes_str, lengthof(data_bytes_str)); /* Get parent timeline before printing */ parent_tli = get_parent_tli(backup->tli); fprintf(out, "%-8s %-19s %s%-9s %2d / %d %5s %6s %2X/%08X %2X/%08X %-8s\n", base36enc(backup->start_time), timestamp, backupModes[backup->backup_mode], backup->stream ? "+STREAM": "+ARCHIVE", backup->tli, parent_tli, duration, data_bytes_str, (uint32) (backup->start_lsn >> 32), (uint32) backup->start_lsn, (uint32) (backup->stop_lsn >> 32), (uint32) backup->stop_lsn, status2str(backup->status)); } } static void show_backup_detail(FILE *out, pgBackup *backup) { pgBackupWriteControl(out, backup); }