[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [tor/master] Split the or_state_t portions of config.c into their own file
commit 7d11952bf4b7bda604e503485036b1a80edf3fd9
Author: Nick Mathewson <nickm@xxxxxxxxxxxxxx>
Date: Wed Sep 12 17:58:33 2012 -0400
Split the or_state_t portions of config.c into their own file
---
src/or/circuitbuild.c | 1 +
src/or/config.c | 576 +----------------------------------------------
src/or/config.h | 8 -
src/or/hibernate.c | 1 +
src/or/include.am | 2 +
src/or/main.c | 2 +
src/or/router.c | 1 +
src/or/statefile.c | 606 +++++++++++++++++++++++++++++++++++++++++++++++++
src/or/statefile.h | 22 ++
src/or/transports.c | 1 +
10 files changed, 638 insertions(+), 582 deletions(-)
diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c
index ae44de0..e9d010e 100644
--- a/src/or/circuitbuild.c
+++ b/src/or/circuitbuild.c
@@ -33,6 +33,7 @@
#include "router.h"
#include "routerlist.h"
#include "routerparse.h"
+#include "statefile.h"
#include "crypto.h"
#undef log
#include <math.h>
diff --git a/src/or/config.c b/src/or/config.c
index ce495f3..3536db6 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -36,6 +36,7 @@
#include "router.h"
#include "util.h"
#include "routerlist.h"
+#include "statefile.h"
#include "transports.h"
#ifdef _WIN32
#include <shlobj.h>
@@ -93,18 +94,6 @@ static config_abbrev_t _option_abbrevs[] = {
{ NULL, NULL, 0, 0},
};
-/** A list of state-file "abbreviations," for compatibility. */
-static config_abbrev_t _state_abbrevs[] = {
- { "AccountingBytesReadInterval", "AccountingBytesReadInInterval", 0, 0 },
- { "HelperNode", "EntryGuard", 0, 0 },
- { "HelperNodeDownSince", "EntryGuardDownSince", 0, 0 },
- { "HelperNodeUnlistedSince", "EntryGuardUnlistedSince", 0, 0 },
- { "EntryNode", "EntryGuard", 0, 0 },
- { "EntryNodeDownSince", "EntryGuardDownSince", 0, 0 },
- { "EntryNodeUnlistedSince", "EntryGuardUnlistedSince", 0, 0 },
- { NULL, NULL, 0, 0},
-};
-
/** An entry for config_vars: "The option <b>name</b> has type
* CONFIG_TYPE_<b>conftype</b>, and corresponds to
* or_options_t.<b>member</b>"
@@ -441,63 +430,6 @@ static const config_var_t testing_tor_network_defaults[] = {
{ NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
};
-#undef VAR
-
-#define VAR(name,conftype,member,initvalue) \
- { name, CONFIG_TYPE_ ## conftype, STRUCT_OFFSET(or_state_t, member), \
- initvalue }
-
-/** Array of "state" variables saved to the ~/.tor/state file. */
-static config_var_t _state_vars[] = {
- /* Remember to document these in state-contents.txt ! */
-
- V(AccountingBytesReadInInterval, MEMUNIT, NULL),
- V(AccountingBytesWrittenInInterval, MEMUNIT, NULL),
- V(AccountingExpectedUsage, MEMUNIT, NULL),
- V(AccountingIntervalStart, ISOTIME, NULL),
- V(AccountingSecondsActive, INTERVAL, NULL),
- V(AccountingSecondsToReachSoftLimit,INTERVAL, NULL),
- V(AccountingSoftLimitHitAt, ISOTIME, NULL),
- V(AccountingBytesAtSoftLimit, MEMUNIT, NULL),
-
- VAR("EntryGuard", LINELIST_S, EntryGuards, NULL),
- VAR("EntryGuardDownSince", LINELIST_S, EntryGuards, NULL),
- VAR("EntryGuardUnlistedSince", LINELIST_S, EntryGuards, NULL),
- VAR("EntryGuardAddedBy", LINELIST_S, EntryGuards, NULL),
- VAR("EntryGuardPathBias", LINELIST_S, EntryGuards, NULL),
- V(EntryGuards, LINELIST_V, NULL),
-
- VAR("TransportProxy", LINELIST_S, TransportProxies, NULL),
- V(TransportProxies, LINELIST_V, NULL),
-
- V(BWHistoryReadEnds, ISOTIME, NULL),
- V(BWHistoryReadInterval, UINT, "900"),
- V(BWHistoryReadValues, CSV, ""),
- V(BWHistoryReadMaxima, CSV, ""),
- V(BWHistoryWriteEnds, ISOTIME, NULL),
- V(BWHistoryWriteInterval, UINT, "900"),
- V(BWHistoryWriteValues, CSV, ""),
- V(BWHistoryWriteMaxima, CSV, ""),
- V(BWHistoryDirReadEnds, ISOTIME, NULL),
- V(BWHistoryDirReadInterval, UINT, "900"),
- V(BWHistoryDirReadValues, CSV, ""),
- V(BWHistoryDirReadMaxima, CSV, ""),
- V(BWHistoryDirWriteEnds, ISOTIME, NULL),
- V(BWHistoryDirWriteInterval, UINT, "900"),
- V(BWHistoryDirWriteValues, CSV, ""),
- V(BWHistoryDirWriteMaxima, CSV, ""),
-
- V(TorVersion, STRING, NULL),
-
- V(LastRotatedOnionKey, ISOTIME, NULL),
- V(LastWritten, ISOTIME, NULL),
-
- V(TotalBuildTimes, UINT, NULL),
- V(CircuitBuildAbandonedCount, UINT, "0"),
- VAR("CircuitBuildTimeBin", LINELIST_S, BuildtimeHistogram, NULL),
- VAR("BuildtimeHistogram", LINELIST_V, BuildtimeHistogram, NULL),
- { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
-};
#undef VAR
#undef V
@@ -536,9 +468,6 @@ static int check_server_ports(const smartlist_t *ports,
static int validate_data_directory(or_options_t *options);
static int write_configuration_file(const char *fname,
const or_options_t *options);
-static int or_state_validate(or_state_t *old_options, or_state_t *options,
- int from_setconf, char **msg);
-static int or_state_load(void);
static int options_init_logs(or_options_t *options, int validate_only);
static void init_libevent(const or_options_t *options);
@@ -558,26 +487,6 @@ static config_format_t options_format = {
NULL
};
-/** Magic value for or_state_t. */
-#define OR_STATE_MAGIC 0x57A73f57
-
-/** "Extra" variable in the state that receives lines we can't parse. This
- * lets us preserve options from versions of Tor newer than us. */
-static config_var_t state_extra_var = {
- "__extra", CONFIG_TYPE_LINELIST, STRUCT_OFFSET(or_state_t, ExtraLines), NULL
-};
-
-/** Configuration format for or_state_t. */
-static const config_format_t state_format = {
- sizeof(or_state_t),
- OR_STATE_MAGIC,
- STRUCT_OFFSET(or_state_t, _magic),
- _state_abbrevs,
- _state_vars,
- (validate_fn_t)or_state_validate,
- &state_extra_var,
-};
-
/*
* Functions to read and write the global options pointer.
*/
@@ -591,8 +500,6 @@ static or_options_t *global_default_options = NULL;
static char *torrc_fname = NULL;
/** Name of the most recently read torrc-defaults file.*/
static char *torrc_defaults_fname;
-/** Persistent serialized state. */
-static or_state_t *global_state = NULL;
/** Configuration Options set by command line. */
static config_line_t *global_cmdline_options = NULL;
/** Contents of most recently read DirPortFrontPage file. */
@@ -748,9 +655,6 @@ config_free_all(void)
or_options_free(global_default_options);
global_default_options = NULL;
- config_free(&state_format, global_state);
- global_state = NULL;
-
config_free_lines(global_cmdline_options);
global_cmdline_options = NULL;
@@ -1336,7 +1240,7 @@ options_act(const or_options_t *old_options)
}
/* Load state */
- if (! global_state && running_tor) {
+ if (! or_state_loaded() && running_tor) {
if (or_state_load())
return -1;
rep_hist_load_mtbf_data(time(NULL));
@@ -5391,14 +5295,6 @@ init_libevent(const or_options_t *options)
}
}
-/** Return the persistent state struct for this Tor. */
-or_state_t *
-get_or_state(void)
-{
- tor_assert(global_state);
- return global_state;
-}
-
/** Return a newly allocated string holding a filename relative to the data
* directory. If <b>sub1</b> is present, it is the first path component after
* the data directory. If <b>sub2</b> is also present, it is the second path
@@ -5449,474 +5345,6 @@ options_get_datadir_fname2_suffix(const or_options_t *options,
return fname;
}
-/** Return true if <b>line</b> is a valid state TransportProxy line.
- * Return false otherwise. */
-static int
-state_transport_line_is_valid(const char *line)
-{
- smartlist_t *items = NULL;
- char *addrport=NULL;
- tor_addr_t addr;
- uint16_t port = 0;
- int r;
-
- items = smartlist_new();
- smartlist_split_string(items, line, NULL,
- SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
-
- if (smartlist_len(items) != 2) {
- log_warn(LD_CONFIG, "state: Not enough arguments in TransportProxy line.");
- goto err;
- }
-
- addrport = smartlist_get(items, 1);
- if (tor_addr_port_lookup(addrport, &addr, &port) < 0) {
- log_warn(LD_CONFIG, "state: Could not parse addrport.");
- goto err;
- }
-
- if (!port) {
- log_warn(LD_CONFIG, "state: Transport line did not contain port.");
- goto err;
- }
-
- r = 1;
- goto done;
-
- err:
- r = 0;
-
- done:
- SMARTLIST_FOREACH(items, char*, s, tor_free(s));
- smartlist_free(items);
- return r;
-}
-
-/** Return 0 if all TransportProxy lines in <b>state</b> are well
- * formed. Otherwise, return -1. */
-static int
-validate_transports_in_state(or_state_t *state)
-{
- int broken = 0;
- config_line_t *line;
-
- for (line = state->TransportProxies ; line ; line = line->next) {
- tor_assert(!strcmp(line->key, "TransportProxy"));
- if (!state_transport_line_is_valid(line->value))
- broken = 1;
- }
-
- if (broken)
- log_warn(LD_CONFIG, "state: State file seems to be broken.");
-
- return 0;
-}
-
-/** Return 0 if every setting in <b>state</b> is reasonable, and a
- * permissible transition from <b>old_state</b>. Else warn and return -1.
- * Should have no side effects, except for normalizing the contents of
- * <b>state</b>.
- */
-/* XXX from_setconf is here because of bug 238 */
-static int
-or_state_validate(or_state_t *old_state, or_state_t *state,
- int from_setconf, char **msg)
-{
- /* We don't use these; only options do. Still, we need to match that
- * signature. */
- (void) from_setconf;
- (void) old_state;
-
- if (entry_guards_parse_state(state, 0, msg)<0)
- return -1;
-
- if (validate_transports_in_state(state)<0)
- return -1;
-
- return 0;
-}
-
-/** Replace the current persistent state with <b>new_state</b> */
-static int
-or_state_set(or_state_t *new_state)
-{
- char *err = NULL;
- int ret = 0;
- tor_assert(new_state);
- config_free(&state_format, global_state);
- global_state = new_state;
- if (entry_guards_parse_state(global_state, 1, &err)<0) {
- log_warn(LD_GENERAL,"%s",err);
- tor_free(err);
- ret = -1;
- }
- if (rep_hist_load_state(global_state, &err)<0) {
- log_warn(LD_GENERAL,"Unparseable bandwidth history state: %s",err);
- tor_free(err);
- ret = -1;
- }
- if (circuit_build_times_parse_state(&circ_times, global_state) < 0) {
- ret = -1;
- }
- return ret;
-}
-
-/**
- * Save a broken state file to a backup location.
- */
-static void
-or_state_save_broken(char *fname)
-{
- int i;
- file_status_t status;
- char *fname2 = NULL;
- for (i = 0; i < 100; ++i) {
- tor_asprintf(&fname2, "%s.%d", fname, i);
- status = file_status(fname2);
- if (status == FN_NOENT)
- break;
- tor_free(fname2);
- }
- if (i == 100) {
- log_warn(LD_BUG, "Unable to parse state in \"%s\"; too many saved bad "
- "state files to move aside. Discarding the old state file.",
- fname);
- unlink(fname);
- } else {
- log_warn(LD_BUG, "Unable to parse state in \"%s\". Moving it aside "
- "to \"%s\". This could be a bug in Tor; please tell "
- "the developers.", fname, fname2);
- if (rename(fname, fname2) < 0) {
- log_warn(LD_BUG, "Weirdly, I couldn't even move the state aside. The "
- "OS gave an error of %s", strerror(errno));
- }
- }
- tor_free(fname2);
-}
-
-/** Reload the persistent state from disk, generating a new state as needed.
- * Return 0 on success, less than 0 on failure.
- */
-static int
-or_state_load(void)
-{
- or_state_t *new_state = NULL;
- char *contents = NULL, *fname;
- char *errmsg = NULL;
- int r = -1, badstate = 0;
-
- fname = get_datadir_fname("state");
- switch (file_status(fname)) {
- case FN_FILE:
- if (!(contents = read_file_to_str(fname, 0, NULL))) {
- log_warn(LD_FS, "Unable to read state file \"%s\"", fname);
- goto done;
- }
- break;
- case FN_NOENT:
- break;
- case FN_ERROR:
- case FN_DIR:
- default:
- log_warn(LD_GENERAL,"State file \"%s\" is not a file? Failing.", fname);
- goto done;
- }
- new_state = tor_malloc_zero(sizeof(or_state_t));
- new_state->_magic = OR_STATE_MAGIC;
- config_init(&state_format, new_state);
- if (contents) {
- config_line_t *lines=NULL;
- int assign_retval;
- if (config_get_lines(contents, &lines, 0)<0)
- goto done;
- assign_retval = config_assign(&state_format, new_state,
- lines, 0, 0, &errmsg);
- config_free_lines(lines);
- if (assign_retval<0)
- badstate = 1;
- if (errmsg) {
- log_warn(LD_GENERAL, "%s", errmsg);
- tor_free(errmsg);
- }
- }
-
- if (!badstate && or_state_validate(NULL, new_state, 1, &errmsg) < 0)
- badstate = 1;
-
- if (errmsg) {
- log_warn(LD_GENERAL, "%s", errmsg);
- tor_free(errmsg);
- }
-
- if (badstate && !contents) {
- log_warn(LD_BUG, "Uh oh. We couldn't even validate our own default state."
- " This is a bug in Tor.");
- goto done;
- } else if (badstate && contents) {
- or_state_save_broken(fname);
-
- tor_free(contents);
- config_free(&state_format, new_state);
-
- new_state = tor_malloc_zero(sizeof(or_state_t));
- new_state->_magic = OR_STATE_MAGIC;
- config_init(&state_format, new_state);
- } else if (contents) {
- log_info(LD_GENERAL, "Loaded state from \"%s\"", fname);
- } else {
- log_info(LD_GENERAL, "Initialized state");
- }
- if (or_state_set(new_state) == -1) {
- or_state_save_broken(fname);
- }
- new_state = NULL;
- if (!contents) {
- global_state->next_write = 0;
- or_state_save(time(NULL));
- }
- r = 0;
-
- done:
- tor_free(fname);
- tor_free(contents);
- if (new_state)
- config_free(&state_format, new_state);
-
- return r;
-}
-
-/** Did the last time we tried to write the state file fail? If so, we
- * should consider disabling such features as preemptive circuit generation
- * to compute circuit-build-time. */
-static int last_state_file_write_failed = 0;
-
-/** Return whether the state file failed to write last time we tried. */
-int
-did_last_state_file_write_fail(void)
-{
- return last_state_file_write_failed;
-}
-
-/** If writing the state to disk fails, try again after this many seconds. */
-#define STATE_WRITE_RETRY_INTERVAL 3600
-
-/** If we're a relay, how often should we checkpoint our state file even
- * if nothing else dirties it? This will checkpoint ongoing stats like
- * bandwidth used, per-country user stats, etc. */
-#define STATE_RELAY_CHECKPOINT_INTERVAL (12*60*60)
-
-/** Write the persistent state to disk. Return 0 for success, <0 on failure. */
-int
-or_state_save(time_t now)
-{
- char *state, *contents;
- char tbuf[ISO_TIME_LEN+1];
- char *fname;
-
- tor_assert(global_state);
-
- if (global_state->next_write > now)
- return 0;
-
- /* Call everything else that might dirty the state even more, in order
- * to avoid redundant writes. */
- entry_guards_update_state(global_state);
- rep_hist_update_state(global_state);
- circuit_build_times_update_state(&circ_times, global_state);
- if (accounting_is_enabled(get_options()))
- accounting_run_housekeeping(now);
-
- global_state->LastWritten = now;
-
- tor_free(global_state->TorVersion);
- tor_asprintf(&global_state->TorVersion, "Tor %s", get_version());
-
- state = config_dump(&state_format, NULL, global_state, 1, 0);
- format_local_iso_time(tbuf, now);
- tor_asprintf(&contents,
- "# Tor state file last generated on %s local time\n"
- "# Other times below are in GMT\n"
- "# You *do not* need to edit this file.\n\n%s",
- tbuf, state);
- tor_free(state);
- fname = get_datadir_fname("state");
- if (write_str_to_file(fname, contents, 0)<0) {
- log_warn(LD_FS, "Unable to write state to file \"%s\"; "
- "will try again later", fname);
- last_state_file_write_failed = 1;
- tor_free(fname);
- tor_free(contents);
- /* Try again after STATE_WRITE_RETRY_INTERVAL (or sooner, if the state
- * changes sooner). */
- global_state->next_write = now + STATE_WRITE_RETRY_INTERVAL;
- return -1;
- }
-
- last_state_file_write_failed = 0;
- log_info(LD_GENERAL, "Saved state to \"%s\"", fname);
- tor_free(fname);
- tor_free(contents);
-
- if (server_mode(get_options()))
- global_state->next_write = now + STATE_RELAY_CHECKPOINT_INTERVAL;
- else
- global_state->next_write = TIME_MAX;
-
- return 0;
-}
-
-/** Return the config line for transport <b>transport</b> in the current state.
- * Return NULL if there is no config line for <b>transport</b>. */
-static config_line_t *
-get_transport_in_state_by_name(const char *transport)
-{
- or_state_t *or_state = get_or_state();
- config_line_t *line;
- config_line_t *ret = NULL;
- smartlist_t *items = NULL;
-
- for (line = or_state->TransportProxies ; line ; line = line->next) {
- tor_assert(!strcmp(line->key, "TransportProxy"));
-
- items = smartlist_new();
- smartlist_split_string(items, line->value, NULL,
- SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
- if (smartlist_len(items) != 2) /* broken state */
- goto done;
-
- if (!strcmp(smartlist_get(items, 0), transport)) {
- ret = line;
- goto done;
- }
-
- SMARTLIST_FOREACH(items, char*, s, tor_free(s));
- smartlist_free(items);
- items = NULL;
- }
-
- done:
- if (items) {
- SMARTLIST_FOREACH(items, char*, s, tor_free(s));
- smartlist_free(items);
- }
- return ret;
-}
-
-/** Return string containing the address:port part of the
- * TransportProxy <b>line</b> for transport <b>transport</b>.
- * If the line is corrupted, return NULL. */
-static const char *
-get_transport_bindaddr(const char *line, const char *transport)
-{
- char *line_tmp = NULL;
-
- if (strlen(line) < strlen(transport) + 2) {
- goto broken_state;
- } else {
- /* line should start with the name of the transport and a space.
- (for example, "obfs2 127.0.0.1:47245") */
- tor_asprintf(&line_tmp, "%s ", transport);
- if (strcmpstart(line, line_tmp))
- goto broken_state;
-
- tor_free(line_tmp);
- return (line+strlen(transport)+1);
- }
-
- broken_state:
- tor_free(line_tmp);
- return NULL;
-}
-
-/** Return a string containing the address:port that a proxy transport
- * should bind on. The string is stored on the heap and must be freed
- * by the caller of this function. */
-char *
-get_stored_bindaddr_for_server_transport(const char *transport)
-{
- char *default_addrport = NULL;
- const char *stored_bindaddr = NULL;
-
- config_line_t *line = get_transport_in_state_by_name(transport);
- if (!line) /* Found no references in state for this transport. */
- goto no_bindaddr_found;
-
- stored_bindaddr = get_transport_bindaddr(line->value, transport);
- if (stored_bindaddr) /* found stored bindaddr in state file. */
- return tor_strdup(stored_bindaddr);
-
- no_bindaddr_found:
- /** If we didn't find references for this pluggable transport in the
- state file, we should instruct the pluggable transport proxy to
- listen on INADDR_ANY on a random ephemeral port. */
- tor_asprintf(&default_addrport, "%s:%s", fmt_addr32(INADDR_ANY), "0");
- return default_addrport;
-}
-
-/** Save <b>transport</b> listening on <b>addr</b>:<b>port</b> to
- state */
-void
-save_transport_to_state(const char *transport,
- const tor_addr_t *addr, uint16_t port)
-{
- or_state_t *state = get_or_state();
-
- char *transport_addrport=NULL;
-
- /** find where to write on the state */
- config_line_t **next, *line;
-
- /* see if this transport is already stored in state */
- config_line_t *transport_line =
- get_transport_in_state_by_name(transport);
-
- if (transport_line) { /* if transport already exists in state... */
- const char *prev_bindaddr = /* get its addrport... */
- get_transport_bindaddr(transport_line->value, transport);
- tor_asprintf(&transport_addrport, "%s:%d", fmt_addr(addr), (int)port);
-
- /* if transport in state has the same address as this one, life is good */
- if (!strcmp(prev_bindaddr, transport_addrport)) {
- log_info(LD_CONFIG, "Transport seems to have spawned on its usual "
- "address:port.");
- goto done;
- } else { /* if addrport in state is different than the one we got */
- log_info(LD_CONFIG, "Transport seems to have spawned on different "
- "address:port. Let's update the state file with the new "
- "address:port");
- tor_free(transport_line->value); /* free the old line */
- tor_asprintf(&transport_line->value, "%s %s:%d", transport,
- fmt_addr(addr),
- (int) port); /* replace old addrport line with new line */
- }
- } else { /* never seen this one before; save it in state for next time */
- log_info(LD_CONFIG, "It's the first time we see this transport. "
- "Let's save its address:port");
- next = &state->TransportProxies;
- /* find the last TransportProxy line in the state and point 'next'
- right after it */
- line = state->TransportProxies;
- while (line) {
- next = &(line->next);
- line = line->next;
- }
-
- /* allocate space for the new line and fill it in */
- *next = line = tor_malloc_zero(sizeof(config_line_t));
- line->key = tor_strdup("TransportProxy");
- tor_asprintf(&line->value, "%s %s:%d", transport,
- fmt_addr(addr), (int) port);
-
- next = &(line->next);
- }
-
- if (!get_options()->AvoidDiskWrites)
- or_state_mark_dirty(state, 0);
-
- done:
- tor_free(transport_addrport);
-}
-
/** Given a file name check to see whether the file exists but has not been
* modified for a very long time. If so, remove it. */
void
diff --git a/src/or/config.h b/src/or/config.h
index abfbef7..9d170b8 100644
--- a/src/or/config.h
+++ b/src/or/config.h
@@ -59,10 +59,6 @@ char *options_get_datadir_fname2_suffix(const or_options_t *options,
int get_num_cpus(const or_options_t *options);
-or_state_t *get_or_state(void);
-int did_last_state_file_write_fail(void);
-int or_state_save(time_t now);
-
const smartlist_t *get_configured_ports(void);
int get_first_advertised_port_by_type_af(int listener_type,
int address_family);
@@ -76,10 +72,6 @@ char *get_first_listener_addrport_string(int listener_type);
int options_need_geoip_info(const or_options_t *options,
const char **reason_out);
-void save_transport_to_state(const char *transport_name,
- const tor_addr_t *addr, uint16_t port);
-char *get_stored_bindaddr_for_server_transport(const char *transport);
-
smartlist_t *get_list_of_ports_to_forward(void);
int getinfo_helper_config(control_connection_t *conn,
diff --git a/src/or/hibernate.c b/src/or/hibernate.c
index 3a9c1e4..b33e5e2 100644
--- a/src/or/hibernate.c
+++ b/src/or/hibernate.c
@@ -29,6 +29,7 @@ hibernating, phase 2:
#include "hibernate.h"
#include "main.h"
#include "router.h"
+#include "statefile.h"
extern long stats_n_seconds_working; /* published uptime */
diff --git a/src/or/include.am b/src/or/include.am
index fc41c6d..5c93e3e 100644
--- a/src/or/include.am
+++ b/src/or/include.am
@@ -53,6 +53,7 @@ src_or_libtor_a_SOURCES = \
src/or/router.c \
src/or/routerlist.c \
src/or/routerparse.c \
+ src/or/statefile.c \
src/or/status.c \
$(evdns_source) \
$(tor_platform_source) \
@@ -124,6 +125,7 @@ ORHEADERS = \
src/or/router.h \
src/or/routerlist.h \
src/or/routerparse.h \
+ src/or/statefile.h \
src/or/status.h
noinst_HEADERS+= $(ORHEADERS) micro-revision.i
diff --git a/src/or/main.c b/src/or/main.c
index 39eccd6..01da7a4 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -46,6 +46,7 @@
#include "router.h"
#include "routerlist.h"
#include "routerparse.h"
+#include "statefile.h"
#include "status.h"
#ifdef USE_DMALLOC
#include <dmalloc.h>
@@ -2458,6 +2459,7 @@ tor_free_all(int postfork)
microdesc_free_all();
if (!postfork) {
config_free_all();
+ or_state_free_all();
router_free_all();
policies_free_all();
}
diff --git a/src/or/router.c b/src/or/router.c
index a26dccc..91209ee 100644
--- a/src/or/router.c
+++ b/src/or/router.c
@@ -27,6 +27,7 @@
#include "router.h"
#include "routerlist.h"
#include "routerparse.h"
+#include "statefile.h"
#include "transports.h"
/**
diff --git a/src/or/statefile.c b/src/or/statefile.c
new file mode 100644
index 0000000..499572a
--- /dev/null
+++ b/src/or/statefile.c
@@ -0,0 +1,606 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2012, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "or.h"
+#include "circuitbuild.h"
+#include "config.h"
+#include "confparse.h"
+#include "hibernate.h"
+#include "rephist.h"
+#include "router.h"
+#include "statefile.h"
+
+/** A list of state-file "abbreviations," for compatibility. */
+static config_abbrev_t _state_abbrevs[] = {
+ { "AccountingBytesReadInterval", "AccountingBytesReadInInterval", 0, 0 },
+ { "HelperNode", "EntryGuard", 0, 0 },
+ { "HelperNodeDownSince", "EntryGuardDownSince", 0, 0 },
+ { "HelperNodeUnlistedSince", "EntryGuardUnlistedSince", 0, 0 },
+ { "EntryNode", "EntryGuard", 0, 0 },
+ { "EntryNodeDownSince", "EntryGuardDownSince", 0, 0 },
+ { "EntryNodeUnlistedSince", "EntryGuardUnlistedSince", 0, 0 },
+ { NULL, NULL, 0, 0},
+};
+
+/*XXXX these next two are duplicates or near-duplicates from config.c */
+#define VAR(name,conftype,member,initvalue) \
+ { name, CONFIG_TYPE_ ## conftype, STRUCT_OFFSET(or_state_t, member), \
+ initvalue }
+/** As VAR, but the option name and member name are the same. */
+#define V(member,conftype,initvalue) \
+ VAR(#member, conftype, member, initvalue)
+
+/** Array of "state" variables saved to the ~/.tor/state file. */
+static config_var_t _state_vars[] = {
+ /* Remember to document these in state-contents.txt ! */
+
+ V(AccountingBytesReadInInterval, MEMUNIT, NULL),
+ V(AccountingBytesWrittenInInterval, MEMUNIT, NULL),
+ V(AccountingExpectedUsage, MEMUNIT, NULL),
+ V(AccountingIntervalStart, ISOTIME, NULL),
+ V(AccountingSecondsActive, INTERVAL, NULL),
+ V(AccountingSecondsToReachSoftLimit,INTERVAL, NULL),
+ V(AccountingSoftLimitHitAt, ISOTIME, NULL),
+ V(AccountingBytesAtSoftLimit, MEMUNIT, NULL),
+
+ VAR("EntryGuard", LINELIST_S, EntryGuards, NULL),
+ VAR("EntryGuardDownSince", LINELIST_S, EntryGuards, NULL),
+ VAR("EntryGuardUnlistedSince", LINELIST_S, EntryGuards, NULL),
+ VAR("EntryGuardAddedBy", LINELIST_S, EntryGuards, NULL),
+ VAR("EntryGuardPathBias", LINELIST_S, EntryGuards, NULL),
+ V(EntryGuards, LINELIST_V, NULL),
+
+ VAR("TransportProxy", LINELIST_S, TransportProxies, NULL),
+ V(TransportProxies, LINELIST_V, NULL),
+
+ V(BWHistoryReadEnds, ISOTIME, NULL),
+ V(BWHistoryReadInterval, UINT, "900"),
+ V(BWHistoryReadValues, CSV, ""),
+ V(BWHistoryReadMaxima, CSV, ""),
+ V(BWHistoryWriteEnds, ISOTIME, NULL),
+ V(BWHistoryWriteInterval, UINT, "900"),
+ V(BWHistoryWriteValues, CSV, ""),
+ V(BWHistoryWriteMaxima, CSV, ""),
+ V(BWHistoryDirReadEnds, ISOTIME, NULL),
+ V(BWHistoryDirReadInterval, UINT, "900"),
+ V(BWHistoryDirReadValues, CSV, ""),
+ V(BWHistoryDirReadMaxima, CSV, ""),
+ V(BWHistoryDirWriteEnds, ISOTIME, NULL),
+ V(BWHistoryDirWriteInterval, UINT, "900"),
+ V(BWHistoryDirWriteValues, CSV, ""),
+ V(BWHistoryDirWriteMaxima, CSV, ""),
+
+ V(TorVersion, STRING, NULL),
+
+ V(LastRotatedOnionKey, ISOTIME, NULL),
+ V(LastWritten, ISOTIME, NULL),
+
+ V(TotalBuildTimes, UINT, NULL),
+ V(CircuitBuildAbandonedCount, UINT, "0"),
+ VAR("CircuitBuildTimeBin", LINELIST_S, BuildtimeHistogram, NULL),
+ VAR("BuildtimeHistogram", LINELIST_V, BuildtimeHistogram, NULL),
+ { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
+};
+
+#undef VAR
+#undef V
+
+static int or_state_validate(or_state_t *old_options, or_state_t *options,
+ int from_setconf, char **msg);
+
+/** Magic value for or_state_t. */
+#define OR_STATE_MAGIC 0x57A73f57
+
+/** "Extra" variable in the state that receives lines we can't parse. This
+ * lets us preserve options from versions of Tor newer than us. */
+static config_var_t state_extra_var = {
+ "__extra", CONFIG_TYPE_LINELIST, STRUCT_OFFSET(or_state_t, ExtraLines), NULL
+};
+
+/** Configuration format for or_state_t. */
+static const config_format_t state_format = {
+ sizeof(or_state_t),
+ OR_STATE_MAGIC,
+ STRUCT_OFFSET(or_state_t, _magic),
+ _state_abbrevs,
+ _state_vars,
+ (validate_fn_t)or_state_validate,
+ &state_extra_var,
+};
+
+/** Persistent serialized state. */
+static or_state_t *global_state = NULL;
+
+/** Return the persistent state struct for this Tor. */
+or_state_t *
+get_or_state(void)
+{
+ tor_assert(global_state);
+ return global_state;
+}
+
+/** Return true iff we have loaded the global state for this Tor */
+int
+or_state_loaded(void)
+{
+ return global_state != NULL;
+}
+
+/** Return true if <b>line</b> is a valid state TransportProxy line.
+ * Return false otherwise. */
+static int
+state_transport_line_is_valid(const char *line)
+{
+ smartlist_t *items = NULL;
+ char *addrport=NULL;
+ tor_addr_t addr;
+ uint16_t port = 0;
+ int r;
+
+ items = smartlist_new();
+ smartlist_split_string(items, line, NULL,
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
+
+ if (smartlist_len(items) != 2) {
+ log_warn(LD_CONFIG, "state: Not enough arguments in TransportProxy line.");
+ goto err;
+ }
+
+ addrport = smartlist_get(items, 1);
+ if (tor_addr_port_lookup(addrport, &addr, &port) < 0) {
+ log_warn(LD_CONFIG, "state: Could not parse addrport.");
+ goto err;
+ }
+
+ if (!port) {
+ log_warn(LD_CONFIG, "state: Transport line did not contain port.");
+ goto err;
+ }
+
+ r = 1;
+ goto done;
+
+ err:
+ r = 0;
+
+ done:
+ SMARTLIST_FOREACH(items, char*, s, tor_free(s));
+ smartlist_free(items);
+ return r;
+}
+
+/** Return 0 if all TransportProxy lines in <b>state</b> are well
+ * formed. Otherwise, return -1. */
+static int
+validate_transports_in_state(or_state_t *state)
+{
+ int broken = 0;
+ config_line_t *line;
+
+ for (line = state->TransportProxies ; line ; line = line->next) {
+ tor_assert(!strcmp(line->key, "TransportProxy"));
+ if (!state_transport_line_is_valid(line->value))
+ broken = 1;
+ }
+
+ if (broken)
+ log_warn(LD_CONFIG, "state: State file seems to be broken.");
+
+ return 0;
+}
+
+/** Return 0 if every setting in <b>state</b> is reasonable, and a
+ * permissible transition from <b>old_state</b>. Else warn and return -1.
+ * Should have no side effects, except for normalizing the contents of
+ * <b>state</b>.
+ */
+/* XXX from_setconf is here because of bug 238 */
+static int
+or_state_validate(or_state_t *old_state, or_state_t *state,
+ int from_setconf, char **msg)
+{
+ /* We don't use these; only options do. Still, we need to match that
+ * signature. */
+ (void) from_setconf;
+ (void) old_state;
+
+ if (entry_guards_parse_state(state, 0, msg)<0)
+ return -1;
+
+ if (validate_transports_in_state(state)<0)
+ return -1;
+
+ return 0;
+}
+
+/** Replace the current persistent state with <b>new_state</b> */
+static int
+or_state_set(or_state_t *new_state)
+{
+ char *err = NULL;
+ int ret = 0;
+ tor_assert(new_state);
+ config_free(&state_format, global_state);
+ global_state = new_state;
+ if (entry_guards_parse_state(global_state, 1, &err)<0) {
+ log_warn(LD_GENERAL,"%s",err);
+ tor_free(err);
+ ret = -1;
+ }
+ if (rep_hist_load_state(global_state, &err)<0) {
+ log_warn(LD_GENERAL,"Unparseable bandwidth history state: %s",err);
+ tor_free(err);
+ ret = -1;
+ }
+ if (circuit_build_times_parse_state(&circ_times, global_state) < 0) {
+ ret = -1;
+ }
+ return ret;
+}
+
+/**
+ * Save a broken state file to a backup location.
+ */
+static void
+or_state_save_broken(char *fname)
+{
+ int i;
+ file_status_t status;
+ char *fname2 = NULL;
+ for (i = 0; i < 100; ++i) {
+ tor_asprintf(&fname2, "%s.%d", fname, i);
+ status = file_status(fname2);
+ if (status == FN_NOENT)
+ break;
+ tor_free(fname2);
+ }
+ if (i == 100) {
+ log_warn(LD_BUG, "Unable to parse state in \"%s\"; too many saved bad "
+ "state files to move aside. Discarding the old state file.",
+ fname);
+ unlink(fname);
+ } else {
+ log_warn(LD_BUG, "Unable to parse state in \"%s\". Moving it aside "
+ "to \"%s\". This could be a bug in Tor; please tell "
+ "the developers.", fname, fname2);
+ if (rename(fname, fname2) < 0) {
+ log_warn(LD_BUG, "Weirdly, I couldn't even move the state aside. The "
+ "OS gave an error of %s", strerror(errno));
+ }
+ }
+ tor_free(fname2);
+}
+
+/** Reload the persistent state from disk, generating a new state as needed.
+ * Return 0 on success, less than 0 on failure.
+ */
+int
+or_state_load(void)
+{
+ or_state_t *new_state = NULL;
+ char *contents = NULL, *fname;
+ char *errmsg = NULL;
+ int r = -1, badstate = 0;
+
+ fname = get_datadir_fname("state");
+ switch (file_status(fname)) {
+ case FN_FILE:
+ if (!(contents = read_file_to_str(fname, 0, NULL))) {
+ log_warn(LD_FS, "Unable to read state file \"%s\"", fname);
+ goto done;
+ }
+ break;
+ case FN_NOENT:
+ break;
+ case FN_ERROR:
+ case FN_DIR:
+ default:
+ log_warn(LD_GENERAL,"State file \"%s\" is not a file? Failing.", fname);
+ goto done;
+ }
+ new_state = tor_malloc_zero(sizeof(or_state_t));
+ new_state->_magic = OR_STATE_MAGIC;
+ config_init(&state_format, new_state);
+ if (contents) {
+ config_line_t *lines=NULL;
+ int assign_retval;
+ if (config_get_lines(contents, &lines, 0)<0)
+ goto done;
+ assign_retval = config_assign(&state_format, new_state,
+ lines, 0, 0, &errmsg);
+ config_free_lines(lines);
+ if (assign_retval<0)
+ badstate = 1;
+ if (errmsg) {
+ log_warn(LD_GENERAL, "%s", errmsg);
+ tor_free(errmsg);
+ }
+ }
+
+ if (!badstate && or_state_validate(NULL, new_state, 1, &errmsg) < 0)
+ badstate = 1;
+
+ if (errmsg) {
+ log_warn(LD_GENERAL, "%s", errmsg);
+ tor_free(errmsg);
+ }
+
+ if (badstate && !contents) {
+ log_warn(LD_BUG, "Uh oh. We couldn't even validate our own default state."
+ " This is a bug in Tor.");
+ goto done;
+ } else if (badstate && contents) {
+ or_state_save_broken(fname);
+
+ tor_free(contents);
+ config_free(&state_format, new_state);
+
+ new_state = tor_malloc_zero(sizeof(or_state_t));
+ new_state->_magic = OR_STATE_MAGIC;
+ config_init(&state_format, new_state);
+ } else if (contents) {
+ log_info(LD_GENERAL, "Loaded state from \"%s\"", fname);
+ } else {
+ log_info(LD_GENERAL, "Initialized state");
+ }
+ if (or_state_set(new_state) == -1) {
+ or_state_save_broken(fname);
+ }
+ new_state = NULL;
+ if (!contents) {
+ global_state->next_write = 0;
+ or_state_save(time(NULL));
+ }
+ r = 0;
+
+ done:
+ tor_free(fname);
+ tor_free(contents);
+ if (new_state)
+ config_free(&state_format, new_state);
+
+ return r;
+}
+
+/** Did the last time we tried to write the state file fail? If so, we
+ * should consider disabling such features as preemptive circuit generation
+ * to compute circuit-build-time. */
+static int last_state_file_write_failed = 0;
+
+/** Return whether the state file failed to write last time we tried. */
+int
+did_last_state_file_write_fail(void)
+{
+ return last_state_file_write_failed;
+}
+
+/** If writing the state to disk fails, try again after this many seconds. */
+#define STATE_WRITE_RETRY_INTERVAL 3600
+
+/** If we're a relay, how often should we checkpoint our state file even
+ * if nothing else dirties it? This will checkpoint ongoing stats like
+ * bandwidth used, per-country user stats, etc. */
+#define STATE_RELAY_CHECKPOINT_INTERVAL (12*60*60)
+
+/** Write the persistent state to disk. Return 0 for success, <0 on failure. */
+int
+or_state_save(time_t now)
+{
+ char *state, *contents;
+ char tbuf[ISO_TIME_LEN+1];
+ char *fname;
+
+ tor_assert(global_state);
+
+ if (global_state->next_write > now)
+ return 0;
+
+ /* Call everything else that might dirty the state even more, in order
+ * to avoid redundant writes. */
+ entry_guards_update_state(global_state);
+ rep_hist_update_state(global_state);
+ circuit_build_times_update_state(&circ_times, global_state);
+ if (accounting_is_enabled(get_options()))
+ accounting_run_housekeeping(now);
+
+ global_state->LastWritten = now;
+
+ tor_free(global_state->TorVersion);
+ tor_asprintf(&global_state->TorVersion, "Tor %s", get_version());
+
+ state = config_dump(&state_format, NULL, global_state, 1, 0);
+ format_local_iso_time(tbuf, now);
+ tor_asprintf(&contents,
+ "# Tor state file last generated on %s local time\n"
+ "# Other times below are in GMT\n"
+ "# You *do not* need to edit this file.\n\n%s",
+ tbuf, state);
+ tor_free(state);
+ fname = get_datadir_fname("state");
+ if (write_str_to_file(fname, contents, 0)<0) {
+ log_warn(LD_FS, "Unable to write state to file \"%s\"; "
+ "will try again later", fname);
+ last_state_file_write_failed = 1;
+ tor_free(fname);
+ tor_free(contents);
+ /* Try again after STATE_WRITE_RETRY_INTERVAL (or sooner, if the state
+ * changes sooner). */
+ global_state->next_write = now + STATE_WRITE_RETRY_INTERVAL;
+ return -1;
+ }
+
+ last_state_file_write_failed = 0;
+ log_info(LD_GENERAL, "Saved state to \"%s\"", fname);
+ tor_free(fname);
+ tor_free(contents);
+
+ if (server_mode(get_options()))
+ global_state->next_write = now + STATE_RELAY_CHECKPOINT_INTERVAL;
+ else
+ global_state->next_write = TIME_MAX;
+
+ return 0;
+}
+
+/** Return the config line for transport <b>transport</b> in the current state.
+ * Return NULL if there is no config line for <b>transport</b>. */
+static config_line_t *
+get_transport_in_state_by_name(const char *transport)
+{
+ or_state_t *or_state = get_or_state();
+ config_line_t *line;
+ config_line_t *ret = NULL;
+ smartlist_t *items = NULL;
+
+ for (line = or_state->TransportProxies ; line ; line = line->next) {
+ tor_assert(!strcmp(line->key, "TransportProxy"));
+
+ items = smartlist_new();
+ smartlist_split_string(items, line->value, NULL,
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
+ if (smartlist_len(items) != 2) /* broken state */
+ goto done;
+
+ if (!strcmp(smartlist_get(items, 0), transport)) {
+ ret = line;
+ goto done;
+ }
+
+ SMARTLIST_FOREACH(items, char*, s, tor_free(s));
+ smartlist_free(items);
+ items = NULL;
+ }
+
+ done:
+ if (items) {
+ SMARTLIST_FOREACH(items, char*, s, tor_free(s));
+ smartlist_free(items);
+ }
+ return ret;
+}
+
+/** Return string containing the address:port part of the
+ * TransportProxy <b>line</b> for transport <b>transport</b>.
+ * If the line is corrupted, return NULL. */
+static const char *
+get_transport_bindaddr(const char *line, const char *transport)
+{
+ char *line_tmp = NULL;
+
+ if (strlen(line) < strlen(transport) + 2) {
+ goto broken_state;
+ } else {
+ /* line should start with the name of the transport and a space.
+ (for example, "obfs2 127.0.0.1:47245") */
+ tor_asprintf(&line_tmp, "%s ", transport);
+ if (strcmpstart(line, line_tmp))
+ goto broken_state;
+
+ tor_free(line_tmp);
+ return (line+strlen(transport)+1);
+ }
+
+ broken_state:
+ tor_free(line_tmp);
+ return NULL;
+}
+
+/** Return a string containing the address:port that a proxy transport
+ * should bind on. The string is stored on the heap and must be freed
+ * by the caller of this function. */
+char *
+get_stored_bindaddr_for_server_transport(const char *transport)
+{
+ char *default_addrport = NULL;
+ const char *stored_bindaddr = NULL;
+
+ config_line_t *line = get_transport_in_state_by_name(transport);
+ if (!line) /* Found no references in state for this transport. */
+ goto no_bindaddr_found;
+
+ stored_bindaddr = get_transport_bindaddr(line->value, transport);
+ if (stored_bindaddr) /* found stored bindaddr in state file. */
+ return tor_strdup(stored_bindaddr);
+
+ no_bindaddr_found:
+ /** If we didn't find references for this pluggable transport in the
+ state file, we should instruct the pluggable transport proxy to
+ listen on INADDR_ANY on a random ephemeral port. */
+ tor_asprintf(&default_addrport, "%s:%s", fmt_addr32(INADDR_ANY), "0");
+ return default_addrport;
+}
+
+/** Save <b>transport</b> listening on <b>addr</b>:<b>port</b> to
+ state */
+void
+save_transport_to_state(const char *transport,
+ const tor_addr_t *addr, uint16_t port)
+{
+ or_state_t *state = get_or_state();
+
+ char *transport_addrport=NULL;
+
+ /** find where to write on the state */
+ config_line_t **next, *line;
+
+ /* see if this transport is already stored in state */
+ config_line_t *transport_line =
+ get_transport_in_state_by_name(transport);
+
+ if (transport_line) { /* if transport already exists in state... */
+ const char *prev_bindaddr = /* get its addrport... */
+ get_transport_bindaddr(transport_line->value, transport);
+ tor_asprintf(&transport_addrport, "%s:%d", fmt_addr(addr), (int)port);
+
+ /* if transport in state has the same address as this one, life is good */
+ if (!strcmp(prev_bindaddr, transport_addrport)) {
+ log_info(LD_CONFIG, "Transport seems to have spawned on its usual "
+ "address:port.");
+ goto done;
+ } else { /* if addrport in state is different than the one we got */
+ log_info(LD_CONFIG, "Transport seems to have spawned on different "
+ "address:port. Let's update the state file with the new "
+ "address:port");
+ tor_free(transport_line->value); /* free the old line */
+ tor_asprintf(&transport_line->value, "%s %s:%d", transport,
+ fmt_addr(addr),
+ (int) port); /* replace old addrport line with new line */
+ }
+ } else { /* never seen this one before; save it in state for next time */
+ log_info(LD_CONFIG, "It's the first time we see this transport. "
+ "Let's save its address:port");
+ next = &state->TransportProxies;
+ /* find the last TransportProxy line in the state and point 'next'
+ right after it */
+ line = state->TransportProxies;
+ while (line) {
+ next = &(line->next);
+ line = line->next;
+ }
+
+ /* allocate space for the new line and fill it in */
+ *next = line = tor_malloc_zero(sizeof(config_line_t));
+ line->key = tor_strdup("TransportProxy");
+ tor_asprintf(&line->value, "%s %s:%d", transport,
+ fmt_addr(addr), (int) port);
+
+ next = &(line->next);
+ }
+
+ if (!get_options()->AvoidDiskWrites)
+ or_state_mark_dirty(state, 0);
+
+ done:
+ tor_free(transport_addrport);
+}
+
+void
+or_state_free_all(void)
+{
+ config_free(&state_format, global_state);
+ global_state = NULL;
+}
+
diff --git a/src/or/statefile.h b/src/or/statefile.h
new file mode 100644
index 0000000..4770d50
--- /dev/null
+++ b/src/or/statefile.h
@@ -0,0 +1,22 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2012, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_STATEFILE_H
+#define TOR_STATEFILE_H
+
+or_state_t *get_or_state(void);
+int did_last_state_file_write_fail(void);
+int or_state_save(time_t now);
+
+void save_transport_to_state(const char *transport_name,
+ const tor_addr_t *addr, uint16_t port);
+char *get_stored_bindaddr_for_server_transport(const char *transport);
+int or_state_load(void);
+int or_state_loaded(void);
+void or_state_free_all(void);
+
+#endif
+
diff --git a/src/or/transports.c b/src/or/transports.c
index f2c604c..34fe679 100644
--- a/src/or/transports.c
+++ b/src/or/transports.c
@@ -94,6 +94,7 @@
#include "transports.h"
#include "util.h"
#include "router.h"
+#include "statefile.h"
static process_environment_t *
create_managed_proxy_environment(const managed_proxy_t *mp);
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits