/* Watch logins and logouts...

   Print "in:   whatshisname" whenever someone signs on, and
   print "out:  whatshismane" whenever someone signs off.  If
   the "-s" option is given, no bells get rung with the messages.
   If a list of login names is given on the argument string
   only those people get watched.  If this program is not run
   in background it puts itself in background.  Stops when you
   log off.
   
   Also lets you know when someone is in party if party is installed.
   If you are NOT using party with this as well then please dont define
   PARTY below.
   
   Snooze controls how often watch will look at other users. On slow systems
   this number may need to be high. (Checks every x seconds...)
   10 works well on MY system. (4 users, '386dx-25)

   If you find any bugs with this please leave me mail at:
   elixir@wybbs.mi.org or if that bounces try
   elixir@therip.grand-rapids.mi.us
*/

#include <stdio.h>
#include <utmp.h>
#include <signal.h> 
#include <sys/types.h>
#include <sys/stat.h>

#define PARTY		/* Watch Party too */

#define SNOOZE 10

char WTMP[] = "/etc/wtmp";
char UTMP[] = "/etc/utmp";
#ifdef PARTY
char PARTYFILE[] = "/home/party/log";
long partytime;
int partyactive = 0;
char checkparty = 1;
char checklogin = 1;
struct stat stbuf;
#endif

#define MAXTTY 32
char wason[MAXTTY+1][8] =
	{"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
	 "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""};

int first = 1;
char bell = '';
int wtemp;
int otemp;
int statfd;
char *malloc();

main(argc,argv)
int argc;
char **argv;
{
char **tmpv;
int pid;
register int i;

	/* Get into background, if not there already */
	if (isatty(0))
	{
		/* Start a child, and exit */
		if ((pid = fork()) > 0)
		{
			printf("Started %s process %d\n",argv[0],pid);
			exit(0);
		}
		if (pid < 0)
		{
			printf("%s: Could not fork a process\n",argv[0]);
			exit(1);
		}
	}

	for (first = 1; argv[first][0] == '-'; first++)
		switch (argv[first][1])
		{
		case 's':
			bell = ' ';
			break;
#ifdef PARTY
		case 'l':
			checklogin = 0;
			break;
		case 'p':
			checkparty = 0;
			break;
		default:
			printf("Usage: %s [-pls] user...]\n",argv[0]);
#else
		default:
			printf("Usage: %s [-s] [user...]\n",argv[0]);
#endif
		}

	/* Make a copy of argv */
	tmpv = (char **) malloc((argc+1)*sizeof(char *));
	for (i=0; i<=argc; i++)
		tmpv[i] = argv[i];

	/* Hide names of people being watched */
	if (argc > first)
	{
		argv[first] = "...";
		argv[first+1] = NULL;
	}

#ifdef PARTY
	if (checkparty)
	{
		/* Initialize the party watcher */
		if ((statfd = open(PARTYFILE,0)) < 0 || fstat(statfd,&stbuf))
		{
			printf("%s: Can't stat party file\n",argv[0]);
			if (!checklogin) exit(1);
			checkparty = 0;
		}
		else
			partytime = stbuf.st_mtime;
	}
#endif

/*	signal(SIGHUP,SIG_DFL);
	signal(SIGINT,SIG_IGN);
	signal(SIGQUIT,SIG_IGN);
	signal(SIGTSTP,SIG_IGN);
*/	
#ifdef PARTY
	if (checklogin)
	{
#endif
		/* Open the wtmp file */
		if ((otemp = open (WTMP, 0)) < 0)
		{
			printf("%s: cannot open %s\n",argv[0],WTMP);
			exit(1);
		}
		lseek(otemp,0L,2);	/* Move to end of file */

		/* Open the utmp file */
		if ((wtemp = open (UTMP, 0)) < 0)
		{
			printf("%s: cannot open %s\n",argv[0],UTMP);
			exit(1);
		}
		delta(argc,tmpv);
		close(wtemp);

		/* Open the wtmp file */
		wtemp = otemp;
#ifdef PARTY
	}
#endif

	/* Print names of anyone who is newly on */
	for (;;)
	{
#ifdef PARTY
		if (checkparty)
			chkparty();
		if (checklogin)
#endif
			delta(argc,tmpv);	/* Print changes */
		sleep(SNOOZE);		/* Snooze for a while */
	}
}

check(name,argc,argv)
char *name;
int argc;
char **argv;
{
int i;
	if (*name == 0) return(0);
	if (argc == first) return (1);
	for (i=first;i<argc;i++)
		if (!strncmp(name,argv[i],8))
			return(1);
	return(0);
}

delta(argc,argv)
int argc;
char **argv;
{
register char *was;
register char n;
struct utmp utbuf;

	while (read(wtemp,&utbuf,sizeof(struct utmp)) > 0)
	{
		/* Find appropriate slot of wason table */
		if (utbuf.ut_line[0] == 'c')
			n = MAXTTY;
		else
			if ((n = atoi(utbuf.ut_line+3)) >= MAXTTY)
				continue;
		was = wason[n];

		/* If there was previous user, say he left */
		if (was[0] != 0)
			if (check(was,argc,argv) && (strpbrk(was,"LOGIN")==NULL))
			    printf("out:%c   %-8.8s\r\n", bell,was);

		/* If there is new user, say he entered */
		if (utbuf.ut_name[0] != 0)
			if (check(utbuf.ut_name,argc,argv) && (strpbrk(utbuf.ut_name,"LOGIN")==NULL))
			    printf("in:%c    %-8.8s\r\n",
				bell,utbuf.ut_name);
		strncpy(was,utbuf.ut_name,8);
	}
}

#ifdef PARTY

/* This prints a message if the party file has been changed but hasn't been */
/* changed in the previous three clicks.                                    */
 
chkparty()
{
	if (fstat(statfd,&stbuf))
		return(0);
	if (stbuf.st_mtime > partytime)
	{
		partytime = stbuf.st_mtime;
		if (!partyactive)
			/* This is new activity -- send message */
			printf(" Party in progress. %c\n",bell);
		/* Treat it as active, until 3 clicks of idle time pass */
		partyactive = 3;
	}
	else
	{
		/* If thought it was active, it is less active now */
		if (partyactive) partyactive--;
	}
}
#endif
