File Coverage

blib/lib/Unix/Mgt.pm
Criterion Covered Total %
statement 135 457 29.5
branch 15 148 10.1
condition 0 3 0.0
subroutine 36 73 49.3
pod 0 8 0.0
total 186 689 27.0


line stmt bran cond sub pod time code
1             ###############################################################################
2             # Unix::Mgt
3             #
4             package Unix::Mgt;
5 1     1   735 use strict;
  1         2  
  1         27  
6 1     1   955 use IPC::System::Simple 'runx';
  1         14653  
  1         67  
7 1     1   890 use Capture::Tiny 'capture_merged';
  1         49790  
  1         76  
8 1     1   954 use String::Util qw{define nocontent rtrim};
  1         3315  
  1         86  
9 1     1   784 use Unix::SearchPathGuess 'cmd_path_guess';
  1         2137  
  1         54  
10 1     1   5 use Carp 'croak';
  1         2  
  1         58  
11              
12             # debug tools
13             # use Debug::ShowStuff ':all';
14             # use Debug::ShowStuff::ShowVar;
15              
16             # version
17             our $VERSION = '0.14';
18              
19              
20             #------------------------------------------------------------------------------
21             # export
22             #
23 1     1   4 use base 'Exporter';
  1         2  
  1         59  
24 1     1   5 use vars qw[@EXPORT_OK %EXPORT_TAGS];
  1         2  
  1         649  
25             push @EXPORT_OK, qw{unix_mgt_err unix_mgt_err_id};
26             %EXPORT_TAGS = ('all' => [@EXPORT_OK]);
27             #
28             # export
29             #------------------------------------------------------------------------------
30              
31              
32             #------------------------------------------------------------------------------
33             # opening POD
34             #
35              
36             =head1 NAME
37              
38             Unix::Mgt - lightweight Unix management tools
39              
40             =head1 SYNOPSIS
41              
42             # get user account
43             $user = Unix::Mgt::User->get('fred');
44              
45             # display some info
46             print 'uid: ', $user->uid, "\n";
47             print join(', ', $user->groups()), "\n";
48              
49             # set some properties
50             $user->gid('websters');
51             $user->shell('/bin/bash');
52             $user->add_to_group('postgres');
53              
54             # create user account
55             $user = Unix::Mgt::User->create('vera');
56              
57             # get user account, creating it if necessary
58             $user = Unix::Mgt::User->ensure('molly');
59              
60             # get group
61             $group = Unix::Mgt::Group->get('www-data');
62              
63             # display some info
64             print 'gid: ', $group->gid, "\n";
65             print join(', ', $group->members()), "\n";
66              
67             # add a member
68             $group->add_member('tucker');
69              
70             =head1 DESCRIPTION
71              
72             Unix::Mgt provides simple object-oriented tools for managing your Unixish
73             system. Currently this module provides tools for managing users and groups.
74             Other tools may follow as they evolve.
75              
76             Unix::Mgt does not directly manipulate any of the system files such as
77             C. This module uses Perl's built-in Unix functions such as
78             C to get information, and Unix's built-in programs such as
79             C.
80              
81             =head2 Early release
82              
83             In the spirit of "release early, release often", I'm releasing this version
84             of Unix::Mgt before it has all the features that might be expected. This
85             version does not include methods for removing users from groups, renaming
86             users or groups, or several other methods.
87              
88             =cut
89              
90             #
91             # opening POD
92             #------------------------------------------------------------------------------
93              
94              
95             #------------------------------------------------------------------------------
96             # error id and message globals
97             #
98             our $err_id;
99             our $err_msg;
100             #
101             # error id and message globals
102             #------------------------------------------------------------------------------
103              
104              
105             #------------------------------------------------------------------------------
106             # set_err, reset_err
107             #
108             sub set_err {
109 4     4 0 11 my ($class, $id, $msg) = @_;
110 4         8 $err_id = $id;
111 4         8 $err_msg = $msg;
112 4         15 return undef;
113             }
114              
115             sub reset_err {
116 6     6 0 12 undef $err_id;
117 6         11 undef $err_msg;
118             }
119              
120             sub unix_mgt_err {
121 0 0   0 0 0 if ($err_id)
122 0         0 { return $err_id . rtrim(': ' . define($err_msg))}
123             else
124 0         0 { return '' }
125             }
126              
127             sub unix_mgt_err_id {
128 0 0   0 0 0 if ($err_id)
129 0         0 { return $err_id}
130             else
131 0         0 { return '' }
132             }
133             #
134             # reset_err
135             #------------------------------------------------------------------------------
136              
137              
138             #------------------------------------------------------------------------------
139             # called_sub
140             #
141             sub called_sub {
142 4     4 0 8 my (@caller, $sub_name);
143            
144             # TESTING
145             # println subname(class=>1); ##i
146            
147             # get caller info
148 4         41 @caller = caller(1);
149            
150             # get subroutine name and make it look like a method call
151 4         10 $sub_name = $caller[3];
152 4         47 $sub_name =~ s|^(.*)\:\:|$1\-\>|s;
153 4         10 $sub_name .= '()';
154            
155             # return
156 4         53 return $sub_name;
157             }
158             #
159             # called_sub
160             #------------------------------------------------------------------------------
161              
162              
163             #------------------------------------------------------------------------------
164             # even_odd_params
165             #
166             sub even_odd_params {
167 6     6 0 12 my $class = shift(@_);
168 6         12 my $id = shift(@_);
169 6         10 my ($name, %opts);
170            
171             # get params: even number means all params (except class) are options,
172             # odd number means first param is id
173 6 100       23 if (@_ % 2) {
174 4         15 ($name, %opts) = @_;
175             }
176             else {
177 2         5 %opts = @_;
178 2         6 $name = delete($opts{$id});
179             }
180            
181             # return
182 6         24 return ($name, %opts);
183             }
184             #
185             # even_odd_params
186             #------------------------------------------------------------------------------
187              
188              
189             #------------------------------------------------------------------------------
190             # run_cmd
191             #
192             sub run_cmd {
193 0     0 0 0 my ($class, $err_id_use, $cmd_id, @args) = @_;
194 0         0 my ($cmd, $out, $rv);
195            
196             # TESTING
197             # println subname(method=>1); ##i
198            
199             # get command
200 0         0 $cmd = cmd_path_guess($cmd_id);
201 0 0       0 $cmd or croak "do not find path for command $cmd_id";
202            
203             # run command
204             $out = capture_merged{
205 0     0   0 $rv = runx(IPC::System::Simple::EXIT_ANY, $cmd, @args);
206 0         0 };
207            
208             # if error
209 0 0       0 if ($rv) {
210 0         0 return $class->set_err(
211             $err_id_use,
212             "error running program $cmd: " . $out,
213             );
214             }
215            
216             # return success
217 0         0 return 1;
218             }
219             #
220             # run_cmd
221             #------------------------------------------------------------------------------
222              
223              
224             #------------------------------------------------------------------------------
225             # use_bsd
226             #
227             our ($use_bsd);
228              
229             sub use_bsd {
230 1     1 0 7683 my ($ug) = @_;
231 1         4 my $use_bsd = \$Unix::Mgt::use_bsd;
232            
233             # TESTING
234             # println subname(method=>1); ##i
235            
236             # if cached, use that
237 1 50       6 if (defined $$use_bsd)
238 0         0 { return $$use_bsd }
239            
240             # else get the command, cache, and return
241 1         6 $$use_bsd = cmd_path_guess('pw');
242 1         649858 return $$use_bsd;
243             }
244             #
245             # use_bsd
246             #------------------------------------------------------------------------------
247              
248              
249             #
250             # Unix::Mgt
251             ###############################################################################
252              
253              
254              
255             ###############################################################################
256             # Unix::Mgt::UGCommon
257             #
258             package Unix::Mgt::UGCommon;
259 1     1   5 use strict;
  1         2  
  1         25  
