1#!/usr/bin/perl 2# 3 4use strict; 5 6use Getopt::Long; 7use Cwd qw(abs_path); 8 9my $opt_help = 0; 10my $opt_passwd_path = undef; 11my $opt_group_path = undef; 12my $opt_action = undef; 13my $opt_type = undef; 14my $opt_name = undef; 15my $opt_member = undef; 16 17my $passwdfn = undef; 18my $groupfn = undef; 19my $memberfn = undef; 20my $actionfn = undef; 21 22sub passwd_add($$$$); 23sub passwd_delete($$$$); 24sub group_add($$$$); 25sub group_delete($$$$); 26sub member_add($$$$); 27sub member_delete($$$$); 28 29sub check_path($$); 30 31my $result = GetOptions( 32 'help|h|?' => \$opt_help, 33 'passwd_path=s' => \$opt_passwd_path, 34 'group_path=s' => \$opt_group_path, 35 'action=s' => \$opt_action, 36 'type=s' => \$opt_type, 37 'name=s' => \$opt_name, 38 'member=s' => \$opt_member 39); 40 41sub usage($;$) 42{ 43 my ($ret, $msg) = @_; 44 45 print $msg."\n\n" if defined($msg); 46 47 print "usage: 48 49 --help|-h|-? Show this help. 50 51 --passwd_path <path> Path of the 'passwd' file. 52 --group_path <path> Path of the 'group' file. 53 54 --type <type> 'passwd', 'group' and 'member' are supported. 55 56 --action <action> 'add' or 'delete'. 57 58 --name <name> The name of the object. 59 60 --member <member> The name of the member. 61"; 62 exit($ret); 63} 64 65usage(1) if (not $result); 66 67usage(0) if ($opt_help); 68 69if (not defined($opt_action)) { 70 usage(1, "missing: --action [add|delete]"); 71} 72if ($opt_action eq "add") { 73 $passwdfn = \&passwd_add; 74 $groupfn = \&group_add; 75 $memberfn = \&member_add; 76} elsif ($opt_action eq "delete") { 77 $passwdfn = \&passwd_delete; 78 $groupfn = \&group_delete; 79 $memberfn = \&member_delete; 80} else { 81 usage(1, "invalid: --action [add|delete]: '$opt_action'"); 82} 83 84if (not defined($opt_type)) { 85 usage(1, "missing: --type [passwd|group|member]"); 86} 87if ($opt_type eq "member" and not defined($opt_member)) { 88 usage(1, "missing: --member <member>"); 89} 90my $opt_fullpath_passwd; 91my $opt_fullpath_group; 92if ($opt_type eq "passwd") { 93 $actionfn = $passwdfn; 94 $opt_fullpath_passwd = check_path($opt_passwd_path, $opt_type); 95} elsif ($opt_type eq "group") { 96 $actionfn = $groupfn; 97 $opt_fullpath_group = check_path($opt_group_path, $opt_type); 98} elsif ($opt_type eq "member") { 99 $actionfn = $memberfn; 100 $opt_fullpath_passwd = check_path($opt_passwd_path, "passwd"); 101 $opt_fullpath_group = check_path($opt_group_path, "group"); 102} else { 103 usage(1, "invalid: --type [passwd|group]: '$opt_type'") 104} 105 106if (not defined($opt_name)) { 107 usage(1, "missing: --name <name>"); 108} 109if ($opt_name eq "") { 110 usage(1, "invalid: --name <name>"); 111} 112 113exit $actionfn->($opt_fullpath_passwd, $opt_member, $opt_fullpath_group, $opt_name); 114 115sub check_path($$) 116{ 117 my ($path,$type) = @_; 118 119 if (not defined($path)) { 120 usage(1, "missing: --$type\_path <path>"); 121 } 122 if ($path eq "" or $path eq "/") { 123 usage(1, "invalid: --$type\_path <path>: '$path'"); 124 } 125 my $fullpath = abs_path($path); 126 if (not defined($fullpath)) { 127 usage(1, "invalid: --$type\_path <path>: '$path'"); 128 } 129 return $fullpath; 130} 131 132sub passwd_add_entry($$); 133 134sub passwd_load($) 135{ 136 my ($path) = @_; 137 my @lines; 138 my $passwd = undef; 139 140 open(PWD, "<$path") or die("Unable to open '$path' for read"); 141 @lines = <PWD>; 142 close(PWD); 143 144 $passwd->{array} = (); 145 $passwd->{name} = {}; 146 $passwd->{uid} = {}; 147 $passwd->{path} = $path; 148 149 foreach my $line (@lines) { 150 passwd_add_entry($passwd, $line); 151 } 152 153 return $passwd; 154} 155 156sub group_add_entry($$); 157 158sub group_load($) 159{ 160 my ($path) = @_; 161 my @lines; 162 my $group = undef; 163 164 open(GROUP, "<$path") or die("Unable to open '$path' for read"); 165 @lines = <GROUP>; 166 close(GROUP); 167 168 $group->{array} = (); 169 $group->{name} = {}; 170 $group->{gid} = {}; 171 $group->{path} = $path; 172 173 foreach my $line (@lines) { 174 group_add_entry($group, $line); 175 } 176 177 return $group; 178} 179 180sub passwd_lookup_name($$) 181{ 182 my ($passwd, $name) = @_; 183 184 return undef unless defined($passwd->{name}{$name}); 185 186 return $passwd->{name}{$name}; 187} 188 189sub group_lookup_name($$) 190{ 191 my ($group, $name) = @_; 192 193 return undef unless defined($group->{name}{$name}); 194 195 return $group->{name}{$name}; 196} 197 198sub passwd_lookup_uid($$) 199{ 200 my ($passwd, $uid) = @_; 201 202 return undef unless defined($passwd->{uid}{$uid}); 203 204 return $passwd->{uid}{$uid}; 205} 206 207sub group_lookup_gid($$) 208{ 209 my ($group, $gid) = @_; 210 211 return undef unless defined($group->{gid}{$gid}); 212 213 return $group->{gid}{$gid}; 214} 215 216sub passwd_get_free_uid($) 217{ 218 my ($passwd) = @_; 219 my $uid = 1000; 220 221 while (passwd_lookup_uid($passwd, $uid)) { 222 $uid++; 223 } 224 225 return $uid; 226} 227 228sub group_get_free_gid($) 229{ 230 my ($group) = @_; 231 my $gid = 1000; 232 233 while (group_lookup_gid($group, $gid)) { 234 $gid++; 235 } 236 237 return $gid; 238} 239 240sub passwd_add_entry($$) 241{ 242 my ($passwd, $str) = @_; 243 244 chomp $str; 245 my @e = split(':', $str); 246 247 push(@{$passwd->{array}}, \@e); 248 $passwd->{name}{$e[0]} = \@e; 249 $passwd->{uid}{$e[2]} = \@e; 250} 251 252sub group_add_entry($$) 253{ 254 my ($group, $str) = @_; 255 256 chomp $str; 257 my @e = split(':', $str); 258 259 push(@{$group->{array}}, \@e); 260 $group->{name}{$e[0]} = \@e; 261 $group->{gid}{$e[2]} = \@e; 262} 263 264sub passwd_remove_entry($$) 265{ 266 my ($passwd, $eref) = @_; 267 268 for (my $i = 0; defined($passwd->{array}[$i]); $i++) { 269 if ($eref == $passwd->{array}[$i]) { 270 $passwd->{array}[$i] = undef; 271 } 272 } 273 274 delete $passwd->{name}{${$eref}[0]}; 275 delete $passwd->{uid}{${$eref}[2]}; 276} 277 278sub group_remove_entry($$) 279{ 280 my ($group, $eref) = @_; 281 282 for (my $i = 0; defined($group->{array}[$i]); $i++) { 283 if ($eref == $group->{array}[$i]) { 284 $group->{array}[$i] = undef; 285 } 286 } 287 288 delete $group->{name}{${$eref}[0]}; 289 delete $group->{gid}{${$eref}[2]}; 290} 291 292sub group_add_member($$$) 293{ 294 my ($group, $eref, $username) = @_; 295 296 my @members; 297 my $str = @$eref[3] || undef; 298 if ($str) { 299 @members = split(",", $str); 300 } 301 302 foreach my $member (@members) { 303 if ($member and $member eq $username) { 304 die("account[$username] is already member of '@$eref[0]'"); 305 } 306 } 307 308 push(@members, $username); 309 310 my $gwent = @$eref[0].":x:".@$eref[2].":".join(",", @members); 311 312 group_remove_entry($group, $eref); 313 314 group_add_entry($group, $gwent); 315} 316 317sub group_delete_member($$$) 318{ 319 my ($group, $eref, $username) = @_; 320 321 my @members = undef; 322 my $str = @$eref[3] || undef; 323 if ($str) { 324 @members = split(",", $str); 325 } 326 my @new_members; 327 my $removed = 0; 328 329 foreach my $member (@members) { 330 if ($member and $member ne $username) { 331 push(@new_members, $member); 332 } else { 333 $removed = 1; 334 } 335 } 336 337 if ($removed != 1) { 338 die("account[$username] is not member of '@$eref[0]'"); 339 } 340 341 my $gwent = @$eref[0].":x:".@$eref[2].":".join(",", @new_members); 342 343 group_remove_entry($group, $eref); 344 345 group_add_entry($group, $gwent); 346} 347 348sub passwd_save($) 349{ 350 my ($passwd) = @_; 351 my @lines = (); 352 my $path = $passwd->{path}; 353 my $tmppath = $path.$$; 354 355 foreach my $eref (@{$passwd->{array}}) { 356 next unless defined($eref); 357 358 my $line = join(':', @{$eref}); 359 push(@lines, $line); 360 } 361 362 open(PWD, ">$tmppath") or die("Unable to open '$tmppath' for write"); 363 print PWD join("\n", @lines)."\n"; 364 close(PWD); 365 rename($tmppath, $path) or die("Unable to rename $tmppath => $path"); 366} 367 368sub group_save($) 369{ 370 my ($group) = @_; 371 my @lines = (); 372 my $path = $group->{path}; 373 my $tmppath = $path.$$; 374 375 foreach my $eref (@{$group->{array}}) { 376 next unless defined($eref); 377 378 my $line = join(':', @{$eref}); 379 if (scalar(@{$eref}) == 3) { 380 $line .= ":"; 381 } 382 push(@lines, $line); 383 } 384 385 open(GROUP, ">$tmppath") or die("Unable to open '$tmppath' for write"); 386 print GROUP join("\n", @lines)."\n"; 387 close(GROUP); 388 rename($tmppath, $path) or die("Unable to rename $tmppath => $path"); 389} 390 391sub passwd_add($$$$) 392{ 393 my ($path, $dummy, $dummy2, $name) = @_; 394 395 #print "passwd_add: '$name' in '$path'\n"; 396 397 my $passwd = passwd_load($path); 398 399 my $e = passwd_lookup_name($passwd, $name); 400 die("account[$name] already exists in '$path'") if defined($e); 401 402 my $uid = passwd_get_free_uid($passwd); 403 my $gid = 65534;# nogroup gid 404 405 my $pwent = $name.":x:".$uid.":".$gid.":".$name." gecos:/nodir:/bin/false"; 406 407 passwd_add_entry($passwd, $pwent); 408 409 passwd_save($passwd); 410 411 return 0; 412} 413 414sub passwd_delete($$$$) 415{ 416 my ($path, $dummy, $dummy2, $name) = @_; 417 418 #print "passwd_delete: '$name' in '$path'\n"; 419 420 my $passwd = passwd_load($path); 421 422 my $e = passwd_lookup_name($passwd, $name); 423 die("account[$name] does not exists in '$path'") unless defined($e); 424 425 passwd_remove_entry($passwd, $e); 426 427 passwd_save($passwd); 428 429 return 0; 430} 431 432sub group_add($$$$) 433{ 434 my ($dummy, $dummy2, $path, $name) = @_; 435 436 #print "group_add: '$name' in '$path'\n"; 437 438 my $group = group_load($path); 439 440 my $e = group_lookup_name($group, $name); 441 die("group[$name] already exists in '$path'") if defined($e); 442 443 my $gid = group_get_free_gid($group); 444 445 my $gwent = $name.":x:".$gid.":".""; 446 447 group_add_entry($group, $gwent); 448 449 group_save($group); 450 451 #printf("%d\n", $gid); 452 453 return 0; 454} 455 456sub group_delete($$$$) 457{ 458 my ($dummy, $dummy2, $path, $name) = @_; 459 460 #print "group_delete: '$name' in '$path'\n"; 461 462 my $group = group_load($path); 463 464 my $e = group_lookup_name($group, $name); 465 die("group[$name] does not exists in '$path'") unless defined($e); 466 467 group_remove_entry($group, $e); 468 469 group_save($group); 470 471 return 0; 472} 473 474sub member_add($$$$) 475{ 476 my ($passwd_path, $username, $group_path, $groupname) = @_; 477 478 #print "member_add: adding '$username' in '$passwd_path' to '$groupname' in '$group_path'\n"; 479 480 my $group = group_load($group_path); 481 482 my $g = group_lookup_name($group, $groupname); 483 die("group[$groupname] does not exists in '$group_path'") unless defined($g); 484 485 my $passwd = passwd_load($passwd_path); 486 487 my $u = passwd_lookup_name($passwd, $username); 488 die("account[$username] does not exists in '$passwd_path'") unless defined($u); 489 490 group_add_member($group, $g, $username); 491 492 group_save($group); 493 494 return 0; 495} 496 497sub member_delete($$$$) 498{ 499 my ($passwd_path, $username, $group_path, $groupname) = @_; 500 501 #print "member_delete: removing '$username' in '$passwd_path' from '$groupname' in '$group_path'\n"; 502 503 my $group = group_load($group_path); 504 505 my $g = group_lookup_name($group, $groupname); 506 die("group[$groupname] does not exists in '$group_path'") unless defined($g); 507 508 my $passwd = passwd_load($passwd_path); 509 510 my $u = passwd_lookup_name($passwd, $username); 511 die("account[$username] does not exists in '$passwd_path'") unless defined($u); 512 513 group_delete_member($group, $g, $username); 514 515 group_save($group); 516 517 return 0; 518} 519