server.c 23.3 KB
Newer Older
1
/*  Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
2 3 4 5 6 7 8 9 10 11 12 13

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
14
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
15 16
 */

17
#define __APPLE_USE_RFC_3542
18

19
#include <stdlib.h>
20
#include <assert.h>
21
#include <urcu.h>
22
#include <netinet/tcp.h>
23

24
#include "libknot/errcode.h"
25
#include "libknot/yparser/ypschema.h"
26
#include "knot/common/log.h"
27
#include "knot/common/stats.h"
28
#include "knot/conf/confio.h"
29
#include "knot/conf/migration.h"
30
#include "knot/conf/module.h"
31
#include "knot/dnssec/kasp/kasp_db.h"
32
#include "knot/journal/journal_basic.h"
Lubos Slovak's avatar
Lubos Slovak committed
33 34 35
#include "knot/server/server.h"
#include "knot/server/udp-handler.h"
#include "knot/server/tcp-handler.h"
36
#include "knot/zone/timers.h"
37
#include "knot/zone/zonedb-load.h"
38
#include "knot/worker/pool.h"
39 40
#include "contrib/net.h"
#include "contrib/sockaddr.h"
41
#include "contrib/trim.h"
42

43 44 45 46 47 48 49 50
/*! \brief Minimal send/receive buffer sizes. */
enum {
	UDP_MIN_RCVSIZE = 4096,
	UDP_MIN_SNDSIZE = 4096,
	TCP_MIN_RCVSIZE = 4096,
	TCP_MIN_SNDSIZE = sizeof(uint16_t) + UINT16_MAX
};

51 52
/*! \brief Unbind interface and clear the structure. */
static void server_deinit_iface(iface_t *iface)
53 54
{
	/* Free UDP handler. */
55 56 57 58 59
	for (int i = 0; i < iface->fd_udp_count; i++) {
		if (iface->fd_udp[i] > -1) {
			close(iface->fd_udp[i]);
		}
	}
Jan Včelák's avatar
Jan Včelák committed
60
	free(iface->fd_udp);
61 62

	/* Free TCP handler. */
63 64
	if (iface->fd_tcp > -1) {
		close(iface->fd_tcp);
65 66
	}

67 68 69 70 71 72 73 74 75 76 77
	memset(iface, 0, sizeof(*iface));
}

/*! \brief Unbind and dispose given interface. */
static void server_remove_iface(iface_t *iface)
{
	if (!iface) {
		return;
	}

	server_deinit_iface(iface);
78 79 80
	free(iface);
}

81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
/*! \brief Set lower bound for socket option. */
static bool setsockopt_min(int sock, int option, int min)
{
	int value = 0;
	socklen_t len = sizeof(value);

	if (getsockopt(sock, SOL_SOCKET, option, &value, &len) != 0) {
		return false;
	}

	assert(len == sizeof(value));
	if (value >= min) {
		return true;
	}

	return setsockopt(sock, SOL_SOCKET, option, &min, sizeof(min)) == 0;
}

/*!
 * \brief Enlarge send/receive buffers.
 */
102
static bool enlarge_net_buffers(int sock, int min_recvsize, int min_sndsize)
103 104 105 106 107
{
	return setsockopt_min(sock, SO_RCVBUF, min_recvsize) &&
	       setsockopt_min(sock, SO_SNDBUF, min_sndsize);
}

108 109 110 111 112 113 114 115 116 117 118
/*!
 * \brief Enable source packet information retrieval.
 */
static bool enable_pktinfo(int sock, int family)
{
	int level = 0;
	int option = 0;

	switch (family) {
	case AF_INET:
		level = IPPROTO_IP;
119
#if defined(IP_PKTINFO)
120
		option = IP_PKTINFO; /* Linux */
121
#elif defined(IP_RECVDSTADDR)
122
		option = IP_RECVDSTADDR; /* BSD */
123 124
#else
		return false;
125
#endif
126 127 128 129 130 131 132 133 134 135 136 137
		break;
	case AF_INET6:
		level = IPPROTO_IPV6;
		option = IPV6_RECVPKTINFO;
		break;
	default:
		return false;
	}

	const int on = 1;
	return setsockopt(sock, level, option, &on, sizeof(on)) == 0;
}
138

