1
0
mirror of https://github.com/postgrespro/pg_probackup.git synced 2025-03-29 22:55:18 +02:00

Convert newline characters from CRLF to LF.

git-svn-id: http://pg-rman.googlecode.com/svn/trunk@70 182aca00-e38e-11de-a668-6fd11605f5ce
This commit is contained in:
otsuka.knj@gmail.com 2013-01-24 06:35:48 +00:00
parent dfecc77c5e
commit 98b6bac4b9
36 changed files with 11447 additions and 11447 deletions

1250
catalog.c

File diff suppressed because it is too large Load Diff

624
clean.c

@ -1,312 +1,312 @@
/*-------------------------------------------------------------------------
*
* clean.c: cleanup backup files.
*
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
#include <dirent.h>
#include <unistd.h>
static void clean_backup(Database db, pgBackup *backup);
#define CLEAN_MASK (BACKUP_MASK(BACKUP_ERROR) | BACKUP_MASK(BACKUP_BAD))
void
do_clean(int keep_data_generations,
int keep_data_days,
int keep_srvlog_files,
int keep_srvlog_days)
{
Database db;
List *backups;
ListCell *cell;
db = db_open();
backups = db_list_backups(db, make_range(0, NULL), CLEAN_MASK);
foreach (cell, backups)
clean_backup(db, lfirst(cell));
db_close(db);
list_free_deep(backups);
}
/*
* Delete files of the backup and update the status to DELETED.
*/
static void
clean_backup(Database db, pgBackup *backup)
{
char datetime[DATESTRLEN];
char path[MAXPGPATH];
elog(INFO, "clean: %s", date2str(datetime, backup->start_time));
/*
* update the status to BAD before the actual deletion because abort
* during deletion could leave corrupted backup files.
*/
if (backup->status != BACKUP_BAD)
{
backup->status = BACKUP_BAD;
db_update_status(db, backup, NIL);
}
/* remove data files. */
make_backup_path(path, backup->start_time);
remove_file(path);
/* update the status to DELETED */
backup->status = BACKUP_DELETED;
db_update_status(db, backup, NIL);
}
#if 0
/* that are older than KEEP_xxx_DAYS and have more generations
* than KEEP_xxx_FILES.
*/
int i;
parray *backup_list;
int backup_num;
time_t days_threshold = current.start_time - (keep_days * 60 * 60 * 24);
/* cleanup files which satisfy both condition */
if (keep_generations == KEEP_INFINITE || keep_days == KEEP_INFINITE)
{
elog(LOG, "%s() infinite", __FUNCTION__);
return;
}
/* get list of backups. */
backup_list = catalog_get_backup_list(NULL);
/*
* Remove backup files
*/
void
clean_backup(int keep_generations, int keep_days)
{
backup_num = 0;
/* find cleanup target backup. */
for (i = 0; i < parray_num(backup_list); i++)
{
pgBackup *backup = (pgBackup *)parray_get(backup_list, i);
elog(LOG, "%s() %lu", __FUNCTION__, backup->start_time);
/*
* when verify full backup was found, we can cleanup the backup
* that is older than it
*/
if (backup->backup_mode >= BACKUP_MODE_FULL &&
backup->status == BACKUP_OK)
backup_num++;
/* do not include the latest full backup in a count. */
if (backup_num - 1 <= keep_generations)
{
elog(LOG, "%s() backup are only %d", __FUNCTION__, backup_num);
continue;
}
/*
* If the start time of the backup is older than the threshold and
* there are enough generations of full backups, cleanup the backup.
*/
if (backup->start_time >= days_threshold)
{
elog(LOG, "%s() %lu is not older than %lu", __FUNCTION__,
backup->start_time, days_threshold);
continue;
}
}
}
static void delete_old_files(const char *root, parray *files, int keep_files,
int keep_days, int server_version, bool is_arclog);
static void delete_arclog_link(void);
static void delete_online_wal_backup(void);
/*
* Delete files modified before than KEEP_xxx_DAYS or more than KEEP_xxx_FILES
* of newer files exist.
*/
static void
delete_old_files(const char *root,
parray *files,
int keep_files,
int keep_days,
int server_version,
bool is_arclog)
{
int i;
int j;
int file_num = 0;
time_t days_threshold = start_time - (keep_days * 60 * 60 * 24);
if (verbose)
{
char files_str[100];
char days_str[100];
if (keep_files == KEEP_INFINITE)
strncpy(files_str, "INFINITE", lengthof(files_str));
else
snprintf(files_str, lengthof(files_str), "%d", keep_files);
if (keep_days == KEEP_INFINITE)
strncpy(days_str, "INFINITE", lengthof(days_str));
else
snprintf(days_str, lengthof(days_str), "%d", keep_days);
printf("cleanup old files from \"%s\" (files=%s, days=%s)\n",
root, files_str, days_str);
}
/* cleanup files which satisfy both conditions */
if (keep_files == KEEP_INFINITE || keep_days == KEEP_INFINITE)
{
elog(LOG, "%s() infinite", __FUNCTION__);
return;
}
parray_qsort(files, pgFileCompareMtime);
for (i = parray_num(files) - 1; i >= 0; i--)
{
pgFile *file = (pgFile *) parray_get(files, i);
elog(LOG, "%s() %s", __FUNCTION__, file->path);
/* Delete completed WALs only. */
if (is_arclog && !xlog_completed(file, server_version))
{
elog(LOG, "%s() not complete WAL", __FUNCTION__);
continue;
}
file_num++;
/*
* If the mtime of the file is older than the threshold and there are
* enough number of files newer than the files, cleanup the file.
*/
if (file->mtime >= days_threshold)
{
elog(LOG, "%s() %lu is not older than %lu", __FUNCTION__,
file->mtime, days_threshold);
continue;
}
elog(LOG, "%s() %lu is older than %lu", __FUNCTION__,
file->mtime, days_threshold);
if (file_num <= keep_files)
{
elog(LOG, "%s() newer files are only %d", __FUNCTION__, file_num);
continue;
}
}
}
static void
delete_online_wal_backup(void)
{
int i;
parray *files = parray_new();
char work_path[MAXPGPATH];
if (verbose)
{
printf("========================================\n"));
printf("cleanup online WAL backup\n"));
}
snprintf(work_path, lengthof(work_path), "%s/%s/%s", backup_path,
RESTORE_WORK_DIR, PG_XLOG_DIR);
/* don't cleanup root dir */
files = pgFileEnum(work_path, NULL, true, false);
if (parray_num(files) == 0)
{
parray_free(files);
return;
}
parray_qsort(files, pgFileComparePathDesc); /* cleanup from leaf */
for (i = 0; i < parray_num(files); i++)
{
pgFile *file = (pgFile *) parray_get(files, i);
if (verbose)
printf("cleanup \"%s\"\n", file->path);
if (!check)
pgFileDelete(file);
}
parray_walk(files, pgFile_free);
parray_free(files);
}
/*
* Remove symbolic links point archived WAL in backup catalog.
*/
static void
delete_arclog_link(void)
{
int i;
parray *files = parray_new();
if (verbose)
{
printf("========================================\n"));
printf("cleanup symbolic link in archive directory\n"));
}
files = pgFileEnum(arclog_path, NULL, false, false);
for (i = 0; i < parray_num(files); i++)
{
pgFile *file = (pgFile *) parray_get(files, i);
if (!S_ISLNK(file->mode))
continue;
if (verbose)
printf("cleanup \"%s\"\n", file->path);
if (!check && remove(file->path) == -1)
elog(ERROR_SYSTEM, "could not remove link \"%s\": %s", file->path,
strerror(errno));
}
parray_walk(files, pgFile_free);
parray_free(files);
}
/*
* Delete old files (archived WAL and serverlog) after update of status.
*/
if (HAVE_ARCLOG(&current))
delete_old_files(arclog_path, files_arclog, keep_arclog_files,
keep_arclog_days, server_version, true);
/* Delete old backup files after all backup operation. */
clean_backup(keep_data_generations, keep_data_days);
/*
* If this backup is full backup, cleanup backup of online WAL.
* Note that sereverlog files which were backed up during first restoration
* don't be cleanup.
* Also cleanup symbolic link in the archive directory.
*/
if (backup_mode == BACKUP_MODE_FULL)
{
delete_online_wal_backup();
delete_arclog_link();
}
#endif
/*-------------------------------------------------------------------------
*
* clean.c: cleanup backup files.
*
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
#include <dirent.h>
#include <unistd.h>
static void clean_backup(Database db, pgBackup *backup);
#define CLEAN_MASK (BACKUP_MASK(BACKUP_ERROR) | BACKUP_MASK(BACKUP_BAD))
void
do_clean(int keep_data_generations,
int keep_data_days,
int keep_srvlog_files,
int keep_srvlog_days)
{
Database db;
List *backups;
ListCell *cell;
db = db_open();
backups = db_list_backups(db, make_range(0, NULL), CLEAN_MASK);
foreach (cell, backups)
clean_backup(db, lfirst(cell));
db_close(db);
list_free_deep(backups);
}
/*
* Delete files of the backup and update the status to DELETED.
*/
static void
clean_backup(Database db, pgBackup *backup)
{
char datetime[DATESTRLEN];
char path[MAXPGPATH];
elog(INFO, "clean: %s", date2str(datetime, backup->start_time));
/*
* update the status to BAD before the actual deletion because abort
* during deletion could leave corrupted backup files.
*/
if (backup->status != BACKUP_BAD)
{
backup->status = BACKUP_BAD;
db_update_status(db, backup, NIL);
}
/* remove data files. */
make_backup_path(path, backup->start_time);
remove_file(path);
/* update the status to DELETED */
backup->status = BACKUP_DELETED;
db_update_status(db, backup, NIL);
}
#if 0
/* that are older than KEEP_xxx_DAYS and have more generations
* than KEEP_xxx_FILES.
*/
int i;
parray *backup_list;
int backup_num;
time_t days_threshold = current.start_time - (keep_days * 60 * 60 * 24);
/* cleanup files which satisfy both condition */
if (keep_generations == KEEP_INFINITE || keep_days == KEEP_INFINITE)
{
elog(LOG, "%s() infinite", __FUNCTION__);
return;
}
/* get list of backups. */
backup_list = catalog_get_backup_list(NULL);
/*
* Remove backup files
*/
void
clean_backup(int keep_generations, int keep_days)
{
backup_num = 0;
/* find cleanup target backup. */
for (i = 0; i < parray_num(backup_list); i++)
{
pgBackup *backup = (pgBackup *)parray_get(backup_list, i);
elog(LOG, "%s() %lu", __FUNCTION__, backup->start_time);
/*
* when verify full backup was found, we can cleanup the backup
* that is older than it
*/
if (backup->backup_mode >= BACKUP_MODE_FULL &&
backup->status == BACKUP_OK)
backup_num++;
/* do not include the latest full backup in a count. */
if (backup_num - 1 <= keep_generations)
{
elog(LOG, "%s() backup are only %d", __FUNCTION__, backup_num);
continue;
}
/*
* If the start time of the backup is older than the threshold and
* there are enough generations of full backups, cleanup the backup.
*/
if (backup->start_time >= days_threshold)
{
elog(LOG, "%s() %lu is not older than %lu", __FUNCTION__,
backup->start_time, days_threshold);
continue;
}
}
}
static void delete_old_files(const char *root, parray *files, int keep_files,
int keep_days, int server_version, bool is_arclog);
static void delete_arclog_link(void);
static void delete_online_wal_backup(void);
/*
* Delete files modified before than KEEP_xxx_DAYS or more than KEEP_xxx_FILES
* of newer files exist.
*/
static void
delete_old_files(const char *root,
parray *files,
int keep_files,
int keep_days,
int server_version,
bool is_arclog)
{
int i;
int j;
int file_num = 0;
time_t days_threshold = start_time - (keep_days * 60 * 60 * 24);
if (verbose)
{
char files_str[100];
char days_str[100];
if (keep_files == KEEP_INFINITE)
strncpy(files_str, "INFINITE", lengthof(files_str));
else
snprintf(files_str, lengthof(files_str), "%d", keep_files);
if (keep_days == KEEP_INFINITE)
strncpy(days_str, "INFINITE", lengthof(days_str));
else
snprintf(days_str, lengthof(days_str), "%d", keep_days);
printf("cleanup old files from \"%s\" (files=%s, days=%s)\n",
root, files_str, days_str);
}
/* cleanup files which satisfy both conditions */
if (keep_files == KEEP_INFINITE || keep_days == KEEP_INFINITE)
{
elog(LOG, "%s() infinite", __FUNCTION__);
return;
}
parray_qsort(files, pgFileCompareMtime);
for (i = parray_num(files) - 1; i >= 0; i--)
{
pgFile *file = (pgFile *) parray_get(files, i);
elog(LOG, "%s() %s", __FUNCTION__, file->path);
/* Delete completed WALs only. */
if (is_arclog && !xlog_completed(file, server_version))
{
elog(LOG, "%s() not complete WAL", __FUNCTION__);
continue;
}
file_num++;
/*
* If the mtime of the file is older than the threshold and there are
* enough number of files newer than the files, cleanup the file.
*/
if (file->mtime >= days_threshold)
{
elog(LOG, "%s() %lu is not older than %lu", __FUNCTION__,
file->mtime, days_threshold);
continue;
}
elog(LOG, "%s() %lu is older than %lu", __FUNCTION__,
file->mtime, days_threshold);
if (file_num <= keep_files)
{
elog(LOG, "%s() newer files are only %d", __FUNCTION__, file_num);
continue;
}
}
}
static void
delete_online_wal_backup(void)
{
int i;
parray *files = parray_new();
char work_path[MAXPGPATH];
if (verbose)
{
printf("========================================\n"));
printf("cleanup online WAL backup\n"));
}
snprintf(work_path, lengthof(work_path), "%s/%s/%s", backup_path,
RESTORE_WORK_DIR, PG_XLOG_DIR);
/* don't cleanup root dir */
files = pgFileEnum(work_path, NULL, true, false);
if (parray_num(files) == 0)
{
parray_free(files);
return;
}
parray_qsort(files, pgFileComparePathDesc); /* cleanup from leaf */
for (i = 0; i < parray_num(files); i++)
{
pgFile *file = (pgFile *) parray_get(files, i);
if (verbose)
printf("cleanup \"%s\"\n", file->path);
if (!check)
pgFileDelete(file);
}
parray_walk(files, pgFile_free);
parray_free(files);
}
/*
* Remove symbolic links point archived WAL in backup catalog.
*/
static void
delete_arclog_link(void)
{
int i;
parray *files = parray_new();
if (verbose)
{
printf("========================================\n"));
printf("cleanup symbolic link in archive directory\n"));
}
files = pgFileEnum(arclog_path, NULL, false, false);
for (i = 0; i < parray_num(files); i++)
{
pgFile *file = (pgFile *) parray_get(files, i);
if (!S_ISLNK(file->mode))
continue;
if (verbose)
printf("cleanup \"%s\"\n", file->path);
if (!check && remove(file->path) == -1)
elog(ERROR_SYSTEM, "could not remove link \"%s\": %s", file->path,
strerror(errno));
}
parray_walk(files, pgFile_free);
parray_free(files);
}
/*
* Delete old files (archived WAL and serverlog) after update of status.
*/
if (HAVE_ARCLOG(&current))
delete_old_files(arclog_path, files_arclog, keep_arclog_files,
keep_arclog_days, server_version, true);
/* Delete old backup files after all backup operation. */
clean_backup(keep_data_generations, keep_data_days);
/*
* If this backup is full backup, cleanup backup of online WAL.
* Note that sereverlog files which were backed up during first restoration
* don't be cleanup.
* Also cleanup symbolic link in the archive directory.
*/
if (backup_mode == BACKUP_MODE_FULL)
{
delete_online_wal_backup();
delete_arclog_link();
}
#endif

2038
data.c

File diff suppressed because it is too large Load Diff

@ -1,18 +1,18 @@
# configuration
BACKUP_MODE=FULL
WITH_SERVERLOG=NO
COMPRESS_DATA=NO
# result
TIMELINEID=1
START_LSN=0/0b40c800
STOP_LSN=0/0b4c8020
START_TIME='2009-05-31 17:05:53'
END_TIME='2009-05-31 17:09:13'
TOTAL_DATA_BYTES=1242102558
READ_DATA_BYTES=1024
READ_ARCLOG_BYTES=9223372036854775807
READ_SRVLOG_BYTES=-1
WRITE_BYTES=242102558
BLOCK_SIZE=8192
XLOG_BLOCK_SIZE=8192
STATUS=DONE
# configuration
BACKUP_MODE=FULL
WITH_SERVERLOG=NO
COMPRESS_DATA=NO
# result
TIMELINEID=1
START_LSN=0/0b40c800
STOP_LSN=0/0b4c8020
START_TIME='2009-05-31 17:05:53'
END_TIME='2009-05-31 17:09:13'
TOTAL_DATA_BYTES=1242102558
READ_DATA_BYTES=1024
READ_ARCLOG_BYTES=9223372036854775807
READ_SRVLOG_BYTES=-1
WRITE_BYTES=242102558
BLOCK_SIZE=8192
XLOG_BLOCK_SIZE=8192
STATUS=DONE

@ -1 +1 @@
PG_VERSION f 4 4277607361 0600 2009-08-06 18:40:18
PG_VERSION f 4 4277607361 0600 2009-08-06 18:40:18

@ -1,18 +1,18 @@
# configuration
BACKUP_MODE=INCREMENTAL
WITH_SERVERLOG=NO
COMPRESS_DATA=NO
# result
TIMELINEID=1
START_LSN=0/0b40c800
STOP_LSN=0/0b4c8020
START_TIME='2009-06-01 17:05:53'
END_TIME='2009-06-01 17:09:13'
TOTAL_DATA_BYTES=1242102558
READ_DATA_BYTES=9223372036854775807
READ_ARCLOG_BYTES=16777216
READ_SRVLOG_BYTES=-1
WRITE_BYTES=162372983
BLOCK_SIZE=8192
XLOG_BLOCK_SIZE=8192
STATUS=DONE
# configuration
BACKUP_MODE=INCREMENTAL
WITH_SERVERLOG=NO
COMPRESS_DATA=NO
# result
TIMELINEID=1
START_LSN=0/0b40c800
STOP_LSN=0/0b4c8020
START_TIME='2009-06-01 17:05:53'
END_TIME='2009-06-01 17:09:13'
TOTAL_DATA_BYTES=1242102558
READ_DATA_BYTES=9223372036854775807
READ_ARCLOG_BYTES=16777216
READ_SRVLOG_BYTES=-1
WRITE_BYTES=162372983
BLOCK_SIZE=8192
XLOG_BLOCK_SIZE=8192
STATUS=DONE

@ -1 +1 @@
PG_VERSION f 4 0 0600 2009-08-06 18:40:18
PG_VERSION f 4 0 0600 2009-08-06 18:40:18

@ -1,18 +1,18 @@
# configuration
BACKUP_MODE=ARCHIVE
WITH_SERVERLOG=YES
COMPRESS_DATA=NO
# result
TIMELINEID=1
START_LSN=0/0b40c800
STOP_LSN=0/0b4c8020
START_TIME='2009-06-02 17:05:03'
END_TIME='2009-06-02 17:05:03'
TOTAL_DATA_BYTES=-1
READ_DATA_BYTES=-1
READ_ARCLOG_BYTES=-1
READ_SRVLOG_BYTES=4335423
WRITE_BYTES=162372983
BLOCK_SIZE=8192
XLOG_BLOCK_SIZE=8192
STATUS=DELETED
# configuration
BACKUP_MODE=ARCHIVE
WITH_SERVERLOG=YES
COMPRESS_DATA=NO
# result
TIMELINEID=1
START_LSN=0/0b40c800
STOP_LSN=0/0b4c8020
START_TIME='2009-06-02 17:05:03'
END_TIME='2009-06-02 17:05:03'
TOTAL_DATA_BYTES=-1
READ_DATA_BYTES=-1
READ_ARCLOG_BYTES=-1
READ_SRVLOG_BYTES=4335423
WRITE_BYTES=162372983
BLOCK_SIZE=8192
XLOG_BLOCK_SIZE=8192
STATUS=DELETED

@ -1,18 +1,18 @@
# configuration
BACKUP_MODE=FULL
WITH_SERVERLOG=YES
COMPRESS_DATA=NO
# result
TIMELINEID=1
START_LSN=0/0b40c800
STOP_LSN=0/0b4c8020
START_TIME='2009-06-03 17:05:53'
END_TIME='2009-06-03 17:05:53'
TOTAL_DATA_BYTES=-1
READ_DATA_BYTES=-1
READ_ARCLOG_BYTES=-1
READ_SRVLOG_BYTES=-1
WRITE_BYTES=-1
BLOCK_SIZE=8192
XLOG_BLOCK_SIZE=8192
STATUS=RUNNING
# configuration
BACKUP_MODE=FULL
WITH_SERVERLOG=YES
COMPRESS_DATA=NO
# result
TIMELINEID=1
START_LSN=0/0b40c800
STOP_LSN=0/0b4c8020
START_TIME='2009-06-03 17:05:53'
END_TIME='2009-06-03 17:05:53'
TOTAL_DATA_BYTES=-1
READ_DATA_BYTES=-1
READ_ARCLOG_BYTES=-1
READ_SRVLOG_BYTES=-1
WRITE_BYTES=-1
BLOCK_SIZE=8192
XLOG_BLOCK_SIZE=8192
STATUS=RUNNING

1458
db.c

File diff suppressed because it is too large Load Diff

526
delete.c

@ -1,263 +1,263 @@
/*-------------------------------------------------------------------------
*
* delete.c: delete backup files.
*
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
static int pgBackupDeleteFiles(pgBackup *backup);
static bool checkIfDeletable(pgBackup *backup);
int
//do_delete(pgBackupRange *range)
do_delete(pgBackupRange *range, bool force)
{
int i;
int ret;
parray *backup_list;
bool do_delete;
bool force_delete;
/* DATE are always required */
if (!pgBackupRangeIsValid(range))
elog(ERROR_ARGS, _("required delete range option not specified: delete DATE"));
/* get exclusive lock of backup catalog */
ret = catalog_lock();
if (ret == -1)
elog(ERROR_SYSTEM, _("can't lock backup catalog."));
else if (ret == 1)
elog(ERROR_ALREADY_RUNNING,
_("another pg_rman is running, stop delete."));
/* get list of backups. */
backup_list = catalog_get_backup_list(NULL);
if(!backup_list){
elog(ERROR_SYSTEM, _("can't process any more."));
}
do_delete = false;
force_delete = false;
/* find delete target backup. */
for (i = 0; i < parray_num(backup_list); i++)
{
pgBackup *backup = (pgBackup *)parray_get(backup_list, i);
if(force)
force_delete = checkIfDeletable(backup);
/* delete backup and update status to DELETED */
if (do_delete || force_delete)
{
/* check for interrupt */
if (interrupted)
elog(ERROR_INTERRUPTED, _("interrupted during delete backup"));
pgBackupDeleteFiles(backup);
continue;
}
/* find latest full backup. */
if (backup->backup_mode >= BACKUP_MODE_FULL &&
backup->status == BACKUP_STATUS_OK &&
backup->start_time <= range->begin)
do_delete = true;
}
/* release catalog lock */
catalog_unlock();
/* cleanup */
parray_walk(backup_list, pgBackupFree);
parray_free(backup_list);
return 0;
}
/*
* Delete backups that are older than KEEP_xxx_DAYS and have more generations
* than KEEP_xxx_FILES.
*/
void
pgBackupDelete(int keep_generations, int keep_days)
{
int i;
parray *backup_list;
int backup_num;
time_t days_threshold = current.start_time - (keep_days * 60 * 60 * 24);
if (verbose)
{
char generations_str[100];
char days_str[100];
if (keep_generations == KEEP_INFINITE)
strncpy(generations_str, "INFINITE",
lengthof(generations_str));
else
snprintf(generations_str, lengthof(generations_str),
"%d", keep_generations);
if (keep_days == KEEP_INFINITE)
strncpy(days_str, "INFINITE", lengthof(days_str));
else
snprintf(days_str, lengthof(days_str), "%d", keep_days);
printf(_("delete old backups (generations=%s, days=%s)\n"),
generations_str, days_str);
}
/* delete files which satisfy both condition */
if (keep_generations == KEEP_INFINITE || keep_days == KEEP_INFINITE)
{
elog(LOG, "%s() infinite", __FUNCTION__);
return;
}
/* get list of backups. */
backup_list = catalog_get_backup_list(NULL);
backup_num = 0;
/* find delete target backup. */
for (i = 0; i < parray_num(backup_list); i++)
{
pgBackup *backup = (pgBackup *)parray_get(backup_list, i);
elog(LOG, "%s() %lu", __FUNCTION__, backup->start_time);
/*
* when validate full backup was found, we can delete the backup
* that is older than it
*/
if (backup->backup_mode >= BACKUP_MODE_FULL &&
backup->status == BACKUP_STATUS_OK)
backup_num++;
/* do not include the latest full backup in a count. */
// if (backup_num - 1 <= keep_generations)
if (backup_num <= keep_generations)
{
elog(LOG, "%s() backup are only %d", __FUNCTION__, backup_num);
continue;
}
/*
* If the start time of the backup is older than the threshold and
* there are enough generations of full backups, delete the backup.
*/
if (backup->start_time >= days_threshold)
{
elog(LOG, "%s() %lu is not older than %lu", __FUNCTION__,
backup->start_time, days_threshold);
continue;
}
elog(LOG, "%s() %lu is older than %lu", __FUNCTION__,
backup->start_time, days_threshold);
/* delete backup and update status to DELETED */
pgBackupDeleteFiles(backup);
}
/* cleanup */
parray_walk(backup_list, pgBackupFree);
parray_free(backup_list);
}
/*
* Delete backup files of the backup and update the status of the backup to
* BACKUP_STATUS_DELETED.
*/
static int
pgBackupDeleteFiles(pgBackup *backup)
{
int i;
char path[MAXPGPATH];
char timestamp[20];
parray *files;
/*
* If the backup was deleted already, nothing to do and such situation
* is not error.
*/
if (backup->status == BACKUP_STATUS_DELETED)
return 0;
time2iso(timestamp, lengthof(timestamp), backup->start_time);
elog(INFO, _("delete: %s"), timestamp);
/*
* update STATUS to BACKUP_STATUS_DELETING in preparation for the case which
* the error occurs before deleting all backup files.
*/
if (!check)
{
backup->status = BACKUP_STATUS_DELETING;
pgBackupWriteIni(backup);
}
/* list files to be deleted */
files = parray_new();
pgBackupGetPath(backup, path, lengthof(path), DATABASE_DIR);
dir_list_file(files, path, NULL, true, true);
pgBackupGetPath(backup, path, lengthof(path), ARCLOG_DIR);
dir_list_file(files, path, NULL, true, true);
pgBackupGetPath(backup, path, lengthof(path), SRVLOG_DIR);
dir_list_file(files, path, NULL, true, true);
/* delete leaf node first */
parray_qsort(files, pgFileComparePathDesc);
for (i = 0; i < parray_num(files); i++)
{
pgFile *file = (pgFile *) parray_get(files, i);
/* print progress */
elog(LOG, _("delete file(%d/%lu) \"%s\"\n"), i + 1,
(unsigned long) parray_num(files), file->path);
/* skip actual deletion in check mode */
if (!check)
{
if (remove(file->path))
{
elog(WARNING, _("can't remove \"%s\": %s"), file->path,
strerror(errno));
parray_walk(files, pgFileFree);
parray_free(files);
return 1;
}
}
}
/*
* After deleting all of the backup files, update STATUS to
* BACKUP_STATUS_DELETED.
*/
if (!check)
{
backup->status = BACKUP_STATUS_DELETED;
pgBackupWriteIni(backup);
}
parray_walk(files, pgFileFree);
parray_free(files);
return 0;
}
bool
checkIfDeletable(pgBackup *backup)
{
/* find latest full backup. */
if (backup->status != BACKUP_STATUS_OK &&
backup->status != BACKUP_STATUS_DELETED &&
backup->status != BACKUP_STATUS_DONE)
return true;
return false;
}
/*-------------------------------------------------------------------------
*
* delete.c: delete backup files.
*
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
static int pgBackupDeleteFiles(pgBackup *backup);
static bool checkIfDeletable(pgBackup *backup);
int
//do_delete(pgBackupRange *range)
do_delete(pgBackupRange *range, bool force)
{
int i;
int ret;
parray *backup_list;
bool do_delete;
bool force_delete;
/* DATE are always required */
if (!pgBackupRangeIsValid(range))
elog(ERROR_ARGS, _("required delete range option not specified: delete DATE"));
/* get exclusive lock of backup catalog */
ret = catalog_lock();
if (ret == -1)
elog(ERROR_SYSTEM, _("can't lock backup catalog."));
else if (ret == 1)
elog(ERROR_ALREADY_RUNNING,
_("another pg_rman is running, stop delete."));
/* get list of backups. */
backup_list = catalog_get_backup_list(NULL);
if(!backup_list){
elog(ERROR_SYSTEM, _("can't process any more."));
}
do_delete = false;
force_delete = false;
/* find delete target backup. */
for (i = 0; i < parray_num(backup_list); i++)
{
pgBackup *backup = (pgBackup *)parray_get(backup_list, i);
if(force)
force_delete = checkIfDeletable(backup);
/* delete backup and update status to DELETED */
if (do_delete || force_delete)
{
/* check for interrupt */
if (interrupted)
elog(ERROR_INTERRUPTED, _("interrupted during delete backup"));
pgBackupDeleteFiles(backup);
continue;
}
/* find latest full backup. */
if (backup->backup_mode >= BACKUP_MODE_FULL &&
backup->status == BACKUP_STATUS_OK &&
backup->start_time <= range->begin)
do_delete = true;
}
/* release catalog lock */
catalog_unlock();
/* cleanup */
parray_walk(backup_list, pgBackupFree);
parray_free(backup_list);
return 0;
}
/*
* Delete backups that are older than KEEP_xxx_DAYS and have more generations
* than KEEP_xxx_FILES.
*/
void
pgBackupDelete(int keep_generations, int keep_days)
{
int i;
parray *backup_list;
int backup_num;
time_t days_threshold = current.start_time - (keep_days * 60 * 60 * 24);
if (verbose)
{
char generations_str[100];
char days_str[100];
if (keep_generations == KEEP_INFINITE)
strncpy(generations_str, "INFINITE",
lengthof(generations_str));
else
snprintf(generations_str, lengthof(generations_str),
"%d", keep_generations);
if (keep_days == KEEP_INFINITE)
strncpy(days_str, "INFINITE", lengthof(days_str));
else
snprintf(days_str, lengthof(days_str), "%d", keep_days);
printf(_("delete old backups (generations=%s, days=%s)\n"),
generations_str, days_str);
}
/* delete files which satisfy both condition */
if (keep_generations == KEEP_INFINITE || keep_days == KEEP_INFINITE)
{
elog(LOG, "%s() infinite", __FUNCTION__);
return;
}
/* get list of backups. */
backup_list = catalog_get_backup_list(NULL);
backup_num = 0;
/* find delete target backup. */
for (i = 0; i < parray_num(backup_list); i++)
{
pgBackup *backup = (pgBackup *)parray_get(backup_list, i);
elog(LOG, "%s() %lu", __FUNCTION__, backup->start_time);
/*
* when validate full backup was found, we can delete the backup
* that is older than it
*/
if (backup->backup_mode >= BACKUP_MODE_FULL &&
backup->status == BACKUP_STATUS_OK)
backup_num++;
/* do not include the latest full backup in a count. */
// if (backup_num - 1 <= keep_generations)
if (backup_num <= keep_generations)
{
elog(LOG, "%s() backup are only %d", __FUNCTION__, backup_num);
continue;
}
/*
* If the start time of the backup is older than the threshold and
* there are enough generations of full backups, delete the backup.
*/
if (backup->start_time >= days_threshold)
{
elog(LOG, "%s() %lu is not older than %lu", __FUNCTION__,
backup->start_time, days_threshold);
continue;
}
elog(LOG, "%s() %lu is older than %lu", __FUNCTION__,
backup->start_time, days_threshold);
/* delete backup and update status to DELETED */
pgBackupDeleteFiles(backup);
}
/* cleanup */
parray_walk(backup_list, pgBackupFree);
parray_free(backup_list);
}
/*
* Delete backup files of the backup and update the status of the backup to
* BACKUP_STATUS_DELETED.
*/
static int
pgBackupDeleteFiles(pgBackup *backup)
{
int i;
char path[MAXPGPATH];
char timestamp[20];
parray *files;
/*
* If the backup was deleted already, nothing to do and such situation
* is not error.
*/
if (backup->status == BACKUP_STATUS_DELETED)
return 0;
time2iso(timestamp, lengthof(timestamp), backup->start_time);
elog(INFO, _("delete: %s"), timestamp);
/*
* update STATUS to BACKUP_STATUS_DELETING in preparation for the case which
* the error occurs before deleting all backup files.
*/
if (!check)
{
backup->status = BACKUP_STATUS_DELETING;
pgBackupWriteIni(backup);
}
/* list files to be deleted */
files = parray_new();
pgBackupGetPath(backup, path, lengthof(path), DATABASE_DIR);
dir_list_file(files, path, NULL, true, true);
pgBackupGetPath(backup, path, lengthof(path), ARCLOG_DIR);
dir_list_file(files, path, NULL, true, true);
pgBackupGetPath(backup, path, lengthof(path), SRVLOG_DIR);
dir_list_file(files, path, NULL, true, true);
/* delete leaf node first */
parray_qsort(files, pgFileComparePathDesc);
for (i = 0; i < parray_num(files); i++)
{
pgFile *file = (pgFile *) parray_get(files, i);
/* print progress */
elog(LOG, _("delete file(%d/%lu) \"%s\"\n"), i + 1,
(unsigned long) parray_num(files), file->path);
/* skip actual deletion in check mode */
if (!check)
{
if (remove(file->path))
{
elog(WARNING, _("can't remove \"%s\": %s"), file->path,
strerror(errno));
parray_walk(files, pgFileFree);
parray_free(files);
return 1;
}
}
}
/*
* After deleting all of the backup files, update STATUS to
* BACKUP_STATUS_DELETED.
*/
if (!check)
{
backup->status = BACKUP_STATUS_DELETED;
pgBackupWriteIni(backup);
}
parray_walk(files, pgFileFree);
parray_free(files);
return 0;
}
bool
checkIfDeletable(pgBackup *backup)
{
/* find latest full backup. */
if (backup->status != BACKUP_STATUS_OK &&
backup->status != BACKUP_STATUS_DELETED &&
backup->status != BACKUP_STATUS_DONE)
return true;
return false;
}

