$! KILLER.COM - Idle job killer $! Author: Harry Flowers $! Date: December 1988 $! $! Yes, this would be more efficient in a high level language. But it $! will work on V4.X as well as V5.X systems without re-linking anything. $! It is also easier to tailor. It is intended to be run after hours. $! Because it gradually uses more and more symbols, it should not be left $! running continously, but started and stopped each day. $! $! Operating Parameters hh:mm:ss $ TIME_BETWEEN_CHECKS = "00:05:00.00" ! Time to WAIT between checks $ NUMBER_OF_CHECKS = 12 ! How many checks before kill $ WARN_USERS = "FALSE" ! Send out warning messages? $ FIRST_WARNING_CHECK = 9 ! Check # to start sending $ WARNING1 = "Process has been inactive 45 minutes." $ WARNING2 = "Process has been inactive 50 minutes." $ WARNING3 = "Process has been inactive 55 minutes, " +- "and will be terminated in 5 minutes." $ WARNING4 = "Process has been inactive 1 hour and is being terminated." $! $! For example, if TIME_BETWEEN_CHECKS = "00:05:00.00" and $! NUMBER_OF_CHECKS = 12, then this procedure will check every 5 minutes, $! and stop a process after 12 checks, or 1 hour of inactivity. $! $!--------------------------------------------------------------------------- $! $ SAVERIFY = F$VERIFY(0) $ ON CONTROL_Y THEN GOTO FINISHED $ PREVPRIV = F$SETPRV("WORLD,OPER,NOBYPASS") $ IF .NOT. F$PRIVILEGE("WORLD") THEN GOTO FINISHED $ DBG = "" ! DBG = "" if debugging, "!" if not $'DBG' SLASHFMT = "!6UL/!UL" $'DBG' FAOFMT = "!AS !13AS !13AS !13AS !8AS !1UL !2UL !AS" $! $ LOOP: $ ON ERROR THEN GOTO LOOP $ PID = F$PID(CONTEXT) ! Get the next PID $ IF PID .EQS. "" THEN GOTO WAIT4NEXT1 ! Been through them all $ IF F$TYPE(TERM'PID') .EQS. "" THEN GOTO NEW_PROCESS ! New process? $ IF TERM'PID' .EQS. "" THEN GOTO LOOP ! If no terminal, no check $ BUFIO = F$GETJPI(PID,"BUFIO") ! Buffered I/O's $ DIRIO = F$GETJPI(PID,"DIRIO") ! Direct I/O's $ CPUTIM = F$GETJPI(PID,"CPUTIM") ! CPU time $ IMAGE = F$GETJPI(PID,"IMAGNAME") ! Image name $ SUBPRC = F$GETJPI(PID,"PRCCNT") ! Number of sub-processes $ IMAGE = F$PARSE(IMAGE,"DCL.EXE",,"NAME","SYNTAX_ONLY") ! Image name $ INACTIVE'PID' = INACTIVE'PID' + 1 ! Assume inactive $! $! Modifiers (some programs are active on their own) $ BUFIOMOD = 0 $ DIRIOMOD = 0 $ CPUTIMMOD = 5 $ IF IMAGE .EQS. "OFFICE" THEN BUFIOMOD = 2 $ IF IMAGE .NES. "WPCORP_WP_SHR" THEN GOTO ACTIVE_CHECKS $ BUFIOMOD = 10 $ DIRIOMOD = 10 $ CPUTIMMOD = 10 $! $! Checks for activity $ ACTIVE_CHECKS: $ IF BUFIO .GT. (BUFIO'PID' + BUFIOMOD) THEN INACTIVE'PID' = 0 ! New I/O's $ IF DIRIO .GT. (DIRIO'PID' + DIRIOMOD) THEN INACTIVE'PID' = 0 ! New I/O's $ IF CPUTIM .GT. (CPUTIM'PID' + CPUTIMMOD) THEN INACTIVE'PID' = 0 ! NewCPU $ IF (IMAGE .EQS. "KERMIT") .OR. (IMAGE .EQS. "RTPAD") THEN - INACTIVE'PID' = 0 ! Don't count KERMIT or SET HOST $! $! If a subprocess is active, then mark the master process as active $ MASTER = MASTER'PID' ! Master is PID of top of process tree $ IF (INACTIVE'PID' .EQ. 0) .AND. (MASTER .NES. "") THEN - INACTIVE'MASTER' = 0 $! $! Send out warnings $ IF (.NOT. WARN_USERS) .OR. (INACTIVE'PID' .LT. FIRST_WARNING_CHECK) - .OR. (SUBPRC .GT. 0) THEN GOTO TIME_TO_DIE $ WHO_WARN = "/TERMINAL=" + TERM'PID' $!DBG WHO_WARN = "/USER=" + F$EDIT(F$GETJPI("","USERNAME"),"TRIM") $ WARNING_NUM = INACTIVE'PID' - FIRST_WARNING_CHECK + 1 $ WARNING_MESSAGE = "WARNING: Process ''PID' on " + TERM'PID' +- " is INACTIVE. (''IMAGE')" + F$FAO("!/") +- WARNING'WARNING_NUM' $ REPLY/URGENT/BELL/NONOTIFY'WHO_WARN' "''WARNING_MESSAGE'" $! $! Check to see if it's time to hold the funeral $ TIME_TO_DIE: $! Just for debugging purposes $ WRITE SYS$OUTPUT F$FAO(FAOFMT,PID,- F$FAO(SLASHFMT,BUFIO,BUFIO'PID'),- F$FAO(SLASHFMT,DIRIO,DIRIO'PID'),- F$FAO(SLASHFMT,CPUTIM,CPUTIM'PID'),- TERM'PID',SUBPRC,INACTIVE'PID',IMAGE) $! $ IF (INACTIVE'PID' .LT. NUMBER_OF_CHECKS) .OR. (SUBPRC .GT. 0) THEN - GOTO SURVIVORS $ WRITE SYS$OUTPUT "(^^^ I am killing this one! ^^^)" $ WARNING_MESSAGE = "Stopping inactive process ''PID' on " + TERM'PID' +- "(''IMAGE') for user " + F$EDIT(F$GETJPI(PID,"USERNAME"),"TRIM") $ REQUEST/TO=(CENTRAL,SECURITY) "''WARNING_MESSAGE'" $ IF WARN_USERS THEN WAIT 00:00:01.00 ! Give time to see messages $ STOP/ID='PID' ! Terminate the process $ SET MESSAGE/NOFACILITY/NOSEVERITY/NOIDENTIFICATION/NOTEXT $ DELETE/SYMBOL TERM'PID' ! Clean up a bit $ DELETE/SYMBOL MASTER'PID' $ DELETE/SYMBOL BUFIO'PID' $ DELETE/SYMBOL DIRIO'PID' $ DELETE/SYMBOL CPUTIM'PID' $ DELETE/SYMBOL INACTIVE'PID' $ SET MESSAGE/FACILITY/SEVERITY/IDENTIFICATION/TEXT $ GOTO LOOP $! $! Well, they've survived, so save their new I/O and CPU, and get another $ SURVIVORS: $ BUFIO'PID' = BUFIO $ DIRIO'PID' = DIRIO $ CPUTIM'PID' = CPUTIM $ GOTO LOOP $! $ NEW_PROCESS: ! For processes I've never seen $ TERM'PID' = F$GETJPI(PID,"TERMINAL") ! Get terminal $ MASTER'PID' = "" ! Null if I am top process $ IF TERM'PID' .NES. "" THEN GOTO SET_NEW_PROCESS $ MASTER'PID' = F$GETJPI(PID,"MASTER_PID") ! Master Process $ IF MASTER'PID' .EQS. "" THEN GOTO LOOP ! We were top $ TERM'PID' = F$GETJPI(MASTER'PID',"TERMINAL") ! Master's terminal $ IF TERM'PID' .EQS. "" THEN GOTO LOOP ! If no terminal, no check $ SET_NEW_PROCESS: $ TERM'PID' = TERM'PID' - ":" + ":" ! Make sure all have the colon $ BUFIO'PID' = F$GETJPI(PID,"BUFIO") ! Buffered I/O's $ DIRIO'PID' = F$GETJPI(PID,"DIRIO") ! Direct I/O's $ CPUTIM'PID' = F$GETJPI(PID,"CPUTIM") ! CPU time $ INACTIVE'PID' = 0 $'DBG' WRITE SYS$OUTPUT "New process ''PID' added." $ GOTO LOOP $! $ WAIT4NEXT1: $'DBG' WRITE SYS$OUTPUT "" $ WAIT 'TIME_BETWEEN_CHECKS' ! Wait however long $ CONTEXT = "" ! Want to start F$PID fresh $ HOUR = F$INTEGER(F$CVTIME(,,"HOUR")) ! Only run after hours $ IF (HOUR .LT. 17) .AND. (HOUR .GE. 8) THEN GOTO FINISHED $ GOTO LOOP $! $ FINISHED: $ PREVPRIV = F$SETPRV(PREVPRIV) $ SAVERIFY = F$VERIFY(SAVERIFY) $ EXIT