0

I am trying to come up with a bash script to check if a user idle time is more than 30 minutes then kill the session but I am not able to come up with the right filter.

who -u | cut -c 1-10,38-50 > /tmp/idle$$

for idleSession in `cat /tmp/idle$$ | awk '{print $3}'`
do

    if [ "$idleSession" -gt 30 ]; then
       echo  $idleSession
    fi
done

I have found suggestions with egrep but I don't understand that. I keep getting

user_test.sh: line 6: [: 14:25: integer expression expected

Update: I updated the code with the typo and I got everything printed and value is not getting compared to my limit of 30m

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
user_dev
  • 1,357
  • 3
  • 20
  • 46

1 Answers1

1

This Shellshock-clean code prints details of sessions on the current machine that have been idle for more than 30 minutes:

#! /bin/bash -p

# Check if an idle time string output by 'who -u' represents a long idle time
# (more than 30 minutes)
function is_long_idle_time
{
    local -r idle_time=$1

    [[ $idle_time == old ]] && return 0
    [[ $idle_time == *:* ]] || return 1

    local -r hh=${idle_time%:*}
    local -r mm=${idle_time#*:}
    local -r idle_minutes=$((60*10#$hh + 10#$mm))

    (( idle_minutes > 30 )) && return 0 || return 1
}

who_output=$(LC_ALL=C who -u)

while read -r user tty _ _ _ idle_time pid _ ; do
    if is_long_idle_time "$idle_time" ; then
        printf 'user=%s, tty=%s, idle_time=%s, pid=%s\n' \
            "$user" "$tty" "$idle_time" "$pid"
    fi
done <<<"$who_output"

The code assumes that the output of LC_ALL=C who -H -u looks like:

NAME     LINE         TIME         IDLE          PID COMMENT
username pts/9        Apr 25 18:42 06:44        3366 (:0)
username pts/10       Apr 25 18:42  old         3366 (:0)
username pts/11       Apr 25 18:44   .          3366 (:0)
username pts/12       Apr 25 18:44 00:25        3366 (:0)
...

It may look different on your system, in which case the code might need to be modified.

  • The "idle" string output by who -u can take several different forms. See who (The Open Group Base Specifications Issue 7) for details. Processing it is not completely trivial and is done by a function, is_long_idle_time, to keep the main code simpler.
  • The function extracts the hours (hh (06)) and minutes (mm (44)) from idle strings like '06:44' and calculates a total number of idle minutes (idle_minutes (404)). The base qualifiers (10#) in the arithmetic expression are necessary to prevent strings '08' and '09' being treated as invalid octal numbers. See Value too great for base (error token is "08").
  • The format of the who -u output can (and does) differ according to the Locale. Running it with LC_ALL=C who -u ensures that it will generate the same output regardless of the user's environment. See Explain the effects of export LANG, LC_CTYPE, LC_ALL.
  • Within the main loop you get the username, terminal/line, idle time, and PID of all sessions that have been idle for more than 30 minutes. However, it may not be straightforward to use this information to kill idle sessions. On some systems, multiple sessions may be associated with the same PID. Even if you can reliably determine the PIDs of idle sessions, the idleness may be false. For instance, a session that is running a long-running program that has generated no terminal output (yet) will appear to be idle. Killing it might not be a smart thing to do though. Consider using TMOUT instead. See How can one time out a root shell after a certain period of time? (and note that it can be used for any user, not just root).
pjh
  • 6,388
  • 2
  • 16
  • 17
  • This is in reference to your last point. How do I track those long running processes when it won't send any terminal activity and instead will increase the idle time. Timing out in that case may be catastrophic as it may lead to killing a program which might user wouldn't want to. – user_dev May 08 '19 at 17:05
  • @user_dev, one possibility is to check if the process that you are considering timing out has any child processes. See [How to get child process from parent process](https://stackoverflow.com/q/17743879/4154375). Another possibility is to check for other processes that are associated with the same terminal (e.g. using the `-t` option to `pgrep`). – pjh May 08 '19 at 17:55