From: Wouter Wijngaards Date: Thu, 22 Feb 2007 16:22:54 +0000 (+0000) Subject: daemon code. X-Git-Tag: release-0.1~35 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d5de0d10d5933da6484645922cf84cc7b9ff0411;p=thirdparty%2Funbound.git daemon code. git-svn-id: file:///svn/unbound/trunk@136 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/Makefile.in b/Makefile.in index f2dd6604e..ba622448d 100644 --- a/Makefile.in +++ b/Makefile.in @@ -57,7 +57,7 @@ UNITTEST_SRC=testcode/unitmain.c $(COMMON_SRC) UNITTEST_OBJ=$(addprefix $(BUILD),$(UNITTEST_SRC:.c=.o)) $(COMPAT_OBJ) DAEMON_SRC=$(wildcard daemon/*.c) $(COMMON_SRC) DAEMON_OBJ=$(addprefix $(BUILD),$(DAEMON_SRC:.c=.o)) $(COMPAT_OBJ) -TESTBOUND_SRC=testcode/testbound.c testcode/ldns-testpkts.c daemon/worker.c testcode/replay.c testcode/fake_event.c $(filter-out util/netevent.c services/listen_dnsport.c services/outside_network.c, $(COMMON_SRC)) +TESTBOUND_SRC=testcode/testbound.c testcode/ldns-testpkts.c daemon/worker.c daemon/daemon.c testcode/replay.c testcode/fake_event.c $(filter-out util/netevent.c services/listen_dnsport.c services/outside_network.c, $(COMMON_SRC)) TESTBOUND_OBJ=$(addprefix $(BUILD),$(TESTBOUND_SRC:.c=.o)) $(COMPAT_OBJ) ALL_SRC=$(COMMON_SRC) $(UNITTEST_SRC) $(DAEMON_SRC) $(TESTBOUND_SRC) ALL_OBJ=$(addprefix $(BUILD),$(ALL_SRC:.c=.o) $(addprefix compat/,$(LIBOBJS))) $(COMPAT_OBJ) diff --git a/daemon/daemon.c b/daemon/daemon.c new file mode 100644 index 000000000..a2bc58abd --- /dev/null +++ b/daemon/daemon.c @@ -0,0 +1,117 @@ +/* + * daemon/daemon.c - collection of workers that handles requests. + * + * Copyright (c) 2007, NLnet Labs. All rights reserved. + * + * This software is open source. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NLNET LABS nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * \file + * + * The daemon consists of global settings and a number of workers. + */ + +/** buffer size for network connections */ +#define BUFSZ 65552 + +#include "config.h" +#include "daemon/daemon.h" +#include "daemon/worker.h" +#include "util/log.h" +#include "util/config_file.h" +#include "services/listen_dnsport.h" + +struct daemon* +daemon_init() +{ + struct daemon* daemon = (struct daemon*)calloc(1, + sizeof(struct daemon)); + if(!daemon) + return NULL; + lock_basic_init(&daemon->lock); + daemon->need_to_exit = 0; + return daemon; +} + +int +daemon_open_shared_ports(struct daemon* daemon, struct config_file* cfg) +{ + log_assert(daemon); + daemon->cfg = cfg; + if(daemon->cfg->port == daemon->listening_port) + return 1; + listening_ports_free(daemon->ports); + if(!(daemon->ports=listening_ports_open(daemon->cfg))) + return 0; + daemon->listening_port = daemon->cfg->port; + return 1; +} + +void +daemon_fork(struct daemon* daemon) +{ + /* only one thread for now */ + log_assert(daemon); + daemon->num = 1; + daemon->workers = (struct worker**)calloc((size_t)daemon->num, + sizeof(struct worker*)); + if(!(daemon->workers[0] = worker_init(daemon->cfg, BUFSZ))) + fatal_exit("could not initialize thread # %d", 0); + daemon->workers[0]->daemon = daemon; + daemon->workers[0]->thread_num = 0; + + log_info("start of service (%s).", PACKAGE_STRING); + worker_work(daemon->workers[0]); + daemon->need_to_exit = 1; +} + +void +daemon_cleanup(struct daemon* daemon) +{ + int i; + log_assert(daemon); + for(i=0; inum; i++) + worker_delete(daemon->workers[i]); + free(daemon->workers); + daemon->workers = NULL; + daemon->num = 0; + daemon->cfg = NULL; +} + +void +daemon_delete(struct daemon* daemon) +{ + if(!daemon) + return; + listening_ports_free(daemon->ports); + lock_basic_destroy(&daemon->lock); + free(daemon); +} diff --git a/daemon/daemon.h b/daemon/daemon.h new file mode 100644 index 000000000..256c66893 --- /dev/null +++ b/daemon/daemon.h @@ -0,0 +1,104 @@ +/* + * daemon/daemon.h - collection of workers that handles requests. + * + * Copyright (c) 2007, NLnet Labs. All rights reserved. + * + * This software is open source. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NLNET LABS nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * \file + * + * The daemon consists of global settings and a number of workers. + */ + +#ifndef DAEMON_H +#define DAEMON_H + +#include "util/locks.h" +struct config_file; +struct worker; +struct listen_port; + +/** + * Structure holding worker list. + * Holds globally visible information. + */ +struct daemon { + /** mutex for exclusive access to this structure. */ + lock_basic_t lock; + /** The config settings */ + struct config_file* cfg; + /** port number that has ports opened. */ + int listening_port; + /** listening ports, opened, to be shared by threads */ + struct listen_port* ports; + /** num threads allocated */ + int num; + /** the worker entries */ + struct worker** workers; + /** do we need to exit unbound (or is it only a reload?) */ + int need_to_exit; +}; + +/** + * Initialize daemon structure. + * @return: The daemon structure, or NULL on error. + */ +struct daemon* daemon_init(); + +/** + * Open shared listening ports (if needed). + * @param daemon: the daemon. + * @param cfg: the cfg settings. Applied to daemon. + * @return: false on error. + */ +int daemon_open_shared_ports(struct daemon* daemon, struct config_file* cfg); + +/** + * Fork workers and start service. + * When the routine exits, it is no longer forked. + * @param daemon: the daemon. + */ +void daemon_fork(struct daemon* daemon); + +/** + * Close off the worker thread information. + * Bring the daemon back into state ready for daemon_fork again. + * @param daemon: the daemon. + */ +void daemon_cleanup(struct daemon* daemon); + +/** + * Delete worker. + */ +void daemon_delete(struct daemon* daemon); + +#endif /* DAEMON_H */ diff --git a/daemon/unbound.c b/daemon/unbound.c index 06c0beb4e..b3d4fab11 100644 --- a/daemon/unbound.c +++ b/daemon/unbound.c @@ -42,11 +42,10 @@ #include "config.h" #include "util/log.h" -#include "daemon/worker.h" +#include "daemon/daemon.h" #include "util/config_file.h" - -/** buffer size for network connections */ -#define BUFSZ 65552 +#include +#include /** print usage. */ static void usage() @@ -61,6 +60,51 @@ static void usage() printf("Report bugs to %s\n", PACKAGE_BUGREPORT); } +/** daemonize, drop user priviliges and chroot if needed */ +static void +do_chroot(struct config_file* cfg) +{ + log_assert(cfg); + + /* daemonize last to be able to print error to user */ + if(cfg->chrootdir && cfg->chrootdir[0]) + if(chroot(cfg->chrootdir)) + fatal_exit("unable to chroot: %s", strerror(errno)); + if(cfg->username && cfg->username[0]) { + struct passwd *pwd; + if((pwd = getpwnam(cfg->username)) == NULL) + fatal_exit("user '%s' does not exist.", cfg->username); + if(setgid(pwd->pw_gid) != 0) + fatal_exit("unable to set group id: %s", strerror(errno)); + if(setuid(pwd->pw_uid) != 0) + fatal_exit("unable to set user id: %s", strerror(errno)); + endpwent(); + } + if(cfg->do_daemonize) { + int fd; + /* Take off... */ + switch (fork()) { + case 0: + break; + case -1: + fatal_exit("fork failed: %s", strerror(errno)); + default: + /* exit interactive session */ + exit(0); + } + /* detach */ + if(setsid() == -1) + fatal_exit("setsid() failed: %s", strerror(errno)); + if ((fd = open("/dev/null", O_RDWR, 0)) != -1) { + (void)dup2(fd, STDIN_FILENO); + (void)dup2(fd, STDOUT_FILENO); + (void)dup2(fd, STDERR_FILENO); + if (fd > 2) + (void)close(fd); + } + } +} + /** * Run the daemon. * @param cfgfile: the config file name. @@ -69,35 +113,41 @@ static void usage() */ static void run_daemon(const char* cfgfile, int cmdline_verbose) { - struct worker* worker = NULL; - struct config_file *cfg = NULL; + struct config_file* cfg = NULL; + struct daemon* daemon = NULL; + int done_chroot = 0; - if(!(cfg = config_create())) { - fprintf(stderr, "Could not init config defaults."); - exit(1); - } - if(cfgfile) { - if(!config_read(cfg, cfgfile)) { - config_delete(cfg); - exit(1); - } - verbosity = cmdline_verbose + cfg->verbosity; - } - log_info("Start of %s.", PACKAGE_STRING); + if(!(daemon = daemon_init())) + fatal_exit("alloc failure"); + while(!daemon->need_to_exit) { + if(done_chroot) + log_info("Restart of %s.", PACKAGE_STRING); + else log_info("Start of %s.", PACKAGE_STRING); - /* setup */ - worker = worker_init(cfg, BUFSZ); - if(!worker) { - fatal_exit("could not initialize"); - } + /* config stuff */ + if(!(cfg = config_create())) + fatal_exit("Could not alloc config defaults"); + if(!config_read(cfg, cfgfile)) + fatal_exit("Could not read config file: %s", cfgfile); + verbosity = cmdline_verbose + cfg->verbosity; - /* drop user priviliges and chroot if needed */ - log_info("start of service (%s).", PACKAGE_STRING); - worker_work(worker); + /* prepare */ + if(!daemon_open_shared_ports(daemon, cfg)) + fatal_exit("could not open ports"); + if(!done_chroot) { + do_chroot(cfg); + done_chroot = 1; + } + /* work */ + daemon_fork(daemon); - /* cleanup */ + /* clean up for restart */ + verbose(VERB_ALGO, "cleanup."); + daemon_cleanup(daemon); + config_delete(cfg); + } verbose(VERB_ALGO, "Exit cleanup."); - worker_delete(worker); + daemon_delete(daemon); } /** getopt global, in case header files fail to declare it. */ diff --git a/daemon/worker.c b/daemon/worker.c index 65f8edb75..b4d46e9ed 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -281,6 +281,7 @@ worker_delete(struct worker* worker) outside_network_delete(worker->back); comm_signal_delete(worker->comsig); comm_base_delete(worker->base); + free(worker->rndstate); free(worker); } diff --git a/daemon/worker.h b/daemon/worker.h index 703e0805a..c0d167109 100644 --- a/daemon/worker.h +++ b/daemon/worker.h @@ -45,9 +45,11 @@ #include "config.h" #include "util/netevent.h" +#include "util/locks.h" struct listen_dnsport; struct outside_network; struct config_file; +struct daemon; /** size of table used for random numbers. large to be more secure. */ #define RND_STATE_SIZE 256 @@ -57,6 +59,10 @@ struct config_file; * Holds globally visible information. */ struct worker { + /** global shared daemon structure */ + struct daemon* daemon; + /** the thread number (in daemon array). */ + int thread_num; /** the event base this worker works with */ struct comm_base* base; /** the frontside listening interface where request events come in */ diff --git a/services/listen_dnsport.c b/services/listen_dnsport.c index d9a7b889a..ce00bbaa0 100644 --- a/services/listen_dnsport.c +++ b/services/listen_dnsport.c @@ -391,3 +391,14 @@ listen_delete(struct listen_dnsport* front) ldns_buffer_free(front->udp_buff); free(front); } + +struct listen_port* +listening_ports_open(struct config_file* cfg) +{ + return calloc(1,1); +} + +void listening_ports_free(struct listen_port* list) +{ + free(list); +} diff --git a/services/listen_dnsport.h b/services/listen_dnsport.h index 289c8709d..3b644fe43 100644 --- a/services/listen_dnsport.h +++ b/services/listen_dnsport.h @@ -46,6 +46,7 @@ #include "util/netevent.h" struct listen_list; struct addrinfo; +struct config_file; /** * Listening for queries structure. @@ -73,6 +74,31 @@ struct listen_list { struct comm_point* com; }; +/** + * Single linked list to store shared ports that have been + * opened for use by all threads. + */ +struct listen_port { + /** next in list */ + struct listen_port* next; + /** file descriptor, open and ready for use */ + int fd; + /** type of file descriptor, udp or tcp */ + int is_udp; +}; + +/** + * Create shared listening ports + * @param cfg: settings on what ports to open. + * @return: linked list of ports or NULL on error. + */ +struct listen_port* listening_ports_open(struct config_file* cfg); + +/** + * Close and delete the (list of) listening ports. + */ +void listening_ports_free(struct listen_port* list); + /** * Getaddrinfo, create socket, bind and listen to zero or more * interfaces for IP4 and/or IP6, for UDP and/or TCP. diff --git a/testcode/fake_event.c b/testcode/fake_event.c index 9a88c521b..d6132d421 100644 --- a/testcode/fake_event.c +++ b/testcode/fake_event.c @@ -690,4 +690,15 @@ pending_udp_query(struct outside_network* outnet, ldns_buffer* packet, runtime->pending_list = pend; } +struct listen_port* listening_ports_open(struct config_file* ATTR_UNUSED(cfg)) +{ + return calloc(1, 1); +} + +void listening_ports_free(struct listen_port* list) +{ + free(list); +} + + /*********** End of Dummy routines ***********/ diff --git a/util/config_file.c b/util/config_file.c index 30d201b4f..0c2660476 100644 --- a/util/config_file.c +++ b/util/config_file.c @@ -77,12 +77,20 @@ config_create() cfg->do_tcp = 1; cfg->outgoing_base_port = cfg->port + 1000; cfg->outgoing_num_ports = 16; - cfg->fwd_address = strdup(""); - if(!cfg->fwd_address) { - free(cfg); + if(!(cfg->fwd_address = strdup(""))) { + config_delete(cfg); + return NULL; + } + if(!(cfg->username = strdup(""))) { + config_delete(cfg); + return NULL; + } + if(!(cfg->chrootdir = strdup(""))) { + config_delete(cfg); return NULL; } cfg->fwd_port = UNBOUND_DNS_PORT; + cfg->do_daemonize = 0; return cfg; } @@ -101,7 +109,10 @@ create_cfg_parser(struct config_file* cfg, char* filename) int config_read(struct config_file* cfg, const char* filename) { - FILE *in = fopen(filename, "r"); + FILE *in; + if(!filename) + return 1; + in = fopen(filename, "r"); if(!in) { log_err("Could not open %s: %s", filename, strerror(errno)); return 0; @@ -124,6 +135,8 @@ config_delete(struct config_file* cfg) { if(!cfg) return; free(cfg->fwd_address); + free(cfg->username); + free(cfg->chrootdir); free(cfg); } diff --git a/util/config_file.h b/util/config_file.h index 8e00d32c8..42e2d7ebf 100644 --- a/util/config_file.h +++ b/util/config_file.h @@ -73,6 +73,14 @@ struct config_file { char* fwd_address; /** forwarder port */ int fwd_port; + + /** chrootdir, if not "" or chroot will be done */ + char* chrootdir; + /** username to change to, if not "". */ + char* username; + + /** daemonize, i.e. fork into the background. */ + int do_daemonize; }; /** @@ -84,7 +92,7 @@ struct config_file* config_create(); /** * Read the config file from the specified filename. * @param config: where options are stored into, must be freshly created. - * @param filename: name of configfile. + * @param filename: name of configfile. If NULL nothing is done. * @return: false on error. */ int config_read(struct config_file* config, const char* filename); diff --git a/util/netevent.c b/util/netevent.c index 147f8fa97..36789131f 100644 --- a/util/netevent.c +++ b/util/netevent.c @@ -676,6 +676,7 @@ comm_point_delete(struct comm_point* c) comm_point_delete(c->tcp_handlers[i]); free(c->tcp_handlers); } + free(c->timeout); if(c->type == comm_tcp) ldns_buffer_free(c->buffer); free(c->ev);