main.c 4.53 KB
Newer Older
Marek Vavruša's avatar
Marek Vavruša committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*  Copyright (C) 2014 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/>.
 */

Marek Vavruša's avatar
Marek Vavruša committed
17 18 19
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
20
#include <uv.h>
21
#include <libknot/internal/sockaddr.h>
Marek Vavruša's avatar
Marek Vavruša committed
22

23
#include "lib/defines.h"
24
#include "lib/resolve.h"
25 26
#include "daemon/network.h"
#include "daemon/worker.h"
27
#include "daemon/engine.h"
28
#include "daemon/bindings.h"
29 30 31 32 33 34 35 36

static void tty_read(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf)
{
	if (stream && buf && nread > 0) {
		/* Trim endln */
		char *cmd = buf->base;
		cmd[nread - 1] = '\0';
		/* Execute */
37
		engine_cmd((struct engine *)stream->data, cmd);
38 39 40 41 42 43 44
	}

	printf("> ");
	fflush(stdout);
}

static void tty_alloc(uv_handle_t *handle, size_t suggested, uv_buf_t *buf) {
Marek Vavruša's avatar
Marek Vavruša committed
45 46
	buf->len = suggested;
	buf->base = malloc(suggested);
47
}
48

49 50 51 52 53 54
void signal_handler(uv_signal_t *handle, int signum)
{
	uv_stop(uv_default_loop());
	uv_signal_stop(handle);
}

Marek Vavruša's avatar
Marek Vavruša committed
55
static void help(int argc, char *argv[])
Marek Vavruša's avatar
Marek Vavruša committed
56
{
57
	printf("Usage: %s [parameters] [rundir]\n", argv[0]);
Marek Vavruša's avatar
Marek Vavruša committed
58
	printf("\nParameters:\n"
59 60 61 62 63
	       " -a, --addr=[addr]   Server address (default: localhost#53).\n"
	       " -v, --version       Print version of the server.\n"
	       " -h, --help          Print help and usage.\n"
	       "Options:\n"
	       " [rundir]            Path to the working directory (default: .)\n");
Marek Vavruša's avatar
Marek Vavruša committed
64 65
}

66
static const char *set_addr(char *addr, int *port)
67
{
68 69 70 71
	char *p = strchr(addr, '#');
	if (p) {
		*port = atoi(p + 1);
		*p = '\0';
Marek Vavruša's avatar
Marek Vavruša committed
72
	}
73

74
	return addr;
Marek Vavruša's avatar
Marek Vavruša committed
75 76 77 78
}

int main(int argc, char **argv)
{
79
	const char *addr = NULL;
80
	int port = 53;
Marek Vavruša's avatar
Marek Vavruša committed
81 82 83 84 85

	/* Long options. */
	int c = 0, li = 0, ret = 0;
	struct option opts[] = {
		{"addr", required_argument, 0, 'a'},
86
		{"version",   no_argument,  0, 'v'},
Marek Vavruša's avatar
Marek Vavruša committed
87 88 89
		{"help",      no_argument,  0, 'h'},
		{0, 0, 0, 0}
	};
90
	while ((c = getopt_long(argc, argv, "a:vh", opts, &li)) != -1) {
Marek Vavruša's avatar
Marek Vavruša committed
91 92 93
		switch (c)
		{
		case 'a':
94
			addr = set_addr(optarg, &port);
Marek Vavruša's avatar
Marek Vavruša committed
95
			break;
96
		case 'v':
Marek Vavruša's avatar
Marek Vavruša committed
97 98 99 100
			printf("%s, version %s\n", "Knot DNS Resolver", PACKAGE_VERSION);
			return EXIT_SUCCESS;
		case 'h':
		case '?':
Marek Vavruša's avatar
Marek Vavruša committed
101
			help(argc, argv);
Marek Vavruša's avatar
Marek Vavruša committed
102 103
			return EXIT_SUCCESS;
		default:
Marek Vavruša's avatar
Marek Vavruša committed
104
			help(argc, argv);
Marek Vavruša's avatar
Marek Vavruša committed
105 106 107 108
			return EXIT_FAILURE;
		}
	}

109 110
	/* Switch to rundir. */
	if (optind < argc) {
111 112 113 114 115 116
		const char *rundir = argv[optind];
		if (access(rundir, W_OK) != 0) {
			fprintf(stderr, "[system] rundir '%s': not writeable\n", rundir);
			return EXIT_FAILURE;
		}
		ret = chdir(rundir);
117
		if (ret != 0) {
118
			fprintf(stderr, "[system] rundir '%s': %s\n", rundir, strerror(errno));
119 120
			return EXIT_FAILURE;
		}
121
	}
122

123
	/* Block signals. */
124
	uv_loop_t *loop = uv_default_loop();
125 126 127 128
	uv_signal_t sigint;
	uv_signal_init(loop, &sigint);
	uv_signal_start(&sigint, signal_handler, SIGINT);

129 130 131 132 133 134 135 136 137 138
	/* Create a server engine. */
	mm_ctx_t pool;
	mm_ctx_mempool(&pool, 4096);
	struct engine engine;
	ret = engine_init(&engine, &pool);
	if (ret != 0) {
		fprintf(stderr, "[system] failed to initialize engine: %s\n", kr_strerror(ret));
		return EXIT_FAILURE;
	}

139 140
	/* Load bindings */
	engine_lualib(&engine, "modules", lib_modules);
141 142
	engine_lualib(&engine, "net",     lib_net);
	engine_lualib(&engine, "cache",   lib_cache);
143 144 145 146 147 148 149

	/* Create main worker. */
	struct worker_ctx worker = {
		.engine = &engine,
		.loop = loop,
		.mm = NULL
	};
150
	loop->data = &worker;
151

152
	/* Bind to sockets. */
153
	ret = network_listen(&engine.net, addr, (uint16_t)port, NET_UDP|NET_TCP);
154
	if (ret != 0) {
155
		fprintf(stderr, "[system] bind to '%s#%d' %s\n", addr, port, knot_strerror(ret));
156 157
		ret = EXIT_FAILURE;
	} else {
158 159 160 161 162 163
		/* Allocate TTY */
		uv_pipe_t pipe;
		uv_pipe_init(loop, &pipe, 0);
		uv_pipe_open(&pipe, 0);
		pipe.data = &engine;

164 165
		/* Interactive stdin */
		if (!feof(stdin)) {
166
			printf("[system] listening on '%s#%d'\n", addr, port);
167
			printf("[system] started in interactive mode, type 'help()'\n");
168 169 170
			tty_read(NULL, 0, NULL);
			uv_read_start((uv_stream_t*) &pipe, tty_alloc, tty_read);
		}
171
		/* Run the event loop. */
172
		ret = engine_start(&engine);
173
	}
174 175

	/* Cleanup. */
176
	fprintf(stderr, "\n[system] quitting\n");
177
	engine_deinit(&engine);
178 179 180

	return ret;
}