1172
dir.c

File diff suppressed because it is too large Load Diff

1410
file.c

File diff suppressed because it is too large Load Diff

346
init.c

@ -1,173 +1,173 @@
/*-------------------------------------------------------------------------
*
* init.c: manage backup catalog.
*
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
#include <unistd.h>
#include <dirent.h>
static void parse_postgresql_conf(const char *path, char **log_directory,
char **archive_command);
/*
* selects function for scandir.
*/
static int selects(const struct dirent *dir)
{
return dir->d_name[0] != '.';
}
/*
* Initialize backup catalog.
*/
int
do_init(void)
{
char path[MAXPGPATH];
char *log_directory = NULL;
char *archive_command = NULL;
FILE *fp;
struct dirent **dp;
int results;
if (access(backup_path, F_OK) == 0){
results = scandir(backup_path, &dp, selects, NULL);
if(results != 0){
elog(ERROR, _("backup catalog already exist. and it's not empty."));
}
}
/* create backup catalog root directory */
dir_create_dir(backup_path, DIR_PERMISSION);
/* create directories for backup of online files */
join_path_components(path, backup_path, RESTORE_WORK_DIR);
dir_create_dir(path, DIR_PERMISSION);
snprintf(path, lengthof(path), "%s/%s/%s", backup_path, RESTORE_WORK_DIR,
PG_XLOG_DIR);
dir_create_dir(path, DIR_PERMISSION);
snprintf(path, lengthof(path), "%s/%s/%s", backup_path, RESTORE_WORK_DIR,
SRVLOG_DIR);
dir_create_dir(path, DIR_PERMISSION);
/* create directory for timeline history files */
join_path_components(path, backup_path, TIMELINE_HISTORY_DIR);
dir_create_dir(path, DIR_PERMISSION);
/* read postgresql.conf */
if (pgdata)
{
join_path_components(path, pgdata, "postgresql.conf");
parse_postgresql_conf(path, &log_directory, &archive_command);
}
/* create pg_rman.ini */
join_path_components(path, backup_path, PG_RMAN_INI_FILE);
fp = fopen(path, "wt");
if (fp == NULL)
elog(ERROR_SYSTEM, _("can't create pg_rman.ini: %s"), strerror(errno));
/* set ARCLOG_PATH refered with log_directory */
if (arclog_path == NULL && archive_command && archive_command[0])
{
char *command = pgut_strdup(archive_command);
char *begin;
char *end;
char *fname;
/* example: 'cp "%p" /path/to/arclog/"%f"' */
for (begin = command; *begin;)
{
begin = begin + strspn(begin, " \n\r\t\v");
end = begin + strcspn(begin, " \n\r\t\v");
*end = '\0';
if ((fname = strstr(begin, "%f")) != NULL)
{
while (strchr(" \n\r\t\v\"'", *begin))
begin++;
fname--;
while (fname > begin && strchr(" \n\r\t\v\"'/", fname[-1]))
fname--;
*fname = '\0';
if (is_absolute_path(begin))
arclog_path = pgut_strdup(begin);
break;
}
begin = end + 1;
}
free(command);
}
if (arclog_path)
{
fprintf(fp, "ARCLOG_PATH='%s'\n", arclog_path);
elog(INFO, "ARCLOG_PATH is set to '%s'", arclog_path);
}
else if (archive_command && archive_command[0])
elog(WARNING, "ARCLOG_PATH is not set because failed to parse archive_command '%s'."
"Please set ARCLOG_PATH in pg_rman.ini or environmental variable", archive_command);
else
elog(WARNING, "ARCLOG_PATH is not set because archive_command is empty."
"Please set ARCLOG_PATH in pg_rman.ini or environmental variable");
/* set SRVLOG_PATH refered with log_directory */
if (srvlog_path == NULL)
{
if (log_directory)
{
if (is_absolute_path(log_directory))
srvlog_path = pgut_strdup(log_directory);
else
{
srvlog_path = pgut_malloc(MAXPGPATH);
join_path_components(srvlog_path, pgdata, log_directory);
}
}
else if (pgdata)
{
/* default: log_directory = 'pg_log' */
srvlog_path = pgut_malloc(MAXPGPATH);
join_path_components(srvlog_path, pgdata, "pg_log");
}
}
if (srvlog_path)
{
fprintf(fp, "SRVLOG_PATH='%s'\n", srvlog_path);
elog(INFO, "SRVLOG_PATH is set to '%s'", srvlog_path);
}
fprintf(fp, "\n");
fclose(fp);
free(archive_command);
free(log_directory);
return 0;
}
static void
parse_postgresql_conf(const char *path,
char **log_directory,
char **archive_command)
{
pgut_option options[] =
{
{ 's', 0, "log_directory" , NULL, SOURCE_ENV },
{ 's', 0, "archive_command" , NULL, SOURCE_ENV },
{ 0 }
};
options[0].var = log_directory;
options[1].var = archive_command;
pgut_readopt(path, options, LOG); /* ignore unknown options */
}
/*-------------------------------------------------------------------------
*
* init.c: manage backup catalog.
*
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
#include <unistd.h>
#include <dirent.h>
static void parse_postgresql_conf(const char *path, char **log_directory,
char **archive_command);
/*
* selects function for scandir.
*/
static int selects(const struct dirent *dir)
{
return dir->d_name[0] != '.';
}
/*
* Initialize backup catalog.
*/
int
do_init(void)
{
char path[MAXPGPATH];
char *log_directory = NULL;
char *archive_command = NULL;
FILE *fp;
struct dirent **dp;
int results;
if (access(backup_path, F_OK) == 0){
results = scandir(backup_path, &dp, selects, NULL);
if(results != 0){
elog(ERROR, _("backup catalog already exist. and it's not empty."));
}
}
/* create backup catalog root directory */
dir_create_dir(backup_path, DIR_PERMISSION);
/* create directories for backup of online files */
join_path_components(path, backup_path, RESTORE_WORK_DIR);
dir_create_dir(path, DIR_PERMISSION);
snprintf(path, lengthof(path), "%s/%s/%s", backup_path, RESTORE_WORK_DIR,
PG_XLOG_DIR);
dir_create_dir(path, DIR_PERMISSION);
snprintf(path, lengthof(path), "%s/%s/%s", backup_path, RESTORE_WORK_DIR,
SRVLOG_DIR);
dir_create_dir(path, DIR_PERMISSION);
/* create directory for timeline history files */
join_path_components(path, backup_path, TIMELINE_HISTORY_DIR);
dir_create_dir(path, DIR_PERMISSION);
/* read postgresql.conf */
if (pgdata)
{
join_path_components(path, pgdata, "postgresql.conf");
parse_postgresql_conf(path, &log_directory, &archive_command);
}
/* create pg_rman.ini */
join_path_components(path, backup_path, PG_RMAN_INI_FILE);
fp = fopen(path, "wt");
if (fp == NULL)
elog(ERROR_SYSTEM, _("can't create pg_rman.ini: %s"), strerror(errno));
/* set ARCLOG_PATH refered with log_directory */
if (arclog_path == NULL && archive_command && archive_command[0])
{
char *command = pgut_strdup(archive_command);
char *begin;
char *end;
char *fname;
/* example: 'cp "%p" /path/to/arclog/"%f"' */
for (begin = command; *begin;)
{
begin = begin + strspn(begin, " \n\r\t\v");
end = begin + strcspn(begin, " \n\r\t\v");
*end = '\0';
if ((fname = strstr(begin, "%f")) != NULL)
{
while (strchr(" \n\r\t\v\"'", *begin))
begin++;
fname--;
while (fname > begin && strchr(" \n\r\t\v\"'/", fname[-1]))
fname--;
*fname = '\0';
if (is_absolute_path(begin))
arclog_path = pgut_strdup(begin);
break;
}
begin = end + 1;
}
free(command);
}
if (arclog_path)
{
fprintf(fp, "ARCLOG_PATH='%s'\n", arclog_path);
elog(INFO, "ARCLOG_PATH is set to '%s'", arclog_path);
}
else if (archive_command && archive_command[0])
elog(WARNING, "ARCLOG_PATH is not set because failed to parse archive_command '%s'."
"Please set ARCLOG_PATH in pg_rman.ini or environmental variable", archive_command);
else
elog(WARNING, "ARCLOG_PATH is not set because archive_command is empty."
"Please set ARCLOG_PATH in pg_rman.ini or environmental variable");
/* set SRVLOG_PATH refered with log_directory */
if (srvlog_path == NULL)
{
if (log_directory)
{
if (is_absolute_path(log_directory))
srvlog_path = pgut_strdup(log_directory);
else
{
srvlog_path = pgut_malloc(MAXPGPATH);
join_path_components(srvlog_path, pgdata, log_directory);
}
}
else if (pgdata)
{
/* default: log_directory = 'pg_log' */
srvlog_path = pgut_malloc(MAXPGPATH);
join_path_components(srvlog_path, pgdata, "pg_log");
}
}
if (srvlog_path)
{
fprintf(fp, "SRVLOG_PATH='%s'\n", srvlog_path);
elog(INFO, "SRVLOG_PATH is set to '%s'", srvlog_path);
}
fprintf(fp, "\n");
fclose(fp);
free(archive_command);
free(log_directory);
return 0;
}
static void
parse_postgresql_conf(const char *path,
char **log_directory,
char **archive_command)
{
pgut_option options[] =
{
{ 's', 0, "log_directory" , NULL, SOURCE_ENV },
{ 's', 0, "archive_command" , NULL, SOURCE_ENV },
{ 0 }
};
options[0].var = log_directory;
options[1].var = archive_command;
pgut_readopt(path, options, LOG); /* ignore unknown options */
}

394
parray.c

@ -1,197 +1,197 @@
/*-------------------------------------------------------------------------
*
* parray.c: pointer array collection.
*
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
/* members of struct parray are hidden from client. */
struct parray
{
void **data; /* poiter array, expanded if necessary */
size_t alloced; /* number of elements allocated */
size_t used; /* number of elements in use */
};
/*
* Create new parray object.
* Never returns NULL.
*/
parray *
parray_new(void)
{
parray *a = pgut_new(parray);
a->data = NULL;
a->used = 0;
a->alloced = 0;
parray_expand(a, 1024);
return a;
}
/*
* Expand array pointed by data to newsize.
* Elements in expanded area are initialized to NULL.
* Note: never returns NULL.
*/
void
parray_expand(parray *array, size_t newsize)
{
void **p;
/* already allocated */
if (newsize <= array->alloced)
return;
p = pgut_realloc(array->data, sizeof(void *) * newsize);
/* initialize expanded area to NULL */
memset(p + array->alloced, 0, (newsize - array->alloced) * sizeof(void *));
array->alloced = newsize;
array->data = p;
}
void
parray_free(parray *array)
{
if (array == NULL)
return;
free(array->data);
free(array);
}
void
parray_append(parray *array, void *elem)
{
if (array->used + 1 > array->alloced)
parray_expand(array, array->alloced * 2);
array->data[array->used++] = elem;
}
void
parray_insert(parray *array, size_t index, void *elem)
{
if (array->used + 1 > array->alloced)
parray_expand(array, array->alloced * 2);
memmove(array->data + index + 1, array->data + index,
(array->alloced - index - 1) * sizeof(void *));
array->data[index] = elem;
/* adjust used count */
if (array->used < index + 1)
array->used = index + 1;
else
array->used++;
}
/*
* Concatinate two parray.
* parray_concat() appends the copy of the content of src to the end of dest.
*/
parray *
parray_concat(parray *dest, const parray *src)
{
/* expand head array */
parray_expand(dest, dest->used + src->used);
/* copy content of src after content of dest */
memcpy(dest->data + dest->used, src->data, src->used * sizeof(void *));
dest->used += parray_num(src);
return dest;
}
void
parray_set(parray *array, size_t index, void *elem)
{
if (index > array->alloced - 1)
parray_expand(array, index + 1);
array->data[index] = elem;
/* adjust used count */
if (array->used < index + 1)
array->used = index + 1;
}
void *
parray_get(const parray *array, size_t index)
{
if (index > array->alloced - 1)
return NULL;
return array->data[index];
}
void *
parray_remove(parray *array, size_t index)
{
void *val;
/* removing unused element */
if (index > array->used)
return NULL;
val = array->data[index];
/* Do not move if the last element was removed. */
if (index < array->alloced - 1)
memmove(array->data + index, array->data + index + 1,
(array->alloced - index - 1) * sizeof(void *));
/* adjust used count */
array->used--;
return val;
}
bool
parray_rm(parray *array, const void *key, int(*compare)(const void *, const void *))
{
int i;
for (i = 0; i < array->used; i++)
{
if (compare(&key, &array->data[i]) == 0)
{
parray_remove(array, i);
return true;
}
}
return false;
}
size_t
parray_num(const parray *array)
{
return array->used;
}
void
parray_qsort(parray *array, int(*compare)(const void *, const void *))
{
qsort(array->data, array->used, sizeof(void *), compare);
}
void
parray_walk(parray *array, void (*action)(void *))
{
int i;
for (i = 0; i < array->used; i++)
action(array->data[i]);
}
void *
parray_bsearch(parray *array, const void *key, int(*compare)(const void *, const void *))
{
return bsearch(&key, array->data, array->used, sizeof(void *), compare);
}
/*-------------------------------------------------------------------------
*
* parray.c: pointer array collection.
*
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
/* members of struct parray are hidden from client. */
struct parray
{
void **data; /* poiter array, expanded if necessary */
size_t alloced; /* number of elements allocated */
size_t used; /* number of elements in use */
};
/*
* Create new parray object.
* Never returns NULL.
*/
parray *
parray_new(void)
{
parray *a = pgut_new(parray);
a->data = NULL;
a->used = 0;
a->alloced = 0;
parray_expand(a, 1024);
return a;
}
/*
* Expand array pointed by data to newsize.
* Elements in expanded area are initialized to NULL.
* Note: never returns NULL.
*/
void
parray_expand(parray *array, size_t newsize)
{
void **p;
/* already allocated */
if (newsize <= array->alloced)
return;
p = pgut_realloc(array->data, sizeof(void *) * newsize);
/* initialize expanded area to NULL */
memset(p + array->alloced, 0, (newsize - array->alloced) * sizeof(void *));
array->alloced = newsize;
array->data = p;
}
void
parray_free(parray *array)
{
if (array == NULL)
return;
free(array->data);
free(array);
}
void
parray_append(parray *array, void *elem)
{
if (array->used + 1 > array->alloced)
parray_expand(array, array->alloced * 2);
array->data[array->used++] = elem;
}
void
parray_insert(parray *array, size_t index, void *elem)
{
if (array->used + 1 > array->alloced)
parray_expand(array, array->alloced * 2);
memmove(array->data + index + 1, array->data + index,
(array->alloced - index - 1) * sizeof(void *));
array->data[index] = elem;
/* adjust used count */
if (array->used < index + 1)
array->used = index + 1;
else
array->used++;
}
/*
* Concatinate two parray.
* parray_concat() appends the copy of the content of src to the end of dest.
*/
parray *
parray_concat(parray *dest, const parray *src)
{
/* expand head array */
parray_expand(dest, dest->used + src->used);
/* copy content of src after content of dest */
memcpy(dest->data + dest->used, src->data, src->used * sizeof(void *));
dest->used += parray_num(src);
return dest;
}
void
parray_set(parray *array, size_t index, void *elem)
{
if (index > array->alloced - 1)
parray_expand(array, index + 1);
array->data[index] = elem;
/* adjust used count */
if (array->used < index + 1)
array->used = index + 1;
}
void *
parray_get(const parray *array, size_t index)
{
if (index > array->alloced - 1)
return NULL;
return array->data[index];
}
void *
parray_remove(parray *array, size_t index)
{
void *val;
/* removing unused element */
if (index > array->used)
return NULL;
val = array->data[index];
/* Do not move if the last element was removed. */
if (index < array->alloced - 1)
memmove(array->data + index, array->data + index + 1,
(array->alloced - index - 1) * sizeof(void *));
/* adjust used count */
array->used--;
return val;
}
bool
parray_rm(parray *array, const void *key, int(*compare)(const void *, const void *))
{
int i;
for (i = 0; i < array->used; i++)
{
if (compare(&key, &array->data[i]) == 0)
{
parray_remove(array, i);
return true;
}
}
return false;
}
size_t
parray_num(const parray *array)
{
return array->used;
}
void
parray_qsort(parray *array, int(*compare)(const void *, const void *))
{
qsort(array->data, array->used, sizeof(void *), compare);
}
void
parray_walk(parray *array, void (*action)(void *))
{
int i;
for (i = 0; i < array->used; i++)
action(array->data[i]);
}
void *
parray_bsearch(parray *array, const void *key, int(*compare)(const void *, const void *))
{
return bsearch(&key, array->data, array->used, sizeof(void *), compare);
}

@ -1,35 +1,35 @@
/*-------------------------------------------------------------------------
*
* parray.h: pointer array collection.
*
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#ifndef PARRAY_H
#define PARRAY_H
/*
* "parray" hold pointers to objects in a linear memory area.
* Client use "parray *" to access parray object.
*/
typedef struct parray parray;
extern parray *parray_new(void);
extern void parray_expand(parray *array, size_t newnum);
extern void parray_free(parray *array);
extern void parray_append(parray *array, void *val);
extern void parray_insert(parray *array, size_t index, void *val);
extern parray *parray_concat(parray *head, const parray *tail);
extern void parray_set(parray *array, size_t index, void *val);
extern void *parray_get(const parray *array, size_t index);
extern void *parray_remove(parray *array, size_t index);
extern bool parray_rm(parray *array, const void *key, int(*compare)(const void *, const void *));
extern size_t parray_num(const parray *array);
extern void parray_qsort(parray *array, int(*compare)(const void *, const void *));
extern void *parray_bsearch(parray *array, const void *key, int(*compare)(const void *, const void *));
extern void parray_walk(parray *array, void (*action)(void *));
#endif /* PARRAY_H */
/*-------------------------------------------------------------------------
*
* parray.h: pointer array collection.
*
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#ifndef PARRAY_H
#define PARRAY_H
/*
* "parray" hold pointers to objects in a linear memory area.
* Client use "parray *" to access parray object.
*/
typedef struct parray parray;
extern parray *parray_new(void);
extern void parray_expand(parray *array, size_t newnum);
extern void parray_free(parray *array);
extern void parray_append(parray *array, void *val);
extern void parray_insert(parray *array, size_t index, void *val);
extern parray *parray_concat(parray *head, const parray *tail);
extern void parray_set(parray *array, size_t index, void *val);
extern void *parray_get(const parray *array, size_t index);
extern void *parray_remove(parray *array, size_t index);
extern bool parray_rm(parray *array, const void *key, int(*compare)(const void *, const void *));
extern size_t parray_num(const parray *array);
extern void parray_qsort(parray *array, int(*compare)(const void *, const void *));
extern void *parray_bsearch(parray *array, const void *key, int(*compare)(const void *, const void *));
extern void parray_walk(parray *array, void (*action)(void *));
#endif /* PARRAY_H */

186
pg_crc.c

@ -1,93 +1,93 @@
/*-------------------------------------------------------------------------
*
* pg_crc.c
* PostgreSQL CRC support
*
* See Ross Williams' excellent introduction
* A PAINLESS GUIDE TO CRC ERROR DETECTION ALGORITHMS, available from
* http://www.ross.net/crc/download/crc_v3.txt or several other net sites.
*
* We use a normal (not "reflected", in Williams' terms) CRC, using initial
* all-ones register contents and a final bit inversion.
*
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*-------------------------------------------------------------------------
*/
/* Use c.h so that this file can be built in either frontend or backend */
#include "c.h"
/*
* This table is based on the polynomial
* x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1.
* (This is the same polynomial used in Ethernet checksums, for instance.)
*/
const uint32 pg_crc32_table[256] = {
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
};
/*-------------------------------------------------------------------------
*
* pg_crc.c
* PostgreSQL CRC support
*
* See Ross Williams' excellent introduction
* A PAINLESS GUIDE TO CRC ERROR DETECTION ALGORITHMS, available from
* http://www.ross.net/crc/download/crc_v3.txt or several other net sites.
*
* We use a normal (not "reflected", in Williams' terms) CRC, using initial
* all-ones register contents and a final bit inversion.
*
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*-------------------------------------------------------------------------
*/
/* Use c.h so that this file can be built in either frontend or backend */
#include "c.h"
/*
* This table is based on the polynomial
* x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1.
* (This is the same polynomial used in Ethernet checksums, for instance.)
*/
const uint32 pg_crc32_table[256] = {
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
};

484
pg_ctl.c

