xref: /dflybsd-src/usr.sbin/adduser/rmuser.sh (revision 15a59a19915f78e28400dfa6e8c5b900e91b84c6)
18d20e303SChris Pressey#!/bin/sh
28d20e303SChris Pressey#
38d20e303SChris Pressey# Copyright (c) 2002, 2003 Michael Telahun Makonnen. All rights reserved.
48d20e303SChris Pressey#
58d20e303SChris Pressey# Redistribution and use in source and binary forms, with or without
68d20e303SChris Pressey# modification, are permitted provided that the following conditions
78d20e303SChris Pressey# are met:
88d20e303SChris Pressey# 1. Redistributions of source code must retain the above copyright
98d20e303SChris Pressey#    notice, this list of conditions and the following disclaimer.
108d20e303SChris Pressey# 2. Redistributions in binary form must reproduce the above copyright
118d20e303SChris Pressey#    notice, this list of conditions and the following disclaimer in the
128d20e303SChris Pressey#    documentation and/or other materials provided with the distribution.
138d20e303SChris Pressey#
148d20e303SChris Pressey# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
158d20e303SChris Pressey# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
168d20e303SChris Pressey# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
178d20e303SChris Pressey# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
188d20e303SChris Pressey# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
198d20e303SChris Pressey# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
208d20e303SChris Pressey# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
218d20e303SChris Pressey# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
228d20e303SChris Pressey# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
238d20e303SChris Pressey# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
248d20e303SChris Pressey#
258d20e303SChris Pressey#	Email: Mike Makonnen <mtm@FreeBSD.Org>
268d20e303SChris Pressey#
279ed5bb8aSSascha Wildner# $FreeBSD: src/usr.sbin/adduser/rmuser.sh,v 1.11 2008/07/30 18:37:21 jhb Exp $
288d20e303SChris Pressey#
298d20e303SChris Pressey
308d20e303SChris PresseyATJOBDIR="/var/at/jobs"
318d20e303SChris PresseyCRONJOBDIR="/var/cron/tabs"
328d20e303SChris PresseyMAILSPOOL="/var/mail"
338d20e303SChris PresseySIGKILL="-KILL"
348d20e303SChris PresseyTEMPDIRS="/tmp /var/tmp"
358d20e303SChris PresseyTHISCMD=`/usr/bin/basename $0`
369ed5bb8aSSascha WildnerPWCMD="${PWCMD:-/usr/sbin/pw}"
378d20e303SChris Pressey
388d20e303SChris Pressey# err msg
398d20e303SChris Pressey#	Display $msg on stderr.
408d20e303SChris Pressey#
418d20e303SChris Presseyerr() {
428d20e303SChris Pressey	echo 1>&2 ${THISCMD}: $*
438d20e303SChris Pressey}
448d20e303SChris Pressey
458d20e303SChris Pressey# verbose
468d20e303SChris Pressey#	Returns 0 if verbose mode is set, 1 if it is not.
478d20e303SChris Pressey#
488d20e303SChris Presseyverbose() {
498d20e303SChris Pressey	[ -n "$vflag" ] && return 0 || return 1
508d20e303SChris Pressey}
518d20e303SChris Pressey
528d20e303SChris Pressey# rm_files login
538d20e303SChris Pressey#	Removes files or empty directories belonging to $login from various
548d20e303SChris Pressey#	temporary directories.
558d20e303SChris Pressey#
568d20e303SChris Presseyrm_files() {
578d20e303SChris Pressey	# The argument is required
588d20e303SChris Pressey	[ -n $1 ] && login=$1 || return
598d20e303SChris Pressey
608d20e303SChris Pressey	totalcount=0
618d20e303SChris Pressey	for _dir in ${TEMPDIRS} ; do
628d20e303SChris Pressey		filecount=0
638d20e303SChris Pressey		if [ ! -d $_dir ]; then
648d20e303SChris Pressey			err "$_dir is not a valid directory."
658d20e303SChris Pressey			continue
668d20e303SChris Pressey		fi
678d20e303SChris Pressey		verbose && echo -n "Removing files owned by ($login) in $_dir:"
688d20e303SChris Pressey		filecount=`find 2>/dev/null "$_dir" -user "$login" -delete -print |
698d20e303SChris Pressey		    wc -l | sed 's/ *//'`
708d20e303SChris Pressey		verbose && echo " $filecount removed."
718d20e303SChris Pressey		totalcount=$(($totalcount + $filecount))
728d20e303SChris Pressey	done
738d20e303SChris Pressey	! verbose && [ $totalcount -ne 0 ] && echo -n " files($totalcount)"
748d20e303SChris Pressey}
758d20e303SChris Pressey
768d20e303SChris Pressey# rm_mail login
778d20e303SChris Pressey#	Removes unix mail and pop daemon files belonging to the user
788d20e303SChris Pressey#	specified in the $login argument.
798d20e303SChris Pressey#
808d20e303SChris Presseyrm_mail() {
818d20e303SChris Pressey	# The argument is required
828d20e303SChris Pressey	[ -n $1 ] && login=$1 || return
838d20e303SChris Pressey
848d20e303SChris Pressey	verbose && echo -n "Removing mail spool(s) for ($login):"
858d20e303SChris Pressey	if [ -f ${MAILSPOOL}/$login ]; then
868d20e303SChris Pressey		verbose && echo -n " ${MAILSPOOL}/$login" ||
878d20e303SChris Pressey		    echo -n " mailspool"
888d20e303SChris Pressey		rm ${MAILSPOOL}/$login
898d20e303SChris Pressey	fi
909ed5bb8aSSascha Wildner	if [ -f ${MAILSPOOL}/.${login}.pop ]; then
919ed5bb8aSSascha Wildner		verbose && echo -n " ${MAILSPOOL}/.${login}.pop" ||
928d20e303SChris Pressey		    echo -n " pop3"
939ed5bb8aSSascha Wildner		rm ${MAILSPOOL}/.${login}.pop
948d20e303SChris Pressey	fi
958d20e303SChris Pressey	verbose && echo '.'
968d20e303SChris Pressey}
978d20e303SChris Pressey
988d20e303SChris Pressey# kill_procs login
998d20e303SChris Pressey#	Send a SIGKILL to all processes owned by $login.
1008d20e303SChris Pressey#
1018d20e303SChris Presseykill_procs() {
1028d20e303SChris Pressey	# The argument is required
1038d20e303SChris Pressey	[ -n $1 ] && login=$1 || return
1048d20e303SChris Pressey
1058d20e303SChris Pressey	verbose && echo -n "Terminating all processes owned by ($login):"
1068d20e303SChris Pressey	killcount=0
1078d20e303SChris Pressey	proclist=`ps 2>/dev/null -U $login | grep -v '^\ *PID' | awk '{print $1}'`
1088d20e303SChris Pressey	for _pid in $proclist ; do
1098d20e303SChris Pressey		kill 2>/dev/null ${SIGKILL} $_pid
1108d20e303SChris Pressey		killcount=$(($killcount + 1))
1118d20e303SChris Pressey	done
1128d20e303SChris Pressey	verbose && echo " ${SIGKILL} signal sent to $killcount processes."
1138d20e303SChris Pressey	! verbose && [ $killcount -ne 0 ] && echo -n " processes(${killcount})"
1148d20e303SChris Pressey}
1158d20e303SChris Pressey
1168d20e303SChris Pressey# rm_at_jobs login
1178d20e303SChris Pressey#	Remove at (1) jobs belonging to $login.
1188d20e303SChris Pressey#
1198d20e303SChris Presseyrm_at_jobs() {
1208d20e303SChris Pressey	# The argument is required
1218d20e303SChris Pressey	[ -n $1 ] && login=$1 || return
1228d20e303SChris Pressey
1238d20e303SChris Pressey	atjoblist=`find 2>/dev/null ${ATJOBDIR} -maxdepth 1 -user $login -print`
1248d20e303SChris Pressey	jobcount=0
1258d20e303SChris Pressey	verbose && echo -n "Removing at(1) jobs owned by ($login):"
1268d20e303SChris Pressey	for _atjob in $atjoblist ; do
1278d20e303SChris Pressey		rm -f $_atjob
1288d20e303SChris Pressey		jobcount=$(($jobcount + 1))
1298d20e303SChris Pressey	done
1308d20e303SChris Pressey	verbose && echo " $jobcount removed."
1318d20e303SChris Pressey	! verbose && [ $jobcount -ne 0 ] && echo -n " at($jobcount)"
1328d20e303SChris Pressey}
1338d20e303SChris Pressey
1348d20e303SChris Pressey# rm_crontab login
1358d20e303SChris Pressey#	Removes crontab file belonging to user $login.
1368d20e303SChris Pressey#
1378d20e303SChris Presseyrm_crontab() {
1388d20e303SChris Pressey	# The argument is required
1398d20e303SChris Pressey	[ -n $1 ] && login=$1 || return
1408d20e303SChris Pressey
1418d20e303SChris Pressey	verbose && echo -n "Removing crontab for ($login):"
1428d20e303SChris Pressey	if [ -f ${CRONJOBDIR}/$login ]; then
1438d20e303SChris Pressey		verbose && echo -n " ${CRONJOBDIR}/$login" || echo -n " crontab"
1448d20e303SChris Pressey		rm -f ${CRONJOBDIR}/$login
1458d20e303SChris Pressey	fi
1468d20e303SChris Pressey	verbose && echo '.'
1478d20e303SChris Pressey}
1488d20e303SChris Pressey
1498d20e303SChris Pressey# rm_ipc login
1508d20e303SChris Pressey#	Remove all IPC mechanisms which are owned by $login.
1518d20e303SChris Pressey#
1528d20e303SChris Presseyrm_ipc() {
1538d20e303SChris Pressey	verbose && echo -n "Removing IPC mechanisms"
1548d20e303SChris Pressey	for i in s m q; do
1558d20e303SChris Pressey		ipcs -$i |
1568d20e303SChris Pressey		awk -v i=$i -v login=$1 '$1 == i && $5 == login { print $2 }' |
1578d20e303SChris Pressey		xargs -n 1 ipcrm -$i
1588d20e303SChris Pressey	done
1598d20e303SChris Pressey	verbose && echo '.'
1608d20e303SChris Pressey}
1618d20e303SChris Pressey
1628d20e303SChris Pressey# rm_user login
1638d20e303SChris Pressey#	Remove user $login from the system. This subroutine makes use
1648d20e303SChris Pressey#	of the pw(8) command to remove a user from the system. The pw(8)
1658d20e303SChris Pressey#	command will remove the specified user from the user database
1668d20e303SChris Pressey#	and group file and remove any crontabs. His home
1678d20e303SChris Pressey#	directory will be removed if it is owned by him and contains no
1688d20e303SChris Pressey#	files or subdirectories owned by other users. Mail spool files will
1698d20e303SChris Pressey#	also be removed.
1708d20e303SChris Pressey#
1718d20e303SChris Presseyrm_user() {
1728d20e303SChris Pressey	# The argument is required
1738d20e303SChris Pressey	[ -n $1 ] && login=$1 || return
1748d20e303SChris Pressey
1758d20e303SChris Pressey	verbose && echo -n "Removing user ($login)"
1768d20e303SChris Pressey	[ -n "$pw_rswitch" ] && {
1778d20e303SChris Pressey		verbose && echo -n " (including home directory)"
1788d20e303SChris Pressey		! verbose && echo -n " home"
1798d20e303SChris Pressey	}
1808d20e303SChris Pressey	! verbose && echo -n " passwd"
1818d20e303SChris Pressey	verbose && echo -n " from the system:"
1829ed5bb8aSSascha Wildner	${PWCMD} userdel -n $login $pw_rswitch
1838d20e303SChris Pressey	verbose && echo ' Done.'
1848d20e303SChris Pressey}
1858d20e303SChris Pressey
1868d20e303SChris Pressey# prompt_yesno msg
1878d20e303SChris Pressey#	Prompts the user with a $msg. The answer is expected to be
1888d20e303SChris Pressey#	yes, no, or some variation thereof. This subroutine returns 0
1898d20e303SChris Pressey#	if the answer was yes, 1 if it was not.
1908d20e303SChris Pressey#
1918d20e303SChris Presseyprompt_yesno() {
1928d20e303SChris Pressey	# The argument is required
1938d20e303SChris Pressey	[ -n "$1" ] && msg="$1" || return
1948d20e303SChris Pressey
1958d20e303SChris Pressey	while : ; do
1968d20e303SChris Pressey		echo -n "$msg"
1978d20e303SChris Pressey		read _ans
1988d20e303SChris Pressey		case $_ans in
1998d20e303SChris Pressey		[Nn][Oo]|[Nn])
2008d20e303SChris Pressey			return 1
2018d20e303SChris Pressey			;;
2028d20e303SChris Pressey		[Yy][Ee][Ss]|[Yy][Ee]|[Yy])
2038d20e303SChris Pressey			return 0
2048d20e303SChris Pressey			;;
2058d20e303SChris Pressey		*)
2068d20e303SChris Pressey			;;
2078d20e303SChris Pressey		esac
2088d20e303SChris Pressey	done
2098d20e303SChris Pressey}
2108d20e303SChris Pressey
2118d20e303SChris Pressey# show_usage
2128d20e303SChris Pressey#	(no arguments)
2138d20e303SChris Pressey#	Display usage message.
2148d20e303SChris Pressey#
2158d20e303SChris Presseyshow_usage() {
2168d20e303SChris Pressey	echo "usage: ${THISCMD} [-yv] [-f file] [user ...]"
2178d20e303SChris Pressey	echo "       if the -y switch is used, either the -f switch or"
2188d20e303SChris Pressey	echo "       one or more user names must be given"
2198d20e303SChris Pressey}
2208d20e303SChris Pressey
2218d20e303SChris Pressey#### END SUBROUTINE DEFENITION ####
2228d20e303SChris Pressey
2238d20e303SChris Presseyprocowner=
2248d20e303SChris Presseypw_rswitch=
225*15a59a19SAaron LIffile=
226*15a59a19SAaron LIfflag=
2278d20e303SChris Presseyvflag=
228*15a59a19SAaron LIyflag=
2298d20e303SChris Pressey
2308d20e303SChris Presseyprocowner=`/usr/bin/id -u`
2318d20e303SChris Presseyif [ "$procowner" != "0" ]; then
2328d20e303SChris Pressey	err 'you must be root (0) to use this utility.'
2338d20e303SChris Pressey	exit 1
2348d20e303SChris Presseyfi
2358d20e303SChris Pressey
236*15a59a19SAaron LIwhile getopts :f:hvy opt; do
237*15a59a19SAaron LI	case $opt in
238*15a59a19SAaron LI	f)
239*15a59a19SAaron LI		fflag=1
240*15a59a19SAaron LI		ffile="$OPTARG"
241*15a59a19SAaron LI		;;
242*15a59a19SAaron LI	h)
2438d20e303SChris Pressey		show_usage
2448d20e303SChris Pressey		exit 1
2458d20e303SChris Pressey		;;
246*15a59a19SAaron LI	v)
2478d20e303SChris Pressey		vflag=1
2488d20e303SChris Pressey		;;
249*15a59a19SAaron LI	y)
250*15a59a19SAaron LI		yflag=1
2518d20e303SChris Pressey		;;
252*15a59a19SAaron LI	\?)
253*15a59a19SAaron LI		err "Invalid option -${OPTARG}"
254*15a59a19SAaron LI		show_usage
255*15a59a19SAaron LI		exit 1
256*15a59a19SAaron LI		;;
257*15a59a19SAaron LI	:)
258*15a59a19SAaron LI		err "Option -${OPTARG} requires an argument"
259*15a59a19SAaron LI		show_usage
260*15a59a19SAaron LI		exit 1
2618d20e303SChris Pressey		;;
2628d20e303SChris Pressey	esac
2638d20e303SChris Presseydone
2648d20e303SChris Pressey
265*15a59a19SAaron LIshift $((OPTIND - 1))
266*15a59a19SAaron LI
2678d20e303SChris Pressey# Get user names from a file if the -f switch was used. Otherwise,
2688d20e303SChris Pressey# get them from the commandline arguments. If we're getting it
2698d20e303SChris Pressey# from a file, the file must be owned by and writable only by root.
2708d20e303SChris Pressey#
271*15a59a19SAaron LIif [ -n "$fflag" ]; then
2728d20e303SChris Pressey	_insecure=`find $ffile ! -user 0 -or -perm +0022`
2738d20e303SChris Pressey	if [ -n "$_insecure" ]; then
2748d20e303SChris Pressey		err "file ($ffile) must be owned by and writeable only by root."
2758d20e303SChris Pressey		exit 1
2768d20e303SChris Pressey	fi
2778d20e303SChris Pressey	if [ -r "$ffile" ]; then
2788d20e303SChris Pressey		userlist=`cat $ffile | while read _user _junk ; do
2798d20e303SChris Pressey			case $_user in
2808d20e303SChris Pressey			\#*|'')
2818d20e303SChris Pressey				;;
2828d20e303SChris Pressey			*)
283*15a59a19SAaron LI				echo -n "$_user "
2848d20e303SChris Pressey				;;
2858d20e303SChris Pressey			esac
2868d20e303SChris Pressey		done`
287*15a59a19SAaron LI	else
288*15a59a19SAaron LI		err "($ffile) does not exist or does not contain any user names."
289*15a59a19SAaron LI		exit 1
2908d20e303SChris Pressey	fi
2918d20e303SChris Presseyelse
292*15a59a19SAaron LI	userlist="$@"
2938d20e303SChris Presseyfi
2948d20e303SChris Pressey
295*15a59a19SAaron LI# If the -y switch has been used and the list of users to remove
2968d20e303SChris Pressey# is empty it is a fatal error. Otherwise, prompt the user for a list
2978d20e303SChris Pressey# of one or more user names.
2988d20e303SChris Pressey#
299*15a59a19SAaron LIuserlist=`echo "$userlist" | sed -e 's/[[:space:]]*$//'`
300*15a59a19SAaron LIif [ -z "$userlist" ]; then
301*15a59a19SAaron LI	if [ -n "$yflag" ]; then
3028d20e303SChris Pressey		show_usage
3038d20e303SChris Pressey		exit 1
3048d20e303SChris Pressey	else
3059ed5bb8aSSascha Wildner		echo -n "Please enter one or more user names: "
3068d20e303SChris Pressey		read userlist
3078d20e303SChris Pressey	fi
3088d20e303SChris Presseyfi
3098d20e303SChris Pressey
3108d20e303SChris Pressey_user=
3118d20e303SChris Pressey_uid=
3128d20e303SChris Presseyfor _user in $userlist ; do
3138d20e303SChris Pressey	# Make sure the name exists in the passwd database and that it
3148d20e303SChris Pressey	# does not have a uid of 0
3158d20e303SChris Pressey	#
3169ed5bb8aSSascha Wildner	userrec=`${PWCMD} 2>/dev/null usershow -n $_user`
3178d20e303SChris Pressey	if [ "$?" != "0" ]; then
3188d20e303SChris Pressey		err "user ($_user) does not exist in the password database."
3198d20e303SChris Pressey		continue
3208d20e303SChris Pressey	fi
3218d20e303SChris Pressey	_uid=`echo $userrec | awk -F: '{print $3}'`
3228d20e303SChris Pressey	if [ "$_uid" = "0" ]; then
3238d20e303SChris Pressey		err "user ($_user) has uid 0. You may not remove this user."
3248d20e303SChris Pressey		continue
3258d20e303SChris Pressey	fi
3268d20e303SChris Pressey
3278d20e303SChris Pressey	# If the -y switch was not used ask for confirmation to remove the
3288d20e303SChris Pressey	# user and home directory.
3298d20e303SChris Pressey	#
3308d20e303SChris Pressey	if [ -z "$yflag" ]; then
3318d20e303SChris Pressey		echo "Matching password entry:"
3328d20e303SChris Pressey		echo
3338d20e303SChris Pressey		echo $userrec
3348d20e303SChris Pressey		echo
3358d20e303SChris Pressey		if ! prompt_yesno "Is this the entry you wish to remove? " ; then
3368d20e303SChris Pressey			continue
3378d20e303SChris Pressey		fi
3388d20e303SChris Pressey		_homedir=`echo $userrec | awk -F: '{print $9}'`
3398d20e303SChris Pressey		if prompt_yesno "Remove user's home directory ($_homedir)? "; then
3408d20e303SChris Pressey			pw_rswitch="-r"
3418d20e303SChris Pressey		fi
3428d20e303SChris Pressey	else
3438d20e303SChris Pressey		pw_rswitch="-r"
3448d20e303SChris Pressey	fi
3458d20e303SChris Pressey
3468d20e303SChris Pressey	# Disable any further attempts to log into this account
3479ed5bb8aSSascha Wildner	${PWCMD} 2>/dev/null lock $_user
3488d20e303SChris Pressey
3498d20e303SChris Pressey	# Remove crontab, mail spool, etc. Then obliterate the user from
3508d20e303SChris Pressey	# the passwd and group database.
3518d20e303SChris Pressey	#
3528d20e303SChris Pressey	! verbose && echo -n "Removing user ($_user):"
3538d20e303SChris Pressey	rm_crontab $_user
3548d20e303SChris Pressey	rm_at_jobs $_user
3558d20e303SChris Pressey	rm_ipc $_user
3568d20e303SChris Pressey	kill_procs $_user
3578d20e303SChris Pressey	rm_files $_user
3588d20e303SChris Pressey	rm_mail $_user
3598d20e303SChris Pressey	rm_user $_user
3608d20e303SChris Pressey	! verbose && echo "."
3618d20e303SChris Presseydone
362