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

    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
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

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 32 33
#include "knot/server/server.h"
#include "knot/server/udp-handler.h"
#include "knot/server/tcp-handler.h"
34
#include "knot/zone/timers.h"
35
#include "knot/zone/zonedb-load.h"
36
#include "knot/worker/pool.h"
37 38
#include "contrib/net.h"
#include "contrib/sockaddr.h"
39
#include "contrib/trim.h"
40

41 42 43 44 45 46 47 48
/*! \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
};

49 50
/*! \brief Unbind interface and clear the structure. */
static void server_deinit_iface(iface_t *iface)
51 52
{
	/* Free UDP handler. */
53 54 55 56 57
	for (int i = 0; i < iface->fd_udp_count; i++) {
		if (iface->fd_udp[i] > -1) {
			close(iface->fd_udp[i]);
		}
	}
58
	free(iface->fd_udp);
59 60

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

65 66 67 68 69 70 71 72 73 74 75
	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);
76 77 78
	free(iface);
}

79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
/*! \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.
 */
100
static bool enlarge_net_buffers(int sock, int min_recvsize, int min_sndsize)
101 102 103 104 105
{
	return setsockopt_min(sock, SO_RCVBUF, min_recvsize) &&
	       setsockopt_min(sock, SO_SNDBUF, min_sndsize);
}

106 107 108 109 110 111 112 113 114 115 116
/*!
 * \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;
117
#if defined(IP_PKTINFO)
118
		option = IP_PKTINFO; /* Linux */
119
#elif defined(IP_RECVDSTADDR)
120
		option = IP_RECVDSTADDR; /* BSD */
121 122
#else
		return false;
123
#endif
124 125 126 127 128 129 130 131 132 133 134 135
		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;
}
136

137 138 139 140 141
/**
 * \brief Enable TCP Fast Open.
 */
static int enable_fastopen(int sock, int backlog)
{
142
#if defined(TCP_FASTOPEN)
143 144 145 146 147 148 149 150
#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();
	}
151
#endif
152 153 154
	return KNOT_EOK;
}

155 156 157 158 159 160
/*!
 * \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.
161 162
 * \param cfg_if Interface template from config.
 *
163 164 165
 * \retval 0 if successful (EOK).
 * \retval <0 on errors (EACCES, EINVAL, ENOMEM, EADDRINUSE).
 */
166
static int server_init_iface(iface_t *new_if, struct sockaddr_storage *addr, int udp_thread_count)
167 168
{
	/* Initialize interface. */
Marek Vavrusa's avatar
Marek Vavrusa committed
169
	int ret = 0;
170
	memset(new_if, 0, sizeof(iface_t));
171
	memcpy(&new_if->addr, addr, sizeof(struct sockaddr_storage));
172

173
	/* Convert to string address format. */
174
	char addr_str[SOCKADDR_STRLEN] = { 0 };
175
	sockaddr_tostr(addr_str, sizeof(addr_str), (struct sockaddr *)addr);
Jan Včelák's avatar
Jan Včelák committed
176

177
	int udp_socket_count = 1;
178
	int udp_bind_flags = 0;
179

180
#ifdef ENABLE_REUSEPORT
181
	udp_socket_count = udp_thread_count;
182
	udp_bind_flags |= NET_BIND_MULTIPLE;
183 184 185 186 187 188 189
#endif

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

190 191 192 193 194 195
	/* Initialize the sockets to ensure safe early deinitialization. */
	for (int i = 0; i < udp_socket_count; i++) {
		new_if->fd_udp[new_if->fd_udp_count] = -1;
	};
	new_if->fd_tcp = -1;

196 197 198 199 200
	bool warn_bind = false;
	bool warn_bufsize = false;

	/* Create bound UDP sockets. */
	for (int i = 0; i < udp_socket_count; i++ ) {
201
		int sock = net_bound_socket(SOCK_DGRAM, (struct sockaddr *)addr, udp_bind_flags);
202
		if (sock == KNOT_EADDRNOTAVAIL) {
203 204
			udp_bind_flags |= NET_BIND_NONLOCAL;
			sock = net_bound_socket(SOCK_DGRAM, (struct sockaddr *)addr, udp_bind_flags);
205
			if (sock >= 0 && !warn_bind) {
206
				log_warning("address %s UDP bound, but required nonlocal bind", addr_str);
207
				warn_bind = true;
208 209 210 211
			}
		}

		if (sock < 0) {
212
			log_error("cannot bind address %s (%s)", addr_str,
213 214
			          knot_strerror(sock));
			server_deinit_iface(new_if);
215 216 217
			return sock;
		}

218 219
		if (!enlarge_net_buffers(sock, UDP_MIN_RCVSIZE, UDP_MIN_SNDSIZE) &&
		    !warn_bufsize) {
220
			log_warning("failed to set network buffer sizes for UDP");
221
			warn_bufsize = true;
222 223
		}

224
		if (sockaddr_is_any((struct sockaddr *)addr) && !enable_pktinfo(sock, addr->ss_family)) {
225
			log_warning("failed to enable received packet information retrieval");
226 227
		}

228 229
		new_if->fd_udp[new_if->fd_udp_count] = sock;
		new_if->fd_udp_count += 1;
230 231
	}

232
	/* Create bound TCP socket. */
233 234 235 236 237 238
	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) {
239
			log_warning("address %s TCP bound, but required nonlocal bind", addr_str);
240 241 242
		}
	}

