v07i079: Log out idle users (untamo revised)

sources-request at mirror.UUCP sources-request at mirror.UUCP
Sat Dec 6 07:51:28 AEST 1986


Submitted by: Wombat <rsk at j.cc.purdue.edu>
Mod.sources: Volume 7, Issue 79
Archive-name: untamo2

Hopefully, this one will be easier for the net at large to deal with.

---Rsk

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	README
#	Makefile
#	untamo.cf
#	doc
#	untamo.8l
#	untamo.cf.5l
#	insque.c
#	list.c
#	untamo.c
#	warn.c
#	zap.c
#	parse.y
#	scan.l
#	ttydev.h
#	untamo.h
cat << \SHAR_EOF > README
Untamo is a locally developed daemon which periodically wakes up and
logs off idle terminals; it also can deal with multiply-logged in
users.  It is configurable without recompilation, and features tunable
parameters such as maximum allowed idle time, maximum allowable
multiple logins, exemption lists, and so on.  (Note: session limits
do not work at the moment.)

We use this program to ensure availability of one of our scarcer
resources: terminals.  Others may find it useful for different reasons;
preventing users from leaving a terminal logged-in and unattended
for hours is probably a reasonable security measure.

The original posting caused a deluge of mail, as the sources contained
references to local include files.  This has been fixed by #ifdef'ing
the code which applies only locally.  This version has been successfully
compiled on a Sequent Balance 21000 running Dynix v2.0.6, a CCI 6/32
running 4.3bsd, a DEC VAX-8600 running 4.3bsd, a Gould PowerNode 9080
Running UTX-32 Release 1.3, and and a VAX-11/780 running 4.2bsd.

Please address correspondence concerning untamo to "doc" on this machine;
the original author has left Purdue, but Craig Norborg (aka "doc") is
reasonably familiar with the program.
-- 
Rich Kulawiec, pucc-j!rsk, rsk at j.cc.purdue.edu, rsk at purdue-asc.arpa
SHAR_EOF
cat << \SHAR_EOF > Makefile
# Makefile for /usr/src/new/untamo
#
# Author : Craig Norborg, Purdue University Computing Center
# To configure this for your system, be sure to check out untamo.h
# Also, if compiling on generic 4.2BSD system, be sure to make dependancies
# or remove all occurances of "rld" stuff from dependancies lines.  -doc

# C compiler flags for PUCC.
# Note, when the DEBUG flag is set, the RLD_FILE is changed to /tmp/rlds.
#INCLUDE = 
#DEFS = -DPUCC -DBSD4_2
#CFLAGS = -O ${DEFS} ${INCLUDE}
#LIBS = -lacct

# C compiler flags for generic BSD4_2
INCLUDE = 
DEFS = -DBSD4_2
CFLAGS = -O ${DEFS} ${INCLUDE}
LIBS =

#install flags
DEST = /usr/new/etc
FDEST = /usr/local/lib
OWNER = doc
GROUP = system
MODE = 751
SHMODE = 755
FMODE = 644

# Source files to be mkdepended
SRC = insque.c list.c untamo.c warn.c zap.c
SRCl = parse.y scan.l
SRCg = parse.c scan.c
HDR = ttydev.h untamo.h
OBJ = insque.o list.o parse.o scan.o untamo.o warn.o zap.o

# programs that need explicit make lines, plain files
NSTD = untamo
FILE = untamo.cf doc
MAN  = untamo.8l untamo.cf.5l

all: ${NSTD} ${FILE}

clean:
	rm -f a.out ${OBJ} core errs lint.errs Makefile.bak *.s tags \
		${SRCg} ${NSTD} y.tab.* lex.yy.c

depend: ${SRC} ${SRCg} ${HDR}
	maketd -a ${DEFS} ${INCLUDE} ${SRC} ${SRCg}

install: all 
	-for p in ${NSTD}; do \
		install -c -m ${MODE} -o ${OWNER} -g ${GROUP} -s $$p ${DEST}; \
	done
	for p in ${FILE}; do \
		install -c -m ${FMODE} -o ${OWNER} -g ${GROUP} -s $$p ${FDEST};\
	done

lint: ${SRC} ${SRCg}
	lint -hxn ${DEFS} ${SRC} ${SRCg}
	
print: 
	lpr -p -Pstaff -J"Untamo Source" Makefile ${SRCl} ${SRC} ${HDR}

shar:
	shar README Makefile ${FILE} ${MAN} ${SRC} ${SRCl} ${HDR} > untamo.shar

source: ${SRC} ${SRCl} ${HDR} ${MAN} ${FILE}

spotless:
	rm -f a.out ${OBJ} core errs lint.errs Makefile.bak y.tab.* yacc.act\
		yacc.tmp *.s ${NSTD} tags parse.c scan.c
	rcsclean ${SRC} ${SRCl} ${HDR} ${FILE} ${MAN}

tags: ${SRCS} ${SRCg} tags
	ctags ${SRC} ${SRCg}

${SRC} ${SRCl} ${HDR} ${FILE} ${MAN}:
	co $@

# rules for everybody in ${NSTD} go here
untamo: parse.o scan.o insque.o list.o untamo.o warn.o zap.o
	cc ${CFLAGS} -o untamo insque.o list.o parse.o scan.o untamo.o \
				warn.o zap.o ${LIBS}
y.tab.h: parse.c

parse.c:
	yacc -d parse.y
	mv y.tab.c parse.c

scan.c:
	lex scan.l
	mv lex.yy.c scan.c

# DO NOT DELETE THIS LINE - make depend DEPENDS ON IT
I=/usr/include
S=/usr/include/sys

insque.o: insque.c

list.o: $I/setjmp.h $I/stdio.h $S/file.h $S/types.h list.c untamo.h y.tab.h

untamo.o: $I/pwd.h $I/setjmp.h $I/signal.h $I/stdio.h $S/file.h $S/ioctl.h \
	$S/jioctl.h $S/stat.h $S/ttychars.h $S/ttydev.h $S/types.h $I/utmp.h \
	untamo.c untamo.h y.tab.h

untamo.o: $I/setjmp.h $I/stdio.h $S/types.h untamo.h

warn.o: $I/setjmp.h $I/signal.h $I/stdio.h $S/ioctl.h $S/jioctl.h \
	$S/ttychars.h $S/ttydev.h $S/types.h untamo.h warn.c y.tab.h

zap.o: $I/setjmp.h $I/stdio.h $S/file.h $S/ioctl.h $S/jioctl.h $S/ttychars.h \
	$S/ttydev.h $S/types.h $S/wait.h $I/utmp.h untamo.h zap.c

parse.o: $I/grp.h $I/setjmp.h $I/stdio.h $S/types.h parse.c untamo.h

