src: replace gmtime_r/localtime_r/strftime with GDateTime

gmtime_r/localtime_r are mostly used in combination with
strftime to format timestamps in libvirt. This can all
be replaced with GDateTime resulting in simpler code
that is also more portable.

There is some boundary condition problem in parsing POSIX
timezone offsets in GLib which tickles our test suite.
The test suite is hacked to avoid the problem. The upsteam
GLib bug report is

  https://gitlab.gnome.org/GNOME/glib/issues/1999

Reviewed-by: Pavel Hrdina <phrdina@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
This commit is contained in:
Daniel P. Berrangé 2020-01-09 14:07:15 +00:00
parent 7c828af858
commit 3caa28dc50
14 changed files with 96 additions and 186 deletions

View File

@ -26790,11 +26790,12 @@ virDomainGraphicsAuthDefFormatAttr(virBufferPtr buf,
def->passwd);
if (def->expires) {
char strbuf[100];
struct tm tmbuf, *tm;
tm = gmtime_r(&def->validTo, &tmbuf);
strftime(strbuf, sizeof(strbuf), "%Y-%m-%dT%H:%M:%S", tm);
virBufferAsprintf(buf, " passwdValidTo='%s'", strbuf);
g_autoptr(GDateTime) then = NULL;
g_autofree char *thenstr = NULL;
then = g_date_time_new_from_unix_utc(def->validTo);
thenstr = g_date_time_format(then, "%Y-%m-%dT%H:%M:%S");
virBufferAsprintf(buf, " passwdValidTo='%s'", thenstr);
}
if (def->connected)

View File

@ -943,16 +943,14 @@ libxlDomainAutoCoreDump(libxlDriverPrivatePtr driver,
virDomainObjPtr vm)
{
g_autoptr(libxlDriverConfig) cfg = libxlDriverConfigGet(driver);
time_t curtime = time(NULL);
char timestr[100];
struct tm time_info;
g_autoptr(GDateTime) now = g_date_time_new_now_local();
g_autofree char *nowstr = NULL;
char *dumpfile = NULL;
localtime_r(&curtime, &time_info);
strftime(timestr, sizeof(timestr), "%Y-%m-%d-%H:%M:%S", &time_info);
nowstr = g_date_time_format(now, "%Y-%m-%d-%H:%M:%S");
dumpfile = g_strdup_printf("%s/%s-%s", cfg->autoDumpDir, vm->def->name,
timestr);
nowstr);
/* Unlock virDomainObj while dumping core */
virObjectUnlock(vm);

View File

@ -6070,8 +6070,9 @@ qemuBuildClockArgStr(virDomainClockDefPtr def)
break;
case VIR_DOMAIN_CLOCK_OFFSET_VARIABLE: {
time_t now = time(NULL);
struct tm nowbits;
g_autoptr(GDateTime) now = g_date_time_new_now_utc();
g_autoptr(GDateTime) then = NULL;
g_autofree char *thenstr = NULL;
if (def->data.variable.basis == VIR_DOMAIN_CLOCK_BASIS_LOCALTIME) {
long localOffset;
@ -6094,8 +6095,8 @@ qemuBuildClockArgStr(virDomainClockDefPtr def)
def->data.variable.basis = VIR_DOMAIN_CLOCK_BASIS_UTC;
}
now += def->data.variable.adjustment;
gmtime_r(&now, &nowbits);
then = g_date_time_add_seconds(now, def->data.variable.adjustment);
thenstr = g_date_time_format(then, "%Y-%m-%dT%H:%M:%S");
/* when an RTC_CHANGE event is received from qemu, we need to
* have the adjustment used at domain start time available to
@ -6105,13 +6106,7 @@ qemuBuildClockArgStr(virDomainClockDefPtr def)
*/
def->data.variable.adjustment0 = def->data.variable.adjustment;
virBufferAsprintf(&buf, "base=%d-%02d-%02dT%02d:%02d:%02d",
nowbits.tm_year + 1900,
nowbits.tm_mon + 1,
nowbits.tm_mday,
nowbits.tm_hour,
nowbits.tm_min,
nowbits.tm_sec);
virBufferAsprintf(&buf, "base=%s", thenstr);
} break;
default:

View File

