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:
parent
dfecc77c5e
commit
98b6bac4b9
624
clean.c
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(¤t))
|
||||
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(¤t))
|
||||
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
|
||||
|
@ -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
|
||||
|
526
delete.c
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;
|
||||
}
|
||||
|
346
init.c
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
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);
|
||||
}
|
||||
|
||||
|
70
parray.h
70
parray.h
@ -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
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
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
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 */
|
||||
|
1030
pgsql_src/pg_crc.c
1030
pgsql_src/pg_crc.c
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);
|
||||
}
|
||||
|
||||
|
386
pgut/pgut-port.c
386
pgut/pgut-port.c
@ -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
|
||||
|
100
pgut/pgut-port.h
100
pgut/pgut-port.h
@ -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 */
|
||||
|
3408
pgut/pgut.c
3408
pgut/pgut.c
File diff suppressed because it is too large
Load Diff
506
pgut/pgut.h
506
pgut/pgut.h
@ -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
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;
|
||||
}
|
||||
|
@ -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
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
|
||||
|
||||
|
160
sql/option.sh
160
sql/option.sh
@ -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
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
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
|
||||
|
420
validate.c
420
validate.c
@ -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
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
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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user