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