@ -4090,9 +4090,8 @@ getAutoDumpPath(virQEMUDriverPtr driver,
virDomainObjPtr vm)
{
g_autofree char *domname = virDomainDefGetShortName(vm->def);
char timestr[100];
struct tm time_info;
time_t curtime = time(NULL);
g_autoptr(GDateTime) now = g_date_time_new_now_local();
g_autofree char *nowstr = NULL;
g_autoptr(virQEMUDriverConfig) cfg = NULL;
if (!domname)
@ -4100,10 +4099,9 @@ getAutoDumpPath(virQEMUDriverPtr driver,
cfg = virQEMUDriverGetConfig(driver);
localtime_r(&curtime, &time_info);
strftime(timestr, sizeof(timestr), "%Y-%m-%d-%H:%M:%S", &time_info);
nowstr = g_date_time_format(now, "%Y-%m-%d-%H:%M:%S");
return g_strdup_printf("%s/%s-%s", cfg->autoDumpPath, domname, timestr);
return g_strdup_printf("%s/%s-%s", cfg->autoDumpPath, domname, nowstr);
}
static void

View File

@ -302,8 +302,8 @@ char *virTimeStringThen(unsigned long long when)
/**
* virTimeLocalOffsetFromUTC:
*
* This function is threadsafe, but is *not* async signal safe (due to
* gmtime_r() and mktime()).
* This function is threadsafe, but is *not* async signal safe
* due to use of GLib APIs.
*
* @offset: pointer to time_t that will be set to the difference
* between localtime and UTC in seconds (east of UTC is a
@ -314,34 +314,11 @@ char *virTimeStringThen(unsigned long long when)
int
virTimeLocalOffsetFromUTC(long *offset)
{
struct tm gmtimeinfo;
time_t current, utc;
g_autoptr(GDateTime) now = g_date_time_new_now_local();
GTimeSpan diff = g_date_time_get_utc_offset(now);
/* time() gives seconds since Epoch in current timezone */
if ((current = time(NULL)) == (time_t)-1) {
virReportSystemError(errno, "%s",
_("failed to get current system time"));
return -1;
}
/* treat current as if it were in UTC */
if (!gmtime_r(&current, &gmtimeinfo)) {
virReportSystemError(errno, "%s",
_("gmtime_r failed"));
return -1;
}
/* tell mktime to figure out itself whether or not DST is in effect */
gmtimeinfo.tm_isdst = -1;
/* mktime() also obeys current timezone rules */
if ((utc = mktime(&gmtimeinfo)) == (time_t)-1) {
virReportSystemError(errno, "%s",
_("mktime failed"));
return -1;
}
*offset = current - utc;
/* GTimeSpan measures microseconds, we want seconds */
*offset = diff / 1000000;
return 0;
}

View File

@ -47,12 +47,14 @@ long virGetSystemPageSize(void)
return 4096;
}
time_t time(time_t *t)
GDateTime *g_date_time_new_now_utc(void)
{
const time_t ret = 1234567890;
if (t)
*t = ret;
return ret;
return g_date_time_new_from_unix_utc(1234567890);
}
GDateTime *g_date_time_new_now_local(void)
{
return g_date_time_new_from_unix_local(1234567890);
}
bool

View File

