http://qs321.pair.com?node_id=74680
Category: NT Admin
Author/Contact Info idnopheq
Description: who - who is on the system

The who utility can list the user's name and login time for each current system user.

#!/usr/local/bin/perl -w
#-*-perl-*-
#

use strict;
use Win32::NetAdmin;
use Win32API::Net;
use vars qw ( @WhoList %UserInfo $UserName %Option );
use Getopt::Std;
use Win32::EventLog;

my ($VERSION) = '$Revision: 1.0 $' =~ /([.\d]+)/;

my $warnings = 0;

# Print a usuage message on a unknown option.

$SIG {__WARN__} = sub {
    if (substr ($_ [0], 0, 14) eq "Unknown option") {die "Usage"};
    require File::Basename;
    $0 = File::Basename::basename ($0);
    $warnings = 1;
    warn "$0: @_";
};

$SIG {__DIE__} = sub {
    require File::Basename;
    $0 = File::Basename::basename ($0);
    if (substr ($_ [0], 0,  5) eq "Usage") {
        die <<EOF;
$0 (NT Perl bin utils) $VERSION
$0 [ -H | -b | -B ] [ -h ]
EOF
    }
    die "$0: @_";
};

# Get the options.

getopts ( 
     'HhbB',
      \%Option 
    );                    # -h, -H, -B & -b take no option

die "Usage" if ( $Option{'h'} || scalar keys %Option > 1 );

my $Server = "";
my $Level = "11";
my @Month = (
        "Jan",
        "Feb",
        "Mar",
        "Apr",
        "May",
        "Jun",
        "Jul",
        "Aug",
        "Sep",
        "Oct",
        "Nov",
        "Dec"
        );

LastReboot()  if $Option{'b'};
EventID6005() if $Option{'B'};

Win32::NetAdmin::LoggedOnUsers (
                $Server, 
                \@WhoList
                   ) 
  or die "$^E\n";

printf "%s%30s\n", "USER", "LOGIN-TIME" if $Option{'H'};

foreach $UserName ( @WhoList ) {
    UserGetInfo();
}

sub UserGetInfo {
    my $LastLogon;
    my $Length;
    my $UserNameLength = length $UserName;
    if ( Win32API::Net::UserGetInfo (
                     $Server,
                     $UserName,
                     $Level,
                     \%UserInfo
                    ) ) {
    my (
        $sec,
        $min,
        $hour,
        $mday,
        $mon,
        $year,
        $wday,
        $yday,
        $isdst
       ) = localtime ( $UserInfo{lastLogon} );
    $Length = 27 - $UserNameLength;
    printf "%s%${Length}s %2d %2d:%2d\n",
         $UserName,
         $Month[$mon],
         $mday,
         $hour,
         $min;
    }
    else {
    $Length = 31 - $UserNameLength;
    printf "%s%${Length}s\n", $UserName, "unknown";
    }
}

sub LastReboot {
    my $Reboot = time - ( Win32::GetTickCount() / 1000 );
    my (
    $sec,
    $min,
    $hour,
    $mday,
    $mon,
    $year,
    $wday,
    $yday,
    $isdst
       ) = localtime ( $Reboot );
    printf "%22s %4s %2d %2d:%2d\n", "system boot", $Month[$mon], $mda
+y, $hour, $min;
    exit;
}

sub EventID6005 {
    my (
    $EventLog, 
    $First, 
    $Count, 
    $Event,
    %Data
       );
    Win32::EventLog::Open($EventLog , "System", "") 
    or die ("EventLog Open() failed");
    $EventLog->GetOldest($First)
      or die ("EventLog GetOldest() failed");
    $EventLog->GetNumber($Count) 
      or die ("EventLog GetNumber() failed");

    $EventLog->Read (
             (EVENTLOG_SEEK_READ | EVENTLOG_BACKWARDS_READ),
             $First+$Count,
             $Event
            );
    
    for my $i (0 .. $First+$Count-1) {
    $EventLog->Read (
             (EVENTLOG_SEQUENTIAL_READ|EVENTLOG_BACKWARDS_READ),
             0,
             $Event
            )
      or die ("EventLog Read() failed at event $i");
    
    %Data = %{$Event};
    $Data{"EventID"} = $Data{"EventID"} & 0xffff;
    
    next unless $Data{"EventID"} == 6005;
    my (
        $sec,
        $min,
        $hour,
        $mday,
        $mon,
        $year,
        $wday,
        $yday,
        $isdst
       ) = localtime ( $Data{"TimeGenerated"} );
    printf "%22s %4s %2d %2d:%2d\n", "system boot", $Month[$mon], $mda
+y, $hour, $min;
    exit;
    }
}

=pod

=head1 NAME

B<who> - who is on the system

=head1 SYNOPSIS

B<who> [ -H | -b | -B ]

=head1 DESCRIPTION

The who utility can list the user's name and login time for each curre
+nt system user.

The general format for output is:

name time

where:

=over

=item name

user's login name.

=item time

time since user's login.

=back

=head2 OPTIONS

The following options are supported:

=over 4

=item -b

Indicate the approximate time and date of the last reboot.

=item -B

Indicate a more exact but slower to acquire time and date of the last 
+reboot.

=item -H

Output column headings above the regular output.

=item -h

Display syntax.

=back

=head1 EXAMPLE

Below is an example of the output B<who> provides without options:

C:\> who
shoehorn                unknown
Administrator           Jun 22  9:39

Below is an example of the output of B<who -H>:

C:\> who -H
USER                    LOGIN-TIME
shoehorn                unknown
Administrator           Jun 22  9:39

Below is an example of the output of B<who -b> and B<who -B>:

C:\> who -b
           system boot  Jun 21 15:29

=head1 ENVIRONMENT

The working of B<who> is not influenced by any environment variables.

=head1 BUGS

B<who> isn't as nice as I would like, but Win32 isn't Unix, now is it?
+  This I<really> doesn't like Samba domain controllers, which is whi 
+I added the 'unknown' entry in the output.

The B<-b> option returns an approximate uptime.  It uses the Win32::Ge
+tTickCount() function, an imprecise mechanism.  A better was to derri
+ve the last boot is to query the event log for the most recent 6005 o
+r 6009 event and grab that time.  If log files are large, this can be
+ time consuming.

B<printf> does not seem to want to pad numbers with '0's.

=head1 STANDARDS

It does not make sense to talk about standards in a B<who> manual page
+.

=head1 REVISION HISTORY

    who
    Revision 1.0  2000/06/22 07:14:57  idnopheq
    Initial revision

=head1 AUTHOR

The Perl implementation of B<who> was written by Dexter Coffin, I<idno
+pheq@home.com>.

=head1 COPYRIGHT and LICENSE

This program is copyright by Dexter Coffin 2000.

This program is free and open software. You may use, copy, modify, dis
+tribute,
and sell this program (and any modified variants) in any way you wish,
provided you do not restrict others from doing the same.

=head1 SEE ALSO

=for html
<a href="uptime.html">uptime</a>, <a href="users.html">users</a><p>

=head1 NEXT TOPIC

=cut