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

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