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)
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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 */
#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()
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.
*/
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. */
outside_network_delete(worker->back);
comm_signal_delete(worker->comsig);
comm_base_delete(worker->base);
+ free(worker->rndstate);
free(worker);
}
#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
* 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 */
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);
+}
#include "util/netevent.h"
struct listen_list;
struct addrinfo;
+struct config_file;
/**
* Listening for queries structure.
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.
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 ***********/
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;
}
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;
{
if(!cfg) return;
free(cfg->fwd_address);
+ free(cfg->username);
+ free(cfg->chrootdir);
free(cfg);
}
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;
};
/**
/**
* 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);
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);