243
	if (sock < 0) {
244
		log_error("cannot bind address %s (%s)", addr_str,
245 246
		          knot_strerror(sock));
		server_deinit_iface(new_if);
247
		return sock;
248 249
	}

250
	if (!enlarge_net_buffers(sock, TCP_MIN_RCVSIZE, TCP_MIN_SNDSIZE)) {
251 252 253
		log_warning("failed to set network buffer sizes for TCP");
	}

254
	new_if->fd_tcp = sock;
255 256

	/* Listen for incoming connections. */
257
	ret = listen(sock, TCP_BACKLOG_SIZE);
258
	if (ret < 0) {
259
		log_error("failed to listen on TCP interface %s", addr_str);
260
		server_deinit_iface(new_if);
261
		return KNOT_ERROR;
262
	}
263

264 265 266
	/* TCP Fast Open. */
	ret = enable_fastopen(sock, TCP_BACKLOG_SIZE);
	if (ret < 0) {
267
		log_warning("failed to enable TCP Fast Open on %s (%s)",
268 269 270
		            addr_str, knot_strerror(ret));
	}

Marek Vavrusa's avatar
Marek Vavrusa committed
271
	return KNOT_EOK;
272 273
}

274
static void remove_ifacelist(struct ref *p)
275 276
{
	ifacelist_t *ifaces = (ifacelist_t *)p;
Jan Včelák's avatar
Jan Včelák committed
277

278
	/* Remove deprecated interfaces. */
279
	char addr_str[SOCKADDR_STRLEN] = {0};
280 281
	iface_t *n = NULL, *m = NULL;
	WALK_LIST_DELSAFE(n, m, ifaces->u) {
282
		sockaddr_tostr(addr_str, sizeof(addr_str), (struct sockaddr *)&n->addr);
283
		log_info("removing interface %s", addr_str);
284 285 286 287 288
		server_remove_iface(n);
	}
	WALK_LIST_DELSAFE(n, m, ifaces->l) {
		free(n);
	}
Jan Včelák's avatar
Jan Včelák committed
289

290 291 292
	free(ifaces);
}

293 294 295 296 297 298
/*!
 * \brief Update bound sockets according to configuration.
 *
 * \param server Server instance.
 * \return number of added sockets.
 */
