.TITLE USERMN User Terminal Monitor .IDENT /V03.02/ ; Third released version .ENABL LC .NLIST CND ; Don't list unsatisfied conditionals .SBTTL Introduction ; ; USERMN - User Terminal Activity Monitor for RSX-11M/M+ ; ; Authors: ; Bruce R. Mitchell ; ; Patterned from the POOL monitor task supplied by Digital Equipment ; Corporation on RSX-11M-Plus V1.0 Autopatch E ; ; Thanks to Michael J. Lynch for abundant help with system internals ; Source Site: ; Machine Intelligence and Industrial Magic ; PO Box 601, Hudson, Wisconsin 54016 ; Source Hardware and Operating System: ; DEC PDP-11/70 under RSX-11M-Plus V2.0 Autopatch A ; Target Site: ; Same ; Target Hardware and Operating System: ; Same ; Revision History: ; 31-Dec-81 First version written ; 11-Sep-82 M-Plus V2.0 / M V4.0 - Don't count self as an active task ; 12-Feb-83 We finally beat the DECnet RMT logout problem ; 17-Feb-83 Allow only one copy of self per system ; 17-Feb-83 Add support for hibernation; 'wakeup' and 'sleep' times ; 28-Feb-83 Conditionalize against old RSX-11M V3.2 / M-Plus V1.0 ; 22-May-83 Don't set terminal active bits for tasks which are CLIs .PAGE .SBTTL Description and Philosophy 101 ; ; Problem: On most corporate development systems, a somewhat ; secure operating system is desired. While RSX-11 is a fine tool ; for developing code for industrial applications, its security ; regarding the matter of unattended terminals leaves a good deal ; to be desired. ; ; One solution to this problem is to force every user on the ; system to log out at noon and 5 PM, thereby ensuring that those ; terminals which were left unattended up to those times are now ; secure. However, in between these forced logouts, any fool can ; walk up to a terminal and delete files, possibly steal sensitive ; information, or worse, if the terminal is privileged (as most ; users are on development systems) delete any and all files he ; wishes. ; ; This task addresses itself to this problem. It examines ; the terminal driver data structures looking for logged-in ; terminals. It also scans the active task list and notes the ; "owning terminal" for all active tasks. ; ; The monitor then follows this procedure for each logged-on ; terminal: (1) If a terminal has no active tasks, bumps an idle ; time counter for the terminal, or (2) if a terminal has active ; tasks, resets the idle time counter for the terminal. If the ; idle time counter shows that the terminal should receive a ; first, second or final warning, a warning is sent to the ; terminal via a breakthrough write. If the idle time counter ; shows that the terminal has been idle the maximum permissible ; time, the terminal is logged out and a message is printed on the ; system console noting this transaction. ; .PAGE .SBTTL Caveats and Restrictions ; ; Those sites which do not use VT100s as their primary type ; terminal should not select VT100 support in the build file. The ; selection of VT100 support forces escape sequences onto target ; terminals causing blinking, intensity changes and screen clears. ; These VT100 sequences cause particular problems on VT52s and ; VT100s in VT52 mode. ; ; The "console" terminal, TT0:, is never logged out by the monitor ; due to the fact that every task installed at startup time has ; TT0: as its owning terminal. Obviously, TT0: will always have ; something active on it. We do not feel that this is a major ; problem since TT0: is usually located in the computer room and ; access to it is therefore (probably) restricted. ; ; If DECnet HT: support is selected, the user monitor MUST be ; started AFTER DECnet is completely up. The user monitor may try ; to access the DECnet HT: driver structures while they are being ; built or accessed by DECnet if the monitor is loaded and run ; before DECnet is up; this will cause flaky, intermittent crashes ; during DECnet startup, and the cause will not be immediately ; obvious. ; ; The author disclaims any responsibility or liability for damages ; which may result from the use of this program. As it is ; distributed, it runs fine and no ill effects have been noticed. ; Remember that it runs naked through the Exec and examines system ; data structures. Change anything in it as you wish; but if you ; do and it does something foul, don't bother to tell me about it. ; .PAGE .SBTTL .SBTTL Local Definitions ; Define O$$LDM for old versions of RSX (where GCII$ nonexistent) .IF NDF S$$NM1 ; If an old version of RSX O$$LDM = 1 ; This is an old version of RSX .ENDC ; NDF S$$NM1 ; Special ASCII characters ESC = 33 ; Escape BEL = 7 ; Bell LF = 12 ; Line feed CR = 15 ; Carriage return ; Local assignments LUN1 = 1 ; LUN 1 is used for console I/O LUN2 = 2 ; LUN 2 is used for user terminal I/O EFN1 = 1 ; EFN 1 is used for console I/O EFN2 = 2 ; EFN 2 is used for user terminal I/O MRKEFN = 3 ; EFN 3 is used for for mark times SPNEFN = 4 ; EFN 4 is used for spawns HIBEFN = 5 ; EFN 5 is used for hibernation CVTPRM = 10012 ; Conversion parameters for $CBTA ; (RADIX 10., FIELD WIDTH 2) ; Bit masks for terminal and task characteristics TM.LOG = 1 ; Terminal logged in TM.TSK = 2 ; Task active on terminal TM.MCR = 4 ; Terminal CLI is MCR (not used) TM.1ST = 10 ; First warning message sent TM.2ND = 20 ; Second warning message sent TM.3RD = 40 ; Final warning message sent .PAGE .SBTTL Macro Calls and Definitions ; ; System Definition Macros from LB:[1,1]EXEMC.MLB ; .MCALL DCBDF$ ; Device Control Block offsets .MCALL HWDDF$ ; Hardware register symbols .MCALL TCBDF$ ; Task Control Block offsets .MCALL UCBDF$ ; Unit Control Block offsets DCBDF$ HWDDF$ TCBDF$ UCBDF$ ; ; Directive Macros from LB:[1,1]SYSLIB.OLB ; .MCALL ALTP$ ; Alter priority .MCALL ALUN$ ; Assign Logical Unit Number .MCALL DIR$ ; Execute directive .MCALL DSCP$S ; Disable checkpointing for self .MCALL ENCP$S ; Enable checkpointing for self .MCALL EXIT$S ; Exit to RSX .IF NDF O$$LDM ; If not an old RSX system .MCALL GCII$ ; Get command interpreter info .ENDC ; NDF O$$LDM .MCALL GTIM$ ; Get system clock time .MCALL GTSK$ ; Get task parameters .MCALL MRKT$ ; Mark time .MCALL QIOW$ ; Queue I/O .MCALL SPWN$ ; Spawn task .MCALL STSE$ ; Stop for single event flag .MCALL WTSE$ ; Wait for single event flag .PAGE .SBTTL Variables ; ; Variables ; .IF NDF O$$LDM ; If not an old version of RSX .IF DF RS.NSL ; If DECnet support selected CIIBUF: .BLKW 2 ; Buffer area for CLI name .ENDC ; DF RS.NSL .ENDC ; NDF O$$LDM DATBUF: .BLKW 8. ; Buffer area for date and time TSKBUF: .BLKW 16. ; Buffer area for task parameters ; ; Local terminal data storage area ; ; Format: Flags word - Idle counter ; ; TT Offset TT Offset TT Offset ; ; 0 0 30 140 60 300 ; 1 4 31 144 61 304 ; 2 10 32 150 62 310 ; 3 14 33 154 63 314 ; 4 20 34 160 64 320 ; 5 24 35 164 65 324 ; 6 30 36 170 66 330 ; 7 34 37 174 67 334 ; ; 10 40 40 200 70 340 ; 11 44 41 204 71 344 ; 12 50 42 210 72 350 ; 13 54 43 214 73 354 ; 14 60 44 220 74 360 ; 15 64 45 224 75 364 ; 16 70 46 230 76 370 ; 17 74 47 234 77 374 ; ; 20 100 50 240 100 400 ; 21 104 51 244 101 404 ; 22 110 52 250 102 410 ; 23 114 53 254 103 414 ; 24 120 54 260 104 420 ; 25 124 55 264 105 424 ; 26 130 56 270 106 430 ; 27 134 57 274 107 434 TRMDAT: ; Beginning of data storage area .REPT NTT ; Repeat for number of terminals .WORD 0 ; Storage for terminal flags .WORD 0 ; Storage for idle time counter .ENDR ; # NTT .IF DF RS.NSL ; If number of HT: terminals defined ; ; DECnet Terminal Data Storage Area (Conditionally assembled) ; NETDAT: ; Beginning of data storage area .REPT NHT ; Repeat for number of terminals .WORD 0 ; Storage for terminal flags .WORD 0 ; Storage for idle time counter .ENDR ; # NHT .ENDC ; DF RS.NSL .PAGE .SBTTL Data Structures ; ; Directive Data Structures ; .IF DF T$ILIM ; If time limitation selected ; Lower priority while hibernating LOWPRI: ALTP$ , 10. ; Lower priority to 10. ; Raise priority after hibernation RAIPRI: ALTP$ , 150. ; Raise priority back to normal .ENDC ; DF T$ILIM ; Assign LUN for a terminal warning or logout message ASSLUN: ALUN$ 2, TT, 0 .IF NDF O$$LDM ; If not an old version of RSX .IF DF RS.NSL ; If DECnet support selected ; Obtain command line interpreter name for a given terminal GCINFO: GCII$ CIIBUF, 4,, TT ; Get CLI info buffer is CIIBUF ; Length is 4 bytes ; No CLI name specified ; Unit type is TT .ENDC ; DF RS.NSL .ENDC ; DF O$$LDM ; Obtain information about self from the system GSINFO: GTSK$ TSKBUF ; Get information on self ; Obtain the current time and date from the system DATDPB: GTIM$ DATBUF ; DPB for obtaining current time .IF DF T$ILIM ; If time limitation selected ; Mark time to set self into hibernation during inactive time MRKDPB: MRKT$ HIBEFN, 0, 3 ; DPB for mark time .ENDC ; DF T$ILIM ; Mark time for the monitoring interval in seconds TIMDPB: MRKT$ MRKEFN, SECNDS, 2 ; DPB for mark time ; Print messages on terminals BRODPB: QIOW$ IO.WBT, LUN2, EFN2,,,, < , > ; Print logging message on the system console CONDPB: QIOW$ IO.WBT, LUN1, EFN1,,,, <0, 0, 40> ; Spawn MCR... to log out the terminal specified at address MCRTRM, ; setting event flag SPNEFN on exit or status emission MCRSPN: SPWN$ MCR...,,,,, SPNEFN,,, MCRBYE, BYELTH,, TT ; Spawn MCR... for the terminal ; Set SPNEFN event flag on exit ; Command line stored in MCRBYE ; Length of command line is BYELTH ; Terminal type TT MCRBYE: .ASCII /BYE/ ; MCR command line to log out terminal BYELTH = . - MCRBYE ; Length of command line .EVEN .IF DF T$ILIM ; If time limitation selected ; Stop self while waiting for the hibernation period to expire HIBERN: STSE$ HIBEFN ; Go to sleep and wait for wakeup .ENDC ; DF T$ILIM ; Stop self while waiting to examine terminals again SLEEP: STSE$ MRKEFN ; Go to sleep and wait for wakeup ; Wait for the spawn event flag or timeout event flag (EFNs 2 and 3) SPNWAI: WTSE$ SPNEFN ; Wait for SPAWN exit or timeout .PAGE .SBTTL Messages ; ; Messages ; .NLIST BIN ; This message is printed on the system console or logging device CONMSG: .BLKB 8. ; Storage for time string .ASCII / Idle terminal forced logout on / .EVEN LGODEV: .BLKW 1 ; Space to print terminal type LGOTRM: .BLKB 2 ; Space to print terminal number .ASCII /:/ CONLEN = . - CONMSG .IF DF T$ILIM ; If time limited support included ; This is the message printed when the monitor begins hibernation BEDMSG: .BLKB 8. ; Storage for time string .ASCII / User monitor beginning hibernation/ BEDLEN = . - BEDMSG ; This is the message printed when the monitor comes out of it WKPMSG: .BLKB 8. ; Storage for time string .ASCII / User monitor ending hibernation/ WKPLEN = . - WKPMSG .ENDC ; DF T$ILIM ; This is the first warning message printed on a user's terminal ; On a VT100 time remaining will blink in bright mode WARN1: .ASCII .ASCII /From USER monitor: First idle terminal warning!/ .ASCII / / .IF DF V$T100 ; If VT100 support selected .ASCII <133><65><73><61><155> ; $[5;1m .ENDC ; DF V$T100 .EVEN .WORD TIME1 ; Remaining time .ASCII / minutes/ .IF DF V$T100 ; If VT100 support selected .ASCII <133><60><155> ; $[0m .ENDC ; V$T100 .ASCII / before forced logout/ .ASCII WRN1LN =. - WARN1 ; Length of warning message ; This is the second warning message printed on a user's terminal ; On a VT100 time remaining will blink in bright mode WARN2: .ASCII .ASCII /From USER monitor: Second idle terminal warning!/ .ASCII / / .IF DF V$T100 ; If VT100 support selected .ASCII <133><65><73><61><155> ; $[5;1m .ENDC ; V$T100 .EVEN .WORD TIME2 ; Remaining time .ASCII / minutes/ .IF DF V$T100 ; If VT100 support selected .ASCII <133><60><155> ; $[0m .ENDC ; V$T100 .ASCII / before forced logout/ .ASCII WRN2LN =. - WARN2 ; Length of warning message ; This is the third warning message printed on a user's terminal ; On a VT100 time remaining will blink in bright mode WARN3: .ASCII .ASCII /From USER monitor: Final idle terminal warning!/ .ASCII / / .IF DF V$T100 ; If VT100 support selected .ASCII <133><65><73><61><155> ; $[5;1m .ENDC ; V$T100 .ASCII /1 minute/ .IF DF V$T100 ; If VT100 support selected .ASCII <133><60><155> ; $[0m .ENDC ; V$T100 .ASCII / before forced logout/ .ASCII WRN3LN = . - WARN3 ; Length of warning message ; This is the message printed on a user's terminal at forced logout ; On a VT100 it will home the cursor, clear the screen and blink BANZAI: .ASCII .IF DF V$T100 ; If VT100 support selected .ASCII <133><61><73><61><110> ; $[1;1H .ASCII <133><62><112> ; $[2J .ASCII <133><65><155> ; $[5m .ENDC ; V$T100 .ASCII /From USER monitor: Forced idle terminal logout/ .IF DF V$T100 ; If VT100 support selected .ASCII <133><60><155> ; $[0m .ENDC ; V$T100 .ASCII BANZLN = . - BANZAI ; Length of warning message .EVEN .LIST BIN .PAGE .SBTTL .SBTTL Mainline Code .SBTTL .SBTTL USERMN Entry point ; ; The task has just been initiated. There is no point in having ; more than one copy running on the system, so this code ensures ; that only one copy, with the name USERMN, will run on the system. ; USERMN: ; Entry point ; Ensure that we are the only copy of USERMN running on the system DIR$ #GSINFO ; Get information about self CMP TSKBUF+G.TSTN, #^RUSE ; Is first half of name "USE"? BNE 10$ ; If not, prepare to exit CMP TSKBUF+G.TSTN+2, #^RRMN ; Is second half of name "RMN"? .IF DF T$ILIM ; If time limitation supported BEQ TIMCHK ; If so, jump into time checking .IFF ; If time limitation not supported BEQ TSKS ; If so, proceed .ENDC ; DF T$ILIM ; This task's name is not USERMN; exit 10$: EXIT$S ; Exit .IF DF T$ILIM ; If a time-limited version .PAGE .SBTTL TIMCHK Validate Activity Time ; ; This conditional code determines whether the monitor should be ; running at this time. It checks against the early and late ; limits for operation, continuing activity if within those limits ; and ceasing activity outside of them. Wakeup and bedtime ; console messages are also printed here. ; ; Note that if this option is selected, checkpointing becomes a ; dynamic process; checkpointing is disabled during active operation ; and disabled while the task is sleeping. ; TIMCHK: DIR$ #DATDPB ; Get current time MOV DATBUF+G.TIMI, R2 ; Load minutes into R0 MOV DATBUF+G.TIHR, R1 ; Load hours into R1 ; Find out if it is time to hibernate CMP R1, #ERLHR ; Is this the hour to hibernate? BLT TSKS ; If LT, active; jump into active code BGT 10$ ; If GT; maybe; proceed CMP R2, #ERLMN ; Hour EQ; is this the minute hibernate? BLT TSKS ; If LT, active; jump into active code ; It may be necessary to hibernate; but is it really the active period? 10$: CMP R1, #LATHR ; Is it the hour to wake up? BGT TSKS ; If GT, active; jump into active code BLT 20$ ; If LT, sleep period; go set up for it CMP R2, #LATMN ; Hour EQ; is it the minute to wake up? BGE TSKS ; If so, jump into active code ; It's time to sleep or hibernation period; print console message 20$: MOV #BEDMSG, R3 ; Load time field address into R3 MOV R3, CONDPB+Q.IOPL ; Load message address into QIO DPB MOV #BEDLEN, CONDPB+Q.IOPL+2 ; Load message length into QIO DPB CALL DATSUB ; Load ASCII time into message DIR$ #CONDPB ; Print the console wakeup message ; Calculate amount of time to sleep between now and wakeup time MOV #<+LATMN>, R0 ; Load wakeup time in minutes into R0 MUL #60., R1 ; Hour * 60. is minutes past midnight ADD R2, R1 ; Add minutes, for real figure SUB R1, R0 ; Subtract current time from wakeup MOV R0, MRKDPB+M.KTMG ; Poke sleep time into marktime DPB DIR$ #MRKDPB ; Set marktime for startup time ; Note while hibernating, R0 shows length of hibernation; other Rs zero CLR R1 ; Clear R1 before hibernation CLR R2 ; Clear R2 before hibernation CLR R3 ; Clear R3 before hibernation CLR R4 ; Clear R4 before hibernation CLR R5 ; Clear R5 before hibernation ENCP$S ; Enable checkpointing while asleep DIR$ #LOWPRI ; Lower priority (10) while hibernating DIR$ #HIBERN ; Hibernate until time to wake up ; The task has now just waked up; print a console message DIR$ #RAIPRI ; Raise priority, we're active again MOV #WKPMSG, R3 ; Load time field address into R3 MOV R3, CONDPB+Q.IOPL ; Load message address into QIO DPB MOV #WKPLEN, CONDPB+Q.IOPL+2 ; Load message length into QIO DPB CALL DATSUB ; Load ASCII time into message DIR$ #CONDPB ; Print the console wakeup message DSCP$S ; Disable checkpointing while awake ; Clear all the information buffers MOV #TRMDAT, R0 ; Load terminal data address into R0 MOV #NTT*2, R1 ; Count down words to clear in R1 30$: CLR (R0)+ ; Clear a single word in the block SOB R1, 30$ ; And loop until all cleared .IF DF RS.NSL ; If DECnet is supported MOV #NETDAT, R0 ; Load DECnet HT: data address into R0 MOV #NHT*2, R1 ; Count down words to clear in R1 40$: CLR (R0)+ ; Clear a single word in the block SOB R1, 40$ ; And loop until all cleared .ENDC ; DF RS.NSL .ENDC ; DF T$ILIM .PAGE .SBTTL TSKS Search Active Task List ; ; Mainline code ; ; We just came in; the first thing to do is scan the active task ; list to find out which terminals have active tasks. Before that, ; though, we have to clear the logged-in and task active flags for ; the terminals. ; ; Note that the "physical unit number" in a TT's UCB is not really ; the physical unit number for the device; it is the offset from ; the base unit number for the TT's DCB. Therefore we have to get ; the true unit number by dividing the distance between the TT's ; UCB and the DCB's base UCB by the length of the TT UCB, and then ; adding the resulting number to the TT's DCB base unit number. ; ; Since a number of tasks may be active on CO0:, VT1:, HT3: and so ; on, a check is made for terminals of type TTnn: . (If DECnet is ; supported, a check is made for terminals of type HTnn: .) No ; action is taken if the terminal is not a TT: . (or HT: in the ; case of a DECnet terminal.) ; TSKS: MOV #, R0 ; R0 counts down TT: data blocks 10$: BIC #, TRMDAT(R0) ; Clear terminal active flags SUB #4, R0 ; Point at flag for preceding unit BGE 10$ ; If TT0: not processed, loop again .IF DF RS.NSL ; If DECnet is supported MOV #, R0 ; R0 counts down HT: data blocks 20$: BIC #, NETDAT(R0) ; Clear terminal active flags SUB #4, R0 ; Point at flag for preceding unit BGE 20$ ; If HT0: not processed, loop again .ENDC ; DF RS.NSL ; Switch to system state and check active task list SWSTK$ COMPAR ; Switch to system state; return ; from it at COMPAR ; When called back here, task is at system state and maps the Executive MOV $ACTHD, R0 ;; R0 points to active task listhead ; Check to see if task being examined is self (current active task) 30$: CMP R0, $TKTCB ;; Is task being examined current task? BEQ 70$ ;; If so, don't do bit setting ; Task being examined is not self; see if it is a CLI BIT #T3.CLI, T.ST3(R0) ;; Is task a command line intepreter? BNE 70$ ;; If so, skip around bit setting code ; Set task active bits for the owning terminal MOV T.UCB(R0), R3 ;; R3 points at UCB for task's "TI:" MOV U.DCB(R3), R1 ;; R1 points at DCB for task's "TI:" CMP D.NAM(R1), #"TT ;; Is this a terminal DCB? .IF NDF RS.NSL ; If DECnet is not supported BNE 70$ ;; If not, we don't care about it .IFF ; If DECnet is supported BNE 40$ ;; If not, check for DECnet terminal CLR R4 ;; R4 clear indicates a TT: device BR 50$ ;; Go process it 40$: CMP D.NAM(R1), #"HT ;; Is this a DECnet terminal DCB? BNE 70$ ;; If not, we don't care about it INC R4 ;; R4 nonzero indicates an HT: device .ENDC ; NDF RS.NSL ; Calculate terminal number and set active task flag for it 50$: SUB D.UCB(R1), R3 ;; R3 is distance of UCB from 1st UCB CLR R2 ;; Clear 32-bit R2 extension word DIV D.UCBL(R1), R2 ;; Divide distance by length of UCB MOVB D.UNIT(R1), R1 ;; R1 is lowest unit # on DCB ADD R1, R2 ;; R2 is now terminal number ASL R2 ;; Multiply R2 by 4 for addressing ASL R2 ;; (2 bytes/word * 2 words) .IF NDF RS.NSL ; If DECnet not supported BIS #TM.TSK, TRMDAT(R2) ;; Set active task flag for terminal .IFF ; If DECnet is supported TST R4 ;; Is this an HT: or a TT: device? BNE 60$ ;; If an HT: device, go process it BIS #TM.TSK, TRMDAT(R2) ;; Set active task flag for TT: device BR 70$ ;; Go set up to do next task 60$: BIS #TM.TSK, NETDAT(R2) ;; Set active task flag for HT: device .ENDC ;; NDF RS.NSL ; Set up pointers for next active task and check for list end 70$: MOV T.ACTL(R0), R0 ;; Point to next active task TST R0 ;; Is this the list termination? BNE 30$ ;; Loop back for more if not .PAGE .SBTTL DCBS Search DCB List ; ; Now we know which terminals have tasks active on them. The next ; thing to do is find out which terminals are logged in. Before that, ; though, we have to set and clear the flags indicating which units ; are logged in. ; DCBS: MOV $DEVHD, R0 ;; R0 points to DCB listhead 10$: CMP D.NAM(R0), #"TT ;; Is this a terminal DCB? .IF NDF RS.NSL ; If DECnet not supported BNE 70$ ;; If not, go get a new DCB address .IFF ; If DECnet is supported BNE 20$ ;; If not, go check for DECnet HT: CLR R4 ;; R4 clear indicates a TT: device BR 30$ ;; Go process it 20$: CMP D.NAM(R0), #"HT ;; Is this a DECnet HT: DCB? BNE 70$ ;; If not, go get a new DCB address INC R4 ;; R4 nonzero indicates an HT: device .ENDC ; NDF RS.NSL 30$: MOVB D.UNIT(R0), R1 ;; R1 is now lowest unit number on DCB MOVB D.UNIT+1(R0), R3 ;; Highest unit number served by DCB SUB R1, R3 ;; R3 is number of units served - 1 INC R3 ;; Now R3 is usable as a down counter MOV R1, R2 ;; R2 is now lowest unit number on DCB ASL R2 ;; Multiply it by 4 for addressing ASL R2 ;; (2 bytes/word * 2 words) MOV D.UCB(R0), R1 ;; R1 points to first terminal's UCB ; Loop through all the units on this DCB, checking logged-in terminals 40$: BIT #U2.LOG, U.CW2(R1) ;; Is this unit logged in? BNE 60$ ;; If not, go look at next terminal .IF NDF RS.NSL ;; If DECnet not supported BIS #TM.LOG, TRMDAT(R2) ;; Set logged-in terminal flag .IFF ;; If DECnet is supported TST R4 ;; Is this a TT: or an HT: device? BNE 50$ ;; If an HT: device, go process it BIS #TM.LOG, TRMDAT(R2) ;; Set logged-in flag for TT: device BR 60$ ;; Go set up to do next device 50$: BIS #TM.LOG, NETDAT(R2) ;; Set logged-in flag for HT: device .ENDC ;; NDF RS.NSL 60$: ADD #4, R2 ;; R2 points at next term flag word ADD D.UCBL(R0), R1 ;; R1 now points at next UCB on DCB SOB R3, 40$ ;; Loop for each UCB on each DCB ; Finished with DCB, prepare to examine next DCB 70$: MOV D.LNK(R0), R0 ;; R0 now points at next DCB in list BNE 10$ ;; If not list termination, loop back RETURN ;; No more DCBs, return to user state .PAGE .SBTTL COMPAR Crosscheck Tasks and Terminals ; ; Back in user state. Now we have to cross-check logged-in terminals ; against active tasks, and bump the idle counter for those terminals ; which had no tasks active on them. For those terminals which had ; tasks active on them, the idle counter has to be reset. ; COMPAR: MOV #, R0 ; R0 counts down terminal data blocks 10$: BIT #TM.LOG, TRMDAT(R0) ; Is this terminal logged in? BNE 20$ ; If so, skip and process it CLR TRMDAT+2(R0) ; Not logged in; clear idle counter BR 40$ ; Go set up to do next data block ; Terminal logged in; check for an active task on it 20$: BIT #TM.TSK, TRMDAT(R0) ; Does it have an active task? BNE 30$ ; If so, skip around inactive code INC TRMDAT+2(R0) ; No active task; bump idle counter BR 40$ ; Go set up to do next one ; Terminal logged in with active task; reset necessary flags 30$: BIC #, TRMDAT(R0) ; Clear warning flags CLR TRMDAT+2(R0) ; Active task; clear idle counter ; Set up to do next terminal data block 40$: SUB #4, R0 ; Point at flag word of preceding unit BGE 10$ ; If TT0: not processed, loop again .IF DF RS.NSL ; If DECnet is supported MOV #, R0 ; R0 counts down HT: device data blocks 50$: BIT #TM.LOG, NETDAT(R0) ; Is this HT: logged in? BNE 60$ ; If so, skip and process it CLR NETDAT+2(R0) ; Not logged in; clear idle counter BR 80$ ; Go set up to do next data block ; HT: is logged in; check for an active task on it 60$: BIT #TM.TSK, NETDAT(R0) ; Does it have an active task? BNE 70$ ; If so, skip around inactive code INC NETDAT+2(R0) ; No active task; bump idle counter BR 80$ ; Go set up to do next one ; DECnet terminal logged in with active task; reset necessary flags 70$: BIC #, NETDAT(R0) ; Clear warning flags CLR NETDAT+2(R0) ; Active task; clear idle counter ; Set up to do next HT: data block 80$: SUB #4, R0 ; Point at flag word of preceding unit BGE 50$ ; If TT0: not processed, loop again .ENDC ; DF RS.NSL .PAGE .SBTTL CKTIME Check Terminal Idle Time ; ; Now we have to check terminals with nonzero idle time counters to ; see if they rate warnings or a forced logout. Those terminals with ; idle time counters of zero can be ignored. (If DECnet HT: device ; checking support is included, these devices will be examined also.) ; ; Idle time for idle terminals is compared against the generation-time ; limits. If the idle time is greater than the limits, messages are ; sent or logouts are spawned by the appropriate subroutines. ; CKTIME: CLR R1 ; R1 is terminal number CLR R2 ; R2 is offset in terminal data block .IF DF RS.NSL ; If DECnet is supported CLR R3 ; Assume terminal type is TT .ENDC ; DF RS.NSL MOV #"TT, ASSLUN+A.LUNA ; Set device to TT for ALUN$ MOV #"TT, MCRSPN+S.PWDN ; Set device to TT for SPWN$ MOV #"TT, LGODEV ; Set device to TT for console message 10$: TST TRMDAT+2(R2) ; Is this an idle terminal? BEQ 70$ ; If not, set up to examine next term .IF NDF O$$LDM ; If not an old version of RSX .IF DF RS.NSL ; If DECnet support selected ; Is this terminal logged in to another system via DECnet? MOV R1, GCINFO+G.CIUN ; Load the terminal number into DPB DIR$ #GCINFO ; Go get the information CMP CIIBUF+G.CICL, #^RRMT ; Is the CLI set to RMT? BNE 20$ ; If not, go check the idle code ; Terminal is logged in remote to another system; reset idle counter CLR TRMDAT+2(R2) ; Clear the idle counter BR 70$ ; Jump around logout code .ENDC ; DF RS.NSL .ENDC ; DF O$$LDM ; Terminal is idle; check against time limits for warnings and logout 20$: CMP TRMDAT+2(R2), #FIRST ; Idle less than first warning time? BLT 70$ ; If so, set up to examine next term ; Check against logout time limit 30$: CMP TRMDAT+2(R2), #FOURTH ; Does idle time indicate a logout? BLT 40$ ; If not, go check for final warning CALL KILLIT ; If so, go log it out BR 70$ ; Go set up to process next terminal ; Check against final warning time limit 40$: CMP TRMDAT+2(R2), #THIRD ; Does idle time indicate final warn? BLT 50$ ; If not, go check for second warning CALL WARFIN ; If so, go issue the final warning BR 70$ ; Go set up to process next terminal ; Check against second warning time limit 50$: CMP TRMDAT+2(R2), #SECOND ; Does idle time indicate second warn? BLT 60$ ; If not, go issue first warning CALL WARTWO ; If so, go issue the second warning BR 70$ ; Go set up to process next terminal ; Issue first warning 60$: CALL WARONE ; Issue the first warning ; Check for last terminal and setup for next terminal 70$: CMP R1, #ITT ; Has the last terminal been done? .IF NDF RS.NSL ; If DECnet is not supported BGE 150$ ; If so, exit this loop .IFF ; If DECnet is supported BGE 80$ ; Go process DECnet HT: devices .ENDC ; NDF RS.NSL INC R1 ; R1 is now number of next terminal ADD #4, R2 ; R2 is now offset to next data block BR 10$ ; Not finished yet, loop .IF DF RS.NSL ; If DECnet is supported 80$: CLR R1 ; R1 is HT: device number CLR R2 ; R2 is offset in HT: data block MOV #1, R3 ; Terminal type is HT: MOV #"HT, ASSLUN+A.LUNA ; Set device to HT for ALUN$ MOV #"HT, MCRSPN+S.PWDN ; Set device to HT for SPWN$ MOV #"HT, LGODEV ; Set device to HT for console message 90$: TST NETDAT+2(R2) ; Is this an idle HT: terminal? BEQ 140$ ; If not, set up to examine next term ; HT: is idle; check against time limits for warnings and logout CMP NETDAT+2(R2), #FIRST ; Idle less than first warning time? BLT 140$ ; If so, set up to examine next HT: ; Check against logout time limit 100$: CMP NETDAT+2(R2), #FOURTH ; Does idle time indicate a logout? BLT 110$ ; If not, go check for final warning CALL KILLIT ; If so, go log it out BR 140$ ; Go set up to process next HT: ; Check against final warning time limit 110$: CMP NETDAT+2(R2), #THIRD ; Does idle time indicate final warn? BLT 120$ ; If not, go check for second warning CALL WARFIN ; If so, go issue the final warning BR 140$ ; Go set up to process next HT: ; Check against second warning time limit 120$: CMP NETDAT+2(R2), #SECOND ; Does idle time indicate second warn? BLT 130$ ; If not, go issue first warning CALL WARTWO ; If so, go issue the second warning BR 140$ ; Go set up to process next HT: ; Issue first warning 130$: CALL WARONE ; Issue the first warning ; Check for last HT: and setup for next HT: 140$: CMP R1, #IHT ; Has the last HT: been done? BGE 150$ ; If so, exit this loop INC R1 ; R1 is now number of next HT: ADD #4, R2 ; R2 is now offset to next data block BR 90$ ; Not finished yet, loop .ENDC ; DF RS.NSL ; All terminals are processed; go back to sleep 150$: DIR$ #TIMDPB ; Set marktime for monitoring wakeup DIR$ #SLEEP ; Hide from users and go to sleep .IF DF T$ILIM ; If time limitation supported JMP TIMCHK ; Go check the current time again .IFF ; If time limitation not supported JMP TSKS ; Go take another look at everybody .ENDC ; DF T$ILIM .PAGE .SBTTL .SBTTL Subroutines .SBTTL .SBTTL DATSUB Create ASCII Time ; ; DATSUB - Obtain the Current Time and Convert to ASCII ; ; Inputs: R3 - Address of output field for ASCII time ; ; Outputs: (R3) gets ASCII time ; ; All registers used are saved and restored ; DATSUB: MOV R0, -(SP) ; Push volatile registers on stack MOV R1, -(SP) ; MOV R2, -(SP) ; DIR$ #DATDPB ; Execute get time and date directive MOV R3, R0 ; Load ASCII time field address in R0 MOV #DATBUF+G.TIHR, R1 ; Load binary time field address in R1 MOV #3, R2 ; Specify HH:MM:SS format CALL $TIM ; Convert time to ASCII MOV (SP)+, R2 ; Pop volatile registers off stack MOV (SP)+, R1 ; MOV (SP)+, R0 ; RETURN ; Return to caller .PAGE .SBTTL KILLIT Log Out an Idle Terminal ; ; KILLIT - Log Out an Idle Terminal ; ; Inputs: R1 - Number of target terminal ; R2 - Pointer to terminal data block ; R3 - 0 if terminal TT, 1 if terminal HT ; ; Outputs: Terminal data flag word is cleared ; ; Registers used are not saved and restored ; KILLIT: MOV R1, MCRSPN+S.PWVT ; Load terminal number in SPWN$ DPB DIR$ #MCRSPN ; Execute directive to log it out BCC 5$ ; If successful, continue RETURN ; If spawn failed, just forget it 5$: DIR$ #SPNWAI ; Wait for spawn complete MOV R1, ASSLUN+A.LUNU ; Load terminal number in ALUN$ DPB DIR$ #ASSLUN ; Assign LUN 2 to target terminal BCC 10$ ; If it succeeded, go issue message RETURN ; If LUN assign failed, just forget it ; Log the forced logout on the target terminal 10$: MOV #BANZAI, BRODPB+Q.IOPL ; Load message address into QIO DPB MOV #BANZLN, BRODPB+Q.IOPL+2; Load message length into QIO DPB DIR$ #BRODPB ; Send logout message .IF DF RS.NSL ; If DECnet is supported TST R3 ; Is this a TT or an HT BEQ 15$ ; If a TT, jump around HT code CLR NETDAT(R2) ; Clear all flags for HT BR 20$ ; And continue 15$: CLR TRMDAT(R2) ; Clear all flags for terminal .IFF ; If DECnet not supported CLR TRMDAT(R2) ; Clear all flags for terminal .ENDC ; DF RS.NSL ; Log the forced logout on the system console 20$: MOV #CONMSG, R3 ; Load target time field addr into R3 MOV R3, CONDPB+Q.IOPL ; Load message address into print DPB MOV #CONLEN, CONDPB+Q.IOPL+2 ; Load message length into pring DPB CALL DATSUB ; Load console message time and date MOV R0, -(SP) ; Save volatile registers on stack MOV R1, -(SP) ; yup MOV R2, -(SP) ; yup MOV #0, LGOTRM ; Null fill terminal number MOV #LGOTRM, R0 ; Move address of output field to R0 CLR R2 ; Specify suppression of leading 0s CALL $CBOMG ; Convert binary TT number to ASCII MOV (SP)+, R2 ; Restore volatile registers MOV (SP)+, R1 ; yup MOV (SP)+, R0 ; yup DIR$ #CONDPB ; Print logging message on console RETURN ; Return to caller .PAGE .SBTTL WARONE Issue First Warning ; ; WARONE - Issue First Warning to a Terminal ; ; Inputs: R1 - Target terminal number ; R2 - Pointer to terminal data block ; R3 - 0 if terminal is a TT, 1 if an HT ; ; Outputs: Terminal flag word is updated ; ; Registers used are not saved and restored ; .IF DF RS.NSL ; If DECnet is supported WARONE: TST R3 ; Is this a TT or an HT BEQ 5$ ; If a TT, skip HT code BIT #TM.1ST, NETDAT(R2) ; Has the first message been sent? BEQ 10$ ; If not, go issue it 5$: BIT #TM.1ST, TRMDAT(R2) ; Has the first message been sent? .IFF ; If DECnet not supported WARONE: BIT #TM.1ST, TRMDAT(R2) ; Has the first message been sent? .ENDC ; DF RS.NSL BEQ 10$ ; If not, go issue it RETURN ; If so, return to caller 10$: MOV R1, ASSLUN+A.LUNU ; Load terminal number in ALUN$ DPB DIR$ #ASSLUN ; Assign LUN 2 to target terminal BCC 20$ ; If it succeeded, issue the message RETURN ; If LUN assign failed, just forget it 20$: MOV #WARN1, BRODPB+Q.IOPL ; Load message address into QIO DPB MOV #WRN1LN, BRODPB+Q.IOPL+2; Load message length into QIO DPB DIR$ #BRODPB ; Send warning message .IF DF RS.NSL ; If DECnet is supported TST R3 ; Is this a TT or an HT BEQ 25$ ; If a TT, skip HT code BIS #TM.1ST, NETDAT(R2) ; Set first message sent flag RETURN ; Return to caller 25$: BIS #TM.1ST, TRMDAT(R2) ; Set first message sent flag RETURN ; Return to caller .IFF ; If DECnet not supported BIS #TM.1ST, TRMDAT(R2) ; Set first message sent flag RETURN ; Return to caller .ENDC ; DF RS.NSL .PAGE .SBTTL WARTWO Issue Second Warning ; ; WARTWO - Issue Second Warning to a Terminal ; ; Inputs: R1 - Target terminal number ; R2 - Pointer to terminal data block ; R3 - 0 if terminal is a TT, 1 if an HT ; ; Outputs: Terminal flag word is updated ; ; Registers used are not saved and restored ; .IF DF RS.NSL ; If DECnet is supported WARTWO: TST R3 ; Is this a TT or an HT BEQ 5$ ; If a TT, skip HT code BIT #TM.2ND, NETDAT(R2) ; Has the second message been sent? BEQ 10$ ; If not, go issue it 5$: BIT #TM.2ND, TRMDAT(R2) ; Has the second message been sent? .IFF ; If DECnet not supported WARTWO: BIT #TM.2ND, TRMDAT(R2) ; Has the second message been sent? .ENDC ; DF RS.NSL BEQ 10$ ; If not, go issue it RETURN ; If so, return to caller 10$: MOV R1, ASSLUN+A.LUNU ; Load terminal number in ALUN$ DPB DIR$ #ASSLUN ; Assign LUN 2 to target terminal BCC 20$ ; If it succeeded, issue the message RETURN ; If LUN assign failed, just forget it 20$: MOV #WARN2, BRODPB+Q.IOPL ; Load message address into QIO DPB MOV #WRN2LN, BRODPB+Q.IOPL+2; Load message length into QIO DPB DIR$ #BRODPB ; Send warning message .IF DF RS.NSL ; If DECnet is supported TST R3 ; Is this a TT or an HT BEQ 25$ ; If a TT, skip HT code BIS #TM.2ND, NETDAT(R2) ; Set second message sent flag RETURN ; Return to caller 25$: BIS #TM.2ND, TRMDAT(R2) ; Set second message sent flag RETURN ; Return to caller .IFF ; If DECnet not supported BIS #TM.2ND, TRMDAT(R2) ; Set second message sent flag RETURN ; Return to caller .ENDC ; DF RS.NSL .PAGE .SBTTL WARFIN Issue Final Warning ; ; WARFIN - Issue Final Warning to a Terminal ; ; Inputs: R1 - Target terminal number ; R2 - Pointer to terminal data block ; R3 - 0 if terminal is a TT, 1 if an HT ; ; Outputs: Terminal flag word is updated ; ; Registers used are not saved and restored ; .IF DF RS.NSL ; If DECnet is supported WARFIN: TST R3 ; Is this a TT or an HT BEQ 5$ ; If a TT, skip HT code BIT #TM.3RD, NETDAT(R2) ; Has the final message been sent? BEQ 10$ ; If not, go issue it 5$: BIT #TM.3RD, TRMDAT(R2) ; Has the final message been sent? .IFF ; If DECnet not supported WARFIN: BIT #TM.3RD, TRMDAT(R2) ; Has the final message been sent? .ENDC ; DF RS.NSL BEQ 10$ ; If not, go issue it RETURN ; If so, return to caller 10$: MOV R1, ASSLUN+A.LUNU ; Load terminal number in ALUN$ DPB MOV R1, ASSLUN+A.LUNU ; Load terminal number in ALUN$ DPB DIR$ #ASSLUN ; Assign LUN 2 to target terminal BCC 20$ ; If it succeeded, go issue message RETURN ; If LUN assign failed, just forget it 20$: MOV #WARN3, BRODPB+Q.IOPL ; Load message address into QIO DPB MOV #WRN3LN, BRODPB+Q.IOPL+2; Load message length into QIO DPB DIR$ #BRODPB ; Send warning message .IF DF RS.NSL ; If DECnet is supported TST R3 ; Is this a TT or an HT BEQ 25$ ; If a TT, skip HT code BIS #TM.3RD, NETDAT(R2) ; Set final message sent flag RETURN ; Return to caller 25$: BIS #TM.3RD, TRMDAT(R2) ; Set second message sent flag RETURN ; Return to caller .IFF ; If DECnet not supported BIS #TM.3RD, TRMDAT(R2) ; Set second message sent flag RETURN ; Return to caller .ENDC ; DF RS.NSL .END USERMN