260 1     1   5 use String::Util ':all';
  1         2  
  1         226  
261 1     1   4 use Carp 'croak';
  1         2  
  1         57  
262 1     1   5 use Unix::SearchPathGuess 'cmd_path_guess';
  1         1  
  1         42  
263 1     1   4 use base 'Unix::Mgt';
  1         2  
  1         127  
264              
265             # debug tools
266             # use Debug::ShowStuff ':all';
267              
268              
269             #------------------------------------------------------------------------------
270             # object overloading
271             #
272             use overload
273 2     2   946 '""' => sub{$_[0]->{'name'}}, # stringification
274 1     1   17 fallback => 1; # operations not defined here
  1         2  
  1         13  
275             #
276             # object overloading
277             #------------------------------------------------------------------------------
278              
279              
280             #------------------------------------------------------------------------------
281             # normalize_name
282             #
283             sub normalize_name {
284 4     4   9 my ($class, $name) = @_;
285            
286             # TESTING
287             # println subname(method=>1), ' $name: ', $name; ##i
288            
289             # if defined, remove eerything after first space
290 4 50       10 if (defined $name) {
291 4         8 $name =~ s|\s.*||sg;
292             }
293            
294             # TESTING
295             # showvar $name;
296            
297             # return
298 4         16 return $name;
299             }
300             #
301             # normalize_name
302             #------------------------------------------------------------------------------
303              
304              
305             #------------------------------------------------------------------------------
306             # name_check
307             #
308             sub name_check {
309 6     6   16 my ($class, $name, $id) = @_;
310            
311             # TESTING
312             # println subname(method=>1); ##i
313            
314             # if name does not have content, that's an error
315 6 100       96 if (nocontent $name) {
316 2         37 return $class->set_err(
317             $id,
318             $class->called_sub() . ' requires a user name parameter'
319             );
320             }
321            
322             # normalize
323 4         55 $name = $class->normalize_name($name);
324            
325             # return
326 4         6 return $name;
327             }
328             #
329             # name_check
330             #------------------------------------------------------------------------------
331              
332              
333             #------------------------------------------------------------------------------
334             # mod_only
335             #
336             sub mod_only {
337 0     0   0 my ($class, $name) = @_;
338 0         0 my ($only);
339            
340             # TESTING
341             # println subname(method=>1); ##i
342            
343             # get class
344 0 0       0 if (ref $class)
345 0         0 { $class = ref($class) }
346            
347             # get hash with destrictions
348             # KLUDGE: This is an awkward way to get the variable, but I didn't want
349             # to remember how to work through package hashes.
350 0 0       0 if ( $class eq 'Unix::Mgt::User' )
    0          
351 0         0 { $only = $Unix::Mgt::User::MOD_ONLY }
352             elsif ($class eq 'Unix::Mgt::Group')
353 0         0 { $only = $Unix::Mgt::Group::MOD_ONLY }
354             else
355 0         0 { croak qq|do not know package "$class" for mod restrictions | }
356            
357             # if $only is defined, name must be in the hash
358 0 0       0 if ($only) {
359             # deref
360 0 0       0 if (ref $name)
361 0         0 { $name = $name->{'name'} }
362            
363             # if no content in name, fail
364 0 0       0 if (nocontent $name)
365 0         0 { croak 'no content in $name' }
366            
367 0 0       0 if (! exists($only->{$name})) {
368 0         0 croak qq|cannot modify user "$name"|;
369             }
370             }
371            
372             # else it's ok to mod that user
373 0         0 return 1;
374             }
375             #
376             # mod_only
377             #------------------------------------------------------------------------------
378              
379              
380             #------------------------------------------------------------------------------
381             # ensure
382             #
383             sub ensure {
384 0     0   0 my $class = shift(@_);
385 0         0 my ($name, %opts) = $class->even_odd_params('name', @_);
386            
387             # TESTING
388             # println subname(method=>1); ##i
389            
390             # reset error globals
391 0         0 $class->reset_err();
392            
393             # check and normalize name
394 0         0 $name = $class->name_check($name, 'missing-user-name');
395 0 0       0 $name or return undef;
396            
397             # if user exists, return get method
398 0 0       0 if (my @fields = $class->fields($name)) {
399 0         0 return $class->get($name, fields=>\@fields)
400             }
401            
402             # else return create
403             else {
404 0         0 return $class->create($name);
405             }
406             }
407             #
408             # ensure
409             #------------------------------------------------------------------------------
410              
411              
412             #
413             # Unix::Mgt::UGCommon
414             ###############################################################################
415              
416              
417              
418             ###############################################################################
419             # Unix::Mgt::User
420             #
421             package Unix::Mgt::User;
422 1     1   536 use strict;
  1         2  
  1         21  
423 1     1   5 use Carp 'croak';
  1         2  
  1         42  
424 1     1   5 use String::Util ':all';
  1         1  
  1         213  
425 1     1   5 use Unix::SearchPathGuess 'cmd_path_guess';
  1         2  
  1         37  
426 1     1   5 use IPC::System::Simple 'capturex';
  1         2  
  1         43  
427 1     1   5 use base 'Unix::Mgt::UGCommon';
  1         1  
  1         3041  
