From: Alain Spineux Date: Thu, 20 Jan 2022 16:20:08 +0000 (+0100) Subject: Fix #8275 & #8622 Detect and report postgresql and system timezone mismatch X-Git-Tag: Beta-15.0.0~670 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ad533c82226ca87ab79626b2ad5de25ae7c6a3f2;p=thirdparty%2Fbacula.git Fix #8275 & #8622 Detect and report postgresql and system timezone mismatch - The first fix of #8275 was a mistake - This fix, detect and report an offset between PGSQL and the system via a Jmsg(WARNING) --- diff --git a/bacula/src/cats/postgresql.c b/bacula/src/cats/postgresql.c index 3c7ba650a..836876186 100644 --- a/bacula/src/cats/postgresql.c +++ b/bacula/src/cats/postgresql.c @@ -238,6 +238,104 @@ static int pgsql_check_database_encoding(JCR *jcr, BDB_POSTGRESQL *mdb) return 0; } +/* + * Get the time offset of the Postgress server in second, can be negative + * + * SELECT CURRENT_TIMESTAMP; + * 2022-01-20 07:49:35.19033-05 <== US/Eastern ==> return -5*3600 + * 2022-01-20 16:27:30.096223+03:30 <== Asia/Tehran ==> return 3*3600+30*60 + * 2022-01-20 12:59:33.42909+00 <= UTC ==> return 0 + */ +int get_utc_off(const char *st, int *offset) +{ + int err = 0; /* no error */ + const char *p = st; + int i = strlen(p) - 1; + int off = 0; + int off_multiplier = 60; + bool got_colon = false; + /* searching for [0-9:+-] starting from the end */ + while (i>=0) { + if (isdigit(p[i])) { + off = off + (p[i]-'0') * off_multiplier; + switch (off_multiplier) { + case 60: + off_multiplier = 600; break; + case 600: + off_multiplier = 3600; break; + case 3600: + off_multiplier = 36000; break; + case 36000: + off_multiplier = 0; break; + case 0: + err = 1; // too much digit + default: + break; + } + } else if (p[i] == ':') { + got_colon = true; + if (off_multiplier != 3600) { + err = 1; + } + } else if (p[i] == '+' || p[i] == '-' || (p[i] == ' ' && off_multiplier == 3600)) { + if (got_colon && off_multiplier != 0) { + err = 1; + } + if (off_multiplier == 3600) { + off *= 60; // there was no minutes (no colon) + } + if (p[i] == '-') { + off = - off; + } + *offset = off; + return err; + } + i--; + } + return 1; +} + +/* return current system time zone offset in second using strfstime() + */ +int get_system_utc_offset() +{ + char buf[128]; + time_t now; + struct tm tm; + + time(&now); + localtime_r(&now, &tm); + int l=strftime(buf, sizeof(buf), "%z", &tm); + /* expect "+hhmm" or "-hhmm" */ + if (l != 5) { + return 0; /* Unknown timezone on the system (Windows can do that) */ + } + int off = ((buf[1]-'0')*10+(buf[2]-'0'))*3600 + ((buf[3]-'0')*10+(buf[4]-'0'))*60; + if (buf[0] == '-') { + off = -off; + } + return off; +} + +static int pgsql_get_utc_offset(BDB_POSTGRESQL *mdb, int *offset) +{ + SQL_ROW row; + + if (!mdb->sql_query("SELECT CURRENT_TIMESTAMP;", QF_STORE_RESULT)) { + return M_ERROR; + } + + if ((row = mdb->sql_fetch_row()) == NULL) { + Mmsg1(mdb->errmsg, _("Can't retrieve time offset. Error fetching row: %s\n"), mdb->sql_strerror()); + return M_ERROR; + } + int err = get_utc_off(row[0], offset); + if (err) { + Mmsg1(mdb->errmsg, _("Can't retrieve time offset. Invalid time format: %s\n"), row[0]); + return M_WARNING; + } + return 0; +} /* * Now actually open the database. This can generate errors, * which are returned in the errmsg @@ -251,7 +349,6 @@ bool BDB_POSTGRESQL::bdb_open_database(JCR *jcr) char buf[10], *port; BDB_POSTGRESQL *mdb = this; int print_msg=0; /* 1: warning, 2: error, 3 fatal */ - const char *tz = get_timezone(); P(mutex); if (mdb->m_connected) { @@ -350,10 +447,22 @@ bool BDB_POSTGRESQL::bdb_open_database(JCR *jcr) sql_query("SET cursor_tuple_fraction=1"); sql_query("SET client_min_messages TO WARNING"); - /* For the Timezone to the current director one */ - if (tz && *tz) { - Mmsg(mdb->cmd, "SET timezone = '%s'", tz); - sql_query(mdb->cmd); + /* Check that PGSQL timezone match system timezone */ + { + int pgsql_offset = 0; + int sys_offset = get_system_utc_offset(); + int ret = pgsql_get_utc_offset(this, &pgsql_offset); + if (ret != 0) { + Jmsg(jcr, ret, 0, "%s", errmsg); + } else { + if (sys_offset != pgsql_offset) { + /* try again in case we would be just on Daylight Saving Time switch */ + sys_offset = get_system_utc_offset(); + } + if (sys_offset != pgsql_offset) { + Jmsg(jcr, M_WARNING, 0, _("Postgresql and sytem timezone mismatch detected\n")); + } + } } /* diff --git a/bacula/src/lib/bsys.c b/bacula/src/lib/bsys.c index 69e25a9e9..71454d2cb 100644 --- a/bacula/src/lib/bsys.c +++ b/bacula/src/lib/bsys.c @@ -1879,34 +1879,6 @@ int get_home_directories(const char *grpname, alist *dirs) return (dirs->size() > 0) ? 0 : -1; } -static pthread_once_t tz_control = PTHREAD_ONCE_INIT; -static void init_timezone() -{ -#ifdef HAVE_TZSET - tzset(); - -#elif defined(HAVE_WIN32) - _tzset(); -#endif -} - -const char *get_timezone() -{ - if (pthread_once(&tz_control, init_timezone) != 0) { - /* Should not fail, but tzname is initialized to GMT if needed */ - Dmsg0(0, "pthread_once() call for init_timezone() failed\n"); - } -#ifdef HAVE_TZSET - if (*tzname[1]) { - return tzname[1]; - - } else if (*tzname[0]) { - return tzname[0]; - } -#endif - return ""; -} - #ifdef TEST_PROGRAM #include "unittests.h"