vnstat-1.11/0000755000000000000000000000000011571514113011450 5ustar rootrootvnstat-1.11/Makefile0000644000000000000000000001274611570527576013142 0ustar rootroot# bin and man dirs for Linux BIN = $(DESTDIR)/usr/bin SBIN = $(DESTDIR)/usr/sbin MAN = $(DESTDIR)/usr/share/man # bin and man dirs for *BSD BIN_BSD = $(DESTDIR)/usr/local/bin SBIN_BSD = $(DESTDIR)/usr/local/sbin MAN_BSD = $(DESTDIR)/usr/local/man default: vnstat vnstat: +make -C src all: +make -C src all clean: make -C src clean 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 "$(DESTDIR)/etc/vnstat.conf" ]; \ then echo "Installing config to $(DESTDIR)/etc/vnstat.conf"; \ install -D -m 644 cfg/vnstat.conf $(DESTDIR)/etc/vnstat.conf; \ fi # install everything else install -d -m 755 $(BIN) $(SBIN) $(MAN)/man1 $(MAN)/man5 $(DESTDIR)/var/lib/vnstat 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 startup script or cron entry 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 $(DESTDIR)/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 $(DESTDIR)/etc/vnstat.conf ]; \ then echo "Installing config to $(DESTDIR)/etc/vnstat.conf"; \ install -d -m 755 $(DESTDIR)/etc; \ install -m 644 cfg/vnstat.conf $(DESTDIR)/etc/vnstat.conf; \ sed -e 's/lib/db/g' $(DESTDIR)/etc/vnstat.conf >$(DESTDIR)/etc/vnstat.conf.bsd; \ mv -f $(DESTDIR)/etc/vnstat.conf.bsd $(DESTDIR)/etc/vnstat.conf; \ fi # update man page 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 startup script or cron entry 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 $(DESTDIR)/etc/vnstat.conf @echo "A possible cron entry needs to be removed manually if such exists." vnstat-1.11/UNINSTALL0000644000000000000000000000025311565017537012757 0ustar rootroot If the daemon is running then stop it first. Linux: Run 'make uninstall' and follow the instructions. BSD: Run 'make bsduninstall' and follow the instructions. vnstat-1.11/FAQ0000644000000000000000000001067211570527576012030 0ustar rootrootThat FAQ is located at http://humdi.net/vnstat/FAQ since that's the easiest way to keep it updated. ---- A snapshot of the FAQ (updated 29.5.2011): Is there some kind of support forum available? The forum is located at http://forums.humdi.net/ 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.11/examples/0000755000000000000000000000000011571514114013267 5ustar rootrootvnstat-1.11/examples/vnstat_ip-down0000644000000000000000000000015611565017276016202 0ustar rootroot#!/bin/sh if [ -x /usr/bin/vnstat ] && [ -w /var/lib/vnstat/$1 ]; then /usr/bin/vnstat -r --disable -i $1; fi vnstat-1.11/examples/vnstat.cgi0000755000000000000000000001112211571514010015265 0ustar rootroot#!/usr/bin/perl -w # vnstat.cgi -- example cgi for vnStat image output # copyright (c) 2008-2011 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 $host = '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'; # cache time in minutes, set 0 to disable my $cachetime = '15'; # shown interfaces, remove unnecessary lines my @graphs = ( { interface => 'eth0' }, { interface => 'eth1' }, ); ################ my $VERSION = "1.3"; 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 $host 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 $host 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() { 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.11/examples/vnstat.cron0000755000000000000000000000024411565017537015506 0ustar rootroot# run vnstat update every 5 minutes if installed */5 * * * * root if [ -x /usr/bin/vnstat ] && [ `ls /var/lib/vnstat/ | wc -l` -ge 1 ]; then /usr/bin/vnstat -u; fi vnstat-1.11/examples/init.d/0000755000000000000000000000000011571514114014454 5ustar rootrootvnstat-1.11/examples/init.d/arch/0000755000000000000000000000000011571514114015371 5ustar rootrootvnstat-1.11/examples/init.d/arch/vnstat0000755000000000000000000000145411565017537016654 0ustar rootroot#!/bin/bash . /etc/rc.conf . /etc/rc.d/functions PID=`pidof -o %PPID /usr/sbin/vnstatd` case "$1" in start) stat_busy "Starting vnStat daemon" [ -z "$PID" ] && /usr/sbin/vnstatd -d if [ $? -gt 0 ]; then stat_fail else add_daemon vnstat stat_done fi ;; stop) stat_busy "Stopping vnStat daemon" [ ! -z "$PID" ] && kill $PID &> /dev/null if [ $? -gt 0 ]; then stat_fail else rm_daemon vnstat stat_done fi ;; reload) stat_busy "Reloading vnStat daemon configuration" [ ! -z "$PID" ] && kill -HUP $PID &> /dev/null if [ $? -gt 0 ]; then stat_fail else stat_done fi ;; restart) $0 stop sleep 1 $0 start ;; *) echo "usage: $0 {start|stop|restart|reload}" esac exit 0 vnstat-1.11/examples/init.d/redhat/0000755000000000000000000000000011571514113015722 5ustar rootrootvnstat-1.11/examples/init.d/redhat/vnstat0000755000000000000000000000267111565017537017210 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.11/examples/init.d/debian/0000755000000000000000000000000011571514113015675 5ustar rootrootvnstat-1.11/examples/init.d/debian/vnstat0000755000000000000000000000504211565017537017156 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.11/examples/init.d/ubuntu0000777000000000000000000000000011571514113017062 2debianustar rootrootvnstat-1.11/examples/init.d/fedora0000777000000000000000000000000011571514113017025 2redhatustar rootrootvnstat-1.11/examples/init.d/centos0000777000000000000000000000000011571514114017061 2redhatustar rootrootvnstat-1.11/examples/init.d/opensuse/0000755000000000000000000000000011571514113016314 5ustar rootrootvnstat-1.11/examples/init.d/opensuse/vnstat0000755000000000000000000000602111565017537017573 0ustar rootroot#! /bin/bash # # 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 # First reset status of this service . /etc/rc.status rc_reset # Shell functions sourced from /etc/rc.status: # rc_check check and set local and overall rc status # rc_status check and set local and overall rc status # rc_status -v ditto but be verbose in local rc status # rc_status -v -r ditto and clear the local rc status # rc_failed set local and overall rc status to failed # rc_reset clear local rc status (overall remains) # rc_exit exit appropriate to overall rc status # Source function library for OpenSuse if ! . /etc/sysconfig/network/scripts/functions 2>/dev/null; then echo -n "Network: file /etc/sysconfig/network/scripts/functions is missing." rc_failed rc_status -v rc_exit fi # Return values acc. to LSB for all commands but status: # 0 - success # 1 - generic or unspecified error # 2 - invalid or excess argument(s) # 3 - unimplemented feature (e.g. "reload") # 4 - insufficient privilege # 5 - program is not installed # 6 - program is not configured # 7 - program is not running VNSTATD_BIN=/usr/sbin/vnstatd test -x $VNSTATD_BIN || exit 5 VNSTATD_BIN_CONFIG=/etc/vnstat.conf test -f $VNSTATD_BIN_CONFIG || exit 6 prog=vnstatd VNSTATD_PIDFILE=/var/run/vnstat.pid start() { echo -n $"Starting $prog: " # NOTE: start_daemon return 0, even if service is # already running to match LSB spec. start_daemon -p $VNSTATD_PIDFILE $VNSTATD_BIN --daemon # Remember status and be verbose rc_status -v } stop() { echo -n $"Shutting down $prog: " ## Stop daemon with killproc(8) and if this fails ## set echo the echo return value. killproc -p $VNSTATD_PIDFILE -TERM $VNSTATD_BIN # Remember status and be verbose rc_status -v } reload() { echo -n $"Reloading $prog configuration: " killproc -p $VNSTATD_PIDFILE -HUP $VNSTATD_BIN rc_status -v } case "$1" in start) start ;; stop) stop ;; reload) reload ;; restart) stop start ;; try-restart) if [ -f $VNSTATD_PIDFILE ]; then stop start fi ;; force-reload) reload || (stop; start) ;; status) echo -n "Checking for vnStat daemon (vnstatd): " ## Check status with checkproc(8), if process is running ## checkproc will return with exit status 0. # Status has a slightly different for the status command: # 0 - service running # 1 - service dead, but /var/run/ pid file exists # 2 - service dead, but /var/lock/ lock file exists # 3 - service not running checkproc -p $VNSTATD_PIDFILE $VNSTATD_BIN rc_status -v ;; *) echo $"Usage: $0 {start|stop|reload|force-reload|restart|try-restart|status}" rc_status -v esac rc_exit vnstat-1.11/examples/vnstat_ip-up0000644000000000000000000000015511565017276015656 0ustar rootroot#!/bin/sh if [ -x /usr/bin/vnstat ] && [ -w /var/lib/vnstat/$1 ]; then /usr/bin/vnstat -r --enable -i $1; fi vnstat-1.11/CHANGES0000644000000000000000000001655211571514010012450 0ustar rootroot1.11 / 1-Jun-11 - 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-10 - 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-09 - 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-09 - 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-09 - 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-08 - 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-07 - 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-04 - 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-04 - 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-03 - 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-03 - 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-02 - 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-02 - Initial public release vnstat-1.11/COPYING0000644000000000000000000004311011565015143012504 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. vnstat-1.11/INSTALL0000644000000000000000000001251211571514010012476 0ustar rootroot(Updated 29.5.2011 for version 1.11) Compiling the binaries :::::::::::::::::::::: This source package contains the required sources 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). 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. vnstat --testkernel The only way to fix a faulty kernel (afaik) is to compile/install a newer one. Next every interface that should be monitored needs to be introduced to vnStat. Replace 'eth0' in the command with any available interface if needed. vnstat -u -i eth0 Repeat that for every other interface you wish to use. If you are unsure of available interface names then run vnstat --iflist and select a suitable looking interface from the provided list. Usually 'lo' isn't the one you are looking for. 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). There are two way for updating interface data, daemon and cron. It is suggested to use daemon based updating because it generated less disk access and is more accurate especially when monitoring interfaces that aren't always available. Select ONLY ONE of these two: 1) daemon based updating :::::::::::::::::::::::: Configure init scripts so that the following command is executed once during system start: vnstatd -d The examples/init.d directory contains suitable init.d scripts for some commonly used distributions. An alternative option 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 for that. 2) cron based updating :::::::::::::::::::::: Copy the example cron file to /etc/cron.d cp examples/vnstat.cron /etc/cron.d/vnstat and edit the cron file if the default 5 minute update interval is too long. See the vnStat man page 'restrictions' section for limitations regarding fast interfaces and 32 bit interface traffic counters. Devices like ppp0 also require vnStat enable and disable commands to be added to suitable script file when cron based updating is used. Example scripts can be found in the 'examples' directory. 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 your $PATH. 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 you homedir path execute cd ; pwd The ouput should tell your homedir. Next every interface that should be monitored needs to be introduced to vnStat. Replace 'eth0' in the command with any available interface if needed. vnstat -u -i eth0 Repeat that for every other interface you wish to use. 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): */5 * * * * ~/bin/vnstat -u OR if the daemon can be used @reboot ~/bin/vnstatd -d If you found yourself using a strange editor then 'man vi' should help. If you choose to use the daemon then 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 (if using the daemon) try starting it with 'vnstat -d'. After that wait for (or generate) at least 1024 bytes of network traffic (and 5 min for the next cron/daemon update). vnstat Now you should get some stats about your network usage. See the config file ~/.vnstatrc for interface and other settings. vnstat-1.11/src/0000755000000000000000000000000011571515225012244 5ustar rootrootvnstat-1.11/src/dbcache.h0000644000000000000000000000106011565020424013756 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.11/src/dbmerge.c0000644000000000000000000001053411571265737014032 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(mergedata.interface, iface, 32); strncpy(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.11/src/Makefile0000644000000000000000000000246311571514010013700 0ustar rootrootCC = gcc CFLAGS = -O2 LDLIBS = -lm OBJS = vnstat.o ifinfo.o dbxml.o dbshow.o dbaccess.o dbmerge.o common.o misc.o cfg.o traffic.o DOBJS = vnstatd.o ifinfo.o dbaccess.o dbcache.o common.o misc.o cfg.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 dbshow.h dbaccess.h dbmerge.h misc.h cfg.h vnstatd.o: vnstatd.c vnstatd.h common.h ifinfo.h dbaccess.h dbcache.h misc.h cfg.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 traffic.o: traffic.c traffic.h common.h ifinfo.h misc.h dbxml.o: dbxml.c dbxml.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 common.o: common.c common.h misc.o: misc.c misc.h common.h cfg.o: cfg.c cfg.h common.h image.o: image.c image.h vnstati.h common.h misc.h clean: rm -f *.o *~ core *.i vnstat vnstatd vnstati vnstat-1.11/src/ifinfo.h0000644000000000000000000000065211570527576013705 0ustar rootroot#ifndef IFINFO_H #define IFINFO_H int getifinfo(const char *iface); int getiflist(char **ifacelist); int readproc(const char *iface); int readsysclassnet(const char *iface); void parseifinfo(int newdb); uint64_t countercalc(uint64_t a, uint64_t b); #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) || defined(__FreeBSD_kernel__) int readifaddrs(const char *iface); #endif #endif vnstat-1.11/src/dbshow.h0000644000000000000000000000055511565020270013702 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 dumpdb(void); void showbar(uint64_t rx, int rxk, uint64_t tx, int txk, uint64_t max, int len); void indent(int i); #endif vnstat-1.11/src/dbxml.h0000644000000000000000000000014311565017276013527 0ustar rootroot#ifndef DBXML_H #define DBXML_H void showxml(void); void xmldate(time_t *date, int type); #endif vnstat-1.11/src/misc.c0000644000000000000000000002167311571265737013366 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."); 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."); printe(PT_Error); if (noexit) { return 0; } else { exit(1); } } check=0; while (fgets(statline,128,fp)!=NULL) { sscanf(statline,"%64s",temp); if (strcmp(temp,"btime")==0) { /* if (debug) printf("\n%s\n",statline); */ check=1; break; } } 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); fclose(fp); #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; } void addtraffic(uint64_t *destmb, int *destkb, uint64_t srcmb, int srckb) { *destmb=*destmb+srcmb; *destkb=*destkb+srckb; if (*destkb>=1024) { *destmb+=*destkb/1024; *destkb-=(*destkb/1024)*1024; } } 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) { if (kb>=1024) { mb+=kb/1024; kb-=(kb/1024)*1024; } kB=(mb*1024)+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 { #if !defined(__OpenBSD__) && !defined(__NetBSD__) /* try to figure out what unit to use */ if (kB>=1048576000) { /* 1024*1024*1000 - value >=1000 GiB -> show in TiB */ snprintf(buffer, 64, "%'*.*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, "%'*.*f %s", len, declen, kB/(float)1048576, getunit(3)); /* 1024*1024 */ } else if (kB>=1000) { if (type==2) { declen=0; } snprintf(buffer, 64, "%'*.*f %s", len, declen, kB/(float)1024, getunit(2)); } else { snprintf(buffer, 64, "%'*"PRIu64" %s", len, kB, getunit(1)); } #else /* try to figure out what unit to use */ if (kB>=1048576000) { /* 1024*1024*1000 - value >=1000 GiB -> show in TiB */ snprintf(buffer, 64, "%*.*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, "%*.*f %s", len, declen, kB/(float)1048576, getunit(3)); /* 1024*1024 */ } else if (kB>=1000) { if (type==2) { declen=0; } snprintf(buffer, 64, "%*.*f %s", len, declen, kB/(float)1024, getunit(2)); } else { snprintf(buffer, 64, "%*"PRIu64" %s", len, kB, getunit(1)); } #endif } 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; uint32_t limit[3]; float rate; if (interval==0) { snprintf(buffer, 64, "%*s", len, "n/a"); return buffer; } if (mb!=0) { if (kb>=1024) { mb+=kb/1024; kb-=(kb/1024)*1024; } kB=(mb*1024)+kb; } else { kB=kb; } /* convert to proper unit */ if (cfg.rateunit) { limit[0] = 1000; limit[1] = 1000000; limit[2] = 1000000000; rate = (kB*8)/(float)interval; unit = 2; if (interval<5) { declen = 0; } } else { limit[0] = 1024; limit[1] = 1024000; limit[2] = 1048576000; rate = kB/(float)interval; unit = cfg.unit; } /* tune spacing according to unit */ len -= strlen(getrateunit(unit, 1)) + 1; if (len<0) { len = 1; } #if !defined(__OpenBSD__) && !defined(__NetBSD__) /* try to figure out what unit to use */ if (rate>=limit[2]) { snprintf(buffer, 64, "%'*.2f %s", len, rate/(float)limit[2], getrateunit(unit, 4)); } else if (rate>=limit[1]) { snprintf(buffer, 64, "%'*.2f %s", len, rate/(float)limit[1], getrateunit(unit, 3)); } else if (rate>=limit[0]) { snprintf(buffer, 64, "%'*.2f %s", len, rate/(float)limit[0], getrateunit(unit, 2)); } else { snprintf(buffer, 64, "%'*.*f %s", len, declen, rate, getrateunit(unit, 1)); } #else /* try to figure out what unit to use */ if (rate>=limit[2]) { snprintf(buffer, 64, "%*.2f %s", len, rate/(float)limit[2], getrateunit(unit, 4)); } else if (rate>=limit[1]) { snprintf(buffer, 64, "%*.2f %s", len, rate/(float)limit[1], getrateunit(unit, 3)); } else if (rate>=limit[0]) { snprintf(buffer, 64, "%*.2f %s", len, rate/(float)limit[0], getrateunit(unit, 2)); } else { snprintf(buffer, 64, "%*.*f %s", len, declen, rate, getrateunit(unit, 1)); } #endif return buffer; } 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", "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]; } } vnstat-1.11/src/dbcache.c0000644000000000000000000001346311571265737014002 0ustar rootroot#include "common.h" #include "ifinfo.h" #include "dbaccess.h" #include "dbcache.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(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; } else { /* 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", p->data.interface, p->filled); } return n->filled; } void cacheshow(void) { int i = 1; datanode *p = dataptr; if (p == NULL) { printf("cache: empty.\n"); } else { printf("cache:"); while (p != NULL) { printf(" %d. \"%s\" ", i, p->data.interface); p = p->next; i++; } printf("\n"); } } void cachestatus(void) { char buffer[512]; int b = 13, count = 0; datanode *p = dataptr; snprintf(buffer, b, "Monitoring: "); if (p != NULL) { while (p != NULL) { if ((b+strlen(p->data.interface)+1) < 508) { strncat(buffer, p->data.interface, strlen(p->data.interface)); strcat(buffer, " "); b = b+strlen(p->data.interface)+1; } else { strcat(buffer, "..."); break; } count++; p = p->next; } } if (count) { strncpy(errorstring, buffer, 512); errorstring[511] = '\0'; } else { snprintf(errorstring, 512, "Nothing to monitor"); } printe(PT_Info); } int cacheget(datanode *dn) { 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) { return 0; } newhash = simplehash(ifacelist, (int)strlen(ifacelist)); /* search for changes if hash doesn't match */ if (newhash!=dbhash) { 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, "%32s", 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.11/src/common.h0000644000000000000000000001362311571514010013701 0ustar rootroot#ifndef COMMON_H #define COMMON_H #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 /* Note! These are only the default values for settings and most can be changed later from the config file. */ /* location of the database directory */ #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) #define DATABASEDIR "/var/db/vnstat" #else #define DATABASEDIR "/var/lib/vnstat" #endif /* 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 */ #define DEFIFACE "eth0" /* default locale */ #define LOCALE "-" /* default maximum bandwidth (Mbit) for all interfaces */ /* 0 = feature disabled */ #define DEFMAXBW 100 /* 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 = dumpdb, 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 /* 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.11" /* xml format version */ /* 1 = 1.7- */ #define XMLVERSION 1 /* --oneline format version */ #define ONELINEVERSION 1 /* integer limits */ #define FP32 4294967295ULL #define FP64 18446744073709551615ULL /* sampletime in seconds for live traffic */ /* don't use values below 2 */ #define LIVETIME 2 /* /proc/net/dev */ #define PROCNETDEV "/proc/net/dev" /* daemon defaults */ #define UPDATEINTERVAL 30 #define POLLINTERVAL 5 #define SAVEINTERVAL 5 #define OFFSAVEINTERVAL 30 #define SAVESTATUS 1 #define USELOGGING 2 #define LOGFILE "/var/log/vnstat.log" #define PIDFILE "/var/run/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]; short unit, ostyle, rateunit, bvar, qmode, sampletime, hourlyrate, summaryrate; short monthrotate, maxbw, flock, spacecheck, traflessday, transbg, slayout; char logfile[512], pidfile[512]; short updateinterval, pollinterval, saveinterval, offsaveinterval, savestatus, uselogging; } 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]; int limit; 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); /* 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.11/src/dbaccess.h0000644000000000000000000000245511565020270014164 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(FILE *db); 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); /* version 1.0 database format aka db v1 */ typedef struct { char date[11]; uint64_t rx, tx; } DAY10; typedef struct { char month[4]; uint64_t rx, tx; } MONTH10; typedef struct { int version; char interface[32]; uint64_t totalrx, totaltx, currx, curtx; int totalrxk, totaltxk; time_t lastupdated, created; DAY10 day[30]; MONTH10 month[12]; DAY10 top10[10]; uint64_t btime; } DATA10; /* version 1.1-1.2 database format aka db v2 */ typedef struct { time_t date; uint64_t rx, tx; int used; } DAY12; typedef struct { time_t month; uint64_t rx, tx; int used; } MONTH12; 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; DAY12 day[30]; MONTH12 month[12]; DAY12 top10[10]; uint64_t btime; } DATA12; #endif vnstat-1.11/src/vnstati.c0000644000000000000000000002631411571265737014120 0ustar rootroot/* vnStat image output - Copyright (c) 2007-11 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[]) { FILE *pngout; int currentarg, cache=0, help=0, showheader=1, showedge=1; char interface[32], dirname[512], filename[512], cfgfile[512]; struct stat filestat; noexit = 0; /* allow functions to exit in case of error */ debug = 0; /* debug disabled by default */ cfgfile[0] = '\0'; /* early check for debug and config parameter */ if (argc > 1) { for (currentarg=1; currentarg0) { setlocale(LC_ALL, cfg.locale); } else { if (getenv("LC_ALL")) { setlocale(LC_ALL, getenv("LC_ALL")); } } strncpy(interface, cfg.iface, 32); strncpy(dirname, cfg.dbdir, 512); filename[0] = '\0'; current = time(NULL); /* parse parameters */ 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 (help || argc == 1) { 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(" -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", 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(" --transparent toggle background transparency\n\n"); printf("See also \"man vnstati\".\n"); return 0; } /* validate input */ if (!cfg.qmode || filename[0]=='\0') { printf("At least output mode and file parameter needs to be given.\n"); printf("Use -? or --help for getting short help.\n"); return 1; } /* check caching */ if (cache>0 && filename[0]!='-') { if (stat(filename, &filestat)==0) { if ((current-filestat.st_mtime)<(cache*60)) { if (debug) printf("Using cached file (%d<%d).\n", (int)(current-filestat.st_mtime), cache*60); return 0; } } 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", filename, strerror(errno), errno); return 1; } } } /* load database and do merge if needed */ if (strstr(interface, "+")) { if (!mergedb(interface, dirname)) { return 1; } } else { if (readdb(interface, dirname)==1) { return 1; } } /* open file */ if (filename[0]!='-') { if ((pngout = fopen(filename, "w"))==NULL) { printf("Error: Opening file \"%s\" for output failed.\n", filename); return 1; } } else { /* output to stdout */ if ((pngout = fdopen(1, "w"))==NULL) { printf("Error: Opening stdout for output failed.\n"); return 1; } } if (debug) printf("Qmode: %d\n", cfg.qmode); /* draw image */ switch (cfg.qmode) { case 1: drawdaily(showheader, showedge); break; case 2: drawmonthly(showheader, showedge); break; case 3: drawtop(showheader, showedge); break; case 5: if (cfg.slayout) { drawsummary(0, showheader, showedge, 0); } else { drawoldsummary(0, showheader, showedge, 0); } break; case 51: if (cfg.slayout) { drawsummary(1, showheader, showedge, cfg.hourlyrate); } else { drawoldsummary(1, showheader, showedge, cfg.hourlyrate); } break; case 52: if (cfg.slayout) { drawsummary(2, showheader, showedge, cfg.hourlyrate); } else { drawoldsummary(2, showheader, showedge, cfg.hourlyrate); } break; case 7: drawhourly(showheader, showedge, cfg.hourlyrate); break; default: break; } /* enable background transparency if needed */ if (cfg.transbg) { gdImageColorTransparent(im, cbackground); } /* write image */ gdImagePng(im, pngout); fclose(pngout); gdImageDestroy(im); /* cleanup */ ibwflush(); if (debug) printf("all done\n"); return 0; } vnstat-1.11/src/dbaccess.c0000644000000000000000000004255011571265737014177 0ustar rootroot#include "common.h" #include "dbaccess.h" int readdb(const char *iface, const char *dirname) { FILE *db; char file[512], backup[512]; int newdb=0; snprintf(file, 512, "%s/%s", dirname, iface); snprintf(backup, 512, "%s/.%s", dirname, iface); if ((db=fopen(file,"r"))!=NULL) { /* lock file */ if (!lockdb(fileno(db), 0)) { 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(data.nick, iface, 32); } strncpy(data.interface, iface, 32); } } else { snprintf(errorstring, 512, "Unable to read database \"%s\".",file); printe(PT_Error); newdb=1; initdb(); strncpy(data.interface, iface, 32); strncpy(data.nick, data.interface, 32); } return newdb; } 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=FP32; } 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 create database backup \"%s\".", backup); printe(PT_Error); return 0; } /* make sure version stays correct */ data.version=DBVERSION; if ((db=fopen(file,"w"))!=NULL) { /* lock file */ if (!lockdb(fileno(db), 1)) { 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\".", file); printe(PT_Error); 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); } } } else { snprintf(errorstring, 512, "Unable to write database \"%s\".", file); printe(PT_Error); return 0; } 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(FILE *db) { int i, days, mod; DATA10 data10; DATA12 data12; time_t current; struct tm *d; int month=0, day; int tm_mday, tm_mon, tm_year; int converted=0; current=time(NULL); d=localtime(¤t); days=d->tm_mday-1; tm_mday=d->tm_mday; tm_mon=d->tm_mon; tm_year=d->tm_year; /* version 1.0 database format */ if (data.version==1) { snprintf(errorstring, 512, "Converting to db v2...\n"); printe(PT_Info); rewind(db); if (fread(&data10, sizeof(DATA10), 1, db)==0) { snprintf(errorstring, 512, "Unable to convert corrupted database."); printe(PT_Error); return 0; } /* set basic values */ data12.version=2; strncpy(data12.interface, data10.interface, 32); strncpy(data12.nick, data10.interface, 32); data12.active=1; data12.totalrx=data10.totalrx; data12.totaltx=data10.totaltx; data12.currx=data10.currx; data12.curtx=data10.curtx; data12.totalrxk=data10.totalrxk; data12.totaltxk=data10.totaltxk; data12.lastupdated=data10.lastupdated; data12.created=data10.created; data12.btime=data10.btime; /* days */ for (i=0; i<=29; i++) { if (data10.day[i].rx+data10.day[i].tx>0) { data12.day[i].rx=data10.day[i].rx; data12.day[i].tx=data10.day[i].tx; data12.day[i].date=current-(i*86400); data12.day[i].used=1; } else { data12.day[i].rx=0; data12.day[i].tx=0; data12.day[i].used=0; } } /* months */ for (i=0; i<=11; i++) { if (data10.month[i].rx+data10.month[i].tx>0) { data12.month[i].rx=data10.month[i].rx; data12.month[i].tx=data10.month[i].tx; data12.month[i].month=current-(86400*days); /* we have to do this modulo thing to get the number of days right */ mod=(d->tm_mon-i-1)%12; if (mod<0) mod=12+mod; days+=dmonth(mod); data12.month[i].used=1; } else { data12.month[i].rx=0; data12.month[i].tx=0; data12.month[i].used=0; } } /* top10 */ for (i=0; i<=9; i++) { if (data10.top10[i].rx+data10.top10[i].tx>0) { data12.top10[i].rx=data10.top10[i].rx; data12.top10[i].tx=data10.top10[i].tx; /* get day */ day=atoi(data10.top10[i].date+7); /* parse month */ if (strncmp(data10.top10[i].date+4, "Jan", 3)==0) { month=0; } else if (strncmp(data10.top10[i].date+4, "Feb", 3)==0) { month=1; } else if (strncmp(data10.top10[i].date+4, "Mar", 3)==0) { month=2; } else if (strncmp(data10.top10[i].date+4, "Apr", 3)==0) { month=3; } else if (strncmp(data10.top10[i].date+4, "May", 3)==0) { month=4; } else if (strncmp(data10.top10[i].date+4, "Jun", 3)==0) { month=5; } else if (strncmp(data10.top10[i].date+4, "Jul", 3)==0) { month=6; } else if (strncmp(data10.top10[i].date+4, "Aug", 3)==0) { month=7; } else if (strncmp(data10.top10[i].date+4, "Sep", 3)==0) { month=8; } else if (strncmp(data10.top10[i].date+4, "Oct", 3)==0) { month=9; } else if (strncmp(data10.top10[i].date+4, "Nov", 3)==0) { month=10; } else if (strncmp(data10.top10[i].date+4, "Dec", 3)==0) { month=11; } else { month=-1; snprintf(errorstring, 512, "Convertion for date \"%s\" failed.", data10.top10[i].date); printe(PT_Error); } if (month==-1) break; /* guess year */ d->tm_year=tm_year; if ((month>tm_mon) || ((month==tm_mon) && (day>tm_mday))) d->tm_year--; d->tm_mday=day; d->tm_mon=month; data12.top10[i].date=mktime(d); data12.top10[i].used=1; } else { data12.top10[i].used=0; data12.top10[i].rx=0; data12.top10[i].tx=0; } } /* reset top10 if there was some problem */ if (month==-1) { for (i=0; i<=9; i++) { data12.top10[i].rx=data.top10[i].tx=0; data12.top10[i].used=0; } } converted=1; } /* version 1.1-1.2 database format */ if (data.version==2 || converted==1) { printf("Converting to db v3...\n"); /* don't read from file if already in memory */ if (converted==0) { rewind(db); if (fread(&data12, sizeof(DATA12), 1, db)==0) { snprintf(errorstring, 512, "Unable to convert corrupted database."); printe(PT_Error); return 0; } } /* set basic values */ data.version=3; strncpy(data.interface, data12.interface, 32); strncpy(data.nick, data12.nick, 32); data.active=data12.active; data.totalrx=data12.totalrx; data.totaltx=data12.totaltx; data.currx=data12.currx; data.curtx=data12.curtx; data.totalrxk=data12.totalrxk; data.totaltxk=data12.totaltxk; data.lastupdated=data12.lastupdated; data.created=data12.created; data.btime=data12.btime; /* days */ for (i=0; i<=29; i++) { if (data12.day[i].used) { data.day[i].rx=data12.day[i].rx; data.day[i].tx=data12.day[i].tx; data.day[i].rxk=data.day[i].txk=0; data.day[i].date=data12.day[i].date; data.day[i].used=1; } else { data.day[i].rx=data.day[i].tx=0; data.day[i].rxk=data.day[i].txk=0; data.day[i].used=0; } } /* months */ for (i=0; i<=11; i++) { if (data12.month[i].used) { data.month[i].rx=data12.month[i].rx; data.month[i].tx=data12.month[i].tx; data.month[i].rxk=data.month[i].txk=0; data.month[i].month=data12.month[i].month; data.month[i].used=1; } else { data.month[i].rx=data.month[i].tx=0; data.month[i].rxk=data.month[i].txk=0; data.month[i].used=0; } } /* top10 */ for (i=0;i<=9;i++) { if (data12.top10[i].used) { data.top10[i].rx=data12.top10[i].rx; data.top10[i].tx=data12.top10[i].tx; data.top10[i].rxk=data.top10[i].txk=0; data.top10[i].date=data12.top10[i].date; data.top10[i].used=1; } else { data.top10[i].rx=data.top10[i].tx=0; data.top10[i].rxk=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; } } /* 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.version>DBVERSION) { 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; } snprintf(errorstring, 512, "Conversion done."); printe(PT_Info); return 1; } int lockdb(int fd, int dbwrite) { int operation, locktry=1; /* lock only if configured to do so */ if (cfg.flock) { 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) { perror("unlink"); 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); } } vnstat-1.11/src/ifinfo.c0000644000000000000000000002574311571265737013707 0ustar rootroot#include "common.h" #include "misc.h" #include "dbaccess.h" #include "cfg.h" #include "ifinfo.h" int getifinfo(const char *iface) { char inface[32]; ifinfo.filled = 0; if (strcmp(iface, "default")==0) { strncpy(inface, cfg.iface, 32); } else { strncpy(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) { #if defined(__linux__) char interface[32]; FILE *fp; DIR *dp; struct dirent *di; char procline[512], temp[64]; #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) || defined(__FreeBSD_kernel__) struct ifaddrs *ifap, *ifa; #endif /* initialize list */ *ifacelist = malloc(sizeof(char)); *ifacelist[0] = '\0'; #if defined(__linux__) if ((fp=fopen(PROCNETDEV, "r"))!=NULL) { /* make list of interfaces */ while (fgets(procline, 512, fp)!=NULL) { sscanf(procline, "%64s", temp); if (isdigit(temp[(strlen(temp)-1)]) || temp[(strlen(temp)-1)]==':') { sscanf(temp, "%32[^':']s", interface); *ifacelist = realloc(*ifacelist, ( ( strlen(*ifacelist) + strlen(interface) + 2 ) * sizeof(char)) ); strncat(*ifacelist, interface, strlen(interface)); strcat(*ifacelist, " "); } } fclose(fp); return 1; } else { if ((dp=opendir("/sys/class/net"))!=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)) ); strncat(*ifacelist, di->d_name, strlen(di->d_name)); strcat(*ifacelist, " "); } } 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)) ); strncat(*ifacelist, ifa->ifa_name, strlen(ifa->ifa_name)); strcat(*ifacelist, " "); } } 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.\n", PROCNETDEV); return 0; } strncpy(ifaceid, iface, 32); strcat(ifaceid, ":"); check = 0; while (fgets(procline, 512, fp)!=NULL) { sscanf(procline, "%512s", 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(ifinfo.name, iface, 32); /* get rx and tx from procline */ proclineptr = strchr(procline, ':'); sscanf(proclineptr+1, "%64s %64s %*s %*s %*s %*s %*s %*s %64s %64s", 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(ifinfo.name, iface, 32); snprintf(path, 64, "/sys/class/net/%s/statistics", 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\n", file); return 0; } else { if (fgets(buffer, 64, fp)!=NULL) { ifinfo.rx = strtoull(buffer, (char **)NULL, 0); } else { 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\n", file); return 0; } else { if (fgets(buffer, 64, fp)!=NULL) { ifinfo.tx = strtoull(buffer, (char **)NULL, 0); } else { 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\n", file); return 0; } else { if (fgets(buffer, 64, fp)!=NULL) { ifinfo.rxp = strtoull(buffer, (char **)NULL, 0); } else { 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\n", file); return 0; } else { if (fgets(buffer, 64, fp)!=NULL) { ifinfo.txp = strtoull(buffer, (char **)NULL, 0); } else { 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); 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 ((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->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->tm_mon!=month) && (day>=cfg.monthrotate)) { rotatemonths(); } } uint64_t countercalc(uint64_t a, 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>FP32) { if (debug) printf("cc64: uint64 - %"PRIu64" + %"PRIu64" = %"PRIu64"\n", a, b, (uint64_t)FP64-a+b); return FP64-a+b; /* original counter is 32bit */ } else { if (debug) printf("cc32: uint32 - %"PRIu64" + %"PRIu64" = %"PRIu64"\n", a, b, (uint64_t)FP32-a+b); return FP32-a+b; } } } #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) || defined(__FreeBSD_kernel__) int readifaddrs(const char *iface) { struct ifaddrs *ifap, *ifa; struct if_data *ifd = NULL; int check = 0; if (getifaddrs(&ifap) < 0) { if (debug) printf("getifaddrs() failed.. exiting.\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)) { ifd = ifa->ifa_data; check = 1; break; } } freeifaddrs(ifap); if (check == 0) { if (debug) printf("Requested interface \"%s\" not found.\n", iface); return 0; } else { strncpy(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 vnstat-1.11/src/common.c0000644000000000000000000000617311571265737013721 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; /* logfile */ if (cfg.uselogging==1) { 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_Multiline: 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; } } vnstat-1.11/src/dbmerge.h0000644000000000000000000000025011565020424014012 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.11/src/vnstat.c0000644000000000000000000006071711571265737013754 0ustar rootroot/* vnStat - Copyright (c) 2002-11 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 "dbshow.h" #include "dbaccess.h" #include "dbmerge.h" #include "misc.h" #include "cfg.h" #include "vnstat.h" int main(int argc, char *argv[]) { int i; int currentarg, update=0, query=1, newdb=0, reset=0, sync=0, merged=0, savemerged=0; int active=-1, files=0, force=0, cleartop=0, rebuildtotal=0, traffic=0; int livetraffic=0, defaultiface=1, delete=0, livemode=0; char interface[32], dirname[512], nick[32]; char definterface[32], cfgfile[512], *ifacelist=NULL; time_t current; DIR *dir=NULL; struct dirent *di=NULL; noexit = 0; /* allow functions to exit in case of error */ debug = 0; /* debug disabled by default */ cfgfile[0] = '\0'; /* early check for debug and config parameter */ if (argc > 1) { for (currentarg=1; currentarg0) { setlocale(LC_ALL, cfg.locale); } else { if (getenv("LC_ALL")) { setlocale(LC_ALL, getenv("LC_ALL")); } } strncpy(interface, "default", 32); strncpy(definterface, cfg.iface, 32); strncpy(nick, "none", 32); current = time(NULL); /* init dirname */ strncpy(dirname, cfg.dbdir, 512); /* parse parameters, maybe not the best way but... */ for (currentarg=1; currentarg\n\n", VNSTATVERSION); printf(" Update:\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 top10\n"); printf(" --rebuildtotal rebuild total transfers from months\n"); 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 top10\n"); printf(" -s, --short use short output\n"); printf(" -ru, --rateunit swap configured rate unit\n"); printf(" --oneline show simple parseable format\n"); printf(" --dumpdb show database in parseable format\n"); printf(" --xml show database in xml format\n"); printf(" Misc:\n"); printf(" -i, --iface select interface (default: %s)\n", 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(" --delete delete database and stop monitoring\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"); return 0; } else if ((strcmp(argv[currentarg],"-?")==0) || (strcmp(argv[currentarg],"--help")==0)) { 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 top10\n"); printf(" -s, --short use short output\n"); printf(" -u, --update update database\n"); printf(" -i, --iface select interface (default: %s)\n", 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"); return 0; } else if ((strcmp(argv[currentarg],"-i")==0) || (strcmp(argv[currentarg],"--iface")==0)) { if (currentarg+1 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; } 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; } 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) { active=1; query=0; } else if ((strcmp(argv[currentarg],"-tr")==0) || (strcmp(argv[currentarg],"--traffic")==0)) { if (currentarg+1 1 || 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; } currentarg++; } livetraffic=1; query=0; } else if (strcmp(argv[currentarg],"--force")==0) { force=1; } else if (strcmp(argv[currentarg],"--cleartop")==0) { cleartop=1; } else if (strcmp(argv[currentarg],"--rebuildtotal")==0) { rebuildtotal=1; } else if (strcmp(argv[currentarg],"--disable")==0) { active=0; 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) { delete=1; query=0; } else if (strcmp(argv[currentarg],"--iflist")==0) { getiflist(&ifacelist); printf("Available interfaces: %s\n", ifacelist); free(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)) { reset=1; query=0; } else if (strcmp(argv[currentarg],"--sync")==0) { sync=1; 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 (!traffic && !livetraffic) { if ((dir=opendir(dirname))!=NULL) { if (debug) printf("Dir OK\n"); while ((di=readdir(dir))) { if (di->d_name[0]!='.') { strncpy(definterface, di->d_name, 32); files++; } } if (debug) printf("%d file(s) found\n", files); if (files>1) { strncpy(definterface, cfg.iface, 32); } closedir(dir); } else { printf("Error: Unable to open database directory \"%s\".\n", dirname); printf("Make sure it exists and is at least read enabled for current user.\n"); printf("Exiting...\n"); return 1; } } /* set used interface if none specified and make sure it's null terminated */ if (defaultiface) { strncpy(interface, definterface, 32); } interface[31]='\0'; /* db merge */ if (query && strstr(interface, "+")) { if (mergedb(interface, dirname)) { files = merged = 1; } else { return 1; } } /* save merged database */ if (merged && savemerged) { data.lastupdated = 0; if (writedb("mergeddb", ".", 2)) { printf("Database saved as \"mergeddb\" in the current directory.\n"); } return 0; } /* counter reset */ if (reset) { if (!spacecheck(dirname) && !force) { printf("Error: Not enough free diskspace available.\n"); return 1; } readdb(interface, dirname); data.currx=0; data.curtx=0; writedb(interface, dirname, 0); if (debug) printf("Counters reseted for \"%s\"\n", data.interface); } /* counter sync */ if (sync) { if (!spacecheck(dirname) && !force) { printf("Error: Not enough free diskspace available.\n"); return 1; } if (!synccounters(interface, dirname)) { return 1; } if (debug) printf("Counters synced for \"%s\"\n", data.interface); } /* delete */ if (delete) { if (force) { if (checkdb(interface, dirname)) { if (removedb(interface, dirname)) { printf("Database for interface \"%s\" deleted.\n", interface); printf("The interface will no longer be monitored.\n"); return 0; } else { printf("Error: Deleting database for interface \"%s\" failed.\n", interface); return 1; } } else { printf("Error: No database found for interface \"%s\".\n", interface); return 1; } } else { printf("Warning:\nThe current option would delete the database for \"%s\".\n", interface); printf("Use --force in order to really do that.\n"); return 1; } } /* clear top10 */ if (cleartop) { if (!spacecheck(dirname) && !force) { printf("Error: Not enough free diskspace available.\n"); return 1; } if (force) { readdb(interface, dirname); 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(interface, dirname, 0); printf("Top10 cleared for interface \"%s\".\n", data.interface); query=0; } else { printf("Warning:\nThe current option would clear the top10 for \"%s\".\n", interface); printf("Use --force in order to really do that.\n"); return 1; } } /* rebuild total */ if (rebuildtotal) { if (!spacecheck(dirname)) { printf("Error: Not enough free diskspace available.\n"); return 1; } if (force) { readdb(interface, dirname); data.totalrx=data.totaltx=data.totalrxk=data.totaltxk=0; for (i=0; i<=29; 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(interface, dirname, 0); printf("Total transfer rebuild completed for interface \"%s\".\n", data.interface); query=0; } else { printf("Warning:\nThe current option would rebuild total tranfers for \"%s\".\n", interface); printf("Use --force in order to really do that.\n"); return 1; } } /* enable & disable */ if (active==1) { if (!spacecheck(dirname) && !force) { printf("Error: Not enough free diskspace available.\n"); return 1; } newdb=readdb(interface, dirname); if (!data.active && !newdb) { data.active=1; writedb(interface, dirname, 0); if (debug) printf("Interface \"%s\" enabled.\n", data.interface); } else if (!newdb) { printf("Interface \"%s\" is already enabled.\n", data.interface); } } else if (active==0) { if (!spacecheck(dirname) && !force) { printf("Error: Not enough free diskspace available.\n"); return 1; } newdb=readdb(interface, dirname); if (data.active && !newdb) { data.active=0; writedb(interface, dirname, 0); if (debug) printf("Interface \"%s\" disabled.\n", data.interface); } else if (!newdb) { printf("Interface \"%s\" is already disabled.\n", data.interface); } } /* update */ if (update) { /* check that there's some free diskspace left */ if (!spacecheck(dirname) && !force) { printf("Error: Not enough free diskspace available.\n"); return 1; } /* update every file if -i isn't specified */ if (defaultiface) { dir=opendir(dirname); files=0; while ((di=readdir(dir))) { if (di->d_name[0]!='.') { files++; strncpy(interface, di->d_name, 32); if (debug) printf("\nProcessing file \"%s/%s\"...\n", dirname, interface); newdb=readdb(interface, dirname); if (data.active) { /* skip interface if not available */ if (!getifinfo(data.interface)) { if (debug) printf("Interface \"%s\" not available, skipping.\n", data.interface); continue; } parseifinfo(newdb); /* check that the time is correct */ if ((current>=data.lastupdated) || force) { writedb(interface, dirname, 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"); return 1; } else { if (debug) printf("\"%s\" not updated, %s > %s.\n", data.interface, (char*)asctime(localtime(&data.lastupdated)), (char*)asctime(localtime(¤t))); } } } else { if (debug) printf("Disabled interface \"%s\" not updated.\n", data.interface); } } } closedir(dir); if (files==0) { // printf("No database found.\n"); update=0; } /* update only selected file */ } else { newdb=readdb(interface, dirname); if (data.active) { if (!getifinfo(data.interface) && !force) { getiflist(&ifacelist); printf("Error: Interface \"%s\" couldn't be found.\n Only available interfaces can be added for monitoring.\n", data.interface); printf("\n The following interfaces are currently available:\n %s\n", ifacelist); free(ifacelist); return 1; } parseifinfo(newdb); if ((current>=data.lastupdated) || force) { if (strcmp(nick, "none")!=0) strncpy(data.nick, nick, 32); writedb(interface, dirname, 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"); return 1; } else { if (debug) printf("\"%s\" not updated, %s > %s.\n", data.interface, (char*)asctime(localtime(&data.lastupdated)), (char*)asctime(localtime(¤t))); } } } else { if (debug) printf("Disabled interface \"%s\" not updated.\n", data.interface); } } } /* show databases */ if (query) { /* show all interfaces if -i isn't specified */ if (defaultiface) { if (files==0) { /* printf("No database found.\n"); */ query=0; } else if ((cfg.qmode==0 || cfg.qmode==8) && (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 { printf("\n", VNSTATVERSION, XMLVERSION); } dir=opendir(dirname); while ((di=readdir(dir))) { if (di->d_name[0]!='.') { strncpy(interface, di->d_name, 32); if (debug) printf("\nProcessing file \"%s/%s\"...\n", dirname, interface); newdb=readdb(interface, dirname); if (!newdb) { if (cfg.qmode==0) { showdb(5); } else { showxml(); } } } } closedir(dir); if (cfg.qmode==8) { printf("\n"); } /* show in qmode if there's only one file or qmode!=0 */ } else { if (!merged) { newdb=readdb(definterface, dirname); } if (!newdb) { 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) { showdb(cfg.qmode); } else { printf("\n", VNSTATVERSION, XMLVERSION); showxml(); printf("\n"); } } } /* show only specified file */ } else { if (!merged) { newdb=readdb(interface, dirname); } if (!newdb) { 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) { showdb(cfg.qmode); } else { printf("\n", VNSTATVERSION, XMLVERSION); showxml(); printf("\n"); } } } } /* calculate traffic */ if (traffic) { trafficmeter(interface, cfg.sampletime); } /* live traffic */ if (livetraffic) { livetrafficmeter(interface, livemode); } /* if nothing was shown previously */ if (!query && !update && !reset && !sync && active==-1 && !cleartop && !rebuildtotal && !traffic && !livetraffic) { /* give more help if there's no database */ if (files==0) { getiflist(&ifacelist); 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 -u -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", ifacelist); free(ifacelist); } else { printf("Nothing to do. Use --help for help.\n"); } } /* cleanup */ ibwflush(); return 0; } 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; } vnstat-1.11/src/traffic.c0000644000000000000000000001747311571265737014054 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;irxc)) { rxmin = rxc; } if ((txmin==-1.0) || (txmin>txc)) { txmin = txc; } if (rxmaxrxpc)) { rxpmin = rxpc; } if ((txpmin==FP64) || (txpmin>txpc)) { txpmin = txpc; } if (rxpmax1 && cfg.ostyle!=4) { if (debug) { printf("\nlinelen: %d\n", len); } else { for (i=0;i10) { 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", getrate(0, rxmax, LIVETIME, 13)); printf(" | %s\n", getrate(0, txmax, LIVETIME, 13)); printf(" average %s", getrate(0, rintf(rxtotal/(float)1024), timespent, 13)); printf(" | %s\n", getrate(0, rintf(txtotal/(float)1024), timespent, 13)); printf(" min %s", getrate(0, rxmin, LIVETIME, 13)); printf(" | %s\n", getrate(0, 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, txpmax); 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, txpmin); 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.11/src/vnstatd.c0000644000000000000000000003775411571265737014125 0ustar rootroot/* vnStat daemon - Copyright (c) 2008-11 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 "dbaccess.h" #include "dbcache.h" #include "misc.h" #include "cfg.h" #include "vnstatd.h" int main(int argc, char *argv[]) { int currentarg, running = 1, updateinterval, dbcount, dodbsave, rundaemon; int dbsaved = 1, showhelp = 1, sync = 0, saveinterval, forcesave = 0, noadd = 0; uint32_t dbhash = 0; char cfgfile[512], dirname[512]; DIR *dir; struct dirent *di; datanode *datalist; time_t current, prevdbupdate, prevdbsave; noexit = 1; /* disable exits in functions */ debug = 0; /* debug disabled by default */ rundaemon = 0; /* daemon disabled by default */ cfgfile[0] = '\0'; prevdbupdate = prevdbsave = dbcount = 0; /* 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\n\n", VNSTATVERSION); printf(" -d, --daemon fork process to background\n"); printf(" -n, --nodaemon stay in foreground attached to the terminal\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(" --config select used config file\n\n"); printf(" --noadd don't add found interfaces if no dbs are found\n"); printf("See also \"man vnstatd\".\n"); return 0; } /* check that directory is ok */ if ((dir=opendir(dirname))==NULL) { printf("Error: Unable to open database directory \"%s\".\n", dirname); printf("Make sure it exists and is at least read enabled for current user.\n"); printf("Exiting...\n"); return 1; } else { /* check if there's something to work with */ dbcount = 0; while ((di=readdir(dir))) { if (di->d_name[0]!='.') { dbcount++; } } closedir(dir); if (dbcount==0) { if (noadd) { printf("Zero database found, exiting.\n"); return 1; } else { if (!spacecheck(dirname)) { printf("Error: Not enough free diskspace available, exiting.\n"); return 1; } printf("Zero database found, adding available interfaces...\n"); if (!addinterfaces(dirname)) { printf("Nothing to do, exiting.\n"); return 1; } dbcount = 0; } } else { /* set counter back to zero so that dbs will be cached later */ dbcount = 0; } } /* init signal traps */ intsignal = 0; if (signal(SIGINT, sighandler) == SIG_ERR) { perror("signal"); return 1; } if (signal(SIGHUP, sighandler) == SIG_ERR) { perror("signal"); return 1; } if (signal(SIGTERM, sighandler) == SIG_ERR) { perror("signal"); return 1; } /* start as daemon if needed and debug isn't enabled */ if (rundaemon && !debug) { noexit++; daemonize(); } /* main loop */ while(running) { /* keep track of time */ current = time(NULL); /* track interface status only if at least one database exists */ if (dbcount!=0) { dbhash = dbcheck(dbhash, &forcesave); } if ((current-prevdbupdate)>=updateinterval) { updateinterval = cfg.updateinterval; if (debug) { cacheshow(); } /* read database list if cache is empty */ if (dbcount==0) { if ((dir=opendir(dirname))!=NULL) { while ((di=readdir(dir))) { if (di->d_name[0]!='.') { if (debug) { printf("\nProcessing file \"%s/%s\"...\n", dirname, di->d_name); } if (!cacheadd(di->d_name, sync)) { snprintf(errorstring, 512, "Cache memory allocation failed, exiting."); printe(PT_Error); /* clean daemon stuff before exit */ if (rundaemon && !debug) { close(pidfile); unlink(cfg.pidfile); } ibwflush(); return 1; } dbcount++; } } closedir(dir); 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 (dbcount) { updateinterval = 0; intsignal = 42; prevdbsave = current; /* list monitored interfaces to log */ cachestatus(); } else { updateinterval = 120; } } else { snprintf(errorstring, 512, "Unable to access database directory \"%s\", exiting.", dirname); printe(PT_Error); /* clean daemon stuff before exit */ if (rundaemon && !debug) { close(pidfile); unlink(cfg.pidfile); } ibwflush(); return 1; } /* update data cache */ } else { prevdbupdate = current; datalist = dataptr; /* alter save interval if all interfaces are unavailable */ if (cacheactivecount()) { saveinterval = cfg.saveinterval*60; } else { saveinterval = cfg.offsaveinterval*60; } if ((current-prevdbsave)>=(saveinterval) || forcesave) { dodbsave = 1; forcesave = 0; prevdbsave = current; } else { dodbsave = 0; } /* check all list entries*/ while (datalist!=NULL) { if (debug) { printf("d: processing %s (%d)...\n", datalist->data.interface, dodbsave); } /* get data from cache if available */ if (cacheget(datalist)==0) { /* try to read data from file if not cached */ if (readdb(datalist->data.interface, dirname)!=-1) { /* mark cache as filled on read success and force interface status update */ datalist->filled = 1; dbhash = 0; } else { datalist = datalist->next; continue; } } /* get info if interface has been marked as active */ if (data.active) { if (getifinfo(data.interface)) { if (datalist->sync) { /* if --sync was used during startup */ data.currx = ifinfo.rx; data.curtx = ifinfo.tx; datalist->sync = 0; } else { parseifinfo(0); } } else { /* 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); } } else if (debug) { printf("d: interface is disabled\n"); } /* check that the time is correct */ if (current>=data.lastupdated) { data.lastupdated = current; cacheupdate(); } else { /* 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>(current+86400)) { snprintf(errorstring, 512, "Interface \"%s\" has previous update date too much in the future, exiting.", data.interface); printe(PT_Error); /* clean daemon stuff before exit */ if (rundaemon && !debug) { close(pidfile); unlink(cfg.pidfile); } ibwflush(); return 1; } else { datalist = datalist->next; continue; } } /* write data to file if now is the time for it */ if (dodbsave) { if (checkdb(datalist->data.interface, dirname)) { if (spacecheck(dirname)) { if (writedb(datalist->data.interface, dirname, 0)) { if (!dbsaved) { snprintf(errorstring, 512, "Database write possible again."); printe(PT_Info); dbsaved = 1; } } else { if (dbsaved) { snprintf(errorstring, 512, "Unable to write database, continuing with cached data."); printe(PT_Error); dbsaved = 0; } } } else { /* show freespace error only once */ if (dbsaved) { snprintf(errorstring, 512, "Free diskspace check failed, unable to write database, continuing with cached data."); printe(PT_Error); dbsaved = 0; } } } else { /* 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.", datalist->data.interface); printe(PT_Info); datalist = cacheremove(datalist->data.interface); dbcount--; cachestatus(); continue; } } datalist = datalist->next; } if (debug) { printf("\n"); } } } /* dbupdate */ if (running && intsignal==0) { sleep(cfg.pollinterval); } /* take actions from signals */ if (intsignal) { switch (intsignal) { case SIGHUP: snprintf(errorstring, 512, "SIGHUP received, flushing data to disk and reloading config."); printe(PT_Info); cacheflush(dirname); dbcount = 0; ibwflush(); if (loadcfg(cfgfile)) { strncpy(dirname, cfg.dbdir, 512); } break; case SIGINT: snprintf(errorstring, 512, "SIGINT received, exiting."); printe(PT_Info); running = 0; break; case SIGTERM: snprintf(errorstring, 512, "SIGTERM received, exiting."); printe(PT_Info); running = 0; break; case 42: break; default: snprintf(errorstring, 512, "Unkown signal %d received, ignoring.", intsignal); printe(PT_Info); break; } intsignal = 0; } } /* while */ cacheflush(dirname); ibwflush(); /* clean daemon stuff */ if (rundaemon && !debug) { close(pidfile); unlink(cfg.pidfile); } return 0; } void daemonize(void) { int i; char str[10]; if (getppid()==1) { return; /* already a daemon */ } i = (int)fork(); if (i<0) { /* fork error */ perror("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.", VNSTATVERSION); 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("pidfile"); snprintf(errorstring, 512, "pidfile failed, exiting."); printe(PT_Error); exit(EXIT_FAILURE); /* can't open */ } if (lockf(pidfile,F_TLOCK,0)<0) { perror("pidfile lock"); snprintf(errorstring, 512, "pidfile lock failed, exiting."); 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("dup(stdout)"); snprintf(errorstring, 512, "dup(stdout) failed, exiting."); printe(PT_Error); exit(EXIT_FAILURE); } /* stderr */ if (dup(i) < 0) { perror("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("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 lockfile */ if (write(pidfile,str,strlen(str)) < 0) { perror("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; /* get list of currently visible interfaces */ if (getiflist(&ifacelist)==0) { return 0; } if (strlen(ifacelist)<2) { return 0; } if (debug) printf("Interface list: \"%s\"\n", ifacelist); while (sscanf(ifacelist+index, "%32s", 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(data.interface, interface, 32); strncpy(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++; printf("\"%s\" added, %d Mbit bandwidth limit.\n", interface, ibwget(interface)); } if (count==1) { printf("-> %d interface added.", count); } else { printf("-> %d interfaces added.", count); } if (count) { printf(" Limits can be modified using the configuration file."); } printf("\n"); free(ifacelist); return count; } vnstat-1.11/src/image.h0000644000000000000000000000212011570527576013505 0ustar rootroot#ifndef IMAGE_H #define IMAGE_H void colorinit(void); void colorinitcheck(char *color, int value, char *cfgtext, int *rgb); void layoutinit(char *title, int width, int height, int showheader, int showedge); void drawlegend(int x, int y); void drawbar(int x, int y, int len, uint64_t rx, int rxk, uint64_t tx, int txk, uint64_t max); void drawpole(int x, int y, int len, uint64_t rx, uint64_t tx, uint64_t max); void drawdonut(int x, int y, float rxp, float txp); void drawhours(int x, int y, int rate); void drawsummary(int type, int showheader, int showedge, int rate); void drawoldsummary(int type, int showheader, int showedge, int rate); void drawhourly(int showheader, int showedge, int rate); void drawdaily(int showheader, int showedge); void drawmonthly(int showheader, int showedge); void drawtop(int showheader, int showedge); void addtraffic(uint64_t *destmb, int *destkb, uint64_t srcmb, int srckb); 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.11/src/dbxml.c0000644000000000000000000000513611571265737013535 0ustar rootroot#include "common.h" #include "dbxml.h" void showxml(void) { int i; 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); 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"); 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"); 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"); 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"); printf(" \n"); printf(" \n"); } void xmldate(time_t *date, int type) { struct tm *d; char *buffer; char *type1 = "%Y%m%d"; char *type2 = "%Y%m%d"; char *type3 = "%Y%m"; d = localtime(date); if (type == 1) { buffer = malloc(strlen(type1)+3); strftime(buffer, strlen(type1)+3, type1, d); printf("%s", buffer); free(buffer); } else if (type == 2) { buffer = malloc(strlen(type2)+3); strftime(buffer, strlen(type2)+3, type2, d); printf("%s", buffer); free(buffer); } else if (type == 3) { buffer = malloc(strlen(type3)+3); strftime(buffer, strlen(type3)+3, type3, d); printf("%s", buffer); free(buffer); } } vnstat-1.11/src/cfg.c0000644000000000000000000004642511570527576013175 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("# 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 = dumpdb, 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("# 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("# 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("# 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; char buffer[512]; int i, j, k, linelen, cfglen, tryhome; char value[512], cfgline[512]; struct cfgsetting cset[] = { { "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 }, { "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 }, { "UpdateInterval", 0, &cfg.updateinterval, 0, 0 }, { "PollInterval", 0, &cfg.pollinterval, 0, 0 }, { "SaveInterval", 0, &cfg.saveinterval, 0, 0 }, { "OfflineSaveInterval", 0, &cfg.offsaveinterval, 0, 0 }, { "SaveOnStatusChange", 0, &cfg.savestatus, 0, 0 }, { "UseLogging", 0, &cfg.uselogging, 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 } }; ifacebw = NULL; /* clear buffer */ for (i=0; i<512; i++) { buffer[i] = '\0'; } /* load default config */ defaultcfg(); /* 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\".\n", cfgfile); printe(PT_Error); return 0; } } else { if (getenv("HOME")) { strncpy(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 (debug) printf("Config file: none\n"); return 1; } } rewind(fd); /* parse every config file line */ while (!feof(fd)) { cfgline[0] = '\0'; /* get current line */ if (fgets(cfgline, 512, fd)==NULL) { break; } linelen = (int)strlen(cfgline); if (linelen>2 && cfgline[0]!='#') { 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) ) { /* clear value buffer */ for (j=0; j<512; j++) { value[j]='\0'; } /* search value */ j = 0; for (k=cfglen; k0) { strncpy(cset[i].locc, value, cset[i].namelen); cset[i].locc[cset[i].namelen-1]='\0'; if (debug) printf(" c: %s -> \"%s\": \"%s\"\n", cfgline, cset[i].name, cset[i].locc); } else if (isdigit(value[0])) { *cset[i].loci = atoi(value); if (debug) printf(" i: %s -> \"%s\": %d\n", cfgline, cset[i].name, *cset[i].loci); } else { continue; } cset[i].found = 1; break; } } /* if */ } /* for */ if ((debug) && (!cset[i].found) && (strncasecmp(cfgline, "MaxBW", 5)!=0)) printf("Unknown configuration line: %s", cfgline); } /* if */ } /* while */ /* search for interface specific limits */ ibwcfgread(fd); fclose(fd); if (debug) ibwlist(); /* 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<0 || 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>10000) { 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(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.logfile[0] != '/') { strncpy(cfg.logfile, LOGFILE, 512); snprintf(errorstring, 512, "LogFile doesn't start with \"/\", resetting to default."); printe(PT_Config); } if (cfg.pidfile[0] != '/') { strncpy(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); } } void defaultcfg(void) { ibwflush(); cfg.bvar = BVAR; cfg.qmode = DEFQMODE; cfg.sampletime = DEFSAMPTIME; cfg.monthrotate = MONTHROTATE; cfg.unit = UNITMODE; cfg.ostyle = OSTYLE; cfg.rateunit = RATEUNIT; cfg.maxbw = DEFMAXBW; cfg.spacecheck = USESPACECHECK; cfg.flock = USEFLOCK; cfg.hourlyrate = HOURLYRATE; cfg.summaryrate = SUMMARYRATE; cfg.slayout = SUMMARYLAYOUT; cfg.traflessday = TRAFLESSDAY; strncpy(cfg.dbdir, DATABASEDIR, 512); strncpy(cfg.iface, DEFIFACE, 32); strncpy(cfg.locale, LOCALE, 32); strncpy(cfg.dformat, DFORMAT, 64); strncpy(cfg.mformat, MFORMAT, 64); strncpy(cfg.tformat, TFORMAT, 64); strncpy(cfg.hformat, HFORMAT, 64); strncpy(cfg.rxchar, RXCHAR, 1); strncpy(cfg.txchar, TXCHAR, 1); strncpy(cfg.rxhourchar, RXHOURCHAR, 1); strncpy(cfg.txhourchar, TXHOURCHAR, 1); cfg.updateinterval = UPDATEINTERVAL; cfg.pollinterval = POLLINTERVAL; cfg.saveinterval = SAVEINTERVAL; cfg.offsaveinterval = OFFSAVEINTERVAL; cfg.savestatus = SAVESTATUS; cfg.uselogging = USELOGGING; strncpy(cfg.logfile, LOGFILE, 512); strncpy(cfg.pidfile, PIDFILE, 512); cfg.transbg = TRANSBG; strncpy(cfg.cbg, CBACKGROUND, 7); strncpy(cfg.cedge, CEDGE, 7); strncpy(cfg.cheader, CHEADER, 7); strncpy(cfg.cheadertitle, CHEADERTITLE, 7); strncpy(cfg.cheaderdate, CHEADERDATE, 7); strncpy(cfg.ctext, CTEXT, 7); strncpy(cfg.cline, CLINE, 7); strncpy(cfg.clinel, CLINEL, 7); strncpy(cfg.crx, CRX, 7); strncpy(cfg.crxd, CRXD, 7); strncpy(cfg.ctx, CTX, 7); strncpy(cfg.ctxd, CTXD, 7); } int ibwadd(const char *iface, int 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(n->interface, iface, 32); n->limit = limit; } 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(n->interface, iface, 32); n->limit = limit; } return 1; } void ibwlist(void) { int i=1; ibwnode *p = ifacebw; if (p == NULL) { printf("ibw list is empty.\n"); } else { printf("ibw:\n"); while (p != NULL) { printf(" %2d: \"%s\" \"%d\"\n", i, p->interface, p->limit); p = p->next; i++; } } } int ibwget(const char *iface) { ibwnode *p = ifacebw; /* search for interface specific limit */ while (p != NULL) { if (strcasecmp(p->interface, iface)==0) { if (p->limit>0) { return p->limit; } else { return -1; } } p = p->next; } /* return default limit if specified */ if (cfg.maxbw>0) { return cfg.maxbw; } else { return -1; } } 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, 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]!='#') { if (strncasecmp(cfgline, "MaxBW", 5)==0) { /* clear name and value buffers */ for (j=0; j<512; j++) { name[j]=value[j]='\0'; } /* get interface name */ j=0; for (i=5; i10000) { snprintf(errorstring, 512, "Invalid value \"%d\" for MaxBW%s, ignoring parameter.", ivalue, name); printe(PT_Config); } else { ibwadd(name, ivalue); } } } } return count; } vnstat-1.11/src/dbshow.c0000644000000000000000000007521311571265737013720 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); } else { switch(qmode) { case 0: showsummary(); break; case 1: showdays(); break; case 2: showmonths(); break; case 3: showtop(); break; case 4: dumpdb(); 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, t_rx, t_tx; int t_rxk, t_txk; time_t current, yesterday; current=time(NULL); yesterday=current-86400; e_rx=e_tx=t_rx=t_tx=t_rxk=t_txk=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(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(" %8s %s", datebuff, getvalue(data.month[1].rx, data.month[1].rxk, 11, 1)); } else { printf("%-11s %s", 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(" %8s %s", datebuff, getvalue(data.month[0].rx, data.month[0].rxk, 11, 1)); } else { printf("%-11s %s", 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(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("%9s %s", daytemp, getvalue(data.day[1].rx, data.day[1].rxk, 11, 1)); } else { printf("%-11s %s", 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("%9s %s", daytemp2, getvalue(data.day[0].rx, data.day[0].rxk, 11, 1)); } else { printf("%-11s %s", 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, t_rx, t_tx; int t_rxk, t_txk; time_t current, yesterday; current=time(NULL); yesterday=current-86400; e_rx=e_tx=t_rx=t_tx=t_rxk=t_txk=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(" %8s %s", datebuff, getvalue(data.month[1].rx, data.month[1].rxk, 11, 1)); } else { printf(" %-11s %s", 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(" %8s %s", datebuff, getvalue(data.month[0].rx, data.month[0].rxk, 11, 1)); } else { printf(" %-11s %s", 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(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(daytemp, "yesterday", 32); } if (data.day[1].date!=0) { printf(" %9s %s",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(" %9s %s", 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, t_tx, max; int t_rxk, t_txk; 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 / 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(" %9s %s", datebuff, getvalue(data.day[i].rx, data.day[i].rxk, 11, 1)); } else { printf(" %-11s %s", 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, t_tx, max; int t_rxk, t_txk; 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 / 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(" %9s %s", datebuff, getvalue(data.month[i].rx, data.month[i].rxk, 11, 1)); } else { printf(" %-11s %s", 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, t_tx, max; int t_rxk, t_txk; t_rx=t_tx=t_rxk=t_txk=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 %-11s %s", i+1, 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; 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 */ 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]=' '; } } /* structure */ snprintf(matrix[11], 81, " -+--------------------------------------------------------------------------->"); if (cfg.unit==0) { snprintf(matrix[14], 81, " h rx (KiB) tx (KiB) h rx (KiB) tx (KiB) h rx (KiB) tx (KiB)"); } else { snprintf(matrix[14], 81, " h rx (KB) tx (KB) h rx (KB) tx (KB) h rx (KB) tx (KB)"); } 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 dumpdb(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); } } void 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) { len=((rx+tx)/(float)max)*len; } 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.11/src/traffic.h0000644000000000000000000000021511565020424014024 0ustar rootroot#ifndef TRAFFIC_H #define TRAFFIC_H void trafficmeter(char iface[], int sampletime); void livetrafficmeter(char iface[], int mode); #endif vnstat-1.11/src/vnstatd.h0000644000000000000000000000015311570527576014112 0ustar rootroot#ifndef VNSTATD_H #define VNSTATD_H void daemonize(void); int addinterfaces(const char *dirname); #endif vnstat-1.11/src/cfg.h0000644000000000000000000000057711565020424013160 0ustar rootroot#ifndef CFG_H #define CFG_H void printcfgfile(void); int loadcfg(const char *cfgfile); void validatecfg(void); void defaultcfg(void); int ibwadd(const char *iface, int limit); void ibwlist(void); int ibwget(const char *iface); void ibwflush(void); int ibwcfgread(FILE *fd); struct cfgsetting { const char *name; char *locc; short *loci; short namelen; short found; }; #endif vnstat-1.11/src/vnstati.h0000644000000000000000000000120311565017537014107 0ustar rootroot#ifndef VNSTATI_H #define VNSTATI_H #include /* fstat() */ #include /* libgd2-dev libgd2 */ #include /* gdFontGetTiny() */ #include /* gdFontGetSmall() */ #include /* gdFontGetMediumBold() */ #include /* gdFontGetLarge() */ #include /* gdFontGetGiant() */ #define YBEGINOFFSET -1 #define YENDOFFSET 6 #define DOUTRAD 49 #define DINRAD 15 /* global variables for vnstati */ gdImagePtr im; int cbackground, cedge, cheader, cheadertitle, cheaderdate, ctext, cline, clinel, cvnstat; int crx, crxd, ctx, ctxd, cbgoffset; time_t current; #endif vnstat-1.11/src/vnstat.h0000644000000000000000000000014511565020270013726 0ustar rootroot#ifndef VNSTAT_H #define VNSTAT_H int synccounters(const char *iface, const char *dirname); #endif vnstat-1.11/src/misc.h0000644000000000000000000000070711565017537013362 0ustar rootroot#ifndef MISC_H #define MISC_H #define UNITCOUNT 4 int kerneltest(void); int spacecheck(char *path); void sighandler(int); int getbtime(void); void addtraffic(uint64_t *destmb, int *destkb, uint64_t srcmb, int srckb); 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); uint64_t getscale(uint64_t kb); char *getunit(int index); char *getrateunit(int unit, int index); #endif vnstat-1.11/src/image.c0000644000000000000000000012643711571265737013521 0ustar rootroot#include "common.h" #include "vnstati.h" #include "misc.h" #include "image.h" void colorinit(void) { int rgb[3]; /* text, edge and header colors */ hextorgb(cfg.ctext, rgb); ctext = gdImageColorAllocate(im, rgb[0], rgb[1], rgb[2]); colorinitcheck("ctext", ctext, cfg.ctext, rgb); hextorgb(cfg.cedge, rgb); cedge = gdImageColorAllocate(im, rgb[0], rgb[1], rgb[2]); colorinitcheck("cedge", cedge, cfg.cedge, rgb); hextorgb(cfg.cheader, rgb); cheader = gdImageColorAllocate(im, rgb[0], rgb[1], rgb[2]); colorinitcheck("cheader", cheader, cfg.cheader, rgb); hextorgb(cfg.cheadertitle, rgb); cheadertitle = gdImageColorAllocate(im, rgb[0], rgb[1], rgb[2]); colorinitcheck("cheadertitle", cheadertitle, cfg.cheadertitle, rgb); hextorgb(cfg.cheaderdate, rgb); cheaderdate = gdImageColorAllocate(im, rgb[0], rgb[1], rgb[2]); colorinitcheck("cheaderdate", cheaderdate, cfg.cheaderdate, rgb); /* lines */ hextorgb(cfg.cline, rgb); cline = gdImageColorAllocate(im, rgb[0], rgb[1], rgb[2]); colorinitcheck("cline", cline, cfg.cline, rgb); if (cfg.clinel[0] == '-') { modcolor(rgb, 50, 1); } else { hextorgb(cfg.clinel, rgb); } clinel = gdImageColorAllocate(im, rgb[0], rgb[1], rgb[2]); colorinitcheck("clinel", clinel, cfg.clinel, rgb); /* background */ hextorgb(cfg.cbg, rgb); cbackground = gdImageColorAllocate(im, rgb[0], rgb[1], rgb[2]); colorinitcheck("cbackground", cbackground, cfg.cbg, rgb); modcolor(rgb, -35, 0); cvnstat = gdImageColorAllocate(im, rgb[0], rgb[1], rgb[2]); colorinitcheck("cvnstat", cvnstat, cfg.cbg, rgb); hextorgb(cfg.cbg, rgb); modcolor(rgb, -15, 0); cbgoffset = gdImageColorAllocate(im, rgb[0], rgb[1], rgb[2]); colorinitcheck("cbgoffset", cbgoffset, cfg.cbg, rgb); /* rx */ hextorgb(cfg.crx, rgb); crx = gdImageColorAllocate(im, rgb[0], rgb[1], rgb[2]); colorinitcheck("crx", crx, cfg.crx, rgb); if (cfg.crxd[0] == '-') { modcolor(rgb, -50, 1); } else { hextorgb(cfg.crxd, rgb); } crxd = gdImageColorAllocate(im, rgb[0], rgb[1], rgb[2]); colorinitcheck("crxd", crxd, cfg.crxd, rgb); /* tx */ hextorgb(cfg.ctx, rgb); ctx = gdImageColorAllocate(im, rgb[0], rgb[1], rgb[2]); colorinitcheck("ctx", ctx, cfg.ctx, rgb); if (cfg.ctxd[0] == '-') { modcolor(rgb, -50, 1); } else { hextorgb(cfg.ctxd, rgb); } ctxd = gdImageColorAllocate(im, rgb[0], rgb[1], rgb[2]); colorinitcheck("ctxd", 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(char *title, int width, int height, int showheader, int showedge) { struct tm *d; char datestring[64]; /* get time in given format */ d = localtime(&data.lastupdated); strftime(datestring, 64, cfg.hformat, d); /* background, edges */ gdImageFill(im, 0, 0, cbackground); if (showedge) { gdImageRectangle(im, 0, 0, width-1, height-1, cedge); } /* titlebox, title, date */ if (showheader) { if (showedge) { gdImageFilledRectangle(im, 3, 3, width-4, 24, cheader); gdImageString(im, gdFontGetGiant(), 12, 6, (unsigned char*)title, cheadertitle); gdImageString(im, gdFontGetTiny(), width-(strlen(datestring)*gdFontGetTiny()->w+12), 10, (unsigned char*)datestring, cheaderdate); } else { gdImageFilledRectangle(im, 2, 2, width-3, 24, cheader); gdImageString(im, gdFontGetGiant(), 12, 5, (unsigned char*)title, cheadertitle); gdImageString(im, gdFontGetTiny(), width-(strlen(datestring)*gdFontGetTiny()->w+12), 9, (unsigned char*)datestring, cheaderdate); } } else { if (showedge) { gdImageString(im, gdFontGetTiny(), 6, height-13, (unsigned char*)datestring, cvnstat); } else { gdImageString(im, gdFontGetTiny(), 5, height-12, (unsigned char*)datestring, cvnstat); } } /* generator */ if (showedge) { gdImageString(im, gdFontGetTiny(), width-115, height-13, (unsigned char*)"vnStat / Teemu Toivola", cvnstat); } else { gdImageString(im, gdFontGetTiny(), width-114, height-12, (unsigned char*)"vnStat / Teemu Toivola", cvnstat); } } void drawlegend(int x, int y) { /* color legend */ gdImageString(im, gdFontGetSmall(), x, y, (unsigned char*)"rx tx", ctext); gdImageFilledRectangle(im, x-12, y+4, x-6, y+10, crx); gdImageRectangle(im, x-12, y+4, x-6, y+10, ctext); gdImageFilledRectangle(im, x+30, y+4, x+36, y+10, ctx); gdImageRectangle(im, x+30, y+4, x+36, y+10, ctext); } void drawbar(int x, int y, int len, uint64_t rx, int rxk, uint64_t tx, int txk, uint64_t max) { int 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) { len=((rx+tx)/(float)max)*len; } if (len!=0) { if (tx>rx) { l=rintf((rx/(float)(rx+tx)*len)); gdImageFilledRectangle(im, x, y+YBEGINOFFSET, x+l, y+YENDOFFSET, crx); gdImageRectangle(im, x, y+YBEGINOFFSET, x+l, y+YENDOFFSET, crxd); gdImageFilledRectangle(im, x+l, y+YBEGINOFFSET, x+len, y+YENDOFFSET, ctx); gdImageRectangle(im, x+l, y+YBEGINOFFSET, x+len, y+YENDOFFSET, ctxd); } else { l=rintf((tx/(float)(rx+tx)*len)); gdImageFilledRectangle(im, x, y+YBEGINOFFSET, x+(len-l), y+YENDOFFSET, crx); gdImageRectangle(im, x, y+YBEGINOFFSET, x+(len-l), y+YENDOFFSET, crxd); gdImageFilledRectangle(im, x+(len-l), y+YBEGINOFFSET, x+len, y+YENDOFFSET, ctx); gdImageRectangle(im, x+(len-l), y+YBEGINOFFSET, x+len, y+YENDOFFSET, ctxd); } } } void drawpole(int x, int y, int len, uint64_t rx, uint64_t tx, uint64_t max) { int l; l = (rx/(float)max)*len; gdImageFilledRectangle(im, x, y+(len-l), x+7, y+len, crx); l = (tx/(float)max)*len; gdImageFilledRectangle(im, x+5, y+(len-l), x+12, y+len, ctx); } void drawdonut(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(im, x, y, DOUTRAD, DOUTRAD, 0, 360, cbgoffset, 0); if ( (int)(rxp + txp) > 0 ) { gdImageFilledArc(im, x, y, DOUTRAD, DOUTRAD, 270, 270+txarc, ctx, 0); gdImageFilledArc(im, x, y, DOUTRAD, DOUTRAD, 270, 270+txarc, ctxd, gdEdged|gdNoFill); gdImageFilledArc(im, x, y, DOUTRAD, DOUTRAD, 270+txarc, 270+txarc+rxarc, crx, 0); gdImageFilledArc(im, x, y, DOUTRAD, DOUTRAD, 270+txarc, 270+txarc+rxarc, crxd, gdEdged|gdNoFill); gdImageFilledArc(im, x, y, DINRAD, DINRAD, 270, 270+txarc, ctxd, gdEdged|gdNoFill); gdImageFilledArc(im, x, y, DINRAD, DINRAD, 270+txarc, 270+txarc+rxarc, crxd, gdEdged|gdNoFill); } gdImageFilledArc(im, x, y, DINRAD-2, DINRAD-2, 0, 360, cbackground, 0); } void drawhours(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; current = time(NULL); chour = localtime(¤t)->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 ((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(im, x+36, y+124-s, x+460, y+124-s, cline); gdImageLine(im, x+36, y+124-((s+prev)/2), x+460, y+124-((s+prev)/2), clinel); gdImageString(im, gdFontGetTiny(), x+16, y+121-s, (unsigned char*)getimagevalue(scaleunit*i, 3, rate), ctext); prev = s; } s = 121 * ((scaleunit * i) / (float)max); if ( ((s+prev)/2) <= 128 ) { gdImageLine(im, x+36, y+124-((s+prev)/2), x+460, y+124-((s+prev)/2), clinel); } /* scale text */ if (rate) { gdImageStringUp(im, gdFontGetTiny(), x-2, y+70, (unsigned char*)getimagescale(scaleunit, 1), ctext); } else { gdImageStringUp(im, gdFontGetTiny(), x-2, y+60, (unsigned char*)getimagescale(scaleunit, 0), 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(im, gdFontGetTiny(), x+440-(i*17), y+128, (unsigned char*)buffer, ctext); drawpole(x+438-(i*17), y, 124, data.hour[s].rx, data.hour[s].tx, max); } /* axis */ gdImageLine(im, x+36-4, y+124, x+466, y+124, ctext); gdImageLine(im, x+36, y-10, x+36, y+124+4, ctext); /* arrows */ gdImageLine(im, x+465, y+124, x+462, y+122, ctext); gdImageLine(im, x+465, y+124, x+462, y+126, ctext); gdImageLine(im, x+462, y+122, x+462, y+126, ctext); gdImageLine(im, x+36, y-9, x+38, y-6, ctext); gdImageLine(im, x+36, y-9, x+34, y-6, ctext); gdImageLine(im, x+34, y-6, x+38, y-6, ctext); } void drawsummary(int type, int showheader, int showedge, 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 (!showheader) { headermod = 26; height -= 22; } else { headermod = 0; } yesterday=current-86400; im = gdImageCreate(width, height); colorinit(); if (strcmp(data.nick, data.interface)==0) { snprintf(buffer, 512, "%s", data.interface); } else { snprintf(buffer, 512, "%s (%s)", data.nick, data.interface); } layoutinit(buffer, width, height, showheader, showedge); /* 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(150+offset, 75-headermod, rxp, txp); textx = 100+offset; texty = 30-headermod; /* get formated date for today */ d = localtime(¤t); 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(daytemp, "today", 32); } snprintf(buffer, 32, "%12s", daytemp); gdImageString(im, gdFontGetLarge(), textx-54, texty-1, (unsigned char*)buffer, 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(im, gdFontGetSmall(), textx-74, texty+58, (unsigned char*)buffer, ctext); } else { texty += 7; } snprintf(buffer, 4, "rx "); strncat(buffer, getvalue(data.day[0].rx, data.day[0].rxk, 12, 1), 32); gdImageString(im, gdFontGetSmall(), textx-74, texty+18, (unsigned char*)buffer, ctext); snprintf(buffer, 4, "tx "); strncat(buffer, getvalue(data.day[0].tx, data.day[0].txk, 12, 1), 32); gdImageString(im, gdFontGetSmall(), textx-74, texty+30, (unsigned char*)buffer, 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(im, gdFontGetSmall(), textx-74, texty+44, (unsigned char*)buffer, 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(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(daytemp, "yesterday", 32); } snprintf(buffer, 32, "%12s", daytemp); gdImageString(im, gdFontGetLarge(), textx-54, texty-1, (unsigned char*)buffer, 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(im, gdFontGetSmall(), textx-74, texty+58, (unsigned char*)buffer, ctext); } else { texty += 7; } snprintf(buffer, 4, "rx "); strncat(buffer, getvalue(data.day[1].rx, data.day[1].rxk, 12, 1), 32); gdImageString(im, gdFontGetSmall(), textx-74, texty+18, (unsigned char*)buffer, ctext); snprintf(buffer, 4, "tx "); strncat(buffer, getvalue(data.day[1].tx, data.day[1].txk, 12, 1), 32); gdImageString(im, gdFontGetSmall(), textx-74, texty+30, (unsigned char*)buffer, 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(im, gdFontGetSmall(), textx-74, texty+44, (unsigned char*)buffer, 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(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, "%12s", daytemp); gdImageString(im, gdFontGetLarge(), textx-54, texty-1, (unsigned char*)buffer, 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(im, gdFontGetSmall(), textx-74, texty+58, (unsigned char*)buffer, ctext); } else { texty += 7; } snprintf(buffer, 4, "rx "); strncat(buffer, getvalue(data.month[0].rx, data.month[0].rxk, 12, 1), 32); gdImageString(im, gdFontGetSmall(), textx-74, texty+18, (unsigned char*)buffer, ctext); snprintf(buffer, 4, "tx "); strncat(buffer, getvalue(data.month[0].tx, data.month[0].txk, 12, 1), 32); gdImageString(im, gdFontGetSmall(), textx-74, texty+30, (unsigned char*)buffer, 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(im, gdFontGetSmall(), textx-74, texty+44, (unsigned char*)buffer, 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(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, "%12s", daytemp); gdImageString(im, gdFontGetLarge(), textx-54, texty-1, (unsigned char*)buffer, 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(im, gdFontGetSmall(), textx-74, texty+58, (unsigned char*)buffer, ctext); } else { texty += 7; } snprintf(buffer, 4, "rx "); strncat(buffer, getvalue(data.month[1].rx, data.month[1].rxk, 12, 1), 32); gdImageString(im, gdFontGetSmall(), textx-74, texty+18, (unsigned char*)buffer, ctext); snprintf(buffer, 4, "tx "); strncat(buffer, getvalue(data.month[1].tx, data.month[1].txk, 12, 1), 32); gdImageString(im, gdFontGetSmall(), textx-74, texty+30, (unsigned char*)buffer, 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(im, gdFontGetSmall(), textx-74, texty+44, (unsigned char*)buffer, ctext); } /* all time */ textx = 385; texty = 57-headermod; gdImageString(im, gdFontGetLarge(), textx+12, texty, (unsigned char*)"all time", ctext); snprintf(buffer, 4, "rx "); strncat(buffer, getvalue(data.totalrx, data.totalrxk, 12, 1), 32); gdImageString(im, gdFontGetSmall(), textx, texty+24, (unsigned char*)buffer, ctext); snprintf(buffer, 4, "tx "); strncat(buffer, getvalue(data.totaltx, data.totaltxk, 12, 1), 32); gdImageString(im, gdFontGetSmall(), textx, texty+36, (unsigned char*)buffer, ctext); snprintf(buffer, 4, " = "); strncat(buffer, getvalue(data.totalrx+data.totaltx, data.totalrxk+data.totaltxk, 12, 1), 32); gdImageString(im, gdFontGetSmall(), textx, texty+50, (unsigned char*)buffer, 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(im, gdFontGetSmall(), textx-24, texty+70, (unsigned char*)buffer, ctext); drawlegend(410, 155-headermod); /* hours if requested */ switch (type) { case 1: drawhours(500, 46-headermod, rate); break; case 2: drawhours(16, 215-headermod, rate); break; default: break; } } void drawoldsummary(int type, int showheader, int showedge, 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 (!showheader) { headermod = 26; height -= 22; } else { headermod = 0; } yesterday=current-86400; im = gdImageCreate(width, height); colorinit(); if (strcmp(data.nick, data.interface)==0) { snprintf(buffer, 512, "%s", data.interface); } else { snprintf(buffer, 512, "%s (%s)", data.nick, data.interface); } layoutinit(buffer, width, height, showheader, showedge); drawlegend(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(im, piex, piey+i, piew, pieh, 270, 270+arc, ctxd, gdEdged|gdNoFill); gdImageFilledArc(im, piex, piey+i, piew, pieh, 270+arc, 270, crxd, gdEdged|gdNoFill); } gdImageFilledArc(im, piex, piey, piew, pieh, 270, 270+arc, ctx, 0); gdImageFilledArc(im, piex, piey, piew, pieh, 270, 270+arc, ctxd, gdEdged|gdNoFill); gdImageFilledArc(im, piex, piey, piew, pieh, 270+arc, 270, crx, 0); gdImageFilledArc(im, piex, piey, piew, pieh, 270+arc, 270, crxd, gdEdged|gdNoFill); textx = 30; texty = 48-headermod; /* totals */ snprintf(buffer, 512, " received: %s (%.1f%%)", getvalue(data.totalrx, data.totalrxk, 14, 1), rxp); gdImageString(im, gdFontGetLarge(), textx, texty, (unsigned char*)buffer, ctext); snprintf(buffer, 512, "transmitted: %s (%.1f%%)", getvalue(data.totaltx, data.totaltxk, 14, 1), txp); gdImageString(im, gdFontGetLarge(), textx, texty+15, (unsigned char*)buffer, ctext); snprintf(buffer, 512, " total: %s", getvalue(data.totalrx+data.totaltx, data.totalrxk+data.totaltxk, 14, 1)); gdImageString(im, gdFontGetLarge(), textx, texty+30, (unsigned char*)buffer, 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(daytemp, "yesterday", 32); } /* get formated date for today */ d=localtime(¤t); 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(daytemp2, "today", 32); } textx = 20; texty = 118-headermod; /* yesterday & today */ gdImageString(im, gdFontGetSmall(), textx, texty, (unsigned char*)" rx tx total", ctext); gdImageLine(im, textx-4, texty+16, textx+290, texty+16, cline); gdImageLine(im, textx-4, texty+49, textx+290, texty+49, cline); gdImageLine(im, textx+140, texty+4, textx+140, texty+64, cline); gdImageLine(im, textx+218, texty+4, textx+218, texty+64, cline); if (data.day[1].date!=0) { snprintf(buffer, 32, "%9s ", 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(im, gdFontGetSmall(), textx, texty+20, (unsigned char*)buffer, ctext); } snprintf(buffer, 32, "%9s ", 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(im, gdFontGetSmall(), textx, texty+32, (unsigned char*)buffer, 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(im, gdFontGetSmall(), textx, texty+52, (unsigned char*)buffer, 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; if (tk>=1024) { t+=tk/1024; tk-=(tk/1024)*1024; } t=(t*1024)+tk; if (t>max) { max=t; } } } /* bars for both */ drawbar(textx+300, texty+24, 165, data.day[1].rx, data.day[1].rxk, data.day[1].tx, data.day[1].txk, max); drawbar(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(500, 46-headermod, rate); break; case 2: drawhours(16, 215-headermod, rate); break; default: break; } } void drawhourly(int showheader, int showedge, int rate) { int width, height; char buffer[512]; width = 500; height = 200; if (!showheader) { height -= 22; } im = gdImageCreate(width, height); colorinit(); 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(buffer, width, height, showheader, showedge); if (showheader) { drawhours(12, 46, rate); } else { drawhours(12, 22, rate); } } void drawdaily(int showheader, int showedge) { 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 (!showheader) { headermod = 26; height -= 22; } else { headermod = 0; } im = gdImageCreate(width, height); colorinit(); 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(buffer, width, height, showheader, showedge); if (lines) { if (cfg.ostyle>2) { drawlegend(432, 40-headermod); } else { drawlegend(385, 40-headermod); } } textx = 10; texty = 40-headermod; if (cfg.ostyle>2) { gdImageString(im, gdFontGetSmall(), textx, texty, (unsigned char*)" day rx tx total avg. rate", ctext); gdImageLine(im, textx+2, texty+16, textx+392, texty+16, cline); gdImageLine(im, textx+144, texty+2, textx+144, texty+40+(12*lines), cline); gdImageLine(im, textx+222, texty+2, textx+222, texty+40+(12*lines), cline); gdImageLine(im, textx+300, texty+2, textx+300, texty+40+(12*lines), cline); } else { gdImageString(im, gdFontGetSmall(), textx, texty, (unsigned char*)" day rx tx total", ctext); gdImageLine(im, textx+2, texty+16, textx+296, texty+16, cline); gdImageLine(im, textx+144, texty+2, textx+144, texty+40+(12*lines), cline); gdImageLine(im, textx+222, texty+2, textx+222, texty+40+(12*lines), 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, " %8s ", datebuff); } else { snprintf(buffer, 32, " %-11s ", 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(im, gdFontGetSmall(), textx, texty, (unsigned char*)buffer, ctext); if (cfg.ostyle>2) { drawbar(textx+400, texty+4, 78, data.day[i].rx, data.day[i].rxk, data.day[i].tx, data.day[i].txk, max); } else { drawbar(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(im, gdFontGetSmall(), textx, texty, (unsigned char*)" no data available", ctext); texty += 12; } if (cfg.ostyle>2) { gdImageLine(im, textx+2, texty+5, textx+392, texty+5, cline); } else { gdImageLine(im, textx+2, texty+5, textx+296, texty+5, 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(im, gdFontGetSmall(), textx, texty+8, (unsigned char*)buffer, ctext); } } void drawmonthly(int showheader, int showedge) { 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 (!showheader) { headermod = 26; height -= 22; } else { headermod = 0; } im = gdImageCreate(width, height); colorinit(); 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(buffer, width, height, showheader, showedge); if (lines) { if (cfg.ostyle>2) { drawlegend(432, 40-headermod); } else { drawlegend(385, 40-headermod); } } textx = 10; texty = 40-headermod; if (cfg.ostyle>2) { gdImageString(im, gdFontGetSmall(), textx, texty, (unsigned char*)" month rx tx total avg. rate", ctext); gdImageLine(im, textx+2, texty+16, textx+392, texty+16, cline); gdImageLine(im, textx+144, texty+2, textx+144, texty+40+(12*lines), cline); gdImageLine(im, textx+222, texty+2, textx+222, texty+40+(12*lines), cline); gdImageLine(im, textx+300, texty+2, textx+300, texty+40+(12*lines), cline); } else { gdImageString(im, gdFontGetSmall(), textx, texty, (unsigned char*)" month rx tx total", ctext); gdImageLine(im, textx+2, texty+16, textx+296, texty+16, cline); gdImageLine(im, textx+144, texty+2, textx+144, texty+40+(12*lines), cline); gdImageLine(im, textx+222, texty+2, textx+222, texty+40+(12*lines), 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, " %9s ", datebuff); } else { snprintf(buffer, 32, " %-11s ", 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(im, gdFontGetSmall(), textx, texty, (unsigned char*)buffer, ctext); if (cfg.ostyle>2) { drawbar(textx+400, texty+4, 78, data.month[i].rx, data.month[i].rxk, data.month[i].tx, data.month[i].txk, max); } else { drawbar(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(im, gdFontGetSmall(), textx, texty, (unsigned char*)" no data available", ctext); texty += 12; } if (cfg.ostyle>2) { gdImageLine(im, textx+2, texty+5, textx+392, texty+5, cline); } else { gdImageLine(im, textx+2, texty+5, textx+296, texty+5, 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(im, gdFontGetSmall(), textx, texty+8, (unsigned char*)buffer, ctext); } } void drawtop(int showheader, int showedge) { 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 (!showheader) { headermod = 26; height -= 22; } else { headermod = 0; } im = gdImageCreate(width, height); colorinit(); 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(buffer, width, height, showheader, showedge); if (lines) { if (cfg.ostyle<=2) { drawlegend(398, 40-headermod); } } textx = 10; texty = 40-headermod; if (cfg.ostyle>2) { gdImageString(im, gdFontGetSmall(), textx, texty, (unsigned char*)" # day rx tx total avg. rate", ctext); gdImageLine(im, textx+2, texty+16, textx+422, texty+16, cline); if (lines) { gdImageLine(im, textx+174, texty+2, textx+174, texty+24+(12*lines), cline); gdImageLine(im, textx+252, texty+2, textx+252, texty+24+(12*lines), cline); gdImageLine(im, textx+330, texty+2, textx+330, texty+24+(12*lines), cline); } } else { gdImageString(im, gdFontGetSmall(), textx, texty, (unsigned char*)" # day rx tx total", ctext); gdImageLine(im, textx+2, texty+16, textx+326, texty+16, cline); if (lines) { gdImageLine(im, textx+174, texty+2, textx+174, texty+24+(12*lines), cline); gdImageLine(im, textx+252, texty+2, textx+252, texty+24+(12*lines), 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 %8s ", i+1, datebuff); } else { snprintf(buffer, 32, " %2d %-11s ", i+1, 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(im, gdFontGetSmall(), textx, texty, (unsigned char*)buffer, ctext); if (cfg.ostyle>2) { drawbar(textx+428, texty+4, 52, data.top10[i].rx, data.top10[i].rxk, data.top10[i].tx, data.top10[i].txk, max); } else { drawbar(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(im, gdFontGetSmall(), textx, texty, (unsigned char*)" no data available", ctext); texty += 12; } if (cfg.ostyle>2) { gdImageLine(im, textx+2, texty+5, textx+422, texty+5, cline); } else { gdImageLine(im, textx+2, texty+5, textx+326, texty+5, 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.11/cfg/0000755000000000000000000000000011571514113012207 5ustar rootrootvnstat-1.11/cfg/vnstat.conf0000644000000000000000000000551111570760102014377 0ustar rootroot# vnStat 1.11 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 # maximum bandwidth (Mbit) for all interfaces, 0 = disable feature # (unless interface specific limit is given) MaxBandwidth 100 # interface specific limits # example 8Mbit limit for eth0 (remove # to activate): #MaxBWeth0 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 = dumpdb, 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 ## # 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 # force data save when interface status changes (1 = enabled, 0 = disabled) SaveOnStatusChange 1 # enable / disable logging (0 = disabled, 1 = logfile, 2 = syslog) UseLogging 2 # file used for logging if UseLogging is set to 1 LogFile "/var/log/vnstat.log" # file used as daemon pid / lock file PidFile "/var/run/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.11/UPGRADE0000644000000000000000000000443711570527576012512 0ustar rootroot(Updated 29.5.2011 for version 1.11) New configuration settings :::::::::::::::::::::::::: 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.1 - 1.9 to 1.11 ::::::::::::::::::::::::::::::::::::::::: 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. Upgrading from version 1.0 :::::::::::::::::::::::::: Make a backup of your current database. For the old multiuser install that would be /var/spool/vnstat/db and for singleuser it's ~/.vnstatdb Copy that file to some other directory that isn't in any relation with vnStat. All this is only for backup so the file can be removed once 1.3 or later has been installed and found working. The next thing is to rename that database according to the interface it's monitoring. Here's an example if the interface is eth0. mv /var/spool/vnstat/db /var/spool/vnstat/eth0 If there are multiple databases that where used with the -f parameter then repeat that procedure for each of of them. In a similar way those using the singleuser version would first make a directory labelled '~/.vnstat' and move the old database there. mv ~/.vnstatdb ~/.vnstat/eth0 Now your all set to continue the install process like the INSTALL file tells. Remember that there's no need to uninstall version 1.0 before installing a more recent version. vnstat-1.11/man/0000755000000000000000000000000011571514113012223 5ustar rootrootvnstat-1.11/man/vnstatd.10000644000000000000000000001462011571514010013767 0ustar rootroot.TH VNSTATD 1 "JUNE 2011" "version 1.11" "User Manuals" .SH NAME vnStat daemon \- the alternative for cron based updating .SH SYNOPSIS .B vnstatd [ .B \-Ddnpsv? ] [ .B \-\-config .I file ] [ .B \-\-daemon ] [ .B \-\-debug ] [ .B \-\-help ] [ .B \-\-noadd ] [ .B \-\-nodaemon ] [ .B \-\-pidfile .I file ] [ .B \-\-sync ] [ .B \-\-version ] .SH DESCRIPTION The purpose of .B vnstatd is to provide a more flexible 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 requires less disk access since data can be cached and written only later to disk at a user configurable interval. It is also able to track how interfaces come and go without the need of additional scripts that are required with cron based updates. .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 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. .SH OPTIONS .TP .BI "--config " file Use .I file as config file instead of using normal config file search function. .TP .BI "-d, --daemon" Fork process to background and run as a daemon. .TP .BI "-D, --debug" Provide additional output for debug purposes. The process will stay attached to the terminal for output. .TP .BI "--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 .BI "-n, --nodaemon" Stay in foreground attached to the current terminal and start 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 .BI "-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 "-v, --version" Show current version. .TP .BI "-?, --help" Show a command summary. .SH CONFIGURATION The behaviour of the daemon is configured mainly using the configuration keywords .BR "UpdateInterval, PollInterval" and .BR SaveInterval in the configuration file. .PP .BR UpdateInterval defines in seconds how often the interface data is updated. This is similar to the run interval for alternative cron based updating. However, the difference is that the data doesn't get written to disk during updates. .PP .BR 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. .BR PollInterval also defines the resolution for other intervals. .PP .BR 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 .BR UpdateInterval with a maximum value of 60 minutes. .PP The default values of .BR UpdateInterval 20, .BR SaveInterval 5 and .BR PollInterval 2 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 .BR PollInterval can be rised to around 10-30 seconds and .BR UpdateInterval set to 60 seconds. Higher values up to 300 seconds are possible if the interface speed is 10 Mbit or less. .BR SaveInterval can be rised 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 .BR "SIGHUP, SIGINT" and .BR SIGTERM. Sending the .BR 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 will not be updated even if it's configuration setting has been changed. .PP .BR SIGTERM and .BR 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 wrap 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 working solution. .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.11/man/vnstat.conf.50000644000000000000000000001443311571514010014555 0ustar rootroot.TH VNSTAT.CONF 5 "JUNE 2011" "version 1.11" "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 storing configuration data. Some of the settings are common for all three programs. The file contains keyword-argument pairs, one per line. Lines starting with '#' and empty lines 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. .SH COMMON KEYWORDS .TP .BI BootVariation Time in seconds how much the boot time reported by system kernel can variate between updates. .TP .BI CheckDiskSpace Enable or disable the checking of at least some free disk space before a database write. 1 = enabled, 0 = disabled. .TP .BI DatabaseDir Specifies the directory where interface databases are to be stored. A full path must be given and a leading '/' isn't required. .TP .BI "DayFormat, MonthFormat, TopFormat" Formatting of date in available outputs. Uses the same format as .BR date (1). (vnstat and vnstati only) .TP .BI Interface Default interface used when no other interface is specified on the command line. (vnstat and vnstati only) .TP .BI Locale Locale setting to be used for prints. This replaces the LC_ALL environment variable. Set to "-" in order to use the system default value. (vnstat and vnstati only) .TP .BI MaxBandwidth Maximum bandwidth for all interfaces. If the counted traffic exceeds the given value then the data is assumed to be invalid and rejected. Set to 0 in order to disable the feature. (vnstat and vnstatd only) .TP .BI 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. (vnstat and vnstatd only) .TP .BI 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. (vnstat and vnstatd only) .TP .BI 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 .BI QueryMode Default query mode when no parameters are given. 0 = normal, 1 = days, 2 = months, 3 = top10, 4 = dumpdb, 5 = short, 6 = weeks, 7 = hours and 8 = xml. (vnstat only) .TP .BI RateUnit Select which unit is used when traffic rate is visible. 0 = bytes, 1 = bits. (vnstat and vnstati only) .TP .BI "RXCharacter, TXCharacter" Character used for representing the percentual share of received and transmitted traffic in daily output. (vnstat only) .TP .BI "RXHourCharacter, TXHourCharacter" Character used for representing the percentual share of received and transmitted traffic in hourly output. (vnstat only) .TP .BI Sampletime Defines how many seconds the -tr option will sample traffic. (vnstat only) .TP .BI TrafficlessDays Log days without any traffic to daily list. 1 = enabled, 0 = disabled. (vnstat and vnstatd only) .TP .BI UnitMode Select how units are prefixed. 0 = IEC standard prefixes (KiB/MiB/GiB/TiB), 1 = old style binary prefixes (KB/MB/GB/TB). (vnstat and vnstati only) .TP .BI 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 .BI LogFile Specify log file path and name to be used if UseLogging is set to 1. .TP .BI OfflineSaveInterval How often in minutes cached interface data is saved to file when all monitored interfaces are offline. .TP .BI PidFile Specify pid file path and name to be used. .TP .BI PollInterval How often in seconds interfaces are checked for status changes. .TP .BI SaveInterval How often in minutes cached interface data is saved to file. .TP .BI 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 .BI UpdateInterval How often in seconds the interface data is updated. .TP .BI UseLogging Enable or disable logging. 0 = disabled, 1 = logfile and 2 = syslog. .SH IMAGE OUTPUT RELATED KEYWORDS .TP .BI CBackground Background color. .TP .BI CEdge Edge color if visible. .TP .BI CHeader Header background color. .TP .BI CHeaderTitle Header title text color. .TP .BI CHeaderDate Header date text color. .TP .BI CLine Line color. .TP .BI CLineL Lighter version of line color. Set to '-' in order to use a calculated value based on CLine. .TP .BI CRx Color for received data. .TP .BI CRxD Darker version of received data color. Set to '-' in order to use a calculated value based on CRx. .TP .BI CText Common text color. .TP .BI CTx Color for transmitted data. .TP .BI CTxD Darker version of transmitted data color. Set to '-' in order to use a calculated value based on CTx. .TP .BI HeaderFormat Formatting of date in header. Uses the same format as .BR date (1). .TP .BI HourlyRate Show hours with rate instead of transfered amount. 1 = enabled, 0 = disabled. .TP .BI 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 .BI SummaryRate Show rate in summary output if available. 1 = enabled, 0 = disabled. .TP .BI 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.11/man/vnstat.10000644000000000000000000002442011571514010013622 0ustar rootroot.TH VNSTAT 1 "JUNE 2011" "version 1.11" "User Manuals" .SH NAME vnStat \- a console-based network traffic monitor .SH SYNOPSIS .B vnstat [ .B \-Ddhlmqrstuvw? ] [ .B \-\-cleartop ] [ .B \-\-config .I file ] [ .B \-\-days ] [ .B \-\-delete ] [ .B \-\-dbdir .I directory ] [ .B \-\-debug ] [ .B \-\-disable ] [ .B \-\-dumpdb ] [ .B \-\-enable ] [ .B \-\-help ] [ .B \-\-hours ] [ .B \-i .I interface ] [ .B \-\-iface .I interface ] [ .B \-\-iflist ] [ .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 ] .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 analyzed from the .BR proc (5) and .BR sys filesystems depending on availability. That way vnStat can be used even without root permissions on most systems. .SH OPTIONS .TP .BI "--cleartop" Remove all top10 entries. .TP .BI "--config " file Use .I file as config file instead of using normal config file search function. .TP .BI "-d, --days" Show traffic for 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 "-D, --debug" Show additional debug output. .TP .BI "--delete" Delete the database for the selected interface and stop monitoring it. .TP .BI "--dumpdb" Instead of showing the database with a formated output, this output will dump the whole database in a format that should be easy to parse with most script languages. Use this for example with PHP, Perl or Python to make a custom webpage. 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 = top10 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 .BI "--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 .BI "-h, --hours" Show traffic for the last 24 hours. .TP .BI "-i, --iface " interface Select one specific .I interface and apply actions to only it. .TP .BI "--iflist" Show list of currently available interfaces. .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 .BI "--longhelp" Show complete options list. .TP .BI "-m, --months" Show traffic for 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. .TP .BI "--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 .BI "-q, --query" Force database query mode. .TP .BI "-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. .TP .BI "--rebuildtotal" Reset the total traffic counters and recount those using recorded months. .TP .BI "-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 .BI "--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. Top10 traffic days isn't included in the merge and will start empty in the new database. .TP .BI "-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 .BI "--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. .TP .BI "--testkernel" Test if the kernel boot time information always stays the same like it should or if it's shifting. .TP .BI "-t, --top10" Show all time top10 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 included. .TP .BI "-u, --update" Update all enabled databases or only the one specified with .B -i parameter. .TP .BI "-v, --version" Show current version. .TP .BI "-w, --weeks" Show traffic for 7 days, current and previous week. .TP .BI "--xml" Show database content for selected interface or all interfaces in xml format. All traffic values in the output are in KiB. .TP .BI "-?, --help" Show a command summary. .SH FILES .TP .I /var/lib/vnstat/ This directory contains all databases the program uses. 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 .BI "vnstat" Display traffic summary for the default interface. .TP .BI "vnstat -i eth0+eth1+eth3" Display traffic summary for a merge of interfaces eth0, eth1 and eth3. .TP .BI "vnstat -i eth2 --xml" Output all information about interface eth2 in xml format. .TP .BI "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. .TP .BI "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 .BI "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 wrap 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 still a working option. .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.11/man/vnstati.10000644000000000000000000001146011571514010013773 0ustar rootroot.TH VNSTATI 1 "JUNE 2011" "version 1.11" "User Manuals" .SH NAME vnStat image output \- png image output support for vnStat .SH SYNOPSIS .B vnstati [ .B \-cdhimostv? ] [ .B \-\-cache .I time ] [ .B \-\-config .I file ] [ .B \-\-days ] [ .B \-\-dbdir .I directory ] [ .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 \-\-noedge ] [ .B \-\-noheader ] [ .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 .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 config file instead of using normal config file search function. .TP .BI "-d, --days" Output traffic for 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 "-h, --hours" Output traffic for the last 24 hours. .TP .BI "-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 .BI "-m, --months" Output traffic for months. .TP .BI "-ne, --noedge" Remove darker edges from around the image. .TP .BI "-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 .BI "-o, --output " file Write png image to .I file and exit. Output can be directed to stdout by giving "-" as filename. .TP .BI "-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 .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 .BI "-s, --summary" Output traffic summary. .TP .BI "-t, --top10" Output all time top10 traffic days. .TP .BI "--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 .BI "-v, --version" Show current version. .TP .BI "-vs, --vsummary" Output traffic summary including hourly data using a vertical layout. .TP .BI "-?, --help" Show a command 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 .BI "vnstati -s -i eth0 -o /tmp/vnstat.png" Output traffic summary for interface eth0 to file /tmp/vnstat.png. .TP .BI "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 .BI "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 .BI "vnstati -d -ne -nh -o -" Output daily traffic statistics without displaying the header section and edges for default interface to standard output (stdout). .TP .BI "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 AUTHOR Teemu Toivola .SH "SEE ALSO" .BR vnstat (1), .BR vnstatd (1), .BR vnstat.conf (5), .BR units (7) vnstat-1.11/INSTALL_BSD0000644000000000000000000001104611570527576013213 0ustar rootroot(Updated 29.5.2011 for version 1.11) Compiling the binaries :::::::::::::::::::::: This source package contains the required sources 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. In BSD ports libgd2 is usually named simply 'gd'. 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. Some BSD users might need to use LIBRARY_PATH="/usr/local/lib" CPATH="/usr/local/include" make all instead if libgd2 is installed but still not found. Installing as root :::::::::::::::::: Login as root and run the following command: make bsdinstall If there were no errors, vnStat binaries, man pages and a config file should now be installed. Next every interface that should be monitored needs to be introduced to vnStat. Replace 'eth0' in the command with any available interface if needed. vnstat -u -i eth0 Repeat that for every other interface you wish to use. If you are unsure of available interface names then run vnstat --iflist and select a suitable looking interface from the provided list. Usually 'lo' isn't the one you are looking for. 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). There are two way for updating interface data, daemon and cron. It is suggested to use daemon based updating because it generated less disk access and is more accurate especially when monitoring interfaces that aren't always available. Select only one of these two: 1) daemon based updating :::::::::::::::::::::::: 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. 2) cron based updating :::::::::::::::::::::: Add the following line to /etc/crontab (or with minor modifications to the personal crontab of the user responsible for updates): */5 * * * * root /usr/local/bin/vnstat -u That will update interface every 5 minutes. Modify the update interval if needed. See the vnStat man page 'restrictions' section for limitations regarding fast interfaces and 32 bit interface traffic counters. Devices like ppp0 also require vnStat enable and disable commands to be added to suitable script file when cron based updating is used. Example scripts can be found in the 'examples' directory. 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 you homedir path execute cd ; pwd The ouput should tell your homedir. Next every interface that should be monitored needs to be introduced to vnStat. Replace 'eth0' in the command with any available interface if needed. vnstat -u -i eth0 Repeat that for every other interface you wish to use. 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): */5 * * * * ~/bin/vnstat -u OR if the daemon can be used @reboot ~/bin/vnstatd -d If you found yourself using a strange editor then 'man vi' should help. If you choose to use the daemon then 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 (if using the daemon) try starting it with 'vnstat -d'. After that wait for (or generate) at least 1024 bytes of network traffic (and 5 min for the next cron/daemon update). vnstat Now you should get some stats about your network usage. See the config file ~/.vnstatrc for interface and other settings. vnstat-1.11/README0000644000000000000000000000406611571514010012332 0ustar rootroot(Updated 29.5.2011 for version 1.11) What is vnStat :::::::::::::: In short, 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. Optional image output is available starting from version 1.7. See the webpage http://humdi.net/vnstat/ for output examples. Getting started ::::::::::::::: First you should install vnStat. See the INSTALL or INSTALL_BSD file depending on used operating system, it contains 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: 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) The current version of vnStat is always available from http://humdi.net/vnstat/ Discussion forum http://forums.humdi.net/ Email alerts about new versions are available by subscription at http://freshmeat.net/projects/vnstat/