/*
 *	Polytechnic UNIX accounting Package - Program: conn.c
 *
 *	Author:	John Buck
 *	Polytechnic Institute NY, September 1981
 *
 *	Program Description:
 *		Program will read the /etc/wtmp file, and tally up
 *		the connect time for each person in it.  It will place
 *		the result plus the previous connect total back into
 *		the accounting file.  If desired, the wtmp file may then
 *		be zeroed. (This is a good idea.)
 *
 *	Note: this program is a real work of art. (Some of THE worst code
 *		I've seen in my life.  This because I was too lazy to
 *		re-write Henry Yen's code, which, to say the least, is
 *		unique.
 */



#define	KILLWTMP 1
#define oneshift 0
char *wtmpfile = "/etc/wtmp";

#include	"sys/types.h"
#include	"utmp.h"
#include	"hsubs.h"
#include	"actrecs.h"

struct acctrec	arec;

char	wbuf[4608];		/* 9 blocks, nice with 36 byte utmp records */

struct {
	int gd , id;
	long on;
} utmp[64];

struct	huser {
	short	hid;
	short	hgd;
	long	value;
} user[NHENT];
/* Connect rates:      3rd   2nd   1st */
/* Formerly:
float	conrate[3] = { 1.0 , 1.5 , 3.0};
*/
/*
 * Floating point constant folding is invalid in this C
 */
float	conrate[3] = {
	1.00,	/* OFFPEAK/CONRATE */
	1.521739, /* NORMRATE/CONRATE */
	2.82609 /* PEAKRATE/CONRATE */
};
long	rate();

long now, then, next;
#define mon 0,0,0
#define tue 0,0,1
#define wed 0,0,2
#define thu 0,0,3
#define fri 0,0,4
#define sat 0,0,5
#define sun 0,0,6
#define END 0,0,0,0,0,0

static struct shifts  {
	struct shifts *next;	/* pointer to next shift */
	long diff;		/* number of seconds since monday 00:00:00 */
	int day;
	int hour;
	int min;
	int shift;
} shifts[] = {
	sun,17,00,2,
	sun,23,00,3,
	mon,8,00,1,
	mon,21,00,2,
	mon,23,00,3,
	tue,8,00,1,
	tue,21,00,2,
	tue,23,00,3,
	wed,8,00,1,
	wed,21,00,2,
	wed,23,00,3,
	thu,8,00,1,
	thu,21,00,2,
	thu,23,00,3,
	fri,8,00,1,
	fri,21,00,2,
	fri,23,00,3,
	sat,23,00,3,
	END
};

init()
{
	register int n = 0;

m:	shifts[n].diff = shifts[n].min + shifts[n].hour*60 + shifts[n].day*1440;
	shifts[n].diff *= 60L;
	if (shifts[n+1].shift == 0) {
		shifts[n].next = &shifts[0];
		return;
	}
	shifts[n].next = &shifts[n+1];
	n++;
	goto m;
}

shiftnum()
{
	register int *tim;
	int *localtime();
	long t;
	register struct shifts *ptr1, *ptr2;

	init();
	time(&t);
	tim = localtime(&t);
	if (tim[6])
		tim[6]--;
	else
		tim[6] = 6;
	t = tim[6]*1440L*60L + tim[2]*60L*60L + tim[1]*60L + tim[0];
	ptr1 = &shifts[0];
	ptr2 = ptr1 -> next;
loop:
	if (ptr1->diff < ptr2->diff) {
		if ((t >= ptr1->diff) && (t < ptr2->diff))
			return(ptr1->shift);
	} else {
		if ((t >= ptr1->diff) || (t < ptr2->diff))
			return(ptr1->shift);
	}
	ptr1 = ptr2;
	ptr2 = ptr2->next;
	goto loop;
}
int retcode , af , base , hilim , max;

char	tlist[64][8];
short	ntty = 0;
main(argc, argv)
int argc;
char **argv;
{
	int m, m1;
	register short n;
	short i;
	register struct utmp *pw, *pl;
	int fd;

	if (argc > 1)
		wtmpfile = argv[1];
	fd = open(wtmpfile,0);
	if(fd == -1 || (af = open(DFILE,2)) == -1)
		error("No accounting file\n");
	base = max = 0;
	lseek(fd, 0L, 0);
	init();
	while((i = read(fd, wbuf, 4608)) > 0 && i%(sizeof (struct utmp)) == 0){
	for(pw = wbuf, pl = &wbuf[i]; pw < pl; pw++){
		now = pw->ut_time;
		switch (pw->ut_type) {
			case BOOT_TIME:
			case HALT_TIME:
				logoff(-1);
				break;
			case OLD_TIME:
				then = now;
				break;
			case NEW_TIME:
				if (then) {
					then -= now;
					for (m = 0;m < 64;m++)
						if (utmp[m].on)
							utmp[m].on -= then;
				} else
					sick(0);
				then = 0L;
				break;
			default:
				n = getty(pw->ut_line);
				if(pw->ut_type != DEAD_PROCESS && 
					pw->ut_name[0]){
					if (utmp[n].on)
						logoff(n);
					getuinfo(pw->ut_name, &m1, &m);
					if(m1 < 0 || m < 0){
						printf("conn: Bad uid/gid %d,%d for %8.8s\n",
							m1, m, pw->ut_name);
						break;
					}
					utmp[n].on = now;
					utmp[n].gd = m;
					utmp[n].id = m1;
				} else
					logoff(n);
				break;
		}
	}	/* for */
	}	/* while */
	if (then)
		sick(1);
	logoff(-1);
done:

	writit();
	zerowtmp();
	exit(retcode);
}

