File Coverage

blib/lib/Passwd/Unix.pm
Criterion Covered Total %
statement 142 452 31.4
branch 40 292 13.7
condition 30 165 18.1
subroutine 34 68 50.0
pod 38 38 100.0
total 284 1015 27.9


line stmt bran cond sub pod time code
1             package Passwd::Unix;
2             $Passwd::Unix::VERSION = '1.09';
3 2     2   184137 use parent qw( Exporter::Tiny );
  2         603  
  2         13  
4 2     2   3959 use warnings;
  2         4  
  2         52  
5 2     2   10 use strict;
  2         5  
  2         33  
6             #-----------------------------------------------------------------------
7 2     2   9 use Config;
  2         4  
  2         80  
8 2     2   512 use Crypt::Password;
  2         5557  
  2         311  
9 2     2   849 use IO::Compress::Bzip2 qw( bzip2 $Bzip2Error );
  2         33545  
  2         303  
10 2     2   961 use Path::Tiny;
  2         15746  
  2         105  
11 2     2   571 use Tie::Array::CSV;
  2         42119  
  2         75  
12             #-----------------------------------------------------------------------
13 2     2   46 use constant DAY => 86400;
  2         5  
  2         186  
14 2     2   13 use constant SEP => q[:];
  2         4  
  2         105  
15              
16 2     2   11 use constant ALG => q[sha512];
  2         6  
  2         120  
17 2     2   14 use constant BCK => 1;
  2         11  
  2         101  
18 2     2   12 use constant CMP => 1;
  2         4  
  2         93  
19 2     2   13 use constant DBG => 0;
  2         4  
  2         97  
20 2     2   14 use constant WRN => 0;
  2         3  
  2         117  
21 2     2   14 use constant MSK => 0022;
  2         28  
  2         110  
22              
23 2     2   13 use constant PWD => q[/etc/passwd];
  2         4  
  2         81  
24 2     2   11 use constant GRP => q[/etc/group];
  2         4  
  2         120  
25 2     2   14 use constant PSH => q[/etc/shadow];
  2         4  
  2         126  
26 2     2   15 use constant GSH => q[/etc/gshadow];
  2         3  
  2         12153  
27             #=======================================================================
28             our @EXPORT_OK = qw(
29            
30             backup
31             compress
32             debug
33             warnings
34             error
35            
36             encpass
37            
38             exists_user
39             exists_group
40            
41             passwd_file
42             group_file
43             shadow_file
44             gshadow_file
45            
46             user
47             uid
48             gid
49             gecos
50             home
51             shell
52             passwd
53             rename
54            
55             del
56             del_user
57            
58             group
59             del_group
60            
61             users
62             users_from_shadow
63            
64             minuid
65             mingid
66            
67             maxuid
68             maxgid
69            
70             unused_uid
71             unused_gid
72            
73             groups
74             groups_from_gshadow
75            
76             reset
77            
78             default_umask
79             );
80             #======================================================================
81             my $Self = __PACKAGE__->new();
82             #======================================================================
83             sub new {
84 3     3 1 742 my ( $class, %opt ) = @_;
85            
86 3         9 my $self = bless { }, $class;
87            
88 3   100     68 $self->algorithm ( $opt{ algorithm } // ALG );
89 3   100     19 $self->backup ( $opt{ backup } // BCK );
90 3   100     26 $self->compress ( $opt{ compress } // CMP );
91 3   50     21 $self->debug ( $opt{ debug } // DBG );
92 3   50     20 $self->default_umask( $opt{ umask } // MSK );
93 3   100     25 $self->warnings ( $opt{ warnings } // WRN );
94            
95            
96 3   100     32 $self->passwd_file ( $opt{ passwd } // PWD );
97 3   100     32 $self->group_file ( $opt{ group } // GRP );
98 3   100     19 $self->shadow_file ( $opt{ shadow } // PSH );
99 3   100     27 $self->gshadow_file ( $opt{ gshadow } // GSH );
100            
101 3         11 return $self;
102             }
103             #=======================================================================
104             sub _err {
105 0     0   0 my ( $self, @str ) = @_;
106            
107 0         0 $self->{ err } = join( q[], @str );
108 0 0       0 warn $self->{ err } if $self->{ wrn };
109            
110 0         0 return;
111             }
112             #=======================================================================
113             sub _dat {
114 0     0   0 my ( $sec, $min, $hour, $mday, $mon, $year ) = localtime( time );
115            
116 0         0 $year += 1900;
117 0         0 $mon += 1;
118            
119 0 0       0 $sec = q[0] . $sec if $sec =~ /^\d$/o;
120 0 0       0 $min = q[0] . $min if $min =~ /^\d$/o;
121 0 0       0 $hour = q[0] . $hour if $hour =~ /^\d$/o;
122 0 0       0 $mday = q[0] . $mday if $mday =~ /^\d$/o;
123 0 0       0 $mon = q[0] . $mon if $mon =~ /^\d$/o;
124            
125 0         0 return $year . q[.] . $mon . q[.] . $mday . q[-] . $hour . q[.] . $min . q[.] . $sec;
126             }
127             #=======================================================================
128             sub _bck {
129 0     0   0 my ( $self ) = @_;
130            
131             #-------------------------------------------------------------------
132 0 0       0 return unless $self->{ bck };
133            
134             #-------------------------------------------------------------------
135 0         0 my $dir = path( $self->{ pwd } . q[.bak], _dat() );
136            
137 0 0       0 if( $dir->exists ){
138 0 0       0 return $self->_err( $dir . " is a file. It should be a directory.\n" ) if $dir->is_file;
139             }else{
140 0         0 $dir->mkpath;
141             }
142            
143             #-------------------------------------------------------------------
144 0         0 for my $dis ( qw( pwd grp psh gsh ) ){
145 0         0 my $src = path( $self->{ $dis } );
146 0         0 my $dst = $dir->child( $src->basename . q[.bz2] );
147            
148 0 0       0 if( $self->{ cmp } ){
149 0         0 $dst->touch;
150 0         0 $dst->chmod( $src->stat->mode );
151 0 0       0 bzip2 $src->stringify => $dst->stringify or return $self->_err( $Bzip2Error );
152             }else{
153 0         0 $src->copy( $dst );
154             }
155             }
156            
157             #-------------------------------------------------------------------
158 0         0 return 1;
159             }
160             #=======================================================================
161             sub del_group {
162             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
163 0 0 0 0 1 0 my $self = scalar @_ && ref $_[0] eq __PACKAGE__ ? shift : $Self;
164             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
165 0         0 my ( @val ) = @_;
166            
167 0 0       0 return $self->_err( q[Supplied value is undefined.] ) unless @val;
168             #return $self->_err( q[Supplied group does not exists.] ) unless _exs( $self->{ grp }, $val );
169 0 0       0 return $self->_err( q[Unsufficient permissions.] ) unless open my $fhd, '>>', $self->{ gsh };
170            
171 0         0 close( $fhd );
172            
173             #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
174 0 0       0 $self->_bck or return;
175             #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
176            
177             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
178             tie my @ary, q[Tie::Array::CSV], $self->{ grp }, {
179 0         0 tie_file => {
180             autochomp => 1,
181             },
182             text_csv => {
183             sep_char => SEP,
184             binary => 1,
185             quote_char => undef,
186             },
187             };
188             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
189             tie my @yra, q[Tie::Array::CSV], $self->{ gsh }, {
190 0         0 tie_file => {
191             autochomp => 1,
192             },
193             text_csv => {
194             sep_char => SEP,
195             binary => 1,
196             quote_char => undef,
197             },
198             };
199             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
200 0         0 for my $val ( @val ){
201 0         0 my $sav;
202 0         0 for my $idx ( 0 .. $#ary ){
203 0 0       0 next if $ary[ $idx ][ 0 ] ne $val;
204 0         0 splice @ary, $idx, 1;
205 0         0 $sav = $idx;
206 0         0 last;
207             }
208             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
209 0 0       0 if( $yra[ $sav ][ 0 ] eq $val ){
210 0         0 splice @yra, $sav, 1;
211             }else{
212 0         0 for my $idx ( 0 .. $#yra ){
213 0 0       0 next if $yra[ $idx ][ 0 ] ne $val;
214 0         0 splice @yra, $idx, 1;
215 0         0 last;
216             }
217             }
218             }
219             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
220 0         0 return 1;
221             }
222             #=======================================================================
223             #*del_user = { };
224             *del_user = \&del;
225             #-----------------------------------------------------------------------
226             sub del {
227             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
228 0 0 0 0 1 0 my $self = scalar @_ && ref $_[0] eq __PACKAGE__ ? shift : $Self;
229             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
230 0         0 my ( @val ) = @_;
231            
232 0 0       0 return $self->_err( q[Supplied value is undefined.] ) unless @val;
233             #return $self->_err( q[Supplied user does not exists.] ) unless _exs( $self->{ pwd }, $val );
234 0 0       0 return $self->_err( q[Unsufficient permissions.] ) unless open my $fhd, '>>', $self->{ psh };
235            
236 0         0 close( $fhd );
237            
238             #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
239 0 0       0 $self->_bck or return;
240             #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
241            
242             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
243             tie my @ary, q[Tie::Array::CSV], $self->{ pwd }, {
244 0         0 tie_file => {
245             autochomp => 1,
246             },
247             text_csv => {
248             sep_char => SEP,
249             binary => 1,
250             quote_char => undef,
251             },
252             };
253             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
254             tie my @yra, q[Tie::Array::CSV], $self->{ psh }, {
255 0         0 tie_file => {
256             autochomp => 1,
257             },
258             text_csv => {
259             sep_char => SEP,
260             binary => 1,
261             quote_char => undef,
262             },
263             };
264             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
265 0         0 for my $val ( @val ){
266 0         0 my $usr;
267             my $sav;
268 0         0 for my $idx ( 0 .. $#ary ){
269 0 0       0 next if $ary[ $idx ][ 0 ] ne $val;
270 0         0 $usr = splice @ary, $idx, 1;
271 0         0 $sav = $idx;
272 0         0 last;
273             }
274             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
275 0 0       0 if( $yra[ $sav ][ 0 ] eq $val ){
276 0         0 splice @yra, $sav, 1;
277             }else{
278 0         0 for my $idx ( 0 .. $#yra ){
279 0 0       0 next if $yra[ $idx ][ 0 ] ne $val;
280 0         0 splice @yra, $idx, 1;
281 0         0 last;
282             }
283             }
284             }
285             #+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
286 0         0 untie @ary;
287 0         0 untie @yra;
288            
289 0         0 @ary = ( );
290 0         0 @yra = ( );
291            
292             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
293             tie @ary, q[Tie::Array::CSV], $self->{ grp }, {
294 0         0 tie_file => {
295             autochomp => 1,
296             },
297             text_csv => {
298             sep_char => SEP,
299             binary => 1,
300             quote_char => undef,
301             },
302             };
303             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
304             tie @yra, q[Tie::Array::CSV], $self->{ gsh }, {
305 0         0 tie_file => {
306             autochomp => 1,
307             },
308             text_csv => {
309             sep_char => SEP,
310             binary => 1,
311             quote_char => undef,
312             },
313             };
314             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
315 0         0 for my $val ( @val ){
316 0         0 my $sav;
317 0         0 for my $idx ( 0 .. $#ary ){
318 0 0       0 next if $ary[ $idx ][ 3 ] !~ /\b$val\b/;
319 0         0 $ary[ $idx ][ 3 ] = join( q[,], grep { $_ ne $val } split( /\s*,\s*/, $ary[ $idx ][ 3 ] ) );
  0         0  
320             }
321             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
322 0         0 for my $idx ( 0 .. $#yra ){
323 0 0       0 next if $ary[ $idx ][ 3 ] !~ /\b$val\b/;
324 0         0 $ary[ $sav ][ 3 ] = join( q[,], grep { $_ ne $val } split( /\s*,\s*/, $ary[ $sav ][ 3 ] ) );
  0         0  
325             }
326             }
327             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
328 0         0 return 1;
329             }
330             #=======================================================================
331             sub rename {
332             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
333 0 0 0 0 1 0 my $self = scalar @_ && ref $_[0] eq __PACKAGE__ ? shift : $Self;
334             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
335 0         0 my ( $usr, $val ) = @_;
336            
337 0 0 0     0 return $self->_err( q[Supplied value is undefined.] ) unless defined $usr and defined $val;
338 0 0       0 return $self->_err( q[Supplied user does not exists.] ) unless _exs( $self->{ pwd }, $usr );
339            
340 0         0 _set( $self->{ pwd }, $usr, 0, $val );
341 0         0 _set( $self->{ psh }, $usr, 0, $val );
342            
343 0         0 return;
344             }
345             #=======================================================================
346             sub passwd {
347             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
348 0 0 0 0 1 0 my $self = scalar @_ && ref $_[0] eq __PACKAGE__ ? shift : $Self;
349             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
350 0         0 my ( $usr, $val ) = @_;
351            
352 0 0 0     0 return $self->_err( q[Supplied value is undefined.] ) unless defined $usr and defined $val;
353 0 0       0 return $self->_err( q[Supplied user does not exists.] ) unless _exs( $self->{ pwd }, $usr );
354            
355 0 0       0 if( defined $val ){
356 0         0 return $self->_set( $self->{ psh }, $usr, 1, $val );
357             }else{
358 0         0 return $self->_get( $self->{ psh }, $usr, 1 );
359             }
360             }
361             #=======================================================================
362             sub shell {
363             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
364 0 0 0 0 1 0 my $self = scalar @_ && ref $_[0] eq __PACKAGE__ ? shift : $Self;
365             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
366 0         0 my ( $usr, $val ) = @_;
367            
368 0 0 0     0 return $self->_err( q[Supplied value is undefined.] ) unless defined $usr and defined $val;
369 0 0       0 return $self->_err( q[Supplied user does not exists.] ) unless _exs( $self->{ pwd }, $usr );
370            
371 0 0       0 if( defined $val ){
372 0         0 return $self->_set( $self->{ pwd }, $usr, 6, $val );
373             }else{
374 0         0 return $self->_get( $self->{ pwd }, $usr, 6 );
375             }
376             }
377             #=======================================================================
378             sub home {
379             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
380 0 0 0 0 1 0 my $self = scalar @_ && ref $_[0] eq __PACKAGE__ ? shift : $Self;
381             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
382 0         0 my ( $usr, $val ) = @_;
383            
384 0 0 0     0 return $self->_err( q[Supplied value is undefined.] ) unless defined $usr and defined $val;
385 0 0       0 return $self->_err( q[Supplied user does not exists.] ) unless _exs( $self->{ pwd }, $usr );
386            
387 0 0       0 if( defined $val ){
388 0         0 return $self->_set( $self->{ pwd }, $usr, 5, $val );
389             }else{
390 0         0 return $self->_get( $self->{ pwd }, $usr, 5 );
391             }
392             }
393             #=======================================================================
394             sub gecos {
395             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
396 0 0 0 0 1 0 my $self = scalar @_ && ref $_[0] eq __PACKAGE__ ? shift : $Self;
397             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
398 0         0 my ( $usr, $val ) = @_;
399            
400 0 0 0     0 return $self->_err( q[Supplied value is undefined.] ) unless defined $usr and defined $val;
401 0 0       0 return $self->_err( q[Supplied user does not exists.] ) unless _exs( $self->{ pwd }, $usr );
402            
403 0 0       0 if( defined $val ){
404 0         0 return $self->_set( $self->{ pwd }, $usr, 4, $val );
405             }else{
406 0         0 return $self->_get( $self->{ pwd }, $usr, 4 );
407             }
408             }
409             #=======================================================================
410             sub gid {
411             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
412 0 0 0 0 1 0 my $self = scalar @_ && ref $_[0] eq __PACKAGE__ ? shift : $Self;
413             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
414 0         0 my ( $usr, $val ) = @_;
415            
416 0 0 0     0 return $self->_err( q[Supplied value is undefined.] ) unless defined $usr and defined $val;
417 0 0       0 return $self->_err( q[Supplied user does not exists.] ) unless _exs( $self->{ pwd }, $usr );
418            
419 0 0       0 if( defined $val ){
420 0         0 return $self->_set( $self->{ pwd }, $usr, 3, $val );
421             }else{
422 0         0 return $self->_get( $self->{ pwd }, $usr, 3 );
423             }
424             }
425             #=======================================================================
426             sub uid {
427             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
428 0 0 0 0 1 0 my $self = scalar @_ && ref $_[0] eq __PACKAGE__ ? shift : $Self;
429             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
430 0         0 my ( $usr, $val ) = @_;
431            
432 0 0 0     0 return $self->_err( q[Supplied value is undefined.] ) unless defined $usr and defined $val;
433 0 0       0 return $self->_err( q[Supplied user does not exists.] ) unless _exs( $self->{ pwd }, $usr );
434            
435 0 0       0 if( defined $val ){
436 0         0 return $self->_set( $self->{ pwd }, $usr, 2, $val );
437             }else{
438 0         0 return $self->_get( $self->{ pwd }, $usr, 2 );
439             }
440             }
441             #=======================================================================
442             sub _set {
443 0     0   0 my ( $self, $pth, $usr, $pos, $val ) = @_;
444              
445 0 0       0 return $self->_err( q[Unsufficient permissions.] ) unless open my $fhd, '>>', $pth;
446 0         0 close( $fhd );
447            
448             #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
449 0 0       0 $self->_bck or return;
450             #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
451            
452 0         0 tie my @ary, q[Tie::Array::CSV], $pth, {
453             tie_file => {
454             autochomp => 1,
455             },
456             text_csv => {
457             sep_char => SEP,
458             binary => 1,
459             quote_char => undef,
460             },
461             };
462              
463 0         0 for my $idx ( 0 .. $#ary ){
464 0 0       0 next if $ary[ $idx ][ 0 ] ne $usr;
465 0         0 $ary[ $idx ][ $pos ] = $val;
466             }
467            
468 0         0 return 1;
469             }
470             #=======================================================================
471             sub _get {
472 0     0   0 my ( $self, $pth, $usr, $pos ) = @_;
473            
474 0 0       0 return $self->_err( q[Unsufficient permissions.] ) unless open my $fhd, '<', $pth;
475            
476 0         0 while( <$fhd> ){
477 0         0 my @tmp = split /:/;
478 0 0       0 next if $tmp[ 0 ] ne $usr;
479 0         0 return $tmp[ $pos ];
480             }
481 0         0 close( $fhd );
482            
483 0         0 return;
484             }
485             #=======================================================================
486             sub group {
487             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
488 0 0 0 0 1 0 my $self = scalar @_ && ref $_[0] eq __PACKAGE__ ? shift : $Self;
489             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
490 0         0 my ( @arg ) = @_;
491            
492             #-------------------------------------------------------------------
493 0 0       0 if( @arg == 3 ){
    0          
494 0 0       0 return $self->_err( q[Unsufficient permissions.] ) unless open my $fhd, '>>', $self->{ gsh };
495 0         0 close( $fhd );
496            
497             #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
498 0 0       0 $self->_bck or return;
499             #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
500            
501             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
502             tie my @ary, q[Tie::Array::CSV], $self->{ grp }, {
503 0         0 tie_file => {
504             autochomp => 1,
505             },
506             text_csv => {
507             sep_char => SEP,
508             binary => 1,
509             quote_char => undef,
510             },
511             };
512             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
513             tie my @yra, q[Tie::Array::CSV], $self->{ gsh }, {
514 0         0 tie_file => {
515             autochomp => 1,
516             },
517             text_csv => {
518             sep_char => SEP,
519             binary => 1,
520             quote_char => undef,
521             },
522             };
523             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
524 0         0 my $lst = join( q[,], @{ $arg[ 2 ] } );
  0         0  
525            
526 0 0       0 if( _exs( $self->{ grp }, $arg[ 0 ] ) ){
527 0         0 my $sav;
528 0         0 for my $idx ( 0 .. $#ary ){
529 0 0       0 next if $ary[ $idx ][ 0 ] ne $arg[ 0 ];
530 0         0 $ary[ $idx ][ 2 ] = $arg[ 1 ];
531 0         0 $ary[ $idx ][ 3 ] = $lst;
532 0         0 $sav = $idx;
533 0         0 last;
534             }
535 0 0       0 if( $yra[ $sav ][ 0 ] eq $arg[ 0 ] ){
536 0         0 $yra[ $sav ][ 3 ] = $lst;
537             }else{
538 0         0 for my $idx ( 0 .. $#yra ){
539 0 0       0 next if $yra[ $idx ][ 0 ] ne $arg[ 0 ];
540 0         0 $yra[ $idx ][ 3 ] = $lst;
541 0         0 last;
542             }
543             }
544             }else{
545 0         0 push @ary, [
546             $arg[ 0 ],
547             q[x],
548             $arg[ 1 ],
549             $lst
550             ];
551            
552 0         0 push @yra, [
553             $arg[ 0 ],
554             q[!],
555             q[],
556             $lst
557             ];
558             }
559             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
560 0         0 return 1;
561             }
562             #-------------------------------------------------------------------
563             elsif( @arg == 1 ){
564 0 0       0 open( my $fhd, '<', $self->{ grp } ) or die $!;
565 0         0 my @grp = ( undef, [ ] );
566 0         0 while( <$fhd> ){
567 0         0 my @tmp = split /:/;
568 0 0       0 next if $tmp[ 0 ] ne $arg[ 0 ];
569            
570 0         0 chomp $tmp[ 3 ];
571            
572 0         0 $grp[ 0 ] = $tmp[ 2 ];
573 0         0 $grp[ 1 ] = [ split( /\s*,\s*/, $tmp[ 3 ] ) ];
574            
575 0         0 last;
576             }
577 0         0 close( $fhd );
578            
579 0 0       0 return wantarray ? @grp : \@grp;
580             }
581             #-------------------------------------------------------------------
582             }
583             #=======================================================================
584             sub user {
585             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
586 0 0 0 0 1 0 my $self = scalar @_ && ref $_[0] eq __PACKAGE__ ? shift : $Self;
587             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
588 0         0 my ( @arg ) = @_;
589            
590             #-------------------------------------------------------------------
591 0 0       0 if( @arg == 7 ){
    0          
592 0 0       0 return $self->_err( q[Unsufficient permissions.] ) unless open my $fhd, '>>', $self->{ psh };
593 0         0 close( $fhd );
594            
595             #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
596 0 0       0 $self->_bck or return;
597             #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
598            
599             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
600 0         0 my $pwd = $arg[ 1 ];
601 0         0 $arg[ 1 ] = q[x];
602             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
603             tie my @ary, q[Tie::Array::CSV], $self->{ pwd }, {
604 0         0 tie_file => {
605             autochomp => 1,
606             },
607             text_csv => {
608             sep_char => SEP,
609             binary => 1,
610             quote_char => undef,
611             },
612             };
613             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
614             tie my @yra, q[Tie::Array::CSV], $self->{ psh }, {
615 0         0 tie_file => {
616             autochomp => 1,
617             },
618             text_csv => {
619             sep_char => SEP,
620             binary => 1,
621             quote_char => undef,
622             },
623             };
624             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
625 0 0       0 if( _exs( $self->{ pwd }, $arg[ 0 ] ) ){
626 0         0 my $sav;
627 0         0 for my $idx ( 0 .. $#ary ){
628 0 0       0 next if $ary[ $idx ][ 0 ] ne $arg[ 0 ];
629 0         0 $ary[ $idx ][ $_ ] = $arg[ $_ ] for 1 .. 6;
630 0         0 $sav = $idx;
631 0         0 last;
632             }
633             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
634 0 0       0 if( $yra[ $sav ][ 0 ] eq $arg[ 0 ] ){
635 0         0 $yra[ $sav ][ 1 ] = $pwd;
636             }else{
637 0         0 for my $idx ( 0 .. $#yra ){
638 0 0       0 next if $yra[ $idx ][ 0 ] ne $arg[ 0 ];
639 0         0 $yra[ $idx ][ 1 ] = $pwd;
640 0         0 last;
641             }
642             }
643             }else{
644 0         0 push @ary, \@arg;
645 0         0 push @yra, [
646             $arg[ 0 ],
647             $pwd,
648             int( time / DAY ),
649             0,
650             99999,
651             7,
652             q[], q[], q[]
653             ];
654             }
655             #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
656 0         0 return 1;
657             }
658             #-------------------------------------------------------------------
659             elsif( @arg == 1 ){
660 0         0 my @usr;
661 0 0       0 open( my $fhd, '<', $self->{ pwd } ) or die $!;
662 0         0 while( <$fhd> ){
663 0         0 my @tmp = split /:/;
664 0 0       0 next if $tmp[ 0 ] ne $arg[ 0 ];
665 0         0 $usr[ $_ - 1 ] = $tmp[ $_ ] for 1 .. 6;
666 0         0 last;
667             }
668 0         0 close( $fhd );
669            
670 0 0       0 chomp $usr[ $#usr ] if @usr;
671            
672             #if( $> == 0 ){
673 0 0       0 if( open( my $fhd, '<', $self->{ psh } ) ){
674 0         0 while( <$fhd> ){
675 0         0 my @tmp = split /:/;
676 0 0       0 next if $tmp[ 0 ] ne $arg[ 0 ];
677 0         0 $usr[ 0 ] = $tmp[ 1 ];
678 0         0 last;
679             }
680 0         0 close( $fhd );
681             }
682             #}
683            
684 0 0       0 return wantarray ? @usr : \@usr;
685             }
686             #-------------------------------------------------------------------
687             }
688             #=======================================================================
689             sub _unu {
690 0     0   0 my ( $pth, $min, $max ) = @_;
691            
692 0         0 my %all;
693 0 0       0 open( my $fhd, '<', $pth ) or die $!;
694 0         0 while( <$fhd> ){
695 0         0 $all{ ( split /:/ )[ 2 ] } = 1;
696             }
697 0         0 close( $fhd );
698            
699 0         0 for( my $idx = $min; $idx <= $max; $idx++ ){
700 0 0       0 return $idx if not exists $all{ $idx };
701             }
702             }
703             #=======================================================================
704             sub unused_uid {
705             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
706 0 0 0 0 1 0 my $self = scalar @_ && ref $_[0] eq __PACKAGE__ ? shift : $Self;
707             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
708 0         0 my ( $min, $max ) = @_;
709            
710 0   0     0 return _unu( $self->{ pwd }, $min || 0, $max || ( 2 ** ( $Config{ intsize } * 8 ) ) );
      0        
711             }
712             #=======================================================================
713             sub unused_gid {
714             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
715 0 0 0 0 1 0 my $self = scalar @_ && ref $_[0] eq __PACKAGE__ ? shift : $Self;
716             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
717 0         0 my ( $min, $max ) = @_;
718            
719 0   0     0 return _unu( $self->{ grp }, $min || 0, $max || ( 2 ** ( $Config{ intsize } * 8 ) ) );
      0        
720             }
721             #=======================================================================
722             sub _min {
723 0     0   0 my ( $pth, $min ) = @_;
724            
725 0         0 my %all;
726 0 0       0 open( my $fhd, '<', $pth ) or die $!;
727 0         0 while( <$fhd> ){
728 0         0 $all{ ( split /:/ )[ 2 ] } = 1;
729             }
730 0         0 close( $fhd );
731            
732 0         0 for( ;; ){
733 0 0       0 return $min if exists $all{ $min };
734 0         0 $min++;
735             };
736             }
737             #=======================================================================
738             sub minuid {
739             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
740 0 0 0 0 1 0 my $self = scalar @_ && ref $_[0] eq __PACKAGE__ ? shift : $Self;
741             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
742 0         0 my ( $val ) = @_;
743            
744 0   0     0 return _min( $self->{ pwd }, $val || 0 );
745             }
746             #=======================================================================
747             sub mingid {
748             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
749 0 0 0 0 1 0 my $self = scalar @_ && ref $_[0] eq __PACKAGE__ ? shift : $Self;
750             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
751 0         0 my ( $val ) = @_;
752            
753 0   0     0 return _min( $self->{ grp }, $val || 0 );
754             }
755             #=======================================================================
756             sub _max {
757 0     0   0 my ( $pth ) = @_;
758            
759 0         0 my $max = 0;
760 0 0       0 open( my $fhd, '<', $pth ) or die $!;
761 0         0 while( <$fhd> ){
762 0         0 my @tmp = split /:/;
763 0 0       0 $max = $tmp[ 2 ] if $tmp[ 2 ] > $max;
764             }
765 0         0 close( $fhd );
766            
767 0         0 return $max;
768             }
769             #======================================================================
770             sub maxuid {
771             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
772 0 0 0 0 1 0 my $self = scalar @_ && ref $_[0] eq __PACKAGE__ ? shift : $Self;
773             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
774            
775 0         0 return _max( $self->{ pwd } );
776             }
777             #======================================================================
778             sub maxgid {
779             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
780 0 0 0 0 1 0 my $self = scalar @_ && ref $_[0] eq __PACKAGE__ ? shift : $Self;
781             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
782            
783 0         0 return _max( $self->{ grp } );
784             }
785             #=======================================================================
786             sub _exs {
787 0     0   0 my ( $pth, $val, $pos ) = @_;
788            
789 0   0     0 $pos //= 0;
790            
791 0 0       0 open( my $fhd, '<', $pth ) or die $!;
792 0         0 while( <$fhd> ){
793 0         0 my @tmp = split /:/;
794 0 0       0 return 1 if $tmp[ $pos ] eq $val;
795             }
796 0         0 close( $fhd );
797              
798 0         0 return;
799             }
800             #======================================================================
801             sub exists_user {
802             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
803 0 0 0 0 1 0 my $self = scalar @_ && ref $_[0] eq __PACKAGE__ ? shift : $Self;
804             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
805 0         0 my ( $val ) = @_;
806            
807 0 0       0 return unless defined $val;
808 0         0 return _exs( $self->{ pwd }, $val );
809             }
810             #======================================================================
811             sub exists_group {
812             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
813 0 0 0 0 1 0 my $self = scalar @_ && ref $_[0] eq __PACKAGE__ ? shift : $Self;
814             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
815 0         0 my ( $val ) = @_;
816            
817 0 0       0 return unless defined $val;
818 0         0 return _exs( $self->{ grp }, $val );
819             }
820             #=======================================================================
821             sub reset {
822             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
823 0 0 0 0 1 0 my $self = scalar @_ && ref $_[0] eq __PACKAGE__ ? shift : $Self;
824             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
825            
826 0         0 $self->passwd_file ( PWD );
827 0         0 $self->group_file ( GRP );
828 0         0 $self->shadow_file ( PSH );
829 0         0 $self->gshadow_file ( GSH );
830            
831 0         0 return 1;
832             }
833             #=======================================================================
834             sub encpass {
835             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
836 0 0 0 0 1 0 my $self = scalar @_ && ref $_[0] eq __PACKAGE__ ? shift : $Self;
837             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
838 0         0 my ( $val ) = @_;
839            
840 0 0       0 return unless defined $val;
841 0         0 return password( $val, undef, $self->{ alg } );
842             }
843             #=======================================================================
844             sub algorithm {
845             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
846 3 50 33 3 1 29 my $self = scalar @_ && ref $_[0] eq __PACKAGE__ ? shift : $Self;
847             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
848 3         6 my ( $val ) = @_;
849            
850 3 50       10 return $self->{ alg } unless defined $val;
851              
852 3 100       15 my $alg = $val eq q[md5] ? $val :
    50          
    50          
853             $val eq q[blowfish] ? $val :
854             $val eq q[sha256] ? $val : q[sha512];
855            
856 3         28 $self->{ alg } = $alg;
857            
858 3         12 return $self->{ alg };
859             }
860             #=======================================================================
861             sub default_umask {
862             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
863 3 50 33 3 1 14 my $self = scalar @_ && ref $_[0] eq __PACKAGE__ ? shift : $Self;
864             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
865 3         6 my ( $val ) = @_;
866            
867 3 50       9 return $self->{ msk } unless defined $val;
868            
869 3 50       18 $self->{ msk } = $val ? 1 : 0;
870            
871 3         6 return $self->{ msk };
872             }
873             #=======================================================================
874             sub backup {
875             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
876 3 50 33 3 1 28 my $self = scalar @_ && ref $_[0] eq __PACKAGE__ ? shift : $Self;
877             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
878 3         7 my ( $val ) = @_;
879            
880 3 50       12 return $self->{ bck } unless defined $val;
881            
882 3 100       10 $self->{ bck } = $val ? 1 : 0;
883            
884 3         6 return $self->{ bck };
885             }
886             #=======================================================================
887             sub compress {
888             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
889 3 50 33 3 1 19 my $self = scalar @_ && ref $_[0] eq __PACKAGE__ ? shift : $Self;
890             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
891 3         7 my ( $val ) = @_;
892            
893 3 50       9 return $self->{ cmp } unless defined $val;
894            
895 3 100       9 $self->{ cmp } = $val ? 1 : 0;
896            
897 3         6 return $self->{ cmp };
898             }
899             #=======================================================================
900             sub warnings {
901             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
902 3 50 33 3 1 66 my $self = scalar @_ && ref $_[0] eq __PACKAGE__ ? shift : $Self;
903             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
904 3         12 my ( $val ) = @_;
905            
906 3 50       9 return $self->{ wrn } unless defined $val;
907            
908 3 100       18 $self->{ wrn } = $val ? 1 : 0;
909            
910 3         7 return $self->{ wrn };
911             }
912             #=======================================================================
913             sub debug {
914             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
915 3 50 33 3 1 21 my $self = scalar @_ && ref $_[0] eq __PACKAGE__ ? shift : $Self;
916             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
917 3         9 my ( $val ) = @_;
918            
919 3 50       21 return $self->{ dbg } unless defined $val;
920            
921 3 50       13 $self->{ dbg } = $val ? 1 : 0;
922            
923 3         5 return $self->{ dbg };
924             }
925             #=======================================================================
926             sub error {
927             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
928 0 0 0 0 1 0 my $self = scalar @_ && ref $_[0] eq __PACKAGE__ ? shift : $Self;
929             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
930 0         0 my ( $val ) = @_;
931            
932 0         0 return $self->{ err };
933             }
934             #=======================================================================
935             sub passwd_file {
936             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
937 3 50 33 3 1 22 my $self = scalar @_ && ref $_[0] eq __PACKAGE__ ? shift : $Self;
938             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
939 3         7 my ( $val ) = @_;
940            
941 3 50       11 return $self->{ pwd } unless defined $val;
942            
943 3         20 my $pth = path( $val );
944 3 50       202 die q[Password file cannot be a directory.] if $pth->is_dir;
945            
946             #$pth->touchpath unless $pth->exists;
947            
948 3         218 $self->{ pwd } = $pth->absolute->canonpath;
949            
950 3         333 return $self->{ pwd };
951             }
952             #=======================================================================
953             sub group_file {
954             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
955 3 50 33 3 1 45 my $self = scalar @_ && ref $_[0] eq __PACKAGE__ ? shift : $Self;
956             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
957 3         9 my ( $val ) = @_;
958            
959 3 50       13 return $self->{ grp } unless defined $val;
960            
961 3         17 my $pth = path( $val );
962 3 50       119 die q[Group file cannot be a directory.] if $pth->is_dir;
963            
964             #$pth->touchpath unless $pth->exists;
965            
966 3         67 $self->{ grp } = $pth->absolute->canonpath;
967            
968 3         165 return $self->{ grp };
969             }
970             #=======================================================================
971             sub shadow_file {
972             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
973 3 50 33 3 1 29 my $self = scalar @_ && ref $_[0] eq __PACKAGE__ ? shift : $Self;
974             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
975 3         7 my ( $val ) = @_;
976            
977 3 50       9 return $self->{ psh } unless defined $val;
978            
979 3         15 my $pth = path( $val );
980 3 50       112 die q[Shadowed passwd file (aka "shadow") file cannot be a directory.] if $pth->is_dir;
981            
982             #$pth->touchpath unless $pth->exists;
983            
984 3         93 $self->{ psh } = $pth->absolute->canonpath;
985            
986 3         146 return $self->{ psh };
987             }
988             #=======================================================================
989             sub gshadow_file {
990             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
991 3 50 33 3 1 19 my $self = scalar @_ && ref $_[0] eq __PACKAGE__ ? shift : $Self;
992             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
993 3         9 my ( $val ) = @_;
994            
995 3 50       9 return $self->{ gsh } unless defined $val;
996            
997 3         10 my $pth = path( $val );
998 3 50       108 die q[Shadowed group file (aka "gshadow") file cannot be a directory.] if $pth->is_dir;
999            
1000             #$pth->touchpath unless $pth->exists;
1001            
1002 3         87 $self->{ gsh } = $pth->absolute->canonpath;
1003            
1004 3         154 return $self->{ gsh };
1005             }
1006             #=======================================================================
1007             sub _lst {
1008 2     2   6 my ( $pth ) = @_;
1009            
1010 2         4 my @ary;
1011 2 50       94 open( my $fhd, '<', $pth ) or die $!;
1012 2         102 push @ary, ( split( /:/, $_ ) )[ 0 ] while <$fhd>;
1013 2         23 close($fhd);
1014            
1015 2 50       31 return wantarray ? @ary : \@ary;
1016             }
1017             #=======================================================================
1018             sub groups {
1019             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1020 0 0 0 0 1 0 my $self = scalar @_ && ref $_[0] eq __PACKAGE__ ? shift : $Self;
1021             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1022 0         0 return _lst( $self->{ grp } );
1023             }
1024             #=======================================================================
1025             sub groups_from_gshadow {
1026             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1027 0 0 0 0 1 0 my $self = scalar @_ && ref $_[0] eq __PACKAGE__ ? shift : $Self;
1028             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1029 0         0 return _lst( $self->{ gsh } );
1030             }
1031             #=======================================================================
1032             sub users {
1033             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1034 1 50 33 1 1 1414 my $self = scalar @_ && ref $_[0] eq __PACKAGE__ ? shift : $Self;
1035             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1036 1         4 return _lst( $self->{ pwd } );
1037             }
1038             #=======================================================================
1039             sub check_sanity {
1040 0     0 1 0 return 1;
1041             }
1042             #=======================================================================
1043             sub users_from_shadow {
1044             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1045 1 50 33 1 1 308 my $self = scalar @_ && ref $_[0] eq __PACKAGE__ ? shift : $Self;
1046             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1047 1         5 return _lst( $self->{ psh } );
1048             }
1049             #======================================================================
1050              
1051             =head1 NAME
1052              
1053             Passwd::Unix - access to standard unix files
1054              
1055             =head1 SYNOPSIS
1056              
1057             use Passwd::Unix;
1058            
1059             my $pu = Passwd::Unix->new;
1060            
1061             my $err = $pu->user(
1062             "example",
1063             $pu->encpass("my_secret"),
1064             $pu->unused_uid,
1065             $pu->unused_gid,
1066             "My User",
1067             "/home/example",
1068             "/bin/bash"
1069             );
1070            
1071             $pu->passwd("example", $pu->encpass( "newsecret") );
1072             foreach my $user ($pu->users) {
1073             print "Username: $user\nFull Name: ", $pu->gecos($user), "\n\n";
1074             }
1075            
1076             my $uid = $pu->uid('example');
1077             $pu->del("example");
1078              
1079             # or
1080              
1081             use Passwd::Unix qw(
1082             algorithm backup check_sanity compress del del_group del_user
1083             encpass exists_group exists_user gecos gid group group_file
1084             groups groups_from_gshadow home maxgid maxuid mingid minuid
1085             passwd passwd_file rename reset shadow_file shell uid user
1086             users users_from_shadow warnings
1087             );
1088            
1089             my $err = user( "example", encpass("my_secret"), unused_uid(), unused_gid(),
1090             "My User", "/home/example", "/bin/bash" );
1091             passwd("example",encpass("newsecret"));
1092             foreach my $user ( users() ) {
1093             print "Username: $user\nFull Name: ", gecos( $user ), "\n\n";
1094             }
1095            
1096             my $uid = uid( 'example' );
1097             del( 'example' );
1098              
1099             =head1 ABSTRACT
1100              
1101             Passwd::Unix provides an abstract object-oriented and function interface to
1102             standard Unix files, such as /etc/passwd, /etc/shadow, /etc/group. Additionally
1103             this module provides environment for testing software without using
1104             system critical files in /etc/ dir (you can specify other files than
1105             /etc/passwd etc.).
1106              
1107             =head1 DESCRIPTION
1108              
1109             The Passwd::Unix module provides an abstract interface to /etc/passwd,
1110             /etc/shadow, /etc/group, /etc/gshadow format files. It is inspired by
1111             Unix::PasswdFile module (that one does not handle /etc/shadow file).
1112              
1113             B
1114             newer hash algorithms and so on), however with compatibility in mind.
1115             Despite this some incompatibilities can occur.>
1116              
1117             =head1 SUBROUTINES/METHODS
1118              
1119             =over 4
1120              
1121             =item B 1, param1 => 0... ] )>
1122              
1123             Constructor. Possible parameters are:
1124              
1125             =over 8
1126              
1127             =item B - path to passwd file; default C
1128              
1129             =item B - path to shadow file; default C
1130              
1131             =item B - path to group file; default C
1132              
1133             =item B - path to gshadow file if any; default C
1134              
1135             =item B - hash algorithm, possible values: md5, blowfish, sha256, sha512; default C
1136              
1137             =item B - not used anymore; left only for compatibility reason
1138              
1139             =item B - not used anymore; left only for compatibility reason
1140              
1141             =item B - boolean; if set to C<1>, backup will be made; default C<1>
1142              
1143             =item B - boolean; if set to C<1>, backup compression will be made; default C<1>
1144              
1145             =item B - boolean; if set to C<1>, important warnings will be displayed; default C<0>
1146              
1147             =back
1148              
1149             =item B
1150              
1151             This method allows to specify algorithm for password generation. Possible values: C, C, C, C
1152              
1153             =item B
1154              
1155             This method allows to specify if backups files have to be made before every modyfication (C<1> for on, C<0> for off).
1156              
1157             =item B
1158              
1159             This method allows to specify if compression to backup files has to be made (C<1> for on, C<0> for off).
1160              
1161             =item B
1162              
1163             This function was left only for compatibility reason. Currently it does nothing (always returns 1).
1164              
1165             =item B
1166              
1167             This function was left only for compatibility reason. Currently it does nothing.
1168              
1169             =item B
1170              
1171             This function was left only for compatibility reason. Currently it does nothing.
1172              
1173             =item B
1174              
1175             This method is an alias for C. It's for transition only.
1176              
1177             =item B
1178              
1179             This method will delete the list of users. It has no effect if the
1180             supplied users do not exist.
1181              
1182             =item B
1183              
1184             This method will delete the list of groups. It has no effect if the
1185             supplied groups do not exist.
1186              
1187             =item B
1188              
1189             This method will encrypt plain text into unix style password.
1190              
1191             =item B
1192              
1193             This method returns the last error (even if "warnings" is disabled).
1194              
1195             =item B
1196              
1197             This method checks if specified user exists. It returns C on failure and C<1> on success.
1198              
1199             =item B
1200              
1201             This method checks if specified group exists. It returns C on failure and C<1> on success.
1202              
1203             =item B
1204              
1205             Read or modify a user's GECOS string (typically full name).
1206             Returns the result of operation (C<1> or C) if GECOS was specified.
1207             Otherwhise returns the GECOS if any.
1208              
1209             =item B
1210              
1211             Read or modify a user's GID. Returns the result of operation (C<1> or C) if GID was specified otherwhise returns the GID if any.
1212              
1213             =item B
1214              
1215             This method can add, modify, or return information about a group.
1216             Supplied with a single groupname parameter, it will return a two element
1217             list consisting of (GID, ARRAYREF), where ARRAYREF is a ref to array
1218             consisting names of users in this GROUP. It will return undef and ref to empty array (C) if no such group
1219             exists. If you supply all three parameters, the named group will be
1220             created or modified if it already exists.
1221              
1222             =item B
1223              
1224             This method, if called with an argument, sets path to the I file.
1225             Otherwise returns the current PATH.
1226              
1227             =item B
1228              
1229             This method returns a list of all existing groups.
1230              
1231             =item B
1232              
1233             This method returns a list of all existing groups in a gshadow file.
1234              
1235             =item B
1236              
1237             This method, if called with an argument, sets path to the I file.
1238             Otherwise returns the current PATH.
1239              
1240             =item B
1241              
1242             Read or modify a user's home directory. Returns the result of operation
1243             (C<1> or C) if HOMEDIR was specified otherwhise returns the HOMEDIR if any.
1244              
1245             =item B
1246              
1247             This method returns the maximum UID in use.
1248              
1249             =item B
1250              
1251             This method returns the maximum GID in use.
1252              
1253             =item B
1254              
1255             This method returns the minimum UID in use, that is greater then spupplied.
1256              
1257             =item B
1258              
1259             This method returns the minimum GID in use, that is greater then spupplied.
1260              
1261             =item B
1262              
1263             Read or modify a user's password. If you have a plaintext password,
1264             use the encpass method to encrypt it before passing it to this method.
1265             Returns the result of operation (C<1> or C) if PASSWD was specified.
1266             Otherwhise returns the PASSWD if any.
1267              
1268             =item B
1269              
1270             This method, if called with an argument, sets path to the I file.
1271             Otherwise returns the current PATH.
1272              
1273             =item B
1274              
1275             This method changes the username for a user. If NEWNAME corresponds to
1276             an existing user, that user will be overwritten. It returns C on
1277             failure and C<1> on success.
1278              
1279             =item B
1280              
1281             This method sets paths to files I, I, I, I to the
1282             default values.
1283              
1284             =item B
1285              
1286             Read or modify a user's shell. Returns the result of operation (C<1> or C) if SHELL was specified otherwhise returns the SHELL if any.
1287              
1288             =item B
1289              
1290             Read or modify a user's UID. Returns the result of operation (C<1> or C) if UID was specified otherwhise returns the UID if any.
1291              
1292             =item B
1293              
1294             This method can add, modify, or return information about a user.
1295             Supplied with a single username parameter, it will return a six element
1296             list consisting of (PASSWORD, UID, GID, GECOS, HOMEDIR, SHELL), or
1297             undef if no such user exists. If you supply all seven parameters,
1298             the named user will be created or modified if it already exists.
1299              
1300             =item B
1301              
1302             This method returns a list of all existing usernames.
1303              
1304             =item B
1305              
1306             This method returns a list of all existing usernames in a shadow file.
1307              
1308             =item B
1309              
1310             This method, if called with an argument, sets path to the I file.
1311             Otherwise returns the current PATH.
1312              
1313             =item B
1314              
1315             This method returns the first unused UID in a given range. The default MINUID is 0. The default MAXUID is maximal integer value (computed from C<$Config{ intsize }> ).
1316              
1317             =item B
1318              
1319             This method returns the first unused GID in a given range. The default MINGID is 0. The default MAXGID is maximal integer value (computed from C<$Config{ intsize }> ).
1320              
1321             =item B
1322              
1323             This method allows to specify if warnings has to be displayed (C<1> for on, C<0> for off). Whether you can check last warning/failure by calling C.
1324              
1325             =back
1326              
1327             =head1 DEPENDENCIES
1328              
1329             =over 4
1330              
1331             =item Crypt::Password
1332              
1333             =item IO::Compress::Bzip2
1334              
1335             =item Path::Tiny
1336              
1337             =item Tie::Array::CSV
1338              
1339             =back
1340              
1341             =head1 TODO
1342              
1343             Preparation of tests.
1344              
1345             =head1 INCOMPATIBILITIES
1346              
1347             None known.
1348              
1349             =head1 BUGS AND LIMITATIONS
1350              
1351             None. I hope.
1352              
1353             =head1 THANKS
1354              
1355             =over 4
1356              
1357             =item Thanks to Jonas Genannt for many suggestions and patches!
1358              
1359             =item Thanks to Christian Kuelker for suggestions and reporting some bugs :-).
1360              
1361             =item Thanks to Steven Haryanto for suggestions.
1362              
1363             =item BIG THANKS to Lopes Victor for reporting some bugs and his exact sugesstions :-)
1364              
1365             =item Thanks to Foudil BRÉTEL for some remarks, suggestions as well as supplying relevant patch!
1366              
1367             =item BIG thanks to Artem Russakovskii for reporting a bug.
1368              
1369             =back
1370              
1371             =head1 AUTHOR
1372              
1373             Strzelecki Lukasz
1374              
1375             =head1 LICENCE AND COPYRIGHT
1376              
1377             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
1378              
1379             See http://www.perl.com/perl/misc/Artistic.html