@ -1,242 +1,242 @@
/*-------------------------------------------------------------------------
*
* pg_ctl.c: operations for control file
*
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "postgres_fe.h"
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include "pg_rman.h"
/* PID can be negative for standalone backend */
typedef long pgpid_t;
static pgpid_t get_pgpid(void);
static bool postmaster_is_alive(pid_t pid);
static bool read_control_file(const char *path, ControlFileData *ctrl);
static pgpid_t
get_pgpid(void)
{
FILE *fp;
pgpid_t pid;
char path[MAXPGPATH];
snprintf(path, lengthof(path), "%s/postmaster.pid", pgdata);
if ((fp = pgut_fopen(path, "r")) == NULL)
return 0; /* No pid file, not an error on startup */
if (fscanf(fp, "%ld", &pid) != 1)
elog(ERROR_INCOMPATIBLE, "invalid data in PID file \"%s\"", path);
fclose(fp);
return pid;
}
/*
* utility routines
*/
static bool
postmaster_is_alive(pid_t pid)
{
/*
* Test to see if the process is still there. Note that we do not
* consider an EPERM failure to mean that the process is still there;
* EPERM must mean that the given PID belongs to some other userid, and
* considering the permissions on $PGDATA, that means it's not the
* postmaster we are after.
*
* Don't believe that our own PID or parent shell's PID is the postmaster,
* either. (Windows hasn't got getppid(), though.)
*/
if (pid == getpid())
return false;
#ifndef WIN32
if (pid == getppid())
return false;
#endif
if (kill(pid, 0) == 0)
return true;
return false;
}
/*
* original is do_status() in src/bin/pg_ctl/pg_ctl.c
* changes are:
* renamed from do_status() from do_status().
* return true if PG server is running.
* don't print any message.
* don't print postopts file.
* log with elog() in pgut library.
*/
bool
is_pg_running(void)
{
pgpid_t pid;
pid = get_pgpid();
if (pid == 0) /* 0 means no pid file */
return false;
if (pid < 0) /* standalone backend */
pid = -pid;
return postmaster_is_alive((pid_t) pid);
}
static void
compare_uint32(const char *name, uint32 server, uint32 backup)
{
if (server != backup)
elog(ERROR_INCOMPATIBLE,
"incompatible %s: server=%u / backup=%u",
name, server, backup);
}
static void
compare_uint64(const char *name, uint64 server, uint64 backup)
{
if (server != backup)
elog(ERROR_INCOMPATIBLE,
"incompatible %s: server=" UINT64_FORMAT " / backup=" UINT64_FORMAT,
name, server, backup);
}
static void
compare_double(const char *name, double server, double backup)
{
if (server != backup)
elog(ERROR_INCOMPATIBLE,
"incompatible %s: server=%f / backup=%f",
name, server, backup);
}
static void
compare_bool(const char *name, bool server, bool backup)
{
if ((server && !backup) || (!server && backup))
elog(ERROR_INCOMPATIBLE,
"incompatible %s: server=%s / backup=%s",
name, (server ? "true" : "false"), (backup ? "true" : "false"));
}
/* verify control file */
bool
verify_control_file(const char *pgdata, const char *catalog)
{
char path[MAXPGPATH];
ControlFileData ctrl;
bool in_pgdata;
bool in_backup;
snprintf(path, MAXPGPATH, "%s/global/pg_control", pgdata);
in_pgdata = read_control_file(path, &pgControlFile);
snprintf(path, MAXPGPATH, "%s/pg_control", catalog);
in_backup = read_control_file(path, &ctrl);
if (in_pgdata)
{
if (in_backup)
{
/* compare control files */
compare_uint32("pg_control version number",
pgControlFile.pg_control_version, ctrl.pg_control_version);
compare_uint32("catalog version number",
pgControlFile.catalog_version_no, ctrl.catalog_version_no);
compare_uint64("database system identifier",
pgControlFile.system_identifier, ctrl.system_identifier);
compare_uint32("maximum data alignment",
pgControlFile.maxAlign, ctrl.maxAlign);
compare_uint32("float format",
pgControlFile.floatFormat, ctrl.floatFormat);
compare_double("database block size",
pgControlFile.blcksz, ctrl.blcksz);
compare_uint32("blocks per segment of large relation",
pgControlFile.relseg_size, ctrl.relseg_size);
compare_uint32("wal block size",
pgControlFile.xlog_blcksz, ctrl.xlog_blcksz);
compare_uint32("bytes per wal segment",
pgControlFile.xlog_seg_size, ctrl.xlog_seg_size);
compare_uint32("maximum length of identifiers",
pgControlFile.nameDataLen, ctrl.nameDataLen);
compare_uint32("maximum columns in an index",
pgControlFile.indexMaxKeys, ctrl.indexMaxKeys);
compare_uint32("maximum size of a toast chunk",
pgControlFile.toast_max_chunk_size, ctrl.toast_max_chunk_size);
compare_bool("date/time type storage",
pgControlFile.enableIntTimes, ctrl.enableIntTimes);
compare_bool("float4 argument passing",
pgControlFile.float4ByVal, ctrl.float4ByVal);
compare_bool("float8 argument passing",
pgControlFile.float8ByVal, ctrl.float8ByVal);
}
else
{
/* write in_backup pg_control */
FILE *fp = pgut_fopen(path, "w");
if (fwrite(&pgControlFile, 1, sizeof(ControlFileData), fp) != sizeof(ControlFileData))
{
fclose(fp);
unlink(path);
ereport(ERROR,
(errcode_errno(),
errmsg("could not write control file \"%s\": ", path)));
}
fclose(fp);
}
return true;
}
else
{
if (in_backup)
{
/* volatile parts are unavialable */
memset(((char *) &ctrl) + offsetof(ControlFileData, state), 0,
offsetof(ControlFileData, maxAlign) - offsetof(ControlFileData, state));
pgControlFile = ctrl;
}
else
ereport(ERROR,
(errcode(ENOENT),
errmsg("control files not found")));
return false;
}
}
/*
* FIXME: ControlFileData might be changed in versions.
*/
static bool
read_control_file(const char *path, ControlFileData *ctrl)
{
FILE *fp;
pg_crc32 crc;
if ((fp = pgut_fopen(path, "r")) == NULL)
{
memset(ctrl, 0, sizeof(ControlFileData));
return false;
}
if (fread(ctrl, 1, sizeof(ControlFileData), fp) != sizeof(ControlFileData))
elog(ERROR_INCOMPATIBLE,
"could not read control file \"%s\": %s", path, strerror(errno));
fclose(fp);
/* Check the CRC. */
INIT_CRC32(crc);
COMP_CRC32(crc, ctrl, offsetof(ControlFileData, crc));
FIN_CRC32(crc);
if (!EQ_CRC32(crc, ctrl->crc))
elog(ERROR_INCOMPATIBLE,
"bad CRC checksum for control file \"%s\"", path);
return true;
}
/*-------------------------------------------------------------------------
*
* pg_ctl.c: operations for control file
*
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "postgres_fe.h"
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include "pg_rman.h"
/* PID can be negative for standalone backend */
typedef long pgpid_t;
static pgpid_t get_pgpid(void);
static bool postmaster_is_alive(pid_t pid);
static bool read_control_file(const char *path, ControlFileData *ctrl);
static pgpid_t
get_pgpid(void)
{
FILE *fp;
pgpid_t pid;
char path[MAXPGPATH];
snprintf(path, lengthof(path), "%s/postmaster.pid", pgdata);
if ((fp = pgut_fopen(path, "r")) == NULL)
return 0; /* No pid file, not an error on startup */
if (fscanf(fp, "%ld", &pid) != 1)
elog(ERROR_INCOMPATIBLE, "invalid data in PID file \"%s\"", path);
fclose(fp);
return pid;
}
/*
* utility routines
*/
static bool
postmaster_is_alive(pid_t pid)
{
/*
* Test to see if the process is still there. Note that we do not
* consider an EPERM failure to mean that the process is still there;
* EPERM must mean that the given PID belongs to some other userid, and
* considering the permissions on $PGDATA, that means it's not the
* postmaster we are after.
*
* Don't believe that our own PID or parent shell's PID is the postmaster,
* either. (Windows hasn't got getppid(), though.)
*/
if (pid == getpid())
return false;
#ifndef WIN32
if (pid == getppid())
return false;
#endif
if (kill(pid, 0) == 0)
return true;
return false;
}
/*
* original is do_status() in src/bin/pg_ctl/pg_ctl.c
* changes are:
* renamed from do_status() from do_status().
* return true if PG server is running.
* don't print any message.
* don't print postopts file.
* log with elog() in pgut library.
*/
bool
is_pg_running(void)
{
pgpid_t pid;
pid = get_pgpid();
if (pid == 0) /* 0 means no pid file */
return false;
if (pid < 0) /* standalone backend */
pid = -pid;
return postmaster_is_alive((pid_t) pid);
}
static void
compare_uint32(const char *name, uint32 server, uint32 backup)
{
if (server != backup)
elog(ERROR_INCOMPATIBLE,
"incompatible %s: server=%u / backup=%u",
name, server, backup);
}
static void
compare_uint64(const char *name, uint64 server, uint64 backup)
{
if (server != backup)
elog(ERROR_INCOMPATIBLE,
"incompatible %s: server=" UINT64_FORMAT " / backup=" UINT64_FORMAT,
name, server, backup);
}
static void
compare_double(const char *name, double server, double backup)
{
if (server != backup)
elog(ERROR_INCOMPATIBLE,
"incompatible %s: server=%f / backup=%f",
name, server, backup);
}
static void
compare_bool(const char *name, bool server, bool backup)
{
if ((server && !backup) || (!server && backup))
elog(ERROR_INCOMPATIBLE,
"incompatible %s: server=%s / backup=%s",
name, (server ? "true" : "false"), (backup ? "true" : "false"));
}
/* verify control file */
bool
verify_control_file(const char *pgdata, const char *catalog)
{
char path[MAXPGPATH];
ControlFileData ctrl;
bool in_pgdata;
bool in_backup;
snprintf(path, MAXPGPATH, "%s/global/pg_control", pgdata);
in_pgdata = read_control_file(path, &pgControlFile);
snprintf(path, MAXPGPATH, "%s/pg_control", catalog);
in_backup = read_control_file(path, &ctrl);
if (in_pgdata)
{
if (in_backup)
{
/* compare control files */
compare_uint32("pg_control version number",
pgControlFile.pg_control_version, ctrl.pg_control_version);
compare_uint32("catalog version number",
pgControlFile.catalog_version_no, ctrl.catalog_version_no);
compare_uint64("database system identifier",
pgControlFile.system_identifier, ctrl.system_identifier);
compare_uint32("maximum data alignment",
pgControlFile.maxAlign, ctrl.maxAlign);
compare_uint32("float format",
pgControlFile.floatFormat, ctrl.floatFormat);
compare_double("database block size",
pgControlFile.blcksz, ctrl.blcksz);
compare_uint32("blocks per segment of large relation",
pgControlFile.relseg_size, ctrl.relseg_size);
compare_uint32("wal block size",
pgControlFile.xlog_blcksz, ctrl.xlog_blcksz);
compare_uint32("bytes per wal segment",
pgControlFile.xlog_seg_size, ctrl.xlog_seg_size);
compare_uint32("maximum length of identifiers",
pgControlFile.nameDataLen, ctrl.nameDataLen);
compare_uint32("maximum columns in an index",
pgControlFile.indexMaxKeys, ctrl.indexMaxKeys);
compare_uint32("maximum size of a toast chunk",
pgControlFile.toast_max_chunk_size, ctrl.toast_max_chunk_size);
compare_bool("date/time type storage",
pgControlFile.enableIntTimes, ctrl.enableIntTimes);
compare_bool("float4 argument passing",
pgControlFile.float4ByVal, ctrl.float4ByVal);
compare_bool("float8 argument passing",
pgControlFile.float8ByVal, ctrl.float8ByVal);
}
else
{
/* write in_backup pg_control */
FILE *fp = pgut_fopen(path, "w");
if (fwrite(&pgControlFile, 1, sizeof(ControlFileData), fp) != sizeof(ControlFileData))
{
fclose(fp);
unlink(path);
ereport(ERROR,
(errcode_errno(),
errmsg("could not write control file \"%s\": ", path)));
}
fclose(fp);
}
return true;
}
else
{
if (in_backup)
{
/* volatile parts are unavialable */
memset(((char *) &ctrl) + offsetof(ControlFileData, state), 0,
offsetof(ControlFileData, maxAlign) - offsetof(ControlFileData, state));
pgControlFile = ctrl;
}
else
ereport(ERROR,
(errcode(ENOENT),
errmsg("control files not found")));
return false;
}
}
/*
* FIXME: ControlFileData might be changed in versions.
*/
static bool
read_control_file(const char *path, ControlFileData *ctrl)
{
FILE *fp;
pg_crc32 crc;
if ((fp = pgut_fopen(path, "r")) == NULL)
{
memset(ctrl, 0, sizeof(ControlFileData));
return false;
}
if (fread(ctrl, 1, sizeof(ControlFileData), fp) != sizeof(ControlFileData))
elog(ERROR_INCOMPATIBLE,
"could not read control file \"%s\": %s", path, strerror(errno));
fclose(fp);
/* Check the CRC. */
INIT_CRC32(crc);
COMP_CRC32(crc, ctrl, offsetof(ControlFileData, crc));
FIN_CRC32(crc);
if (!EQ_CRC32(crc, ctrl->crc))
elog(ERROR_INCOMPATIBLE,
"bad CRC checksum for control file \"%s\"", path);
return true;
}

666
pg_rman.h

@ -1,333 +1,333 @@
/*-------------------------------------------------------------------------
*
* pg_rman.h: Backup/Recovery manager for PostgreSQL.
*
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#ifndef PG_RMAN_H
#define PG_RMAN_H
#include "postgres_fe.h"
#include <limits.h>
#include "libpq-fe.h"
#include "pgut/pgut.h"
#include "access/xlogdefs.h"
#include "utils/pg_crc.h"
#include "parray.h"
#if PG_VERSION_NUM < 80200
#define XLOG_BLCKSZ BLCKSZ
#endif
#if PG_VERSION_NUM < 80300
#define TXID_CURRENT_SQL "SELECT transactionid FROM pg_locks WHERE locktype = 'transactionid' AND pid = pg_backend_pid();"
#include <sys/stat.h>
#else
#define TXID_CURRENT_SQL "SELECT txid_current();"
#endif
/* Directory/File names */
#define DATABASE_DIR "database"
#define ARCLOG_DIR "arclog"
#define SRVLOG_DIR "srvlog"
#define RESTORE_WORK_DIR "backup"
#define PG_XLOG_DIR "pg_xlog"
#define PG_TBLSPC_DIR "pg_tblspc"
#define TIMELINE_HISTORY_DIR "timeline_history"
#define BACKUP_INI_FILE "backup.ini"
#define PG_RMAN_INI_FILE "pg_rman.ini"
#define MKDIRS_SH_FILE "mkdirs.sh"
#define DATABASE_FILE_LIST "file_database.txt"
#define ARCLOG_FILE_LIST "file_arclog.txt"
#define SRVLOG_FILE_LIST "file_srvlog.txt"
#define SNAPSHOT_SCRIPT_FILE "snapshot_script"
/* Snapshot script command */
#define SNAPSHOT_FREEZE "freeze"
#define SNAPSHOT_UNFREEZE "unfreeze"
#define SNAPSHOT_SPLIT "split"
#define SNAPSHOT_RESYNC "resync"
#define SNAPSHOT_MOUNT "mount"
#define SNAPSHOT_UMOUNT "umount"
/* Direcotry/File permission */
#define DIR_PERMISSION (0700)
#define FILE_PERMISSION (0600)
/* Exit code */
#define ERROR_ARCHIVE_FAILED 20 /* cannot archive xlog file */
#define ERROR_NO_BACKUP 21 /* backup was not found in the catalog */
#define ERROR_CORRUPTED 22 /* backup catalog is corrupted */
#define ERROR_ALREADY_RUNNING 23 /* another pg_rman is running */
#define ERROR_PG_INCOMPATIBLE 24 /* block size is not compatible */
#define ERROR_PG_RUNNING 25 /* PostgreSQL server is running */
#define ERROR_PID_BROKEN 26 /* postmaster.pid file is broken */
/* backup mode file */
typedef struct pgFile
{
time_t mtime; /* time of last modification */
mode_t mode; /* protection (file type and permission) */
size_t size; /* size of the file */
size_t read_size; /* size of the portion read (if only some pages are
backed up partially, it's different from size) */
size_t write_size; /* size of the backed-up file. BYTES_INVALID means
that the file existed but was not backed up
because not modified since last backup. */
pg_crc32 crc; /* CRC value of the file, regular file only */
char *linked; /* path of the linked file */
bool is_datafile; /* true if the file is PostgreSQL data file */
char path[1]; /* path of the file */
} pgFile;
typedef struct pgBackupRange
{
time_t begin;
time_t end; /* begin +1 when one backup is target */
} pgBackupRange;
#define pgBackupRangeIsValid(range) \
(((range)->begin != (time_t) 0) || ((range)->end != (time_t) 0))
#define pgBackupRangeIsSingle(range) \
(pgBackupRangeIsValid(range) && (range)->begin == ((range)->end))
#define IsValidTime(tm) \
((tm.tm_sec >= 0 && tm.tm_sec <= 60) && /* range check for tm_sec (0-60) */ \
(tm.tm_min >= 0 && tm.tm_min <= 59) && /* range check for tm_min (0-59) */ \
(tm.tm_hour >= 0 && tm.tm_hour <= 23) && /* range check for tm_hour(0-23) */ \
(tm.tm_mday >= 1 && tm.tm_mday <= 31) && /* range check for tm_mday(1-31) */ \
(tm.tm_mon >= 0 && tm.tm_mon <= 11) && /* range check for tm_mon (0-23) */ \
(tm.tm_year + 1900 >= 1900)) /* range check for tm_year(70-) */
/* Backup status */
/* XXX re-order ? */
typedef enum BackupStatus
{
BACKUP_STATUS_INVALID, /* the pgBackup is invalid */
BACKUP_STATUS_OK, /* completed backup */
BACKUP_STATUS_RUNNING, /* running backup */
BACKUP_STATUS_ERROR, /* aborted because of unexpected error */
BACKUP_STATUS_DELETING, /* data files are being deleted */
BACKUP_STATUS_DELETED, /* data files have been deleted */
BACKUP_STATUS_DONE, /* completed but not validated yet */
BACKUP_STATUS_CORRUPT /* files are corrupted, not available */
} BackupStatus;
typedef enum BackupMode
{
BACKUP_MODE_INVALID,
BACKUP_MODE_ARCHIVE, /* archive only */
BACKUP_MODE_INCREMENTAL, /* incremental backup */
BACKUP_MODE_FULL /* full backup */
} BackupMode;
/*
* pg_rman takes backup into the directroy $BACKUP_PATH/<date>/<time>.
*
* status == -1 indicates the pgBackup is invalid.
*/
typedef struct pgBackup
{
/* Backup Level */
BackupMode backup_mode;
bool with_serverlog;
bool compress_data;
/* Status - one of BACKUP_STATUS_xxx */
BackupStatus status;
/* Timestamp, etc. */
TimeLineID tli;
XLogRecPtr start_lsn;
XLogRecPtr stop_lsn;
time_t start_time;
time_t end_time;
time_t recovery_time;
uint32 recovery_xid;
/* Size (-1 means not-backup'ed) */
int64 total_data_bytes;
int64 read_data_bytes;
int64 read_arclog_bytes;
int64 read_srvlog_bytes;
int64 write_bytes;
/* data/wal block size for compatibility check */
uint32 block_size;
uint32 wal_block_size;
} pgBackup;
/* special values of pgBackup */
#define KEEP_INFINITE (INT_MAX)
#define BYTES_INVALID (-1)
#define HAVE_DATABASE(backup) ((backup)->backup_mode >= BACKUP_MODE_INCREMENTAL)
#define HAVE_ARCLOG(backup) ((backup)->backup_mode >= BACKUP_MODE_ARCHIVE)
#define TOTAL_READ_SIZE(backup) \
((HAVE_DATABASE((backup)) ? (backup)->read_data_bytes : 0) + \
(HAVE_ARCLOG((backup)) ? (backup)->read_arclog_bytes : 0) + \
((backup)->with_serverlog ? (backup)->read_srvlog_bytes : 0))
typedef struct pgTimeLine
{
TimeLineID tli;
XLogRecPtr end;
} pgTimeLine;
typedef struct pgRecoveryTarget
{
bool time_specified;
time_t recovery_target_time;
bool xid_specified;
unsigned int recovery_target_xid;
bool recovery_target_inclusive;
} pgRecoveryTarget;
typedef enum CompressionMode
{
NO_COMPRESSION,
COMPRESSION,
DECOMPRESSION,
} CompressionMode;
/*
* return pointer that exceeds the length of prefix from character string.
* ex. str="/xxx/yyy/zzz", prefix="/xxx/yyy", return="zzz".
*/
#define JoinPathEnd(str, prefix) \
((strlen(str) <= strlen(prefix)) ? "" : str + strlen(prefix) + 1)
/* path configuration */
extern char *backup_path;
extern char *pgdata;
extern char *arclog_path;
extern char *srvlog_path;
/* common configuration */
extern bool verbose;
extern bool check;
/* current settings */
extern pgBackup current;
/* exclude directory list for $PGDATA file listing */
extern const char *pgdata_exclude[];
/* in backup.c */
extern int do_backup(bool smooth_checkpoint,
int keep_arclog_files,
int keep_arclog_days,
int keep_srvlog_files,
int keep_srvlog_days,
int keep_data_generations,
int keep_data_days);
extern BackupMode parse_backup_mode(const char *value, int elevel);
extern int get_server_version(void);
/* in restore.c */
extern int do_restore(const char *target_time,
const char *target_xid,
const char *target_inclusive,
TimeLineID target_tli);
/* in init.c */
extern int do_init(void);
/* in show.c */
extern int do_show(pgBackupRange *range, bool show_timeline, bool show_all);
/* in delete.c */
//extern int do_delete(pgBackupRange *range);
extern int do_delete(pgBackupRange *range, bool force);
extern void pgBackupDelete(int keep_generations, int keep_days);
/* in validate.c */
extern int do_validate(pgBackupRange *range);
extern void pgBackupValidate(pgBackup *backup, bool size_only, bool for_get_timeline, bool with_database);
/* in catalog.c */
extern pgBackup *catalog_get_backup(time_t timestamp);
extern parray *catalog_get_backup_list(const pgBackupRange *range);
extern pgBackup *catalog_get_last_data_backup(parray *backup_list);
extern pgBackup *catalog_get_last_arclog_backup(parray *backup_list);
extern pgBackup *catalog_get_last_srvlog_backup(parray *backup_list);
extern int catalog_lock(void);
extern void catalog_unlock(void);
extern void catalog_init_config(pgBackup *backup);
extern void pgBackupWriteConfigSection(FILE *out, pgBackup *backup);
extern void pgBackupWriteResultSection(FILE *out, pgBackup *backup);
extern void pgBackupWriteIni(pgBackup *backup);
extern void pgBackupGetPath(const pgBackup *backup, char *path, size_t len, const char *subdir);
extern int pgBackupCreateDir(pgBackup *backup);
extern void pgBackupFree(void *backup);
extern int pgBackupCompareId(const void *f1, const void *f2);
extern int pgBackupCompareIdDesc(const void *f1, const void *f2);
/* in dir.c */
extern void dir_list_file(parray *files, const char *root, const char *exclude[], bool omit_symlink, bool add_root);
extern void dir_print_mkdirs_sh(FILE *out, const parray *files, const char *root);
extern void dir_print_file_list(FILE *out, const parray *files, const char *root, const char *prefix);
extern parray *dir_read_file_list(const char *root, const char *file_txt);
extern int dir_create_dir(const char *path, mode_t mode);
extern void dir_copy_files(const char *from_root, const char *to_root);
extern void pgFileDelete(pgFile *file);
extern void pgFileFree(void *file);
extern pg_crc32 pgFileGetCRC(pgFile *file);
extern int pgFileComparePath(const void *f1, const void *f2);
extern int pgFileComparePathDesc(const void *f1, const void *f2);
extern int pgFileCompareMtime(const void *f1, const void *f2);
extern int pgFileCompareMtimeDesc(const void *f1, const void *f2);
/* in xlog.c */
extern bool xlog_is_complete_wal(const pgFile *file, int server_version);
extern bool xlog_logfname2lsn(const char *logfname, XLogRecPtr *lsn);
extern void xlog_fname(char *fname, size_t len, TimeLineID tli, XLogRecPtr *lsn);
/* in data.c */
extern bool backup_data_file(const char *from_root, const char *to_root,
pgFile *file, const XLogRecPtr *lsn, bool compress);
extern void restore_data_file(const char *from_root, const char *to_root,
pgFile *file, bool compress);
extern bool copy_file(const char *from_root, const char *to_root,
pgFile *file, CompressionMode compress);
/* in util.c */
extern void time2iso(char *buf, size_t len, time_t time);
extern const char *status2str(BackupStatus status);
extern void remove_trailing_space(char *buf, int comment_mark);
extern void remove_not_digit(char *buf, size_t len, const char *str);
/* in pgsql_src/pg_ctl.c */
extern bool is_pg_running(void);
/* access/xlog_internal.h */
#define XLogSegSize ((uint32) XLOG_SEG_SIZE)
#define XLogSegsPerFile (((uint32) 0xffffffff) / XLogSegSize)
#define XLogFileSize (XLogSegsPerFile * XLogSegSize)
#define NextLogSeg(logId, logSeg) \
do { \
if ((logSeg) >= XLogSegsPerFile-1) \
{ \
(logId)++; \
(logSeg) = 0; \
} \
else \
(logSeg)++; \
} while (0)
#define MAXFNAMELEN 64
#define XLogFileName(fname, tli, log, seg) \
snprintf(fname, MAXFNAMELEN, "%08X%08X%08X", tli, log, seg)
#endif /* PG_RMAN_H */
/*-------------------------------------------------------------------------
*
* pg_rman.h: Backup/Recovery manager for PostgreSQL.
*
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#ifndef PG_RMAN_H
#define PG_RMAN_H
#include "postgres_fe.h"
#include <limits.h>
#include "libpq-fe.h"
#include "pgut/pgut.h"
#include "access/xlogdefs.h"
#include "utils/pg_crc.h"
#include "parray.h"
#if PG_VERSION_NUM < 80200
#define XLOG_BLCKSZ BLCKSZ
#endif
#if PG_VERSION_NUM < 80300
#define TXID_CURRENT_SQL "SELECT transactionid FROM pg_locks WHERE locktype = 'transactionid' AND pid = pg_backend_pid();"
#include <sys/stat.h>
#else
#define TXID_CURRENT_SQL "SELECT txid_current();"
#endif
/* Directory/File names */
#define DATABASE_DIR "database"
#define ARCLOG_DIR "arclog"
#define SRVLOG_DIR "srvlog"
#define RESTORE_WORK_DIR "backup"
#define PG_XLOG_DIR "pg_xlog"
#define PG_TBLSPC_DIR "pg_tblspc"
#define TIMELINE_HISTORY_DIR "timeline_history"
#define BACKUP_INI_FILE "backup.ini"
#define PG_RMAN_INI_FILE "pg_rman.ini"
#define MKDIRS_SH_FILE "mkdirs.sh"
#define DATABASE_FILE_LIST "file_database.txt"
#define ARCLOG_FILE_LIST "file_arclog.txt"
#define SRVLOG_FILE_LIST "file_srvlog.txt"
#define SNAPSHOT_SCRIPT_FILE "snapshot_script"
/* Snapshot script command */
#define SNAPSHOT_FREEZE "freeze"
#define SNAPSHOT_UNFREEZE "unfreeze"
#define SNAPSHOT_SPLIT "split"
#define SNAPSHOT_RESYNC "resync"
#define SNAPSHOT_MOUNT "mount"
#define SNAPSHOT_UMOUNT "umount"
/* Direcotry/File permission */
#define DIR_PERMISSION (0700)
#define FILE_PERMISSION (0600)
/* Exit code */
#define ERROR_ARCHIVE_FAILED 20 /* cannot archive xlog file */
#define ERROR_NO_BACKUP 21 /* backup was not found in the catalog */
#define ERROR_CORRUPTED 22 /* backup catalog is corrupted */
#define ERROR_ALREADY_RUNNING 23 /* another pg_rman is running */
#define ERROR_PG_INCOMPATIBLE 24 /* block size is not compatible */
#define ERROR_PG_RUNNING 25 /* PostgreSQL server is running */
#define ERROR_PID_BROKEN 26 /* postmaster.pid file is broken */
/* backup mode file */
typedef struct pgFile
{
time_t mtime; /* time of last modification */
mode_t mode; /* protection (file type and permission) */
size_t size; /* size of the file */
size_t read_size; /* size of the portion read (if only some pages are
backed up partially, it's different from size) */
size_t write_size; /* size of the backed-up file. BYTES_INVALID means
that the file existed but was not backed up
because not modified since last backup. */
pg_crc32 crc; /* CRC value of the file, regular file only */
char *linked; /* path of the linked file */
bool is_datafile; /* true if the file is PostgreSQL data file */
char path[1]; /* path of the file */
} pgFile;
typedef struct pgBackupRange
{
time_t begin;
time_t end; /* begin +1 when one backup is target */
} pgBackupRange;
#define pgBackupRangeIsValid(range) \
(((range)->begin != (time_t) 0) || ((range)->end != (time_t) 0))
#define pgBackupRangeIsSingle(range) \
(pgBackupRangeIsValid(range) && (range)->begin == ((range)->end))
#define IsValidTime(tm) \
((tm.tm_sec >= 0 && tm.tm_sec <= 60) && /* range check for tm_sec (0-60) */ \
(tm.tm_min >= 0 && tm.tm_min <= 59) && /* range check for tm_min (0-59) */ \
(tm.tm_hour >= 0 && tm.tm_hour <= 23) && /* range check for tm_hour(0-23) */ \
(tm.tm_mday >= 1 && tm.tm_mday <= 31) && /* range check for tm_mday(1-31) */ \
(tm.tm_mon >= 0 && tm.tm_mon <= 11) && /* range check for tm_mon (0-23) */ \
(tm.tm_year + 1900 >= 1900)) /* range check for tm_year(70-) */
/* Backup status */
/* XXX re-order ? */
typedef enum BackupStatus
{
BACKUP_STATUS_INVALID, /* the pgBackup is invalid */
BACKUP_STATUS_OK, /* completed backup */
BACKUP_STATUS_RUNNING, /* running backup */
BACKUP_STATUS_ERROR, /* aborted because of unexpected error */
BACKUP_STATUS_DELETING, /* data files are being deleted */
BACKUP_STATUS_DELETED, /* data files have been deleted */
BACKUP_STATUS_DONE, /* completed but not validated yet */
BACKUP_STATUS_CORRUPT /* files are corrupted, not available */
} BackupStatus;
typedef enum BackupMode
{
BACKUP_MODE_INVALID,
BACKUP_MODE_ARCHIVE, /* archive only */
BACKUP_MODE_INCREMENTAL, /* incremental backup */
BACKUP_MODE_FULL /* full backup */
} BackupMode;
/*
* pg_rman takes backup into the directroy $BACKUP_PATH/<date>/<time>.
*
* status == -1 indicates the pgBackup is invalid.
*/
typedef struct pgBackup
{
/* Backup Level */
BackupMode backup_mode;
bool with_serverlog;
bool compress_data;
/* Status - one of BACKUP_STATUS_xxx */
BackupStatus status;
/* Timestamp, etc. */
TimeLineID tli;
XLogRecPtr start_lsn;
XLogRecPtr stop_lsn;
time_t start_time;
time_t end_time;
time_t recovery_time;
uint32 recovery_xid;
/* Size (-1 means not-backup'ed) */
int64 total_data_bytes;
int64 read_data_bytes;
int64 read_arclog_bytes;
int64 read_srvlog_bytes;
int64 write_bytes;
/* data/wal block size for compatibility check */
uint32 block_size;
uint32 wal_block_size;
} pgBackup;
/* special values of pgBackup */
#define KEEP_INFINITE (INT_MAX)
#define BYTES_INVALID (-1)
#define HAVE_DATABASE(backup) ((backup)->backup_mode >= BACKUP_MODE_INCREMENTAL)
#define HAVE_ARCLOG(backup) ((backup)->backup_mode >= BACKUP_MODE_ARCHIVE)
#define TOTAL_READ_SIZE(backup) \
((HAVE_DATABASE((backup)) ? (backup)->read_data_bytes : 0) + \
(HAVE_ARCLOG((backup)) ? (backup)->read_arclog_bytes : 0) + \
((backup)->with_serverlog ? (backup)->read_srvlog_bytes : 0))
typedef struct pgTimeLine
{
TimeLineID tli;
XLogRecPtr end;
} pgTimeLine;
typedef struct pgRecoveryTarget
{
bool time_specified;
time_t recovery_target_time;
bool xid_specified;
unsigned int recovery_target_xid;
bool recovery_target_inclusive;
} pgRecoveryTarget;
typedef enum CompressionMode
{
NO_COMPRESSION,
COMPRESSION,
DECOMPRESSION,
} CompressionMode;
/*
* return pointer that exceeds the length of prefix from character string.
* ex. str="/xxx/yyy/zzz", prefix="/xxx/yyy", return="zzz".
*/
#define JoinPathEnd(str, prefix) \
((strlen(str) <= strlen(prefix)) ? "" : str + strlen(prefix) + 1)
/* path configuration */
extern char *backup_path;
extern char *pgdata;
extern char *arclog_path;
extern char *srvlog_path;
/* common configuration */
extern bool verbose;
extern bool check;
/* current settings */
extern pgBackup current;
/* exclude directory list for $PGDATA file listing */
extern const char *pgdata_exclude[];
/* in backup.c */
extern int do_backup(bool smooth_checkpoint,
int keep_arclog_files,
int keep_arclog_days,
int keep_srvlog_files,
int keep_srvlog_days,
int keep_data_generations,
int keep_data_days);
extern BackupMode parse_backup_mode(const char *value, int elevel);
extern int get_server_version(void);
/* in restore.c */
extern int do_restore(const char *target_time,
const char *target_xid,
const char *target_inclusive,
TimeLineID target_tli);
/* in init.c */
extern int do_init(void);
/* in show.c */
extern int do_show(pgBackupRange *range, bool show_timeline, bool show_all);
/* in delete.c */
//extern int do_delete(pgBackupRange *range);
extern int do_delete(pgBackupRange *range, bool force);
extern void pgBackupDelete(int keep_generations, int keep_days);
/* in validate.c */
extern int do_validate(pgBackupRange *range);
extern void pgBackupValidate(pgBackup *backup, bool size_only, bool for_get_timeline, bool with_database);
/* in catalog.c */
extern pgBackup *catalog_get_backup(time_t timestamp);
extern parray *catalog_get_backup_list(const pgBackupRange *range);
extern pgBackup *catalog_get_last_data_backup(parray *backup_list);
extern pgBackup *catalog_get_last_arclog_backup(parray *backup_list);
extern pgBackup *catalog_get_last_srvlog_backup(parray *backup_list);
extern int catalog_lock(void);
extern void catalog_unlock(void);
extern void catalog_init_config(pgBackup *backup);
extern void pgBackupWriteConfigSection(FILE *out, pgBackup *backup);
extern void pgBackupWriteResultSection(FILE *out, pgBackup *backup);
extern void pgBackupWriteIni(pgBackup *backup);
extern void pgBackupGetPath(const pgBackup *backup, char *path, size_t len, const char *subdir);
extern int pgBackupCreateDir(pgBackup *backup);
extern void pgBackupFree(void *backup);
extern int pgBackupCompareId(const void *f1, const void *f2);
extern int pgBackupCompareIdDesc(const void *f1, const void *f2);
/* in dir.c */
extern void dir_list_file(parray *files, const char *root, const char *exclude[], bool omit_symlink, bool add_root);
extern void dir_print_mkdirs_sh(FILE *out, const parray *files, const char *root);
extern void dir_print_file_list(FILE *out, const parray *files, const char *root, const char *prefix);
extern parray *dir_read_file_list(const char *root, const char *file_txt);
extern int dir_create_dir(const char *path, mode_t mode);
extern void dir_copy_files(const char *from_root, const char *to_root);
extern void pgFileDelete(pgFile *file);
extern void pgFileFree(void *file);
extern pg_crc32 pgFileGetCRC(pgFile *file);
extern int pgFileComparePath(const void *f1, const void *f2);
extern int pgFileComparePathDesc(const void *f1, const void *f2);
extern int pgFileCompareMtime(const void *f1, const void *f2);
extern int pgFileCompareMtimeDesc(const void *f1, const void *f2);
/* in xlog.c */
extern bool xlog_is_complete_wal(const pgFile *file, int server_version);
extern bool xlog_logfname2lsn(const char *logfname, XLogRecPtr *lsn);
extern void xlog_fname(char *fname, size_t len, TimeLineID tli, XLogRecPtr *lsn);
/* in data.c */
extern bool backup_data_file(const char *from_root, const char *to_root,
pgFile *file, const XLogRecPtr *lsn, bool compress);
extern void restore_data_file(const char *from_root, const char *to_root,
pgFile *file, bool compress);
extern bool copy_file(const char *from_root, const char *to_root,
pgFile *file, CompressionMode compress);
/* in util.c */
extern void time2iso(char *buf, size_t len, time_t time);
extern const char *status2str(BackupStatus status);
extern void remove_trailing_space(char *buf, int comment_mark);
extern void remove_not_digit(char *buf, size_t len, const char *str);
/* in pgsql_src/pg_ctl.c */
extern bool is_pg_running(void);
/* access/xlog_internal.h */
#define XLogSegSize ((uint32) XLOG_SEG_SIZE)
#define XLogSegsPerFile (((uint32) 0xffffffff) / XLogSegSize)
#define XLogFileSize (XLogSegsPerFile * XLogSegSize)
#define NextLogSeg(logId, logSeg) \
do { \
if ((logSeg) >= XLogSegsPerFile-1) \
{ \
(logId)++; \
(logSeg) = 0; \
} \
else \
(logSeg)++; \
} while (0)
#define MAXFNAMELEN 64
#define XLogFileName(fname, tli, log, seg) \
snprintf(fname, MAXFNAMELEN, "%08X%08X%08X", tli, log, seg)
#endif /* PG_RMAN_H */

