2 * copyright (c) 2001-2011 Emil Mikulic.
4 * darkstat.c: signals, cmdline parsing, program body.
6 * You may use, modify and redistribute this file under the terms of the
7 * GNU General Public License version 2. (see COPYING.GPL)
40 # define INADDR_NONE (-1) /* Solaris */
43 /* --- Signal handling --- */
44 static volatile int running
= 1;
45 static void sig_shutdown(int signum _unused_
) { running
= 0; }
47 static volatile int reset_pending
= 0, export_pending
= 0;
48 static void sig_reset(int signum _unused_
)
54 static void sig_export(int signum _unused_
) { export_pending
= 1; }
56 /* --- Commandline parsing --- */
58 parsenum(const char *str
, unsigned long max
/* 0 for no max */)
64 n
= strtoul(str
, &end
, 10);
66 errx(1, "\"%s\" is not a valid number", str
);
68 errx(1, "\"%s\" is out of range", str
);
69 if ((max
!= 0) && (n
> max
))
70 errx(1, "\"%s\" is out of range (max %lu)", str
, max
);
74 const char *opt_interface
= NULL
;
75 static void cb_interface(const char *arg
) { opt_interface
= arg
; }
77 const char *opt_capfile
= NULL
;
78 static void cb_capfile(const char *arg
) { opt_capfile
= arg
; }
80 int opt_want_snaplen
= -1;
81 static void cb_snaplen(const char *arg
)
82 { opt_want_snaplen
= (int)parsenum(arg
, 0); }
84 int opt_want_pppoe
= 0;
85 static void cb_pppoe(const char *arg _unused_
) { opt_want_pppoe
= 1; }
87 int opt_want_syslog
= 0;
88 static void cb_syslog(const char *arg _unused_
) { opt_want_syslog
= 1; }
90 int opt_want_verbose
= 0;
91 static void cb_verbose(const char *arg _unused_
) { opt_want_verbose
= 1; }
93 int opt_want_daemonize
= 1;
94 static void cb_no_daemon(const char *arg _unused_
) { opt_want_daemonize
= 0; }
96 int opt_want_promisc
= 1;
97 static void cb_no_promisc(const char *arg _unused_
) { opt_want_promisc
= 0; }
100 static void cb_no_dns(const char *arg _unused_
) { opt_want_dns
= 0; }
102 int opt_want_macs
= 1;
103 static void cb_no_macs(const char *arg _unused_
) { opt_want_macs
= 0; }
105 int opt_want_lastseen
= 1;
106 static void cb_no_lastseen(const char *arg _unused_
) { opt_want_lastseen
= 0; }
108 unsigned short opt_bindport
= 667;
109 static void cb_port(const char *arg
)
110 { opt_bindport
= (unsigned short)parsenum(arg
, 65536); }
112 static void cb_bindaddr(const char *arg
) { http_add_bindaddr(arg
); }
114 const char *opt_filter
= NULL
;
115 static void cb_filter(const char *arg
) { opt_filter
= arg
; }
117 static void cb_local(const char *arg
) { acct_init_localnet(arg
); }
119 const char *opt_chroot_dir
= NULL
;
120 static void cb_chroot(const char *arg
) { opt_chroot_dir
= arg
; }
122 const char *opt_privdrop_user
= NULL
;
123 static void cb_user(const char *arg
) { opt_privdrop_user
= arg
; }
125 const char *daylog_fn
= NULL
;
126 static void cb_daylog(const char *arg
)
128 if (opt_chroot_dir
== NULL
)
129 errx(1, "the daylog file is relative to the chroot.\n"
130 "You must specify a --chroot dir before you can use --daylog.");
135 const char *import_fn
= NULL
;
136 static void cb_import(const char *arg
)
138 if (opt_chroot_dir
== NULL
)
139 errx(1, "the import file is relative to the chroot.\n"
140 "You must specify a --chroot dir before you can use --import.");
145 const char *export_fn
= NULL
;
146 static void cb_export(const char *arg
)
148 if ((opt_chroot_dir
== NULL
) && (opt_capfile
== NULL
))
149 errx(1, "the export file is relative to the chroot.\n"
150 "You must specify a --chroot dir before you can use --export.");
155 static const char *pid_fn
= NULL
;
156 static void cb_pidfile(const char *arg
)
158 if (opt_chroot_dir
== NULL
)
159 errx(1, "the pidfile is relative to the chroot.\n"
160 "You must specify a --chroot dir before you can use --pidfile.");
165 unsigned int opt_hosts_max
= 1000;
166 static void cb_hosts_max(const char *arg
)
167 { opt_hosts_max
= parsenum(arg
, 0); }
169 unsigned int opt_hosts_keep
= 500;
170 static void cb_hosts_keep(const char *arg
)
171 { opt_hosts_keep
= parsenum(arg
, 0); }
173 unsigned int opt_ports_max
= 200;
174 static void cb_ports_max(const char *arg
)
175 { opt_ports_max
= parsenum(arg
, 65536); }
177 unsigned int opt_ports_keep
= 30;
178 static void cb_ports_keep(const char *arg
)
179 { opt_ports_keep
= parsenum(arg
, 65536); }
181 unsigned int opt_highest_port
= 65535;
182 static void cb_highest_port(const char *arg
)
183 { opt_highest_port
= parsenum(arg
, 65535); }
185 int opt_wait_secs
= -1;
186 static void cb_wait_secs(const char *arg
)
187 { opt_wait_secs
= (int)parsenum(arg
, 0); }
189 int opt_want_hexdump
= 0;
190 static void cb_hexdump(const char *arg _unused_
) { opt_want_hexdump
= 1; }
195 const char *name
, *arg_name
; /* NULL arg_name means unary */
196 void (*callback
)(const char *arg
);
200 static struct cmdline_arg cmdline_args
[] = {
201 {"-i", "interface", cb_interface
, 0},
202 {"-r", "file", cb_capfile
, 0},
203 {"--snaplen", "bytes", cb_snaplen
, 0},
204 {"--pppoe", NULL
, cb_pppoe
, 0},
205 {"--syslog", NULL
, cb_syslog
, 0},
206 {"--verbose", NULL
, cb_verbose
, 0},
207 {"--no-daemon", NULL
, cb_no_daemon
, 0},
208 {"--no-promisc", NULL
, cb_no_promisc
, 0},
209 {"--no-dns", NULL
, cb_no_dns
, 0},
210 {"--no-macs", NULL
, cb_no_macs
, 0},
211 {"--no-lastseen", NULL
, cb_no_lastseen
, 0},
212 {"-p", "port", cb_port
, 0},
213 {"-b", "bindaddr", cb_bindaddr
, -1},
214 {"-f", "filter", cb_filter
, 0},
215 {"-l", "network/netmask", cb_local
, 0},
216 {"--chroot", "dir", cb_chroot
, 0},
217 {"--user", "username", cb_user
, 0},
218 {"--daylog", "filename", cb_daylog
, 0},
219 {"--import", "filename", cb_import
, 0},
220 {"--export", "filename", cb_export
, 0},
221 {"--pidfile", "filename", cb_pidfile
, 0},
222 {"--hosts-max", "count", cb_hosts_max
, 0},
223 {"--hosts-keep", "count", cb_hosts_keep
, 0},
224 {"--ports-max", "count", cb_ports_max
, 0},
225 {"--ports-keep", "count", cb_ports_keep
, 0},
226 {"--highest-port", "port", cb_highest_port
, 0},
227 {"--wait", "secs", cb_wait_secs
, 0},
228 {"--hexdump", NULL
, cb_hexdump
, 0},
229 {NULL
, NULL
, NULL
, 0}
236 for (i
=0; i
<width
; i
++) printf(" ");
240 * We autogenerate the usage statement from the cmdline_args data structure.
246 struct cmdline_arg
*arg
;
248 printf(PACKAGE_STRING
" (built with libpcap %d.%d)\n\n",
249 PCAP_VERSION_MAJOR
, PCAP_VERSION_MINOR
);
251 width
= printf("usage: darkstat ");
254 for (arg
= cmdline_args
; arg
->name
!= NULL
; arg
++) {
255 if (first
) first
= 0; else pad(width
);
256 printf("[ %s", arg
->name
);
257 if (arg
->arg_name
!= NULL
) printf(" %s", arg
->arg_name
);
261 "Please refer to the darkstat(8) manual page for further\n"
262 "documentation and usage examples.\n");
266 parse_sub_cmdline(const int argc
, char * const *argv
)
268 struct cmdline_arg
*arg
;
270 if (argc
== 0) return;
271 for (arg
= cmdline_args
; arg
->name
!= NULL
; arg
++)
272 if (strcmp(argv
[0], arg
->name
) == 0) {
273 if ((arg
->arg_name
!= NULL
) && (argc
== 1)) {
275 "error: argument \"%s\" requires parameter \"%s\"\n",
276 arg
->name
, arg
->arg_name
);
280 if (arg
->num_seen
> 0) {
282 "error: already specified argument \"%s\"\n",
288 if (arg
->num_seen
!= -1) /* accept more than one */
291 if (arg
->arg_name
== NULL
) {
293 parse_sub_cmdline(argc
-1, argv
+1);
295 arg
->callback(argv
[1]);
296 parse_sub_cmdline(argc
-2, argv
+2);
301 fprintf(stderr
, "error: illegal argument: \"%s\"\n", argv
[0]);
307 parse_cmdline(const int argc
, char * const *argv
)
310 /* Not enough args. */
315 parse_sub_cmdline(argc
, argv
);
317 /* start syslogging as early as possible */
318 if (opt_want_syslog
) openlog("darkstat", LOG_NDELAY
| LOG_PID
, LOG_DAEMON
);
320 /* some default values */
321 if (opt_chroot_dir
== NULL
) opt_chroot_dir
= CHROOT_DIR
;
322 if (opt_privdrop_user
== NULL
) opt_privdrop_user
= PRIVDROP_USER
;
324 /* sanity check args */
325 if ((opt_interface
== NULL
) && (opt_capfile
== NULL
))
326 errx(1, "must specify either interface (-i) or capture file (-r)");
328 if ((opt_interface
!= NULL
) && (opt_capfile
!= NULL
))
329 errx(1, "can't specify both interface (-i) and capture file (-r)");
331 if ((opt_hosts_max
!= 0) && (opt_hosts_keep
>= opt_hosts_max
)) {
332 opt_hosts_keep
= opt_hosts_max
/ 2;
333 warnx("reducing --hosts-keep to %u, to be under --hosts-max (%u)",
334 opt_hosts_keep
, opt_hosts_max
);
336 verbosef("max %u hosts, cutting down to %u when exceeded",
337 opt_hosts_max
, opt_hosts_keep
);
339 if ((opt_ports_max
!= 0) && (opt_ports_keep
>= opt_ports_max
)) {
340 opt_ports_keep
= opt_ports_max
/ 2;
341 warnx("reducing --ports-keep to %u, to be under --ports-max (%u)",
342 opt_ports_keep
, opt_ports_max
);
344 verbosef("max %u ports per host, cutting down to %u when exceeded",
345 opt_ports_max
, opt_ports_keep
);
347 if (opt_want_hexdump
&& !opt_want_verbose
) {
348 opt_want_verbose
= 1;
349 verbosef("--hexdump implies --verbose");
352 if (opt_want_hexdump
&& opt_want_daemonize
) {
353 opt_want_daemonize
= 0;
354 verbosef("--hexdump implies --no-daemon");
359 run_from_capfile(void)
363 cap_from_file(opt_capfile
, opt_filter
);
365 if (export_fn
!= NULL
) db_export(export_fn
);
369 #warning "PRIu64 is not defined, using qu instead"
372 verbosef("Total packets: %"PRIu64
", bytes: %"PRIu64
,
373 acct_total_packets
, acct_total_bytes
);
376 /* --- Program body --- */
378 main(int argc
, char **argv
)
381 parse_cmdline(argc
-1, argv
+1);
385 * This is very different from a regular run against a network
392 /* must verbosef() before first fork to init lock */
393 verbosef("starting up");
394 if (pid_fn
) pidfile_create(opt_chroot_dir
, pid_fn
, opt_privdrop_user
);
396 if (opt_want_daemonize
) {
397 verbosef("daemonizing to run in the background!");
399 verbosef("I am the main process");
401 if (pid_fn
) pidfile_write_close();
403 /* do this first as it forks - minimize memory use */
404 if (opt_want_dns
) dns_init(opt_privdrop_user
);
405 cap_init(opt_interface
, opt_filter
, opt_want_promisc
); /* needs root */
406 http_listen(opt_bindport
);
407 ncache_init(); /* must do before chroot() */
409 privdrop(opt_chroot_dir
, opt_privdrop_user
);
411 /* Don't need root privs for these: */
413 if (daylog_fn
!= NULL
) daylog_init(daylog_fn
);
416 if (import_fn
!= NULL
) db_import(import_fn
);
417 localip_init(opt_interface
);
419 if (signal(SIGTERM
, sig_shutdown
) == SIG_ERR
)
420 errx(1, "signal(SIGTERM) failed");
421 if (signal(SIGINT
, sig_shutdown
) == SIG_ERR
)
422 errx(1, "signal(SIGINT) failed");
423 if (signal(SIGUSR1
, sig_reset
) == SIG_ERR
)
424 errx(1, "signal(SIGUSR1) failed");
425 if (signal(SIGUSR2
, sig_export
) == SIG_ERR
)
426 errx(1, "signal(SIGUSR2) failed");
428 verbosef("entering main loop");
432 int select_ret
, max_fd
= -1, use_timeout
= 0;
433 struct timeval timeout
;
438 if (export_pending
) {
439 if (export_fn
!= NULL
)
440 db_export(export_fn
);
453 cap_fd_set(&rs
, &max_fd
, &timeout
, &use_timeout
);
454 http_fd_set(&rs
, &ws
, &max_fd
, &timeout
, &use_timeout
);
456 select_ret
= select(max_fd
+1, &rs
, &ws
, NULL
,
457 (use_timeout
) ? &timeout
: NULL
);
459 if ((select_ret
== 0) && (!use_timeout
))
460 errx(1, "select() erroneously timed out");
462 if (select_ret
== -1) {
476 verbosef("shutting down");
477 verbosef("pcap stats: %u packets received, %u packets dropped",
478 cap_pkts_recv
, cap_pkts_drop
);
482 if (export_fn
!= NULL
) db_export(export_fn
);
485 if (daylog_fn
!= NULL
) daylog_free();
487 if (pid_fn
) pidfile_unlink();
488 verbosef("shut down");
489 return (EXIT_SUCCESS
);
492 /* vim:set ts=3 sw=3 tw=78 expandtab: */