]> granicus.if.org Git - vnstat/commitdiff
Version 1.7 v1.7
authorTeemu Toivola <git@humdi.net>
Mon, 24 Feb 2014 19:53:25 +0000 (21:53 +0200)
committerTeemu Toivola <git@humdi.net>
Mon, 24 Feb 2014 19:53:25 +0000 (21:53 +0200)
47 files changed:
CHANGES
FAQ
INSTALL
INSTALL_BSD [new file with mode: 0644]
Makefile
README
UNINSTALL
UPGRADE
cfg/vnstat.conf
cron/vnstat
examples/vnstat.cgi [new file with mode: 0755]
examples/vnstat_ip-down [moved from pppd/vnstat_ip-down with 100% similarity]
examples/vnstat_ip-up [moved from pppd/vnstat_ip-up with 100% similarity]
man/vnstat.1
man/vnstat.conf.1 [new file with mode: 0644]
man/vnstatd.1 [new file with mode: 0644]
man/vnstati.1 [new file with mode: 0644]
src/Makefile
src/cfg.c
src/cfg.h
src/common.c [new file with mode: 0644]
src/common.h [new file with mode: 0644]
src/db.h [deleted file]
src/dbaccess.c
src/dbaccess.h
src/dbcache.c [new file with mode: 0644]
src/dbcache.h [new file with mode: 0644]
src/dbshow.c [moved from src/db.c with 60% similarity]
src/dbshow.h [new file with mode: 0644]
src/dbxml.c [new file with mode: 0644]
src/dbxml.h [new file with mode: 0644]
src/ifinfo.c
src/ifinfo.h
src/image.c [new file with mode: 0644]
src/image.h [new file with mode: 0644]
src/misc.c
src/misc.h
src/proc.c [deleted file]
src/proc.h [deleted file]
src/traffic.c [new file with mode: 0644]
src/traffic.h [new file with mode: 0644]
src/vnstat.c
src/vnstat.h
src/vnstatd.c [new file with mode: 0644]
src/vnstatd.h [new file with mode: 0644]
src/vnstati.c [new file with mode: 0644]
src/vnstati.h [new file with mode: 0644]

diff --git a/CHANGES b/CHANGES
index 9c6612d5095acae47ab6b3c73f7d718d963a59b3..12d65179aab46c55f8ab84db36a945fd3783c1f8 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,27 @@
+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,
diff --git a/FAQ b/FAQ
index ab9570ba5d1b104390e8262b0b235f8566b229c3..1e171d20b9c68610a6bf5971b1347c22194d963c 100644 (file)
--- a/FAQ
+++ b/FAQ
@@ -9,7 +9,7 @@ since that's the easiest way to keep it updated.
 ----
 
 
-A snapshot of the FAQ (updated 30.12.2007):
+A snapshot of the FAQ (updated 12.4.2009):
 
 
 Is there some kind of support forum available?
@@ -23,10 +23,12 @@ Is there some kind of support forum available?
 How should dialup users use vnStat? There's some errors because the interface
 isn't available when offline.
 
-  That's all explained at the end of the README. The idea is to include
-  vnStat with enable/disable parameters in scripts related with the used
-  interface. Example scripts can be found from the pppd directory that came
-  with the source package.
+  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?
@@ -43,7 +45,8 @@ 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.
+  monitoring. The daemon needs to be stopped before this procedure if it's
+  being used for updates.
 
 
 How do I uninstall vnStat?
@@ -54,15 +57,15 @@ How do I uninstall vnStat?
   then refer to intructions provided by the package manager.
 
 
-Is the MB value reported by vnStat 10^6 or 2^20 bytes?
+What is this KiB/MiB/GiB/TiB thing?
 
-  2^20 bytes. It could as well be MiB but due to space limitations in some
-  outputs that isn't used.
+  See http://en.wikipedia.org/wiki/Binary_prefix#Prefixes
 
 
-Why kB and not KB?
+Is the MB value reported by vnStat 10^6 or 2^20 bytes?
 
-  Because it's kilo (k) and not kelvin (K) 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?
@@ -96,7 +99,9 @@ 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. See the next question for some explanation.
+  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?
diff --git a/INSTALL b/INSTALL
index e88611c57aeb4ba1a30f5423c5c05052b0f85959..0fc554a44a120f834ea3e8bc6c206a383a131941 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -1,26 +1,38 @@
-(Updated 1.1.2008 for version 1.6 and later)
+(Updated 12.4.2009 for version 1.7)
 
 
-'I don't read manuals' install
-::::::::::::::::::::::::::::::
+Compiling the binaries
+::::::::::::::::::::::
+
+ This source package contains the required sources for vnStat including
+ the daemon (vnstatd) and image output (vnstati). Executing
+
+     make
 
- Run as root:
+ 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 && make install ; vnstat --testkernel && vnstat -u -i eth0
+     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
 ::::::::::::::::::
 
First login as root and run the following command:
Login as root and run the following command:
 
-     make && make install
+     make install
 
- If there was no errors, vnStat should now be installed (warnings aren't
- aren't considered as errors). 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.
+ 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
 
@@ -31,27 +43,57 @@ Installing as root
 
      vnstat -u -i eth0
 
- Repeat that for every other interface you wish to use. After that wait
- for about 10 kB of network traffic (and 5 min for the next cron update).
+ Repeat that for every other interface you wish to use. If you are unsure
+ of available interface names then run
 
-     vnstat
+     vnstat --iflist
 
- Now you should get some stats about your network usage. See the config
file /etc/vnstat.conf for interface and other settings.
+ 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.
 
-Installing without root access
-::::::::::::::::::::::::::::::
+ 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:
 
- This install method is a little bit more complicated but shouldn't be
- impossible. :) First compile the binary.
+ 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.
 
-     make
 
- If there was no errors, copy src/vnstat to some directory included in your
- $PATH (~/bin/ is an example) and make the database directory.
+ 2) cron based updating
+ ::::::::::::::::::::::
 
-     cp src/vnstat ~/bin/
+   Copy the example cron file to /etc/cron.d
+
+       cp cron/vnstat /etc/cron.d
+
+   and edit the cron file is 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
 
@@ -94,9 +136,19 @@ Installing without root access
 
      */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.
- After that wait for about 10 kB of network traffic (and 5 min for the
- next cron update).
+
+ 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
 
diff --git a/INSTALL_BSD b/INSTALL_BSD
new file mode 100644 (file)
index 0000000..a79dc63
--- /dev/null
@@ -0,0 +1,145 @@
+(Updated 12.4.2009 for version 1.7)
+
+
+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.
index 33144c3ed88a37a5fe31ab2fd76f1fc7a7edd461..c4306f52562e494b262e000fe8adeb921114c25a 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,10 +1,20 @@
-# bin, man and cron dirs
+# bin and man dirs for Linux
 BIN = $(DESTDIR)/usr/bin
+SBIN = $(DESTDIR)/usr/sbin
 MAN = $(DESTDIR)/usr/share/man
-CRON = $(DESTDIR)/etc/cron.d
+
+# 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 vnstat
+       +make -C src
+
+all:
+       +make -C src all
 
 clean:
        make -C src clean
@@ -12,46 +22,67 @@ 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 ]; \
+       @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 ]; \
+       @if [ -x "$(DESTDIR)/usr/local/bin/vnstat" ]; \
        then echo "Removing old binary..."; \
        rm -f $(DESTDIR)/usr/local/bin/vnstat; \
        fi
 
-# install ppp scripts if directory is found
-       @if [ -d $(DESTDIR)/etc/ppp/ip-up.d ]; \
-       then echo "Installing ppp/ip-up script"; \
-       cp -f pppd/vnstat_ip-up $(DESTDIR)/etc/ppp/ip-up.d/vnstat; \
-       chmod 755 $(DESTDIR)/etc/ppp/ip-up.d/vnstat; \
-       fi
-       @if [ -d $(DESTDIR)/etc/ppp/ip-down.d ]; \
-       then echo "Installing ppp/ip-down script"; \
-       cp -f pppd/vnstat_ip-down $(DESTDIR)/etc/ppp/ip-down.d/vnstat; \
-       chmod 755 $(DESTDIR)/etc/ppp/ip-down.d/vnstat; \
-       fi
-
 # install default config if such doesn't exist
-       @if [ ! -f $(DESTDIR)/etc/vnstat.conf ]; \
-       then install -m 644 cfg/vnstat.conf $(DESTDIR)/etc; \
+       @if [ ! -f "$(DESTDIR)/etc/vnstat.conf" ]; \
+       then echo "Installing config to $(DESTDIR)/etc/vnstat.conf"; \
+       install -m 644 cfg/vnstat.conf $(DESTDIR)/etc; \
        fi
 
