xref: /openbsd-src/usr.sbin/unbound/daemon/unbound.c (revision 98bc733b08604094f4138174a0ee0bb9faaca4bd)
1933707f3Ssthen /*
2933707f3Ssthen  * daemon/unbound.c - main program for unbound DNS resolver daemon.
3933707f3Ssthen  *
4933707f3Ssthen  * Copyright (c) 2007, NLnet Labs. All rights reserved.
5933707f3Ssthen  *
6933707f3Ssthen  * This software is open source.
7933707f3Ssthen  *
8933707f3Ssthen  * Redistribution and use in source and binary forms, with or without
9933707f3Ssthen  * modification, are permitted provided that the following conditions
10933707f3Ssthen  * are met:
11933707f3Ssthen  *
12933707f3Ssthen  * Redistributions of source code must retain the above copyright notice,
13933707f3Ssthen  * this list of conditions and the following disclaimer.
14933707f3Ssthen  *
15933707f3Ssthen  * Redistributions in binary form must reproduce the above copyright notice,
16933707f3Ssthen  * this list of conditions and the following disclaimer in the documentation
17933707f3Ssthen  * and/or other materials provided with the distribution.
18933707f3Ssthen  *
19933707f3Ssthen  * Neither the name of the NLNET LABS nor the names of its contributors may
20933707f3Ssthen  * be used to endorse or promote products derived from this software without
21933707f3Ssthen  * specific prior written permission.
22933707f3Ssthen  *
23933707f3Ssthen  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
240b68ff31Ssthen  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
250b68ff31Ssthen  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
260b68ff31Ssthen  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
270b68ff31Ssthen  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
280b68ff31Ssthen  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
290b68ff31Ssthen  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
300b68ff31Ssthen  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
310b68ff31Ssthen  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
320b68ff31Ssthen  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
330b68ff31Ssthen  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34933707f3Ssthen  *
35933707f3Ssthen  */
36933707f3Ssthen 
37933707f3Ssthen /**
38933707f3Ssthen  * \file
39933707f3Ssthen  *
40933707f3Ssthen  * Main program to start the DNS resolver daemon.
41933707f3Ssthen  */
42933707f3Ssthen 
43933707f3Ssthen #include "config.h"
44933707f3Ssthen #ifdef HAVE_GETOPT_H
45933707f3Ssthen #include <getopt.h>
46933707f3Ssthen #endif
47933707f3Ssthen #include <sys/time.h>
48933707f3Ssthen #include "util/log.h"
49933707f3Ssthen #include "daemon/daemon.h"
50933707f3Ssthen #include "daemon/remote.h"
51933707f3Ssthen #include "util/config_file.h"
52933707f3Ssthen #include "util/storage/slabhash.h"
53933707f3Ssthen #include "services/listen_dnsport.h"
54933707f3Ssthen #include "services/cache/rrset.h"
55933707f3Ssthen #include "services/cache/infra.h"
56e10d3884Sbrad #include "util/fptr_wlist.h"
57933707f3Ssthen #include "util/data/msgreply.h"
58933707f3Ssthen #include "util/module.h"
59933707f3Ssthen #include "util/net_help.h"
602ee382b6Ssthen #include "util/ub_event.h"
61933707f3Ssthen #include <signal.h>
62933707f3Ssthen #include <fcntl.h>
63933707f3Ssthen #include <openssl/crypto.h>
64933707f3Ssthen #ifdef HAVE_PWD_H
65933707f3Ssthen #include <pwd.h>
66933707f3Ssthen #endif
67933707f3Ssthen #ifdef HAVE_GRP_H
68933707f3Ssthen #include <grp.h>
69933707f3Ssthen #endif
70f6b99bafSsthen #include <openssl/ssl.h>
71933707f3Ssthen 
720b68ff31Ssthen #ifndef S_SPLINT_S
730b68ff31Ssthen /* splint chokes on this system header file */
74933707f3Ssthen #ifdef HAVE_SYS_RESOURCE_H
75933707f3Ssthen #include <sys/resource.h>
76933707f3Ssthen #endif
770b68ff31Ssthen #endif /* S_SPLINT_S */
78933707f3Ssthen #ifdef HAVE_LOGIN_CAP_H
79933707f3Ssthen #include <login_cap.h>
80933707f3Ssthen #endif
81933707f3Ssthen 
82933707f3Ssthen #ifdef UB_ON_WINDOWS
83933707f3Ssthen #  include "winrc/win_svc.h"
84933707f3Ssthen #endif
85933707f3Ssthen 
86cebdf579Ssthen #ifdef HAVE_NSS
87e9c7b4efSsthen /* nss3 */
88e9c7b4efSsthen #  include "nss.h"
89cebdf579Ssthen #endif
90cebdf579Ssthen 
91a3167c07Ssthen #ifdef HAVE_TARGETCONDITIONALS_H
92a3167c07Ssthen #include <TargetConditionals.h>
93a3167c07Ssthen #endif
94a3167c07Ssthen 
952c144df0Ssthen #if (defined(TARGET_OS_TV) && TARGET_OS_TV) || (defined(TARGET_OS_WATCH) && TARGET_OS_WATCH)
96a3167c07Ssthen #undef HAVE_FORK
97a3167c07Ssthen #endif
98a3167c07Ssthen 
998240c1b9Ssthen /** print build options. */
1008240c1b9Ssthen static void
1018240c1b9Ssthen print_build_options(void)
102933707f3Ssthen {
103933707f3Ssthen 	const char** m;
104933707f3Ssthen 	const char *evnm="event", *evsys="", *evmethod="";
105df365112Ssthen 	time_t t;
106df365112Ssthen 	struct timeval now;
107df365112Ssthen 	struct ub_event_base* base;
1088240c1b9Ssthen 	printf("Version %s\n\n", PACKAGE_VERSION);
1098240c1b9Ssthen 	printf("Configure line: %s\n", CONFCMDLINE);
110df365112Ssthen 	base = ub_default_event_base(0,&t,&now);
111df365112Ssthen 	ub_get_event_sys(base, &evnm, &evsys, &evmethod);
1128240c1b9Ssthen 	printf("Linked libs: %s %s (it uses %s), %s\n",
1130b68ff31Ssthen 		evnm, evsys, evmethod,
114cebdf579Ssthen #ifdef HAVE_SSL
11577079be7Ssthen #  ifdef SSLEAY_VERSION
116cebdf579Ssthen 		SSLeay_version(SSLEAY_VERSION)
11777079be7Ssthen #  else
11877079be7Ssthen 		OpenSSL_version(OPENSSL_VERSION)
11977079be7Ssthen #  endif
120cebdf579Ssthen #elif defined(HAVE_NSS)
121cebdf579Ssthen 		NSS_GetVersion()
12224893edcSsthen #elif defined(HAVE_NETTLE)
12324893edcSsthen 		"nettle"
124cebdf579Ssthen #endif
125cebdf579Ssthen 		);
1268240c1b9Ssthen 	printf("Linked modules:");
127933707f3Ssthen 	for(m = module_list_avail(); *m; m++)
128933707f3Ssthen 		printf(" %s", *m);
129933707f3Ssthen 	printf("\n");
1307191de28Ssthen #ifdef USE_DNSCRYPT
1317191de28Ssthen 	printf("DNSCrypt feature available\n");
1327191de28Ssthen #endif
1338240c1b9Ssthen #ifdef USE_TCP_FASTOPEN
1348240c1b9Ssthen 	printf("TCP Fastopen feature available\n");
1358240c1b9Ssthen #endif
1368240c1b9Ssthen 	ub_event_base_free(base);
1378240c1b9Ssthen 	printf("\nBSD licensed, see LICENSE in source package for details.\n");
1388240c1b9Ssthen 	printf("Report bugs to %s\n", PACKAGE_BUGREPORT);
1398240c1b9Ssthen }
1408240c1b9Ssthen 
1418240c1b9Ssthen /** print usage. */
1428240c1b9Ssthen static void
1438240c1b9Ssthen usage(void)
1448240c1b9Ssthen {
1458240c1b9Ssthen 	printf("usage:  unbound [options]\n");
1468240c1b9Ssthen 	printf("	start unbound daemon DNS resolver.\n");
1478240c1b9Ssthen 	printf("-h	this help.\n");
1488240c1b9Ssthen 	printf("-c file	config file to read instead of %s\n", CONFIGFILE);
1498240c1b9Ssthen 	printf("	file format is described in unbound.conf(5).\n");
1508240c1b9Ssthen 	printf("-d	do not fork into the background.\n");
1518240c1b9Ssthen 	printf("-p	do not create a pidfile.\n");
1528240c1b9Ssthen 	printf("-v	verbose (more times to increase verbosity).\n");
1538240c1b9Ssthen 	printf("-V	show version number and build options.\n");
1548240c1b9Ssthen #ifdef UB_ON_WINDOWS
1558240c1b9Ssthen 	printf("-w opt	windows option: \n");
1568240c1b9Ssthen 	printf("   	install, remove - manage the services entry\n");
1578240c1b9Ssthen 	printf("   	service - used to start from services control panel\n");
1588240c1b9Ssthen #endif
1598240c1b9Ssthen 	printf("\nVersion %s\n", PACKAGE_VERSION);
160933707f3Ssthen 	printf("BSD licensed, see LICENSE in source package for details.\n");
161933707f3Ssthen 	printf("Report bugs to %s\n", PACKAGE_BUGREPORT);
162933707f3Ssthen }
163933707f3Ssthen 
164933707f3Ssthen #ifndef unbound_testbound
165933707f3Ssthen int replay_var_compare(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
166933707f3Ssthen {
167933707f3Ssthen         log_assert(0);
168933707f3Ssthen         return 0;
169933707f3Ssthen }
170933707f3Ssthen #endif
171933707f3Ssthen 
172933707f3Ssthen /** check file descriptor count */
173933707f3Ssthen static void
174933707f3Ssthen checkrlimits(struct config_file* cfg)
175933707f3Ssthen {
1760b68ff31Ssthen #ifndef S_SPLINT_S
177933707f3Ssthen #ifdef HAVE_GETRLIMIT
178933707f3Ssthen 	/* list has number of ports to listen to, ifs number addresses */
179933707f3Ssthen 	int list = ((cfg->do_udp?1:0) + (cfg->do_tcp?1 +
180933707f3Ssthen 			(int)cfg->incoming_num_tcp:0));
181933707f3Ssthen 	size_t listen_ifs = (size_t)(cfg->num_ifs==0?
182933707f3Ssthen 		((cfg->do_ip4 && !cfg->if_automatic?1:0) +
183933707f3Ssthen 		 (cfg->do_ip6?1:0)):cfg->num_ifs);
184933707f3Ssthen 	size_t listen_num = list*listen_ifs;
185933707f3Ssthen 	size_t outudpnum = (size_t)cfg->outgoing_num_ports;
186933707f3Ssthen 	size_t outtcpnum = cfg->outgoing_num_tcp;
187933707f3Ssthen 	size_t misc = 4; /* logfile, pidfile, stdout... */
188933707f3Ssthen 	size_t perthread_noudp = listen_num + outtcpnum +
189933707f3Ssthen 		2/*cmdpipe*/ + 2/*libevent*/ + misc;
190933707f3Ssthen 	size_t perthread = perthread_noudp + outudpnum;
191933707f3Ssthen 
192933707f3Ssthen #if !defined(HAVE_PTHREAD) && !defined(HAVE_SOLARIS_THREADS)
193933707f3Ssthen 	int numthread = 1; /* it forks */
194933707f3Ssthen #else
195933707f3Ssthen 	int numthread = (cfg->num_threads?cfg->num_threads:1);
196933707f3Ssthen #endif
197933707f3Ssthen 	size_t total = numthread * perthread + misc;
198933707f3Ssthen 	size_t avail;
199933707f3Ssthen 	struct rlimit rlim;
200191f22c6Ssthen 	size_t memsize_expect = cfg->msg_cache_size + cfg->rrset_cache_size
201191f22c6Ssthen 		+ (cfg->do_tcp?cfg->stream_wait_size:0)
202191f22c6Ssthen 		+ (cfg->ip_ratelimit?cfg->ip_ratelimit_size:0)
203191f22c6Ssthen 		+ (cfg->ratelimit?cfg->ratelimit_size:0)
204191f22c6Ssthen 		+ (cfg->dnscrypt?cfg->dnscrypt_shared_secret_cache_size + cfg->dnscrypt_nonce_cache_size:0)
205191f22c6Ssthen 		+ cfg->infra_cache_numhosts * (sizeof(struct infra_key)+sizeof(struct infra_data));
206191f22c6Ssthen 	if(strstr(cfg->module_conf, "validator") && (cfg->trust_anchor_file_list || cfg->trust_anchor_list || cfg->auto_trust_anchor_file_list || cfg->trusted_keys_file_list)) {
207191f22c6Ssthen 		memsize_expect += cfg->key_cache_size + cfg->neg_cache_size;
208191f22c6Ssthen 	}
209191f22c6Ssthen #ifdef HAVE_NGHTTP2_NGHTTP2_H
210191f22c6Ssthen 	if(cfg_has_https(cfg)) {
211191f22c6Ssthen 		memsize_expect += cfg->http_query_buffer_size + cfg->http_response_buffer_size;
212191f22c6Ssthen 	}
213191f22c6Ssthen #endif
214191f22c6Ssthen 
215191f22c6Ssthen #ifdef RLIMIT_AS
216191f22c6Ssthen 	if(getrlimit(RLIMIT_AS, &rlim) == 0) {
217191f22c6Ssthen 		if(rlim.rlim_cur != (rlim_t)RLIM_INFINITY &&
218191f22c6Ssthen 			rlim.rlim_cur < (rlim_t)memsize_expect) {
219191f22c6Ssthen 			log_warn("the ulimit(max memory size) is smaller than the expected memory usage (added size of caches). %u < %u bytes", (unsigned)rlim.rlim_cur, (unsigned)memsize_expect);
220191f22c6Ssthen 		}
221191f22c6Ssthen 	}
222191f22c6Ssthen #endif
223191f22c6Ssthen 	if(getrlimit(RLIMIT_DATA, &rlim) == 0) {
224191f22c6Ssthen 		if(rlim.rlim_cur != (rlim_t)RLIM_INFINITY &&
225191f22c6Ssthen 			rlim.rlim_cur < (rlim_t)memsize_expect) {
226191f22c6Ssthen 			log_warn("the ulimit(data seg size) is smaller than the expected memory usage (added size of caches). %u < %u bytes", (unsigned)rlim.rlim_cur, (unsigned)memsize_expect);
227191f22c6Ssthen 		}
228191f22c6Ssthen 	}
229933707f3Ssthen 
230933707f3Ssthen 	if(total > 1024 &&
2312ee382b6Ssthen 		strncmp(ub_event_get_version(), "mini-event", 10) == 0) {
232933707f3Ssthen 		log_warn("too many file descriptors requested. The builtin"
233933707f3Ssthen 			"mini-event cannot handle more than 1024. Config "
234933707f3Ssthen 			"for less fds or compile with libevent");
235933707f3Ssthen 		if(numthread*perthread_noudp+15 > 1024)
236933707f3Ssthen 			fatal_exit("too much tcp. not enough fds.");
237933707f3Ssthen 		cfg->outgoing_num_ports = (int)((1024
238933707f3Ssthen 			- numthread*perthread_noudp
239933707f3Ssthen 			- 10 /* safety margin */) /numthread);
240933707f3Ssthen 		log_warn("continuing with less udp ports: %u",
241933707f3Ssthen 			cfg->outgoing_num_ports);
242933707f3Ssthen 		total = 1024;
243933707f3Ssthen 	}
244933707f3Ssthen 	if(perthread > 64 &&
2452ee382b6Ssthen 		strncmp(ub_event_get_version(), "winsock-event", 13) == 0) {
246933707f3Ssthen 		log_err("too many file descriptors requested. The winsock"
247933707f3Ssthen 			" event handler cannot handle more than 64 per "
248933707f3Ssthen 			" thread. Config for less fds");
249933707f3Ssthen 		if(perthread_noudp+2 > 64)
250933707f3Ssthen 			fatal_exit("too much tcp. not enough fds.");
251933707f3Ssthen 		cfg->outgoing_num_ports = (int)((64
252933707f3Ssthen 			- perthread_noudp
253933707f3Ssthen 			- 2/* safety margin */));
254933707f3Ssthen 		log_warn("continuing with less udp ports: %u",
255933707f3Ssthen 			cfg->outgoing_num_ports);
256933707f3Ssthen 		total = numthread*(perthread_noudp+
257933707f3Ssthen 			(size_t)cfg->outgoing_num_ports)+misc;
258933707f3Ssthen 	}
259933707f3Ssthen 	if(getrlimit(RLIMIT_NOFILE, &rlim) < 0) {
260933707f3Ssthen 		log_warn("getrlimit: %s", strerror(errno));
261933707f3Ssthen 		return;
262933707f3Ssthen 	}
263933707f3Ssthen 	if(rlim.rlim_cur == (rlim_t)RLIM_INFINITY)
264933707f3Ssthen 		return;
265933707f3Ssthen 	if((size_t)rlim.rlim_cur < total) {
266933707f3Ssthen 		avail = (size_t)rlim.rlim_cur;
267933707f3Ssthen 		rlim.rlim_cur = (rlim_t)(total + 10);
268933707f3Ssthen 		rlim.rlim_max = (rlim_t)(total + 10);
269933707f3Ssthen #ifdef HAVE_SETRLIMIT
270933707f3Ssthen 		if(setrlimit(RLIMIT_NOFILE, &rlim) < 0) {
271933707f3Ssthen 			log_warn("setrlimit: %s", strerror(errno));
272933707f3Ssthen #endif
273933707f3Ssthen 			log_warn("cannot increase max open fds from %u to %u",
274933707f3Ssthen 				(unsigned)avail, (unsigned)total+10);
275933707f3Ssthen 			/* check that calculation below does not underflow,
276933707f3Ssthen 			 * with 15 as margin */
277933707f3Ssthen 			if(numthread*perthread_noudp+15 > avail)
278933707f3Ssthen 				fatal_exit("too much tcp. not enough fds.");
279933707f3Ssthen 			cfg->outgoing_num_ports = (int)((avail
280933707f3Ssthen 				- numthread*perthread_noudp
281933707f3Ssthen 				- 10 /* safety margin */) /numthread);
282933707f3Ssthen 			log_warn("continuing with less udp ports: %u",
283933707f3Ssthen 				cfg->outgoing_num_ports);
284933707f3Ssthen 			log_warn("increase ulimit or decrease threads, "
285933707f3Ssthen 				"ports in config to remove this warning");
286933707f3Ssthen 			return;
287e10d3884Sbrad #ifdef HAVE_SETRLIMIT
288933707f3Ssthen 		}
289e10d3884Sbrad #endif
29057dceb2aSbrad 		verbose(VERB_ALGO, "increased limit(open files) from %u to %u",
291933707f3Ssthen 			(unsigned)avail, (unsigned)total+10);
292933707f3Ssthen 	}
293933707f3Ssthen #else
294933707f3Ssthen 	(void)cfg;
295933707f3Ssthen #endif /* HAVE_GETRLIMIT */
2960b68ff31Ssthen #endif /* S_SPLINT_S */
297933707f3Ssthen }
298933707f3Ssthen 
299933707f3Ssthen /** set verbosity, check rlimits, cache settings */
300933707f3Ssthen static void
301933707f3Ssthen apply_settings(struct daemon* daemon, struct config_file* cfg,
302eaf2578eSsthen 	int cmdline_verbose, int debug_mode)
303933707f3Ssthen {
304933707f3Ssthen 	/* apply if they have changed */
305933707f3Ssthen 	verbosity = cmdline_verbose + cfg->verbosity;
306e10d3884Sbrad 	if (debug_mode > 1) {
307e10d3884Sbrad 		cfg->use_syslog = 0;
30877079be7Ssthen 		free(cfg->logfile);
309e10d3884Sbrad 		cfg->logfile = NULL;
310e10d3884Sbrad 	}
311933707f3Ssthen 	daemon_apply_cfg(daemon, cfg);
312933707f3Ssthen 	checkrlimits(cfg);
31377079be7Ssthen 
31477079be7Ssthen 	if (cfg->use_systemd && cfg->do_daemonize) {
31577079be7Ssthen 		log_warn("use-systemd and do-daemonize should not be enabled at the same time");
31677079be7Ssthen 	}
31777079be7Ssthen 
318eaf2578eSsthen 	log_ident_set_or_default(cfg->log_identity);
319933707f3Ssthen }
320933707f3Ssthen 
321933707f3Ssthen #ifdef HAVE_KILL
322933707f3Ssthen /** Read existing pid from pidfile.
323933707f3Ssthen  * @param file: file name of pid file.
324933707f3Ssthen  * @return: the pid from the file or -1 if none.
325933707f3Ssthen  */
326933707f3Ssthen static pid_t
327933707f3Ssthen readpid (const char* file)
328933707f3Ssthen {
329933707f3Ssthen 	int fd;
330933707f3Ssthen 	pid_t pid;
331933707f3Ssthen 	char pidbuf[32];
332933707f3Ssthen 	char* t;
333933707f3Ssthen 	ssize_t l;
334933707f3Ssthen 
335933707f3Ssthen 	if ((fd = open(file, O_RDONLY)) == -1) {
336933707f3Ssthen 		if(errno != ENOENT)
337933707f3Ssthen 			log_err("Could not read pidfile %s: %s",
338933707f3Ssthen 				file, strerror(errno));
339933707f3Ssthen 		return -1;
340933707f3Ssthen 	}
341933707f3Ssthen 
342933707f3Ssthen 	if (((l = read(fd, pidbuf, sizeof(pidbuf)))) == -1) {
343933707f3Ssthen 		if(errno != ENOENT)
344933707f3Ssthen 			log_err("Could not read pidfile %s: %s",
345933707f3Ssthen 				file, strerror(errno));
346933707f3Ssthen 		close(fd);
347933707f3Ssthen 		return -1;
348933707f3Ssthen 	}
349933707f3Ssthen 
350933707f3Ssthen 	close(fd);
351933707f3Ssthen 
352933707f3Ssthen 	/* Empty pidfile means no pidfile... */
353933707f3Ssthen 	if (l == 0) {
354933707f3Ssthen 		return -1;
355933707f3Ssthen 	}
356933707f3Ssthen 
357933707f3Ssthen 	pidbuf[sizeof(pidbuf)-1] = 0;
358933707f3Ssthen 	pid = (pid_t)strtol(pidbuf, &t, 10);
359933707f3Ssthen 
360933707f3Ssthen 	if (*t && *t != '\n') {
361933707f3Ssthen 		return -1;
362933707f3Ssthen 	}
363933707f3Ssthen 	return pid;
364933707f3Ssthen }
365933707f3Ssthen 
366933707f3Ssthen /** write pid to file.
367933707f3Ssthen  * @param pidfile: file name of pid file.
368933707f3Ssthen  * @param pid: pid to write to file.
369933707f3Ssthen  */
3702bdc0ed1Ssthen static void
371933707f3Ssthen writepid (const char* pidfile, pid_t pid)
372933707f3Ssthen {
373eba819a2Ssthen 	int fd;
374eba819a2Ssthen 	char pidbuf[32];
375eba819a2Ssthen 	size_t count = 0;
376eba819a2Ssthen 	snprintf(pidbuf, sizeof(pidbuf), "%lu\n", (unsigned long)pid);
377933707f3Ssthen 
378eba819a2Ssthen 	if((fd = open(pidfile, O_WRONLY | O_CREAT | O_TRUNC
379eba819a2Ssthen #ifdef O_NOFOLLOW
380eba819a2Ssthen 		| O_NOFOLLOW
381eba819a2Ssthen #endif
382eba819a2Ssthen 		, 0644)) == -1) {
383933707f3Ssthen 		log_err("cannot open pidfile %s: %s",
384933707f3Ssthen 			pidfile, strerror(errno));
3852bdc0ed1Ssthen 		return;
386933707f3Ssthen 	}
387eba819a2Ssthen 	while(count < strlen(pidbuf)) {
388eba819a2Ssthen 		ssize_t r = write(fd, pidbuf+count, strlen(pidbuf)-count);
389eba819a2Ssthen 		if(r == -1) {
390eba819a2Ssthen 			if(errno == EAGAIN || errno == EINTR)
391eba819a2Ssthen 				continue;
392933707f3Ssthen 			log_err("cannot write to pidfile %s: %s",
393933707f3Ssthen 				pidfile, strerror(errno));
394eba819a2Ssthen 			close(fd);
3952bdc0ed1Ssthen 			return;
396eba819a2Ssthen 		} else if(r == 0) {
397eba819a2Ssthen 			log_err("cannot write any bytes to pidfile %s: "
398eba819a2Ssthen 				"write returns 0 bytes written", pidfile);
399eba819a2Ssthen 			close(fd);
4002bdc0ed1Ssthen 			return;
401933707f3Ssthen 		}
402eba819a2Ssthen 		count += r;
403eba819a2Ssthen 	}
404eba819a2Ssthen 	close(fd);
405933707f3Ssthen }
406933707f3Ssthen 
407933707f3Ssthen /**
408933707f3Ssthen  * check old pid file.
409933707f3Ssthen  * @param pidfile: the file name of the pid file.
410933707f3Ssthen  * @param inchroot: if pidfile is inchroot and we can thus expect to
411933707f3Ssthen  *	be able to delete it.
412933707f3Ssthen  */
413933707f3Ssthen static void
414933707f3Ssthen checkoldpid(char* pidfile, int inchroot)
415933707f3Ssthen {
416933707f3Ssthen 	pid_t old;
417933707f3Ssthen 	if((old = readpid(pidfile)) != -1) {
418933707f3Ssthen 		/* see if it is still alive */
419933707f3Ssthen 		if(kill(old, 0) == 0 || errno == EPERM)
420933707f3Ssthen 			log_warn("unbound is already running as pid %u.",
421933707f3Ssthen 				(unsigned)old);
422933707f3Ssthen 		else	if(inchroot)
423933707f3Ssthen 			log_warn("did not exit gracefully last time (%u)",
424933707f3Ssthen 				(unsigned)old);
425933707f3Ssthen 	}
426933707f3Ssthen }
427933707f3Ssthen #endif /* HAVE_KILL */
428933707f3Ssthen 
429933707f3Ssthen /** detach from command line */
430933707f3Ssthen static void
431933707f3Ssthen detach(void)
432933707f3Ssthen {
433933707f3Ssthen #if defined(HAVE_DAEMON) && !defined(DEPRECATED_DAEMON)
434933707f3Ssthen 	/* use POSIX daemon(3) function */
435933707f3Ssthen 	if(daemon(1, 0) != 0)
436933707f3Ssthen 		fatal_exit("daemon failed: %s", strerror(errno));
437933707f3Ssthen #else /* no HAVE_DAEMON */
438933707f3Ssthen #ifdef HAVE_FORK
439933707f3Ssthen 	int fd;
440933707f3Ssthen 	/* Take off... */
441933707f3Ssthen 	switch (fork()) {
442933707f3Ssthen 		case 0:
443933707f3Ssthen 			break;
444933707f3Ssthen 		case -1:
445933707f3Ssthen 			fatal_exit("fork failed: %s", strerror(errno));
446933707f3Ssthen 		default:
447933707f3Ssthen 			/* exit interactive session */
448933707f3Ssthen 			exit(0);
449933707f3Ssthen 	}
450933707f3Ssthen 	/* detach */
451933707f3Ssthen #ifdef HAVE_SETSID
452933707f3Ssthen 	if(setsid() == -1)
453933707f3Ssthen 		fatal_exit("setsid() failed: %s", strerror(errno));
454933707f3Ssthen #endif
455933707f3Ssthen 	if ((fd = open("/dev/null", O_RDWR, 0)) != -1) {
456933707f3Ssthen 		(void)dup2(fd, STDIN_FILENO);
457933707f3Ssthen 		(void)dup2(fd, STDOUT_FILENO);
458933707f3Ssthen 		(void)dup2(fd, STDERR_FILENO);
459933707f3Ssthen 		if (fd > 2)
460933707f3Ssthen 			(void)close(fd);
461933707f3Ssthen 	}
462933707f3Ssthen #endif /* HAVE_FORK */
463933707f3Ssthen #endif /* HAVE_DAEMON */
464933707f3Ssthen }
465933707f3Ssthen 
466bdfc4d55Sflorian /** daemonize, drop user privileges and chroot if needed */
467933707f3Ssthen static void
468933707f3Ssthen perform_setup(struct daemon* daemon, struct config_file* cfg, int debug_mode,
4697191de28Ssthen 	const char** cfgfile, int need_pidfile)
470933707f3Ssthen {
47132e31f52Ssthen #ifdef HAVE_KILL
47232e31f52Ssthen 	int pidinchroot;
47332e31f52Ssthen #endif
474933707f3Ssthen #ifdef HAVE_GETPWNAM
475933707f3Ssthen 	struct passwd *pwd = NULL;
476*98bc733bSsthen #endif
477933707f3Ssthen 
478*98bc733bSsthen 	if(!daemon_privileged(daemon))
479*98bc733bSsthen 		fatal_exit("could not do privileged setup");
480*98bc733bSsthen #ifdef HAVE_GETPWNAM
481933707f3Ssthen 	if(cfg->username && cfg->username[0]) {
482933707f3Ssthen 		if((pwd = getpwnam(cfg->username)) == NULL)
483933707f3Ssthen 			fatal_exit("user '%s' does not exist.", cfg->username);
484933707f3Ssthen 		/* endpwent below, in case we need pwd for setusercontext */
485933707f3Ssthen 	}
486933707f3Ssthen #endif
48724893edcSsthen #ifdef UB_ON_WINDOWS
48824893edcSsthen 	w_config_adjust_directory(cfg);
48924893edcSsthen #endif
490933707f3Ssthen 
491938a3a5eSflorian 	/* read ssl keys while superuser and outside chroot */
492938a3a5eSflorian #ifdef HAVE_SSL
493938a3a5eSflorian 	if(!(daemon->rc = daemon_remote_create(cfg)))
494938a3a5eSflorian 		fatal_exit("could not set up remote-control");
495938a3a5eSflorian 	if(cfg->ssl_service_key && cfg->ssl_service_key[0]) {
496938a3a5eSflorian 		if(!(daemon->listen_sslctx = listen_sslctx_create(
497938a3a5eSflorian 			cfg->ssl_service_key, cfg->ssl_service_pem, NULL)))
498938a3a5eSflorian 			fatal_exit("could not set up listen SSL_CTX");
499f6b99bafSsthen 		if(cfg->tls_ciphers && cfg->tls_ciphers[0]) {
500f6b99bafSsthen 			if (!SSL_CTX_set_cipher_list(daemon->listen_sslctx, cfg->tls_ciphers)) {
501f6b99bafSsthen 				fatal_exit("failed to set tls-cipher %s", cfg->tls_ciphers);
502f6b99bafSsthen 			}
503f6b99bafSsthen 		}
504f6b99bafSsthen #ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
505f6b99bafSsthen 		if(cfg->tls_ciphersuites && cfg->tls_ciphersuites[0]) {
506f6b99bafSsthen 			if (!SSL_CTX_set_ciphersuites(daemon->listen_sslctx, cfg->tls_ciphersuites)) {
507f6b99bafSsthen 				fatal_exit("failed to set tls-ciphersuites %s", cfg->tls_ciphersuites);
508f6b99bafSsthen 			}
509f6b99bafSsthen 		}
510f6b99bafSsthen #endif
511550cf4a9Ssthen 		if(cfg->tls_session_ticket_keys.first &&
512550cf4a9Ssthen 			cfg->tls_session_ticket_keys.first->str[0] != 0) {
513f6b99bafSsthen 			if(!listen_sslctx_setup_ticket_keys(daemon->listen_sslctx, cfg->tls_session_ticket_keys.first)) {
514f6b99bafSsthen 				fatal_exit("could not set session ticket SSL_CTX");
515f6b99bafSsthen 			}
516f6b99bafSsthen 		}
517938a3a5eSflorian 	}
518938a3a5eSflorian 	if(!(daemon->connect_sslctx = connect_sslctx_create(NULL, NULL,
51920237c55Ssthen 		cfg->tls_cert_bundle, cfg->tls_win_cert)))
520938a3a5eSflorian 		fatal_exit("could not set up connect SSL_CTX");
521938a3a5eSflorian #endif
522938a3a5eSflorian 
523933707f3Ssthen 	/* init syslog (as root) if needed, before daemonize, otherwise
524933707f3Ssthen 	 * a fork error could not be printed since daemonize closed stderr.*/
525933707f3Ssthen 	if(cfg->use_syslog) {
526933707f3Ssthen 		log_init(cfg->logfile, cfg->use_syslog, cfg->chrootdir);
527933707f3Ssthen 	}
528933707f3Ssthen 	/* if using a logfile, we cannot open it because the logfile would
529933707f3Ssthen 	 * be created with the wrong permissions, we cannot chown it because
530933707f3Ssthen 	 * we cannot chown system logfiles, so we do not open at all.
531933707f3Ssthen 	 * So, using a logfile, the user does not see errors unless -d is
532933707f3Ssthen 	 * given to unbound on the commandline. */
533933707f3Ssthen 
534933707f3Ssthen #ifdef HAVE_KILL
53532e31f52Ssthen 	/* true if pidfile is inside chrootdir, or nochroot */
5367191de28Ssthen 	pidinchroot = need_pidfile && (!(cfg->chrootdir && cfg->chrootdir[0]) ||
53732e31f52Ssthen 				(cfg->chrootdir && cfg->chrootdir[0] &&
53832e31f52Ssthen 				strncmp(cfg->pidfile, cfg->chrootdir,
5397191de28Ssthen 				strlen(cfg->chrootdir))==0));
54032e31f52Ssthen 
541933707f3Ssthen 	/* check old pid file before forking */
5427191de28Ssthen 	if(cfg->pidfile && cfg->pidfile[0] && need_pidfile) {
543933707f3Ssthen 		/* calculate position of pidfile */
544933707f3Ssthen 		if(cfg->pidfile[0] == '/')
545933707f3Ssthen 			daemon->pidfile = strdup(cfg->pidfile);
546933707f3Ssthen 		else	daemon->pidfile = fname_after_chroot(cfg->pidfile,
547933707f3Ssthen 				cfg, 1);
548933707f3Ssthen 		if(!daemon->pidfile)
549933707f3Ssthen 			fatal_exit("pidfile alloc: out of memory");
5502bdc0ed1Ssthen 		/* Check old pid if there is no username configured.
5512bdc0ed1Ssthen 		 * With a username, the assumption is that the privilege
5522bdc0ed1Ssthen 		 * drop makes a pidfile not removed when the server stopped
5532bdc0ed1Ssthen 		 * last time. The server does not chown the pidfile for it,
5542bdc0ed1Ssthen 		 * because that creates privilege escape problems, with the
5552bdc0ed1Ssthen 		 * pidfile writable by unprivileged users, but used by
5562bdc0ed1Ssthen 		 * privileged users. */
557*98bc733bSsthen 		if(!(cfg->username && cfg->username[0]))
55832e31f52Ssthen 			checkoldpid(daemon->pidfile, pidinchroot);
559933707f3Ssthen 	}
560933707f3Ssthen #endif
561933707f3Ssthen 
562933707f3Ssthen 	/* daemonize because pid is needed by the writepid func */
563933707f3Ssthen 	if(!debug_mode && cfg->do_daemonize) {
564933707f3Ssthen 		detach();
565933707f3Ssthen 	}
566933707f3Ssthen 
567933707f3Ssthen 	/* write new pidfile (while still root, so can be outside chroot) */
568933707f3Ssthen #ifdef HAVE_KILL
5697191de28Ssthen 	if(cfg->pidfile && cfg->pidfile[0] && need_pidfile) {
5702bdc0ed1Ssthen 		writepid(daemon->pidfile, getpid());
571eba819a2Ssthen 	}
572933707f3Ssthen #else
573933707f3Ssthen 	(void)daemon;
5747191de28Ssthen 	(void)need_pidfile;
57531f127bbSsthen #endif /* HAVE_KILL */
576933707f3Ssthen 
5771ace9f49Ssthen 	/* Set user context */
5781ace9f49Ssthen #ifdef HAVE_GETPWNAM
579a58bff56Ssthen 	if(cfg->username && cfg->username[0] && cfg_uid != (uid_t)-1) {
5801ace9f49Ssthen #ifdef HAVE_SETUSERCONTEXT
5811ace9f49Ssthen 		/* setusercontext does initgroups, setuid, setgid, and
5821ace9f49Ssthen 		 * also resource limits from login config, but we
5831ace9f49Ssthen 		 * still call setresuid, setresgid to be sure to set all uid*/
58447dfde74Sflorian 		if(setusercontext(NULL, pwd, cfg_uid, (unsigned)
5851ace9f49Ssthen 			LOGIN_SETALL & ~LOGIN_SETUSER & ~LOGIN_SETGROUP) != 0)
5861ace9f49Ssthen 			log_warn("unable to setusercontext %s: %s",
5871ace9f49Ssthen 				cfg->username, strerror(errno));
588a3167c07Ssthen #else
589a3167c07Ssthen 		(void)pwd;
5901ace9f49Ssthen #endif /* HAVE_SETUSERCONTEXT */
5911ace9f49Ssthen 	}
5921ace9f49Ssthen #endif /* HAVE_GETPWNAM */
5931ace9f49Ssthen 
594933707f3Ssthen 	/* box into the chroot */
595933707f3Ssthen #ifdef HAVE_CHROOT
596933707f3Ssthen 	if(cfg->chrootdir && cfg->chrootdir[0]) {
597933707f3Ssthen 		if(chdir(cfg->chrootdir)) {
598933707f3Ssthen 			fatal_exit("unable to chdir to chroot %s: %s",
599933707f3Ssthen 				cfg->chrootdir, strerror(errno));
600933707f3Ssthen 		}
601933707f3Ssthen 		verbose(VERB_QUERY, "chdir to %s", cfg->chrootdir);
602933707f3Ssthen 		if(chroot(cfg->chrootdir))
603933707f3Ssthen 			fatal_exit("unable to chroot to %s: %s",
604933707f3Ssthen 				cfg->chrootdir, strerror(errno));
605e9c7b4efSsthen 		if(chdir("/"))
606e9c7b4efSsthen 			fatal_exit("unable to chdir to / in chroot %s: %s",
607e9c7b4efSsthen 				cfg->chrootdir, strerror(errno));
608933707f3Ssthen 		verbose(VERB_QUERY, "chroot to %s", cfg->chrootdir);
609933707f3Ssthen 		if(strncmp(*cfgfile, cfg->chrootdir,
610933707f3Ssthen 			strlen(cfg->chrootdir)) == 0)
611933707f3Ssthen 			(*cfgfile) += strlen(cfg->chrootdir);
612933707f3Ssthen 
613933707f3Ssthen 		/* adjust stored pidfile for chroot */
614933707f3Ssthen 		if(daemon->pidfile && daemon->pidfile[0] &&
615933707f3Ssthen 			strncmp(daemon->pidfile, cfg->chrootdir,
616933707f3Ssthen 			strlen(cfg->chrootdir))==0) {
617933707f3Ssthen 			char* old = daemon->pidfile;
618933707f3Ssthen 			daemon->pidfile = strdup(old+strlen(cfg->chrootdir));
619933707f3Ssthen 			free(old);
620933707f3Ssthen 			if(!daemon->pidfile)
621933707f3Ssthen 				log_err("out of memory in pidfile adjust");
622933707f3Ssthen 		}
623933707f3Ssthen 		daemon->chroot = strdup(cfg->chrootdir);
624933707f3Ssthen 		if(!daemon->chroot)
625933707f3Ssthen 			log_err("out of memory in daemon chroot dir storage");
626933707f3Ssthen 	}
627933707f3Ssthen #else
628933707f3Ssthen 	(void)cfgfile;
629933707f3Ssthen #endif
630933707f3Ssthen 	/* change to working directory inside chroot */
631933707f3Ssthen 	if(cfg->directory && cfg->directory[0]) {
632933707f3Ssthen 		char* dir = cfg->directory;
633933707f3Ssthen 		if(cfg->chrootdir && cfg->chrootdir[0] &&
634933707f3Ssthen 			strncmp(dir, cfg->chrootdir,
635933707f3Ssthen 			strlen(cfg->chrootdir)) == 0)
636933707f3Ssthen 			dir += strlen(cfg->chrootdir);
637933707f3Ssthen 		if(dir[0]) {
638933707f3Ssthen 			if(chdir(dir)) {
639933707f3Ssthen 				fatal_exit("Could not chdir to %s: %s",
640933707f3Ssthen 					dir, strerror(errno));
641933707f3Ssthen 			}
642933707f3Ssthen 			verbose(VERB_QUERY, "chdir to %s", dir);
643933707f3Ssthen 		}
644933707f3Ssthen 	}
645933707f3Ssthen 
646933707f3Ssthen 	/* drop permissions after chroot, getpwnam, pidfile, syslog done*/
647933707f3Ssthen #ifdef HAVE_GETPWNAM
648a58bff56Ssthen 	if(cfg->username && cfg->username[0] && cfg_uid != (uid_t)-1) {
649933707f3Ssthen #  ifdef HAVE_INITGROUPS
65047dfde74Sflorian 		if(initgroups(cfg->username, cfg_gid) != 0)
651933707f3Ssthen 			log_warn("unable to initgroups %s: %s",
652933707f3Ssthen 				cfg->username, strerror(errno));
653933707f3Ssthen #  endif /* HAVE_INITGROUPS */
65477079be7Ssthen #  ifdef HAVE_ENDPWENT
655933707f3Ssthen 		endpwent();
65677079be7Ssthen #  endif
657933707f3Ssthen 
658933707f3Ssthen #ifdef HAVE_SETRESGID
65947dfde74Sflorian 		if(setresgid(cfg_gid,cfg_gid,cfg_gid) != 0)
660933707f3Ssthen #elif defined(HAVE_SETREGID) && !defined(DARWIN_BROKEN_SETREUID)
66147dfde74Sflorian 		if(setregid(cfg_gid,cfg_gid) != 0)
662933707f3Ssthen #else /* use setgid */
66347dfde74Sflorian 		if(setgid(cfg_gid) != 0)
664933707f3Ssthen #endif /* HAVE_SETRESGID */
665933707f3Ssthen 			fatal_exit("unable to set group id of %s: %s",
666933707f3Ssthen 				cfg->username, strerror(errno));
667933707f3Ssthen #ifdef HAVE_SETRESUID
66847dfde74Sflorian 		if(setresuid(cfg_uid,cfg_uid,cfg_uid) != 0)
669933707f3Ssthen #elif defined(HAVE_SETREUID) && !defined(DARWIN_BROKEN_SETREUID)
67047dfde74Sflorian 		if(setreuid(cfg_uid,cfg_uid) != 0)
671933707f3Ssthen #else /* use setuid */
67247dfde74Sflorian 		if(setuid(cfg_uid) != 0)
673933707f3Ssthen #endif /* HAVE_SETRESUID */
674933707f3Ssthen 			fatal_exit("unable to set user id of %s: %s",
675933707f3Ssthen 				cfg->username, strerror(errno));
676933707f3Ssthen 		verbose(VERB_QUERY, "drop user privileges, run as %s",
677933707f3Ssthen 			cfg->username);
678933707f3Ssthen 	}
679933707f3Ssthen #endif /* HAVE_GETPWNAM */
680933707f3Ssthen 	/* file logging inited after chroot,chdir,setuid is done so that
681933707f3Ssthen 	 * it would succeed on SIGHUP as well */
682933707f3Ssthen 	if(!cfg->use_syslog)
683933707f3Ssthen 		log_init(cfg->logfile, cfg->use_syslog, cfg->chrootdir);
684933707f3Ssthen }
685933707f3Ssthen 
686933707f3Ssthen /**
687933707f3Ssthen  * Run the daemon.
688933707f3Ssthen  * @param cfgfile: the config file name.
689933707f3Ssthen  * @param cmdline_verbose: verbosity resulting from commandline -v.
690933707f3Ssthen  *    These increase verbosity as specified in the config file.
691933707f3Ssthen  * @param debug_mode: if set, do not daemonize.
6927191de28Ssthen  * @param need_pidfile: if false, no pidfile is checked or created.
693933707f3Ssthen  */
694933707f3Ssthen static void
695eaf2578eSsthen run_daemon(const char* cfgfile, int cmdline_verbose, int debug_mode, int need_pidfile)
696933707f3Ssthen {
697933707f3Ssthen 	struct config_file* cfg = NULL;
698933707f3Ssthen 	struct daemon* daemon = NULL;
699933707f3Ssthen 	int done_setup = 0;
700933707f3Ssthen 
701933707f3Ssthen 	if(!(daemon = daemon_init()))
702933707f3Ssthen 		fatal_exit("alloc failure");
703933707f3Ssthen 	while(!daemon->need_to_exit) {
704933707f3Ssthen 		if(done_setup)
705933707f3Ssthen 			verbose(VERB_OPS, "Restart of %s.", PACKAGE_STRING);
706933707f3Ssthen 		else	verbose(VERB_OPS, "Start of %s.", PACKAGE_STRING);
707933707f3Ssthen 
708933707f3Ssthen 		/* config stuff */
709933707f3Ssthen 		if(!(cfg = config_create()))
710933707f3Ssthen 			fatal_exit("Could not alloc config defaults");
711933707f3Ssthen 		if(!config_read(cfg, cfgfile, daemon->chroot)) {
712933707f3Ssthen 			if(errno != ENOENT)
7132308e98cSsthen 				fatal_exit("Could not read config file: %s."
7142308e98cSsthen 					" Maybe try unbound -dd, it stays on "
7152308e98cSsthen 					"the commandline to see more errors, "
7162308e98cSsthen 					"or unbound-checkconf", cfgfile);
717933707f3Ssthen 			log_warn("Continuing with default config settings");
718933707f3Ssthen 		}
719eaf2578eSsthen 		apply_settings(daemon, cfg, cmdline_verbose, debug_mode);
72047dfde74Sflorian 		if(!done_setup)
72131f127bbSsthen 			config_lookup_uid(cfg);
722933707f3Ssthen 
723933707f3Ssthen 		/* prepare */
724933707f3Ssthen 		if(!daemon_open_shared_ports(daemon))
725933707f3Ssthen 			fatal_exit("could not open ports");
726933707f3Ssthen 		if(!done_setup) {
7277191de28Ssthen 			perform_setup(daemon, cfg, debug_mode, &cfgfile, need_pidfile);
728933707f3Ssthen 			done_setup = 1;
729933707f3Ssthen 		} else {
730933707f3Ssthen 			/* reopen log after HUP to facilitate log rotation */
731933707f3Ssthen 			if(!cfg->use_syslog)
732933707f3Ssthen 				log_init(cfg->logfile, 0, cfg->chrootdir);
733933707f3Ssthen 		}
734933707f3Ssthen 		/* work */
735933707f3Ssthen 		daemon_fork(daemon);
736933707f3Ssthen 
737933707f3Ssthen 		/* clean up for restart */
738933707f3Ssthen 		verbose(VERB_ALGO, "cleanup.");
739933707f3Ssthen 		daemon_cleanup(daemon);
740933707f3Ssthen 		config_delete(cfg);
741933707f3Ssthen 	}
742933707f3Ssthen 	verbose(VERB_ALGO, "Exit cleanup.");
743933707f3Ssthen 	/* this unlink may not work if the pidfile is located outside
744933707f3Ssthen 	 * of the chroot/workdir or we no longer have permissions */
745933707f3Ssthen 	if(daemon->pidfile) {
746933707f3Ssthen 		int fd;
747933707f3Ssthen 		/* truncate pidfile */
7482bdc0ed1Ssthen 		fd = open(daemon->pidfile, O_WRONLY | O_TRUNC
7492bdc0ed1Ssthen #ifdef O_NOFOLLOW
7502bdc0ed1Ssthen 			| O_NOFOLLOW
7512bdc0ed1Ssthen #endif
7522bdc0ed1Ssthen 			, 0644);
753933707f3Ssthen 		if(fd != -1)
754933707f3Ssthen 			close(fd);
755933707f3Ssthen 		/* delete pidfile */
756933707f3Ssthen 		unlink(daemon->pidfile);
757933707f3Ssthen 	}
758933707f3Ssthen 	daemon_delete(daemon);
759933707f3Ssthen }
760933707f3Ssthen 
761933707f3Ssthen /** getopt global, in case header files fail to declare it. */
762933707f3Ssthen extern int optind;
763933707f3Ssthen /** getopt global, in case header files fail to declare it. */
764933707f3Ssthen extern char* optarg;
765933707f3Ssthen 
766933707f3Ssthen /**
767933707f3Ssthen  * main program. Set options given commandline arguments.
768933707f3Ssthen  * @param argc: number of commandline arguments.
769933707f3Ssthen  * @param argv: array of commandline arguments.
770933707f3Ssthen  * @return: exit status of the program.
771933707f3Ssthen  */
772933707f3Ssthen int
773933707f3Ssthen main(int argc, char* argv[])
774933707f3Ssthen {
775933707f3Ssthen 	int c;
776933707f3Ssthen 	const char* cfgfile = CONFIGFILE;
777933707f3Ssthen 	const char* winopt = NULL;
77877079be7Ssthen 	const char* log_ident_default;
779933707f3Ssthen 	int cmdline_verbose = 0;
780933707f3Ssthen 	int debug_mode = 0;
7817191de28Ssthen 	int need_pidfile = 1;
7827191de28Ssthen 
783933707f3Ssthen #ifdef UB_ON_WINDOWS
784933707f3Ssthen 	int cmdline_cfg = 0;
785933707f3Ssthen #endif
786933707f3Ssthen 
787e21c60efSsthen 	checklock_start();
788933707f3Ssthen 	log_init(NULL, 0, NULL);
78977079be7Ssthen 	log_ident_default = strrchr(argv[0],'/')?strrchr(argv[0],'/')+1:argv[0];
790eaf2578eSsthen 	log_ident_set_default(log_ident_default);
79177079be7Ssthen 	log_ident_set(log_ident_default);
792933707f3Ssthen 	/* parse the options */
7938240c1b9Ssthen 	while( (c=getopt(argc, argv, "c:dhpvw:V")) != -1) {
794933707f3Ssthen 		switch(c) {
795933707f3Ssthen 		case 'c':
796933707f3Ssthen 			cfgfile = optarg;
797933707f3Ssthen #ifdef UB_ON_WINDOWS
798933707f3Ssthen 			cmdline_cfg = 1;
799933707f3Ssthen #endif
800933707f3Ssthen 			break;
801933707f3Ssthen 		case 'v':
802933707f3Ssthen 			cmdline_verbose++;
803933707f3Ssthen 			verbosity++;
804933707f3Ssthen 			break;
8057191de28Ssthen 		case 'p':
8067191de28Ssthen 			need_pidfile = 0;
8077191de28Ssthen 			break;
808933707f3Ssthen 		case 'd':
809e10d3884Sbrad 			debug_mode++;
810933707f3Ssthen 			break;
811933707f3Ssthen 		case 'w':
812933707f3Ssthen 			winopt = optarg;
813933707f3Ssthen 			break;
8148240c1b9Ssthen 		case 'V':
8158240c1b9Ssthen 			print_build_options();
8168240c1b9Ssthen 			return 0;
817933707f3Ssthen 		case '?':
818933707f3Ssthen 		case 'h':
819933707f3Ssthen 		default:
820933707f3Ssthen 			usage();
821933707f3Ssthen 			return 1;
822933707f3Ssthen 		}
823933707f3Ssthen 	}
824933707f3Ssthen 	argc -= optind;
825452a1548Ssthen 	/* argv += optind; not using further arguments */
826933707f3Ssthen 
827933707f3Ssthen 	if(winopt) {
828933707f3Ssthen #ifdef UB_ON_WINDOWS
829933707f3Ssthen 		wsvc_command_option(winopt, cfgfile, cmdline_verbose,
830933707f3Ssthen 			cmdline_cfg);
831933707f3Ssthen #else
832933707f3Ssthen 		fatal_exit("option not supported");
833933707f3Ssthen #endif
834933707f3Ssthen 	}
835933707f3Ssthen 
836933707f3Ssthen 	if(argc != 0) {
837933707f3Ssthen 		usage();
838933707f3Ssthen 		return 1;
839933707f3Ssthen 	}
840933707f3Ssthen 
841eaf2578eSsthen 	run_daemon(cfgfile, cmdline_verbose, debug_mode, need_pidfile);
842933707f3Ssthen 	log_init(NULL, 0, NULL); /* close logfile */
843938a3a5eSflorian #ifndef unbound_testbound
844938a3a5eSflorian 	if(log_get_lock()) {
845ebf5bb73Ssthen 		lock_basic_destroy((lock_basic_type*)log_get_lock());
846938a3a5eSflorian 	}
847938a3a5eSflorian #endif
848933707f3Ssthen 	return 0;
849933707f3Ssthen }
850