139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
/*!
 * Linux 3.15 has IP_PMTUDISC_OMIT which makes sockets
 * ignore PMTU information and send packets with DF=0.
 * Fragmentation is allowed if and only if the packet
 * size exceeds the outgoing interface MTU or the packet
 * encounters smaller MTU link in network.
 * This mitigates DNS fragmentation attacks by preventing
 * forged PMTU information.
 * FreeBSD already has same semantics without setting
 * the option.
 */
static int disable_pmtudisc(int sock, int family)
{
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_OMIT)
	if (family == AF_INET) {
		int action_omit = IP_PMTUDISC_OMIT;
		if (setsockopt(sock, IPPROTO_IP, IP_MTU_DISCOVER, &action_omit,
		    sizeof(action_omit)) != 0) {
			return knot_map_errno();
		}
	}
#endif
	return KNOT_EOK;
}

164 165 166 167 168
/**
 * \brief Enable TCP Fast Open.
 */
static int enable_fastopen(int sock, int backlog)
{
169
#if defined(TCP_FASTOPEN)
170 171 172 173 174 175 176 177
#if __APPLE__
	if (backlog > 0) {
		backlog = 1; // just on-off switch on macOS
	}
#endif
	if (setsockopt(sock, IPPROTO_TCP, TCP_FASTOPEN, &backlog, sizeof(backlog)) != 0) {
		return knot_map_errno();
	}
178
#endif
179 180 181
	return KNOT_EOK;
}

182 183 184 185 186 187
/*!
 * \brief Initialize new interface from config value.
 *
 * Both TCP and UDP sockets will be created for the interface.
 *
 * \param new_if Allocated memory for the interface.
Lubos Slovak's avatar
Lubos Slovak committed
188 189
 * \param cfg_if Interface template from config.
 *
190 191 192
 * \retval 0 if successful (EOK).
 * \retval <0 on errors (EACCES, EINVAL, ENOMEM, EADDRINUSE).
 */
193
static int server_init_iface(iface_t *new_if, struct sockaddr_storage *addr, int udp_thread_count)
194 195
{
	/* Initialize interface. */
Marek Vavrusa's avatar
Marek Vavrusa committed
196
	int ret = 0;
197
	memset(new_if, 0, sizeof(iface_t));
Daniel Salzman's avatar
Daniel Salzman committed
198
	memcpy(&new_if->addr, addr, sizeof(struct sockaddr_storage));
199

200
	/* Convert to string address format. */
Daniel Salzman's avatar
Daniel Salzman committed
201
	char addr_str[SOCKADDR_STRLEN] = { 0 };
202
	sockaddr_tostr(addr_str, sizeof(addr_str), (struct sockaddr *)addr);
Jan Včelák's avatar
Jan Včelák committed
203

204
	int udp_socket_count = 1;
205
	int udp_bind_flags = 0;
206

207
#ifdef ENABLE_REUSEPORT
208
	udp_socket_count = udp_thread_count;
209
	udp_bind_flags |= NET_BIND_MULTIPLE;
210 211 212 213 214 215 216
#endif

	new_if->fd_udp = malloc(udp_socket_count * sizeof(int));
	if (!new_if->fd_udp) {
		return KNOT_ENOMEM;
	}

217 218
	/* Initialize the sockets to ensure safe early deinitialization. */
	for (int i = 0; i < udp_socket_count; i++) {
219 220
		new_if->fd_udp[i] = -1;
	}
221 222
	new_if->fd_tcp = -1;

223 224 225 226 227
	bool warn_bind = false;
	bool warn_bufsize = false;

	/* Create bound UDP sockets. */
	for (int i = 0; i < udp_socket_count; i++ ) {
228
		int sock = net_bound_socket(SOCK_DGRAM, (struct sockaddr *)addr, udp_bind_flags);
229
		if (sock == KNOT_EADDRNOTAVAIL) {
230 231
			udp_bind_flags |= NET_BIND_NONLOCAL;
			sock = net_bound_socket(SOCK_DGRAM, (struct sockaddr *)addr, udp_bind_flags);
232
			if (sock >= 0 && !warn_bind) {
233
				log_warning("address %s UDP bound, but required nonlocal bind", addr_str);
234
				warn_bind = true;
235 236 237 238
			}
		}

		if (sock < 0) {
239
			log_error("cannot bind address %s (%s)", addr_str,
240 241
			          knot_strerror(sock));
			server_deinit_iface(new_if);
242 243 244
			return sock;
		}

245 246
		if (!enlarge_net_buffers(sock, UDP_MIN_RCVSIZE, UDP_MIN_SNDSIZE) &&
		    !warn_bufsize) {
247
			log_warning("failed to set network buffer sizes for UDP");
248
			warn_bufsize = true;
249 250
		}

251
		if (sockaddr_is_any((struct sockaddr *)addr) && !enable_pktinfo(sock, addr->ss_family)) {
252
			log_warning("failed to enable received packet information retrieval");
253 254
		}

255 256 257 258 259 260
		ret = disable_pmtudisc(sock, addr->ss_family);
		if (ret != KNOT_EOK) {
			log_warning("failed to disable Path MTU discovery for IPv4/UDP (%s)",
			            knot_strerror(ret));
		}

261 262
		new_if->fd_udp[new_if->fd_udp_count] = sock;
		new_if->fd_udp_count += 1;
263 264
	}

265
	/* Create bound TCP socket. */
266 267 268 269 270 271
	int tcp_bind_flags = 0;
	int sock = net_bound_socket(SOCK_STREAM, (struct sockaddr *)addr, tcp_bind_flags);
	if (sock == KNOT_EADDRNOTAVAIL) {
		tcp_bind_flags |= NET_BIND_NONLOCAL;
		sock = net_bound_socket(SOCK_STREAM, (struct sockaddr *)addr, tcp_bind_flags);
		if (sock >= 0) {
272
			log_warning("address %s TCP bound, but required nonlocal bind", addr_str);
273 274 275
		}
	}

276
	if (sock < 0) {
277
		log_error("cannot bind address %s (%s)", addr_str,
278 279
		          knot_strerror(sock));
		server_deinit_iface(new_if);
280
		return sock;
281 282
	}

283
	if (!enlarge_net_buffers(sock, TCP_MIN_RCVSIZE, TCP_MIN_SNDSIZE)) {
284 285 286
		log_warning("failed to set network buffer sizes for TCP");
	}

287
	new_if->fd_tcp = sock;
288 289

	/* Listen for incoming connections. */
290
	ret = listen(sock, TCP_BACKLOG_SIZE);
291
	if (ret < 0) {
292
		log_error("failed to listen on TCP interface %s", addr_str);
293
		server_deinit_iface(new_if);
294
		return KNOT_ERROR;
295
	}
296

297 298 299
	/* TCP Fast Open. */
	ret = enable_fastopen(sock, TCP_BACKLOG_SIZE);
	if (ret < 0) {
300
		log_warning("failed to enable TCP Fast Open on %s (%s)",
301 302 303
		            addr_str, knot_strerror(ret));
	}

Marek Vavrusa's avatar
Marek Vavrusa committed
304
	return KNOT_EOK;
305 306
}

