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