提交 3ec12898 编写于 作者: D Daniel P. Berrange

Add internal APIs for dealing with time

The logging APIs need to be able to generate formatted timestamps
using only async signal safe functions. This rules out using
gmtime/localtime/malloc/gettimeday(!) and much more.

Introduce a new internal API which is async signal safe.

  virTimeMillisNowRaw replacement for gettimeofday. Uses clock_gettime
                      where available, otherwise falls back to the unsafe
                      gettimeofday

  virTimeFieldsNowRaw  replacements for gmtime(), convert a timestamp
  virTimeFieldsThenRaw into a broken out set of fields. No localtime()
                       replacement is provided, because converting to
                       local time is not practical with only async signal
                       safe APIs.

  virTimeStringNowRaw  replacements for strftime() which print a timestamp
  virTimeStringThenRaw into a string, using a pre-determined format, with
                       a fixed size buffer (VIR_TIME_STRING_BUFLEN)

For each of these there is also a version without the Raw postfix
which raises a full libvirt error. These versions are not async
signal safe

* src/Makefile.am, src/util/virtime.c, src/util/virtime.h: New files
* src/libvirt_private.syms: New APis
* configure.ac: Check for clock_gettime in -lrt
* tests/virtimetest.c, tests/Makefile.am: Test new APIs
上级 380110cf
......@@ -147,6 +147,16 @@ LIBS="$LIBS $LIB_PTHREAD $LIBMULTITHREAD"
AC_CHECK_FUNCS([pthread_mutexattr_init])
LIBS=$old_libs
old_LIBS=$LIBS
RT_LIBS=
LIBS="$LIBS $LIB_PTHREAD -lrt"
AC_CHECK_FUNC([clock_gettime],[
AC_DEFINE([HAVE_CLOCK_GETTIME],[],[Defined if clock_gettime() exists in librt.so])
RT_LIBS=-lrt
])
LIBS=$old_libs
AC_SUBST(RT_LIBS)
dnl Availability of various common headers (non-fatal if missing).
AC_CHECK_HEADERS([pwd.h paths.h regex.h sys/un.h \
sys/poll.h syslog.h mntent.h net/ethernet.h linux/magic.h \
......
......@@ -134,6 +134,7 @@ src/util/virnodesuspend.c
src/util/virpidfile.c
src/util/virsocketaddr.c
src/util/virterror.c
src/util/virtime.c
src/util/xml.c
src/vbox/vbox_MSCOMGlue.c
src/vbox/vbox_XPCOMCGlue.c
......
......@@ -98,7 +98,8 @@ UTIL_SOURCES = \
util/virnetdevtap.h util/virnetdevtap.c \
util/virnetdevveth.h util/virnetdevveth.c \
util/virnetdevvportprofile.h util/virnetdevvportprofile.c \
util/virsocketaddr.h util/virsocketaddr.c
util/virsocketaddr.h util/virsocketaddr.c \
util/virtime.h util/virtime.c
EXTRA_DIST += $(srcdir)/util/virkeymaps.h $(srcdir)/util/keymaps.csv \
$(srcdir)/util/virkeycode-mapgen.py
......@@ -562,7 +563,8 @@ libvirt_util_la_SOURCES = \
libvirt_util_la_CFLAGS = $(CAPNG_CFLAGS) $(YAJL_CFLAGS) $(LIBNL_CFLAGS) \
$(AM_CFLAGS) $(AUDIT_CFLAGS) $(DEVMAPPER_CFLAGS)
libvirt_util_la_LIBADD = $(CAPNG_LIBS) $(YAJL_LIBS) $(LIBNL_LIBS) \
$(THREAD_LIBS) $(AUDIT_LIBS) $(DEVMAPPER_LIBS)
$(THREAD_LIBS) $(AUDIT_LIBS) $(DEVMAPPER_LIBS) \
$(RT_LIBS)
noinst_LTLIBRARIES += libvirt_conf.la
......@@ -1501,6 +1503,7 @@ libvirt_lxc_LDFLAGS = $(WARN_CFLAGS) $(AM_LDFLAGS)
libvirt_lxc_LDADD = $(CAPNG_LIBS) $(YAJL_LIBS) \
$(LIBXML_LIBS) $(NUMACTL_LIBS) $(THREAD_LIBS) \
$(LIBNL_LIBS) $(AUDIT_LIBS) $(DEVMAPPER_LIBS) \
$(RT_LIBS) \
../gnulib/lib/libgnu.la
if WITH_DTRACE
libvirt_lxc_LDADD += probes.o
......
......@@ -1387,6 +1387,17 @@ virKeycodeSetTypeFromString;
virKeycodeValueFromString;
virKeycodeValueTranslate;
# virtime.h
virTimeMillisNow;
virTimeFieldsNow;
virTimeFieldsThen;
virTimeStringNow;
virTimeStringThen;
virTimeStringNewNow;
virTimeStringNewThen;
# xml.h
virXMLParseHelper;
virXMLPropString;
......
/*
* virtime.c: Time handling functions
*
* Copyright (C) 2006-2011 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Daniel P. Berrange <berrange@redhat.com>
*
* The intent is that this file provides a set of time APIs which
* are async signal safe, to allow use in between fork/exec eg by
* the logging code.
*
* The reality is that wsnprintf is technically unsafe. We ought
* to roll out our int -> str conversions to avoid this.
*
* We do *not* use regular libvirt error APIs for most of the code,
* since those are not async signal safe, and we dont want logging
* APIs generating timestamps to blow away real errors
*/
#include <config.h>
#include <stdio.h>
#ifndef HAVE_CLOCK_GETTIME
# include <sys/time.h>
#endif
#include "virtime.h"
#include "util.h"
#include "memory.h"
#include "virterror_internal.h"
#define VIR_FROM_THIS VIR_FROM_NONE
/* We prefer clock_gettime if available because that is officially
* async signal safe according to POSIX. Many platforms lack it
* though, so fallback to gettimeofday everywhere else
*/
/**
* virTimeMillisNowRaw:
* @now: filled with current time in milliseconds
*
* Retrieves the current system time, in milliseconds since the
* epoch
*
* Returns 0 on success, -1 on error with errno set
*/
int virTimeMillisNowRaw(unsigned long long *now)
{
#ifdef HAVE_CLOCK_GETTIME
struct timespec ts;
if (clock_gettime(CLOCK_REALTIME, &ts) < 0)
return -1;
*now = (ts.tv_sec * 1000ull) + (ts.tv_nsec / (1000ull * 1000ull));
#else
struct timeval tv;
if (gettimeofday(&tv, NULL) < 0)
return -1;
*now = (tv.tv_sec * 1000ull) + (tv.tv_usec / 1000ull);
#endif
return 0;
}
/**
* virTimeFieldsNowRaw:
* @fields: filled with current time fields
*
* Retrieves the current time, in broken-down field format.
* The time is always in UTC.
*
* Returns 0 on success, -1 on error with errno set
*/
int virTimeFieldsNowRaw(struct tm *fields)
{
unsigned long long now;
if (virTimeMillisNowRaw(&now) < 0)
return -1;
return virTimeFieldsThenRaw(now, fields);
}
#define SECS_PER_HOUR (60 * 60)
#define SECS_PER_DAY (SECS_PER_HOUR * 24)
#define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
#define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400))
const unsigned short int __mon_yday[2][13] = {
/* Normal years. */
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
/* Leap years. */
{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
};
/**
* virTimeFieldsThenRaw:
* @when: the time to convert in milliseconds
* @fields: filled with time @when fields
*
* Converts the timestamp @when into broken-down field format.
* Time time is always in UTC
*
* Returns 0 on success, -1 on error with errno set
*/
int virTimeFieldsThenRaw(unsigned long long when, struct tm *fields)
{
/* This code is taken from GLibC under terms of LGPLv2+ */
long int days, rem, y;
const unsigned short int *ip;
unsigned long long whenSecs = when / 1000ull;
unsigned int offset = 0; /* We hardcoded GMT */
days = whenSecs / SECS_PER_DAY;
rem = whenSecs % SECS_PER_DAY;
rem += offset;
while (rem < 0) {
rem += SECS_PER_DAY;
--days;
}
while (rem >= SECS_PER_DAY) {
rem -= SECS_PER_DAY;
++days;
}
fields->tm_hour = rem / SECS_PER_HOUR;
rem %= SECS_PER_HOUR;
fields->tm_min = rem / 60;
fields->tm_sec = rem % 60;
/* January 1, 1970 was a Thursday. */
fields->tm_wday = (4 + days) % 7;
if (fields->tm_wday < 0)
fields->tm_wday += 7;
y = 1970;
while (days < 0 || days >= (__isleap (y) ? 366 : 365)) {
/* Guess a corrected year, assuming 365 days per year. */
long int yg = y + days / 365 - (days % 365 < 0);
/* Adjust DAYS and Y to match the guessed year. */
days -= ((yg - y) * 365
+ LEAPS_THRU_END_OF (yg - 1)
- LEAPS_THRU_END_OF (y - 1));
y = yg;
}
fields->tm_year = y - 1900;
fields->tm_yday = days;
ip = __mon_yday[__isleap(y)];
for (y = 11; days < (long int) ip[y]; --y)
continue;
days -= ip[y];
fields->tm_mon = y;
fields->tm_mday = days + 1;
return 0;
}
/**
* virTimeStringNowRaw:
* @buf: a buffer at least VIR_TIME_STRING_BUFLEN in length
*
* Initializes @buf to contain a formatted timestamp
* corresponding to the current time.
*
* Returns 0 on success, -1 on error
*/
int virTimeStringNowRaw(char *buf)
{
unsigned long long now;
if (virTimeMillisNowRaw(&now) < 0)
return -1;
return virTimeStringThenRaw(now, buf);
}
/**
* virTimeStringThenRaw:
* @when: the time to format in milliseconds
* @buf: a buffer at least VIR_TIME_STRING_BUFLEN in length
*
* Initializes @buf to contain a formatted timestamp
* corresponding to the time @when.
*
* Returns 0 on success, -1 on error
*/
int virTimeStringThenRaw(unsigned long long when, char *buf)
{
struct tm fields;
if (virTimeFieldsThenRaw(when, &fields) < 0)
return -1;
fields.tm_year += 1900;
fields.tm_mon += 1;
if (snprintf(buf, VIR_TIME_STRING_BUFLEN,
"%4d-%02d-%02d %02d:%02d:%02d.%03d+0000",
fields.tm_year, fields.tm_mon, fields.tm_mday,
fields.tm_hour, fields.tm_min, fields.tm_sec,
(int) (when % 1000)) >= VIR_TIME_STRING_BUFLEN) {
errno = ERANGE;
return -1;
}
return 0;
}
/**
* virTimeMillisNow:
* @now: filled with current time in milliseconds
*
* Retrieves the current system time, in milliseconds since the
* epoch
*
* Returns 0 on success, -1 on error with error reported
*/
int virTimeMillisNow(unsigned long long *now)
{
if (virTimeMillisNowRaw(now) < 0) {
virReportSystemError(errno, "%s",
_("Unable to get current time"));
return -1;
}
return 0;
}
/**
* virTimeFieldsNowRaw:
* @fields: filled with current time fields
*
* Retrieves the current time, in broken-down field format.
* The time is always in UTC.
*
* Returns 0 on success, -1 on error with errno reported
*/
int virTimeFieldsNow(struct tm *fields)
{
unsigned long long now;
if (virTimeMillisNow(&now) < 0)
return -1;
return virTimeFieldsThen(now, fields);
}
/**
* virTimeFieldsThen:
* @when: the time to convert in milliseconds
* @fields: filled with time @when fields
*
* Converts the timestamp @when into broken-down field format.
* Time time is always in UTC
*
* Returns 0 on success, -1 on error with error reported
*/
int virTimeFieldsThen(unsigned long long when, struct tm *fields)
{
if (virTimeFieldsThenRaw(when, fields) < 0) {
virReportSystemError(errno, "%s",
_("Unable to break out time format"));
return -1;
}
return 0;
}
/**
* virTimeStringNow:
*
* Creates a string containing a formatted timestamp
* corresponding to the current time.
*
* This function is not async signal safe
*
* Returns a formatted allocated string, or NULL on error
*/
char *virTimeStringNow(void)
{
char *ret;
if (VIR_ALLOC_N(ret, VIR_TIME_STRING_BUFLEN) < 0) {
virReportOOMError();
return NULL;
}
if (virTimeStringNowRaw(ret) < 0) {
virReportSystemError(errno, "%s",
_("Unable to format time"));
VIR_FREE(ret);
return NULL;
}
return ret;
}
/**
* virTimeStringThen:
* @when: the time to format in milliseconds
*
* Creates a string containing a formatted timestamp
* corresponding to the time @when.
*
* This function is not async signal safe
*
* Returns a formatted allocated string, or NULL on error
*/
char *virTimeStringThen(unsigned long long when)
{
char *ret;
if (VIR_ALLOC_N(ret, VIR_TIME_STRING_BUFLEN) < 0) {
virReportOOMError();
return NULL;
}
if (virTimeStringThenRaw(when, ret) < 0) {
virReportSystemError(errno, "%s",
_("Unable to format time"));
VIR_FREE(ret);
return NULL;
}
return ret;
}
/*
* virtime.h: Time handling functions
*
* Copyright (C) 2006-2011 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Daniel P. Berrange <berrange@redhat.com>
*/
#ifndef __VIR_TIME_H__
# define __VIR_TIME_H__
# include <time.h>
# include "internal.h"
/* The format string we intend to use is:
*
* Yr Mon Day Hour Min Sec Ms TZ
* %4d-%02d-%02d %02d:%02d:%02d.%03d+0000
*
*/
# define VIR_TIME_STRING_BUFLEN \
(4 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 3 + 5 + 1)
/* Yr Mon Day Hour Min Sec Ms TZ NULL */
/* These APIs are async signal safe and return -1, setting
* errno on failure */
int virTimeMillisNowRaw(unsigned long long *now)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK;
int virTimeFieldsNowRaw(struct tm *fields)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK;
int virTimeFieldsThenRaw(unsigned long long when, struct tm *fields)
ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK;
int virTimeStringNowRaw(char *buf)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK;
int virTimeStringThenRaw(unsigned long long when, char *buf)
ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK;
/* These APIs are *not* async signal safe and return -1,
* raising a libvirt error on failure
*/
int virTimeMillisNow(unsigned long long *now)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK;
int virTimeFieldsNow(struct tm *fields)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK;
int virTimeFieldsThen(unsigned long long when, struct tm *fields)
ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK;
char *virTimeStringNow(void);
char *virTimeStringThen(unsigned long long when);
#endif
......@@ -36,6 +36,7 @@ virnetmessagetest
virnetsockettest
virnettlscontexttest
virshtest
virtimetest
vmx2xmltest
xencapstest
xmconfigtest
......
......@@ -96,7 +96,8 @@ check_PROGRAMS = virshtest conftest sockettest \
nodeinfotest qparamtest virbuftest \
commandtest commandhelper seclabeltest \
hashtest virnetmessagetest virnetsockettest ssh \
utiltest virnettlscontexttest shunloadtest
utiltest virnettlscontexttest shunloadtest \
virtimetest
check_LTLIBRARIES = libshunload.la
......@@ -217,6 +218,7 @@ TESTS = virshtest \
virnetmessagetest \
virnetsockettest \
virnettlscontexttest \
virtimetest \
shunloadtest \
utiltest \
$(test_scripts)
......@@ -495,6 +497,11 @@ else
EXTRA_DIST += pkix_asn1_tab.c
endif
virtimetest_SOURCES = \
virtimetest.c testutils.h testutils.c
virtimetest_CFLAGS = -Dabs_builddir="\"$(abs_builddir)\"" $(AM_CFLAGS)
virtimetest_LDADD = ../src/libvirt-net-rpc.la $(LDADDS)
seclabeltest_SOURCES = \
seclabeltest.c
......
/*
* Copyright (C) 2011 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Daniel P. Berrange <berrange@redhat.com>
*/
#include <config.h>
#include <stdlib.h>
#include <signal.h>
#include "testutils.h"
#include "util.h"
#include "virterror_internal.h"
#include "memory.h"
#include "logging.h"
#include "virtime.h"
#define VIR_FROM_THIS VIR_FROM_RPC
struct testTimeFieldsData {
unsigned long long when;
struct tm fields;
};
static int testTimeFields(const void *args)
{
const struct testTimeFieldsData *data = args;
struct tm actual;
if (virTimeFieldsThen(data->when, &actual) < 0)
return -1;
#define COMPARE(field) \
do { \
if (data->fields.field != actual.field) { \
VIR_DEBUG("Expect " #field " %d got %d", \
data->fields.field, actual.field); \
return -1; \
} \
} while (0)
/* tm_year value 0 is based off epoch 1900 */
actual.tm_year += 1900;
/* tm_mon is range 0-11, but we want 1-12 */
actual.tm_mon += 1;
COMPARE(tm_year);
COMPARE(tm_mon);
COMPARE(tm_mday);
COMPARE(tm_hour);
COMPARE(tm_min);
COMPARE(tm_sec);
return 0;
}
static int
mymain(void)
{
int ret = 0;
signal(SIGPIPE, SIG_IGN);
#define TEST_FIELDS(ts, year, mon, day, hour, min, sec) \
do { \
struct testTimeFieldsData data = { \
.when = ts, \
.fields = { \
.tm_year = year, \
.tm_mon = mon, \
.tm_mday = day, \
.tm_hour = hour, \
.tm_min = min, \
.tm_sec = sec, \
.tm_wday = 0, \
.tm_yday = 0, \
.tm_isdst = 0, \
}, \
}; \
if (virtTestRun("Test fields " #ts " " #year " ", 1, testTimeFields, &data) < 0) \
ret = -1; \
} while (0)
TEST_FIELDS( 0ull, 1970, 1, 1, 0, 0, 0);
TEST_FIELDS( 5000ull, 1970, 1, 1, 0, 0, 5);
TEST_FIELDS( 3605000ull, 1970, 1, 1, 1, 0, 5);
TEST_FIELDS( 86405000ull, 1970, 1, 2, 0, 0, 5);
TEST_FIELDS( 31536000000ull, 1971, 1, 1, 0, 0, 0);
TEST_FIELDS( 30866399000ull, 1970, 12, 24, 5, 59, 59);
TEST_FIELDS( 123465599000ull, 1973, 11, 29, 23, 59, 59);
TEST_FIELDS( 155001599000ull, 1974, 11, 29, 23, 59, 59);
TEST_FIELDS( 186537599000ull, 1975, 11, 29, 23, 59, 59);
TEST_FIELDS( 344390399000ull, 1980, 11, 29, 23, 59, 59);
TEST_FIELDS(1203161493000ull, 2008, 2, 16, 11, 31, 33);
TEST_FIELDS(1234567890000ull, 2009, 2, 13, 23, 31, 30);
TEST_FIELDS(1322524800000ull, 2011, 11, 29, 0, 0, 0);
TEST_FIELDS(1322611199000ull, 2011, 11, 29, 23, 59, 59);
TEST_FIELDS(2147483648000ull, 2038, 1, 19, 3, 14, 8);
return (ret==0 ? EXIT_SUCCESS : EXIT_FAILURE);
}
VIRT_TEST_MAIN(mymain)
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册