428              
429             # debug tools
430             # use Debug::ShowStuff ':all';
431              
432             # safety mechanism for development
433             our $MOD_ONLY;
434              
435              
436             #------------------------------------------------------------------------------
437             # POD
438             #
439              
440             =head1 Unix::Mgt::User
441              
442             A Unix::Mgt::User object represents a user in the Unix system. The object
443             allows you to get and set information about the user account. A user object
444             is created in one of three ways: C, C, or C. The C
445             method is an alias for C.
446              
447             Unix::Mgt::User objects stringify to the account's name. For example, the
448             following code would output C.
449              
450             $user = Unix::Mgt::User->get('miko');
451             print $user, "\n";
452              
453             =cut
454              
455             #
456             # POD
457             #------------------------------------------------------------------------------
458              
459              
460             #------------------------------------------------------------------------------
461             # field_names
462             #
463             our @field_names = qw{
464             name
465             passwd
466             uid
467             gid
468             quota
469             comment
470             gecos
471             dir
472             shell
473             expire
474             };
475             #
476             # field_names
477             #------------------------------------------------------------------------------
478              
479              
480              
481             #------------------------------------------------------------------------------
482             # get
483             #
484              
485             =head2 get
486              
487             Unix::Mgt::User->get() retrieves user account information using C or
488             C. The single param for this method is either the name or the uid of
489             the user.
490              
491             $user = Unix::Mgt::User->get('vera');
492             $user = Unix::Mgt::User->get('1010');
493              
494             If the user is not found then the C error id is set in
495             C<$Unix::Mgt::err_id> and undef is returned.
496              
497             =cut
498              
499             # alias new to get
500             sub new {
501 0     0   0 my $class = shift(@_);
502 0         0 return $class->get(@_);
503             }
504              
505             sub get {
506 4     4   15954 my $class = shift(@_);
507 4         29 my ($name, %opts) = $class->even_odd_params('name', @_);
508 4         7 my (@fields, $user);
509            
510             # TESTING
511             # println subname(method=>1); ##i
512            
513             # reset error globals
514 4         20 $class->reset_err();
515            
516             # check and normalize name
517 4         19 $name = $class->name_check($name, 'missing-user-name');
518 4 100       25 $name or return undef;
519            
520             # get fields
521 3         11 @fields = $class->fields($name);
522            
523             # if user exists, get name, else throw error
524 3 100       14 if (@fields) {
525 2         6 $name = $fields[0];
526             }
527             else {
528 1         7 return $class->set_err(
529             'do-not-have-user',
530             $class->called_sub() . qq|: do not find a user with name "$name"|,
531             );
532             }
533            
534             # create object
535 2         6 $user = bless({}, $class);
536            
537             # hold on to name
538 2         94 $user->{'name'} = $name;
539            
540             # return
541 2         10 return $user;
542             }
543             #
544             # get
545             #------------------------------------------------------------------------------
546              
547              
548             #------------------------------------------------------------------------------
549             # entry
550             #
551             sub entry {
552 0     0   0 my ($user) = @_;
553 0         0 my (@fields, %entry);
554            
555             # TESTING
556             # println subname(method=>1); ##i
557            
558             # get fields
559 0         0 @fields = $user->fields($user->{'name'});
560            
561             # if no fields, set error and return undef
562 0 0       0 if (! @fields) {
563             return $user->set_err(
564             'do-not-have-user-entry-anymore',
565 0         0 $user->called_sub() . ': do not have a user with name "' . $user->{'name'} . '"',
566             );
567             }
568            
569             # set hash
570 0         0 @entry{@field_names} = @fields;
571            
572             # return
573 0         0 return \%entry;
574             }
575             #
576             # entry
577             #------------------------------------------------------------------------------
578              
579              
580             #------------------------------------------------------------------------------
581             # fields
582             #
583             sub fields {
584 3     3   4 my ($class, $name) = @_;
585            
586             # TESTING
587             # println subname(method=>1); ##i
588            
589             # return
590 3 50       11 if ($name =~ m|^\d+$|s)
591 0         0 { return getpwuid($name) }
592             else
593 3         3742 { return getpwnam($name) }
594             }
595             #
596             # fields
597             #------------------------------------------------------------------------------
598              
599              
600             #------------------------------------------------------------------------------
601             # create
602             #
603              
604             =head2 create
605              
606             Unix::Mgt::User->create() creates a user account. The required param for this
607             method is the name for the new account.
608              
609             $user = Unix::Mgt::User->create('vera');
610              
611             If the C param is true, then the account is created as a system user,
612             like this:
613              
614             $user = Unix::Mgt::User->create('lanny', system=>1);
615              
616             create() uses the Unix C program.
617              
618             =cut
619              
620             sub create {
621 0     0   0 my $class = shift(@_);
622 0         0 my ($name, %opts) = $class->even_odd_params('name', @_);
623 0         0 my ($user, @cmd);
624            
625             # TESTING
626             # println subname(method=>1); ##i
627            
628             # reset error globals
629 0         0 $class->reset_err();
630            
631             # check and normalize name
632 0         0 $name = $class->name_check($name, 'missing-user-name');
633 0 0       0 $name or return undef;
634            
635             # if user exists, throw error
636 0 0       0 if ($class->fields($name)) {
637 0         0 return $class->set_err(
638             'already-have-user',
639             $class->called_sub() . qq|: already have a user with name "$name"|,
640             );
641             }
642            
643             # safety check
644 0         0 $class->mod_only($name);
645            
646             # BSD style
647 0 0       0 if ($class->use_bsd) {
648 0         0 @cmd = (
649             'pw',
650             'useradd',
651             '-n',
652             'user-1',
653             '-m'
654             );
655             }
656            
657             # else Linux style
658             else {
659 0         0 @cmd = (
660             'adduser',
661             '--disabled-password',
662             '--gecos', '',
663             );
664            
665             # if creating as system user
666 0 0       0 if ($opts{'system'})
667 0         0 { push @cmd, '--system' }
668            
669             # add name
670 0         0 push @cmd, $name;
671             }
672            
673             # run command
674 0 0       0 $class->run_cmd('error-creating-user', @cmd) or return undef;
675            
676             # create object
677 0         0 $user = bless({}, $class);
678            
679             # hold on to name
680 0         0 $user->{'name'} = $name;
681            
682             # return
683 0         0 return $user;
684             }
685             #
686             # create
687             #------------------------------------------------------------------------------
688              
689              
690             #------------------------------------------------------------------------------
691             # POD for ensure()
692             #
693              
694             =head2 ensure
695              
696             Unix::Mgt::User->ensure() gets a user account if it already exists, and
697             creates the account if it does not. For example, the following lines ensures
698             the C account:
699              
700             $user = Unix::Mgt::User->ensure('molly');
701              
702             =cut
703              
704             #
705             # POD for ensure()
706             #------------------------------------------------------------------------------
707              
708              
709             #------------------------------------------------------------------------------
710             # field gets
711             #
712              
713             =head2 name
714              
715             Returns the name of the user account. Currently this method cannot be used to
716             set the account name.
717              
718             print $user->name(), "\n";
719              
720             =head2 uid
721              
722             Returns the user's user id (uid).
723              
724             print $user->uid(), "\n";
725              
726             =head2 passwd
727              
728             Returns the password field from C. This method will not actually
729             return a password, it will probably just return C<*>.
730              
731             print $user->passwd(), "\n"; # probably outputs "*"
732              
733             =cut
734              
735             sub field_get {
736 0     0   0 my ($user, $key) = @_;
737 0         0 my ($entry);
738            
739             # TESTING
740             # println subname(method=>1); ##i
741            
742             # reset error
743 0         0 $user->reset_err();
744            
745             # get entry
746 0         0 $entry = $user->entry();
747 0 0       0 $entry or return undef;
748            
749             # return
750 0         0 return $entry->{$key};
751             }
752              
753 0     0   0 sub name { return shift->field_get('name') }
754 0     0   0 sub uid { return shift->field_get('uid') }
755 0     0   0 sub passwd { return shift->field_get('passwd') }
756              
757             #
758             # field gets
759             #------------------------------------------------------------------------------
760              
761              
762             #------------------------------------------------------------------------------
763             # field get|sets
764             #
765              
766             =head2 gid
767              
768             Sets/gets the gid of the user's primary group. Called without params, it
769             returns the user's gid:
770              
771             print $user->gid(), "\n";
772              
773             Called with a single param, gid() sets, then returns the user's primary
774             group id:
775              
776             print $user->gid('1010'), "\n";
777              
778             If you want to get a Unix::Mgt::Group object representing the user's primary
779             group, use $user->group().
780              
781             =head2 dir
782              
783             Sets/gets the user's home directory. Called without params, it returns the
784             directory name:
785              
786             print $user->dir(), "\n";
787              
788             Called with a single param, dir() sets, then returns the user's home directory:
789              
790             print $user->dir('/tmp'), "\n";
791              
792             =head2 shell
793              
794             Sets/gets the user's default command line shell. Called without params, it
795             returns the shell name:
796              
797             print $user->shell(), "\n";
798              
799             Called with a single param, shell() sets, then returns the user's shell:
800              
801             print $user->shell('/bin/sh'), "\n";
802              
803             =cut
804              
805             our %bsd_switches = (
806             home => 'd',
807             gid => 'g',
808             shell => 's',
809             );
810              
811             sub field_get_set {
812 0     0   0 my $user = shift(@_);
813 0         0 my $field = shift(@_);
814 0         0 my $option = shift(@_);
815            
816             # TESTING
817             # println subname(method=>1); ##i
818            
819             # if a value was sent, set the field to that value
820 0 0       0 if (@_) {
821 0         0 my ($value) = @_;
822 0         0 my (@cmd);
823            
824             # safety check
825 0         0 $user->mod_only($user->{'name'});
826            
827             # BSD style
828 0 0       0 if ($user->use_bsd) {
829             @cmd = (
830             'pw',
831             'usermod',
832             $user->{'name'},
833 0         0 '-' . $bsd_switches{$option},
834             $value
835             );
836             }
837            
838             # else Linux style
839             else {
840             # build command
841             @cmd = (
842             'usermod',
843             "--$option",
844             $value,
845 0         0 $user->{'name'},
846             );
847             }
848            
849             # TESTING
850             # sudo pw usermod -d "/home/whatever"
851             # showaref @cmd;
852            
853             # run command
854 0 0       0 $user->run_cmd("usermod-error-$field", @cmd) or return undef;
855             }
856            
857             # return field
858 0         0 return $user->field_get($field);
859             }
860              
861              
862 0     0   0 sub gid { return shift->field_get_set('gid', 'gid', @_) }
863 0     0   0 sub dir { return shift->field_get_set('dir', 'home', @_) }
864 0     0   0 sub shell { return shift->field_get_set('shell', 'shell', @_) }
865              
866             # sub quota { return shift->field_get_set('quota') }
867             # sub comment { return shift->field_get_set('comment', 'comment', @_) }
868             # sub expire { return shift->field_get_set('expire', 'expiredate', @_) }
869             # sub gecos { return shift->field_get_set('gecos') }
870              
871             #
872             # field get|sets
873             #------------------------------------------------------------------------------
874              
875              
876             #------------------------------------------------------------------------------
877             # group
878             #
879              
880             =head2 group
881              
882             Sets/gets the user's primary group. When called without any params, C
883             returns a Unix::Mgt::Group object representing the user's primary group:
884              
885             $group = $user->group();
886              
887             When called with a single param, C sets the user's primary group. The
888             param can be either the group's name or its gid:
889              
890             $user->group('video');
891             $user->group(44);
892              
893             =cut
894              
895             sub group {
896 0     0   0 my $user = shift(@_);
897 0         0 my ($new_group, %opts) = $user->even_odd_params('new', @_);
898 0         0 my ($entry, $gid, $group);
899            
900             # TESTING
901             # println subname(method=>1); ##i
902            
903             # default options
904 0         0 %opts = (object=>1, %opts);
905            
906             # set new group
907 0 0       0 if (defined $new_group) {
908 0         0 my (@args, $success);
909            
910             # reset error globals
911 0         0 $user->reset_err();
912            
913             # BSD style
914 0 0       0 if ($user->use_bsd) {
915             @args = (
916             'pw',
917             'usermod',
918 0         0 $user->{'name'},
919             '-g',
920             "$new_group",
921             );
922             }
923            
924             # else Linux style
925             else {
926             # build usermod arguments
927 0         0 @args = (
928             'usermod',
929             '-g',
930             "$new_group",
931             "$user"
932             );
933             }
934            
935             # change user's group
936 0         0 $success = $user->run_cmd('error-setting-user-group', @args);
937 0 0       0 $success or return 0;
938             }
939            
940             # get gid
941 0         0 $gid = $user->gid();
942 0 0       0 defined($gid) or return undef;
943            
944             # get group
945 0         0 $group = Unix::Mgt::Group->get($gid);
946            
947             # return
948 0 0       0 if ($opts{'object'})
949 0         0 { return $group }
950             else
951 0         0 { return $group->name }
952             }
953             #
954             # group
955             #------------------------------------------------------------------------------
956              
957              
958             #------------------------------------------------------------------------------
959             # secondary_groups
960             #
961              
962             =head2 secondary_groups
963              
964             C returns an array of the user's secondary groups. Each
965             element in the array is a Unix::Mgt::Group object.
966              
967             @groups = $user->secondary_groups();
968              
969             =cut
970              
971             sub secondary_groups {
972 0     0   0 my ($user, %opts) = @_;
973 0         0 my (%groups, @rv);
974            
975             # TESTING
976             # println subname(method=>1); ##i
977            
978             # default options
979 0         0 %opts = (object=>1, %opts);
980            
981             # loop through all groups
982 0         0 while (my @fields = getgrent()) {
983 0         0 my (%group);
984 0         0 @group{@Unix::Mgt::Group::field_names} = @fields;
985            
986             # if there are any members, of the group, see if this user is in it
987 0 0       0 if (my $member_str = $group{'members'}) {
988 0         0 my (%members);
989            
990             # parse out members
991 0         0 $member_str = crunch($member_str);
992 0         0 @members{split m|\s+|, $member_str} = ();
993            
994             # if this user is in the membership
995 0 0       0 if (exists $members{$user->{'name'}})
996 0         0 { $groups{$group{'name'}} = 1 }
997             }
998             }
999            
1000             # build return value
1001 0         0 foreach my $key (keys %groups) {
1002 0         0 my $group = Unix::Mgt::Group->get($key);
1003            
1004             # set as just string if options indicate to do so
1005 0 0       0 if (! $opts{'object'})
1006 0         0 { $group = $group->{'name'} }
1007            
1008             # add to return array
1009 0         0 push @rv, $group;
1010             }
1011            
1012             # return
1013 0         0 return @rv;
1014             }
1015             #
1016             # secondary_groups
1017             #------------------------------------------------------------------------------
1018              
1019              
1020             #------------------------------------------------------------------------------
1021             # groups
1022             #
1023              
1024             =head2 groups
1025              
1026             C returns an array of all of the groups the user is a member of. The
1027             first element in the array will be the user's primary group.
1028              
1029             @groups = $user->groups();
1030              
1031             =cut
1032              
1033             sub groups {
1034 0     0   0 my ($user, %opts) = @_;
1035 0         0 my (@rv);
1036            
1037             # TESTING
1038             # println subname(method=>1); ##i
1039            
1040             # get user's primary group
1041 0         0 push @rv, $user->group(%opts);
1042            
1043             # add user's secondary groups
1044 0         0 push @rv, $user->secondary_groups(%opts);
1045            
1046             # return
1047 0         0 return @rv;
1048             }
1049             #
1050             # groups
1051             #------------------------------------------------------------------------------
1052              
1053              
1054             #------------------------------------------------------------------------------
1055             # add_to_group
1056             #
1057              
1058             =head2 add_to_group
1059              
1060             C adds the user to a group. The group will be one of the user's
1061             secondary groups, not the primary group.
1062              
1063             $user->add_to_group('video');
1064              
1065             =cut
1066              
1067             sub add_to_group {
1068 0     0   0 my ($user, $group) = @_;
1069 0         0 my (@args, $success);
1070            
1071             # TESTING
1072             # println subname(method=>1); ##i
1073            
1074             # BSD style
1075 0 0       0 if ($user->use_bsd) {
1076             @args = (
1077             'pw',
1078             'usermod',
1079 0         0 $user->{'name'},
1080             '-G',
1081             "$group"
1082             );
1083             }
1084            
1085             # else Linux style
1086             else {
1087             # build command arguments
1088 0         0 @args = (
1089             'usermod',
1090             '--append',
1091             '--groups',
1092             "$group",
1093             "$user"
1094             );
1095             }
1096            
1097             # run command
1098 0         0 $success = $user->run_cmd('error-adding-user-to-group', @args);
1099            
1100             # return success|failure
1101 0         0 return $success;
1102             }
1103             #
1104             # add_to_group
1105             #------------------------------------------------------------------------------
1106              
1107              
1108             #------------------------------------------------------------------------------
1109             # remove
1110             #
1111              
1112             =head2 remove
1113              
1114             C removes the user account from the system. C does not take any
1115             parameters.
1116              
1117             $user->remove();
1118              
1119             =cut
1120              
1121             sub remove {
1122 0     0   0 my ($user) = @_;
1123 0         0 my (@args);
1124            
1125             # TESTING
1126             # println subname(method=>1); ##i
1127            
1128             # safety check
1129 0         0 $user->mod_only($user);
1130            
1131             # reset error
1132 0         0 $user->reset_err();
1133            
1134             # BSD style
1135 0 0       0 if ($user->use_bsd) {
1136 0         0 @args = (
1137             'pw',
1138             'userdel',
1139             '-n',
1140             $user->name,
1141             '-r',
1142             );
1143             }
1144            
1145             # else use userdel
1146             else {
1147 0         0 @args = (
1148             'userdel',
1149             $user->name
1150             );
1151             }
1152            
1153 0 0       0 $user->run_cmd('error-deleting-user', @args) or return undef;
1154            
1155             # return
1156 0         0 return 1;
1157             }
1158             #
1159             # remove
1160             #------------------------------------------------------------------------------
1161              
1162              
1163             #------------------------------------------------------------------------------
1164             # default_inc
1165             #
1166             sub default_inc {
1167 0     0   0 my ($env_path, $perl_path, $default_str, @default_arr);
1168            
1169             # TESTING
1170             # println subname(); ##i
1171            
1172             # path to perl binary currently being run
1173 0         0 $perl_path = $^X;
1174            
1175             # path to the env command
1176 0         0 $env_path = cmd_path_guess('env');
1177 0 0       0 $env_path or return undef;
1178            
1179             # get raw string containing the default @INC
1180 0         0 eval {
1181 0         0 $default_str = capturex(
1182             $env_path,
1183             '-i',
1184             $perl_path,
1185             '-wT',
1186             '-e',
1187             'print join(":", @INC)',
1188             );
1189             };
1190            
1191             # if the capture threw an error, return empty array
1192 0 0       0 if ($@) {
1193             # println 'error getting default @INC: ', $@;
1194 0         0 return ();
1195             }
1196            
1197             # if we didn't get a defined string, return empty array
1198 0 0       0 if (! defined $default_str)
1199 0         0 { return () }
1200            
1201             # parse string
1202 0         0 @default_arr = split(':', $default_str);
1203            
1204             # return
1205 0         0 return (@default_arr);
1206             }
1207             #
1208             # default_inc
1209             #------------------------------------------------------------------------------
1210              
1211              
1212             #------------------------------------------------------------------------------
1213             # new_libs
1214             # private sub
1215             #
1216             sub new_libs {
1217 0     0   0 my (@defaults_arr, %defaults, @rv);
1218            
1219             # TESTING
1220             # println subname(as_sub=>1); ##i
1221            
1222             # get defaults, return if none
1223 0         0 @defaults_arr = default_inc();
1224 0 0       0 @defaults_arr or return();
1225            
1226             # build hash of defaults
1227 0         0 @defaults{@defaults_arr} = ();
1228            
1229             # loop through @INC, adding paths that aren't in defaults
1230             LIB_LOOP:
1231 0         0 foreach my $lib (@INC) {
1232             # special case: remove current directory
1233 0 0       0 if ($lib eq '.')
1234 0         0 { next LIB_LOOP }
1235            
1236             # if the library isn't in the defaults, add it to the return array
1237 0 0       0 if (! exists $defaults{$lib})
1238 0         0 { push @rv, $lib }
1239             }
1240            
1241             # return
1242 0         0 return join(':', @rv);
1243             }
1244             #
1245             # new_libs
1246             #------------------------------------------------------------------------------
1247              
1248              
1249             #------------------------------------------------------------------------------
1250             # file_tests
1251             # TODO:
1252             # - Write regtests for this method.
1253             # - Document this method.
1254             #
1255             # Document: If -e returns false, not other tests are run. -e does not indiciate
1256             # why it returns false. I might be because the file doesn't exist. It also
1257             # might be that the user doesn't have permission to even see the file.
1258             #
1259             # Regtest: test with multiple extra libs
1260             #
1261             sub file_tests {
1262 0     0   0 my ($user, $paths_in) = @_;
1263 0         0 my (@cmd, $single, @paths, $cmd_str, @su, $json, $tests);
1264            
1265             # TESTING
1266             # println subname(); ##i
1267            
1268             # load JSON module
1269 0         0 require JSON::Tiny;
1270            
1271             # paths must be sent
1272 0 0       0 if (! defined $paths_in)
1273 0         0 { croak 'defined $paths_in not sent' }
1274            
1275             # build @paths
1276 0 0       0 if (ref $paths_in) {
1277 0         0 @paths = @$paths_in;
1278             }
1279             else {
1280 0         0 @paths = $paths_in;
1281 0         0 $single = $paths[0];
1282             }
1283            
1284             # remove empties
1285 0         0 @paths = grep {$_ =~ m|\S|s} @paths;
  0         0  
1286            
1287             # if no paths, nothing to do
1288 0 0       0 if (! @paths)
1289 0         0 { croak 'no @paths sent' }
1290            
1291             # escape quotes, add quotes
1292 0         0 foreach my $path (@paths) {
1293 0         0 $path =~ s|\"|\\"|sg;
1294 0         0 $path =~ s|\'|\\'|sg;
1295 0         0 $path = "'$path'";
1296             }
1297            
1298             # path to perl binary currently being run
1299 0         0 push @cmd, $^X, '-wT';
1300            
1301             # extra libs to add to perl command
1302 0 0       0 if (my $new_libs = new_libs()) {
1303 0         0 my @libs = split(':', $new_libs);
1304            
1305             # loop through libs adding each one to command
1306 0         0 foreach my $lib (@libs) {
1307 0         0 push @cmd, '-I', $lib;
1308             }
1309             }
1310            
1311             # add Unix::Mgt module
1312 0         0 push @cmd, '-MUnix::Mgt';
1313            
1314             # add call to file_tests_from_external
1315 0         0 push @cmd, '-e', '"Unix::Mgt::User::file_tests_from_external(' . join(', ', @paths) . ')"';
1316            
1317             # get command to send to su
1318 0         0 $cmd_str = join(' ', @cmd);
1319            
1320             # build su command
1321 0         0 @su = (
1322             cmd_path_guess('su'),
1323             $user->name(),
1324             '-c',
1325             $cmd_str,
1326             );
1327            
1328             # run command, get results
1329 0         0 $json = capturex(@su);
1330            
1331             # decode json
1332 0         0 eval {
1333 0         0 $tests = JSON::Tiny::decode_json($json);
1334             };
1335            
1336             # throw error if unable to decode
1337 0 0       0 if (! $tests) {
1338 0         0 croak 'unable to parse results from file tests';
1339             }
1340            
1341             # if single, just return the tests for that file
1342 0 0       0 if ($single)
1343 0         0 { return $tests->{$single} }
1344            
1345             # else return the entire hash
1346             else
1347 0         0 { return $tests }
1348             }
1349             #
1350             # file_tests
1351             #------------------------------------------------------------------------------
1352              
1353              
1354             #------------------------------------------------------------------------------
1355             # file_tests_from_external
1356             # Don't call this sub directly.
1357             #
1358             sub file_tests_from_external {
1359 0     0   0 my (@paths) = @_;
1360 0         0 my ($rv);
1361            
1362             # TESTING
1363             # println subname(); ##i
1364            
1365             # load JSON module
1366 0         0 require JSON::Tiny;
1367            
1368             # initialize return hashref
1369 0         0 $rv = {};
1370            
1371             # loop through paths
1372             PATH_LOOP:
1373 0         0 foreach my $path (@paths) {
1374 0         0 my ($tests);
1375            
1376             # TESTING
1377             # println '$path: ', $path; ##i
1378            
1379             # initialize tests for this path
1380 0         0 $rv->{$path} = $tests = {};
1381            
1382             # -e file exists
1383 0 0       0 unless ($tests->{'-e'} = -e($path)) {
1384 0         0 next PATH_LOOP;
1385             }
1386            
1387             # add rest of tests
1388 0         0 $tests->{'-r'} = -r($path); # -r File is readable by effective uid/gid.
1389 0         0 $tests->{'-w'} = -w($path); # -w File is writable by effective uid/gid.
1390 0         0 $tests->{'-x'} = -x($path); # -x File is executable by effective uid/gid.
1391 0         0 $tests->{'-o'} = -o($path); # -o File is owned by effective uid.
1392 0         0 $tests->{'-R'} = -R($path); # -R File is readable by real uid/gid.
1393 0         0 $tests->{'-W'} = -W($path); # -W File is writable by real uid/gid.
1394 0         0 $tests->{'-X'} = -X($path); # -X File is executable by real uid/gid.
1395 0         0 $tests->{'-O'} = -O($path); # -O File is owned by real uid.
1396             }
1397            
1398             # TODO:
1399             # - test for multiple files
1400             # - write tests
1401             # - document
1402            
1403             # output
1404 0         0 print JSON::Tiny::encode_json($rv);
1405             }
1406             #
1407             # file_tests_from_external
1408             #------------------------------------------------------------------------------
1409              
1410              
1411              
1412             #
1413             # Unix::Mgt::User
1414             ###############################################################################
1415              
1416              
1417              
1418             ###############################################################################
1419             # Unix::Mgt::Group
1420             #
1421             package Unix::Mgt::Group;
1422 1     1   6 use strict;
  1         1  
  1         26  
