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