scan.o: $I/stdio.h scan.c y.tab.h

# *** Do not add anything here - It will go away. ***
SHAR_EOF
cat << \SHAR_EOF > untamo.cf
*
*       how long to sleep between checks
*
   sleep 5
*
*
*
   threshold multiple 30
   threshold session  43
*
*       now list the timeout rules
*
   timeout cluster public 45
   timeout default 60
*
* 	"necessary" exemptions
*
   exempt tty console all
   exempt group staff multiple
   exempt group system multiple
   exempt login kdr all
SHAR_EOF
cat << \SHAR_EOF > doc
.nr PS 12
.TL
The 'Untamo' Project
.AU
Andy Wilcox
.AI
Purdue University Computing Center
.PP
The Purdue University Computing Center needs more machines and
terminals to provide better services to our users.  The purchase
of more machines and terminals will come in time.  However, ways are needed
now to more evenly distribute our present resources.
The Untamo project is an attempt to solve these and related problems.
.PP
There is always a problem at the beginning of the semester with naive
users who turn their terminals off without logging out.  Many of these
unfortunate souls will soon be in the consultants office explaining
how their account was booby trapped by a malicious user.  Untamo will
decide that a terminal is idle if the keyboard has not been touched
for thirty minutes (this choice of time is under investigation).  Untamo
will then log out idle terminals after a warning message has been sent to them.
This will help combat this serious security problem, and save large
amounts of work on the user's part if his or her files were deleted.
.PP
Sometimes it is advantageous to have idle terminals, for example, a
Vaxen console.  Untamo also provides an 'exemption' feature that allows 
such cases.
.PP
Late in the semester when final projects are due, there are inevitably
waiting lines for the terminals, and ideally we would prefer one user to
be logged on only once.  With job control in c-shell, there is no
need to be logged in more than once.  Untamo looks for these multiple logins,
warns each one of them, and then will log out all but the first terminal
to be logged on.
.PP
Once again, there are exceptions to the case of multiple logins.  
Many times the consultants can log a person in again and fix a hung terminal
by killing a runaway process without having to call the console room.
Untamo allows a few minutes of multiple login time, which is enough time
to locate and kill a runaway process.
.PP
It is important to note that Untamo will primarily affect our public
terminals.  Most other terminals will be 'exempt'.
.PP
Untamo is a dynamically reconfigurable program, so if there is a 
problem with someone needing more idle time, or a multiple login,
it can be added without killing Untamo, recompiling, reinstalling, 
and restarting.  These changes take effect in a few minutes, so the
wait is not long.  
.PP
The amount of time Untamo 'sleeps' between checking logins and idle times
is also redefinable, it can be made larger by the operator on a 
heavily loaded machine to allow more time for a multiple login.  This 
necessary human interaction with Untamo is questionable, and 
ways of making the sleep time a function of the 
load are being investigated.  In any case, the operator always has
final control of Untamo.
SHAR_EOF
cat << \SHAR_EOF > untamo.8l
.TH UNTAMO 8L PUCC
.SH NAME
untamo \- Idle terminal and multiple login monitor daemon.
.SH SYNOPSIS
.B /usr/local/etc/untamo
.SH DESCRIPTION
.PP
.I Untamo
wakes up at regular intervals and scans /etc/utmp to see
which users are currently logged in, how long they have been idle,
whether they are logged in more than once, etc.
.I Untamo
then warns and logs out users based on a set of rules in its
configuration file.
.I Untamo
is usually started from /etc/rc.local.
.PP
.I Untamo
uses a configuration file,
.IR untamo.cf ,
to find out how long a terminal must be unused to be considered
\*(lqidle\*(rq, and which users, groups, terminals, or clusters of
terminals are exempt from being logged out.
.PP
.I Untamo
gets its name from the Finnish god of sleep and dreams.
.SH FILES
.TP
.B /usr/local/lib/untamo.cf
Configuration file which specifies how often untamo is to wake up, and
exemptions to rules, etc.
.TP
.B /usr/adm/untamo.log
Log of when untamo is started, killed, who it logs off and why, and any
errors it encounters.
.SH DIAGNOSTICS
Various \*(lqcouldn't open ...\*(rq error messages.  Since untamo
dissacociates itself from the invoking terminal, most of the errors
get put in the log file.
.SH SEE ALSO
untamo.cf(5L), utmp(5)
.SH BUGS
If a user logs off and then on again fast enough and manages to get a
different tty, he may be warned about a multiple login.
SHAR_EOF
cat << \SHAR_EOF > untamo.cf.5l
.TH UNTAMO.CF 5L "PUCC
.SH NAME
/usr/local/lib/untamo.cf \- untamo configuration file format
.SH DESCRIPTION
.IR Untamo,
the login monitor daemon, decides how it should act based on the file
untamo.cf.
.PP
The file untamo.cf consists of a series of specifications which each
describe an aspect of untamo's actions.  There are four types of commands: 
.IP 1.
.B Exemption  
commands
specify login ids that are exempt from untamo's actions.
.IP 2.
.B Timeout
commands
define how long a terminal must be idle before it is logged out.
.IP 3.
.B Sleep
commands
set the number of minutes untamo should sleep between checks, 
as well as the amount of time between warnings and logouts.
.IP 4.
.B Threshold
commands
set the threshold number of users who must be logged in before multiple
login control takes effect.  If the number of users on the system exceeds
the threshold, then warnings and logouts for multiple logins will be issued.
.PP
Lines beginning with an asterisk in column one are comments.
Other lines may be indented for readability.
.SH EXEMPTIONS

.PP
Exemptions have the form:

.IP
.B exempt
.I who
.I from

.PP
.I Who
must be one of:
.IP
.B cluster 
.I name
.IP
.B login
.I id
.IP
.B group
.I groupname
.IP
.B tty
.I ttyname
.IP
.B rld
.I num
.PP
where
.I name 
must be a terminal cluster name such as \*(lqpublic\*(rq or \*(lqstaff\*(rq.
(See the PUCC in-house document Z0-TERMFIL for more information about clusters.)
.I Id
must be a valid login id as in /etc/passwd.
.I Groupname
must be a valid group name as in /etc/groups.
.I Ttyname
is a terminal name as in /etc/utmp (e.g., \*(lqtty\fIXX\fP\*(rq).
.I Num
must be an rld (terminal) number.

.I From 
must be one of:
.IP
.B multiple
.IP
.B idle
.IP
.B session
.IP
.B all
.PP
which specify that
.I who
is exempt from being logged off for maintaining multiple logins,
remaining idle longer than the idle timeout period, exceeding a login
session limit, or all three.

.SH TIMEOUTS 
.PP
Timeouts are of the form:
.IP
.B timeout
.I who
.I minutes
.br
.B timeout default
.I minutes
.PP
In the first form,
.I who
must be a valid login id and 
.I minutes 
a decimal number.
This form indicates that
.I who
will be logged off after remaining idle for
.I minutes
minutes.  The second form sets a default idle timeout that affects
anyone not otherwise exempted from timeouts or mentioned by login id
in an explicit timeout rule.

.SH SLEEP
.PP
Sleep commands are of the form:
.IP
.B sleep
.I minutes
.PP
and specify that untamo will sleep 
.I minutes
minutes between its checks.
If there is more than one sleep specification, only the last one is used.
.PP
.SH THRESHOLD
Threshold commands are of the form:
.IP
.B threshold 
.I number 
.PP
This specifies that at least 
.I number
ttys must be active before untamo will check for multiple logins.
If there is more than one
.I threshold
command, only the last one is used.
.SH EXAMPLE
.PP
A sample untamo configuration file follows:
.br
.nf
   *
   * sleep 5 minutes between checks
   *
       sleep 5

   *
   * 30 users must be logged on before
   * multiple login checks will begin
   *
       threshold 30
   *
   * Set the default idle timeout to 60 mintes.  For the
   * public cluster, set the idle timeout to 45 minutes.
   *
       timeout cluster public 45
       timeout default 60
   *
   * Necessary exemptions to make sure the console doesn't
   * get logged off and staff members can log in more than once.
   *
       exempt tty console all
       exempt group staff multiple

.fi
.SH "SEE ALSO"
untamo(8L), utmp(5), rld(1L)
SHAR_EOF
cat << \SHAR_EOF > insque.c
/* Queue insertion and deletion routines for non-Vaxen */
struct qelem { 
    struct qelem *q_forw , *q_back;
#ifdef LINT
    char q_data[];
#endif LINT
};

#ifndef VAX
insque( elem , pred )
    register struct qelem *elem , *pred;
{
    elem->q_forw = pred->q_forw;
    pred->q_forw = elem;
    elem->q_forw->q_back = elem;
    elem->q_back = pred;
}

#endif VAX
SHAR_EOF
cat << \SHAR_EOF > list.c
#ifdef PUCC
#include <sys/types.h>
#include <rld.h>
#endif PUCC
#include <sys/file.h>
#include "untamo.h"
#include "y.tab.h"

extern char *malloc(), *strcpy(), *ctime();
extern strlen();
time_t time();
long lseek();

/*
 * addlist -- adds a record to the list "list".  Uses the 
 *	      insque(3) call for speed.
 *	      list -- which list to add the rule to (rules or exempt)
 *	      type -- what kind of rule (LOGIN, GROUP, etc)
 *	      name -- who it applies to (ajw, console, etc)
 *	      num --  who it applies to (1505 (rld), 5 (group staff) )
 *	      flag -- idle time for a "rule" rule, or exemption type
 *		      for "exempt" rule: IDLE, MULTIPLE, etc.
 */
addlist(list, type, name, num, flag)
struct qelem *list;
char *name;
int type, flag, num;
{
	struct qelem *new_node;
	struct qelem *ptr;
	struct item  *new_data;

	/* 
	 * make all the new structures
	 */
	new_node = (struct qelem *) malloc( sizeof(struct qelem) );
	new_data = (struct item  *) malloc( sizeof(struct item ) );
	new_data->name_t = type;
	new_data->name = name;
	new_data->num = num;
	new_data->flag = flag;
	new_node->q_item = new_data; 
	/*
	 * find where to insert it in the list
	 */
	ptr = list->q_forw;
	while (ptr != list)	{
		if (ptr->q_item->name_t <= new_data->name_t)
			break;
		ptr = ptr->q_forw;
	}
	/*
	 * and insert it
	 */
	(void) insque(new_node,ptr->q_back);
}


/*
 * freelist -- frees up the space in the list pointed to 
 *	       by ptr.  Uses free.
 */
freelist(ptr)
struct qelem *ptr;
{
	struct qelem *dead;
	struct qelem *start;

	start = ptr;
	ptr = ptr->q_forw;
	while ( ptr != start )   {
		dead = ptr;
		ptr = ptr->q_forw;
		(void) free( (char *) dead->q_item);  	/* kill the data */
		(void) free( (char *) dead );		/* now get the node */
	}
	start->q_forw = start;		/* reset pointers for a null list */
	start->q_back = start;
}


/*
 * setlimits -- looks through the rules list and uses the most
 *		specific rule for users[i], then looks through
 *		the exceptions and uses the (again) most specific
 *		exemption for the user.
 */

setlimits(i)
int i;
{
	struct qelem *ptr;
	int rule;
	time_t tempus;

	/*
	 * look down the rules list and set the
	 * most specific rule that applies to 
	 * this user
	 */
	rule = 0;
	(void) time(&tempus);
	ptr = rules->q_back;		/* start at the end of the list */
	users[i].warned = 0;		/* clear his warning and exempt flag */
	users[i].exempt = 0;		/* to avoid granularity problems */

	users[i].next = tempus;		/* next time is set to now, so */
					/* the new rules (or exemptions) */
					/* take affect immediately */
	/* 
	 * while we haven't looked through the whole list,
	 * and we haven't found a rule...
	 */
	while ( (ptr != rules) && (!rule) )	{
		if ( (rule = find(ptr, i)) )  {
			users[i].idle = (ptr->q_item)->flag * 60;
			users[i].exempt &= ~IS_IDLE;
		} else
			ptr = ptr->q_back;		/* move back one */
	}

	rule = 0;
	ptr = session->q_back;
	while ( (ptr != session) && (!rule) ){
		if ( (rule = find(ptr, i)) )  {
			users[i].session = (ptr->q_item)->flag * 60;
			users[i].exempt &= ~IS_LIMIT;
		} else
			ptr = ptr->q_back;		/* move back one */
	}
	/*
	 * now look down the exemptions list and
	 * set the first exemptions for this user.
	 */
	rule = 0;
	ptr = exmpt->q_back;		/* start at the end of the list */
	while ( (ptr != exmpt) && (!rule) )	{
		if ( (rule = find(ptr, i)) )  {
			if ( (ptr->q_item)->flag == ALL)
				users[i].exempt |= IS_IDLE|IS_MULT|IS_LIMIT;
			else if ( (ptr->q_item)->flag == IDLE)
				users[i].exempt |= IS_IDLE;
			else if ( (ptr->q_item)->flag == MULTIPLE)
				users[i].exempt |= IS_MULT;
			else if ( (ptr->q_item)->flag == SESSION)
				users[i].exempt |= IS_LIMIT;
		} else
			ptr = ptr->q_back;		/* move back one */
	}
}


/*
 * find -- given a rule (or exemption) and a users, see if it applies.
 *	   Example: If the rule is a LOGIN type rule, compare the name with that
 *	   of the user to see if it applies.  If we find a match, return 1,
 *	   else return 0.
 */
find(ptr,i)
int i;
struct qelem *ptr;
{
	switch ( (ptr->q_item)->name_t )	{
	case LOGIN: 
		if (!strcmp( (ptr->q_item)->name, users[i].uid) )
			return(1);
		break;
	case TTY:
		if (!strcmp( (ptr->q_item)->name, (users[i].line+5) ) ) 
			return(1);
		break;
	case GROUP:
		if ( (ptr->q_item)->num == users[i].ugroup ) 
			return(1);
		break;
#ifdef PUCC
	case CLUSTER:
		if (!strcmp( (ptr->q_item)->name, users[i].clust) )
			return(1);
		break;
	case RLD:
		if ( (ptr->q_item)->num == users[i].rld ) 
			return(1);
		break;
#endif PUCC
	case DEFAULT:
		return(1);
		break;
	}
	return(0);
}

#ifdef PUCC

#define makestr(Z)	(strcpy(malloc( (unsigned) strlen(Z)+1),Z))

/*
 * findcluster -- passes an rld number, it returns a char *
 *		  to the name of the cluster associated with it
 */
char *
findcluster(rld)
int rld;
{
	int fd;
	static struct rld_data data;

#ifdef BSD2_9
	if ( (fd = open(RLD_FILE,O_RDONLY) ) < 0 )  {
#else BSD2_9
	if ( (fd = open(RLD_FILE,O_RDONLY,0) ) < 0 )  {
#endif BSD2_9
		(void) error("Can't open rld file.");
		exit(1);
	}

	/* 
	 * seek to the proper rld and read it.
	 */
	(void) lseek(fd, rld*sizeof(data), 0);

	(void) read(fd, (char *) &data, sizeof(data) );

	(void) close(fd);

	/*
	 * return the cluster name, if null, return "public"
	 */
	if (data.rld_cluster)
		return(makestr(data.rld_cluster));
	else
		return(makestr("public"));
}
#endif PUCC
SHAR_EOF
cat << \SHAR_EOF > untamo.c
#ifdef PUCC
#include <sys/types.h>
#include <rld.h>
#endif PUCC
#include <utmp.h>
#include <signal.h>
#include <sys/file.h>
#ifndef F_OK
#    define F_OK 0
#endif F_OK
#include <sys/ioctl.h>
#include "untamo.h"
#include <sys/stat.h>

#ifdef PUCC
#define PWIDLE	6	/*accounting bit for no idle time logout     */
#define PWMULT	7	/*accounting bit for multiple logins allowed */
#include <passwd.h>
struct usrpwd *getupnam();
#else PUCC
#include <pwd.h>
#endif PUCC

#include "y.tab.h"

struct user users[MAXUSERS];
struct user *pusers[MAXUSERS];

extern char *malloc(), *strcpy(), *ctime() , *strcat();
extern unsigned strlen();
extern time_t time();

struct qelem *rules, 
	     *session,
	     *exmpt;	/* lists for timeouts, session limits, and exemptions */

jmp_buf env_buf;
int sleeptime;		/* time to sleep between checks			  */
FILE *logfd;		/* log file file descriptor pointer		  */
int m_threshold;	/* number of users before multiple limits 	  */
int s_threshold;	/* number of users for session limits		  */
int warn_flags = IS_IDLE | IS_MULT | IS_LIMIT; 
			/* what sorts of warnings should be accepted    */

main(n_args, ppch_args)
	int n_args;
	char **ppch_args;
{
	struct utmp utmpbuf;
	struct stat statbuf;
#ifdef PUCC
	struct usrpwd *pswd;
#else PUCC
	struct passwd *pswd;
#endif PUCC
	struct user *user;
	char pathbuf[20];
	int utmptr, utmpfd;
	time_t conf_oldstamp;
	int userptr;
	int res, td;
	int new;		/* if the configuration file is new */
	time_t tempus;
	int finish(), wakeup();
	FILE *conffd, *freopen(), *fopen();
	/* command line flags */
	int fl_multiple = 1, fl_session = 1, fl_idle = 1;

	while( --n_args && *++ppch_args && **ppch_args == '-' ) {
		while( *++(*ppch_args) ){
			switch( **ppch_args ){
			case 'm': /* don't even think about multiple logins */
				fl_multiple = 0;
				break;
			case 'i': /* don't even think about idle timeouts */
				fl_idle = 0;
				break;
			case 's': /* don't even think about session limits */
				fl_session = 0;
				break;
			default:
				fprintf( stderr, 
				"untamo: bad flag -%c\n", **ppch_args );
				break;
			}
		}
	}

	if( !fl_multiple && !fl_idle && !fl_session ){
		/* do absolutely nothing!! */
		exit(0);
	}

#ifdef PUCC
	if ( access( "/flags/testsys" , F_OK ) == 0 ) 
	    exit(0);				/* dont run in test mode */
#endif PUCC
	(void) signal(SIGHUP,  SIG_IGN);	
	(void) signal(SIGQUIT, SIG_IGN);
	(void) signal(SIGINT,  SIG_IGN);
#ifdef BSD4_2
	(void) signal(SIGTTOU, SIG_IGN);
	(void) signal(SIGTSTP, SIG_IGN);
#endif BSD4_2

	(void) signal(SIGTERM, finish);
	(void) signal(SIGALRM, wakeup);
	conf_oldstamp = 1;			/* a very old stamp */
	/*
	 * set up new header nodes for each of the lists.
	 * The forw and back pointers must point to them
	 * selves so the system insque routine can be used
	 */
	rules = (struct qelem *) malloc( sizeof(struct qelem) );
	exmpt = (struct qelem *) malloc( sizeof(struct qelem) );
	session = (struct qelem *) malloc( sizeof(struct qelem) );
	rules->q_forw = rules->q_back = rules;
	exmpt->q_forw = exmpt->q_back = exmpt;
	session->q_forw = session->q_back = session;
	rules->q_item = session->q_item = exmpt->q_item = NULL;

	if ( (logfd = fopen(LOGFILE,"a")) > 0)  {
		(void) time(&tempus);
		(void) fprintf(logfd,"%24.24s  Untamo started\n",ctime(&tempus) );
		(void) fclose(logfd);
	} else {
		(void) fprintf( stderr , "Untamo: couldn't open log file: %s\n", LOGFILE );
		exit(1);
	}

	if ( (res = fork()) < 0)	{
		(void) fprintf(stderr,"Untamo: couldn't start\n");
	}
	if (res){  /* if the parent */
#ifdef DEBUG
		exit(res);
#else
		exit(0);
#endif DEBUG
	}

	/*
	 * lose our controlling terminal
	 */
#ifdef BSD2_9
	td = open("/dev/tty", O_RDWR);
#else BSD2_9
	td = open("/dev/tty", O_RDWR, 0600);
#endif BSD2_9
	if (td >= 0){
		(void) ioctl(td, TIOCNOTTY, (char *)0);
		(void) close( td );
	}

	/*
	 * now sit in an infinite loop and work
	 */

while (1){
	if ( stat(CONFIG,&statbuf) < 0)  {
		(void) error("Untamo: couldn't stat conf file");
		exit(1);
	}

	if ( statbuf.st_mtime > conf_oldstamp ) {
		conf_oldstamp = statbuf.st_mtime;

		if ( (conffd = freopen(CONFIG, "r", stdin)) < 0) {
			(void) error("Untamo: can't open configuration file");
			exit(1);
		}

		/*
		 * get rid of the old rules and exempt lists
		 */
		(void) freelist(rules);
		(void) freelist(exmpt);
		(void) freelist(session);
		m_threshold = 0;
		s_threshold = 0;
		/*
		 * now read the configuration file and set up the
		 * rules and exemption lists
		 */
		(void) yyparse();
		new = 1;
	} else
		new = 0;
#ifdef BSD2_9
	if ( (utmpfd = open(UTMP, O_RDONLY)) < 0) {
#else BSD2_9
	if ( (utmpfd = open(UTMP, O_RDONLY, 0)) < 0) {
#endif BSD2_9
		(void) error("Untamo: can't open /etc/utmp");
		exit(1);
	} /* } <-- to match ifdefed open... */

	utmptr = 0;
	userptr = 0;
	/*
	 * look through the utmp file, compare each entry to the users
	 * array to see if an entry has changed.  If it has, build a new
	 * record for that person, if it hasn't, see  if it is time to
	 * examine him again.
	 */
	while ( (res = read(utmpfd, (char *)&utmpbuf, sizeof(struct utmp)) ) > 0 ) {

		if (res != sizeof(struct utmp)) {
			(void) error("Untamo: error reading utmp file, continuing");
			continue;
		}
		(void) time(&tempus);
		if (utmpbuf.ut_name[0] != '\0')   {
			user = &users[utmptr];
			if ( !(strcmp(user->uid,utmpbuf.ut_name)) &&
			   (user->time_on == utmpbuf.ut_time) )	{
				if (new)
					(void) setlimits(utmptr);
				if (fl_idle && tempus > user->next) {
					(void) checkidle(utmptr);
				}
			} else {
				/*
				 * build a new record
				 */
				user->warned  = 0;
				(void) strcpy(pathbuf,DEV);
				(void) strcat(pathbuf,utmpbuf.ut_line);
#ifdef PUCC
				user->rld = findrld(pathbuf);
				(void) strcpy(user->clust, findcluster(user->rld));
#endif PUCC

				(void) strcpy(user->line, pathbuf);
				(void) stat(pathbuf,&statbuf);
				(void) strcpy(user->uid, utmpbuf.ut_name);

#ifdef PUCC
				pswd = getupnam(utmpbuf.ut_name);
				user->ugroup = pswd->up_gid;
#else PUCC
				pswd = getpwnam(utmpbuf.ut_name);
				user->ugroup = pswd->pw_gid;
#endif PUCC

				user->time_on = utmpbuf.ut_time;
				(void) setlimits(utmptr);

#ifdef PUCC
				if( pswd->up_flags & (1l << PWMULT ))
				    user->exempt |= IS_MULT;
				if( pswd->up_flags & (1l << PWIDLE ))
				    user->exempt |= IS_IDLE;
#endif PUCC
				user->next = tempus;
 			}
			pusers[userptr++] = user;
		}
		utmptr++;
	}

	(void) close(utmpfd);
	(void) fclose(conffd);

#ifdef PUCC
	/*
	** check session limits
	*/

	if( fl_session ){
		(void) chk_session(userptr);
	}
#endif PUCC

	/*
	** check for and warn multiple logins
	*/

	if( fl_multiple ){
		(void) chk_multiple(userptr);
	}

	/*
	** wait sleeptime minutes
	*/

	(void) sleep( (unsigned) sleeptime * 60);
    }
}

#ifdef PUCC
/*
 * chk_session( users ) 
 * find out how many people are on sds ports, 
 * and try to warn enough people to get below the threshold
 */

chk_session( n_users )
	register int n_users;
{
	register int which_user;
	time_t tempus;
	register int n_sds_ports = 0;
	static int fl_sessionlimits = 0;

	(void) time(&tempus);
	for( which_user = 0; which_user < n_users ; which_user++ ){
		if( pusers[which_user]->warned & IS_LIMIT ){
			(void) warn(which_user,IS_LIMIT);
		} else if( is_sds_port( pusers[which_user]->rld ) ){
			n_sds_ports++;
		}
	}

	if( n_sds_ports > s_threshold && !fl_sessionlimits ){
		(void) close( creat( "/flags/sessionlimits", 0600 ));
		fl_sessionlimits = 1;
	}

	if( n_sds_ports < s_threshold && fl_sessionlimits ){
		unlink( "/flags/sessionlimits" );
		fl_sessionlimits = 0;
	}
		
	while( n_sds_ports>s_threshold  && s_threshold>0 && which_user>0 ){
		which_user--;
		if( tempus-pusers[which_user]->time_on > pusers[which_user]->session 
		    && pusers[which_user]->session > 2*60 
		    && !(pusers[which_user]->warned & IS_LIMIT)  ){

			(void) warn(which_user,IS_LIMIT);
			n_sds_ports--;
		}
	}
}


int 
is_sds_port( rld_number )
	int rld_number;
{
	int fd, res;
	struct rld_data rdat;

	/*
	 * Get to the right place in /etc/rlds
	 * Complements of Jeff Smith...
	 */
	if( fd = open (RLD_FILE, O_RDONLY, 0) >= 0 ) {
		lseek (fd, (long) rld_number * sizeof (struct rld_data), L_SET);
		res = read (fd, (char *) &rdat, sizeof (struct rld_data));
		(void) close(fd);
		if (res == sizeof(struct rld_data) && rdat.rld_tio != -1) {
			return 1;
		}
	}
	return 0;
}
#endif PUCC

/*
 * chk_multiple -- given the number of users (i), warn any of 
 *	       them  that have multiple logins.  Calls qsort(3)
 *	       to sort them by id.
 */
chk_multiple(i)
int i;
{
	int j, comp(); 
	int match, skip = -1;
	int wait = 0;

	if( i < m_threshold && m_threshold > 0 ) { /* below threshold...*/
		return;
	}
	(void) qsort( (char *) pusers, i, sizeof(struct user *), comp);
	for (j=0; j<i-1; j++)	{
		/*
		 * if not all the multiple logins logged out,
		 * decide on one not to kill, clear his warned
		 * bit, and continue.  But don't look again until
		 * we have passed all the guys with the same login.
		 */
		if ( wait == 0 )  {
			match = 0;
			skip = decide(j, i, &wait);
		} else
			wait--;

		if ( ( (*pusers[j]).exempt & IS_MULT) || (j == skip) )  {
			continue;	/* he's exempt ! */
		}

		if ( !strcmp( (*pusers[j]).uid, (*pusers[j+1]).uid) )	{
			match = 1;
			(void) warn(j,IS_MULT);
		} else {
			if ( match )
				(void) warn(j,IS_MULT);
			match = 0;
		}
	}
}


/*
 * decide -- given a bunch of multiply logged on terminals that did
 * 	     not heed the warning, decide returns the index into the 
 *	     *pusers array of the user NOT to log off.  Wait is the
 *  	     number of ids that chk_multiple must skip before calling
 *	     decide again.  Admittedly this is gross, but it works.
 */
decide(j, num, wait)
int j, num, *wait;
{
	int i;
	int count = 1;
	int warned = 1;
	int skip;

	/* 
	 * look through the users and find how many 
	 * of login (*pusers[i]).uid are logged on
	 * and whether or not they have been warned
	 */
	for ( i=j; i<num; i++)  {
		if ( !((*pusers[i]).warned & IS_MULT) )
			warned = 0;

		if ( !strcmp( (*pusers[i]).uid, (*pusers[i+1]).uid) )
			count++;
		else
			break;
	}
	/*
	 * now, if there is a need to skip someone, do it
	 */
	*wait = count-1;
	if ( (warned) && (count > 1) )  {
		skip = j;
		/*
		 * set skip to the alpha-numerical least tty
		 */
		for(i=j+1; i<j+count; i++)
			if (strcmp((*pusers[skip]).line,(*pusers[i]).line)>0)
				skip = i;
		(*pusers[skip]).warned &= ~IS_MULT;
		return(skip);
	}
	return(-1);
}

	
/*
 * finish -- end Untamo
 */
finish()
{
	time_t tempus;
	FILE *logfd;

	(void) signal(SIGTERM, SIG_IGN);
	(void) time(&tempus);
	(void) unlink( "/flags/sessionlimits");
	if ( (logfd = fopen(LOGFILE,"a")) > 0)  {
		(void) time(&tempus);
		(void) fprintf(logfd,"%24.24s  Untamo killed.\n",ctime(&tempus) );
		(void) fclose(logfd);
	}
	exit(0);
}


/*
 * comp -- used by qsort to sort by id
 */
comp(h1, h2)
struct user **h1, **h2;
{
	return( strcmp((**h1).uid, (**h2).uid) );
}


/* 
 * checkidle -- given a user, see if we want to warn him about idle time.
 *		first check the exempt vector to see if he is exempt.
 */
#define min(a,b) ( (a)<(b)?(a):(b) )

checkidle(i)
int i;
{
	struct stat statbuf;
	time_t tempus;

	(void) time(&tempus);
	(void) stat(users[i].line,&statbuf);
#ifdef DEBUG
	{ static char debugprint[80];
	  sprintf(debugprint,"**debug: checkidle(%d); %d %d %x\n",
		i, users[i].session, users[i].idle, users[i].exempt);
	  error(debugprint);
	  sprintf(debugprint," *debug: %s %s\n", users[i].line, users[i].uid);
	  error(debugprint);
	}
#endif DEBUG
	if (( tempus - statbuf.st_atime) < users[i].idle ) {
		users[i].warned &= ~IS_IDLE;
	} else {
		if (users[i].idle > 2*60 && !(users[i].exempt & IS_IDLE)) {
			(void) warn(i,IS_IDLE);
		}
	}
	users[i].next = min( statbuf.st_atime + users[i].idle,
			     users[i].time_on + users[i].session );
}
SHAR_EOF
cat << \SHAR_EOF > warn.c
#include <signal.h>
#include <sys/ioctl.h>
#include "untamo.h"
#include "y.tab.h"

extern char *malloc() , *strcpy() , *ctime();
extern unsigned strlen();

/*
 * warn -- warn a user that he is logged in multiply, or has
 *	   been idle past his limit.  Uses the magic of setjmp
 *	   and longjmp to avoid a timeout on a terminal write.
 */
warn(i,type)
int i, type;
{
	int res;
	int opened;
	struct user *him;
	FILE *termf;

	if( !(type & warn_flags )) { /* we are not doing this warning now.. */
		return 0;
	}

	if (type == IS_IDLE )
		him = &users[i];
	else  /* type == IS_MULT || type == IS_LIMIT */
		him = pusers[i];

	if ( (res=vfork()) < 0)	{
		(void) error("couldn't fork in warn");
		exit(0);
	}
	/* 
	 * the parent returns after it
	 * has modified the global data structures
	 * that the child obviously can't
	 */
	if (res)   {
		if ( (type == IS_MULT) && !(him->warned & IS_MULT) )
			him->warned |= IS_MULT;
		if ( (type == IS_IDLE) && !(him->warned & IS_IDLE) )
			him->warned |= IS_IDLE;
#ifdef PUCC
		if ( (type == IS_LIMIT) && !(him->warned & IS_LIMIT) )
			him->warned |= IS_LIMIT;
#endif PUCC
		return( wait(0) );
	}
	
	/*
	 * child continues here
	 */
	if (setjmp(env_buf) == 0) {
		opened = 0;
		(void) alarm(5);
		if ((termf = fopen( him->line, "w")) != NULL)  {
			opened = 1;
			/*
			 *  start the terminal if stopped
			 */
			(void) ioctl(fileno(termf),TIOCSTART,(char *) 0);
			if (type == IS_MULT)  {
				if (him->warned & IS_MULT)  {
					(void) zap( him );
				} else {
				(void)fprintf(termf,"\007\r\n\r\nThis user id is ");
				(void)fprintf(termf,"logged on more than ");
				(void)fprintf(termf,"once, please end\r\n");
				(void)fprintf(termf,"all but one of your ");
				(void)fprintf(termf,"logins in the next");
				(void)fprintf(termf," %1d minutes\r\n",
						       sleeptime);
				(void)fprintf(termf,"or you will be logged ");
				(void)fprintf(termf,"out by the system.\r\n\r\n\007");
				}
			}
			else if (type == IS_IDLE)  {
				if( him->warned & IS_IDLE )	{
					(void) zap( him );
				} else {
				(void)fprintf(termf,
				"\007\r\n\r\nThis terminal has been idle %2d ",
				him->idle/60);

				(void)fprintf(termf,
				"minutes.  If it remains\r\nidle for %1d ",
				sleeptime);

				(void)fprintf(termf,
				"minutes it will be logged out by the system.");
				(void)fprintf(termf,"\r\n\r\n\007");
				}
			}
			else if (type == IS_LIMIT)  {
				if( him->warned & IS_LIMIT )	{
					(void) zap( him );
				} else {
				(void)fprintf(termf,
				"\007\r\n\r\nThis terminal has been in use %2d",
				him->session/60);

				(void)fprintf(termf, " minutes.\nIn %1d ",
				sleeptime);

				(void)fprintf(termf,
				"minutes it will be logged out by the system.");
				(void)fprintf(termf,"\r\n\r\n\007");
				}
			}
			(void) fclose(termf);
			opened = 0;
		} 
		(void) alarm(0);
	} else {
		/* we timed out */

		if ( opened ) {
		   /* free FILE without write()   */
			termf->_ptr = termf->_base;
			(void) fclose(termf);
		}
	}
	exit(0);	/* child exits here */
	return(0);	/* lint doesnt believe in exit()... */
}


/*
 * wakeup -- signal handler for SIGALRM
 */
wakeup()
{
	(void) longjmp(env_buf, 1);
}
SHAR_EOF
cat << \SHAR_EOF > zap.c
#ifdef PUCC
#include <sys/types.h>
#endif PUCC
#include <utmp.h>

#ifdef BSD2_9
#include <wait.h>
#else BSD2_9
#include <sys/wait.h>
#endif BSD2_9

#include <sys/ioctl.h>
#include <sys/file.h>
#include "untamo.h"

#ifdef BSD2_9
#include <sys/param.h>
#define getdtablesize() NOFILE
#endif BSD2_9

extern char *malloc() , *ctime() , *strcpy();
time_t time();

/*
 * zap -- disconnect the person logged on to tty "dev".
 * 	  makes use of ioctl(2) to get a new controlling
 *	  terminal and the infinitely clever vhangup(2)
 * 	  to disconnect it.  
 */
zap(him)
struct user *him;
{
	static char message[] = "\n\n\007Logged out by the system.\n";
	int dts, td;
	time_t tempus;

	/*
	 * close all the child's descriptors 
	 */
	dts = getdtablesize();
	for ( dts--; dts>=0; dts--){
		(void) close(dts);
	}
	/*
	 * now tell him it's over, and disconnect.
	 */
#ifdef BSD2_9
	td = open(him->line, O_RDWR);
#else BSD2_9
	td = open(him->line, O_RDWR, 0600);
#endif BSD2_9
	(void) ioctl(td, TIOCSTART, (char *)0);
	if (td >= 0)  {
		(void) write(td, message, sizeof(message) );
		(void) fsync(td);
		(void) ioctl(td, TIOCFLUSH, (char *)0);
		td = vhangup();
		(void) time(&tempus);
		if ( (logfd = fopen(LOGFILE,"a")) != NULL)  {
			(void)fprintf(logfd,
			"%19.19s : %s on %s because %s, vhangup returned %d\n",
			ctime(&tempus), him->uid, him->line, 
			( (him->warned & IS_MULT) ? "multiple" : (him->warned & IS_IDLE ? "idle" : "session" )), td );
			(void)fclose(logfd);
		}
	} else {
		(void) time(&tempus);
		if ( (logfd = fopen(LOGFILE,"a")) != NULL)  {
			(void)fprintf(logfd,
			"%19.19s : couldn't open %s on %s\n",
			ctime(&tempus), him->line, him->uid );
			(void)fclose(logfd);
		}
	}
}


error(sb)
char *sb;
{
	if ( (logfd = fopen(LOGFILE,"a")) != NULL)   {
		(void)fprintf(logfd,"%s",sb);
		(void)fclose(logfd);
	}
}
SHAR_EOF
cat << \SHAR_EOF > parse.y
%{
#include <stdio.h> 
#include <grp.h>
#include "untamo.h"
struct group *grp;
char oct[5];		/* rld kludge */
char *name;
int  num;
extern char *yytext;

  /*
   *			vvvvvvvvvvvvvvvvvvvvvvvvvvvvv
   *   			>>>>>>>>> IMPORTANT <<<<<<<<<
   *			^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   *  The order of the tokens in the *first line* is significant.  
   *
   *  They dictate which rules and exemptions have precence.  This order 
   *  states that RLD is the  most significant, and Untamo will use an
   *  rld rule first if it has a choice.  The second two %token lines may
   *  be ordered anyway.  DEFAULT is the least specific, but will always
   *  match.  It must always remain in the last position.
   */ 
%}

%token RLD TTY LOGIN GROUP CLUSTER DEFAULT

%token EXEMPT TIMEOUT SLEEP SESSION
%token NUM IDLE MULTIPLE NAME ALL 
%token THRESHOLD NL

%union {
	char *sb; 
	int nb;
       }

%type <sb> NAME
%type <nb> NUM LOGIN GROUP TTY RLD ALL IDLE MULTIPLE 
%type <nb> who exempt_type name_type

%start cmd_cmd

%%

cmd_cmd	: /*EMPTY*/
	| cmd_cmd exempt_cmd
	| cmd_cmd idle_cmd
	| cmd_cmd sleep_cmd
	| cmd_cmd session_cmd
	| cmd_cmd thresh_cmd
	| cmd_cmd error NL
	| cmd_cmd NL
	;

thresh_cmd : THRESHOLD MULTIPLE NUM NL
	{
		m_threshold = $3; 
	}
	| THRESHOLD SESSION NUM NL
	{
		s_threshold = $3; 
	}
	| THRESHOLD error NL
	{
		yyerror("Malformed threshold command.");
	}
	;
	

exempt_cmd	: EXEMPT who exempt_type NL
		{
			addlist(exmpt,$2,name,num,$3);
		}
		| EXEMPT error NL
		{
			yyerror("Malformed exempt command.");
		}
		;

session_cmd     : SESSION who NUM NL
		{
			addlist(session,$2,name,num,$3);
		}
		| SESSION error NL
		{
			yyerror("Malformed session command.");
		}
		;

idle_cmd	: TIMEOUT who NUM NL
		{
			addlist(rules,$2,name,num,$3);
		}
		| TIMEOUT DEFAULT NUM NL
		{
			addlist(rules, DEFAULT, NULL, 0, $3);
		}
		| TIMEOUT error NL
		{
			yyerror("Malformed timeout command.");
		}
		;

sleep_cmd	: SLEEP NUM NL
		{
			sleeptime = $2;
		}
		| SLEEP error NL
		{
			yyerror("Malformed sleep command.");
		}
		;

who		: name_type NAME
		{ 
			$$ = $1;
			name = $2;
			if ($1 == GROUP)  {
				grp = getgrnam(name);
				num = grp->gr_gid;
			}
		}
		| name_type NUM
		{
			$$ = $1;
			/*
			 * Kludge alert: here we must convert the
			 * rld number, which was read as decimal
			 * (lex doesn't know any better...) to an 
			 * octal so that it will jive with what
			 * findrld (in libacct) returns.
			 */
			if ($1 == RLD)  {
				sprintf(oct,"%d",$2);
				sscanf(oct,"%o",&num);
			} else
				num = $2;
		}
		;

name_type	: CLUSTER	{ $$ = CLUSTER; }
		| LOGIN 	{ $$ = LOGIN;   }
		| GROUP		{ $$ = GROUP;   }
		| TTY		{ $$ = TTY;     }
		| RLD		{ $$ = RLD;     }
		;

exempt_type	: ALL		{ $$ = ALL; 	 }
		| IDLE		{ $$ = IDLE; 	 }
		| MULTIPLE	{ $$ = MULTIPLE; }
		| SESSION	{ $$ = SESSION;  }
		;

%%
static int errorcnt = 0;

yyerror(sb)
char *sb;
{
	extern int linenum;
	char buf[128];

	sprintf(buf, "%s: line %d: %s\n", CONFIG, linenum, sb);
	error( buf );
	errorcnt++;
}

yywrap()
{
	extern int linenum;

	if( errorcnt > 0 ){
	    error( "Aborting due to config file syntax errors.\n");
	    exit( 1 );
	}
	linenum = 1;
	return 1;
}
SHAR_EOF
cat << \SHAR_EOF > scan.l

%{
/*
 *  Lex grammer to scan input file for untamo
 */
#include <stdio.h>
#include "y.tab.h"
#define makestr(Z)	((char *)strcpy(malloc(strlen(Z)+1),Z))
int linenum = 1;	/* current line number for error messages */
%}

%%
exempt		return EXEMPT;
timeout		return TIMEOUT;
sleep		return SLEEP;
login		return LOGIN;
group		return GROUP;
tty		return TTY;
rld		return RLD;
cluster		return CLUSTER;
default		return DEFAULT;
idle		return IDLE;
multiple	return MULTIPLE;
session		return SESSION;
threshold	return THRESHOLD;
all		return ALL;

[/A-Za-z][/A-Za-z0-9_]*	{   
		    yylval.sb = makestr(yytext);
		    return NAME; 
		}

[0-9]+		{
		    yylval.nb = atoi(yytext);
		    return NUM;
		}

"*".*		;
"\n"		{ 
		    linenum++; 
		    return NL;
		}
[ \t]*		;
.		{
		    static char *errormsg ="Illegal character ' '.";

		    errormsg[19] = yytext[0];
		    yyerror( errormsg );
		}
SHAR_EOF
cat << \SHAR_EOF > ttydev.h
/*	ttydev.h	6.1	83/07/29	*/

/*
 * Terminal definitions related to underlying hardware.
 */
#ifndef _TTYDEV_
#define	_TTYDEV_

/*
 * Speeds
 */
#define B0	0
#define B50	1
#define B75	2
#define B110	3
#define B134	4
#define B150	5
#define B200	6
#define B300	7
#define B600	8
#define B1200	9
#define	B1800	10
#define B2400	11
#define B4800	12
#define B9600	13
#define EXTA	14
#define EXTB	15

#ifdef KERNEL
/*
 * Hardware bits.
 * SHOULD NOT BE HERE.
 */
#define	DONE	0200
#define	IENABLE	0100

/*
 * Modem control commands.
 */
#define	DMSET		0
#define	DMBIS		1
#define	DMBIC		2
#define	DMGET		3
#endif
#endif
SHAR_EOF
cat << \SHAR_EOF > untamo.h
#include <sys/types.h>
#include <stdio.h>
#include <setjmp.h>

#define UTMP 		"/etc/utmp"	/* name of utmp file */
#define TERMFILE	"/etc/termfile" /* name of termfile  */

#ifdef DEBUG
#define CONFIG		"/userf/doc/untamo/untamo.cf"
#define LOGFILE		"/userf/doc/untamo/untamo.log"
#else DEBUG
#define CONFIG		"/usr/local/lib/untamo.cf"
#define LOGFILE		"/usr/adm/untamo.log"
#endif DEBUG

#define DEV		"/dev/\0"

#define MAXUSERS	100		/* max people expected */
#define NAMELEN		8		/* length of login name */
#define IS_IDLE		01
#define IS_MULT		02
#define IS_LIMIT	04

struct user {
#ifdef PUCC
	int rld;		/* rld number last logged in to */
	char clust[7]; 		/* cluster */
#endif PUCC
	int idle;		/* max idle limit for this user */
	int ugroup;		/* gid obtained from getgrent call */
	int session;		/* session limit for this user */
	int warned;		/* if he has been warned before */
	int exempt;		/* what is this guy exempt from? */
	time_t next;		/* next time to examine this structure */
	time_t time_on;		/* loggin time (express terminals?) */
	char uid[NAMELEN];	/* who is this is? */
	char line[14];		/* his tty in the form "/dev/ttyxx"*/
	char site[10];		/* where */
};

/* 
   next will be cur_time+limit-idle do all we have to do is check
   the current time against the action field when the daemon comes
   around.  if >= then it's time to check the idle time again, else
   just skip him.
*/

extern struct user users[];
extern struct user *pusers[];

/*
 * records that the nodes of the linked list 
 * will have pointers too
 */
struct item {
	int name_t;	/* is it a login, group, etc... */
	char *name;	/* which login, etc */
	int num;	/* which rld (special case, rld is a #)*/
			/* or group -doc*/
	int flag;	/* what is the timeout/exemption ? */
};

/*
 * necessary structures to use the system
 * linked list stuff.  q_item will be a pointer
 * to stuct items.
 */
struct qelem {
	struct qelem *q_forw;
	struct qelem *q_back;
	struct item  *q_item;
};

extern jmp_buf env_buf;		/* where to jump on timeouts	*/
extern FILE *logfd;		/* log file file descriptor	*/
#ifdef PUCC
extern char *findcluster();
#endif PUCC

/* These items are gleaned from the configuration file...	*/

extern struct qelem *rules, 	/* list of idle timeout rules	*/
		    *exmpt, 	/* list of exemptions		*/
		    *session;	/* list of session limit rules  */
extern int sleeptime;		/* how long to sleep between scans of utmp    */
extern int m_threshold;	/* number of users for multiple login warnings        */
extern int s_threshold; /* number of users (sds-ports)  fork session limits   */
extern int warn_flags;	/* what warnings to make	                      */
SHAR_EOF
#	End of shell archive
exit 0



More information about the Mod.sources mailing list