.TITLE KILLER - delete idle jobs .ENABLE DEBUG .IDENT /04/ ; actually, 4w for window ; +++ ; ; FUNCTIONAL DESCRIPTION: ; This program deletes idle jobs and timestamps the console. ; An idle job is defined as any job that does not have one of the ; following characteristics: ; o Used 100ms of CPU time in the last DELAY_TIME minutes ; o Done a buffered or direct I/O operation ; o Has a GROUP UIC of less than MAX_SYSGRP ; o Has a subprocess ; o Has no terminal, and has no owner ; o Has a terminal, but the terminal is a TW type ; The program logic is as follows: ; LOOP FOREVER ; Check every process. If idle, increment IDLECOUNT[PID] ; if IDLECOUNT[PID] = WARN_LIMIT, warn this user ; if IDLECOUNT[PID] = KILL_LIMIT, kill this user ; ; When done with processes, ; timestamp console ; $HIBER for DELAY_TIME minutes ; END LOOP ; ; This program should be run as follows: ; $ RUN KILLER/DETACH/PRIVILEGE=(OPER,WORLD,ALTPRI) ; it needs OPER to send messages; it needs WORLD to kill processes ; ; IMPLICIT INPUTS: ; process tables ; ; IMPLICIT OUTPUTS: ; messages to console, to users ; ; COMPLETION STATUS: ; never completes. May abort with error return. ; ; SIDE EFFECTS: ; deletes processes, timestamps console ; ; AUTHOR, DATE;TIME of CREATION ; Joel M Snyder, 22-Sep-1985 (Version 4 VMS version) ; modified 23-Nov-1985, added some I/O checks ; modified 10-Mar-1986, added breakbusters ; modified 1-Jul-1987, changed spelling error /04/ ; modified 10-Jun-1989, added window terminal exceptions ; ; --- .page ; +++ ; CUSTOMIZATION NOTES: ; ; There's a lot of places to customize this program, without ; even changing the code. In cosmetic things, the program sets ; it's priority down to 1 (search on $SETPRI); this is for ; maximum politeness. It also sets its name (search on ; process_name); you can change that without affecting anything. ; It has been argued that the idle job killer should set its ; priority up to 9 and not down to 1. I let you decide. ; ; In functional changes, there are three main constants -- the ; number of minutes to wait between loops over the system (DELAY_TIME) ; the number of times to loop on a process before warning it ; (WARN_LIMIT) and the number of times to loop on a process before ; killing it (KILL_LIMIT). In this distribution, DELAY_TIME=5, ; WARN_LIMIT=3, KILL_LIMIT=4, which means that a process is warned ; after 15 minutes, and killed after 20. If you choose to use ; the time stamp code (it sends a time-of-day message to the ; console every DELAY_TIME minutes), then decreasing DELAY_TIME won't ; win a lot of praise from the operators. ; ; You can always delete the time-stamp code, and also might want ; to delete the sending of a message to the console whenever a ; process is warned/killed. This is in there in case you want it, ; but it gets pretty dull after a while. There are additional ; notes in the code to tell you how to take that stuff out. ; ; This is the simplest of simple idle job killers. There are about ; 20 of them out there in DECUSland, but they have two problems: ; (1) they don't work under VMS V4 and (2) they're too damn complicated ; to use. This program is about 6 blocks without the comments; that ; seems a reasonable size for something that just kills idle jobs. ; For sites that don't need any special games or other tomfoolery, ; this might be just the simple ticket. ; ; KNOWN BUGS: ; I don't warn a subprocess that it's going to be killed, since ; it doesn't have a terminal. ; But I kill it anyway. ; ; If you find a bug, or if you have questions as to how to ; modify to fit your site, feel free to call me: ; ; Joel Snyder, University of Arizona, ; Dep't of Mgmt Information Systems ; Tucson,AZ 85721 (602) 621-2748 ; ; --- ; system call macros $JPIDEF ; $GETJPI definitions $SSDEF ; normal system service defs $BRKDEF ; $BRKTHRU definitions ; local macros .macro errorck,?l1 blbs r0,l1 $exit_s code=r0 l1: .endm ; constants ; The following five constants are a site-dependent list. Maxe sure ; that (1) MAXUSER is the maximum number of processes on your system ; (sysgen var MAXPROCCNT) (2) WARN_LIMIT < KILL_LIMIT (otherwise makes ; no sense (3) MIN_TIME is not too small. 5 appears to be OK. If you're ; idle, then you're really idle. The most often changed parameters ; are DELAY_TIME, WARN_LIMIT, and KILL_LIMIT. Make sure that your ; DECNET and other detached jobs are run with UIC < MAX_SYSGRP. Note: ; Digital defines MAX_SYSGRP as 10 (octal) or below. These numbers here ; are in decimal, so our MAX_SYSGRP is 14 (octal) ; MAXUSER = 130 ; maximum number of users <7><7> user_control_k: .ASCID ^!/!/!/!/!/!/!/!/!/!10*% Job Killer !%D !10*%!/ ^ - ^!AS terminated by system after 60 minutes idle time.^<7> fao_buffer: .long ORIGINAL_FAO_LEN .address fao_output fao_output: .blkb ORIGINAL_FAO_LEN fao_length: .long 0 .address fao_output console_terminal: .ASCID /_OPA0:/ ;console_terminal: .ASCID /_TXD5:/ ; for debugging purposes, instead of having to run down to the machine room .page ; ; code ; .PSECT CODE, EXE, RD, NOWRT ; killer: .word 0 ; register mask, save none $schdwk_s daytim=sleep_time,- ; schedule recurring wakeup reptim=sleep_time ; for now to eternity errorck $setprn_s prcnam=process_name ; change process name ; errorck ; how could this ever fail? ; (except by having someone) ; (else named this name) $setpri_s pri=#PROCESS_PRIORITY ; lower priority ; errorck ; how could this ever fail? ; (well, if you didn't have ; (ALTPRI and needed it) movl #WILDCARD, seedpid ; seed for wildcard $GETJPI ; main loop get: ;06-Mar-1986/jms ; added code to call breakbusters ; If you feel you don't need breakbusters (ie, you don't have secure server ; turned on on any terminals), then by all means comment this line out. ; ; calls #0,break ; ;06-Mar-1986/jms ; end of edit $getjpiw_s pidadr=seedpid,- ; get process info itmlst=jpilist cmpw r0,#SS$_NOMOREPROC ; if no more processes, bnequ next ; hiber and start over wait: $hiber_s ; wait errorck movl #WILDCARD,seedpid ; reset wildcard ; to eliminate timestamping of the console, comment out ; the following lines: ; $fao_s ctrstr=time_control,- ; get time of day ; outbuf=fao_buffer,- ; outlen=fao_length ; errorck ; ; $brkthru_s msgbuf=fao_length,- ; and send it to the console ; sendto=console_terminal,- ; sndtyp=#BRK$C_DEVICE,- ; reqid=#BRK$C_SHUTDOWN ; errorck ; to eliminate timestamping of the console, comment out ; the previous lines: jmp get ; and go back next: addl2 current_dirio,current_bufio ; add i/o counts together ;; movzwl current_index,R6 ; get index into R6 incl warncount[R6] ; increment warnings cmpl pids[R6],current_pid ; is this a different process? beqlu check_grp ; if not, check for stuff clrl cputime[R6] ; if so, clear out warning clrl warncount[R6] ; and CPU counters clrl ios[R6] ; and io counters, but save ;; movl current_pid,pids[R6] ; PID to check on next jmp get ; iteration. check_grp: ; if group is LE MAX_SYSGRP, cmpl current_grp,#MAX_SYSGRP ; process is safe from being bgtru check_prccnt ; killed clrl warncount[R6] check_prccnt: ; Processes that have tstl current_prccnt ; subprocesses are safe from blequ check_tty ; being killed. clrl warncount[R6] check_tty: ; If you don't have a terminal, tstl current_terminal_l ; you can't be killed. bgtru check_window clrl warncount[R6] ;+++ ; Added this code so that DECwindows jobs will work OK. ; You can comment out the following four lines if you ; want to kill off DECwindows jobs too. ;--- check_window: cmpw current_terminal,window_terminal; is this a window terminal? bnequ check_cpu ; if it is, you're OK clrl warncount[R6] ; jms/890610 check_cpu: cmpl current_bufio,ios[R6] ; compare i/o counts ;; bnequ check_cpu_ok ; if not equal, user OK ;; subl #MIN_TIME,current_cpu ; if you use MIN_TIME CPU cmpl current_cpu,cputime[R6] ; ticks every round, on blequ check_to_warn ; the average, you're OK. check_cpu_ok: clrl warncount[R6] ; clear out the warnings movl current_bufio,ios[R6] ; and save the io counts ;; movl current_cpu,cputime[R6] ; and the CPU for addl #MIN_TIME,cputime[R6] ; comparison next time. check_to_warn: cmpl warncount[R6],#WARN_LIMIT beqlu warn_him ; if equal, warn him jmp check_to_kill ; if not, check to see if kill warn_him: tstl current_terminal_l ; is there a tty to beqlu warn_him_no_tty ; send a message to? $fao_s ctrstr=user_control_w,- ; make warning message outbuf=fao_buffer,- outlen=fao_length,- p1=#0 errorck $brkthru_s msgbuf=fao_length,- ; send message to user sendto=current_terminal_l,- sndtyp=#BRK$C_DEVICE,- flags=#BRK$M_SCREEN!4,- reqid=#BRK$C_SHUTDOWN errorck warn_him_no_tty: ; to eliminate "warned him" messages on the console, ; comment out the following lines: ; $fao_s ctrstr=cons_control_w,- ; make warning message ; outbuf=fao_buffer,- ; outlen=fao_length,- ; p1=#0,- ; p2=current_pid,- ; p3=#current_username_l ; errorck ; $brkthru_s msgbuf=fao_length,- ; and send to console ; sendto=console_terminal,- ; sndtyp=#BRK$C_DEVICE,- ; reqid=#BRK$C_SHUTDOWN ; errorck ; to eliminate "warned him" messages on the console, ; comment out the previous lines: check_to_kill: cmpl warncount[R6],#KILL_LIMIT ; if equal, goto beqlu kill_him ; kill him, if not jmp get ; equal, goto next user kill_him: tstl current_terminal_l ; is there a tty to beqlu kill_him_no_tty ; send a message to? $fao_s ctrstr=user_control_k,- ; make killed msg outbuf=fao_buffer,- outlen=fao_length,- p1=#0,- p2=#current_username_l errorck $brkthru_s msgbuf=fao_length,- sendto=current_terminal_l,- sndtyp=#BRK$C_DEVICE,- flags=#BRK$M_SCREEN!24,- reqid=#BRK$C_SHUTDOWN errorck kill_him_no_tty: $fao_s ctrstr=cons_control_k,- ; make killed msg outbuf=fao_buffer,- outlen=fao_length,- p1=#0,- p2=current_pid,- p3=#current_username_l errorck ; $brkthru_s msgbuf=fao_length,- ; and send to console ; sendto=console_terminal,- ; sndtyp=#BRK$C_DEVICE,- ; reqid=#BRK$C_SHUTDOWN ; errorck ; as Frank Nagy says, "Kill the wabbit, kill the wabbit." ; If you don't trust my code (which is OK with me), comment ; out the following two lines and watch the messages until ; you feel comfortable. $delprc_s pidadr=current_pid errorck goto_get: jmp get .end killer