PROGRAM WATCH_DOG IMPLICIT NONE C C Program: WATCHDOG C C Original: unknown (program came from Decus Tape) C C Modifier's Address: C George H. Walrod III Phone: (301) 251-8485 C MS-34 C 1801 Research Boulevard C Rockville, MD 20850 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/SELECTIVE/NOTRACEBACK/MAP C C Other Modules: C WATCHSYM.MAR - System Symbols for Watch Dog C C Corrected 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 Correct Bug if User Spawns a Subprocess the Subprocess C Does not Have a Terminal when you do a GETJPI, so we C Search for the Parent Process to Get the Terminal. If C Parent has No Terminal They are probley a Detach Process C and We Do Not Tough thoses Process. C 04/22/85 George H. Walrod III C C Security/Bug, If user Spawn a Subprocess and then C Suspends his Parent Process, Then we can't find his C Terminal. The User seems to be trying to avoid Watchdog C So we warning him but send no Messages, and eventually C log him off, Knowing That it will probley hang him in C Suspended Process. But we try to resume the Parent C Process first knowing that this will probably no work. C 04/22/85 George H. Walrod III C C Watch Dog get hits by Car! When Watchdog tells the user C that they are being logged off and the User logs out on C their own, When Watch goes to bite(Delete) the user C process for the final Kill, and user is gone. Watchdog C dies when a car(Error) come from no where. The license C plate of the car is "SS$_NONEXPR" (nonexistent process). C 04/26/85 George H. Walrod III C C Dec Goes Against Own Standard, Version 4.X changes the C Extented Pid to Normal Pid. Added Routine to Change C Pid (GETPID). THIS CALL MUST BE COMMENTED FOR VMS C VERSION LESS THAN 4.X. C 12/06/85 George H. Walrod III C Alan Cutler 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 + MAXUSER = 256, ! Maximum Number Users + SEND_STAMP = 12, ! Time Stamp(send_stamp*asctim) + START_MESSAGE = 2, ! Start Sending Warning Msg + STOP_MESSAGE = 3, ! Stop Process Msg + JPI_WILDCARD = -1, ! Get Job Process Info Wildcard + EFN = 1, ! Event Flag + CPU_50MS = 5, ! 50 MS Resolution + SYSGRP = 7, ! System Group Number + UNKNOWN_TERM = 'UNKN', ! Unknown Terminal Name + PROCESS_NAME = 'WATCHDOG', ! Process Name + BELL = char(7), ! Ascii Bell + FAO_IN_STR = + '!AS !AS on !AS has been inactive for !SL min.!/!AS', + HEADER_MSG = ! Header Message + BELL//'MESSAGE FROM WATCH_DOG'//BELL, + STAMP_MSG = ! Time Stamp Message + 'WATCH DOG TIME STAMP', + LOGOFF_DEF = ! DEFAULT LOGOFF + ' and is being Logged Off', + INIT_MSG = ! Initial Message + 'WATCH DOG HAS BEEN INITIALIZED AND IS RUNNING' LOGICAL*1 + MS_TYPE, ! Operator Request Type + OPER_MSG_BUF(8), ! Operator Message Buffer + STOPABLE(MAXUSER), ! Process Stopable + NO_WARNING ! Send No Warning Message CHARACTER + ACCNAM*8, ! Account Name + TIMBF*8, ! Current Time Buffer + TTYNUM*7, ! Terminal Name + USRNAM*12, ! User Name + LOGOFF_MSG*24, ! Logoff Message + MESSAGE*85, ! Message Buffer + ASCTIM*13 /'0 00:05:00.00'/, ! Time Delay + OPER_MESS*95 ! Operator Message INTEGER*2 + JPIBUF2(14), ! Job Process Info Buffer + JPIBUF(62), ! Job Process Info Buffer + SEQ_NUMBER(2), ! Sequence Number PID + SEQ(MAXUSER) ! Sequence Part Of Pid INTEGER*4 + MS_TARGET, ! Operator Terminal Type + TIME_STAMP, ! Time Stamp Counter + SEEDPID, ! Seed Pid for GETJPI + CUR_PID, ! Current Pid + BINTIM(2), ! Binary Time + RANGE(2) /0,'7FFFFFFF'X/,! Working Set Purge + CPUTIM(MAXUSER), ! Cpu Time Used + BUFIOC(MAXUSER), ! Number Of Buffer I/O + WARNING(MAXUSER), ! Number Of Warning So Far + SS_STATUS, ! Sys Service Status + LIB$MATCHC, ! RTL Index for Colon + OTS$CVT_TI_L, ! RTL Convert Decimal->Binary + SYS$SETPRN, ! SS Set Process Name + SYS$FAO, ! SS Format Ascii Output + SYS$SCHDWK, ! SS Schedule Wake + SYS$PURGWS, ! SS Purge Working set + SYS$BINTIM, ! SS Binary Time + SYS$DELPRC, ! SS Delete Process + SYS$BRDCST, ! SS Broadcast + SYS$SNDOPR, ! SS Send Operator + SYS$GETJPI, ! SS Get Job Process Info + SYS$WAITFR, ! SS Wait For Efn + SYS$RESUME, ! SS Resume + SYS$HIBER, ! SS Hiberate + START_IDX, ! Index Pointer of TIME + TIME_MINUTES, ! Time in Minutes + MESSAGE_LEN, ! Message Length + PID, ! Process Identifacation No. + OWNER, ! Process Owner Pid + NEWCPU, ! New Cpu Time + NEWIOC, ! New Buffer I/O Count + STATE, ! Process State + GRPNUM, ! Group Number + SUBCOUNT, ! Sub-Process Count + PIDADDR, ! Addr of Pid + PIDLEN, ! Length of Pid + CPUTADDR, ! Addr of Cpu Time + CPULEN, ! Length of Cpu Time + BIOADDR, ! Addr Buffer I/O + BIOLEN, ! Length Buffer I/O + STATEADDR, ! Addr of Process State + STATELEN, ! Length of Process State + USRNAM_L, ! User Name Length + USERADDR, ! Addr of User Name + USERLEN, ! Length of User Name + ACCNAM_L, ! Account Name Length + ACCADDR, ! Addr of Account Name + ACCLEN, ! Length of Account Name + TTYNUM_L, ! Terminal Name Length + TERMADDR, ! Addr of Terminal Name + TERMLEN, ! Length of Terminal Name + GRPADDR, ! Addr of Group Number + GRPLEN, ! Length Group Number + PRCCADDR, ! Addr of Process Count + PRCCLEN, ! Length of Process Count + OWNERADDR, ! Addr of Owner Pid + OWNERLEN, ! Length of Owner Pid + TERMADDR2, ! Addr of Terminal Name + TERMLEN2, ! Length of Terminal Name + OWNERADDR2, ! Addr of Owner Pid + OWNERLEN2 ! Length of Owner Pid EXTERNAL + SS$_DEVOFFLINE, ! Device Offline Line + SS$_NOMOREPROC, ! No More Processes + SS$_NONEXPR, ! Non Existent Process + SS$_NORMAL, ! Normal Completion + SS$_SUSPENDED, ! Process Suspended + JPI$_PID, ! Pid + JPI$_CPUTIM, ! Cpu Time + JPI$_BUFIO, ! Buffer I/O + JPI$_STATE, ! Process State + JPI$_USERNAME, ! User Name + JPI$_ACCOUNT, ! Account Name + JPI$_TERMINAL, ! Terminal Name + JPI$_GRP, ! Group Number + JPI$_PRCCNT, ! Process Count + JPI$_OWNER, ! Process Owner + JPI$C_LISTEND, ! End List + SCH$C_HIB, ! Schedule Hiberation + SCH$C_HIBO, ! Schedule Hiberation Out Swap + OPC$M_NM_CENTRL, ! Operator Central + OPC$_RQ_RQST ! Operator Request C C Define Equivalence Fields for Data Buffers C EQUIVALENCE + (PIDADDR, JPIBUF(03)), + (PIDLEN, JPIBUF(05)), + (CPUTADDR, JPIBUF(09)), + (CPULEN, JPIBUF(11)), + (BIOADDR, JPIBUF(15)), + (BIOLEN, JPIBUF(17)), + (STATEADDR, JPIBUF(21)), + (STATELEN, JPIBUF(23)), + (USERADDR, JPIBUF(27)), + (USERLEN, JPIBUF(29)), + (ACCADDR, JPIBUF(33)), + (ACCLEN, JPIBUF(35)), + (TERMADDR, JPIBUF(39)), + (TERMLEN, JPIBUF(41)), + (GRPADDR, JPIBUF(45)), + (GRPLEN, JPIBUF(47)), + (PRCCADDR, JPIBUF(51)), + (PRCCLEN, JPIBUF(53)), + (OWNERADDR, JPIBUF(57)), + (OWNERLEN, JPIBUF(59)), + (TERMADDR2, JPIBUF2(03)), + (TERMLEN2, JPIBUF2(05)), + (OWNERADDR2, JPIBUF2(09)), + (OWNERLEN2, JPIBUF2(11)), + (MS_TYPE, OPER_MSG_BUF(1)), + (MS_TARGET, OPER_MSG_BUF(2)), + (OPER_MSG_BUF, OPER_MESS), + (MESSAGE, OPER_MESS(11:)), + (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 Job Process Information Layout Subprocess C ! +---------------+ JPIBUF2(01) = 7 ! | Terminal Size | JPIBUF2(02) = %loc(jpi$_terminal) ! | Terminal | TERMADDR2 = %loc(ttynum) ! | Terminal Addr | TERMLEN2 = %loc(ttynum_l) ! | Terminal Len | JPIBUF2(07) = 4 ! | Owner Size | JPIBUF2(08) = %loc(jpi$_owner) ! | Owner Pid | OWNERADDR2 = %loc(owner) ! | Owner Addr | OWNERLEN2 = 0 ! | Owner Ret Len | JPIBUF2(13) = %loc(jpi$c_listend) ! | END OF LIST | JPIBUF2(14) = %loc(jpi$c_listend) ! | END OF LIST | C +---------------+ C End of Job Process Information List for Subprocess 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), + 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 D900 FORMAT(' Working With PID ',Z8) 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 D TYPE 900,PID IF (SS_STATUS) then SS_STATUS = SYS$WAITFR(%val(EFN)) ELSE IF ((SS_STATUS .ne. %loc(SS$_NOMOREPROC)) .and. + 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 CALL GETPID (PID) ! Change for Version 4.X 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. + (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. But C If He a Subprocess Find His Parent terminal and send a message C to him. C D901 FORMAT(' Working With Owner PID ',Z8) NO_WARNING = .FALSE. IF (TTYNUM_L .eq. 0) then DO WHILE (OWNER .ne. 0) SS_STATUS = SYS$GETJPI(%val(EFN),%ref(Owner),, + %ref(JPIBUF2),,,) D TYPE *,'Type Return Status from SYS$GETJPI for Subprocess is ', D + SS_STATUS D TYPE 901,Owner IF (SS_STATUS) then SS_STATUS = SYS$WAITFR(%val(EFN)) ELSE IF (SS_STATUS .ne. %loc(SS$_SUSPENDED)) then CALL LIB$STOP(%val(SS_STATUS)) ELSE NO_WARNING = .TRUE. endif endif end do IF ((TTYNUM_L .eq. 0) .and. + (.not. NO_WARNING)) GOTO 20 else IF (SUBCOUNT .GT. 0) then WARNING(CUR_PID) = 0 GOTO 10 endif end if 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. + (CPUTIM(CUR_PID)+CPU_50MS .le. NEWCPU)) 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 If (No_Warning) then TTYNUM = UNKNOWN_TERM TTYNUM_L = len(UNKNOWN_TERM) end if SS_STATUS = SYS$FAO(FAO_IN_STR, MESSAGE_LEN, MESSAGE, + TIMBF, + USRNAM(1:usrnam_l), + TTYNUM(1:ttynum_l), + %val(TIME_MINUTES*WARNING(CUR_PID)), + 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(1:Message_len+10),) C IF (.not. SS_STATUS) CALL LIB$STOP(%val(SS_STATUS)) IF (.not. NO_WARNING) then SS_STATUS = SYS$BRDCST(HEADER_MSG,TTYNUM) IF ((.not. SS_STATUS) .and. + (SS_STATUS .ne. %loc(SS$_DEVOFFLINE))) + CALL LIB$STOP(%val(SS_STATUS)) SS_STATUS = SYS$BRDCST(OPER_MESS(9:Message_len+10),TTYNUM) IF ((.not. SS_STATUS) .and. + (SS_STATUS .ne. %loc(SS$_DEVOFFLINE))) + CALL LIB$STOP(%val(SS_STATUS)) endif 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(1:Message_len+10),) IF (.not. SS_STATUS) CALL LIB$STOP(%val(SS_STATUS)) IF (NO_WARNING) then SS_STATUS = SYS$RESUME(%ref(OWNER),) IF ((SS_STATUS .ne. %loc(SS$_NORMAL)) .and. + SS_STATUS .ne. %loc(SS$_NONEXPR)) + CALL LIB$STOP(%val(SS_STATUS)) endif 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