Last User Login Script

I am trying to write a script that will tell when a user on a network of 90 computers last logged in. Each Computer has its own wtmp file that holds last login information. The password file for the network is stored on a central computer and all the computers use NIS and NFS for file sharing. I want the script to go to each computer on the network (I have a list of computers it will read from called computer.list) and have it go in and tell me when the user last logged in on the network.
Who is Participating?
leendertConnect With a Mentor Commented:
Hi there,

Ok here is what I did. I wrote two perl programs. A client which
must be installed on the target computer and a "querylu" (Query
Last User) script that sits on your computer.

What you need to do is to install the client script on the target
computer and to create an entry in the /etc/services and
/etc/inetd files.

Here are two examples of such entries :

/etc/services file :
lastuser         5610/tcp

/etc/inetd.conf file :
lastuser    stream  tcp nowait  root /usr/lenny/bin/script/lastuser lastuser

Note : the above line must be on one line. it wrapped while I
typed it in. Modify the path to the script to suit your needs.

After you modified these files you will have to "kill -1" the
inetd process. So do a "ps -ef/-aux | grep inetd" and kill it
with a -1 (HUP) signal. This will force inetd to reload it's

The source of the lastuser script (client side) :


open (PWFile, "/etc/passwd");
@PWLines = <PWFile>;
close (PWFile);

foreach $PWLine (@PWLines) {
    chop ($PWLine);
    # Check for known user shells to identify valid users
    if ( ($PWLine =~ /ksh/) || ($PWLine =~ /tcsh/) ||
           ($PWLine =~ /csh/) || ($PWLine =~ /zsh/) ) {
        # This means we have a valid user.
        @Tok = split (/:/, $PWLine);
        $UserName = $Tok[0];
        $LastLine = `last | grep $UserName | head -1`;
        if ($LastLine ne "") {
            push (@LastLines, $LastLine);

while ($Input = <STDIN>) {
    if ($Input eq "all") {
        print @LastLines;
    else {
        foreach $Line (@LastLines) {
            if ($Line =~ /$Input/) {
                print $Line;
                `echo $Line > /tmp/dump`;

The source of the querylu (query last user ) script:


use Socket;

if ($#ARGV == -1) {
    printf ("Usage : querylu (all/<username>)\n");
$UserName = $ARGV[0];
open (CompFile, "computer.list") || die "Could not open computer.list file";
close (CompFile);
foreach $Computer (@CompLines) {
    chop ($Computer);

    $IAddr = inet_aton($Computer);
    $PAddr = sockaddr_in($QueryPort, $IAddr);
    $ProtoCol = getprotobyname('tcp');
    socket (SOCK, PF_INET, SOCK_STREAM, $ProtoCol) or die "Socket error : $!";
    connect (SOCK, $PAddr) or die "Connect error : $!";
    $| = 1;
    print SOCK "$UserName\n";
    while ( $Line  = <SOCK> ) {
        printf ("[$Computer]$Line");
    close (SOCK);

Notes :
You can modify the script to read the passwd file from your
central storage.

The query script will read the "computer.list"
file and query the specified users on the machines in the

If you decide to use a different port in the services file,
please modify it in the querylu script as well.

I use these scripts with Perl v 5.004_04 on freeBSD 2.2.6

Usage :

After these scripts are installed, you can query all the users
by typing : "querylu all"

or you can specify a username on the command line.

I hope this will help you. If you have more problems concerning
this, please let me know.

You could certainly do this, but it would be wrong, from both efficiency and security standpoints.  Instead, let me suggest that you have each user run a perl script upon login (via the global login files, depending on shell).

That script will connect with a server running on some designated machine (make sure to set the name of the machine in an environment variable so that it can be easily changed later), and hand off the name of the user and the name of the machine being logged into to the server.

The server script, also, of course, written in perl, will simply store the current time, username and machine name.  You'll probably want to use a berkeley db file for this.

You'll  find an example server and client in the camel.

EnslaverAuthor Commented:
With a network of 1000+ users that would be too tough. And I am not concerned about security. I have already thought this through and found my way to be the best way to do it.
The 14th Annual Expert Award Winners

The results are in! Meet the top members of our 2017 Expert Awards. Congratulations to all who qualified!

can you finger your user at all hosts from computer.list?
Then it could be done.
EnslaverAuthor Commented:
Nope, fingerd only accepts requests from localhost.
Would be a hard job then ;-|

If fingerd is somehow secure, I assume that wtmp is not accesable from network too.

So you either must export your wtmp to a public (network-) accessable directory -not very clever-, or use a solution as b2pi suggested -you rejected-, or you do it similar as b2pi as follows:

each login writes to a public accessable file a notification line
a program reads this file at each login to get requiered information (you don't need a client/server solution for this)
EnslaverAuthor Commented:
Ok well my goal is to find out who to date hasnt logged in in 3 months. Not find out 3 months from now, and all wtmp's are accessable through nfs because we have each of the computers mounted on /net/computername
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.