/*
		"@(#) autologout.c by David Dickson" 

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/
#include    <stdio.h>
#include    <signal.h>
#include    <string.h>
#include    <sys/types.h>
#include    <sys/stat.h>
#include    <utmp.h>
#define     GRACE       60  /* grace time (sec) for user reply */
#define     KWAIT       20  /* time to wait after kill (in sec.) */
#define     WARNING     1   /* a warning message */
#define     LOGOFF      2   /* a log-off message */
#define     NOLOGOFF    3   /* a log-off failure message */
#define     SEC         60  /* seconds per minute */
#define     TIMEMAX     30  /* maximum idle time (minutes) */

struct utmp *utmpp;         /* pointer to utmp file entry */
char        *ctime();       /* returns pointer to time string */
struct utmp *getutent();    /* returns next utmp file entry */

main()
{
    if (fork())             /* the parent process */
        exit(0);            /* exits */
    /* the child processes all utmp file entries: */
    while ((utmpp = getutent()) != (struct utmp *) NULL)
        check_idle();
    exit(0);    /* done, so bye */
}

check_idle()    /* select utmp entries older than TIMEMAX */
{
    char        dev[24], name[12];
    struct stat status, *pstat;
    time_t      idle, pres_time, start, time();

    pstat = &status;    /* point to status structure */
    if (utmpp->ut_type != USER_PROCESS) /* if not user process */
        return(0);                      /* skip the utmp entry */
    strcpy(dev, "/dev/");   /* add "/dev/" directory prefix */
    strcat(dev, utmpp->ut_line);    /* append basename of port */
    if (stat(dev, pstat))   /* if can't get status for port */
        bailout("can't get status of user's terminal", 1);
    pres_time = time((time_t *)0);  /* get current time */
    /* idle time is current time less last access time: */
    idle = (pres_time - pstat->st_atime) / SEC;
    if (idle < TIMEMAX)     /* if user was recently active */
        return(0);          /* ignore this utmp entry */
    strncpy(name, utmpp->ut_user, 8);   /* else get user name */
    name[8] = '\0';          /* null terminate user name string */
    mesg(WARNING, name, dev, (int)idle); /* send warning to user */
    if (stat(dev, pstat))
        bailout("can't get status of user's terminal", 2);
    start = pstat->st_atime;    /* start time for countdown */
    sleep(GRACE);
    if (stat(dev, pstat))
        bailout("can't get status of user's terminal", 3);
    if (start < pstat->st_atime)    /* user did something */
        return(0);
    else {  /* user abandoned terminal */
        if (!killit(utmpp->ut_pid))
            mesg(NOLOGOFF, name, dev, (int)idle); /* couldn't kill */
        else
            mesg(LOGOFF, name, dev, (int)idle); /* okay */
        return(0);
    }
}

mesg(flag, name, dev, idle) /* mail to user and log message */
int     flag, idle;     /* flag indicates message type */
char    *name, *dev;
{
    char    mbuf[256];  /* message buffer */
    time_t  tvec;
    FILE    *fopen(), *fp, *log, *mprog;

    time(&tvec);    /* store time in tvec */
    if ((log = fopen("/usr/adm/logoutlog", "a")) == (FILE *) NULL)
        bailout("can't open log file", 4);
    if (flag == WARNING) {  /* process warning message */
        if ((fp = fopen(dev, "w")) == (FILE *) NULL)
            bailout("can't open user's terminal", 5);
        fprintf(fp,
            "%s: You've been idle for %d min.\07\n", name, idle);
        fprintf(fp,
            "you'll be logged off in 60 sec. unless you hit return:");
        fclose(fp);
        fprintf(log, "** WARNING ** %s %s (%d min idle time) %s",
            name, dev+5, idle, ctime(&tvec)+3);
    }
    if (flag == LOGOFF) {   /* process log-off message */
        fprintf(log, "** LOGOFF ** %s %s (%d min idle time) %s",
            name, dev + 5, idle, ctime(&tvec) + 3);
        sprintf(mbuf, "/bin/mail %s", name);
        /* open pipe to mail program for writing */
        if ((mprog = popen(mbuf, "w")) == (FILE *) NULL)
            bailout("can't use /bin/mail program", 6);
        fprintf(mprog, "Subject: Excess Idle Time\nLogged \
off - excess idle time - %s %s\ntty = %s\n",
            name, ctime(&tvec), dev + 5);
        fclose(mprog);
    }
    if (flag == NOLOGOFF) {  /* send mail to root if can't kill */
        fprintf(log, "** LOGOFF-FAIL ** %s (pid = %d) \
%s (%d min idle time) %s",
            name, utmpp->ut_pid, dev+5, idle, ctime(&tvec)+3);
        sprintf(mbuf, "/bin/mail root");
        if ((mprog = popen(mbuf, "w")) == (FILE *) NULL)
            bailout("can't use /bin/mail program", 7);
        fprintf(mprog, "Subject: Can't logoff %s\nCan't Log off \
- %s %s\ntty = %s\n", name, name, ctime(&tvec), dev+5);
        fclose(mprog);
    }
    fclose(log);
    return(0);
}

killit(pid) /* terminate process using SIGHUP, then SIGKILL */
int pid;
{
    kill(pid, SIGHUP);  /* first send "hangup" signal */
    sleep(KWAIT);
    if (!kill(pid, 0)) {    /* SIGHUP might be ignored */
        kill(pid, SIGKILL); /* then send sure "kill" signal */
        sleep(KWAIT);
        if (!kill(pid, 0))
            return(0);  /* failure--refuses to die! */
        else
            return(1);  /* successful kill with SIGKILL */
    } else
        return(1);  /* successful kill with SIGHUP */
}

bailout(message, status) /* display error message and exit */
int     status;     /* exit status */
char    *message;   /* pointer to the error message */
{
    fprintf(stderr, "autologout: %s\n", message);
    exit(status);
}
