X-Git-Url: https://unix4lyfe.org/gitweb/darkstat/blobdiff_plain/2a9f7d48789fbf932fd96125d8ee9f49b45b09ac..65f1fa720bae005f117cedb5c0c7c7e9689e52ba:/darkstat.c diff --git a/darkstat.c b/darkstat.c index 22a55cf..8713998 100644 --- a/darkstat.c +++ b/darkstat.c @@ -1,5 +1,5 @@ /* darkstat 3 - * copyright (c) 2001-2010 Emil Mikulic. + * copyright (c) 2001-2011 Emil Mikulic. * * darkstat.c: signals, cmdline parsing, program body. * @@ -7,25 +7,25 @@ * GNU General Public License version 2. (see COPYING.GPL) */ -#include "darkstat.h" #include "acct.h" #include "cap.h" +#include "cdefs.h" +#include "config.h" #include "conv.h" #include "daylog.h" #include "db.h" #include "dns.h" +#include "err.h" #include "http.h" #include "hosts_db.h" #include "localip.h" #include "ncache.h" +#include "now.h" #include "pidfile.h" -#include "err.h" -#include -#include -#include #include #include +#include #include #include #include @@ -34,9 +34,6 @@ #include #include -#include "now.h" -time_t now; - #ifndef INADDR_NONE # define INADDR_NONE (-1) /* Solaris */ #endif @@ -46,8 +43,7 @@ static volatile int running = 1; static void sig_shutdown(int signum _unused_) { running = 0; } static volatile int reset_pending = 0, export_pending = 0; -static void sig_reset(int signum _unused_) -{ +static void sig_reset(int signum _unused_) { reset_pending = 1; export_pending = 1; } @@ -55,9 +51,8 @@ static void sig_reset(int signum _unused_) static void sig_export(int signum _unused_) { export_pending = 1; } /* --- Commandline parsing --- */ -static unsigned long -parsenum(const char *str, unsigned long max /* 0 for no max */) -{ +static unsigned long parsenum(const char *str, + unsigned long max /* 0 for no max */) { unsigned long n; char *end; @@ -72,87 +67,82 @@ parsenum(const char *str, unsigned long max /* 0 for no max */) return n; } -const char *interface = NULL; -static void cb_interface(const char *arg) { interface = arg; } - -const char *capfile = NULL; -static void cb_capfile(const char *arg) { capfile = arg; } +static int opt_iface_seen = 0; +static void cb_interface(const char *arg) { + cap_add_ifname(arg); + opt_iface_seen = 1; +} -int want_snaplen = -1; -static void cb_snaplen(const char *arg) { want_snaplen = parsenum(arg, 0); } +static void cb_filter(const char *arg) { cap_add_filter(arg); } -int want_pppoe = 0; -static void cb_pppoe(const char *arg _unused_) { want_pppoe = 1; } +const char *opt_capfile = NULL; +static void cb_capfile(const char *arg) { opt_capfile = arg; } -static void cb_syslog(const char *arg _unused_) { want_syslog = 1; } +int opt_want_snaplen = -1; +static void cb_snaplen(const char *arg) +{ opt_want_snaplen = (int)parsenum(arg, 0); } -static void cb_verbose(const char *arg _unused_) { want_verbose = 1; } +int opt_want_pppoe = 0; +static void cb_pppoe(const char *arg _unused_) { opt_want_pppoe = 1; } -int want_daemonize = 1; -static void cb_no_daemon(const char *arg _unused_) { want_daemonize = 0; } +int opt_want_syslog = 0; +static void cb_syslog(const char *arg _unused_) { opt_want_syslog = 1; } -int want_promisc = 1; -static void cb_no_promisc(const char *arg _unused_) { want_promisc = 0; } +int opt_want_verbose = 0; +static void cb_verbose(const char *arg _unused_) { opt_want_verbose = 1; } -int want_dns = 1; -static void cb_no_dns(const char *arg _unused_) { want_dns = 0; } +int opt_want_daemonize = 1; +static void cb_no_daemon(const char *arg _unused_) { opt_want_daemonize = 0; } -int want_macs = 1; -static void cb_no_macs(const char *arg _unused_) { want_macs = 0; } +int opt_want_promisc = 1; +static void cb_no_promisc(const char *arg _unused_) { opt_want_promisc = 0; } -int want_lastseen = 1; -static void cb_no_lastseen(const char *arg _unused_) { want_lastseen = 0; } +int opt_want_dns = 1; +static void cb_no_dns(const char *arg _unused_) { opt_want_dns = 0; } -unsigned short bindport = 667; -static void cb_port(const char *arg) { bindport = parsenum(arg, 65536); } +int opt_want_macs = 1; +static void cb_no_macs(const char *arg _unused_) { opt_want_macs = 0; } -const char *bindaddr = NULL; -static void cb_bindaddr(const char *arg) -{ - struct addrinfo hints, *ai; +int opt_want_lastseen = 1; +static void cb_no_lastseen(const char *arg _unused_) { opt_want_lastseen = 0; } - memset(&hints, 0, sizeof(hints)); - hints.ai_flags = AI_PASSIVE; -#ifdef AI_ADDRCONFIG - hints.ai_flags |= AI_ADDRCONFIG; -#endif - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; +unsigned short opt_bindport = 667; +static void cb_port(const char *arg) +{ opt_bindport = (unsigned short)parsenum(arg, 65536); } - if (getaddrinfo(arg, NULL, &hints, &ai)) - errx(1, "malformed address \"%s\"", arg); +static void cb_bindaddr(const char *arg) { http_add_bindaddr(arg); } - freeaddrinfo(ai); - bindaddr = arg; +static int is_localnet_specified = 0; +static void cb_local(const char *arg) +{ + acct_init_localnet(arg); + is_localnet_specified = 1; } -const char *filter = NULL; -static void cb_filter(const char *arg) { filter = arg; } +int opt_want_local_only = 0; +static void cb_local_only(const char *arg _unused_) +{ opt_want_local_only = 1; } -static void cb_local(const char *arg) { acct_init_localnet(arg); } +const char *opt_chroot_dir = NULL; +static void cb_chroot(const char *arg) { opt_chroot_dir = arg; } -const char *chroot_dir = NULL; -static void cb_chroot(const char *arg) { chroot_dir = arg; } +const char *opt_privdrop_user = NULL; +static void cb_user(const char *arg) { opt_privdrop_user = arg; } -static void cb_base(const char *arg) { http_init_base(arg); } - -const char *privdrop_user = NULL; -static void cb_user(const char *arg) { privdrop_user = arg; } - -const char *daylog_fn = NULL; +const char *opt_daylog_fn = NULL; static void cb_daylog(const char *arg) { - if (chroot_dir == NULL) + if (opt_chroot_dir == NULL) errx(1, "the daylog file is relative to the chroot.\n" "You must specify a --chroot dir before you can use --daylog."); else - daylog_fn = arg; + opt_daylog_fn = arg; } const char *import_fn = NULL; static void cb_import(const char *arg) { - if (chroot_dir == NULL) + if (opt_chroot_dir == NULL) errx(1, "the import file is relative to the chroot.\n" "You must specify a --chroot dir before you can use --import."); else @@ -162,7 +152,7 @@ static void cb_import(const char *arg) const char *export_fn = NULL; static void cb_export(const char *arg) { - if ((chroot_dir == NULL) && (capfile == NULL)) + if ((opt_chroot_dir == NULL) && (opt_capfile == NULL)) errx(1, "the export file is relative to the chroot.\n" "You must specify a --chroot dir before you can use --export."); else @@ -172,39 +162,46 @@ static void cb_export(const char *arg) static const char *pid_fn = NULL; static void cb_pidfile(const char *arg) { - if (chroot_dir == NULL) + if (opt_chroot_dir == NULL) errx(1, "the pidfile is relative to the chroot.\n" "You must specify a --chroot dir before you can use --pidfile."); else pid_fn = arg; } -unsigned int hosts_max = 1000; +unsigned int opt_hosts_max = 1000; static void cb_hosts_max(const char *arg) -{ hosts_max = parsenum(arg, 0); } +{ opt_hosts_max = parsenum(arg, 0); } -unsigned int hosts_keep = 500; +unsigned int opt_hosts_keep = 500; static void cb_hosts_keep(const char *arg) -{ hosts_keep = parsenum(arg, 0); } +{ opt_hosts_keep = parsenum(arg, 0); } -unsigned int ports_max = 200; +unsigned int opt_ports_max = 200; static void cb_ports_max(const char *arg) -{ ports_max = parsenum(arg, 65536); } +{ opt_ports_max = parsenum(arg, 65536); } -unsigned int ports_keep = 30; +unsigned int opt_ports_keep = 30; static void cb_ports_keep(const char *arg) -{ ports_keep = parsenum(arg, 65536); } +{ opt_ports_keep = parsenum(arg, 65536); } -unsigned int highest_port = 65535; +unsigned int opt_highest_port = 65535; static void cb_highest_port(const char *arg) -{ highest_port = parsenum(arg, 65535); } +{ opt_highest_port = parsenum(arg, 65535); } -int wait_secs = -1; +int opt_wait_secs = -1; static void cb_wait_secs(const char *arg) -{ wait_secs = parsenum(arg, 0); } +{ opt_wait_secs = (int)parsenum(arg, 0); } -int want_hexdump = 0; -static void cb_hexdump(const char *arg _unused_) { want_hexdump = 1; } +int opt_want_hexdump = 0; +static void cb_hexdump(const char *arg _unused_) +{ opt_want_hexdump = 1; } + +int opt_want_help = 0; +static void cb_help(const char *arg _unused_) +{ opt_want_help = 1; } +static void cb_version(const char *arg _unused_) +{ opt_want_help = -1; } /* --- */ @@ -215,8 +212,13 @@ struct cmdline_arg { }; static struct cmdline_arg cmdline_args[] = { - {"-i", "interface", cb_interface, 0}, - {"-r", "file", cb_capfile, 0}, + {"-i", "interface", cb_interface, -1}, + {"-f", "filter", cb_filter, -1}, + {"-r", "capfile", cb_capfile, 0}, + {"-p", "port", cb_port, 0}, + {"-b", "bindaddr", cb_bindaddr, -1}, + {"-l", "network/netmask", cb_local, 0}, + {"--local-only", NULL, cb_local_only, 0}, {"--snaplen", "bytes", cb_snaplen, 0}, {"--pppoe", NULL, cb_pppoe, 0}, {"--syslog", NULL, cb_syslog, 0}, @@ -226,11 +228,6 @@ static struct cmdline_arg cmdline_args[] = { {"--no-dns", NULL, cb_no_dns, 0}, {"--no-macs", NULL, cb_no_macs, 0}, {"--no-lastseen", NULL, cb_no_lastseen, 0}, - {"-p", "port", cb_port, 0}, - {"-b", "bindaddr", cb_bindaddr, 0}, - {"--base", "path", cb_base, 0}, - {"-f", "filter", cb_filter, 0}, - {"-l", "network/netmask", cb_local, 0}, {"--chroot", "dir", cb_chroot, 0}, {"--user", "username", cb_user, 0}, {"--daylog", "filename", cb_daylog, 0}, @@ -244,45 +241,38 @@ static struct cmdline_arg cmdline_args[] = { {"--highest-port", "port", cb_highest_port, 0}, {"--wait", "secs", cb_wait_secs, 0}, {"--hexdump", NULL, cb_hexdump, 0}, + {"--version", NULL, cb_version, 0}, + {"--help", NULL, cb_help, 0}, {NULL, NULL, NULL, 0} }; -static void -pad(const int width) -{ - int i; - for (i=0; iname != NULL; arg++) { - if (first) first = 0; else pad(width); - printf("[ %s", arg->name); - if (arg->arg_name != NULL) printf(" %s", arg->arg_name); - printf(" ]\n"); + printf("%s[ %s%s%s ]\n", + indent, + arg->name, + arg->arg_name != NULL ? " " : "", + arg->arg_name != NULL ? arg->arg_name : ""); + indent[0] = ' '; } printf("\n" "Please refer to the darkstat(8) manual page for further\n" "documentation and usage examples.\n"); } -static void -parse_sub_cmdline(const int argc, char * const *argv) -{ +static void parse_sub_cmdline(const int argc, char * const *argv) { struct cmdline_arg *arg; if (argc == 0) return; @@ -303,7 +293,9 @@ parse_sub_cmdline(const int argc, char * const *argv) exit(EXIT_FAILURE); } - arg->num_seen++; + if (arg->num_seen != -1) /* accept more than one */ + arg->num_seen++; + if (arg->arg_name == NULL) { arg->callback(NULL); parse_sub_cmdline(argc-1, argv+1); @@ -319,9 +311,7 @@ parse_sub_cmdline(const int argc, char * const *argv) exit(EXIT_FAILURE); } -static void -parse_cmdline(const int argc, char * const *argv) -{ +static void parse_cmdline(const int argc, char * const *argv) { if (argc < 1) { /* Not enough args. */ usage(); @@ -330,58 +320,69 @@ parse_cmdline(const int argc, char * const *argv) parse_sub_cmdline(argc, argv); + if (opt_want_help) { + usage(); + exit(EXIT_SUCCESS); + } + /* start syslogging as early as possible */ - if (want_syslog) openlog("darkstat", LOG_NDELAY | LOG_PID, LOG_DAEMON); + if (opt_want_syslog) + openlog("darkstat", LOG_NDELAY | LOG_PID, LOG_DAEMON); /* some default values */ - if (chroot_dir == NULL) chroot_dir = CHROOT_DIR; - if (privdrop_user == NULL) privdrop_user = PRIVDROP_USER; + if (opt_chroot_dir == NULL) + opt_chroot_dir = CHROOT_DIR; + if (opt_privdrop_user == NULL) + opt_privdrop_user = PRIVDROP_USER; /* sanity check args */ - if ((interface == NULL) && (capfile == NULL)) + if (!opt_iface_seen && opt_capfile == NULL) errx(1, "must specify either interface (-i) or capture file (-r)"); - if ((interface != NULL) && (capfile != NULL)) + if (opt_iface_seen && opt_capfile != NULL) errx(1, "can't specify both interface (-i) and capture file (-r)"); - if ((hosts_max != 0) && (hosts_keep >= hosts_max)) { - hosts_keep = hosts_max / 2; + if ((opt_hosts_max != 0) && (opt_hosts_keep >= opt_hosts_max)) { + opt_hosts_keep = opt_hosts_max / 2; warnx("reducing --hosts-keep to %u, to be under --hosts-max (%u)", - hosts_keep, hosts_max); + opt_hosts_keep, opt_hosts_max); } verbosef("max %u hosts, cutting down to %u when exceeded", - hosts_max, hosts_keep); + opt_hosts_max, opt_hosts_keep); - if ((ports_max != 0) && (ports_keep >= ports_max)) { - ports_keep = ports_max / 2; + if ((opt_ports_max != 0) && (opt_ports_keep >= opt_ports_max)) { + opt_ports_keep = opt_ports_max / 2; warnx("reducing --ports-keep to %u, to be under --ports-max (%u)", - ports_keep, ports_max); + opt_ports_keep, opt_ports_max); } verbosef("max %u ports per host, cutting down to %u when exceeded", - ports_max, ports_keep); + opt_ports_max, opt_ports_keep); - if (want_hexdump && !want_verbose) { - want_verbose = 1; + if (opt_want_hexdump && !opt_want_verbose) { + opt_want_verbose = 1; verbosef("--hexdump implies --verbose"); } - if (want_hexdump && want_daemonize) { - want_daemonize = 0; + if (opt_want_hexdump && opt_want_daemonize) { + opt_want_daemonize = 0; verbosef("--hexdump implies --no-daemon"); } + + if (opt_want_local_only && !is_localnet_specified) + verbosef("WARNING: --local-only without -l only matches the local host"); } -static void -run_from_capfile(void) -{ +static void run_from_capfile(void) { + now_init(); graph_init(); hosts_db_init(); - cap_from_file(capfile, filter); - cap_stop(); + cap_from_file(opt_capfile); if (export_fn != NULL) db_export(export_fn); hosts_db_free(); graph_free(); - verbosef("Total packets: %qu, bytes: %qu", total_packets, total_bytes); + verbosef("Total packets: %llu, bytes: %llu", + (unsigned long long)acct_total_packets, + (unsigned long long)acct_total_bytes); } /* --- Program body --- */ @@ -391,20 +392,16 @@ main(int argc, char **argv) test_64order(); parse_cmdline(argc-1, argv+1); - if (capfile) { - /* - * This is very different from a regular run against a network - * interface. - */ + if (opt_capfile) { run_from_capfile(); return 0; } /* must verbosef() before first fork to init lock */ verbosef("starting up"); - if (pid_fn) pidfile_create(chroot_dir, pid_fn, privdrop_user); + if (pid_fn) pidfile_create(opt_chroot_dir, pid_fn, opt_privdrop_user); - if (want_daemonize) { + if (opt_want_daemonize) { verbosef("daemonizing to run in the background!"); daemonize_start(); verbosef("I am the main process"); @@ -412,20 +409,19 @@ main(int argc, char **argv) if (pid_fn) pidfile_write_close(); /* do this first as it forks - minimize memory use */ - if (want_dns) dns_init(privdrop_user); - cap_init(interface, filter, want_promisc); /* needs root */ - http_init(bindaddr, bindport, /*maxconn=*/ -1); /* low ports need root */ + if (opt_want_dns) dns_init(opt_privdrop_user); + cap_start(opt_want_promisc); /* needs root */ + http_listen(opt_bindport); ncache_init(); /* must do before chroot() */ - privdrop(chroot_dir, privdrop_user); + privdrop(opt_chroot_dir, opt_privdrop_user); /* Don't need root privs for these: */ - now = time(NULL); - if (daylog_fn != NULL) daylog_init(daylog_fn); + now_init(); + if (opt_daylog_fn != NULL) daylog_init(opt_daylog_fn); graph_init(); hosts_db_init(); if (import_fn != NULL) db_import(import_fn); - localip_init(interface); if (signal(SIGTERM, sig_shutdown) == SIG_ERR) errx(1, "signal(SIGTERM) failed"); @@ -442,57 +438,59 @@ main(int argc, char **argv) while (running) { int select_ret, max_fd = -1, use_timeout = 0; struct timeval timeout; + struct timespec t; fd_set rs, ws; - now = time(NULL); - - if (export_pending) { - if (export_fn != NULL) - db_export(export_fn); - export_pending = 0; - } - - if (reset_pending) { - hosts_db_reset(); - graph_reset(); - reset_pending = 0; - } - FD_ZERO(&rs); FD_ZERO(&ws); - cap_fd_set(&rs, &max_fd, &timeout, &use_timeout); http_fd_set(&rs, &ws, &max_fd, &timeout, &use_timeout); select_ret = select(max_fd+1, &rs, &ws, NULL, (use_timeout) ? &timeout : NULL); - - if ((select_ret == 0) && (!use_timeout)) + if (select_ret == 0 && !use_timeout) errx(1, "select() erroneously timed out"); - if (select_ret == -1) { if (errno == EINTR) continue; else err(1, "select()"); } - else { - graph_rotate(); - cap_poll(&rs); - dns_poll(); - http_poll(&rs, &ws); + + timer_start(&t); + now_update(); + + if (export_pending) { + if (export_fn != NULL) + db_export(export_fn); + export_pending = 0; } + + if (reset_pending) { + if (export_pending) + continue; /* export before reset */ + hosts_db_reset(); + graph_reset(); + reset_pending = 0; + } + + graph_rotate(); + cap_poll(&rs); + dns_poll(); + http_poll(&rs, &ws); + timer_stop(&t, 1000000000, "event processing took longer than a second"); } verbosef("shutting down"); verbosef("pcap stats: %u packets received, %u packets dropped", - pkts_recv, pkts_drop); + cap_pkts_recv, cap_pkts_drop); + http_stop(); cap_stop(); dns_stop(); if (export_fn != NULL) db_export(export_fn); hosts_db_free(); graph_free(); - if (daylog_fn != NULL) daylog_free(); + if (opt_daylog_fn != NULL) daylog_free(); ncache_free(); if (pid_fn) pidfile_unlink(); verbosef("shut down");