#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <utmp.h>
#include <time.h>
#include <sys/file.h>
#include <signal.h>
#include <strings.h>

#if defined(_AIX)
#define SYSV_UTMP
#endif

#if defined(__NetBSD__) || defined(linux)
#define UTMP_FILE "/var/run/utmp"
#else
#define UTMP_FILE "/etc/utmp"
#endif

#define	EVER	;;

#define	UUCP	1		/* mach!user */
#define	ARPA	2		/* user@mach */
#define UNAMELEN 8		/* Max length of uname */

#define	DEBUG(x)    if(debug)\
    {\
    	fprintf(stdout,x);\
	fflush(stdout);\
    }


typedef struct	amigos
	{
		char	uname[14];
		int	poly,
			tmpcnt;
		struct	amigos	*prev,
				*next;
	}AMIGOS;

FILE	*debugfd;

int	debug = 0,
        idformat = ARPA,
	havetime = 0,
	world = 0;

#define	HOSTNAMELEN	20

AMIGOS	*ap = (AMIGOS *) NULL,
	*app = (AMIGOS *) NULL,
	*apsort = (AMIGOS *) NULL;

static	char	startmsg[] = "[",
		*hellomsg[] = {"Here is", "Here are"},
  		*byemsg[] = {"There goes", "There go"},
    		*anothermsg = "another ",
    		*amsg = "a ",
    		*polymsg = "poly",
    		*comma = ",",
    		*andmsg = " and ",
    		*endmsg = ".]";


char	*malloc(),
	*getlogin(),
	*dotp,
	h_msgbuf[256],
	b_msgbuf[256],
	tmp[80],
	terminal[16],
	master[14],
	ident[HOSTNAMELEN + 20],	/* for:  argus@ wherever */
	host[HOSTNAMELEN],
	*junkp,
	*uname,
	debugstr[80];


