"Fossies" - the Fresh Open Source Software Archive 
Member "darkstat-3.0.721/darkstat.c" (12 Jan 2022, 14599 Bytes) of package /linux/privat/darkstat-3.0.721.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "darkstat.c" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
3.0.719_vs_3.0.721.
1 /* darkstat 3
2 * copyright (c) 2001-2014 Emil Mikulic.
3 *
4 * darkstat.c: signals, cmdline parsing, program body.
5 *
6 * You may use, modify and redistribute this file under the terms of the
7 * GNU General Public License version 2. (see COPYING.GPL)
8 */
9
10 #include "acct.h"
11 #include "cap.h"
12 #include "cdefs.h"
13 #include "config.h"
14 #include "conv.h"
15 #include "daylog.h"
16 #include "db.h"
17 #include "dns.h"
18 #include "err.h"
19 #include "hosts_db.h"
20 #include "http.h"
21 #include "localip.h"
22 #include "ncache.h"
23 #include "now.h"
24 #include "pidfile.h"
25 #include "str.h"
26
27 #include <assert.h>
28 #include <errno.h>
29 #include <inttypes.h>
30 #include <signal.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <syslog.h>
35 #include <unistd.h>
36 #include <pcap.h>
37
38 #ifndef INADDR_NONE
39 # define INADDR_NONE (-1) /* Solaris */
40 #endif
41
42 /* --- Signal handling --- */
43 static volatile int running = 1;
44 static void sig_shutdown(int signum _unused_) { running = 0; }
45
46 static volatile int reset_pending = 0, export_pending = 0;
47 static void sig_reset(int signum _unused_) {
48 reset_pending = 1;
49 export_pending = 1;
50 }
51
52 static void sig_export(int signum _unused_) { export_pending = 1; }
53
54 /* --- Commandline parsing --- */
55 static unsigned long parsenum(const char *str,
56 unsigned long max /* 0 for no max */) {
57 unsigned long n;
58 char *end;
59
60 errno = 0;
61 n = strtoul(str, &end, 10);
62 if (*end != '\0')
63 errx(1, "\"%s\" is not a valid number", str);
64 if (errno == ERANGE)
65 errx(1, "\"%s\" is out of range", str);
66 if ((max != 0) && (n > max))
67 errx(1, "\"%s\" is out of range (max %lu)", str, max);
68 return n;
69 }
70
71 static int opt_iface_seen = 0;
72 static void cb_interface(const char *arg) {
73 cap_add_ifname(arg);
74 opt_iface_seen = 1;
75 }
76
77 static void cb_filter(const char *arg) { cap_add_filter(arg); }
78
79 static const char *opt_capfile = NULL;
80 static void cb_capfile(const char *arg) { opt_capfile = arg; }
81
82 int opt_want_snaplen = -1;
83 static void cb_snaplen(const char *arg)
84 { opt_want_snaplen = (int)parsenum(arg, 0); }
85
86 int opt_want_pppoe = 0;
87 static void cb_pppoe(const char *arg _unused_) { opt_want_pppoe = 1; }
88
89 int opt_want_syslog = 0;
90 static void cb_syslog(const char *arg _unused_) { opt_want_syslog = 1; }
91
92 int opt_want_verbose = 0;
93 static void cb_verbose(const char *arg _unused_) { opt_want_verbose = 1; }
94
95 static int opt_want_daemonize = 1;
96 static void cb_no_daemon(const char *arg _unused_) { opt_want_daemonize = 0; }
97
98 static int opt_want_promisc = 1;
99 static void cb_no_promisc(const char *arg _unused_) { opt_want_promisc = 0; }
100
101 static int opt_want_dns = 1;
102 static void cb_no_dns(const char *arg _unused_) { opt_want_dns = 0; }
103
104 int opt_want_macs = 1;
105 static void cb_no_macs(const char *arg _unused_) { opt_want_macs = 0; }
106
107 int opt_want_lastseen = 1;
108 static void cb_no_lastseen(const char *arg _unused_) { opt_want_lastseen = 0; }
109
110 static unsigned short opt_bindport = 667;
111 static void cb_port(const char *arg)
112 { opt_bindport = (unsigned short)parsenum(arg, 65536); }
113
114 static void cb_bindaddr(const char *arg) { http_add_bindaddr(arg); }
115
116 static int is_localnet_specified = 0;
117 static void cb_local(const char *arg)
118 {
119 acct_init_localnet(arg);
120 is_localnet_specified = 1;
121 }
122
123 int opt_want_local_only = 0;
124 static void cb_local_only(const char *arg _unused_)
125 { opt_want_local_only = 1; }
126
127 static const char *opt_chroot_dir = NULL;
128 static void cb_chroot(const char *arg) { opt_chroot_dir = arg; }
129
130 static const char *opt_base = NULL;
131 static void cb_base(const char *arg) { opt_base = arg; }
132
133 static const char *opt_privdrop_user = NULL;
134 static void cb_user(const char *arg) { opt_privdrop_user = arg; }
135
136 static const char *opt_daylog_fn = NULL;
137 static void cb_daylog(const char *arg) { opt_daylog_fn = arg; }
138
139 static const char *import_fn = NULL;
140 static void cb_import(const char *arg) { import_fn = arg; }
141
142 static const char *export_fn = NULL;
143 static void cb_export(const char *arg) { export_fn = arg; }
144
145 static const char *pid_fn = NULL;
146 static void cb_pidfile(const char *arg) { pid_fn = arg; }
147
148 unsigned int opt_hosts_max = 1000;
149 static void cb_hosts_max(const char *arg)
150 { opt_hosts_max = parsenum(arg, 0); }
151
152 unsigned int opt_hosts_keep = 500;
153 static void cb_hosts_keep(const char *arg)
154 { opt_hosts_keep = parsenum(arg, 0); }
155
156 unsigned int opt_ports_max = 60;
157 static void cb_ports_max(const char *arg)
158 { opt_ports_max = parsenum(arg, 65536); }
159
160 unsigned int opt_ports_keep = 30;
161 static void cb_ports_keep(const char *arg)
162 { opt_ports_keep = parsenum(arg, 65536); }
163
164 unsigned int opt_highest_port = 65535;
165 static void cb_highest_port(const char *arg)
166 { opt_highest_port = parsenum(arg, 65535); }
167
168 int opt_wait_secs = -1;
169 static void cb_wait_secs(const char *arg)
170 { opt_wait_secs = (int)parsenum(arg, 0); }
171
172 int opt_want_hexdump = 0;
173 static void cb_hexdump(const char *arg _unused_)
174 { opt_want_hexdump = 1; }
175
176 static int opt_want_help = 0;
177 static void cb_help(const char *arg _unused_)
178 { opt_want_help = 1; }
179 static void cb_version(const char *arg _unused_)
180 { opt_want_help = -1; }
181
182 /* --- */
183
184 struct cmdline_arg {
185 const char *name, *arg_name; /* NULL arg_name means unary */
186 void (*callback)(const char *arg);
187 int num_seen;
188 };
189
190 static struct cmdline_arg cmdline_args[] = {
191 {"-i", "interface", cb_interface, -1},
192 {"-f", "filter", cb_filter, -1},
193 {"-r", "capfile", cb_capfile, 0},
194 {"-p", "port", cb_port, 0},
195 {"-b", "bindaddr", cb_bindaddr, -1},
196 {"-l", "network/netmask", cb_local, 0},
197 {"--base", "path", cb_base, 0},
198 {"--local-only", NULL, cb_local_only, 0},
199 {"--snaplen", "bytes", cb_snaplen, 0},
200 {"--pppoe", NULL, cb_pppoe, 0},
201 {"--syslog", NULL, cb_syslog, 0},
202 {"--verbose", NULL, cb_verbose, 0},
203 {"--no-daemon", NULL, cb_no_daemon, 0},
204 {"--no-promisc", NULL, cb_no_promisc, 0},
205 {"--no-dns", NULL, cb_no_dns, 0},
206 {"--no-macs", NULL, cb_no_macs, 0},
207 {"--no-lastseen", NULL, cb_no_lastseen, 0},
208 {"--chroot", "dir", cb_chroot, 0},
209 {"--user", "username", cb_user, 0},
210 {"--daylog", "filename", cb_daylog, 0},
211 {"--import", "filename", cb_import, 0},
212 {"--export", "filename", cb_export, 0},
213 {"--pidfile", "filename", cb_pidfile, 0},
214 {"--hosts-max", "count", cb_hosts_max, 0},
215 {"--hosts-keep", "count", cb_hosts_keep, 0},
216 {"--ports-max", "count", cb_ports_max, 0},
217 {"--ports-keep", "count", cb_ports_keep, 0},
218 {"--highest-port", "port", cb_highest_port, 0},
219 {"--wait", "secs", cb_wait_secs, 0},
220 {"--hexdump", NULL, cb_hexdump, 0},
221 {"--version", NULL, cb_version, 0},
222 {"--help", NULL, cb_help, 0},
223 {NULL, NULL, NULL, 0}
224 };
225
226 /* We autogenerate the usage statement from the cmdline_args data structure. */
227 static void usage(void) {
228 static char intro[] = "usage: darkstat ";
229 char indent[sizeof(intro)];
230 struct cmdline_arg *arg;
231
232 printf(PACKAGE_STRING " (using %s)\n", pcap_lib_version());
233 if (opt_want_help == -1) return;
234
235 memset(indent, ' ', sizeof(indent));
236 indent[0] = indent[sizeof(indent) - 1] = 0;
237
238 printf("\n%s", intro);
239 for (arg = cmdline_args; arg->name != NULL; arg++) {
240 printf("%s[ %s%s%s ]\n",
241 indent,
242 arg->name,
243 arg->arg_name != NULL ? " " : "",
244 arg->arg_name != NULL ? arg->arg_name : "");
245 indent[0] = ' ';
246 }
247 printf("\n"
248 "Please refer to the darkstat(8) manual page for further\n"
249 "documentation and usage examples.\n");
250 }
251
252 static void parse_sub_cmdline(const int argc, char * const *argv) {
253 struct cmdline_arg *arg;
254
255 if (argc == 0) return;
256 for (arg = cmdline_args; arg->name != NULL; arg++)
257 if (strcmp(argv[0], arg->name) == 0) {
258 if ((arg->arg_name != NULL) && (argc == 1)) {
259 fprintf(stderr,
260 "error: argument \"%s\" requires parameter \"%s\"\n",
261 arg->name, arg->arg_name);
262 usage();
263 exit(EXIT_FAILURE);
264 }
265 if (arg->num_seen > 0) {
266 fprintf(stderr,
267 "error: already specified argument \"%s\"\n",
268 arg->name);
269 usage();
270 exit(EXIT_FAILURE);
271 }
272
273 if (arg->num_seen != -1) /* accept more than one */
274 arg->num_seen++;
275
276 if (arg->arg_name == NULL) {
277 arg->callback(NULL);
278 parse_sub_cmdline(argc-1, argv+1);
279 } else {
280 arg->callback(argv[1]);
281 parse_sub_cmdline(argc-2, argv+2);
282 }
283 return;
284 }
285
286 fprintf(stderr, "error: illegal argument: \"%s\"\n", argv[0]);
287 usage();
288 exit(EXIT_FAILURE);
289 }
290
291 static void parse_cmdline(const int argc, char * const *argv) {
292 if (argc < 1) {
293 /* Not enough args. */
294 usage();
295 exit(EXIT_FAILURE);
296 }
297
298 parse_sub_cmdline(argc, argv);
299
300 if (opt_want_help) {
301 usage();
302 exit(EXIT_SUCCESS);
303 }
304
305 /* start syslogging as early as possible */
306 if (opt_want_syslog)
307 openlog("darkstat", LOG_NDELAY | LOG_PID, LOG_DAEMON);
308
309 /* default value */
310 if (opt_privdrop_user == NULL)
311 opt_privdrop_user = PRIVDROP_USER;
312
313 /* sanity check args */
314 if (!opt_iface_seen && opt_capfile == NULL)
315 errx(1, "must specify either interface (-i) or capture file (-r)");
316
317 if (opt_iface_seen && opt_capfile != NULL)
318 errx(1, "can't specify both interface (-i) and capture file (-r)");
319
320 if ((opt_hosts_max != 0) && (opt_hosts_keep >= opt_hosts_max)) {
321 opt_hosts_keep = opt_hosts_max / 2;
322 warnx("reducing --hosts-keep to %u, to be under --hosts-max (%u)",
323 opt_hosts_keep, opt_hosts_max);
324 }
325 verbosef("max %u hosts, cutting down to %u when exceeded",
326 opt_hosts_max, opt_hosts_keep);
327
328 if ((opt_ports_max != 0) && (opt_ports_keep >= opt_ports_max)) {
329 opt_ports_keep = opt_ports_max / 2;
330 warnx("reducing --ports-keep to %u, to be under --ports-max (%u)",
331 opt_ports_keep, opt_ports_max);
332 }
333 verbosef("max %u ports per host, cutting down to %u when exceeded",
334 opt_ports_max, opt_ports_keep);
335
336 if (opt_want_hexdump && !opt_want_verbose) {
337 opt_want_verbose = 1;
338 verbosef("--hexdump implies --verbose");
339 }
340
341 if (opt_want_hexdump && opt_want_daemonize) {
342 opt_want_daemonize = 0;
343 verbosef("--hexdump implies --no-daemon");
344 }
345
346 if (opt_want_local_only && !is_localnet_specified)
347 verbosef("WARNING: --local-only without -l only matches the local host");
348 }
349
350 static void run_from_capfile(void) {
351 now_init();
352 graph_init();
353 hosts_db_init();
354 cap_from_file(opt_capfile);
355 if (export_fn != NULL) db_export(export_fn);
356 hosts_db_free();
357 graph_free();
358 verbosef("Total packets: %llu, bytes: %llu",
359 (llu)acct_total_packets,
360 (llu)acct_total_bytes);
361 }
362
363 /* --- Program body --- */
364 int
365 main(int argc, char **argv)
366 {
367 test_64order();
368 parse_cmdline(argc-1, argv+1);
369
370 if (opt_capfile) {
371 run_from_capfile();
372 return 0;
373 }
374
375 /* must verbosef() before first fork to init lock */
376 verbosef("starting up");
377 if (pid_fn) pidfile_create(opt_chroot_dir, pid_fn, opt_privdrop_user);
378
379 if (opt_want_daemonize) {
380 verbosef("daemonizing to run in the background!");
381 daemonize_start();
382 verbosef("I am the main process");
383 }
384 if (pid_fn) pidfile_write_close();
385
386 /* do this first as it forks - minimize memory use */
387 if (opt_want_dns) dns_init(opt_privdrop_user);
388 cap_start(opt_want_promisc); /* needs root */
389 http_init_base(opt_base);
390 http_listen(opt_bindport);
391 ncache_init(); /* must do before chroot() */
392
393 privdrop(opt_chroot_dir, opt_privdrop_user);
394
395 /* Don't need root privs for these: */
396 now_init();
397 if (opt_daylog_fn != NULL) daylog_init(opt_daylog_fn);
398 graph_init();
399 hosts_db_init();
400 if (import_fn != NULL) db_import(import_fn);
401
402 if (signal(SIGTERM, sig_shutdown) == SIG_ERR)
403 errx(1, "signal(SIGTERM) failed");
404 if (signal(SIGINT, sig_shutdown) == SIG_ERR)
405 errx(1, "signal(SIGINT) failed");
406 if (signal(SIGUSR1, sig_reset) == SIG_ERR)
407 errx(1, "signal(SIGUSR1) failed");
408 if (signal(SIGUSR2, sig_export) == SIG_ERR)
409 errx(1, "signal(SIGUSR2) failed");
410
411 verbosef("entering main loop");
412 daemonize_finish();
413
414 while (running) {
415 int select_ret;
416 int max_fd = -1;
417 int use_timeout = 0;
418 int cap_ret;
419 struct timeval timeout;
420 struct timespec t;
421 fd_set rs, ws;
422
423 FD_ZERO(&rs);
424 FD_ZERO(&ws);
425 cap_fd_set(&rs, &max_fd, &timeout, &use_timeout);
426 http_fd_set(&rs, &ws, &max_fd, &timeout, &use_timeout);
427
428 select_ret = select(max_fd+1, &rs, &ws, NULL,
429 (use_timeout) ? &timeout : NULL);
430 if (select_ret == 0 && !use_timeout)
431 errx(1, "select() erroneously timed out");
432 if (select_ret == -1) {
433 if (errno == EINTR)
434 continue;
435 else
436 err(1, "select()");
437 }
438
439 timer_start(&t);
440 now_update();
441
442 if (export_pending) {
443 if (export_fn != NULL)
444 db_export(export_fn);
445 export_pending = 0;
446 }
447
448 if (reset_pending) {
449 if (export_pending)
450 continue; /* export before reset */
451 hosts_db_reset();
452 graph_reset();
453 reset_pending = 0;
454 }
455
456 graph_rotate();
457 cap_ret = cap_poll(&rs);
458 dns_poll();
459 http_poll(&rs, &ws);
460 timer_stop(&t, 1000000000, "event processing took longer than a second");
461
462 if (!cap_ret) {
463 running = 0;
464 }
465 }
466
467 verbosef("shutting down");
468 verbosef("pcap stats: %u packets received, %u packets dropped",
469 cap_pkts_recv, cap_pkts_drop);
470 http_stop();
471 cap_stop();
472 dns_stop();
473 if (export_fn != NULL) db_export(export_fn);
474 hosts_db_free();
475 graph_free();
476 if (opt_daylog_fn != NULL) daylog_free();
477 ncache_free();
478 if (pid_fn) pidfile_unlink();
479 verbosef("shut down");
480 return (EXIT_SUCCESS);
481 }
482
483 /* vim:set ts=3 sw=3 tw=78 expandtab: */