*BSD News Article 85878


Return to BSD News archive

Path: euryale.cc.adfa.oz.au!newshost.carno.net.au!harbinger.cc.monash.edu.au!news.rmit.EDU.AU!news.unimelb.EDU.AU!munnari.OZ.AU!spool.mu.edu!uwm.edu!newsfeeds.sol.net!newspump.sol.net!mindspring!uunet!in3.uu.net!193.10.88.100!news00.sunet.se!sunic!mn6.swip.net!seunet!news2.swip.net!nike.volvo.se!cyklop.volvo.se!peter
From: peter hakanson <peter@cyklop.volvo.se>
Newsgroups: comp.unix.bsd.bsdi.misc
Subject: Re: Kicking Off people
Date: Mon, 30 Dec 1996 09:38:51 +0100
Organization: Volvo Corp.
Lines: 465
Message-ID: <Pine.BSD/.3.91.961230093735.10565A-100000@cyklop.volvo.se>
References: <199612300225.UAA13547@news1.anet-dfw.com>
NNTP-Posting-Host: cyklop.volvo.se
Mime-Version: 1.0
Content-Type: TEXT/PLAIN; charset=US-ASCII
In-Reply-To: <199612300225.UAA13547@news1.anet-dfw.com>
Xref: euryale.cc.adfa.oz.au comp.unix.bsd.bsdi.misc:5497

My pleasure.

Snip%snip%snip%snip%snip%snip%snip%snip%snip%snip%snip%snip%
/* @(#) idler.c
 * 
 * Authors: Fred Buck and Steve Manes
 * 
 *      This program is hereby released into the public domain.
 *      Any claims or rights of copyright are relinquished, however
 *      the authors disclaim any liability for the use of this program.
 peter h volvodata modiferat enl:
sighup ignoererat !
fork() drar loss oss fraan terminalen
BUMPROOT = 0
 * redone for bsd/sysV
 *
 * Usage: idler
 *        idler <filename>
 *        idler <tty-basename> <tty-basename> <tty-basename> ...
 * 
 * Inactivity timer daemon for Xenix
 * 
 * This program, when started at bootup by the line "/etc/idler"
 *      (or a line like "/etc/idler <filename>")
 * in /etc/rc.user, watches each terminal for inactivity lasting longer
 * than IDLEMAX minutes, sends a warning message to each inactive
 * terminal, and sends hangup or termination signals (see below) to
 * the processes associated with that terminal if it remains inactive
 * for 60 seconds after the warning.
 * 
 * If command-line arguments are given, they are interpreted as 
 * follows: if the first argument is the name of a readable ordinary
 * file, that file is assumed to contain a list of terminals to watch,
 * one terminal name per line within the file and all subsequent
 * command-line arguments are disregarded.  Otherwise, the command-line
 * arguments are assumed to be names of terminals to watch for inactivity.
 * Examples:
 *
 *      /etc/idler
 *          watches every active terminal;
 *
 *      /etc/idler filename
 *          watches the terminals specified, one per line, in file
 *          "filename";
 *
 *      /etc/idler tty01 tty02 tty03
 *          watches the terminals "/dev/tty01", "/dev/tty02", and
 *          "/dev/tty03".
 *
 * If the first command argument is the name of a readable ordinary
 * file, and the file is changed while this program is running, then 
 * the program will re-read the file at the beginning of each watching
 * cycle.  If the first line of such a file is a number, then that
 * number will be taken as the new value for the number of minutes a
 * user must be inactive to be classified as idle; example:
 *
 *      20
 *      tty01
 *      tty02
 *
 * if appearing in 'filename', and if this program was invoked with
 * 'filename' as its only argument, then the idle-time is reset to 20
 * minutes and only /dev/tty01 and /dev/tty02 are watched for inactivity.
 * 
 * NOTE that terminal names specified on the command line must consist
 * of the terminal basenames only; the program prefixes each such name
 * with "/dev/"; the same observation applies to specifications within
 * <filename>.
 * 
 * In the event that a terminal's processes must be terminated,
 * the process with the lowest number (presumably the login shell)
 * is sent SIGHUP and all other processes are sent SIGTERM so as to
 * kill 'nohup' processes possibly created previously on that terminal.
 * Possibly, a malicious user may have created processes immune to
 * SIGHUP and SIGTERM.  If you fear this, you can change the spec for
 * SIGTERM to SIGKILL: the only warning I offer is that SIGKILL leaves
 * no room for a user process to clean up after itself before it dies.
 * 
 * This program assumes that the 'ps' command takes a '-t' option and
 * that the first line of "ps -t" output contains the string "PID".
 * 
 * The user 'root' is, by default, immune from interference from this
 * program.  To change this, #define BUMPROOT as 1 below.
 */