307
static void remove_ifacelist(struct ref *p)
308 309
{
	ifacelist_t *ifaces = (ifacelist_t *)p;
Jan Včelák's avatar
Jan Včelák committed
310

311
	/* Remove deprecated interfaces. */
312
	char addr_str[SOCKADDR_STRLEN] = {0};
313 314
	iface_t *n = NULL, *m = NULL;
	WALK_LIST_DELSAFE(n, m, ifaces->u) {
315
		sockaddr_tostr(addr_str, sizeof(addr_str), (struct sockaddr *)&n->addr);
316
		log_info("removing interface %s", addr_str);
317 318 319 320 321
		server_remove_iface(n);
	}
	WALK_LIST_DELSAFE(n, m, ifaces->l) {
		free(n);
	}
Jan Včelák's avatar
Jan Včelák committed
322

323 324 325
	free(ifaces);
}

326 327 328 329 330 331
/*!
 * \brief Update bound sockets according to configuration.
 *
 * \param server Server instance.
 * \return number of added sockets.
 */
Daniel Salzman's avatar
Daniel Salzman committed
332
static int reconfigure_sockets(conf_t *conf, server_t *s)
333
{
334 335
	/* Prepare helper lists. */
	int bound = 0;
336
	ifacelist_t *oldlist = s->ifaces;
337 338 339 340 341
	ifacelist_t *newlist = malloc(sizeof(ifacelist_t));
	ref_init(&newlist->ref, &remove_ifacelist);
	ref_retain(&newlist->ref);
	init_list(&newlist->u);
	init_list(&newlist->l);
Jan Včelák's avatar
Jan Včelák committed
342

343 344
	/* Duplicate current list. */
	/*! \note Pointers to addr, handlers etc. will be shared. */
345 346
	if (s->ifaces) {
		list_dup(&s->ifaces->u, &s->ifaces->l, sizeof(iface_t));
347 348 349
	}

	/* Update bound interfaces. */
Daniel Salzman's avatar
Daniel Salzman committed
350 351 352 353 354
	conf_val_t listen_val = conf_get(conf, C_SRV, C_LISTEN);
	conf_val_t rundir_val = conf_get(conf, C_SRV, C_RUNDIR);
	char *rundir = conf_abs_path(&rundir_val, NULL);
	while (listen_val.code == KNOT_EOK) {
		iface_t *m = NULL;
355

356 357
		/* Find already matching interface. */
		int found_match = 0;
Daniel Salzman's avatar
Daniel Salzman committed
358
		struct sockaddr_storage addr = conf_addr(&listen_val, rundir);
359 360 361
		if (s->ifaces) {
			WALK_LIST(m, s->ifaces->u) {
				/* Matching port and address. */
362 363
				if (sockaddr_cmp((struct sockaddr *)&addr,
				                 (struct sockaddr *)&m->addr) == 0) {
364 365
					found_match = 1;
					break;
366 367
				}
			}
368 369
		}

370 371
		/* Found already bound interface. */
		if (found_match) {
372
			rem_node((node_t *)m);
373
		} else {
Daniel Salzman's avatar
Daniel Salzman committed
374
			char addr_str[SOCKADDR_STRLEN] = { 0 };
375
			sockaddr_tostr(addr_str, sizeof(addr_str), (struct sockaddr *)&addr);
376
			log_info("binding to interface %s", addr_str);
377

378 379
			/* Create new interface. */
			m = malloc(sizeof(iface_t));
380 381
			unsigned size = s->handlers[IO_UDP].handler.unit->size;
			if (server_init_iface(m, &addr, size) < 0) {
382 383 384
				free(m);
				m = 0;
			}
385
		}
386 387 388

		/* Move to new list. */
		if (m) {
389
			add_tail(&newlist->l, (node_t *)m);
390
			++bound;
391
		}
Daniel Salzman's avatar
Daniel Salzman committed
392 393

		conf_val_next(&listen_val);
394
	}
Daniel Salzman's avatar
Daniel Salzman committed
395
	free(rundir);
396

397 398
	/* Wait for readers that are reconfiguring right now. */
	/*! \note This subsystem will be reworked in #239 */
399 400 401
	for (unsigned proto = IO_UDP; proto <= IO_TCP; ++proto) {
		dt_unit_t *tu = s->handlers[proto].handler.unit;
		iohandler_t *ioh = &s->handlers[proto].handler;
402 403 404 405 406 407 408
		for (unsigned i = 0; i < tu->size; ++i) {
			while (ioh->thread_state[i] & ServerReload) {
				sleep(1);
			}
		}
	}

409
	/* Publish new list. */
410
	s->ifaces = newlist;
Jan Včelák's avatar
Jan Včelák committed
411

412
	/* Update TCP+UDP ifacelist (reload all threads). */
413
	unsigned thread_count = 0;
414 415
	for (unsigned proto = IO_UDP; proto <= IO_TCP; ++proto) {
		dt_unit_t *tu = s->handlers[proto].handler.unit;
416 417
		for (unsigned i = 0; i < tu->size; ++i) {
			ref_retain((ref_t *)newlist);
418 419
			s->handlers[proto].handler.thread_state[i] |= ServerReload;
			s->handlers[proto].handler.thread_id[i] = thread_count++;
420 421 422 423
			if (s->state & ServerRunning) {
				dt_activate(tu->threads[i]);
				dt_signalize(tu->threads[i], SIGALRM);
			}
424
		}
425
	}
426

427
	ref_release(&oldlist->ref);
428

429
	return bound;
430 431
}