File diff suppressed because it is too large Load Diff

@ -1,105 +1,105 @@
/*-------------------------------------------------------------------------
*
* pg_ctl --- start/stops/restarts the PostgreSQL server
*
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
*
* $PostgreSQL: pgsql/src/bin/pg_ctl/pg_ctl.c,v 1.111 2009/06/11 14:49:07 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres_fe.h"
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include "pg_rman.h"
/* PID can be negative for standalone backend */
typedef long pgpid_t;
static pgpid_t get_pgpid(void);
static bool postmaster_is_alive(pid_t pid);
static char pid_file[MAXPGPATH];
static pgpid_t
get_pgpid(void)
{
FILE *pidf;
long pid;
snprintf(pid_file, lengthof(pid_file), "%s/postmaster.pid", pgdata);
pidf = fopen(pid_file, "r");
if (pidf == NULL)
{
/* No pid file, not an error on startup */
if (errno == ENOENT)
return 0;
else
elog(ERROR_SYSTEM, _("could not open PID file \"%s\": %s\n"),
pid_file, strerror(errno));
}
if (fscanf(pidf, "%ld", &pid) != 1)
elog(ERROR_PID_BROKEN, _("invalid data in PID file \"%s\"\n"), pid_file);
fclose(pidf);
return (pgpid_t) pid;
}
/*
* utility routines
*/
static bool
postmaster_is_alive(pid_t pid)
{
/*
* Test to see if the process is still there. Note that we do not
* consider an EPERM failure to mean that the process is still there;
* EPERM must mean that the given PID belongs to some other userid, and
* considering the permissions on $PGDATA, that means it's not the
* postmaster we are after.
*
* Don't believe that our own PID or parent shell's PID is the postmaster,
* either. (Windows hasn't got getppid(), though.)
*/
if (pid == getpid())
return false;
#ifndef WIN32
if (pid == getppid())
return false;
#endif
if (kill(pid, 0) == 0)
return true;
return false;
}
/*
* original is do_status() in src/bin/pg_ctl/pg_ctl.c
* changes are:
* renamed from do_status() from do_status().
* return true if PG server is running.
* don't print any message.
* don't print postopts file.
* log with elog() in pgut library.
*/
bool
is_pg_running(void)
{
pgpid_t pid;
pid = get_pgpid();
if (pid == 0) /* 0 means no pid file */
return false;
if (pid < 0) /* standalone backend */
pid = -pid;
return postmaster_is_alive((pid_t) pid);
}
/*-------------------------------------------------------------------------
*
* pg_ctl --- start/stops/restarts the PostgreSQL server
*
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
*
* $PostgreSQL: pgsql/src/bin/pg_ctl/pg_ctl.c,v 1.111 2009/06/11 14:49:07 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres_fe.h"
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include "pg_rman.h"
/* PID can be negative for standalone backend */
typedef long pgpid_t;
static pgpid_t get_pgpid(void);
static bool postmaster_is_alive(pid_t pid);
static char pid_file[MAXPGPATH];
static pgpid_t
get_pgpid(void)
{
FILE *pidf;
long pid;
snprintf(pid_file, lengthof(pid_file), "%s/postmaster.pid", pgdata);
pidf = fopen(pid_file, "r");
if (pidf == NULL)
{
/* No pid file, not an error on startup */
if (errno == ENOENT)
return 0;
else
elog(ERROR_SYSTEM, _("could not open PID file \"%s\": %s\n"),
pid_file, strerror(errno));
}
if (fscanf(pidf, "%ld", &pid) != 1)
elog(ERROR_PID_BROKEN, _("invalid data in PID file \"%s\"\n"), pid_file);
fclose(pidf);
return (pgpid_t) pid;
}
/*
* utility routines
*/
static bool
postmaster_is_alive(pid_t pid)
{
/*
* Test to see if the process is still there. Note that we do not
* consider an EPERM failure to mean that the process is still there;
* EPERM must mean that the given PID belongs to some other userid, and
* considering the permissions on $PGDATA, that means it's not the
* postmaster we are after.
*
* Don't believe that our own PID or parent shell's PID is the postmaster,
* either. (Windows hasn't got getppid(), though.)
*/
if (pid == getpid())
return false;
#ifndef WIN32
if (pid == getppid())
return false;
#endif
if (kill(pid, 0) == 0)
return true;
return false;
}
/*
* original is do_status() in src/bin/pg_ctl/pg_ctl.c
* changes are:
* renamed from do_status() from do_status().
* return true if PG server is running.
* don't print any message.
* don't print postopts file.
* log with elog() in pgut library.
*/
bool
is_pg_running(void)
{
pgpid_t pid;
pid = get_pgpid();
if (pid == 0) /* 0 means no pid file */
return false;
if (pid < 0) /* standalone backend */
pid = -pid;
return postmaster_is_alive((pid_t) pid);
}

@ -1,193 +1,193 @@
/*-------------------------------------------------------------------------
*
* pgut-port.c
*
* Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "c.h"
#include "pgut-port.h"
#undef flock
#include <unistd.h>
#include <fcntl.h>
#ifdef WIN32
#include <winioctl.h>
#define REPARSE_DATA_SIZE 1024
/* same layout as REPARSE_DATA_BUFFER, which is defined only in old winnt.h */
typedef struct REPARSE_DATA
{
ULONG ReparseTag;
WORD ReparseDataLength;
WORD Reserved;
union
{
struct
{
WORD SubstituteNameOffset;
WORD SubstituteNameLength;
WORD PrintNameOffset;
WORD PrintNameLength;
ULONG Flags;
WCHAR PathBuffer[1];
} Symlink;
struct
{
WORD SubstituteNameOffset;
WORD SubstituteNameLength;
WORD PrintNameOffset;
WORD PrintNameLength;
WCHAR PathBuffer[1];
} Mount;
struct
{
BYTE DataBuffer[REPARSE_DATA_SIZE];
} Generic;
};
} REPARSE_DATA;
ssize_t
readlink(const char *path, char *target, size_t size)
{
HANDLE handle;
DWORD attr;
REPARSE_DATA data;
DWORD datasize;
PCWSTR wpath;
int wlen;
int r;
attr = GetFileAttributes(path);
if (attr == INVALID_FILE_ATTRIBUTES)
{
_dosmaperr(GetLastError());
return -1;
}
if ((attr & FILE_ATTRIBUTE_REPARSE_POINT) == 0)
{
errno = EINVAL; /* not a symlink */
return -1;
}
handle = CreateFileA(path, 0,
FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
if (handle == INVALID_HANDLE_VALUE)
{
_dosmaperr(GetLastError());
return -1;
}
wpath = NULL;
if (DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, NULL, 0,
&data, sizeof(data), &datasize, NULL))
{
switch (data.ReparseTag)
{
case IO_REPARSE_TAG_MOUNT_POINT:
{
wpath = data.Mount.PathBuffer + data.Mount.SubstituteNameOffset;
wlen = data.Mount.SubstituteNameLength;
break;
}
case IO_REPARSE_TAG_SYMLINK:
{
wpath = data.Symlink.PathBuffer + data.Symlink.SubstituteNameOffset;
wlen = data.Symlink.SubstituteNameLength;
break;
}
}
}
if (wpath == NULL)
r = -1;
else
{
if (wcsncmp(wpath, L"\\??\\", 4) == 0 ||
wcsncmp(wpath, L"\\\\?\\", 4) == 0)
{
wpath += 4;
wlen -= 4;
}
r = WideCharToMultiByte(CP_ACP, 0, wpath, wlen, target, size, NULL, NULL);
}
CloseHandle(handle);
return r;
}
#endif
#ifdef PGUT_FLOCK
#ifdef WIN32
int
pgut_flock(int fd, int operation)
{
BOOL ret;
HANDLE handle = (HANDLE) _get_osfhandle(fd);
DWORD lo = 0;
DWORD hi = 0;
if (operation & LOCK_UN)
{
ret = UnlockFileEx(handle, 0, lo, hi, NULL);
}
else
{
DWORD flags = 0;
if (operation & LOCK_EX)
flags |= LOCKFILE_EXCLUSIVE_LOCK;
if (operation & LOCK_NB)
flags |= LOCKFILE_FAIL_IMMEDIATELY;
ret = LockFileEx(handle, flags, 0, lo, hi, NULL);
}
if (!ret)
{
_dosmaperr(GetLastError());
return -1;
}
return 0;
}
#else
int
pgut_flock(int fd, int operation)
{
struct flock lck;
int cmd;
memset(&lck, 0, sizeof(lck));
lck.l_whence = SEEK_SET;
lck.l_start = 0;
lck.l_len = 0;
lck.l_pid = getpid();
if (operation & LOCK_UN)
lck.l_type = F_UNLCK;
else if (operation & LOCK_EX)
lck.l_type = F_WRLCK;
else
lck.l_type = F_RDLCK;
if (operation & LOCK_NB)
cmd = F_SETLK;
else
cmd = F_SETLKW;
return fcntl(fd, cmd, &lck);
}
#endif
#endif
/*-------------------------------------------------------------------------
*
* pgut-port.c
*
* Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "c.h"
#include "pgut-port.h"
#undef flock
#include <unistd.h>
#include <fcntl.h>
#ifdef WIN32
#include <winioctl.h>
#define REPARSE_DATA_SIZE 1024
/* same layout as REPARSE_DATA_BUFFER, which is defined only in old winnt.h */
typedef struct REPARSE_DATA
{
ULONG ReparseTag;
WORD ReparseDataLength;
WORD Reserved;
union
{
struct
{
WORD SubstituteNameOffset;
WORD SubstituteNameLength;
WORD PrintNameOffset;
WORD PrintNameLength;
ULONG Flags;
WCHAR PathBuffer[1];
} Symlink;
struct
{
WORD SubstituteNameOffset;
WORD SubstituteNameLength;
WORD PrintNameOffset;
WORD PrintNameLength;
WCHAR PathBuffer[1];
} Mount;
struct
{
BYTE DataBuffer[REPARSE_DATA_SIZE];
} Generic;
};
} REPARSE_DATA;
ssize_t
readlink(const char *path, char *target, size_t size)
{
HANDLE handle;
DWORD attr;
REPARSE_DATA data;
DWORD datasize;
PCWSTR wpath;
int wlen;
int r;
attr = GetFileAttributes(path);
if (attr == INVALID_FILE_ATTRIBUTES)
{
_dosmaperr(GetLastError());
return -1;
}
if ((attr & FILE_ATTRIBUTE_REPARSE_POINT) == 0)
{
errno = EINVAL; /* not a symlink */
return -1;
}
handle = CreateFileA(path, 0,
FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
if (handle == INVALID_HANDLE_VALUE)
{
_dosmaperr(GetLastError());
return -1;
}
wpath = NULL;
if (DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, NULL, 0,
&data, sizeof(data), &datasize, NULL))
{
switch (data.ReparseTag)
{
case IO_REPARSE_TAG_MOUNT_POINT:
{
wpath = data.Mount.PathBuffer + data.Mount.SubstituteNameOffset;
wlen = data.Mount.SubstituteNameLength;
break;
}
case IO_REPARSE_TAG_SYMLINK:
{
wpath = data.Symlink.PathBuffer + data.Symlink.SubstituteNameOffset;
wlen = data.Symlink.SubstituteNameLength;
break;
}
}
}
if (wpath == NULL)
r = -1;
else
{
if (wcsncmp(wpath, L"\\??\\", 4) == 0 ||
wcsncmp(wpath, L"\\\\?\\", 4) == 0)
{
wpath += 4;
wlen -= 4;
}
r = WideCharToMultiByte(CP_ACP, 0, wpath, wlen, target, size, NULL, NULL);
}
CloseHandle(handle);
return r;
}
#endif
#ifdef PGUT_FLOCK
#ifdef WIN32
int
pgut_flock(int fd, int operation)
{
BOOL ret;
HANDLE handle = (HANDLE) _get_osfhandle(fd);
DWORD lo = 0;
DWORD hi = 0;
if (operation & LOCK_UN)
{
ret = UnlockFileEx(handle, 0, lo, hi, NULL);
}
else
{
DWORD flags = 0;
if (operation & LOCK_EX)
flags |= LOCKFILE_EXCLUSIVE_LOCK;
if (operation & LOCK_NB)
flags |= LOCKFILE_FAIL_IMMEDIATELY;
ret = LockFileEx(handle, flags, 0, lo, hi, NULL);
}
if (!ret)
{
_dosmaperr(GetLastError());
return -1;
}
return 0;
}
#else
int
pgut_flock(int fd, int operation)
{
struct flock lck;
int cmd;
memset(&lck, 0, sizeof(lck));
lck.l_whence = SEEK_SET;
lck.l_start = 0;
lck.l_len = 0;
lck.l_pid = getpid();
if (operation & LOCK_UN)
lck.l_type = F_UNLCK;
else if (operation & LOCK_EX)
lck.l_type = F_WRLCK;
else
lck.l_type = F_RDLCK;
if (operation & LOCK_NB)
cmd = F_SETLK;
else
cmd = F_SETLKW;
return fcntl(fd, cmd, &lck);
}
#endif
#endif

@ -1,50 +1,50 @@
/*-------------------------------------------------------------------------
*
* pgut-port.h
*
* Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#ifndef PGUT_PORT_H
#define PGUT_PORT_H
/*
* readlink ports
*/
#ifdef WIN32
#define S_IFLNK (0)
#define S_IRWXG (0)
#define S_IRWXO (0)
#define S_ISLNK(mode) (0)
extern ssize_t readlink(const char *path, char *target, size_t size);
#endif
/*
* flock ports
*/
#ifndef LOCK_EX
#define PGUT_FLOCK
#undef LOCK_SH
#undef LOCK_EX
#undef LOCK_UN
#undef LOCK_NB
#define LOCK_SH 1 /* Shared lock. */
#define LOCK_EX 2 /* Exclusive lock. */
#define LOCK_UN 8 /* Unlock. */
#define LOCK_NB 4 /* Don't block when locking. */
extern int pgut_flock(int fd, int operation);
#define flock pgut_flock
#endif
#endif /* PGUT_PORT_H */
/*-------------------------------------------------------------------------
*
* pgut-port.h
*
* Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#ifndef PGUT_PORT_H
#define PGUT_PORT_H
/*
* readlink ports
*/
#ifdef WIN32
#define S_IFLNK (0)
#define S_IRWXG (0)
#define S_IRWXO (0)
#define S_ISLNK(mode) (0)
extern ssize_t readlink(const char *path, char *target, size_t size);
#endif
/*
* flock ports
*/
#ifndef LOCK_EX
#define PGUT_FLOCK
#undef LOCK_SH
#undef LOCK_EX
#undef LOCK_UN
#undef LOCK_NB
#define LOCK_SH 1 /* Shared lock. */
#define LOCK_EX 2 /* Exclusive lock. */
#define LOCK_UN 8 /* Unlock. */
#define LOCK_NB 4 /* Don't block when locking. */
extern int pgut_flock(int fd, int operation);
#define flock pgut_flock
#endif
#endif /* PGUT_PORT_H */

File diff suppressed because it is too large Load Diff

@ -1,253 +1,253 @@
/*-------------------------------------------------------------------------
*
* pgut.h
*
* Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#ifndef PGUT_H
#define PGUT_H
#include "libpq-fe.h"
#include "pqexpbuffer.h"
#include <assert.h>
#include <sys/time.h>
#if !defined(C_H) && !defined(__cplusplus)
#ifndef bool
typedef char bool;
#endif
#ifndef true
#define true ((bool) 1)
#endif
#ifndef false
#define false ((bool) 0)
#endif
#endif
#define INFINITE_STR "INFINITE"
typedef enum YesNo
{
DEFAULT,
NO,
YES
} YesNo;
typedef enum pgut_optsrc
{
SOURCE_DEFAULT,
SOURCE_ENV,
SOURCE_FILE,
SOURCE_CMDLINE,
SOURCE_CONST
} pgut_optsrc;
/*
* type:
* b: bool (true)
* B: bool (false)
* f: pgut_optfn
* i: 32bit signed integer
* u: 32bit unsigned integer
* I: 64bit signed integer
* U: 64bit unsigned integer
* s: string
* t: time_t
* y: YesNo (YES)
* Y: YesNo (NO)
*/
typedef struct pgut_option
{
char type;
char sname; /* short name */
const char *lname; /* long name */
void *var; /* pointer to variable */
pgut_optsrc allowed; /* allowed source */
pgut_optsrc source; /* actual source */
} pgut_option;
typedef void (*pgut_optfn) (pgut_option *opt, const char *arg);
typedef void (*pgut_atexit_callback)(bool fatal, void *userdata);
/*
* pgut client variables and functions
*/
extern const char *PROGRAM_NAME;
extern const char *PROGRAM_VERSION;
extern const char *PROGRAM_URL;
extern const char *PROGRAM_EMAIL;
extern void pgut_help(bool details);
/*
* pgut framework variables and functions
*/
extern const char *dbname;
extern const char *host;
extern const char *port;
extern const char *username;
extern char *password;
extern bool debug;
extern bool quiet;
#ifndef PGUT_NO_PROMPT
extern YesNo prompt_password;
#endif
extern PGconn *connection;
extern bool interrupted;
extern void help(bool details);
extern int pgut_getopt(int argc, char **argv, pgut_option options[]);
extern void pgut_readopt(const char *path, pgut_option options[], int elevel);
extern void pgut_atexit_push(pgut_atexit_callback callback, void *userdata);
extern void pgut_atexit_pop(pgut_atexit_callback callback, void *userdata);
/*
* Database connections
*/
extern PGconn *pgut_connect(int elevel);
extern void pgut_disconnect(PGconn *conn);
extern PGresult *pgut_execute(PGconn* conn, const char *query, int nParams, const char **params, int elevel);
extern void pgut_command(PGconn* conn, const char *query, int nParams, const char **params, int elevel);
extern bool pgut_send(PGconn* conn, const char *query, int nParams, const char **params, int elevel);
extern int pgut_wait(int num, PGconn *connections[], struct timeval *timeout);
extern PGconn *reconnect_elevel(int elevel);
extern void reconnect(void);
extern void disconnect(void);
extern PGresult *execute_elevel(const char *query, int nParams, const char **params, int elevel);
extern PGresult *execute(const char *query, int nParams, const char **params);
extern void command(const char *query, int nParams, const char **params);
/*
* memory allocators
*/
extern void *pgut_malloc(size_t size);
extern void *pgut_realloc(void *p, size_t size);
extern char *pgut_strdup(const char *str);
extern char *strdup_with_len(const char *str, size_t len);
extern char *strdup_trim(const char *str);
#define pgut_new(type) ((type *) pgut_malloc(sizeof(type)))
#define pgut_newarray(type, n) ((type *) pgut_malloc(sizeof(type) * (n)))
/*
* file operations
*/
extern FILE *pgut_fopen(const char *path, const char *mode, bool missing_ok);
/*
* elog
*/
#define LOG (-4)
#define INFO (-3)
#define NOTICE (-2)
#define WARNING (-1)
#define HELP 1
#define ERROR 2
#define FATAL 3
#define PANIC 4
#define ERROR_SYSTEM 10 /* I/O or system error */
#define ERROR_NOMEM 11 /* memory exhausted */
#define ERROR_ARGS 12 /* some configurations are invalid */
#define ERROR_INTERRUPTED 13 /* interrupted by signal */
#define ERROR_PG_COMMAND 14 /* PostgreSQL query or command error */
#define ERROR_PG_CONNECT 15 /* PostgreSQL connection error */
#undef elog
extern void
elog(int elevel, const char *fmt, ...)
__attribute__((format(printf, 2, 3)));
/*
* Assert
*/
#undef Assert
#undef AssertArg
#undef AssertMacro
#ifdef USE_ASSERT_CHECKING
#define Assert(x) assert(x)
#define AssertArg(x) assert(x)
#define AssertMacro(x) assert(x)
#else
#define Assert(x) ((void) 0)
#define AssertArg(x) ((void) 0)
#define AssertMacro(x) ((void) 0)
#endif
/*
* StringInfo and string operations
*/
#define STRINGINFO_H
#define StringInfoData PQExpBufferData
#define StringInfo PQExpBuffer
#define makeStringInfo createPQExpBuffer
#define initStringInfo initPQExpBuffer
#define freeStringInfo destroyPQExpBuffer
#define termStringInfo termPQExpBuffer
#define resetStringInfo resetPQExpBuffer
#define enlargeStringInfo enlargePQExpBuffer
#define printfStringInfo printfPQExpBuffer /* reset + append */
#define appendStringInfo appendPQExpBuffer
#define appendStringInfoString appendPQExpBufferStr
#define appendStringInfoChar appendPQExpBufferChar
#define appendBinaryStringInfo appendBinaryPQExpBuffer
extern int appendStringInfoFile(StringInfo str, FILE *fp);
extern int appendStringInfoFd(StringInfo str, int fd);
extern bool parse_bool(const char *value, bool *result);
extern bool parse_bool_with_len(const char *value, size_t len, bool *result);
extern bool parse_int32(const char *value, int32 *result);
extern bool parse_uint32(const char *value, uint32 *result);
extern bool parse_int64(const char *value, int64 *result);
extern bool parse_uint64(const char *value, uint64 *result);
extern bool parse_time(const char *value, time_t *time);
#define IsSpace(c) (isspace((unsigned char)(c)))
#define IsAlpha(c) (isalpha((unsigned char)(c)))
#define IsAlnum(c) (isalnum((unsigned char)(c)))
#define IsIdentHead(c) (IsAlpha(c) || (c) == '_')
#define IsIdentBody(c) (IsAlnum(c) || (c) == '_')
#define ToLower(c) (tolower((unsigned char)(c)))
#define ToUpper(c) (toupper((unsigned char)(c)))
/*
* socket operations
*/
extern int wait_for_socket(int sock, struct timeval *timeout);
extern int wait_for_sockets(int nfds, fd_set *fds, struct timeval *timeout);
/*
* import from postgres.h and catalog/genbki.h in 8.4
*/
#if PG_VERSION_NUM < 80400
typedef unsigned long Datum;
typedef struct MemoryContextData *MemoryContext;
#define CATALOG(name,oid) typedef struct CppConcat(FormData_,name)
#define BKI_BOOTSTRAP
#define BKI_SHARED_RELATION
#define BKI_WITHOUT_OIDS
#define DATA(x) extern int no_such_variable
#define DESCR(x) extern int no_such_variable
#define SHDESCR(x) extern int no_such_variable
typedef int aclitem;
#endif
#ifdef WIN32
extern int sleep(unsigned int seconds);
extern int usleep(unsigned int usec);
#endif
#endif /* PGUT_H */
/*-------------------------------------------------------------------------
*
* pgut.h
*
* Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#ifndef PGUT_H
#define PGUT_H
#include "libpq-fe.h"
#include "pqexpbuffer.h"
#include <assert.h>
#include <sys/time.h>
#if !defined(C_H) && !defined(__cplusplus)
#ifndef bool
typedef char bool;
#endif
#ifndef true
#define true ((bool) 1)
#endif
#ifndef false
#define false ((bool) 0)
#endif
#endif
#define INFINITE_STR "INFINITE"
typedef enum YesNo
{
DEFAULT,
NO,
YES
} YesNo;
typedef enum pgut_optsrc
{
SOURCE_DEFAULT,
SOURCE_ENV,
SOURCE_FILE,
SOURCE_CMDLINE,
SOURCE_CONST
} pgut_optsrc;
/*
* type:
* b: bool (true)
* B: bool (false)
* f: pgut_optfn
* i: 32bit signed integer
* u: 32bit unsigned integer
* I: 64bit signed integer
* U: 64bit unsigned integer
* s: string
* t: time_t
* y: YesNo (YES)
* Y: YesNo (NO)
*/
typedef struct pgut_option
{
char type;
char sname; /* short name */
const char *lname; /* long name */
void *var; /* pointer to variable */
pgut_optsrc allowed; /* allowed source */
pgut_optsrc source; /* actual source */
} pgut_option;
typedef void (*pgut_optfn) (pgut_option *opt, const char *arg);
typedef void (*pgut_atexit_callback)(bool fatal, void *userdata);
/*
* pgut client variables and functions
*/
extern const char *PROGRAM_NAME;
extern const char *PROGRAM_VERSION;
extern const char *PROGRAM_URL;
extern const char *PROGRAM_EMAIL;
extern void pgut_help(bool details);
/*
* pgut framework variables and functions
*/
extern const char *dbname;
extern const char *host;
extern const char *port;
extern const char *username;
extern char *password;
extern bool debug;
extern bool quiet;
#ifndef PGUT_NO_PROMPT
extern YesNo prompt_password;
#endif
extern PGconn *connection;
extern bool interrupted;
extern void help(bool details);
extern int pgut_getopt(int argc, char **argv, pgut_option options[]);
extern void pgut_readopt(const char *path, pgut_option options[], int elevel);
extern void pgut_atexit_push(pgut_atexit_callback callback, void *userdata);
extern void pgut_atexit_pop(pgut_atexit_callback callback, void *userdata);
/*
* Database connections
*/
extern PGconn *pgut_connect(int elevel);
extern void pgut_disconnect(PGconn *conn);
extern PGresult *pgut_execute(PGconn* conn, const char *query, int nParams, const char **params, int elevel);
extern void pgut_command(PGconn* conn, const char *query, int nParams, const char **params, int elevel);
extern bool pgut_send(PGconn* conn, const char *query, int nParams, const char **params, int elevel);
extern int pgut_wait(int num, PGconn *connections[], struct timeval *timeout);
extern PGconn *reconnect_elevel(int elevel);
extern void reconnect(void);
extern void disconnect(void);
extern PGresult *execute_elevel(const char *query, int nParams, const char **params, int elevel);
extern PGresult *execute(const char *query, int nParams, const char **params);
extern void command(const char *query, int nParams, const char **params);
/*
* memory allocators
*/
extern void *pgut_malloc(size_t size);
extern void *pgut_realloc(void *p, size_t size);
extern char *pgut_strdup(const char *str);
extern char *strdup_with_len(const char *str, size_t len);
extern char *strdup_trim(const char *str);
#define pgut_new(type) ((type *) pgut_malloc(sizeof(type)))
#define pgut_newarray(type, n) ((type *) pgut_malloc(sizeof(type) * (n)))
/*
* file operations
*/
extern FILE *pgut_fopen(const char *path, const char *mode, bool missing_ok);
/*
* elog
*/
#define LOG (-4)
#define INFO (-3)
#define NOTICE (-2)
#define WARNING (-1)
#define HELP 1
#define ERROR 2
#define FATAL 3
#define PANIC 4
#define ERROR_SYSTEM 10 /* I/O or system error */
#define ERROR_NOMEM 11 /* memory exhausted */
#define ERROR_ARGS 12 /* some configurations are invalid */
#define ERROR_INTERRUPTED 13 /* interrupted by signal */
#define ERROR_PG_COMMAND 14 /* PostgreSQL query or command error */
#define ERROR_PG_CONNECT 15 /* PostgreSQL connection error */
#undef elog
extern void
elog(int elevel, const char *fmt, ...)
__attribute__((format(printf, 2, 3)));
/*
* Assert
*/
#undef Assert
#undef AssertArg
#undef AssertMacro
#ifdef USE_ASSERT_CHECKING
#define Assert(x) assert(x)
#define AssertArg(x) assert(x)
#define AssertMacro(x) assert(x)
#else
#define Assert(x) ((void) 0)
#define AssertArg(x) ((void) 0)
#define AssertMacro(x) ((void) 0)
#endif
/*
* StringInfo and string operations
*/
#define STRINGINFO_H
#define StringInfoData PQExpBufferData
#define StringInfo PQExpBuffer
#define makeStringInfo createPQExpBuffer
#define initStringInfo initPQExpBuffer
#define freeStringInfo destroyPQExpBuffer
#define termStringInfo termPQExpBuffer
#define resetStringInfo resetPQExpBuffer
#define enlargeStringInfo enlargePQExpBuffer
#define printfStringInfo printfPQExpBuffer /* reset + append */
#define appendStringInfo appendPQExpBuffer
#define appendStringInfoString appendPQExpBufferStr
#define appendStringInfoChar appendPQExpBufferChar
#define appendBinaryStringInfo appendBinaryPQExpBuffer
extern int appendStringInfoFile(StringInfo str, FILE *fp);
extern int appendStringInfoFd(StringInfo str, int fd);
extern bool parse_bool(const char *value, bool *result);
extern bool parse_bool_with_len(const char *value, size_t len, bool *result);
extern bool parse_int32(const char *value, int32 *result);
extern bool parse_uint32(const char *value, uint32 *result);
extern bool parse_int64(const char *value, int64 *result);
extern bool parse_uint64(const char *value, uint64 *result);
extern bool parse_time(const char *value, time_t *time);
#define IsSpace(c) (isspace((unsigned char)(c)))
#define IsAlpha(c) (isalpha((unsigned char)(c)))
#define IsAlnum(c) (isalnum((unsigned char)(c)))
#define IsIdentHead(c) (IsAlpha(c) || (c) == '_')
#define IsIdentBody(c) (IsAlnum(c) || (c) == '_')
#define ToLower(c) (tolower((unsigned char)(c)))
#define ToUpper(c) (toupper((unsigned char)(c)))
/*
* socket operations
*/
extern int wait_for_socket(int sock, struct timeval *timeout);
extern int wait_for_sockets(int nfds, fd_set *fds, struct timeval *timeout);
/*
* import from postgres.h and catalog/genbki.h in 8.4
*/
#if PG_VERSION_NUM < 80400
typedef unsigned long Datum;
typedef struct MemoryContextData *MemoryContext;
#define CATALOG(name,oid) typedef struct CppConcat(FormData_,name)
#define BKI_BOOTSTRAP
#define BKI_SHARED_RELATION
#define BKI_WITHOUT_OIDS
#define DATA(x) extern int no_such_variable
#define DESCR(x) extern int no_such_variable
#define SHDESCR(x) extern int no_such_variable
typedef int aclitem;
#endif
#ifdef WIN32
extern int sleep(unsigned int seconds);
extern int usleep(unsigned int usec);
#endif
#endif /* PGUT_H */

