73ed913a5f
Approved by: wpaul MFC after: 1 day
361 lines
8.9 KiB
Bash
361 lines
8.9 KiB
Bash
#!/bin/sh
|
|
#
|
|
# Copyright (c) 2002, 2003 Michael Telahun Makonnen. All rights reserved.
|
|
#
|
|
# Redistribution and use in source and binary forms, with or without
|
|
# modification, are permitted provided that the following conditions
|
|
# are met:
|
|
# 1. Redistributions of source code must retain the above copyright
|
|
# notice, this list of conditions and the following disclaimer.
|
|
# 2. Redistributions in binary form must reproduce the above copyright
|
|
# notice, this list of conditions and the following disclaimer in the
|
|
# documentation and/or other materials provided with the distribution.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
#
|
|
# Email: Mike Makonnen <mtm@FreeBSD.Org>
|
|
#
|
|
# $FreeBSD$
|
|
#
|
|
|
|
ATJOBDIR="/var/at/jobs"
|
|
CRONJOBDIR="/var/cron/tabs"
|
|
MAILSPOOL="/var/mail"
|
|
SIGKILL="-KILL"
|
|
TEMPDIRS="/tmp /var/tmp"
|
|
THISCMD=`/usr/bin/basename $0`
|
|
|
|
# err msg
|
|
# Display $msg on stderr.
|
|
#
|
|
err() {
|
|
echo 1>&2 ${THISCMD}: $*
|
|
}
|
|
|
|
# verbose
|
|
# Returns 0 if verbose mode is set, 1 if it is not.
|
|
#
|
|
verbose() {
|
|
[ -n "$vflag" ] && return 0 || return 1
|
|
}
|
|
|
|
# rm_files login
|
|
# Removes files or empty directories belonging to $login from various
|
|
# temporary directories.
|
|
#
|
|
rm_files() {
|
|
# The argument is required
|
|
[ -n $1 ] && login=$1 || return
|
|
|
|
totalcount=0
|
|
for _dir in ${TEMPDIRS} ; do
|
|
filecount=0
|
|
if [ ! -d $_dir ]; then
|
|
err "$_dir is not a valid directory."
|
|
continue
|
|
fi
|
|
verbose && echo -n "Removing files owned by ($login) in $_dir:"
|
|
filecount=`find 2>/dev/null "$_dir" -user "$login" -delete -print |
|
|
wc -l | sed 's/ *//'`
|
|
verbose && echo " $filecount removed."
|
|
totalcount=$(($totalcount + $filecount))
|
|
done
|
|
! verbose && [ $totalcount -ne 0 ] && echo -n " files($totalcount)"
|
|
}
|
|
|
|
# rm_mail login
|
|
# Removes unix mail and pop daemon files belonging to the user
|
|
# specified in the $login argument.
|
|
#
|
|
rm_mail() {
|
|
# The argument is required
|
|
[ -n $1 ] && login=$1 || return
|
|
|
|
verbose && echo -n "Removing mail spool(s) for ($login):"
|
|
if [ -f ${MAILSPOOL}/$login ]; then
|
|
verbose && echo -n " ${MAILSPOOL}/$login" ||
|
|
echo -n " mailspool"
|
|
rm ${MAILSPOOL}/$login
|
|
fi
|
|
if [ -f ${MAILSPOOL}/${login}.pop ]; then
|
|
verbose && echo -n " ${MAILSPOOL}/${login}.pop" ||
|
|
echo -n " pop3"
|
|
rm ${MAILSPOOL}/${login}.pop
|
|
fi
|
|
verbose && echo '.'
|
|
}
|
|
|
|
# kill_procs login
|
|
# Send a SIGKILL to all processes owned by $login.
|
|
#
|
|
kill_procs() {
|
|
# The argument is required
|
|
[ -n $1 ] && login=$1 || return
|
|
|
|
verbose && echo -n "Terminating all processes owned by ($login):"
|
|
killcount=0
|
|
proclist=`ps 2>/dev/null -U $login | grep -v '^\ *PID' | awk '{print $1}'`
|
|
for _pid in $proclist ; do
|
|
kill 2>/dev/null ${SIGKILL} $_pid
|
|
killcount=$(($killcount + 1))
|
|
done
|
|
verbose && echo " ${SIGKILL} signal sent to $killcount processes."
|
|
! verbose && [ $killcount -ne 0 ] && echo -n " processes(${killcount})"
|
|
}
|
|
|
|
# rm_at_jobs login
|
|
# Remove at (1) jobs belonging to $login.
|
|
#
|
|
rm_at_jobs() {
|
|
# The argument is required
|
|
[ -n $1 ] && login=$1 || return
|
|
|
|
atjoblist=`find 2>/dev/null ${ATJOBDIR} -maxdepth 1 -user $login -print`
|
|
jobcount=0
|
|
verbose && echo -n "Removing at(1) jobs owned by ($login):"
|
|
for _atjob in $atjoblist ; do
|
|
rm -f $_atjob
|
|
jobcount=$(($jobcount + 1))
|
|
done
|
|
verbose && echo " $jobcount removed."
|
|
! verbose && [ $jobcount -ne 0 ] && echo -n " at($jobcount)"
|
|
}
|
|
|
|
# rm_crontab login
|
|
# Removes crontab file belonging to user $login.
|
|
#
|
|
rm_crontab() {
|
|
# The argument is required
|
|
[ -n $1 ] && login=$1 || return
|
|
|
|
verbose && echo -n "Removing crontab for ($login):"
|
|
if [ -f ${CRONJOBDIR}/$login ]; then
|
|
verbose && echo -n " ${CRONJOBDIR}/$login" || echo -n " crontab"
|
|
rm -f ${CRONJOBDIR}/$login
|
|
fi
|
|
verbose && echo '.'
|
|
}
|
|
|
|
# rm_ipc login
|
|
# Remove all IPC mechanisms which are owned by $login.
|
|
#
|
|
rm_ipc() {
|
|
verbose && echo -n "Removing IPC mechanisms"
|
|
for i in s m q; do
|
|
ipcs -$i |
|
|
awk -v i=$i -v login=$1 '$1 == i && $5 == login { print $2 }' |
|
|
xargs -n 1 ipcrm -$i
|
|
done
|
|
verbose && echo '.'
|
|
}
|
|
|
|
# rm_user login
|
|
# Remove user $login from the system. This subroutine makes use
|
|
# of the pw(8) command to remove a user from the system. The pw(8)
|
|
# command will remove the specified user from the user database
|
|
# and group file and remove any crontabs. His home
|
|
# directory will be removed if it is owned by him and contains no
|
|
# files or subdirectories owned by other users. Mail spool files will
|
|
# also be removed.
|
|
#
|
|
rm_user() {
|
|
# The argument is required
|
|
[ -n $1 ] && login=$1 || return
|
|
|
|
verbose && echo -n "Removing user ($login)"
|
|
[ -n "$pw_rswitch" ] && {
|
|
verbose && echo -n " (including home directory)"
|
|
! verbose && echo -n " home"
|
|
}
|
|
! verbose && echo -n " passwd"
|
|
verbose && echo -n " from the system:"
|
|
pw userdel -n $login $pw_rswitch
|
|
verbose && echo ' Done.'
|
|
}
|
|
|
|
# prompt_yesno msg
|
|
# Prompts the user with a $msg. The answer is expected to be
|
|
# yes, no, or some variation thereof. This subroutine returns 0
|
|
# if the answer was yes, 1 if it was not.
|
|
#
|
|
prompt_yesno() {
|
|
# The argument is required
|
|
[ -n "$1" ] && msg="$1" || return
|
|
|
|
while : ; do
|
|
echo -n "$msg"
|
|
read _ans
|
|
case $_ans in
|
|
[Nn][Oo]|[Nn])
|
|
return 1
|
|
;;
|
|
[Yy][Ee][Ss]|[Yy][Ee]|[Yy])
|
|
return 0
|
|
;;
|
|
*)
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
# show_usage
|
|
# (no arguments)
|
|
# Display usage message.
|
|
#
|
|
show_usage() {
|
|
echo "usage: ${THISCMD} [-yv] [-f file] [user ...]"
|
|
echo " if the -y switch is used, either the -f switch or"
|
|
echo " one or more user names must be given"
|
|
}
|
|
|
|
#### END SUBROUTINE DEFENITION ####
|
|
|
|
ffile=
|
|
fflag=
|
|
procowner=
|
|
pw_rswitch=
|
|
userlist=
|
|
yflag=
|
|
vflag=
|
|
|
|
procowner=`/usr/bin/id -u`
|
|
if [ "$procowner" != "0" ]; then
|
|
err 'you must be root (0) to use this utility.'
|
|
exit 1
|
|
fi
|
|
|
|
args=`getopt 2>/dev/null yvf: $*`
|
|
if [ "$?" != "0" ]; then
|
|
show_usage
|
|
exit 1
|
|
fi
|
|
set -- $args
|
|
for _switch ; do
|
|
case $_switch in
|
|
-y)
|
|
yflag=1
|
|
shift
|
|
;;
|
|
-v)
|
|
vflag=1
|
|
shift
|
|
;;
|
|
-f)
|
|
fflag=1
|
|
ffile="$2"
|
|
shift; shift
|
|
;;
|
|
--)
|
|
shift
|
|
break
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Get user names from a file if the -f switch was used. Otherwise,
|
|
# get them from the commandline arguments. If we're getting it
|
|
# from a file, the file must be owned by and writable only by root.
|
|
#
|
|
if [ $fflag ]; then
|
|
_insecure=`find $ffile ! -user 0 -or -perm +0022`
|
|
if [ -n "$_insecure" ]; then
|
|
err "file ($ffile) must be owned by and writeable only by root."
|
|
exit 1
|
|
fi
|
|
if [ -r "$ffile" ]; then
|
|
userlist=`cat $ffile | while read _user _junk ; do
|
|
case $_user in
|
|
\#*|'')
|
|
;;
|
|
*)
|
|
echo -n "$userlist $_user"
|
|
;;
|
|
esac
|
|
done`
|
|
fi
|
|
else
|
|
while [ $1 ] ; do
|
|
userlist="$userlist $1"
|
|
shift
|
|
done
|
|
fi
|
|
|
|
# If the -y or -f switch has been used and the list of users to remove
|
|
# is empty it is a fatal error. Otherwise, prompt the user for a list
|
|
# of one or more user names.
|
|
#
|
|
if [ ! "$userlist" ]; then
|
|
if [ $fflag ]; then
|
|
err "($ffile) does not exist or does not contain any user names."
|
|
exit 1
|
|
elif [ $yflag ]; then
|
|
show_usage
|
|
exit 1
|
|
else
|
|
echo -n "Please enter one or more usernames: "
|
|
read userlist
|
|
fi
|
|
fi
|
|
|
|
_user=
|
|
_uid=
|
|
for _user in $userlist ; do
|
|
# Make sure the name exists in the passwd database and that it
|
|
# does not have a uid of 0
|
|
#
|
|
userrec=`pw 2>/dev/null usershow -n $_user`
|
|
if [ "$?" != "0" ]; then
|
|
err "user ($_user) does not exist in the password database."
|
|
continue
|
|
fi
|
|
_uid=`echo $userrec | awk -F: '{print $3}'`
|
|
if [ "$_uid" = "0" ]; then
|
|
err "user ($_user) has uid 0. You may not remove this user."
|
|
continue
|
|
fi
|
|
|
|
# If the -y switch was not used ask for confirmation to remove the
|
|
# user and home directory.
|
|
#
|
|
if [ -z "$yflag" ]; then
|
|
echo "Matching password entry:"
|
|
echo
|
|
echo $userrec
|
|
echo
|
|
if ! prompt_yesno "Is this the entry you wish to remove? " ; then
|
|
continue
|
|
fi
|
|
_homedir=`echo $userrec | awk -F: '{print $9}'`
|
|
if prompt_yesno "Remove user's home directory ($_homedir)? "; then
|
|
pw_rswitch="-r"
|
|
fi
|
|
else
|
|
pw_rswitch="-r"
|
|
fi
|
|
|
|
# Disable any further attempts to log into this account
|
|
pw 2>/dev/null lock $_user
|
|
|
|
# Remove crontab, mail spool, etc. Then obliterate the user from
|
|
# the passwd and group database.
|
|
#
|
|
! verbose && echo -n "Removing user ($_user):"
|
|
rm_crontab $_user
|
|
rm_at_jobs $_user
|
|
rm_ipc $_user
|
|
kill_procs $_user
|
|
rm_files $_user
|
|
rm_mail $_user
|
|
rm_user $_user
|
|
! verbose && echo "."
|
|
done
|