1
0
mirror of https://github.com/postgrespro/pg_probackup.git synced 2024-11-28 09:33:54 +02:00
pg_probackup/show.c

265 lines
5.9 KiB
C

/*-------------------------------------------------------------------------
*
* show.c: show backup catalog.
*
* Portions Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
* Portions Copyright (c) 2015-2017, Postgres Professional
*
*-------------------------------------------------------------------------
*/
#include "pg_probackup.h"
static void show_backup_list(FILE *out, parray *backup_list);
static void show_backup_detail(FILE *out, pgBackup *backup);
/*
* Show backup catalog information.
* If range is { 0, 0 }, show list of all backup, otherwise show detail of the
* backup indicated by id.
*/
int
do_show(time_t backup_id)
{
/*
* Safety check for archive folder, this is necessary to fetch
* the parent TLI from history field generated by server after
* child timeline is chosen.
*/
if (backup_id != 0)
{
pgBackup *backup;
pid_t run_pid;
backup = read_backup(backup_id);
if (backup == NULL)
{
char timestamp[100];
time2iso(timestamp, lengthof(timestamp), backup_id);
elog(INFO, "backup taken at \"%s\" does not exist.",
timestamp);
/* This is not error case */
return 0;
}
/* Fix backup status */
if (backup->status == BACKUP_STATUS_RUNNING)
{
catalog_lock(false, &run_pid);
if (run_pid == 0)
{
backup->status = BACKUP_STATUS_ERROR;
pgBackupWriteIni(backup);
}
}
show_backup_detail(stdout, backup);
/* cleanup */
pgBackupFree(backup);
}
else
{
parray *backup_list;
backup_list = catalog_get_backup_list(backup_id);
if (backup_list == NULL)
elog(ERROR, "can't process any more.");
show_backup_list(stdout, backup_list);
/* cleanup */
parray_walk(backup_list, pgBackupFree);
parray_free(backup_list);
}
return 0;
}
/*
* Show backup catalog retention policy configuration.
*/
int
do_retention_show(void)
{
if (retention_redundancy == 0 && retention_window == 0)
{
fprintf(stdout, "No retention policy is set\n");
return 0;
}
fprintf(stdout, "# retention policy\n");
if (retention_redundancy > 0)
fprintf(stdout, "REDUNDANCY=%u\n", retention_redundancy);
if (retention_window > 0)
fprintf(stdout, "WINDOW=%u\n", retention_window);
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;
}
}
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;
pid_t run_pid = -1;
/* show header */
fputs("=========================================================================================\n", out);
fputs("ID Recovery time Mode Current/Parent TLI Time Data Status \n", out);
fputs("=========================================================================================\n", out);
for (i = 0; i < parray_num(backup_list); i++)
{
pgBackup *backup = parray_get(backup_list, i);
const char *modes[] = {"", "PAGE", "PTRACK", "FULL", "", "PAGE+STREAM", "PTRACK+STREAM", "FULL+STREAM"};
TimeLineID parent_tli;
char timestamp[20] = "----";
char duration[20] = "----";
char data_bytes_str[10] = "----";
/* Fix backup status */
if (backup->status == BACKUP_STATUS_RUNNING)
{
if (run_pid == -1)
catalog_lock(false, &run_pid);
if (run_pid == 0 || i + 1 < parray_num(backup_list))
backup->status = BACKUP_STATUS_ERROR;
if (run_pid == 0)
pgBackupWriteIni(backup);
}
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), "%lum",
(backup->end_time - backup->start_time) / 60);
/*
* 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 %-13s %2d /%2d %5s %6s %s \n",
base36enc(backup->start_time),
timestamp,
modes[backup->backup_mode + (BACKUP_MODE_FULL + 1)*backup->stream],
backup->tli,
parent_tli,
duration,
data_bytes_str,
status2str(backup->status));
}
}
static void
show_backup_detail(FILE *out, pgBackup *backup)
{
pgBackupWriteConfigSection(out, backup);
pgBackupWriteResultSection(out, backup);
}