/* #define DOLOG*/
#include <sys/types.h>
#include <stdio.h>
#include <fcntl.h>
#include <utmp.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#ifdef __bsdi__
#include <termios.h>
#else
#include <termio.h>
#endif
#include <signal.h>
#ifdef DOLOG
#include <syslog.h>
#endif

#define IDLEMAX  8*60             /* minutes of idle time allowed */
#define MAXUSERS 50                     /* maximum number of users at a time */
#define BUMPROOT 0                      /* if 0, 'root' immune to this pgm */
#define LOGUSER  1                      /* keeping a log? */
#define MAILUSER 1			/* notify by mail */
#define LOGFILE  "/var/log/idler.log"    /* sleepy user logname */
#define BSD				/* or sysV */

/* System V Xenix puts more stuff into /etc/utmp than System
 * III Xenix, such as that running on the Tandy 6000, does.
 * If you have a Tandy 6000, delete the following #define.
 */
#ifndef __bsdi__
#define NOTABODY(x) (x.ut_type != USER_PROCESS) /* delete for Tandy 6000 */
#endif

static  char ttynames[MAXUSERS][20];    /* ttynames to monitor */
int     idlemins = IDLEMAX;             /* this can be changed in <filename> */

void    warn(), bump(), urec_cop(), read_tfile();

void   hup();           /* ignore sighup */
main(argc,argv)
int     argc;
char    **argv;
{
        register int i, idlers;
        static  FILE *tfile;
        FILE    *fopen();
        struct  stat argv1;
        static  time_t tfiletime;
        int     utmpfd,fd;
        struct  utmp user, users[MAXUSERS];
#ifdef __bsdi__
	struct  termios dummy;
#else
        struct  termio dummy;
#endif
        char    device[20];

        if (argc > 1) {
                if ((tfile = (FILE *)fopen(argv[1], "r")) != NULL &&
#ifdef __bsdi__
                        ioctl(fileno(tfile), TIOCGETA, &dummy) < 0)
#else
                        ioctl(fileno(tfile), TCGETA, &dummy) < 0)
#endif
								{
                                /* argv[1] not a tty */
                                fstat(fileno(tfile), &argv1);
                                tfiletime = argv1.st_mtime;
                                read_tfile(tfile);
                        }
                else {                          /* argv is list of ttys */
                        for (i=1; i < argc && i < MAXUSERS; ++i)
                        strncpy(ttynames[i-1], argv[i], 19);
                }
        }
/* porting changes peter h*/
#ifdef DOLOG
	openlog("idler", LOG_PID | LOG_CONS, LOG_DAEMON);
	syslog(LOG_CRIT,"Starting");
#endif
	chdir("/");
	umask(0);
        if(fork() != 0)
                exit(0);        /* become non-process grp leader */
#ifdef sysV
        setpgrp();              /* lose controlling term & become grp leader */
#endif
#ifdef BSD
	setpgrp(0,getpid());
	if( (fd = open("/dev/tty", O_RDWR)) >= 0){
		ioctl(fd,TIOCNOTTY,(char *) 0);
		close(fd);
	}
#endif
#ifdef SIGTTOU
	signal(SIGTTOU,SIG_IGN);
#endif
        if(fork() != 0)
                exit(0);        /* first child process */
	
#ifdef __bsdi__
        if ((utmpfd = open("/var/run/utmp", 0)) < 0)
#else
        if ((utmpfd = open("/etc/utmp", 0)) < 0)