299
static int reconfigure_sockets(conf_t *conf, server_t *s)
300
{
301 302
	/* Prepare helper lists. */
	int bound = 0;
303
	ifacelist_t *oldlist = s->ifaces;
304 305 306 307 308
	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
309

310 311
	/* Duplicate current list. */
	/*! \note Pointers to addr, handlers etc. will be shared. */
312 313
	if (s->ifaces) {
		list_dup(&s->ifaces->u, &s->ifaces->l, sizeof(iface_t));
314 315 316
	}

	/* Update bound interfaces. */
317 318 319 320 321
	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;
322

323 324
		/* Find already matching interface. */
		int found_match = 0;
325
		struct sockaddr_storage addr = conf_addr(&listen_val, rundir);
326 327 328
		if (s->ifaces) {
			WALK_LIST(m, s->ifaces->u) {
				/* Matching port and address. */
329 330
				if (sockaddr_cmp((struct sockaddr *)&addr,
				                 (struct sockaddr *)&m->addr) == 0) {
331 332
					found_match = 1;
					break;
333 334
				}
			}
335 336
		}

337 338
		/* Found already bound interface. */
		if (found_match) {
339
			rem_node((node_t *)m);
340
		} else {
341
			char addr_str[SOCKADDR_STRLEN] = { 0 };
342
			sockaddr_tostr(addr_str, sizeof(addr_str), (struct sockaddr *)&addr);
343
			log_info("binding to interface %s", addr_str);
344

345 346
			/* Create new interface. */
			m = malloc(sizeof(iface_t));
347 348
			unsigned size = s->handlers[IO_UDP].handler.unit->size;
			if (server_init_iface(m, &addr, size) < 0) {
349 350 351
				free(m);
				m = 0;
			}
352
		}
353 354 355

		/* Move to new list. */
		if (m) {
356
			add_tail(&newlist->l, (node_t *)m);
357
			++bound;
358
		}
359 360

		conf_val_next(&listen_val);
361
	}
362
	free(rundir);
363

364 365
	/* Wait for readers that are reconfiguring right now. */
	/*! \note This subsystem will be reworked in #239 */
366 367 368
	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;
369 370 371 372 373 374 375
		for (unsigned i = 0; i < tu->size; ++i) {
			while (ioh->thread_state[i] & ServerReload) {
				sleep(1);
			}
		}
	}

376
	/* Publish new list. */
377
	s->ifaces = newlist;
Jan Včelák's avatar
Jan Včelák committed
378

379
	/* Update TCP+UDP ifacelist (reload all threads). */
380
	unsigned thread_count = 0;
381 382
	for (unsigned proto = IO_UDP; proto <= IO_TCP; ++proto) {
		dt_unit_t *tu = s->handlers[proto].handler.unit;
383 384
		for (unsigned i = 0; i < tu->size; ++i) {
			ref_retain((ref_t *)newlist);
385 386
			s->handlers[proto].handler.thread_state[i] |= ServerReload;
			s->handlers[proto].handler.thread_id[i] = thread_count++;
387 388 389 390
			if (s->state & ServerRunning) {
				dt_activate(tu->threads[i]);
				dt_signalize(tu->threads[i], SIGALRM);
			}
391
		}
392
	}
393

394
	ref_release(&oldlist->ref);
395

396
	return bound;
397 398
}

399
int server_init(server_t *server, int bg_workers)
400
{
Marek Vavrusa's avatar
Marek Vavrusa committed
401
	if (server == NULL) {
402
		return KNOT_EINVAL;
Marek Vavrusa's avatar
Marek Vavrusa committed
403
	}
404

405
	/* Clear the structure. */
406
	memset(server, 0, sizeof(server_t));
407

408 409
	/* Initialize event scheduler. */
	if (evsched_init(&server->sched, server) != KNOT_EOK) {
410 411 412
		return KNOT_ENOMEM;
	}

413
	server->workers = worker_pool_create(bg_workers);
414
	if (server->workers == NULL) {
415
		evsched_deinit(&server->sched);
416 417
		return KNOT_ENOMEM;
	}
418

419
	char *journal_dir = conf_journalfile(conf());
420
	conf_val_t journal_size = conf_default_get(conf(), C_MAX_JOURNAL_DB_SIZE);
421
	conf_val_t journal_mode = conf_default_get(conf(), C_JOURNAL_DB_MODE);
422
	int ret = journal_db_init(&server->journal_db, journal_dir,
423
	                          conf_int(&journal_size), conf_opt(&journal_mode));
424 425 426 427 428 429 430
	free(journal_dir);
	if (ret != KNOT_EOK) {
		worker_pool_destroy(server->workers);
		evsched_deinit(&server->sched);
		return ret;
	}

431
	char *kasp_dir = conf_kaspdir(conf());
432 433
	conf_val_t kasp_size = conf_default_get(conf(), C_MAX_KASP_DB_SIZE);
	ret = kasp_db_init(kaspdb(), kasp_dir, conf_int(&kasp_size));
434 435 436 437 438 439 440 441
	free(kasp_dir);
	if (ret != KNOT_EOK) {
		journal_db_close(&server->journal_db);
		worker_pool_destroy(server->workers);
		evsched_deinit(&server->sched);
		return ret;
	}

442 443 444 445 446 447 448
	return KNOT_EOK;
}

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