@ -101,20 +101,12 @@ testTimeLocalOffset(const void *args)
static bool
isNearYearEnd(void)
{
time_t current = time(NULL);
struct tm timeinfo;
g_autoptr(GDateTime) now = g_date_time_new_now_local();
if (current == (time_t)-1) {
VIR_DEBUG("time() failed");
return false;
}
if (!localtime_r(&current, &timeinfo)) {
VIR_DEBUG("localtime_r() failed");
return false;
}
return (timeinfo.tm_mon == 0 && timeinfo.tm_mday == 1) ||
(timeinfo.tm_mon == 11 && timeinfo.tm_mday == 31);
return ((g_date_time_get_month(now) == 1 &&
g_date_time_get_day_of_month(now) == 1) ||
(g_date_time_get_month(now) == 12 &&
g_date_time_get_day_of_month(now) == 31));
}
@ -186,14 +178,21 @@ mymain(void)
/* test DST processing with timezones that always
* have DST in effect; what's more, cover a zone with
* with an unusual DST different than a usual one hour
*
* These tests originally used '0' as the first day,
* but changed to '1' due to GLib GTimeZone parsing bug:
* https://gitlab.gnome.org/GNOME/glib/issues/1999
*
* Once we depend on a new enough GLib, we can put then
* back to 0 again.
*/
TEST_LOCALOFFSET("VIR-00:30VID,0/00:00:00,365/23:59:59",
TEST_LOCALOFFSET("VIR-00:30VID,1/00:00:00,364/23:59:59",
((1 * 60) + 30) * 60);
TEST_LOCALOFFSET("VIR-02:30VID,0/00:00:00,365/23:59:59",
TEST_LOCALOFFSET("VIR-02:30VID,1/00:00:00,364/23:59:59",
((3 * 60) + 30) * 60);
TEST_LOCALOFFSET("VIR-02:30VID-04:30,0/00:00:00,365/23:59:59",
TEST_LOCALOFFSET("VIR-02:30VID-04:30,1/00:00:00,364/23:59:59",
((4 * 60) + 30) * 60);
TEST_LOCALOFFSET("VIR-12:00VID-13:00,0/00:00:00,365/23:59:59",
TEST_LOCALOFFSET("VIR-12:00VID-13:00,1/00:00:00,364/23:59:59",
((13 * 60) + 0) * 60);
if (!isNearYearEnd()) {
@ -209,11 +208,11 @@ mymain(void)
* tests, except on Dec 31 and Jan 1.
*/
TEST_LOCALOFFSET("VIR02:45VID00:45,0/00:00:00,365/23:59:59",
TEST_LOCALOFFSET("VIR02:45VID00:45,1/00:00:00,364/23:59:59",
-45 * 60);
TEST_LOCALOFFSET("VIR05:00VID04:00,0/00:00:00,365/23:59:59",
TEST_LOCALOFFSET("VIR05:00VID04:00,1/00:00:00,364/23:59:59",
((-4 * 60) + 0) * 60);
TEST_LOCALOFFSET("VIR11:00VID10:00,0/00:00:00,365/23:59:59",
TEST_LOCALOFFSET("VIR11:00VID10:00,1/00:00:00,364/23:59:59",
((-10 * 60) + 0) * 60);
}

View File

@ -719,9 +719,8 @@ cmdCheckpointList(vshControl *ctl,
char *doc = NULL;
virDomainCheckpointPtr checkpoint = NULL;
long long creation_longlong;
time_t creation_time_t;
char timestr[100];
struct tm time_info;
g_autoptr(GDateTime) then = NULL;
g_autofree gchar *thenstr = NULL;
bool tree = vshCommandOptBool(cmd, "tree");
bool name = vshCommandOptBool(cmd, "name");
bool from = vshCommandOptBool(cmd, "from");
@ -835,21 +834,16 @@ cmdCheckpointList(vshControl *ctl,
if (virXPathLongLong("string(/domaincheckpoint/creationTime)", ctxt,
&creation_longlong) < 0)
continue;
creation_time_t = creation_longlong;
if (creation_time_t != creation_longlong) {
vshError(ctl, "%s", _("time_t overflow"));
continue;
}
localtime_r(&creation_time_t, &time_info);
strftime(timestr, sizeof(timestr), "%Y-%m-%d %H:%M:%S %z",
&time_info);
then = g_date_time_new_from_unix_local(creation_longlong);
thenstr = g_date_time_format(then, "%Y-%m-%d %H:%M:%S %z");
if (parent) {
if (vshTableRowAppend(table, chk_name, timestr,
if (vshTableRowAppend(table, chk_name, thenstr,
NULLSTR_EMPTY(parent_chk), NULL) < 0)
goto cleanup;
} else {
if (vshTableRowAppend(table, chk_name, timestr, NULL) < 0)
if (vshTableRowAppend(table, chk_name, thenstr, NULL) < 0)
goto cleanup;
}
}

View File

@ -1534,17 +1534,13 @@ cmdDomTime(vshControl *ctl, const vshCmd *cmd)
goto cleanup;
if (pretty) {
char timestr[100];
time_t cur_time = seconds;
struct tm time_info;
g_autoptr(GDateTime) then = NULL;
g_autofree char *thenstr = NULL;
if (!gmtime_r(&cur_time, &time_info)) {
vshError(ctl, _("Unable to format time"));
goto cleanup;
}
strftime(timestr, sizeof(timestr), "%Y-%m-%d %H:%M:%S", &time_info);
then = g_date_time_new_from_unix_utc(seconds);
thenstr = g_date_time_format(then, "%Y-%m-%d %H:%M:%S");
vshPrint(ctl, _("Time: %s"), timestr);
vshPrint(ctl, _("Time: %s"), thenstr);
} else {
vshPrint(ctl, _("Time: %lld"), seconds);
}

View File

@ -5492,9 +5492,8 @@ static const vshCmdOptDef opts_screenshot[] = {
static char *
virshGenFileName(vshControl *ctl, virDomainPtr dom, const char *mime)
{
char timestr[100];
time_t cur_time;
struct tm time_info;
g_autoptr(GDateTime) now = g_date_time_new_now_local();
g_autofree char *nowstr = NULL;
const char *ext = NULL;
char *ret = NULL;
@ -5509,12 +5508,10 @@ virshGenFileName(vshControl *ctl, virDomainPtr dom, const char *mime)
ext = ".png";
/* add mime type here */
time(&cur_time);
localtime_r(&cur_time, &time_info);
strftime(timestr, sizeof(timestr), "%Y-%m-%d-%H:%M:%S", &time_info);
nowstr = g_date_time_format(now, "%Y-%m-%d-%H:%M:%S");
ret = g_strdup_printf("%s-%s%s", virDomainGetName(dom), timestr,
NULLSTR_EMPTY(ext));
ret = g_strdup_printf("%s-%s%s", virDomainGetName(dom),
nowstr, NULLSTR_EMPTY(ext));
return ret;
}

View File

@ -1433,11 +1433,10 @@ cmdNetworkDHCPLeases(vshControl *ctl, const vshCmd *cmd)
const char *typestr = NULL;
g_autofree char *cidr_format = NULL;
virNetworkDHCPLeasePtr lease = leases[i];
time_t expirytime_tmp = lease->expirytime;
struct tm ts;
char expirytime[32];
localtime_r(&expirytime_tmp, &ts);
strftime(expirytime, sizeof(expirytime), "%Y-%m-%d %H:%M:%S", &ts);
g_autoptr(GDateTime) then = g_date_time_new_from_unix_local(lease->expirytime);
g_autofree char *thenstr = NULL;
thenstr = g_date_time_format(then, "%Y-%m-%d %H:%M:%S");
if (lease->type == VIR_IP_ADDR_TYPE_IPV4)
typestr = "ipv4";
@ -1447,7 +1446,7 @@ cmdNetworkDHCPLeases(vshControl *ctl, const vshCmd *cmd)
cidr_format = g_strdup_printf("%s/%d", lease->ipaddr, lease->prefix);
if (vshTableRowAppend(table,
expirytime,
thenstr,
NULLSTR_MINUS(lease->mac),
NULLSTR_MINUS(typestr),
NULLSTR_MINUS(cidr_format),

View File

@ -1492,9 +1492,8 @@ cmdSnapshotList(vshControl *ctl, const vshCmd *cmd)
virDomainSnapshotPtr snapshot = NULL;
char *state = NULL;
long long creation_longlong;
time_t creation_time_t;
char timestr[100];
struct tm time_info;
g_autoptr(GDateTime) then = NULL;
g_autofree gchar *thenstr = NULL;
bool tree = vshCommandOptBool(cmd, "tree");
bool name = vshCommandOptBool(cmd, "name");
bool from = vshCommandOptBool(cmd, "from");
@ -1624,22 +1623,16 @@ cmdSnapshotList(vshControl *ctl, const vshCmd *cmd)
if (virXPathLongLong("string(/domainsnapshot/creationTime)", ctxt,
&creation_longlong) < 0)
continue;
creation_time_t = creation_longlong;
if (creation_time_t != creation_longlong) {
vshError(ctl, "%s", _("time_t overflow"));
continue;
}
localtime_r(&creation_time_t, &time_info);
strftime(timestr, sizeof(timestr), "%Y-%m-%d %H:%M:%S %z",
&time_info);
then = g_date_time_new_from_unix_local(creation_longlong);
thenstr = g_date_time_format(then, "%Y-%m-%d %H:%M:%S %z");
if (parent) {
if (vshTableRowAppend(table, snap_name, timestr, state,
if (vshTableRowAppend(table, snap_name, thenstr, state,
NULLSTR_EMPTY(parent_snap),
NULL) < 0)
goto cleanup;
} else {
if (vshTableRowAppend(table, snap_name, timestr, state,
if (vshTableRowAppend(table, snap_name, thenstr, state,
NULL) < 0)
goto cleanup;
}

View File

@ -69,40 +69,6 @@ vshAdmClientTransportToString(int transport)
return str ? _(str) : _("unknown");
}
/*
* vshAdmGetTimeStr:
*
* Produces string representation (local time) of @then
* (seconds since epoch UTC) using format 'YYYY-MM-DD HH:MM:SS+ZZZZ'.
*
* Returns 0 if conversion finished successfully, -1 in case of an error.
* Caller is responsible for freeing the string returned.
*/
static int
vshAdmGetTimeStr(vshControl *ctl, time_t then, char **result)
{
char *tmp = NULL;
struct tm timeinfo;
if (!localtime_r(&then, &timeinfo))
goto error;
if (VIR_ALLOC_N(tmp, VIRT_ADMIN_TIME_BUFLEN) < 0)
goto error;
if (strftime(tmp, VIRT_ADMIN_TIME_BUFLEN, "%Y-%m-%d %H:%M:%S%z",
&timeinfo) == 0) {
VIR_FREE(tmp);
goto error;
}
*result = tmp;
return 0;
error:
vshError(ctl, "%s", _("Timestamp string conversion failed"));
return -1;
}
/*
* vshAdmCatchDisconnect:
@ -646,19 +612,19 @@ cmdSrvClientsList(vshControl *ctl, const vshCmd *cmd)
goto cleanup;
for (i = 0; i < nclts; i++) {
g_autofree char *timestr = NULL;
g_autoptr(GDateTime) then = NULL;
g_autofree gchar *thenstr = NULL;
g_autofree char *idStr = NULL;
virAdmClientPtr client = clts[i];
id = virAdmClientGetID(client);
then = g_date_time_new_from_unix_local(virAdmClientGetTimestamp(client));
transport = virAdmClientGetTransport(client);
if (vshAdmGetTimeStr(ctl, virAdmClientGetTimestamp(client),
&timestr) < 0)
goto cleanup;
thenstr = g_date_time_format(then, "%Y-%m-%d %H:%M:%S%z");
idStr = g_strdup_printf("%llu", id);
if (vshTableRowAppend(table, idStr,
vshAdmClientTransportToString(transport),
timestr, NULL) < 0)
thenstr, NULL) < 0)
goto cleanup;
}
@ -714,7 +680,8 @@ cmdClientInfo(vshControl *ctl, const vshCmd *cmd)
size_t i;
unsigned long long id;
const char *srvname = NULL;
char *timestr = NULL;
g_autoptr(GDateTime) then = NULL;
g_autofree gchar *thenstr = NULL;
virAdmServerPtr srv = NULL;
virAdmClientPtr clnt = NULL;
virTypedParameterPtr params = NULL;
@ -739,12 +706,13 @@ cmdClientInfo(vshControl *ctl, const vshCmd *cmd)
goto cleanup;
}
if (vshAdmGetTimeStr(ctl, virAdmClientGetTimestamp(clnt), &timestr) < 0)
goto cleanup;
then = g_date_time_new_from_unix_local(virAdmClientGetTimestamp(clnt));
thenstr = g_date_time_format(then, "%Y-%m-%d %H:%M:%S%z");
/* this info is provided by the client object itself */
vshPrint(ctl, "%-15s: %llu\n", "id", virAdmClientGetID(clnt));
vshPrint(ctl, "%-15s: %s\n", "connection_time", timestr);
vshPrint(ctl, "%-15s: %s\n", "connection_time", thenstr);
vshPrint(ctl, "%-15s: %s\n", "transport",
vshAdmClientTransportToString(virAdmClientGetTransport(clnt)));
@ -760,7 +728,6 @@ cmdClientInfo(vshControl *ctl, const vshCmd *cmd)
virTypedParamsFree(params, nparams);
virAdmServerFree(srv);
virAdmClientFree(clnt);
VIR_FREE(timestr);
return ret;
}

View File

@ -2182,8 +2182,8 @@ vshOutputLogFile(vshControl *ctl, int log_level, const char *msg_format,
char *str = NULL;
size_t len;
const char *lvl = "";
time_t stTime;
struct tm stTm;
g_autoptr(GDateTime) now = g_date_time_new_now_local();
g_autofree gchar *nowstr = NULL;
if (ctl->log_fd == -1)
return;
@ -2193,15 +2193,9 @@ vshOutputLogFile(vshControl *ctl, int log_level, const char *msg_format,
*
* [YYYY.MM.DD HH:MM:SS SIGNATURE PID] LOG_LEVEL message
*/
time(&stTime);
localtime_r(&stTime, &stTm);
virBufferAsprintf(&buf, "[%d.%02d.%02d %02d:%02d:%02d %s %d] ",
(1900 + stTm.tm_year),
(1 + stTm.tm_mon),
stTm.tm_mday,
stTm.tm_hour,
stTm.tm_min,
stTm.tm_sec,
nowstr = g_date_time_format(now, "%Y.%m.%d %H:%M:%S");
virBufferAsprintf(&buf, "[%s %s %d] ",
nowstr,
ctl->progname,
(int) getpid());
switch (log_level) {