PROGRAM WATCH_DOG IMPLICIT NONE C C Program: WATCHDOG C C Original: unknown (program came from Decus Tape) C C Purpose: This Program Monitors Interactive Processes and Logs Off C Processes Set There Idle. C C Compilation: C FORTRAN /CONTINUATION=99/LIST/CROSS WATCHDOG C MACRO WATCHSYM C LINK WATCHDOG,WATCHSYM/SELECT/NOTRACEBACK C C Other Modules: C WATCHSYM.MAR - System Symbols for Watch Dog C C Modification: C Restructured Program adding comments and Fixing a Bug C if you login into another System using Decnet C you idle on Host but still working on other system. C 04/06/85 George H. Walrod III C C Notes: C C Variables of Some Importants : C Maxuser : Maximum Number of Users Program can Support C Send_Stamp : What Interval Time Stamp Should be Send C Start_Message : What Interval Cycle Should Warning Message C Starts. C Stop_Message : What Interval Cycle Should Process be Deleted C ASCTIM : Single Interval Cycle C C Debugging Tools Build-in : C D-Lines are used for Debug, note when you compile Watchdog C with D_LINES only Processes with Group Number matching the C System Group(variable name SYSGRP) will be monitored. C PARAMETER 1 MAXUSER = 256, ! Maximum Number Users 2 SEND_STAMP = 6, ! Time Stamp(send_stamp*asctim) 4 START_MESSAGE = 1, ! Start Sending Warning Msg 5 STOP_MESSAGE = 2, ! Stop Process Msg 2 JPI_WILDCARD = -1, ! Get Job Process Info Wildcard 4 EFN = 1, ! Event Flag 6 CPU_50MS = 5, ! 50 MS Resolution 2 SYSGRP = 7, ! System Group Number 5 PROCESS_NAME = 'gATCHDOG', ! Process Name 8 BELL = char(7), ! Ascii Bell 1 FAO_IN_STR = 2 '!AS !AS on !AS has been inactive for !SL min.!AS', 9 HEADER_MSG = ! Header Message 1 BELL//'MESSAGE FROM WATCH_DOG'//BELL, 2 STAMP_MSG = ! Time Stamp Message 3 'WATCH DOG TIME STAMP', 4 LOGOFF_DEF = ! DEFAULT LOGOFF 5 ' and is being logged off', 4 INIT_MSG = ! Initial Message 5 'WATCH DOG HAS BEEN INITIALIZED AND IS RUNNING' BYTE 1 MS_TYPE, ! Operator Request Type 2 OPER_MSG_BUF(8), ! Operator Message Buffer 3 STOPABLE(MAXUSER) ! Process Stopable CHARACTER 1 ACCNAM*8, ! Account Name 3 TIMBF*8, ! Current Time Buffer 2 TTYNUM*7, ! Terminal Name 3 USRNAM*12, ! User Name 4 LOGOFF_MSG*24, ! Logoff Message 5 MESSAGE*85, ! Message Buffer 3 ASCTIM*13 /'0 00:01:00.00'/, ! Time Delay 6 OPER_MESS*80 ! Operator Message INTEGER*2 1 JPIBUF(60), ! Job Process Info Buffer 2 SEQ_NUMBER(2), ! Sequence Number PID 3 SEQ(MAXUSER) ! Sequence Part Of Pid INTEGER*4 3 MS_TARGET, ! Operator Terminal Type 3 TIME_STAMP, ! Time Stamp Counter 1 SEEDPID, ! Seed Pid for GETJPI 6 CUR_PID, ! Current Pid 6 BINTIM(2), ! Binary Time 7 RANGE(2) /0,'7FFFFFFF'X/,! Working Set Purge 9 CPUTIM(MAXUSER), ! Cpu Time Used 1 BUFIOC(MAXUSER), ! Number Of Buffer I/O 5 WARNING(MAXUSER), ! Number Of Warning So Far 1 SS_STATUS, ! Sys Service Status 2 LIB$MATCHC, ! RTL Index for Colon 2 OTS$CVT_TI_L, ! RTL Convert Decimal->Binary 2 SYS$SETPRN, ! SS Set Process Name 2 SYS$FAO, ! SS Format Ascii Output 3 SYS$SCHDWK, ! SS Schedule Wake 8 SYS$PURGWS, ! SS Purge Working set 9 SYS$BINTIM, ! SS Binary Time 8 SYS$DELPRC, ! SS Delete Process 9 SYS$BRDCST, ! SS Broadcast 1 SYS$SNDOPR, ! SS Send Operator 4 SYS$GETJPI, ! SS Get Job Process Info 5 SYS$WAITFR, ! SS Wait For Efn 6 SYS$HIBER, ! SS Hiberate 7 START_IDX, ! Index Pointer of TIME 7 TIME_MINUTES, ! Time in Minutes 8 MESSAGE_LEN, ! Message Length 1 PID, ! Process Identifacation No. 2 OWNER, ! Process Owner Pid 2 NEWCPU, ! New Cpu Time 3 NEWIOC, ! New Buffer I/O Count 6 STATE, ! Process State 4 GRPNUM, ! Group Number 5 SUBCOUNT, ! Sub-Process Count 6 PIDADDR, ! Addr of Pid 7 PIDLEN, ! Length of Pid 8 CPUTADDR, ! Addr of Cpu Time 9 CPULEN, ! Length of Cpu Time 1 BIOADDR, ! Addr Buffer I/O 2 BIOLEN, ! Length Buffer I/O 3 STATEADDR, ! Addr of Process State 4 STATELEN, ! Length of Process State 4 USRNAM_L, ! User Name Length 5 USERADDR, ! Addr of User Name 6 USERLEN, ! Length of User Name 1 ACCNAM_L, ! Account Name Length 7 ACCADDR, ! Addr of Account Name 8 ACCLEN, ! Length of Account Name 3 TTYNUM_L, ! Terminal Name Length 9 TERMADDR, ! Addr of Terminal Name 1 TERMLEN, ! Length of Terminal Name 2 GRPADDR, ! Addr of Group Number 3 GRPLEN, ! Length Group Number 4 PRCCADDR, ! Addr of Process Count 5 PRCCLEN, ! Length of Process Count 4 OWNERADDR, ! Addr of Owner Pid 5 OWNERLEN ! Length of Owner Pid EXTERNAL 1 SS$_DEVOFFLINE, ! Device Offline Line 1 SS$_NOMOREPROC, ! No More Processes 2 SS$_NORMAL, ! Normal Completion 3 SS$_SUSPENDED, ! Process Suspended 5 JPI$_PID, ! Pid 6 JPI$_CPUTIM, ! Cpu Time 7 JPI$_BUFIO, ! Buffer I/O 8 JPI$_STATE, ! Process State 9 JPI$_USERNAME, ! User Name 1 JPI$_ACCOUNT, ! Account Name 2 JPI$_TERMINAL, ! Terminal Name 3 JPI$_GRP, ! Group Number 4 JPI$_PRCCNT, ! Process Count 5 JPI$_OWNER, ! Process Owner 5 JPI$C_LISTEND, ! End List 6 SCH$C_HIB, ! Schedule Hiberation 6 SCH$C_HIBO, ! Schedule Hiberation Out Swap 6 OPC$M_NM_CENTRL, ! Operator Central 7 OPC$_RQ_RQST ! Operator Request C C Define Equivalence Fields for Data Buffers C EQUIVALENCE 1 (PIDADDR, JPIBUF(03)), 2 (PIDLEN, JPIBUF(05)), 3 (CPUTADDR, JPIBUF(09)), 4 (CPULEN, JPIBUF(11)), 5 (BIOADDR, JPIBUF(15)), 6 (BIOLEN, JPIBUF(17)), 7 (STATEADDR, JPIBUF(21)), 8 (STATELEN, JPIBUF(23)), 9 (USERADDR, JPIBUF(27)), 1 (USERLEN, JPIBUF(29)), 2 (ACCADDR, JPIBUF(33)), 3 (ACCLEN, JPIBUF(35)), 4 (TERMADDR, JPIBUF(39)), 5 (TERMLEN, JPIBUF(41)), 6 (GRPADDR, JPIBUF(45)), 7 (GRPLEN, JPIBUF(47)), 8 (PRCCADDR, JPIBUF(51)), 9 (PRCCLEN, JPIBUF(53)), 1 (OWNERADDR, JPIBUF(57)), 2 (OWNERLEN, JPIBUF(59)), 1 (MS_TYPE, OPER_MSG_BUF(1)), 2 (MS_TARGET, OPER_MSG_BUF(2)), 3 (OPER_MSG_BUF, OPER_MESS), 4 (MESSAGE, OPER_MESS(11:)), 5 (SEQ_NUMBER(1), PID) C C Job Process Information Layout C ! +---------------+ JPIBUF(01) = 4 ! | Pid Buf Size | JPIBUF(02) = %loc(jpi$_pid) ! | PID | PIDADDR = %loc(pid) ! | Pid Addr | PIDLEN = 0 ! | Pid Ret Len | JPIBUF(7) = 4 ! | Cpu Time Len | JPIBUF(8) = %loc(jpi$_cputim) ! | CPUTIM | CPUTADDR = %loc(newcpu) ! | Cpu Time Addr | CPULEN = 0 ! | Cpu Ret Len | JPIBUF(13) = 4 ! | Buf I/O Size | JPIBUF(14) = %loc(jpi$_bufio) ! | Buffer I/O | BIOADDR = %loc(newioc) ! | Buf I/O Addr | BIOLEN = 0 ! | BufIO Ret Len | JPIBUF(19) = 4 ! |Proc State Size| JPIBUF(20) = %loc(jpi$_state) ! | Process State ! STATEADDR = %loc(state) ! |Proc State Addr| STATELEN = 0 ! | State Ret Len | JPIBUF(25) = 12 ! | User Name Len | JPIBUF(26) = %loc(jpi$_username) ! | User Name | USERADDR = %loc(usrnam) ! | User Name Addr| USERLEN = %loc(usrnam_l) ! | User Ret Len | JPIBUF(31) = 8 ! | Account Sizen | JPIBUF(32) = %loc(jpi$_account) ! | Account | ACCADDR = %loc(accnam) ! | Account Addr | ACCLEN = %loc(accnam_l) ! | Account Retlen| JPIBUF(37) = 7 ! | Terminal Size | JPIBUF(38) = %loc(jpi$_terminal) ! | Terminal | TERMADDR = %loc(ttynum) ! | Terminal Addr | TERMLEN = %loc(ttynum_l) ! | Terminal Len | JPIBUF(43) = 4 ! | Grpno Size | JPIBUF(44) = %loc(jpi$_grp) ! | Group Number | GRPADDR = %loc(grpnum) ! | Grpno Addr | GRPLEN = 0 ! | Grpno Ret Len | JPIBUF(49) = 4 ! | Process Size | JPIBUF(50) = %loc(jpi$_prccnt) ! | Process Count | PRCCADDR = %loc(subcount) ! | Proc Addr | PRCCLEN = 0 ! | Proc Ret Len | JPIBUF(55) = 4 ! | Owner Size | JPIBUF(56) = %loc(jpi$_owner) ! | Owner Pid | OWNERADDR = %loc(owner) ! | Owner Addr | OWNERLEN = 0 ! | Owner Ret Len | JPIBUF(61) = %loc(jpi$c_listend) ! | END OF LIST | JPIBUF(62) = %loc(jpi$c_listend) ! | END OF LIST | C +---------------+ C End of Job Process Information List C C C Set-Up Data Buffer for Operator Message C MS_TYPE = %loc(OPC$_RQ_RQST) MS_TARGET = %loc(OPC$M_NM_CENTRL) OPER_MESS(9:10) = BELL // BELL C C Convert Ascii Time to Binary Time C SS_STATUS = SYS$BINTIM(ASCTIM,BINTIM) IF (.not. SS_STATUS) CALL LIB$STOP(%VAL(SS_STATUS)) C C Extract Minutes From Asctim for Warning Messages C START_IDX = LIB$MATCHC(':',ASCTIM) + 1 D TYPE *,'ASCII Time Intervals is ',ASCTIM D TYPE *,'Number of Minutes ASCII ',ASCTIM(START_IDX:START_IDX+1) SS_STATUS = OTS$CVT_TI_L(ASCTIM(START_IDX:START_IDX+1), 1 TIME_MINUTES) D TYPE *,'Type Return Status from OTS$CVT_TI_L is ',SS_STATUS IF (.not. SS_STATUS) CALL LIB$STOP(%VAL(SS_STATUS)) C C Set Process Name to "WATCH_DOG" C SS_STATUS = SYS$SETPRN(PROCESS_NAME) IF (.not. SS_STATUS) CALL LIB$STOP(%VAL(SS_STATUS)) C C Send Operator Console Message Tell Them That I am Running C MESSAGE = INIT_MSG SS_STATUS = SYS$SNDOPR(OPER_MESS,) D TYPE *,'Type Return Status from SYS$SNDOPR is ',SS_STATUS IF (.not. SS_STATUS) CALL LIB$STOP(%VAL(SS_STATUS)) C C Initialize Time Stamp Time So That I Send Time Stamp Message C Too When I Start Running C TIME_STAMP = SEND_STAMP - 1 C C Main Section of Program, Get Data on Users C 1 CONTINUE TIME_STAMP = TIME_STAMP + 1 C C Send Time Stamp Message If It is Time C IF (TIME_STAMP .eq. SEND_STAMP ) then MESSAGE = STAMP_MSG SS_STATUS = SYS$SNDOPR(OPER_MESS,) D TYPE *,'Type Return Status from SYS$SNDOPR is ',SS_STATUS IF (.not. SS_STATUS) CALL LIB$STOP(%VAL(SS_STATUS)) TIME_STAMP=0 ENDIF C C Loop Until We Have Looked At All Processes using C Get Job Process Information with a Wild Card C SEEDPID = JPI_WILDCARD SS_STATUS = 0 DO WHILE (SS_STATUS .ne. %loc(SS$_NOMOREPROC)) SS_STATUS = SYS$GETJPI(%val(EFN),%ref(SEEDPID),,%ref(JPIBUF),,,) D TYPE *,'Type Return Status from SYS$GETJPI is ',SS_STATUS D900 FORMAT(' Working With PID ',Z8) D TYPE 900,PID IF (SS_STATUS) then SS_STATUS = SYS$WAITFR(%val(EFN)) ELSE IF ((SS_STATUS .ne. %loc(SS$_NOMOREPROC)) .and. 1 SS_STATUS .ne. %loc(SS$_SUSPENDED)) then CALL LIB$STOP(%VAL(SS_STATUS)) ELSE GOTO 20 endif endif C C We Have A User, Get The Low 16 Bits Of His Pid (the index) C the High Order Bits Are the Sequence number C CUR_PID = SEQ_NUMBER(1) C C If The Sequence Number Has Changed Since We Last Logged In, C We Have A New Sucker. Reset All The Use Counts, And Dont Bother Him C IF (SEQ(CUR_PID) .ne. SEQ_NUMBER(2)) then D TYPE *,'Never Seen This Person Before' SEQ(CUR_PID) = SEQ_NUMBER(2) WARNING(CUR_PID) = 0 STOPABLE(CUR_PID) = .TRUE. GOTO 10 endif C C Is The Process Subject to Watchdog Monitoring C (am I wasteing my time) C IF (.not. STOPABLE(CUR_PID)) GOTO 10 C C Leave The Processes Alone, Which are Hiberating C IF ((STATE .eq. %loc(SCH$C_HIB)) .or. 1 (STATE .eq. %loc(SCH$C_HIBO))) GOTO 10 C C Leave The System Processes Alone, Things Break If I Don't C C IF (ACCNAM(1:ACCNAM_L) .eq. 'SYSTEM') then C STOPABLE(CUR_PID) = .false. C GOTO 20 C end if C C I leave The Systems Staff Alone, Otherwise I Would Be Considered C Anti-Social. C C IF (GRPNUM .le. SYSGRP) then C STOPABLE(CUR_PID) = .false. C GOTO 20 C end if C C Debuging to only look at SYSGRP Uic's C D IF (GRPNUM .ne. SYSGRP) then D STOPABLE(CUR_PID) = .false. D GOTO 20 D end if C C If He Has No Tty Associated, He Is A Batch Job, Or An Acp C Or Something Like That. If He Is A Batch Job During Primetime C It Is Likely That He Is Not Running Enough To Notice C IF (TTYNUM_L .eq. 0) GOTO 20 C C Normal User, He Can Be Saved By Doing An I/O, Or Using 50ms Of Cpu C If He Has Some Subprocess' Running Then He May Be Waiting C So Don't Blow Him Away C IF ((BUFIOC(CUR_PID) .lt. NEWIOC) .or. 1 (CPUTIM(CUR_PID)+CPU_50MS .le. NEWCPU) .or. 2 (SUBCOUNT .GT. 0)) then WARNING(CUR_PID) = 0 GOTO 10 endif C C We Have Them Now, Check To See if We Can Start Sending Messages Yet C to User and/or Central Operator C D TYPE *,'Number of Warning ',WARNING(CUR_PID) WARNING(CUR_PID) = WARNING(CUR_PID) + 1 IF (WARNING(CUR_PID) .ge. START_MESSAGE) then MESSAGE = ' ' D TYPE *,'Warn User ',USRNAM(1:USRNAM_L) C C Determine If We Are Going to Make Them History for the Message C Get Time of Day and Assemble The Message C LOGOFF_MSG = LOGOFF_DEF IF (WARNING(CUR_PID) .ne. STOP_MESSAGE) LOGOFF_MSG = ' ' CALL TIME(TIMBF) Usrnam_l = LIB$MATCHC(' ',usrnam) - 1 SS_STATUS = SYS$FAO(FAO_IN_STR, MESSAGE_LEN, MESSAGE, 1 TIMBF, 1 USRNAM(1:usrnam_l), 2 TTYNUM(1:ttynum_l), 2 %val(TIME_MINUTES*WARNING(CUR_PID)), 3 LOGOFF_MSG) IF (.not. SS_STATUS) CALL LIB$STOP(%VAL(SS_STATUS)) D TYPE *,'STATUS RETURN FROM FAO IS ',SS_STATUS D TYPE *,'IDLE TIME ',TIME_MINUTES*WARNING(CUR_PID) C SS_STATUS = SYS$SNDOPR(OPER_MESS,) C IF (.not. SS_STATUS) CALL LIB$STOP(%VAL(SS_STATUS)) SS_STATUS = SYS$BRDCST(HEADER_MSG,TTYNUM) IF ((.not. SS_STATUS) .and. 1 (SS_STATUS .ne. %loc(SS$_DEVOFFLINE))) 2 CALL LIB$STOP(%VAL(SS_STATUS)) SS_STATUS = SYS$BRDCST(OPER_MESS(9:),TTYNUM) IF ((.not. SS_STATUS) .and. 1 (SS_STATUS .ne. %loc(SS$_DEVOFFLINE))) 2 CALL LIB$STOP(%VAL(SS_STATUS)) endif C C Can We Delete Process, Check C IF (WARNING(CUR_PID) .eq. STOP_MESSAGE) then D TYPE *,'Deleting User ',USRNAM(1:USRNAM_L) SS_STATUS = SYS$SNDOPR(OPER_MESS,) IF (.not. SS_STATUS) CALL LIB$STOP(%VAL(SS_STATUS)) SS_STATUS=sys$delprc(%ref(pid),) IF (.not. SS_STATUS) CALL LIB$STOP(%VAL(SS_STATUS)) endif C C Go Get Another Process, Also Assign the CPUTIM and BUFIOC C 10 CONTINUE CPUTIM(CUR_PID)=NEWCPU BUFIOC(CUR_PID)=NEWIOC 20 CONTINUE END DO C C Set Up a Scheduled Wake Up to Occur C SS_STATUS = SYS$SCHDWK(,,BINTIM,) IF (.not. SS_STATUS) CALL LIB$STOP(%VAL(SS_STATUS)) C C Dont Waste Memory While Waiting C SS_STATUS = SYS$PURGWS(%ref(RANGE)) IF (.not. SS_STATUS) CALL LIB$STOP(%VAL(SS_STATUS)) C C Hibernate Till the Scheduled Wake-up C D TYPE *,'Going To Hiberate' SS_STATUS = SYS$HIBER() D TYPE *,'WATCH DOG Awake And Feeling Refreshed' GOTO 1 end ! Call It a Day