#endif
                return(1);
        idlers = 0;

        signal(SIGHUP,hup);                     /* ignore SIGHUP */
        while (1) {
             /*   sleep(idlemins * 60);*/           /* sleep most of the time */
                sleep( 60);           /* sleep most of the time */
#ifdef DOLOG
syslog(LOG_CRIT,"After sleep");
#endif
                if (tfiletime) {                /* should we re-read tfile? */
                        fstat(fileno(tfile), &argv1);
                        if (argv1.st_mtime > tfiletime) {
                                tfiletime = argv1.st_mtime;
                                read_tfile(tfile);
                        }
                }
                idlers = 0;
                lseek(utmpfd,0L,0);
                while ( read(utmpfd, &user, sizeof(user)) ) {
                        if (user.ut_line[0] == '\0')
                                continue;
                        if (user.ut_name[0] == '\0')
                                continue;
#ifdef NOTABODY
                /* for Sys V, skip over non-user entries */
                if (NOTABODY(user))
                        continue;
#endif
                        if (argc > 1) {
                                for (i=0; i < MAXUSERS;++i) 
                                        if (!strcmp(user.ut_line, ttynames[i]))
                                                break;
                                if (i == MAXUSERS)
                                        continue;
                        }
                        if (is_idle(&user))
                                urec_cop(&user, &users[idlers++]);
                }
#ifdef DOLOG
syslog(LOG_CRIT,"if (idlers) ");
#endif
                if (idlers) {
                        for (i=0; i < idlers; ++i)
                                warn(&users[i], 0);
                        sleep(60);
#ifdef DOLOG
syslog(LOG_CRIT,"sleep (60) ");
#endif
                        for (i=0; i < idlers; ++i)
                                bump(&users[i]);
                }
        }
}

int     is_idle( user )         /* returns 1 if idle, 0 otherwise */
struct  utmp *user;
{
        long    int idlemax;
        struct  stat tty;
        char    device[20];
        time_t  todnow;

#ifdef __bsdi__
	if (!strcmp("console",user->ut_line))
		return(0);
#else
        if (user->ut_type == LOGIN_PROCESS || user->ut_type == INIT_PROCESS)
                return(0);
#endif
        if (!BUMPROOT && !strcmp(user->ut_name, "root"))
                return(0);
        sprintf(device, "/dev/%s", user->ut_line);
#ifdef DOLOG
syslog(LOG_CRIT,device);
#endif
#ifndef __bsdi__
        if (user->ut_type == LOGIN_PROCESS || user->ut_type == INIT_PROCESS)
                return(0);
#endif
#ifdef DOLOG
syslog(LOG_CRIT,"stat(%s,&tty)",device);
#endif
        stat(device, &tty);
        time(&todnow);
        idlemax = idlemins * 60;
        return( (todnow-tty.st_mtime) > idlemax &&
                (todnow-tty.st_atime) > idlemax &&
                (todnow-tty.st_ctime) > idlemax );
}

void    bump( user )            /* kills user if idle > 60 secs */
struct  utmp *user;
{
        register int i;
        int     ttyfd;
        FILE    *processes, *popen();
        int     ps_num;
        time_t  todnow;
        struct  stat tty;
        char    device[20], cmdstr[50];

        if (user->ut_line[0] == '\0' || user->ut_line == NULL)
                return;
        time(&todnow);
        sprintf(device,  "/dev/%s", user->ut_line);
        stat(device, &tty);
        if ((todnow-tty.st_atime) < 60)
                return;                 /* guess user heeded warning */

        warn(user, 1);

        /* to get the user's process ID numbers, we read
         * from the pipeline "ps -t <ttyname> | awk '!/PID/ {print $1}'"
         */
#ifdef __bsdi__
        strcpy(cmdstr,"ps -t /dev/");
#else
        strcpy(cmdstr,"ps -t ");
#endif
        if (*user->ut_line)
                strcat(cmdstr, user->ut_line);

        strcat(cmdstr, " | awk '!/PID/ {print $1}' | sort");
        processes = popen(cmdstr,"r");
        i = 0;
        while ( fscanf(processes,"%d",&ps_num) != EOF) 
                if (ps_num > 1)                 /* never swapper or init */
                        kill(ps_num, (((i++) == 0) ? SIGHUP : SIGTERM));

        /* We use SIGTERM for processes other than the login
        * shell to catch any 'nohup' commands.
        */
        pclose(processes);
}

void    urec_cop( ufrom, uto )  /* copies utmp structs */
struct  utmp *ufrom, *uto;
{
        register int i;
        char    *to, *from;

        to = (char *) uto;
        from = (char *) ufrom;
        for (i=0; i < sizeof(struct utmp); ++i)
                *(to++) = *(from++);
}

