Cover V03, I03
Figure 1
Figure 2
Figure 3
Listing 1
Listing 2
Listing 3


Killidle Revisited

Matt Ganis

I found Larry Reznick's article "Timing Out Idle Users" (Nov/Dec 1993 Sys Admin, vol. 2, no. 6, p. 43) extremely interesting and valuable. When I read it, I was in the process of porting some C code to do the same task and was making no progress. The program I was porting did have some limitations that I hoped to overcome later, but I preferred Reznick's approach using a shell/awk script. For my purposes, I decided to update his original work.

My version's new features include:

  • warning users who are about to be forced off the system (the original code killed without warning) and notifying users that the login is being terminated

  • the ability to selectively assign maximum timeout values and warning time values to individual users, such as the system administrator

  • support for idle times greater than 24 hours.

  • The original killidle script scanned the /etc/utmp file using the who(1) command. When it spotted a terminal idle for longer than a predetermined time period, it sent a SIGKILL (-9) signal to the process ID (pid) and logged the action.

    I've modified the algorithm a little to scan the /etc/utmp file using the w(1) command -- more on why later -- for each login. I then look up the amount of idle time allotted for the user, and if the amount of idle time is greater than or equal to the warning time, I send a warning message to the login. If the idle time is greater than or equal to the maximum idle time allocated to the user, I send a message to the login about the impending death, issue the kill via a SIGHUP (-1), which is a safer signal, and log the kill action.

    getparms (Listing 2) is a simple shell script that looks up a user and returns the maximum amount of idle time allotted and the amount of idle time allowed before a warning should be issued. The script simply greps a priv.users file for the login name and returns two fields in the record found: a maximum timeout minutes value and warning minutes value. If the login is not in the file, getparms looks for the keyword "DEFAULT" and returns its values.

    Figure 1 shows a sample priv.users file. In this sample, the user "mrg" gets 999,999 minutes (over 694 days) of idle time before being warned or knocked off the system. The user "erc" gets warnings after 540 minutes (9 hours) and killed after 1,440 minutes (1 day) of idle time. All other users get warnings after 15 minutes and killed after 30 minutes of idle time.

    I chose the w command over the who for two reasons. First, I wanted to support idle times over 24 hours. Comparing Figures 2 and 3, you can see the difference in the idle times. With the who command (Figure 3) idle times greater than 24 hours are indicated by the keyword "old." The w command (Figure 2) shows the number of days the login was idle. Compare, for example, the two entries for pts/2.

    Second, I wanted to warn my users that they've been idle too long and, if the session goes away, tell them why. If you read the man pages for both commands closely, you'll see a subtle difference in the interpretation of the idle field. The w command uses keystrokes to determine how long you've been sitting idle -- if you don't touch the keyboard for 10 minutes you're idle for 10 minutes. The who command, on the other hand, uses screen activity as an indication of idle time. So if a program displays a message on a terminal, that terminal's idle time would reset to 0 minutes even though nothing was typed. It is for this reason that I chose to create the wruser program (Listing 3) to display a message on the terminal. If I had used a conventional approach (i.e., written "some warning" > /dev/pts2"), then it would have been perceived as terminal activity, and the user idle time would be reset to zero. [Editor's note: w is a BSD command. It is included in many versions of UNIX, but not all. If your version doesn't have w, try whodo -l as a substitute.]

    Listing 1 shows the main program, idleout. You'll need to change the variable localpath to the directory where you plan to keep the getparms script and the wruser program (more on wruser later). The first thing idleout does is look up each user in the file priv.users using the getparms script. This lookup sets two idleout variables: warntime and killtime. warntime is the amount of elapsed idle time before warning messages are sent to the user; killtime is the amount of elapsed time before the user is removed from the system.

    Next, idleout takes the idle field from the w command's output and converts it into a numeric value expressed in minutes, idletotal. If the total idle time is greater than the killtime, wruser sends a message to the user's terminal telling of the login's imminent death, the terminal is killed, and a kill log file is updated. If the total idle time is greater than the warntime but less than the killtime, idleout sends a warning message to the terminal.

    The wruser program takes two parameters, the terminal to write to, without the /dev, and the message to be displayed. After recombining the message it searches through /etc/utmp looking for a match for the terminal (ut_line holds the terminal). Once the match is found, the /dev/ is prepended and the process ID is stored. The write() function displays the message, and the pid is both printed and returned.

    This is where it starts to get a little complicated. I needed to execute wruser from idleout's awk script and receive wruser's return value back into the awk script. To accomplish this, I form the command using sprintf, issue the command, and pipe the output to awk's built-in getline function. getline reads the next line of input, in this case the printed output piped from wruser, and puts the result in awk's pid variable.

    idleout is designed to run as a root cron job. It must run with root authority to allow wruser to open the terminal. Currently I run it every 2 minutes with the cron entry:

    0,2,4,6,8,10,12,14,16,18, \\
    20,22,24,26,28,30,32,34,36,38, \\
    40,42,44,46,48,50,52,54,56,58 * * * * (sh /usr/user/idleout)

    One last feature that's not in this version is the ability to dynamically change a user's killtime and warntime. The file priv.users can be updated any time and the next time the script is run, the new file will be used. I'd like to see killtime and warntime change according to what application the user is running. For example, an archie search can take over 20 minutes. With no keyboard activity the session could be terminated due to a perceived idle condition. But that's for a future day.

    About the Author

    Matt Ganis is currently an Advisory programmer for the Advantis corporation, located in White Plains, NY where he works on multi-protocol networking. In his spare time he teaches Astronomy at Pace University in Pleasantville, NY. He can be reached at 14 Udell Ct, Cortlandt Manor, NY 10566 or