340
queue.c

@ -1,170 +1,170 @@
/*-------------------------------------------------------------------------
*
* queue.c: Job queue with thread pooling.
*
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
#include "pgut/pgut-pthread.h"
struct JobQueue
{
pthread_mutex_t mutex; /* protects the queue data */
pthread_cond_t anyjobs; /* fired if any jobs */
pthread_cond_t nojobs; /* fired if no jobs */
List *threads; /* list of worker thread handles */
List *jobs; /* pending jobs */
volatile int maximum; /* maximum allowed threads */
volatile int idle; /* number of idle threads */
volatile bool terminated; /* in termination? */
};
static void *worker_thread(void *arg);
JobQueue *
JobQueue_new(int nthreads)
{
JobQueue *queue;
Assert(nthreads >= 1);
queue = pgut_new(JobQueue);
pthread_mutex_init(&queue->mutex, NULL);
pthread_cond_init(&queue->anyjobs, NULL);
pthread_cond_init(&queue->nojobs, NULL);
queue->threads = NIL;
queue->jobs = NIL;
queue->maximum = nthreads;
queue->idle = 0;
queue->terminated = false;
return queue;
}
/*
* Job must be allocated with malloc. The ownership will be granted to
* the queue.
*/
void
JobQueue_push(JobQueue *queue, Job *job)
{
Assert(queue);
Assert(!queue->terminated);
Assert(job);
Assert(job->routine);
pgut_mutex_lock(&queue->mutex);
queue->jobs = lappend(queue->jobs, job);
if (queue->idle > 0)
pthread_cond_signal(&queue->anyjobs);
else if (list_length(queue->threads) < queue->maximum)
{
pthread_t th;
if (pthread_create(&th, NULL, worker_thread, queue))
ereport(ERROR,
(errcode_errno(),
errmsg("could not create thread: ")));
queue->threads = lappend(queue->threads, (void *) th);
Assert(list_length(queue->threads) <= queue->maximum);
}
pthread_mutex_unlock(&queue->mutex);
}
/* wait for all job finished */
void
JobQueue_wait(JobQueue *queue)
{
Assert(queue);
Assert(!queue->terminated);
pgut_mutex_lock(&queue->mutex);
while (queue->jobs || queue->idle < list_length(queue->threads))
pgut_cond_wait(&queue->nojobs, &queue->mutex);
pthread_mutex_unlock(&queue->mutex);
}
/* Free job queue. All pending jobs are also discarded. */
void
JobQueue_free(JobQueue *queue)
{
ListCell *cell;
if (queue == NULL)
return;
Assert(!queue->terminated);
/* Terminate all threads. */
pgut_mutex_lock(&queue->mutex);
queue->terminated = true;
pthread_cond_broadcast(&queue->anyjobs);
pthread_mutex_unlock(&queue->mutex);
/*
* Wait for all threads.
* XXX: cancel thread for long running jobs?
*/
foreach(cell, queue->threads)
{
pthread_t th = (pthread_t) lfirst(cell);
pthread_join(th, NULL);
}
list_free(queue->threads);
/* Free all pending jobs, though it must be avoided. */
list_free_deep(queue->jobs);
pthread_cond_destroy(&queue->nojobs);
pthread_cond_destroy(&queue->anyjobs);
pthread_mutex_destroy(&queue->mutex);
free(queue);
}
static void *
worker_thread(void *arg)
{
JobQueue *queue = (JobQueue *) arg;
pgut_mutex_lock(&queue->mutex);
while (!queue->terminated)
{
Job *job;
if (queue->jobs == NIL)
{
queue->idle++;
/* notify if done all jobs */
if (queue->idle >= list_length(queue->threads))
pthread_cond_broadcast(&queue->nojobs);
pgut_cond_wait(&queue->anyjobs, &queue->mutex);
queue->idle--;
if (queue->terminated)
break;
}
if (queue->jobs == NIL)
continue; /* job might have done by another worker */
job = linitial(queue->jobs);
queue->jobs = list_delete_first(queue->jobs);
pthread_mutex_unlock(&queue->mutex);
job->routine(job);
free(job);
pgut_mutex_lock(&queue->mutex);
}
pthread_mutex_unlock(&queue->mutex);
return NULL;
}
/*-------------------------------------------------------------------------
*
* queue.c: Job queue with thread pooling.
*
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
#include "pgut/pgut-pthread.h"
struct JobQueue
{
pthread_mutex_t mutex; /* protects the queue data */
pthread_cond_t anyjobs; /* fired if any jobs */
pthread_cond_t nojobs; /* fired if no jobs */
List *threads; /* list of worker thread handles */
List *jobs; /* pending jobs */
volatile int maximum; /* maximum allowed threads */
volatile int idle; /* number of idle threads */
volatile bool terminated; /* in termination? */
};
static void *worker_thread(void *arg);
JobQueue *
JobQueue_new(int nthreads)
{
JobQueue *queue;
Assert(nthreads >= 1);
queue = pgut_new(JobQueue);
pthread_mutex_init(&queue->mutex, NULL);
pthread_cond_init(&queue->anyjobs, NULL);
pthread_cond_init(&queue->nojobs, NULL);
queue->threads = NIL;
queue->jobs = NIL;
queue->maximum = nthreads;
queue->idle = 0;
queue->terminated = false;
return queue;
}
/*
* Job must be allocated with malloc. The ownership will be granted to
* the queue.
*/
void
JobQueue_push(JobQueue *queue, Job *job)
{
Assert(queue);
Assert(!queue->terminated);
Assert(job);
Assert(job->routine);
pgut_mutex_lock(&queue->mutex);
queue->jobs = lappend(queue->jobs, job);
if (queue->idle > 0)
pthread_cond_signal(&queue->anyjobs);
else if (list_length(queue->threads) < queue->maximum)
{
pthread_t th;
if (pthread_create(&th, NULL, worker_thread, queue))
ereport(ERROR,
(errcode_errno(),
errmsg("could not create thread: ")));
queue->threads = lappend(queue->threads, (void *) th);
Assert(list_length(queue->threads) <= queue->maximum);
}
pthread_mutex_unlock(&queue->mutex);
}
/* wait for all job finished */
void
JobQueue_wait(JobQueue *queue)
{
Assert(queue);
Assert(!queue->terminated);
pgut_mutex_lock(&queue->mutex);
while (queue->jobs || queue->idle < list_length(queue->threads))
pgut_cond_wait(&queue->nojobs, &queue->mutex);
pthread_mutex_unlock(&queue->mutex);
}
/* Free job queue. All pending jobs are also discarded. */
void
JobQueue_free(JobQueue *queue)
{
ListCell *cell;
if (queue == NULL)
return;
Assert(!queue->terminated);
/* Terminate all threads. */
pgut_mutex_lock(&queue->mutex);
queue->terminated = true;
pthread_cond_broadcast(&queue->anyjobs);
pthread_mutex_unlock(&queue->mutex);
/*
* Wait for all threads.
* XXX: cancel thread for long running jobs?
*/
foreach(cell, queue->threads)
{
pthread_t th = (pthread_t) lfirst(cell);
pthread_join(th, NULL);
}
list_free(queue->threads);
/* Free all pending jobs, though it must be avoided. */
list_free_deep(queue->jobs);
pthread_cond_destroy(&queue->nojobs);
pthread_cond_destroy(&queue->anyjobs);
pthread_mutex_destroy(&queue->mutex);
free(queue);
}
static void *
worker_thread(void *arg)
{
JobQueue *queue = (JobQueue *) arg;
pgut_mutex_lock(&queue->mutex);
while (!queue->terminated)
{
Job *job;
if (queue->jobs == NIL)
{
queue->idle++;
/* notify if done all jobs */
if (queue->idle >= list_length(queue->threads))
pthread_cond_broadcast(&queue->nojobs);
pgut_cond_wait(&queue->anyjobs, &queue->mutex);
queue->idle--;
if (queue->terminated)
break;
}
if (queue->jobs == NIL)
continue; /* job might have done by another worker */
job = linitial(queue->jobs);
queue->jobs = list_delete_first(queue->jobs);
pthread_mutex_unlock(&queue->mutex);
job->routine(job);
free(job);
pgut_mutex_lock(&queue->mutex);
}
pthread_mutex_unlock(&queue->mutex);
return NULL;
}

2118
restore.c

File diff suppressed because it is too large Load Diff

@ -1,306 +1,306 @@
#!/bin/sh
#############################################################################
# Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
#############################################################################
CMD_SUDO="/usr/bin/sudo"
CMD_LVCREATE="${CMD_SUDO} /usr/sbin/lvcreate"
CMD_LVREMOVE="${CMD_SUDO} /usr/sbin/lvremove"
CMD_MOUNT="${CMD_SUDO} /bin/mount"
CMD_UMOUNT="${CMD_SUDO} /bin/umount"
INFO=-2
WARNING=-1
ERROR=0
#
# Please set the information necessary for the snapshot acquisition
# to each array.
#
# The following arrays are one sets.
#
# SNAPSHOT_NAME .... name for snapshot volume
# SNAPSHOT_SIZE .... size to allocate for snapshot volume
# SNAPSHOT_VG ...... volume group to which logical volume to create snapshot belongs
# SNAPSHOT_LV ...... logical volume to create snapshot
# SNAPSHOT_MOUNT ... mount point to file-system of snapshot volume
#
# increase and decrease the slot in proportion to the number of acquisition
# of snapshots.
#
# example:
#
# SNAPSHOT_NAME[0]="snap00"
# SNAPSHOT_SIZE[0]="2G"
# SNAPSHOT_VG[0]="/dev/VolumeGroup00"
# SNAPSHOT_LV[0]="/dev/VolumeGroup00/LogicalVolume00"
# SNAPSHOT_MOUNT[0]="/mnt/pgdata"
#
# SNAPSHOT_NAME[1]="snap01"
# SNAPSHOT_SIZE[1]="2G"
# SNAPSHOT_VG[1]="/dev/VolumeGroup00"
# SNAPSHOT_LV[1]="/dev/VolumeGroup00/LogicalVolume01"
# SNAPSHOT_MOUNT[1]="/mnt/tblspc/account"
#
#SNAPSHOT_NAME[0]=""
#SNAPSHOT_SIZE[0]=""
#SNAPSHOT_VG[0]=""
#SNAPSHOT_LV[0]=""
#SNAPSHOT_MOUNT[0]=""
#
# Please set the tablespace name and tablespace storage path in snapshot
# to each array.
#
# The following arrays are one sets.
#
# SNAPSHOT_TBLSPC ........ name of tablespace in snapshot volume
# SNAPSHOT_TBLSPC_DIR .... stored directory of tablespace in snapshot volume
#
# Note: set 'PG-DATA' to SNAPSHOT_TBLSPC when PGDATA in snapshot volume.
#
# increase and decrease the slot in proportion to the number of acquisition
# of tablespace.
#
# example:
#
# SNAPSHOT_TBLSPC[0]="PG-DATA"
# SNAPSHOT_TBLSPC_DIR[0]="/mnt/pgdata/pgdata"
# SNAPSHOT_TBLSPC[1]="custom"
# SNAPSHOT_TBLSPC_DIR[1]="/mnt/tblspc_custom/tblspc/custom"
# SNAPSHOT_TBLSPC[2]="account"
# SNAPSHOT_TBLSPC_DIR[2]="/mnt/tblspc_account/tblspc/account"
#
#SNAPSHOT_TBLSPC[0]=""
#SNAPSHOT_TBLSPC_DIR[0]=""
#
# argument of the command.
# this variables are set by set_args().
#
ARGS_SS_NAME="" # SNAPSHOT_NAME[N]
ARGS_SS_SIZE="" # SNAPSHOT_SIZE[N]
ARGS_SS_VG="" # SNAPSHOT_VG[N]
ARGS_SS_LV="" # SNAPSHOT_LV[N]
ARGS_SS_MOUNT="" # SNAPSHOT_MOUNT[N]
#
# implement of interface 'freeze'.
# don't remove this function even if there is no necessity.
#
function freeze()
{
# nothing to do
return
}
#
# implement of interface 'unfreeze'.
# don't remove this function even if there is no necessity.
#
function unfreeze()
{
# nothing to do
return
}
#
# implement of interface 'split'.
# create a snapshot volume from the setting of the specified slot.
# don't remove this function even if there is no necessity.
#
function split()
{
local i=0
for ss_name in "${SNAPSHOT_NAME[@]}"
do
set_args "${ss_name}" "${SNAPSHOT_SIZE[${i}]}" "" "${SNAPSHOT_LV[${i}]}" ""
execute_split
i=$(expr ${i} + 1)
done
# print tablespace name
i=0
for tblspc in "${SNAPSHOT_TBLSPC[@]}"
do
local tblspc="${SNAPSHOT_TBLSPC[${i}]}"
echo "${tblspc}"
i=$(expr ${i} + 1)
done
return
}
#
# implement of interface 'resync'.
# remove a snapshot volume from the setting of the specified slot.
# don't remove this function even if there is no necessity.
#
function resync()
{
local i=0
for ss_name in "${SNAPSHOT_NAME[@]}"
do
set_args "${ss_name}" "" "${SNAPSHOT_VG[${i}]}" "" ""
execute_resync
i=$(expr ${i} + 1)
done
return
}
#
# implement of interface 'mount'.
# create mount point of the snapshot volume to the file-system.
# don't remove this function even if there is no necessity.
#
function mount()
{
local i=0
for ss_name in "${SNAPSHOT_NAME[@]}"
do
set_args "${ss_name}" "" "${SNAPSHOT_VG[${i}]}" "" "${SNAPSHOT_MOUNT[${i}]}"
execute_mount
i=$(expr ${i} + 1)
done
# print tablespace name and stored directory
i=0
for tblspc in "${SNAPSHOT_TBLSPC[@]}"
do
local tblspc_mp="${SNAPSHOT_TBLSPC_DIR[${i}]}"
echo "${tblspc}=${tblspc_mp}"
i=$(expr ${i} + 1)
done
return
}
#
# implement of interface 'umount'.
# remove mount point of the snapshot volume from the file-system.
# don't remove this function even if there is no necessity.
#
function umount()
{
for ss_mp in "${SNAPSHOT_MOUNT[@]}"
do
set_args "" "" "" "" "${ss_mp}"
execute_umount
done
return
}
#
# create the snapshot volume.
#
function execute_split()
{
${CMD_LVCREATE} --snapshot --size=${ARGS_SS_SIZE} --name="${ARGS_SS_NAME}" "${ARGS_SS_LV}" > /dev/null
[ ${?} -ne 0 ] && \
print_log ${ERROR} "${CMD_LVCREATE} command failed: ${ARGS_SS_LV}"
}
#
# remove the snapshot volume.
#
function execute_resync()
{
${CMD_LVREMOVE} -f "${ARGS_SS_VG}/${ARGS_SS_NAME}" > /dev/null
[ ${?} -ne 0 ] && \
print_log ${ERROR} "${CMD_LVREMOVE} command failed: ${ARGS_SS_VG}/${ARGS_SS_NAME}"
}
#
# mount the snapshot volume to file-system.
#
function execute_mount()
{
${CMD_MOUNT} "${ARGS_SS_VG}/${ARGS_SS_NAME}" "${ARGS_SS_MOUNT}" > /dev/null
[ ${?} -ne 0 ] && \
print_log ${ERROR} "${CMD_MOUNT} command failed: ${ARGS_SS_MOUNT}"
}
#
# unmount the directory from file-system in snapshot volume.
#
function execute_umount()
{
${CMD_UMOUNT} "${ARGS_SS_MOUNT}" > /dev/null
[ ${?} -ne 0 ] && \
print_log ${ERROR} "${CMD_UMOUNT} command failed: ${ARGS_SS_MOUNT}"
}
#
# set argument of command to execute.
#
set_args()
{
ARGS_SS_NAME="${1}"
ARGS_SS_SIZE="${2}"
ARGS_SS_VG="${3}"
ARGS_SS_LV="${4}"
ARGS_SS_MOUNT="${5}"
}
#
# output the log message and abort the script when level <= ERROR
#
function print_log()
{
local level="${1}"
local message="${2}"
# if cleanup enable change ERROR to WARNING
[ -n "${cleanup}" -a ${level} -ge 0 ] && \
level=${WARNING}
case "${level}" in
${INFO} ) # INFO
echo "INFO: ${message}" 1>&2
;;
${WARNING} ) # WARNING
echo "WARNING: ${message}" 1>&2
;;
${ERROR} ) # ERROR
echo "ERROR: ${message}" 1>&2
;;
esac
[ ${level} -ge 0 ] && exit
}
#
# main
#
command="${1}"
cleanup="${2}"
case "${command}" in
"freeze" )
freeze
;;
"unfreeze" )
unfreeze
;;
"split" )
split
;;
"resync" )
resync
;;
"mount" )
mount
;;
"umount" )
umount
;;
* )
print_log ${ERROR} "specified invalid command: ${command} (internal error)"
;;
esac
echo "SUCCESS"
exit
#!/bin/sh
#############################################################################
# Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
#############################################################################
CMD_SUDO="/usr/bin/sudo"
CMD_LVCREATE="${CMD_SUDO} /usr/sbin/lvcreate"
CMD_LVREMOVE="${CMD_SUDO} /usr/sbin/lvremove"
CMD_MOUNT="${CMD_SUDO} /bin/mount"
CMD_UMOUNT="${CMD_SUDO} /bin/umount"
INFO=-2
WARNING=-1
ERROR=0
#
# Please set the information necessary for the snapshot acquisition
# to each array.
#
# The following arrays are one sets.
#
# SNAPSHOT_NAME .... name for snapshot volume
# SNAPSHOT_SIZE .... size to allocate for snapshot volume
# SNAPSHOT_VG ...... volume group to which logical volume to create snapshot belongs
# SNAPSHOT_LV ...... logical volume to create snapshot
# SNAPSHOT_MOUNT ... mount point to file-system of snapshot volume
#
# increase and decrease the slot in proportion to the number of acquisition
# of snapshots.
#
# example:
#
# SNAPSHOT_NAME[0]="snap00"
# SNAPSHOT_SIZE[0]="2G"
# SNAPSHOT_VG[0]="/dev/VolumeGroup00"
# SNAPSHOT_LV[0]="/dev/VolumeGroup00/LogicalVolume00"
# SNAPSHOT_MOUNT[0]="/mnt/pgdata"
#
# SNAPSHOT_NAME[1]="snap01"
# SNAPSHOT_SIZE[1]="2G"
# SNAPSHOT_VG[1]="/dev/VolumeGroup00"
# SNAPSHOT_LV[1]="/dev/VolumeGroup00/LogicalVolume01"
# SNAPSHOT_MOUNT[1]="/mnt/tblspc/account"
#
#SNAPSHOT_NAME[0]=""
#SNAPSHOT_SIZE[0]=""
#SNAPSHOT_VG[0]=""
#SNAPSHOT_LV[0]=""
#SNAPSHOT_MOUNT[0]=""
#
# Please set the tablespace name and tablespace storage path in snapshot
# to each array.
#
# The following arrays are one sets.
#
# SNAPSHOT_TBLSPC ........ name of tablespace in snapshot volume
# SNAPSHOT_TBLSPC_DIR .... stored directory of tablespace in snapshot volume
#
# Note: set 'PG-DATA' to SNAPSHOT_TBLSPC when PGDATA in snapshot volume.
#
# increase and decrease the slot in proportion to the number of acquisition
# of tablespace.
#
# example:
#
# SNAPSHOT_TBLSPC[0]="PG-DATA"
# SNAPSHOT_TBLSPC_DIR[0]="/mnt/pgdata/pgdata"
# SNAPSHOT_TBLSPC[1]="custom"
# SNAPSHOT_TBLSPC_DIR[1]="/mnt/tblspc_custom/tblspc/custom"
# SNAPSHOT_TBLSPC[2]="account"
# SNAPSHOT_TBLSPC_DIR[2]="/mnt/tblspc_account/tblspc/account"
#
#SNAPSHOT_TBLSPC[0]=""
#SNAPSHOT_TBLSPC_DIR[0]=""
#
# argument of the command.
# this variables are set by set_args().
#
ARGS_SS_NAME="" # SNAPSHOT_NAME[N]
ARGS_SS_SIZE="" # SNAPSHOT_SIZE[N]
ARGS_SS_VG="" # SNAPSHOT_VG[N]
ARGS_SS_LV="" # SNAPSHOT_LV[N]
ARGS_SS_MOUNT="" # SNAPSHOT_MOUNT[N]
#
# implement of interface 'freeze'.
# don't remove this function even if there is no necessity.
#
function freeze()
{
# nothing to do
return
}
#
# implement of interface 'unfreeze'.
# don't remove this function even if there is no necessity.
#
function unfreeze()
{
# nothing to do
return
}
#
# implement of interface 'split'.
# create a snapshot volume from the setting of the specified slot.
# don't remove this function even if there is no necessity.
#
function split()
{
local i=0
for ss_name in "${SNAPSHOT_NAME[@]}"
do
set_args "${ss_name}" "${SNAPSHOT_SIZE[${i}]}" "" "${SNAPSHOT_LV[${i}]}" ""
execute_split
i=$(expr ${i} + 1)
done
# print tablespace name
i=0
for tblspc in "${SNAPSHOT_TBLSPC[@]}"
do
local tblspc="${SNAPSHOT_TBLSPC[${i}]}"
echo "${tblspc}"
i=$(expr ${i} + 1)
done
return
}
#
# implement of interface 'resync'.
# remove a snapshot volume from the setting of the specified slot.
# don't remove this function even if there is no necessity.
#
function resync()
{
local i=0
for ss_name in "${SNAPSHOT_NAME[@]}"
do
set_args "${ss_name}" "" "${SNAPSHOT_VG[${i}]}" "" ""
execute_resync
i=$(expr ${i} + 1)
done
return
}
#
# implement of interface 'mount'.
# create mount point of the snapshot volume to the file-system.
# don't remove this function even if there is no necessity.
#
function mount()
{
local i=0
for ss_name in "${SNAPSHOT_NAME[@]}"
do
set_args "${ss_name}" "" "${SNAPSHOT_VG[${i}]}" "" "${SNAPSHOT_MOUNT[${i}]}"
execute_mount
i=$(expr ${i} + 1)
done
# print tablespace name and stored directory
i=0
for tblspc in "${SNAPSHOT_TBLSPC[@]}"
do
local tblspc_mp="${SNAPSHOT_TBLSPC_DIR[${i}]}"
echo "${tblspc}=${tblspc_mp}"
i=$(expr ${i} + 1)
done
return
}
#
# implement of interface 'umount'.
# remove mount point of the snapshot volume from the file-system.
# don't remove this function even if there is no necessity.
#
function umount()
{
for ss_mp in "${SNAPSHOT_MOUNT[@]}"
do
set_args "" "" "" "" "${ss_mp}"
execute_umount
done
return
}
#
# create the snapshot volume.
#
function execute_split()
{
${CMD_LVCREATE} --snapshot --size=${ARGS_SS_SIZE} --name="${ARGS_SS_NAME}" "${ARGS_SS_LV}" > /dev/null
[ ${?} -ne 0 ] && \
print_log ${ERROR} "${CMD_LVCREATE} command failed: ${ARGS_SS_LV}"
}
#
# remove the snapshot volume.
#
function execute_resync()
{
${CMD_LVREMOVE} -f "${ARGS_SS_VG}/${ARGS_SS_NAME}" > /dev/null
[ ${?} -ne 0 ] && \
print_log ${ERROR} "${CMD_LVREMOVE} command failed: ${ARGS_SS_VG}/${ARGS_SS_NAME}"
}
#
# mount the snapshot volume to file-system.
#
function execute_mount()
{
${CMD_MOUNT} "${ARGS_SS_VG}/${ARGS_SS_NAME}" "${ARGS_SS_MOUNT}" > /dev/null
[ ${?} -ne 0 ] && \
print_log ${ERROR} "${CMD_MOUNT} command failed: ${ARGS_SS_MOUNT}"
}
#
# unmount the directory from file-system in snapshot volume.
#
function execute_umount()
{
${CMD_UMOUNT} "${ARGS_SS_MOUNT}" > /dev/null
[ ${?} -ne 0 ] && \
print_log ${ERROR} "${CMD_UMOUNT} command failed: ${ARGS_SS_MOUNT}"
}
#
# set argument of command to execute.
#
set_args()
{
ARGS_SS_NAME="${1}"
ARGS_SS_SIZE="${2}"
ARGS_SS_VG="${3}"
ARGS_SS_LV="${4}"
ARGS_SS_MOUNT="${5}"
}
#
# output the log message and abort the script when level <= ERROR
#
function print_log()
{
local level="${1}"
local message="${2}"
# if cleanup enable change ERROR to WARNING
[ -n "${cleanup}" -a ${level} -ge 0 ] && \
level=${WARNING}
case "${level}" in
${INFO} ) # INFO
echo "INFO: ${message}" 1>&2
;;
${WARNING} ) # WARNING
echo "WARNING: ${message}" 1>&2
;;
${ERROR} ) # ERROR
echo "ERROR: ${message}" 1>&2
;;
esac
[ ${level} -ge 0 ] && exit
}
#
# main
#
command="${1}"
cleanup="${2}"
case "${command}" in
"freeze" )
freeze
;;
"unfreeze" )
unfreeze
;;
"split" )
split
;;
"resync" )
resync
;;
"mount" )
mount
;;
"umount" )
umount
;;
* )
print_log ${ERROR} "specified invalid command: ${command} (internal error)"
;;
esac
echo "SUCCESS"
exit

512
show.c

