1 /**
2  * @file flowgrindd.c
3  * @brief Flowgrind daemon
4  */
5 
6 /*
7  * Copyright (C) 2013-2014 Alexander Zimmermann <alexander.zimmermann@netapp.com>
8  * Copyright (C) 2010-2013 Arnd Hannemann <arnd@arndnet.de>
9  * Copyright (C) 2010-2013 Christian Samsel <christian.samsel@rwth-aachen.de>
10  * Copyright (C) 2009 Tim Kosse <tim.kosse@gmx.de>
11  * Copyright (C) 2007-2008 Daniel Schaffrath <daniel.schaffrath@mac.com>
12  *
13  * This file is part of Flowgrind.
14  *
15  * Flowgrind is free software: you can redistribute it and/or modify
16  * it under the terms of the GNU General Public License as published by
17  * the Free Software Foundation, either version 3 of the License, or
18  * (at your option) any later version.
19  *
20  * Flowgrind is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with Flowgrind.  If not, see <http://www.gnu.org/licenses/>.
27  *
28  */
29 
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif /* HAVE_CONFIG_H */
33 
34 #ifdef __DARWIN__
35 /** Temporarily renaming daemon() so compiler does not see the warning on OS X. */
36 #define daemon fake_daemon_function
37 #endif /* __DARWIN__ */
38 
39 #include <stdlib.h>
40 #include <stdio.h>
41 #include <unistd.h>
42 #include <pthread.h>
43 #include <errno.h>
44 #include <signal.h>
45 #include <syslog.h>
46 #include <string.h>
47 #include <sys/utsname.h>
48 #include <sys/wait.h>
49 #include <netinet/in.h>
50 #include <netinet/tcp.h>
51 #include <fcntl.h>
52 #include <netdb.h>
53 #include <sys/stat.h>
54 
55 /* xmlrpc-c */
56 #include <xmlrpc-c/base.h>
57 #include <xmlrpc-c/server.h>
58 #include <xmlrpc-c/server_abyss.h>
59 #include <xmlrpc-c/util.h>
60 
61 #include "common.h"
62 #include "daemon.h"
63 #include "fg_log.h"
64 #include "fg_affinity.h"
65 #include "fg_error.h"
66 #include "fg_math.h"
67 #include "fg_progname.h"
68 #include "fg_string.h"
69 #include "fg_time.h"
70 #include "fg_definitions.h"
71 #include "debug.h"
72 #include "fg_argparser.h"
73 #include "fg_rpc_server.h"
74 
75 #ifdef HAVE_LIBPCAP
76 #include "fg_pcap.h"
77 #endif /* HAVE_LIBPCAP */
78 
79 #ifdef __DARWIN__
80 /** Remap daemon() function. */
81 #undef daemon
82 extern int daemon(int, int);
83 #endif /* __DARWIN__ */
84 
85 /** Print error message, usage string and exit. Used for cmdline parsing errors. */
86 #define PARSE_ERR(err_msg, ...) do {	\
87 	errx(err_msg, ##__VA_ARGS__);	\
88 	usage(EXIT_FAILURE);		\
89 } while (0)
90 
91 /* External global variables */
92 extern const char *progname;
93 
94 /* XXX add a brief description doxygen */
95 static unsigned port = DEFAULT_LISTEN_PORT;
96 
97 /* XXX add a brief description doxygen */
98 static char *rpc_bind_addr = NULL;
99 
100 /** CPU core to which flowgrindd should bind to. */
101 static int core;
102 
103 /** Command line option parser. */
104 static struct arg_parser parser;
105 
106 /* Forward declarations */
107 static void usage(short status) __attribute__((noreturn));
108 static void tear_down_daemon(void);
109 
110 /**
111  * Print usage or error message and exit.
112  *
113  * Depending on exit status @p status print either the usage or an error
114  * message. In all cases it call exit() with the given exit status @p status.
115  *
116  * @param[in] status exit status
117  */
usage(short status)118 static void usage(short status)
119 {
120 	/* Syntax error. Emit 'try help' to stderr and exit */
121 	if (status != EXIT_SUCCESS) {
122 		fprintf(stderr, "Try '%s -h' for more information\n", progname);
123 		exit(status);
124 	}
125 
126 	fprintf(stdout,
127 		"Usage: %1$s [OPTION]...\n"
128 		"Advanced TCP traffic generator for Linux, FreeBSD, and Mac OS X.\n\n"
129 
130 		"Mandatory arguments to long options are mandatory for short options too.\n"
131 		"  -b ADDR        XML-RPC server bind address\n"
132 		"  -c #           bound daemon to specific CPU. First CPU is 0\n"
133 #ifdef DEBUG
134 		"  -d, --debug    increase debugging verbosity. Add option multiple times to\n"
135 		"                 increase the verbosity (no daemon, log to stderr)\n"
136 #else /* DEBUG */
137 		"  -d             don't fork into background, log to stderr\n"
138 #endif /* DEBUG */
139 		"  -h, --help     display this help and exit\n"
140 		"  -p #           XML-RPC server port\n"
141 #ifdef HAVE_LIBPCAP
142 		"  -w DIR         target directory for dump files. The daemon must be run as root\n"
143 #endif /* HAVE_LIBPCAP */
144 		"  -v, --version  print version information and exit\n",
145 		progname);
146 	exit(EXIT_SUCCESS);
147 }
148 
149 /**
150  * Signal handler to catching signals.
151  *
152  * @param[in] sig signal to catch
153  */
sighandler(int sig)154 static void sighandler(int sig)
155 {
156 	int status;
157 
158 	switch (sig) {
159 	case SIGCHLD:
160 		while (waitpid(-1, &status, WNOHANG) > 0)
161 			logging(LOG_NOTICE, "child returned (status = %d)",
162 				status);
163 		break;
164 	case SIGHUP:
165 		logging(LOG_NOTICE, "caught SIGHUP. don't know what to do.");
166 		break;
167 	case SIGALRM:
168 		logging(LOG_NOTICE, "caught SIGALRM, don't know what to do.");
169 		break;
170 	case SIGPIPE:
171 		break;
172 	case SIGINT:
173 	case SIGTERM:
174 		logging(LOG_NOTICE, "caught SIGINT/SIGTERM, tear down daemon");
175 		tear_down_daemon();
176 		break;
177 	default:
178 		logging(LOG_ALERT, "caught signal %d, but don't remember "
179 			"intercepting it, aborting...", sig);
180 		abort();
181 	}
182 }
183 
create_daemon_thread()184 void create_daemon_thread()
185 {
186 	int flags;
187 
188 	if (pipe(daemon_pipe) == -1)
189 		crit("could not create pipe");
190 
191 	if ((flags = fcntl(daemon_pipe[0], F_GETFL, 0)) == -1)
192 		flags = 0;
193 	fcntl(daemon_pipe[0], F_SETFL, flags | O_NONBLOCK);
194 
195 	pthread_mutex_init(&mutex, NULL);
196 
197 	int rc = pthread_create(&daemon_thread, NULL, daemon_main, 0);
198 	if (rc)
199 		critc(rc, "could not start thread");
200 }
201 
bind_daemon_to_core(void)202 void bind_daemon_to_core(void)
203 {
204 	pthread_t thread = pthread_self();
205 	int rc = pthread_setaffinity(thread, core);
206 
207 	if (rc)
208 		logging(LOG_WARNING, "failed to bind %s (PID %d) to CPU core %i",
209 			progname, getpid(), core);
210 	else
211 		DEBUG_MSG(LOG_INFO, "bind %s (PID %d) to CPU core %i",
212 			  progname, getpid(), core);
213 }
214 
215 #ifdef HAVE_LIBPCAP
process_dump_dir()216 int process_dump_dir() {
217 	if (!dump_dir)
218 		dump_dir = getcwd(NULL, 0);
219 
220 	struct stat dirstats;
221 
222 	if (stat(dump_dir, &dirstats) == -1) {
223 		DEBUG_MSG(LOG_WARNING, "unable to stat %s: %s", dump_dir,
224 			  strerror(errno));
225 		return 0;
226 	}
227 
228 	if (!S_ISDIR(dirstats.st_mode)) {
229 		DEBUG_MSG(LOG_ERR, "provided path %s is not a directory",
230 			  dump_dir);
231 		return 0;
232 	}
233 
234 	if (access(dump_dir, W_OK | X_OK) == -1) {
235 		DEBUG_MSG(LOG_ERR, "insufficent permissions to access %s: %s",
236 			  dump_dir, strerror(errno));
237 		return 0;
238 	}
239 
240 	/* ensure path contains terminating slash */
241 	if (dump_dir[strlen(dump_dir) - 1] != '/')
242 		asprintf_append(&dump_dir, "/");
243 
244 	return 1;
245 }
246 #endif /* HAVE_LIBPCAP */
247 
248 /**
249  * Parse command line options to initialize global options.
250  *
251  * @param[in] argc number of command line arguments
252  * @param[in] argv arguments provided by the command line
253  */
parse_cmdline(int argc,char * argv[])254 static void parse_cmdline(int argc, char *argv[])
255 {
256 	const struct ap_Option options[] = {
257 		{'b', 0, ap_yes, 0, 0},
258 		{'c', 0, ap_yes, 0, 0},
259 #ifdef DEBUG
260 		{'d', "debug", ap_no, 0, 0},
261 #else /* DEBUG */
262 		{'d', 0, ap_no, 0, 0},
263 #endif
264 		{'h', "help", ap_no, 0, 0},
265 		{'o', 0, ap_yes, 0, 0},
266 		{'p', 0, ap_yes, 0, 0},
267 		{'v', "version", ap_no, 0, 0},
268 #ifdef HAVE_LIBPCAP
269 		{'w', 0, ap_yes, 0, 0},
270 #endif /* HAVE_LIBPCAP */
271 		{0, 0, ap_no, 0, 0}
272 	};
273 
274 	if (!ap_init(&parser, argc, (const char* const*) argv, options, 0))
275 		critx("could not allocate memory for option parser");
276 	if (ap_error(&parser))
277 		PARSE_ERR("%s", ap_error(&parser));
278 
279 	/* parse command line */
280 	for (int argind = 0; argind < ap_arguments(&parser); argind++) {
281 		const int code = ap_code(&parser, argind);
282 		const char *arg = ap_argument(&parser, argind);
283 
284 		switch (code) {
285 		case 0:
286 			PARSE_ERR("invalid argument: %s", arg);
287 		case 'b':
288 			rpc_bind_addr = strdup(arg);
289 			if (sscanf(arg, "%s", rpc_bind_addr) != 1)
290 				PARSE_ERR("failed to parse bind address");
291 			break;
292 		case 'c':
293 			if (sscanf(arg, "%u", &core) != 1)
294 				PARSE_ERR("failed to parse CPU number");
295 			break;
296 		case 'd':
297 #ifdef DEBUG
298 			increase_debuglevel();
299 #endif /* DEBUG */
300 			break;
301 		case 'h':
302 			usage(EXIT_SUCCESS);
303 			break;
304 		case 'p':
305 			if (sscanf(arg, "%u", &port) != 1)
306 				PARSE_ERR("failed to parse port number");
307 			break;
308 #ifdef HAVE_LIBPCAP
309 		case 'w':
310 			dump_dir = strdup(arg);
311 			break;
312 #endif /* HAVE_LIBPCAP */
313 		case 'v':
314 			fprintf(stdout, "%s %s\%s\n%s\n\n%s\n", progname,
315 				FLOWGRIND_VERSION, FLOWGRIND_COPYRIGHT,
316 				FLOWGRIND_COPYING, FLOWGRIND_AUTHORS);
317 			exit(EXIT_SUCCESS);
318 			break;
319 		default:
320 			PARSE_ERR("uncaught option: %s", arg);
321 			break;
322 		}
323 	}
324 
325 #ifdef HAVE_LIBPCAP
326 	if (!process_dump_dir()) {
327 		if (ap_is_used(&parser, 'w'))
328 			PARSE_ERR("the dump directory %s for tcpdumps does "
329 				  "either not exist or you have insufficient "
330 				  "permissions to write to it", dump_dir);
331 		else
332 			warnx("tcpdumping will not be available since you "
333 			      "don't have sufficient permissions to write to "
334 			      "%s", dump_dir);
335 	}
336 #endif /* HAVE_LIBPCAP */
337 }
338 
sanity_check(void)339 static void sanity_check(void)
340 {
341 	if (core < 0) {
342 		errx("CPU binding failed. Given CPU ID is negative");
343 		exit(EXIT_FAILURE);
344 	}
345 
346 	if (core > get_ncores(NCORE_CURRENT)) {
347 		errx("CPU binding failed. Given CPU ID is higher then "
348 		     "available CPU cores");
349 		exit(EXIT_FAILURE);
350 	}
351 
352 	/* TODO more sanity checks... (e.g. if port is in valid range) */
353 }
354 
355 /**
356  * Gracefully tear down daemon
357  */
tear_down_daemon(void)358 static void tear_down_daemon(void)
359 {
360 	ap_free(&parser);
361 	close_logging();
362 	exit(EXIT_SUCCESS);
363 }
364 
main(int argc,char * argv[])365 int main(int argc, char *argv[])
366 {
367 	/* Info about the xmlrpc server */
368 	struct fg_rpc_server server;
369 
370 	/* Initialize sighandler */
371 	struct sigaction sa;
372 	sa.sa_handler = sighandler;
373 	sa.sa_flags = 0;
374 	sigemptyset (&sa.sa_mask);
375 	if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
376 		crit("could not ignore SIGPIPE");
377 	if (sigaction (SIGHUP, &sa, NULL))
378 		critx("could not set handler for SIGUP");
379 	if (sigaction (SIGALRM, &sa, NULL))
380 		critx("could not set handler for SIGALRM");
381 	if (sigaction (SIGCHLD, &sa, NULL))
382 		critx("could not set handler for SIGCHLD");
383 	if (sigaction(SIGINT, &sa, NULL))
384 		critx("could not set handler for SIGINT");
385 	if (sigaction(SIGTERM, &sa, NULL))
386 		critx("could not set handler for SIGTERM");
387 
388 	set_progname(argv[0]);
389 	parse_cmdline(argc, argv);
390 	sanity_check();
391 
392 	/* Initialize logging */
393 	if (!ap_is_used(&parser, 'd'))
394 		init_logging(LOGGING_SYSLOG);
395 	else
396 		init_logging(LOGGING_STDERR);
397 
398 	fg_list_init(&flows);
399 
400 #ifdef HAVE_LIBPCAP
401 	fg_pcap_init();
402 #endif /* HAVE_LIBPCAP */
403 
404 	init_rpc_server(&server, rpc_bind_addr, port);
405 
406 	/* Push flowgrindd into the background */
407 	if (!ap_is_used(&parser, 'd')) {
408 		/* Need to call daemon() before creating the thread because
409 		 * it internally calls fork() which does not copy threads. */
410 		if (daemon(0, 0) == -1)
411 			crit("daemon() failed");
412 		logging(LOG_NOTICE, "flowgrindd daemonized");
413 	}
414 
415 	if (ap_is_used(&parser, 'c'))
416 		bind_daemon_to_core();
417 
418 	create_daemon_thread();
419 
420 	/* This will block */
421 	run_rpc_server(&server);
422 	critx("control should never reach end of main()");
423 }
424