rmuser.sh revision 126398
11573Srgrimes#!/bin/sh
21573Srgrimes#
31573Srgrimes# Copyright (c) 2002, 2003 Michael Telahun Makonnen. All rights reserved.
41573Srgrimes#
51573Srgrimes# Redistribution and use in source and binary forms, with or without
61573Srgrimes# modification, are permitted provided that the following conditions
71573Srgrimes# are met:
81573Srgrimes# 1. Redistributions of source code must retain the above copyright
91573Srgrimes#    notice, this list of conditions and the following disclaimer.
101573Srgrimes# 2. Redistributions in binary form must reproduce the above copyright
111573Srgrimes#    notice, this list of conditions and the following disclaimer in the
121573Srgrimes#    documentation and/or other materials provided with the distribution.
131573Srgrimes#
141573Srgrimes# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
151573Srgrimes# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
161573Srgrimes# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
171573Srgrimes# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
181573Srgrimes# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
191573Srgrimes# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
201573Srgrimes# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
211573Srgrimes# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
221573Srgrimes# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
231573Srgrimes# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
241573Srgrimes#
251573Srgrimes#	Email: Mike Makonnen <mtm@FreeBSD.Org>
261573Srgrimes#
271573Srgrimes# $FreeBSD: head/usr.sbin/adduser/rmuser.sh 126398 2004-02-29 09:54:15Z schweikh $
281573Srgrimes#
291573Srgrimes
301573SrgrimesATJOBDIR="/var/at/jobs"
311573SrgrimesCRONJOBDIR="/var/cron/tabs"
321573SrgrimesMAILSPOOL="/var/mail"
331573SrgrimesSIGKILL="-KILL"
341573SrgrimesTEMPDIRS="/tmp /var/tmp"
351573SrgrimesTHISCMD=`/usr/bin/basename $0`
361573Srgrimes
371573Srgrimes# err msg
381573Srgrimes#	Display $msg on stderr.
391573Srgrimes#
401573Srgrimeserr() {
411573Srgrimes	echo 1>&2 ${THISCMD}: $*
421573Srgrimes}
431573Srgrimes
441573Srgrimes# verbose
451573Srgrimes#	Returns 0 if verbose mode is set, 1 if it is not.
461573Srgrimes#
471573Srgrimesverbose() {
481573Srgrimes	[ -n "$vflag" ] && return 0 || return 1
491573Srgrimes}
501573Srgrimes
511573Srgrimes# rm_files login
521573Srgrimes#	Removes files or empty directories belonging to $login from various
531573Srgrimes#	temporary directories.
541573Srgrimes#
551573Srgrimesrm_files() {
561573Srgrimes	# The argument is required
571573Srgrimes	[ -n $1 ] && login=$1 || return
581573Srgrimes
591573Srgrimes	totalcount=0
601573Srgrimes	for _dir in ${TEMPDIRS} ; do
611573Srgrimes		filecount=0
621573Srgrimes		if [ ! -d $_dir ]; then
631573Srgrimes			err "$_dir is not a valid directory."
641573Srgrimes			continue
651573Srgrimes		fi
661573Srgrimes		verbose && echo -n "Removing files owned by ($login) in $_dir:"
671573Srgrimes		filecount=`find 2>/dev/null "$_dir" -user "$login" -delete -print |
681573Srgrimes		    wc -l | sed 's/ *//'`
691573Srgrimes		verbose && echo " $filecount removed."
701573Srgrimes		totalcount=$(($totalcount + $filecount))
711573Srgrimes	done
721573Srgrimes	! verbose && [ $totalcount -ne 0 ] && echo -n " files($totalcount)"
731573Srgrimes}
741573Srgrimes
751573Srgrimes# rm_mail login
761573Srgrimes#	Removes unix mail and pop daemon files belonging to the user
771573Srgrimes#	specified in the $login argument.
781573Srgrimes#
791573Srgrimesrm_mail() {
801573Srgrimes	# The argument is required
811573Srgrimes	[ -n $1 ] && login=$1 || return
821573Srgrimes
831573Srgrimes	verbose && echo -n "Removing mail spool(s) for ($login):"
841573Srgrimes	if [ -f ${MAILSPOOL}/$login ]; then
851573Srgrimes		verbose && echo -n " ${MAILSPOOL}/$login" ||
861573Srgrimes		    echo -n " mailspool"
871573Srgrimes		rm ${MAILSPOOL}/$login
881573Srgrimes	fi
891573Srgrimes	if [ -f ${MAILSPOOL}/${login}.pop ]; then
901573Srgrimes		verbose && echo -n " ${MAILSPOOL}/${login}.pop" ||
911573Srgrimes		    echo -n " pop3"
921573Srgrimes		rm ${MAILSPOOL}/${login}.pop
931573Srgrimes	fi
941573Srgrimes	verbose && echo '.'
951573Srgrimes}
961573Srgrimes
971573Srgrimes# kill_procs login
98#	Send a SIGKILL to all processes owned by $login.
99#
100kill_procs() {
101	# The argument is required
102	[ -n $1 ] && login=$1 || return
103
104	verbose && echo -n "Terminating all processes owned by ($login):"
105	killcount=0
106	proclist=`ps 2>/dev/null -U $login | grep -v '^\ *PID' | awk '{print $1}'`
107	for _pid in $proclist ; do
108		kill 2>/dev/null ${SIGKILL} $_pid
109		killcount=$(($killcount + 1))
110	done
111	verbose && echo " ${SIGKILL} signal sent to $killcount processes."
112	! verbose && [ $killcount -ne 0 ] && echo -n " processes(${killcount})"
113}
114
115# rm_at_jobs login
116#	Remove at (1) jobs belonging to $login.
117#
118rm_at_jobs() {
119	# The argument is required
120	[ -n $1 ] && login=$1 || return
121
122	atjoblist=`find 2>/dev/null ${ATJOBDIR} -maxdepth 1 -user $login -print`
123	jobcount=0
124	verbose && echo -n "Removing at(1) jobs owned by ($login):"
125	for _atjob in $atjoblist ; do
126		rm -f $_atjob
127		jobcount=$(($jobcount + 1))
128	done
129	verbose && echo " $jobcount removed."
130	! verbose && [ $jobcount -ne 0 ] && echo -n " at($jobcount)"
131}
132
133# rm_crontab login
134#	Removes crontab file belonging to user $login.
135#
136rm_crontab() {
137	# The argument is required
138	[ -n $1 ] && login=$1 || return
139
140	verbose && echo -n "Removing crontab for ($login):"
141	if [ -f ${CRONJOBDIR}/$login ]; then
142		verbose && echo -n " ${CRONJOBDIR}/$login" || echo -n " crontab"
143		rm -f ${CRONJOBDIR}/$login
144	fi
145	verbose && echo '.'
146}
147
148# rm_ipc login
149#	Remove all IPC mechanisms which are owned by $login.
150#
151rm_ipc() {
152	verbose && echo -n "Removing IPC mechanisms"
153	for i in s m q; do
154		ipcs -$i |
155		awk -v i=$i -v login=$1 '$1 == i && $5 == login { print $2 }' |
156		xargs -n 1 ipcrm -$i
157	done
158	verbose && echo '.'
159}
160
161# rm_user login
162#	Remove user $login from the system. This subroutine makes use
163#	of the pw(8) command to remove a user from the system. The pw(8)
164#	command will remove the specified user from the user database
165#	and group file and remove any crontabs. His home
166#	directory will be removed if it is owned by him and contains no 
167#	files or subdirectories owned by other users. Mail spool files will
168#	also be removed.
169#
170rm_user() {
171	# The argument is required
172	[ -n $1 ] && login=$1 || return
173
174	verbose && echo -n "Removing user ($login)"
175	[ -n "$pw_rswitch" ] && {
176		verbose && echo -n " (including home directory)"
177		! verbose && echo -n " home"
178	}
179	! verbose && echo -n " passwd"
180	verbose && echo -n " from the system:"
181	pw userdel -n $login $pw_rswitch
182	verbose && echo ' Done.'
183}
184
185# prompt_yesno msg
186#	Prompts the user with a $msg. The answer is expected to be
187#	yes, no, or some variation thereof. This subroutine returns 0
188#	if the answer was yes, 1 if it was not.
189#
190prompt_yesno() {
191	# The argument is required
192	[ -n "$1" ] && msg="$1" || return
193
194        while : ; do
195                echo -n "$msg"
196                read _ans
197                case $_ans in
198                [Nn][Oo]|[Nn])
199			return 1
200                        ;;
201                [Yy][Ee][Ss]|[Yy][Ee]|[Yy])
202                        return 0
203                        ;;
204                *)
205                        ;;
206                esac
207	done
208}
209
210# show_usage
211#	(no arguments)
212#	Display usage message.
213#
214show_usage() {
215	echo "usage: ${THISCMD} [-yv] [-f file] [user ...]"
216	echo "       if the -y switch is used, either the -f switch or"
217	echo "       one or more user names must be given"
218}
219
220#### END SUBROUTINE DEFENITION ####
221
222ffile=
223fflag=
224procowner=
225pw_rswitch=
226userlist=
227yflag=
228vflag=
229
230procowner=`/usr/bin/id -u`
231if [ "$procowner" != "0" ]; then
232	err 'you must be root (0) to use this utility.'
233	exit 1
234fi
235
236args=`getopt 2>/dev/null yvf: $*`
237if [ "$?" != "0" ]; then
238	show_usage
239	exit 1
240fi
241set -- $args
242for _switch ; do
243	case $_switch in
244	-y)
245		yflag=1
246		shift
247		;;
248	-v)
249		vflag=1
250		shift
251		;;
252	-f)
253		fflag=1
254		ffile="$2"
255		shift; shift
256		;;
257	--)
258		shift
259		break
260		;;
261	esac
262done
263
264# Get user names from a file if the -f switch was used. Otherwise,
265# get them from the commandline arguments. If we're getting it
266# from a file, the file must be owned by and writable only by root.
267#
268if [ $fflag ]; then
269	_insecure=`find $ffile ! -user 0 -or -perm +0022`
270	if [ -n "$_insecure" ]; then
271		err "file ($ffile) must be owned by and writeable only by root."
272		exit 1
273	fi
274	if [ -r "$ffile" ]; then
275		userlist=`cat $ffile | while read _user _junk ; do
276			case $_user in
277			\#*|'')
278				;;
279			*)
280				echo -n "$userlist $_user"
281				;;
282			esac
283		done`
284	fi
285else
286	while [ $1 ] ; do
287		userlist="$userlist $1"
288		shift
289	done
290fi
291
292# If the -y or -f switch has been used and the list of users to remove
293# is empty it is a fatal error. Otherwise, prompt the user for a list
294# of one or more user names.
295#
296if [ ! "$userlist" ]; then
297	if [ $fflag ]; then
298		err "($ffile) does not exist or does not contain any user names."
299		exit 1
300	elif [ $yflag ]; then
301		show_usage
302		exit 1
303	else
304		echo -n "Please enter one or more user name's: "
305		read userlist
306	fi
307fi
308
309_user=
310_uid=
311for _user in $userlist ; do
312	# Make sure the name exists in the passwd database and that it
313	# does not have a uid of 0
314	#
315	userrec=`pw 2>/dev/null usershow -n $_user`
316	if [ "$?" != "0" ]; then
317		err "user ($_user) does not exist in the password database."
318		continue
319	fi
320	_uid=`echo $userrec | awk -F: '{print $3}'`
321	if [ "$_uid" = "0" ]; then
322		err "user ($_user) has uid 0. You may not remove this user."
323		continue
324	fi
325
326	# If the -y switch was not used ask for confirmation to remove the
327	# user and home directory.
328	#
329	if [ -z "$yflag" ]; then
330		echo "Matching password entry:"
331		echo
332		echo $userrec
333		echo
334		if ! prompt_yesno "Is this the entry you wish to remove? " ; then
335			continue
336		fi
337		_homedir=`echo $userrec | awk -F: '{print $9}'`
338		if prompt_yesno "Remove user's home directory ($_homedir)? "; then
339			pw_rswitch="-r"
340		fi
341	else
342		pw_rswitch="-r"
343	fi
344
345	# Disable any further attempts to log into this account
346	pw 2>/dev/null lock $_user
347
348	# Remove crontab, mail spool, etc. Then obliterate the user from
349	# the passwd and group database.
350	#
351	! verbose && echo -n "Removing user ($_user):"
352	rm_crontab $_user
353	rm_at_jobs $_user
354	rm_ipc $_user
355	kill_procs $_user
356	rm_files $_user
357	rm_mail $_user
358	rm_user $_user
359	! verbose && echo "."
360done
361