v06i002: Untamo, another idle daemon (untamo)

sources-request at mirror.UUCP sources-request at mirror.UUCP
Tue Jun 17 10:12:38 AEST 1986


Submitted by: Wombat <cca!caip!pur-ee!pucc-j.Purdue.EDU!rsk>
Mod.sources: Volume 6, Issue 2
Archive-name: untamo


This is "untamo", a locally-developed program (daemon) which logs off
idle terminals, quashes multiple logins, and so forth; it is configurable
to the individual sites' taste.  See the README in the shell archive for
more details.
-- 
Rich Kulawiec, pucc-j!rsk, rsk at asc.purdue.edu

#	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
#	doc
#	insque.c
#	list.c
#	parse.y
#	scan.l
#	ttydev.h
#	untamo.8l
#	untamo.c
#	untamo.cf
#	untamo.h
#	untamocf.5l
#	warn.c
#	zap.c
# This archive created: Thu May 29 17:12:16 1986
# By:	Wombat (Purdue University)
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.

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.

Please address correspondence concerning untamo to me; the author has
left Purdue.
-- 
Rich Kulawiec, pucc-j!rsk, rsk at asc.purdue.edu
SHAR_EOF
cat << \SHAR_EOF > Makefile
# Makefile for /usr/src/new/untamo
#
# Author : Marc Mengel, Purdue University Computing Center

# programs that need explicit make lines, plain files
NSTD = untamo
FILE = untamo.cf

# C compiler flags
INCLUDE = 
DEFS = -DPUCC -DBSD4_2
CFLAGS = -O ${DEFS} ${INCLUDE}

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

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

all: ${MAKEFILES} ${STD} ${NSTD} ${LIB} ${FILE}

clean: ${MAKEFILES} 
	rm -f a.out *.o core errs lint.errs Makefile.bak *.s y.tab.h\
		${SRCg} ${STD} ${NSTD} ${LIB}

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

install: all 
	-for p in ${STD} ${NSTD} ${ACCT}; 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 -Pstaff -J"Untamo Source" Makefile ${SRCl} ${SRC}

spotless:
	rm -f a.out *.o core errs lint.errs Makefile.bak y.tab.* yacc.act\
		yacc.tmp *.s ${STD} ${NSTD} ${SCRIPT} ${LIB}
	rcsclean Makefile ${SRC} ${SSRC}

${MAKEFILES} ${SRC} ${SRCl} ${FILE}:
	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 -lacct
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}/rld.h ${I}/setjmp.h ${I}/sgtty.h ${I}/stdio.h ${S}/file.h
list.o: ${S}/ioctl.h ${S}/jioctl.h ${S}/ttychars.h ${S}/ttydev.h
list.o: ${S}/types.h list.c untamo.h y.tab.h

untamo.o: ${I}/passwd.h ${I}/rld.h ${I}/setjmp.h ${I}/sgtty.h ${I}/signal.h
untamo.o: ${I}/stdio.h ${S}/file.h ${S}/ioctl.h ${S}/jioctl.h ${S}/stat.h
untamo.o: ${S}/ttychars.h ${S}/ttydev.h ${S}/types.h ${I}/utmp.h untamo.c
untamo.o: 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
warn.o: ${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
zap.o: ${S}/ttychars.h ${S}/ttydev.h ${S}/types.h ${S}/wait.h ${I}/utmp.h
zap.o: untamo.h zap.c

parse.o: ${I}/grp.h ${I}/setjmp.h ${I}/stdio.h ${S}/types.h parse.c
parse.o: 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 > doc
.nr PS 12
.TL
The 'Untamo' Project
.AU
Andy Wilcox and Marc Mengel
.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
Another problem has arisen since the advent of unrestricted terminal access,
namely people staying logged on for long periods of time, and causing huge
queues to build up waiting for ports on busy machines.
We have recorded times in the queue in excess of 2 hours, and have had eight
deep rlogins on ports from people avoiding going back out to the switch.
The apparently neccesary solution to these problems is session limits. 
Untamo will now enforce session limits -- after the specified session limit
has expired, the user is given a warning, and soon thereafter logged off.
.PP
Similar to idle time, there are terminals and/or people who need to be
logged on indefinitely.
Untamo provides exemptions for session limits to provide for theses situations.
.PP
Untamo is also usage sensitive.
It can be configured with a threshold number of users for both multiple login
restriction and session limit enforcement.
This allows Untamo to enforce these policies only when they are needed
(i.e. when ports are scarce), and not at other times.
.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 > 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
#include <sys/types.h>
#include <rld.h>
#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;
	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;
	case DEFAULT:
		return(1);
		break;
	}
	return(0);
}


