home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / compsrcs / unix / volume07 / untamo3 < prev    next >
Encoding:
Text File  |  1988-09-11  |  42.5 KB  |  1,853 lines

  1. Subject:  v07i079:  Log out idle users (untamo revised)
  2. Newsgroups: mod.sources
  3. Approved: mirror!rs
  4.  
  5. Submitted by: Wombat <rsk@j.cc.purdue.edu>
  6. Mod.sources: Volume 7, Issue 79
  7. Archive-name: untamo3
  8.  
  9.  
  10. Hopefully, this one will be easier for the net at large to deal with.
  11.  
  12. ---Rsk
  13.  
  14. #    This is a shell archive.
  15. #    Remove everything above and including the cut line.
  16. #    Then run the rest of the file through sh.
  17. #----cut here-----cut here-----cut here-----cut here----#
  18. #!/bin/sh
  19. # shar:    Shell Archiver
  20. #    Run the following text with /bin/sh to create:
  21. #    README
  22. #    Makefile
  23. #    untamo.cf
  24. #    doc
  25. #    untamo.8l
  26. #    untamo.cf.5l
  27. #    insque.c
  28. #    list.c
  29. #    untamo.c
  30. #    warn.c
  31. #    zap.c
  32. #    parse.y
  33. #    scan.l
  34. #    ttydev.h
  35. #    untamo.h
  36. cat << \SHAR_EOF > README
  37. Untamo is a locally developed daemon which periodically wakes up and
  38. logs off idle terminals; it also can deal with multiply-logged in
  39. users.  It is configurable without recompilation, and features tunable
  40. parameters such as maximum allowed idle time, maximum allowable
  41. multiple logins, exemption lists, and so on.  (Note: session limits
  42. do not work at the moment.)
  43.  
  44. We use this program to ensure availability of one of our scarcer
  45. resources: terminals.  Others may find it useful for different reasons;
  46. preventing users from leaving a terminal logged-in and unattended
  47. for hours is probably a reasonable security measure.
  48.  
  49. The original posting caused a deluge of mail, as the sources contained
  50. references to local include files.  This has been fixed by #ifdef'ing
  51. the code which applies only locally.  This version has been successfully
  52. compiled on a Sequent Balance 21000 running Dynix v2.0.6, a CCI 6/32
  53. running 4.3bsd, a DEC VAX-8600 running 4.3bsd, a Gould PowerNode 9080
  54. Running UTX-32 Release 1.3, and and a VAX-11/780 running 4.2bsd.
  55.  
  56. Please address correspondence concerning untamo to "doc" on this machine;
  57. the original author has left Purdue, but Craig Norborg (aka "doc") is
  58. reasonably familiar with the program.
  59. -- 
  60. Rich Kulawiec, pucc-j!rsk, rsk@j.cc.purdue.edu, rsk@purdue-asc.arpa
  61. SHAR_EOF
  62. cat << \SHAR_EOF > Makefile
  63. # Makefile for /usr/src/new/untamo
  64. #
  65. # Author : Craig Norborg, Purdue University Computing Center
  66. # To configure this for your system, be sure to check out untamo.h
  67. # Also, if compiling on generic 4.2BSD system, be sure to make dependancies
  68. # or remove all occurances of "rld" stuff from dependancies lines.  -doc
  69.  
  70. # C compiler flags for PUCC.
  71. # Note, when the DEBUG flag is set, the RLD_FILE is changed to /tmp/rlds.
  72. #INCLUDE = 
  73. #DEFS = -DPUCC -DBSD4_2
  74. #CFLAGS = -O ${DEFS} ${INCLUDE}
  75. #LIBS = -lacct
  76.  
  77. # C compiler flags for generic BSD4_2
  78. INCLUDE = 
  79. DEFS = -DBSD4_2
  80. CFLAGS = -O ${DEFS} ${INCLUDE}
  81. LIBS =
  82.  
  83. #install flags
  84. DEST = /usr/new/etc
  85. FDEST = /usr/local/lib
  86. OWNER = doc
  87. GROUP = system
  88. MODE = 751
  89. SHMODE = 755
  90. FMODE = 644
  91.  
  92. # Source files to be mkdepended
  93. SRC = insque.c list.c untamo.c warn.c zap.c
  94. SRCl = parse.y scan.l
  95. SRCg = parse.c scan.c
  96. HDR = ttydev.h untamo.h
  97. OBJ = insque.o list.o parse.o scan.o untamo.o warn.o zap.o
  98.  
  99. # programs that need explicit make lines, plain files
  100. NSTD = untamo
  101. FILE = untamo.cf doc
  102. MAN  = untamo.8l untamo.cf.5l
  103.  
  104. all: ${NSTD} ${FILE}
  105.  
  106. clean:
  107.     rm -f a.out ${OBJ} core errs lint.errs Makefile.bak *.s tags \
  108.         ${SRCg} ${NSTD} y.tab.* lex.yy.c
  109.  
  110. depend: ${SRC} ${SRCg} ${HDR}
  111.     maketd -a ${DEFS} ${INCLUDE} ${SRC} ${SRCg}
  112.  
  113. install: all 
  114.     -for p in ${NSTD}; do \
  115.         install -c -m ${MODE} -o ${OWNER} -g ${GROUP} -s $$p ${DEST}; \
  116.     done
  117.     for p in ${FILE}; do \
  118.         install -c -m ${FMODE} -o ${OWNER} -g ${GROUP} -s $$p ${FDEST};\
  119.     done
  120.  
  121. lint: ${SRC} ${SRCg}
  122.     lint -hxn ${DEFS} ${SRC} ${SRCg}
  123.     
  124. print: 
  125.     lpr -p -Pstaff -J"Untamo Source" Makefile ${SRCl} ${SRC} ${HDR}
  126.  
  127. shar:
  128.     shar README Makefile ${FILE} ${MAN} ${SRC} ${SRCl} ${HDR} > untamo.shar
  129.  
  130. source: ${SRC} ${SRCl} ${HDR} ${MAN} ${FILE}
  131.  
  132. spotless:
  133.     rm -f a.out ${OBJ} core errs lint.errs Makefile.bak y.tab.* yacc.act\
  134.         yacc.tmp *.s ${NSTD} tags parse.c scan.c
  135.     rcsclean ${SRC} ${SRCl} ${HDR} ${FILE} ${MAN}
  136.  
  137. tags: ${SRCS} ${SRCg} tags
  138.     ctags ${SRC} ${SRCg}
  139.  
  140. ${SRC} ${SRCl} ${HDR} ${FILE} ${MAN}:
  141.     co $@
  142.  
  143. # rules for everybody in ${NSTD} go here
  144. untamo: parse.o scan.o insque.o list.o untamo.o warn.o zap.o
  145.     cc ${CFLAGS} -o untamo insque.o list.o parse.o scan.o untamo.o \
  146.                 warn.o zap.o ${LIBS}
  147. y.tab.h: parse.c
  148.  
  149. parse.c:
  150.     yacc -d parse.y
  151.     mv y.tab.c parse.c
  152.  
  153. scan.c:
  154.     lex scan.l
  155.     mv lex.yy.c scan.c
  156.  
  157. # DO NOT DELETE THIS LINE - make depend DEPENDS ON IT
  158. I=/usr/include
  159. S=/usr/include/sys
  160.  
  161. insque.o: insque.c
  162.  
  163. list.o: $I/setjmp.h $I/stdio.h $S/file.h $S/types.h list.c untamo.h y.tab.h
  164.  
  165. untamo.o: $I/pwd.h $I/setjmp.h $I/signal.h $I/stdio.h $S/file.h $S/ioctl.h \
  166.     $S/jioctl.h $S/stat.h $S/ttychars.h $S/ttydev.h $S/types.h $I/utmp.h \
  167.     untamo.c untamo.h y.tab.h
  168.  
  169. untamo.o: $I/setjmp.h $I/stdio.h $S/types.h untamo.h
  170.  
  171. warn.o: $I/setjmp.h $I/signal.h $I/stdio.h $S/ioctl.h $S/jioctl.h \
  172.     $S/ttychars.h $S/ttydev.h $S/types.h untamo.h warn.c y.tab.h
  173.  
  174. zap.o: $I/setjmp.h $I/stdio.h $S/file.h $S/ioctl.h $S/jioctl.h $S/ttychars.h \
  175.     $S/ttydev.h $S/types.h $S/wait.h $I/utmp.h untamo.h zap.c
  176.  
  177. parse.o: $I/grp.h $I/setjmp.h $I/stdio.h $S/types.h parse.c untamo.h
  178.  
  179. scan.o: $I/stdio.h scan.c y.tab.h
  180.  
  181. # *** Do not add anything here - It will go away. ***
  182. SHAR_EOF
  183. cat << \SHAR_EOF > untamo.cf
  184. *
  185. *       how long to sleep between checks
  186. *
  187.    sleep 5
  188. *
  189. *
  190. *
  191.    threshold multiple 30
  192.    threshold session  43
  193. *
  194. *       now list the timeout rules
  195. *
  196.    timeout cluster public 45
  197.    timeout default 60
  198. *
  199. *     "necessary" exemptions
  200. *
  201.    exempt tty console all
  202.    exempt group staff multiple
  203.    exempt group system multiple
  204.    exempt login kdr all
  205. SHAR_EOF
  206. cat << \SHAR_EOF > doc
  207. .nr PS 12
  208. .TL
  209. The 'Untamo' Project
  210. .AU
  211. Andy Wilcox
  212. .AI
  213. Purdue University Computing Center
  214. .PP
  215. The Purdue University Computing Center needs more machines and
  216. terminals to provide better services to our users.  The purchase
  217. of more machines and terminals will come in time.  However, ways are needed
  218. now to more evenly distribute our present resources.
  219. The Untamo project is an attempt to solve these and related problems.
  220. .PP
  221. There is always a problem at the beginning of the semester with naive
  222. users who turn their terminals off without logging out.  Many of these
  223. unfortunate souls will soon be in the consultants office explaining
  224. how their account was booby trapped by a malicious user.  Untamo will
  225. decide that a terminal is idle if the keyboard has not been touched
  226. for thirty minutes (this choice of time is under investigation).  Untamo
  227. will then log out idle terminals after a warning message has been sent to them.
  228. This will help combat this serious security problem, and save large
  229. amounts of work on the user's part if his or her files were deleted.
  230. .PP
  231. Sometimes it is advantageous to have idle terminals, for example, a
  232. Vaxen console.  Untamo also provides an 'exemption' feature that allows 
  233. such cases.
  234. .PP
  235. Late in the semester when final projects are due, there are inevitably
  236. waiting lines for the terminals, and ideally we would prefer one user to
  237. be logged on only once.  With job control in c-shell, there is no
  238. need to be logged in more than once.  Untamo looks for these multiple logins,
  239. warns each one of them, and then will log out all but the first terminal
  240. to be logged on.
  241. .PP
  242. Once again, there are exceptions to the case of multiple logins.  
  243. Many times the consultants can log a person in again and fix a hung terminal
  244. by killing a runaway process without having to call the console room.
  245. Untamo allows a few minutes of multiple login time, which is enough time
  246. to locate and kill a runaway process.
  247. .PP
  248. It is important to note that Untamo will primarily affect our public
  249. terminals.  Most other terminals will be 'exempt'.
  250. .PP
  251. Untamo is a dynamically reconfigurable program, so if there is a 
  252. problem with someone needing more idle time, or a multiple login,
  253. it can be added without killing Untamo, recompiling, reinstalling, 
  254. and restarting.  These changes take effect in a few minutes, so the
  255. wait is not long.  
  256. .PP
  257. The amount of time Untamo 'sleeps' between checking logins and idle times
  258. is also redefinable, it can be made larger by the operator on a 
  259. heavily loaded machine to allow more time for a multiple login.  This 
  260. necessary human interaction with Untamo is questionable, and 
  261. ways of making the sleep time a function of the 
  262. load are being investigated.  In any case, the operator always has
  263. final control of Untamo.
  264. SHAR_EOF
  265. cat << \SHAR_EOF > untamo.8l
  266. .TH UNTAMO 8L PUCC
  267. .SH NAME
  268. untamo \- Idle terminal and multiple login monitor daemon.
  269. .SH SYNOPSIS
  270. .B /usr/local/etc/untamo
  271. .SH DESCRIPTION
  272. .PP
  273. .I Untamo
  274. wakes up at regular intervals and scans /etc/utmp to see
  275. which users are currently logged in, how long they have been idle,
  276. whether they are logged in more than once, etc.
  277. .I Untamo
  278. then warns and logs out users based on a set of rules in its
  279. configuration file.
  280. .I Untamo
  281. is usually started from /etc/rc.local.
  282. .PP
  283. .I Untamo
  284. uses a configuration file,
  285. .IR untamo.cf ,
  286. to find out how long a terminal must be unused to be considered
  287. \*(lqidle\*(rq, and which users, groups, terminals, or clusters of
  288. terminals are exempt from being logged out.
  289. .PP
  290. .I Untamo
  291. gets its name from the Finnish god of sleep and dreams.
  292. .SH FILES
  293. .TP
  294. .B /usr/local/lib/untamo.cf
  295. Configuration file which specifies how often untamo is to wake up, and
  296. exemptions to rules, etc.
  297. .TP
  298. .B /usr/adm/untamo.log
  299. Log of when untamo is started, killed, who it logs off and why, and any
  300. errors it encounters.
  301. .SH DIAGNOSTICS
  302. Various \*(lqcouldn't open ...\*(rq error messages.  Since untamo
  303. dissacociates itself from the invoking terminal, most of the errors
  304. get put in the log file.
  305. .SH SEE ALSO
  306. untamo.cf(5L), utmp(5)
  307. .SH BUGS
  308. If a user logs off and then on again fast enough and manages to get a
  309. different tty, he may be warned about a multiple login.
  310. SHAR_EOF
  311. cat << \SHAR_EOF > untamo.cf.5l
  312. .TH UNTAMO.CF 5L "PUCC
  313. .SH NAME
  314. /usr/local/lib/untamo.cf \- untamo configuration file format
  315. .SH DESCRIPTION
  316. .IR Untamo,
  317. the login monitor daemon, decides how it should act based on the file
  318. untamo.cf.
  319. .PP
  320. The file untamo.cf consists of a series of specifications which each
  321. describe an aspect of untamo's actions.  There are four types of commands: 
  322. .IP 1.
  323. .B Exemption  
  324. commands
  325. specify login ids that are exempt from untamo's actions.
  326. .IP 2.
  327. .B Timeout
  328. commands
  329. define how long a terminal must be idle before it is logged out.
  330. .IP 3.
  331. .B Sleep
  332. commands
  333. set the number of minutes untamo should sleep between checks, 
  334. as well as the amount of time between warnings and logouts.
  335. .IP 4.
  336. .B Threshold
  337. commands
  338. set the threshold number of users who must be logged in before multiple
  339. login control takes effect.  If the number of users on the system exceeds
  340. the threshold, then warnings and logouts for multiple logins will be issued.
  341. .PP
  342. Lines beginning with an asterisk in column one are comments.
  343. Other lines may be indented for readability.
  344. .SH EXEMPTIONS
  345.  
  346. .PP
  347. Exemptions have the form:
  348.  
  349. .IP
  350. .B exempt
  351. .I who
  352. .I from
  353.  
  354. .PP
  355. .I Who
  356. must be one of:
  357. .IP
  358. .B cluster 
  359. .I name
  360. .IP
  361. .B login
  362. .I id
  363. .IP
  364. .B group
  365. .I groupname
  366. .IP
  367. .B tty
  368. .I ttyname
  369. .IP
  370. .B rld
  371. .I num
  372. .PP
  373. where
  374. .I name 
  375. must be a terminal cluster name such as \*(lqpublic\*(rq or \*(lqstaff\*(rq.
  376. (See the PUCC in-house document Z0-TERMFIL for more information about clusters.)
  377. .I Id
  378. must be a valid login id as in /etc/passwd.
  379. .I Groupname
  380. must be a valid group name as in /etc/groups.
  381. .I Ttyname
  382. is a terminal name as in /etc/utmp (e.g., \*(lqtty\fIXX\fP\*(rq).
  383. .I Num
  384. must be an rld (terminal) number.
  385.  
  386. .I From 
  387. must be one of:
  388. .IP
  389. .B multiple
  390. .IP
  391. .B idle
  392. .IP
  393. .B session
  394. .IP
  395. .B all
  396. .PP
  397. which specify that
  398. .I who
  399. is exempt from being logged off for maintaining multiple logins,
  400. remaining idle longer than the idle timeout period, exceeding a login
  401. session limit, or all three.
  402.  
  403. .SH TIMEOUTS 
  404. .PP
  405. Timeouts are of the form:
  406. .IP
  407. .B timeout
  408. .I who
  409. .I minutes
  410. .br
  411. .B timeout default
  412. .I minutes
  413. .PP
  414. In the first form,
  415. .I who
  416. must be a valid login id and 
  417. .I minutes 
  418. a decimal number.
  419. This form indicates that
  420. .I who
  421. will be logged off after remaining idle for
  422. .I minutes
  423. minutes.  The second form sets a default idle timeout that affects
  424. anyone not otherwise exempted from timeouts or mentioned by login id
  425. in an explicit timeout rule.
  426.  
  427. .SH SLEEP
  428. .PP
  429. Sleep commands are of the form:
  430. .IP
  431. .B sleep
  432. .I minutes
  433. .PP
  434. and specify that untamo will sleep 
  435. .I minutes
  436. minutes between its checks.
  437. If there is more than one sleep specification, only the last one is used.
  438. .PP
  439. .SH THRESHOLD
  440. Threshold commands are of the form:
  441. .IP
  442. .B threshold 
  443. .I number 
  444. .PP
  445. This specifies that at least 
  446. .I number
  447. ttys must be active before untamo will check for multiple logins.
  448. If there is more than one
  449. .I threshold
  450. command, only the last one is used.
  451. .SH EXAMPLE
  452. .PP
  453. A sample untamo configuration file follows:
  454. .br
  455. .nf
  456.    *
  457.    * sleep 5 minutes between checks
  458.    *
  459.        sleep 5
  460.  
  461.    *
  462.    * 30 users must be logged on before
  463.    * multiple login checks will begin
  464.    *
  465.        threshold 30
  466.    *
  467.    * Set the default idle timeout to 60 mintes.  For the
  468.    * public cluster, set the idle timeout to 45 minutes.
  469.    *
  470.        timeout cluster public 45
  471.        timeout default 60
  472.    *
  473.    * Necessary exemptions to make sure the console doesn't
  474.    * get logged off and staff members can log in more than once.
  475.    *
  476.        exempt tty console all
  477.        exempt group staff multiple
  478.  
  479. .fi
  480. .SH "SEE ALSO"
  481. untamo(8L), utmp(5), rld(1L)
  482. SHAR_EOF
  483. cat << \SHAR_EOF > insque.c
  484. /* Queue insertion and deletion routines for non-Vaxen */
  485. struct qelem { 
  486.     struct qelem *q_forw , *q_back;
  487. #ifdef LINT
  488.     char q_data[];
  489. #endif LINT
  490. };
  491.  
  492. #ifndef VAX
  493. insque( elem , pred )
  494.     register struct qelem *elem , *pred;
  495. {
  496.     elem->q_forw = pred->q_forw;
  497.     pred->q_forw = elem;
  498.     elem->q_forw->q_back = elem;
  499.     elem->q_back = pred;
  500. }
  501.  
  502. #endif VAX
  503. SHAR_EOF
  504. cat << \SHAR_EOF > list.c
  505. #ifdef PUCC
  506. #include <sys/types.h>
  507. #include <rld.h>
  508. #endif PUCC
  509. #include <sys/file.h>
  510. #include "untamo.h"
  511. #include "y.tab.h"
  512.  
  513. extern char *malloc(), *strcpy(), *ctime();
  514. extern strlen();
  515. time_t time();
  516. long lseek();
  517.  
  518. /*
  519.  * addlist -- adds a record to the list "list".  Uses the 
  520.  *          insque(3) call for speed.
  521.  *          list -- which list to add the rule to (rules or exempt)
  522.  *          type -- what kind of rule (LOGIN, GROUP, etc)
  523.  *          name -- who it applies to (ajw, console, etc)
  524.  *          num --  who it applies to (1505 (rld), 5 (group staff) )
  525.  *          flag -- idle time for a "rule" rule, or exemption type
  526.  *              for "exempt" rule: IDLE, MULTIPLE, etc.
  527.  */
  528. addlist(list, type, name, num, flag)
  529. struct qelem *list;
  530. char *name;
  531. int type, flag, num;
  532. {
  533.     struct qelem *new_node;
  534.     struct qelem *ptr;
  535.     struct item  *new_data;
  536.  
  537.     /* 
  538.      * make all the new structures
  539.      */
  540.     new_node = (struct qelem *) malloc( sizeof(struct qelem) );
  541.     new_data = (struct item  *) malloc( sizeof(struct item ) );
  542.     new_data->name_t = type;
  543.     new_data->name = name;
  544.     new_data->num = num;
  545.     new_data->flag = flag;
  546.     new_node->q_item = new_data; 
  547.     /*
  548.      * find where to insert it in the list
  549.      */
  550.     ptr = list->q_forw;
  551.     while (ptr != list)    {
  552.         if (ptr->q_item->name_t <= new_data->name_t)
  553.             break;
  554.         ptr = ptr->q_forw;
  555.     }
  556.     /*
  557.      * and insert it
  558.      */
  559.     (void) insque(new_node,ptr->q_back);
  560. }
  561.  
  562.  
  563. /*
  564.  * freelist -- frees up the space in the list pointed to 
  565.  *           by ptr.  Uses free.
  566.  */
  567. freelist(ptr)
  568. struct qelem *ptr;
  569. {
  570.     struct qelem *dead;
  571.     struct qelem *start;
  572.  
  573.     start = ptr;
  574.     ptr = ptr->q_forw;
  575.     while ( ptr != start )   {
  576.         dead = ptr;
  577.         ptr = ptr->q_forw;
  578.         (void) free( (char *) dead->q_item);      /* kill the data */
  579.         (void) free( (char *) dead );        /* now get the node */
  580.     }
  581.     start->q_forw = start;        /* reset pointers for a null list */
  582.     start->q_back = start;
  583. }
  584.  
  585.  
  586. /*
  587.  * setlimits -- looks through the rules list and uses the most
  588.  *        specific rule for users[i], then looks through
  589.  *        the exceptions and uses the (again) most specific
  590.  *        exemption for the user.
  591.  */
  592.  
  593. setlimits(i)
  594. int i;
  595. {
  596.     struct qelem *ptr;
  597.     int rule;
  598.     time_t tempus;
  599.  
  600.     /*
  601.      * look down the rules list and set the
  602.      * most specific rule that applies to 
  603.      * this user
  604.      */
  605.     rule = 0;
  606.     (void) time(&tempus);
  607.     ptr = rules->q_back;        /* start at the end of the list */
  608.     users[i].warned = 0;        /* clear his warning and exempt flag */
  609.     users[i].exempt = 0;        /* to avoid granularity problems */
  610.  
  611.     users[i].next = tempus;        /* next time is set to now, so */
  612.                     /* the new rules (or exemptions) */
  613.                     /* take affect immediately */
  614.     /* 
  615.      * while we haven't looked through the whole list,
  616.      * and we haven't found a rule...
  617.      */
  618.     while ( (ptr != rules) && (!rule) )    {
  619.         if ( (rule = find(ptr, i)) )  {
  620.             users[i].idle = (ptr->q_item)->flag * 60;
  621.             users[i].exempt &= ~IS_IDLE;
  622.         } else
  623.             ptr = ptr->q_back;        /* move back one */
  624.     }
  625.  
  626.     rule = 0;
  627.     ptr = session->q_back;
  628.     while ( (ptr != session) && (!rule) ){
  629.         if ( (rule = find(ptr, i)) )  {
  630.             users[i].session = (ptr->q_item)->flag * 60;
  631.             users[i].exempt &= ~IS_LIMIT;
  632.         } else
  633.             ptr = ptr->q_back;        /* move back one */
  634.     }
  635.     /*
  636.      * now look down the exemptions list and
  637.      * set the first exemptions for this user.
  638.      */
  639.     rule = 0;
  640.     ptr = exmpt->q_back;        /* start at the end of the list */
  641.     while ( (ptr != exmpt) && (!rule) )    {
  642.         if ( (rule = find(ptr, i)) )  {
  643.             if ( (ptr->q_item)->flag == ALL)
  644.                 users[i].exempt |= IS_IDLE|IS_MULT|IS_LIMIT;
  645.             else if ( (ptr->q_item)->flag == IDLE)
  646.                 users[i].exempt |= IS_IDLE;
  647.             else if ( (ptr->q_item)->flag == MULTIPLE)
  648.                 users[i].exempt |= IS_MULT;
  649.             else if ( (ptr->q_item)->flag == SESSION)
  650.                 users[i].exempt |= IS_LIMIT;
  651.         } else
  652.             ptr = ptr->q_back;        /* move back one */
  653.     }
  654. }
  655.  
  656.  
  657. /*
  658.  * find -- given a rule (or exemption) and a users, see if it applies.
  659.  *       Example: If the rule is a LOGIN type rule, compare the name with that
  660.  *       of the user to see if it applies.  If we find a match, return 1,
  661.  *       else return 0.
  662.  */
  663. find(ptr,i)
  664. int i;
  665. struct qelem *ptr;
  666. {
  667.     switch ( (ptr->q_item)->name_t )    {
  668.     case LOGIN: 
  669.         if (!strcmp( (ptr->q_item)->name, users[i].uid) )
  670.             return(1);
  671.         break;
  672.     case TTY:
  673.         if (!strcmp( (ptr->q_item)->name, (users[i].line+5) ) ) 
  674.             return(1);
  675.         break;
  676.     case GROUP:
  677.         if ( (ptr->q_item)->num == users[i].ugroup ) 
  678.             return(1);
  679.         break;
  680. #ifdef PUCC
  681.     case CLUSTER:
  682.         if (!strcmp( (ptr->q_item)->name, users[i].clust) )
  683.             return(1);
  684.         break;
  685.     case RLD:
  686.         if ( (ptr->q_item)->num == users[i].rld ) 
  687.             return(1);
  688.         break;
  689. #endif PUCC
  690.     case DEFAULT:
  691.         return(1);
  692.         break;
  693.     }
  694.     return(0);
  695. }
  696.  
  697. #ifdef PUCC
  698.  
  699. #define makestr(Z)    (strcpy(malloc( (unsigned) strlen(Z)+1),Z))
  700.  
  701. /*
  702.  * findcluster -- passes an rld number, it returns a char *
  703.  *          to the name of the cluster associated with it
  704.  */
  705. char *
  706. findcluster(rld)
  707. int rld;
  708. {
  709.     int fd;
  710.     static struct rld_data data;
  711.  
  712. #ifdef BSD2_9
  713.     if ( (fd = open(RLD_FILE,O_RDONLY) ) < 0 )  {
  714. #else BSD2_9
  715.     if ( (fd = open(RLD_FILE,O_RDONLY,0) ) < 0 )  {
  716. #endif BSD2_9
  717.         (void) error("Can't open rld file.");
  718.         exit(1);
  719.     }
  720.  
  721.     /* 
  722.      * seek to the proper rld and read it.
  723.      */
  724.     (void) lseek(fd, rld*sizeof(data), 0);
  725.  
  726.     (void) read(fd, (char *) &data, sizeof(data) );
  727.  
  728.     (void) close(fd);
  729.  
  730.     /*
  731.      * return the cluster name, if null, return "public"
  732.      */
  733.     if (data.rld_cluster)
  734.         return(makestr(data.rld_cluster));
  735.     else
  736.         return(makestr("public"));
  737. }
  738. #endif PUCC
  739. SHAR_EOF
  740. cat << \SHAR_EOF > untamo.c
  741. #ifdef PUCC
  742. #include <sys/types.h>
  743. #include <rld.h>
  744. #endif PUCC
  745. #include <utmp.h>
  746. #include <signal.h>
  747. #include <sys/file.h>
  748. #ifndef F_OK
  749. #    define F_OK 0
  750. #endif F_OK
  751. #include <sys/ioctl.h>
  752. #include "untamo.h"
  753. #include <sys/stat.h>
  754.  
  755. #ifdef PUCC
  756. #define PWIDLE    6    /*accounting bit for no idle time logout     */
  757. #define PWMULT    7    /*accounting bit for multiple logins allowed */
  758. #include <passwd.h>
  759. struct usrpwd *getupnam();
  760. #else PUCC
  761. #include <pwd.h>
  762. #endif PUCC
  763.  
  764. #include "y.tab.h"
  765.  
  766. struct user users[MAXUSERS];
  767. struct user *pusers[MAXUSERS];
  768.  
  769. extern char *malloc(), *strcpy(), *ctime() , *strcat();
  770. extern unsigned strlen();
  771. extern time_t time();
  772.  
  773. struct qelem *rules, 
  774.          *session,
  775.          *exmpt;    /* lists for timeouts, session limits, and exemptions */
  776.  
  777. jmp_buf env_buf;
  778. int sleeptime;        /* time to sleep between checks              */
  779. FILE *logfd;        /* log file file descriptor pointer          */
  780. int m_threshold;    /* number of users before multiple limits       */
  781. int s_threshold;    /* number of users for session limits          */
  782. int warn_flags = IS_IDLE | IS_MULT | IS_LIMIT; 
  783.             /* what sorts of warnings should be accepted    */
  784.  
  785. main(n_args, ppch_args)
  786.     int n_args;
  787.     char **ppch_args;
  788. {
  789.     struct utmp utmpbuf;
  790.     struct stat statbuf;
  791. #ifdef PUCC
  792.     struct usrpwd *pswd;
  793. #else PUCC
  794.     struct passwd *pswd;
  795. #endif PUCC
  796.     struct user *user;
  797.     char pathbuf[20];
  798.     int utmptr, utmpfd;
  799.     time_t conf_oldstamp;
  800.     int userptr;
  801.     int res, td;
  802.     int new;        /* if the configuration file is new */
  803.     time_t tempus;
  804.     int finish(), wakeup();
  805.     FILE *conffd, *freopen(), *fopen();
  806.     /* command line flags */
  807.     int fl_multiple = 1, fl_session = 1, fl_idle = 1;
  808.  
  809.     while( --n_args && *++ppch_args && **ppch_args == '-' ) {
  810.         while( *++(*ppch_args) ){
  811.             switch( **ppch_args ){
  812.             case 'm': /* don't even think about multiple logins */
  813.                 fl_multiple = 0;
  814.                 break;
  815.             case 'i': /* don't even think about idle timeouts */
  816.                 fl_idle = 0;
  817.                 break;
  818.             case 's': /* don't even think about session limits */
  819.                 fl_session = 0;
  820.                 break;
  821.             default:
  822.                 fprintf( stderr, 
  823.                 "untamo: bad flag -%c\n", **ppch_args );
  824.                 break;
  825.             }
  826.         }
  827.     }
  828.  
  829.     if( !fl_multiple && !fl_idle && !fl_session ){
  830.         /* do absolutely nothing!! */
  831.         exit(0);
  832.     }
  833.  
  834. #ifdef PUCC
  835.     if ( access( "/flags/testsys" , F_OK ) == 0 ) 
  836.         exit(0);                /* dont run in test mode */
  837. #endif PUCC
  838.     (void) signal(SIGHUP,  SIG_IGN);    
  839.     (void) signal(SIGQUIT, SIG_IGN);
  840.     (void) signal(SIGINT,  SIG_IGN);
  841. #ifdef BSD4_2
  842.     (void) signal(SIGTTOU, SIG_IGN);
  843.     (void) signal(SIGTSTP, SIG_IGN);
  844. #endif BSD4_2
  845.  
  846.     (void) signal(SIGTERM, finish);
  847.     (void) signal(SIGALRM, wakeup);
  848.     conf_oldstamp = 1;            /* a very old stamp */
  849.     /*
  850.      * set up new header nodes for each of the lists.
  851.      * The forw and back pointers must point to them
  852.      * selves so the system insque routine can be used
  853.      */
  854.     rules = (struct qelem *) malloc( sizeof(struct qelem) );
  855.     exmpt = (struct qelem *) malloc( sizeof(struct qelem) );
  856.     session = (struct qelem *) malloc( sizeof(struct qelem) );
  857.     rules->q_forw = rules->q_back = rules;
  858.     exmpt->q_forw = exmpt->q_back = exmpt;
  859.     session->q_forw = session->q_back = session;
  860.     rules->q_item = session->q_item = exmpt->q_item = NULL;
  861.  
  862.     if ( (logfd = fopen(LOGFILE,"a")) > 0)  {
  863.         (void) time(&tempus);
  864.         (void) fprintf(logfd,"%24.24s  Untamo started\n",ctime(&tempus) );
  865.         (void) fclose(logfd);
  866.     } else {
  867.         (void) fprintf( stderr , "Untamo: couldn't open log file: %s\n", LOGFILE );
  868.         exit(1);
  869.     }
  870.  
  871.     if ( (res = fork()) < 0)    {
  872.         (void) fprintf(stderr,"Untamo: couldn't start\n");
  873.     }
  874.     if (res){  /* if the parent */
  875. #ifdef DEBUG
  876.         exit(res);
  877. #else
  878.         exit(0);
  879. #endif DEBUG
  880.     }
  881.  
  882.     /*
  883.      * lose our controlling terminal
  884.      */
  885. #ifdef BSD2_9
  886.     td = open("/dev/tty", O_RDWR);
  887. #else BSD2_9
  888.     td = open("/dev/tty", O_RDWR, 0600);
  889. #endif BSD2_9
  890.     if (td >= 0){
  891.         (void) ioctl(td, TIOCNOTTY, (char *)0);
  892.         (void) close( td );
  893.     }
  894.  
  895.     /*
  896.      * now sit in an infinite loop and work
  897.      */
  898.  
  899. while (1){
  900.     if ( stat(CONFIG,&statbuf) < 0)  {
  901.         (void) error("Untamo: couldn't stat conf file");
  902.         exit(1);
  903.     }
  904.  
  905.     if ( statbuf.st_mtime > conf_oldstamp ) {
  906.         conf_oldstamp = statbuf.st_mtime;
  907.  
  908.         if ( (conffd = freopen(CONFIG, "r", stdin)) < 0) {
  909.             (void) error("Untamo: can't open configuration file");
  910.             exit(1);
  911.         }
  912.  
  913.         /*
  914.          * get rid of the old rules and exempt lists
  915.          */
  916.         (void) freelist(rules);
  917.         (void) freelist(exmpt);
  918.         (void) freelist(session);
  919.         m_threshold = 0;
  920.         s_threshold = 0;
  921.         /*
  922.          * now read the configuration file and set up the
  923.          * rules and exemption lists
  924.          */
  925.         (void) yyparse();
  926.         new = 1;
  927.     } else
  928.         new = 0;
  929. #ifdef BSD2_9
  930.     if ( (utmpfd = open(UTMP, O_RDONLY)) < 0) {
  931. #else BSD2_9
  932.     if ( (utmpfd = open(UTMP, O_RDONLY, 0)) < 0) {
  933. #endif BSD2_9
  934.         (void) error("Untamo: can't open /etc/utmp");
  935.         exit(1);
  936.     } /* } <-- to match ifdefed open... */
  937.  
  938.     utmptr = 0;
  939.     userptr = 0;
  940.     /*
  941.      * look through the utmp file, compare each entry to the users
  942.      * array to see if an entry has changed.  If it has, build a new
  943.      * record for that person, if it hasn't, see  if it is time to
  944.      * examine him again.
  945.      */
  946.     while ( (res = read(utmpfd, (char *)&utmpbuf, sizeof(struct utmp)) ) > 0 ) {
  947.  
  948.         if (res != sizeof(struct utmp)) {
  949.             (void) error("Untamo: error reading utmp file, continuing");
  950.             continue;
  951.         }
  952.         (void) time(&tempus);
  953.         if (utmpbuf.ut_name[0] != '\0')   {
  954.             user = &users[utmptr];
  955.             if ( !(strcmp(user->uid,utmpbuf.ut_name)) &&
  956.                (user->time_on == utmpbuf.ut_time) )    {
  957.                 if (new)
  958.                     (void) setlimits(utmptr);
  959.                 if (fl_idle && tempus > user->next) {
  960.                     (void) checkidle(utmptr);
  961.                 }
  962.             } else {
  963.                 /*
  964.                  * build a new record
  965.                  */
  966.                 user->warned  = 0;
  967.                 (void) strcpy(pathbuf,DEV);
  968.                 (void) strcat(pathbuf,utmpbuf.ut_line);
  969. #ifdef PUCC
  970.                 user->rld = findrld(pathbuf);
  971.                 (void) strcpy(user->clust, findcluster(user->rld));
  972. #endif PUCC
  973.  
  974.                 (void) strcpy(user->line, pathbuf);
  975.                 (void) stat(pathbuf,&statbuf);
  976.                 (void) strcpy(user->uid, utmpbuf.ut_name);
  977.  
  978. #ifdef PUCC
  979.                 pswd = getupnam(utmpbuf.ut_name);
  980.                 user->ugroup = pswd->up_gid;
  981. #else PUCC
  982.                 pswd = getpwnam(utmpbuf.ut_name);
  983.                 user->ugroup = pswd->pw_gid;
  984. #endif PUCC
  985.  
  986.                 user->time_on = utmpbuf.ut_time;
  987.                 (void) setlimits(utmptr);
  988.  
  989. #ifdef PUCC
  990.                 if( pswd->up_flags & (1l << PWMULT ))
  991.                     user->exempt |= IS_MULT;
  992.                 if( pswd->up_flags & (1l << PWIDLE ))
  993.                     user->exempt |= IS_IDLE;
  994. #endif PUCC
  995.                 user->next = tempus;
  996.              }
  997.             pusers[userptr++] = user;
  998.         }
  999.         utmptr++;
  1000.     }
  1001.  
  1002.     (void) close(utmpfd);
  1003.     (void) fclose(conffd);
  1004.  
  1005. #ifdef PUCC
  1006.     /*
  1007.     ** check session limits
  1008.     */
  1009.  
  1010.     if( fl_session ){
  1011.         (void) chk_session(userptr);
  1012.     }
  1013. #endif PUCC
  1014.  
  1015.     /*
  1016.     ** check for and warn multiple logins
  1017.     */
  1018.  
  1019.     if( fl_multiple ){
  1020.         (void) chk_multiple(userptr);
  1021.     }
  1022.  
  1023.     /*
  1024.     ** wait sleeptime minutes
  1025.     */
  1026.  
  1027.     (void) sleep( (unsigned) sleeptime * 60);
  1028.     }
  1029. }
  1030.  
  1031. #ifdef PUCC
  1032. /*
  1033.  * chk_session( users ) 
  1034.  * find out how many people are on sds ports, 
  1035.  * and try to warn enough people to get below the threshold
  1036.  */
  1037.  
  1038. chk_session( n_users )
  1039.     register int n_users;
  1040. {
  1041.     register int which_user;
  1042.     time_t tempus;
  1043.     register int n_sds_ports = 0;
  1044.     static int fl_sessionlimits = 0;
  1045.  
  1046.     (void) time(&tempus);
  1047.     for( which_user = 0; which_user < n_users ; which_user++ ){
  1048.         if( pusers[which_user]->warned & IS_LIMIT ){
  1049.             (void) warn(which_user,IS_LIMIT);
  1050.         } else if( is_sds_port( pusers[which_user]->rld ) ){
  1051.             n_sds_ports++;
  1052.         }
  1053.     }
  1054.  
  1055.     if( n_sds_ports > s_threshold && !fl_sessionlimits ){
  1056.         (void) close( creat( "/flags/sessionlimits", 0600 ));
  1057.         fl_sessionlimits = 1;
  1058.     }
  1059.  
  1060.     if( n_sds_ports < s_threshold && fl_sessionlimits ){
  1061.         unlink( "/flags/sessionlimits" );
  1062.         fl_sessionlimits = 0;
  1063.     }
  1064.         
  1065.     while( n_sds_ports>s_threshold  && s_threshold>0 && which_user>0 ){
  1066.         which_user--;
  1067.         if( tempus-pusers[which_user]->time_on > pusers[which_user]->session 
  1068.             && pusers[which_user]->session > 2*60 
  1069.             && !(pusers[which_user]->warned & IS_LIMIT)  ){
  1070.  
  1071.             (void) warn(which_user,IS_LIMIT);
  1072.             n_sds_ports--;
  1073.         }
  1074.     }
  1075. }
  1076.  
  1077.  
  1078. int 
  1079. is_sds_port( rld_number )
  1080.     int rld_number;
  1081. {
  1082.     int fd, res;
  1083.     struct rld_data rdat;
  1084.  
  1085.     /*
  1086.      * Get to the right place in /etc/rlds
  1087.      * Complements of Jeff Smith...
  1088.      */
  1089.     if( fd = open (RLD_FILE, O_RDONLY, 0) >= 0 ) {
  1090.         lseek (fd, (long) rld_number * sizeof (struct rld_data), L_SET);
  1091.         res = read (fd, (char *) &rdat, sizeof (struct rld_data));
  1092.         (void) close(fd);
  1093.         if (res == sizeof(struct rld_data) && rdat.rld_tio != -1) {
  1094.             return 1;
  1095.         }
  1096.     }
  1097.     return 0;
  1098. }
  1099. #endif PUCC
  1100.  
  1101. /*
  1102.  * chk_multiple -- given the number of users (i), warn any of 
  1103.  *           them  that have multiple logins.  Calls qsort(3)
  1104.  *           to sort them by id.
  1105.  */
  1106. chk_multiple(i)
  1107. int i;
  1108. {
  1109.     int j, comp(); 
  1110.     int match, skip = -1;
  1111.     int wait = 0;
  1112.  
  1113.     if( i < m_threshold && m_threshold > 0 ) { /* below threshold...*/
  1114.         return;
  1115.     }
  1116.     (void) qsort( (char *) pusers, i, sizeof(struct user *), comp);
  1117.     for (j=0; j<i-1; j++)    {
  1118.         /*
  1119.          * if not all the multiple logins logged out,
  1120.          * decide on one not to kill, clear his warned
  1121.          * bit, and continue.  But don't look again until
  1122.          * we have passed all the guys with the same login.
  1123.          */
  1124.         if ( wait == 0 )  {
  1125.             match = 0;
  1126.             skip = decide(j, i, &wait);
  1127.         } else
  1128.             wait--;
  1129.  
  1130.         if ( ( (*pusers[j]).exempt & IS_MULT) || (j == skip) )  {
  1131.             continue;    /* he's exempt ! */
  1132.         }
  1133.  
  1134.         if ( !strcmp( (*pusers[j]).uid, (*pusers[j+1]).uid) )    {
  1135.             match = 1;
  1136.             (void) warn(j,IS_MULT);
  1137.         } else {
  1138.             if ( match )
  1139.                 (void) warn(j,IS_MULT);
  1140.             match = 0;
  1141.         }
  1142.     }
  1143. }
  1144.  
  1145.  
  1146. /*
  1147.  * decide -- given a bunch of multiply logged on terminals that did
  1148.  *          not heed the warning, decide returns the index into the 
  1149.  *         *pusers array of the user NOT to log off.  Wait is the
  1150.  *           number of ids that chk_multiple must skip before calling
  1151.  *         decide again.  Admittedly this is gross, but it works.
  1152.  */
  1153. decide(j, num, wait)
  1154. int j, num, *wait;
  1155. {
  1156.     int i;
  1157.     int count = 1;
  1158.     int warned = 1;
  1159.     int skip;
  1160.  
  1161.     /* 
  1162.      * look through the users and find how many 
  1163.      * of login (*pusers[i]).uid are logged on
  1164.      * and whether or not they have been warned
  1165.      */
  1166.     for ( i=j; i<num; i++)  {
  1167.         if ( !((*pusers[i]).warned & IS_MULT) )
  1168.             warned = 0;
  1169.  
  1170.         if ( !strcmp( (*pusers[i]).uid, (*pusers[i+1]).uid) )
  1171.             count++;
  1172.         else
  1173.             break;
  1174.     }
  1175.     /*
  1176.      * now, if there is a need to skip someone, do it
  1177.      */
  1178.     *wait = count-1;
  1179.     if ( (warned) && (count > 1) )  {
  1180.         skip = j;
  1181.         /*
  1182.          * set skip to the alpha-numerical least tty
  1183.          */
  1184.         for(i=j+1; i<j+count; i++)
  1185.             if (strcmp((*pusers[skip]).line,(*pusers[i]).line)>0)
  1186.                 skip = i;
  1187.         (*pusers[skip]).warned &= ~IS_MULT;
  1188.         return(skip);
  1189.     }
  1190.     return(-1);
  1191. }
  1192.  
  1193.     
  1194. /*
  1195.  * finish -- end Untamo
  1196.  */
  1197. finish()
  1198. {
  1199.     time_t tempus;
  1200.     FILE *logfd;
  1201.  
  1202.     (void) signal(SIGTERM, SIG_IGN);
  1203.     (void) time(&tempus);
  1204.     (void) unlink( "/flags/sessionlimits");
  1205.     if ( (logfd = fopen(LOGFILE,"a")) > 0)  {
  1206.         (void) time(&tempus);
  1207.         (void) fprintf(logfd,"%24.24s  Untamo killed.\n",ctime(&tempus) );
  1208.         (void) fclose(logfd);
  1209.     }
  1210.     exit(0);
  1211. }
  1212.  
  1213.  
  1214. /*
  1215.  * comp -- used by qsort to sort by id
  1216.  */
  1217. comp(h1, h2)
  1218. struct user **h1, **h2;
  1219. {
  1220.     return( strcmp((**h1).uid, (**h2).uid) );
  1221. }
  1222.  
  1223.  
  1224. /* 
  1225.  * checkidle -- given a user, see if we want to warn him about idle time.
  1226.  *        first check the exempt vector to see if he is exempt.
  1227.  */
  1228. #define min(a,b) ( (a)<(b)?(a):(b) )
  1229.  
  1230. checkidle(i)
  1231. int i;
  1232. {
  1233.     struct stat statbuf;
  1234.     time_t tempus;
  1235.  
  1236.     (void) time(&tempus);
  1237.     (void) stat(users[i].line,&statbuf);
  1238. #ifdef DEBUG
  1239.     { static char debugprint[80];
  1240.       sprintf(debugprint,"**debug: checkidle(%d); %d %d %x\n",
  1241.         i, users[i].session, users[i].idle, users[i].exempt);
  1242.       error(debugprint);
  1243.       sprintf(debugprint," *debug: %s %s\n", users[i].line, users[i].uid);
  1244.       error(debugprint);
  1245.     }
  1246. #endif DEBUG
  1247.     if (( tempus - statbuf.st_atime) < users[i].idle ) {
  1248.         users[i].warned &= ~IS_IDLE;
  1249.     } else {
  1250.         if (users[i].idle > 2*60 && !(users[i].exempt & IS_IDLE)) {
  1251.             (void) warn(i,IS_IDLE);
  1252.         }
  1253.     }
  1254.     users[i].next = min( statbuf.st_atime + users[i].idle,
  1255.                  users[i].time_on + users[i].session );
  1256. }
  1257. SHAR_EOF
  1258. cat << \SHAR_EOF > warn.c
  1259. #include <signal.h>
  1260. #include <sys/ioctl.h>
  1261. #include "untamo.h"
  1262. #include "y.tab.h"
  1263.  
  1264. extern char *malloc() , *strcpy() , *ctime();
  1265. extern unsigned strlen();
  1266.  
  1267. /*
  1268.  * warn -- warn a user that he is logged in multiply, or has
  1269.  *       been idle past his limit.  Uses the magic of setjmp
  1270.  *       and longjmp to avoid a timeout on a terminal write.
  1271.  */
  1272. warn(i,type)
  1273. int i, type;
  1274. {
  1275.     int res;
  1276.     int opened;
  1277.     struct user *him;
  1278.     FILE *termf;
  1279.  
  1280.     if( !(type & warn_flags )) { /* we are not doing this warning now.. */
  1281.         return 0;
  1282.     }
  1283.  
  1284.     if (type == IS_IDLE )
  1285.         him = &users[i];
  1286.     else  /* type == IS_MULT || type == IS_LIMIT */
  1287.         him = pusers[i];
  1288.  
  1289.     if ( (res=vfork()) < 0)    {
  1290.         (void) error("couldn't fork in warn");
  1291.         exit(0);
  1292.     }
  1293.     /* 
  1294.      * the parent returns after it
  1295.      * has modified the global data structures
  1296.      * that the child obviously can't
  1297.      */
  1298.     if (res)   {
  1299.         if ( (type == IS_MULT) && !(him->warned & IS_MULT) )
  1300.             him->warned |= IS_MULT;
  1301.         if ( (type == IS_IDLE) && !(him->warned & IS_IDLE) )
  1302.             him->warned |= IS_IDLE;
  1303. #ifdef PUCC
  1304.         if ( (type == IS_LIMIT) && !(him->warned & IS_LIMIT) )
  1305.             him->warned |= IS_LIMIT;
  1306. #endif PUCC
  1307.         return( wait(0) );
  1308.     }
  1309.     
  1310.     /*
  1311.      * child continues here
  1312.      */
  1313.     if (setjmp(env_buf) == 0) {
  1314.         opened = 0;
  1315.         (void) alarm(5);
  1316.         if ((termf = fopen( him->line, "w")) != NULL)  {
  1317.             opened = 1;
  1318.             /*
  1319.              *  start the terminal if stopped
  1320.              */
  1321.             (void) ioctl(fileno(termf),TIOCSTART,(char *) 0);
  1322.             if (type == IS_MULT)  {
  1323.                 if (him->warned & IS_MULT)  {
  1324.                     (void) zap( him );
  1325.                 } else {
  1326.                 (void)fprintf(termf,"\007\r\n\r\nThis user id is ");
  1327.                 (void)fprintf(termf,"logged on more than ");
  1328.                 (void)fprintf(termf,"once, please end\r\n");
  1329.                 (void)fprintf(termf,"all but one of your ");
  1330.                 (void)fprintf(termf,"logins in the next");
  1331.                 (void)fprintf(termf," %1d minutes\r\n",
  1332.                                sleeptime);
  1333.                 (void)fprintf(termf,"or you will be logged ");
  1334.                 (void)fprintf(termf,"out by the system.\r\n\r\n\007");
  1335.                 }
  1336.             }
  1337.             else if (type == IS_IDLE)  {
  1338.                 if( him->warned & IS_IDLE )    {
  1339.                     (void) zap( him );
  1340.                 } else {
  1341.                 (void)fprintf(termf,
  1342.                 "\007\r\n\r\nThis terminal has been idle %2d ",
  1343.                 him->idle/60);
  1344.  
  1345.                 (void)fprintf(termf,
  1346.                 "minutes.  If it remains\r\nidle for %1d ",
  1347.                 sleeptime);
  1348.  
  1349.                 (void)fprintf(termf,
  1350.                 "minutes it will be logged out by the system.");
  1351.                 (void)fprintf(termf,"\r\n\r\n\007");
  1352.                 }
  1353.             }
  1354.             else if (type == IS_LIMIT)  {
  1355.                 if( him->warned & IS_LIMIT )    {
  1356.                     (void) zap( him );
  1357.                 } else {
  1358.                 (void)fprintf(termf,
  1359.                 "\007\r\n\r\nThis terminal has been in use %2d",
  1360.                 him->session/60);
  1361.  
  1362.                 (void)fprintf(termf, " minutes.\nIn %1d ",
  1363.                 sleeptime);
  1364.  
  1365.                 (void)fprintf(termf,
  1366.                 "minutes it will be logged out by the system.");
  1367.                 (void)fprintf(termf,"\r\n\r\n\007");
  1368.                 }
  1369.             }
  1370.             (void) fclose(termf);
  1371.             opened = 0;
  1372.         } 
  1373.         (void) alarm(0);
  1374.     } else {
  1375.         /* we timed out */
  1376.  
  1377.         if ( opened ) {
  1378.            /* free FILE without write()   */
  1379.             termf->_ptr = termf->_base;
  1380.             (void) fclose(termf);
  1381.         }
  1382.     }
  1383.     exit(0);    /* child exits here */
  1384.     return(0);    /* lint doesnt believe in exit()... */
  1385. }
  1386.  
  1387.  
  1388. /*
  1389.  * wakeup -- signal handler for SIGALRM
  1390.  */
  1391. wakeup()
  1392. {
  1393.     (void) longjmp(env_buf, 1);
  1394. }
  1395. SHAR_EOF
  1396. cat << \SHAR_EOF > zap.c
  1397. #ifdef PUCC
  1398. #include <sys/types.h>
  1399. #endif PUCC
  1400. #include <utmp.h>
  1401.  
  1402. #ifdef BSD2_9
  1403. #include <wait.h>
  1404. #else BSD2_9
  1405. #include <sys/wait.h>
  1406. #endif BSD2_9
  1407.  
  1408. #include <sys/ioctl.h>
  1409. #include <sys/file.h>
  1410. #include "untamo.h"
  1411.  
  1412. #ifdef BSD2_9
  1413. #include <sys/param.h>
  1414. #define getdtablesize() NOFILE
  1415. #endif BSD2_9
  1416.  
  1417. extern char *malloc() , *ctime() , *strcpy();
  1418. time_t time();
  1419.  
  1420. /*
  1421.  * zap -- disconnect the person logged on to tty "dev".
  1422.  *       makes use of ioctl(2) to get a new controlling
  1423.  *      terminal and the infinitely clever vhangup(2)
  1424.  *       to disconnect it.  
  1425.  */
  1426. zap(him)
  1427. struct user *him;
  1428. {
  1429.     static char message[] = "\n\n\007Logged out by the system.\n";
  1430.     int dts, td;
  1431.     time_t tempus;
  1432.  
  1433.     /*
  1434.      * close all the child's descriptors 
  1435.      */
  1436.     dts = getdtablesize();
  1437.     for ( dts--; dts>=0; dts--){
  1438.         (void) close(dts);
  1439.     }
  1440.     /*
  1441.      * now tell him it's over, and disconnect.
  1442.      */
  1443. #ifdef BSD2_9
  1444.     td = open(him->line, O_RDWR);
  1445. #else BSD2_9
  1446.     td = open(him->line, O_RDWR, 0600);
  1447. #endif BSD2_9
  1448.     (void) ioctl(td, TIOCSTART, (char *)0);
  1449.     if (td >= 0)  {
  1450.         (void) write(td, message, sizeof(message) );
  1451.         (void) fsync(td);
  1452.         (void) ioctl(td, TIOCFLUSH, (char *)0);
  1453.         td = vhangup();
  1454.         (void) time(&tempus);
  1455.         if ( (logfd = fopen(LOGFILE,"a")) != NULL)  {
  1456.             (void)fprintf(logfd,
  1457.             "%19.19s : %s on %s because %s, vhangup returned %d\n",
  1458.             ctime(&tempus), him->uid, him->line, 
  1459.             ( (him->warned & IS_MULT) ? "multiple" : (him->warned & IS_IDLE ? "idle" : "session" )), td );
  1460.             (void)fclose(logfd);
  1461.         }
  1462.     } else {
  1463.         (void) time(&tempus);
  1464.         if ( (logfd = fopen(LOGFILE,"a")) != NULL)  {
  1465.             (void)fprintf(logfd,
  1466.             "%19.19s : couldn't open %s on %s\n",
  1467.             ctime(&tempus), him->line, him->uid );
  1468.             (void)fclose(logfd);
  1469.         }
  1470.     }
  1471. }
  1472.  
  1473.  
  1474. error(sb)
  1475. char *sb;
  1476. {
  1477.     if ( (logfd = fopen(LOGFILE,"a")) != NULL)   {
  1478.         (void)fprintf(logfd,"%s",sb);
  1479.         (void)fclose(logfd);
  1480.     }
  1481. }
  1482. SHAR_EOF
  1483. cat << \SHAR_EOF > parse.y
  1484. %{
  1485. #include <stdio.h> 
  1486. #include <grp.h>
  1487. #include "untamo.h"
  1488. struct group *grp;
  1489. char oct[5];        /* rld kludge */
  1490. char *name;
  1491. int  num;
  1492. extern char *yytext;
  1493.  
  1494.   /*
  1495.    *            vvvvvvvvvvvvvvvvvvvvvvvvvvvvv
  1496.    *               >>>>>>>>> IMPORTANT <<<<<<<<<
  1497.    *            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  1498.    *  The order of the tokens in the *first line* is significant.  
  1499.    *
  1500.    *  They dictate which rules and exemptions have precence.  This order 
  1501.    *  states that RLD is the  most significant, and Untamo will use an
  1502.    *  rld rule first if it has a choice.  The second two %token lines may
  1503.    *  be ordered anyway.  DEFAULT is the least specific, but will always
  1504.    *  match.  It must always remain in the last position.
  1505.    */ 
  1506. %}
  1507.  
  1508. %token RLD TTY LOGIN GROUP CLUSTER DEFAULT
  1509.  
  1510. %token EXEMPT TIMEOUT SLEEP SESSION
  1511. %token NUM IDLE MULTIPLE NAME ALL 
  1512. %token THRESHOLD NL
  1513.  
  1514. %union {
  1515.     char *sb; 
  1516.     int nb;
  1517.        }
  1518.  
  1519. %type <sb> NAME
  1520. %type <nb> NUM LOGIN GROUP TTY RLD ALL IDLE MULTIPLE 
  1521. %type <nb> who exempt_type name_type
  1522.  
  1523. %start cmd_cmd
  1524.  
  1525. %%
  1526.  
  1527. cmd_cmd    : /*EMPTY*/
  1528.     | cmd_cmd exempt_cmd
  1529.     | cmd_cmd idle_cmd
  1530.     | cmd_cmd sleep_cmd
  1531.     | cmd_cmd session_cmd
  1532.     | cmd_cmd thresh_cmd
  1533.     | cmd_cmd error NL
  1534.     | cmd_cmd NL
  1535.     ;
  1536.  
  1537. thresh_cmd : THRESHOLD MULTIPLE NUM NL
  1538.     {
  1539.         m_threshold = $3; 
  1540.     }
  1541.     | THRESHOLD SESSION NUM NL
  1542.     {
  1543.         s_threshold = $3; 
  1544.     }
  1545.     | THRESHOLD error NL
  1546.     {
  1547.         yyerror("Malformed threshold command.");
  1548.     }
  1549.     ;
  1550.     
  1551.  
  1552. exempt_cmd    : EXEMPT who exempt_type NL
  1553.         {
  1554.             addlist(exmpt,$2,name,num,$3);
  1555.         }
  1556.         | EXEMPT error NL
  1557.         {
  1558.             yyerror("Malformed exempt command.");
  1559.         }
  1560.         ;
  1561.  
  1562. session_cmd     : SESSION who NUM NL
  1563.         {
  1564.             addlist(session,$2,name,num,$3);
  1565.         }
  1566.         | SESSION error NL
  1567.         {
  1568.             yyerror("Malformed session command.");
  1569.         }
  1570.         ;
  1571.  
  1572. idle_cmd    : TIMEOUT who NUM NL
  1573.         {
  1574.             addlist(rules,$2,name,num,$3);
  1575.         }
  1576.         | TIMEOUT DEFAULT NUM NL
  1577.         {
  1578.             addlist(rules, DEFAULT, NULL, 0, $3);
  1579.         }
  1580.         | TIMEOUT error NL
  1581.         {
  1582.             yyerror("Malformed timeout command.");
  1583.         }
  1584.         ;
  1585.  
  1586. sleep_cmd    : SLEEP NUM NL
  1587.         {
  1588.             sleeptime = $2;
  1589.         }
  1590.         | SLEEP error NL
  1591.         {
  1592.             yyerror("Malformed sleep command.");
  1593.         }
  1594.         ;
  1595.  
  1596. who        : name_type NAME
  1597.         { 
  1598.             $$ = $1;
  1599.             name = $2;
  1600.             if ($1 == GROUP)  {
  1601.                 grp = getgrnam(name);
  1602.                 num = grp->gr_gid;
  1603.             }
  1604.         }
  1605.         | name_type NUM
  1606.         {
  1607.             $$ = $1;
  1608.             /*
  1609.              * Kludge alert: here we must convert the
  1610.              * rld number, which was read as decimal
  1611.              * (lex doesn't know any better...) to an 
  1612.              * octal so that it will jive with what
  1613.              * findrld (in libacct) returns.
  1614.              */
  1615.             if ($1 == RLD)  {
  1616.                 sprintf(oct,"%d",$2);
  1617.                 sscanf(oct,"%o",&num);
  1618.             } else
  1619.                 num = $2;
  1620.         }
  1621.         ;
  1622.  
  1623. name_type    : CLUSTER    { $$ = CLUSTER; }
  1624.         | LOGIN     { $$ = LOGIN;   }
  1625.         | GROUP        { $$ = GROUP;   }
  1626.         | TTY        { $$ = TTY;     }
  1627.         | RLD        { $$ = RLD;     }
  1628.         ;
  1629.  
  1630. exempt_type    : ALL        { $$ = ALL;      }
  1631.         | IDLE        { $$ = IDLE;      }
  1632.         | MULTIPLE    { $$ = MULTIPLE; }
  1633.         | SESSION    { $$ = SESSION;  }
  1634.         ;
  1635.  
  1636. %%
  1637. static int errorcnt = 0;
  1638.  
  1639. yyerror(sb)
  1640. char *sb;
  1641. {
  1642.     extern int linenum;
  1643.     char buf[128];
  1644.  
  1645.     sprintf(buf, "%s: line %d: %s\n", CONFIG, linenum, sb);
  1646.     error( buf );
  1647.     errorcnt++;
  1648. }
  1649.  
  1650. yywrap()
  1651. {
  1652.     extern int linenum;
  1653.  
  1654.     if( errorcnt > 0 ){
  1655.         error( "Aborting due to config file syntax errors.\n");
  1656.         exit( 1 );
  1657.     }
  1658.     linenum = 1;
  1659.     return 1;
  1660. }
  1661. SHAR_EOF
  1662. cat << \SHAR_EOF > scan.l
  1663.  
  1664. %{
  1665. /*
  1666.  *  Lex grammer to scan input file for untamo
  1667.  */
  1668. #include <stdio.h>
  1669. #include "y.tab.h"
  1670. #define makestr(Z)    ((char *)strcpy(malloc(strlen(Z)+1),Z))
  1671. int linenum = 1;    /* current line number for error messages */
  1672. %}
  1673.  
  1674. %%
  1675. exempt        return EXEMPT;
  1676. timeout        return TIMEOUT;
  1677. sleep        return SLEEP;
  1678. login        return LOGIN;
  1679. group        return GROUP;
  1680. tty        return TTY;
  1681. rld        return RLD;
  1682. cluster        return CLUSTER;
  1683. default        return DEFAULT;
  1684. idle        return IDLE;
  1685. multiple    return MULTIPLE;
  1686. session        return SESSION;
  1687. threshold    return THRESHOLD;
  1688. all        return ALL;
  1689.  
  1690. [/A-Za-z][/A-Za-z0-9_]*    {   
  1691.             yylval.sb = makestr(yytext);
  1692.             return NAME; 
  1693.         }
  1694.  
  1695. [0-9]+        {
  1696.             yylval.nb = atoi(yytext);
  1697.             return NUM;
  1698.         }
  1699.  
  1700. "*".*        ;
  1701. "\n"        { 
  1702.             linenum++; 
  1703.             return NL;
  1704.         }
  1705. [ \t]*        ;
  1706. .        {
  1707.             static char *errormsg ="Illegal character ' '.";
  1708.  
  1709.             errormsg[19] = yytext[0];
  1710.             yyerror( errormsg );
  1711.         }
  1712. SHAR_EOF
  1713. cat << \SHAR_EOF > ttydev.h
  1714. /*    ttydev.h    6.1    83/07/29    */
  1715.  
  1716. /*
  1717.  * Terminal definitions related to underlying hardware.
  1718.  */
  1719. #ifndef _TTYDEV_
  1720. #define    _TTYDEV_
  1721.  
  1722. /*
  1723.  * Speeds
  1724.  */
  1725. #define B0    0
  1726. #define B50    1
  1727. #define B75    2
  1728. #define B110    3
  1729. #define B134    4
  1730. #define B150    5
  1731. #define B200    6
  1732. #define B300    7
  1733. #define B600    8
  1734. #define B1200    9
  1735. #define    B1800    10
  1736. #define B2400    11
  1737. #define B4800    12
  1738. #define B9600    13
  1739. #define EXTA    14
  1740. #define EXTB    15
  1741.  
  1742. #ifdef KERNEL
  1743. /*
  1744.  * Hardware bits.
  1745.  * SHOULD NOT BE HERE.
  1746.  */
  1747. #define    DONE    0200
  1748. #define    IENABLE    0100
  1749.  
  1750. /*
  1751.  * Modem control commands.
  1752.  */
  1753. #define    DMSET        0
  1754. #define    DMBIS        1
  1755. #define    DMBIC        2
  1756. #define    DMGET        3
  1757. #endif
  1758. #endif
  1759. SHAR_EOF
  1760. cat << \SHAR_EOF > untamo.h
  1761. #include <sys/types.h>
  1762. #include <stdio.h>
  1763. #include <setjmp.h>
  1764.  
  1765. #define UTMP         "/etc/utmp"    /* name of utmp file */
  1766. #define TERMFILE    "/etc/termfile" /* name of termfile  */
  1767.  
  1768. #ifdef DEBUG
  1769. #define CONFIG        "/userf/doc/untamo/untamo.cf"
  1770. #define LOGFILE        "/userf/doc/untamo/untamo.log"
  1771. #else DEBUG
  1772. #define CONFIG        "/usr/local/lib/untamo.cf"
  1773. #define LOGFILE        "/usr/adm/untamo.log"
  1774. #endif DEBUG
  1775.  
  1776. #define DEV        "/dev/\0"
  1777.  
  1778. #define MAXUSERS    100        /* max people expected */
  1779. #define NAMELEN        8        /* length of login name */
  1780. #define IS_IDLE        01
  1781. #define IS_MULT        02
  1782. #define IS_LIMIT    04
  1783.  
  1784. struct user {
  1785. #ifdef PUCC
  1786.     int rld;        /* rld number last logged in to */
  1787.     char clust[7];         /* cluster */
  1788. #endif PUCC
  1789.     int idle;        /* max idle limit for this user */
  1790.     int ugroup;        /* gid obtained from getgrent call */
  1791.     int session;        /* session limit for this user */
  1792.     int warned;        /* if he has been warned before */
  1793.     int exempt;        /* what is this guy exempt from? */
  1794.     time_t next;        /* next time to examine this structure */
  1795.     time_t time_on;        /* loggin time (express terminals?) */
  1796.     char uid[NAMELEN];    /* who is this is? */
  1797.     char line[14];        /* his tty in the form "/dev/ttyxx"*/
  1798.     char site[10];        /* where */
  1799. };
  1800.  
  1801. /* 
  1802.    next will be cur_time+limit-idle do all we have to do is check
  1803.    the current time against the action field when the daemon comes
  1804.    around.  if >= then it's time to check the idle time again, else
  1805.    just skip him.
  1806. */
  1807.  
  1808. extern struct user users[];
  1809. extern struct user *pusers[];
  1810.  
  1811. /*
  1812.  * records that the nodes of the linked list 
  1813.  * will have pointers too
  1814.  */
  1815. struct item {
  1816.     int name_t;    /* is it a login, group, etc... */
  1817.     char *name;    /* which login, etc */
  1818.     int num;    /* which rld (special case, rld is a #)*/
  1819.             /* or group -doc*/
  1820.     int flag;    /* what is the timeout/exemption ? */
  1821. };
  1822.  
  1823. /*
  1824.  * necessary structures to use the system
  1825.  * linked list stuff.  q_item will be a pointer
  1826.  * to stuct items.
  1827.  */
  1828. struct qelem {
  1829.     struct qelem *q_forw;
  1830.     struct qelem *q_back;
  1831.     struct item  *q_item;
  1832. };
  1833.  
  1834. extern jmp_buf env_buf;        /* where to jump on timeouts    */
  1835. extern FILE *logfd;        /* log file file descriptor    */
  1836. #ifdef PUCC
  1837. extern char *findcluster();
  1838. #endif PUCC
  1839.  
  1840. /* These items are gleaned from the configuration file...    */
  1841.  
  1842. extern struct qelem *rules,     /* list of idle timeout rules    */
  1843.             *exmpt,     /* list of exemptions        */
  1844.             *session;    /* list of session limit rules  */
  1845. extern int sleeptime;        /* how long to sleep between scans of utmp    */
  1846. extern int m_threshold;    /* number of users for multiple login warnings        */
  1847. extern int s_threshold; /* number of users (sds-ports)  fork session limits   */
  1848. extern int warn_flags;    /* what warnings to make                          */
  1849. SHAR_EOF
  1850. #    End of shell archive
  1851. exit 0
  1852.  
  1853.