1package Unix::GroupFile; 2 3# $Id: GroupFile.pm,v 1.6 2000/05/02 15:59:34 ssnodgra Exp $ 4 5use strict; 6use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); 7use Unix::ConfigFile; 8 9require Exporter; 10 11@ISA = qw(Unix::ConfigFile Exporter); 12# Items to export into callers namespace by default. Note: do not export 13# names by default without a very good reason. Use EXPORT_OK instead. 14# Do not simply export all your public functions/methods/constants. 15@EXPORT = qw( 16 17); 18$VERSION = '0.06'; 19 20# Package variables 21my $MAXLINELEN = 511; 22 23# Implementation Notes 24# 25# This module adds 3 new fields to the basic ConfigFile object. The fields 26# are 'gid', 'gpass', and 'group'. All three of these fields are hashes. 27# The gid field maps names to GIDs. The gpass field maps names to passwords. 28# The group fields maps GIDs to another hash of group members. There are 29# no real values in the group subhash, just a '1' as a placeholder. This is 30# a hash instead of a list because it makes duplicate elimination and user 31# deletion much easier to deal with. 32 33# Preloaded methods go here. 34 35# Read in the data structures from the supplied file 36sub read { 37 my ($this, $fh) = @_; 38 39 while (<$fh>) { 40 chop; 41 my ($name, $password, $gid, $users) = split /:/; 42 my @users = split /,/, $users; 43 if (defined $this->{group}{$gid}) { 44 foreach (@users) { 45 $this->{group}{$gid}{$_} = 1; 46 } 47 } 48 else { 49 $this->group($name, $password, $gid, @users); 50 } 51 } 52 return 1; 53} 54 55 56# Add, modify, or get a group 57sub group { 58 my $this = shift; 59 my $name = shift; 60 61 # If no more parameters, we return group info 62 unless (@_) { 63 my $gid = $this->gid($name); 64 return undef unless defined $gid; 65 return ($this->passwd($name), $gid, $this->members($name)); 66 } 67 68 # Create or modify a group 69 return undef if @_ < 2; 70 my $password = shift; 71 my $gid = shift; 72 73 # Have to be careful with this test - 0 is a legitimate return value 74 return undef unless defined $this->gid($name, $gid); 75 $this->passwd($name, $password); 76 $this->members($name, @_); 77 return ($gid, $password, $this->members($name)); 78} 79 80 81# Delete a group 82sub delete { 83 my ($this, $name) = @_; 84 85 my $gid = $this->gid($name); 86 return 0 unless defined $gid; 87 delete $this->{gpass}{$name}; 88 delete $this->{group}{$gid}; 89 delete $this->{gid}{$name}; 90 return 1; 91} 92 93 94# Add users to an existing group 95sub add_user { 96 my $this = shift; 97 my $name = shift; 98 my @groups = ($name eq "*") ? $this->groups : ($name); 99 100 foreach (@groups) { 101 my $gid = $this->gid($_); 102 return 0 unless defined $gid; 103 foreach my $user (@_) { 104 $this->{group}{$gid}{$user} = 1; 105 } 106 } 107 return 1; 108} 109 110 111# Remove users from an existing group 112sub remove_user { 113 my $this = shift; 114 my $name = shift; 115 my @groups = ($name eq "*") ? $this->groups : ($name); 116 117 foreach (@groups) { 118 my $gid = $this->gid($_); 119 return 0 unless defined $gid; 120 foreach my $user (@_) { 121 delete $this->{group}{$gid}{$user}; 122 } 123 } 124 return 1; 125} 126 127 128# Rename a user 129sub rename_user { 130 my ($this, $oldname, $newname) = @_; 131 132 my $count = 0; 133 foreach ($this->groups) { 134 my $gid = $this->gid($_); 135 if (exists $this->{group}{$gid}{$oldname}) { 136 delete $this->{group}{$gid}{$oldname}; 137 $this->{group}{$gid}{$newname} = 1; 138 $count++; 139 } 140 } 141 return $count; 142} 143 144 145# Return the list of groups 146# Accepts a sorting order parameter: gid or name (default gid) 147sub groups { 148 my $this = shift; 149 my $order = @_ ? shift : "gid"; 150 151 return keys %{$this->{gid}} unless wantarray; 152 if ($order eq "name") { 153 return sort keys %{$this->{gid}}; 154 } 155 else { 156 return sort { $this->gid($a) <=> $this->gid($b) } keys %{$this->{gid}}; 157 } 158} 159 160 161# Returns the maximum GID in use in the file 162sub maxgid { 163 my $this = shift; 164 my @gids = sort { $a <=> $b } keys %{$this->{group}}; 165 return pop @gids; 166} 167 168 169# Output the file to disk 170sub write { 171 my ($this, $fh) = @_; 172 173 foreach my $name ($this->groups) { 174 my @users = $this->members($name); 175 my $head = join(":", $name, $this->passwd($name), $this->gid($name), ""); 176 my $ind = join(":", "$name%n", $this->passwd($name), $this->gid($name), ""); 177 print $fh $this->joinwrap($MAXLINELEN, $head, $ind, ",", "", @users), 178 "\n" or return 0; 179 } 180 return 1; 181} 182 183 184# Accessors (these all accept a group name and an optional value) 185sub passwd { 186 my $this = shift; 187 my $name = shift; 188 @_ ? $this->{gpass}{$name} = shift : $this->{gpass}{$name}; 189} 190 191 192# Note that it is illegal to change a group's GID to one used by another group 193# This method also has to take into account side effects produced by doing 194# this, such as the fact that the member hash is keyed against the GID. 195sub gid { 196 my $this = shift; 197 my $name = shift; 198 199 return $this->{gid}{$name} unless @_; 200 my $newgid = shift; 201 my $oldgid = $this->{gid}{$name}; 202 # Return OK if you try to set the same GID a group already has 203 return $oldgid if defined $oldgid && $newgid == $oldgid; 204 return undef if grep { $newgid == $_ } values %{$this->{gid}}; 205 if (defined $oldgid) { 206 $this->{group}{$newgid} = $this->{group}{$oldgid}; 207 delete $this->{group}{$oldgid}; 208 } 209 $this->{gid}{$name} = $newgid; 210} 211 212 213# Return or set the list of users in a group 214sub members { 215 my $this = shift; 216 my $name = shift; 217 218 my $gid = $this->gid($name); 219 return undef unless defined $gid; 220 if (@_) { 221 $this->{group}{$gid} = { }; 222 $this->add_user($name, @_); 223 } 224 return keys %{$this->{group}{$gid}} unless wantarray; 225 return sort keys %{$this->{group}{$gid}}; 226} 227 228# Autoload methods go after =cut, and are processed by the autosplit program. 229 2301; 231__END__ 232# Below is the stub of documentation for your module. You better edit it! 233 234=head1 NAME 235 236Unix::GroupFile - Perl interface to /etc/group format files 237 238=head1 SYNOPSIS 239 240 use Unix::GroupFile; 241 242 $grp = new Unix::GroupFile "/etc/group"; 243 $grp->group("bozos", "*", $grp->maxgid + 1, @members); 244 $grp->remove_user("coolgrp", "bgates", "badguy"); 245 $grp->add_user("coolgrp", "joecool", "goodguy"); 246 $grp->remove_user("*", "deadguy"); 247 $grp->passwd("bozos", $grp->encpass("newpass")); 248 $grp->commit(); 249 undef $grp; 250 251=head1 DESCRIPTION 252 253The Unix::GroupFile module provides an abstract interface to /etc/group format 254files. It automatically handles file locking, getting colons and commas in 255the right places, and all the other niggling details. 256 257This module also handles the annoying problem (at least on some systems) of 258trying to create a group line longer than 512 characters. Typically this is 259done by creating multiple lines of groups with the same GID. When a new 260GroupFile object is created, all members of groups with the same GID are 261merged into a single group with a name corresponding to the first name found 262in the file for that GID. When the file is committed, long groups are written 263out as multiple lines of no more than 512 characters, with numbers appended to 264the group name for the extra lines. 265 266=head1 METHODS 267 268=head2 add_user( GROUP, @USERS ) 269 270This method will add the list of users to an existing group. Users that are 271already members of the group are silently ignored. The special group name * 272will add the users to every group. Returns 1 on success or 0 on failure. 273 274=head2 commit( [BACKUPEXT] ) 275 276See the Unix::ConfigFile documentation for a description of this method. 277 278=head2 delete( GROUP ) 279 280This method will delete the named group. It has no effect if the supplied 281group does not exist. 282 283=head2 encpass( PASSWORD ) 284 285See the Unix::ConfigFile documentation for a description of this method. 286 287=head2 gid( GROUP [,GID] ) 288 289Read or modify a group's GID. Returns the GID in either case. Note that it 290is illegal to change a group's GID to a GID that is already in use by another 291group. In this case, the method returns undef. 292 293=head2 group( GROUP [,PASSWD, GID, @USERS] ) 294 295This method can add, modify, or return information about a group. Supplied 296with a single group parameter, it will return a list consisting of (PASSWORD, 297GID, @MEMBERS), or undef if no such group exists. If you supply at least 298three parameters, the named group will be created or modified if it already 299exists. The list is also returned to you in this case. Note that it is 300illegal to specify a GID that is already in use by another group. In this 301case, the method returns undef. 302 303=head2 groups( [SORTBY] ) 304 305This method returns a list of all existing groups. By default the list will 306be sorted in order of the GIDs of the groups. You may also supply "name" as a 307parameter to the method to get the list sorted by group name. In scalar 308context, this method returns the total number of groups. 309 310=head2 maxgid( ) 311 312This method returns the maximum GID in use by all groups. 313 314=head2 members( GROUP [,@USERS] ) 315 316Read or modify the list of members associated with a group. If you specify 317any users when you call the method, all existing members of the group are 318removed and your list becomes the new set of members. In scalar context, 319this method returns the total number of members in the group. 320 321=head2 new( FILENAME [,OPTIONS] ) 322 323See the Unix::ConfigFile documentation for a description of this method. 324 325=head2 passwd( GROUP [,PASSWD] ) 326 327Read or modify a group's password. Returns the encrypted password in either 328case. If you have a plaintext password, use the encpass method to encrypt it 329before passing it to this method. 330 331=head2 remove_user( GROUP, @USERS ) 332 333This method will remove the list of users from an existing group. Users that 334are not members of the group are silently ignored. The special group name * 335will remove the users from every group. Returns 1 on success or 0 on failure. 336 337=head2 rename_user( OLDNAME, NEWNAME ) 338 339This method will change one username to another in every group. Returns the 340number of groups affected. 341 342=head1 AUTHOR 343 344Steve Snodgrass, ssnodgra@fore.com 345 346=head1 SEE ALSO 347 348Unix::AliasFile, Unix::AutomountFile, Unix::ConfigFile, Unix::PasswdFile 349 350=cut 351