@ -1,256 +1,256 @@
/*-------------------------------------------------------------------------
*
* show.c: show backup catalog.
*
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
static void show_backup_list(FILE *out, parray *backup_list, bool show_all);
static void show_timeline_backup_list(FILE *out, parray *backup_list, bool show_all);
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(pgBackupRange *range, bool show_timeline, bool show_all)
{
if (pgBackupRangeIsSingle(range))
{
pgBackup *backup;
backup = catalog_get_backup(range->begin);
if (backup == NULL)
{
char timestamp[100];
time2iso(timestamp, lengthof(timestamp), range->begin);
elog(INFO, _("backup taken at \"%s\" doesn not exist."),
timestamp);
/* This is not error case */
return 0;
}
show_backup_detail(stdout, backup);
/* cleanup */
pgBackupFree(backup);
}
else
{
parray *backup_list;
backup_list = catalog_get_backup_list(range);
if (backup_list == NULL){
elog(ERROR_SYSTEM, _("can't process any more."));
}
if (!show_timeline)
show_backup_list(stdout, backup_list, show_all);
else
show_timeline_backup_list(stdout, backup_list, show_all);
/* 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;
}
}
static TimeLineID
get_parent_tli(TimeLineID child_tli)
{
TimeLineID result = 0;
char path[MAXPGPATH];
char fline[MAXPGPATH];
FILE *fd;
/* search from timeline history dir */
snprintf(path, lengthof(path), "%s/%s/%08X.history", backup_path,
TIMELINE_HISTORY_DIR, child_tli);
fd = fopen(path, "rt");
if (fd == NULL)
{
if (errno != ENOENT)
elog(ERROR_SYSTEM, _("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_CORRUPTED,
_("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, bool show_all)
{
int i;
/* show header */
fputs("============================================================================\n", out);
fputs("Start Time Total Data WAL Log Backup Status \n", out);
fputs("============================================================================\n", out);
for (i = 0; i < parray_num(backup_list); i++)
{
pgBackup *backup;
char timestamp[20];
char duration[20] = "----";
char total_data_bytes_str[10] = "----";
char read_data_bytes_str[10] = "----";
char read_arclog_bytes_str[10] = "----";
char read_srvlog_bytes_str[10] = "----";
char write_bytes_str[10];
backup = parray_get(backup_list, i);
/* skip deleted backup */
if (backup->status == BACKUP_STATUS_DELETED && !show_all)
continue;
time2iso(timestamp, lengthof(timestamp), backup->start_time);
if (backup->end_time != (time_t) 0)
snprintf(duration, lengthof(duration), "%lum",
(backup->end_time - backup->start_time) / 60);
/* "Full" is only for full backup */
if (backup->backup_mode >= BACKUP_MODE_FULL)
pretty_size(backup->total_data_bytes, total_data_bytes_str,
lengthof(total_data_bytes_str));
else if (backup->backup_mode >= BACKUP_MODE_INCREMENTAL)
pretty_size(backup->read_data_bytes, read_data_bytes_str,
lengthof(read_data_bytes_str));
if (HAVE_ARCLOG(backup))
pretty_size(backup->read_arclog_bytes, read_arclog_bytes_str,
lengthof(read_arclog_bytes_str));
if (backup->with_serverlog)
pretty_size(backup->read_srvlog_bytes, read_srvlog_bytes_str,
lengthof(read_srvlog_bytes_str));
pretty_size(backup->write_bytes, write_bytes_str,
lengthof(write_bytes_str));
fprintf(out, "%-19s %5s %6s %6s %6s %6s %6s %s\n",
timestamp, duration,
total_data_bytes_str, read_data_bytes_str, read_arclog_bytes_str,
read_srvlog_bytes_str, write_bytes_str, status2str(backup->status));
}
}
static void
show_timeline_backup_list(FILE *out, parray *backup_list, bool show_all)
{
int i;
/* show header */
fputs("============================================================\n", out);
fputs("Start Mode Current TLI Parent TLI Status \n", out);
fputs("============================================================\n", out);
for (i = 0; i < parray_num(backup_list); i++)
{
static const char *modes[] = { "", "ARCH", "INCR", "FULL"};
pgBackup *backup;
char timestamp[20];
TimeLineID parent_tli;
backup = parray_get(backup_list, i);
/* skip deleted backup and serverlog backup */
if ((backup->status == BACKUP_STATUS_DELETED || !HAVE_ARCLOG(backup)) &&
!show_all)
continue;
time2iso(timestamp, lengthof(timestamp), backup->start_time);
parent_tli = get_parent_tli(backup->tli);
fprintf(out, "%-19s %-4s %10d %10d %s\n",
timestamp, modes[backup->backup_mode], backup->tli, parent_tli,
status2str(backup->status));
}
}
static void
show_backup_detail(FILE *out, pgBackup *backup)
{
pgBackupWriteConfigSection(out, backup);
pgBackupWriteResultSection(out, backup);
}
/*-------------------------------------------------------------------------
*
* show.c: show backup catalog.
*
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
static void show_backup_list(FILE *out, parray *backup_list, bool show_all);
static void show_timeline_backup_list(FILE *out, parray *backup_list, bool show_all);
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(pgBackupRange *range, bool show_timeline, bool show_all)
{
if (pgBackupRangeIsSingle(range))
{
pgBackup *backup;
backup = catalog_get_backup(range->begin);
if (backup == NULL)
{
char timestamp[100];
time2iso(timestamp, lengthof(timestamp), range->begin);
elog(INFO, _("backup taken at \"%s\" doesn not exist."),
timestamp);
/* This is not error case */
return 0;
}
show_backup_detail(stdout, backup);
/* cleanup */
pgBackupFree(backup);
}
else
{
parray *backup_list;
backup_list = catalog_get_backup_list(range);
if (backup_list == NULL){
elog(ERROR_SYSTEM, _("can't process any more."));
}
if (!show_timeline)
show_backup_list(stdout, backup_list, show_all);
else
show_timeline_backup_list(stdout, backup_list, show_all);
/* 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;
}
}
static TimeLineID
get_parent_tli(TimeLineID child_tli)
{
TimeLineID result = 0;
char path[MAXPGPATH];
char fline[MAXPGPATH];
FILE *fd;
/* search from timeline history dir */
snprintf(path, lengthof(path), "%s/%s/%08X.history", backup_path,
TIMELINE_HISTORY_DIR, child_tli);
fd = fopen(path, "rt");
if (fd == NULL)
{
if (errno != ENOENT)
elog(ERROR_SYSTEM, _("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_CORRUPTED,
_("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, bool show_all)
{
int i;
/* show header */
fputs("============================================================================\n", out);
fputs("Start Time Total Data WAL Log Backup Status \n", out);
fputs("============================================================================\n", out);
for (i = 0; i < parray_num(backup_list); i++)
{
pgBackup *backup;
char timestamp[20];
char duration[20] = "----";
char total_data_bytes_str[10] = "----";
char read_data_bytes_str[10] = "----";
char read_arclog_bytes_str[10] = "----";
char read_srvlog_bytes_str[10] = "----";
char write_bytes_str[10];
backup = parray_get(backup_list, i);
/* skip deleted backup */
if (backup->status == BACKUP_STATUS_DELETED && !show_all)
continue;
time2iso(timestamp, lengthof(timestamp), backup->start_time);
if (backup->end_time != (time_t) 0)
snprintf(duration, lengthof(duration), "%lum",
(backup->end_time - backup->start_time) / 60);
/* "Full" is only for full backup */
if (backup->backup_mode >= BACKUP_MODE_FULL)
pretty_size(backup->total_data_bytes, total_data_bytes_str,
lengthof(total_data_bytes_str));
else if (backup->backup_mode >= BACKUP_MODE_INCREMENTAL)
pretty_size(backup->read_data_bytes, read_data_bytes_str,
lengthof(read_data_bytes_str));
if (HAVE_ARCLOG(backup))
pretty_size(backup->read_arclog_bytes, read_arclog_bytes_str,
lengthof(read_arclog_bytes_str));
if (backup->with_serverlog)
pretty_size(backup->read_srvlog_bytes, read_srvlog_bytes_str,
lengthof(read_srvlog_bytes_str));
pretty_size(backup->write_bytes, write_bytes_str,
lengthof(write_bytes_str));
fprintf(out, "%-19s %5s %6s %6s %6s %6s %6s %s\n",
timestamp, duration,
total_data_bytes_str, read_data_bytes_str, read_arclog_bytes_str,
read_srvlog_bytes_str, write_bytes_str, status2str(backup->status));
}
}
static void
show_timeline_backup_list(FILE *out, parray *backup_list, bool show_all)
{
int i;
/* show header */
fputs("============================================================\n", out);
fputs("Start Mode Current TLI Parent TLI Status \n", out);
fputs("============================================================\n", out);
for (i = 0; i < parray_num(backup_list); i++)
{
static const char *modes[] = { "", "ARCH", "INCR", "FULL"};
pgBackup *backup;
char timestamp[20];
TimeLineID parent_tli;
backup = parray_get(backup_list, i);
/* skip deleted backup and serverlog backup */
if ((backup->status == BACKUP_STATUS_DELETED || !HAVE_ARCLOG(backup)) &&
!show_all)
continue;
time2iso(timestamp, lengthof(timestamp), backup->start_time);
parent_tli = get_parent_tli(backup->tli);
fprintf(out, "%-19s %-4s %10d %10d %s\n",
timestamp, modes[backup->backup_mode], backup->tli, parent_tli,
status2str(backup->status));
}
}
static void
show_backup_detail(FILE *out, pgBackup *backup)
{
pgBackupWriteConfigSection(out, backup);
pgBackupWriteResultSection(out, backup);
}

@ -1,281 +1,281 @@
#!/bin/sh
BASE_PATH=`pwd`
export PGDATA=$BASE_PATH/results/sample_database
export BACKUP_PATH=$BASE_PATH/results/sample_backup2
export ARCLOG_PATH=$BASE_PATH/results/arclog
export SRVLOG_PATH=$PGDATA/pg_log
export COMPRESS_DATA=YES
XLOG_PATH=$PGDATA/pg_xlog
TBLSPC_PATH=$BASE_PATH/results/tblspc
# Port used for test database cluster
TEST_PGPORT=54321
# configuration
SCALE=1
DURATION=10
ISOLATE_SRVLOG=0
ISOLATE_WAL=0
while [ $# -gt 0 ]; do
case $1 in
"-s")
ISOLATE_SRVLOG=1
shift
;;
"-w")
ISOLATE_WAL=1
shift
;;
"-d")
DURATION=`expr $2 + 0`
if [ $? -ne 0 ]; then
echo "invalid duration"
exit 1
fi
shift 2
;;
"-s")
SCALE=`expr $2 + 0`
if [ $? -ne 0 ]; then
echo "invalid scale"
exit 1
fi
shift 2
;;
*)
shift
;;
esac
done
# delete old database cluster
pg_ctl stop -m immediate > /dev/null 2>&1
rm -rf $PGDATA
rm -rf $BASE_PATH/results/pg_xlog
rm -rf $BASE_PATH/results/srvlog
rm -rf $ARCLOG_PATH
rm -rf $SRVLOG_PATH
rm -rf $TBLSPC_PATH
# create new backup catalog
rm -rf $BACKUP_PATH
pg_rman init -B $BACKUP_PATH --quiet
# create default configuration file
cat << EOF > $BACKUP_PATH/pg_rman.ini
# comment
BACKUP_MODE = F # comment
EOF
# create new database cluster
initdb --no-locale > $BASE_PATH/results/initdb.log 2>&1
cat << EOF >> $PGDATA/postgresql.conf
port = $TEST_PGPORT
logging_collector = on
wal_level = archive
archive_mode = on
archive_command = 'cp "%p" "$ARCLOG_PATH/%f"'
EOF
mkdir -p $ARCLOG_PATH
mkdir -p $TBLSPC_PATH
# determine serverlog directory
if [ "$ISOLATE_SRVLOG" -ne 0 ]; then
export SRVLOG_PATH=$BASE_PATH/results/srvlog
echo "log_directory = '$SRVLOG_PATH'" >> $PGDATA/postgresql.conf
mkdir -p $SRVLOG_PATH
else
export SRVLOG_PATH=$PGDATA/pg_log
echo "log_directory = 'pg_log'" >> $PGDATA/postgresql.conf
fi
# isolate online WAL
if [ "$ISOLATE_WAL" -ne 0 ]; then
XLOG_PATH=$BASE_PATH/results/pg_xlog
mv $PGDATA/pg_xlog $XLOG_PATH
ln -s $XLOG_PATH $PGDATA/pg_xlog
fi
# start PostgreSQL
pg_ctl start -w -t 3600 > /dev/null 2>&1
# create tablespace and database for pgbench
mkdir -p $TBLSPC_PATH/pgbench
psql -p $TEST_PGPORT postgres <<EOF
CREATE TABLESPACE pgbench LOCATION '$TBLSPC_PATH/pgbench';
CREATE DATABASE pgbench TABLESPACE = pgbench;
EOF
# data_delete
export KEEP_DATA_GENERATIONS=2
export KEEP_DATA_DAYS=0
for i in `seq 1 5`; do
# pg_rman -p $TEST_PGPORT backup --verbose -d postgres > $BASE_PATH/results/log_full_0_$i 2>&1
pg_rman -w -p $TEST_PGPORT backup --verbose -d postgres > $BASE_PATH/results/log_full_0_$i 2>&1
done
pg_rman -p $TEST_PGPORT show `date +%Y` -a --verbose -d postgres > $BASE_PATH/results/log_show_d_1 2>&1
echo "# of deleted backups"
grep -c DELETED $BASE_PATH/results/log_show_d_1
pgbench -p $TEST_PGPORT -i -s $SCALE pgbench > $BASE_PATH/results/pgbench.log 2>&1
echo "full database backup"
psql -p $TEST_PGPORT postgres -c "checkpoint"
#pg_rman -p $TEST_PGPORT backup --verbose -d postgres > $BASE_PATH/results/log_full_1 2>&1
pg_rman -w -p $TEST_PGPORT backup --verbose -d postgres > $BASE_PATH/results/log_full_1 2>&1
pgbench -p $TEST_PGPORT -T $DURATION -c 10 pgbench >> $BASE_PATH/results/pgbench.log 2>&1
echo "incremental database backup"
psql -p $TEST_PGPORT postgres -c "checkpoint"
#pg_rman -p $TEST_PGPORT backup -b i --verbose -d postgres > $BASE_PATH/results/log_incr1 2>&1
pg_rman -w -p $TEST_PGPORT backup -b i --verbose -d postgres > $BASE_PATH/results/log_incr1 2>&1
# validate all backup
pg_rman validate `date +%Y` --verbose > $BASE_PATH/results/log_validate1 2>&1
pg_rman -p $TEST_PGPORT show `date +%Y` -a --verbose -d postgres > $BASE_PATH/results/log_show0 2>&1
pg_dumpall > $BASE_PATH/results/dump_before_rtx.sql
target_xid=`psql -p $TEST_PGPORT pgbench -tAq -c "INSERT INTO pgbench_history VALUES (1) RETURNING(xmin);"`
psql -p $TEST_PGPORT postgres -c "checkpoint"
#pg_rman -p $TEST_PGPORT backup -b i --verbose -d postgres > $BASE_PATH/results/log_incr2 2>&1
pg_rman -w -p $TEST_PGPORT backup -b i --verbose -d postgres > $BASE_PATH/results/log_incr2 2>&1
pgbench -p $TEST_PGPORT -T $DURATION -c 10 pgbench >> $BASE_PATH/results/pgbench.log 2>&1
echo "archived WAL and serverlog backup"
#pg_rman -p $TEST_PGPORT backup -b a --verbose -d postgres > $BASE_PATH/results/log_arclog 2>&1
pg_rman -w -p $TEST_PGPORT backup -b a --verbose -d postgres > $BASE_PATH/results/log_arclog 2>&1
# stop PG during transaction and get commited info for verifing
echo "stop DB during running pgbench"
pgbench -p $TEST_PGPORT -T $DURATION -c 10 pgbench >> $BASE_PATH/results/pgbench.log 2>&1 &
sleep `expr $DURATION / 2`
pg_ctl stop -m immediate > /dev/null 2>&1
cp -rp $PGDATA $PGDATA.bak
pg_ctl start -w -t 3600 > /dev/null 2>&1
pg_dumpall > $BASE_PATH/results/dump_before.sql
# revert to crushed cluster
pg_ctl stop > /dev/null 2>&1
rm -rf $PGDATA
mv $PGDATA.bak $PGDATA
# validate all backup
pg_rman validate `date +%Y` --verbose > $BASE_PATH/results/log_validate2 2>&1
# restore check with pg_rman
pg_rman restore -! --verbose --check > $BASE_PATH/results/log_restore_check_1 2>&1
# restore with pg_rman
CUR_TLI=`pg_controldata | grep TimeLineID | awk '{print $4}'`
pg_rman restore -! --verbose > $BASE_PATH/results/log_restore1_1 2>&1
CUR_TLI_R=`grep "current timeline ID = " $BASE_PATH/results/log_restore1_1 | awk '{print $5}'`
TARGET_TLI=`grep "target timeline ID = " $BASE_PATH/results/log_restore1_1 | awk '{print $5}'`
if [ "$CUR_TLI" != "$CUR_TLI_R" -o "$CUR_TLI" != "$CUR_TLI_R" ]; then
echo "failed: bad timeline ID" CUR_TLI=$CUR_TLI CUR_TLI_R=$CUR_TLI_R
fi
# Backup of online-WAL and serverlog.
echo "diff files in BACKUP_PATH/backup/pg_xlog"
diff -r $PGDATA/pg_xlog $BACKUP_PATH/backup/pg_xlog
echo "# of files in BACKUP_PATH/backup/srvlog"
find $BACKUP_PATH/backup/srvlog -type f | wc -l
# recovery database
pg_ctl start -w -t 3600 > /dev/null 2>&1
# re-restore with pg_rman
pg_ctl stop -m immediate > /dev/null 2>&1
# restore check with pg_rman
pg_rman restore -! --verbose --check > $BASE_PATH/results/log_restore_check_2 2>&1
CUR_TLI=`pg_controldata | grep TimeLineID | awk '{print $4}'`
pg_rman restore -! --verbose > $BASE_PATH/results/log_restore1_2 2>&1
CUR_TLI_R=`grep "current timeline ID = " $BASE_PATH/results/log_restore1_2 | awk '{print $5}'`
TARGET_TLI=`grep "target timeline ID = " $BASE_PATH/results/log_restore1_2 | awk '{print $5}'`
if [ "$CUR_TLI" != "$CUR_TLI_R" -o "$CUR_TLI" != "$CUR_TLI_R" ]; then
echo "failed: bad timeline ID" CUR_TLI=$CUR_TLI CUR_TLI_R=$CUR_TLI_R
fi
# Backup of online-WAL and serverlog.
echo "diff files in BACKUP_PATH/backup/pg_xlog"
diff -r $PGDATA/pg_xlog $BACKUP_PATH/backup/pg_xlog
echo "# of files in BACKUP_PATH/backup/srvlog"
find $BACKUP_PATH/backup/srvlog -type f | wc -l
# re-recovery database
pg_ctl start -w -t 3600 > /dev/null 2>&1
# compare recovery results
pg_dumpall > $BASE_PATH/results/dump_after.sql
diff $BASE_PATH/results/dump_before.sql $BASE_PATH/results/dump_after.sql
# take a backup and delete backed up online files
# incrementa backup can't find last full backup because new timeline started.
echo "full database backup after recovery"
psql -p $TEST_PGPORT postgres -c "checkpoint"
#pg_rman -p $TEST_PGPORT backup -b f --verbose -d postgres > $BASE_PATH/results/log_full2 2>&1
pg_rman -w -p $TEST_PGPORT backup -b f --verbose -d postgres > $BASE_PATH/results/log_full2 2>&1
# Backup of online-WAL should been deleted, but serverlog remain.
echo "# of files in BACKUP_PATH/backup/pg_xlog"
find $BACKUP_PATH/backup/pg_xlog -type f | wc -l
echo "# of files in BACKUP_PATH/backup/srvlog"
find $BACKUP_PATH/backup/srvlog -type f | wc -l
# Symbolic links in $ARCLOG_PATH should be deleted.
echo "# of symbolic links in ARCLOG_PATH"
find $ARCLOG_PATH -type l | wc -l
# timeline history files are backed up.
echo "# of files in BACKUP_PATH/timeline_history"
find $BACKUP_PATH/timeline_history -type f | wc -l
# restore with pg_rman
pg_ctl stop -m immediate > /dev/null 2>&1
# restore check with pg_rman
pg_rman restore -! --verbose --check > $BASE_PATH/results/log_restore_check_3 2>&1
CUR_TLI=`pg_controldata | grep TimeLineID | awk '{print $4}'`
pg_rman restore -! --recovery-target-xid $target_xid --recovery-target-inclusive false --verbose > $BASE_PATH/results/log_restore2 2>&1
CUR_TLI_R=`grep "current timeline ID = " $BASE_PATH/results/log_restore2 | awk '{print $5}'`
TARGET_TLI=`grep "target timeline ID = " $BASE_PATH/results/log_restore2 | awk '{print $5}'`
if [ "$CUR_TLI" != "$CUR_TLI_R" -o "$CUR_TLI" != "$CUR_TLI_R" ]; then
echo "failed: bad timeline ID" CUR_TLI=$CUR_TLI CUR_TLI_R=$CUR_TLI_R
fi
echo "# of recovery target option in recovery.conf"
grep -c "recovery_target_" $PGDATA/recovery.conf
# recovery database
pg_ctl start -w -t 3600 > /dev/null 2>&1
pg_dumpall > $BASE_PATH/results/dump_after_rtx.sql
diff $BASE_PATH/results/dump_before_rtx.sql $BASE_PATH/results/dump_after_rtx.sql
# show timeline
pg_rman -p $TEST_PGPORT show timeline --verbose -a -d postgres > $BASE_PATH/results/log_show_timeline_1 2>&1
pg_rman -p $TEST_PGPORT show timeline `date +%Y` -a --verbose -d postgres > $BASE_PATH/results/log_show_timeline_2 2>&1
pg_rman -p $TEST_PGPORT show timeline `date +%Y` --verbose -d postgres > $BASE_PATH/results/log_show_timeline_3 2>&1
echo "# of deleted backups (show all)"
grep -c DELETED $BASE_PATH/results/log_show_timeline_2
echo "# of deleted backups"
grep -c DELETED $BASE_PATH/results/log_show_timeline_3
echo "delete backup"
pg_rman -p $TEST_PGPORT delete --debug -d postgres > $BASE_PATH/results/log_delete1 2>&1
pg_rman -p $TEST_PGPORT show `date +%Y` -a --verbose -d postgres > $BASE_PATH/results/log_show1 2>&1
echo "# of deleted backups"
grep -c DELETED $BASE_PATH/results/log_show1
pg_rman -p $TEST_PGPORT delete `date "+%Y-%m-%d %T"` --debug -d postgres > $BASE_PATH/results/log_delete2 2>&1
pg_rman -p $TEST_PGPORT show `date +%Y` -a --verbose -d postgres > $BASE_PATH/results/log_show2 2>&1
echo "# of deleted backups"
grep -c DELETED $BASE_PATH/results/log_show2
pg_rman -p $TEST_PGPORT show timeline `date +%Y` -a --verbose -d postgres > $BASE_PATH/results/log_show_timeline_4 2>&1
# cleanup
pg_ctl stop -m immediate > /dev/null 2>&1
#!/bin/sh
BASE_PATH=`pwd`
export PGDATA=$BASE_PATH/results/sample_database
export BACKUP_PATH=$BASE_PATH/results/sample_backup2
export ARCLOG_PATH=$BASE_PATH/results/arclog
export SRVLOG_PATH=$PGDATA/pg_log
export COMPRESS_DATA=YES
XLOG_PATH=$PGDATA/pg_xlog
TBLSPC_PATH=$BASE_PATH/results/tblspc
# Port used for test database cluster
TEST_PGPORT=54321
# configuration
SCALE=1
DURATION=10
ISOLATE_SRVLOG=0
ISOLATE_WAL=0
while [ $# -gt 0 ]; do
case $1 in
"-s")
ISOLATE_SRVLOG=1
shift
;;
"-w")
ISOLATE_WAL=1
shift
;;
"-d")
DURATION=`expr $2 + 0`
if [ $? -ne 0 ]; then
echo "invalid duration"
exit 1
fi
shift 2
;;
"-s")
SCALE=`expr $2 + 0`
if [ $? -ne 0 ]; then
echo "invalid scale"
exit 1
fi
shift 2
;;
*)
shift
;;
esac
done
# delete old database cluster
pg_ctl stop -m immediate > /dev/null 2>&1
rm -rf $PGDATA
rm -rf $BASE_PATH/results/pg_xlog
rm -rf $BASE_PATH/results/srvlog
rm -rf $ARCLOG_PATH
rm -rf $SRVLOG_PATH
rm -rf $TBLSPC_PATH
# create new backup catalog
rm -rf $BACKUP_PATH
pg_rman init -B $BACKUP_PATH --quiet
# create default configuration file
cat << EOF > $BACKUP_PATH/pg_rman.ini
# comment
BACKUP_MODE = F # comment
EOF
# create new database cluster
initdb --no-locale > $BASE_PATH/results/initdb.log 2>&1
cat << EOF >> $PGDATA/postgresql.conf
port = $TEST_PGPORT
logging_collector = on
wal_level = archive
archive_mode = on
archive_command = 'cp "%p" "$ARCLOG_PATH/%f"'
EOF
mkdir -p $ARCLOG_PATH
mkdir -p $TBLSPC_PATH
# determine serverlog directory
if [ "$ISOLATE_SRVLOG" -ne 0 ]; then
export SRVLOG_PATH=$BASE_PATH/results/srvlog
echo "log_directory = '$SRVLOG_PATH'" >> $PGDATA/postgresql.conf
mkdir -p $SRVLOG_PATH
else
export SRVLOG_PATH=$PGDATA/pg_log
echo "log_directory = 'pg_log'" >> $PGDATA/postgresql.conf
fi
# isolate online WAL
if [ "$ISOLATE_WAL" -ne 0 ]; then
XLOG_PATH=$BASE_PATH/results/pg_xlog
mv $PGDATA/pg_xlog $XLOG_PATH
ln -s $XLOG_PATH $PGDATA/pg_xlog
fi
# start PostgreSQL
pg_ctl start -w -t 3600 > /dev/null 2>&1
# create tablespace and database for pgbench
mkdir -p $TBLSPC_PATH/pgbench
psql -p $TEST_PGPORT postgres <<EOF
CREATE TABLESPACE pgbench LOCATION '$TBLSPC_PATH/pgbench';
CREATE DATABASE pgbench TABLESPACE = pgbench;
EOF
# data_delete
export KEEP_DATA_GENERATIONS=2
export KEEP_DATA_DAYS=0
for i in `seq 1 5`; do
# pg_rman -p $TEST_PGPORT backup --verbose -d postgres > $BASE_PATH/results/log_full_0_$i 2>&1
pg_rman -w -p $TEST_PGPORT backup --verbose -d postgres > $BASE_PATH/results/log_full_0_$i 2>&1
done
pg_rman -p $TEST_PGPORT show `date +%Y` -a --verbose -d postgres > $BASE_PATH/results/log_show_d_1 2>&1
echo "# of deleted backups"
grep -c DELETED $BASE_PATH/results/log_show_d_1
pgbench -p $TEST_PGPORT -i -s $SCALE pgbench > $BASE_PATH/results/pgbench.log 2>&1
echo "full database backup"
psql -p $TEST_PGPORT postgres -c "checkpoint"
#pg_rman -p $TEST_PGPORT backup --verbose -d postgres > $BASE_PATH/results/log_full_1 2>&1
pg_rman -w -p $TEST_PGPORT backup --verbose -d postgres > $BASE_PATH/results/log_full_1 2>&1
pgbench -p $TEST_PGPORT -T $DURATION -c 10 pgbench >> $BASE_PATH/results/pgbench.log 2>&1
echo "incremental database backup"
psql -p $TEST_PGPORT postgres -c "checkpoint"
#pg_rman -p $TEST_PGPORT backup -b i --verbose -d postgres > $BASE_PATH/results/log_incr1 2>&1
pg_rman -w -p $TEST_PGPORT backup -b i --verbose -d postgres > $BASE_PATH/results/log_incr1 2>&1
# validate all backup
pg_rman validate `date +%Y` --verbose > $BASE_PATH/results/log_validate1 2>&1
pg_rman -p $TEST_PGPORT show `date +%Y` -a --verbose -d postgres > $BASE_PATH/results/log_show0 2>&1
pg_dumpall > $BASE_PATH/results/dump_before_rtx.sql
target_xid=`psql -p $TEST_PGPORT pgbench -tAq -c "INSERT INTO pgbench_history VALUES (1) RETURNING(xmin);"`
psql -p $TEST_PGPORT postgres -c "checkpoint"
#pg_rman -p $TEST_PGPORT backup -b i --verbose -d postgres > $BASE_PATH/results/log_incr2 2>&1
pg_rman -w -p $TEST_PGPORT backup -b i --verbose -d postgres > $BASE_PATH/results/log_incr2 2>&1
pgbench -p $TEST_PGPORT -T $DURATION -c 10 pgbench >> $BASE_PATH/results/pgbench.log 2>&1
echo "archived WAL and serverlog backup"
#pg_rman -p $TEST_PGPORT backup -b a --verbose -d postgres > $BASE_PATH/results/log_arclog 2>&1
pg_rman -w -p $TEST_PGPORT backup -b a --verbose -d postgres > $BASE_PATH/results/log_arclog 2>&1
# stop PG during transaction and get commited info for verifing
echo "stop DB during running pgbench"
pgbench -p $TEST_PGPORT -T $DURATION -c 10 pgbench >> $BASE_PATH/results/pgbench.log 2>&1 &
sleep `expr $DURATION / 2`
pg_ctl stop -m immediate > /dev/null 2>&1
cp -rp $PGDATA $PGDATA.bak
pg_ctl start -w -t 3600 > /dev/null 2>&1
pg_dumpall > $BASE_PATH/results/dump_before.sql
# revert to crushed cluster
pg_ctl stop > /dev/null 2>&1
rm -rf $PGDATA
mv $PGDATA.bak $PGDATA
# validate all backup
pg_rman validate `date +%Y` --verbose > $BASE_PATH/results/log_validate2 2>&1
# restore check with pg_rman
pg_rman restore -! --verbose --check > $BASE_PATH/results/log_restore_check_1 2>&1
# restore with pg_rman
CUR_TLI=`pg_controldata | grep TimeLineID | awk '{print $4}'`
pg_rman restore -! --verbose > $BASE_PATH/results/log_restore1_1 2>&1
CUR_TLI_R=`grep "current timeline ID = " $BASE_PATH/results/log_restore1_1 | awk '{print $5}'`
TARGET_TLI=`grep "target timeline ID = " $BASE_PATH/results/log_restore1_1 | awk '{print $5}'`
if [ "$CUR_TLI" != "$CUR_TLI_R" -o "$CUR_TLI" != "$CUR_TLI_R" ]; then
echo "failed: bad timeline ID" CUR_TLI=$CUR_TLI CUR_TLI_R=$CUR_TLI_R
fi
# Backup of online-WAL and serverlog.
echo "diff files in BACKUP_PATH/backup/pg_xlog"
diff -r $PGDATA/pg_xlog $BACKUP_PATH/backup/pg_xlog
echo "# of files in BACKUP_PATH/backup/srvlog"
find $BACKUP_PATH/backup/srvlog -type f | wc -l
# recovery database
pg_ctl start -w -t 3600 > /dev/null 2>&1
# re-restore with pg_rman
pg_ctl stop -m immediate > /dev/null 2>&1
# restore check with pg_rman
pg_rman restore -! --verbose --check > $BASE_PATH/results/log_restore_check_2 2>&1
CUR_TLI=`pg_controldata | grep TimeLineID | awk '{print $4}'`
pg_rman restore -! --verbose > $BASE_PATH/results/log_restore1_2 2>&1
CUR_TLI_R=`grep "current timeline ID = " $BASE_PATH/results/log_restore1_2 | awk '{print $5}'`
TARGET_TLI=`grep "target timeline ID = " $BASE_PATH/results/log_restore1_2 | awk '{print $5}'`
if [ "$CUR_TLI" != "$CUR_TLI_R" -o "$CUR_TLI" != "$CUR_TLI_R" ]; then
echo "failed: bad timeline ID" CUR_TLI=$CUR_TLI CUR_TLI_R=$CUR_TLI_R
fi
# Backup of online-WAL and serverlog.
echo "diff files in BACKUP_PATH/backup/pg_xlog"
diff -r $PGDATA/pg_xlog $BACKUP_PATH/backup/pg_xlog
echo "# of files in BACKUP_PATH/backup/srvlog"
find $BACKUP_PATH/backup/srvlog -type f | wc -l
# re-recovery database
pg_ctl start -w -t 3600 > /dev/null 2>&1
# compare recovery results
pg_dumpall > $BASE_PATH/results/dump_after.sql
diff $BASE_PATH/results/dump_before.sql $BASE_PATH/results/dump_after.sql
# take a backup and delete backed up online files
# incrementa backup can't find last full backup because new timeline started.
echo "full database backup after recovery"
psql -p $TEST_PGPORT postgres -c "checkpoint"
#pg_rman -p $TEST_PGPORT backup -b f --verbose -d postgres > $BASE_PATH/results/log_full2 2>&1
pg_rman -w -p $TEST_PGPORT backup -b f --verbose -d postgres > $BASE_PATH/results/log_full2 2>&1
# Backup of online-WAL should been deleted, but serverlog remain.
echo "# of files in BACKUP_PATH/backup/pg_xlog"
find $BACKUP_PATH/backup/pg_xlog -type f | wc -l
echo "# of files in BACKUP_PATH/backup/srvlog"
find $BACKUP_PATH/backup/srvlog -type f | wc -l
# Symbolic links in $ARCLOG_PATH should be deleted.
echo "# of symbolic links in ARCLOG_PATH"
find $ARCLOG_PATH -type l | wc -l
# timeline history files are backed up.
echo "# of files in BACKUP_PATH/timeline_history"
find $BACKUP_PATH/timeline_history -type f | wc -l
# restore with pg_rman
pg_ctl stop -m immediate > /dev/null 2>&1
# restore check with pg_rman
pg_rman restore -! --verbose --check > $BASE_PATH/results/log_restore_check_3 2>&1
CUR_TLI=`pg_controldata | grep TimeLineID | awk '{print $4}'`
pg_rman restore -! --recovery-target-xid $target_xid --recovery-target-inclusive false --verbose > $BASE_PATH/results/log_restore2 2>&1
CUR_TLI_R=`grep "current timeline ID = " $BASE_PATH/results/log_restore2 | awk '{print $5}'`
TARGET_TLI=`grep "target timeline ID = " $BASE_PATH/results/log_restore2 | awk '{print $5}'`
if [ "$CUR_TLI" != "$CUR_TLI_R" -o "$CUR_TLI" != "$CUR_TLI_R" ]; then
echo "failed: bad timeline ID" CUR_TLI=$CUR_TLI CUR_TLI_R=$CUR_TLI_R
fi
echo "# of recovery target option in recovery.conf"
grep -c "recovery_target_" $PGDATA/recovery.conf
# recovery database
pg_ctl start -w -t 3600 > /dev/null 2>&1
pg_dumpall > $BASE_PATH/results/dump_after_rtx.sql
diff $BASE_PATH/results/dump_before_rtx.sql $BASE_PATH/results/dump_after_rtx.sql
# show timeline
pg_rman -p $TEST_PGPORT show timeline --verbose -a -d postgres > $BASE_PATH/results/log_show_timeline_1 2>&1
pg_rman -p $TEST_PGPORT show timeline `date +%Y` -a --verbose -d postgres > $BASE_PATH/results/log_show_timeline_2 2>&1
pg_rman -p $TEST_PGPORT show timeline `date +%Y` --verbose -d postgres > $BASE_PATH/results/log_show_timeline_3 2>&1
echo "# of deleted backups (show all)"
grep -c DELETED $BASE_PATH/results/log_show_timeline_2
echo "# of deleted backups"
grep -c DELETED $BASE_PATH/results/log_show_timeline_3
echo "delete backup"
pg_rman -p $TEST_PGPORT delete --debug -d postgres > $BASE_PATH/results/log_delete1 2>&1
pg_rman -p $TEST_PGPORT show `date +%Y` -a --verbose -d postgres > $BASE_PATH/results/log_show1 2>&1
echo "# of deleted backups"
grep -c DELETED $BASE_PATH/results/log_show1
pg_rman -p $TEST_PGPORT delete `date "+%Y-%m-%d %T"` --debug -d postgres > $BASE_PATH/results/log_delete2 2>&1
pg_rman -p $TEST_PGPORT show `date +%Y` -a --verbose -d postgres > $BASE_PATH/results/log_show2 2>&1
echo "# of deleted backups"
grep -c DELETED $BASE_PATH/results/log_show2
pg_rman -p $TEST_PGPORT show timeline `date +%Y` -a --verbose -d postgres > $BASE_PATH/results/log_show_timeline_4 2>&1
# cleanup
pg_ctl stop -m immediate > /dev/null 2>&1