432
int server_init(server_t *server, int bg_workers)
433
{
Marek Vavrusa's avatar
Marek Vavrusa committed
434
	if (server == NULL) {
435
		return KNOT_EINVAL;
Marek Vavrusa's avatar
Marek Vavrusa committed
436
	}
437

438
	/* Clear the structure. */
439
	memset(server, 0, sizeof(server_t));
440

441 442
	/* Initialize event scheduler. */
	if (evsched_init(&server->sched, server) != KNOT_EOK) {
443 444 445
		return KNOT_ENOMEM;
	}

446
	server->workers = worker_pool_create(bg_workers);
447
	if (server->workers == NULL) {
448
		evsched_deinit(&server->sched);
449 450
		return KNOT_ENOMEM;
	}
451

452
	char *journal_dir = conf_db(conf(), C_JOURNAL_DB);
453
	conf_val_t journal_size = conf_default_get(conf(), C_MAX_JOURNAL_DB_SIZE);
454
	conf_val_t journal_mode = conf_default_get(conf(), C_JOURNAL_DB_MODE);
455
	knot_lmdb_init(&server->journaldb, journal_dir, conf_int(&journal_size), journal_env_flags(conf_opt(&journal_mode)), NULL);
456 457
	free(journal_dir);

458
	char *kasp_dir = conf_db(conf(), C_KASP_DB);
459
	conf_val_t kasp_size = conf_default_get(conf(), C_MAX_KASP_DB_SIZE);
460
	knot_lmdb_init(&server->kaspdb, kasp_dir, conf_int(&kasp_size), 0, "keys_db");
461 462
	free(kasp_dir);

463 464 465 466
	char *timer_dir = conf_db(conf(), C_TIMER_DB);
	conf_val_t timer_size = conf_default_get(conf(), C_MAX_TIMER_DB_SIZE);
	knot_lmdb_init(&server->timerdb, timer_dir, conf_int(&timer_size), 0, NULL);
	free(timer_dir);
467

468 469 470 471 472 473 474
	return KNOT_EOK;
}