-       install -d $(BIN) $(MAN)/man1 $(CRON) $(DESTDIR)/var/lib/vnstat
+
+# install everything else
+       install -d -m 755 $(BIN) $(SBIN) $(MAN)/man1 $(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 page, gzip it if previous version was done so     
+# 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.1 $(MAN)/man1
+       @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 install -m 644 man/vnstat.1 $(MAN)/man1; \
-       gzip -f9 $(MAN)/man1/vnstat.1; \
-       else install -m 644 man/vnstat.1 $(MAN)/man1; \
+       then gzip -f9 $(MAN)/man1/vnstat.1; \
+       gzip -f9 $(MAN)/man1/vnstatd.1; \
+       gzip -f9 $(MAN)/man1/vnstat.conf.1; \
+       if [ -f "src/vnstati" ]; \
+       then gzip -f9 $(MAN)/man1/vnstati.1; \
+       fi; \
        fi
 
-       install -m 644 cron/vnstat $(CRON)
+       @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..."
@@ -59,12 +90,81 @@ uninstall:
        @echo "Note: this will also remove the database directory"
        @echo "including any database located there"
        @echo
-       @echo "Press CTRL-C to abort within 10 sec."
+       @echo "Press CTRL-C within 10 seconds to abort."
        @sleep 10
        rm -fr $(DESTDIR)/var/lib/vnstat
        rm -f $(BIN)/vnstat
-       rm -f $(MAN)/man1/vnstat.1*
-       rm -f $(CRON)/vnstat
+       rm -f $(BIN)/vnstati
+       rm -f $(SBIN)/vnstatd
+       rm -f $(MAN)/man1/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 -dm 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 -m 644 cfg/vnstat.conf $(DESTDIR)/etc; \
+       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.1 $(MAN_BSD)/man1
+       gzip -f9 $(MAN_BSD)/man1/vnstat.1
+       gzip -f9 $(MAN_BSD)/man1/vnstatd.1
+       gzip -f9 $(MAN_BSD)/man1/vnstat.conf.1
+       @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
+
+       @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 $(DESTDIR)/etc/vnstat.conf
+       @echo "A possible cron entry needs to be removed manually if such exists."
diff --git a/README b/README
index 5067de83ecb15fc052a318f1a2273e3950f4019f..1c8f83c40f47cb4d763d87b24899e7250c3702b0 100644 (file)
--- a/README
+++ b/README
@@ -1,30 +1,38 @@
-(Updated 27.11.2007 for version 1.5 and later)
+(Updated 6.4.2009 for version 1.7)
 
 
 What is vnStat
 ::::::::::::::
 
  In short, vnStat is a console-based network traffic monitor that uses the
- /proc filesystem to get the needed information. This means that vnStat
- won't actually be sniffing any traffic. 
+ 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 for few 'screenshots'.
+ See the webpage for 'screenshots'.
 
 
 Getting started
 :::::::::::::::
 
- First you should install vnStat. See the INSTALL file, it contains all needed
- information for the installing process. Users of version 1.0 should see the 
- UPGRADE file first.
+ 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. Users of version 1.0 should see the UPGRADE
+ file first.
 
 
-Available options
-:::::::::::::::::
+Available options and documentation
+:::::::::::::::::::::::::::::::::::
 
  A list of commonly used options is available with 'vnstat --help'. The
- complete list can be accessed with 'vnstat --longhelp'. See the man(1)
- page for option explanations.
+ 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
@@ -38,10 +46,12 @@ Usage tips
     vnstat --showconfig >newconfig
 
  The config file contains comments before any settings describing with
- the purpose and usage of each setting is.
+ 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.
 
- By default vnStat will use dd.mm.yy format in timestamps. For example
- this can be changed from the config file by locating lines
+ Note that by default vnStat will use dd.mm.yy format in timestamps.
For example this can be changed from the config file by locating lines
 
 DayFormat    "%d.%m."
 MonthFormat  "%b '%y"
@@ -53,7 +63,7 @@ DayFormat    "%m/%d"
 MonthFormat  "%b '%y"
 TopFormat    "%%m/%d/%y"
 
Note that some field have space limits and giving too long parameters
Some of these field have space limitations and giving too long parameters
  might affect the readability of the output. Changing date formats won't
  affect the database(s) in any way.
 
index 456e87743bb1ffcfc4f9a910d1847ad13affef24..26de04e3e2863d031ee6c5eda21a706feb3db48f 100644 (file)
--- a/UNINSTALL
+++ b/UNINSTALL
@@ -1,3 +1,7 @@
 
-Run 'make uninstall' and follow the instructions.
+Linux:
+   Run 'make uninstall' and follow the instructions.
+
+BSD:
+   Run 'make bsduninstall' and follow the instructions.
 
diff --git a/UPGRADE b/UPGRADE
index c0afd40cd167e010a960a0fb2fba88d4c0a12fd6..bc1b24ca7ea9ff86cf19da81742937122f3b620a 100644 (file)
--- a/UPGRADE
+++ b/UPGRADE
@@ -1,10 +1,11 @@
-(Updated 31.12.2007 for version 1.6)
+(Updated 30.3.2009 for version 1.7)
 
 
 This file will try to explain how vnStat is supposed to be upgraded 
-from version 1.0. The makefile should handle upgrades from version 1.1
-to 1.6 without user interaction. But for those still using 1.0, please
-read the instructions once before executing step by step.
+from version 1.0. The makefile should handle upgrades from any other
+older version to 1.7 without user interaction. But for those still
+using 1.0, please read the instructions once before executing step by
+step.
 
 But once again to make this clear:
 
@@ -36,4 +37,4 @@ What to do before upgrading from version 1.0?
 
  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 1.6.
+ installing 1.7.
index f36933231feacf1f2f66c9b11a80c99709cc1fef..43e3bc45b67cb99366c802ed4096a1729b105cc7 100644 (file)
@@ -1,4 +1,4 @@
-# vnStat 1.6 config file
+# vnStat 1.7 config file
 ##
 
 # location of the database directory
@@ -22,6 +22,11 @@ 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
+
 # default interface
 Interface "eth0"
 
@@ -41,8 +46,53 @@ Sampletime 5
 # 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
+
+
+# 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) interface data is saved to file
+SaveInterval 5
+
+# 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
+##
+
+HeaderFormat    "%d.%m.%Y %H:%M"
+
+# colors
+CBackground     "FFFFFF"
+CEdge           "AEAEAE"
+CHeader         "606060"
+CHeaderTitle    "FFFFFF"
+CHeaderDate     "FFFFFF"
+CText           "000000"
+CLine           "B0B0B0"
+CLineL          "-"
+CRx             "92CF00"
+CTx             "606060"
+CRxD            "-"
+CTxD            "-"
index df971cc2b829b48090523e81d030b8643f3c8f32..19ef530deb14f060f7da4c8f6b5947d621c2bff2 100644 (file)
@@ -1 +1,2 @@
+# run vnstat update every 5 minutes
 */5 *  * * *   root    if [ -x /usr/bin/vnstat ] && [ `ls /var/lib/vnstat/ | wc -l` -ge 1 ]; then /usr/bin/vnstat -u; fi
diff --git a/examples/vnstat.cgi b/examples/vnstat.cgi
new file mode 100755 (executable)
index 0000000..a6afa2a
--- /dev/null
@@ -0,0 +1,191 @@
+#!/usr/bin/perl -w
+
+# vnstat.cgi -- example cgi for vnStat image output
+# copyright (c) 2008-2009 Teemu Toivola <tst at iki dot fi>
+#
+# based on mailgraph.cgi
+# copyright (c) 2000-2007 ETH Zurich
+# copyright (c) 2000-2007 David Schweikert <dws@ee.ethz.ch>
+# 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.2";
+
+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 <<HEADER;
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<meta name="Generator" content="vnstat.cgi $VERSION">
+<title>Traffic Statistics for $host</title>
+<style type="text/css">
+<!--
+a { text-decoration: underline; }
+a:link { color: #b0b0b0; }
+a:visited { color: #b0b0b0; }
+a:hover { color: #000000; }
+small { font-size: 8px; color: #cbcbcb; }
+-->
+</style>
+</head>
+<body bgcolor="#ffffff">
+HEADER
+
+       for my $n (0..$#graphs) {
+               print "<p><a href=\"$scriptname?${n}-f\"><img src=\"$scriptname?${n}-hs\" border=\"0\" alt=\"$graphs[$n]{interface} summary\"></a></p>\n";
+       }
+
+       print <<FOOTER;
+<small>Images generated using <a href="http://humdi.net/vnstat/">vnStat</a> image output.</small>
+</body>
+</html>
+FOOTER
+}
+
+sub print_fullhtml($)
+{
+       my ($interface) = @_;
+
+       print "Content-Type: text/html\n\n";
+
+       print <<HEADER;
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<meta name="Generator" content="vnstat.cgi $VERSION">
+<title>Traffic Statistics for $host</title>
+<style type="text/css">
+<!--
+a { text-decoration: underline; }
+a:link { color: #b0b0b0; }
+a:visited { color: #b0b0b0; }
+a:hover { color: #000000; }
+small { font-size: 8px; color: #cbcbcb; }
+-->
+</style>
+</head>
+<body bgcolor="#ffffff">
+HEADER
+
+       print "<table border=\"0\"><tr><td>\n";
+       print "<img src=\"$scriptname?${interface}-s\" border=\"0\" alt=\"${interface} summary\">";
+       print "</td><td>\n";
+       print "<img src=\"$scriptname?${interface}-h\" border=\"0\" alt=\"${interface} hourly\">";
+       print "</td></tr><tr><td valign=\"top\">\n";
+       print "<img src=\"$scriptname?${interface}-d\" border=\"0\" alt=\"${interface} daily\">";
+       print "</td><td valign=\"top\">\n";
+       print "<img src=\"$scriptname?${interface}-t\" border=\"0\" alt=\"${interface} top 10\"><br>\n";
+       print "<img src=\"$scriptname?${interface}-m\" border=\"0\" alt=\"${interface} monthly\" vspace=\"4\">";
+       print "</td></tr>\n</table>\n";
+
+       print <<FOOTER;
+<small><br>&nbsp;Images generated using <a href="http://humdi.net/vnstat/">vnStat</a> image output.</small>
+</body>
+</html>
+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, 0777 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();
similarity index 100%
rename from pppd/vnstat_ip-down
rename to examples/vnstat_ip-down
similarity index 100%
rename from pppd/vnstat_ip-up
rename to examples/vnstat_ip-up
index 74db0c15b5664c6622d85977321dee9832d67219..43234bf5ced98638179b4961f028dd049bcfb831 100644 (file)
@@ -1,4 +1,4 @@
-.TH VNSTAT 1 "JANUARY 2008" "version 1.6" "User Manuals"
+.TH VNSTAT 1 "APRIL 2009" "version 1.7" "User Manuals"
 .SH NAME
 vnStat \- a console-based network traffic monitor
 .SH SYNOPSIS
@@ -34,6 +34,8 @@ vnStat \- a console-based network traffic monitor
 .B \-\-iface
 .I interface
 ] [
+.B \-\-iflist
+] [
 .B \-\-live
 ] [
 .B \-\-longhelp
@@ -53,6 +55,8 @@ vnStat \- a console-based network traffic monitor
 ] [
 .B \-\-showconfig
 ] [
+.B \-\-sync
+] [
 .B \-\-testkernel
 ] [
 .B \-\-top10
@@ -65,6 +69,8 @@ vnStat \- a console-based network traffic monitor
 .B \-\-version
 ] [
 .B \-\-weeks
+] [
+.B \-\-xml
 ]
 .SH DESCRIPTION
 .B vnStat
@@ -72,7 +78,10 @@ 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)
-filesystem. That way vnStat can be used even without root permissions.
+and
+.BR sys
+filesystems depending on availability. That way vnStat can be used even
+without root permissions on most systems.
 .SH OPTIONS
 .TP
 .BI "-d, --days"
@@ -112,6 +121,9 @@ Select one specific
 .I interface
 and apply actions to only it.
 .TP
+.BI "--iflist"
+Show list of currently available interfaces.
+.TP
 .BI "-q, --query"
 Force database query mode.
 .TP
@@ -176,40 +188,48 @@ if it's shifting.
 .BI "-D, --debug"
 Show additional debug output.
 .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 "--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.
-
-  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 MB
-  totaltx;2023708                 all time total transmitted MB
-  currx;621673719                 latest rx value in /proc
-  curtx;981730184                 latest tx value in /proc
-  totalrxk;644                    total rx kB counter
-  totaltxk;494                    total tx kB counter
-  btime;1059414541                system boot time in Unix time
+.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
+      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 MB, 7433 = tx MB, 68 = rx kB, 557 = tx kB and 1 tells that
+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.
-
-  m;0;1078092000;48649;139704;527;252;1   (x12)
-  t;0;1078351200;5979;47155;362;525;1     (x10)
-  h;0;1078699800;118265;516545            (x24)
+.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 kB value. For hours the forth and fifth fields
-have values in kB.
+except hours that doesn't have a separate KiB value. For hours the forth and fifth fields
+have values in KiB.
 .SH FILES
 .TP
 .I /var/lib/vnstat/
@@ -219,7 +239,9 @@ named according to the monitored interfaces.
 .I /etc/vnstat.conf
 Config file that will be used unless
 .I $HOME/.vnstatrc
-exists.
+exists. See
+.BR vnstat.conf (1)
+for more information.
 .SH EXAMPLES
 .BI "vnstat -u -i" 
 .I interface
@@ -252,25 +274,33 @@ to generate enough traffic to wrap the kernel interface traffic counter. Otherwi
 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 GB. Calculated theoretical times are:
-.PP
-    10 Mbit:  54 minutes
-   100 Mbit:   5 minutes
-  1000 Mbit:  30 seconds
-.PP
+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
 Estimated traffic values are likely to be somewhat inaccurate if daily
-traffic is low because only the MB counter is used to calculate the
+traffic is low because only the MiB counter is used to calculate the
 estimate.
 .PP
-Virtual and aliased interfaces can't be monitored because the kernel doesn't
+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 <tst at iki dot fi>
 .SH "SEE ALSO"
+.BR vnstatd (1),
+.BR vnstati (1),
+.BR vnstat.conf (1),
 .BR proc (5),
-.BR ifconfig (8)
+.BR ifconfig (8),
+.BR units (7)
diff --git a/man/vnstat.conf.1 b/man/vnstat.conf.1
new file mode 100644 (file)
index 0000000..4541911
--- /dev/null
@@ -0,0 +1,164 @@
+.TH VNSTAT.CONF 1 "APRIL 2009" "version 1.7" "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 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 Locale
+Locale setting to be used for prints. This replaces the LC_ALL
+environment variable. (vnstat and vnstati 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 "DayFormat, MonthFormat, TopFormat"
+Formatting of date in available outputs. Uses the same format as
+.BR date (1).
+(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 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 Interface
+Default interface used when no other interface is specified on
+the command line. (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 Sampletime
+Defines how many seconds the -tr option will sample traffic. (vnstat 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 CheckDiskSpace
+Enable or disable the checking of at least some free disk space before
+a database write. 1 = enabled, 0 = disabled.
+.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.
+.TP
+.BI BootVariation
+Time in seconds how much the boot time reported by system kernel can variate
+between updates.
+.SH DAEMON RELATED KEYWORDS
+.TP
+.BI UpdateInterval
+How often in seconds the interface data is updated.
+.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 UseLogging
+Enable or disable logging. 0 = disabled, 1 = logfile and 2 = syslog.
+.TP
+.BI LogFile
+Specify log file path and name to be used if UseLogging is set to 1.
+.TP
+.BI PidFile
+Specify pid file path and name to be used.
+.SH IMAGE OUTPUT RELATED KEYWORDS
+.TP
+.BI HeaderFormat
+Formatting of date in header. Uses the same format as
+.BR date (1).
+.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 CText
+Common 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 CTx
+Color for transmitted data.
+.TP
+.BI CRxD
+Darker version of received data color. Set to '-' in order to use
+a calculated value based on CRx.
+.TP
+.BI CTxD
+Darker version of transmitted data color. Set to '-' in order to use
+a calculated value based on CTx.
+.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 <tst at iki dot fi>
+.SH "SEE ALSO"
+.BR vnstat (1),
+.BR vnstati (1),
+.BR vnstatd (1),
+.BR units (7)
diff --git a/man/vnstatd.1 b/man/vnstatd.1
new file mode 100644 (file)
index 0000000..40708f9
--- /dev/null
@@ -0,0 +1,191 @@
+.TH VNSTATD 1 "APRIL 2009" "version 1.7" "User Manuals"
+.SH NAME
+vnStat daemon \- the alternative for cron based updating
+.SH SYNOPSIS
+.B vnstatd
+[
+.B \-Ddnpv?
+] [
+.B \-\-daemon
+] [
+.B \-\-nodaemon
+] [
+.B \-\-debug
+] [
+.B \-\-help
+] [
+.B \-\-version
+] [
+.B \-\-pidfile
+.I file
+] [
+.B \-\-config
+.I file
+]
+.SH DESCRIPTION
+The purpose of
+.B vnstatd
+is to provide an alternative way for updating
+.BR vnstat (1)
+databases. It 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. The
+daemon 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 and exit if no databases can be found. The reason for this behaviour
+is to avoid starting the daemon when it's clear that it wouldn't have
+anything to do.
+.SH OPTIONS
+.TP
+.BI "-d, --daemon"
+Fork process to background and run as a daemon.
+.TP
+.BI "-n, --nodaemon"
+Stay in foreground attached to the current terminal and start update
+process.
+.TP
+.BI "-D, --debug"
+Provide additional output for debug purposes. The process will stay
+attached to the terminal for output.
+.TP
+.BI "-?, --help"
+Show a command summary.
+.TP
+.BI "-v, --version"
+Show current version.
+.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 "--config " file
+Use
+.I file
+as config file instead of using normal config file search function.
+.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 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
+.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
+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 (1)
+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 <tst at iki dot fi>
+.SH "SEE ALSO"
+.BR vnstat (1),
+.BR vnstati (1),
+.BR vnstat.conf (1),
+.BR signal (7)
diff --git a/man/vnstati.1 b/man/vnstati.1
new file mode 100644 (file)
index 0000000..c5b8e08
--- /dev/null
@@ -0,0 +1,158 @@
+.TH VNSTATI 1 "APRIL 2009" "version 1.7" "User Manuals"
+.SH NAME
+vnStat image output \- png image output support for vnStat
+.SH SYNOPSIS
+.B vnstati
+[
+.B \-cdhimorstv?
+] [
+.B \-i
+.I interface
+] [
+.B \-\-hours
+] [
+.B \-\-days
+] [
+.B \-\-months
+] [
+.B \-\-top10
+] [
+.B \-\-summary
+] [
+.B \-hs
+] [
+.B \-\-hsummary
+] [
+.B \-vs
+] [
+.B \-\-vsummary
+] [
+.B \-ne
+] [
+.B \-\-noedge
+] [
+.B \-nh
+] [
+.B \-\-noheader
+] [
+.B \-\-rate
+] [
+.B \-\-output
+.I file
+] [
+.B \-\-cache
+.I time
+] [
+.B \-\-iface
+.I interface
+] [
+.B \-\-config
+.I file
+] [
+.B \-\-help
+] [
+.B \-\-version
+]
+.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 "-s, --summary"
+Output traffic summary.
+.TP
+.BI "-hs, --hsummary"
+Output traffic summary including hourly data using a horizontal layout.
+.TP
+.BI "-vs, --vsummary"
+Output traffic summary including hourly data using a vertical layout.
+.TP
+.BI "-d, --days"
+Output traffic for days.
+.TP
+.BI "-h, --hours"
+Output traffic for the last 24 hours.
+.TP
+.BI "-m, --months"
+Output traffic for months.
+.TP
+.BI "-t, --top10"
+Output all time top10 traffic days.
+.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 "-r, --rate"
+Show traffic in hourly output as hourly average rate instead of showing
+how much was transferred. This option has effect only when the selected output
+contains the hourly output.
+.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 "-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 "-i, --iface " interface
+Use
+.I interface
+instead of default or configured interface.
+.TP
+.BI "--config " file
+Use
+.I file
+as config file instead of using normal config file search function.
+.TP
+.BI "-?, --help"
+Show a command summary.
+.TP
+.BI "-v, --version"
+Show current version.
+.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 (1)
+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 -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 <tst at iki dot fi>
+.SH "SEE ALSO"
+.BR vnstat (1),
+.BR vnstatd (1),
+.BR vnstat.conf (1),
+.BR units (7)
index c6960ac2c15f1dfa5020210431ee255fa3d857bf..1f25a88f155592511850bf8051e2c3d2ebfee838 100644 (file)
@@ -1,26 +1,35 @@
 CC = gcc
 CFLAGS = -O2
-OBJS = ifinfo.o db.o dbaccess.o misc.o cfg.o
-
-default: vnstat
-
-vnstat: $(OBJS) vnstat.c vnstat.h
-       $(CC) $(CFLAGS) -lm -o vnstat vnstat.c $(OBJS)
-
-ifinfo.o: ifinfo.c ifinfo.h vnstat.h db.h misc.h cfg.h
-       $(CC) $(CFLAGS) -c ifinfo.c
-
-db.o: db.c db.h dbaccess.h ifinfo.h misc.h vnstat.h
-       $(CC) $(CFLAGS) -c db.c
-
-dbaccess.o: dbaccess.c dbaccess.h vnstat.h
-       $(CC) $(CFLAGS) -c dbaccess.c
-
-misc.o: misc.c misc.h vnstat.h
-       $(CC) $(CFLAGS) -c misc.c
-
-cfg.o: cfg.c cfg.h
-       $(CC) $(CFLAGS) -c cfg.c
+LDLIBS = -lm
+OBJS = vnstat.o ifinfo.o dbxml.o dbshow.o dbaccess.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 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 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
+
+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
+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
+       rm -f *.o *~ core *.i vnstat vnstatd vnstati
index b36e1e772719b71b63bb7402c844eca7daa258c7..7fd333a0ba066ecbae8890f223ff026420236c16 100644 (file)
--- a/src/cfg.c
+++ b/src/cfg.c
@@ -1,8 +1,8 @@
-#include "vnstat.h"
+#include "common.h"
 #include "cfg.h"
 
-void printcfgfile(void) {
-
+void printcfgfile(void)
+{
        ibwnode *p = ifacebw;
 
        printf("# vnStat %s config file\n", VNSTATVERSION);
@@ -29,6 +29,11 @@ void printcfgfile(void) {
        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("# default interface\n");
        printf("Interface \"%s\"\n\n", cfg.iface);
 
@@ -54,27 +59,70 @@ void printcfgfile(void) {
        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", cfg.bvar);
 
-}
+       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) interface data is saved to file\n");
+       printf("SaveInterval %d\n\n", cfg.saveinterval);
+
+       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);
 
-int loadcfg(char *cfgfile) {
+       printf("# file used as daemon pid / lock file\n");
+       printf("PidFile \"%s\"\n", cfg.pidfile);
 
+       printf("\n\n");
+
+       printf("# vnstati\n##\n\n");
+       printf("HeaderFormat    \"%s\"\n\n", cfg.hformat);
+       printf("# 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(char *cfgfile)
+{
        FILE *fd;
        char buffer[512];
        int i, j, k, linelen, cfglen, tryhome;
 
        char value[512], cfgline[512];
     
-       char *cfgname[] = { "DatabaseDir", "Locale", "MonthRotate", "DayFormat", "MonthFormat", "TopFormat", "RXCharacter", "TXCharacter", "RXHourCharacter", "TXHourCharacter", "Interface", "MaxBandwidth", "Sampletime", "QueryMode", "UseFileLocking", "BootVariation", 0 };
-       char *cfglocc[] = { cfg.dbdir, cfg.locale, 0, cfg.dformat, cfg.mformat, cfg.tformat, cfg.rxchar, cfg.txchar, cfg.rxhourchar, cfg.txhourchar, cfg.iface, 0, 0, 0, 0, 0 };
-       int *cfgloci[] = { 0, 0, &cfg.monthrotate, 0, 0, 0, 0, 0, 0, 0, 0, &cfg.maxbw, &cfg.sampletime, &cfg.qmode, &cfg.flock, &cfg.bvar };
-       int cfgnamelen[] = { 512, 32, 0, 64, 64, 64, 1, 1, 1, 1, 32, 0, 0, 0, 0, 0 };
-       int cfgfound[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+       char *cfgname[] = { "DatabaseDir", "Locale", "MonthRotate", "DayFormat", "MonthFormat", "TopFormat", "RXCharacter", "TXCharacter", "RXHourCharacter", "TXHourCharacter", "UnitMode", "Interface", "MaxBandwidth", "Sampletime", "QueryMode", "CheckDiskSpace", "UseFileLocking", "BootVariation", "UpdateInterval", "PollInterval", "SaveInterval", "UseLogging", "LogFile", "PidFile", "CBackground", "CEdge", "CHeader", "CHeaderTitle", "CHeaderDate", "CText", "CLine", "CLineL", "CRx", "CRxD", "CTx", "CTxD", "HeaderFormat", 0 };
+       char *cfglocc[] = { cfg.dbdir, cfg.locale, 0, cfg.dformat, cfg.mformat, cfg.tformat, cfg.rxchar, cfg.txchar, cfg.rxhourchar, cfg.txhourchar, 0, cfg.iface, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, cfg.logfile, cfg.pidfile, cfg.cbg, cfg.cedge, cfg.cheader, cfg.cheadertitle, cfg.cheaderdate, cfg.ctext, cfg.cline, cfg.clinel, cfg.crx, cfg.crxd, cfg.ctx, cfg.ctxd, cfg.hformat };
+       short *cfgloci[] = { 0, 0, &cfg.monthrotate, 0, 0, 0, 0, 0, 0, 0, &cfg.unit, 0, &cfg.maxbw, &cfg.sampletime, &cfg.qmode, &cfg.spacecheck, &cfg.flock, &cfg.bvar, &cfg.updateinterval, &cfg.pollinterval, &cfg.saveinterval, &cfg.uselogging, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+       short cfgnamelen[] = { 512, 32, 0, 64, 64, 64, 1, 1, 1, 1, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 512, 512, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 32 };
+       short cfgfound[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
 
        ifacebw = NULL;
 
@@ -95,7 +143,9 @@ int loadcfg(char *cfgfile) {
                        if (debug)
                                printf("Config file: --config\n");
                } else {
-                       printf("Error:\nUnable to open given config file \"%s\".\n", cfgfile);
+                       snprintf(errorstring, 512, "Unable to open given config file \"%s\".\n", cfgfile);
+                       printe(PT_Error);
+                       return 0;
                }
        
        } else {
@@ -118,7 +168,7 @@ int loadcfg(char *cfgfile) {
                } else {
                        if (debug)
                                printf("Config file: none\n");
-                       return 0;
+                       return 1;
                }
        }
 
@@ -130,7 +180,9 @@ int loadcfg(char *cfgfile) {
                cfgline[0] = '\0';
                
                /* get current line */
-               fgets(cfgline, 512, fd);
+               if (fgets(cfgline, 512, fd)==NULL) {
+                       break;
+               }
                
                linelen = strlen(cfgline);
                if (linelen>2 && cfgline[0]!='#') {
@@ -200,17 +252,119 @@ int loadcfg(char *cfgfile) {
        if (debug)
                ibwlist();
 
+       /* validate config */
+       validatecfg();
+
        return 1;
 
 }
 
-void defaultcfg(void) {
+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.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>31) {
+               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.updateinterval<cfg.pollinterval || cfg.updateinterval>300) {
+               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)<cfg.updateinterval || cfg.saveinterval>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.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);
+       }
+}
+
+void defaultcfg(void)
+{
+       ibwflush();
 
        cfg.bvar = BVAR;
        cfg.qmode = DEFQMODE;
        cfg.sampletime = DEFSAMPTIME;
        cfg.monthrotate = MONTHROTATE;
+       cfg.unit = UNITMODE;
        cfg.maxbw = DEFMAXBW;
+       cfg.spacecheck = USESPACECHECK;
        cfg.flock = USEFLOCK;
        strncpy(cfg.dbdir, DATABASEDIR, 512);
        strncpy(cfg.iface, DEFIFACE, 32);
@@ -218,25 +372,45 @@ void defaultcfg(void) {
        strncpy(cfg.dformat, DFORMAT, 64);
        strncpy(cfg.mformat, MFORMAT, 64);
        strncpy(cfg.tformat, TFORMAT, 64);
+       strncpy(cfg.hformat, HFORMAT, 32);
        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.uselogging = USELOGGING;
+       strncpy(cfg.logfile, LOGFILE, 512);
+       strncpy(cfg.pidfile, PIDFILE, 512);
        
+       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(char *iface, int limit)
 {
-       ibwnode *n = malloc(sizeof(ibwnode));
+       ibwnode *p = malloc(sizeof(ibwnode));
 
-       if (n == NULL) {
+       if (p == NULL) {
                return 0;
        }
 
-       n->next = ifacebw;
-       ifacebw = n;
-       strcpy(n->interface, iface);
-       n->limit = limit;
+       p->next = ifacebw;
+       ifacebw = p;
+       strncpy(p->interface, iface, 32);
+       p->limit = limit;
 
        return 1;
 }
@@ -263,36 +437,44 @@ void ibwlist(void)
 int ibwget(char *iface)
 {
        ibwnode *p = ifacebw;
-       
-       if (p == NULL) {
 
-               if (cfg.maxbw>0) {
-                       return cfg.maxbw;
-               } else {
-                       return -1;
+       /* 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 {
-       
-               while (p != NULL) {
-                       if (strcasecmp(p->interface, iface)==0) {
-                               if (p->limit>0) {
-                                       return p->limit;
-                               } else {
-                                       return -1;
-                               }
-                       }
-                       p = p->next;
-               }
-               
                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;
+       int i, j, linelen, count = 0, ivalue;
 
        /* start from value search from first line */
        rewind(fd);
@@ -303,7 +485,9 @@ int ibwcfgread(FILE *fd)
                cfgline[0] = '\0';
 
                /* get current line */
-               fgets(cfgline, 512, fd);
+               if (fgets(cfgline, 512, fd)==NULL) {
+                       break;
+               }
 
                linelen = strlen(cfgline);
 
@@ -358,8 +542,14 @@ int ibwcfgread(FILE *fd)
                                        continue;
                                }
                        
-                               /* add interface and limit to list */
-                               ibwadd(name, atoi(value));
+                               /* add interface and limit to list if value is within limits */
+                               ivalue = atoi(value);
+                               if (ivalue<0 || ivalue>10000) {
+                                       snprintf(errorstring, 512, "Invalid value \"%d\" for MaxBW%s, ignoring parameter.", ivalue, name);
+                                       printe(PT_Config);
+                               } else {
+                                       ibwadd(name, ivalue);
+                               }
                        }
                }
        }
index 66cc5c9547b4efb77b9a0f3f3248e9ab9e60c9c2..9a10ea5b55c63be9ead9340f4915491e27f7f031 100644 (file)
--- a/src/cfg.h
+++ b/src/cfg.h
@@ -1,7 +1,14 @@
+#ifndef CFG_H
+#define CFG_H
+
 void printcfgfile(void);
 int loadcfg(char *cfgfile);
+void validatecfg(void);
 void defaultcfg(void);
 int ibwadd(char *iface, int limit);
 void ibwlist(void);
 int ibwget(char *iface);
+void ibwflush(void);
 int ibwcfgread(FILE *fd);
+
+#endif
diff --git a/src/common.c b/src/common.c
new file mode 100644 (file)
index 0000000..e26c032
--- /dev/null
@@ -0,0 +1,126 @@
+#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(&current));
+
+               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;
+}
diff --git a/src/common.h b/src/common.h
new file mode 100644 (file)
index 0000000..a4bbc4d
--- /dev/null
@@ -0,0 +1,244 @@
+#ifndef COMMON_H
+#define COMMON_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <locale.h>
+#include <time.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <signal.h>
+#include <math.h>
+#include <errno.h>
+#include <sys/file.h>
+#include <inttypes.h>
+#include <syslog.h>
+#include <sys/statvfs.h>
+
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+#include <net/if.h>
+#include <ifaddrs.h>
+#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(__linux__)
+#define DATABASEDIR "/var/lib/vnstat"
+#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
+#define DATABASEDIR "/var/db/vnstat"
+#endif
+
+/* on which day should months change */
+#define MONTHROTATE 1
+
+/* date output formats for -d, -m, -t, -w  and image header*/
+/* see 'man date' for control codes */
+#define DFORMAT "%d.%m."
+#define MFORMAT "%b '%y"
+#define TFORMAT "%d.%m.%y"
+#define HFORMAT "%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
+
+/* default interface */
+#define DEFIFACE "eth0"
+
+/* default locale */
+#define LOCALE "en_US"
+
+/* 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
+
+/* 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.7"
+
+/* xml format version */
+/* 1 = 1.7- */
+#define XMLVERSION 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 USELOGGING 2
+#define LOGFILE "/var/log/vnstat.log"
+#define PIDFILE "/var/run/vnstat.pid"
+
+/* 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[32];
+       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;
+       short bvar;
+       short qmode;
+       short sampletime;
+       short monthrotate;
+       short maxbw;
+       short flock;
+       short spacecheck;
+       char logfile[512], pidfile[512];
+       short updateinterval, pollinterval, saveinterval, 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);
+
+/* 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
diff --git a/src/db.h b/src/db.h
deleted file mode 100644 (file)
index 7d44c8e..0000000
--- a/src/db.h
+++ /dev/null
@@ -1,6 +0,0 @@
-void showdb(int qmode);
-void showhours(void);
-void cleanhours(void);
-void rotatedays(void);
-void rotatemonths(void);
-void synccounters(char iface[32], char dirname[512]);
index 61ed937d52bcc221e0585f39cc3afc3ffc66917d..c2d3ca06f6c3fa734de91b29a170822b34757c46 100644 (file)
@@ -1,4 +1,4 @@
-#include "vnstat.h"
+#include "common.h"
 #include "dbaccess.h"
 
 int readdb(char iface[32], char dirname[512])
@@ -13,55 +13,114 @@ int readdb(char iface[32], char dirname[512])
        if ((db=fopen(file,"r"))!=NULL) {
        
                /* lock file */
-               lockdb(fileno(db), 0);
+               if (!lockdb(fileno(db), 0)) {
+                       return -1;
+               }
 
-               fread(&data,sizeof(DATA),1,db);
-               if (debug)
-                       printf("Database loaded for interface \"%s\"...\n",data.interface);
+               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.version!=DBVERSION) {
-                       printf("Trying to convert database \"%s\" (v%d) to current db format\n", file, data.version);
+               if (data.version<DBVERSION) {
+                       if (data.version!=-1) {
+                               snprintf(errorstring, 512, "Trying to convert database \"%s\" (v%d) to current db format", file, data.version);
+                               printe(PT_Info);
+                       }
 
-                       if (!convertdb(db)) {
+                       if ((data.version==-1) || (!convertdb(db))) {
 
                                /* close current db and try using backup if database conversion failed */
                                fclose(db);
                                if ((db=fopen(backup,"r"))!=NULL) {
                                
                                        /* lock file */
-                                       lockdb(fileno(db), 0);
+                                       if (!lockdb(fileno(db), 0)) {
+                                               fclose(db);
+                                               return -1;
+                                       }
                                
-                                       fread(&data,sizeof(DATA),1,db);
-                                       if (debug)
-                                               printf("Database loaded for interface \"%s\"...\n",data.interface);
+                                       if (fread(&data,sizeof(DATA),1,db)==0) {
+                                               snprintf(errorstring, 512, "Database load failed even when using backup. Aborting.");
+                                               printe(PT_Error);
+                                               fclose(db);
+                                               
+                                               if (noexit) {
+                                                       return -1;
+                                               } else {
+                                                       exit(1);
+                                               }
+                                       } else {
+                                               if (debug) {
+                                                       printf("db: Database loaded for interface \"%s\"...\n", data.interface);
+                                               }
+                                       }
 
                                        if (data.version!=DBVERSION) {
                                                if (!convertdb(db)) {
-                                                       printf("Error:\nUnable to use backup database.\n");
-                                                       exit(1);
+                                                       snprintf(errorstring, 512, "Unable to use backup database.");
+                                                       printe(PT_Error);
+                                                       fclose(db);
+                                                       
+                                                       if (noexit) {
+                                                               return -1;
+                                                       } else {
+                                                               exit(1);
+                                                       }
                                                }
                                        }
-                                       printf("Database possibly corrupted, using backup instead.\n");
+                                       snprintf(errorstring, 512, "Database possibly corrupted, using backup instead.");
+                                       printe(PT_Info);
                                } else {
-                                       printf("Error:\nUnable to open backup database \"%s\".\n", backup);
-                                       exit(1);
+                                       snprintf(errorstring, 512, "Unable to open backup database \"%s\".", backup);
+                                       printe(PT_Error);
+                                       if (noexit) {
+                                               return -1;
+                                       } else {
+                                               exit(1);
+                                       }
                                }
                        }
+
+               } else if (data.version>DBVERSION) {
+                       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(1);
+                       }               
                }
 
                fclose(db);
                        
                if (strcmp(data.interface,iface)) {
-                       printf("Warning:\nThe previous interface for this file was \"%s\".\n",data.interface);
-                       printf("It has now been replaced with \"%s\".\n\n",iface);
-                       printf("You can ignore this message if you renamed the filename.\n");
-                       if (strcmp(data.interface, data.nick)==0)
+                       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 {
-               printf("Error:\nUnable to read database \"%s\".\n",file);
+               snprintf(errorstring, 512, "Unable to read database \"%s\".",file);
+               printe(PT_Error);
+               
                newdb=1;
                
                /* set default values for a new database */
@@ -121,7 +180,7 @@ int readdb(char iface[32], char dirname[512])
        return newdb;
 }
 
-void writedb(char iface[32], char dirname[512], int newdb)
+int writedb(char iface[32], char dirname[512], int newdb)
 {
        FILE *db;
        char file[512], backup[512];
@@ -129,31 +188,80 @@ void writedb(char iface[32], char dirname[512], int newdb)
        snprintf(file, 512, "%s/%s", dirname, iface);
        snprintf(backup, 512, "%s/.%s", dirname, iface);
 
-       /* try to make backup of old file */
-       rename(file, backup);
-
+       /* 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 */
-               lockdb(fileno(db), 1);
+               if (!lockdb(fileno(db), 1)) {
+                       return 0;
+               }
 
                data.lastupdated=time(NULL);
-               fwrite(&data,sizeof(DATA),1,db);
-               if (debug)
-                       printf("Database saved...\n");
-               fclose(db);
-               if (newdb)
-                       printf("-> A new database has been created.\n");
+               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) {
+                               snprintf(errorstring, 512, "-> A new database has been created.");
+                               printe(PT_Info);
+                       }
+               }
        } else {
-               printf("Error:\nUnable to write database \"%s\".\n",file);
-               printf("Make sure it's write enabled for this user.\n");
-               printf("Database not updated.\n");
+               snprintf(errorstring, 512, "Unable to write database \"%s\".", file);
+               printe(PT_Error);
+
+               return 0;
        }
+       
+       return 1;
 }
 
+int backupdb(char *current, 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 = 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)
 {
@@ -178,10 +286,15 @@ int convertdb(FILE *db)
        /* version 1.0 database format */
        if (data.version==1) {
                
-               printf("Converting to db v2...\n");
+               snprintf(errorstring, 512, "Converting to db v2...\n");
+               printe(PT_Info);
                
                rewind(db);
-               fread(&data10, sizeof(DATA10), 1, 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;
@@ -267,8 +380,8 @@ int convertdb(FILE *db)
                                        month=11;
                                } else {
                                        month=-1;
-                                       printf("Convertion for date \"%s\" failed.\n",data10.top10[i].date);
-                                       printf("Reseting top10...\n");
+                                       snprintf(errorstring, 512, "Convertion for date \"%s\" failed.", data10.top10[i].date);
+                                       printe(PT_Error);
                                        
                                }
 
@@ -313,7 +426,11 @@ int convertdb(FILE *db)
                /* don't read from file if already in memory */
                if (converted==0) {
                        rewind(db);
-                       fread(&data12, sizeof(DATA12), 1, 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 */
@@ -388,19 +505,26 @@ int convertdb(FILE *db)
        
        /* corrupted or unknown version handling */
        if (data.version==0) {
-               printf("Error:\nUnable to convert corrupted database.\n");
+               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) {
-               printf("Error:\nUnable to convert database version \"%d\".\n", data.version);
+               snprintf(errorstring, 512, "Unable to convert database version \"%d\".", data.version);
+               printe(PT_Error);
                return 0;
        }
 
-       printf("Convertion done.\n");
+       snprintf(errorstring, 512, "Conversion done.");
+       printe(PT_Info);
 
        return 1;
 }
 
-void lockdb(int fd, int dbwrite)
+int lockdb(int fd, int dbwrite)
 {
        int operation, locktry=1;
 
@@ -416,17 +540,19 @@ void lockdb(int fd, int dbwrite)
                /* try locking file */
                while (flock(fd, operation)!=0) {
                
-                       if (debug)
-                               printf("Database access locked (%d, %d)\n", dbwrite, locktry);
+                       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) {
-                                       printf("Error:\nLocking database file for write failed for %d tries:\n%s (%d)\n", locktry, strerror(errno), errno);
+                                       snprintf(errorstring, 512, "Locking database file for write failed for %d tries:\n%s (%d)", locktry, strerror(errno), errno);
                                } else {
-                                       printf("Error:\nLocking database file for read failed for %d tries:\n%s (%d)\n", locktry, strerror(errno), errno);
-                               }       
-                               exit(1);                                
+                                       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 */
@@ -436,14 +562,141 @@ void lockdb(int fd, int dbwrite)
                        /* real error */
                        } else {
                                if (dbwrite) {
-                                       printf("Error:\nLocking database file for write failed:\n%s (%d)\n", strerror(errno), errno);
+                                       snprintf(errorstring, 512, "Locking database file for write failed:\n%s (%d)", strerror(errno), errno);
                                } else {
-                                       printf("Error:\nLocking database file for read failed:\n%s (%d)\n", strerror(errno), errno);
+                                       snprintf(errorstring, 512, "Locking database file for read failed:\n%s (%d)", strerror(errno), errno);
                                }
-                               exit(1);
+                               printe(PT_Error);
+                               return 0;
                        }
        
                        locktry++;
                }
        }
+       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 (%d) cleaned.\n", i, (int)data.hour[i].date);
+                       }
+               }
+       }
+       
+       /* clean current if it's not from today to avoid incrementing old data */
+       d=localtime(&current);
+       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 (%d) cleaned.\n", hour, (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);
+       }
 }
index e0379990fe140d6d3540cda852dbc6e0d05de15e..49d8e90f745fc760f7ac6d1c436c47fb29fcec07 100644 (file)
@@ -1,7 +1,15 @@
+#ifndef DBACCESS_H
+#define DBACCESS_H
+
 int readdb(char iface[32], char dirname[512]);
-void writedb(char iface[32], char dirname[512], int newdb);
+int writedb(char iface[32], char dirname[512], int newdb);
+int backupdb(char *current, char *backup);
 int convertdb(FILE *db);
-void lockdb(int fd, int dbwrite);
+int lockdb(int fd, int dbwrite);
+void cleanhours(void);
+void rotatedays(void);
+void rotatemonths(void);
+
 
 /* version 1.0 database format aka db v1 */
 typedef struct {
@@ -53,3 +61,5 @@ typedef struct {
        DAY12 top10[10];
        uint64_t btime;
 } DATA12;
+
+#endif
diff --git a/src/dbcache.c b/src/dbcache.c
new file mode 100644 (file)
index 0000000..e059029
--- /dev/null
@@ -0,0 +1,240 @@
+#include "common.h"
+#include "ifinfo.h"
+#include "dbaccess.h"
+#include "dbcache.h"
+
+int dataadd(char *iface)
+{
+       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->filled = 0;
+
+       if (debug) {
+               printf("cache: %s added\n", iface);
+       }
+
+       return 1;
+}
+
+int dataupdate(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 datashow(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");
+       }
+}
+
+int dataget(char *iface)
+{
+       datanode *p = dataptr;
+       
+       if (debug) {
+               printf("cache search: %s... ", iface);
+       }
+       
+       while (p != NULL) {
+               if (strcmp(p->data.interface, iface)==0) {
+                       if (p->filled) {
+                               memcpy(&data, &p->data, sizeof(data));
+                       }
+                       if (debug) {
+                               printf("found (%d)\n", p->filled);
+                       }
+                       return p->filled;
+               }
+               p = p->next;
+       }
+
+       if (debug) {
+               printf("not found\n");
+       }
+       return 0;
+}
+
+/* flush cached data to disk and free all memory allocted for it */
+void dataflush(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 datacount(void)
+{
+       datanode *p = dataptr;
+       int i = 0;
+
+       while (p != NULL) {
+               i++;
+               p = p->next;
+       }
+       
+       return i;
+}
+
+uint32_t dbcheck(uint32_t dbhash)
+{
+       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, 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 <= strlen(ifacelist)) {
+                                       sscanf(ifacelist+offset, "%32s", interface);
+                                       if (strcmp(p->data.interface, interface)==0) {
+                                               found = 1;
+                                               break;
+                                       }
+                                       offset += strlen(interface)+1;
+                               }
+                               
+                               if (p->data.active==1 && found==0) {
+                                       p->data.active = 0;
+                                       p->data.currx = p->data.curtx = 0;
+                                       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;
+                                       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;
+}
diff --git a/src/dbcache.h b/src/dbcache.h
new file mode 100644 (file)
index 0000000..446f9ad
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef DBCACHE_H
+#define DBCACHE_H
+
+int dataadd(char *iface);
+int dataupdate(void);
+void datashow(void);
+int dataget(char *iface);
+void dataflush(char *dirname);
+int datacount(void);
+uint32_t dbcheck(uint32_t dbhash);
+uint32_t simplehash(const char *data, int len);
+
+typedef struct datanode {
+       DATA data;
+       short filled;
+       struct datanode *next;
+} datanode;
+
+/* global variables */
+datanode *dataptr;
+
+#endif
similarity index 60%
rename from src/db.c
rename to src/dbshow.c
index 126f425614546da5098ae4bc3d7e0eb259b54014..3d6898150e70d781f64a39f05335a165f28388f1 100644 (file)
--- a/src/db.c
@@ -1,16 +1,14 @@
-#include "vnstat.h"
-#include "db.h"
-#include "dbaccess.h"
-#include "ifinfo.h"
+#include "common.h"
 #include "misc.h"
+#include "dbshow.h"
 
 void showdb(int qmode)
 {
-       float rx, tx;
+       float rxp, txp;
        int i, used, week, temp;
        struct tm *d;
        char datebuff[16];
-       char daytemp[128];
+       char daytemp[32], daytemp2[32];
        uint64_t e_rx, e_tx, t_rx, t_tx, max;
        int t_rxk, t_txk;
        time_t current, yesterday;
@@ -30,10 +28,28 @@ void showdb(int qmode)
                /* total with today and possible yesterday */
                if (qmode==0) {
                
+                       /* get formated date for yesterday */
                        d=localtime(&current);
+                       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);
+                       }
                        
-                       rx=(data.totalrx/((float)data.totalrx+data.totaltx))*100;
-                       tx=(data.totaltx/((float)data.totalrx+data.totaltx))*100;
+                       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;
+
+                       /* 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;
@@ -48,14 +64,14 @@ void showdb(int qmode)
                        
                        if (strcmp(data.interface, data.nick)==0) {
                                if (data.active)
-                                       printf("\t%s\n\n", data.interface);
+                                       printf("        %s\n\n", data.interface);
                                else
-                                       printf("\t%s [disabled]\n\n", data.interface);
+                                       printf("        %s [disabled]\n\n", data.interface);
                        } else {
                                if (data.active)
-                                       printf("\t%s (%s)\n\n", data.nick, data.interface);
+                                       printf("        %s (%s)\n\n", data.nick, data.interface);
                                else
-                                       printf("\t%s (%s) [disabled]\n\n", data.nick, data.interface);
+                                       printf("        %s (%s) [disabled]\n\n", data.nick, data.interface);
                        }
                        
                        /* get formated date for yesterday */
@@ -71,29 +87,34 @@ void showdb(int qmode)
                                strncpy(daytemp, "yesterday", 32);
                        }
                        
-                       printf("\t   received: %s", getvalue(data.totalrx, data.totalrxk, 10));
-                       printf(" (%.1f%%)\n",rx);
-                       printf("\ttransmitted: %s", getvalue(data.totaltx, data.totaltxk, 10));
-                       printf(" (%.1f%%)\n",tx);
-                       printf("\t      total: %s", getvalue(data.totalrx+data.totaltx, data.totalrxk+data.totaltxk, 10));
-                       printf("\n\n");
+                       /* get formated date for creation date */
+                       d=localtime(&data.created);
+                       strftime(datebuff, 16, cfg.tformat, d);
+                       
+                       printf("           received: %s", getvalue(data.totalrx, data.totalrxk, 10, 1));
+                       printf("   (%.1f%%)\n", rxp);
+                       printf("        transmitted: %s", getvalue(data.totaltx, data.totaltxk, 10, 1));
+                       printf("   (%.1f%%)\n", txp);
+                       printf("              total: %s", getvalue(data.totalrx+data.totaltx, data.totalrxk+data.totaltxk, 10, 1));
+                       printf("   since %s\n", datebuff);
+                       printf("\n");
                        
-                       printf("\t                rx     |     tx     |  total\n");
-                       printf("\t-----------------------+------------+-----------\n");
+                       printf("                        rx      |     tx      |   total\n");
+                       printf("        ------------------------+-------------+------------\n");
                        if (data.day[1].date!=0) {
-                               printf("\t%9s   %s", daytemp, getvalue(data.day[1].rx, data.day[1].rxk, 7));
-                               printf(" | %s", getvalue(data.day[1].tx, data.day[1].txk, 7));
-                               printf(" | %s", getvalue(data.day[1].rx+data.day[1].tx, data.day[1].rxk+data.day[1].txk, 7));
+                               printf("        %9s   %s", daytemp, getvalue(data.day[1].rx, data.day[1].rxk, 7, 1));
+                               printf(" | %s", getvalue(data.day[1].tx, data.day[1].txk, 7, 1));
+                               printf(" | %s", getvalue(data.day[1].rx+data.day[1].tx, data.day[1].rxk+data.day[1].txk, 7, -1));
                                printf("\n");
                        }
-                       printf("\t    today   %s", getvalue(data.day[0].rx, data.day[0].rxk, 7));
-                       printf(" | %s", getvalue(data.day[0].tx, data.day[0].txk, 7));
-                       printf(" | %s", getvalue(data.day[0].rx+data.day[0].tx, data.day[0].rxk+data.day[0].txk, 7));
+                       printf("        %9s   %s", daytemp2, getvalue(data.day[0].rx, data.day[0].rxk, 7, 1));
+                       printf(" | %s", getvalue(data.day[0].tx, data.day[0].txk, 7, 1));
+                       printf(" | %s", getvalue(data.day[0].rx+data.day[0].tx, data.day[0].rxk+data.day[0].txk, 7, -1));
                        printf("\n");
-                       printf("\t-----------------------+------------+-----------\n");
-                       printf("\testimated   %s", getvalue(e_rx, 0, -7));
-                       printf(" | %s", getvalue(e_tx, 0, -7));
-                       printf(" | %s", getvalue(e_rx+e_tx, 0, -7));
+                       printf("        ------------------------+-------------+------------\n");
+                       printf("        estimated   %s", getvalue(e_rx, 0, 7, 2));
+                       printf(" | %s", getvalue(e_tx, 0, 7, 2));
+                       printf(" | %s", getvalue(e_rx+e_tx, 0, 7, -2));
                        printf("\n");
 
                /* days */
@@ -141,28 +162,29 @@ void showdb(int qmode)
                                if (data.day[i].used) {
                                        d=localtime(&data.day[i].date);
                                        strftime(datebuff, 16, cfg.dformat, d);
-                                       printf(" %8s   %s", datebuff, getvalue(data.day[i].rx, data.day[i].rxk, 7));
-                                       printf("  | %s", getvalue(data.day[i].tx, data.day[i].txk, 7));
-                                       printf("  | %s", getvalue(data.day[i].rx+data.day[i].tx, data.day[i].rxk+data.day[i].txk, 7));
+                                       printf(" %8s   %s", datebuff, getvalue(data.day[i].rx, data.day[i].rxk, 7, 1));
+                                       printf(" | %s", getvalue(data.day[i].tx, data.day[i].txk, 7, 1));
+                                       printf(" | %s", getvalue(data.day[i].rx+data.day[i].tx, data.day[i].rxk+data.day[i].txk, 7, 1));
                                        showbar(data.day[i].rx, data.day[i].rxk, data.day[i].tx, data.day[i].txk, max, 25);
                                        printf("\n");
                                        used++;
                                }
                        }
                        if (used==0)
-                               printf("                       no data available\n");
+                               printf("                        no data available\n");
                        printf("------------------------+-------------+----------------------------------------\n");
                        if (used!=0) {
-                               d=localtime(&current);
+                               /* 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;
                                }
-                               printf(" estimated  %s", getvalue(e_rx, 0, -7));
-                               printf("  | %s", getvalue(e_tx, 0, -7));
-                               printf("  | %s", getvalue(e_rx+e_tx, 0, -7));
+                               printf(" estimated  %s", getvalue(e_rx, 0, 7, 2));
+                               printf(" | %s", getvalue(e_tx, 0, 7, 2));
+                               printf(" | %s", getvalue(e_rx+e_tx, 0, 7, 2));
                                printf("\n");
                        }
                        
@@ -211,9 +233,9 @@ void showdb(int qmode)
                                if (data.month[i].used) {
                                        d=localtime(&data.month[i].month);
                                        strftime(datebuff, 16, cfg.mformat, d);
-                                       printf(" %8s   %s", datebuff, getvalue(data.month[i].rx, data.month[i].rxk, 8));
-                                       printf("  | %s", getvalue(data.month[i].tx, data.month[i].txk, 8));
-                                       printf("  | %s", getvalue(data.month[i].rx+data.month[i].tx, data.month[i].rxk+data.month[i].txk, 8));
+                                       printf(" %8s   %s", datebuff, getvalue(data.month[i].rx, data.month[i].rxk, 8, 1));
+                                       printf(" | %s", getvalue(data.month[i].tx, data.month[i].txk, 8, 1));
+                                       printf(" | %s", getvalue(data.month[i].rx+data.month[i].tx, data.month[i].rxk+data.month[i].txk, 8, 1));
                                        showbar(data.month[i].rx, data.month[i].rxk, data.month[i].tx, data.month[i].txk, max, 22);
                                        printf("\n");
                                        used++;
@@ -223,16 +245,17 @@ void showdb(int qmode)
                                printf("                        no data available\n");
                        printf("-------------------------+--------------+--------------------------------------\n");                    
                        if (used!=0) {
-                               d=localtime(&current);
+                               /* use database update time for estimates */
+                               d=localtime(&data.lastupdated);
                                if ( data.month[0].rx==0 || data.month[0].tx==0 || ((d->tm_mday-1)*24+d->tm_hour)==0 ) {
                                        e_rx=e_tx=0;
                                } else {
                                        e_rx=((data.month[0].rx)/(float)((d->tm_mday-1)*24+d->tm_hour))*(dmonth[d->tm_mon]*24);
                                        e_tx=((data.month[0].tx)/(float)((d->tm_mday-1)*24+d->tm_hour))*(dmonth[d->tm_mon]*24);
                                }
-                               printf(" estimated  %s", getvalue(e_rx, 0, -8));
-                               printf("  | %s", getvalue(e_tx, 0, -8));
-                               printf("  | %s", getvalue(e_rx+e_tx, 0, -8));
+                               printf(" estimated  %s", getvalue(e_rx, 0, 8, 2));
+                               printf(" | %s", getvalue(e_tx, 0, 8, 2));
+                               printf(" | %s", getvalue(e_rx+e_tx, 0, 8, 2));
                                printf("\n");
                        }
                
@@ -252,7 +275,7 @@ void showdb(int qmode)
                                        printf(" %s (%s) [disabled]  /  top 10\n\n", data.nick, data.interface);
                        }
                        
-                       printf("   #       day         rx      |     tx      |  total\n");
+                       printf("   #       day          rx     |      tx     |  total\n");
                        printf("-------------------------------+-------------+---------------------------------\n");
 
                        /* search maximum */
@@ -281,16 +304,16 @@ void showdb(int qmode)
                                if (data.top10[i].used) {
                                        d=localtime(&data.top10[i].date);
                                        strftime(datebuff, 16, cfg.tformat, d);
-                                       printf("  %2d  %10s   %s", i+1, datebuff, getvalue(data.top10[i].rx, data.top10[i].rxk, 7));
-                                       printf("  | %s", getvalue(data.top10[i].tx, data.top10[i].txk, 7));
-                                       printf("  | %s", getvalue(data.top10[i].rx+data.top10[i].tx, data.top10[i].rxk+data.top10[i].txk, 7));
+                                       printf("  %2d  %10s   %s", i+1, datebuff, getvalue(data.top10[i].rx, data.top10[i].rxk, 7, 1));
+                                       printf(" | %s", getvalue(data.top10[i].tx, data.top10[i].txk, 7, 1));
+                                       printf(" | %s", getvalue(data.top10[i].rx+data.top10[i].tx, data.top10[i].rxk+data.top10[i].txk, 7, 1));
                                        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");
+                               printf("                             no data available\n");
                        printf("-------------------------------+-------------+---------------------------------\n");
                
                /* dumpdb */
@@ -303,27 +326,27 @@ void showdb(int qmode)
                        printf("created;%d\n", (int)data.created);
                        printf("updated;%d\n", (int)data.lastupdated);
                        
-                       printf("totalrx;%Lu\n", data.totalrx);
-                       printf("totaltx;%Lu\n", data.totaltx);
-                       printf("currx;%Lu\n", data.currx);
-                       printf("curtx;%Lu\n", data.curtx);
+                       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;%Lu\n", data.btime);
+                       printf("btime;%"PRIu64"\n", data.btime);
                        
                        for (i=0;i<=29;i++) {
-                               printf("d;%d;%d;%Lu;%Lu;%d;%d;%d\n",i, (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);
+                               printf("d;%d;%d;%"PRIu64";%"PRIu64";%d;%d;%d\n",i, (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;%d;%Lu;%Lu;%d;%d;%d\n",i, (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);
+                               printf("m;%d;%d;%"PRIu64";%"PRIu64";%d;%d;%d\n",i, (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;%d;%Lu;%Lu;%d;%d;%d\n",i,(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);
+                               printf("t;%d;%d;%"PRIu64";%"PRIu64";%d;%d;%d\n",i,(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;%d;%Lu;%Lu\n",i,(int)data.hour[i].date, data.hour[i].rx, data.hour[i].tx);
+                               printf("h;%d;%d;%"PRIu64";%"PRIu64"\n",i,(int)data.hour[i].date, data.hour[i].rx, data.hour[i].tx);
                        }                       
                
                /* multiple dbs in one print */
@@ -340,8 +363,23 @@ void showdb(int qmode)
                                else
                                        printf(" %s (%s) [disabled]:\n", data.nick, data.interface);
                        }
-                       /* time needed for estimates */
+
+                       /* get formated date for today */
                        d=localtime(&current);
+                       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 ( data.day[0].rx==0 || data.day[0].tx==0 || (d->tm_hour*60+d->tm_min)==0 ) {
                                e_rx=e_tx=0;
                        } else {
@@ -363,15 +401,15 @@ void showdb(int qmode)
                        }
                        
                        if (data.day[1].date!=0) {
-                               printf("     %9s   %s",daytemp, getvalue(data.day[1].rx, data.day[1].rxk, 7));
-                               printf("  / %s", getvalue(data.day[1].tx, data.day[1].txk, 7));
-                               printf("  / %s", getvalue(data.day[1].rx+data.day[1].tx, data.day[1].rxk+data.day[1].txk, 7));
+                               printf("     %9s   %s",daytemp, getvalue(data.day[1].rx, data.day[1].rxk, 7, 1));
+                               printf("  / %s", getvalue(data.day[1].tx, data.day[1].txk, 7, 1));
+                               printf("  / %s", getvalue(data.day[1].rx+data.day[1].tx, data.day[1].rxk+data.day[1].txk, 7, 1));
                                printf("\n");
                        }
-                       printf("         today   %s", getvalue(data.day[0].rx, data.day[0].rxk, 7));
-                       printf("  / %s", getvalue(data.day[0].tx, data.day[0].txk, 7));
-                       printf("  / %s", getvalue(data.day[0].rx+data.day[0].tx, data.day[0].rxk+data.day[0].txk, 7));
-                       printf("  / %s", getvalue(e_rx+e_tx, 0, -7));
+                       printf("     %9s   %s", daytemp2, getvalue(data.day[0].rx, data.day[0].rxk, 7, 1));
+                       printf("  / %s", getvalue(data.day[0].tx, data.day[0].txk, 7, 1));
+                       printf("  / %s", getvalue(data.day[0].rx+data.day[0].tx, data.day[0].rxk+data.day[0].txk, 7, 1));
+                       printf("  / %s", getvalue(e_rx+e_tx, 0, 7, 2));
                        printf("\n\n");
 
                /* last 7 */
@@ -380,18 +418,18 @@ void showdb(int qmode)
                        printf("\n");
                        if (strcmp(data.interface, data.nick)==0) {
                                if (data.active)
-                                       printf("\t%s  /  weekly\n\n", data.interface);
+                                       printf("        %s  /  weekly\n\n", data.interface);
                                else
-                                       printf("\t%s [disabled]  /  weekly\n\n", data.interface);
+                                       printf("        %s [disabled]  /  weekly\n\n", data.interface);
                        } else {
                                if (data.active)
-                                       printf("\t%s (%s)  /  weekly\n\n", data.nick, data.interface);
+                                       printf("        %s (%s)  /  weekly\n\n", data.nick, data.interface);
                                else
-                                       printf("\t%s (%s) [disabled]  /  weekly\n\n", data.nick, data.interface);
+                                       printf("        %s (%s) [disabled]  /  weekly\n\n", data.nick, data.interface);
                        }       
 
-                       printf("\t                    rx      |       tx      |    total\n");
-                       printf("\t----------------------------+---------------+--------------\n");
+                       printf("                            rx      |     tx      |  total\n");
+                       printf("         ---------------------------+-------------+------------\n");
                        
                        /* get current week number */
                        d=localtime(&current);
@@ -411,9 +449,9 @@ void showdb(int qmode)
                        }
 
                        if (used!=0) {
-                               printf("\t  last 7 days %s", getvalue(t_rx, t_rxk, 9));
-                               printf("  | %s", getvalue(t_tx, t_txk, 9));
-                               printf("  | %s", getvalue(t_rx+t_tx, t_rxk+t_txk, 9));
+                               printf("          last 7 days   %s", getvalue(t_rx, t_rxk, 7, 1));
+                               printf(" | %s", getvalue(t_tx, t_txk, 7, 1));
+                               printf(" | %s", getvalue(t_rx+t_tx, t_rxk+t_txk, 7, -1));
                                printf("\n");
                                temp++;
                        }
@@ -434,9 +472,9 @@ void showdb(int qmode)
                        }
                        
                        if (used!=0) {
-                               printf("\t    last week %s", getvalue(t_rx, t_rxk, 9));
-                               printf("  | %s", getvalue(t_tx, t_txk, 9));
-                               printf("  | %s", getvalue(t_rx+t_tx, t_rxk+t_txk, 9));
+                               printf("            last week   %s", getvalue(t_rx, t_rxk, 7, 1));
+                               printf(" | %s", getvalue(t_tx, t_txk, 7, 1));
+                               printf(" | %s", getvalue(t_rx+t_tx, t_rxk+t_txk, 7, -1));
                                printf("\n");
                                temp++;
                        }
@@ -458,7 +496,8 @@ void showdb(int qmode)
 
                        /* get estimate for current week */
                        if (used!=0) {
-                               d=localtime(&current);
+                               /* 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;
@@ -466,21 +505,21 @@ void showdb(int qmode)
                                        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);
                                }
-                               printf("\t current week %s", getvalue(t_rx, t_rxk, 9));
-                               printf("  | %s", getvalue(t_tx, t_txk, 9));
-                               printf("  | %s", getvalue(t_rx+t_tx, t_rxk+t_txk, 9));
+                               printf("         current week   %s", getvalue(t_rx, t_rxk, 7, 1));
+                               printf(" | %s", getvalue(t_tx, t_txk, 7, 1));
+                               printf(" | %s", getvalue(t_rx+t_tx, t_rxk+t_txk, 7, -1));
                                printf("\n");
                                temp++;
                        }
                        
                        if (temp==0)
-                               printf("\t                         no data available\n");
+                               printf("                                  no data available\n");
                        
-                       printf("\t----------------------------+---------------+--------------\n");
+                       printf("         ---------------------------+-------------+------------\n");
                        if (used!=0) {
-                               printf("\t    estimated %s", getvalue(e_rx, 0, -9));
-                               printf("  | %s", getvalue(e_tx, 0, -9));
-                               printf("  | %s", getvalue(e_rx+e_tx, 0, -9));
+                               printf("            estimated   %s", getvalue(e_rx, 0, 7, 2));
+                               printf(" | %s", getvalue(e_tx, 0, 7, 2));
+                               printf(" | %s", getvalue(e_rx+e_tx, 0, 7, -2));
                                printf("\n");
                        }
 
@@ -536,7 +575,11 @@ void showhours(void)
        
        /* structure */
        sprintf(matrix[11]," -+--------------------------------------------------------------------------->");
-       sprintf(matrix[14]," h   rx (kB)    tx (kB)      h   rx (kB)    tx (kB)      h   rx (kB)    tx (kB)");
+       if (cfg.unit==0) {
+               sprintf(matrix[14]," h  rx (KiB)   tx (KiB)      h  rx (KiB)   tx (KiB)      h  rx (KiB)   tx (KiB)");
+       } else {
+               sprintf(matrix[14]," h   rx (KB)    tx (KB)      h   rx (KB)    tx (KB)      h   rx (KB)    tx (KB)");
+       }
 
        for (i=10;i>1;i--)
                matrix[i][2]='|';
@@ -560,7 +603,7 @@ void showhours(void)
        /* time to the corner */
        sprintf(matrix[0]+74,"%02d:%02d", hour, min);
        
-       /* numbers under x-axel and graphics :) */
+       /* numbers under x-axis and graphics :) */
        k=5;
        for (i=23;i>=0;i--) {
                s=tmax-i;
@@ -583,7 +626,11 @@ void showhours(void)
        /* hours and traffic */
        for (i=0;i<=7;i++) {
                s=tmax+i+1;
-               sprintf(matrix[15+i],"%02d %'10Lu %'10Lu    %02d %'10Lu %'10Lu    %02d %'10Lu %'10Lu",s%24, data.hour[s%24].rx, data.hour[s%24].tx, (s+8)%24, data.hour[(s+8)%24].rx, data.hour[(s+8)%24].tx, (s+16)%24, data.hour[(s+16)%24].rx, data.hour[(s+16)%24].tx);
+#if !defined(__OpenBSD__)
+               sprintf(matrix[15+i],"%02d %'10"PRIu64" %'10"PRIu64"    %02d %'10"PRIu64" %'10"PRIu64"    %02d %'10"PRIu64" %'10"PRIu64"",s%24, data.hour[s%24].rx, data.hour[s%24].tx, (s+8)%24, data.hour[(s+8)%24].rx, data.hour[(s+8)%24].tx, (s+16)%24, data.hour[(s+16)%24].rx, data.hour[(s+16)%24].tx);
+#else
+               sprintf(matrix[15+i],"%02d %10"PRIu64" %10"PRIu64"    %02d %10"PRIu64" %10"PRIu64"    %02d %10"PRIu64" %10"PRIu64"",s%24, data.hour[s%24].rx, data.hour[s%24].tx, (s+8)%24, data.hour[(s+8)%24].rx, data.hour[(s+8)%24].tx, (s+16)%24, data.hour[(s+16)%24].rx, data.hour[(s+16)%24].tx);
+#endif
        }
        
        /* clean \0 */
@@ -605,139 +652,51 @@ void showhours(void)
 
 }
 
-
-void cleanhours(void)
+void showbar(uint64_t rx, int rxk, uint64_t tx, int txk, uint64_t max, int len)
 {
-       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("Hour %d (%d) cleaned.\n",i, (int)data.hour[i].date);
-                       }
-               }
-       }
+       int i, l;
        
-       /* clean current if it's not from today to avoid incrementing old data */
-       d=localtime(&current);
-       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("Current hour %d (%d) cleaned.\n",hour, (int)data.hour[i].date);
-                       }
-                       data.hour[hour].date=current;
+       if (rxk>=1024) {
+               rx+=rxk/1024;
+               rxk-=(rxk/1024)*1024;
        }
-}
 
-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;
+       if (txk>=1024) {
+               tx+=txk/1024;
+               txk-=(txk/1024)*1024;
        }
+
+       rx=(rx*1024)+rxk;
+       tx=(tx*1024)+txk;
        
-       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("Days rotated. Current date: %d.%d.%d\n",d->tm_mday, d->tm_mon+1, d->tm_year+1900);
+       if ((rx+tx)!=max) {
+               len=((rx+tx)/(float)max)*len;
        }
 
-       /* 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("Top10 updated.\n");
 
-}
+       if (len!=0) {
+               printf("  ");
 
-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("Months rotated. Current month: \"%d\".\n",d->tm_mon+1);
-       }
-}
-
-void synccounters(char iface[32], char dirname[512])
-{
-       readdb(iface, dirname);
-       getifinfo(iface);
+               if (tx>rx) {
+                       l=rintf((rx/(float)(rx+tx)*len));
 
-       /* set counters to current without counting traffic */
-       data.currx = ifinfo.rx;
-       data.curtx = ifinfo.tx;
+                       for (i=0;i<l;i++) {
+                               printf("%c", cfg.rxchar[0]);
+                       }
+                       for (i=0;i<(len-l);i++) {
+                               printf("%c", cfg.txchar[0]);
+                       }
+               } else {
+                       l=rintf((tx/(float)(rx+tx)*len));
+                       
+                       for (i=0;i<(len-l);i++) {
+                               printf("%c", cfg.rxchar[0]);
+                       }
+                       for (i=0;i<l;i++) {
+                               printf("%c", cfg.txchar[0]);
+                       }               
+               }
+               
+       }
        
-       writedb(iface, dirname, 0);
 }
diff --git a/src/dbshow.h b/src/dbshow.h
new file mode 100644 (file)
index 0000000..d0ab857
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef DBSHOW_H
+#define DBSHOW_H
+
+void showdb(int qmode);
+void showhours(void);
+void showbar(uint64_t rx, int rxk, uint64_t tx, int txk, uint64_t max, int len);
+
+#endif
diff --git a/src/dbxml.c b/src/dbxml.c
new file mode 100644 (file)
index 0000000..f4dbe39
--- /dev/null
@@ -0,0 +1,94 @@
+#include "common.h"
+#include "dbxml.h"
+
+void showxml(void)
+{
+       int i;
+
+       printf(" <interface id=\"%s\">\n", data.interface);
+
+       printf("  <id>%s</id>\n", data.interface);
+       printf("  <nick>%s</nick>\n", data.nick);
+
+       printf("  <created>");
+       xmldate(&data.created, 1);
+       printf("</created>\n");
+       printf("  <updated>");
+       xmldate(&data.lastupdated, 2);
+       printf("</updated>\n");
+
+       printf("  <traffic>\n");
+       printf("   <total><rx>%"PRIu64"</rx><tx>%"PRIu64"</tx></total>\n", (data.totalrx*1024)+data.totalrxk, (data.totaltx*1024)+data.totaltxk);
+
+       printf("   <days>\n");
+       for (i=0;i<=29;i++) {
+               if (data.day[i].used) {
+                       printf("    <day id=\"%d\">", i);
+                       xmldate(&data.day[i].date, 1);
+                       printf("<rx>%"PRIu64"</rx><tx>%"PRIu64"</tx></day>\n", (data.day[i].rx*1024)+data.day[i].rxk, (data.day[i].tx*1024)+data.day[i].txk);
+               }
+       }       
+       printf("   </days>\n");
+
+       printf("   <months>\n");
+       for (i=0;i<=11;i++) {
+               if (data.month[i].used) {
+                       printf("    <month id=\"%d\">", i);
+                       xmldate(&data.month[i].month, 3);
+                       printf("<rx>%"PRIu64"</rx><tx>%"PRIu64"</tx></month>\n", (data.month[i].rx*1024)+data.month[i].rxk, (data.month[i].tx*1024)+data.month[i].txk);
+               }
+       }       
+       printf("   </months>\n");
+
+       printf("   <tops>\n");
+       for (i=0;i<=9;i++) {
+               if (data.top10[i].used) {
+                       printf("    <top id=\"%d\">", i);
+                       xmldate(&data.top10[i].date, 2);
+                       printf("<rx>%"PRIu64"</rx><tx>%"PRIu64"</tx></top>\n", (data.top10[i].rx*1024)+data.top10[i].rxk, (data.top10[i].tx*1024)+data.top10[i].txk);
+               }
+       }       
+       printf("   </tops>\n");
+
+       printf("   <hours>\n");
+       for (i=0;i<=23;i++) {
+               if (data.hour[i].date!=0) {
+                       printf("    <hour id=\"%d\">", i);
+                       xmldate(&data.hour[i].date, 1);
+                       printf("<rx>%"PRIu64"</rx><tx>%"PRIu64"</tx></hour>\n", data.hour[i].rx, data.hour[i].tx);
+               }
+       }       
+       printf("   </hours>\n");
+       
+       printf("  </traffic>\n");
+       printf(" </interface>\n");
+
+}
+
+void xmldate(time_t *date, int type)
+{
+       struct tm *d;
+       char *buffer;
+       char *type1 = "<date><year>%Y</year><month>%m</month><day>%d</day></date>";
+       char *type2 = "<date><year>%Y</year><month>%m</month><day>%d</day></date><time><hour>%H</hour><minute>%M</minute></time>";
+       char *type3 = "<date><year>%Y</year><month>%m</month></date>";
+       
+       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);
+       }
+}
diff --git a/src/dbxml.h b/src/dbxml.h
new file mode 100644 (file)
index 0000000..52a375f
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef DBXML_H
+#define DBXML_H
+
+void showxml(void);
+void xmldate(time_t *date, int type);
+
+#endif
index 52f0d2bb036313ed8a106bb2bea7e223816bb565..047d38cf93695a89e6d504b52b4991e6278d127b 100644 (file)
@@ -1,10 +1,10 @@
-#include "vnstat.h"
-#include "ifinfo.h"
+#include "common.h"
 #include "misc.h"
-#include "db.h"
+#include "dbaccess.h"
 #include "cfg.h"
+#include "ifinfo.h"
 
-void getifinfo(char iface[32])
+int getifinfo(char iface[32])
 {
        char inface[32];
 
@@ -16,37 +16,124 @@ void getifinfo(char iface[32])
                strncpy(inface, iface, 32);
        }
 
+#if defined(__linux__)
        /* try getting interface info from /proc */
        if (readproc(inface)!=1) {
                if (debug)
-                       printf("Failed to use /proc/net/dev as source.\n");
+                       printf("Failed to use %s as source.\n", PROCNETDEV);
 
                /* try getting interface info from /sys */
                if (readsysclassnet(inface)!=1) {
-                       printf("Error:\nUnable to get interface statistics.\n");
-                       exit(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__)
+       if (readifaddrs(inface)!=1) {
+               snprintf(errorstring, 512, "Unable to get interface \"%s\" statistics.", inface);
+               printe(PT_Error);
+               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__)
+       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)+1) + (strlen(interface)+1)) * sizeof(char)) );
+                               strcat(*ifacelist, 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)+1) + (strlen(interface)+1)) * sizeof(char)) );
+                                       strncat(*ifacelist, di->d_name, 32);
+                                       strcat(*ifacelist, " ");
+                               }
+                       }
+
+                       closedir(dp);
+                       return 1;
+
+               }       
+       }
+
+#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
+       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)+1) + (strlen(ifa->ifa_name)+1)) * sizeof(char)) );
+                               strncat(*ifacelist, ifa->ifa_name, 32);
+                               strcat(*ifacelist, " ");
+                       }
                }
