Update transports.[ch] to support SIGHUPs.

This commit is contained in:
George Kadianakis 2011-09-11 20:29:12 +02:00
parent fa514fb207
commit 1e92b24889
2 changed files with 291 additions and 72 deletions

View File

@ -17,10 +17,7 @@
static void set_managed_proxy_environment(char ***envp, const managed_proxy_t *mp); 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 INLINE int proxy_configuration_finished(const managed_proxy_t *mp);
static void managed_proxy_destroy_impl(managed_proxy_t *mp, static void managed_proxy_destroy(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 handle_finished_proxy(managed_proxy_t *mp); static void handle_finished_proxy(managed_proxy_t *mp);
static void configure_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 #define PROTO_VERSION_ONE 1
/** List of unconfigured managed proxies. */ /** 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: /* The main idea here is:
A managed proxy is represented by a managed_proxy_t struct and can A managed proxy is represented by a managed_proxy_t struct and can
spawn multiple transports. 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 proxies; everytime we find a managed proxy in torrc we add it in
that list. that list.
In every run_scheduled_event() tick, we attempt to launch and then In every run_scheduled_event() tick, we attempt to launch and then
@ -79,8 +78,7 @@ static smartlist_t *unconfigured_proxy_list = NULL;
int int
pt_proxies_configuration_pending(void) pt_proxies_configuration_pending(void)
{ {
if (!unconfigured_proxy_list) return 0; return !! unconfigured_proxies_n;
return !!smartlist_len(unconfigured_proxy_list);
} }
/** Return true if <b>mp</b> has the same argv as <b>proxy_argv</b> */ /** 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 * static managed_proxy_t *
get_managed_proxy_by_argv(char **proxy_argv) get_managed_proxy_by_argv(char **proxy_argv)
{ {
if (!unconfigured_proxy_list) if (!managed_proxy_list)
return NULL; 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)) if (managed_proxy_has_argv(mp, proxy_argv))
return mp; return mp;
} SMARTLIST_FOREACH_END(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)); 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>. */ /** Launch managed proxy <b>mp</b>. */
static int static int
launch_managed_proxy(managed_proxy_t *mp) launch_managed_proxy(managed_proxy_t *mp)
{ {
char **envp=NULL; char **envp=NULL;
int retval; int pid;
FILE *stdout_read = NULL; FILE *stdout_read = NULL;
int stdout_pipe=-1, stderr_pipe=-1; int stdout_pipe=-1, stderr_pipe=-1;
/* prepare the environment variables for the managed proxy */ /* prepare the environment variables for the managed proxy */
set_managed_proxy_environment(&envp, mp); set_managed_proxy_environment(&envp, mp);
retval = tor_spawn_background(mp->argv[0], &stdout_pipe, pid = tor_spawn_background(mp->argv[0], &stdout_pipe,
&stderr_pipe, (const char **)mp->argv, &stderr_pipe, (const char **)mp->argv,
(const char **)envp); (const char **)envp);
if (retval < 0) { if (pid < 0) {
log_warn(LD_GENERAL, "Spawn failed"); log_warn(LD_GENERAL, "Spawn failed");
return -1; return -1;
} }
@ -157,10 +221,11 @@ launch_managed_proxy(managed_proxy_t *mp)
/* Open the buffered IO streams */ /* Open the buffered IO streams */
stdout_read = fdopen(stdout_pipe, "r"); 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->conf_state = PT_PROTO_LAUNCHED;
mp->stdout = stdout_read; mp->stdout = stdout_read;
mp->pid = pid;
return 0; return 0;
} }
@ -171,12 +236,32 @@ launch_managed_proxy(managed_proxy_t *mp)
void void
pt_configure_remaining_proxies(void) pt_configure_remaining_proxies(void)
{ {
log_warn(LD_CONFIG, "We start configuring remaining managed proxies!"); log_warn(LD_CONFIG, "We start configuring remaining managed proxies (%d)!",
SMARTLIST_FOREACH_BEGIN(unconfigured_proxy_list, managed_proxy_t *, mp) { unconfigured_proxies_n);
/* configured proxies shouldn't be in unconfigured_proxy_list. */ SMARTLIST_FOREACH_BEGIN(managed_proxy_list, managed_proxy_t *, mp) {
tor_assert(!proxy_configuration_finished(mp)); tor_assert(mp->conf_state != PT_PROTO_BROKEN);
configure_proxy(mp); 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;
}
/* If the proxy is not fully configured, try to configure it
futher. */
if (!proxy_configuration_finished(mp))
configure_proxy(mp);
} SMARTLIST_FOREACH_END(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 */ /** Register server managed proxy <b>mp</b> transports to state */
static void static void
register_server_proxy(const managed_proxy_t *mp) register_server_proxy(managed_proxy_t *mp)
{ {
if (mp->is_server) { smartlist_t *sm_tmp = smartlist_create();
SMARTLIST_FOREACH_BEGIN(mp->transports, transport_t *, t) {
save_transport_to_state(t->name,&t->addr,t->port); /* pass tor_addr_t? */ tor_assert(mp->conf_state != PT_PROTO_COMPLETED);
} SMARTLIST_FOREACH_END(t); 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 /** Register all the transports supported by client managed proxy
* <b>mp</b> to the bridge subsystem. */ * <b>mp</b> to the bridge subsystem. */
static void 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) { 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); log_warn(LD_GENERAL, "Could not add transport %s. Skipping.", t->name);
transport_free(t); transport_free(t);
} else { break;
case 0:
log_warn(LD_GENERAL, "Succesfully registered transport %s", t->name); 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_FOREACH_END(t);
smartlist_free(mp->transports);
mp->transports = sm_tmp;
} }
/** Register the transports of managed proxy <b>mp</b>. */ /** Register the transports of managed proxy <b>mp</b>. */
static INLINE void static INLINE void
register_proxy(const managed_proxy_t *mp) register_proxy(managed_proxy_t *mp)
{ {
if (mp->is_server) if (mp->is_server)
register_server_proxy(mp); register_server_proxy(mp);
@ -256,18 +363,17 @@ register_proxy(const managed_proxy_t *mp)
register_client_proxy(mp); register_client_proxy(mp);
} }
/** Free memory allocated by managed proxy <b>mp</b>. /** 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. */
static void 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 */ printf("Destroying mp %p\n", mp);
if (also_free_transports) if (mp->conf_state != PT_PROTO_COMPLETED)
SMARTLIST_FOREACH(mp->transports, transport_t *, t, transport_free(t)); 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 */ /* free the transports smartlist */
smartlist_clear(mp->transports);
smartlist_free(mp->transports); smartlist_free(mp->transports);
SMARTLIST_FOREACH(mp->transports_to_launch, char *, t, tor_free(t)); 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); smartlist_free(mp->transports_to_launch);
/* remove it from the list of managed proxies */ /* remove it from the list of managed proxies */
smartlist_remove(unconfigured_proxy_list, mp); smartlist_remove(managed_proxy_list, mp);
/* close its stdout stream */ /* close its stdout stream */
fclose(mp->stdout); if (mp->stdout)
fclose(mp->stdout);
/* free the argv */ /* free the argv */
free_execve_args(mp->argv); free_execve_args(mp->argv);
if (mp->pid)
tor_terminate_process(mp->pid);
tor_free(mp); tor_free(mp);
} }
/** Handle a configured or broken managed proxy <b>mp</b>. */ /** Handle a configured or broken managed proxy <b>mp</b>. */
static void static void
handle_finished_proxy(managed_proxy_t *mp) handle_finished_proxy(managed_proxy_t *mp)
{ {
switch (mp->conf_state) { switch (mp->conf_state) {
case PT_PROTO_BROKEN: /* if broken: */ 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; break;
case PT_PROTO_CONFIGURED: /* if configured correctly: */ case PT_PROTO_CONFIGURED: /* if configured correctly: */
register_proxy(mp); /* register transports */ register_proxy(mp); /* register transports */
mp->conf_state = PT_PROTO_COMPLETED; /* mark it as completed, */ mp->conf_state = PT_PROTO_COMPLETED; /* mark it as completed. */
managed_proxy_destroy(mp); /* destroy the managed proxy struct,
keeping the transports intact */
break; break;
default: default:
log_warn(LD_CONFIG, "Unfinished managed proxy in " log_warn(LD_CONFIG, "Unfinished managed proxy in "
@ -309,7 +416,8 @@ handle_finished_proxy(managed_proxy_t *mp)
tor_assert(0); 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 /** 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 static void
handle_methods_done(const managed_proxy_t *mp) 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); 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 /** Register <b>transport</b> using proxy with <b>proxy_argv</b> to
* the managed proxy subsystem. * the managed proxy subsystem.
* If <b>is_server</b> is true, then the proxy is a server proxy. */ * 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); mp = get_managed_proxy_by_argv(proxy_argv);
if (!mp) { /* we haven't seen this proxy before */ if (!mp) { /* we haven't seen this proxy before */
/* create a managed proxy */ managed_proxy_create(transport, proxy_argv, 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(); } else { /* known proxy. add its transport to its transport list */
add_transport_to_proxy(transport, mp); 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); add_transport_to_proxy(transport, mp);
free_execve_args(proxy_argv); free_execve_args(proxy_argv);
} }
@ -738,25 +876,66 @@ free_execve_args(char **arg)
tor_free(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. */ /** Release all storage held by the pluggable transports subsystem. */
void void
pt_free_all(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 /* If the proxy is in PT_PROTO_COMPLETED, it has registered its
transports and it's the duty of the circuitbuild.c subsystem to transports and it's the duty of the circuitbuild.c subsystem to
free them. Otherwise, it hasn't registered its transports yet free them. Otherwise, it hasn't registered its transports yet
and we should free them here. */ and we should free them here. */
SMARTLIST_FOREACH_BEGIN(unconfigured_proxy_list, managed_proxy_t *, mp) { SMARTLIST_FOREACH(managed_proxy_list, managed_proxy_t *, mp,
if (mp->conf_state == PT_PROTO_COMPLETED) managed_proxy_destroy(mp));
managed_proxy_destroy(mp);
else
managed_proxy_destroy_with_transports(mp);
} SMARTLIST_FOREACH_END(mp);
smartlist_clear(unconfigured_proxy_list); smartlist_free(managed_proxy_list);
smartlist_free(unconfigured_proxy_list); managed_proxy_list=NULL;
unconfigured_proxy_list=NULL;
} }
} }

View File

@ -25,6 +25,9 @@ int pt_proxies_configuration_pending(void);
void pt_free_all(void); void pt_free_all(void);
void pt_prepare_proxy_list_for_config_read(void);
void sweep_proxy_list(void);
#ifdef PT_PRIVATE #ifdef PT_PRIVATE
/** State of the managed proxy configuration protocol. */ /** State of the managed proxy configuration protocol. */
enum pt_proto_state { enum pt_proto_state {
@ -47,8 +50,45 @@ typedef struct {
FILE *stdout; /* a stream to its stdout FILE *stdout; /* a stream to its stdout
(closed in managed_proxy_destroy()) */ (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_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; } managed_proxy_t;
int parse_cmethod_line(const char *line, managed_proxy_t *mp); int parse_cmethod_line(const char *line, managed_proxy_t *mp);