void server_deinit(server_t *server)
{
	if (server == NULL) {
		return;
Marek Vavrusa's avatar
Marek Vavrusa committed
475
	}
476

477 478 479 480 481 482 483 484 485 486
	/* Save zone timers. */
	if (server->zone_db != NULL) {
		log_info("updating persistent timer DB");
		int ret = zone_timers_write_all(&server->timerdb, server->zone_db);
		if (ret != KNOT_EOK) {
			log_warning("failed to update persistent timer DB (%s)",
				    knot_strerror(ret));
		}
	}

487 488 489 490 491 492 493
	/* Free remaining interfaces. */
	if (server->ifaces) {
		iface_t *n = NULL, *m = NULL;
		WALK_LIST_DELSAFE(n, m, server->ifaces->l) {
			server_remove_iface(n);
		}
		free(server->ifaces);
494 495
	}

496
	/* Free threads and event handlers. */
497
	worker_pool_destroy(server->workers);
498 499 500 501 502

	/* Free zone database. */
	knot_zonedb_deep_free(&server->zone_db);

	/* Free remaining events. */
503
	evsched_deinit(&server->sched);
504

505 506 507
	/* Close persistent timers DB. */
	knot_lmdb_deinit(&server->timerdb);

508
	/* Close kasp_db. */
509
	knot_lmdb_deinit(&server->kaspdb);
510

511
	/* Close journal database if open. */
512
	knot_lmdb_deinit(&server->journaldb);
513

514 515
	/* Clear the structure. */
	memset(server, 0, sizeof(server_t));
516 517
}

518 519
static int server_init_handler(server_t *server, int index, int thread_count,
                               runnable_t runnable, runnable_t destructor)
520
{
521
	/* Initialize */
522
	iohandler_t *h = &server->handlers[index].handler;
523
	memset(h, 0, sizeof(iohandler_t));
524 525 526 527 528 529
	h->server = server;
	h->unit = dt_create(thread_count, runnable, destructor, h);
	if (h->unit == NULL) {
		return KNOT_ENOMEM;
	}

530
	h->thread_state = calloc(thread_count, sizeof(unsigned));
531 532 533
	if (h->thread_state == NULL) {
		dt_delete(&h->unit);
		return KNOT_ENOMEM;
Marek Vavrusa's avatar
Marek Vavrusa committed
534 535
	}

536 537
	h->thread_id = calloc(thread_count, sizeof(unsigned));
	if (h->thread_id == NULL) {
Daniel Salzman's avatar
Daniel Salzman committed
538
		free(h->thread_state);
539 540 541 542
		dt_delete(&h->unit);
		return KNOT_ENOMEM;
	}

543
	return KNOT_EOK;
544 545
}

546
static void server_free_handler(iohandler_t *h)
547
{
548 549
	if (h == NULL || h->server == NULL) {
		return;
550 551 552 553
	}

	/* Wait for threads to finish */
	if (h->unit) {
554 555
		dt_stop(h->unit);
		dt_join(h->unit);
Marek Vavrusa's avatar
Marek Vavrusa committed
556 557
	}

558
	/* Destroy worker context. */
559
	dt_delete(&h->unit);
560
	free(h->thread_state);
561
	free(h->thread_id);
562
	memset(h, 0, sizeof(iohandler_t));
563 564
}

