3 # Copyright (C)1995-9 Ian Jackson <ijackson@chiark.greenend.org.uk>
5 # This is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2, or (at your option)
10 # It is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
20 print(<<END) || die "groupmanage: write usage: $!\n";
23 groupmanage <groupname> [--info]
24 groupmanage <groupname> <action> [<action> ...]
25 groupmanage <groupname> --create [<action> <action> ...]
28 --add <username> <username> ...
29 --remove <username> <username> ...
31 --manager-add <username> <username> ...
32 --manager-remove <username> <username> ...
34 --owner <username> [root only]
35 groupmanage is Copyright. It is free software, released under the GNU
36 GPL v2 or later. There is NO WARRANTY. See the GPL for details.
41 @ARGV || &usage('too few arguments');
44 exec 'userv','root','groupmanage',@ARGV;
45 &quit("unable to execute userv to gain root privilege: $!");
48 chdir("/etc") || die "groupmanage: chdir /etc: $!\n";
50 $groupname= shift(@ARGV);
51 $groupname =~ y/\n//d;
53 $groupname =~ m/^\w[-0-9A-Za-z]*$/ ||
54 &quit("first argument is invalid - must be group name");
56 @ARGV || push(@ARGV,'--info');
58 if ($ARGV[0] eq '--info') {
59 @ARGV == 1 || &usage('no arguments allowed after --info');
68 $callinguser= exists $ENV{'USERV_UID'} ? $ENV{'USERV_UID'} : $<;
70 %opt= ('user-create','0',
71 'user-create-minunameu','5',
72 'user-create-min','10000',
73 'user-create-max','19999',
74 'user-create-nameintitle','0',
75 'user-create-maxperu','5');
76 %ovalid= ('user-create','boolean',
77 'user-create-minunameu','number',
78 'user-create-min','number',
79 'user-create-max','number',
80 'user-create-nameintitle','boolean',
81 'user-create-maxperu','number');
84 $cov= $_ eq 'yes' ? 1 :
86 &quit("groupmanage.conf:$.: bad boolean value");
90 m/^[0-9]{1,10}$/ || &quit("groupmanage.conf:$.: bad numerical value");
93 open(GMC,"groupmanage.conf") || &quit("read groupmanage.conf: $!");
95 next if m/^\#/ || !m/\S/;
97 s/^\s*([-0-9a-z]+)\s*// || &quit("groupmanage.conf:$.: bad option format");
99 defined($opt{$co}) || &quit("groupmanage.conf:$.: unknown option $co");
101 $ovf= 'ov_'.$ovalid{$co};
108 $callinguser || return;
110 print(STDERR <<END) || &quit("write err re name: $!");
111 groupmanage: groups you create must be named after you ...
112 <usernamepart>-<identifier>
113 You must quote at least $opt{'user-create-minunameu'} chars of your username $createby
114 (or all of it if it is shorter).
119 if ($ARGV[0] eq '--create') {
120 $opt{'user-create'} || !$callinguser ||
121 &quit("group creation by users disabled by administrator");
122 length($groupname) <= 8 || &quit("group names must be 8 chars or fewer");
123 $groupname =~ m/^([-0-9A-Za-z]+)-([0-9a-z]+)$/ || &naming;
126 $!=0; (@pw= getpwuid($callinguser))
127 || &quit("cannot get your passwd entry: $!");
129 $upart eq $createby ||
130 (length($upart) >= $opt{'user-create-minunameu'} &&
131 substr($createby,0,length($upart)) eq $upart)
141 $bythisowner < $opt{'user-create-maxperu'} ||
142 &quit("you already have $bythisowner group(s)");
143 $groupfileix==-1 || &quit("group already exists, cannot create it");
144 $grouplistix==-1 || &quit("group is already in grouplist, cannot create it");
145 for ($gid= $opt{'user-create-min'};
146 $gid < $opt{'user-create-max'} && defined(getgrgid($gid));
148 $gid <= $opt{'user-create-max'} || &quit("out of gids to use, contact admin");
149 $password=''; @members=($createby);
150 $description= "${createby}'s -- user-defined, no title";
151 $owner= $createby; @managers=(); @members= ($createby);
152 $groupfileix=$#groupfile+1;
153 $grouplistix=$#grouplist+1;
154 &p("created group $groupname");
157 &p("modifying group $groupname");
160 &weare($owner) || grep(&weare($_),@managers) || !$callinguser ||
161 &quit("you may not manage $groupname");
166 if (m/^--(add|remove)$/) {
167 $action= $1; $clist= 'members'; $what= 'member';
168 } elsif (m/^--owner$/) {
169 !$callinguser || &quit("only root may change owner");
170 @ARGV || &usage("no username owner after --owner");
171 $owner= shift(@ARGV);
172 &p("owner set to $owner");
173 } elsif (m/^--manager-(add|remove)$/) {
174 $action= $1; $clist= 'managers'; $what= 'manager';
175 } elsif (m/^--clear$/) {
176 &p('cleared list of members');
177 @members=(); $action='none'; $memc++;
178 } elsif (m/^--manager-clear$/) {
179 &p('cleared list of managers');
180 @managers=(); $action='none';
181 } elsif (m/^--title$/) {
182 &weare($owner) || !$callinguser ||
183 &quit("only group's owner ($owner) may change title");
184 @ARGV || &usage("no title after --title");
185 $_= shift(@ARGV); y/\020-\176//cd; y/:\\//d;
186 if ($opt{'user-create-nameintitle'} &&
187 $gid >= $opt{'user-create-min'} && $gid <= $opt{'user-create-max'}) {
188 $_= "${owner}'s -- $_";
191 &p("title set to $description");
193 &usage("unknown option $_");
194 } elsif (m/^\w[-0-9A-Za-z]*$/) {
197 getpwnam($chgu) || &quit("username $chgu does not exist");
198 eval "\@l = \@$clist; 1" || &quit("internal error: $@");
199 $already= grep($_ eq $chgu, @l);
200 if ($action eq 'add') {
202 &p("$chgu already $what");
204 &p("added $what $chgu");
206 $memc+= ($clist eq 'members');
208 } elsif ($action eq 'remove') {
210 &p("removed $what $chgu");
211 @l= grep($_ ne $chgu, @l);
212 $memc+= ($clist eq 'members');
214 &p("$chgu is already not $what");
217 &usage("username found but no action to take for them");
219 eval "\@$clist = \@l; 1" || &quit("internal error: $@");
221 &usage("bad username or option $_");
224 &p("nb: a change to group membership only takes effect at the user's next login")
226 $groupfile[$groupfileix]=
227 "$groupname:$password:$gid:".join(',',@members)."\n";
228 $grouplist[$grouplistix]=
229 "$groupname:$description:$owner:".join(',',@managers).":$homedir\n";
230 &save('group',@groupfile);
231 &save('grouplist',@grouplist);
232 unlink('gtmp') || &quit("unlock group (remove gtmp): $!");
237 open(GF,"< group") || &quit("read group: $!");
238 @groupfile=<GF>; close(GF);
240 for ($i=0; $i<=$#groupfile; $i++) {
241 $_= $groupfile[$i]; s/\n$//;
243 m/^(\w[-0-9A-Za-z]*):([^:]*):(\d+):([-0-9A-Za-z,]*)$/ ||
244 &quit("bad entry in group: $_");
246 next unless $1 eq $groupname;
247 $groupfileix<0 || &quit("duplicate entries in group");
251 @members= split(/,/,$4);
253 open(GL,"< grouplist") || &quit("read grouplist: $!");
254 @grouplist=<GL>; close(GL);
256 for ($i=0; $i<=$#grouplist; $i++) {
257 $_= $grouplist[$i]; s/\n$//;
259 m/^(\w[-0-9A-Za-z]*):([^:]*):(\w[-0-9A-Za-z]*):([-0-9A-Za-z,]*):([^:]*)$/ ||
260 &quit("bad entry in grouplist: $_");
261 $bythisowner++ if ($create && $3 eq $createby &&
262 $gname2gid{$1} >= $opt{'user-create-min'} &&
263 $gname2gid{$1} <= $opt{'user-create-max'});
264 next unless $1 eq $groupname;
265 $grouplistix<0 || &quit("duplicate entries in grouplist");
270 @managers= split(/,/,$4);
275 $grouplistix>=0 || &quit("no entry in grouplist for $groupname");
276 $groupfileix>=0 || &quit("no entry in group for $groupname");
280 return 0 if $_[0] eq '';
281 @pw= getpwnam($_[0]);
282 return @pw && $pw[2] == $callinguser ? 1 : 0;
286 $filename= shift(@_);
287 unlink("$filename~");
288 open(DUMP,"> $filename.new") || &quit("create new $filename: $!");
289 print(DUMP @_) || &quit("write new $filename: $!");
290 close(DUMP) || &quit("close new $filename: $!");
291 link("$filename","$filename~") || &quit("create backup $filename: $!");
292 rename("$filename.new","$filename") || &quit("install new $filename: $!");
298 die "groupmanage: @_\n";
302 link('group','gtmp') || &quit("create gtmp: $!");
307 return unless $locked;
309 unlink('gtmp') || warn("unlock group file (remove gtmp): $!\n");
313 print(<<END) || &quit("write to stdout: $!\n");
316 description $description
325 print(STDOUT "$stdout_string") || &quit("write to stdout: $!\n");
330 $stdout_string.= $_[0]."\n";