1423 1     1   12 use String::Util ':all';
  1         2  
  1         214  
1424 1     1   5 use Carp 'croak';
  1         2  
  1         44  
1425 1     1   4 use base 'Unix::Mgt::UGCommon';
  1         2  
  1         1951  
1426              
1427              
1428             # debug tools
1429             # use Debug::ShowStuff ':all';
1430              
1431             # safety mechanism for development
1432             our $MOD_ONLY;
1433              
1434             #------------------------------------------------------------------------------
1435             # POD
1436             #
1437              
1438             =head1 Unix::Mgt::Group
1439              
1440             A Unix::Mgt::Group object represents a group in the Unix system. The object
1441             allows you to get and set information about the group. A group object is
1442             created in one of three ways: C, C, or C. The C
1443             method is an alias for C.
1444              
1445             Unix::Mgt::Group objects stringify to the groups's name. For example, the
1446             following code would output C
1447              
1448             $group = Unix::Mgt::Group->get('video');
1449             print $group, "\n";
1450              
1451             =cut
1452              
1453             #
1454             # POD
1455             #------------------------------------------------------------------------------
1456              
1457              
1458             #------------------------------------------------------------------------------
1459             # field_names
1460             #
1461             our @field_names = qw{
1462             name
1463             passwd
1464             gid
1465             members
1466             };
1467             #
1468             # field_names
1469             #------------------------------------------------------------------------------
1470              
1471              
1472             #------------------------------------------------------------------------------
1473             # fields
1474             #
1475             sub fields {
1476 1     1   3 my ($class, $name) = @_;
1477            
1478             # TESTING
1479             # println subname(method=>1); ##i
1480            
1481             # return
1482 1 50       4 if ($name =~ m|^\d+$|s)
1483 0         0 { return getgrgid($name) }
1484             else
1485 1         158 { return getgrnam($name) }
1486             }
1487             #
1488             # fields
1489             #------------------------------------------------------------------------------
1490              
1491              
1492             #------------------------------------------------------------------------------
1493             # get
1494             #
1495              
1496             =head2 get
1497              
1498             Unix::Mgt::Group->get() retrieves group information using C or
1499             C. The single param for this method is either the name or the gid of
1500             the group.
1501              
1502             $group = Unix::Mgt::Group->get('video');
1503             $group = Unix::Mgt::Group->get('44');
1504              
1505             If the group is not found then the C error id is set in
1506             C<$Unix::Mgt::err_id> and undef is returned.
1507              
1508             =cut
1509              
1510             # alias new to get
1511             sub new {
1512 0     0   0 my $class = shift(@_);
1513 0         0 return $class->get(@_);
1514             }
1515              
1516             sub get {
1517 2     2   1220 my $class = shift(@_);
1518 2         10 my ($name, %opts) = $class->even_odd_params('name', @_);
1519 2         4 my (@fields, $group);
1520            
1521             # TESTING
1522             # println subname(method=>1); ##i
1523            
1524             # reset error globals
1525 2         15 $class->reset_err();
1526            
1527             # check and normalize name
1528 2         8 $name = $class->name_check($name, 'missing-group-name');
1529 2 100       9 $name or return undef;
1530            
1531             # get fields
1532 1         5 @fields = $class->fields($name);
1533            
1534             # if group exists, set name, else throw error
1535 1 50       11 if (@fields) {
1536 0         0 $name = $fields[0];
1537             }
1538             else {
1539 1         4 return $class->set_err(
1540             'do-not-have-group',
1541             $class->called_sub() . qq|: do not find a group with name "$name"|,
1542             );
1543             }
1544            
1545             # create object
1546 0           $group = bless({}, $class);
1547            
1548             # hold on to name
1549 0           $group->{'name'} = $name;
1550            
1551             # return
1552 0           return $group;
1553             }
1554             #
1555             # get
1556             #------------------------------------------------------------------------------
1557              
1558              
1559             #------------------------------------------------------------------------------
1560             # create
1561             #
1562              
1563             =head2 create
1564              
1565             Unix::Mgt::Group->create() creates a group. The required param for this method
1566             is the name for the new group.
1567              
1568             $group = Unix::Mgt::Group->create('websters');
1569              
1570             create() uses the Unix C program.
1571              
1572             =cut
1573              
1574             sub create {
1575 0     0     my $class = shift(@_);
1576 0           my ($name, %opts) = $class->even_odd_params('name', @_);
1577 0           my ($group, @cmd);
1578            
1579             # TESTING
1580             # println subname(method=>1); ##i
1581            
1582             # reset error globals
1583 0           $class->reset_err();
1584            
1585             # check and normalize name
1586 0           $name = $class->name_check($name, 'missing-group-name');
1587 0 0         $name or return undef;
1588            
1589             # if group exists, throw error
1590 0 0         if ($class->fields($name)) {
1591 0           return $class->set_err(
1592             'already-have-group',
1593             $class->called_sub() . qq|: already have a group with name "$name"|,
1594             );
1595             }
1596            
1597             # safety check
1598 0           $class->mod_only($name);
1599            
1600             # BSD style
1601 0 0         if ($class->use_bsd) {
1602 0           @cmd = (
1603             'pw',
1604             'groupadd',
1605             '-n',
1606             $name,
1607             );
1608             }
1609            
1610             # else Linux style
1611             else {
1612             # command
1613 0           push @cmd, 'groupadd';
1614            
1615             # if creating as system group
1616 0 0         if ($opts{'system'})
1617 0           { push @cmd, '--system' }
1618            
1619             # add name
1620 0           push @cmd, $name;
1621             }
1622            
1623             # run command
1624 0 0         $class->run_cmd('error-creating-group', @cmd) or return undef;
1625            
1626             # create object
1627 0           $group = bless({}, $class);
1628            
1629             # hold on to name
1630 0           $group->{'name'} = $name;
1631            
1632             # return
1633 0           return $group;
1634             }
1635             #
1636             # create
1637             #------------------------------------------------------------------------------
1638              
1639              
1640             #------------------------------------------------------------------------------
1641             # POD for ensure()
1642             #
1643              
1644             =head2 ensure
1645              
1646             Unix::Mgt::Group->ensure() gets a group if it already exists, and creates the
1647             group if it does not. For example, the following lines ensures
1648             the C group:
1649              
1650             $group = Unix::Mgt::User->ensure('wbesters');
1651              
1652             =cut
1653              
1654             #
1655             # POD for ensure()
1656             #------------------------------------------------------------------------------
1657              
1658              
1659             #------------------------------------------------------------------------------
1660             # field gets
1661             #
1662              
1663             =head2 name
1664              
1665             Returns the name of the group. Currently this method cannot be used to set the
1666             group name.
1667              
1668             print $group->name(), "\n";
1669              
1670             =head2 gid
1671              
1672             Returns the groups's group id (gid).
1673              
1674             print $group->gid(), "\n";
1675              
1676             =cut
1677              
1678             sub field_get {
1679 0     0     my ($group, $key) = @_;
1680 0           my ($entry);
1681            
1682             # TESTING
1683             # println subname(method=>1); ##i
1684            
1685             # reset error
1686 0           $group->reset_err();
1687            
1688             # get entry
1689 0           $entry = $group->entry();
1690 0 0         $entry or return undef;
1691            
1692             # return
1693 0           return $entry->{$key};
1694             }
1695              
1696 0     0     sub name { return shift->field_get('name') }
1697 0     0     sub gid { return shift->field_get('gid') }
1698              
1699             #
1700             # field gets
1701             #------------------------------------------------------------------------------
1702              
1703              
1704             #------------------------------------------------------------------------------
1705             # entry
1706             #
1707             sub entry {
1708 0     0     my ($group) = @_;
1709 0           my (@fields, %entry);
1710            
1711             # TESTING
1712             # println subname(method=>1); ##i
1713            
1714             # get fields
1715 0           @fields = $group->fields($group->{'name'});
1716            
1717             # if no fields, set error and return undef
1718 0 0         if (! @fields) {
1719             return $group->set_err(
1720             'do-not-have-group-entry-anymore',
1721 0           $group->called_sub() . ': do not have a group with name "' . $group->{'name'} . '"',
1722             );
1723             }
1724            
1725             # set hash
1726 0           @entry{@field_names} = @fields;
1727            
1728             # return
1729 0           return \%entry;
1730             }
1731             #
1732             # entry
1733             #------------------------------------------------------------------------------
1734              
1735              
1736             #------------------------------------------------------------------------------
1737             # members
1738             #
1739              
1740             =head2 members
1741              
1742             C returns an array of all members of the group. Both users for whom
1743             this is the primary group, and users for whom this is a secondary group are
1744             returned.
1745              
1746             @members = $group->members();
1747              
1748             The elements in the array are Unix::Mgt::User objects.
1749              
1750             =cut
1751              
1752             sub members {
1753 0     0     my ($group, %opts) = @_;
1754 0           my (%members, @rv);
1755            
1756             # add users for whom this is their primary group
1757 0           foreach my $user ($group->primary_members(%opts)) {
1758 0           $members{"$user"} = $user;
1759             }
1760            
1761             # add users for whom this is a secondary group
1762 0           foreach my $user ($group->secondary_members(%opts)) {
1763 0           $members{"$user"} = $user;
1764             }
1765            
1766             # build return value
1767 0           @rv = values(%members);
1768            
1769             # return
1770 0           return @rv;
1771             }
1772             #
1773             # members
1774             #------------------------------------------------------------------------------
1775              
1776              
1777             #------------------------------------------------------------------------------
1778             # primary_members
1779             #
1780              
1781             =head2 primary_members
1782              
1783             C returns an array of users for whom this is the primary
1784             group.
1785              
1786             @members = $group->primary_members();
1787              
1788             The elements in the returned array are Unix::Mgt::User objects.
1789              
1790             =cut
1791              
1792             sub primary_members {
1793 0     0     my ($group, %opts) = @_;
1794 0           my ($gid, %members, @rv);
1795            
1796             # TESTING
1797             # println subname(method=>1); ##i
1798            
1799             # default options
1800 0           %opts = (object=>1, %opts);
1801            
1802             # get gid
1803 0           $gid = $group->gid();
1804            
1805             # get users for whom this i
1806 0           while (my @fields = getpwent()) {
1807 0           my (%user);
1808 0           @user{@Unix::Mgt::User::field_names} = @fields;
1809            
1810             # if the user is in the group, add to %members
1811 0 0 0       if ( defined($user{'gid'}) && ($user{'gid'} eq $gid) ) {
1812 0           $members{$user{'name'}} = 1;
1813             }
1814             }
1815            
1816             # build return array of objects
1817 0 0         if ($opts{'object'}) {
1818 0           foreach my $name (keys %members) {
1819 0           push @rv, Unix::Mgt::User->get($name);
1820             }
1821             }
1822            
1823             # else build return array of names
1824             else {
1825 0           @rv = keys(%members);
1826             }
1827            
1828             # return
1829 0           return @rv;
1830             }
1831             #
1832             # primary_members
1833             #------------------------------------------------------------------------------
1834              
1835              
1836             #------------------------------------------------------------------------------
1837             # secondary_members
1838             #
1839              
1840             =head2 secondary_members
1841              
1842             C returns an array of users for whom this is a secondary group.
1843              
1844             @members = $group->secondary_members();
1845              
1846             The elements in the returned array are Unix::Mgt::User objects.
1847              
1848             =cut
1849              
1850             sub secondary_members {
1851 0     0     my ($group, %opts) = @_;
1852 0           my ($gid, $members_str, %members, @rv);
1853            
1854             # TESTING
1855             # println subname(method=>1); ##i
1856            
1857             # default options
1858 0           %opts = (object=>1, %opts);
1859            
1860             # get users for whom this is a secondary group
1861 0           $members_str = $group->entry->{'members'};
1862 0 0         defined($members_str) or return ();
1863            
1864             # loop through members
1865             NAME_LOOP:
1866 0           foreach my $name (split m|\s+|s, $members_str) {
1867 0 0         if (hascontent $name) {
1868 0           my $user = Unix::Mgt::User->get($name);
1869 0           $members{$user->{'name'}} = 1;
1870             }
1871             }
1872            
1873             # build return array of objects
1874 0 0         if ($opts{'object'}) {
1875 0           foreach my $name (keys %members) {
1876 0           push @rv, Unix::Mgt::User->get($name);
1877             }
1878             }
1879            
1880             # else build return array of names
1881             else {
1882 0           @rv = keys(%members);
1883             }
1884            
1885             # return
1886 0           return @rv;
1887             }
1888             #
1889             # secondary_members
1890             #------------------------------------------------------------------------------
1891              
1892              
1893             #------------------------------------------------------------------------------
1894             # add_member
1895             #
1896              
1897             =head2 add_member
1898              
1899             C adds a user to the group as a secondary group. The single
1900             param can be a user name, uid, or Unix::Mgt::User object.
1901              
1902             $group->add_member('miko');
1903              
1904             If the user is already a member of the group then nothing is done and no error
1905             is set.
1906              
1907             =cut
1908              
1909             sub add_member {
1910 0     0     my ($group, $user) = @_;
1911            
1912             # TESTING
1913             # println subname(method=>1); ##i
1914            
1915             # get user object
1916 0 0         if (! ref $user)
1917 0           { $user = Unix::Mgt::User->get($user) }
1918            
1919             # add user to group
1920 0           return $user->add_to_group($group);
1921             }
1922             #
1923             # add_member
1924             #------------------------------------------------------------------------------
1925              
1926              
1927             #------------------------------------------------------------------------------
1928             # remove
1929             #
1930              
1931             =head2 remove
1932              
1933             C removes the group from the system. C does not take any
1934             parameters.
1935              
1936             $group->remove();
1937              
1938             If any users have the group as a primary group then this method will fail.
1939              
1940             =cut
1941              
1942             sub remove {
1943 0     0     my ($group) = @_;
1944 0           my (@cmd);
1945            
1946             # TESTING
1947             # println subname(method=>1); ##i
1948            
1949             # safety check
1950 0           $group->mod_only($group);
1951            
1952             # reset error
1953 0           $group->reset_err();
1954            
1955             # cannot remove if any users have this group as primary group
1956 0 0         if (my @primaries = $group->primary_members) {
1957 0           my ($id, $msg);
1958            
1959             # ste id
1960 0           $id = 'cannot-remove-primary-group';
1961            
1962             # begin message
1963 0           $msg =
1964             'cannot remove the group "' . $group->name() .
1965             '" because it is the primary group for ';
1966            
1967             # plural
1968 0 0         if (@primaries > 1) {
1969 0           $msg .= 'the following users: ' . join(', ', @primaries);
1970             }
1971            
1972             # singular
1973             else {
1974 0           $msg .= 'the user "' . $primaries[0] . '"';
1975             }
1976            
1977             # return failure
1978 0           return $group->set_err($id, $msg);
1979             }
1980            
1981             # if using pw command
1982 0 0         if ($group->use_bsd) {
1983 0           @cmd = (
1984             'pw',
1985             'groupdel',
1986             '-n',
1987             $group->name,
1988             );
1989             }
1990            
1991             # else use groupdel
1992             else {
1993 0           @cmd = (
1994             'groupdel',
1995             $group->name,
1996             );
1997             }
1998            
1999             # run command
2000 0 0         $group->run_cmd('error-deleting-group', @cmd) or return undef;
2001            
2002             # return
2003 0           return 1;
2004             }
2005             #
2006             # remove
2007             #------------------------------------------------------------------------------
2008              
2009              
2010             #
2011             # Unix::Mgt::Group
2012             ###############################################################################
2013              
2014              
2015              
2016             # return true
2017             1;
2018              
2019             __END__