main(argc,argv)
int	argc;
char	*argv[];
{

	char	*prog_name;
	
	int	fstat(),
		ufd,
		mtime,
		h_pluralp = 0,
		b_pluralp = 0,
		pid,
		hellop = 0,
		byep = 0;

	int found, killme = 0;

	AMIGOS	*addluser();

	struct	stat	sbuf;

	struct	utmp	utrec;

	prog_name = (prog_name=rindex(argv[0],'/')) ? (++prog_name) : argv[0];

	strcpy(terminal,ttyname(fileno(stderr)));
	strcpy(master,getlogin());

	DEBUG("master  name=");
	DEBUG(master);
	DEBUG("   terminal=");
	DEBUG(terminal);
	DEBUG("\n");
	
	pid = fork();
	if(pid != 0)
		exit(0);

#ifdef SIGNAL
	signal(SIGQUIT,SIG_IGN);	/* Ignore Signals */
	signal(SIGTERM,SIG_IGN);
	signal(SIGHUP,SIG_IGN);
	signal(SIGINT,SIG_IGN);
#endif SIGNAL
	nice(10);			/* Lower the priority */

#if 0
	strcpy(host,"localhost");	/* i think the call is uname(2),
					     i have to check */
#else
	gethostname(host, HOSTNAMELEN);
#endif

	if(dotp = index(host,'.'))	 /* kill domain name, if any ... */
	  	*dotp = '\0';		


	while(argc-- > 1)
	{
	    	if(*argv[argc] == '-')
		{
		    DEBUG("Parsing Options...");
		    while(*(++argv[argc]))
		       switch(*argv[argc])
		       {
		               case 'a':
			           idformat = ARPA;
				   break;

			       case 't':
				   havetime = 1;
				   break;

			       case 'u':
				   idformat = UUCP;
				   break;

			       case 'x':
				   debug = 1;
				   break;

			       case 'w':
				   world = 1;
				   break;
		       }
		       continue;
		}
		
		addluser(argv[argc]);
	 }



/* option of UUCP style name, default is arpa ... */

	if(idformat == UUCP)
	      sprintf(ident,"%s!%s",host,prog_name);
	else
	      sprintf(ident,"%s@%s",prog_name,host);

	ufd = open(UTMP_FILE, O_RDONLY);
	DEBUG("opened utmp...\n");

	mtime = 0;	/* so we can get modified... */

	for(EVER)
	{
		fstat(ufd, &sbuf);
	
		if(mtime == sbuf.st_mtime)
		{
			DEBUG("No login changes since last check...\n");
			sleep(5);
			continue;
		}

		DEBUG("Argus update...\n");

		mtime = sbuf.st_mtime;
		h_pluralp = 0;
		b_pluralp = 0;
		hellop = 0;
		byep = 0;

		for(ap = app; ap != ((AMIGOS *) NULL); ap = ap->next)
		{
			DEBUG("Resetting count for ");
			DEBUG(ap->uname);
			DEBUG(".\n");
			app->tmpcnt = 0;
		}
		DEBUG("tmpcounts modified...\n");

		lseek(ufd, 0L, 0);

		DEBUG("master terminal = ");
		DEBUG(terminal);
		DEBUG("\n");
		
		found = 0;
		while(read(ufd, &utrec, sizeof(struct utmp)))
		{
			if (!strcmp(terminal+5,utrec.ut_line)) {
#ifdef SYSV_UTMP
			    if(!strcmp(master, utrec.ut_user) &&
			       utrec.ut_type == USER_PROCESS)
#else
			    if(!strncmp(master, utrec.ut_name, UNAMELEN))
#endif
				    found++;
			    else
				    continue;
			}
			
#ifdef SYSV_UTMP
			if(!strcmp(utrec.ut_user, "") ||
			   utrec.ut_type != USER_PROCESS)
#else
			if(!strncmp(utrec.ut_name,"",UNAMELEN))
#endif
				continue;
#ifdef SYSV_UTMP
			uname = utrec.ut_user;
#else
			uname = utrec.ut_name;
#endif

			DEBUG("Valid utmp record: user ");
			DEBUG(uname);
			DEBUG(" terminal ");
			DEBUG(utrec.ut_line);
			DEBUG("\n");

			if(world)
			{    
			        ap = addluser(uname);
				ap->tmpcnt++;
				continue;
			}

			DEBUG(".\ngrunging...");
			for(ap = app; (ap != (AMIGOS *)NULL) ; ap = ap->next)
			{
				DEBUG(ap->uname);
				if(strncmp(uname, ap->uname, UNAMELEN))
					continue;

				DEBUG("GOT IT!!!\n");
				sprintf(debugstr,"Old count %d (poly %d), ",ap->tmpcnt, ap->poly);
				DEBUG(debugstr);
				ap->tmpcnt++;
				sprintf(debugstr,"new count %d.\n",ap->tmpcnt);
				DEBUG(debugstr);
			}

		}
		if (killme || !found)
		    {
			DEBUG("Killing the faithful dog...\n");
			kill(getpid(),SIGKILL);
		    }

		strcpy(h_msgbuf,"");		/* clear buffer */
		strcpy(b_msgbuf,"");
		DEBUG("Display TIME!!!!!!!\n");

		for(ap = app; ap != (AMIGOS *)NULL; ap = ap->next)
		{
			DEBUG("\nworking on ");
			sprintf(debugstr,"%s...",ap->uname);
			DEBUG(debugstr);
			if(ap->tmpcnt == ap->poly)
			{
				DEBUG("NO CHANGE.\n");
				ap->tmpcnt = 0;
				continue;
			}

			if(ap->tmpcnt < ap->poly)	/* one or more left */
			{
				if(byep)
					strcat(b_msgbuf,comma);
				byep++;
				DEBUG("DIFFERENT (logout) --");
				if(ap->tmpcnt)		/* at least one */
				{
					DEBUG("at least one left -- ");
					if((ap->poly - ap->tmpcnt) > 1)
					{
						DEBUG("MORE THAN  ONE LEFT -- ");
						strcat(b_msgbuf," ");
						sprintf(tmp,"%d ",ap->poly - ap->tmpcnt);
						strcat(b_msgbuf, tmp);
						strcat(b_msgbuf, ap->uname);
						strcat(b_msgbuf,"s");

					}
					else
					{
						DEBUG("ONLY ONE LOGGED OUT -- ");
						strcat(b_msgbuf," ");
						strcat(b_msgbuf,amsg);
						strcat(b_msgbuf, ap->uname);
					}
				}
				else
				{
						DEBUG("AND THEN THERE WERE NONE --");
						strcat(b_msgbuf," ");
						strcat(b_msgbuf,ap->uname);
				}

			}
			else
			{
				if(hellop)
					strcat(h_msgbuf,comma);
				hellop++;
				if(((ap->tmpcnt - ap->poly) > 1) && ap->poly)
				{
					DEBUG("YOW!  MORE FRIENDS -- ");
					sprintf(tmp," %d more ",ap->tmpcnt - ap->poly);
					strcat(h_msgbuf,tmp);
					strcat(h_msgbuf,ap->uname);
					strcat(h_msgbuf,"s");
				}
				else if((ap->tmpcnt - ap->poly) > 1)
				{
					DEBUG("Yow!!  MANY MORE -- ");
					sprintf(tmp," %d ",ap->tmpcnt - ap->poly);
					strcat(h_msgbuf,tmp);
					strcat(h_msgbuf,ap->uname);
					strcat(h_msgbuf,"s");
				}
				else if(ap->poly)
				{
					DEBUG("ONE MORE (poly) -- ");
					strcat(h_msgbuf," ");
					strcat(h_msgbuf,anothermsg);
					strcat(h_msgbuf,ap->uname);
				}
				else
				{
					DEBUG("JUST LOGGED IN --");
					strcat(h_msgbuf, " ");
					strcat(h_msgbuf,ap->uname);
				}
			}
			h_pluralp++;
			DEBUG("END OF LOOP -- RESETTING\n");
			ap->poly = ap->tmpcnt;
			ap->tmpcnt = 0;
			sprintf(debugstr,"NEW poly for %s -- %d, tmp = %d.\n",ap->uname, ap->poly, ap->tmpcnt);
			DEBUG(debugstr);
			
		}
		
		if(hellop)
		        printmsg(hellop, hellomsg, h_msgbuf);
		
		if(byep)
		  	printmsg(byep, byemsg, b_msgbuf);
	}
}

		


				




