[comp.os.vms] Nanny Source V1.0 3/7

zar@JULIET.CALTECH.EDU.UUCP (06/18/87)

$!------------------------------Cut Here-------------------------
$ chk = "224578872"
$ create nm1.for
$ deck/dollars="ThEgReAtZaR"
        program         Nanny
c
c       My Daemon (Nanny)
c
c       Written by Daniel M. Zirin for Zar Limited
c       ZAR @ CITCHEM (bitnet) ZAR @ XHMEIA.Caltech.Edu ([192.12.19.15] arpa)
c       XHMEIA::ZAR (area 5 node 920 span/hepnet)
c
c       command syntax:
c               command ret_mail_box parameters ascii_blank
c
c Nanny input bit masks:
c       Debugging        0 - No debugging messages
c       (Bits 31 & 32    1 - Do not execute vital functions and display
c       of the Function      extra messages
c       mask)            2 - Execute all functions and display extra
c                            messages
c
c       Function mask   Bit  Meaning
c                        1 - Allow batch priority dither
c                        2 - Allow detach priority dither
c                        3 - Allow normal user priority dither
c                        4 - Allow network process priority dither
c                        5 - Allow subprocess priority dither
c                        6 - CPU total is accumulated by all dup procs
c                        7 - Allow suspending on low memory
c                        8 - Allow batch processes to be suspended
c                        9 - Allow detach processes to be suspended
c                       10 - Allow normal processes to be suspended
c                       11 - Allow network processes to be suspended
c                       12 - Allow subprocesses to be suspended
c                       13 - If set, logoff entire process tree when
c                            idle. If clear, logoff process with no
c                            subprocesses first
c                       14 - If set, system users are subject to idle-
c                            time logout (interactive only)
c                       15 - Give idle time warning @1/2 maxidle
c                       16 - Give idle time warning @<60 seconds left
c                       17 - Give CPU warning @time-cycle
c                       18 - Allow preference dither
c                       19 - Not used
c                       27 - Not used
c                       28 - Assign/system node name and load average
c                            to sys$announce
c                       29 - Assign/system the day of week and allow
c                            time reset for DST
c                       30 - Reserved
c                       31 - Debugging bit 1
c                       32 - Debugging bit 2
c
c       Command msk     Bit  Meaning
c                        1 - Disable ADDACC command
c                        2 - Disable DIE command
c                        3 - Disable ENTER command
c                        4 - Disable FORGET command
c                        5 - Disable FREE command
c                        6 - Disable GRAB command
c                        7 - Disable IGNORE command
c                        8 - Disable KILL command
c                        9 - Disable LISTEN command
c                       10 - Disable NEW command
c                       11 - Disable ODIS command
c                       12 - Disable OEN command
c                       13 - Disable QSTART command
c                       14 - Disable QSTOP command
c                       15 - Disable REQUEUE command
c                       16 - Disable RESUME command
c                       17 - Disable STOP command
c                       18 - Disable SUSPEND command
c                       19 - Disable WAKE command *
c                       20 - Disable WCLR command *
c                       21 - Disable WSHOW command *
c                       22 - Not used
c                       32 - Not used
c
c       OUTPUT routine   0 - Output to OPA0: only
c                        1 - Output to logfile only
c                        2 - Output to OPA0: and logfile
c
c * = Returns special values/output in return mailbox. See help file.
c
c       Created         Zar ltd.    20-May-83
c       Revised         Zar ltd.    30-Jun-83
c       Revised         Zar ltd.     2-Aug-83
c       Revised         Zar ltd.    30-Oct-83
c       Revised         Zar ltd.     5-Oct-85 for VMS V4
c       Revised         Zar ltd.     9-Jun-87
c
c       (c) Zar Ltd. 1983,1985,1986,1987
c
        parameter       pcb$v_batch    = '00e'x
        parameter       pcb$v_netwrk   = '015'x
        implicit        integer*4 (a-z)
        include         'nanny.inc'
        include         '($brkdef)'
        include         '($ssdef)'
        character*100   message
        character*12    username(maxuser)
        character*10    terminal(maxuser),phy_term(maxuser)
        character*9     cur_date
        character*8     account(maxuser)
        character*2     old_date
        integer*4       authpriv(maxuser,2)
        integer*4       bufio(maxuser)
        integer*4       cputim(maxuser)
        integer*4       dirio(maxuser)
        integer*4       ncputim(maxuser,max_average)
        integer*4       owner(maxuser)
        integer*4       pageflts(maxuser)
        integer*4       second3(2)
        integer*4       seedpid
        integer*4       sts(maxuser)
        integer*4       warn(maxuser)
        integer*4       wssize(maxuser)
        integer*4       wstore(2)
        integer*2       o_seqnum
        integer*2       uic(maxuser,2)
        integer*2       usedpri(0:31)
        real*4          new_cputim
        real*4          preadd
        logical*1       dst_chk
        logical*1       dumy
        logical*1       first
        logical*1       limwarn(maxuser)
        logical*1       resumeok
        logical*1       suspendok
        logical*1       susp_flg(maxuser)
        logical*1       warned(maxuser)
        logical*1       ws_mem_purge
        external        nanjpi,gotmess,wait_rel,boost