+               
+               freeifaddrs(ifap);
+               return 1;
        }
+
+#endif
+
+       return 0;
 }
 
 int readproc(char iface[32])
 {
        FILE *fp;
-       char temp[4][64], procline[512], *proclineptr;
+       char temp[4][64], procline[512], *proclineptr, ifaceid[33];
        int check;
        
-       if ((fp=fopen("/proc/net/dev", "r"))==NULL) {
+       if ((fp=fopen(PROCNETDEV, "r"))==NULL) {
                if (debug)
-                       printf("Error: Unable to read /proc/net/dev.\n");
+                       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, "%s", temp[0]);
-               if (strncmp(iface, temp[0], strlen(iface))==0) {
-                       if (debug)
-                               printf("\n%s\n", procline);
+               sscanf(procline, "%512s", temp[0]);
+               if (strncmp(ifaceid, temp[0], strlen(ifaceid))==0) {
+                       /* if (debug)
+                               printf("\n%s\n", procline); */
                        check = 1;
                        break;
                }
@@ -63,12 +150,16 @@ int readproc(char iface[32])
 
                /* get rx and tx from procline */
                proclineptr = strchr(procline, ':');
-               sscanf(proclineptr+1, "%s %s %*s %*s %*s %*s %*s %*s %s %s", temp[0], temp[1], temp[2], temp[3]);
+               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);
-               ifinfo.rxp = strtoull(temp[1], (char **)NULL, 0);
-               ifinfo.txp = strtoull(temp[3], (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;
        }
@@ -118,35 +209,39 @@ int readsysclassnet(char iface[32])
        }
        fclose(fp);
 
-       /* 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 {
+       /* 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);
+               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 {
+               /* 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);
        }
-       fclose(fp);
 
        ifinfo.filled = 1;
 
@@ -155,13 +250,14 @@ int readsysclassnet(char iface[32])
 
 void parseifinfo(int newdb)
 {
-       uint64_t rxchange=0, txchange=0, btime;   /* rxchange = rx change in MB */
+       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();
@@ -180,16 +276,20 @@ void parseifinfo(int newdb)
 
                /* process rx & tx */
                if (newdb!=1) {
-                       rxchange = countercalc(data.currx, ifinfo.rx)/1024/1024;
-                       krxchange = countercalc(data.currx, ifinfo.rx)/1024;
-                       rxkchange = (countercalc(data.currx, ifinfo.rx)/1024)%1024;
-
-                       txchange = countercalc(data.curtx, ifinfo.tx)/1024/1024;
-                       ktxchange = countercalc(data.curtx, ifinfo.tx)/1024;
-                       txkchange = (countercalc(data.curtx, ifinfo.tx)/1024)%1024;
+                       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 maximum bandwidth */
+               /* get bandwidth limit for current interface */
                maxbw = ibwget(data.interface);
 
                if (maxbw > 0) {
@@ -199,27 +299,28 @@ void parseifinfo(int newdb)
                        maxtransfer = ceil((maxbw/(float)8)*interval*(float)1.1);
 
                        if (debug)
-                               printf("interval: %Lu  maxbw: %d  maxrate: %Lu  rxc: %Lu  txc: %Lu\n", (uint64_t)interval, maxbw, maxtransfer, rxchange, txchange); 
+                               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) ) {
-                               rxchange=krxchange=rxkchange=txchange=ktxchange=txkchange=0;
-                               if (debug)
-                                       printf("Traffic is greater than set maximum, counters synced.\n");
+                               rxchange = krxchange = rxkchange = txchange = ktxchange = txkchange = 0;
+                               ifinfo.rxp = ifinfo.txp = 0;
+                               snprintf(errorstring, 512, "Traffic rate for \"%s\" higher than set maximum %d Mbit, syncing.", data.interface, maxbw);
+                               printe(PT_Info);
                        }
                }
 
        } else {
                if (debug)
-                       printf("Too much time passed since previous update, syncing. (%Lu < %d)\n", (uint64_t)interval, 60*MAXUPDATEINTERVAL);
+                       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;
-       data.curtx = ifinfo.tx;
+       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);
 