451 452 453 454 455 456 457
	/* 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);
458 459
	}

460
	/* Free threads and event handlers. */
461
	worker_pool_destroy(server->workers);
462 463 464 465 466

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

	/* Free remaining events. */
467
	evsched_deinit(&server->sched);
468

469 470 471
	/* Close kasp_db. */
	kasp_db_close(kaspdb());

472
	/* Close journal database if open. */
473
	journal_db_close(&server->journal_db);
474

475
	/* Close persistent timers database. */
476
	zone_timers_close(server->timers_db);
477

478 479
	/* Clear the structure. */
	memset(server, 0, sizeof(server_t));
480 481
}

482 483
static int server_init_handler(server_t *server, int index, int thread_count,
                               runnable_t runnable, runnable_t destructor)
484
{
485
	/* Initialize */
486
	iohandler_t *h = &server->handlers[index].handler;
487
	memset(h, 0, sizeof(iohandler_t));
488 489 490 491 492 493
	h->server = server;
	h->unit = dt_create(thread_count, runnable, destructor, h);
	if (h->unit == NULL) {
		return KNOT_ENOMEM;
	}

494
	h->thread_state = calloc(thread_count, sizeof(unsigned));
495 496 497
	if (h->thread_state == NULL) {
		dt_delete(&h->unit);
		return KNOT_ENOMEM;
Marek Vavrusa's avatar
Marek Vavrusa committed
498 499
	}

500 501
	h->thread_id = calloc(thread_count, sizeof(unsigned));
	if (h->thread_id == NULL) {
502
		free(h->thread_state);
503 504 505 506
		dt_delete(&h->unit);
		return KNOT_ENOMEM;
	}

507
	return KNOT_EOK;
508 509
}

510
static void server_free_handler(iohandler_t *h)
511
{
512 513
	if (h == NULL || h->server == NULL) {
		return;
514 515 516 517
	}

	/* Wait for threads to finish */
	if (h->unit) {
518 519
		dt_stop(h->unit);
		dt_join(h->unit);
Marek Vavrusa's avatar
Marek Vavrusa committed
520 521
	}

522
	/* Destroy worker context. */
523
	dt_delete(&h->unit);
524
	free(h->thread_state);
525
	free(h->thread_id);
526
	memset(h, 0, sizeof(iohandler_t));
527 528
}

529
int server_start(server_t *server, bool async)
530
{
531
	if (server == NULL) {
Marek Vavrusa's avatar
Marek Vavrusa committed
532
		return KNOT_EINVAL;
533 534
	}

535
	/* Start workers. */
536
	worker_pool_start(server->workers);
Marek Vavrusa's avatar
Marek Vavrusa committed
537

538 539
	/* Wait for enqueued events if not asynchronous. */
	if (!async) {
540
		worker_pool_wait(server->workers);
541
	}
Jan Včelák's avatar
Jan Včelák committed
542

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

546
	/* Start I/O handlers. */
547
	server->state |= ServerRunning;
548 549 550
	for (int proto = IO_UDP; proto <= IO_TCP; ++proto) {
		if (server->handlers[proto].size > 0) {
			int ret = dt_start(server->handlers[proto].handler.unit);
551 552 553
			if (ret != KNOT_EOK) {
				return ret;
			}
554
		}
Marek Vavrusa's avatar
Marek Vavrusa committed
555
	}
Jan Včelák's avatar
Jan Včelák committed
556

557
	return KNOT_EOK;
558
}
559

560
void server_wait(server_t *server)
561
{
562
	if (server == NULL) {
563 564
		return;
	}
Jan Včelák's avatar
Jan Včelák committed
565

566 567
	evsched_join(&server->sched);
	worker_pool_join(server->workers);
568

569 570 571
	for (int proto = IO_UDP; proto <= IO_TCP; ++proto) {
		if (server->handlers[proto].size > 0) {
			server_free_handler(&server->handlers[proto].handler);
572
		}
Marek Vavrusa's avatar
Marek Vavrusa committed
573
	}
574 575
}

576 577
static int reload_conf(conf_t *new_conf)
{
578
	yp_schema_purge_dynamic(new_conf->schema);
579 580 581 582 583 584 585

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

586 587 588 589 590 591
	/* Re-import zonefile if specified. */
	const char *filename = conf()->filename;
	if (filename != NULL) {
		log_info("reloading configuration file '%s'", filename);

		/* Import the configuration file. */
592
		ret = conf_import(new_conf, filename, true);
593 594 595 596 597 598 599
		if (ret != KNOT_EOK) {
			log_error("failed to load configuration file (%s)",
			          knot_strerror(ret));
			return ret;
		}
	} else {
		log_info("reloading configuration database");
600 601 602 603 604 605 606 607 608 609 610 611

		/* 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;
			}
		}
612 613
	}

614 615
	conf_mod_load_purge(new_conf, false);

616
	// Migrate from old schema.
617
	ret = conf_migrate(new_conf);
618 619 620 621
	if (ret != KNOT_EOK) {
		log_error("failed to migrate configuration (%s)", knot_strerror(ret));
	}

622 623 624 625 626 627 628
	/* Refresh hostname. */
	conf_refresh_hostname(new_conf);

	return KNOT_EOK;
}

int server_reload(server_t *server)
629
{
630
	if (server == NULL) {
631 632
		return KNOT_EINVAL;
	}
Jan Včelák's avatar
Jan Včelák committed
633

634
	/* Check for no edit mode. */
635 636
	if (conf()->io.txn != NULL) {
		log_warning("reload aborted due to active configuration transaction");
637
		return KNOT_TXN_EEXISTS;
638
	}
639

640 641 642 643 644 645 646
	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;
	}
647

648 649 650
	yp_flag_t flags = conf()->io.flags;
	bool full = !(flags & CONF_IO_FACTIVE);
	bool reuse_modules = !full && !(flags & CONF_IO_FRLD_MOD);
651

652 653
	/* Reload configuration and modules if full reload or a module change. */
	if (full || !reuse_modules) {
654
		ret = reload_conf(new_conf);
655
		if (ret != KNOT_EOK) {
656
			conf_free(new_conf);
657
			return ret;
658
		}
Jan Včelák's avatar
Jan Včelák committed
659

660
		conf_activate_modules(new_conf, NULL, new_conf->query_modules,
661 662
		                      &new_conf->query_plan);
	}
663

664 665 666 667
	conf_update_flag_t upd_flags = CONF_UPD_FNOFREE;
	if (full) {
		upd_flags |= CONF_UPD_FCONFIO;
	}
668 669
	if (reuse_modules) {
		upd_flags |= CONF_UPD_FMODULES;
670 671
	}

672
	/* Update to the new config. */
673
	conf_t *old_conf = conf_update(new_conf, upd_flags);
674

675 676 677 678 679 680
	/* 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);
681
		stats_reconfigure(conf(), server);
682 683 684 685
	}
	if (full || (flags & (CONF_IO_FRLD_ZONES | CONF_IO_FRLD_ZONE))) {
		server_update_zones(conf(), server);
	}
686

687 688 689
	/* Free old config needed for module unload in zone reload. */
	conf_free(old_conf);

690 691 692 693 694
	if (full) {
		log_info("configuration reloaded");
	} else {
		// Reset confio reload context.
		conf()->io.flags = YP_FNONE;
695
		if (conf()->io.zones != NULL) {
696
			trie_clear(conf()->io.zones);
697
		}
698
	}
699

700
	return KNOT_EOK;
701 702
}

