#! /usr/bin/perl -W # # # Copyright (c) 2004 Daniel Molina Wegener # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # # # # Author: Daniel Molina Wegener # Version: $Id: grped,v 1.5 2004/11/01 15:59:36 dmw Exp $ # Keywords: # X-URL: require 5.6.1; use strict; # use diagnostics; use Getopt::Long qw(:config bundling no_ignore_case_always no_ignore_case); use FileHandle; use Fcntl ':flock'; use Data::Dumper; $SIG{INT} = sub { print "grped: ignored signal INT\n"; }; $SIG{HUP} = sub { print "grped: ignored signal HUP\n"; }; sub trim { my ($line) = @_; $line .= " "; $line =~ s/[\s\f\t\n\r]+//gmi; return $line; } sub grpl2hash { my ($line) = @_; my (@empty, @group, %hash); @empty = (); @group = split(/:/, $line); $hash{'group'} = trim($group[0]); $hash{'passwd'} = trim($group[1]); $hash{'id'} = trim($group[2]); if (defined($group[3])) { if ($group[3] =~ /,/) { @empty = split(/,/, trim($group[3])); } else { @empty = trim($group[3]); } $hash{'users'} = \@empty; } else { $hash{'users'} = \@empty; } return %hash; } sub hash2grpl { my (%hash) = @_; my ($line); $line = $hash{'group'}; $line .= ":" . $hash{'passwd'}; $line .= ":" . $hash{'id'}; $line .= ":" . join(',', @{$hash{'users'}}); return $line; } sub group_bygid { my ($gid, @grplst) = @_; my ($counter, $group, $id); $group = '*'; for ($counter = 0; $counter <= $#grplst; $counter++) { $id = $grplst[$counter]{'id'}; if ("$id:" eq "$gid:") { $group = $grplst[$counter]{'group'}; } } return $group; } sub group_rename { my ($grp, $name, @grplst) = @_; my ($grp_idx); $grp_idx = group_index($grp, @grplst); if ($grp_idx >= 0) { $grplst[$grp_idx]{'group'} = $name; } return @grplst; } sub group_remove { my ($grp, @grplst) = @_; my ($grp_idx); $grp_idx = group_index($grp, @grplst); if ($grp_idx >= 0) { splice(@grplst, $grp_idx, 1); } return @grplst; } sub group_create { my ($group, $gid, $users, $grplst) = @_; my (%grp, $bygid, $newgid); $grp{'group'} = $group; if ("$gid:" ne "RAND:") { $grp{'id'} = $gid; } else { do { $newgid = int(rand(999999)); $bygid = group_bygid($newgid, @$grplst); } while ("$bygid:" ne "*:"); $grp{'id'} = $newgid; } $grp{'passwd'} = '*'; $grp{'users'} = $users; push(@$grplst, \%grp); return @$grplst; } sub group_index { my ($grpname, @grplst) = @_; my (%hash, $item, $counter); for ($counter = 0; $counter <= $#grplst; $counter++) { if ($grplst[$counter]{'group'} eq $grpname) { return $counter; } } return -1; } sub group_users { my ($grpname, $sep, @grplst) = @_; my ($grp_idx); $grp_idx = group_index($grpname, @grplst); return join($sep, @{$grplst[$grp_idx]{'users'}}) } sub group_query { my ($grpname, $query, @grplst) = @_; my ($grp_idx, $user, @usrlst); my ($counter); $grp_idx = group_index($grpname, @grplst); @usrlst = (); foreach $user (@{$grplst[$grp_idx]{'users'}}) { if ($user =~ $query) { push @usrlst, $user; } } return @usrlst; } sub group_uregex { my ($query, @grplst) = @_; my ($counter, $user, @auxlst); @auxlst = (); for ($counter = 0; $counter <= $#grplst; $counter++) { my %hash; my $group; my @users = (); foreach $user (@{$grplst[$counter]{'users'}}) { if ($user =~ $query) { push(@users, $user); } } if ($#users >= 0) { $group = $grplst[$counter]{'group'}; $hash{'group'} = $group; $hash{'users'} = \@users; push(@auxlst, \%hash); } } return @auxlst; } sub group_list { my ($grplst) = @_; my (@lst, $c_grp, %hash); foreach $c_grp (@$grplst) { %hash = %$c_grp; push(@lst, $hash{'group'}); } return @lst; } sub user_chk { my ($usr, @usrlst) = @_; my ($_usr); foreach $_usr (@usrlst) { if ("$_usr:" eq "$usr:") { return 1; } } return 0; } sub user_index { my ($usr, @usrlst) = @_; my ($counter); for ($counter = 0; $counter <= $#usrlst; $counter++) { if ("$usrlst[$counter]:" eq "$usr:") { return $counter; } } return -1; } sub user_add { my ($grpname, $usr, @grplst) = @_; my ($idx, @usrlst); $idx = group_index($grpname, @grplst); if ($idx < 0) { print "grped: no such group $grpname\n"; return @grplst; } @usrlst = @{$grplst[$idx]{'users'}}; if (user_chk($usr, @usrlst)) { return @grplst; } else { push(@usrlst, $usr); @{$grplst[$idx]{'users'}} = @usrlst; return @grplst; } } sub user_remove { my ($grpname, $usr, @grplst) = @_; my ($idx_grp, $idx_usr, @usrlst); $idx_grp = group_index($grpname, @grplst); if ($idx_grp < 0) { print "grped: no such group $grpname\n"; return @grplst; } @usrlst = @{$grplst[$idx_grp]{'users'}}; if (user_chk($usr, @usrlst)) { $idx_usr = user_index($usr, @usrlst); if ($idx_usr < 0) { print "grped: no such user $usr in $grpname\n"; } else { splice(@usrlst, $idx_usr, 1); @{$grplst[$idx_grp]{'users'}} = @usrlst; } } return @grplst; } sub get_list { my ($str) = @_; $str = trim($str); if ($str =~ /,/) { return split(/,/,join(',',@_)); } else { return @_; } } sub grpf_lock { my ($fh) = @_; return flock($fh, LOCK_EX); } sub grpf_unlock { my ($fh) = @_; return flock($fh, LOCK_UN); } sub grpf_open { my ($fn) = @_; if (-r "$fn" && -w "$fn") { my $fh = new FileHandle "+< $fn"; if (defined($fh)) { # grpf_lock($fh); return $fh; } return 0; } return 0; } sub grpf_close { my ($fh) = @_; if (defined($fh)) { # grpf_unlock($fh); return $fh->close; } return 0; } sub grpf_load { my ($fh) = @_; my ($line, @grplst); @grplst = (); if (defined($fh)) { while (defined($line = $fh->getline)) { $line = trim($line); if ($line !~ m/^\#/) { my %hash = grpl2hash($line); push(@grplst, \%hash); } } } return @grplst; } sub grpf_save { my ($fh, @grplst) = @_; my ($grp, %grp, $line); if (defined($fh)) { foreach $grp (@grplst) { %grp = %{$grp}; $line = hash2grpl(%grp); print $fh "$line\n"; } return 1; } } sub debug_dump { my ($var, $qw) = @_; my ($dumper, $dump); $dumper = new Data::Dumper([$var], ["$qw"]); $Data::Dumper::Indent = 3; $Data::Dumper::Terse = 0; $dump = $dumper->Dump(); $dumper->Reset(); return $dump; } my @cmd_add = (); my @cmd_remove = (); my $cmd_list = ''; my $cmd_query = ''; my @cmd_destroy = (); my $cmd_rename = ''; my $opt_create = ''; my @opt_grp = (); my $opt_gid = ''; my @opt_user = (); my $opt_in = ''; my $opt_out = ''; my $opt_sep = ''; my $opt_version = ''; my $opt_dbg = ''; my $opt_verbose = ''; my $opt_hlp = ''; my (%opts); %opts = ( 'add|a=s' => \@cmd_add, 'remove|r=s' => \@cmd_remove, 'list|l' => \$cmd_list, 'query|q=s' => \$cmd_query, 'create|c=s' => \$opt_create, 'destroy|d=s' => \@cmd_destroy, 'rename|m=s' => \$cmd_rename, 'user|u=s' => \@opt_user, 'group|g=s' => \@opt_grp, 'gid=i' => \$opt_gid, 'input|i=s' => \$opt_in, 'output|o=s' => \$opt_out, 'separator|w=s' => \$opt_sep, 'verbose|v' => \$opt_verbose, 'debug|b' => \$opt_dbg, 'help|h' => \$opt_hlp, 'version|s' => \$opt_version ); my ($help, $version); my ($opt_parser); my ($gfn, $gsn, $ghl, $ghs); my (@grplst, %group, $line); my ($c_grp, $c_usr, $counter); my (@users, @qrylst); my $rcsid = q($Id: grped,v 1.5 2004/11/01 15:59:36 dmw Exp $); my $dmwid = q($DMW$); $version = < This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. EOF ; $help = <= 0) { if ($opt_parser->getoptions(%opts)) { @cmd_add = get_list(@cmd_add); @cmd_remove = get_list(@cmd_remove); @opt_user = get_list(@opt_user); @opt_grp = get_list(@opt_grp); @cmd_destroy = get_list(@cmd_destroy); ### display help if ($opt_hlp) { die "$help\n"; } ### display version if ($opt_version) { die "$version\n"; } if ($opt_dbg) { print "grped: configuration\n" . debug_dump(\%opts, "opts"); } if ($opt_in) { $gfn = $opt_in; } else { $gfn = '/etc/group'; } if ($opt_out) { $gsn = $opt_out; } else { $gsn = $gfn; } $ghl = grpf_open($gfn); @grplst = grpf_load($ghl); $ghs = grpf_open($gsn); if ($#grplst >= 0) { if ($opt_verbose) { print "grped: " . ($#grplst + 1) . " group(s)\n"; } if ($opt_dbg) { print "grped: groups in $gfn\n".debug_dump(\@grplst, "grplst"); } } else { grpf_close($ghl); die "grped: no groups in $gfn\n"; } ### add users if ($#cmd_add >= 0 && $#opt_grp >= 0) { foreach $c_usr (@cmd_add) { foreach $c_grp (@opt_grp) { if ($opt_verbose) { print "grped: adding $c_usr into $c_grp\n"; } # add users @grplst = user_add($c_grp, $c_usr, @grplst); } } if ($opt_dbg) { print "groups with added users\n"; print debug_dump(\@grplst, "grplst"); } # save group file if (grpf_save($ghs, @grplst)) { if ($opt_verbose) { print "grped: saving into $gsn success\n"; } } else { die "grped: error while writing $gsn\n"; } grpf_close($ghs); grpf_close($ghl); exit 0; } elsif ($#cmd_add >= 0 && $#opt_grp < 0) { die "grped: for adding users you must specify the group(s)\n"; } ### remove users if ($#cmd_remove >= 0 && $#opt_grp >= 0) { foreach $c_usr (@cmd_remove) { foreach $c_grp (@opt_grp) { if ($opt_verbose) { print "grped: removing $c_usr from $c_grp\n"; } @grplst = user_remove($c_grp, $c_usr, @grplst); } } if ($opt_dbg) { print "groups with removed users\n"; print debug_dump(\@grplst, "grplst"); } # save group file if (grpf_save($ghs, @grplst)) { if ($opt_verbose) { print "grped: saving into $gsn success\n"; } } else { die "grped: error while writing $gsn\n"; } grpf_close($ghs); grpf_close($ghl); exit 0; } elsif ($#cmd_remove >= 0 && $#opt_grp < 0) { die "grped: for removing users you must specify the group(s)\n"; } ### list users if ($cmd_list && $#opt_grp >= 0) { foreach $c_grp (@opt_grp) { if ("$opt_sep:" ne ":") { print group_users($c_grp, $opt_sep, @grplst); } else { print group_users($c_grp, ",", @grplst); } } grpf_close($ghs); grpf_close($ghl); exit 0; } elsif ($cmd_list && $#opt_grp < 0) { my @lst = group_list(\@grplst); if ($opt_sep) { print join($opt_sep, @lst); } else { print join(',', @lst); } exit 0; } ### query users if ($cmd_query) { if ($#opt_grp >= 0) { foreach $c_grp (@opt_grp) { # search for pattern in the given groups if ($opt_verbose) { print "grped: searching for '$cmd_query' in $c_grp\n"; } @users = group_query($c_grp, $cmd_query, @grplst); if ($#users >= 0) { print "$c_grp:" . join(',', @users) . "\n"; } } grpf_close($ghs); grpf_close($ghl); exit 0; } else { # search for pattern in all groups if ($opt_verbose) { print "grped: searching for '$cmd_query' in all groups\n"; } @qrylst = group_uregex($cmd_query, @grplst); if ($opt_dbg) { print "grped: user list"; print debug_dump(\@qrylst, "qrylst"); } if ($#qrylst >= 0) { foreach $c_grp (@qrylst) { %group = %{$c_grp}; print $group{'group'} . ":" . join(',', @{$group{'users'}}) . "\n"; } } else { print "grped: no match for '$cmd_query'\n"; } grpf_close($ghs); grpf_close($ghl); exit 0; } } ### create group(s) if ("$opt_create:" ne ":") { if ($opt_gid >= 0) { if (group_bygid($opt_gid, @grplst) eq '*') { @grplst = group_create($opt_create, $opt_gid, \@opt_user, \@grplst); } else { print "grped: group with gid $opt_gid already exists\n"; } } else { @grplst = group_create($opt_create, 'RAND', \@opt_user, \@grplst); } if ($opt_dbg) { print "grped: created group $opt_create\n"; print debug_dump(\@grplst, "grplst"); } # save group file if (grpf_save($ghs, @grplst)) { if ($opt_verbose) { print "grped: saving into $gsn success\n"; } grpf_close($ghs); grpf_close($ghl); exit 0; } else { die "grped: error while writing $gsn\n"; } } ### destroy group(s) if ($#cmd_destroy >= 0) { foreach $c_grp (@cmd_destroy) { # destroy the given group if ($opt_verbose) { print "grped: removing $c_grp group\n"; } @grplst = group_remove($c_grp, @grplst); } if ($opt_dbg) { print "grped: removed groups\n"; print debug_dump(\@grplst, "grplst"); } # save group file if (grpf_save($ghs, @grplst)) { if ($opt_verbose) { print "grped: saving into $gsn success\n"; } grpf_close($ghs); grpf_close($ghl); exit 0; } else { die "grped: error while writing $gsn\n"; } exit 0; } ### rename a group if ($cmd_rename && $#opt_grp == 0) { if ($opt_verbose) { print "grped: $opt_grp[0] renamed to $cmd_rename\n"; } @grplst = group_rename($opt_grp[0], $cmd_rename, @grplst); if ($opt_dbg) { print "grped: renamed groups\n"; print debug_dump(\@grplst, "grplst"); } # save group file if (grpf_save($ghs, @grplst)) { if ($opt_verbose) { print "grped: saving into $gsn success\n"; } grpf_close($ghs); grpf_close($ghl); exit 0; } else { die "grped: error while writing $gsn\n"; } exit 0; } elsif ($cmd_rename && $#opt_grp != 0) { die "grped: you must specify 'one' group to rename\n"; } } else { die "error processing options\n"; } } else { die "no options given, try: grped --help\n"; } # Local Variables: # mode: cperl # End: # vim:set ft=perl ff=unix ts=4 sw=4 enc=latin1 expandtab: # grped ends here