#!/usr/bin/env perl

# Usage: idle [tmax]

=head1 NAME

idle - display idle times

=head1 SYNOPSIS

idle [tmax]

=head1 DESCRIPTION

B<idle> displays idle times for miscellaneous devices (B<kbd>, B<mouse>,
B<tty>I<n>, B<tty[p-z]>I<n>, B<vt>I<n> and B<pts/>I<n>). The format is:

C<device  ddd+hh:mm:ss  (user)>

where I<ddd> is the number of days, I<hh> is the number of hours,
I<mm> is the number of minutes and I<ss> is the number of seconds.
If I<ddd> is 0, it is not displayed.

An argument can be given. It is the maximum number of seconds for which
the idle time is displayed.

=head1 NOTE

B<idle> was originally written for Solaris, then updated to work under
Linux. It may work on other systems, perhaps not completely.

It has been tested on some machines (but the results may change from
one configuration to another, these are only examples):

=over 4

=item *

Solaris: B<kbd> = keyboard, B<mouse> = mouse, B<pts>I<n> = text terminals.

=item *

Linux/x86: B<mouse> = mouse (no longer seems to work), B<tty1> to B<tty6>
= Linux consoles, B<tty7> = X server, B<ttyp>I<n> = text terminals,
B<pts/>I<n> = ssh connections.

=item *

Linux/PowerPC: B<tty1> to B<tty6> = Linux consoles, B<tty7> = X server,
B<ttyp>I<n> = some text terminals, B<pts/>I<n> = some text terminals
and ssh connections.

=item *

OSF: B<ttyp>I<n> and B<pts/>I<n> = text terminals, but B<ttyp>I<n>
and B<pts/>I<n> give the same results, so you may want to filter
one of them.

=item *

Darwin (Mac OS X): B<tty[p-z]>I<n> = text terminals and ssh connections.

=back

=head1 AUTHOR

Vincent Lefevre <vincent@vinc17.org>. Mail comments and bug reports about
B<idle> to <idle@vinc17.org>.

=head1 AVAILABILITY

The latest version is available at <http://www.vinc17.org/unix/>.

=cut

########################################################################

use integer;
use strict;

# $Id: idle 11969 2006-04-23 09:40:47Z lefevre $

my ($tmax,%t,%u);

$ARGV[0] =~ /^\d+$/ and $tmax = $ARGV[0];

my $len = 5;
foreach ('kbd', 'mouse')
  {
    my @s = stat "/dev/$_" or next;
    $t{$_} = $s[8];
    $u{$_} = $s[4];
  }

opendir DEV, '/dev' or die "idle: can't open directory /dev\n";
while (defined($_ = readdir DEV))
  {
    /^(tty[p-z]?|vt)\d+$/ or next;
    my @s = stat "/dev/$_" or next;
    $s[4] or next;
    $t{$_} = $s[8];
    $u{$_} = $s[4];
  }
closedir DEV or die "idle: can't close directory /dev\n";

my $pts = '/dev/pts';
if (opendir PTS, $pts)
  {
    while (defined($_ = readdir PTS))
      {
        /^\d+$/ or next;
        my @s = stat "$pts/$_" or next;
        $s[5] or next;
        $t{"pts/$_"} = $s[8];
        $u{"pts/$_"} = $s[4];
      }
    closedir PTS or die "idle: can't close directory $pts\n";
  }

my $time = time;

foreach (sort { $t{$b} <=> $t{$a} } keys %t)
  {
    my $t = $time - $t{$_};
    defined $tmax && $t > $tmax and next;
    printf "%-8s %12s  (%s)\n", $_, &disptime($t), &user($u{$_});
  }

sub disptime
  {
    my $t = $_[0];
    my $s = $t % 60; $t /= 60;
    my $m = $t % 60; $t /= 60;
    my $h = $t % 24; $t /= 24;
    sprintf "%s%02d:%02d:%02d", ($t ? "$t+" : ""), $h, $m, $s
  }

sub user
  {
    my $u = (getpwuid $_[0])[0];
    return $u ne '' ? $u : $_[0];
  }