mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-15 10:17:20 +00:00
Cleaned up some messages, added a check to remove a leftover popd file
from /var/mail, added a routine to delete the removed user's files from /tmp, /var/tmp, & /var/tmp/vi.recover, and added code to kill any running processes owned by the removed user). I've also added a flag for non-interactive execution, cleaned up the man page, and adjusted my address. Submitted by: ghelmer@cs.iastate.edu (Guy Helmer)
This commit is contained in:
parent
53a8ba6224
commit
1e85e4dbb0
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=23534
@ -1,5 +1,5 @@
|
||||
.\" Copyright 1995, 1996
|
||||
.\" Guy Helmer, Madison, South Dakota 57042. All rights reserved.
|
||||
.\" Copyright 1995, 1996, 1997
|
||||
.\" Guy Helmer, Ames, Iowa 50014. All rights reserved.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
@ -26,50 +26,92 @@
|
||||
.\"
|
||||
.\" $Id$
|
||||
.\"
|
||||
.Dd July 16, 1996
|
||||
.Dd February 23, 1997
|
||||
.Dt RMUSER 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm rmuser
|
||||
.Nd remove users from the system
|
||||
.Nd removes users from the system
|
||||
.Sh SYNOPSIS
|
||||
.Nm rmuser
|
||||
.Op Fl y
|
||||
.Op Ar username
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
The utility
|
||||
.Nm rmuser
|
||||
utility removes a user's
|
||||
.Pp
|
||||
.Bl -enum
|
||||
.It
|
||||
Removes the user's
|
||||
.Xr crontab 1
|
||||
entry (if any) and any
|
||||
entry (if any)
|
||||
.It
|
||||
Removes any
|
||||
.Xr at 1
|
||||
jobs belonging to the user,
|
||||
then removes a user from the system's local password file, removes
|
||||
the user's home directory if it is owned by the user, and removes
|
||||
the user's incoming mail file if it exists. The username is removed
|
||||
from any groups to which it belongs in the file
|
||||
jobs belonging to the user
|
||||
.It
|
||||
Sends a SIGKILL signal to all processes owned by the user
|
||||
.It
|
||||
Removes the user from the system's local password file
|
||||
.It
|
||||
Removes the user's home directory (if it is owned by the user),
|
||||
including handling of symbolic links in the path to the actual home
|
||||
directory
|
||||
.It
|
||||
Removes the incoming mail and pop daemon mail files belonging to the
|
||||
user from
|
||||
.Pa /var/mail
|
||||
.It
|
||||
Removes all files owned by the user from
|
||||
.Pa /tmp ,
|
||||
.Pa /var/tmp ,
|
||||
and
|
||||
.Pa /var/tmp/vi.recover .
|
||||
.It
|
||||
Removes the username from all groups to which it belongs in
|
||||
.Pa /etc/group .
|
||||
If a group becomes empty and the group name is the same as the username,
|
||||
the group is removed (this complements
|
||||
(If a group becomes empty and the group name is the same as the username,
|
||||
the group is removed; this complements
|
||||
.Xr adduser 8 's
|
||||
per-user unique groups).
|
||||
.El
|
||||
.Pp
|
||||
.Nm rmuser
|
||||
politely refuses to remove users whose uid is 0 (typically root), since
|
||||
it seemed like a good idea at the time
|
||||
.Nm rmuser
|
||||
was written.
|
||||
certain actions (namely, killing all the user's processes, and perhaps
|
||||
removing the user's home directory) would cause damage to a running system.
|
||||
If it is necessary to remove a user whose uid is 0, see
|
||||
.Xr vipw 8
|
||||
for information on directly editing the password file, by which the desired
|
||||
user's
|
||||
.Xr passwd 5
|
||||
entry may be removed manually.
|
||||
.Pp
|
||||
If not running "affirmitively" (i.e., option
|
||||
.Fl y
|
||||
is not specified),
|
||||
.Nm rmuser
|
||||
shows the selected user's password file entry and asks for confirmation
|
||||
that you wish to remove the user. If the user's home directory is owned
|
||||
by the user (and not by any other user),
|
||||
by the user,
|
||||
.Nm rmuser
|
||||
asks whether you wish to remove the user's home directory and everything
|
||||
below.
|
||||
.Pp
|
||||
As
|
||||
.Nm rmuser
|
||||
operates, it informs the user regarding the current activity. If any
|
||||
errors occur, they are posted to standard error and, if it is possible for
|
||||
.Nm rmuser
|
||||
to continue, it will.
|
||||
.Pp
|
||||
Available options:
|
||||
.Pp
|
||||
.Bl -tag -width username
|
||||
.It Fl y
|
||||
Affirm - any question that would be asked is answered implicitly in
|
||||
the affirmative (i.e., yes). A username must also be specified on the
|
||||
command line if this option is used.
|
||||
.It Ar \&username
|
||||
Identifies the user to be removed; if not present,
|
||||
.Nm rmuser
|
||||
@ -98,7 +140,30 @@ interactively asks for the user to be removed.
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Nm
|
||||
command appeared in
|
||||
.Fx 2.1.5 .
|
||||
command appeared in
|
||||
.Fx 2.2 .
|
||||
.\" .Sh AUTHOR
|
||||
.\" Guy Helmer, Madison, South Dakota
|
||||
.\" Guy Helmer, Ames, Iowa
|
||||
.Sh BUGS
|
||||
.Nm rmuser
|
||||
does not comprehensively search the filesystem for all files
|
||||
owned by the removed user and remove them; to do so on a system
|
||||
of any size is prohibitively slow and I/O intensive.
|
||||
.Nm rmuser
|
||||
also is unable to remove symbolic links that were created by the
|
||||
user in
|
||||
.Pa /tmp
|
||||
or
|
||||
.Pa /var/tmp
|
||||
as symbolic links on 4.4BSD filesystems do not contain information
|
||||
as to who created them. Also, there may be other files created in
|
||||
.Pa /var/mail
|
||||
other than
|
||||
.Pa /var/mail/username
|
||||
and
|
||||
.Pa /var/mail/.pop.username
|
||||
that are not owned by the removed user but should be removed.
|
||||
.Pp
|
||||
.Nm rmuser
|
||||
has no knowledge of NIS (Yellow Pages), and it operates only on the
|
||||
local password file.
|
||||
|
@ -1,6 +1,6 @@
|
||||
#!/usr/bin/perl
|
||||
# -*- perl -*-
|
||||
# Copyright 1995, 1996 Guy Helmer, Madison, South Dakota 57042.
|
||||
# Copyright 1995, 1996, 1997 Guy Helmer, Ames, Iowa 50014.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
@ -28,7 +28,7 @@
|
||||
#
|
||||
# rmuser - Perl script to remove users
|
||||
#
|
||||
# Guy Helmer <ghelmer@alpha.dsu.edu>, 07/17/96
|
||||
# Guy Helmer <ghelmer@cs.iastate.edu>, 02/23/97
|
||||
#
|
||||
# $Id$
|
||||
|
||||
@ -48,6 +48,7 @@ $new_group_file = "${group_file}.new.$$";
|
||||
$mail_dir = "/var/mail";
|
||||
$crontab_dir = "/var/cron/tabs";
|
||||
$atjob_dir = "/var/at/jobs";
|
||||
$affirm = 0;
|
||||
|
||||
#$debug = 1;
|
||||
|
||||
@ -72,7 +73,7 @@ sub lockpw {
|
||||
fcntl(MASTER_PW, &F_SETFD, 1);
|
||||
# Apply an advisory lock the password file
|
||||
if (!flock(MASTER_PW, &LOCK_EX|&LOCK_NB)) {
|
||||
print STDERR "Couldn't lock ${passwd_file}: $!\n";
|
||||
print STDERR "${whoami}: Error: Couldn't lock ${passwd_file}: $!\n";
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
@ -86,8 +87,13 @@ $SIG{'QUIT'} = 'cleanup';
|
||||
$SIG{'HUP'} = 'cleanup';
|
||||
$SIG{'TERM'} = 'cleanup';
|
||||
|
||||
if ($#ARGV == 1 && $ARGV[0] eq '-y') {
|
||||
shift @ARGV;
|
||||
$affirm = 1;
|
||||
}
|
||||
|
||||
if ($#ARGV > 0) {
|
||||
print STDERR "usage: ${whoami} [username]\n";
|
||||
print STDERR "usage: ${whoami} [-y] [username]\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@ -104,6 +110,11 @@ if ($#ARGV == 0) {
|
||||
die "Sorry, login name must contain alphanumeric characters only.\n"
|
||||
if ($login_name !~ /^[a-z0-9_][a-z0-9_\-]*$/);
|
||||
} else {
|
||||
if ($affirm) {
|
||||
print STDERR "${whoami}: Error: -y option given without username!\n";
|
||||
&unlockpw;
|
||||
exit 1;
|
||||
}
|
||||
# Get the user name from the user
|
||||
$login_name = &get_login_name;
|
||||
}
|
||||
@ -118,19 +129,21 @@ if (($pw_ent = &check_login_name($login_name)) eq '0') {
|
||||
$shell) = split(/:/, $pw_ent);
|
||||
|
||||
if ($uid == 0) {
|
||||
print "${whoami}: Sorry, I'd rather not remove a user with a uid of 0.\n";
|
||||
print "${whoami}: Error: I'd rather not remove a user with a uid of 0.\n";
|
||||
&unlockpw;
|
||||
exit 1;
|
||||
}
|
||||
|
||||
print "Matching password entry:\n\n$pw_ent\n\n";
|
||||
if (! $affirm) {
|
||||
print "Matching password entry:\n\n$pw_ent\n\n";
|
||||
|
||||
$ans = &get_yn("Is this the entry you wish to remove? ");
|
||||
$ans = &get_yn("Is this the entry you wish to remove? ");
|
||||
|
||||
if ($ans eq 'N') {
|
||||
print "User ${login_name} not removed.\n";
|
||||
&unlockpw;
|
||||
exit 0;
|
||||
if ($ans eq 'N') {
|
||||
print "${whoami}: Informational: User ${login_name} not removed.\n";
|
||||
&unlockpw;
|
||||
exit 0;
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
@ -149,21 +162,24 @@ if (-l $home_dir) {
|
||||
# If home_dir is a symlink and points to something that isn't a directory,
|
||||
# or if home_dir is not a symlink and is not a directory, don't remove
|
||||
# home_dir -- seems like a good thing to do, but probably isn't necessary...
|
||||
|
||||
if (((-l $home_dir) && ((-e $real_home_dir) && !(-d $real_home_dir))) ||
|
||||
(!(-l $home_dir) && !(-d $home_dir))) {
|
||||
print STDERR "${whoami}: Home ${home_dir} is not a directory, so it won't be removed\n";
|
||||
print STDERR "${whoami}: Informational: Home ${home_dir} is not a directory, so it won't be removed\n";
|
||||
$remove_directory = 0;
|
||||
}
|
||||
|
||||
if (length($real_home_dir) && -d $real_home_dir) {
|
||||
$dir_owner = (stat($real_home_dir))[4]; # UID
|
||||
if ($dir_owner != $uid) {
|
||||
print STDERR "${whoami}: Home dir ${real_home_dir} is not owned by ${login_name} (uid ${dir_owner})\n";
|
||||
print STDERR "${whoami}: Informational: Home dir ${real_home_dir} is" .
|
||||
" not owned by ${login_name} (uid ${dir_owner})\n," .
|
||||
"\tso it won't be removed\n";
|
||||
$remove_directory = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ($remove_directory) {
|
||||
if ($remove_directory && ! $affirm) {
|
||||
$ans = &get_yn("Remove user's home directory ($home_dir)? ");
|
||||
if ($ans eq 'N') {
|
||||
$remove_directory = 0;
|
||||
@ -188,6 +204,11 @@ if (-e "$crontab_dir/$login_name") {
|
||||
|
||||
&remove_at_jobs($login_name, $uid);
|
||||
|
||||
#
|
||||
# Kill all the user's processes
|
||||
|
||||
&kill_users_processes($login_name, $uid);
|
||||
|
||||
#
|
||||
# Copy master password file to new file less removed user's entry
|
||||
|
||||
@ -208,15 +229,39 @@ if ($remove_directory) {
|
||||
}
|
||||
|
||||
#
|
||||
# Remove the user's incoming mail file
|
||||
# Remove files related to the user from the mail directory
|
||||
|
||||
if (-e "$mail_dir/$login_name" || -l "$mail_dir/$login_name") {
|
||||
print STDERR "Removing user's incoming mail file ($mail_dir/$login_name):";
|
||||
unlink "$mail_dir/$login_name" ||
|
||||
print STDERR "\n${whoami}: warning: unlink on $mail_dir/$login_name failed ($!) - continuing\n";
|
||||
#&remove_files_from_dir($mail_dir, $login_name, $uid);
|
||||
$file = "$mail_dir/$login_name";
|
||||
if (-e $file || -l $file) {
|
||||
print STDERR "Removing user's incoming mail file ${file}:";
|
||||
unlink $file ||
|
||||
print STDERR "\n${whoami}: Warning: unlink on $file failed ($!) - continuing\n";
|
||||
print STDERR " done.\n";
|
||||
}
|
||||
|
||||
#
|
||||
# Remove some pop daemon's leftover file
|
||||
|
||||
$file = "$maildir/.${login_name}.pop";
|
||||
if (-e $file || -l $file) {
|
||||
print STDERR "Removing pop daemon's temporary mail file ${file}:";
|
||||
unlink $file ||
|
||||
print STDERR "\n${whoami}: Warning: unlink on $file failed ($!) - continuing\n";
|
||||
print STDERR " done.\n";
|
||||
}
|
||||
|
||||
#
|
||||
# Remove files belonging to the user from the directories /tmp, /var/tmp,
|
||||
# and /var/tmp/vi.recover. Note that this doesn't take care of the
|
||||
# problem where a user may have directories or symbolic links in those
|
||||
# directories -- only regular files are removed.
|
||||
|
||||
&remove_files_from_dir('/tmp', $login_name, $uid);
|
||||
&remove_files_from_dir('/var/tmp', $login_name, $uid);
|
||||
&remove_files_from_dir('/var/tmp/vi.recover', $login_name, $uid)
|
||||
if (-e '/var/tmp/vi.recover');
|
||||
|
||||
#
|
||||
# All done!
|
||||
|
||||
@ -296,7 +341,7 @@ sub update_passwd_file {
|
||||
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";
|
||||
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";
|
||||
$skipped = 0;
|
||||
while ($i = <MASTER_PW>) {
|
||||
if ($i =~ /\n$/) {
|
||||
@ -315,7 +360,7 @@ sub update_passwd_file {
|
||||
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";
|
||||
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";
|
||||
&unlockpw;
|
||||
exit 1;
|
||||
}
|
||||
@ -333,9 +378,10 @@ sub update_passwd_file {
|
||||
sub update_group_file {
|
||||
local($login_name) = @_;
|
||||
|
||||
local($i, $j, $grmember_list, $new_grent);
|
||||
local($i, $j, $grmember_list, $new_grent, $changes);
|
||||
local($grname, $grpass, $grgid, $grmember_list, @grmembers);
|
||||
|
||||
$changes = 0;
|
||||
print STDERR "Updating group file:";
|
||||
open(GROUP, $group_file) ||
|
||||
die "\n${whoami}: Error: couldn't open ${group_file}: $!\n";
|
||||
@ -348,9 +394,9 @@ sub update_group_file {
|
||||
open(NEW_GROUP, ">$new_group_file") ||
|
||||
die "\n${whoami}: Error: couldn't open ${new_group_file}: $!\n";
|
||||
chmod($group_perms, $new_group_file) ||
|
||||
printf STDERR "\n${whoami}: warning: could not set permissions of new group file to %o ($!)\n\tContinuing, but please check permissions of $group_file!\n", $group_perms;
|
||||
printf STDERR "\n${whoami}: Warning: could not set permissions of new group file to %o ($!)\n\tContinuing, but please check permissions of $group_file!\n", $group_perms;
|
||||
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";
|
||||
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/)) {
|
||||
# Line doesn't contain any references to the user, so just add it
|
||||
@ -368,14 +414,16 @@ sub update_group_file {
|
||||
local(@new_grmembers);
|
||||
foreach $j (@grmembers) {
|
||||
if ($j ne $login_name) {
|
||||
push(new_grmembers, $j);
|
||||
} elsif ($debug) {
|
||||
print STDERR "Removing $login_name from group $grname\n";
|
||||
push(@new_grmembers, $j);
|
||||
} else {
|
||||
print STDERR " $grname";
|
||||
$changes = 1;
|
||||
}
|
||||
}
|
||||
if ($grname eq $login_name && $#new_grmembers == -1) {
|
||||
# Remove a user's personal group if empty
|
||||
print STDERR "Removing group $grname -- personal group is empty\n";
|
||||
print STDERR " (removing group $grname -- personal group is empty)";
|
||||
$changes = 1;
|
||||
} else {
|
||||
$grmember_list = join(',', @new_grmembers);
|
||||
$new_grent = join(':', $grname, $grpass, $grgid, $grmember_list);
|
||||
@ -385,8 +433,9 @@ sub update_group_file {
|
||||
}
|
||||
close(NEW_GROUP);
|
||||
rename($new_group_file, $group_file) || # Replace old group file with new
|
||||
die "\n${whoami}: error: couldn't rename $new_group_file to $group_file ($!)\n";
|
||||
die "\n${whoami}: Error: couldn't rename $new_group_file to $group_file ($!)\n";
|
||||
close(GROUP); # File handle is worthless now
|
||||
print STDERR " (no changes)" if (! $changes);
|
||||
print STDERR " done.\n";
|
||||
}
|
||||
|
||||
@ -416,6 +465,36 @@ sub remove_dir {
|
||||
system('/bin/rm', '-rf', $dir);
|
||||
}
|
||||
|
||||
sub remove_files_from_dir {
|
||||
local($dir, $login_name, $uid) = @_;
|
||||
local($path, $i, $owner);
|
||||
|
||||
print STDERR "Removing files belonging to ${login_name} from ${dir}:";
|
||||
|
||||
if (!opendir(DELDIR, $dir)) {
|
||||
print STDERR "\n${whoami}: Warning: couldn't open directory ${dir} ($!)\n";
|
||||
return;
|
||||
}
|
||||
while ($i = readdir(DELDIR)) {
|
||||
next if $i eq '.';
|
||||
next if $i eq '..';
|
||||
|
||||
$owner = (stat("$dir/$i"))[4]; # UID
|
||||
if ($uid == $owner) {
|
||||
if (-f "$dir/$i") {
|
||||
print STDERR " $i";
|
||||
unlink "$dir/$i" ||
|
||||
print STDERR "\n${whoami}: Warning: unlink on ${dir}/${i} failed ($!) - continuing\n";
|
||||
} else {
|
||||
print STDERR " ($i not a regular file - skipped)";
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(DELDIR);
|
||||
|
||||
printf STDERR " done.\n";
|
||||
}
|
||||
|
||||
sub remove_at_jobs {
|
||||
local($login_name, $uid) = @_;
|
||||
local($i, $owner, $found);
|
||||
@ -462,3 +541,36 @@ sub resolvelink {
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
|
||||
sub kill_users_processes {
|
||||
local($login_name, $uid) = @_;
|
||||
local($pid, $result);
|
||||
|
||||
#
|
||||
# Do something a little complex: fork a child that changes its
|
||||
# real and effective UID to that of the removed user, then issues
|
||||
# a "kill(9, -1)" to kill all processes of the same uid as the sender
|
||||
# (see kill(2) for details).
|
||||
# The parent waits for the exit of the child and then returns.
|
||||
|
||||
if ($pid = fork) {
|
||||
# Parent process
|
||||
waitpid($pid, 0);
|
||||
} elsif (defined $pid) {
|
||||
# Child process
|
||||
$< = $uid;
|
||||
$> = $uid;
|
||||
if ($< != $uid || $> != $uid) {
|
||||
print STDERR "${whoami}: Error (kill_users_processes):\n" .
|
||||
"\tCouldn't reset uid/euid to ${uid}: current uid/euid's are $< and $>\n";
|
||||
exit 1;
|
||||
}
|
||||
$result = kill(9, -1);
|
||||
print STDERR "Killed process(es) belonging to $login_name.\n"
|
||||
if $result;
|
||||
exit 0;
|
||||
} else {
|
||||
# Couldn't fork!
|
||||
print STDERR "${whoami}: Error: couldn't fork to kill ${login_name}'s processes - continuing\n";
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user