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