@@ -236,15 +337,16 @@ void parseifinfo(int newdb)
        year=d->tm_year;
        hour=d->tm_hour;
        min=d->tm_min;
-       
-       /* shift traffic to previous hour when update happens at X:00 */
-       if (min==0) {
-               shift=hour;
+       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+=24;     /* hour can't be -1 :) */
-       } else {
-               shift=hour;
+               if (hour<0) {
+                       hour=23;
+               }
        }
        
        /* clean and update hourly */
@@ -255,249 +357,15 @@ void parseifinfo(int newdb)
        
        /* 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))
+       if ((d->tm_mday!=day) || (d->tm_mon!=month) || (d->tm_year!=year)) {
                rotatedays();
+       }
 
        /* rotate months in database if needed */
        d=localtime(&data.month[0].month);
-       if ((d->tm_mon!=month) && (day>=MONTHROTATE))
+       if ((d->tm_mon!=month) && (day>=cfg.monthrotate)) {
                rotatemonths();
-
-}
-
-void trafficmeter(char iface[32], 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:\nTime for sampling too short.\n");
-               exit(1);
-       }
-
-       /* read interface info and get values to the first list */
-       getifinfo(iface);
-       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 :) */
-       sprintf(buffer,"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;i<len;i++) {
-               printf("\b \b");
        }
