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

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