From 4265e5d362914f3732b4035dcf67162e525e0142 Mon Sep 17 00:00:00 2001 Message-Id: <4265e5d362914f3732b4035dcf67162e525e0142.1715302936.git.mdw@distorted.org.uk> From: Mark Wooding Date: Sun, 18 Oct 2009 14:00:03 +0100 Subject: [PATCH] Shun time(), since on Linux it is not monotonic with gettimeofday(). Organization: Straylight/Edgeware From: Richard Kettlewell http://ewx.livejournal.com/530850.html describes the underlying issue and http://code.google.com/p/disorder/issues/detail?id=35 describes the effect of this on DisOrder. --- cgi/actions.c | 2 +- cgi/disorder-cgi.h | 1 + clients/playrtp.c | 2 +- disobedience/client.c | 4 ++-- disobedience/queue-generic.c | 2 +- disobedience/queue.c | 2 +- lib/cache.c | 7 ++++--- lib/cookies.c | 7 ++++--- lib/eclient.c | 2 +- lib/sendmail.c | 2 +- lib/syscalls.c | 9 +++++++++ lib/syscalls.h | 1 + lib/trackdb.c | 4 ++-- libtests/t-dateparse.c | 2 +- libtests/t-event.c | 6 +++--- server/choose.c | 2 +- server/disorderd.c | 2 +- server/play.c | 8 ++++---- server/queue-ops.c | 2 +- server/rescan.c | 10 +++++----- server/schedule.c | 4 ++-- server/server-queue.c | 4 ++-- server/server.c | 12 ++++++------ server/speaker.c | 4 ++-- 24 files changed, 57 insertions(+), 44 deletions(-) diff --git a/cgi/actions.c b/cgi/actions.c index d752660..ab52c2d 100644 --- a/cgi/actions.c +++ b/cgi/actions.c @@ -75,7 +75,7 @@ static void act_playing(void) { && length && dcgi_playing->sofar >= 0) { /* Try to put the next refresh at the start of the next track. */ - time(&now); + xtime(&now); fin = now + length - dcgi_playing->sofar + config->gap; if(now + refresh > fin) refresh = fin - now; diff --git a/cgi/disorder-cgi.h b/cgi/disorder-cgi.h index 9a455e9..4587e78 100644 --- a/cgi/disorder-cgi.h +++ b/cgi/disorder-cgi.h @@ -52,6 +52,7 @@ #include "mime.h" #include "sendmail.h" #include "charset.h" +#include "syscalls.h" extern disorder_client *dcgi_client; extern char *dcgi_cookie; diff --git a/clients/playrtp.c b/clients/playrtp.c index c07c153..7eed9eb 100644 --- a/clients/playrtp.c +++ b/clients/playrtp.c @@ -858,7 +858,7 @@ int main(int argc, char **argv) { || (nsamples > 0 && contains(pheap_first(&packets), next_timestamp))) { if(monitor) { - time_t now = time(0); + time_t now = xtime(0); if(now >= lastlog + 60) { int offset = nsamples - minbuffer; diff --git a/disobedience/client.c b/disobedience/client.c index dd40597..45c6c0a 100644 --- a/disobedience/client.c +++ b/disobedience/client.c @@ -37,7 +37,7 @@ static gboolean gtkclient_prepare(GSource *source, gint *timeout) { const struct eclient_source *esource = (struct eclient_source *)source; D(("gtkclient_prepare")); - if(time(0) > esource->last_poll + 10) + if(xtime(0) > esource->last_poll + 10) return TRUE; /* timed out */ *timeout = 3000/*milliseconds*/; return FALSE; /* please poll */ @@ -65,7 +65,7 @@ static gboolean gtkclient_dispatch(GSource *source, mode |= DISORDER_POLL_READ; if(revents & (G_IO_OUT|G_IO_HUP|G_IO_ERR)) mode |= DISORDER_POLL_WRITE; - time(&esource->last_poll); + xtime(&esource->last_poll); disorder_eclient_polled(esource->client, mode); return TRUE; /* ??? not documented */ } diff --git a/disobedience/queue-generic.c b/disobedience/queue-generic.c index 636f10c..aa29c28 100644 --- a/disobedience/queue-generic.c +++ b/disobedience/queue-generic.c @@ -129,7 +129,7 @@ const char *column_length(const struct queue_entry *q, else { if(!last_playing) return NULL; - time(&now); + xtime(&now); l = playing_track->sofar + (now - last_playing); } byte_xasprintf(&played, "%ld:%02ld/%s", l / 60, l % 60, length); diff --git a/disobedience/queue.c b/disobedience/queue.c index 6dac960..7779c0f 100644 --- a/disobedience/queue.c +++ b/disobedience/queue.c @@ -97,7 +97,7 @@ static void playing_completed(void attribute((unused)) *v, } actual_playing_track = q; queue_playing_changed(); - time(&last_playing); + xtime(&last_playing); } /** @brief Schedule an update to the queue diff --git a/lib/cache.c b/lib/cache.c index e0f69ef..52629de 100644 --- a/lib/cache.c +++ b/lib/cache.c @@ -24,6 +24,7 @@ #include "hash.h" #include "mem.h" #include "log.h" +#include "syscalls.h" #include "cache.h" /** @brief The global cache */ @@ -60,7 +61,7 @@ void cache_put(const struct cache_type *type, c = xmalloc(sizeof *c); c->type = type; c->value = value; - time(&c->birth); + xtime(&c->birth); hash_add(h, key, c, HASH_INSERT_OR_REPLACE); } @@ -75,7 +76,7 @@ const void *cache_get(const struct cache_type *type, const char *key) { if(h && (c = hash_find(h, key)) && c->type == type - && !expired(c, time(0))) + && !expired(c, xtime(0))) return c->value; else return 0; @@ -98,7 +99,7 @@ void cache_expire(void) { time_t now; if(h) { - time(&now); + xtime(&now); hash_foreach(h, expiry_callback, &now); } } diff --git a/lib/cookies.c b/lib/cookies.c index 0ff17b5..e32fc0c 100644 --- a/lib/cookies.c +++ b/lib/cookies.c @@ -35,6 +35,7 @@ #include "configuration.h" #include "kvp.h" #include "trackdb.h" +#include "syscalls.h" /** @brief Hash function used in signing HMAC */ #define ALGO GCRY_MD_SHA1 @@ -66,7 +67,7 @@ static int revoked_cleanup_callback(const char *key, void *value, static void newkey(void) { time_t now; - time(&now); + xtime(&now); memcpy(old_signing_key, signing_key, HASHSIZE); gcry_randomize(signing_key, HASHSIZE, GCRY_STRONG_RANDOM); signing_key_validity_limit = now + config->cookie_key_lifetime; @@ -134,7 +135,7 @@ char *make_cookie(const char *user) { return 0; } /* make sure we have a valid signing key */ - time(&now); + xtime(&now); if(now >= signing_key_validity_limit) newkey(); /* construct the subject */ @@ -187,7 +188,7 @@ char *verify_cookie(const char *cookie, rights_type *rights) { /* Extract the username */ user = xstrndup(c1 + 1, c2 - (c1 + 1)); /* check expiry */ - time(&now); + xtime(&now); if(now >= t) { error(0, "cookie has expired"); return 0; diff --git a/lib/eclient.c b/lib/eclient.c index 40639f3..101a7bf 100644 --- a/lib/eclient.c +++ b/lib/eclient.c @@ -417,7 +417,7 @@ void disorder_eclient_polled(disorder_eclient *c, unsigned mode) { /* Queue up a byte to send */ if(c->state == state_log && c->output.nvec == 0 - && time(&now) - c->last_prod > LOG_PROD_INTERVAL) { + && xtime(&now) - c->last_prod > LOG_PROD_INTERVAL) { put(c, "x", 1); c->last_prod = now; } diff --git a/lib/sendmail.c b/lib/sendmail.c index 5b9a82c..a01b9d3 100644 --- a/lib/sendmail.c +++ b/lib/sendmail.c @@ -126,7 +126,7 @@ static int sendmailfp(const char *tag, FILE *in, FILE *out, time_t now; char date[128]; - time(&now); + xtime(&now); gmtime_r(&now, &ut); strftime(date, sizeof date, "%a, %d %b %Y %H:%M:%S +0000", &ut); gcry_create_nonce(idbuf, sizeof idbuf); diff --git a/lib/syscalls.c b/lib/syscalls.c index 0d5dc18..b175666 100644 --- a/lib/syscalls.c +++ b/lib/syscalls.c @@ -148,6 +148,15 @@ void xgettimeofday(struct timeval *tv, struct timezone *tz) { mustnotbeminus1("gettimeofday", gettimeofday(tv, tz)); } +time_t xtime(time_t *whenp) { + struct timeval tv; + + xgettimeofday(&tv, NULL); + if(whenp) + *whenp = tv.tv_sec; + return tv.tv_sec; +} + /* Local Variables: c-basic-offset:2 diff --git a/lib/syscalls.h b/lib/syscalls.h index 9f8bd20..e45125a 100644 --- a/lib/syscalls.h +++ b/lib/syscalls.h @@ -48,6 +48,7 @@ int xprintf(const char *, ...) void xfclose(FILE *); int xnice(int); void xgettimeofday(struct timeval *, struct timezone *); +time_t xtime(time_t *when); /* the above all call @fatal@ if the system call fails */ void nonblock(int fd); diff --git a/lib/trackdb.c b/lib/trackdb.c index 83a67af..6984b21 100644 --- a/lib/trackdb.c +++ b/lib/trackdb.c @@ -1042,7 +1042,7 @@ int trackdb_notice_tid(const char *track, /* this is a real track */ t_changed += kvp_set(&t, "_alias_for", 0); t_changed += kvp_set(&t, "_path", path); - time(&now); + xtime(&now); if(ret == DB_NOTFOUND) { /* It's a new track; record the time */ byte_xasprintf(¬iced, "%lld", (long long)now); @@ -2598,7 +2598,7 @@ static int create_user(const char *user, kvp_set(&k, "email", email); if(confirmation) kvp_set(&k, "confirmation", confirmation); - snprintf(s, sizeof s, "%jd", (intmax_t)time(0)); + snprintf(s, sizeof s, "%jd", (intmax_t)xtime(0)); kvp_set(&k, "created", s); return trackdb_putdata(trackdb_usersdb, user, k, tid, flags); } diff --git a/libtests/t-dateparse.c b/libtests/t-dateparse.c index a0a3b0d..7926e01 100644 --- a/libtests/t-dateparse.c +++ b/libtests/t-dateparse.c @@ -33,7 +33,7 @@ static void check_date(time_t when, } static void test_dateparse(void) { - time_t now = time(0); + time_t now = xtime(0); check_date(now, "%Y-%m-%d %H:%M:%S", localtime); #if 0 /* see dateparse.c */ check_date(now, "%Y-%m-%d %H:%M:%S %Z", localtime); diff --git a/libtests/t-event.c b/libtests/t-event.c index 3a723b4..1474408 100644 --- a/libtests/t-event.c +++ b/libtests/t-event.c @@ -51,13 +51,13 @@ static void test_event(void) { ev_source *ev; ev = ev_new(); - w.tv_sec = time(0) + 2; + w.tv_sec = xtime(0) + 2; w.tv_usec = 0; ev_timeout(ev, &t1, &w, callback1, 0); - w.tv_sec = time(0) + 3; + w.tv_sec = xtime(0) + 3; w.tv_usec = 0; ev_timeout(ev, &t2, &w, callback2, 0); - w.tv_sec = time(0) + 4; + w.tv_sec = xtime(0) + 4; w.tv_usec = 0; ev_timeout(ev, &t3, &w, callback3, 0); check_integer(ev_run(ev), 1); diff --git a/server/choose.c b/server/choose.c index 10ed360..733b3ca 100644 --- a/server/choose.c +++ b/server/choose.c @@ -92,7 +92,7 @@ static unsigned long compute_weight(const char *track, struct kvp *prefs) { const char *s; char **track_tags; - time_t last, now = time(0); + time_t last, now = xtime(0); /* Reject tracks not in any collection (race between edit config and * rescan) */ diff --git a/server/disorderd.c b/server/disorderd.c index 31b70a4..ab88190 100644 --- a/server/disorderd.c +++ b/server/disorderd.c @@ -207,7 +207,7 @@ int main(int argc, char **argv) { } info("process ID %lu", (unsigned long)getpid()); fix_path(); - srand(time(0)); /* don't start the same every time */ + srand(xtime(0)); /* don't start the same every time */ /* gcrypt initialization */ if(!gcry_check_version(NULL)) disorder_fatal(0, "gcry_check_version failed"); diff --git a/server/play.c b/server/play.c index f971991..b9d452b 100644 --- a/server/play.c +++ b/server/play.c @@ -583,7 +583,7 @@ void play(ev_source *ev) { } /* It's become the playing track */ playing = q; - time(&playing->played); + xtime(&playing->played); playing->state = playing_started; notify_play(playing->track, playing->submitter); eventlog("playing", playing->track, @@ -685,7 +685,7 @@ void scratch(const char *who, const char *id) { q = queue_add(config->scratch.s[r], who, WHERE_START, origin_scratch); } notify_scratch(playing->track, playing->submitter, who, - time(0) - playing->played); + xtime(0) - playing->played); } } @@ -735,7 +735,7 @@ int pause_playing(const char *who) { error(0, "player indicates it cannot pause"); return -1; } - time(&playing->lastpaused); + xtime(&playing->lastpaused); playing->uptopause = played; playing->lastresumed = 0; break; @@ -769,7 +769,7 @@ void resume_playing(const char *who) { return; } play_resume(playing->pl, playing->data); - time(&playing->lastresumed); + xtime(&playing->lastresumed); break; case DISORDER_PLAYER_RAW: memset(&sm, 0, sizeof sm); diff --git a/server/queue-ops.c b/server/queue-ops.c index d5b5bf3..ed97c0c 100644 --- a/server/queue-ops.c +++ b/server/queue-ops.c @@ -59,7 +59,7 @@ struct queue_entry *queue_add(const char *track, const char *submitter, q->origin = origin; q->pid = -1; queue_id(q); - time(&q->when); + xtime(&q->when); switch(where) { case WHERE_START: queue_insert_entry(&qhead, q); diff --git a/server/rescan.c b/server/rescan.c index ebff098..dfe855a 100644 --- a/server/rescan.c +++ b/server/rescan.c @@ -134,9 +134,9 @@ static void rescan_collection(const struct collection *c) { if(n < config->player.n) { nnew += !!trackdb_notice(track, path); ++ntracks; - if(ntracks % 100 == 0 && time(0) > last_report + 10) { + if(ntracks % 100 == 0 && xtime(0) > last_report + 10) { info("rescanning %s, %ld tracks so far", c->root, ntracks); - time(&last_report); + xtime(&last_report); } } } @@ -300,12 +300,12 @@ static void recheck_collection(const struct collection *c) { return; recheck_track(&cs, t); ++nrc; - if(nrc % 100 == 0 && time(0) > last_report + 10) { + if(nrc % 100 == 0 && xtime(0) > last_report + 10) { if(c) info("rechecking %s, %ld tracks so far", c->root, nrc); else info("rechecking all tracks, %ld tracks so far", nrc); - time(&last_report); + xtime(&last_report); } } if(c) @@ -350,7 +350,7 @@ static void do_all(void (*fn)(const struct collection *c)) { static void expire_noticed(void) { time_t now; - time(&now); + xtime(&now); trackdb_expire_noticed(now - config->noticed_history * 86400); } diff --git a/server/schedule.c b/server/schedule.c index ec7eddc..2669fed 100644 --- a/server/schedule.c +++ b/server/schedule.c @@ -182,7 +182,7 @@ static int schedule_init_tid(ev_source *ev, } when.tv_usec = 0; /* The action might be in the past */ - if(when.tv_sec < time(0)) { + if(when.tv_sec < xtime(0)) { const char *priority = kvp_get(actiondata, "priority"); if(priority && !strcmp(priority, "junk")) { @@ -287,7 +287,7 @@ const char *schedule_add(ev_source *ev, when.tv_sec = atoll(kvp_get(actiondata, "when")); when.tv_usec = 0; /* Reject events in the past */ - if(when.tv_sec <= time(0)) { + if(when.tv_sec <= xtime(0)) { error(0, "new scheduled event is in the past"); return 0; } diff --git a/server/server-queue.c b/server/server-queue.c index 2efd94a..ab15474 100644 --- a/server/server-queue.c +++ b/server/server-queue.c @@ -50,11 +50,11 @@ void queue_fix_sofar(struct queue_entry *q) { if(q->uptopause == -1) /* Don't know how far thru. */ sofar = -1; else if(q->lastresumed) /* Has been paused and resumed. */ - sofar = q->uptopause + time(0) - q->lastresumed; + sofar = q->uptopause + xtime(0) - q->lastresumed; else /* Currently paused. */ sofar = q->uptopause; } else /* Never been paused. */ - sofar = time(0) - q->played; + sofar = xtime(0) - q->played; q->sofar = sofar; } } diff --git a/server/server.c b/server/server.c index 8bd23e7..feaac7e 100644 --- a/server/server.c +++ b/server/server.c @@ -541,13 +541,13 @@ static int c_queue(struct conn *c, queue_fix_sofar(playing); if((l = trackdb_get(playing->track, "_length")) && (length = atol(l))) { - time(&when); + xtime(&when); when += length - playing->sofar + config->gap; } } else /* Nothing is playing but playing is enabled, so whatever is * first in the queue can be expected to start immediately. */ - time(&when); + xtime(&when); } for(q = qhead.next; q != &qhead; q = q->next) { /* fill in estimated start time */ @@ -894,7 +894,7 @@ static void logclient(const char *msg, void *user) { return; } sink_printf(ev_writer_sink(c->w), "%"PRIxMAX" %s\n", - (uintmax_t)time(0), msg); + (uintmax_t)xtime(0), msg); } static int c_log(struct conn *c, @@ -904,7 +904,7 @@ static int c_log(struct conn *c, sink_writes(ev_writer_sink(c->w), "254 OK\n"); /* pump out initial state */ - time(&now); + xtime(&now); sink_printf(ev_writer_sink(c->w), "%"PRIxMAX" state %s\n", (uintmax_t)now, playing_is_enabled() ? "enable_play" : "disable_play"); @@ -1262,7 +1262,7 @@ static int c_edituser(struct conn *c, if(d->lo) sink_printf(ev_writer_sink(d->w), "%"PRIxMAX" rights_changed %s\n", - (uintmax_t)time(0), + (uintmax_t)xtime(0), quoteutf8(new_rights)); } } @@ -1443,7 +1443,7 @@ static int c_reminder(struct conn *c, if(!last_reminder) last_reminder = hash_new(sizeof (time_t)); last = hash_find(last_reminder, vec[0]); - time(&now); + xtime(&now); if(last && now < *last + config->reminder_interval) { error(0, "sent a password reminder to '%s' too recently", vec[0]); sink_writes(ev_writer_sink(c->w), "550 Cannot send a reminder email\n"); diff --git a/server/speaker.c b/server/speaker.c index 2206547..892e33c 100644 --- a/server/speaker.c +++ b/server/speaker.c @@ -370,7 +370,7 @@ static void report(void) { strcpy(sm.id, playing->id); sm.data = playing->played / (uaudio_rate * uaudio_channels); speaker_send(1, &sm); - time(&last_report); + xtime(&last_report); } } @@ -684,7 +684,7 @@ static void mainloop(void) { } } /* If we've not reported our state for a second do so now. */ - if(force_report || time(0) > last_report) + if(force_report || xtime(0) > last_report) report(); } } -- [mdw]