I have written a daemon for my own amusement. You can try it out.
the log file /var/log/bwmon.[ifn] can be used to find the b/w usage across any time range.
Code:
/*
* Persistant net bandwidth usage monitor daemon
*
* Copyright: ephemera @ daemonforums.org
*/
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#ifdef BSD
# include <sys/socket.h>
# include <ifaddrs.h>
#endif /* BSD */
#ifdef SOLARIS
# include <kstat.h>
#endif /* SOLARIS */
#include <net/if.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#ifdef LINUX
# include <regex.h>
#endif /* LINUX */
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <syslog.h>
#include <time.h>
#include <unistd.h>
#define ARGV0 "bwmond"
typedef unsigned long long Bwbyte_t;
static char LOGFILE[PATH_MAX] = "/var/log/"ARGV0".";
static char RECFILE[PATH_MAX] = "/var/spool/"ARGV0".";
static char interface[IFNAMSIZ];
static void usage(void);
static int update(Bwbyte_t *, Bwbyte_t *);
static void logpr(int, char *, ...);
static void err_sys(int, char *, char *);
static void sig_term(int);
int
main(int argc, char *argv[])
{
int i, recfd, logfd;
time_t t, t2;
Bwbyte_t u, d, last_up, last_down, hr_up, hr_down;
Bwbyte_t *record;
enum {UP, DOWN};
int record_len = 2 * sizeof(*record);
const int interval = 60;
int log_interval = 60 * 5;
pid_t pid;
sigset_t sigset;
struct sigaction sigact;
switch (--argc) {
case 2:
log_interval = 60 * strtoul(*++argv, (char **)NULL, 10);
if (log_interval <= 0)
usage();
/* FALLTHROUGH */
case 1:
strncpy(interface, *++argv, sizeof(interface));
interface[sizeof(interface) - 1] = '\0';
strncat(RECFILE, interface, sizeof(interface) - 1);
strncat(LOGFILE, interface, sizeof(interface) - 1);
break;
default:
usage();
}
if ( (pid = fork()) < 0)
err_sys(1, "fork", "");
if (pid > 0)
exit(0);
setsid();
chdir("/");
umask(0);
for (i = getdtablesize(); i >= 0; --i)
close(i);
i = open("/dev/null", O_RDWR);
dup2(i, STDIN_FILENO);
dup2(i, STDOUT_FILENO);
dup2(i, STDERR_FILENO);
sigfillset(&sigset);
sigdelset(&sigset, SIGTERM);
sigprocmask(SIG_BLOCK, &sigset, NULL);
sigact.sa_handler = sig_term;
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = 0;
sigaction(SIGTERM, &sigact, NULL);
openlog(ARGV0, LOG_PID, LOG_DAEMON);
syslog(LOG_INFO, "Started on %s.", interface);
if (access(LOGFILE, F_OK) != 0)
syslog(LOG_NOTICE, "Creating Logfile: %s. "
"If this is not the first time that "ARGV0" is being run then this indicates a Problem."
, LOGFILE);
logfd = open(LOGFILE, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
if (logfd < 0)
err_sys(1, "open", LOGFILE);
if (access(RECFILE, F_OK) != 0) {
recfd = open(RECFILE
,O_RDWR | O_CREAT | O_SYNC
,S_IRUSR | S_IWUSR);
if (recfd >= 0) {
syslog(LOG_NOTICE, "Creating Recordfile: %s. "
"If this is not the first time that "ARGV0" is being run then this indicates a Problem."
, RECFILE);
if (lseek(recfd, record_len, SEEK_SET) < 0)
err_sys(1, "lseek", RECFILE);
if (write(recfd, "", 1) < 1)
err_sys(1, "write", RECFILE);
}
} else {
recfd = open(RECFILE, O_RDWR | O_SYNC);
}
if (recfd < 0)
err_sys(1, "open", RECFILE);
record = (Bwbyte_t *) mmap(NULL
,record_len
,PROT_READ | PROT_WRITE
,MAP_SHARED
,recfd
,0);
if ( (caddr_t)record == (caddr_t)-1)
err_sys(1, "mmap", RECFILE);
close(recfd);
u = d = last_up = last_down = hr_up = hr_down = 0;
t = time(NULL);
while (!update(&hr_up, &hr_down) || !update(&last_up, &last_down))
sleep(interval);
for ( ; ; ) {
if (update(&u, &d)) {
if (u < last_up || d < last_down) {
last_up = last_down = hr_up = hr_down = 0;
continue;
}
record[UP] += u - last_up;
record[DOWN] += d - last_down;
last_up = u;
last_down = d;
t2 = time(NULL);
if (t2 - t >= log_interval) {
logpr(logfd
,"%6llu kb Up %6llu kb Dn %10llu / %llu\n"
,(last_up - hr_up) / 1024
,(last_down - hr_down) / 1024
,record[UP] / 1024
,record[DOWN] / 1024);
t = t2;
hr_up = last_up;
hr_down = last_down;
}
msync((void *)record, record_len, MS_SYNC);
}
sleep(interval);
}
/* NOTREACHED */
}
static void
err_sys(int quit, char *fn, char *arg)
{
if (!*arg)
syslog(LOG_ERR, "Error: %s: %m\n", fn);
else
syslog(LOG_ERR, "Error: %s: %s: %m\n", fn, arg);
if (quit) {
syslog(LOG_ERR, "Terminated due to error on %s.", interface);
closelog();
exit(quit);
}
}
static int
update(Bwbyte_t *up, Bwbyte_t *down)
{
int updated = 0;
errno = 0;
#ifdef BSD
struct ifaddrs *ifa, *f;
if (getifaddrs(&ifa) == 0) {
for (f = ifa; f; f = f->ifa_next) {
if (strcmp(interface, f->ifa_name) == 0 &&
f->ifa_addr->sa_family == AF_LINK) {
*down = ((struct if_data*)f->ifa_data)->ifi_ibytes;
*up =((struct if_data*)f->ifa_data)->ifi_obytes;
updated = 1;
break;
}
}
freeifaddrs(ifa);
}
#endif /* BSD */
#ifdef LINUX
char line[2048];
static char repatrn[128] = "";
regmatch_t rematch[3];
static regex_t regex;
FILE *pfd;
if (!*repatrn) {
snprintf(repatrn, sizeof(repatrn)
,"^ *%s: *([0-9]+) +[0-9]+ +[0-9]+ +[0-9]+ +[0-9]+"
" +[0-9]+ +[0-9]+ +[0-9]+ +([0-9]+) "
,interface);
if (regcomp(®ex, repatrn, REG_EXTENDED) != 0)
repatrn[0] = 0;
}
if (*repatrn && (pfd = fopen("/proc/net/dev", "r")) != NULL) {
while(fgets(line, sizeof(line), pfd) != NULL) {
if (regexec(®ex, line, 3, rematch, 0) == 0) {
*down = atoll(line + rematch[1].rm_so);
*up = atoll(line + rematch[2].rm_so);
updated = 1;
break;
}
}
fclose(pfd);
}
#endif /* LINUX */
#ifdef SOLARIS
kstat_ctl_t *kc;
kstat_t *ksp;
kstat_named_t *knp;
if ( (kc = kstat_open()) != NULL) {
/* workaround? kstat_open sets errno even on success */
errno = 0;
for (ksp = kc->kc_chain; ksp != NULL; ksp = ksp->ks_next) {
if (strcmp(ksp->ks_class, "net") == 0 &&
strcmp(ksp->ks_name, interface) == 0 &&
kstat_read(kc, ksp, NULL) != -1) {
if ((knp=kstat_data_lookup(ksp,"rbytes64")) != NULL)
*down = knp->value.ui64;
if ((knp=kstat_data_lookup(ksp,"obytes64")) != NULL) {
*up = knp->value.ui64;
updated = 1;
break;
}
}
}
kstat_close(kc);
}
#endif /* SOLARIS */
if (errno)
err_sys(0, "update", "");
return updated;
}
static void
logpr(int logfd, char *msg, ...)
{
#define LOG_LINE_MAX 256
char logent[LOG_LINE_MAX];
size_t r;
time_t clk;
va_list ap;
va_start(ap, msg);
clk = time(NULL);
r = strftime(logent, sizeof(logent)
,"%b %e %H:%M:%S %Y: "
,localtime(&clk));
vsnprintf(logent + r, sizeof(logent) - r, msg, ap);
if (write(logfd, logent, strlen(logent)) < 0)
err_sys(0, "logpr", "");
va_end(ap);
}
static void
usage(void)
{
fprintf(stderr, "Usage: %s [log interval] interface\n",
ARGV0);
exit(1);
}
static void
sig_term(int signo)
{
syslog(LOG_INFO, "Stopped on %s.", interface);
closelog();
exit(0);
}
you might want to run it from rc.local at startup. Example: