File Coverage

blib/lib/Unix/PasswdFileOps.pm
Criterion Covered Total %
statement 15 103 14.5
branch 0 36 0.0
condition 0 35 0.0
subroutine 5 15 33.3
pod 9 9 100.0
total 29 198 14.6


line stmt bran cond sub pod time code
1             package Unix::PasswdFileOps;
2              
3             =head1 NAME
4              
5             Unix::PasswdFileOps - Operations on Unix Passwd file
6              
7             =head1 SYNOPSIS
8              
9             use Unix::PasswdFileOps;
10              
11             my $pass = Unix::PasswdFileOps->new('passwd' => '/etc/passwd');
12             print $pass->passwd();
13             $pass->verbose(1);
14             $pass->protect_zero(1);
15             if($pass->sort_passwd())
16             {
17             print $pass->error;
18             }
19              
20             =head1 DESCRIPTION
21              
22             This module will perform sorting on a standard UNIX passwd file, the sort is
23             performed against the UID by default although this can be altered using the
24             sort_field() function.
25              
26             Additionally it can populate an internal hash of arrays with line information
27             this provides a nice interface to find information about user accounts.
28              
29             =cut
30              
31 1     1   53485 use 5.008007;
  1         4  
  1         75  
32 1     1   4 use strict;
  1         3  
  1         31  
33 1     1   5 use warnings;
  1         5  
  1         34  
34 1     1   4 use Carp;
  1         1  
  1         98  
35 1     1   154 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  1         3  
  1         1845  
36              
37             require Exporter;
38              
39             @ISA = qw(Exporter);
40             @EXPORT_OK = qw/new validate identify barcode type/;
41             @EXPORT = qw//;
42             %EXPORT_TAGS = (all => [@EXPORT_OK]);
43             $VERSION = "0.4";
44              
45             # Preloaded methods go here.
46              
47             =head1 FUNCTIONS
48              
49             =head2 new
50              
51             my $passwd = Unix::PasswdFileOps->new('passwd' => '/etc/passwd');
52              
53             You can pass in the optional parameters 'passwd', 'protect_zero'
54             'verbose' and 'sort_field'
55              
56             =cut
57              
58             sub new {
59 0     0 1   my $class = "Unix::PasswdFileOps";
60 0           my %params = @_;
61 0           my $self = {};
62 0   0       $self->{'passwd'} = $params{'passwd'} || undef;
63 0   0       $self->{'protect_zero'} = $params{'protect_zero'} || undef;
64 0   0       $self->{'verbose'} = $params{'verbose'} || undef;
65 0   0       $self->{'sort_field'} = $params{'sort_field'} || undef;
66 0           bless $self, $class;
67 0           return $self;
68             }
69              
70             ###
71             # Internal function
72             #
73              
74             sub _error_msg {
75 0     0     my $self = shift;
76 0           $self->{'error'} = shift;
77             }
78              
79             =head2 passwd
80              
81             my $passwd_file = $pass->passwd();
82             # or
83             $pass->passwd('/etc/passwd');
84              
85             This function will allow you to view or set the passwd file
86             parameter, if you pass a parameter it will set the passwd file to
87             that parameter, if you do not it will just return the
88             currently set file.
89              
90             =cut
91              
92             sub passwd {
93 0     0 1   my ($self, $new_val) = @_;
94 0 0         $self->{'passwd'} = $new_val if $new_val;
95 0           return $self->{'passwd'};
96             }
97              
98             =head2 protect_zero
99              
100             my $zero = $pass->protect_zero();
101             # or
102             $pass->protect_zero(1);
103              
104             This function will allow you to view or set the protect_zero
105             parameter, if you pass a parameter it will set the protect_zero option
106             to 1, if you do not it will just return the currently set parameter.
107              
108             If the protect_zero option is set all 0 uids will be unsorted and
109             left at the top of the file, this is useful if you have more than
110             one user with UID 0.
111              
112             =cut
113              
114             sub protect_zero {
115 0     0 1   my ($self, $new_val) = @_;
116 0 0         $self->{'protect_zero'} = 1 if $new_val;
117 0           return $self->{'protect_zero'};
118             }
119              
120             =head2 unprotect_zero
121              
122             my $zero = $pass-unprotect_zero();
123             # or
124             $pass->unprotect_zero(1);
125              
126             This function will allow you to view or unset the protect_zero
127             parameter, if you pass a parameter it will set the protect_zero option
128             to undef, if you do not it will just return the currently set parameter.
129              
130             If the protect_zero option is set all 0 uids will be unsorted and
131             left at the top of the file, this is useful if you have more than
132             one user with UID 0.
133              
134             =cut
135              
136             sub unprotect_zero {
137 0     0 1   my ($self, $new_val) = @_;
138 0 0         $self->{'protect_zero'} = undef if $new_val;
139 0           return $self->{'protect_zero'};
140             }
141              
142             =head2 verbose
143              
144             my $verbose = $pass->verbose();
145             # or
146             $pass->verbose(1);
147              
148             This function will allow you to view or set the verbose
149             parameter, if you pass a parameter it will set the verbose option
150             to that parameter, if you do not it will just return the
151             currently set parameter.
152              
153             This is only useful during the passwd_sort function output
154             will be printed to screen if this is enabled
155              
156             =cut
157              
158             sub verbose {
159 0     0 1   my ($self, $new_val) = @_;
160 0 0         $self->{'verbose'} = $new_val if $new_val;
161 0           return $self->{'verbose'};
162             }
163              
164             =head2 sort_field
165              
166             my $sort_field = $pass->sort_field();
167             # or
168             $pass->sort_field(1);
169              
170             This function will allow you to view or set the sort_field
171             parameter, if you pass a parameter it will set the sort_field option
172             to that parameter, if you do not it will just return the
173             currently set parameter.
174              
175             The sort_field option determines which field to sort the passwd
176             file by, the default is field 2 the UID field.
177              
178             =cut
179              
180             sub sort_field {
181 0     0 1   my ($self, $new_val) = @_;
182 0 0 0       if (($new_val) && ($new_val >= 0) && ($new_val <= 6)) {
      0        
183 0           $self->{'sort_field'} = $new_val;
184             }
185             else {
186 0           _error_msg($self, "$new_val not within 0 - 6 range");
187 0           return 1;
188             }
189 0           return $self->{'sort_field'};
190             }
191              
192             =head2 error
193              
194             my $err = $pass->error();
195              
196             This function will allow you to view any error messages
197              
198             =cut
199              
200             sub error {
201 0     0 1   my $self = shift;
202 0           return $self->{'error'};
203             }
204              
205             =head2 sort_passwd
206              
207             my $zero = $pass->sort_passwd();
208             # or
209             $pass->sort_passwd();
210              
211             This function performs a sort on the current passwd file,
212             technically this is a safe process the sort type is determined
213             by the sort_field option or the default field 2 (array element 2)
214             this has been tested on Linux, Solaris 8, and almost 200 System V R 4.3
215             machines. It should be safe on any standard unix passwd file.
216              
217             The file is read into the function and the sorted output is written
218             back to the file, it is suggested that you create a backup of the
219             passwd file before running this function.
220              
221             =cut
222              
223             sub sort_passwd {
224 0     0 1   my $self = shift;
225 0           my %sort_lines = ();
226 0           my @unsort_lines = ();
227 0           my $sort_field = shift;
228              
229 0 0 0       if (($self->{'sort_field'}) && ($self->{'sort_field'} <= 0) && ($self->{'sort_field'} >= 6)) {
    0 0        
      0        
      0        
230 0           _error_msg($self, $self->{'sort_field'}." not within 0 - 6 range");
231 0           return 1;
232             }
233             elsif (($self->{'sort_field'}) && ($self->{'sort_field'} >= 0) && ($self->{'sort_field'} <= 6)) {
234 0           $sort_field = $self->{'sort_field'};
235             }
236             else {
237 0           $sort_field = 2;
238             }
239              
240 0 0         defined($self->{'passwd'}) ? my $passwd = $self->{'passwd'} : return 1;
241            
242 0 0 0       open(PWD,"$passwd") || _error_msg($self, "Cannot open $passwd: $!") && return 1;
243             {
244 0           while() {
  0            
245 0           chomp();
246 0           my $line = $_;
247 0           my @lines = split(":", $_, 6);
248            
249 0 0 0       if (($lines[2] == 0) && ($self->{'protect_zero'})) {
250 0 0         print "Not sorting: $line" if $self->{'verbose'};
251 0           push(@unsort_lines, $line);
252             }
253             else {
254 0 0         if ( $sort_lines{$lines[$sort_field]} ) {
255 0           $sort_lines{$lines[$sort_field]} .= "\n".$line;
256             }
257             else {
258 0           $sort_lines{$lines[$sort_field]} = $line;
259             }
260             }
261             }
262             }
263 0           close(PWD);
264              
265 0 0 0       open(PASS2, ">$passwd") || _error_msg($self, "Cannot open $passwd: $!") && return 1; ;
266             {
267 0           foreach my $key (@unsort_lines) {
  0            
268 0           print PASS2 "$key\n";
269             }
270              
271 0 0         if ($sort_field !~ /[23]/) {
272 0           foreach my $key (sort { $a cmp $b } keys %sort_lines) {
  0            
273 0 0         print $sort_lines{$key}."\n" if $self->{'verbose'};
274 0           print PASS2 $sort_lines{$key}."\n";
275             }
276             }
277             else {
278 0           foreach my $key (sort { $a <=> $b } keys %sort_lines) {
  0            
279 0 0         print $sort_lines{$key}."\n" if $self->{'verbose'};
280 0           print PASS2 $sort_lines{$key}."\n";
281             }
282             }
283             }
284 0           close(PASS2);
285              
286 0           return 0;
287             }
288              
289             =head2 populate_stats
290              
291             $pass->populate_stats();
292              
293             This function will populate an internal hash of arrays with the
294             contents of the passwd file. You can access these using the following:
295              
296             $pass->{'file_stats'}->{'username'}->{'list'}[0];
297              
298             You can substitute 'username' with 'uid', 'gid', 'fullname', 'homedir',
299             and 'shell'.
300              
301             =cut
302              
303             sub populate_stats {
304 0     0 1   my $self = shift;
305              
306 0 0         defined($self->{'passwd'}) ? my $passwd = $self->{'passwd'} : return 1;
307            
308 0 0 0       open(PWD,"$passwd") || _error_msg($self, "Cannot open $passwd: $!") && return 1;
309              
310 0           while() {
311 0           chomp;
312 0           my @lines = split(/:/, $_);
313              
314 0           push(@{ $self->{'file_stats'}->{'username'}->{'list'} }, $lines[0]);
  0            
315 0           push(@{ $self->{'file_stats'}->{'uid'}->{'list'} }, $lines[2]);
  0            
316 0           push(@{ $self->{'file_stats'}->{'gid'}->{'list'} }, $lines[3]);
  0            
317 0           push(@{ $self->{'file_stats'}->{'fullname'}->{'list'} }, $lines[4]);
  0            
318 0           push(@{ $self->{'file_stats'}->{'homedir'}->{'list'} }, $lines[5]);
  0            
319 0           push(@{ $self->{'file_stats'}->{'shell'}->{'list'} }, $lines[6]);
  0            
320             }
321              
322 0           close(PWD);
323             }
324              
325             1;
326             __END__