@ -1,80 +1,80 @@
#!/bin/sh
#============================================================================
# This is a test script for option test of pg_rman.
#============================================================================
BASE_PATH=`pwd`
# Clear environment variables used by pg_rman except $PGDATA.
# List of environment variables is defined in catalog.c.
unset BACKUP_PATH
unset ARCLOG_PATH
unset SRVLOG_PATH
unset BACKUP_MODE
unset COMPRESS_DATA
unset KEEP_ARCLOG_DAYS
unset KEEP_DATA_GENERATIONS
unset KEEP_DATA_DAYS
unset KEEP_SRVLOG_FILES
unset KEEP_SRVLOG_DAYS
export PGDATA=$BASE_PATH/results/sample_database
# Note: not exported
BACKUP_PATH=$BASE_PATH/results/sample_backup2
# Setup backup catalog for backup test.
rm -rf $BACKUP_PATH
cp -rp data/sample_backup $BACKUP_PATH
# general option
pg_rman --help
pg_rman --version
# backup option
# required arguments check
pg_rman backup --verbose
pg_rman backup --verbose -B $BACKUP_PATH
pg_rman backup --verbose -B $BACKUP_PATH -b f
pg_rman backup --verbose -B $BACKUP_PATH -b i
pg_rman backup --verbose -B $BACKUP_PATH -b a
# bad arguments check
pg_rman backup --verbose -B $BACKUP_PATH -b bad
# delete or validate requires DATE
pg_rman delete -B $BACKUP_PATH
pg_rman validate -B $BACKUP_PATH
# invalid configuration file check
echo " = INFINITE" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "BACKUP_MODE= " > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "BACKUP_MODE = F#S" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "BACKUP_MODE = F #comment A" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "BACKUP_MODE=B" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "COMPRESS_DATA=FOO" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "KEEP_ARCLOG_FILES=YES" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "TIMELINEID=-1" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "BACKUP_TARGETS=F" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "BACKUP_MODE='F''\'\\\F'" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
# configuration priorityfile check
echo "BACKUP_MODE=ENV_PATH" > $BACKUP_PATH/pg_rman.ini
mkdir $BACKUP_PATH/conf_path
echo "BACKUP_PATH=$BACKUP_PATH/conf_path" > $BACKUP_PATH/pg_rman.conf
echo "BACKUP_MODE=CONF_PATH" > $BACKUP_PATH/conf_path/pg_rman.ini
mkdir $BACKUP_PATH/comm_path
echo "BACKUP_MODE=COMM_PATH" > $BACKUP_PATH/comm_path/pg_rman.ini
export BACKUP_PATH=$BACKUP_PATH
pg_rman backup --verbose
#!/bin/sh
#============================================================================
# This is a test script for option test of pg_rman.
#============================================================================
BASE_PATH=`pwd`
# Clear environment variables used by pg_rman except $PGDATA.
# List of environment variables is defined in catalog.c.
unset BACKUP_PATH
unset ARCLOG_PATH
unset SRVLOG_PATH
unset BACKUP_MODE
unset COMPRESS_DATA
unset KEEP_ARCLOG_DAYS
unset KEEP_DATA_GENERATIONS
unset KEEP_DATA_DAYS
unset KEEP_SRVLOG_FILES
unset KEEP_SRVLOG_DAYS
export PGDATA=$BASE_PATH/results/sample_database
# Note: not exported
BACKUP_PATH=$BASE_PATH/results/sample_backup2
# Setup backup catalog for backup test.
rm -rf $BACKUP_PATH
cp -rp data/sample_backup $BACKUP_PATH
# general option
pg_rman --help
pg_rman --version
# backup option
# required arguments check
pg_rman backup --verbose
pg_rman backup --verbose -B $BACKUP_PATH
pg_rman backup --verbose -B $BACKUP_PATH -b f
pg_rman backup --verbose -B $BACKUP_PATH -b i
pg_rman backup --verbose -B $BACKUP_PATH -b a
# bad arguments check
pg_rman backup --verbose -B $BACKUP_PATH -b bad
# delete or validate requires DATE
pg_rman delete -B $BACKUP_PATH
pg_rman validate -B $BACKUP_PATH
# invalid configuration file check
echo " = INFINITE" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "BACKUP_MODE= " > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "BACKUP_MODE = F#S" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "BACKUP_MODE = F #comment A" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "BACKUP_MODE=B" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "COMPRESS_DATA=FOO" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "KEEP_ARCLOG_FILES=YES" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "TIMELINEID=-1" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "BACKUP_TARGETS=F" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "BACKUP_MODE='F''\'\\\F'" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
# configuration priorityfile check
echo "BACKUP_MODE=ENV_PATH" > $BACKUP_PATH/pg_rman.ini
mkdir $BACKUP_PATH/conf_path
echo "BACKUP_PATH=$BACKUP_PATH/conf_path" > $BACKUP_PATH/pg_rman.conf
echo "BACKUP_MODE=CONF_PATH" > $BACKUP_PATH/conf_path/pg_rman.ini
mkdir $BACKUP_PATH/comm_path
echo "BACKUP_MODE=COMM_PATH" > $BACKUP_PATH/comm_path/pg_rman.ini
export BACKUP_PATH=$BACKUP_PATH
pg_rman backup --verbose

164
util.c

@ -1,82 +1,82 @@
/*-------------------------------------------------------------------------
*
* util.c: log messages to log file or stderr, and misc code.
*
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
#include <time.h>
/*
* Convert time_t value to ISO-8601 format string
*/
void
time2iso(char *buf, size_t len, time_t time)
{
struct tm *tm = localtime(&time);
strftime(buf, len, "%Y-%m-%d %H:%M:%S", tm);
}
const char *
status2str(BackupStatus status)
{
static const char *statusName[] =
{
"UNKNOWN",
"OK",
"RUNNING",
"ERROR",
"DELETING",
"DELETED",
"DONE",
"CORRUPT"
};
if (status < BACKUP_STATUS_INVALID || BACKUP_STATUS_CORRUPT < status)
return "UNKNOWN";
return statusName[status];
}
void
remove_trailing_space(char *buf, int comment_mark)
{
int i;
char *last_char = NULL;
for (i = 0; buf[i]; i++)
{
if (buf[i] == comment_mark || buf[i] == '\n' || buf[i] == '\r')
{
buf[i] = '\0';
break;
}
}
for (i = 0; buf[i]; i++)
{
if (!isspace(buf[i]))
last_char = buf + i;
}
if (last_char != NULL)
*(last_char + 1) = '\0';
}
void
remove_not_digit(char *buf, size_t len, const char *str)
{
int i, j;
for (i = 0, j = 0; str[i] && j < len; i++)
{
if (!isdigit(str[i]))
continue;
buf[j++] = str[i];
}
buf[j] = '\0';
}
/*-------------------------------------------------------------------------
*
* util.c: log messages to log file or stderr, and misc code.
*
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
#include <time.h>
/*
* Convert time_t value to ISO-8601 format string
*/
void
time2iso(char *buf, size_t len, time_t time)
{
struct tm *tm = localtime(&time);
strftime(buf, len, "%Y-%m-%d %H:%M:%S", tm);
}
const char *
status2str(BackupStatus status)
{
static const char *statusName[] =
{
"UNKNOWN",
"OK",
"RUNNING",
"ERROR",
"DELETING",
"DELETED",
"DONE",
"CORRUPT"
};
if (status < BACKUP_STATUS_INVALID || BACKUP_STATUS_CORRUPT < status)
return "UNKNOWN";
return statusName[status];
}
void
remove_trailing_space(char *buf, int comment_mark)
{
int i;
char *last_char = NULL;
for (i = 0; buf[i]; i++)
{
if (buf[i] == comment_mark || buf[i] == '\n' || buf[i] == '\r')
{
buf[i] = '\0';
break;
}
}
for (i = 0; buf[i]; i++)
{
if (!isspace(buf[i]))
last_char = buf + i;
}
if (last_char != NULL)
*(last_char + 1) = '\0';
}
void
remove_not_digit(char *buf, size_t len, const char *str)
{
int i, j;
for (i = 0, j = 0; str[i] && j < len; i++)
{
if (!isdigit(str[i]))
continue;
buf[j++] = str[i];
}
buf[j] = '\0';
}

972
utils.c

@ -1,486 +1,486 @@
/*-------------------------------------------------------------------------
*
* utils.c:
*
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
#include <dirent.h>
#include <time.h>
#include <limits.h>
#ifdef WIN32
#include <winioctl.h>
#endif
/*
* Convert time_t value to ISO-8601 format string.
* The size of buffer must be larger than DATESTRLEN.
*/
char *
date2str(char *buf, time_t date)
{
struct tm *tm = localtime(&date);
strftime(buf, DATESTRLEN, "%Y-%m-%d %H:%M:%S", tm);
return buf;
}
/*
* The size of buffer must be larger than TIMESTRLEN.
*/
char *
time2str(char *buf, time_t time)
{
/* set empty if nagative duration */
if (time < 0)
buf[0] = '\0';
else if (time >= 100 * 24 * 60 * 60)
snprintf(buf, TIMESTRLEN, "%.1fd", time / 86400.0);
else if (time >= 60 * 60)
snprintf(buf, TIMESTRLEN, "%.1fh", time / 3600.0);
else if (time >= 60)
snprintf(buf, TIMESTRLEN, "%.1fm", time / 60.0);
else
snprintf(buf, TIMESTRLEN, "%lds", (long) time);
return buf;
}
/*
* The size of buffer must be larger than SIZESTRLEN.
*/
char *
size2str(char *buf, int64 size)
{
int exp;
int64 base;
double n;
static const char *units[] = { "B ", "KB", "MB", "GB", "TB", "PB" };
/* set empty if nagative size */
if (size < 0)
{
buf[0] = '\0';
return buf;
}
/* determine the unit */
for (exp = 0, base = 1;
exp < lengthof(units) && base * 1024 < size;
++exp, base *= 1024)
;
n = size / (double) base;
if (n >= 100.0)
snprintf(buf, SIZESTRLEN, "%4.0f%s", n, units[exp]);
else if (n >= 10.0)
snprintf(buf, SIZESTRLEN, "%3.1f%s", n, units[exp]);
else
snprintf(buf, SIZESTRLEN, "%3.2f%s", n, units[exp]);
return buf;
}
/*
* Parse for backup mode. empty input is treated as full.
*/
BackupMode
parse_backup_mode(const char *value)
{
const char *v = value;
size_t len;
if (v == NULL)
return MODE_FULL; /* null input is full. */
while (IsSpace(*v)) { v++; }
if ((len = strlen(v)) == 0)
return MODE_FULL; /* empty input is full. */
/* Do a prefix match. For example, "incr" means incremental. */
if (pg_strncasecmp("full", v, len) == 0)
return MODE_FULL;
else if (pg_strncasecmp("incremental", v, len) == 0)
return MODE_INCREMENTAL;
else if (pg_strncasecmp("archive", v, len) == 0)
return MODE_ARCHIVE;
ereport(ERROR,
(errcode(EINVAL),
errmsg("invalid backup mode: '%s'", value)));
return (BackupMode) -1;
}
XLogName
parse_xlogname(const char *value)
{
XLogName xlog;
char junk[2];
if (sscanf(value, "%08X%08X%08X%1s",
&xlog.tli, &xlog.log, &xlog.seg, junk) != 3)
ereport(ERROR,
(errcode(EINVAL),
errmsg("invalid xlog name: '%s'", value)));
return xlog;
}
/* return max value of time_t */
time_t
time_max(void)
{
static time_t value = 0;
if (value == 0)
{
if (sizeof(time_t) > sizeof(int32))
{
struct tm tm = { 0 };
/* '9999-12-31 23:59:59' for 64bit time_t */
tm.tm_year = 9999 - 1900;
tm.tm_mon = 12 - 1;
tm.tm_mday = 31;
tm.tm_hour = 23;
tm.tm_min = 59;
tm.tm_sec = 59;
value = mktime(&tm);
}
else
{
/* '2038-01-19 03:14:07' for 32bit time_t */
value = INT_MAX;
}
}
return value;
}
/*
* Create range object from one or two arguments.
* All not-digit characters in the argument(s) are igonred.
*/
pgRange
make_range(int argc, char * const *argv)
{
pgRange range;
const char *arg1;
const char *arg2;
size_t len;
char *tmp;
int i;
struct tm tm;
char junk[2];
/* takes 0, 1, or 2 arguments */
if (argc > 2)
ereport(ERROR,
(errcode(EINVAL),
errmsg("too many arguments")));
/* no input means unlimited range */
if (argc < 1)
{
range.begin = 0;
range.end = time_max();
return range;
}
arg1 = argv[0];
arg2 = (argc > 1 ? argv[1] : "");
/* tmp = replace( concat(arg1, arg2), !isalnum, ' ' ) */
tmp = pgut_malloc(strlen(arg1) + strlen(arg2) + 1);
len = 0;
for (i = 0; arg1[i]; i++)
tmp[len++] = (IsAlnum(arg1[i]) ? arg1[i] : ' ');
for (i = 0; arg2[i]; i++)
tmp[len++] = (IsAlnum(arg2[i]) ? arg2[i] : ' ');
tmp[len] = '\0';
/* parse for "YYYY-MM-DD HH:MI:SS" */
tm.tm_year = 0; /* tm_year is year - 1900 */
tm.tm_mon = 0; /* tm_mon is 0 - 11 */
tm.tm_mday = 1; /* tm_mday is 1 - 31 */
tm.tm_hour = 0;
tm.tm_min = 0;
tm.tm_sec = 0;
i = sscanf(tmp, "%04d %02d %02d %02d %02d %02d%1s",
&tm.tm_year, &tm.tm_mon, &tm.tm_mday,
&tm.tm_hour, &tm.tm_min, &tm.tm_sec, junk);
if (i < 1 || 6 < i)
ereport(ERROR,
(errcode(EINVAL),
errmsg("invalid range syntax: '%s'", tmp)));
free(tmp);
/* adjust year */
if (tm.tm_year < 100)
tm.tm_year += 2000 - 1900;
else if (tm.tm_year >= 1900)
tm.tm_year -= 1900;
/* adjust month */
if (i > 1)
tm.tm_mon -= 1;
range.begin = mktime(&tm);
switch (i)
{
case 1:
tm.tm_year++;
break;
case 2:
tm.tm_mon++;
break;
case 3:
tm.tm_mday++;
break;
case 4:
tm.tm_hour++;
break;
case 5:
tm.tm_min++;
break;
case 6:
tm.tm_sec++;
break;
}
range.end = mktime(&tm);
return range;
}
/*
* check path is a directory and returns errno of opendir.
* ENOENT is treated as succeeded if missing_ok.
*/
int
check_dir(const char *path, bool missing_ok)
{
DIR *dir;
if ((dir = opendir(path)) == NULL)
{
if (missing_ok && errno == ENOENT)
return 0;
else
return errno;
}
closedir(dir);
return 0;
}
/*
* make sure the directory either doesn't exist or is empty
*
* Returns 0 if nonexistent, 1 if exists and empty, 2 if not empty,
* or -1 if trouble accessing directory
*/
void
make_empty_dir(const char *path)
{
DIR *dir;
errno = 0;
if ((dir = opendir(path)) == NULL)
{
/* Directory does not exist. */
if (errno != ENOENT)
ereport(ERROR,
(errcode_errno(),
errmsg("could not access directory \"%s\": ", path)));
pgut_mkdir(path);
}
else
{
/* Directory exists. */
struct dirent *file;
while ((file = readdir(dir)) != NULL)
{
if (strcmp(".", file->d_name) == 0 ||
strcmp("..", file->d_name) == 0)
{
/* skip this and parent directory */
continue;
}
else
{
/* Present and not empty */
closedir(dir);
ereport(ERROR,
(errcode(EEXIST),
errmsg("directory \"%s\" exists but is not empty", path)));
}
}
#ifdef WIN32
/*
* This fix is in mingw cvs (runtime/mingwex/dirent.c rev 1.4),
* but not in released version
*/
if (GetLastError() == ERROR_NO_MORE_FILES)
errno = 0;
#endif
closedir(dir);
}
}
/*
* Remove files recursively, but follow symbolic link to directories.
* We remove the symbolic link files, but delete the linked directories.
*/
void
remove_file(const char *path)
{
remove_children(path);
if (remove(path) != 0 && errno != ENOENT)
elog(ERROR, "could not remove file \"%s\": %s", path, strerror(errno));
}
void
remove_children(const char *path)
{
DIR *dir;
/* try to open as directory and remove children. */
if ((dir = opendir(path)) != NULL)
{
struct dirent *dent;
while ((dent = readdir(dir)) != NULL)
{
char child[MAXPGPATH];
/* skip entries point current dir or parent dir */
if (strcmp(dent->d_name, ".") == 0 ||
strcmp(dent->d_name, "..") == 0)
continue;
join_path_components(child, path, dent->d_name);
remove_file(child);
}
closedir(dir);
}
}
#ifdef WIN32
#define REPARSE_DATA_SIZE 1024
/* same layout as REPARSE_DATA_BUFFER, which is defined only in old winnt.h */
typedef struct REPARSE_DATA
{
ULONG ReparseTag;
WORD ReparseDataLength;
WORD Reserved;
union
{
struct
{
WORD SubstituteNameOffset;
WORD SubstituteNameLength;
WORD PrintNameOffset;
WORD PrintNameLength;
ULONG Flags;
WCHAR PathBuffer[1];
} Symlink;
struct
{
WORD SubstituteNameOffset;
WORD SubstituteNameLength;
WORD PrintNameOffset;
WORD PrintNameLength;
WCHAR PathBuffer[1];
} Mount;
struct
{
BYTE DataBuffer[REPARSE_DATA_SIZE];
} Generic;
};
} REPARSE_DATA;
ssize_t
readlink(const char *path, char *target, size_t size)
{
HANDLE handle;
DWORD attr;
REPARSE_DATA data;
DWORD datasize;
PCWSTR wpath;
int wlen;
int r;
attr = GetFileAttributes(path);
if (attr == INVALID_FILE_ATTRIBUTES)
{
_dosmaperr(GetLastError());
return -1;
}
if ((attr & FILE_ATTRIBUTE_REPARSE_POINT) == 0)
{
errno = EINVAL; /* not a symlink */
return -1;
}
handle = CreateFileA(path, 0,
FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
if (handle == INVALID_HANDLE_VALUE)
{
_dosmaperr(GetLastError());
return -1;
}
wpath = NULL;
if (DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, NULL, 0,
&data, sizeof(data), &datasize, NULL))
{
switch (data.ReparseTag)
{
case IO_REPARSE_TAG_MOUNT_POINT:
{
wpath = data.Mount.PathBuffer + data.Mount.SubstituteNameOffset;
wlen = data.Mount.SubstituteNameLength;
break;
}
case IO_REPARSE_TAG_SYMLINK:
{
wpath = data.Symlink.PathBuffer + data.Symlink.SubstituteNameOffset;
wlen = data.Symlink.SubstituteNameLength;
break;
}
}
}
if (wpath == NULL)
r = -1;
else
{
if (wcsncmp(wpath, L"\\??\\", 4) == 0 ||
wcsncmp(wpath, L"\\\\?\\", 4) == 0)
{
wpath += 4;
wlen -= 4;
}
r = WideCharToMultiByte(CP_ACP, 0, wpath, wlen, target, size, NULL, NULL);
}
CloseHandle(handle);
return r;
}
#endif
/*-------------------------------------------------------------------------
*
* utils.c:
*
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
#include <dirent.h>
#include <time.h>
#include <limits.h>
#ifdef WIN32
#include <winioctl.h>
#endif
/*
* Convert time_t value to ISO-8601 format string.
* The size of buffer must be larger than DATESTRLEN.
*/
char *
date2str(char *buf, time_t date)
{
struct tm *tm = localtime(&date);
strftime(buf, DATESTRLEN, "%Y-%m-%d %H:%M:%S", tm);
return buf;
}
/*
* The size of buffer must be larger than TIMESTRLEN.
*/
char *
time2str(char *buf, time_t time)
{
/* set empty if nagative duration */
if (time < 0)
buf[0] = '\0';
else if (time >= 100 * 24 * 60 * 60)
snprintf(buf, TIMESTRLEN, "%.1fd", time / 86400.0);
else if (time >= 60 * 60)
snprintf(buf, TIMESTRLEN, "%.1fh", time / 3600.0);
else if (time >= 60)
snprintf(buf, TIMESTRLEN, "%.1fm", time / 60.0);
else
snprintf(buf, TIMESTRLEN, "%lds", (long) time);
return buf;
}
/*
* The size of buffer must be larger than SIZESTRLEN.
*/
char *
size2str(char *buf, int64 size)
{
int exp;
int64 base;
double n;
static const char *units[] = { "B ", "KB", "MB", "GB", "TB", "PB" };
/* set empty if nagative size */
if (size < 0)
{
buf[0] = '\0';
return buf;
}
/* determine the unit */
for (exp = 0, base = 1;
exp < lengthof(units) && base * 1024 < size;
++exp, base *= 1024)
;
n = size / (double) base;
if (n >= 100.0)
snprintf(buf, SIZESTRLEN, "%4.0f%s", n, units[exp]);
else if (n >= 10.0)
snprintf(buf, SIZESTRLEN, "%3.1f%s", n, units[exp]);
else
snprintf(buf, SIZESTRLEN, "%3.2f%s", n, units[exp]);
return buf;
}
/*
* Parse for backup mode. empty input is treated as full.
*/
BackupMode
parse_backup_mode(const char *value)
{
const char *v = value;
size_t len;
if (v == NULL)
return MODE_FULL; /* null input is full. */
while (IsSpace(*v)) { v++; }
if ((len = strlen(v)) == 0)
return MODE_FULL; /* empty input is full. */
/* Do a prefix match. For example, "incr" means incremental. */
if (pg_strncasecmp("full", v, len) == 0)
return MODE_FULL;
else if (pg_strncasecmp("incremental", v, len) == 0)
return MODE_INCREMENTAL;
else if (pg_strncasecmp("archive", v, len) == 0)
return MODE_ARCHIVE;
ereport(ERROR,
(errcode(EINVAL),
errmsg("invalid backup mode: '%s'", value)));
return (BackupMode) -1;
}
XLogName
parse_xlogname(const char *value)
{
XLogName xlog;
char junk[2];
if (sscanf(value, "%08X%08X%08X%1s",
&xlog.tli, &xlog.log, &xlog.seg, junk) != 3)
ereport(ERROR,
(errcode(EINVAL),
errmsg("invalid xlog name: '%s'", value)));
return xlog;
}
/* return max value of time_t */
time_t
time_max(void)
{
static time_t value = 0;
if (value == 0)
{
if (sizeof(time_t) > sizeof(int32))
{
struct tm tm = { 0 };
/* '9999-12-31 23:59:59' for 64bit time_t */
tm.tm_year = 9999 - 1900;
tm.tm_mon = 12 - 1;
tm.tm_mday = 31;
tm.tm_hour = 23;
tm.tm_min = 59;
tm.tm_sec = 59;
value = mktime(&tm);
}
else
{
/* '2038-01-19 03:14:07' for 32bit time_t */
value = INT_MAX;
}
}
return value;
}
/*
* Create range object from one or two arguments.
* All not-digit characters in the argument(s) are igonred.
*/
pgRange
make_range(int argc, char * const *argv)
{
pgRange range;
const char *arg1;
const char *arg2;
size_t len;
char *tmp;
int i;
struct tm tm;
char junk[2];
/* takes 0, 1, or 2 arguments */
if (argc > 2)
ereport(ERROR,
(errcode(EINVAL),
errmsg("too many arguments")));
/* no input means unlimited range */
if (argc < 1)
{
range.begin = 0;
range.end = time_max();
return range;
}
arg1 = argv[0];
arg2 = (argc > 1 ? argv[1] : "");
/* tmp = replace( concat(arg1, arg2), !isalnum, ' ' ) */
tmp = pgut_malloc(strlen(arg1) + strlen(arg2) + 1);
len = 0;
for (i = 0; arg1[i]; i++)
tmp[len++] = (IsAlnum(arg1[i]) ? arg1[i] : ' ');
for (i = 0; arg2[i]; i++)
tmp[len++] = (IsAlnum(arg2[i]) ? arg2[i] : ' ');
tmp[len] = '\0';
/* parse for "YYYY-MM-DD HH:MI:SS" */
tm.tm_year = 0; /* tm_year is year - 1900 */
tm.tm_mon = 0; /* tm_mon is 0 - 11 */
tm.tm_mday = 1; /* tm_mday is 1 - 31 */
tm.tm_hour = 0;
tm.tm_min = 0;
tm.tm_sec = 0;
i = sscanf(tmp, "%04d %02d %02d %02d %02d %02d%1s",
&tm.tm_year, &tm.tm_mon, &tm.tm_mday,
&tm.tm_hour, &tm.tm_min, &tm.tm_sec, junk);
if (i < 1 || 6 < i)
ereport(ERROR,
(errcode(EINVAL),
errmsg("invalid range syntax: '%s'", tmp)));
free(tmp);
/* adjust year */
if (tm.tm_year < 100)
tm.tm_year += 2000 - 1900;
else if (tm.tm_year >= 1900)
tm.tm_year -= 1900;
/* adjust month */
if (i > 1)
tm.tm_mon -= 1;
range.begin = mktime(&tm);
switch (i)
{
case 1:
tm.tm_year++;
break;
case 2:
tm.tm_mon++;
break;
case 3:
tm.tm_mday++;
break;
case 4:
tm.tm_hour++;
break;
case 5:
tm.tm_min++;
break;
case 6:
tm.tm_sec++;
break;
}
range.end = mktime(&tm);
return range;
}
/*
* check path is a directory and returns errno of opendir.
* ENOENT is treated as succeeded if missing_ok.
*/
int
check_dir(const char *path, bool missing_ok)
{
DIR *dir;
if ((dir = opendir(path)) == NULL)
{
if (missing_ok && errno == ENOENT)
return 0;
else
return errno;
}
closedir(dir);
return 0;
}
/*
* make sure the directory either doesn't exist or is empty
*
* Returns 0 if nonexistent, 1 if exists and empty, 2 if not empty,
* or -1 if trouble accessing directory
*/
void
make_empty_dir(const char *path)
{
DIR *dir;
errno = 0;
if ((dir = opendir(path)) == NULL)
{
/* Directory does not exist. */
if (errno != ENOENT)
ereport(ERROR,
(errcode_errno(),
errmsg("could not access directory \"%s\": ", path)));
pgut_mkdir(path);
}
else
{
/* Directory exists. */
struct dirent *file;
while ((file = readdir(dir)) != NULL)
{
if (strcmp(".", file->d_name) == 0 ||
strcmp("..", file->d_name) == 0)
{
/* skip this and parent directory */
continue;
}
else
{
/* Present and not empty */
closedir(dir);
ereport(ERROR,
(errcode(EEXIST),
errmsg("directory \"%s\" exists but is not empty", path)));
}
}
#ifdef WIN32
/*
* This fix is in mingw cvs (runtime/mingwex/dirent.c rev 1.4),
* but not in released version
*/
if (GetLastError() == ERROR_NO_MORE_FILES)
errno = 0;
#endif
closedir(dir);
}
}
/*
* Remove files recursively, but follow symbolic link to directories.
* We remove the symbolic link files, but delete the linked directories.
*/
void
remove_file(const char *path)
{
remove_children(path);
if (remove(path) != 0 && errno != ENOENT)
elog(ERROR, "could not remove file \"%s\": %s", path, strerror(errno));
}
void
remove_children(const char *path)
{
DIR *dir;
/* try to open as directory and remove children. */
if ((dir = opendir(path)) != NULL)
{
struct dirent *dent;
while ((dent = readdir(dir)) != NULL)
{
char child[MAXPGPATH];
/* skip entries point current dir or parent dir */
if (strcmp(dent->d_name, ".") == 0 ||
strcmp(dent->d_name, "..") == 0)
continue;
join_path_components(child, path, dent->d_name);
remove_file(child);
}
closedir(dir);
}
}
#ifdef WIN32
#define REPARSE_DATA_SIZE 1024
/* same layout as REPARSE_DATA_BUFFER, which is defined only in old winnt.h */
typedef struct REPARSE_DATA
{
ULONG ReparseTag;
WORD ReparseDataLength;
WORD Reserved;
union
{
struct
{
WORD SubstituteNameOffset;
WORD SubstituteNameLength;
WORD PrintNameOffset;
WORD PrintNameLength;
ULONG Flags;
WCHAR PathBuffer[1];
} Symlink;
struct
{
WORD SubstituteNameOffset;
WORD SubstituteNameLength;
WORD PrintNameOffset;
WORD PrintNameLength;
WCHAR PathBuffer[1];
} Mount;
struct
{
BYTE DataBuffer[REPARSE_DATA_SIZE];
} Generic;
};
} REPARSE_DATA;
ssize_t
readlink(const char *path, char *target, size_t size)
{
HANDLE handle;
DWORD attr;
REPARSE_DATA data;
DWORD datasize;
PCWSTR wpath;
int wlen;
int r;
attr = GetFileAttributes(path);
if (attr == INVALID_FILE_ATTRIBUTES)
{
_dosmaperr(GetLastError());
return -1;
}
if ((attr & FILE_ATTRIBUTE_REPARSE_POINT) == 0)
{
errno = EINVAL; /* not a symlink */
return -1;
}
handle = CreateFileA(path, 0,
FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
if (handle == INVALID_HANDLE_VALUE)
{
_dosmaperr(GetLastError());
return -1;
}
wpath = NULL;
if (DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, NULL, 0,
&data, sizeof(data), &datasize, NULL))
{
switch (data.ReparseTag)
{
case IO_REPARSE_TAG_MOUNT_POINT:
{
wpath = data.Mount.PathBuffer + data.Mount.SubstituteNameOffset;
wlen = data.Mount.SubstituteNameLength;
break;
}
case IO_REPARSE_TAG_SYMLINK:
{
wpath = data.Symlink.PathBuffer + data.Symlink.SubstituteNameOffset;
wlen = data.Symlink.SubstituteNameLength;
break;
}
}
}
if (wpath == NULL)
r = -1;
else
{
if (wcsncmp(wpath, L"\\??\\", 4) == 0 ||
wcsncmp(wpath, L"\\\\?\\", 4) == 0)
{
wpath += 4;
wlen -= 4;
}
r = WideCharToMultiByte(CP_ACP, 0, wpath, wlen, target, size, NULL, NULL);
}
CloseHandle(handle);
return r;
}
#endif