c
c       Initialize some variables
c
        die            = 0
        cycle_num      = 0
        suspids        = 0
        maxwstot       = 0
        c_uic(2)       = 1            !Fake current UIC is the SYSMGR's
        c_uic(1)       = 4
        wstore(1)      = '7fffffff'x
        wstore(2)      = 0
        startup_flg    = .false.
        listen         = .false.
        first          = .true.
        debugging      = 0
        resumeok       = .false.
        do i=1,maxuser
         pid(i)        = 0
         ipid(i)       = 0
         susp_flg(i)   = .false.
         limwarn(i)    = 0
         do j=1,max_average
          ncputim(i,j) = 0
         end do
        end do
        call sys$bintim('0 0:0:3.0',second3)
        call date(cur_date)
        old_date    = cur_date(1:2)
        call waker('1',0,c_uic,old_date(1:1))      !Init the wake-up calls
c
c       Declerations done, lets annoy some users
c
        code = sys$setprn('Nanny')
c
c       Get parameters from input file.
c
        call new_log(dumy)
        if (dumy.ne.ss$_normal) goto 3
c
c       Day of week setup
c
        call dayweek(.true.)
        dst_chk = .true.
c
c       Create a mailbox for communication to the outside world and
c       to read termination messages when sending messages to the
c       system managers.
c               (needs PRMMBX priv)
c
        code = sys$crembx(%val(1),mbxchan,,,,,'NANNYS$BOX')
        if (bug(code,'CREMBX').ne.ss$_normal) call sys$exit(%val(code))
        code = sys$crembx(%val(0),mbx2,,,,,)
        if (bug(code,'CREMBX').ne.ss$_normal) call sys$exit(%val(code))
        code = sys$crembx(%val(0),mbx3,,,,,'NANNY$PEEK')
        if (bug(code,'CREMBX').ne.ss$_normal) call sys$exit(%val(code))
        call loop
c
c       (start of infinite loop)
c
c       Set a timer to wake-up after hibernating
c
        call output(0,'Logfile initialized')
1       continue
        suspendok   = .false.
        ws_mem_purge= .true.
        seedpid     = -1
        jpicode     = 0
        o_seqnum    = -1
        wstotal     = 0
        highbat     = -1
        lowbat      = 32
        cycle_num   = cycle_num + 1
        do i = 0, 31
         usedpri(i) = 0
        end do
        code        = sys$setimr(,truewait,wait_rel,%val(1))
        if (bug(code,'SETIMR').ne.ss$_normal) goto 4
c
c       As long as there are users
c
        do while (jpicode.ne.ss$_nomoreproc)
c
c       Do the $GETJPI service in EXECutive mode
c               (needs CMEXEC priv)
c
         ranjpipid = seedpid
         jpicode   = sys$cmexec(nanjpi,)
         seedpid   = ranjpipid
         if (jpicode.ne.ss$_nopriv.and.jpicode.ne.ss$_nomoreproc.and.
     1   jpicode.ne.ss$_suspended) jpicode = bug(jpicode,'GETJPI')
         if ((jpicode.eq.ss$_normal.or.jpicode.eq.ss$_suspended).and.
     1   c_pid.ne.0) then
c
c       Get the current user's sequence number(PROC_INDEX)
c
c          point     = lib$extzv(0,8,c_pid) + 1  !commented out for VMS 4
           point     = c_ipid
           maxseqnum = max(maxseqnum,point)
c          if (o_seqnum.ge.point) goto 6  !Don't cycle twice (VMS V4 - out)
c
c       Get rid of any user's we had that were skipped over
c
           imsk = 'ffffffbf'x.or.functmsk                !Function 7
           do i=o_seqnum+1,point,-1
            if (imsk.eq.'ffffffff'x) then
              if (i.lt.point.or.(i.eq.point.and.pid(point).ne.c_pid))
     1        then
                do j=1,suspids
                 if (pid(i).eq.susp_pid(j)) then
                   do k=j,suspids-1
                    susp_pid(k) = susp_pid(k+1)
                   end do
                   susp_pid(suspids) = 0
                   suspids = suspids - 1
                   goto 10
                 end if
                end do
              end if
            end if