void    read_tfile( fp )        /* loads 'ttynames' array from file */
FILE    *fp;
{
        register int i;
        int     newtimeout;
        char    *strchr();

        fseek(fp, 0L, 0);
        i = 0;
        while (i < MAXUSERS && fgets(ttynames[i], 20, fp) != NULL) {

                /* if first line is a number, then that's the new timeout */
                if (i == 0 && (newtimeout = atoi(ttynames[0]))) {
                        idlemins = newtimeout;
#ifdef DOLOG
syslog(LOG_CRIT,"new timeout");
#endif
                        continue;
                }
                *(ttynames[i] + strlen(ttynames[i]) -1) = '\0';
                ++i;
        }
}

/*----------------------------------------------------------------------
 *      warn(user, kickoff)
 *      warn sleepy user. 
 *
 *      mode =  0:  send warning message
 *              1:  send him goodbye message, mail and log 'em
 *----------------------------------------------------------------------*/
void    warn( user, mode )
struct  utmp *user;
int     mode;
{
        int     utty;
        char    device[20];
        FILE    *fid;
        time_t  now;
        char    buff[20];
        char    *warn0 =
        "\n\007One more minute without input and you will be disconnected!\n";
        char    *warn1 = "\n\nTime's up. Goodbye!\n\n";

        sprintf(device, "/dev/%s", user->ut_line);
        utty = open(device, (O_RDWR | O_NDELAY));
        if (mode == 0)  write(utty, warn0, strlen(warn0));
        else            write(utty, warn1, strlen(warn1));
        close(device);

        if (mode == 0)
                return;
        
        /* user's been kicked off */
        time(&now);
        if (LOGUSER && (fid = fopen(LOGFILE, "a+")) > 0) {
#ifdef __bsdi__
                fprintf(fid, "%s on /dev/%s killed: %s", user->ut_name,user->ut_line, ctime(&now));
#else
                fprintf(fid, "%s killed: %s", user->ut_user, ctime(&now));
#endif
                fclose(fid);
        }

        /* mail user */
#ifdef MAILUSER
#ifdef __bsdi__
        sprintf(buff, "mail -s \"Idler Disconnect\" %s", user->ut_name);
#else
        sprintf(buff, "mail %s", user->ut_user);
#endif
        if ( !(fid = popen(buff, "w")) )
                return;
        time(&now); /* added */
        fprintf(fid, "\n\tFrom Idler at %s", ctime(&now));
        fprintf(fid, "\n\tYou were automatically logged off ");
        fprintf(fid, "\n\tfor terminal inactivity more then %d minutes.\n",idlemins);
        pclose(fid);
#endif
}
void  hup()             /* SIGHUP handler */
{
        signal (SIGHUP,hup);    /* re-arm */
#ifdef DOLOG
syslog(LOG_CRIT,"SIGHUP");
#endif
}

Snip%snip%snip%snip%snip%snip%snip%snip%snip%snip%snip%snip%

--
Peter Hakanson  VolvoData Dep 2580 phone +46 31 66 74 27

On Sun, 29 Dec 1996 david@news1.anet-dfw.com wrote:

> I would like a copy please.
> 
> 
> DB
> 
> In article <5a6is6$n66@nike.volvo.se> you wrote:
> : Once upon a time i picked up a program called 'idler' 
> : that killed idle users (settable)
> 
> : It was originally written for xenix, but i have used it
> : on RT/PC, hpux and bsdi. 
> 
> : Want a copy ?
> : Bryan Logan (blogan@host1.dia.net) wrote:
> : : OK, we sometimes have these people that just dial-in and they leave.  We 
> : : have some that are PPP'd in for 24 hrs. sometimes.  So henceforth the 
> : : obvious question:
> 
> : : How can I kick off people who are PPP'd in without telnetting to the 
> : : router or flipping the modem? (I'm not at the office). 
> 
> : : Thanks.
> : : --
> : : -----
> : : Bryan Logan
> : : blogan@host1.dia.net
> : : http://www.dia.net/~blogan   <---Now loaded with ActiveX!
> 
> : --
> : --
> : NO JUNK EMAIL <peter@cyklop.volvo.se>
> : Peter Hakanson  VolvoData Dep 2580 phone +46 31 66 74 27
>