424 lines
15 KiB
Perl
424 lines
15 KiB
Perl
#!/usr/bin/perl -w
|
|
#==============================================================================
|
|
# lat-groups
|
|
# ==========
|
|
# 0.9.0 (2004-09-08)
|
|
# (c)2003-2004 Altiplano bvba
|
|
#==============================================================================
|
|
package esmith;
|
|
use strict;
|
|
use esmith::db;
|
|
use esmith::util;
|
|
use Pod::Usage;
|
|
use Getopt::Long;
|
|
my %conf;
|
|
tie %conf, 'esmith::config';
|
|
my %accounts;
|
|
tie %accounts, 'esmith::config', '/home/e-smith/db/accounts';
|
|
my ($Hlp, $Cml, $Frc, $Inp, $Noa);
|
|
my $Add =0;
|
|
my $Del =0;
|
|
my $Rem =0;
|
|
|
|
#==============================================================================
|
|
# Main
|
|
#==============================================================================
|
|
# Analyze commandline options
|
|
GetOptions ("help" => \$Hlp,
|
|
"add" => \$Add,
|
|
"delete" => \$Del,
|
|
"force" => \$Frc,
|
|
"no-admin", => \$Noa,
|
|
"remove-user" => \$Rem,
|
|
"command-line=s" => \$Cml,
|
|
"input-file=s" => \$Inp);
|
|
|
|
if ( $Hlp ) { &PrintPod(9); exit; }
|
|
|
|
# We need one argument or the other, but not both
|
|
if ((($Cml && $Inp && $Rem) || (! $Cml && ! $Inp && ! $Rem)) ||
|
|
($Add + $Del + $Rem != 1))
|
|
{ &PrintPod(1); exit; }
|
|
|
|
my @records;
|
|
if ($Inp) {
|
|
open(LIST,"< $Inp") || die "Can't find $Inp.\n";
|
|
@records = grep(!/(^\s*#)|(^\s*$)/,<LIST>);
|
|
close(LIST); }
|
|
elsif ($Cml) { @records=($Cml); }
|
|
else { &PrintPod(1); exit; }
|
|
|
|
# Add groups
|
|
if ($Add) {
|
|
# Process each group
|
|
foreach my $record (@records) {
|
|
my @fields=split(/\|/,$record);
|
|
for (my $cnt=0; $cnt <= $#fields; ++$cnt) { for ($fields[$cnt]) { s/^\s+//; s/\s+$//; }}
|
|
my $groupname = $fields[0];
|
|
|
|
# We need a valid group name
|
|
if ( ! &TestName($fields[0])) { exit; }
|
|
|
|
# Create the group, if needed
|
|
if ( ! db_get(\%accounts, $groupname)) {
|
|
# Find a usable GID
|
|
my $gid;
|
|
if ($fields[2]) { $gid = $fields[2]; }
|
|
else { $gid = &FindUid;}
|
|
|
|
if (&TestUid($gid)) {
|
|
my %group = ("Description", $fields[1],
|
|
"Uid",$gid,
|
|
"Gid",$gid,
|
|
"Members","");
|
|
print "Creating group '$fields[0]' (GID:$gid).\n";
|
|
db_set(\%accounts, $groupname, 'group', \%group);
|
|
system("/sbin/e-smith/signal-event", "group-create", $groupname) == 0
|
|
or die ("An error occurred while creating group $groupname.\n\a");
|
|
}
|
|
}
|
|
|
|
# Add the members to the group, if they are valid accounts
|
|
if ( db_get_type(\%accounts, $groupname) eq "group" ) {
|
|
for (my $cnt=3; $cnt < @fields; ++$cnt ) {
|
|
# Check if this user exists on the server
|
|
if (((db_get(\%accounts, $fields[$cnt])) &&
|
|
(db_get_type(\%accounts, $fields[$cnt])) eq "user") ||
|
|
($fields[$cnt] eq 'admin')) {
|
|
# Is she already a member of this group?
|
|
my $members = db_get_prop(\%accounts, $groupname, 'Members');
|
|
if (! $members) { $members="" }
|
|
if (! ($members =~ m/\b$fields[$cnt]\b/)) {
|
|
# Append the new member to the list and update the group
|
|
if (length($members) > 0) { $members=$members.",";}
|
|
$members = $members."$fields[$cnt]";
|
|
print "Adding user '$fields[$cnt]' to group '$fields[0]'.\n";
|
|
db_set_prop(\%accounts, $groupname, 'Members', $members);
|
|
system("/sbin/e-smith/signal-event", "group-modify", $groupname) == 0
|
|
or die ("An error occurred while updating group $groupname.\n\a");
|
|
}
|
|
}
|
|
else { print "User '$fields[$cnt]' doesn't exist on this server.\n\a"; }
|
|
}
|
|
}
|
|
else { print "Error: '$groupname' is not a valid group name on this system.\a\n"; }
|
|
|
|
# Remove 'admin' and 'www' from /etc/group if we don't want them.
|
|
if ($Noa) {
|
|
system("/usr/bin/gpasswd -d www $groupname > /dev/null");
|
|
system("/usr/bin/gpasswd -d admin $groupname > /dev/null");
|
|
}
|
|
}
|
|
}
|
|
|
|
# Delete groups
|
|
if ($Del) {
|
|
&ExpandWildCard; # Check for wildcards and expand if necessary
|
|
|
|
# Process each group
|
|
foreach my $record (@records) {
|
|
my @fields=split(/\|/,$record);
|
|
for (my $cnt=0; $cnt <= $#fields; ++$cnt) { for ($fields[$cnt]) { s/^\s+//; s/\s+$//; }}
|
|
my $groupname = $fields[0];
|
|
|
|
# Silly, but if 'www' and 'admin' aren't member of this group an error message will be shown :-/
|
|
system("/usr/bin/gpasswd -a www $groupname > /dev/null");
|
|
system("/usr/bin/gpasswd -a admin $groupname > /dev/null");
|
|
|
|
if ((db_get_type(\%accounts, $groupname)) &&
|
|
(db_get_type(\%accounts, $groupname)) eq 'group')
|
|
{
|
|
my $yn = 'yes';
|
|
if (! $Frc) {
|
|
print "Delete group '$groupname' [yes/NO/all]? "; $yn = <STDIN>;
|
|
if ($yn =~ /^a/i) { $Frc = -1; $yn="yes"; }
|
|
}
|
|
if ($yn =~ /^y/i) {
|
|
print "Deleting group '$groupname'.\n";
|
|
db_delete(\%accounts, $groupname);
|
|
system("/sbin/e-smith/signal-event", "group-delete", $groupname);
|
|
}
|
|
}
|
|
else { print "Group '$groupname' doesn't exist on this server.\n\a"; }
|
|
}
|
|
}
|
|
|
|
# Remove users from group
|
|
if ($Rem) {
|
|
&ExpandWildCard; # Check for wildcards and expand if necessary
|
|
|
|
# Process each group
|
|
foreach my $record (@records) {
|
|
my @fields=split(/\|/,$record);
|
|
for (my $cnt=0; $cnt <= $#fields; ++$cnt) { for ($fields[$cnt]) { s/^\s+//; s/\s+$//; }}
|
|
my $groupname = $fields[0];
|
|
|
|
if ( db_get(\%accounts, $groupname)) {
|
|
# Remove the members from the group
|
|
for (my $cnt=1; $cnt < @fields; ++$cnt ) {
|
|
my $members = db_get_prop(\%accounts, $groupname, 'Members');
|
|
if (! $members) { $members="" }
|
|
my $yn = 'yes';
|
|
if (! $Frc) {
|
|
print "Remove user '$fields[$cnt]' from group '$groupname' [yes/NO/all]? "; $yn = <STDIN>;
|
|
if ($yn =~ /^a/i) { $Frc = -1; $yn="yes"; }
|
|
}
|
|
if ($yn =~ /^y/i) {
|
|
print "Removing member '$fields[$cnt]' from group '$fields[0]'.\n";
|
|
$members =~ s/$fields[$cnt]//;
|
|
$members =~ s/,,/,/ ;
|
|
$members =~ s/(^,)|(,$)//;
|
|
|
|
db_set_prop(\%accounts, $groupname, 'Members', $members);
|
|
system("/sbin/e-smith/signal-event", "group-modify", $groupname) == 0
|
|
or die ("An error occurred while updating group $groupname.\n\a");
|
|
}
|
|
}
|
|
}
|
|
else { print "The group '$groupname' does not exist on this server.\n\a"; }
|
|
}
|
|
}
|
|
#==============================================================================
|
|
# Subroutines
|
|
#==============================================================================
|
|
# Find an unused Uid/Gid
|
|
sub FindUid {
|
|
open(ACC,"/home/e-smith/db/accounts") || die "Can't find /home/e-smith/db/accounts";
|
|
my @recs = grep(/Uid\|\d*/,<ACC>);
|
|
close(ACC);
|
|
open(PWD,"/etc/passwd") || die "Can't find /etc/passwd";
|
|
my @pwds = grep(/\:\d*\:/,<PWD>);
|
|
close(PWD);
|
|
open(GRP,"/etc/group") || die "Can't find /etc/group";
|
|
my @grps = grep(/\:\d*\:/,<GRP>);
|
|
close(GRP);
|
|
|
|
my $newuid=db_get(\%conf, 'MinUid');
|
|
do { ++$newuid;} until ((! grep(/Uid\|$newuid\D/,@recs)) &&
|
|
(! grep(/\:$newuid\:/,@pwds)) &&
|
|
(! grep(/\:$newuid\:/,@grps)));
|
|
return $newuid;
|
|
}
|
|
#==============================================================================
|
|
# Test the gid for availability and legality
|
|
sub TestUid {
|
|
open(ACC,"/home/e-smith/db/accounts") || die "Can't find /home/e-smith/db/accounts";
|
|
my @recs = grep(/Gid\|\d*/,<ACC>);
|
|
close(ACC);
|
|
open(PWD,"/etc/passwd") || die "Can't find /etc/passwd";
|
|
my @pwds = grep(/\:\d*\:/,<PWD>);
|
|
close(PWD);
|
|
open(GRP,"/etc/group") || die "Can't find /etc/group";
|
|
my @grps = grep(/\:\d*\:/,<GRP>);
|
|
close(GRP);
|
|
|
|
if (! ($_[0] =~ /^\d*$/)) {
|
|
print "Error: A group ID should contain only numbers.\a\n";
|
|
exit;
|
|
}
|
|
elsif ( $_[0] < db_get(\%conf, 'MinUid')) {
|
|
print "Error: The group ID should be greater or equal to ".&FindUid.".\a\n";
|
|
exit;
|
|
}
|
|
|
|
elsif ((grep(/Gid\|$_[0]\D/,@recs)) or
|
|
(grep(/\:$_[0]\:/,@pwds)) or
|
|
(grep(/\:$_[0]\:/,@grps))) {
|
|
print "Error: The gid '$_[0]' is already in use\a\n";
|
|
exit;
|
|
}
|
|
else { return 1 };
|
|
}
|
|
#==============================================================================
|
|
# Test name for illegal characters
|
|
sub TestName {
|
|
if ( $_[0] =~ /^[a-z][a-z\-\_\d]*$/ ) {
|
|
return -1;
|
|
}
|
|
else {
|
|
print "The group name '$_[0]' contains illegal characters.\n";
|
|
print "The name should contain only lower-case letters, numbers, hyphens and\n";
|
|
print "underscores, and should start with a lower-case letter.\n\a";
|
|
return 0;
|
|
}
|
|
}
|
|
#==============================================================================
|
|
# Test for wildcards in the groupname. If any wildecards are found, the array
|
|
# @records is expanded with the groupnames that meet the conditions.
|
|
sub ExpandWildCard {
|
|
my $ctrec = 0;
|
|
foreach my $record (@records)
|
|
{
|
|
my @fld=split(/\|/,$record);
|
|
for (my $cnt=0; $cnt <= $#fld; ++$cnt) { for ($fld[$cnt]) { s/^\s+//; s/\s+$//; }}
|
|
|
|
if ($fld[0] =~ /\*|\?/) { # Does it contain the wildcards?
|
|
$fld[0] =~ s/\*/\.\*/g; # Replace * with .* to allow for grep.
|
|
$fld[0] =~ s/\?/\./g; # Replace ? with . to allow for grep.
|
|
|
|
open USRS, "</home/e-smith/db/accounts" or die "Can't open /home/e-smith/db/accounts: $!";
|
|
my @match = grep /^$fld[0]\=group\|/i, <USRS>;
|
|
close(USRS);
|
|
|
|
my $cu = 0;
|
|
foreach my $tst (@match) {
|
|
$tst =~ /\=/; $tst = $`;
|
|
for (my $cnt=1; $cnt <= $#fld; ++$cnt) { $tst = $tst." | ".$fld[$cnt]; };
|
|
if ($cu == 0 ) {
|
|
$records[$ctrec] = $tst;
|
|
$cu =1;
|
|
}
|
|
else {
|
|
push(@records, $tst);
|
|
}
|
|
}
|
|
}
|
|
++$ctrec;
|
|
}
|
|
}
|
|
#==============================================================================
|
|
# Print the pod text as a help screen
|
|
sub PrintPod {
|
|
my ($verbose, $message) = @_;
|
|
pod2usage(-verbose => $verbose, -message => $message, -exitval => 64);
|
|
}
|
|
|
|
#==============================================================================
|
|
|
|
=pod
|
|
|
|
=head1 NAME
|
|
|
|
B<lat-groups> - The lazy administrator's tool to create groups and to add members to it.
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
Creates or deletes groups on Mitel's or SME Servers (5.x/6.x/7.x).
|
|
This tool is functionally equivalent to the 'groups' option in the server-manager, but can be run from the command line or called from an other script.
|
|
It allows you, for example, to add a large number of accounts to a group, or delete groups on a remote machine via an ssh console.
|
|
|
|
See F</usr/doc/lazy-admin-tools/example.groups> for the format of the input file.
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
B<lat-groups> -a [-n] -c "group | descr. | gid | user1| user2"
|
|
|
|
B<lat-groups> -a [-n] -i /path/to/groups.list
|
|
|
|
B<lat-groups> -r [-f] -c "group | user1 | user2"
|
|
|
|
B<lat-groups> -d [-f] -c "group"
|
|
|
|
B<lat-groups> -d [-f] -i /path/to/groups.list
|
|
|
|
=head1 OPTIONS
|
|
|
|
The following options are supported:
|
|
|
|
=over 4
|
|
|
|
=item B<-a>, B<--add>
|
|
|
|
Add a group to the server, or add users to an existing group.
|
|
|
|
=item B<-c "Arguments">, B<--command-line="Aruments">
|
|
|
|
Take arguments from the command line.
|
|
See the 'Arguments' section below for the various arguments that are accepted.
|
|
|
|
=item B<-d>, B<--delete>
|
|
|
|
Delete a group from the server. Wildcards (* and ?) are accepted.
|
|
|
|
=item B<-f>, B<--force>
|
|
|
|
Don't prompt before deleting.
|
|
|
|
=item B<-h>, B<--help>
|
|
|
|
Extended help for this tool
|
|
|
|
=item B<-i FILE>, B<--input-file=FILE>
|
|
|
|
Use the information from F<FILE> to create or delete the group(s).
|
|
See F</usr/doc/lazy-admin-tools> for an example of an input file.
|
|
|
|
=item B<-n>, B<--no-admin>
|
|
|
|
Do not add 'admin' and 'www' as users to this group. Remove them if they are already member of this group.
|
|
|
|
Default behaviour on SME is to add both users to any group, thus limiting the number of groups on the server to 28.
|
|
Note, however, that SME is very persistent with this 'feature'. Each time you add or remove a user via the
|
|
server-manager, both 'admin' and 'www' are added again.
|
|
|
|
=item B<-r>, B<--remove-users>
|
|
|
|
Remove users from this group. Wildcards (* and ?) are accepted for the groupname.
|
|
|
|
|
|
=back
|
|
|
|
=head2 Arguments:
|
|
|
|
group* : Must contain only lower-case letters, numbers,
|
|
hyphens, periods and underscores, and should
|
|
start with a lower-case letter. Wildcards (* and ?)
|
|
can only be used to delete groups.
|
|
descr. : Free-text description of the group
|
|
gid : Group ID. If omitted, a suitable gid will be generated.
|
|
member(s) : Username of a member of the group. Must be an
|
|
existing account on the server.
|
|
|
|
* mandatory field
|
|
|
|
=head1 EXAMPLES
|
|
|
|
B<lat-groups -a -c "hogwarts | School of Witchcraft | | harry | hermione">
|
|
|
|
Creates group 'hogwarts' with users 'harry' and 'hermione'.
|
|
|
|
B<lat-groups -a -i /root/griffindor.list>
|
|
|
|
Creates the groups defined in /root/griffindor.list.
|
|
Refer to /usr/doc/lazy-admin-tools/example.groups for an example of an input file.
|
|
|
|
B<lat-groups -a -n -i /root/griffindor.list>
|
|
|
|
Creates the groups defined in /root/griffindor.list.
|
|
The users 'admin' and 'www' are *not* added to the group.
|
|
|
|
B<lat-groups -d -f -c "class*">
|
|
|
|
Deletes all groups whose name start with 'class'.
|
|
All groups are deleted without prompting (-f).
|
|
|
|
B<lat-groups -r -f -c " * | admin | www">
|
|
|
|
Removes users 'admin' and 'www' from all groups on the system.
|
|
Don't prompt before removing.
|
|
|
|
B<lat-groups -a -c "quidditch | Different ball game | 7005">
|
|
|
|
Creates group 'quidditch' with group ID 7005.
|
|
|
|
=head1 SEE ALSO
|
|
|
|
lat-users(8), lat-pseudonyms(8), lat-ibays(8), lat-quota(8), lat-domains(8), lat-hosts(8), lat-procmail(8), lat-pptp(8), lat-dump(8)
|
|
|
|
=head1 VERSION
|
|
|
|
Version 0.9.0 (2004-09-08). The latest version is hosted at B<http://www.contribs.org/contribs/mblotwijk/>
|
|
|
|
=head1 COPYRIGHT
|
|
|
|
(c)2003-2004, Altiplano bvba (B<http://www.altiplano.be>). Released under the terms of the GNU license.
|
|
|
|
|
|
=head1 BUGS
|
|
|
|
Please report bugs to <Bugs@Altiplano.Be>
|
|
|
|
=cut
|
|
|
|
#==============================================================================
|