565
int server_start(server_t *server, bool async)
566
{
567
	if (server == NULL) {
Marek Vavrusa's avatar
Marek Vavrusa committed
568
		return KNOT_EINVAL;
569 570
	}

571
	/* Start workers. */
572
	worker_pool_start(server->workers);
Marek Vavrusa's avatar
Marek Vavrusa committed
573

574 575
	/* Wait for enqueued events if not asynchronous. */
	if (!async) {
576
		worker_pool_wait(server->workers);
577
	}
Jan Včelák's avatar
Jan Včelák committed
578

579
	/* Start evsched handler. */
580
	evsched_start(&server->sched);
Jan Včelák's avatar
Jan Včelák committed
581

582
	/* Start I/O handlers. */
583
	server->state |= ServerRunning;
584 585 586
	for (int proto = IO_UDP; proto <= IO_TCP; ++proto) {
		if (server->handlers[proto].size > 0) {
			int ret = dt_start(server->handlers[proto].handler.unit);
587 588 589
			if (ret != KNOT_EOK) {
				return ret;
			}
590
		}
Marek Vavrusa's avatar
Marek Vavrusa committed
591
	}
Jan Včelák's avatar
Jan Včelák committed
592

593
	return KNOT_EOK;
594
}
595

596
void server_wait(server_t *server)
597
{
598
	if (server == NULL) {
599 600
		return;
	}
Jan Včelák's avatar
Jan Včelák committed
601

602 603
	evsched_join(&server->sched);
	worker_pool_join(server->workers);
604

605 606 607
	for (int proto = IO_UDP; proto <= IO_TCP; ++proto) {
		if (server->handlers[proto].size > 0) {
			server_free_handler(&server->handlers[proto].handler);
608
		}
Marek Vavrusa's avatar
Marek Vavrusa committed
609
	}
610 611
}

612 613
static int reload_conf(conf_t *new_conf)
{
614
	yp_schema_purge_dynamic(new_conf->schema);
615 616 617 618 619 620 621

	/* Re-load common modules. */
	int ret = conf_mod_load_common(new_conf);
	if (ret != KNOT_EOK) {
		return ret;
	}

622 623 624 625 626 627
	/* Re-import zonefile if specified. */
	const char *filename = conf()->filename;
	if (filename != NULL) {
		log_info("reloading configuration file '%s'", filename);

		/* Import the configuration file. */
628
		ret = conf_import(new_conf, filename, true);
629 630 631 632 633 634 635
		if (ret != KNOT_EOK) {
			log_error("failed to load configuration file (%s)",
			          knot_strerror(ret));
			return ret;
		}
	} else {
		log_info("reloading configuration database");
636 637 638 639 640 641 642 643 644 645 646 647

		/* Re-load extra modules. */
		for (conf_iter_t iter = conf_iter(new_conf, C_MODULE);
		     iter.code == KNOT_EOK; conf_iter_next(new_conf, &iter)) {
			conf_val_t id = conf_iter_id(new_conf, &iter);
			conf_val_t file = conf_id_get(new_conf, C_MODULE, C_FILE, &id);
			ret = conf_mod_load_extra(new_conf, conf_str(&id), conf_str(&file), false);
			if (ret != KNOT_EOK) {
				conf_iter_finish(new_conf, &iter);
				return ret;
			}
		}
648 649
	}

650 651
	conf_mod_load_purge(new_conf, false);

652
	// Migrate from old schema.
653
	ret = conf_migrate(new_conf);
654 655 656 657
	if (ret != KNOT_EOK) {
		log_error("failed to migrate configuration (%s)", knot_strerror(ret));
	}

658 659 660 661 662 663 664
	/* Refresh hostname. */
	conf_refresh_hostname(new_conf);

	return KNOT_EOK;
}