logoff(a)
int a;
{
	register int x, xx, hk;
	long yy;

	if (a < 0) {
		for (x = 0;x < 64;x++)
			if (utmp[x].on)
				logoff(x);
		return;
	}
	if (utmp[a].on == 0)
		return;
	yy = utmp[a].on;
	hk = akey(utmp[a].id);
lp:
	if(user[hk].hid != utmp[a].id || user[hk].hgd != utmp[a].gd){
		if(user[hk].hid == 0 && user[hk].hgd == 0){
			user[hk].hid = utmp[a].id;
			user[hk].hgd = utmp[a].gd;
		} else {
			if(++hk >= NHENT)hk = 0;
			goto lp;
		}
	}
#if oneshift
#ifdef DEBUG
	printf("u:%-5d g:%-5d Conn:%-7D\n", user[hk].hid, user[hk].hgd,
		(now-yy));
#endif
	user[hk].value += (now-yy);
	utmp[a].on = 0L;
	return;
#else
more:	xx = shift(yy);
	xx = 3-xx;
	if (next >= now) {
		user[hk].value += rate((now-yy), xx);
		utmp[a].on = 0L;
		return;
	}
	user[hk].value += rate((next-yy), xx);
	yy = next;
	goto more;
#endif
}

#if oneshift == 0
shift(a)
long a;
{
	register struct shifts *v, *vv;
	long wdiff;
	long qq;
	long when();

	wdiff = when(a);
	v = &shifts[0];
	vv = v -> next;
top:	if (v->diff < vv -> diff) {
		if ((wdiff >= v-> diff) && (wdiff < vv -> diff))
			goto out;
	} else if (wdiff >= v-> diff)
		goto out2;
	else if (wdiff < vv -> diff)
		goto out;
	v = vv;
	vv = v-> next;
	goto top;
out2:	wdiff -= 1440L*60L*7L;
out:	next = ((vv -> diff) - wdiff) + a;
	return(v -> shift);
}

long when(a)
long a;
{
	register int *b;
	static long last = 0L;
	static long val;
	long keep;
	int *localtime();

	keep = a;
	if (last == 0) {
		b = localtime(&a);
		if (b[6])
			b[6]--;
		else
			b[6] = 6;
		val = b[6]*1440L*60L + b[2]*60L*60L + b[1]*60L + b[0];
	} else {
		a -= last;
		val += a;
		while (val < 0)
			val += 1440L*60L*7L;
		val %= 1440L*60L*7L;
	}
	last = keep;
	return(val);
}
#endif

sick(a)
int a;
{
	if (a)
		write(2,"URK2\n",5);
	else
		write(2,"URK1\n",5);
	if (!++retcode)
		retcode++;
}


error(s)
char *s;
{
	printf("conn: %s\n",s);
	exit(1);
}


long	rate(n,s)
long n;
int s;
{
	float f;
	f = (float)(n) * conrate[s];
	return((long)(f));
}



zerowtmp(){
#ifdef	KILLWTMP
	close(creat(wtmpfile,0644));
#endif
	return(0);
}


getty(s)
register char *s;
{
	register int i;
	for(i = ntty-1; i >= 0; i--)
		if(strncmp(s, tlist[i], 8) == 0)
			return(i);
	strncpy(tlist[ntty], s, 8);
	return(ntty++);
}


getuinfo(s, u, g)
char *s;
int *u, *g;
{
	static char xx[10];
	static char xbuf[128];
	register short i;
	register char *t;

	strncpy(xx, s, 8);
	xx[8] = 0;
	*u = *g = -1;
	if(ngetent(HFILE_NM, xx, xbuf)){
		return;
	}

	t = xbuf;
	while(*t++ != ':');
	while(*t++ != ':');
	i = 0;
	while(*t >= '0' && *t <= '9')
		i = i * 10 + (*t++) - '0';
	*u = i;
	i = 0;
	if(*t++ != ':'){
		*u = *g = -1;
		return;
	}
	while(*t >= '0' && *t <= '9')
		i = i * 10 + (*t++) - '0';
	*g = i;
	return;
}

writit()
{
	register int i, err, hk;

	for(i = 0; i < NHENT; i++){
		if(user[i].value == 0L)continue;
		if(user[i].hgd == 0 && user[i].hid == 0)
			user[i].hgd = 255;
		hk = user[i].hid;
		lseek(af, (long)(akey(hk))*AREAD, 0);
		err = 0;
lp:
		if(read(af, &arec, AREAD) != AREAD){
			if(err++)error("read error");
			lseek(af, 0L, 0);
			goto lp;
		}
		if(arec.a_gid != user[i].hgd || arec.a_uid != user[i].hid){
			if(arec.a_uid == 0 && arec.a_gid == 0){
				arec.a_uid = user[i].hid;
				arec.a_gid = user[i].hgd;
			} else
				goto lp;
		}
		arec.a_connect += user[i].value;
		lseek(af, -(long)(AREAD), 1);
		write(af, &arec, AREAD);
#ifdef	DEBUG
		printf("U:%-5d G:%-5d  Conn:%7D\n", arec.a_uid, arec.a_gid,
			arec.a_connect);
#endif
	}
	close(af);
}
