diff --git a/src/backup.c b/src/backup.c index 557727ef..393e08f5 100644 --- a/src/backup.c +++ b/src/backup.c @@ -133,7 +133,7 @@ do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool pg_ptrack_clear(backup_conn, nodeInfo->ptrack_version_num); /* notify start of backup to PostgreSQL server */ - time2iso(label, lengthof(label), current.start_time); + time2iso(label, lengthof(label), current.start_time, false); strncat(label, " with pg_probackup", lengthof(label) - strlen(" with pg_probackup")); diff --git a/src/catalog.c b/src/catalog.c index d154253f..4da6ff05 100644 --- a/src/catalog.c +++ b/src/catalog.c @@ -789,7 +789,7 @@ catalog_get_backup_list(const char *instance_name, time_t requested_backup_id) } else if (strcmp(base36enc(backup->start_time), data_ent->d_name) != 0) { - elog(VERBOSE, "backup ID in control file \"%s\" doesn't match name of the backup folder \"%s\"", + elog(WARNING, "backup ID in control file \"%s\" doesn't match name of the backup folder \"%s\"", base36enc(backup->start_time), backup_conf_path); } @@ -1952,7 +1952,7 @@ pin_backup(pgBackup *target_backup, pgSetBackupParams *set_backup_params) { char expire_timestamp[100]; - time2iso(expire_timestamp, lengthof(expire_timestamp), target_backup->expire_time); + time2iso(expire_timestamp, lengthof(expire_timestamp), target_backup->expire_time, false); elog(INFO, "Backup %s is pinned until '%s'", base36enc(target_backup->start_time), expire_timestamp); } @@ -2003,7 +2003,7 @@ add_note(pgBackup *target_backup, char *note) * Write information about backup.in to stream "out". */ void -pgBackupWriteControl(FILE *out, pgBackup *backup) +pgBackupWriteControl(FILE *out, pgBackup *backup, bool utc) { char timestamp[100]; @@ -2035,27 +2035,27 @@ pgBackupWriteControl(FILE *out, pgBackup *backup) (uint32) (backup->stop_lsn >> 32), (uint32) backup->stop_lsn); - time2iso(timestamp, lengthof(timestamp), backup->start_time); + time2iso(timestamp, lengthof(timestamp), backup->start_time, utc); fio_fprintf(out, "start-time = '%s'\n", timestamp); if (backup->merge_time > 0) { - time2iso(timestamp, lengthof(timestamp), backup->merge_time); + time2iso(timestamp, lengthof(timestamp), backup->merge_time, utc); fio_fprintf(out, "merge-time = '%s'\n", timestamp); } if (backup->end_time > 0) { - time2iso(timestamp, lengthof(timestamp), backup->end_time); + time2iso(timestamp, lengthof(timestamp), backup->end_time, utc); fio_fprintf(out, "end-time = '%s'\n", timestamp); } fio_fprintf(out, "recovery-xid = " XID_FMT "\n", backup->recovery_xid); if (backup->recovery_time > 0) { - time2iso(timestamp, lengthof(timestamp), backup->recovery_time); + time2iso(timestamp, lengthof(timestamp), backup->recovery_time, utc); fio_fprintf(out, "recovery-time = '%s'\n", timestamp); } if (backup->expire_time > 0) { - time2iso(timestamp, lengthof(timestamp), backup->expire_time); + time2iso(timestamp, lengthof(timestamp), backup->expire_time, utc); fio_fprintf(out, "expire-time = '%s'\n", timestamp); } @@ -2109,7 +2109,7 @@ pgBackupWriteControl(FILE *out, pgBackup *backup) void write_backup(pgBackup *backup, bool strict) { - FILE *fp_out = NULL; + FILE *fp = NULL; char path[MAXPGPATH]; char path_temp[MAXPGPATH]; char buf[8192]; @@ -2117,8 +2117,8 @@ write_backup(pgBackup *backup, bool strict) join_path_components(path, backup->root_dir, BACKUP_CONTROL_FILE); snprintf(path_temp, sizeof(path_temp), "%s.tmp", path); - fp_out = fopen(path_temp, PG_BINARY_W); - if (fp_out == NULL) + fp = fopen(path_temp, PG_BINARY_W); + if (fp == NULL) elog(ERROR, "Cannot open control file \"%s\": %s", path_temp, strerror(errno)); @@ -2126,12 +2126,12 @@ write_backup(pgBackup *backup, bool strict) elog(ERROR, "Cannot change mode of \"%s\": %s", path_temp, strerror(errno)); - setvbuf(fp_out, buf, _IOFBF, sizeof(buf)); + setvbuf(fp, buf, _IOFBF, sizeof(buf)); - pgBackupWriteControl(fp_out, backup); + pgBackupWriteControl(fp, backup, true); /* Ignore 'out of space' error in lax mode */ - if (fflush(fp_out) != 0) + if (fflush(fp) != 0) { int elevel = ERROR; int save_errno = errno; @@ -2144,17 +2144,18 @@ write_backup(pgBackup *backup, bool strict) if (!strict && (save_errno == ENOSPC)) { - fclose(fp_out); + fclose(fp); + fio_unlink(path_temp, FIO_BACKUP_HOST); return; } } - if (fsync(fileno(fp_out)) < 0) - elog(ERROR, "Cannot sync control file \"%s\": %s", + if (fclose(fp) != 0) + elog(ERROR, "Cannot close control file \"%s\": %s", path_temp, strerror(errno)); - if (fclose(fp_out) != 0) - elog(ERROR, "Cannot close control file \"%s\": %s", + if (fio_sync(path_temp, FIO_BACKUP_HOST) < 0) + elog(ERROR, "Cannot sync control file \"%s\": %s", path_temp, strerror(errno)); if (rename(path_temp, path) < 0) diff --git a/src/delete.c b/src/delete.c index b2010c28..a6d6b51b 100644 --- a/src/delete.c +++ b/src/delete.c @@ -316,7 +316,7 @@ do_retention_internal(parray *backup_list, parray *to_keep_list, parray *to_purg (backup->expire_time > current_time)) { char expire_timestamp[100]; - time2iso(expire_timestamp, lengthof(expire_timestamp), backup->expire_time); + time2iso(expire_timestamp, lengthof(expire_timestamp), backup->expire_time, false); elog(LOG, "Backup %s is pinned until '%s', retain", base36enc(backup->start_time), expire_timestamp); @@ -740,7 +740,7 @@ delete_backup_files(pgBackup *backup) return; } - time2iso(timestamp, lengthof(timestamp), backup->recovery_time); + time2iso(timestamp, lengthof(timestamp), backup->recovery_time, false); elog(INFO, "Delete: %s %s", base36enc(backup->start_time), timestamp); diff --git a/src/parsexlog.c b/src/parsexlog.c index 38c62c6a..41a410d3 100644 --- a/src/parsexlog.c +++ b/src/parsexlog.c @@ -480,7 +480,7 @@ validate_wal(pgBackup *backup, const char *archivedir, last_rec.rec_xid = backup->recovery_xid; last_rec.rec_lsn = backup->stop_lsn; - time2iso(last_timestamp, lengthof(last_timestamp), backup->recovery_time); + time2iso(last_timestamp, lengthof(last_timestamp), backup->recovery_time, false); if ((TransactionIdIsValid(target_xid) && target_xid == last_rec.rec_xid) || (target_time != 0 && backup->recovery_time >= target_time) @@ -493,7 +493,7 @@ validate_wal(pgBackup *backup, const char *archivedir, InvalidXLogRecPtr, true, validateXLogRecord, &last_rec, true); if (last_rec.rec_time > 0) time2iso(last_timestamp, lengthof(last_timestamp), - timestamptz_to_time_t(last_rec.rec_time)); + timestamptz_to_time_t(last_rec.rec_time), false); /* There are all needed WAL records */ if (all_wal) @@ -508,7 +508,7 @@ validate_wal(pgBackup *backup, const char *archivedir, (uint32) (last_rec.rec_lsn >> 32), (uint32) last_rec.rec_lsn); if (target_time > 0) - time2iso(target_timestamp, lengthof(target_timestamp), target_time); + time2iso(target_timestamp, lengthof(target_timestamp), target_time, false); if (TransactionIdIsValid(target_xid) && target_time != 0) elog(ERROR, "Not enough WAL records to time %s and xid " XID_FMT, target_timestamp, target_xid); diff --git a/src/pg_probackup.h b/src/pg_probackup.h index d0b4891d..edd03304 100644 --- a/src/pg_probackup.h +++ b/src/pg_probackup.h @@ -394,10 +394,9 @@ struct pgBackup TimeLineID tli; /* timeline of start and stop backup lsns */ XLogRecPtr start_lsn; /* backup's starting transaction log location */ XLogRecPtr stop_lsn; /* backup's finishing transaction log location */ - time_t start_time; /* since this moment backup has status - * BACKUP_STATUS_RUNNING */ - time_t merge_dest_backup; /* start_time of incremental backup, - * this backup is merging with. + time_t start_time; /* UTC time of backup creation */ + time_t merge_dest_backup; /* start_time of incremental backup with + * which this backup is merging with. * Only available for FULL backups * with MERGING or MERGED statuses */ time_t merge_time; /* the moment when merge was started or 0 */ @@ -892,7 +891,7 @@ extern void do_set_backup(const char *instance_name, time_t backup_id, extern void pin_backup(pgBackup *target_backup, pgSetBackupParams *set_backup_params); extern void add_note(pgBackup *target_backup, char *note); -extern void pgBackupWriteControl(FILE *out, pgBackup *backup); +extern void pgBackupWriteControl(FILE *out, pgBackup *backup, bool utc); extern void write_backup_filelist(pgBackup *backup, parray *files, const char *root, parray *external_list, bool sync); @@ -1081,7 +1080,7 @@ extern void set_min_recovery_point(pgFile *file, const char *backup_path, extern void copy_pgcontrol_file(const char *from_fullpath, fio_location from_location, const char *to_fullpath, fio_location to_location, pgFile *file); -extern void time2iso(char *buf, size_t len, time_t time); +extern void time2iso(char *buf, size_t len, time_t time, bool utc); extern const char *status2str(BackupStatus status); extern BackupStatus str2status(const char *status); extern const char *base36enc(long unsigned int value); diff --git a/src/restore.c b/src/restore.c index 7bfc1b7f..81b50cb3 100644 --- a/src/restore.c +++ b/src/restore.c @@ -643,7 +643,7 @@ restore_chain(pgBackup *dest_backup, parray *parent_chain, time_t start_time, end_time; /* Preparations for actual restoring */ - time2iso(timestamp, lengthof(timestamp), dest_backup->start_time); + time2iso(timestamp, lengthof(timestamp), dest_backup->start_time, false); elog(INFO, "Restoring the database from backup at %s", timestamp); dest_files = get_backup_filelist(dest_backup, true); @@ -1465,7 +1465,7 @@ pg12_recovery_config(pgBackup *backup, bool add_include) { char current_time_str[100]; - time2iso(current_time_str, lengthof(current_time_str), current_time); + time2iso(current_time_str, lengthof(current_time_str), current_time, false); snprintf(postgres_auto_path, lengthof(postgres_auto_path), "%s/postgresql.auto.conf", instance_config.pgdata); diff --git a/src/show.c b/src/show.c index ce6604ac..c1482772 100644 --- a/src/show.c +++ b/src/show.c @@ -374,12 +374,12 @@ print_backup_json_object(PQExpBuffer buf, pgBackup *backup) (uint32) (backup->stop_lsn >> 32), (uint32) backup->stop_lsn); json_add_value(buf, "stop-lsn", lsn, json_level, true); - time2iso(timestamp, lengthof(timestamp), backup->start_time); + time2iso(timestamp, lengthof(timestamp), backup->start_time, false); json_add_value(buf, "start-time", timestamp, json_level, true); if (backup->end_time) { - time2iso(timestamp, lengthof(timestamp), backup->end_time); + time2iso(timestamp, lengthof(timestamp), backup->end_time, false); json_add_value(buf, "end-time", timestamp, json_level, true); } @@ -388,13 +388,13 @@ print_backup_json_object(PQExpBuffer buf, pgBackup *backup) if (backup->recovery_time > 0) { - time2iso(timestamp, lengthof(timestamp), backup->recovery_time); + time2iso(timestamp, lengthof(timestamp), backup->recovery_time, false); json_add_value(buf, "recovery-time", timestamp, json_level, true); } if (backup->expire_time > 0) { - time2iso(timestamp, lengthof(timestamp), backup->expire_time); + time2iso(timestamp, lengthof(timestamp), backup->expire_time, false); json_add_value(buf, "expire-time", timestamp, json_level, true); } @@ -482,7 +482,7 @@ show_backup(const char *instance_name, time_t requested_backup_id) } if (show_format == SHOW_PLAIN) - pgBackupWriteControl(stdout, backup); + pgBackupWriteControl(stdout, backup, false); else elog(ERROR, "Invalid show format %d", (int) show_format); @@ -550,7 +550,7 @@ show_instance_plain(const char *instance_name, parray *backup_list, bool show_na /* Recovery Time */ if (backup->recovery_time != (time_t) 0) time2iso(row->recovery_time, lengthof(row->recovery_time), - backup->recovery_time); + backup->recovery_time, false); else StrNCpy(row->recovery_time, "----", sizeof(row->recovery_time)); widths[cur] = Max(widths[cur], strlen(row->recovery_time)); diff --git a/src/utils/configuration.c b/src/utils/configuration.c index 1ef332ed..a7dbfab2 100644 --- a/src/utils/configuration.c +++ b/src/utils/configuration.c @@ -679,7 +679,7 @@ option_get_value(ConfigOption *opt) if (t > 0) { timestamp = palloc(100); - time2iso(timestamp, 100, t); + time2iso(timestamp, 100, t, false); } else timestamp = palloc0(1 /* just null termination */); @@ -1111,6 +1111,8 @@ parse_time(const char *value, time_t *result, bool utc_default) struct tm tm; char junk[2]; + char *local_tz = getenv("TZ"); + /* tmp = replace( value, !isalnum, ' ' ) */ tmp = pgut_malloc(strlen(value) + + 1); len = 0; @@ -1221,26 +1223,27 @@ parse_time(const char *value, time_t *result, bool utc_default) /* determine whether Daylight Saving Time is in effect */ tm.tm_isdst = -1; + /* set timezone to UTC */ + setenv("TZ", "UTC", 1); + + /* convert time to utc unix time */ *result = mktime(&tm); + /* return old timezone back if any */ + if (local_tz) + setenv("TZ", local_tz, 1); + else + unsetenv("TZ"); + /* adjust time zone */ if (tz_set || utc_default) { - time_t ltime = time(NULL); - struct tm *ptm = gmtime(<ime); - time_t gmt = mktime(ptm); - time_t offset; - /* UTC time */ *result -= tz; - - /* Get local time */ - ptm = localtime(<ime); - offset = ltime - gmt + (ptm->tm_isdst ? 3600 : 0); - - *result += offset; } + pg_free(local_tz); + return true; } @@ -1462,14 +1465,32 @@ convert_from_base_unit_u(uint64 base_value, int base_unit, * Convert time_t value to ISO-8601 format string. Always set timezone offset. */ void -time2iso(char *buf, size_t len, time_t time) +time2iso(char *buf, size_t len, time_t time, bool utc) { - struct tm *ptm = gmtime(&time); - time_t gmt = mktime(ptm); + struct tm *ptm = NULL; + time_t gmt; time_t offset; char *ptr = buf; + char *local_tz = getenv("TZ"); + /* set timezone to UTC if requested */ + if (utc) + setenv("TZ", "UTC", 1); + + ptm = gmtime(&time); + gmt = mktime(ptm); ptm = localtime(&time); + + if (utc) + { + /* return old timezone back if any */ + if (local_tz) + setenv("TZ", local_tz, 1); + else + unsetenv("TZ"); + } + + /* adjust timezone offset */ offset = time - gmt + (ptm->tm_isdst ? 3600 : 0); strftime(ptr, len, "%Y-%m-%d %H:%M:%S", ptm); @@ -1485,4 +1506,6 @@ time2iso(char *buf, size_t len, time_t time) snprintf(ptr, len - (ptr - buf), ":%02d", abs((int) offset % SECS_PER_HOUR) / SECS_PER_MINUTE); } + + pg_free(local_tz); } diff --git a/src/utils/configuration.h b/src/utils/configuration.h index 46b5d6c1..eea8c774 100644 --- a/src/utils/configuration.h +++ b/src/utils/configuration.h @@ -96,7 +96,7 @@ extern bool parse_int(const char *value, int *result, int flags, const char **hintmsg); extern bool parse_lsn(const char *value, XLogRecPtr *result); -extern void time2iso(char *buf, size_t len, time_t time); +extern void time2iso(char *buf, size_t len, time_t time, bool utc); extern void convert_from_base_unit(int64 base_value, int base_unit, int64 *value, const char **unit);