vnstat-1.14/0000755000000000000000000000000012517211132011447 5ustar rootrootvnstat-1.14/INSTALL_BSD0000644000000000000000000000612512517206712013204 0ustar rootroot Compiling the binaries :::::::::::::::::::::: This source package contains the required source files for vnStat including the daemon (vnstatd) and image output (vnstati). Executing gmake will compile 'vnstat' and 'vnstatd' without requiring additional libraries. The optional image output however requires libgd2 to be available. In BSD ports libgd2 is usually named simply 'gd'. Executing gmake all will compile everything including the image output support. An example cgi ('vnstat.cgi') to be used with http server with the image output support has been provided in the 'examples' directory. Configuration options for the cgi are in the beginning of the file. Some BSD users may need to use LIBRARY_PATH="/usr/local/lib" CPATH="/usr/local/include" gmake all instead if libgd2 is installed but still not found. Installing as root :::::::::::::::::: Login as root and run the following command: gmake bsdinstall If there were no errors, vnStat binaries, man pages and a config file should now be installed. The configuration file /etc/vnstat.conf should also be checked at this point. See the vnstat.conf man page for documentation about available options. Finally make vnStat monitor the selected interface(s). Configure init scripts so that the following command is executed once during system start: vnstatd -d One suitable place is for example /etc/rc.local. During first startup, the daemon (vnstatd) should list and add all available interfaces for monitoring. Depending on configuration, it may take some minutes for the 'vnstat' command to begin showing results. Monitoring of unwanted interfaces can be stopped with: vnstat --delete -i ethunwanted Installing without root access :::::::::::::::::::::::::::::: Copy all needed binaries to some directory included in your PATH (~/bin/ is an example) and make the database directory. cp src/vnstat src/vnstatd src/vnstati ~/bin/ cp cfg/vnstat.conf ~/.vnstatrc mkdir ~/.vnstat Next open the config file ~/.vnstatrc with your favorite text editor and locate the following line: DatabaseDir "/var/lib/vnstat" and replace it with DatabaseDir "/pathtomyhomedir/.vnstat" and save the file. If you are unsure about your home directory path, execute cd ; pwd The ouput should tell your home directory. Now it's time to add a crontab entry for vnStat. Do that by executing the command 'crontab -e' and add the following line (without leading spaces, remember to change the path): @reboot ~/bin/vnstatd -d If you found yourself using a strange editor then 'man vi' should help. Make sure the configuration file (~/.vnstatrc) has the log option either disabled or set to a file that is located in a place where you have write permissions, such as your home dir. Then try starting the daemon with vnstat -d After that wait for (or generate) at least 1024 bytes of network traffic (and 5 min for the next database file save). vnstat Now you should get some stats about your network usage. See the config file ~/.vnstatrc for interface and other settings. vnstat-1.14/examples/0000755000000000000000000000000012455271265013303 5ustar rootrootvnstat-1.14/examples/vnstat-json.cgi0000755000000000000000000000141212455271265016256 0ustar rootroot#!/usr/bin/perl -w # vnstat-json.cgi -- example cgi for vnStat json output # copyright (c) 2015 Teemu Toivola # released under the GNU General Public License # location of vnstat binary my $vnstat_cmd = '/usr/bin/vnstat'; # shown interfaces, edit as necessary my @interfaces = ('eth0', 'eth1', 'ethX'); ################ my $iface = ""; my $getiface = ""; my @values = split(/&/,$ENV{'QUERY_STRING'}); foreach $i (@values) { ($varname, $varvalue) = split(/=/,$i); if ($varname == 'interface' && $varvalue =~ /^(\d+)$/) { $getiface = $varvalue; } } if (length($getiface) > 0 && $getiface >= 0 && $getiface <= $#interfaces) { $iface = "-i @interfaces[$getiface]"; } print "Content-Type: application/json\n\n"; exec("$vnstat_cmd --json $iface"); vnstat-1.14/examples/launchd/0000755000000000000000000000000012316622471014714 5ustar rootrootvnstat-1.14/examples/launchd/net.humdi.vnstat.plist0000644000000000000000000000116712317002176021202 0ustar rootroot Label net.humdi.vnstat ProgramArguments /usr/local/libexec/vnstatd -n KeepAlive NetworkState RunAtLoad WorkingDirectory / ProcessType Background vnstat-1.14/examples/systemd/0000755000000000000000000000000012317464750014773 5ustar rootrootvnstat-1.14/examples/systemd/vnstat.service0000644000000000000000000000037012317464750017674 0ustar rootroot[Unit] Description=vnStat network traffic monitor Documentation=man:vnstatd(1) man:vnstat(1) man:vnstat.conf(5) After=network.target [Service] ExecStart=/usr/sbin/vnstatd -n ExecReload=/bin/kill -HUP $MAINPID [Install] WantedBy=multi-user.target vnstat-1.14/examples/upstart/0000755000000000000000000000000012372464015015000 5ustar rootrootvnstat-1.14/examples/upstart/vnstat.conf0000644000000000000000000000024412372464102017163 0ustar rootrootdescription "vnStat network traffic monitor" start on runlevel [2345] stop on runlevel [016] or deconfiguring-networking or unmounting-filesystem exec vnstatd -n vnstat-1.14/examples/init.d/0000755000000000000000000000000012352076647014473 5ustar rootrootvnstat-1.14/examples/init.d/centos0000777000000000000000000000000012302723260017061 2redhatustar rootrootvnstat-1.14/examples/init.d/redhat/0000755000000000000000000000000012302723260015723 5ustar rootrootvnstat-1.14/examples/init.d/redhat/vnstat0000755000000000000000000000267112302723260017176 0ustar rootroot#! /bin/sh # # chkconfig: 2345 20 50 # description: vnStat - a lightweight network traffic monitor # processname: vnstatd # config: /etc/vnstat.conf ### BEGIN INIT INFO # Provides: vnstat # Required-Start: $local_fs $remote_fs $network # Required-Stop: $local_fs $remote_fs $network # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: lightweight network traffic monitor ### END INIT INFO VNSTATD_BIN=/usr/sbin/vnstatd [ -x $VNSTATD_BIN ] || exit 0 # Source function library . /etc/rc.d/init.d/functions RETVAL=0 prog=vnstatd pidfile=/var/run/vnstat.pid start() { echo -n $"Starting $prog: " if [ -e "$pidfile" ] && [ -e /proc/`cat "$pidfile"` ]; then echo -n $"already running."; success "$prog is already running."; echo return 0 fi daemon $VNSTATD_BIN -d RETVAL=$? echo return $RETVAL } stop() { echo -n $"Shutting down $prog: " killproc $VNSTATD_BIN RETVAL=$? echo rm -f $pidfile return $RETVAL } reload() { echo -n $"Reloading $prog configuration: " killproc $VNSTATD_BIN -HUP RETVAL=$? echo return $RETVAL } case "$1" in start) start ;; stop) stop ;; reload) reload ;; restart) stop start ;; try-restart) if [ -f $pidfile ]; then stop start fi ;; force-reload) reload || (stop; start) ;; status) status $prog RETVAL=$? ;; *) echo $"Usage: $0 {start|stop|reload|force-reload|restart|try-restart|status}" RETVAL=3 esac exit $RETVAL vnstat-1.14/examples/init.d/debian/0000755000000000000000000000000012302723260015676 5ustar rootrootvnstat-1.14/examples/init.d/debian/vnstat0000755000000000000000000000504212302723260017144 0ustar rootroot#! /bin/sh ### BEGIN INIT INFO # Provides: vnstat # Required-Start: $local_fs $remote_fs $network # Required-Stop: $local_fs $remote_fs $network # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: lightweight network traffic monitor ### END INIT INFO PATH=/usr/sbin:/usr/bin:/sbin:/bin DESC="vnStat daemon" NAME=vnstatd DAEMON=/usr/sbin/$NAME DAEMON_ARGS="-d" PIDFILE=/var/run/vnstat.pid SCRIPTNAME=/etc/init.d/vnstat # Exit if the package is not installed [ -x "$DAEMON" ] || exit 0 # # Function that starts the daemon/service # do_start() { # Return # 0 if daemon has been started # 1 if daemon was already running # 2 if daemon could not be started start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ || return 1 start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \ $DAEMON_ARGS \ || return 2 } # # Function that stops the daemon/service # do_stop() { # Return # 0 if daemon has been stopped # 1 if daemon was already stopped # 2 if daemon could not be stopped # other if a failure occurred start-stop-daemon --stop --quiet --retry=TERM/15/KILL/5 --pidfile $PIDFILE --name $NAME RETVAL="$?" [ "$RETVAL" = 2 ] && return 2 rm -f $PIDFILE return "$RETVAL" } # # Function that sends a SIGHUP to the daemon/service # do_reload() { start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME return 0 } do_status() { # Return # 0 if daemon is stopped # 1 if daemon is running start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ || return 1 } case "$1" in start) echo -n "Starting $DESC: $NAME" do_start case "$?" in 0) echo "." ;; 1) echo " already running." ;; 2) echo " start failed." ; exit 1 ;; esac ;; stop) echo -n "Stopping $DESC: $NAME" do_stop case "$?" in 0) echo "." ;; 1) echo " already stopped." ;; 2) echo " stop failed." ; exit 1 ;; esac ;; status) echo -n "Checking $DESC: " do_status case "$?" in 0) echo "stopped." ; exit 3 ;; 1) echo "running." ;; esac ;; reload|force-reload) echo -n "Reloading $DESC configuration..." do_reload echo "done." ;; restart) echo -n "Restarting $DESC: $NAME" do_stop case "$?" in 0|1) do_start case "$?" in 0) echo "." ;; 1) echo " old process is still running." ;; *) echo " start failed." ; exit 1 ;; esac ;; *) echo " stop failed." exit 1 ;; esac ;; *) echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload|status}" >&2 exit 1 ;; esac exit 0 vnstat-1.14/examples/init.d/ubuntu0000777000000000000000000000000012302723260017063 2debianustar rootrootvnstat-1.14/examples/vnstat.cgi0000755000000000000000000001213312411261305015273 0ustar rootroot#!/usr/bin/perl -w # vnstat.cgi -- example cgi for vnStat image output # copyright (c) 2008-2014 Teemu Toivola # # based on mailgraph.cgi # copyright (c) 2000-2007 ETH Zurich # copyright (c) 2000-2007 David Schweikert # released under the GNU General Public License my $servername = 'Some Server'; my $scriptname = 'vnstat.cgi'; # temporary directory where to store the images my $tmp_dir = '/tmp/vnstatcgi'; # location of vnstati my $vnstati_cmd = '/usr/bin/vnstati'; # image cache time in minutes, set 0 to disable my $cachetime = '15'; # shown interfaces, remove unnecessary lines my @graphs = ( { interface => 'eth0' }, { interface => 'eth1' }, ); # center images on page instead of left alignment, set 0 to disable my $aligncenter = '1'; # page background color my $bgcolor = "white"; ################ my $VERSION = "1.4"; my $cssbody = "body { background-color: $bgcolor; }"; sub graph($$$) { my ($interface, $file, $param) = @_; my $result = `"$vnstati_cmd" -i "$interface" -c $cachetime $param -o "$file"`; } sub print_html() { print "Content-Type: text/html\n\n"; print < Traffic Statistics for $servername HEADER for my $n (0..$#graphs) { print "

\"$graphs[$n]{interface}

\n"; } print <Images generated using vnStat image output. FOOTER } sub print_fullhtml($) { my ($interface) = @_; print "Content-Type: text/html\n\n"; print < Traffic Statistics for $servername HEADER print "\n
\n"; print "\"${interface}"; print "\n"; print "\"${interface}"; print "
\n"; print "\"${interface}"; print "\n"; print "\"${interface}
\n"; print "\"${interface}"; print "
\n"; print <
 Images generated using vnStat image output. FOOTER } sub send_image($) { my ($file)= @_; -r $file or do { print "Content-type: text/plain\n\nERROR: can't find $file\n"; exit 1; }; print "Content-type: image/png\n"; print "Content-length: ".((stat($file))[7])."\n"; print "\n"; open(IMG, $file) or die; my $data; print $data while read(IMG, $data, 16384)>0; } sub main() { if($aligncenter != '0') { $cssbody = "html { display: table; width: 100%; }\nbody { background-color: $bgcolor; display: table-cell; text-align: center; vertical-align: middle; }\ntable { margin-left: auto; margin-right: auto; margin-top: 10px; }"; } mkdir $tmp_dir, 0755 unless -d $tmp_dir; my $img = $ENV{QUERY_STRING}; if(defined $img and $img =~ /\S/) { if($img =~ /^(\d+)-s$/) { my $file = "$tmp_dir/vnstat_$1.png"; graph($graphs[$1]{interface}, $file, "-s"); send_image($file); } elsif($img =~ /^(\d+)-hs$/) { my $file = "$tmp_dir/vnstat_$1_hs.png"; graph($graphs[$1]{interface}, $file, "-hs"); send_image($file); } elsif($img =~ /^(\d+)-d$/) { my $file = "$tmp_dir/vnstat_$1_d.png"; graph($graphs[$1]{interface}, $file, "-d"); send_image($file); } elsif($img =~ /^(\d+)-m$/) { my $file = "$tmp_dir/vnstat_$1_m.png"; graph($graphs[$1]{interface}, $file, "-m"); send_image($file); } elsif($img =~ /^(\d+)-t$/) { my $file = "$tmp_dir/vnstat_$1_t.png"; graph($graphs[$1]{interface}, $file, "-t"); send_image($file); } elsif($img =~ /^(\d+)-h$/) { my $file = "$tmp_dir/vnstat_$1_h.png"; graph($graphs[$1]{interface}, $file, "-h"); send_image($file); } elsif($img =~ /^(\d+)-f$/) { print_fullhtml($1); } else { die "ERROR: invalid argument\n"; } } else { if ($#graphs == 0) { print_fullhtml(0); } else { print_html(); } } } main(); vnstat-1.14/examples/vnstat-json.php0000644000000000000000000000134512455271026016300 0ustar rootroot */ /* released under the GNU General Public License */ /* list of available interfaces, edit as necessary */ $interfaces = array("eth0", "eth1", "ethX"); /* location of vnstat binary */ $vnstat_cmd = "/usr/bin/vnstat"; /* no editing should be needed below this line */ $iface = ""; $getiface = ""; if (isset($_GET['interface']) && ctype_digit($_GET['interface'])) { $getiface = $_GET['interface']; } if (strlen($getiface) > 0 && $getiface >= 0 && $getiface < count($interfaces)) { $iface = " -i ".$interfaces[$getiface]; } header("Content-Type: application/json"); passthru($vnstat_cmd." --json".$iface); ?> vnstat-1.14/UNINSTALL0000644000000000000000000000033312517202246012750 0ustar rootroot If the daemon is running then stop it first and remove any manually installed service files. Linux: Run 'make uninstall' and follow the instructions. BSD: Run 'make bsduninstall' and follow the instructions. vnstat-1.14/README0000644000000000000000000000443212517202243012335 0ustar rootroot What is vnStat :::::::::::::: vnStat is a console-based network traffic monitor that uses the network interface statistics provided by the kernel as information source. This means that vnStat won't actually be sniffing any traffic and also ensures light use of system resources. Traffic statistics are stored on a hourly level for the last 24 hours, on a daily level for the last 30 days and on a monthly level for the last 12 months. Total seen traffic and a top 10 days listing is also provided. Optional png image output is available in systems with the gd library installed. See the webpage http://humdi.net/vnstat/ for output examples. Getting started ::::::::::::::: vnStat works best when installed. See the INSTALL or INSTALL_BSD file depending on used operating system. Experimental instructions for OS X are available in the INSTALL_OSX file. These files contain all needed information for the installing process. Instructions for upgrading from a previous version are included in the UPGRADE file. Available options and documentation ::::::::::::::::::::::::::::::::::: A list of commonly used options is available with 'vnstat --help'. The complete list can be accessed with 'vnstat --longhelp'. The following man pages are available with more detailed descriptions of each option including some usage examples: vnstat general documentation and console query parameters vnstatd daemon documentation vnstati image output documentation vnstat.conf configuration file Usage tips :::::::::: Most vnStat settings can be changed from the config file. The config file is either ~/.vnstatrc or /etc/vnstat.conf depending which one is found first. A new config file can be generated using current settings with the following command: vnstat --showconfig >newconfig The config file contains comments before any settings describing with the purpose and usage of each setting is. More detailed descriptions about each setting and suggested usage can be found from the vnstat.conf man page. Contacting the author ::::::::::::::::::::: email: Teemu Toivola irc: Vergo (IRCNet) git: https://github.com/vergoh/vnstat The current version of vnStat is always available from http://humdi.net/vnstat/ vnstat-1.14/Makefile0000644000000000000000000001431312452606771013130 0ustar rootroot# bin and man dirs for Linux BIN ?= $(DESTDIR)/usr/bin SBIN ?= $(DESTDIR)/usr/sbin MAN ?= $(DESTDIR)/usr/share/man ETC ?= $(DESTDIR)/etc/ # bin and man dirs for *BSD BIN_BSD ?= $(DESTDIR)/usr/local/bin SBIN_BSD ?= $(DESTDIR)/usr/local/sbin MAN_BSD ?= $(DESTDIR)/usr/local/man ETC_BSD ?= $(DESTDIR)/etc .PHONY: vnstat tests check all clean debug install uninstall bsdinstall bsduninstall dist default: vnstat vnstat: $(MAKE) -C src tests: $(MAKE) -C tests check: tests all: $(MAKE) -C src all clean: $(MAKE) -C src clean $(MAKE) -C tests clean debug: $(MAKE) CFLAGS='-Wall -Wextra -g' -C src all install: @echo "Installing vnStat..." # check that system is really Linux @if [ `uname` != "Linux" ]; \ then echo "This isn't a Linux system. Maybe 'make bsdinstall' is what you need?"; \ false; \ fi # check that there's something to install @if [ ! -f "src/vnstat" ] || [ ! -f "src/vnstatd" ]; \ then echo "Nothing to install, run make first."; \ false; \ fi # move some really old version database(s) if found @if [ -d "$(DESTDIR)/var/spool/vnstat" ]; \ then echo "Moving old database(s) to new location..."; \ mv -f $(DESTDIR)/var/spool/vnstat $(DESTDIR)/var/lib/; \ fi # remove some really old version binary if found @if [ -x "$(DESTDIR)/usr/local/bin/vnstat" ]; \ then echo "Removing old binary..."; \ rm -f $(DESTDIR)/usr/local/bin/vnstat; \ fi # install default config if such doesn't exist @if [ ! -f "$(ETC)/vnstat.conf" ]; \ then echo "Installing config to $(ETC)/vnstat.conf"; \ install -d -m 755 $(ETC); \ install -m 644 cfg/vnstat.conf $(ETC)/vnstat.conf; \ fi # install everything else install -d -m 755 $(BIN) $(SBIN) $(MAN)/man1 $(MAN)/man5 install -s -m 755 src/vnstat $(BIN) install -s -m 755 src/vnstatd $(SBIN) @if [ -f "src/vnstati" ]; \ then echo install -s -m 755 src/vnstati $(BIN); \ install -s -m 755 src/vnstati $(BIN); \ fi # update man pages, gzip it if previous version was done so install -m 644 man/vnstat.1 $(MAN)/man1 install -m 644 man/vnstatd.1 $(MAN)/man1 install -m 644 man/vnstat.conf.5 $(MAN)/man5 @if [ -f "src/vnstati" ]; \ then echo install -m 644 man/vnstati.1 $(MAN)/man1; \ install -m 644 man/vnstati.1 $(MAN)/man1; \ fi @if [ -f "$(MAN)/man1/vnstat.1.gz" ]; \ then gzip -f9 $(MAN)/man1/vnstat.1; \ gzip -f9 $(MAN)/man1/vnstatd.1; \ gzip -f9 $(MAN)/man5/vnstat.conf.5; \ if [ -f "src/vnstati" ]; \ then gzip -f9 $(MAN)/man1/vnstati.1; \ fi; \ fi # remove vnstat.conf.1 is such exists in the wrong place @if [ -f "$(MAN)/man1/vnstat.conf.1.gz" ]; \ then rm -f $(MAN)/man1/vnstat.conf.1.gz; \ fi @if [ -f "$(MAN)/man1/vnstat.conf.1" ]; \ then rm -f $(MAN)/man1/vnstat.conf.1; \ fi @echo " " @echo "No service file or startup script has been installed. See the" @echo "INSTALL document for instructions on how to enable vnStat." uninstall: @echo "Uninstalling vnStat..." @echo @echo "Note: this will also remove the database directory" @echo "including any database located there." @echo @echo "Press CTRL-C within 10 seconds to abort." @sleep 10 rm -fr $(DESTDIR)/var/lib/vnstat rm -f $(BIN)/vnstat rm -f $(BIN)/vnstati rm -f $(SBIN)/vnstatd rm -f $(MAN)/man1/vnstat* rm -f $(MAN)/man5/vnstat* rm -f $(DESTDIR)/etc/cron.d/vnstat rm -f $(ETC)/vnstat.conf rm -f $(DESTDIR)/etc/ppp/ip-up.d/vnstat rm -f $(DESTDIR)/etc/ppp/ip-down.d/vnstat bsdinstall: @echo "Installing vnStat (BSD)..." # check that system isn't Linux @if [ `uname` = "Linux" ]; \ then echo "This is a Linux system. You shouldn't be using 'bsdinstall'"; \ false; \ fi # check that there's something to install @if [ ! -f "src/vnstat" ] || [ ! -f "src/vnstatd" ]; \ then echo "Nothing to install, run make first."; \ false; \ fi # install binaries install -d -m 755 $(DESTDIR)/var/db/vnstat install -s -m 755 src/vnstat $(BIN_BSD) install -s -m 755 src/vnstatd $(SBIN_BSD) @if [ -f "src/vnstati" ]; \ then echo install -s -m 755 src/vnstati $(BIN_BSD); \ install -s -m 755 src/vnstati $(BIN_BSD); \ fi # install default config if such doesn't exist @if [ ! -f "$(ETC_BSD)/vnstat.conf" ]; \ then echo "Installing config to $(ETC_BSD)/vnstat.conf"; \ install -d -m 755 $(ETC_BSD); \ install -m 644 cfg/vnstat.conf $(ETC_BSD)/vnstat.conf; \ sed -e 's:/lib/:/db/:g' $(ETC_BSD)/vnstat.conf >$(ETC_BSD)/vnstat.conf.bsd; \ mv -f $(ETC_BSD)/vnstat.conf.bsd $(ETC_BSD)/vnstat.conf; \ fi # update man page for m in vnstat.1 vnstati.1 vnstatd.1 vnstat.conf.5; do \ sed -e 's:/lib/:/db/:g' man/$$m > man/$$m.tmp; \ mv -f man/$$m.tmp man/$$m; \ done install -m 644 man/vnstat.1 $(MAN_BSD)/man1 install -m 644 man/vnstatd.1 $(MAN_BSD)/man1 install -m 644 man/vnstat.conf.5 $(MAN_BSD)/man5 gzip -f9 $(MAN_BSD)/man1/vnstat.1 gzip -f9 $(MAN_BSD)/man1/vnstatd.1 gzip -f9 $(MAN_BSD)/man5/vnstat.conf.5 @if [ -f "src/vnstati" ]; \ then echo install -m 644 man/vnstati.1 $(MAN_BSD)/man1; \ install -m 644 man/vnstati.1 $(MAN_BSD)/man1; \ echo gzip -f9 $(MAN_BSD)/man1/vnstati.1; \ gzip -f9 $(MAN_BSD)/man1/vnstati.1; \ fi # remove vnstat.conf.1 is such exists in the wrong place @if [ -f "$(MAN_BSD)/man1/vnstat.conf.1.gz" ]; \ then rm -f $(MAN_BSD)/man1/vnstat.conf.1.gz; \ fi @if [ -f "$(MAN_BSD)/man1/vnstat.conf.1" ]; \ then rm -f $(MAN_BSD)/man1/vnstat.conf.1; \ fi @echo " " @echo "No service file or startup script has been installed. See the" @echo "INSTALL_BSD document for instructions on how to enable vnStat." bsduninstall: @echo "Uninstalling vnStat (BSD)..." @echo @echo "Note: this will also remove the database directory" @echo "including any database located there." @echo @echo "Press CTRL-C within 10 seconds to abort." @sleep 10 rm -fr $(DESTDIR)/var/db/vnstat rm -f $(BIN_BSD)/vnstat rm -f $(BIN_BSD)/vnstati rm -f $(SBIN_BSD)/vnstatd rm -f $(MAN_BSD)/man1/vnstat* rm -f $(MAN_BSD)/man5/vnstat* rm -f $(ETC_BSD)/vnstat.conf @echo "A possible cron entry needs to be removed manually if such exists." dist: clean $(eval VER := $(shell grep VNSTATVERSION src/common.h | cut -d\" -f2 | sed 's: :_:g')) @echo @echo "Packaging $(VER)..." @rm -fr "../vnstat-$(VER).tar.gz" "../vnstat-$(VER)" @mkdir "../vnstat-$(VER)" @cp -ax * "../vnstat-$(VER)/" @fakeroot tar zcf "../vnstat-$(VER).tar.gz" "../vnstat-$(VER)" @rm -fr "../vnstat-$(VER)" @ls -l "../vnstat-$(VER).tar.gz" vnstat-1.14/INSTALL0000644000000000000000000001123612517206706012516 0ustar rootroot Compiling the binaries :::::::::::::::::::::: This source package contains the required source files for vnStat including the daemon (vnstatd) and image output (vnstati). Executing make will compile 'vnstat' and 'vnstatd' without requiring additional libraries. The optional image output however requires libgd2 to be available. The required extra packages are usually named libgd2 and libgd2-dev (or libgd2-noxpm and libgd2-noxpm-dev in Debian/Ubuntu, xpm version can also be used, libgd-dev in more recent releases). Executing make all will compile everything including the image output support. An example cgi ('vnstat.cgi') to be used with http server with the image output support has been provided in the 'examples' directory. Configuration options for the cgi are in the beginning of the file. Installing as root :::::::::::::::::: Login as root and run the following command: make install If there were no errors, vnStat binaries, man pages and a config file should now be installed. Next the used kernel should be checked that it is able to provide static information about the system boot time. This has mainly been an issue in some kernels from the 2.4 series. vnstat --testkernel The only way to fix a faulty kernel (afaik) is to compile/install a newer one. The configuration file /etc/vnstat.conf should also be checked at this point. See the vnstat.conf man page for documentation about available options. Finally, make vnStat monitor available interfaces. Configure init scripts so that the following command is executed once during system start: vnstatd -d The 'examples' directory contains suitable files for most commonly used service managers: systemd: cp -v examples/systemd/vnstat.service /etc/systemd/system/ systemctl enable vnstat systemctl start vnstat upstart: cp -v examples/upstart/vnstat.conf /etc/init/ initctl start vnstat init.d: Debian: cp -v examples/init.d/debian/vnstat /etc/init.d/ update-rc.d vnstat defaults service vnstat start Red Hat / CentOS: cp -v examples/init.d/redhat/vnstat /etc/init.d/ chkconfig vnstat on service vnstat start An alternative method is to just add the command to an already existing script that gets executed during startup. In many distributions /etc/rc.local can be used if nothing else suitable can be found. During first startup, the daemon (vnstatd) should list and add all available interfaces for monitoring. Depending on configuration, it may take some minutes for the 'vnstat' command to begin showing results. Monitoring of unwanted interfaces can be stopped with: vnstat --delete -i ethunwanted Installing without root access :::::::::::::::::::::::::::::: Copy all needed binaries to some directory included in your PATH (~/bin/ is an example) and make the database directory. cp src/vnstat src/vnstatd src/vnstati ~/bin/ cp cfg/vnstat.conf ~/.vnstatrc mkdir ~/.vnstat During version 1.0 it became clear that some kernels are broken and don't provide to correct boot time for the system. vnStat requires this information so there's a test to see if the kernel is working correctly. vnstat --testkernel If this gives 'command not found' then check content of your PATH variable. The only way to fix a faulty kernel (afaik) is to compile/install a newer one. This can't be done without the root account so you'll have to solve this problem with your sysadmin if the kernel is broken. Now open the config file ~/.vnstatrc with your favorite text editor and locate the following line: DatabaseDir "/var/lib/vnstat" and replace it with DatabaseDir "/pathtomyhomedir/.vnstat" and save the file. If you are unsure about your home directory path, execute cd ; pwd The ouput should tell your home directory. Now it's time to add a crontab entry for vnStat in order to get the daemon running automatically after a system startup. Do that by executing the command 'crontab -e' and add the following line (without leading spaces, remember to change the path): @reboot ~/bin/vnstatd -d If you found yourself using a strange editor then 'man vi' should help. Make sure the configuration file (~/.vnstatrc) has the log option either disabled or set to a file that is located in a place where you have write permissions, such as your home dir. Then try starting the daemon with vnstat -d After that wait for (or generate) at least 1024 bytes of network traffic (and 5 min for the next database file save). vnstat Now you should get some stats about your network usage. See the config file ~/.vnstatrc for interface and other settings. vnstat-1.14/src/0000755000000000000000000000000012517211114012236 5ustar rootrootvnstat-1.14/src/dbxml.h0000644000000000000000000000034712463170065013532 0ustar rootroot#ifndef DBXML_H #define DBXML_H void showxml(char mode); void xmldays(void); void xmlmonths(void); void xmltops(void); void xmlhours(void); void xmldate(time_t *date, int type); void xmlheader(void); void xmlfooter(void); #endif vnstat-1.14/src/dbxml.c0000644000000000000000000000576412463170056013535 0ustar rootroot#include "common.h" #include "dbxml.h" void showxml(char mode) { printf(" \n", data.interface); printf(" %s\n", data.interface); printf(" %s\n", data.nick); printf(" "); xmldate(&data.created, 1); printf("\n"); printf(" "); xmldate(&data.lastupdated, 2); printf("\n"); printf(" \n"); printf(" %"PRIu64"%"PRIu64"\n", (data.totalrx*1024)+data.totalrxk, (data.totaltx*1024)+data.totaltxk); switch (mode) { case 'd': xmldays(); break; case 'm': xmlmonths(); break; case 't': xmltops(); break; case 'h': xmlhours(); break; case 'a': default: xmldays(); printf(","); xmlmonths(); printf(","); xmltops(); printf(","); xmlhours(); break; } printf(" \n"); printf(" \n"); } void xmldays(void) { int i; printf(" \n"); for (i=0;i<=29;i++) { if (data.day[i].used) { printf(" ", i); xmldate(&data.day[i].date, 1); printf("%"PRIu64"%"PRIu64"\n", (data.day[i].rx*1024)+data.day[i].rxk, (data.day[i].tx*1024)+data.day[i].txk); } } printf(" \n"); } void xmlmonths(void) { int i; printf(" \n"); for (i=0;i<=11;i++) { if (data.month[i].used) { printf(" ", i); xmldate(&data.month[i].month, 3); printf("%"PRIu64"%"PRIu64"\n", (data.month[i].rx*1024)+data.month[i].rxk, (data.month[i].tx*1024)+data.month[i].txk); } } printf(" \n"); } void xmltops(void) { int i; printf(" \n"); for (i=0;i<=9;i++) { if (data.top10[i].used) { printf(" ", i); xmldate(&data.top10[i].date, 2); printf("%"PRIu64"%"PRIu64"\n", (data.top10[i].rx*1024)+data.top10[i].rxk, (data.top10[i].tx*1024)+data.top10[i].txk); } } printf(" \n"); } void xmlhours(void) { int i; printf(" \n"); for (i=0;i<=23;i++) { if (data.hour[i].date!=0) { printf(" ", i); xmldate(&data.hour[i].date, 1); printf("%"PRIu64"%"PRIu64"\n", data.hour[i].rx, data.hour[i].tx); } } printf(" \n"); } void xmldate(time_t *date, int type) { struct tm *d; char *type1 = "%d%02d%02d"; char *type2 = "%d%02d%02d"; char *type3 = "%d%02d"; d = localtime(date); if (type == 1) { printf(type1, 1900+d->tm_year, 1+d->tm_mon, d->tm_mday); } else if (type == 2) { printf(type2, 1900+d->tm_year, 1+d->tm_mon, d->tm_mday, d->tm_hour, d->tm_min); } else if (type == 3) { printf(type3, 1900+d->tm_year, 1+d->tm_mon); } } void xmlheader(void) { printf("\n", VNSTATVERSION, XMLVERSION); } void xmlfooter(void) { printf("\n"); } vnstat-1.14/src/cfg.c0000644000000000000000000004647312506570641013172 0ustar rootroot#include "common.h" #include "cfg.h" void printcfgfile(void) { ibwnode *p = ifacebw; printf("# vnStat %s config file\n", VNSTATVERSION); printf("##\n\n"); printf("# default interface\n"); printf("Interface \"%s\"\n\n", cfg.iface); printf("# location of the database directory\n"); printf("DatabaseDir \"%s\"\n\n", cfg.dbdir); printf("# locale (LC_ALL) (\"-\" = use system locale)\n"); printf("Locale \"%s\"\n\n", cfg.locale); printf("# on which day should months change\n"); printf("MonthRotate %d\n\n", cfg.monthrotate); printf("# date output formats for -d, -m, -t and -w\n"); printf("# see 'man date' for control codes\n"); printf("DayFormat \"%s\"\n", cfg.dformat); printf("MonthFormat \"%s\"\n", cfg.mformat); printf("TopFormat \"%s\"\n\n", cfg.tformat); printf("# characters used for visuals\n"); printf("RXCharacter \"%c\"\n", cfg.rxchar[0]); printf("TXCharacter \"%c\"\n", cfg.txchar[0]); printf("RXHourCharacter \"%c\"\n", cfg.rxhourchar[0]); printf("TXHourCharacter \"%c\"\n\n", cfg.txhourchar[0]); printf("# how units are prefixed when traffic is shown\n"); printf("# 0 = IEC standard prefixes (KiB/MiB/GiB/TiB)\n"); printf("# 1 = old style binary prefixes (KB/MB/GB/TB)\n"); printf("UnitMode %d\n\n", cfg.unit); printf("# output style\n"); printf("# 0 = minimal & narrow, 1 = bar column visible\n"); printf("# 2 = same as 1 except rate in summary and weekly\n"); printf("# 3 = rate column visible\n"); printf("OutputStyle %d\n\n", cfg.ostyle); printf("# used rate unit (0 = bytes, 1 = bits)\n"); printf("RateUnit %d\n\n", cfg.rateunit); printf("# try to detect interface maximum bandwidth, 0 = disable feature\n"); printf("# MaxBandwidth will be used as fallback value when enabled\n"); printf("BandwidthDetection %d\n\n", cfg.bwdetection); printf("# maximum bandwidth (Mbit) for all interfaces, 0 = disable feature\n# (unless interface specific limit is given)\n"); printf("MaxBandwidth %d\n\n", cfg.maxbw); printf("# interface specific limits\n"); printf("# example 8Mbit limit for eth0 (remove # to activate):\n"); printf("#MaxBWeth0 8\n"); while (p != NULL) { printf("MaxBW%s %d\n", p->interface, p->limit); p = p->next; } printf("\n"); printf("# how many seconds should sampling for -tr take by default\n"); printf("Sampletime %d\n\n", cfg.sampletime); printf("# default query mode\n"); printf("# 0 = normal, 1 = days, 2 = months, 3 = top10\n"); printf("# 4 = exportdb, 5 = short, 6 = weeks, 7 = hours\n"); printf("QueryMode %d\n\n", cfg.qmode); printf("# filesystem disk space check (1 = enabled, 0 = disabled)\n"); printf("CheckDiskSpace %d\n\n", cfg.spacecheck); printf("# database file locking (1 = enabled, 0 = disabled)\n"); printf("UseFileLocking %d\n\n", cfg.flock); printf("# how much the boot time can variate between updates (seconds)\n"); printf("BootVariation %d\n\n", cfg.bvar); printf("# log days without traffic to daily list (1 = enabled, 0 = disabled)\n"); printf("TrafficlessDays %d\n", cfg.traflessday); printf("\n\n"); printf("# vnstatd\n##\n\n"); printf("# switch to given user when started as root (leave empty to disable)\n"); printf("DaemonUser \"%s\"\n\n", cfg.daemonuser); printf("# switch to given user when started as root (leave empty to disable)\n"); printf("DaemonGroup \"%s\"\n\n", cfg.daemongroup); printf("# how often (in seconds) interface data is updated\n"); printf("UpdateInterval %d\n\n", cfg.updateinterval); printf("# how often (in seconds) interface status changes are checked\n"); printf("PollInterval %d\n\n", cfg.pollinterval); printf("# how often (in minutes) data is saved to file\n"); printf("SaveInterval %d\n\n", cfg.saveinterval); printf("# how often (in minutes) data is saved when all interface are offline\n"); printf("OfflineSaveInterval %d\n\n", cfg.offsaveinterval); printf("# how often (in minutes) bandwidth detection is redone when\n"); printf("# BandwidthDetection is enabled (0 = disabled)\n"); printf("BandwidthDetectionInterval %d\n\n", cfg.bwdetectioninterval); printf("# force data save when interface status changes (1 = enabled, 0 = disabled)\n"); printf("SaveOnStatusChange %d\n\n", cfg.savestatus); printf("# enable / disable logging (0 = disabled, 1 = logfile, 2 = syslog)\n"); printf("UseLogging %d\n\n", cfg.uselogging); printf("# create dirs if needed (1 = enabled, 0 = disabled)\n"); printf("CreateDirs %d\n\n", cfg.createdirs); printf("# update ownership of files if needed (1 = enabled, 0 = disabled)\n"); printf("UpdateFileOwner %d\n\n", cfg.updatefileowner); printf("# file used for logging if UseLogging is set to 1\n"); printf("LogFile \"%s\"\n\n", cfg.logfile); printf("# file used as daemon pid / lock file\n"); printf("PidFile \"%s\"\n", cfg.pidfile); printf("\n\n"); printf("# vnstati\n##\n\n"); printf("# title timestamp format\n"); printf("HeaderFormat \"%s\"\n\n", cfg.hformat); printf("# show hours with rate (1 = enabled, 0 = disabled)\n"); printf("HourlyRate %d\n\n", cfg.hourlyrate); printf("# show rate in summary (1 = enabled, 0 = disabled)\n"); printf("SummaryRate %d\n\n", cfg.summaryrate); printf("# layout of summary (1 = with monthly, 0 = without monthly)\n"); printf("SummaryLayout %d\n\n", cfg.slayout); printf("# transparent background (1 = enabled, 0 = disabled)\n"); printf("TransparentBg %d\n\n", cfg.transbg); printf("# image colors\n"); printf("CBackground \"%s\"\n", cfg.cbg); printf("CEdge \"%s\"\n", cfg.cedge); printf("CHeader \"%s\"\n", cfg.cheader); printf("CHeaderTitle \"%s\"\n", cfg.cheadertitle); printf("CHeaderDate \"%s\"\n", cfg.cheaderdate); printf("CText \"%s\"\n", cfg.ctext); printf("CLine \"%s\"\n", cfg.cline); printf("CLineL \"%s\"\n", cfg.clinel); printf("CRx \"%s\"\n", cfg.crx); printf("CTx \"%s\"\n", cfg.ctx); printf("CRxD \"%s\"\n", cfg.crxd); printf("CTxD \"%s\"\n", cfg.ctxd); } int loadcfg(const char *cfgfile) { FILE *fd; int i, linelen, cfglen; char value[512], cfgline[512]; struct cfgsetting cset[] = { /* cfg string, char var name, int var name, char len, fill status */ { "Interface", cfg.iface, 0, 32, 0 }, { "DatabaseDir", cfg.dbdir, 0, 512, 0 }, { "Locale", cfg.locale, 0, 32, 0 }, { "MonthRotate", 0, &cfg.monthrotate, 0, 0 }, { "DayFormat", cfg.dformat, 0, 64, 0 }, { "MonthFormat", cfg.mformat, 0, 64, 0 }, { "TopFormat", cfg.tformat, 0, 64, 0 }, { "RXCharacter", cfg.rxchar, 0, 2, 0 }, { "TXCharacter", cfg.txchar, 0, 2, 0 }, { "RXHourCharacter", cfg.rxhourchar, 0, 2, 0 }, { "TXHourCharacter", cfg.txhourchar, 0, 2, 0 }, { "UnitMode", 0, &cfg.unit, 0, 0 }, { "OutputStyle", 0, &cfg.ostyle, 0, 0 }, { "RateUnit", 0, &cfg.rateunit, 0, 0 }, { "BandwidthDetection", 0, &cfg.bwdetection, 0, 0 }, { "MaxBandwidth", 0, &cfg.maxbw, 0, 0 }, { "Sampletime", 0, &cfg.sampletime, 0, 0 }, { "QueryMode", 0, &cfg.qmode, 0, 0 }, { "CheckDiskSpace", 0, &cfg.spacecheck, 0, 0 }, { "UseFileLocking", 0, &cfg.flock, 0, 0 }, { "BootVariation", 0, &cfg.bvar, 0, 0 }, { "TrafficlessDays", 0, &cfg.traflessday, 0, 0 }, { "DaemonUser", cfg.daemonuser, 0, 33, 0 }, { "DaemonGroup", cfg.daemongroup, 0, 33, 0 }, { "UpdateInterval", 0, &cfg.updateinterval, 0, 0 }, { "PollInterval", 0, &cfg.pollinterval, 0, 0 }, { "SaveInterval", 0, &cfg.saveinterval, 0, 0 }, { "OfflineSaveInterval", 0, &cfg.offsaveinterval, 0, 0 }, { "BandwidthDetectionInterval", 0, &cfg.bwdetectioninterval, 0, 0 }, { "SaveOnStatusChange", 0, &cfg.savestatus, 0, 0 }, { "UseLogging", 0, &cfg.uselogging, 0, 0 }, { "CreateDirs", 0, &cfg.createdirs, 0, 0 }, { "UpdateFileOwner", 0, &cfg.updatefileowner, 0, 0 }, { "LogFile", cfg.logfile, 0, 512, 0 }, { "PidFile", cfg.pidfile, 0, 512, 0 }, { "HeaderFormat", cfg.hformat, 0, 64, 0 }, { "HourlyRate", 0, &cfg.hourlyrate, 0, 0 }, { "SummaryRate", 0, &cfg.summaryrate, 0, 0 }, { "SummaryLayout", 0, &cfg.slayout, 0, 0 }, { "TransparentBg", 0, &cfg.transbg, 0, 0 }, { "CBackground", cfg.cbg, 0, 8, 0 }, { "CEdge", cfg.cedge, 0, 8, 0 }, { "CHeader", cfg.cheader, 0, 8, 0 }, { "CHeaderTitle", cfg.cheadertitle, 0, 8, 0 }, { "CHeaderDate", cfg.cheaderdate, 0, 8, 0 }, { "CText", cfg.ctext, 0, 8, 0 }, { "CLine", cfg.cline, 0, 8, 0 }, { "CLineL", cfg.clinel, 0, 8, 0 }, { "CRx", cfg.crx, 0, 8, 0 }, { "CRxD", cfg.crxd, 0, 8, 0 }, { "CTx", cfg.ctx, 0, 8, 0 }, { "CTxD", cfg.ctxd, 0, 8, 0 }, { 0, 0, 0, 0, 0 } }; /* load default config */ defaultcfg(); i = opencfgfile(cfgfile, &fd); if (i != 2) return i; rewind(fd); /* parse every config file line */ while (!feof(fd)) { cfgline[0] = '\0'; if (fgets(cfgline, 512, fd)==NULL) { break; } linelen = (int)strlen(cfgline); if (linelen<=2 || cfgline[0]=='#') { continue; } for (i=0; cset[i].name!=0; i++) { if (cset[i].found) { continue; } cfglen = (int)strlen(cset[i].name); if ( (linelen<(cfglen+2)) || (strncasecmp(cfgline, cset[i].name, cfglen)!=0) ) { continue; } if (!extractcfgvalue(value, cfgline, cfglen)) { if (debug) printf(" c: %s -> \"%s\" with no value, keeping default.\n", cfgline, cset[i].name); cset[i].found = 1; break; } if (!setcfgvalue(&cset[i], value, cfgline)) { continue; } cset[i].found = 1; break; } if ((debug) && (!cset[i].found) && (strncasecmp(cfgline, "MaxBW", 5)!=0)) printf("Unknown configuration line: %s", cfgline); } fclose(fd); /* validate config */ validatecfg(); return 1; } void validatecfg(void) { if (cfg.unit<0 || cfg.unit>1) { cfg.unit = UNITMODE; snprintf(errorstring, 512, "Invalid value for UnitMode, resetting to \"%d\".", cfg.unit); printe(PT_Config); } if (cfg.ostyle<0 || cfg.ostyle>3) { cfg.ostyle = OSTYLE; snprintf(errorstring, 512, "Invalid value for OutputStyle, resetting to \"%d\".", cfg.ostyle); printe(PT_Config); } if (cfg.bvar<0 || cfg.bvar>300) { cfg.bvar = BVAR; snprintf(errorstring, 512, "Invalid value for BootVariation, resetting to \"%d\".", cfg.bvar); printe(PT_Config); } if (cfg.sampletime<2 || cfg.sampletime>600) { cfg.sampletime = DEFSAMPTIME; snprintf(errorstring, 512, "Invalid value for Sampletime, resetting to \"%d\".", cfg.sampletime); printe(PT_Config); } if (cfg.monthrotate<1 || cfg.monthrotate>28) { cfg.monthrotate = MONTHROTATE; snprintf(errorstring, 512, "Invalid value for MonthRotate, resetting to \"%d\".", cfg.monthrotate); printe(PT_Config); } if (cfg.maxbw<0 || cfg.maxbw>BWMAX) { cfg.maxbw = DEFMAXBW; snprintf(errorstring, 512, "Invalid value for MaxBandwidth, resetting to \"%d\".", cfg.maxbw); printe(PT_Config); } if (cfg.spacecheck<0 || cfg.spacecheck>1) { cfg.spacecheck = USESPACECHECK; snprintf(errorstring, 512, "Invalid value for CheckDiskSpace, resetting to \"%d\".", cfg.spacecheck); printe(PT_Config); } if (cfg.flock<0 || cfg.flock>1) { cfg.flock = USEFLOCK; snprintf(errorstring, 512, "Invalid value for UseFileLocking, resetting to \"%d\".", cfg.flock); printe(PT_Config); } if (cfg.dbdir[0] != '/') { strncpy_nt(cfg.dbdir, DATABASEDIR, 512); snprintf(errorstring, 512, "DatabaseDir doesn't start with \"/\", resetting to default."); printe(PT_Config); } if (cfg.pollinterval<2 || cfg.pollinterval>60) { cfg.pollinterval = POLLINTERVAL; snprintf(errorstring, 512, "Invalid value for PollInterval, resetting to \"%d\".", cfg.pollinterval); printe(PT_Config); } if (cfg.updateinterval300) { if (cfg.pollinterval>UPDATEINTERVAL) { cfg.updateinterval = cfg.pollinterval; } else { cfg.updateinterval = UPDATEINTERVAL; } snprintf(errorstring, 512, "Invalid value for UpdateInterval, resetting to \"%d\".", cfg.updateinterval); printe(PT_Config); } if ((cfg.saveinterval*60)60) { if (cfg.updateinterval>(SAVEINTERVAL*60)) { cfg.saveinterval = cfg.updateinterval; } else { cfg.saveinterval = SAVEINTERVAL; } snprintf(errorstring, 512, "Invalid value for SaveInterval, resetting to \"%d\".", cfg.saveinterval); printe(PT_Config); } if (cfg.offsaveinterval60) { if (cfg.saveinterval>OFFSAVEINTERVAL) { cfg.offsaveinterval = cfg.saveinterval; } else { cfg.offsaveinterval = OFFSAVEINTERVAL; } snprintf(errorstring, 512, "Invalid value for OfflineSaveInterval, resetting to \"%d\".", cfg.offsaveinterval); printe(PT_Config); } if (cfg.savestatus<0 || cfg.savestatus>1) { cfg.savestatus = SAVESTATUS; snprintf(errorstring, 512, "Invalid value for SaveOnStatusChange, resetting to \"%d\".", cfg.savestatus); printe(PT_Config); } if (cfg.uselogging<0 || cfg.uselogging>2) { cfg.uselogging = USELOGGING; snprintf(errorstring, 512, "Invalid value for UseLogging, resetting to \"%d\".", cfg.uselogging); printe(PT_Config); } if (cfg.createdirs<0 || cfg.createdirs>2) { cfg.createdirs = CREATEDIRS; snprintf(errorstring, 512, "Invalid value for CreateDirs, resetting to \"%d\".", cfg.createdirs); printe(PT_Config); } if (cfg.updatefileowner<0 || cfg.updatefileowner>2) { cfg.updatefileowner = UPDATEFILEOWNER; snprintf(errorstring, 512, "Invalid value for UpdateFileOwner, resetting to \"%d\".", cfg.updatefileowner); printe(PT_Config); } if (cfg.logfile[0] != '/') { strncpy_nt(cfg.logfile, LOGFILE, 512); snprintf(errorstring, 512, "LogFile doesn't start with \"/\", resetting to default."); printe(PT_Config); } if (cfg.pidfile[0] != '/') { strncpy_nt(cfg.pidfile, PIDFILE, 512); snprintf(errorstring, 512, "PidFile doesn't start with \"/\", resetting to default."); printe(PT_Config); } if (cfg.transbg<0 || cfg.transbg>1) { cfg.transbg = TRANSBG; snprintf(errorstring, 512, "Invalid value for TransparentBg, resetting to \"%d\".", cfg.transbg); printe(PT_Config); } if (cfg.hourlyrate<0 || cfg.hourlyrate>1) { cfg.hourlyrate = HOURLYRATE; snprintf(errorstring, 512, "Invalid value for HourlyRate, resetting to \"%d\".", cfg.hourlyrate); printe(PT_Config); } if (cfg.summaryrate<0 || cfg.summaryrate>1) { cfg.summaryrate = SUMMARYRATE; snprintf(errorstring, 512, "Invalid value for SummaryRate, resetting to \"%d\".", cfg.summaryrate); printe(PT_Config); } if (cfg.slayout<0 || cfg.slayout>1) { cfg.slayout = SUMMARYLAYOUT; snprintf(errorstring, 512, "Invalid value for SummaryLayout, resetting to \"%d\".", cfg.slayout); printe(PT_Config); } if (cfg.traflessday<0 || cfg.traflessday>1) { cfg.traflessday = TRAFLESSDAY; snprintf(errorstring, 512, "Invalid value for TrafficlessDays, resetting to \"%d\".", cfg.transbg); printe(PT_Config); } if (cfg.bwdetection<0 || cfg.bwdetection>1) { cfg.bwdetection = BWDETECT; snprintf(errorstring, 512, "Invalid value for BandwidthDetection, resetting to \"%d\".", cfg.bwdetection); printe(PT_Config); } if (cfg.bwdetectioninterval<0 || cfg.bwdetectioninterval>30) { cfg.bwdetectioninterval = BWDETECTINTERVAL; snprintf(errorstring, 512, "Invalid value for BandwidthDetectionInterval, resetting to \"%d\".", cfg.bwdetectioninterval); printe(PT_Config); } } void defaultcfg(void) { ifacebw = NULL; cfg.bvar = BVAR; cfg.qmode = DEFQMODE; cfg.sampletime = DEFSAMPTIME; cfg.monthrotate = MONTHROTATE; cfg.unit = UNITMODE; cfg.ostyle = OSTYLE; cfg.rateunit = RATEUNIT; cfg.bwdetection = BWDETECT; cfg.bwdetectioninterval = BWDETECTINTERVAL; cfg.maxbw = DEFMAXBW; cfg.spacecheck = USESPACECHECK; cfg.flock = USEFLOCK; cfg.hourlyrate = HOURLYRATE; cfg.summaryrate = SUMMARYRATE; cfg.slayout = SUMMARYLAYOUT; cfg.traflessday = TRAFLESSDAY; cfg.utflocale = UTFLOCALE; strncpy_nt(cfg.dbdir, DATABASEDIR, 512); strncpy_nt(cfg.iface, DEFIFACE, 32); strncpy_nt(cfg.locale, LOCALE, 32); strncpy_nt(cfg.dformat, DFORMAT, 64); strncpy_nt(cfg.mformat, MFORMAT, 64); strncpy_nt(cfg.tformat, TFORMAT, 64); strncpy_nt(cfg.hformat, HFORMAT, 64); strncpy_nt(cfg.rxchar, RXCHAR, 2); strncpy_nt(cfg.txchar, TXCHAR, 2); strncpy_nt(cfg.rxhourchar, RXHOURCHAR, 2); strncpy_nt(cfg.txhourchar, TXHOURCHAR, 2); cfg.daemonuser[0] = '\0'; cfg.daemongroup[0] = '\0'; cfg.updateinterval = UPDATEINTERVAL; cfg.pollinterval = POLLINTERVAL; cfg.saveinterval = SAVEINTERVAL; cfg.offsaveinterval = OFFSAVEINTERVAL; cfg.savestatus = SAVESTATUS; cfg.uselogging = USELOGGING; cfg.createdirs = CREATEDIRS; cfg.updatefileowner = UPDATEFILEOWNER; strncpy_nt(cfg.logfile, LOGFILE, 512); strncpy_nt(cfg.pidfile, PIDFILE, 512); cfg.transbg = TRANSBG; strncpy_nt(cfg.cbg, CBACKGROUND, 8); strncpy_nt(cfg.cedge, CEDGE, 8); strncpy_nt(cfg.cheader, CHEADER, 8); strncpy_nt(cfg.cheadertitle, CHEADERTITLE, 8); strncpy_nt(cfg.cheaderdate, CHEADERDATE, 8); strncpy_nt(cfg.ctext, CTEXT, 8); strncpy_nt(cfg.cline, CLINE, 8); strncpy_nt(cfg.clinel, CLINEL, 8); strncpy_nt(cfg.crx, CRX, 8); strncpy_nt(cfg.crxd, CRXD, 8); strncpy_nt(cfg.ctx, CTX, 8); strncpy_nt(cfg.ctxd, CTXD, 8); } int opencfgfile(const char *cfgfile, FILE **fd) { char buffer[512]; int i, tryhome; /* clear buffer */ for (i=0; i<512; i++) { buffer[i] = '\0'; } /* possible config files: 1) --config 2) $HOME/.vnstatrc 3) /etc/vnstat.conf 4) none */ if (cfgfile[0]!='\0') { /* try to open given file */ if ((*fd=fopen(cfgfile, "r"))!=NULL) { if (debug) printf("Config file: --config\n"); } else { snprintf(errorstring, 512, "Unable to open given config file \"%s\": %s\n", cfgfile, strerror(errno)); printe(PT_Error); return 0; } } else { if (getenv("HOME")) { strncpy_nt(buffer, getenv("HOME"), 500); strcat(buffer, "/.vnstatrc"); tryhome = 1; } else { tryhome = 0; } /* try to open first available config file */ if (tryhome && (*fd=fopen(buffer, "r"))!=NULL) { if (debug) printf("Config file: $HOME/.vnstatrc\n"); } else if ((*fd=fopen("/etc/vnstat.conf", "r"))!=NULL) { if (debug) printf("Config file: /etc/vnstat.conf\n"); } else if ((*fd=fopen("/usr/local/etc/vnstat.conf", "r"))!=NULL) { if (debug) printf("Config file: /usr/local/etc/vnstat.conf\n"); } else { if (debug) printf("Config file: none\n"); return 1; } } return 2; } int extractcfgvalue(char *value, const char *cfgline, int cfglen) { int i, j, linelen; linelen = (int)strlen(cfgline); for (i=0; i<512; i++) { value[i]='\0'; } i = 0; for (j=cfglen; jnamelen>0) { strncpy_nt(cset->locc, value, cset->namelen); cset->locc[cset->namelen-1]='\0'; if (debug) printf(" c: %s -> \"%s\": \"%s\"\n", cfgline, cset->name, cset->locc); } else if (isdigit(value[0])) { *cset->loci = strtol(value, (char **)NULL, 0); if (debug) printf(" i: %s -> \"%s\": %d\n", cfgline, cset->name, *cset->loci); } else { return 0; } return 1; } void configlocale(void) { if (cfg.locale[0]!='-' && strlen(cfg.locale)>0) { setlocale(LC_ALL, cfg.locale); } else { if (getenv("LC_ALL")) { setlocale(LC_ALL, getenv("LC_ALL")); } else { setlocale(LC_ALL, ""); } } if (getenv("LC_ALL")) { if (strstr(getenv("LC_ALL"), "UTF") != NULL) { cfg.utflocale = 1; } else { cfg.utflocale = 0; } } } vnstat-1.14/src/ifinfo.c0000644000000000000000000003257512506045367013705 0ustar rootroot#include "common.h" #include "misc.h" #include "dbaccess.h" #include "cfg.h" #include "ibw.h" #include "ifinfo.h" int getifinfo(const char *iface) { char inface[32]; ifinfo.filled = 0; if (strcmp(iface, "default")==0) { strncpy_nt(inface, cfg.iface, 32); } else { strncpy_nt(inface, iface, 32); } #if defined(__linux__) /* try getting interface info from /proc */ if (readproc(inface)!=1) { if (debug) printf("Failed to use %s as source.\n", PROCNETDEV); /* try getting interface info from /sys */ if (readsysclassnet(inface)!=1) { snprintf(errorstring, 512, "Unable to get interface \"%s\" statistics.", inface); printe(PT_Error); return 0; } } #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) || defined(__FreeBSD_kernel__) if (readifaddrs(inface)!=1) { snprintf(errorstring, 512, "Unable to get interface \"%s\" statistics.", inface); printe(PT_Error); return 0; } #else return 0; #endif return 1; } int getiflist(char **ifacelist, int showspeed) { uint32_t speed; char temp[64]; #if defined(__linux__) char interface[32]; FILE *fp; DIR *dp; struct dirent *di; char procline[512]; #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) || defined(__FreeBSD_kernel__) struct ifaddrs *ifap, *ifa; #endif /* initialize list */ *ifacelist = malloc(sizeof(char)); if (*ifacelist == NULL) { panicexit(__FILE__, __LINE__); } *ifacelist[0] = '\0'; #if defined(__linux__) if ((fp=fopen(PROCNETDEV, "r"))!=NULL) { /* make list of interfaces */ while (fgets(procline, 512, fp)!=NULL) { sscanf(procline, "%63s", temp); if (strlen(temp)>0 && (isdigit(temp[(strlen(temp)-1)]) || temp[(strlen(temp)-1)]==':')) { sscanf(temp, "%31[^':']s", interface); *ifacelist = realloc(*ifacelist, ( ( strlen(*ifacelist) + strlen(interface) + 2 ) * sizeof(char)) ); if (*ifacelist == NULL) { panicexit(__FILE__, __LINE__); } strncat(*ifacelist, interface, strlen(interface)); strcat(*ifacelist, " "); if (!showspeed) { continue; } speed = getifspeed(interface); if (speed > 0) { snprintf(temp, 64, "(%u Mbit) ", speed); *ifacelist = realloc(*ifacelist, ( ( strlen(*ifacelist) + strlen(temp) + 1 ) * sizeof(char)) ); if (*ifacelist == NULL) { panicexit(__FILE__, __LINE__); } strncat(*ifacelist, temp, strlen(temp)); } } } fclose(fp); return 1; } else { if ((dp=opendir(SYSCLASSNET))!=NULL) { /* make list of interfaces */ while ((di=readdir(dp))) { if (di->d_name[0]!='.') { *ifacelist = realloc(*ifacelist, ( ( strlen(*ifacelist) + strlen(di->d_name) + 2 ) * sizeof(char)) ); if (*ifacelist == NULL) { panicexit(__FILE__, __LINE__); } strncat(*ifacelist, di->d_name, strlen(di->d_name)); strcat(*ifacelist, " "); if (!showspeed) { continue; } speed = getifspeed(di->d_name); if (speed > 0) { snprintf(temp, 64, "(%u Mbit) ", speed); *ifacelist = realloc(*ifacelist, ( ( strlen(*ifacelist) + strlen(temp) + 1 ) * sizeof(char)) ); if (*ifacelist == NULL) { panicexit(__FILE__, __LINE__); } strncat(*ifacelist, temp, strlen(temp)); } } } closedir(dp); return 1; } } #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) || defined(__FreeBSD_kernel__) if (getifaddrs(&ifap) >= 0) { /* make list of interfaces */ for (ifa = ifap; ifa; ifa = ifa->ifa_next) { if (ifa->ifa_addr->sa_family == AF_LINK) { *ifacelist = realloc(*ifacelist, ( ( strlen(*ifacelist) + strlen(ifa->ifa_name) + 2 ) * sizeof(char)) ); if (*ifacelist == NULL) { panicexit(__FILE__, __LINE__); } strncat(*ifacelist, ifa->ifa_name, strlen(ifa->ifa_name)); strcat(*ifacelist, " "); if (!showspeed) { continue; } speed = getifspeed(ifa->ifa_name); if (speed > 0) { snprintf(temp, 64, "(%u Mbit) ", speed); *ifacelist = realloc(*ifacelist, ( ( strlen(*ifacelist) + strlen(temp) + 1 ) * sizeof(char)) ); if (*ifacelist == NULL) { panicexit(__FILE__, __LINE__); } strncat(*ifacelist, temp, strlen(temp)); } } } freeifaddrs(ifap); return 1; } #endif return 0; } int readproc(const char *iface) { FILE *fp; char temp[4][64], procline[512], *proclineptr, ifaceid[33]; int check; if ((fp=fopen(PROCNETDEV, "r"))==NULL) { if (debug) printf("Error: Unable to read %s: %s\n", PROCNETDEV, strerror(errno)); return 0; } strncpy_nt(ifaceid, iface, 32); strcat(ifaceid, ":"); check = 0; while (fgets(procline, 512, fp)!=NULL) { sscanf(procline, "%511s", temp[0]); if (strncmp(ifaceid, temp[0], strlen(ifaceid))==0) { /* if (debug) printf("\n%s\n", procline); */ check = 1; break; } } fclose(fp); if (check==0) { if (debug) printf("Requested interface \"%s\" not found.\n", iface); return 0; } else { strncpy_nt(ifinfo.name, iface, 32); /* get rx and tx from procline */ proclineptr = strchr(procline, ':'); sscanf(proclineptr+1, "%63s %63s %*s %*s %*s %*s %*s %*s %63s %63s", temp[0], temp[1], temp[2], temp[3]); ifinfo.rx = strtoull(temp[0], (char **)NULL, 0); ifinfo.tx = strtoull(temp[2], (char **)NULL, 0); /* daemon doesn't need packet data */ if (!noexit) { ifinfo.rxp = strtoull(temp[1], (char **)NULL, 0); ifinfo.txp = strtoull(temp[3], (char **)NULL, 0); } ifinfo.filled = 1; } return 1; } int readsysclassnet(const char *iface) { FILE *fp; char path[64], file[76], buffer[64]; strncpy_nt(ifinfo.name, iface, 32); snprintf(path, 64, "%s/%s/statistics", SYSCLASSNET, iface); if (debug) printf("path: %s\n", path); /* rx bytes */ snprintf(file, 76, "%s/rx_bytes", path); if ((fp=fopen(file, "r"))==NULL) { if (debug) printf("Unable to read: %s - %s\n", file, strerror(errno)); return 0; } else { if (fgets(buffer, 64, fp)!=NULL) { ifinfo.rx = strtoull(buffer, (char **)NULL, 0); } else { fclose(fp); return 0; } } fclose(fp); /* tx bytes */ snprintf(file, 76, "%s/tx_bytes", path); if ((fp=fopen(file, "r"))==NULL) { if (debug) printf("Unable to read: %s - %s\n", file, strerror(errno)); return 0; } else { if (fgets(buffer, 64, fp)!=NULL) { ifinfo.tx = strtoull(buffer, (char **)NULL, 0); } else { fclose(fp); return 0; } } fclose(fp); /* daemon doesn't need packet data */ if (!noexit) { /* rx packets */ snprintf(file, 76, "%s/rx_packets", path); if ((fp=fopen(file, "r"))==NULL) { if (debug) printf("Unable to read: %s - %s\n", file, strerror(errno)); return 0; } else { if (fgets(buffer, 64, fp)!=NULL) { ifinfo.rxp = strtoull(buffer, (char **)NULL, 0); } else { fclose(fp); return 0; } } fclose(fp); /* tx packets */ snprintf(file, 76, "%s/tx_packets", path); if ((fp=fopen(file, "r"))==NULL) { if (debug) printf("Unable to read: %s - %s\n", file, strerror(errno)); return 0; } else { if (fgets(buffer, 64, fp)!=NULL) { ifinfo.txp = strtoull(buffer, (char **)NULL, 0); } else { fclose(fp); return 0; } } fclose(fp); } ifinfo.filled = 1; return 1; } void parseifinfo(int newdb) { uint64_t rxchange=0, txchange=0, btime, cc; /* rxchange = rx change in MB */ uint64_t krxchange=0, ktxchange=0, maxtransfer; /* krxchange = rx change in kB */ time_t current, interval; struct tm *d; int day, month, year, hour, min, shift, maxbw; int rxkchange=0, txkchange=0; /* changes in the kB counters */ ifinfo.rxp = ifinfo.txp = 0; current=time(NULL); interval=current-data.lastupdated; btime=getbtime(); /* count traffic only if previous update wasn't too long ago */ if ( interval < (60*MAXUPDATEINTERVAL) ) { /* btime in /proc/stat seems to vary ±1 second so we use btime-BVAR just to be safe */ /* the variation is also slightly different between various kernels... */ if (data.btime < (btime-cfg.bvar)) { data.currx=0; data.curtx=0; if (debug) printf("System has been booted.\n"); } /* process rx & tx */ if (newdb!=1) { cc = countercalc(&data.currx, &ifinfo.rx); rxchange = cc/1048576; /* 1024/1024 */ rxkchange = (cc/1024)%1024; krxchange = cc/1024; ifinfo.rxp = cc%1024; cc = countercalc(&data.curtx, &ifinfo.tx); txchange = cc/1048576; /* 1024/1024 */ txkchange = (cc/1024)%1024; ktxchange = cc/1024; ifinfo.txp = cc%1024; } /* get bandwidth limit for current interface */ maxbw = ibwget(data.interface); if (maxbw > 0) { /* calculate maximum possible transfer since last update based on set maximum rate */ /* and add 10% in order to be on the safe side */ maxtransfer = ceil((maxbw/(float)8)*interval*(float)1.1); if (debug) printf("interval: %"PRIu64" maxbw: %d maxrate: %"PRIu64" rxc: %"PRIu64" txc: %"PRIu64"\n", (uint64_t)interval, maxbw, maxtransfer, rxchange, txchange); /* sync counters if traffic is greater than set maximum */ if ( (rxchange > maxtransfer) || (txchange > maxtransfer) ) { snprintf(errorstring, 512, "Traffic rate for \"%s\" higher than set maximum %d Mbit (%"PRIu64"->%"PRIu64", r%"PRIu64" t%"PRIu64"), syncing.", data.interface, maxbw, (uint64_t)interval, maxtransfer, rxchange, txchange); printe(PT_Info); rxchange = krxchange = rxkchange = txchange = ktxchange = txkchange = 0; ifinfo.rxp = ifinfo.txp = 0; } } } else { if (debug) printf("Too much time passed since previous update, syncing. (%"PRIu64" < %d)\n", (uint64_t)interval, 60*MAXUPDATEINTERVAL); } /* keep btime updated in case it drifts slowly */ data.btime = btime; data.currx = ifinfo.rx - ifinfo.rxp; data.curtx = ifinfo.tx - ifinfo.txp; addtraffic(&data.totalrx, &data.totalrxk, rxchange, rxkchange); addtraffic(&data.totaltx, &data.totaltxk, txchange, txkchange); /* update days and months */ addtraffic(&data.day[0].rx, &data.day[0].rxk, rxchange, rxkchange); addtraffic(&data.day[0].tx, &data.day[0].txk, txchange, txkchange); addtraffic(&data.month[0].rx, &data.month[0].rxk, rxchange, rxkchange); addtraffic(&data.month[0].tx, &data.month[0].txk, txchange, txkchange); /* fill some variables from current date & time */ d=localtime(¤t); if (d==NULL) { panicexit(__FILE__, __LINE__); } day=d->tm_mday; month=d->tm_mon; year=d->tm_year; hour=d->tm_hour; min=d->tm_min; shift=hour; /* add traffic to previous hour when update happens at X:00 */ /* and previous update was during previous hour */ d=localtime(&data.lastupdated); if (d==NULL) { panicexit(__FILE__, __LINE__); } if ((min==0) && (d->tm_hour!=hour) && ((current-data.lastupdated)<=3600)) { hour--; if (hour<0) { hour=23; } } /* clean and update hourly */ cleanhours(); data.hour[shift].date=current; /* avoid shifting timestamp */ data.hour[hour].rx+=krxchange; data.hour[hour].tx+=ktxchange; /* rotate days in database if needed */ d=localtime(&data.day[0].date); if (d==NULL) { panicexit(__FILE__, __LINE__); } if ((d->tm_mday!=day) || (d->tm_mon!=month) || (d->tm_year!=year)) { /* make a new entry only if there's something to remember (configuration dependent) */ if ( (data.day[0].rx==0) && (data.day[0].tx==0) && (data.day[0].rxk==0) && (data.day[0].txk==0) && (cfg.traflessday==0) ) { data.day[0].date=current; } else { rotatedays(); } } /* rotate months in database if needed */ d=localtime(&data.month[0].month); if (d==NULL) { panicexit(__FILE__, __LINE__); } if ((d->tm_mon!=month) && (day>=cfg.monthrotate)) { rotatemonths(); } } #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) || defined(__FreeBSD_kernel__) int getifdata(const char *iface, struct if_data *ifd) { struct ifaddrs *ifap, *ifa; int check = 0; if (getifaddrs(&ifap) < 0) { if (debug) printf("readifaddrs:getifaddrs() failed.\n"); return 0; } for (ifa = ifap; ifa; ifa = ifa->ifa_next) { if ((strcmp(ifa->ifa_name, iface) == 0) && (ifa->ifa_addr->sa_family == AF_LINK)) { if (ifa->ifa_data != NULL) { memcpy(ifd, ifa->ifa_data, sizeof(struct if_data)); check = 1; } break; } } freeifaddrs(ifap); return check; } int readifaddrs(const char *iface) { struct if_data ifd; if (!getifdata(iface, &ifd)) { if (debug) printf("Requested interface \"%s\" not found.\n", iface); return 0; } else { strncpy_nt(ifinfo.name, iface, 32); ifinfo.rx = ifd.ifi_ibytes; ifinfo.tx = ifd.ifi_obytes; ifinfo.rxp = ifd.ifi_ipackets; ifinfo.txp = ifd.ifi_opackets; ifinfo.filled = 1; } return 1; } #endif uint32_t getifspeed(const char *iface) { uint32_t speed = 0; #if defined(__linux__) FILE *fp; char file[64], buffer[64]; snprintf(file, 64, "%s/%s/speed", SYSCLASSNET, iface); if ((fp=fopen(file, "r"))==NULL) { if (debug) printf("Unable to open: %s - %s\n", file, strerror(errno)); return 0; } else { if (fgets(buffer, 64, fp)!=NULL) { speed = strtoul(buffer, (char **)NULL, 0); } else { if (debug) printf("Unable to read: %s - %s\n", file, strerror(errno)); fclose(fp); return 0; } } fclose(fp); #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) || defined(__FreeBSD_kernel__) struct if_data ifd; if (!getifdata(iface, &ifd)) { if (debug) printf("Requested interface \"%s\" not found.\n", iface); return 0; } else { speed = ifd.ifi_baudrate; } #endif if (debug) printf("getifspeed: \"%s\": %d\n", iface, speed); if (speed > 1000000) { speed = 0; } return speed; } vnstat-1.14/src/dbshow.h0000644000000000000000000000055612461023305013704 0ustar rootroot#ifndef DBSHOW_H #define DBSHOW_H void showdb(int qmode); void showsummary(void); void showshort(void); void showdays(void); void showmonths(void); void showtop(void); void showweeks(void); void showhours(void); void showoneline(void); void exportdb(void); int showbar(uint64_t rx, int rxk, uint64_t tx, int txk, uint64_t max, int len); void indent(int i); #endif vnstat-1.14/src/image.h0000644000000000000000000000337412410752713013507 0ustar rootroot#ifndef IMAGE_H #define IMAGE_H #include /* libgd2-dev libgd2 */ #include /* gdFontGetTiny() */ #include /* gdFontGetSmall() */ #include /* gdFontGetMediumBold() */ #include /* gdFontGetLarge() */ #include /* gdFontGetGiant() */ /* rectangle size */ #define YBEGINOFFSET -1 #define YENDOFFSET 6 /* donut size */ #define DOUTRAD 49 #define DINRAD 15 typedef struct { gdImagePtr im; int cbackground, cedge, cheader, cheadertitle, cheaderdate, ctext, cline, clinel, cvnstat; int crx, crxd, ctx, ctxd, cbgoffset, showheader, showedge, showlegend, altdate; char headertext[65]; time_t current; } IMAGECONTENT; void initimagecontent(IMAGECONTENT *ic); void drawimage(IMAGECONTENT *ic); void colorinit(IMAGECONTENT *ic); void colorinitcheck(char *color, int value, char *cfgtext, int *rgb); void layoutinit(IMAGECONTENT *ic, char *title, int width, int height); void drawlegend(IMAGECONTENT *ic, int x, int y); void drawbar(IMAGECONTENT *ic, int x, int y, int len, uint64_t rx, int rxk, uint64_t tx, int txk, uint64_t max); void drawpole(IMAGECONTENT *ic, int x, int y, int len, uint64_t rx, uint64_t tx, uint64_t max); void drawdonut(IMAGECONTENT *ic, int x, int y, float rxp, float txp); void drawhours(IMAGECONTENT *ic, int x, int y, int rate); void drawsummary(IMAGECONTENT *ic, int type, int rate); void drawoldsummary(IMAGECONTENT *ic, int type, int rate); void drawhourly(IMAGECONTENT *ic, int rate); void drawdaily(IMAGECONTENT *ic); void drawmonthly(IMAGECONTENT *ic); void drawtop(IMAGECONTENT *ic); void hextorgb(char *input, int *rgb); void modcolor(int *rgb, int offset, int force); char *getimagevalue(uint64_t kb, int len, int rate); char *getimagescale(uint64_t kb, int rate); #endif vnstat-1.14/src/daemon.h0000644000000000000000000000230712376402626013671 0ustar rootroot#ifndef DAEMON_H #define DAEMON_H typedef struct { int running, updateinterval, dbcount, dodbsave, rundaemon; int dbsaved, showhelp, sync, saveinterval, forcesave, noadd; uint32_t dbhash; char cfgfile[512], dirname[512]; char user[33], group[33]; time_t current, prevdbupdate, prevdbsave; datanode *datalist; } DSTATE; void daemonize(void); int addinterfaces(const char *dirname); void debugtimestamp(void); uid_t getuser(const char *user); gid_t getgroup(const char *group); void setuser(const char *user); void setgroup(const char *group); int direxists(const char *dir); int mkpath(const char *dir, const mode_t mode); void initdstate(DSTATE *s); void preparedatabases(DSTATE *s); void setsignaltraps(void); void filldatabaselist(DSTATE *s); void adjustsaveinterval(DSTATE *s); void checkdbsaveneed(DSTATE *s); void processdatalist(DSTATE *s); int datalist_cacheget(DSTATE *s); void datalist_getifinfo(DSTATE *s); int datalist_timevalidation(DSTATE *s); int datalist_writedb(DSTATE *s); void handleintsignals(DSTATE *s); void preparedirs(DSTATE *s); void preparevnstatdir(const char *dir, const char *user, const char *group); void updatedirowner(const char *dir, const char *user, const char *group); #endif vnstat-1.14/src/ifinfo.h0000644000000000000000000000120312453710706013667 0ustar rootroot#ifndef IFINFO_H #define IFINFO_H #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) || defined(__FreeBSD_kernel__) #include #endif int getifinfo(const char *iface); int getiflist(char **ifacelist, int showspeed); int readproc(const char *iface); int readsysclassnet(const char *iface); void parseifinfo(int newdb); #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) || defined(__FreeBSD_kernel__) int getifdata(const char *iface, struct if_data *ifd); int readifaddrs(const char *iface); #endif uint32_t getifspeed(const char *iface); #endif vnstat-1.14/src/dbcache.c0000644000000000000000000001405712453551561013776 0ustar rootroot#include "common.h" #include "ifinfo.h" #include "dbaccess.h" #include "dbcache.h" #include "cfg.h" #include "ibw.h" int cacheadd(const char *iface, int sync) { datanode *p, *n; p = dataptr; /* skip if already in list */ while (p != NULL) { if (strcmp(p->data.interface, iface)==0) { if (debug) { printf("cache: %s already cached\n", iface); } return 1; } p = p->next; } /* add new node if not in list */ n = malloc(sizeof(datanode)); if (n == NULL) { return 0; } n->next = dataptr; dataptr = n; strncpy_nt(n->data.interface, iface, 32); n->data.interface[31] = '\0'; n->data.active = 1; n->filled = 0; n->sync = sync; if (debug) { printf("cache: %s added\n", iface); } return 1; } datanode *cacheremove(const char *iface) { datanode *p, *o; p = dataptr; if (p == NULL) { return NULL; } /* handle list head remove */ if (strcmp(p->data.interface, iface)==0) { dataptr = p->next; if (debug) { printf("cache: h %s removed\n", iface); } free(p); return dataptr; } o = p; p = p->next; /* handle other locations */ while (p != NULL) { if (strcmp(p->data.interface, iface)==0) { o->next = p->next; if (debug) { printf("cache: %s removed\n", iface); } free(p); return o->next; } o = p; p = p->next; } return NULL; } int cacheupdate(void) { datanode *p, *n; p = dataptr; /* update if already in list */ while (p != NULL) { if (strcmp(p->data.interface, data.interface)==0) { if (memcpy(&p->data, &data, sizeof(p->data))!=NULL) { p->filled = 1; } else { p->filled = 0; } if (debug) { printf("cache: %s updated (%d)\n", p->data.interface, p->filled); } return p->filled; } p = p->next; } /* add new node if not in list */ n = malloc(sizeof(datanode)); if (n == NULL) { return 0; } n->next = dataptr; dataptr = n; if (memcpy(&n->data, &data, sizeof(n->data))!=NULL) { n->filled = 1; } else { n->filled = 0; } if (debug) { printf("cache: %s added and updated (%d)\n", n->data.interface, n->filled); } return n->filled; } void cacheshow(void) { int i = 1; datanode *p = dataptr; if (p == NULL) { printf("cache: empty.\n"); return; } printf("cache:"); while (p != NULL) { printf(" %d. \"%s\" ", i, p->data.interface); p = p->next; i++; } printf("\n"); } void cachestatus(void) { char buffer[512], bwtemp[16]; int b = 13, count = 0, bwlimit = 0; datanode *p = dataptr; snprintf(buffer, b, "Monitoring: "); while (p != NULL) { if ((b+strlen(p->data.interface)+16) < 508) { bwlimit = ibwget(p->data.interface); if (bwlimit < 0) { snprintf(bwtemp, 16, " (no limit) "); } else { snprintf(bwtemp, 16, " (%d Mbit) ", bwlimit); } strncat(buffer, p->data.interface, strlen(p->data.interface)); strncat(buffer, bwtemp, strlen(bwtemp)); b += strlen(p->data.interface) + strlen(bwtemp); } else { strcat(buffer, "..."); break; } count++; p = p->next; } if (count) { strncpy_nt(errorstring, buffer, 512); errorstring[511] = '\0'; } else { snprintf(errorstring, 512, "Nothing to monitor"); } printe(PT_Info); } int cacheget(datanode *dn) { if (dn == NULL) return 0; if (dn->filled) { memcpy(&data, &dn->data, sizeof(data)); /* do simple data validation */ if (data.version != DBVERSION || data.created == 0 || data.lastupdated == 0 || data.interface[0] == '\0' || data.active > 1 || data.active < 0) { if (debug) printf("cache get: validation failed (%d/%u/%u/%d/%d)\n", data.version, (unsigned int)data.created, (unsigned int)data.lastupdated, data.interface[0], data.active); /* force reading of database file */ dn->filled = 0; } } if (debug) { printf("cache get: %s (%d/%d)\n", dn->data.interface, dn->filled, dn->data.active); } return dn->filled; } /* flush cached data to disk and free all memory allocted for it */ void cacheflush(const char *dirname) { datanode *f, *p = dataptr; while (p != NULL) { f = p; p = p->next; /* write data to file if needed */ if (f->filled && dirname!=NULL) { memcpy(&data, &f->data, sizeof(data)); writedb(f->data.interface, dirname, 0); } free(f); } dataptr = NULL; } int cachecount(void) { datanode *p = dataptr; int c = 0; while (p != NULL) { c++; p = p->next; } return c; } int cacheactivecount(void) { datanode *p = dataptr; int c = 0; while (p != NULL) { if (p->data.active) { c++; } p = p->next; } return c; } uint32_t dbcheck(uint32_t dbhash, int *forcesave) { char *ifacelist, interface[32]; datanode *p = dataptr; uint32_t newhash; int offset, found; /* get list of currently visible interfaces */ if (getiflist(&ifacelist, 0)==0) { free(ifacelist); return 0; } newhash = simplehash(ifacelist, (int)strlen(ifacelist)); if (newhash == dbhash) { free(ifacelist); return newhash; } /* search for changes if hash doesn't match */ if (debug) { printf("ifacelist changed: '%s' %u <> %u\n", ifacelist, dbhash, newhash); } while (p != NULL) { if (p->filled) { found = offset = 0; while (offset <= (int)strlen(ifacelist)) { sscanf(ifacelist+offset, "%31s", interface); if (strcmp(p->data.interface, interface)==0) { found = 1; break; } offset += (int)strlen(interface)+1; } if (p->data.active==1 && found==0) { p->data.active = 0; p->data.currx = p->data.curtx = 0; if (cfg.savestatus) { *forcesave = 1; } snprintf(errorstring, 512, "Interface \"%s\" disabled.", p->data.interface); printe(PT_Info); } else if (p->data.active==0 && found==1) { p->data.active = 1; p->data.currx = p->data.curtx = 0; if (cfg.savestatus) { *forcesave = 1; } snprintf(errorstring, 512, "Interface \"%s\" enabled.", p->data.interface); printe(PT_Info); } } p = p->next; } free(ifacelist); return newhash; } uint32_t simplehash(const char *data, int len) { uint32_t hash = len; if (len <= 0 || data == NULL) { return 0; } for (len--; len >= 0; len--) { if (len > 0) { hash += (int)data[len] * len; } else { hash += (int)data[len]; } } return hash; } vnstat-1.14/src/Makefile0000644000000000000000000000304512506045367013715 0ustar rootrootifeq "$(origin CC)" "default" CC = gcc endif CFLAGS ?= -O2 LDLIBS = -lm OBJS = vnstat.o ifinfo.o dbxml.o dbjson.o dbshow.o dbaccess.o dbmerge.o common.o misc.o cfg.o ibw.o traffic.o DOBJS = vnstatd.o ifinfo.o dbaccess.o dbcache.o common.o misc.o cfg.o ibw.o daemon.o IOBJS = vnstati.o image.o dbaccess.o dbmerge.o common.o misc.o cfg.o default: vnstat vnstatd all: vnstat vnstatd vnstati vnstat: $(OBJS) $(CC) $(LDFLAGS) $(OBJS) $(LDLIBS) -o vnstat vnstatd: $(DOBJS) $(CC) $(LDFLAGS) $(DOBJS) $(LDLIBS) -o vnstatd vnstati: $(IOBJS) $(CC) $(LDFLAGS) $(IOBJS) $(LDLIBS) -lgd -o vnstati vnstat.o: vnstat.c vnstat.h common.h ifinfo.h traffic.h dbxml.h dbjson.h dbshow.h dbaccess.h dbmerge.h misc.h cfg.h vnstatd.o: vnstatd.c vnstatd.h common.h dbcache.h cfg.h daemon.h vnstati.o: vnstati.c vnstati.h common.h image.h cfg.h dbaccess.h dbmerge.h ifinfo.o: ifinfo.c ifinfo.h common.h dbaccess.h misc.h cfg.h ibw.h traffic.o: traffic.c traffic.h common.h ifinfo.h misc.h dbxml.o: dbxml.c dbxml.h common.h dbjson.o: dbjson.c dbjson.h common.h dbshow.o: dbshow.c dbshow.h misc.h common.h dbaccess.o: dbaccess.c dbaccess.h common.h dbmerge.o: dbmerge.c dbmerge.h dbaccess.h common.h dbcache.o: dbcache.c dbcache.h dbaccess.h common.h ifinfo.h cfg.h ibw.h common.o: common.c common.h misc.o: misc.c misc.h common.h cfg.o: cfg.c cfg.h common.h ibw.o: ibw.c ibw.h cfg.h ifinfo.h common.h daemon.o: daemon.c daemon.h common.h ifinfo.h dbaccess.h dbcache.h misc.h cfg.h ibw.h image.o: image.c image.h vnstati.h common.h misc.h clean: rm -f *.o *~ core *.i vnstat vnstatd vnstati vnstat-1.14/src/vnstati.c0000644000000000000000000002646212506567240014120 0ustar rootroot/* vnStat image output - Copyright (c) 2007-2015 Teemu Toivola 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; version 2 dated June, 1991. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave., Cambridge, MA 02139, USA. */ #include "common.h" #include "image.h" #include "cfg.h" #include "dbaccess.h" #include "dbmerge.h" #include "vnstati.h" int main(int argc, char *argv[]) { int currentarg; IPARAMS p; IMAGECONTENT ic; initiparams(&p); initimagecontent(&ic); /* early check for debug and config parameter */ if (argc > 1) { for (currentarg=1; currentarg 3 || cfg.ostyle < 0) { printf("Error: Invalid style parameter \"%d\" for --style.\n", cfg.ostyle); return 1; } if (debug) printf("Style changed: %d\n", cfg.ostyle); currentarg++; continue; } else { printf("Error: Style parameter for --style missing.\n"); return 1; } } else if ((strcmp(argv[currentarg],"--transparent"))==0) { if (currentarg+1 1 || cfg.transbg < 0) { printf("Error: Invalid parameter \"%d\" for --transparent.\n", cfg.transbg); return 1; } if (debug) printf("Transparency changed: %d\n", cfg.transbg); currentarg++; continue; } else { cfg.transbg = !cfg.transbg; if (debug) printf("Transparency changed: %d\n", cfg.transbg); } } else if ((strcmp(argv[currentarg],"--dbdir"))==0) { if (currentarg+1 1 || cfg.rateunit < 0) { printf("Error: Invalid parameter \"%d\" for --rateunit.\n", cfg.rateunit); printf(" Valid parameters:\n"); printf(" 0 - bytes\n"); printf(" 1 - bits\n"); return 1; } if (debug) printf("Rateunit changed: %d\n", cfg.rateunit); currentarg++; continue; } else { cfg.rateunit = !cfg.rateunit; if (debug) printf("Rateunit changed: %d\n", cfg.rateunit); } } else if ((strcmp(argv[currentarg],"-v")==0) || (strcmp(argv[currentarg],"--version"))==0) { printf("vnStat image output %s by Teemu Toivola \n", VNSTATVERSION); return 0; } else { printf("Unknown arg \"%s\". Use --help for help.\n",argv[currentarg]); return 1; } } if (p.help || argc == 1) { showihelp(&p); return 0; } validateinput(&p); handlecaching(&p, &ic); handledatabase(&p); openoutput(&p); if (debug) printf("Qmode: %d\n", cfg.qmode); drawimage(&ic); writeoutput(&p, &ic); /* cleanup */ if (debug) printf("all done\n"); return 0; } void initiparams(IPARAMS *p) { noexit = 0; /* allow functions to exit in case of error */ debug = 0; /* debug disabled by default */ p->interface[0] = '\0'; p->dirname[0] = '\0'; p->filename[0] = '\0'; p->cfgfile[0] = '\0'; p->cache = 0; p->help = 0; } void showihelp(IPARAMS *p) { printf(" vnStat image output %s by Teemu Toivola \n\n", VNSTATVERSION); printf(" -h, --hours output hours\n"); printf(" -d, --days output days\n"); printf(" -m, --months output months\n"); printf(" -t, --top10 output top10\n"); printf(" -s, --summary output summary\n"); printf(" -hs, --hsummary output horizontal summary with hours\n"); printf(" -vs, --vsummary output vertical summary with hours\n"); printf(" -nh, --noheader remove header from output\n"); printf(" -ne, --noedge remove edge from output\n"); printf(" -nl, --nolegend remove legend from output\n"); printf(" -ru, --rateunit swap configured rate unit\n"); printf(" -o, --output select output filename\n"); printf(" -c, --cache update output only when too old\n"); printf(" -i, --iface used interface (default: %s)\n", p->interface); printf(" -?, --help this help\n"); printf(" -D, --debug show some additional debug information\n"); printf(" -v, --version show version\n"); printf(" --dbdir select database directory\n"); printf(" --style select output style (0-3)\n"); printf(" --locale set locale\n"); printf(" --config select config file\n"); printf(" --altdate use alternative date location\n"); printf(" --headertext specify header text string\n"); printf(" --transparent toggle background transparency\n\n"); printf("See also \"man vnstati\".\n"); } void validateinput(IPARAMS *p) { if (!cfg.qmode || !strlen(p->filename)) { printf("At least output mode and file parameter needs to be given.\n"); printf("Use -? or --help for getting short help.\n"); exit(EXIT_FAILURE); } } void handlecaching(IPARAMS *p, IMAGECONTENT *ic) { struct stat filestat; if (p->cache==0 || p->filename[0]=='-') { return; } if (stat(p->filename, &filestat)==0) { if ((ic->current-filestat.st_mtime)<(p->cache*60)) { if (debug) printf("Using cached file (%d<%d).\n", (int)(ic->current-filestat.st_mtime), p->cache*60); exit(EXIT_SUCCESS); } } else { /* abort if error is something else than file not found */ if (errno!=ENOENT) { printf("Error: Getting status for file \"%s\" failed: %s (%d)\n", p->filename, strerror(errno), errno); exit(EXIT_FAILURE); } } } void handledatabase(IPARAMS *p) { if (strstr(p->interface, "+")) { if (!mergedb(p->interface, p->dirname)) { exit(EXIT_FAILURE); } } else { if (readdb(p->interface, p->dirname)==1) { exit(EXIT_FAILURE); } } } void openoutput(IPARAMS *p) { if (p->filename[0]!='-') { if ((p->pngout = fopen(p->filename, "w"))==NULL) { printf("Error: Opening file \"%s\" for output failed: %s\n", p->filename, strerror(errno)); exit(EXIT_FAILURE); } } else { /* output to stdout */ if ((p->pngout = fdopen(1, "w"))==NULL) { printf("Error: Opening stdout for output failed: %s\n", strerror(errno)); exit(EXIT_FAILURE); } } } void writeoutput(IPARAMS *p, IMAGECONTENT *ic) { gdImagePng(ic->im, p->pngout); fclose(p->pngout); gdImageDestroy(ic->im); } vnstat-1.14/src/cfg.h0000644000000000000000000000072412506566027013167 0ustar rootroot#ifndef CFG_H #define CFG_H struct cfgsetting { const char *name; char *locc; int32_t *loci; short namelen; short found; }; void printcfgfile(void); int loadcfg(const char *cfgfile); void validatecfg(void); void defaultcfg(void); int opencfgfile(const char *cfgfile, FILE **fd); int extractcfgvalue(char *value, const char *cfgline, int cfglen); int setcfgvalue(struct cfgsetting *cset, const char *value, const char *cfgline); void configlocale(void); #endif vnstat-1.14/src/dbjson.h0000644000000000000000000000037612462504463013707 0ustar rootroot#ifndef DBJSON_H #define DBJSON_H void showjson(int dbcount, char mode); void jsondays(void); void jsonmonths(void); void jsontops(void); void jsonhours(void); void jsondate(time_t *date, int type); void jsonheader(void); void jsonfooter(void); #endif vnstat-1.14/src/misc.c0000644000000000000000000002102412506565174013354 0ustar rootroot#include "common.h" #include "misc.h" int kerneltest(void) { int i=0, bmax, bmin, btemp; bmax=bmin=getbtime(); printf("This test will take about 60 seconds.\n"); printf("[ ]"); for (i=0; i<=30; i++) { printf("\b"); } fflush(stdout); for (i=0;i<30;i++) { sleep(2); fflush(stdout); btemp=getbtime(); if (btemp > bmax) { bmax = btemp; } if (btemp < bmin) { bmin = btemp; } printf("="); fflush(stdout); } printf("] done.\n\n"); printf("Detected boot time variation during test: %2d\n", bmax-bmin); printf("Maximum boot time variation set in config: %2d\n\n", cfg.bvar); if ((bmax-bmin)>20) { printf("The current kernel has a broken boot time information and\n"); printf("vnStat is likely not to work correctly. Upgrading the kernel\n"); printf("is likely to solve this problem.\n\n"); return 1; } else if ((bmax-bmin)>cfg.bvar) { printf("The current kernel has a boot time variation greater than assumed\n"); printf("in the vnStat config. That it likely to cause errors in results.\n"); printf("Set \"BootVariation\" to something greater than \"%d\" and run this\n", (bmax-bmin)); printf("test again.\n\n"); return 1; } else if ((bmax-bmin)==0) { printf("The current kernel doesn't seem to suffer from boot time variation problems.\n"); printf("Everything is ok.\n\n"); return 0; } else { printf("The current kernel is ok.\n\n"); return 0; } } int spacecheck(char *path) { struct statvfs buf; uint64_t free; /* do space check only when configured for it */ if (!cfg.spacecheck) { return 1; } if (statvfs(path, &buf)) { if (noexit) { return 0; } else { snprintf(errorstring, 512, "Free diskspace check failed: %s", strerror(errno)); printe(PT_Error); exit(EXIT_FAILURE); } } free=(buf.f_bavail/(float)1024)*buf.f_bsize; if (debug) { printf("bsize %d\n", (int)buf.f_bsize); printf("blocks %lu\n", (unsigned long int)buf.f_blocks); printf("bfree %lu\n", (unsigned long int)buf.f_bfree); printf("bavail %lu\n", (unsigned long int)buf.f_bavail); printf("ffree %lu\n", (unsigned long int)buf.f_ffree); printf("%"PRIu64" free space left\n", free); } /* the database is less than 3kB but let's require */ /* 1MB to be on the safe side, anyway, the filesystem should */ /* always have more free space than that */ if (free<=1024) { return 0; } else { return 1; } } void sighandler(int sig) { /* set signal */ intsignal = sig; if (debug) { switch (sig) { case SIGHUP: snprintf(errorstring, 512, "DEBUG: SIGHUP (%d)", sig); break; case SIGTERM: snprintf(errorstring, 512, "DEBUG: SIGTERM (%d)", sig); break; case SIGINT: snprintf(errorstring, 512, "DEBUG: SIGINT (%d)", sig); break; default: snprintf(errorstring, 512, "DEBUG: Unknown signal %d", sig); break; } printe(PT_Info); } } int getbtime(void) { int result=0; #if defined(__linux__) FILE *fp; int check; char temp[64], statline[128]; if ((fp=fopen("/proc/stat","r"))==NULL) { snprintf(errorstring, 512, "Unable to read /proc/stat: %s", strerror(errno)); printe(PT_Error); if (noexit) { return 0; } else { exit(1); } } check=0; while (fgets(statline,128,fp)!=NULL) { sscanf(statline,"%63s",temp); if (strcmp(temp,"btime")==0) { /* if (debug) printf("\n%s\n",statline); */ check=1; break; } } fclose(fp); if (check==0) { snprintf(errorstring, 512, "btime missing from /proc/stat."); printe(PT_Error); if (noexit) { return 0; } else { exit(1); } } result = strtoul(statline+6, (char **)NULL, 0); #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) || defined(__FreeBSD_kernel__) struct timeval btm; size_t len = sizeof(btm); int mib[2] = {CTL_KERN, KERN_BOOTTIME}; if (sysctl(mib, 2, &btm, &len, NULL, 0) < 0) { if (debug) printf("sysctl(kern.boottime) failed.\n"); return 0; } result = btm.tv_sec; #endif return result; } char *getvalue(uint64_t mb, uint64_t kb, int len, int type) { static char buffer[64]; int declen=2; uint64_t kB; /* request types: 1) normal 2) estimate 3) image scale */ if (type==3) { declen=0; } if (mb!=0) { kB=mbkbtokb(mb, kb); } else { kB=kb; } /* tune spacing according to unit */ len -= strlen(getunit(1)) + 1; if (len<0) { len = 1; } if ( (type==2) && (kB==0) ){ snprintf(buffer, 64, "%*s ", len-cfg.unit, "--"); } else { /* try to figure out what unit to use */ if (kB>=1048576000) { /* 1024*1024*1000 - value >=1000 GiB -> show in TiB */ snprintf(buffer, 64, "%"DECCONV"*.*f %s", len, declen, kB/(float)1073741824, getunit(4)); /* 1024*1024*1024 */ } else if (kB>=1024000) { /* 1024*1000 - value >=1000 MiB -> show in GiB */ snprintf(buffer, 64, "%"DECCONV"*.*f %s", len, declen, kB/(float)1048576, getunit(3)); /* 1024*1024 */ } else if (kB>=1000) { if (type==2) { declen=0; } snprintf(buffer, 64, "%"DECCONV"*.*f %s", len, declen, kB/(float)1024, getunit(2)); } else { snprintf(buffer, 64, "%"DECCONV"*"PRIu64" %s", len, kB, getunit(1)); } } return buffer; } char *getrate(uint64_t mb, uint64_t kb, uint32_t interval, int len) { static char buffer[64]; int unit, declen = 2; uint64_t kB; float rate; if (interval==0) { snprintf(buffer, 64, "%*s", len, "n/a"); return buffer; } if (mb!=0) { kB=mbkbtokb(mb, kb); } else { kB=kb; } /* convert to proper unit */ if (cfg.rateunit) { rate = (kB*8)/(float)interval; unit = 2; if (interval<5) { declen = 0; } } else { rate = kB/(float)interval; unit = cfg.unit; } return getratestring(rate, len, declen, unit); } char *gettrafficrate(uint64_t bytes, uint32_t interval, int len) { static char buffer[64]; int unit, declen = 2; float rate; if (interval==0) { snprintf(buffer, 64, "%*s", len, "n/a"); return buffer; } /* convert to proper unit */ if (cfg.rateunit) { rate = (bytes*8)/(float)(interval*1024); unit = 2; if (interval<5) { declen = 0; } } else { rate = bytes/(float)(interval*1024); unit = cfg.unit; } return getratestring(rate, len, declen, unit); } uint64_t getscale(uint64_t kb) { int i; uint64_t result; result = kb; /* get unit */ for (i=0; result>1024; i++) { result = result / 1024; } /* round result depending of scale */ if (result>300) { result = result/4 + (100 - ((result/4) % 100)); } else if (result>20) { result = result/4 + (10 - ((result/4) % 10)); } else { result = result/4; } /* put unit back */ if (i) { result = result * pow(1024, i); } /* make sure result isn't zero */ if (!result) { result = pow(1024, i); } return result; } char *getunit(int index) { static char *unit[] = { "na", "KiB", "MiB", "GiB", "TiB", "KB", "MB", "GB", "TB" }; if (index>UNITCOUNT) { return unit[0]; } else { return unit[(cfg.unit*UNITCOUNT)+index]; } } char *getrateunit(int unit, int index) { static char *bunit[] = { "na", "KiB/s", "MiB/s", "GiB/s", "TiB/s", "KB/s", "MB/s", "GB/s", "TB/s", "kbit/s", "Mbit/s", "Gbit/s", "Tbit/s" }; if (index>UNITCOUNT) { return bunit[0]; } else { return bunit[(unit*UNITCOUNT)+index]; } } uint32_t getunitdivider(int unit, int index) { uint32_t unitdiv[] = { 0, 0, 1024, 1048576, 1073741824, 0, 1024, 1048576, 1073741824, 0, 1000, 1000000, 1000000000 }; if (index>UNITCOUNT) { return unitdiv[0]; } else { return unitdiv[(unit*UNITCOUNT)+index]; } } char *getratestring(float rate, int len, int declen, int unit) { static char buffer[64]; uint32_t limit[3] = { 1000, 1024000, 1048576000 }; if (cfg.rateunit) { limit[0] = 1000; limit[1] = 1000000; limit[2] = 1000000000; } /* tune spacing according to unit */ len -= strlen(getrateunit(unit, 1)) + 1; if (len<0) { len = 1; } /* try to figure out what unit to use */ if (rate>=limit[2]) { snprintf(buffer, 64, "%"DECCONV"*.2f %s", len, rate/(float)getunitdivider(unit, 4), getrateunit(unit, 4)); } else if (rate>=limit[1]) { snprintf(buffer, 64, "%"DECCONV"*.2f %s", len, rate/(float)getunitdivider(unit, 3), getrateunit(unit, 3)); } else if (rate>=limit[0]) { snprintf(buffer, 64, "%"DECCONV"*.2f %s", len, rate/(float)getunitdivider(unit, 2), getrateunit(unit, 2)); } else { snprintf(buffer, 64, "%"DECCONV"*.*f %s", len, declen, rate, getrateunit(unit, 1)); } return buffer; } int getpadding(int len, char *str) { if (!cfg.utflocale) { return len; } return len + ((int)strlen(str) - (int)mbstowcs(NULL, str, 0)); } vnstat-1.14/src/dbaccess.c0000644000000000000000000004304012455260350014161 0ustar rootroot#include "common.h" #include "dbaccess.h" int readdb(const char *iface, const char *dirname) { FILE *db; char file[512], backup[512]; snprintf(file, 512, "%s/%s", dirname, iface); snprintf(backup, 512, "%s/.%s", dirname, iface); if ((db=fopen(file,"r"))==NULL) { snprintf(errorstring, 512, "Unable to read database \"%s\": %s", file, strerror(errno)); printe(PT_Error); /* create new database template */ initdb(); strncpy_nt(data.interface, iface, 32); strncpy_nt(data.nick, data.interface, 32); return 1; } /* lock file */ if (!lockdb(fileno(db), 0)) { fclose(db); return -1; } if (fread(&data,sizeof(DATA),1,db)==0) { data.version=-1; if (debug) { printf("db: Database read failed for file \"%s\".\n", file); } } else { if (debug) { printf("db: Database loaded for interface \"%s\"...\n", data.interface); } } /* convert old database to new format if necessary */ if (data.versionDBVERSION) { snprintf(errorstring, 512, "Downgrading database \"%s\" (v%d) is not supported.", file, data.version); printe(PT_Error); fclose(db); if (noexit) { return -1; } else { exit(EXIT_FAILURE); } } fclose(db); if (strcmp(data.interface,iface)) { snprintf(errorstring, 512, "Warning:\nThe previous interface for this file was \"%s\".",data.interface); printe(PT_Multiline); snprintf(errorstring, 512, "It has now been replaced with \"%s\".",iface); printe(PT_Multiline); snprintf(errorstring, 512, "You can ignore this message if you renamed the filename."); printe(PT_Multiline); snprintf(errorstring, 512, "Interface name mismatch, renamed \"%s\" -> \"%s\"", data.interface, iface); printe(PT_ShortMultiline); if (strcmp(data.interface, data.nick)==0) { strncpy_nt(data.nick, iface, 32); } strncpy_nt(data.interface, iface, 32); } return 0; } void initdb(void) { int i; time_t current; struct tm *d; current=time(NULL); d=localtime(¤t); /* set default values for a new database */ data.version=DBVERSION; data.active=1; data.totalrx=0; data.totaltx=0; data.currx=0; data.curtx=0; data.totalrxk=0; data.totaltxk=0; data.lastupdated=current; data.created=current; /* days */ for (i=0;i<=29;i++) { data.day[i].rx=0; data.day[i].tx=0; data.day[i].rxk=0; data.day[i].txk=0; data.day[i].date=0; data.day[i].used=0; } /* months */ for (i=0;i<=11;i++) { data.month[i].rx=0; data.month[i].tx=0; data.month[i].rxk=0; data.month[i].txk=0; data.month[i].month=0; data.month[i].used=0; } /* top10 */ for (i=0;i<=9;i++) { data.top10[i].rx=0; data.top10[i].tx=0; data.top10[i].rxk=0; data.top10[i].txk=0; data.top10[i].date=0; data.top10[i].used=0; } /* hours */ for (i=0;i<=23;i++) { data.hour[i].rx=0; data.hour[i].tx=0; data.hour[i].date=0; } data.day[0].used=data.month[0].used=1; data.day[0].date=current; /* calculate new date for current month if current day is less than the set monthrotate value so that new databases begin from the right month */ if (d->tm_mday < cfg.monthrotate) { d->tm_mday=cfg.monthrotate; d->tm_mon--; data.month[0].month=mktime(d); } else { data.month[0].month=current; } data.btime=MAX32; } int writedb(const char *iface, const char *dirname, int newdb) { FILE *db; char file[512], backup[512]; snprintf(file, 512, "%s/%s", dirname, iface); snprintf(backup, 512, "%s/.%s", dirname, iface); /* try to make backup of old data if this isn't a new database */ if (!newdb && !backupdb(file, backup)) { snprintf(errorstring, 512, "Unable to create database backup \"%s\".", backup); printe(PT_Error); return 0; } /* make sure version stays correct */ data.version=DBVERSION; if ((db=fopen(file,"w"))==NULL) { snprintf(errorstring, 512, "Unable to open database \"%s\" for writing: %s", file, strerror(errno)); printe(PT_Error); return 0; } /* lock file */ if (!lockdb(fileno(db), 1)) { fclose(db); return 0; } /* update timestamp when not merging */ if (newdb!=2) { data.lastupdated=time(NULL); } if (fwrite(&data,sizeof(DATA),1,db)==0) { snprintf(errorstring, 512, "Unable to write database \"%s\": %s", file, strerror(errno)); printe(PT_Error); fclose(db); return 0; } else { if (debug) { printf("db: Database \"%s\" saved.\n", file); } fclose(db); if ((newdb) && (noexit==0)) { snprintf(errorstring, 512, "-> A new database has been created."); printe(PT_Info); } } return 1; } int backupdb(const char *current, const char *backup) { FILE *bf; int c, b, bytes; char buffer[512]; /* from */ if ((c = open(current, O_RDONLY)) == -1) { return 0; } /* to, fopen() in order to get file mode bits correctly */ if ((bf = fopen(backup, "w")) == NULL) { close(c); return 0; } b = fileno(bf); /* copy data */ while((bytes = (int)read(c, buffer, sizeof(buffer))) > 0) { if (write(b, buffer, bytes) < 0) { close(c); fclose(bf); return 0; } } close(c); fclose(bf); return 1; } int convertdb(void) { /* corrupted or unknown version handling */ if (data.version==0) { snprintf(errorstring, 512, "Unable to convert corrupted database."); printe(PT_Error); return 0; } else if (data.versionDBVERSION) { snprintf(errorstring, 512, "Unable to downgrade database from version \"%d\".", data.version); printe(PT_Error); return 0; } else if (data.version!=DBVERSION) { snprintf(errorstring, 512, "Unable to convert database version \"%d\".", data.version); printe(PT_Error); return 0; } return 1; } int lockdb(int fd, int dbwrite) { int operation, locktry=1; /* lock only if configured to do so */ if (!cfg.flock) { return 1; } if (dbwrite) { operation = LOCK_EX|LOCK_NB; } else { operation = LOCK_SH|LOCK_NB; } /* try locking file */ while (flock(fd, operation)!=0) { if (debug) { printf("db: Database access locked (%d, %d)\n", dbwrite, locktry); } /* give up if lock can't be obtained */ if (locktry>=LOCKTRYLIMIT) { if (dbwrite) { snprintf(errorstring, 512, "Locking database file for write failed for %d tries:\n%s (%d)", locktry, strerror(errno), errno); } else { snprintf(errorstring, 512, "Locking database file for read failed for %d tries:\n%s (%d)", locktry, strerror(errno), errno); } printe(PT_Error); return 0; } /* someone else has the lock */ if (errno==EWOULDBLOCK) { sleep(1); /* real error */ } else { if (dbwrite) { snprintf(errorstring, 512, "Locking database file for write failed:\n%s (%d)", strerror(errno), errno); } else { snprintf(errorstring, 512, "Locking database file for read failed:\n%s (%d)", strerror(errno), errno); } printe(PT_Error); return 0; } locktry++; } return 1; } int checkdb(const char *iface, const char *dirname) { char file[512]; struct statvfs buf; snprintf(file, 512, "%s/%s", dirname, iface); if (statvfs(file, &buf)==0) { return 1; /* file exists */ } else { return 0; /* no file or some other error */ } } int removedb(const char *iface, const char *dirname) { char file[512]; /* remove backup first */ snprintf(file, 512, "%s/.%s", dirname, iface); unlink(file); snprintf(file, 512, "%s/%s", dirname, iface); if (unlink(file)!=0) { return 0; } return 1; } void cleanhours(void) { int i, day, hour; time_t current; struct tm *d; current=time(NULL); /* remove old data if needed */ for (i=0;i<=23;i++) { if ( (data.hour[i].date!=0) && (data.hour[i].date<=(current-86400)) ) { /* 86400 = 24 hours = too old */ data.hour[i].rx=0; data.hour[i].tx=0; data.hour[i].date=0; if (debug) { printf("db: Hour %d (%u) cleaned.\n", i, (unsigned int)data.hour[i].date); } } } /* clean current if it's not from today to avoid incrementing old data */ d=localtime(¤t); day=d->tm_mday; hour=d->tm_hour; d=localtime(&data.hour[hour].date); if (d->tm_mday!=day) { data.hour[hour].rx=0; data.hour[hour].tx=0; if (debug) { printf("db: Current hour %d (%u) cleaned.\n", hour, (unsigned int)data.hour[hour].date); } data.hour[hour].date=current; } } void rotatedays(void) { int i, j; time_t current; struct tm *d; for (i=29;i>=1;i--) { data.day[i].rx=data.day[i-1].rx; data.day[i].tx=data.day[i-1].tx; data.day[i].rxk=data.day[i-1].rxk; data.day[i].txk=data.day[i-1].txk; data.day[i].date=data.day[i-1].date; data.day[i].used=data.day[i-1].used; } current=time(NULL); data.day[0].rx=0; data.day[0].tx=0; data.day[0].rxk=0; data.day[0].txk=0; data.day[0].date=current; if (debug) { d=localtime(&data.day[0].date); printf("db: Days rotated. Current date: %d.%d.%d\n", d->tm_mday, d->tm_mon+1, d->tm_year+1900); } /* top10 update */ for (i=0;i<=9;i++) { if ( data.day[1].rx+data.day[1].tx >= data.top10[i].rx+data.top10[i].tx ) { /* if MBs are same but kB smaller then continue searching */ if ( (data.day[1].rx+data.day[1].tx == data.top10[i].rx+data.top10[i].tx) && (data.day[1].rxk+data.day[1].txk <= data.top10[i].rxk+data.top10[i].txk) ) { continue; } for (j=9;j>=i+1;j--) { data.top10[j].rx=data.top10[j-1].rx; data.top10[j].tx=data.top10[j-1].tx; data.top10[j].rxk=data.top10[j-1].rxk; data.top10[j].txk=data.top10[j-1].txk; data.top10[j].date=data.top10[j-1].date; data.top10[j].used=data.top10[j-1].used; } data.top10[i].rx=data.day[1].rx; data.top10[i].tx=data.day[1].tx; data.top10[i].rxk=data.day[1].rxk; data.top10[i].txk=data.day[1].txk; data.top10[i].date=data.day[1].date; data.top10[i].used=data.day[1].used; break; } } if (debug) { printf("db: Top10 updated.\n"); } } void rotatemonths(void) { int i; time_t current; struct tm *d; for (i=11;i>=1;i--) { data.month[i].rx=data.month[i-1].rx; data.month[i].tx=data.month[i-1].tx; data.month[i].rxk=data.month[i-1].rxk; data.month[i].txk=data.month[i-1].txk; data.month[i].month=data.month[i-1].month; data.month[i].used=data.month[i-1].used; } current=time(NULL); data.month[0].rx=0; data.month[0].tx=0; data.month[0].rxk=0; data.month[0].txk=0; data.month[0].month=current; if (debug) { d=localtime(&data.month[0].month); printf("db: Months rotated. Current month: \"%d\".\n", d->tm_mon+1); } } void cleartop10(const char *iface, const char *dirname) { int i; if (readdb(iface, dirname)!=0) { exit(EXIT_FAILURE); } for (i=0; i<=9; i++) { data.top10[i].rx=data.top10[i].tx=0; data.top10[i].rxk=data.top10[i].txk=0; data.top10[i].used=0; } writedb(iface, dirname, 0); printf("Top10 cleared for interface \"%s\".\n", data.interface); } void rebuilddbtotal(const char *iface, const char *dirname) { int i; if (readdb(iface, dirname)!=0) { exit(EXIT_FAILURE); } data.totalrx=data.totaltx=data.totalrxk=data.totaltxk=0; for (i=0; i<=11; i++) { if (data.month[i].used) { addtraffic(&data.totalrx, &data.totalrxk, data.month[i].rx, data.month[i].rxk); addtraffic(&data.totaltx, &data.totaltxk, data.month[i].tx, data.month[i].txk); } } writedb(iface, dirname, 0); printf("Total transfer rebuild completed for interface \"%s\".\n", data.interface); } int validatedb(void) { int i, used; uint64_t rxsum, txsum; if (data.version>DBVERSION) { printf("Invalid database version: %d\n", data.version); return 0; } if (data.active<0 || data.active>1) { printf("Invalid database activity status: %d\n", data.active); return 0; } if (!strlen(data.interface)) { printf("Invalid database interface string: %s\n", data.interface); return 0; } if (!data.created || !data.lastupdated || !data.btime) { printf("Invalid database timestamp.\n"); return 0; } rxsum = txsum = 0; used = 1; for (i=0; i<30; i++) { if (data.day[i].used<0 || data.day[i].used>1) { printf("Invalid database daily use information: %d %d\n", i, data.day[i].used); return 0; } if (data.day[i].rxk<0 || data.day[i].txk<0) { printf("Invalid database daily traffic: %d\n", i); return 0; } if (data.day[i].used && !used) { printf("Invalid database daily use order: %d\n", i); return 0; } else if (!data.day[i].used) { used = 0; } if (data.day[i].used) { rxsum += data.day[i].rx; txsum += data.day[i].tx; } } if (data.totalrx < rxsum || data.totaltx < txsum) { printf("Invalid database total traffic compared to daily usage.\n"); return 0; } rxsum = txsum = 0; used = 1; for (i=0; i<12; i++) { if (data.month[i].used<0 || data.month[i].used>1) { printf("Invalid database monthly use information: %d %d\n", i, data.month[i].used); return 0; } if (data.month[i].rxk<0 || data.month[i].txk<0) { printf("Invalid database monthly traffic: %d\n", i); return 0; } if (data.month[i].used && !used) { printf("Invalid database monthly use order: %d\n", i); return 0; } else if (!data.month[i].used) { used = 0; } if (data.month[i].used) { rxsum += data.month[i].rx; txsum += data.month[i].tx; } } if (data.totalrx < rxsum || data.totaltx < txsum) { printf("Invalid database total traffic compared to monthly usage.\n"); return 0; } used = 1; for (i=0; i<10; i++) { if (data.top10[i].used<0 || data.top10[i].used>1) { printf("Invalid database top10 use information: %d %d\n", i, data.top10[i].used); return 0; } if (data.top10[i].rxk<0 || data.top10[i].txk<0) { printf("Invalid database top10 traffic: %d\n", i); return 0; } if (data.top10[i].used && !used) { printf("Invalid database top10 use order: %d\n", i); return 0; } else if (!data.top10[i].used) { used = 0; } } return 1; } int importdb(const char *filename) { FILE *input; char line[512]; int i, count, linecount, scancount; uint64_t tempint; DAY day; MONTH month; HOUR hour; if ((input=fopen(filename, "r"))==NULL) { printf("Error: opening file \"%s\" failed: %s\n", filename, strerror(errno)); return 0; } linecount = 0; while (fgets(line, sizeof(line), input) != NULL) { if (debug) { printf("parsing %s", line); } if (strlen(line)<6) { continue; } scancount = 0; scancount += sscanf(line, "version;%2d", &data.version); scancount += sscanf(line, "active;%2d", &data.active); scancount += sscanf(line, "interface;%31s", data.interface); scancount += sscanf(line, "nick;%31s", data.nick); if (sscanf(line, "created;%20"PRIu64, &tempint)) { data.created = (time_t)tempint; scancount++; } if (sscanf(line, "updated;%20"PRIu64, &tempint)) { data.lastupdated = (time_t)tempint; scancount++; } scancount += sscanf(line, "totalrx;%20"PRIu64, &data.totalrx); scancount += sscanf(line, "totaltx;%20"PRIu64, &data.totaltx); scancount += sscanf(line, "currx;%20"PRIu64, &data.currx); scancount += sscanf(line, "curtx;%20"PRIu64, &data.curtx); scancount += sscanf(line, "totalrxk;%10d", &data.totalrxk); scancount += sscanf(line, "totaltxk;%10d", &data.totaltxk); scancount += sscanf(line, "btime;%20"PRIu64, &data.btime); count = sscanf(line, "d;%2d;%20"PRIu64";%20"PRIu64";%20"PRIu64";%10d;%10d;%2d", &i, &tempint, &day.rx, &day.tx, &day.rxk, &day.txk, &day.used); if (count == 7) { if (i >= 0 && i < (int)sizeof(data.day) / (int)sizeof(DAY)) { day.date = (time_t)tempint; data.day[i] = day; scancount++; } } count = sscanf(line, "m;%2d;%20"PRIu64";%20"PRIu64";%20"PRIu64";%10d;%10d;%2d", &i, &tempint, &month.rx, &month.tx, &month.rxk, &month.txk, &month.used); if (count == 7) { if ( i >= 0 && i < (int)sizeof(data.month) / (int)sizeof(MONTH) ) { month.month = (time_t)tempint; data.month[i] = month; scancount++; } } count = sscanf(line, "t;%2d;%20"PRIu64";%20"PRIu64";%20"PRIu64";%10d;%10d;%2d", &i, &tempint, &day.rx, &day.tx, &day.rxk, &day.txk, &day.used); if (count == 7) { if ( i >= 0 && i < (int)sizeof(data.top10) / (int)sizeof(DAY) ) { day.date = (time_t)tempint; data.top10[i] = day; scancount++; } } count = sscanf(line, "h;%2d;%20"PRIu64";%20"PRIu64";%20"PRIu64, &i, &tempint, &hour.rx, &hour.tx); if (count == 4) { if ( i >= 0 && i < (int)sizeof(data.hour) / (int)sizeof(HOUR) ) { hour.date = (time_t)tempint; data.hour[i] = hour; scancount++; } } if (scancount) { linecount++; } } fclose(input); return linecount; } vnstat-1.14/src/vnstati.h0000644000000000000000000000067112404124554014111 0ustar rootroot#ifndef VNSTATI_H #define VNSTATI_H #include "image.h" typedef struct { int cache, help; char interface[32], dirname[512], filename[512], cfgfile[512]; FILE *pngout; } IPARAMS; void initiparams(IPARAMS *p); void showihelp(IPARAMS *p); void validateinput(IPARAMS *p); void handlecaching(IPARAMS *p, IMAGECONTENT *ic); void handledatabase(IPARAMS *p); void openoutput(IPARAMS *p); void writeoutput(IPARAMS *p, IMAGECONTENT *ic); #endif vnstat-1.14/src/dbshow.c0000644000000000000000000007515312506565132013715 0ustar rootroot#include "common.h" #include "misc.h" #include "dbshow.h" void showdb(int qmode) { if (data.totalrx+data.totaltx==0 && data.totalrxk+data.totaltxk==0 && qmode!=4) { printf(" %s: Not enough data available yet.\n", data.interface); return; } switch(qmode) { case 0: showsummary(); break; case 1: showdays(); break; case 2: showmonths(); break; case 3: showtop(); break; case 4: exportdb(); break; case 5: showshort(); break; case 6: showweeks(); break; case 7: showhours(); break; case 9: showoneline(); break; default: printf("Error: Not such query mode: %d\n", qmode); break; } } void showsummary(void) { struct tm *d; char datebuff[16]; char daytemp[32], daytemp2[32]; uint64_t e_rx, e_tx; time_t current, yesterday; current=time(NULL); yesterday=current-86400; e_rx=e_tx=0; /* get formated date for today */ d=localtime(¤t); strftime(datebuff, 16, cfg.dformat, d); /* get formated date for latest day in database */ d=localtime(&data.day[0].date); strftime(daytemp2, 16, cfg.dformat, d); /* change daytemp to today if formated days match */ if (strcmp(datebuff, daytemp2)==0) { strncpy_nt(daytemp2, " today", 32); } if (data.lastupdated) { printf("Database updated: %s\n",(char*)asctime(localtime(&data.lastupdated))); } else { printf("\n"); } indent(3); if (strcmp(data.interface, data.nick)==0) { if (data.active) printf("%s", data.interface); else printf("%s [disabled]", data.interface); } else { if (data.active) printf("%s (%s)", data.nick, data.interface); else printf("%s (%s) [disabled]", data.nick, data.interface); } /* get formated date for creation date */ d=localtime(&data.created); strftime(datebuff, 16, cfg.tformat, d); printf(" since %s\n\n", datebuff); indent(10); printf("rx: %s", getvalue(data.totalrx, data.totalrxk, 1, 1)); indent(3); printf(" tx: %s", getvalue(data.totaltx, data.totaltxk, 1, 1)); indent(3); printf(" total: %s\n\n", getvalue(data.totalrx+data.totaltx, data.totalrxk+data.totaltxk, 1, 1)); indent(3); printf("monthly\n"); indent(5); if (cfg.ostyle >= 2) { printf(" rx | tx | total | avg. rate\n"); indent(5); printf("------------------------+-------------+-------------+---------------\n"); } else { printf(" rx | tx | total\n"); indent(5); printf("------------------------+-------------+------------\n"); } if (data.month[1].used) { indent(5); d=localtime(&data.month[1].month); if (strftime(datebuff, 16, cfg.mformat, d)<=8) { printf(" %*s %s", getpadding(8, datebuff), datebuff, getvalue(data.month[1].rx, data.month[1].rxk, 11, 1)); } else { printf("%-*s %s", getpadding(11, datebuff), datebuff, getvalue(data.month[1].rx, data.month[1].rxk, 11, 1)); } printf(" | %s", getvalue(data.month[1].tx, data.month[1].txk, 11, 1)); printf(" | %s", getvalue(data.month[1].rx+data.month[1].tx, data.month[1].rxk+data.month[1].txk, 11, 1)); if (cfg.ostyle >= 2) { printf(" | %s", getrate(data.month[1].rx+data.month[1].tx, data.month[1].rxk+data.month[1].txk, dmonth(d->tm_mon)*86400, 14)); } printf("\n"); } indent(5); d=localtime(&data.month[0].month); if (strftime(datebuff, 16, cfg.mformat, d)<=8) { printf(" %*s %s", getpadding(8, datebuff), datebuff, getvalue(data.month[0].rx, data.month[0].rxk, 11, 1)); } else { printf("%-*s %s", getpadding(11, datebuff), datebuff, getvalue(data.month[0].rx, data.month[0].rxk, 11, 1)); } printf(" | %s", getvalue(data.month[0].tx, data.month[0].txk, 11, 1)); printf(" | %s", getvalue(data.month[0].rx+data.month[0].tx, data.month[0].rxk+data.month[0].txk, 11, 1)); if (cfg.ostyle >= 2) { printf(" | %s", getrate(data.month[0].rx+data.month[0].tx, data.month[0].rxk+data.month[0].txk, mosecs(), 14)); } printf("\n"); indent(5); if (cfg.ostyle >= 2) { printf("------------------------+-------------+-------------+---------------\n"); } else { printf("------------------------+-------------+------------\n"); } /* use database update time for estimates */ d=localtime(&data.month[0].month); if ( data.month[0].rx==0 || data.month[0].tx==0 || (data.lastupdated-data.month[0].month)==0 ) { e_rx=e_tx=0; } else { e_rx=(data.month[0].rx/(float)(mosecs()))*(dmonth(d->tm_mon)*86400); e_tx=(data.month[0].tx/(float)(mosecs()))*(dmonth(d->tm_mon)*86400); } indent(5); printf("estimated %s", getvalue(e_rx, 0, 11, 2)); printf(" | %s", getvalue(e_tx, 0, 11, 2)); printf(" | %s", getvalue(e_rx+e_tx, 0, 11, 2)); if (cfg.ostyle >= 2) { printf(" |\n\n"); } else { printf("\n\n"); } /* get formated date for yesterday */ d=localtime(&yesterday); strftime(datebuff, 16, cfg.dformat, d); /* get formated date for previous day in database */ d=localtime(&data.day[1].date); strftime(daytemp, 16, cfg.dformat, d); /* change daytemp to yesterday if formated days match */ if (strcmp(datebuff, daytemp)==0) { strncpy_nt(daytemp, "yesterday", 32); } /* use database update time for estimates */ d=localtime(&data.lastupdated); if ( data.day[0].rx==0 || data.day[0].tx==0 || (d->tm_hour*60+d->tm_min)==0 ) { e_rx=e_tx=0; } else { e_rx=((data.day[0].rx)/(float)(d->tm_hour*60+d->tm_min))*1440; e_tx=((data.day[0].tx)/(float)(d->tm_hour*60+d->tm_min))*1440; } indent(3); printf("daily\n"); indent(5); if (cfg.ostyle >= 2) { printf(" rx | tx | total | avg. rate\n"); indent(5); printf("------------------------+-------------+-------------+---------------\n"); } else { printf(" rx | tx | total\n"); indent(5); printf("------------------------+-------------+------------\n"); } if (data.day[1].date!=0) { indent(5); if (strlen(daytemp)<=9) { printf("%*s %s", getpadding(9, daytemp), daytemp, getvalue(data.day[1].rx, data.day[1].rxk, 11, 1)); } else { printf("%-*s %s", getpadding(11, daytemp), daytemp, getvalue(data.day[1].rx, data.day[1].rxk, 11, 1)); } printf(" | %s", getvalue(data.day[1].tx, data.day[1].txk, 11, 1)); printf(" | %s", getvalue(data.day[1].rx+data.day[1].tx, data.day[1].rxk+data.day[1].txk, 11, 1)); if (cfg.ostyle >= 2) { printf(" | %s", getrate(data.day[1].rx+data.day[1].tx, data.day[1].rxk+data.day[1].txk, 86400, 14)); } printf("\n"); } indent(5); if (strlen(daytemp2)<=9) { printf("%*s %s", getpadding(9, daytemp2), daytemp2, getvalue(data.day[0].rx, data.day[0].rxk, 11, 1)); } else { printf("%-*s %s", getpadding(11, daytemp2), daytemp2, getvalue(data.day[0].rx, data.day[0].rxk, 11, 1)); } printf(" | %s", getvalue(data.day[0].tx, data.day[0].txk, 11, 1)); printf(" | %s", getvalue(data.day[0].rx+data.day[0].tx, data.day[0].rxk+data.day[0].txk, 11, 1)); if (cfg.ostyle >= 2) { printf(" | %s", getrate(data.day[0].rx+data.day[0].tx, data.day[0].rxk+data.day[0].txk, d->tm_sec+(d->tm_min*60)+(d->tm_hour*3600), 14)); } printf("\n"); indent(5); if (cfg.ostyle >= 2) { printf("------------------------+-------------+-------------+---------------\n"); } else { printf("------------------------+-------------+------------\n"); } indent(5); printf("estimated %s", getvalue(e_rx, 0, 11, 2)); printf(" | %s", getvalue(e_tx, 0, 11, 2)); printf(" | %s", getvalue(e_rx+e_tx, 0, 11, 2)); if (cfg.ostyle >= 2) { printf(" |\n"); } else { printf("\n"); } } void showshort(void) { struct tm *d; char datebuff[16]; char daytemp[32], daytemp2[32]; uint64_t e_rx, e_tx; time_t current, yesterday; current=time(NULL); yesterday=current-86400; e_rx=e_tx=0; if (strcmp(data.interface, data.nick)==0) { if (data.active) printf(" %s:\n", data.interface); else printf(" %s [disabled]:\n", data.interface); } else { if (data.active) printf(" %s (%s):\n", data.nick, data.interface); else printf(" %s (%s) [disabled]:\n", data.nick, data.interface); } if (data.month[1].used) { d=localtime(&data.month[1].month); if (strftime(datebuff, 16, cfg.mformat, d)<=8) { printf(" %*s %s", getpadding(8, datebuff), datebuff, getvalue(data.month[1].rx, data.month[1].rxk, 11, 1)); } else { printf(" %-*s %s", getpadding(11, datebuff), datebuff, getvalue(data.month[1].rx, data.month[1].rxk, 11, 1)); } printf(" / %s", getvalue(data.month[1].tx, data.month[1].txk, 11, 1)); printf(" / %s", getvalue(data.month[1].rx+data.month[1].tx, data.month[1].rxk+data.month[1].txk, 11, 1)); printf("\n"); } d=localtime(&data.month[0].month); if (cfg.ostyle != 0) { if ( data.month[0].rx==0 || data.month[0].tx==0 || (data.lastupdated-data.month[0].month)==0 ) { e_rx=e_tx=0; } else { e_rx=(data.month[0].rx/(float)(mosecs()))*(dmonth(d->tm_mon)*86400); e_tx=(data.month[0].tx/(float)(mosecs()))*(dmonth(d->tm_mon)*86400); } } if (strftime(datebuff, 16, cfg.mformat, d)<=8) { printf(" %*s %s", getpadding(8, datebuff), datebuff, getvalue(data.month[0].rx, data.month[0].rxk, 11, 1)); } else { printf(" %-*s %s", getpadding(11, datebuff), datebuff, getvalue(data.month[0].rx, data.month[0].rxk, 11, 1)); } printf(" / %s", getvalue(data.month[0].tx, data.month[0].txk, 11, 1)); printf(" / %s", getvalue(data.month[0].rx+data.month[0].tx, data.month[0].rxk+data.month[0].txk, 11, 1)); if (cfg.ostyle != 0) { printf(" / %s", getvalue(e_rx+e_tx, 0, 11, 1)); } printf("\n"); /* get formated date for today */ d=localtime(¤t); strftime(datebuff, 16, cfg.dformat, d); /* get formated date for lastest day in database */ d=localtime(&data.day[0].date); strftime(daytemp2, 16, cfg.dformat, d); /* change daytemp to today if formated days match */ if (strcmp(datebuff, daytemp2)==0) { strncpy_nt(daytemp2, "today", 32); } /* use database update time for estimates */ d=localtime(&data.lastupdated); if (cfg.ostyle != 0) { if ( data.day[0].rx==0 || data.day[0].tx==0 || (d->tm_hour*60+d->tm_min)==0 ) { e_rx=e_tx=0; } else { e_rx=((data.day[0].rx)/(float)(d->tm_hour*60+d->tm_min))*1440; e_tx=((data.day[0].tx)/(float)(d->tm_hour*60+d->tm_min))*1440; } } /* get formated date for yesterday */ d=localtime(&yesterday); strftime(datebuff, 16, cfg.dformat, d); /* get formated date for previous day in database */ d=localtime(&data.day[1].date); strftime(daytemp, 16, cfg.dformat, d); /* change daytemp to yesterday if formated days match */ if (strcmp(datebuff, daytemp)==0) { strncpy_nt(daytemp, "yesterday", 32); } if (data.day[1].date!=0) { printf(" %*s %s", getpadding(9, daytemp), daytemp, getvalue(data.day[1].rx, data.day[1].rxk, 11, 1)); printf(" / %s", getvalue(data.day[1].tx, data.day[1].txk, 11, 1)); printf(" / %s", getvalue(data.day[1].rx+data.day[1].tx, data.day[1].rxk+data.day[1].txk, 11, 1)); printf("\n"); } printf(" %*s %s", getpadding(9, daytemp2), daytemp2, getvalue(data.day[0].rx, data.day[0].rxk, 11, 1)); printf(" / %s", getvalue(data.day[0].tx, data.day[0].txk, 11, 1)); printf(" / %s", getvalue(data.day[0].rx+data.day[0].tx, data.day[0].rxk+data.day[0].txk, 11, 1)); if (cfg.ostyle != 0) { printf(" / %s", getvalue(e_rx+e_tx, 0, 11, 2)); } printf("\n\n"); } void showdays(void) { int i, used; struct tm *d; char datebuff[16]; uint64_t e_rx, e_tx, t_rx, max; int t_rxk; e_rx=e_tx=t_rx=t_rxk=0; printf("\n"); if (strcmp(data.interface, data.nick)==0) { if (data.active) printf(" %s / daily\n\n", data.interface); else printf(" %s [disabled] / daily\n\n", data.interface); } else { if (data.active) printf(" %s (%s) / daily\n\n", data.nick, data.interface); else printf(" %s (%s) [disabled] / daily\n\n", data.nick, data.interface); } if (cfg.ostyle == 3) { printf(" day rx | tx | total | avg. rate\n"); printf(" ------------------------+-------------+-------------+---------------\n"); } else { printf(" day rx | tx | total\n"); if (cfg.ostyle != 0) { printf("-------------------------+-------------+---------------------------------------\n"); } else { printf("-------------------------+-------------+------------\n"); } } /* search maximum */ max=0; for (i=29;i>=0;i--) { if (data.day[i].used) { t_rx=data.day[i].rx+data.day[i].tx; t_rxk=data.day[i].rxk+data.day[i].txk; if (t_rxk>=1024) { t_rx+=t_rxk/1024; t_rxk-=(t_rxk/1024)*1024; } t_rx=(t_rx*1024)+t_rxk; if (t_rx>max) { max=t_rx; } } } used=0; for (i=29;i>=0;i--) { if (data.day[i].used) { d=localtime(&data.day[i].date); strftime(datebuff, 16, cfg.dformat, d); if (cfg.ostyle == 3) { printf(" "); } if (strlen(datebuff)<=9) { printf(" %*s %s", getpadding(9, datebuff), datebuff, getvalue(data.day[i].rx, data.day[i].rxk, 11, 1)); } else { printf(" %-*s %s", getpadding(11, datebuff), datebuff, getvalue(data.day[i].rx, data.day[i].rxk, 11, 1)); } printf(" | %s", getvalue(data.day[i].tx, data.day[i].txk, 11, 1)); printf(" | %s", getvalue(data.day[i].rx+data.day[i].tx, data.day[i].rxk+data.day[i].txk, 11, 1)); if (cfg.ostyle == 3) { if (i==0) { d=localtime(&data.lastupdated); printf(" | %s", getrate(data.day[i].rx+data.day[i].tx, data.day[i].rxk+data.day[i].txk, d->tm_sec+(d->tm_min*60)+(d->tm_hour*3600), 14)); } else { printf(" | %s", getrate(data.day[i].rx+data.day[i].tx, data.day[i].rxk+data.day[i].txk, 86400, 14)); } } else if (cfg.ostyle != 0) { showbar(data.day[i].rx, data.day[i].rxk, data.day[i].tx, data.day[i].txk, max, 24); } printf("\n"); used++; } } if (used==0) printf(" no data available\n"); if (cfg.ostyle == 3) { printf(" ------------------------+-------------+-------------+---------------\n"); } else { if (cfg.ostyle != 0) { printf("-------------------------+-------------+---------------------------------------\n"); } else { printf("-------------------------+-------------+------------\n"); } } if (used!=0) { /* use database update time for estimates */ d=localtime(&data.lastupdated); if ( data.day[0].rx==0 || data.day[0].tx==0 || (d->tm_hour*60+d->tm_min)==0 ) { e_rx=e_tx=0; } else { e_rx=((data.day[0].rx)/(float)(d->tm_hour*60+d->tm_min))*1440; e_tx=((data.day[0].tx)/(float)(d->tm_hour*60+d->tm_min))*1440; } if (cfg.ostyle == 3) { printf(" "); } printf(" estimated %s", getvalue(e_rx, 0, 11, 2)); printf(" | %s", getvalue(e_tx, 0, 11, 2)); printf(" | %s", getvalue(e_rx+e_tx, 0, 11, 2)); if (cfg.ostyle == 3) { printf(" |"); } printf("\n"); } } void showmonths(void) { int i, used; struct tm *d; char datebuff[16]; uint64_t e_rx, e_tx, t_rx, max; int t_rxk; e_rx=e_tx=t_rx=t_rxk=0; printf("\n"); if (strcmp(data.interface, data.nick)==0) { if (data.active) printf(" %s / monthly\n\n", data.interface); else printf(" %s [disabled] / monthly\n\n", data.interface); } else { if (data.active) printf(" %s (%s) / monthly\n\n", data.nick, data.interface); else printf(" %s (%s) [disabled] / monthly\n\n", data.nick, data.interface); } if (cfg.ostyle == 3) { printf(" month rx | tx | total | avg. rate\n"); printf(" ------------------------+-------------+-------------+---------------\n"); } else { printf(" month rx | tx | total\n"); if (cfg.ostyle != 0) { printf("-------------------------+-------------+---------------------------------------\n"); } else { printf("-------------------------+-------------+------------\n"); } } /* search maximum */ max=0; for (i=11;i>=0;i--) { if (data.month[i].used) { t_rx=data.month[i].rx+data.month[i].tx; t_rxk=data.month[i].rxk+data.month[i].txk; if (t_rxk>=1024) { t_rx+=t_rxk/1024; t_rxk-=(t_rxk/1024)*1024; } t_rx=(t_rx*1024)+t_rxk; if (t_rx>max) { max=t_rx; } } } used=0; for (i=11;i>=0;i--) { if (data.month[i].used) { d=localtime(&data.month[i].month); if (cfg.ostyle == 3) { printf(" "); } if (strftime(datebuff, 16, cfg.mformat, d)<=9) { printf(" %*s %s", getpadding(9, datebuff), datebuff, getvalue(data.month[i].rx, data.month[i].rxk, 11, 1)); } else { printf(" %-*s %s", getpadding(11, datebuff), datebuff, getvalue(data.month[i].rx, data.month[i].rxk, 11, 1)); } printf(" | %s", getvalue(data.month[i].tx, data.month[i].txk, 11, 1)); printf(" | %s", getvalue(data.month[i].rx+data.month[i].tx, data.month[i].rxk+data.month[i].txk, 11, 1)); if (cfg.ostyle == 3) { if (i == 0) { printf(" | %s", getrate(data.month[0].rx+data.month[0].tx, data.month[0].rxk+data.month[0].txk, mosecs(), 14)); } else { printf(" | %s", getrate(data.month[i].rx+data.month[i].tx, data.month[i].rxk+data.month[i].txk, dmonth(d->tm_mon)*86400, 14)); } } else if (cfg.ostyle != 0) { showbar(data.month[i].rx, data.month[i].rxk, data.month[i].tx, data.month[i].txk, max, 24); } printf("\n"); used++; } } if (used == 0) printf(" no data available\n"); if (cfg.ostyle == 3) { printf(" ------------------------+-------------+-------------+---------------\n"); } else { if (cfg.ostyle != 0) { printf("-------------------------+-------------+---------------------------------------\n"); } else { printf("-------------------------+-------------+------------\n"); } } if (used!=0) { /* use database update time for estimates */ d=localtime(&data.month[0].month); if ( data.month[0].rx==0 || data.month[0].tx==0 || (data.lastupdated-data.month[0].month)==0 ) { e_rx=e_tx=0; } else { e_rx=(data.month[0].rx/(float)(mosecs()))*(dmonth(d->tm_mon)*86400); e_tx=(data.month[0].tx/(float)(mosecs()))*(dmonth(d->tm_mon)*86400); } if (cfg.ostyle == 3) { printf(" "); } printf(" estimated %s", getvalue(e_rx, 0, 12, 2)); printf(" | %s", getvalue(e_tx, 0, 11, 2)); printf(" | %s", getvalue(e_rx+e_tx, 0, 11, 2)); if (cfg.ostyle == 3) { printf(" |"); } printf("\n"); } } void showtop(void) { int i, used; struct tm *d; char datebuff[16]; uint64_t t_rx, max; int t_rxk; t_rx=t_rxk=0; printf("\n"); if (strcmp(data.interface, data.nick)==0) { if (data.active) printf(" %s / top 10\n\n", data.interface); else printf(" %s [disabled] / top 10\n\n", data.interface); } else { if (data.active) printf(" %s (%s) / top 10\n\n", data.nick, data.interface); else printf(" %s (%s) [disabled] / top 10\n\n", data.nick, data.interface); } if (cfg.ostyle == 3) { printf(" # day rx | tx | total | avg. rate\n"); printf(" -----------------------------+-------------+-------------+---------------\n"); } else { printf(" # day rx | tx | total\n"); if (cfg.ostyle != 0) { printf("-------------------------------+-------------+---------------------------------\n"); } else { printf("-------------------------------+-------------+------------\n"); } } /* search maximum */ max=0; for (i=0;i<=9;i++) { if (data.top10[i].used) { t_rx=data.top10[i].rx+data.top10[i].tx; t_rxk=data.top10[i].rxk+data.top10[i].txk; if (t_rxk>=1024) { t_rx+=t_rxk/1024; t_rxk-=(t_rxk/1024)*1024; } t_rx=(t_rx*1024)+t_rxk; if (t_rx>max) { max=t_rx; } } } used=0; for (i=0;i<=9;i++) { if (data.top10[i].used) { d=localtime(&data.top10[i].date); strftime(datebuff, 16, cfg.tformat, d); if (cfg.ostyle == 3) { printf(" "); } printf(" %2d %-*s %s", i+1, getpadding(11, datebuff), datebuff, getvalue(data.top10[i].rx, data.top10[i].rxk, 11, 1)); printf(" | %s", getvalue(data.top10[i].tx, data.top10[i].txk, 11, 1)); printf(" | %s", getvalue(data.top10[i].rx+data.top10[i].tx, data.top10[i].rxk+data.top10[i].txk, 11, 1)); if (cfg.ostyle == 3) { printf(" | %s", getrate(data.top10[i].rx+data.top10[i].tx, data.top10[i].rxk+data.top10[i].txk, 86400, 14)); } else if (cfg.ostyle != 0) { showbar(data.top10[i].rx, data.top10[i].rxk, data.top10[i].tx, data.top10[i].txk, max, 18); } printf("\n"); used++; } } if (used == 0) printf(" no data available\n"); if (cfg.ostyle == 3) { printf(" -----------------------------+-------------+-------------+---------------\n"); } else { if (cfg.ostyle != 0) { printf("-------------------------------+-------------+---------------------------------\n"); } else { printf("-------------------------------+-------------+------------\n"); } } } void showweeks(void) { int i, used, week, temp; struct tm *d; char daytemp[32]; uint64_t e_rx, e_tx, t_rx, t_tx; int t_rxk, t_txk; time_t current; current=time(NULL); e_rx=e_tx=t_rx=t_tx=t_rxk=t_txk=0; printf("\n"); if (strcmp(data.interface, data.nick)==0) { if (data.active) printf(" %s / weekly\n\n", data.interface); else printf(" %s [disabled] / weekly\n\n", data.interface); } else { if (data.active) printf(" %s (%s) / weekly\n\n", data.nick, data.interface); else printf(" %s (%s) [disabled] / weekly\n\n", data.nick, data.interface); } indent(3); if (cfg.ostyle >= 2) { printf(" rx | tx | total | avg. rate\n"); indent(3); printf("---------------------------+-------------+-------------+---------------\n"); } else { printf(" rx | tx | total\n"); indent(3); printf("---------------------------+-------------+------------\n"); } /* get current week number */ d=localtime(¤t); strftime(daytemp, 16, "%V", d); week=atoi(daytemp); /* last 7 days */ used=0; temp=0; t_rx=t_tx=t_rxk=t_txk=0; for (i=0;i<30;i++) { if ((data.day[i].used) && (data.day[i].date>=current-604800)) { addtraffic(&t_rx, &t_rxk, data.day[i].rx, data.day[i].rxk); addtraffic(&t_tx, &t_txk, data.day[i].tx, data.day[i].txk); used++; } } if (used!=0) { indent(3); printf(" last 7 days %s", getvalue(t_rx, t_rxk, 11, 1)); printf(" | %s", getvalue(t_tx, t_txk, 11, 1)); printf(" | %s", getvalue(t_rx+t_tx, t_rxk+t_txk, 11, 1)); if (cfg.ostyle >= 2) { d=localtime(&data.lastupdated); printf(" | %s", getrate(t_rx+t_tx, t_rxk+t_txk, 518400+d->tm_sec+(d->tm_min*60)+(d->tm_hour*3600), 14)); } printf("\n"); temp++; } /* traffic for previous week */ used=0; t_rx=t_tx=t_rxk=t_txk=0; for (i=0;i<30;i++) { if (data.day[i].used) { d=localtime(&data.day[i].date); strftime(daytemp, 16, "%V", d); if (atoi(daytemp)==week-1) { addtraffic(&t_rx, &t_rxk, data.day[i].rx, data.day[i].rxk); addtraffic(&t_tx, &t_txk, data.day[i].tx, data.day[i].txk); used++; } } } if (used!=0) { indent(3); printf(" last week %s", getvalue(t_rx, t_rxk, 11, 1)); printf(" | %s", getvalue(t_tx, t_txk, 11, 1)); printf(" | %s", getvalue(t_rx+t_tx, t_rxk+t_txk, 11, 1)); if (cfg.ostyle >= 2) { printf(" | %s", getrate(t_rx+t_tx, t_rxk+t_txk, 604800, 14)); } printf("\n"); temp++; } /* this week */ used=0; t_rx=t_tx=t_rxk=t_txk=0; for (i=0;i<30;i++) { if (data.day[i].used) { d=localtime(&data.day[i].date); strftime(daytemp, 16, "%V", d); if (atoi(daytemp)==week) { addtraffic(&t_rx, &t_rxk, data.day[i].rx, data.day[i].rxk); addtraffic(&t_tx, &t_txk, data.day[i].tx, data.day[i].txk); used++; } } } /* get estimate for current week */ if (used!=0) { /* use database update time for estimates */ d=localtime(&data.lastupdated); strftime(daytemp, 16, "%u", d); if ( t_rx==0 || t_tx==0 || ((atoi(daytemp)-1)*24+d->tm_hour)==0 ) { e_rx=e_tx=0; } else { e_rx=((t_rx)/(float)((atoi(daytemp)-1)*24+d->tm_hour)*168); e_tx=((t_tx)/(float)((atoi(daytemp)-1)*24+d->tm_hour)*168); } indent(3); printf("current week %s", getvalue(t_rx, t_rxk, 11, 1)); printf(" | %s", getvalue(t_tx, t_txk, 11, 1)); printf(" | %s", getvalue(t_rx+t_tx, t_rxk+t_txk, 11, 1)); if (cfg.ostyle >= 2) { printf(" | %s", getrate(t_rx+t_tx, t_rxk+t_txk, (86400*(atoi(daytemp)-1))+d->tm_sec+(d->tm_min*60)+(d->tm_hour*3600), 14)); } printf("\n"); temp++; } if (temp==0) { indent(3); printf(" no data available\n"); } indent(3); if (cfg.ostyle >= 2) { printf("---------------------------+-------------+-------------+---------------\n"); } else { printf("---------------------------+-------------+------------\n"); } if (used!=0) { indent(3); printf(" estimated %s", getvalue(e_rx, 0, 11, 2)); printf(" | %s", getvalue(e_tx, 0, 11, 2)); printf(" | %s", getvalue(e_rx+e_tx, 0, 11, 2)); if (cfg.ostyle >= 2) { printf(" |\n"); } else { printf("\n"); } } } void showhours(void) { int i, k, s=0, hour, minute, declen=2, div=1; unsigned int j, tmax=0, dots=0; uint64_t max=0; char matrix[24][81]; /* width is one over 80 so that snprintf can write the end char */ char unit[4]; struct tm *d; /* tmax = time max = current hour */ /* max = transfer max */ d=localtime(&data.lastupdated); hour=d->tm_hour; minute=d->tm_min; for (i=0;i<=23;i++) { if (data.hour[i].date>=data.hour[tmax].date) { tmax=i; } if (data.hour[i].rx>=max) { max=data.hour[i].rx; } if (data.hour[i].tx>=max) { max=data.hour[i].tx; } } /* mr. proper */ for (i=0;i<24;i++) { for (j=0;j<81;j++) { matrix[i][j]=' '; } } /* unit selection */ while (max/(pow(1024, div))>=100 && div"); for (i=0;i<3;i++) { snprintf(matrix[14]+(i*28), 25, " h %2$*1$srx (%3$s) %2$*1$stx (%3$s)", 1+cfg.unit, " ", unit); } for (i=10;i>1;i--) matrix[i][2]='|'; matrix[1][2]='^'; matrix[12][2]='|'; /* title */ if (strcmp(data.interface, data.nick)==0) { if (data.active) snprintf(matrix[0], 81, " %s", data.interface); else snprintf(matrix[0], 81, " %s [disabled]", data.interface); } else { if (data.active) snprintf(matrix[0], 81, " %s (%s)", data.nick, data.interface); else snprintf(matrix[0], 81, " %s (%s) [disabled]", data.nick, data.interface); } /* time to the corner */ snprintf(matrix[0]+74, 7, "%02d:%02d", hour, minute); /* numbers under x-axis and graphics :) */ k=5; for (i=23;i>=0;i--) { s=tmax-i; if (s<0) s+=24; snprintf(matrix[12]+k, 81-k, "%02d ", s); dots=10*(data.hour[s].rx/(float)max); for (j=0;jtm_sec+(d->tm_min*60)+(d->tm_hour*3600), 1)); d=localtime(&data.month[0].month); strftime(daytemp, 16, cfg.mformat, d); printf("%s;", daytemp); /* monthly */ printf("%s;", getvalue(data.month[0].rx, data.month[0].rxk, 1, 1)); printf("%s;", getvalue(data.month[0].tx, data.month[0].txk, 1, 1)); printf("%s;", getvalue(data.month[0].rx+data.month[0].tx, data.month[0].rxk+data.month[0].txk, 1, 1)); printf("%s;", getrate(data.month[0].rx+data.month[0].tx, data.month[0].rxk+data.month[0].txk, mosecs(), 1)); /* all time total */ printf("%s;", getvalue(data.totalrx, data.totalrxk, 1, 1)); printf("%s;", getvalue(data.totaltx, data.totaltxk, 1, 1)); printf("%s\n", getvalue(data.totalrx+data.totaltx, data.totalrxk+data.totaltxk, 1, 1)); } void exportdb(void) { int i; printf("version;%d\n", data.version); printf("active;%d\n", data.active); printf("interface;%s\n", data.interface); printf("nick;%s\n", data.nick); printf("created;%u\n", (unsigned int)data.created); printf("updated;%u\n", (unsigned int)data.lastupdated); printf("totalrx;%"PRIu64"\n", data.totalrx); printf("totaltx;%"PRIu64"\n", data.totaltx); printf("currx;%"PRIu64"\n", data.currx); printf("curtx;%"PRIu64"\n", data.curtx); printf("totalrxk;%d\n", data.totalrxk); printf("totaltxk;%d\n", data.totaltxk); printf("btime;%"PRIu64"\n", data.btime); for (i=0;i<=29;i++) { printf("d;%d;%u;%"PRIu64";%"PRIu64";%d;%d;%d\n", i, (unsigned int)data.day[i].date, data.day[i].rx, data.day[i].tx, data.day[i].rxk, data.day[i].txk, data.day[i].used); } for (i=0;i<=11;i++) { printf("m;%d;%u;%"PRIu64";%"PRIu64";%d;%d;%d\n", i, (unsigned int)data.month[i].month, data.month[i].rx, data.month[i].tx, data.month[i].rxk, data.month[i].txk, data.month[i].used); } for (i=0;i<=9;i++) { printf("t;%d;%u;%"PRIu64";%"PRIu64";%d;%d;%d\n", i, (unsigned int)data.top10[i].date, data.top10[i].rx, data.top10[i].tx, data.top10[i].rxk, data.top10[i].txk, data.top10[i].used); } for (i=0;i<=23;i++) { printf("h;%d;%u;%"PRIu64";%"PRIu64"\n", i, (unsigned int)data.hour[i].date, data.hour[i].rx, data.hour[i].tx); } } int showbar(uint64_t rx, int rxk, uint64_t tx, int txk, uint64_t max, int len) { int i, l; if (rxk>=1024) { rx+=rxk/1024; rxk-=(rxk/1024)*1024; } if (txk>=1024) { tx+=txk/1024; txk-=(txk/1024)*1024; } rx=(rx*1024)+rxk; tx=(tx*1024)+txk; if ((rx+tx)max) { return 0; } if (len>0) { printf(" "); if (tx>rx) { l=rintf((rx/(float)(rx+tx)*len)); for (i=0;i 0) && (i > 0)) { printf("%*s", i, " "); } } vnstat-1.14/src/dbmerge.h0000644000000000000000000000025012355210223014011 0ustar rootroot#ifndef DBMERGE_H #define DBMERGE_H int mergedb(char iface[], char dirname[]); void emptydb(DATA *dat); int mergewith(DATA *dat); void cleanmerged(DATA *dat); #endif vnstat-1.14/src/vnstatd.h0000644000000000000000000000010212370505005014065 0ustar rootroot#ifndef VNSTATD_H #define VNSTATD_H void showhelp(void); #endif vnstat-1.14/src/traffic.c0000644000000000000000000001732712355210223014032 0ustar rootroot#include "common.h" #include "ifinfo.h" #include "misc.h" #include "traffic.h" void trafficmeter(char iface[], int sampletime) { /* received bytes packets errs drop fifo frame compressed multicast */ /* transmitted bytes packets errs drop fifo colls carrier compressed */ uint64_t rx, tx, rxp, txp; IFINFO firstinfo; int i, len; char buffer[256]; /* less than 2 seconds doesn't produce good results */ if (sampletime<2) { printf("Error: Time for sampling too short.\n"); exit(EXIT_FAILURE); } /* read interface info and get values to the first list */ if (!getifinfo(iface)) { printf("Error: Interface \"%s\" not available, exiting.\n", iface); exit(EXIT_FAILURE); } firstinfo.rx = ifinfo.rx; firstinfo.tx = ifinfo.tx; firstinfo.rxp = ifinfo.rxp; firstinfo.txp = ifinfo.txp; /* wait sampletime and print some nice dots so that the user thinks something is done :) */ snprintf(buffer, 256, "Sampling %s (%d seconds average)", iface,sampletime); printf("%s", buffer); fflush(stdout); sleep(sampletime/3); printf("."); fflush(stdout); sleep(sampletime/3); printf("."); fflush(stdout); sleep(sampletime/3); printf("."); fflush(stdout); if ((sampletime/3)*3!=sampletime) { sleep(sampletime-((sampletime/3)*3)); } len=strlen(buffer)+3; for (i=0;irx) { rxmin = rx; } if (txmin>tx) { txmin = tx; } if (rxmaxrxp) { rxpmin = rxp; } if (txpmin>txp) { txpmin = txp; } if (rxpmax1 && cfg.ostyle!=4) { if (debug) { printf("\nlinelen: %d\n", len); } else { for (i=0;i=10) { printf("\n %s / traffic statistics\n\n", iface); printf(" rx | tx\n"); printf("--------------------------------------+------------------\n"); printf(" bytes %s", getvalue(0, rintf(rxtotal/(float)1024), 13, 1)); printf(" | %s", getvalue(0, rintf(txtotal/(float)1024), 13, 1)); printf("\n"); printf("--------------------------------------+------------------\n"); printf(" max %s", gettrafficrate(rxmax, LIVETIME, 13)); printf(" | %s\n", gettrafficrate(txmax, LIVETIME, 13)); printf(" average %s", gettrafficrate(rxtotal, timespent, 13)); printf(" | %s\n", gettrafficrate(txtotal, timespent, 13)); printf(" min %s", gettrafficrate(rxmin, LIVETIME, 13)); printf(" | %s\n", gettrafficrate(txmin, LIVETIME, 13)); printf("--------------------------------------+------------------\n"); printf(" packets %12"PRIu64" | %12"PRIu64"\n", rxptotal, txptotal); printf("--------------------------------------+------------------\n"); printf(" max %9"PRIu64" p/s | %9"PRIu64" p/s\n", rxpmax/LIVETIME, txpmax/LIVETIME); printf(" average %9"PRIu64" p/s | %9"PRIu64" p/s\n", rxptotal/timespent, txptotal/timespent); printf(" min %9"PRIu64" p/s | %9"PRIu64" p/s\n", rxpmin/LIVETIME, txpmin/LIVETIME); printf("--------------------------------------+------------------\n"); if (timespent<=60) { printf(" time %9"PRIu64" seconds\n", timespent); } else { printf(" time %7.2f minutes\n", timespent/(float)60); } printf("\n"); } } vnstat-1.14/src/vnstat.h0000644000000000000000000000173412463170135013742 0ustar rootroot#ifndef VNSTAT_H #define VNSTAT_H typedef struct { int update, query, newdb, reset, sync, merged, savemerged, import; int create, active, files, force, cleartop, rebuildtotal, traffic; int livetraffic, defaultiface, delete, livemode; char interface[32], dirname[512], nick[32], filename[512]; char definterface[32], cfgfile[512], *ifacelist, jsonmode, xmlmode; } PARAMS; void initparams(PARAMS *p); int synccounters(const char *iface, const char *dirname); void showhelp(PARAMS *p); void showlonghelp(PARAMS *p); void handledbmerge(PARAMS *p); void handlecounterreset(PARAMS *p); void handleimport(PARAMS *p); void handlecountersync(PARAMS *p); void handledelete(PARAMS *p); void handlecleartop10(PARAMS *p); void handlerebuildtotal(PARAMS *p); void handleenabledisable(PARAMS *p); void handlecreate(PARAMS *p); void handleupdate(PARAMS *p); void handleshowdatabases(PARAMS *p); void showoneinterface(PARAMS *p, const char *interface); void handletrafficmeters(PARAMS *p); #endif vnstat-1.14/src/dbaccess.h0000644000000000000000000000120512455260303014161 0ustar rootroot#ifndef DBACCESS_H #define DBACCESS_H int readdb(const char *iface, const char *dirname); void initdb(void); int writedb(const char *iface, const char *dirname, int newdb); int backupdb(const char *current, const char *backup); int convertdb(void); int lockdb(int fd, int dbwrite); int checkdb(const char *iface, const char *dirname); int removedb(const char *iface, const char *dirname); void cleanhours(void); void rotatedays(void); void rotatemonths(void); void cleartop10(const char *iface, const char *dirname); void rebuilddbtotal(const char *iface, const char *dirname); int validatedb(void); int importdb(const char *filename); #endif vnstat-1.14/src/vnstat.c0000644000000000000000000007221412506566067013751 0ustar rootroot/* vnStat - Copyright (c) 2002-2015 Teemu Toivola 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; version 2 dated June, 1991. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave., Cambridge, MA 02139, USA. */ #include "common.h" #include "ifinfo.h" #include "traffic.h" #include "dbxml.h" #include "dbjson.h" #include "dbshow.h" #include "dbaccess.h" #include "dbmerge.h" #include "misc.h" #include "cfg.h" #include "ibw.h" #include "vnstat.h" int main(int argc, char *argv[]) { int i, currentarg; DIR *dir = NULL; struct dirent *di = NULL; PARAMS p; initparams(&p); /* early check for debug and config parameter */ if (argc > 1) { for (currentarg=1; currentarg 4 || cfg.ostyle < 0) { printf("Error: Invalid style parameter \"%d\" for --style.\n", cfg.ostyle); printf(" Valid parameters:\n"); printf(" 0 - a more narrow output\n"); printf(" 1 - enable bar column if available\n"); printf(" 2 - average traffic rate in summary and weekly outputs\n"); printf(" 3 - average traffic rate in all outputs if available\n"); printf(" 4 - disable terminal control characters in -l / --live\n"); return 1; } cfg.ostyle = atoi(argv[currentarg+1]); if (debug) printf("Style changed: %d\n", cfg.ostyle); currentarg++; continue; } else { printf("Error: Style parameter for --style missing.\n"); printf(" Valid parameters:\n"); printf(" 0 - a more narrow output\n"); printf(" 1 - enable bar column if available\n"); printf(" 2 - average traffic rate in summary and weekly outputs\n"); printf(" 3 - average traffic rate in all outputs if available\n"); printf(" 4 - disable terminal control characters in -l / --live\n"); return 1; } } else if ((strcmp(argv[currentarg],"--dbdir"))==0) { if (currentarg+1 1 || cfg.rateunit < 0) { printf("Error: Invalid parameter \"%d\" for --rateunit.\n", cfg.rateunit); printf(" Valid parameters:\n"); printf(" 0 - bytes\n"); printf(" 1 - bits\n"); return 1; } cfg.rateunit = atoi(argv[currentarg+1]); if (debug) printf("Rateunit changed: %d\n", cfg.rateunit); currentarg++; continue; } else { cfg.rateunit = !cfg.rateunit; if (debug) printf("Rateunit changed: %d\n", cfg.rateunit); } } else if (strcmp(argv[currentarg],"--enable")==0) { p.active=1; p.query=0; } else if ((strcmp(argv[currentarg],"-tr")==0) || (strcmp(argv[currentarg],"--traffic")==0)) { if (currentarg+1 1 || p.livemode < 0) { printf("Error: Invalid mode parameter \"%s\" for -l / --live.\n", argv[currentarg+1]); printf(" Valid parameters:\n"); printf(" 0 - show packets per second (default)\n"); printf(" 1 - show transfer counters\n"); return 1; } p.livemode = atoi(argv[currentarg+1]); currentarg++; } p.livetraffic=1; p.query=0; } else if (strcmp(argv[currentarg],"--force")==0) { p.force=1; } else if (strcmp(argv[currentarg],"--cleartop")==0) { p.cleartop=1; } else if (strcmp(argv[currentarg],"--rebuildtotal")==0) { p.rebuildtotal=1; } else if (strcmp(argv[currentarg],"--disable")==0) { p.active=0; p.query=0; } else if (strcmp(argv[currentarg],"--testkernel")==0) { i=kerneltest(); return i; } else if (strcmp(argv[currentarg],"--showconfig")==0) { printcfgfile(); return 0; } else if (strcmp(argv[currentarg],"--delete")==0) { p.delete=1; p.query=0; } else if (strcmp(argv[currentarg],"--iflist")==0) { getiflist(&p.ifacelist, 1); printf("Available interfaces: %s\n", p.ifacelist); free(p.ifacelist); return 0; } else if ((strcmp(argv[currentarg],"-v")==0) || (strcmp(argv[currentarg],"--version")==0)) { printf("vnStat %s by Teemu Toivola \n", VNSTATVERSION); return 0; } else if ((strcmp(argv[currentarg],"-r")==0) || (strcmp(argv[currentarg],"--reset")==0)) { p.reset=1; p.query=0; } else if (strcmp(argv[currentarg],"--sync")==0) { p.sync=1; p.query=0; } else { printf("Unknown parameter \"%s\". Use --help for help.\n",argv[currentarg]); return 1; } } /* check if the database dir exists and if it contains files */ if (!p.traffic && !p.livetraffic) { if ((dir=opendir(p.dirname))!=NULL) { if (debug) printf("Dir OK\n"); while ((di=readdir(dir))) { if ((di->d_name[0]=='.') || (strcmp(di->d_name, DATABASEFILE)==0)) { continue; } strncpy_nt(p.definterface, di->d_name, 32); p.files++; } if (debug) printf("%d file(s) found\n", p.files); if (p.files>1) { strncpy_nt(p.definterface, cfg.iface, 32); } closedir(dir); } else { printf("Error: Unable to open database directory \"%s\": %s\n", p.dirname, strerror(errno)); if (errno==ENOENT) { printf("The vnStat daemon should have created this directory when started.\n"); printf("Check that it is is configured and running. See also \"man vnstatd\".\n"); } else { printf("Make sure it is at least read enabled for current user.\n"); printf("Use --help for help.\n"); } return 1; } } /* set used interface if none specified and make sure it's null terminated */ if (p.defaultiface) { strncpy_nt(p.interface, p.definterface, 32); } p.interface[31]='\0'; /* parameter handlers */ handledbmerge(&p); handlecounterreset(&p); handleimport(&p); handlecountersync(&p); handledelete(&p); handlecleartop10(&p); handlerebuildtotal(&p); handleenabledisable(&p); handlecreate(&p); handleupdate(&p); handleshowdatabases(&p); handletrafficmeters(&p); /* show something if nothing was shown previously */ if (!p.query && !p.update && !p.create && !p.reset && !p.sync && p.active==-1 && !p.cleartop && !p.rebuildtotal && !p.traffic && !p.livetraffic) { /* give more help if there's no database */ if (p.files==0) { getiflist(&p.ifacelist, 1); printf("No database found, nothing to do. Use --help for help.\n\n"); printf("A new database can be created with the following command:\n"); printf(" %s --create -i eth0\n\n", argv[0]); printf("Replace 'eth0' with the interface that should be monitored.\n\n"); printf("The following interfaces are currently available:\n %s\n", p.ifacelist); free(p.ifacelist); } else { printf("Nothing to do. Use --help for help.\n"); } } /* cleanup */ ibwflush(); return 0; } void initparams(PARAMS *p) { noexit = 0; /* allow functions to exit in case of error */ debug = 0; /* debug disabled by default */ p->create = 0; p->update = 0; p->query = 1; p->newdb = 0; p->reset = 0; p->sync = 0; p->merged = 0; p->savemerged = 0; p->import = 0; p->active = -1; p->files = 0; p->force = 0; p->cleartop = 0; p->rebuildtotal = 0; p->traffic = 0; p->livetraffic = 0; p->defaultiface = 1; p->delete=0; p->livemode = 0; p->ifacelist = NULL; p->cfgfile[0] = '\0'; p->jsonmode = 'a'; p->xmlmode = 'a'; } int synccounters(const char *iface, const char *dirname) { readdb(iface, dirname); if (!getifinfo(iface)) { printf("Error: Unable to sync unavailable interface \"%s\".", iface); return 0; } /* set counters to current without counting traffic */ data.currx = ifinfo.rx; data.curtx = ifinfo.tx; writedb(iface, dirname, 0); return 1; } void showhelp(PARAMS *p) { printf(" vnStat %s by Teemu Toivola \n\n", VNSTATVERSION); printf(" -q, --query query database\n"); printf(" -h, --hours show hours\n"); printf(" -d, --days show days\n"); printf(" -m, --months show months\n"); printf(" -w, --weeks show weeks\n"); printf(" -t, --top10 show top 10 days\n"); printf(" -s, --short use short output\n"); printf(" -u, --update update database\n"); printf(" -i, --iface select interface (default: %s)\n", p->definterface); printf(" -?, --help short help\n"); printf(" -v, --version show version\n"); printf(" -tr, --traffic calculate traffic\n"); printf(" -ru, --rateunit swap configured rate unit\n"); printf(" -l, --live show transfer rate in real time\n\n"); printf("See also \"--longhelp\" for complete options list and \"man vnstat\".\n"); } void showlonghelp(PARAMS *p) { printf(" vnStat %s by Teemu Toivola \n\n", VNSTATVERSION); printf(" Query:\n"); printf(" -q, --query query database\n"); printf(" -h, --hours show hours\n"); printf(" -d, --days show days\n"); printf(" -m, --months show months\n"); printf(" -w, --weeks show weeks\n"); printf(" -t, --top10 show top 10 days\n"); printf(" -s, --short use short output\n"); printf(" -ru, --rateunit swap configured rate unit\n"); printf(" --oneline show simple parseable format\n"); printf(" --exportdb dump database in text format\n"); printf(" --importdb import previously exported database\n"); printf(" --json show database in json format\n"); printf(" --xml show database in xml format\n"); printf(" Modify:\n"); printf(" --create create database\n"); printf(" --delete delete database\n"); printf(" -u, --update update database\n"); printf(" -r, --reset reset interface counters\n"); printf(" --sync sync interface counters\n"); printf(" --enable enable interface\n"); printf(" --disable disable interface\n"); printf(" --nick set a nickname for interface\n"); printf(" --cleartop clear the top 10\n"); printf(" --rebuildtotal rebuild total transfers from months\n"); printf(" Misc:\n"); printf(" -i, --iface select interface (default: %s)\n", p->definterface); printf(" -?, --help short help\n"); printf(" -D, --debug show some additional debug information\n"); printf(" -v, --version show version\n"); printf(" -tr, --traffic calculate traffic\n"); printf(" -l, --live show transfer rate in real time\n"); printf(" --style select output style (0-4)\n"); printf(" --iflist show list of available interfaces\n"); printf(" --dbdir select database directory\n"); printf(" --locale set locale\n"); printf(" --config select config file\n"); printf(" --savemerged save merged database to current directory\n"); printf(" --showconfig dump config file with current settings\n"); printf(" --testkernel check if the kernel is broken\n"); printf(" --longhelp display this help\n\n"); printf("See also \"man vnstat\".\n"); } void handledbmerge(PARAMS *p) { /* db merge */ if (p->query && strstr(p->interface, "+")) { if (mergedb(p->interface, p->dirname)) { p->files = p->merged = 1; } else { exit(EXIT_FAILURE); } } /* save merged database */ if (p->merged && p->savemerged) { data.lastupdated = 0; if (writedb("mergeddb", ".", 2)) { printf("Database saved as \"mergeddb\" in the current directory.\n"); } exit(EXIT_SUCCESS); } } void handlecounterreset(PARAMS *p) { if (!p->reset) { return; } if (!spacecheck(p->dirname) && !p->force) { printf("Error: Not enough free diskspace available.\n"); exit(EXIT_FAILURE); } readdb(p->interface, p->dirname); data.currx=0; data.curtx=0; writedb(p->interface, p->dirname, 0); if (debug) printf("Counters reseted for \"%s\"\n", data.interface); } void handleimport(PARAMS *p) { if (!p->import) { return; } if (p->defaultiface) { printf("Error: Specify interface to be imported using the -i parameter.\n"); exit(EXIT_FAILURE); } if (!spacecheck(p->dirname) && !p->force) { printf("Error: Not enough free diskspace available.\n"); exit(EXIT_FAILURE); } if (checkdb(p->interface, p->dirname) && !p->force) { printf("Error: Database file for interface \"%s\" already exists.\n", p->interface); printf("Add --force parameter to overwrite it.\n"); exit(EXIT_FAILURE); } initdb(); if (!importdb(p->filename)) { exit(EXIT_FAILURE); } if (!validatedb()) { printf("Error: validation of imported database failed.\n"); exit(EXIT_FAILURE); } strncpy_nt(data.interface, p->interface, 32); if (writedb(p->interface, p->dirname, 1)) { printf("Database import for \"%s\" completed.\n", data.interface); } exit(EXIT_SUCCESS); } void handlecountersync(PARAMS *p) { if (!p->sync) { return; } if (!spacecheck(p->dirname) && !p->force) { printf("Error: Not enough free diskspace available.\n"); exit(EXIT_FAILURE); } if (!synccounters(p->interface, p->dirname)) { exit(EXIT_FAILURE); } if (debug) printf("Counters synced for \"%s\"\n", data.interface); } void handledelete(PARAMS *p) { if (!p->delete) { return; } if (!p->force) { printf("Warning:\nThe current option would delete the database for \"%s\".\n", p->interface); printf("Use --force in order to really do that.\n"); exit(EXIT_FAILURE); } if (checkdb(p->interface, p->dirname)) { if (removedb(p->interface, p->dirname)) { printf("Database for interface \"%s\" deleted.\n", p->interface); printf("The interface will no longer be monitored. Use --create\n"); printf("if monitoring the interface is again needed.\n"); exit(EXIT_SUCCESS); } else { printf("Error: Deleting database for interface \"%s\" failed.\n", p->interface); exit(EXIT_FAILURE); } } else { printf("Error: No database found for interface \"%s\".\n", p->interface); exit(EXIT_FAILURE); } } void handlecleartop10(PARAMS *p) { if (!p->cleartop) { return; } if (!spacecheck(p->dirname) && !p->force) { printf("Error: Not enough free diskspace available.\n"); exit(EXIT_FAILURE); } if (p->force) { cleartop10(p->interface, p->dirname); p->query=0; } else { printf("Warning:\nThe current option would clear the top 10 for \"%s\".\n", p->interface); printf("Use --force in order to really do that.\n"); exit(EXIT_FAILURE); } } void handlerebuildtotal(PARAMS *p) { if (!p->rebuildtotal) { return; } if (!spacecheck(p->dirname)) { printf("Error: Not enough free diskspace available.\n"); exit(EXIT_FAILURE); } if (p->force) { rebuilddbtotal(p->interface, p->dirname); p->query=0; } else { printf("Warning:\nThe current option would rebuild total tranfers for \"%s\".\n", p->interface); printf("Use --force in order to really do that.\n"); exit(EXIT_FAILURE); } } void handleenabledisable(PARAMS *p) { /* enable & disable */ if (p->active==1) { if (!spacecheck(p->dirname) && !p->force) { printf("Error: Not enough free diskspace available.\n"); exit(EXIT_FAILURE); } p->newdb=readdb(p->interface, p->dirname); if (!data.active && !p->newdb) { data.active=1; writedb(p->interface, p->dirname, 0); if (debug) printf("Interface \"%s\" enabled.\n", data.interface); } else if (!p->newdb) { printf("Interface \"%s\" is already enabled.\n", data.interface); } } else if (p->active==0) { if (!spacecheck(p->dirname) && !p->force) { printf("Error: Not enough free diskspace available.\n"); exit(EXIT_FAILURE); } p->newdb=readdb(p->interface, p->dirname); if (data.active && !p->newdb) { data.active=0; writedb(p->interface, p->dirname, 0); if (debug) printf("Interface \"%s\" disabled.\n", data.interface); } else if (!p->newdb) { printf("Interface \"%s\" is already disabled.\n", data.interface); } } } void handlecreate(PARAMS *p) { if (!p->create) { return; } if (p->defaultiface) { printf("Error: Use -i parameter to specify an interface.\n"); exit(EXIT_FAILURE); } if (!getifinfo(p->interface) && !p->force) { getiflist(&p->ifacelist, 1); printf("Only available interfaces can be added for monitoring.\n\n"); printf("The following interfaces are currently available:\n %s\n", p->ifacelist); free(p->ifacelist); exit(EXIT_FAILURE); } if (checkdb(p->interface, p->dirname)) { printf("Error: Database for interface \"%s\" already exists.\n", p->interface); exit(EXIT_FAILURE); } if (!spacecheck(p->dirname) && !p->force) { printf("Error: Not enough free diskspace available.\n"); exit(EXIT_FAILURE); } printf("Creating database for interface \"%s\"...\n", p->interface); initdb(); strncpy_nt(data.interface, p->interface, 32); strncpy_nt(data.nick, p->interface, 32); if (writedb(p->interface, p->dirname, 1)) { printf("\nRestart the vnStat daemon if it is currently running in order to start monitoring \"%s\".\n", p->interface); } } void handleupdate(PARAMS *p) { DIR *dir = NULL; struct dirent *di = NULL; time_t current; if (!p->update) { return; } current = time(NULL); /* check that there's some free diskspace left */ if (!spacecheck(p->dirname) && !p->force) { printf("Error: Not enough free diskspace available.\n"); exit(EXIT_FAILURE); } /* update every file if -i isn't specified */ if (p->defaultiface) { if ((dir=opendir(p->dirname))==NULL) { return; } p->files=0; while ((di=readdir(dir))) { /* ignore backup files, '.' and '..' dirs */ if ((di->d_name[0]=='.') || (strcmp(di->d_name, DATABASEFILE)==0)) { continue; } p->files++; strncpy_nt(p->interface, di->d_name, 32); if (debug) printf("\nProcessing file \"%s/%s\"...\n", p->dirname, p->interface); p->newdb=readdb(p->interface, p->dirname); if (!data.active) { if (debug) printf("Disabled interface \"%s\" not updated.\n", data.interface); continue; } /* skip interface if not available */ if (!getifinfo(data.interface)) { if (debug) printf("Interface \"%s\" not available, skipping.\n", data.interface); continue; } parseifinfo(p->newdb); /* check that the time is correct */ if ((current>=data.lastupdated) || p->force) { writedb(p->interface, p->dirname, p->newdb); } else { /* print error if previous update is more than 6 hours in the future */ /* otherwise do nothing */ if (data.lastupdated>=(current+21600)) { printf("Error: The previous update was after the current date.\n\n"); printf("Previous update: %s", (char*)asctime(localtime(&data.lastupdated))); printf(" Current time: %s\n", (char*)asctime(localtime(¤t))); printf("Use --force to override this message.\n"); exit(EXIT_FAILURE); } else { if (debug) printf("\"%s\" not updated, %s > %s.\n", data.interface, (char*)asctime(localtime(&data.lastupdated)), (char*)asctime(localtime(¤t))); } } } closedir(dir); if (p->files==0) { /* no database found */ p->update=0; } /* update only selected file */ } else { p->newdb=readdb(p->interface, p->dirname); if (!data.active) { if (debug) printf("Disabled interface \"%s\" not updated.\n", data.interface); return; } if (!getifinfo(data.interface) && !p->force) { getiflist(&p->ifacelist, 1); printf("Only available interfaces can be added for monitoring.\n\n"); printf("The following interfaces are currently available:\n %s\n", p->ifacelist); free(p->ifacelist); exit(EXIT_FAILURE); } parseifinfo(p->newdb); if ((current>=data.lastupdated) || p->force) { if (strcmp(p->nick, "none")!=0) strncpy_nt(data.nick, p->nick, 32); writedb(p->interface, p->dirname, p->newdb); } else { /* print error if previous update is more than 6 hours in the future */ /* otherwise do nothing */ if (data.lastupdated>=(current+21600)) { printf("Error: The previous update was after the current date.\n\n"); printf("Previous update: %s", (char*)asctime(localtime(&data.lastupdated))); printf(" Current time: %s\n", (char*)asctime(localtime(¤t))); printf("Use --force to override this message.\n"); exit(EXIT_FAILURE); } else { if (debug) printf("\"%s\" not updated, %s > %s.\n", data.interface, (char*)asctime(localtime(&data.lastupdated)), (char*)asctime(localtime(¤t))); } } } } void handleshowdatabases(PARAMS *p) { DIR *dir = NULL; struct dirent *di = NULL; int dbcount = 0; if (!p->query) { return; } /* show only specified file */ if (!p->defaultiface) { showoneinterface(p, p->interface); return; } /* show all interfaces if -i isn't specified */ if (p->files==0) { /* printf("No database found.\n"); */ p->query=0; } else if ((cfg.qmode==0 || cfg.qmode==8 || cfg.qmode==10) && (p->files>1)) { if (cfg.qmode==0) { if (cfg.ostyle!=0) { printf("\n rx / tx / total / estimated\n"); } else { printf("\n rx / tx / total\n"); } } else if (cfg.qmode==8) { xmlheader(); } else if (cfg.qmode==10) { jsonheader(); } if ((dir=opendir(p->dirname))==NULL) { return; } while ((di=readdir(dir))) { if ((di->d_name[0]=='.') || (strcmp(di->d_name, DATABASEFILE)==0)) { continue; } strncpy_nt(p->interface, di->d_name, 32); if (debug) printf("\nProcessing file \"%s/%s\"...\n", p->dirname, p->interface); p->newdb=readdb(p->interface, p->dirname); if (p->newdb) { continue; } if (cfg.qmode==0) { showdb(5); } else if (cfg.qmode==8) { showxml(p->xmlmode); } else if (cfg.qmode==10) { showjson(dbcount, p->jsonmode); } dbcount++; } closedir(dir); if (cfg.qmode==8) { xmlfooter(); } else if (cfg.qmode==10) { jsonfooter(); } /* show in qmode if there's only one file or qmode!=0 */ } else { showoneinterface(p, p->definterface); } } void showoneinterface(PARAMS *p, const char *interface) { if (!p->merged) { p->newdb=readdb(interface, p->dirname); } if (p->newdb) { return; } if (cfg.qmode==5) { if (cfg.ostyle!=0) { printf("\n rx / tx / total / estimated\n"); } else { printf("\n rx / tx / total\n"); } } if (cfg.qmode!=8 && cfg.qmode!=10) { showdb(cfg.qmode); } else if (cfg.qmode==8) { xmlheader(); showxml(p->xmlmode); xmlfooter(); } else if (cfg.qmode==10) { jsonheader(); showjson(0, p->jsonmode); jsonfooter(); } } void handletrafficmeters(PARAMS *p) { /* calculate traffic */ if (p->traffic) { trafficmeter(p->interface, cfg.sampletime); } /* live traffic */ if (p->livetraffic) { livetrafficmeter(p->interface, p->livemode); } } vnstat-1.14/src/vnstatd.c0000644000000000000000000001464212506045367014111 0ustar rootroot/* vnStat daemon - Copyright (c) 2008-2015 Teemu Toivola 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; version 2 dated June, 1991. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave., Cambridge, MA 02139, USA. */ #include "common.h" #include "dbcache.h" #include "cfg.h" #include "ibw.h" #include "daemon.h" #include "vnstatd.h" int main(int argc, char *argv[]) { int currentarg; DSTATE s; initdstate(&s); /* early check for debug and config parameter */ if (argc > 1) { for (currentarg=1; currentarg\n", VNSTATVERSION); return 0; } else if ((strcmp(argv[currentarg],"-p")==0) || (strcmp(argv[currentarg],"--pidfile")==0)) { if (currentarg+1= s.updateinterval) { s.updateinterval = cfg.updateinterval; if (debug) { debugtimestamp(); cacheshow(); ibwlist(); } /* fill database list if cache is empty */ if (s.dbcount == 0) { filldatabaselist(&s); /* update data cache */ } else { s.prevdbupdate = s.current; s.datalist = dataptr; adjustsaveinterval(&s); checkdbsaveneed(&s); processdatalist(&s); if (debug) { printf("\n"); } } } if (s.running && intsignal==0) { sleep(cfg.pollinterval); } if (intsignal) { handleintsignals(&s); } } cacheflush(s.dirname); ibwflush(); if (s.rundaemon && !debug) { close(pidfile); unlink(cfg.pidfile); } return 0; } void showhelp(void) { printf(" vnStat daemon %s by Teemu Toivola \n\n", VNSTATVERSION); printf(" -d, --daemon fork process to background\n"); printf(" -n, --nodaemon stay in foreground attached to the terminal\n\n"); printf(" -s, --sync sync interface counters on first update\n"); printf(" -D, --debug show additional debug and disable daemon\n"); printf(" -?, --help show this help\n"); printf(" -v, --version show version\n"); printf(" -p, --pidfile select used pid file\n"); printf(" -u, --user set daemon process user\n"); printf(" -g, --group set daemon process group\n"); printf(" --config select used config file\n"); printf(" --noadd don't add found interfaces if no dbs are found\n\n"); printf("See also \"man vnstatd\".\n"); } vnstat-1.14/src/dbcache.h0000644000000000000000000000106012355210223013755 0ustar rootroot#ifndef DBCACHE_H #define DBCACHE_H typedef struct datanode { DATA data; short filled; short sync; struct datanode *next; } datanode; int cacheadd(const char *iface, int sync); datanode *cacheremove(const char *iface); int cacheupdate(void); void cacheshow(void); void cachestatus(void); int cacheget(datanode *dn); void cacheflush(const char *dirname); int cachecount(void); int cacheactivecount(void); uint32_t dbcheck(uint32_t dbhash, int *forcesave); uint32_t simplehash(const char *data, int len); /* global variables */ datanode *dataptr; #endif vnstat-1.14/src/daemon.c0000644000000000000000000004272512453703512013666 0ustar rootroot#include "common.h" #include "ifinfo.h" #include "dbaccess.h" #include "dbcache.h" #include "misc.h" #include "cfg.h" #include "ibw.h" #include "daemon.h" void daemonize(void) { int i; char str[10]; if (getppid()==1) { return; /* already a daemon */ } i = (int)fork(); if (i<0) { /* fork error */ perror("Error: fork"); exit(EXIT_FAILURE); } if (i>0) { /* parent exits */ exit(EXIT_SUCCESS); } /* child (daemon) continues */ setsid(); /* obtain a new process group */ if (cfg.uselogging) { snprintf(errorstring, 512, "vnStat daemon %s started. (uid:%d gid:%d)", VNSTATVERSION, (int)getuid(), (int)getgid()); if (!printe(PT_Info)) { printf("Error: Unable to use logfile. Exiting.\n"); exit(EXIT_FAILURE); } } /* lock / pid file */ pidfile = open(cfg.pidfile, O_RDWR|O_CREAT, 0644); if (pidfile<0) { perror("Error: pidfile"); snprintf(errorstring, 512, "opening pidfile \"%s\" failed (%s), exiting.", cfg.pidfile, strerror(errno)); printe(PT_Error); exit(EXIT_FAILURE); /* can't open */ } if (lockf(pidfile,F_TLOCK,0)<0) { perror("Error: pidfile lock"); snprintf(errorstring, 512, "pidfile \"%s\" lock failed (%s), exiting.", cfg.pidfile, strerror(errno)); printe(PT_Error); exit(EXIT_FAILURE); /* can't lock */ } /* close all descriptors except lock file */ for (i=getdtablesize();i>=0;--i) { if (i!=pidfile) { close(i); } } /* redirect standard i/o to null */ i=open("/dev/null",O_RDWR); /* stdin */ /* stdout */ if (dup(i) < 0) { perror("Error: dup(stdout)"); snprintf(errorstring, 512, "dup(stdout) failed, exiting."); printe(PT_Error); exit(EXIT_FAILURE); } /* stderr */ if (dup(i) < 0) { perror("Error: dup(stderr)"); snprintf(errorstring, 512, "dup(stderr) failed, exiting."); printe(PT_Error); exit(EXIT_FAILURE); } umask(027); /* set newly created file permissions */ /* change running directory */ if (chdir("/") < 0) { perror("Error: chdir(/)"); snprintf(errorstring, 512, "directory change to / failed, exiting."); printe(PT_Error); exit(EXIT_FAILURE); } /* first instance continues */ snprintf(str, 10, "%d\n", (int)getpid()); /* record pid to pidfile */ if (write(pidfile,str,strlen(str)) < 0) { perror("Error: write(pidfile)"); snprintf(errorstring, 512, "writing to pidfile %s failed, exiting.", cfg.pidfile); printe(PT_Error); exit(EXIT_FAILURE); } signal(SIGCHLD,SIG_IGN); /* ignore child */ signal(SIGTSTP,SIG_IGN); /* ignore tty signals */ signal(SIGTTOU,SIG_IGN); signal(SIGTTIN,SIG_IGN); if (cfg.uselogging==1) { snprintf(errorstring, 512, "Daemon running with pid %d.", (int)getpid()); printe(PT_Info); } } int addinterfaces(const char *dirname) { char *ifacelist, interface[32]; int index = 0, count = 0, bwlimit = 0; /* get list of currently visible interfaces */ if (getiflist(&ifacelist, 0)==0) { free(ifacelist); return 0; } if (strlen(ifacelist)<2) { free(ifacelist); return 0; } if (debug) printf("Interface list: \"%s\"\n", ifacelist); while (sscanf(ifacelist+index, "%31s", interface)!=EOF) { if (debug) printf("Processing: \"%s\"\n", interface); index += strlen(interface)+1; /* skip local interfaces */ if ((strcmp(interface,"lo")==0) || (strcmp(interface,"lo0")==0) || (strcmp(interface,"sit0")==0)) { if (debug) printf("skip\n"); continue; } /* create database for interface */ initdb(); strncpy_nt(data.interface, interface, 32); strncpy_nt(data.nick, data.interface, 32); if (!getifinfo(interface)) { if (debug) printf("getifinfo failed, skip\n"); continue; } parseifinfo(1); if (!writedb(interface, dirname, 1)) { continue; } count++; bwlimit = ibwget(interface); if (bwlimit > 0) { printf("\"%s\" added with %d Mbit bandwidth limit.\n", interface, bwlimit); } else { printf("\"%s\" added. Warning: no bandwidth limit has been set.\n", interface); } } if (count==1) { printf("-> %d interface added.", count); } else { printf("-> %d interfaces added.", count); } if (count) { printf("\nLimits can be modified using the configuration file. See \"man vnstat.conf\".\n"); printf("Unwanted interfaces can be removed from monitoring with \"vnstat --delete\"."); } printf("\n"); free(ifacelist); return count; } void debugtimestamp(void) { time_t now; char timestamp[22]; now = time(NULL); strftime(timestamp, 22, "%Y-%m-%d %H:%M:%S", localtime(&now)); printf("%s\n", timestamp); } uid_t getuser(const char *user) { struct passwd *pw; uid_t uid; if (!strlen(user)) { return getuid(); } if (isnumeric(user)) { uid = atoi(user); pw = getpwuid(uid); } else { pw = getpwnam(user); } if (pw == NULL) { printf("Error: No such user: \"%s\".\n", user); exit(EXIT_FAILURE); } uid = pw->pw_uid; if (debug) printf("getuser(%s / %d): %s (%d)\n", user, atoi(user), pw->pw_name, (int)uid); return uid; } gid_t getgroup(const char *group) { struct group *gr; gid_t gid; if (!strlen(group)) { return getgid(); } if (isnumeric(group)) { gid = atoi(group); gr = getgrgid(gid); } else { gr = getgrnam(group); } if (gr == NULL) { printf("Error: No such group: \"%s\".\n", group); exit(EXIT_FAILURE); } gid = gr->gr_gid; if (debug) printf("getgroup(%s / %d): %s (%d)\n", group, atoi(group), gr->gr_name, (int)gid); return gid; } void setuser(const char *user) { uid_t uid; if (!strlen(user)) { return; } if (getuid() != 0 && geteuid() != 0) { printf("Error: User can only be set as root.\n"); exit(EXIT_FAILURE); } if (isnumeric(user) && atoi(user) == 0) { return; } uid = getuser(user); if (debug) printf("switching to user id %d.\n", uid); if (setuid(uid) != 0) { perror("Error: setuid"); exit(EXIT_FAILURE); } } void setgroup(const char *group) { gid_t gid; if (!strlen(group)) { return; } if (getuid() != 0 && geteuid() != 0) { printf("Error: Group can only be set as root.\n"); exit(EXIT_FAILURE); } if (isnumeric(group) && atoi(group) == 0) { return; } gid = getgroup(group); if (debug) printf("switching to group id %d.\n", gid); if (setgid(gid) != 0) { perror("Error: setgid"); exit(EXIT_FAILURE); } } int direxists(const char *dir) { struct stat statbuf; if (stat(dir, &statbuf)!=0) { if (errno==ENOENT) { return 0; } if (debug) printf("Error: stat() \"%s\": %s\n", dir, strerror(errno)); } return 1; } int mkpath(const char *dir, const mode_t mode) { int i = 0, len = 0, ret = 1; char *tmp = NULL; if (!strlen(dir)) { if (debug) printf("Error: mkpath(), no directory given\n"); return 0; } if (direxists(dir)) { if (debug) printf("already exists: %s\n", dir); return 1; } if (!cfg.createdirs) { return 0; } tmp = strdup(dir); if (tmp == NULL) { return 0; } len = strlen(tmp); if (tmp[len-1] == '/') { tmp[len-1] = '\0'; } if (tmp[0] == '/') { i++; } for (; irundaemon = 0; /* daemon disabled by default */ s->running = 1; s->dbsaved = 1; s->showhelp = 1; s->sync = 0; s->forcesave = 0; s->noadd = 0; s->dbhash = 0; s->cfgfile[0] = '\0'; s->dirname[0] = '\0'; s->user[0] = '\0'; s->group[0] = '\0'; s->prevdbupdate = 0; s->prevdbsave = 0; s->dbcount = 0; s->dodbsave = 0; s->datalist = NULL; } void preparedatabases(DSTATE *s) { DIR *dir; struct dirent *di; /* check that directory is ok */ if ((dir=opendir(s->dirname))==NULL) { printf("Error: Unable to open database directory \"%s\": %s\n", s->dirname, strerror(errno)); printf("Make sure it exists and is at least read enabled for current user.\n"); printf("Exiting...\n"); exit(EXIT_FAILURE); } /* check if there's something to work with */ s->dbcount = 0; while ((di=readdir(dir))) { if ((di->d_name[0]!='.') && (strcmp(di->d_name, DATABASEFILE)!=0)) { s->dbcount++; } } closedir(dir); if (s->dbcount > 0) { s->dbcount = 0; return; } if (s->noadd) { printf("Zero database found, exiting.\n"); exit(EXIT_FAILURE); } if (!spacecheck(s->dirname)) { printf("Error: Not enough free diskspace available, exiting.\n"); exit(EXIT_FAILURE); } printf("Zero database found, adding available interfaces...\n"); if (!addinterfaces(s->dirname)) { printf("Nothing to do, exiting.\n"); exit(EXIT_FAILURE); } /* set counter back to zero so that dbs will be cached later */ s->dbcount = 0; } void setsignaltraps(void) { intsignal = 0; if (signal(SIGINT, sighandler) == SIG_ERR) { perror("Error: signal SIGINT"); exit(EXIT_FAILURE); } if (signal(SIGHUP, sighandler) == SIG_ERR) { perror("Error: signal SIGHUP"); exit(EXIT_FAILURE); } if (signal(SIGTERM, sighandler) == SIG_ERR) { perror("Error: signal SIGTERM"); exit(EXIT_FAILURE); } } void filldatabaselist(DSTATE *s) { DIR *dir; struct dirent *di; if ((dir=opendir(s->dirname))==NULL) { snprintf(errorstring, 512, "Unable to access database directory \"%s\" (%s), exiting.", s->dirname, strerror(errno)); printe(PT_Error); /* clean daemon stuff before exit */ if (s->rundaemon && !debug) { close(pidfile); unlink(cfg.pidfile); } ibwflush(); exit(EXIT_FAILURE); } while ((di=readdir(dir))) { if ((di->d_name[0]=='.') || (strcmp(di->d_name, DATABASEFILE)==0)) { continue; } if (debug) { printf("\nProcessing file \"%s/%s\"...\n", s->dirname, di->d_name); } if (!cacheadd(di->d_name, s->sync)) { snprintf(errorstring, 512, "Cache memory allocation failed, exiting."); printe(PT_Error); /* clean daemon stuff before exit */ if (s->rundaemon && !debug) { close(pidfile); unlink(cfg.pidfile); } ibwflush(); exit(EXIT_FAILURE); } s->dbcount++; } closedir(dir); s->sync = 0; /* disable update interval check for one loop if database list was refreshed */ /* otherwise increase default update interval since there's nothing else to do */ if (s->dbcount) { s->updateinterval = 0; intsignal = 42; s->prevdbsave = s->current; /* list monitored interfaces to log */ cachestatus(); } else { s->updateinterval = 120; } } void adjustsaveinterval(DSTATE *s) { /* modify active save interval if all interfaces are unavailable */ if (cacheactivecount() > 0) { s->saveinterval = cfg.saveinterval * 60; } else { s->saveinterval = cfg.offsaveinterval * 60; } } void checkdbsaveneed(DSTATE *s) { if ((s->current - s->prevdbsave) >= (s->saveinterval) || s->forcesave) { s->dodbsave = 1; s->forcesave = 0; s->prevdbsave = s->current; } else { s->dodbsave = 0; } } void processdatalist(DSTATE *s) { while (s->datalist!=NULL) { if (debug) { printf("d: processing %s (%d)...\n", s->datalist->data.interface, s->dodbsave); } /* get data from cache if available */ if (!datalist_cacheget(s)) { s->datalist = s->datalist->next; continue; } /* get info if interface has been marked as active */ datalist_getifinfo(s); /* check that the time is correct */ if (!datalist_timevalidation(s)) { s->datalist = s->datalist->next; continue; } /* write data to file if now is the time for it */ if (!datalist_writedb(s)) { /* remove interface from update list since the database file doesn't exist anymore */ snprintf(errorstring, 512, "Database for interface \"%s\" no longer exists, removing from update list.", s->datalist->data.interface); printe(PT_Info); s->datalist = cacheremove(s->datalist->data.interface); s->dbcount--; cachestatus(); continue; } s->datalist = s->datalist->next; } } int datalist_cacheget(DSTATE *s) { if (cacheget(s->datalist)==0) { /* try to read data from file if not cached */ if (readdb(s->datalist->data.interface, s->dirname)==0) { /* mark cache as filled on read success and force interface status update */ s->datalist->filled = 1; s->dbhash = 0; } else { return 0; } } return 1; } void datalist_getifinfo(DSTATE *s) { if (!data.active) { if (debug) printf("d: interface is disabled\n"); return; } if (!getifinfo(data.interface)) { /* disable interface since we can't access its data */ data.active = 0; snprintf(errorstring, 512, "Interface \"%s\" not available, disabling.", data.interface); printe(PT_Info); return; } if (s->datalist->sync) { /* if --sync was used during startup */ data.currx = ifinfo.rx; data.curtx = ifinfo.tx; s->datalist->sync = 0; } else { parseifinfo(0); } } int datalist_timevalidation(DSTATE *s) { if (s->current >= data.lastupdated) { data.lastupdated = s->current; cacheupdate(); return 1; } /* skip update if previous update is less than a day in the future */ /* otherwise exit with error message since the clock is problably messed */ if (data.lastupdated > (s->current+86400)) { snprintf(errorstring, 512, "Interface \"%s\" has previous update date too much in the future, exiting. (%u / %u)", data.interface, (unsigned int)data.lastupdated, (unsigned int)s->current); printe(PT_Error); /* clean daemon stuff before exit */ if (s->rundaemon && !debug) { close(pidfile); unlink(cfg.pidfile); } ibwflush(); exit(EXIT_FAILURE); } return 0; } int datalist_writedb(DSTATE *s) { if (!s->dodbsave) { return 1; } if (!checkdb(s->datalist->data.interface, s->dirname)) { return 0; } if (spacecheck(s->dirname)) { if (writedb(s->datalist->data.interface, s->dirname, 0)) { if (!s->dbsaved) { snprintf(errorstring, 512, "Database write possible again."); printe(PT_Info); s->dbsaved = 1; } } else { if (s->dbsaved) { snprintf(errorstring, 512, "Unable to write database, continuing with cached data."); printe(PT_Error); s->dbsaved = 0; } } } else { /* show freespace error only once */ if (s->dbsaved) { snprintf(errorstring, 512, "Free diskspace check failed, unable to write database, continuing with cached data."); printe(PT_Error); s->dbsaved = 0; } } return 1; } void handleintsignals(DSTATE *s) { switch (intsignal) { case SIGHUP: snprintf(errorstring, 512, "SIGHUP received, flushing data to disk and reloading config."); printe(PT_Info); cacheflush(s->dirname); s->dbcount = 0; ibwflush(); if (loadcfg(s->cfgfile)) { strncpy_nt(s->dirname, cfg.dbdir, 512); } ibwloadcfg(s->cfgfile); break; case SIGINT: snprintf(errorstring, 512, "SIGINT received, exiting."); printe(PT_Info); s->running = 0; break; case SIGTERM: snprintf(errorstring, 512, "SIGTERM received, exiting."); printe(PT_Info); s->running = 0; break; case 42: break; case 0: break; default: snprintf(errorstring, 512, "Unkown signal %d received, ignoring.", intsignal); printe(PT_Info); break; } intsignal = 0; } void preparedirs(DSTATE *s) { /* database directory */ if (mkpath(s->dirname, 0775)) { updatedirowner(s->dirname, s->user, s->group); } if (!cfg.createdirs || !s->rundaemon) { return; } /* possible pid/lock and log directory */ preparevnstatdir(cfg.pidfile, s->user, s->group); if (cfg.uselogging == 1) { preparevnstatdir(cfg.logfile, s->user, s->group); } } void preparevnstatdir(const char *file, const char *user, const char *group) { int len, i, lastslash=0; char *path, *base; if (file == NULL) { return; } len = strlen(file); if (len<2) { return; } if (file[len-1] == '/') { return; } path = strdup(file); if (path == NULL) { return; } /* verify that path ends with vnstat or vnstatd */ base = basename(dirname(path)); if (strcmp(base, "vnstat")!=0 && strcmp(base, "vnstatd")!=0) { free(path); return; } free(path); path = strdup(file); if (path == NULL) { return; } /* extract path */ for (i=0; id_type != DT_REG) { continue; } snprintf(entryname, 512, "%s/%s", dir, di->d_name); if (stat(entryname, &statbuf)!=0) { continue; } if (statbuf.st_uid != uid || statbuf.st_gid != gid) { if (chown(entryname, uid, gid) != 0) { if (debug) printf("Error: chown() \"%s\": %s\n", entryname, strerror(errno)); } else { if (debug) printf("\"%s\" chown completed\n", entryname); } } } closedir(d); } vnstat-1.14/src/misc.h0000644000000000000000000000112012506563705013352 0ustar rootroot#ifndef MISC_H #define MISC_H #define UNITCOUNT 4 int kerneltest(void); int spacecheck(char *path); void sighandler(int); int getbtime(void); char *getvalue(uint64_t mb, uint64_t kb, int len, int type); char *getrate(uint64_t mb, uint64_t kb, uint32_t interval, int len); char *gettrafficrate(uint64_t bytes, uint32_t interval, int len); uint64_t getscale(uint64_t kb); char *getunit(int index); char *getrateunit(int unit, int index); uint32_t getunitdivider(int unit, int index); char *getratestring(float rate, int len, int declen, int unit); int getpadding(int len, char *str); #endif vnstat-1.14/src/dbmerge.c0000644000000000000000000001054112355210223014010 0ustar rootroot#include "common.h" #include "dbaccess.h" #include "dbmerge.h" int mergedb(char iface[], char dirname[]) { DATA mergedata; char *ifaceptr; if (!strstr(iface, "+")) { return 0; } /* create empty database */ emptydb(&mergedata); strncpy_nt(mergedata.interface, iface, 32); strncpy_nt(mergedata.nick, mergedata.interface, 32); if (debug) printf("iface merge: %s\n", iface); ifaceptr = strtok(iface, "+"); /* merge all databases in given string */ while (ifaceptr != NULL) { if (debug) printf("merging %s:\n", ifaceptr); if (readdb(ifaceptr, dirname)!=0) { printf("Merge \"%s\" failed.\n", mergedata.interface); return 0; } if (!mergewith(&mergedata)) { printf("Merge \"%s\" failed for interface \"%s\".\n", mergedata.interface, ifaceptr); return 0; } ifaceptr = strtok(NULL, "+"); } /* clean possible glitches */ cleanmerged(&mergedata); /* replace active data with merged */ if (memcpy(&data, &mergedata, sizeof(data)) != NULL) { return 1; } else { return 0; } } void emptydb(DATA *dat) { int i; struct tm *d; time_t current; current = time(NULL); dat->version = DBVERSION; dat->active = 1; dat->totalrx = 0; dat->totaltx = 0; dat->currx = 0; dat->curtx = 0; dat->totalrxk = 0; dat->totaltxk = 0; dat->lastupdated = 0; dat->created = current; /* days */ d = localtime(¤t); for (i=0;i<=29;i++) { dat->day[i].rx = 0; dat->day[i].tx = 0; dat->day[i].rxk = 0; dat->day[i].txk = 0; dat->day[i].date = mktime(d); dat->day[i].used = 1; d->tm_mday--; } /* months */ d = localtime(¤t); for (i=0;i<=11;i++) { dat->month[i].rx = 0; dat->month[i].tx = 0; dat->month[i].rxk = 0; dat->month[i].txk = 0; dat->month[i].month = mktime(d); dat->month[i].used = 1; d->tm_mon--; } /* top10 */ for (i=0;i<=9;i++) { dat->top10[i].rx = 0; dat->top10[i].tx = 0; dat->top10[i].rxk = 0; dat->top10[i].txk = 0; dat->top10[i].date = 0; dat->top10[i].used = 0; } /* hours */ for (i=0;i<=23;i++) { dat->hour[i].rx = 0; dat->hour[i].tx = 0; dat->hour[i].date = 0; } dat->btime = 0; } int mergewith(DATA *dat) { int i, j, orig, merged; struct tm *d; /* merge totals */ dat->totalrx += data.totalrx; dat->totaltx += data.totaltx; dat->totalrxk += data.totalrxk; dat->totaltxk += data.totaltxk; if (data.created < dat->created) { dat->created = data.created; } if (data.lastupdated > dat->lastupdated) { dat->lastupdated = data.lastupdated; } /* clean hours from loaded db */ cleanhours(); /* merge hours */ for (i=0;i<=23;i++) { if (data.hour[i].date!=0) { dat->hour[i].rx += data.hour[i].rx; dat->hour[i].tx += data.hour[i].tx; dat->hour[i].date = data.hour[i].date; } } /* merge days */ for (i=0;i<=29;i++) { if (data.day[i].used) { d = localtime(&data.day[i].date); orig = d->tm_year * 1000 + d->tm_yday; for (j=0;j<=29;j++) { d = localtime(&dat->day[j].date); merged = d->tm_year * 1000 + d->tm_yday; if (orig == merged) { dat->day[j].rx += data.day[i].rx; dat->day[j].tx += data.day[i].tx; dat->day[j].rxk += data.day[i].rxk; dat->day[j].txk += data.day[i].txk; if (dat->day[j].date > data.day[i].date) { dat->day[j].date = data.day[i].date; } } else if (merged < orig) { break; } } } } /* merge months */ for (i=0;i<=11;i++) { if (data.month[i].used) { d = localtime(&data.month[i].month); orig = d->tm_year * 100 + d->tm_mon; for (j=0;j<=11;j++) { d = localtime(&dat->month[j].month); merged = d->tm_year * 100 + d->tm_mon; if (orig == merged) { dat->month[j].rx += data.month[i].rx; dat->month[j].tx += data.month[i].tx; dat->month[j].rxk += data.month[i].rxk; dat->month[j].txk += data.month[i].txk; if (dat->month[j].month > data.month[i].month) { dat->month[j].month = data.month[i].month; } } else if (merged < orig) { break; } } } } return 1; } void cleanmerged(DATA *dat) { int i; /* clean days */ for (i=29;i>=0;i--) { if ((dat->day[i].rx == 0) && (dat->day[i].tx == 0) && (dat->day[i].rxk == 0) && (dat->day[i].txk == 0)) { dat->day[i].used = 0; } else { break; } } /* clean days */ for (i=11;i>=0;i--) { if ((dat->month[i].rx == 0) && (dat->month[i].tx == 0) && (dat->month[i].rxk == 0) && (dat->month[i].txk == 0)) { dat->month[i].used = 0; } else { break; } } } vnstat-1.14/src/image.c0000644000000000000000000013250312510042373013472 0ustar rootroot#include "common.h" #include "vnstati.h" #include "misc.h" #include "image.h" void initimagecontent(IMAGECONTENT *ic) { ic->showheader = 1; ic->showedge = 1; ic->showlegend = 1; ic->altdate = 0; ic->headertext[0] = '\0'; } void drawimage(IMAGECONTENT *ic) { switch (cfg.qmode) { case 1: drawdaily(ic); break; case 2: drawmonthly(ic); break; case 3: drawtop(ic); break; case 5: if (cfg.slayout) { drawsummary(ic, 0, 0); } else { drawoldsummary(ic, 0, 0); } break; case 51: if (cfg.slayout) { drawsummary(ic, 1, cfg.hourlyrate); } else { drawoldsummary(ic, 1, cfg.hourlyrate); } break; case 52: if (cfg.slayout) { drawsummary(ic, 2, cfg.hourlyrate); } else { drawoldsummary(ic, 2, cfg.hourlyrate); } break; case 7: drawhourly(ic, cfg.hourlyrate); break; default: break; } /* enable background transparency if needed */ if (cfg.transbg) { gdImageColorTransparent(ic->im, ic->cbackground); } } void colorinit(IMAGECONTENT *ic) { int rgb[3]; /* text, edge and header colors */ hextorgb(cfg.ctext, rgb); ic->ctext = gdImageColorAllocate(ic->im, rgb[0], rgb[1], rgb[2]); colorinitcheck("ctext", ic->ctext, cfg.ctext, rgb); hextorgb(cfg.cedge, rgb); ic->cedge = gdImageColorAllocate(ic->im, rgb[0], rgb[1], rgb[2]); colorinitcheck("cedge", ic->cedge, cfg.cedge, rgb); hextorgb(cfg.cheader, rgb); ic->cheader = gdImageColorAllocate(ic->im, rgb[0], rgb[1], rgb[2]); colorinitcheck("cheader", ic->cheader, cfg.cheader, rgb); hextorgb(cfg.cheadertitle, rgb); ic->cheadertitle = gdImageColorAllocate(ic->im, rgb[0], rgb[1], rgb[2]); colorinitcheck("cheadertitle", ic->cheadertitle, cfg.cheadertitle, rgb); hextorgb(cfg.cheaderdate, rgb); ic->cheaderdate = gdImageColorAllocate(ic->im, rgb[0], rgb[1], rgb[2]); colorinitcheck("cheaderdate", ic->cheaderdate, cfg.cheaderdate, rgb); /* lines */ hextorgb(cfg.cline, rgb); ic->cline = gdImageColorAllocate(ic->im, rgb[0], rgb[1], rgb[2]); colorinitcheck("cline", ic->cline, cfg.cline, rgb); if (cfg.clinel[0] == '-') { modcolor(rgb, 50, 1); } else { hextorgb(cfg.clinel, rgb); } ic->clinel = gdImageColorAllocate(ic->im, rgb[0], rgb[1], rgb[2]); colorinitcheck("clinel", ic->clinel, cfg.clinel, rgb); /* background */ hextorgb(cfg.cbg, rgb); ic->cbackground = gdImageColorAllocate(ic->im, rgb[0], rgb[1], rgb[2]); colorinitcheck("cbackground", ic->cbackground, cfg.cbg, rgb); modcolor(rgb, -35, 0); ic->cvnstat = gdImageColorAllocate(ic->im, rgb[0], rgb[1], rgb[2]); colorinitcheck("cvnstat", ic->cvnstat, cfg.cbg, rgb); hextorgb(cfg.cbg, rgb); modcolor(rgb, -15, 0); ic->cbgoffset = gdImageColorAllocate(ic->im, rgb[0], rgb[1], rgb[2]); colorinitcheck("cbgoffset", ic->cbgoffset, cfg.cbg, rgb); /* rx */ hextorgb(cfg.crx, rgb); ic->crx = gdImageColorAllocate(ic->im, rgb[0], rgb[1], rgb[2]); colorinitcheck("crx", ic->crx, cfg.crx, rgb); if (cfg.crxd[0] == '-') { modcolor(rgb, -50, 1); } else { hextorgb(cfg.crxd, rgb); } ic->crxd = gdImageColorAllocate(ic->im, rgb[0], rgb[1], rgb[2]); colorinitcheck("crxd", ic->crxd, cfg.crxd, rgb); /* tx */ hextorgb(cfg.ctx, rgb); ic->ctx = gdImageColorAllocate(ic->im, rgb[0], rgb[1], rgb[2]); colorinitcheck("ctx", ic->ctx, cfg.ctx, rgb); if (cfg.ctxd[0] == '-') { modcolor(rgb, -50, 1); } else { hextorgb(cfg.ctxd, rgb); } ic->ctxd = gdImageColorAllocate(ic->im, rgb[0], rgb[1], rgb[2]); colorinitcheck("ctxd", ic->ctxd, cfg.ctxd, rgb); } void colorinitcheck(char *color, int value, char *cfgtext, int *rgb) { if (value==-1) { printf("Error: ImageColorAllocate failed.\n"); printf(" C: \"%s\" T: \"%s\" RGB: %d/%d/%d\n", color, cfgtext, rgb[0], rgb[1], rgb[2]); exit(EXIT_FAILURE); } } void layoutinit(IMAGECONTENT *ic, char *title, int width, int height) { struct tm *d; char datestring[64]; /* get time in given format */ d = localtime(&data.lastupdated); strftime(datestring, 64, cfg.hformat, d); /* background, edges */ gdImageFill(ic->im, 0, 0, ic->cbackground); if (ic->showedge) { gdImageRectangle(ic->im, 0, 0, width-1, height-1, ic->cedge); } /* titlebox with title */ if (ic->showheader) { gdImageFilledRectangle(ic->im, 2+ic->showedge, 2+ic->showedge, width-3-ic->showedge, 24, ic->cheader); gdImageString(ic->im, gdFontGetGiant(), 12, 5+ic->showedge, (unsigned char*)title, ic->cheadertitle); } /* date */ if (!ic->showheader || ic->altdate) { gdImageString(ic->im, gdFontGetTiny(), 5+ic->showedge, height-12-ic->showedge, (unsigned char*)datestring, ic->cvnstat); } else { gdImageString(ic->im, gdFontGetTiny(), width-(strlen(datestring)*gdFontGetTiny()->w+12), 9+ic->showedge, (unsigned char*)datestring, ic->cheaderdate); } /* generator */ gdImageString(ic->im, gdFontGetTiny(), width-114-ic->showedge, height-12-ic->showedge, (unsigned char*)"vnStat / Teemu Toivola", ic->cvnstat); } void drawlegend(IMAGECONTENT *ic, int x, int y) { if (!ic->showlegend) { return; } /* color legend */ gdImageString(ic->im, gdFontGetSmall(), x, y, (unsigned char*)"rx tx", ic->ctext); gdImageFilledRectangle(ic->im, x-12, y+4, x-6, y+10, ic->crx); gdImageRectangle(ic->im, x-12, y+4, x-6, y+10, ic->ctext); gdImageFilledRectangle(ic->im, x+30, y+4, x+36, y+10, ic->ctx); gdImageRectangle(ic->im, x+30, y+4, x+36, y+10, ic->ctext); } void drawbar(IMAGECONTENT *ic, int x, int y, int len, uint64_t rx, int rxk, uint64_t tx, int txk, uint64_t max) { int l; rx=mbkbtokb(rx, rxk); tx=mbkbtokb(tx, txk); if ((rx+tx)!=max) { len=((rx+tx)/(float)max)*len; } if (len!=0) { if (tx>rx) { l=rintf((rx/(float)(rx+tx)*len)); gdImageFilledRectangle(ic->im, x, y+YBEGINOFFSET, x+l, y+YENDOFFSET, ic->crx); gdImageRectangle(ic->im, x, y+YBEGINOFFSET, x+l, y+YENDOFFSET, ic->crxd); gdImageFilledRectangle(ic->im, x+l, y+YBEGINOFFSET, x+len, y+YENDOFFSET, ic->ctx); gdImageRectangle(ic->im, x+l, y+YBEGINOFFSET, x+len, y+YENDOFFSET, ic->ctxd); } else { l=rintf((tx/(float)(rx+tx)*len)); gdImageFilledRectangle(ic->im, x, y+YBEGINOFFSET, x+(len-l), y+YENDOFFSET, ic->crx); gdImageRectangle(ic->im, x, y+YBEGINOFFSET, x+(len-l), y+YENDOFFSET, ic->crxd); gdImageFilledRectangle(ic->im, x+(len-l), y+YBEGINOFFSET, x+len, y+YENDOFFSET, ic->ctx); gdImageRectangle(ic->im, x+(len-l), y+YBEGINOFFSET, x+len, y+YENDOFFSET, ic->ctxd); } } } void drawpole(IMAGECONTENT *ic, int x, int y, int len, uint64_t rx, uint64_t tx, uint64_t max) { int l; l = (rx/(float)max)*len; gdImageFilledRectangle(ic->im, x, y+(len-l), x+7, y+len, ic->crx); l = (tx/(float)max)*len; gdImageFilledRectangle(ic->im, x+5, y+(len-l), x+12, y+len, ic->ctx); } void drawdonut(IMAGECONTENT *ic, int x, int y, float rxp, float txp) { int rxarc = 0, txarc = 0; if ( (int)(rxp + txp) > 0 ) { rxarc = 360 * (rxp / (float)100); if ( (int)(rxp + txp) == 100 ) { txarc = 360 - rxarc; } else { txarc = 360 * (txp / (float)100); } /* fix possible graphical glitch */ if (!rxarc) { rxarc = 1; } if (!txarc) { txarc = 1; } } gdImageFilledArc(ic->im, x, y, DOUTRAD, DOUTRAD, 0, 360, ic->cbgoffset, 0); if ( (int)(rxp + txp) > 0 ) { gdImageFilledArc(ic->im, x, y, DOUTRAD, DOUTRAD, 270, 270+txarc, ic->ctx, 0); gdImageFilledArc(ic->im, x, y, DOUTRAD, DOUTRAD, 270, 270+txarc, ic->ctxd, gdEdged|gdNoFill); gdImageFilledArc(ic->im, x, y, DOUTRAD, DOUTRAD, 270+txarc, 270+txarc+rxarc, ic->crx, 0); gdImageFilledArc(ic->im, x, y, DOUTRAD, DOUTRAD, 270+txarc, 270+txarc+rxarc, ic->crxd, gdEdged|gdNoFill); gdImageFilledArc(ic->im, x, y, DINRAD, DINRAD, 270, 270+txarc, ic->ctxd, gdEdged|gdNoFill); gdImageFilledArc(ic->im, x, y, DINRAD, DINRAD, 270+txarc, 270+txarc+rxarc, ic->crxd, gdEdged|gdNoFill); } gdImageFilledArc(ic->im, x, y, DINRAD-2, DINRAD-2, 0, 360, ic->cbackground, 0); } void drawhours(IMAGECONTENT *ic, int x, int y, int rate) { int i, tmax=0, s=0, step, prev=0, diff=0, chour; float ratediv; uint64_t max=1, scaleunit=0; char buffer[32]; struct tm *d; ic->current = time(NULL); chour = localtime(&ic->current)->tm_hour; if (cfg.rateunit) { ratediv = 450; /* x * 8 / 3600 */ } else { ratediv = 3600; } /* tmax (time max) = current hour */ /* max = transfer max */ for (i = 0; i < 24; i++) { /* convert hourly transfer to hourly rate if needed */ if (rate) { if ((ic->current-data.hour[i].date) > 3600) { data.hour[i].rx = data.hour[i].rx / ratediv; data.hour[i].tx = data.hour[i].tx / ratediv; } else { /* scale ongoing hour properly */ d = localtime(&data.hour[i].date); if (chour != d->tm_hour) { data.hour[i].rx = data.hour[i].rx / ratediv; data.hour[i].tx = data.hour[i].tx / ratediv; } else { diff = d->tm_min*60; if (!diff) { diff = 1; } if (cfg.rateunit==1) { data.hour[i].rx = data.hour[i].rx * 8 / (float)diff; data.hour[i].tx = data.hour[i].tx * 8 / (float)diff; } else { data.hour[i].rx = data.hour[i].rx / (float)diff; data.hour[i].tx = data.hour[i].tx / (float)diff; } } } } if (data.hour[i].date>=data.hour[tmax].date) { tmax=i; } if (data.hour[i].rx>=max) { max=data.hour[i].rx; } if (data.hour[i].tx>=max) { max=data.hour[i].tx; } } /* scale values */ scaleunit = getscale(max); if (max/scaleunit > 4) { step = 2; } else { step = 1; } for (i=step; (uint64_t)(scaleunit*i) <= max; i=i+step) { s = 121 * ((scaleunit * i) / (float)max); gdImageLine(ic->im, x+36, y+124-s, x+460, y+124-s, ic->cline); gdImageLine(ic->im, x+36, y+124-((s+prev)/2), x+460, y+124-((s+prev)/2), ic->clinel); gdImageString(ic->im, gdFontGetTiny(), x+16, y+121-s, (unsigned char*)getimagevalue(scaleunit*i, 3, rate), ic->ctext); prev = s; } s = 121 * ((scaleunit * i) / (float)max); if ( ((s+prev)/2) <= 128 ) { gdImageLine(ic->im, x+36, y+124-((s+prev)/2), x+460, y+124-((s+prev)/2), ic->clinel); } /* scale text */ if (rate) { gdImageStringUp(ic->im, gdFontGetTiny(), x-2, y+70, (unsigned char*)getimagescale(scaleunit, 1), ic->ctext); } else { gdImageStringUp(ic->im, gdFontGetTiny(), x-2, y+60, (unsigned char*)getimagescale(scaleunit, 0), ic->ctext); } /* x-axis values and poles */ for (i = 0; i < 24; i++) { s=tmax-i; if (s<0) { s+=24; } snprintf(buffer, 32, "%02d ", s); gdImageString(ic->im, gdFontGetTiny(), x+440-(i*17), y+128, (unsigned char*)buffer, ic->ctext); drawpole(ic, x+438-(i*17), y, 124, data.hour[s].rx, data.hour[s].tx, max); } /* axis */ gdImageLine(ic->im, x+36-4, y+124, x+466, y+124, ic->ctext); gdImageLine(ic->im, x+36, y-10, x+36, y+124+4, ic->ctext); /* arrows */ gdImageLine(ic->im, x+465, y+124, x+462, y+122, ic->ctext); gdImageLine(ic->im, x+465, y+124, x+462, y+126, ic->ctext); gdImageLine(ic->im, x+462, y+122, x+462, y+126, ic->ctext); gdImageLine(ic->im, x+36, y-9, x+38, y-6, ic->ctext); gdImageLine(ic->im, x+36, y-9, x+34, y-6, ic->ctext); gdImageLine(ic->im, x+34, y-6, x+38, y-6, ic->ctext); } void drawsummary(IMAGECONTENT *ic, int type, int rate) { int textx, texty, offset = 0; int width, height, headermod; float rxp = 50, txp = 50, mod; char buffer[512], datebuff[16], daytemp[32]; time_t yesterday; struct tm *d; switch (type) { case 1: width = 980; height = 200; break; case 2: width = 500; height = 370; break; default: width = 500; height = 200; break; } if (!ic->showheader) { headermod = 26; height -= 22; } else { headermod = 0; } yesterday=ic->current-86400; ic->im = gdImageCreate(width, height); colorinit(ic); if (strlen(ic->headertext)) { strncpy_nt(buffer, ic->headertext, 65); } else { if (strcmp(data.nick, data.interface)==0) { snprintf(buffer, 512, "%s", data.interface); } else { snprintf(buffer, 512, "%s (%s)", data.nick, data.interface); } } layoutinit(ic, buffer, width, height); /* today */ if (data.day[0].rx>1024 || data.day[0].tx>1024) { rxp = (data.day[0].rx/(float)(data.day[0].rx+data.day[0].tx))*100; txp = (float)100 - rxp; } else { if ( (data.day[0].rx*+data.day[0].rxk)+(data.day[0].tx*+data.day[0].txk) == 0 ) { rxp = txp = 0; } else { rxp = ( ((data.day[0].rx*1024)+data.day[0].rxk) / (float)(((data.day[0].rx*1024)+data.day[0].rxk)+((data.day[0].tx*1024)+data.day[0].txk)) )*100; txp = (float)100 - rxp; } } /* do scaling if needed */ if ( (data.day[0].rx+data.day[0].tx) < (data.day[1].rx+data.day[1].tx) ) { if ( (data.day[0].rx+data.day[0].tx)>1024 || (data.day[1].rx+data.day[1].tx)>1024 ) { mod = (data.day[0].rx+data.day[0].tx) / (float)(data.day[1].rx+data.day[1].tx); } else { mod = (((data.day[0].rx*1024)+data.day[0].rxk)+((data.day[0].tx*1024)+data.day[0].txk)) / (float)(((data.day[1].rx*1024)+data.day[1].rxk)+((data.day[1].tx*1024)+data.day[1].txk)); } rxp = rxp * mod; txp = txp * mod; } /* move graph to center if there's only one to draw for this line */ if (!data.day[1].date) { offset = 85; } else { offset = 0; } drawdonut(ic, 150+offset, 75-headermod, rxp, txp); textx = 100+offset; texty = 30-headermod; /* get formated date for today */ d = localtime(&ic->current); strftime(datebuff, 16, cfg.dformat, d); /* get formated date for current day in database */ d = localtime(&data.day[0].date); strftime(daytemp, 16, cfg.dformat, d); /* change daytemp to today if formated days match */ if (strcmp(datebuff, daytemp)==0) { strncpy_nt(daytemp, "today", 32); } snprintf(buffer, 32, "%*s", getpadding(12, daytemp), daytemp); gdImageString(ic->im, gdFontGetLarge(), textx-54, texty-1, (unsigned char*)buffer, ic->ctext); if (cfg.summaryrate) { d = localtime(&data.lastupdated); snprintf(buffer, 16, "%15s", getrate(data.day[0].rx+data.day[0].tx, data.day[0].rxk+data.day[0].txk, d->tm_sec+(d->tm_min*60)+(d->tm_hour*3600), 15)); gdImageString(ic->im, gdFontGetSmall(), textx-74, texty+58, (unsigned char*)buffer, ic->ctext); } else { texty += 7; } snprintf(buffer, 4, "rx "); strncat(buffer, getvalue(data.day[0].rx, data.day[0].rxk, 12, 1), 32); gdImageString(ic->im, gdFontGetSmall(), textx-74, texty+18, (unsigned char*)buffer, ic->ctext); snprintf(buffer, 4, "tx "); strncat(buffer, getvalue(data.day[0].tx, data.day[0].txk, 12, 1), 32); gdImageString(ic->im, gdFontGetSmall(), textx-74, texty+30, (unsigned char*)buffer, ic->ctext); snprintf(buffer, 4, " = "); strncat(buffer, getvalue(data.day[0].rx+data.day[0].tx, data.day[0].rxk+data.day[0].txk, 12, 1), 32); gdImageString(ic->im, gdFontGetSmall(), textx-74, texty+44, (unsigned char*)buffer, ic->ctext); /* yesterday */ if (data.day[1].date) { if (data.day[1].rx>1024 || data.day[1].tx>1024) { rxp = (data.day[1].rx/(float)(data.day[1].rx+data.day[1].tx))*100; txp = (float)100 - rxp; } else { if ( (data.day[1].rx*+data.day[1].rxk)+(data.day[1].tx*+data.day[1].txk) == 0 ) { rxp = txp = 0; } else { rxp = ( ((data.day[1].rx*1024)+data.day[1].rxk) / (float)(((data.day[1].rx*1024)+data.day[1].rxk)+((data.day[1].tx*1024)+data.day[1].txk)) )*100; txp = (float)100 - rxp; } } /* do scaling if needed */ if ( (data.day[1].rx+data.day[1].tx) < (data.day[0].rx+data.day[0].tx) ) { if ( (data.day[1].rx+data.day[1].tx)>1024 || (data.day[0].rx+data.day[0].tx)>1024 ) { mod = (data.day[1].rx+data.day[1].tx) / (float)(data.day[0].rx+data.day[0].tx); } else { mod = (((data.day[1].rx*1024)+data.day[1].rxk)+((data.day[1].tx*1024)+data.day[1].txk)) / (float)(((data.day[0].rx*1024)+data.day[0].rxk)+((data.day[0].tx*1024)+data.day[0].txk)); } rxp = rxp * mod; txp = txp * mod; } drawdonut(ic, 330, 75-headermod, rxp, txp); textx = 280; texty = 30-headermod; /* get formated date for yesterday */ d = localtime(&yesterday); strftime(datebuff, 16, cfg.dformat, d); /* get formated date for previous day in database */ d = localtime(&data.day[1].date); strftime(daytemp, 16, cfg.dformat, d); /* change daytemp to yesterday if formated days match */ if (strcmp(datebuff, daytemp)==0) { strncpy_nt(daytemp, "yesterday", 32); } snprintf(buffer, 32, "%*s", getpadding(12, daytemp), daytemp); gdImageString(ic->im, gdFontGetLarge(), textx-54, texty-1, (unsigned char*)buffer, ic->ctext); if (cfg.summaryrate) { snprintf(buffer, 16, "%15s", getrate(data.day[1].rx+data.day[1].tx, data.day[1].rxk+data.day[1].txk, 86400, 15)); gdImageString(ic->im, gdFontGetSmall(), textx-74, texty+58, (unsigned char*)buffer, ic->ctext); } else { texty += 7; } snprintf(buffer, 4, "rx "); strncat(buffer, getvalue(data.day[1].rx, data.day[1].rxk, 12, 1), 32); gdImageString(ic->im, gdFontGetSmall(), textx-74, texty+18, (unsigned char*)buffer, ic->ctext); snprintf(buffer, 4, "tx "); strncat(buffer, getvalue(data.day[1].tx, data.day[1].txk, 12, 1), 32); gdImageString(ic->im, gdFontGetSmall(), textx-74, texty+30, (unsigned char*)buffer, ic->ctext); snprintf(buffer, 4, " = "); strncat(buffer, getvalue(data.day[1].rx+data.day[1].tx, data.day[1].rxk+data.day[1].txk, 12, 1), 32); gdImageString(ic->im, gdFontGetSmall(), textx-74, texty+44, (unsigned char*)buffer, ic->ctext); } /* current month */ if (data.month[0].rx>1024 || data.month[0].tx>1024) { rxp = (data.month[0].rx/(float)(data.month[0].rx+data.month[0].tx))*100; txp = (float)100 - rxp; } else { if ( (data.month[0].rx+data.month[0].rxk)+(data.month[0].tx+data.month[0].txk) == 0 ) { rxp = txp = 0; } else { rxp = ( ((data.month[0].rx*1024)+data.month[0].rxk) / (float)(((data.month[0].rx*1024)+data.month[0].rxk)+((data.month[0].tx*1024)+data.month[0].txk)) )*100; txp = (float)100 - rxp; } } /* do scaling if needed */ if ( (data.month[0].rx+data.month[0].tx) < (data.month[1].rx+data.month[1].tx) ) { if ( (data.month[0].rx+data.month[0].tx)>1024 || (data.month[1].rx+data.month[1].tx)>1024 ) { mod = (data.month[0].rx+data.month[0].tx) / (float)(data.month[1].rx+data.month[1].tx); } else { mod = (((data.month[0].rx*1024)+data.month[0].rxk)+((data.month[0].tx*1024)+data.month[0].txk)) / (float)(((data.month[1].rx*1024)+data.month[1].rxk)+((data.month[1].tx*1024)+data.month[1].txk)); } rxp = rxp * mod; txp = txp * mod; } /* move graph to center if there's only one to draw for this line */ if (!data.month[1].month) { offset = 85; } else { offset = 0; } drawdonut(ic, 150+offset, 163-headermod, rxp, txp); textx = 100+offset; texty = 118-headermod; d = localtime(&data.month[0].month); strftime(daytemp, 16, cfg.mformat, d); snprintf(buffer, 32, "%*s", getpadding(12, daytemp), daytemp); gdImageString(ic->im, gdFontGetLarge(), textx-54, texty-1, (unsigned char*)buffer, ic->ctext); if (cfg.summaryrate) { snprintf(buffer, 16, "%15s", getrate(data.month[0].rx+data.month[0].tx, data.month[0].rxk+data.month[0].txk, mosecs(), 15)); gdImageString(ic->im, gdFontGetSmall(), textx-74, texty+58, (unsigned char*)buffer, ic->ctext); } else { texty += 7; } snprintf(buffer, 4, "rx "); strncat(buffer, getvalue(data.month[0].rx, data.month[0].rxk, 12, 1), 32); gdImageString(ic->im, gdFontGetSmall(), textx-74, texty+18, (unsigned char*)buffer, ic->ctext); snprintf(buffer, 4, "tx "); strncat(buffer, getvalue(data.month[0].tx, data.month[0].txk, 12, 1), 32); gdImageString(ic->im, gdFontGetSmall(), textx-74, texty+30, (unsigned char*)buffer, ic->ctext); snprintf(buffer, 4, " = "); strncat(buffer, getvalue(data.month[0].rx+data.month[0].tx, data.month[0].rxk+data.month[0].txk, 12, 1), 32); gdImageString(ic->im, gdFontGetSmall(), textx-74, texty+44, (unsigned char*)buffer, ic->ctext); /* previous month */ if (data.month[1].month) { if (data.month[1].rx>1024 || data.month[1].tx>1024) { rxp = (data.month[1].rx/(float)(data.month[1].rx+data.month[1].tx))*100; txp = (float)100 - rxp; } else { if ( (data.month[1].rx+data.month[1].rxk)+(data.month[1].tx+data.month[1].txk) == 0 ) { rxp = txp = 0; } else { rxp = ( ((data.month[1].rx*1024)+data.month[1].rxk) / (float)(((data.month[1].rx*1024)+data.month[1].rxk)+((data.month[1].tx*1024)+data.month[1].txk)) )*100; txp = (float)100 - rxp; } } /* do scaling if needed */ if ( (data.month[1].rx+data.month[1].tx) < (data.month[0].rx+data.month[0].tx) ) { if ( (data.month[1].rx+data.month[1].tx)>1024 || (data.month[0].rx+data.month[0].tx)>1024 ) { mod = (data.month[1].rx+data.month[1].tx) / (float)(data.month[0].rx+data.month[0].tx); } else { mod = (((data.month[1].rx*1024)+data.month[1].rxk)+((data.month[1].tx*1024)+data.month[1].txk)) / (float)(((data.month[0].rx*1024)+data.month[0].rxk)+((data.month[0].tx*1024)+data.month[0].txk)); } rxp = rxp * mod; txp = txp * mod; } drawdonut(ic, 330, 163-headermod, rxp, txp); textx = 280; texty = 118-headermod; d = localtime(&data.month[1].month); strftime(daytemp, 16, cfg.mformat, d); snprintf(buffer, 32, "%*s", getpadding(12, daytemp), daytemp); gdImageString(ic->im, gdFontGetLarge(), textx-54, texty-1, (unsigned char*)buffer, ic->ctext); if (cfg.summaryrate) { snprintf(buffer, 16, "%15s", getrate(data.month[1].rx+data.month[1].tx, data.month[1].rxk+data.month[1].txk, dmonth(d->tm_mon)*86400, 15)); gdImageString(ic->im, gdFontGetSmall(), textx-74, texty+58, (unsigned char*)buffer, ic->ctext); } else { texty += 7; } snprintf(buffer, 4, "rx "); strncat(buffer, getvalue(data.month[1].rx, data.month[1].rxk, 12, 1), 32); gdImageString(ic->im, gdFontGetSmall(), textx-74, texty+18, (unsigned char*)buffer, ic->ctext); snprintf(buffer, 4, "tx "); strncat(buffer, getvalue(data.month[1].tx, data.month[1].txk, 12, 1), 32); gdImageString(ic->im, gdFontGetSmall(), textx-74, texty+30, (unsigned char*)buffer, ic->ctext); snprintf(buffer, 4, " = "); strncat(buffer, getvalue(data.month[1].rx+data.month[1].tx, data.month[1].rxk+data.month[1].txk, 12, 1), 32); gdImageString(ic->im, gdFontGetSmall(), textx-74, texty+44, (unsigned char*)buffer, ic->ctext); } /* all time */ textx = 385; texty = 57-headermod; gdImageString(ic->im, gdFontGetLarge(), textx+12, texty, (unsigned char*)"all time", ic->ctext); snprintf(buffer, 4, "rx "); strncat(buffer, getvalue(data.totalrx, data.totalrxk, 12, 1), 32); gdImageString(ic->im, gdFontGetSmall(), textx, texty+24, (unsigned char*)buffer, ic->ctext); snprintf(buffer, 4, "tx "); strncat(buffer, getvalue(data.totaltx, data.totaltxk, 12, 1), 32); gdImageString(ic->im, gdFontGetSmall(), textx, texty+36, (unsigned char*)buffer, ic->ctext); snprintf(buffer, 4, " = "); strncat(buffer, getvalue(data.totalrx+data.totaltx, data.totalrxk+data.totaltxk, 12, 1), 32); gdImageString(ic->im, gdFontGetSmall(), textx, texty+50, (unsigned char*)buffer, ic->ctext); d = localtime(&data.created); strftime(datebuff, 16, cfg.tformat, d); snprintf(daytemp, 7, "since "); strncat(daytemp, datebuff, 16); snprintf(buffer, 20, "%19s", daytemp); gdImageString(ic->im, gdFontGetSmall(), textx-24, texty+70, (unsigned char*)buffer, ic->ctext); drawlegend(ic, 410, 155-headermod); /* hours if requested */ switch (type) { case 1: drawhours(ic, 500, 46-headermod, rate); break; case 2: drawhours(ic, 6, 215-headermod, rate); break; default: break; } } void drawoldsummary(IMAGECONTENT *ic, int type, int rate) { int piex, piey, piew, pieh, arc, textx, texty; int i, tk, width, height, headermod; float rxp = 50, txp = 50; uint64_t t, max, e_rx, e_tx; char buffer[512], datebuff[16], daytemp[32], daytemp2[32]; time_t yesterday; struct tm *d; switch (type) { case 1: width = 980; height = 200; break; case 2: width = 500; height = 370; break; default: width = 500; height = 200; break; } if (!ic->showheader) { headermod = 26; height -= 22; } else { headermod = 0; } yesterday=ic->current-86400; ic->im = gdImageCreate(width, height); colorinit(ic); if (strcmp(data.nick, data.interface)==0) { snprintf(buffer, 512, "%s", data.interface); } else { snprintf(buffer, 512, "%s (%s)", data.nick, data.interface); } layoutinit(ic, buffer, width, height); drawlegend(ic, 383, 110-headermod); if (data.totalrx || data.totalrxk || data.totaltx || data.totaltxk) { if (data.totalrx>1024 || data.totaltx>1024) { rxp = (data.totalrx/(float)(data.totalrx+data.totaltx))*100; } else { rxp = ( ((data.totalrx*1024)+data.totalrxk) / (float)(((data.totalrx*1024)+data.totalrxk)+((data.totaltx*1024)+data.totaltxk)) )*100; } txp = (float)100 - rxp; } d=localtime(&data.lastupdated); if ( data.day[0].rx==0 || data.day[0].tx==0 || (d->tm_hour*60+d->tm_min)==0 ) { e_rx = e_tx=0; } else { e_rx = ((data.day[0].rx)/(float)(d->tm_hour*60+d->tm_min))*1440; e_tx = ((data.day[0].tx)/(float)(d->tm_hour*60+d->tm_min))*1440; } piex = 400; piey = 63-headermod; piew = 110; pieh = 45; arc = (txp / (float)100) * 360; /* pie chart */ for(i = 14; i > 0; i--) { gdImageFilledArc(ic->im, piex, piey+i, piew, pieh, 270, 270+arc, ic->ctxd, gdEdged|gdNoFill); gdImageFilledArc(ic->im, piex, piey+i, piew, pieh, 270+arc, 270, ic->crxd, gdEdged|gdNoFill); } gdImageFilledArc(ic->im, piex, piey, piew, pieh, 270, 270+arc, ic->ctx, 0); gdImageFilledArc(ic->im, piex, piey, piew, pieh, 270, 270+arc, ic->ctxd, gdEdged|gdNoFill); gdImageFilledArc(ic->im, piex, piey, piew, pieh, 270+arc, 270, ic->crx, 0); gdImageFilledArc(ic->im, piex, piey, piew, pieh, 270+arc, 270, ic->crxd, gdEdged|gdNoFill); textx = 30; texty = 48-headermod; /* totals */ snprintf(buffer, 512, " received: %s (%.1f%%)", getvalue(data.totalrx, data.totalrxk, 14, 1), rxp); gdImageString(ic->im, gdFontGetLarge(), textx, texty, (unsigned char*)buffer, ic->ctext); snprintf(buffer, 512, "transmitted: %s (%.1f%%)", getvalue(data.totaltx, data.totaltxk, 14, 1), txp); gdImageString(ic->im, gdFontGetLarge(), textx, texty+15, (unsigned char*)buffer, ic->ctext); snprintf(buffer, 512, " total: %s", getvalue(data.totalrx+data.totaltx, data.totalrxk+data.totaltxk, 14, 1)); gdImageString(ic->im, gdFontGetLarge(), textx, texty+30, (unsigned char*)buffer, ic->ctext); /* get formated date for yesterday */ d=localtime(&yesterday); strftime(datebuff, 16, cfg.dformat, d); /* get formated date for previous day in database */ d=localtime(&data.day[1].date); strftime(daytemp, 16, cfg.dformat, d); /* change daytemp to yesterday if formated days match */ if (strcmp(datebuff, daytemp)==0) { strncpy_nt(daytemp, "yesterday", 32); } /* get formated date for today */ d=localtime(&ic->current); strftime(datebuff, 16, cfg.dformat, d); /* get formated date for current day in database */ d=localtime(&data.day[0].date); strftime(daytemp2, 16, cfg.dformat, d); /* change daytemp to today if formated days match */ if (strcmp(datebuff, daytemp2)==0) { strncpy_nt(daytemp2, "today", 32); } textx = 20; texty = 118-headermod; /* yesterday & today */ gdImageString(ic->im, gdFontGetSmall(), textx, texty, (unsigned char*)" rx tx total", ic->ctext); gdImageLine(ic->im, textx-4, texty+16, textx+290, texty+16, ic->cline); gdImageLine(ic->im, textx-4, texty+49, textx+290, texty+49, ic->cline); gdImageLine(ic->im, textx+140, texty+4, textx+140, texty+64, ic->cline); gdImageLine(ic->im, textx+218, texty+4, textx+218, texty+64, ic->cline); if (data.day[1].date!=0) { snprintf(buffer, 32, "%*s ", getpadding(9, daytemp), daytemp); strncat(buffer, getvalue(data.day[1].rx, data.day[1].rxk, 10, 1), 32); strcat(buffer, " "); strncat(buffer, getvalue(data.day[1].tx, data.day[1].txk, 10, 1), 32); strcat(buffer, " "); strncat(buffer, getvalue(data.day[1].rx+data.day[1].tx, data.day[1].rxk+data.day[1].txk, 10, 1), 32); gdImageString(ic->im, gdFontGetSmall(), textx, texty+20, (unsigned char*)buffer, ic->ctext); } snprintf(buffer, 32, "%*s ", getpadding(9, daytemp2), daytemp2); strncat(buffer, getvalue(data.day[0].rx, data.day[0].rxk, 10, 1), 32); strcat(buffer, " "); strncat(buffer, getvalue(data.day[0].tx, data.day[0].txk, 10, 1), 32); strcat(buffer, " "); strncat(buffer, getvalue(data.day[0].rx+data.day[0].tx, data.day[0].rxk+data.day[0].txk, 10, 1), 32); gdImageString(ic->im, gdFontGetSmall(), textx, texty+32, (unsigned char*)buffer, ic->ctext); snprintf(buffer, 32, "estimated "); strncat(buffer, getvalue(e_rx, 0, 10, 2), 32); strcat(buffer, " "); strncat(buffer, getvalue(e_tx, 0, 10, 2), 32); strcat(buffer, " "); strncat(buffer, getvalue(e_rx+e_tx, 0, 10, 2), 32); gdImageString(ic->im, gdFontGetSmall(), textx, texty+52, (unsigned char*)buffer, ic->ctext); /* search maximum */ max=1; for (i = 1; i >= 0; i--) { if (data.day[i].used) { t=data.day[i].rx+data.day[i].tx; tk=data.day[i].rxk+data.day[i].txk; t=mbkbtokb(t, tk); if (t>max) { max=t; } } } /* bars for both */ drawbar(ic, textx+300, texty+24, 165, data.day[1].rx, data.day[1].rxk, data.day[1].tx, data.day[1].txk, max); drawbar(ic, textx+300, texty+36, 165, data.day[0].rx, data.day[0].rxk, data.day[0].tx, data.day[0].txk, max); /* hours if requested */ switch (type) { case 1: drawhours(ic, 500, 46-headermod, rate); break; case 2: drawhours(ic, 16, 215-headermod, rate); break; default: break; } } void drawhourly(IMAGECONTENT *ic, int rate) { int width, height, headermod; char buffer[512]; width = 500; height = 200; if (!ic->showheader) { headermod = 26; height -= 22; } else { headermod = 0; } ic->im = gdImageCreate(width, height); colorinit(ic); if (strcmp(data.nick, data.interface)==0) { snprintf(buffer, 512, "%s / hourly", data.interface); } else { snprintf(buffer, 512, "%s (%s) / hourly", data.nick, data.interface); } layoutinit(ic, buffer, width, height); drawlegend(ic, 242, 183-headermod); drawhours(ic, 12, 46-headermod, rate); } void drawdaily(IMAGECONTENT *ic) { int textx, texty, lines; int i, tk, width, height, headermod; uint64_t t, max, e_rx, e_tx; char buffer[512], datebuff[16]; struct tm *d; /* count how many days needs to be shown */ lines = 0; for (i = 0; i < 30; i++) { if (data.day[i].used) { lines++; } } width = 500; height = 98 + (12 * lines); if (!ic->showheader) { headermod = 26; height -= 22; } else { headermod = 0; } ic->im = gdImageCreate(width, height); colorinit(ic); if (strcmp(data.nick, data.interface)==0) { snprintf(buffer, 512, "%s / daily", data.interface); } else { snprintf(buffer, 512, "%s (%s) / daily", data.nick, data.interface); } layoutinit(ic, buffer, width, height); if (lines) { if (cfg.ostyle>2) { drawlegend(ic, 432, 40-headermod); } else { drawlegend(ic, 385, 40-headermod); } } textx = 10; texty = 40-headermod; if (cfg.ostyle>2) { gdImageString(ic->im, gdFontGetSmall(), textx, texty, (unsigned char*)" day rx tx total avg. rate", ic->ctext); gdImageLine(ic->im, textx+2, texty+16, textx+392, texty+16, ic->cline); gdImageLine(ic->im, textx+144, texty+2, textx+144, texty+40+(12*lines), ic->cline); gdImageLine(ic->im, textx+222, texty+2, textx+222, texty+40+(12*lines), ic->cline); gdImageLine(ic->im, textx+300, texty+2, textx+300, texty+40+(12*lines), ic->cline); } else { gdImageString(ic->im, gdFontGetSmall(), textx, texty, (unsigned char*)" day rx tx total", ic->ctext); gdImageLine(ic->im, textx+2, texty+16, textx+296, texty+16, ic->cline); gdImageLine(ic->im, textx+144, texty+2, textx+144, texty+40+(12*lines), ic->cline); gdImageLine(ic->im, textx+222, texty+2, textx+222, texty+40+(12*lines), ic->cline); } texty += 20; /* search maximum */ max=1; for (i = 29; i >= 0; i--) { if (data.day[i].used) { t=data.day[i].rx+data.day[i].tx; tk=data.day[i].rxk+data.day[i].txk; if (tk>=1024) { t+=tk/1024; tk-=(tk/1024)*1024; } t=(t*1024)+tk; if (t>max) { max=t; } } } for (i = 29; i >= 0; i--) { if (data.day[i].used) { d = localtime(&data.day[i].date); if (strftime(datebuff, 16, cfg.dformat, d)<=8) { snprintf(buffer, 32, " %*s ", getpadding(8, datebuff), datebuff); } else { snprintf(buffer, 32, " %-*s ", getpadding(11, datebuff), datebuff); } strncat(buffer, getvalue(data.day[i].rx, data.day[i].rxk, 10, 1), 32); strcat(buffer, " "); strncat(buffer, getvalue(data.day[i].tx, data.day[i].txk, 10, 1), 32); strcat(buffer, " "); strncat(buffer, getvalue(data.day[i].rx+data.day[i].tx, data.day[i].rxk+data.day[i].txk, 10, 1), 32); if (cfg.ostyle>2) { strcat(buffer, " "); if (i==0) { d = localtime(&data.lastupdated); strncat(buffer, getrate(data.day[i].rx+data.day[i].tx, data.day[i].rxk+data.day[i].txk, d->tm_sec+(d->tm_min*60)+(d->tm_hour*3600), 14), 32); } else { strncat(buffer, getrate(data.day[i].rx+data.day[i].tx, data.day[i].rxk+data.day[i].txk, 86400, 14), 32); } } gdImageString(ic->im, gdFontGetSmall(), textx, texty, (unsigned char*)buffer, ic->ctext); if (cfg.ostyle>2) { drawbar(ic, textx+400, texty+4, 78, data.day[i].rx, data.day[i].rxk, data.day[i].tx, data.day[i].txk, max); } else { drawbar(ic, textx+304, texty+4, 170, data.day[i].rx, data.day[i].rxk, data.day[i].tx, data.day[i].txk, max); } texty += 12; } } if (lines==0) { gdImageString(ic->im, gdFontGetSmall(), textx, texty, (unsigned char*)" no data available", ic->ctext); texty += 12; } if (cfg.ostyle>2) { gdImageLine(ic->im, textx+2, texty+5, textx+392, texty+5, ic->cline); } else { gdImageLine(ic->im, textx+2, texty+5, textx+296, texty+5, ic->cline); } if (lines) { d=localtime(&data.lastupdated); if ( data.day[0].rx==0 || data.day[0].tx==0 || (d->tm_hour*60+d->tm_min)==0 ) { e_rx=e_tx=0; } else { e_rx=((data.day[0].rx)/(float)(d->tm_hour*60+d->tm_min))*1440; e_tx=((data.day[0].tx)/(float)(d->tm_hour*60+d->tm_min))*1440; } snprintf(buffer, 32, " estimated "); strncat(buffer, getvalue(e_rx, 0, 10, 2), 32); strcat(buffer, " "); strncat(buffer, getvalue(e_tx, 0, 10, 2), 32); strcat(buffer, " "); strncat(buffer, getvalue(e_rx+e_tx, 0, 10, 2), 32); gdImageString(ic->im, gdFontGetSmall(), textx, texty+8, (unsigned char*)buffer, ic->ctext); } } void drawmonthly(IMAGECONTENT *ic) { int textx, texty, lines; int i, tk, width, height, headermod; uint64_t t, max, e_rx, e_tx; char buffer[512], datebuff[16]; struct tm *d; /* count how many months needs to be shown */ lines = 0; for (i = 0; i < 12; i++) { if (data.month[i].used) { lines++; } } width = 500; height = 98 + (12 * lines); if (!ic->showheader) { headermod = 26; height -= 22; } else { headermod = 0; } ic->im = gdImageCreate(width, height); colorinit(ic); if (strcmp(data.nick, data.interface)==0) { snprintf(buffer, 512, "%s / monthly", data.interface); } else { snprintf(buffer, 512, "%s (%s) / monthly", data.nick, data.interface); } layoutinit(ic, buffer, width, height); if (lines) { if (cfg.ostyle>2) { drawlegend(ic, 432, 40-headermod); } else { drawlegend(ic, 385, 40-headermod); } } textx = 10; texty = 40-headermod; if (cfg.ostyle>2) { gdImageString(ic->im, gdFontGetSmall(), textx, texty, (unsigned char*)" month rx tx total avg. rate", ic->ctext); gdImageLine(ic->im, textx+2, texty+16, textx+392, texty+16, ic->cline); gdImageLine(ic->im, textx+144, texty+2, textx+144, texty+40+(12*lines), ic->cline); gdImageLine(ic->im, textx+222, texty+2, textx+222, texty+40+(12*lines), ic->cline); gdImageLine(ic->im, textx+300, texty+2, textx+300, texty+40+(12*lines), ic->cline); } else { gdImageString(ic->im, gdFontGetSmall(), textx, texty, (unsigned char*)" month rx tx total", ic->ctext); gdImageLine(ic->im, textx+2, texty+16, textx+296, texty+16, ic->cline); gdImageLine(ic->im, textx+144, texty+2, textx+144, texty+40+(12*lines), ic->cline); gdImageLine(ic->im, textx+222, texty+2, textx+222, texty+40+(12*lines), ic->cline); } texty += 20; /* search maximum */ max=1; for (i = 11; i >= 0; i--) { if (data.month[i].used) { t=data.month[i].rx+data.month[i].tx; tk=data.month[i].rxk+data.month[i].txk; if (tk>=1024) { t+=tk/1024; tk-=(tk/1024)*1024; } t=(t*1024)+tk; if (t>max) { max=t; } } } for (i = 11; i >= 0; i--) { if (data.month[i].used) { d = localtime(&data.month[i].month); if (strftime(datebuff, 16, cfg.mformat, d)<=9) { snprintf(buffer, 32, " %*s ", getpadding(9, datebuff), datebuff); } else { snprintf(buffer, 32, " %-*s ", getpadding(11, datebuff), datebuff); } strncat(buffer, getvalue(data.month[i].rx, data.month[i].rxk, 10, 1), 32); strcat(buffer, " "); strncat(buffer, getvalue(data.month[i].tx, data.month[i].txk, 10, 1), 32); strcat(buffer, " "); strncat(buffer, getvalue(data.month[i].rx+data.month[i].tx, data.month[i].rxk+data.month[i].txk, 10, 1), 32); if (cfg.ostyle>2) { strcat(buffer, " "); if (i==0) { strncat(buffer, getrate(data.month[0].rx+data.month[0].tx, data.month[0].rxk+data.month[0].txk, mosecs(), 14), 32); } else { strncat(buffer, getrate(data.month[i].rx+data.month[i].tx, data.month[i].rxk+data.month[i].txk, dmonth(d->tm_mon)*86400, 14), 32); } } gdImageString(ic->im, gdFontGetSmall(), textx, texty, (unsigned char*)buffer, ic->ctext); if (cfg.ostyle>2) { drawbar(ic, textx+400, texty+4, 78, data.month[i].rx, data.month[i].rxk, data.month[i].tx, data.month[i].txk, max); } else { drawbar(ic, textx+304, texty+4, 170, data.month[i].rx, data.month[i].rxk, data.month[i].tx, data.month[i].txk, max); } texty += 12; } } if (lines==0) { gdImageString(ic->im, gdFontGetSmall(), textx, texty, (unsigned char*)" no data available", ic->ctext); texty += 12; } if (cfg.ostyle>2) { gdImageLine(ic->im, textx+2, texty+5, textx+392, texty+5, ic->cline); } else { gdImageLine(ic->im, textx+2, texty+5, textx+296, texty+5, ic->cline); } if (lines) { d=localtime(&data.month[0].month); if ( data.month[0].rx==0 || data.month[0].tx==0 || (data.lastupdated-data.month[0].month)==0 ) { e_rx=e_tx=0; } else { e_rx=(data.month[0].rx/(float)(mosecs()))*(dmonth(d->tm_mon)*86400); e_tx=(data.month[0].tx/(float)(mosecs()))*(dmonth(d->tm_mon)*86400); } snprintf(buffer, 32, " estimated "); strncat(buffer, getvalue(e_rx, 0, 10, 2), 32); strcat(buffer, " "); strncat(buffer, getvalue(e_tx, 0, 10, 2), 32); strcat(buffer, " "); strncat(buffer, getvalue(e_rx+e_tx, 0, 10, 2), 32); gdImageString(ic->im, gdFontGetSmall(), textx, texty+8, (unsigned char*)buffer, ic->ctext); } } void drawtop(IMAGECONTENT *ic) { int textx, texty, lines = 0; int i, tk, width, height, headermod; uint64_t t, max = 1; char buffer[512], datebuff[16]; struct tm *d; /* count how many days needs to be shown and search maximum */ for (i = 0; i < 10; i++) { if (data.top10[i].used) { lines++; t=data.top10[i].rx+data.top10[i].tx; tk=data.top10[i].rxk+data.top10[i].txk; if (tk>=1024) { t+=tk/1024; tk-=(tk/1024)*1024; } t=(t*1024)+tk; if (t>max) { max=t; } } } width = 500; if (lines) { height = 86 + (12 * lines); } else { height = 98; } if (!ic->showheader) { headermod = 26; height -= 22; } else { headermod = 0; } ic->im = gdImageCreate(width, height); colorinit(ic); if (strcmp(data.nick, data.interface)==0) { snprintf(buffer, 512, "%s / top 10", data.interface); } else { snprintf(buffer, 512, "%s (%s) / top 10", data.nick, data.interface); } layoutinit(ic, buffer, width, height); if (lines) { if (cfg.ostyle<=2) { drawlegend(ic, 398, 40-headermod); } } textx = 10; texty = 40-headermod; if (cfg.ostyle>2) { gdImageString(ic->im, gdFontGetSmall(), textx, texty, (unsigned char*)" # day rx tx total avg. rate", ic->ctext); gdImageLine(ic->im, textx+2, texty+16, textx+422, texty+16, ic->cline); if (lines) { gdImageLine(ic->im, textx+174, texty+2, textx+174, texty+24+(12*lines), ic->cline); gdImageLine(ic->im, textx+252, texty+2, textx+252, texty+24+(12*lines), ic->cline); gdImageLine(ic->im, textx+330, texty+2, textx+330, texty+24+(12*lines), ic->cline); } } else { gdImageString(ic->im, gdFontGetSmall(), textx, texty, (unsigned char*)" # day rx tx total", ic->ctext); gdImageLine(ic->im, textx+2, texty+16, textx+326, texty+16, ic->cline); if (lines) { gdImageLine(ic->im, textx+174, texty+2, textx+174, texty+24+(12*lines), ic->cline); gdImageLine(ic->im, textx+252, texty+2, textx+252, texty+24+(12*lines), ic->cline); } } texty += 20; for (i = 0; i < 10; i++) { if (data.top10[i].used) { d = localtime(&data.top10[i].date); if (strftime(datebuff, 16, cfg.tformat, d)<=8) { snprintf(buffer, 32, " %2d %*s ", i+1, getpadding(8, datebuff), datebuff); } else { snprintf(buffer, 32, " %2d %-*s ", i+1, getpadding(11, datebuff), datebuff); } strncat(buffer, getvalue(data.top10[i].rx, data.top10[i].rxk, 10, 1), 32); strcat(buffer, " "); strncat(buffer, getvalue(data.top10[i].tx, data.top10[i].txk, 10, 1), 32); strcat(buffer, " "); strncat(buffer, getvalue(data.top10[i].rx+data.top10[i].tx, data.top10[i].rxk+data.top10[i].txk, 10, 1), 32); if (cfg.ostyle>2) { strcat(buffer, " "); strncat(buffer, getrate(data.top10[i].rx+data.top10[i].tx, data.top10[i].rxk+data.top10[i].txk, 86400, 14), 32); } gdImageString(ic->im, gdFontGetSmall(), textx, texty, (unsigned char*)buffer, ic->ctext); if (cfg.ostyle>2) { drawbar(ic, textx+428, texty+4, 52, data.top10[i].rx, data.top10[i].rxk, data.top10[i].tx, data.top10[i].txk, max); } else { drawbar(ic, textx+336, texty+4, 140, data.top10[i].rx, data.top10[i].rxk, data.top10[i].tx, data.top10[i].txk, max); } texty += 12; } } if (lines==0) { gdImageString(ic->im, gdFontGetSmall(), textx, texty, (unsigned char*)" no data available", ic->ctext); texty += 12; } if (cfg.ostyle>2) { gdImageLine(ic->im, textx+2, texty+5, textx+422, texty+5, ic->cline); } else { gdImageLine(ic->im, textx+2, texty+5, textx+326, texty+5, ic->cline); } } void hextorgb(char *input, int *rgb) { int offset; char hex[3], dec[4]; if (input[0] == '#') { offset = 1; } else { offset = 0; } snprintf(hex, 3, "%c%c", input[(0+offset)], input[(1+offset)]); snprintf(dec, 4, "%d", (int)strtol(hex, NULL, 16)); rgb[0] = atoi(dec); snprintf(hex, 3, "%c%c", input[(2+offset)], input[(3+offset)]); snprintf(dec, 4, "%d", (int)strtol(hex, NULL, 16)); rgb[1] = atoi(dec); snprintf(hex, 3, "%c%c", input[(4+offset)], input[(5+offset)]); snprintf(dec, 4, "%d", (int)strtol(hex, NULL, 16)); rgb[2] = atoi(dec); if (debug) { printf("%s -> %d, %d, %d\n", input, rgb[0], rgb[1], rgb[2]); } } void modcolor(int *rgb, int offset, int force) { int i, overflow = 0; if (debug) { printf("m%d (%d): %d, %d, %d -> ", offset, force, rgb[0], rgb[1], rgb[2]); } for (i=0; i<3; i++) { if ((rgb[i]+offset)>255 || (rgb[i]+offset)<0) { overflow++; } } /* positive offset gives lighter color, negative darker if forced */ /* otherwise the direction is changed depending on possible overflows */ for (i=0; i<3; i++) { if (overflow<2 || force) { if ((rgb[i]+offset)>255) { rgb[i] = 255; } else if ((rgb[i]+offset)<0) { rgb[i] = 0; } else { rgb[i] += offset; } } else { if ((rgb[i]-offset)<0) { rgb[i] = 0; } else if ((rgb[i]-offset)>255) { rgb[i] = 255; } else { rgb[i] -= offset; } } } if (debug) { printf("%d, %d, %d\n", rgb[0], rgb[1], rgb[2]); } } char *getimagevalue(uint64_t kb, int len, int rate) { static char buffer[64]; int declen=0; if (kb==0){ snprintf(buffer, 64, "%*s", len, "--"); } else { /* try to figure out what unit to use */ if (rate) { if (kb>=1000000000) { /* 1000*1000*1000 - value >=1000 Gbps -> show in Tbps */ snprintf(buffer, 64, "%*.*f", len, declen, kb/(float)1000000000); /* 1000*1000*1000 */ } else if (kb>=1000000) { /* 1000*1000 - value >=1000 Mbps -> show in Gbps */ snprintf(buffer, 64, "%*.*f", len, declen, kb/(float)1000000); /* 1000*1000 */ } else if (kb>=1000) { snprintf(buffer, 64, "%*.*f", len, declen, kb/(float)1000); } else { snprintf(buffer, 64, "%*"PRIu64"", len, kb); } } else { if (kb>=1048576000) { /* 1024*1024*1000 - value >=1000 GiB -> show in TiB */ snprintf(buffer, 64, "%*.*f", len, declen, kb/(float)1073741824); /* 1024*1024*1024 */ } else if (kb>=1024000) { /* 1024*1000 - value >=1000 MiB -> show in GiB */ snprintf(buffer, 64, "%*.*f", len, declen, kb/(float)1048576); /* 1024*1024 */ } else if (kb>=1000) { snprintf(buffer, 64, "%*.*f", len, declen, kb/(float)1024); } else { snprintf(buffer, 64, "%*"PRIu64"", len, kb); } } } return buffer; } char *getimagescale(uint64_t kb, int rate) { static char buffer[8]; uint32_t limit[3]; int unit; if (kb==0) { snprintf(buffer, 8, "--"); } else { if (rate) { /* convert to proper unit */ if (cfg.rateunit) { limit[0] = 1000; limit[1] = 1000000; limit[2] = 1000000000; unit = 2; } else { limit[0] = 1024; limit[1] = 1024000; limit[2] = 1048576000; unit = cfg.unit; } if (kb>=limit[2]) { snprintf(buffer, 8, "%s", getrateunit(unit, 4)); } else if (kb>=limit[1]) { snprintf(buffer, 8, "%s", getrateunit(unit, 3)); } else if (kb>=limit[0]) { snprintf(buffer, 8, "%s", getrateunit(unit, 2)); } else { snprintf(buffer, 8, "%s", getrateunit(unit, 1)); } } else { if (kb>=1048576000) { /* 1024*1024*1000 - value >=1000 GiB -> show in TiB */ snprintf(buffer, 8, "%s", getunit(4)); } else if (kb>=1024000) { /* 1024*1000 - value >=1000 MiB -> show in GiB */ snprintf(buffer, 8, "%s", getunit(3)); } else if (kb>=1000) { snprintf(buffer, 8, "%s", getunit(2)); } else { snprintf(buffer, 8, "%s", getunit(1)); } } } return buffer; } vnstat-1.14/src/ibw.h0000644000000000000000000000040312453711115013172 0ustar rootroot#ifndef IBW_H #define IBW_H int ibwloadcfg(const char *cfgfile); int ibwadd(const char *iface, uint32_t limit); void ibwlist(void); int ibwget(const char *iface); ibwnode *ibwgetnode(const char *iface); void ibwflush(void); int ibwcfgread(FILE *fd); #endif vnstat-1.14/src/dbjson.c0000644000000000000000000000612112462505225013671 0ustar rootroot#include "common.h" #include "dbjson.h" void showjson(int dbcount, char mode) { if (dbcount) { printf(","); } printf("{"); printf("\"id\":\"%s\",", data.interface); printf("\"nick\":\"%s\",", data.nick); printf("\"created\":{"); jsondate(&data.created, 1); printf("},"); printf("\"updated\":{"); jsondate(&data.lastupdated, 2); printf("},"); printf("\"traffic\":"); printf("{\"total\":{\"rx\":%"PRIu64",\"tx\":%"PRIu64"},", (data.totalrx*1024)+data.totalrxk, (data.totaltx*1024)+data.totaltxk); switch (mode) { case 'd': jsondays(); break; case 'm': jsonmonths(); break; case 't': jsontops(); break; case 'h': jsonhours(); break; case 'a': default: jsondays(); printf(","); jsonmonths(); printf(","); jsontops(); printf(","); jsonhours(); break; } printf("}}"); } void jsondays(void) { int i, first; printf("\"days\":["); for (i=0,first=1;i<=29;i++) { if (!data.day[i].used) { continue; } if (!first) { printf(","); } printf("{\"id\":%d,", i); jsondate(&data.day[i].date, 1); printf(",\"rx\":%"PRIu64",\"tx\":%"PRIu64"}", (data.day[i].rx*1024)+data.day[i].rxk, (data.day[i].tx*1024)+data.day[i].txk); first = 0; } printf("]"); } void jsonmonths(void) { int i, first; printf("\"months\":["); for (i=0,first=1;i<=11;i++) { if (!data.month[i].used) { continue; } if (!first) { printf(","); } printf("{\"id\":%d,", i); jsondate(&data.month[i].month, 3); printf(",\"rx\":%"PRIu64",\"tx\":%"PRIu64"}", (data.month[i].rx*1024)+data.month[i].rxk, (data.month[i].tx*1024)+data.month[i].txk); first = 0; } printf("]"); } void jsontops(void) { int i, first; printf("\"tops\":["); for (i=0,first=1;i<=9;i++) { if (!data.top10[i].used) { continue; } if (!first) { printf(","); } printf("{\"id\":%d,", i); jsondate(&data.top10[i].date, 2); printf(",\"rx\":%"PRIu64",\"tx\":%"PRIu64"}", (data.top10[i].rx*1024)+data.top10[i].rxk, (data.top10[i].tx*1024)+data.top10[i].txk); first = 0; } printf("]"); } void jsonhours(void) { int i, first; printf("\"hours\":["); for (i=0,first=1;i<=23;i++) { if (data.hour[i].date==0) { continue; } if (!first) { printf(","); } printf("{\"id\":%d,", i); jsondate(&data.hour[i].date, 1); printf(",\"rx\":%"PRIu64",\"tx\":%"PRIu64"}", data.hour[i].rx, data.hour[i].tx); first = 0; } printf("]"); } void jsondate(time_t *date, int type) { struct tm *d; char *type1 = "\"date\":{\"year\":%d,\"month\":%d,\"day\":%d}"; char *type2 = "\"date\":{\"year\":%d,\"month\":%d,\"day\":%d},\"time\":{\"hour\":%d,\"minutes\":%d}"; char *type3 = "\"date\":{\"year\":%d,\"month\":%d}"; d = localtime(date); if (type == 1) { printf(type1, 1900+d->tm_year, 1+d->tm_mon, d->tm_mday); } else if (type == 2) { printf(type2, 1900+d->tm_year, 1+d->tm_mon, d->tm_mday, d->tm_hour, d->tm_min); } else if (type == 3) { printf(type3, 1900+d->tm_year, 1+d->tm_mon); } } void jsonheader(void) { printf("{\"vnstatversion\":\"%s\",\"jsonversion\":\"%d\",\"interfaces\":[", VNSTATVERSION, JSONVERSION); } void jsonfooter(void) { printf("]}\n"); } vnstat-1.14/src/ibw.c0000644000000000000000000001060112453711611013167 0ustar rootroot#include "common.h" #include "cfg.h" #include "ifinfo.h" #include "ibw.h" int ibwloadcfg(const char *cfgfile) { FILE *fd; int ret; ifacebw = NULL; ret = opencfgfile(cfgfile, &fd); if (ret != 2) return ret; rewind(fd); ibwcfgread(fd); fclose(fd); if (debug) ibwlist(); return 1; } int ibwadd(const char *iface, uint32_t limit) { ibwnode *n, *p = ifacebw; /* add new node if list is empty */ if (p == NULL) { n = malloc(sizeof(ibwnode)); if (n == NULL) { return 0; } n->next = ifacebw; ifacebw = n; strncpy_nt(n->interface, iface, 32); n->limit = limit; n->fallback = limit; n->retries = 0; n->detected = 0; } else { /* update previous value if already in list */ while (p != NULL) { if (strcmp(p->interface, iface)==0) { p->limit = limit; return 1; } p = p->next; } /* add new node if not found */ n = malloc(sizeof(ibwnode)); if (n == NULL) { return 0; } n->next = ifacebw; ifacebw = n; strncpy_nt(n->interface, iface, 32); n->limit = limit; n->fallback = limit; n->retries = 0; n->detected = 0; } return 1; } void ibwlist(void) { int i=1; ibwnode *p = ifacebw; if (p == NULL) { printf("ibw list is empty.\n"); return; } printf("ibw: "); while (p != NULL) { printf("%2d: i\"%s\" l%u f%u r%d d%u ", i, p->interface, p->limit, p->fallback, p->retries, (unsigned int)p->detected); p = p->next; i++; } printf("\n"); } int ibwget(const char *iface) { ibwnode *p = ifacebw; time_t current; uint32_t speed; current = time(NULL); /* search for interface specific limit */ while (p != NULL) { if (strcasecmp(p->interface, iface)==0) { if (cfg.bwdetection && p->retries < 5) { if (cfg.bwdetectioninterval > 0 && (current - p->detected) > (cfg.bwdetectioninterval * 60)) { speed = getifspeed(iface); if (speed > 0) { p->limit = speed; p->retries = 0; p->detected = current; return speed; } p->retries++; } } if (p->limit>0) { return p->limit; } else { return -1; } } p = p->next; } if (cfg.bwdetection) { ibwadd(iface, cfg.maxbw); p = ibwgetnode(iface); speed = getifspeed(iface); if (speed > 0) { p->limit = speed; p->retries = 0; p->detected = current; return speed; } p->retries++; } /* return default limit if specified */ if (cfg.maxbw>0) { return cfg.maxbw; } else { return -1; } } ibwnode *ibwgetnode(const char *iface) { ibwnode *p = ifacebw; while (p != NULL) { if (strcasecmp(p->interface, iface)==0) { return p; } p = p->next; } return NULL; } void ibwflush(void) { ibwnode *f, *p = ifacebw; while (p != NULL) { f = p; p = p->next; free(f); } ifacebw = NULL; } int ibwcfgread(FILE *fd) { char cfgline[512], name[512], value[512]; int i, j, linelen, count = 0; int32_t ivalue; /* start from value search from first line */ rewind(fd); /* cycle all lines */ while (!feof(fd)) { cfgline[0] = '\0'; /* get current line */ if (fgets(cfgline, 512, fd)==NULL) { break; } linelen = (int)strlen(cfgline); if (linelen<=8 || cfgline[0]=='#') { continue; } if (strncasecmp(cfgline, "MaxBW", 5)!=0) { continue; } /* clear name and value buffers */ for (j=0; j<512; j++) { name[j]=value[j]='\0'; } /* get interface name */ j=0; for (i=5; iBWMAX) { snprintf(errorstring, 512, "Invalid value \"%d\" for MaxBW%s, ignoring parameter.", ivalue, name); printe(PT_Config); } else { ibwadd(name, ivalue); } } return count; } vnstat-1.14/src/common.c0000644000000000000000000001131612376402612013704 0ustar rootroot#include "common.h" int printe(PrintType type) { int result = 1; /* daemon running but log not enabled */ if (noexit==2 && cfg.uselogging==0) { return 1; /* daemon running, log enabled */ } else if (noexit==2) { switch (type) { case PT_Multiline: break; default: result = logprint(type); break; } /* daemon isn't running */ } else { switch (type) { case PT_Info: printf("Info: %s\n", errorstring); break; case PT_Error: printf("Error: %s\n", errorstring); break; case PT_Config: printf("Config: %s\n", errorstring); break; case PT_Multiline: printf("%s\n", errorstring); break; case PT_ShortMultiline: break; default: printf("%d: %s\n", type, errorstring); break; } } return result; } int logprint(PrintType type) { char timestamp[22], buffer[512]; time_t current; FILE *logfile; buffer[0] = '\0'; /* logfile */ if (cfg.uselogging==1) { if (type == PT_Multiline) { return 0; } if ((logfile = fopen(cfg.logfile, "a")) == NULL) { return 0; } current = time(NULL); strftime(timestamp, 22, "%Y-%m-%d %H:%M:%S", localtime(¤t)); switch (type) { case PT_Info: snprintf(buffer, 512, "[%s] %s\n", timestamp, errorstring); break; case PT_Error: snprintf(buffer, 512, "[%s] Error: %s\n", timestamp, errorstring); break; case PT_Config: snprintf(buffer, 512, "[%s] Config: %s\n", timestamp, errorstring); break; case PT_ShortMultiline: snprintf(buffer, 512, "[%s] %s\n", timestamp, errorstring); break; default: snprintf(buffer, 512, "[%s] (%d): %s\n", timestamp, type, errorstring); break; } if (fwrite(buffer, strlen(buffer), 1, logfile)!=1) { fclose(logfile); return 0; } fclose(logfile); return 1; /* syslog */ } else if (cfg.uselogging==2) { openlog("vnstatd", LOG_PID, LOG_DAEMON); switch (type) { case PT_Multiline: break; case PT_Error: snprintf(buffer, 512, "Error: %s", errorstring); syslog(LOG_ERR, "%s", buffer); break; case PT_Config: snprintf(buffer, 512, "Config: %s", errorstring); syslog(LOG_ERR, "%s", buffer); break; case PT_Info: case PT_ShortMultiline: snprintf(buffer, 512, "%s", errorstring); syslog(LOG_NOTICE, "%s", buffer); break; default: snprintf(buffer, 512, "(%d): %s", type, errorstring); syslog(LOG_NOTICE, "%s", buffer); break; } closelog(); return 1; } return 0; } int dmonth(int month) { static int dmon[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; int year; time_t current; /* handle leap years */ if (month==1) { current = time(NULL); year = localtime(¤t)->tm_year; if ( ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0) ) { return 29; } else { return 28; } } else { return dmon[month]; } } uint32_t mosecs(void) { struct tm *d; d = localtime(&data.month[0].month); if (d == NULL) { return 0; } if (d->tm_mday < cfg.monthrotate) { return 0; } d->tm_mday = cfg.monthrotate; d->tm_hour = d->tm_min = d->tm_sec = 0; if ((data.lastupdated-data.month[0].month)>0) { return data.lastupdated-mktime(d); } else { return 0; } } uint64_t countercalc(const uint64_t *a, const uint64_t *b) { /* no flip */ if (*b>=*a) { if (debug) printf("cc: %"PRIu64" - %"PRIu64" = %"PRIu64"\n", *b, *a, *b-*a); return *b-*a; /* flip exists */ } else { /* original counter is 64bit */ if (*a>MAX32) { if (debug) printf("cc64: uint64 - %"PRIu64" + %"PRIu64" = %"PRIu64"\n", *a, *b, (uint64_t)MAX64-*a+*b); return MAX64-*a+*b; /* original counter is 32bit */ } else { if (debug) printf("cc32: uint32 - %"PRIu64" + %"PRIu64" = %"PRIu64"\n", *a, *b, (uint64_t)MAX32-*a+*b); return MAX32-*a+*b; } } } void addtraffic(uint64_t *destmb, int *destkb, const uint64_t srcmb, const int srckb) { *destmb+=srcmb; *destkb+=srckb; if (*destkb>=1024) { *destmb+=*destkb/1024; *destkb-=(*destkb/1024)*1024; } } uint64_t mbkbtokb(uint64_t mb, uint64_t kb) { if (kb>=1024) { mb+=kb/1024; kb-=(kb/1024)*1024; } return (mb*1024)+kb; } /* strncpy with ensured null termination */ char *strncpy_nt(char *dest, const char *src, size_t n) { strncpy(dest, src, n); dest[n-1] = '\0'; return dest; } int isnumeric(const char *s) { int i, len; len = strlen(s); if (!len) { return 0; } for (i=0; i #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) || defined(__FreeBSD_kernel__) #include #include #include #include #include #include #include #endif /* OpenBSD and NetBSD don't support the ' character (decimal conversion) in printf formatting */ #if !defined(__OpenBSD__) && !defined(__NetBSD__) #define DECCONV "'" #else #define DECCONV #endif /* Note! These are only the default values for settings and most can be changed later from the config file. */ /* location of the database directory */ #ifndef DATABASEDIR #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) #define DATABASEDIR "/var/db/vnstat" #else #define DATABASEDIR "/var/lib/vnstat" #endif #endif #define DATABASEFILE "vnstat.db" /* on which day should months change */ #define MONTHROTATE 1 /* date output formats for -d, -m, -t and image header*/ /* see 'man date' for control codes <1.8 values */ #define DFORMAT "%x" /* "%d.%m." */ #define MFORMAT "%b '%y" /* "%b '%y" */ #define TFORMAT "%x" /* "%d.%m.%y" */ #define HFORMAT "%x %H:%M" /* "%d.%m.%Y %H:%M" */ /* characters used for visuals */ #define RXCHAR "%" #define TXCHAR ":" #define RXHOURCHAR "r" #define TXHOURCHAR "t" /* unit mode */ /* 0 = KiB/MiB/GiB/TiB, 1 = KB/MB/GB/TB */ #define UNITMODE 0 /* output style */ /* 0 = minimal/narrow, 1 = bars everywhere */ /* 2 = same as 1 + rate in summary and weekly */ /* 3 = rate everywhere */ #define OSTYLE 3 /* rate in vnstati summary output */ #define SUMMARYRATE 1 /* layout of summary output in vnstati */ #define SUMMARYLAYOUT 1 /* rate in vnstati hourly output */ #define HOURLYRATE 1 /* rate unit */ /* 0 = bytes, 1 = bits */ #define RATEUNIT 1 /* default interface */ #ifndef DEFIFACE #define DEFIFACE "eth0" #endif /* default locale */ #define LOCALE "-" /* bandwidth detection, 0 = feature disabled */ #define BWDETECT 1 #define BWDETECTINTERVAL 5 /* default maximum bandwidth (Mbit) for all interfaces */ /* 0 = feature disabled */ #define DEFMAXBW 1000 /* maximum allowed config value for bandwidth */ #define BWMAX 50000 /* how many seconds should sampling take by default */ #define DEFSAMPTIME 5 /* maximum time (minutes) between two updates before traffic */ /* for that period will be discarded */ /* set to a little over one hour so that it doesn't break using */ /* cron.hourly like Gentoo seems to do */ #define MAXUPDATEINTERVAL 62 /* default query mode */ /* 0 = normal, 1 = days, 2 = months, 3 = top10 */ /* 4 = exportdb, 5 = short, 6 = weeks, 7 = hours */ /* 8 = xml */ #define DEFQMODE 0 /* how much the boot time can variate between updates (seconds) */ #define BVAR 15 /* check disk space by default */ #define USESPACECHECK 1 /* use file locking by default */ #define USEFLOCK 1 /* log trafficless days by default */ #define TRAFLESSDAY 1 /* assume that locale can be UTF-n when enabled */ #define UTFLOCALE 1 /* how many times try file locking before giving up */ /* each try takes about a second */ #define LOCKTRYLIMIT 5 /* database version */ /* 1 = 1.0, 2 = 1.1-1.2, 3 = 1.3- */ #define DBVERSION 3 /* version string */ #define VNSTATVERSION "1.14" /* xml format version */ /* 1 = 1.7- */ #define XMLVERSION 1 /* json format version */ /* 1 = 1.13- */ #define JSONVERSION 1 /* --oneline format version */ #define ONELINEVERSION 1 /* integer limits */ #define MAX32 4294967295ULL #define MAX64 18446744073709551615ULL /* sampletime in seconds for live traffic */ /* don't use values below 2 */ #define LIVETIME 2 /* /proc/net/dev */ #ifndef PROCNETDEV #define PROCNETDEV "/proc/net/dev" #endif /* /sys/class/net */ #ifndef SYSCLASSNET #define SYSCLASSNET "/sys/class/net" #endif /* daemon defaults */ #define UPDATEINTERVAL 30 #define POLLINTERVAL 5 #define SAVEINTERVAL 5 #define OFFSAVEINTERVAL 30 #define SAVESTATUS 1 #define USELOGGING 2 #define CREATEDIRS 1 #define UPDATEFILEOWNER 1 #define LOGFILE "/var/log/vnstat/vnstat.log" #define PIDFILE "/var/run/vnstat/vnstat.pid" /* no transparency by default */ #define TRANSBG 0 /* default colors */ #define CBACKGROUND "FFFFFF" #define CEDGE "AEAEAE" #define CHEADER "606060" #define CHEADERTITLE "FFFFFF" #define CHEADERDATE "FFFFFF" #define CTEXT "000000" #define CLINE "B0B0B0" #define CLINEL "-" #define CRX "92CF00" #define CRXD "-" #define CTX "606060" #define CTXD "-" /* internal config structure */ typedef struct { char dformat[64], mformat[64], tformat[64], hformat[64]; char iface[32]; char locale[32]; char dbdir[512]; char rxchar[2], txchar[2], rxhourchar[2], txhourchar[2]; char cbg[8], cedge[8], cheader[8], cheadertitle[8], cheaderdate[8], ctext[8]; char cline[8], clinel[8], cvnstat[8], crx[8], crxd[8], ctx[8], ctxd[8]; int32_t unit, ostyle, rateunit, bvar, qmode, sampletime, hourlyrate, summaryrate; int32_t monthrotate, maxbw, flock, spacecheck, traflessday, transbg, slayout; char logfile[512], pidfile[512]; char daemonuser[33], daemongroup[33]; int32_t updateinterval, pollinterval, saveinterval, offsaveinterval, savestatus, uselogging; int32_t createdirs, updatefileowner, bwdetection, bwdetectioninterval, utflocale; } CFG; /* internal interface information structure */ typedef struct { char name[32]; int filled; uint64_t rx; uint64_t tx; uint64_t rxp; uint64_t txp; } IFINFO; typedef struct { time_t date; uint64_t rx, tx; } HOUR; typedef struct { time_t date; uint64_t rx, tx; int rxk, txk; int used; } DAY; typedef struct { time_t month; uint64_t rx, tx; int rxk, txk; int used; } MONTH; /* db structure */ typedef struct { int version; char interface[32]; char nick[32]; int active; uint64_t totalrx, totaltx, currx, curtx; int totalrxk, totaltxk; time_t lastupdated, created; DAY day[30]; MONTH month[12]; DAY top10[10]; HOUR hour[24]; uint64_t btime; } DATA; typedef struct ibwnode { char interface[32]; uint32_t limit; uint32_t fallback; short retries; time_t detected; struct ibwnode *next; } ibwnode; typedef enum PrintType { PT_Info = 0, PT_Error, PT_Config, PT_Multiline, PT_ShortMultiline } PrintType; /* common functions */ int printe(PrintType type); int logprint(PrintType type); int dmonth(int month); uint32_t mosecs(void); uint64_t countercalc(const uint64_t *a, const uint64_t *b); void addtraffic(uint64_t *destmb, int *destkb, const uint64_t srcmb, const int srckb); uint64_t mbkbtokb(uint64_t mb, uint64_t kb); char *strncpy_nt(char *dest, const char *src, size_t n); int isnumeric(const char *s); void panicexit(const char *sourcefile, const int sourceline); /* global variables */ DATA data; CFG cfg; IFINFO ifinfo; char errorstring[512]; ibwnode *ifacebw; int debug; int noexit; /* = running as daemon if 2 */ int intsignal; int pidfile; #endif vnstat-1.14/src/traffic.h0000644000000000000000000000021512355210223014023 0ustar rootroot#ifndef TRAFFIC_H #define TRAFFIC_H void trafficmeter(char iface[], int sampletime); void livetrafficmeter(char iface[], int mode); #endif vnstat-1.14/FAQ0000644000000000000000000001047512303447775012031 0ustar rootrootThe latest version of this FAQ is available at http://humdi.net/vnstat/FAQ ---- A snapshot of the FAQ (updated 26.2.2014): I managed to get invalid data into the database after ... playing around with the system clock / doing some strange network experiments / having the cron entry not properly configured. Is there a database editor available? No. How should dialup users use vnStat? There's some errors because the interface isn't available when offline. Dialup users should use the daemon for updates. It will take care of enabling and disabling the database every time the interface comes and goes. It is still also possible to use cron based updates even with a dialup interface but that requires proper setup of scripts with enable/disable commands every time the status of the interface changes. Example scripts can be founds from the pppd directory that came with the source packages. Does vnStat work for PPPoE users? Yes. PPPoE is basically like a dialup connection and it can be monitored in the same way as any ppp interface. However, with PPPoE it's usually also possible to bring up the LAN interface used for the connection without configuring any ip to it. Monitoring that interface is a good alternative since it doesn't go down and up when the connection is closed and opened again. How do I stop monitoring an interface? Go to the vnStat database directory (default: /var/lib/vnstat) and delete the database file named according to the interface you want to stop monitoring. The daemon needs to be stopped before this procedure if it's being used for updates. How do I uninstall vnStat? You only need to run 'make uninstall' in the directory that comes when the .tar.gz is extracted. Just make sure it's the same version you have installed. If you've used a binary package included with the distribution then refer to intructions provided by the package manager. What is this KiB/MiB/GiB/TiB thing? See http://en.wikipedia.org/wiki/Binary_prefix#Prefixes and UnitMode option in the configuration file. Is the MB value reported by vnStat 10^6 or 2^20 bytes? 2^20 bytes. The prefix can be configured to show MiB from the configuration file instead of MB if that looks better. What does the 'estimated' value mean? The estimated value is an calculated average that tries to predict the total traffic for the current day/month based on previous traffic. This estimate works fairly well if the monitored interface has constant traffic of the same magnitude (like game servers). It can't predic peaks but the accuracy usually gets better by the end of the day/month. Why isn't the estimated value shown with --dumpdb? That features only dumps the database and since the estimate is always calculated in real time, there's no reason to write it into the database. How is the estimated value calculated? estimate = ( x / y ) * z x = traffic so far for the day/month y = passed minutes/hours ... z = total minutes/hours ... Why does vnStat show sometimes multiple GB of traffic although my network connection can't even transfer that much? OR Each update adds the complete amount of traffic and 'vnstat -u -D' claims that the system has been rebooted between updates. That's most likely a broken kernel. Run with --testkernel (version 1.2+ required) and see the result. Also make sure that the maximun bandwidth setting has been configured properly according to the network connection. See the next question for some explanation about possible kernel problems. Why is there problems with most 2.4.18 kernels? Every current kernel has a btime (boot time) value in /proc/stat that indicates when the system was booted. vnStat uses this to know when the interface counters in the database should be reseted. Unfortunately, some kernels don't keep the btime value static even when the system isn't rebooted. About bug reports Any bug report should at least include an explanation about how the bug can be reproduced. Having output dumps usually helps and the --dumpdb feature should be used if there's some reason to assume the bug has something to do with the database. Also include information about the used distribution, kernel (uname -a), compiler (gcc --version) and network interface card. Read the report again before sending it. :) vnstat-1.14/man/0000755000000000000000000000000012517206051012226 5ustar rootrootvnstat-1.14/man/vnstatd.10000644000000000000000000001663612517203736014016 0ustar rootroot.TH VNSTATD 1 "APRIL 2015" "version 1.14" "User Manuals" .SH NAME vnstatd \- daemon based database updating for vnStat .SH SYNOPSIS .B vnstatd [ .B \-Ddnpsv? ] [ .B \-\-config .I file ] [ .B \-\-daemon ] [ .B \-\-debug ] [ .B \-g .I group ] [ .B \-\-group .I group ] [ .B \-\-help ] [ .B \-\-noadd ] [ .B \-\-nodaemon ] [ .B \-\-pidfile .I file ] [ .B \-\-sync ] [ .B \-\-u .I user ] [ .B \-\-user .I user ] [ .B \-\-version ] .SH DESCRIPTION The purpose of .B vnstatd is to provide a more flexible and robust way for updating .BR vnstat (1) databases than what using cron for updating can provide. The daemon makes possible updating databases more often but at the same time causes less disk access since data can be cached and written only later to disk at a user configurable interval. The availability of each interface is automatically tracked which removes the need for additional scripts to be implemented and called when an interface comes online or goes offline. .PP .B vnstatd is the command for starting the daemon. The daemon can either fork itself to run as a background process or stay attached to the terminal. It supports logging to a user selectable file or using syslog. .PP Once started, the daemon will read .BR vnstat.conf (5) if available and then check if there are any databases available in the database directory that has been specified in the configuration file. New databases will be created for all available interfaces excluding pseudo interfaces lo, lo0 and sit0 if no databases are found during startup. This behaviour can be disabled if needed. The daemon will then proceed to track the availability of monitored interfaces, process the interface traffic statistics and write new values to databases at a configured interval. As a result, the daemon ends up spending most of the time sleeping between updates. .SH OPTIONS .TP .BI "--config " file Use .I file as configuration file instead of using normal configuration file search functionality. .TP .B "-d, --daemon" Fork process to background and run as a daemon. .TP .B "-D, --debug" Provide additional output for debug purposes. The process will stay attached to the terminal for output. .TP .BI "-g, --group " group Set daemon process group to .I group during startup. .I group can be either the name of the group or a numerical group id. This option can only be used when the process is started as root. .TP .B "--noadd" Disable automatic creation of new databases for all available interfaces if the daemon is started with zero database found. Pseudo interfaces lo, lo0 and sit0 are excluded from getting added. .TP .B "-n, --nodaemon" Stay in foreground attached to the current terminal and start the update process. .TP .BI "-p, --pidfile " file Write the process id to .I file and use it for locking so that another instance of the daemon cannot be started if the same .I file is specified. .TP .B "-s, --sync" Synchronize internal counters in the database with interface counters for all available interfaces before starting traffic monitoring. Use this option if the traffic between the previous shutdown and the current startup of the daemon needs to be ignored. This option isn't required in normal use because the daemon will automatically synchronize the internal counters after a system reboot, if enought time has passed since the daemon was previously running or if the internal counters are clearly out of sync. .TP .BI "-u, --user " user Set daemon process user to .I user during startup. .I user can be either the login of the user or a numerical user id. This option can only be used when the process is started as root. .TP .B "-v, --version" Show current version of the daemon executable. .TP .B "-?, --help" Show a command option summary. .SH CONFIGURATION The behaviour of the daemon is configured mainly using the configuration keywords .B "UpdateInterval, PollInterval" and .B SaveInterval in the configuration file. .PP .B UpdateInterval defines in seconds how often the interface data is fetched and updated. This is similar to the run interval for alternative cron based updating. However, the difference is that the data doesn't directly get written to disk during updates. .PP .B PollInterval defines in seconds how often the list of available interfaces is checked for possible changes. The minimum value is 2 seconds and the maximum 60 seconds. .B PollInterval also defines the resolution for other intervals. .PP .B SaveInterval defines in minutes how often cached interface data is written to disk. A write can only occur during the updating of interface data. Therefore, the value should be a multiple of .B UpdateInterval with a maximum value of 60 minutes. .PP The default values of .B UpdateInterval 30, .B SaveInterval 5 and .B PollInterval 5 are usually suitable for most systems and provide a similar behaviour as cron based updating does but with a better resolution for interface changes and fast interfaces. .PP For embedded and/or low power systems more tuned configurations are possible. In such cases if the interfaces are mostly static the .B PollInterval can be increased to around 10-30 seconds and .B UpdateInterval set to 60 seconds. Higher values up to 300 seconds are possible if the interface speed is 10 Mbit or less. .B SaveInterval can be increased for example to 15, 30 or even 60 minutes depending on how often the data needs to be viewed. .SH SIGNALS The daemon is listening to signals .B "SIGHUP, SIGINT" and .B SIGTERM. Sending the .B SIGHUP signal to the daemon will cause cached data to be written to disk, a rescan of the database directory and a reload of settings from the configuration file. However, the pid file location will not be changed even if it's configuration setting has been modified. .PP .B SIGTERM and .B SIGINT signals will cause the daemon to write all cached data to disk and then exit. .SH FILES .TP .I /var/lib/vnstat/ Default database directory. Files are named according to the monitored interfaces. .TP .I /etc/vnstat.conf Config file that will be used unless .I $HOME/.vnstatrc exists. See the configuration chapter and .BR vnstat.conf (5) for more information. .TP .I /var/log/vnstat.log Log file that will be used if logging to file is enable and no other file is specified in the config file. .TP .I /var/run/vnstat.pid File used for storing the process id if no other file is specified in the configuration file or using the command line parameter. .SH RESTRICTIONS Updates needs to be executed at least as often as it is possible for the interface to generate enough traffic to overflow the kernel interface traffic counter. Otherwise, it is possible that some traffic won't be seen. This isn't an issue for 64-bit kernels but at least one update every hour is always required in order to provide proper input. With 32-bit kernels, the maximum time between two updates depends on how fast the interface can transfer 4 GiB. Calculated theoretical times are: .RS .TS l l. 10 Mbit: 54 minutes 100 Mbit: 5 minutes 1000 Mbit: 30 seconds .TE .RE However, for 1000 Mbit interfaces updating once every minute is usually a usable solution if faster updates can't be used. .PP Virtual and aliased interfaces cannot be monitored because the kernel doesn't provide traffic information for that type of interfaces. Such interfaces are usually named eth0:0, eth0:1, eth0:2 etc. where eth0 is the actual interface being aliased. .SH AUTHOR Teemu Toivola .SH "SEE ALSO" .BR vnstat (1), .BR vnstati (1), .BR vnstat.conf (5), .BR signal (7) vnstat-1.14/man/vnstat.10000644000000000000000000003167512517204227013646 0ustar rootroot.TH VNSTAT 1 "APRIL 2015" "version 1.14" "User Manuals" .SH NAME vnstat \- a console-based network traffic monitor .SH SYNOPSIS .B vnstat [ .B \-Ddhlmqrstuvw? ] [ .B \-\-cleartop ] [ .B \-\-config .I file ] [ .B \-\-create ] [ .B \-\-days ] [ .B \-\-delete ] [ .B \-\-dbdir .I directory ] [ .B \-\-debug ] [ .B \-\-disable ] [ .B \-\-enable ] [ .B \-\-exportdb ] [ .B \-\-help ] [ .B \-\-hours ] [ .B \-\-importdb .I file ] [ .B \-i .I interface ] [ .B \-\-iface .I interface ] [ .B \-\-iflist ] [ .B \-\-json .I mode ] [ .B \-\-live .I mode ] [ .B \-\-locale .I locale ] [ .B \-\-longhelp ] [ .B \-\-months ] [ .B \-\-nick .I nickname ] [ .B \-\-oneline ] [ .B \-\-query ] [ .B \-\-rateunit ] [ .B \-\-rebuildtotal ] [ .B \-\-reset ] [ .B \-ru ] [ .B \-\-savemerged ] [ .B \-\-short ] [ .B \-\-showconfig ] [ .B \-\-style .I number ] [ .B \-\-sync ] [ .B \-\-testkernel ] [ .B \-\-top10 ] [ .B \-tr .I time ] [ .B \-\-traffic .I time ] [ .B \-\-update ] [ .B \-\-version ] [ .B \-\-weeks ] [ .B \-\-xml .I mode ] .SH DESCRIPTION .B vnStat is a console-based network traffic monitor. It keeps a log of hourly, daily and monthly network traffic for the selected interface(s). However, it isn't a packet sniffer. The traffic information is read from the .BR proc (5) or .B sys filesystems depending on availability. That way vnStat can be used even without root permissions on most systems. .PP The implementation is divided into two commands. The purpose of the .B vnstat command is to provide an interface for querying the traffic information stored in network interface specific databases where as the daemon .BR vnstatd (1) is responsible for data retrieval and storage. Although the daemon process is constantly running as a service, it is actually spending most of the time sleeping between data updates. .SH OPTIONS .TP .B "--cleartop" Remove all top 10 entries. .TP .BI "--config " file Use .I file as configuration file instead of using normal configuration file search functionality. .TP .B "--create" Create database for interface specified with .B \-i or .B \-\-iface option. .TP .B "-d, --days" Show traffic statistics on a daily basis for the last 30 days. .TP .BI "--dbdir " directory Use .I directory as database directory instead of using the directory specified in the configuration file or the hardcoded default if no configuration file is available. .TP .B "-D, --debug" Show additional debug output. .TP .B "--delete" Delete the database for the interface specified with .B \-i or .B \-\-iface and stop monitoring it. .TP .B "--enable, --disable" Enable or disable updates for selected interface. Useful for interfaces that aren't always available, like ppp0. If the interface goes down it should be disabled in order to avoid errors. Add something like .B "vnstat -r --disable -i ppp0" to the script that's executed when the interface goes down and .B "vnstat --enable -i ppp0" to the up script. These two options aren't needed when the daemon is used. .TP .B "--exportdb" Instead of showing the database with a formatted output, this output will dump the whole database in a plain text based architeture independent format. The output can be imported back using the .B "--importdb" option and can be used for moving a database from one host to another. See the .B "--importdb" documentation below for an example. Using the output for scripting is possible but the outputs of .B "--xml" and .B "--json" are likely to be more suitable. The dump uses ; as field delimeter. .TS l l. active;1 activity status interface;eth0 name for the interface nick;inet nick (if given) created;1023895272 creation date in Unix time updated;1065467100 when the database was updated totalrx;569605 all time total received MiB totaltx;2023708 all time total transmitted MiB currx;621673719 latest rx value in /proc curtx;981730184 latest tx value in /proc totalrxk;644 total rx KiB counter totaltxk;494 total tx KiB counter btime;1059414541 system boot time in Unix time .TE Then follows 30 lines like the following d;0;1078696800;559;7433;68;557;1 where d = days, 0 = day number in database (0 is today), 1077314401 date in Unix time, 559 = rx MiB, 7433 = tx MiB, 68 = rx KiB, 557 = tx KiB and 1 tells that vnStat has filled this value and it is in use. .TS l l. m;0;1078092000;48649;139704;527;252;1 (x12) t;0;1078351200;5979;47155;362;525;1 (x10) h;0;1078699800;118265;516545 (x24) .TE m = months, t = top 10 and h = hours, all other fields are in the same order as in days except hours that doesn't have a separate KiB value. For hours the forth and fifth fields have values in KiB. .TP .B "-h, --hours" Show traffic statistics on a hourly basis for the last 24 hours. .TP .BI "--importdb " file Import a database from .I file which was previously exported using the .B "--exportdb" option. This can be used to transfer a database between different architectures and hosts, as the database is architecture dependant and not compatible between different architectures. First dump the database on one host, e.g. with .B "vnstat -i ppp0 --exportdb >ppp0db.txt" and then import the text file on a different host using .B "vnstat -i ppp0 --importdb ppp0db.txt" .TP .BI "-i, --iface " interface Select one specific .I interface and apply actions to only it. For queries, it is possible to merge the information of two or more interfaces using the .I interface1+interface2+... syntax. .TP .B "--iflist" Show list of currently available interfaces. .TP .BI "--json " mode Show database content for selected interface or all interfaces in json format. All traffic values in the output are in KiB. An optional .I mode parameter can be used for limiting the output to only selected information. Everything is shown by default. Setting .I mode to 'h' will output only hours, 'd' days, 'm' months and 't' the top 10. .TP .BI "-l, --live " mode Display current transfer rate for the selected interface in real time until interrupted. Statistics will be shown after interruption if the runtime was more than 10 seconds. An optional .I mode parameter can be used to select between the displaying of packets per second (mode 0) and transfer counters (mode 1) during execution. .B "--style" can also be used to affect the layout of the output. .TP .BI "--locale " locale Use .I locale instead of using the locale setting specified in the configuration file or the system default if no configuration file is available. .TP .B "--longhelp" Show complete options list. .TP .B "-m, --months" Show traffic statistics on a monthly basis for the last 12 months. .TP .BI "--nick " nickname Set the selected interfaces .I nickname as an alias the will be displayed in queries. Usage of .B \-u is required to save the change and the daemon may not be running during the set operation. .TP .B "--oneline" Show traffic summary for selected interface using one line with a parseable format. The output contains 15 fields with ; used as field delimeter. The 1st field contains the version information of the output that will be changed in future versions of vnStat if the field structure changes. The following fields in order 2) interface name, 3) timestamp for today, 4) rx for today, 5) tx for today, 6) total for today, 7) average traffic rate for today, 8) timestamp for current month, 9) rx for current month, 10) tx for current month, 11) total for current month, 12) average traffic rate for today, 13) all time total rx, 14) all time total tx, 15) all time total traffic. .TP .B "-q, --query" Force database query mode. .TP .B "-r, --reset" Reset the internal counters in the database for the selected interface. Use this if the interface goes down and back up, otherwise that interface will get some extra traffic to its database. Not needed when the daemon is used. .TP .B "--rebuildtotal" Reset the total traffic counters and recount those using recorded months. .TP .B "-ru, --rateunit" Swap the configured rate unit. If rate has been configured to be shown in bytes then rate will be shown in bits if this option is present. In the same way, if rate has been configured to be shown in bits then rate will be shown in bytes when this option is present. Alternatively 0 or 1 can be given as parameter for this option in order to select between bytes (0) and bits (1) regardless of the configuration file setting. .TP .B "--savemerged" Write the end result of a database merge to the file .I mergeddb that can then be used as a new database if renamed. Top 10 traffic days isn't included in the merge and will start empty in the new database. The merge interface syntax is documented in .B "-i, --iface" option. .TP .B "-s, --short" Use short output mode. This mode is also used if more than one database is available. .TP .BI "--style " number Modify the content and style of outputs. Set .I number to 0 for a more narrow output, 1 for enabling bar column, 2 for same as previous but with average traffic rate visible in summary and weekly outputs and 3 for enabling average traffic rate in all outputs where it is supported. 4 disables the use of terminal control characters in .B "-l / --live" mode. .TP .B "--sync" Synchronize internal counters in the database with interface counters for the selected interface. Use this if the system is rebooted but interface counters aren't reseted. Such can occur when suspend to ram/disk is used. Not needed when the daemon is used. .TP .B "--testkernel" Test if the kernel boot time information always stays the same like it should or if it's shifting. .TP .B "-t, --top10" Show all time top 10 traffic days. .TP .BI "-tr " time Calculate how much traffic goes through the selected interface during the given .I time seconds. The .I time will be 5 seconds if a number parameter isn't specified. .TP .B "-u, --update" Update all enabled databases or only the one specified with .B \-i parameter. Not supported when the daemon is running. .TP .B "-v, --version" Show current version. .TP .B "-w, --weeks" Show traffic for 7 days, current and previous week. .TP .BI "--xml " mode Show database content for selected interface or all interfaces in xml format. All traffic values in the output are in KiB. An optional .I mode parameter can be used for limiting the output to only selected information. Everything is shown by default. Setting .I mode to 'h' will output only hours, 'd' days, 'm' months and 't' the top 10. .TP .B "-?, --help" Show a command option summary. .SH FILES .TP .I /var/lib/vnstat/ This directory contains all databases the program uses. Files are named according to the monitored interfaces. A backup copy of each database is kept in a file starting with a . (dot character) and otherwise named according to the original file. .TP .I /etc/vnstat.conf Config file that will be used unless .I $HOME/.vnstatrc exists. See .BR vnstat.conf (5) for more information. .SH EXAMPLES .TP .B "vnstat" Display traffic summary for the default interface or multiple interfaces when more than one is monitored. .TP .B "vnstat -i eth0+eth1+eth3" Display traffic summary for a merge of interfaces eth0, eth1 and eth3. .TP .B "vnstat -i eth2 --xml" Output all information about interface eth2 in xml format. .TP .B "vnstat --json" Output all information of all monitored interfaces in json format. .TP .B "vnstat -u -i eth0" Force a database update for interface eth0 or create the database if it doesn't exist. This is usually the first command used after a fresh install if the daemon isn't used. .TP .B "vnstat -u -i eth0 --nick local" Give interface eth0 the nickname "local". That information will be later later visible as a label when eth0 is queried. The database will also be updated when this command is executed or created if the database doesn't exist. .TP .B "vnstat -i eth2 --delete" Delete database of interface eth2 and stop monitoring it. .SH RESTRICTIONS Updates needs to be executed at least as often as it is possible for the interface to generate enough traffic to overflow the kernel interface traffic counter. Otherwise, it is possible that some traffic won't be seen. This isn't an issue for 64-bit kernels but at least one update every hour is always required in order to provide proper input. With 32-bit kernels, the maximum time between two updates depends on how fast the interface can transfer 4 GiB. Calculated theoretical times are: .RS .TS l l. 10 Mbit: 54 minutes 100 Mbit: 5 minutes 1000 Mbit: 30 seconds .TE .RE However, for 1000 Mbit interfaces updating once every minute is usually a usable solution if faster updates can't be used. .PP Estimated traffic values are likely to be somewhat inaccurate if daily traffic is low because only the MiB counter is used to calculate the estimate. .PP Virtual and aliased interfaces cannot be monitored because the kernel doesn't provide traffic information for that type of interfaces. Such interfaces are usually named eth0:0, eth0:1, eth0:2 etc. where eth0 is the actual interface being aliased. .SH AUTHOR Teemu Toivola .SH "SEE ALSO" .BR vnstatd (1), .BR vnstati (1), .BR vnstat.conf (5), .BR proc (5), .BR ifconfig (8), .BR units (7) vnstat-1.14/man/vnstat.conf.50000644000000000000000000002126112517206051014561 0ustar rootroot.TH VNSTAT.CONF 5 "APRIL 2015" "version 1.14" "User Manuals" .SH NAME vnstat.conf \- vnStat configuration file .SH SYNOPSIS .B /etc/vnstat.conf .SH DESCRIPTION .BR vnstat (1), .BR vnstati (1) and .BR vnstatd (1) all use the same configuration file for configuration related settings. Some of the settings are common for all three programs. The file consists of keyword-argument pairs, one per line. Empty lines and lines starting with\ '#\' are interpreted as comments and not processed. Arguments may optionally be enclosed in double quotes (") in order to represent arguments containing spaces. Arguments can be padded with spaces or tabulator characters. A hardcoded default value will be used if a keyword can't be found from the configuration file. .PP The configuration file is divided into three sections based on the usage of each keyword. The first section contains keywords that are considered generic for all commands, the second section is for daemon related keywords and the last section for image output. .SH COMMON KEYWORDS .TP .B BandwidthDetection Try to automatically detect .B MaxBandwidth value for each monitored interface. Mostly only ethernet interfaces support this feature. .B MaxBandwidth or interface specfic .B MaxBW will be used as fallback value if detection fails. 1 = enabled, 0 = disabled. (vnstat and vnstatd only) .TP .B BootVariation Time in seconds how much the boot time reported by system kernel can variate between updates. Value range: 0..300 .TP .B CheckDiskSpace Enable or disable the availability check of at least some free disk space before a database write. 1 = enabled, 0 = disabled. .TP .B DatabaseDir Specifies the directory where interface databases are to be stored. A full path must be given and a leading '/' isn't required. .TP .B "DayFormat, MonthFormat, TopFormat" Formatting of date in available outputs. Uses the same format as .BR date (1). (vnstat and vnstati only) .TP .B Interface Default interface used when no other interface is specified on the command line. (vnstat and vnstati only) .TP .B Locale Locale setting to be used for prints. This replaces the LC_ALL environment variable. Set to "-" or leave empty in order to use the system default value. (vnstat and vnstati only) .TP .B MaxBandwidth Maximum bandwidth for all interfaces. If the interface specific traffic exceeds the given value then the data is assumed to be invalid and rejected. Set to 0 in order to disable the feature. Value range: 0..50000 (vnstat and vnstatd only) .TP .B MaxBW Same as MaxBandwidth but can be used for setting individual limits for selected interfaces. The name of the interface is specified directly after the MaxBW keyword without spaces. For example MaxBWeth0 for eth0 and MaxBWppp0 for ppp0. Value range: 0..50000 (vnstat and vnstatd only) .TP .B MonthRotate Day of month that months are expected to change. Usually set to 1 but can be set to alternative values for example for tracking monthly billed traffic where the billing period doesn't start on the first day. Value range: 1..28 (vnstat and vnstatd only) .TP .B OutputStyle Modify the content and style of text outputs. 0 = minimal and narrow output for terminal with limited width, 1 = normal output with bar column visible, 2 = same as 1 except rate is visible in summary and weekly outputs, 3 = rate column is visible in all outputs where it is supported. (vnstat and vnstati only) .TP .B QueryMode Default query mode when no parameters are given. 0 = normal, 1 = days, 2 = months, 3 = top10, 4 = exportdb, 5 = short, 6 = weeks, 7 = hours and 8 = xml. (vnstat only) .TP .B RateUnit Select which unit is used when traffic rate is visible. 0 = bytes, 1 = bits. (vnstat and vnstati only) .TP .B "RXCharacter, TXCharacter" Character used for representing the percentual share of received and transmitted traffic in daily output. (vnstat only) .TP .B "RXHourCharacter, TXHourCharacter" Character used for representing the percentual share of received and transmitted traffic in hourly output. (vnstat only) .TP .B Sampletime Defines how many seconds the \-tr option will sample traffic. Value range: 2..600 (vnstat only) .TP .B TrafficlessDays Log days without any traffic to daily list. 1 = enabled, 0 = disabled. (vnstat and vnstatd only) .TP .B UnitMode Select how units are prefixed. This will option changes only the prefix, not how traffic is calculated. 0 = IEC standard prefixes (KiB/MiB/GiB/TiB), 1 = old style binary prefixes (KB/MB/GB/TB). (vnstat and vnstati only) .TP .B UseFileLocking Enable or disable the use of file locking during database access. Disabling file locking may cause database corruption if several processes are trying to write to the file at the same time. .SH DAEMON RELATED KEYWORDS .TP .B BandwidthDetectionInterval How often in minutes interface specific detection of .B MaxBandwidth is done for detecting possible changes when .B BandwidthDetection is enabled. Can be disabled by setting to 0. Value range: 0..30 .TP .B CreateDirs Enable or disable the creation of directories when a configured path doesn't exist. This includes .B DatabaseDir , .B LogFile and .B PidFile directories. The .B LogFile directory will be created only when .B UseLogging has been set to 1. The .B PidFile directory will be created only if the daemon is started as a background process. The daemon process will try to create the directory using permissions of the user used to start the process. .TP .B DaemonGroup Specify the user to which the daemon process will switch during startup. The user can either be the name of the group or a numerical group id. Leave empty to disable the group switch. This option can only be used when the process is started as root. .TP .B DaemonUser Specify the user to which the daemon process will switch during startup. The user can either be the login of the user or a numerical user id. Leave empty to disable the user switch. This option can only be used when the process is started as root. .TP .B LogFile Specify log file path and name to be used if UseLogging is set to 1. .TP .B OfflineSaveInterval How often in minutes cached interface data is saved to file when all monitored interfaces are offline. Value range: .BR SaveInterval "..60" .TP .B PidFile Specify pid file path and name to be used. .TP .B PollInterval How often in seconds interfaces are checked for status changes. Value range: 2..300 .TP .B SaveInterval How often in minutes cached interface data is saved to file. Value range: ( .BR UpdateInterval " / 60 )..60" .TP .B SaveOnStatusChange Enable or disable the additional saving to file of cached interface data when the availability of an interface changes, i.e., when an interface goes offline or comes online. 1 = enabled, 0 = disabled. .TP .B UpdateFileOwner Enable or disable the update of file ownership during daemon process startup. Only database, log and pid files will be modified if the user or group change feature is enabled and the files don't match the requested user or group. This option can only be used when the process is started as root. .TP .B UpdateInterval How often in seconds the interface data is updated. Value range: .BR PollInterval "..300" .TP .B UseLogging Enable or disable logging. 0 = disabled, 1 = logfile and 2 = syslog. .SH IMAGE OUTPUT RELATED KEYWORDS .TP .B CBackground Background color. .TP .B CEdge Edge color if visible. .TP .B CHeader Header background color. .TP .B CHeaderTitle Header title text color. .TP .B CHeaderDate Header date text color. .TP .B CLine Line color. .TP .B CLineL Lighter version of line color. Set to '-' in order to use a calculated value based on CLine. .TP .B CRx Color for received data. .TP .B CRxD Darker version of received data color. Set to '-' in order to use a calculated value based on CRx. .TP .B CText Common text color. .TP .B CTx Color for transmitted data. .TP .B CTxD Darker version of transmitted data color. Set to '-' in order to use a calculated value based on CTx. .TP .B HeaderFormat Formatting of date in header. Uses the same format as .BR date (1). .TP .B HourlyRate Show hours with rate instead of transfered amount. 1 = enabled, 0 = disabled. .TP .B SummaryLayout Select the used layout of the summary output. 1 = layout introduced in version 1.8 with monthly traffic included, 0 = layout used before version 1.8, doesn't contain monthly traffic and doesn't support average rate. .TP .B SummaryRate Show rate in summary output if available. 1 = enabled, 0 = disabled. .TP .B TransparentBg Set background color as transparent. 1 = enabled, 0 = disabled. .SH FILES .TP .I /etc/vnstat.conf Config file that will be used unless .I $HOME/.vnstatrc exists or alternative value is given as command line parameter. .SH AUTHOR Teemu Toivola .SH "SEE ALSO" .BR vnstat (1), .BR vnstati (1), .BR vnstatd (1), .BR units (7) vnstat-1.14/man/vnstati.10000644000000000000000000001351212517203740014004 0ustar rootroot.TH VNSTATI 1 "APRIL 2015" "version 1.14" "User Manuals" .SH NAME vnstati \- png image output support for vnStat .SH SYNOPSIS .B vnstati [ .B \-cdhimostv? ] [ .B \-\-altdate ] [ .B \-\-cache .I time ] [ .B \-\-config .I file ] [ .B \-\-days ] [ .B \-\-dbdir .I directory ] [ .B \-\-headertext .I text ] [ .B \-\-help ] [ .B \-\-hours ] [ .B \-hs ] [ .B \-\-hsummary ] [ .B \-i .I interface ] [ .B \-\-iface .I interface ] [ .B \-\-locale .I locale ] [ .B \-\-months ] [ .B \-ne ] [ .B \-nh ] [ .B \-nl ] [ .B \-\-noedge ] [ .B \-\-noheader ] [ .B \-\-nolegend ] [ .B \-\-output .I file ] [ .B \-ru ] [ .B \-\-rateunit ] [ .B \-\-style .I number ] [ .B \-\-summary ] [ .B \-\-top10 ] [ .B \-\-transparent ] [ .B \-\-version ] [ .B \-vs ] [ .B \-\-vsummary ] .SH DESCRIPTION The purpose of .B vnstati is to provide image output support for statistics collected using .BR vnstat (1). The image file format is limited to png. All basic outputs of vnStat are supported excluding live traffic features. The image can be outputted either to a file or to standard output. .SH OPTIONS .TP .B "--altdate" Use alternative date and time text location. The date and time text will be moved from the upper right header section to the lower left corner. This option will have no effect if .B "-nh, --noheader" has been selected. .TP .BI "-c, --cache " time Update output file only if at least .I time minutes have passed since the previous file update. This option is ignored if stdout is used as output. .TP .BI "--config " file Use .I file as configuration file instead of using normal configuration file search functionality. .TP .B "-d, --days" Output traffic statistics on a daily basis for the last 30 days. .TP .BI "--dbdir " directory Use .I directory as database directory instead of using the directory specified in the configuration file or the hardcoded default if no configuration file is available. .TP .BI "--headertext " text Show .I text in image header section instead of automatically generated interface identification. .I text is limited to 64 characters and may not get completely shown if longer than the width of the image. Use with .B "--altdate" if maximal space is needed. This option will have no effect if .B "-nh, --noheader" has been selected. .TP .B "-h, --hours" Output traffic statistics on a hourly basis for the last 24 hours. .TP .B "-hs, --hsummary" Output traffic summary including hourly data using a horizontal layout. .TP .BI "-i, --iface " interface Use .I interface instead of default or configured interface. .TP .BI "--locale " locale Use .I locale instead of using the locale setting specified in the configuration file or the system default if no configuration file is available. .TP .B "-m, --months" Output traffic statistics on a monthly basis for the last 12 months. .TP .B "-ne, --noedge" Remove darker edges from around the image. .TP .B "-nh, --noheader" Remove header containing title and update time. Time of the previous update will still be visible in the lower right corner using a less visible color. .TP .B "-nl, --nolegend" Remove legend containing rx and tx color mapping information from the image. .TP .BI "-o, --output " file Write png image to .I file and exit. Output can be directed to stdout by giving "-" as filename. .TP .B "-ru, --rateunit" Change the configured rate unit. If rate has been configured to be shown in bytes then rate will be shown in bits if this option is present. In the same way, if rate has been configured to be shown in bits then rate will be shown in bytes when this option is present. Alternatively 0 or 1 can be given as parameter for this option in order to select between bytes (0) and bits (1) regardless of the configuration file setting. .TP .BI "--style " number Modify the content and style of outputs. Setting .I number to 3 will show average traffic rate in all outputs where it is supported. Other values will show bar graphics instead. .TP .B "-s, --summary" Output traffic statistics summary. .TP .B "-t, --top10" Output all time top 10 traffic days. .TP .B "--transparent" Toggle background color transparency depending of the TransparentBg setting in the configuration file. Alternatively 0 or 1 can be given as parameter for this option in order to either disable (0) or enable (1) transparency regardless of the configuration file setting. .TP .B "-v, --version" Show current version. .TP .B "-vs, --vsummary" Output traffic summary including hourly data using a vertical layout. .TP .B "-?, --help" Show a command option summary. .SH FILES .TP .I /var/lib/vnstat/ Default database directory. Files are named according to the monitored interfaces. .TP .I /etc/vnstat.conf Config file that will be used unless .I $HOME/.vnstatrc exists. See .BR vnstat.conf (5) for more information. .SH EXAMPLES .TP .B "vnstati -s -i eth0 -o /tmp/vnstat.png" Output traffic summary for interface eth0 to file /tmp/vnstat.png. .TP .B "vnstati -vs -i eth0+eth1+eth2 -o /tmp/vnstat.png" Output traffic summary with hourly data under the normal summary for a merge of interfaces eth0, eth1 and eth2 to file /tmp/vnstat.png. .TP .B "vnstati -h -c 15 -o /tmp/vnstat_h.png" Output hourly traffic statistics for default interface to file /tmp/vnstat_h.png if the file has not been updated within the last 15 minutes. .TP .B "vnstati -d -ne -nh -o -" Output daily traffic statistics without displaying the header section and edges for default interface to standard output (stdout). .TP .B "vnstati -m --config /home/me/vnstat.cfg -i -o -" Output monthly traffic statistics for default interface specified in configuration file /home/me/vnstat.cfg to standard output (stdout). .SH RESTRICTIONS Depending on the font provided by the GD library, not all characters may end up shown correctly when a UTF-8 locale is used. .SH AUTHOR Teemu Toivola .SH "SEE ALSO" .BR vnstat (1), .BR vnstatd (1), .BR vnstat.conf (5), .BR units (7) vnstat-1.14/cfg/0000755000000000000000000000000012453560472012223 5ustar rootrootvnstat-1.14/cfg/vnstat.conf0000644000000000000000000000664512453560472014424 0ustar rootroot# vnStat 1.13 config file ## # default interface Interface "eth0" # location of the database directory DatabaseDir "/var/lib/vnstat" # locale (LC_ALL) ("-" = use system locale) Locale "-" # on which day should months change MonthRotate 1 # date output formats for -d, -m, -t and -w # see 'man date' for control codes DayFormat "%x" MonthFormat "%b '%y" TopFormat "%x" # characters used for visuals RXCharacter "%" TXCharacter ":" RXHourCharacter "r" TXHourCharacter "t" # how units are prefixed when traffic is shown # 0 = IEC standard prefixes (KiB/MiB/GiB/TiB) # 1 = old style binary prefixes (KB/MB/GB/TB) UnitMode 0 # output style # 0 = minimal & narrow, 1 = bar column visible # 2 = same as 1 except rate in summary and weekly # 3 = rate column visible OutputStyle 3 # used rate unit (0 = bytes, 1 = bits) RateUnit 1 # try to detect interface maximum bandwidth, 0 = disable feature # MaxBandwidth will be used as fallback value when enabled BandwidthDetection 1 # maximum bandwidth (Mbit) for all interfaces, 0 = disable feature # (unless interface specific limit is given) MaxBandwidth 1000 # interface specific limits # example 8Mbit limit for 'ethnone': MaxBWethnone 8 # how many seconds should sampling for -tr take by default Sampletime 5 # default query mode # 0 = normal, 1 = days, 2 = months, 3 = top10 # 4 = exportdb, 5 = short, 6 = weeks, 7 = hours QueryMode 0 # filesystem disk space check (1 = enabled, 0 = disabled) CheckDiskSpace 1 # database file locking (1 = enabled, 0 = disabled) UseFileLocking 1 # how much the boot time can variate between updates (seconds) BootVariation 15 # log days without traffic to daily list (1 = enabled, 0 = disabled) TrafficlessDays 1 # vnstatd ## # switch to given user when started as root (leave empty to disable) DaemonUser "" # switch to given user when started as root (leave empty to disable) DaemonGroup "" # how often (in seconds) interface data is updated UpdateInterval 30 # how often (in seconds) interface status changes are checked PollInterval 5 # how often (in minutes) data is saved to file SaveInterval 5 # how often (in minutes) data is saved when all interface are offline OfflineSaveInterval 30 # how often (in minutes) bandwidth detection is redone when # BandwidthDetection is enabled (0 = disabled) BandwidthDetectionInterval 5 # force data save when interface status changes (1 = enabled, 0 = disabled) SaveOnStatusChange 1 # enable / disable logging (0 = disabled, 1 = logfile, 2 = syslog) UseLogging 2 # create dirs if needed (1 = enabled, 0 = disabled) CreateDirs 1 # update ownership of files if needed (1 = enabled, 0 = disabled) UpdateFileOwner 1 # file used for logging if UseLogging is set to 1 LogFile "/var/log/vnstat/vnstat.log" # file used as daemon pid / lock file PidFile "/var/run/vnstat/vnstat.pid" # vnstati ## # title timestamp format HeaderFormat "%x %H:%M" # show hours with rate (1 = enabled, 0 = disabled) HourlyRate 1 # show rate in summary (1 = enabled, 0 = disabled) SummaryRate 1 # layout of summary (1 = with monthly, 0 = without monthly) SummaryLayout 1 # transparent background (1 = enabled, 0 = disabled) TransparentBg 0 # image colors CBackground "FFFFFF" CEdge "AEAEAE" CHeader "606060" CHeaderTitle "FFFFFF" CHeaderDate "FFFFFF" CText "000000" CLine "B0B0B0" CLineL "-" CRx "92CF00" CTx "606060" CRxD "-" CTxD "-" vnstat-1.14/INSTALL_OSX0000644000000000000000000000353312517202056013242 0ustar rootroot Experimental instructions for OS X :::::::::::::::::::::::::::::::::: The following instructions should be considered as experimental due to not having been verified in several different OS X machines. As with any other intructions, errors in any steps should be solved before continuing with remaining steps. Compiling the binaries requires Xcode to be installed. However, Xcode is only used for installing the command line tools. The image output support hasn't been verified and isn't therefore installed with these instructions. In Xcode, open "Preferences...", then click "Downloads" and install "Command Line Tools". Open a new terminal and execute sudo -s -H followed with a password that may be required. Verify that the user is now 'root' with whoami Go to the directory where this package was extracted and compile the binaries: make This command should not have resulted in any errors. Then proceed on installing the configuration file and updating the default interface from eth0 to the currently used: cp -v cfg/vnstat.conf /etc/ nano /etc/vnstat.conf Create necessary directories, install the binaries and launchd plist file: mkdir -p /usr/local/bin mkdir -p /usr/local/libexec cp -v src/vnstat /usr/local/bin/ cp -v src/vnstatd /usr/local/libexec/ cp -v examples/launchd/net.humdi.vnstat.plist /Library/LaunchDaemons/ chown root:wheel /Library/LaunchDaemons/net.humdi.vnstat.plist Start the daemon process launchctl load /Library/LaunchDaemons/net.humdi.vnstat.plist During first startup, the daemon (vnstatd) should list and add all available interfaces for monitoring. Depending on configuration, it may take some minutes for the 'vnstat' command to begin showing results. Monitoring of unwanted interfaces can be stopped with: vnstat --delete -i ethunwanted vnstat-1.14/tests/0000755000000000000000000000000012517206620012617 5ustar rootrootvnstat-1.14/tests/misc_tests.h0000644000000000000000000000012212317006312015132 0ustar rootroot#ifndef MISC_TESTS_H #define MISC_TESTS_H void add_misc_tests(Suite *s); #endif vnstat-1.14/tests/config_tests.c0000644000000000000000000001165212453021166015456 0ustar rootroot#include "vnstat_tests.h" #include "config_tests.h" #include "common.h" #include "cfg.h" #include "ibw.h" START_TEST(validatecfg_default) { defaultcfg(); validatecfg(); } END_TEST START_TEST(printcfgfile_default) { defaultcfg(); ck_assert_int_eq(ibwadd("name1", 1), 1); ck_assert_int_eq(ibwadd("name2", 2), 1); suppress_output(); printcfgfile(); } END_TEST START_TEST(loadcfg_included_default) { ck_assert_int_eq(loadcfg("../cfg/vnstat.conf"), 1); } END_TEST START_TEST(loadcfg_no_file) { ck_assert_int_eq(loadcfg(""), 1); } END_TEST START_TEST(loadcfg_nonexistent_file) { suppress_output(); ck_assert_int_eq(loadcfg("_nosuchfile_"), 0); } END_TEST START_TEST(loadcfg_not_a_cfgfile) { ck_assert_int_eq(loadcfg("Makefile"), 1); } END_TEST START_TEST(ibwloadcfg_included_default) { ck_assert_int_eq(ibwloadcfg("../cfg/vnstat.conf"), 1); } END_TEST START_TEST(ibwloadcfg_no_file) { ck_assert_int_eq(ibwloadcfg(""), 1); } END_TEST START_TEST(ibwloadcfg_nonexistent_file) { suppress_output(); ck_assert_int_eq(ibwloadcfg("_nosuchfile_"), 0); } END_TEST START_TEST(ibwloadcfg_not_a_cfgfile) { ck_assert_int_eq(ibwloadcfg("Makefile"), 1); } END_TEST START_TEST(ibwget_with_empty_list_and_no_maxbw) { cfg.maxbw = 0; ck_assert_int_eq(ibwget("does_not_exist"), -1); } END_TEST START_TEST(ibwget_with_empty_list_and_maxbw) { cfg.maxbw = 10; ck_assert_int_eq(ibwget("does_not_exist"), 10); } END_TEST START_TEST(ibwget_from_config) { ck_assert_int_eq(loadcfg("../cfg/vnstat.conf"), 1); ck_assert_int_eq(ibwloadcfg("../cfg/vnstat.conf"), 1); cfg.maxbw = 10; ck_assert_int_eq(ibwget("ethnone"), 8); } END_TEST START_TEST(ibwadd_single_success) { cfg.maxbw = 0; ck_assert_int_eq(ibwadd("newinterface", 1), 1); ck_assert_int_eq(ibwget("does_not_exist"), -1); ck_assert_int_eq(ibwget("newinterface"), 1); } END_TEST START_TEST(ibwadd_multi_success) { cfg.maxbw = 0; ck_assert_int_eq(ibwadd("name1", 1), 1); ck_assert_int_eq(ibwadd("name2", 2), 1); ck_assert_int_eq(ibwadd("name3", 3), 1); ck_assert_int_eq(ibwadd("name4", 2), 1); ck_assert_int_eq(ibwadd("name5", 1), 1); ck_assert_int_eq(ibwadd("name6", 10), 1); ck_assert_int_eq(ibwget("does_not_exist"), -1); ck_assert_int_eq(ibwget("name1"), 1); ck_assert_int_eq(ibwget("name3"), 3); ck_assert_int_eq(ibwget("name4"), 2); ck_assert_int_eq(ibwget("name6"), 10); ck_assert_int_eq(ibwget("name2"), 2); ck_assert_int_eq(ibwget("name5"), 1); ck_assert_int_eq(ibwget("name1"), 1); ck_assert_int_eq(ibwget("does_not_exist"), -1); } END_TEST START_TEST(ibwadd_update_success) { cfg.maxbw = 0; ck_assert_int_eq(ibwadd("name1", 1), 1); ck_assert_int_eq(ibwadd("name2", 2), 1); ck_assert_int_eq(ibwget("does_not_exist"), -1); ck_assert_int_eq(ibwget("name1"), 1); ck_assert_int_eq(ibwget("name2"), 2); ck_assert_int_eq(ibwget("does_not_exist"), -1); ck_assert_int_eq(ibwadd("name2", 5), 1); ck_assert_int_eq(ibwadd("name1", 4), 1); ck_assert_int_eq(ibwget("name1"), 4); ck_assert_int_eq(ibwget("name2"), 5); ck_assert_int_eq(ibwget("does_not_exist"), -1); } END_TEST START_TEST(ibwflush_success) { cfg.maxbw = 0; ck_assert_int_eq(ibwadd("name1", 1), 1); ck_assert_int_eq(ibwadd("name2", 2), 1); ck_assert_int_eq(ibwget("name1"), 1); ck_assert_int_eq(ibwget("name2"), 2); ck_assert_int_eq(ibwget("does_not_exist"), -1); ibwflush(); ck_assert_int_eq(ibwget("name1"), -1); ck_assert_int_eq(ibwget("name2"), -1); ck_assert_int_eq(ibwget("does_not_exist"), -1); ck_assert_int_eq(ibwadd("name1", 1), 1); ck_assert_int_eq(ibwadd("name2", 2), 1); ck_assert_int_eq(ibwget("name1"), 1); ck_assert_int_eq(ibwget("name2"), 2); ck_assert_int_eq(ibwget("does_not_exist"), -1); } END_TEST START_TEST(ibwlist_empty) { suppress_output(); ibwlist(); } END_TEST START_TEST(ibwlist_filled) { cfg.maxbw = 0; ck_assert_int_eq(ibwadd("name1", 1), 1); ck_assert_int_eq(ibwadd("name2", 2), 1); suppress_output(); ibwlist(); } END_TEST void add_config_tests(Suite *s) { TCase *tc_config = tcase_create("Config"); tcase_add_test(tc_config, validatecfg_default); tcase_add_test(tc_config, printcfgfile_default); tcase_add_test(tc_config, loadcfg_included_default); tcase_add_test(tc_config, loadcfg_no_file); tcase_add_test(tc_config, loadcfg_nonexistent_file); tcase_add_test(tc_config, loadcfg_not_a_cfgfile); tcase_add_test(tc_config, ibwloadcfg_included_default); tcase_add_test(tc_config, ibwloadcfg_no_file); tcase_add_test(tc_config, ibwloadcfg_nonexistent_file); tcase_add_test(tc_config, ibwloadcfg_not_a_cfgfile); tcase_add_test(tc_config, ibwget_with_empty_list_and_no_maxbw); tcase_add_test(tc_config, ibwget_with_empty_list_and_maxbw); tcase_add_test(tc_config, ibwget_from_config); tcase_add_test(tc_config, ibwadd_single_success); tcase_add_test(tc_config, ibwadd_multi_success); tcase_add_test(tc_config, ibwadd_update_success); tcase_add_test(tc_config, ibwflush_success); tcase_add_test(tc_config, ibwlist_empty); tcase_add_test(tc_config, ibwlist_filled); suite_add_tcase(s, tc_config); } vnstat-1.14/tests/ifinfo_tests.c0000644000000000000000000003462212460000732015457 0ustar rootroot#include "vnstat_tests.h" #include "ifinfo_tests.h" #include "common.h" #include "ifinfo.h" #include "dbaccess.h" #include "misc.h" #include "cfg.h" #include "ibw.h" START_TEST(parseifinfo_zero_change) { initdb(); data.btime = getbtime(); data.lastupdated -= 100; strcpy(data.interface, "eth0"); ck_assert_int_eq(ibwadd("eth0", 10), 1); strcpy(ifinfo.name, "eth0"); ifinfo.filled = 1; ifinfo.rx = 0; ifinfo.tx = 0; ifinfo.rxp = ifinfo.txp = 0; parseifinfo(0); ck_assert_int_eq(ifinfo.rx, 0); ck_assert_int_eq(ifinfo.tx, 0); ck_assert_int_eq(ifinfo.rxp, 0); ck_assert_int_eq(ifinfo.txp, 0); ck_assert_int_eq(data.day[0].rx, 0); ck_assert_int_eq(data.day[0].tx, 0); ck_assert_int_eq(data.day[0].rxk, 0); ck_assert_int_eq(data.day[0].txk, 0); ck_assert_int_eq(data.month[0].rx, 0); ck_assert_int_eq(data.month[0].tx, 0); ck_assert_int_eq(data.month[0].rxk, 0); ck_assert_int_eq(data.month[0].txk, 0); ck_assert_int_eq(data.totalrx, 0); ck_assert_int_eq(data.totaltx, 0); ck_assert_int_eq(data.totalrxk, 0); ck_assert_int_eq(data.totaltxk, 0); ck_assert_int_eq(data.currx, 0); ck_assert_int_eq(data.curtx, 0); } END_TEST START_TEST(parseifinfo_1kb_change) { initdb(); data.btime = getbtime(); data.lastupdated -= 100; data.currx = 1024; data.curtx = 1024; strcpy(data.interface, "eth0"); ck_assert_int_eq(ibwadd("eth0", 10), 1); strcpy(ifinfo.name, "eth0"); ifinfo.filled = 1; ifinfo.rx = 2048; ifinfo.tx = 2048; ifinfo.rxp = ifinfo.txp = 0; parseifinfo(0); ck_assert_int_eq(ifinfo.rx, 2048); ck_assert_int_eq(ifinfo.tx, 2048); ck_assert_int_eq(ifinfo.rxp, 0); ck_assert_int_eq(ifinfo.txp, 0); ck_assert_int_eq(data.day[0].rx, 0); ck_assert_int_eq(data.day[0].tx, 0); ck_assert_int_eq(data.day[0].rxk, 1); ck_assert_int_eq(data.day[0].txk, 1); ck_assert_int_eq(data.month[0].rx, 0); ck_assert_int_eq(data.month[0].tx, 0); ck_assert_int_eq(data.month[0].rxk, 1); ck_assert_int_eq(data.month[0].txk, 1); ck_assert_int_eq(data.totalrx, 0); ck_assert_int_eq(data.totaltx, 0); ck_assert_int_eq(data.totalrxk, 1); ck_assert_int_eq(data.totaltxk, 1); ck_assert_int_eq(data.currx, 2048); ck_assert_int_eq(data.curtx, 2048); } END_TEST START_TEST(parseifinfo_newdb) { initdb(); data.btime = getbtime(); data.lastupdated -= 100; data.currx = 1024; data.curtx = 1024; strcpy(data.interface, "eth0"); ck_assert_int_eq(ibwadd("eth0", 10), 1); strcpy(ifinfo.name, "eth0"); ifinfo.filled = 1; ifinfo.rx = 2048; ifinfo.tx = 2048; ifinfo.rxp = ifinfo.txp = 0; parseifinfo(1); ck_assert_int_eq(ifinfo.rx, 2048); ck_assert_int_eq(ifinfo.tx, 2048); ck_assert_int_eq(ifinfo.rxp, 0); ck_assert_int_eq(ifinfo.txp, 0); ck_assert_int_eq(data.day[0].rx, 0); ck_assert_int_eq(data.day[0].tx, 0); ck_assert_int_eq(data.day[0].rxk, 0); ck_assert_int_eq(data.day[0].txk, 0); ck_assert_int_eq(data.totalrx, 0); ck_assert_int_eq(data.totaltx, 0); ck_assert_int_eq(data.totalrxk, 0); ck_assert_int_eq(data.totaltxk, 0); ck_assert_int_eq(data.currx, 2048); ck_assert_int_eq(data.curtx, 2048); } END_TEST START_TEST(parseifinfo_1kb_change_with_booted_system) { initdb(); data.btime = 0; data.lastupdated -= 100; data.currx = 1024; data.curtx = 1024; strcpy(data.interface, "eth0"); ck_assert_int_eq(ibwadd("eth0", 10), 1); strcpy(ifinfo.name, "eth0"); ifinfo.filled = 1; ifinfo.rx = 1024; ifinfo.tx = 1024; ifinfo.rxp = ifinfo.txp = 0; parseifinfo(0); ck_assert_int_eq(ifinfo.rx, 1024); ck_assert_int_eq(ifinfo.tx, 1024); ck_assert_int_eq(ifinfo.rxp, 0); ck_assert_int_eq(ifinfo.txp, 0); ck_assert_int_eq(data.day[0].rx, 0); ck_assert_int_eq(data.day[0].tx, 0); ck_assert_int_eq(data.day[0].rxk, 1); ck_assert_int_eq(data.day[0].txk, 1); ck_assert_int_eq(data.totalrx, 0); ck_assert_int_eq(data.totaltx, 0); ck_assert_int_eq(data.totalrxk, 1); ck_assert_int_eq(data.totaltxk, 1); ck_assert_int_eq(data.currx, 1024); ck_assert_int_eq(data.curtx, 1024); } END_TEST START_TEST(parseifinfo_long_update_interval_causes_sync) { initdb(); data.btime = getbtime(); data.lastupdated -= (60*MAXUPDATEINTERVAL + 10); strcpy(data.interface, "eth0"); ck_assert_int_eq(ibwadd("eth0", 10), 1); strcpy(ifinfo.name, "eth0"); ifinfo.filled = 1; ifinfo.rx = 1024; ifinfo.tx = 1024; ifinfo.rxp = ifinfo.txp = 0; parseifinfo(0); ck_assert_int_eq(ifinfo.rx, 1024); ck_assert_int_eq(ifinfo.tx, 1024); ck_assert_int_eq(ifinfo.rxp, 0); ck_assert_int_eq(ifinfo.txp, 0); ck_assert_int_eq(data.day[0].rx, 0); ck_assert_int_eq(data.day[0].tx, 0); ck_assert_int_eq(data.day[0].rxk, 0); ck_assert_int_eq(data.day[0].txk, 0); ck_assert_int_eq(data.totalrx, 0); ck_assert_int_eq(data.totaltx, 0); ck_assert_int_eq(data.totalrxk, 0); ck_assert_int_eq(data.totaltxk, 0); ck_assert_int_eq(data.currx, 1024); ck_assert_int_eq(data.curtx, 1024); } END_TEST START_TEST(parseifinfo_hitting_maxbw_limit_causes_sync) { initdb(); data.btime = getbtime(); data.lastupdated -= 1; data.currx = 1024; data.curtx = 1024; strcpy(data.interface, "eth0"); ck_assert_int_eq(ibwadd("eth0", 10), 1); strcpy(ifinfo.name, "eth0"); ifinfo.filled = 1; ifinfo.rx = 123456789; ifinfo.tx = 123456789; ifinfo.rxp = ifinfo.txp = 0; debug = 1; suppress_output(); parseifinfo(0); ck_assert_int_eq(ifinfo.rx, 123456789); ck_assert_int_eq(ifinfo.tx, 123456789); ck_assert_int_eq(ifinfo.rxp, 0); ck_assert_int_eq(ifinfo.txp, 0); ck_assert_int_eq(data.day[0].rx, 0); ck_assert_int_eq(data.day[0].tx, 0); ck_assert_int_eq(data.day[0].rxk, 0); ck_assert_int_eq(data.day[0].txk, 0); ck_assert_int_eq(data.totalrx, 0); ck_assert_int_eq(data.totaltx, 0); ck_assert_int_eq(data.totalrxk, 0); ck_assert_int_eq(data.totaltxk, 0); ck_assert_int_eq(data.currx, 123456789); ck_assert_int_eq(data.curtx, 123456789); } END_TEST START_TEST(parseifinfo_multiple_parses) { initdb(); data.btime = getbtime(); data.lastupdated -= 20; data.currx = 0; data.curtx = 0; strcpy(data.interface, "eth0"); ck_assert_int_eq(ibwadd("eth0", 10), 1); strcpy(ifinfo.name, "eth0"); ifinfo.filled = 1; ifinfo.rx = 2049; ifinfo.tx = 2049; ifinfo.rxp = ifinfo.txp = 0; parseifinfo(0); ck_assert_int_eq(ifinfo.rx, 2049); ck_assert_int_eq(ifinfo.tx, 2049); ck_assert_int_eq(ifinfo.rxp, 1); ck_assert_int_eq(ifinfo.txp, 1); ck_assert_int_eq(data.day[0].rx, 0); ck_assert_int_eq(data.day[0].tx, 0); ck_assert_int_eq(data.day[0].rxk, 2); ck_assert_int_eq(data.day[0].txk, 2); ck_assert_int_eq(data.totalrx, 0); ck_assert_int_eq(data.totaltx, 0); ck_assert_int_eq(data.totalrxk, 2); ck_assert_int_eq(data.totaltxk, 2); ck_assert_int_eq(data.currx, 2048); ck_assert_int_eq(data.curtx, 2048); data.lastupdated -= 15; ifinfo.rx = 4098; ifinfo.tx = 4098; ifinfo.rxp = ifinfo.txp = 0; parseifinfo(0); ck_assert_int_eq(ifinfo.rx, 4098); ck_assert_int_eq(ifinfo.tx, 4098); ck_assert_int_eq(ifinfo.rxp, 2); ck_assert_int_eq(ifinfo.txp, 2); ck_assert_int_eq(data.day[0].rx, 0); ck_assert_int_eq(data.day[0].tx, 0); ck_assert_int_eq(data.day[0].rxk, 4); ck_assert_int_eq(data.day[0].txk, 4); ck_assert_int_eq(data.totalrx, 0); ck_assert_int_eq(data.totaltx, 0); ck_assert_int_eq(data.totalrxk, 4); ck_assert_int_eq(data.totaltxk, 4); ck_assert_int_eq(data.currx, 4096); ck_assert_int_eq(data.curtx, 4096); data.lastupdated -= 10; ifinfo.rx = 8192; ifinfo.tx = 8192; ifinfo.rxp = ifinfo.txp = 0; parseifinfo(0); ck_assert_int_eq(ifinfo.rx, 8192); ck_assert_int_eq(ifinfo.tx, 8192); ck_assert_int_eq(ifinfo.rxp, 0); ck_assert_int_eq(ifinfo.txp, 0); ck_assert_int_eq(data.day[0].rx, 0); ck_assert_int_eq(data.day[0].tx, 0); ck_assert_int_eq(data.day[0].rxk, 8); ck_assert_int_eq(data.day[0].txk, 8); ck_assert_int_eq(data.totalrx, 0); ck_assert_int_eq(data.totaltx, 0); ck_assert_int_eq(data.totalrxk, 8); ck_assert_int_eq(data.totaltxk, 8); ck_assert_int_eq(data.currx, 8192); ck_assert_int_eq(data.curtx, 8192); data.lastupdated -= 5; ifinfo.rx = 1048576; ifinfo.tx = 1048576; ifinfo.rxp = ifinfo.txp = 0; parseifinfo(0); ck_assert_int_eq(ifinfo.rx, 1048576); ck_assert_int_eq(ifinfo.tx, 1048576); ck_assert_int_eq(ifinfo.rxp, 0); ck_assert_int_eq(ifinfo.txp, 0); ck_assert_int_eq(data.day[0].rx, 1); ck_assert_int_eq(data.day[0].tx, 1); ck_assert_int_eq(data.day[0].rxk, 0); ck_assert_int_eq(data.day[0].txk, 0); ck_assert_int_eq(data.totalrx, 1); ck_assert_int_eq(data.totaltx, 1); ck_assert_int_eq(data.totalrxk, 0); ck_assert_int_eq(data.totaltxk, 0); ck_assert_int_eq(data.currx, 1048576); ck_assert_int_eq(data.curtx, 1048576); } END_TEST START_TEST(getiflist_no_source) { char *ifacelist; linuxonly; ck_assert_int_eq(remove_directory(TESTDIR), 1); ck_assert_int_eq(getiflist(&ifacelist, 0), 0); ck_assert_str_eq(ifacelist, ""); free(ifacelist); } END_TEST START_TEST(getiflist_proc_one_interface) { char *ifacelist; linuxonly; ck_assert_int_eq(remove_directory(TESTDIR), 1); fake_proc_net_dev("w", "ethunusual", 0, 0, 0, 0); ck_assert_int_eq(getiflist(&ifacelist, 1), 1); ck_assert_str_eq(ifacelist, "lo ethunusual "); free(ifacelist); } END_TEST START_TEST(getiflist_proc_one_interface_with_speed) { char *ifacelist; linuxonly; ck_assert_int_eq(remove_directory(TESTDIR), 1); fake_proc_net_dev("w", "ethunusual", 0, 0, 0, 0); fake_sys_class_net("ethunusual", 0, 0, 0, 0, 10); ck_assert_int_eq(getiflist(&ifacelist, 1), 1); ck_assert_str_eq(ifacelist, "lo ethunusual (10 Mbit) "); free(ifacelist); } END_TEST START_TEST(getiflist_proc_multiple_interfaces) { char *ifacelist; linuxonly; ck_assert_int_eq(remove_directory(TESTDIR), 1); fake_proc_net_dev("w", "random", 0, 0, 0, 0); fake_proc_net_dev("a", "interfaces", 0, 0, 0, 0); fake_proc_net_dev("a", "having", 0, 0, 0, 0); fake_proc_net_dev("a", "fun", 0, 0, 0, 0); fake_proc_net_dev("a", "i", 0, 0, 0, 0); ck_assert_int_eq(getiflist(&ifacelist, 0), 1); ck_assert_str_eq(ifacelist, "lo random interfaces having fun i "); free(ifacelist); } END_TEST START_TEST(getiflist_sysclassnet_one_interface) { char *ifacelist; linuxonly; ck_assert_int_eq(remove_directory(TESTDIR), 1); fake_sys_class_net("ethunusual", 0, 0, 0, 0, 0); ck_assert_int_eq(getiflist(&ifacelist, 1), 1); ck_assert_str_eq(ifacelist, "ethunusual "); free(ifacelist); } END_TEST START_TEST(getiflist_sysclassnet_one_interface_with_speed) { char *ifacelist; linuxonly; ck_assert_int_eq(remove_directory(TESTDIR), 1); fake_sys_class_net("ethunusual", 0, 0, 0, 0, 10); ck_assert_int_eq(getiflist(&ifacelist, 1), 1); ck_assert_str_eq(ifacelist, "ethunusual (10 Mbit) "); free(ifacelist); } END_TEST START_TEST(getiflist_sysclassnet_multiple_interfaces) { char *ifacelist; linuxonly; ck_assert_int_eq(remove_directory(TESTDIR), 1); fake_sys_class_net("random", 0, 0, 0, 0, 0); fake_sys_class_net("interfaces", 0, 0, 0, 0, 0); fake_sys_class_net("having", 0, 0, 0, 0, 0); fake_sys_class_net("fun", 0, 0, 0, 0, 0); fake_sys_class_net("i", 0, 0, 0, 0, 0); ck_assert_int_eq(getiflist(&ifacelist, 0), 1); ck_assert_int_eq(strlen(ifacelist), 31); free(ifacelist); } END_TEST START_TEST(readproc_no_file) { linuxonly; ck_assert_int_eq(remove_directory(TESTDIR), 1); ck_assert_int_eq(readproc("ethunusual"), 0); } END_TEST START_TEST(readproc_not_found) { linuxonly; ck_assert_int_eq(remove_directory(TESTDIR), 1); fake_proc_net_dev("w", "ethwrong", 10, 20, 30, 40); ck_assert_int_eq(readproc("ethunusual"), 0); } END_TEST START_TEST(readproc_success) { linuxonly; ck_assert_int_eq(remove_directory(TESTDIR), 1); fake_proc_net_dev("w", "ethwrong", 10, 20, 30, 40); fake_proc_net_dev("a", "ethunusual", 1, 2, 3, 4); ck_assert_int_eq(readproc("ethunusual"), 1); ck_assert_str_eq(ifinfo.name, "ethunusual"); ck_assert_int_eq(ifinfo.filled, 1); ck_assert_int_eq(ifinfo.rx, 1); ck_assert_int_eq(ifinfo.tx, 2); ck_assert_int_eq(ifinfo.rxp, 3); ck_assert_int_eq(ifinfo.txp, 4); } END_TEST START_TEST(readsysclassnet_not_found) { linuxonly; ck_assert_int_eq(remove_directory(TESTDIR), 1); fake_sys_class_net("ethwrong", 10, 20, 30, 40, 50); ck_assert_int_eq(readsysclassnet("ethunusual"), 0); } END_TEST START_TEST(readsysclassnet_success) { linuxonly; ck_assert_int_eq(remove_directory(TESTDIR), 1); fake_sys_class_net("ethwrong", 10, 20, 30, 40, 50); fake_sys_class_net("ethunusual", 1, 2, 3, 4, 5); ck_assert_int_eq(readsysclassnet("ethunusual"), 1); ck_assert_str_eq(ifinfo.name, "ethunusual"); ck_assert_int_eq(ifinfo.filled, 1); ck_assert_int_eq(ifinfo.rx, 1); ck_assert_int_eq(ifinfo.tx, 2); ck_assert_int_eq(ifinfo.rxp, 3); ck_assert_int_eq(ifinfo.txp, 4); } END_TEST START_TEST(getifinfo_not_found) { linuxonly; suppress_output(); ck_assert_int_eq(remove_directory(TESTDIR), 1); fake_proc_net_dev("w", "ethwrong", 10, 20, 30, 40); ck_assert_int_eq(getifinfo("ethunusual"), 0); } END_TEST START_TEST(getifinfo_success) { linuxonly; suppress_output(); ck_assert_int_eq(remove_directory(TESTDIR), 1); fake_proc_net_dev("w", "ethwrong", 10, 20, 30, 40); fake_proc_net_dev("a", "ethunusual", 1, 2, 3, 4); ck_assert_int_eq(getifinfo("ethunusual"), 1); ck_assert_str_eq(ifinfo.name, "ethunusual"); ck_assert_int_eq(ifinfo.filled, 1); ck_assert_int_eq(ifinfo.rx, 1); ck_assert_int_eq(ifinfo.tx, 2); ck_assert_int_eq(ifinfo.rxp, 3); ck_assert_int_eq(ifinfo.txp, 4); } END_TEST void add_ifinfo_tests(Suite *s) { TCase *tc_ifinfo = tcase_create("Ifinfo"); tcase_add_test(tc_ifinfo, parseifinfo_zero_change); tcase_add_test(tc_ifinfo, parseifinfo_1kb_change); tcase_add_test(tc_ifinfo, parseifinfo_newdb); tcase_add_test(tc_ifinfo, parseifinfo_1kb_change_with_booted_system); tcase_add_test(tc_ifinfo, parseifinfo_long_update_interval_causes_sync); tcase_add_test(tc_ifinfo, parseifinfo_hitting_maxbw_limit_causes_sync); tcase_add_test(tc_ifinfo, parseifinfo_multiple_parses); tcase_add_test(tc_ifinfo, getiflist_no_source); tcase_add_test(tc_ifinfo, getiflist_proc_one_interface); tcase_add_test(tc_ifinfo, getiflist_proc_one_interface_with_speed); tcase_add_test(tc_ifinfo, getiflist_proc_multiple_interfaces); tcase_add_test(tc_ifinfo, getiflist_sysclassnet_one_interface); tcase_add_test(tc_ifinfo, getiflist_sysclassnet_one_interface_with_speed); tcase_add_test(tc_ifinfo, getiflist_sysclassnet_multiple_interfaces); tcase_add_test(tc_ifinfo, readproc_no_file); tcase_add_test(tc_ifinfo, readproc_not_found); tcase_add_test(tc_ifinfo, readproc_success); tcase_add_test(tc_ifinfo, readsysclassnet_not_found); tcase_add_test(tc_ifinfo, readsysclassnet_success); tcase_add_test(tc_ifinfo, getifinfo_not_found); tcase_add_test(tc_ifinfo, getifinfo_success); suite_add_tcase(s, tc_ifinfo); } vnstat-1.14/tests/vnstat_tests.h0000644000000000000000000000263012506045367015541 0ustar rootroot#ifndef VNSTAT_TESTS_H #define VNSTAT_TESTS_H #include Suite *test_suite(void); void suppress_output(void); int pipe_output(void); void disable_logprints(void); int clean_testdbdir(void); int create_testdir(void); int create_directory(const char *directory); int remove_directory(const char *directory); int create_zerosize_dbfile(const char *iface); int check_dbfile_exists(const char *iface, const int minsize); int fake_proc_net_dev(const char *mode, const char *iface, const int rx, const int tx, const int rxp, const int txp); int fake_sys_class_net(const char *iface, const int rx, const int tx, const int rxp, const int txp, const int speed); #define TESTDIR "testdir" #define TESTDBDIR "testdir/database" #define TESTPROCDIR "testdir/proc" #define TESTSYSCLASSNETDIR "testdir/sysclassnet" #if !defined(__linux__) #define linuxonly return #else #define linuxonly #endif #if !defined(__linux__) #define linuxonly_exit exit(1) #else #define linuxonly_exit #endif /* for compatibility with older check framework versions */ #ifndef ck_assert_int_ge #define ck_assert_int_ge(X, Y) _ck_assert_int(X, >=, Y) #endif #ifndef ck_assert_int_gt #define ck_assert_int_gt(X, Y) _ck_assert_int(X, >, Y) #endif #ifndef ck_assert_int_le #define ck_assert_int_le(X, Y) _ck_assert_int(X, <=, Y) #endif #ifndef ck_assert_int_lt #define ck_assert_int_lt(X, Y) _ck_assert_int(X, <, Y) #endif #endif vnstat-1.14/tests/database_tests.c0000644000000000000000000007166212461026403015762 0ustar rootroot#include "vnstat_tests.h" #include "database_tests.h" #include "common.h" #include "ifinfo.h" #include "dbaccess.h" #include "dbcache.h" #include "dbshow.h" #include "cfg.h" #include "ibw.h" START_TEST(initdb_activates_database) { initdb(); ck_assert_int_eq(data.active, 1); } END_TEST START_TEST(cleanhours_really_cleans_hours) { int i; initdb(); for (i=0; i<24; i++) { ck_assert_int_eq(data.hour[i].rx, 0); ck_assert_int_eq(data.hour[i].tx, 0); } for (i=0; i<24; i++) { data.hour[i].rx = data.hour[i].tx = i+1; data.hour[i].date = 1; } cleanhours(); for (i=0; i<24; i++) { ck_assert_int_eq(data.hour[i].rx, 0); ck_assert_int_eq(data.hour[i].tx, 0); } } END_TEST START_TEST(cleanhours_does_not_remove_current_data) { int i; initdb(); for (i=0; i<24; i++) { ck_assert_int_eq(data.hour[i].rx, 0); ck_assert_int_eq(data.hour[i].tx, 0); } for (i=0; i<24; i++) { data.hour[i].rx = data.hour[i].tx = i+1; data.hour[i].date = time(NULL); } cleanhours(); for (i=0; i<24; i++) { ck_assert_int_ne(data.hour[i].rx, 0); ck_assert_int_ne(data.hour[i].tx, 0); } } END_TEST START_TEST(rotatedays_really_rotates_days) { int i; initdb(); for (i=0; i<30; i++) { ck_assert_int_eq(data.day[i].rx, 0); ck_assert_int_eq(data.day[i].tx, 0); ck_assert_int_eq(data.day[i].rxk, 0); ck_assert_int_eq(data.day[i].txk, 0); } data.day[0].rx = data.day[0].tx = 1; data.day[0].rxk = data.day[0].txk = 1; data.day[0].date = 1; data.day[0].used = 1; rotatedays(); ck_assert_int_eq(data.day[0].rx, 0); ck_assert_int_eq(data.day[0].tx, 0); ck_assert_int_eq(data.day[0].rxk, 0); ck_assert_int_eq(data.day[0].txk, 0); ck_assert_int_ne(data.day[0].date, 0); ck_assert_int_eq(data.day[1].rx, 1); ck_assert_int_eq(data.day[1].tx, 1); ck_assert_int_eq(data.day[1].rxk, 1); ck_assert_int_eq(data.day[1].txk, 1); ck_assert_int_eq(data.day[1].date, 1); ck_assert_int_eq(data.day[1].used, 1); } END_TEST START_TEST(rotatedays_updates_top10) { int i; initdb(); for (i=0; i<10 ; i++) { ck_assert_int_eq(data.top10[i].rx, 0); ck_assert_int_eq(data.top10[i].tx, 0); ck_assert_int_eq(data.top10[i].rxk, 0); ck_assert_int_eq(data.top10[i].txk, 0); } data.day[0].rx = data.day[0].tx = 1; data.day[0].rxk = data.day[0].txk = 1; data.day[0].date = 1; data.day[0].used = 1; rotatedays(); ck_assert_int_eq(data.top10[0].rx, 1); ck_assert_int_eq(data.top10[0].tx, 1); ck_assert_int_eq(data.top10[0].rxk, 1); ck_assert_int_eq(data.top10[0].txk, 1); for (i=1; i<10 ; i++) { ck_assert_int_eq(data.top10[i].rx, 0); ck_assert_int_eq(data.top10[i].tx, 0); ck_assert_int_eq(data.top10[i].rxk, 0); ck_assert_int_eq(data.top10[i].txk, 0); } data.day[0].rx = data.day[0].tx = 3; data.day[0].rxk = data.day[0].txk = 3; data.day[0].date = 3; data.day[0].used = 3; rotatedays(); ck_assert_int_eq(data.top10[0].rx, 3); ck_assert_int_eq(data.top10[0].tx, 3); ck_assert_int_eq(data.top10[0].rxk, 3); ck_assert_int_eq(data.top10[0].txk, 3); ck_assert_int_eq(data.top10[1].rx, 1); ck_assert_int_eq(data.top10[1].tx, 1); ck_assert_int_eq(data.top10[1].rxk, 1); ck_assert_int_eq(data.top10[1].txk, 1); for (i=2; i<10 ; i++) { ck_assert_int_eq(data.top10[i].rx, 0); ck_assert_int_eq(data.top10[i].tx, 0); ck_assert_int_eq(data.top10[i].rxk, 0); ck_assert_int_eq(data.top10[i].txk, 0); } data.day[0].rx = data.day[0].tx = 2; data.day[0].rxk = data.day[0].txk = 2; data.day[0].date = 2; data.day[0].used = 2; rotatedays(); ck_assert_int_eq(data.top10[0].rx, 3); ck_assert_int_eq(data.top10[0].tx, 3); ck_assert_int_eq(data.top10[0].rxk, 3); ck_assert_int_eq(data.top10[0].txk, 3); ck_assert_int_eq(data.top10[1].rx, 2); ck_assert_int_eq(data.top10[1].tx, 2); ck_assert_int_eq(data.top10[1].rxk, 2); ck_assert_int_eq(data.top10[1].txk, 2); ck_assert_int_eq(data.top10[2].rx, 1); ck_assert_int_eq(data.top10[2].tx, 1); ck_assert_int_eq(data.top10[2].rxk, 1); ck_assert_int_eq(data.top10[2].txk, 1); for (i=3; i<10 ; i++) { ck_assert_int_eq(data.top10[i].rx, 0); ck_assert_int_eq(data.top10[i].tx, 0); ck_assert_int_eq(data.top10[i].rxk, 0); ck_assert_int_eq(data.top10[i].txk, 0); } } END_TEST START_TEST(rotatemonths_really_rotates_months) { int i; initdb(); for (i=0; i<12; i++) { ck_assert_int_eq(data.month[i].rx, 0); ck_assert_int_eq(data.month[i].tx, 0); ck_assert_int_eq(data.month[i].rxk, 0); ck_assert_int_eq(data.month[i].txk, 0); } data.month[0].rx = data.month[0].tx = 1; data.month[0].rxk = data.month[0].txk = 1; data.month[0].month = 1; data.month[0].used = 1; rotatemonths(); ck_assert_int_eq(data.month[0].rx, 0); ck_assert_int_eq(data.month[0].tx, 0); ck_assert_int_eq(data.month[0].rxk, 0); ck_assert_int_eq(data.month[0].txk, 0); ck_assert_int_ne(data.month[0].month, 0); ck_assert_int_eq(data.month[1].rx, 1); ck_assert_int_eq(data.month[1].tx, 1); ck_assert_int_eq(data.month[1].rxk, 1); ck_assert_int_eq(data.month[1].txk, 1); ck_assert_int_eq(data.month[1].month, 1); ck_assert_int_eq(data.month[1].used, 1); } END_TEST START_TEST(cachecount_when_empty) { ck_assert_int_eq(cachecount(), 0); ck_assert_int_eq(cacheactivecount(), 0); } END_TEST START_TEST(cacheadd_success) { ck_assert_int_eq(cacheadd("name1", 0), 1); ck_assert_int_eq(cacheadd("name1", 1), 1); ck_assert_int_eq(cacheadd("name2", 1), 1); ck_assert_int_eq(cacheadd("name2", 1), 1); } END_TEST START_TEST(cachecount_when_filled) { ck_assert_int_eq(cachecount(), 0); ck_assert_int_eq(cacheactivecount(), 0); ck_assert_int_eq(cacheadd("name1", 0), 1); ck_assert_int_eq(cachecount(), 1); ck_assert_int_eq(cacheactivecount(), 1); ck_assert_int_eq(cacheadd("name2", 0), 1); ck_assert_int_eq(cachecount(), 2); ck_assert_int_eq(cacheactivecount(), 2); ck_assert_int_eq(cacheadd("name1", 0), 1); ck_assert_int_eq(cachecount(), 2); ck_assert_int_eq(cacheactivecount(), 2); } END_TEST START_TEST(cacheupdate_when_empty) { initdb(); strcpy(data.interface, "name1"); ck_assert_int_eq(cacheupdate(), 1); ck_assert_int_eq(cachecount(), 1); ck_assert_int_eq(cacheactivecount(), 1); } END_TEST START_TEST(cacheupdate_when_filled) { initdb(); strcpy(data.interface, "name1"); ck_assert_int_eq(cacheupdate(), 1); ck_assert_int_eq(cachecount(), 1); ck_assert_int_eq(cacheactivecount(), 1); strcpy(data.interface, "name2"); ck_assert_int_eq(cacheupdate(), 1); ck_assert_int_eq(cachecount(), 2); ck_assert_int_eq(cacheactivecount(), 2); strcpy(data.interface, "name1"); data.active = 0; ck_assert_int_eq(cacheupdate(), 1); ck_assert_int_eq(cachecount(), 2); ck_assert_int_eq(cacheactivecount(), 1); } END_TEST START_TEST(cacheget_when_empty) { ck_assert_int_eq(cacheget(NULL), 0); } END_TEST START_TEST(cacheget_when_filled) { initdb(); ck_assert_int_eq(cachecount(), 0); strcpy(data.interface, "name1"); ck_assert_int_eq(cacheupdate(), 1); ck_assert_int_eq(cachecount(), 1); strcpy(data.interface, "empty"); ck_assert_int_eq(cacheget(dataptr), 1); ck_assert_str_eq(data.interface, "name1"); } END_TEST START_TEST(cacheremove_when_empty) { ck_assert_int_eq(cacheremove("does_not_exist"), NULL); } END_TEST START_TEST(cacheremove_when_filled) { initdb(); ck_assert_int_eq(cachecount(), 0); ck_assert_int_eq(cacheadd("name4", 0), 1); ck_assert_int_eq(cacheadd("name3", 0), 1); ck_assert_int_eq(cacheadd("name2", 0), 1); ck_assert_int_eq(cacheadd("name1", 0), 1); ck_assert_int_eq(cachecount(), 4); ck_assert(cacheremove("does_not_exist")==NULL); ck_assert_int_eq(cachecount(), 4); ck_assert(cacheremove("name1")!=NULL); ck_assert_int_eq(cachecount(), 3); ck_assert(cacheremove("name1")==NULL); ck_assert_int_eq(cachecount(), 3); ck_assert(cacheremove("name3")!=NULL); ck_assert_int_eq(cachecount(), 2); ck_assert(cacheremove("name4")==NULL); ck_assert_int_eq(cachecount(), 1); ck_assert(cacheremove("name2")==NULL); ck_assert_int_eq(cachecount(), 0); } END_TEST START_TEST(simplehash_with_empty_strings) { ck_assert_int_eq(simplehash(NULL, 10), 0); ck_assert_int_eq(simplehash("empty", 0), 0); } END_TEST START_TEST(simplehash_with_simple_strings) { ck_assert_int_eq(simplehash("0", 1), 49); ck_assert_int_eq(simplehash("1", 1), 50); ck_assert_int_eq(simplehash("12", 2), 101); } END_TEST START_TEST(cacheshow_empty) { suppress_output(); cacheshow(); } END_TEST START_TEST(cacheshow_filled) { initdb(); ck_assert_int_eq(cachecount(), 0); ck_assert_int_eq(cacheadd("name4", 0), 1); ck_assert_int_eq(cacheadd("name3", 0), 1); ck_assert_int_eq(cacheadd("name2", 0), 1); ck_assert_int_eq(cacheadd("name1", 0), 1); ck_assert_int_eq(cachecount(), 4); suppress_output(); cacheshow(); } END_TEST START_TEST(cachestatus_empty) { disable_logprints(); cachestatus(); } END_TEST START_TEST(cachestatus_filled) { initdb(); disable_logprints(); ck_assert_int_eq(cachecount(), 0); ck_assert_int_eq(cacheadd("name4", 0), 1); ck_assert_int_eq(cacheadd("name3", 0), 1); ck_assert_int_eq(cacheadd("name2", 0), 1); ck_assert_int_eq(cacheadd("name1", 0), 1); ck_assert_int_eq(cachecount(), 4); cachestatus(); } END_TEST START_TEST(cachestatus_full) { int i; char buffer[8]; initdb(); defaultcfg(); disable_logprints(); ck_assert_int_eq(cachecount(), 0); for (i=1; i<=50; i++) { snprintf(buffer, 8, "name%d", i); ck_assert_int_eq(cacheadd(buffer, 0), 1); ck_assert_int_eq(ibwadd(buffer, 50-i), 1); } ck_assert_int_eq(cachecount(), 50); cachestatus(); } END_TEST START_TEST(cacheflush_flushes_cache) { initdb(); disable_logprints(); ck_assert_int_eq(clean_testdbdir(), 1); ck_assert_int_eq(create_zerosize_dbfile("name1"), 1); ck_assert_int_eq(create_zerosize_dbfile("name2"), 1); ck_assert_int_eq(check_dbfile_exists("name1", 0), 1); ck_assert_int_eq(check_dbfile_exists(".name1", 0), 0); ck_assert_int_eq(check_dbfile_exists("name2", 0), 1); ck_assert_int_eq(check_dbfile_exists(".name2", 0), 0); ck_assert_int_eq(cachecount(), 0); strcpy(data.interface, "name1"); ck_assert_int_eq(cacheupdate(), 1); strcpy(data.interface, "name2"); ck_assert_int_eq(cacheupdate(), 1); ck_assert_int_eq(cacheadd("notfilled", 0), 1); ck_assert_int_eq(cachecount(), 3); ck_assert_int_eq(cacheactivecount(), 3); cacheflush(TESTDBDIR); ck_assert_int_eq(cachecount(), 0); ck_assert_int_eq(cacheactivecount(), 0); ck_assert_int_eq(check_dbfile_exists("name1", sizeof(DATA)), 1); ck_assert_int_eq(check_dbfile_exists(".name1", 0), 1); ck_assert_int_eq(check_dbfile_exists("name2", sizeof(DATA)), 1); ck_assert_int_eq(check_dbfile_exists(".name2", 0), 1); ck_assert_int_eq(check_dbfile_exists("notfilled", 0), 0); ck_assert_int_eq(check_dbfile_exists(".notfilled", 0), 0); } END_TEST START_TEST(removedb_with_existing_files) { int ret; ck_assert_int_eq(clean_testdbdir(), 1); ck_assert_int_eq(create_zerosize_dbfile("ethall"), 1); ck_assert_int_eq(create_zerosize_dbfile(".ethall"), 1); ck_assert_int_eq(check_dbfile_exists("ethall", 0), 1); ck_assert_int_eq(check_dbfile_exists(".ethall", 0), 1); /* variable needed due to bug in old versions of check framework: http://sourceforge.net/p/check/bugs/71/ */ ret = removedb("ethall", TESTDBDIR); ck_assert_int_eq(ret, 1); ck_assert_int_eq(check_dbfile_exists("ethall", 0), 0); ck_assert_int_eq(check_dbfile_exists(".ethall", 0), 0); } END_TEST START_TEST(removedb_with_nonexisting_file) { ck_assert_int_eq(clean_testdbdir(), 1); ck_assert_int_eq(create_zerosize_dbfile("ethall"), 1); ck_assert_int_eq(create_zerosize_dbfile(".ethall"), 1); ck_assert_int_eq(check_dbfile_exists("ethall", 0), 1); ck_assert_int_eq(check_dbfile_exists(".ethall", 0), 1); ck_assert_int_eq(removedb("ethnone", TESTDBDIR), 0); ck_assert_int_eq(check_dbfile_exists("ethall", 0), 1); ck_assert_int_eq(check_dbfile_exists(".ethall", 0), 1); } END_TEST START_TEST(checkdb_finds_existing_file) { ck_assert_int_eq(clean_testdbdir(), 1); ck_assert_int_eq(create_zerosize_dbfile("existingdb"), 1); ck_assert_int_eq(checkdb("existingdb", TESTDBDIR), 1); } END_TEST START_TEST(checkdb_does_not_find_nonexisting_file) { ck_assert_int_eq(clean_testdbdir(), 1); ck_assert_int_eq(checkdb("nonexistingdb", TESTDBDIR), 0); } END_TEST START_TEST(readdb_with_empty_file) { disable_logprints(); cfg.flock = 1; ck_assert_int_eq(clean_testdbdir(), 1); ck_assert_int_eq(create_zerosize_dbfile("existingdb"), 1); ck_assert_int_eq(readdb("existingdb", TESTDBDIR), -1); } END_TEST START_TEST(readdb_with_empty_file_and_backup) { disable_logprints(); cfg.flock = 1; ck_assert_int_eq(clean_testdbdir(), 1); ck_assert_int_eq(create_zerosize_dbfile("existingdb"), 1); ck_assert_int_eq(create_zerosize_dbfile(".existingdb"), 1); ck_assert_int_eq(readdb("existingdb", TESTDBDIR), -1); } END_TEST START_TEST(readdb_with_nonexisting_file) { disable_logprints(); cfg.flock = 1; strcpy(data.interface, "none"); ck_assert_int_eq(clean_testdbdir(), 1); ck_assert_int_eq(readdb("existingdb", TESTDBDIR), 1); ck_assert_str_eq(data.interface, "existingdb"); ck_assert_str_eq(data.nick, "existingdb"); } END_TEST START_TEST(readdb_with_existing_dbfile) { initdb(); disable_logprints(); cfg.flock = 1; strcpy(data.interface, "ethtest"); ck_assert_int_eq(clean_testdbdir(), 1); ck_assert_int_eq(writedb("ethtest", TESTDBDIR, 1), 1); ck_assert_int_eq(check_dbfile_exists("ethtest", sizeof(DATA)), 1); strcpy(data.interface, "none"); ck_assert_int_eq(readdb("ethtest", TESTDBDIR), 0); ck_assert_str_eq(data.interface, "ethtest"); } END_TEST START_TEST(readdb_with_existing_dbfile_and_max_name_length) { initdb(); disable_logprints(); cfg.flock = 1; strcpy(data.interface, "1234567890123456789012345678901"); ck_assert_int_eq(clean_testdbdir(), 1); ck_assert_int_eq(writedb("1234567890123456789012345678901", TESTDBDIR, 1), 1); ck_assert_int_eq(check_dbfile_exists("1234567890123456789012345678901", sizeof(DATA)), 1); strcpy(data.interface, "none"); ck_assert_int_eq(readdb("1234567890123456789012345678901", TESTDBDIR), 0); ck_assert_str_eq(data.interface, "1234567890123456789012345678901"); } END_TEST START_TEST(readdb_with_existing_dbfile_with_rename) { initdb(); disable_logprints(); cfg.flock = 1; strcpy(data.interface, "ethtest"); strcpy(data.nick, "ethtest"); ck_assert_int_eq(clean_testdbdir(), 1); ck_assert_int_eq(writedb("ethtest2", TESTDBDIR, 1), 1); ck_assert_int_eq(check_dbfile_exists("ethtest2", sizeof(DATA)), 1); strcpy(data.interface, "none"); strcpy(data.nick, "none"); ck_assert_int_eq(readdb("ethtest2", TESTDBDIR), 0); ck_assert_str_eq(data.interface, "ethtest2"); ck_assert_str_eq(data.nick, "ethtest2"); } END_TEST START_TEST(readdb_with_existing_dbfile_and_over_max_name_length) { initdb(); disable_logprints(); cfg.flock = 1; strcpy(data.interface, "dummy"); strcpy(data.nick, "dummy"); ck_assert_int_eq(clean_testdbdir(), 1); ck_assert_int_eq(writedb("1234567890123456789012345678901XX", TESTDBDIR, 1), 1); ck_assert_int_eq(check_dbfile_exists("1234567890123456789012345678901XX", sizeof(DATA)), 1); strcpy(data.interface, "none"); strcpy(data.nick, "none"); ck_assert_int_eq(readdb("1234567890123456789012345678901XX", TESTDBDIR), 0); ck_assert_str_eq(data.interface, "1234567890123456789012345678901"); ck_assert_str_eq(data.nick, "1234567890123456789012345678901"); } END_TEST START_TEST(cleartop10_clears_top10) { int i; initdb(); suppress_output(); disable_logprints(); cfg.flock = 1; strcpy(data.interface, "ethtest"); strcpy(data.nick, "ethtest"); for (i=0; i<10; i++) { data.top10[i].rx = 1; data.top10[i].tx = 1; data.top10[i].used = 1; } ck_assert_int_eq(clean_testdbdir(), 1); ck_assert_int_eq(writedb("ethtest", TESTDBDIR, 1), 1); cleartop10("ethtest", TESTDBDIR); ck_assert_int_eq(check_dbfile_exists("ethtest", sizeof(DATA)), 1); ck_assert_int_eq(check_dbfile_exists(".ethtest", sizeof(DATA)), 1); for (i=0; i<10; i++) { ck_assert_int_eq(data.top10[i].rx, 0); ck_assert_int_eq(data.top10[i].tx, 0); ck_assert_int_eq(data.top10[i].rxk, 0); ck_assert_int_eq(data.top10[i].txk, 0); ck_assert_int_eq(data.top10[i].used, 0); } } END_TEST START_TEST(cleartop10_with_nonexisting_file) { disable_logprints(); ck_assert_int_eq(clean_testdbdir(), 1); cleartop10("ethtest", TESTDBDIR); } END_TEST START_TEST(rebuilddbtotal_rebuilds_total) { int i; initdb(); suppress_output(); disable_logprints(); cfg.flock = 1; strcpy(data.interface, "ethtest"); strcpy(data.nick, "ethtest"); data.totalrx = 0; data.totaltx = 0; for (i=0; i<12; i++) { data.month[i].rx = 1; data.month[i].tx = 2; data.month[i].used = 1; } ck_assert_int_eq(clean_testdbdir(), 1); ck_assert_int_eq(writedb("ethtest", TESTDBDIR, 1), 1); rebuilddbtotal("ethtest", TESTDBDIR); ck_assert_int_eq(check_dbfile_exists("ethtest", sizeof(DATA)), 1); ck_assert_int_eq(check_dbfile_exists(".ethtest", sizeof(DATA)), 1); ck_assert_int_eq(data.totalrx, 12); ck_assert_int_eq(data.totaltx, 24); } END_TEST START_TEST(rebuilddbtotal_with_nonexisting_file) { disable_logprints(); ck_assert_int_eq(clean_testdbdir(), 1); rebuilddbtotal("ethtest", TESTDBDIR); } END_TEST START_TEST(validatedb_with_initdb) { initdb(); strcpy(data.interface, "ethtest"); ck_assert_int_eq(validatedb(), 1); } END_TEST START_TEST(validatedb_with_invalid_totals) { initdb(); suppress_output(); strcpy(data.interface, "ethtest"); data.day[0].rx++; ck_assert_int_eq(validatedb(), 0); } END_TEST START_TEST(validatedb_with_top10_use) { initdb(); suppress_output(); strcpy(data.interface, "ethtest"); data.top10[0].used = 1; data.top10[1].used = 1; data.top10[2].used = 1; data.top10[5].used = 1; ck_assert_int_eq(validatedb(), 0); } END_TEST START_TEST(dbcheck_with_no_interfaces) { int forcesave = 0; linuxonly; ck_assert_int_eq(remove_directory(TESTDIR), 1); ck_assert_int_eq(dbcheck(0, &forcesave), 0); ck_assert_int_eq(forcesave, 0); } END_TEST START_TEST(dbcheck_with_empty_cache) { int forcesave = 0; linuxonly; ck_assert_int_eq(remove_directory(TESTDIR), 1); fake_proc_net_dev("w", "ethsomething", 1, 2, 3, 4); fake_proc_net_dev("a", "ethelse", 5, 6, 7, 8); ck_assert_int_ne(dbcheck(0, &forcesave), 0); ck_assert_int_eq(forcesave, 0); } END_TEST START_TEST(dbcheck_with_no_changes_in_iflist) { int forcesave = 0; uint32_t ifhash; char *ifacelist; linuxonly; ck_assert_int_eq(remove_directory(TESTDIR), 1); fake_proc_net_dev("w", "ethsomething", 1, 2, 3, 4); fake_proc_net_dev("a", "ethelse", 5, 6, 7, 8); ck_assert_int_ne(getiflist(&ifacelist, 0), 0); ifhash = simplehash(ifacelist, (int)strlen(ifacelist)); ck_assert_int_eq(dbcheck(ifhash, &forcesave), ifhash); ck_assert_int_eq(forcesave, 0); } END_TEST START_TEST(dbcheck_with_filled_cache) { int forcesave = 0; linuxonly; initdb(); defaultcfg(); disable_logprints(); ck_assert_int_eq(remove_directory(TESTDIR), 1); ck_assert_int_eq(cachecount(), 0); strcpy(data.interface, "ethbasic"); ck_assert_int_eq(cacheupdate(), 1); strcpy(data.interface, "ethactive"); ck_assert_int_eq(cacheupdate(), 1); strcpy(data.interface, "ethnotactive"); data.active = 0; ck_assert_int_eq(cacheupdate(), 1); ck_assert_int_eq(cachecount(), 3); ck_assert_int_eq(cacheactivecount(), 2); fake_proc_net_dev("w", "ethbasic", 1, 2, 3, 4); fake_proc_net_dev("a", "ethnotactive", 5, 6, 7, 8); ck_assert_int_ne(dbcheck(0, &forcesave), 0); ck_assert_int_eq(forcesave, 1); } END_TEST START_TEST(importdb_can_parse_exported_database) { int i; char buffer[512]; DATA filleddb; FILE *exportfile; initdb(); strcpy(data.interface, "something"); strcpy(data.nick, "nothing"); data.totalrx = 1; data.totaltx = 2; data.currx = 3; data.curtx = 4; data.totalrxk = 5; data.totaltxk = 6; data.btime = 7; for (i=0; i<30; i++) { data.day[i].date = i+1; data.day[i].rx = data.day[i].tx = i*100; data.day[i].rxk = data.day[i].txk = i; data.day[i].used = 1; } for (i=0; i<10; i++) { data.top10[i].date = i+1; data.top10[i].rx = data.top10[i].tx = i*100; data.top10[i].rxk = data.top10[i].txk = i; data.top10[i].used = 1; } for (i=0; i<12; i++) { data.month[i].month = i+1; data.month[i].rx = data.month[i].tx = i*100; data.month[i].rxk = data.month[i].txk = i; data.month[i].used = 1; } for (i=0; i<24; i++) { data.hour[i].date = i+1; data.hour[i].rx = data.hour[i].tx = i*100; } memcpy(&filleddb, &data, sizeof(DATA)); ck_assert_int_eq(remove_directory(TESTDIR), 1); ck_assert_int_eq(clean_testdbdir(), 1); fflush(stdout); snprintf(buffer, 512, "%s/dbexport", TESTDBDIR); exportfile = fopen(buffer, "w"); dup2(fileno(exportfile), STDOUT_FILENO); fclose(exportfile); exportdb(); fflush(stdout); memset(&data, '\0', sizeof(DATA)); ck_assert_int_gt(importdb(buffer), 0); ck_assert_str_eq(data.interface, filleddb.interface); ck_assert_str_eq(data.nick, filleddb.nick); ck_assert_int_eq(data.version, filleddb.version); ck_assert_int_eq(data.active, filleddb.active); ck_assert_int_eq(data.totalrx, filleddb.totalrx); ck_assert_int_eq(data.totaltx, filleddb.totaltx); ck_assert_int_eq(data.currx, filleddb.currx); ck_assert_int_eq(data.curtx, filleddb.curtx); ck_assert_int_eq(data.totalrxk, filleddb.totalrxk); ck_assert_int_eq(data.totaltxk, filleddb.totaltxk); ck_assert_int_eq(data.btime, filleddb.btime); ck_assert_int_eq(data.created, filleddb.created); ck_assert_int_eq(data.lastupdated, filleddb.lastupdated); for (i=0; i<30; i++) { ck_assert_int_eq(data.day[i].date, filleddb.day[i].date); ck_assert_int_eq(data.day[i].rx, filleddb.day[i].rx); ck_assert_int_eq(data.day[i].tx, filleddb.day[i].tx); ck_assert_int_eq(data.day[i].rxk, filleddb.day[i].rxk); ck_assert_int_eq(data.day[i].txk, filleddb.day[i].txk); ck_assert_int_eq(data.day[i].used, filleddb.day[i].used); } for (i=0; i<10; i++) { ck_assert_int_eq(data.top10[i].date, filleddb.top10[i].date); ck_assert_int_eq(data.top10[i].rx, filleddb.top10[i].rx); ck_assert_int_eq(data.top10[i].tx, filleddb.top10[i].tx); ck_assert_int_eq(data.top10[i].rxk, filleddb.top10[i].rxk); ck_assert_int_eq(data.top10[i].txk, filleddb.top10[i].txk); ck_assert_int_eq(data.top10[i].used, filleddb.top10[i].used); } for (i=0; i<12; i++) { ck_assert_int_eq(data.month[i].month, filleddb.month[i].month); ck_assert_int_eq(data.month[i].rx, filleddb.month[i].rx); ck_assert_int_eq(data.month[i].tx, filleddb.month[i].tx); ck_assert_int_eq(data.month[i].rxk, filleddb.month[i].rxk); ck_assert_int_eq(data.month[i].txk, filleddb.month[i].txk); ck_assert_int_eq(data.month[i].used, filleddb.month[i].used); } for (i=0; i<24; i++) { ck_assert_int_eq(data.hour[i].date, filleddb.hour[i].date); ck_assert_int_eq(data.hour[i].rx, filleddb.hour[i].rx); ck_assert_int_eq(data.hour[i].tx, filleddb.hour[i].tx); } } END_TEST START_TEST(database_outputs_do_not_crash) { int i; initdb(); defaultcfg(); strcpy(data.interface, "something"); strcpy(data.nick, "nothing"); data.totalrx = 1; data.totaltx = 2; data.currx = 3; data.curtx = 4; data.totalrxk = 5; data.totaltxk = 6; data.btime = 7; for (i=0; i<30; i++) { data.day[i].date = i+1; data.day[i].rx = data.day[i].tx = i*100; data.day[i].rxk = data.day[i].txk = i; data.day[i].used = 1; } for (i=0; i<10; i++) { data.top10[i].date = i+1; data.top10[i].rx = data.top10[i].tx = i*100; data.top10[i].rxk = data.top10[i].txk = i; data.top10[i].used = 1; } for (i=0; i<12; i++) { data.month[i].month = i+1; data.month[i].rx = data.month[i].tx = i*100; data.month[i].rxk = data.month[i].txk = i; data.month[i].used = 1; } for (i=0; i<24; i++) { data.hour[i].date = i+1; data.hour[i].rx = data.hour[i].tx = i*100; } suppress_output(); showdb(0); showdb(1); showdb(2); showdb(3); showdb(4); showdb(5); showdb(6); showdb(7); showdb(8); showdb(9); } END_TEST START_TEST(showbar_with_zero_len_is_nothing) { int len; suppress_output(); len = showbar(1, 0, 2, 0, 3, 0); ck_assert_int_eq(len, 0); } END_TEST START_TEST(showbar_with_big_max_and_small_numbers) { int len; suppress_output(); len = showbar(0, 1, 0, 2, 1000, 10); ck_assert_int_eq(len, 0); } END_TEST START_TEST(showbar_with_all_rx) { int pipe, len; char buffer[512]; memset(&buffer, '\0', sizeof(buffer)); defaultcfg(); cfg.rxchar[0] = 'r'; cfg.txchar[0] = 't'; pipe = pipe_output(); len = showbar(0, 1, 0, 0, 1, 10); ck_assert_int_eq(len, 10); fflush(stdout); read(pipe, buffer, 512); ck_assert_str_eq(buffer, " rrrrrrrrrr"); } END_TEST START_TEST(showbar_with_all_tx) { int pipe, len; char buffer[512]; memset(&buffer, '\0', sizeof(buffer)); defaultcfg(); cfg.rxchar[0] = 'r'; cfg.txchar[0] = 't'; pipe = pipe_output(); len = showbar(0, 0, 0, 1, 1, 10); ck_assert_int_eq(len, 10); fflush(stdout); read(pipe, buffer, 512); ck_assert_str_eq(buffer, " tttttttttt"); } END_TEST START_TEST(showbar_with_half_and_half) { int pipe, len; char buffer[512]; memset(&buffer, '\0', sizeof(buffer)); defaultcfg(); cfg.rxchar[0] = 'r'; cfg.txchar[0] = 't'; pipe = pipe_output(); len = showbar(0, 1, 0, 1, 2, 10); ck_assert_int_eq(len, 10); fflush(stdout); read(pipe, buffer, 512); ck_assert_str_eq(buffer, " rrrrrttttt"); } END_TEST START_TEST(showbar_with_one_tenth) { int pipe, len; char buffer[512]; memset(&buffer, '\0', sizeof(buffer)); defaultcfg(); cfg.rxchar[0] = 'r'; cfg.txchar[0] = 't'; pipe = pipe_output(); len = showbar(0, 1, 0, 9, 10, 10); ck_assert_int_eq(len, 10); fflush(stdout); read(pipe, buffer, 512); ck_assert_str_eq(buffer, " rttttttttt"); } END_TEST START_TEST(showbar_with_small_rx_shows_all_tx) { int pipe, len; char buffer[512]; memset(&buffer, '\0', sizeof(buffer)); defaultcfg(); cfg.rxchar[0] = 'r'; cfg.txchar[0] = 't'; pipe = pipe_output(); len = showbar(0, 1, 0, 1000, 1001, 10); ck_assert_int_eq(len, 10); fflush(stdout); read(pipe, buffer, 512); ck_assert_str_eq(buffer, " tttttttttt"); } END_TEST START_TEST(showbar_with_max_smaller_than_real_max) { int len; suppress_output(); len = showbar(0, 1, 0, 2, 1, 10); ck_assert_int_eq(len, 0); } END_TEST START_TEST(showbar_can_also_do_mb_calculations) { int pipe, len; char buffer[512]; memset(&buffer, '\0', sizeof(buffer)); defaultcfg(); cfg.rxchar[0] = 'r'; cfg.txchar[0] = 't'; pipe = pipe_output(); len = showbar(0, 1024, 0, 1024, 2048, 2); ck_assert_int_eq(len, 2); fflush(stdout); read(pipe, buffer, 512); ck_assert_str_eq(buffer, " rt"); } END_TEST void add_database_tests(Suite *s) { TCase *tc_db = tcase_create("Database"); tcase_add_test(tc_db, initdb_activates_database); tcase_add_test(tc_db, cleanhours_really_cleans_hours); tcase_add_test(tc_db, cleanhours_does_not_remove_current_data); tcase_add_test(tc_db, rotatedays_really_rotates_days); tcase_add_test(tc_db, rotatedays_updates_top10); tcase_add_test(tc_db, rotatemonths_really_rotates_months); tcase_add_test(tc_db, cachecount_when_empty); tcase_add_test(tc_db, cacheadd_success); tcase_add_test(tc_db, cachecount_when_filled); tcase_add_test(tc_db, cacheupdate_when_empty); tcase_add_test(tc_db, cacheupdate_when_filled); tcase_add_test(tc_db, cacheget_when_empty); tcase_add_test(tc_db, cacheget_when_filled); tcase_add_test(tc_db, cacheremove_when_empty); tcase_add_test(tc_db, cacheremove_when_filled); tcase_add_test(tc_db, simplehash_with_empty_strings); tcase_add_test(tc_db, simplehash_with_simple_strings); tcase_add_test(tc_db, cacheshow_empty); tcase_add_test(tc_db, cacheshow_filled); tcase_add_test(tc_db, cachestatus_empty); tcase_add_test(tc_db, cachestatus_filled); tcase_add_test(tc_db, cachestatus_full); tcase_add_test(tc_db, cacheflush_flushes_cache); tcase_add_test(tc_db, removedb_with_existing_files); tcase_add_test(tc_db, removedb_with_nonexisting_file); tcase_add_test(tc_db, checkdb_finds_existing_file); tcase_add_test(tc_db, checkdb_does_not_find_nonexisting_file); tcase_add_test(tc_db, readdb_with_empty_file); tcase_add_test(tc_db, readdb_with_empty_file_and_backup); tcase_add_test(tc_db, readdb_with_nonexisting_file); tcase_add_test(tc_db, readdb_with_existing_dbfile); tcase_add_test(tc_db, readdb_with_existing_dbfile_and_max_name_length); tcase_add_test(tc_db, readdb_with_existing_dbfile_with_rename); tcase_add_test(tc_db, readdb_with_existing_dbfile_and_over_max_name_length); tcase_add_test(tc_db, cleartop10_clears_top10); tcase_add_exit_test(tc_db, cleartop10_with_nonexisting_file, 1); tcase_add_test(tc_db, rebuilddbtotal_rebuilds_total); tcase_add_exit_test(tc_db, rebuilddbtotal_with_nonexisting_file, 1); tcase_add_test(tc_db, validatedb_with_initdb); tcase_add_test(tc_db, validatedb_with_invalid_totals); tcase_add_test(tc_db, validatedb_with_top10_use); tcase_add_test(tc_db, dbcheck_with_no_interfaces); tcase_add_test(tc_db, dbcheck_with_empty_cache); tcase_add_test(tc_db, dbcheck_with_no_changes_in_iflist); tcase_add_test(tc_db, dbcheck_with_filled_cache); tcase_add_test(tc_db, importdb_can_parse_exported_database); tcase_add_test(tc_db, database_outputs_do_not_crash); tcase_add_test(tc_db, showbar_with_zero_len_is_nothing); tcase_add_test(tc_db, showbar_with_big_max_and_small_numbers); tcase_add_test(tc_db, showbar_with_all_rx); tcase_add_test(tc_db, showbar_with_all_tx); tcase_add_test(tc_db, showbar_with_half_and_half); tcase_add_test(tc_db, showbar_with_one_tenth); tcase_add_test(tc_db, showbar_with_small_rx_shows_all_tx); tcase_add_test(tc_db, showbar_with_max_smaller_than_real_max); tcase_add_test(tc_db, showbar_can_also_do_mb_calculations); suite_add_tcase(s, tc_db); } vnstat-1.14/tests/ifinfo_tests.h0000644000000000000000000000013012317006312015450 0ustar rootroot#ifndef IFINFO_TESTS_H #define IFINFO_TESTS_H void add_ifinfo_tests(Suite *s); #endif vnstat-1.14/tests/common_tests.h0000644000000000000000000000013012317006311015465 0ustar rootroot#ifndef COMMON_TESTS_H #define COMMON_TESTS_H void add_common_tests(Suite *s); #endif vnstat-1.14/tests/misc_tests.c0000644000000000000000000002465212403124234015143 0ustar rootroot#include "vnstat_tests.h" #include "misc_tests.h" #include "common.h" #include "misc.h" START_TEST(getbtime_does_not_return_zero) { ck_assert_int_gt(getbtime(), 0); } END_TEST START_TEST(getunit_returns_something_with_all_cfg_combinations) { char *string; int j; cfg.unit = _i; for (j=1; j<=(UNITCOUNT+1); j++) { string = getunit(j); ck_assert_int_gt(strlen(string), 0); } } END_TEST START_TEST(getrateunit_returns_something_with_all_cfg_combinations) { char *string; int j; for (j=1; j<=(UNITCOUNT+1); j++) { string = getrateunit(_i, j); ck_assert_int_gt(strlen(string), 0); } } END_TEST START_TEST(getunitdivider_returns_something_with_all_cfg_combinations) { int j; for (j=1; j<=(UNITCOUNT+1); j++) { if (j==1 || j>UNITCOUNT) { ck_assert_int_eq(getunitdivider(_i, j), 0); } else { ck_assert_int_ne(getunitdivider(_i, j), 0); } } } END_TEST START_TEST(spacecheck_does_not_check_when_not_configured) { cfg.spacecheck = 0; ck_assert_int_eq(spacecheck("/nonexistentpath"), 1); } END_TEST START_TEST(spacecheck_checks_space) { cfg.spacecheck = 1; /* it's assumed that /tmp isn't full */ ck_assert_int_eq(spacecheck("/tmp"), 1); } END_TEST START_TEST(spacecheck_fails_with_invalid_path) { noexit = 1; cfg.spacecheck = 1; ck_assert_int_eq(spacecheck("/nonexistentpath"), 0); } END_TEST START_TEST(getvalue_normal) { cfg.unit = 0; ck_assert_str_eq(getvalue(0, 100, 0, 1), "100 KiB"); ck_assert_str_eq(getvalue(1, 0, 0, 1), "1.00 MiB"); ck_assert_str_eq(getvalue(1024, 0, 0, 1), "1.00 GiB"); ck_assert_str_eq(getvalue(1048576, 0, 0, 2), "1.00 TiB"); cfg.unit = 1; ck_assert_str_eq(getvalue(0, 100, 0, 1), "100 KB"); ck_assert_str_eq(getvalue(1, 0, 0, 1), "1.00 MB"); ck_assert_str_eq(getvalue(1024, 0, 0, 1), "1.00 GB"); ck_assert_str_eq(getvalue(1048576, 0, 0, 2), "1.00 TB"); } END_TEST START_TEST(getvalue_estimate) { cfg.unit = 0; ck_assert_str_eq(getvalue(0, 100, 0, 2), "100 KiB"); ck_assert_str_eq(getvalue(1, 0, 0, 2), "1 MiB"); ck_assert_str_eq(getvalue(1024, 0, 0, 2), "1.00 GiB"); ck_assert_str_eq(getvalue(1048576, 0, 0, 2), "1.00 TiB"); cfg.unit = 1; ck_assert_str_eq(getvalue(0, 100, 0, 2), "100 KB"); ck_assert_str_eq(getvalue(1, 0, 0, 2), "1 MB"); ck_assert_str_eq(getvalue(1024, 0, 0, 2), "1.00 GB"); ck_assert_str_eq(getvalue(1048576, 0, 0, 2), "1.00 TB"); } END_TEST START_TEST(getvalue_imagescale) { cfg.unit = 0; ck_assert_str_eq(getvalue(0, 100, 0, 3), "100 KiB"); ck_assert_str_eq(getvalue(1, 0, 0, 3), "1 MiB"); ck_assert_str_eq(getvalue(1024, 0, 0, 3), "1 GiB"); ck_assert_str_eq(getvalue(1048576, 0, 0, 3), "1 TiB"); cfg.unit = 1; ck_assert_str_eq(getvalue(0, 100, 0, 3), "100 KB"); ck_assert_str_eq(getvalue(1, 0, 0, 3), "1 MB"); ck_assert_str_eq(getvalue(1024, 0, 0, 3), "1 GB"); ck_assert_str_eq(getvalue(1048576, 0, 0, 3), "1 TB"); } END_TEST START_TEST(getvalue_padding) { cfg.unit = 0; ck_assert_str_eq(getvalue(0, 100, 10, 1), " 100 KiB"); cfg.unit = 1; ck_assert_str_eq(getvalue(0, 100, 10, 1), " 100 KB"); } END_TEST START_TEST(getvalue_mb_kb_mixed) { cfg.unit = 0; ck_assert_str_eq(getvalue(1, 3210, 0, 1), "4.13 MiB"); cfg.unit = 1; ck_assert_str_eq(getvalue(1, 3210, 0, 1), "4.13 MB"); } END_TEST START_TEST(getvalue_zero_values) { cfg.unit = 0; ck_assert_str_eq(getvalue(0, 0, 0, 1), "0 KiB"); ck_assert_str_eq(getvalue(0, 0, 0, 2), "-- "); ck_assert_str_eq(getvalue(0, 0, 0, 3), "0 KiB"); cfg.unit = 1; ck_assert_str_eq(getvalue(0, 0, 0, 1), "0 KB"); ck_assert_str_eq(getvalue(0, 0, 0, 2), "-- "); ck_assert_str_eq(getvalue(0, 0, 0, 3), "0 KB"); } END_TEST START_TEST(getrate_zero_interval) { int i, j; for (i=0; i<=1; i++) { cfg.rateunit = i; for (j=0; j<=2; j++) { cfg.unit = j; ck_assert_str_eq(getrate(1, 0, 0, 0), "n/a"); } } } END_TEST START_TEST(getrate_bytes) { cfg.rateunit = 0; cfg.unit = 0; ck_assert_str_eq(getrate(0, 100, 1, 0), "100.00 KiB/s"); ck_assert_str_eq(getrate(1, 3210, 1, 0), "4.13 MiB/s"); ck_assert_str_eq(getrate(1024, 0, 1, 0), "1.00 GiB/s"); ck_assert_str_eq(getrate(1048576, 0, 1, 0), "1.00 TiB/s"); cfg.unit = 1; ck_assert_str_eq(getrate(0, 100, 1, 0), "100.00 KB/s"); ck_assert_str_eq(getrate(1, 3210, 1, 0), "4.13 MB/s"); ck_assert_str_eq(getrate(1024, 0, 1, 0), "1.00 GB/s"); ck_assert_str_eq(getrate(1048576, 0, 1, 0), "1.00 TB/s"); } END_TEST START_TEST(getrate_bits) { cfg.rateunit = 1; cfg.unit = 0; ck_assert_str_eq(getrate(0, 100, 1, 0), "800 kbit/s"); ck_assert_str_eq(getrate(1, 3210, 1, 0), "33.87 Mbit/s"); ck_assert_str_eq(getrate(1024, 0, 1, 0), "8.39 Gbit/s"); ck_assert_str_eq(getrate(1048576, 0, 1, 0), "8.59 Tbit/s"); cfg.unit = 1; ck_assert_str_eq(getrate(0, 100, 1, 0), "800 kbit/s"); ck_assert_str_eq(getrate(1, 3210, 1, 0), "33.87 Mbit/s"); ck_assert_str_eq(getrate(1024, 0, 1, 0), "8.39 Gbit/s"); ck_assert_str_eq(getrate(1048576, 0, 1, 0), "8.59 Tbit/s"); } END_TEST START_TEST(getrate_interval_divides) { cfg.unit = 0; cfg.rateunit = 0; ck_assert_str_eq(getrate(0, 100, 1, 0), "100.00 KiB/s"); ck_assert_str_eq(getrate(0, 100, 2, 0), "50.00 KiB/s"); ck_assert_str_eq(getrate(0, 100, 10, 0), "10.00 KiB/s"); cfg.rateunit = 1; ck_assert_str_eq(getrate(0, 100, 1, 0), "800 kbit/s"); ck_assert_str_eq(getrate(0, 100, 2, 0), "400 kbit/s"); ck_assert_str_eq(getrate(0, 100, 10, 0), "80.00 kbit/s"); } END_TEST START_TEST(getrate_padding) { cfg.unit = 0; cfg.rateunit = 0; ck_assert_str_eq(getrate(0, 100, 1, 0), "100.00 KiB/s"); ck_assert_str_eq(getrate(0, 100, 1, 12), "100.00 KiB/s"); ck_assert_str_eq(getrate(0, 100, 1, 14), " 100.00 KiB/s"); } END_TEST START_TEST(gettrafficrate_zero_interval) { int i, j; for (i=0; i<=1; i++) { cfg.rateunit = i; for (j=0; j<=2; j++) { cfg.unit = j; ck_assert_str_eq(gettrafficrate(1, 0, 0), "n/a"); } } } END_TEST START_TEST(gettrafficrate_bytes) { cfg.rateunit = 0; cfg.unit = 0; ck_assert_str_eq(gettrafficrate(102400, 1, 0), "100.00 KiB/s"); ck_assert_str_eq(gettrafficrate(1048576, 1, 0), "1.00 MiB/s"); ck_assert_str_eq(gettrafficrate(1073741824, 1, 0), "1.00 GiB/s"); ck_assert_str_eq(gettrafficrate(1099511627776ULL, 1, 0), "1.00 TiB/s"); cfg.unit = 1; ck_assert_str_eq(gettrafficrate(102400, 1, 0), "100.00 KB/s"); ck_assert_str_eq(gettrafficrate(1048576, 1, 0), "1.00 MB/s"); ck_assert_str_eq(gettrafficrate(1073741824, 1, 0), "1.00 GB/s"); ck_assert_str_eq(gettrafficrate(1099511627776ULL, 1, 0), "1.00 TB/s"); } END_TEST START_TEST(gettrafficrate_bits) { cfg.rateunit = 1; cfg.unit = 0; ck_assert_str_eq(gettrafficrate(102400, 1, 0), "800 kbit/s"); ck_assert_str_eq(gettrafficrate(1048576, 1, 0), "8.19 Mbit/s"); ck_assert_str_eq(gettrafficrate(1073741824, 1, 0), "8.39 Gbit/s"); ck_assert_str_eq(gettrafficrate(1099511627776ULL, 1, 0), "8.59 Tbit/s"); cfg.unit = 1; ck_assert_str_eq(gettrafficrate(102400, 1, 0), "800 kbit/s"); ck_assert_str_eq(gettrafficrate(1048576, 1, 0), "8.19 Mbit/s"); ck_assert_str_eq(gettrafficrate(1073741824, 1, 0), "8.39 Gbit/s"); ck_assert_str_eq(gettrafficrate(1099511627776ULL, 1, 0), "8.59 Tbit/s"); } END_TEST START_TEST(gettrafficrate_interval_divides) { cfg.unit = 0; cfg.rateunit = 0; ck_assert_str_eq(gettrafficrate(102400, 1, 0), "100.00 KiB/s"); ck_assert_str_eq(gettrafficrate(102400, 2, 0), "50.00 KiB/s"); ck_assert_str_eq(gettrafficrate(102400, 10, 0), "10.00 KiB/s"); cfg.rateunit = 1; ck_assert_str_eq(gettrafficrate(102400, 1, 0), "800 kbit/s"); ck_assert_str_eq(gettrafficrate(102400, 2, 0), "400 kbit/s"); ck_assert_str_eq(gettrafficrate(102400, 10, 0), "80.00 kbit/s"); } END_TEST START_TEST(gettrafficrate_padding) { cfg.unit = 0; cfg.rateunit = 0; ck_assert_str_eq(gettrafficrate(102400, 1, 0), "100.00 KiB/s"); ck_assert_str_eq(gettrafficrate(102400, 1, 12), "100.00 KiB/s"); ck_assert_str_eq(gettrafficrate(102400, 1, 14), " 100.00 KiB/s"); } END_TEST START_TEST(getscale_zero) { ck_assert_int_eq(getscale(0), 1); } END_TEST START_TEST(getscale_nonzero) { ck_assert_int_eq(getscale(1), 1); ck_assert_int_eq(getscale(2), 1); ck_assert_int_eq(getscale(10), 2); ck_assert_int_eq(getscale(20), 5); ck_assert_int_eq(getscale(50), 20); ck_assert_int_eq(getscale(1023), 300); ck_assert_int_eq(getscale(1024), 300); ck_assert_int_eq(getscale(1025), 1024); ck_assert_int_eq(getscale(1026), 1024); ck_assert_int_eq(getscale(1500), 1024); ck_assert_int_eq(getscale(2047), 1024); ck_assert_int_eq(getscale(2048), 1024); ck_assert_int_eq(getscale(2049), 1024); ck_assert_int_eq(getscale(8191), 1024); ck_assert_int_eq(getscale(8192), 2048); ck_assert_int_eq(getscale(8193), 2048); } END_TEST START_TEST(sighandler_sets_signal) { debug = 1; intsignal = 0; disable_logprints(); ck_assert_int_ne(signal(SIGINT, sighandler), SIG_ERR); ck_assert_int_ne(signal(SIGHUP, sighandler), SIG_ERR); ck_assert_int_ne(signal(SIGTERM, sighandler), SIG_ERR); ck_assert_int_eq(kill(getpid(), SIGINT), 0); ck_assert_int_eq(intsignal, SIGINT); ck_assert_int_eq(kill(getpid(), SIGHUP), 0); ck_assert_int_eq(intsignal, SIGHUP); ck_assert_int_eq(kill(getpid(), SIGTERM), 0); ck_assert_int_eq(intsignal, SIGTERM); } END_TEST void add_misc_tests(Suite *s) { TCase *tc_misc = tcase_create("Misc"); tcase_add_test(tc_misc, getbtime_does_not_return_zero); tcase_add_loop_test(tc_misc, getunit_returns_something_with_all_cfg_combinations, 0, 2); tcase_add_loop_test(tc_misc, getrateunit_returns_something_with_all_cfg_combinations, 0, 3); tcase_add_loop_test(tc_misc, getunitdivider_returns_something_with_all_cfg_combinations, 0, 3); tcase_add_test(tc_misc, spacecheck_does_not_check_when_not_configured); tcase_add_test(tc_misc, spacecheck_checks_space); tcase_add_test(tc_misc, spacecheck_fails_with_invalid_path); tcase_add_test(tc_misc, getvalue_normal); tcase_add_test(tc_misc, getvalue_estimate); tcase_add_test(tc_misc, getvalue_imagescale); tcase_add_test(tc_misc, getvalue_padding); tcase_add_test(tc_misc, getvalue_mb_kb_mixed); tcase_add_test(tc_misc, getvalue_zero_values); tcase_add_test(tc_misc, getrate_zero_interval); tcase_add_test(tc_misc, getrate_bytes); tcase_add_test(tc_misc, getrate_bits); tcase_add_test(tc_misc, getrate_interval_divides); tcase_add_test(tc_misc, getrate_padding); tcase_add_test(tc_misc, gettrafficrate_zero_interval); tcase_add_test(tc_misc, gettrafficrate_bytes); tcase_add_test(tc_misc, gettrafficrate_bits); tcase_add_test(tc_misc, gettrafficrate_interval_divides); tcase_add_test(tc_misc, gettrafficrate_padding); tcase_add_test(tc_misc, getscale_zero); tcase_add_test(tc_misc, getscale_nonzero); tcase_add_test(tc_misc, sighandler_sets_signal); suite_add_tcase(s, tc_misc); } vnstat-1.14/tests/daemon_tests.c0000644000000000000000000005525612403124201015451 0ustar rootroot#include "vnstat_tests.h" #include "daemon_tests.h" #include "common.h" #include "dbaccess.h" #include "dbcache.h" #include "cfg.h" #include "daemon.h" START_TEST(getuser_root_string) { ck_assert_int_eq((int)getuser("root"), 0); } END_TEST START_TEST(getuser_root_numeric) { ck_assert_int_eq((int)getuser("0"), 0); } END_TEST START_TEST(getuser_no_such_user_string) { suppress_output(); getuser("reallynosuchuser"); } END_TEST START_TEST(getuser_no_such_user_numeric) { suppress_output(); getuser("99999999"); } END_TEST START_TEST(getgroup_root_string) { #if defined(__linux__) || defined(__GNU__) || defined(__GLIBC__) ck_assert_int_eq((int)getgroup("root"), 0); #else ck_assert_int_eq((int)getgroup("wheel"), 0); #endif } END_TEST START_TEST(getgroup_root_numeric) { ck_assert_int_eq((int)getgroup("0"), 0); } END_TEST START_TEST(getgroup_no_such_user_string) { suppress_output(); getgroup("reallynosuchgroup"); } END_TEST START_TEST(getgroup_no_such_user_numeric) { suppress_output(); getgroup("99999999"); } END_TEST START_TEST(debugtimestamp_does_not_exit) { suppress_output(); debugtimestamp(); } END_TEST START_TEST(addinterfaces_does_nothing_with_no_files) { linuxonly; defaultcfg(); suppress_output(); ck_assert_int_eq(remove_directory(TESTDIR), 1); ck_assert_int_eq(clean_testdbdir(), 1); ck_assert_int_eq(addinterfaces(TESTDBDIR), 0); } END_TEST START_TEST(addinterfaces_adds_interfaces) { linuxonly; defaultcfg(); suppress_output(); ck_assert_int_eq(remove_directory(TESTDIR), 1); ck_assert_int_eq(clean_testdbdir(), 1); fake_proc_net_dev("w", "ethone", 1, 2, 3, 4); fake_proc_net_dev("a", "lo0", 0, 0, 0, 0); fake_proc_net_dev("a", "ethtwo", 5, 6, 7, 8); fake_proc_net_dev("a", "sit0", 0, 0, 0, 0); ck_assert_int_eq(addinterfaces(TESTDBDIR), 2); ck_assert_int_eq(check_dbfile_exists("ethone", sizeof(DATA)), 1); ck_assert_int_eq(check_dbfile_exists(".ethone", sizeof(DATA)), 0); ck_assert_int_eq(check_dbfile_exists("ethtwo", sizeof(DATA)), 1); ck_assert_int_eq(check_dbfile_exists(".ethtwo", sizeof(DATA)), 0); } END_TEST START_TEST(initdstate_does_not_crash) { DSTATE s; defaultcfg(); initdstate(&s); } END_TEST START_TEST(preparedatabases_exits_with_no_database_dir) { DSTATE s; linuxonly_exit; defaultcfg(); initdstate(&s); suppress_output(); preparedatabases(&s); } END_TEST START_TEST(preparedatabases_exits_with_no_databases) { DSTATE s; linuxonly_exit; defaultcfg(); initdstate(&s); suppress_output(); ck_assert_int_eq(clean_testdbdir(), 1); preparedatabases(&s); } END_TEST START_TEST(preparedatabases_with_no_databases_creates_databases) { DSTATE s; linuxonly; defaultcfg(); initdstate(&s); suppress_output(); strncpy_nt(s.dirname, TESTDBDIR, 512); ck_assert_int_eq(remove_directory(TESTDIR), 1); ck_assert_int_eq(clean_testdbdir(), 1); fake_proc_net_dev("w", "ethone", 1, 2, 3, 4); fake_proc_net_dev("a", "lo0", 0, 0, 0, 0); fake_proc_net_dev("a", "ethtwo", 5, 6, 7, 8); fake_proc_net_dev("a", "sit0", 0, 0, 0, 0); preparedatabases(&s); ck_assert_int_eq(check_dbfile_exists("ethone", sizeof(DATA)), 1); ck_assert_int_eq(check_dbfile_exists(".ethone", sizeof(DATA)), 0); ck_assert_int_eq(check_dbfile_exists("ethtwo", sizeof(DATA)), 1); ck_assert_int_eq(check_dbfile_exists(".ethtwo", sizeof(DATA)), 0); } END_TEST START_TEST(setsignaltraps_does_not_exit) { intsignal = 1; setsignaltraps(); ck_assert_int_eq(intsignal, 0); } END_TEST START_TEST(filldatabaselist_exits_with_no_database_dir) { DSTATE s; defaultcfg(); initdstate(&s); disable_logprints(); strncpy_nt(s.dirname, TESTDBDIR, 512); ck_assert_int_eq(remove_directory(TESTDIR), 1); filldatabaselist(&s); } END_TEST START_TEST(filldatabaselist_does_not_exit_with_empty_database_dir) { DSTATE s; defaultcfg(); initdstate(&s); disable_logprints(); strncpy_nt(s.dirname, TESTDBDIR, 512); s.sync = 1; ck_assert_int_eq(remove_directory(TESTDIR), 1); ck_assert_int_eq(clean_testdbdir(), 1); filldatabaselist(&s); ck_assert_int_eq(s.dbcount, 0); ck_assert_int_eq(s.sync, 0); ck_assert_int_eq(s.updateinterval, 120); } END_TEST START_TEST(filldatabaselist_adds_databases) { DSTATE s; defaultcfg(); initdstate(&s); disable_logprints(); strncpy_nt(s.dirname, TESTDBDIR, 512); s.sync = 1; ck_assert_int_eq(remove_directory(TESTDIR), 1); ck_assert_int_eq(clean_testdbdir(), 1); ck_assert_int_eq(create_zerosize_dbfile("name1"), 1); ck_assert_int_eq(create_zerosize_dbfile("name2"), 1); ck_assert_int_eq(check_dbfile_exists("name1", 0), 1); ck_assert_int_eq(check_dbfile_exists(".name1", 0), 0); ck_assert_int_eq(check_dbfile_exists("name2", 0), 1); ck_assert_int_eq(check_dbfile_exists(".name2", 0), 0); filldatabaselist(&s); ck_assert_int_eq(cachecount(), 2); ck_assert_int_eq(cacheactivecount(), 2); ck_assert_int_eq(check_dbfile_exists("name1", 0), 1); ck_assert_int_eq(check_dbfile_exists(".name1", 0), 0); ck_assert_int_eq(check_dbfile_exists("name2", 0), 1); ck_assert_int_eq(check_dbfile_exists(".name2", 0), 0); ck_assert_int_eq(s.dbcount, 2); ck_assert_int_eq(s.sync, 0); ck_assert_int_eq(s.updateinterval, 0); ck_assert_int_eq(intsignal, 42); } END_TEST START_TEST(adjustsaveinterval_with_empty_cache) { DSTATE s; defaultcfg(); initdstate(&s); s.saveinterval = 0; ck_assert_int_eq(cacheactivecount(), 0); adjustsaveinterval(&s); ck_assert_int_eq(s.saveinterval, cfg.offsaveinterval * 60); } END_TEST START_TEST(adjustsaveinterval_with_filled_cache) { DSTATE s; defaultcfg(); initdb(); initdstate(&s); s.saveinterval = 0; strcpy(data.interface, "name1"); ck_assert_int_eq(cacheupdate(), 1); ck_assert_int_eq(cacheactivecount(), 1); adjustsaveinterval(&s); ck_assert_int_eq(s.saveinterval, cfg.saveinterval * 60); } END_TEST START_TEST(checkdbsaveneed_has_no_need) { DSTATE s; initdstate(&s); s.dodbsave = 2; s.current = 10; s.prevdbsave = 0; s.saveinterval = 30; s.forcesave = 0; checkdbsaveneed(&s); ck_assert_int_eq(s.dodbsave, 0); ck_assert_int_ne(s.prevdbsave, s.current); } END_TEST START_TEST(checkdbsaveneed_is_forced) { DSTATE s; initdstate(&s); s.dodbsave = 2; s.current = 10; s.prevdbsave = 0; s.saveinterval = 30; s.forcesave = 1; checkdbsaveneed(&s); ck_assert_int_eq(s.dodbsave, 1); ck_assert_int_eq(s.prevdbsave, s.current); ck_assert_int_eq(s.forcesave, 0); } END_TEST START_TEST(checkdbsaveneed_needs) { DSTATE s; initdstate(&s); s.dodbsave = 2; s.current = 60; s.prevdbsave = 5; s.saveinterval = 30; s.forcesave = 0; checkdbsaveneed(&s); ck_assert_int_eq(s.dodbsave, 1); ck_assert_int_eq(s.prevdbsave, s.current); ck_assert_int_eq(s.forcesave, 0); } END_TEST START_TEST(datalist_cacheget_with_no_database) { DSTATE s; defaultcfg(); initdb(); initdstate(&s); disable_logprints(); strncpy_nt(s.dirname, TESTDBDIR, 512); ck_assert_int_eq(remove_directory(TESTDIR), 1); ck_assert_int_eq(clean_testdbdir(), 1); ck_assert_int_eq(cacheadd("name1", 0), 1); s.dbcount = 1; s.dbhash = 123; s.datalist = dataptr; ck_assert_int_eq(datalist_cacheget(&s), 0); ck_assert_int_eq(s.dbhash, 123); ck_assert_int_eq(s.datalist->filled, 0); } END_TEST START_TEST(datalist_cacheget_with_database) { DSTATE s; defaultcfg(); initdb(); initdstate(&s); strncpy_nt(s.dirname, TESTDBDIR, 512); ck_assert_int_eq(remove_directory(TESTDIR), 1); ck_assert_int_eq(clean_testdbdir(), 1); ck_assert_int_eq(cacheadd("name1", 0), 1); strncpy_nt(data.interface, "name1", 32); ck_assert_int_eq(writedb("name1", TESTDBDIR, 1), 1); ck_assert_int_eq(check_dbfile_exists("name1", sizeof(DATA)), 1); s.dbcount = 1; s.dbhash = 123; s.datalist = dataptr; ck_assert_int_eq(datalist_cacheget(&s), 1); ck_assert_int_eq(s.dbhash, 0); ck_assert_int_eq(s.datalist->filled, 1); } END_TEST START_TEST(datalist_getifinfo_with_disabled_interface) { DSTATE s; linuxonly; initdb(); initdstate(&s); ck_assert_int_eq(remove_directory(TESTDIR), 1); data.active = 0; datalist_getifinfo(&s); ck_assert_int_eq(data.active, 0); } END_TEST START_TEST(datalist_getifinfo_with_enabled_unavailable_interface) { DSTATE s; linuxonly; initdb(); initdstate(&s); disable_logprints(); ck_assert_int_eq(remove_directory(TESTDIR), 1); strncpy_nt(data.interface, "name1", 32); data.active = 1; datalist_getifinfo(&s); ck_assert_int_eq(data.active, 0); } END_TEST START_TEST(datalist_getifinfo_with_interface_sync) { DSTATE s; linuxonly; initdb(); initdstate(&s); disable_logprints(); ck_assert_int_eq(remove_directory(TESTDIR), 1); fake_proc_net_dev("w", "name1", 1025, 2050, 30, 40); strncpy_nt(data.interface, "name1", 32); ck_assert_int_eq(cacheadd("name1", 1), 1); data.active = 1; data.currx = 1; data.curtx = 2; s.dbcount = 1; s.datalist = dataptr; ck_assert_int_eq(s.datalist->sync, 1); ck_assert_int_eq(data.currx, 1); ck_assert_int_eq(data.curtx, 2); ck_assert_int_eq(data.totalrxk, 0); ck_assert_int_eq(data.totaltxk, 0); datalist_getifinfo(&s); ck_assert_int_eq(data.active, 1); ck_assert_int_eq(data.totalrxk, 0); ck_assert_int_eq(data.totaltxk, 0); ck_assert_int_eq(data.currx, 1025); ck_assert_int_eq(data.curtx, 2050); ck_assert_int_eq(s.datalist->sync, 0); } END_TEST START_TEST(datalist_getifinfo_with_interface_and_no_sync) { DSTATE s; linuxonly; initdb(); initdstate(&s); disable_logprints(); ck_assert_int_eq(remove_directory(TESTDIR), 1); fake_proc_net_dev("w", "name1", 1025, 2050, 30, 40); strncpy_nt(data.interface, "name1", 32); ck_assert_int_eq(cacheadd("name1", 0), 1); data.active = 1; data.currx = 1; data.curtx = 2; s.dbcount = 1; s.datalist = dataptr; ck_assert_int_eq(s.datalist->sync, 0); ck_assert_int_eq(data.currx, 1); ck_assert_int_eq(data.curtx, 2); ck_assert_int_eq(data.totalrxk, 0); ck_assert_int_eq(data.totaltxk, 0); datalist_getifinfo(&s); ck_assert_int_eq(data.active, 1); ck_assert_int_eq(data.totalrxk, 1); ck_assert_int_eq(data.totaltxk, 2); ck_assert_int_eq(data.currx, 1025); ck_assert_int_eq(data.curtx, 2050); ck_assert_int_eq(s.datalist->sync, 0); } END_TEST START_TEST(datalist_timevalidation_in_normal_time) { DSTATE s; initdb(); initdstate(&s); data.lastupdated = time(NULL); s.current = time(NULL); ck_assert_int_eq(datalist_timevalidation(&s), 1); ck_assert_int_eq(data.lastupdated, s.current); s.current++; ck_assert_int_eq(datalist_timevalidation(&s), 1); ck_assert_int_eq(data.lastupdated, s.current); } END_TEST START_TEST(datalist_timevalidation_in_future_time) { DSTATE s; initdb(); initdstate(&s); data.lastupdated = time(NULL)+10; s.current = time(NULL); ck_assert_int_eq(datalist_timevalidation(&s), 0); ck_assert_int_ne(data.lastupdated, s.current); } END_TEST START_TEST(datalist_timevalidation_in_too_future_time) { DSTATE s; initdb(); initdstate(&s); disable_logprints(); data.lastupdated = time(NULL)+90000; s.current = time(NULL); ck_assert_int_eq(remove_directory(TESTDIR), 1); ck_assert_int_eq(datalist_timevalidation(&s), 0); ck_assert_int_ne(data.lastupdated, s.current); } END_TEST START_TEST(datalist_writedb_does_not_save_unless_requested) { DSTATE s; initdstate(&s); s.dodbsave = 0; s.dbsaved = 0; ck_assert_int_eq(datalist_writedb(&s), 1); ck_assert_int_eq(s.dbsaved, 0); } END_TEST START_TEST(datalist_writedb_detects_missing_database_file) { DSTATE s; initdstate(&s); s.dodbsave = 1; s.dbsaved = 0; strncpy_nt(s.dirname, TESTDBDIR, 512); ck_assert_int_eq(remove_directory(TESTDIR), 1); ck_assert_int_eq(cacheadd("name1", 0), 1); s.datalist = dataptr; ck_assert_int_eq(datalist_writedb(&s), 0); ck_assert_int_eq(s.dbsaved, 0); } END_TEST START_TEST(datalist_writedb_writes_database_file) { DSTATE s; initdstate(&s); s.dodbsave = 1; s.dbsaved = 0; disable_logprints(); strncpy_nt(s.dirname, TESTDBDIR, 512); ck_assert_int_eq(remove_directory(TESTDIR), 1); ck_assert_int_eq(clean_testdbdir(), 1); ck_assert_int_eq(create_zerosize_dbfile("name1"), 1); ck_assert_int_eq(cacheadd("name1", 0), 1); s.datalist = dataptr; ck_assert_int_eq(datalist_writedb(&s), 1); ck_assert_int_eq(s.dbsaved, 1); ck_assert_int_eq(check_dbfile_exists("name1", sizeof(DATA)), 1); ck_assert_int_eq(check_dbfile_exists(".name1", 0), 1); } END_TEST START_TEST(processdatalist_empty_does_nothing) { DSTATE s; initdstate(&s); processdatalist(&s); } END_TEST START_TEST(processdatalist_filled_does_things) { DSTATE s; linuxonly; initdb(); initdstate(&s); disable_logprints(); s.current = time(NULL); strncpy_nt(s.dirname, TESTDBDIR, 512); ck_assert_int_eq(remove_directory(TESTDIR), 1); ck_assert_int_eq(clean_testdbdir(), 1); ck_assert_int_eq(create_zerosize_dbfile("ethnormal"), 1); ck_assert_int_eq(create_zerosize_dbfile("ethunavailable"), 1); ck_assert_int_eq(create_zerosize_dbfile("ethbogus"), 1); ck_assert_int_eq(create_zerosize_dbfile("ethfuture"), 1); fake_proc_net_dev("w", "ethbogus", 1, 2, 3, 4); fake_proc_net_dev("a", "ethnormal", 1024, 2048, 30, 40); fake_proc_net_dev("a", "ethnodb", 2048, 3072, 40, 50); fake_proc_net_dev("a", "ethfuture", 3072, 4096, 50, 60); strcpy(data.interface, "ethnormal"); ck_assert_int_eq(cacheupdate(), 1); strcpy(data.interface, "ethunavailable"); ck_assert_int_eq(cacheupdate(), 1); strcpy(data.interface, "ethnodb"); ck_assert_int_eq(cacheupdate(), 1); strcpy(data.interface, "ethfuture"); data.lastupdated = time(NULL)+10; ck_assert_int_eq(cacheupdate(), 1); strcpy(data.interface, "ethbogus"); data.lastupdated = time(NULL); data.version = 0; ck_assert_int_eq(cacheupdate(), 1); strcpy(data.interface, "foo"); s.dbcount = 5; s.dodbsave = 1; s.datalist = dataptr; processdatalist(&s); ck_assert_int_eq(s.dbcount, 4); ck_assert_int_eq(cachegetname("ethnodb"), 0); ck_assert_int_eq(cachegetname("ethunavailable"), 1); ck_assert_int_eq(data.active, 0); ck_assert_int_eq(cachegetname("ethnormal"), 1); ck_assert_int_eq(data.active, 1); ck_assert_int_eq(data.currx, 1024); ck_assert_int_eq(data.curtx, 2048); ck_assert_int_eq(cachegetname("ethfuture"), 1); ck_assert_int_eq(data.active, 1); ck_assert_int_eq(data.currx, 0); ck_assert_int_eq(data.curtx, 0); ck_assert_int_eq(cachegetname("ethbogus"), 1); ck_assert_int_eq(data.active, 1); ck_assert_int_eq(data.currx, 0); ck_assert_int_eq(data.curtx, 0); } END_TEST START_TEST(handleintsignals_handles_signals) { DSTATE s; defaultcfg(); initdstate(&s); s.running = 1; s.dbcount = 1; intsignal = 0; handleintsignals(&s); ck_assert_int_eq(intsignal, 0); ck_assert_int_eq(s.running, 1); ck_assert_int_eq(s.dbcount, 1); intsignal = 42; handleintsignals(&s); ck_assert_int_eq(intsignal, 0); ck_assert_int_eq(s.running, 1); ck_assert_int_eq(s.dbcount, 1); disable_logprints(); intsignal = 43; handleintsignals(&s); ck_assert_int_eq(intsignal, 0); ck_assert_int_eq(s.running, 1); ck_assert_int_eq(s.dbcount, 1); intsignal = SIGTERM; handleintsignals(&s); ck_assert_int_eq(intsignal, 0); ck_assert_int_eq(s.running, 0); ck_assert_int_eq(s.dbcount, 1); s.running = 1; intsignal = SIGINT; handleintsignals(&s); ck_assert_int_eq(intsignal, 0); ck_assert_int_eq(s.running, 0); ck_assert_int_eq(s.dbcount, 1); s.running = 1; intsignal = SIGHUP; strncpy_nt(s.dirname, TESTDBDIR, 512); handleintsignals(&s); ck_assert_int_eq(intsignal, 0); ck_assert_int_eq(s.running, 1); ck_assert_int_eq(s.dbcount, 0); } END_TEST START_TEST(direxists_with_no_dir) { defaultcfg(); ck_assert_int_eq(remove_directory(TESTDIR), 1); ck_assert_int_eq(direxists(""), 0); ck_assert_int_eq(direxists(TESTDIR), 0); } END_TEST START_TEST(direxists_with_dir) { defaultcfg(); ck_assert_int_eq(remove_directory(TESTDIR), 1); ck_assert_int_eq(clean_testdbdir(), 1); ck_assert_int_eq(direxists(TESTDIR), 1); ck_assert_int_eq(direxists(TESTDBDIR), 1); } END_TEST START_TEST(mkpath_with_no_dir) { defaultcfg(); ck_assert_int_eq(remove_directory(TESTDIR), 1); ck_assert_int_eq(mkpath("", 0775), 0); } END_TEST START_TEST(mkpath_with_dir) { defaultcfg(); ck_assert_int_eq(remove_directory(TESTDIR), 1); ck_assert_int_eq(direxists(TESTDIR), 0); ck_assert_int_eq(direxists(TESTDBDIR), 0); ck_assert_int_eq(mkpath(TESTDIR, 0775), 1); ck_assert_int_eq(direxists(TESTDIR), 1); ck_assert_int_eq(direxists(TESTDBDIR), 0); ck_assert_int_eq(mkpath(TESTDBDIR, 0775), 1); ck_assert_int_eq(direxists(TESTDBDIR), 1); ck_assert_int_eq(remove_directory(TESTDIR), 1); ck_assert_int_eq(direxists(TESTDBDIR), 0); ck_assert_int_eq(mkpath(TESTDBDIR, 0775), 1); ck_assert_int_eq(direxists(TESTDBDIR), 1); } END_TEST START_TEST(preparedirs_with_no_dir) { char logdir[512], piddir[512]; DSTATE s; initdstate(&s); defaultcfg(); cfg.uselogging = 1; s.rundaemon = 1; strncpy_nt(s.dirname, TESTDBDIR, 512); snprintf(logdir, 512, "%s/log/vnstat", TESTDIR); snprintf(piddir, 512, "%s/pid/vnstat", TESTDIR); snprintf(cfg.logfile, 512, "%s/vnstat.log", logdir); snprintf(cfg.pidfile, 512, "%s/vnstat.pid", piddir); ck_assert_int_eq(remove_directory(TESTDIR), 1); ck_assert_int_eq(direxists(TESTDBDIR), 0); ck_assert_int_eq(direxists(logdir), 0); ck_assert_int_eq(direxists(piddir), 0); preparedirs(&s); ck_assert_int_eq(direxists(TESTDBDIR), 1); ck_assert_int_eq(direxists(logdir), 1); ck_assert_int_eq(direxists(piddir), 1); } END_TEST START_TEST(preparedirs_with_dir) { char logdir[512], piddir[512]; DSTATE s; initdstate(&s); defaultcfg(); cfg.uselogging = 1; s.rundaemon = 1; strncpy_nt(s.dirname, TESTDBDIR, 512); snprintf(logdir, 512, "%s/log/vnstat", TESTDIR); snprintf(piddir, 512, "%s/pid/vnstat", TESTDIR); snprintf(cfg.logfile, 512, "%s/vnstat.log", logdir); snprintf(cfg.pidfile, 512, "%s/vnstat.pid", piddir); ck_assert_int_eq(remove_directory(TESTDIR), 1); ck_assert_int_eq(direxists(TESTDBDIR), 0); ck_assert_int_eq(mkpath(TESTDBDIR, 0775), 1); ck_assert_int_eq(direxists(TESTDBDIR), 1); ck_assert_int_eq(direxists(logdir), 0); ck_assert_int_eq(direxists(piddir), 0); preparedirs(&s); ck_assert_int_eq(direxists(TESTDBDIR), 1); ck_assert_int_eq(direxists(logdir), 1); ck_assert_int_eq(direxists(piddir), 1); } END_TEST START_TEST(preparevnstatdir_with_no_vnstat) { char testdir[512], testpath[512]; defaultcfg(); cfg.updatefileowner = 0; ck_assert_int_eq(remove_directory(TESTDIR), 1); ck_assert_int_eq(direxists(TESTDIR), 0); snprintf(testdir, 512, "%s/here/be/dragons", TESTDIR); snprintf(testpath, 512, "%s/or_something.txt", testdir); preparevnstatdir(testpath, "user", "group"); ck_assert_int_eq(direxists(TESTDIR), 0); ck_assert_int_eq(direxists(testdir), 0); snprintf(testdir, 512, "%s/here/be/vnstat/dragons", TESTDIR); snprintf(testpath, 512, "%s/or_something.txt", testdir); preparevnstatdir(testpath, "user", "group"); ck_assert_int_eq(direxists(TESTDIR), 0); ck_assert_int_eq(direxists(testdir), 0); snprintf(testdir, 512, "%s/here/be/vnstati", TESTDIR); snprintf(testpath, 512, "%s/or_something.txt", testdir); preparevnstatdir(testpath, "user", "group"); ck_assert_int_eq(direxists(TESTDIR), 0); ck_assert_int_eq(direxists(testdir), 0); } END_TEST START_TEST(preparevnstatdir_with_vnstat) { char testdir[512], testpath[512]; defaultcfg(); cfg.updatefileowner = 0; ck_assert_int_eq(remove_directory(TESTDIR), 1); ck_assert_int_eq(direxists(TESTDIR), 0); snprintf(testdir, 512, "%s/here/be/vnstat", TESTDIR); snprintf(testpath, 512, "%s/or_something.txt", testdir); preparevnstatdir(testpath, "user", "group"); ck_assert_int_eq(direxists(TESTDIR), 1); ck_assert_int_eq(direxists(testdir), 1); ck_assert_int_eq(remove_directory(TESTDIR), 1); ck_assert_int_eq(direxists(TESTDIR), 0); snprintf(testdir, 512, "%s/here/be/vnstatd", TESTDIR); snprintf(testpath, 512, "%s/or_something.txt", testdir); preparevnstatdir(testpath, "user", "group"); ck_assert_int_eq(direxists(TESTDIR), 1); ck_assert_int_eq(direxists(testdir), 1); } END_TEST void add_daemon_tests(Suite *s) { TCase *tc_daemon = tcase_create("Daemon"); tcase_add_test(tc_daemon, getuser_root_string); tcase_add_test(tc_daemon, getuser_root_numeric); tcase_add_exit_test(tc_daemon, getuser_no_such_user_string, 1); tcase_add_exit_test(tc_daemon, getuser_no_such_user_numeric, 1); tcase_add_test(tc_daemon, getgroup_root_string); tcase_add_test(tc_daemon, getgroup_root_numeric); tcase_add_exit_test(tc_daemon, getgroup_no_such_user_string, 1); tcase_add_exit_test(tc_daemon, getgroup_no_such_user_numeric, 1); tcase_add_test(tc_daemon, debugtimestamp_does_not_exit); tcase_add_test(tc_daemon, initdstate_does_not_crash); tcase_add_test(tc_daemon, addinterfaces_does_nothing_with_no_files); tcase_add_test(tc_daemon, addinterfaces_adds_interfaces); tcase_add_exit_test(tc_daemon, preparedatabases_exits_with_no_database_dir, 1); tcase_add_exit_test(tc_daemon, preparedatabases_exits_with_no_databases, 1); tcase_add_test(tc_daemon, preparedatabases_with_no_databases_creates_databases); tcase_add_test(tc_daemon, setsignaltraps_does_not_exit); tcase_add_exit_test(tc_daemon, filldatabaselist_exits_with_no_database_dir, 1); tcase_add_test(tc_daemon, filldatabaselist_does_not_exit_with_empty_database_dir); tcase_add_test(tc_daemon, filldatabaselist_adds_databases); tcase_add_test(tc_daemon, adjustsaveinterval_with_empty_cache); tcase_add_test(tc_daemon, adjustsaveinterval_with_filled_cache); tcase_add_test(tc_daemon, checkdbsaveneed_has_no_need); tcase_add_test(tc_daemon, checkdbsaveneed_is_forced); tcase_add_test(tc_daemon, checkdbsaveneed_needs); tcase_add_test(tc_daemon, datalist_cacheget_with_no_database); tcase_add_test(tc_daemon, datalist_cacheget_with_database); tcase_add_test(tc_daemon, datalist_getifinfo_with_disabled_interface); tcase_add_test(tc_daemon, datalist_getifinfo_with_enabled_unavailable_interface); tcase_add_test(tc_daemon, datalist_getifinfo_with_interface_sync); tcase_add_test(tc_daemon, datalist_getifinfo_with_interface_and_no_sync); tcase_add_test(tc_daemon, datalist_timevalidation_in_normal_time); tcase_add_test(tc_daemon, datalist_timevalidation_in_future_time); tcase_add_exit_test(tc_daemon, datalist_timevalidation_in_too_future_time, 1); tcase_add_test(tc_daemon, datalist_writedb_does_not_save_unless_requested); tcase_add_test(tc_daemon, datalist_writedb_detects_missing_database_file); tcase_add_test(tc_daemon, datalist_writedb_writes_database_file); tcase_add_test(tc_daemon, processdatalist_empty_does_nothing); tcase_add_test(tc_daemon, processdatalist_filled_does_things); tcase_add_test(tc_daemon, handleintsignals_handles_signals); tcase_add_test(tc_daemon, direxists_with_no_dir); tcase_add_test(tc_daemon, direxists_with_dir); tcase_add_test(tc_daemon, mkpath_with_no_dir); tcase_add_test(tc_daemon, mkpath_with_dir); tcase_add_test(tc_daemon, preparedirs_with_no_dir); tcase_add_test(tc_daemon, preparedirs_with_dir); tcase_add_test(tc_daemon, preparevnstatdir_with_no_vnstat); tcase_add_test(tc_daemon, preparevnstatdir_with_vnstat); suite_add_tcase(s, tc_daemon); } int cachegetname(const char *iface) { datanode *dn; dn = dataptr; while (dn != NULL) { if (strcmp(dn->data.interface, iface) == 0) { memcpy(&data, &dn->data, sizeof(data)); return 1; } dn = dn->next; } return 0; } vnstat-1.14/tests/Makefile0000644000000000000000000000371512506045367014274 0ustar rootrootifeq "$(origin CC)" "default" CC = gcc endif CFLAGS ?= -Wall -Wextra -g CFLAGS += -DPROCNETDEV=\"testdir/proc/dev\" -DSYSCLASSNET=\"testdir/sysclassnet\" LDLIBS = -lm -lcheck -lrt -lpthread OBJS = vnstat_tests.o common_tests.o database_tests.o config_tests.o ifinfo_tests.o misc_tests.o daemon_tests.o OBJS += common.o ifinfo.o dbshow.o dbaccess.o dbcache.o cfg.o ibw.o misc.o daemon.o vpath %.c ../src vpath %.h ../src CFLAGS += -I../src default: runtests runtests: vnstat_tests ./vnstat_tests profile: CFLAGS += -fprofile-arcs -ftest-coverage profile: LDLIBS += -fprofile-arcs -ftest-coverage profile: runtests lcov -c -d $(PWD) -o coverage.lcov genhtml coverage.lcov --num-spaces 4 -p $(shell dirname $(PWD)) -o coverage vnstat_tests: $(OBJS) $(CC) $(LDFLAGS) $(OBJS) $(LDLIBS) -o vnstat_tests vnstat_tests.o: vnstat_tests.c vnstat_tests.h common_tests.h database_tests.h common.h common_tests.o: common_tests.c common_tests.h vnstat_tests.h common.h dbaccess.h cfg.h database_tests.o: database_tests.c database_tests.h vnstat_tests.h common.h ifinfo.h dbaccess.h dbcache.h dbshow.h cfg.h ibw.h config_tests.o: config_tests.c config_tests.h vnstat_tests.h common.h cfg.h ibw.h ifinfo_tests.o: ifinfo_tests.c ifinfo_tests.h vnstat_tests.h common.h ifinfo.h dbaccess.h misc.h cfg.h ibw.h misc_tests.o: misc_tests.c misc_tests.h vnstat_tests.h common.h misc.h daemon_tests.o: daemon_tests.c daemon_tests.h common.h dbaccess.h dbcache.h cfg.h daemon.h common.o: common.c common.h ifinfo.o: ifinfo.c ifinfo.h common.h dbaccess.h misc.h cfg.h ibw.h dbaccess.o: dbaccess.c dbaccess.h common.h dbcache.o: dbcache.c dbcache.h dbaccess.h common.h ifinfo.h cfg.h ibw.h dbshow.o: dbshow.c dbshow.h misc.h common.h cfg.o: cfg.c cfg.h common.h ibw.o: ibw.c ibw.h cfg.h ifinfo.h common.h misc.o: misc.c misc.h common.h daemon.o: daemon.c daemon.h common.h ifinfo.h dbaccess.h dbcache.h misc.h cfg.h ibw.h clean: rm -fr *.o *~ core *.i *.gc* test.log test.xml *.lcov coverage vnstat_tests testdir vnstat-1.14/tests/database_tests.h0000644000000000000000000000013612317006312015750 0ustar rootroot#ifndef DATABASE_TESTS_H #define DATABASE_TESTS_H void add_database_tests(Suite *s); #endif vnstat-1.14/tests/common_tests.c0000644000000000000000000001731212403124157015477 0ustar rootroot#include "vnstat_tests.h" #include "common_tests.h" #include "common.h" #include "dbaccess.h" #include "cfg.h" START_TEST(printe_options) { noexit = 2; cfg.uselogging = 0; ck_assert_int_eq(printe(PT_Info), 1); cfg.uselogging = 1; ck_assert_int_eq(printe(PT_Multiline), 1); noexit = 0; strcpy(errorstring, "dummy string"); suppress_output(); ck_assert_int_eq(printe(PT_Info), 1); ck_assert_int_eq(printe(PT_Error), 1); ck_assert_int_eq(printe(PT_Config), 1); ck_assert_int_eq(printe(PT_Multiline), 1); ck_assert_int_eq(printe(PT_ShortMultiline), 1); ck_assert_int_eq(printe(5), 1); } END_TEST START_TEST(logprint_options) { cfg.uselogging = 0; ck_assert_int_eq(logprint(PT_Info), 0); cfg.uselogging = 1; strcpy(cfg.logfile, "/dev/null"); strcpy(errorstring, "dummy string"); ck_assert_int_eq(logprint(PT_Info), 1); ck_assert_int_eq(logprint(PT_Error), 1); ck_assert_int_eq(logprint(PT_Config), 1); ck_assert_int_eq(logprint(PT_Multiline), 0); ck_assert_int_eq(logprint(PT_ShortMultiline), 1); ck_assert_int_eq(logprint(5), 1); } END_TEST START_TEST(dmonth_return_within_range) { int m; m = dmonth(_i); ck_assert_int_ge(m, 28); ck_assert_int_le(m, 31); } END_TEST START_TEST(mosecs_return_values) { initdb(); defaultcfg(); ck_assert_int_eq(cfg.monthrotate, 1); ck_assert_int_eq((int)mosecs(), 0); sleep(1); data.lastupdated = time(NULL); ck_assert_int_ne((int)mosecs(), 0); } END_TEST START_TEST(countercalc_no_change) { uint64_t a, b; a = b = 0; ck_assert_int_eq(countercalc(&a, &b), 0); a = b = 1; ck_assert_int_eq(countercalc(&a, &b), 0); } END_TEST START_TEST(countercalc_small_change) { uint64_t a, b; a = 0; b = 1; ck_assert_int_eq(countercalc(&a, &b), 1); a = 1; b = 2; ck_assert_int_eq(countercalc(&a, &b), 1); b = 3; ck_assert_int_eq(countercalc(&a, &b), 2); } END_TEST START_TEST(countercalc_32bit) { uint64_t a, b; a = 1; b = 0; ck_assert(countercalc(&a, &b)==(MAX32-1)); } END_TEST START_TEST(countercalc_64bit) { uint64_t a, b; a = MAX32+1; b = 0; ck_assert(countercalc(&a, &b)==(MAX64-MAX32-1)); } END_TEST START_TEST(addtraffic_does_not_add_zero_traffic) { uint64_t srcmb, destmb; int srckb, destkb; srcmb=srckb=destmb=destkb=0; addtraffic(&destmb, &destkb, srcmb, srckb); ck_assert_int_eq(srcmb, 0); ck_assert_int_eq(srckb, 0); ck_assert_int_eq(destmb, 0); ck_assert_int_eq(destkb, 0); } END_TEST START_TEST(addtraffic_with_simple_mb_addition) { uint64_t srcmb, destmb; int srckb, destkb; destmb=destkb=0; srcmb=1; srckb=0; addtraffic(&destmb, &destkb, srcmb, srckb); ck_assert_int_eq(srcmb, 1); ck_assert_int_eq(srckb, 0); ck_assert_int_eq(destmb, 1); ck_assert_int_eq(destkb, 0); } END_TEST START_TEST(addtraffic_with_simple_kb_addition) { uint64_t srcmb, destmb; int srckb, destkb; destmb=destkb=0; srcmb=0; srckb=1; addtraffic(&destmb, &destkb, srcmb, srckb); ck_assert_int_eq(srcmb, 0); ck_assert_int_eq(srckb, 1); ck_assert_int_eq(destmb, 0); ck_assert_int_eq(destkb, 1); } END_TEST START_TEST(addtraffic_with_simple_mixed_addition) { uint64_t srcmb, destmb; int srckb, destkb; destmb=destkb=0; srcmb=1; srckb=1; addtraffic(&destmb, &destkb, srcmb, srckb); ck_assert_int_eq(srcmb, 1); ck_assert_int_eq(srckb, 1); ck_assert_int_eq(destmb, 1); ck_assert_int_eq(destkb, 1); } END_TEST START_TEST(addtraffic_with_multiple_mixed_additions) { uint64_t srcmb, destmb; int srckb, destkb, i; destmb=destkb=0; srcmb=1; srckb=1; for (i=1; i<=10; i++) { addtraffic(&destmb, &destkb, srcmb, srckb); ck_assert_int_eq(srcmb, 1); ck_assert_int_eq(srckb, 1); ck_assert_int_eq(destmb, (uint64_t)i); ck_assert_int_eq(destkb, i); } } END_TEST START_TEST(addtraffic_with_exact_kb_to_mb_conversion) { uint64_t srcmb, destmb; int srckb, destkb; destmb=destkb=0; srcmb=0; srckb=1024; addtraffic(&destmb, &destkb, srcmb, srckb); ck_assert_int_eq(srcmb, 0); ck_assert_int_eq(srckb, 1024); ck_assert_int_eq(destmb, 1); ck_assert_int_eq(destkb, 0); } END_TEST START_TEST(addtraffic_with_inexact_kb_to_mb_conversion) { uint64_t srcmb, destmb; int srckb, destkb; destmb=destkb=0; srcmb=0; srckb=1025; addtraffic(&destmb, &destkb, srcmb, srckb); ck_assert_int_eq(srcmb, 0); ck_assert_int_eq(srckb, 1025); ck_assert_int_eq(destmb, 1); ck_assert_int_eq(destkb, 1); } END_TEST START_TEST(addtraffic_with_multiple_kb_to_mb_conversions) { uint64_t destmb; int destkb; destmb=destkb=0; addtraffic(&destmb, &destkb, 0, 1023); ck_assert_int_eq(destmb, 0); ck_assert_int_eq(destkb, 1023); addtraffic(&destmb, &destkb, 0, 1); ck_assert_int_eq(destmb, 1); ck_assert_int_eq(destkb, 0); addtraffic(&destmb, &destkb, 0, 1); ck_assert_int_eq(destmb, 1); ck_assert_int_eq(destkb, 1); addtraffic(&destmb, &destkb, 0, 1023); ck_assert_int_eq(destmb, 2); ck_assert_int_eq(destkb, 0); addtraffic(&destmb, &destkb, 0, 1024); ck_assert_int_eq(destmb, 3); ck_assert_int_eq(destkb, 0); addtraffic(&destmb, &destkb, 0, 1025); ck_assert_int_eq(destmb, 4); ck_assert_int_eq(destkb, 1); addtraffic(&destmb, &destkb, 0, 512); ck_assert_int_eq(destmb, 4); ck_assert_int_eq(destkb, 513); addtraffic(&destmb, &destkb, 0, 512); ck_assert_int_eq(destmb, 5); ck_assert_int_eq(destkb, 1); addtraffic(&destmb, &destkb, 0, 2048); ck_assert_int_eq(destmb, 7); ck_assert_int_eq(destkb, 1); } END_TEST START_TEST(strncpy_nt_with_below_maximum_length_string) { char dst[6]; strncpy_nt(dst, "123", 6); ck_assert_str_eq(dst, "123"); } END_TEST START_TEST(strncpy_nt_with_maximum_length_string) { char dst[6]; strncpy_nt(dst, "12345", 6); ck_assert_str_eq(dst, "12345"); } END_TEST START_TEST(strncpy_nt_with_over_maximum_length_string) { char dst[6]; strncpy_nt(dst, "123456", 6); ck_assert_str_eq(dst, "12345"); strncpy_nt(dst, "1234567890", 6); ck_assert_str_eq(dst, "12345"); } END_TEST START_TEST(isnumeric_empty) { ck_assert_int_eq(isnumeric(""), 0); } END_TEST START_TEST(isnumeric_it_is) { ck_assert_int_eq(isnumeric("0"), 1); ck_assert_int_eq(isnumeric("1"), 1); ck_assert_int_eq(isnumeric("12"), 1); ck_assert_int_eq(isnumeric("123"), 1); } END_TEST START_TEST(isnumeric_it_is_not) { ck_assert_int_eq(isnumeric("a"), 0); ck_assert_int_eq(isnumeric("abc"), 0); ck_assert_int_eq(isnumeric("a1"), 0); ck_assert_int_eq(isnumeric("1a"), 0); ck_assert_int_eq(isnumeric("123abc"), 0); ck_assert_int_eq(isnumeric("/"), 0); ck_assert_int_eq(isnumeric("-"), 0); } END_TEST void add_common_tests(Suite *s) { TCase *tc_common = tcase_create("Common"); tcase_add_test(tc_common, printe_options); tcase_add_test(tc_common, logprint_options); tcase_add_loop_test(tc_common, dmonth_return_within_range, 0, 12); tcase_add_test(tc_common, mosecs_return_values); tcase_add_test(tc_common, countercalc_no_change); tcase_add_test(tc_common, countercalc_small_change); tcase_add_test(tc_common, countercalc_32bit); tcase_add_test(tc_common, countercalc_64bit); tcase_add_test(tc_common, addtraffic_does_not_add_zero_traffic); tcase_add_test(tc_common, addtraffic_with_simple_mb_addition); tcase_add_test(tc_common, addtraffic_with_simple_kb_addition); tcase_add_test(tc_common, addtraffic_with_simple_mixed_addition); tcase_add_test(tc_common, addtraffic_with_multiple_mixed_additions); tcase_add_test(tc_common, addtraffic_with_exact_kb_to_mb_conversion); tcase_add_test(tc_common, addtraffic_with_inexact_kb_to_mb_conversion); tcase_add_test(tc_common, addtraffic_with_multiple_kb_to_mb_conversions); tcase_add_test(tc_common, strncpy_nt_with_below_maximum_length_string); tcase_add_test(tc_common, strncpy_nt_with_maximum_length_string); tcase_add_test(tc_common, strncpy_nt_with_over_maximum_length_string); tcase_add_test(tc_common, isnumeric_empty); tcase_add_test(tc_common, isnumeric_it_is); tcase_add_test(tc_common, isnumeric_it_is_not); suite_add_tcase(s, tc_common); } vnstat-1.14/tests/vnstat_tests.c0000644000000000000000000001542612506045367015543 0ustar rootroot#include "vnstat_tests.h" #include "common_tests.h" #include "database_tests.h" #include "config_tests.h" #include "ifinfo_tests.h" #include "misc_tests.h" #include "daemon_tests.h" #include "common.h" int main(void) { int number_failed = 0; debug = 0; Suite *s = test_suite(); SRunner *sr = srunner_create(s); srunner_set_log(sr, "test.log"); srunner_set_xml(sr, "test.xml"); srunner_run_all(sr, CK_NORMAL); number_failed = srunner_ntests_failed(sr); srunner_free(sr); if (number_failed == 0) { remove_directory(TESTDIR); } return number_failed; } Suite *test_suite(void) { Suite *s = suite_create("vnStat"); add_common_tests(s); add_database_tests(s); add_config_tests(s); add_ifinfo_tests(s); add_misc_tests(s); add_daemon_tests(s); return s; } void suppress_output(void) { fclose(stdout); } int pipe_output(void) { int out_pipe[2]; if (pipe(out_pipe) != 0) { ck_abort_msg("error \"%s\" while creating pipe", strerror(errno)); } dup2(out_pipe[1], STDOUT_FILENO); close(out_pipe[1]); return out_pipe[0]; } void disable_logprints(void) { noexit = 2; cfg.uselogging = 0; } int clean_testdbdir(void) { struct stat statbuf; create_testdir(); if (stat(TESTDBDIR, &statbuf)!=0) { if (errno==ENOENT) { if (mkdir(TESTDBDIR, 0755)==0) { return 1; } } ck_abort_msg("error \"%s\" while creating directory \"%s\"", strerror(errno), TESTDBDIR); } if (!remove_directory(TESTDBDIR)) { ck_abort_msg("error \"%s\" while removing directory \"%s\", please remove it manually", strerror(errno), TESTDBDIR); } if (mkdir(TESTDBDIR, 0755)!=0) { ck_abort_msg("error \"%s\" while creating directory \"%s\"", strerror(errno), TESTDBDIR); } return 1; } int create_testdir(void) { struct stat statbuf; if (stat(TESTDIR, &statbuf)!=0) { if (errno==ENOENT) { if (mkdir(TESTDIR, 0755)==0) { return 1; } } ck_abort_msg("error \"%s\" while creating directory \"%s\"", strerror(errno), TESTDIR); } return 1; } int create_directory(const char *directory) { struct stat statbuf; if (stat(directory, &statbuf)!=0) { if (errno==ENOENT) { if (mkdir(directory, 0755) != 0) { ck_abort_msg("error \"%s\" while creating directory \"%s\"", strerror(errno), directory); } } else { ck_abort_msg("error \"%s\" while creating directory \"%s\"", strerror(errno), directory); } } return 1; } int remove_directory(const char *directory) { DIR *dir = NULL; struct dirent *di = NULL; char entryname[512]; if ((dir=opendir(directory))==NULL) { if (errno==ENOENT) { return 1; } else { return 0; } } while ((di=readdir(dir))) { switch (di->d_type) { case DT_LNK: case DT_REG: snprintf(entryname, 512, "%s/%s", directory, di->d_name); if (unlink(entryname)!=0) { closedir(dir); return 0; } break; case DT_DIR: if (strcmp(di->d_name, ".")==0 || strcmp(di->d_name, "..")==0) { continue; } snprintf(entryname, 512, "%s/%s", directory, di->d_name); if (!remove_directory(entryname)) { closedir(dir); return 0; } break; default: continue; } } closedir(dir); if (rmdir(directory)!=0) { return 0; } return 1; } int create_zerosize_dbfile(const char *iface) { FILE *fp; char filename[512]; snprintf(filename, 512, "%s/%s", TESTDBDIR, iface); if ((fp=fopen(filename, "w"))==NULL) { ck_abort_msg("error \"%s\" while opening file \"%s\" for writing", strerror(errno), filename); } fclose(fp); return 1; } int check_dbfile_exists(const char *iface, const int minsize) { struct stat statbuf; char filename[512]; snprintf(filename, 512, "%s/%s", TESTDBDIR, iface); if (stat(filename, &statbuf)!=0) { if (errno==ENOENT) { return 0; } ck_abort_msg("error \"%s\" while inspecting file \"%s\"", strerror(errno), filename); } if (statbuf.st_size < minsize) { ck_abort_msg("file \"%s\" is smaller (%d) then given minimum %d", filename, (int)statbuf.st_size, minsize); } return 1; } int fake_proc_net_dev(const char *mode, const char *iface, const int rx, const int tx, const int rxp, const int txp) { FILE *devfp; char filename[512]; if (strcmp(mode, "w") != 0 && strcmp(mode, "a") != 0) { ck_abort_msg("error: only w and a modes are supported"); } create_testdir(); create_directory(TESTPROCDIR); snprintf(filename, 512, "%s/dev", TESTPROCDIR); if ((devfp=fopen(filename, mode))==NULL) { ck_abort_msg("error \"%s\" while opening file \"%s\" for writing", strerror(errno), filename); } if (strcmp(mode, "w") == 0) { fprintf(devfp, "Inter-| Receive | Transmit\n"); fprintf(devfp, " face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed\n"); fprintf(devfp, " lo: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"); } fprintf(devfp, "%6s: %7d %7d 0 0 0 0 0 0 %7d %7d 0 0 0 0 0 0\n", iface, rx, rxp, tx, txp); fclose(devfp); return 1; } int fake_sys_class_net(const char *iface, const int rx, const int tx, const int rxp, const int txp, const int speed) { FILE *devfp; char dirname[512]; char filename[512]; create_testdir(); create_directory(TESTSYSCLASSNETDIR); snprintf(dirname, 512, "%s/%s", TESTSYSCLASSNETDIR, iface); create_directory(dirname); snprintf(dirname, 512, "%s/%s/statistics", TESTSYSCLASSNETDIR, iface); create_directory(dirname); if (speed != 0) { snprintf(filename, 512, "%s/%s/speed", TESTSYSCLASSNETDIR, iface); if ((devfp=fopen(filename, "w"))==NULL) { ck_abort_msg("error \"%s\" while opening file \"%s\" for writing", strerror(errno), filename); } fprintf(devfp, "%d\n", speed); fclose(devfp); } snprintf(filename, 512, "%s/%s/statistics/rx_bytes", TESTSYSCLASSNETDIR, iface); if ((devfp=fopen(filename, "w"))==NULL) { ck_abort_msg("error \"%s\" while opening file \"%s\" for writing", strerror(errno), filename); } fprintf(devfp, "%d\n", rx); fclose(devfp); snprintf(filename, 512, "%s/%s/statistics/tx_bytes", TESTSYSCLASSNETDIR, iface); if ((devfp=fopen(filename, "w"))==NULL) { ck_abort_msg("error \"%s\" while opening file \"%s\" for writing", strerror(errno), filename); } fprintf(devfp, "%d\n", tx); fclose(devfp); snprintf(filename, 512, "%s/%s/statistics/rx_packets", TESTSYSCLASSNETDIR, iface); if ((devfp=fopen(filename, "w"))==NULL) { ck_abort_msg("error \"%s\" while opening file \"%s\" for writing", strerror(errno), filename); } fprintf(devfp, "%d\n", rxp); fclose(devfp); snprintf(filename, 512, "%s/%s/statistics/tx_packets", TESTSYSCLASSNETDIR, iface); if ((devfp=fopen(filename, "w"))==NULL) { ck_abort_msg("error \"%s\" while opening file \"%s\" for writing", strerror(errno), filename); } fprintf(devfp, "%d\n", txp); fclose(devfp); return 1; } vnstat-1.14/tests/config_tests.h0000644000000000000000000000013012317006311015442 0ustar rootroot#ifndef CONFIG_TESTS_H #define CONFIG_TESTS_H void add_config_tests(Suite *s); #endif vnstat-1.14/tests/daemon_tests.h0000644000000000000000000000017512371477207015471 0ustar rootroot#ifndef DAEMON_TESTS_H #define DAEMON_TESTS_H void add_daemon_tests(Suite *s); int cachegetname(const char *iface); #endif vnstat-1.14/CHANGES0000644000000000000000000002445612517211044012457 0ustar rootroot1.14 / 26-Apr-2015 - Fix: JSON output syntax during first day of newly created databases (pull request by Stefan Merettig) - Fix: field padding when using UTF-8 locale - Add optional mode parameter to --json and --xml for limiting the output to only selected information 1.13 / 18-Jan-2015 - Fix: getgroup() test in Debian GNU/kFreeBSD (patch by Felix Geyer) - Fix: struct if_data usage after freeifaddrs() call in BSD - Dynamic unit selection in hourly output instead of being fixed to KiB - New options in image output (vnstati) * -nl / --nolegend for hiding the rx/tx legend * --altdate for using alternative date and time text location * --headertext for using custom text string in image header section - Add legend to hourly output image - Add option for content alignment and page background color to vnstat.cgi - Document keyword value ranges in configuration file man page - Import Makefile improvements from OpenBSD (pull requests by Jasper Lievisse Adriaanse) - Automatic interface bandwidth detection, BandwidthDetection in config, default fallback value MaxBandwidth changed from 100 to 1000 - JSON output (--json), vnstat-json.cgi and vnstat-json.php examples provided in the 'examples' directory - Drop support for over 10 year old database formats (versions 1.0 - 1.2) 1.12 / 30-Aug-2014 - Fix: Memory allocation when zero interfaces are available, also lo needed to be missing in order to trigger this leak - Fix: Rebuild total indexing (thanks to William Epp for reporting this) - Fix: Some error situations could leave database file open until the end of command execution - Fix: Live traffic meter occasionally showing higher minimum than average in end statistics (Debian Bug #687812) - Fix: Setting locale when none is specified in config (Debian Bug #606397) - Fix: Cppcheck findings (may fix Debian Bug #692330) - Improve traffic meter output accuracy - Add tests and debug compilation target - Remove use of -D parameter in Makefile install commands in order to improve cross-compilation support - Set CFLAGS in Makefiles only when not already defined - Add support for database import from text file (--importdb) (based on patch by Tilmann Bubeck) - Rename --dumpdb to --exportdb - Add example systemd service file - Add example launchd plist file for OS X - Use ISO YYYY-MM-DD date format timestamps if logfile is used - Improve daemon startup prints - Add parameters for changing daemon process user and group - Add example upstart job configuration file (thanks to Cameron Norman) - Create database, pid and log dirs during daemon startup if necessary - Update ownership of database, log and pid files if needed during daemon startup if started as root and configured to change process user and group - Remove cron update related example files and documentation, the cron update method should be considered as deprecated - --create parameter for database creation 1.11 / 1-Jun-2011 - Fix: Memory allocation was miscalculated when creating interface list from /sys/class/net when /proc/net/dev wasn't available which in turn could crash the daemon - Fix: Daemon database cache could remain empty after a -HUP signal - Fix: Don't make temp directory in vnstat.cgi writable for everyone - Import GNU/kFreeBSD support from Debian (#608963, patch by Mats Erik Andersson) - Remove usage of GNU only '-D' option for install for BSD in Makefile - The daemon now automatically creates databases for available interfaces if no databases are found during startup 1.10 / 2-Jan-2010 - Fix: Buffer overflow was possible in hourly image output when RateUnit=1 and HourlyRate=1 - Fix: Minor memory leak was possible in the handling of HUP signal in daemon - Fix: Graphical elements weren't correctly aligned in summary image when header wasn't visible (-nh) - Fix: --delete didn't work - Possibility to merge statistics from several databases and save the end result to a new database (--mergesaved) - Added validation of database cache in daemon in order to be more robust in case of system memory corruption - Support for --style to -l (live mode) - Alternative print mode to -l (live mode) with optional parameter - Present options and elements in man pages in alphabetical order - Code cleanup 1.9 / 12-Sep-2009 - Fix: TrafficlessDays configuration option was enabled when set to zero when it should have been disabled - Fix: MonthRotate setting was getting ignored for the first month if the database is created when day of month < MonthRotate value - Configurable summary layout in image output (1.7 <> 1.8 layouts) - --oneline, a simple parseable one line output - --transparent for setting image background transparency from the command line - --delete for deleting databases and stopping monitoring, doesn't require restarting the daemon - A database write can be configure to occur after interface status changes - Different database write interval can be configure to be used when all monitored interfaces are offline - Better configurability for the content of outputs, including a narrow output for space limited terminals (OutputStyle and --style) - Code cleanup - New configuration file settings: OutputStyle, SummaryLayout, SummaryRate, SaveOnStatusChange, OfflineSaveInterval 1.8 / 1-Aug-2009 - Fix: OpenBSD print issues (was: ' used for formatting in sprintf) - Fix: Monthly estimate for February during a leap year was wrong - Fix: 'make install' didn't install the config file is $(DESTDIR) was used - Traffic of current and previous months added to summary - Increased accuracy of monthly estimate calculation - Traffic rates can be made visible and the shown unit is configurable as bits or bytes - New summary layout in image output - Image output can have the background color set as transparent - Possibility to merge data from several interfaces to one output with interface1+interface2 syntax - -s / --sync parameter added to daemon - --dbdir and --locale parameters added - Outputs now use system locale by default and formatting of dates follow the locale setting unless configured otherwise - New configuration file settings: ShowRate, RateUnit, TrafficlessDays, HourlyRate, TransparentBg 1.7 / 12-Apr-2009 - Fix: Timezone changes shouldn't cause a flood of errors anymore - Fix: Statvfs used instead of statfs for BSD compatibility (Thanks to Mark Mitchell for pointing this out) - Fix: Makefile didn't properly support LDFLAGS and -lm was misplaced - Fix: MonthRotate value in config wasn't used - Fix: Unreadable configuration file doesn't cause a segmentation fault - Fix: /proc/net/dev parser didn't see the difference between eth10 and eth1 - Fix: Updating a database from two of more processes at the same time was able to sometimes cause data loss - Fix: Estimates are calculated based on last database update - Fix: Each interface update had a possibility of losing 1023 bytes of traffic at most due to improper rounding (Thanks to Michael Berlin for suggesting a suitable correction) - Daemon support as alternative for cron based updates - PNG image output (hourly rate based on patch by Sergio Ammirata) - XML output - BSD support - Diskspace check can be disabled from configuration file - IEC standard prefixes (KiB/MiB/GiB/TiB) - List of available interfaces if wrong of unavailable interface is selected 1.6 / 1-Jan-2008 - Fix: Segmentation fault when environment variable "HOME" wasn't defined, this broke most CGI and PHP scripts that used vnStat - Support for /sys/class/net/* if /proc/net/dev is unavailable - Config file parser redesigned - --config parameter for selecting config file - Consistent exit status for all operations (0 for success, 1 for error) - Improved file locking - Man page restrictions paragraph updated - Minor non-visible fixes and code cleanup 1.5 / 3-Dec-2007 - Fix: compile time warnings solved - Fix: kernel test is now more verbose - Fix: more informative error messages - Fix: possible division by zero for traffic estimates right after midnight - Fix: interface names longer than 6 chars (patch by Jan Schmidle) - Realtime transfer rate mode - Automatic 32bit/64bit counter detection - Config file support - Internal database backups and locking - More visuals in outputs - Adaptive units (kB, MB, GB, TB) - Possibility to sync counters without counting traffic - Maximum bandwidth of interfaces can be set 1.4 / 26-Mar-2004 - Fix: major output problems when compiled with some compilers (thanks to DukePyrolator for reporting this bug and Juha / vlu for testing the patch) - Fix: month rotation if database isn't updated every day - Minor non-visible fixes and code cleanup 1.3 / 8-Mar-2004 - Fix: support for 64bit counters - Fix: 'yesterday' was showing 01.01. with new databases - Fix: free space warning changed from 1% to 1MB - Fix: minor security exploit - A man page has been included - Hourly output with textgraphical view - kBs are visible when traffic is under 1000 MB - Webpage moved to http://humdi.net/vnstat/ - Minor visual updates 1.2 / 7-Oct-2003 - Changed file paths to conform with FHS (http://www.pathname.com/fhs/) /usr/local/bin/vnstat -> /usr/bin/vnstat /var/spool/vnstat -> /var/lib/vnstat - Added FAQ - Included sample scripts for pppd users - Support for 64bit counters in /proc/net/dev (Thanks to Stephan van Hienen for the test account) - Actual date is shown if the previous day in the database isn't yesterday - Weekly and last 7 days traffic can be shown. (Thanks to Derk-Jan Hartman for the suggestion) - Average kB/s meter - --testkernel fixed - Documentation for --dumpdb included in the README 1.1 / 13-Jul-2003 - Support for multiple interfaces - Date checking (update shouldn't be before previous update) - New database structure - Possibility to change date output format - Test tool for faulty kernels - Parseable output - Better support for ppp/dsl users - Free diskspace check - Possibility to change month rotation day (like 25. instead of 1.) - Daily/monthly traffic estimation 22-Nov-2002 - Renamed the cron script file that would be installed to /etc/cron.d from vnstat.cron to vnstat because debian systems didn't execute scripts containing dots (Thanks to frangen / Simo Salminen for this note) 1.0 / 23-Sep-2002 - Initial public release vnstat-1.14/UPGRADE0000644000000000000000000000252212517202275012472 0ustar rootroot New configuration settings :::::::::::::::::::::::::: 1.14: (none) 1.13: BandwidthDetection, BandwidthDetectionInterval 1.12: DaemonUser, DaemonGroup, CreateDirs, UpdateFileOwner 1.10 - 1.11: (none) 1.9: OutputStyle, SummaryLayout, SummaryRate, SaveOnStatusChange, OfflineSaveInterval 1.8: ShowRate, RateUnit, TrafficlessDays, HourlyRate, TransparentBg 1.7: UnitMode + all settings under vnstatd and vnstati Upgrading from versions 1.3 and later ::::::::::::::::::::::::::::::::::::: 1) Follow the normal install procedure, 'make install' will not overwrite your configuration file 2) Generate a new configuration file using current settings in order to get new configuration tags included: vnstat --showconfig >/tmp/newconf Never overwrite the current configuration file directly! That will result in all previous settings getting replaced by defaults. 3) Check that the configuration settings in /tmp/newconf are correct. If date formatting using system locale is used then use the following settings: Locale "-" DayFormat "%x" MonthFormat "%b '%y" TopFormat "%x" HeaderFormat "%x %H:%M" 4) Replace the old configuration file: mv /tmp/newconf /etc/vnstat.conf 5) If the daemon (vnstatd) is used then restart the daemon after the configuration file has been updated. vnstat-1.14/COPYING0000644000000000000000000004311011565015143012507 0ustar rootroot GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 2 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License.