rmuser.sh revision 107543
1107543Sscottl#!/bin/sh
2107543Sscottl#
3107543Sscottl# Copyright (c) 2002 Michael Telahun Makonnen. All rights reserved.
4107543Sscottl#
5107543Sscottl# Redistribution and use in source and binary forms, with or without
6107543Sscottl# modification, are permitted provided that the following conditions
7107543Sscottl# are met:
8107543Sscottl# 1. Redistributions of source code must retain the above copyright
9107543Sscottl#    notice, this list of conditions and the following disclaimer.
10107543Sscottl# 2. Redistributions in binary form must reproduce the above copyright
11107543Sscottl#    notice, this list of conditions and the following disclaimer in the
12107543Sscottl#    documentation and/or other materials provided with the distribution.
13107543Sscottl# 3. The name of the author may not be used to endorse or promote products
14107543Sscottl#    derived from this software without specific prior written permission.
15107543Sscottl#
16107543Sscottl# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17107543Sscottl# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18107543Sscottl# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19107543Sscottl# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20107543Sscottl# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21107543Sscottl# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22107543Sscottl# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23107543Sscottl# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24107543Sscottl# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25107543Sscottl# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26107543Sscottl#
27107543Sscottl#	Email: Mike Makonnen <mtm@identd.net>
28107543Sscottl#
29107543Sscottl# $FreeBSD: head/usr.sbin/adduser/rmuser.sh 107543 2002-12-03 05:41:09Z scottl $
30107543Sscottl#
31107543Sscottl
32107543SscottlATJOBDIR="/var/at/jobs"
33107543SscottlCRONJOBDIR="/var/cron/tabs"
34107543SscottlMAILSPOOL="/var/mail"
35107543SscottlSIGKILL="-KILL"
36107543SscottlTEMPDIRS="/tmp /var/tmp /var/tmp/vi.recover"
37107543SscottlTHISCMD=`/usr/bin/basename $0`
38107543Sscottl
39107543Sscottl# err msg
40107543Sscottl#	Display $msg on stderr.
41107543Sscottl#
42107543Sscottlerr() {
43107543Sscottl	echo 1>&2 ${THISCMD}: $*
44107543Sscottl}
45107543Sscottl
46107543Sscottl# rm_files login
47107543Sscottl#	Removes files or empty directories belonging to $login from various
48107543Sscottl#	temporary directories.
49107543Sscottl#
50107543Sscottlrm_files() {
51107543Sscottl	# The argument is required
52107543Sscottl	[ -n $1 ] && login=$1 || return
53107543Sscottl	
54107543Sscottl	for _dir in ${TEMPDIRS} ; do
55107543Sscottl		if [ ! -d $_dir ]; then
56107543Sscottl			err "$_dir is not a valid directory."
57107543Sscottl			continue
58107543Sscottl		fi
59107543Sscottl		echo -n "Removing files owned by ($login) in $_dir:"
60107543Sscottl		filecount=0
61107543Sscottl		_ownedfiles=`find 2>/dev/null $_dir -maxdepth 1 -user $login -print`
62107543Sscottl		for _file in $_ownedfiles ; do
63107543Sscottl			rm -fd $_file
64107543Sscottl			filecount=`expr $filecount + 1`
65107543Sscottl		done
66107543Sscottl		echo " $filecount removed."
67107543Sscottl	done
68107543Sscottl}
69107543Sscottl
70107543Sscottl# rm_mail login
71107543Sscottl#	Removes unix mail and pop daemon files belonging to the user
72107543Sscottl#	specified in the $login argument.
73107543Sscottl#
74107543Sscottlrm_mail() {
75107543Sscottl	# The argument is required
76107543Sscottl	[ -n $1 ] && login=$1 || return
77107543Sscottl
78107543Sscottl	echo -n "Removing mail spool(s) for ($login):"
79107543Sscottl	if [ -f ${MAILSPOOL}/$login ]; then
80107543Sscottl		echo -n " ${MAILSPOOL}/$login"
81107543Sscottl		rm ${MAILSPOOL}/$login
82107543Sscottl	fi
83107543Sscottl	if [ -f ${MAILSPOOL}/${login}.pop ]; then
84107543Sscottl		echo -n " ${MAILSPOOL}/${login}.pop"
85107543Sscottl		rm ${MAILSPOOL}/${login}.pop
86107543Sscottl	fi
87107543Sscottl	echo '.'
88107543Sscottl}
89107543Sscottl
90107543Sscottl# kill_procs login
91107543Sscottl#	Send a SIGKILL to all processes owned by $login.
92107543Sscottl#
93107543Sscottlkill_procs() {
94107543Sscottl	# The argument is required
95107543Sscottl	[ -n $1 ] && login=$1 || return
96107543Sscottl
97107543Sscottl	echo -n "Terminating all processes owned by ($login):"
98107543Sscottl	killcount=0
99107543Sscottl	proclist=`ps 2>/dev/null -U $login | grep -v '^\ *PID' | awk '{print $1}'`
100107543Sscottl	for _pid in $proclist ; do
101107543Sscottl		kill 2>/dev/null ${SIGKILL} $_pid
102107543Sscottl		killcount=`expr $killcount + 1`
103107543Sscottl	done
104107543Sscottl	echo " ${SIGKILL} signal sent to $killcount processes."
105107543Sscottl}
106107543Sscottl
107107543Sscottl# rm_at_jobs login
108107543Sscottl#	Remove at (1) jobs belonging to $login.
109107543Sscottl#
110107543Sscottlrm_at_jobs() {
111107543Sscottl	# The argument is required
112107543Sscottl	[ -n $1 ] && login=$1 || return
113107543Sscottl
114107543Sscottl	atjoblist=`find 2>/dev/null ${ATJOBDIR} -maxdepth 1 -user $login -print`
115107543Sscottl	jobcount=0
116107543Sscottl	echo -n "Removing at(1) jobs owned by ($login):"
117107543Sscottl	for _atjob in $atjoblist ; do
118107543Sscottl		rm -f $_atjob
119107543Sscottl		jobcount=`expr $jobcount + 1`
120107543Sscottl	done
121107543Sscottl	echo " $jobcount removed."
122107543Sscottl}
123107543Sscottl
124107543Sscottl# rm_crontab login
125107543Sscottl#	Removes crontab file belonging to user $login.
126107543Sscottl#
127107543Sscottlrm_crontab() {
128107543Sscottl	# The argument is required
129107543Sscottl	[ -n $1 ] && login=$1 || return
130107543Sscottl
131107543Sscottl	echo -n "Removing crontab for ($login):"
132107543Sscottl	if [ -f ${CRONJOBDIR}/$login ]; then
133107543Sscottl		echo -n " ${CRONJOBDIR}/$login"
134107543Sscottl		rm -f ${CRONJOBDIR}/$login
135107543Sscottl	fi
136107543Sscottl	echo '.'
137107543Sscottl}
138107543Sscottl
139107543Sscottl# rm_user login
140107543Sscottl#	Remove user $login from the system. This subroutine makes use
141107543Sscottl#	of the pw(8) command to remove a user from the system. The pw(8)
142107543Sscottl#	command will remove the specified user from the user database
143107543Sscottl#	and group file and remove any crontabs. His home
144107543Sscottl#	directory will be removed if it is owned by him and contains no 
145107543Sscottl#	files or subdirectories owned by other users. Mail spool files will
146107543Sscottl#	also be removed.
147107543Sscottl#
148107543Sscottlrm_user() {
149107543Sscottl	# The argument is required
150107543Sscottl	[ -n $1 ] && login=$1 || return
151107543Sscottl
152107543Sscottl	echo -n "Removing user ($login)"
153107543Sscottl	[ -n "$pw_rswitch" ] && echo -n " (including home directory)"
154107543Sscottl	echo -n " from the system:"
155107543Sscottl	pw userdel -n $login $pw_rswitch
156107543Sscottl	echo ' Done.'
157107543Sscottl}
158107543Sscottl
159107543Sscottl# prompt_yesno msg
160107543Sscottl#	Prompts the user with a $msg. The answer is expected to be
161107543Sscottl#	yes, no, or some variation thereof. This subroutine returns 0
162107543Sscottl#	if the answer was yes, 1 if it was not.
163107543Sscottl#
164107543Sscottlprompt_yesno() {
165107543Sscottl	# The argument is required
166107543Sscottl	[ -n "$1" ] && msg=$1 || return
167107543Sscottl
168107543Sscottl        while : ; do
169107543Sscottl                echo -n $msg
170107543Sscottl                read _ans
171107543Sscottl                case $_ans in
172107543Sscottl                [Nn][Oo]|[Nn])
173107543Sscottl			return 1
174107543Sscottl                        ;;
175107543Sscottl                [Yy][Ee][Ss]|[Yy][Ee]|[Yy])
176107543Sscottl                        return 0
177107543Sscottl                        ;;
178107543Sscottl                *)
179107543Sscottl                        ;;
180107543Sscottl                esac
181107543Sscottl	done
182107543Sscottl}
183107543Sscottl
184107543Sscottl# show_usage
185107543Sscottl#	(no arguments)
186107543Sscottl#	Display usage message.
187107543Sscottl#
188107543Sscottlshow_usage() {
189107543Sscottl	echo "usage: ${THISCMD} [-y] [-f file] [user ...]"
190107543Sscottl	echo "       if the -y switch is used, either the -f switch or"
191107543Sscottl	echo "       one or more user names must be given"
192107543Sscottl}
193107543Sscottl
194107543Sscottl#### END SUBROUTINE DEFENITION ####
195107543Sscottl
196107543Sscottlffile=
197107543Sscottlfflag=
198107543Sscottlprocowner=
199107543Sscottlpw_rswitch=
200107543Sscottluserlist=
201107543Sscottlyflag=
202107543Sscottl
203107543Sscottlprocowner=`/usr/bin/id -u`
204107543Sscottlif [ "$procowner" != "0" ]; then
205107543Sscottl	err 'you must be root (0) to use this utility.'
206107543Sscottl	exit 1
207107543Sscottlfi
208107543Sscottl
209107543Sscottlargs=`getopt 2>/dev/null yf: $*`
210107543Sscottlif [ "$?" != "0" ]; then
211107543Sscottl	show_usage
212107543Sscottl	exit 1
213107543Sscottlfi
214107543Sscottlset -- $args
215107543Sscottlfor _switch ; do
216107543Sscottl	case $_switch in
217107543Sscottl	-y)
218107543Sscottl		yflag=1
219107543Sscottl		shift
220107543Sscottl		;;
221107543Sscottl	-f)
222107543Sscottl		fflag=1
223107543Sscottl		ffile="$2"
224107543Sscottl		shift; shift
225107543Sscottl		;;
226107543Sscottl	--)
227107543Sscottl		shift
228107543Sscottl		break
229107543Sscottl		;;
230107543Sscottl	esac
231107543Sscottldone
232107543Sscottl
233107543Sscottl# Get user names from a file if the -f switch was used. Otherwise,
234107543Sscottl# get them from the commandline arguments. If we're getting it
235107543Sscottl# from a file, the file must be owned by and writable only by root.
236107543Sscottl#
237107543Sscottlif [ $fflag ]; then
238107543Sscottl	_insecure=`find $ffile ! -user 0 -or -perm +0022`
239107543Sscottl	if [ -n "$_insecure" ]; then
240107543Sscottl		err "file ($ffile) must be owned by and writeable only by root."
241107543Sscottl		exit 1
242107543Sscottl	fi
243107543Sscottl	if [ -r "$ffile" ]; then
244107543Sscottl		userlist=`cat $ffile | while read _user _junk ; do
245107543Sscottl			case $_user in
246107543Sscottl			\#*|'')
247107543Sscottl				;;
248107543Sscottl			*)
249107543Sscottl				echo -n "$userlist $_user"
250107543Sscottl				;;
251107543Sscottl			esac
252107543Sscottl		done`
253107543Sscottl	fi
254107543Sscottlelse
255107543Sscottl	while [ $1 ] ; do
256107543Sscottl		userlist="$userlist $1"
257107543Sscottl		shift
258107543Sscottl	done
259107543Sscottlfi
260107543Sscottl
261107543Sscottl# If the -y or -f switch has been used and the list of users to remove
262107543Sscottl# is empty it is a fatal error. Otherwise, prompt the user for a list
263107543Sscottl# of one or more user names.
264107543Sscottl#
265107543Sscottlif [ ! "$userlist" ]; then
266107543Sscottl	if [ $fflag ]; then
267107543Sscottl		err "($ffile) does not exist or does not contain any user names."
268107543Sscottl		exit 1
269107543Sscottl	elif [ $yflag ]; then
270107543Sscottl		show_usage
271107543Sscottl		exit 1
272107543Sscottl	else
273107543Sscottl		echo -n "Please enter one or more user name's: "
274107543Sscottl		read userlist
275107543Sscottl	fi
276107543Sscottlfi
277107543Sscottl
278107543Sscottl_user=
279107543Sscottl_uid=
280107543Sscottlfor _user in $userlist ; do
281107543Sscottl	# Make sure the name exists in the passwd database and that it
282107543Sscottl	# does not have a uid of 0
283107543Sscottl	#
284107543Sscottl	userrec=`pw 2>/dev/null usershow -n $_user`
285107543Sscottl	if [ "$?" != "0" ]; then
286107543Sscottl		err "user ($_user) does not exist in the password database."
287107543Sscottl		continue
288107543Sscottl	fi
289107543Sscottl	_uid=`echo $userrec | awk -F: '{print $3}'`
290107543Sscottl	if [ "$_uid" = "0" ]; then
291107543Sscottl		err "user ($_user) has uid 0. You may not remove this user."
292107543Sscottl		continue
293107543Sscottl	fi
294107543Sscottl
295107543Sscottl	# If the -y switch was not used ask for confirmation to remove the
296107543Sscottl	# user and home directory.
297107543Sscottl	#
298107543Sscottl	if [ -z "$yflag" ]; then
299107543Sscottl		echo "Matching password entry:"
300107543Sscottl		echo
301107543Sscottl		echo $userrec
302107543Sscottl		echo
303107543Sscottl		if ! prompt_yesno "Is this the entry you wish to remove? " ; then
304107543Sscottl			continue
305107543Sscottl		fi
306107543Sscottl		_homedir=`echo $userrec | awk -F: '{print $9}'`
307107543Sscottl		if prompt_yesno "Remove user's home directory ($_homedir)?: "; then
308107543Sscottl			pw_rswitch="-r"
309107543Sscottl		fi
310107543Sscottl	else
311107543Sscottl		pw_rswitch="-r"
312107543Sscottl	fi
313107543Sscottl
314107543Sscottl	# Disable any further attempts to log into this account
315107543Sscottl	pw 2>/dev/null lock $_user
316107543Sscottl
317107543Sscottl	# Remove crontab, mail spool, etc. Then obliterate the user from
318107543Sscottl	# the passwd and group database.
319107543Sscottl	rm_crontab $_user
320107543Sscottl	rm_at_jobs $_user
321107543Sscottl	kill_procs $_user
322107543Sscottl	rm_mail $_user
323107543Sscottl	rm_files $_user
324107543Sscottl	rm_user $_user
325107543Sscottldone
326