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

    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/>.
 */

Lubos Slovak's avatar
Lubos Slovak committed
17
#include <stdio.h>
18
#include <stdlib.h>
19
#include <unistd.h>
Marek Vavrusa's avatar
Marek Vavrusa committed
20
#include <getopt.h>
21
#include <sys/stat.h>
22
#include <limits.h>
23

24 25 26
#ifdef HAVE_CAP_NG_H
#include <cap-ng.h>
#endif /* HAVE_CAP_NG_H */
Lubos Slovak's avatar
Lubos Slovak committed
27

28 29 30 31
#ifdef ENABLE_SYSTEMD_NOTIFY
#include <systemd/sd-daemon.h>
#endif

32
#include "libknot/common.h"
33
#include "libknot/dnssec/crypto.h"
34
#include "knot/knot.h"
35 36
#include "knot/server/server.h"
#include "knot/ctl/process.h"
37
#include "knot/ctl/remote.h"
38 39
#include "knot/conf/conf.h"
#include "knot/conf/logconf.h"
40
#include "knot/server/tcp-handler.h"
Lubos Slovak's avatar
Lubos Slovak committed
41

42 43 44 45
/* Signal flags. */
static volatile short sig_req_stop = 0;
static volatile short sig_req_reload = 0;
static volatile short sig_stopping = 0;
46

Daniel Salzman's avatar
Daniel Salzman committed
47
/* \brief Signal started state to the init system. */
48 49 50 51 52 53 54
static void init_signal_started(void)
{
#ifdef ENABLE_SYSTEMD_NOTIFY
	sd_notify(0, "READY=1");
#endif
}

Daniel Salzman's avatar
Daniel Salzman committed
55
/*! \brief atexit() handler for server code. */
56
static void knot_crypto_deinit(void)
57 58 59 60 61
{
	knot_crypto_cleanup();
	knot_crypto_cleanup_threads();
}

Daniel Salzman's avatar
Daniel Salzman committed
62 63 64 65 66 67 68 69 70
/*! \brief PID file cleanup handler. */
static void pid_cleanup(char *pidfile)
{
	if (pidfile && pid_remove(pidfile) < 0) {
		log_server_warning("Failed to remove PID file.\n");
	}
}

/*! \brief SIGINT signal handler. */
71 72
void interrupt_handle(int s)
{
Daniel Salzman's avatar
Daniel Salzman committed
73
	/* Reload configuration. */
74
	if (s == SIGHUP) {
75 76
		sig_req_reload = 1;
		return;
77
	}
Jan Včelák's avatar
Jan Včelák committed
78

Daniel Salzman's avatar
Daniel Salzman committed
79
	/* Stop server. */
80
	if (s == SIGINT || s == SIGTERM) {
81 82 83
		if (sig_stopping == 0) {
			sig_req_stop = 1;
			sig_stopping = 1;
84 85 86
		} else {
			exit(1);
		}
Lubos Slovak's avatar
Lubos Slovak committed
87
	}
88 89
}

90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
/*! \brief POSIX 1003.1e capabilities. */
static void setup_capabilities(void)
{
#ifdef HAVE_CAP_NG_H
	/* Drop all capabilities. */
	if (capng_have_capability(CAPNG_EFFECTIVE, CAP_SETPCAP)) {
		capng_clear(CAPNG_SELECT_BOTH);

		/* Retain ability to set capabilities and FS access. */
		capng_type_t tp = CAPNG_EFFECTIVE|CAPNG_PERMITTED;
		capng_update(CAPNG_ADD, tp, CAP_SETPCAP);
		capng_update(CAPNG_ADD, tp, CAP_DAC_OVERRIDE);
		capng_update(CAPNG_ADD, tp, CAP_CHOWN); /* Storage ownership. */

		/* Allow binding to privileged ports.
		 * (Not inheritable)
		 */
		capng_update(CAPNG_ADD, tp, CAP_NET_BIND_SERVICE);

		/* Allow setuid/setgid. */
		capng_update(CAPNG_ADD, tp, CAP_SETUID);
		capng_update(CAPNG_ADD, tp, CAP_SETGID);

		/* Allow priorities changing. */
		capng_update(CAPNG_ADD, tp, CAP_SYS_NICE);

Daniel Salzman's avatar
Daniel Salzman committed
116
		/* Apply. */
117 118 119 120 121 122 123 124 125 126 127 128 129 130
		if (capng_apply(CAPNG_SELECT_BOTH) < 0) {
			log_server_error("Couldn't set process capabilities - "
			                 "%s.\n", strerror(errno));
		}
	} else {
		log_server_info("User uid=%d is not allowed to set "
		                "capabilities, skipping.\n", getuid());
	}
#endif /* HAVE_CAP_NG_H */
}

