1package User::pwent; 2 3use 5.006; 4our $VERSION = '1.01'; 5 6use strict; 7use warnings; 8 9use Config; 10use Carp; 11 12our(@EXPORT, @EXPORT_OK, %EXPORT_TAGS); 13our ( $pw_name, $pw_passwd, $pw_uid, $pw_gid, 14 $pw_gecos, $pw_dir, $pw_shell, 15 $pw_expire, $pw_change, $pw_class, 16 $pw_age, 17 $pw_quota, $pw_comment, 18 ); 19BEGIN { 20 use Exporter (); 21 @EXPORT = qw(getpwent getpwuid getpwnam getpw); 22 @EXPORT_OK = qw( 23 pw_has 24 25 $pw_name $pw_passwd $pw_uid $pw_gid 26 $pw_gecos $pw_dir $pw_shell 27 $pw_expire $pw_change $pw_class 28 $pw_age 29 $pw_quota $pw_comment 30 ); 31 %EXPORT_TAGS = ( 32 FIELDS => [ grep(/^\$pw_/, @EXPORT_OK), @EXPORT ], 33 ALL => [ @EXPORT, @EXPORT_OK ], 34 ); 35} 36 37# 38# XXX: these mean somebody hacked this module's source 39# without understanding the underlying assumptions. 40# 41my $IE = "[INTERNAL ERROR]"; 42 43# Class::Struct forbids use of @ISA 44sub import { goto &Exporter::import } 45 46use Class::Struct qw(struct); 47struct 'User::pwent' => [ 48 name => '$', # pwent[0] 49 passwd => '$', # pwent[1] 50 uid => '$', # pwent[2] 51 gid => '$', # pwent[3] 52 53 # you'll only have one/none of these three 54 change => '$', # pwent[4] 55 age => '$', # pwent[4] 56 quota => '$', # pwent[4] 57 58 # you'll only have one/none of these two 59 comment => '$', # pwent[5] 60 class => '$', # pwent[5] 61 62 # you might not have this one 63 gecos => '$', # pwent[6] 64 65 dir => '$', # pwent[7] 66 shell => '$', # pwent[8] 67 68 # you might not have this one 69 expire => '$', # pwent[9] 70 71]; 72 73 74# init our groks hash to be true if the built platform knew how 75# to do each struct pwd field that perl can ever under any circumstances 76# know about. we do not use /^pw_?/, but just the tails. 77sub _feature_init { 78 our %Groks; # whether build system knew how to do this feature 79 for my $feep ( qw{ 80 pwage pwchange pwclass pwcomment 81 pwexpire pwgecos pwpasswd pwquota 82 } 83 ) 84 { 85 my $short = $feep =~ /^pw(.*)/ 86 ? $1 87 : do { 88 # not cluck, as we know we called ourselves, 89 # and a confession is probably imminent anyway 90 warn("$IE $feep is a funny struct pwd field"); 91 $feep; 92 }; 93 94 exists $Config{ "d_" . $feep } 95 || confess("$IE Configure doesn't d_$feep"); 96 $Groks{$short} = defined $Config{ "d_" . $feep }; 97 } 98 # assume that any that are left are always there 99 for my $feep (grep /^\$pw_/s, @EXPORT_OK) { 100 $feep =~ /^\$pw_(.*)/; 101 $Groks{$1} = 1 unless defined $Groks{$1}; 102 } 103} 104 105# With arguments, reports whether one or more fields are all implemented 106# in the build machine's struct pwd pw_*. May be whitespace separated. 107# We do not use /^pw_?/, just the tails. 108# 109# Without arguments, returns the list of fields implemented on build 110# machine, space separated in scalar context. 111# 112# Takes exception to being asked whether this machine's struct pwd has 113# a field that Perl never knows how to provide under any circumstances. 114# If the module does this idiocy to itself, the explosion is noisier. 115# 116sub pw_has { 117 our %Groks; # whether build system knew how to do this feature 118 my $cando = 1; 119 my $sploder = caller() ne __PACKAGE__ 120 ? \&croak 121 : sub { confess("$IE @_") }; 122 if (@_ == 0) { 123 my @valid = sort grep { $Groks{$_} } keys %Groks; 124 return wantarray ? @valid : "@valid"; 125 } 126 for my $feep (map { split } @_) { 127 defined $Groks{$feep} 128 || $sploder->("$feep is never a valid struct pwd field"); 129 $cando &&= $Groks{$feep}; 130 } 131 return $cando; 132} 133 134sub _populate (@) { 135 return unless @_; 136 my $pwob = new(); 137 138 # Any that haven't been pw_had are assumed on "all" platforms of 139 # course, this may not be so, but you can't get here otherwise, 140 # since the underlying core call already took exception to your 141 # impudence. 142 143 $pw_name = $pwob->name ( $_[0] ); 144 $pw_passwd = $pwob->passwd ( $_[1] ) if pw_has("passwd"); 145 $pw_uid = $pwob->uid ( $_[2] ); 146 $pw_gid = $pwob->gid ( $_[3] ); 147 148 if (pw_has("change")) { 149 $pw_change = $pwob->change ( $_[4] ); 150 } 151 elsif (pw_has("age")) { 152 $pw_age = $pwob->age ( $_[4] ); 153 } 154 elsif (pw_has("quota")) { 155 $pw_quota = $pwob->quota ( $_[4] ); 156 } 157 158 if (pw_has("class")) { 159 $pw_class = $pwob->class ( $_[5] ); 160 } 161 elsif (pw_has("comment")) { 162 $pw_comment = $pwob->comment( $_[5] ); 163 } 164 165 $pw_gecos = $pwob->gecos ( $_[6] ) if pw_has("gecos"); 166 167 $pw_dir = $pwob->dir ( $_[7] ); 168 $pw_shell = $pwob->shell ( $_[8] ); 169 170 $pw_expire = $pwob->expire ( $_[9] ) if pw_has("expire"); 171 172 return $pwob; 173} 174 175sub getpwent ( ) { _populate(CORE::getpwent()) } 176sub getpwnam ($) { _populate(CORE::getpwnam(shift)) } 177sub getpwuid ($) { _populate(CORE::getpwuid(shift)) } 178sub getpw ($) { ($_[0] =~ /^\d+\z/s) ? &getpwuid : &getpwnam } 179 180_feature_init(); 181 1821; 183__END__ 184 185=head1 NAME 186 187User::pwent - by-name interface to Perl's built-in getpw*() functions 188 189=head1 SYNOPSIS 190 191 use User::pwent; 192 $pw = getpwnam('daemon') || die "No daemon user"; 193 if ( $pw->uid == 1 && $pw->dir =~ m#^/(bin|tmp)?\z#s ) { 194 print "gid 1 on root dir"; 195 } 196 197 $real_shell = $pw->shell || '/bin/sh'; 198 199 for (($fullname, $office, $workphone, $homephone) = 200 split /\s*,\s*/, $pw->gecos) 201 { 202 s/&/ucfirst(lc($pw->name))/ge; 203 } 204 205 use User::pwent qw(:FIELDS); 206 getpwnam('daemon') || die "No daemon user"; 207 if ( $pw_uid == 1 && $pw_dir =~ m#^/(bin|tmp)?\z#s ) { 208 print "gid 1 on root dir"; 209 } 210 211 $pw = getpw($whoever); 212 213 use User::pwent qw/:DEFAULT pw_has/; 214 if (pw_has(qw[gecos expire quota])) { .... } 215 if (pw_has("name uid gid passwd")) { .... } 216 print "Your struct pwd has: ", scalar pw_has(), "\n"; 217 218=head1 DESCRIPTION 219 220This module's default exports override the core getpwent(), getpwuid(), 221and getpwnam() functions, replacing them with versions that return 222C<User::pwent> objects. This object has methods that return the 223similarly named structure field name from the C's passwd structure 224from F<pwd.h>, stripped of their leading "pw_" parts, namely C<name>, 225C<passwd>, C<uid>, C<gid>, C<change>, C<age>, C<quota>, C<comment>, 226C<class>, C<gecos>, C<dir>, C<shell>, and C<expire>. The C<passwd>, 227C<gecos>, and C<shell> fields are tainted when running in taint mode. 228 229You may also import all the structure fields directly into your 230namespace as regular variables using the :FIELDS import tag. (Note 231that this still overrides your core functions.) Access these fields 232as variables named with a preceding C<pw_> in front their method 233names. Thus, C<< $passwd_obj->shell >> corresponds to $pw_shell 234if you import the fields. 235 236The getpw() function is a simple front-end that forwards 237a numeric argument to getpwuid() and the rest to getpwnam(). 238 239To access this functionality without the core overrides, pass the 240C<use> an empty import list, and then access function functions 241with their full qualified names. The built-ins are always still 242available via the C<CORE::> pseudo-package. 243 244=head2 System Specifics 245 246Perl believes that no machine ever has more than one of C<change>, 247C<age>, or C<quota> implemented, nor more than one of either 248C<comment> or C<class>. Some machines do not support C<expire>, 249C<gecos>, or allegedly, C<passwd>. You may call these methods 250no matter what machine you're on, but they return C<undef> if 251unimplemented. 252 253You may ask whether one of these was implemented on the system Perl 254was built on by asking the importable C<pw_has> function about them. 255This function returns true if all parameters are supported fields 256on the build platform, false if one or more were not, and raises 257an exception if you asked about a field that Perl never knows how 258to provide. Parameters may be in a space-separated string, or as 259separate arguments. If you pass no parameters, the function returns 260the list of C<struct pwd> fields supported by your build platform's 261C library, as a list in list context, or a space-separated string 262in scalar context. Note that just because your C library had 263a field doesn't necessarily mean that it's fully implemented on 264that system. 265 266Interpretation of the C<gecos> field varies between systems, but 267traditionally holds 4 comma-separated fields containing the user's 268full name, office location, work phone number, and home phone number. 269An C<&> in the gecos field should be replaced by the user's properly 270capitalized login C<name>. The C<shell> field, if blank, must be 271assumed to be F</bin/sh>. Perl does not do this for you. The 272C<passwd> is one-way hashed garble, not clear text, and may not be 273unhashed save by brute-force guessing. Secure systems use more a 274more secure hashing than DES. On systems supporting shadow password 275systems, Perl automatically returns the shadow password entry when 276called by a suitably empowered user, even if your underlying 277vendor-provided C library was too short-sighted to realize it should 278do this. 279 280See passwd(5) and getpwent(3) for details. 281 282=head1 NOTE 283 284While this class is currently implemented using the Class::Struct 285module to build a struct-like class, you shouldn't rely upon this. 286 287=head1 AUTHOR 288 289Tom Christiansen 290 291=head1 HISTORY 292 293=over 4 294 295=item March 18th, 2000 296 297Reworked internals to support better interface to dodgey fields 298than normal Perl function provides. Added pw_has() field. Improved 299documentation. 300 301=back 302