/*
 * A program to sit quietly in the background and check for terminal
 * activity.  Checks will be made once a minute.  (Can be altered with
 * +arg.)  If the terminal line has been idle more than 20 minutes
 * (can be altered with -arg), a system call will be made to kill the
 * parent process id. (So this should be run from the login shell.)
 *
 * This program will be useful if you loose your connection but your
 * session is not terminated.  Written (stolen) 17 January 1992.
 *
 * Based on deadkill.c with modem code removed -- just kill the login
 * shell.  deadkill.c was based directly on hangself.c, which in turn
 * is based heavily on my clock100 program (sleeps, checks activity,
 * and spits) and my hang_up program (spits to reset modem).
 *
 *
 * Brent Chivers		Mail Stop Z268
 * Systems Engineer		The MITRE Corporation
 * bchivers@mitre.org		7525 Colshire Drive
 * (703) 883-6734		McLean, VA 22102
 */

#include <stdio.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>

#define BELL		"\007"		/* ASCII bell character */
#define MINUTES		* 60		/* convert to seconds (unit used by sleep) */
#define WARNINGS	3		/* number of warnings to give */

main (argc, argv)
char  **argv;
{
    int check_time = 1;			/* update interval (default 1 minute) */
    int hang_up_time = 20;		/* try to hang up and reset the modem if idle this long */
    int check_secs;			/* check_time in seconds */

    int parent;				/* process id of shell that started us (we hope it's the login shell) */
    int pro;				/* process id of forked child */
    char origlogin[20];			/* login name when started */
    char *getlogin();			/* method to obtain login name */
    char devname[20];			/* device file name */
    char *ttyname();			/* function to get device file name */
    struct stat stbuf;			/* device status, especially access time */

    while (--argc > 0)
	{
	argv++;
	if (**argv == '-')
	    {
	    char *c = *argv;
	    *c++;			/* find character following "-" */
	    if ((*c >= '0') && (*c <= '9'))
		hang_up_time = atoi (c);
	      else
		badargs(*argv);		/* print "bad argument message */
	    }
	  else if (**argv == '+')
	    {
	    char *c = *argv;
	    *c++;			/* find character following "+" */
	    if ((*c >= '0') && (*c <= '9'))
		check_time = atoi (c);
	      else
		badargs(*argv);		/* print "bad argument message */
	    }
	  else if ((**argv >= '0') && (**argv <= '9'))
	    hang_up_time = atoi (*argv);
	  else
	    badargs(*argv);		/* print "bad argument message */
	}

    strcpy(origlogin,getlogin());	/* make note of original login */

    parent = getppid();			/* find out who started/owns us */
    pro = fork();

    if (pro)
	{
	printf ("Auto-Shell-Kill process created.  PID: %d  PPID: %d\n", pro, parent);
	printf ("Idle time will be checked ");	/* we take special pains with singular/plural here */
	if (check_time == 1) printf ("once a minute");
		else	     printf ("every %d minutes", check_time);
#if (WARNINGS == 1)
	printf (".\nYou will receive a warning message after %d minutes of idle time.\n", hang_up_time);
#else
	printf (".\nYou will receive %d warning messages after %d minutes of idle time.\n", WARNINGS, hang_up_time);
#endif
	printf ("Process %d will be killed after %d minutes of idle time.\n",
	    parent, ((WARNINGS * check_time) + hang_up_time));

	_exit(0);
	}

    strcpy(devname, ttyname(1));		/* get dev name */
    check_secs = check_time MINUTES;		/* convert checking interval to seconds */
    hang_up_time = hang_up_time MINUTES;	/* convert hang-up time limit to seconds */
						/* overwrite the minute value -- we don't need it anymore */
    while (1)
	{
	char thislogin[20];			/* current login name */
	char kill_command [40];			/* formatting buffer */
	register struct tm *nowt;
	long now, lastaction, diff;
	int idle;				/* terminal busy? (boolean) */
	int warned;				/* have we warned the user that we're going to kill the PPID (shell)? */
						/* it's nice to give a warning first, eh? */
	int i;					/* loop counter */

	strcpy(thislogin,getlogin());		/* still logged in? */
	if (strcmp(origlogin, thislogin)) exit(0);

	time (&now);
	stat(devname, &stbuf);			/* check terminal activity */
	lastaction = stbuf.st_atime;
	diff = now - lastaction;		/* idle time in seconds */
	if (diff < 0) diff = 0;
	idle = (diff > hang_up_time);

	if (idle)				/* if the line has been sitting idle, */
	    {
	    if (warned >= WARNINGS)		/* and the user has already received enough warnings, */
		{				/* then go ahead and kill this session */
		printf ("\n\n\t _______________________________________________\n");
		printf ("\t/\t\t\t\t\t\t\\\n");
		printf ("\t|\t\tTIME'S UP -- BYE BYE!!%s\t\t|\n", BELL);
		printf ("\t\\_______________________________________________/\n");
		system ("echo \"I've been idle too long.\" | /usr/ucb/mail -s \"killing idle login\" root\n");
		sprintf (kill_command, "kill -1 %d\n", parent);	/* make the killer command */
		system (kill_command);		/* become an orphan */
		/* printf (kill_command);	/* just for testing... */
		}
	      else
		{
		printf ("\n\n\t _______________________________________________\n");
		printf ("\t/\t\t\t\t\t\t\\\n");
		printf ("\t|\t");
#if (WARNINGS > 1)
		if (warned == (WARNINGS - 1)) printf ("LAST ");
#endif
		printf ("WARNING!!\tHANG-UP ALERT!!%s\t\t|\n", BELL);
		printf ("\t|\t\t\t\t\t\t|%s\n", BELL);
		printf ("\t|    YOUR TERMINAL HAS BEEN IDLE TOO LONG!!%s\t|\n", BELL);
		printf ("\t|    I WILL KILL YOUR SHELL IF THERE IS NO%s\t|\n", BELL);
		printf ("\t|    ACTIVITY FROM YOUR END IN ");
		if ((check_time == 1) && ((WARNINGS == 1) || (warned == (WARNINGS - 1))))
		    printf ("60 SECONDS!!%s\t|\n", BELL);
		  else
		    printf ("%d MINUTES!!%s\t|\n", (check_time * (WARNINGS - warned)), BELL);
		printf ("\t\\_______________________________________________/\n");
		}
	    warned++;				/* the user has been warned now! */
	    }
	  else
	    warned = 0;				/* hang-up not iminent, no pending warnings */

	if (warned == WARNINGS)			/* no more warnings before clobbering the modem */
	    {
	 /* printf ("\t\tThat was your last warning....\n"); */
	    for ( i = 0 ; i < check_secs ; i++ )
		{
		sleep (1);			/* wait a second */
		if (idle)
		    {
		    printf ("%s", BELL);	/* then ring the terminal bell */
		    fflush (stdout);
		    }
		time (&now);
		stat(devname, &stbuf);		/* check terminal activity */
		lastaction = stbuf.st_atime;
		diff = now - lastaction;	/* idle time in seconds */
		if (diff < 0) diff = 0;
		idle = (diff > hang_up_time);
		}
	    }
	  else
	    sleep (check_secs);			/* wait a bit, then do it all again */
    }
}

badargs(botch)			/* print bad argument message and exit */
char *botch;			/* invalid argument */
{
    fprintf (stderr, "bad agument: %s\n", botch);
    fprintf (stderr, "usage:  suicide [-/+interval] \n");
    listoptions();
}

listoptions()			/* list valid options */
    {
    printf ("[-]number:\tidle time limit before warning and hang-up (default 20 minutes)\n");
    printf (" +number:\tidle check interval (default 1 minute)\n");
    printf ("[-]?:\t\tdisplay this help screen\n");
    exit (1);
    }