#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
		(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"));
}
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.8l
.TH UNTAMO 8L PUCC
.SH NAME
untamo \- Idle terminal and multiple login monitor daemon.
.SH SYNOPSIS
.B /usr/new/etc/untamo
.SH DESCRIPTION
.PP
Untamo wakes up at regular intervals and scans /etc/utmp for information
about who is currently logged in, how long they have been idle, etc. and
logs people out who are idle or multiply logged in.  
Untamo is usually started from /etc/rc.local.
.PP
Untamo uses a configuration file, untamo.cf, to find out how long a 
terminal must be unused to be "idle" and which
users, groups, terminals, or clusters of terminals are exempt from
being logged out.
.PP
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 "couldn't open ..." error messages.  Since untamo dissacociates
itself from the invoking terminal, most of the errors get put in the
log file.
.SH SEE ALSO
untamo.config(5L), utmp(5), termfile(5L)
.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.
.LP
Creative users can defeat the idle timeout and keep terminals "locked"
indefinitely.
SHAR_EOF
cat << \SHAR_EOF > untamo.c
#include <sys/types.h>
#include <utmp.h>
#include <rld.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
#include <passwd.h>
#define PWIDLE	6	/*accounting bit for no idle time logout     */
#define PWMULT	7	/*accounting bit for multiple logins allowed */
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;
	/* 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\n" );
		exit(1);
	}

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

	/*
	 * 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);
		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);
				user->rld = findrld(pathbuf);

				(void) strcpy(user->line, pathbuf);
				(void) strcpy(user->clust, findcluster(user->rld));
				(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);

	/*
	** check session limits
	*/

	if( fl_session ){
		(void) chk_session(userptr);
	}

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

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

	/*
	** wait sleeptime minutes
	*/

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

/*
 * 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 ){
		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));
		close(fd);
		if (res == sizeof(struct rld_data) && rdat.rld_tio != -1) {
			return 1;
		}
	}
	return 0;
}

/*
 * 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);
	}
#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 > 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 > 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		"/usr/src/new/etc/untamo/untamo.cf"
#define LOGFILE		"/usr/src/new/etc/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 {
	int rld;		/* rld number last logged in to */
	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 */
	char clust[7]; 		/* cluster */
};

/* 
   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;	/* whick rld (special case, rld is a #) */
	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	*/
extern char *findcluster();

/* 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
cat << \SHAR_EOF > untamocf.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 commands  which describe some
aspect of how untamo should act.  There are 4 types of commands: 
.IP 1.
.B exemptions  
specify people who are exempt from untamo
.IP 2.
.B timeouts
define how long a terminal must be unused to be considered idle.
.IP 3.
.B sleep
sets the number of minutes untamo should sleep between checks, 
and the amount of time between warnings and logouts.
.IP 4.
.B minusers
sets the threshold number of users for multiple logins.  
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 are comments.
.SH Exemptions
.PP
Exemptions have the form:

.B exempt
.I who
.I from

where 
.I who
is 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
and
.I name 
is a terminal cluster name as per /etc/termfile -- such as public or staff, 
.I id
is a valid user id,
.I groupname
is a valid group name as per /etc/groups,
.I ttyname
is a terminal name as per /etc/utmp, and
.I num
is an rld number; and
.I from 
is one of:
.IP
.B multiple
.IP
.B idle
.IP
.B all
.PP
indicate that
.I who
is exempt from being logged off for having multiple logins and/or being idle
more than the idle timeout time.

.SH Timeouts 
.PP
Timeouts take the form:
.IP
.B timeout
.I who
.I minutes
.PP
where
.I who
is the same as above, but can also be
.B default
, and 
.I minutes 
is a decimal number. This says that the idle timeout for
.I who
is
.I minutes
minutes.

.SH Sleep
.PP
Sleep commands look like:
.IP
.B sleep
.I minutes
.PP
and specify that untamo will sleep 
.I minutes
minutes between scans of /etc/utmp.
If there is more than one sleep specification, only the last one is used.
.PP
.SH Minusers
Minusers commands look like:
.IP
.B minusers 
.I number 
.PP
This specifies that at least 
.I number
ttys must be active for untamo to check for multiple logins.
If there is more than one minusers command, the last one is taken.
.PP
A sample untamo configuration file follows:
.br
.nf
   * sample untamo configuration
   sleep 5
   minusers 30
   timeout cluster public 45
   timeout default 60
   exempt tty console all
   exempt group staff multiple

.fi
This indicates that
.IP
untamo should sleep 5 minutes between scans of utmp.
.IP
no multiple login warnings or logouts should be issued if less than 30
people are logged in.
.IP
terminals in the public sites should be logged out after 45 minutes of
idle time.
.IP
all other terminals should be logged out after 60 minutes of idle time.
.IP
the console is exempt from being logged off for any reason.
.IP
staff members may log on multiply.
.SH "SEE ALSO"
untamo(8L), utmp(5), termfile(5L), rld(1L)
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;
		if ( (type == IS_LIMIT) && !(him->warned & IS_LIMIT) )
			him->warned |= IS_LIMIT;
		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
#include <sys/types.h>
#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;
{
	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, strlen(message) );
		(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
#	End of shell archive
exit 0



More information about the Mod.sources mailing list