rmuser.sh revision 146556
11553Srgrimes#!/bin/sh
21553Srgrimes#
31553Srgrimes# Copyright (c) 2002, 2003 Michael Telahun Makonnen. All rights reserved.
41553Srgrimes#
51553Srgrimes# Redistribution and use in source and binary forms, with or without
61553Srgrimes# modification, are permitted provided that the following conditions
71553Srgrimes# are met:
81553Srgrimes# 1. Redistributions of source code must retain the above copyright
91553Srgrimes#    notice, this list of conditions and the following disclaimer.
101553Srgrimes# 2. Redistributions in binary form must reproduce the above copyright
111553Srgrimes#    notice, this list of conditions and the following disclaimer in the
121553Srgrimes#    documentation and/or other materials provided with the distribution.
131553Srgrimes#
141553Srgrimes# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
151553Srgrimes# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
161553Srgrimes# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
171553Srgrimes# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
181553Srgrimes# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
191553Srgrimes# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
201553Srgrimes# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
211553Srgrimes# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
221553Srgrimes# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
231553Srgrimes# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
241553Srgrimes#
251553Srgrimes#	Email: Mike Makonnen <mtm@FreeBSD.Org>
261553Srgrimes#
271553Srgrimes# $FreeBSD: head/usr.sbin/adduser/rmuser.sh 146556 2005-05-24 04:50:07Z adamw $
281553Srgrimes#
291553Srgrimes
301553SrgrimesATJOBDIR="/var/at/jobs"
311553SrgrimesCRONJOBDIR="/var/cron/tabs"
321553SrgrimesMAILSPOOL="/var/mail"
331553SrgrimesSIGKILL="-KILL"
341553SrgrimesTEMPDIRS="/tmp /var/tmp"
351553SrgrimesTHISCMD=`/usr/bin/basename $0`
361553Srgrimes
371553Srgrimes# err msg
381553Srgrimes#	Display $msg on stderr.
391553Srgrimes#
401553Srgrimeserr() {
411553Srgrimes	echo 1>&2 ${THISCMD}: $*
421553Srgrimes}
431553Srgrimes
441553Srgrimes# verbose
451553Srgrimes#	Returns 0 if verbose mode is set, 1 if it is not.
461553Srgrimes#
471553Srgrimesverbose() {
481553Srgrimes	[ -n "$vflag" ] && return 0 || return 1
491553Srgrimes}
501553Srgrimes
511553Srgrimes# rm_files login
521553Srgrimes#	Removes files or empty directories belonging to $login from various
531553Srgrimes#	temporary directories.
541553Srgrimes#
551553Srgrimesrm_files() {
561553Srgrimes	# The argument is required
571553Srgrimes	[ -n $1 ] && login=$1 || return
581553Srgrimes
591553Srgrimes	totalcount=0
601553Srgrimes	for _dir in ${TEMPDIRS} ; do
611553Srgrimes		filecount=0
621553Srgrimes		if [ ! -d $_dir ]; then
631553Srgrimes			err "$_dir is not a valid directory."
641553Srgrimes			continue
651553Srgrimes		fi
661553Srgrimes		verbose && echo -n "Removing files owned by ($login) in $_dir:"
671553Srgrimes		filecount=`find 2>/dev/null "$_dir" -user "$login" -delete -print |
681553Srgrimes		    wc -l | sed 's/ *//'`
691553Srgrimes		verbose && echo " $filecount removed."
7010087Sjkh		totalcount=$(($totalcount + $filecount))
7110087Sjkh	done
7210087Sjkh	! verbose && [ $totalcount -ne 0 ] && echo -n " files($totalcount)"
7310087Sjkh}
7410087Sjkh
7510087Sjkh# rm_mail login
7610087Sjkh#	Removes unix mail and pop daemon files belonging to the user
7710087Sjkh#	specified in the $login argument.
7810087Sjkh#
7910087Sjkhrm_mail() {
8010087Sjkh	# The argument is required
8110087Sjkh	[ -n $1 ] && login=$1 || return
8210087Sjkh
8310087Sjkh	verbose && echo -n "Removing mail spool(s) for ($login):"
8410087Sjkh	if [ -f ${MAILSPOOL}/$login ]; then
8510087Sjkh		verbose && echo -n " ${MAILSPOOL}/$login" ||
8610087Sjkh		    echo -n " mailspool"
8710087Sjkh		rm ${MAILSPOOL}/$login
8810087Sjkh	fi
8910087Sjkh	if [ -f ${MAILSPOOL}/${login}.pop ]; then
9010087Sjkh		verbose && echo -n " ${MAILSPOOL}/${login}.pop" ||
9110087Sjkh		    echo -n " pop3"
9210087Sjkh		rm ${MAILSPOOL}/${login}.pop
9310087Sjkh	fi
9410087Sjkh	verbose && echo '.'
9510087Sjkh}
9610087Sjkh
9710087Sjkh# kill_procs login
9810087Sjkh#	Send a SIGKILL to all processes owned by $login.
9910087Sjkh#
10010087Sjkhkill_procs() {
10110087Sjkh	# The argument is required
10210087Sjkh	[ -n $1 ] && login=$1 || return
10310087Sjkh
10410087Sjkh	verbose && echo -n "Terminating all processes owned by ($login):"
10510087Sjkh	killcount=0
10610087Sjkh	proclist=`ps 2>/dev/null -U $login | grep -v '^\ *PID' | awk '{print $1}'`
10710087Sjkh	for _pid in $proclist ; do
10810087Sjkh		kill 2>/dev/null ${SIGKILL} $_pid
10910087Sjkh		killcount=$(($killcount + 1))
11010087Sjkh	done
11110087Sjkh	verbose && echo " ${SIGKILL} signal sent to $killcount processes."
11210087Sjkh	! verbose && [ $killcount -ne 0 ] && echo -n " processes(${killcount})"
1131553Srgrimes}
1141553Srgrimes
1151553Srgrimes# rm_at_jobs login
1161553Srgrimes#	Remove at (1) jobs belonging to $login.
1171553Srgrimes#
1181553Srgrimesrm_at_jobs() {
1191553Srgrimes	# The argument is required
1201553Srgrimes	[ -n $1 ] && login=$1 || return
1211553Srgrimes
1221553Srgrimes	atjoblist=`find 2>/dev/null ${ATJOBDIR} -maxdepth 1 -user $login -print`
1231553Srgrimes	jobcount=0
1241553Srgrimes	verbose && echo -n "Removing at(1) jobs owned by ($login):"
1251553Srgrimes	for _atjob in $atjoblist ; do
1261553Srgrimes		rm -f $_atjob
1271553Srgrimes		jobcount=$(($jobcount + 1))
1281553Srgrimes	done
1291553Srgrimes	verbose && echo " $jobcount removed."
1301553Srgrimes	! verbose && [ $jobcount -ne 0 ] && echo -n " at($jobcount)"
1311553Srgrimes}
1321553Srgrimes
1331553Srgrimes# rm_crontab login
1341553Srgrimes#	Removes crontab file belonging to user $login.
1351553Srgrimes#
1361553Srgrimesrm_crontab() {
1371553Srgrimes	# The argument is required
1381553Srgrimes	[ -n $1 ] && login=$1 || return
1391553Srgrimes
1401553Srgrimes	verbose && echo -n "Removing crontab for ($login):"
1411553Srgrimes	if [ -f ${CRONJOBDIR}/$login ]; then
1421553Srgrimes		verbose && echo -n " ${CRONJOBDIR}/$login" || echo -n " crontab"
1431553Srgrimes		rm -f ${CRONJOBDIR}/$login
1441553Srgrimes	fi
1451553Srgrimes	verbose && echo '.'
1461553Srgrimes}
1471553Srgrimes
1481553Srgrimes# rm_ipc login
1491553Srgrimes#	Remove all IPC mechanisms which are owned by $login.
1501553Srgrimes#
1511553Srgrimesrm_ipc() {
1521553Srgrimes	verbose && echo -n "Removing IPC mechanisms"
1531553Srgrimes	for i in s m q; do
1541553Srgrimes		ipcs -$i |
15510087Sjkh		awk -v i=$i -v login=$1 '$1 == i && $5 == login { print $2 }' |
1561553Srgrimes		xargs -n 1 ipcrm -$i
1571553Srgrimes	done
1581553Srgrimes	verbose && echo '.'
1591553Srgrimes}
1601553Srgrimes
1611553Srgrimes# rm_user login
1621553Srgrimes#	Remove user $login from the system. This subroutine makes use
1631553Srgrimes#	of the pw(8) command to remove a user from the system. The pw(8)
1641553Srgrimes#	command will remove the specified user from the user database
1651553Srgrimes#	and group file and remove any crontabs. His home
1661553Srgrimes#	directory will be removed if it is owned by him and contains no 
1671553Srgrimes#	files or subdirectories owned by other users. Mail spool files will
16810087Sjkh#	also be removed.
16910087Sjkh#
17010087Sjkhrm_user() {
17110087Sjkh	# The argument is required
17210087Sjkh	[ -n $1 ] && login=$1 || return
17310087Sjkh
17410087Sjkh	verbose && echo -n "Removing user ($login)"
17510087Sjkh	[ -n "$pw_rswitch" ] && {
17610087Sjkh		verbose && echo -n " (including home directory)"
17710087Sjkh		! verbose && echo -n " home"
17810087Sjkh	}
17910087Sjkh	! verbose && echo -n " passwd"
18010087Sjkh	verbose && echo -n " from the system:"
18110087Sjkh	pw userdel -n $login $pw_rswitch
18210087Sjkh	verbose && echo ' Done.'
18310087Sjkh}
18410087Sjkh
18510087Sjkh# prompt_yesno msg
18610087Sjkh#	Prompts the user with a $msg. The answer is expected to be
18710087Sjkh#	yes, no, or some variation thereof. This subroutine returns 0
18810087Sjkh#	if the answer was yes, 1 if it was not.
1891553Srgrimes#
1901553Srgrimesprompt_yesno() {
1911553Srgrimes	# The argument is required
1921553Srgrimes	[ -n "$1" ] && msg="$1" || return
1931553Srgrimes
19410087Sjkh        while : ; do
19510087Sjkh                echo -n "$msg"
19610087Sjkh                read _ans
19710087Sjkh                case $_ans in
19810087Sjkh                [Nn][Oo]|[Nn])
19910087Sjkh			return 1
20010087Sjkh                        ;;
2011553Srgrimes                [Yy][Ee][Ss]|[Yy][Ee]|[Yy])
20210087Sjkh                        return 0
2031553Srgrimes                        ;;
2041553Srgrimes                *)
2051553Srgrimes                        ;;
2061553Srgrimes                esac
2071553Srgrimes	done
2081553Srgrimes}
2091553Srgrimes
2101553Srgrimes# show_usage
2111553Srgrimes#	(no arguments)
2121553Srgrimes#	Display usage message.
2131553Srgrimes#
2141553Srgrimesshow_usage() {
2151553Srgrimes	echo "usage: ${THISCMD} [-yv] [-f file] [user ...]"
2161553Srgrimes	echo "       if the -y switch is used, either the -f switch or"
2171553Srgrimes	echo "       one or more user names must be given"
2181553Srgrimes}
2191553Srgrimes
2201553Srgrimes#### END SUBROUTINE DEFENITION ####
2211553Srgrimes
2221553Srgrimesffile=
2231553Srgrimesfflag=
2241553Srgrimesprocowner=
2251553Srgrimespw_rswitch=
2261553Srgrimesuserlist=
2271553Srgrimesyflag=
2281553Srgrimesvflag=
2291553Srgrimes
23010087Sjkhprocowner=`/usr/bin/id -u`
2311553Srgrimesif [ "$procowner" != "0" ]; then
2321553Srgrimes	err 'you must be root (0) to use this utility.'
2331553Srgrimes	exit 1
2341553Srgrimesfi
2351553Srgrimes
2361553Srgrimesargs=`getopt 2>/dev/null yvf: $*`
2371553Srgrimesif [ "$?" != "0" ]; then
2381553Srgrimes	show_usage
2391553Srgrimes	exit 1
2401553Srgrimesfi
2411553Srgrimesset -- $args
2421553Srgrimesfor _switch ; do
2431553Srgrimes	case $_switch in
2441553Srgrimes	-y)
2451553Srgrimes		yflag=1
2461553Srgrimes		shift
2471553Srgrimes		;;
2481553Srgrimes	-v)
2491553Srgrimes		vflag=1
2501553Srgrimes		shift
2511553Srgrimes		;;
2521553Srgrimes	-f)
2531553Srgrimes		fflag=1
2541553Srgrimes		ffile="$2"
2551553Srgrimes		shift; shift
2561553Srgrimes		;;
2571553Srgrimes	--)
2581553Srgrimes		shift
2591553Srgrimes		break
2601553Srgrimes		;;
2611553Srgrimes	esac
2621553Srgrimesdone
2631553Srgrimes
2641553Srgrimes# Get user names from a file if the -f switch was used. Otherwise,
2651553Srgrimes# get them from the commandline arguments. If we're getting it
2661553Srgrimes# from a file, the file must be owned by and writable only by root.
2671553Srgrimes#
2681553Srgrimesif [ $fflag ]; then
2691553Srgrimes	_insecure=`find $ffile ! -user 0 -or -perm +0022`
2701553Srgrimes	if [ -n "$_insecure" ]; then
2711553Srgrimes		err "file ($ffile) must be owned by and writeable only by root."
2721553Srgrimes		exit 1
2731553Srgrimes	fi
2741553Srgrimes	if [ -r "$ffile" ]; then
2751553Srgrimes		userlist=`cat $ffile | while read _user _junk ; do
2761553Srgrimes			case $_user in
2771553Srgrimes			\#*|'')
2781553Srgrimes				;;
2791553Srgrimes			*)
2801553Srgrimes				echo -n "$userlist $_user"
2811553Srgrimes				;;
2821553Srgrimes			esac
2831553Srgrimes		done`
2841553Srgrimes	fi
2851553Srgrimeselse
2861553Srgrimes	while [ $1 ] ; do
2871553Srgrimes		userlist="$userlist $1"
2881553Srgrimes		shift
2891553Srgrimes	done
2901553Srgrimesfi
2911553Srgrimes
2921553Srgrimes# If the -y or -f switch has been used and the list of users to remove
2931553Srgrimes# is empty it is a fatal error. Otherwise, prompt the user for a list
2941553Srgrimes# of one or more user names.
2951553Srgrimes#
2961553Srgrimesif [ ! "$userlist" ]; then
2971553Srgrimes	if [ $fflag ]; then
2981553Srgrimes		err "($ffile) does not exist or does not contain any user names."
2991553Srgrimes		exit 1
3001553Srgrimes	elif [ $yflag ]; then
3011553Srgrimes		show_usage
3021553Srgrimes		exit 1
3031553Srgrimes	else
3041553Srgrimes		echo -n "Please enter one or more usernames: "
3051553Srgrimes		read userlist
3061553Srgrimes	fi
3071553Srgrimesfi
3081553Srgrimes
3091553Srgrimes_user=
3101553Srgrimes_uid=
3111553Srgrimesfor _user in $userlist ; do
3121553Srgrimes	# Make sure the name exists in the passwd database and that it
3131553Srgrimes	# does not have a uid of 0
3141553Srgrimes	#
3151553Srgrimes	userrec=`pw 2>/dev/null usershow -n $_user`
3161553Srgrimes	if [ "$?" != "0" ]; then
3171553Srgrimes		err "user ($_user) does not exist in the password database."
3181553Srgrimes		continue
3191553Srgrimes	fi
3201553Srgrimes	_uid=`echo $userrec | awk -F: '{print $3}'`
3211553Srgrimes	if [ "$_uid" = "0" ]; then
3221553Srgrimes		err "user ($_user) has uid 0. You may not remove this user."
3231553Srgrimes		continue
3241553Srgrimes	fi
3251553Srgrimes
3261553Srgrimes	# If the -y switch was not used ask for confirmation to remove the
3271553Srgrimes	# user and home directory.
3281553Srgrimes	#
3291553Srgrimes	if [ -z "$yflag" ]; then
3301553Srgrimes		echo "Matching password entry:"
3311553Srgrimes		echo
3321553Srgrimes		echo $userrec
3331553Srgrimes		echo
3341553Srgrimes		if ! prompt_yesno "Is this the entry you wish to remove? " ; then
3351553Srgrimes			continue
3361553Srgrimes		fi
3371553Srgrimes		_homedir=`echo $userrec | awk -F: '{print $9}'`
3381553Srgrimes		if prompt_yesno "Remove user's home directory ($_homedir)? "; then
3391553Srgrimes			pw_rswitch="-r"
3401553Srgrimes		fi
3411553Srgrimes	else
3421553Srgrimes		pw_rswitch="-r"
3431553Srgrimes	fi
3441553Srgrimes
3451553Srgrimes	# Disable any further attempts to log into this account
3461553Srgrimes	pw 2>/dev/null lock $_user
3471553Srgrimes
3481553Srgrimes	# Remove crontab, mail spool, etc. Then obliterate the user from
3491553Srgrimes	# the passwd and group database.
3501553Srgrimes	#
3511553Srgrimes	! verbose && echo -n "Removing user ($_user):"
3521553Srgrimes	rm_crontab $_user
3531553Srgrimes	rm_at_jobs $_user
3541553Srgrimes	rm_ipc $_user
3551553Srgrimes	kill_procs $_user
3561553Srgrimes	rm_files $_user
3571553Srgrimes	rm_mail $_user
3581553Srgrimes	rm_user $_user
3591553Srgrimes	! verbose && echo "."
3601553Srgrimesdone
3611553Srgrimes