This is certainly a case of rtfm, as I've come across many similar posts, but I'm getting lost in trying to find what I'm looking for. I imagine awk or grep will be able to help somehow, I could use some assistance in pointing me in the right direction.

I have a future plan of writing a script with prompts that another user can run for this function, but first I want to get the basic idea to work for myself.

I need to use who -u to display currently logged on users, and then based on the time they are idle for, display their userid, idle time, and pid.

For example,

01sale1  pts/11       2016-10-05 06:39 00:04       14132 (192.168...)
01sale2  pts/16       2016-10-05 06:40 00:07       17902 (192.168...)
01sale3  pts/14       2016-10-05 06:40   .         17779 (192.168...)
01sale5  pts/25       2016-10-05 06:41 00:04       18339 (192.168...)

The list is quite long when everyone is on, and we are running into issues reaching the limit of licenses for this specific software. Until that is resolved, I need to kick users idle over an hour or so.

What I would like is to run who -u to show me just columns 1, 5 and 6 based on if column 6 is over an hour, so it would appear 01:** or above.

Eventually, I want to make a script that a user without root privileges can run to show them this info, and then kill idle users. I'm not sure if I want this totally automated or not yet though.

I'm running this application on an older Red Hat

  • In case your users login via ssh. It may be better to log them out if their connection is idle (when they are doing nothing for some time). /etc/sshd_config: ClientAliveInterval 3600 and ClientAliveCountMax 0 – rudimeier Oct 5 '16 at 13:17

The idle time is derived from the last access time of the tty device, so you could do:

kill_idle() (
  old_ttys=$(find /dev/pts -type c ! -name ptmx -amin +60 -printf %p,)
  [ "$#" -eq 0 ] || pkill "-$sig" -t "$old_ttys"
kill_idle HUP; sleep 3; kill_idle KILL

To kill all the processes controlled by those inactive ttys.

Now, as rightly pointed out by @rudimeter, /dev/pts devices are not always only used for user terminal sessions, so we may end-up killing processes that use pseudo-terminals for other purposes (like expect, socat or pty using other terminal disciplines).

Things like terminal emulators, getty, screen, sshd register the pseudo-terminals that they spawn in the utmp database which is what who -u queries.

To get the idle terminals from the output of who -u instead, you could do:

  who -u | awk '$5 != "." && $5 !~ /^00/ {printf "%s,", $2}'

Note however that the output of who -u can be tampered with on systems that use utempter to register the pty.

For instance, here using socat to create the pty:

name=$':0)\nnobody pts/2 2016-10-05 00:00 old 123 (:0' socat pty \
  system:'/usr/lib/x86_64-linux-gnu/utempter/utempter add \"$name\"; sleep infinity',nofork &

utempter is a sgid command that adds the entry in utmp. It does validate its input to some extent, as in its stdin has to point to ptmx and the corresponding slave pts must be owned by the caller, but it doesn't validate the host field. Here by specifying a host field with a newline character in it, we're making a fake entry in the output of who -u:

nobody pts/2 2016-10-05 00:00 old 123 (:0)

That means one can make you kill the processes on any pts.

To just list the idle terminals:

who -u | awk '$5 != "." && $5 !~ /^00/'

Or just to list the user, idle time and pid of the controlling process of the terminal:

who -u | awk '
  $5 != "." && $5 !~ /^00/ {printf "%-16s %6s %d\n", $1, $5, $6}'

The w command might give you a more useful output (it's also not vulnerable to the exploit mentioned above):

$ w -sf | awk 'NR > 2 && $3 !~ /[0-9]s?$/'
stephane tty7      50days /usr/bin/lxsession -s LXDE -e LXDE
stephane pts/1      3:07m /bin/zsh
stephane pts/3      1:25m elinks
  • Hm, this finds also pids for ttys which are not shown in who -u. And seems that it often kills pids when they don't exist anymore. That's not good in case such particular pid is re-used already. – rudimeier Oct 5 '16 at 13:27
  • @rudimeter, that's a TOCTOU race (albeit a very small one, now further reduced by using pkill instead of ps+kill, see edit). Not much you can do about it. I agree that it's probably better to only look at ptys that register with utmp (show in who -u) as ptys could be used for something else than user terminal sessions. – Stéphane Chazelas Oct 5 '16 at 13:48
  • I'd like to include this in possible future scripts, but for now, I'd just like to see what is idle before automatically killing it. Thanks though – Mike D Oct 5 '16 at 14:41
  • That is very informative @StéphaneChazelas, I'll have to check back with this as this proposed script grows. Thanks! – Mike D Oct 5 '16 at 15:34
  • @MikeD, I did add the listing of idle terminals in a later edit (see the last line). But again, what you see might have been forged. – Stéphane Chazelas Oct 5 '16 at 15:43

You could try something like this:

who -u | \
awk '$5 ~ /^[0-9][1-9]:.*$|^[1-9][0-9]:.*$/ {printf "%s %s %s\n", $1,$5,$6}'  

awk match for patterns beginning with 0[1-9] || [1-9][0-9].
You could try to match patterns not beginning with 00 if you want.

  • 1
    Good lord, that's wonderful! By the way, I can't count... it should have been column 5, so I changed awk to $2 as you originally had it. I've been trying to find something like this for a while now, thank you very much! :) – Mike D Oct 5 '16 at 13:50
  • 1
    You could avoid tr and cut: who -u | awk '$5 ~ /^[0-9][1-9]:.*$|^[1-9][0-9]:.*$/ {print $1,$5,$6}' – rudimeier Oct 5 '16 at 13:55
  • Ok @Mike_D ... I've made a rollback to my answer ;) – Echoes_86 Oct 5 '16 at 16:47
  • Why would you compact spaces and cut fields exernally, when the next step is Awk??? Don't you realize that the default FS in Awk matches multiple spaces (and trims leading and trailing space) and that you can do $0="$1 $5 $6" to reduce the record to just the needed fields (and have it split again into fields). – Kaz Oct 6 '16 at 12:35
  • 1
    printf("%s %s %s\n", x, y, z) is an anti-pattern in Awk, precisely identical to print x, y, z when OFS and RS have their default values of space and newline, respectively. (By the way, sorry about that! No, you can't do $0="$1 $5 $6"). – Kaz Oct 6 '16 at 12:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.