|
Home - Old Man Programmer
| Displaying projects/sac/sac.c
/* $Copyright: $
* Copyright (c) 1995 - 2001 by Steve Baker (ice@mama.indstate.edu)
* All Rights reserved
*
* This software is provided as is without any express or implied
* warranties, including, without limitation, the implied warranties
* of merchant-ability and fitness for a particular purpose.
*/
#define MAIN
#include "sac.h"
static char *version = "$Version: $ sac v1.9 (c) 1995 - 2001 by Steve Baker $";
/*
* sac [-w wtmplist|-] [-acdDfFhilmoprtSUX[3|4]] [-b hour[:min[:sec]]]
* [-s start] [-e end] [[-u] userlist] [-x [userlist]] [-T [ttylist]]
* [-H [hostlist]] [-M hourrangespec[,hourrangspec[,...]]]
* [-R [portmasterlist]] [-I hours[:min[:sec]]] [--seconds] [--hms]
* [--hm] [--hours] [--round] [--longdate] [--help] [--cutoff H[:M[:S]]]
* [--discard H[:M[:S]]] [--verbose] [--version]
*/
int main(int argc, char **argv)
{
struct exc *xp;
struct usr *u, *cp, *pp;
struct file *f, *files = NULL, *l, *p;
char *start = NULL, *end = NULL;
char listtype = USERLIST;
int i, j, n, fd=0;
time_t t;
#ifdef USER_PROCESS
void (*gronk)() = gronk_sysv;
#else
void (*gronk)() = gronk_bsd;
#endif
char fastseek = FALSE;
char ft= ACCT_WTMP;
for(n=i=1;i<argc;i=n) {
n++;
if (argv[i][0] == '-' && argv[i][1]) {
for(j=1;argv[i][j];j++) {
switch (argv[i][j]) {
case 'w':
listtype = FILELIST;
break;
case 'p':
type = USER | (type & 0xFFF0);
break;
case 'd':
type = DAY | (type & 0xFFF0);
break;
case 't':
type = TTY | (type & 0xFFF0);
break;
case 'r':
type = RAW | (type & 0xFFF0) | HOUR;
break;
case 'U':
type = SIMUSE | (type & 0xFFF0);
/*
if (argv[n] == NULL) usage(10);
usespec = argv[n++];
*/
break;
case 'a':
type |= AVERAGE;
break;
case 'c':
type |= CLIP;
break;
case 'h':
type |= HOUR;
break;
case 'm':
type |= MINMAX;
break;
case 'l':
type |= LOGIN;
break;
case 'i':
hosttoo |= TRUE;
break;
case 'P':
type |= IPACCT;
break;
case 'C':
type |= CALLID;
break;
case 'I':
type |= IGNORE;
ignore = argv[n++];
break;
case 'F':
gronk = gronk_ftp;
ft = ACCT_WTMP;
break;
case 'f':
gronk = gronk_both;
ft = ACCT_WTMP;
break;
case 'R':
gronk = gronk_radius;
listtype = PORTMASTERLIST;
ft = ACCT_RADIUS;
break;
case 'L':
gronk = gronk_radiuslogfile;
listtype = LOGFILELIST;
ft = ACCT_RADIUS_LOGFILE;
break;
case 'o':
gronk = gronk_bsd;
ft = ACCT_ANCIENT;
break;
case 'X':
gronk = gronk_tacacs3;
ft = ACCT_TACACS3;
if (argv[i][j+1] == '4') {
gronk = gronk_tacacs4;
ft = ACCT_TACACS4;
j++;
} else if (argv[i][j+1] == '3') {
j++;
}
break;
case 'b':
if (argv[n] == NULL) usage(7);
back = argv[n++];
break;
case 's':
if (argv[n] == NULL) usage(7);
start = argv[n++];
break;
case 'e':
if (argv[n] == NULL) usage(7);
end = argv[n++];
break;
case 'S':
fastseek = TRUE;
break;
case 'D':
radhost = TRUE;
break;
case 'x':
listtype = EXCLUDELIST;
break;
case 'T':
listtype = TTYLIST;
break;
case 'u':
listtype = USERLIST;
break;
case 'H':
listtype = HOSTLIST;
break;
case 'M':
if (argv[n] == NULL) usage(7);
hourmask |= hourrangespec(argv[n++]);
break;
case '-':
if (j == 1) {
if (!strcmp("--help",argv[i])) usage(4);
if (!strcmp("--version",argv[i])) usage(9);
if (!strcmp("--hms",argv[i])) {
j = strlen(argv[i])-1;
timefmt = HMS;
break;
}
if (!strcmp("--hm",argv[i])) {
j = strlen(argv[i])-1;
timefmt = HM;
break;
}
if (!strcmp("--hours",argv[i])) {
j = strlen(argv[i])-1;
timefmt = HOURS;
break;
}
if (!strcmp("--seconds",argv[i])) {
j = strlen(argv[i])-1;
timefmt = SECONDS;
break;
}
if (!strcmp("--round",argv[i])) {
j = strlen(argv[i])-1;
timefmt |= ROUND;
break;
}
if (!strcmp("--verbose",argv[i])) {
j = strlen(argv[i])-1;
verbose = TRUE;
break;
}
if (!strcmp("--longdate",argv[i])) {
j = strlen(argv[i])-1;
longdate = TRUE;
break;
}
if (!strcmp("--cutoff",argv[i])) {
j = strlen(argv[i])-1;
if (argv[n] == NULL) usage(7);
cutoff = churn_time(argv[n++]);
break;
}
if (!strcmp("--discard",argv[i])) {
j = strlen(argv[i])-1;
if (argv[n] == NULL) usage(7);
discard = churn_time(argv[n++]);
break;
}
}
default:
usage(1);
}
}
} else {
switch(listtype) {
case USERLIST:
adduser(&us,argv[i]);
fix = TRUE;
break;
case EXCLUDELIST:
addexclude(argv[i]);
exclude = TRUE;
break;
case TTYLIST:
addtty(argv[i]);
fixtty = TRUE;
break;
case HOSTLIST:
addhost(argv[i]);
fixhost = TRUE;
break;
case PORTMASTERLIST:
case FILELIST:
f = bmalloc(sizeof(struct file));
if (listtype == FILELIST) f->file = scopy(argv[i]);
else {
if (ispattern(argv[i])) {
free(f);
while ((f = parseradiusdir(RADIUS_DIR,argv[i],DETAIL_NAME))) {
f->gronk = gronk;
f->ftype = ft;
f->nxt = files;
files = f;
}
break;
}
sprintf(buf,"%s/%s/%s",RADIUS_DIR,argv[i],DETAIL_NAME);
f->file = scopy(buf);
}
f->gronk = gronk;
f->ftype = ft;
f->nxt = files;
files = f;
break;
case LOGFILELIST:
f = bmalloc(sizeof(struct file));
f->file = scopy(argv[i]);
f->gronk = gronk;
f->ftype = ft;
if (files == NULL) files = f;
else {
for(l=p=files;p;p++) {
if (strncmp(p->file,"lastlog",7)) break;
if (strncmp(f->file,p->file,14) < 0) break;
l=p;
}
f->nxt = p;
if (p == files) files = f;
else l->nxt = f;
}
break;
}
}
}
for(xp=ex;xp;xp=xp->nxt) {
while ((u = finduser(us,xp->user))) {
for(pp=cp=us;cp && cp != u;cp=cp->nxt) pp = cp;
if (!cp) break;
if (us == cp) us = u->nxt;
else pp->nxt = u->nxt;
free(u);
}
}
if (start) {
if (start[0] == '-' || start[0] == '+') {
for(i=1;start[i];i++) if (!isdigit(start[i])) usage(2);
sm = atoi(start);
if (sm == 0) {
if (start[0] == '-') sm = MINUSZERO;
else sm = PLUSZERO;
}
} else sd = getsacdate(start);
}
if (end) {
if (end[0] == '-' || end[0] == '+') {
t = time(0);
for(i=1;end[i];i++) if (!isdigit(end[i])) usage(2);
em = atoi(end);
if (em == 0) {
if (end[0] == '-') em = MINUSZERO;
else em = PLUSZERO;
}
} else ed = getsacdate(end);
}
if (back) backtime = (curtime=time(0)) - churn_time(back);
if (ignore) {
ignoretime = churn_time(ignore);
for(u=us;u;u=u->nxt)
u->ignore = ignoretime;
}
if (files == NULL) {
files = bmalloc(sizeof(struct file));
files->file = scopy(_PATH_WTMP);
files->gronk = gronk;
}
for(f=files;f;f=f->nxt) {
if (!strcmp(f->file,"-")) fd = 0;
else {
if ((fd = open(f->file,O_RDONLY)) < 0) {
fprintf(stderr,"sac: Error opening '%s' for reading. [%d]\n",f->file,errno);
continue;
}
}
if (fastseek) try_fastseek(fd,f);
f->gronk(fd);
cleanup();
if (fd != 0) close(fd);
}
report();
return 0;
}
void usage(int n)
{
switch (n) {
case 1:
fprintf(stderr,"usage: sac [-w [wtmp-list|-]] [-acdDfFhilmoprtSUX[3|4]] [-s start] [-e end]\n [-b H[:M[:S]]] [[-u] userlist] [-x [userlist]] [-T [ttylist]]\n [-H [hostlist]] [-M hour-range-spec[,hour-range-spec[,...]]]\n [-R [portmaster-list]] [--hms] [--hm] [--hours] [--round]\n [--version] [--help]\n");
break;
case 2:
fprintf(stderr,"sac: Invalid date. Format: +days | -days | mm/dd/yy\n");
break;
case 3:
fprintf(stderr,"sac: Invalid time. Format: hours[:minutes[:seconds]]\n");
break;
case 4:
fprintf(stderr,"usage: sac [-w [wtmp-list|-]] [-acdDfFhilmoprtSUX[3|4]] [-s start] [-e end]\n [-b H[:M[:S]]] [[-u] userlist] [-x [userlist]] [-T [ttylist]]\n [-H [hostlist]] [-M hour-range-spec[,hour-range-spec[,...]]]\n [-R [portmaster-list]] [--hms] [--hm] [--hours] [--round]\n [--version] [--help]\n");
fprintf(stderr," -w [wtmp|-] Read alternate wtmp file(s).\n");
fprintf(stderr," -d Perform daily accounting.\n");
fprintf(stderr," -a Average usage / login.\n");
fprintf(stderr," -t Report usage by tty line.\n");
fprintf(stderr," -U List simultaneous usage levels.\n");
fprintf(stderr," -r Show raw statistics about accounting file(s).\n");
fprintf(stderr," -h Show hourly profile.\n");
fprintf(stderr," -l Show login listing.\n");
fprintf(stderr," -p Per user accounting.\n");
fprintf(stderr," -f Perform accounting for ftp logins too.\n");
fprintf(stderr," -F Perform accounting for ftp logins only.\n");
fprintf(stderr," -m Show min/max # of concurrent logins.\n");
fprintf(stderr," -c Perform login clipping.\n");
fprintf(stderr," -o Read old decrepit wtmp format.\n");
fprintf(stderr," -X[3] Read tacacs 3.4 - 3.5 wtmp format.\n");
fprintf(stderr," -X4 Read tacacs 4.x wtmp format.\n");
fprintf(stderr," -i Include hostname in determining login/logouts.\n");
fprintf(stderr," -R [pmstrs] Read Radius detail files for each portmaster listed.\n");
fprintf(stderr," -D Use @hostname in user@hostname instead of portmaster name.\n");
fprintf(stderr," -P Perform packet and octet accounting.\n");
fprintf(stderr," -s start Display accounting info from `start'.\n");
fprintf(stderr," -e end Display accounting info up to `end'.\n");
fprintf(stderr," -S Seek to start of day specified with -s (MM/DD/YY format).\n");
fprintf(stderr," -M hourspec Select only specific hours to perform accounting on.\n");
fprintf(stderr," -b H:M:S Show accounting info from the last few hours:minutes:seconds.\n");
fprintf(stderr," -I H:M:S Ignore usage before starting accounting (per user only).\n");
fprintf(stderr," -x [users] Does not perform accounting for [users].\n");
fprintf(stderr," -T [ttys] Perform accounting on only those ttys listed.\n");
fprintf(stderr," -H [hosts] Perform accounting only for the hosts listed.\n");
fprintf(stderr," -u [users] Perform accounting only for the users listed.\n");
fprintf(stderr," userlist Perform accounting only for the users listed.\n");
fprintf(stderr," --seconds Display time in seconds.\n");
fprintf(stderr," --hms Display time in hours:minutes:seconds format.\n");
fprintf(stderr," --hm Display time in hours:minutes format.\n");
fprintf(stderr," --hours Display time in hours only format.\n");
fprintf(stderr," --round Round time to nearest minute or hour instead of always down.\n");
fprintf(stderr," --longdate Display dates in long format.\n");
fprintf(stderr," --version Display sac version.\n");
fprintf(stderr," --help Display this help message.\n");
break;
case 5:
fprintf(stderr,"sac: Invalid hour range. Format: (0-23)[-(0-23)[,...]]\n");
break;
case 6:
fprintf(stderr,"sac: Malformed wildcard.\n");
break;
case 7:
fprintf(stderr,"sac: Missing argument.\n");
break;
case 8:
fprintf(stderr,"sac: Must specify at least one portmaster with -R option \n or specify stdin or detail file with -w.\n");
break;
case 9:
{
char *v = version+12;
printf("%.*s\n",(int)strlen(v)-1,v);
exit(0);
}
case 10:
fprintf(stderr,"sac: Invalid usage range. Format: [+|-]N[-[+|-]M]\n");
break;
}
exit(1);
}
struct file *parseradiusdir(char *base, char *pattern, char *file)
{
static DIR *dir = NULL;
struct dirent *ent;
struct file *f;
if (!dir) {
dir = opendir(base);
if (!dir) return NULL;
}
for(;;) {
ent = readdir(dir);
if (ent == NULL) {
closedir(dir);
dir = NULL;
return NULL;
}
if (patmatch(ent->d_name,pattern) != 1) continue;
sprintf(buf,"%s/%s/%s",RADIUS_DIR,ent->d_name,DETAIL_NAME);
if (access(buf,F_OK)) continue;
f = bmalloc(sizeof(struct file));
f->file = scopy(buf);
return f;
}
}
/*
* Turn "mm/dd/yy" into time in seconds.
*/
time_t getsacdate(char *s)
{
struct tm tm;
int y;
time_t t;
/*
* <puke>
* Need a real date parser that can handle different formats and separators
*/
if (!isdigit(s[0]) || !isdigit(s[1]) || isdigit(s[2])) usage(2);
if (!isdigit(s[3]) || !isdigit(s[4]) || isdigit(s[5])) usage(2);
if (!isdigit(s[6]) || !isdigit(s[7]) || s[8]) usage(2);
tm.tm_mon = (((s[0]-'0')*10) + (s[1]-'0')) -1;
tm.tm_mday = (((s[3]-'0')*10) + (s[4]-'0'));
y = (((s[6]-'0')*10) + (s[7]-'0'));
tm.tm_year = (y < 70? 100 + y : y);
tm.tm_isdst = -1;
tm.tm_hour = 0;
tm.tm_min = tm.tm_sec = 0;
t = mktime(&tm);
if (t == (time_t)(-1)) {
if (verbose) fprintf(stderr,"oops\n");
usage(2);
}
return t;
}
time_t churn_time(char *s)
{
time_t t = 0, mult=3600;
u_char nbuf[20], p;
for(p=0;*s;s++) {
if (*s == ':') {
nbuf[p] = (u_char)0;
t += atoi(nbuf) * mult;
p = 0;
if (mult > 1) mult /= 60;
else usage(3);
} else if (isdigit(*s)) {
if (p < 15) nbuf[p++] = (u_char)*s;
else usage(3);
} else usage(3);
}
nbuf[p] = (u_char)0;
t += atoi(nbuf) * mult;
return t;
}
/*
* hourrangespec = [0-23][-[0-23]][,...]
*/
u_long hourrangespec(char *s)
{
u_long mask = 0;
int i= 0, j = 0, p = 0;
while(1) {
if (!*s) usage(5);
for(i=0;isdigit(*s) && i < 24;s++) i = (i*10) + (*s-'0');
if (i > 23) usage(5);
if (*s == '-') {
s++;
if (!*s) usage(5);
for(j=0;isdigit(*s) && j < 24;s++) j = (j*10) + (*s-'0');
if (j > 23) usage(5);
for(p=i;p<=j;p++) mask |= (1<<p);
} else mask |= (1<<i);
if (!*s) return mask;
if (*s++ == ',') continue;
usage(5);
}
/* NOT REACHED */
return mask;
}
/*
* usagespec = [-] N [ + | - [-] M ]
*/
struct simuse *usagespec(char *t)
{
struct simuse *s, **st;
char ibuf[32];
long bf=0,b=-1,ef=0, e=-1;
int i, n;
if (!*t) usage(10);
if (*t == '-'){
bf = -1;
t++;
}
for(i=0; isdigit(*t) && i < 10; i++) ibuf[i] = *t++;
if (isdigit(*t)) usage(10);
ibuf[i] = 0;
b = atoi(ibuf);
if (*t == '+') {
ef = 1;
} else if (*t == '-') {
t++;
if (*t == '-') {
ef = -1;
t++;
}
for(i=0; isdigit(*t) && i < 10; i++) ibuf[i] = *t++;
if (isdigit(*t)) usage(10);
ibuf[i] = 0;
e = atoi(ibuf);
}
if (!*t) usage(10);
if (bf == -1 || ef == -1) {
for(i=0,s=simuse; s; s=s->nxt) i++;
st = malloc(sizeof(struct simuse *) * i);
for(i=0,s=simuse; s; s=s->nxt) st[i++] = s;
n = i-1;
}
return NULL;
}
/*
* Adjust the time warp factor by as many days as you think are necessary.
* If you have very infrequent logins, a WARP_FACTOR greater than 60 days
* would probably be called for. It's probably safe to have a huge warp
* factor. Most errors in time are either small or very very large (i.e.
* year 2038/1970).
*/
#define WARP_FACTOR (60*24*60*60)
/*
* Make sure the day that the wtmp entry corresponds to, exists in our
* days list. If day is less than the starting day by more than a day, or
* greater than the last day by more than a day, allocate all the days in
* between as well.
*/
int checkday(time_t t)
{
struct day *d, *p;
if (days == NULL) {
end = days = mkday(t);
} else {
if (t < days->start) {
if (t < days->start-WARP_FACTOR) return -1;
p = d = mkday(t);
while (p->stop+1 < days->start) {
p->nxt = mkday(p->stop+1);
p = p->nxt;
}
p->nxt = days;
days = d;
} else if (t > end->stop) {
if (t > end->stop+WARP_FACTOR) return -1;
p = d = mkday(end->stop+1);
while (p->stop < t) {
p->nxt = mkday(p->stop+1);
p = p->nxt;
}
end->nxt = d;
end = p;
}
}
return 0;
}
/*
* Makes a day entry. We'll assume a day is exactly 24 hours or 86400
* seconds long. I don't know if this is the case or not. I don't think
* the time algorithm takes into account leap seconds.
*
* Oh boy! Daylight savings time screws this all to hell. We can't
* assume anything. Check the time at 12:00:00am and 11:59:59pm to get
* the time bound for the day.
*/
struct day *mkday(time_t t)
{
struct day *dp;
struct tm tm, *xtm;
int yday;
xtm = localtime(&t);
memcpy(&tm,xtm,sizeof(tm));
tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
yday = tm.tm_yday;
dp = bmalloc(sizeof(struct day));
dp->start = mktime(&tm);
/* They may not have a 12am on the day they change to daylight savings time. */
while (yday != tm.tm_yday) {
tm.tm_hour++;
dp->start = mktime(&tm);
}
tm.tm_isdst = -1;
xtm = &tm;
xtm->tm_hour = 23;
xtm->tm_min = 59;
xtm->tm_sec = 59;
dp->stop = mktime(xtm);
dp->day = xtm->tm_mday;
dp->wday = xtm->tm_wday;
dp->month = xtm->tm_mon;
dp->year = 1900+xtm->tm_year;
dp->minlogins = -1;
ndays++;
/*
if (d->stop > (d->start + (24*60*60))) fprintf(stderr,"Created long day: %s %d.\n",month[tm.tm_mon],tm.tm_mday);
if (d->start > (d->stop - ((24*60*60)-1))) fprintf(stderr,"Created short day: %s %d.\n",month[tm.tm_mon],tm.tm_mday);
*/
return dp;
}
void saclogin(struct sactmp su)
{
struct user *q;
struct day *d;
struct usr *u = NULL, *up;
int l;
/*
* If we still have a login on this line, it's logged out for sure now!
* Wtmp could be corrupted.
*/
saclogout(su,FALSE);
/* printf ("Logging in %s [%s] <%ld>...\n",u.ut_user,u.ut_line,u.ut_time); */
q = bmalloc(sizeof(struct user));
strncpy(q->line,su.u.ut_line,UT_LINESIZE);
strncpy(q->user,su.u.ut_user,UT_NAMESIZE);
strncpy(q->host,su.u.ut_host,UT_HOSTSIZE);
q->user[UT_NAMESIZE] = q->line[UT_LINESIZE] = q->host[UT_HOSTSIZE] = 0;
q->in = su.u.ut_time;
q->nxt = usr;
usr = q;
if ((type & 0x000F) == SIMUSE) {
release(q,su.u.ut_time,&su,0,1);
return;
}
if ((type & MINMAX) == MINMAX && !(fix && (finduser(us,q->user)) == NULL) && !(exclude && isexcluded(q->user)) && !(fixtty && !istty(q->line)) && !(fixhost && !ishost(q->host))) {
l = loggedin++;
maxlogins = max(maxlogins,loggedin);
if (minlogins > -1) minlogins = min(minlogins,l);
else minlogins = l;
if ((type & 0x000F) == USER) {
if ((u = finduser(us,q->user)) == NULL) u = adduser(&us,q->user);
u->loggedin++;
u->maxlogins = max(u->maxlogins,u->loggedin);
if (u->minlogins > -1) u->minlogins = min(u->minlogins,u->loggedin-1);
else u->minlogins = u->loggedin-1;
}
if (su.u.ut_time < end->start) {
for(d=days;d;d=d->nxt) {
if (su.u.ut_time >= d->start && su.u.ut_time <= d->stop) {
d->maxlogins = max(d->maxlogins,loggedin);
if (d->minlogins > -1) d->minlogins = min(d->minlogins,l);
else d->minlogins = l;
if ((type & 0x000F) == USER) {
if ((up = finduser(d->us,q->user)) == NULL) up = adduser(&d->us,q->user);
up->maxlogins = max(up->maxlogins,u->loggedin);
if (up->minlogins > -1) up->minlogins = min(up->minlogins,u->loggedin-1);
else up->minlogins = u->loggedin-1;
}
break;
}
}
} else {
end->maxlogins = max(end->maxlogins,loggedin);
if (end->minlogins > -1) end->minlogins = min(end->minlogins,l);
else end->minlogins = l;
if ((type & 0x000F) == USER) {
if ((up = finduser(end->us,q->user)) == NULL) up = adduser(&end->us,q->user);
up->maxlogins = max(up->maxlogins,u->loggedin);
if (up->minlogins > -1) up->minlogins = min(up->minlogins,u->loggedin-1);
else up->minlogins = u->loggedin-1;
}
}
}
}
void saclogout(struct sactmp su, int usext)
{
struct user *p, *q;
struct usr *u = NULL, *up;
struct day *d;
/*
time_t t = su.u.ut_time - end->start,h,m,s;
char uid[UT_NAMESIZE+2];
int hack;
hack = TRUE;
*/
for(p=q=usr;p;) {
if (!strncmp(su.u.ut_line,p->line,UT_LINESIZE) && (hosttoo? !strncmp(su.u.ut_host,p->host,UT_HOSTSIZE) : TRUE)) {
/* printf ("Logging out %s [%s] <%ld>...\n",u.ut_user,u.ut_line,u.ut_time); */
release(p,su.u.ut_time,&su,usext,-1);
if ((type & MINMAX) == MINMAX && !(fix && (finduser(us,p->user)) == NULL) && !(exclude && isexcluded(p->user)) && !(fixtty && !istty(p->line)) && !(fixhost && !ishost(p->host))) {
loggedin = max(0,loggedin-1);
if (minlogins > -1) minlogins = min(minlogins,loggedin);
else minlogins = loggedin;
if ((type & 0x000F) == USER && ((u = finduser(us,p->user)) != NULL)) {
u->loggedin = max(0,u->loggedin-1);
}
if (su.u.ut_time < end->start) {
for(d=days;d;d=d->nxt) {
if (su.u.ut_time >= d->start && su.u.ut_time <= d->stop) {
d->maxlogins = max(d->maxlogins,loggedin+1);
if (d->minlogins > -1) d->minlogins = min(d->minlogins,loggedin);
else d->minlogins = loggedin;
if ((type & 0x000F) == USER) {
if ((up = finduser(d->us,p->user)) == NULL) up = adduser(&d->us,p->user);
up->maxlogins = max(up->maxlogins,u->loggedin+1);
if (up->minlogins > -1) up->minlogins = min(up->minlogins,u->loggedin);
else up->minlogins = up->loggedin;
}
break;
}
}
} else {
end->maxlogins = max(end->maxlogins,loggedin+1);
if (end->minlogins > -1) end->minlogins = min(end->minlogins,loggedin);
else end->minlogins = loggedin;
if ((type & 0x000F) == USER) {
if ((up = finduser(end->us,p->user)) == NULL) up = adduser(&end->us,p->user);
up->maxlogins = max(up->maxlogins,u->loggedin+1);
if (up->minlogins > -1) up->minlogins = min(up->minlogins,u->loggedin);
else up->minlogins = u->loggedin;
}
}
}
if (p == usr) {
usr = p->nxt;
free(p);
p = q = usr;
} else {
q->nxt = p->nxt;
free(p);
p = q->nxt;
}
return;
}
q = p;
p = p->nxt;
}
/* Vain attempt to reasearch reaping logouts w/ missing logins.
if (hack) {
if (usext && su.u.ut_type != DEAD_PROCESS);
if (!su.u.ut_name[0] && su.u.ut_name[1]) {
h=t/3600;
m=(t-(h*3600))/60;
s=t-(h*3600)-(m*60);
memcpy(uid,su.u.ut_name,UT_NAMESIZE);
if (!uid[0]) uid[0] = '.';
uid[UT_NAMESIZE] = 0;
if (!strcmp("LOGIN", uid) || !strcmp(".OGIN", uid)) return;
fprintf(stderr,"sac: no matching login for logout of [%s] at %s %d %2ld:%02ld:%02ld.\n",uid,month[end->month],end->day,h,m,s);
}
}
*/
}
/*
* logout everyone on reboots or crashes.
*/
void do_reboot(time_t t)
{
struct user *p;
struct day *d;
struct usr *u;
for(p=usr;usr;p=usr){
release(p,t,NULL,0,-1);
usr=usr->nxt;
free(p);
}
simdex = 0;
simtime = 0;
if ((type & MINMAX) == MINMAX) {
loggedin = 0;
minlogins = 0;
if ((type & 0x000F) == USER) {
for(u=us;u;u=u->nxt) {
u->loggedin = 0;
u->minlogins = 0;
}
}
if (t < end->start) {
for(d=days;d;d=d->nxt)
if (t >= d->start && t <= d->stop) {
d->minlogins = 0;
break;
}
} else end->minlogins = 0;
}
}
/*
* A utmp entry denoted with a line of "|" denotes the "old" time before
* netdate updated the time. An entry with line of "{" denotes the new time.
* The difference in time is applied to the login time of every user still
* logged in.
*/
void changetime(time_t t, char *line)
{
static time_t old = 0;
struct user *p;
signed long dif;
if (!strcmp("|",line)) {
old = t;
return;
}
if (old == 0) return;
dif = (signed long)(t - old);
for(p=usr;p;p=p->nxt) p->in -= dif;
/* I *think* this is the right thing to do */
simtime -= dif;
}
/*
* Apply login time for users who haven't logged out yet (or that the wtmp file
* in particular indicates haven't logged out by the EOF) to the days that
* are in days list. Login time is not applied to days not listed in the
* wtmp file (therefor all the days between the first and last wtmp entries).
* The reason for this is that if you're inspecting an old wtmp file, the
* wtmp may not indicate all logouts for the last day. It is not possible
* to know when the wtmp file really truly ends however, so we apply the
* minimum of the current time or the ending time for the last day.
*/
void cleanup(void)
{
time_t t = time(0);
struct day *lastday;
struct user *p;
struct usr *u;
/* Ooops, the -t option can't just ignore time that isn't in our days list
* so we'll just force t to not go beyond the end of the last day in the
* wtmp file. An option should be made so you can opt to just use the
* last wtmp entry for the end time.
*/
if (lastent) {
lastday = mkday(lastent);
if (t > lastday->stop) t = lastday->stop;
}
/*
* Oops, if we're clipping, we have to remove the released time from the
* list. This is pretty easily done, by moving usr, instead of using a
* temporary pointer.
*/
for(p=usr;usr;p=usr) {
release(usr,t,NULL,0,-1);
usr=usr->nxt;
}
loggedin = 0;
minlogins = 0;
simdex = 0;
if ((type & 0x000F) == USER) {
for(u=us;u;u=u->nxt) {
u->loggedin = 0;
u->minlogins = 0;
}
}
}
/*
* Release a login entry, applying the login time to the particular day
* entries.
* A user is logged in on a particular day when:
* in >= start && in <= stop ||
* out >= start && out <= stop ||
* in < start && out > stop
*/
void release(struct user *u, time_t t, struct sactmp *su, int usext, int siminc)
{
struct day *p;
struct usr *up;
struct user *q;
struct tty *tp;
struct simuse *s;
int i;
if ((signed long)((t - u->in) + 1) < 0) return;
if (cutoff && ((t - u->in)+1) > cutoff) t = u->in+cutoff;
if (discard && ((t - u->in)+1) > discard) return;
/* if ((type & 0x000F) == SIMUSE) return; */
/*
* Clipping assumes that time moves forward. Only in computers is this
* not necessarily a safe assumption.
* Clipping rules:
* If (login time < all other login times)
* reduce logout time (t) to least login time (-1).
* else clip entirely.
*/
if ((type & CLIP) == CLIP && (type & 0x000F) != SIMUSE) {
for(q=usr;q;q=q->nxt) {
if (q != u && !strncmp(u->user,q->user,UT_NAMESIZE)) {
/* throw it out if he's already logged in earlier */
if (q->in <= u->in) return;
/* reduce the logout time to the lowest login time. */
if (q->in < t) t = q->in-1;
}
}
}
if (backtime) {
if (u->in > curtime || t < backtime) return;
if (u->in < backtime && t > backtime) u->in = backtime;
if (u->in < curtime && t > curtime) t = curtime;
}
/* Check the user list if it's fixed. */
if (fix && (up = finduser(us,u->user)) == NULL) return;
/* Check the exclude user list. */
if (exclude && isexcluded(u->user)) return;
/* Check the tty list. */
if (fixtty && !istty(u->line)) return;
/* Check the host list. */
if (fixhost && !ishost(u->host)) return;
if ((type & 0x000F) == SIMUSE) {
/* if (t == simtime && siminc == -1) fprintf(stderr,"mark! [simtime=%ld:%d]\n",simtime,simdex); */
if (siminc != -1) t--;
/* if (t < simtime && siminc == 1) fprintf(stderr,"mark! [simtime=%ld:%d]\n",simtime,simdex); */
if (simdex && simtime <= t) {
if (simtime < end->start) {
for(p=days;p;p=p->nxt) {
if (simtime >= p->start && simtime <= p->stop) {
s = findsim(&p->simuse,simdex);
s->time += (min(t,p->stop) - simtime) + 1;
s->count++;
if ((type & HOUR) == HOUR) apply_hours(simtime,t,p->start,s->h);
} else if (t >= p->start && t <= p->stop) {
s = findsim(&p->simuse,simdex);
s->time += (t - p->start) + 1;
s->count++;
if ((type & HOUR) == HOUR) apply_hours(simtime,t,p->start,s->h);
} else if (simtime < p->start && t > p->stop) {
s = findsim(&p->simuse,simdex);
s->time += (p->stop - p->start) + 1;
s->count++;
if ((type & HOUR) == HOUR) for(i=0;i<24;i++) s->h[i] += 3600;
}
}
if ((type & LOGIN) == LOGIN) {
for(p=days;p;p=p->nxt)
if (simtime <= p->stop) break;
s = findsim(&p->simuse,simdex);
addlogin(&s->login,&s->lastlogin,simtime,t,NULL,NULL,NULL);
}
} else {
s = findsim(&end->simuse, simdex);
s->time += (t - simtime) + 1;
s->count++;
if ((type & HOUR) == HOUR) apply_hours(simtime,t,end->start,s->h);
if ((type & LOGIN) == LOGIN) addlogin(&s->login,&s->lastlogin,simtime,t,NULL,NULL,NULL);
}
}
simdex = max(0,simdex+siminc);
simtime = t+1;
return;
}
/* Only count logins that we actually apply. */
logins++;
/* if we're logging usage / tty then apply login time to tty entry. */
if ((type & 0x000F) == TTY || (type & 0x000F) == RAW) {
tp=findtty(ttys,u->line);
if (tp == NULL) {
tp = bmalloc(sizeof(struct tty));
strncpy(tp->line,u->line,UT_LINESIZE);
if (ttys == NULL) ttys = tp;
else {
tp->nxt = ttys;
ttys = tp;
}
}
if ((type & LOGIN) == LOGIN) addlogin(&tp->login,&tp->lastlogin,u->in,t,u->user,NULL,u->host);
tp->logins++;
if (u->in < end->start) {
for(p=days;p;p=p->nxt) {
if (u->in >= p->start && u->in <= p->stop) tty_apply_hours(u->in,t,p,tp);
else if (t >= p->start && t <= p->stop) tty_apply_hours(u->in,t,p,tp);
else if (u->in < p->start && t > p->stop) tty_apply_hours(u->in,t,p,tp);
}
} else tty_apply_hours(u->in,t,end,tp);
if (usext && (type & IPACCT) == IPACCT) {
tp->inoct += su->inoct;
tp->outoct += su->outoct;
tp->inpkt += su->inpkt;
tp->outpkt += su->outpkt;
tp->datarate = tp->logins? ((tp->datarate*(tp->logins-1))+su->datarate)/tp->logins : su->datarate;
}
if ((type & 0x000F) != RAW) return;
}
/* if we're logging usage / user then apply login time to user entry. */
if (((type & 0x000F) == USER || (type&0x000F) == RAW) && (((up = finduser(us,u->user)) != NULL) || !fix)) {
if (up == NULL) up = adduser(&us,u->user);
/*
{
time_t tt;
u_long m,s;
tt = (t-u->in)+1;
m=tt/60;
s=tt-(m*60);
printf("Released %5ld:%ld\n",m,s);
}
*/
if ((type & LOGIN) == LOGIN) addlogin(&up->login,&up->lastlogin,u->in,t,NULL,u->line,u->host);
up->logins++;
if (u->in < end->start) {
for(p=days;p;p=p->nxt) {
if (u->in >= p->start && u->in <= p->stop) user_apply_hours(u,t,p);
else if (t >= p->start && t <= p->stop) user_apply_hours(u,t,p);
else if (u->in < p->start && t > p->stop) user_apply_hours(u,t,p);
}
} else user_apply_hours(u,t,end);
if (usext && (type & IPACCT) == IPACCT) {
struct usr *ud;
double tt;
if (u->in < end->start) {
for(p=days;p;p=p->nxt) {
if ((u->in >= p->start && u->in <= p->stop) || (t >= p->start && t <= p->stop) ||
(u->in < p->start && t > p->stop)) {
if ((ud = finduser(p->us,u->user)) == NULL) ud = adduser(&p->us,u->user);
tt = (double)((min(p->stop,t) - max(p->start,u->in)) + 1) / (double)((t - u->in) + 1);
ud->inoct += (su->inoct * tt);
ud->outoct += (su->outoct * tt);
ud->inpkt += (su->inpkt * tt);
ud->outpkt += (su->outpkt * tt);
ud->datarate = up->logins? ((up->datarate*(up->logins-1))+(su->datarate*tt))/up->logins : (su->datarate*tt);
}
}
} else {
if ((ud = finduser(end->us,u->user)) == NULL) ud = adduser(&end->us,u->user);
ud->inoct += su->inoct;
ud->outoct += su->outoct;
ud->inpkt += su->inpkt;
ud->outpkt += su->outpkt;
ud->datarate = up->logins? ((up->datarate*(up->logins-1))+su->datarate)/up->logins : su->datarate;
}
}
if ((type & 0x000F) != RAW) return;
}
/*
* We go through this for both daily and total. The total will be accumulated
* at the end since we don't know the starting and ending dates until we're
* done.
*/
if (u->in < end->start) {
/* Ugh, it's probably yesterday, but we've got to start all the way at the
* beginning. I wonder if double-linking the list would speed things up.
*/
for(p=days;p;p=p->nxt) {
if (u->in >= p->start && u->in <= p->stop) {
p->time += (min(p->stop,t) - u->in) + 1;
p->logins++;
if (hourmask || (type & HOUR) == HOUR) apply_hours(u->in,t,p->start,p->h);
} else if (t >= p->start && t <= p->stop) {
p->time += (t - max(p->start,u->in)) + 1;
p->logins++;
if (hourmask || (type & HOUR) == HOUR) apply_hours(u->in,t,p->start,p->h);
} else if (u->in < p->start && t > p->stop) {
p->time += 86400;
p->logins++;
if (hourmask || (type & HOUR) == HOUR)
for(i=0;i<24;i++) p->h[i] += 3600;
}
}
if ((type & LOGIN) == LOGIN) {
for(p=days;p;p=p->nxt)
if (u->in <= p->stop) break;
addlogin(&p->login,&p->lastlogin,u->in,t,u->user,u->line,u->host);
}
/*
if ((type & LOGIN) == LOGIN) addlogin(&p->login,&p->lastlogin,u->in,t,u->user,u->line,u->host);
if (usext && (type & IPACCT) == IPACCT) {
p->inoct += su->inoct;
p->outoct += su->outoct;
p->inpkt += su->inpkt;
p->outpkt += su->outpkt;
p->datarate = p->logins? ((p->datarate*(p->logins-1))+su->datarate)/p->logins : su->datarate;
}
}
*/
if (usext && (type & IPACCT) == IPACCT) {
struct usr *ud;
double tt;
for(p=days;p;p=p->nxt) {
if ((u->in >= p->start && u->in <= p->stop) || (t >= p->start && t <= p->stop) ||
(u->in < p->start && t > p->stop)) {
tt = (double)((min(p->stop,t) - max(p->start,u->in)) + 1) / (double)((t - u->in) + 1);
p->inoct += (su->inoct * tt);
p->outoct += (su->outoct * tt);
p->inpkt += (su->inpkt * tt);
p->outpkt += (su->outpkt * tt);
p->datarate = p->logins? ((p->datarate*(p->logins-1))+(su->datarate*tt))/p->logins : (su->datarate*tt);
}
}
}
} else {
end->time += (min(end->stop,t) - max(end->start,u->in)) + 1;
end->logins++;
if (hourmask || (type & HOUR) == HOUR) apply_hours(u->in,t,end->start,end->h);
if ((type & LOGIN) == LOGIN) addlogin(&end->login,&end->lastlogin,u->in,t,u->user,u->line,u->host);
if (usext && (type & IPACCT) == IPACCT) {
end->inoct += su->inoct;
end->outoct += su->outoct;
end->inpkt += su->inpkt;
end->outpkt += su->outpkt;
end->datarate = end->logins? ((end->datarate*(end->logins-1))+su->datarate)/end->logins : su->datarate;
}
}
}
void addlogin(struct login **login, struct login **last, time_t in, time_t out, char *name, char *tty, char *host)
{
struct login *l;
l = bmalloc(sizeof(struct login));
l->start = in;
l->stop = out;
l->name = name? scopy(name) : NULL;
l->tty = tty? scopy(tty) : NULL;
l->host = host? scopy(host) : NULL;
l->nxt = NULL;
if (!*last) *last = *login = l;
else {
(*last)->nxt = l;
*last = l;
}
}
void apply_hours(time_t in, time_t out,time_t start, time_t h[24])
{
int i;
time_t b, e;
b = start;
e = start + 3599;
for(i=0;i<24;i++) {
if (in >= b && in <= e) h[i] += (min(e,out) - in) + 1;
else if (out >= b && out <= e) h[i] += (out - max(b,in)) + 1;
else if (in < b && out > e) h[i] += 3600;
b += 3600;
e += 3600;
}
}
void user_apply_hours(struct user *u, time_t out, struct day *d)
{
int i;
time_t b, e;
struct usr *up;
if ((up = finduser(d->us,u->user)) == NULL) up = adduser(&d->us,u->user);
up->time += (min(out,d->stop) - max(u->in,d->start)) + 1;
if (max(u->in,d->start) == u->in) up->logins++;
else up->xlogins++;
if (hourmask || ((type & HOUR) == HOUR)) {
b = d->start;
e = d->start + 3599;
for(i=0;i<24;i++) {
if (u->in >= b && u->in <= e) up->h[i] += (min(e,out) - u->in) + 1;
else if (out >= b && out <= e) up->h[i] += (out - max(b,u->in)) + 1;
else if (u->in < b && out > e) up->h[i] += 3600;
b += 3600;
e += 3600;
}
}
}
void tty_apply_hours(time_t in, time_t out, struct day *d, struct tty *t)
{
int i;
time_t b, e;
struct tty *tp;
if ((tp = findtty(d->tty,t->line)) == NULL) {
tp = bmalloc(sizeof(struct tty));
strncpy(tp->line,t->line,UT_LINESIZE);
if (d->tty == NULL) d->tty = tp;
else {
tp->nxt = d->tty;
d->tty = tp;
}
}
tp->time += (min(out,d->stop) - max(in,d->start)) + 1;
if (max(in,d->start) == in) tp->logins++;
else tp->xlogins++;
if (hourmask || ((type & HOUR) == HOUR)) {
b = d->start;
e = d->start + 3599;
for(i=0;i<24;i++) {
if (in >= b && in <= e) tp->h[i] += (min(e,out) - in) + 1;
else if (out >= b && out <= e) tp->h[i] += (out - max(b,in)) + 1;
else if (in < b && out > e) tp->h[i] += 3600;
b += 3600;
e += 3600;
}
}
}
char *getwrd(char *s)
{
char *t;
while(*s && isspace(*s)) s++;
t = s;
while(*s && !isspace(*s)) s++;
*s = 0;
return t;
}
struct usr *adduser(struct usr **up, char *s)
{
struct usr *u;
if (*s == '@') {
char sbuf[BUFSIZE], *str;
FILE *fd;
sbuf[BUFSIZE-1] = 0;
if ((fd = fopen(s+1,"r")) == NULL) {
fprintf(stderr,"sac: error opening file '%s' for reading.\n",s+1);
exit(1);
}
while (fgets(sbuf,BUFSIZE-1,fd) != NULL) {
str = getwrd(sbuf);
if (!str[0] || str[0] == '#') continue;
adduser(up,str);
}
fclose(fd);
return NULL;
}
u = bmalloc(sizeof(struct usr));
strncpy(u->user,s,UT_NAMESIZE);
u->minlogins = -1;
u->nxt = *up;
*up = u;
return *up;
}
struct usr *finduser(struct usr *up, char *s)
{
struct usr *u;
for(u=up;u;u=u->nxt)
if (!strcmp(s,u->user)) return u;
return NULL;
}
void addexclude(char *s)
{
struct exc *x;
if (*s == '@') {
char sbuf[BUFSIZE], *str;
FILE *fd;
sbuf[BUFSIZE-1] = 0;
if ((fd = fopen(s+1,"r")) == NULL) {
fprintf(stderr,"sac: error opening file '%s' for reading.\n",s+1);
exit(1);
}
while (fgets(sbuf,BUFSIZE-1,fd) != NULL) {
str = getwrd(sbuf);
if (!str[0] || str[0] == '#') continue;
addexclude(str);
}
fclose(fd);
return;
}
x = bmalloc(sizeof(struct exc));
strncpy(x->user,s,UT_NAMESIZE);
x->user[UT_NAMESIZE] = 0;
x->nxt = ex;
ex = x;
return;
}
int isexcluded(char *s)
{
struct exc *u;
for(u=ex;u;u=u->nxt)
if (!strncmp(s,u->user,UT_NAMESIZE)) return TRUE;
return FALSE;
}
struct tty *findtty(struct tty *tt, char *l)
{
struct tty *t;
for(t=tt;t;t=t->nxt)
if (!strncmp(t->line,l,UT_LINESIZE)) return t;
return NULL;
}
void addtty(char *s)
{
struct ttys *t;
int ispat;
if (*s == '@') {
char sbuf[BUFSIZE], *str;
FILE *fd;
sbuf[BUFSIZE-1] = 0;
if ((fd = fopen(s+1,"r")) == NULL) {
fprintf(stderr,"sac: error opening file '%s' for reading.\n",s+1);
exit(1);
}
while (fgets(sbuf,BUFSIZE-1,fd) != NULL) {
str = getwrd(sbuf);
if (!str[0] || str[0] == '#') continue;
addtty(str);
}
fclose(fd);
return;
}
ispat = ispattern(s);
if (ispat == -1 || (ispat && patmatch("ttyp0",s) == -1)) usage(6);
t = bmalloc(sizeof(struct ttys));
t->line = scopy(s);
t->ispat = ispat;
t->nxt = tty;
tty = t;
return;
}
int istty(char *s)
{
struct ttys *t;
for(t=tty;t;t=t->nxt) {
if (t->ispat) {
if (patmatch(s,t->line) == 1) return TRUE;
continue;
}
if (!strncmp(s,t->line,UT_LINESIZE)) return TRUE;
}
return FALSE;
}
void addhost(char *s)
{
struct hosts *h;
int ispat;
if (*s == '@') {
char sbuf[BUFSIZE], *str;
FILE *fd;
sbuf[BUFSIZE-1] = 0;
if ((fd = fopen(s+1,"r")) == NULL) {
fprintf(stderr,"sac: error opening file '%s' for reading.\n",s+1);
exit(1);
}
while (fgets(sbuf,BUFSIZE-1,fd) != NULL) {
str = getwrd(sbuf);
if (!str[0] || str[0] == '#') continue;
addhost(str);
}
fclose(fd);
return;
}
ispat = ispattern(s);
if (ispat == -1 || (ispat && patmatch("tty",s) == -1)) usage(6);
h = bmalloc(sizeof(struct hosts));
h->host = scopy(s);
h->ispat = ispat;
h->len = strlen(h->host);
if (!ispat && ((index(h->host,'.') == NULL) || (h->host[h->len-1] == '.'))) h->ss = TRUE;
else h->ss = FALSE;
h->nxt = hosts;
hosts = h;
return;
}
int ishost(char *s)
{
struct hosts *h;
for(h=hosts;h;h=h->nxt) {
if (h->ispat) {
if (patmatch(s,h->host) == 1) return TRUE;
continue;
}
if (h->ss) {
if (!strncmp(s,h->host,h->len)) return TRUE;
} else {
if (!strncmp(s,h->host,UT_HOSTSIZE)) return TRUE;
}
}
return FALSE;
}
struct simuse *findsim(struct simuse **s, int i)
{
struct simuse *sp, *pp, *n;
for(pp=sp=*s;sp;sp=sp->nxt) {
if (sp->index == i) return sp;
if (sp->index > i) break;
pp = sp;
}
n = bmalloc(sizeof(struct simuse));
n->index = i;
n->nxt = sp;
if (sp == *s) *s = n;
else pp->nxt = n;
return n;
}
/* Tests for *, ?, and [] */
int ispattern(char *s)
{
for(;*s;s++) {
if (*s == '*') return 1;
if (*s == '?') return 1;
if (*s == '[') {
for(;*s;s++)
if (*s == ']') return 1;
return -1;
}
}
return 0;
}
/*
* Patmatch() code courtesy of Thomas Moore (dark@mama.indstate.edu)
* returns:
* 1 on a match
* 0 on a mismatch
* -1 on a syntax error in the pattern
*/
int patmatch(char *sbuf, char *pat)
{
int match = 1,m,n;
while(*pat && match) {
switch(*pat) {
case '[':
pat++;
if(*pat != '^') {
n = 1;
match = 0;
} else {
pat++;
n = 0;
}
while(*pat != ']'){
if(*pat == '\\') pat++;
if(!*pat) return -1;
if(pat[1] == '-'){
m = *pat;
pat += 2;
if(*pat == '\\' && *pat)
pat++;
if(*sbuf >= m && *sbuf <= *pat)
match = n;
if(!*pat)
pat--;
} else if(*sbuf == *pat) match = n;
pat++;
}
sbuf++;
break;
case '*':
pat++;
if(!*pat) return 1;
while(*sbuf && !(match = patmatch(sbuf++,pat)));
return match;
case '?':
if(!*sbuf) return 0;
sbuf++;
break;
case '\\':
if(*pat) pat++;
default:
match = (*sbuf++ == *pat);
break;
}
pat++;
if(match<1) return match;
}
if(!*sbuf) return match;
return 0;
}
void *bmalloc(size_t s)
{
void *m;
m = malloc(s);
if (m == NULL) {
fprintf(stderr,"sac: memory exhausted.\n");
exit(1);
}
memset(m,0,s);
return m;
}
void report(void)
{
struct day *d;
struct usr *u, *up;
struct tty *t, *tp;
struct simuse *s, *sp;
struct login *l;
struct tm in, *out;
time_t h[24], tt;
double inoct, outoct, inpkt, outpkt;
long datarate = 0;
int i;
/*
* Narrow down the days list to the range of days specified by the -s and -e
* options. This seems kludged in to me, but I really can't see how else to
* do it.
*/
if (sd || ed || sm || em) {
if (!sd) {
if (sm == MINUSZERO) sd = end->start;
else if (!sm || sm == PLUSZERO) sd = days->start;
else if (sm < 0) sd = (end->start + (86400*sm)) + 7200;
else if (sm > 0) sd = (days->start + (86400*sm)) + 7200;
}
if (!ed) {
if (em == PLUSZERO) ed = days->start;
else if (!em || em == MINUSZERO) ed = end->start;
else if (em < 0) ed = (end->start + (86400*em)) + 7200;
else if (em > 0) ed = (days->start + (86400*em)) + 7200;
}
if (sd > ed) {
fprintf(stderr,"sac: Start time [%ld] is greater than the end time [%ld]\n",sd,ed);
exit(0);
}
for(d=days;d;d=d->nxt) {
if (d->start <= sd && d->stop >= sd) days = d;
if (d->start <= ed && d->stop >= ed) {
d->nxt = NULL;
end = d;
}
}
}
/* Produce the reports... */
switch(type & 0x000f) {
case TOTAL:
inoct = outoct = inpkt = outpkt = 0;
datarate = 0;
for(ndays=total=0,d=days;d;d=d->nxt) {
if (hourmask) {
for(i=0;i<24;i++) if (hourmask&(1<<i)) total += d->h[i];
} else total += d->time;
ndays++;
if ((type & IPACCT) == IPACCT) {
inoct += d->inoct;
outoct += d->outoct;
inpkt += d->inpkt;
outpkt += d->outpkt;
datarate = ndays? ((datarate*(ndays-1))+d->datarate)/ndays : d->datarate;
}
}
if (backtime) printf("Total: %s over %s hours.\n", dtime(total,0), back);
else printf("Total: %12s over %d days.\n", dtime(total,0), ndays);
if ((type & AVERAGE) == AVERAGE) {
printf("Average: %10s / day, ",dtime(total,ndays));
printf("%10s / login\n",dtime(total,logins));
}
if ((type & MINMAX) == MINMAX) printf("Logins: %11d Min: %3d Max: %3d\n",logins, max(0,minlogins), maxlogins);
if ((type & IPACCT) == IPACCT) {
printf("Octets: %12.0f in/out: %10.0f/%12.0f\n",inoct+outoct, inoct, outoct);
printf("Packets:%12.0f in/out: %10.0f/%12.0f\n",inpkt+outpkt, inpkt, outpkt);
if (datarate) printf("\tData rate: %9ld\n",datarate);
}
if ((type & LOGIN) == LOGIN) {
for(d=days;d;d=d->nxt) {
for(l=d->login;l;l=l->nxt) {
out = localtime(&l->start);
memcpy(&in,out,sizeof(struct tm));
out = localtime(&l->stop);
tt = (l->stop - l->start) + 1;
if (l->stop < days->start || l->start > end->stop) continue;
if (longdate) printf(" %s %s %2d %4d",dayabbr[in.tm_wday], month[in.tm_mon], in.tm_mday, 1900+in.tm_year);
else printf(" %s %2d", month[in.tm_mon], in.tm_mday);
printf(" %02d:%02d:%02d - %02d:%02d:%02d %-8.8s %-6.12s %-16.16s %9s\n",
in.tm_hour, in.tm_min, in.tm_sec, out->tm_hour, out->tm_min, out->tm_sec,
l->name, l->tty, l->host, dtime(tt,0));
}
}
}
if ((type & HOUR) == HOUR) {
for(i=0;i<24;i++) h[i] = 0;
for(d=days;d;d=d->nxt)
for(i=0;i<24;i++) h[i] += d->h[i];
print_hours(h,total);
}
break;
case DAY:
for(d=days;d;d=d->nxt) {
if (hourmask) {
for(total=i=0;i<24;i++) if (hourmask&(1<<i)) total += d->h[i];
} else total = d->time;
if (longdate) printf("%s %s %2d %4d",dayabbr[d->wday], month[d->month], d->day, d->year);
else printf("%s %2d",month[d->month],d->day);
printf(" total %10s",dtime(total,0));
if ((type & AVERAGE) == AVERAGE) {
if (d->logins)
printf(" %5ld logins %8s %s/login",d->logins,dtime(total,d->logins),timefmt==SECONDS?"secs":"hrs");
else {
printf(" no logins");
if ((type & MINMAX) == MINMAX) printf(" ");
}
}
if ((type & MINMAX) == MINMAX) {
if (d->minlogins == -1) d->maxlogins = d->minlogins = d->logins;
printf(" Min: %-3d Max: %3d",d->minlogins, d->maxlogins);
}
putchar('\n');
if ((type & IPACCT) == IPACCT) {
printf("\tOctets: %12.0f in/out: %10.0f/%12.0f\n",d->inoct+d->outoct, d->inoct, d->outoct);
printf("\tPackets:%12.0f in/out: %10.0f/%12.0f\n",d->inpkt+d->outpkt, d->inpkt, d->outpkt);
if (d->datarate) printf("\tData rate: %9ld\n",d->datarate);
}
if ((type & LOGIN) == LOGIN) {
for(l=d->login;l;l=l->nxt) {
out = localtime(&l->start);
memcpy(&in,out,sizeof(struct tm));
out = localtime(&l->stop);
tt = (l->stop - l->start) + 1;
if (l->stop < days->start || l->start > end->stop) continue;
printf(" %-8.8s %02d:%02d:%02d - %02d:%02d:%02d port %-6.12s %-16.16s %10s\n",
l->name, in.tm_hour, in.tm_min, in.tm_sec,
out->tm_hour, out->tm_min, out->tm_sec,l->tty, l->host, dtime(tt,0));
}
}
if ((type & HOUR) == HOUR) print_hours(d->h,d->time);
}
break;
case USER:
for(u=us;u;u=u->nxt) {
u->logins = 0;
u->minlogins = -1;
u->maxlogins = 0;
u->ignore = ignoretime;
u->inoct = u->outoct = u->inpkt = u->outpkt = 0;
u->datarate = 0;
for(ndays=total=0,d=days;d;d=d->nxt) {
if ((up = finduser(d->us,u->user)) != NULL) {
if (u->ignore) {
if (hourmask || (type & HOUR) == HOUR) {
up->time -= (up->time <= u->ignore) ? up->time : u->ignore;
for(i=0;i<24 && u->ignore; i++)
if (up->h[i] <= u->ignore) {
u->ignore -= up->h[i];
up->h[i] = 0;
} else {
up->h[i] -= u->ignore;
u->ignore = 0;
}
} else {
if (up->time <= u->ignore) {
u->ignore -= up->time;
up->time = 0;
} else {
up->time -= u->ignore;
u->ignore = 0;
}
}
}
if (hourmask) {
for(i=0;i<24;i++) if (hourmask&(1<<i)) u->time += up->h[i];
} else u->time += up->time;
u->logins += up->logins;
if (d == days) u->logins += up->xlogins;
if ((type & HOUR) == HOUR) for(i=0;i<24;i++) u->h[i] += up->h[i];
if ((type & MINMAX) == MINMAX) {
if (up->minlogins == -1) up->maxlogins = up->minlogins = up->logins;
u->maxlogins = max(u->maxlogins,up->maxlogins);
if (u->minlogins > -1) u->minlogins = min(u->minlogins,up->minlogins);
else u->minlogins = up->minlogins;
}
if ((type & IPACCT) == IPACCT) {
u->inoct += up->inoct;
u->outoct += up->outoct;
u->inpkt += up->inpkt;
u->outpkt += up->outpkt;
ndays++;
u->datarate = ndays? ((datarate*(ndays-1))+up->datarate)/ndays : up->datarate;
}
} else {
if ((type & MINMAX) == MINMAX) u->minlogins = min(0,u->minlogins);
}
}
printf("%-16s %10s",u->user,dtime(u->time,0));
if ((type & AVERAGE) == AVERAGE) {
if (u->logins)
printf(" %4ld logins %9s %s/login",u->logins,dtime(u->time,u->logins),timefmt==SECONDS?"secs":"hrs");
else {
printf(" no logins");
if ((type & MINMAX) == MINMAX) printf(" ");
}
}
if ((type & MINMAX) == MINMAX) {
if (u->minlogins == -1) u->maxlogins = u->minlogins = 0;
printf(" Min: %-3d Max: %3d",u->minlogins, u->maxlogins);
}
putchar('\n');
if ((type & IPACCT) == IPACCT) {
printf("\tOctets: %12.0f in/out: %10.0f/%12.0f\n",u->inoct+u->outoct, u->inoct, u->outoct);
printf("\tPackets:%12.0f in/out: %10.0f/%12.0f\n",u->inpkt+u->outpkt, u->inpkt, u->outpkt);
if (u->datarate) printf("\tData rate: %9ld\n",u->datarate);
}
if ((type & LOGIN) == LOGIN) {
for(l=u->login;l;l=l->nxt) {
out = localtime(&l->start);
memcpy(&in,out,sizeof(struct tm));
out = localtime(&l->stop);
tt = (l->stop - l->start) + 1;
if (l->stop < days->start || l->start > end->stop) continue;
if (longdate) printf(" %s %s %2d %4d",dayabbr[in.tm_wday], month[in.tm_mon], in.tm_mday, 1900+in.tm_year);
else printf(" %s %2d", month[in.tm_mon], in.tm_mday);
printf(" %02d:%02d:%02d - %02d:%02d:%02d port %-6.12s %-16.16s %10s\n",
in.tm_hour, in.tm_min, in.tm_sec, out->tm_hour, out->tm_min, out->tm_sec,
l->tty, l->host, dtime(tt,0));
}
}
if ((type & HOUR) == HOUR) print_hours(u->h,u->time);
}
break;
case TTY:
for(t=ttys;t;t=t->nxt) {
t->logins = 0;
for (d=days;d;d=d->nxt) {
if ((tp = findtty(d->tty,t->line)) != NULL) {
if (hourmask) {
for(total=i=0;i<24;i++) if (hourmask&(1<<i)) t->time += tp->h[i];
} else t->time += tp->time;
t->logins += tp->logins;
if (d == days) t->logins += tp->xlogins;
if ((type & HOUR) == HOUR) for(i=0;i<24;i++) t->h[i] += tp->h[i];
}
}
if (t->time == 0) continue;
printf(" %-8s %10s",t->line,dtime(t->time,0));
if ((type & AVERAGE) == AVERAGE)
printf("\t%5ld logins %10s %s / login",t->logins,dtime(t->time,t->logins),timefmt==SECONDS?"seconds":"hours");
putchar('\n');
if ((type & IPACCT) == IPACCT) {
printf("\tOctets: %12.0f in/out: %10.0f/%12.0f\n",t->inoct+t->outoct, t->inoct, t->outoct);
printf("\tPackets:%12.0f in/out: %10.0f/%12.0f\n",t->inpkt+t->outpkt, t->inpkt, t->outpkt);
if (t->datarate) printf("\tData rate: %9ld\n",t->datarate);
}
if ((type & LOGIN) == LOGIN) {
for(l=t->login;l;l=l->nxt) {
out = localtime(&l->start);
memcpy(&in,out,sizeof(struct tm));
out = localtime(&l->stop);
tt = (l->stop - l->start) + 1;
if (l->stop < days->start || l->start > end->stop) continue;
if (longdate) printf(" %s %s %2d %4d",dayabbr[in.tm_wday], month[in.tm_mon], in.tm_mday, 1900+in.tm_year);
else printf(" %s %2d", month[in.tm_mon], in.tm_mday);
printf(" %02d:%02d:%02d - %02d:%02d:%02d %-8.8s %-16.16s %10s\n",
in.tm_hour, in.tm_min, in.tm_sec, out->tm_hour, out->tm_min, out->tm_sec,
l->name, l->host, dtime(tt,0));
}
}
if ((type & HOUR) == HOUR) print_hours(t->h,t->time);
}
break;
case SIMUSE:
for(d=days;d;d=d->nxt) {
for(s=d->simuse;s;s=s->nxt) {
sp = findsim(&simuse,s->index);
sp->count += s->count;
sp->time += s->time;
for(i=0;i<24;i++) sp->h[i] += s->h[i];
if (s->login == NULL) continue;
if (sp->login == NULL) sp->login = s->login;
else sp->lastlogin->nxt = s->login;
sp->lastlogin = s->lastlogin;
}
}
for (s=simuse;s;s=s->nxt) {
printf(" %3d ttys %10s",s->index,dtime(s->time,0));
printf(" %10s %s usage",dtime(s->time*s->index,0),timefmt==SECONDS?"secs": "hrs");
if ((type & AVERAGE) == AVERAGE)
printf(" %5ld times %8s /instance", s->count, dtime(s->time,s->count));
putchar('\n');
if ((type & LOGIN) == LOGIN) {
for(l=s->login;l;l=l->nxt) {
out = localtime(&l->start);
memcpy(&in,out,sizeof(struct tm));
out = localtime(&l->stop);
tt = (l->stop - l->start) + 1;
if (l->stop < days->start || l->start > end->stop) continue;
if (longdate) printf(" %s %s %2d %4d",dayabbr[in.tm_wday], month[in.tm_mon], in.tm_mday, 1900+in.tm_year);
else printf(" %s %2d", month[in.tm_mon], in.tm_mday);
printf(" %02d:%02d:%02d - %02d:%02d:%02d %10s\n",
in.tm_hour, in.tm_min, in.tm_sec, out->tm_hour, out->tm_min, out->tm_sec, dtime(tt,0));
}
}
if ((type & HOUR) == HOUR) print_hours(s->h,s->time);
}
break;
case RAW:
for(i=0;i<24;i++) h[i] = 0;
for(ndays=total=0,d=days;d;d=d->nxt) {
if (hourmask) {
for(i=0;i<24;i++) {
if (hourmask&(1<<i)) {
total += d->h[i];
h[i] += d->h[i];
}
}
} else {
total += d->time;
for(i=0;i<24;i++) h[i] += d->h[i];
}
ndays++;
for(u=us;u;u=u->nxt) {
if ((up = finduser(d->us,u->user)) != NULL) {
if (hourmask) {
for(i=0;i<24;i++) if (hourmask&(1<<i)) u->time += up->h[i];
} else u->time += up->time;
u->logins += up->logins;
if (d == days) u->logins += up->xlogins;
for(i=0;i<24;i++) u->h[i] += up->h[i];
}
}
for(t=ttys;t;t=t->nxt) {
if ((tp = findtty(d->tty,t->line)) != NULL) {
if (hourmask) {
for(i=0;i<24;i++) if (hourmask&(1<<i)) t->time += tp->h[i];
} else t->time += tp->time;
t->logins += tp->logins;
if (d == days) t->logins += tp->xlogins;
for(i=0;i<24;i++) t->h[i] += tp->h[i];
}
}
}
if (days) printf("Days in report: %d Start: %02d/%02d/%02d End: %02d/%02d/%02d Hourmask: %06lX\n",ndays,days->month,days->day,days->year,end->month,end->day,end->year,hourmask&0x0FFF);
else printf("Days in report: 0 Start: -/-/- End: -/-/- Hourmask: %06lX\n",hourmask&0x0FFF);
printf("Total: %lu (%u logins) hours:",total,logins);
for(i=0;i<24;i++) printf(" %lu",h[i]);
printf("\n\n");
for(i=0,u=us;u;u=u->nxt) i++;
printf("Users in report: %d\n",i);
for(u=us;u;u=u->nxt) {
printf(" %8s total: %-10lu (%5ld logins) hours:",u->user,u->time,u->logins);
for(i=0;i<24;i++) printf(" %lu",u->h[i]);
printf("\n");
}
printf("\n");
for(i=0,t=ttys;t;t=t->nxt) i++;
printf("TTY's in report: %d\n",i);
for(t=ttys;t;t=t->nxt) {
printf(" %8s total: %-10lu (%5ld logins) hours:",t->line,t->time,t->logins);
for(i=0;i<24;i++) printf(" %lu",t->h[i]);
printf("\n");
}
printf("\n");
for(d=days;d;d=d->nxt) {
printf("%02d/%02d/%02d total: %-10lu (%5ld logins) hours:",d->month,d->day,d->year,d->time,d->logins);
for(i=0;i<24;i++) printf(" %lu",d->h[i]);
printf("\n");
for(u=d->us;u;u=u->nxt) {
printf(" %8s total: %-10lu (%5ld logins) hours:",u->user,u->time,u->logins+(d==days?u->xlogins:0));
for(i=0;i<24;i++) printf(" %lu",u->h[i]);
printf("\n");
}
printf("\n");
for(t=d->tty;t;t=t->nxt) {
printf(" %8s total: %-10lu (%5ld logins) hours:",t->line,t->time,t->logins+(d==days?t->xlogins:0));
for(i=0;i<24;i++) printf(" %lu",t->h[i]);
printf("\n");
}
printf("\n");
}
}
}
void print_hours(time_t h[24], time_t total)
{
static char *bar = "########################################################################";
int i, bl = strlen(bar)-9;
float p[24], scale, maxp = 0;
if (!total) {
for(i=0;i<24;i++)
printf("%02d-: %8s\n",i,dtime(0,0));
} else {
for(maxp=0,i=0;i<24;i++) {
p[i] = (float)h[i] / (float)total;
if (p[i] > maxp) maxp = p[i];
}
scale = (float)bl / maxp;
for(i=0;i<24;i++) {
if (hourmask && !(hourmask & (1<<i))) continue;
printf("%02d-: %10s %.*s\n",i,dtime(h[i],0),(int)(scale*p[i]),bar);
}
}
}
char *dtime(time_t t, int d)
{
static char tbuf[80];
float ft;
time_t h,m,s;
char rup = FALSE;
if ((timefmt&ROUND) == ROUND) rup = TRUE;
switch(timefmt&0x0f) {
case FRAC:
if (d) ft=((float)t/3600)/d;
else ft=(float)t/3600;
sprintf(tbuf,"%.2f",ft);
break;
case SECONDS:
if (d) h = t/d;
else h = t;
sprintf(tbuf,"%ld",h);
break;
case HMS:
if (d) t=(time_t)((float)t/d);
h=t/3600;
m=(t-(h*3600))/60;
s=t-((h*3600)+(m*60));
sprintf(tbuf,"%ld:%02ld:%02ld",h,m,s);
break;
case HM:
if (d) t=(time_t)((float)t/d);
h=t/3600;
m=(t-(h*3600))/60;
if (rup) {
s=t-((h*3600)+(m*60));
if (s >= 30) m++;
if (m >= 60) {h++; m=0;}
}
sprintf(tbuf,"%ld:%02ld",h,m);
break;
case HOURS:
if (d) t=(time_t)((float)t/d);
h=t/3600;
if (rup) {
m=(t-(h*3600))/60;
s=t-((h*3600)+(m*60));
if (s >= 30) m++;
if (m >= 60) {h++; m=0;}
if (m >= 30) h++;
}
sprintf(tbuf,"%ld",h);
break;
}
return tbuf;
}
|