/*! \brief Event loop listening for signals and remote commands. */
static void event_loop(server_t *server)
{
Daniel Salzman's avatar
Daniel Salzman committed
131
	/* Setup signal handler. */
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
	struct sigaction sa;
	memset(&sa, 0, sizeof(sa));
	sa.sa_handler = interrupt_handle;
	sigaction(SIGINT,  &sa, NULL);
	sigaction(SIGTERM, &sa, NULL);
	sigaction(SIGHUP,  &sa, NULL);
	sigaction(SIGPIPE, &sa, NULL);
	sigaction(SIGALRM, &sa, NULL);
	pthread_sigmask(SIG_BLOCK, &sa.sa_mask, NULL);

	/* Bind to control interface. */
	uint8_t buf[KNOT_WIRE_MAX_PKTSIZE];
	size_t buflen = sizeof(buf);
	int remote = remote_bind(conf()->ctl.iface);

	/* Run event loop. */
Daniel Salzman's avatar
Daniel Salzman committed
148
	for (;;) {
149 150 151 152 153 154 155 156
		pthread_sigmask(SIG_UNBLOCK, &sa.sa_mask, NULL);
		int ret = remote_poll(remote);
		pthread_sigmask(SIG_BLOCK, &sa.sa_mask, NULL);

		/* Events. */
		if (ret > 0) {
			ret = remote_process(server, conf()->ctl.iface,
			                     remote, buf, buflen);
Daniel Salzman's avatar
Daniel Salzman committed
157
			switch (ret) {
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
			case KNOT_CTL_STOP:
				sig_req_stop = 1;
				break;
			default:
				break;
			}
		}

		/* Interrupts. */
		if (sig_req_stop) {
			sig_req_stop = 0;
			server_stop(server);
			break;
		}
		if (sig_req_reload) {
			sig_req_reload = 0;
			server_reload(server, conf()->filename);
		}
	}
	pthread_sigmask(SIG_UNBLOCK, &sa.sa_mask, NULL);

Daniel Salzman's avatar
Daniel Salzman committed
179
	/* Close remote control interface. */
180 181 182 183 184 185 186
	remote_unbind(conf()->ctl.iface, remote);

	/* Wait for server to finish. */
	server_wait(server);
}

static void help(void)
187
{
Marek Vavrusa's avatar
Marek Vavrusa committed
188 189
	printf("Usage: %sd [parameters]\n",
	       PACKAGE_NAME);
190
	printf("\nParameters:\n"
191 192
	       " -c, --config <file>     Select configuration file.\n"
	       " -d, --daemonize=[dir]   Run server as a daemon.\n"
193 194
	       " -V, --version           Print version of the server.\n"
	       " -h, --help              Print help and usage.\n");
195 196
}