AMIGOS	*addluser(luser)			/* add another user to watch */
char	*luser;
{
	DEBUG("Working on name ");
	DEBUG(luser);
	DEBUG(".\n");

	if(app != (AMIGOS *) NULL)
	{
	    for(apsort = app; (apsort->next != (AMIGOS *) NULL) && (strncmp(apsort->uname, luser, UNAMELEN) < 0) ; apsort = apsort->next)
	    {
		  DEBUG("\tpassing by ");
		  DEBUG(apsort->uname);
		  DEBUG(".\n");
	    }

	    if(!strncmp(luser,apsort->uname, UNAMELEN))
	          return(apsort);
	}

	ap = (AMIGOS *) malloc(sizeof(AMIGOS));
	strncpy(ap->uname, luser, UNAMELEN);
	strcat(ap->uname, "");
	ap->poly = ap->tmpcnt = 0;
	ap->next = ap->prev = (AMIGOS *) NULL;
	DEBUG("inserting into chain...");
						
	if(app == (AMIGOS *) NULL)
	{
	    app = ap;
	    return(ap);
	}

	if((apsort->next == (AMIGOS *) NULL) && (strcmp(apsort->uname, ap->uname) < 0))
	{
		DEBUG("appending name...");
		apsort->next = ap;
		ap->prev = apsort;
		ap->next = (AMIGOS *)NULL;
		DEBUG("previous is now ");
		DEBUG(ap->prev->uname);
		DEBUG(", and next is NULL (this is tail).\n");
		return(ap);
	}

	ap->prev = apsort->prev;    /* insert into chain */
	ap->next = apsort;
	if(apsort->prev != (AMIGOS *) NULL)
	{
		DEBUG("backpointers fixed...");
		apsort->prev->next = ap;
	}
	else
	{
		DEBUG("making this one new head of chain...");
		app = ap;
	}
	apsort->prev = ap;

	DEBUG("previous is ");
	DEBUG(ap->prev->uname);
	DEBUG(", next is ");
	DEBUG(ap->next->uname);
	DEBUG(".\n");

	return(ap);
}



printmsg(flag,header,buf)
int	flag;
char	**header,
        *buf;

{

        char	*junkp;
	char    timebuf[16];

	if (havetime) {
		long time(), ntime;
		struct tm *ltime, *localtime();
		ntime = time(0);
		ltime = localtime(&ntime);
		sprintf(timebuf, " (%02d:%02d): ", ltime->tm_hour,
			ltime->tm_min);
	} else
		strcpy(timebuf, ": ");

	if(flag > 1)
        {
		for(junkp = buf + strlen(buf); *junkp != ',';junkp--);
		if(flag > 2)
		{
			junkp++;	/* points to space */
			*junkp = '\0';
			junkp++;
		}
		else
		{
			*junkp = '\0';	/* Kill the comma*/
			junkp += 2;
		}
		fprintf(stderr,"%s%s%s%s%s%s%s%s\n", startmsg, ident, timebuf, header[(flag > 1)], buf,andmsg,junkp,endmsg);
	}
	else
		fprintf(stderr,"%s%s%s%s%s%s\n", startmsg, ident, timebuf, header[(flag > 1)], buf,endmsg);

}
