rmuser.sh revision 107543
1#!/bin/sh
2#
3# Copyright (c) 2002 Michael Telahun Makonnen. All rights reserved.
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions
7# are met:
8# 1. Redistributions of source code must retain the above copyright
9#    notice, this list of conditions and the following disclaimer.
10# 2. Redistributions in binary form must reproduce the above copyright
11#    notice, this list of conditions and the following disclaimer in the
12#    documentation and/or other materials provided with the distribution.
13# 3. The name of the author may not be used to endorse or promote products
14#    derived from this software without specific prior written permission.
15#
16# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26#
27#	Email: Mike Makonnen <mtm@identd.net>
28#
29# $FreeBSD: head/usr.sbin/adduser/rmuser.sh 107543 2002-12-03 05:41:09Z scottl $
30#
31
32ATJOBDIR="/var/at/jobs"
33CRONJOBDIR="/var/cron/tabs"
34MAILSPOOL="/var/mail"
35SIGKILL="-KILL"
36TEMPDIRS="/tmp /var/tmp /var/tmp/vi.recover"
37THISCMD=`/usr/bin/basename $0`
38
39# err msg
40#	Display $msg on stderr.
41#
42err() {
43	echo 1>&2 ${THISCMD}: $*
44}
45
46# rm_files login
47#	Removes files or empty directories belonging to $login from various
48#	temporary directories.
49#
50rm_files() {
51	# The argument is required
52	[ -n $1 ] && login=$1 || return
53	
54	for _dir in ${TEMPDIRS} ; do
55		if [ ! -d $_dir ]; then
56			err "$_dir is not a valid directory."
57			continue
58		fi
59		echo -n "Removing files owned by ($login) in $_dir:"
60		filecount=0
61		_ownedfiles=`find 2>/dev/null $_dir -maxdepth 1 -user $login -print`
62		for _file in $_ownedfiles ; do
63			rm -fd $_file
64			filecount=`expr $filecount + 1`
65		done
66		echo " $filecount removed."
67	done
68}
69
70# rm_mail login
71#	Removes unix mail and pop daemon files belonging to the user
72#	specified in the $login argument.
73#
74rm_mail() {
75	# The argument is required
76	[ -n $1 ] && login=$1 || return
77
78	echo -n "Removing mail spool(s) for ($login):"
79	if [ -f ${MAILSPOOL}/$login ]; then
80		echo -n " ${MAILSPOOL}/$login"
81		rm ${MAILSPOOL}/$login
82	fi
83	if [ -f ${MAILSPOOL}/${login}.pop ]; then
84		echo -n " ${MAILSPOOL}/${login}.pop"
85		rm ${MAILSPOOL}/${login}.pop
86	fi
87	echo '.'
88}
89
90# kill_procs login
91#	Send a SIGKILL to all processes owned by $login.
92#
93kill_procs() {
94	# The argument is required
95	[ -n $1 ] && login=$1 || return
96
97	echo -n "Terminating all processes owned by ($login):"
98	killcount=0
99	proclist=`ps 2>/dev/null -U $login | grep -v '^\ *PID' | awk '{print $1}'`
100	for _pid in $proclist ; do
101		kill 2>/dev/null ${SIGKILL} $_pid
102		killcount=`expr $killcount + 1`
103	done
104	echo " ${SIGKILL} signal sent to $killcount processes."
105}
106
107# rm_at_jobs login
108#	Remove at (1) jobs belonging to $login.
109#
110rm_at_jobs() {
111	# The argument is required
112	[ -n $1 ] && login=$1 || return
113
114	atjoblist=`find 2>/dev/null ${ATJOBDIR} -maxdepth 1 -user $login -print`
115	jobcount=0
116	echo -n "Removing at(1) jobs owned by ($login):"
117	for _atjob in $atjoblist ; do
118		rm -f $_atjob
119		jobcount=`expr $jobcount + 1`
120	done
121	echo " $jobcount removed."
122}
123
124# rm_crontab login
125#	Removes crontab file belonging to user $login.
126#
127rm_crontab() {
128	# The argument is required
129	[ -n $1 ] && login=$1 || return
130
131	echo -n "Removing crontab for ($login):"
132	if [ -f ${CRONJOBDIR}/$login ]; then
133		echo -n " ${CRONJOBDIR}/$login"
134		rm -f ${CRONJOBDIR}/$login
135	fi
136	echo '.'
137}
138
139# rm_user login
140#	Remove user $login from the system. This subroutine makes use
141#	of the pw(8) command to remove a user from the system. The pw(8)
142#	command will remove the specified user from the user database
143#	and group file and remove any crontabs. His home
144#	directory will be removed if it is owned by him and contains no 
145#	files or subdirectories owned by other users. Mail spool files will
146#	also be removed.
147#
148rm_user() {
149	# The argument is required
150	[ -n $1 ] && login=$1 || return
151
152	echo -n "Removing user ($login)"
153	[ -n "$pw_rswitch" ] && echo -n " (including home directory)"
154	echo -n " from the system:"
155	pw userdel -n $login $pw_rswitch
156	echo ' Done.'
157}
158
159# prompt_yesno msg
160#	Prompts the user with a $msg. The answer is expected to be
161#	yes, no, or some variation thereof. This subroutine returns 0
162#	if the answer was yes, 1 if it was not.
163#
164prompt_yesno() {
165	# The argument is required
166	[ -n "$1" ] && msg=$1 || return
167
168        while : ; do
169                echo -n $msg
170                read _ans
171                case $_ans in
172                [Nn][Oo]|[Nn])
173			return 1
174                        ;;
175                [Yy][Ee][Ss]|[Yy][Ee]|[Yy])
176                        return 0
177                        ;;
178                *)
179                        ;;
180                esac
181	done
182}
183
184# show_usage
185#	(no arguments)
186#	Display usage message.
187#
188show_usage() {
189	echo "usage: ${THISCMD} [-y] [-f file] [user ...]"
190	echo "       if the -y switch is used, either the -f switch or"
191	echo "       one or more user names must be given"
192}
193
194#### END SUBROUTINE DEFENITION ####
195
196ffile=
197fflag=
198procowner=
199pw_rswitch=
200userlist=
201yflag=
202
203procowner=`/usr/bin/id -u`
204if [ "$procowner" != "0" ]; then
205	err 'you must be root (0) to use this utility.'
206	exit 1
207fi
208
209args=`getopt 2>/dev/null yf: $*`
210if [ "$?" != "0" ]; then
211	show_usage
212	exit 1
213fi
214set -- $args
215for _switch ; do
216	case $_switch in
217	-y)
218		yflag=1
219		shift
220		;;
221	-f)
222		fflag=1
223		ffile="$2"
224		shift; shift
225		;;
226	--)
227		shift
228		break
229		;;
230	esac
231done
232
233# Get user names from a file if the -f switch was used. Otherwise,
234# get them from the commandline arguments. If we're getting it
235# from a file, the file must be owned by and writable only by root.
236#
237if [ $fflag ]; then
238	_insecure=`find $ffile ! -user 0 -or -perm +0022`
239	if [ -n "$_insecure" ]; then
240		err "file ($ffile) must be owned by and writeable only by root."
241		exit 1
242	fi
243	if [ -r "$ffile" ]; then
244		userlist=`cat $ffile | while read _user _junk ; do
245			case $_user in
246			\#*|'')
247				;;
248			*)
249				echo -n "$userlist $_user"
250				;;
251			esac
252		done`
253	fi
254else
255	while [ $1 ] ; do
256		userlist="$userlist $1"
257		shift
258	done
259fi
260
261# If the -y or -f switch has been used and the list of users to remove
262# is empty it is a fatal error. Otherwise, prompt the user for a list
263# of one or more user names.
264#
265if [ ! "$userlist" ]; then
266	if [ $fflag ]; then
267		err "($ffile) does not exist or does not contain any user names."
268		exit 1
269	elif [ $yflag ]; then
270		show_usage
271		exit 1
272	else
273		echo -n "Please enter one or more user name's: "
274		read userlist
275	fi
276fi
277
278_user=
279_uid=
280for _user in $userlist ; do
281	# Make sure the name exists in the passwd database and that it
282	# does not have a uid of 0
283	#
284	userrec=`pw 2>/dev/null usershow -n $_user`
285	if [ "$?" != "0" ]; then
286		err "user ($_user) does not exist in the password database."
287		continue
288	fi
289	_uid=`echo $userrec | awk -F: '{print $3}'`
290	if [ "$_uid" = "0" ]; then
291		err "user ($_user) has uid 0. You may not remove this user."
292		continue
293	fi
294
295	# If the -y switch was not used ask for confirmation to remove the
296	# user and home directory.
297	#
298	if [ -z "$yflag" ]; then
299		echo "Matching password entry:"
300		echo
301		echo $userrec
302		echo
303		if ! prompt_yesno "Is this the entry you wish to remove? " ; then
304			continue
305		fi
306		_homedir=`echo $userrec | awk -F: '{print $9}'`
307		if prompt_yesno "Remove user's home directory ($_homedir)?: "; then
308			pw_rswitch="-r"
309		fi
310	else
311		pw_rswitch="-r"
312	fi
313
314	# Disable any further attempts to log into this account
315	pw 2>/dev/null lock $_user
316
317	# Remove crontab, mail spool, etc. Then obliterate the user from
318	# the passwd and group database.
319	rm_crontab $_user
320	rm_at_jobs $_user
321	kill_procs $_user
322	rm_mail $_user
323	rm_files $_user
324	rm_user $_user
325done
326