[tor-commits] [tor/master] Allow controllers a more up-to-date view of bridge usage.
nickm at torproject.org
nickm at torproject.org
Thu Apr 7 15:56:59 UTC 2011
commit 118d8ffdcb74137a36d22928ce6f46897809391e
Author: Nick Mathewson <nickm at torproject.org>
Date: Tue Mar 15 22:39:22 2011 -0400
Allow controllers a more up-to-date view of bridge usage.
Instead of answering GETINFO requests about our geoip usage only after
running for 24 hours, this patch makes us answer GETINFO requests
immediately. We still round and quantize as before.
Implements bug2711.
Also, refactor the heck out of the bridge usage formatting code. No
longer should we need to do a generate-parse-and-regenerate cycle to
get the controller string, and that lets us simplify the code a lot.
---
changes/feature2711 | 4 +
src/or/control.c | 4 +-
src/or/geoip.c | 172 ++++++++++++++++++++++++++++-----------------------
src/or/geoip.h | 2 +-
4 files changed, 101 insertions(+), 81 deletions(-)
diff --git a/changes/feature2711 b/changes/feature2711
new file mode 100644
index 0000000..7cdcfbf
--- /dev/null
+++ b/changes/feature2711
@@ -0,0 +1,4 @@
+ o Minor features
+ - Export GeoIP information on usage to bridge controller even if we have
+ not yet been running for 24 hours.
+
diff --git a/src/or/control.c b/src/or/control.c
index fb9c1ae..8f3af0b 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -1785,12 +1785,12 @@ getinfo_helper_events(control_connection_t *control_conn,
"information", question);
}
} else if (!strcmp(question, "status/clients-seen")) {
- const char *bridge_stats = geoip_get_bridge_stats_controller(time(NULL));
+ char *bridge_stats = geoip_get_bridge_stats_controller(time(NULL));
if (!bridge_stats) {
*errmsg = "No bridge-client stats available";
return -1;
}
- *answer = tor_strdup(bridge_stats);
+ *answer = bridge_stats;
} else {
return 0;
}
diff --git a/src/or/geoip.c b/src/or/geoip.c
index 654241c..28f570a 100644
--- a/src/or/geoip.c
+++ b/src/or/geoip.c
@@ -1083,14 +1083,14 @@ geoip_bridge_stats_term(void)
start_of_bridge_stats_interval = 0;
}
-/** Parse the bridge statistics as they are written to extra-info
- * descriptors for being returned to controller clients. Return the
- * controller string if successful, or NULL otherwise. */
-static char *
-parse_bridge_stats_controller(const char *stats_str, time_t now)
+/** Validate a bridge statistics string as it would be written to a
+ * current extra-info descriptor. Return 1 if the string is valid and
+ * recent enough, or 0 otherwise. */
+static int
+validate_bridge_stats(const char *stats_str, time_t now)
{
char stats_end_str[ISO_TIME_LEN+1], stats_start_str[ISO_TIME_LEN+1],
- *controller_str, *eos, *eol, *summary;
+ *eos;
const char *BRIDGE_STATS_END = "bridge-stats-end ";
const char *BRIDGE_IPS = "bridge-ips ";
@@ -1104,63 +1104,90 @@ parse_bridge_stats_controller(const char *stats_str, time_t now)
"bridge-stats-end YYYY-MM-DD HH:MM:SS (N s)" */
tmp = find_str_at_start_of_line(stats_str, BRIDGE_STATS_END);
if (!tmp)
- return NULL;
+ return 0;
tmp += strlen(BRIDGE_STATS_END);
if (strlen(tmp) < ISO_TIME_LEN + 6)
- return NULL;
+ return 0;
strlcpy(stats_end_str, tmp, sizeof(stats_end_str));
if (parse_iso_time(stats_end_str, &stats_end_time) < 0)
- return NULL;
+ return 0;
if (stats_end_time < now - (25*60*60) ||
stats_end_time > now + (1*60*60))
- return NULL;
+ return 0;
seconds = (int)strtol(tmp + ISO_TIME_LEN + 2, &eos, 10);
if (!eos || seconds < 23*60*60)
- return NULL;
+ return 0;
format_iso_time(stats_start_str, stats_end_time - seconds);
/* Parse: "bridge-ips CC=N,CC=N,..." */
tmp = find_str_at_start_of_line(stats_str, BRIDGE_IPS);
- if (tmp) {
- tmp += strlen(BRIDGE_IPS);
- tmp = eat_whitespace_no_nl(tmp);
- eol = strchr(tmp, '\n');
- if (eol)
- summary = tor_strndup(tmp, eol-tmp);
- else
- summary = tor_strdup(tmp);
- } else {
+ if (!tmp) {
/* Look if there is an empty "bridge-ips" line */
tmp = find_str_at_start_of_line(stats_str, BRIDGE_IPS_EMPTY_LINE);
if (!tmp)
- return NULL;
- summary = tor_strdup("");
+ return 0;
}
- tor_asprintf(&controller_str,
- "TimeStarted=\"%s\" CountrySummary=%s",
- stats_start_str, summary);
- tor_free(summary);
- return controller_str;
+ return 1;
}
/** Most recent bridge statistics formatted to be written to extra-info
* descriptors. */
static char *bridge_stats_extrainfo = NULL;
-/** Most recent bridge statistics formatted to be returned to controller
- * clients. */
-static char *bridge_stats_controller = NULL;
+/** Return a newly allocated string holding our bridge usage stats by country
+ * in a format suitable for inclusion in an extrainfo document. Return NULL on
+ * failure. */
+static char *
+format_bridge_stats_extrainfo(time_t now)
+{
+ char *out = NULL, *data = NULL;
+ long duration = now - start_of_bridge_stats_interval;
+ char written[ISO_TIME_LEN+1];
+
+ if (duration < 0)
+ return NULL;
+
+ format_iso_time(written, now);
+ data = geoip_get_client_history(GEOIP_CLIENT_CONNECT);
+
+ tor_asprintf(&out,
+ "bridge-stats-end %s (%ld s)\n"
+ "bridge-ips %s\n",
+ written, duration,
+ data ? data : "");
+ tor_free(data);
+
+ return out;
+}
+
+/** Return a newly allocated string holding our bridge usage stats by country
+ * in a format suitable for the answer to a controller request. Return NULL on
+ * failure. */
+static char *
+format_bridge_stats_controller(time_t now)
+{
+ char *out = NULL, *data = NULL;
+ char started[ISO_TIME_LEN+1];
+ (void) now;
+
+ format_iso_time(started, start_of_bridge_stats_interval);
+ data = geoip_get_client_history(GEOIP_CLIENT_CONNECT);
+
+ tor_asprintf(&out,
+ "TimeStarted=\"%s\" CountrySummary=%s",
+ started, data ? data : "");
+ tor_free(data);
+ return out;
+}
/** Write bridge statistics to $DATADIR/stats/bridge-stats and return
* when we should next try to write statistics. */
time_t
geoip_bridge_stats_write(time_t now)
{
- char *statsdir = NULL, *filename = NULL, *data = NULL,
- written[ISO_TIME_LEN+1], *out = NULL, *controller_str;
- size_t len;
+ char *filename = NULL, *val = NULL, *statsdir = NULL;
/* Check if 24 hours have passed since starting measurements. */
if (now < start_of_bridge_stats_interval + WRITE_STATS_INTERVAL)
@@ -1169,64 +1196,54 @@ geoip_bridge_stats_write(time_t now)
/* Discard all items in the client history that are too old. */
geoip_remove_old_clients(start_of_bridge_stats_interval);
+ /* Generate formatted string */
+ val = format_bridge_stats_extrainfo(now);
+ if (val == NULL)
+ goto done;
+
+ /* Update the stored value. */
+ tor_free(bridge_stats_extrainfo);
+ bridge_stats_extrainfo = val;
+ start_of_bridge_stats_interval = now;
+
+ /* Write it to disk. */
statsdir = get_datadir_fname("stats");
if (check_private_dir(statsdir, CPD_CREATE) < 0)
goto done;
filename = get_datadir_fname2("stats", "bridge-stats");
- data = geoip_get_client_history(GEOIP_CLIENT_CONNECT);
- format_iso_time(written, now);
- len = strlen("bridge-stats-end (999999 s)\nbridge-ips \n") +
- ISO_TIME_LEN + (data ? strlen(data) : 0) + 42;
- out = tor_malloc(len);
- if (tor_snprintf(out, len, "bridge-stats-end %s (%u s)\nbridge-ips %s\n",
- written, (unsigned) (now - start_of_bridge_stats_interval),
- data ? data : "") < 0)
- goto done;
- write_str_to_file(filename, out, 0);
- controller_str = parse_bridge_stats_controller(out, now);
- if (!controller_str)
- goto done;
- start_of_bridge_stats_interval = now;
- tor_free(bridge_stats_extrainfo);
- tor_free(bridge_stats_controller);
- bridge_stats_extrainfo = out;
- out = NULL;
- bridge_stats_controller = controller_str;
- control_event_clients_seen(controller_str);
+
+ write_str_to_file(filename, bridge_stats_extrainfo, 0);
+
+ /* Tell the controller, "hey, there are clients!" */
+ {
+ char *controller_str = format_bridge_stats_controller(now);
+ if (controller_str)
+ control_event_clients_seen(controller_str);
+ tor_free(controller_str);
+ }
done:
tor_free(filename);
tor_free(statsdir);
- tor_free(data);
- tor_free(out);
- return start_of_bridge_stats_interval +
- WRITE_STATS_INTERVAL;
+
+ return start_of_bridge_stats_interval + WRITE_STATS_INTERVAL;
}
/** Try to load the most recent bridge statistics from disk, unless we
- * have finished a measurement interval lately. */
+ * have finished a measurement interval lately, and check whether they
+ * are still recent enough. */
static void
load_bridge_stats(time_t now)
{
- char *statsdir, *fname=NULL, *contents, *controller_str;
+ char *fname, *contents;
if (bridge_stats_extrainfo)
return;
- statsdir = get_datadir_fname("stats");
- if (check_private_dir(statsdir, CPD_CREATE) < 0)
- goto done;
+
fname = get_datadir_fname2("stats", "bridge-stats");
contents = read_file_to_str(fname, RFTS_IGNORE_MISSING, NULL);
- if (contents) {
- controller_str = parse_bridge_stats_controller(contents, now);
- if (controller_str) {
- bridge_stats_extrainfo = contents;
- bridge_stats_controller = controller_str;
- } else {
- tor_free(contents);
- }
- }
- done:
+ if (contents && validate_bridge_stats(contents, now))
+ bridge_stats_extrainfo = contents;
+
tor_free(fname);
- tor_free(statsdir);
}
/** Return most recent bridge statistics for inclusion in extra-info
@@ -1238,13 +1255,12 @@ geoip_get_bridge_stats_extrainfo(time_t now)
return bridge_stats_extrainfo;
}
-/** Return most recent bridge statistics to be returned to controller
- * clients, or NULL if we don't have recent bridge statistics. */
-const char *
+/** Return a new string containing the recent bridge statistics to be returned
+ * to controller clients, or NULL if we don't have any bridge statistics. */
+char *
geoip_get_bridge_stats_controller(time_t now)
{
- load_bridge_stats(now);
- return bridge_stats_controller;
+ return format_bridge_stats_controller(now);
}
/** Start time of entry stats or 0 if we're not collecting entry
diff --git a/src/or/geoip.h b/src/or/geoip.h
index bafbeea..24f7c5b 100644
--- a/src/or/geoip.h
+++ b/src/or/geoip.h
@@ -51,7 +51,7 @@ void geoip_bridge_stats_init(time_t now);
time_t geoip_bridge_stats_write(time_t now);
void geoip_bridge_stats_term(void);
const char *geoip_get_bridge_stats_extrainfo(time_t);
-const char *geoip_get_bridge_stats_controller(time_t);
+char *geoip_get_bridge_stats_controller(time_t);
#endif
More information about the tor-commits
mailing list