From 4ac78818292900a2ed381604dc67fdad6a870ad2 Mon Sep 17 00:00:00 2001 From: Wouter Wijngaards Date: Wed, 3 Dec 2008 15:20:56 +0000 Subject: [PATCH] fixup remote control so most commands work in nonthreaded environment. git-svn-id: file:///svn/unbound/trunk@1382 be551aaa-1e26-0410-a405-d3ace91eadb9 --- daemon/remote.c | 91 +++++++++++++++++++++++++++------- daemon/remote.h | 6 +++ daemon/worker.c | 6 +++ daemon/worker.h | 4 +- doc/Changelog | 4 ++ services/localzone.c | 3 +- testcode/fake_event.c | 5 ++ testdata/remote-threaded.tpkg | Bin 7096 -> 7327 bytes 8 files changed, 99 insertions(+), 20 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 59cca8013..e889ca4dd 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -445,6 +445,8 @@ int ssl_print_text(SSL* ssl, const char* text) { int r; + if(!ssl) + return 0; ERR_clear_error(); if((r=SSL_write(ssl, text, (int)strlen(text))) <= 0) { if(SSL_get_error(ssl, r) == SSL_ERROR_ZERO_RETURN) { @@ -483,6 +485,8 @@ ssl_read_line(SSL* ssl, char* buf, size_t max) { int r; size_t len = 0; + if(!ssl) + return 0; while(len < max) { ERR_clear_error(); if((r=SSL_read(ssl, buf+len, 1)) <= 0) { @@ -1182,45 +1186,96 @@ do_flush_name(SSL* ssl, struct worker* worker, char* arg) send_ok(ssl); } +/** tell other processes to execute the command */ +void +distribute_cmd(struct daemon_remote* rc, SSL* ssl, char* cmd) +{ + int i; + if(!cmd || !ssl) + return; + /* skip i=0 which is me */ + for(i=1; iworker->daemon->num; i++) { + worker_send_cmd(rc->worker->daemon->workers[i], + worker_cmd_remote); + if(!tube_write_msg(rc->worker->daemon->workers[i]->cmd, + (uint8_t*)cmd, strlen(cmd)+1, 0)) { + ssl_printf(ssl, "error could not distribute cmd\n"); + return; + } + } +} + /** execute a remote control command */ static void -execute_cmd(struct daemon_remote* rc, SSL* ssl, char* cmd) +execute_cmd(struct daemon_remote* rc, SSL* ssl, char* cmd, + struct worker* worker) { char* p = skipwhite(cmd); /* compare command - check longer strings first in case of substrings*/ if(strncmp(p, "stop", 4) == 0) { do_stop(ssl, rc); + return; } else if(strncmp(p, "reload", 6) == 0) { do_reload(ssl, rc); - } else if(strncmp(p, "verbosity", 9) == 0) { - do_verbosity(ssl, skipwhite(p+9)); + return; } else if(strncmp(p, "stats", 5) == 0) { do_stats(ssl, rc); - } else if(strncmp(p, "local_zone_remove", 17) == 0) { - do_zone_remove(ssl, rc->worker, skipwhite(p+17)); - } else if(strncmp(p, "local_zone", 10) == 0) { - do_zone_add(ssl, rc->worker, skipwhite(p+10)); - } else if(strncmp(p, "local_data_remove", 17) == 0) { - do_data_remove(ssl, rc->worker, skipwhite(p+17)); - } else if(strncmp(p, "local_data", 10) == 0) { - do_data_add(ssl, rc->worker, skipwhite(p+10)); + return; } else if(strncmp(p, "dump_cache", 10) == 0) { - (void)dump_cache(ssl, rc->worker); + (void)dump_cache(ssl, worker); + return; } else if(strncmp(p, "load_cache", 10) == 0) { - if(load_cache(ssl, rc->worker)) send_ok(ssl); + if(load_cache(ssl, worker)) send_ok(ssl); + return; } else if(strncmp(p, "lookup", 6) == 0) { - do_lookup(ssl, rc->worker, skipwhite(p+6)); + do_lookup(ssl, worker, skipwhite(p+6)); + return; + } + +#ifdef THREADS_DISABLED + /* other processes must execute the command as well */ + /* commands that should not be distributed, returned above. */ + if(rc) { /* only if this thread is the master (rc) thread */ + /* done before the code below, which may split the string */ + distribute_cmd(rc, ssl, cmd); + } +#endif + if(strncmp(p, "verbosity", 9) == 0) { + do_verbosity(ssl, skipwhite(p+9)); + } else if(strncmp(p, "local_zone_remove", 17) == 0) { + do_zone_remove(ssl, worker, skipwhite(p+17)); + } else if(strncmp(p, "local_zone", 10) == 0) { + do_zone_add(ssl, worker, skipwhite(p+10)); + } else if(strncmp(p, "local_data_remove", 17) == 0) { + do_data_remove(ssl, worker, skipwhite(p+17)); + } else if(strncmp(p, "local_data", 10) == 0) { + do_data_add(ssl, worker, skipwhite(p+10)); } else if(strncmp(p, "flush_zone", 10) == 0) { - do_flush_zone(ssl, rc->worker, skipwhite(p+10)); + do_flush_zone(ssl, worker, skipwhite(p+10)); } else if(strncmp(p, "flush_type", 10) == 0) { - do_flush_type(ssl, rc->worker, skipwhite(p+10)); + do_flush_type(ssl, worker, skipwhite(p+10)); } else if(strncmp(p, "flush", 5) == 0) { - do_flush_name(ssl, rc->worker, skipwhite(p+5)); + do_flush_name(ssl, worker, skipwhite(p+5)); } else { (void)ssl_printf(ssl, "error unknown command '%s'\n", p); } } +void +daemon_remote_exec(struct worker* worker) +{ + /* read the cmd string */ + uint8_t* msg = NULL; + size_t len = 0; + if(!tube_read_msg(worker->cmd, &msg, &len, 0)) { + log_err("daemon_remote_exec: tube_read_msg failed"); + return; + } + verbose(VERB_ALGO, "remote exec distributed: %s", (char*)msg); + execute_cmd(NULL, NULL, (char*)msg, worker); + free(msg); +} + /** handle remote control request */ static void handle_req(struct daemon_remote* rc, struct rc_state* s, SSL* ssl) @@ -1256,7 +1311,7 @@ handle_req(struct daemon_remote* rc, struct rc_state* s, SSL* ssl) verbose(VERB_DETAIL, "control cmd: %s", buf); /* figure out what to do */ - execute_cmd(rc, ssl, buf); + execute_cmd(rc, ssl, buf, rc->worker); } int remote_control_callback(struct comm_point* c, void* arg, int err, diff --git a/daemon/remote.h b/daemon/remote.h index ea30c803e..f47a9c62d 100644 --- a/daemon/remote.h +++ b/daemon/remote.h @@ -124,6 +124,12 @@ struct listen_port* daemon_remote_open_ports(struct config_file* cfg); int daemon_remote_open_accept(struct daemon_remote* rc, struct listen_port* ports); +/** + * Handle nonthreaded remote cmd execution. + * @param worker: this worker (the remote worker). + */ +void daemon_remote_exec(struct worker* worker); + /** handle remote control accept callbacks */ int remote_accept_callback(struct comm_point*, void*, int, struct comm_reply*); diff --git a/daemon/worker.c b/daemon/worker.c index 856b0be9a..911ad193e 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -344,6 +344,12 @@ worker_handle_control_cmd(struct tube* ATTR_UNUSED(tube), uint8_t* msg, verbose(VERB_ALGO, "got control cmd stats"); server_stats_reply(worker); break; +#ifdef THREADS_DISABLED + case worker_cmd_remote: + verbose(VERB_ALGO, "got control cmd remote"); + daemon_remote_exec(worker); + break; +#endif default: log_err("bad command %d", (int)cmd); break; diff --git a/daemon/worker.h b/daemon/worker.h index fd83b61c6..84b43980b 100644 --- a/daemon/worker.h +++ b/daemon/worker.h @@ -66,7 +66,9 @@ enum worker_commands { /** make the worker quit */ worker_cmd_quit, /** obtain statistics */ - worker_cmd_stats + worker_cmd_stats, + /** execute remote control command */ + worker_cmd_remote }; /** diff --git a/doc/Changelog b/doc/Changelog index 1be7e7132..fb882bb47 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -4,6 +4,10 @@ was using multiple processes. - iana portlist updated. - test for remote control with interprocess communication. + - created command distribution mechanism so that remote control + commands other than 'stats' work on all processes in a nonthreaded + compiled version. dump/load cache work, on the first process. + - fixup remote control local_data addition memory corruption bug. 1 December 2008: Wouter - SElinux policy files in contrib/selinux for the unbound daemon, diff --git a/services/localzone.c b/services/localzone.c index d0c7af830..354db66df 100644 --- a/services/localzone.c +++ b/services/localzone.c @@ -1233,10 +1233,11 @@ local_zones_add_RR(struct local_zones* zones, const char* rr, ldns_buffer* buf) lock_quick_unlock(&zones->lock); return 0; } + } else { + free(rr_name); } lock_rw_wrlock(&z->lock); lock_quick_unlock(&zones->lock); - free(rr_name); r = lz_enter_rr_into_zone(z, buf, rr); lock_rw_unlock(&z->lock); return r; diff --git a/testcode/fake_event.c b/testcode/fake_event.c index 3cc522ae0..47a9a271d 100644 --- a/testcode/fake_event.c +++ b/testcode/fake_event.c @@ -57,6 +57,7 @@ #include "testcode/ldns-testpkts.h" #include "util/log.h" #include +struct worker; /** Global variable: the scenario. Saved here for when event_init is done. */ static struct replay_scenario* saved_scenario = NULL; @@ -1160,4 +1161,8 @@ struct event_base* comm_base_internal(struct comm_base* ATTR_UNUSED(b)) return NULL; } +void daemon_remote_exec(struct worker* ATTR_UNUSED(worker)) +{ +} + /*********** End of Dummy routines ***********/ diff --git a/testdata/remote-threaded.tpkg b/testdata/remote-threaded.tpkg index 17947ce85d967303660b43384e49ae57821e54cc..8ff01f22057fa7027e795f85d0dd9756dbedbbb9 100644 GIT binary patch literal 7327 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 F000For+)wd literal 7096 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