int server_reload(server_t *server)
Marek Vavrusa's avatar
Marek Vavrusa committed
665
{
Daniel Salzman's avatar
Daniel Salzman committed
666
	if (server == NULL) {
Marek Vavrusa's avatar
Marek Vavrusa committed
667 668
		return KNOT_EINVAL;
	}
Jan Včelák's avatar
Jan Včelák committed
669

670
	/* Check for no edit mode. */
671 672
	if (conf()->io.txn != NULL) {
		log_warning("reload aborted due to active configuration transaction");
673
		return KNOT_TXN_EEXISTS;
674
	}
Daniel Salzman's avatar
Daniel Salzman committed
675

676 677 678 679 680 681 682
	conf_t *new_conf = NULL;
	int ret = conf_clone(&new_conf);
	if (ret != KNOT_EOK) {
		log_error("failed to initialize configuration (%s)",
		          knot_strerror(ret));
		return ret;
	}
Daniel Salzman's avatar
Daniel Salzman committed
683

684 685 686
	yp_flag_t flags = conf()->io.flags;
	bool full = !(flags & CONF_IO_FACTIVE);
	bool reuse_modules = !full && !(flags & CONF_IO_FRLD_MOD);
Daniel Salzman's avatar
Daniel Salzman committed
687

688 689
	/* Reload configuration and modules if full reload or a module change. */
	if (full || !reuse_modules) {
690
		ret = reload_conf(new_conf);
Daniel Salzman's avatar
Daniel Salzman committed
691
		if (ret != KNOT_EOK) {
692
			conf_free(new_conf);
693
			return ret;
Daniel Salzman's avatar
Daniel Salzman committed
694
		}
Jan Včelák's avatar
Jan Včelák committed
695

696
		conf_activate_modules(new_conf, NULL, new_conf->query_modules,
697 698
		                      &new_conf->query_plan);
	}
699

700
	conf_update_flag_t upd_flags = CONF_UPD_FNOFREE;
701
	if (!full) {
702 703
		upd_flags |= CONF_UPD_FCONFIO;
	}
704 705
	if (reuse_modules) {
		upd_flags |= CONF_UPD_FMODULES;
706 707
	}

708
	/* Update to the new config. */
709
	conf_t *old_conf = conf_update(new_conf, upd_flags);
710

711 712 713 714 715 716
	/* Reload each component if full reload or a specific one if required. */
	if (full || (flags & CONF_IO_FRLD_LOG)) {
		log_reconfigure(conf());
	}
	if (full || (flags & CONF_IO_FRLD_SRV)) {
		server_reconfigure(conf(), server);
717
		stats_reconfigure(conf(), server);
718 719 720 721
	}
	if (full || (flags & (CONF_IO_FRLD_ZONES | CONF_IO_FRLD_ZONE))) {
		server_update_zones(conf(), server);
	}
Daniel Salzman's avatar
Daniel Salzman committed
722

723 724 725
	/* Free old config needed for module unload in zone reload. */
	conf_free(old_conf);

726 727 728 729 730
	if (full) {
		log_info("configuration reloaded");
	} else {
		// Reset confio reload context.
		conf()->io.flags = YP_FNONE;
731
		if (conf()->io.zones != NULL) {
732
			trie_clear(conf()->io.zones);
733
		}
734
	}
Daniel Salzman's avatar
Daniel Salzman committed
735

736
	return KNOT_EOK;
Marek Vavrusa's avatar
Marek Vavrusa committed
737 738
}

739 740
void server_stop(server_t *server)
{
741
	log_info("stopping server");
Jan Včelák's avatar
Jan Včelák committed
742

Vitezslav Kriz's avatar
Vitezslav Kriz committed
743 744
	/* Stop scheduler. */
	evsched_stop(&server->sched);
745 746
	/* Interrupt background workers. */
	worker_pool_stop(server->workers);
747

748
	/* Clear 'running' flag. */
749
	server->state &= ~ServerRunning;
750 751
}

752
static int reset_handler(server_t *server, int index, unsigned size, runnable_t run)
753
{
754
	if (server->handlers[index].size != size) {
755
		/* Free old handlers */
756 757
		if (server->handlers[index].size > 0) {
			server_free_handler(&server->handlers[index].handler);
758
		}
Jan Včelák's avatar
Jan Včelák committed
759

760
		/* Initialize I/O handlers. */
761
		int ret = server_init_handler(server, index, size, run, NULL);
762 763 764 765
		if (ret != KNOT_EOK) {
			return ret;
		}

766
		/* Start if server is running. */
767
		if (server->state & ServerRunning) {
768 769 770
			ret = dt_start(server->handlers[index].handler.unit);
			if (ret != KNOT_EOK) {
				return ret;
771 772
			}
		}
773
		server->handlers[index].size = size;
774
	}
Jan Včelák's avatar
Jan Včelák committed
775

776 777 778 779 780 781 782 783 784 785 786 787
	return KNOT_EOK;
}