-       
-       /* read those values again... */
-       getifinfo(iface);
-
-       /* calculate traffic and packets seen between updates */
-       rx = countercalc(firstinfo.rx, ifinfo.rx);
-       tx = countercalc(firstinfo.tx, ifinfo.tx);
-       rxp = countercalc(firstinfo.rxp, ifinfo.rxp);
-       txp = countercalc(firstinfo.txp, ifinfo.txp);
-
-       /* show the difference in a readable format */
-       printf("%Lu packets sampled in %d seconds\n", rxp+txp, sampletime);
-       printf("Traffic average for %s\n", iface);
-       printf("\n      rx     %10.2f kB/s          %5Lu packets/s\n", rx/(float)sampletime/(float)1024, (uint64_t)(rxp/sampletime));
-       printf("      tx     %10.2f kB/s          %5Lu packets/s\n\n", tx/(float)sampletime/(float)1024, (uint64_t)(txp/sampletime));
-
-}
-
-void livetrafficmeter(char iface[32])
-{
-       /* received bytes packets errs drop fifo frame compressed multicast */
-       /* transmitted bytes packets errs drop fifo colls carrier compressed */
-       uint64_t rx, tx, rxp, txp, rxpc, txpc, timespent;
-       uint64_t rxtotal, txtotal, rxptotal, txptotal;
-       uint64_t rxpmin, txpmin, rxpmax, txpmax;
-       float rxmin, txmin, rxmax, txmax, rxc, txc;
-       int i, len;
-       char buffer[256];
-       IFINFO previnfo;
-
-       printf("Monitoring %s...    (press CTRL-C to stop)\n\n", iface);
-       printf("   getting traffic...");
-       len=21;
-       fflush(stdout);
-
-       /* enable signal trap */
-       if (signal(SIGINT, intr) == SIG_ERR) {
-               perror("signal");
-               exit(1);
-       }
-
-       /* set some defaults */
-       rxtotal=txtotal=rxptotal=txptotal=rxpmax=txpmax=0;
-       rxpmin=txpmin=-1;
-       rxmax=txmax=0.0;
-       rxmin=txmin=-1.0;
-       
-       timespent = (uint64_t)time(NULL);
-
-       /* read /proc/net/dev and get values to the first list */
-       getifinfo(iface);
-
-       /* loop until user gets bored */
-       while (intsignal==0) {
-
-               /* wait 2 seconds for more traffic */
-               sleep(LIVETIME);
-
-               /* break loop without calculations because sleep was probably interrupted */
-               if (intsignal) {
-                       break;
-               }
-
-               /* use values from previous loop if this isn't the first time
-               if (len!=21) {
-                       for (i=0;i<16;i++) {
-                               p1[i]=p2[i];
-                       }
-               } */
-
-               previnfo.rx = ifinfo.rx;
-               previnfo.tx = ifinfo.tx;
-               previnfo.rxp = ifinfo.rxp;
-               previnfo.txp = ifinfo.txp;
-
-               /* read those values again... */
-               getifinfo(iface);
-
-               /* calculate traffic and packets seen between updates */
-               rx = countercalc(previnfo.rx, ifinfo.rx);
-               tx = countercalc(previnfo.tx, ifinfo.tx);
-               rxp = countercalc(previnfo.rxp, ifinfo.rxp);
-               txp = countercalc(previnfo.txp, ifinfo.txp);
-
-               /* update totals */
-               rxtotal += rx;
-               txtotal += tx;
-               rxptotal += rxp;
-               txptotal += txp;
-
-               rxc = rx/(float)LIVETIME/(float)1024;
-               txc = tx/(float)LIVETIME/(float)1024;
-               rxpc = rxp/LIVETIME;
-               txpc = txp/LIVETIME;            
-
-               /* update min & max */
-               if ((rxmin==-1.0) || (rxmin>rxc)) {
-                       rxmin = rxc;
-               }
-               if ((txmin==-1.0) || (txmin>txc)) {
-                       txmin = txc;
-               }
-               if (rxmax<rxc) {
-                       rxmax = rxc;
-               }
-               if (txmax<txc) {
-                       txmax = txc;
-               }
-               
-               if ((rxpmin==-1) || (rxpmin>rxpc)) {
-                       rxpmin = rxpc;
-               }
-               if ((txpmin==-1) || (txpmin>txpc)) {
-                       txpmin = txpc;
-               }
-               if (rxpmax<rxpc) {
-                       rxpmax = rxpc;
-               }
-               if (txpmax<txpc) {
-                       txpmax = txpc;
-               }
-
-               /* show the difference in a readable format */
-               snprintf(buffer, 256, "   rx: %10.2f kB/s %5Lu p/s            tx: %10.2f kB/s %5Lu p/s", rxc, (uint64_t)rxpc, txc, (uint64_t)txpc);
-               
-               if (len>1) {
-                       if (debug) {
-                               printf("\nlinelen: %d\n", len);
-                       } else {
-                               for (i=0;i<len;i++) {
-                                       printf("\b \b");
-                               }
-                               fflush(stdout);
-                       }
-               }
-               printf("%s", buffer);
-               fflush(stdout);
-               len=strlen(buffer);
-       
-       }
-
-       timespent = (uint64_t)time(NULL) - timespent;
-
-       printf("\n\n");
-
-       /* print some statistics if enough time did pass */
-       if (timespent>10) {
-
-               printf("\n %s  /  traffic statistics\n\n", iface);
-
-               printf("                             rx       |       tx\n");
-               printf("--------------------------------------+----------------------------------------\n");
-               printf("  bytes                %s", getvalue(0, rxtotal/1024, 10));
-               printf("  | %s", getvalue(0, txtotal/1024, 10));
-               printf("\n");
-               printf("--------------------------------------+----------------------------------------\n");
-               printf("          max          ");
-               showspeed(rxmax, 8);
-               printf("  | ");
-               showspeed(txmax, 8);
-               printf("\n");
-               printf("      average          ");
-               showspeed(rxtotal/(float)timespent/(float)1024, 8);
-               printf("  | ");
-               showspeed(txtotal/(float)timespent/(float)1024, 8);
-               printf("\n");
-               printf("          min          ");
-               showspeed(rxmin, 8);
-               printf("  | ");
-               showspeed(txmin, 8);
-               printf("\n");
-               printf("--------------------------------------+----------------------------------------\n");
-               printf("  packets               %12Lu  |  %12Lu\n", rxptotal, txptotal);
-               printf("--------------------------------------+----------------------------------------\n");
-               printf("          max          %9Lu p/s  | %9Lu p/s\n", rxpmax, txpmax);
-               printf("      average          %9Lu p/s  | %9Lu p/s\n", rxptotal/timespent, txptotal/timespent);
-               printf("          min          %9Lu p/s  | %9Lu p/s\n", rxpmin, txpmin);
-               printf("--------------------------------------+----------------------------------------\n");
-               
-               if (timespent<=60) {
-                       printf("  time             %9Lu seconds\n", timespent);
-               } else {
-                       printf("  time               %7.2f minutes\n", timespent/(float)60);
-               }
-
-               printf("\n");
-       }
-       
 }
 
 uint64_t countercalc(uint64_t a, uint64_t b)
@@ -505,7 +373,7 @@ uint64_t countercalc(uint64_t a, uint64_t b)
        /* no flip */
        if (b>=a) {
                if (debug)
-                       printf("cc: %Lu - %Lu = %Lu\n", b, a, b-a);
+                       printf("cc: %"PRIu64" - %"PRIu64" = %"PRIu64"\n", b, a, b-a);
                return b-a;
 
        /* flip exists */
@@ -513,23 +381,52 @@ uint64_t countercalc(uint64_t a, uint64_t b)
                /* original counter is 64bit */
                if (a>FP32) {
                        if (debug)
-                               printf("cc64: uint64 - %Lu + %Lu = %Lu\n", a, b, FP64-a+b);
+                               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 - %Lu + %Lu = %Lu\n", a, b, FP32-a+b);
+                               printf("cc32: uint32 - %"PRIu64" + %"PRIu64" = %"PRIu64"\n", a, b, (uint64_t)FP32-a+b);
                        return FP32-a+b;
                }
        }
 }
 
