Copyright 1992-2016 by Kevin G. Barkes All rights reserved. This article may be duplicated or redistributed provided no alterations of any kind are made to this file. This edition of DCL Dialogue is sponsored by Networking Dynamics, developers and marketers of productivity software for OpenVMS systems. Contact our website www.networkingdynamics.com to download free demos of our software and see how you will save time, money and raise productivity! Be sure to mention DCL Dialogue! DCL DIALOGUE Originally published January, 1992 DCL Chainsaw Massacre By Kevin G. Barkes There's an old saying "the cobbler's children go without shoes", a quaint reminder that those who provide services to others often neglect their own needs. It may come as a surprise to you, gentle reader, but I do not spend countless hours endlessly refining the scores of DCL command procedures which litter my system. My current DECW$LOGIN.COM file, for example, has a date of 26-DEC-1990. That's when I commented out the little program which displays a Christmas tree and animated electric train in a window on my VAXstation. I didn't bother with it this past holiday. (Okay, so I'm a grinch. At least I kept the starship icon.) I must be sufficiently motivated to write new DCL procedures or change my operating environment. The former comes monthly, with friendly VAXmail messages from various editorial types who enjoy reminding me of the scores of equally talented but deadline-observing individuals who could write this column. The latter recently occurred when I received a telephone bill of Congressional restaurant proportions. An investigation revealed I had inadvertantly forgotten to log off a client's VAX system. I had been extremely busy that day, fired up a terminal emulator on my laptop, and dialed out on an "emergency backup" phone line which I generally never use. I was distracted, wandered away, and when I returned I shut down the little PC without noticing I was still online. The next month I discovered the connection had been maintained for 22 days, 14 hours, and 55 minutes, which worked out to $651.51. It could have been worse, but the electric utility is less reliable than the phone company: the client VAX finally went down due to a power failure, which severed the link. Thank goodness it was a local call. You may ask how someone could remain on a port for three weeks without being noticed. On my end, the situation was sheer negligence. The dial-in phones on the client end were "seeking" lines which automatically connected callers to the next open number. No one ever got a busy signal. The client's staff was used to seeing me on the system at strange hours and thought nothing of me showing up whenever the occasional SHOW USER command was issued. Determined to avoid a recurrence of such a disaster, I decided to take appropriate action. The modem's built-in timer, which is supposed to automatically disconnect the line after 30 minutes of inactivity, was obviously unreliable. Fortunately, the VAX port into which I had dialed had the proper hardware configuration and SET TERM settings. Logging out or otherwise killing the process connected to the line would break the connection. The site doesn't use an idle process killer program. Frankly, it's unnecessary given the applications in use there and the nature of the environment. What I needed was something that would monitor just my process and kill it if it became inactive. My first crack at the problem was BADHACK.COM (Figure 1). Executed as a "shell" right after LOGIN.COM, it used the READ command's /TIMEOUT qualifier to keep track of idle time and to log me out if I didn't do something within three 100 second periods. I added a few bells and whistles, like "pseudo-recall", necessary since READ doesn't store commands in the process' RECALL buffer. Unfortunately, this attempt wasn't completely successful, since it didn't permit true command line editing and didn't display symbol translations. Its major drawback, though, was that it would not log out the process while a command was being executed. If I ran an editor, for example, I could stay there forever, or until the next power failure. The procedure was also longer than I liked. There had to be a better way. I vaguely recalled a letter I received years ago from a reader who had a rather simple solution to the problem. But I couldn't remember his name or the approach he took, other than it had a rather unsavory name. I never throw anything away; I knew I had my response to his letter buried somewhere in an archived, compressed file on my PC. After spending several hours using Norton's text search command and a rather bizarre collection of search terms ("Killer", "Guillotine", "Noose", etc.), I finally hit paydirt with the string "Chainsaw Massacre". About three years ago, Jim McDowell of Windsor, Ontario, Canada sent me a number of command files. He called one of them his "Chainsaw Massacre" procedure because of its crude but effective approach. I couldn't locate his original procedure, but I created a functional equivalent (Program 2). It's really quite simple. I added the following line to my LOGIN.COM files on all the systems into which I dial in: $ SPAWN/NOKEY/NOLOG/NOLOGICAL- /NOSYMBOL/NOWAIT @SYS$LOGIN:NORMAN_BATES.COM This spawns a subprocess which runs the matricidal command file. We use /NOKEY, /NOLOGICAL, and /NOSYMBOL to speed up the SPAWN; these qualifiers eliminate the system's need to pass the parent process' logical name, symbol, and key definitions on to the child process. The subprocess periodically looks at its parent to see if it's idle. If it is, it issues a STOP command to kill it off; in so doing, it also terminates itself. Of course, the major drawback to this procedure is that it's expensive, in that it requires another process slot to do its job. Applying this approach to all users would be unsound, since you'd be doubling the system's process count. But when you only want to watch one or two users, or monitor specific dial-in lines, it's quite sensible and easy to customize and embellish. Using STOP is also rather crude; as we've discussed in past columns, STOP bypasses user mode exit handlers and can cause problems with certain applications, such as database programs. In my situation, it was acceptable. It also drove home the point that it's unwise to dismiss command procedures simply because they're "inelegant". Sometimes a sledgehammer is the proper tool for the job. Speaking of sledgehammers, I have an "auto-disconnect modem" which requires some fine adjustment. So much for warranties. *************************** Kevin G. Barkes is an independent consultant who never throws away anything, which is really irrelevant since he can never remember where he puts things. He can be found lurking on comp.os.vms, or reached at kgbarkes@gmail.com. ************************** PROGRAM 1: $! BADHACK.COM $ SET NOON $! Initialize "variables" $ TIMECOUNT = 0 $ COMMANDCOUNT = 21 $! Set the goto point $ ERRORJUMP = "TIMELOOP" $! Main processing loop $ TIMELOOP: $! Output a blank line $ WRITE SYS$OUTPUT "" $! Increment the counter $ TIMECOUNT = TIMECOUNT + 1 $! If we've gone through three idle periods, $! then exit... $ IF TIMECOUNT .EQ. 3 THEN ERRORJUMP = "TIMEOUT" $! Otherwise wait for input. $! The F$ENVIRONMENT call displays the current $! default directory. $ READ/TIME=100/ERROR='ERRORJUMP' - SYS$COMMAND RECORD - /PROMPT="''F$ENVIRONMENT("DEFAULT")'> " $! If the RECall command is entered, display the $! contents of the pseudo-recall buffer. $ RECALLTEST = - F$EDIT(F$EXTRACT(0,3,RECORD),"COLLAPSE, UPCASE") $ IF RECALLTEST .EQS. "REC" THEN SHOW SYMBOL X* $ IF RECALLTEST .EQS. "REC" THEN GOTO TIMELOOP $! Decrement the recall buffer. Add a leading 0 $! to the symbol name so the sorted symbol list $! displays properly. $ COMMANDCOUNT = COMMANDCOUNT - 1 $ IF COMMANDCOUNT .EQ. 0 - THEN COMMANDCOUNT = 20 $ IF F$LENGTH(COMMANDCOUNT) .EQ. 1 - THEN COMMANDCOUNT = - "0"+F$STRING(COMMANDCOUNT) $ X'COMMANDCOUNT' = "''RECORD'" $! If it's a real command, then execute it. $ ASSIGN/NOLOG/USER SYS$COMMAND SYS$INPUT $ 'RECORD' $! Reset the timeout counter $ TIMECOUNT = 0 $ GOTO TIMELOOP $! If we've timed out, log out, unless we're in $! a DECterm window. $ TIMEOUT: $ WRITE SYS$OUTPUT "Timeout." $ IF F$EXTRACT(0,2,F$GETJPI("","TERMINAL")) - .NES. "TW" THEN EOJ $ EXIT ************************** PROGRAM 2: $! NORMAN_BATES.COM $! Or, a simple way to dispose of your parent process $ SET NOON $! If we're a batch job, then exit: $ IF F$MODE() .EQS. "BATCH" THEN EXIT $! Get the parent's PID $ DEAR_OLD_MOM = F$GETJPI("","MASTER_PID") $! Record the amount of cpu time used to this point. $ START_TIME_USED = F$GETJPI(DEAR_OLD_MOM,"CPUTIM") $ LOOP: $ WAIT 00:10:00 $! Get the amount of cpu time used $ STOP_TIME_USED = F$GETJPI(DEAR_OLD_MOM,"CPUTIM") $! If less than 100 milliseconds of time used, then $! kill the parent process. This also kills the $! subprocess executing this procedure. $ IF (STOP_TIME_USED - START_TIME_USED) .LT. 100 THEN - STOP/ID='DEAR_OLD_MOM' $! Otherwise, update the cpu time and loop $ START_TIME_USED = STOP_TIME_USED $ GOTO LOOP $ EXIT