Book Home Perl for System AdministrationSearch this book

6.2. Finger: A Simple Directory Service

Finger and WHOIS are good examples of simple directory services. Finger exists primarily to provide read-only information about the users of a machine (although we'll see some more creative uses shortly). Later versions of Finger, like the GNU Finger server and its derivatives, expanded upon this basic functionality by allowing you to query one machine and receive information back from all of the machines on your network.

Finger was one of the first widely deployed directory services. Once upon a time, if you wanted to locate a user's email address at another site, or even within your own, the finger command was the best option. finger harry@hogwarts.edu would tell you whether Harry's email address was harry, hpotter, or something more obscure (along with listing all of the other Harrys at that school). Though it is still in use today, Finger's popularity has waned over time as web home pages became prevalent and the practice of freely giving out user information became problematic.

Using the Finger protocol from Perl provides another good example of TMTOWTDI. When I first looked on CPAN for something to perform Finger operations, there were no modules available for this task. If you look now, you'll find Dennis Taylor's Net::Finger module, which he published six months or so after my initial search. We'll see how to use it in a moment, but in the meantime, let's pretend it doesn't exist and take advantage of this opportunity to learn how to use a more generic module to talk a specific protocol when the "perfect" module doesn't exist.

The Finger protocol itself is a very simple TCP/IP-based text protocol. Defined in RFC1288, it calls for a standard TCP connect to port 79. The client passes a simple CRLF-terminated[1] string over the connection. This string either requests specific user information or, if empty, asks for information about all users of that machine. The server responds with the requested data and closes the connection at the end of the data stream. You can see this in action by telnet ing to the Finger port directly on a remote machine:

[1]Carriage return + linefeed, i.e., ASCII 13 + ASCII 10.

$ telnet kantine.diku.dk 79
Trying 192.38.109.142 ...
Connected to kantine.diku.dk.
Escape character is '^]'.
cola<CR><LF>
Login: cola                             Name: RHS Linux User
Directory: /home/cola                   Shell: /bin/noshell
Never logged in.
No mail.
Plan:

Current state of the coke machine at DIKU
This file is updated every 5 seconds
At the moment, it's necessary to use correct change. 
This has been the case the last 19 hours and 17 minutes

Column 1 is currently *empty*.
   It's been 14 hours and 59 minutes since it became empty.
   31 items were sold from this column before it became empty.
Column 2 contains some cokes.
   It's been 2 days, 17 hours, and 43 minutes since it was filled.
   Meanwhile, 30 items have been sold from this column.
Column 3 contains some cokes.
   It's been 2 days, 17 hours, and 41 minutes since it was filled.
   Meanwhile, 11 items have been sold from this column.
Column 4 contains some cokes.
   It's been 5 days, 15 hours, and 28 minutes since it was filled.
   Meanwhile, 26 items have been sold from this column.
Column 5 contains some cokes.
   It's been 5 days, 15 hours, and 29 minutes since it was filled.
   Meanwhile, 18 items have been sold from this column.
Column 6 contains some coke-lights.
   It's been 5 days, 15 hours, and 30 minutes since it was filled.
   Meanwhile, 16 items have been sold from this column.

Connection closed by foreign host.
$

In this example we've connected directly to kantine.diku.dk's Finger port. We typed the user name "cola," and the server returned information about that user.

I chose this particular host and user just to show you some of the whimsy that accompanied the early days of the Internet. Finger servers got pressed into service for all sorts of tasks. In this case, anyone anywhere on the planet can see whether the soda machine at the Department of Computer Science at the University of Copenhagen is currently stocked. For more examples of strange devices hooked to Finger servers, you may wish to check out Bennet Yee's "Internet Accessible Coke Machines" and "Internet Accessible Machines" pages; they are available online at http://www.cs.ucsd.edu/~bsy/fun.html.

Let's take the network communication we just performed using a telnet binary back to the world of Perl. With Perl, we can also open up a network socket and communicate over it. Instead of using lower-level socket commands, we'll use Jay Roger's Net::Telnet module to introduce a family of modules that handle generic network discussions. Other modules in this family (some of which we use in other chapters) include Eric Arnold's Comm.pl, Austin Schutz's Expect.pm, and the venerable but outdated and nonportable chat2.pl by Randal L. Schwartz.

Net::Telnet will handle all of the connection setup work for us and provides a clean interface for sending and receiving data over this connection. Though we won't use them in this example, Net::Telnet also provides some handy pattern-scanning mechanisms that allow your program to watch for specific responses from the other server.

Here's a Net::Telnet version of a simple Finger client. This code takes an argument of the form user@finger_server. If the user name is omitted, a list of all users considered active by the server will be returned. If the hostname is omitted, we query the local host:

use Net::Telnet;

($username,$host) = split(/\@/,$ARGV[0]);
$host = $host ? $host : 'localhost';

# create a new connection
$cn = new Net::Telnet(Host => $host,
                      Port => 'finger');

# send the username down this connection
unless ($cn->print("$username")){ # could be "/W $username"
    $cn->close;              
    die "Unable to send finger string: ".$cn->errmg."\n";
}

# grab all of the data we receive, stopping when the 
# connection is dropped
while (defined $ret = $cn->get) {
    $data .= $ret;
}

# close the connection
$cn->close;                  

# display the data we collected
print $data;

RFC1288 specifies that a /W switch can be prepended to the username sent to the server to request it to provide "a higher level of verbosity in the user information output," hence the /W comment above.

If you need to connect to another TCP-based text protocol besides Finger, you'd use very similar code. For example, to connect to a Daytime server (which shows the local time on a machine) the code looks very similar:

use Net::Telnet;

$host = $ARGV[0] ? $ARGV[0] : 'localhost';

$cn = new Net::Telnet(Host => $host, 
                      Port => 'daytime');

while (defined $ret = $cn->get) {
    $data .= $ret;
}
$cn->close;                  

print $data;

Now you have a sense of how easy it is to create generic TCP-based network clients. If someone has taken the time to write a module specifically designed to handle a protocol, it can be even easier. In the case of Finger, you can use Taylor's Net::Finger to turn the whole task into a single function call:

use Net::Finger;

# finger(  ) takes a user@host string and returns the data received
print finger($ARGV[0]);

Just to present all of the options, there's also the fallback position of calling another executable (if it exists on the machine) like so:

($username,$host) = split('@',$ARGV[0]);
$host = $host ? $host : 'localhost';

# location of finger executable, MacOS users can't use this method
$fingerex = ($^O eq "MSWin32") ? 
                 $ENV{'SYSTEMROOT'}."\\System32\\finger" :
                 "/usr/ucb/finger";  # (could also be /usr/bin/finger)

print `$fingerex ${username}\@${host}`

Now you've seen three different methods for performing Finger requests. The third method is probably the least ideal because it requires spawning another process. Net::Finger will handle simple Finger requests; for everything else, Net::Telnet or any of its kin should work well for you.



Library Navigation Links

Copyright © 2001 O'Reilly & Associates. All rights reserved.