10          continue
            if (i.lt.point) pid(i) = 0
           end do
           o_seqnum = point
c
c       Flag the process if it is suspended
c
           susp_flg(point) = .false.
           if (jpicode.eq.ss$_suspended) susp_flg(point) = .true.
c
c       If the current PID isn't the same as the one we have for this
c       sequence number, its a new user. Initialize the counters.
c       (or if a system user has changed into something else)
c
           if ((pid(point).ne.c_pid).or.(system(point).ge.nan$_system
     1     .and.(lench(c_account).gt.0.and.c_uic(2).gt.sysgrp.and.
     2     c_account(:len1(c_account)).ne.sysaccnam(:len1(sysaccnam)))))
     3     then
             username(point)   = c_username
             account(point)    = c_account
             terminal(point)   = c_terminal
             phy_term(point)   = c_phy_term
             authpriv(point,1) = c_authpriv(1)
             authpriv(point,2) = c_authpriv(2)
             bufio(point)      = 0
             cputim(point)     = 0
             dirio(point)      = 0
             owner(point)      = c_owner
             pageflts(point)   = 0
             ipid(point)       = c_ipid
             pid(point)        = c_pid
             authpri(point)    = c_prib
             warn(point)       = 0
             warned(point)     = 0
             limwarn(point)    = 0
c
c       Determine the type of process we have
c
             typ = nan$v_inter     !Default is interactive process
             if (lib$extzv(pcb$v_batch,1,c_sts).eq.1) typ = nan$v_batch
             if (lib$extzv(pcb$v_netwrk,1,c_sts).eq.1)
     1       typ = nan$v_network
             if (typ.ne.nan$v_network.and.typ.ne.nan$v_batch.and.
     1       lench(c_terminal).eq.0) typ = nan$v_detach
             if (owner(point).ne.0) typ = nan$v_subproc
             if (c_uic(2).le.sysgrp.or.c_account(:len1(c_account)).eq.
     1       sysaccnam(:len1(sysaccnam)).or.lench(c_account).eq.0.or.
     2       c_prib.gt.maxpri) typ = typ + nan$_system
             system(point) = typ
c
c       If we should ignore this user or terminal set typ to 4
c
             do cnt=1,maxig_user
              if (lench(ig_user(cnt)).le.0) goto 11
              if (ig_user(cnt)(:len1(ig_user(cnt))).eq.
     1        c_username(:len1(c_username))) system(point) =
     2        nan$v_unknown
             end do
11           continue
             if (system(point).ne.nan$v_unknown) then
               do cnt=1,maxig_term
                if (lench(ig_term(cnt)).le.0) goto 12
                if (ig_term(cnt)(:len1(ig_term(cnt))).eq.
     1          c_terminal(:len1(c_terminal))) system(point) =
     2          nan$v_unknown
               end do
             end if
12           continue
           end if
           sts(point) = 0      !Used for new cpu use stats later
c
c       Ignore him if SUSPended
c
           if (susp_flg(point).and.system(point).lt.nan$_system)
     1     system(point) = system(point) + nan$_system
           if (c_prib.gt.authpri(point)) authpri(point) = c_prib
c
c       Increment elapsed time logoff if the function is on
c
           if (maxelapsed.gt.0.and.lib$extzv(16,16,warn(point))
     1     .lt.maxelapsed) warn(point) = warn(point) + 1 * 2**16
c
c       Check Normal users for being idle
c
           if (maxidle.gt.0.and.(minio.gt.0.or.mintim.gt.0).and.
     1     lib$extzv(0,16,warn(point)).lt.maxidle) then
             warn(point)                = warn(point) + 1
             do i=1,cpu_average-1
              ncputim(point,i)          = ncputim(point,i+1)
             end do
             ncputim(point,cpu_average) = 0
             if (system(point).eq.nan$v_inter.or.system(point)
     1       .eq.nan$v_subproc.or.system(point).eq.nan$v_inter+
     2       nan$_system) then
               if (bufio(point)+dirio(point)+minio.le.c_bufio+
     1         c_dirio.and.minio.ne.0) warn(point) = lib$extzv(
     2         16,16,warn(point)) * 2**16
               if (cputim(point)+mintim.le.c_cputim.and.mintim
     1         .ne.0) warn(point) = (lib$extzv(16,16,warn(point))
     2         * 2**16)
               imsk = 'ffffefff'x.or.functmsk            !Function 13
               if (imsk.ne.'ffffffff'x.and.c_prccnt.gt.0)
     1         warn(point) = lib$extzv(16,16,warn(point)) * 2**16
             else
               warn(point) = lib$extzv(16,16,warn(point)) * 2**16
             end if
             if (lib$extzv(0,16,warn(point)).eq.0)
     1       warned(point) = 0
