Use a monotonic clock where appropriate.
authorEmil Mikulic <emikulic@gmail.com>
Sun, 8 Jul 2012 06:56:29 +0000 (16:56 +1000)
committerEmil Mikulic <emikulic@gmail.com>
Sun, 8 Jul 2012 07:56:02 +0000 (17:56 +1000)
- Host last seen time (although we have to launder it to/from real time
  when dealing with export files)

- Time since localip_update (this fixes a long-standing FIXME)

- HTTP timeouts.

16 files changed:
Makefile.in
acct.c
cap.c
configure.ac
darkstat.c
daylog.c
graph_db.c
hosts_db.c
hosts_db.h
hosts_sort.c
http.c
localip.c
localip.h
now.c [new file with mode: 0644]
now.h
release.sh

index fbd871f..d06c8a6 100644 (file)
@@ -43,6 +43,7 @@ html.c                \
 http.c         \
 localip.c      \
 ncache.c       \
+now.c          \
 pidfile.c      \
 str.c
 
@@ -129,7 +130,7 @@ cap.o: cap.c acct.h cdefs.h cap.h config.h conv.h decode.h addr.h err.h \
 conv.o: conv.c conv.h err.h cdefs.h
 darkstat.o: darkstat.c acct.h cap.h cdefs.h config.h conv.h daylog.h \
  graph_db.h db.h dns.h err.h http.h hosts_db.h addr.h localip.h ncache.h \
pidfile.h now.h
now.h pidfile.h
 daylog.o: daylog.c err.h cdefs.h daylog.h graph_db.h str.h now.h
 db.o: db.c cdefs.h err.h hosts_db.h addr.h graph_db.h db.h
 decode.o: decode.c cdefs.h decode.h addr.h err.h opt.h
@@ -144,7 +145,9 @@ hosts_sort.o: hosts_sort.c cdefs.h err.h hosts_db.h addr.h
 html.o: html.c config.h str.h html.h opt.h
 http.o: http.c cdefs.h config.h conv.h err.h graph_db.h hosts_db.h addr.h \
  http.h now.h queue.h str.h stylecss.h graphjs.h
-localip.o: localip.c addr.h config.h conv.h err.h cdefs.h localip.h bsd.h
+localip.o: localip.c addr.h bsd.h config.h conv.h err.h cdefs.h localip.h \
+ now.h
 ncache.o: ncache.c conv.h err.h cdefs.h ncache.h tree.h bsd.h config.h
+now.o: now.c err.h cdefs.h now.h
 pidfile.o: pidfile.c err.h cdefs.h str.h pidfile.h
 str.o: str.c conv.h err.h cdefs.h str.h
diff --git a/acct.c b/acct.c
index bfdf1a1..c02c8cd 100644 (file)
--- a/acct.c
+++ b/acct.c
@@ -205,7 +205,7 @@ acct_for(const struct pktsummary * const sm)
       hs->out   += sm->len;
       hs->total += sm->len;
       memcpy(hs->u.host.mac_addr, sm->src_mac, sizeof(sm->src_mac));
