+v3.0.718 (25 January 2014)
+ - (SECURITY!) Don't chroot() by default. The user must specify
+ a --chroot dir for this to happen now.
+ - Bring back the "--base /path" functionality.
+ - Add explicit warning about graphs being blank if we can't get
+ local IPs on an interface.
+ - Don't crash in timer_stop() if monotonic time stops or goes
+ backwards.
+ - Lots of internal cleanups.
+ - Use time_t instead of "long" for time. This is more correct
+ and should fix darkstat on OpenBSD 5.5 on 32-bit systems.
+
v3.0.717 (14 August 2013)
- (OS X only) Work around lack of clock_gettime().
- Fix crash due to str_appendf() not understanding %ld.
- Allow sort on last-seen, thanks to Dirk Koopman.
- Support multiple bind addresses.
- Add --disable-debug configure flag, thanks to Malte S. Stretz.
- - Make it possible to save the DB without resetting it (SIGUSR2).
- - Web: Use relative URLs, so darkstat works properly behind mod_proxy,
- thanks to Malte S. Stretz.
+ - Make it possible to export the database without resetting it:
+ by sending SIGUSR2.
+ - Web: Use relative URLs, so darkstat works properly
+ behind mod_proxy, thanks to Malte S. Stretz.
v3.0.713 (March 2010)
- Don't require --verbose for pcap_stats.
- Survive interface going down on Linux.
- Support DLT_RAW, implemented by Anton S. Ustyuzhanin.
- - Skip accounting for hosts or ports if their max is set to zero.
+ - Skip accounting for hosts or ports if their max
+ is set to zero.
- Implement --hexdump for troubleshooting.
- Web: Implement --no-lastseen
- Implement --snaplen manual override.
v2.6 (Nov 2003)
- End of the line for darkstat 2
+ End of the line for darkstat 2.
vim:set noet ts=8 sw=8 tw=72:
# darkstat 3
-# copyright (c) 2001-2011 Emil Mikulic.
+# copyright (c) 2001-2014 Emil Mikulic.
#
# You may use, modify and redistribute this file under the terms of the
# GNU General Public License version 2. (see COPYING.GPL)
hosts_db.h localip.h now.h opt.h queue.h str.h
conv.o: conv.c conv.h err.h cdefs.h
darkstat.o: darkstat.c acct.h cap.h cdefs.h config.h conv.h daylog.h \
- graph_db.h db.h dns.h err.h http.h hosts_db.h addr.h localip.h ncache.h \
- now.h pidfile.h
-daylog.o: daylog.c err.h cdefs.h daylog.h graph_db.h str.h now.h
+ graph_db.h db.h dns.h err.h hosts_db.h addr.h http.h localip.h ncache.h \
+ now.h pidfile.h str.h
+daylog.o: daylog.c cdefs.h err.h daylog.h graph_db.h str.h now.h
db.o: db.c cdefs.h err.h hosts_db.h addr.h graph_db.h db.h
decode.o: decode.c cdefs.h decode.h addr.h err.h opt.h
dns.o: dns.c cdefs.h conv.h decode.h addr.h dns.h err.h hosts_db.h \
hosts_db.o: hosts_db.c cdefs.h conv.h decode.h addr.h dns.h err.h \
hosts_db.h db.h html.h ncache.h now.h opt.h str.h
hosts_sort.o: hosts_sort.c cdefs.h err.h hosts_db.h addr.h
-html.o: html.c config.h str.h html.h opt.h
+html.o: html.c config.h str.h cdefs.h html.h opt.h
http.o: http.c cdefs.h config.h conv.h err.h graph_db.h hosts_db.h addr.h \
http.h now.h queue.h str.h stylecss.h graphjs.h
localip.o: localip.c addr.h bsd.h config.h conv.h err.h cdefs.h localip.h \
now.h
ncache.o: ncache.c conv.h err.h cdefs.h ncache.h tree.h bsd.h config.h
-now.o: now.c err.h cdefs.h now.h
+now.o: now.c err.h cdefs.h now.h str.h
pidfile.o: pidfile.c err.h cdefs.h str.h pidfile.h
str.o: str.c conv.h err.h cdefs.h str.h
Changes to defaults, most recent first:
+- After v3.0.717, the user must specify a --chroot dir for chroot() to happen.
+ We don't set a default in the configure script anymore.
+
- After v3.0.708, --debug was split into --verbose and --no-daemon.
- Since v3.0.694, darkstat is able to save its internal database into a file and
terminal, and run in the background.
After 540, the default has been inverted. darkstat will daemonize by default.
+
+vim:set noet ts=8 sw=8 tw=80:
/* darkstat 3
- * copyright (c) 2001-2011 Emil Mikulic.
+ * copyright (c) 2001-2012 Emil Mikulic.
*
* acct.c: traffic accounting
*
/* darkstat 3
- * copyright (c) 2001-2011 Emil Mikulic.
+ * copyright (c) 2001-2012 Emil Mikulic.
*
* acct.h: traffic accounting
*/
/* darkstat 3
- * copyright (c) 2001-2011 Emil Mikulic.
+ * copyright (c) 2011 Emil Mikulic.
*
* bsd.c: *BSD compatibility.
*
/* darkstat 3
- * copyright (c) 2001-2011 Emil Mikulic.
+ * copyright (c) 2011-2014 Emil Mikulic.
*
* bsd.h: *BSD compatibility.
*/
#endif
#ifndef HAVE_SETPROCTITLE
-#define setproctitle(fmt, ...) /* no-op */
+#define setproctitle(fmt) /* no-op */
#endif
/* vim:set ts=3 sw=3 tw=78 expandtab: */
/* darkstat 3
- * copyright (c) 2001-2011 Emil Mikulic.
+ * copyright (c) 2001-2014 Emil Mikulic.
*
* cap.c: capture packets, and hand them off to decode and acct.
*
/* Process any packets currently in the capture buffer. */
void cap_poll(fd_set *read_set _unused_on_linux_) {
struct cap_iface *iface;
+ static int told = 0;
STAILQ_FOREACH(iface, &cap_ifs, entries) {
/* Once per capture poll, check our IP address. It's used in accounting
* for traffic graphs.
*/
localip_update(iface->name, &iface->local_ips);
+ if (!told && iface->local_ips.num_addrs == 0) {
+ verbosef("interface '%s' has no addresses, "
+ "your graphs will be blank",
+ iface->name);
+ verbosef("please read the darkstat manpage, "
+ "and consider using the -l option");
+ told = 1;
+ }
for (;;) {
struct timespec t;
-1, /* count = entire buffer */
callback,
(u_char*)iface); /* user = struct to pass to callback */
+ timer_stop(&t,
+ 2 * CAP_TIMEOUT_MSEC * 1000000,
+ "pcap_dispatch took too long");
if (ret < 0) {
warnx("pcap_dispatch('%s'): %s",
iface->name, pcap_geterr(iface->pcap));
continue;
}
- timer_stop(&t,
- 2 * CAP_TIMEOUT_MSEC * 1000000,
- "pcap_dispatch took too long");
- if (0) /* debugging */
- verbosef("iface '%s' got %d pkts", iface->name, ret);
+#if 0 /* debugging */
+ verbosef("iface '%s' got %d pkts", iface->name, ret);
+#endif
#ifdef linux
/* keep looping until we've dispatched all the outstanding packets */
/* darkstat 3
- * copyright (c) 2001-2011 Emil Mikulic.
+ * copyright (c) 2001-2012 Emil Mikulic.
*
* cap.h: interface to libpcap.
*/
/* darkstat 3
- * copyright (c) 2001-2011 Emil Mikulic.
+ * copyright (c) 2001-2014 Emil Mikulic.
*
* cdefs.h: compiler-specific defines
*
#endif
#if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901
-#define restrict __restrict
+# define restrict __restrict
#endif
#ifndef MAX
# define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
+#if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 201112L
+# ifdef __COUNTER__
+# define _Static_assert(x, y) __Static_assert(x, __COUNTER__)
+# else
+# define _Static_assert(x, y) __Static_assert(x, __LINE__)
+# endif
+# define __Static_assert(x, y) ___Static_assert(x, y)
+# define ___Static_assert(x, y) typedef char __assert_ ## y[(x) ? 1 : -1]
+#endif
+
/* vim:set ts=3 sw=3 tw=78 expandtab: */
/* config.h.in. Generated from configure.ac by autoheader. */
-/* Default chroot directory. */
-#undef CHROOT_DIR
-
/* Define to 1 if you have the <bsd/string.h> header file. */
#undef HAVE_BSD_STRING_H
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for darkstat 3.0.717.
+# Generated by GNU Autoconf 2.69 for darkstat 3.0.718.
#
#
# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
# Identity of this package.
PACKAGE_NAME='darkstat'
PACKAGE_TARNAME='darkstat'
-PACKAGE_VERSION='3.0.717'
-PACKAGE_STRING='darkstat 3.0.717'
+PACKAGE_VERSION='3.0.718'
+PACKAGE_STRING='darkstat 3.0.718'
PACKAGE_BUGREPORT=''
PACKAGE_URL='http://unix4lyfe.org/darkstat/'
ac_subst_files=''
ac_user_opts='
enable_option_checking
-with_chroot_dir
with_privdrop_user
enable_silent_rules
enable_debug
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures darkstat 3.0.717 to adapt to many kinds of systems.
+\`configure' configures darkstat 3.0.718 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of darkstat 3.0.717:";;
+ short | recursive ) echo "Configuration of darkstat 3.0.718:";;
esac
cat <<\_ACEOF
Optional Packages:
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
--without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
- --with-chroot-dir specify the chroot directory (default: /var/empty)
--with-privdrop-user specify which user to drop privileges to (default:
nobody)
--with-pcap=DIR prefix to libpcap installation
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-darkstat configure 3.0.717
+darkstat configure 3.0.718
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by darkstat $as_me 3.0.717, which was
+It was created by darkstat $as_me 3.0.718, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
RULE="------------------------------------------------------------"
-# Let user specify CHROOT_DIR, or else autodetect it, or else die.
-
-# Check whether --with-chroot-dir was given.
-if test "${with_chroot_dir+set}" = set; then :
- withval=$with_chroot_dir; if test "x$withval" = "xyes"; then
- as_fn_error $? "please specify --with-chroot-dir=/path/to/darkstat/chroot" "$LINENO" 5
- fi
- _chd="$withval"
-else
- # Find an "empty" directory to serve as the chroot.
- _chd="/var/empty"
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $_chd" >&5
-$as_echo_n "checking for $_chd... " >&6; }
- if test -d $_chd ; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: found it" >&5
-$as_echo "found it" >&6; }
- else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: not there" >&5
-$as_echo "not there" >&6; }
- _chd="/var/lib/empty"
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $_chd" >&5
-$as_echo_n "checking for $_chd... " >&6; }
- if test -d $_chd ; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: found it" >&5
-$as_echo "found it" >&6; }
- else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: not there either" >&5
-$as_echo "not there either" >&6; }
- as_fn_error $? "please specify --with-chroot-dir=/path/to/darkstat/chroot" "$LINENO" 5
- fi
- fi
-fi
-
-
-cat >>confdefs.h <<_ACEOF
-#define CHROOT_DIR "$_chd"
-_ACEOF
-
-
# Allow configure-time override of PRIVDROP_USER.
# Check whether --with-privdrop-user was given.
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by darkstat $as_me 3.0.717, which was
+This file was extended by darkstat $as_me 3.0.718, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-darkstat config.status 3.0.717
+darkstat config.status 3.0.718
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
# Need at least 2.64 for PACKAGE_URL
AC_PREREQ([2.64])
-AC_INIT(darkstat, 3.0.717, , , http://unix4lyfe.org/darkstat/)
+AC_INIT(darkstat, 3.0.718, , , http://unix4lyfe.org/darkstat/)
AC_CONFIG_SRCDIR([darkstat.c])
AC_CONFIG_HEADER([config.h])
RULE="------------------------------------------------------------"
-# Let user specify CHROOT_DIR, or else autodetect it, or else die.
-AC_ARG_WITH(chroot-dir, AS_HELP_STRING([--with-chroot-dir],
- [specify the chroot directory (default: /var/empty)]),
- [if test "x$withval" = "xyes"; then
- AC_MSG_ERROR([please specify --with-chroot-dir=/path/to/darkstat/chroot])
- fi
- _chd="$withval"],
- [# Find an "empty" directory to serve as the chroot.
- _chd="/var/empty"
- AC_MSG_CHECKING([for $_chd])
- if test -d $_chd ; then
- AC_MSG_RESULT(found it)
- else
- AC_MSG_RESULT(not there)
- _chd="/var/lib/empty"
- AC_MSG_CHECKING([for $_chd])
- if test -d $_chd ; then
- AC_MSG_RESULT(found it)
- else
- AC_MSG_RESULT(not there either)
- AC_MSG_ERROR([please specify --with-chroot-dir=/path/to/darkstat/chroot])
- fi
- fi])
-AC_DEFINE_UNQUOTED(CHROOT_DIR, "$_chd", [Default chroot directory.])
-
# Allow configure-time override of PRIVDROP_USER.
AC_ARG_WITH(privdrop-user, AS_HELP_STRING([--with-privdrop-user],
[specify which user to drop privileges to (default: nobody)]),
--- /dev/null
+#!/bin/sh
+
+# Copyright 2013 MediaMobil Communication GmbH
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# This script converts a binary .db file into a .csv file.
+# The .db file was generated by darkstat with the --export option.
+# The .csv file shall be read by any spreadsheet application.
+SCRIPTNAME=$( basename $0)
+if test -z "$( type -P awk )" ; then
+ echo "${SCRIPTNAME}: missing AWK interpreter, at least not found in PATH"
+ echo "${SCRIPTNAME}: every POSIX compliant OS has one; add the location to PATH"
+ exit 1
+fi
+if test -z "$( type -P od )" ; then
+ echo "${SCRIPTNAME}: missing od file dump tool, at least not found in PATH"
+ echo "${SCRIPTNAME}: every POSIX compliant OS has one; add the location to PATH"
+ exit 1
+fi
+if test $# -ne 1; then
+ echo "${SCRIPTNAME}: missing parameter; need file name of .db file"
+ exit 1
+fi
+DBFILENAME=$1
+if test -r ${DBFILENAME}; then
+ echo ${SCRIPTNAME}: Found file ${DBFILENAME}
+else
+ echo ${SCRIPTNAME}: file ${DBFILENAME} does not exist
+ exit 1
+fi
+CSVFILENAME=${DBFILENAME%%.*}.csv
+echo ${SCRIPTNAME}: Writing output into ${CSVFILENAME}
+
+# The spec of the .db export format exists for different versions:
+# http://unix4lyfe.org/gitweb/darkstat/blob/0a152e51f5d9c1771308caa7135d363a722aee18:/export-format.txt
+# http://git.msquadrat.de/darkstat.git/blob_plain/master:/export-format.txt
+# http://phil.lavin.me.uk/downloads/parse.phps
+# Only file format version 1 is supported by us.
+# Obviously, darkstat itself distinguishes 3 different host format versions.
+# Only host format version 2 is supported by us.
+# The darkstat database file is converted from binary format
+# to ASCII by the standard Unix command od.
+
+# Some things don't work correctly yet.
+# Probably because there is no DNS server configured in our embedded device
+# that produces .db files within OpenWRT.
+# - host name contains nonsense at constant length 5
+# - "last seen" timing information contains always 0
+# - we read the graphics section of the file but ignore it
+
+# Let the od tool convert each binary byte into several textual formats.
+# The AWK script reads all variants and later picks the format it needs.
+od -Ad -v -tx1 -tu1 -ta -w1 < ${DBFILENAME} |
+awk '
+ NF==2 { addr = 0 + $1; hex[addr] = $2; next }
+ NF==1 && addr in dec { ascii[addr]=$1; next }
+ NF==1 && ! (addr in dec) { dec[addr]=$1; next }
+ # Now all variants of the bytes are available in certain arrays.
+ # The array indices cover the range 0 .. addr.
+
+ function read_bytes(array, address, count, retval, c) {
+ retval=""
+ for (c=0; c<count; c++)
+ retval = retval array[address+c]
+ return retval
+ }
+ function read_number(address, count, retval, c) {
+ retval=0
+ for (c=0; c<count; c++)
+ retval = retval*256 + dec[address+c]
+ return retval
+ }
+ function read_text(address, count, retval, c) {
+ retval=""
+ for (c=0; c<count; c++)
+ retval = retval ascii[address+c]
+ return retval
+ }
+ function quit(reason, terminate, retval) {
+ if (length(reason) > 0)
+ print reason
+ if (terminate != 0) {
+ # Any remaining bytes in the file shall be dumped.
+ for (i=ai; i<=addr; i++)
+ print i, hex[i], ascii[i]
+ exit(retval)
+ }
+ }
+ function readIPsection() {
+ ip_protos_data=read_bytes(ascii, ai, 1)
+ if (ip_protos_data != "P")
+ quit("expected ip_protos_data P, found " ip_protos_data, 1, 1)
+ ai += 1
+ ip_proto_count=read_number(ai, 1)
+ ai += 1
+ for (pi=0; pi<ip_proto_count; pi++) {
+ ip_proto_type=read_number(ai, 1)
+ ai += 1
+ IPprotos = IPprotos " " ip_proto_type
+ ip_proto_in += read_number(ai, 8)
+ ai += 8
+ ip_proto_out += read_number(ai, 8)
+ ai += 8
+ }
+ }
+ function readTCPsection() {
+ tcp_protos_data=read_bytes(ascii, ai, 1)
+ if (tcp_protos_data != "T")
+ quit("expected tcp_protos_data T, found " tcp_protos_data, 1, 1)
+ ai += 1
+ tcp_proto_count=read_number(ai, 2)
+ ai += 2
+ for (ti=0; ti<tcp_proto_count; ti++) {
+ tcp_proto_port=read_number(ai, 2)
+ ai += 2
+ TCPports = TCPports " " tcp_proto_port
+ tcp_proto_syn=read_number(ai, 8)
+ ai += 8
+ tcp_proto_in += read_number(ai, 8)
+ ai += 8
+ tcp_proto_out += read_number(ai, 8)
+ ai += 8
+ if (tcp_proto_port == 22) {
+ ssh_in += tcp_proto_in
+ ssh_out += tcp_proto_out
+ }
+ if (tcp_proto_port == 3389) {
+ rdp_in += tcp_proto_in
+ rdp_out += tcp_proto_out
+ }
+ }
+ }
+ function readUDPsection() {
+ udp_protos_data=read_bytes(ascii, ai, 1)
+ if (udp_protos_data != "U")
+ quit("expected udp_protos_data U, found " udp_protos_data, 1, 1)
+ ai += 1
+ udp_proto_count=read_number(ai, 2)
+ ai += 2
+ for (ui=0; ui<udp_proto_count; ui++) {
+ udp_proto_port=read_number(ai, 2)
+ ai += 2
+ UDPports = UDPports " " udp_proto_port
+ udp_proto_in += read_number(ai, 8)
+ ai += 8
+ udp_proto_out += read_number(ai, 8)
+ ai += 8
+ if (udp_proto_port == 22) {
+ ssh_in += udp_proto_in
+ ssh_out += udp_proto_out
+ }
+ if (udp_proto_port == 3389) {
+ rdp_in += udp_proto_in
+ rdp_out += udp_proto_out
+ }
+ }
+ }
+ function readGraphsection(interval) {
+ n_bars=read_number(ai++, 1)
+ i_bars=read_number(ai++, 1)
+ for (bi=0; bi<n_bars; bi++) {
+ graph_bytes_in=read_number(ai, 8)
+ ai += 8
+ graph_bytes_out=read_number(ai, 8)
+ ai += 8
+ }
+ }
+
+ END {
+ file_header=read_bytes(hex, 0, 4)
+ if (file_header != "da314159")
+ quit("input data is not an exported darkstat .db file, wrong header: " file_header, 1, 1)
+ section_header=read_bytes(hex, 4, 3)
+ if (section_header != "da4853")
+ quit("section header da4853 expected: " section_header, 1, 1)
+ db_version=read_bytes(hex, 7, 1)
+ if (db_version != "01")
+ quit("file format supported only in version 01", 1, 1)
+ host_count=read_number(8, 4)
+ ai=12
+ # Print a header into the .csv file.
+ printf("IP address;MAC address;host in bytes;host out bytes;IP protos;IP in bytes;IP out bytes;TCP port count;TCP in bytes;TCP out bytes;UDP port count;UDP in bytes;UDP out bytes;ssh in bytes;ssh out bytes;rdp in bytes;rdp out bytes;TCP ports;UDP ports\n")
+ for (hi=1; hi<=host_count; hi++) {
+ # Make sure all variables to be printed are initially empty.
+ ip_address=mac_address=""
+ host_bytes_in=host_bytes_out=ip_proto_in=ip_proto_out=tcp_proto_in=tcp_proto_out=udp_proto_in=udp_proto_out=ssh_in=ssh_out=rdp_in=rdp_out=0
+ IPprotos=TCPports=UDPports=""
+ tcp_proto_count=udp_proto_count=0
+ host_header=read_bytes(hex, ai, 3)
+ host_version=read_bytes(hex, ai+3, 1)
+ ai += 4
+ if (host_version == "02") {
+ ip_address=read_number(ai+0,1) "." read_number(ai+1,1) "." read_number(ai+2,1) "." read_number(ai+3,1)
+ ai += 4
+ if ((host_version+0) > 1) {
+ last_seen=read_number(ai, 4)
+ # This value is always 0 in our files.
+ ai += 4
+ }
+ mac_address=hex[ai+0] ":" hex[ai+1] ":" hex[ai+2] ":" hex[ai+3] ":" hex[ai+4] ":" hex[ai+5]
+ ai += 6
+ # Weird stuff: the host name should be read.
+ # But there are only 5 bytes of nonsense.
+ # The first byte should be the length counter, but it isnt.
+ # The last byte is in fact a 0 byte.
+ # Probably caused by the missing DNS server.
+ # ignore 5 bytes with nonsense
+ nonsense=read_text(ai, 5)
+ ai += 5
+ host_bytes_in=read_number(ai, 8)
+ ai += 8
+ host_bytes_out=read_number(ai, 8)
+ ai += 8
+ readIPsection()
+ readTCPsection()
+ readUDPsection()
+ } else {
+ quit("host format supported only in version 02: " host_version, 1, 1)
+ #address_familiy=read_bytes(hex, ai, 1)
+ #print "address familiy = " address_familiy
+ }
+ printf("\"%s\";\"%s\";%d;%d;%s;%d;%d;%d;%d;%d;%d;%d;%d;%d;%d;%d;%d;%s;%s\n",
+ ip_address, mac_address, host_bytes_in, host_bytes_out,
+ IPprotos, ip_proto_in, ip_proto_out,
+ tcp_proto_count, tcp_proto_in, tcp_proto_out,
+ udp_proto_count, udp_proto_in, udp_proto_out,
+ ssh_in, ssh_out, rdp_in, rdp_out,
+ TCPports, UDPports)
+ }
+ section_header=read_bytes(hex, ai, 3)
+ if (section_header != "da4752")
+ quit("section header da4752 expected: " section_header, 1, 1)
+ ai += 3
+ db_version=read_bytes(hex, ai, 1)
+ if (db_version != "01")
+ quit("file format supported only in version 01", 1, 1)
+ ai += 1
+ last_time=read_number(ai, 8)
+ ai += 8
+ readGraphsection("60 seconds")
+ readGraphsection("60 minutes")
+ readGraphsection("24 hours")
+ readGraphsection("31 days")
+ # The complete file has been parsed, no bytes should be left over.
+ # Terminate with return value 0 if the byte numbers match.
+ quit("", (addr != ai+1) ?0:1, addr != ai+1)
+ }
+ ' > ${CSVFILENAME}
/* darkstat 3
- * copyright (c) 2001-2011 Emil Mikulic.
+ * copyright (c) 2001-2014 Emil Mikulic.
*
* conv.c: convenience functions.
*
* For security, chroot (optionally) and drop privileges.
* Pass a NULL chroot_dir to disable chroot() behaviour.
*/
-void
-privdrop(const char *chroot_dir, const char *privdrop_user)
-{
+void privdrop(const char *chroot_dir, const char *privdrop_user) {
struct passwd *pw;
errno = 0;
else
err(1, "getpwnam(\"%s\") failed", privdrop_user);
}
- if (chroot_dir != NULL) {
+ if (chroot_dir == NULL) {
+ verbosef("no --chroot dir specified, darkstat will not chroot()");
+ } else {
tzset(); /* read /etc/localtime before we chroot */
if (chdir(chroot_dir) == -1)
err(1, "chdir(\"%s\") failed", chroot_dir);
err(1, "fcntl(fd %d) to unset O_NONBLOCK", fd);
assert( (fcntl(fd, F_GETFL, 0) & O_NONBLOCK ) == 0 );
}
+
+/* vim:set ts=3 sw=3 tw=78 expandtab: */
.\"
.\" darkstat 3
-.\" Copyright 2001-2011, Emil Mikulic.
+.\" Copyright 2001-2014, Emil Mikulic.
.\"
.\" You may use, modify and redistribute this file under the terms of the
.\" GNU General Public License version 2. (see COPYING.GPL)
] [
.BI \-b " bindaddr"
] [
+.BI \-\-base " path"
+] [
.BI \-f " filter"
] [
.BI \-l " network/netmask"
The default is to listen on all interfaces.
.\"
.TP
+.BI \-\-base " path"
+.RS
+Specify the path of the base URL.
+This can be useful if \fIdarkstat\fR is accessed via a reverse proxy.
+
+For example, if you use Apache's \fImod_proxy\fR and want to avoid a
+complicated setup with \fImod_proxy_html\fR (and \fImod_header\fR to unset
+the \fIAccept-Encoding\fR header), just set the base path to something like
+\fIstats\fR and use a config similar to the following snippet:
+
+.IP
+ ProxyPass /stats/ http://localhost:667/stats/
+ ProxyPassReverse /stats/ http://localhost:667/stats/
+.PP
+
+The default is \fI/\fR (i.e. the root).
+.RE
+.\"
+.TP
.BI \-f " filter"
Use the specified filter expression when capturing traffic.
The filter syntax is beyond the scope of this manual page;
please refer to the
-.BR tcpdump (1)
+.BR tcpdump (8)
documentation.
.\"
.TP
.PP
.\"
(For a full reference on filter syntax, refer to the
-.BR tcpdump (1)
+.BR tcpdump (8)
manpage)
.PP
.\"
an appropriate netmask.
.\"
.SH SEE ALSO
-.BR tcpdump (1)
+.BR tcpdump (8)
.\"
.SH HISTORY
.I darkstat
/* darkstat 3
- * copyright (c) 2001-2011 Emil Mikulic.
+ * copyright (c) 2001-2014 Emil Mikulic.
*
* darkstat.c: signals, cmdline parsing, program body.
*
#include "db.h"
#include "dns.h"
#include "err.h"
-#include "http.h"
#include "hosts_db.h"
+#include "http.h"
#include "localip.h"
#include "ncache.h"
#include "now.h"
#include "pidfile.h"
+#include "str.h"
#include <assert.h>
#include <errno.h>
const char *opt_chroot_dir = NULL;
static void cb_chroot(const char *arg) { opt_chroot_dir = arg; }
+const char *opt_base = NULL;
+static void cb_base(const char *arg) { opt_base = arg; }
+
const char *opt_privdrop_user = NULL;
static void cb_user(const char *arg) { opt_privdrop_user = arg; }
-const char *daylog_fn = NULL;
-static void cb_daylog(const char *arg)
-{
- 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;
-}
+const char *opt_daylog_fn = NULL;
+static void cb_daylog(const char *arg) { opt_daylog_fn = arg; }
const char *import_fn = NULL;
-static void cb_import(const char *arg)
-{
- 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
- import_fn = arg;
-}
+static void cb_import(const char *arg) { import_fn = arg; }
const char *export_fn = NULL;
-static void cb_export(const char *arg)
-{
- 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
- export_fn = arg;
-}
+static void cb_export(const char *arg) { export_fn = arg; }
static const char *pid_fn = NULL;
-static void cb_pidfile(const char *arg)
-{
- 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;
-}
+static void cb_pidfile(const char *arg) { pid_fn = arg; }
unsigned int opt_hosts_max = 1000;
static void cb_hosts_max(const char *arg)
{"-p", "port", cb_port, 0},
{"-b", "bindaddr", cb_bindaddr, -1},
{"-l", "network/netmask", cb_local, 0},
+ {"--base", "path", cb_base, 0},
{"--local-only", NULL, cb_local_only, 0},
{"--snaplen", "bytes", cb_snaplen, 0},
{"--pppoe", NULL, cb_pppoe, 0},
if (opt_want_syslog)
openlog("darkstat", LOG_NDELAY | LOG_PID, LOG_DAEMON);
- /* some default values */
- if (opt_chroot_dir == NULL)
- opt_chroot_dir = CHROOT_DIR;
+ /* default value */
if (opt_privdrop_user == NULL)
opt_privdrop_user = PRIVDROP_USER;
hosts_db_free();
graph_free();
verbosef("Total packets: %llu, bytes: %llu",
- (unsigned long long)acct_total_packets,
- (unsigned long long)acct_total_bytes);
+ (llu)acct_total_packets,
+ (llu)acct_total_bytes);
}
/* --- Program body --- */
/* do this first as it forks - minimize memory use */
if (opt_want_dns) dns_init(opt_privdrop_user);
cap_start(opt_want_promisc); /* needs root */
+ http_init_base(opt_base);
http_listen(opt_bindport);
ncache_init(); /* must do before chroot() */
/* Don't need root privs for these: */
now_init();
- if (daylog_fn != NULL) daylog_init(daylog_fn);
+ if (opt_daylog_fn != NULL) daylog_init(opt_daylog_fn);
graph_init();
hosts_db_init();
if (import_fn != NULL) db_import(import_fn);
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");
/* darkstat 3
- * copyright (c) 2007-2011 Emil Mikulic.
+ * copyright (c) 2007-2014 Emil Mikulic.
*
* daylog.c: daily usage log
*
#define _GNU_SOURCE 1 /* for O_NOFOLLOW on Linux */
+#include "cdefs.h"
#include "err.h"
#include "daylog.h"
#include "str.h"
#include <unistd.h>
static const char *daylog_fn = NULL;
-static long today_real, tomorrow_real;
+static time_t today_real, tomorrow_real;
static uint64_t bytes_in, bytes_out, pkts_in, pkts_out;
#define DAYLOG_DATE_LEN 26 /* strlen("1900-01-01 00:00:00 +1234") + 1 */
}
/* Warns on error. */
+static void daylog_write(const char *format, ...) _printflike_(1, 2);
static void daylog_write(const char *format, ...) {
int fd;
ssize_t wr;
static void daylog_emit(void) {
daylog_write("%s|%qu|%qu|%qu|%qu|%qu\n",
- fmt_date(today_real), (uint64_t)today_real,
- bytes_in, bytes_out, pkts_in, pkts_out);
+ fmt_date(today_real),
+ (qu)today_real,
+ (qu)bytes_in,
+ (qu)bytes_out,
+ (qu)pkts_in,
+ (qu)pkts_out);
}
void daylog_init(const char *filename) {
daylog_fn = filename;
today_real = now_real();
tomorrow_real = tomorrow(today_real);
- verbosef("today is %ld, tomorrow is %ld",
- (long)today_real, (long)tomorrow_real);
+ verbosef("today is %llu, tomorrow is %llu",
+ (llu)today_real,
+ (llu)tomorrow_real);
bytes_in = bytes_out = pkts_in = pkts_out = 0;
daylog_write("# logging started at %s (%qu)\n",
- fmt_date(today_real), (uint64_t)today_real);
+ fmt_date(today_real), (qu)today_real);
}
void daylog_free(void) {
today_real = now_real();
daylog_emit(); /* Emit what's currently accumulated before we exit. */
daylog_write("# logging stopped at %s (%qu)\n",
- fmt_date(today_real), (uint64_t)today_real);
+ fmt_date(today_real), (qu)today_real);
}
void daylog_acct(uint64_t amount, enum graph_dir dir) {
today_real = now_real();
tomorrow_real = tomorrow(today_real);
bytes_in = bytes_out = pkts_in = pkts_out = 0;
- verbosef("updated daylog, tomorrow = %ld", (long)tomorrow_real);
+ verbosef("updated daylog, tomorrow = %llu", (llu)tomorrow_real);
}
/* Accounting. */
/* darkstat 3
*
* db.c: load and save in-memory database from/to file
- * copyright (c) 2007-2011 Ben Stewart, Emil Mikulic.
+ * copyright (c) 2007-2012 Ben Stewart, Emil Mikulic.
*
* You may use, modify and redistribute this file under the terms of the
* GNU General Public License version 2. (see COPYING.GPL)
/* darkstat 3
*
* db.h: load and save in-memory database from/to file
- * copyright (c) 2007-2011 Ben Stewart, Emil Mikulic.
+ * copyright (c) 2007-2012 Ben Stewart, Emil Mikulic.
*/
#include <sys/types.h> /* for size_t */
+++ /dev/null
-Repackaged upstream source:
-===========================
-
-The original darkstat tarball includes contrib/darkproxy.php, this file
-does not have any valid license that allow us o modify and redistribute it
-so the file is being removed from the original tarball to fulfill the DFSG.
-
-If you need to recreate the tarball you can execute
-uuscan --verbose --no-symlink this will download the current version and
-it will create the +dfsg.orig.tar.gz file.
-darkstat (3.0.717-1) UNRELEASED; urgency=low
+darkstat (3.0.718-2) unstable; urgency=low
- * Imported Upstream version 3.0.717
+ * Fix FTBFS on Hurd
- -- Emil Mikulic <emikulic@gmail.com> Sun, 18 Aug 2013 16:37:10 +1000
+ -- Rene Mayorga <rmayorga@debian.org> Sun, 13 Apr 2014 18:41:00 +0000
+
+darkstat (3.0.718-1) unstable; urgency=low
+
+ * New upstream release (Closes: #737857)
+ + Fix IPv6 error on start (Closes: 687967)
+ + Fix exits when monitoring ppp0 (Closes: #663515)
+ * debian/watch - remove dversionmangle and repack script
+ * debian/rpack.sh deleted.
+ * README.Source deleted
+ * Add Emil as a co-maint
+ * debian/rules
+ + remove not needed options on configure-stamp
+ + use dpkg-buildflags
+ * use dh compat 9, debhelper B-D updated
+ * Update debian policy to 3.9.5
+ + use mandatory build-arch and build-indep targets
+ * Convert to dpkg-source 3.0 quilt format
+ * Correct tcpdump's section number on the manpage (Closes: #671442)
+
+ -- Rene Mayorga <rmayorga@debian.org> Sun, 23 Mar 2014 12:36:51 +0000
darkstat (3.0.715-1) unstable; urgency=low
Priority: optional
Maintainer: Rene Mayorga <rmayorga@debian.org>
Uploaders: Emil Mikulic <emikulic@gmail.com>
-Build-Depends: debhelper (>= 7), libpcap-dev, autotools-dev, po-debconf,
+Build-Depends: debhelper (>= 9), libpcap-dev, autotools-dev, po-debconf,
zlib1g-dev
-Standards-Version: 3.9.2
+Standards-Version: 3.9.5
Homepage: http://unix4lyfe.org/darkstat/
Package: darkstat
--- /dev/null
+Description: Correct tcpdump section number
+ Use tcpdump section number 8 for debian systems.
+Author: Rene Mayorga <rmayorga@debian.org>
+Forwarded: not-needed
+Last-Update: 2014-03-23
+---
+This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
+--- a/darkstat.8.in
++++ b/darkstat.8.in
+@@ -194,7 +194,7 @@
+ Use the specified filter expression when capturing traffic.
+ The filter syntax is beyond the scope of this manual page;
+ please refer to the
+-.BR tcpdump (1)
++.BR tcpdump (8)
+ documentation.
+ .\"
+ .TP
+@@ -402,7 +402,7 @@
+ .PP
+ .\"
+ (For a full reference on filter syntax, refer to the
+-.BR tcpdump (1)
++.BR tcpdump (8)
+ manpage)
+ .PP
+ .\"
+@@ -460,7 +460,7 @@
+ an appropriate netmask.
+ .\"
+ .SH SEE ALSO
+-.BR tcpdump (1)
++.BR tcpdump (8)
+ .\"
+ .SH HISTORY
+ .I darkstat
--- /dev/null
+Description: Use the correct macro for OS X
+ The OS X fix use the macro __MACH__, this catch up on Hurd, so
+ better use __APPLE__
+Author: René Mayorga <rmayorga@debian.org>
+---
+This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
+--- a/now.c
++++ b/now.c
+@@ -23,7 +23,7 @@
+ #include <string.h>
+ #include <time.h>
+
+-#ifdef __MACH__
++#ifdef __APPLE__
+ /* Fake up clock_gettime() on OS X. */
+ # include <sys/time.h>
+ # include <inttypes.h>
--- /dev/null
+CorrectTcmpdump-section
+FixHURDsFTBS.patch
+++ /dev/null
-set -e
-set -u
-
-VER="3.0.714"
-FILE="../darkstat-3.0.714.tar.bz2"
-PKG="darkstat"
-
-SUFFIX="+dfsg"
-
-echo
-echo "Repackaging $FILE"
-echo
-
-DIR=`mktemp -d ./tmpRepackXXXXXX`
-DIR=$(readlink -f "$DIR")
-trap "/bin/rm -rf \"$DIR\"" QUIT INT EXIT
-
-
-UP_BASE="$DIR/unpack"
-mkdir "$UP_BASE"
-tar -xvjf "$FILE" -C /"$UP_BASE"
-
-set -f
-MYORIGPWD=$(pwd)
-cd "$UP_BASE"
-rm "$PKG-$VER/contrib/darkproxy.php"
-tar -cvzf "$PKG-$VER$SUFFIX.orig.tar.gz" "$PKG-$VER/"
-mv "$PKG-$VER$SUFFIX.orig.tar.gz" "$MYORIGPWD/../"
-cd "$MYORIGPWD"
-
-echo
-echo "done"
-echo
-
-rm "$FILE"
-rm -rf "$DIR"
DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
-CFLAGS += -Wall -g
+# CFLAGS += -Wall -g
+#
+
+DPKG_EXPORT_BUILDFLAGS = 1
+include /usr/share/dpkg/buildflags.mk
configure: configure-stamp
configure-stamp:
dh_testdir
cp /usr/share/misc/config.sub /usr/share/misc/config.guess .
- CFLAGS="$(CFLAGS)" LDFLAGS="$(INSTALL_PROGRAM)" ./configure --host=$(DEB_HOST_GNU_TYPE) --build=$(DEB_BUILD_GNU_TYPE) --prefix=/usr --mandir=\$${prefix}/share/man --with-chroot-dir=/var/lib/darkstat
+ CFLAGS="$(CFLAGS)" ./configure --host=$(DEB_HOST_GNU_TYPE) --build=$(DEB_BUILD_GNU_TYPE) --prefix=/usr --mandir=\$${prefix}/share/man
touch $@
build: configure-stamp build-stamp
+build-arch: build
+build-indep: build
build-stamp: configure-stamp
dh_testdir
version=3
-options=dversionmangle=s/\+dfsg// \
- http://unix4lyfe.org/darkstat/darkstat-(.*)\.tar\.bz2 \
- debian debian/rpack.sh
+http://unix4lyfe.org/darkstat/darkstat-(.*)\.tar\.bz2
/* darkstat 3
- * copyright (c) 2001-2011 Emil Mikulic.
+ * copyright (c) 2001-2012 Emil Mikulic.
*
* decode.c: packet decoding.
*
/* darkstat 3
- * copyright (c) 2001-2011 Emil Mikulic.
+ * copyright (c) 2001-2012 Emil Mikulic.
*
* decode.h: packet decoding.
*
/* darkstat 3
- * copyright (c) 2001-2011 Emil Mikulic.
+ * copyright (c) 2001-2014 Emil Mikulic.
*
* dns.c: synchronous DNS in a child process.
*
#define CHILD 0 /* child process uses this socket */
#define PARENT 1
-static int sock[2];
+static int dns_sock[2];
static pid_t pid = -1;
struct dns_reply {
void
dns_init(const char *privdrop_user)
{
- if (socketpair(AF_UNIX, SOCK_STREAM, 0, sock) == -1)
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, dns_sock) == -1)
err(1, "socketpair");
pid = fork();
if (pid == 0) {
/* We are the child. */
privdrop(NULL /* don't chroot */, privdrop_user);
- close(sock[PARENT]);
- sock[PARENT] = -1;
+ close(dns_sock[PARENT]);
+ dns_sock[PARENT] = -1;
daemonize_finish(); /* drop our copy of the lifeline! */
if (signal(SIGUSR1, SIG_IGN) == SIG_ERR)
errx(1, "signal(SIGUSR1, ignore) failed");
dns_main();
- verbosef("fell out of dns_main()");
exit(0);
} else {
/* We are the parent. */
- close(sock[CHILD]);
- sock[CHILD] = -1;
- fd_set_nonblock(sock[PARENT]);
+ close(dns_sock[CHILD]);
+ dns_sock[CHILD] = -1;
+ fd_set_nonblock(dns_sock[PARENT]);
verbosef("DNS child has PID %d", pid);
}
}
{
if (pid == -1)
return; /* no child was started */
- close(sock[PARENT]);
+ close(dns_sock[PARENT]);
if (kill(pid, SIGINT) == -1)
err(1, "kill");
verbosef("dns_stop() waiting for child");
}
static RB_HEAD(tree_t, tree_rec) ip_tree = RB_INITIALIZER(&tree_rec);
-/* Quiet warnings. */
-static struct tree_rec * tree_t_RB_NEXT(struct tree_rec *elm)
- _unused_;
-static struct tree_rec * tree_t_RB_MINMAX(struct tree_t *head, int val)
- _unused_;
-RB_GENERATE(tree_t, tree_rec, ptree, tree_cmp)
+RB_GENERATE_STATIC(tree_t, tree_rec, ptree, tree_cmp)
void
dns_queue(const struct addr *const ipaddr)
return;
}
- num_w = write(sock[PARENT], ipaddr, sizeof(*ipaddr)); /* won't block */
+ num_w = write(dns_sock[PARENT], ipaddr, sizeof(*ipaddr)); /* won't block */
if (num_w == 0)
warnx("dns_queue: write: ignoring end of file");
else if (num_w == -1)
struct dns_reply reply;
ssize_t numread;
- numread = read(sock[PARENT], &reply, sizeof(reply));
+ numread = read(dns_sock[PARENT], &reply, sizeof(reply));
if (numread == -1) {
if (errno == EAGAIN)
return (0); /* no input waiting */
struct addr ip;
};
-STAILQ_HEAD(qhead, qitem) queue = STAILQ_HEAD_INITIALIZER(queue);
+static STAILQ_HEAD(qhead, qitem) queue = STAILQ_HEAD_INITIALIZER(queue);
static void
enqueue(const struct addr *const ip)
struct addr ip;
setproctitle("DNS child");
- fd_set_nonblock(sock[CHILD]);
+ fd_set_nonblock(dns_sock[CHILD]);
verbosef("DNS child entering main DNS loop");
for (;;) {
int blocking;
if (STAILQ_EMPTY(&queue)) {
blocking = 1;
- fd_set_block(sock[CHILD]);
+ fd_set_block(dns_sock[CHILD]);
verbosef("entering blocking read loop");
} else {
blocking = 0;
- fd_set_nonblock(sock[CHILD]);
+ fd_set_nonblock(dns_sock[CHILD]);
verbosef("non-blocking poll");
}
for (;;) {
/* While we have input to process... */
- ssize_t numread = read(sock[CHILD], &ip, sizeof(ip));
+ ssize_t numread = read(dns_sock[CHILD], &ip, sizeof(ip));
if (numread == 0)
exit(0); /* end of file, nothing more to do here. */
if (numread == -1) {
* run out of input we fall through to queue processing.
*/
blocking = 0;
- fd_set_nonblock(sock[CHILD]);
+ fd_set_nonblock(dns_sock[CHILD]);
}
}
strlcpy(reply.name, host, sizeof(reply.name));
reply.error = 0;
}
- fd_set_block(sock[CHILD]);
- xwrite(sock[CHILD], &reply, sizeof(reply));
+ fd_set_block(dns_sock[CHILD]);
+ xwrite(dns_sock[CHILD], &reply, sizeof(reply));
verbosef("DNS: %s is \"%s\".", addr_to_str(&reply.addr),
(ret == 0) ? reply.name : gai_strerror(ret));
}
/* darkstat 3
- * copyright (c) 2001-2011 Emil Mikulic.
+ * copyright (c) 2001-2012 Emil Mikulic.
*
* err.c: BSD-like err() and warn() functions
*
/* darkstat 3
- * copyright (c) 2001-2011 Emil Mikulic.
+ * copyright (c) 2001-2014 Emil Mikulic.
*
* err.h: BSD-like err() and warn() functions
*
void warnx(const char *format, ...) _printflike_(1, 2);
void verbosef(const char *format, ...) _printflike_(1, 2);
-void dverbosef(const char *format _unused_, ...);
+void dverbosef(const char *format _unused_, ...) _printflike_(1, 2);
/* vim:set ts=3 sw=3 tw=78 expandtab: */
/* darkstat 3
- * copyright (c) 2006-2011 Emil Mikulic.
+ * copyright (c) 2006-2014 Emil Mikulic.
*
* graph_db.c: round robin database for graph data
*
};
static unsigned int graph_db_size = sizeof(graph_db)/sizeof(*graph_db);
-static long start_mono, start_real, last_real;
+static time_t start_mono, start_real, last_real;
void graph_init(void) {
unsigned int i;
}
start_mono = now_mono();
start_real = now_real();
+ last_real = 0;
graph_reset();
}
memcpy(g->out, tmp, size);
free(tmp);
+ assert(g->num_bars > 0);
assert(pos == ( (g->pos + ofs) % g->num_bars ));
g->pos = pos;
}
}
void graph_rotate(void) {
- long t, td;
+ time_t t, td;
struct tm *tm;
unsigned int i;
struct str *buf, *rf;
unsigned int i;
char start_when[100];
- long d_real, d_mono;
+ time_t d_real, d_mono;
buf = str_make();
html_open(buf, "Graphs", /*path_depth=*/0, /*want_graph_js=*/1);
str_append(buf, "</span>");
if (abs(d_real - d_mono) > 1)
str_appendf(buf, " (real time is off by %qd sec)",
- (int64_t)d_real - (int64_t)d_mono);
+ (qd)d_real - (qd)d_mono);
if (strftime(start_when, sizeof(start_when),
"%Y-%m-%d %H:%M:%S %Z%z", localtime(&start_real)) != 0)
"(<span id=\"pc\">%'u</span> <b>captured,</b> "
"<span id=\"pd\">%'u</span> <b>dropped)</b><br>\n"
"</p>\n",
- acct_total_bytes,
- acct_total_packets,
- cap_pkts_recv, cap_pkts_drop);
+ (qu)acct_total_bytes,
+ (qu)acct_total_packets,
+ cap_pkts_recv,
+ cap_pkts_drop);
str_append(buf,
"<div id=\"graphs\">\n"
struct str *buf = str_make(), *rf;
str_appendf(buf, "<graphs tp=\"%qu\" tb=\"%qu\" pc=\"%u\" pd=\"%u\" rf=\"",
- acct_total_packets, acct_total_bytes, cap_pkts_recv, cap_pkts_drop);
+ (qu)acct_total_packets,
+ (qu)acct_total_bytes,
+ cap_pkts_recv,
+ cap_pkts_drop);
rf = length_of_time(now_real() - start_real);
str_appendstr(buf, rf);
str_free(rf);
j = (j + 1) % g->num_bars;
/* <element pos="" in="" out=""/> */
str_appendf(buf, "<e p=\"%u\" i=\"%qu\" o=\"%qu\"/>\n",
- g->offset + j, g->in[j], g->out[j]);
+ g->offset + j,
+ (qu)g->in[j],
+ (qu)g->out[j]);
} while (j != g->pos);
str_appendf(buf, "</%s>\n", g->unit);
}
/* darkstat 3
- * copyright (c) 2001-2011 Emil Mikulic.
+ * copyright (c) 2001-2014 Emil Mikulic.
*
* hosts_db.c: database of hosts, ports, protocols.
*
#include "opt.h"
#include "str.h"
-#include <netdb.h> /* struct addrinfo */
+#include <netdb.h> /* struct addrinfo */
#include <assert.h>
#include <errno.h>
#include <stdio.h>
" <td class=\"num\">%'qu</td>\n"
" <td class=\"num\">%'qu</td>\n"
" <td class=\"num\">%'qu</td>\n",
- b->in, b->out, b->total);
+ (qu)b->in,
+ (qu)b->out,
+ (qu)b->total);
if (opt_want_lastseen) {
- long last = b->u.host.last_seen_mono;
+ time_t last = b->u.host.last_seen_mono;
struct str *last_str = NULL;
if ((now_mono() >= last) && (last > 0))
if (last == 0)
str_append(buf, "(never)");
else
- str_append(buf, "(clock error)");
+ str_appendf(buf, "(clock error: now = %qu, last = %qu)",
+ (qu)now_mono(),
+ (qu)last);
} else {
str_appendstr(buf, last_str);
str_free(last_str);
" <td class=\"num\">%'qu</td>\n"
"</tr>\n",
css_class,
- p->port, getservtcp(p->port), b->in, b->out, b->total, p->syn
+ p->port,
+ getservtcp(p->port),
+ (qu)b->in,
+ (qu)b->out,
+ (qu)b->total,
+ (qu)p->syn
);
}
" <td class=\"num\">%'qu</td>\n"
"</tr>\n",
css_class,
- p->port, getservudp(p->port), b->in, b->out, b->total
+ p->port,
+ getservudp(p->port),
+ (qu)b->in,
+ (qu)b->out,
+ (qu)b->total
);
}
" <td class=\"num\">%'qu</td>\n"
"</tr>\n",
css_class,
- p->proto, getproto(p->proto),
- b->in, b->out, b->total
+ p->proto,
+ getproto(p->proto),
+ (qu)b->in,
+ (qu)b->out,
+ (qu)b->total
);
}
struct str *buf, *ls_len;
char ls_when[100];
const char *canonical;
- time_t last_real;
+ time_t last_seen_real;
h = host_search(ip);
if (h == NULL)
"<p>\n"
"<b>Last seen:</b> ");
- last_real = mono_to_real(h->u.host.last_seen_mono);
+ last_seen_real = mono_to_real(h->u.host.last_seen_mono);
if (strftime(ls_when, sizeof(ls_when),
- "%Y-%m-%d %H:%M:%S %Z%z", localtime(&last_real)) != 0)
+ "%Y-%m-%d %H:%M:%S %Z%z", localtime(&last_seen_real)) != 0)
str_append(buf, ls_when);
if (h->u.host.last_seen_mono <= now_mono()) {
" <b>Out:</b> %'qu<br>\n"
" <b>Total:</b> %'qu<br>\n"
"</p>\n",
- h->in, h->out, h->total);
+ (qu)h->in,
+ (qu)h->out,
+ (qu)h->total);
str_append(buf, "<h3>TCP ports</h3>\n");
format_table(buf, h->u.host.ports_tcp, 0,TOTAL,0);
/* darkstat 3
- * copyright (c) 2001-2011 Emil Mikulic.
+ * copyright (c) 2001-2014 Emil Mikulic.
*
* hosts_db.h: database of hosts, ports, protocols.
*
struct addr addr;
char *dns;
uint8_t mac_addr[6];
- long last_seen_mono;
+ time_t last_seen_mono;
struct hashtable *ports_tcp, *ports_udp, *ip_protos;
};
/* darkstat 3
- * copyright (c) 2001-2011 Emil Mikulic.
+ * copyright (c) 2001-2012 Emil Mikulic.
*
* hosts_sort.c: quicksort a table of buckets.
*
/* darkstat 3
- * copyright (c) 2001-2012 Emil Mikulic.
+ * copyright (c) 2001-2014 Emil Mikulic.
*
* http.c: embedded webserver.
* This borrows a lot of code from darkhttpd.
#include <unistd.h>
#include <zlib.h>
+static char *http_base_url = NULL;
+static int http_base_len = 0;
+
static const char mime_type_xml[] = "text/xml";
static const char mime_type_html[] = "text/html; charset=us-ascii";
static const char mime_type_css[] = "text/css";
int socket;
struct sockaddr_storage client;
- long last_active_mono;
+ time_t last_active_mono;
enum {
RECV_REQUEST, /* receiving request */
SEND_HEADER_AND_REPLY, /* try to send header+reply together */
if (conn->encoding == NULL)
conn->encoding = encoding_identity;
- verbosef("http: %d %s (%s: %zu bytes)", code, text,
- conn->encoding, conn->reply_length);
+ verbosef("http: %d %s (%s: %zu bytes)",
+ code,
+ text,
+ conn->encoding,
+ conn->reply_length);
conn->header_length = xasprintf(&(conn->header),
"HTTP/1.1 %d %s\r\n"
"Date: %s\r\n"
"Server: %s\r\n"
"Vary: Accept-Encoding\r\n"
"Content-Type: %s\r\n"
- "Content-Length: %d\r\n"
+ "Content-Length: %qu\r\n"
"Content-Encoding: %s\r\n"
"X-Robots-Tag: noindex, noarchive\r\n"
"%s"
- "\r\n"
- ,
+ "\r\n",
code, text,
- rfc1123_date(date, now_real()), server,
- conn->mime_type, conn->reply_length, conn->encoding,
+ rfc1123_date(date, now_real()),
+ server,
+ conn->mime_type,
+ (qu)conn->reply_length,
+ conn->encoding,
conn->header_extra);
conn->http_code = code;
}
/* ---------------------------------------------------------------------------
* A default reply for any (erroneous) occasion.
*/
+static void default_reply(struct connection *conn,
+ const int errcode, const char *errname, const char *format, ...)
+ _printflike_(4, 5);
static void default_reply(struct connection *conn,
const int errcode, const char *errname, const char *format, ...)
{
zs.zfree = Z_NULL;
zs.opaque = Z_NULL;
- if (deflateInit2(&zs, Z_BEST_COMPRESSION, Z_DEFLATED,
- 15+16, /* 15 = biggest window, 16 = add gzip header+trailer */
- 8 /* default */,
- Z_DEFAULT_STRATEGY) != Z_OK)
- return;
+ if (deflateInit2(&zs,
+ Z_BEST_COMPRESSION,
+ Z_DEFLATED,
+ 15+16, /* 15 = biggest window,
+ 16 = add gzip header+trailer */
+ 8 /* default */,
+ Z_DEFAULT_STRATEGY) != Z_OK) {
+ free(buf);
+ return;
+ }
zs.avail_in = conn->reply_length;
zs.next_in = (unsigned char *)conn->reply;
if (deflate(&zs, Z_FINISH) != Z_STREAM_END) {
deflateEnd(&zs);
free(buf);
- verbosef("failed to compress %u bytes", (unsigned int)len);
+ verbosef("failed to compress %zu bytes", len);
return;
}
*/
static void process_get(struct connection *conn)
{
- char *decoded_url, *safe_url;
+ char *safe_url;
verbosef("http: %s \"%s\" %s", conn->method, conn->uri,
(conn->query == NULL)?"":conn->query);
- /* work out path of file being requested */
- decoded_url = urldecode(conn->uri);
-
- /* make sure it's safe */
- safe_url = make_safe_uri(decoded_url);
- free(decoded_url);
- if (safe_url == NULL)
{
- default_reply(conn, 400, "Bad Request",
- "You requested an invalid URI: %s", conn->uri);
- return;
+ /* Decode the URL being requested. */
+ char *decoded_url;
+ char *decoded_url_offset;
+
+ decoded_url = urldecode(conn->uri);
+
+ /* Optionally strip the base. */
+ decoded_url_offset = decoded_url;
+ if (str_starts_with(decoded_url, http_base_url)) {
+ decoded_url_offset += http_base_len - 1;
+ }
+
+ /* Make sure it's safe. */
+ safe_url = make_safe_uri(decoded_url_offset);
+ free(decoded_url);
+ if (safe_url == NULL) {
+ default_reply(conn, 400, "Bad Request",
+ "You requested an invalid URI: %s", conn->uri);
+ return;
+ }
}
if (strcmp(safe_url, "/") == 0) {
if (conn->reply_sent == conn->reply_length) conn->state = DONE;
}
+
+
+/* --------------------------------------------------------------------------
+ * Initialize the base url.
+ */
+void http_init_base(const char *url) {
+ char *slashed_url, *safe_url;
+ size_t urllen;
+
+ if (url == NULL) {
+ http_base_url = strdup("/");
+ } else {
+ /* Make sure that the url has leading and trailing slashes. */
+ urllen = strlen(url);
+ slashed_url = xmalloc(urllen+3);
+ slashed_url[0] = '/';
+ memcpy(slashed_url+1, url, urllen); /* don't copy NUL */
+ slashed_url[urllen+1] = '/';
+ slashed_url[urllen+2] = '\0';
+
+ /* Clean the url. */
+ safe_url = make_safe_uri(slashed_url);
+ free(slashed_url);
+ if (safe_url == NULL) {
+ verbosef("invalid base \"%s\", ignored", url);
+ http_base_url = strdup("/"); /* set to default */
+ } else {
+ http_base_url = safe_url;
+ }
+ }
+ http_base_len = strlen(http_base_url);
+ verbosef("set base url to \"%s\"", http_base_url);
+}
+
/* Use getaddrinfo to figure out what type of socket to create and
* what to bind it to. "bindaddr" can be NULL. Remember to freeaddrinfo()
* the result.
if (listen(sockin, 128) == -1)
err(1, "listen() failed");
- verbosef("listening on http://%s%s%s:%u/",
+ verbosef("listening on http://%s%s%s:%u%s",
(ai->ai_family == AF_INET6) ? "[" : "",
ipaddr,
(ai->ai_family == AF_INET6) ? "]" : "",
- bindport);
+ bindport,
+ http_base_url);
/* add to insocks */
insocks = xrealloc(insocks, sizeof(*insocks) * (insock_num + 1));
struct connection *conn;
unsigned int i;
+ free(http_base_url);
+
/* Close listening sockets. */
for (i=0; i<insock_num; i++)
close(insocks[i]);
/* darkstat 3
- * copyright (c) 2001-2011 Emil Mikulic.
+ * copyright (c) 2001-2014 Emil Mikulic.
*
* http.h: embedded webserver.
*/
#include <sys/select.h>
#include <netinet/in.h>
+void http_init_base(const char *url);
void http_add_bindaddr(const char *bindaddr);
void http_listen(const unsigned short bindport);
void http_fd_set(fd_set *recv_set, fd_set *send_set, int *max_fd,
/* darkstat 3
- * copyright (c) 2001-2011 Emil Mikulic.
+ * copyright (c) 2001-2012 Emil Mikulic.
*
* localip.c: determine local IPs of an interface
*
/* darkstat 3
- * copyright (c) 2001-2011 Emil Mikulic.
+ * copyright (c) 2001-2014 Emil Mikulic.
*
* localip.h: determine the local IPs of an interface
*
struct local_ips {
int is_valid;
- long last_update_mono;
+ time_t last_update_mono;
int num_addrs;
struct addr *addrs;
};
/* darkstat 3
- * copyright (c) 2001-2011 Emil Mikulic.
+ * copyright (c) 2001-2014 Emil Mikulic.
*
* ncache.c: cache of protocol and service names.
*
}
RB_HEAD(nc_tree, name_rec);
-RB_GENERATE(nc_tree, name_rec, ptree, rec_cmp)
+RB_GENERATE_STATIC(nc_tree, name_rec, ptree, rec_cmp)
static struct nc_tree
t_proto = RB_INITIALIZER(&name_rec),
/* darkstat 3
- * copyright (c) 2012 Emil Mikulic.
+ * copyright (c) 2012-2014 Emil Mikulic.
*
* now.c: a cache of the current time.
*
*/
#include "err.h"
#include "now.h"
+#include "str.h"
#include <assert.h>
#include <string.h>
#include <time.h>
-#ifdef __MACH__
+#ifdef __APPLE__
/* Fake up clock_gettime() on OS X. */
# include <sys/time.h>
# include <inttypes.h>
static struct timespec clock_real, clock_mono;
static int now_initialized = 0;
-long now_real(void) {
+time_t now_real(void) {
assert(now_initialized);
return clock_real.tv_sec;
}
-long now_mono(void) {
+time_t now_mono(void) {
assert(now_initialized);
return clock_mono.tv_sec;
}
return 0;
}
+static void warn_backwards(const char *name,
+ const struct timespec * const t0,
+ const struct timespec * const t1) {
+ verbosef("%s clock went backwards from %lld.%09lld to %lld.%09lld",
+ name,
+ (lld)t0->tv_sec,
+ (lld)t0->tv_nsec,
+ (lld)t1->tv_sec,
+ (lld)t1->tv_nsec);
+}
+
static void clock_update(const clockid_t clk_id,
struct timespec *dest,
const char *name) {
clock_gettime(clk_id, &t);
if (now_initialized && before(&t, dest)) {
- verbosef("%s clock went backwards from %ld.%09ld to %ld.%09ld",
- name,
- (long)dest->tv_sec,
- (long)dest->tv_nsec,
- (long)t.tv_sec,
- (long)t.tv_nsec);
+ warn_backwards(name, &t, dest);
}
memcpy(dest, &t, sizeof(t));
}
all_clocks_update();
}
-long mono_to_real(const long t) {
+time_t mono_to_real(const time_t t) {
assert(now_initialized);
return t - clock_mono.tv_sec + clock_real.tv_sec;
}
-long real_to_mono(const long t) {
+time_t real_to_mono(const time_t t) {
assert(now_initialized);
return t - clock_real.tv_sec + clock_mono.tv_sec;
}
a->tv_nsec - b->tv_nsec;
}
-void timer_stop(const struct timespec * const t,
+void timer_stop(const struct timespec * const t0,
const int64_t nsec,
const char *warning) {
- struct timespec t2;
+ struct timespec t1;
int64_t diff;
- clock_gettime(CLOCK_MONOTONIC, &t2);
- diff = ts_diff(&t2, t);
- assert(diff > 0);
- if (diff > nsec)
+ clock_gettime(CLOCK_MONOTONIC, &t1);
+ if (before(&t1, t0)) {
+ warn_backwards("monotonic timer", t0, &t1);
+ return;
+ }
+ diff = ts_diff(&t1, t0);
+ if (diff > nsec) {
warnx("%s (took %lld nsec, over threshold of %lld nsec)",
warning,
- (long long)diff,
- (long long)nsec);
+ (lld)diff,
+ (lld)nsec);
+ }
}
/* vim:set ts=3 sw=3 tw=80 et: */
/* darkstat 3
- * copyright (c) 2001-2006 Emil Mikulic.
+ * copyright (c) 2001-2014 Emil Mikulic.
*
* now.h: a cache of the current time.
*
void now_init(void);
void now_update(void); /* once per event loop (in darkstat.c) */
-long now_real(void);
-long now_mono(void);
+time_t now_real(void);
+time_t now_mono(void);
-long mono_to_real(const long t);
-long real_to_mono(const long t);
+time_t mono_to_real(const time_t t);
+time_t real_to_mono(const time_t t);
/* Emits warnings if a call is too slow. */
struct timespec;
/* darkstat 3
- * copyright (c) 2001-2011 Emil Mikulic.
+ * copyright (c) 2001-2012 Emil Mikulic.
*
* opt.h: global options
*/
/* darkstat 3
- * copyright (c) 2007-2011 Emil Mikulic.
+ * copyright (c) 2007-2014 Emil Mikulic.
*
* pidfile.h: pidfile manglement
*
static int pidfd = -1;
static const char *pidname = NULL;
-void
-pidfile_create(const char *chroot_dir, const char *filename,
- const char *privdrop_user)
-{
+void pidfile_create(const char *chroot_dir,
+ const char *filename,
+ const char *privdrop_user) {
struct passwd *pw;
if (pidfd != -1)
err(1, "getpwnam(\"%s\") failed", privdrop_user);
}
- if (chdir(chroot_dir) == -1)
- err(1, "chdir(\"%s\") failed", chroot_dir);
+ if (chroot_dir != NULL) {
+ if (chdir(chroot_dir) == -1) {
+ err(1, "chdir(\"%s\") failed", chroot_dir);
+ }
+ }
pidname = filename;
pidfd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0600);
if (pidfd == -1)
/* darkstat 3
- * copyright (c) 2001-2011 Emil Mikulic.
+ * copyright (c) 2001-2012 Emil Mikulic.
*
* str.c: string buffer with pool-based reallocation
*
/* darkstat 3
- * copyright (c) 2001-2011 Emil Mikulic.
+ * copyright (c) 2001-2014 Emil Mikulic.
*
* str.h: string buffer with pool-based reallocation
*
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#ifndef __DARKSTAT_STR_H
+#define __DARKSTAT_STR_H
+
+#include "cdefs.h"
#include <sys/types.h>
#include <stdarg.h>
+#include <stdint.h> /* for uint64_t */
+
+typedef long long signed int qd; /* as in appendf("%qd") */
+typedef long long unsigned int qu; /* as in appendf("%qu") */
+typedef long long unsigned int lld; /* as in printf("%lld") */
+typedef long long unsigned int llu; /* as in printf("%llu") */
+
+_Static_assert(sizeof(qd) == sizeof(int64_t), "qd must be int64_t sized");
+_Static_assert(sizeof(qu) == sizeof(uint64_t), "qu must be uint64_t sized");
+_Static_assert(sizeof(lld) == sizeof(int64_t), "lld must be int64_t sized");
+_Static_assert(sizeof(llu) == sizeof(uint64_t), "llu must be uint64_t sized");
/* Note: the contents are 8-bit clean and not zero terminated! */
void str_append(struct str *buf, const char *s);
#endif
-size_t xvasprintf(char **result, const char *format, va_list va);
-size_t xasprintf(char **result, const char *format, ...);
-void str_appendf(struct str *buf, const char *format, ...);
-void str_vappendf(struct str *s, const char *format, va_list va);
+size_t xvasprintf(char **result, const char *format, va_list va)
+ _printflike_(2, 0);
+size_t xasprintf(char **result, const char *format, ...) _printflike_(2, 3);
+void str_vappendf(struct str *s, const char *format, va_list va)
+ _printflike_(2, 0);
+void str_appendf(struct str *s, const char *format, ...) _printflike_(2, 3);
struct str *length_of_time(const time_t t);
ssize_t str_write(const struct str * const buf, const int fd);
size_t str_len(const struct str * const buf);
+#endif /* __DARKSTAT_STR_H */
/* vim:set ts=3 sw=3 tw=78 expandtab: */
-/* This is a cut down version of FreeBSD's
- * src/sys/sys/tree.h,v 1.5
- *
- * Also tagged
- * $NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $
- * $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $
- *
- * The original file's license:
- *
+/* This is a cut down version of NetBSD's /usr/include/sys/tree.h */
+
+/* $NetBSD: tree.h,v 1.20 2013/09/14 13:20:45 joerg Exp $ */
+/* $OpenBSD: tree.h,v 1.13 2011/07/09 00:19:45 pirofti Exp $ */
+/*
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#ifndef _SYS_TREE_H_
+#define _SYS_TREE_H_
+
+#include "cdefs.h"
+
+/* Macros that define a red-black tree */
#define RB_HEAD(name, type) \
struct name { \
struct type *rbh_root; /* root of the tree */ \
}
-#define RB_INITIALIZER(root) { NULL }
+#define RB_INITIALIZER(root) \
+ { NULL }
+
+#define RB_INIT(root) do { \
+ (root)->rbh_root = NULL; \
+} while (/*CONSTCOND*/ 0)
#define RB_BLACK 0
#define RB_RED 1
#define RB_PARENT(elm, field) (elm)->field.rbe_parent
#define RB_COLOR(elm, field) (elm)->field.rbe_color
#define RB_ROOT(head) (head)->rbh_root
+#define RB_EMPTY(head) (RB_ROOT(head) == NULL)
#define RB_SET(elm, parent, field) do { \
RB_PARENT(elm, field) = parent; \
RB_COLOR(red, field) = RB_RED; \
} while (/*CONSTCOND*/ 0)
-#define RB_AUGMENT(x) do {} while (0)
+#ifndef RB_AUGMENT
+#define RB_AUGMENT(x) do {} while (/*CONSTCOND*/ 0)
+#endif
#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \
(tmp) = RB_RIGHT(elm, field); \
RB_AUGMENT(RB_PARENT(tmp, field)); \
} while (/*CONSTCOND*/ 0)
+/* Generates prototypes and inline functions */
+#define RB_PROTOTYPE(name, type, field, cmp) \
+ RB_PROTOTYPE_INTERNAL(name, type, field, cmp,)
+#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \
+ RB_PROTOTYPE_INTERNAL(name, type, field, cmp, _unused_ static)
+#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \
+attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \
+attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\
+attr struct type *name##_RB_REMOVE(struct name *, struct type *); \
+attr struct type *name##_RB_INSERT(struct name *, struct type *); \
+attr struct type *name##_RB_FIND(struct name *, struct type *); \
+attr struct type *name##_RB_NFIND(struct name *, struct type *); \
+attr struct type *name##_RB_NEXT(struct type *); \
+attr struct type *name##_RB_PREV(struct type *); \
+attr struct type *name##_RB_MINMAX(struct name *, int); \
+ \
+
+#include <assert.h>
+
+/* Main rb operation.
+ * Moves node close to the key of elm to top
+ */
#define RB_GENERATE(name, type, field, cmp) \
- RB_GENERATE_INTERNAL(name, type, field, cmp, static)
+ RB_GENERATE_INTERNAL(name, type, field, cmp,)
+#define RB_GENERATE_STATIC(name, type, field, cmp) \
+ RB_GENERATE_INTERNAL(name, type, field, cmp, _unused_ static)
#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \
attr void \
name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \
while ((parent = RB_PARENT(elm, field)) != NULL && \
RB_COLOR(parent, field) == RB_RED) { \
gparent = RB_PARENT(parent, field); \
+ assert(gparent != NULL); \
if (parent == RB_LEFT(gparent, field)) { \
tmp = RB_RIGHT(gparent, field); \
if (tmp && RB_COLOR(tmp, field) == RB_RED) { \
RB_ROTATE_LEFT(head, parent, tmp, field);\
tmp = RB_RIGHT(parent, field); \
} \
+ assert(tmp != NULL); \
if ((RB_LEFT(tmp, field) == NULL || \
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
(RB_RIGHT(tmp, field) == NULL || \
RB_ROTATE_RIGHT(head, parent, tmp, field);\
tmp = RB_LEFT(parent, field); \
} \
+ assert(tmp != NULL); \
if ((RB_LEFT(tmp, field) == NULL || \
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
(RB_RIGHT(tmp, field) == NULL || \
RB_AUGMENT(RB_PARENT(old, field)); \
} else \
RB_ROOT(head) = elm; \
+ assert(RB_LEFT(old, field) != NULL); \
RB_PARENT(RB_LEFT(old, field), field) = elm; \
if (RB_RIGHT(old, field)) \
RB_PARENT(RB_RIGHT(old, field), field) = elm; \
return (NULL); \
} \
\
+/* Finds the first node greater than or equal to the search key */ \
+attr struct type * \
+name##_RB_NFIND(struct name *head, struct type *elm) \
+{ \
+ struct type *tmp = RB_ROOT(head); \
+ struct type *res = NULL; \
+ int comp; \
+ while (tmp) { \
+ comp = cmp(elm, tmp); \
+ if (comp < 0) { \
+ res = tmp; \
+ tmp = RB_LEFT(tmp, field); \
+ } \
+ else if (comp > 0) \
+ tmp = RB_RIGHT(tmp, field); \
+ else \
+ return (tmp); \
+ } \
+ return (res); \
+} \
+ \
/* ARGSUSED */ \
attr struct type * \
name##_RB_NEXT(struct type *elm) \
return (elm); \
} \
\
+/* ARGSUSED */ \
+attr struct type * \
+name##_RB_PREV(struct type *elm) \
+{ \
+ if (RB_LEFT(elm, field)) { \
+ elm = RB_LEFT(elm, field); \
+ while (RB_RIGHT(elm, field)) \
+ elm = RB_RIGHT(elm, field); \
+ } else { \
+ if (RB_PARENT(elm, field) && \
+ (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \
+ elm = RB_PARENT(elm, field); \
+ else { \
+ while (RB_PARENT(elm, field) && \
+ (elm == RB_LEFT(RB_PARENT(elm, field), field)))\
+ elm = RB_PARENT(elm, field); \
+ elm = RB_PARENT(elm, field); \
+ } \
+ } \
+ return (elm); \
+} \
+ \
attr struct type * \
name##_RB_MINMAX(struct name *head, int val) \
{ \
}
#define RB_NEGINF -1
+#define RB_INF 1
#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y)
#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y)
#define RB_FIND(name, x, y) name##_RB_FIND(x, y)
+#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y)
#define RB_NEXT(name, x, y) name##_RB_NEXT(y)
+#define RB_PREV(name, x, y) name##_RB_PREV(y)
#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF)
+#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF)
+
+#define RB_FOREACH(x, name, head) \
+ for ((x) = RB_MIN(name, head); \
+ (x) != NULL; \
+ (x) = name##_RB_NEXT(x))
+
+#define RB_FOREACH_FROM(x, name, y) \
+ for ((x) = (y); \
+ ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \
+ (x) = (y))
+
+#define RB_FOREACH_SAFE(x, name, head, y) \
+ for ((x) = RB_MIN(name, head); \
+ ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \
+ (x) = (y))
+
+#define RB_FOREACH_REVERSE(x, name, head) \
+ for ((x) = RB_MAX(name, head); \
+ (x) != NULL; \
+ (x) = name##_RB_PREV(x))
+
+#define RB_FOREACH_REVERSE_FROM(x, name, y) \
+ for ((x) = (y); \
+ ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \
+ (x) = (y))
+
+#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \
+ for ((x) = RB_MAX(name, head); \
+ ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \
+ (x) = (y))
+
+#endif /* _SYS_TREE_H_ */