#
# $FreeBSD$
+use Fcntl;
+
sub LOCK_SH {0x01;}
sub LOCK_EX {0x02;}
sub LOCK_NB {0x04;}
umask(022);
$whoami = $0;
$passwd_file = "/etc/master.passwd";
-$new_passwd_file = "${passwd_file}.new.$$";
+$passwd_tmp = "/etc/ptmp";
$group_file = "/etc/group";
$new_group_file = "${group_file}.new.$$";
$mail_dir = "/var/mail";
$crontab_dir = "/var/cron/tabs";
-$atjob_dir = "/var/at/jobs";
$affirm = 0;
#$debug = 1;
if ($#ARGV == 0) {
# Username was given as a parameter
$login_name = pop(@ARGV);
- die "Sorry, login name must contain alphanumeric characters only.\n"
- if ($login_name !~ /^[a-zA-Z0-9_]\w*$/);
} else {
if ($affirm) {
print STDERR "${whoami}: Error: -y option given without username!\n";
$login_name = &get_login_name;
}
-if (($pw_ent = &check_login_name($login_name)) eq '0') {
+($name, $password, $uid, $gid, $change, $class, $gecos, $home_dir, $shell) =
+ (getpwnam("$login_name"));
+
+if (!defined $uid) {
print STDERR "${whoami}: Error: User ${login_name} not in password database\n";
&unlockpw;
exit 1;
}
-($name, $password, $uid, $gid, $class, $change, $expire, $gecos, $home_dir,
- $shell) = split(/:/, $pw_ent);
-
if ($uid == 0) {
print "${whoami}: Error: I'd rather not remove a user with a uid of 0.\n";
&unlockpw;
}
if (! $affirm) {
- print "Matching password entry:\n\n$pw_ent\n\n";
+ print "Matching password entry:\n\n$name\:$password\:$uid\:$gid\:$class\:$change\:0\:$gecos\:$home_dir\:$shell\n\n";
$ans = &get_yn("Is this the entry you wish to remove? ");
# Remove the user's at jobs, if any
# (probably also needs to be done before password databases are updated)
-&remove_at_jobs($login_name, $uid);
+&remove_at_jobs($login_name);
#
# Kill all the user's processes
for ($done = 0; ! $done; ) {
print "Enter login name for user to remove: ";
$login_name = <>;
- chop $login_name;
- if (!($login_name =~ /^[a-z0-9_][a-z0-9_\-]*$/)) {
- print STDERR "Sorry, login name must contain alphanumeric characters only.\n";
- } elsif (length($login_name) > 16 || length($login_name) == 0) {
- print STDERR "Sorry, login name must be 16 characters or less.\n";
+ chomp $login_name;
+ if (not getpwnam("$login_name")) {
+ print STDERR "Sorry, login name not in password database.\n";
} else {
$done = 1;
}
return($login_name);
}
-sub check_login_name {
- #
- # Check to see whether login name is in password file
- local($login_name) = @_;
- local($Mname, $Mpassword, $Muid, $Mgid, $Mclass, $Mchange, $Mexpire,
- $Mgecos, $Mhome_dir, $Mshell);
- local($i);
-
- seek(MASTER_PW, 0, 0);
- while ($i = <MASTER_PW>) {
- chop $i;
- ($Mname, $Mpassword, $Muid, $Mgid, $Mclass, $Mchange, $Mexpire,
- $Mgecos, $Mhome_dir, $Mshell) = split(/:/, $i);
- if ($Mname eq $login_name) {
- seek(MASTER_PW, 0, 0);
- return($i); # User is in password database
- }
- }
- seek(MASTER_PW, 0, 0);
-
- return '0'; # User wasn't found
-}
-
sub get_yn {
#
# Get a yes or no answer; return 'Y' or 'N'
}
sub update_passwd_file {
- local($skipped, $i);
+ local($skipped);
print STDERR "Updating password file,";
seek(MASTER_PW, 0, 0);
- open(NEW_PW, ">$new_passwd_file") ||
- die "\n${whoami}: Error: Couldn't open file ${new_passwd_file}:\n $!\n";
- chmod(0600, $new_passwd_file) ||
- print STDERR "\n${whoami}: Warning: couldn't set mode of $new_passwd_file to 0600 ($!)\n\tcontinuing, but please check mode of /etc/master.passwd!\n";
+
+ sysopen(NEW_PW, $passwd_tmp, O_RDWR|O_CREAT|O_EXCL, 0600) ||
+ die "\n${whoami}: Error: Couldn't open file ${passwd_tmp}:\n $!\n";
+
$skipped = 0;
- while ($i = <MASTER_PW>) {
- if ($i =~ /\n$/) {
- chop $i;
- }
- if ($i ne $pw_ent) {
- print NEW_PW "$i\n";
- } else {
+ while (<MASTER_PW>) {
+ if (/^\Q$login_name:/o) {
print STDERR "Dropped entry for $login_name\n" if $debug;
$skipped = 1;
+ } else {
+ print NEW_PW;
+ # The other perl password tools assume all lowercase entries.
+ # Add a warning to help unsuspecting admins who might be
+ # using the wrong tool for the job, or might otherwise
+ # be unwittingly holding a loaded foot-shooting device.
+ if (/^\Q$login_name:/io) {
+ my $name = $_;
+ $name =~ s#\:.*\n##;
+ print STDERR "\n\n\tThere is also an entry for $name in your",
+ "password file.\n\tThis can cause problems in some ",
+ "situations.\n\n";
+ }
}
}
close(NEW_PW);
if ($skipped == 0) {
print STDERR "\n${whoami}: Whoops! Didn't find ${login_name}'s entry second time around!\n";
- unlink($new_passwd_file) ||
- print STDERR "\n${whoami}: Warning: couldn't unlink $new_passwd_file ($!)\n\tPlease investigate, as this file should not be left in the filesystem\n";
+ unlink($passwd_tmp) ||
+ print STDERR "\n${whoami}: Warning: couldn't unlink $passwd_tmp ($!)\n\tPlease investigate, as this file should not be left in the filesystem\n";
&unlockpw;
exit 1;
}
# Run pwd_mkdb to install the updated password files and databases
print STDERR " updating databases,";
- system('/usr/sbin/pwd_mkdb', '-p', ${new_passwd_file});
+ system('/usr/sbin/pwd_mkdb', '-p', ${passwd_tmp});
print STDERR " done.\n";
close(MASTER_PW); # Not useful anymore
chown($group_uid, $group_gid, $new_group_file) ||
print STDERR "\n${whoami}: Warning: could not set owner/group of new group file to ${group_uid}/${group_gid} ($!)\n\rContinuing, but please check ownership of $group_file!\n";
while ($i = <GROUP>) {
- if (!($i =~ /$login_name/)) {
+ if (!($i =~ /\Q$login_name\E/)) {
# Line doesn't contain any references to the user, so just add it
# to the new file
print NEW_GROUP $i;
printf STDERR " done.\n";
}
-sub remove_at_jobs {
- local($login_name, $uid) = @_;
- local($i, $owner, $found);
- $found = 0;
- opendir(ATDIR, $atjob_dir) || return;
- while ($i = readdir(ATDIR)) {
- next if $i eq '.';
- next if $i eq '..';
- next if $i eq '.lockfile';
-
- $owner = (stat("$atjob_dir/$i"))[4]; # UID
- if ($uid == $owner) {
- if (!$found) {
- print STDERR "Removing user's at jobs:";
- $found = 1;
- }
- # Use atrm to remove the job
- print STDERR " $i";
- system('/usr/bin/atrm', $i);
+sub invoke_atq {
+ local *ATQ;
+ my($user) = (shift || "");
+ my($path_atq) = "/usr/bin/atq";
+ my(@at) = ();
+ my($pid, $line);
+
+ return @at if ($user eq "");
+
+ if (!defined($pid = open(ATQ, "-|"))) {
+ die("creating pipe to atq: $!\n");
+ } elsif ($pid == 0) {
+ exec($path_atq, $user);
+ die("executing $path_atq: $!\n");
+ }
+
+ while(defined($_ = <ATQ>)) {
+ chomp;
+ if (/^\d\d.\d\d.\d\d\s+\d\d.\d\d.\d\d\s+(\S+)\s+\S+\s+(\d+)$/) {
+ push(@at, $2) if ($1 eq $user);
}
}
- closedir(ATDIR);
- if ($found) {
- print STDERR " done.\n";
+ close ATQ;
+ return @at;
+}
+
+sub invoke_atrm {
+ local *ATRM;
+ my($user) = (shift || "");
+ my($path_atrm) = "/usr/bin/atrm";
+ my(@jobs) = @_;
+ my($pid);
+ my($txt) = "";
+
+ return "Invalid arguments" if (($user eq "") || ($#jobs == -1));
+
+ if (!defined($pid = open(ATRM, "-|"))) {
+ die("creating pipe to atrm: $!\n");
+ } elsif ($pid == 0) {
+ exec($path_atrm, $user, @jobs);
+ }
+
+ while(defined($_ = <ATRM>)) {
+ $txt .= $_;
+ }
+ close ATRM;
+ return $txt;
+}
+
+sub remove_at_jobs {
+ my($user) = (shift || "");
+ my(@at, $atrm);
+
+ return 1 if ($user eq "");
+
+ @at = invoke_atq($user);
+ return 0 if ($#at == -1);
+
+ print STDERR "Removing user's at jobs:";
+ print STDERR " @at:";
+ $atrm = invoke_atrm($user, @at);
+ if ($atrm ne "") {
+ print STDERR " -- $atrm\n";
+ return 1;
}
+
+ print STDERR " done.\n";
+ return 0;
}
sub resolvelink {