-      hs->u.host.lastseen = now;
+      hs->u.host.last_seen_mono = now_mono();
    }
 
    if (!opt_want_local_only || addr_is_local(&sm->dst)) {
diff --git a/cap.c b/cap.c
index 3c8025d..7757153 100644 (file)
--- a/cap.c
+++ b/cap.c
@@ -300,12 +300,10 @@ cap_poll(fd_set *read_set
    }
 #endif
 
-   /*
-    * Once per capture poll, check our IP address.  It's used in accounting
+   /* Once per capture poll, check our IP address.  It's used in accounting
     * for traffic graphs.
     */
    localip_update(opt_interface, local_ips);
-   /* FIXME: ^ this might run too often */
 
    total = 0;
    for (;;) {
index 0da026f..c8c3e38 100644 (file)
@@ -322,5 +322,7 @@ AC_SEARCH_LIBS(strlcat, [bsd],
 AC_CHECK_HEADERS(bsd/string.h)
 AC_CHECK_HEADERS(bsd/unistd.h)
 
+AC_SEARCH_LIBS(clock_gettime, rt)
+
 AC_CONFIG_FILES([Makefile darkstat.8])
 AC_OUTPUT
index 26f289b..ea9bf2a 100644 (file)
@@ -20,6 +20,7 @@
 #include "hosts_db.h"
 #include "localip.h"
 #include "ncache.h"
+#include "now.h"
 #include "pidfile.h"
 
 #include <assert.h>
@@ -33,9 +34,6 @@
 #include <unistd.h>
 #include <pcap.h>
 
-#include "now.h"
-time_t now;
-
 #ifndef INADDR_NONE
 # define INADDR_NONE (-1) /* Solaris */
 #endif
@@ -433,7 +431,7 @@ main(int argc, char **argv)
    privdrop(opt_chroot_dir, opt_privdrop_user);
 
    /* Don't need root privs for these: */
-   now = time(NULL);
+   now_init();
    if (daylog_fn != NULL) daylog_init(daylog_fn);
    graph_init();
    hosts_db_init();
@@ -459,22 +457,6 @@ main(int argc, char **argv)
       struct timeval timeout;
       fd_set rs, ws;
 
-      now = time(NULL);
-
-      if (export_pending) {
-         if (export_fn != NULL)
-            db_export(export_fn);
-         export_pending = 0;
-      }
-
-      if (reset_pending) {
-         if (export_pending)
-            continue; /* export before reset */
-         hosts_db_reset();
-         graph_reset();
-         reset_pending = 0;
-      }
-
       FD_ZERO(&rs);
       FD_ZERO(&ws);
 
@@ -483,22 +465,35 @@ main(int argc, char **argv)
 
       select_ret = select(max_fd+1, &rs, &ws, NULL,
          (use_timeout) ? &timeout : NULL);
-
-      if ((select_ret == 0) && (!use_timeout))
+      if (select_ret == 0 && !use_timeout)
             errx(1, "select() erroneously timed out");
-
       if (select_ret == -1) {
          if (errno == EINTR)
             continue;
          else
             err(1, "select()");
       }
-      else {
-         graph_rotate();
-         cap_poll(&rs);
-         dns_poll();
-         http_poll(&rs, &ws);
+
+      now_update();
+
+      if (export_pending) {
+         if (export_fn != NULL)
+            db_export(export_fn);
+         export_pending = 0;
       }
+
+      if (reset_pending) {
+         if (export_pending)
+            continue; /* export before reset */
+         hosts_db_reset();
+         graph_reset();
+         reset_pending = 0;
+      }
+
+      graph_rotate();
+      cap_poll(&rs);
+      dns_poll();
+      http_poll(&rs, &ws);
    }
 
    verbosef("shutting down");
index eb9d2eb..d443dcf 100644 (file)
--- a/daylog.c
+++ b/daylog.c
@@ -9,50 +9,49 @@
 
 #define _GNU_SOURCE 1 /* for O_NOFOLLOW on Linux */
 
-#include <sys/types.h>
+#include "err.h"
+#include "daylog.h"
+#include "str.h"
+#include "now.h"
+
 #include <assert.h>
 #include <fcntl.h>
 #include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
+#include <time.h>
 #include <unistd.h>
 
-#include "err.h"
-#include "daylog.h"
-#include "str.h"
-#include "now.h"
-
 static const char *daylog_fn = NULL;
-static time_t today_time, tomorrow_time;
+static long today_real, tomorrow_real;
 static uint64_t bytes_in, bytes_out, pkts_in, pkts_out;
 
 #define DAYLOG_DATE_LEN 26 /* strlen("1900-01-01 00:00:00 +1234") + 1 */
 static char datebuf[DAYLOG_DATE_LEN];
 
-static char *
-fmt_date(const time_t when)
-{
-    time_t tmp = when;
-    if (strftime(datebuf, DAYLOG_DATE_LEN,
-        "%Y-%m-%d %H:%M:%S %z", localtime(&tmp) ) == 0)
+static char *fmt_date(time_t when) {
+    if (strftime(datebuf,
+                 DAYLOG_DATE_LEN,
+                 "%Y-%m-%d %H:%M:%S %z",
+                 localtime(&when)) == 0)
             errx(1, "strftime() failed in fmt_date()");
-    return (datebuf);
+    return datebuf;
 }
 
 /* Given some time today, find the first second of tomorrow. */
-static time_t
-tomorrow(const time_t today)
-{
-   time_t tmp = today;
+static time_t tomorrow(time_t t_before) {
+   time_t t_after;
    struct tm tm, *lt;
 
-   lt = localtime(&tmp);
+   lt = localtime(&t_before);
    memcpy(&tm, lt, sizeof(tm));
    tm.tm_sec = 0;
    tm.tm_min = 0;
    tm.tm_hour = 0;
    tm.tm_mday = lt->tm_mday + 1; /* tomorrow */
-   return mktime(&tm);
+   t_after = mktime(&tm);
+   assert(t_after > t_before);
+   return t_after;
 }
 
 /* Warns on error. */
@@ -87,41 +86,42 @@ static void daylog_write(const char *format, ...) {
 }
 
 static void daylog_emit(void) {
-   daylog_write("%s|%u|%qu|%qu|%qu|%qu\n",
-                fmt_date(today_time), (unsigned int)today_time,
+   daylog_write("%s|%ld|%qu|%qu|%qu|%qu\n",
+                fmt_date(today_real), (long)today_real,
                 bytes_in, bytes_out, pkts_in, pkts_out);
 }
 
 void daylog_init(const char *filename) {
    daylog_fn = filename;
-   today_time = time(NULL);
-   tomorrow_time = tomorrow(today_time);
-   verbosef("today is %u, tomorrow is %u",
-      (unsigned int)today_time, (unsigned int)tomorrow_time);
+   today_real = now_real();
+   tomorrow_real = tomorrow(today_real);
+   verbosef("today is %ld, tomorrow is %ld",
+            (long)today_real, (long)tomorrow_real);
    bytes_in = bytes_out = pkts_in = pkts_out = 0;
 
-   daylog_write("# logging started at %s (%u)\n",
-                fmt_date(today_time), (unsigned int)today_time);
+   daylog_write("# logging started at %s (%ld)\n",
+                fmt_date(today_real), (long)today_real);
 }
 
 void daylog_free(void) {
-   today_time = time(NULL);
+   today_real = now_real();
    daylog_emit(); /* Emit what's currently accumulated before we exit. */
-   daylog_write("# logging stopped at %s (%u)\n",
-                fmt_date(today_time), (unsigned int)today_time);
+   daylog_write("# logging stopped at %s (%ld)\n",
+                fmt_date(today_real), (long)today_real);
 }
 
 void daylog_acct(uint64_t amount, enum graph_dir dir) {
-   if (daylog_fn == NULL) return; /* disabled */
+   if (daylog_fn == NULL)
+      return; /* daylogging disabled */
 
    /* Check if we need to update the log. */
-   if (now >= tomorrow_time) {
+   if (now_real() >= tomorrow_real) {
       daylog_emit();
 
-      today_time = now;
-      tomorrow_time = tomorrow(today_time);
+      today_real = now_real();
+      tomorrow_real = tomorrow(today_real);
       bytes_in = bytes_out = pkts_in = pkts_out = 0;
-      verbosef("updated daylog, tomorrow = %u", (unsigned int)tomorrow_time);
+      verbosef("updated daylog, tomorrow = %ld", (long)tomorrow_real);
    }
 
    /* Accounting. */
index 8c72102..bc774b4 100644 (file)
@@ -23,6 +23,7 @@
 #include <assert.h>
 #include <stdlib.h>
 #include <string.h> /* for memcpy() */
+#include <time.h>
 
 #define GRAPH_WIDTH "320"
 #define GRAPH_HEIGHT "200"
@@ -46,63 +47,54 @@ static struct graph *graph_db[] = {
 };
 
 static unsigned int graph_db_size = sizeof(graph_db)/sizeof(*graph_db);
+static long start_mono, start_real, last_real;
 
-static time_t start_time, last_time;
-
-void
-graph_init(void)
-{
+void graph_init(void) {
    unsigned int i;
    for (i=0; i<graph_db_size; i++) {
       graph_db[i]->in  = xmalloc(sizeof(uint64_t) * graph_db[i]->num_bars);
       graph_db[i]->out = xmalloc(sizeof(uint64_t) * graph_db[i]->num_bars);
    }
-   start_time = time(NULL);
+   start_mono = now_mono();
+   start_real = now_real();
    graph_reset();
 }
 
-static void
-zero_graph(struct graph *g)
-{
+static void zero_graph(struct graph *g) {
    memset(g->in,  0, sizeof(uint64_t) * g->num_bars);
    memset(g->out, 0, sizeof(uint64_t) * g->num_bars);
 }
 
-void
-graph_reset(void)
-{
+void graph_reset(void) {
    unsigned int i;
+
    for (i=0; i<graph_db_size; i++)
       zero_graph(graph_db[i]);
-   last_time = 0;
+   last_real = 0;
 }
 
-void
-graph_free(void)
-{
+void graph_free(void) {
    unsigned int i;
+
    for (i=0; i<graph_db_size; i++) {
       free(graph_db[i]->in);
       free(graph_db[i]->out);
    }
 }
 
-void
-graph_acct(uint64_t amount, enum graph_dir dir)
-{
+void graph_acct(uint64_t amount, enum graph_dir dir) {
    unsigned int i;
    for (i=0; i<graph_db_size; i++)
-    switch (dir) {
-     case GRAPH_IN:  graph_db[i]->in[  graph_db[i]->pos ] += amount; break;
-     case GRAPH_OUT: graph_db[i]->out[ graph_db[i]->pos ] += amount; break;
-     default: errx(1, "unknown graph_dir in graph_acct: %d", dir);
-    }
+      if (dir == GRAPH_IN) {
+         graph_db[i]->in[  graph_db[i]->pos ] += amount;
+      } else {
+         assert(dir == GRAPH_OUT);
+         graph_db[i]->out[ graph_db[i]->pos ] += amount;
+      }
 }
 
 /* Advance a graph: advance the pos, zeroing out bars as we move. */
-static void
-advance(struct graph *g, const unsigned int pos)
-{
+static void advance(struct graph *g, const unsigned int pos) {
    if (g->pos == pos)
       return; /* didn't need to advance */
    do {
@@ -112,10 +104,9 @@ advance(struct graph *g, const unsigned int pos)
 }
 
 /* Rotate a graph: rotate all bars so that the bar at the current pos is moved
- * to the newly given pos.  This is non-destructive. */
-static void
-rotate(struct graph *g, const unsigned int pos)
-{
+ * to the newly given pos.
+ */
+static void rotate(struct graph *g, const unsigned int pos) {
    uint64_t *tmp;
    unsigned int i, ofs;
    size_t size;
@@ -140,13 +131,11 @@ rotate(struct graph *g, const unsigned int pos)
    g->pos = pos;
 }
 
-static void
-graph_resync(const time_t new_time)
-{
+static void graph_resync(const long new_time) {
    struct tm *tm;
    /*
-    * If time went backwards, we assume that real time is continuous and that
-    * the time adjustment should only affect display.  i.e., if we have:
+    * If real time went backwards, we assume that the time adjustment should
+    * only affect display.  i.e., if we have:
     *
     * second 15: 12  bytes
     * second 16: 345 bytes
@@ -159,11 +148,13 @@ graph_resync(const time_t new_time)
     * second 7: 345 bytes
     * second 8: <-- current pos
     *
-    * Note that we don't make any corrections for time being stepped forward.
+    * We don't make any corrections for time being stepped forward,
+    * it's treated as though there was no traffic during that time.
+    *
     * We rely on graph advancement to happen at the correct real time to
     * account for, for example, bandwidth used per day.
     */
-   assert(new_time < last_time);
+   assert(new_time < last_real);
 
    tm = localtime(&new_time);
    if (tm->tm_sec == 60)
@@ -174,25 +165,21 @@ graph_resync(const time_t new_time)
    rotate(&graph_hrs, tm->tm_hour);
    rotate(&graph_days, tm->tm_mday - 1);
 
-   last_time = new_time;
+   last_real = new_time;
 }
 
-void
-graph_rotate(void)
-{
-   time_t t, td;
+void graph_rotate(void) {
+   long t, td;
    struct tm *tm;
    unsigned int i;
 
-   t = now;
+   t = now_real();
+   td = t - last_real;
 
-   if (last_time == 0) {
+   if (last_real == 0) {
       verbosef("first rotate");
-      last_time = t;
+      last_real = t;
       tm = localtime(&t);
-      if (tm->tm_sec == 60)
-         tm->tm_sec = 59; /* mis-handle leap seconds */
-
       graph_secs.pos = tm->tm_sec;
       graph_mins.pos = tm->tm_min;
       graph_hrs.pos = tm->tm_hour;
@@ -200,22 +187,20 @@ graph_rotate(void)
       return;
    }
 
-   if (t == last_time)
-      return; /* superfluous rotate */
+   if (t == last_real)
+      return; /* time has not advanced a full second, don't rotate */
 
-   if (t < last_time) {
-      verbosef("time went backwards! (from %u to %u, offset is %d)",
-         (unsigned int)last_time, (unsigned int)t, (int)(t - last_time));
+   if (t < last_real) {
+      verbosef("graph_db: realtime went backwards! "
+               "(from %ld to %ld, offset is %ld)",
+               last_real, t, td);
       graph_resync(t);
       return;
    }
 
    /* else, normal rotation */
-   td = t - last_time;
-   last_time = t;
+   last_real = t;
    tm = localtime(&t);
-   if (tm->tm_sec == 60)
-      tm->tm_sec = 59; /* mis-handle leap seconds */
 
    /* zero out graphs which have been completely rotated through */
    for (i=0; i<graph_db_size; i++)
@@ -236,14 +221,12 @@ graph_rotate(void)
  * to have validated the header of the segment, and left the file position at
  * the start of the data.
  */
-int
-graph_import(const int fd)
-{
+int graph_import(const int fd) {
    uint64_t last;
    unsigned int i, j;
 
    if (!read64(fd, &last)) return 0;
-   last_time = (time_t)last;
+   last_real = last;
 
    for (i=0; i<graph_db_size; i++) {
       unsigned char num_bars, pos;
@@ -281,12 +264,10 @@ graph_import(const int fd)
  * Database Export: Dump hosts_db into a file provided by the caller.
  * The caller is responsible for writing out the header first.
  */
-int
-graph_export(const int fd)
-{
+int graph_export(const int fd) {
    unsigned int i, j;
 
-   if (!write64(fd, (uint64_t)last_time)) return 0;
+   if (!write64(fd, (uint64_t)last_real)) return 0;
    for (i=0; i<graph_db_size; i++) {
       if (!write8(fd, graph_db[i]->num_bars)) return 0;
       if (!write8(fd, graph_db[i]->pos)) return 0;
@@ -302,26 +283,28 @@ graph_export(const int fd)
 /* ---------------------------------------------------------------------------
  * Web interface: front page!
  */
-struct str *
-html_front_page(void)
-{
+struct str *html_front_page(void) {
    struct str *buf, *rf;
    unsigned int i;
    char start_when[100];
+   long d_real, d_mono;
 
    buf = str_make();
    html_open(buf, "Graphs", /*path_depth=*/0, /*want_graph_js=*/1);
 
+   d_mono = now_mono() - start_mono;
+   d_real = now_real() - start_real;
    str_append(buf, "<p>\n");
    str_append(buf, "<b>Running for</b> <span id=\"rf\">");
-   rf = length_of_time(now - start_time);
-   /* FIXME: use a more monotonic clock perhaps? */
+   rf = length_of_time(d_mono);
    str_appendstr(buf, rf);
    str_free(rf);
    str_append(buf, "</span>");
+   if (abs(d_real - d_mono) > 1)
+      str_appendf(buf, " (real time is off by %ld sec)", d_real - d_mono);
 
    if (strftime(start_when, sizeof(start_when),
-      "%Y-%m-%d %H:%M:%S %Z%z", localtime(&start_time)) != 0)
+      "%Y-%m-%d %H:%M:%S %Z%z", localtime(&start_real)) != 0)
       str_appendf(buf, "<b>, since</b> %s", start_when);
 
    str_appendf(buf,"<b>.</b><br>\n"
@@ -372,15 +355,13 @@ html_front_page(void)
 /* ---------------------------------------------------------------------------
  * Web interface: graphs.xml
  */
-struct str *
-xml_graphs(void)
-{
+struct str *xml_graphs(void) {
    unsigned int i, j;
    struct str *buf = str_make(), *rf;
 
    str_appendf(buf, "<graphs tp=\"%qu\" tb=\"%qu\" pc=\"%u\" pd=\"%u\" rf=\"",
       acct_total_packets, acct_total_bytes, cap_pkts_recv, cap_pkts_drop);
-   rf = length_of_time(now - start_time);
+   rf = length_of_time(now_real() - start_real);
    str_appendstr(buf, rf);
    str_free(rf);
    str_append(buf, "\">\n");
@@ -402,4 +383,4 @@ xml_graphs(void)
    return (buf);
 }
 
-/* vim:set ts=3 sw=3 tw=78 expandtab: */
+/* vim:set ts=3 sw=3 tw=80 et: */
index 754dacc..c2d7ae4 100644 (file)
@@ -26,6 +26,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h> /* memset(), strcmp() */
+#include <time.h>
 #include <unistd.h>
 
 int hosts_db_show_macs = 0;
@@ -236,7 +237,7 @@ make_func_host(const void *key)
    MAKE_BUCKET(b, h, host);
    h->addr = CASTKEY(struct addr);
    h->dns = NULL;
-   h->lastseen = 0;
+   h->last_seen_mono = 0;
    memset(&h->mac_addr, 0, sizeof(h->mac_addr));
    h->ports_tcp = NULL;
    h->ports_udp = NULL;
@@ -344,16 +345,15 @@ format_row_host(struct str *buf, const struct bucket *b,
       b->in, b->out, b->total);
 
    if (opt_want_lastseen) {
-      time_t last_t = b->u.host.lastseen;
+      long last = b->u.host.last_seen_mono;
       struct str *last_str = NULL;
 
-      if ((now >= last_t) && (last_t > 0))
-         last_str = length_of_time(now - last_t);
+      if ((now_mono() >= last) && (last > 0))
+         last_str = length_of_time(now_mono() - last);
 
-      str_append(buf,
-         " <td class=\"num\">");
+      str_append(buf, " <td class=\"num\">");
       if (last_str == NULL) {
-         if (last_t == 0)
+         if (last == 0)
             str_append(buf, "(never)");
          else
             str_append(buf, "(clock error)");
@@ -361,12 +361,10 @@ format_row_host(struct str *buf, const struct bucket *b,
          str_appendstr(buf, last_str);
          str_free(last_str);
       }
-      str_append(buf,
-         "</td>");
+      str_append(buf, "</td>");
    }
 
-   str_appendf(buf,
-      "</tr>\n");
+   str_appendf(buf, "</tr>\n");
 
    /* Only resolve hosts "on demand" */
    if (b->u.host.dns == NULL)
@@ -1024,14 +1022,12 @@ done:
 /* ---------------------------------------------------------------------------
  * Web interface: detailed view of a single host.
  */
-static struct str *
-html_hosts_detail(const char *ip)
-{
+static struct str *html_hosts_detail(const char *ip) {
    struct bucket *h;
    struct str *buf, *ls_len;
    char ls_when[100];
    const char *canonical;
-   time_t ls;
+   time_t last_real;
 
    h = host_search(ip);
    if (h == NULL)
@@ -1069,13 +1065,13 @@ html_hosts_detail(const char *ip)
       "<p>\n"
       "<b>Last seen:</b> ");
 
-   ls = h->u.host.lastseen;
+   last_real = mono_to_real(h->u.host.last_seen_mono);
    if (strftime(ls_when, sizeof(ls_when),
-      "%Y-%m-%d %H:%M:%S %Z%z", localtime(&ls)) != 0)
+      "%Y-%m-%d %H:%M:%S %Z%z", localtime(&last_real)) != 0)
          str_append(buf, ls_when);
 
-   if (h->u.host.lastseen <= now) {
-      ls_len = length_of_time(now - h->u.host.lastseen);
+   if (h->u.host.last_seen_mono <= now_mono()) {
+      ls_len = length_of_time(now_mono() - h->u.host.last_seen_mono);
       str_append(buf, " (");
       str_appendstr(buf, ls_len);
       str_free(ls_len);
@@ -1103,7 +1099,7 @@ html_hosts_detail(const char *ip)
    format_table(buf, h->u.host.ip_protos, 0,TOTAL,0);
 
    html_close(buf);
-   return (buf);
+   return buf;
 }
 
 /* ---------------------------------------------------------------------------
@@ -1263,7 +1259,7 @@ hosts_db_import_host(const int fd)
    if (ver > 1) {
       uint64_t t;
       if (!read64(fd, &t)) return 0;
-      host->u.host.lastseen = (time_t)t;
+      host->u.host.last_seen_mono = real_to_mono(t);
    }
 
    assert(sizeof(host->u.host.mac_addr) == 6);
@@ -1336,9 +1332,11 @@ int hosts_db_export(const int fd)
       if (!writen(fd, export_tag_host_ver3, sizeof(export_tag_host_ver3)))
          return 0;
 
-      if (!writeaddr(fd, &(b->u.host.addr))) return 0;
+      if (!writeaddr(fd, &(b->u.host.addr)))
+         return 0;
 
-      if (!write64(fd, (uint64_t)(b->u.host.lastseen))) return 0;
+      if (!write64(fd, (uint64_t)mono_to_real(b->u.host.last_seen_mono)))
+         return 0;
 
       assert(sizeof(b->u.host.mac_addr) == 6);
       if (!writen(fd, b->u.host.mac_addr, sizeof(b->u.host.mac_addr)))
index f2f389a..3f869f1 100644 (file)
@@ -9,7 +9,7 @@
 #ifndef __DARKSTAT_HOSTS_DB_H
 #define __DARKSTAT_HOSTS_DB_H
 
-#include <sys/types.h> /* for time_t and uint64_t (esp on FreeBSD) */
+#include <sys/types.h> /* for uint64_t */
 
 #include "addr.h"
 
@@ -19,7 +19,7 @@ struct host {
    struct addr addr;
    char *dns;
    uint8_t mac_addr[6];
-   time_t lastseen;
+   long last_seen_mono;
    struct hashtable *ports_tcp, *ports_udp, *ip_protos;
 };
 
index d17a21f..b6f32dd 100644 (file)
@@ -34,8 +34,8 @@ cmp(const struct bucket * const *x, const struct bucket * const *y,
       b = (*y)->total;
       break;
    case LASTSEEN:
-      a = (*x)->u.host.lastseen;
-      b = (*y)->u.host.lastseen;
+      a = (*x)->u.host.last_seen_mono;
+      b = (*y)->u.host.last_seen_mono;
       break;
    default:
       errx(1, "cmp: unknown direction: %d", dir);
diff --git a/http.c b/http.c
index 016d7e2..add1031 100644 (file)
--- a/http.c
+++ b/http.c
@@ -33,6 +33,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <time.h>
 #include <unistd.h>
 #include <zlib.h>
 
@@ -55,7 +56,7 @@ struct connection {
 
     int socket;
     struct sockaddr_storage client;
-    time_t last_active;
+    long last_active_mono;
     enum {
         RECV_REQUEST,          /* receiving request */
         SEND_HEADER_AND_REPLY, /* try to send header+reply together */
@@ -266,7 +267,7 @@ static struct connection *new_connection(void)
 
     conn->socket = -1;
     memset(&conn->client, 0, sizeof(conn->client));
-    conn->last_active = now;
+    conn->last_active_mono = now_mono();
     conn->request = NULL;
     conn->request_length = 0;
     conn->accept_gzip = 0;
@@ -363,13 +364,11 @@ static void free_connection(struct connection *conn)
  * buffer is returned for convenience.
  */
 #define DATE_LEN 30 /* strlen("Fri, 28 Feb 2003 00:02:08 GMT")+1 */
-static char *rfc1123_date(char *dest, const time_t when)
-{
-    time_t tmp = when;
+static char *rfc1123_date(char *dest, time_t when) {
     if (strftime(dest, DATE_LEN,
-        "%a, %d %b %Y %H:%M:%S %Z", gmtime(&tmp) ) == 0)
+        "%a, %d %b %Y %H:%M:%S %Z", gmtime(&when) ) == 0)
             errx(1, "strftime() failed [%s]", dest);
-    return (dest);
+    return dest;
 }
 
 static void generate_header(struct connection *conn,
@@ -397,7 +396,7 @@ static void generate_header(struct connection *conn,
         "\r\n"
         ,
         code, text,
-        rfc1123_date(date, now), server,
+        rfc1123_date(date, now_real()), server,
         conn->mime_type, conn->reply_length, conn->encoding,
         conn->header_extra);
     conn->http_code = code;
@@ -719,7 +718,7 @@ static void poll_recv_request(struct connection *conn)
         conn->state = DONE;
         return;
     }
-    conn->last_active = now;
+    conn->last_active_mono = now_mono();
 
     /* append to conn->request */
     conn->request = xrealloc(conn->request, conn->request_length+recvd+1);
@@ -772,7 +771,7 @@ static void poll_send_header_and_reply(struct connection *conn)
     iov[1].iov_len = conn->reply_length;
 
     sent = writev(conn->socket, iov, 2);
-    conn->last_active = now;
+    conn->last_active_mono = now_mono();
 
     /* handle any errors (-1) or closure (0) in send() */
     if (sent < 1) {
@@ -814,7 +813,7 @@ static void poll_send_header(struct connection *conn)
 
     sent = send(conn->socket, conn->header + conn->header_sent,
         conn->header_length - conn->header_sent, 0);
-    conn->last_active = now;
+    conn->last_active_mono = now_mono();
     dverbosef("poll_send_header(%d) sent %d bytes", conn->socket, (int)sent);
 
     /* handle any errors (-1) or closure (0) in send() */
@@ -850,7 +849,7 @@ static void poll_send_reply(struct connection *conn)
     sent = send(conn->socket,
         conn->reply + conn->reply_sent,
         conn->reply_length - conn->reply_sent, 0);
-    conn->last_active = now;
+    conn->last_active_mono = now_mono();
     dverbosef("poll_send_reply(%d) sent %d: [%d-%d] of %d",
         conn->socket, (int)sent,
         (int)conn->reply_sent,
@@ -1025,7 +1024,7 @@ http_fd_set(fd_set *recv_set, fd_set *send_set, int *max_fd,
 
     LIST_FOREACH_SAFE(conn, &connlist, entries, next)
     {
-        int idlefor = now - conn->last_active;
+        int idlefor = now_mono() - conn->last_active_mono;
 
         /* Time out dead connections. */
         if (idlefor >= idletime) {
index 53820d6..dae3d02 100644 (file)
--- a/localip.c
+++ b/localip.c
@@ -8,11 +8,12 @@
  */
 
 #include "addr.h"
+#include "bsd.h" /* for strlcpy */
 #include "config.h" /* for HAVE_IFADDRS_H */
 #include "conv.h"
 #include "err.h"
 #include "localip.h"
-#include "bsd.h" /* for strlcpy */
+#include "now.h"
 
 #include <sys/socket.h>
 #include <net/if.h>
@@ -38,7 +39,7 @@ struct local_ips *localip_make(void) {
    struct local_ips *ips = xmalloc(sizeof(*ips));
 
    ips->is_valid = 0;
-   ips->last_update = 0;
+   ips->last_update_mono = 0;
    ips->num_addrs = 0;
    ips->addrs = NULL;
    return ips;
@@ -82,6 +83,12 @@ void localip_update(const char *iface, struct local_ips *ips) {
       return;
    }
 
+   if (ips->last_update_mono == now_mono()) {
+      /* Too soon, bail out. */
+      return;
+   }
+   ips->last_update_mono = now_mono();
+
 #ifdef HAVE_IFADDRS_H
    {
       struct ifaddrs *ifas, *ifa;
@@ -150,7 +157,6 @@ void localip_update(const char *iface, struct local_ips *ips) {
             iface, ips->num_addrs, new_addrs);
       ips->num_addrs = new_addrs;
    }
-   ips->last_update = time(NULL);
 }
 
 int is_localip(const struct addr * const a,
index 392a642..df8b4d4 100644 (file)
--- a/localip.h
+++ b/localip.h
@@ -13,7 +13,7 @@
 
 struct local_ips {
    int is_valid;
-   time_t last_update;
+   long last_update_mono;
    int num_addrs;
    struct addr *addrs;
 };
diff --git a/now.c b/now.c
new file mode 100644 (file)
index 0000000..ffc1433
--- /dev/null
+++ b/now.c
@@ -0,0 +1,89 @@
+/* darkstat 3
+ * copyright (c) 2012 Emil Mikulic.
+ *
+ * now.c: a cache of the current time.
+ *
+ * Permission to use, copy, modify, and distribute this file for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include "err.h"
+#include "now.h"
+
+#include <assert.h>
+#include <string.h>
+#include <time.h>
+
+static struct timespec clock_real, clock_mono;
+static int now_initialized = 0;
+
+long now_real(void) {
+   assert(now_initialized);
+   return clock_real.tv_sec;
+}
+
+long now_mono(void) {
+   assert(now_initialized);
+   return clock_mono.tv_sec;
+}
+
+static int before(const struct timespec *a, const struct timespec *b) {
+   if (a->tv_sec < b->tv_sec)
+      return 1;
+   if (a->tv_sec == b->tv_sec && a->tv_nsec < b->tv_nsec)
+      return 1;
+   return 0;
+}
+
+static void clock_update(const clockid_t clk_id,
+                         struct timespec *dest,
+                         const char *name) {
+   struct timespec t;
+
+   clock_gettime(clk_id, &t);
+   if (now_initialized && before(&t, dest)) {
+      verbosef("%s clock went backwards from %ld.%09ld to %ld.%09ld",
+               name,
+               (long)dest->tv_sec,
+               (long)dest->tv_nsec,
+               (long)t.tv_sec,
+               (long)t.tv_nsec);
+   }
+   memcpy(dest, &t, sizeof(t));
+}
+
+static void all_clocks_update(void) {
+   clock_update(CLOCK_REALTIME,  &clock_real, "realtime");
+   clock_update(CLOCK_MONOTONIC, &clock_mono, "monotonic");
+}
+
+void now_init(void) {
+   assert(!now_initialized);
+   all_clocks_update();
+   now_initialized = 1;
+}
+
+void now_update(void) {
+   assert(now_initialized);
+   all_clocks_update();
+}
+
+long mono_to_real(const long t) {
+   assert(now_initialized);
+   return t - clock_mono.tv_sec + clock_real.tv_sec;
+}
+
+long real_to_mono(const long t) {
+   assert(now_initialized);
+   return t - clock_real.tv_sec + clock_mono.tv_sec;
+}
+
+/* vim:set ts=3 sw=3 tw=80 et: */
diff --git a/now.h b/now.h
index 2dd8e4a..7f89423 100644 (file)
--- a/now.h
+++ b/now.h
@@ -1,9 +1,28 @@
 /* darkstat 3
  * copyright (c) 2001-2006 Emil Mikulic.
  *
- * now.h: a cache of the current time
- * This lets us avoid superfluous gettimeofday() syscalls.
+ * now.h: a cache of the current time.
+ *
+ * Permission to use, copy, modify, and distribute this file for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#include <time.h>
 
-extern time_t now; /* updated in the event loop in darkstat.c */
+void now_init(void);
+void now_update(void); /* once per event loop (in darkstat.c) */
+
+long now_real(void);
+long now_mono(void);
+
+long mono_to_real(const long t);
+long real_to_mono(const long t);
+
+/* vim:set ts=3 sw=3 tw=80 et: */
index e831e1e..a59d7c4 100755 (executable)
@@ -63,6 +63,7 @@ localip.c \
 localip.h \
 ncache.c \
 ncache.h \
+now.c \
 now.h \
 opt.h \
 pidfile.c \