]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
daemon code.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Thu, 22 Feb 2007 16:22:54 +0000 (16:22 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Thu, 22 Feb 2007 16:22:54 +0000 (16:22 +0000)
git-svn-id: file:///svn/unbound/trunk@136 be551aaa-1e26-0410-a405-d3ace91eadb9

12 files changed:
Makefile.in
daemon/daemon.c [new file with mode: 0644]
daemon/daemon.h [new file with mode: 0644]
daemon/unbound.c
daemon/worker.c
daemon/worker.h
services/listen_dnsport.c
services/listen_dnsport.h
testcode/fake_event.c
util/config_file.c
util/config_file.h
util/netevent.c

index f2dd6604e71ee82c47d1cecd6aa7892cc8affa33..ba622448d7fea691b37a64960259b2b4e4102695 100644 (file)
@@ -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 (file)
index 0000000..a2bc58a
--- /dev/null
@@ -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; i<daemon->num; 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 (file)
index 0000000..256c668
--- /dev/null
@@ -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 */
index 06c0beb4e896f67146a7aab9948b5d7f440abd4d..b3d4fab1100bfb937adad73c87d7eb67ecfe356a 100644 (file)
 
 #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 <fcntl.h>
+#include <pwd.h>
 
 /** 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. */
index 65f8edb7553f451635548b16d115b32fa65c0a2f..b4d46e9edeb3534e1c9e78ec92751bf60bdffacc 100644 (file)
@@ -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);
 }
 
index 703e0805aeca8336fbe043d1de6f0d92e9191fb6..c0d1671096943731432a11c99957b3c19f2a7807 100644 (file)
 
 #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 */
index d9a7b889a62e933182807c73a1c5964ea58f6d6a..ce00bbaa029822ba4be1177b6f78ada0424f29a4 100644 (file)
@@ -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);
+}
index 289c8709da4e69c8cc53b6bf06fdb05d1234d6e3..3b644fe4339a48e22cd7d8c0fe434203738d7a41 100644 (file)
@@ -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.
index 9a88c521b6e657e1dac7106484a38e5bfbf3af7a..d6132d421664984332a656772df3b5df11221650 100644 (file)
@@ -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 ***********/
index 30d201b4fed6d19914f1ef3b5de5f7b2665f23d2..0c2660476db322440fc906ab361a3ba63e26f1dd 100644 (file)
@@ -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);
 }
 
index 8e00d32c837c3241800d03310d8123f76af3dd10..42e2d7ebfc313b158449b4d4e4d5dcfbfca7bd4e 100644 (file)
@@ -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);
index 147f8fa977bff1c889c393aad7b5e3e01c765632..36789131f56dfcf274edcdc336983d8c950c2c51 100644 (file)
@@ -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);