Lubos Slovak's avatar
Lubos Slovak committed
197
int main(int argc, char **argv)
Lubos Slovak's avatar
Lubos Slovak committed
198
{
Daniel Salzman's avatar
Daniel Salzman committed
199
	/* Parse command line arguments. */
Marek Vavrusa's avatar
Marek Vavrusa committed
200
	int c = 0, li = 0;
201
	int daemonize = 0;
202 203
	const char *config_fn = conf_find_default();
	const char *daemon_root = "/";
Jan Včelák's avatar
Jan Včelák committed
204

Marek Vavrusa's avatar
Marek Vavrusa committed
205 206 207
	/* Long options. */
	struct option opts[] = {
		{"config",    required_argument, 0, 'c'},
208
		{"daemonize", optional_argument, 0, 'd'},
Marek Vavrusa's avatar
Marek Vavrusa committed
209 210 211 212
		{"version",   no_argument,       0, 'V'},
		{"help",      no_argument,       0, 'h'},
		{0, 0, 0, 0}
	};
Jan Včelák's avatar
Jan Včelák committed
213

214
	while ((c = getopt_long(argc, argv, "c:dVh", opts, &li)) != -1) {
215 216
		switch (c)
		{
217
		case 'c':
218
			config_fn = optarg;
219
			break;
220 221
		case 'd':
			daemonize = 1;
222
			if (optarg) {
223
				daemon_root = optarg;
224
			}
225 226
			break;
		case 'V':
227
			printf("%s, version %s\n", "Knot DNS", PACKAGE_VERSION);
228
			return EXIT_SUCCESS;
229 230
		case 'h':
		case '?':
231
			help();
232
			return EXIT_SUCCESS;
233
		default:
234
			help();
235
			return EXIT_FAILURE;
236 237
		}
	}
238

Daniel Salzman's avatar
Daniel Salzman committed
239
	/* Check for non-option parameters. */
240 241
	if (argc - optind > 0) {
		help();
242
		return EXIT_FAILURE;
243 244
	}

Daniel Salzman's avatar
Daniel Salzman committed
245
	/* Now check if we want to daemonize. */
246 247
	if (daemonize) {
		if (daemon(1, 0) != 0) {
248
			fprintf(stderr, "Daemonization failed, shutting down...\n");
249
			return EXIT_FAILURE;
250 251 252
		}
	}

Daniel Salzman's avatar
Daniel Salzman committed
253
	/* Initialize cryptographic backend. */
254 255
	knot_crypto_init();
	knot_crypto_init_threads();
256
	atexit(knot_crypto_deinit);
257

Daniel Salzman's avatar
Daniel Salzman committed
258
	/* Initialize pseudorandom number generator. */
259
	srand(time(NULL));
260

261
	/* POSIX 1003.1e capabilities. */
262
	setup_capabilities();
Jan Včelák's avatar
Jan Včelák committed
263

264 265 266
	/* Default logging to std out/err. */
	log_init();

Daniel Salzman's avatar
Daniel Salzman committed
267
	/* Open configuration. */
268
	int res = conf_open(config_fn);
269
	conf_t *config = conf();
270 271 272
	if (res != KNOT_EOK) {
		log_server_fatal("Couldn't load configuration '%s': %s\n",
		                 config_fn, knot_strerror(res));
273
		return EXIT_FAILURE;
274 275
	}

276 277 278 279 280 281 282
	/* Initialize logging subsystem.
	 * @note We're logging since now. */
	log_reconfigure(config, NULL);
	conf_add_hook(config, CONF_LOG, log_reconfigure, NULL);

	/* Initialize server. */
	server_t server;
283
	res = server_init(&server, config->bg_workers);
284 285 286
	if (res != KNOT_EOK) {
		log_server_fatal("Could not initialize server: %s\n",
		                 knot_strerror(res));
287
		conf_free(conf());
Daniel Salzman's avatar
Daniel Salzman committed
288
		log_close();
289
		return EXIT_FAILURE;
290 291
	}

292 293 294 295 296 297 298 299
	/* Reconfigure server interfaces.
	 * @note This MUST be done before we drop privileges. */
	server_reconfigure(config, &server);
	conf_add_hook(config, CONF_ALL, server_reconfigure, &server);
	log_server_info("Configured %zu interfaces and %zu zones.\n",
	                list_size(&config->ifaces), hattrie_weight(config->zones));


300
	/* Alter privileges. */
301 302
	log_update_privileges(config->uid, config->gid);
	if (proc_update_privileges(config->uid, config->gid) != KNOT_EOK) {
303
		server_deinit(&server);
304
		conf_free(conf());
Daniel Salzman's avatar
Daniel Salzman committed
305
		log_close();
306 307
		return EXIT_FAILURE;
	}
308 309 310

	/* Check and create PID file. */
	long pid = (long)getpid();
311
	char *pidfile = NULL;
312
	if (daemonize) {
313 314
		pidfile = pid_check_and_create();
		if (pidfile == NULL) {
315
			server_deinit(&server);
316
			conf_free(conf());
317
			log_close();
318
			return EXIT_FAILURE;
319
		}
320 321

		log_server_info("PID stored in '%s'\n", pidfile);
322
		if (chdir(daemon_root) != 0) {
323 324
			log_server_warning("Can't change working directory to %s.\n",
			                   daemon_root);
325
		} else {
326
			log_server_info("Changed directory to %s.\n",
327
			                daemon_root);
328
		}
329 330
	}

331 332 333 334 335 336
	/* Register base signal handling. */
	struct sigaction emptyset;
	memset(&emptyset, 0, sizeof(struct sigaction));
	emptyset.sa_handler = interrupt_handle;
	sigaction(SIGALRM, &emptyset, NULL);
	sigaction(SIGPIPE, &emptyset, NULL);
337

338 339
	/* Now we're going multithreaded. */
	rcu_register_thread();
340

341 342 343 344 345 346 347 348 349
	/* Populate zone database and add reconfiguration hook. */
	log_server_info("Loading zones...\n");
	server_update_zones(config, &server);
	conf_add_hook(config, CONF_ALL, server_update_zones, &server);

	/* Check number of loaded zones. */
	if (knot_zonedb_size(server.zone_db) == 0) {
		log_server_warning("No zones loaded.\n");
	}
350

351 352
	/* Start it up. */
	log_server_info("Starting server...\n");
Daniel Salzman's avatar
Daniel Salzman committed
353 354 355 356 357 358 359 360
	res = server_start(&server);
	if (res != KNOT_EOK) {
		log_server_fatal("Failed to start server: %s.\n",
		                 knot_strerror(res));
		server_deinit(&server);
		rcu_unregister_thread();
		pid_cleanup(pidfile);
		log_close();
361
		conf_free(conf());
Daniel Salzman's avatar
Daniel Salzman committed
362 363
		return EXIT_FAILURE;
	}
364

Daniel Salzman's avatar
Daniel Salzman committed
365 366
	if (daemonize) {
		log_server_info("Server started as a daemon, PID = %ld\n", pid);
Lubos Slovak's avatar
Lubos Slovak committed
367
	} else {
Daniel Salzman's avatar
Daniel Salzman committed
368 369
		log_server_info("Server started in foreground, PID = %ld\n", pid);
		init_signal_started();
370 371
	}

Daniel Salzman's avatar
Daniel Salzman committed
372
	/* Start the event loop. */
373
	config = NULL; /* @note Invalidate pointer, as it may change now. */
Daniel Salzman's avatar
Daniel Salzman committed
374
	event_loop(&server);
375 376 377

	/* Teardown server and configuration. */
	server_deinit(&server);
378

379 380 381
	/* Free configuration. */
	conf_free(conf());

Daniel Salzman's avatar
Daniel Salzman committed
382 383 384
	/* Unhook from RCU. */
	rcu_unregister_thread();

385
	/* Cleanup PID file. */
Daniel Salzman's avatar
Daniel Salzman committed
386
	pid_cleanup(pidfile);
387

388 389
	log_server_info("Shut down.\n");
	log_close();
Marek Vavrusa's avatar
Marek Vavrusa committed
390

391
	return EXIT_SUCCESS;
392
}