commit 1e92b24889bd64ccdd568366aaf989714d130f31 Author: George Kadianakis desnacked@gmail.com Date: Sun Sep 11 20:29:12 2011 +0200
Update transports.[ch] to support SIGHUPs. --- src/or/transports.c | 323 +++++++++++++++++++++++++++++++++++++++------------ src/or/transports.h | 42 +++++++- 2 files changed, 292 insertions(+), 73 deletions(-)
diff --git a/src/or/transports.c b/src/or/transports.c index 6255a56..c4391c5 100644 --- a/src/or/transports.c +++ b/src/or/transports.c @@ -17,10 +17,7 @@ static void set_managed_proxy_environment(char ***envp, const managed_proxy_t *mp); static INLINE int proxy_configuration_finished(const managed_proxy_t *mp);
-static void managed_proxy_destroy_impl(managed_proxy_t *mp, - int also_free_transports); -#define managed_proxy_destroy(mp) managed_proxy_destroy_impl(mp, 0) -#define managed_proxy_destroy_with_transports(mp) managed_proxy_destroy_impl(mp, 1) +static void managed_proxy_destroy(managed_proxy_t *mp);
static void handle_finished_proxy(managed_proxy_t *mp); static void configure_proxy(managed_proxy_t *mp); @@ -55,14 +52,16 @@ static INLINE void free_execve_args(char **arg); #define PROTO_VERSION_ONE 1
/** List of unconfigured managed proxies. */ -static smartlist_t *unconfigured_proxy_list = NULL; +static smartlist_t *managed_proxy_list = NULL; +/** Number of still unconfigured proxies. */ +static int unconfigured_proxies_n = 0;
/* The main idea here is:
A managed proxy is represented by a managed_proxy_t struct and can spawn multiple transports.
- unconfigured_proxy_list is a list of all the unconfigured managed + managed_proxy_list is a list of all the unconfigured managed proxies; everytime we find a managed proxy in torrc we add it in that list. In every run_scheduled_event() tick, we attempt to launch and then @@ -79,8 +78,7 @@ static smartlist_t *unconfigured_proxy_list = NULL; int pt_proxies_configuration_pending(void) { - if (!unconfigured_proxy_list) return 0; - return !!smartlist_len(unconfigured_proxy_list); + return !! unconfigured_proxies_n; }
/** Return true if <b>mp</b> has the same argv as <b>proxy_argv</b> */ @@ -109,10 +107,10 @@ managed_proxy_has_argv(managed_proxy_t *mp, char **proxy_argv) static managed_proxy_t * get_managed_proxy_by_argv(char **proxy_argv) { - if (!unconfigured_proxy_list) + if (!managed_proxy_list) return NULL;
- SMARTLIST_FOREACH_BEGIN(unconfigured_proxy_list, managed_proxy_t *, mp) { + SMARTLIST_FOREACH_BEGIN(managed_proxy_list, managed_proxy_t *, mp) { if (managed_proxy_has_argv(mp, proxy_argv)) return mp; } SMARTLIST_FOREACH_END(mp); @@ -129,22 +127,88 @@ add_transport_to_proxy(const char *transport, managed_proxy_t *mp) smartlist_add(mp->transports_to_launch, tor_strdup(transport)); }
+/** Called when a SIGHUP occurs. + * Returns true if managed proxy <b>mp</b> needs to be restarted + * after the SIGHUP based on the new torrc. */ +static int +proxy_needs_restart(managed_proxy_t *mp) +{ + /* mp->transport_to_launch is populated with the names of the + transports that must be launched *after* the SIGHUP. + + Since only PT_PROTO_COMPLETED proxies reach this function, + mp->transports is populated with strings of the *names of the + transports* that were launched *before* the SIGHUP. + + If the two lists contain the same strings, we don't need to + restart the proxy, since it already does what we want. */ + + tor_assert(smartlist_len(mp->transports_to_launch) > 0); + tor_assert(mp->conf_state == PT_PROTO_COMPLETED); + + if (smartlist_len(mp->transports_to_launch) != smartlist_len(mp->transports)) + goto needs_restart; + + SMARTLIST_FOREACH_BEGIN(mp->transports_to_launch, char *, t_t_l) { + if (!smartlist_string_isin(mp->transports, t_t_l)) + goto needs_restart; + + } SMARTLIST_FOREACH_END(t_t_l); + + return 0; + + needs_restart: + return 1; +} + +/** Managed proxy <b>mp</b> must be restarted. Do all the necessary + * preparations and then flag its state so that it will be launched + * in the next tick. */ +static void +proxy_prepare_for_restart(managed_proxy_t *mp) +{ + transport_t *t_tmp = NULL; + + tor_assert(mp->conf_state == PT_PROTO_COMPLETED); + tor_assert(mp->pid); + + /* kill the old obfsproxy process */ + tor_terminate_process(mp->pid); + mp->pid = 0; + fclose(mp->stdout); + + /* destroy all its old transports. we no longer use them. */ + SMARTLIST_FOREACH_BEGIN(mp->transports, const char *, t_name) { + t_tmp = transport_get_by_name(t_name); + if (t_tmp) + t_tmp->marked_for_removal = 1; + } SMARTLIST_FOREACH_END(t_name); + sweep_transport_list(); + + /* free the transport names in mp->transports */ + SMARTLIST_FOREACH(mp->transports, char *, t_name, tor_free(t_name)); + smartlist_clear(mp->transports); + + /* flag it as an infant proxy so that it gets launched on next tick */ + mp->conf_state = PT_PROTO_INFANT; +} + /** Launch managed proxy <b>mp</b>. */ static int launch_managed_proxy(managed_proxy_t *mp) { char **envp=NULL; - int retval; + int pid; FILE *stdout_read = NULL; int stdout_pipe=-1, stderr_pipe=-1;
/* prepare the environment variables for the managed proxy */ set_managed_proxy_environment(&envp, mp);
- retval = tor_spawn_background(mp->argv[0], &stdout_pipe, - &stderr_pipe, (const char **)mp->argv, - (const char **)envp); - if (retval < 0) { + pid = tor_spawn_background(mp->argv[0], &stdout_pipe, + &stderr_pipe, (const char **)mp->argv, + (const char **)envp); + if (pid < 0) { log_warn(LD_GENERAL, "Spawn failed"); return -1; } @@ -157,10 +221,11 @@ launch_managed_proxy(managed_proxy_t *mp) /* Open the buffered IO streams */ stdout_read = fdopen(stdout_pipe, "r");
- log_warn(LD_CONFIG, "The spawn is alive (%d)!", retval); + log_warn(LD_CONFIG, "The spawn is alive (%d)!", pid);
mp->conf_state = PT_PROTO_LAUNCHED; mp->stdout = stdout_read; + mp->pid = pid;
return 0; } @@ -171,12 +236,32 @@ launch_managed_proxy(managed_proxy_t *mp) void pt_configure_remaining_proxies(void) { - log_warn(LD_CONFIG, "We start configuring remaining managed proxies!"); - SMARTLIST_FOREACH_BEGIN(unconfigured_proxy_list, managed_proxy_t *, mp) { - /* configured proxies shouldn't be in unconfigured_proxy_list. */ - tor_assert(!proxy_configuration_finished(mp)); + log_warn(LD_CONFIG, "We start configuring remaining managed proxies (%d)!", + unconfigured_proxies_n); + SMARTLIST_FOREACH_BEGIN(managed_proxy_list, managed_proxy_t *, mp) { + tor_assert(mp->conf_state != PT_PROTO_BROKEN); + + if (mp->got_hup) { + mp->got_hup = 0; + + /* This proxy is marked by a SIGHUP. Check whether we need to + restart it. */ + if (proxy_needs_restart(mp)) { + proxy_prepare_for_restart(mp); + continue; + } else { /* it doesn't need to be restarted. */ + printf("No need for restart; status quo\n"); + unconfigured_proxies_n--; + tor_assert(unconfigured_proxies_n >= 0); + } + + continue; + }
- configure_proxy(mp); + /* If the proxy is not fully configured, try to configure it + futher. */ + if (!proxy_configuration_finished(mp)) + configure_proxy(mp);
} SMARTLIST_FOREACH_END(mp); } @@ -222,33 +307,55 @@ configure_proxy(managed_proxy_t *mp)
/** Register server managed proxy <b>mp</b> transports to state */ static void -register_server_proxy(const managed_proxy_t *mp) +register_server_proxy(managed_proxy_t *mp) { - if (mp->is_server) { - SMARTLIST_FOREACH_BEGIN(mp->transports, transport_t *, t) { - save_transport_to_state(t->name,&t->addr,t->port); /* pass tor_addr_t? */ - } SMARTLIST_FOREACH_END(t); - } + smartlist_t *sm_tmp = smartlist_create(); + + tor_assert(mp->conf_state != PT_PROTO_COMPLETED); + SMARTLIST_FOREACH_BEGIN(mp->transports, transport_t *, t) { + save_transport_to_state(t->name,&t->addr,t->port); /* pass tor_addr_t? */ + smartlist_add(sm_tmp, tor_strdup(t->name)); + } SMARTLIST_FOREACH_END(t); + + smartlist_free(mp->transports); + mp->transports = sm_tmp; }
/** Register all the transports supported by client managed proxy * <b>mp</b> to the bridge subsystem. */ static void -register_client_proxy(const managed_proxy_t *mp) +register_client_proxy(managed_proxy_t *mp) { + int r; + smartlist_t *sm_tmp = smartlist_create(); + + tor_assert(mp->conf_state != PT_PROTO_COMPLETED); SMARTLIST_FOREACH_BEGIN(mp->transports, transport_t *, t) { - if (transport_add(t)<0) { + r = transport_add(t); + switch (r) { + case -1: log_warn(LD_GENERAL, "Could not add transport %s. Skipping.", t->name); transport_free(t); - } else { + break; + case 0: log_warn(LD_GENERAL, "Succesfully registered transport %s", t->name); + smartlist_add(sm_tmp, tor_strdup(t->name)); + break; + case 1: + log_warn(LD_GENERAL, "Succesfully registered transport %s", t->name); + smartlist_add(sm_tmp, tor_strdup(t->name)); + transport_free(t); + break; } } SMARTLIST_FOREACH_END(t); + + smartlist_free(mp->transports); + mp->transports = sm_tmp; }
/** Register the transports of managed proxy <b>mp</b>. */ static INLINE void -register_proxy(const managed_proxy_t *mp) +register_proxy(managed_proxy_t *mp) { if (mp->is_server) register_server_proxy(mp); @@ -256,18 +363,17 @@ register_proxy(const managed_proxy_t *mp) register_client_proxy(mp); }
-/** Free memory allocated by managed proxy <b>mp</b>. - * If <b>also_free_transports</b> is set, also free the transports - * associated with this managed proxy. */ +/** Free memory allocated by managed proxy <b>mp</b>. */ static void -managed_proxy_destroy_impl(managed_proxy_t *mp, int also_free_transports) +managed_proxy_destroy(managed_proxy_t *mp) { - /* transport_free() all its transports */ - if (also_free_transports) + printf("Destroying mp %p\n", mp); + if (mp->conf_state != PT_PROTO_COMPLETED) SMARTLIST_FOREACH(mp->transports, transport_t *, t, transport_free(t)); + else + SMARTLIST_FOREACH(mp->transports, char *, t_name, tor_free(t_name));
/* free the transports smartlist */ - smartlist_clear(mp->transports); smartlist_free(mp->transports);
SMARTLIST_FOREACH(mp->transports_to_launch, char *, t, tor_free(t)); @@ -277,31 +383,32 @@ managed_proxy_destroy_impl(managed_proxy_t *mp, int also_free_transports) smartlist_free(mp->transports_to_launch);
/* remove it from the list of managed proxies */ - smartlist_remove(unconfigured_proxy_list, mp); + smartlist_remove(managed_proxy_list, mp);
/* close its stdout stream */ - fclose(mp->stdout); + if (mp->stdout) + fclose(mp->stdout);
/* free the argv */ free_execve_args(mp->argv);
+ if (mp->pid) + tor_terminate_process(mp->pid); + tor_free(mp); }
- /** Handle a configured or broken managed proxy <b>mp</b>. */ static void handle_finished_proxy(managed_proxy_t *mp) { switch (mp->conf_state) { case PT_PROTO_BROKEN: /* if broken: */ - managed_proxy_destroy_with_transports(mp); /* destroy it and all its transports */ + managed_proxy_destroy(mp); /* annihilate it. */ break; case PT_PROTO_CONFIGURED: /* if configured correctly: */ register_proxy(mp); /* register transports */ - mp->conf_state = PT_PROTO_COMPLETED; /* mark it as completed, */ - managed_proxy_destroy(mp); /* destroy the managed proxy struct, - keeping the transports intact */ + mp->conf_state = PT_PROTO_COMPLETED; /* mark it as completed. */ break; default: log_warn(LD_CONFIG, "Unfinished managed proxy in " @@ -309,7 +416,8 @@ handle_finished_proxy(managed_proxy_t *mp) tor_assert(0); }
- tor_assert(smartlist_len(unconfigured_proxy_list) >= 0); + unconfigured_proxies_n--; + tor_assert(unconfigured_proxies_n >= 0); }
/** Return true if the configuration of the managed proxy <b>mp</b> is @@ -322,8 +430,7 @@ proxy_configuration_finished(const managed_proxy_t *mp) }
-/** This function is called when a proxy sends an {S,C}METHODS DONE message, - */ +/** This function is called when a proxy sends an {S,C}METHODS DONE message. */ static void handle_methods_done(const managed_proxy_t *mp) { @@ -694,6 +801,30 @@ set_managed_proxy_environment(char ***envp, const managed_proxy_t *mp) tor_free(bindaddr); }
+/** Create and return a new managed proxy for <b>transport</b> using + * <b>proxy_argv</b>. If <b>is_server</b> is true, it's a server + * managed proxy. */ +static managed_proxy_t * +managed_proxy_create(const char *transport, char **proxy_argv, int is_server) +{ + managed_proxy_t *mp = tor_malloc_zero(sizeof(managed_proxy_t)); + mp->conf_state = PT_PROTO_INFANT; + mp->is_server = is_server; + mp->argv = proxy_argv; + mp->transports = smartlist_create(); + + mp->transports_to_launch = smartlist_create(); + add_transport_to_proxy(transport, mp); + + /* register the managed proxy */ + if (!managed_proxy_list) + managed_proxy_list = smartlist_create(); + smartlist_add(managed_proxy_list, mp); + unconfigured_proxies_n++; + + return mp; +} + /** Register <b>transport</b> using proxy with <b>proxy_argv</b> to * the managed proxy subsystem. * If <b>is_server</b> is true, then the proxy is a server proxy. */ @@ -705,21 +836,28 @@ pt_kickstart_proxy(const char *transport, char **proxy_argv, int is_server) mp = get_managed_proxy_by_argv(proxy_argv);
if (!mp) { /* we haven't seen this proxy before */ - /* create a managed proxy */ - managed_proxy_t *mp = tor_malloc_zero(sizeof(managed_proxy_t)); - mp->conf_state = PT_PROTO_INFANT; - mp->is_server = is_server; - mp->argv = proxy_argv; - mp->transports = smartlist_create(); - - mp->transports_to_launch = smartlist_create(); - add_transport_to_proxy(transport, mp); + managed_proxy_create(transport, proxy_argv, is_server); + + } else { /* known proxy. add its transport to its transport list */ + if (mp->got_hup) { + /* If the managed proxy we found is marked by a SIGHUP, it means + that it's not useless and should be kept. If it's marked for + removal, unmark it and increase the unconfigured proxies so + that we try to restart it if we need to. Afterwards, check if + a transport_t for 'transport' used to exist before the SIGHUP + and make sure it doesn't get deleted because we might reuse + it. */ + if (mp->marked_for_removal) { + mp->marked_for_removal = 0; + unconfigured_proxies_n++; + } + + transport_t *old_transport = NULL; + old_transport = transport_get_by_name(transport); + if (old_transport) + old_transport->marked_for_removal = 0; + }
- /* register the managed proxy */ - if (!unconfigured_proxy_list) - unconfigured_proxy_list = smartlist_create(); - smartlist_add(unconfigured_proxy_list, mp); - } else { /* known proxy. just add transport to its transport list */ add_transport_to_proxy(transport, mp); free_execve_args(proxy_argv); } @@ -738,25 +876,66 @@ free_execve_args(char **arg) tor_free(arg); }
+ +/** Tor will read its config, prepare the managed proxy list so that + * proxies that are not used in the new config will shutdown, and + * proxies that need to spawn more transports will do so. */ +void +pt_prepare_proxy_list_for_config_read(void) +{ + if (!managed_proxy_list) + return; + + SMARTLIST_FOREACH_BEGIN(managed_proxy_list, managed_proxy_t *, mp) { + /* Destroy unconfigured proxies. */ + if (mp->conf_state != PT_PROTO_COMPLETED) { + managed_proxy_destroy(mp); + unconfigured_proxies_n--; + continue; + } + + tor_assert(mp->conf_state == PT_PROTO_COMPLETED); + + mp->marked_for_removal = 1; + mp->got_hup = 1; + SMARTLIST_FOREACH(mp->transports_to_launch, char *, t, tor_free(t)); + smartlist_clear(mp->transports_to_launch); + } SMARTLIST_FOREACH_END(mp); + + tor_assert(unconfigured_proxies_n == 0); +} + +/** The tor config was read, destroy all managed proxies that were + * marked by a previous call to prepare_proxy_list_for_config_read() + * and are not used by the new config. */ +void +sweep_proxy_list(void) +{ + if (!managed_proxy_list) + return; + + SMARTLIST_FOREACH_BEGIN(managed_proxy_list, managed_proxy_t *, mp) { + if (mp->marked_for_removal) { + SMARTLIST_DEL_CURRENT(managed_proxy_list, mp); + managed_proxy_destroy(mp); + } + } SMARTLIST_FOREACH_END(mp); +} + /** Release all storage held by the pluggable transports subsystem. */ void pt_free_all(void) { - if (unconfigured_proxy_list) { + if (managed_proxy_list) { /* If the proxy is in PT_PROTO_COMPLETED, it has registered its transports and it's the duty of the circuitbuild.c subsystem to free them. Otherwise, it hasn't registered its transports yet and we should free them here. */ - SMARTLIST_FOREACH_BEGIN(unconfigured_proxy_list, managed_proxy_t *, mp) { - if (mp->conf_state == PT_PROTO_COMPLETED) - managed_proxy_destroy(mp); - else - managed_proxy_destroy_with_transports(mp); - } SMARTLIST_FOREACH_END(mp); + SMARTLIST_FOREACH(managed_proxy_list, managed_proxy_t *, mp, + managed_proxy_destroy(mp));
- smartlist_clear(unconfigured_proxy_list); - smartlist_free(unconfigured_proxy_list); - unconfigured_proxy_list=NULL; + smartlist_free(managed_proxy_list); + managed_proxy_list=NULL; } }
diff --git a/src/or/transports.h b/src/or/transports.h index 6fec1dc..48b7839 100644 --- a/src/or/transports.h +++ b/src/or/transports.h @@ -25,6 +25,9 @@ int pt_proxies_configuration_pending(void);
void pt_free_all(void);
+void pt_prepare_proxy_list_for_config_read(void); +void sweep_proxy_list(void); + #ifdef PT_PRIVATE /** State of the managed proxy configuration protocol. */ enum pt_proto_state { @@ -47,8 +50,45 @@ typedef struct { FILE *stdout; /* a stream to its stdout (closed in managed_proxy_destroy()) */
+ int pid; /* The Process ID this managed proxy is using. */ + + /** Boolean: We are re-parsing our config, and we are going to + * remove this managed proxy if we don't find it any transport + * plugins that use it. */ + unsigned int marked_for_removal : 1; + + /** Boolean: We got a SIGHUP while this proxy was running. We use + * this flag to signify that this proxy might need to be restarted + * so that it can listen for other transports according to the new + * torrc. */ + unsigned int got_hup : 1; + smartlist_t *transports_to_launch; /* transports to-be-launched by this proxy */ - smartlist_t *transports; /* list of transport_t this proxy spawned */ + + /* The 'transports' list contains all the transports this proxy has + launched. + + Before a managed_proxy_t reaches the PT_PROTO_COMPLETED phase, + this smartlist contains a 'transport_t' for every transport it + has launched. + + When the managed_proxy_t reaches the PT_PROTO_COMPLETED phase, it + registers all its transports to the circuitbuild.c subsystem. At + that point the 'transport_t's are owned by the circuitbuild.c + subsystem. + + To avoid carrying dangling 'transport_t's in this smartlist, + right before the managed_proxy_t reaches the PT_PROTO_COMPLETED + phase we replace all 'transport_t's with strings of their + transport names. + + So, tl;dr: + When (conf_state != PT_PROTO_COMPLETED) this list carries + (transport_t *). + When (conf_state == PT_PROTO_COMPLETED) this list carries + (char *). + */ + smartlist_t *transports; } managed_proxy_t;
int parse_cmethod_line(const char *line, managed_proxy_t *mp);
tor-commits@lists.torproject.org