/*! \brief Reconfigure UDP and TCP query processing threads. */
static int reconfigure_threads(conf_t *conf, server_t *server)
{
	int ret = reset_handler(server, IO_UDP, conf_udp_threads(conf), udp_master);
	if (ret != KNOT_EOK) {
		return ret;
	}

	return reset_handler(server, IO_TCP, conf_tcp_threads(conf), tcp_master);
788 789
}

790
static int reconfigure_journal_db(conf_t *conf, server_t *server)
791
{
792
	char *journal_dir = conf_db(conf, C_JOURNAL_DB);
793
	conf_val_t journal_size = conf_default_get(conf, C_MAX_JOURNAL_DB_SIZE);
794
	conf_val_t journal_mode = conf_default_get(conf, C_JOURNAL_DB_MODE);
795 796 797 798
	int ret = knot_lmdb_reinit(&server->journaldb, journal_dir, conf_int(&journal_size),
	                           journal_env_flags(conf_opt(&journal_mode)));
	if (ret != KNOT_EOK) {
		log_warning("ignored reconfiguration of journal DB (%s)", knot_strerror(ret));
799
	}
800
	free(journal_dir);
Jan Včelák's avatar
Jan Včelák committed
801

802
	return KNOT_EOK; // not "ret"
803 804
}

805 806
static int reconfigure_kasp_db(conf_t *conf, server_t *server)
{
807
	char *kasp_dir = conf_db(conf, C_KASP_DB);
808
	conf_val_t kasp_size = conf_default_get(conf, C_MAX_KASP_DB_SIZE);
809 810 811
	int ret = knot_lmdb_reinit(&server->kaspdb, kasp_dir, conf_int(&kasp_size), 0);
	if (ret != KNOT_EOK) {
		log_warning("ignored reconfiguration of KASP DB (%s)", knot_strerror(ret));
812 813
	}
	free(kasp_dir);
814

815
	return KNOT_EOK; // not "ret"
816 817
}

818 819
static int reconfigure_timer_db(conf_t *conf, server_t *server)
{
820 821 822 823
	char *timer_dir = conf_db(conf, C_TIMER_DB);
	conf_val_t timer_size = conf_default_get(conf, C_MAX_TIMER_DB_SIZE);
	int ret = knot_lmdb_reconfigure(&server->timerdb, timer_dir, conf_int(&timer_size), 0);
	free(timer_dir);
824 825 826
	return ret;
}

827
void server_reconfigure(conf_t *conf, server_t *server)
828
{
829 830
	if (conf == NULL || server == NULL) {
		return;
831 832 833 834
	}

	/* First reconfiguration. */
	if (!(server->state & ServerRunning)) {
835
		log_info("Knot DNS %s starting", PACKAGE_VERSION);
836 837 838
	}

	/* Reconfigure server threads. */
839
	int ret;
840
	if ((ret = reconfigure_threads(conf, server)) < 0) {
841 842
		log_error("failed to reconfigure server threads (%s)",
		          knot_strerror(ret));
843 844
	}

845 846 847
	/* Reconfigure journal DB. */
	if ((ret = reconfigure_journal_db(conf, server)) < 0) {
		log_error("failed to reconfigure journal DB (%s)",
848
		          knot_strerror(ret));
849 850
	}

851 852 853 854 855 856
	/* Reconfigure KASP DB. */
	if ((ret = reconfigure_kasp_db(conf, server)) < 0) {
		log_error("failed to reconfigure KASP DB (%s)",
		          knot_strerror(ret));
	}

857 858 859 860 861 862
	/* Reconfiure Timer DB. */
	if ((ret = reconfigure_timer_db(conf, server)) < 0) {
		log_error("failed to reconfigure Timer DB (%s)",
		          knot_strerror(ret));
	}

863
	/* Update bound sockets. */
864
	if ((ret = reconfigure_sockets(conf, server)) < 0) {
865 866
		log_error("failed to reconfigure server sockets (%s)",
		          knot_strerror(ret));
867 868 869
	}
}

870
void server_update_zones(conf_t *conf, server_t *server)
871
{
872 873 874 875
	if (conf == NULL || server == NULL) {
		return;
	}

876
	/* Prevent emitting of new zone events. */
877 878
	if (server->zone_db) {
		knot_zonedb_foreach(server->zone_db, zone_events_freeze);
879 880
	}