703 704
void server_stop(server_t *server)
{
705
	log_info("stopping server");
Jan Včelák's avatar
Jan Včelák committed
706

Vitezslav Kriz's avatar
Vitezslav Kriz committed
707 708
	/* Stop scheduler. */
	evsched_stop(&server->sched);
709 710
	/* Interrupt background workers. */
	worker_pool_stop(server->workers);
711

712
	/* Clear 'running' flag. */
713
	server->state &= ~ServerRunning;
714 715
}

716
static int reset_handler(server_t *server, int index, unsigned size, runnable_t run)
717
{
718
	if (server->handlers[index].size != size) {
719
		/* Free old handlers */
720 721
		if (server->handlers[index].size > 0) {
			server_free_handler(&server->handlers[index].handler);
722
		}
Jan Včelák's avatar
Jan Včelák committed
723

724
		/* Initialize I/O handlers. */
725
		int ret = server_init_handler(server, index, size, run, NULL);
726 727 728 729
		if (ret != KNOT_EOK) {
			return ret;
		}

730
		/* Start if server is running. */
731
		if (server->state & ServerRunning) {
732 733 734
			ret = dt_start(server->handlers[index].handler.unit);
			if (ret != KNOT_EOK) {
				return ret;
735 736
			}
		}
737
		server->handlers[index].size = size;
738
	}
Jan Včelák's avatar
Jan Včelák committed
739

740 741 742 743 744 745 746 747 748 749 750 751
	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);
752 753
}

754
static int reconfigure_journal_db(conf_t *conf, server_t *server)
755
{
756 757
	char *journal_dir = conf_journalfile(conf);
	conf_val_t journal_size = conf_default_get(conf, C_MAX_JOURNAL_DB_SIZE);
758
	conf_val_t journal_mode = conf_default_get(conf, C_JOURNAL_DB_MODE);
759 760
	bool changed_path = (strcmp(journal_dir, server->journal_db->path) != 0);
	bool changed_size = (conf_int(&journal_size) != server->journal_db->fslimit);
761
	bool changed_mode = (conf_opt(&journal_mode) != server->journal_db->mode);
762 763 764 765
	int ret = KNOT_EOK;

	if (server->journal_db->db != NULL) {
		if (changed_path) {
766
			log_warning("ignored reconfiguration of journal DB path (already open)");
767 768
		}
		if (changed_size) {
769
			log_warning("ignored reconfiguration of journal DB max size (already open)");
770
		}
771
		if (changed_mode) {
772
			log_warning("ignored reconfiguration of journal DB mode (already open)");
773 774
		}
	} else if (changed_path || changed_size || changed_mode) {
775
		journal_db_t *newjdb = NULL;
776 777
		ret = journal_db_init(&newjdb, journal_dir, conf_int(&journal_size),
		                      conf_opt(&journal_mode));
778 779 780
		if (ret == KNOT_EOK) {
			journal_db_close(&server->journal_db);
			server->journal_db = newjdb;
781 782
		}
	}
783
	free(journal_dir);
Jan Včelák's avatar
Jan Včelák committed
784

785
	return ret;
786 787
}

788 789 790
static int reconfigure_kasp_db(conf_t *conf, server_t *server)
{
	char *kasp_dir = conf_kaspdir(conf);
791 792
	conf_val_t kasp_size = conf_default_get(conf, C_MAX_KASP_DB_SIZE);
	int ret = kasp_db_reconfigure(kaspdb(), kasp_dir, conf_int(&kasp_size));
793 794
	switch (ret) {
	case KNOT_EBUSY:
795
		log_warning("ignored reconfiguration of KASP DB path (already open)");
796 797 798
		break;
	case KNOT_EEXIST:
		ret = KNOT_EBUSY;
799
		log_warning("ignored reconfiguration of KASP DB max size (already open)");
800 801 802 803 804 805 806 807 808
		break;
	case KNOT_ENODIFF:
	case KNOT_EOK:
		ret = KNOT_EOK;
		break;
	default:
		break;
	}
	free(kasp_dir);
809

810 811 812
	return ret;
}