-void showspeed(float xfer, int len)
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
+int readifaddrs(char iface[32])
 {
-       if (xfer>=1000) {
-               printf("%'*.2f MB/s", len, xfer/(float)1024);
+       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 {
-               printf("%'*.2f kB/s", len, xfer);
+               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
index 11928a482ccbecb030dd5fd99d0fb73a14482213..db9aa175ea6c6c53100151db31f6f3ea3cc2fcae 100644 (file)
@@ -1,8 +1,14 @@
-void getifinfo(char iface[32]);
+#ifndef IFINFO_H
+#define IFINFO_H
+
+int getifinfo(char iface[32]);
+int getiflist(char **ifacelist);
 int readproc(char iface[32]);
 int readsysclassnet(char iface[32]);
 void parseifinfo(int newdb);
-void trafficmeter(char iface[32], int sampletime);
-void livetrafficmeter(char iface[32]);
 uint64_t countercalc(uint64_t a, uint64_t b);
-void showspeed(float xfer, int len);
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
+int readifaddrs(char iface[32]);
+#endif
+
+#endif
diff --git a/src/image.c b/src/image.c
new file mode 100644 (file)
index 0000000..d651941
--- /dev/null
@@ -0,0 +1,954 @@
+#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]);
+       hextorgb(cfg.cedge, rgb);
+       cedge = gdImageColorAllocate(im, rgb[0], rgb[1], rgb[2]);
+       hextorgb(cfg.cheader, rgb);
+       cheader = gdImageColorAllocate(im, rgb[0], rgb[1], rgb[2]);
+       hextorgb(cfg.cheadertitle, rgb);
+       cheadertitle = gdImageColorAllocate(im, rgb[0], rgb[1], rgb[2]);
+       hextorgb(cfg.cheaderdate, rgb);
+       cheaderdate = gdImageColorAllocate(im, rgb[0], rgb[1], rgb[2]);
+
+       /* lines */
+       hextorgb(cfg.cline, rgb);
+       cline = gdImageColorAllocate(im, rgb[0], rgb[1], rgb[2]);
+       if (cfg.clinel[0] == '-') {
+               modcolor(rgb, 50, 1);
+       } else {
+               hextorgb(cfg.clinel, rgb);
+       }
+       clinel = gdImageColorAllocate(im, rgb[0], rgb[1], rgb[2]);
+
+       /* background */
+       hextorgb(cfg.cbg, rgb); 
+       cbackground = gdImageColorAllocate(im, rgb[0], rgb[1], rgb[2]);
+       modcolor(rgb, -35, 0);
+       cvnstat = gdImageColorAllocate(im, rgb[0], rgb[1], rgb[2]);
+
+       /* rx */
+       hextorgb(cfg.crx, rgb);
+       crx = gdImageColorAllocate(im, rgb[0], rgb[1], rgb[2]);
+       if (cfg.crxd[0] == '-') {
+               modcolor(rgb, -50, 1);
+       } else {
+               hextorgb(cfg.crxd, rgb);
+       }
+       crxd = gdImageColorAllocate(im, rgb[0], rgb[1], rgb[2]);
+       
+       /* tx */
+       hextorgb(cfg.ctx, rgb);
+       ctx = gdImageColorAllocate(im, rgb[0], rgb[1], rgb[2]);
+       if (cfg.ctxd[0] == '-') {
+               modcolor(rgb, -50, 1);
+       } else {
+               hextorgb(cfg.ctxd, rgb);
+       }
+       ctxd = gdImageColorAllocate(im, rgb[0], rgb[1], rgb[2]);
+}
+
+void layoutinit(char *title, int width, int height, int showheader, int showedge)
+{
+       struct tm *d;
+       char datestring[32];
+
+       /* get time in given format */
+       d = localtime(&data.lastupdated);
+       strftime(datestring, 32, 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 drawhours(int x, int y, int rate)
+{
+       int i, tmax=0, s=0, step, prev=0, diff=0, chour;
+       uint64_t max=1, scaleunit=0;
+       char buffer[32];
+       time_t current;
+       struct tm *d;
+       
+       current = time(NULL);
+       chour = localtime(&current)->tm_hour;
+
+       /* 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 / (float)450; /* rx * 8 / 3600 */
+                               data.hour[i].tx = data.hour[i].tx / (float)450; /* tx * 8 / 3600 */                     
+                       } else {
+                               /* scale ongoing hour properly */
+                               d = localtime(&data.hour[i].date);
+                               if (chour != d->tm_hour) {
+                                       data.hour[i].rx = data.hour[i].rx / (float)450; /* rx * 8 / 3600 */
+                                       data.hour[i].tx = data.hour[i].tx / (float)450; /* tx * 8 / 3600 */                             
+                               } else {
+                                       diff = d->tm_min*60;
+                                       if (!diff) {
+                                               diff = 1;
+                                       }
+                                       data.hour[i].rx = data.hour[i].rx * 8 / (float)diff;
+                                       data.hour[i].tx = data.hour[i].tx * 8 / (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; 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;
+               }
+               sprintf(buffer, "%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 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) {
+               sprintf(buffer, "%s", data.interface);  
+       } else {
+               sprintf(buffer, "%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 */
+       sprintf(buffer, "   received: %s  (%.1f%%)", getvalue(data.totalrx, data.totalrxk, 9, -1), rxp);
+       gdImageString(im, gdFontGetLarge(), textx, texty, (unsigned char*)buffer, ctext);
+       sprintf(buffer, "transmitted: %s  (%.1f%%)", getvalue(data.totaltx, data.totaltxk, 9, -1), txp);
+       gdImageString(im, gdFontGetLarge(), textx, texty+15, (unsigned char*)buffer, ctext);
+       sprintf(buffer, "      total: %s", getvalue(data.totalrx+data.totaltx, data.totalrxk+data.totaltxk, 9, -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(&current);
+       strftime(datebuff, 16, cfg.dformat, d);
+
+       /* get formated date for current day in database */
+       d=localtime(&data.day[0].date);
+       strftime(daytemp2, 16, cfg.dformat, d);
+
+       /* change daytemp to today if formated days match */
+       if (strcmp(datebuff, daytemp2)==0) {
+               strncpy(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, 6, -1), 32);
+               strcat(buffer, "   ");
+               strncat(buffer, getvalue(data.day[1].tx, data.day[1].txk, 6, -1), 32);
+               strcat(buffer, "   ");
+               strncat(buffer, getvalue(data.day[1].rx+data.day[1].tx, data.day[1].rxk+data.day[1].txk, 6, -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, 6, -1), 32);
+       strcat(buffer, "   ");
+       strncat(buffer, getvalue(data.day[0].tx, data.day[0].txk, 6, -1), 32);
+       strcat(buffer, "   ");
+       strncat(buffer, getvalue(data.day[0].rx+data.day[0].tx, data.day[0].rxk+data.day[0].txk, 6, -1), 32);
+       gdImageString(im, gdFontGetSmall(), textx, texty+32, (unsigned char*)buffer, ctext);
+
+       sprintf(buffer, "estimated   ");
+       strncat(buffer, getvalue(e_rx, 0, 6, -2), 32);
+       strcat(buffer, "   ");
+       strncat(buffer, getvalue(e_tx, 0, 6, -2), 32);
+       strcat(buffer, "   ");
+       strncat(buffer, getvalue(e_rx+e_tx, 0, 6, -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) {
+               sprintf(buffer, "%s / hourly", data.interface); 
+       } else {
+               sprintf(buffer, "%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) {
+               sprintf(buffer, "%s / daily", data.interface);  
+       } else {
+               sprintf(buffer, "%s (%s) / daily", data.nick, data.interface);
+       }
+       
+       layoutinit(buffer, width, height, showheader, showedge);
+       
+       if (lines) {
+               drawlegend(385, 40-headermod);
+       }
+
+       textx = 10;
+       texty = 40-headermod;
+       
+       gdImageString(im, gdFontGetSmall(), textx, texty, (unsigned char*)"    day         rx            tx         total", ctext);
+       gdImageLine(im, textx+2, texty+16, textx+302, texty+16, cline);
+       gdImageLine(im, textx+144, texty+2, textx+144, texty+40+(12*lines), cline);
+       gdImageLine(im, textx+228, texty+2, textx+228, 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);
+                       strftime(datebuff, 16, cfg.dformat, d);
+                       snprintf(buffer, 32, " %8s   ", datebuff);
+                       strncat(buffer, getvalue(data.day[i].rx, data.day[i].rxk, 6, -1), 32);
+                       strcat(buffer, "    ");
+                       strncat(buffer, getvalue(data.day[i].tx, data.day[i].txk, 6, -1), 32);
+                       strcat(buffer, "    ");
+                       strncat(buffer, getvalue(data.day[i].rx+data.day[i].tx, data.day[i].rxk+data.day[i].txk, 6, -1), 32);
+                       gdImageString(im, gdFontGetSmall(), textx, texty, (unsigned char*)buffer, ctext);
+                       drawbar(textx+310, texty+4, 165, 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;
+       }
+
+       gdImageLine(im, textx+2, texty+5, textx+302, 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;
+               }
+               sprintf(buffer, " estimated  ");
+               strncat(buffer, getvalue(e_rx, 0, 6, -2), 32);
+               strcat(buffer, "    ");
+               strncat(buffer, getvalue(e_tx, 0, 6, -2), 32);
+               strcat(buffer, "    ");
+               strncat(buffer, getvalue(e_rx+e_tx, 0, 6, -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;
+       int dmonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+       /* 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) {
+               sprintf(buffer, "%s / monthly", data.interface);        
+       } else {
+               sprintf(buffer, "%s (%s) / monthly", data.nick, data.interface);
+       }
+       
+       layoutinit(buffer, width, height, showheader, showedge);
+       
+       if (lines) {
+               drawlegend(385, 40-headermod);
+       }
+
+       textx = 10;
+       texty = 40-headermod;
+       
+       gdImageString(im, gdFontGetSmall(), textx, texty, (unsigned char*)"   month        rx            tx         total", ctext);
+       gdImageLine(im, textx+2, texty+16, textx+302, texty+16, cline);
+       gdImageLine(im, textx+144, texty+2, textx+144, texty+40+(12*lines), cline);
+       gdImageLine(im, textx+228, texty+2, textx+228, 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);
+                       strftime(datebuff, 16, cfg.mformat, d);
+                       snprintf(buffer, 32, " %8s   ", datebuff);
+                       strncat(buffer, getvalue(data.month[i].rx, data.month[i].rxk, 6, -1), 32);
+                       strcat(buffer, "    ");
+                       strncat(buffer, getvalue(data.month[i].tx, data.month[i].txk, 6, -1), 32);
+                       strcat(buffer, "    ");
+                       strncat(buffer, getvalue(data.month[i].rx+data.month[i].tx, data.month[i].rxk+data.month[i].txk, 6, -1), 32);
+                       gdImageString(im, gdFontGetSmall(), textx, texty, (unsigned char*)buffer, ctext);
+                       drawbar(textx+310, texty+4, 165, 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;
+       }
+
+       gdImageLine(im, textx+2, texty+5, textx+302, texty+5, cline);
+
+       if (lines) {
+
+               d=localtime(&data.lastupdated);
+               if ( data.month[0].rx==0 || data.month[0].tx==0 || ((d->tm_mday-1)*24+d->tm_hour)==0 ) {
+                       e_rx=e_tx=0;
+               } else {                                
+                       e_rx=((data.month[0].rx)/(float)((d->tm_mday-1)*24+d->tm_hour))*(dmonth[d->tm_mon]*24);
+                       e_tx=((data.month[0].tx)/(float)((d->tm_mday-1)*24+d->tm_hour))*(dmonth[d->tm_mon]*24);
+               }
+               sprintf(buffer, " estimated  ");
+               strncat(buffer, getvalue(e_rx, 0, 6, -2), 32);
+               strcat(buffer, "    ");
+               strncat(buffer, getvalue(e_tx, 0, 6, -2), 32);
+               strcat(buffer, "    ");
+               strncat(buffer, getvalue(e_rx+e_tx, 0, 6, -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;
+       height = 86 + (12 * lines);
+
+       if (!showheader) {
+               headermod = 26;
+               height -= 22;
+       } else {
+               headermod = 0;
+       }
+
+       im = gdImageCreate(width, height);
+
+       colorinit();
+       
+       if (strcmp(data.nick, data.interface)==0) {
+               sprintf(buffer, "%s / top 10", data.interface); 
+       } else {
+               sprintf(buffer, "%s (%s) / top 10", data.nick, data.interface);
+       }
+       
+       layoutinit(buffer, width, height, showheader, showedge);
+       
+       if (lines) {
+               drawlegend(405, 40-headermod);
+       }
+
+       textx = 10;
+       texty = 40-headermod;
+       
+       gdImageString(im, gdFontGetSmall(), textx, texty, (unsigned char*)"   #      day         rx            tx         total", ctext);
+       gdImageLine(im, textx+2, texty+16, textx+338, texty+16, cline);
+       if (lines) {
+               gdImageLine(im, textx+180, texty+2, textx+180, texty+24+(12*lines), cline);
+               gdImageLine(im, textx+264, texty+2, textx+264, texty+24+(12*lines), cline);
+       }
+
+       texty += 20;
+
+       for (i = 0; i < 10; i++) {
+               if (data.top10[i].used) {
+
+                       d = localtime(&data.top10[i].date);
+                       strftime(datebuff, 16, cfg.tformat, d);
+                       snprintf(buffer, 32, "  %2d   %8s   ", i+1, datebuff);
+                       strncat(buffer, getvalue(data.top10[i].rx, data.top10[i].rxk, 6, -1), 32);
+                       strcat(buffer, "    ");
+                       strncat(buffer, getvalue(data.top10[i].tx, data.top10[i].txk, 6, -1), 32);
+                       strcat(buffer, "    ");
+                       strncat(buffer, getvalue(data.top10[i].rx+data.top10[i].tx, data.top10[i].rxk+data.top10[i].txk, 6, -1), 32);
+                       gdImageString(im, gdFontGetSmall(), textx, texty, (unsigned char*)buffer, ctext);
+                       drawbar(textx+346, texty+4, 130, 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;
+       }
+
+       gdImageLine(im, textx+2, texty+5, textx+338, texty+5, cline);
+
+}
+
+void hextorgb(char *input, int *rgb)
+{
+       int offset;
+       char hex[3], dec[4];
+
+       if (input[0] == '#') {
+               offset = 1;
+       } else {
+               offset = 0;
+       }
+
+       sprintf(hex, "%c%c", input[(0+offset)], input[(1+offset)]);
+       sprintf(dec, "%d", (int)strtol(hex, NULL, 16));
+       rgb[0] = atoi(dec);
+       sprintf(hex, "%c%c", input[(2+offset)], input[(3+offset)]);
+       sprintf(dec, "%d", (int)strtol(hex, NULL, 16));
+       rgb[1] = atoi(dec);
+       sprintf(hex, "%c%c", input[(4+offset)], input[(5+offset)]);
+       sprintf(dec, "%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){
+               sprintf(buffer, "%*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 */
+                               sprintf(buffer, "%*.*f", len, declen, kb/(float)1000000000); /* 1000*1000*1000 */
+                       } else if (kb>=1000000) { /* 1000*1000 - value >=1000 Mbps -> show in Gbps */
+                               sprintf(buffer, "%*.*f", len, declen, kb/(float)1000000); /* 1000*1000 */
+                       } else if (kb>=1000) {
+                               sprintf(buffer, "%*.*f", len, declen, kb/(float)1000);
+                       } else {
+                               sprintf(buffer, "%*"PRIu64"", len, kb);
+                       }               
+               } else {
+                       if (kb>=1048576000) { /* 1024*1024*1000 - value >=1000 GiB -> show in TiB */
+                               sprintf(buffer, "%*.*f", len, declen, kb/(float)1073741824); /* 1024*1024*1024 */
+                       } else if (kb>=1024000) { /* 1024*1000 - value >=1000 MiB -> show in GiB */
+                               sprintf(buffer, "%*.*f", len, declen, kb/(float)1048576); /* 1024*1024 */
+                       } else if (kb>=1000) {
+                               sprintf(buffer, "%*.*f", len, declen, kb/(float)1024);
+                       } else {
+                               sprintf(buffer, "%*"PRIu64"", len, kb);
+                       }
+               }
+       }
+       
+       return buffer;
+}
+
+char *getimagescale(uint64_t kb, int rate)
+{
+       static char buffer[6];
+       
+       if (kb==0) {
+               sprintf(buffer, "--");
+       } else {
+       
+               if (rate) {
+                       if (kb>=1000000000) { /* 1000*1000*1000 - value >=1000 Gbps -> show in Tbps */
+                               sprintf(buffer, "%s/s", getbunit(2, 4));
+                       } else if (kb>=1000000) { /* 1000*1000 - value >=1000 Mbps -> show in Gbps */
+                               sprintf(buffer, "%s/s", getbunit(2, 3));
+                       } else if (kb>=1000) {
+                               sprintf(buffer, "%s/s", getbunit(2, 2));
+                       } else {
+                               sprintf(buffer, "%s/s", getbunit(2, 1));
+                       }               
+               } else {
+                       if (kb>=1048576000) { /* 1024*1024*1000 - value >=1000 GiB -> show in TiB */
+                               sprintf(buffer, "%s", getunit(4));
+                       } else if (kb>=1024000) { /* 1024*1000 - value >=1000 MiB -> show in GiB */
+                               sprintf(buffer, "%s", getunit(3));
+                       } else if (kb>=1000) {
+                               sprintf(buffer, "%s", getunit(2));
+                       } else {
+                               sprintf(buffer, "%s", getunit(1));
+                       }               
+               }
+       
+       }
+
+       return buffer;
+}
diff --git a/src/image.h b/src/image.h
new file mode 100644 (file)
index 0000000..fd78833
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef IMAGE_H
+#define IMAGE_H
+
+void colorinit(void);
+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 drawhours(int x, int y, int rate);
+void drawsummary(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
index 8a3164fd208c77d8e46ffcc36b801e30c9a3a536..858825cb79d5f947a49da16be8954b75a908d47f 100644 (file)
@@ -1,4 +1,4 @@
-#include "vnstat.h"
+#include "common.h"
 #include "misc.h"
 
 int kerneltest(void)
@@ -60,24 +60,33 @@ int kerneltest(void)
 
 int spacecheck(char *path)
 {
-       struct statfs buf;
+       struct statvfs buf;
        uint64_t free;
 
-       if (statfs(path, &buf)) {
-               perror("Free diskspace check");
-               exit(1);
+       /* do space check only when configured for it */
+       if (!cfg.spacecheck) {
+               return 1;
+       }
+
+       if (statvfs(path, &buf)) {
+               snprintf(errorstring, 512, "Free diskspace check failed.");
+               printe(PT_Error);
+               if (noexit) {
+                       return 0;
+               } else {
+                       exit(1);
+               }
        }
 
        free=(buf.f_bavail/(float)1024)*buf.f_bsize;
 
        if (debug) {
-               printf("bsize %d\n", buf.f_bsize);
-               printf("blocks %lu\n", buf.f_blocks);
-               printf("bfree %lu\n", buf.f_bfree);
-               printf("bavail %lu\n", buf.f_bavail);
-               printf("ffree %lu\n", buf.f_ffree);
-               
-               printf("%Lu free space left\n", free);
+               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 */
@@ -90,43 +99,91 @@ int spacecheck(char *path)
        }
 }
 
-void intr(int sig)
+void sighandler(int sig)
 {
-       intsignal=1;
-       if (debug)
-               printf("Got signal: %d\n", 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, result=0;
+       int check;
        char temp[64], statline[128];
 
        if ((fp=fopen("/proc/stat","r"))==NULL) {
-               printf("Error:\nUnable to read /proc/stat.\n");
-               exit(1);
+               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,"%s",temp);
+               sscanf(statline,"%64s",temp);
                if (strcmp(temp,"btime")==0) {
-                       if (debug)
-                               printf("\n%s\n",statline);
+                       /* if (debug)
+                               printf("\n%s\n",statline); */
                        check=1;
                        break;
                }
        }
        
        if (check==0) {
-               printf("Error:\nbtime missing from /proc/stat.\n");
-               exit(1);
+               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__)
+       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;
 }
 
@@ -141,16 +198,24 @@ void addtraffic(uint64_t *destmb, int *destkb, uint64_t srcmb, int srckb)
         }
 }
 
-char *getvalue(uint64_t mb, uint64_t kb, int len)
+char *getvalue(uint64_t mb, uint64_t kb, int len, int type)
 {
        static char buffer[64];
-       int declen=2;
+       int declen=2, pad=-3;
        uint64_t kB;
 
-       /* don't show decimals .00 if len was negative (set that way for estimates) */
-       if (len<0) {
+       /* negative type disables left padding of value */
+       if (type<0) {
+               type=type*-1;
+               pad=0;
+               if (cfg.unit>0) {
+                       len++;
+               }
+       }
+
+       /* request types: 1) normal  2) estimate  3) image scale */
+       if (type==3) {
                declen=0;
-               len=abs(len);
        }
 
        if (mb!=0) {
@@ -163,71 +228,99 @@ char *getvalue(uint64_t mb, uint64_t kb, int len)
                kB=kb;
        }
 
-
-       if ( (declen==0) && (kB==0) ){
-               sprintf(buffer, "%*s   ", len, "--");
+       if ( (type==2) && (kB==0) ){
+               sprintf(buffer, "%*s    ", len, "--");
        } else {
+#if !defined(__OpenBSD__)
                /* try to figure out what unit to use */
-               if (kB>=(1024*1024*1000)) {
-                       sprintf(buffer, "%'*.2f TB", len, kB/(float)1024/(float)1024/(float)1024);
-               } else if (kB>=(1024*1000)) {
-                       sprintf(buffer, "%'*.2f GB", len, kB/(float)1024/(float)1024);
+               if (kB>=1048576000) { /* 1024*1024*1000 - value >=1000 GiB -> show in TiB */
+                       sprintf(buffer, "%'*.*f %*s", len, declen, kB/(float)1073741824, pad, getunit(4)); /* 1024*1024*1024 */
+               } else if (kB>=1024000) { /* 1024*1000 - value >=1000 MiB -> show in GiB */
+                       sprintf(buffer, "%'*.*f %*s", len, declen, kB/(float)1048576, pad, getunit(3)); /* 1024*1024 */
                } else if (kB>=1000) {
-                       sprintf(buffer, "%'*.*f MB", len, declen, kB/(float)1024);
+                       if (type==2) {
+                               declen=0;
+                       }
+                       sprintf(buffer, "%'*.*f %*s", len, declen, kB/(float)1024, pad, getunit(2));
                } else {
-                       sprintf(buffer, "%'*Lu kB", len, kB);
+                       sprintf(buffer, "%'*"PRIu64" %*s", len, kB, pad, getunit(1));
                }
+#else
+               /* try to figure out what unit to use */
+               if (kB>=1048576000) { /* 1024*1024*1000 - value >=1000 GiB -> show in TiB */
+                       sprintf(buffer, "%*.*f %*s", len, declen, kB/(float)1073741824, pad, getunit(4)); /* 1024*1024*1024 */
+               } else if (kB>=1024000) { /* 1024*1000 - value >=1000 MiB -> show in GiB */
+                       sprintf(buffer, "%*.*f %*s", len, declen, kB/(float)1048576, pad, getunit(3)); /* 1024*1024 */
+               } else if (kB>=1000) {
+                       if (type==2) {
+                               declen=0;
+                       }
+                       sprintf(buffer, "%*.*f %*s", len, declen, kB/(float)1024, pad, getunit(2));
+               } else {
+                       sprintf(buffer, "%'*"PRIu64" %*s", len, kB, pad, getunit(1));
+               }
+#endif
        }
        
        return buffer;
 }
 
-
-void showbar(uint64_t rx, int rxk, uint64_t tx, int txk, uint64_t max, int len)
+uint64_t getscale(uint64_t kb)
 {
-       int i, l;
+       int i;
+       uint64_t result;
+
+       result = kb;
        
-       if (rxk>=1024) {
-               rx+=rxk/1024;
-               rxk-=(rxk/1024)*1024;
+       /* get unit */
+       for (i=0; result>1024; i++) {
+               result = result / 1024;
        }
 
-       if (txk>=1024) {
-               tx+=txk/1024;
-               txk-=(txk/1024)*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;
        }
-
-       rx=(rx*1024)+rxk;
-       tx=(tx*1024)+txk;
        
-       if ((rx+tx)!=max) {
-               len=((rx+tx)/(float)max)*len;
+       /* put unit back */
+       if (i) {
+               result = result * pow(1024, i);
        }
 
+       /* make sure result isn't zero */
+       if (!result) {
+               result = pow(1024, i);
+       }
 
-       if (len!=0) {
-               printf("   ");
+       return result;
+}
 
-               if (tx>rx) {
-                       l=rint((rx/(float)(rx+tx)*len));
+char *getunit(int index)
+{
+       static char *unit[] = { "na", "KiB", "MiB", "GiB", "TiB",
+                                   "KB",  "MB",  "GB",  "TB",
+                                   "kB",  "MB",  "GB",  "TB" };
 
-                       for (i=0;i<l;i++) {
-                               printf("%c", cfg.rxchar[0]);
-                       }
-                       for (i=0;i<(len-l);i++) {
-                               printf("%c", cfg.txchar[0]);
-                       }
-               } else {
-                       l=rint((tx/(float)(rx+tx)*len));
-                       
-                       for (i=0;i<(len-l);i++) {
-                               printf("%c", cfg.rxchar[0]);
-                       }
-                       for (i=0;i<l;i++) {
-                               printf("%c", cfg.txchar[0]);
-                       }               
-               }
-               
+       if (index>UNITCOUNT) {
+               return unit[0];
+       } else {
+               return unit[(cfg.unit*UNITCOUNT)+index];
+       }
+}
+
+char *getbunit(int unit, int index)
+{
+       static char *bunit[] = { "na", "Kibit", "Mibit", "Gibit", "Tibit",
+                                    "Kbit",  "Mbit",  "Gbit",  "Tbit",
+                                    "kbit",  "Mbit",  "Gbit",  "Tbit" };
+
+       if (index>UNITCOUNT) {
+               return bunit[0];
+       } else {
+               return bunit[(unit*UNITCOUNT)+index];
        }
-       
 }
index 7c11632d7d938371335c17b76562bd22f125afac..f4485ba24cb528c02e2e1b2cbb9dffba2f464c41 100644 (file)
@@ -1,7 +1,16 @@
+#ifndef MISC_H
+#define MISC_H
+
+#define UNITCOUNT 4
+
 int kerneltest(void);
 int spacecheck(char *path);
-void intr(int);
+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);
-void showbar(uint64_t rx, int rxk, uint64_t tx, int txk, uint64_t max, int len);
+char *getvalue(uint64_t mb, uint64_t kb, int len, int type);
+uint64_t getscale(uint64_t kb);
+char *getunit(int index);
+char *getbunit(int unit, int index);
+
+#endif
diff --git a/src/proc.c b/src/proc.c
deleted file mode 100644 (file)
index 81525fa..0000000
+++ /dev/null
@@ -1,484 +0,0 @@
-#include "vnstat.h"
-#include "proc.h"
-#include "misc.h"
-#include "db.h"
-#include "cfg.h"
-
-void readproc(char iface[32])
-{
-       FILE *fp;
-       char temp[64], inface[32];
-       int check;
-       
-       if ((fp=fopen("/proc/net/dev","r"))==NULL) {
-               printf("Error:\nUnable to read /proc/net/dev.\n");
-               exit(1);
-       }
-
-       if (strcmp(iface,"default")==0) {
-               strncpy(inface, cfg.iface, 32);
-       } else {
-               strncpy(inface, iface, 32);
-       }
-
-       check=0;
-       while (fgets(procline,512,fp)!=NULL) {
-               sscanf(procline,"%s",temp);
-               if (strncmp(inface,temp,strlen(inface))==0) {
-                       if (debug)
-                               printf("\n%s\n",procline);
-                       check=1;
-                       break;
-               }
-       }
-       fclose(fp);
-       
-       if (check==0) {
-               printf("Requested interface \"%s\" not found.\n",inface);
-               printf("Exiting...\n");
-               exit(1);
-       }
-
-}
-
-void parseproc(int newdb)
-{
-       char temp[64], temp2[64];
-       uint64_t rx, tx, rxchange=0, txchange=0, btime;   /* 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 */
-       char *proclineptr;
-
-       current=time(NULL);
-       interval=current-data.lastupdated;
-       btime=getbtime();
-
-       /* get rx and tx from procline */
-       proclineptr = strchr(procline, ':');
-       sscanf(proclineptr+1, "%s %*s %*s %*s %*s %*s %*s %*s %s",temp, temp2);
-
-       rx=strtoull(temp, (char **)NULL, 0);
-       tx=strtoull(temp2, (char **)NULL, 0);
-
-       /* 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) {
-                       rxchange = countercalc(data.currx, rx)/1024/1024;
-                       krxchange = countercalc(data.currx, rx)/1024;
-                       rxkchange = (countercalc(data.currx, rx)/1024)%1024;
-
-                       txchange = countercalc(data.curtx, tx)/1024/1024;
-                       ktxchange = countercalc(data.curtx, tx)/1024;
-                       txkchange = (countercalc(data.curtx, tx)/1024)%1024;
-               }
-
-               /* get maximum bandwidth */
-               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: %Lu  maxbw: %d  maxrate: %Lu  rxc: %Lu  txc: %Lu\n", (uint64_t)interval, maxbw, maxtransfer, rxchange, txchange); 
-
-                       /* sync counters if traffic is greater than set maximum */
-                       if ( (rxchange > maxtransfer) || (txchange > maxtransfer) ) {
-                               rxchange=krxchange=rxkchange=txchange=ktxchange=txkchange=0;
-                               if (debug)
-                                       printf("Traffic is greater than set maximum, counters synced.\n");
-                       }
-               }
-
-       } else {
-               if (debug)
-                       printf("Too much time passed since previous update, syncing. (%Lu < %d)\n", (uint64_t)interval, 60*MAXUPDATEINTERVAL);
-       }
-
-
-       /* keep btime updated in case it drifts slowly */
-       data.btime=btime;
-
-       data.currx=rx;  
-       data.curtx=tx;
-       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(&current);
-       day=d->tm_mday;
-       month=d->tm_mon;
-       year=d->tm_year;
-       hour=d->tm_hour;
-       min=d->tm_min;
-       
-       /* shift traffic to previous hour when update happens at X:00 */
-       if (min==0) {
-               shift=hour;
-               hour--;
-               if (hour<0)
-                       hour+=24;     /* hour can't be -1 :) */
-       } else {
-               shift=hour;
-       }
-       
-       /* 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))
-               rotatedays();
-
-       /* rotate months in database if needed */
-       d=localtime(&data.month[0].month);
-       if ((d->tm_mon!=month) && (day>=MONTHROTATE))
-               rotatemonths();
-
-}
-
-void trafficmeter(char iface[32], int sampletime)
-{
-       /* received bytes packets errs drop fifo frame compressed multicast */
-       /* transmitted bytes packets errs drop fifo colls carrier compressed */
-       uint64_t p1[16], p2[16], rx, tx, rxp, txp;
-       int i, j, len;
-       char temp[64], buffer[256];
-       char *proclineptr;
-
-       /* less than 2 seconds doesn't produce good results */
-       if (sampletime<2) {
-               printf("Error:\nTime for sampling too short.\n");
-               exit(1);
-       }
-
-       /* read /proc/net/dev and get values to the first list */
-       j=0;
-       readproc(iface);
-       proclineptr = strchr(procline, ':');
-       for (i=1;i<strlen(proclineptr);i++) {
-               if (proclineptr[i]!=' ') {
-                       sscanf(proclineptr+i,"%s",temp);
-                       i+=strlen(temp);
-                       p1[j]=strtoull(temp, (char **)NULL, 0);
-                       if (debug)
-                               printf("%8d '%s' -> '%Lu'\n",j,temp,p1[j]);
-                       j++;
-               }
-       }
-
-       /* wait sampletime and print some nice dots so that the user thinks
-       something is done :) */
-       sprintf(buffer,"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;i<len;i++) {
-               printf("\b \b");
-       }
-       
-       /* read those values again... */
-       j=0;
-       readproc(iface);
-       proclineptr = strchr(procline, ':');
-       for (i=1;i<strlen(proclineptr);i++) {
-               if (proclineptr[i]!=' ') {
-                       sscanf(proclineptr+i,"%s",temp);
-                       i+=strlen(temp);
-                       p2[j]=strtoull(temp, (char **)NULL, 0);
-                       if (debug)
-                               printf("%8d '%s' -> '%Lu'\n",j,temp,p2[j]);
-                       j++;
-               }
-       }
-
-       /* calculate traffic and packets seen between updates */
-       rx = countercalc(p1[0], p2[0]);
-       tx = countercalc(p1[8], p2[8]);
-       rxp = countercalc(p1[1], p2[1]);
-       txp = countercalc(p1[9], p2[9]);
-
-       /* show the difference in a readable format */
-       printf("%Lu packets sampled in %d seconds\n", rxp+txp, sampletime);
-       printf("Traffic average for %s\n", iface);
-       printf("\n      rx     %10.2f kB/s          %5Lu packets/s\n", rx/(float)sampletime/(float)1024, (uint64_t)(rxp/sampletime));
-       printf("      tx     %10.2f kB/s          %5Lu packets/s\n\n", tx/(float)sampletime/(float)1024, (uint64_t)(txp/sampletime));
-
-}
-
-void livetrafficmeter(char iface[32])
-{
-       /* received bytes packets errs drop fifo frame compressed multicast */
-       /* transmitted bytes packets errs drop fifo colls carrier compressed */
-       uint64_t p1[16], p2[16], rx, tx, rxp, txp, rxpc, txpc, timespent;
-       uint64_t rxtotal, txtotal, rxptotal, txptotal;
-       uint64_t rxpmin, txpmin, rxpmax, txpmax;
-       float rxmin, txmin, rxmax, txmax, rxc, txc;
-       int i, j, len;
-       char temp[64], buffer[256];
-       char *proclineptr;
-
-       printf("Monitoring %s...    (press CTRL-C to stop)\n\n", iface);
-       printf("   getting traffic...");
-       len=21;
-       fflush(stdout);
-
-       /* enable signal trap */
-       if (signal(SIGINT, intr) == SIG_ERR) {
-               perror("signal");
-               exit(1);
-       }
-
-       /* set some defaults */
-       rxtotal=txtotal=rxptotal=txptotal=rxpmax=txpmax=0;
-       rxpmin=txpmin=-1;
-       rxmax=txmax=0.0;
-       rxmin=txmin=-1.0;
-       
-       timespent = (uint64_t)time(NULL);
-
-       /* read /proc/net/dev and get values to the first list */
-       j=0;
-       readproc(iface);
-       proclineptr = strchr(procline, ':');
-       for (i=1;i<strlen(proclineptr);i++) {
-               if (proclineptr[i]!=' ') {
-                       sscanf(proclineptr+i,"%s",temp);
-                       i+=strlen(temp);
-                       p1[j]=strtoull(temp, (char **)NULL, 0);
-                       if (debug)
-                               printf("%8d '%s' -> '%Lu'\n",j,temp,p1[j]);
-                       j++;
-               }
-       }
-
-       /* loop until user gets bored */
-       while (intsignal==0) {
-
-               /* wait 2 seconds for more traffic */
-               sleep(LIVETIME);
-
-               /* break loop without calculations because sleep was probably interrupted */
-               if (intsignal) {
-                       break;
-               }
-
-               /* use values from previous loop if this isn't the first time */
-               if (len!=21) {
-                       for (i=0;i<16;i++) {
-                               p1[i]=p2[i];
-                       }
-               }
-
-               /* read those values again... */
-               j=0;
-               readproc(iface);
-               proclineptr = strchr(procline, ':');
-               for (i=1;i<strlen(proclineptr);i++) {
-                       if (proclineptr[i]!=' ') {
-                               sscanf(proclineptr+i,"%s",temp);
-                               i+=strlen(temp);
-                               p2[j]=strtoull(temp, (char **)NULL, 0);
-                               if (debug)
-                                       printf("%8d '%s' -> '%Lu'\n",j,temp,p2[j]);
-                               j++;
-                       }
-               }
-
-               /* calculate traffic and packets seen between updates */
-               rx = countercalc(p1[0], p2[0]);
-               tx = countercalc(p1[8], p2[8]);
-               rxp = countercalc(p1[1], p2[1]);
-               txp = countercalc(p1[9], p2[9]);
-
-               /* update totals */
-               rxtotal += rx;
-               txtotal += tx;
-               rxptotal += rxp;
-               txptotal += txp;
-
-               rxc = rx/(float)LIVETIME/(float)1024;
-               txc = tx/(float)LIVETIME/(float)1024;
-               rxpc = rxp/LIVETIME;
-               txpc = txp/LIVETIME;            
-
-               /* update min & max */
-               if ((rxmin==-1.0) || (rxmin>rxc)) {
-                       rxmin = rxc;
-               }
-               if ((txmin==-1.0) || (txmin>txc)) {
-                       txmin = txc;
-               }
-               if (rxmax<rxc) {
-                       rxmax = rxc;
-               }
-               if (txmax<txc) {
-                       txmax = txc;
-               }
-               
-               if ((rxpmin==-1) || (rxpmin>rxpc)) {
-                       rxpmin = rxpc;
-               }
-               if ((txpmin==-1) || (txpmin>txpc)) {
-                       txpmin = txpc;
-               }
-               if (rxpmax<rxpc) {
-                       rxpmax = rxpc;
-               }
-               if (txpmax<txpc) {
-                       txpmax = txpc;
-               }
-
-               /* show the difference in a readable format */
-               snprintf(buffer, 256, "   rx: %10.2f kB/s %5Lu p/s            tx: %10.2f kB/s %5Lu p/s", rxc, (uint64_t)rxpc, txc, (uint64_t)txpc);
-               
-               if (len>1) {
-                       if (debug) {
-                               printf("\nlinelen: %d\n", len);
-                       } else {
-                               for (i=0;i<len;i++) {
-                                       printf("\b \b");
-                               }
-                               fflush(stdout);
-                       }
-               }
-               printf("%s", buffer);
-               fflush(stdout);
-               len=strlen(buffer);
-       
-       }
-
-       timespent = (uint64_t)time(NULL) - timespent;
-
-       printf("\n\n");
-
-       /* print some statistics if enough time did pass */
-       if (timespent>10) {
-
-               printf("\n %s  /  traffic statistics\n\n", iface);
-
-               printf("                             rx       |       tx\n");
-               printf("--------------------------------------+----------------------------------------\n");
-               printf("  bytes                ");
-               showint(0, rxtotal/1024, 10);
-               printf("  | ");
-               showint(0, txtotal/1024, 10);
-               printf("\n");
-               printf("--------------------------------------+----------------------------------------\n");
-               printf("          max          ");
-               showspeed(rxmax, 8);
-               printf("  | ");
-               showspeed(txmax, 8);
-               printf("\n");
-               printf("      average          ");
-               showspeed(rxtotal/(float)timespent/(float)1024, 8);
-               printf("  | ");
-               showspeed(txtotal/(float)timespent/(float)1024, 8);
-               printf("\n");
-               printf("          min          ");
-               showspeed(rxmin, 8);
-               printf("  | ");
-               showspeed(txmin, 8);
-               printf("\n");
-               printf("--------------------------------------+----------------------------------------\n");
-               printf("  packets               %12Lu  |  %12Lu\n", rxptotal, txptotal);
-               printf("--------------------------------------+----------------------------------------\n");
-               printf("          max          %9Lu p/s  | %9Lu p/s\n", rxpmax, txpmax);
-               printf("      average          %9Lu p/s  | %9Lu p/s\n", rxptotal/timespent, txptotal/timespent);
-               printf("          min          %9Lu p/s  | %9Lu p/s\n", rxpmin, txpmin);
-               printf("--------------------------------------+----------------------------------------\n");
-               
-               if (timespent<=60) {
-                       printf("  time             %9Lu seconds\n", timespent);
-               } else {
-                       printf("  time               %7.2f minutes\n", timespent/(float)60);
-               }
-
-               printf("\n");
-       }
-       
-}
-
-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;
-       }
-}
-
-uint64_t countercalc(uint64_t a, uint64_t b)
-{
-       /* no flip */
-       if (b>=a) {
-               if (debug)
-                       printf("cc: %Lu - %Lu = %Lu\n", b, a, b-a);
-               return b-a;
-
-       /* flip exists */
-       } else {
-               /* original counter is 64bit */
-               if (a>FP32) {
-                       if (debug)
-                               printf("cc64: uint64 - %Lu + %Lu = %Lu\n", a, b, FP64-a+b);
-                       return FP64-a+b;
-
-               /* original counter is 32bit */
-               } else {
-                       if (debug)
-                               printf("cc32: uint32 - %Lu + %Lu = %Lu\n", a, b, FP32-a+b);
-                       return FP32-a+b;
-               }
-       }
-}
-
-void showspeed(float xfer, int len)
-{
-       if (xfer>1024) {
-               printf("%'*.2f MB/s", len, xfer/(float)1024);
-       } else {
-               printf("%'*.2f kB/s", len, xfer);
-       }
-}
diff --git a/src/proc.h b/src/proc.h
deleted file mode 100644 (file)
index cc58a39..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-void readproc(char iface[32]);
-void parseproc(int newdb);
-void trafficmeter(char iface[32], int sampletime);
-void livetrafficmeter(char iface[32]);
-void addtraffic(uint64_t *destmb, int *destkb, uint64_t srcmb, int srckb);
-uint64_t countercalc(uint64_t a, uint64_t b);
-void showspeed(float xfer, int len);
diff --git a/src/traffic.c b/src/traffic.c
new file mode 100644 (file)
index 0000000..52a7cf4
--- /dev/null
@@ -0,0 +1,267 @@
+#include "common.h"
+#include "ifinfo.h"
+#include "misc.h"
+#include "traffic.h"
+
+void trafficmeter(char iface[32], 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(1);
+       }
+
+       /* read interface info and get values to the first list */
+       if (!getifinfo(iface)) {
+               printf("Error: Interface \"%s\" not available, exiting.\n", iface);
+               exit(1);
+       }
+       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 :) */
+       sprintf(buffer,"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;i<len;i++) {
+               printf("\b \b");
+       }
+       
+       /* read those values again... */
+       if (!getifinfo(iface)) {
+               printf("Error: Interface \"%s\" not available, exiting.\n", iface);
+               exit(1);
+       }
+
+       /* calculate traffic and packets seen between updates */
+       rx = countercalc(firstinfo.rx, ifinfo.rx);
+       tx = countercalc(firstinfo.tx, ifinfo.tx);
+       rxp = countercalc(firstinfo.rxp, ifinfo.rxp);
+       txp = countercalc(firstinfo.txp, ifinfo.txp);
+
+       /* show the difference in a readable format */
+       printf("%"PRIu64" packets sampled in %d seconds\n", rxp+txp, sampletime);
+       printf("Traffic average for %s\n", iface);
+       printf("\n      rx     %10.2f %s/s         %5"PRIu64" packets/s\n", rx/(float)sampletime/(float)1024, getunit(1), (uint64_t)(rxp/sampletime));
+       printf("      tx     %10.2f %s/s         %5"PRIu64" packets/s\n\n", tx/(float)sampletime/(float)1024, getunit(1), (uint64_t)(txp/sampletime));
+
+}
+
+void livetrafficmeter(char iface[32])
+{
+       /* received bytes packets errs drop fifo frame compressed multicast */
+       /* transmitted bytes packets errs drop fifo colls carrier compressed */
+       uint64_t rx, tx, rxp, txp, rxpc, txpc, timespent;
+       uint64_t rxtotal, txtotal, rxptotal, txptotal;
+       uint64_t rxpmin, txpmin, rxpmax, txpmax;
+       float rxmin, txmin, rxmax, txmax, rxc, txc;
+       int i, len;
+       char buffer[256];
+       IFINFO previnfo;
+
+       printf("Monitoring %s...    (press CTRL-C to stop)\n\n", iface);
+       printf("   getting traffic...");
+       len=21; /* length of previous print */
+       fflush(stdout);
+
+       /* enable signal trap */
+       intsignal = 0;
+       if (signal(SIGINT, sighandler) == SIG_ERR) {
+               perror("signal");
+               exit(1);
+       }
+
+       /* set some defaults */
+       rxtotal=txtotal=rxptotal=txptotal=rxpmax=txpmax=0;
+       rxpmin=txpmin=-1;
+       rxmax=txmax=0.0;
+       rxmin=txmin=-1.0;
+       
+       timespent = (uint64_t)time(NULL);
+
+       /* read /proc/net/dev and get values to the first list */
+       if (!getifinfo(iface)) {
+               printf("Error: Interface \"%s\" not available, exiting.\n", iface);
+               exit(1);
+       }
+
+       /* loop until user gets bored */
+       while (intsignal==0) {
+
+               /* wait 2 seconds for more traffic */
+               sleep(LIVETIME);
+
+               /* break loop without calculations because sleep was probably interrupted */
+               if (intsignal) {
+                       break;
+               }
+
+               /* use values from previous loop if this isn't the first time */
+               previnfo.rx = ifinfo.rx;
+               previnfo.tx = ifinfo.tx;
+               previnfo.rxp = ifinfo.rxp;
+               previnfo.txp = ifinfo.txp;
+
+               /* read those values again... */
+               if (!getifinfo(iface)) {
+                       printf("Error: Interface \"%s\" not available, exiting.\n", iface);
+                       exit(1);
+               }
+
+               /* calculate traffic and packets seen between updates */
+               rx = countercalc(previnfo.rx, ifinfo.rx);
+               tx = countercalc(previnfo.tx, ifinfo.tx);
+               rxp = countercalc(previnfo.rxp, ifinfo.rxp);
+               txp = countercalc(previnfo.txp, ifinfo.txp);
+
+               /* update totals */
+               rxtotal += rx;
+               txtotal += tx;
+               rxptotal += rxp;
+               txptotal += txp;
+
+               rxc = rx/(float)LIVETIME/(float)1024;
+               txc = tx/(float)LIVETIME/(float)1024;
+               rxpc = rxp/LIVETIME;
+               txpc = txp/LIVETIME;            
+
+               /* update min & max */
+               if ((rxmin==-1.0) || (rxmin>rxc)) {
+                       rxmin = rxc;
+               }
+               if ((txmin==-1.0) || (txmin>txc)) {
+                       txmin = txc;
+               }
+               if (rxmax<rxc) {
+                       rxmax = rxc;
+               }
+               if (txmax<txc) {
+                       txmax = txc;
+               }
+               
+               if ((rxpmin==-1) || (rxpmin>rxpc)) {
+                       rxpmin = rxpc;
+               }
+               if ((txpmin==-1) || (txpmin>txpc)) {
+                       txpmin = txpc;
+               }
+               if (rxpmax<rxpc) {
+                       rxpmax = rxpc;
+               }
+               if (txpmax<txpc) {
+                       txpmax = txpc;
+               }
+
+               /* show the difference in a readable format */
+               snprintf(buffer, 256, "   rx: %10.2f %s/s %5"PRIu64" p/s          tx: %10.2f %s/s %5"PRIu64" p/s", rxc, getunit(1), (uint64_t)rxpc, txc, getunit(1), (uint64_t)txpc);
+               
+               if (len>1) {
+                       if (debug) {
+                               printf("\nlinelen: %d\n", len);
+                       } else {
+                               for (i=0;i<len;i++) {
+                                       printf("\b \b");
+                               }
+                               fflush(stdout);
+                       }
+               }
+               printf("%s", buffer);
+               fflush(stdout);
+               len=strlen(buffer);
+       
+       }
+
+       timespent = (uint64_t)time(NULL) - timespent;
+
+       printf("\n\n");
+
+       /* print some statistics if enough time did pass */
+       if (timespent>10) {
+
+               printf("\n %s  /  traffic statistics\n\n", iface);
+
+               printf("                             rx       |       tx\n");
+               printf("--------------------------------------+----------------------------------------\n");
+               printf("  bytes                %s", getvalue(0, rxtotal/1024, 9, -1));
+               printf("  | %s", getvalue(0, txtotal/1024, 9, -1));
+               printf("\n");
+               printf("--------------------------------------+----------------------------------------\n");
+               printf("          max          ");
+               showspeed(rxmax, 7);
+               printf("  | ");
+               showspeed(txmax, 7);
+               printf("\n");
+               printf("      average          ");
+               showspeed(rxtotal/(float)timespent/(float)1024, 7);
+               printf("  | ");
+               showspeed(txtotal/(float)timespent/(float)1024, 7);
+               printf("\n");
+               printf("          min          ");
+               showspeed(rxmin, 7);
+               printf("  | ");
+               showspeed(txmin, 7);
+               printf("\n");
+               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");
+       }
+       
+}
+
+void showspeed(float xfer, int len)
+{
+       if (cfg.unit>0) {
+               len++;
+       }
+
+#if !defined(__OpenBSD__)
+       if (xfer>=1000) {
+               printf("%'*.2f %s/s", len, xfer/(float)1024, getunit(2));
+       } else {
+               printf("%'*.2f %s/s", len, xfer, getunit(1));
+       }
+#else
+       if (xfer>=1000) {
+               printf("%*.2f %s/s", len, xfer/(float)1024, getunit(2));
+       } else {
+               printf("%*.2f KiB/s", len, xfer, getunit(1));
+       }
+#endif
+}
diff --git a/src/traffic.h b/src/traffic.h
new file mode 100644 (file)
index 0000000..4450d0c
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef TRAFFIC_H
+#define TRAFFIC_H
+
+void trafficmeter(char iface[32], int sampletime);
+void livetrafficmeter(char iface[32]);
+void showspeed(float xfer, int len);
+
+#endif
index 50c2ed739e705b7fd44956aca7fe1eadb54c629b..8553a32c2a98325d7755c9f6171363614d50cb9f 100644 (file)
@@ -1,5 +1,5 @@
 /*
-vnStat - Copyright (c) 2002-08 Teemu Toivola <tst@iki.fi>
+vnStat - Copyright (c) 2002-09 Teemu Toivola <tst@iki.fi>
 
    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
@@ -15,12 +15,15 @@ vnStat - Copyright (c) 2002-08 Teemu Toivola <tst@iki.fi>
    Foundation, Inc., 675 Mass Ave., Cambridge, MA 02139, USA.
 */
 
-#include "vnstat.h"
+#include "common.h"
 #include "ifinfo.h"
-#include "db.h"
+#include "traffic.h"
+#include "dbxml.h"
+#include "dbshow.h"
 #include "dbaccess.h"
 #include "misc.h"
 #include "cfg.h"
+#include "vnstat.h"
 
 int main(int argc, char *argv[]) {
 
@@ -29,11 +32,12 @@ int main(int argc, char *argv[]) {
        int active=-1, files=0, force=0, cleartop=0, rebuildtotal=0, traffic=0;
        int livetraffic=0;
        char interface[32], dirname[512], nick[32];
-       char definterface[32], cfgfile[512];
+       char definterface[32], cfgfile[512], *ifacelist;
        time_t current;
        DIR *dir;
        struct dirent *di;
 
+       noexit = 0; /* allow functions to exit in case of error */
        debug = 0; /* debug disabled by default */
        cfgfile[0] = '\0';
 
@@ -50,7 +54,7 @@ int main(int argc, char *argv[]) {
                                        currentarg++;
                                        continue;
                                } else {
-                                       printf("Error:\nFile for --config missing.\n");
+                                       printf("Error: File for --config missing.\n");
                                        return 1;
                                }
                        }
@@ -58,7 +62,9 @@ int main(int argc, char *argv[]) {
        }
        
        /* load config if available */
-       loadcfg(cfgfile);
+       if (!loadcfg(cfgfile)) {
+               return 1;
+       }
 
        setlocale(LC_ALL, cfg.locale);
        strncpy(interface, "default", 32);
@@ -97,6 +103,7 @@ int main(int argc, char *argv[]) {
                        printf("         -t, --top10           show top10\n");
                        printf("         -s, --short           use short output\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);
@@ -105,6 +112,7 @@ int main(int argc, char *argv[]) {
                        printf("         -v,  --version        show version\n");
                        printf("         -tr, --traffic        calculate traffic\n");
                        printf("         -l,  --live           show transfer rate in real time\n");
+                       printf("         --iflist              show list of available interfaces\n");
                        printf("         --config              select used config file\n");
                        printf("         --showconfig          dump config file with current settings\n");
                        printf("         --testkernel          check if the kernel is broken\n");
@@ -144,7 +152,7 @@ int main(int argc, char *argv[]) {
                                currentarg++;
                                continue;
                        } else {
-                               printf("Error:\nInterface for -i missing.\n");
+                               printf("Error: Interface for -i missing.\n");
                                return 1;
                        }
                } else if (strcmp(argv[currentarg],"--config")==0) {
@@ -159,7 +167,7 @@ int main(int argc, char *argv[]) {
                                currentarg++;
                                continue;
                        } else {
-                               printf("Error:\nNick for --nick missing.\n");
+                               printf("Error: Nick for --nick missing.\n");
                                return 1;
                        }
                } else if ((strcmp(argv[currentarg],"-u")==0) || (strcmp(argv[currentarg],"--update")==0)) {
@@ -185,6 +193,8 @@ int main(int argc, char *argv[]) {
                        cfg.qmode=7;
                } else if (strcmp(argv[currentarg],"--dumpdb")==0) {
                        cfg.qmode=4;
+               } else if (strcmp(argv[currentarg],"--xml")==0) {
+                       cfg.qmode=8;
                } else if (strcmp(argv[currentarg],"--enable")==0) {
                        active=1;
                        query=0;
@@ -218,6 +228,11 @@ int main(int argc, char *argv[]) {
                } else if (strcmp(argv[currentarg],"--showconfig")==0) {
                        printcfgfile();
                        return 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 <tst at iki dot fi>\n", VNSTATVERSION);
                        return 0;
@@ -228,7 +243,7 @@ int main(int argc, char *argv[]) {
                        sync=1;
                        query=0;                        
                } else {
-                       printf("Unknown arg \"%s\". Use --help for help.\n",argv[currentarg]);
+                       printf("Unknown parameter \"%s\". Use --help for help.\n",argv[currentarg]);
                        return 1;
                }
                
@@ -252,7 +267,7 @@ int main(int argc, char *argv[]) {
                        }
                        closedir(dir);
                } else {
-                       printf("Error:\nUnable to open database directory \"%s\".\n", dirname);
+                       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;
@@ -262,7 +277,7 @@ int main(int argc, char *argv[]) {
        /* counter reset */
        if (reset) {
                if (!spacecheck(dirname) && !force) {
-                       printf("Error:\nNot enough free diskspace available.\n");
+                       printf("Error: Not enough free diskspace available.\n");
                        return 1;
                }
                if (strcmp(interface, "default")==0)
@@ -278,12 +293,14 @@ int main(int argc, char *argv[]) {
        /* counter sync */
        if (sync) {
                if (!spacecheck(dirname) && !force) {
-                       printf("Error:\nNot enough free diskspace available.\n");
+                       printf("Error: Not enough free diskspace available.\n");
                        return 1;
                }
                if (strcmp(interface, "default")==0)
                        strncpy(interface, definterface, 32);
-               synccounters(interface, dirname);
+               if (!synccounters(interface, dirname)) {
+                       return 1;
+               }
                if (debug)
                        printf("Counters synced for \"%s\"\n", data.interface);
        }
@@ -291,7 +308,7 @@ int main(int argc, char *argv[]) {
        /* clear top10 */
        if (cleartop) {
                if (!spacecheck(dirname) && !force) {
-                       printf("Error:\nNot enough free diskspace available.\n");
+                       printf("Error: Not enough free diskspace available.\n");
                        return 1;
                }
                if (strcmp(interface, "default")==0)
@@ -318,7 +335,7 @@ int main(int argc, char *argv[]) {
        /* rebuild total */
        if (rebuildtotal) {
                if (!spacecheck(dirname)) {
-                       printf("Error:\nNot enough free diskspace available.\n");
+                       printf("Error: Not enough free diskspace available.\n");
                        return 1;
                }
                if (strcmp(interface, "default")==0)
@@ -347,7 +364,7 @@ int main(int argc, char *argv[]) {
        /* enable & disable */
        if (active==1) {
                if (!spacecheck(dirname) && !force) {
-                       printf("Error:\nNot enough free diskspace available.\n");
+                       printf("Error: Not enough free diskspace available.\n");
                        return 1;
                }
                if (strcmp(interface, "default")==0)
@@ -363,7 +380,7 @@ int main(int argc, char *argv[]) {
                }
        } else if (active==0) {
                if (!spacecheck(dirname) && !force) {
-                       printf("Error:\nNot enough free diskspace available.\n");
+                       printf("Error: Not enough free diskspace available.\n");
                        return 1;
                }
                if (strcmp(interface, "default")==0)
@@ -384,7 +401,7 @@ int main(int argc, char *argv[]) {
        
                /* check that there's some free diskspace left */
                if (!spacecheck(dirname) && !force) {
-                       printf("Error:\nNot enough free diskspace available.\n");
+                       printf("Error: Not enough free diskspace available.\n");
                        return 1;
                }
                
@@ -402,18 +419,30 @@ int main(int argc, char *argv[]) {
                                                printf("\nProcessing file \"%s/%s\"...\n", dirname, interface);
                                        newdb=readdb(interface, dirname);
                                        if (data.active) {
-                                               getifinfo(data.interface);
+                                               /* 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 {
-                                                       printf("Error:\nThe 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(&current)));
-                                                       printf("Use --force to override this message.\n");
-                                                       return 1;
+                                                       /* 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(&current)));
+                                                               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(&current)));
+                                                       }
                                                }
                                        } else {
                                                if (debug)
@@ -435,18 +464,31 @@ int main(int argc, char *argv[]) {
                } else {
                        newdb=readdb(interface, dirname);
                        if (data.active) {
-                               getifinfo(data.interface);
+                               if (!getifinfo(data.interface)) {
+                                       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 {
-                                       printf("Error:\nThe 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(&current)));
-                                       printf("Use --force to override this message.\n");
-                                       return 1;
+                                       /* 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(&current)));
+                                               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(&current)));
+                                       }
                                }
                        } else {
                                if (debug)
@@ -462,42 +504,65 @@ int main(int argc, char *argv[]) {
                if (strcmp(interface, "default")==0) {
                        
                        if (files==0) {
-                               // printf("No database found.\n");
+                               /* printf("No database found.\n"); */
                                query=0;
-                       } else if ((cfg.qmode==0) && (files>1)) {
+                       } else if ((cfg.qmode==0 || cfg.qmode==8) && (files>1)) {
 
+                               if (cfg.qmode==0) {
+                                       printf("\n                      rx      /      tx      /     total    /   estimated\n");
+                               } else {
+                                       printf("<vnstat version=\"%s\" xmlversion=\"%d\">\n", VNSTATVERSION, XMLVERSION);
+                               }
                                dir=opendir(dirname);
-                               printf("\n                     rx      /     tx      /    total    /  estimated\n");
                                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);
-                                               readdb(interface, dirname);
-                                               if (!newdb)
-                                                       showdb(5);
-                                               
+                                               newdb=readdb(interface, dirname);
+                                               if (!newdb) {
+                                                       if (cfg.qmode==0) {
+                                                               showdb(5);
+                                                       } else {
+                                                               showxml();
+                                                       }
+                                               }
                                        }
                                }
                                closedir(dir);
+                               if (cfg.qmode==8) {
+                                       printf("</vnstat>\n");
+                               }
                                
                        /* show in qmode if there's only one file or qmode!=0 */
                        } else {
-                               readdb(definterface, dirname);
+                               newdb=readdb(definterface, dirname);
                                if (!newdb) {
                                        if (cfg.qmode==5)
                                                printf("\n                     rx      /     tx      /    total    /  estimated\n");
-                                       showdb(cfg.qmode);
+                                       if (cfg.qmode!=8) {
+                                               showdb(cfg.qmode);
+                                       } else {
+                                               printf("<vnstat version=\"%s\" xmlversion=\"%d\">\n", VNSTATVERSION, XMLVERSION);
+                                               showxml();
+                                               printf("</vnstat>\n");
+                                       }
                                }
                        }
                
                /* show only specified file */
                } else {
-                       readdb(interface, dirname);
+                       newdb=readdb(interface, dirname);
                        if (!newdb) {
                                if (cfg.qmode==5)
                                        printf("\n                     rx      /     tx      /    total    /  estimated\n");
-                               showdb(cfg.qmode);
+                               if (cfg.qmode!=8) {
+                                       showdb(cfg.qmode);
+                               } else {
+                                       printf("<vnstat version=\"%s\" xmlversion=\"%d\">\n", VNSTATVERSION, XMLVERSION);
+                                       showxml();
+                                       printf("</vnstat>\n");
+                               }
                        }
                }
        }
@@ -522,11 +587,13 @@ int main(int argc, char *argv[]) {
                
                /* 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. A list\n");
-                       printf("of available interfaces can be seen with the 'ifconfig' command.\n");
+                       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");
                }
@@ -534,3 +601,20 @@ int main(int argc, char *argv[]) {
        
        return 0;
 }
+
+
+int synccounters(char iface[32], char dirname[512])
+{
+       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;
+}
index 6c576ceee82ed7f1a3da86698492b588c6c33891..2b430bf568fa1a5d7301ece29adbb270c71bbe31 100644 (file)
@@ -1,163 +1,6 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <unistd.h>
-#include <locale.h>
-#include <time.h>
-#include <string.h>
-#include <sys/types.h>
-#include <dirent.h>
-#include <sys/vfs.h>
-#include <ctype.h>
-#include <signal.h>
-#include <math.h>
-#include <errno.h>
-#include <sys/file.h>
+#ifndef VNSTAT_H
+#define VNSTAT_H
 
-/*
+int synccounters(char iface[32], char dirname[512]);
 
-Note! These are only the default values for settings
-and most can be changed later from the config file.
-
-*/
-
-/* location of the database directory */
-#define DATABASEDIR "/var/lib/vnstat"
-
-/* on which day should months change */
-#define MONTHROTATE 1
-
-/* date output formats for -d, -m, -t and -w */
-/* see 'man date' for control codes */
-#define DFORMAT "%d.%m."
-#define MFORMAT "%b '%y"
-#define TFORMAT "%d.%m.%y"
-
-/* characters used for visuals */
-#define RXCHAR "%"
-#define TXCHAR ":"
-#define RXHOURCHAR "r"
-#define TXHOURCHAR "t"
-
-/* default interface */
-#define DEFIFACE "eth0"
-
-/* default locale */
-#define LOCALE "en_US"
-
-/* 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 */
-#define DEFQMODE 0
-
-/* how much the boot time can variate between updates (seconds) */
-#define BVAR 15
-
-/* use file locking by default */
-#define USEFLOCK 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.6"
-
-/* integer limits */
-#define FP32 4294967295ULL
-#define FP64 18446744073709551615ULL
-
-/* sampletime in seconds for live traffic */
-/* don't use values below 2 */
-#define LIVETIME 2
-
-/* internal config structure */
-typedef struct {
-       int bvar;
-       int qmode;
-       int sampletime;
-       int monthrotate;
-       int maxbw;
-       int flock;
-       char dformat[64], mformat[64], tformat[64];
-       char iface[32];
-       char locale[32];
-       char dbdir[512];
-       char rxchar[2], txchar[2], rxhourchar[2], txhourchar[2];
-} 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 ibw {
-       char interface[32];
-       int limit;
-       struct ibw *next;
-} ibwnode;
-
-/* global variables */
-DATA data;
-CFG cfg;
-IFINFO ifinfo;
-ibwnode *ifacebw;
-int debug;
-int intsignal;
+#endif
diff --git a/src/vnstatd.c b/src/vnstatd.c
new file mode 100644 (file)
index 0000000..6770f2a
--- /dev/null
@@ -0,0 +1,446 @@
+/*
+vnStat daemon - Copyright (c) 2008-09 Teemu Toivola <tst@iki.fi>
+
+   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;
+       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;
+       updateinterval = 20;
+
+       /* early check for debug and config parameter */
+       if (argc > 1) {
+               for (currentarg=1; currentarg<argc; currentarg++) {
+                       if ((strcmp(argv[currentarg],"-D")==0) || (strcmp(argv[currentarg],"--debug")==0)) {
+                               debug = 1;
+                       } else if (strcmp(argv[currentarg],"--config")==0) {
+                               if (currentarg+1<argc) {
+                                       strncpy(cfgfile, argv[currentarg+1], 512);
+                                       if (debug)
+                                               printf("Used config file: %s\n", cfgfile);
+                                       currentarg++;
+                                       continue;
+                               } else {
+                                       printf("Error: File for --config missing.\n");
+                                       return 1;
+                               }
+                       }
+               }
+       }
+       
+       /* load config if available */
+       if (!loadcfg(cfgfile)) {
+               return 1;
+       }
+
+       setlocale(LC_ALL, cfg.locale);
+
+       /* init dirname and other config settings */
+       strncpy(dirname, cfg.dbdir, 512);
+       updateinterval = cfg.updateinterval;
+
+       /* parse parameters, maybe not the best way but... */
+       for (currentarg=1; currentarg<argc; currentarg++) {
+               if (debug)
+                       printf("arg %d: \"%s\"\n",currentarg,argv[currentarg]);
+               if ((strcmp(argv[currentarg],"-?")==0) || (strcmp(argv[currentarg],"--help")==0)) {
+                       break;
+               } else if (strcmp(argv[currentarg],"--config")==0) {
+                       /* config has already been parsed earlier so not but to do here */
+                       currentarg++;
+                       continue;
+               } else if ((strcmp(argv[currentarg],"-D")==0) || (strcmp(argv[currentarg],"--debug")==0)) {
+                       debug=1;
+               } else if ((strcmp(argv[currentarg],"-d")==0) || (strcmp(argv[currentarg],"--daemon")==0)) {
+                       rundaemon = 1;
+                       showhelp = 0;
+               } else if ((strcmp(argv[currentarg],"-n")==0) || (strcmp(argv[currentarg],"--nodaemon")==0)) {
+                       showhelp = 0;
+               } else if ((strcmp(argv[currentarg],"-v")==0) || (strcmp(argv[currentarg],"--version")==0)) {
+                       printf("vnStat daemon %s by Teemu Toivola <tst at iki dot fi>\n", VNSTATVERSION);
+                       return 0;
+               } else if ((strcmp(argv[currentarg],"-p")==0) || (strcmp(argv[currentarg],"--pidfile")==0)) {
+                       if (currentarg+1<argc) {
+                               strncpy(cfg.pidfile, argv[currentarg+1], 512);
+                               if (debug)
+                                       printf("Used pid file: %s\n", cfg.pidfile);
+                               currentarg++;
+                               continue;
+                       } else {
+                               printf("Error: File for --pidfile missing.\n");
+                               return 1;
+                       }
+               } else {
+                       printf("Unknown arg \"%s\". Use --help for help.\n",argv[currentarg]);
+                       return 1;
+               }
+       }
+
+       /* show help if nothing else was asked to be done */
+       if (showhelp) {
+               printf(" vnStat daemon %s by Teemu Toivola <tst at iki dot fi>\n\n", VNSTATVERSION);
+               printf("         -d, --daemon         fork process to background\n");
+               printf("         -n, --nodaemon       stay in foreground attached to the terminal\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("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) {
+                       printf("Zero database found, exiting.\n");
+                       return 1;
+               }
+       }
+
+       /* 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);
+               }
+
+               if ((current-prevdbupdate)>=updateinterval) {
+               
+                       updateinterval = cfg.updateinterval;
+
+                       if (debug) {
+                               datashow();
+                       }
+
+                       /* read database list if cache is empty */
+                       if (datacount()==0) {
+               
+                               if ((dir=opendir(dirname))!=NULL) {
+                                       dbcount = 0;
+                                       while ((di=readdir(dir))) {
+                                               if (di->d_name[0]!='.') {
+                                                       if (debug)
+                                                               printf("\nProcessing file \"%s/%s\"...\n", dirname, di->d_name);
+                                                       
+                                                       if (!dataadd(di->d_name)) {
+                                                               snprintf(errorstring, 512, "Cache memory allocation failed, exiting.");
+                                                               printe(PT_Error);
+                                                               return 1;
+                                                       }
+                                                       
+                                                       dbcount++;
+                                               }
+                                       }
+
+                                       /* disable update interval check for one loop if database list was refreshed */
+                                       /* otherwise rise default update interval since there's nothing else to do */
+                                       if (dbcount) {
+                                               updateinterval = 0;
+                                               intsignal = 42;
+                                               prevdbsave = current;
+                                       } else {
+                                               updateinterval = 120;
+                                       }
+
+                                       closedir(dir);
+                               
+                               } else {
+                                       snprintf(errorstring, 512, "Unable to access database directory \"%s\", exiting.", dirname);
+                                       printe(PT_Error);
+                                       return 1;
+                               }
+
+                       /* update data cache */
+                       } else {
+                               prevdbupdate = current;
+                               datalist = dataptr;
+                       
+                               if ((current-prevdbsave)>=(cfg.saveinterval*60)) {
+                                       dodbsave = 1;
+                                       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 (dataget(datalist->data.interface)==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)) {
+                                                       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;
+                                               dataupdate();
+                                       } 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);
+                                                       return 1;
+                                               } else {
+                                                       datalist = datalist->next;
+                                                       continue;
+                                               }
+                                       }
+
+                                       /* write data to file if now is the time for it */
+                                       if (dodbsave) {
+                                               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 {
+                                                       /* show freespace error only once */
+                                                       if (dbsaved) {
+                                                               snprintf(errorstring, 512, "Unable to write database, continuing with cached data.");
+                                                               printe(PT_Error);
+                                                               dbsaved = 0;
+                                                       }
+                                               }
+                                       }
+               
+                                       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);
+                                       dataflush(dirname);
+                                       if (loadcfg(cfgfile)) {
+                                               setlocale(LC_ALL, cfg.locale);
+                                               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 */
+
+       dataflush(dirname);
+
+       /* 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 = fork();
+       
+       if (i<0) { /* fork error */
+               perror("fork");
+               exit(1); 
+       }
+       if (i>0) { /* parent exits */
+               exit(0);
+       }
+       /* 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(1);
+               }
+       }
+
+       /* 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(1); /* can't open */
+       }
+       if (lockf(pidfile,F_TLOCK,0)<0) {
+               perror("pidfile lock");
+               snprintf(errorstring, 512, "pidfile lock failed, exiting.");
+               printe(PT_Error);
+               exit(1); /* 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 */
+       dup(i); /* stdout */
+       dup(i); /* stderr */
+       
+       umask(027); /* set newly created file permissions */
+       chdir("/"); /* change running directory */
+
+       /* first instance continues */
+       sprintf(str,"%d\n",getpid());
+       write(pidfile,str,strlen(str)); /* record pid to lockfile */
+
+       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.", getpid());
+               printe(PT_Info);
+       }
+}
diff --git a/src/vnstatd.h b/src/vnstatd.h
new file mode 100644 (file)
index 0000000..0f48850
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef VNSTATD_H
+#define VNSTATD_H
+
+void daemonize(void);
+
+#endif
diff --git a/src/vnstati.c b/src/vnstati.c
new file mode 100644 (file)
index 0000000..c497217
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+vnStat image output - Copyright (c) 2007-09 Teemu Toivola <tst@iki.fi>
+
+   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 "vnstati.h"
+
+int main(int argc, char *argv[])
+{
+       FILE *pngout;
+       int currentarg, cache=0, help=0, showheader=1, showedge=1, showrate=0;
+       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; currentarg<argc; currentarg++) {
+                       if ((strcmp(argv[currentarg],"-D")==0) || (strcmp(argv[currentarg],"--debug")==0)) {
+                               debug = 1;
+                       } else if (strcmp(argv[currentarg],"--config")==0) {
+                               if (currentarg+1<argc) {
+                                       strncpy(cfgfile, argv[currentarg+1], 512);
+                                       if (debug)
+                                               printf("Used config file: %s\n", cfgfile);
+                                       currentarg++;
+                                       continue;
+                               } else {
+                                       printf("Error: File for --config missing.\n");
+                                       return 1;
+                               }
+                       }
+               }
+       }
+       
+       /* load config if available */
+       if (!loadcfg(cfgfile)) {
+               return 1;
+       }
+       cfg.qmode = 0;
+
+       setlocale(LC_ALL, cfg.locale);
+       strncpy(interface, cfg.iface, 32);
+       strncpy(dirname, cfg.dbdir, 512);
+       filename[0] = '\0';
+       current = time(NULL);
+
+       /* parse parameters */
+       for (currentarg=1; currentarg<argc; currentarg++) {
+               if (debug)
+                       printf("arg %d: \"%s\"\n",currentarg,argv[currentarg]);
+               if ((strcmp(argv[currentarg],"-?")==0) || (strcmp(argv[currentarg],"--help"))==0) {
+                       help = 1;
+               } else if ((strcmp(argv[currentarg],"-i")==0) || (strcmp(argv[currentarg],"--iface"))==0) {
+                       if (currentarg+1<argc) {
+                               strncpy(interface, argv[currentarg+1], 32);
+                               if (debug)
+                                       printf("Used interface: \"%s\"\n", filename);
+                               currentarg++;
+                               continue;
+                       } else {
+                               printf("Error: Interface for -i missing.\n");
+                               return 1;
+                       }
+               } else if ((strcmp(argv[currentarg],"-o")==0) || (strcmp(argv[currentarg],"--output"))==0) {
+                       if (currentarg+1<argc) {
+                               strncpy(filename, argv[currentarg+1], 512);
+                               if (debug)
+                                       printf("Output file: \"%s\"\n", filename);
+                               currentarg++;
+                               continue;
+                       } else {
+                               printf("Error: Filename for -o missing.\n");
+                               return 1;
+                       }                       
+               } else if ((strcmp(argv[currentarg],"-c")==0) || (strcmp(argv[currentarg],"--cache"))==0) {
+                       if (currentarg+1<argc && isdigit(argv[currentarg+1][0])) {
+                               cache = atoi(argv[currentarg+1]);
+                               if (debug)
+                                       printf("Cache time: %d minutes\n", cache);
+                               currentarg++;
+                               continue;
+                       } else {
+                               printf("Error: Parameter for -c missing or invalid.\n");
+                               return 1;
+                       }
+               } else if (strcmp(argv[currentarg],"--config")==0) {
+                       /* config has already been parsed earlier so not but to do here*/
+                       currentarg++;
+                       continue;                               
+               } else if ((strcmp(argv[currentarg],"-D")==0) || (strcmp(argv[currentarg],"--debug"))==0) {
+                       debug = 1;
+               } else if ((strcmp(argv[currentarg],"-d")==0) || (strcmp(argv[currentarg],"--days"))==0) {
+                       cfg.qmode = 1;
+               } else if ((strcmp(argv[currentarg],"-m")==0) || (strcmp(argv[currentarg],"--months"))==0) {
+                       cfg.qmode = 2;
+               } else if ((strcmp(argv[currentarg],"-t")==0) || (strcmp(argv[currentarg],"--top10"))==0) {
+                       cfg.qmode = 3;
+               } else if ((strcmp(argv[currentarg],"-s")==0) || (strcmp(argv[currentarg],"--summary"))==0) {
+                       cfg.qmode = 5;
+               } else if ((strcmp(argv[currentarg],"-h")==0) || (strcmp(argv[currentarg],"--hours"))==0) {
+                       cfg.qmode = 7;
+               } else if ((strcmp(argv[currentarg],"-hs")==0) || (strcmp(argv[currentarg],"--hsummary"))==0) {
+                       cfg.qmode = 51;
+               } else if ((strcmp(argv[currentarg],"-vs")==0) || (strcmp(argv[currentarg],"--vsummary"))==0) {
+                       cfg.qmode = 52;
+               } else if ((strcmp(argv[currentarg],"-nh")==0) || (strcmp(argv[currentarg],"--noheader"))==0) {
+                       showheader = 0;
+               } else if ((strcmp(argv[currentarg],"-ne")==0) || (strcmp(argv[currentarg],"--noedge"))==0) {
+                       showedge = 0;
+               } else if ((strcmp(argv[currentarg],"-r")==0) || (strcmp(argv[currentarg],"--rate"))==0) {
+                       showrate = 1;
+               } else if ((strcmp(argv[currentarg],"-v")==0) || (strcmp(argv[currentarg],"--version"))==0) {
+                       printf("vnStat image output %s by Teemu Toivola <tst at iki dot fi>\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 <tst at iki dot fi>\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("         -r,  --rate              show hours with rate\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("         --config                 select used config file\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 */
+       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:
+                       drawsummary(0, showheader, showedge, showrate);
+                       break;
+               case 51:
+                       drawsummary(1, showheader, showedge, showrate);
+                       break;
+               case 52:
+                       drawsummary(2, showheader, showedge, showrate);
+                       break;
+               case 7:
+                       drawhourly(showheader, showedge, showrate);
+                       break;
+               default:
+                       break;
+       }
+
+       /* write image */
+       gdImagePng(im, pngout);
+       fclose(pngout);
+       gdImageDestroy(im);
+
+       if (debug)
+               printf("all done\n");
+
+       return 0;
+}
diff --git a/src/vnstati.h b/src/vnstati.h
new file mode 100644 (file)
index 0000000..2d155a5
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef VNSTATI_H
+#define VNSTATI_H
+
+#include <sys/stat.h>  /* fstat() */
+#include <gd.h>        /* libgd2-dev libgd2 */
+#include <gdfontt.h>   /* gdFontGetTiny() */
+#include <gdfonts.h>   /* gdFontGetSmall() */
+#include <gdfontmb.h>  /* gdFontGetMediumBold() */
+#include <gdfontl.h>   /* gdFontGetLarge() */
+#include <gdfontg.h>   /* gdFontGetGiant() */
+
+#define YBEGINOFFSET -1
+#define YENDOFFSET 6
+
+/* global variables for vnstati */
+gdImagePtr im;
+int cbackground, cedge, cheader, cheadertitle, cheaderdate, ctext, cline, clinel, cvnstat;
+int crx, crxd, ctx, ctxd;
+time_t current;
+
+#endif