rmuser.sh revision 109750
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 109750 2003-01-23 19:58:16Z fjoe $
30#
31
32ATJOBDIR="/var/at/jobs"
33CRONJOBDIR="/var/cron/tabs"
34MAILSPOOL="/var/mail"
35SIGKILL="-KILL"
36TEMPDIRS="/tmp /var/tmp"
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=`find 2>/dev/null "$_dir" -user "$login" -delete -print | \
61		    wc -l | sed 's/ *//'`
62		echo " $filecount removed."
63	done
64}
65
66# rm_mail login
67#	Removes unix mail and pop daemon files belonging to the user
68#	specified in the $login argument.
69#
70rm_mail() {
71	# The argument is required
72	[ -n $1 ] && login=$1 || return
73
74	echo -n "Removing mail spool(s) for ($login):"
75	if [ -f ${MAILSPOOL}/$login ]; then
76		echo -n " ${MAILSPOOL}/$login"
77		rm ${MAILSPOOL}/$login
78	fi
79	if [ -f ${MAILSPOOL}/${login}.pop ]; then
80		echo -n " ${MAILSPOOL}/${login}.pop"
81		rm ${MAILSPOOL}/${login}.pop
82	fi
83	echo '.'
84}
85
86# kill_procs login
87#	Send a SIGKILL to all processes owned by $login.
88#
89kill_procs() {
90	# The argument is required
91	[ -n $1 ] && login=$1 || return
92
93	echo -n "Terminating all processes owned by ($login):"
94	killcount=0
95	proclist=`ps 2>/dev/null -U $login | grep -v '^\ *PID' | awk '{print $1}'`
96	for _pid in $proclist ; do
97		kill 2>/dev/null ${SIGKILL} $_pid
98		killcount=`expr $killcount + 1`
99	done
100	echo " ${SIGKILL} signal sent to $killcount processes."
101}
102
103# rm_at_jobs login
104#	Remove at (1) jobs belonging to $login.
105#
106rm_at_jobs() {
107	# The argument is required
108	[ -n $1 ] && login=$1 || return
109
110	atjoblist=`find 2>/dev/null ${ATJOBDIR} -maxdepth 1 -user $login -print`
111	jobcount=0
112	echo -n "Removing at(1) jobs owned by ($login):"
113	for _atjob in $atjoblist ; do
114		rm -f $_atjob
115		jobcount=`expr $jobcount + 1`
116	done
117	echo " $jobcount removed."
118}
119
120# rm_crontab login
121#	Removes crontab file belonging to user $login.
122#
123rm_crontab() {
124	# The argument is required
125	[ -n $1 ] && login=$1 || return
126
127	echo -n "Removing crontab for ($login):"
128	if [ -f ${CRONJOBDIR}/$login ]; then
129		echo -n " ${CRONJOBDIR}/$login"
130		rm -f ${CRONJOBDIR}/$login
131	fi
132	echo '.'
133}
134
135# rm_user login
136#	Remove user $login from the system. This subroutine makes use
137#	of the pw(8) command to remove a user from the system. The pw(8)
138#	command will remove the specified user from the user database
139#	and group file and remove any crontabs. His home
140#	directory will be removed if it is owned by him and contains no 
141#	files or subdirectories owned by other users. Mail spool files will
142#	also be removed.
143#
144rm_user() {
145	# The argument is required
146	[ -n $1 ] && login=$1 || return
147
148	echo -n "Removing user ($login)"
149	[ -n "$pw_rswitch" ] && echo -n " (including home directory)"
150	echo -n " from the system:"
151	pw userdel -n $login $pw_rswitch
152	echo ' Done.'
153}
154
155# prompt_yesno msg
156#	Prompts the user with a $msg. The answer is expected to be
157#	yes, no, or some variation thereof. This subroutine returns 0
158#	if the answer was yes, 1 if it was not.
159#
160prompt_yesno() {
161	# The argument is required
162	[ -n "$1" ] && msg="$1" || return
163
164        while : ; do
165                echo -n "$msg"
166                read _ans
167                case $_ans in
168                [Nn][Oo]|[Nn])
169			return 1
170                        ;;
171                [Yy][Ee][Ss]|[Yy][Ee]|[Yy])
172                        return 0
173                        ;;
174                *)
175                        ;;
176                esac
177	done
178}
179
180# show_usage
181#	(no arguments)
182#	Display usage message.
183#
184show_usage() {
185	echo "usage: ${THISCMD} [-y] [-f file] [user ...]"
186	echo "       if the -y switch is used, either the -f switch or"
187	echo "       one or more user names must be given"
188}
189
190#### END SUBROUTINE DEFENITION ####
191
192ffile=
193fflag=
194procowner=
195pw_rswitch=
196userlist=
197yflag=
198
199procowner=`/usr/bin/id -u`
200if [ "$procowner" != "0" ]; then
201	err 'you must be root (0) to use this utility.'
202	exit 1
203fi
204
205args=`getopt 2>/dev/null yf: $*`
206if [ "$?" != "0" ]; then
207	show_usage
208	exit 1
209fi
210set -- $args
211for _switch ; do
212	case $_switch in
213	-y)
214		yflag=1
215		shift
216		;;
217	-f)
218		fflag=1
219		ffile="$2"
220		shift; shift
221		;;
222	--)
223		shift
224		break
225		;;
226	esac
227done
228
229# Get user names from a file if the -f switch was used. Otherwise,
230# get them from the commandline arguments. If we're getting it
231# from a file, the file must be owned by and writable only by root.
232#
233if [ $fflag ]; then
234	_insecure=`find $ffile ! -user 0 -or -perm +0022`
235	if [ -n "$_insecure" ]; then
236		err "file ($ffile) must be owned by and writeable only by root."
237		exit 1
238	fi
239	if [ -r "$ffile" ]; then
240		userlist=`cat $ffile | while read _user _junk ; do
241			case $_user in
242			\#*|'')
243				;;
244			*)
245				echo -n "$userlist $_user"
246				;;
247			esac
248		done`
249	fi
250else
251	while [ $1 ] ; do
252		userlist="$userlist $1"
253		shift
254	done
255fi
256
257# If the -y or -f switch has been used and the list of users to remove
258# is empty it is a fatal error. Otherwise, prompt the user for a list
259# of one or more user names.
260#
261if [ ! "$userlist" ]; then
262	if [ $fflag ]; then
263		err "($ffile) does not exist or does not contain any user names."
264		exit 1
265	elif [ $yflag ]; then
266		show_usage
267		exit 1
268	else
269		echo -n "Please enter one or more user name's: "
270		read userlist
271	fi
272fi
273
274_user=
275_uid=
276for _user in $userlist ; do
277	# Make sure the name exists in the passwd database and that it
278	# does not have a uid of 0
279	#
280	userrec=`pw 2>/dev/null usershow -n $_user`
281	if [ "$?" != "0" ]; then
282		err "user ($_user) does not exist in the password database."
283		continue
284	fi
285	_uid=`echo $userrec | awk -F: '{print $3}'`
286	if [ "$_uid" = "0" ]; then
287		err "user ($_user) has uid 0. You may not remove this user."
288		continue
289	fi
290
291	# If the -y switch was not used ask for confirmation to remove the
292	# user and home directory.
293	#
294	if [ -z "$yflag" ]; then
295		echo "Matching password entry:"
296		echo
297		echo $userrec
298		echo
299		if ! prompt_yesno "Is this the entry you wish to remove? " ; then
300			continue
301		fi
302		_homedir=`echo $userrec | awk -F: '{print $9}'`
303		if prompt_yesno "Remove user's home directory ($_homedir)? "; then
304			pw_rswitch="-r"
305		fi
306	else
307		pw_rswitch="-r"
308	fi
309
310	# Disable any further attempts to log into this account
311	pw 2>/dev/null lock $_user
312
313	# Remove crontab, mail spool, etc. Then obliterate the user from
314	# the passwd and group database.
315	rm_crontab $_user
316	rm_at_jobs $_user
317	kill_procs $_user
318	rm_mail $_user
319	rm_files $_user
320	rm_user $_user
321done
322