813
void server_reconfigure(conf_t *conf, server_t *server)
814
{
815 816
	if (conf == NULL || server == NULL) {
		return;
817 818 819 820
	}

	/* First reconfiguration. */
	if (!(server->state & ServerRunning)) {
821
		log_info("Knot DNS %s starting", PACKAGE_VERSION);
822 823 824
	}

	/* Reconfigure server threads. */
825
	int ret;
826
	if ((ret = reconfigure_threads(conf, server)) < 0) {
827 828
		log_error("failed to reconfigure server threads (%s)",
		          knot_strerror(ret));
829 830
	}

831 832 833
	/* Reconfigure journal DB. */
	if ((ret = reconfigure_journal_db(conf, server)) < 0) {
		log_error("failed to reconfigure journal DB (%s)",
834
		          knot_strerror(ret));
835 836
	}

837 838 839 840 841 842
	/* Reconfigure KASP DB. */
	if ((ret = reconfigure_kasp_db(conf, server)) < 0) {
		log_error("failed to reconfigure KASP DB (%s)",
		          knot_strerror(ret));
	}

843
	/* Update bound sockets. */
844
	if ((ret = reconfigure_sockets(conf, server)) < 0) {
845 846
		log_error("failed to reconfigure server sockets (%s)",
		          knot_strerror(ret));
847 848 849
	}
}

850
static void reopen_timers_database(conf_t *conf, server_t *server)
851
{
852
	zone_timers_close(server->timers_db);
853 854
	server->timers_db = NULL;

855 856
	conf_val_t val = conf_default_get(conf, C_STORAGE);
	char *storage = conf_abs_path(&val, NULL);
857 858
	val = conf_default_get(conf, C_TIMER_DB);
	char *timer_db = conf_abs_path(&val, storage);
859
	free(storage);
860 861
	val = conf_default_get(conf, C_MAX_TIMER_DB_SIZE);
	size_t mapsize = conf_int(&val);
862

863
	int ret = zone_timers_open(timer_db, &server->timers_db, mapsize);
864
	if (ret != KNOT_EOK) {
865
		log_warning("cannot open persistent timer DB '%s' (%s)",
866
		            timer_db, knot_strerror(ret));
867
	}
868 869

	free(timer_db);
870 871
}

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

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

883 884
	/* Suspend workers, clear wating events, finish running events. */
	worker_pool_suspend(server->workers);
885
	worker_pool_clear(server->workers);
886 887
	worker_pool_wait(server->workers);

888
	/* Reload zone database and free old zones. */
889
	reopen_timers_database(conf, server);
890
	zonedb_reload(conf, server);
891

892 893 894
	/* Trim extra heap. */
	mem_trim();

895 896
	/* Resume workers and allow events on new zones. */
	worker_pool_resume(server->workers);
897 898
	if (server->zone_db) {
		knot_zonedb_foreach(server->zone_db, zone_events_start);
899 900 901
	}
}

902
ref_t *server_set_ifaces(server_t *server, fdset_t *fds, int index, int thread_id)
903
{
904 905 906
	if (server == NULL || server->ifaces == NULL || fds == NULL) {
		return NULL;
	}
Jan Včelák's avatar
Jan Včelák committed
907

908
	rcu_read_lock();
909
	fdset_clear(fds);
910

911 912
	iface_t *i = NULL;
	WALK_LIST(i, server->ifaces->l) {
913
#ifdef ENABLE_REUSEPORT
914
		int udp_id = thread_id % i->fd_udp_count;
915
#else
916
		int udp_id = 0;
917
#endif
918 919 920 921 922 923 924 925 926
		switch(index) {
		case IO_TCP:
			fdset_add(fds, i->fd_tcp, POLLIN, NULL);
			break;
		case IO_UDP:
			fdset_add(fds, i->fd_udp[udp_id], POLLIN, NULL);
			break;
		default:
			assert(0);
927 928 929
		}
	}
	rcu_read_unlock();
930 931

	return &server->ifaces->ref;
932
}