Contents:
Getting Password and Group Information
Packing and Unpacking Binary Data
Getting Network Information
Exercise
The information that the UNIX system keeps about your username and user ID is fairly public. In fact, nearly everything but your unencrypted password is available for perusal by any program that cares to scan the /etc/passwd file. This file has a particular format, defined in passwd (5), which looks something like this:
name:passwd:uid:gid:gcos:dir:shell
The fields are defined as follows:
name
The login name of the user
passwd
The encrypted password, or something simple if a shadow password file is being used
uid
The user ID number (0 for
root
, nonzero for normal users)
gid
The default login group (group 0 may be privileged, but not necessarily)
gcos
The GCOS field, which typically contains the user's full name followed by a comma and some other information
dir
The home directory (where you go when you type cd without any arguments and where most of your "dot-files" are kept)
shell
Your login shell, typically /bin/sh or /bin/csh (or maybe even /usr/bin/perl , if you're crazy)
A typical portion of the password file looks like this:
fred:*:123:15:Fred Flintstone,,,:/home/fred:/bin/csh barney:*:125:15:Barney Rubble,,,:/home/barney:/bin/csh
Now, Perl has enough tools to parse this kind of line easily (using
split
, for example), without drawing on special purpose routines. But the UNIX programing library does have a set of special routines:
getpwent
(3),
getpwuid
(3),
getpwnam
(3), and so on. These routines are available in Perl using the same names and similar arguments and return values.
For example, the
getpwnam
routine becomes the Perl
getpwnam
function. The single argument is a username (like
fred
or
barney
), and the return value is the
/etc/passwd
line split apart into a list with the following values:
($name, $passwd, $uid, $gid, $quota, $comment, $gcos, $dir, $shell)
Note that there are few more values here than in the password file. For every UNIX system we've seen, the
$quota
field is always empty, and the
$comment
and the
$gcos
field often both contain the entire GCOS field. So, for good old
fred
, you get
("fred", "*", 123, 15, "", "Fred Flintstone,,,", "Fred Flintstone,,,", "/home/fred"," /bin/csh")
by invoking either of the following two calls:
getpwuid(123) getpwnam("fred")
Note that
getpwuid
takes a UID number, while
getpwnam
takes the login name as its argument.
The
getpwnam
and
getpwuid
functions also have a return value when called in a scalar sense. They each return the thing you've asked them to get. For example:
$idnum = getpwuid("daemon"); $login = getpwnam(25);
You'll probably want to pick this apart, using some of the list operations that we've seen before. One way is to grab a part of the list using a list slice, such as getting just the home directory for Fred using:
($fred_home) = (getpwnam ("fred"))[7]; # put Fred's home
How would you scan through the entire password file? Well, you could do something like this:
for($id = 0; $id <= 10_000; $id++) { @stuff = getpwuid $id; } ### not recommended!
But this is probably the wrong way to go. Just because there's more than one way to do it doesn't mean that all ways are equally cool.
You can think of the
getpwuid
and
getpwnam
functions as
random access
; they grab a specific entry by key, so you have to have a key to start with. Another way of accessing the password file is
sequential access
- grabbing each entry one at a time.
The sequential access routines for the password file are the
setpwent
,
getpwent
, and
endpwent
functions. Together, these three functions perform a sequential pass over all values in the password file. The
setpwent
function initializes the scan at the beginning. After initialization, each call to
getpwent
returns the next entry from the password file. When there is no more data to process,
getpwent
returns an
empty list. Finally, calling
endpwent
frees the resources used by the scanner; this is performed automatically upon exiting the program as well.
This description begs for an example, so here's one now:
setpwent(); # initialize the scan while (@list = getpwent()) { # fetch the next entry ($login,$home) = @list[0,7]; # grab login name and home print "Home directory for $login is $home\n"; # say so } endpwent(); # all done
This example shows the
home directory of everyone in the password file. Suppose you wanted them alphabetically by home directory? We learned about
sort
in the previous chapter, so let's use it:
setpwent(); # initialize the scan while (@list = getpwent()) { # fetch the next entry ($login,$home) = @list[0,7]; # grab login name and home $home{$login} = $home; # save it away } endpwent(); # all done @keys = sort { $home{$a} cmp $home{$b} } keys %home; foreach $login (@keys) { # step through the sorted names print "home of $login is $home{$login}\n"; }
This fragment, while a little longer, illustrates an important thing about scanning sequentially through the password file; you can save away the pertinent portions of the data in data structures of your choice. The first part of the example scans through the entire password file, creating a hash where the key is the login name and the value is the corresponding home directory for that login name. The
sort
line takes the keys of the hash and sorts them according to string value. The final loop steps through the sorted keys, printing each value in turn.
Generally, you should use the random access routines (
getpwuid
and
getpwnam
) when you are looking up just a few values. For more than a few values, or even an exhaustive search, it's generally easier to do a sequential access pass (using
setpwent
,
getpwent
, and
endpwent
) and extract the particular values you'll be looking for into a hash.[
1
]
[1] If you're on a site with a large NIS map, you probably do not want to preprocess the password file this way for performance reasons.
The
/etc/group
file is accessed in a similar way. Sequential access is provided with the
setgrent
,
getgrent
, and
endgrent
calls. The
getgrent
call returns values of the form:
($name, $passwd, $gid, $members)
These four values correspond roughly to the four fields of the
/etc/group
file, so see the descriptions in the manpages about this file format for details. The corresponding random access functions are
getgrgid
(by group ID) and
getgrnam
(by group name).