c
c       Figure out total new CPU use for this user in this CPU group
c
             if (system(point).lt.nan$v_unknown) then
               imsk='ffffffdf'x.or.functmsk              !Function 6
               if (imsk.eq.'ffffffff'x) then
                 do i=1,point-1
c
c       If this is a valid process, our PIDs are different, our
c       authorized priorities are the same, and our usernames are
c       the same, then...
c
                  if (pid(i).ne.0.and.pid(i).ne.pid(point).and.
     1            authpri(i).eq.authpri(point).and.username(i).eq.
     2            username(point)) then
                    j = point                 !J = (>ncpu user)
                    k = i                     !K = (<=ncpu user)
                    if (ncputim(i,cpu_average).gt.ncputim(point,
     1              cpu_average))        j  = i
                    if (j.eq.i)          k  = point
c
c       After getting all processes in this phase of Nanny, 'sts'
c       will contain extra cpu to add to 'ncputim' for hogs check
c
                    sts(j) = sts(k) + ncputim(k,cpu_average)
                    sts(k)                  = 0
                    ncputim(k,cpu_average)  = 0
                    usedpri(authpri(point)) = usedpri(authpri(
     1              point)) - 1
                  end if
                 end do
               end if
             end if
           end if
c
c       Check off potential batch priorities not to suspend
c
           imsk = 'ffffffbf'x.or.functmsk                !Function 7
           if (imsk.eq.'ffffffff'x) then
             if (system(point).eq.nan$v_batch) then
               if (highbat.ne.-1.and.authpri(point).lt.highbat)
     1         suspendok = .true.
               if (authpri(point).gt.highbat) highbat = authpri(point)
               if (authpri(point).lt.lowbat)  lowbat  = authpri(point)
             end if
           end if
c
c       Total things
c
           ncputim(point,cpu_average) = c_cputim - cputim(point) + j
           bufio(point)               = c_bufio
           cputim(point)              = c_cputim
           dirio(point)               = c_dirio
           pageflts(point)            = c_pageflts
           prib(point)                = c_prib
           wssize(point)              = c_wssize
           if (.not.susp_flg(point)) wstotal = wstotal + c_wssize
           if (lench(phy_term(point)).gt.0.and.system(point).lt.
     1     nan$v_unknown) usedpri(authpri(point)) = usedpri(
     2     authpri(point)) + 1
c
c       Warn the user if close to his CPU limit
c
           imsk = 'fffeffff'x.or.functmsk                !Function 17
           if (imsk.eq.'ffffffff'x.and.(system(point).eq.nan$v_inter.or.
     1     system(point).eq.nan$v_inter+nan$_system).and.c_cpulim.ne.0)
     2     then
             if (cputim(point).ge.c_cpulim-waitim*2.and.limwarn(point)
     1       .lt.2) then
               if (limwarn(point).eq.0.or.(cputim(point).ge.
     1         c_cpulim-waitim)) then
                 call time(cur_date)
                 message = cur_date(:8)//' Nanny: You are about to '//
     1           'exceed your CPU limit.'//char(7)
                 code = sys$brkthru(,%descr(message(:len1(message))),
     1           %descr(terminal(point)(:len1(terminal(point)))),
     2           %val(brk$c_device),,,,,%val(5),,)
                 call bug(code,'BRKTHRU')
                 limwarn(point) = limwarn(point) + 1
                 call output(1,'Warned '//username(point)(:len1(
     1           username(point)))//' about CPU time limit')
               end if
             end if
           end if
         end if
        end do
c
c       We got all the users, let's do something to them...
c
6       if (first) goto 2
        if (wstotal.gt.maxwstot) maxwstot = wstotal
        crush_p = 0
c
c       If memory is plentiful, turn off suspendok flag
c
        imsk = 'ffffffbf'x.or.functmsk                   !Function 7
        if (imsk.ne.'ffffffff'x.or.wstotal.le.maxphymem.or.maxphymem
     1  .eq.0)              ws_mem_purge = .false.
        if (.not.ws_mem_purge) suspendok = .false.
        do point=1,maxseqnum
         if (pid(point).ne.0.and.system(point).ne.nan$v_unknown) then
ThEgReAtZaR
$ checksum nm1.for
$ if chk.nes.checksum$checksum then write sys$output -
  "NM1.FOR didn't pass checksum. File may be corrupted."
$ if chk.nes.checksum$checksum then exit %x2c
$ exit