@ -1,210 +1,210 @@
/*-------------------------------------------------------------------------
*
* validate.c: validate backup files.
*
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
#include <sys/stat.h>
static bool pgBackupValidateFiles(parray *files, const char *root, bool size_only);
/*
* Validate files in the backup and update its status to OK.
* If any of files are corrupted, update its stutus to CORRUPT.
*/
int
do_validate(pgBackupRange *range)
{
int i;
parray *backup_list;
int ret;
bool another_pg_rman = false;
ret = catalog_lock();
if (ret == 1)
another_pg_rman = true;
/* get backup list matches given range */
backup_list = catalog_get_backup_list(range);
if(!backup_list){
elog(ERROR_SYSTEM, _("can't process any more."));
}
parray_qsort(backup_list, pgBackupCompareId);
for (i = 0; i < parray_num(backup_list); i++)
{
pgBackup *backup = (pgBackup *)parray_get(backup_list, i);
/* clean extra backups (switch STATUS to ERROR) */
if(!another_pg_rman &&
(backup->status == BACKUP_STATUS_RUNNING || backup->status == BACKUP_STATUS_DELETING)){
backup->status = BACKUP_STATUS_ERROR;
pgBackupWriteIni(backup);
}
/* Validate completed backups only. */
if (backup->status != BACKUP_STATUS_DONE)
continue;
/* validate with CRC value and update status to OK */
pgBackupValidate(backup, false, false, (HAVE_DATABASE(backup)));
}
/* cleanup */
parray_walk(backup_list, pgBackupFree);
parray_free(backup_list);
catalog_unlock();
return 0;
}
/*
* Validate each files in the backup with its size.
*/
void
pgBackupValidate(pgBackup *backup, bool size_only, bool for_get_timeline, bool with_database)
{
char timestamp[100];
char base_path[MAXPGPATH];
char path[MAXPGPATH];
parray *files;
bool corrupted = false;
time2iso(timestamp, lengthof(timestamp), backup->start_time);
if(!for_get_timeline){
if (with_database)
elog(INFO, "validate: %s backup and archive log files by %s", timestamp, (size_only ? "SIZE" : "CRC"));
else{
if (backup->backup_mode == BACKUP_MODE_ARCHIVE)
elog(INFO, "validate: %s archive log files by %s", timestamp, (size_only ? "SIZE" : "CRC"));
else if (backup->with_serverlog)
elog(INFO, "validate: %s server log files by %s", timestamp, (size_only ? "SIZE" : "CRC"));
}
}
if(!check){
if (HAVE_DATABASE(backup))
{
elog(LOG, "database files...");
pgBackupGetPath(backup, base_path, lengthof(base_path), DATABASE_DIR);
pgBackupGetPath(backup, path, lengthof(path),
DATABASE_FILE_LIST);
files = dir_read_file_list(base_path, path);
if (!pgBackupValidateFiles(files, base_path, size_only))
corrupted = true;
parray_walk(files, pgFileFree);
parray_free(files);
}
if (HAVE_ARCLOG(backup))
{
elog(LOG, "archive WAL files...");
pgBackupGetPath(backup, base_path, lengthof(base_path), ARCLOG_DIR);
pgBackupGetPath(backup, path, lengthof(path), ARCLOG_FILE_LIST);
files = dir_read_file_list(base_path, path);
if (!pgBackupValidateFiles(files, base_path, size_only))
corrupted = true;
parray_walk(files, pgFileFree);
parray_free(files);
}
if (backup->with_serverlog)
{
elog(LOG, "server log files...");
pgBackupGetPath(backup, base_path, lengthof(base_path), SRVLOG_DIR);
pgBackupGetPath(backup, path, lengthof(path), SRVLOG_FILE_LIST);
files = dir_read_file_list(base_path, path);
if (!pgBackupValidateFiles(files, base_path, size_only))
corrupted = true;
parray_walk(files, pgFileFree);
parray_free(files);
}
/* update status to OK */
if (corrupted)
backup->status = BACKUP_STATUS_CORRUPT;
else
backup->status = BACKUP_STATUS_OK;
pgBackupWriteIni(backup);
if (corrupted)
elog(WARNING, "backup %s is corrupted", timestamp);
else
elog(LOG, "backup %s is valid", timestamp);
}
}
static const char *
get_relative_path(const char *path, const char *root)
{
size_t rootlen = strlen(root);
if (strncmp(path, root, rootlen) == 0 && path[rootlen] == '/')
return path + rootlen + 1;
else
return path;
}
/*
* Validate files in the backup with size or CRC.
*/
static bool
pgBackupValidateFiles(parray *files, const char *root, bool size_only)
{
int i;
for (i = 0; i < parray_num(files); i++)
{
struct stat st;
pgFile *file = (pgFile *) parray_get(files, i);
if (interrupted)
elog(ERROR_INTERRUPTED, _("interrupted during validate"));
/* skipped backup while incremental backup */
if (file->write_size == BYTES_INVALID || !S_ISREG(file->mode))
continue;
/* print progress */
elog(LOG, _("(%d/%lu) %s"), i + 1, (unsigned long) parray_num(files),
get_relative_path(file->path, root));
/* always validate file size */
if (stat(file->path, &st) == -1)
{
if (errno == ENOENT)
elog(WARNING, _("backup file \"%s\" vanished"), file->path);
else
elog(ERROR_SYSTEM, _("can't stat backup file \"%s\": %s"),
get_relative_path(file->path, root), strerror(errno));
return false;
}
if (file->write_size != st.st_size)
{
elog(WARNING, _("size of backup file \"%s\" must be %lu but %lu"),
get_relative_path(file->path, root),
(unsigned long) file->write_size,
(unsigned long) st.st_size);
return false;
}
/* validate CRC too */
if (!size_only)
{
pg_crc32 crc;
crc = pgFileGetCRC(file);
if (crc != file->crc)
{
elog(WARNING, _("CRC of backup file \"%s\" must be %X but %X"),
get_relative_path(file->path, root), file->crc, crc);
return false;
}
}
}
return true;
}
/*-------------------------------------------------------------------------
*
* validate.c: validate backup files.
*
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
#include <sys/stat.h>
static bool pgBackupValidateFiles(parray *files, const char *root, bool size_only);
/*
* Validate files in the backup and update its status to OK.
* If any of files are corrupted, update its stutus to CORRUPT.
*/
int
do_validate(pgBackupRange *range)
{
int i;
parray *backup_list;
int ret;
bool another_pg_rman = false;
ret = catalog_lock();
if (ret == 1)
another_pg_rman = true;
/* get backup list matches given range */
backup_list = catalog_get_backup_list(range);
if(!backup_list){
elog(ERROR_SYSTEM, _("can't process any more."));
}
parray_qsort(backup_list, pgBackupCompareId);
for (i = 0; i < parray_num(backup_list); i++)
{
pgBackup *backup = (pgBackup *)parray_get(backup_list, i);
/* clean extra backups (switch STATUS to ERROR) */
if(!another_pg_rman &&
(backup->status == BACKUP_STATUS_RUNNING || backup->status == BACKUP_STATUS_DELETING)){
backup->status = BACKUP_STATUS_ERROR;
pgBackupWriteIni(backup);
}
/* Validate completed backups only. */
if (backup->status != BACKUP_STATUS_DONE)
continue;
/* validate with CRC value and update status to OK */
pgBackupValidate(backup, false, false, (HAVE_DATABASE(backup)));
}
/* cleanup */
parray_walk(backup_list, pgBackupFree);
parray_free(backup_list);
catalog_unlock();
return 0;
}
/*
* Validate each files in the backup with its size.
*/
void
pgBackupValidate(pgBackup *backup, bool size_only, bool for_get_timeline, bool with_database)
{
char timestamp[100];
char base_path[MAXPGPATH];
char path[MAXPGPATH];
parray *files;
bool corrupted = false;
time2iso(timestamp, lengthof(timestamp), backup->start_time);
if(!for_get_timeline){
if (with_database)
elog(INFO, "validate: %s backup and archive log files by %s", timestamp, (size_only ? "SIZE" : "CRC"));
else{
if (backup->backup_mode == BACKUP_MODE_ARCHIVE)
elog(INFO, "validate: %s archive log files by %s", timestamp, (size_only ? "SIZE" : "CRC"));
else if (backup->with_serverlog)
elog(INFO, "validate: %s server log files by %s", timestamp, (size_only ? "SIZE" : "CRC"));
}
}
if(!check){
if (HAVE_DATABASE(backup))
{
elog(LOG, "database files...");
pgBackupGetPath(backup, base_path, lengthof(base_path), DATABASE_DIR);
pgBackupGetPath(backup, path, lengthof(path),
DATABASE_FILE_LIST);
files = dir_read_file_list(base_path, path);
if (!pgBackupValidateFiles(files, base_path, size_only))
corrupted = true;
parray_walk(files, pgFileFree);
parray_free(files);
}
if (HAVE_ARCLOG(backup))
{
elog(LOG, "archive WAL files...");
pgBackupGetPath(backup, base_path, lengthof(base_path), ARCLOG_DIR);
pgBackupGetPath(backup, path, lengthof(path), ARCLOG_FILE_LIST);
files = dir_read_file_list(base_path, path);
if (!pgBackupValidateFiles(files, base_path, size_only))
corrupted = true;
parray_walk(files, pgFileFree);
parray_free(files);
}
if (backup->with_serverlog)
{
elog(LOG, "server log files...");
pgBackupGetPath(backup, base_path, lengthof(base_path), SRVLOG_DIR);
pgBackupGetPath(backup, path, lengthof(path), SRVLOG_FILE_LIST);
files = dir_read_file_list(base_path, path);
if (!pgBackupValidateFiles(files, base_path, size_only))
corrupted = true;
parray_walk(files, pgFileFree);
parray_free(files);
}
/* update status to OK */
if (corrupted)
backup->status = BACKUP_STATUS_CORRUPT;
else
backup->status = BACKUP_STATUS_OK;
pgBackupWriteIni(backup);
if (corrupted)
elog(WARNING, "backup %s is corrupted", timestamp);
else
elog(LOG, "backup %s is valid", timestamp);
}
}
static const char *
get_relative_path(const char *path, const char *root)
{
size_t rootlen = strlen(root);
if (strncmp(path, root, rootlen) == 0 && path[rootlen] == '/')
return path + rootlen + 1;
else
return path;
}
/*
* Validate files in the backup with size or CRC.
*/
static bool
pgBackupValidateFiles(parray *files, const char *root, bool size_only)
{
int i;
for (i = 0; i < parray_num(files); i++)
{
struct stat st;
pgFile *file = (pgFile *) parray_get(files, i);
if (interrupted)
elog(ERROR_INTERRUPTED, _("interrupted during validate"));
/* skipped backup while incremental backup */
if (file->write_size == BYTES_INVALID || !S_ISREG(file->mode))
continue;
/* print progress */
elog(LOG, _("(%d/%lu) %s"), i + 1, (unsigned long) parray_num(files),
get_relative_path(file->path, root));
/* always validate file size */
if (stat(file->path, &st) == -1)
{
if (errno == ENOENT)
elog(WARNING, _("backup file \"%s\" vanished"), file->path);
else
elog(ERROR_SYSTEM, _("can't stat backup file \"%s\": %s"),
get_relative_path(file->path, root), strerror(errno));
return false;
}
if (file->write_size != st.st_size)
{
elog(WARNING, _("size of backup file \"%s\" must be %lu but %lu"),
get_relative_path(file->path, root),
(unsigned long) file->write_size,
(unsigned long) st.st_size);
return false;
}
/* validate CRC too */
if (!size_only)
{
pg_crc32 crc;
crc = pgFileGetCRC(file);
if (crc != file->crc)
{
elog(WARNING, _("CRC of backup file \"%s\" must be %X but %X"),
get_relative_path(file->path, root), file->crc, crc);
return false;
}
}
}
return true;
}

368
verify.c

@ -1,184 +1,184 @@
/*-------------------------------------------------------------------------
*
* verify.c: verify backup files.
*
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
#include <unistd.h>
static bool verify_files(List *files, const char *root);
static bool verify_file(pgFile *file, const char *root);
typedef struct VerifyJob
{
void (*routine)(struct VerifyJob *);
pgFile *file;
const char *root;
volatile bool *ok;
} VerifyJob;
/* copy the file into backup */
static void
verify_routine(VerifyJob *job)
{
if (*job->ok && !verify_file(job->file, job->root))
*job->ok = false;
}
#define VERIFY_MASK (BACKUP_MASK(BACKUP_DONE))
void
do_verify(pgRange range)
{
Database db;
List *backups;
ListCell *cell;
db = db_open();
backups = db_list_backups(db, range, VERIFY_MASK);
foreach (cell, backups)
verify_backup(db, lfirst(cell));
db_close(db);
list_free_deep(backups);
}
/*
* Verify files in the backup and update the status to OK or BAD.
*/
void
verify_backup(Database db, pgBackup *backup)
{
char root[MAXPGPATH];
char datetime[DATESTRLEN];
List *dbfiles;
List *arclogs = NIL;
bool ok;
if (backup->status == BACKUP_OK)
return; /* already verified */
elog(INFO, "verify: %s", date2str(datetime, backup->start_time));
make_backup_path(root, backup->start_time);
/* Verify data files. */
dbfiles = db_list_dbfiles(db, backup);
ok = verify_files(dbfiles, root);
list_destroy(dbfiles, pgFile_free);
/* Verify archive log files. */
arclogs = db_list_arclogs(db, backup);
ok = ok && verify_files(arclogs, root);
/* update the status to OK or BAD */
backup->status = (ok ? BACKUP_OK : BACKUP_BAD);
db_update_status(db, backup, arclogs);
list_destroy(arclogs, pgFile_free);
if (!ok)
elog(WARNING, "corrupted backup: %s", datetime);
}
static bool
verify_files(List *files, const char *root)
{
volatile bool ok = true;
ListCell *cell;
foreach (cell, files)
{
pgFile *file = lfirst(cell);
VerifyJob *job;
CHECK_FOR_INTERRUPTS();
if (!ok)
break;
job = pgut_new(VerifyJob);
job->routine = verify_routine;
job->file = file;
job->root = root;
job->ok = &ok;
job_push((Job *) job);
}
job_wait();
return ok;
}
/*
* Verify files in the backup with size or CRC.
*/
static bool
verify_file(pgFile *file, const char *root)
{
FILE *fp;
pg_crc32 crc;
size_t len;
char path[MAXPGPATH];
char buf[8291];
/* skipped or already verified file */
if (file->flags & (PGFILE_UNMODIFIED | PGFILE_VERIFIED))
return true;
/* not a file */
if (!S_ISREG(file->mode))
{
/* XXX: check if exists? */
return true;
}
elog(LOG, "verify file: %s", file->name);
/*
* Exit on error and don't mark the backup in corrupted status to
* let users retry verification.
*/
join_path_components(path, root, file->name);
if ((fp = pgut_fopen(path, "r+")) == NULL)
{
elog(WARNING, "missing file \"%s\"", path);
return false;
}
/* Does file have correct crc? */
INIT_CRC32(crc);
while ((len = fread(buf, 1, sizeof(buf), fp)) > 0)
{
CHECK_FOR_INTERRUPTS();
COMP_CRC32(crc, buf, len);
}
FIN_CRC32(crc);
/* Update crc if not calclated yet. */
if ((file->flags & PGFILE_CRC) == 0)
{
file->crc = crc;
file->flags |= PGFILE_CRC;
}
else if (file->crc != crc)
{
fclose(fp);
elog(WARNING, "corrupted file \"%s\"", path);
return false;
}
/* Flush backup files into disk */
if (fflush(fp) != 0 || fsync(fileno(fp)) != 0)
ereport(ERROR,
(errcode_errno(),
errmsg("could not flush file \"%s\": ", path)));
fclose(fp);
file->flags |= PGFILE_VERIFIED;
return true;
}
/*-------------------------------------------------------------------------
*
* verify.c: verify backup files.
*
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
#include <unistd.h>
static bool verify_files(List *files, const char *root);
static bool verify_file(pgFile *file, const char *root);
typedef struct VerifyJob
{
void (*routine)(struct VerifyJob *);
pgFile *file;
const char *root;
volatile bool *ok;
} VerifyJob;
/* copy the file into backup */
static void
verify_routine(VerifyJob *job)
{
if (*job->ok && !verify_file(job->file, job->root))
*job->ok = false;
}
#define VERIFY_MASK (BACKUP_MASK(BACKUP_DONE))
void
do_verify(pgRange range)
{
Database db;
List *backups;
ListCell *cell;
db = db_open();
backups = db_list_backups(db, range, VERIFY_MASK);
foreach (cell, backups)
verify_backup(db, lfirst(cell));
db_close(db);
list_free_deep(backups);
}
/*
* Verify files in the backup and update the status to OK or BAD.
*/
void
verify_backup(Database db, pgBackup *backup)
{
char root[MAXPGPATH];
char datetime[DATESTRLEN];
List *dbfiles;
List *arclogs = NIL;
bool ok;
if (backup->status == BACKUP_OK)
return; /* already verified */
elog(INFO, "verify: %s", date2str(datetime, backup->start_time));
make_backup_path(root, backup->start_time);
/* Verify data files. */
dbfiles = db_list_dbfiles(db, backup);
ok = verify_files(dbfiles, root);
list_destroy(dbfiles, pgFile_free);
/* Verify archive log files. */
arclogs = db_list_arclogs(db, backup);
ok = ok && verify_files(arclogs, root);
/* update the status to OK or BAD */
backup->status = (ok ? BACKUP_OK : BACKUP_BAD);
db_update_status(db, backup, arclogs);
list_destroy(arclogs, pgFile_free);
if (!ok)
elog(WARNING, "corrupted backup: %s", datetime);
}
static bool
verify_files(List *files, const char *root)
{
volatile bool ok = true;
ListCell *cell;
foreach (cell, files)
{
pgFile *file = lfirst(cell);
VerifyJob *job;
CHECK_FOR_INTERRUPTS();
if (!ok)
break;
job = pgut_new(VerifyJob);
job->routine = verify_routine;
job->file = file;
job->root = root;
job->ok = &ok;
job_push((Job *) job);
}
job_wait();
return ok;
}
/*
* Verify files in the backup with size or CRC.
*/
static bool
verify_file(pgFile *file, const char *root)
{
FILE *fp;
pg_crc32 crc;
size_t len;
char path[MAXPGPATH];
char buf[8291];
/* skipped or already verified file */
if (file->flags & (PGFILE_UNMODIFIED | PGFILE_VERIFIED))
return true;
/* not a file */
if (!S_ISREG(file->mode))
{
/* XXX: check if exists? */
return true;
}
elog(LOG, "verify file: %s", file->name);
/*
* Exit on error and don't mark the backup in corrupted status to
* let users retry verification.
*/
join_path_components(path, root, file->name);
if ((fp = pgut_fopen(path, "r+")) == NULL)
{
elog(WARNING, "missing file \"%s\"", path);
return false;
}
/* Does file have correct crc? */
INIT_CRC32(crc);
while ((len = fread(buf, 1, sizeof(buf), fp)) > 0)
{
CHECK_FOR_INTERRUPTS();
COMP_CRC32(crc, buf, len);
}
FIN_CRC32(crc);
/* Update crc if not calclated yet. */
if ((file->flags & PGFILE_CRC) == 0)
{
file->crc = crc;
file->flags |= PGFILE_CRC;
}
else if (file->crc != crc)
{
fclose(fp);
elog(WARNING, "corrupted file \"%s\"", path);
return false;
}
/* Flush backup files into disk */
if (fflush(fp) != 0 || fsync(fileno(fp)) != 0)
ereport(ERROR,
(errcode_errno(),
errmsg("could not flush file \"%s\": ", path)));
fclose(fp);
file->flags |= PGFILE_VERIFIED;
return true;
}

254
xlog.c

@ -1,127 +1,127 @@
/*-------------------------------------------------------------------------
*
* xlog.c: Parse WAL files.
*
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#if PG_VERSION_NUM >= 80400
typedef unsigned long Datum;
typedef struct MemoryContextData *MemoryContext;
#endif
#include "access/xlog_internal.h"
#define XLOG_PAGE_MAGIC_v80 0xD05C /* 8.0 */
#define XLOG_PAGE_MAGIC_v81 0xD05D /* 8.1 */
#define XLOG_PAGE_MAGIC_v82 0xD05E /* 8.2 */
#define XLOG_PAGE_MAGIC_v83 0xD062 /* 8.3 */
#define XLOG_PAGE_MAGIC_v84 0xD063 /* 8.4 */
#define XLOG_PAGE_MAGIC_v90 0xD064 /* 9.0 */
#define XLOG_PAGE_MAGIC_v91 0xD066 /* 9.1 */
/*
* XLogLongPageHeaderData is modified in 8.3, but the layout is compatible
* except xlp_xlog_blcksz.
*/
typedef union XLogPage
{
XLogPageHeaderData header;
XLogLongPageHeaderData lheader;
char data[XLOG_BLCKSZ];
} XLogPage;
/*
* Return whether the file is a WAL segment or not.
* based on ValidXLOGHeader() in src/backend/access/transam/xlog.c.
*/
bool
xlog_is_complete_wal(const pgFile *file, int server_version)
{
FILE *fp;
XLogPage page;
uint16 xlog_page_magic;
fp = fopen(file->path, "r");
if (!fp)
return false;
if (fread(&page, 1, sizeof(page), fp) != XLOG_BLCKSZ)
{
fclose(fp);
return false;
}
fclose(fp);
/* xlog_page_magic from server version */
if (server_version < 80000)
return false; /* never happen */
else if (server_version < 80100)
xlog_page_magic = XLOG_PAGE_MAGIC_v80;
else if (server_version < 80200)
xlog_page_magic = XLOG_PAGE_MAGIC_v81;
else if (server_version < 80300)
xlog_page_magic = XLOG_PAGE_MAGIC_v82;
else if (server_version < 80400)
xlog_page_magic = XLOG_PAGE_MAGIC_v83;
else if (server_version < 90000)
xlog_page_magic = XLOG_PAGE_MAGIC_v84;
else if (server_version < 90100)
xlog_page_magic = XLOG_PAGE_MAGIC_v90;
else if (server_version < 90200)
xlog_page_magic = XLOG_PAGE_MAGIC_v91;
else
return false; /* not supported */
/* check header */
if (page.header.xlp_magic != xlog_page_magic)
return false;
if ((page.header.xlp_info & ~XLP_ALL_FLAGS) != 0)
return false;
if ((page.header.xlp_info & XLP_LONG_HEADER) == 0)
return false;
if (page.lheader.xlp_seg_size != XLogSegSize)
return false;
if (server_version >= 80300 && page.lheader.xlp_xlog_blcksz != XLOG_BLCKSZ)
return false;
/*
* check size (actual file size, not backup file size)
* TODO: Support pre-compressed xlog. They might have different file sizes.
*/
if (file->size != XLogSegSize)
return false;
return true;
}
bool
xlog_logfname2lsn(const char *logfname, XLogRecPtr *lsn)
{
uint32 tli;
if (sscanf(logfname, "%08X%08X%08X",
&tli, &lsn->xlogid, &lsn->xrecoff) != 3)
return false;
lsn->xrecoff *= XLogSegSize;
return true;
}
/*
* based on XLogFileName() in xlog_internal.h
*/
void
xlog_fname(char *fname, size_t len, TimeLineID tli, XLogRecPtr *lsn)
{
snprintf(fname, len, "%08X%08X%08X", tli,
lsn->xlogid, lsn->xrecoff / XLogSegSize);
}
/*-------------------------------------------------------------------------
*
* xlog.c: Parse WAL files.
*
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#if PG_VERSION_NUM >= 80400
typedef unsigned long Datum;
typedef struct MemoryContextData *MemoryContext;
#endif
#include "access/xlog_internal.h"
#define XLOG_PAGE_MAGIC_v80 0xD05C /* 8.0 */
#define XLOG_PAGE_MAGIC_v81 0xD05D /* 8.1 */
#define XLOG_PAGE_MAGIC_v82 0xD05E /* 8.2 */
#define XLOG_PAGE_MAGIC_v83 0xD062 /* 8.3 */
#define XLOG_PAGE_MAGIC_v84 0xD063 /* 8.4 */
#define XLOG_PAGE_MAGIC_v90 0xD064 /* 9.0 */
#define XLOG_PAGE_MAGIC_v91 0xD066 /* 9.1 */
/*
* XLogLongPageHeaderData is modified in 8.3, but the layout is compatible
* except xlp_xlog_blcksz.
*/
typedef union XLogPage
{
XLogPageHeaderData header;
XLogLongPageHeaderData lheader;
char data[XLOG_BLCKSZ];
} XLogPage;
/*
* Return whether the file is a WAL segment or not.
* based on ValidXLOGHeader() in src/backend/access/transam/xlog.c.
*/
bool
xlog_is_complete_wal(const pgFile *file, int server_version)
{
FILE *fp;
XLogPage page;
uint16 xlog_page_magic;
fp = fopen(file->path, "r");
if (!fp)
return false;
if (fread(&page, 1, sizeof(page), fp) != XLOG_BLCKSZ)
{
fclose(fp);
return false;
}
fclose(fp);
/* xlog_page_magic from server version */
if (server_version < 80000)
return false; /* never happen */
else if (server_version < 80100)
xlog_page_magic = XLOG_PAGE_MAGIC_v80;
else if (server_version < 80200)
xlog_page_magic = XLOG_PAGE_MAGIC_v81;
else if (server_version < 80300)
xlog_page_magic = XLOG_PAGE_MAGIC_v82;
else if (server_version < 80400)
xlog_page_magic = XLOG_PAGE_MAGIC_v83;
else if (server_version < 90000)
xlog_page_magic = XLOG_PAGE_MAGIC_v84;
else if (server_version < 90100)
xlog_page_magic = XLOG_PAGE_MAGIC_v90;
else if (server_version < 90200)
xlog_page_magic = XLOG_PAGE_MAGIC_v91;
else
return false; /* not supported */
/* check header */
if (page.header.xlp_magic != xlog_page_magic)
return false;
if ((page.header.xlp_info & ~XLP_ALL_FLAGS) != 0)
return false;
if ((page.header.xlp_info & XLP_LONG_HEADER) == 0)
return false;
if (page.lheader.xlp_seg_size != XLogSegSize)
return false;
if (server_version >= 80300 && page.lheader.xlp_xlog_blcksz != XLOG_BLCKSZ)
return false;
/*
* check size (actual file size, not backup file size)
* TODO: Support pre-compressed xlog. They might have different file sizes.
*/
if (file->size != XLogSegSize)
return false;
return true;
}
bool
xlog_logfname2lsn(const char *logfname, XLogRecPtr *lsn)
{
uint32 tli;
if (sscanf(logfname, "%08X%08X%08X",
&tli, &lsn->xlogid, &lsn->xrecoff) != 3)
return false;
lsn->xrecoff *= XLogSegSize;
return true;
}
/*
* based on XLogFileName() in xlog_internal.h
*/
void
xlog_fname(char *fname, size_t len, TimeLineID tli, XLogRecPtr *lsn)
{
snprintf(fname, len, "%08X%08X%08X", tli,
lsn->xlogid, lsn->xrecoff / XLogSegSize);
}