From 536646258cdf77d28015493c460022e6a239b4f3 Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Thu, 26 Apr 2018 15:56:10 -0400 Subject: [PATCH] Report community / route expiration times in UTC * add utility methods for timespec math * add method to convert between CLOCK_* --- common/tr_comm.c | 15 +++++- common/tr_comm_encoders.c | 19 +++---- common/tr_util.c | 125 +++++++++++++++++++++++++++++++++++++++++++--- include/tr_comm.h | 1 + include/tr_util.h | 8 ++- include/trp_route.h | 1 + trp/trp_route.c | 13 +++++ trp/trp_route_encoders.c | 9 ++-- trp/trps.c | 7 +-- 9 files changed, 174 insertions(+), 24 deletions(-) diff --git a/common/tr_comm.c b/common/tr_comm.c index f8f91df..9f173c6 100644 --- a/common/tr_comm.c +++ b/common/tr_comm.c @@ -40,9 +40,10 @@ #include #include #include +#include #include #include - +#include static int tr_comm_destructor(void *obj) { @@ -1017,6 +1018,18 @@ struct timespec *tr_comm_memb_get_expiry(TR_COMM_MEMB *memb) return memb->expiry; } +/** + * Get the expiration according to the realtime clock + * + * @param memb + * @param result space to store the result + * @return pointer to the result, or null on error + */ +struct timespec *tr_comm_memb_get_expiry_realtime(TR_COMM_MEMB *memb, struct timespec *result) +{ + return tr_clock_convert(TRP_CLOCK, memb->expiry, CLOCK_REALTIME, result); +} + int tr_comm_memb_is_expired(TR_COMM_MEMB *memb, struct timespec *curtime) { tr_debug("tr_comm_memb_is_expired: (cur->tv_sec>memb->expiry->tv_sec)=(%u > %u)=%s", diff --git a/common/tr_comm_encoders.c b/common/tr_comm_encoders.c index 2655fe5..798c636 100644 --- a/common/tr_comm_encoders.c +++ b/common/tr_comm_encoders.c @@ -40,19 +40,20 @@ static json_t *expiry_to_json_string(TR_COMM_MEMB *memb) { - struct timespec ts_zero = {0, 0}; + struct timespec ts = {0}; /* initialization to zero is important */ char *s = NULL; json_t *jstr = NULL; - if (tr_cmp_timespec(tr_comm_memb_get_expiry(memb), &ts_zero) == 0) { - s = strdup(""); - } else { - s = timespec_to_str(tr_comm_memb_get_expiry(memb)); - } + if (tr_cmp_timespec(tr_comm_memb_get_expiry(memb), &ts) > 0) { + if (tr_comm_memb_get_expiry_realtime(memb, &ts) == NULL) + s = strdup("error"); + else + s = timespec_to_str(&ts); - if (s) { - jstr = json_string(s); - free(s); + if (s) { + jstr = json_string(s); + free(s); + } } return jstr; diff --git a/common/tr_util.c b/common/tr_util.c index ef85776..f45ca2b 100644 --- a/common/tr_util.c +++ b/common/tr_util.c @@ -51,9 +51,16 @@ void tr_bin_to_hex(const unsigned char * bin, size_t bin_len, } } -/* Returns 0 if ts1==ts2, <0 if ts1= if ts1>ts2. - * Assumes that tv_nsec <= 1e9. */ -int tr_cmp_timespec(struct timespec *ts1, struct timespec *ts2) +/** + * Compare two timespecs + * + * Assumes tv_nsec <= 1e9 + * + * @param ts1 + * @param ts2 + * @return 0 if ts1==ts2, -1 if ts1ts2. + */ +int tr_cmp_timespec(const struct timespec *ts1, const struct timespec *ts2) { if (ts1->tv_sec > ts2->tv_sec) return 1; @@ -73,26 +80,132 @@ int tr_cmp_timespec(struct timespec *ts1, struct timespec *ts2) } /** + * Compute ts1 + ts2 + * + * @param ts1 + * @param ts2 + * @param sum ts1 + ts2 + * @return 0 on success, nonzero on error + */ +int tr_add_timespec(const struct timespec *ts1, const struct timespec *ts2, struct timespec *sum) +{ + const time_t ONE_BILLION = 1000000000; + + if (!ts1 || !ts2 || !sum) + return -1; + + /* would be nice to do range checking, but I don't know a portable way to get the + * max value of a time_t. Figure that nsec <= 1e9 and seconds are unlikely to go off + * too close to infinity, so overflow is unlikely */ + sum->tv_nsec = ts1->tv_nsec + ts2->tv_nsec; + sum->tv_sec = ts1->tv_sec + ts2->tv_sec; + + /* make sure that we have no more than a second worth of nsec */ + while (sum->tv_nsec >= ONE_BILLION) { + sum->tv_nsec -= ONE_BILLION; + sum->tv_sec += 1; + } + + return 0; +} + +/** + * Compute ts1 - ts2 + * + * Allows negative results, which will be represented as a negative tv_sec. + * The returned tv_nsec is always positive and less than 1e9. + * + * (The value represented is tv_sec + tv_nsec/1e9 seconds - this is a little + * counterintuitive when tv_sec is positive. E.g., -1.5 seconds is represented + * as {-2, 500000000).) + * + * @param ts1 + * @param ts2 + * @param diff ts1 - ts2 + * @return 0 on success, nonzero on error + */ +int tr_sub_timespec(const struct timespec *ts1, const struct timespec *ts2, struct timespec *diff) +{ + const time_t ONE_BILLION = 1000000000; + struct timespec ts1_copy = {0}; + struct timespec ts2_copy = {0}; + + if (!ts1 || !ts2 || !diff) + return -1; + + ts1_copy = *ts1; + ts2_copy = *ts2; + + while (ts2_copy.tv_nsec > ts1_copy.tv_nsec) { + /* Reduce ts2 by one second worth of nsec, balanced by removing a second + * from ts1. Repeat until ts2->tv_nsec <= ts1->tv_nsec. */ + ts2_copy.tv_nsec -= ONE_BILLION; + ts1_copy.tv_sec -= 1; + } + + diff->tv_nsec = ts1_copy.tv_nsec - ts2_copy.tv_nsec; /* >= 0 */ + diff->tv_sec = ts1_copy.tv_sec - ts2_copy.tv_sec; /* sign indeterminate */ + + /* make sure we have no more than 1 sec worth of nsec */ + while (diff->tv_nsec > ONE_BILLION) { + diff->tv_nsec -= ONE_BILLION; + diff->tv_sec += 1; + } + + return 0; +} + +/** * Convert a struct timespec to a string representation * @param ts * @return */ -char *timespec_to_str(struct timespec *ts) +char *timespec_to_str(const struct timespec *ts) { struct tm tm; char *s=NULL; - if (localtime_r(&(ts->tv_sec), &tm)==NULL) + if (gmtime_r(&(ts->tv_sec), &tm)==NULL) return NULL; s=malloc(40); /* long enough to contain strftime result */ if (s==NULL) return NULL; - if (strftime(s, 40, "%F %T", &tm)==0) { + if (strftime(s, 40, "%F %T UTC", &tm)==0) { free(s); return NULL; } return s; } +/** + * Convert a time from one clock to another + * + * Because this involves reading each clock, it is not exact. + * + * @param from clock to convert from + * @param when time to convert, measured on the 'from' clock + * @param to clock to convert to + * @param dst destination, measured on the 'to' clock + * @return dst or null on error + */ +struct timespec *tr_clock_convert(clockid_t from, const struct timespec *when, + clockid_t to, struct timespec *dst) +{ + struct timespec now_from = {0}; + struct timespec diff = {0}; /* difference between when and now_from */ + struct timespec now_to = {0}; + + if ((clock_gettime(from, &now_from) != 0) + || (clock_gettime(to, &now_to) != 0)) { + return NULL; + } + if (tr_sub_timespec(when, &now_from, &diff) != 0) { + return NULL; + } + if (tr_add_timespec(&now_to, &diff, dst) != 0) { + return NULL; + } + return dst; +} \ No newline at end of file diff --git a/include/tr_comm.h b/include/tr_comm.h index 6228c1f..c65d82a 100644 --- a/include/tr_comm.h +++ b/include/tr_comm.h @@ -155,6 +155,7 @@ void tr_comm_memb_set_interval(TR_COMM_MEMB *memb, unsigned int interval); unsigned int tr_comm_memb_get_interval(TR_COMM_MEMB *memb); void tr_comm_memb_set_expiry(TR_COMM_MEMB *memb, struct timespec *time); struct timespec *tr_comm_memb_get_expiry(TR_COMM_MEMB *memb); +struct timespec *tr_comm_memb_get_expiry_realtime(TR_COMM_MEMB *memb, struct timespec *result); int tr_comm_memb_is_expired(TR_COMM_MEMB *memb, struct timespec *curtime); void tr_comm_memb_set_triggered(TR_COMM_MEMB *memb, int trig); int tr_comm_memb_is_triggered(TR_COMM_MEMB *memb); diff --git a/include/tr_util.h b/include/tr_util.h index bed0482..9bf7d8e 100644 --- a/include/tr_util.h +++ b/include/tr_util.h @@ -40,7 +40,11 @@ /* NB, tr_bin_to_hex() is also prototyped in trust_router/tr_dh.h */ TR_EXPORT void tr_bin_to_hex(const unsigned char * bin, size_t binlen, char * hex_out, size_t hex_len); -TR_EXPORT int tr_cmp_timespec(struct timespec *ts1, struct timespec *ts2); -char *timespec_to_str(struct timespec *ts); +TR_EXPORT int tr_cmp_timespec(const struct timespec *ts1, const struct timespec *ts2); +int tr_add_timespec(const struct timespec *ts1, const struct timespec *ts2, struct timespec *sum); +int tr_sub_timespec(const struct timespec *ts1_copy, const struct timespec *ts2_copy, struct timespec *diff); +char *timespec_to_str(const struct timespec *ts); +struct timespec *tr_clock_convert(clockid_t from, const struct timespec *when, + clockid_t to, struct timespec *dst); #endif /* TR_UTIL_H */ diff --git a/include/trp_route.h b/include/trp_route.h index d159861..aff0ba0 100644 --- a/include/trp_route.h +++ b/include/trp_route.h @@ -78,6 +78,7 @@ void trp_route_set_interval(TRP_ROUTE *entry, int interval); int trp_route_get_interval(TRP_ROUTE *entry); void trp_route_set_expiry(TRP_ROUTE *entry, struct timespec *exp); struct timespec *trp_route_get_expiry(TRP_ROUTE *entry); +struct timespec *trp_route_get_expiry_realtime(TRP_ROUTE *comm, struct timespec *result); void trp_route_set_local(TRP_ROUTE *entry, int local); int trp_route_is_local(TRP_ROUTE *entry); void trp_route_set_triggered(TRP_ROUTE *entry, int trig); diff --git a/trp/trp_route.c b/trp/trp_route.c index 1334032..6178d97 100644 --- a/trp/trp_route.c +++ b/trp/trp_route.c @@ -45,6 +45,7 @@ #include #include #include +#include /* Note: be careful mixing talloc with glib. */ @@ -225,6 +226,18 @@ struct timespec *trp_route_get_expiry(TRP_ROUTE *entry) return entry->expiry; } +/** + * Get the expiration according to the realtime clock + * + * @param entry + * @param result space to store the result + * @return pointer to the result, or null on error + */ +struct timespec *trp_route_get_expiry_realtime(TRP_ROUTE *entry, struct timespec *result) +{ + return tr_clock_convert(TRP_CLOCK, entry->expiry, CLOCK_REALTIME, result); +} + void trp_route_set_local(TRP_ROUTE *entry, int local) { entry->local=local; diff --git a/trp/trp_route_encoders.c b/trp/trp_route_encoders.c index 8e1809b..c954a81 100644 --- a/trp/trp_route_encoders.c +++ b/trp/trp_route_encoders.c @@ -84,12 +84,15 @@ char *trp_route_to_str(TALLOC_CTX *mem_ctx, TRP_ROUTE *entry, const char *sep) /* helper */ static json_t *expiry_to_json_string(TRP_ROUTE *route) { - struct timespec ts_zero = {0, 0}; + struct timespec ts = {0}; /* initialization to zero is important */ char *s = NULL; json_t *jstr = NULL; - if (tr_cmp_timespec(trp_route_get_expiry(route), &ts_zero) > 0) { - s = timespec_to_str(trp_route_get_expiry(route)); + if (tr_cmp_timespec(trp_route_get_expiry(route), &ts) > 0) { + if (trp_route_get_expiry_realtime(route, &ts) == NULL) + s = strdup("error"); + else + s = timespec_to_str(&ts); if (s) { jstr = json_string(s); diff --git a/trp/trps.c b/trp/trps.c index 573f0a3..f2d3b9d 100644 --- a/trp/trps.c +++ b/trp/trps.c @@ -1179,6 +1179,7 @@ TRP_RC trps_sweep_ctable(TRPS_INSTANCE *trps) { TALLOC_CTX *tmp_ctx=talloc_new(NULL); struct timespec sweep_time={0,0}; + struct timespec tmp = {0}; TR_COMM_MEMB *memb=NULL; TR_COMM_ITER *iter=NULL; TRP_RC rc=TRP_ERROR; @@ -1211,7 +1212,7 @@ TRP_RC trps_sweep_ctable(TRPS_INSTANCE *trps) tr_comm_memb_get_realm_id(memb)->len, tr_comm_memb_get_realm_id(memb)->buf, tr_comm_get_id(tr_comm_memb_get_comm(memb))->len, tr_comm_get_id(tr_comm_memb_get_comm(memb))->buf, tr_comm_memb_get_origin(memb)->len, tr_comm_memb_get_origin(memb)->buf, - timespec_to_str(tr_comm_memb_get_expiry(memb))); + timespec_to_str(tr_comm_memb_get_expiry_realtime(memb, &tmp))); tr_comm_table_remove_memb(trps->ctable, memb); tr_comm_memb_free(memb); } else { @@ -1219,8 +1220,8 @@ TRP_RC trps_sweep_ctable(TRPS_INSTANCE *trps) tr_comm_memb_expire(memb); trps_compute_expiry(trps, tr_comm_memb_get_interval(memb), tr_comm_memb_get_expiry(memb)); tr_debug("trps_sweep_ctable: community membership expired at %s, resetting expiry to %s (%.*s in %.*s, origin %.*s).", - timespec_to_str(&sweep_time), - timespec_to_str(tr_comm_memb_get_expiry(memb)), + timespec_to_str(tr_clock_convert(TRP_CLOCK, &sweep_time, CLOCK_REALTIME, &tmp)), + timespec_to_str(tr_comm_memb_get_expiry_realtime(memb, &tmp)), tr_comm_memb_get_realm_id(memb)->len, tr_comm_memb_get_realm_id(memb)->buf, tr_comm_get_id(tr_comm_memb_get_comm(memb))->len, tr_comm_get_id(tr_comm_memb_get_comm(memb))->buf, tr_comm_memb_get_origin(memb)->len, tr_comm_memb_get_origin(memb)->buf); -- 2.1.4